Three defects in the published @ruvector/rvf-wasm@0.1.9 microkernel, found while building an independent browser RVF viewer (rvQR) against the C-ABI. All reproduced directly against pkg/rvf_wasm_bg.wasm with a genuine 2304-byte container from rvf-cli (24 vectors, dim 16, 4 segments).
1. rvf_verify_checksum never detects corruption (most serious)
pristine container -> 0 (success)
one payload byte flipped -> 0 (success)
2304 bytes random noise -> 0 (success)
empty buffer -> -1 (error)
It succeeds on everything except an empty buffer. A verification primitive that always returns success is worse than an absent one, because callers reasonably surface it as "integrity verified" — a false guarantee, and this is a security-relevant API.
Note the container also carries no reference CRC to compare against: I checked all 64 header bytes of every segment. So it isn't clear the function can do what its name promises with the current segment format. Either give segment headers a stored checksum and verify against it, or rename/remove the export so nobody builds a trust decision on it.
2. rvf_witness_count returns -1 for every non-zero length
rvf_witness_count(68) -> -1
rvf_witness_count(0) -> 0
The demo container has a real 68-byte WITNESS segment. The chain cannot be counted or verified through the wasm API.
3. rvf_store_open transposes count/dim, and opens anything
store_open(valid container) -> handle 1, count=16, dim=24 (container is 24 vectors x dim 16)
store_open(random noise) -> handle 2 (no error)
The count/dim swap is confirmed by arithmetic: the Vec segment is 1734 bytes = 6 + 24 x (8 + 16 x 4), which only works for 24 vectors of dimension 16. Because the fields are swapped, rvf_store_query strides at the wrong record size and returns ids that are fragments of vector data rather than real ids.
Separately, store_open returns a valid-looking handle for arbitrary bytes — random noise and the wasm binary itself — so a successful open must not be treated as validation by any caller.
Reproduction
const w = (await WebAssembly.instantiate(fs.readFileSync('pkg/rvf_wasm_bg.wasm'), {})).instance.exports;
const put = b => { const p = w.rvf_alloc(b.length); new Uint8Array(w.memory.buffer, p, b.length).set(b); return p; };
const p = put(bytes);
w.rvf_verify_checksum(p, bytes.length); // 0 for anything non-empty
const h = w.rvf_store_open(p, bytes.length);
w.rvf_store_count(h); w.rvf_store_dimension(h); // transposed
(The kernel takes zero imports, so {} is the whole instantiation contract — pleasant to work with otherwise.)
Impact / workaround in the meantime
rvQR now surfaces all three as unavailable/warn rather than as passing checks, reads the Vec segment directly for count/dim/search instead of trusting store_open, and displays the kernel-vs-direct disagreement rather than silently picking a winner. Happy to send that probe harness if useful.
Also worth noting for the ADR-009 discussion in #775: the segment walker itself is correct — segment_count/segment_info enumerate all 4 segments accurately and account for every one of the 2304 bytes.
🤖 Generated with claude-flow
Three defects in the published
@ruvector/rvf-wasm@0.1.9microkernel, found while building an independent browser RVF viewer (rvQR) against the C-ABI. All reproduced directly againstpkg/rvf_wasm_bg.wasmwith a genuine 2304-byte container fromrvf-cli(24 vectors, dim 16, 4 segments).1.
rvf_verify_checksumnever detects corruption (most serious)It succeeds on everything except an empty buffer. A verification primitive that always returns success is worse than an absent one, because callers reasonably surface it as "integrity verified" — a false guarantee, and this is a security-relevant API.
Note the container also carries no reference CRC to compare against: I checked all 64 header bytes of every segment. So it isn't clear the function can do what its name promises with the current segment format. Either give segment headers a stored checksum and verify against it, or rename/remove the export so nobody builds a trust decision on it.
2.
rvf_witness_countreturns -1 for every non-zero lengthThe demo container has a real 68-byte WITNESS segment. The chain cannot be counted or verified through the wasm API.
3.
rvf_store_opentransposes count/dim, and opens anythingThe count/dim swap is confirmed by arithmetic: the Vec segment is 1734 bytes = 6 + 24 x (8 + 16 x 4), which only works for 24 vectors of dimension 16. Because the fields are swapped,
rvf_store_querystrides at the wrong record size and returns ids that are fragments of vector data rather than real ids.Separately,
store_openreturns a valid-looking handle for arbitrary bytes — random noise and the wasm binary itself — so a successful open must not be treated as validation by any caller.Reproduction
(The kernel takes zero imports, so
{}is the whole instantiation contract — pleasant to work with otherwise.)Impact / workaround in the meantime
rvQR now surfaces all three as
unavailable/warnrather than as passing checks, reads the Vec segment directly for count/dim/search instead of trustingstore_open, and displays the kernel-vs-direct disagreement rather than silently picking a winner. Happy to send that probe harness if useful.Also worth noting for the ADR-009 discussion in #775: the segment walker itself is correct —
segment_count/segment_infoenumerate all 4 segments accurately and account for every one of the 2304 bytes.🤖 Generated with claude-flow