Skip to content

runtime: panic FP walk can start from a returned C helper frame #2149

Description

@cpunion

Summary

An unrecovered panic can fall back from LLGo's Go-style traceback to the raw clite PC dump when the physical frame-pointer walk starts from an invalid frame. The failure was first exposed by combining a logging dependency graph with a panic probe, but the minimized reproducer only needs a shallow panic, the public runtime package, DWARF, and -O0.

This is independent of #2115: funcinfo and runtime.Stack remain available; only the panic-time physical walk returns no frames.

Reproduction

On Darwin/arm64 with current main:

package main

import "runtime"

//go:noinline
func panicSite() {
	panic("shallow-panic")
}

func main() {
	_ = runtime.NumCPU()
	panicSite()
}
llgo run -a -O0 -ldflags=-w=false main.go

Actual output falls back to raw addresses:

panic: shallow-panic

[0x... command-line-arguments.panicSite+0x20, SP = ...]
[0x... command-line-arguments.main+..., SP = ...]
[0x... main+..., SP = ...]

Expected output starts with goroutine 1 [running]: and contains Go-style main.panicSite(...) and main.main(...) frames.

Root cause

runtime/internal/lib/runtime/_wrap/runtime.c:llgo_framepointer returns __builtin_frame_address(0), the C helper's own frame address. The Go walker dereferences that address after the helper has returned, so it is a dangling stack-frame pointer. In the failing O0/DWARF layout its saved-FP slot has already been overwritten (observed value: 64), and the monotonic-chain guard stops immediately.

Optimized layouts often leave the stale bytes intact long enough to hide the bug. This explains why #2143's O2 pipeline appears to fix the larger probe while -O0 still fails.

The helper should read its saved caller FP while its own frame is alive and return that FP value. That matches the Go-side walker's existing assumption and skip++ accounting.

Acceptance

  • A shallow unrecovered panic emits a Go-style traceback under -O0 -ldflags=-w=false.
  • Caller/Callers/Stack and panic traceback tests pass at O0/O2 with and without DWARF.
  • Darwin and Linux native builds pass.

Proposed fix

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions