Go version
go version go1.25-20250216-RC00 cl/727547642 +d524e1eccd X:fieldtrack,boringcrypto linux/amd64
Output of go env in your module/workspace:
AR='ar'
CC='clang'
CGO_CFLAGS='-O2 -g'
CGO_CPPFLAGS=''
CGO_CXXFLAGS='-O2 -g'
CGO_ENABLED='1'
CGO_FFLAGS='-O2 -g'
CGO_LDFLAGS='-O2 -g'
CXX='clang++'
GCCGO='gccgo'
GO111MODULE=''
GOAMD64='v1'
GOARCH='amd64'
GOAUTH='netrc'
GOBIN=''
GOCACHE='/usr/local/google/home/mtp/.cache/go-build'
GOCACHEPROG=''
GODEBUG=''
GOENV='/usr/local/google/home/mtp/.config/go/env'
GOEXE=''
GOEXPERIMENT='fieldtrack,boringcrypto'
GOFIPS140='off'
GOFLAGS=''
GOGCCFLAGS='-fPIC -m64 -pthread -fno-caret-diagnostics -Qunused-arguments -Wl,--no-gc-sections -fmessage-length=0 -ffile-prefix-map=/tmp/go-build3976460735=/tmp/go-build -gno-record-gcc-switches'
GOHOSTARCH='amd64'
GOHOSTOS='linux'
GOINSECURE=''
GOMOD='/dev/null'
GOMODCACHE='/usr/local/google/home/mtp/go/pkg/mod'
GONOPROXY=''
GONOSUMDB=''
GOOS='linux'
GOPATH='/usr/local/google/home/mtp/go'
GOPRIVATE=''
GOPROXY='https://proxy.golang.org,direct'
GOROOT='/usr/lib/google-golang'
GOSUMDB='sum.golang.org'
GOTELEMETRY='local'
GOTELEMETRYDIR='/usr/local/google/home/mtp/.config/go/telemetry'
GOTMPDIR=''
GOTOOLCHAIN='auto'
GOTOOLDIR='/usr/lib/google-golang/pkg/tool/linux_amd64'
GOVCS=''
GOVERSION='go1.25-20250216-RC00 cl/727547642 +d524e1eccd X:fieldtrack,boringcrypto'
GOWORK=''
PKG_CONFIG='pkg-config'
What did you do?
I noticed that runtime.AddCleanup does not robustly handle a case like this:
type NeedsCleanup struct { ... }
func (c *NeedsCleanup) Close() { ... }
type Embeds struct { NeedsCleanup }
func New() *Embeds {
var e Embeds
runtime.AddCleanup(&e, (*NeedsCleanup).Close, &e.NeedsCleanup)
...
return &e
}
What did you see happen?
The runtime panics:
panic: runtime.AddCleanup: ptr is equal to arg, cleanup will never run
What did you expect to see?
I would expect that this snippet of code to be more robust to the case of struct embedding (if that is at all possible), or the documentation for runtime.AddFunc also mention that naive struct embedding of the data that needs to be cleaned up into an outer struct won't work due to how alignment of memory works.
We found a workaround by amending the definition of the outer type as follows:
type NeedsCleanup struct { ... }
func (c *NeedsCleanup) Close() { ... }
type Embeds struct {
_ int // Ensures that NeedsCleanup's address does not align with Embeds for finalization.
NeedsCleanup
}
func New() *Embeds {
var e Embeds
runtime.AddCleanup(&e, (*NeedsCleanup).Close, &e.NeedsCleanup)
...
return &e
}
Go version
go version go1.25-20250216-RC00 cl/727547642 +d524e1eccd X:fieldtrack,boringcrypto linux/amd64
Output of
go envin your module/workspace:What did you do?
I noticed that
runtime.AddCleanupdoes not robustly handle a case like this:What did you see happen?
The runtime panics:
What did you expect to see?
I would expect that this snippet of code to be more robust to the case of struct embedding (if that is at all possible), or the documentation for
runtime.AddFuncalso mention that naive struct embedding of the data that needs to be cleaned up into an outer struct won't work due to how alignment of memory works.We found a workaround by amending the definition of the outer type as follows: