Skip to content

Commit 533d085

Browse files
authored
Merge pull request argotorg#3793 from ethereum/rationalNumberLimit
Rational number limit
2 parents 8be19ad + 33fbf88 commit 533d085

11 files changed

+297
-20
lines changed

Changelog.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ Bugfixes:
4343
* Type System: Improve error message when attempting to shift by a fractional amount.
4444
* Type System: Make external library functions accessible.
4545
* Type System: Prevent encoding of weird types.
46+
* Type System: Restrict rational numbers to 4096 bits.
4647
* Static Analyzer: Fix non-deterministic order of unused variable warnings.
4748
* Static Analyzer: Invalid arithmetic with constant expressions causes errors.
4849

libsolidity/ast/Types.cpp

Lines changed: 160 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,85 @@ using namespace std;
4444
using namespace dev;
4545
using namespace dev::solidity;
4646

47+
namespace
48+
{
49+
50+
unsigned int mostSignificantBit(bigint const& _number)
51+
{
52+
#if BOOST_VERSION < 105500
53+
solAssert(_number > 0, "");
54+
bigint number = _number;
55+
unsigned int result = 0;
56+
while (number != 0)
57+
{
58+
number >>= 1;
59+
++result;
60+
}
61+
return --result;
62+
#else
63+
return boost::multiprecision::msb(_number);
64+
#endif
65+
}
66+
67+
/// Check whether (_base ** _exp) fits into 4096 bits.
68+
bool fitsPrecisionExp(bigint const& _base, bigint const& _exp)
69+
{
70+
if (_base == 0)
71+
return true;
72+
73+
solAssert(_base > 0, "");
74+
75+
size_t const bitsMax = 4096;
76+
77+
unsigned mostSignificantBaseBit = mostSignificantBit(_base);
78+
if (mostSignificantBaseBit == 0) // _base == 1
79+
return true;
80+
if (mostSignificantBaseBit > bitsMax) // _base >= 2 ^ 4096
81+
return false;
82+
83+
bigint bitsNeeded = _exp * (mostSignificantBaseBit + 1);
84+
85+
return bitsNeeded <= bitsMax;
86+
}
87+
88+
/// Checks whether _mantissa * (X ** _exp) fits into 4096 bits,
89+
/// where X is given indirectly via _log2OfBase = log2(X).
90+
bool fitsPrecisionBaseX(
91+
bigint const& _mantissa,
92+
double _log2OfBase,
93+
uint32_t _exp
94+
)
95+
{
96+
if (_mantissa == 0)
97+
return true;
98+
99+
solAssert(_mantissa > 0, "");
100+
101+
size_t const bitsMax = 4096;
102+
103+
unsigned mostSignificantMantissaBit = mostSignificantBit(_mantissa);
104+
if (mostSignificantMantissaBit > bitsMax) // _mantissa >= 2 ^ 4096
105+
return false;
106+
107+
bigint bitsNeeded = mostSignificantMantissaBit + bigint(floor(double(_exp) * _log2OfBase)) + 1;
108+
return bitsNeeded <= bitsMax;
109+
}
110+
111+
/// Checks whether _mantissa * (10 ** _expBase10) fits into 4096 bits.
112+
bool fitsPrecisionBase10(bigint const& _mantissa, uint32_t _expBase10)
113+
{
114+
double const log2Of10AwayFromZero = 3.3219280948873624;
115+
return fitsPrecisionBaseX(_mantissa, log2Of10AwayFromZero, _expBase10);
116+
}
117+
118+
/// Checks whether _mantissa * (2 ** _expBase10) fits into 4096 bits.
119+
bool fitsPrecisionBase2(bigint const& _mantissa, uint32_t _expBase2)
120+
{
121+
return fitsPrecisionBaseX(_mantissa, 1.0, _expBase2);
122+
}
123+
124+
}
125+
47126
void StorageOffsets::computeOffsets(TypePointers const& _types)
48127
{
49128
bigint slotOffset = 0;
@@ -689,31 +768,39 @@ tuple<bool, rational> RationalNumberType::isValidLiteral(Literal const& _literal
689768
}
690769
else if (expPoint != _literal.value().end())
691770
{
692-
// parse the exponent
771+
// Parse base and exponent. Checks numeric limit.
693772
bigint exp = bigint(string(expPoint + 1, _literal.value().end()));
694773

695774
if (exp > numeric_limits<int32_t>::max() || exp < numeric_limits<int32_t>::min())
696775
return make_tuple(false, rational(0));
697776

698-
// parse the base
777+
uint32_t expAbs = bigint(abs(exp)).convert_to<uint32_t>();
778+
779+
699780
tuple<bool, rational> base = parseRational(string(_literal.value().begin(), expPoint));
781+
700782
if (!get<0>(base))
701783
return make_tuple(false, rational(0));
702784
value = get<1>(base);
703785

704786
if (exp < 0)
705787
{
706-
exp *= -1;
788+
if (!fitsPrecisionBase10(abs(value.denominator()), expAbs))
789+
return make_tuple(false, rational(0));
707790
value /= boost::multiprecision::pow(
708791
bigint(10),
709-
exp.convert_to<int32_t>()
792+
expAbs
710793
);
711794
}
712-
else
795+
else if (exp > 0)
796+
{
797+
if (!fitsPrecisionBase10(abs(value.numerator()), expAbs))
798+
return make_tuple(false, rational(0));
713799
value *= boost::multiprecision::pow(
714800
bigint(10),
715-
exp.convert_to<int32_t>()
801+
expAbs
716802
);
803+
}
717804
}
718805
else
719806
{
@@ -912,16 +999,49 @@ TypePointer RationalNumberType::binaryOperatorResult(Token::Value _operator, Typ
912999
using boost::multiprecision::pow;
9131000
if (other.isFractional())
9141001
return TypePointer();
915-
else if (abs(other.m_value) > numeric_limits<uint32_t>::max())
916-
return TypePointer(); // This will need too much memory to represent.
917-
uint32_t exponent = abs(other.m_value).numerator().convert_to<uint32_t>();
918-
bigint numerator = pow(m_value.numerator(), exponent);
919-
bigint denominator = pow(m_value.denominator(), exponent);
920-
if (other.m_value >= 0)
921-
value = rational(numerator, denominator);
1002+
solAssert(other.m_value.denominator() == 1, "");
1003+
bigint const& exp = other.m_value.numerator();
1004+
1005+
// x ** 0 = 1
1006+
// for 0, 1 and -1 the size of the exponent doesn't have to be restricted
1007+
if (exp == 0)
1008+
value = 1;
1009+
else if (m_value.numerator() == 0 || m_value == 1)
1010+
value = m_value;
1011+
else if (m_value == -1)
1012+
{
1013+
bigint isOdd = abs(exp) & bigint(1);
1014+
value = 1 - 2 * isOdd.convert_to<int>();
1015+
}
9221016
else
923-
// invert
924-
value = rational(denominator, numerator);
1017+
{
1018+
if (abs(exp) > numeric_limits<uint32_t>::max())
1019+
return TypePointer(); // This will need too much memory to represent.
1020+
1021+
uint32_t absExp = bigint(abs(exp)).convert_to<uint32_t>();
1022+
1023+
// Limit size to 4096 bits
1024+
if (!fitsPrecisionExp(abs(m_value.numerator()), absExp) || !fitsPrecisionExp(abs(m_value.denominator()), absExp))
1025+
return TypePointer();
1026+
1027+
static auto const optimizedPow = [](bigint const& _base, uint32_t _exponent) -> bigint {
1028+
if (_base == 1)
1029+
return 1;
1030+
else if (_base == -1)
1031+
return 1 - 2 * int(_exponent & 1);
1032+
else
1033+
return pow(_base, _exponent);
1034+
};
1035+
1036+
bigint numerator = optimizedPow(m_value.numerator(), absExp);
1037+
bigint denominator = optimizedPow(m_value.denominator(), absExp);
1038+
1039+
if (exp >= 0)
1040+
value = rational(numerator, denominator);
1041+
else
1042+
// invert
1043+
value = rational(denominator, numerator);
1044+
}
9251045
break;
9261046
}
9271047
case Token::SHL:
@@ -933,28 +1053,48 @@ TypePointer RationalNumberType::binaryOperatorResult(Token::Value _operator, Typ
9331053
return TypePointer();
9341054
else if (other.m_value > numeric_limits<uint32_t>::max())
9351055
return TypePointer();
936-
uint32_t exponent = other.m_value.numerator().convert_to<uint32_t>();
937-
value = m_value.numerator() * pow(bigint(2), exponent);
1056+
if (m_value.numerator() == 0)
1057+
value = 0;
1058+
else
1059+
{
1060+
uint32_t exponent = other.m_value.numerator().convert_to<uint32_t>();
1061+
if (!fitsPrecisionBase2(abs(m_value.numerator()), exponent))
1062+
return TypePointer();
1063+
value = m_value.numerator() * pow(bigint(2), exponent);
1064+
}
9381065
break;
9391066
}
9401067
// NOTE: we're using >> (SAR) to denote right shifting. The type of the LValue
9411068
// determines the resulting type and the type of shift (SAR or SHR).
9421069
case Token::SAR:
9431070
{
944-
using boost::multiprecision::pow;
1071+
namespace mp = boost::multiprecision;
9451072
if (fractional)
9461073
return TypePointer();
9471074
else if (other.m_value < 0)
9481075
return TypePointer();
9491076
else if (other.m_value > numeric_limits<uint32_t>::max())
9501077
return TypePointer();
951-
uint32_t exponent = other.m_value.numerator().convert_to<uint32_t>();
952-
value = rational(m_value.numerator() / pow(bigint(2), exponent), 1);
1078+
if (m_value.numerator() == 0)
1079+
value = 0;
1080+
else
1081+
{
1082+
uint32_t exponent = other.m_value.numerator().convert_to<uint32_t>();
1083+
if (exponent > mostSignificantBit(mp::abs(m_value.numerator())))
1084+
value = 0;
1085+
else
1086+
value = rational(m_value.numerator() / mp::pow(bigint(2), exponent), 1);
1087+
}
9531088
break;
9541089
}
9551090
default:
9561091
return TypePointer();
9571092
}
1093+
1094+
// verify that numerator and denominator fit into 4096 bit after every operation
1095+
if (value.numerator() != 0 && max(mostSignificantBit(abs(value.numerator())), mostSignificantBit(abs(value.denominator()))) > 4096)
1096+
return TypePointer();
1097+
9581098
return make_shared<RationalNumberType>(value);
9591099
}
9601100
}

test/libsolidity/SolidityEndToEndTest.cpp

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,29 @@ BOOST_AUTO_TEST_CASE(exp_operator_const_signed)
102102
ABI_CHECK(callContractFunction("f()", bytes()), toBigEndian(u256(-8)));
103103
}
104104

105+
BOOST_AUTO_TEST_CASE(exp_zero)
106+
{
107+
char const* sourceCode = R"(
108+
contract test {
109+
function f(uint a) returns(uint d) { return a ** 0; }
110+
}
111+
)";
112+
compileAndRun(sourceCode);
113+
testContractAgainstCppOnRange("f(uint256)", [](u256 const&) -> u256 { return u256(1); }, 0, 16);
114+
}
115+
116+
BOOST_AUTO_TEST_CASE(exp_zero_literal)
117+
{
118+
char const* sourceCode = R"(
119+
contract test {
120+
function f() returns(uint d) { return 0 ** 0; }
121+
}
122+
)";
123+
compileAndRun(sourceCode);
124+
ABI_CHECK(callContractFunction("f()", bytes()), toBigEndian(u256(1)));
125+
}
126+
127+
105128
BOOST_AUTO_TEST_CASE(conditional_expression_true_literal)
106129
{
107130
char const* sourceCode = R"(
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
contract c {
2+
uint[2**253] data;
3+
}
4+
// ----
5+
// Warning: (17-34): Variable covers a large part of storage and thus makes collisions likely. Either use mappings or dynamic arrays and allow their size to be increased only in small quantities per transaction.
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
contract c {
2+
function f() public pure {
3+
int a;
4+
a = 1 << 4095; // shift is fine, but result too large
5+
a = 1 << 4096; // too large
6+
a = (1E1233) << 2; // too large
7+
}
8+
}
9+
// ----
10+
// TypeError: (71-80): Type int_const 5221...(1225 digits omitted)...5168 is not implicitly convertible to expected type int256.
11+
// TypeError: (133-142): Operator << not compatible with types int_const 1 and int_const 4096
12+
// TypeError: (169-182): Operator << not compatible with types int_const 1000...(1226 digits omitted)...0000 and int_const 2
13+
// TypeError: (169-182): Type int_const 1000...(1226 digits omitted)...0000 is not implicitly convertible to expected type int256.
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
contract c {
2+
function f() public pure {
3+
int a;
4+
a = 1/(2<<4094)/(2<<4094);
5+
}
6+
}
7+
// ----
8+
// TypeError: (71-92): Operator / not compatible with types rational_const 1 / 5221...(1225 digits omitted)...5168 and int_const 5221...(1225 digits omitted)...5168
9+
// TypeError: (71-92): Type rational_const 1 / 5221...(1225 digits omitted)...5168 is not implicitly convertible to expected type int256. Try converting to type ufixed8x80 or use an explicit conversion.
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
contract c {
2+
function f() public pure {
3+
int a;
4+
a = 4 ** 4 ** 2 ** 4 ** 4 ** 4 ** 4;
5+
a = -4 ** 4 ** 2 ** 4 ** 4 ** 4 ** 4 ** 4;
6+
a = 4 ** (-(2 ** 4 ** 4 ** 4 ** 4 ** 4));
7+
a = 0 ** 1E1233; // fine
8+
a = 1 ** 1E1233; // fine
9+
a = -1 ** 1E1233; // fine
10+
a = 2 ** 1E1233;
11+
a = -2 ** 1E1233;
12+
a = 2 ** -1E1233;
13+
a = -2 ** -1E1233;
14+
a = 1E1233 ** 2;
15+
a = -1E1233 ** 2;
16+
a = 1E1233 ** -2;
17+
a = -1E1233 ** -2;
18+
a = 1E1233 ** 1E1233;
19+
a = 1E1233 ** -1E1233;
20+
a = -1E1233 ** 1E1233;
21+
a = -1E1233 ** -1E1233;
22+
}
23+
}
24+
// ----
25+
// TypeError: (71-102): Operator ** not compatible with types int_const 1797...(301 digits omitted)...7216 and int_const 4
26+
// TypeError: (71-102): Type int_const 1797...(301 digits omitted)...7216 is not implicitly convertible to expected type int256.
27+
// TypeError: (116-148): Operator ** not compatible with types int_const 1797...(301 digits omitted)...7216 and int_const 4
28+
// TypeError: (116-153): Operator ** not compatible with types int_const 1797...(301 digits omitted)...7216 and int_const 4
29+
// TypeError: (116-153): Type int_const 1797...(301 digits omitted)...7216 is not implicitly convertible to expected type int256.
30+
// TypeError: (167-203): Operator ** not compatible with types int_const 4 and int_const -179...(302 digits omitted)...7216
31+
// TypeError: (317-328): Operator ** not compatible with types int_const 2 and int_const 1000...(1226 digits omitted)...0000
32+
// TypeError: (342-354): Operator ** not compatible with types int_const -2 and int_const 1000...(1226 digits omitted)...0000
33+
// TypeError: (368-380): Operator ** not compatible with types int_const 2 and int_const -100...(1227 digits omitted)...0000
34+
// TypeError: (394-407): Operator ** not compatible with types int_const -2 and int_const -100...(1227 digits omitted)...0000
35+
// TypeError: (421-432): Operator ** not compatible with types int_const 1000...(1226 digits omitted)...0000 and int_const 2
36+
// TypeError: (421-432): Type int_const 1000...(1226 digits omitted)...0000 is not implicitly convertible to expected type int256.
37+
// TypeError: (446-458): Operator ** not compatible with types int_const -100...(1227 digits omitted)...0000 and int_const 2
38+
// TypeError: (446-458): Type int_const -100...(1227 digits omitted)...0000 is not implicitly convertible to expected type int256.
39+
// TypeError: (472-484): Operator ** not compatible with types int_const 1000...(1226 digits omitted)...0000 and int_const -2
40+
// TypeError: (472-484): Type int_const 1000...(1226 digits omitted)...0000 is not implicitly convertible to expected type int256.
41+
// TypeError: (498-511): Operator ** not compatible with types int_const -100...(1227 digits omitted)...0000 and int_const -2
42+
// TypeError: (498-511): Type int_const -100...(1227 digits omitted)...0000 is not implicitly convertible to expected type int256.
43+
// TypeError: (525-541): Operator ** not compatible with types int_const 1000...(1226 digits omitted)...0000 and int_const 1000...(1226 digits omitted)...0000
44+
// TypeError: (525-541): Type int_const 1000...(1226 digits omitted)...0000 is not implicitly convertible to expected type int256.
45+
// TypeError: (555-572): Operator ** not compatible with types int_const 1000...(1226 digits omitted)...0000 and int_const -100...(1227 digits omitted)...0000
46+
// TypeError: (555-572): Type int_const 1000...(1226 digits omitted)...0000 is not implicitly convertible to expected type int256.
47+
// TypeError: (586-603): Operator ** not compatible with types int_const -100...(1227 digits omitted)...0000 and int_const 1000...(1226 digits omitted)...0000
48+
// TypeError: (586-603): Type int_const -100...(1227 digits omitted)...0000 is not implicitly convertible to expected type int256.
49+
// TypeError: (617-635): Operator ** not compatible with types int_const -100...(1227 digits omitted)...0000 and int_const -100...(1227 digits omitted)...0000
50+
// TypeError: (617-635): Type int_const -100...(1227 digits omitted)...0000 is not implicitly convertible to expected type int256.
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
contract c {
2+
function bignum() public {
3+
uint256 a;
4+
a = 1e1233 / 1e1233; // 1e1233 is still fine
5+
a = 1e1234; // 1e1234 is too big
6+
}
7+
}
8+
// ----
9+
// TypeError: (128-134): Invalid literal value.
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
contract c {
2+
function bignum() public {
3+
uint a;
4+
a = 134562324532464234452335168163516E1200 / 134562324532464234452335168163516E1200; // still fine
5+
a = 1345623245324642344523351681635168E1200; // too large
6+
}
7+
}
8+
// ----
9+
// TypeError: (179-218): Invalid literal value.
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
contract c {
2+
function bignum() public {
3+
uint a;
4+
a = 134562324532464.234452335168163517E1200 / 134562324532464.234452335168163517E1200; // still fine
5+
a = 134562324532464.2344523351681635177E1200; // too large
6+
}
7+
}
8+
// ----
9+
// TypeError: (181-221): Invalid literal value.

0 commit comments

Comments
 (0)