Skip to content

fix(gosym): prevent 32-bit truncation panic in PCIndex - #5

Open
ladung wants to merge 1 commit into
coroot:mainfrom
ladung:fix/pcindex-32bit-truncation-panic
Open

fix(gosym): prevent 32-bit truncation panic in PCIndex#5
ladung wants to merge 1 commit into
coroot:mainfrom
ladung:fix/pcindex-32bit-truncation-panic

Conversation

@ladung

@ladung ladung commented Aug 10, 2026

Copy link
Copy Markdown

Problem

PCIndex.FindIndex guards its lower bound against the full 64-bit address:

if addr < uint64(it.i32[0]) {
    return -1
}

but the search immediately after truncates it to 32 bits:

i, found := slices.BinarySearch(it.i32, uint32(addr))

The two disagree for any address at or above 2^32, and the outcome depends on the low 32 bits:

  • low word below i32[0] — the guard passes, BinarySearch returns (0, false), i-- makes it -1, and it.i32[i] panics with runtime error: index out of range [-1]
  • low word inside the range — a wrong entry matches and FindIndex returns it, so the profiler silently attributes samples to the wrong symbol

Observed in production as a crash-looping coroot-node-agent 1.35.4 DaemonSet pod:

panic: runtime error: index out of range [-1]

github.com/grafana/pyroscope/ebpf/symtab/gosym.(*PCIndex).FindIndex
      symtab/gosym/pcindex.go:81
github.com/grafana/pyroscope/ebpf/symtab/elf.(*SymbolTable).Resolve
      symtab/elf/symbol_table.go:125
github.com/grafana/pyroscope/ebpf/symtab.(*ElfTable).Resolve
      symtab/elf.go:229
github.com/grafana/pyroscope/ebpf/symtab.(*ProcTable).Resolve
      symtab/proc.go:165
github.com/grafana/pyroscope/ebpf.(*session).WalkStack
      session.go:552
github.com/grafana/pyroscope/ebpf.(*session).collectRegularProfile
      session.go:373

The i64 branch is unaffected — it performs no truncation.

Fix

An address above MaxUint32 cannot be covered by a 32-bit index in the first place: Set() only stores into i32 when the value fits in 32 bits, promoting to i64 otherwise. So -1 is the correct answer here, and callers already expect it — elf.SymbolTable.Resolve checks if i == -1 { return "" } (symbol_table.go:126).

if addr > math.MaxUint32 || addr < uint64(it.i32[0]) {
    return -1
}

Guarding only the negative index (if i < 0 { return -1 } before the load) would stop the crash but leave the silent-wrong-symbol case intact. Fixing the comparison closes both, and silent wrong data is the worse of the two failures.

Tests

Four tests in pcindex_panic_test.go:

Test Covers
TestFindIndexTruncationPanic the index out of range [-1] panic
TestFindIndexTruncationWrongSymbol the silent wrong-symbol match — why a bare i < 0 guard is insufficient
TestFindIndexBelowFirstStillReturnsMinusOne pins the existing below-first behaviour
TestFindIndexNormalLookupsUnaffected pins exact / between / past-the-end lookups

The first two fail on main — the first by panicking, the second by returning 0 instead of -1. All four pass with the fix, and the rest of the gosym package is unaffected.
Note: merging this does not ship the fix. coroot-node-agent pins this module by pseudo-version (go.mod:220v0.0.0-20260804213318-758a0e72af3a, the buggy commit), so its replace needs bumping and a release cut afterwards. Until then the only workaround is nodeAgent.ebpfProfiler.enabled: false, which is the sole path in node-agent that reaches FindIndex.

@ladung

ladung commented Aug 10, 2026

Copy link
Copy Markdown
Author

@def please help review

@ladung

ladung commented Aug 12, 2026

Copy link
Copy Markdown
Author

hello @def

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants