aboutsummaryrefslogtreecommitdiff
path: root/src/runtime/malloc.go
diff options
context:
space:
mode:
Diffstat (limited to 'src/runtime/malloc.go')
-rw-r--r--src/runtime/malloc.go46
1 files changed, 29 insertions, 17 deletions
diff --git a/src/runtime/malloc.go b/src/runtime/malloc.go
index 7f85dfd0ed..46200037e2 100644
--- a/src/runtime/malloc.go
+++ b/src/runtime/malloc.go
@@ -1548,12 +1548,13 @@ func mallocgcLarge(size uintptr, typ *_type, needzero bool) (unsafe.Pointer, uin
size = span.elemsize
x := unsafe.Pointer(span.base())
- // Ensure that the stores above that initialize x to
- // type-safe memory and set the heap bits occur before
- // the caller can make x observable to the garbage
- // collector. Otherwise, on weakly ordered machines,
- // the garbage collector could follow a pointer to x,
- // but see uninitialized memory or stale heap bits.
+ // Ensure that the store above that sets largeType to
+ // nil happens before the caller can make x observable
+ // to the garbage collector.
+ //
+ // Otherwise, on weakly ordered machines, the garbage
+ // collector could follow a pointer to x, but see a stale
+ // largeType value.
publicationBarrier()
if writeBarrier.enabled {
@@ -1596,22 +1597,33 @@ func mallocgcLarge(size uintptr, typ *_type, needzero bool) (unsafe.Pointer, uin
}
// Objects can be zeroed late in a context where preemption can occur.
- // If the object contains pointers, its pointer data must be cleared
- // or otherwise indicate that the GC shouldn't scan it.
+ //
// x will keep the memory alive.
- if noscan := typ == nil || !typ.Pointers(); !noscan || (needzero && span.needzero != 0) {
+ if needzero && span.needzero != 0 {
// N.B. size == fullSize always in this case.
memclrNoHeapPointersChunked(size, x) // This is a possible preemption point: see #47302
+ }
- // Finish storing the type information for this case.
- mp := acquirem()
- if !noscan {
- getMCache(mp).scanAlloc += heapSetTypeLarge(uintptr(x), size, typ, span)
- }
- // Publish the object with the now-zeroed memory.
- publicationBarrier()
- releasem(mp)
+ // Set the type and run the publication barrier while non-preemptible. We need to make
+ // sure that between heapSetTypeLarge and publicationBarrier we cannot get preempted,
+ // otherwise the GC could potentially observe non-zeroed memory but largeType set on weak
+ // memory architectures.
+ //
+ // The GC can also potentially observe non-zeroed memory if conservative scanning spuriously
+ // observes a partially-allocated object, see the freeIndexForScan update above. This case is
+ // handled by synchronization inside heapSetTypeLarge.
+ mp = acquirem()
+ if typ != nil && typ.Pointers() {
+ // Finish storing the type information, now that we're certain the memory is zeroed.
+ getMCache(mp).scanAlloc += heapSetTypeLarge(uintptr(x), size, typ, span)
}
+ // Publish the object again, now with zeroed memory and initialized type information.
+ //
+ // Even if we didn't update any type information, this is necessary to ensure that, for example,
+ // x written to a global without any synchronization still results in other goroutines observing
+ // zeroed memory.
+ publicationBarrier()
+ releasem(mp)
return x, size
}