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.

selftests/memfd: use IPC semaphore instead of SIGSTOP/SIGCONT

selftests/memfd: use IPC semaphore instead of SIGSTOP/SIGCONT

In order to synchronize new processes to test inheritance of memfd_noexec
sysctl, memfd_test sets up the sysctl with a value before creating the new
process. The new process then sends itself a SIGSTOP in order to wait for
the parent to flip the sysctl value and send a SIGCONT signal.

This would work as intended if it wasn't the fact that the new process is
being created with CLONE_NEWPID, which creates a new PID namespace and the
new process has PID 1 in this namespace. There're restrictions on sending
signals to PID 1 and, although it's relaxed for other than root PID
namespace, it's biting us here. In this specific case the SIGSTOP sent by
the new process is ignored (no error to kill() is returned) and it never
stops its execution. This is usually not noticiable as the parent usually
manages to set the new sysctl value before the child has a chance to run
and the test succeeds. But if you run the test in a loop, it eventually
reproduces:

while [ 1 ]; do ./memfd_test >log 2>&1 || break; done; cat log

So this patch replaces the SIGSTOP/SIGCONT synchronization with IPC
semaphore.

Link: https://lkml.kernel.org/r/a7776389-b3d6-4b18-b438-0b0e3ed1fd3b@work
Fixes: 6469b66e3f5a ("selftests: improve vm.memfd_noexec sysctl tests")
Signed-off-by: Aristeu Rozanski <aris@redhat.com>
Cc: Aleksa Sarai <cyphar@cyphar.com>
Cc: Shuah Khan <shuah@kernel.org>
Cc: liuye <liuye@kylinos.cn>
Cc: Lorenzo Stoakes <lorenzo.stoakes@oracle.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>

authored by

Aristeu Rozanski and committed by
Andrew Morton
b2433552 9333980c

+105 -8
+105 -8
tools/testing/selftests/memfd/memfd_test.c
··· 18 18 #include <sys/stat.h> 19 19 #include <sys/syscall.h> 20 20 #include <sys/wait.h> 21 + #include <sys/types.h> 22 + #include <sys/ipc.h> 23 + #include <sys/sem.h> 21 24 #include <unistd.h> 22 25 #include <ctype.h> 23 26 ··· 42 39 F_SEAL_EXEC) 43 40 44 41 #define MFD_NOEXEC_SEAL 0x0008U 42 + union semun { 43 + int val; 44 + struct semid_ds *buf; 45 + unsigned short int *array; 46 + struct seminfo *__buf; 47 + }; 48 + 49 + /* 50 + * we use semaphores on nested wait tasks due the use of CLONE_NEWPID: the 51 + * child will be PID 1 and can't send SIGSTOP to themselves due special 52 + * treatment of the init task, so the SIGSTOP/SIGCONT synchronization 53 + * approach can't be used here. 54 + */ 55 + #define SEM_KEY 0xdeadbeef 45 56 46 57 /* 47 58 * Default is not to test hugetlbfs ··· 1350 1333 1351 1334 static int sysctl_nested_wait(void *arg) 1352 1335 { 1353 - /* Wait for a SIGCONT. */ 1354 - kill(getpid(), SIGSTOP); 1336 + int sem = semget(SEM_KEY, 1, 0600); 1337 + struct sembuf sembuf; 1338 + 1339 + if (sem < 0) { 1340 + perror("semget:"); 1341 + abort(); 1342 + } 1343 + sembuf.sem_num = 0; 1344 + sembuf.sem_flg = 0; 1345 + sembuf.sem_op = 0; 1346 + 1347 + if (semop(sem, &sembuf, 1) < 0) { 1348 + perror("semop:"); 1349 + abort(); 1350 + } 1351 + 1355 1352 return sysctl_nested(arg); 1356 1353 } 1357 1354 ··· 1386 1355 1387 1356 static int sysctl_nested_child(void *arg) 1388 1357 { 1389 - int pid; 1358 + int pid, sem; 1359 + union semun semun; 1360 + struct sembuf sembuf; 1390 1361 1391 1362 printf("%s nested sysctl 0\n", memfd_str); 1392 1363 sysctl_assert_write("0"); ··· 1422 1389 test_sysctl_sysctl2_failset); 1423 1390 join_thread(pid); 1424 1391 1392 + sem = semget(SEM_KEY, 1, IPC_CREAT | 0600); 1393 + if (sem < 0) { 1394 + perror("semget:"); 1395 + return 1; 1396 + } 1397 + semun.val = 1; 1398 + sembuf.sem_op = -1; 1399 + sembuf.sem_flg = 0; 1400 + sembuf.sem_num = 0; 1401 + 1425 1402 /* Verify that the rules are actually inherited after fork. */ 1426 1403 printf("%s nested sysctl 0 -> 1 after fork\n", memfd_str); 1427 1404 sysctl_assert_write("0"); 1428 1405 1406 + if (semctl(sem, 0, SETVAL, semun) < 0) { 1407 + perror("semctl:"); 1408 + return 1; 1409 + } 1410 + 1429 1411 pid = spawn_thread(CLONE_NEWPID, sysctl_nested_wait, 1430 1412 test_sysctl_sysctl1_failset); 1431 1413 sysctl_assert_write("1"); 1432 - kill(pid, SIGCONT); 1414 + 1415 + /* Allow child to continue */ 1416 + if (semop(sem, &sembuf, 1) < 0) { 1417 + perror("semop:"); 1418 + return 1; 1419 + } 1433 1420 join_thread(pid); 1434 1421 1435 1422 printf("%s nested sysctl 0 -> 2 after fork\n", memfd_str); 1436 1423 sysctl_assert_write("0"); 1437 1424 1425 + if (semctl(sem, 0, SETVAL, semun) < 0) { 1426 + perror("semctl:"); 1427 + return 1; 1428 + } 1429 + 1438 1430 pid = spawn_thread(CLONE_NEWPID, sysctl_nested_wait, 1439 1431 test_sysctl_sysctl2_failset); 1440 1432 sysctl_assert_write("2"); 1441 - kill(pid, SIGCONT); 1433 + 1434 + /* Allow child to continue */ 1435 + if (semop(sem, &sembuf, 1) < 0) { 1436 + perror("semop:"); 1437 + return 1; 1438 + } 1442 1439 join_thread(pid); 1443 1440 1444 1441 /* ··· 1478 1415 */ 1479 1416 printf("%s nested sysctl 2 -> 1 after fork\n", memfd_str); 1480 1417 sysctl_assert_write("2"); 1418 + 1419 + if (semctl(sem, 0, SETVAL, semun) < 0) { 1420 + perror("semctl:"); 1421 + return 1; 1422 + } 1423 + 1481 1424 pid = spawn_thread(CLONE_NEWPID, sysctl_nested_wait, 1482 1425 test_sysctl_sysctl2); 1483 1426 sysctl_assert_write("1"); 1484 - kill(pid, SIGCONT); 1427 + 1428 + /* Allow child to continue */ 1429 + if (semop(sem, &sembuf, 1) < 0) { 1430 + perror("semop:"); 1431 + return 1; 1432 + } 1485 1433 join_thread(pid); 1486 1434 1487 1435 printf("%s nested sysctl 2 -> 0 after fork\n", memfd_str); 1488 1436 sysctl_assert_write("2"); 1437 + 1438 + if (semctl(sem, 0, SETVAL, semun) < 0) { 1439 + perror("semctl:"); 1440 + return 1; 1441 + } 1442 + 1489 1443 pid = spawn_thread(CLONE_NEWPID, sysctl_nested_wait, 1490 1444 test_sysctl_sysctl2); 1491 1445 sysctl_assert_write("0"); 1492 - kill(pid, SIGCONT); 1446 + 1447 + /* Allow child to continue */ 1448 + if (semop(sem, &sembuf, 1) < 0) { 1449 + perror("semop:"); 1450 + return 1; 1451 + } 1493 1452 join_thread(pid); 1494 1453 1495 1454 printf("%s nested sysctl 1 -> 0 after fork\n", memfd_str); 1496 1455 sysctl_assert_write("1"); 1456 + 1457 + if (semctl(sem, 0, SETVAL, semun) < 0) { 1458 + perror("semctl:"); 1459 + return 1; 1460 + } 1461 + 1497 1462 pid = spawn_thread(CLONE_NEWPID, sysctl_nested_wait, 1498 1463 test_sysctl_sysctl1); 1499 1464 sysctl_assert_write("0"); 1500 - kill(pid, SIGCONT); 1465 + /* Allow child to continue */ 1466 + if (semop(sem, &sembuf, 1) < 0) { 1467 + perror("semop:"); 1468 + return 1; 1469 + } 1501 1470 join_thread(pid); 1471 + 1472 + semctl(sem, 0, IPC_RMID); 1502 1473 1503 1474 return 0; 1504 1475 }