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.
This commit is contained in:
Bartosz Taudul
2026-08-30 16:48:01 +02:00
parent b36c8f2fc5
commit 6d794801c6

View File

@@ -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.