fix(debug): handle mapping edges, stray traps and dying debuggees
Assisted-by: GLM 5.3
This commit is contained in:
@@ -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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user