Skip to content

Commit

Permalink
Disable single stepping once we've stepped over the breakpoint (fix #223
Browse files Browse the repository at this point in the history
) (#224)

This PR fixes a regression in the KVM backend that was introduced in `v0.5.5` when implementing RIP traces. 

The issue happens when the user has a breakpoint set-up that won't move execution; in that case `wtf` needs to step-over that breakpoint to carry on execution. To do that, it temporarily removes the breakpoint off of memory and will single-step this instruction. After the single-step, we receive a fault and we can figure out that the reason why we're getting this fault is because we were single-stepping over a breakpoint in which case we need to re-enable it, etc.

Because that single-step bit wasn't properly unset in that case, execution would carry on and re-enter with another single step instruction but this time the state didn't indicate that it was because we were performing a step-over, so `wtf` aborts.

Here is an illustration of the bug with the HEVD/KVM with logging on:
```
bubuntu:~/wtf/targets/hevd$ sudo ../../src/build/wtf run --name hevd --input ./inputs/A --backend kvm
...
Running ./inputs/A
kvm: exit_reason = KVM_EXIT_DEBUG @ 0x7ff6f5bb111e
kvm: Handling bp @ 0x7ff6f5bb111e
Hevd: Hello!
kvm: Disarming bp and turning on RFLAGS.TF (rip=0x7ff6f5bb111e)
kvm: Turning on RFLAGS.TF
kvm: exit_reason = KVM_EXIT_DEBUG @ 0x7ff83e2e6360
kvm: Received debug trap @ 0x7ff83e2e6360 <------------------------ first, expected trap
kvm: Resetting breakpoint @ 0xd37411e
kvm: Turning off RFLAGS.TF                              <------------------------ this was actually not done
kvm: exit_reason = KVM_EXIT_DEBUG @ 0x7ff83e2e6365
kvm: Received debug trap @ 0x7ff83e2e6365 <------------------------- second trap which is unexpected
Got into OnDebugTrap with LastBreakpointGpa_ = none
--------------------------------------------------
Run stats:
          Dirty pages: 53248 bytes, 13 pages, 0 MB
            UffdPages: 90112 bytes, 22 pages, 0 MB
              VMExits: 3
Instructions executed: 2
#1 cov: 0 exec/s: 0.0 lastcov: 0.0s crash: 0 timeout: 0 cr3: 0 uptime: 0.0s
```

Here is the expected output / the fixed version:

```
bubuntu:~/wtf/targets/hevd$ sudo ../../src/build/wtf run --name hevd --input ./inputs/A --backend kvm
...
Running ./inputs/A
kvm: exit_reason = KVM_EXIT_DEBUG @ 0x7ff6f5bb111e
kvm: Handling bp @ 0x7ff6f5bb111e
Hevd: This is a breakpoint executed before the first instruction :)
kvm: Disarming bp and will turn on single step (rip=0x7ff6f5bb111e)
kvm: Turning on SINGLESTEP
kvm: exit_reason = KVM_EXIT_DEBUG @ 0x7ff83e2e6360
kvm: Received debug trap @ 0x7ff83e2e6360
kvm: Resetting breakpoint @ 0xd37411e
kvm: Turning off SINGLESTEP
kvm: exit_reason = KVM_EXIT_DEBUG @ 0xfffff8046f122bb0
kvm: Handling bp @ 0xfffff8046f122bb0
Hevd: DbgPrintEx: [-] Invalid IOCTL Code: 0x%X
kvm: The bp handler ended up moving @rip from 0xfffff8046f122bb0 to 0xfffff8046ca955ec so no need to do the step-over dance
kvm: exit_reason = KVM_EXIT_DEBUG @ 0x7ff6f5bb1124
kvm: Handling bp @ 0x7ff6f5bb1124
Hevd: Back from kernel!
kvm: The bp handler asked us to stop so no need to do the step-over dance
--------------------------------------------------
Run stats:
          Dirty pages: 663552 bytes, 162 pages, 0 MB
            UffdPages: 684032 bytes, 167 pages, 0 MB
              VMExits: 4
Instructions executed: 6400
#1 cov: 0 exec/s: 0.0 lastcov: 0.0s crash: 0 timeout: 0 cr3: 0 uptime: 0.0s
```
  • Loading branch information
0vercl0k authored Jan 23, 2025
1 parent ebdccdd commit 8576012
Show file tree
Hide file tree
Showing 3 changed files with 386 additions and 371 deletions.
16 changes: 15 additions & 1 deletion src/wtf/fuzzer_hevd.cc
Original file line number Diff line number Diff line change
Expand Up @@ -61,10 +61,24 @@ bool InsertTestcase(const uint8_t *Buffer, const size_t BufferSize) {
bool Init(const Options_t &Opts, const CpuState_t &) {

//
// Stop the test-case once we return back from the call [DeviceIoControl]
// Set a breakpoint on the first instruction just for fun. Also, that type of
// breakpoint would have caught this bug:
// `https://github.com/0vercl0k/wtf/issues/223`
//

const Gva_t Rip = Gva_t(g_Backend->Rip());
if (!g_Backend->SetBreakpoint(Rip, [](Backend_t *Backend) {
DebugPrint(
"This is a breakpoint executed before the first instruction :)\n");
})) {
DebugPrint("Failed to SetBreakpoint on first instruction\n");
return false;
}

//
// Stop the test-case once we return back from the call [DeviceIoControl]
//

const Gva_t AfterCall = Rip + Gva_t(6);
if (!g_Backend->SetBreakpoint(AfterCall, [](Backend_t *Backend) {
DebugPrint("Back from kernel!\n");
Expand Down
19 changes: 8 additions & 11 deletions src/wtf/kvm_backend.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1532,7 +1532,8 @@ bool KvmBackend_t::OnExitDebug(struct kvm_debug_exit_arch &Debug) {
// fault.
//

KvmDebugPrint("Disarming bp and turning on RFLAGS.TF (rip={:#x})\n", Rip);
KvmDebugPrint("Disarming bp and will turn on single step (rip={:#x})\n",
Rip);
LastBreakpointGpa_ = Breakpoint.Gpa;

Ram_.RemoveBreakpoint(Breakpoint.Gpa);
Expand All @@ -1558,7 +1559,7 @@ bool KvmBackend_t::OnExitDebug(struct kvm_debug_exit_arch &Debug) {
//

if (TraceType_ != TraceType_t::Rip && !LastBreakpointGpa_) {
fmt::print("Got into OnDebugTrap with LastBreakpointGpa_ = none");
fmt::print("Got into OnDebugTrap with LastBreakpointGpa_ = none\n");
return false;
}

Expand All @@ -1568,7 +1569,7 @@ bool KvmBackend_t::OnExitDebug(struct kvm_debug_exit_arch &Debug) {
//

if (LastBreakpointGpa_) {
KvmDebugPrint("Resetting breakpoint @ {:#x}", *LastBreakpointGpa_);
KvmDebugPrint("Resetting breakpoint @ {:#x}\n", *LastBreakpointGpa_);

//
// Remember if we get there, it is because we hit a breakpoint, turned on
Expand All @@ -1580,12 +1581,7 @@ bool KvmBackend_t::OnExitDebug(struct kvm_debug_exit_arch &Debug) {
LastBreakpointGpa_.reset();
}

if (TraceType_ == TraceType_t::Rip) {
TrapFlag(true);
} else {
KvmDebugPrint("Turning off RFLAGS.TF\n");
}

TrapFlag(TraceType_ == TraceType_t::Rip ? true : false);
return true;
} else {
std::abort();
Expand Down Expand Up @@ -2651,13 +2647,14 @@ void KvmBackend_t::StaticSignalAlarm(int, siginfo_t *, void *) {
}

void KvmBackend_t::TrapFlag(const bool Arm) {
KvmDebugPrint("Turning on RFLAGS.TF\n");

struct kvm_guest_debug Dreg = {0};
Dreg.control = KVM_GUESTDBG_USE_SW_BP | KVM_GUESTDBG_ENABLE;

if (Arm) {
KvmDebugPrint("Turning on SINGLESTEP\n");
Dreg.control |= KVM_GUESTDBG_SINGLESTEP;
} else {
KvmDebugPrint("Turning off SINGLESTEP\n");
}

if (!SetDreg(Dreg)) {
Expand Down
Loading

0 comments on commit 8576012

Please sign in to comment.