Skip to content

Commit 643e160

Browse files
bsach64avagin
authored andcommitted
zdtm: Check dead pidfd is restored correctly
After, C/R of pidfds that point to dead processes their inodes might change. But if two pidfds point to same dead process they should continue to do so after C/R. This test ensures that this happens by calling `statx()` on pidfds after C/R and then comparing their inode numbers. Support for comparing pidfds by using `statx()` and inode numbers was introduced alongside pidfs. So if `f_type` of pidfd is not equal to `PID_FS_MAGIC` then we skip this test. signed-off-by: Bhavik Sachdev <[email protected]>
1 parent 032a822 commit 643e160

File tree

2 files changed

+245
-0
lines changed

2 files changed

+245
-0
lines changed

test/zdtm/static/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ TST_NOFILE := \
5454
shm-mp \
5555
ptrace_sig \
5656
pidfd_self \
57+
pidfd_dead \
5758
pidfd_child \
5859
pidfd_kill \
5960
pipe00 \

test/zdtm/static/pidfd_dead.c

Lines changed: 244 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,244 @@
1+
#include <sys/statfs.h>
2+
#include <sys/syscall.h>
3+
#include <unistd.h>
4+
#include <sys/wait.h>
5+
#include <fcntl.h>
6+
#include <sys/stat.h>
7+
8+
#include "zdtmtst.h"
9+
10+
const char *test_doc = "Check C/R of pidfds that point to dead processes\n";
11+
const char *test_author = "Bhavik Sachdev <[email protected]>";
12+
13+
#ifndef PID_FS_MAGIC
14+
#define PID_FS_MAGIC 0x50494446
15+
#endif
16+
17+
/*
18+
* main
19+
* `- child
20+
* `- grandchild
21+
*
22+
* main opens a pidfd for both child and grandchild.
23+
* Before C/R we kill both child and grandchild.
24+
* We end up with two unique dead pidfds.
25+
*/
26+
27+
static long get_fs_type(int lfd)
28+
{
29+
struct statfs fst;
30+
31+
if (fstatfs(lfd, &fst)) {
32+
return -1;
33+
}
34+
return fst.f_type;
35+
}
36+
37+
static int pidfd_open(pid_t pid, unsigned int flags)
38+
{
39+
return syscall(__NR_pidfd_open, pid, flags);
40+
}
41+
42+
static int pidfd_send_signal(int pidfd, int sig, siginfo_t* info, unsigned int flags)
43+
{
44+
return syscall(__NR_pidfd_send_signal, pidfd, sig, info, flags);
45+
}
46+
47+
static int open_pidfd_pair(int pidfd[2], int pid)
48+
{
49+
pidfd[0] = pidfd_open(pid, 0);
50+
if (pidfd[0] < 0) {
51+
pr_perror("pidfd_open() failed");
52+
return 1;
53+
}
54+
55+
pidfd[1] = pidfd_open(pid, 0);
56+
if (pidfd[1] < 0) {
57+
close(pidfd[0]);
58+
pr_perror("pidfd_open() failed");
59+
return 1;
60+
}
61+
return 0;
62+
}
63+
64+
static int compare_pidfds(int pidfd[2])
65+
{
66+
/*
67+
* After linux 6.9 we can compare inode numbers
68+
* to determine if two pidfds point to the same process.
69+
* While the inode number may change before and after C/R
70+
* pidfds pointing to the same pid should have the same inode number.
71+
*/
72+
struct statx stats[2];
73+
statx(pidfd[0], "", AT_EMPTY_PATH, STATX_ALL, &stats[0]);
74+
statx(pidfd[1], "", AT_EMPTY_PATH, STATX_ALL, &stats[1]);
75+
if (stats[0].stx_ino != stats[1].stx_ino)
76+
return 1;
77+
return 0;
78+
}
79+
80+
static int check_for_pidfs(void)
81+
{
82+
long type;
83+
int pidfd = pidfd_open(getpid(), 0);
84+
if (pidfd < 0) {
85+
pr_perror("pidfd open() failed");
86+
return -1;
87+
}
88+
type = get_fs_type(pidfd);
89+
close(pidfd);
90+
return type == PID_FS_MAGIC;
91+
}
92+
93+
int main(int argc, char* argv[])
94+
{
95+
#define READ 0
96+
#define WRITE 1
97+
98+
int child, ret, gchild, p[2], status;
99+
int cpidfd[2], gpidfd[2];
100+
struct statx stats[2];
101+
102+
test_init(argc, argv);
103+
104+
ret = check_for_pidfs();
105+
if (ret < 0)
106+
return 1;
107+
108+
if (ret == 0) {
109+
test_daemon();
110+
test_waitsig();
111+
skip("Test requires pidfs. skipping...");
112+
pass();
113+
return 0;
114+
}
115+
116+
if (pipe(p)) {
117+
pr_perror("pipe");
118+
return 1;
119+
}
120+
121+
child = test_fork();
122+
if (child < 0) {
123+
pr_perror("fork");
124+
return 1;
125+
} else if (child == 0) {
126+
int gchild = test_fork();
127+
close(p[READ]);
128+
if (gchild < 0) {
129+
pr_perror("fork");
130+
return 1;
131+
} else if (gchild == 0) {
132+
close(p[WRITE]);
133+
while(1)
134+
sleep(1000);
135+
} else {
136+
if (write(p[WRITE], &gchild, sizeof(int)) != sizeof(int)) {
137+
pr_perror("write");
138+
return 1;
139+
}
140+
close(p[WRITE]);
141+
if (waitpid(gchild, &status, 0) != gchild) {
142+
pr_perror("waitpid");
143+
return 1;
144+
}
145+
146+
if (!WIFSIGNALED(status)) {
147+
fail("Expected grandchild to be terminated by a signal");
148+
return 1;
149+
}
150+
151+
if (WTERMSIG(status) != SIGKILL) {
152+
fail("Expected grandchild to be terminated by SIGKILL");
153+
return 1;
154+
}
155+
156+
return 0;
157+
}
158+
}
159+
160+
ret = open_pidfd_pair(cpidfd, child);
161+
if (ret)
162+
return 1;
163+
164+
close(p[WRITE]);
165+
if (read(p[READ], &gchild, sizeof(int)) != sizeof(int)) {
166+
pr_perror("write");
167+
return 1;
168+
}
169+
close(p[READ]);
170+
171+
ret = open_pidfd_pair(gpidfd, gchild);
172+
if (ret)
173+
return 1;
174+
175+
/*
176+
* We kill grandchild and child processes only after opening pidfds.
177+
*/
178+
if (pidfd_send_signal(gpidfd[0], SIGKILL, NULL, 0)) {
179+
pr_perror("pidfd_send_signal");
180+
goto fail_close;
181+
}
182+
183+
if (waitpid(child, &status, 0) != child) {
184+
pr_perror("waitpid");
185+
goto fail_close;
186+
}
187+
188+
if (!WIFEXITED(status)) {
189+
fail("Expected child to exit normally");
190+
goto fail_close;
191+
}
192+
193+
if (WEXITSTATUS(status) != 0) {
194+
fail("Expected child to exit with 0");
195+
goto fail_close;
196+
}
197+
usleep(1000);
198+
199+
if (kill(gchild, 0) != -1 && errno != ESRCH) {
200+
fail("Expected grand child to not exist");
201+
goto fail_close;
202+
}
203+
204+
if (kill(child, 0) != -1 && errno != ESRCH) {
205+
fail("Expected child to not exist");
206+
goto fail_close;
207+
}
208+
209+
test_daemon();
210+
test_waitsig();
211+
212+
ret = compare_pidfds(cpidfd);
213+
if (ret) {
214+
fail("inodes not same for same pid");
215+
goto fail_close;
216+
}
217+
218+
ret = compare_pidfds(gpidfd);
219+
if (ret) {
220+
fail("inodes not same for same pid");
221+
goto fail_close;
222+
}
223+
224+
statx(cpidfd[0], "", AT_EMPTY_PATH, STATX_ALL, &stats[0]);
225+
statx(gpidfd[0], "", AT_EMPTY_PATH, STATX_ALL, &stats[1]);
226+
if (stats[0].stx_ino == stats[1].stx_ino) {
227+
fail("pidfds pointing to diff pids should have diff inodes");
228+
goto fail_close;
229+
}
230+
231+
pass();
232+
close(cpidfd[0]);
233+
close(cpidfd[1]);
234+
close(gpidfd[0]);
235+
close(gpidfd[1]);
236+
return 0;
237+
238+
fail_close:
239+
close(cpidfd[0]);
240+
close(cpidfd[1]);
241+
close(gpidfd[0]);
242+
close(gpidfd[1]);
243+
return 1;
244+
}

0 commit comments

Comments
 (0)