• Russ Cox's avatar
    reflect, runtime: fix crash in GC due to reflect.call + precise GC · 72c5d5e7
    Russ Cox authored
    Given
            type Outer struct {
                    *Inner
                    ...
            }
    the compiler generates the implementation of (*Outer).M dispatching to
    the embedded Inner. The implementation is logically:
            func (p *Outer) M() {
                    (p.Inner).M()
            }
    but since the only change here is the replacement of one pointer
    receiver with another, the actual generated code overwrites the
    original receiver with the p.Inner pointer and then jumps to the M
    method expecting the *Inner receiver.
    
    During reflect.Value.Call, we create an argument frame and the
    associated data structures to describe it to the garbage collector,
    populate the frame, call reflect.call to run a function call using
    that frame, and then copy the results back out of the frame. The
    reflect.call function does a memmove of the frame structure onto the
    stack (to set up the inputs), runs the call, and the memmoves the
    stack back to the frame structure (to preserve the outputs).
    
    Originally reflect.call did not distinguish inputs from outputs: both
    memmoves were for the full stack frame. However, in the case where the
    called function was one of these wrappers, the rewritten receiver is
    almost certainly a different type than the original receiver. This is
    not a problem on the stack, where we use the program counter to
    determine the type information and understand that during (*Outer).M
    the receiver is an *Outer while during (*Inner).M the receiver in the
    same memory word is now an *Inner. But in the statically typed
    argument frame created by reflect, the receiver is always an *Outer.
    Copying the modified receiver pointer off the stack into the frame
    will store an *Inner there, and then if a garbage collection happens
    to scan that argument frame before it is discarded, it will scan the
    *Inner memory as if it were an *Outer. If the two have different
    memory layouts, the collection will intepret the memory incorrectly.
    
    Fix by only copying back the results.
    
    Fixes #7725.
    
    LGTM=khr
    R=khr
    CC=dave, golang-codereviews
    https://golang.org/cl/85180043
    72c5d5e7
export_test.go 447 Bytes