# Poseidon2 verify_batch opcode can send to execution bus for invalid row

Poseidon2 AIR left end_top_level unconstrained, letting a malicious prover emit a VERIFY_BATCH execution-bus interaction from an invalid row, bypassing row constraints.

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

**Target:** zkVM Poseidon2 chip AIR constraints (air.rs)
**Root cause:** end_top_level is only assert_bool'd, not tied to enabled (incorporate_row+incorporate_sibling+inside_row+simple), so the VERIFY_BATCH execution interaction can be emitted from a row that isn't a valid top-level row.
**Steps:**
1. Read poseidon2/air.rs#L102-L106: enabled = incorporate_row+incorporate_sibling+inside_row+simple, both enabled and end_top_level only assert_bool.
2. Note execution_bridge.execute_and_increment_pc for VERIFY_BATCH is gated on end_top_level (L340-L359) with no constraint linking it to enabled.
3. Construct a witness row with end_top_level=1 but incorporate_row/incorporate_sibling=0, bypassing their constraints while still emitting the execution interaction.
4. Also note a row can be simple and end_top_level simultaneously since they emit at different timestamps (start vs very_first).
5. Fix: constrain end_top_level => incorporate_row+incorporate_sibling (commit 6b1d8f14).
**How found:** Code review of the Poseidon2 AIR: noticed enabled and end_top_level are both only boolean-constrained, then traced that the execution interaction is gated on end_top_level alone, revealing the missing implication constraint.
**Exploitation:** Malicious prover crafts a Poseidon2 trace row that emits a VERIFY_BATCH execution-bus event without satisfying the corresponding row constraints, making the opcode malleable and producing a proof the verifier accepts for an invalid execution.
**Insight:** In multi-opcode AIRs, every flag gating a bus interaction must be constrained to the row-validity predicate; check that each interaction's enable flag implies the chip's enabled condition, not just assert_bool.
