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))
2627#define SCM_PIDFD 0x04
2728#endif
2829
30+ #define CHILD_EXIT_CODE_OK 123
31+
2932static 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+
129183static 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+
294410static 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
429558TEST_HARNESS_MAIN
0 commit comments