1
0
mirror of https://github.com/golang/go synced 2024-11-05 15:26:15 -07:00

runtime: ensure allocToCache updates searchAddr in a valid way

Currently allocToCache assumes it can move the search address past the
block it allocated the cache from, which violates the property that
searchAddr should always point to mapped memory (i.e. memory represented
by pageAlloc.inUse).

This bug was already fixed once for pageAlloc.alloc in the Go 1.14
release via CL 216697, but that changed failed to take into account
allocToCache.

Fixes #38605.

Change-Id: Id08180aa10d19dc0f9f551a1d9e327a295560dff
Reviewed-on: https://go-review.googlesource.com/c/go/+/229577
Run-TryBot: Michael Knyszek <mknyszek@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: David Chase <drchase@google.com>
This commit is contained in:
Michael Anthony Knyszek 2020-04-22 21:36:11 +00:00 committed by Michael Knyszek
parent 9a3f22be7a
commit 287d1ec96c
2 changed files with 40 additions and 6 deletions

View File

@ -148,9 +148,14 @@ func (s *pageAlloc) allocToCache() pageCache {
// Update as an allocation, but note that it's not contiguous. // Update as an allocation, but note that it's not contiguous.
s.update(c.base, pageCachePages, false, true) s.update(c.base, pageCachePages, false, true)
// We're always searching for the first free page, and we always know the // Set the search address to the last page represented by the cache.
// up to pageCache size bits will be allocated, so we can always move the // Since all of the pages in this block are going to the cache, and we
// searchAddr past the cache. // searched for the first free page, we can confidently start at the
s.searchAddr = c.base + pageSize*pageCachePages // next page.
//
// However, s.searchAddr is not allowed to point into unmapped heap memory
// unless it is maxSearchAddr, so make it the last page as opposed to
// the page after.
s.searchAddr = c.base + pageSize*(pageCachePages-1)
return c return c
} }

View File

@ -260,12 +260,13 @@ func TestPageAllocAllocToCache(t *testing.T) {
if GOOS == "openbsd" && testing.Short() { if GOOS == "openbsd" && testing.Short() {
t.Skip("skipping because virtual memory is limited; see #36210") t.Skip("skipping because virtual memory is limited; see #36210")
} }
tests := map[string]struct { type test struct {
before map[ChunkIdx][]BitRange before map[ChunkIdx][]BitRange
scav map[ChunkIdx][]BitRange scav map[ChunkIdx][]BitRange
hits []PageCache // expected base addresses and patterns hits []PageCache // expected base addresses and patterns
after map[ChunkIdx][]BitRange after map[ChunkIdx][]BitRange
}{ }
tests := map[string]test{
"AllFree": { "AllFree": {
before: map[ChunkIdx][]BitRange{ before: map[ChunkIdx][]BitRange{
BaseChunkIdx: {}, BaseChunkIdx: {},
@ -349,6 +350,34 @@ func TestPageAllocAllocToCache(t *testing.T) {
}, },
}, },
} }
if PageAlloc64Bit != 0 {
const chunkIdxBigJump = 0x100000 // chunk index offset which translates to O(TiB)
// This test is similar to the one with the same name for
// pageAlloc.alloc and serves the same purpose.
// See mpagealloc_test.go for details.
sumsPerPhysPage := ChunkIdx(PhysPageSize / PallocSumBytes)
baseChunkIdx := BaseChunkIdx &^ (sumsPerPhysPage - 1)
tests["DiscontiguousMappedSumBoundary"] = test{
before: map[ChunkIdx][]BitRange{
baseChunkIdx + sumsPerPhysPage - 1: {{0, PallocChunkPages - 1}},
baseChunkIdx + chunkIdxBigJump: {{1, PallocChunkPages - 1}},
},
scav: map[ChunkIdx][]BitRange{
baseChunkIdx + sumsPerPhysPage - 1: {},
baseChunkIdx + chunkIdxBigJump: {},
},
hits: []PageCache{
NewPageCache(PageBase(baseChunkIdx+sumsPerPhysPage-1, PallocChunkPages-64), 1<<63, 0),
NewPageCache(PageBase(baseChunkIdx+chunkIdxBigJump, 0), 1, 0),
NewPageCache(0, 0, 0),
},
after: map[ChunkIdx][]BitRange{
baseChunkIdx + sumsPerPhysPage - 1: {{0, PallocChunkPages}},
baseChunkIdx + chunkIdxBigJump: {{0, PallocChunkPages}},
},
}
}
for name, v := range tests { for name, v := range tests {
v := v v := v
t.Run(name, func(t *testing.T) { t.Run(name, func(t *testing.T) {