Skip to content

Commit aa581f5

Browse files
committed
runtime: use typedmemclr for typed memory
The hybrid barrier requires distinguishing typed and untyped memory even when zeroing because the *current* contents of the memory matters even when overwriting. This commit introduces runtime.typedmemclr and runtime.memclrHasPointers as a typed memory clearing functions parallel to runtime.typedmemmove. Currently these simply call memclr, but with the hybrid barrier we'll need to shade any pointers we're overwriting. These will provide us with the necessary hooks to do so. Updates #17503. Change-Id: I74478619f8907825898092aaa204d6e4690f27e6 Reviewed-on: https://go-review.googlesource.com/31366 Reviewed-by: Keith Randall <[email protected]> Reviewed-by: Rick Hudson <[email protected]>
1 parent a475a38 commit aa581f5

File tree

7 files changed

+53
-10
lines changed

7 files changed

+53
-10
lines changed

src/reflect/value.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -440,6 +440,8 @@ func (v Value) call(op string, in []Value) []Value {
440440

441441
var ret []Value
442442
if nout == 0 {
443+
// This is untyped because the frame is really a
444+
// stack, even though it's a heap object.
443445
memclr(args, frametype.size)
444446
framePool.Put(args)
445447
} else {
@@ -644,6 +646,8 @@ func callMethod(ctxt *methodValue, frame unsafe.Pointer) {
644646
retOffset,
645647
frametype.size-retOffset)
646648

649+
// This is untyped because the frame is really a stack, even
650+
// though it's a heap object.
647651
memclr(args, frametype.size)
648652
framePool.Put(args)
649653
}

src/runtime/chan.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -334,7 +334,7 @@ func closechan(c *hchan) {
334334
break
335335
}
336336
if sg.elem != nil {
337-
memclr(sg.elem, uintptr(c.elemsize))
337+
typedmemclr(c.elemtype, sg.elem)
338338
sg.elem = nil
339339
}
340340
if sg.releasetime != 0 {
@@ -443,7 +443,7 @@ func chanrecv(t *chantype, c *hchan, ep unsafe.Pointer, block bool) (selected, r
443443
}
444444
unlock(&c.lock)
445445
if ep != nil {
446-
memclr(ep, uintptr(c.elemsize))
446+
typedmemclr(c.elemtype, ep)
447447
}
448448
return true, false
449449
}
@@ -467,7 +467,7 @@ func chanrecv(t *chantype, c *hchan, ep unsafe.Pointer, block bool) (selected, r
467467
if ep != nil {
468468
typedmemmove(c.elemtype, ep, qp)
469469
}
470-
memclr(qp, uintptr(c.elemsize))
470+
typedmemclr(c.elemtype, qp)
471471
c.recvx++
472472
if c.recvx == c.dataqsiz {
473473
c.recvx = 0

src/runtime/hashmap.go

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -637,9 +637,17 @@ func mapdelete(t *maptype, h *hmap, key unsafe.Pointer) {
637637
if !alg.equal(key, k2) {
638638
continue
639639
}
640-
memclr(k, uintptr(t.keysize))
640+
if t.indirectkey {
641+
*(*unsafe.Pointer)(k) = nil
642+
} else {
643+
typedmemclr(t.key, k)
644+
}
641645
v := unsafe.Pointer(uintptr(unsafe.Pointer(b)) + dataOffset + bucketCnt*uintptr(t.keysize) + i*uintptr(t.valuesize))
642-
memclr(v, uintptr(t.valuesize))
646+
if t.indirectvalue {
647+
*(*unsafe.Pointer)(v) = nil
648+
} else {
649+
typedmemclr(t.elem, v)
650+
}
643651
b.tophash[i] = empty
644652
h.count--
645653
goto done
@@ -1079,7 +1087,11 @@ func evacuate(t *maptype, h *hmap, oldbucket uintptr) {
10791087
b = (*bmap)(add(h.oldbuckets, oldbucket*uintptr(t.bucketsize)))
10801088
// Preserve b.tophash because the evacuation
10811089
// state is maintained there.
1082-
memclr(add(unsafe.Pointer(b), dataOffset), uintptr(t.bucketsize)-dataOffset)
1090+
if t.bucket.kind&kindNoPointers == 0 {
1091+
memclrHasPointers(add(unsafe.Pointer(b), dataOffset), uintptr(t.bucketsize)-dataOffset)
1092+
} else {
1093+
memclr(add(unsafe.Pointer(b), dataOffset), uintptr(t.bucketsize)-dataOffset)
1094+
}
10831095
}
10841096
}
10851097

src/runtime/iface.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -222,7 +222,7 @@ func assertI2T(t *_type, i iface, r unsafe.Pointer) {
222222
func assertI2T2(t *_type, i iface, r unsafe.Pointer) bool {
223223
tab := i.tab
224224
if tab == nil || tab._type != t {
225-
memclr(r, t.size)
225+
typedmemclr(t, r)
226226
return false
227227
}
228228
if isDirectIface(t) {
@@ -257,7 +257,7 @@ func assertE2T2(t *_type, e eface, r unsafe.Pointer) bool {
257257
GC()
258258
}
259259
if e._type != t {
260-
memclr(r, t.size)
260+
typedmemclr(t, r)
261261
return false
262262
}
263263
if isDirectIface(t) {

src/runtime/mbarrier.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -331,3 +331,24 @@ func reflect_typedslicecopy(elemType *_type, dst, src slice) int {
331331
}
332332
return typedslicecopy(elemType, dst, src)
333333
}
334+
335+
// typedmemclr clears the typed memory at ptr with type typ. The
336+
// memory at ptr must already be type-safe.
337+
//
338+
// If the caller knows that typ has pointers, it can alternatively
339+
// call memclrHasPointers.
340+
//
341+
//go:nosplit
342+
func typedmemclr(typ *_type, ptr unsafe.Pointer) {
343+
memclr(ptr, typ.size)
344+
}
345+
346+
// memclrHasPointers clears n bytes of typed memory starting at ptr.
347+
// The caller must ensure that the type of the object at ptr has
348+
// pointers, usually by checking typ.kind&kindNoPointers. However, ptr
349+
// does not have to point to the start of the allocation.
350+
//
351+
//go:nosplit
352+
func memclrHasPointers(ptr unsafe.Pointer, n uintptr) {
353+
memclr(ptr, n)
354+
}

src/runtime/select.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -518,7 +518,7 @@ bufrecv:
518518
if cas.elem != nil {
519519
typedmemmove(c.elemtype, cas.elem, qp)
520520
}
521-
memclr(qp, uintptr(c.elemsize))
521+
typedmemclr(c.elemtype, qp)
522522
c.recvx++
523523
if c.recvx == c.dataqsiz {
524524
c.recvx = 0
@@ -564,7 +564,7 @@ rclose:
564564
*cas.receivedp = false
565565
}
566566
if cas.elem != nil {
567-
memclr(cas.elem, uintptr(c.elemsize))
567+
typedmemclr(c.elemtype, cas.elem)
568568
}
569569
if raceenabled {
570570
raceacquire(unsafe.Pointer(c))

src/runtime/stubs.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,12 @@ func badsystemstack() {
6161
}
6262

6363
// memclr clears n bytes starting at ptr.
64+
//
65+
// Usually you should use typedmemclr. memclr should be used only when
66+
// the caller knows that *ptr contains no heap pointers or to
67+
// initialize memory to a type-safe state when allocation reuses dead
68+
// memory.
69+
//
6470
// in memclr_*.s
6571
//go:noescape
6672
func memclr(ptr unsafe.Pointer, n uintptr)

0 commit comments

Comments
 (0)