scroll-tech / scroll-tech/ceno
circuit under-constraint in opcode-level bugs
@spherel is already working on this.
Since Jun 4, 2026.
- Dominant language
- Rust
- Stars
- 153
- Forks
- 43
- Avg merge
- 3d 13h
- Merged PRs (30d)
- 4
Description
Summary
Credited to Shankara Pailoor and Veridise https://github.com/scroll-tech/ceno/issues/883#issuecomment-4165050346
cite to original report ceno bugs
This issue highlights several soundness bugs and constraint inconsistencies identified across multiple opcode circuits. These issues may allow a malicious prover to satisfy constraints while violating intended semantic bounds.
v1 in Goldilocks Circuit: Missing Outflow Range Assumption
Issue
There is a general pattern in opcode circuits that rely on inflow/outflow-style constraints. These implicitly assume that outflow ∈ [0, 2^32).
Reference (assumption):
https://github.com/scroll-tech/ceno/blob/e33cca0738ac6e9dba1266c50ddad7d8ef8a7515/ceno_zkvm/src/instructions/riscv/shift/shift_circuit.rs#L74
However, the actual constraint enforcing outflow is implemented differently:
This does not guarantee that outflow ≥ 0 or that it is bounded within [0, 2^32).
Impact
A malicious prover can set:
outflow ≥ 2^32
while still satisfying the constraint by compensating with a small positive pow2_rs2_low5.
This breaks the intended soundness assumption.
v2 in BabyBear circuit: Limb Multiplication Overflow
Issue
In the BabyBear field, special care is required when multiplying limbs that may exceed u16.
This constraint can be interpreted as:
carry_low[i] * 2^16 = limb - rd_low[i]
Problem
carry_low[i]can be up to 18 bits- LHS becomes up to
2^(18+16)bits, exceeding the BabyBear field modulus
Impact
- The RHS is no longer canonical in the field
- Multiple values can satisfy the equation
- This introduces ambiguity in
rd_low[i], leading to multiple valid witness assignments
Incorrect RW Flag in ECALL
Issue
All ECALL argument 0 (arg0) are marked as read-write = true generically:
Impact
This allows unintended mutation of the pointer value.
A malicious prover can:
- Manipulate
arg0 - Redirect it to arbitrary memory locations after the operation
This breaks memory safety assumptions.
missing u8 range check for opcode not leverage lookup
Issue
There are based config which assume external range check has done by caller
e.g.
However caller also missed range check
https://github.com/scroll-tech/ceno/blob/e33cca0738ac6e9dba1266c50ddad7d8ef8a7515/ceno_zkvm/src/instructions/riscv/shift/shift_circuit_v2.rs#L295-L296
Impact
Missing u8 lookup range check lead to non-canonical decomposition of a 16 bits number, which malicious could change opcode behaviours
Issue
In the BabyBear field, special care is required when multiplying limbs that may exceed u16.
This constraint can be interpreted as:
carry_low[i] * 2^16 = limb - rd_low[i]
Problem
carry_low[i]can be up to 18 bits- LHS becomes up to
2^(18+16)bits, exceeding the BabyBear field modulus
Impact
- The RHS is no longer canonical in the field
- Multiple values can satisfy the equation
- This introduces ambiguity in
rd_low[i], leading to multiple valid witness assignments
Conclusion
These issues collectively introduce soundness risks:
- Missing range constraints
- Field overflow leading to non-uniqueness
- Incorrect memory access permissions
Each should be addressed to restore constraint correctness and prevent adversarial witness construction.
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Assessment
This issue has not been assessed yet.