xref: /linux/tools/testing/selftests/mm/merge.c (revision c06944560a562828d507166b4f87c01c367cc9c1)
110d28896SLorenzo Stoakes // SPDX-License-Identifier: GPL-2.0-or-later
210d28896SLorenzo Stoakes 
310d28896SLorenzo Stoakes #define _GNU_SOURCE
410d28896SLorenzo Stoakes #include "../kselftest_harness.h"
5efe99fabSPu Lehui #include <fcntl.h>
610d28896SLorenzo Stoakes #include <stdio.h>
710d28896SLorenzo Stoakes #include <stdlib.h>
810d28896SLorenzo Stoakes #include <unistd.h>
910d28896SLorenzo Stoakes #include <sys/mman.h>
10efe99fabSPu Lehui #include <sys/syscall.h>
1110d28896SLorenzo Stoakes #include <sys/wait.h>
12efe99fabSPu Lehui #include <linux/perf_event.h>
1310d28896SLorenzo Stoakes #include "vm_util.h"
1410d28896SLorenzo Stoakes 
FIXTURE(merge)1510d28896SLorenzo Stoakes FIXTURE(merge)
1610d28896SLorenzo Stoakes {
1710d28896SLorenzo Stoakes 	unsigned int page_size;
1810d28896SLorenzo Stoakes 	char *carveout;
1910d28896SLorenzo Stoakes 	struct procmap_fd procmap;
2010d28896SLorenzo Stoakes };
2110d28896SLorenzo Stoakes 
FIXTURE_SETUP(merge)2210d28896SLorenzo Stoakes FIXTURE_SETUP(merge)
2310d28896SLorenzo Stoakes {
2410d28896SLorenzo Stoakes 	self->page_size = psize();
2510d28896SLorenzo Stoakes 	/* Carve out PROT_NONE region to map over. */
2610d28896SLorenzo Stoakes 	self->carveout = mmap(NULL, 12 * self->page_size, PROT_NONE,
2710d28896SLorenzo Stoakes 			      MAP_ANON | MAP_PRIVATE, -1, 0);
2810d28896SLorenzo Stoakes 	ASSERT_NE(self->carveout, MAP_FAILED);
2910d28896SLorenzo Stoakes 	/* Setup PROCMAP_QUERY interface. */
3010d28896SLorenzo Stoakes 	ASSERT_EQ(open_self_procmap(&self->procmap), 0);
3110d28896SLorenzo Stoakes }
3210d28896SLorenzo Stoakes 
FIXTURE_TEARDOWN(merge)3310d28896SLorenzo Stoakes FIXTURE_TEARDOWN(merge)
3410d28896SLorenzo Stoakes {
3510d28896SLorenzo Stoakes 	ASSERT_EQ(munmap(self->carveout, 12 * self->page_size), 0);
3610d28896SLorenzo Stoakes 	ASSERT_EQ(close_procmap(&self->procmap), 0);
3710d28896SLorenzo Stoakes }
3810d28896SLorenzo Stoakes 
TEST_F(merge,mprotect_unfaulted_left)3910d28896SLorenzo Stoakes TEST_F(merge, mprotect_unfaulted_left)
4010d28896SLorenzo Stoakes {
4110d28896SLorenzo Stoakes 	unsigned int page_size = self->page_size;
4210d28896SLorenzo Stoakes 	char *carveout = self->carveout;
4310d28896SLorenzo Stoakes 	struct procmap_fd *procmap = &self->procmap;
4410d28896SLorenzo Stoakes 	char *ptr;
4510d28896SLorenzo Stoakes 
4610d28896SLorenzo Stoakes 	/*
4710d28896SLorenzo Stoakes 	 * Map 10 pages of R/W memory within. MAP_NORESERVE so we don't hit
4810d28896SLorenzo Stoakes 	 * merge failure due to lack of VM_ACCOUNT flag by mistake.
4910d28896SLorenzo Stoakes 	 *
5010d28896SLorenzo Stoakes 	 * |-----------------------|
5110d28896SLorenzo Stoakes 	 * |       unfaulted       |
5210d28896SLorenzo Stoakes 	 * |-----------------------|
5310d28896SLorenzo Stoakes 	 */
5410d28896SLorenzo Stoakes 	ptr = mmap(&carveout[page_size], 10 * page_size, PROT_READ | PROT_WRITE,
5510d28896SLorenzo Stoakes 		   MAP_ANON | MAP_PRIVATE | MAP_FIXED | MAP_NORESERVE, -1, 0);
5610d28896SLorenzo Stoakes 	ASSERT_NE(ptr, MAP_FAILED);
5710d28896SLorenzo Stoakes 	/*
5810d28896SLorenzo Stoakes 	 * Now make the first 5 pages read-only, splitting the VMA:
5910d28896SLorenzo Stoakes 	 *
6010d28896SLorenzo Stoakes 	 *      RO          RW
6110d28896SLorenzo Stoakes 	 * |-----------|-----------|
6210d28896SLorenzo Stoakes 	 * | unfaulted | unfaulted |
6310d28896SLorenzo Stoakes 	 * |-----------|-----------|
6410d28896SLorenzo Stoakes 	 */
6510d28896SLorenzo Stoakes 	ASSERT_EQ(mprotect(ptr, 5 * page_size, PROT_READ), 0);
6610d28896SLorenzo Stoakes 	/*
6710d28896SLorenzo Stoakes 	 * Fault in the first of the last 5 pages so it gets an anon_vma and
6810d28896SLorenzo Stoakes 	 * thus the whole VMA becomes 'faulted':
6910d28896SLorenzo Stoakes 	 *
7010d28896SLorenzo Stoakes 	 *      RO          RW
7110d28896SLorenzo Stoakes 	 * |-----------|-----------|
7210d28896SLorenzo Stoakes 	 * | unfaulted |  faulted  |
7310d28896SLorenzo Stoakes 	 * |-----------|-----------|
7410d28896SLorenzo Stoakes 	 */
7510d28896SLorenzo Stoakes 	ptr[5 * page_size] = 'x';
7610d28896SLorenzo Stoakes 	/*
7710d28896SLorenzo Stoakes 	 * Now mprotect() the RW region read-only, we should merge (though for
7810d28896SLorenzo Stoakes 	 * ~15 years we did not! :):
7910d28896SLorenzo Stoakes 	 *
8010d28896SLorenzo Stoakes 	 *             RO
8110d28896SLorenzo Stoakes 	 * |-----------------------|
8210d28896SLorenzo Stoakes 	 * |        faulted        |
8310d28896SLorenzo Stoakes 	 * |-----------------------|
8410d28896SLorenzo Stoakes 	 */
8510d28896SLorenzo Stoakes 	ASSERT_EQ(mprotect(&ptr[5 * page_size], 5 * page_size, PROT_READ), 0);
8610d28896SLorenzo Stoakes 
8710d28896SLorenzo Stoakes 	/* Assert that the merge succeeded using PROCMAP_QUERY. */
8810d28896SLorenzo Stoakes 	ASSERT_TRUE(find_vma_procmap(procmap, ptr));
8910d28896SLorenzo Stoakes 	ASSERT_EQ(procmap->query.vma_start, (unsigned long)ptr);
9010d28896SLorenzo Stoakes 	ASSERT_EQ(procmap->query.vma_end, (unsigned long)ptr + 10 * page_size);
9110d28896SLorenzo Stoakes }
9210d28896SLorenzo Stoakes 
TEST_F(merge,mprotect_unfaulted_right)9310d28896SLorenzo Stoakes TEST_F(merge, mprotect_unfaulted_right)
9410d28896SLorenzo Stoakes {
9510d28896SLorenzo Stoakes 	unsigned int page_size = self->page_size;
9610d28896SLorenzo Stoakes 	char *carveout = self->carveout;
9710d28896SLorenzo Stoakes 	struct procmap_fd *procmap = &self->procmap;
9810d28896SLorenzo Stoakes 	char *ptr;
9910d28896SLorenzo Stoakes 
10010d28896SLorenzo Stoakes 	/*
10110d28896SLorenzo Stoakes 	 * |-----------------------|
10210d28896SLorenzo Stoakes 	 * |       unfaulted       |
10310d28896SLorenzo Stoakes 	 * |-----------------------|
10410d28896SLorenzo Stoakes 	 */
10510d28896SLorenzo Stoakes 	ptr = mmap(&carveout[page_size], 10 * page_size, PROT_READ | PROT_WRITE,
10610d28896SLorenzo Stoakes 		   MAP_ANON | MAP_PRIVATE | MAP_FIXED | MAP_NORESERVE, -1, 0);
10710d28896SLorenzo Stoakes 	ASSERT_NE(ptr, MAP_FAILED);
10810d28896SLorenzo Stoakes 	/*
10910d28896SLorenzo Stoakes 	 * Now make the last 5 pages read-only, splitting the VMA:
11010d28896SLorenzo Stoakes 	 *
11110d28896SLorenzo Stoakes 	 *      RW          RO
11210d28896SLorenzo Stoakes 	 * |-----------|-----------|
11310d28896SLorenzo Stoakes 	 * | unfaulted | unfaulted |
11410d28896SLorenzo Stoakes 	 * |-----------|-----------|
11510d28896SLorenzo Stoakes 	 */
11610d28896SLorenzo Stoakes 	ASSERT_EQ(mprotect(&ptr[5 * page_size], 5 * page_size, PROT_READ), 0);
11710d28896SLorenzo Stoakes 	/*
11810d28896SLorenzo Stoakes 	 * Fault in the first of the first 5 pages so it gets an anon_vma and
11910d28896SLorenzo Stoakes 	 * thus the whole VMA becomes 'faulted':
12010d28896SLorenzo Stoakes 	 *
12110d28896SLorenzo Stoakes 	 *      RW          RO
12210d28896SLorenzo Stoakes 	 * |-----------|-----------|
12310d28896SLorenzo Stoakes 	 * |  faulted  | unfaulted |
12410d28896SLorenzo Stoakes 	 * |-----------|-----------|
12510d28896SLorenzo Stoakes 	 */
12610d28896SLorenzo Stoakes 	ptr[0] = 'x';
12710d28896SLorenzo Stoakes 	/*
12810d28896SLorenzo Stoakes 	 * Now mprotect() the RW region read-only, we should merge:
12910d28896SLorenzo Stoakes 	 *
13010d28896SLorenzo Stoakes 	 *             RO
13110d28896SLorenzo Stoakes 	 * |-----------------------|
13210d28896SLorenzo Stoakes 	 * |        faulted        |
13310d28896SLorenzo Stoakes 	 * |-----------------------|
13410d28896SLorenzo Stoakes 	 */
13510d28896SLorenzo Stoakes 	ASSERT_EQ(mprotect(ptr, 5 * page_size, PROT_READ), 0);
13610d28896SLorenzo Stoakes 
13710d28896SLorenzo Stoakes 	/* Assert that the merge succeeded using PROCMAP_QUERY. */
13810d28896SLorenzo Stoakes 	ASSERT_TRUE(find_vma_procmap(procmap, ptr));
13910d28896SLorenzo Stoakes 	ASSERT_EQ(procmap->query.vma_start, (unsigned long)ptr);
14010d28896SLorenzo Stoakes 	ASSERT_EQ(procmap->query.vma_end, (unsigned long)ptr + 10 * page_size);
14110d28896SLorenzo Stoakes }
14210d28896SLorenzo Stoakes 
TEST_F(merge,mprotect_unfaulted_both)14310d28896SLorenzo Stoakes TEST_F(merge, mprotect_unfaulted_both)
14410d28896SLorenzo Stoakes {
14510d28896SLorenzo Stoakes 	unsigned int page_size = self->page_size;
14610d28896SLorenzo Stoakes 	char *carveout = self->carveout;
14710d28896SLorenzo Stoakes 	struct procmap_fd *procmap = &self->procmap;
14810d28896SLorenzo Stoakes 	char *ptr;
14910d28896SLorenzo Stoakes 
15010d28896SLorenzo Stoakes 	/*
15110d28896SLorenzo Stoakes 	 * |-----------------------|
15210d28896SLorenzo Stoakes 	 * |       unfaulted       |
15310d28896SLorenzo Stoakes 	 * |-----------------------|
15410d28896SLorenzo Stoakes 	 */
15510d28896SLorenzo Stoakes 	ptr = mmap(&carveout[2 * page_size], 9 * page_size, PROT_READ | PROT_WRITE,
15610d28896SLorenzo Stoakes 		   MAP_ANON | MAP_PRIVATE | MAP_FIXED | MAP_NORESERVE, -1, 0);
15710d28896SLorenzo Stoakes 	ASSERT_NE(ptr, MAP_FAILED);
15810d28896SLorenzo Stoakes 	/*
15910d28896SLorenzo Stoakes 	 * Now make the first and last 3 pages read-only, splitting the VMA:
16010d28896SLorenzo Stoakes 	 *
16110d28896SLorenzo Stoakes 	 *      RO          RW          RO
16210d28896SLorenzo Stoakes 	 * |-----------|-----------|-----------|
16310d28896SLorenzo Stoakes 	 * | unfaulted | unfaulted | unfaulted |
16410d28896SLorenzo Stoakes 	 * |-----------|-----------|-----------|
16510d28896SLorenzo Stoakes 	 */
16610d28896SLorenzo Stoakes 	ASSERT_EQ(mprotect(ptr, 3 * page_size, PROT_READ), 0);
16710d28896SLorenzo Stoakes 	ASSERT_EQ(mprotect(&ptr[6 * page_size], 3 * page_size, PROT_READ), 0);
16810d28896SLorenzo Stoakes 	/*
16910d28896SLorenzo Stoakes 	 * Fault in the first of the middle 3 pages so it gets an anon_vma and
17010d28896SLorenzo Stoakes 	 * thus the whole VMA becomes 'faulted':
17110d28896SLorenzo Stoakes 	 *
17210d28896SLorenzo Stoakes 	 *      RO          RW          RO
17310d28896SLorenzo Stoakes 	 * |-----------|-----------|-----------|
17410d28896SLorenzo Stoakes 	 * | unfaulted |  faulted  | unfaulted |
17510d28896SLorenzo Stoakes 	 * |-----------|-----------|-----------|
17610d28896SLorenzo Stoakes 	 */
17710d28896SLorenzo Stoakes 	ptr[3 * page_size] = 'x';
17810d28896SLorenzo Stoakes 	/*
17910d28896SLorenzo Stoakes 	 * Now mprotect() the RW region read-only, we should merge:
18010d28896SLorenzo Stoakes 	 *
18110d28896SLorenzo Stoakes 	 *             RO
18210d28896SLorenzo Stoakes 	 * |-----------------------|
18310d28896SLorenzo Stoakes 	 * |        faulted        |
18410d28896SLorenzo Stoakes 	 * |-----------------------|
18510d28896SLorenzo Stoakes 	 */
18610d28896SLorenzo Stoakes 	ASSERT_EQ(mprotect(&ptr[3 * page_size], 3 * page_size, PROT_READ), 0);
18710d28896SLorenzo Stoakes 
18810d28896SLorenzo Stoakes 	/* Assert that the merge succeeded using PROCMAP_QUERY. */
18910d28896SLorenzo Stoakes 	ASSERT_TRUE(find_vma_procmap(procmap, ptr));
19010d28896SLorenzo Stoakes 	ASSERT_EQ(procmap->query.vma_start, (unsigned long)ptr);
19110d28896SLorenzo Stoakes 	ASSERT_EQ(procmap->query.vma_end, (unsigned long)ptr + 9 * page_size);
19210d28896SLorenzo Stoakes }
19310d28896SLorenzo Stoakes 
TEST_F(merge,mprotect_faulted_left_unfaulted_right)19410d28896SLorenzo Stoakes TEST_F(merge, mprotect_faulted_left_unfaulted_right)
19510d28896SLorenzo Stoakes {
19610d28896SLorenzo Stoakes 	unsigned int page_size = self->page_size;
19710d28896SLorenzo Stoakes 	char *carveout = self->carveout;
19810d28896SLorenzo Stoakes 	struct procmap_fd *procmap = &self->procmap;
19910d28896SLorenzo Stoakes 	char *ptr;
20010d28896SLorenzo Stoakes 
20110d28896SLorenzo Stoakes 	/*
20210d28896SLorenzo Stoakes 	 * |-----------------------|
20310d28896SLorenzo Stoakes 	 * |       unfaulted       |
20410d28896SLorenzo Stoakes 	 * |-----------------------|
20510d28896SLorenzo Stoakes 	 */
20610d28896SLorenzo Stoakes 	ptr = mmap(&carveout[2 * page_size], 9 * page_size, PROT_READ | PROT_WRITE,
20710d28896SLorenzo Stoakes 		   MAP_ANON | MAP_PRIVATE | MAP_FIXED | MAP_NORESERVE, -1, 0);
20810d28896SLorenzo Stoakes 	ASSERT_NE(ptr, MAP_FAILED);
20910d28896SLorenzo Stoakes 	/*
21010d28896SLorenzo Stoakes 	 * Now make the last 3 pages read-only, splitting the VMA:
21110d28896SLorenzo Stoakes 	 *
21210d28896SLorenzo Stoakes 	 *             RW               RO
21310d28896SLorenzo Stoakes 	 * |-----------------------|-----------|
21410d28896SLorenzo Stoakes 	 * |       unfaulted       | unfaulted |
21510d28896SLorenzo Stoakes 	 * |-----------------------|-----------|
21610d28896SLorenzo Stoakes 	 */
21710d28896SLorenzo Stoakes 	ASSERT_EQ(mprotect(&ptr[6 * page_size], 3 * page_size, PROT_READ), 0);
21810d28896SLorenzo Stoakes 	/*
21910d28896SLorenzo Stoakes 	 * Fault in the first of the first 6 pages so it gets an anon_vma and
22010d28896SLorenzo Stoakes 	 * thus the whole VMA becomes 'faulted':
22110d28896SLorenzo Stoakes 	 *
22210d28896SLorenzo Stoakes 	 *             RW               RO
22310d28896SLorenzo Stoakes 	 * |-----------------------|-----------|
22410d28896SLorenzo Stoakes 	 * |       unfaulted       | unfaulted |
22510d28896SLorenzo Stoakes 	 * |-----------------------|-----------|
22610d28896SLorenzo Stoakes 	 */
22710d28896SLorenzo Stoakes 	ptr[0] = 'x';
22810d28896SLorenzo Stoakes 	/*
22910d28896SLorenzo Stoakes 	 * Now make the first 3 pages read-only, splitting the VMA:
23010d28896SLorenzo Stoakes 	 *
23110d28896SLorenzo Stoakes 	 *      RO          RW          RO
23210d28896SLorenzo Stoakes 	 * |-----------|-----------|-----------|
23310d28896SLorenzo Stoakes 	 * |  faulted  |  faulted  | unfaulted |
23410d28896SLorenzo Stoakes 	 * |-----------|-----------|-----------|
23510d28896SLorenzo Stoakes 	 */
23610d28896SLorenzo Stoakes 	ASSERT_EQ(mprotect(ptr, 3 * page_size, PROT_READ), 0);
23710d28896SLorenzo Stoakes 	/*
23810d28896SLorenzo Stoakes 	 * Now mprotect() the RW region read-only, we should merge:
23910d28896SLorenzo Stoakes 	 *
24010d28896SLorenzo Stoakes 	 *             RO
24110d28896SLorenzo Stoakes 	 * |-----------------------|
24210d28896SLorenzo Stoakes 	 * |        faulted        |
24310d28896SLorenzo Stoakes 	 * |-----------------------|
24410d28896SLorenzo Stoakes 	 */
24510d28896SLorenzo Stoakes 	ASSERT_EQ(mprotect(&ptr[3 * page_size], 3 * page_size, PROT_READ), 0);
24610d28896SLorenzo Stoakes 
24710d28896SLorenzo Stoakes 	/* Assert that the merge succeeded using PROCMAP_QUERY. */
24810d28896SLorenzo Stoakes 	ASSERT_TRUE(find_vma_procmap(procmap, ptr));
24910d28896SLorenzo Stoakes 	ASSERT_EQ(procmap->query.vma_start, (unsigned long)ptr);
25010d28896SLorenzo Stoakes 	ASSERT_EQ(procmap->query.vma_end, (unsigned long)ptr + 9 * page_size);
25110d28896SLorenzo Stoakes }
25210d28896SLorenzo Stoakes 
TEST_F(merge,mprotect_unfaulted_left_faulted_right)25310d28896SLorenzo Stoakes TEST_F(merge, mprotect_unfaulted_left_faulted_right)
25410d28896SLorenzo Stoakes {
25510d28896SLorenzo Stoakes 	unsigned int page_size = self->page_size;
25610d28896SLorenzo Stoakes 	char *carveout = self->carveout;
25710d28896SLorenzo Stoakes 	struct procmap_fd *procmap = &self->procmap;
25810d28896SLorenzo Stoakes 	char *ptr;
25910d28896SLorenzo Stoakes 
26010d28896SLorenzo Stoakes 	/*
26110d28896SLorenzo Stoakes 	 * |-----------------------|
26210d28896SLorenzo Stoakes 	 * |       unfaulted       |
26310d28896SLorenzo Stoakes 	 * |-----------------------|
26410d28896SLorenzo Stoakes 	 */
26510d28896SLorenzo Stoakes 	ptr = mmap(&carveout[2 * page_size], 9 * page_size, PROT_READ | PROT_WRITE,
26610d28896SLorenzo Stoakes 		   MAP_ANON | MAP_PRIVATE | MAP_FIXED | MAP_NORESERVE, -1, 0);
26710d28896SLorenzo Stoakes 	ASSERT_NE(ptr, MAP_FAILED);
26810d28896SLorenzo Stoakes 	/*
26910d28896SLorenzo Stoakes 	 * Now make the first 3 pages read-only, splitting the VMA:
27010d28896SLorenzo Stoakes 	 *
27110d28896SLorenzo Stoakes 	 *      RO                RW
27210d28896SLorenzo Stoakes 	 * |-----------|-----------------------|
27310d28896SLorenzo Stoakes 	 * | unfaulted |       unfaulted       |
27410d28896SLorenzo Stoakes 	 * |-----------|-----------------------|
27510d28896SLorenzo Stoakes 	 */
27610d28896SLorenzo Stoakes 	ASSERT_EQ(mprotect(ptr, 3 * page_size, PROT_READ), 0);
27710d28896SLorenzo Stoakes 	/*
27810d28896SLorenzo Stoakes 	 * Fault in the first of the last 6 pages so it gets an anon_vma and
27910d28896SLorenzo Stoakes 	 * thus the whole VMA becomes 'faulted':
28010d28896SLorenzo Stoakes 	 *
28110d28896SLorenzo Stoakes 	 *      RO                RW
28210d28896SLorenzo Stoakes 	 * |-----------|-----------------------|
28310d28896SLorenzo Stoakes 	 * | unfaulted |        faulted        |
28410d28896SLorenzo Stoakes 	 * |-----------|-----------------------|
28510d28896SLorenzo Stoakes 	 */
28610d28896SLorenzo Stoakes 	ptr[3 * page_size] = 'x';
28710d28896SLorenzo Stoakes 	/*
28810d28896SLorenzo Stoakes 	 * Now make the last 3 pages read-only, splitting the VMA:
28910d28896SLorenzo Stoakes 	 *
29010d28896SLorenzo Stoakes 	 *      RO          RW          RO
29110d28896SLorenzo Stoakes 	 * |-----------|-----------|-----------|
29210d28896SLorenzo Stoakes 	 * | unfaulted |  faulted  |  faulted  |
29310d28896SLorenzo Stoakes 	 * |-----------|-----------|-----------|
29410d28896SLorenzo Stoakes 	 */
29510d28896SLorenzo Stoakes 	ASSERT_EQ(mprotect(&ptr[6 * page_size], 3 * page_size, PROT_READ), 0);
29610d28896SLorenzo Stoakes 	/*
29710d28896SLorenzo Stoakes 	 * Now mprotect() the RW region read-only, we should merge:
29810d28896SLorenzo Stoakes 	 *
29910d28896SLorenzo Stoakes 	 *             RO
30010d28896SLorenzo Stoakes 	 * |-----------------------|
30110d28896SLorenzo Stoakes 	 * |        faulted        |
30210d28896SLorenzo Stoakes 	 * |-----------------------|
30310d28896SLorenzo Stoakes 	 */
30410d28896SLorenzo Stoakes 	ASSERT_EQ(mprotect(&ptr[3 * page_size], 3 * page_size, PROT_READ), 0);
30510d28896SLorenzo Stoakes 
30610d28896SLorenzo Stoakes 	/* Assert that the merge succeeded using PROCMAP_QUERY. */
30710d28896SLorenzo Stoakes 	ASSERT_TRUE(find_vma_procmap(procmap, ptr));
30810d28896SLorenzo Stoakes 	ASSERT_EQ(procmap->query.vma_start, (unsigned long)ptr);
30910d28896SLorenzo Stoakes 	ASSERT_EQ(procmap->query.vma_end, (unsigned long)ptr + 9 * page_size);
31010d28896SLorenzo Stoakes }
31110d28896SLorenzo Stoakes 
TEST_F(merge,forked_target_vma)31210d28896SLorenzo Stoakes TEST_F(merge, forked_target_vma)
31310d28896SLorenzo Stoakes {
31410d28896SLorenzo Stoakes 	unsigned int page_size = self->page_size;
31510d28896SLorenzo Stoakes 	char *carveout = self->carveout;
31610d28896SLorenzo Stoakes 	struct procmap_fd *procmap = &self->procmap;
31710d28896SLorenzo Stoakes 	pid_t pid;
31810d28896SLorenzo Stoakes 	char *ptr, *ptr2;
31910d28896SLorenzo Stoakes 	int i;
32010d28896SLorenzo Stoakes 
32110d28896SLorenzo Stoakes 	/*
32210d28896SLorenzo Stoakes 	 * |-----------|
32310d28896SLorenzo Stoakes 	 * | unfaulted |
32410d28896SLorenzo Stoakes 	 * |-----------|
32510d28896SLorenzo Stoakes 	 */
32610d28896SLorenzo Stoakes 	ptr = mmap(&carveout[page_size], 5 * page_size, PROT_READ | PROT_WRITE,
32710d28896SLorenzo Stoakes 		   MAP_ANON | MAP_PRIVATE | MAP_FIXED, -1, 0);
32810d28896SLorenzo Stoakes 	ASSERT_NE(ptr, MAP_FAILED);
32910d28896SLorenzo Stoakes 
33010d28896SLorenzo Stoakes 	/*
33110d28896SLorenzo Stoakes 	 * Fault in process.
33210d28896SLorenzo Stoakes 	 *
33310d28896SLorenzo Stoakes 	 * |-----------|
33410d28896SLorenzo Stoakes 	 * |  faulted  |
33510d28896SLorenzo Stoakes 	 * |-----------|
33610d28896SLorenzo Stoakes 	 */
33710d28896SLorenzo Stoakes 	ptr[0] = 'x';
33810d28896SLorenzo Stoakes 
33910d28896SLorenzo Stoakes 	pid = fork();
34010d28896SLorenzo Stoakes 	ASSERT_NE(pid, -1);
34110d28896SLorenzo Stoakes 
34210d28896SLorenzo Stoakes 	if (pid != 0) {
34310d28896SLorenzo Stoakes 		wait(NULL);
34410d28896SLorenzo Stoakes 		return;
34510d28896SLorenzo Stoakes 	}
34610d28896SLorenzo Stoakes 
34710d28896SLorenzo Stoakes 	/* Child process below: */
34810d28896SLorenzo Stoakes 
34910d28896SLorenzo Stoakes 	/* Reopen for child. */
35010d28896SLorenzo Stoakes 	ASSERT_EQ(close_procmap(&self->procmap), 0);
35110d28896SLorenzo Stoakes 	ASSERT_EQ(open_self_procmap(&self->procmap), 0);
35210d28896SLorenzo Stoakes 
35310d28896SLorenzo Stoakes 	/* unCOWing everything does not cause the AVC to go away. */
35410d28896SLorenzo Stoakes 	for (i = 0; i < 5 * page_size; i += page_size)
35510d28896SLorenzo Stoakes 		ptr[i] = 'x';
35610d28896SLorenzo Stoakes 
35710d28896SLorenzo Stoakes 	/*
35810d28896SLorenzo Stoakes 	 * Map in adjacent VMA in child.
35910d28896SLorenzo Stoakes 	 *
36010d28896SLorenzo Stoakes 	 *     forked
36110d28896SLorenzo Stoakes 	 * |-----------|-----------|
36210d28896SLorenzo Stoakes 	 * |  faulted  | unfaulted |
36310d28896SLorenzo Stoakes 	 * |-----------|-----------|
36410d28896SLorenzo Stoakes 	 *      ptr         ptr2
36510d28896SLorenzo Stoakes 	 */
36610d28896SLorenzo Stoakes 	ptr2 = mmap(&ptr[5 * page_size], 5 * page_size, PROT_READ | PROT_WRITE,
36710d28896SLorenzo Stoakes 		   MAP_ANON | MAP_PRIVATE | MAP_FIXED, -1, 0);
36810d28896SLorenzo Stoakes 	ASSERT_NE(ptr2, MAP_FAILED);
36910d28896SLorenzo Stoakes 
37010d28896SLorenzo Stoakes 	/* Make sure not merged. */
37110d28896SLorenzo Stoakes 	ASSERT_TRUE(find_vma_procmap(procmap, ptr));
37210d28896SLorenzo Stoakes 	ASSERT_EQ(procmap->query.vma_start, (unsigned long)ptr);
37310d28896SLorenzo Stoakes 	ASSERT_EQ(procmap->query.vma_end, (unsigned long)ptr + 5 * page_size);
37410d28896SLorenzo Stoakes }
37510d28896SLorenzo Stoakes 
TEST_F(merge,forked_source_vma)37610d28896SLorenzo Stoakes TEST_F(merge, forked_source_vma)
37710d28896SLorenzo Stoakes {
37810d28896SLorenzo Stoakes 	unsigned int page_size = self->page_size;
37910d28896SLorenzo Stoakes 	char *carveout = self->carveout;
38010d28896SLorenzo Stoakes 	struct procmap_fd *procmap = &self->procmap;
38110d28896SLorenzo Stoakes 	pid_t pid;
38210d28896SLorenzo Stoakes 	char *ptr, *ptr2;
38310d28896SLorenzo Stoakes 	int i;
38410d28896SLorenzo Stoakes 
38510d28896SLorenzo Stoakes 	/*
38610d28896SLorenzo Stoakes 	 * |-----------|------------|
38710d28896SLorenzo Stoakes 	 * | unfaulted | <unmapped> |
38810d28896SLorenzo Stoakes 	 * |-----------|------------|
38910d28896SLorenzo Stoakes 	 */
39010d28896SLorenzo Stoakes 	ptr = mmap(&carveout[page_size], 5 * page_size, PROT_READ | PROT_WRITE,
39110d28896SLorenzo Stoakes 		   MAP_ANON | MAP_PRIVATE | MAP_FIXED | MAP_NORESERVE, -1, 0);
39210d28896SLorenzo Stoakes 	ASSERT_NE(ptr, MAP_FAILED);
39310d28896SLorenzo Stoakes 
39410d28896SLorenzo Stoakes 	/*
39510d28896SLorenzo Stoakes 	 * Fault in process.
39610d28896SLorenzo Stoakes 	 *
39710d28896SLorenzo Stoakes 	 * |-----------|------------|
39810d28896SLorenzo Stoakes 	 * |  faulted  | <unmapped> |
39910d28896SLorenzo Stoakes 	 * |-----------|------------|
40010d28896SLorenzo Stoakes 	 */
40110d28896SLorenzo Stoakes 	ptr[0] = 'x';
40210d28896SLorenzo Stoakes 
40310d28896SLorenzo Stoakes 	pid = fork();
40410d28896SLorenzo Stoakes 	ASSERT_NE(pid, -1);
40510d28896SLorenzo Stoakes 
40610d28896SLorenzo Stoakes 	if (pid != 0) {
40710d28896SLorenzo Stoakes 		wait(NULL);
40810d28896SLorenzo Stoakes 		return;
40910d28896SLorenzo Stoakes 	}
41010d28896SLorenzo Stoakes 
41110d28896SLorenzo Stoakes 	/* Child process below: */
41210d28896SLorenzo Stoakes 
41310d28896SLorenzo Stoakes 	/* Reopen for child. */
41410d28896SLorenzo Stoakes 	ASSERT_EQ(close_procmap(&self->procmap), 0);
41510d28896SLorenzo Stoakes 	ASSERT_EQ(open_self_procmap(&self->procmap), 0);
41610d28896SLorenzo Stoakes 
41710d28896SLorenzo Stoakes 	/* unCOWing everything does not cause the AVC to go away. */
41810d28896SLorenzo Stoakes 	for (i = 0; i < 5 * page_size; i += page_size)
41910d28896SLorenzo Stoakes 		ptr[i] = 'x';
42010d28896SLorenzo Stoakes 
42110d28896SLorenzo Stoakes 	/*
42210d28896SLorenzo Stoakes 	 * Map in adjacent VMA in child, ptr2 after ptr, but incompatible.
42310d28896SLorenzo Stoakes 	 *
42410d28896SLorenzo Stoakes 	 *   forked RW      RWX
42510d28896SLorenzo Stoakes 	 * |-----------|-----------|
42610d28896SLorenzo Stoakes 	 * |  faulted  | unfaulted |
42710d28896SLorenzo Stoakes 	 * |-----------|-----------|
42810d28896SLorenzo Stoakes 	 *      ptr        ptr2
42910d28896SLorenzo Stoakes 	 */
43010d28896SLorenzo Stoakes 	ptr2 = mmap(&carveout[6 * page_size], 5 * page_size, PROT_READ | PROT_WRITE | PROT_EXEC,
43110d28896SLorenzo Stoakes 		   MAP_ANON | MAP_PRIVATE | MAP_FIXED | MAP_NORESERVE, -1, 0);
43210d28896SLorenzo Stoakes 	ASSERT_NE(ptr2, MAP_FAILED);
43310d28896SLorenzo Stoakes 
43410d28896SLorenzo Stoakes 	/* Make sure not merged. */
43510d28896SLorenzo Stoakes 	ASSERT_TRUE(find_vma_procmap(procmap, ptr2));
43610d28896SLorenzo Stoakes 	ASSERT_EQ(procmap->query.vma_start, (unsigned long)ptr2);
43710d28896SLorenzo Stoakes 	ASSERT_EQ(procmap->query.vma_end, (unsigned long)ptr2 + 5 * page_size);
43810d28896SLorenzo Stoakes 
43910d28896SLorenzo Stoakes 	/*
44010d28896SLorenzo Stoakes 	 * Now mprotect forked region to RWX so it becomes the source for the
44110d28896SLorenzo Stoakes 	 * merge to unfaulted region:
44210d28896SLorenzo Stoakes 	 *
44310d28896SLorenzo Stoakes 	 *  forked RWX      RWX
44410d28896SLorenzo Stoakes 	 * |-----------|-----------|
44510d28896SLorenzo Stoakes 	 * |  faulted  | unfaulted |
44610d28896SLorenzo Stoakes 	 * |-----------|-----------|
44710d28896SLorenzo Stoakes 	 *      ptr         ptr2
44810d28896SLorenzo Stoakes 	 *
44910d28896SLorenzo Stoakes 	 * This should NOT result in a merge, as ptr was forked.
45010d28896SLorenzo Stoakes 	 */
45110d28896SLorenzo Stoakes 	ASSERT_EQ(mprotect(ptr, 5 * page_size, PROT_READ | PROT_WRITE | PROT_EXEC), 0);
45210d28896SLorenzo Stoakes 	/* Again, make sure not merged. */
45310d28896SLorenzo Stoakes 	ASSERT_TRUE(find_vma_procmap(procmap, ptr2));
45410d28896SLorenzo Stoakes 	ASSERT_EQ(procmap->query.vma_start, (unsigned long)ptr2);
45510d28896SLorenzo Stoakes 	ASSERT_EQ(procmap->query.vma_end, (unsigned long)ptr2 + 5 * page_size);
45610d28896SLorenzo Stoakes }
45710d28896SLorenzo Stoakes 
TEST_F(merge,handle_uprobe_upon_merged_vma)458efe99fabSPu Lehui TEST_F(merge, handle_uprobe_upon_merged_vma)
459efe99fabSPu Lehui {
460efe99fabSPu Lehui 	const size_t attr_sz = sizeof(struct perf_event_attr);
461efe99fabSPu Lehui 	unsigned int page_size = self->page_size;
462efe99fabSPu Lehui 	const char *probe_file = "./foo";
463efe99fabSPu Lehui 	char *carveout = self->carveout;
464efe99fabSPu Lehui 	struct perf_event_attr attr;
465efe99fabSPu Lehui 	unsigned long type;
466efe99fabSPu Lehui 	void *ptr1, *ptr2;
467efe99fabSPu Lehui 	int fd;
468efe99fabSPu Lehui 
469efe99fabSPu Lehui 	fd = open(probe_file, O_RDWR|O_CREAT, 0600);
470efe99fabSPu Lehui 	ASSERT_GE(fd, 0);
471efe99fabSPu Lehui 
472efe99fabSPu Lehui 	ASSERT_EQ(ftruncate(fd, page_size), 0);
473*33338712SPedro Falcato 	if (read_sysfs("/sys/bus/event_source/devices/uprobe/type", &type) != 0) {
474*33338712SPedro Falcato 		SKIP(goto out, "Failed to read uprobe sysfs file, skipping");
475*33338712SPedro Falcato 	}
476efe99fabSPu Lehui 
477efe99fabSPu Lehui 	memset(&attr, 0, attr_sz);
478efe99fabSPu Lehui 	attr.size = attr_sz;
479efe99fabSPu Lehui 	attr.type = type;
480efe99fabSPu Lehui 	attr.config1 = (__u64)(long)probe_file;
481efe99fabSPu Lehui 	attr.config2 = 0x0;
482efe99fabSPu Lehui 
483efe99fabSPu Lehui 	ASSERT_GE(syscall(__NR_perf_event_open, &attr, 0, -1, -1, 0), 0);
484efe99fabSPu Lehui 
485efe99fabSPu Lehui 	ptr1 = mmap(&carveout[page_size], 10 * page_size, PROT_EXEC,
486efe99fabSPu Lehui 		    MAP_PRIVATE | MAP_FIXED, fd, 0);
487efe99fabSPu Lehui 	ASSERT_NE(ptr1, MAP_FAILED);
488efe99fabSPu Lehui 
489efe99fabSPu Lehui 	ptr2 = mremap(ptr1, page_size, 2 * page_size,
490efe99fabSPu Lehui 		      MREMAP_MAYMOVE | MREMAP_FIXED, ptr1 + 5 * page_size);
491efe99fabSPu Lehui 	ASSERT_NE(ptr2, MAP_FAILED);
492efe99fabSPu Lehui 
493efe99fabSPu Lehui 	ASSERT_NE(mremap(ptr2, page_size, page_size,
494efe99fabSPu Lehui 			 MREMAP_MAYMOVE | MREMAP_FIXED, ptr1), MAP_FAILED);
495efe99fabSPu Lehui 
496*33338712SPedro Falcato out:
497efe99fabSPu Lehui 	close(fd);
498efe99fabSPu Lehui 	remove(probe_file);
499efe99fabSPu Lehui }
500efe99fabSPu Lehui 
50110d28896SLorenzo Stoakes TEST_HARNESS_MAIN
502