Files
alkblobs/fuzz
glm-5.3-flash 9134d4444d feat(hashing): the git-object derivation family lands — review-002 C-1's implementation half (wave 13)
The hash registry extends per ADR-018: bytes 0x03/0x04 (git-object
sha-1/sha-256), preimage-identity hashing over git's loose-object form
as the stored preimage — the digest byte-identical to git's oid, every
kind included. Re-derived from poc-o-git-objects, ADR-stamped, not
merged.

- Registry + Algorithm members after the blob members (total order
  rides the registry bytes, 0x02 < 0x03 < 0x04); the derivation arms
  (derive's len parameter inert for the family — documented,
  test-pinned); the BlobHasher no-preamble arms (finish pinned equal
  to derive); from_git_oid_hex's contract pinned blob-family-only.
- keys.rs: the extended physical layout round-trips in total order;
  rejects-never-guesses unchanged (0x05 is the unknown-tag example).
- put path: put_git_object(algorithm, len, stream) public — family
  members only (KeyInvalid otherwise), known-length only, the
  inherited Verification/Io mappings — via put_known_as; the
  unknown-length drivers take their derivation from callers.
- fleet threading (ADR-018 §7): every hasher site
  derivation-parameterized (put_known_as; fleet_put_pglo_known/
  _unknown); put_git_object under a fleet declaration lands through
  the joint entry+pin tx / pg-lo consolidation / pin-first staged
  forms — pinned by two env-driven tests with exact-count sweep
  outcomes (kv joint form byte-exact + dedup row-alone; consolidation
  form with LO pages landing and dying exactly).
- git_interop: the family tests green against the real git CLI
  (tree/commit/tag byte-exact, dedup, the blob-tag negative, the
  sha-256 sibling), the seam guard (blob algorithms reject), the
  preimage-length stat assertion; in-module empty-tree known-answer
  vectors.
- Conformance: the INV-1/2/6 reference rows gain family legs
  (round-trip incl. the staged form, dedup-by-oid, the stored-preimage
  stat semantic); backlog pins + README inventory updated.
- kind-decision delta (ADR-018 §2): behavior unchanged, texts honest —
  the ops module doc's digest-wire-form paragraph, digest_ingress's
  doc, the specs' headers.
- fuzz: the key_encoding generator + shared battery extended to the
  four-member registry (family keys, known-answer vectors, the len
  inertness pin, 0x05 as the invalid tag); committed corpus
  regenerated per policy (0x03 was an invalid-tag seed — 117 rewritten,
  67 added); idempotent after landing.

Verification: cargo test (default, 153+54+7+2), cargo test
--all-features server-less (skip-clean) and against the live pglo-poc
harness (336+125+7+3), cargo clippy --all-targets both feature
variants (-D warnings), cargo fmt --check, cargo doc --no-deps, Cargo
test --manifest-path fuzz/shared/Cargo.toml (the corpus-replay gate).
Task: tasks/c1-git-object-family-impl.md (status completed, notes +
summary recorded).
2026-10-18 00:00:46 +00:00
..

alkblobs fuzzing

libFuzzer targets for alkblobs' parse surfaces (see docs/plans/implementation.md §Wave 11 for the family pattern and target inventory).

Layout

  • fuzz_targets/ — one binary per target; thin fuzz_target! wrappers.
  • shared/ — the invariant logic, as a plain library so normal cargo test (stable toolchain) can replay the committed corpora through the same invariants (fuzz/shared/src/*.rs #[cfg(test)] modules; quinn's CI pattern). The fuzz binaries are nightly-only; the shared crate is stable-clean. It consumes alkblobs' public API only and must never become a dependency of the root crate.
  • corpus/<target>/ — committed hand-made seeds (regenerate with python3 fuzz/gen_fuzz_seeds.py from the repo root — must be a no-op diff on the committed set) plus promoted campaign crash inputs (seed-900+ numbering). Only seed-* files are tracked; grown corpora (SHA-named units), artifacts, and coverage output are gitignored and are not merged back into the committed seeds.
  • run-detached.sh — mandatory runner for agent sessions: wraps cargo fuzz run in setsid + nohup + log redirection so an OOM in a target can never take down the agent host.
  • json.dict — JSON token dictionary for the structured targets.
  • gen_fuzz_seeds.py — deterministic seed generator (quiche pattern).

Targets

Target Drives Invariants
key_encoding Key::from_bytes / Key::as_bytes (src/keys.rs), Digest::from_git_oid_hex, Algorithm::try_from_byte / byte / digest_len, Key::derive / Digest::derive / Algorithm::derive (src/hashing.rs) — entire public API no-panic on any byte sequence and any string (lossy-decoded and arbitrary-structured alike); every from_bytes failure exactly Error::KeyInvalid, every from_git_oid_hex failure exactly Error::KeyInvalid (rejects, never guesses — truncated, over-long, reserved/unknown tags, tag/digest-length mismatches, wrong-length, uppercase and non-hex hex); round-trip closure (accepted keys re-encode byte-identically, derived keys decode back); double-invocation determinism on both parsers; the derived Ord sort equals the as_bytes byte sort over arbitrary accepted-key collections with reparses staying accepted (ADR-002's sort-stability clause); hex posture: lowercase-only acceptance, length selects the algorithm (40 → sha-1, 64 → sha-256, every other length rejected), decoded length == digest_len; registry agreement (byte(try_from_byte(b)) == b for every accepted byte, reserved 0x00 and all unknown bytes reject — full byte-space sweep); derive determinism with the git-computed known-answer vectors pinned
ops_ingress fetch_ranges_ingress + FetchRange/RangedBounds (the ranged-fetch bounds ingress, review-001 S-2 — via the #[doc(hidden)] fuzz/test re-exports of the exact production function), PinToken::from_hex (the renewal token ingress, review-001 S-5, src/ops/tokens.rs) ranges: no-panic on any Value (raw-byte serde_json parses plus arbitrary-derived JsonValues); the absent-field contract (no ranges field → Ok(None), the whole-form fetch); item-shape rejects (non-array ranges; items missing offset/len or carrying non-u64 shapes — negatives, floats, strings, nulls, nested) → Err with the stable domain text, never a panic, never Ok; bound arithmetic (checked_add overflow of the running sum → Err; a sum over max_total_bytes → Err carrying the wire numbers; a sum within the bound → Ok(Some) with every range's (offset, len) preserved exactly and in order); boundary exactness (total == bound accepts, bound + 1 rejects); the count bound is not this ingress's duty (an over-count array within the bytes bound parses Ok — the maxItems gate lives at the schema, alkcall's; pinned honestly, not asserted away); determinism. tokens: no-panic on any string (lossy-decoded and arbitrary-derived alike); length-before-decode (any length ≠ 64 → None, the 1 MiB canary pins the rejection); non-hex 64-char → None; case-insensitive decode agreement with the wire schema's pattern (upper == lower == Some, same bytes); round-trip closure (to_hex ∘ from_hex on accepted tokens); determinism

Commands

cargo fuzz build and cargo fuzz run must be executed with fuzz/ (or deeper) as the working directory so rustup selects the pinned nightly toolchain — the detached runner handles that itself.

# build (nightly, pinned by rust-toolchain.toml inside fuzz/; run from fuzz/)
cargo fuzz build

# agent sessions: detached campaign (never foreground; CWD-independent)
FUZZ_RUNTIME_SECS=600 fuzz/run-detached.sh <target> [extra libfuzzer args...]

# corpus replay through the invariants (stable toolchain, no nightly)
cargo test --manifest-path fuzz/shared/Cargo.toml

# coverage
cargo fuzz coverage <target>

The fuzz workspace is excluded from the main workspace (exclude in the root Cargo.toml); it pins its own nightly toolchain via rust-toolchain.toml and does not affect the root workspace's stable toolchain or MSRV.