2 * Copyright (c) 2021 Dell Inc. or its subsidiaries. All Rights Reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
13 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
14 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
17 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * Test behavior when a mapping of a shared shadow vm object is
28 * invalidated by COW from another mapping.
30 * This is a regression test for an issue isolated by rlibby@FreeBSD.org
31 * from an issue detected by stress2's collapse.sh by jeff@FreeBSD.org.
32 * The issue became CVE-2021-29626.
34 * This file is written as an ATF test suite but may be compiled as a
35 * standalone program with -DSTANDALONE (and optionally -DDEBUG).
38 #include <sys/types.h>
40 #include <sys/procctl.h>
42 #include <machine/atomic.h>
53 #define ATF_REQUIRE(x) do { \
62 #define dprintf(...) printf(__VA_ARGS__)
76 volatile bool exiting[DEPTH];
78 volatile bool p3_did_write;
81 static long g_pagesize;
84 * Program flow. There are three or four processes that are descendants
85 * of the process running the test (P0), where arrows go from parents to
86 * children, and thicker arrows indicate sharing a certain memory region
87 * without COW semantics:
88 * P0 -> P1 -> P2 => P3
90 * The main idea is that P1 maps a memory region, and that region is
91 * shared with P2/P3, but with COW semantics. When P3 modifies the
92 * memory, P2 ought to see that modification. P4 optionally exists to
93 * defeat a COW optimization.
96 #define child_err(...) do { \
98 err(1, __VA_ARGS__); \
101 #define child_errx(...) do { \
103 errx(1, __VA_ARGS__); \
106 #define SLEEP_TIME_US 1000
108 static void child(struct shared_state *ss, int depth);
111 child_fork(struct shared_state *ss, int depth)
122 child_fault(struct shared_state *ss)
126 for (i = 0; i < ss->len; i += g_pagesize)
127 (void)((volatile char *)ss->p)[i];
131 child_write(struct shared_state *ss, int val, size_t len)
135 for (i = 0; i < len; i += g_pagesize)
136 ((int *)ss->p)[i / sizeof(int)] = val;
137 atomic_thread_fence_rel();
141 child_wait_p3_write(struct shared_state *ss)
143 while (!ss->p3_did_write) {
146 usleep(SLEEP_TIME_US);
148 atomic_thread_fence_acq();
152 child_verify(struct shared_state *ss, int depth, int newval, int oldval)
155 int expectval, foundval;
157 for (i = 0; i < ss->len; i += g_pagesize) {
158 expectval = i < ss->modlen ? newval : oldval;
159 foundval = ((int *)ss->p)[i / sizeof(int)];
160 if (foundval == expectval)
162 child_errx("P%d saw %d but expected %d, %d was the old value",
163 depth, foundval, expectval, oldval);
168 child(struct shared_state *ss, int depth)
170 pid_t mypid, oldval, pid;
172 if (depth < 1 || depth >= DEPTH)
173 child_errx("Bad depth %d", depth);
175 dprintf("P%d (pid %d) started\n", depth, mypid);
178 /* Shared memory undergoing test. */
179 ss->p = mmap(NULL, ss->len, PROT_READ | PROT_WRITE,
180 MAP_SHARED | MAP_ANON, -1, 0);
181 if (ss->p == MAP_FAILED)
183 /* P1 stamps the shared memory. */
184 child_write(ss, mypid, ss->len);
185 if (ss->block_xfer) {
187 * P4 is forked so that its existence blocks a page COW
188 * path where the page is simply transferred between
189 * objects, rather than being copied.
194 * P1 specifies that modifications from its child processes not
195 * be shared with P1. Child process reads can be serviced from
196 * pages in P1's object, but writes must be COW'd.
198 if (minherit(ss->p, ss->len, INHERIT_COPY) != 0)
199 child_err("minherit");
201 child_fork(ss, depth + 1);
202 /* P1 and P4 wait for P3's writes before exiting. */
203 child_wait_p3_write(ss);
204 child_verify(ss, depth, mypid, mypid);
206 /* Hang around to prevent collapse. */
208 usleep(SLEEP_TIME_US);
210 /* Exit so the P2 -> P1/P4 shadow chain can collapse. */
214 * P2 now specifies that modifications from its child processes
215 * be shared. P2 and P3 will share a shadow object.
217 if (minherit(ss->p, ss->len, INHERIT_SHARE) != 0)
218 child_err("minherit");
220 * P2 faults a page in P1's object before P1 exits and the
221 * shadow chain is collapsed.
224 oldval = atomic_load_acq_int(ss->p);
226 pid = child_fork(ss, depth + 1);
228 /* Wait for P1 and P4 to exit, triggering collapse. */
229 while (!ss->exiting[1] ||
230 (ss->block_xfer && !ss->exiting[4]))
231 usleep(SLEEP_TIME_US);
233 * This is racy, just guess at how long it may take
234 * them to finish exiting.
238 /* P2 waits for P3's modification. */
239 child_wait_p3_write(ss);
240 child_verify(ss, depth, pid, oldval);
246 * P3 writes the memory. A page is faulted into the shared
247 * P2/P3 shadow object. P2's mapping of the page in P1's
248 * object must now be shot down, or else P2 will wrongly
249 * continue to have that page mapped.
251 child_write(ss, mypid, ss->modlen);
252 ss->p3_did_write = true;
253 dprintf("P3 (pid %d) wrote its pid\n", mypid);
256 /* Just hang around until P3 is done writing. */
257 oldval = atomic_load_acq_int(ss->p);
258 child_wait_p3_write(ss);
259 child_verify(ss, depth, oldval, oldval);
262 child_errx("Bad depth %d", depth);
265 dprintf("P%d (pid %d) exiting\n", depth, mypid);
266 ss->exiting[depth] = true;
271 do_shared_shadow_inval(bool collapse, bool block_xfer, bool full_mod)
273 struct shared_state *ss;
278 dprintf("P0 (pid %d) %s(collapse=%d, block_xfer=%d, full_mod=%d)\n",
279 pid, __func__, (int)collapse, (int)block_xfer, (int)full_mod);
281 g_pagesize = sysconf(_SC_PAGESIZE);
282 ATF_REQUIRE(g_pagesize > 0);
284 ATF_REQUIRE(procctl(P_PID, pid, PROC_REAP_ACQUIRE, NULL) == 0);
286 /* Shared memory for coordination. */
287 ss = mmap(NULL, sizeof(*ss), PROT_READ | PROT_WRITE,
288 MAP_SHARED | MAP_ANON, -1, 0);
289 ATF_REQUIRE(ss != MAP_FAILED);
291 ss->len = 2 * 1024 * 1024 + g_pagesize; /* 2 MB + page size */
292 ss->modlen = full_mod ? ss->len : ss->len / 2;
293 ss->collapse = collapse;
294 ss->block_xfer = block_xfer;
297 ATF_REQUIRE(pid != -1);
301 /* Wait for all descendants to exit. */
304 } while (pid != -1 || errno != ECHILD);
306 atomic_thread_fence_acq();
307 ATF_REQUIRE(ss->okay);
309 ATF_REQUIRE(munmap(ss, sizeof(*ss)) == 0);
310 ATF_REQUIRE(procctl(P_PID, getpid(), PROC_REAP_RELEASE, NULL) == 0);
318 do_shared_shadow_inval(false, false, false);
319 do_shared_shadow_inval(false, false, true);
320 do_shared_shadow_inval(false, true, false);
321 do_shared_shadow_inval(false, true, true);
322 do_shared_shadow_inval(true, false, false);
323 do_shared_shadow_inval(true, false, true);
324 do_shared_shadow_inval(true, true, false);
325 do_shared_shadow_inval(true, true, true);
330 #define SHARED_SHADOW_INVAL_TC(suffix, collapse, block_xfer, full_mod) \
331 ATF_TC_WITHOUT_HEAD(shared_shadow_inval__##suffix); \
332 ATF_TC_BODY(shared_shadow_inval__##suffix, tc) \
334 do_shared_shadow_inval(collapse, block_xfer, full_mod); \
337 SHARED_SHADOW_INVAL_TC(nocollapse_noblockxfer_nofullmod, false, false, false);
338 SHARED_SHADOW_INVAL_TC(nocollapse_noblockxfer_fullmod, false, false, true);
339 SHARED_SHADOW_INVAL_TC(nocollapse_blockxfer_nofullmod, false, true, false);
340 SHARED_SHADOW_INVAL_TC(nocollapse_blockxfer_fullmod, false, true, true);
341 SHARED_SHADOW_INVAL_TC(collapse_noblockxfer_nofullmod, true, false, false);
342 SHARED_SHADOW_INVAL_TC(collapse_noblockxfer_fullmod, true, false, true);
343 SHARED_SHADOW_INVAL_TC(collapse_blockxfer_nofullmod, true, true, false);
344 SHARED_SHADOW_INVAL_TC(collapse_blockxfer_fullmod, true, true, true);
349 shared_shadow_inval__nocollapse_noblockxfer_nofullmod);
350 ATF_TP_ADD_TC(tp, shared_shadow_inval__nocollapse_noblockxfer_fullmod);
351 ATF_TP_ADD_TC(tp, shared_shadow_inval__nocollapse_blockxfer_nofullmod);
352 ATF_TP_ADD_TC(tp, shared_shadow_inval__nocollapse_blockxfer_fullmod);
353 ATF_TP_ADD_TC(tp, shared_shadow_inval__collapse_noblockxfer_nofullmod);
354 ATF_TP_ADD_TC(tp, shared_shadow_inval__collapse_noblockxfer_fullmod);
355 ATF_TP_ADD_TC(tp, shared_shadow_inval__collapse_blockxfer_nofullmod);
356 ATF_TP_ADD_TC(tp, shared_shadow_inval__collapse_blockxfer_fullmod);
358 return atf_no_error();