Merge pull request #2206 from ethereum/fixoptimizer

Constant optimizer fix
This commit is contained in:
chriseth 2017-05-03 11:54:12 +02:00 committed by GitHub
commit 00933b99cc
5 changed files with 87 additions and 25 deletions

View File

@ -20,6 +20,7 @@ Bugfixes:
and source mappings. and source mappings.
* Gas Estimator: Reflect the most recent fee schedule. * Gas Estimator: Reflect the most recent fee schedule.
* Type system: Contract inheriting from base with unimplemented constructor should be abstract. * Type system: Contract inheriting from base with unimplemented constructor should be abstract.
* Optimizer: Number representation bug in the constant optimizer fixed.
### 0.4.10 (2017-03-15) ### 0.4.10 (2017-03-15)

View File

@ -1,4 +1,15 @@
[ [
{
"name": "ConstantOptimizerSubtraction",
"summary": "In some situations, the optimizer replaces certain numbers in the code with routines that compute different numbers.",
"description": "The optimizer tries to represent any number in the bytecode by routines that compute them with less gas. For some special numbers, an incorrect routine is generated. This could allow an attacker to e.g. trick victims about a specific amount of ether, or function calls to call different functions (or none at all).",
"link": "https://blog.ethereum.org/2017/04/24/solidity-optimizer-bug/",
"fixed": "0.4.11",
"severity": "low",
"conditions": {
"optimizer": true
}
},
{ {
"name": "IdentityPrecompileReturnIgnored", "name": "IdentityPrecompileReturnIgnored",
"summary": "Failure of the identity precompile was ignored.", "summary": "Failure of the identity precompile was ignored.",

View File

@ -1,6 +1,7 @@
{ {
"0.1.0": { "0.1.0": {
"bugs": [ "bugs": [
"ConstantOptimizerSubtraction",
"IdentityPrecompileReturnIgnored", "IdentityPrecompileReturnIgnored",
"OptimizerStaleKnowledgeAboutSHA3", "OptimizerStaleKnowledgeAboutSHA3",
"SendFailsForZeroEther", "SendFailsForZeroEther",
@ -14,6 +15,7 @@
}, },
"0.1.1": { "0.1.1": {
"bugs": [ "bugs": [
"ConstantOptimizerSubtraction",
"IdentityPrecompileReturnIgnored", "IdentityPrecompileReturnIgnored",
"OptimizerStaleKnowledgeAboutSHA3", "OptimizerStaleKnowledgeAboutSHA3",
"SendFailsForZeroEther", "SendFailsForZeroEther",
@ -27,6 +29,7 @@
}, },
"0.1.2": { "0.1.2": {
"bugs": [ "bugs": [
"ConstantOptimizerSubtraction",
"IdentityPrecompileReturnIgnored", "IdentityPrecompileReturnIgnored",
"OptimizerStaleKnowledgeAboutSHA3", "OptimizerStaleKnowledgeAboutSHA3",
"SendFailsForZeroEther", "SendFailsForZeroEther",
@ -40,6 +43,7 @@
}, },
"0.1.3": { "0.1.3": {
"bugs": [ "bugs": [
"ConstantOptimizerSubtraction",
"IdentityPrecompileReturnIgnored", "IdentityPrecompileReturnIgnored",
"OptimizerStaleKnowledgeAboutSHA3", "OptimizerStaleKnowledgeAboutSHA3",
"SendFailsForZeroEther", "SendFailsForZeroEther",
@ -53,6 +57,7 @@
}, },
"0.1.4": { "0.1.4": {
"bugs": [ "bugs": [
"ConstantOptimizerSubtraction",
"IdentityPrecompileReturnIgnored", "IdentityPrecompileReturnIgnored",
"OptimizerStaleKnowledgeAboutSHA3", "OptimizerStaleKnowledgeAboutSHA3",
"SendFailsForZeroEther", "SendFailsForZeroEther",
@ -66,6 +71,7 @@
}, },
"0.1.5": { "0.1.5": {
"bugs": [ "bugs": [
"ConstantOptimizerSubtraction",
"IdentityPrecompileReturnIgnored", "IdentityPrecompileReturnIgnored",
"OptimizerStaleKnowledgeAboutSHA3", "OptimizerStaleKnowledgeAboutSHA3",
"SendFailsForZeroEther", "SendFailsForZeroEther",
@ -79,6 +85,7 @@
}, },
"0.1.6": { "0.1.6": {
"bugs": [ "bugs": [
"ConstantOptimizerSubtraction",
"IdentityPrecompileReturnIgnored", "IdentityPrecompileReturnIgnored",
"HighOrderByteCleanStorage", "HighOrderByteCleanStorage",
"OptimizerStaleKnowledgeAboutSHA3", "OptimizerStaleKnowledgeAboutSHA3",
@ -93,6 +100,7 @@
}, },
"0.1.7": { "0.1.7": {
"bugs": [ "bugs": [
"ConstantOptimizerSubtraction",
"IdentityPrecompileReturnIgnored", "IdentityPrecompileReturnIgnored",
"HighOrderByteCleanStorage", "HighOrderByteCleanStorage",
"OptimizerStaleKnowledgeAboutSHA3", "OptimizerStaleKnowledgeAboutSHA3",
@ -107,6 +115,7 @@
}, },
"0.2.0": { "0.2.0": {
"bugs": [ "bugs": [
"ConstantOptimizerSubtraction",
"IdentityPrecompileReturnIgnored", "IdentityPrecompileReturnIgnored",
"HighOrderByteCleanStorage", "HighOrderByteCleanStorage",
"OptimizerStaleKnowledgeAboutSHA3", "OptimizerStaleKnowledgeAboutSHA3",
@ -121,6 +130,7 @@
}, },
"0.2.1": { "0.2.1": {
"bugs": [ "bugs": [
"ConstantOptimizerSubtraction",
"IdentityPrecompileReturnIgnored", "IdentityPrecompileReturnIgnored",
"HighOrderByteCleanStorage", "HighOrderByteCleanStorage",
"OptimizerStaleKnowledgeAboutSHA3", "OptimizerStaleKnowledgeAboutSHA3",
@ -135,6 +145,7 @@
}, },
"0.2.2": { "0.2.2": {
"bugs": [ "bugs": [
"ConstantOptimizerSubtraction",
"IdentityPrecompileReturnIgnored", "IdentityPrecompileReturnIgnored",
"HighOrderByteCleanStorage", "HighOrderByteCleanStorage",
"OptimizerStaleKnowledgeAboutSHA3", "OptimizerStaleKnowledgeAboutSHA3",
@ -149,6 +160,7 @@
}, },
"0.3.0": { "0.3.0": {
"bugs": [ "bugs": [
"ConstantOptimizerSubtraction",
"IdentityPrecompileReturnIgnored", "IdentityPrecompileReturnIgnored",
"HighOrderByteCleanStorage", "HighOrderByteCleanStorage",
"OptimizerStaleKnowledgeAboutSHA3", "OptimizerStaleKnowledgeAboutSHA3",
@ -162,6 +174,7 @@
}, },
"0.3.1": { "0.3.1": {
"bugs": [ "bugs": [
"ConstantOptimizerSubtraction",
"IdentityPrecompileReturnIgnored", "IdentityPrecompileReturnIgnored",
"HighOrderByteCleanStorage", "HighOrderByteCleanStorage",
"OptimizerStaleKnowledgeAboutSHA3", "OptimizerStaleKnowledgeAboutSHA3",
@ -174,6 +187,7 @@
}, },
"0.3.2": { "0.3.2": {
"bugs": [ "bugs": [
"ConstantOptimizerSubtraction",
"IdentityPrecompileReturnIgnored", "IdentityPrecompileReturnIgnored",
"HighOrderByteCleanStorage", "HighOrderByteCleanStorage",
"OptimizerStaleKnowledgeAboutSHA3", "OptimizerStaleKnowledgeAboutSHA3",
@ -186,6 +200,7 @@
}, },
"0.3.3": { "0.3.3": {
"bugs": [ "bugs": [
"ConstantOptimizerSubtraction",
"IdentityPrecompileReturnIgnored", "IdentityPrecompileReturnIgnored",
"HighOrderByteCleanStorage", "HighOrderByteCleanStorage",
"OptimizerStaleKnowledgeAboutSHA3", "OptimizerStaleKnowledgeAboutSHA3",
@ -197,6 +212,7 @@
}, },
"0.3.4": { "0.3.4": {
"bugs": [ "bugs": [
"ConstantOptimizerSubtraction",
"IdentityPrecompileReturnIgnored", "IdentityPrecompileReturnIgnored",
"HighOrderByteCleanStorage", "HighOrderByteCleanStorage",
"OptimizerStaleKnowledgeAboutSHA3", "OptimizerStaleKnowledgeAboutSHA3",
@ -208,6 +224,7 @@
}, },
"0.3.5": { "0.3.5": {
"bugs": [ "bugs": [
"ConstantOptimizerSubtraction",
"IdentityPrecompileReturnIgnored", "IdentityPrecompileReturnIgnored",
"HighOrderByteCleanStorage", "HighOrderByteCleanStorage",
"OptimizerStaleKnowledgeAboutSHA3", "OptimizerStaleKnowledgeAboutSHA3",
@ -219,6 +236,7 @@
}, },
"0.3.6": { "0.3.6": {
"bugs": [ "bugs": [
"ConstantOptimizerSubtraction",
"IdentityPrecompileReturnIgnored", "IdentityPrecompileReturnIgnored",
"HighOrderByteCleanStorage", "HighOrderByteCleanStorage",
"OptimizerStaleKnowledgeAboutSHA3", "OptimizerStaleKnowledgeAboutSHA3",
@ -228,6 +246,7 @@
}, },
"0.4.0": { "0.4.0": {
"bugs": [ "bugs": [
"ConstantOptimizerSubtraction",
"IdentityPrecompileReturnIgnored", "IdentityPrecompileReturnIgnored",
"HighOrderByteCleanStorage", "HighOrderByteCleanStorage",
"OptimizerStaleKnowledgeAboutSHA3", "OptimizerStaleKnowledgeAboutSHA3",
@ -237,6 +256,7 @@
}, },
"0.4.1": { "0.4.1": {
"bugs": [ "bugs": [
"ConstantOptimizerSubtraction",
"IdentityPrecompileReturnIgnored", "IdentityPrecompileReturnIgnored",
"HighOrderByteCleanStorage", "HighOrderByteCleanStorage",
"OptimizerStaleKnowledgeAboutSHA3", "OptimizerStaleKnowledgeAboutSHA3",
@ -245,11 +265,14 @@
"released": "2016-09-09" "released": "2016-09-09"
}, },
"0.4.10": { "0.4.10": {
"bugs": [], "bugs": [
"ConstantOptimizerSubtraction"
],
"released": "2017-03-15" "released": "2017-03-15"
}, },
"0.4.2": { "0.4.2": {
"bugs": [ "bugs": [
"ConstantOptimizerSubtraction",
"IdentityPrecompileReturnIgnored", "IdentityPrecompileReturnIgnored",
"HighOrderByteCleanStorage", "HighOrderByteCleanStorage",
"OptimizerStaleKnowledgeAboutSHA3" "OptimizerStaleKnowledgeAboutSHA3"
@ -258,6 +281,7 @@
}, },
"0.4.3": { "0.4.3": {
"bugs": [ "bugs": [
"ConstantOptimizerSubtraction",
"IdentityPrecompileReturnIgnored", "IdentityPrecompileReturnIgnored",
"HighOrderByteCleanStorage" "HighOrderByteCleanStorage"
], ],
@ -265,12 +289,14 @@
}, },
"0.4.4": { "0.4.4": {
"bugs": [ "bugs": [
"ConstantOptimizerSubtraction",
"IdentityPrecompileReturnIgnored" "IdentityPrecompileReturnIgnored"
], ],
"released": "2016-10-31" "released": "2016-10-31"
}, },
"0.4.5": { "0.4.5": {
"bugs": [ "bugs": [
"ConstantOptimizerSubtraction",
"IdentityPrecompileReturnIgnored", "IdentityPrecompileReturnIgnored",
"OptimizerStateKnowledgeNotResetForJumpdest" "OptimizerStateKnowledgeNotResetForJumpdest"
], ],
@ -278,20 +304,27 @@
}, },
"0.4.6": { "0.4.6": {
"bugs": [ "bugs": [
"ConstantOptimizerSubtraction",
"IdentityPrecompileReturnIgnored" "IdentityPrecompileReturnIgnored"
], ],
"released": "2016-11-22" "released": "2016-11-22"
}, },
"0.4.7": { "0.4.7": {
"bugs": [], "bugs": [
"ConstantOptimizerSubtraction"
],
"released": "2016-12-15" "released": "2016-12-15"
}, },
"0.4.8": { "0.4.8": {
"bugs": [], "bugs": [
"ConstantOptimizerSubtraction"
],
"released": "2017-01-13" "released": "2017-01-13"
}, },
"0.4.9": { "0.4.9": {
"bugs": [], "bugs": [
"ConstantOptimizerSubtraction"
],
"released": "2017-01-31" "released": "2017-01-31"
} }
} }

View File

@ -203,8 +203,13 @@ AssemblyItems ComputeMethod::findRepresentation(u256 const& _value)
u256 powerOfTwo = u256(1) << bits; u256 powerOfTwo = u256(1) << bits;
u256 upperPart = _value >> bits; u256 upperPart = _value >> bits;
bigint lowerPart = _value & (powerOfTwo - 1); bigint lowerPart = _value & (powerOfTwo - 1);
if (abs(powerOfTwo - lowerPart) < lowerPart) if ((powerOfTwo - lowerPart) < lowerPart)
{
lowerPart = lowerPart - powerOfTwo; // make it negative lowerPart = lowerPart - powerOfTwo; // make it negative
upperPart++;
}
if (upperPart == 0)
continue;
if (abs(lowerPart) >= (powerOfTwo >> 8)) if (abs(lowerPart) >= (powerOfTwo >> 8))
continue; continue;
@ -212,7 +217,7 @@ AssemblyItems ComputeMethod::findRepresentation(u256 const& _value)
if (lowerPart != 0) if (lowerPart != 0)
newRoutine += findRepresentation(u256(abs(lowerPart))); newRoutine += findRepresentation(u256(abs(lowerPart)));
newRoutine += AssemblyItems{u256(bits), u256(2), Instruction::EXP}; newRoutine += AssemblyItems{u256(bits), u256(2), Instruction::EXP};
if (upperPart != 1 && upperPart != 0) if (upperPart != 1)
newRoutine += findRepresentation(upperPart) + AssemblyItems{Instruction::MUL}; newRoutine += findRepresentation(upperPart) + AssemblyItems{Instruction::MUL};
if (lowerPart > 0) if (lowerPart > 0)
newRoutine += AssemblyItems{Instruction::ADD}; newRoutine += AssemblyItems{Instruction::ADD};

View File

@ -74,16 +74,17 @@ public:
void compileBothVersions( void compileBothVersions(
std::string const& _sourceCode, std::string const& _sourceCode,
u256 const& _value = 0, u256 const& _value = 0,
std::string const& _contractName = "" std::string const& _contractName = "",
unsigned const _optimizeRuns = 200
) )
{ {
bytes nonOptimizedBytecode = compileAndRunWithOptimizer(_sourceCode, _value, _contractName, false); bytes nonOptimizedBytecode = compileAndRunWithOptimizer(_sourceCode, _value, _contractName, false, _optimizeRuns);
m_nonOptimizedContract = m_contractAddress; m_nonOptimizedContract = m_contractAddress;
bytes optimizedBytecode = compileAndRunWithOptimizer(_sourceCode, _value, _contractName, true); bytes optimizedBytecode = compileAndRunWithOptimizer(_sourceCode, _value, _contractName, true, _optimizeRuns);
size_t nonOptimizedSize = numInstructions(nonOptimizedBytecode); size_t nonOptimizedSize = numInstructions(nonOptimizedBytecode);
size_t optimizedSize = numInstructions(optimizedBytecode); size_t optimizedSize = numInstructions(optimizedBytecode);
BOOST_CHECK_MESSAGE( BOOST_CHECK_MESSAGE(
optimizedSize < nonOptimizedSize, _optimizeRuns < 50 || optimizedSize < nonOptimizedSize,
string("Optimizer did not reduce bytecode size. Non-optimized size: ") + string("Optimizer did not reduce bytecode size. Non-optimized size: ") +
std::to_string(nonOptimizedSize) + " - optimized size: " + std::to_string(nonOptimizedSize) + " - optimized size: " +
std::to_string(optimizedSize) std::to_string(optimizedSize)
@ -1191,31 +1192,42 @@ BOOST_AUTO_TEST_CASE(clear_unreachable_code)
BOOST_AUTO_TEST_CASE(computing_constants) BOOST_AUTO_TEST_CASE(computing_constants)
{ {
char const* sourceCode = R"( char const* sourceCode = R"(
contract c { contract C {
uint a; uint m_a;
uint b; uint m_b;
uint c; uint m_c;
function set() returns (uint a, uint b, uint c) { uint m_d;
a = 0x77abc0000000000000000000000000000000000000000000000000000000001; function C() {
b = 0x817416927846239487123469187231298734162934871263941234127518276; set();
}
function set() returns (uint) {
m_a = 0x77abc0000000000000000000000000000000000000000000000000000000001;
m_b = 0x817416927846239487123469187231298734162934871263941234127518276;
g(); g();
return 1;
} }
function g() { function g() {
b = 0x817416927846239487123469187231298734162934871263941234127518276; m_b = 0x817416927846239487123469187231298734162934871263941234127518276;
c = 0x817416927846239487123469187231298734162934871263941234127518276; m_c = 0x817416927846239487123469187231298734162934871263941234127518276;
h();
} }
function get() returns (uint ra, uint rb, uint rc) { function h() {
ra = a; m_d = 0xff05694900000000000000000000000000000000000000000000000000000000;
rb = b; }
rc = c ; function get() returns (uint ra, uint rb, uint rc, uint rd) {
ra = m_a;
rb = m_b;
rc = m_c;
rd = m_d;
} }
} }
)"; )";
compileBothVersions(sourceCode); compileBothVersions(sourceCode, 0, "C", 1);
compareVersions("get()");
compareVersions("set()"); compareVersions("set()");
compareVersions("get()"); compareVersions("get()");
bytes optimizedBytecode = compileAndRunWithOptimizer(sourceCode, 0, "c", true, 1); bytes optimizedBytecode = compileAndRunWithOptimizer(sourceCode, 0, "C", true, 1);
bytes complicatedConstant = toBigEndian(u256("0x817416927846239487123469187231298734162934871263941234127518276")); bytes complicatedConstant = toBigEndian(u256("0x817416927846239487123469187231298734162934871263941234127518276"));
unsigned occurrences = 0; unsigned occurrences = 0;
for (auto iter = optimizedBytecode.cbegin(); iter < optimizedBytecode.cend(); ++occurrences) for (auto iter = optimizedBytecode.cbegin(); iter < optimizedBytecode.cend(); ++occurrences)