ruvnet / ruvnet/RuVector

rvf-wasm: verify_checksum always succeeds, witness_count always fails, store_open transposes count/dim and accepts anything

Open
#776 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Rust
Stars
4.5k
Forks
603
Avg merge
23h 32m
Merged PRs (30d)
59

Description

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

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the rvf_verify_checksum, rvf_witness_count, rvf_store_open, rvf_store_count, and rvf_store_dimension exports in pkg/rvf_wasm_bg.wasm and reproduce the results using the supplied JavaScript harness and 2304-byte container. Compare the exports with the segment format and C-ABI expectations. Done means checksum behavior and witness counting are defined and correct, count/dimension are not transposed, and invalid input is rejected or explicitly documented.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust, wasm
Domain
backend-api-design, databases
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
45/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.