ptracing long-running process hangs

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

Answers (1)

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

Related Questions