-
Notifications
You must be signed in to change notification settings - Fork 18k
syscall: ptrace weird behaviour #50920
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
I don't really see how anything that a Go program could do would affect the ptrace behavior. The only interesting thing that I can think of is that Go programs are always multi-threaded, and I don't know how ptrace handles multi-threaded programs. If you can tell us something to change in Go, then we can consider changing it. But right now I have no idea how to make any progress on this issue. Sorry. |
As the ptrace documentation states, with PTRACE_SETOPTIONS is possible to specify few flags like:
Now this should be enough to cover an usual go program execution (main thread, goroutines, exec and so on..). |
I have made a testcase reproducer, the following code does the exact same thing I described: package main
import (
"fmt"
"log"
"os"
"os/exec"
"syscall"
"time"
"unsafe"
)
func callpayload(data []byte) {
missing := len(data)
pagesize := os.Getpagesize()
if (pagesize-1)&missing > 0 {
missing = ((missing + pagesize) & (^(pagesize - 1))) - missing
}
padding := make([]byte, missing)
data = append(data, padding...)
if err := syscall.Mprotect(data, syscall.PROT_READ|syscall.PROT_EXEC|syscall.PROT_READ); err != nil {
log.Fatal(err)
}
fnc_ptr := &data
fncptr := unsafe.Pointer(&fnc_ptr)
fnc_type := *(*func())(fncptr)
fnc_type()
}
func debugger(cmd *exec.Cmd) {
var status syscall.WaitStatus
if err := cmd.Start(); err != nil {
log.Fatal(err)
}
_ = cmd.Wait()
wpid := cmd.Process.Pid
var err error
syscall.PtraceSetOptions(-1, 0x100000|syscall.PTRACE_O_TRACECLONE|syscall.PTRACE_O_TRACEFORK|syscall.PTRACE_O_TRACEEXEC|syscall.PTRACE_O_TRACEVFORK)
syscall.PtraceCont(wpid, 0)
proc := cmd.Process
for {
wpid, err = syscall.Wait4(-1, &status, syscall.WALL, nil)
if err != nil {
err = fmt.Errorf("error while waiting for process: %v", err)
break
}
if status.Exited() || status.Signaled() {
log.Println("CHILD EXITED")
err = nil
break
} else if status.Stopped() {
if proc.Pid == wpid && (status.StopSignal() == syscall.SIGSEGV) {
var regs syscall.PtraceRegs
if err = syscall.PtraceGetRegs(proc.Pid, ®s); err != nil {
err = fmt.Errorf("error while getting registers: %v", err)
break
}
log.Printf("Got SIGSEGV %x", regs.Rip)
// Skil the actual hlt instruction so the execution should continue
regs.Rip += 1
if err = syscall.PtraceSetRegs(proc.Pid, ®s); err != nil {
err = fmt.Errorf("error while setting registers: %v", err)
break
}
}
if err = syscall.PtraceCont(proc.Pid, 0); err != nil {
err = fmt.Errorf("error while sending continue signal: %v", err)
break
}
} else if proc.Pid == wpid && (status.StopSignal() == syscall.SIGTRAP) {
log.Println("SIGTRAP!")
if err = syscall.PtraceCont(proc.Pid, 0); err != nil {
log.Printf("Error while sending continue signal: %v", err)
break
}
} else {
if err = syscall.PtraceCont(proc.Pid, int(status.StopSignal())); err != nil {
log.Printf("Error while sending continue signal: %v", err)
break
}
}
}
if err != nil {
proc.Kill()
log.Fatal(err)
}
os.Exit(0)
}
func install() {
var found bool = false
var index int = -1
for i, arg := range os.Args {
if arg == "test" {
found = true
index = i
}
}
if !found {
os.Args = append(os.Args, "test")
cmd := exec.Command(os.Args[0], os.Args[1:]...)
cmd.Env = os.Environ()
cmd.Stderr = os.Stderr
cmd.Stdout = os.Stdout
cmd.Stdin = os.Stdin
cmd.SysProcAttr = &syscall.SysProcAttr{Ptrace: true}
debugger(cmd)
} else {
os.Args = os.Args[0:index]
}
}
func main() {
// runtime.LockOSThread() if I use lock os thread the tracer is always able to catch SIGSEGV
// install the debugger on the parent process, and does nothing on the tracee
install()
// call a fake function with hlt instruction
counter := 0
for counter < 5 {
fnc := []byte{0x55, 0x48, 0x89, 0xe5, 0xf4, 0x5d, 0xc3} // 0xf4 = HLT
callpayload(fnc)
counter++
time.Sleep(10 * time.Microsecond)
}
fmt.Println("If you can see this the tracer worked just fine")
} That's the output of a normal execution flow:
Basically each SIGSEGV the signal is sent to the parent process (the tracer) which suppress it calling ptrace(PTRACE_CONT, pid, 0, 0), now this should be the default behaviour of the program however sometimes this happen:
Also seems like runtime.LockOSThread prevent this behaviour from happening, still can't find the root cause this stuff is driving me crazy. |
Timed out in state WaitingForInfo. Closing. (I am just a bot, though. Please speak up if this is a mistake or you have the requested information.) |
What version of Go are you using (
go version
)?Does this issue reproduce with the latest release?
Need to test with latest version
What operating system and processor architecture are you using (
go env
)?go env
OutputWhat did you do?
I have a binary and I replace some conditional jumps to HLT instruction which cause a SIGSEGV once executed , when the process start the first time the process run again itself but with different argument and start to trace the child , then in the ptrace loop the tracer handle all the SIGSEGV and replace the register with the correct values. Now this actually works fine but sometimes even if the child is uder ptrace (I confirmed that calling ptrace twice in the child and got operation not permitted) seems like the tracer can't catch the signals from the tracee , so the tracee actually crash with a panic (that should never happen in my opinion since the tracee was already being traced).
Maybe golang handle signals in a unique way , but since i'm tracing all the fork/exec + childs this should never happen anyway.
What did you expect to see?
I expected to be able to catch SIGSEGV signal everytime since signals should be handled by the tracer and then passed to the tracee.
What did you see instead?
The tracee actually process the signal SIGSEGV and panic
The text was updated successfully, but these errors were encountered: