1 // SPDX-License-Identifier: GPL-2.0 OR MIT
8 #include <sys/socket.h>
9 #include <linux/socket.h>
14 #include <sys/signal.h>
15 #include <sys/types.h>
18 #include "../../kselftest_harness.h"
20 #define clean_errno() (errno == 0 ? "None" : strerror(errno))
21 #define log_err(MSG, ...) \
22 fprintf(stderr, "(%s:%d: errno: %s) " MSG "\n", __FILE__, __LINE__, \
23 clean_errno(), ##__VA_ARGS__)
26 #define SCM_PIDFD 0x04
29 static void child_die()
34 static int safe_int(const char *numstr
, int *converted
)
40 sli
= strtol(numstr
, &err
, 0);
41 if (errno
== ERANGE
&& (sli
== LONG_MAX
|| sli
== LONG_MIN
))
44 if (errno
!= 0 && sli
== 0)
47 if (err
== numstr
|| *err
!= '\0')
50 if (sli
> INT_MAX
|| sli
< INT_MIN
)
53 *converted
= (int)sli
;
57 static int char_left_gc(const char *buffer
, size_t len
)
61 for (i
= 0; i
< len
; i
++) {
62 if (buffer
[i
] == ' ' || buffer
[i
] == '\t')
71 static int char_right_gc(const char *buffer
, size_t len
)
75 for (i
= len
- 1; i
>= 0; i
--) {
76 if (buffer
[i
] == ' ' || buffer
[i
] == '\t' ||
77 buffer
[i
] == '\n' || buffer
[i
] == '\0')
86 static char *trim_whitespace_in_place(char *buffer
)
88 buffer
+= char_left_gc(buffer
, strlen(buffer
));
89 buffer
[char_right_gc(buffer
, strlen(buffer
))] = '\0';
93 /* borrowed (with all helpers) from pidfd/pidfd_open_test.c */
94 static pid_t
get_pid_from_fdinfo_file(int pidfd
, const char *key
, size_t keylen
)
103 snprintf(path
, sizeof(path
), "/proc/self/fdinfo/%d", pidfd
);
105 f
= fopen(path
, "re");
109 while (getline(&line
, &n
, f
) != -1) {
112 if (strncmp(line
, key
, keylen
))
115 numstr
= trim_whitespace_in_place(line
+ 4);
116 ret
= safe_int(numstr
, &result
);
129 static int cmsg_check(int fd
)
131 struct msghdr msg
= { 0 };
132 struct cmsghdr
*cmsg
;
134 struct ucred
*ucred
= NULL
;
136 char control
[CMSG_SPACE(sizeof(struct ucred
)) +
137 CMSG_SPACE(sizeof(int))] = { 0 };
142 iov
.iov_base
= &data
;
143 iov
.iov_len
= sizeof(data
);
147 msg
.msg_control
= control
;
148 msg
.msg_controllen
= sizeof(control
);
150 err
= recvmsg(fd
, &msg
, 0);
156 if (msg
.msg_flags
& (MSG_TRUNC
| MSG_CTRUNC
)) {
157 log_err("recvmsg: truncated");
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");
170 pidfd
= (void *)CMSG_DATA(cmsg
);
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");
180 ucred
= (void *)CMSG_DATA(cmsg
);
184 /* send(pfd, "x", sizeof(char), 0) */
186 log_err("recvmsg: data corruption");
191 log_err("CMSG parse: SCM_PIDFD not found");
196 log_err("CMSG parse: SCM_CREDENTIALS not found");
200 /* pidfd from SCM_PIDFD should point to the parent process 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());
213 struct sockaddr_un listen_addr
;
222 struct sock_addr server_addr
;
223 struct sock_addr
*client_addr
;
226 FIXTURE_VARIANT(scm_pidfd
)
232 FIXTURE_VARIANT_ADD(scm_pidfd
, stream_pathname
)
238 FIXTURE_VARIANT_ADD(scm_pidfd
, stream_abstract
)
244 FIXTURE_VARIANT_ADD(scm_pidfd
, dgram_pathname
)
250 FIXTURE_VARIANT_ADD(scm_pidfd
, dgram_abstract
)
256 FIXTURE_SETUP(scm_pidfd
)
258 self
->client_addr
= mmap(NULL
, sizeof(*self
->client_addr
), PROT_READ
| PROT_WRITE
,
259 MAP_SHARED
| MAP_ANONYMOUS
, -1, 0);
260 ASSERT_NE(MAP_FAILED
, self
->client_addr
);
263 FIXTURE_TEARDOWN(scm_pidfd
)
267 kill(self
->client_pid
, SIGKILL
);
268 waitpid(self
->client_pid
, NULL
, 0);
270 if (!variant
->abstract
) {
271 unlink(self
->server_addr
.sock_name
);
272 unlink(self
->client_addr
->sock_name
);
276 static void fill_sockaddr(struct sock_addr
*addr
, bool abstract
)
278 char *sun_path_buf
= (char *)&addr
->listen_addr
.sun_path
;
280 addr
->listen_addr
.sun_family
= AF_UNIX
;
281 addr
->addrlen
= offsetof(struct sockaddr_un
, sun_path
);
282 snprintf(addr
->sock_name
, sizeof(addr
->sock_name
), "scm_pidfd_%d", getpid());
283 addr
->addrlen
+= strlen(addr
->sock_name
);
285 *sun_path_buf
= '\0';
289 unlink(addr
->sock_name
);
291 memcpy(sun_path_buf
, addr
->sock_name
, strlen(addr
->sock_name
));
294 static void client(FIXTURE_DATA(scm_pidfd
) *self
,
295 const FIXTURE_VARIANT(scm_pidfd
) *variant
)
299 struct ucred peer_cred
;
304 cfd
= socket(AF_UNIX
, variant
->type
, 0);
310 if (variant
->type
== SOCK_DGRAM
) {
311 fill_sockaddr(self
->client_addr
, variant
->abstract
);
313 if (bind(cfd
, (struct sockaddr
*)&self
->client_addr
->listen_addr
, self
->client_addr
->addrlen
)) {
319 if (connect(cfd
, (struct sockaddr
*)&self
->server_addr
.listen_addr
,
320 self
->server_addr
.addrlen
) != 0) {
326 if (setsockopt(cfd
, SOL_SOCKET
, SO_PASSCRED
, &on
, sizeof(on
))) {
327 log_err("Failed to set SO_PASSCRED");
331 if (setsockopt(cfd
, SOL_SOCKET
, SO_PASSPIDFD
, &on
, sizeof(on
))) {
332 log_err("Failed to set SO_PASSPIDFD");
336 close(self
->startup_pipe
[1]);
338 if (cmsg_check(cfd
)) {
339 log_err("cmsg_check failed");
343 /* skip further for SOCK_DGRAM as it's not applicable */
344 if (variant
->type
== SOCK_DGRAM
)
347 len
= sizeof(peer_cred
);
348 if (getsockopt(cfd
, SOL_SOCKET
, SO_PEERCRED
, &peer_cred
, &len
)) {
349 log_err("Failed to get SO_PEERCRED");
353 len
= sizeof(peer_pidfd
);
354 if (getsockopt(cfd
, SOL_SOCKET
, SO_PEERPIDFD
, &peer_pidfd
, &len
)) {
355 log_err("Failed to get SO_PEERPIDFD");
359 /* pid from SO_PEERCRED should point to the parent process PID */
360 if (peer_cred
.pid
!= getppid()) {
361 log_err("peer_cred.pid != getppid(): %d != %d", peer_cred
.pid
, getppid());
365 peer_pid
= get_pid_from_fdinfo_file(peer_pidfd
,
366 "Pid:", sizeof("Pid:") - 1);
367 if (peer_pid
!= peer_cred
.pid
) {
368 log_err("peer_pid != peer_cred.pid: %d != %d", peer_pid
, peer_cred
.pid
);
373 TEST_F(scm_pidfd
, test
)
377 int child_status
= 0;
379 self
->server
= socket(AF_UNIX
, variant
->type
, 0);
380 ASSERT_NE(-1, self
->server
);
382 fill_sockaddr(&self
->server_addr
, variant
->abstract
);
384 err
= bind(self
->server
, (struct sockaddr
*)&self
->server_addr
.listen_addr
, self
->server_addr
.addrlen
);
387 if (variant
->type
== SOCK_STREAM
) {
388 err
= listen(self
->server
, 1);
392 err
= pipe(self
->startup_pipe
);
395 self
->client_pid
= fork();
396 ASSERT_NE(-1, self
->client_pid
);
397 if (self
->client_pid
== 0) {
399 close(self
->startup_pipe
[0]);
400 client(self
, variant
);
403 close(self
->startup_pipe
[1]);
405 if (variant
->type
== SOCK_STREAM
) {
406 pfd
= accept(self
->server
, NULL
, NULL
);
412 /* wait until the child arrives at checkpoint */
413 read(self
->startup_pipe
[0], &err
, sizeof(int));
414 close(self
->startup_pipe
[0]);
416 if (variant
->type
== SOCK_DGRAM
) {
417 err
= sendto(pfd
, "x", sizeof(char), 0, (struct sockaddr
*)&self
->client_addr
->listen_addr
, self
->client_addr
->addrlen
);
420 err
= send(pfd
, "x", sizeof(char), 0);
425 waitpid(self
->client_pid
, &child_status
, 0);
426 ASSERT_EQ(0, WIFEXITED(child_status
) ? WEXITSTATUS(child_status
) : 1);