reflect: check for access in the Interface method call
This fixes a type system loophole. The following program would incorrectly run in TinyGo, while it would trigger a panic in Go: package main import "reflect" func main() { v := reflect.ValueOf(struct { x int }{}) x := v.Field(0).Interface() println("x:", x.(int)) } Playground link: https://play.golang.org/p/nvvA18XFqFC The panic in Go is the following: panic: reflect.Value.Interface: cannot return value obtained from unexported field or method I've shortened it in TinyGo to save a little bit of space.
Этот коммит содержится в:
родитель
46a7993fb8
коммит
f800f7507c
2 изменённых файлов: 25 добавлений и 2 удалений
|
@ -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.
|
||||
|
|
|
@ -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:
|
||||
|
|
Загрузка…
Создание таблицы
Сослаться в новой задаче