Missing range check in constructors
Description
Some bigfield
constructors do not range check their arguments appropriately. For example, consider the following bigfield
constructor:
// we assume the limbs have already been normalized!
bigfield(const field_t<Builder>& a,
const field_t<Builder>& b,
const field_t<Builder>& c,
const field_t<Builder>& d,
const bool can_overflow = false)
{
context = a.context;
binary_basis_limbs[0] = Limb(field_t(a));
binary_basis_limbs[1] = Limb(field_t(b));
binary_basis_limbs[2] = Limb(field_t(c));
binary_basis_limbs[3] =
Limb(field_t(d), can_overflow ? DEFAULT_MAXIMUM_LIMB : DEFAULT_MAXIMUM_MOST_SIGNIFICANT_LIMB);
prime_basis_limb =
(binary_basis_limbs[3].element * shift_3)
.add_two(binary_basis_limbs[2].element * shift_2, binary_basis_limbs[1].element * shift_1);
prime_basis_limb += (binary_basis_limbs[0].element);
};
It takes as input four values in the native field and creates four Limb
objects from those values. However, the relevant constructor for the Limb
object is implemented as follows:
Limb(const field_t<Builder>& input, const uint256_t max = uint256_t(0))
: element(input)
{
if (input.witness_index == IS_CONSTANT) {
maximum_value = uint256_t(input.additive_constant) + 1;
} else if (max != uint256_t(0)) {
maximum_value = max;
} else {
maximum_value = DEFAULT_MAXIMUM_LIMB;
}
}
It assigns the value to the element
field but does not check for the value being in the correct range that is indicated by maximum_value
.
The same issue occurs with the bigfield
constructor taking in an additional argument prime_limb
.
Impact
All further computations are based on the crucial assumption that the limb elements are less than or equal to the maximum_value
stored alongside them. Violation of that assumption can, for example, cause overflows on later use, impacting soundness by allowing to prove for example incorrect products of bigfield
elements.
Recommendations
Implement the appropriate range check in the constructor of Limb
or in the bigfield
constructors. Alternatively, specify clearly that the constructors assume that the arguments have already been range checked to lie in specific ranges.