@@ -218,4 +218,155 @@ TEST_F(pidfd_info, success_reaped_poll)
218218 ASSERT_EQ (WTERMSIG (info .exit_code ), SIGKILL );
219219}
220220
221+ static void * pidfd_info_pause_thread (void * arg )
222+ {
223+ pid_t pid_thread = gettid ();
224+ int ipc_socket = * (int * )arg ;
225+
226+ /* Inform the grand-parent what the tid of this thread is. */
227+ if (write_nointr (ipc_socket , & pid_thread , sizeof (pid_thread )) != sizeof (pid_thread ))
228+ return NULL ;
229+
230+ close (ipc_socket );
231+
232+ /* Sleep untill we're killed. */
233+ pause ();
234+ return NULL ;
235+ }
236+
237+ TEST_F (pidfd_info , thread_group )
238+ {
239+ pid_t pid_leader , pid_thread ;
240+ pthread_t thread ;
241+ int nevents , pidfd_leader , pidfd_thread , pidfd_leader_thread , ret ;
242+ int ipc_sockets [2 ];
243+ struct pollfd fds = {};
244+ struct pidfd_info info = {
245+ .mask = PIDFD_INFO_CGROUPID | PIDFD_INFO_EXIT ,
246+ }, info2 ;
247+
248+ ret = socketpair (AF_LOCAL , SOCK_STREAM | SOCK_CLOEXEC , 0 , ipc_sockets );
249+ EXPECT_EQ (ret , 0 );
250+
251+ pid_leader = create_child (& pidfd_leader , 0 );
252+ EXPECT_GE (pid_leader , 0 );
253+
254+ if (pid_leader == 0 ) {
255+ close (ipc_sockets [0 ]);
256+
257+ /* The thread will outlive the thread-group leader. */
258+ if (pthread_create (& thread , NULL , pidfd_info_pause_thread , & ipc_sockets [1 ]))
259+ syscall (__NR_exit , EXIT_FAILURE );
260+
261+ /* Make the thread-group leader exit prematurely. */
262+ syscall (__NR_exit , EXIT_SUCCESS );
263+ }
264+
265+ /* Retrieve the tid of the thread. */
266+ EXPECT_EQ (close (ipc_sockets [1 ]), 0 );
267+ ASSERT_EQ (read_nointr (ipc_sockets [0 ], & pid_thread , sizeof (pid_thread )), sizeof (pid_thread ));
268+ EXPECT_EQ (close (ipc_sockets [0 ]), 0 );
269+
270+ /* Opening a thread as a thread-group leader must fail. */
271+ pidfd_thread = sys_pidfd_open (pid_thread , 0 );
272+ ASSERT_LT (pidfd_thread , 0 );
273+
274+ /* Opening a thread as a PIDFD_THREAD must succeed. */
275+ pidfd_thread = sys_pidfd_open (pid_thread , PIDFD_THREAD );
276+ ASSERT_GE (pidfd_thread , 0 );
277+
278+ /*
279+ * Opening a PIDFD_THREAD aka thread-specific pidfd based on a
280+ * thread-group leader must succeed.
281+ */
282+ pidfd_leader_thread = sys_pidfd_open (pid_leader , PIDFD_THREAD );
283+ ASSERT_GE (pidfd_leader_thread , 0 );
284+
285+ /*
286+ * Note that pidfd_leader is a thread-group pidfd, so polling on it
287+ * would only notify us once all thread in the thread-group have
288+ * exited. So we can't poll before we have taken down the whole
289+ * thread-group.
290+ */
291+
292+ /* Get PIDFD_GET_INFO using the thread-group leader pidfd. */
293+ ASSERT_EQ (ioctl (pidfd_leader , PIDFD_GET_INFO , & info ), 0 );
294+ ASSERT_TRUE (!!(info .mask & PIDFD_INFO_CREDS ));
295+ /* Process has exited but not been reaped, so no PIDFD_INFO_EXIT information yet. */
296+ ASSERT_FALSE (!!(info .mask & PIDFD_INFO_EXIT ));
297+ ASSERT_EQ (info .pid , pid_leader );
298+
299+ /*
300+ * Now retrieve the same info using the thread specific pidfd
301+ * for the thread-group leader.
302+ */
303+ info2 .mask = PIDFD_INFO_CGROUPID | PIDFD_INFO_EXIT ;
304+ ASSERT_EQ (ioctl (pidfd_leader_thread , PIDFD_GET_INFO , & info2 ), 0 );
305+ ASSERT_TRUE (!!(info2 .mask & PIDFD_INFO_CREDS ));
306+ /* Process has exited but not been reaped, so no PIDFD_INFO_EXIT information yet. */
307+ ASSERT_FALSE (!!(info2 .mask & PIDFD_INFO_EXIT ));
308+ ASSERT_EQ (info2 .pid , pid_leader );
309+
310+ /* Now try the thread-specific pidfd. */
311+ ASSERT_EQ (ioctl (pidfd_thread , PIDFD_GET_INFO , & info ), 0 );
312+ ASSERT_TRUE (!!(info .mask & PIDFD_INFO_CREDS ));
313+ /* The thread hasn't exited, so no PIDFD_INFO_EXIT information yet. */
314+ ASSERT_FALSE (!!(info .mask & PIDFD_INFO_EXIT ));
315+ ASSERT_EQ (info .pid , pid_thread );
316+
317+ /*
318+ * Take down the whole thread-group. The thread-group leader
319+ * exited successfully but the thread will now be SIGKILLed.
320+ * This must be reflected in the recorded exit information.
321+ */
322+ EXPECT_EQ (sys_pidfd_send_signal (pidfd_leader , SIGKILL , NULL , 0 ), 0 );
323+ EXPECT_EQ (sys_waitid (P_PIDFD , pidfd_leader , NULL , WEXITED ), 0 );
324+
325+ fds .events = POLLIN ;
326+ fds .fd = pidfd_leader ;
327+ nevents = poll (& fds , 1 , -1 );
328+ ASSERT_EQ (nevents , 1 );
329+ ASSERT_TRUE (!!(fds .revents & POLLIN ));
330+ /* The thread-group leader has been reaped. */
331+ ASSERT_TRUE (!!(fds .revents & POLLHUP ));
332+
333+ /*
334+ * Retrieve exit information for the thread-group leader via the
335+ * thread-group leader pidfd.
336+ */
337+ info .mask = PIDFD_INFO_CGROUPID | PIDFD_INFO_EXIT ;
338+ ASSERT_EQ (ioctl (pidfd_leader , PIDFD_GET_INFO , & info ), 0 );
339+ ASSERT_FALSE (!!(info .mask & PIDFD_INFO_CREDS ));
340+ ASSERT_TRUE (!!(info .mask & PIDFD_INFO_EXIT ));
341+ /* The thread-group leader exited successfully. Only the specific thread was SIGKILLed. */
342+ ASSERT_TRUE (WIFEXITED (info .exit_code ));
343+ ASSERT_EQ (WEXITSTATUS (info .exit_code ), 0 );
344+
345+ /*
346+ * Retrieve exit information for the thread-group leader via the
347+ * thread-specific pidfd.
348+ */
349+ info2 .mask = PIDFD_INFO_CGROUPID | PIDFD_INFO_EXIT ;
350+ ASSERT_EQ (ioctl (pidfd_leader_thread , PIDFD_GET_INFO , & info2 ), 0 );
351+ ASSERT_FALSE (!!(info2 .mask & PIDFD_INFO_CREDS ));
352+ ASSERT_TRUE (!!(info2 .mask & PIDFD_INFO_EXIT ));
353+
354+ /* The thread-group leader exited successfully. Only the specific thread was SIGKILLed. */
355+ ASSERT_TRUE (WIFEXITED (info2 .exit_code ));
356+ ASSERT_EQ (WEXITSTATUS (info2 .exit_code ), 0 );
357+
358+ /* Retrieve exit information for the thread. */
359+ info .mask = PIDFD_INFO_CGROUPID | PIDFD_INFO_EXIT ;
360+ ASSERT_EQ (ioctl (pidfd_thread , PIDFD_GET_INFO , & info ), 0 );
361+ ASSERT_FALSE (!!(info .mask & PIDFD_INFO_CREDS ));
362+ ASSERT_TRUE (!!(info .mask & PIDFD_INFO_EXIT ));
363+
364+ /* The thread got SIGKILLed. */
365+ ASSERT_TRUE (WIFSIGNALED (info .exit_code ));
366+ ASSERT_EQ (WTERMSIG (info .exit_code ), SIGKILL );
367+
368+ EXPECT_EQ (close (pidfd_leader ), 0 );
369+ EXPECT_EQ (close (pidfd_thread ), 0 );
370+ }
371+
221372TEST_HARNESS_MAIN
0 commit comments