xref: /linux/tools/testing/selftests/mm/merge.c (revision c06944560a562828d507166b4f87c01c367cc9c1)
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 
3 #define _GNU_SOURCE
4 #include "../kselftest_harness.h"
5 #include <fcntl.h>
6 #include <stdio.h>
7 #include <stdlib.h>
8 #include <unistd.h>
9 #include <sys/mman.h>
10 #include <sys/syscall.h>
11 #include <sys/wait.h>
12 #include <linux/perf_event.h>
13 #include "vm_util.h"
14 
FIXTURE(merge)15 FIXTURE(merge)
16 {
17 	unsigned int page_size;
18 	char *carveout;
19 	struct procmap_fd procmap;
20 };
21 
FIXTURE_SETUP(merge)22 FIXTURE_SETUP(merge)
23 {
24 	self->page_size = psize();
25 	/* Carve out PROT_NONE region to map over. */
26 	self->carveout = mmap(NULL, 12 * self->page_size, PROT_NONE,
27 			      MAP_ANON | MAP_PRIVATE, -1, 0);
28 	ASSERT_NE(self->carveout, MAP_FAILED);
29 	/* Setup PROCMAP_QUERY interface. */
30 	ASSERT_EQ(open_self_procmap(&self->procmap), 0);
31 }
32 
FIXTURE_TEARDOWN(merge)33 FIXTURE_TEARDOWN(merge)
34 {
35 	ASSERT_EQ(munmap(self->carveout, 12 * self->page_size), 0);
36 	ASSERT_EQ(close_procmap(&self->procmap), 0);
37 }
38 
TEST_F(merge,mprotect_unfaulted_left)39 TEST_F(merge, mprotect_unfaulted_left)
40 {
41 	unsigned int page_size = self->page_size;
42 	char *carveout = self->carveout;
43 	struct procmap_fd *procmap = &self->procmap;
44 	char *ptr;
45 
46 	/*
47 	 * Map 10 pages of R/W memory within. MAP_NORESERVE so we don't hit
48 	 * merge failure due to lack of VM_ACCOUNT flag by mistake.
49 	 *
50 	 * |-----------------------|
51 	 * |       unfaulted       |
52 	 * |-----------------------|
53 	 */
54 	ptr = mmap(&carveout[page_size], 10 * page_size, PROT_READ | PROT_WRITE,
55 		   MAP_ANON | MAP_PRIVATE | MAP_FIXED | MAP_NORESERVE, -1, 0);
56 	ASSERT_NE(ptr, MAP_FAILED);
57 	/*
58 	 * Now make the first 5 pages read-only, splitting the VMA:
59 	 *
60 	 *      RO          RW
61 	 * |-----------|-----------|
62 	 * | unfaulted | unfaulted |
63 	 * |-----------|-----------|
64 	 */
65 	ASSERT_EQ(mprotect(ptr, 5 * page_size, PROT_READ), 0);
66 	/*
67 	 * Fault in the first of the last 5 pages so it gets an anon_vma and
68 	 * thus the whole VMA becomes 'faulted':
69 	 *
70 	 *      RO          RW
71 	 * |-----------|-----------|
72 	 * | unfaulted |  faulted  |
73 	 * |-----------|-----------|
74 	 */
75 	ptr[5 * page_size] = 'x';
76 	/*
77 	 * Now mprotect() the RW region read-only, we should merge (though for
78 	 * ~15 years we did not! :):
79 	 *
80 	 *             RO
81 	 * |-----------------------|
82 	 * |        faulted        |
83 	 * |-----------------------|
84 	 */
85 	ASSERT_EQ(mprotect(&ptr[5 * page_size], 5 * page_size, PROT_READ), 0);
86 
87 	/* Assert that the merge succeeded using PROCMAP_QUERY. */
88 	ASSERT_TRUE(find_vma_procmap(procmap, ptr));
89 	ASSERT_EQ(procmap->query.vma_start, (unsigned long)ptr);
90 	ASSERT_EQ(procmap->query.vma_end, (unsigned long)ptr + 10 * page_size);
91 }
92 
TEST_F(merge,mprotect_unfaulted_right)93 TEST_F(merge, mprotect_unfaulted_right)
94 {
95 	unsigned int page_size = self->page_size;
96 	char *carveout = self->carveout;
97 	struct procmap_fd *procmap = &self->procmap;
98 	char *ptr;
99 
100 	/*
101 	 * |-----------------------|
102 	 * |       unfaulted       |
103 	 * |-----------------------|
104 	 */
105 	ptr = mmap(&carveout[page_size], 10 * page_size, PROT_READ | PROT_WRITE,
106 		   MAP_ANON | MAP_PRIVATE | MAP_FIXED | MAP_NORESERVE, -1, 0);
107 	ASSERT_NE(ptr, MAP_FAILED);
108 	/*
109 	 * Now make the last 5 pages read-only, splitting the VMA:
110 	 *
111 	 *      RW          RO
112 	 * |-----------|-----------|
113 	 * | unfaulted | unfaulted |
114 	 * |-----------|-----------|
115 	 */
116 	ASSERT_EQ(mprotect(&ptr[5 * page_size], 5 * page_size, PROT_READ), 0);
117 	/*
118 	 * Fault in the first of the first 5 pages so it gets an anon_vma and
119 	 * thus the whole VMA becomes 'faulted':
120 	 *
121 	 *      RW          RO
122 	 * |-----------|-----------|
123 	 * |  faulted  | unfaulted |
124 	 * |-----------|-----------|
125 	 */
126 	ptr[0] = 'x';
127 	/*
128 	 * Now mprotect() the RW region read-only, we should merge:
129 	 *
130 	 *             RO
131 	 * |-----------------------|
132 	 * |        faulted        |
133 	 * |-----------------------|
134 	 */
135 	ASSERT_EQ(mprotect(ptr, 5 * page_size, PROT_READ), 0);
136 
137 	/* Assert that the merge succeeded using PROCMAP_QUERY. */
138 	ASSERT_TRUE(find_vma_procmap(procmap, ptr));
139 	ASSERT_EQ(procmap->query.vma_start, (unsigned long)ptr);
140 	ASSERT_EQ(procmap->query.vma_end, (unsigned long)ptr + 10 * page_size);
141 }
142 
TEST_F(merge,mprotect_unfaulted_both)143 TEST_F(merge, mprotect_unfaulted_both)
144 {
145 	unsigned int page_size = self->page_size;
146 	char *carveout = self->carveout;
147 	struct procmap_fd *procmap = &self->procmap;
148 	char *ptr;
149 
150 	/*
151 	 * |-----------------------|
152 	 * |       unfaulted       |
153 	 * |-----------------------|
154 	 */
155 	ptr = mmap(&carveout[2 * page_size], 9 * page_size, PROT_READ | PROT_WRITE,
156 		   MAP_ANON | MAP_PRIVATE | MAP_FIXED | MAP_NORESERVE, -1, 0);
157 	ASSERT_NE(ptr, MAP_FAILED);
158 	/*
159 	 * Now make the first and last 3 pages read-only, splitting the VMA:
160 	 *
161 	 *      RO          RW          RO
162 	 * |-----------|-----------|-----------|
163 	 * | unfaulted | unfaulted | unfaulted |
164 	 * |-----------|-----------|-----------|
165 	 */
166 	ASSERT_EQ(mprotect(ptr, 3 * page_size, PROT_READ), 0);
167 	ASSERT_EQ(mprotect(&ptr[6 * page_size], 3 * page_size, PROT_READ), 0);
168 	/*
169 	 * Fault in the first of the middle 3 pages so it gets an anon_vma and
170 	 * thus the whole VMA becomes 'faulted':
171 	 *
172 	 *      RO          RW          RO
173 	 * |-----------|-----------|-----------|
174 	 * | unfaulted |  faulted  | unfaulted |
175 	 * |-----------|-----------|-----------|
176 	 */
177 	ptr[3 * page_size] = 'x';
178 	/*
179 	 * Now mprotect() the RW region read-only, we should merge:
180 	 *
181 	 *             RO
182 	 * |-----------------------|
183 	 * |        faulted        |
184 	 * |-----------------------|
185 	 */
186 	ASSERT_EQ(mprotect(&ptr[3 * page_size], 3 * page_size, PROT_READ), 0);
187 
188 	/* Assert that the merge succeeded using PROCMAP_QUERY. */
189 	ASSERT_TRUE(find_vma_procmap(procmap, ptr));
190 	ASSERT_EQ(procmap->query.vma_start, (unsigned long)ptr);
191 	ASSERT_EQ(procmap->query.vma_end, (unsigned long)ptr + 9 * page_size);
192 }
193 
TEST_F(merge,mprotect_faulted_left_unfaulted_right)194 TEST_F(merge, mprotect_faulted_left_unfaulted_right)
195 {
196 	unsigned int page_size = self->page_size;
197 	char *carveout = self->carveout;
198 	struct procmap_fd *procmap = &self->procmap;
199 	char *ptr;
200 
201 	/*
202 	 * |-----------------------|
203 	 * |       unfaulted       |
204 	 * |-----------------------|
205 	 */
206 	ptr = mmap(&carveout[2 * page_size], 9 * page_size, PROT_READ | PROT_WRITE,
207 		   MAP_ANON | MAP_PRIVATE | MAP_FIXED | MAP_NORESERVE, -1, 0);
208 	ASSERT_NE(ptr, MAP_FAILED);
209 	/*
210 	 * Now make the last 3 pages read-only, splitting the VMA:
211 	 *
212 	 *             RW               RO
213 	 * |-----------------------|-----------|
214 	 * |       unfaulted       | unfaulted |
215 	 * |-----------------------|-----------|
216 	 */
217 	ASSERT_EQ(mprotect(&ptr[6 * page_size], 3 * page_size, PROT_READ), 0);
218 	/*
219 	 * Fault in the first of the first 6 pages so it gets an anon_vma and
220 	 * thus the whole VMA becomes 'faulted':
221 	 *
222 	 *             RW               RO
223 	 * |-----------------------|-----------|
224 	 * |       unfaulted       | unfaulted |
225 	 * |-----------------------|-----------|
226 	 */
227 	ptr[0] = 'x';
228 	/*
229 	 * Now make the first 3 pages read-only, splitting the VMA:
230 	 *
231 	 *      RO          RW          RO
232 	 * |-----------|-----------|-----------|
233 	 * |  faulted  |  faulted  | unfaulted |
234 	 * |-----------|-----------|-----------|
235 	 */
236 	ASSERT_EQ(mprotect(ptr, 3 * page_size, PROT_READ), 0);
237 	/*
238 	 * Now mprotect() the RW region read-only, we should merge:
239 	 *
240 	 *             RO
241 	 * |-----------------------|
242 	 * |        faulted        |
243 	 * |-----------------------|
244 	 */
245 	ASSERT_EQ(mprotect(&ptr[3 * page_size], 3 * page_size, PROT_READ), 0);
246 
247 	/* Assert that the merge succeeded using PROCMAP_QUERY. */
248 	ASSERT_TRUE(find_vma_procmap(procmap, ptr));
249 	ASSERT_EQ(procmap->query.vma_start, (unsigned long)ptr);
250 	ASSERT_EQ(procmap->query.vma_end, (unsigned long)ptr + 9 * page_size);
251 }
252 
TEST_F(merge,mprotect_unfaulted_left_faulted_right)253 TEST_F(merge, mprotect_unfaulted_left_faulted_right)
254 {
255 	unsigned int page_size = self->page_size;
256 	char *carveout = self->carveout;
257 	struct procmap_fd *procmap = &self->procmap;
258 	char *ptr;
259 
260 	/*
261 	 * |-----------------------|
262 	 * |       unfaulted       |
263 	 * |-----------------------|
264 	 */
265 	ptr = mmap(&carveout[2 * page_size], 9 * page_size, PROT_READ | PROT_WRITE,
266 		   MAP_ANON | MAP_PRIVATE | MAP_FIXED | MAP_NORESERVE, -1, 0);
267 	ASSERT_NE(ptr, MAP_FAILED);
268 	/*
269 	 * Now make the first 3 pages read-only, splitting the VMA:
270 	 *
271 	 *      RO                RW
272 	 * |-----------|-----------------------|
273 	 * | unfaulted |       unfaulted       |
274 	 * |-----------|-----------------------|
275 	 */
276 	ASSERT_EQ(mprotect(ptr, 3 * page_size, PROT_READ), 0);
277 	/*
278 	 * Fault in the first of the last 6 pages so it gets an anon_vma and
279 	 * thus the whole VMA becomes 'faulted':
280 	 *
281 	 *      RO                RW
282 	 * |-----------|-----------------------|
283 	 * | unfaulted |        faulted        |
284 	 * |-----------|-----------------------|
285 	 */
286 	ptr[3 * page_size] = 'x';
287 	/*
288 	 * Now make the last 3 pages read-only, splitting the VMA:
289 	 *
290 	 *      RO          RW          RO
291 	 * |-----------|-----------|-----------|
292 	 * | unfaulted |  faulted  |  faulted  |
293 	 * |-----------|-----------|-----------|
294 	 */
295 	ASSERT_EQ(mprotect(&ptr[6 * page_size], 3 * page_size, PROT_READ), 0);
296 	/*
297 	 * Now mprotect() the RW region read-only, we should merge:
298 	 *
299 	 *             RO
300 	 * |-----------------------|
301 	 * |        faulted        |
302 	 * |-----------------------|
303 	 */
304 	ASSERT_EQ(mprotect(&ptr[3 * page_size], 3 * page_size, PROT_READ), 0);
305 
306 	/* Assert that the merge succeeded using PROCMAP_QUERY. */
307 	ASSERT_TRUE(find_vma_procmap(procmap, ptr));
308 	ASSERT_EQ(procmap->query.vma_start, (unsigned long)ptr);
309 	ASSERT_EQ(procmap->query.vma_end, (unsigned long)ptr + 9 * page_size);
310 }
311 
TEST_F(merge,forked_target_vma)312 TEST_F(merge, forked_target_vma)
313 {
314 	unsigned int page_size = self->page_size;
315 	char *carveout = self->carveout;
316 	struct procmap_fd *procmap = &self->procmap;
317 	pid_t pid;
318 	char *ptr, *ptr2;
319 	int i;
320 
321 	/*
322 	 * |-----------|
323 	 * | unfaulted |
324 	 * |-----------|
325 	 */
326 	ptr = mmap(&carveout[page_size], 5 * page_size, PROT_READ | PROT_WRITE,
327 		   MAP_ANON | MAP_PRIVATE | MAP_FIXED, -1, 0);
328 	ASSERT_NE(ptr, MAP_FAILED);
329 
330 	/*
331 	 * Fault in process.
332 	 *
333 	 * |-----------|
334 	 * |  faulted  |
335 	 * |-----------|
336 	 */
337 	ptr[0] = 'x';
338 
339 	pid = fork();
340 	ASSERT_NE(pid, -1);
341 
342 	if (pid != 0) {
343 		wait(NULL);
344 		return;
345 	}
346 
347 	/* Child process below: */
348 
349 	/* Reopen for child. */
350 	ASSERT_EQ(close_procmap(&self->procmap), 0);
351 	ASSERT_EQ(open_self_procmap(&self->procmap), 0);
352 
353 	/* unCOWing everything does not cause the AVC to go away. */
354 	for (i = 0; i < 5 * page_size; i += page_size)
355 		ptr[i] = 'x';
356 
357 	/*
358 	 * Map in adjacent VMA in child.
359 	 *
360 	 *     forked
361 	 * |-----------|-----------|
362 	 * |  faulted  | unfaulted |
363 	 * |-----------|-----------|
364 	 *      ptr         ptr2
365 	 */
366 	ptr2 = mmap(&ptr[5 * page_size], 5 * page_size, PROT_READ | PROT_WRITE,
367 		   MAP_ANON | MAP_PRIVATE | MAP_FIXED, -1, 0);
368 	ASSERT_NE(ptr2, MAP_FAILED);
369 
370 	/* Make sure not merged. */
371 	ASSERT_TRUE(find_vma_procmap(procmap, ptr));
372 	ASSERT_EQ(procmap->query.vma_start, (unsigned long)ptr);
373 	ASSERT_EQ(procmap->query.vma_end, (unsigned long)ptr + 5 * page_size);
374 }
375 
TEST_F(merge,forked_source_vma)376 TEST_F(merge, forked_source_vma)
377 {
378 	unsigned int page_size = self->page_size;
379 	char *carveout = self->carveout;
380 	struct procmap_fd *procmap = &self->procmap;
381 	pid_t pid;
382 	char *ptr, *ptr2;
383 	int i;
384 
385 	/*
386 	 * |-----------|------------|
387 	 * | unfaulted | <unmapped> |
388 	 * |-----------|------------|
389 	 */
390 	ptr = mmap(&carveout[page_size], 5 * page_size, PROT_READ | PROT_WRITE,
391 		   MAP_ANON | MAP_PRIVATE | MAP_FIXED | MAP_NORESERVE, -1, 0);
392 	ASSERT_NE(ptr, MAP_FAILED);
393 
394 	/*
395 	 * Fault in process.
396 	 *
397 	 * |-----------|------------|
398 	 * |  faulted  | <unmapped> |
399 	 * |-----------|------------|
400 	 */
401 	ptr[0] = 'x';
402 
403 	pid = fork();
404 	ASSERT_NE(pid, -1);
405 
406 	if (pid != 0) {
407 		wait(NULL);
408 		return;
409 	}
410 
411 	/* Child process below: */
412 
413 	/* Reopen for child. */
414 	ASSERT_EQ(close_procmap(&self->procmap), 0);
415 	ASSERT_EQ(open_self_procmap(&self->procmap), 0);
416 
417 	/* unCOWing everything does not cause the AVC to go away. */
418 	for (i = 0; i < 5 * page_size; i += page_size)
419 		ptr[i] = 'x';
420 
421 	/*
422 	 * Map in adjacent VMA in child, ptr2 after ptr, but incompatible.
423 	 *
424 	 *   forked RW      RWX
425 	 * |-----------|-----------|
426 	 * |  faulted  | unfaulted |
427 	 * |-----------|-----------|
428 	 *      ptr        ptr2
429 	 */
430 	ptr2 = mmap(&carveout[6 * page_size], 5 * page_size, PROT_READ | PROT_WRITE | PROT_EXEC,
431 		   MAP_ANON | MAP_PRIVATE | MAP_FIXED | MAP_NORESERVE, -1, 0);
432 	ASSERT_NE(ptr2, MAP_FAILED);
433 
434 	/* Make sure not merged. */
435 	ASSERT_TRUE(find_vma_procmap(procmap, ptr2));
436 	ASSERT_EQ(procmap->query.vma_start, (unsigned long)ptr2);
437 	ASSERT_EQ(procmap->query.vma_end, (unsigned long)ptr2 + 5 * page_size);
438 
439 	/*
440 	 * Now mprotect forked region to RWX so it becomes the source for the
441 	 * merge to unfaulted region:
442 	 *
443 	 *  forked RWX      RWX
444 	 * |-----------|-----------|
445 	 * |  faulted  | unfaulted |
446 	 * |-----------|-----------|
447 	 *      ptr         ptr2
448 	 *
449 	 * This should NOT result in a merge, as ptr was forked.
450 	 */
451 	ASSERT_EQ(mprotect(ptr, 5 * page_size, PROT_READ | PROT_WRITE | PROT_EXEC), 0);
452 	/* Again, make sure not merged. */
453 	ASSERT_TRUE(find_vma_procmap(procmap, ptr2));
454 	ASSERT_EQ(procmap->query.vma_start, (unsigned long)ptr2);
455 	ASSERT_EQ(procmap->query.vma_end, (unsigned long)ptr2 + 5 * page_size);
456 }
457 
TEST_F(merge,handle_uprobe_upon_merged_vma)458 TEST_F(merge, handle_uprobe_upon_merged_vma)
459 {
460 	const size_t attr_sz = sizeof(struct perf_event_attr);
461 	unsigned int page_size = self->page_size;
462 	const char *probe_file = "./foo";
463 	char *carveout = self->carveout;
464 	struct perf_event_attr attr;
465 	unsigned long type;
466 	void *ptr1, *ptr2;
467 	int fd;
468 
469 	fd = open(probe_file, O_RDWR|O_CREAT, 0600);
470 	ASSERT_GE(fd, 0);
471 
472 	ASSERT_EQ(ftruncate(fd, page_size), 0);
473 	if (read_sysfs("/sys/bus/event_source/devices/uprobe/type", &type) != 0) {
474 		SKIP(goto out, "Failed to read uprobe sysfs file, skipping");
475 	}
476 
477 	memset(&attr, 0, attr_sz);
478 	attr.size = attr_sz;
479 	attr.type = type;
480 	attr.config1 = (__u64)(long)probe_file;
481 	attr.config2 = 0x0;
482 
483 	ASSERT_GE(syscall(__NR_perf_event_open, &attr, 0, -1, -1, 0), 0);
484 
485 	ptr1 = mmap(&carveout[page_size], 10 * page_size, PROT_EXEC,
486 		    MAP_PRIVATE | MAP_FIXED, fd, 0);
487 	ASSERT_NE(ptr1, MAP_FAILED);
488 
489 	ptr2 = mremap(ptr1, page_size, 2 * page_size,
490 		      MREMAP_MAYMOVE | MREMAP_FIXED, ptr1 + 5 * page_size);
491 	ASSERT_NE(ptr2, MAP_FAILED);
492 
493 	ASSERT_NE(mremap(ptr2, page_size, page_size,
494 			 MREMAP_MAYMOVE | MREMAP_FIXED, ptr1), MAP_FAILED);
495 
496 out:
497 	close(fd);
498 	remove(probe_file);
499 }
500 
501 TEST_HARNESS_MAIN
502