Skip to content

Commit

Permalink
WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
sodar committed Sep 16, 2020
1 parent cb292e5 commit 79c421a
Show file tree
Hide file tree
Showing 11 changed files with 222 additions and 3 deletions.
16 changes: 16 additions & 0 deletions src/Event.cc
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,10 @@ Event::Event(const Event& o) : event_type(o.event_type) {
case EV_SYSCALLBUF_FLUSH:
new (&SyscallbufFlush()) SyscallbufFlushEvent(o.SyscallbufFlush());
return;
case EV_SIGSEGV_PATCHING:
sigsegv.addr = o.sigsegv.addr;
sigsegv.len = o.sigsegv.len;
sigsegv.value = o.sigsegv.value;
default:
return;
}
Expand Down Expand Up @@ -211,13 +215,25 @@ std::string Event::type_name() const {
CASE(SYSCALL);
CASE(SYSCALL_INTERRUPTION);
CASE(TRACE_TERMINATION);
CASE(SIGSEGV_PATCHING);
#undef CASE
default:
FATAL() << "Unknown event type " << event_type;
return ""; // not reached
}
}

Event Event::sigsegv_patching(uintptr_t addr, size_t len, uint64_t value)
{
Event ev = Event(EV_SIGSEGV_PATCHING);

ev.sigsegv.addr = addr;
ev.sigsegv.len = len;
ev.sigsegv.value = value;

return ev;
}

const char* state_name(SyscallState state) {
switch (state) {
#define CASE(_id) \
Expand Down
21 changes: 21 additions & 0 deletions src/Event.h
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,8 @@ enum EventType {
// Use .syscall.
EV_SYSCALL,

EV_SIGSEGV_PATCHING,

EV_LAST
};

Expand Down Expand Up @@ -257,6 +259,13 @@ struct syscall_interruption_t {
};
static const syscall_interruption_t interrupted;

// TODO(sodar)
struct SigsegvPatchingEvent {
uintptr_t addr;
size_t len;
uint64_t value;
};

/**
* Sum type for all events (well, a C++ approximation thereof). An
* Event always has a definted EventType. It can be down-casted to
Expand Down Expand Up @@ -320,6 +329,16 @@ struct Event {
return syscall;
}

SigsegvPatchingEvent& Sigsegv() {
DEBUG_ASSERT(event_type == EV_SIGSEGV_PATCHING);
return sigsegv;
}

const SigsegvPatchingEvent& Sigsegv() const {
DEBUG_ASSERT(event_type == EV_SIGSEGV_PATCHING);
return sigsegv;
}

bool record_regs() const;

bool record_extra_regs() const;
Expand Down Expand Up @@ -372,6 +391,7 @@ struct Event {
static Event grow_map() { return Event(EV_GROW_MAP); }
static Event exit() { return Event(EV_EXIT); }
static Event sentinel() { return Event(EV_SENTINEL); }
static Event sigsegv_patching(uintptr_t addr, size_t len, uint64_t value);

private:
Event(EventType type) : event_type(type) {}
Expand All @@ -383,6 +403,7 @@ struct Event {
SignalEvent signal;
SyscallEvent syscall;
SyscallbufFlushEvent syscallbuf_flush;
SigsegvPatchingEvent sigsegv;
};
};

Expand Down
32 changes: 32 additions & 0 deletions src/RecordSession.cc
Original file line number Diff line number Diff line change
Expand Up @@ -785,6 +785,9 @@ void RecordSession::task_continue(const StepState& step_state) {

bool singlestep = is_ptrace_any_singlestep(t->arch(),
t->emulated_ptrace_cont_command);
if (!singlestep && t->sigsegv_patching) {
singlestep = true;
}
if (singlestep && is_at_syscall_instruction(t, t->ip())) {
// We're about to singlestep into a syscall instruction.
// Act like we're NOT singlestepping since doing a PTRACE_SINGLESTEP would
Expand Down Expand Up @@ -1775,6 +1778,32 @@ bool RecordSession::handle_signal_event(RecordTask* t, StepState* step_state) {
return true;
}

// TODO(sodar)
bool RecordSession::handle_sigsegv_patching_event(RecordTask* t, StepState* step_state)
{
((void)step_state);

if (t->ev().type() == EV_SIGSEGV_PATCHING) {
auto sp = t->ev().Sigsegv();

{
AutoRemoteSyscalls remote(t, AutoRemoteSyscalls::DISABLE_MEMORY_PARAMS);
int syscallno = syscall_number_for_mprotect(t->arch());
remote.infallible_syscall(syscallno, sp.addr, sp.len,
PROT_NONE);
}

t->sigsegv_patching = false;
t->pop_event(t->ev().type());
// TODO(sodar): real event here.
t->push_event(Event::noop());

return true;
}

return false;
}

template <typename Arch>
static bool is_ptrace_any_sysemu_arch(int command) {
return command >= 0 &&
Expand Down Expand Up @@ -2347,8 +2376,11 @@ RecordSession::RecordResult RecordSession::record_step() {
if (did_enter_syscall && t->ev().type() == EV_SYSCALL) {
syscall_state_changed(t, &step_state);
}
} else if (rescheduled.by_waitpid && handle_sigsegv_patching_event(t, &step_state)) {
goto sigsegv_patching_handled;
} else if (rescheduled.by_waitpid && handle_signal_event(t, &step_state)) {
} else {
sigsegv_patching_handled:
runnable_state_changed(t, &step_state, &result, rescheduled.by_waitpid);

if (result.status != STEP_CONTINUE ||
Expand Down
1 change: 1 addition & 0 deletions src/RecordSession.h
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,7 @@ class RecordSession : public Session {
bool handle_ptrace_event(RecordTask** t_ptr, StepState* step_state,
RecordResult* result, bool* did_enter_syscall);
bool handle_signal_event(RecordTask* t, StepState* step_state);
bool handle_sigsegv_patching_event(RecordTask* t, StepState* step_state);
void runnable_state_changed(RecordTask* t, StepState* step_state,
RecordResult* step_result,
bool can_consume_wait_status);
Expand Down
3 changes: 2 additions & 1 deletion src/RecordTask.cc
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,8 @@ RecordTask::RecordTask(RecordSession& session, pid_t _tid, uint32_t serial,
waiting_for_reap(false),
waiting_for_zombie(false),
waiting_for_ptrace_exit(false),
retry_syscall_patching(false) {
retry_syscall_patching(false),
sigsegv_patching(false) {
push_event(Event::sentinel());
if (session.tasks().empty()) {
// Initial tracee. It inherited its state from this process, so set it up.
Expand Down
3 changes: 3 additions & 0 deletions src/RecordTask.h
Original file line number Diff line number Diff line change
Expand Up @@ -747,6 +747,9 @@ class RecordTask : public Task {

// When exiting a syscall, we should call MonkeyPatcher::try_patch_syscall again.
bool retry_syscall_patching;

// TODO(sodar): Document.
bool sigsegv_patching;
};

} // namespace rr
Expand Down
76 changes: 76 additions & 0 deletions src/ReplaySession.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1353,6 +1353,9 @@ static bool has_deterministic_ticks(const Event& ev,
if (ev.has_ticks_slop()) {
return false;
}
if (step.action == TSTEP_SIGSEGV_PATCHING) {
return false;
}
// We won't necessarily reach the same ticks when replaying an
// async signal, due to debugger interrupts and other
// implementation details. This is checked in |advance_to()|
Expand Down Expand Up @@ -1426,6 +1429,8 @@ Completion ReplaySession::try_one_trace_step(
return patch_next_syscall(t, constraints, false);
case TSTEP_EXIT_TASK:
return exit_task(t);
case TSTEP_SIGSEGV_PATCHING:
return do_sigsegv_patching(t, constraints);
default:
FATAL() << "Unhandled step type " << current_step.action;
return COMPLETE;
Expand Down Expand Up @@ -1503,6 +1508,72 @@ ReplayTask* ReplaySession::revive_task_for_exec() {
return t;
}

void ReplaySession::rep_process_sigsegv_patching(ReplayTask* t, ReplayTraceStep* step, const Event& ev)
{
((void)t);
((void)step);

const auto& sigsegv = ev.Sigsegv();

uintptr_t addr = sigsegv.addr;
size_t len = sigsegv.len;

((void)addr);
((void)len);

{
AutoRemoteSyscalls remote(t, AutoRemoteSyscalls::DISABLE_MEMORY_PARAMS);
int syscallno = syscall_number_for_mprotect(t->arch());
remote.infallible_syscall(syscallno, addr, len, PROT_READ | PROT_WRITE);
}
}


// Completion ReplaySession::continue_or_step(ReplayTask* t,
// const StepConstraints& constraints,
// TicksRequest tick_request,
// ResumeRequest resume_how) {

Completion ReplaySession::do_sigsegv_patching(ReplayTask *t, const StepConstraints& constraint)
{
((void)t);
((void)constraint);

if (t->tick_count() < trace_frame.ticks()) {
// TicksRequest ticks_request = RESUME_UNLIMITED_TICKS;
// if (constraint.ticks_target <= trace_frame.ticks()) {
// if (!compute_ticks_request(t, constraint, &ticks_request)) {
// return INCOMPLETE;
// }
// }

// if (constraint.is_singlestep()) {
// t->resume_execution(RESUME_SINGLESTEP, RESUME_WAIT, ticks_request);
// } else {
// t->resume_execution(RESUME_CONT, RESUME_WAIT, ticks_request);
// }

// return INCOMPLETE;

while (true) {
TicksRequest ticks_request = (TicksRequest)(trace_frame.ticks() - t->tick_count());
auto completion = continue_or_step(t, constraint, ticks_request);
if (completion == COMPLETE) {
break;
}
}
}

auto& ev = trace_frame.event();
ASSERT(t, ev.type() == EV_SIGSEGV_PATCHING);

auto ptr = remote_ptr<uint64_t>(ev.Sigsegv().addr);
uint64_t val = ev.Sigsegv().value;
t->write_mem(ptr, val);

return COMPLETE;
}

/**
* Set up rep_trace_step state in t's Session to start replaying towards
* the event given by the session's current_trace_frame --- but only if
Expand Down Expand Up @@ -1635,6 +1706,11 @@ ReplayTask* ReplaySession::setup_replay_one_trace_frame(ReplayTask* t) {
}
}
break;
case EV_SIGSEGV_PATCHING: {
current_step.action = TSTEP_SIGSEGV_PATCHING;
rep_process_sigsegv_patching(t, &current_step, ev);
break;
}
default:
FATAL() << "Unexpected event " << ev;
}
Expand Down
6 changes: 6 additions & 0 deletions src/ReplaySession.h
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,9 @@ enum ReplayTraceStepType {
/* Exit the task */
TSTEP_EXIT_TASK,

// TODO(sodar)
TSTEP_SIGSEGV_PATCHING,

/* Frame has been replayed, done. */
TSTEP_RETIRE,
};
Expand Down Expand Up @@ -354,6 +357,9 @@ class ReplaySession : public Session {
const StepConstraints& constraints,
BreakStatus& break_status);

void rep_process_sigsegv_patching(ReplayTask* t, ReplayTraceStep* step, const Event& ev);
Completion do_sigsegv_patching(ReplayTask *t, const StepConstraints& constraint);

void clear_syscall_bp();

std::shared_ptr<EmuFs> emu_fs;
Expand Down
14 changes: 14 additions & 0 deletions src/TraceStream.cc
Original file line number Diff line number Diff line change
Expand Up @@ -499,6 +499,15 @@ void TraceWriter::write_frame(RecordTask* t, const Event& ev,
}
break;
}
case EV_SIGSEGV_PATCHING: {
const auto& s = ev.Sigsegv();
auto sigsegv = event.initSigsegvPatching();
sigsegv.setAddr(s.addr);
sigsegv.setLen(s.len);
sigsegv.setValue(s.value);
// event.setSigsegvPatching(Void());
break;
}
default:
FATAL() << "Event type not recordable";
break;
Expand Down Expand Up @@ -667,6 +676,11 @@ TraceFrame TraceReader::read_frame() {
}
break;
}
case trace::Frame::Event::SIGSEGV_PATCHING: {
auto s = event.getSigsegvPatching();
ret.ev = Event::sigsegv_patching(s.getAddr(), s.getLen(), s.getValue());
break;
}
default:
FATAL() << "Event type not supported";
break;
Expand Down
48 changes: 46 additions & 2 deletions src/record_signal.cc
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,44 @@ static bool try_handle_trapped_instruction(RecordTask* t, siginfo_t* si) {
return true;
}

static bool try_handle_prot_none(RecordTask* t, siginfo_t* si)
{
ASSERT(t, si->si_signo == SIGSEGV);

// Use kernel_abi to avoid odd inconsistencies between distros
auto arch_si = reinterpret_cast<NativeArch::siginfo_t*>(si);
auto addr = arch_si->_sifields._sigfault.si_addr_.rptr();

auto addr_value = addr.as_int();
if (addr_value != 0x10000000ULL) {
return false;
}

auto& mapping = t->vm()->mapping_of(addr);
((void)mapping);

{
AutoRemoteSyscalls remote(t, AutoRemoteSyscalls::DISABLE_MEMORY_PARAMS);
int syscallno = syscall_number_for_mprotect(t->arch());
remote.infallible_syscall(syscallno, mapping.map.start(), mapping.map.size(),
PROT_READ | PROT_WRITE);
}

bool ok = true;
auto p = addr.cast<uint64_t>();
auto val = t->read_mem(p, &ok);
ASSERT(t, ok) << "failed to read mem on sigsegv patching";

t->record_event(Event::sigsegv_patching(mapping.map.start().as_int(), mapping.map.size(), val),
RecordTask::FLUSH_SYSCALLBUF);

t->push_event(Event::sigsegv_patching(mapping.map.start().as_int(), mapping.map.size(), val));
t->push_event(Event::noop());
t->sigsegv_patching = true;

return true;
}

/**
* Return true if |t| was stopped because of a SIGSEGV and we want to retry
* the instruction after emulating MAP_GROWSDOWN.
Expand All @@ -148,8 +186,14 @@ static bool try_grow_map(RecordTask* t, siginfo_t* si) {
auto addr = arch_si->_sifields._sigfault.si_addr_.rptr();

if (t->vm()->has_mapping(addr)) {
LOG(debug) << "try_grow_map " << addr << ": address already mapped";
return false;
if (try_handle_prot_none(t, si)) {
LOG(debug) << "try_handle_prot_none " << addr << ": done";
// TODO(sodar): Return some value.
return true;
} else {
LOG(debug) << "try_grow_map " << addr << ": address already mapped";
return false;
}
}
auto maps = t->vm()->maps_starting_at(floor_page_size(addr));
auto it = maps.begin();
Expand Down
Loading

0 comments on commit 79c421a

Please sign in to comment.