Skip to content

Commit

Permalink
Disarm desched event in AutoRemoteSyscalls
Browse files Browse the repository at this point in the history
If we're running on a thread with a desched event enabled, we can enter
an infinite loop where stepping through the syscall infinitely
retriggers the desched event, and thus the syscall can never complete.
  • Loading branch information
KJTsanaktsidis authored and rocallahan committed Sep 17, 2024
1 parent a6a80fd commit ceeff12
Show file tree
Hide file tree
Showing 4 changed files with 40 additions and 9 deletions.
26 changes: 22 additions & 4 deletions src/AutoRemoteSyscalls.cc
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
#include "kernel_abi.h"
#include "kernel_metadata.h"
#include "log.h"
#include "record_signal.h"
#include "util.h"

using namespace std;
Expand Down Expand Up @@ -97,7 +98,8 @@ AutoRemoteSyscalls::AutoRemoteSyscalls(Task* t,
use_singlestep_path(false),
enable_mem_params_(enable_mem_params),
restore_sigmask(false),
need_sigpending_renable(false) {
need_sigpending_renable(false),
need_desched_event_reenable(false) {
if (initial_at_seccomp) {
// This should only ever happen during recording - we don't use the
// seccomp traps during replay.
Expand Down Expand Up @@ -142,15 +144,28 @@ AutoRemoteSyscalls::AutoRemoteSyscalls(Task* t,
}
if (t->session().is_recording()) {
RecordTask *rt = static_cast<RecordTask*>(t);
sig_set_t signals_to_block = 0;

if (rt->schedule_frozen) {
// If we're explicitly controlling the schedule, make sure not to accidentally run
// any signals that we were not meant to be able to see.
memset(&signals_to_block, 0xff, sizeof(sig_set_t));
}
if (desched_event_armed(rt)) {
// If the desched event is enabled, we need to disable it, so that we don't get
// the desched signal interrupting the syscall we're trying to make. We also
// need to mask it, so that if there's a pending desched signal from before
// we disable it, we don't accidently steal it.
signals_to_block |= signal_bit(rt->session().syscallbuf_desched_sig());
need_desched_event_reenable = true;
disarm_desched_event(rt);
}

if (signals_to_block) {
restore_sigmask = true;
sigmask_to_restore = rt->get_sigmask();
sig_set_t all_blocked;
memset(&all_blocked, 0xff, sizeof(all_blocked));
// Ignore the process dying here - we'll notice later.
(void)rt->set_sigmask(all_blocked);
(void)rt->set_sigmask(signals_to_block | sigmask_to_restore);
}
}
}
Expand Down Expand Up @@ -325,6 +340,9 @@ void AutoRemoteSyscalls::restore_state_to(Task* t) {
if (restore_sigmask) {
static_cast<RecordTask*>(t)->set_sigmask(sigmask_to_restore);
}
if (need_desched_event_reenable) {
arm_desched_event(static_cast<RecordTask*>(t));
}
if (need_sigpending_renable) {
// The purpose of this PTRACE_INTERRUPT is to re-enable TIF_SIGPENDING on
// the tracee, without forcing any actual signals on it. Since PTRACE_INTERRUPT
Expand Down
1 change: 1 addition & 0 deletions src/AutoRemoteSyscalls.h
Original file line number Diff line number Diff line change
Expand Up @@ -329,6 +329,7 @@ class AutoRemoteSyscalls {
sig_set_t sigmask_to_restore;

bool need_sigpending_renable;
bool need_desched_event_reenable;

AutoRemoteSyscalls& operator=(const AutoRemoteSyscalls&) = delete;
AutoRemoteSyscalls(const AutoRemoteSyscalls&) = delete;
Expand Down
21 changes: 16 additions & 5 deletions src/record_signal.cc
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,20 @@ void arm_desched_event(RecordTask* t) {
}
}

bool desched_event_armed(RecordTask *t) {
if (t->syscallbuf_child == nullptr) {
return false;
}
bool ok = true;
bool is_armed = t->read_mem(
REMOTE_PTR_FIELD(t->syscallbuf_child, desched_signal_may_be_relevant), &ok);
if (!ok) {
// If we can't read this (perhaps syscallbuf isn't actually mapped), it's not armed
return false;
}
return is_armed;
}

template <typename Arch>
static remote_code_ptr get_stub_scratch_1_arch(RecordTask* t) {
auto remote_locals = AddressSpace::preload_thread_locals_start()
Expand Down Expand Up @@ -274,8 +288,7 @@ bool handle_syscallbuf_breakpoint(RecordTask* t) {
// We're at an untraced-syscall entry point.
// To allow an AutoRemoteSyscall, we need to make sure desched signals are
// disarmed (and rearmed afterward).
bool armed_desched_event = t->read_mem(
REMOTE_PTR_FIELD(t->syscallbuf_child, desched_signal_may_be_relevant));
bool armed_desched_event = desched_event_armed(t);
if (armed_desched_event) {
disarm_desched_event(t);
}
Expand Down Expand Up @@ -327,9 +340,7 @@ static void handle_desched_event(RecordTask* t) {
* the desched_signal_may_be_relevant was set by the outermost syscallbuf
* invocation.
*/
if (!t->read_mem(REMOTE_PTR_FIELD(t->syscallbuf_child,
desched_signal_may_be_relevant)) ||
t->running_inside_desched()) {
if (!desched_event_armed(t) || t->running_inside_desched()) {
LOG(debug) << " (not entering may-block syscall; resuming)";
/* We have to disarm the event just in case the tracee
* has cleared the relevancy flag, but not yet
Expand Down
1 change: 1 addition & 0 deletions src/record_signal.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ const int SIGCHLD_SYNTHETIC = 0xbeadf00d;

void disarm_desched_event(RecordTask* t);
void arm_desched_event(RecordTask* t);
bool desched_event_armed(RecordTask *t);
bool handle_syscallbuf_breakpoint(RecordTask* t);

enum SignalBlocked { SIG_UNBLOCKED = 0, SIG_BLOCKED = 1 };
Expand Down

0 comments on commit ceeff12

Please sign in to comment.