From 6d794801c61e5afd63a21c69525e5a5702d312ea Mon Sep 17 00:00:00 2001 From: Bartosz Taudul Date: Sun, 30 Aug 2026 16:48:01 +0200 Subject: [PATCH] Verify the exec stop in launch mode. A caught signal (the handlers are inherited by the child) delivered between PTRACE_TRACEME and exec first produces a signal-delivery stop; without handling it, the setup would run on the still pre-exec child and capture the monitor's own image as the target's. Re-inject the caught signal and wait again until the exec stop is reached. Job-control stops are the exception - re-injecting SIGSTOP/SIGTSTP re-stops the child, and PTRACE_CONT rejects a stop carrying the 0x80 job-control bit - so resume those without a signal instead. --- monitor/src/monitor.cpp | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/monitor/src/monitor.cpp b/monitor/src/monitor.cpp index e72cae9b..ec64dbbe 100644 --- a/monitor/src/monitor.cpp +++ b/monitor/src/monitor.cpp @@ -570,6 +570,38 @@ static int RunForked( int argc, char** argv ) return 2; } + while( WIFSTOPPED( status ) && WSTOPSIG( status ) != SIGTRAP ) + { + // A caught signal (the handlers are inherited by the child) arriving between + // PTRACE_TRACEME and exec produces a signal-delivery stop before the exec stop. + // Re-inject it and keep waiting for the exec stop: running the setup on the + // pre-exec child would capture the monitor's own image and mis-symbolicate. + // Job-control signals must never be re-injected: re-delivering SIGSTOP/SIGTSTP + // re-stops the child, so the exec stop would never be reached (or, for a + // stop carrying the 0x80 job-control bit, PTRACE_CONT rejects the signal + // number outright). Resume those without a signal instead: the stop is + // delivered as a group stop at most once more, and the next resume lifts it. + const int rawStopSig = WSTOPSIG( status ); + const int stopSig = rawStopSig & 0x7f; + const int resumeSig = ( rawStopSig & 0x80 || stopSig == SIGSTOP || stopSig == SIGTSTP ) ? 0 : stopSig; + if( ptrace( PTRACE_CONT, childPid, 0, (void*)(unsigned long)resumeSig ) != 0 ) + { + fprintf( stderr, "ptrace failed: %s\n", strerror( errno ) ); + kill( childPid, SIGKILL ); + waitpid( childPid, nullptr, 0 ); + return 2; + } + for(;;) + { + if( waitpid( childPid, &status, 0 ) >= 0 ) break; + if( errno == EINTR ) continue; + fprintf( stderr, "waitpid failed: %s\n", strerror( errno ) ); + kill( childPid, SIGKILL ); + waitpid( childPid, nullptr, 0 ); + return 2; + } + } + if( !WIFSTOPPED( status ) ) { // Child exited or was killed before reaching the post-exec SIGTRAP.