runtime/secret: aggregated heap allocations can keep secret values alive longer than expected
$ go version
go version go1.26-devel_0d0d5c9a82 Fri Dec 5 17:31:30 2025 -0800 linux/amd64
$ cat secret.go
package main
import (
"fmt"
"math"
"runtime"
"runtime/secret"
"unsafe"
)
var (
xa *int
xb *int
)
var addr uintptr
func main() {
secret.Do(func() {
a := math.MaxInt
b := math.MaxInt
xa = &a
xb = &b
addr = uintptr(unsafe.Pointer(&a))
})
xa = nil
runtime.GC()
x := *(*int)(unsafe.Pointer(addr))
fmt.Printf("xa value after gc: %x\n", x)
xb = nil
runtime.GC()
x = *(*int)(unsafe.Pointer(addr))
fmt.Printf("xa value after xb unreachable: %x\n", x)
}
$ GOEXPERIMENT=runtimesecret go run secret.go
xa value after gc: 7fffffffffffffff
xa value after xb unreachable: 0
In this case, a secret value that should have been zeroed is kept "alive" by an adjacent value whos storage is aggregated together with it by the compiler (see cmd/compile/internal/ssagen/ssa.go:/newHeapaddr/)
I'm not sure what the right answer is here. This is a contrived example, but it does break the guarantees set out by the secret package. I think we can either:
- ignore it and document that secret is best effort only and hope that users don't write code like this
- make a new allocator function that knows about the structure of these aggregated values that will de-aggregate when running in secret mode.
- disable this optimization. This would have to be globally since functions cannot know when they are running in secret mode.
Is this just #76356 as it applies to runtime/secret, or is there more here?
cc @golang/runtime
In this case, a secret value that should have been zeroed is kept "alive" by an adjacent value whos storage is aggregated together with it by the compiler (see cmd/compile/internal/ssagen/ssa.go:/newHeapaddr/)
Ah, I see, this is about aggregated heap allocations.
Though I imagine tiny allocations affect secret mode as well?
Tiny allocations are handled specially when we are in secret mode https://cs.opensource.google/go/go/+/master:src/runtime/malloc_stubs.go;l=61
Change https://go.dev/cl/728921 mentions this issue: runtime/secret: warn users about allocations, loosen gurantees.