Skip to content

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

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

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions