Skip to content

SIGTRAP crash when a program mixes Buffer-typed and Uint8Array-typed function parameters #169

@proggeramlug

Description

@proggeramlug

Summary

A program that defines two functions — one taking a `Buffer` parameter, one taking a `Uint8Array` parameter — and invokes them in sequence on the same underlying buffer crashes with SIGTRAP (exit 133) in the second call. The same functions in isolation both work correctly. Reproducible on current `main` with zero local changes applied.

Discovered while extending the #92 / #166 buffer-read intrinsic to cover Buffer-typed params. Blocks extending the extension to Uint8Array-typed params (both shapes are identical from the intrinsic's perspective), so this issue gates that follow-up work.

Repro (crashes on main at v0.5.179)

```ts
const big = Buffer.alloc(1024);
for (let i = 0; i < 256; i++) big.writeInt32BE(i * 37, i * 4);

function sumRow(row: Buffer, n: number): number {
let s = 0;
for (let i = 0; i < n; i++) s += row.readInt32BE(i * 4);
return s;
}
console.log('param sum:', sumRow(big, 256)); // prints 1207680 correctly

function firstBytes(arr: Uint8Array): number {
return arr[0] + arr[1] + arr[2] + arr[3];
}
console.log('u8 param:', firstBytes(big)); // crashes
```

```
$ perry compile repro.ts -o repro && ./repro
param sum: 1207680
; exit: 133
```

Node and Bun both complete normally and print `u8 param: 0`.

Bisect

Each call works in isolation — removing either function eliminates the crash:

Scenario Result
`firstBytes(big)` alone ✅ prints 0
`sumRow(big, 256)` alone ✅ prints 1207680
Both, `firstBytes` first (untested — may not crash)
Both, `sumRow` first ❌ SIGTRAP in `firstBytes`

Tried disabling the #166 Buffer-read intrinsic entirely at the codegen level (`try_emit_buffer_read_intrinsic` forced to return `None`): crash still happens. So the intrinsic is not the cause; this is a pre-existing bug in the interaction between the two param shapes.

Crash signal

`lldb` shows the program stops on `brk #0x1` — an ARM64 instruction LLVM emits as a `noreturn` marker after a call-to-panic. So some Perry runtime helper is calling a panic/abort function inside `firstBytes`. Without debug symbols it's hard to tell which helper, but the likely candidates:

  • Runtime NaN-box unbox assertions (`unbox_buffer_ptr` / `unbox_uint8array_ptr`)
  • GC write-barrier or arena-bounds assertion
  • A type-check failure on the Uint8Array receiver

Worth running with `PERRY_DEBUG_SYMBOLS=1` and a fresh lldb to localize.

Fix direction

Unknown without further investigation — this isn't my area. Possible angles:

  • Some per-function state in codegen is leaking across function boundaries and causing the second function to look up a stale slot
  • The Uint8Array fast path at `expr.rs:4685-4699` has a latent issue that only surfaces after a Buffer fast path in the same compilation
  • A runtime registry is getting corrupted by the first call's arena operations

Impact

Related

Environment

  • Perry 0.5.179 on macOS arm64 (also reproducible on `main` at commit 6e57861)

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