-
Notifications
You must be signed in to change notification settings - Fork 6.3k
Rational number limit #3793
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Rational number limit #3793
Conversation
libsolidity/ast/Types.cpp
Outdated
| bigint denominator = pow(m_value.denominator(), exponent); | ||
|
|
||
| // Limit size to 4096 bits | ||
| if (numerator >= pow(bigint(2), rationalMaxBits) - 1) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This pow should be evaluated in the const variable. Also you can use shift instead of pow for power-of-2.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think it is enough to check for the value after it has ben evaluated. It should be guessed before evaluating with pow.
One of the main problems here is the DoS vector (which is just annoying for a user, for example running the compiler in the browser).
libsolidity/ast/Types.cpp
Outdated
| bigint denominator = pow(m_value.denominator(), exponent); | ||
|
|
||
| // Limit size to 4096 bits | ||
| if (numerator >= pow(bigint(2), rationalMaxBits) - 1) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think it is enough to check for the value after it has ben evaluated. It should be guessed before evaluating with pow.
One of the main problems here is the DoS vector (which is just annoying for a user, for example running the compiler in the browser).
|
@axic I've started testing with two examples of those DoS cases and they're not hanging anymore. After the limit is reached, the function is still called, but |
libsolidity/ast/Types.cpp
Outdated
| namespace | ||
| { | ||
|
|
||
| uint32_t static const exponentLimit = 9999; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We don't really use uint32_t explicitly in the rest of the codebase. I think I almost exclusively use size_t.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
size_t or unsigned is most common in the codebase
libsolidity/ast/Types.cpp
Outdated
|
|
||
| TypePointer IntegerType::binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const | ||
| { | ||
| { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Surplus whitespace.
| // parse the exponent and check limits | ||
| bigint exp = bigint(string(expPoint + 1, _literal.value().end())); | ||
|
|
||
| if (exp > numeric_limits<int32_t>::max() || exp < numeric_limits<int32_t>::min()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This can now be removed.
libsolidity/ast/Types.cpp
Outdated
| if (exp > numeric_limits<int32_t>::max() || exp < numeric_limits<int32_t>::min()) | ||
| return make_tuple(false, rational(0)); | ||
|
|
||
| if (abs(exp) >= exponentLimit) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perhaps it would be better to re-use the same helper in all functions that compute exp for rational numbers. Just pass the base and the exponent, and it will determine if the resulting number still fits our precision.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, if we know the base, we can better estimate the size of the resulting number.
|
Please also add a test for bit-shifts. |
For those test cases it probably is the case, however for the case of doing a very large exp, it will just "hang" or take a lot of time. Please add such a test case too. |
|
Maybe useful to look at the test cases of #3348 (an earlier attempt of the same problem). Additionally, some random notes:
|
04fa673 to
eeaca4a
Compare
| uint a; | ||
| uint b; | ||
| a = 1e9999; | ||
| b = 13456232453246423445233516816351681345623245324642344523351681635168.7161354161363516163161354168154E1200; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Indentation
libsolidity/ast/Types.cpp
Outdated
| if(_base > baseMax) | ||
| return false; | ||
|
|
||
| bigfloat bitsNeeded = bigfloat(_exp) * ceil(log10(bigfloat(_base + 1))); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe log2 instead of log10?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That was a defect ;) log10 is what I wanted but this formula calculated the number of digits and not the number of bits. I've added another formula which estimates the minimum number of bits needed.
libsolidity/ast/Types.cpp
Outdated
| { | ||
| using boost::multiprecision::log10; | ||
| using boost::multiprecision::ceil; | ||
| using bigfloat = boost::multiprecision::number<boost::multiprecision::cpp_dec_float<50>>; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does that mean it allows up to 50 decimal digits? What happens if _base + 1 is larger than 50 decimal digits?
According to boost these are also defined:
typedef number<cpp_dec_float<50> > cpp_dec_float_50;
typedef number<cpp_dec_float<100> > cpp_dec_float_100;
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@axic The precision will be reduced after creating cpp_dec_float_50 with base + 1. That means a if base has more than 50 digits, any additional digit will be represented with a zero. This does not affect the size of it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you add a test for this? Boost had some bugs where an overflow (with shifts) caused the entire value to be 0.
eeaca4a to
5d11596
Compare
libsolidity/ast/Types.cpp
Outdated
| return false; | ||
|
|
||
| bigfloat digitsNeeded = bigfloat(_exp) * ceil(log10(bigfloat(_base + 1))); | ||
| bigfloat bitsNeeded = ceil((digitsNeeded - 1) * log2(bigfloat(10))); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why calculate digitsNeeded as an intermediate result?
You might as well just use log2 directly to get bitsNeeded instead of multiplying with log2(10) later...
log2(10) * log10(a) = log2(a)
I don't think using 10 as base has any advantage for choosing the correct way of rounding things...
What you want to have in the end is: 2 ^ 4096 - 1 >= _base^_exp or, since it's integers:
2^4096 > _base^_exp.
Hence you want log2(2^4096) > log2(_base^_exp).
Hence 4096 > _exp * log2(_base).
You only need to compensate for potential rounding error in log2(_base).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Safest thing would probably be bitsNeeded = _exp * (floor(log2(_base)) + 1).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Although you may want to treat _base <= 1 separately :-).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also: do you only use fitsPrecision for _base >= 0? You'd probably need to treat negative _base differently...
And probably also add tests for that.
axic
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also needs a test for a larger base with a smaller exponent, which still exceeds the limit.
7cd7fd9 to
fc19981
Compare
libsolidity/ast/Types.cpp
Outdated
|
|
||
| size_t const bitsMax = 4096; | ||
| bigint const baseMax = (bigint(2) << bitsMax) - 1; | ||
| if(_base > baseMax) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Space after if
libsolidity/ast/Types.cpp
Outdated
| namespace | ||
| { | ||
|
|
||
| bool fitsPrecision(bigint const _base, bigint const _exp) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not const&?
788c456 to
a3c08f2
Compare
libsolidity/ast/Types.cpp
Outdated
|
|
||
| size_t const bitsMax = 4096; | ||
|
|
||
| auto mostSignificantBit = msb(_mantissa); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please add an explicit type.
fb462eb to
43cb2b0
Compare
libsolidity/ast/Types.cpp
Outdated
| namespace | ||
| { | ||
|
|
||
| // checks whether (_base ^ _exp) fits into 4096 bits |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think ** would be better here. Also we generally use /// ("javadoc") to document functions.
43cb2b0 to
93b3507
Compare
libsolidity/ast/Types.cpp
Outdated
| if (!_base) | ||
| return true; | ||
|
|
||
| // _base > 0 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not turn this into an assert and remove the one above?
libsolidity/ast/Types.cpp
Outdated
|
|
||
| size_t const bitsMax = 4096; | ||
|
|
||
| auto mostSignificantBaseBit = msb(_base); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please use an explicit type here.
0573096 to
49186fb
Compare
|
OK - I'll try to find a good workaround over the weekend. The |
|
@ekpyron were you able to find something? Otherwise I will pick this up later. |
|
@chriseth I was just about to look into it. Unfortunately I think we may have to use |
b9f577c to
dfe8c01
Compare
dfe8c01 to
33fbf88
Compare
|
@chriseth This now contains a workaround for boost < 1.55, which is a naive (but therefore sufficiently simple) implementation of |
|
Now the |
Fixes #3327
Fixes #3759