fix(debug): handle mapping edges, stray traps and dying debuggees

Assisted-by: GLM 5.3
This commit is contained in:
2026-10-02 00:40:54 +02:00
parent dd1782c538
commit f57377abb9
19 changed files with 726 additions and 97 deletions
+18 -1
View File
@@ -30,13 +30,25 @@ const (
// (arch/x86/kernel/ptrace.c send_sigtrap passes regs->ip), so the watched
// data address is recovered from DR6's slot bits (B0-B3, positive polarity
// through PEEKUSER) and the matching DR0-DR3.
//
// The B0-B3 bits are sticky ("the processor never clears DR6", Intel SDM
// vol 3, "Debug Registers"), so they are acknowledged here: cleared once
// read, or the next hit on a different slot would still see this slot's bit
// set and report this slot's address again.
func archWatchpointAddr(s *Session, siAddr uint64) uint64 {
dr6, err := ptracePeekUser(s.pid, dr6Off)
if err != nil {
return siAddr
}
status := dr6 & 0xF
if status == 0 {
return siAddr
}
// Best effort: the write-back only fails for a debuggee that died, in
// which case no further watchpoint can fire anyway.
_ = ptracePokeUser(s.pid, dr6Off, dr6&^0xF)
for slot := range 4 {
if dr6&(1<<slot) != 0 {
if status&(1<<slot) != 0 {
addr, err := ptracePeekUser(s.pid, drOffset+uintptr(slot*8))
if err == nil && addr != 0 {
return addr
@@ -139,6 +151,11 @@ func (s *Session) ClearWatchpoint(slot int) error {
if err := ptracePokeUser(s.pid, dr7Off, dr7); err != nil {
return err
}
// Zero the address register too: a stale address in a disabled slot
// turns any sticky DR6 bit into a misattributed watchpoint report.
if err := ptracePokeUser(s.pid, drOffset+uintptr(slot*8), 0); err != nil {
return err
}
s.wpSlots[slot] = false
return nil
}