Skip to content

Commit

Permalink
Handle case where stashed_signals_blocking_more_signals is true and…
Browse files Browse the repository at this point in the history
… we need to deliver a `TIME_SLICE_SIGNAL` to kick the tracee task out of a wait

The `vfork_done` test was failing itermittently. The grandchild would exit, causing us
to try to deliver a synthetic `SIGCHLD` to the main process during its `waitpid()`.
We'd stash that SIGCHLD according to the logic in `set_siginfo_for_synthetic_SIGCHLD()`
and the main process would resume waiting since it's waiting on its immediate child.
This would trigger `stashed_signals_blocking_more_signals`.
Then the `vfork()` would finish and we'd do another `set_siginfo_for_synthetic_SIGCHLD()`
`PTRACE_EVENT_VFORK_DONE` event. `is_sig_blocked(SIGCHLD)` is false but we actually
are blocking `SIGCHLD` due to `stashed_signals_blocking_more_signals`, so we would not
send any signal and the main process would remain stuck in its `waitpid()`.

When `stashed_signals_blocking_more_signals` is true we need to send the extra
`TIME_SLICE_SIGNAL` to ensure the main process is kicked out of its wait.
  • Loading branch information
rocallahan committed May 31, 2024
1 parent 36bcda7 commit fb5e895
Show file tree
Hide file tree
Showing 2 changed files with 11 additions and 6 deletions.
14 changes: 8 additions & 6 deletions src/RecordTask.cc
Original file line number Diff line number Diff line change
Expand Up @@ -671,6 +671,9 @@ void RecordTask::will_resume_execution(ResumeRequest, WaitRequest,
// We're injecting a signal, so make sure that signal is unblocked.
sigset &= ~signal_bit(sig);
}
LOG(debug) << "Set signal mask for " << tid << " to block all signals (bar "
<< "SYSCALLBUF_DESCHED_SIGNAL/TIME_SLICE_SIGNAL) while we "
<< "have a stashed signal";
set_sigmask(sigset);
}

Expand Down Expand Up @@ -937,7 +940,10 @@ void RecordTask::send_synthetic_SIGCHLD_if_necessary() {
ret = syscall(SYS_rt_tgsigqueueinfo, wake_task->tgid(), wake_task->tid,
SIGCHLD, &si);
ASSERT(this, ret == 0);
if (wake_task->is_sig_blocked(SIGCHLD)) {
// `stashed_signals_blocking_more_signals` means that SIGCHLD can be
// blocked even when `is_sig_blocked` is false.
if (wake_task->is_sig_blocked(SIGCHLD) ||
wake_task->stashed_signals_blocking_more_signals) {
LOG(debug) << "SIGCHLD is blocked, kicking it out of the syscall";
// Just sending SIGCHLD won't wake it up. Send it a TIME_SLICE_SIGNAL
// as well to make sure it exits a blocking syscall. We ensure those
Expand Down Expand Up @@ -1347,10 +1353,6 @@ void RecordTask::set_sigmask(sig_set_t mask) {
return;
}
ASSERT(this, errno == EINVAL);
} else {
LOG(debug) << "Set signal mask to block all signals (bar "
<< "SYSCALLBUF_DESCHED_SIGNAL/TIME_SLICE_SIGNAL) while we "
<< " have a stashed signal";
}
}

Expand Down Expand Up @@ -2190,7 +2192,7 @@ void RecordTask::set_tid_and_update_serial(pid_t tid,

bool RecordTask::may_reap() {
if (emulated_stop_pending) {
LOG(debug) << "Declining to reap " << tid << "; emulated stop pending";
LOG(debug) << "Declining to reap " << tid << "; emulated stop pending " << emulated_stop_code;
// Don't reap until the emulated ptrace stop has been processed.
return false;
}
Expand Down
3 changes: 3 additions & 0 deletions src/RecordTask.h
Original file line number Diff line number Diff line change
Expand Up @@ -795,6 +795,9 @@ class RecordTask final : public Task {
// Stashed signal-delivery state, ready to be delivered at
// next opportunity.
std::deque<StashedSignal> stashed_signals;
// When true, we're blocking signals during a syscall to
// prevent new signals from being delivered. `blocked_sigs_dirty`
// is false and `blocked_sigs` contains the previous sigmask.
bool stashed_signals_blocking_more_signals;
bool stashed_group_stop;
bool break_at_syscallbuf_traced_syscalls;
Expand Down

0 comments on commit fb5e895

Please sign in to comment.