GMP's mpn_mul_n must never take overlapping source and destination buffers as the implementation makes a few passes over source while updating destination at each pass:
``` https://gmplib.org/manual/Low_002dlevel-Functions.html#Low_002dlevel-Functio...
Function: void mpn_mul_n (mp_limb_t *rp, const mp_limb_t *s1p, const mp_limb_t *s2p, mp_size_t n)
Multiply {s1p, n} and {s2p, n}, and write the 2*n-limb result to rp.
The destination has to have space for 2*n limbs, even if the product’s most significant limb is zero. No overlap is permitted between the destination and either source. ```
Violation of this invariant make nettle to compute curve25519 incorrectly at least on sparc and hppa architectures, see https://bugs.gentoo.org/613418 for some details.
The overlap happens in `ecc-add-eh.c` where layout of local GMP variables on scratch space is the following:
``` |x1|y1|z1|........|B| |x3|y3|z3|... ```
x1/x3, y1/y3, z1/z3 pairs share the same memory region.
Overlap happens at a call of ``` ecc_modp_mul (ecc, y3, B, z1); ``` which is basically ``` mpn_mul_n (y3, B, z1, m->size), ```
Note how y3 overwrites z1. This is scary type of aliasing.
The bug manifested in testsuite failure in nettle and gnutls. I've trailed the bug down to faulty function by adding printf() and comparing exact output of scratch space on x86 versus sparc.
`mpn_mul_n` on the same source numbers produces different results.
This change does not fix the underlying overlap but exposes the problem on all architectures and crashes the testsuitewith asset().
Reported-by: Matt Turner Bug: https://bugs.gentoo.org/613418 Signed-off-by: Sergei Trofimovich slyfox@gentoo.org --- ecc-mod-arith.c | 12 ++++++++++++ 1 file changed, 12 insertions(+)
diff --git a/ecc-mod-arith.c b/ecc-mod-arith.c index f2e47f67..735b1238 100644 --- a/ecc-mod-arith.c +++ b/ecc-mod-arith.c @@ -109,11 +109,23 @@ ecc_mod_submul_1 (const struct ecc_modulo *m, mp_limb_t *rp, assert (hi == 0); }
+static int no_overlap(const mp_limb_t * a, size_t la, + const mp_limb_t * b, size_t lb) +{ + if (a < b) + return (size_t)(b - a) >= la; + /* a >= b */ + return (size_t)(a - b) >= lb; +} + /* NOTE: mul and sqr needs 2*m->size limbs at rp */ void ecc_mod_mul (const struct ecc_modulo *m, mp_limb_t *rp, const mp_limb_t *ap, const mp_limb_t *bp) { + /* NOTE: mpn_mul_n does not work correctly when source and destination overlap */ + assert (no_overlap (rp, 2 * m->size, ap, m->size)); + assert (no_overlap (rp, 2 * m->size, bp, m->size)); mpn_mul_n (rp, ap, bp, m->size); m->reduce (m, rp); }