diff options
author | Ian Lance Taylor <iant@golang.org> | 2015-11-13 17:45:22 -0800 |
---|---|---|
committer | Ian Lance Taylor <iant@golang.org> | 2015-11-16 18:39:06 +0000 |
commit | be1ef467756bff3c475321a2213182020454075e (patch) | |
tree | 5b6258c4337051caac514ac9179528e547eede20 /src/runtime/mbarrier.go | |
parent | 1860a0fa571ff13ed2c4a23d50d55945720c86bb (diff) | |
download | go-be1ef467756bff3c475321a2213182020454075e.tar.gz go-be1ef467756bff3c475321a2213182020454075e.zip |
runtime: add optional expensive check for invalid cgo pointer passing
If you set GODEBUG=cgocheck=2 the runtime package will use the write
barrier to detect cases where a Go program writes a Go pointer into
non-Go memory. In conjunction with the existing cgo checks, and the
not-yet-implemented cgo check for exported functions, this should
reliably detect all cases (that do not import the unsafe package) in
which a Go pointer is incorrectly shared with C code. This check is
optional because it turns on the write barrier at all times, which is
known to be expensive.
Update #12416.
Change-Id: I549d8b2956daa76eac853928e9280e615d6365f4
Reviewed-on: https://go-review.googlesource.com/16899
Reviewed-by: Russ Cox <rsc@golang.org>
Diffstat (limited to 'src/runtime/mbarrier.go')
-rw-r--r-- | src/runtime/mbarrier.go | 30 |
1 files changed, 23 insertions, 7 deletions
diff --git a/src/runtime/mbarrier.go b/src/runtime/mbarrier.go index f9553b9e14..45086c43cd 100644 --- a/src/runtime/mbarrier.go +++ b/src/runtime/mbarrier.go @@ -39,7 +39,7 @@ import ( // white object dies before it is reached by the // GC then the object can be collected during this GC cycle // instead of waiting for the next cycle. Unfortunately the cost of -// ensure that the object holding the slot doesn't concurrently +// ensuring that the object holding the slot doesn't concurrently // change to black without the mutator noticing seems prohibitive. // // Consider the following example where the mutator writes into @@ -89,7 +89,7 @@ import ( // stack frames that have not been active. //go:nowritebarrierrec func gcmarkwb_m(slot *uintptr, ptr uintptr) { - if writeBarrierEnabled { + if writeBarrier.needed { if ptr != 0 && inheap(ptr) { shade(ptr) } @@ -128,7 +128,10 @@ func writebarrierptr_nostore1(dst *uintptr, src uintptr) { //go:nosplit func writebarrierptr(dst *uintptr, src uintptr) { *dst = src - if !writeBarrierEnabled { + if writeBarrier.cgo { + cgoCheckWriteBarrier(dst, src) + } + if !writeBarrier.needed { return } if src != 0 && (src < sys.PhysPageSize || src == poisonStack) { @@ -144,7 +147,10 @@ func writebarrierptr(dst *uintptr, src uintptr) { // Do not reapply. //go:nosplit func writebarrierptr_nostore(dst *uintptr, src uintptr) { - if !writeBarrierEnabled { + if writeBarrier.cgo { + cgoCheckWriteBarrier(dst, src) + } + if !writeBarrier.needed { return } if src != 0 && (src < sys.PhysPageSize || src == poisonStack) { @@ -182,6 +188,9 @@ func writebarrieriface(dst *[2]uintptr, src [2]uintptr) { //go:nosplit func typedmemmove(typ *_type, dst, src unsafe.Pointer) { memmove(dst, src, typ.size) + if writeBarrier.cgo { + cgoCheckMemmove(typ, dst, src, 0, typ.size) + } if typ.kind&kindNoPointers != 0 { return } @@ -198,7 +207,10 @@ func reflect_typedmemmove(typ *_type, dst, src unsafe.Pointer) { //go:linkname reflect_typedmemmovepartial reflect.typedmemmovepartial func reflect_typedmemmovepartial(typ *_type, dst, src unsafe.Pointer, off, size uintptr) { memmove(dst, src, size) - if !writeBarrierEnabled || typ.kind&kindNoPointers != 0 || size < sys.PtrSize || !inheap(uintptr(dst)) { + if writeBarrier.cgo { + cgoCheckMemmove(typ, dst, src, off, size) + } + if !writeBarrier.needed || typ.kind&kindNoPointers != 0 || size < sys.PtrSize || !inheap(uintptr(dst)) { return } @@ -218,7 +230,7 @@ func reflect_typedmemmovepartial(typ *_type, dst, src unsafe.Pointer, off, size // not to be preempted before the write barriers have been run. //go:nosplit func callwritebarrier(typ *_type, frame unsafe.Pointer, framesize, retoffset uintptr) { - if !writeBarrierEnabled || typ == nil || typ.kind&kindNoPointers != 0 || framesize-retoffset < sys.PtrSize || !inheap(uintptr(frame)) { + if !writeBarrier.needed || typ == nil || typ.kind&kindNoPointers != 0 || framesize-retoffset < sys.PtrSize || !inheap(uintptr(frame)) { return } heapBitsBulkBarrier(uintptr(add(frame, retoffset)), framesize-retoffset) @@ -249,11 +261,15 @@ func typedslicecopy(typ *_type, dst, src slice) int { msanread(srcp, uintptr(n)*typ.size) } + if writeBarrier.cgo { + cgoCheckSliceCopy(typ, dst, src, n) + } + // Note: No point in checking typ.kind&kindNoPointers here: // compiler only emits calls to typedslicecopy for types with pointers, // and growslice and reflect_typedslicecopy check for pointers // before calling typedslicecopy. - if !writeBarrierEnabled { + if !writeBarrier.needed { memmove(dstp, srcp, uintptr(n)*typ.size) return n } |