1515#include < time.h>
1616#include < unistd.h>
1717
18+ #include < chrono>
1819#include < string>
1920#include < vector>
2021
@@ -155,6 +156,7 @@ void* SiblingExecThread(void*) {
155156
156157struct FatalExecArgs {
157158 int tid_fd;
159+ int release_fd;
158160};
159161
160162void * FatalSiblingExecThread (void * opaque) {
@@ -163,7 +165,17 @@ void* FatalSiblingExecThread(void* opaque) {
163165 if (write (args->tid_fd , &tid, sizeof (tid)) != sizeof (tid)) {
164166 _exit (5 );
165167 }
166- return SiblingExecThread (nullptr );
168+
169+ char ready_fd[16 ];
170+ char release_fd[16 ];
171+ snprintf (ready_fd, sizeof (ready_fd), " %d" , args->tid_fd );
172+ snprintf (release_fd, sizeof (release_fd), " %d" , args->release_fd );
173+ char arg0[] = " /proc/self/exe" ;
174+ char * const argv[] = {arg0, const_cast <char *>(kExecBlockMode ), ready_fd,
175+ release_fd, nullptr };
176+ char * const envp[] = {nullptr };
177+ execve (" /proc/self/exe" , argv, envp);
178+ _exit (errno);
167179}
168180
169181struct BlockingExecArgs {
@@ -434,15 +446,24 @@ TEST(SpawnExecPipeRace, FatalSignalDuringSiblingExecKeepsWaitOwnership) {
434446 for (int i = 0 ; i < kFatalIterations ; ++i) {
435447 ChildCleanup cleanup;
436448 int tid_pipe[2 ] = {-1 , -1 };
449+ int release_pipe[2 ] = {-1 , -1 };
437450 ASSERT_EQ (0 , pipe (tid_pipe)) << " iter=" << i << " : " << strerror (errno);
438451 cleanup.ready_read = tid_pipe[0 ];
439452 cleanup.ready_write = tid_pipe[1 ];
453+ ASSERT_EQ (0 , pipe (release_pipe))
454+ << " iter=" << i << " : " << strerror (errno);
455+ cleanup.release_read = release_pipe[0 ];
456+ cleanup.release_write = release_pipe[1 ];
440457
441458 pid_t child = fork ();
442459 ASSERT_GE (child, 0 ) << " iter=" << i << " : " << strerror (errno);
443460 if (child == 0 ) {
444461 close (tid_pipe[0 ]);
445- FatalExecArgs args = {.tid_fd = tid_pipe[1 ]};
462+ close (release_pipe[1 ]);
463+ FatalExecArgs args = {
464+ .tid_fd = tid_pipe[1 ],
465+ .release_fd = release_pipe[0 ],
466+ };
446467 pthread_t thread;
447468 if (pthread_create (&thread, nullptr , FatalSiblingExecThread, &args) != 0 ) {
448469 _exit (6 );
@@ -455,6 +476,8 @@ TEST(SpawnExecPipeRace, FatalSignalDuringSiblingExecKeepsWaitOwnership) {
455476
456477 close (tid_pipe[1 ]);
457478 cleanup.ready_write = -1 ;
479+ close (release_pipe[0 ]);
480+ cleanup.release_read = -1 ;
458481 pid_t exec_tid = -1 ;
459482 ASSERT_EQ (static_cast <ssize_t >(sizeof (exec_tid)),
460483 read (tid_pipe[0 ], &exec_tid, sizeof (exec_tid)))
@@ -471,17 +494,60 @@ TEST(SpawnExecPipeRace, FatalSignalDuringSiblingExecKeepsWaitOwnership) {
471494 errno = 0 ;
472495 int kill_result = static_cast <int >(
473496 syscall (SYS_tgkill, child, exec_tid, SIGKILL ));
474- ASSERT_TRUE (kill_result == 0 || errno == ESRCH )
475- << " iter=" << i << " : " << strerror (errno);
497+ const int kill_errno = errno;
498+ ASSERT_TRUE (kill_result == 0 ||
499+ (kill_result == -1 && kill_errno == ESRCH ))
500+ << " iter=" << i << " : " << strerror (kill_errno);
501+
502+ bool target_retired = kill_result == -1 ;
503+ if (target_retired) {
504+ close (release_pipe[1 ]);
505+ cleanup.release_write = -1 ;
506+ }
476507
477508 int status = 0 ;
478- ASSERT_TRUE (WaitForChildExit (child, &status, 5000 ))
509+ bool child_exited = false ;
510+ auto deadline = std::chrono::steady_clock::now () +
511+ std::chrono::milliseconds (5000 );
512+ while (std::chrono::steady_clock::now () < deadline) {
513+ pid_t waited = waitpid (child, &status, WNOHANG );
514+ if (waited == -1 && errno == EINTR ) {
515+ continue ;
516+ }
517+ ASSERT_TRUE (waited == 0 || waited == child)
518+ << " iter=" << i << " : " << strerror (errno);
519+ if (waited == child) {
520+ child_exited = true ;
521+ break ;
522+ }
523+
524+ if (!target_retired) {
525+ errno = 0 ;
526+ int probe_result = static_cast <int >(
527+ syscall (SYS_tgkill, child, exec_tid, 0 ));
528+ const int probe_errno = errno;
529+ ASSERT_TRUE (probe_result == 0 ||
530+ (probe_result == -1 && probe_errno == ESRCH ))
531+ << " iter=" << i << " : " << strerror (probe_errno);
532+ if (probe_result == -1 ) {
533+ target_retired = true ;
534+ close (release_pipe[1 ]);
535+ cleanup.release_write = -1 ;
536+ }
537+ }
538+ SleepForMillis (10 );
539+ }
540+
541+ ASSERT_TRUE (child_exited)
479542 << " fatal sibling exec was not waitable at iter=" << i;
480543 cleanup.child = -1 ;
481- ASSERT_TRUE (WIFSIGNALED (status) ||
482- (WIFEXITED (status) &&
483- (WEXITSTATUS (status) == 0 || WEXITSTATUS (status) == EAGAIN )))
484- << " unexpected child status=" << status << " at iter=" << i;
544+ if (WIFSIGNALED (status) && WTERMSIG (status) == SIGKILL ) {
545+ continue ;
546+ }
547+ ASSERT_TRUE (target_retired && WIFEXITED (status) &&
548+ WEXITSTATUS (status) == 3 )
549+ << " thread-targeted SIGKILL was lost: child status=" << status
550+ << " at iter=" << i;
485551 }
486552}
487553
0 commit comments