runtime: fix CheckScavengedBitsCleared with randomized heap base

We cannot easily determine the base address of the arena we added the
random padding pages to, which can cause confusing
TestScavengedBitsCleared failures when the arena we padded does not have
the lowest address of all of the arenas.

Instead of attempting to determine the correct arena to ignore when
checking the scav and alloc bits, switch to just tolerating _one_ arena
having mismatches, which is expected when randomizedHeapBase64 is
enabled. Any other number of arenas having mismatches is likely a real
error.

Fixes #75502

Change-Id: Iacc445b2905824f9f71970c7abd33f187793cf39
Reviewed-on: https://go-review.googlesource.com/c/go/+/704855
Reviewed-by: Michael Knyszek <mknyszek@google.com>
Auto-Submit: Roland Shoemaker <roland@golang.org>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
This commit is contained in:
Roland Shoemaker 2025-09-17 21:04:56 +00:00 committed by Gopher Robot
parent 909704b85e
commit 465b85eb76

View file

@ -1122,8 +1122,6 @@ func CheckScavengedBitsCleared(mismatches []BitsMismatch) (n int, ok bool) {
// Lock so that we can safely access the bitmap.
lock(&mheap_.lock)
heapBase := mheap_.pages.inUse.ranges[0].base.addr()
secondArenaBase := arenaBase(arenaIndex(heapBase) + 1)
chunkLoop:
for i := mheap_.pages.start; i < mheap_.pages.end; i++ {
chunk := mheap_.pages.tryChunkOf(i)
@ -1140,14 +1138,6 @@ func CheckScavengedBitsCleared(mismatches []BitsMismatch) (n int, ok bool) {
want := chunk.scavenged[j] &^ chunk.pallocBits[j]
got := chunk.scavenged[j]
if want != got {
// When goexperiment.RandomizedHeapBase64 is set we use a
// series of padding pages to generate randomized heap base
// address which have both the alloc and scav bits set. If
// we see this for a chunk between the address of the heap
// base, and the address of the second arena continue.
if goexperiment.RandomizedHeapBase64 && (cb >= heapBase && cb < secondArenaBase) {
continue
}
ok = false
if n >= len(mismatches) {
break chunkLoop
@ -1165,6 +1155,37 @@ func CheckScavengedBitsCleared(mismatches []BitsMismatch) (n int, ok bool) {
getg().m.mallocing--
})
if goexperiment.RandomizedHeapBase64 && len(mismatches) > 0 {
// When goexperiment.RandomizedHeapBase64 is set we use a series of
// padding pages to generate randomized heap base address which have
// both the alloc and scav bits set. Because of this we expect exactly
// one arena will have mismatches, so check for that explicitly and
// remove the mismatches if that property holds. If we see more than one
// arena with this property, that is an indication something has
// actually gone wrong, so return the mismatches.
//
// We do this, instead of ignoring the mismatches in the chunkLoop, because
// it's not easy to determine which arena we added the padding pages to
// programmatically, without explicitly recording the base address somewhere
// in a global variable (which we'd rather not do as the address of that variable
// is likely to be somewhat predictable, potentially defeating the purpose
// of our randomization).
affectedArenas := map[arenaIdx]bool{}
for _, mismatch := range mismatches {
if mismatch.Base > 0 {
affectedArenas[arenaIndex(mismatch.Base)] = true
}
}
if len(affectedArenas) == 1 {
ok = true
// zero the mismatches
for i := range n {
mismatches[i] = BitsMismatch{}
}
}
}
return
}