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.
This commit is contained in:
Ayke van Laethem
2021-01-30 12:22:55 +01:00
committed by Ron Evans
parent 46a7993fb8
commit f800f7507c
2 changed files with 25 additions and 2 deletions
+16
View File
@@ -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.