Skip to content

Large Buffer as function parameter corrupted when Buffer.alloc() runs inside callee #42

@proggeramlug

Description

@proggeramlug

Repro

function copy(src: Buffer, n: number): Buffer {
  console.log('  entered copy, src[0]=', src[0]);
  const dst = Buffer.alloc(n);
  console.log('  dst alloc ok, dst.length=', dst.length);
  for (let i = 0; i < n; i++) dst[i] = src[i];
  console.log('  fill done, dst[0]=', dst[0]);
  return dst;
}
const N = 25 * 1024 * 1024;
const buf = Buffer.alloc(N);
for (let i = 0; i < N; i++) buf[i] = i & 0xff;
console.log('before call, buf[0]=', buf[0], 'buf.length=', buf.length);
const out = copy(buf, N);
console.log('after call, out[0]=', out[0]);

Expected

before call, buf[0]= 0 buf.length= 26214400
  entered copy, src[0]= 0
  dst alloc ok, dst.length= 26214400
  fill done, dst[0]= 0
after call, out[0]= 0

Actual (Perry 0.5.30)

before call, buf[0]= 0 buf.length= 26214400
  entered copy, src[0]= 0.0000…0007949928895127363
  dst alloc ok, dst.length= 26214400
(exit=0, no further output)

Observations

  1. src[0] inside the function reads as a tiny denormal float, not 0. That's a NaN-boxed pointer-bit-pattern being interpreted as a double — src has lost its Buffer-ness at the function boundary.
  2. Buffer.alloc(n) inside copy() succeeds.
  3. The subsequent bracket-write loop silently corrupts the process. No SIGSEGV, no stderr, just exit=0 before 'fill done' prints.

Hypothesis

Param src lands in a callee-saved register that the conservative stack scan can't see. The Buffer.alloc(n) inside the body crosses the GC threshold, sweeps src, and either the for-loop reads the freed header or the write corrupts adjacent state.

Workaround (used in honest_bench/workloads/3_image_convolution/perry/image_conv.ts)

Hoist both buffers to module-level const globals. v0.5.28 registers module globals as explicit GC roots, so they survive collection.

Scale

  • At ~25 MB (4K RGB image size) reproduces reliably.
  • Smaller buffers work — haven't bisected the exact threshold.

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