Skip to content

Commit

Permalink
Handle receiving a signal while we're trying to deliver a signal.
Browse files Browse the repository at this point in the history
  • Loading branch information
rocallahan committed Dec 5, 2023
1 parent d412793 commit f90490c
Showing 1 changed file with 35 additions and 13 deletions.
48 changes: 35 additions & 13 deletions src/RecordSession.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1569,23 +1569,45 @@ static bool inject_handled_signal(RecordTask* t) {
t->stashed_signal_processed();

int sig = t->ev().Signal().siginfo.si_signo;
do {
int stop_sig;
while (true) {
// We are ready to inject our signal.
// XXX we assume the kernel won't respond by notifying us of a different
// signal. We don't want to do this with signals blocked because that will
// save a bogus signal mask in the signal frame.
// Ideally we would block signals here so only the signal we care about can be raised.
// But that breaks all kinds of things because signal delivery itself can update
// the sigmaks (e.g. via sa_mask). Emulating all that would be painful, so we
// don't change the sigmask.
if (!t->resume_execution(RESUME_SINGLESTEP, RESUME_WAIT_NO_EXIT, RESUME_NO_TICKS, sig)) {
return false;
}
// Signal injection can change the sigmask due to sa_mask effects, lack of
// SA_NODEFER, and signal frame construction triggering a synchronous
// SIGSEGV.
t->invalidate_sigmask();
// Repeat injection if we got a desched signal. We observe in Linux 4.14.12
// that we get SYSCALLBUF_DESCHED_SIGNAL here once in a while.
} while (t->stop_sig() == t->session().syscallbuf_desched_sig());
// We're expecting a kernel SIGTRAP from the singlestep, but we can also be notified
// of other pending signals unfortunately :-(.
stop_sig = t->stop_sig();
ASSERT(t, stop_sig) << "Only signal-stops should occur here";
if (t->get_siginfo().si_code > 0) {
// Signal was generated by the kernel (i.e. not via kill() etc).
// Signal injection can change the sigmask due to sa_mask effects, lack of
// SA_NODEFER, and signal frame construction triggering a synchronous
// SIGSEGV.
t->invalidate_sigmask();
// We should either have a SIGTRAP or possibly a SIGSEGV due to
// sigframe construction overflowing the stack.
break;
}
if (stop_sig == t->session().syscallbuf_desched_sig()) {
// Repeat injection if we got a desched signal. We observe in Linux 4.14.12
// that we get SYSCALLBUF_DESCHED_SIGNAL here once in a while.
continue;
}
if (stop_sig == sig && sig < SIGRTMIN) {
// Repeat injection if we got the same non-RT signal. We can coalesce
// this with the current undelivered signal so there's no need to stash it.
continue;
}
// Stash it for later processing and retry injecting our signal.
t->stash_sig();
}

if (t->stop_sig() == SIGSEGV) {
if (stop_sig == SIGSEGV) {
// Constructing the signal handler frame must have failed. Stash the signal
// to deliver it later.
t->stash_sig();
Expand All @@ -1600,7 +1622,7 @@ static bool inject_handled_signal(RecordTask* t) {
}

// We stepped into a user signal handler.
ASSERT(t, t->stop_sig() == SIGTRAP)
ASSERT(t, stop_sig == SIGTRAP)
<< "Got unexpected status " << t->status() << " trying to deliver "
<< signal_name(sig) << " siginfo is " << t->get_siginfo();
ASSERT(t, t->get_signal_user_handler(sig) == t->ip())
Expand Down

0 comments on commit f90490c

Please sign in to comment.