aboutsummaryrefslogtreecommitdiff
path: root/src/runtime/mbarrier.go
diff options
context:
space:
mode:
authorIan Lance Taylor <iant@golang.org>2015-11-13 17:45:22 -0800
committerIan Lance Taylor <iant@golang.org>2015-11-16 18:39:06 +0000
commitbe1ef467756bff3c475321a2213182020454075e (patch)
tree5b6258c4337051caac514ac9179528e547eede20 /src/runtime/mbarrier.go
parent1860a0fa571ff13ed2c4a23d50d55945720c86bb (diff)
downloadgo-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.go30
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
}