Summary
OpenVmHalo2Verifier.verify could accept a malformed proof without running the generated Halo2 fallback verifier.
The wrapper constructed raw proof calldata and then called address(this) with that calldata. Because the same contract exposed other external selectors, attacker-controlled proof bytes at calldata offset zero could dispatch to a non-verifier function that returned successfully.
Details
OpenVmHalo2Verifier.verify constructs the raw calldata expected by the generated snark-verifier Halo2 fallback verifier, then performs a staticcall to itself and treats a successful call as proof acceptance.
The beginning of that self-call calldata is copied from user-controlled proofData. If the first four bytes matched another selector exposed by the wrapper contract, the EVM selector dispatch could call that function instead of falling through to the generated Halo2 verifier. A successful non-verifier call would make verify return success without checking the proof suffix, accumulator, app commitments, or public values.
The fix removes the extra public getter selector and adds a defensive check that the first four proof bytes are zero before the self-call. For the expected proof layout, those bytes are part of the first KZG accumulator limb and must be zero.
Impact
Applications using the generated Solidity verifier wrapper could accept arbitrary app commitments and public values with malformed proof data. This bypasses the intended Halo2 proof verification path.
Summary
OpenVmHalo2Verifier.verifycould accept a malformed proof without running the generated Halo2 fallback verifier.The wrapper constructed raw proof calldata and then called
address(this)with that calldata. Because the same contract exposed other external selectors, attacker-controlled proof bytes at calldata offset zero could dispatch to a non-verifier function that returned successfully.Details
OpenVmHalo2Verifier.verifyconstructs the raw calldata expected by the generatedsnark-verifierHalo2 fallback verifier, then performs astaticcallto itself and treats a successful call as proof acceptance.The beginning of that self-call calldata is copied from user-controlled
proofData. If the first four bytes matched another selector exposed by the wrapper contract, the EVM selector dispatch could call that function instead of falling through to the generated Halo2 verifier. A successful non-verifier call would makeverifyreturn success without checking the proof suffix, accumulator, app commitments, or public values.The fix removes the extra public getter selector and adds a defensive check that the first four proof bytes are zero before the self-call. For the expected proof layout, those bytes are part of the first KZG accumulator limb and must be zero.
Impact
Applications using the generated Solidity verifier wrapper could accept arbitrary app commitments and public values with malformed proof data. This bypasses the intended Halo2 proof verification path.