golang / go

The Go programming language
https://go.dev
BSD 3-Clause "New" or "Revised" License
122.85k stars 17.51k forks source link

cmd/exec: Wait hangs on DragonflyBSD if child has a PTY that has been written to #56132

Open prattmic opened 1 year ago

prattmic commented 1 year ago

What version of Go are you using (go version)?

tip at 6a9aaf1f0218d61aa44c3a0ae0bce03cbac93328

Does this issue reproduce with the latest release?

Yes, 1.19.2

What operating system and processor architecture are you using (go env)?

dragonfly-amd64. Specifically, the dragonfly-amd64-622 builder/gomote.

go env Output
$ go env
GO111MODULE=""
GOARCH="amd64"
GOBIN=""
GOCACHE="/root/.cache/go-build"
GOENV="/root/.config/go/env"
GOEXE=""
GOEXPERIMENT=""
GOFLAGS=""
GOHOSTARCH="amd64"
GOHOSTOS="dragonfly"
GOINSECURE=""
GOMODCACHE="/root/go/pkg/mod"
GONOPROXY=""
GONOSUMDB=""
GOOS="dragonfly"
GOPATH="/root/go"
GOPRIVATE=""
GOPROXY="https://proxy.golang.org,direct"
GOROOT="/tmp/workdir/go"
GOSUMDB="sum.golang.org"
GOTMPDIR=""
GOTOOLDIR="/tmp/workdir/go/pkg/tool/dragonfly_amd64"
GOVCS=""
GOVERSION="devel gomote.XXXXX"
GCCGO="gccgo"
GOAMD64="v1"
AR="ar"
CC="gcc"
CXX="g++"
CGO_ENABLED="1"
GOMOD="/tmp/workdir/go/src/go.mod"
GOWORK=""
CGO_CFLAGS="-g -O2"
CGO_CPPFLAGS=""
CGO_CXXFLAGS="-g -O2"
CGO_FFLAGS="-g -O2"
CGO_LDFLAGS="-g -O2"
PKG_CONFIG="pkg-config"
GOGCCFLAGS="-fPIC -m64 -pthread -Wl,--no-gc-sections -fmessage-length=0 -fdebug-prefix-map=/tmp/go-build2229047134=/tmp/go-build -gno-record-gcc-switches"

What did you do?

Run TestDragonfly from CL 441916.

$ git fetch https://go.googlesource.com/go refs/changes/16/441916/1 && git checkout FETCH_HEAD
$ ./make.bash
$ ../bin/go test -run=TestDragonfly -v os/signal

This test:

  1. Creates psuedo-terminal parent and child FDs (with posix_openpt et al via cgo in os/signal/internal/pty).
  2. Starts a child process which inherits the child end of the PTY.
    1. Starting a new session (Setsid), process group (Setpgid), setting the controlling terminal (Setctty), and setting foreground (Foreground) don't seem to make a difference either way, so the repro skips them.
  3. The child immediately exits (reading from the PTY does not make a difference).
  4. The parent writes a byte to the parent end of the PTY (if nothing is written hang does not occur).
  5. The parent waits on the child with cmd.Wait.

What did you expect to see?

Test passes.

Note that TryBots passed on all other Unix OSes.

What did you see instead?

Test hangs, and eventually times out.

$ ../bin/go test -run=TestDragonfly -v -count=1 os/signal
=== RUN   TestDragonfly
    signal_cgo_test.go:301: Waiting for exit...
child exiting
<hung>

Specifically, the child exits (CALL exit(0) according to ktrace), but the parent wait6 call never returns. Full ktrace dump is attached here.

This feels like an OS bug, though I attempted to recreate this in C (dragonfly.c in CL 441916) and was not able to reproduce. That said, there are some differences from Go, most notably that both the parent and child are multithreaded in Go.

cc @golang/dragonfly

gopherbot commented 1 year ago

Change https://go.dev/cl/440220 mentions this issue: os/signal: rewrite TestTerminalSignal without bash