Skip to content

Commit 4bf9818

Browse files
committed
runtime: fix mallocgc for asan
This change finally fully fixes mallocgc for asan after the recent refactoring. Here is everything that changed: Fix the accounting for the alloc header; large objects don't have them. Mask out extra bits set from unrolling the bitmap for slice backing stores in writeHeapBitsSmall. The redzone in asan mode makes it so that dataSize is no longer an exact multiple of typ.Size_ in this case (a new assumption I have recently discovered) but we didn't mask out any extra bits, so we'd accidentally set bits in other allocations. Oops. Move the initHeapBits optimization for the 8-byte scan sizeclass on 64-bit platforms up to mallocgc, out from writeHeapBitsSmall. So, this actually caused a problem with asan when the optimization first landed, but we missed it. The issue was then masked once we started passing the redzone down into writeHeapBitsSmall, since the optimization would no longer erroneously fire on asan. What happened was that dataSize would be 8 (because that was the user-provided alloc size) so we'd skip writing heap bits, but it would turn out the redzone bumped the size class, so we'd actually *have* to write the heap bits for that size class. This is not really a problem now *but* it caused problems for me when debugging, since I would try to remove the red zone from dataSize and this would trigger this bug again. Ultimately, this whole situation is confusing because the check in writeHeapBitsSmall is *not* the same as the check in initHeapBits. By moving this check up to mallocgc, we can make the checks align better by matching on the sizeclass, so this should be less error-prone in the future. Change-Id: I1e9819223be23f722f3bf21e63e812f5fb557194 Reviewed-on: https://go-review.googlesource.com/c/go/+/622041 Reviewed-by: Keith Randall <[email protected]> Reviewed-by: Keith Randall <[email protected]> LUCI-TryBot-Result: Go LUCI <[email protected]>
1 parent 3320ce9 commit 4bf9818

File tree

3 files changed

+24
-12
lines changed

3 files changed

+24
-12
lines changed

src/runtime/arena.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -798,7 +798,7 @@ func newUserArenaChunk() (unsafe.Pointer, *mspan) {
798798

799799
if asanenabled {
800800
// TODO(mknyszek): Track individual objects.
801-
rzSize := computeRZlog(span.elemsize)
801+
rzSize := redZoneSize(span.elemsize)
802802
span.elemsize -= rzSize
803803
span.largeType.Size_ = span.elemsize
804804
rzStart := span.base() + span.elemsize

src/runtime/malloc.go

+12-6
Original file line numberDiff line numberDiff line change
@@ -1035,7 +1035,7 @@ func mallocgc(size uintptr, typ *_type, needzero bool) unsafe.Pointer {
10351035
// These "redzones" are marked as unaddressable.
10361036
var asanRZ uintptr
10371037
if asanenabled {
1038-
asanRZ = computeRZlog(size)
1038+
asanRZ = redZoneSize(size)
10391039
size += asanRZ
10401040
}
10411041

@@ -1074,10 +1074,10 @@ func mallocgc(size uintptr, typ *_type, needzero bool) unsafe.Pointer {
10741074
// Poison the space between the end of the requested size of x
10751075
// and the end of the slot. Unpoison the requested allocation.
10761076
frag := elemsize - size
1077-
if typ != nil && typ.Pointers() && !heapBitsInSpan(elemsize) {
1077+
if typ != nil && typ.Pointers() && !heapBitsInSpan(elemsize) && size <= maxSmallSize-mallocHeaderSize {
10781078
frag -= mallocHeaderSize
10791079
}
1080-
asanpoison(unsafe.Add(x, size-asanRZ), asanRZ+frag)
1080+
asanpoison(unsafe.Add(x, size-asanRZ), asanRZ)
10811081
asanunpoison(x, size-asanRZ)
10821082
}
10831083

@@ -1369,7 +1369,13 @@ func mallocgcSmallScanNoHeader(size uintptr, typ *_type, needzero bool) (unsafe.
13691369
if needzero && span.needzero != 0 {
13701370
memclrNoHeapPointers(x, size)
13711371
}
1372-
c.scanAlloc += heapSetTypeNoHeader(uintptr(x), size, typ, span)
1372+
if goarch.PtrSize == 8 && sizeclass == 1 {
1373+
// initHeapBits already set the pointer bits for the 8-byte sizeclass
1374+
// on 64-bit platforms.
1375+
c.scanAlloc += 8
1376+
} else {
1377+
c.scanAlloc += heapSetTypeNoHeader(uintptr(x), size, typ, span)
1378+
}
13731379
size = uintptr(class_to_size[sizeclass])
13741380

13751381
// Ensure that the stores above that initialize x to
@@ -2040,9 +2046,9 @@ func (p *notInHeap) add(bytes uintptr) *notInHeap {
20402046
return (*notInHeap)(unsafe.Pointer(uintptr(unsafe.Pointer(p)) + bytes))
20412047
}
20422048

2043-
// computeRZlog computes the size of the redzone.
2049+
// redZoneSize computes the size of the redzone for a given allocation.
20442050
// Refer to the implementation of the compiler-rt.
2045-
func computeRZlog(userSize uintptr) uintptr {
2051+
func redZoneSize(userSize uintptr) uintptr {
20462052
switch {
20472053
case userSize <= (64 - 16):
20482054
return 16 << 0

src/runtime/mbitmap.go

+11-5
Original file line numberDiff line numberDiff line change
@@ -640,11 +640,6 @@ func (span *mspan) heapBitsSmallForAddr(addr uintptr) uintptr {
640640
//
641641
//go:nosplit
642642
func (span *mspan) writeHeapBitsSmall(x, dataSize uintptr, typ *_type) (scanSize uintptr) {
643-
if goarch.PtrSize == 8 && dataSize == goarch.PtrSize {
644-
// Already set by initHeapBits.
645-
return
646-
}
647-
648643
// The objects here are always really small, so a single load is sufficient.
649644
src0 := readUintptr(typ.GCData)
650645

@@ -654,10 +649,21 @@ func (span *mspan) writeHeapBitsSmall(x, dataSize uintptr, typ *_type) (scanSize
654649
if typ.Size_ == goarch.PtrSize {
655650
src = (1 << (dataSize / goarch.PtrSize)) - 1
656651
} else {
652+
// N.B. We rely on dataSize being an exact multiple of the type size.
653+
// The alternative is to be defensive and mask out src to the length
654+
// of dataSize. The purpose is to save on one additional masking operation.
655+
if doubleCheckHeapSetType && !asanenabled && dataSize%typ.Size_ != 0 {
656+
throw("runtime: (*mspan).writeHeapBitsSmall: dataSize is not a multiple of typ.Size_")
657+
}
657658
for i := typ.Size_; i < dataSize; i += typ.Size_ {
658659
src |= src0 << (i / goarch.PtrSize)
659660
scanSize += typ.Size_
660661
}
662+
if asanenabled {
663+
// Mask src down to dataSize. dataSize is going to be a strange size because of
664+
// the redzone required for allocations when asan is enabled.
665+
src &= (1 << (dataSize / goarch.PtrSize)) - 1
666+
}
661667
}
662668

663669
// Since we're never writing more than one uintptr's worth of bits, we're either going

0 commit comments

Comments
 (0)