CVE-2025-46723 Overview
OpenVM is a modular zero-knowledge virtual machine (zkVM) framework designed for customization and extensibility. Version 1.0.0 contains a soundness vulnerability in the Add Upper Immediate to Program Counter (AUIPC) chip caused by an incorrect range check on the program counter byte decomposition. A typo in the enumeration logic causes the highest limb of pc to be range checked to 8 bits instead of 6 bits. A malicious prover can exploit this gap to overflow the BabyBear field and force the destination register to take a value that differs from what the AUIPC instruction specifies. The maintainers patched the issue in version 1.1.0.
Critical Impact
A malicious prover can produce valid proofs for incorrect AUIPC execution results, breaking the soundness guarantees of the zkVM and corrupting the integrity of any computation that relies on AUIPC semantics.
Affected Products
- OpenVM zkVM framework version 1.0.0
- The rv32im circuit extension containing the AUIPC chip implementation
- Downstream applications depending on OpenVM proof soundness prior to v1.1.0
Discovery Timeline
- 2025-05-02 - CVE-2025-46723 published to NVD
- 2026-04-15 - Last updated in NVD database
Technical Details for CVE-2025-46723
Vulnerability Analysis
The vulnerability resides in the AUIPC chip constraint logic within extensions/rv32im/circuit/src/auipc/core.rs. The AUIPC RISC-V instruction adds an immediate value to the program counter and writes the result to a destination register. To enforce that the program counter fits within the expected bit width, the circuit decomposes pc into limbs and range checks each limb. The most significant limb must be constrained tightly because RV32 program counters are bounded below 2^32, and the top limb carries fewer significant bits than the lower limbs.
The flaw is a classic off-by-one in iterator indexing [CWE-131]. The code used pc_limbs.iter().skip(1).enumerate(), which yields indices i = 0, 1, 2 over the skipped slice rather than i = 1, 2, 3 over the original array. The conditional if i == pc_limbs.len() - 1 never evaluates true, so the tight 6-bit range check on pc_limbs[3] is never emitted. That limb remains range checked at 8 bits through the default path.
Root Cause
The miscalculated allocation size of bits for the top limb [CWE-131] means the decomposition is permitted to take values outside the intended range. A prover can choose pc_limbs such that the reconstructed value differs from the true pc modulo the BabyBear prime field. Because the decomposition is only weakly bound, the prover controls what value of pc the AUIPC computation effectively uses.
Attack Vector
A malicious prover crafts a witness in which pc_limbs[3] exceeds the 6-bit ceiling. The decomposition still satisfies the 8-bit range check but no longer corresponds to the actual program counter. When the AUIPC chip computes rd = pc + imm, the destination register receives a forged result. The verifier accepts the proof, allowing arbitrary divergence between proven and actual execution.
need_range_check.push(limb.into());
}
+ assert_eq!(pc_limbs.len(), RV32_REGISTER_NUM_LIMBS);
// pc_limbs[0] is already range checked through rd_data[0]
- for (i, limb) in pc_limbs.iter().skip(1).enumerate() {
+ for (i, limb) in pc_limbs.iter().enumerate().skip(1) {
if i == pc_limbs.len() - 1 {
// Range check the most significant limb of pc to be in [0, 2^{PC_BITS-(RV32_REGISTER_NUM_LIMBS-1)*RV32_CELL_BITS})
need_range_check.push(
Source: OpenVM patch commit 68da4b5
The fix swaps the order of enumerate() and skip(1) so that the index reflects the original limb position, ensuring pc_limbs[3] receives the correct tight range check.
Detection Methods for CVE-2025-46723
Indicators of Compromise
- Proofs generated by an OpenVM v1.0.0 prover that verify successfully but produce execution results inconsistent with the source RISC-V program when re-executed natively.
- AUIPC instruction outputs in which the destination register value does not equal pc + imm for the recorded program counter.
- Witness data containing pc_limbs[3] values greater than 2^6 - 1 for any AUIPC operation.
Detection Strategies
- Audit dependency manifests (Cargo.toml, lockfiles) for OpenVM versions pinned at 1.0.0 or earlier and flag any verifier or prover binaries built against them.
- Re-execute AUIPC operations off-chain using a trusted RISC-V interpreter and compare the destination register output against the proven value.
- Inspect the pc_limbs decomposition in proof traces and reject any limb that exceeds the intended bit width when the verifier supports trace introspection.
Monitoring Recommendations
- Track GitHub Security Advisory GHSA-jf2r-x3j4-23m7 and the OpenVM release feed for follow-on advisories affecting other zkVM chips.
- Log all OpenVM verifier invocations along with the prover identity, proof hash, and OpenVM version reported in the proof metadata.
- Alert on proof submissions originating from clients still advertising OpenVM v1.0.0 in build banners or transcript identifiers.
How to Mitigate CVE-2025-46723
Immediate Actions Required
- Upgrade all OpenVM prover and verifier components to version 1.1.0 or later, as published in the v1.1.0 release notes.
- Invalidate or re-verify any proofs produced by OpenVM v1.0.0 that have been used to authorize state transitions, settlements, or financial operations.
- Pin dependencies and rebuild downstream zkVM applications against the patched crate to prevent silent reintroduction of the vulnerable code path.
Patch Information
The fix is contained in commit 68da4b50c033da5603517064aa0a08e1bbf70a01 and shipped in OpenVM v1.1.0. The patch corrects the iterator chain in extensions/rv32im/circuit/src/auipc/core.rs so pc_limbs[3] is range checked to 6 bits and adds an assert_eq! on the limb count to harden the invariant. Additional context is available in the Cantina findings report.
Workarounds
- If upgrading immediately is not possible, disable AUIPC-bearing programs in the prover pipeline and reject any proof whose execution trace references the AUIPC opcode.
- Implement an external post-verification step that re-executes proofs natively and compares the resulting register state before accepting the proof as authoritative.
# Update OpenVM in a Rust project to the patched release
cargo update -p openvm --precise 1.1.0
cargo tree -i openvm # confirm no transitive dep still pulls 1.0.0
cargo build --release
Disclaimer: This content was generated using AI. While we strive for accuracy, please verify critical information with official sources.

