2026-10-14

This month

Arguzz: Fuzzing zkVMs with Product Programs and a Simulated Malicious Prover

Produced by the ZK Security research pipeline. Published only after the automated quality gate. The gate is described in the editorial policy.

"Arguzz: Testing zkVMs for Soundness and Completeness Bugs" is by Christoph Hochrainer (TU Wien), Valentin Wüstholz

Arguzz: Fuzzing zkVMs with Product Programs and a Simulated Malicious Prover

"Arguzz: Testing zkVMs for Soundness and Completeness Bugs" is by Christoph Hochrainer (TU Wien), Valentin Wüstholz (Diligence Security) and Maria Christakis (TU Wien). It was posted to arXiv as 2509.10819 on 2025-09-13. Christakis's publications page lists it in the Proceedings of the 35th USENIX Security Symposium (Security '26), and the camera-ready PDF is hosted there. The authors describe Arguzz as the first automated tool for testing zkVMs for soundness and completeness bugs. They ran it against RISC Zero, Nexus, Jolt, SP1, OpenVM and Pico and found eleven bugs in three of them.

Two bug classes, two oracles

Soundness bugs mean accepting an invalid execution (constraints too weak); completeness bugs mean rejecting a valid one (constraints often too strict). Arguzz combines two techniques.

Metamorphic testing via product programs. Arguzz builds on CIRCUZZ (Hochrainer, Isychev, Wüstholz, Christakis; CCS 2025), which fuzzed DSL pipelines such as Circom, gnark and Noir. It generates a random circuit in the CIRCIL intermediate language and rewrites it with stacked semantics-preserving rules such as commutativity, distributivity and De Morgan's laws. Rules specific to field arithmetic are dropped. Each variant is translated to a Rust function. Arguzz extends CIRCIL with inline RISC-V assembly so that rare instructions such as mulhsu get exercised. The novel step is that, instead of running each variant separately, Arguzz merges them into one product program. That program calls every variant, compares the results, and returns a fixed SUCCESS value (0xC0FFEE) or OOPS. The expected output is therefore always known. Proving cost is also lower because trace padding to a power of two is paid once rather than once per program.

Fault injection as a malicious prover. Threat model: only the verifier is trusted. Metamorphic testing cannot detect a weak constraint when every variant behaves the same on an honest VM. So Arguzz patches the zkVM's execution stage to corrupt a single instruction at a chosen step, for example by changing the opcode, the output value or a register operand. The fault then "ripples" through the trace along normal data dependencies. The trace is consistent up to the fault and consistent again with the faulty state afterwards, leaving only a small local inconsistency for the constraints to catch. A scheduler aims injections at the least-injected instructions seen in the honest trace, which spreads coverage across the instruction set.

A soundness bug is reported only when the product program's output flips from SUCCESS to OOPS and the unmodified verifier accepts the proof.

What it found

Fuzzing started on RISC Zero and Nexus in March 2025. SP1 was added in May 2025, and Jolt, OpenVM and Pico in June 2025. All runs used each project's main branch. The results were eleven previously unknown bugs: three soundness bugs, all found by fault injection, and eight completeness bugs, all found by metamorphic testing. No bugs were reported in SP1, OpenVM or Pico.

# zkVM Type Description (per the paper)
1 RISC Zero soundness Missing constraint in three-register instructions
2 RISC Zero completeness Off-by-one in cycle counting
3 Nexus soundness Unconstrained store operand in sw/sh/sb
4 Nexus completeness Out-of-bounds panic from memory-size misestimation (fixed in 0.3.1)
5 Nexus completeness Carry overflow in the multiplication extension
6 Jolt (v0.1.0) soundness Unconstrained immediate operand in lui
7–11 Jolt (v0.2.0) completeness RAM sizing, high-address bytecode panic, Dory commitment failing on traces under 256 cycles, sumcheck failures for mulhsu and inline div/rem

RISC Zero bug 1 is the most serious. In the paper's worked example, a fault swaps the divisor register of remu for the dividend, and a missing constraint lets a malicious prover prove that 7 % 5 = 0. RISC Zero's advisory GHSA-g3qg-6746-3mg9 (CVE-2025-52484, published 2025-06-18) describes the bug as confusing the VM "into treating the value of the rs1 register as the same as the rs2 register." It affects every three-register instruction in risc0-zkvm 2.0.0–2.0.2. The circuit fix landed in zirgen PR #238 (merged 2025-05-23), titled "Fix to remove extra register read when both source registers are the same." The risc0 update landed in PR #3181 (also merged 2025-05-23). The advisory rates the bug Critical, credits Hochrainer via HackenProof, and says to upgrade to risc0-zkvm 2.1.0 (risc0-circuit-rv32im 2.0.4). Affected on-chain verifiers were disabled via estop; Verifier Router users need no action. The paper reports a $50,000 bounty for bug 1 and $1,000 for bug 2. Nexus and Jolt had no bounty programs.

Nexus bug 3: the lower bits of the register holding the store value were not constrained, so a prover could change them without detection. Jolt bug 6: the lui immediate was unconstrained, so a prover controlled the instruction's output. While testing RISC Zero, Arguzz also hit a Rust 1.80 miscompilation of a boolean expression.

Efficiency

Bug refinding used five 24-hour, 4-CPU campaigns against the original buggy versions. Arguzz rediscovered six of ten bugs (1, 3, 5, 6, 9, 10), including all three soundness bugs, with a median time of 13 hours. Bug 11 was excluded: the preprint says it was unfixed (a Jolt fix, PR #898, was merged and then reverted), and the conference version says it "was fixed after the paper-submission deadline." Turning off fault injection raised throughput by 25.6% (OpenVM) to 98.4% (Pico). The inline-assembly extension raised instruction coverage by between 15.0% (Nexus) and 45.2% (Jolt). On the fixed VMs, between 41.7% (SP1) and 100% (Pico) of instruction-modification injections changed the program output.

Limits

  • Program and input generation is blackbox and random. The only feedback is lightweight instruction coverage for the injection scheduler. The authors leave coverage feedback tailored to zkVMs to future work.
  • Four completeness bugs (2, 4, 7, 8) take days to rediscover because they need narrow conditions to trigger.
  • Fault-injection hooks are written by hand for each VM. Only the instruction-modification type exists in all six VMs, and it found every soundness bug. OpenVM, Pico, Nexus and RISC Zero also have VM-specific injection types.
  • Only RISC-V zkVMs are covered. The authors plan more complex, stateful programs and other ISAs.
  • A run with no findings does not establish soundness; it covers only the perturbations its injection types express.

What it means for zkVM and circuit teams

RISC Zero had been audited before bug 1. Even so, Arguzz's soundness bugs would have been found "with only a few hours of fuzzing," according to the authors. The general method carries over to any proof system with a trusted verifier: test the verifier against a deliberately misbehaving prover, and use programs whose correct output is known in advance so you can tell when it has been fooled. The tool is open source, and the artifact is archived at doi:10.5281/zenodo.16939845.

Timeline

  • 2025-03: Fuzzing of RISC Zero and Nexus begins.
  • 2025-05-23: zirgen #238 and risc0 #3181 merged (RISC Zero bug 1).
  • 2025-06-18: GHSA-g3qg-6746-3mg9 / CVE-2025-52484 published.
  • 2025-08-20: Jolt PR #898 (div/rem) merged; the preprint says it was later reverted.
  • 2025-09-13: arXiv 2509.10819v1 submitted.

Sources

Related guides: under-constrained circuits, auditing circom, choosing a proving system, trusted setup risk, the checklist.