Skip to content

os/exec: CombinedOutput hangs when exec'd task forks a "daemon" #13155

@tych0

Description

@tych0

Consider https://gist.github.com/felixge/452a5a192e217a2a51c0

I get a similar stack trace:

goroutine 1 [chan receive]:
os/exec.(*Cmd).Wait(0xc82008a000, 0x0, 0x0)
    /usr/lib/go/src/os/exec/exec.go:385 +0x2c9
os/exec.(*Cmd).Run(0xc82008a000, 0x0, 0x0)
    /usr/lib/go/src/os/exec/exec.go:258 +0x64
os/exec.(*Cmd).CombinedOutput(0xc82008a000, 0x0, 0x0, 0x0, 0x0, 0x0)
    /usr/lib/go/src/os/exec/exec.go:424 +0x310
main.main()
    /home/tycho/bug.go:22 +0x130

goroutine 6 [syscall]:
syscall.Syscall(0x0, 0x4, 0xc820090000, 0x200, 0x0, 0x0, 0x40eea9)
    /usr/lib/go/src/syscall/asm_linux_amd64.s:18 +0x5
syscall.read(0x4, 0xc820090000, 0x200, 0x200, 0x40f078, 0x0, 0x0)
    /usr/lib/go/src/syscall/zsyscall_linux_amd64.go:783 +0x5f
syscall.Read(0x4, 0xc820090000, 0x200, 0x200, 0xc82002e4e8, 0x0, 0x0)
    /usr/lib/go/src/syscall/syscall_unix.go:160 +0x4d
os.(*File).read(0xc820030038, 0xc820090000, 0x200, 0x200, 0xc820090000, 0x0, 0x0)
    /usr/lib/go/src/os/file_unix.go:211 +0x53
os.(*File).Read(0xc820030038, 0xc820090000, 0x200, 0x200, 0x0, 0x0, 0x0)
    /usr/lib/go/src/os/file.go:95 +0x8a
bytes.(*Buffer).ReadFrom(0xc82001a150, 0x7fa3eff92210, 0xc820030038, 0x0, 0x0, 0x0)
    /usr/lib/go/src/bytes/buffer.go:173 +0x23f
io.copyBuffer(0x7fa3eff921c0, 0xc82001a150, 0x7fa3eff92210, 0xc820030038, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0)
    /usr/lib/go/src/io/io.go:375 +0x180
io.Copy(0x7fa3eff921c0, 0xc82001a150, 0x7fa3eff92210, 0xc820030038, 0x0, 0x0, 0x0)
    /usr/lib/go/src/io/io.go:351 +0x64
os/exec.(*Cmd).writerDescriptor.func1(0x0, 0x0)
    /usr/lib/go/src/os/exec/exec.go:232 +0x8b
os/exec.(*Cmd).Start.func1(0xc82008a000, 0xc82000e300)
    /usr/lib/go/src/os/exec/exec.go:340 +0x1d
created by os/exec.(*Cmd).Start
    /usr/lib/go/src/os/exec/exec.go:341 +0x96d

which for me is roughly:

c.errch <- fn()

Based on a cursory glance through the code, it seems like what is happening is that the std{in,out,err} reading/writing goroutines are blocking on reading the stdin of the process, which isn't closed when the child exits, because the grandchild shares the same fds (and doesn't close them). Wait() attempts to address this with a call to closeDescriptors, but it is after a blocking wait on the goroutines exiting, so it doesn't work either.

The behavior I'd expect is that CombinedOutput() reports exactly what the child task printed, and returns when it exits. At least in the example above, I think it can be resolved by closing the stdin fd for the child once the child exits (but before waiting on the goroutines). However, I haven't tried it and I can imagine other cases where it would still hang.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions