Skip to content

buffer+len native param: Uint8Array view over an ArrayBuffer resolves to the wrong data pointer (silent corruption; standalone arrays fine) #6515

Closed

Description

@proggeramlug

Symptom

When a buffer+len native-library param receives a Uint8Array view over an existing ArrayBuffer, the lowered data_ptr points at the wrong memory. The length resolves correctly, so the callee happily reads/writes len bytes — of the wrong buffer. No error, no null: silent data corruption.

A Uint8Array that owns its storage (new Uint8Array(64)) works correctly.

Repro

Native side (any perry-ext-*-style wrapper), manifest "params": ["i64", "number", "number", "buffer+len"]:

#[no_mangle]
pub unsafe extern "C" fn js_demo_fill(_h: i64, _off: f64, _size: f64, out: *const u8, out_len: usize) -> f64 {
    // fill `out_len` bytes with a known pattern
    std::ptr::write_bytes(out as *mut u8, 0xAB, out_len);
    out_len as f64
}

TypeScript:

// A) standalone Uint8Array — WORKS
const standalone = new Uint8Array(64);
demoFill(h, 0, 64, standalone);
console.log(standalone[0]);           // 0xAB ✓

// B) view over an ArrayBuffer — SILENTLY WRONG
const ab = new ArrayBuffer(64);
const view = new Uint8Array(ab);
demoFill(h, 0, 64, view);
console.log(view[0]);                 // 0 ✗ (callee reported 64 bytes written)

Observed with the real @perryts/webgpu binding (js_webgpu_buffer_read_mapped_into, js_webgpu_queue_write_buffer):

p8 standalone out: n= 64 bytes: 11 22 33 44 55   (expect 11 22 33 44 55)   ✓
p8 view out:       n= 64 bytes: 0 0 0 0 0                                   ✗

Note n=64 in both cases — js_native_abi_check_buffer_byte_len gets the view's length right, so it's specifically js_native_abi_check_buffer_data_ptr that mis-resolves the view (presumably resolving the view object rather than buffer + byteOffset, or failing the registered-buffer lookup and falling back to a bogus pointer).

Why it matters

Views are the normal shape for GPU/binary work — new Uint8Array(f32.buffer, off, len) is how you hand float data to a byte-oriented API, and it's what every WebGPU/three.js-style upload path produces. The failure is silent and far from its cause: we chased "why is my render black" for hours before it reduced to this.

Suggested: js_native_abi_check_buffer_data_ptr should resolve views to storage + byteOffset; if a view genuinely can't be resolved, return null so the callee's null-check fires instead of writing to the wrong place.

Found while bringing up browser-style WebGPU (three.js WebGPURenderer target) on perry. Related: #6486.

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