-
Notifications
You must be signed in to change notification settings - Fork 18.7k
Description
What version of Go are you using (go version)?
go version go1.19.1 darwin/amd64
Does this issue reproduce with the latest release?
Yes.
What operating system and processor architecture are you using (go env)?
go env Output
GO111MODULE="" GOARCH="amd64" GOBIN="" GOCACHE="/Users/seh/Library/Caches/go-build" GOENV="/Users/seh/Library/Application Support/go/env" GOEXE="" GOEXPERIMENT="" GOFLAGS="" GOHOSTARCH="amd64" GOHOSTOS="darwin" GOINSECURE="" GOMODCACHE="/Users/seh/go/pkg/mod" GONOPROXY="" GONOSUMDB="" GOOS="darwin" GOPATH="/Users/seh/go" GOPRIVATE="" GOPROXY="https://proxy.golang.org,direct" GOROOT="/usr/local/Cellar/go/1.19.1/libexec" GOSUMDB="sum.golang.org" GOTMPDIR="" GOTOOLDIR="/usr/local/Cellar/go/1.19.1/libexec/pkg/tool/darwin_amd64" GOVCS="" GOVERSION="go1.19.1" GCCGO="gccgo" GOAMD64="v1" AR="ar" CC="clang" CXX="clang++" CGO_ENABLED="1" GOMOD="/Users/seh/redacted/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 -arch x86_64 -m64 -pthread -fno-caret-diagnostics -Qunused-arguments -fmessage-length=0 -fdebug-prefix-map=/var/folders/pm/0n7z4ljx1c78_d3vb379cqgc0000gn/T/go-build1922992552=/tmp/go-build -gno-record-gcc-switches -fno-common"
What did you do?
Read the documentation for the os/exec package's Cmd.ProcessState field. Note that it promises that its value will be available "after a call to Wait or Run.
Write a program that calls on the (*Cmd).Run method that invokes a nominated command that does not exist (that is, the Cmd.Path field's value does not designate a readable, executable file), then attempt to call methods on the *os.ProcessState field value that I expect to be present in the Cmd's "ProcessState" field.
What did you expect to see?
The Cmd's "ProcessField" value would be a non-nil pointer to an os.ProcessState.
What did you see instead?
My program panics dereferencing a nil pointer. The Cmd.ProcessState field remains nil after (*Cmd).Run returns.
Diagnosis
(*Cmd).Run is a macro operation that does the following:
- Call
(*Cmd).Start. - If that succeeds, call
(*Cmd).Wait.
It turns out that only after (*Cmd).Wait returns—assuming that (*Cmd).Start was called first, and that it hasn't been called already—is the Cmd.ProcessState field populated. If (*Cmd).Run's invocation of (*Cmd).Start fails, then the field will not be populated.
The documentation is simple, but misleading. It would be more accurate to say that the Cmd.ProcessState field will be populated after (*Cmd).Run starts the command.
I came upon this discrepancy while debugging a program that panicked in this situation. It took a while to figure out that the path to the command I was nominating in the Cmd.Path field was wrong, such that (*Cmd).Start—invoked via (*Cmd).Run—was failing. I figured that (*Cmd).Run had returned, even if it had returned an error, so I could count on the Cmd.ProcessState field's value being available for use.