Skip to content

Commit 9b6f723

Browse files
committed
selftests/pidfd: second test for multi-threaded exec polling
Ensure that during a multi-threaded exec and premature thread-group leader exit no exit notification is generated. Link: https://lore.kernel.org/r/20250320-work-pidfs-thread_group-v4-3-da678ce805bf@kernel.org Signed-off-by: Christian Brauner <brauner@kernel.org>
1 parent db7ce91 commit 9b6f723

1 file changed

Lines changed: 48 additions & 24 deletions

File tree

tools/testing/selftests/pidfd/pidfd_info_test.c

Lines changed: 48 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -413,7 +413,7 @@ static void *pidfd_info_thread_exec(void *arg)
413413

414414
TEST_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

Comments
 (0)