Reputation: 23
I'm using Go's syscall package Ptrace interface to trace a process. The problem is, if the tracee is long-running, the tracing seems to hang. I tried replicating the issue with C implementation, but there everything seems to work fine.
Here's a Go code to reproduce the issue:
import (
"fmt"
"os"
"os/exec"
"syscall"
)
func main() {
len := "9999999"
cmd := exec.Command("openssl", "rand", "-hex", len)
cmd.SysProcAttr = &syscall.SysProcAttr{Ptrace: true}
cmd.Stdout = os.Stdout
cmd.Stdin = os.Stdin
cmd.Start()
pid, _ := syscall.Wait4(-1, nil, syscall.WALL, nil)
for {
syscall.PtraceSyscall(pid, 0)
_, err := syscall.Wait4(-1, nil, syscall.WALL, nil)
if err != nil {
fmt.Println(err)
break
}
}
}
When running the above code, the process never completes and it has to be interrupted. If the len
variable is changed to something smaller, for example 9
, the process will complete without issues and output will be something following:
$ go run main.go
d2ff963e65e8e1926b
no child processes
Upvotes: 0
Views: 171
Reputation: 23
Found it. The program hangs when Go runtime changes the thread in which the goroutine is running. Can be verified in the example code by printing fmt.Println(syscall.Gettid())
inside the loop:
package main
import (
"fmt"
"os/exec"
"syscall"
)
func main() {
len := "9999999"
cmd := exec.Command("openssl", "rand", "-hex", len)
cmd.SysProcAttr = &syscall.SysProcAttr{Ptrace: true}
cmd.Start()
pid, _ := syscall.Wait4(-1, nil, syscall.WALL, nil)
for {
fmt.Println(syscall.Gettid())
syscall.PtraceSyscall(pid, 0)
_, err := syscall.Wait4(-1, nil, syscall.WALL, nil)
if err != nil {
fmt.Println(err)
break
}
}
}
Solution: lock the execution of the goroutine to its current thread by using runtime.LockOSThread()
:
....
func main() {
runtime.LockOSThread()
len := "9999999"
cmd := exec.Command("openssl", "rand", "-hex", len)
....
Upvotes: 1