-
-
Notifications
You must be signed in to change notification settings - Fork 399
Description
- staticcheck -version:
staticcheck 2025.1.1 (0.6.1) - staticcheck -debug.version:
staticcheck 2025.1.1 (0.6.1)
Compiled with Go version: go1.25.2
Main module:
honnef.co/go/[email protected] (sum: h1:R094WgE8K4JirYjBaOpz/AvTyUu/3wbmAoskKN/pxTI=)
Dependencies:
github.com/BurntSushi/[email protected] (sum: h1:pxW6RcqyfI9/kWtOwnv/G+AzdKuy2ZrqINhenH4HyNs=)
golang.org/x/exp/[email protected] (sum: h1:1P7xPZEwZMoBoz0Yze5Nx2/4pxj6nw9ZqHWXqP0iRgQ=)
golang.org/x/[email protected] (sum: h1:Zb7khfcRGKk+kqfxFaP5tZqCnDZMjC5VtUBs87Hr6QM=)
golang.org/x/[email protected] (sum: h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w=)
golang.org/x/[email protected] (sum: h1:BgcpHewrV5AUp2G9MebG4XPFI1E2W41zU1SaqVA9vJY=)
- go version:
go version go1.25.2 linux/amd64 - go env:
AR='ar'
CC='gcc'
CGO_CFLAGS='-O2 -g'
CGO_CPPFLAGS=''
CGO_CXXFLAGS='-O2 -g'
CGO_ENABLED='1'
CGO_FFLAGS='-O2 -g'
CGO_LDFLAGS='-O2 -g'
CXX='g++'
GCCGO='gccgo'
GO111MODULE=''
GOAMD64='v1'
GOARCH='amd64'
GOAUTH='netrc'
GOBIN=''
GOCACHE='/home/user/.cache/go-build'
GOCACHEPROG=''
GODEBUG=''
GOENV='/home/user/.config/go/env'
GOEXE=''
GOEXPERIMENT=''
GOFIPS140='off'
GOFLAGS=''
GOGCCFLAGS='-fPIC -m64 -pthread -Wl,--no-gc-sections -fmessage-length=0 -ffile-prefix-map=/tmp/go-build3826221701=/tmp/go-build -gno-record-gcc-switches'
GOHOSTARCH='amd64'
GOHOSTOS='linux'
GOINSECURE=''
GOMOD='/home/user/nautilus/go.mod'
GOMODCACHE='/home/user/pkg/mod'
GONOPROXY=''
GONOSUMDB=''
GOOS='linux'
GOPATH='/home/user'
GOPRIVATE=''
GOPROXY='https://proxy.golang.org,direct'
GOROOT='/nix/store/k1kn1c59ss7nq6agdppzq3krwmi3xqy4-go-1.25.2/share/go'
GOSUMDB='sum.golang.org'
GOTELEMETRY='local'
GOTELEMETRYDIR='/home/user/.config/go/telemetry'
GOTMPDIR=''
GOTOOLCHAIN='auto'
GOTOOLDIR='/nix/store/k1kn1c59ss7nq6agdppzq3krwmi3xqy4-go-1.25.2/share/go/pkg/tool/linux_amd64'
GOVCS=''
GOVERSION='go1.25.2'
GOWORK=''
PKG_CONFIG='pkg-config'
- Command ran:
staticcheck ./...(run inside a module containing the reproducer below)
Code that triggers the bug
Create a module containing this single file (or run go run on it directly):
package main
import "errors"
var errStop = errors.New("stop")
func acquire(flag bool) (func(), error) {
if flag {
return nil, errStop
}
return func() {}, nil
}
func use(flag bool) error {
release, err := acquire(flag)
defer release()
if err != nil {
return err
}
return nil
}
func main() {
_ = use(true)
}Observed behavior
Running this program consistently crashes with panic: runtime error: invalid memory address or nil pointer dereference. When flag is true, acquire reports an error and returns a nil cleanup function. use unconditionally defers release() before inspecting err, so the defer executes with release == nil and the process dies. This pattern occurred in a long-lived production service: a shutdown path hit the nil defer only after years in prod, immediately taking down the whole server.
Expected behavior
Staticcheck (SA5001 or a new check) should warn whenever a func() return value is deferred before verifying that the accompanying error is nil. The tool already flags IO closers that are deferred prior to error handling, but it stays silent for cleanup functions stored in variables. Given that nil func() values panic when called, emitting a diagnostic here would catch a class of severe crashes that are otherwise very hard to spot in code review.