Skip to content

Commit fb39444

Browse files
ljskernelakpm00
authored andcommitted
tools/testing/selftests: add forked (un)/faulted VMA merge tests
Now we correctly handle forked faulted/unfaulted merge on mremap(), exhaustively assert that we handle this correctly. Do this in the less duplicative way by adding a new merge_with_fork fixture and forked/unforked variants, and abstract the forking logic as necessary to avoid code duplication with this also. Link: https://lkml.kernel.org/r/1daf76d89fdb9d96f38a6a0152d8f3c2e9e30ac7.1767638272.git.lorenzo.stoakes@oracle.com Fixes: 879bca0 ("mm/vma: fix incorrectly disallowed anonymous VMA merges") Signed-off-by: Lorenzo Stoakes <lorenzo.stoakes@oracle.com> Cc: David Hildenbrand (Red Hat) <david@kernel.org> Cc: Jann Horn <jannh@google.com> Cc: Jeongjun Park <aha310510@gmail.com> Cc: Liam Howlett <liam.howlett@oracle.com> Cc: Pedro Falcato <pfalcato@suse.de> Cc: Rik van Riel <riel@surriel.com> Cc: Vlastimil Babka <vbabka@suse.cz> Cc: Yeoreum Yun <yeoreum.yun@arm.com> Cc: Harry Yoo <harry.yoo@oracle.com> Cc: <stable@vger.kernel.org> Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
1 parent 3b617fd commit fb39444

1 file changed

Lines changed: 139 additions & 41 deletions

File tree

tools/testing/selftests/mm/merge.c

Lines changed: 139 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,37 @@ FIXTURE(merge)
2222
struct procmap_fd procmap;
2323
};
2424

25+
static char *map_carveout(unsigned int page_size)
26+
{
27+
return mmap(NULL, 30 * page_size, PROT_NONE,
28+
MAP_ANON | MAP_PRIVATE, -1, 0);
29+
}
30+
31+
static pid_t do_fork(struct procmap_fd *procmap)
32+
{
33+
pid_t pid = fork();
34+
35+
if (pid == -1)
36+
return -1;
37+
if (pid != 0) {
38+
wait(NULL);
39+
return pid;
40+
}
41+
42+
/* Reopen for child. */
43+
if (close_procmap(procmap))
44+
return -1;
45+
if (open_self_procmap(procmap))
46+
return -1;
47+
48+
return 0;
49+
}
50+
2551
FIXTURE_SETUP(merge)
2652
{
2753
self->page_size = psize();
2854
/* Carve out PROT_NONE region to map over. */
29-
self->carveout = mmap(NULL, 30 * self->page_size, PROT_NONE,
30-
MAP_ANON | MAP_PRIVATE, -1, 0);
55+
self->carveout = map_carveout(self->page_size);
3156
ASSERT_NE(self->carveout, MAP_FAILED);
3257
/* Setup PROCMAP_QUERY interface. */
3358
ASSERT_EQ(open_self_procmap(&self->procmap), 0);
@@ -36,14 +61,53 @@ FIXTURE_SETUP(merge)
3661
FIXTURE_TEARDOWN(merge)
3762
{
3863
ASSERT_EQ(munmap(self->carveout, 30 * self->page_size), 0);
39-
ASSERT_EQ(close_procmap(&self->procmap), 0);
64+
/* May fail for parent of forked process. */
65+
close_procmap(&self->procmap);
4066
/*
4167
* Clear unconditionally, as some tests set this. It is no issue if this
4268
* fails (KSM may be disabled for instance).
4369
*/
4470
prctl(PR_SET_MEMORY_MERGE, 0, 0, 0, 0);
4571
}
4672

73+
FIXTURE(merge_with_fork)
74+
{
75+
unsigned int page_size;
76+
char *carveout;
77+
struct procmap_fd procmap;
78+
};
79+
80+
FIXTURE_VARIANT(merge_with_fork)
81+
{
82+
bool forked;
83+
};
84+
85+
FIXTURE_VARIANT_ADD(merge_with_fork, forked)
86+
{
87+
.forked = true,
88+
};
89+
90+
FIXTURE_VARIANT_ADD(merge_with_fork, unforked)
91+
{
92+
.forked = false,
93+
};
94+
95+
FIXTURE_SETUP(merge_with_fork)
96+
{
97+
self->page_size = psize();
98+
self->carveout = map_carveout(self->page_size);
99+
ASSERT_NE(self->carveout, MAP_FAILED);
100+
ASSERT_EQ(open_self_procmap(&self->procmap), 0);
101+
}
102+
103+
FIXTURE_TEARDOWN(merge_with_fork)
104+
{
105+
ASSERT_EQ(munmap(self->carveout, 30 * self->page_size), 0);
106+
ASSERT_EQ(close_procmap(&self->procmap), 0);
107+
/* See above. */
108+
prctl(PR_SET_MEMORY_MERGE, 0, 0, 0, 0);
109+
}
110+
47111
TEST_F(merge, mprotect_unfaulted_left)
48112
{
49113
unsigned int page_size = self->page_size;
@@ -322,8 +386,8 @@ TEST_F(merge, forked_target_vma)
322386
unsigned int page_size = self->page_size;
323387
char *carveout = self->carveout;
324388
struct procmap_fd *procmap = &self->procmap;
325-
pid_t pid;
326389
char *ptr, *ptr2;
390+
pid_t pid;
327391
int i;
328392

329393
/*
@@ -344,19 +408,10 @@ TEST_F(merge, forked_target_vma)
344408
*/
345409
ptr[0] = 'x';
346410

347-
pid = fork();
411+
pid = do_fork(&self->procmap);
348412
ASSERT_NE(pid, -1);
349-
350-
if (pid != 0) {
351-
wait(NULL);
413+
if (pid != 0)
352414
return;
353-
}
354-
355-
/* Child process below: */
356-
357-
/* Reopen for child. */
358-
ASSERT_EQ(close_procmap(&self->procmap), 0);
359-
ASSERT_EQ(open_self_procmap(&self->procmap), 0);
360415

361416
/* unCOWing everything does not cause the AVC to go away. */
362417
for (i = 0; i < 5 * page_size; i += page_size)
@@ -386,8 +441,8 @@ TEST_F(merge, forked_source_vma)
386441
unsigned int page_size = self->page_size;
387442
char *carveout = self->carveout;
388443
struct procmap_fd *procmap = &self->procmap;
389-
pid_t pid;
390444
char *ptr, *ptr2;
445+
pid_t pid;
391446
int i;
392447

393448
/*
@@ -408,19 +463,10 @@ TEST_F(merge, forked_source_vma)
408463
*/
409464
ptr[0] = 'x';
410465

411-
pid = fork();
466+
pid = do_fork(&self->procmap);
412467
ASSERT_NE(pid, -1);
413-
414-
if (pid != 0) {
415-
wait(NULL);
468+
if (pid != 0)
416469
return;
417-
}
418-
419-
/* Child process below: */
420-
421-
/* Reopen for child. */
422-
ASSERT_EQ(close_procmap(&self->procmap), 0);
423-
ASSERT_EQ(open_self_procmap(&self->procmap), 0);
424470

425471
/* unCOWing everything does not cause the AVC to go away. */
426472
for (i = 0; i < 5 * page_size; i += page_size)
@@ -1171,10 +1217,11 @@ TEST_F(merge, mremap_correct_placed_faulted)
11711217
ASSERT_EQ(procmap->query.vma_end, (unsigned long)ptr + 15 * page_size);
11721218
}
11731219

1174-
TEST_F(merge, mremap_faulted_to_unfaulted_prev)
1220+
TEST_F(merge_with_fork, mremap_faulted_to_unfaulted_prev)
11751221
{
11761222
struct procmap_fd *procmap = &self->procmap;
11771223
unsigned int page_size = self->page_size;
1224+
unsigned long offset;
11781225
char *ptr_a, *ptr_b;
11791226

11801227
/*
@@ -1197,6 +1244,14 @@ TEST_F(merge, mremap_faulted_to_unfaulted_prev)
11971244
/* Fault it in. */
11981245
ptr_a[0] = 'x';
11991246

1247+
if (variant->forked) {
1248+
pid_t pid = do_fork(&self->procmap);
1249+
1250+
ASSERT_NE(pid, -1);
1251+
if (pid != 0)
1252+
return;
1253+
}
1254+
12001255
/*
12011256
* Now move it out of the way so we can place VMA B in position,
12021257
* unfaulted.
@@ -1220,16 +1275,19 @@ TEST_F(merge, mremap_faulted_to_unfaulted_prev)
12201275
&self->carveout[page_size + 3 * page_size]);
12211276
ASSERT_NE(ptr_a, MAP_FAILED);
12221277

1223-
/* The VMAs should have merged. */
1278+
/* The VMAs should have merged, if not forked. */
12241279
ASSERT_TRUE(find_vma_procmap(procmap, ptr_b));
12251280
ASSERT_EQ(procmap->query.vma_start, (unsigned long)ptr_b);
1226-
ASSERT_EQ(procmap->query.vma_end, (unsigned long)ptr_b + 6 * page_size);
1281+
1282+
offset = variant->forked ? 3 * page_size : 6 * page_size;
1283+
ASSERT_EQ(procmap->query.vma_end, (unsigned long)ptr_b + offset);
12271284
}
12281285

1229-
TEST_F(merge, mremap_faulted_to_unfaulted_next)
1286+
TEST_F(merge_with_fork, mremap_faulted_to_unfaulted_next)
12301287
{
12311288
struct procmap_fd *procmap = &self->procmap;
12321289
unsigned int page_size = self->page_size;
1290+
unsigned long offset;
12331291
char *ptr_a, *ptr_b;
12341292

12351293
/*
@@ -1253,6 +1311,14 @@ TEST_F(merge, mremap_faulted_to_unfaulted_next)
12531311
/* Fault it in. */
12541312
ptr_a[0] = 'x';
12551313

1314+
if (variant->forked) {
1315+
pid_t pid = do_fork(&self->procmap);
1316+
1317+
ASSERT_NE(pid, -1);
1318+
if (pid != 0)
1319+
return;
1320+
}
1321+
12561322
/*
12571323
* Now move it out of the way so we can place VMA B in position,
12581324
* unfaulted.
@@ -1276,16 +1342,18 @@ TEST_F(merge, mremap_faulted_to_unfaulted_next)
12761342
&self->carveout[page_size]);
12771343
ASSERT_NE(ptr_a, MAP_FAILED);
12781344

1279-
/* The VMAs should have merged. */
1345+
/* The VMAs should have merged, if not forked. */
12801346
ASSERT_TRUE(find_vma_procmap(procmap, ptr_a));
12811347
ASSERT_EQ(procmap->query.vma_start, (unsigned long)ptr_a);
1282-
ASSERT_EQ(procmap->query.vma_end, (unsigned long)ptr_a + 6 * page_size);
1348+
offset = variant->forked ? 3 * page_size : 6 * page_size;
1349+
ASSERT_EQ(procmap->query.vma_end, (unsigned long)ptr_a + offset);
12831350
}
12841351

1285-
TEST_F(merge, mremap_faulted_to_unfaulted_prev_unfaulted_next)
1352+
TEST_F(merge_with_fork, mremap_faulted_to_unfaulted_prev_unfaulted_next)
12861353
{
12871354
struct procmap_fd *procmap = &self->procmap;
12881355
unsigned int page_size = self->page_size;
1356+
unsigned long offset;
12891357
char *ptr_a, *ptr_b, *ptr_c;
12901358

12911359
/*
@@ -1307,6 +1375,14 @@ TEST_F(merge, mremap_faulted_to_unfaulted_prev_unfaulted_next)
13071375
/* Fault it in. */
13081376
ptr_b[0] = 'x';
13091377

1378+
if (variant->forked) {
1379+
pid_t pid = do_fork(&self->procmap);
1380+
1381+
ASSERT_NE(pid, -1);
1382+
if (pid != 0)
1383+
return;
1384+
}
1385+
13101386
/*
13111387
* Now move it out of the way so we can place VMAs A, C in position,
13121388
* unfaulted.
@@ -1337,13 +1413,21 @@ TEST_F(merge, mremap_faulted_to_unfaulted_prev_unfaulted_next)
13371413
&self->carveout[page_size + 3 * page_size]);
13381414
ASSERT_NE(ptr_b, MAP_FAILED);
13391415

1340-
/* The VMAs should have merged. */
1416+
/* The VMAs should have merged, if not forked. */
13411417
ASSERT_TRUE(find_vma_procmap(procmap, ptr_a));
13421418
ASSERT_EQ(procmap->query.vma_start, (unsigned long)ptr_a);
1343-
ASSERT_EQ(procmap->query.vma_end, (unsigned long)ptr_a + 9 * page_size);
1419+
offset = variant->forked ? 3 * page_size : 9 * page_size;
1420+
ASSERT_EQ(procmap->query.vma_end, (unsigned long)ptr_a + offset);
1421+
1422+
/* If forked, B and C should also not have merged. */
1423+
if (variant->forked) {
1424+
ASSERT_TRUE(find_vma_procmap(procmap, ptr_b));
1425+
ASSERT_EQ(procmap->query.vma_start, (unsigned long)ptr_b);
1426+
ASSERT_EQ(procmap->query.vma_end, (unsigned long)ptr_b + 3 * page_size);
1427+
}
13441428
}
13451429

1346-
TEST_F(merge, mremap_faulted_to_unfaulted_prev_faulted_next)
1430+
TEST_F(merge_with_fork, mremap_faulted_to_unfaulted_prev_faulted_next)
13471431
{
13481432
struct procmap_fd *procmap = &self->procmap;
13491433
unsigned int page_size = self->page_size;
@@ -1373,6 +1457,14 @@ TEST_F(merge, mremap_faulted_to_unfaulted_prev_faulted_next)
13731457
/* Fault it in. */
13741458
ptr_bc[0] = 'x';
13751459

1460+
if (variant->forked) {
1461+
pid_t pid = do_fork(&self->procmap);
1462+
1463+
ASSERT_NE(pid, -1);
1464+
if (pid != 0)
1465+
return;
1466+
}
1467+
13761468
/*
13771469
* Now move VMA B out the way (splitting VMA BC) so we can place VMA A
13781470
* in position, unfaulted, and leave the remainder of the VMA we just
@@ -1397,10 +1489,16 @@ TEST_F(merge, mremap_faulted_to_unfaulted_prev_faulted_next)
13971489
&self->carveout[page_size + 3 * page_size]);
13981490
ASSERT_NE(ptr_b, MAP_FAILED);
13991491

1400-
/* The VMAs should have merged. */
1401-
ASSERT_TRUE(find_vma_procmap(procmap, ptr_a));
1402-
ASSERT_EQ(procmap->query.vma_start, (unsigned long)ptr_a);
1403-
ASSERT_EQ(procmap->query.vma_end, (unsigned long)ptr_a + 9 * page_size);
1492+
/* The VMAs should have merged. A,B,C if unforked, B, C if forked. */
1493+
if (variant->forked) {
1494+
ASSERT_TRUE(find_vma_procmap(procmap, ptr_b));
1495+
ASSERT_EQ(procmap->query.vma_start, (unsigned long)ptr_b);
1496+
ASSERT_EQ(procmap->query.vma_end, (unsigned long)ptr_b + 6 * page_size);
1497+
} else {
1498+
ASSERT_TRUE(find_vma_procmap(procmap, ptr_a));
1499+
ASSERT_EQ(procmap->query.vma_start, (unsigned long)ptr_a);
1500+
ASSERT_EQ(procmap->query.vma_end, (unsigned long)ptr_a + 9 * page_size);
1501+
}
14041502
}
14051503

14061504
TEST_HARNESS_MAIN

0 commit comments

Comments
 (0)