Skip to content

Commit f511467

Browse files
committed
runtime: fix min/max logic in findScavengeCandidate
Before this CL, if max > min and max was unaligned to min, then the function could return an unaligned (unaligned to min) region to scavenge. On most platforms, this leads to some kind of crash. Fix this by explicitly aligning max to the next multiple of min. Fixes #35445. Updates #35112. Change-Id: I0af42d4a307b48a97e47ed152c619d77b0298291 Reviewed-on: https://go-review.googlesource.com/c/go/+/206277 Reviewed-by: Ian Lance Taylor <[email protected]>
1 parent 696c414 commit f511467

File tree

2 files changed

+24
-6
lines changed

2 files changed

+24
-6
lines changed

src/runtime/mgcscavenge.go

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -602,9 +602,10 @@ func (m *pallocData) hasScavengeCandidate(min uintptr) bool {
602602
// findScavengeCandidate effectively returns entire free and unscavenged regions.
603603
// If max < pallocChunkPages, it may truncate the returned region such that size is
604604
// max. However, findScavengeCandidate may still return a larger region if, for
605-
// example, it chooses to preserve huge pages. That is, even if max is small,
606-
// size is not guaranteed to be equal to max. max is allowed to be less than min,
607-
// in which case it is as if max == min.
605+
// example, it chooses to preserve huge pages, or if max is not aligned to min (it
606+
// will round up). That is, even if max is small, the returned size is not guaranteed
607+
// to be equal to max. max is allowed to be less than min, in which case it is as if
608+
// max == min.
608609
func (m *pallocData) findScavengeCandidate(searchIdx uint, min, max uintptr) (uint, uint) {
609610
if min&(min-1) != 0 || min == 0 {
610611
print("runtime: min = ", min, "\n")
@@ -613,10 +614,15 @@ func (m *pallocData) findScavengeCandidate(searchIdx uint, min, max uintptr) (ui
613614
print("runtime: min = ", min, "\n")
614615
throw("min too large")
615616
}
616-
// max is allowed to be less than min, but we need to ensure
617-
// we never truncate further than min.
618-
if max < min {
617+
// max may not be min-aligned, so we might accidentally truncate to
618+
// a max value which causes us to return a non-min-aligned value.
619+
// To prevent this, align max up to a multiple of min (which is always
620+
// a power of 2). This also prevents max from ever being less than
621+
// min, unless it's zero, so handle that explicitly.
622+
if max == 0 {
619623
max = min
624+
} else {
625+
max = alignUp(max, min)
620626
}
621627

622628
i := int(searchIdx / 64)

src/runtime/mgcscavenge_test.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,12 @@ func TestPallocDataFindScavengeCandidate(t *testing.T) {
184184
max: 3 * m,
185185
want: BitRange{128, 3 * uint(m)},
186186
}
187+
tests["Max0"+suffix] = test{
188+
scavenged: []BitRange{{0, PallocChunkPages - uint(m)}},
189+
min: m,
190+
max: 0,
191+
want: BitRange{PallocChunkPages - uint(m), uint(m)},
192+
}
187193
if m <= 8 {
188194
tests["OneFree"] = test{
189195
alloc: []BitRange{{0, 40}, {40 + uint(m), PallocChunkPages - (40 + uint(m))}},
@@ -200,6 +206,12 @@ func TestPallocDataFindScavengeCandidate(t *testing.T) {
200206
}
201207
}
202208
if m > 1 {
209+
tests["MaxUnaligned"+suffix] = test{
210+
scavenged: []BitRange{{0, PallocChunkPages - uint(m*2-1)}},
211+
min: m,
212+
max: m - 2,
213+
want: BitRange{PallocChunkPages - uint(m), uint(m)},
214+
}
203215
tests["SkipSmall"+suffix] = test{
204216
alloc: []BitRange{{0, 64 - uint(m)}, {64, 5}, {70, 11}, {82, PallocChunkPages - 82}},
205217
min: m,

0 commit comments

Comments
 (0)