Unsafe Comparison
Detects comparison operators used in unconstrained assignments, producing prover-controlled boolean values with no verifier check.
Unsafe Comparison
Overview
Remediation Guide: How to Fix Unsafe Comparisons
The unsafe comparison detector flags uses of <, >, <=, >=, ==, and != inside the right-hand side of an unconstrained assignment (<--). Comparison operators are not native R1CS operations: the constraint system works over a large prime field where “less than” has no meaningful algebraic encoding. Comparisons must be implemented by decomposing operands into binary bits and enforcing bit-range constraints, which is what the helper templates in circomlib (LessThan, LessEqThan, IsEqual) do.
When a developer writes result <-- a < b, the witness generator evaluates the comparison in native integer arithmetic and stores the boolean in result. No constraint records the relationship, so the prover is free to return the opposite value without affecting proof validity.
Why This Is an Issue
Comparisons inside witness assignments are the single most common source of soundness bugs in Circom circuits. The syntax reads like a check; the runtime behaves like a hint. The gap between the two is exactly where provers can lie.
The order comparisons (<, >, <=, >=) are the most dangerous because there is no straightforward way to “undo” the mistake without also adding bit decomposition. Equality comparisons (==, !=) are less severe because the IsEqual and IsZero components provide a drop-in replacement, but they are still unsound when used in <-- form.
The impact depends on what the boolean gates. A comparison used as part of a range check can allow out-of-range values through a proof. A comparison used as an authorization flag can allow unauthorized actions to be proven valid.
How to Resolve
Replace the assignment with a call to a comparator template from circomlib, and constrain the output via <== (or read it via <== into a parent signal).
// Vulnerable: comparison inside <--
template Authorize() {
signal input amount;
signal input limit;
signal output ok;
ok <-- amount <= limit; // prover picks ok freely
}
// Fixed: LessEqThan component with bit decomposition
include "circomlib/circuits/comparators.circom";
template Authorize() {
signal input amount;
signal input limit;
signal output ok;
component cmp = LessEqThan(64); // bit width must cover both operands
cmp.in[0] <== amount;
cmp.in[1] <== limit;
ok <== cmp.out;
}
Choose the bit width carefully: it must be large enough to hold both operands but not so large that the decomposition dominates proving cost.
Sample Sigvex Output
{
"detector": "unsafe-comparison",
"severity": "critical",
"confidence": 0.95,
"title": "Unsafe comparison `<=` in template `Authorize`",
"template": "Authorize",
"line": 6,
"description": "The assignment `ok <-- amount <= limit` uses a comparison operator inside an unconstrained assignment. The prover controls the result because no R1CS constraint enforces the comparison.",
"recommendation": "Replace with a `LessEqThan` component from circomlib and read its output via `<==`."
}
Detection Methodology
For each template assignment, the detector checks whether the assignment type is <-- and whether the right-hand expression contains one of the comparison operators. It takes care to skip the Circom operators that contain a comparison character as a substring: <== (constraining assignment), <-- (unconstrained assignment), === (constraint-only), and ==> (not used but defensively excluded). Order comparisons (<, >, <=, >=) are reported at Critical severity; equality and inequality (==, !=) are reported at High severity.
Limitations
- Only syntactic uses of comparison operators inside
<--are detected. Comparisons hidden inside function calls or macros are not analyzed. - The detector does not verify bit width when a
LessThancomponent is used correctly; choosing too small a width is a separate class of bug. - Comparisons inside conditional expressions that ultimately feed a
<==are currently treated as safe, even though the conditional itself may still require explicit constraints.
Related Detectors
- Range Check — missing bit-range constraints on signals
- Nondeterministic Control — prover-controlled branches
- Under-Constrained Signal — signals lacking any binding