Motivation
#66 proposes ASan + cargo-audit. ASan catches runtime memory errors (use-after-free, OOB), but it does not catch the bug classes that qjson's documented invariants are most exposed to:
get_str pointer invalidation — qjson_get_str / qjson_cursor_get_str hand back a (ptr,len) into the input buffer or doc.scratch; any later *_get_str on the same doc may invalidate it (scratch reuse). This is an aliasing / use-after-reuse contract, not necessarily a heap UAF ASan would flag.
'a → 'static transmute in qjson_parse — lifetime erasure that relies on the caller pinning the buffer. Provenance/lifetime misuse here is UB that Miri models directly.
RefCell aliasing for scratch / skip, and the unsafe extern "C" surface in src/ffi.rs.
These are exactly the things Miri (Stacked/Tree Borrows, pointer provenance, UB in unsafe) is built to detect, and valgrind over the end-to-end Lua path (dlopen → FFI → interleaved get_str) catches at the integration level the way lua-cjson's prove (valgrind) job does.
This issue is complementary to #66 (ASan), not a duplicate — different tools, different bug classes.
Proposed work
1. Miri job (nightly, non-blocking)
- run: rustup +nightly component add miri
- run: cargo +nightly miri test --test ffi_smoke --test ffi_cursor --test ffi_strings
- Prioritize the FFI integration tests and especially
fuzz/fuzz_targets/fuzz_ffi_ops.rs-style interleaved op sequences (a small deterministic harness driving alternating get_str calls to assert the pointer-invalidation contract).
- Some intrinsics (AVX2/NEON) are unsupported under Miri → run with
--no-default-features (scalar scanner) so Miri exercises the safe-vs-unsafe logic without SIMD intrinsics.
2. valgrind over the Lua busted suite (Linux)
- run: |
LD_LIBRARY_PATH="$PWD/target/release" \
valgrind --error-exitcode=1 --leak-check=full \
luajit -e 'require("busted.runner")({...})' ... tests/lua
Mirrors lua-cjson's valgrind gate but over qjson's real deployment path (LuaJIT FFI + dlopen). Catches leaks/invalid reads that only manifest across the FFI boundary.
Tradeoffs
- Miri is slow and can't run SIMD intrinsics → scalar-only, subset of tests, nightly, non-blocking.
- valgrind under LuaJIT needs
--suppressions for LuaJIT's own allocator quirks; expect to commit a suppression file.
Affected files
.github/workflows/ci.yml
- possibly
valgrind.supp (new), a small deterministic FFI-ops test harness
Relates to #66.
Motivation
#66 proposes ASan + cargo-audit. ASan catches runtime memory errors (use-after-free, OOB), but it does not catch the bug classes that qjson's documented invariants are most exposed to:
get_strpointer invalidation —qjson_get_str/qjson_cursor_get_strhand back a(ptr,len)into the input buffer ordoc.scratch; any later*_get_stron the same doc may invalidate it (scratch reuse). This is an aliasing / use-after-reuse contract, not necessarily a heap UAF ASan would flag.'a→'statictransmute inqjson_parse— lifetime erasure that relies on the caller pinning the buffer. Provenance/lifetime misuse here is UB that Miri models directly.RefCellaliasing forscratch/skip, and theunsafe extern "C"surface insrc/ffi.rs.These are exactly the things Miri (Stacked/Tree Borrows, pointer provenance, UB in
unsafe) is built to detect, and valgrind over the end-to-end Lua path (dlopen → FFI → interleavedget_str) catches at the integration level the way lua-cjson'sprove (valgrind)job does.This issue is complementary to #66 (ASan), not a duplicate — different tools, different bug classes.
Proposed work
1. Miri job (nightly, non-blocking)
fuzz/fuzz_targets/fuzz_ffi_ops.rs-style interleaved op sequences (a small deterministic harness driving alternatingget_strcalls to assert the pointer-invalidation contract).--no-default-features(scalar scanner) so Miri exercises the safe-vs-unsafe logic without SIMD intrinsics.2. valgrind over the Lua busted suite (Linux)
Mirrors lua-cjson's valgrind gate but over qjson's real deployment path (LuaJIT FFI + dlopen). Catches leaks/invalid reads that only manifest across the FFI boundary.
Tradeoffs
--suppressionsfor LuaJIT's own allocator quirks; expect to commit a suppression file.Affected files
.github/workflows/ci.ymlvalgrind.supp(new), a small deterministic FFI-ops test harnessRelates to #66.