Skip to content

Commit 873dfd7

Browse files
sargunChristian Brauner
authored andcommitted
test: Add test for pidfd getfd
The following tests: * Fetch FD, and then compare via kcmp * Make sure getfd can be blocked by blocking ptrace_may_access * Making sure fetching bad FDs fails * Make sure trying to set flags to non-zero results in an EINVAL Signed-off-by: Sargun Dhillon <[email protected]> Acked-by: Christian Brauner <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Christian Brauner <[email protected]>
1 parent 9a2cef0 commit 873dfd7

File tree

4 files changed

+260
-1
lines changed

4 files changed

+260
-1
lines changed

tools/testing/selftests/pidfd/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,4 @@ pidfd_open_test
22
pidfd_poll_test
33
pidfd_test
44
pidfd_wait
5+
pidfd_getfd_test
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# SPDX-License-Identifier: GPL-2.0-only
22
CFLAGS += -g -I../../../../usr/include/ -pthread
33

4-
TEST_GEN_PROGS := pidfd_test pidfd_fdinfo_test pidfd_open_test pidfd_poll_test pidfd_wait
4+
TEST_GEN_PROGS := pidfd_test pidfd_fdinfo_test pidfd_open_test pidfd_poll_test pidfd_wait pidfd_getfd_test
55

66
include ../lib.mk
77

tools/testing/selftests/pidfd/pidfd.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,10 @@
3636
#define __NR_clone3 -1
3737
#endif
3838

39+
#ifndef __NR_pidfd_getfd
40+
#define __NR_pidfd_getfd -1
41+
#endif
42+
3943
/*
4044
* The kernel reserves 300 pids via RESERVED_PIDS in kernel/pid.c
4145
* That means, when it wraps around any pid < 300 will be skipped.
@@ -84,4 +88,9 @@ static inline int sys_pidfd_send_signal(int pidfd, int sig, siginfo_t *info,
8488
return syscall(__NR_pidfd_send_signal, pidfd, sig, info, flags);
8589
}
8690

91+
static inline int sys_pidfd_getfd(int pidfd, int fd, int flags)
92+
{
93+
return syscall(__NR_pidfd_getfd, pidfd, fd, flags);
94+
}
95+
8796
#endif /* __PIDFD_H */
Lines changed: 249 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,249 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
3+
#define _GNU_SOURCE
4+
#include <errno.h>
5+
#include <fcntl.h>
6+
#include <limits.h>
7+
#include <linux/types.h>
8+
#include <sched.h>
9+
#include <signal.h>
10+
#include <stdio.h>
11+
#include <stdlib.h>
12+
#include <string.h>
13+
#include <syscall.h>
14+
#include <sys/prctl.h>
15+
#include <sys/wait.h>
16+
#include <unistd.h>
17+
#include <sys/socket.h>
18+
#include <linux/kcmp.h>
19+
20+
#include "pidfd.h"
21+
#include "../kselftest.h"
22+
#include "../kselftest_harness.h"
23+
24+
/*
25+
* UNKNOWN_FD is an fd number that should never exist in the child, as it is
26+
* used to check the negative case.
27+
*/
28+
#define UNKNOWN_FD 111
29+
#define UID_NOBODY 65535
30+
31+
static int sys_kcmp(pid_t pid1, pid_t pid2, int type, unsigned long idx1,
32+
unsigned long idx2)
33+
{
34+
return syscall(__NR_kcmp, pid1, pid2, type, idx1, idx2);
35+
}
36+
37+
static int sys_memfd_create(const char *name, unsigned int flags)
38+
{
39+
return syscall(__NR_memfd_create, name, flags);
40+
}
41+
42+
static int __child(int sk, int memfd)
43+
{
44+
int ret;
45+
char buf;
46+
47+
/*
48+
* Ensure we don't leave around a bunch of orphaned children if our
49+
* tests fail.
50+
*/
51+
ret = prctl(PR_SET_PDEATHSIG, SIGKILL);
52+
if (ret) {
53+
fprintf(stderr, "%s: Child could not set DEATHSIG\n",
54+
strerror(errno));
55+
return -1;
56+
}
57+
58+
ret = send(sk, &memfd, sizeof(memfd), 0);
59+
if (ret != sizeof(memfd)) {
60+
fprintf(stderr, "%s: Child failed to send fd number\n",
61+
strerror(errno));
62+
return -1;
63+
}
64+
65+
/*
66+
* The fixture setup is completed at this point. The tests will run.
67+
*
68+
* This blocking recv enables the parent to message the child.
69+
* Either we will read 'P' off of the sk, indicating that we need
70+
* to disable ptrace, or we will read a 0, indicating that the other
71+
* side has closed the sk. This occurs during fixture teardown time,
72+
* indicating that the child should exit.
73+
*/
74+
while ((ret = recv(sk, &buf, sizeof(buf), 0)) > 0) {
75+
if (buf == 'P') {
76+
ret = prctl(PR_SET_DUMPABLE, 0);
77+
if (ret < 0) {
78+
fprintf(stderr,
79+
"%s: Child failed to disable ptrace\n",
80+
strerror(errno));
81+
return -1;
82+
}
83+
} else {
84+
fprintf(stderr, "Child received unknown command %c\n",
85+
buf);
86+
return -1;
87+
}
88+
ret = send(sk, &buf, sizeof(buf), 0);
89+
if (ret != 1) {
90+
fprintf(stderr, "%s: Child failed to ack\n",
91+
strerror(errno));
92+
return -1;
93+
}
94+
}
95+
if (ret < 0) {
96+
fprintf(stderr, "%s: Child failed to read from socket\n",
97+
strerror(errno));
98+
return -1;
99+
}
100+
101+
return 0;
102+
}
103+
104+
static int child(int sk)
105+
{
106+
int memfd, ret;
107+
108+
memfd = sys_memfd_create("test", 0);
109+
if (memfd < 0) {
110+
fprintf(stderr, "%s: Child could not create memfd\n",
111+
strerror(errno));
112+
ret = -1;
113+
} else {
114+
ret = __child(sk, memfd);
115+
close(memfd);
116+
}
117+
118+
close(sk);
119+
return ret;
120+
}
121+
122+
FIXTURE(child)
123+
{
124+
/*
125+
* remote_fd is the number of the FD which we are trying to retrieve
126+
* from the child.
127+
*/
128+
int remote_fd;
129+
/* pid points to the child which we are fetching FDs from */
130+
pid_t pid;
131+
/* pidfd is the pidfd of the child */
132+
int pidfd;
133+
/*
134+
* sk is our side of the socketpair used to communicate with the child.
135+
* When it is closed, the child will exit.
136+
*/
137+
int sk;
138+
};
139+
140+
FIXTURE_SETUP(child)
141+
{
142+
int ret, sk_pair[2];
143+
144+
ASSERT_EQ(0, socketpair(PF_LOCAL, SOCK_SEQPACKET, 0, sk_pair)) {
145+
TH_LOG("%s: failed to create socketpair", strerror(errno));
146+
}
147+
self->sk = sk_pair[0];
148+
149+
self->pid = fork();
150+
ASSERT_GE(self->pid, 0);
151+
152+
if (self->pid == 0) {
153+
close(sk_pair[0]);
154+
if (child(sk_pair[1]))
155+
_exit(EXIT_FAILURE);
156+
_exit(EXIT_SUCCESS);
157+
}
158+
159+
close(sk_pair[1]);
160+
161+
self->pidfd = sys_pidfd_open(self->pid, 0);
162+
ASSERT_GE(self->pidfd, 0);
163+
164+
/*
165+
* Wait for the child to complete setup. It'll send the remote memfd's
166+
* number when ready.
167+
*/
168+
ret = recv(sk_pair[0], &self->remote_fd, sizeof(self->remote_fd), 0);
169+
ASSERT_EQ(sizeof(self->remote_fd), ret);
170+
}
171+
172+
FIXTURE_TEARDOWN(child)
173+
{
174+
EXPECT_EQ(0, close(self->pidfd));
175+
EXPECT_EQ(0, close(self->sk));
176+
177+
EXPECT_EQ(0, wait_for_pid(self->pid));
178+
}
179+
180+
TEST_F(child, disable_ptrace)
181+
{
182+
int uid, fd;
183+
char c;
184+
185+
/*
186+
* Turn into nobody if we're root, to avoid CAP_SYS_PTRACE
187+
*
188+
* The tests should run in their own process, so even this test fails,
189+
* it shouldn't result in subsequent tests failing.
190+
*/
191+
uid = getuid();
192+
if (uid == 0)
193+
ASSERT_EQ(0, seteuid(UID_NOBODY));
194+
195+
ASSERT_EQ(1, send(self->sk, "P", 1, 0));
196+
ASSERT_EQ(1, recv(self->sk, &c, 1, 0));
197+
198+
fd = sys_pidfd_getfd(self->pidfd, self->remote_fd, 0);
199+
EXPECT_EQ(-1, fd);
200+
EXPECT_EQ(EPERM, errno);
201+
202+
if (uid == 0)
203+
ASSERT_EQ(0, seteuid(0));
204+
}
205+
206+
TEST_F(child, fetch_fd)
207+
{
208+
int fd, ret;
209+
210+
fd = sys_pidfd_getfd(self->pidfd, self->remote_fd, 0);
211+
ASSERT_GE(fd, 0);
212+
213+
EXPECT_EQ(0, sys_kcmp(getpid(), self->pid, KCMP_FILE, fd, self->remote_fd));
214+
215+
ret = fcntl(fd, F_GETFD);
216+
ASSERT_GE(ret, 0);
217+
EXPECT_GE(ret & FD_CLOEXEC, 0);
218+
219+
close(fd);
220+
}
221+
222+
TEST_F(child, test_unknown_fd)
223+
{
224+
int fd;
225+
226+
fd = sys_pidfd_getfd(self->pidfd, UNKNOWN_FD, 0);
227+
EXPECT_EQ(-1, fd) {
228+
TH_LOG("getfd succeeded while fetching unknown fd");
229+
};
230+
EXPECT_EQ(EBADF, errno) {
231+
TH_LOG("%s: getfd did not get EBADF", strerror(errno));
232+
}
233+
}
234+
235+
TEST(flags_set)
236+
{
237+
ASSERT_EQ(-1, sys_pidfd_getfd(0, 0, 1));
238+
EXPECT_EQ(errno, EINVAL);
239+
}
240+
241+
#if __NR_pidfd_getfd == -1
242+
int main(void)
243+
{
244+
fprintf(stderr, "__NR_pidfd_getfd undefined. The pidfd_getfd syscall is unavailable. Test aborting\n");
245+
return KSFT_SKIP;
246+
}
247+
#else
248+
TEST_HARNESS_MAIN
249+
#endif

0 commit comments

Comments
 (0)