runtime: AddCleanup is not robust to arg values pointing to struct embedded data
- Dominant language
- Go
- Stars
- 139k
- Forks
- 19.4k
- PR merge metrics
- PR metrics pending
Description
### Go version
go version go1.25-20250216-RC00 cl/727547642 +d524e1eccd X:fieldtrack,boringcrypto linux/amd64
### Output of `go env` in your module/workspace:
```shell
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`](https://pkg.go.dev/runtime#AddCleanup) does not robustly handle a case like this:
```go
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](https://github.com/golang/go/blob/01ba8bfe868df2eea10ea8dd5bfbe5af0549909d/src/runtime/mcleanup.go#L79-L84) 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](https://dave.cheney.net/tag/alignment).
We found a workaround by amending the definition of the outer type as follows:
```go
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
}
```
Contributor guide
Assessment
This issue has not been assessed yet.