#!stacks
"sigpanic" && "growslice:+17"

Issue created by stacks.

Another impossible crash: the compiler generates code to load the *Snapshot type descriptor from rodata in R4, then calls growslice, which experiences a SEGV while loading from this register at L194.

golang.org/x/tools/gopls/internal/cache.(*Session).ExpandModificationsToDirectories STEXT size=1504 args=0x30 locals=0x318 funcid=0x0 align=0x0
...
    0x019c 00412    L0967   MOVD    $type:*golang.org/x/tools/gopls/internal/cache.Snapshot(SB), R4
    0x01a4 00420    L0967   CALL    runtime.growslice(SB)
...
runtime.growslice STEXT size=1536 args=0x28 locals=0x58 funcid=0x0 align=0x0
    0x0000 00000    L0177   MOVD    16(g), R16
    0x0004 00004    L0177   CMP R16, RSP
    0x0008 00008    L0177   BLS 1500
    0x000c 00012    L0177   MOVD.W  R30, -96(RSP)
    0x0010 00016    L0177   MOVD    R29, -8(RSP)
    0x0014 00020    L0177   SUB $8, RSP, R29
    0x0018 00024    L0178   SUB R3, R1, R3
    0x001c 00028    L0190   TBNZ    $63, R1, 1476
    0x0020 00032    L0194   MOVD    (R4), R5                        <----- SEGV

Unlike all previous crashes, which have been loads from SP or g, this one is an ordinary register. I'm struggling to think of a realistic scenario in which an application bug (e.g. data race, misuse of unsafe) could cause this problem. It's one thing for buggy code to clobber arbitrary memory, but this crash means that a register has been corrupted. R4 has somehow been clobbered between the load from rodata (which can't have been written) and the load at L194, with no intervening function calls. I suppose it's possible that the runtime suspended the g in between, and the memory where the g's register file was saved was corrupted, but that seems very unlikely, not least because there is nothing that would synchronously invoke the scheduler in this handful of instructions.

I'm starting to suspect that this is a bug not in the application, but in the runtime or hardware. @prattmic

This stack fcv6BQ was reported by telemetry:

golang.org/x/tools/gopls@v0.20.0 go1.24.4 darwin/arm64 other,vscode (1)

Comment From: gabyhelp

Related Issues

(Emoji vote if this was helpful or unhelpful; more detailed feedback welcome in this discussion.)

Comment From: prattmic

there is nothing that would synchronously invoke the scheduler in this handful of instructions

The beginning of this function does have a stack check:

runtime.growslice STEXT size=1536 args=0x28 locals=0x58 funcid=0x0 align=0x0
    0x0000 00000    L0177   MOVD    16(g), R16
    0x0004 00004    L0177   CMP R16, RSP
    0x0008 00008    L0177   BLS 1500

The BLS 1500 is presumably a jump down to a call to runtime.morestack. This would be used to grow the stack, or do synchronous preemption (morestack handles both). Upon return, morestack would continue execution of the function.

So if preemption corrupted R4, it didn't need to be asynchronous preemption. Still, corruption of an ordinary general purpose register would be a fairly surprising bug.

cc @golang/runtime