Background
qjson already has an FFI operation fuzz target: fuzz/fuzz_targets/fuzz_ffi_ops.rs.
This target is valuable because it drives the public FFI surface with randomized operation sequences instead of only fuzzing JSON parsing. It can exercise parse/get/cursor/object-entry/free interleavings, error paths, null handling, scratch-buffer reuse, and document/cursor lifetime behavior.
The current CI fuzz guard runs the parse-oriented targets, but does not run fuzz_ffi_ops:
fuzz_parse_eager
fuzz_depth
fuzz_parse_lazy
That leaves an existing FFI lifecycle fuzz target outside the PR regression guard.
Goal
Add the existing fuzz_ffi_ops target to the PR-length fuzz guard so CI continuously checks randomized FFI operation sequences.
This should improve confidence around the most fragile boundary in qjson: the Rust/LuaJIT FFI API and the lifecycle rules around documents, cursors, strings, and frees.
Why This Matters
Before this change, CI mainly checks that input JSON parsing is stable under fuzzing. That is useful, but it does not fully cover how callers use the FFI API after parsing.
The FFI surface has additional risks that parse-only fuzzing is unlikely to catch:
- document and cursor lifetime mistakes
qjson_free interleavings
- invalid or missing paths
- null pointer handling
- cursor field/index traversal edge cases
- object entry access edge cases
get_str scratch-buffer reuse behavior
- panic-barrier and error-code behavior under unusual call sequences
After this change, PR CI should also exercise randomized sequences of FFI calls against generated JSON inputs. This gives a cheap regression guard for lifecycle bugs without introducing a heavier sanitizer or Valgrind requirement into the normal PR path.
Proposed Scope
- Add
fuzz_ffi_ops to the existing CI fuzz job.
- Use the same PR-length budget style as the current fuzz targets, for example:
cargo +nightly fuzz run fuzz_ffi_ops -- -max_total_time=60
- Keep the command easy to run locally as well as in CI.
- Update
CONTRIBUTING.md so the local PR-length fuzz command list includes fuzz_ffi_ops.
- Preserve the existing fuzz targets and their current behavior.
Non-Goals
- Do not add a new fuzz target in this issue; the target already exists.
- Do not expand PR fuzzing into a long-running fuzz campaign.
- Do not increase the default PR fuzz budget beyond the current short regression-guard style.
- Do not make ASan, Valgrind, or other heavier memory tools blocking as part of this issue.
- Do not change the existing Miri, sanitizer, Lua, OpenResty, or package validation jobs.
- Do not refactor the workflow structure unless it is necessary to add this command cleanly.
Acceptance Criteria
- CI runs
fuzz_ffi_ops as part of the PR fuzz guard.
- A crash, panic, assertion failure, or libFuzzer failure from
fuzz_ffi_ops fails the CI job.
- Existing fuzz targets still run.
CONTRIBUTING.md documents the local command for fuzz_ffi_ops alongside the other PR-length fuzz commands.
- The change stays focused on wiring the existing target into CI and local documentation.
Suggested Implementation Notes
The likely implementation is small:
- Add one command to the existing fuzz section in
.github/workflows/ci.yml.
- Add the matching local command to the fuzzing section in
CONTRIBUTING.md.
This keeps local and CI behavior aligned: developers can reproduce the same PR-length fuzz guard before pushing, while CI remains the final enforcement point.
Background
qjsonalready has an FFI operation fuzz target:fuzz/fuzz_targets/fuzz_ffi_ops.rs.This target is valuable because it drives the public FFI surface with randomized operation sequences instead of only fuzzing JSON parsing. It can exercise parse/get/cursor/object-entry/free interleavings, error paths, null handling, scratch-buffer reuse, and document/cursor lifetime behavior.
The current CI fuzz guard runs the parse-oriented targets, but does not run
fuzz_ffi_ops:fuzz_parse_eagerfuzz_depthfuzz_parse_lazyThat leaves an existing FFI lifecycle fuzz target outside the PR regression guard.
Goal
Add the existing
fuzz_ffi_opstarget to the PR-length fuzz guard so CI continuously checks randomized FFI operation sequences.This should improve confidence around the most fragile boundary in
qjson: the Rust/LuaJIT FFI API and the lifecycle rules around documents, cursors, strings, and frees.Why This Matters
Before this change, CI mainly checks that input JSON parsing is stable under fuzzing. That is useful, but it does not fully cover how callers use the FFI API after parsing.
The FFI surface has additional risks that parse-only fuzzing is unlikely to catch:
qjson_freeinterleavingsget_strscratch-buffer reuse behaviorAfter this change, PR CI should also exercise randomized sequences of FFI calls against generated JSON inputs. This gives a cheap regression guard for lifecycle bugs without introducing a heavier sanitizer or Valgrind requirement into the normal PR path.
Proposed Scope
fuzz_ffi_opsto the existing CI fuzz job.CONTRIBUTING.mdso the local PR-length fuzz command list includesfuzz_ffi_ops.Non-Goals
Acceptance Criteria
fuzz_ffi_opsas part of the PR fuzz guard.fuzz_ffi_opsfails the CI job.CONTRIBUTING.mddocuments the local command forfuzz_ffi_opsalongside the other PR-length fuzz commands.Suggested Implementation Notes
The likely implementation is small:
.github/workflows/ci.yml.CONTRIBUTING.md.This keeps local and CI behavior aligned: developers can reproduce the same PR-length fuzz guard before pushing, while CI remains the final enforcement point.