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