Skip to content

Commit b243355

Browse files
aristeuakpm00
authored andcommitted
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: 6469b66 ("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>
1 parent 9333980 commit b243355

1 file changed

Lines changed: 105 additions & 8 deletions

File tree

tools/testing/selftests/memfd/memfd_test.c

Lines changed: 105 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@
1818
#include <sys/stat.h>
1919
#include <sys/syscall.h>
2020
#include <sys/wait.h>
21+
#include <sys/types.h>
22+
#include <sys/ipc.h>
23+
#include <sys/sem.h>
2124
#include <unistd.h>
2225
#include <ctype.h>
2326

@@ -39,6 +42,20 @@
3942
F_SEAL_EXEC)
4043

4144
#define MFD_NOEXEC_SEAL 0x0008U
45+
union semun {
46+
int val;
47+
struct semid_ds *buf;
48+
unsigned short int *array;
49+
struct seminfo *__buf;
50+
};
51+
52+
/*
53+
* we use semaphores on nested wait tasks due the use of CLONE_NEWPID: the
54+
* child will be PID 1 and can't send SIGSTOP to themselves due special
55+
* treatment of the init task, so the SIGSTOP/SIGCONT synchronization
56+
* approach can't be used here.
57+
*/
58+
#define SEM_KEY 0xdeadbeef
4259

4360
/*
4461
* Default is not to test hugetlbfs
@@ -1333,8 +1350,22 @@ static int sysctl_nested(void *arg)
13331350

13341351
static int sysctl_nested_wait(void *arg)
13351352
{
1336-
/* Wait for a SIGCONT. */
1337-
kill(getpid(), SIGSTOP);
1353+
int sem = semget(SEM_KEY, 1, 0600);
1354+
struct sembuf sembuf;
1355+
1356+
if (sem < 0) {
1357+
perror("semget:");
1358+
abort();
1359+
}
1360+
sembuf.sem_num = 0;
1361+
sembuf.sem_flg = 0;
1362+
sembuf.sem_op = 0;
1363+
1364+
if (semop(sem, &sembuf, 1) < 0) {
1365+
perror("semop:");
1366+
abort();
1367+
}
1368+
13381369
return sysctl_nested(arg);
13391370
}
13401371

@@ -1355,7 +1386,9 @@ static void test_sysctl_sysctl2_failset(void)
13551386

13561387
static int sysctl_nested_child(void *arg)
13571388
{
1358-
int pid;
1389+
int pid, sem;
1390+
union semun semun;
1391+
struct sembuf sembuf;
13591392

13601393
printf("%s nested sysctl 0\n", memfd_str);
13611394
sysctl_assert_write("0");
@@ -1389,23 +1422,53 @@ static int sysctl_nested_child(void *arg)
13891422
test_sysctl_sysctl2_failset);
13901423
join_thread(pid);
13911424

1425+
sem = semget(SEM_KEY, 1, IPC_CREAT | 0600);
1426+
if (sem < 0) {
1427+
perror("semget:");
1428+
return 1;
1429+
}
1430+
semun.val = 1;
1431+
sembuf.sem_op = -1;
1432+
sembuf.sem_flg = 0;
1433+
sembuf.sem_num = 0;
1434+
13921435
/* Verify that the rules are actually inherited after fork. */
13931436
printf("%s nested sysctl 0 -> 1 after fork\n", memfd_str);
13941437
sysctl_assert_write("0");
13951438

1439+
if (semctl(sem, 0, SETVAL, semun) < 0) {
1440+
perror("semctl:");
1441+
return 1;
1442+
}
1443+
13961444
pid = spawn_thread(CLONE_NEWPID, sysctl_nested_wait,
13971445
test_sysctl_sysctl1_failset);
13981446
sysctl_assert_write("1");
1399-
kill(pid, SIGCONT);
1447+
1448+
/* Allow child to continue */
1449+
if (semop(sem, &sembuf, 1) < 0) {
1450+
perror("semop:");
1451+
return 1;
1452+
}
14001453
join_thread(pid);
14011454

14021455
printf("%s nested sysctl 0 -> 2 after fork\n", memfd_str);
14031456
sysctl_assert_write("0");
14041457

1458+
if (semctl(sem, 0, SETVAL, semun) < 0) {
1459+
perror("semctl:");
1460+
return 1;
1461+
}
1462+
14051463
pid = spawn_thread(CLONE_NEWPID, sysctl_nested_wait,
14061464
test_sysctl_sysctl2_failset);
14071465
sysctl_assert_write("2");
1408-
kill(pid, SIGCONT);
1466+
1467+
/* Allow child to continue */
1468+
if (semop(sem, &sembuf, 1) < 0) {
1469+
perror("semop:");
1470+
return 1;
1471+
}
14091472
join_thread(pid);
14101473

14111474
/*
@@ -1415,28 +1478,62 @@ static int sysctl_nested_child(void *arg)
14151478
*/
14161479
printf("%s nested sysctl 2 -> 1 after fork\n", memfd_str);
14171480
sysctl_assert_write("2");
1481+
1482+
if (semctl(sem, 0, SETVAL, semun) < 0) {
1483+
perror("semctl:");
1484+
return 1;
1485+
}
1486+
14181487
pid = spawn_thread(CLONE_NEWPID, sysctl_nested_wait,
14191488
test_sysctl_sysctl2);
14201489
sysctl_assert_write("1");
1421-
kill(pid, SIGCONT);
1490+
1491+
/* Allow child to continue */
1492+
if (semop(sem, &sembuf, 1) < 0) {
1493+
perror("semop:");
1494+
return 1;
1495+
}
14221496
join_thread(pid);
14231497

14241498
printf("%s nested sysctl 2 -> 0 after fork\n", memfd_str);
14251499
sysctl_assert_write("2");
1500+
1501+
if (semctl(sem, 0, SETVAL, semun) < 0) {
1502+
perror("semctl:");
1503+
return 1;
1504+
}
1505+
14261506
pid = spawn_thread(CLONE_NEWPID, sysctl_nested_wait,
14271507
test_sysctl_sysctl2);
14281508
sysctl_assert_write("0");
1429-
kill(pid, SIGCONT);
1509+
1510+
/* Allow child to continue */
1511+
if (semop(sem, &sembuf, 1) < 0) {
1512+
perror("semop:");
1513+
return 1;
1514+
}
14301515
join_thread(pid);
14311516

14321517
printf("%s nested sysctl 1 -> 0 after fork\n", memfd_str);
14331518
sysctl_assert_write("1");
1519+
1520+
if (semctl(sem, 0, SETVAL, semun) < 0) {
1521+
perror("semctl:");
1522+
return 1;
1523+
}
1524+
14341525
pid = spawn_thread(CLONE_NEWPID, sysctl_nested_wait,
14351526
test_sysctl_sysctl1);
14361527
sysctl_assert_write("0");
1437-
kill(pid, SIGCONT);
1528+
/* Allow child to continue */
1529+
if (semop(sem, &sembuf, 1) < 0) {
1530+
perror("semop:");
1531+
return 1;
1532+
}
14381533
join_thread(pid);
14391534

1535+
semctl(sem, 0, IPC_RMID);
1536+
14401537
return 0;
14411538
}
14421539

0 commit comments

Comments
 (0)