-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Description
Hi,
Currently we have 2 instances of operations on uninitialized values, in the cmov implementation.
all the _cmov
implementations read from the resulting variable(ie r->n[0] & mask0
) so if r
points to uninitialized values this is undefined behavior [1] [2]
Unlike the memcpy
debate, actual user operations and conditions on uninit values are assumed to not happen in the compiler and are used for optimizations (especially in clang)
Instance 1:
In ecdsa_sign, if noncefp
returns 0 then we get to:
Lines 517 to 518 in 41fc785
secp256k1_scalar_cmov(&r, &secp256k1_scalar_zero, !ret); | |
secp256k1_scalar_cmov(&s, &secp256k1_scalar_zero, !ret); |
with both
r
and s
being unitialized.Instance 2:
In ecdh, we pass
&tmpa
here: secp256k1/src/ecmult_const_impl.h
Line 186 in 41fc785
ECMULT_CONST_TABLE_GET_GE(&tmpa, pre_a, i, WINDOW_A); |
ECMULT_CONST_TABLE_GET_GE
in an uninit state, which become the r
value in the macro.Then in:
secp256k1/src/ecmult_const_impl.h
Lines 31 to 32 in 41fc785
secp256k1_fe_cmov(&(r)->x, &(pre)[m].x, m == idx_n); \ | |
secp256k1_fe_cmov(&(r)->y, &(pre)[m].y, m == idx_n); \ |
we cmov into
&(r)->x
which will perform operations on uninit values.notice that under
VERIFY
we actually do clear those before:secp256k1/src/ecmult_const_impl.h
Lines 26 to 27 in 41fc785
VERIFY_SETUP(secp256k1_fe_clear(&(r)->x)); \ | |
VERIFY_SETUP(secp256k1_fe_clear(&(r)->y)); \ |
Possible solutions:
The only solution I can currently think of is default initializing those arguments.
Mitigations:
We could add VALGRIND_CHECK_MEM_IS_DEFINED(r, sizeof(*r));
to all the cmovs but we only enable valgrind(-DVALGRIND
) under tests, which use the VERIFY macro too, so that will not catch the second case.
[1] http://www.open-std.org/jtc1/sc22/wg14/www/docs/dr_451.htm
[2] https://markshroyer.com/2012/06/c-both-true-and-false/