Skip to content

Commit d183253

Browse files
committed
runtime: check summary before scavenging in fast path
In scavengeOne's fast path, we currently don't check the summary for the chunk that scavAddr points to, which means that we might accidentally scavenge unused address space if the previous scavenge moves the scavAddr into that space. The result of this today is a crash. This change makes it so that scavengeOne's fast path only happens after the check, following the comment in mpagealloc.go. It also adds a test for this case. Fixes #35465. Updates #35112. Change-Id: I861d44ee75e42a0e1f5aaec243bc449228273903 Reviewed-on: https://go-review.googlesource.com/c/go/+/206978 Run-TryBot: Michael Knyszek <[email protected]> TryBot-Result: Gobot Gobot <[email protected]> Reviewed-by: Cherry Zhang <[email protected]>
1 parent 4e8d270 commit d183253

File tree

2 files changed

+33
-7
lines changed

2 files changed

+33
-7
lines changed

src/runtime/mgcscavenge.go

+14-7
Original file line numberDiff line numberDiff line change
@@ -408,13 +408,20 @@ func (s *pageAlloc) scavengeOne(max uintptr, locked bool) uintptr {
408408
// Check the chunk containing the scav addr, starting at the addr
409409
// and see if there are any free and unscavenged pages.
410410
ci := chunkIndex(s.scavAddr)
411-
base, npages := s.chunks[ci].findScavengeCandidate(chunkPageIndex(s.scavAddr), minPages, maxPages)
412-
413-
// If we found something, scavenge it and return!
414-
if npages != 0 {
415-
s.scavengeRangeLocked(ci, base, npages)
416-
unlockHeap()
417-
return uintptr(npages) * pageSize
411+
if s.summary[len(s.summary)-1][ci].max() >= uint(minPages) {
412+
// We only bother looking for a candidate if there at least
413+
// minPages free pages at all. It's important that we only
414+
// continue if the summary says we can because that's how
415+
// we can tell if parts of the address space are unused.
416+
// See the comment on s.chunks in mpagealloc.go.
417+
base, npages := s.chunks[ci].findScavengeCandidate(chunkPageIndex(s.scavAddr), minPages, maxPages)
418+
419+
// If we found something, scavenge it and return!
420+
if npages != 0 {
421+
s.scavengeRangeLocked(ci, base, npages)
422+
unlockHeap()
423+
return uintptr(npages) * pageSize
424+
}
418425
}
419426
unlockHeap()
420427

src/runtime/mgcscavenge_test.go

+19
Original file line numberDiff line numberDiff line change
@@ -373,6 +373,25 @@ func TestPageAllocScavenge(t *testing.T) {
373373
BaseChunkIdx + 1: {{0, PallocChunkPages}},
374374
},
375375
},
376+
"ScavDiscontiguous": {
377+
beforeAlloc: map[ChunkIdx][]BitRange{
378+
BaseChunkIdx: {},
379+
BaseChunkIdx + 0xe: {},
380+
},
381+
beforeScav: map[ChunkIdx][]BitRange{
382+
BaseChunkIdx: {{uint(minPages), PallocChunkPages - uint(2*minPages)}},
383+
BaseChunkIdx + 0xe: {{uint(2 * minPages), PallocChunkPages - uint(2*minPages)}},
384+
},
385+
expect: []test{
386+
{2 * minPages * PageSize, 2 * minPages * PageSize},
387+
{^uintptr(0), 2 * minPages * PageSize},
388+
{^uintptr(0), 0},
389+
},
390+
afterScav: map[ChunkIdx][]BitRange{
391+
BaseChunkIdx: {{0, PallocChunkPages}},
392+
BaseChunkIdx + 0xe: {{0, PallocChunkPages}},
393+
},
394+
},
376395
}
377396
for name, v := range tests {
378397
v := v

0 commit comments

Comments
 (0)