Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 43 additions & 0 deletions src/Event.cc
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,14 @@ 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.state = o.sigsegv.state;

sigsegv.addr = o.sigsegv.addr;
sigsegv.len = o.sigsegv.len;
sigsegv.value = o.sigsegv.value;
return;
}
default:
return;
}
Expand Down Expand Up @@ -85,6 +93,8 @@ bool Event::record_regs() const {
case EV_SIGNAL_DELIVERY:
case EV_SIGNAL_HANDLER:
return true;
case EV_SIGSEGV_PATCHING:
return true;
default:
return false;
}
Expand All @@ -103,6 +113,8 @@ bool Event::record_extra_regs() const {
// entering a signal handler seems to clear FP/SSE regs,
// so record these effects.
return true;
case EV_SIGSEGV_PATCHING:
return true;
default:
return false;
}
Expand Down Expand Up @@ -160,6 +172,23 @@ string Event::str() const {
case EV_SYSCALL_INTERRUPTION:
ss << ": " << syscall_name(Syscall().number, Syscall().regs.arch());
break;
case EV_SIGSEGV_PATCHING: {
ss << ": ";
switch (sigsegv.state) {
case SIGSEGV_PATCHING_DUMMY:
ss << "state=DUMMY ";
break;
case SIGSEGV_PATCHING_ENTERING:
ss << "state=ENTERING ";
break;
case SIGSEGV_PATCHING_EXITING:
ss << "state=EXITING ";
break;
default:
break;
}
break;
}
default:
// No auxiliary information.
break;
Expand Down Expand Up @@ -211,13 +240,27 @@ 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(SigsegvPatchingState state, uintptr_t addr, size_t len, uint64_t value)
{
Event ev = Event(EV_SIGSEGV_PATCHING);

ev.sigsegv.state = state;

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
58 changes: 58 additions & 0 deletions src/Event.h
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,11 @@ enum EventType {
// Use .syscall.
EV_SYSCALL,

/**
* TODO(sodar): Document
*/
EV_SIGSEGV_PATCHING,

EV_LAST
};

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

/**
* TODO(sodar): Document
*/
enum SigsegvPatchingState {
/**
* Dummy value.
*/
SIGSEGV_PATCHING_DUMMY,

/**
* rr caught SIGSEGV on "monitored" memory region. rr should
* change memory region's protection and record memory.
*/
SIGSEGV_PATCHING_ENTERING,

/**
* faulting instruction was single stepped. rr should rever memory region's
* protection and resume to the next event.
*/
SIGSEGV_PATCHING_EXITING,
};

/**
* TODO(sodar): Document
*/
class SigsegvPatchingEvent {
public:
SigsegvPatchingEvent()
: state(SIGSEGV_PATCHING_DUMMY),
addr(0),
len(0),
value(0) {}

public:
SigsegvPatchingState state;

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 +366,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 +428,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(SigsegvPatchingState state, uintptr_t addr, size_t len, uint64_t value);

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

Expand Down
58 changes: 40 additions & 18 deletions src/MonitoredSharedMemory.cc
Original file line number Diff line number Diff line change
Expand Up @@ -18,31 +18,53 @@ namespace rr {
MonitoredSharedMemory::~MonitoredSharedMemory() { munmap(real_mem, size); }

static const char dconf_suffix[] = "/dconf/user";
static const char rtemap_prefix[] = "/dev/hugepages";

void MonitoredSharedMemory::maybe_monitor(RecordTask* t,
const string& file_name,
const AddressSpace::Mapping& m,
int tracee_fd, uint64_t offset) {
AddressSpace::Mapping& m,
int tracee_fd, uint64_t offset)
{
bool is_dconf = true;
bool is_rtemap = true;

size_t dconf_suffix_len = sizeof(dconf_suffix) - 1;
if (file_name.size() < dconf_suffix_len ||
file_name.substr(file_name.size() - dconf_suffix_len) != dconf_suffix) {
return;
is_dconf = false;
}

AutoRemoteSyscalls remote(t);
size_t rtemap_prefix_len = sizeof(rtemap_prefix) - 1;
if (file_name.size() < rtemap_prefix_len ||
file_name.substr(0, rtemap_prefix_len) != rtemap_prefix) {
is_rtemap = false;
}

if (!is_dconf && !is_rtemap) {
return;
};

ScopedFd fd = remote.retrieve_fd(tracee_fd);
uint8_t* real_mem = static_cast<uint8_t*>(
mmap(NULL, m.map.size(), PROT_READ, MAP_SHARED, fd, offset));
ASSERT(t, real_mem != MAP_FAILED);
AutoRemoteSyscalls remote(t, AutoRemoteSyscalls::DISABLE_MEMORY_PARAMS);
int syscallno = syscall_number_for_mprotect(t->arch());
remote.infallible_syscall(syscallno, m.map.start(), m.map.size(),
PROT_NONE);

auto result = shared_ptr<MonitoredSharedMemory>(
new MonitoredSharedMemory(real_mem, m.map.size()));
((void)tracee_fd);
((void)offset);

const AddressSpace::Mapping& shared =
Session::steal_mapping(remote, m, move(result));
// ScopedFd fd = remote.retrieve_fd(tracee_fd);
// uint8_t* real_mem = static_cast<uint8_t*>(
// mmap(NULL, m.map.size(), PROT_READ | PROT_WRITE, MAP_SHARED, fd, offset));
// ASSERT(t, real_mem != MAP_FAILED);

// auto result = shared_ptr<MonitoredSharedMemory>(
// new MonitoredSharedMemory(real_mem, m.map.size()));

// const AddressSpace::Mapping& shared =
// Session::steal_mapping(remote, m, move(result));
// m may be invalid now
memcpy(shared.local_addr, real_mem, shared.map.size());
// memcpy(shared.local_addr, real_mem, shared.map.size());
// ((void)shared);
}

MonitoredSharedMemory::shr_ptr MonitoredSharedMemory::subrange(uintptr_t,
Expand Down Expand Up @@ -74,10 +96,10 @@ void MonitoredSharedMemory::check_for_changes(RecordTask* t,
m = Session::recreate_shared_mmap(remote, m, Session::DISCARD_CONTENTS,
move(msm));
}
if (!memcmp(m.local_addr, real_mem, size)) {
return;
}
memcpy(m.local_addr, real_mem, size);
t->record_local(m.map.start(), size, real_mem);
// if (!memcmp(m.local_addr, real_mem, size)) {
// return;
// }
// memcpy(m.local_addr, real_mem, size);
// t->record_local(m.map.start(), size, real_mem);
}
}
2 changes: 1 addition & 1 deletion src/MonitoredSharedMemory.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ class MonitoredSharedMemory {
typedef std::shared_ptr<MonitoredSharedMemory> shr_ptr;

static void maybe_monitor(RecordTask* t, const std::string& file_name,
const AddressSpace::Mapping& m, int tracee_fd,
AddressSpace::Mapping& m, int tracee_fd,
uint64_t offset);

static void check_all(RecordTask* t);
Expand Down
36 changes: 36 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,36 @@ 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();

{
auto& mapping = t->vm()->mapping_of(sp.addr);

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_NONE);
}

t->sigsegv_patching = false;
t->record_event(Event::sigsegv_patching(SIGSEGV_PATCHING_EXITING, 0, 0, 0),
RecordTask::FLUSH_SYSCALLBUF);
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 @@ -2346,8 +2379,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 @@ -748,6 +748,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
Loading