-
Notifications
You must be signed in to change notification settings - Fork 5.9k
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
Breaking: overflow detection for SDIV - minInt / (-1) #1091
Conversation
5b01e0a
to
b9d6e4d
Compare
// Test for (minInt) / (-1) | ||
if (c_isSigned && _operator == Token::Div) | ||
{ | ||
m_context << Instruction::DUP1 << Instruction::NOT // ~x |
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.
Not sure if this is faster, but you might want to use the new inline assembly here:
m_context.appendInlineAssembly(R"(
and(
eq(x, exp(2, 255)), // x == 2**256 (min int)
iszero(not(y)) // y == -1
)
)",
// names of variables already on the stack
{"y", "x"});
m_context.appendConditionalJumpTo(m_context.errorTag());
Oh by the way, in all other comments of the code generator, the stack grows to the right, I think yours grows to the left.
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.
Oh, I'll change the stack growing direction.
Please don't miss my comment above. |
a5132bb
to
1a6eef6
Compare
No, no, they arrive in my mailbox. |
b680363
to
5717205
Compare
@chriseth The inline assembly caused an exception (possibly because the compiler tried to reduce every constant number, and hit the case?) So I reverted to the old fashioned EVM writing. |
This is done on my side. |
5717205
to
4c4d8ab
Compare
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 entry into the changelog (not for the current version, this has to go into 0.5.0) and also add some documentation ("places where solidity throws").
Note that this PR is only one part of the general "throw on arithmetic overflow" task.
if (c_isSigned && _operator == Token::Div) | ||
{ | ||
m_context << Instruction::DUP1 // x | ||
<< u256(255) << u256(2) << Instruction::EXP // 2 ** 255 |
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.
Better to use (u256(1) << 255)
, the optimizer knows how to represent that.
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 the optimizer use EXP
, PUSH32
directly, or a much better way? I will do an experiment.
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.
It tries various different ways to represent constants and compares the creation-time costs against the execution-time costs.
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.
OK --optimize
does the same thing as I do. I'll change this.
m_context << Instruction::AND; | ||
m_context.appendConditionalJumpTo(m_context.errorTag()); | ||
} | ||
|
||
if (_operator == Token::Div) | ||
m_context << (c_isSigned ? Instruction::SDIV : Instruction::DIV); | ||
else |
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.
Is there also a special case for smod?
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.
The SMOD
specification is ambiguous. I created a separate issue about this.
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.
My conclusion is that SMOD
has no overflows.
Mentioning #796 for cross-referencing. |
4c4d8ab
to
f78f199
Compare
Following a comment ethereum#1091 (comment)
I've dealt with all comments. |
Can we pull out the "When Solidity Throws" change into a separate PR and merge it? |
@axic that's a good idea. I start the separation. |
bd86274
to
5c0c3c3
Compare
Following a comment ethereum#1091 (comment)
5c0c3c3
to
41e2bc6
Compare
Following a comment ethereum#1091 (comment)
41e2bc6
to
ae8e1f0
Compare
Following a comment ethereum#1091 (comment)
34b9bff
to
6dd48f5
Compare
Following a comment ethereum#1091 (comment)
6dd48f5
to
71cfe63
Compare
Tests pass finally. I'll keep rebasing. |
71cfe63
to
041fc84
Compare
Following a comment ethereum#1091 (comment)
041fc84
to
87b09e7
Compare
Following a comment ethereum#1091 (comment)
87b09e7
to
8a8b29b
Compare
Following a comment ethereum#1091 (comment)
if (c_isSigned && _operator == Token::Div) | ||
{ | ||
m_context.appendInlineAssembly(R"({ | ||
jumpi(invalidJumpLabel, and(eq(y, exp(2,255)), iszero(add(x, 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.
The invalidJumpLabel
is gone. This should use something like:
jumpi(safe, iszero(...))
invalid
safe:
Thought not sure if the labels must be unique?
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'll create something like
invalidLabel:
invalid
once somewhere.
It seems I can do something within 3 weeks.
Perhaps `main` has a special syntactic treatment
Throw when the result is not within the expressible range of signed 256 bit integer.
that happens when the smallest signed integer is divided by one.
Following a comment ethereum#1091 (comment)
8a8b29b
to
2dfdfda
Compare
Overflow-detection at runtime is extremely complicated because of the various types we have. I propose to do only compile-time overflow checking and close this pull request. |
@chriseth do you have less number of types at compilation time? |
No, but it is not as complicated and the effects of getting something wrong are not as bad. |
@chriseth really? I think runtime checks are vastly less complicated than compilation time checks. |
@chriseth brought up a convincing argument that all different cases of the runtime overflow checks will not make it until the new intermediate language lands. So I'm closing this. |
Trying to fix #1087.
First I'm trying to catch this in the CI servers.