diff --git a/src/reflect/value.go b/src/reflect/value.go index ec4f8efa..a5f7278e 100644 --- a/src/reflect/value.go +++ b/src/reflect/value.go @@ -28,6 +28,13 @@ func (v Value) isIndirect() bool { return v.flags&valueFlagIndirect != 0 } +// isExported returns whether the value represented by this Value could be +// accessed without violating type system constraints. For example, it is not +// set for unexported struct fields. +func (v Value) isExported() bool { + return v.flags&valueFlagExported != 0 +} + func Indirect(v Value) Value { if v.Kind() != Ptr { return v @@ -51,6 +58,15 @@ func ValueOf(i interface{}) Value { } func (v Value) Interface() interface{} { + if !v.isExported() { + panic("(reflect.Value).Interface: unexported") + } + return valueInterfaceUnsafe(v) +} + +// valueInterfaceUnsafe is used by the runtime to hash map keys. It should not +// be subject to the isExported check. +func valueInterfaceUnsafe(v Value) interface{} { if v.isIndirect() && v.typecode.Size() <= unsafe.Sizeof(uintptr(0)) { // Value was indirect but must be put back directly in the interface // value. diff --git a/src/runtime/hashmap.go b/src/runtime/hashmap.go index 478014bd..fdfbcfde 100644 --- a/src/runtime/hashmap.go +++ b/src/runtime/hashmap.go @@ -349,6 +349,13 @@ func hashmapStringDelete(m *hashmap, key string) { // Hashmap with interface keys (for everything else). +// This is a method that is intentionally unexported in the reflect package. It +// is identical to the Interface() method call, except it doesn't check whether +// a field is exported and thus allows circumventing the type system. +// The hash function needs it as it also needs to hash unexported struct fields. +//go:linkname valueInterfaceUnsafe reflect.valueInterfaceUnsafe +func valueInterfaceUnsafe(v reflect.Value) interface{} + func hashmapInterfaceHash(itf interface{}) uint32 { x := reflect.ValueOf(itf) if x.RawType() == 0 { @@ -384,13 +391,13 @@ func hashmapInterfaceHash(itf interface{}) uint32 { case reflect.Array: var hash uint32 for i := 0; i < x.Len(); i++ { - hash |= hashmapInterfaceHash(x.Index(i).Interface()) + hash |= hashmapInterfaceHash(valueInterfaceUnsafe(x.Index(i))) } return hash case reflect.Struct: var hash uint32 for i := 0; i < x.NumField(); i++ { - hash |= hashmapInterfaceHash(x.Field(i).Interface()) + hash |= hashmapInterfaceHash(valueInterfaceUnsafe(x.Field(i))) } return hash default: