@@ -542,4 +542,151 @@ TEST_F(pidfd_info, thread_group_exec)
542542 EXPECT_EQ (close (pidfd_thread ), 0 );
543543}
544544
545+ static void * pidfd_info_thread_exec_sane (void * arg )
546+ {
547+ pid_t pid_thread = gettid ();
548+ int ipc_socket = * (int * )arg ;
549+
550+ /* Inform the grand-parent what the tid of this thread is. */
551+ if (write_nointr (ipc_socket , & pid_thread , sizeof (pid_thread )) != sizeof (pid_thread ))
552+ return NULL ;
553+
554+ if (read_nointr (ipc_socket , & pid_thread , sizeof (pid_thread )) != sizeof (pid_thread ))
555+ return NULL ;
556+
557+ close (ipc_socket );
558+
559+ sys_execveat (AT_FDCWD , "pidfd_exec_helper" , NULL , NULL , 0 );
560+ return NULL ;
561+ }
562+
563+ TEST_F (pidfd_info , thread_group_exec_thread )
564+ {
565+ pid_t pid_leader , pid_poller , pid_thread ;
566+ pthread_t thread ;
567+ int nevents , pidfd_leader , pidfd_leader_thread , pidfd_thread , ret ;
568+ int ipc_sockets [2 ];
569+ struct pollfd fds = {};
570+ struct pidfd_info info = {
571+ .mask = PIDFD_INFO_CGROUPID | PIDFD_INFO_EXIT ,
572+ };
573+
574+ ret = socketpair (AF_LOCAL , SOCK_STREAM | SOCK_CLOEXEC , 0 , ipc_sockets );
575+ EXPECT_EQ (ret , 0 );
576+
577+ pid_leader = create_child (& pidfd_leader , 0 );
578+ EXPECT_GE (pid_leader , 0 );
579+
580+ if (pid_leader == 0 ) {
581+ close (ipc_sockets [0 ]);
582+
583+ /* The thread will outlive the thread-group leader. */
584+ if (pthread_create (& thread , NULL , pidfd_info_thread_exec_sane , & ipc_sockets [1 ]))
585+ syscall (__NR_exit , EXIT_FAILURE );
586+
587+ /*
588+ * Pause the thread-group leader. It will be killed once
589+ * the subthread execs.
590+ */
591+ pause ();
592+ syscall (__NR_exit , EXIT_SUCCESS );
593+ }
594+
595+ /* Retrieve the tid of the thread. */
596+ EXPECT_EQ (close (ipc_sockets [1 ]), 0 );
597+ ASSERT_EQ (read_nointr (ipc_sockets [0 ], & pid_thread , sizeof (pid_thread )), sizeof (pid_thread ));
598+
599+ /* Opening a thread as a PIDFD_THREAD must succeed. */
600+ pidfd_thread = sys_pidfd_open (pid_thread , PIDFD_THREAD );
601+ ASSERT_GE (pidfd_thread , 0 );
602+
603+ /* Open a thread-specific pidfd for the thread-group leader. */
604+ pidfd_leader_thread = sys_pidfd_open (pid_leader , PIDFD_THREAD );
605+ ASSERT_GE (pidfd_leader_thread , 0 );
606+
607+ pid_poller = fork ();
608+ ASSERT_GE (pid_poller , 0 );
609+ if (pid_poller == 0 ) {
610+ /*
611+ * The subthread will now exec. The struct pid of the old
612+ * thread-group leader will be assumed by the subthread which
613+ * becomes the new thread-group leader. So no exit notification
614+ * must be generated. Wait for 5 seconds and call it a success
615+ * if no notification has been received.
616+ */
617+ fds .events = POLLIN ;
618+ fds .fd = pidfd_leader_thread ;
619+ nevents = poll (& fds , 1 , 10000 /* wait 5 seconds */ );
620+ if (nevents != 0 )
621+ _exit (EXIT_FAILURE );
622+ if (fds .revents & POLLIN )
623+ _exit (EXIT_FAILURE );
624+ if (fds .revents & POLLHUP )
625+ _exit (EXIT_FAILURE );
626+ _exit (EXIT_SUCCESS );
627+ }
628+
629+ /* Now that we've opened a thread-specific pidfd the thread can exec. */
630+ ASSERT_EQ (write_nointr (ipc_sockets [0 ], & pid_thread , sizeof (pid_thread )), sizeof (pid_thread ));
631+ EXPECT_EQ (close (ipc_sockets [0 ]), 0 );
632+ ASSERT_EQ (wait_for_pid (pid_poller ), 0 );
633+
634+ /* Wait until the kernel has SIGKILLed the thread. */
635+ fds .events = POLLHUP ;
636+ fds .fd = pidfd_thread ;
637+ nevents = poll (& fds , 1 , -1 );
638+ ASSERT_EQ (nevents , 1 );
639+ /* The thread has been reaped. */
640+ ASSERT_TRUE (!!(fds .revents & POLLHUP ));
641+
642+ /* Retrieve thread-specific exit info from pidfd. */
643+ ASSERT_EQ (ioctl (pidfd_thread , PIDFD_GET_INFO , & info ), 0 );
644+ ASSERT_FALSE (!!(info .mask & PIDFD_INFO_CREDS ));
645+ ASSERT_TRUE (!!(info .mask & PIDFD_INFO_EXIT ));
646+ /*
647+ * While the kernel will have SIGKILLed the whole thread-group
648+ * during exec it will cause the individual threads to exit
649+ * cleanly.
650+ */
651+ ASSERT_TRUE (WIFEXITED (info .exit_code ));
652+ ASSERT_EQ (WEXITSTATUS (info .exit_code ), 0 );
653+
654+ /*
655+ * The thread-group leader is still alive, the thread has taken
656+ * over its struct pid and thus its pid number.
657+ */
658+ info .mask = PIDFD_INFO_CGROUPID | PIDFD_INFO_EXIT ;
659+ ASSERT_EQ (ioctl (pidfd_leader , PIDFD_GET_INFO , & info ), 0 );
660+ ASSERT_TRUE (!!(info .mask & PIDFD_INFO_CREDS ));
661+ ASSERT_FALSE (!!(info .mask & PIDFD_INFO_EXIT ));
662+ ASSERT_EQ (info .pid , pid_leader );
663+
664+ /* Take down the thread-group leader. */
665+ EXPECT_EQ (sys_pidfd_send_signal (pidfd_leader , SIGKILL , NULL , 0 ), 0 );
666+
667+ /*
668+ * Afte the exec we're dealing with an empty thread-group so now
669+ * we must see an exit notification on the thread-specific pidfd
670+ * for the thread-group leader as there's no subthread that can
671+ * revive the struct pid.
672+ */
673+ fds .events = POLLIN ;
674+ fds .fd = pidfd_leader_thread ;
675+ nevents = poll (& fds , 1 , -1 );
676+ ASSERT_EQ (nevents , 1 );
677+ ASSERT_TRUE (!!(fds .revents & POLLIN ));
678+ ASSERT_FALSE (!!(fds .revents & POLLHUP ));
679+
680+ EXPECT_EQ (sys_waitid (P_PIDFD , pidfd_leader , NULL , WEXITED ), 0 );
681+
682+ /* Retrieve exit information for the thread-group leader. */
683+ info .mask = PIDFD_INFO_CGROUPID | PIDFD_INFO_EXIT ;
684+ ASSERT_EQ (ioctl (pidfd_leader , PIDFD_GET_INFO , & info ), 0 );
685+ ASSERT_FALSE (!!(info .mask & PIDFD_INFO_CREDS ));
686+ ASSERT_TRUE (!!(info .mask & PIDFD_INFO_EXIT ));
687+
688+ EXPECT_EQ (close (pidfd_leader ), 0 );
689+ EXPECT_EQ (close (pidfd_thread ), 0 );
690+ }
691+
545692TEST_HARNESS_MAIN
0 commit comments