diff --git a/src/Event.cc b/src/Event.cc index 5bd4c447325..e3a6737a302 100644 --- a/src/Event.cc +++ b/src/Event.cc @@ -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; } @@ -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; } @@ -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; } @@ -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; @@ -211,6 +240,7 @@ 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; @@ -218,6 +248,19 @@ std::string Event::type_name() const { } } +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) \ diff --git a/src/Event.h b/src/Event.h index c6460aba8d6..c7f00f1c9fe 100644 --- a/src/Event.h +++ b/src/Event.h @@ -85,6 +85,11 @@ enum EventType { // Use .syscall. EV_SYSCALL, + /** + * TODO(sodar): Document + */ + EV_SIGSEGV_PATCHING, + EV_LAST }; @@ -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 @@ -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; @@ -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) {} @@ -383,6 +440,7 @@ struct Event { SignalEvent signal; SyscallEvent syscall; SyscallbufFlushEvent syscallbuf_flush; + SigsegvPatchingEvent sigsegv; }; }; diff --git a/src/MonitoredSharedMemory.cc b/src/MonitoredSharedMemory.cc index 29aac3183ca..3880f1048b4 100644 --- a/src/MonitoredSharedMemory.cc +++ b/src/MonitoredSharedMemory.cc @@ -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( - 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( - 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( + // mmap(NULL, m.map.size(), PROT_READ | PROT_WRITE, MAP_SHARED, fd, offset)); + // ASSERT(t, real_mem != MAP_FAILED); + + // auto result = shared_ptr( + // 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, @@ -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); } } diff --git a/src/MonitoredSharedMemory.h b/src/MonitoredSharedMemory.h index 7bb5f92801d..b9350ba8914 100644 --- a/src/MonitoredSharedMemory.h +++ b/src/MonitoredSharedMemory.h @@ -35,7 +35,7 @@ class MonitoredSharedMemory { typedef std::shared_ptr 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); diff --git a/src/RecordSession.cc b/src/RecordSession.cc index 0041c9c5564..c33353bcbed 100644 --- a/src/RecordSession.cc +++ b/src/RecordSession.cc @@ -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 @@ -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 static bool is_ptrace_any_sysemu_arch(int command) { return command >= 0 && @@ -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 || diff --git a/src/RecordSession.h b/src/RecordSession.h index 33a9ece05f3..481f1a31f94 100644 --- a/src/RecordSession.h +++ b/src/RecordSession.h @@ -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); diff --git a/src/RecordTask.cc b/src/RecordTask.cc index a077a24f798..d8334a22ada 100644 --- a/src/RecordTask.cc +++ b/src/RecordTask.cc @@ -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. diff --git a/src/RecordTask.h b/src/RecordTask.h index e561b5cb19b..8de718cc1ad 100644 --- a/src/RecordTask.h +++ b/src/RecordTask.h @@ -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 diff --git a/src/ReplaySession.cc b/src/ReplaySession.cc index 67564b2ac2d..a9e816bd912 100644 --- a/src/ReplaySession.cc +++ b/src/ReplaySession.cc @@ -1353,6 +1353,10 @@ static bool has_deterministic_ticks(const Event& ev, if (ev.has_ticks_slop()) { return false; } + // TODO(sodar) + // 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()| @@ -1426,6 +1430,10 @@ 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_ENTERING: + return do_sigsegv_patching_entering(t, constraints); + case TSTEP_SIGSEGV_PATCHING_EXITING: + return do_sigsegv_patching_exiting(t, constraints); default: FATAL() << "Unhandled step type " << current_step.action; return COMPLETE; @@ -1503,6 +1511,133 @@ 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); + ((void)ev); +} + +void ReplaySession::rep_process_sigsegv_patching_exiting(ReplayTask* t, ReplayTraceStep* step, const Event& ev) +{ + ((void)t); + ((void)step); + ((void)ev); +} + +// Completion ReplaySession::continue_or_step(ReplayTask* t, +// const StepConstraints& constraints, +// TicksRequest tick_request, +// ResumeRequest resume_how) { + +Completion ReplaySession::do_sigsegv_patching_entering(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; + } + } + + t->resume_execution(RESUME_SINGLESTEP, RESUME_WAIT, RESUME_NO_TICKS); + handle_unrecorded_cpuid_fault(t, constraint); + } + + // SINGLESTEP - should trigger SIGSEGV + { + t->resume_execution(RESUME_SINGLESTEP, RESUME_WAIT, RESUME_NO_TICKS); + handle_unrecorded_cpuid_fault(t, constraint); + + ASSERT(t, t->stop_sig() == SIGSEGV) + << "do_sigsegv_patching_entering expected SIGSEGV, got" << t->stop_sig(); + } + + auto& ev = trace_frame.event(); + ASSERT(t, ev.type() == EV_SIGSEGV_PATCHING); + + { + const auto& sigsegv = ev.Sigsegv(); + ASSERT(t, sigsegv.state == SIGSEGV_PATCHING_ENTERING); + uintptr_t addr = sigsegv.addr; + size_t len = sigsegv.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); + } + + auto ptr = remote_ptr(sigsegv.addr); + uint64_t val = sigsegv.value; + t->write_mem(ptr, val); + + // t->invalidate_sigmask(); + } + + // TODO(sodar) - document + // SINGLESTEP - should read proper value + { + // t->set_status(WaitStatus::for_stop_sig(SIGTRAP)); + // t->resume_execution(RESUME_SINGLESTEP, RESUME_WAIT, RESUME_NO_TICKS); + // handle_unrecorded_cpuid_fault(t, constraint); + + // ASSERT(t, t->stop_sig() == SIGSEGV) + // << "do_sigsegv_patching_entering expected SIGSEGV, got" << t->stop_sig(); + } + + t->set_status(constraint.is_singlestep() ? WaitStatus::for_stop_sig(SIGTRAP) + : WaitStatus()); + + return COMPLETE; +} + +Completion ReplaySession::do_sigsegv_patching_exiting(ReplayTask *t, const StepConstraints& constraint) +{ + // ((void)t); + ((void)constraint); + + auto& ev = trace_frame.event(); + ASSERT(t, ev.type() == EV_SIGSEGV_PATCHING); + + { + const auto& sigsegv = ev.Sigsegv(); + ASSERT(t, sigsegv.state == SIGSEGV_PATCHING_EXITING); + uintptr_t addr = sigsegv.addr; + size_t len = sigsegv.len; + + { + AutoRemoteSyscalls remote(t, AutoRemoteSyscalls::DISABLE_MEMORY_PARAMS); + int syscallno = syscall_number_for_mprotect(t->arch()); + remote.infallible_syscall(syscallno, addr, len, PROT_NONE); + } + } + + t->set_regs(trace_frame.regs()); + t->set_extra_regs(trace_frame.extra_regs()); + + 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 @@ -1635,6 +1770,18 @@ ReplayTask* ReplaySession::setup_replay_one_trace_frame(ReplayTask* t) { } } break; + case EV_SIGSEGV_PATCHING: { + if (trace_frame.event().Sigsegv().state == SIGSEGV_PATCHING_ENTERING) { + current_step.action = TSTEP_SIGSEGV_PATCHING_ENTERING; + rep_process_sigsegv_patching(t, ¤t_step, ev); + } else if (trace_frame.event().Sigsegv().state == SIGSEGV_PATCHING_EXITING) { + current_step.action = TSTEP_SIGSEGV_PATCHING_EXITING; + rep_process_sigsegv_patching_exiting(t, ¤t_step, ev); + } else { + ASSERT(t, false) << "Unexpected SIGSEGV_PATHCHING event"; + } + break; + } default: FATAL() << "Unexpected event " << ev; } diff --git a/src/ReplaySession.h b/src/ReplaySession.h index dc3d0e92373..58cd913fc2b 100644 --- a/src/ReplaySession.h +++ b/src/ReplaySession.h @@ -68,6 +68,12 @@ enum ReplayTraceStepType { /* Exit the task */ TSTEP_EXIT_TASK, + // TODO(sodar) + TSTEP_SIGSEGV_PATCHING_ENTERING, + + // TODO(sodar) + TSTEP_SIGSEGV_PATCHING_EXITING, + /* Frame has been replayed, done. */ TSTEP_RETIRE, }; @@ -354,6 +360,14 @@ class ReplaySession : public Session { const StepConstraints& constraints, BreakStatus& break_status); + // TODO(sodar): Document + void rep_process_sigsegv_patching(ReplayTask* t, ReplayTraceStep* step, const Event& ev); + void rep_process_sigsegv_patching_exiting(ReplayTask* t, ReplayTraceStep* step, const Event& ev); + + // TODO(sodar): Document + Completion do_sigsegv_patching_entering(ReplayTask *t, const StepConstraints& constraint); + Completion do_sigsegv_patching_exiting(ReplayTask *t, const StepConstraints& constraint); + void clear_syscall_bp(); std::shared_ptr emu_fs; diff --git a/src/Session.cc b/src/Session.cc index 77a492391e7..2864f93bc0e 100644 --- a/src/Session.cc +++ b/src/Session.cc @@ -429,17 +429,31 @@ static void remap_shared_mmap(AutoRemoteSyscalls& remote, EmuFs& emu_fs, KernelMapping Session::create_shared_mmap( AutoRemoteSyscalls& remote, size_t size, remote_ptr map_hint, const char* name, int tracee_prot, int tracee_flags, - MonitoredSharedMemory::shr_ptr&& monitored) { + MonitoredSharedMemory::shr_ptr&& monitored) +{ + bool is_huge_backed = false; + + if (monitored) { + is_huge_backed = true; + } + static int nonce = 0; // Create the segment we'll share with the tracee. char path[PATH_MAX]; - snprintf(path, sizeof(path) - 1, "%s%s%s-%d-%d", tmp_dir(), + if (!is_huge_backed) { + snprintf(path, sizeof(path) - 1, "%s%s%s-%d-%d", tmp_dir(), rr_mapping_prefix(), name, remote.task()->real_tgid(), nonce++); + } else { + snprintf(path, sizeof(path) - 1, "%s%s%s-%d-%d", "/dev/hugepages", + rr_mapping_prefix(), name, remote.task()->real_tgid(), nonce++); + } ScopedFd shmem_fd(path, O_CREAT | O_EXCL | O_RDWR); /* Remove the fs name so that we don't have to worry about * cleaning up this segment in error conditions. */ - unlink(path); + if (!is_huge_backed) { + unlink(path); + } int child_shmem_fd = remote.send_fd(shmem_fd); resize_shmem_segment(shmem_fd, size); @@ -448,6 +462,7 @@ KernelMapping Session::create_shared_mmap( // Map the segment in ours and the tracee's address spaces. void* map_addr; int flags = MAP_SHARED; + if ((void*)-1 == (map_addr = mmap(nullptr, size, PROT_READ | PROT_WRITE, flags, shmem_fd, 0))) { FATAL() << "Failed to mmap shmem region"; @@ -455,6 +470,9 @@ KernelMapping Session::create_shared_mmap( if (!map_hint.is_null()) { flags |= MAP_FIXED; } + if (is_huge_backed) { + tracee_prot = PROT_NONE; + } remote_ptr child_map_addr = remote.infallible_mmap_syscall( map_hint, size, tracee_prot, flags, child_shmem_fd, 0); diff --git a/src/TraceStream.cc b/src/TraceStream.cc index 77ab1185432..8f11e27f761 100644 --- a/src/TraceStream.cc +++ b/src/TraceStream.cc @@ -323,6 +323,30 @@ static SyscallState from_trace_syscall_state(trace::SyscallState state) { } } +static trace::SigsegvPatchingState to_trace_sigsegv_state(SigsegvPatchingState state) { + switch (state) { + case SIGSEGV_PATCHING_ENTERING: + return trace::SigsegvPatchingState::ENTERING; + case SIGSEGV_PATCHING_EXITING: + return trace::SigsegvPatchingState::EXITING; + default: + FATAL() << "Unknown sigsegv patching state"; + return trace::SigsegvPatchingState::ENTERING; + } +} + +static SigsegvPatchingState from_trace_sigsegv_state(trace::SigsegvPatchingState state) { + switch (state) { + case trace::SigsegvPatchingState::ENTERING: + return SIGSEGV_PATCHING_ENTERING; + case trace::SigsegvPatchingState::EXITING: + return SIGSEGV_PATCHING_EXITING; + default: + FATAL() << "Unknown sigsegv patching state"; + return SIGSEGV_PATCHING_ENTERING; + } +} + static void to_trace_signal(trace::Signal::Builder signal, const Event& ev) { const SignalEvent& sig_ev = ev.Signal(); signal.setSiginfoArch(to_trace_arch(NativeArch::arch())); @@ -499,6 +523,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.setState(to_trace_sigsegv_state(s.state)); + sigsegv.setAddr(s.addr); + sigsegv.setLen(s.len); + sigsegv.setValue(s.value); + break; + } default: FATAL() << "Event type not recordable"; break; @@ -667,6 +700,12 @@ TraceFrame TraceReader::read_frame() { } break; } + case trace::Frame::Event::SIGSEGV_PATCHING: { + auto s = event.getSigsegvPatching(); + auto state = from_trace_sigsegv_state(s.getState()); + ret.ev = Event::sigsegv_patching(state, s.getAddr(), s.getLen(), s.getValue()); + break; + } default: FATAL() << "Event type not supported"; break; diff --git a/src/record_signal.cc b/src/record_signal.cc index f6e0d38f94a..0e28422c2fc 100644 --- a/src/record_signal.cc +++ b/src/record_signal.cc @@ -136,6 +136,48 @@ 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(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); + if (!mapping.monitored_shared_memory) { + return false; + } + + { + 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(); + auto val = t->read_mem(p, &ok); + ASSERT(t, ok) << "failed to read mem on sigsegv patching"; + + t->record_event(Event::sigsegv_patching(SIGSEGV_PATCHING_ENTERING, mapping.map.start().as_int(), + mapping.map.size(), val), + RecordTask::FLUSH_SYSCALLBUF); + + t->push_event(Event::sigsegv_patching(SIGSEGV_PATCHING_ENTERING, 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. @@ -148,8 +190,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(); diff --git a/src/record_syscall.cc b/src/record_syscall.cc index 3927c0f8cc3..2d814e2ac6c 100644 --- a/src/record_syscall.cc +++ b/src/record_syscall.cc @@ -5524,6 +5524,12 @@ static void process_mmap(RecordTask* t, size_t length, int prot, int flags, MonitoredSharedMemory::maybe_monitor(t, tracee_file_name, t->vm()->mapping_of(addr), fd, offset); } + + if ((prot & (PROT_WRITE | PROT_READ)) == (PROT_WRITE | PROT_READ) && (flags & MAP_SHARED) && + !effectively_anonymous) { + MonitoredSharedMemory::maybe_monitor(t, tracee_file_name, + t->vm()->mapping_of(addr), fd, offset); + } } static void process_mremap(RecordTask* t, remote_ptr old_addr, diff --git a/src/rr_trace.capnp b/src/rr_trace.capnp index b35345b7a7a..5b69186a2fb 100644 --- a/src/rr_trace.capnp +++ b/src/rr_trace.capnp @@ -217,6 +217,11 @@ struct OpenedFd { inode @3 :Inode; } +enum SigsegvPatchingState { + entering @0; + exiting @1; +} + # The 'events' file is a sequence of these. struct Frame { tid @0 :Tid; @@ -269,5 +274,11 @@ struct Frame { } patchAfterSyscall @26: Void; patchVsyscall @27: Void; + sigsegvPatching :group { + state @28 :SigsegvPatchingState; + addr @29 :UInt64; + len @30 :Int64; + value @31 :UInt64; + } } }