Skip to content

Commit 861bdc6

Browse files
mihalicynbrauner
authored andcommitted
selftests: net: extend SCM_PIDFD test to cover stale pidfds
Extend SCM_PIDFD test scenarios to also cover dead task's pidfd retrieval and reading its exit info. Cc: [email protected] Cc: [email protected] Cc: [email protected] Cc: Shuah Khan <[email protected]> Cc: David S. Miller <[email protected]> Cc: Eric Dumazet <[email protected]> Cc: Jakub Kicinski <[email protected]> Cc: Paolo Abeni <[email protected]> Cc: Simon Horman <[email protected]> Cc: Christian Brauner <[email protected]> Cc: Kuniyuki Iwashima <[email protected]> Cc: Lennart Poettering <[email protected]> Cc: Luca Boccassi <[email protected]> Cc: David Rheinsberg <[email protected]> Signed-off-by: Alexander Mikhalitsyn <[email protected]> Link: https://lore.kernel.org/[email protected] Reviewed-by: Christian Brauner <[email protected]> Signed-off-by: Christian Brauner <[email protected]>
1 parent c679d17 commit 861bdc6

File tree

1 file changed

+173
-44
lines changed

1 file changed

+173
-44
lines changed

tools/testing/selftests/net/af_unix/scm_pidfd.c

Lines changed: 173 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#include <sys/types.h>
1616
#include <sys/wait.h>
1717

18+
#include "../../pidfd/pidfd.h"
1819
#include "../../kselftest_harness.h"
1920

2021
#define clean_errno() (errno == 0 ? "None" : strerror(errno))
@@ -26,6 +27,8 @@
2627
#define SCM_PIDFD 0x04
2728
#endif
2829

30+
#define CHILD_EXIT_CODE_OK 123
31+
2932
static void child_die()
3033
{
3134
exit(1);
@@ -126,16 +129,65 @@ static pid_t get_pid_from_fdinfo_file(int pidfd, const char *key, size_t keylen)
126129
return result;
127130
}
128131

132+
struct cmsg_data {
133+
struct ucred *ucred;
134+
int *pidfd;
135+
};
136+
137+
static int parse_cmsg(struct msghdr *msg, struct cmsg_data *res)
138+
{
139+
struct cmsghdr *cmsg;
140+
int data = 0;
141+
142+
if (msg->msg_flags & (MSG_TRUNC | MSG_CTRUNC)) {
143+
log_err("recvmsg: truncated");
144+
return 1;
145+
}
146+
147+
for (cmsg = CMSG_FIRSTHDR(msg); cmsg != NULL;
148+
cmsg = CMSG_NXTHDR(msg, cmsg)) {
149+
if (cmsg->cmsg_level == SOL_SOCKET &&
150+
cmsg->cmsg_type == SCM_PIDFD) {
151+
if (cmsg->cmsg_len < sizeof(*res->pidfd)) {
152+
log_err("CMSG parse: SCM_PIDFD wrong len");
153+
return 1;
154+
}
155+
156+
res->pidfd = (void *)CMSG_DATA(cmsg);
157+
}
158+
159+
if (cmsg->cmsg_level == SOL_SOCKET &&
160+
cmsg->cmsg_type == SCM_CREDENTIALS) {
161+
if (cmsg->cmsg_len < sizeof(*res->ucred)) {
162+
log_err("CMSG parse: SCM_CREDENTIALS wrong len");
163+
return 1;
164+
}
165+
166+
res->ucred = (void *)CMSG_DATA(cmsg);
167+
}
168+
}
169+
170+
if (!res->pidfd) {
171+
log_err("CMSG parse: SCM_PIDFD not found");
172+
return 1;
173+
}
174+
175+
if (!res->ucred) {
176+
log_err("CMSG parse: SCM_CREDENTIALS not found");
177+
return 1;
178+
}
179+
180+
return 0;
181+
}
182+
129183
static int cmsg_check(int fd)
130184
{
131185
struct msghdr msg = { 0 };
132-
struct cmsghdr *cmsg;
186+
struct cmsg_data res;
133187
struct iovec iov;
134-
struct ucred *ucred = NULL;
135188
int data = 0;
136189
char control[CMSG_SPACE(sizeof(struct ucred)) +
137190
CMSG_SPACE(sizeof(int))] = { 0 };
138-
int *pidfd = NULL;
139191
pid_t parent_pid;
140192
int err;
141193

@@ -158,53 +210,99 @@ static int cmsg_check(int fd)
158210
return 1;
159211
}
160212

161-
for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL;
162-
cmsg = CMSG_NXTHDR(&msg, cmsg)) {
163-
if (cmsg->cmsg_level == SOL_SOCKET &&
164-
cmsg->cmsg_type == SCM_PIDFD) {
165-
if (cmsg->cmsg_len < sizeof(*pidfd)) {
166-
log_err("CMSG parse: SCM_PIDFD wrong len");
167-
return 1;
168-
}
213+
/* send(pfd, "x", sizeof(char), 0) */
214+
if (data != 'x') {
215+
log_err("recvmsg: data corruption");
216+
return 1;
217+
}
169218

170-
pidfd = (void *)CMSG_DATA(cmsg);
171-
}
219+
if (parse_cmsg(&msg, &res)) {
220+
log_err("CMSG parse: parse_cmsg() failed");
221+
return 1;
222+
}
172223

173-
if (cmsg->cmsg_level == SOL_SOCKET &&
174-
cmsg->cmsg_type == SCM_CREDENTIALS) {
175-
if (cmsg->cmsg_len < sizeof(*ucred)) {
176-
log_err("CMSG parse: SCM_CREDENTIALS wrong len");
177-
return 1;
178-
}
224+
/* pidfd from SCM_PIDFD should point to the parent process PID */
225+
parent_pid =
226+
get_pid_from_fdinfo_file(*res.pidfd, "Pid:", sizeof("Pid:") - 1);
227+
if (parent_pid != getppid()) {
228+
log_err("wrong SCM_PIDFD %d != %d", parent_pid, getppid());
229+
close(*res.pidfd);
230+
return 1;
231+
}
179232

180-
ucred = (void *)CMSG_DATA(cmsg);
181-
}
233+
close(*res.pidfd);
234+
return 0;
235+
}
236+
237+
static int cmsg_check_dead(int fd, int expected_pid)
238+
{
239+
int err;
240+
struct msghdr msg = { 0 };
241+
struct cmsg_data res;
242+
struct iovec iov;
243+
int data = 0;
244+
char control[CMSG_SPACE(sizeof(struct ucred)) +
245+
CMSG_SPACE(sizeof(int))] = { 0 };
246+
pid_t client_pid;
247+
struct pidfd_info info = {
248+
.mask = PIDFD_INFO_EXIT,
249+
};
250+
251+
iov.iov_base = &data;
252+
iov.iov_len = sizeof(data);
253+
254+
msg.msg_iov = &iov;
255+
msg.msg_iovlen = 1;
256+
msg.msg_control = control;
257+
msg.msg_controllen = sizeof(control);
258+
259+
err = recvmsg(fd, &msg, 0);
260+
if (err < 0) {
261+
log_err("recvmsg");
262+
return 1;
182263
}
183264

184-
/* send(pfd, "x", sizeof(char), 0) */
185-
if (data != 'x') {
265+
if (msg.msg_flags & (MSG_TRUNC | MSG_CTRUNC)) {
266+
log_err("recvmsg: truncated");
267+
return 1;
268+
}
269+
270+
/* send(cfd, "y", sizeof(char), 0) */
271+
if (data != 'y') {
186272
log_err("recvmsg: data corruption");
187273
return 1;
188274
}
189275

190-
if (!pidfd) {
191-
log_err("CMSG parse: SCM_PIDFD not found");
276+
if (parse_cmsg(&msg, &res)) {
277+
log_err("CMSG parse: parse_cmsg() failed");
192278
return 1;
193279
}
194280

195-
if (!ucred) {
196-
log_err("CMSG parse: SCM_CREDENTIALS not found");
281+
/*
282+
* pidfd from SCM_PIDFD should point to the client_pid.
283+
* Let's read exit information and check if it's what
284+
* we expect to see.
285+
*/
286+
if (ioctl(*res.pidfd, PIDFD_GET_INFO, &info)) {
287+
log_err("%s: ioctl(PIDFD_GET_INFO) failed", __func__);
288+
close(*res.pidfd);
197289
return 1;
198290
}
199291

200-
/* pidfd from SCM_PIDFD should point to the parent process PID */
201-
parent_pid =
202-
get_pid_from_fdinfo_file(*pidfd, "Pid:", sizeof("Pid:") - 1);
203-
if (parent_pid != getppid()) {
204-
log_err("wrong SCM_PIDFD %d != %d", parent_pid, getppid());
292+
if (!(info.mask & PIDFD_INFO_EXIT)) {
293+
log_err("%s: No exit information from ioctl(PIDFD_GET_INFO)", __func__);
294+
close(*res.pidfd);
205295
return 1;
206296
}
207297

298+
err = WIFEXITED(info.exit_code) ? WEXITSTATUS(info.exit_code) : 1;
299+
if (err != CHILD_EXIT_CODE_OK) {
300+
log_err("%s: wrong exit_code %d != %d", __func__, err, CHILD_EXIT_CODE_OK);
301+
close(*res.pidfd);
302+
return 1;
303+
}
304+
305+
close(*res.pidfd);
208306
return 0;
209307
}
210308

@@ -291,6 +389,24 @@ static void fill_sockaddr(struct sock_addr *addr, bool abstract)
291389
memcpy(sun_path_buf, addr->sock_name, strlen(addr->sock_name));
292390
}
293391

392+
static int sk_enable_cred_pass(int sk)
393+
{
394+
int on = 0;
395+
396+
on = 1;
397+
if (setsockopt(sk, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on))) {
398+
log_err("Failed to set SO_PASSCRED");
399+
return 1;
400+
}
401+
402+
if (setsockopt(sk, SOL_SOCKET, SO_PASSPIDFD, &on, sizeof(on))) {
403+
log_err("Failed to set SO_PASSPIDFD");
404+
return 1;
405+
}
406+
407+
return 0;
408+
}
409+
294410
static void client(FIXTURE_DATA(scm_pidfd) *self,
295411
const FIXTURE_VARIANT(scm_pidfd) *variant)
296412
{
@@ -299,7 +415,6 @@ static void client(FIXTURE_DATA(scm_pidfd) *self,
299415
struct ucred peer_cred;
300416
int peer_pidfd;
301417
pid_t peer_pid;
302-
int on = 0;
303418

304419
cfd = socket(AF_UNIX, variant->type, 0);
305420
if (cfd < 0) {
@@ -322,14 +437,8 @@ static void client(FIXTURE_DATA(scm_pidfd) *self,
322437
child_die();
323438
}
324439

325-
on = 1;
326-
if (setsockopt(cfd, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on))) {
327-
log_err("Failed to set SO_PASSCRED");
328-
child_die();
329-
}
330-
331-
if (setsockopt(cfd, SOL_SOCKET, SO_PASSPIDFD, &on, sizeof(on))) {
332-
log_err("Failed to set SO_PASSPIDFD");
440+
if (sk_enable_cred_pass(cfd)) {
441+
log_err("sk_enable_cred_pass() failed");
333442
child_die();
334443
}
335444

@@ -340,6 +449,12 @@ static void client(FIXTURE_DATA(scm_pidfd) *self,
340449
child_die();
341450
}
342451

452+
/* send something to the parent so it can receive SCM_PIDFD too and validate it */
453+
if (send(cfd, "y", sizeof(char), 0) == -1) {
454+
log_err("Failed to send(cfd, \"y\", sizeof(char), 0)");
455+
child_die();
456+
}
457+
343458
/* skip further for SOCK_DGRAM as it's not applicable */
344459
if (variant->type == SOCK_DGRAM)
345460
return;
@@ -398,7 +513,13 @@ TEST_F(scm_pidfd, test)
398513
close(self->server);
399514
close(self->startup_pipe[0]);
400515
client(self, variant);
401-
exit(0);
516+
517+
/*
518+
* It's a bit unusual, but in case of success we return non-zero
519+
* exit code (CHILD_EXIT_CODE_OK) and then we expect to read it
520+
* from ioctl(PIDFD_GET_INFO) in cmsg_check_dead().
521+
*/
522+
exit(CHILD_EXIT_CODE_OK);
402523
}
403524
close(self->startup_pipe[1]);
404525

@@ -421,9 +542,17 @@ TEST_F(scm_pidfd, test)
421542
ASSERT_NE(-1, err);
422543
}
423544

424-
close(pfd);
425545
waitpid(self->client_pid, &child_status, 0);
426-
ASSERT_EQ(0, WIFEXITED(child_status) ? WEXITSTATUS(child_status) : 1);
546+
/* see comment before exit(CHILD_EXIT_CODE_OK) */
547+
ASSERT_EQ(CHILD_EXIT_CODE_OK, WIFEXITED(child_status) ? WEXITSTATUS(child_status) : 1);
548+
549+
err = sk_enable_cred_pass(pfd);
550+
ASSERT_EQ(0, err);
551+
552+
err = cmsg_check_dead(pfd, self->client_pid);
553+
ASSERT_EQ(0, err);
554+
555+
close(pfd);
427556
}
428557

429558
TEST_HARNESS_MAIN

0 commit comments

Comments
 (0)