A compiler may add struct padding and fe_cmov is not guaranteed to preserve it.
On the way, we restore the name of the function. It was mistakenly renamed in 6173839c90553385171d560be8a17cbe167e3bef using "search and replace".
A compiler may add struct padding and fe_cmov is not guaranteed to preserve it.
On the way, we restore the name of the function. It was mistakenly renamed in 6173839c90553385171d560be8a17cbe167e3bef using "search and replace".
2458 | - t.normalized = a->normalized; 2459 | -#endif 2460 | - return secp256k1_memcmp_var(a, &t, sizeof(secp256k1_fe)); 2461 | +int fe_memcmp(const secp256k1_fe *a, const secp256k1_fe *b) { 2462 | + /* Compare only the struct member that holds the limbs 2463 | + (there may be others in VERIFY mode). */
Nit: Clarify that only the limbs matter this comparison?
/* Compare only the struct member that holds the limbs
(there may be others in VERIFY mode, but this function
should ignore them). */
ACK 3801e33d400475c2fdaef1280cf8ed820505a45a assuming my understanding is correct:
fe_memcmp should only consider limbs, and ignore the magnitude and normalized fields (both before and after this change)secp256k1_memcmp_var could have returned nonzero even though all limbs are equal, in the case where a and t differ in uninitialized padding bytesfe_memcmp should only consider limbs, and ignore the magnitude and normalized fields (both before and after this change)
Well I guess it wouldn't hurt to compare the VERIFY fields too...
By the way, just curious, which compiler/architecture did you observe (or do you expect) to add padding?
By the way, just curious, which compiler/architecture did you observe (or do you expect) to add padding?
I don't know, my judgement is based on the C standard.
A compiler may add struct padding and fe_cmov is not guaranteed to
preserve it.
On the way, we improve the identity check such that it covers the
VERIFY struct members.
Forced-push, now checks also equality of the VERIFY members.
ACK 3d7cbafb5fd7f152fc47dc907af5df03150accc0
utACK 3d7cbafb5fd7f152fc47dc907af5df03150accc0
2462 | + ret &= (a->magnitude == b->magnitude); 2463 | + ret &= (a->normalized == b->normalized); 2464 | #endif 2465 | - return secp256k1_memcmp_var(a, &t, sizeof(secp256k1_fe)); 2466 | + /* Compare the struct member that holds the limbs. */ 2467 | + ret &= (secp256k1_memcmp_var(a->n, b->n, sizeof(a->n)) == 0);
In case anyone is wondering: I verified that sizeof(a->n) == 40 ( == 5 * 64 / 8 == 10 * 32 / 8)
By the way, just curious, which compiler/architecture did you observe (or do you expect) to add padding?
In a hypothetical system where int is larger than 64 bits, this could happen. I'm not sure that can be done while complying with the C89 (or later) standard, though, as it puts some restrictions on the sizes of integer types.
I know this is getting slightly off-topic but this is maybe educational.
In a hypothetical system where
intis larger than 64 bits, this could happen.
Yeah, but not only there. I think C only requires alignment to be at least the size. But it may be larger. For example, there may be a hypothetical system where the compiler decides it's a good idea to add some padding because then the int can be accessed more quickly.
In practice though, struct padding is pretty much restricted by calling conventions (even though I'm not sure whether this argument would apply here -- maybe the compiler can leverage that this struct is not visible from the outside).
I'm not sure that can be done while complying with the C89 (or later) standard, though, as it puts some restrictions on the sizes of integer types.
C only specifies minimum ranges but no maximum value ranges. See https://en.wikipedia.org/wiki/C_data_types#Main_types for the ranges expressed in bits. (C also requires that the order makes sense signed char <= int <= long <= long long, similar for unsigned, and that signed and unsigned variant have a related range.)