Go 1.15.3 repairs pointer metadata for deferred calls
A missing runtime parameter changed how the garbage collector interpreted an argument pointer on 32-bit systems. The October maintenance release repairs that signature and other compiler-runtime assumptions.
A function declaration can influence memory safety even when the machine code receiving the arguments appears to work. Go 1.15.3 repairs the declaration of a runtime call helper whose missing parameter gave the garbage collector the wrong interpretation of an argument. Under a particular combination of deferred calls, panic handling, and stack growth on 32-bit systems, that mismatch could corrupt memory.
Today's maintenance release includes compiler and runtime fixes as well as changes to cgo, the Go command, and several packages. The pointer-metadata repair is a useful account of the work hidden by that summary. Runtime declarations describe how the implementation should trace and adjust values, as well as how a call passes arguments.
I like Go because ordinary application code rarely needs to reason about stack movement. That convenience depends on accurate metadata at the implementation boundary. A small internal signature error can undermine a guarantee that the rest of the language makes easy to take for granted.
The missing parameter changed the pointer map
The call16 correction restores a missing typ parameter. This helper exists only on 32-bit architectures and its declaration lived separately from the other call helpers. The change explains that macro expansion also prevented the normal vet check from catching the mismatch.
The declaration error caused the arg parameter to be treated as a scalar instead of a pointer. That matters in two ways. Garbage collection does not trace a scalar as a reference, and stack copying does not adjust it as an address into a moved stack. An argument can therefore become invalid even if the call initially received the expected bits.
The affected case is specific. A stack-allocated deferred call must have an argument frame of 16 bytes or less, including a nonempty result area. The deferred function must grow the stack while running during a panic. The commit identifies that combination as the case where the wrong signature matters in current code.
Other uses passed a heap-allocated argument frame that remained reachable through other stack roots. That explains why the declaration could be wrong without every call immediately failing. Another reference could preserve reachability, and heap storage would not need the same adjustment as an address into the copied stack.
The repair changes the metadata contract to match the actual helper. It does not ask application authors to avoid small deferred functions or to predict when panic processing will grow a stack. Those are implementation responsibilities behind valid language behavior.
Liveness needs a reliable source of information
A separate compiler fix for deferred arguments corrects how the compiler computes live-variable information for deferreturn. The old approach used the live set from the last return point. But a function may have no return point, or that point may precede its final deferred call.
The implementation also tracked the last call as an internal compiler value. If dead-code elimination removed that value, its storage could be reused for a different instruction. Information associated with that reused location could not safely describe the original call's liveness.
The patch instead marks the argument slots for open-coded defers as live throughout the function. Those slots are already zero-initialized. This uses a conservative liveness rule to avoid depending on a return or call record that may not describe the state needing protection.
The two fixes concern different stages. The runtime signature tells stack scanning which parameters are pointers. Compiler liveness information tells it which argument storage must remain visible. Both must agree with the actual execution, including paths entered during panic processing.
Parking on a channel is a transition
Another runtime patch closes a stack-shrinking race while a goroutine parks on a channel operation. The goroutine's status changed before its channel-related stack flag had been updated. A concurrent marking worker could observe the new status, conclude the goroutine had stopped, and inspect the old flag.
That stale observation allowed stack shrinking without the synchronization required for concurrent channel operations. Updating pointers into the stack could then race with channel code manipulating the same pointers. The problem occurs in the transition between two apparently stable states.
The correction adds an atomically updated parkingOnChan flag covering that interval. A marking worker that observes it refuses to shrink the stack. Once the flag clears after the transition, the existing channel-stack state is reliable again. The runtime skips shrinking at the unsafe moment instead of trying to complete it with incomplete information.
I regard these changes as evidence that precise boundaries matter more than the apparent size of a patch. Each correction identifies information another subsystem trusted too early or interpreted incorrectly. Go 1.15.3 restores the pointer signature, preserves deferred argument liveness, and prevents stack shrinking during the channel-parking interval where the old state was not yet sufficient.