Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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: 879bca0a2c4f ("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>

authored by

Lorenzo Stoakes and committed by
Andrew Morton
fb394447 3b617fd3

+139 -41
+139 -41
tools/testing/selftests/mm/merge.c
··· 22 22 struct procmap_fd procmap; 23 23 }; 24 24 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 + 25 51 FIXTURE_SETUP(merge) 26 52 { 27 53 self->page_size = psize(); 28 54 /* 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); 31 56 ASSERT_NE(self->carveout, MAP_FAILED); 32 57 /* Setup PROCMAP_QUERY interface. */ 33 58 ASSERT_EQ(open_self_procmap(&self->procmap), 0); ··· 61 36 FIXTURE_TEARDOWN(merge) 62 37 { 63 38 ASSERT_EQ(munmap(self->carveout, 30 * self->page_size), 0); 64 - ASSERT_EQ(close_procmap(&self->procmap), 0); 39 + /* May fail for parent of forked process. */ 40 + close_procmap(&self->procmap); 65 41 /* 66 42 * Clear unconditionally, as some tests set this. It is no issue if this 67 43 * fails (KSM may be disabled for instance). 68 44 */ 45 + prctl(PR_SET_MEMORY_MERGE, 0, 0, 0, 0); 46 + } 47 + 48 + FIXTURE(merge_with_fork) 49 + { 50 + unsigned int page_size; 51 + char *carveout; 52 + struct procmap_fd procmap; 53 + }; 54 + 55 + FIXTURE_VARIANT(merge_with_fork) 56 + { 57 + bool forked; 58 + }; 59 + 60 + FIXTURE_VARIANT_ADD(merge_with_fork, forked) 61 + { 62 + .forked = true, 63 + }; 64 + 65 + FIXTURE_VARIANT_ADD(merge_with_fork, unforked) 66 + { 67 + .forked = false, 68 + }; 69 + 70 + FIXTURE_SETUP(merge_with_fork) 71 + { 72 + self->page_size = psize(); 73 + self->carveout = map_carveout(self->page_size); 74 + ASSERT_NE(self->carveout, MAP_FAILED); 75 + ASSERT_EQ(open_self_procmap(&self->procmap), 0); 76 + } 77 + 78 + FIXTURE_TEARDOWN(merge_with_fork) 79 + { 80 + ASSERT_EQ(munmap(self->carveout, 30 * self->page_size), 0); 81 + ASSERT_EQ(close_procmap(&self->procmap), 0); 82 + /* See above. */ 69 83 prctl(PR_SET_MEMORY_MERGE, 0, 0, 0, 0); 70 84 } 71 85 ··· 386 322 unsigned int page_size = self->page_size; 387 323 char *carveout = self->carveout; 388 324 struct procmap_fd *procmap = &self->procmap; 389 - pid_t pid; 390 325 char *ptr, *ptr2; 326 + pid_t pid; 391 327 int i; 392 328 393 329 /* ··· 408 344 */ 409 345 ptr[0] = 'x'; 410 346 411 - pid = fork(); 347 + pid = do_fork(&self->procmap); 412 348 ASSERT_NE(pid, -1); 413 - 414 - if (pid != 0) { 415 - wait(NULL); 349 + if (pid != 0) 416 350 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); 424 351 425 352 /* unCOWing everything does not cause the AVC to go away. */ 426 353 for (i = 0; i < 5 * page_size; i += page_size) ··· 441 386 unsigned int page_size = self->page_size; 442 387 char *carveout = self->carveout; 443 388 struct procmap_fd *procmap = &self->procmap; 444 - pid_t pid; 445 389 char *ptr, *ptr2; 390 + pid_t pid; 446 391 int i; 447 392 448 393 /* ··· 463 408 */ 464 409 ptr[0] = 'x'; 465 410 466 - pid = fork(); 411 + pid = do_fork(&self->procmap); 467 412 ASSERT_NE(pid, -1); 468 - 469 - if (pid != 0) { 470 - wait(NULL); 413 + if (pid != 0) 471 414 return; 472 - } 473 - 474 - /* Child process below: */ 475 - 476 - /* Reopen for child. */ 477 - ASSERT_EQ(close_procmap(&self->procmap), 0); 478 - ASSERT_EQ(open_self_procmap(&self->procmap), 0); 479 415 480 416 /* unCOWing everything does not cause the AVC to go away. */ 481 417 for (i = 0; i < 5 * page_size; i += page_size) ··· 1217 1171 ASSERT_EQ(procmap->query.vma_end, (unsigned long)ptr + 15 * page_size); 1218 1172 } 1219 1173 1220 - TEST_F(merge, mremap_faulted_to_unfaulted_prev) 1174 + TEST_F(merge_with_fork, mremap_faulted_to_unfaulted_prev) 1221 1175 { 1222 1176 struct procmap_fd *procmap = &self->procmap; 1223 1177 unsigned int page_size = self->page_size; 1178 + unsigned long offset; 1224 1179 char *ptr_a, *ptr_b; 1225 1180 1226 1181 /* ··· 1243 1196 ASSERT_NE(ptr_a, MAP_FAILED); 1244 1197 /* Fault it in. */ 1245 1198 ptr_a[0] = 'x'; 1199 + 1200 + if (variant->forked) { 1201 + pid_t pid = do_fork(&self->procmap); 1202 + 1203 + ASSERT_NE(pid, -1); 1204 + if (pid != 0) 1205 + return; 1206 + } 1246 1207 1247 1208 /* 1248 1209 * Now move it out of the way so we can place VMA B in position, ··· 1275 1220 &self->carveout[page_size + 3 * page_size]); 1276 1221 ASSERT_NE(ptr_a, MAP_FAILED); 1277 1222 1278 - /* The VMAs should have merged. */ 1223 + /* The VMAs should have merged, if not forked. */ 1279 1224 ASSERT_TRUE(find_vma_procmap(procmap, ptr_b)); 1280 1225 ASSERT_EQ(procmap->query.vma_start, (unsigned long)ptr_b); 1281 - ASSERT_EQ(procmap->query.vma_end, (unsigned long)ptr_b + 6 * page_size); 1226 + 1227 + offset = variant->forked ? 3 * page_size : 6 * page_size; 1228 + ASSERT_EQ(procmap->query.vma_end, (unsigned long)ptr_b + offset); 1282 1229 } 1283 1230 1284 - TEST_F(merge, mremap_faulted_to_unfaulted_next) 1231 + TEST_F(merge_with_fork, mremap_faulted_to_unfaulted_next) 1285 1232 { 1286 1233 struct procmap_fd *procmap = &self->procmap; 1287 1234 unsigned int page_size = self->page_size; 1235 + unsigned long offset; 1288 1236 char *ptr_a, *ptr_b; 1289 1237 1290 1238 /* ··· 1310 1252 ASSERT_NE(ptr_a, MAP_FAILED); 1311 1253 /* Fault it in. */ 1312 1254 ptr_a[0] = 'x'; 1255 + 1256 + if (variant->forked) { 1257 + pid_t pid = do_fork(&self->procmap); 1258 + 1259 + ASSERT_NE(pid, -1); 1260 + if (pid != 0) 1261 + return; 1262 + } 1313 1263 1314 1264 /* 1315 1265 * Now move it out of the way so we can place VMA B in position, ··· 1342 1276 &self->carveout[page_size]); 1343 1277 ASSERT_NE(ptr_a, MAP_FAILED); 1344 1278 1345 - /* The VMAs should have merged. */ 1279 + /* The VMAs should have merged, if not forked. */ 1346 1280 ASSERT_TRUE(find_vma_procmap(procmap, ptr_a)); 1347 1281 ASSERT_EQ(procmap->query.vma_start, (unsigned long)ptr_a); 1348 - ASSERT_EQ(procmap->query.vma_end, (unsigned long)ptr_a + 6 * page_size); 1282 + offset = variant->forked ? 3 * page_size : 6 * page_size; 1283 + ASSERT_EQ(procmap->query.vma_end, (unsigned long)ptr_a + offset); 1349 1284 } 1350 1285 1351 - TEST_F(merge, mremap_faulted_to_unfaulted_prev_unfaulted_next) 1286 + TEST_F(merge_with_fork, mremap_faulted_to_unfaulted_prev_unfaulted_next) 1352 1287 { 1353 1288 struct procmap_fd *procmap = &self->procmap; 1354 1289 unsigned int page_size = self->page_size; 1290 + unsigned long offset; 1355 1291 char *ptr_a, *ptr_b, *ptr_c; 1356 1292 1357 1293 /* ··· 1374 1306 ASSERT_NE(ptr_b, MAP_FAILED); 1375 1307 /* Fault it in. */ 1376 1308 ptr_b[0] = 'x'; 1309 + 1310 + if (variant->forked) { 1311 + pid_t pid = do_fork(&self->procmap); 1312 + 1313 + ASSERT_NE(pid, -1); 1314 + if (pid != 0) 1315 + return; 1316 + } 1377 1317 1378 1318 /* 1379 1319 * Now move it out of the way so we can place VMAs A, C in position, ··· 1413 1337 &self->carveout[page_size + 3 * page_size]); 1414 1338 ASSERT_NE(ptr_b, MAP_FAILED); 1415 1339 1416 - /* The VMAs should have merged. */ 1340 + /* The VMAs should have merged, if not forked. */ 1417 1341 ASSERT_TRUE(find_vma_procmap(procmap, ptr_a)); 1418 1342 ASSERT_EQ(procmap->query.vma_start, (unsigned long)ptr_a); 1419 - ASSERT_EQ(procmap->query.vma_end, (unsigned long)ptr_a + 9 * page_size); 1343 + offset = variant->forked ? 3 * page_size : 9 * page_size; 1344 + ASSERT_EQ(procmap->query.vma_end, (unsigned long)ptr_a + offset); 1345 + 1346 + /* If forked, B and C should also not have merged. */ 1347 + if (variant->forked) { 1348 + ASSERT_TRUE(find_vma_procmap(procmap, ptr_b)); 1349 + ASSERT_EQ(procmap->query.vma_start, (unsigned long)ptr_b); 1350 + ASSERT_EQ(procmap->query.vma_end, (unsigned long)ptr_b + 3 * page_size); 1351 + } 1420 1352 } 1421 1353 1422 - TEST_F(merge, mremap_faulted_to_unfaulted_prev_faulted_next) 1354 + TEST_F(merge_with_fork, mremap_faulted_to_unfaulted_prev_faulted_next) 1423 1355 { 1424 1356 struct procmap_fd *procmap = &self->procmap; 1425 1357 unsigned int page_size = self->page_size; ··· 1457 1373 /* Fault it in. */ 1458 1374 ptr_bc[0] = 'x'; 1459 1375 1376 + if (variant->forked) { 1377 + pid_t pid = do_fork(&self->procmap); 1378 + 1379 + ASSERT_NE(pid, -1); 1380 + if (pid != 0) 1381 + return; 1382 + } 1383 + 1460 1384 /* 1461 1385 * Now move VMA B out the way (splitting VMA BC) so we can place VMA A 1462 1386 * in position, unfaulted, and leave the remainder of the VMA we just ··· 1489 1397 &self->carveout[page_size + 3 * page_size]); 1490 1398 ASSERT_NE(ptr_b, MAP_FAILED); 1491 1399 1492 - /* The VMAs should have merged. */ 1493 - ASSERT_TRUE(find_vma_procmap(procmap, ptr_a)); 1494 - ASSERT_EQ(procmap->query.vma_start, (unsigned long)ptr_a); 1495 - ASSERT_EQ(procmap->query.vma_end, (unsigned long)ptr_a + 9 * page_size); 1400 + /* The VMAs should have merged. A,B,C if unforked, B, C if forked. */ 1401 + if (variant->forked) { 1402 + ASSERT_TRUE(find_vma_procmap(procmap, ptr_b)); 1403 + ASSERT_EQ(procmap->query.vma_start, (unsigned long)ptr_b); 1404 + ASSERT_EQ(procmap->query.vma_end, (unsigned long)ptr_b + 6 * page_size); 1405 + } else { 1406 + ASSERT_TRUE(find_vma_procmap(procmap, ptr_a)); 1407 + ASSERT_EQ(procmap->query.vma_start, (unsigned long)ptr_a); 1408 + ASSERT_EQ(procmap->query.vma_end, (unsigned long)ptr_a + 9 * page_size); 1409 + } 1496 1410 } 1497 1411 1498 1412 TEST_HARNESS_MAIN