@@ -413,7 +413,7 @@ static void *pidfd_info_thread_exec(void *arg)
413413
414414TEST_F (pidfd_info , thread_group_exec )
415415{
416- pid_t pid_leader , pid_thread ;
416+ pid_t pid_leader , pid_poller , pid_thread ;
417417 pthread_t thread ;
418418 int nevents , pidfd_leader , pidfd_leader_thread , pidfd_thread , ret ;
419419 int ipc_sockets [2 ];
@@ -439,6 +439,37 @@ TEST_F(pidfd_info, thread_group_exec)
439439 syscall (__NR_exit , EXIT_SUCCESS );
440440 }
441441
442+ /* Open a thread-specific pidfd for the thread-group leader. */
443+ pidfd_leader_thread = sys_pidfd_open (pid_leader , PIDFD_THREAD );
444+ ASSERT_GE (pidfd_leader_thread , 0 );
445+
446+ pid_poller = fork ();
447+ ASSERT_GE (pid_poller , 0 );
448+ if (pid_poller == 0 ) {
449+ /*
450+ * We can't poll and wait for the old thread-group
451+ * leader to exit using a thread-specific pidfd. The
452+ * thread-group leader exited prematurely and
453+ * notification is delayed until all subthreads have
454+ * exited.
455+ *
456+ * When the thread has execed it will taken over the old
457+ * thread-group leaders struct pid. Calling poll after
458+ * the thread execed will thus block again because a new
459+ * thread-group has started.
460+ */
461+ fds .events = POLLIN ;
462+ fds .fd = pidfd_leader_thread ;
463+ nevents = poll (& fds , 1 , 10000 /* wait 5 seconds */ );
464+ if (nevents != 0 )
465+ _exit (EXIT_FAILURE );
466+ if (fds .revents & POLLIN )
467+ _exit (EXIT_FAILURE );
468+ if (fds .revents & POLLHUP )
469+ _exit (EXIT_FAILURE );
470+ _exit (EXIT_SUCCESS );
471+ }
472+
442473 /* Retrieve the tid of the thread. */
443474 EXPECT_EQ (close (ipc_sockets [1 ]), 0 );
444475 ASSERT_EQ (read_nointr (ipc_sockets [0 ], & pid_thread , sizeof (pid_thread )), sizeof (pid_thread ));
@@ -447,33 +478,12 @@ TEST_F(pidfd_info, thread_group_exec)
447478 pidfd_thread = sys_pidfd_open (pid_thread , PIDFD_THREAD );
448479 ASSERT_GE (pidfd_thread , 0 );
449480
450- /* Open a thread-specific pidfd for the thread-group leader. */
451- pidfd_leader_thread = sys_pidfd_open (pid_leader , PIDFD_THREAD );
452- ASSERT_GE (pidfd_leader_thread , 0 );
453-
454- /*
455- * We can poll and wait for the old thread-group leader to exit
456- * using a thread-specific pidfd.
457- *
458- * This only works until the thread has execed. When the thread
459- * has execed it will have taken over the old thread-group
460- * leaders struct pid. Calling poll after the thread execed will
461- * thus block again because a new thread-group has started (Yes,
462- * it's fscked.).
463- */
464- fds .events = POLLIN ;
465- fds .fd = pidfd_leader_thread ;
466- nevents = poll (& fds , 1 , -1 );
467- ASSERT_EQ (nevents , 1 );
468- /* The thread-group leader has exited. */
469- ASSERT_TRUE (!!(fds .revents & POLLIN ));
470- /* The thread-group leader hasn't been reaped. */
471- ASSERT_FALSE (!!(fds .revents & POLLHUP ));
472-
473481 /* Now that we've opened a thread-specific pidfd the thread can exec. */
474482 ASSERT_EQ (write_nointr (ipc_sockets [0 ], & pid_thread , sizeof (pid_thread )), sizeof (pid_thread ));
475483 EXPECT_EQ (close (ipc_sockets [0 ]), 0 );
476484
485+ ASSERT_EQ (wait_for_pid (pid_poller ), 0 );
486+
477487 /* Wait until the kernel has SIGKILLed the thread. */
478488 fds .events = POLLHUP ;
479489 fds .fd = pidfd_thread ;
@@ -506,6 +516,20 @@ TEST_F(pidfd_info, thread_group_exec)
506516
507517 /* Take down the thread-group leader. */
508518 EXPECT_EQ (sys_pidfd_send_signal (pidfd_leader , SIGKILL , NULL , 0 ), 0 );
519+
520+ /*
521+ * Afte the exec we're dealing with an empty thread-group so now
522+ * we must see an exit notification on the thread-specific pidfd
523+ * for the thread-group leader as there's no subthread that can
524+ * revive the struct pid.
525+ */
526+ fds .events = POLLIN ;
527+ fds .fd = pidfd_leader_thread ;
528+ nevents = poll (& fds , 1 , -1 );
529+ ASSERT_EQ (nevents , 1 );
530+ ASSERT_TRUE (!!(fds .revents & POLLIN ));
531+ ASSERT_FALSE (!!(fds .revents & POLLHUP ));
532+
509533 EXPECT_EQ (sys_waitid (P_PIDFD , pidfd_leader , NULL , WEXITED ), 0 );
510534
511535 /* Retrieve exit information for the thread-group leader. */
0 commit comments