# Additional airs can be added to proof in recursion program

OpenVM recursion verifier indexes per_air advice by proof-supplied num_airs, allowing extra AIR proofs to inject unobserved exposed values and forge the cumulative sum check.

- Payout: Contest reward
- Severity: high
- Program: OpenVM
- Credited: cergyk
- Disclosed: 2025-03-24
- Write-up quality: 8/10
- Original: https://cdn.cantina.xyz/reports/cantina_competition_openvm_january2025.pdf#3.1.17
- Library page: https://bugbountylibrary.com/r/cantina%3Aopenvm_january2025%3A3.1.17
- Tags: domain:web3 impact:integrity kind:contest platform:cantina sev:high tech:rust technique:source-review vuln:business-logic vuln:crypto

**Target:** recursion program / STARK verifier (stark/mod.rs)
**Root cause:** Verifier iterates over num_airs taken from the proof rather than the keygen advice per_air length; out-of-bounds builder.get on m_advice_var.per_air returns undefined data instead of failing, and extra exposed_values_after_challenge are folded into the cumulative sum without being observed by the challenger.
**Steps:**
1. Enable dynamic config (builder.flags.static_only == false).
2. Submit a proof with more per_air proofs than chips used at keygen (num_airs > m_advice_var.per_air.len()).
3. In assert_cumulative_sums, loop over air_proofs.len() adds the extra exposed_values_after_challenge term to cumulative_sum.
4. Ensure the extra air_advice.num_exposed_values_after_challenge is zero so the challenger never observes the injected value.
5. Choose the injected term to cancel the cumulative sum, passing sumcheck despite non-cancelling interactions.
**How found:** Code review of the recursion DSL: noticed num_airs derived from proof length is used to index m_advice_var.per_air, which is sized by keygen chips, and that the OOB access does not fail; then traced how the extra exposed value flows into assert_cumulative_sums while the challenger observation loop skips it when num_exposed_values_after_challenge is zero.
**Exploitation:** Prover supplies additional AIR proofs beyond keygen chips; the OOB advice read yields undefined-but-non-failing data, and the extra exposed value is added to the cumulative sum while remaining unobserved by the challenger, letting the prover satisfy the sumcheck with non-cancelling interactions.
**Insight:** In recursive/DSL verifiers, any loop bound taken from prover-supplied data (num_airs) rather than the verification key is suspect; check whether OOB builder.get silently returns undefined and whether the extra element reaches a sum/commitment that the challenger never observes.
