@@ -369,4 +369,129 @@ TEST_F(pidfd_info, thread_group)
369369 EXPECT_EQ (close (pidfd_thread ), 0 );
370370}
371371
372+ static void * pidfd_info_thread_exec (void * arg )
373+ {
374+ pid_t pid_thread = gettid ();
375+ int ipc_socket = * (int * )arg ;
376+
377+ /* Inform the grand-parent what the tid of this thread is. */
378+ if (write_nointr (ipc_socket , & pid_thread , sizeof (pid_thread )) != sizeof (pid_thread ))
379+ return NULL ;
380+
381+ if (read_nointr (ipc_socket , & pid_thread , sizeof (pid_thread )) != sizeof (pid_thread ))
382+ return NULL ;
383+
384+ close (ipc_socket );
385+
386+ sys_execveat (AT_FDCWD , "pidfd_exec_helper" , NULL , NULL , 0 );
387+ return NULL ;
388+ }
389+
390+ TEST_F (pidfd_info , thread_group_exec )
391+ {
392+ pid_t pid_leader , pid_thread ;
393+ pthread_t thread ;
394+ int nevents , pidfd_leader , pidfd_leader_thread , pidfd_thread , ret ;
395+ int ipc_sockets [2 ];
396+ struct pollfd fds = {};
397+ struct pidfd_info info = {
398+ .mask = PIDFD_INFO_CGROUPID | PIDFD_INFO_EXIT ,
399+ };
400+
401+ ret = socketpair (AF_LOCAL , SOCK_STREAM | SOCK_CLOEXEC , 0 , ipc_sockets );
402+ EXPECT_EQ (ret , 0 );
403+
404+ pid_leader = create_child (& pidfd_leader , 0 );
405+ EXPECT_GE (pid_leader , 0 );
406+
407+ if (pid_leader == 0 ) {
408+ close (ipc_sockets [0 ]);
409+
410+ /* The thread will outlive the thread-group leader. */
411+ if (pthread_create (& thread , NULL , pidfd_info_thread_exec , & ipc_sockets [1 ]))
412+ syscall (__NR_exit , EXIT_FAILURE );
413+
414+ /* Make the thread-group leader exit prematurely. */
415+ syscall (__NR_exit , EXIT_SUCCESS );
416+ }
417+
418+ /* Retrieve the tid of the thread. */
419+ EXPECT_EQ (close (ipc_sockets [1 ]), 0 );
420+ ASSERT_EQ (read_nointr (ipc_sockets [0 ], & pid_thread , sizeof (pid_thread )), sizeof (pid_thread ));
421+
422+ /* Opening a thread as a PIDFD_THREAD must succeed. */
423+ pidfd_thread = sys_pidfd_open (pid_thread , PIDFD_THREAD );
424+ ASSERT_GE (pidfd_thread , 0 );
425+
426+ /* Open a thread-specific pidfd for the thread-group leader. */
427+ pidfd_leader_thread = sys_pidfd_open (pid_leader , PIDFD_THREAD );
428+ ASSERT_GE (pidfd_leader_thread , 0 );
429+
430+ /*
431+ * We can poll and wait for the old thread-group leader to exit
432+ * using a thread-specific pidfd.
433+ *
434+ * This only works until the thread has execed. When the thread
435+ * has execed it will have taken over the old thread-group
436+ * leaders struct pid. Calling poll after the thread execed will
437+ * thus block again because a new thread-group has started (Yes,
438+ * it's fscked.).
439+ */
440+ fds .events = POLLIN ;
441+ fds .fd = pidfd_leader_thread ;
442+ nevents = poll (& fds , 1 , -1 );
443+ ASSERT_EQ (nevents , 1 );
444+ /* The thread-group leader has exited. */
445+ ASSERT_TRUE (!!(fds .revents & POLLIN ));
446+ /* The thread-group leader hasn't been reaped. */
447+ ASSERT_FALSE (!!(fds .revents & POLLHUP ));
448+
449+ /* Now that we've opened a thread-specific pidfd the thread can exec. */
450+ ASSERT_EQ (write_nointr (ipc_sockets [0 ], & pid_thread , sizeof (pid_thread )), sizeof (pid_thread ));
451+ EXPECT_EQ (close (ipc_sockets [0 ]), 0 );
452+
453+ /* Wait until the kernel has SIGKILLed the thread. */
454+ fds .events = POLLHUP ;
455+ fds .fd = pidfd_thread ;
456+ nevents = poll (& fds , 1 , -1 );
457+ ASSERT_EQ (nevents , 1 );
458+ /* The thread has been reaped. */
459+ ASSERT_TRUE (!!(fds .revents & POLLHUP ));
460+
461+ /* Retrieve thread-specific exit info from pidfd. */
462+ ASSERT_EQ (ioctl (pidfd_thread , PIDFD_GET_INFO , & info ), 0 );
463+ ASSERT_FALSE (!!(info .mask & PIDFD_INFO_CREDS ));
464+ ASSERT_TRUE (!!(info .mask & PIDFD_INFO_EXIT ));
465+ /*
466+ * While the kernel will have SIGKILLed the whole thread-group
467+ * during exec it will cause the individual threads to exit
468+ * cleanly.
469+ */
470+ ASSERT_TRUE (WIFEXITED (info .exit_code ));
471+ ASSERT_EQ (WEXITSTATUS (info .exit_code ), 0 );
472+
473+ /*
474+ * The thread-group leader is still alive, the thread has taken
475+ * over its struct pid and thus its pid number.
476+ */
477+ info .mask = PIDFD_INFO_CGROUPID | PIDFD_INFO_EXIT ;
478+ ASSERT_EQ (ioctl (pidfd_leader , PIDFD_GET_INFO , & info ), 0 );
479+ ASSERT_TRUE (!!(info .mask & PIDFD_INFO_CREDS ));
480+ ASSERT_FALSE (!!(info .mask & PIDFD_INFO_EXIT ));
481+ ASSERT_EQ (info .pid , pid_leader );
482+
483+ /* Take down the thread-group leader. */
484+ EXPECT_EQ (sys_pidfd_send_signal (pidfd_leader , SIGKILL , NULL , 0 ), 0 );
485+ EXPECT_EQ (sys_waitid (P_PIDFD , pidfd_leader , NULL , WEXITED ), 0 );
486+
487+ /* Retrieve exit information for the thread-group leader. */
488+ info .mask = PIDFD_INFO_CGROUPID | PIDFD_INFO_EXIT ;
489+ ASSERT_EQ (ioctl (pidfd_leader , PIDFD_GET_INFO , & info ), 0 );
490+ ASSERT_FALSE (!!(info .mask & PIDFD_INFO_CREDS ));
491+ ASSERT_TRUE (!!(info .mask & PIDFD_INFO_EXIT ));
492+
493+ EXPECT_EQ (close (pidfd_leader ), 0 );
494+ EXPECT_EQ (close (pidfd_thread ), 0 );
495+ }
496+
372497TEST_HARNESS_MAIN
0 commit comments