Zebra Coverage-Guided Fuzzing Infrastructure

Hello everyone,

I am submitting a proposal to Zcash Community Grants (ZCG) to build a coverage-guided fuzzing infrastructure for Zebra, the Zcash Foundation’s Rust consensus node implementation.

This project focuses on systematically testing Zebra’s critical parsing, networking, and cryptographic components against malformed inputs, enabling continuous, automated discovery of security vulnerabilities and edge-case bugs.


Why This Matters

Zebra currently has zero fuzzing coverage. There are no fuzz targets, no cargo-fuzz configuration, and no OSS-Fuzz registration. After NU7, Zebra becomes the sole consensus node for the entire Zcash network. Any exploitable parsing or validation bug could compromise the network’s security and user funds.

For comparison, Bitcoin Core maintains a mature fuzzing infrastructure with over 100 fuzz targets and continuous OSS-Fuzz integration. Zcash, as a privacy-focused cryptocurrency, arguably requires even more rigorous testing, yet has none.


Project Overview

This proposal introduces a complete fuzzing infrastructure for Zebra that includes:

  • Fuzz targets for all major attack surfaces: transaction and block deserialization, P2P protocol parsing, RPC input handling, script and address parsing, note commitment tree operations, and Equihash verification

  • Seed corpora extracted from Zcash mainnet real data

  • CI integration with PR-level smoke fuzzing and nightly extended fuzzing campaigns via GitHub Actions

  • OSS-Fuzz submission, enrolling Zebra in Google’s continuous fuzzing service for 24/7 automated testing

  • Security reporting with structured crash triage, severity classification, reproduction steps, and fix recommendations


Technical Approach

The infrastructure is built on cargo-fuzz with the libFuzzer backend. For complex inputs, the arbitrary crate enables structured fuzzing. All targets run with AddressSanitizer and UndefinedBehaviorSanitizer for maximum bug detection.

Fuzz targets are prioritized by attack surface:

  • P0 (Critical): Transaction deserialization (v1 through v5+), block and header parsing, P2P message parsing

  • P1 (High): RPC input handling, script and address parsing, note commitment tree operations

  • P2 (Medium): Equihash verification, Orchard and Sapling serialization


Deliverables and Milestones

The proposal is structured into three milestones over 6 months:

  1. Core Fuzzing Framework and Initial Targets — zebra-fuzz crate, 3+ fuzz targets for critical deserialization code, seed corpora from mainnet

  2. Extended Targets, Corpus Optimization and CI — 7+ total fuzz targets, corpus minimization, GitHub Actions CI integration, coverage reporting

  3. Security Analysis, OSS-Fuzz and Documentation — crash analysis report, OSS-Fuzz submission PR, documentation for community contributors, upstream PRs to Zebra


Budget

  • Startup Funding: $3,000 (infrastructure setup)

  • Milestones 1 through 3: $27,000 (engineering work)

  • Total: $30,000


Design Principles

  • Standalone: independent crate, no modifications to Zebra core code required

  • Sustainable: OSS-Fuzz integration ensures fuzzing continues indefinitely after project completion

  • Community-oriented: documentation enables anyone to add new fuzz targets as Zebra evolves


Full proposal details are available here:

Thank you for your time and feedback.

7 Likes

I’m supportive of this grant.

7 Likes

Hi all,

I reviewed this proposal and am supportive, with a few concerns I’d like the committee and proposer to address.

What I Like

Right tool, right scope. The technical approach is sound. cargo-fuzz with libFuzzer is the standard Rust fuzzing toolchain, and the phased milestone structure and $30K budget are reasonable for the scope of work.

OSS-Fuzz submission is the highest-value deliverable. If accepted, Google provides 24/7 continuous fuzzing at zero ongoing cost to the ecosystem. This is where the long-term ROI lives.

Concerns

Proposer credibility gap on fuzzing specifically. The proposer’s GitHub shows meaningful Rust experience, but I found no public evidence of fuzzing work. No fuzz targets, no crash triage, no OSS-Fuzz contributions. I also could not locate a GitHub profile for the co-proposer (aic-larry), who is listed as the primary implementer. The proposer appears to be using Claude Code (based on CLAUDE.md in their pinned repo), which is fine for development but makes it harder to evaluate their independent depth on security analysis and crash triage.

OSS-Fuzz acceptance is not guaranteed, and that changes the value equation. Google’s bar for inclusion is high. Projects must have a significant user base or be critical to global IT infrastructure. If Zebra is not accepted, the fuzzing infrastructure still has value, but becomes a material ongoing cost center (CI compute, corpus maintenance, someone to monitor and triage results). I’d recommend ZCG structure payment so that a meaningful portion of milestone 3 funding is gated on OSS-Fuzz submission acceptance, giving the proposers strong incentive to build a compelling case for inclusion.

Crash triage and severity assessment need more detail. Fuzzing safe Rust code will likely surface many panics and edge cases that are not security-critical. These are still worth fixing, but someone needs to distinguish “consensus node crashes on malformed input from an adversarial peer” from “unreachable panic in a rarely-exercised code path.” It’s not clear to me that the team has experience making these determinations in a consensus-node context. I’d like to see the proposer describe their triage framework and, ideally, point to prior experience assessing bug severity.

Recommendations

  1. Request a proof-of-concept: A single working fuzz target against Zebra’s transaction deserialization, before full grant approval. This would materially de-risk the grant.

  2. Require a GitHub profile or portfolio for aic-larry so the committee can vet the primary implementer.

  3. Gate a portion of milestone 3 on OSS-Fuzz acceptance to align incentives with the proposal’s highest-value deliverable. The proposer obviously can’t control Google’s decision here, but the committee can align incentives such that the proposer makes a compelling case for inclusion in the OSS-Fuzz program.

6 Likes

Hi alchemydc, thank you for the thoughtful feedback and support. I want to address your concerns — and share an important update.

Update first: Since posting our proposal, we’ve continued our PoC work and our deep fuzzing methodology has already uncovered a security issue that we’ve reported to the Zcash Foundation through Zebra’s official responsible disclosure process (GitHub Security Advisory, per Zebra’s SECURITY.md). We can’t share details until the disclosure process completes, but we believe this early finding validates both our approach and our team’s ability to deliver real security value.

Now to your specific points:

1. Team experience and aic-larry

aic-larry has been my long-term Rust development partner. We’ve built production cryptocurrency trading systems together — market-making and arbitrage on Binance Futures, Hyperliquid, Lighter, and Uniswap. These are systems handling real capital where bugs mean real losses, so code correctness and security are ingrained in how we work. Due to the commercially sensitive nature of quantitative trading, these projects live in private repositories — standard practice in this industry.

On the AI tooling question (CLAUDE.md in our repo): yes, we use AI assistants as part of our development workflow. We see this as a strength, not a weakness — AI-assisted fuzzing harness design and code path analysis significantly accelerates our work. The security judgment, methodology design, and responsible disclosure process are all human-driven.

Rather than profiles, I’d point to results: our deep fuzzing approach found a reportable security issue within the first day of deploying our new multi-layer harness methodology. That’s what matters.

2. OSS-Fuzz and M3 funding

Fair concern. We propose splitting M3 ($9,000):

  • $6,000 (fixed): Complete OSS-Fuzz integration PR (fuzz targets, Dockerfile, project.yaml), standalone CI fuzzing pipeline, curated seed corpus, and documentation. This is real engineering work regardless of Google’s decision.

  • $3,000 (conditional): Released upon Google’s acceptance.

Either way, we commit to maintaining continuous fuzzing infrastructure. If OSS-Fuzz declines, we’ll run equivalent continuous fuzzing on dedicated servers (cost ~$500/6 months). The Zcash community gets continuous fuzzing regardless — OSS-Fuzz acceptance just means Google subsidizes the compute.

3. Crash triage process

Here’s our workflow, which we’ve now battle-tested with a real finding:

Severity levels:

  • Critical: Remotely exploitable, affects consensus or network availability

  • High: Node crash or denial of service

  • Medium: Non-security logic errors

  • Low: Edge cases not affecting production

Process:

  1. Discovery — Fuzzer flags crash, triggering input automatically preserved

  2. Reproduction — Minimize test case (cargo fuzz tmin), confirm stable reproduction

  3. Classification — Determine severity, analyze attack reachability (can this be triggered remotely? via P2P? via RPC?)

  4. Reporting — Critical/High: private disclosure to ZF security team within 24 hours. Medium/Low: public GitHub issue

  5. Documentation — Each report includes: minimized input, stack trace, impact analysis, reachability assessment, and suggested fix

  6. Fix assistance — Patch suggestions + verification

  7. Disclosure — 90-day responsible disclosure window

We’ve now executed this entire pipeline end-to-end — from fuzzer discovery through reachability analysis to responsible disclosure — on a real finding. Not a hypothetical workflow anymore.

PoC progress:

Beyond the security finding (details embargoed), our Phase 1 numbers:

  • 95.6 million iterations across 6 targets, zero crashes in standard (Layer 1) fuzzing

  • Multi-layer deep fuzzing methodology: we go beyond “does it deserialize without panicking” to test property access, hash computation, consensus checks, and fee calculations on deserialized objects

  • Real mainnet data as seed corpus (1,565 transactions + 473 blocks from a fully-synced Zebra node)

  • Running across multiple machines (2-core GCP + 8-core dedicated server), with up to 52x performance scaling

The key insight: traditional deserialize-only fuzzing (Layer 1) ran 95M+ iterations and found nothing. Our multi-layer approach found a reportable issue within minutes of deployment. This is the methodological contribution we’re bringing.

Looking forward to the committee’s feedback.

4 Likes

Thanks @robustfengbin for the follow-up. I can confirm that the bug that you found and reported responsibly in Zebra was 1) serious and 2) remotely exploitable. Indeed this validates your approach, methodology and triage capabilities. Bravo!

I appreciate your commitment to M3 and doing the work to get GOOG to accept the project, as well as doing 6 months worth of fuzzing independently of acceptance into the GOOG program.

7 Likes

Thank you @alchemydc — this confirmation means a lot, both as validation of our methodology and as encouragement going forward.

The responsible disclosure process with ZF was smooth and professional. From report to hotfix, the turnaround was impressive, and we appreciate the team’s responsiveness.

Looking forward to continuing this collaboration. :folded_hands:

3 Likes

@thejohnnycrypto
Thanks, really appreciate this perspective.

I fully agree that fuzzing coverage on a consensus-critical node like Zebra is essential. The deserialization panic we found is exactly the kind of issue that can hide in edge cases and only surface under adversarial inputs.

Our goal with this proposal is not just to find isolated bugs, but to build a continuous fuzzing and triage pipeline that provides long-term security assurance for the ecosystem.

Really glad to see the community recognizing the importance of this work.

3 Likes

It’s not zero fuzzing! Ziggurat, which I did during my time at Equilibrium Labs fuzzed the network layer and found several critical-but-now-fixed vulns.

cc @olliten @JoakimEQ

10 Likes

Finding a critical bug was an excellent way to market your proposal :laughing: I support this!

4 Likes

@robustfengbin at the most recent meeting, ZCG voted to approve this proposal. Congratulations!

To keep the community informed, ZCG requests that you provide monthly updates via the forum in this thread.

Please check your forum inbox for a direct message from FPF with important next steps, including a link to the Milestone Payment Request Form and your unique validation code for submitting payment requests.

2 Likes

Thank you ZCG for the approval! I’m excited to get started and grateful for the community’s trust.

I’ll begin work on Milestone 1 immediately and provide monthly progress updates in this thread as requested.

Looking forward to delivering meaningful security improvements for Zebra and the Zcash ecosystem.

2 Likes

M1 Month 1 — Coverage detail + disclosure follow-up

Two finer-grained pieces of evidence from the closed Month 1 window (2026-03-30 → 2026-04-30): a per-target coverage breakdown for the five fuzz harnesses, and a short coordinated-disclosure follow-up. M1 Month 2 (May) is the harden + ship window and is in progress; the M1 final delivery report will be posted ahead of the 2026-05-30 sign-off.


1. Coverage snapshot — 5 fuzz targets

Per-target line coverage at the close of Month 1, measured with cargo fuzz coverage against each target’s corpus and aggregated via llvm-cov report.

(Same data in text form for screen readers.)

Target Corpus files Key file Line coverage
block_deserialize 1,494 zebra-chain/src/block/serialize.rs 64.38%
block_deep_fuzz 353 zebra-chain/src/block/serialize.rs 64.38%
p2p_message_parse 283 zebra-network/src/protocol/external/codec.rs 32.85%
p2p_deep_fuzz 344 zebra-network/src/protocol/external/codec.rs 35.48%
addr_message_fuzz 16 zebra-network/src/protocol/external/addr/v1.rs corpus expansion in flight

Block deserialization — coverage on key zebra-chain files:

File Lines Line coverage
zebra-chain/src/transaction/serialize.rs 828 83.45%
zebra-chain/src/sapling/output.rs 123 83.74%
zebra-chain/src/orchard/action.rs 62 87.10%
zebra-chain/src/sapling/spend.rs 147 74.83%
zebra-chain/src/orchard/note/nullifiers.rs 22 77.27%
zebra-chain/src/serialization/compact_size.rs 116 81.03%
zebra-chain/src/serialization/zcash_deserialize.rs 106 57.55%
zebra-chain/src/serialization/zcash_serialize.rs 96 53.12%
zebra-chain/src/serialization/read_zcash.rs 36 50.00%
zebra-chain/src/block/serialize.rs 160 64.38%

A note on block/serialize.rs: the residual ~36% un-covered region is composed mostly of the CountedHeader serialize/deserialize impls (used by P2P “headers”-only messages, not on the block_deserialize fuzz path) and the SerializedBlock conversion impls (the serialize direction; this target only exercises deserialize). These regions are unreachable from Block::zcash_deserialize regardless of corpus or fuzz duration — they need a separate ser-direction target, which is a Month 2 hardening candidate.

The block target reaches deep into transaction-internal deserialization paths because mainnet blocks embed real V5 / V4 transactions. That’s why transaction/serialize.rs, sapling/output.rs, and orchard/action.rs all sit at 80%+ line coverage from the block target alone.


2. Coordinated disclosure update

While preparing M1 Month 2 work, we identified a panic in Zebra’s V6 transaction hash path that was reachable through a recently exercised code path. We submitted the report through the standard GitHub Security Advisory workflow on the Zebra repository. The Zebra Foundation security team triaged the report promptly, then converted it to a public tracking issue with High severity and the appropriate C-bug / S-needs-triage labels. We would like to thank @conradoplg and the Zebra Foundation security team for the rapid and constructive triage — initial submission to public tracking inside one working day.


3. Where this leaves M1 going into the final stretch

Month 2 is the consolidation window:

  • Hardening of the existing targets and corpus seeding for the queued candidates
  • Initial CI smoke scaffold (M2 deliverable preparation)
  • Upstream re-baselining
  • M1 final delivery report ahead of the 2026-05-30 sign-off

All scope and quality remain anchored to the proposal verbatim deliverables and acceptance criteria. Thanks again to ZCG, the Zebra Foundation, and the broader Zcash community for the support and the responsive coordination on disclosure.

2 Likes

Milestone 1 — Final delivery

With the 2026-05-30 sign-off approaching, here is the Milestone 1 close-out for the Zebra Coverage-Guided Fuzzing Infrastructure grant. The full code, seed corpus, and reproduction scripts are now published on GitHub (link below) — this post is the summary; anyone who wants the per-file detail can clone the repository and reproduce everything in a few minutes.

Fuzz targets & coverage

The five Milestone 1 fuzz targets are complete and running under cargo-fuzz (libFuzzer + AddressSanitizer), covering Zebra’s core untrusted-input surface:

  • block_deserialize — block parsing + merkle-root / hash / coinbase-height invariants

  • block_deep_fuzz — 4-layer block fuzz reaching the consensus-validation layer

  • p2p_message_parse / p2p_deep_fuzz — P2P codec framing + multi-message decode

  • addr_message_fuzz — application-layer Addr handling

On the security-critical deserialization surface — the code an attacker reaches with malformed serialized data — region coverage is 77–89% (transaction 85%, transparent 86%, sapling 84%, orchard 89%, nullifiers 77%). Entry/parser layers (the P2P codec, the outer block parser) read lower by nature for decode-oriented fuzzing. Every figure reproduces against the published repository via the included run-coverage.sh, which replays the committed corpus — no long fuzzing run required.

Bugs found & disclosed

During Milestone 1 our coverage-guided fuzzing surfaced a V6 transaction hash panic (issue #10534): a crafted V6 transaction passes deserialization but then panics while computing its transaction ID, on a path reachable from P2P message decode before consensus checks run. It only affects nodes built with the nu7 + tx_v6 feature flags, which are not enabled in production builds, so it is not currently exploitable — we caught it ahead of the NU7 release and coordinated-disclosed it to the Zcash Foundation, where it is tracked as public issue #10534.

Code

The Milestone 1 fuzzing code is published here:

GitHub - robustfengbin/zebra-fuzz-m1: Milestone 1 of ZCG "Zebra Coverage-Guided Fuzzing Infrastructure" — five cargo-fuzz targets, committed seed corpus, coverage-reproduction scripts, built against ZcashFoundation/zebra main · GitHub

It ships the five fuzz targets, the committed seed corpus (16,180 real mainnet-derived inputs, ~134 MB), and the coverage-reproduction scripts, built against ZcashFoundation/zebra main. The repository is deliberately trimmed to the crates needed to build and reproduce the targets — this keeps the deliverable focused and lets reviewers diff the retained Zebra crates against upstream to confirm the only changes are the two fuzzing feature gates. The five targets themselves are not reduced in scope; they span the full core attack surface listed above.

What’s next

Milestone 2 extends to the proposal’s wider target set (RPC interface, script and address parsing, note-commitment trees, Equihash verification) along with corpus optimization and CI integration. A Month 2/3 progress update will follow in this thread.

Thanks again to ZCG, the Zcash Foundation security team, and the community for the continued review and engagement. Happy to answer questions or walk through reproduction.

Hi ZCG team, Zebra maintainers, and Zcash community,

I’m happy to share the delivery for Milestone 2 of the Zebra coverage-guided fuzzing grant.

Milestone 2

Building on Milestone 1, this milestone delivers the second set of coverage-guided

fuzz targets for Zebra, together with continuous-fuzzing infrastructure on GitHub

Actions and a public, auto-updating coverage dashboard.

Deliverables (all complete)

1. RPC interface input handlingrpc_handler_fuzz and jsonrpsee_envelope_fuzz,

exercising zebra-rpc’s methods.rs (34% region coverage).

2. Script & transparent/shielded address parsingscript_verify_fuzz and

script_flag_matrix_fuzz drive transparent-script verification through the zcash_script

C/C++ library via FFI (sanitizer-coverage does not instrument the C side, so these provide

crash detection rather than Rust region coverage); address_fuzz drives

transparent/address.rs (63% region coverage).

3. Note-commitment tree insert/removenote_commitment_tree_fuzz, exercising

orchard/tree.rs (54% region coverage).

4. Equihash solution verificationequihash_fuzz, exercising work/equihash.rs

(65% region coverage).

That is 7 new operational targets, meeting the proposal’s “7+ targets” criterion and

covering the full deliverable family. Combined with Milestone 1’s 5 block/P2P-deserialization

targets, the suite now runs 12 targets in one cumulative repository.

Continuous fuzzing (ClusterFuzzLite on GitHub Actions)

  • Pull-request fuzzing — short code-change runs with AddressSanitizer + SARIF output

  • Batch fuzzing every 6 hours — accumulates newly-discovered inputs into the corpus

  • Daily coverage report + corpus pruning

Coverage

Per-subject-under-test region coverage is reported per source file. (A whole-report total

is not meaningful here — it averages in the Rust standard library and every third-party

dependency, none of which the fuzzers target.) Beyond the headline files above, **90+

Zebra source files are exercised at ≥5% region coverage**. These are seed-corpus baselines

and grow with sustained fuzzing.

No crashes found to date across the 12 targets.

Corpus

The seed corpus is mainnet-derived public data — real blocks, transactions, headers and

P2P messages dumped from a fully-synced Zcash node — plus a small set of hand-constructed

protocol seeds. The CI corpus accumulates and is periodically pruned/minimized.

Repositories (public, fully reproducible)

The vendored crates are upstream Zebra v5.0.0, byte-identical except for two fuzzing-only

cfg gates that expose otherwise-private modules to the harnesses. Anyone can clone, build

and run (cargo +nightly fuzz build/run --fuzz-dir zebra-fuzz/fuzz <target>), and the

coverage report rebuilds automatically every night in CI.

1 Like

Hi ZCG team, Zebra maintainers, and Zcash community,

I’m happy to share the delivery for Milestone 3 of the Zebra coverage-guided fuzzing grant.

Milestone 3 — Final Delivery

Milestone 3 completes the grant. Building on the M1 (block / P2P deserialization) and
M2 (RPC / script / address / note-commitment-tree / Equihash) targets, this milestone:

  1. rebases the entire suite onto the latest Zebra (v6.2.1, NU6.3 “Ironwood”),
  2. adds dedicated fuzzing for the new Ironwood shielded pool, and
  3. packages Zebra for OSS-Fuzz while keeping continuous fuzzing running on GitHub Actions.

The suite now runs 15 targets in one cumulative, reproducible repository, with no
crashes found
across all of them on the latest consensus code.

Rebased onto the latest Zebra (NU6.3 “Ironwood”)

The 12 M1+M2 targets have been moved from v5.0.0 up to v6.2.1, recompiled and
re-run. The continuous-fuzzing infrastructure therefore now exercises the current
consensus code
, including the NU6.3 changes, rather than a frozen older release. The
vendored crates are byte-identical to upstream Zebra except for two fuzzing-only cfg
gates that expose otherwise-private modules to the harnesses.

New: Ironwood shielded-pool fuzzing

NU6.3 introduces the V6 transaction format, the Ironwood shielded pool, and its
note-commitment tree — a brand-new attack surface reachable from untrusted peer input.
Milestone 3 adds three targets that reach it progressively:

  • v6_transaction_fuzz — V6 wire (de)serialization and round-trip. This is the
    highest-value, lowest-level surface: exactly the bytes a peer puts on the wire.
  • v6_transaction_semantic_fuzz — drives the business logic on a decoded
    transaction: Ironwood accessors, value-balance arithmetic (including the consensus
    non-negativity rule), flag validation, and the structural consensus checks — all
    pure fn(&Transaction) -> Result predicates.
  • ironwood_value_balance_codec_fuzz — round-trips the 48-byte Ironwood
    ValueBalance state encoding.

Together these exercise the Ironwood parsing surface plus the semantic / structural
consensus surface
— roughly 70% of the pool’s business methods. The remainder
(note-commitment-tree state wiring, history-tree MMR, and the halo2 proof verification)
requires block-level and async harnesses and is documented as follow-on scope rather
than claimed as covered. All three targets ran to zero crashes.

Corpus for a not-yet-activated format

NU6.3 is not yet active on mainnet, so there is no ready-made corpus of real V6
transactions — and random bytes essentially never land inside the V6 structure. The seed
corpus draws on three sources:

  • A dedicated fake_v6 generator that uses Zebra’s own fake_v6_transaction /
    fake_v6_orchard_shielded_data builders to synthesize diverse Ironwood shapes
    (bundle presence, flag combinations, value-balance and action-count variations), each
    self-checked by round-trip.
  • The M1+M2 corpus — ~68,000 mainnet-derived seeds (blocks, transactions, headers,
    P2P messages) carried forward.
  • Real testnet samples — NU6.3 is already active on testnet; an isolated node has
    synced past the activation height, and ~200 de-duplicated genuine Ironwood V6
    transactions (carrying real Orchard proofs and actions) have been harvested into the
    corpus. Because they carry real proofs, these samples exercise branches the synthetic
    seeds cannot reach.

Together the synthetic and real seeds drive execution deep into the Ironwood branches —
the synthetic set for breadth of shapes, the testnet samples for real-proof depth —
confirming the new targets genuinely reach the new code rather than bouncing off the wire
format.

Continuous fuzzing (GitHub Actions)

ClusterFuzzLite continues to run on GitHub Actions — pull-request runs, six-hourly batch
runs that grow the corpus, and a daily coverage report with corpus pruning. This baseline
keeps Zebra under continuous, latest-code fuzzing regardless of the OSS-Fuzz outcome
below
.

OSS-Fuzz submission

As the final M3 deliverable, the project is packaged for OSS-Fuzz (projects/zebra/
with Dockerfile, build script and project config). No Zcash-ecosystem project is
currently on OSS-Fuzz
, so this would be the first — putting Zebra on Google’s fuzzing
infrastructure with automated triage, bisection and 24/7 runs. Zebra has been submitted as a pull request to google/oss-fuzz (#15900).

If acceptance requires sponsorship from an established maintainer, we are glad to
coordinate with the Zebra / Zcash Foundation team, and the fuzzing project can live in or
be integrated with the official infrastructure. Either way — accepted upstream or not —
the GitHub Actions setup above guarantees continuous fuzzing, and we intend to maintain
and iterate on this fuzzing project on an ongoing basis
.

Coverage

Per-subject-under-test region coverage is reported per source file (a whole-report total
is not meaningful — it averages in the Rust standard library and third-party dependencies
the fuzzers do not target). The report rebuilds automatically every day in CI.

  • Ironwood transaction — de/serialization (transaction/serialize.rs): 80.9%
  • Ironwood transaction — semantic / consensus (zebra-chain/transaction.rs): 69.3%
  • Ironwood value-balance codec (value_balance.rs): 41.7%
  • Ironwood shielded data (orchard/action.rs 88.7%, orchard/shielded_data.rs
    48.6%) and note-commitment tree (orchard/tree.rs 53.7%)
  • (M1+M2 headline files carried forward: block/P2P deserialization, RPC, script,
    address, note-commitment tree, Equihash — see the repository dashboard.)

No crashes found to date across all 15 targets.

Repositories (public, fully reproducible)

Clone, build and run with cargo +nightly fuzz build/run --fuzz-dir zebra-fuzz/fuzz <target>; the coverage report rebuilds nightly in CI.

Hi ZCG team, Zebra maintainers, and Zcash community,

Monthly update — 2026-07-23 (M3 delivery) → 2026-09-10. Apologies for the gap in this
thread: the work continued through August, the write-up didn’t. Back to monthly from here.

The harnesses now live in ZcashFoundation/zebra

OSS-Fuzz’s review said the project looked good but needed maintainer buy-in, and that the
harnesses should live upstream so maintenance ownership is clear. That is now done:

  • Merged into ZcashFoundation/zebra on 2026-08-17 under zebra-fuzz/
    (#11221) — 15 cargo-fuzz targets,
    30 files, +12,455 lines, 0 deletions, reviewed and approved by a Zebra maintainer.
  • Seed corpora now live in ZcashFoundation/zebra-fuzz-corpora,
    a Foundation repository the Zebra team created and populated.
  • The OSS-Fuzz PR points at the Foundationmain_repo is ZcashFoundation/zebra,
    primary_contact is security@zfnd.org, at their request.
  • The only change to existing crates is an off-by-default fuzzing feature on
    zebra-network and zebra-consensus: 16 lines, 0 deletions, no new dependencies.

A fuzzing suite in a contributor’s repository decays the moment the contributor stops
paying attention; one in the upstream tree is the maintainers’ to keep.

What the review changed — with thanks to @alchemydc

The most valuable findings this window came from the review, not from me. An automated pass
raised 17 items; @alchemydc’s pass then assessed the harnesses for defects found per
CPU-hour
rather than correctness and produced seven must-fix items, all landed 2026-08-14:

  • A sha256d checksum barrier was starving three P2P targets — their compute went into one
    hash per input, then the input was discarded. Fixed:

    target before after change
    p2p_message_parse 1,708 2,943 +72%
    p2p_deep_fuzz 1,793 4,277 +139%
    addr_message_fuzz 1,085 2,178 +101%
  • Two oracles that could not fail, replaced with checks that can.

  • An Equihash probe whose parameters were rejected before reaching the verifier.

  • A script_verify_fuzz oracle that existed only in debug builds, not the one OSS-Fuzz uses.

  • Four wrong dictionary constants; every constant in all four dictionaries re-derived.

  • Seed corpora moved out of Zebra’s tree; per-input setup work removed from four targets.

None were crashes found. They are the fuzzer’s aim being corrected — which decides whether
the continuous compute that follows is worth anything. Thank you, DC.

The backlog is upstream, and other people are working it

The review also produced #11261,
ten further improvements. Since then: another contributor, john-lawniczak, found the
fuzz crate no longer built on main after the zcash_primitives transaction refactor, and
a maintainer invited the repair —
#11394, all 15 targets building.
Reading the same drift, I reported five assertions that no longer test what they claim: two
weakened by the refactor, three that never tested anything from the day they were
introduced — those three are mine.
I’m also proposing a CI job upstream, since nothing in
Zebra’s CI compiles zebra-fuzz today, which is why that break went unnoticed for a week.
#11066 — three caller-contract
panics from the fuzzing, none reachable from untrusted input — is still open awaiting a
maintainer preference on the fix.

Continuous fuzzing

Still running on GitHub Actions: per-PR runs, six-hourly batch runs, daily coverage report
with corpus pruning. No crashes across the 15 targets. To be precise: those runs build
from the vendored snapshot in robustfengbin/zebra-fuzz-m3 pinned at 2026-07-31, not from
Zebra’s main — the upstream copy is not under continuous fuzzing yet, which is what the
OSS-Fuzz submission is for. The snapshot does include NU6.3, so the three Ironwood targets
now run against the format that activated on mainnet on 2026-07-28.

Where the OSS-Fuzz submission stands

google/oss-fuzz#15900 is still open, and
it is not waiting on what it was in July. Maintainer buy-in is in place, @alchemydc
confirmed the Foundation’s side on 2026-08-18, and the OSS-Fuzz reviewer came back on
2026-09-05 with a single one-line request.

The blocker is upstream and ours to clear, not Google’s. The build clones
ZcashFoundation/zebra at HEAD, so the API drift above turns two build configurations red.
The repair (#11394) needs to land on main; it was waiting on #11387, which merged
2026-09-08. Once it is in, the PR goes green in one change.

Acceptance itself is the $3,000 conditional portion of Milestone 3 and that call does
belong to Google; the fixed M3 deliverables were complete at delivery on 2026-07-23. If
accepted, this would be the first Zcash-ecosystem project on OSS-Fuzz. Either way, the
GitHub Actions setup keeps Zebra under continuous fuzzing and I intend to keep maintaining
these harnesses.

Links