diff --git a/src/big-int/bigint-func.cc b/src/big-int/bigint-func.cc index 6ee5210a5c6..fdc72562aab 100644 --- a/src/big-int/bigint-func.cc +++ b/src/big-int/bigint-func.cc @@ -20,7 +20,7 @@ pow (BigInt const &x, unsigned y) y >>= 1; if (y == 0) return r; - a *= a; + a = a * a; } } @@ -44,7 +44,7 @@ pow (BigInt const &x, BigInt const &y, BigInt const &m) b /= 2; if (b.is_zero()) return r; - a *= a; + a = a * a; } } diff --git a/src/big-int/bigint.cc b/src/big-int/bigint.cc index 978c63e2ad0..9c78c353bd5 100644 --- a/src/big-int/bigint.cc +++ b/src/big-int/bigint.cc @@ -11,6 +11,7 @@ #include #include +#include #include // How to report errors. @@ -779,6 +780,16 @@ BigInt::compare (BigInt const &b) const void BigInt::add (onedig_t const *dig, unsigned len, bool pos) { + // dig aliases this->digit when called via x += x or x -= x. The + // resize below may free that buffer, turning dig into a dangling + // pointer. Self-aliased operands are not supported; spell doubling + // as x + x or use a copy of the operand. + if(dig == digit) + { + error("BigInt::add: operand must not alias *this."); + abort(); + } + // Make sure the result fits into this, even with carry. resize ((length > len ? length : len) + 1); @@ -832,6 +843,15 @@ BigInt::add (onedig_t const *dig, unsigned len, bool pos) void BigInt::mul (onedig_t const *dig, unsigned len, bool pos) { + // Self-aliased operands are not supported: parts of the code below + // read the operand after this->digit has been reallocated or + // modified. Spell squaring as x * x or use a copy of the operand. + if(dig == digit) + { + error("BigInt::mul: operand must not alias *this."); + abort(); + } + if (len < 2) { // Handle small dig/len operand efficiently. @@ -1129,6 +1149,13 @@ BigInt::div (BigInt const &x, BigInt const &y, BigInt &q, BigInt &r) BigInt & BigInt::operator/= (BigInt const &y) { + // Self-aliased operands are not supported anywhere in this library. + if(this == &y) + { + error("BigInt::operator/=: operand must not alias *this."); + abort(); + } + // Eliminate some trivial cases. int cmp = ucompare (y); if (cmp < 0) @@ -1197,6 +1224,13 @@ BigInt::operator/= (BigInt const &y) BigInt & BigInt::operator%= (BigInt const &y) { + // Self-aliased operands are not supported anywhere in this library. + if(this == &y) + { + error("BigInt::operator%=: operand must not alias *this."); + abort(); + } + // Eliminate some trivial cases. int cmp = ucompare (y); if (cmp < 0) diff --git a/unit/big-int/big-int.cpp b/unit/big-int/big-int.cpp index 678318bf238..359eaad7e19 100644 --- a/unit/big-int/big-int.cpp +++ b/unit/big-int/big-int.cpp @@ -75,6 +75,51 @@ TEST_CASE("arbitrary precision integers", "[core][big-int][bigint]") REQUIRE(to_string(i) == "1"); } + // ===================================================================== + // Compound assignment with an operand equal to the left-hand side. + // ===================================================================== + // Self-aliased operands (x += x) are not supported and abort at + // runtime: operator+= and operator-= pass the operand's digit buffer + // into add(), which resizes (and possibly frees) this->digit before + // reading that buffer. The tests below use a distinct copy of the + // operand, which is the supported spelling; they still exercise the + // buffer growth in add() with equal-length operands. + SECTION("compound assignment with equal operands") + { + // Scanning from a string produces a buffer without spare capacity: + // 2^192 - 1 occupies six 32-bit digits, the doubled value needs + // seven, forcing the reallocation inside add(). + BigInt x("6277101735386680763835789423207666416102355444464034512895"); + const BigInt x_copy(x); + x += x_copy; + REQUIRE( + to_string(x) == + "12554203470773361527671578846415332832204710888928069025790"); + + BigInt y("6277101735386680763835789423207666416102355444464034512895"); + const BigInt y_copy(y); + y -= y_copy; + REQUIRE(y.is_zero()); + + // Squaring through a copy of the operand; z *= z would abort. + BigInt z("18446744073709551616"); // 2^64 + const BigInt z_copy(z); + z *= z_copy; + REQUIRE(to_string(z) == "340282366920938463463374607431768211456"); + + // Division and remainder with equal operands take the + // value-equality fast path; w /= w and v %= v would abort. + BigInt w("340282366920938463463374607431768211456"); // 2^128 + const BigInt w_copy(w); + w /= w_copy; + REQUIRE(to_string(w) == "1"); + + BigInt v("340282366920938463463374607431768211456"); + const BigInt v_copy(v); + v %= v_copy; + REQUIRE(v.is_zero()); + } + // ===================================================================== // Test cases from the clisp test suite in number.tst. // =====================================================================