From 33fbf88707e69362bd5b6336860827a7b4d74440 Mon Sep 17 00:00:00 2001 From: Erik Kundt Date: Mon, 26 Mar 2018 19:48:20 +0200 Subject: Limits rational numbers to 4096 bits. --- libsolidity/ast/Types.cpp | 180 ++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 160 insertions(+), 20 deletions(-) (limited to 'libsolidity') diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index 68b12777..51739cb0 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -44,6 +44,85 @@ using namespace std; using namespace dev; using namespace dev::solidity; +namespace +{ + +unsigned int mostSignificantBit(bigint const& _number) +{ +#if BOOST_VERSION < 105500 + solAssert(_number > 0, ""); + bigint number = _number; + unsigned int result = 0; + while (number != 0) + { + number >>= 1; + ++result; + } + return --result; +#else + return boost::multiprecision::msb(_number); +#endif +} + +/// Check whether (_base ** _exp) fits into 4096 bits. +bool fitsPrecisionExp(bigint const& _base, bigint const& _exp) +{ + if (_base == 0) + return true; + + solAssert(_base > 0, ""); + + size_t const bitsMax = 4096; + + unsigned mostSignificantBaseBit = mostSignificantBit(_base); + if (mostSignificantBaseBit == 0) // _base == 1 + return true; + if (mostSignificantBaseBit > bitsMax) // _base >= 2 ^ 4096 + return false; + + bigint bitsNeeded = _exp * (mostSignificantBaseBit + 1); + + return bitsNeeded <= bitsMax; +} + +/// Checks whether _mantissa * (X ** _exp) fits into 4096 bits, +/// where X is given indirectly via _log2OfBase = log2(X). +bool fitsPrecisionBaseX( + bigint const& _mantissa, + double _log2OfBase, + uint32_t _exp +) +{ + if (_mantissa == 0) + return true; + + solAssert(_mantissa > 0, ""); + + size_t const bitsMax = 4096; + + unsigned mostSignificantMantissaBit = mostSignificantBit(_mantissa); + if (mostSignificantMantissaBit > bitsMax) // _mantissa >= 2 ^ 4096 + return false; + + bigint bitsNeeded = mostSignificantMantissaBit + bigint(floor(double(_exp) * _log2OfBase)) + 1; + return bitsNeeded <= bitsMax; +} + +/// Checks whether _mantissa * (10 ** _expBase10) fits into 4096 bits. +bool fitsPrecisionBase10(bigint const& _mantissa, uint32_t _expBase10) +{ + double const log2Of10AwayFromZero = 3.3219280948873624; + return fitsPrecisionBaseX(_mantissa, log2Of10AwayFromZero, _expBase10); +} + +/// Checks whether _mantissa * (2 ** _expBase10) fits into 4096 bits. +bool fitsPrecisionBase2(bigint const& _mantissa, uint32_t _expBase2) +{ + return fitsPrecisionBaseX(_mantissa, 1.0, _expBase2); +} + +} + void StorageOffsets::computeOffsets(TypePointers const& _types) { bigint slotOffset = 0; @@ -689,31 +768,39 @@ tuple RationalNumberType::isValidLiteral(Literal const& _literal } else if (expPoint != _literal.value().end()) { - // parse the exponent + // Parse base and exponent. Checks numeric limit. bigint exp = bigint(string(expPoint + 1, _literal.value().end())); if (exp > numeric_limits::max() || exp < numeric_limits::min()) return make_tuple(false, rational(0)); - // parse the base + uint32_t expAbs = bigint(abs(exp)).convert_to(); + + tuple base = parseRational(string(_literal.value().begin(), expPoint)); + if (!get<0>(base)) return make_tuple(false, rational(0)); value = get<1>(base); if (exp < 0) { - exp *= -1; + if (!fitsPrecisionBase10(abs(value.denominator()), expAbs)) + return make_tuple(false, rational(0)); value /= boost::multiprecision::pow( bigint(10), - exp.convert_to() + expAbs ); } - else + else if (exp > 0) + { + if (!fitsPrecisionBase10(abs(value.numerator()), expAbs)) + return make_tuple(false, rational(0)); value *= boost::multiprecision::pow( bigint(10), - exp.convert_to() + expAbs ); + } } else { @@ -912,16 +999,49 @@ TypePointer RationalNumberType::binaryOperatorResult(Token::Value _operator, Typ using boost::multiprecision::pow; if (other.isFractional()) return TypePointer(); - else if (abs(other.m_value) > numeric_limits::max()) - return TypePointer(); // This will need too much memory to represent. - uint32_t exponent = abs(other.m_value).numerator().convert_to(); - bigint numerator = pow(m_value.numerator(), exponent); - bigint denominator = pow(m_value.denominator(), exponent); - if (other.m_value >= 0) - value = rational(numerator, denominator); + solAssert(other.m_value.denominator() == 1, ""); + bigint const& exp = other.m_value.numerator(); + + // x ** 0 = 1 + // for 0, 1 and -1 the size of the exponent doesn't have to be restricted + if (exp == 0) + value = 1; + else if (m_value.numerator() == 0 || m_value == 1) + value = m_value; + else if (m_value == -1) + { + bigint isOdd = abs(exp) & bigint(1); + value = 1 - 2 * isOdd.convert_to(); + } else - // invert - value = rational(denominator, numerator); + { + if (abs(exp) > numeric_limits::max()) + return TypePointer(); // This will need too much memory to represent. + + uint32_t absExp = bigint(abs(exp)).convert_to(); + + // Limit size to 4096 bits + if (!fitsPrecisionExp(abs(m_value.numerator()), absExp) || !fitsPrecisionExp(abs(m_value.denominator()), absExp)) + return TypePointer(); + + static auto const optimizedPow = [](bigint const& _base, uint32_t _exponent) -> bigint { + if (_base == 1) + return 1; + else if (_base == -1) + return 1 - 2 * int(_exponent & 1); + else + return pow(_base, _exponent); + }; + + bigint numerator = optimizedPow(m_value.numerator(), absExp); + bigint denominator = optimizedPow(m_value.denominator(), absExp); + + if (exp >= 0) + value = rational(numerator, denominator); + else + // invert + value = rational(denominator, numerator); + } break; } case Token::SHL: @@ -933,28 +1053,48 @@ TypePointer RationalNumberType::binaryOperatorResult(Token::Value _operator, Typ return TypePointer(); else if (other.m_value > numeric_limits::max()) return TypePointer(); - uint32_t exponent = other.m_value.numerator().convert_to(); - value = m_value.numerator() * pow(bigint(2), exponent); + if (m_value.numerator() == 0) + value = 0; + else + { + uint32_t exponent = other.m_value.numerator().convert_to(); + if (!fitsPrecisionBase2(abs(m_value.numerator()), exponent)) + return TypePointer(); + value = m_value.numerator() * pow(bigint(2), exponent); + } break; } // NOTE: we're using >> (SAR) to denote right shifting. The type of the LValue // determines the resulting type and the type of shift (SAR or SHR). case Token::SAR: { - using boost::multiprecision::pow; + namespace mp = boost::multiprecision; if (fractional) return TypePointer(); else if (other.m_value < 0) return TypePointer(); else if (other.m_value > numeric_limits::max()) return TypePointer(); - uint32_t exponent = other.m_value.numerator().convert_to(); - value = rational(m_value.numerator() / pow(bigint(2), exponent), 1); + if (m_value.numerator() == 0) + value = 0; + else + { + uint32_t exponent = other.m_value.numerator().convert_to(); + if (exponent > mostSignificantBit(mp::abs(m_value.numerator()))) + value = 0; + else + value = rational(m_value.numerator() / mp::pow(bigint(2), exponent), 1); + } break; } default: return TypePointer(); } + + // verify that numerator and denominator fit into 4096 bit after every operation + if (value.numerator() != 0 && max(mostSignificantBit(abs(value.numerator())), mostSignificantBit(abs(value.denominator()))) > 4096) + return TypePointer(); + return make_shared(value); } } -- cgit v1.2.3