aboutsummaryrefslogtreecommitdiff
path: root/src/reflect
diff options
context:
space:
mode:
authorKeith Randall <khr@golang.org>2021-09-15 09:56:09 -0700
committerKeith Randall <khr@golang.org>2021-10-15 18:07:49 +0000
commit8331f25e96d6120bb0ec212bd03abcae53282769 (patch)
tree28214cd8c99d41a8f1e1de8c384f605c2477c09e /src/reflect
parent8c99421f01aca303240a8f809bc65fa0c56db861 (diff)
downloadgo-8331f25e96d6120bb0ec212bd03abcae53282769.tar.gz
go-8331f25e96d6120bb0ec212bd03abcae53282769.zip
reflect: make Elem panic on bad notinheap pointers
This CL fixes the subtle issue that Elem can promote a not-in-heap pointer, which could be any bit pattern, into an unsafe.Pointer, which the garbage collector can see. If that resulting value is bad, it can crash the GC. Make sure that we don't introduce bad pointers that way. We can make Elem() panic, because any such bad pointers are in the Go heap, and not-in-heap pointers are not allowed to point into the Go heap. Update #48399 Change-Id: Ieaf35a611b16b4dfb5e907e229ed4a2aed30e18c Reviewed-on: https://go-review.googlesource.com/c/go/+/350153 Trust: Keith Randall <khr@golang.org> Trust: Ian Lance Taylor <iant@golang.org> Run-TryBot: Keith Randall <khr@golang.org> TryBot-Result: Go Bot <gobot@golang.org> Reviewed-by: Ian Lance Taylor <iant@golang.org>
Diffstat (limited to 'src/reflect')
-rw-r--r--src/reflect/all_test.go20
-rw-r--r--src/reflect/value.go17
2 files changed, 37 insertions, 0 deletions
diff --git a/src/reflect/all_test.go b/src/reflect/all_test.go
index 427855b02e..8642d60f8b 100644
--- a/src/reflect/all_test.go
+++ b/src/reflect/all_test.go
@@ -7697,3 +7697,23 @@ func TestSetIter(t *testing.T) {
t.Errorf("pointer incorrect: got %d want %d", got, b)
}
}
+
+//go:notinheap
+type nih struct{ x int }
+
+var global_nih = nih{x: 7}
+
+func TestNotInHeapDeref(t *testing.T) {
+ // See issue 48399.
+ v := ValueOf((*nih)(nil))
+ v.Elem()
+ shouldPanic("reflect: call of reflect.Value.Field on zero Value", func() { v.Elem().Field(0) })
+
+ v = ValueOf(&global_nih)
+ if got := v.Elem().Field(0).Int(); got != 7 {
+ t.Fatalf("got %d, want 7", got)
+ }
+
+ v = ValueOf((*nih)(unsafe.Pointer(new(int))))
+ shouldPanic("reflect: reflect.Value.Elem on an invalid notinheap pointer", func() { v.Elem() })
+}
diff --git a/src/reflect/value.go b/src/reflect/value.go
index abcc346de8..449f3bbb3c 100644
--- a/src/reflect/value.go
+++ b/src/reflect/value.go
@@ -1169,6 +1169,21 @@ func (v Value) Elem() Value {
case Ptr:
ptr := v.ptr
if v.flag&flagIndir != 0 {
+ if ifaceIndir(v.typ) {
+ // This is a pointer to a not-in-heap object. ptr points to a uintptr
+ // in the heap. That uintptr is the address of a not-in-heap object.
+ // In general, pointers to not-in-heap objects can be total junk.
+ // But Elem() is asking to dereference it, so the user has asserted
+ // that at least it is a valid pointer (not just an integer stored in
+ // a pointer slot). So let's check, to make sure that it isn't a pointer
+ // that the runtime will crash on if it sees it during GC or write barriers.
+ // Since it is a not-in-heap pointer, all pointers to the heap are
+ // forbidden! That makes the test pretty easy.
+ // See issue 48399.
+ if !verifyNotInHeapPtr(*(*uintptr)(ptr)) {
+ panic("reflect: reflect.Value.Elem on an invalid notinheap pointer")
+ }
+ }
ptr = *(*unsafe.Pointer)(ptr)
}
// The returned value's address is v's value.
@@ -3406,6 +3421,8 @@ func typedslicecopy(elemType *rtype, dst, src unsafeheader.Slice) int
//go:noescape
func typehash(t *rtype, p unsafe.Pointer, h uintptr) uintptr
+func verifyNotInHeapPtr(p uintptr) bool
+
// Dummy annotation marking that the value x escapes,
// for use in cases where the reflect code is so clever that
// the compiler cannot follow.