runtime: keep track of secret allocation size

During a naive attempt to test the new runtime/secret package, I tried
wrapping the entire handshake in a secret.Do call. This lead to a panic
because some of the allocator logic had been previously untested.

freeSpecial takes p and size, but they can be misleading. They don't
refer to the pointer and size of the object with the special attached,
but a pointer to the enclosing object and the size of the span element.

The previous code did not take this into account and when passing the
size to memclr would overwrite nearby objects.

Fix by storing the size of the object being cleared inside the special.

Fixes #76865.

Change-Id: Ifae31f1c8d0609a562a37f37c45aec2f369dc6a5
Reviewed-on: https://go-review.googlesource.com/c/go/+/730361
Reviewed-by: Michael Knyszek <mknyszek@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: David Chase <drchase@google.com>
This commit is contained in:
Daniel Morsing 2025-12-16 13:50:57 +00:00 committed by Michael Knyszek
parent 8564fede89
commit b8c4cc63e7
7 changed files with 147 additions and 89 deletions

View File

@ -1213,7 +1213,7 @@ func mallocgc(size uintptr, typ *_type, needzero bool) unsafe.Pointer {
if goexperiment.RuntimeSecret && gp.secret > 0 {
// Mark any object allocated while in secret mode as secret.
// This ensures we zero it immediately when freeing it.
addSecret(x)
addSecret(x, size)
}
// Notify sanitizers, if enabled.

View File

@ -156,7 +156,7 @@ func mallocgcSmallScanNoHeaderSC1(size uintptr, typ *_type, needzero bool) unsaf
gp := getg()
if goexperiment.RuntimeSecret && gp.secret > 0 {
addSecret(x)
addSecret(x, size)
}
if valgrindenabled {
@ -321,7 +321,7 @@ func mallocgcSmallScanNoHeaderSC2(size uintptr, typ *_type, needzero bool) unsaf
gp := getg()
if goexperiment.RuntimeSecret && gp.secret > 0 {
addSecret(x)
addSecret(x, size)
}
if valgrindenabled {
@ -486,7 +486,7 @@ func mallocgcSmallScanNoHeaderSC3(size uintptr, typ *_type, needzero bool) unsaf
gp := getg()
if goexperiment.RuntimeSecret && gp.secret > 0 {
addSecret(x)
addSecret(x, size)
}
if valgrindenabled {
@ -651,7 +651,7 @@ func mallocgcSmallScanNoHeaderSC4(size uintptr, typ *_type, needzero bool) unsaf
gp := getg()
if goexperiment.RuntimeSecret && gp.secret > 0 {
addSecret(x)
addSecret(x, size)
}
if valgrindenabled {
@ -816,7 +816,7 @@ func mallocgcSmallScanNoHeaderSC5(size uintptr, typ *_type, needzero bool) unsaf
gp := getg()
if goexperiment.RuntimeSecret && gp.secret > 0 {
addSecret(x)
addSecret(x, size)
}
if valgrindenabled {
@ -981,7 +981,7 @@ func mallocgcSmallScanNoHeaderSC6(size uintptr, typ *_type, needzero bool) unsaf
gp := getg()
if goexperiment.RuntimeSecret && gp.secret > 0 {
addSecret(x)
addSecret(x, size)
}
if valgrindenabled {
@ -1146,7 +1146,7 @@ func mallocgcSmallScanNoHeaderSC7(size uintptr, typ *_type, needzero bool) unsaf
gp := getg()
if goexperiment.RuntimeSecret && gp.secret > 0 {
addSecret(x)
addSecret(x, size)
}
if valgrindenabled {
@ -1311,7 +1311,7 @@ func mallocgcSmallScanNoHeaderSC8(size uintptr, typ *_type, needzero bool) unsaf
gp := getg()
if goexperiment.RuntimeSecret && gp.secret > 0 {
addSecret(x)
addSecret(x, size)
}
if valgrindenabled {
@ -1476,7 +1476,7 @@ func mallocgcSmallScanNoHeaderSC9(size uintptr, typ *_type, needzero bool) unsaf
gp := getg()
if goexperiment.RuntimeSecret && gp.secret > 0 {
addSecret(x)
addSecret(x, size)
}
if valgrindenabled {
@ -1641,7 +1641,7 @@ func mallocgcSmallScanNoHeaderSC10(size uintptr, typ *_type, needzero bool) unsa
gp := getg()
if goexperiment.RuntimeSecret && gp.secret > 0 {
addSecret(x)
addSecret(x, size)
}
if valgrindenabled {
@ -1806,7 +1806,7 @@ func mallocgcSmallScanNoHeaderSC11(size uintptr, typ *_type, needzero bool) unsa
gp := getg()
if goexperiment.RuntimeSecret && gp.secret > 0 {
addSecret(x)
addSecret(x, size)
}
if valgrindenabled {
@ -1971,7 +1971,7 @@ func mallocgcSmallScanNoHeaderSC12(size uintptr, typ *_type, needzero bool) unsa
gp := getg()
if goexperiment.RuntimeSecret && gp.secret > 0 {
addSecret(x)
addSecret(x, size)
}
if valgrindenabled {
@ -2136,7 +2136,7 @@ func mallocgcSmallScanNoHeaderSC13(size uintptr, typ *_type, needzero bool) unsa
gp := getg()
if goexperiment.RuntimeSecret && gp.secret > 0 {
addSecret(x)
addSecret(x, size)
}
if valgrindenabled {
@ -2301,7 +2301,7 @@ func mallocgcSmallScanNoHeaderSC14(size uintptr, typ *_type, needzero bool) unsa
gp := getg()
if goexperiment.RuntimeSecret && gp.secret > 0 {
addSecret(x)
addSecret(x, size)
}
if valgrindenabled {
@ -2466,7 +2466,7 @@ func mallocgcSmallScanNoHeaderSC15(size uintptr, typ *_type, needzero bool) unsa
gp := getg()
if goexperiment.RuntimeSecret && gp.secret > 0 {
addSecret(x)
addSecret(x, size)
}
if valgrindenabled {
@ -2631,7 +2631,7 @@ func mallocgcSmallScanNoHeaderSC16(size uintptr, typ *_type, needzero bool) unsa
gp := getg()
if goexperiment.RuntimeSecret && gp.secret > 0 {
addSecret(x)
addSecret(x, size)
}
if valgrindenabled {
@ -2796,7 +2796,7 @@ func mallocgcSmallScanNoHeaderSC17(size uintptr, typ *_type, needzero bool) unsa
gp := getg()
if goexperiment.RuntimeSecret && gp.secret > 0 {
addSecret(x)
addSecret(x, size)
}
if valgrindenabled {
@ -2961,7 +2961,7 @@ func mallocgcSmallScanNoHeaderSC18(size uintptr, typ *_type, needzero bool) unsa
gp := getg()
if goexperiment.RuntimeSecret && gp.secret > 0 {
addSecret(x)
addSecret(x, size)
}
if valgrindenabled {
@ -3126,7 +3126,7 @@ func mallocgcSmallScanNoHeaderSC19(size uintptr, typ *_type, needzero bool) unsa
gp := getg()
if goexperiment.RuntimeSecret && gp.secret > 0 {
addSecret(x)
addSecret(x, size)
}
if valgrindenabled {
@ -3291,7 +3291,7 @@ func mallocgcSmallScanNoHeaderSC20(size uintptr, typ *_type, needzero bool) unsa
gp := getg()
if goexperiment.RuntimeSecret && gp.secret > 0 {
addSecret(x)
addSecret(x, size)
}
if valgrindenabled {
@ -3456,7 +3456,7 @@ func mallocgcSmallScanNoHeaderSC21(size uintptr, typ *_type, needzero bool) unsa
gp := getg()
if goexperiment.RuntimeSecret && gp.secret > 0 {
addSecret(x)
addSecret(x, size)
}
if valgrindenabled {
@ -3621,7 +3621,7 @@ func mallocgcSmallScanNoHeaderSC22(size uintptr, typ *_type, needzero bool) unsa
gp := getg()
if goexperiment.RuntimeSecret && gp.secret > 0 {
addSecret(x)
addSecret(x, size)
}
if valgrindenabled {
@ -3786,7 +3786,7 @@ func mallocgcSmallScanNoHeaderSC23(size uintptr, typ *_type, needzero bool) unsa
gp := getg()
if goexperiment.RuntimeSecret && gp.secret > 0 {
addSecret(x)
addSecret(x, size)
}
if valgrindenabled {
@ -3951,7 +3951,7 @@ func mallocgcSmallScanNoHeaderSC24(size uintptr, typ *_type, needzero bool) unsa
gp := getg()
if goexperiment.RuntimeSecret && gp.secret > 0 {
addSecret(x)
addSecret(x, size)
}
if valgrindenabled {
@ -4116,7 +4116,7 @@ func mallocgcSmallScanNoHeaderSC25(size uintptr, typ *_type, needzero bool) unsa
gp := getg()
if goexperiment.RuntimeSecret && gp.secret > 0 {
addSecret(x)
addSecret(x, size)
}
if valgrindenabled {
@ -4281,7 +4281,7 @@ func mallocgcSmallScanNoHeaderSC26(size uintptr, typ *_type, needzero bool) unsa
gp := getg()
if goexperiment.RuntimeSecret && gp.secret > 0 {
addSecret(x)
addSecret(x, size)
}
if valgrindenabled {
@ -6686,7 +6686,7 @@ func mallocgcSmallNoScanSC2(size uintptr, typ *_type, needzero bool) unsafe.Poin
gp := getg()
if goexperiment.RuntimeSecret && gp.secret > 0 {
addSecret(x)
addSecret(x, size)
}
if valgrindenabled {
@ -6757,7 +6757,7 @@ func mallocgcSmallNoScanSC2(size uintptr, typ *_type, needzero bool) unsafe.Poin
gp := getg()
if goexperiment.RuntimeSecret && gp.secret > 0 {
addSecret(x)
addSecret(x, size)
}
if valgrindenabled {
@ -6822,7 +6822,7 @@ func mallocgcSmallNoScanSC3(size uintptr, typ *_type, needzero bool) unsafe.Poin
gp := getg()
if goexperiment.RuntimeSecret && gp.secret > 0 {
addSecret(x)
addSecret(x, size)
}
if valgrindenabled {
@ -6893,7 +6893,7 @@ func mallocgcSmallNoScanSC3(size uintptr, typ *_type, needzero bool) unsafe.Poin
gp := getg()
if goexperiment.RuntimeSecret && gp.secret > 0 {
addSecret(x)
addSecret(x, size)
}
if valgrindenabled {
@ -6958,7 +6958,7 @@ func mallocgcSmallNoScanSC4(size uintptr, typ *_type, needzero bool) unsafe.Poin
gp := getg()
if goexperiment.RuntimeSecret && gp.secret > 0 {
addSecret(x)
addSecret(x, size)
}
if valgrindenabled {
@ -7029,7 +7029,7 @@ func mallocgcSmallNoScanSC4(size uintptr, typ *_type, needzero bool) unsafe.Poin
gp := getg()
if goexperiment.RuntimeSecret && gp.secret > 0 {
addSecret(x)
addSecret(x, size)
}
if valgrindenabled {
@ -7094,7 +7094,7 @@ func mallocgcSmallNoScanSC5(size uintptr, typ *_type, needzero bool) unsafe.Poin
gp := getg()
if goexperiment.RuntimeSecret && gp.secret > 0 {
addSecret(x)
addSecret(x, size)
}
if valgrindenabled {
@ -7165,7 +7165,7 @@ func mallocgcSmallNoScanSC5(size uintptr, typ *_type, needzero bool) unsafe.Poin
gp := getg()
if goexperiment.RuntimeSecret && gp.secret > 0 {
addSecret(x)
addSecret(x, size)
}
if valgrindenabled {
@ -7230,7 +7230,7 @@ func mallocgcSmallNoScanSC6(size uintptr, typ *_type, needzero bool) unsafe.Poin
gp := getg()
if goexperiment.RuntimeSecret && gp.secret > 0 {
addSecret(x)
addSecret(x, size)
}
if valgrindenabled {
@ -7301,7 +7301,7 @@ func mallocgcSmallNoScanSC6(size uintptr, typ *_type, needzero bool) unsafe.Poin
gp := getg()
if goexperiment.RuntimeSecret && gp.secret > 0 {
addSecret(x)
addSecret(x, size)
}
if valgrindenabled {
@ -7366,7 +7366,7 @@ func mallocgcSmallNoScanSC7(size uintptr, typ *_type, needzero bool) unsafe.Poin
gp := getg()
if goexperiment.RuntimeSecret && gp.secret > 0 {
addSecret(x)
addSecret(x, size)
}
if valgrindenabled {
@ -7437,7 +7437,7 @@ func mallocgcSmallNoScanSC7(size uintptr, typ *_type, needzero bool) unsafe.Poin
gp := getg()
if goexperiment.RuntimeSecret && gp.secret > 0 {
addSecret(x)
addSecret(x, size)
}
if valgrindenabled {
@ -7502,7 +7502,7 @@ func mallocgcSmallNoScanSC8(size uintptr, typ *_type, needzero bool) unsafe.Poin
gp := getg()
if goexperiment.RuntimeSecret && gp.secret > 0 {
addSecret(x)
addSecret(x, size)
}
if valgrindenabled {
@ -7573,7 +7573,7 @@ func mallocgcSmallNoScanSC8(size uintptr, typ *_type, needzero bool) unsafe.Poin
gp := getg()
if goexperiment.RuntimeSecret && gp.secret > 0 {
addSecret(x)
addSecret(x, size)
}
if valgrindenabled {
@ -7638,7 +7638,7 @@ func mallocgcSmallNoScanSC9(size uintptr, typ *_type, needzero bool) unsafe.Poin
gp := getg()
if goexperiment.RuntimeSecret && gp.secret > 0 {
addSecret(x)
addSecret(x, size)
}
if valgrindenabled {
@ -7709,7 +7709,7 @@ func mallocgcSmallNoScanSC9(size uintptr, typ *_type, needzero bool) unsafe.Poin
gp := getg()
if goexperiment.RuntimeSecret && gp.secret > 0 {
addSecret(x)
addSecret(x, size)
}
if valgrindenabled {
@ -7774,7 +7774,7 @@ func mallocgcSmallNoScanSC10(size uintptr, typ *_type, needzero bool) unsafe.Poi
gp := getg()
if goexperiment.RuntimeSecret && gp.secret > 0 {
addSecret(x)
addSecret(x, size)
}
if valgrindenabled {
@ -7845,7 +7845,7 @@ func mallocgcSmallNoScanSC10(size uintptr, typ *_type, needzero bool) unsafe.Poi
gp := getg()
if goexperiment.RuntimeSecret && gp.secret > 0 {
addSecret(x)
addSecret(x, size)
}
if valgrindenabled {
@ -7910,7 +7910,7 @@ func mallocgcSmallNoScanSC11(size uintptr, typ *_type, needzero bool) unsafe.Poi
gp := getg()
if goexperiment.RuntimeSecret && gp.secret > 0 {
addSecret(x)
addSecret(x, size)
}
if valgrindenabled {
@ -7981,7 +7981,7 @@ func mallocgcSmallNoScanSC11(size uintptr, typ *_type, needzero bool) unsafe.Poi
gp := getg()
if goexperiment.RuntimeSecret && gp.secret > 0 {
addSecret(x)
addSecret(x, size)
}
if valgrindenabled {
@ -8046,7 +8046,7 @@ func mallocgcSmallNoScanSC12(size uintptr, typ *_type, needzero bool) unsafe.Poi
gp := getg()
if goexperiment.RuntimeSecret && gp.secret > 0 {
addSecret(x)
addSecret(x, size)
}
if valgrindenabled {
@ -8117,7 +8117,7 @@ func mallocgcSmallNoScanSC12(size uintptr, typ *_type, needzero bool) unsafe.Poi
gp := getg()
if goexperiment.RuntimeSecret && gp.secret > 0 {
addSecret(x)
addSecret(x, size)
}
if valgrindenabled {
@ -8182,7 +8182,7 @@ func mallocgcSmallNoScanSC13(size uintptr, typ *_type, needzero bool) unsafe.Poi
gp := getg()
if goexperiment.RuntimeSecret && gp.secret > 0 {
addSecret(x)
addSecret(x, size)
}
if valgrindenabled {
@ -8253,7 +8253,7 @@ func mallocgcSmallNoScanSC13(size uintptr, typ *_type, needzero bool) unsafe.Poi
gp := getg()
if goexperiment.RuntimeSecret && gp.secret > 0 {
addSecret(x)
addSecret(x, size)
}
if valgrindenabled {
@ -8318,7 +8318,7 @@ func mallocgcSmallNoScanSC14(size uintptr, typ *_type, needzero bool) unsafe.Poi
gp := getg()
if goexperiment.RuntimeSecret && gp.secret > 0 {
addSecret(x)
addSecret(x, size)
}
if valgrindenabled {
@ -8389,7 +8389,7 @@ func mallocgcSmallNoScanSC14(size uintptr, typ *_type, needzero bool) unsafe.Poi
gp := getg()
if goexperiment.RuntimeSecret && gp.secret > 0 {
addSecret(x)
addSecret(x, size)
}
if valgrindenabled {
@ -8454,7 +8454,7 @@ func mallocgcSmallNoScanSC15(size uintptr, typ *_type, needzero bool) unsafe.Poi
gp := getg()
if goexperiment.RuntimeSecret && gp.secret > 0 {
addSecret(x)
addSecret(x, size)
}
if valgrindenabled {
@ -8525,7 +8525,7 @@ func mallocgcSmallNoScanSC15(size uintptr, typ *_type, needzero bool) unsafe.Poi
gp := getg()
if goexperiment.RuntimeSecret && gp.secret > 0 {
addSecret(x)
addSecret(x, size)
}
if valgrindenabled {
@ -8590,7 +8590,7 @@ func mallocgcSmallNoScanSC16(size uintptr, typ *_type, needzero bool) unsafe.Poi
gp := getg()
if goexperiment.RuntimeSecret && gp.secret > 0 {
addSecret(x)
addSecret(x, size)
}
if valgrindenabled {
@ -8661,7 +8661,7 @@ func mallocgcSmallNoScanSC16(size uintptr, typ *_type, needzero bool) unsafe.Poi
gp := getg()
if goexperiment.RuntimeSecret && gp.secret > 0 {
addSecret(x)
addSecret(x, size)
}
if valgrindenabled {
@ -8726,7 +8726,7 @@ func mallocgcSmallNoScanSC17(size uintptr, typ *_type, needzero bool) unsafe.Poi
gp := getg()
if goexperiment.RuntimeSecret && gp.secret > 0 {
addSecret(x)
addSecret(x, size)
}
if valgrindenabled {
@ -8797,7 +8797,7 @@ func mallocgcSmallNoScanSC17(size uintptr, typ *_type, needzero bool) unsafe.Poi
gp := getg()
if goexperiment.RuntimeSecret && gp.secret > 0 {
addSecret(x)
addSecret(x, size)
}
if valgrindenabled {
@ -8862,7 +8862,7 @@ func mallocgcSmallNoScanSC18(size uintptr, typ *_type, needzero bool) unsafe.Poi
gp := getg()
if goexperiment.RuntimeSecret && gp.secret > 0 {
addSecret(x)
addSecret(x, size)
}
if valgrindenabled {
@ -8933,7 +8933,7 @@ func mallocgcSmallNoScanSC18(size uintptr, typ *_type, needzero bool) unsafe.Poi
gp := getg()
if goexperiment.RuntimeSecret && gp.secret > 0 {
addSecret(x)
addSecret(x, size)
}
if valgrindenabled {
@ -8998,7 +8998,7 @@ func mallocgcSmallNoScanSC19(size uintptr, typ *_type, needzero bool) unsafe.Poi
gp := getg()
if goexperiment.RuntimeSecret && gp.secret > 0 {
addSecret(x)
addSecret(x, size)
}
if valgrindenabled {
@ -9069,7 +9069,7 @@ func mallocgcSmallNoScanSC19(size uintptr, typ *_type, needzero bool) unsafe.Poi
gp := getg()
if goexperiment.RuntimeSecret && gp.secret > 0 {
addSecret(x)
addSecret(x, size)
}
if valgrindenabled {
@ -9134,7 +9134,7 @@ func mallocgcSmallNoScanSC20(size uintptr, typ *_type, needzero bool) unsafe.Poi
gp := getg()
if goexperiment.RuntimeSecret && gp.secret > 0 {
addSecret(x)
addSecret(x, size)
}
if valgrindenabled {
@ -9205,7 +9205,7 @@ func mallocgcSmallNoScanSC20(size uintptr, typ *_type, needzero bool) unsafe.Poi
gp := getg()
if goexperiment.RuntimeSecret && gp.secret > 0 {
addSecret(x)
addSecret(x, size)
}
if valgrindenabled {
@ -9270,7 +9270,7 @@ func mallocgcSmallNoScanSC21(size uintptr, typ *_type, needzero bool) unsafe.Poi
gp := getg()
if goexperiment.RuntimeSecret && gp.secret > 0 {
addSecret(x)
addSecret(x, size)
}
if valgrindenabled {
@ -9341,7 +9341,7 @@ func mallocgcSmallNoScanSC21(size uintptr, typ *_type, needzero bool) unsafe.Poi
gp := getg()
if goexperiment.RuntimeSecret && gp.secret > 0 {
addSecret(x)
addSecret(x, size)
}
if valgrindenabled {
@ -9406,7 +9406,7 @@ func mallocgcSmallNoScanSC22(size uintptr, typ *_type, needzero bool) unsafe.Poi
gp := getg()
if goexperiment.RuntimeSecret && gp.secret > 0 {
addSecret(x)
addSecret(x, size)
}
if valgrindenabled {
@ -9477,7 +9477,7 @@ func mallocgcSmallNoScanSC22(size uintptr, typ *_type, needzero bool) unsafe.Poi
gp := getg()
if goexperiment.RuntimeSecret && gp.secret > 0 {
addSecret(x)
addSecret(x, size)
}
if valgrindenabled {
@ -9542,7 +9542,7 @@ func mallocgcSmallNoScanSC23(size uintptr, typ *_type, needzero bool) unsafe.Poi
gp := getg()
if goexperiment.RuntimeSecret && gp.secret > 0 {
addSecret(x)
addSecret(x, size)
}
if valgrindenabled {
@ -9613,7 +9613,7 @@ func mallocgcSmallNoScanSC23(size uintptr, typ *_type, needzero bool) unsafe.Poi
gp := getg()
if goexperiment.RuntimeSecret && gp.secret > 0 {
addSecret(x)
addSecret(x, size)
}
if valgrindenabled {
@ -9678,7 +9678,7 @@ func mallocgcSmallNoScanSC24(size uintptr, typ *_type, needzero bool) unsafe.Poi
gp := getg()
if goexperiment.RuntimeSecret && gp.secret > 0 {
addSecret(x)
addSecret(x, size)
}
if valgrindenabled {
@ -9749,7 +9749,7 @@ func mallocgcSmallNoScanSC24(size uintptr, typ *_type, needzero bool) unsafe.Poi
gp := getg()
if goexperiment.RuntimeSecret && gp.secret > 0 {
addSecret(x)
addSecret(x, size)
}
if valgrindenabled {
@ -9814,7 +9814,7 @@ func mallocgcSmallNoScanSC25(size uintptr, typ *_type, needzero bool) unsafe.Poi
gp := getg()
if goexperiment.RuntimeSecret && gp.secret > 0 {
addSecret(x)
addSecret(x, size)
}
if valgrindenabled {
@ -9885,7 +9885,7 @@ func mallocgcSmallNoScanSC25(size uintptr, typ *_type, needzero bool) unsafe.Poi
gp := getg()
if goexperiment.RuntimeSecret && gp.secret > 0 {
addSecret(x)
addSecret(x, size)
}
if valgrindenabled {
@ -9950,7 +9950,7 @@ func mallocgcSmallNoScanSC26(size uintptr, typ *_type, needzero bool) unsafe.Poi
gp := getg()
if goexperiment.RuntimeSecret && gp.secret > 0 {
addSecret(x)
addSecret(x, size)
}
if valgrindenabled {
@ -10021,7 +10021,7 @@ func mallocgcSmallNoScanSC26(size uintptr, typ *_type, needzero bool) unsafe.Poi
gp := getg()
if goexperiment.RuntimeSecret && gp.secret > 0 {
addSecret(x)
addSecret(x, size)
}
if valgrindenabled {

View File

@ -101,7 +101,7 @@ func mallocStub(size uintptr, typ *_type, needzero bool) unsafe.Pointer {
if goexperiment.RuntimeSecret && gp.secret > 0 {
// Mark any object allocated while in secret mode as secret.
// This ensures we zero it immediately when freeing it.
addSecret(x)
addSecret(x, size)
}
}

View File

@ -2745,6 +2745,14 @@ type specialPinCounter struct {
counter uintptr
}
// specialSecret tracks whether we need to zero an object immediately
// upon freeing.
type specialSecret struct {
_ sys.NotInHeap
special special
size uintptr
}
// specialsIter helps iterate over specials lists.
type specialsIter struct {
pprev **special
@ -2775,6 +2783,12 @@ func (i *specialsIter) unlinkAndNext() *special {
// freeSpecial performs any cleanup on special s and deallocates it.
// s must already be unlinked from the specials list.
// TODO(mknyszek): p and size together DO NOT represent a valid allocation.
// size is the size of the allocation block in the span (mspan.elemsize), and p is
// whatever pointer the special was attached to, which need not point to the
// beginning of the block, though it may.
// Consider passing the arguments differently to avoid giving the impression
// that p and size together represent an address range.
func freeSpecial(s *special, p unsafe.Pointer, size uintptr) {
switch s.kind {
case _KindSpecialFinalizer:
@ -2828,7 +2842,19 @@ func freeSpecial(s *special, p unsafe.Pointer, size uintptr) {
mheap_.specialBubbleAlloc.free(unsafe.Pointer(st))
unlock(&mheap_.speciallock)
case _KindSpecialSecret:
memclrNoHeapPointers(p, size)
ss := (*specialSecret)(unsafe.Pointer(s))
// p is the actual byte location that the special was
// attached to, but the size argument is the span
// element size. If we were to zero out using the size
// argument, we'd trounce over adjacent memory in cases
// where the allocation contains a header. Hence, we use
// the user-visible size which we stash in the special itself.
//
// p always points to the beginning of the user-visible
// allocation since the only way to attach a secret special
// is via the allocation path. This isn't universal for
// tiny allocs, but we avoid them in mallocgc anyway.
memclrNoHeapPointers(p, ss.size)
lock(&mheap_.speciallock)
mheap_.specialSecretAlloc.free(unsafe.Pointer(s))
unlock(&mheap_.speciallock)

View File

@ -55,15 +55,9 @@ func secret_eraseSecrets() {
// Don't put any code here: the stack frame's contents are gone!
}
// specialSecret tracks whether we need to zero an object immediately
// upon freeing.
type specialSecret struct {
special special
}
// addSecret records the fact that we need to zero p immediately
// when it is freed.
func addSecret(p unsafe.Pointer) {
func addSecret(p unsafe.Pointer, size uintptr) {
// TODO(dmo): figure out the cost of these. These are mostly
// intended to catch allocations that happen via the runtime
// that the user has no control over and not big buffers that user
@ -72,6 +66,7 @@ func addSecret(p unsafe.Pointer) {
lock(&mheap_.speciallock)
s := (*specialSecret)(mheap_.specialSecretAlloc.alloc())
s.special.kind = _KindSpecialSecret
s.size = size
unlock(&mheap_.speciallock)
addspecial(p, &s.special, false)
}

View File

@ -0,0 +1,39 @@
// Copyright 2025 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build goexperiment.runtimesecret && (arm64 || amd64) && linux
package secret_test
import (
"runtime"
"runtime/secret"
"testing"
)
func TestInterleavedAllocFrees(t *testing.T) {
// Interleave heap objects that are kept alive beyond secret.Do
// with heap objects that do not live past secret.Do.
// The intent is for the clearing of one object (with the wrong size)
// to clobber the type header of the next slot. If the GC sees a nil type header
// when it expects to find one, it can throw.
type T struct {
p *int
x [1024]byte
}
for range 10 {
var s []*T
secret.Do(func() {
for i := range 100 {
t := &T{}
if i%2 == 0 {
s = append(s, t)
}
}
})
runtime.GC()
runtime.GC()
runtime.KeepAlive(s)
}
}

View File

@ -22,9 +22,7 @@ func secret_dec() {}
//go:linkname secret_eraseSecrets runtime/secret.eraseSecrets
func secret_eraseSecrets() {}
func addSecret(p unsafe.Pointer) {}
type specialSecret struct{}
func addSecret(p unsafe.Pointer, size uintptr) {}
//go:linkname secret_getStack runtime/secret.getStack
func secret_getStack() (uintptr, uintptr) { return 0, 0 }