# Unconstrained proof flags in circuits

Prover-supplied flags and boundary columns not pinned to row data or inputs

_Area: Smart contract access control and control flow. Seen in 10 disclosed reports; latest 2026-05-20. Updated 2026-10-23._

## How it works
A circuit proves execution by constraining every witness column to the public input and to neighboring rows. The root cause here is a column or flag that the prover supplies freely: it is boolean-checked or used in one arithmetic expression, but nothing ties it to the actual data. Because the prover chooses it, they can pick the value that makes a false statement verify. The assumption that breaks is "every witness value is determined by the inputs"; in these reports it was not.

## Where it shows up
- Boundary rows (first/last block, digest rows) where a constraint is conditionally skipped [E1](https://bugbountylibrary.com/r/cantina%3Aopenvm_january2025%3A3.1.4.md)
- Selector bits for sign extension or branch choice used only in one addition plus a bool check [E2](https://bugbountylibrary.com/r/cantina%3Aopenvm_january2025%3A3.1.8.md)[E7](https://bugbountylibrary.com/r/cantina%3Aopenvm_january2025%3A3.1.14.md)
- Flags that gate a lookup or permutation connection; zeroing the flag removes the constraint [E3](https://bugbountylibrary.com/r/cantina%3Aopenvm_january2025%3A3.1.12.md)
- Multiplicity expressions of the form `valid - flag` where the flag is not forced to imply valid [E4](https://bugbountylibrary.com/r/cantina%3Aopenvm_january2025%3A3.1.15.md)
- Bus-interaction enable flags only `assert_bool`'d, not tied to the row-validity predicate [E5](https://bugbountylibrary.com/r/cantina%3Aopenvm_january2025%3A3.1.9.md)
- Phase-transition constraints written as one-way implications [E6](https://bugbountylibrary.com/r/cantina%3Aopenvm_january2025%3A3.1.10.md)
- "Exactly once" flags never constrained to be zero elsewhere or to occur a single time [E8](https://bugbountylibrary.com/r/cantina%3Aopenvm_january2025%3A3.1.20.md)

## How to review
For each prover-supplied column, ask: what forces this value? The invariant is that every column read by a chip, bus, or hash must be constrained on every row it is consumed, and every selector flag must be pinned to the underlying data or input. Check that:
- Boundary rows are constrained, not skipped [E1](https://bugbountylibrary.com/r/cantina%3Aopenvm_january2025%3A3.1.4.md)
- Selector bits equal the actual data bit they represent [E2](https://bugbountylibrary.com/r/cantina%3Aopenvm_january2025%3A3.1.8.md)[E7](https://bugbountylibrary.com/r/cantina%3Aopenvm_january2025%3A3.1.14.md)
- A gating flag implies the row-validity predicate, so it cannot be zeroed to skip a connection [E3](https://bugbountylibrary.com/r/cantina%3Aopenvm_january2025%3A3.1.12.md)[E5](https://bugbountylibrary.com/r/cantina%3Aopenvm_january2025%3A3.1.9.md)
- Multiplicities cannot go negative; any subtracted flag implies valid [E4](https://bugbountylibrary.com/r/cantina%3Aopenvm_january2025%3A3.1.15.md)
- Phase transitions exclude every alternative row type, not just imply one [E6](https://bugbountylibrary.com/r/cantina%3Aopenvm_january2025%3A3.1.10.md)
- Exactly-once flags are constrained zero on non-matching rows [E8](https://bugbountylibrary.com/r/cantina%3Aopenvm_january2025%3A3.1.20.md)

## Variants seen
- Unconstrained final digest column on last block [E1](https://bugbountylibrary.com/r/cantina%3Aopenvm_january2025%3A3.1.4.md)
- Unconstrained sign bit redirecting memory access [E2](https://bugbountylibrary.com/r/cantina%3Aopenvm_january2025%3A3.1.8.md) or program flow [E7](https://bugbountylibrary.com/r/cantina%3Aopenvm_january2025%3A3.1.14.md)
- Missing first-row flag enabling arbitrary memory writes [E3](https://bugbountylibrary.com/r/cantina%3Aopenvm_january2025%3A3.1.12.md)
- Negative lookup multiplicity forging range checks [E4](https://bugbountylibrary.com/r/cantina%3Aopenvm_january2025%3A3.1.15.md)
- Bus interaction from an invalid row [E5](https://bugbountylibrary.com/r/cantina%3Aopenvm_january2025%3A3.1.9.md)
- One-way phase implication skipping hashing [E6](https://bugbountylibrary.com/r/cantina%3Aopenvm_january2025%3A3.1.10.md)
- Selector set mid-process altering hash inputs [E8](https://bugbountylibrary.com/r/cantina%3Aopenvm_january2025%3A3.1.20.md)

## Impact and severity
Each case let a malicious prover prove false execution: forged digests, redirected memory, arbitrary writes, out-of-range limbs, bypassed row hashing. Severity is critical because soundness of the whole zkVM fails; a single unconstrained column is enough.

## Why fixes fail
Partial fixes that only bool-check a flag, or constrain one direction of an implication, leave the other path open [E5](https://bugbountylibrary.com/r/cantina%3Aopenvm_january2025%3A3.1.9.md)[E6](https://bugbountylibrary.com/r/cantina%3Aopenvm_january2025%3A3.1.10.md)[E8](https://bugbountylibrary.com/r/cantina%3Aopenvm_january2025%3A3.1.20.md).

## Evidence
- E1: https://bugbountylibrary.com/r/cantina%3Aopenvm_january2025%3A3.1.4.md
- E2: https://bugbountylibrary.com/r/cantina%3Aopenvm_january2025%3A3.1.8.md
- E3: https://bugbountylibrary.com/r/cantina%3Aopenvm_january2025%3A3.1.12.md
- E4: https://bugbountylibrary.com/r/cantina%3Aopenvm_january2025%3A3.1.15.md
- E5: https://bugbountylibrary.com/r/cantina%3Aopenvm_january2025%3A3.1.9.md
- E6: https://bugbountylibrary.com/r/cantina%3Aopenvm_january2025%3A3.1.10.md
- E7: https://bugbountylibrary.com/r/cantina%3Aopenvm_january2025%3A3.1.14.md
- E8: https://bugbountylibrary.com/r/cantina%3Aopenvm_january2025%3A3.1.20.md
