diff --git a/Changelog.md b/Changelog.md index 8f3bfff90..997c40697 100644 --- a/Changelog.md +++ b/Changelog.md @@ -91,6 +91,9 @@ Compiler Features: * Code Generator: Allocate and free local variables according to their scope. * Removed ``pragma experimental "v0.5.0";``. +Critical Bugfixes: + * Code Generator: Properly perform cleanup for exponentiation and non-256 bit types. + Bugfixes: * Build System: Support versions of CVC4 linked against CLN instead of GMP. In case of compilation issues due to the experimental SMT solver support, the solvers can be disabled when configuring the project with CMake using ``-DUSE_CVC4=OFF`` or ``-DUSE_Z3=OFF``. * Tests: Fix chain parameters to make ipc tests work with newer versions of cpp-ethereum. diff --git a/docs/bugs.json b/docs/bugs.json index cf03adfe1..980b38975 100644 --- a/docs/bugs.json +++ b/docs/bugs.json @@ -1,4 +1,12 @@ [ + { + "name": "ExpExponentCleanup", + "summary": "Using the ** operator with an exponent of type shorter than 256 bits can result in unexpected values.", + "description": "Higher order bits in the exponent are not properly cleaned before the EXP opcode is applied if the type of the exponent expression is smaller than 256 bits and not smaller than the type of the base. In that case, the result might be larger than expected if the exponent is assumed to lie within the value range of the type. Literal numbers as exponents are unaffected as are exponents or bases of type uint256.", + "fixed": "0.4.25", + "severity": "medium/high", + "check": {"regex-source": "[^/]\\*\\* *[^/0-9 ]"} + }, { "name": "EventStructWrongData", "summary": "Using structs in events logged wrong data.", diff --git a/docs/bugs_by_version.json b/docs/bugs_by_version.json index ab200036b..96cc3c5c5 100644 --- a/docs/bugs_by_version.json +++ b/docs/bugs_by_version.json @@ -1,6 +1,7 @@ { "0.1.0": { "bugs": [ + "ExpExponentCleanup", "ZeroFunctionSelector", "ECRecoverMalformedInput", "SkipEmptyStringLiteral", @@ -18,6 +19,7 @@ }, "0.1.1": { "bugs": [ + "ExpExponentCleanup", "ZeroFunctionSelector", "ECRecoverMalformedInput", "SkipEmptyStringLiteral", @@ -35,6 +37,7 @@ }, "0.1.2": { "bugs": [ + "ExpExponentCleanup", "ZeroFunctionSelector", "ECRecoverMalformedInput", "SkipEmptyStringLiteral", @@ -52,6 +55,7 @@ }, "0.1.3": { "bugs": [ + "ExpExponentCleanup", "ZeroFunctionSelector", "ECRecoverMalformedInput", "SkipEmptyStringLiteral", @@ -69,6 +73,7 @@ }, "0.1.4": { "bugs": [ + "ExpExponentCleanup", "NestedArrayFunctionCallDecoder", "ZeroFunctionSelector", "ECRecoverMalformedInput", @@ -87,6 +92,7 @@ }, "0.1.5": { "bugs": [ + "ExpExponentCleanup", "NestedArrayFunctionCallDecoder", "ZeroFunctionSelector", "ECRecoverMalformedInput", @@ -105,6 +111,7 @@ }, "0.1.6": { "bugs": [ + "ExpExponentCleanup", "NestedArrayFunctionCallDecoder", "ZeroFunctionSelector", "ECRecoverMalformedInput", @@ -124,6 +131,7 @@ }, "0.1.7": { "bugs": [ + "ExpExponentCleanup", "NestedArrayFunctionCallDecoder", "ZeroFunctionSelector", "ECRecoverMalformedInput", @@ -143,6 +151,7 @@ }, "0.2.0": { "bugs": [ + "ExpExponentCleanup", "NestedArrayFunctionCallDecoder", "ZeroFunctionSelector", "ECRecoverMalformedInput", @@ -162,6 +171,7 @@ }, "0.2.1": { "bugs": [ + "ExpExponentCleanup", "NestedArrayFunctionCallDecoder", "ZeroFunctionSelector", "ECRecoverMalformedInput", @@ -181,6 +191,7 @@ }, "0.2.2": { "bugs": [ + "ExpExponentCleanup", "NestedArrayFunctionCallDecoder", "ZeroFunctionSelector", "ECRecoverMalformedInput", @@ -200,6 +211,7 @@ }, "0.3.0": { "bugs": [ + "ExpExponentCleanup", "NestedArrayFunctionCallDecoder", "ZeroFunctionSelector", "DelegateCallReturnValue", @@ -219,6 +231,7 @@ }, "0.3.1": { "bugs": [ + "ExpExponentCleanup", "NestedArrayFunctionCallDecoder", "ZeroFunctionSelector", "DelegateCallReturnValue", @@ -237,6 +250,7 @@ }, "0.3.2": { "bugs": [ + "ExpExponentCleanup", "NestedArrayFunctionCallDecoder", "ZeroFunctionSelector", "DelegateCallReturnValue", @@ -255,6 +269,7 @@ }, "0.3.3": { "bugs": [ + "ExpExponentCleanup", "NestedArrayFunctionCallDecoder", "ZeroFunctionSelector", "DelegateCallReturnValue", @@ -272,6 +287,7 @@ }, "0.3.4": { "bugs": [ + "ExpExponentCleanup", "NestedArrayFunctionCallDecoder", "ZeroFunctionSelector", "DelegateCallReturnValue", @@ -289,6 +305,7 @@ }, "0.3.5": { "bugs": [ + "ExpExponentCleanup", "NestedArrayFunctionCallDecoder", "ZeroFunctionSelector", "DelegateCallReturnValue", @@ -306,6 +323,7 @@ }, "0.3.6": { "bugs": [ + "ExpExponentCleanup", "NestedArrayFunctionCallDecoder", "ZeroFunctionSelector", "DelegateCallReturnValue", @@ -321,6 +339,7 @@ }, "0.4.0": { "bugs": [ + "ExpExponentCleanup", "NestedArrayFunctionCallDecoder", "ZeroFunctionSelector", "DelegateCallReturnValue", @@ -336,6 +355,7 @@ }, "0.4.1": { "bugs": [ + "ExpExponentCleanup", "NestedArrayFunctionCallDecoder", "ZeroFunctionSelector", "DelegateCallReturnValue", @@ -351,6 +371,7 @@ }, "0.4.10": { "bugs": [ + "ExpExponentCleanup", "NestedArrayFunctionCallDecoder", "ZeroFunctionSelector", "DelegateCallReturnValue", @@ -362,6 +383,7 @@ }, "0.4.11": { "bugs": [ + "ExpExponentCleanup", "NestedArrayFunctionCallDecoder", "ZeroFunctionSelector", "DelegateCallReturnValue", @@ -372,6 +394,7 @@ }, "0.4.12": { "bugs": [ + "ExpExponentCleanup", "NestedArrayFunctionCallDecoder", "ZeroFunctionSelector", "DelegateCallReturnValue", @@ -381,6 +404,7 @@ }, "0.4.13": { "bugs": [ + "ExpExponentCleanup", "NestedArrayFunctionCallDecoder", "ZeroFunctionSelector", "DelegateCallReturnValue", @@ -390,6 +414,7 @@ }, "0.4.14": { "bugs": [ + "ExpExponentCleanup", "NestedArrayFunctionCallDecoder", "ZeroFunctionSelector", "DelegateCallReturnValue" @@ -398,6 +423,7 @@ }, "0.4.15": { "bugs": [ + "ExpExponentCleanup", "NestedArrayFunctionCallDecoder", "ZeroFunctionSelector" ], @@ -405,6 +431,7 @@ }, "0.4.16": { "bugs": [ + "ExpExponentCleanup", "NestedArrayFunctionCallDecoder", "ZeroFunctionSelector" ], @@ -412,6 +439,7 @@ }, "0.4.17": { "bugs": [ + "ExpExponentCleanup", "EventStructWrongData", "NestedArrayFunctionCallDecoder", "ZeroFunctionSelector" @@ -420,6 +448,7 @@ }, "0.4.18": { "bugs": [ + "ExpExponentCleanup", "EventStructWrongData", "NestedArrayFunctionCallDecoder" ], @@ -427,6 +456,7 @@ }, "0.4.19": { "bugs": [ + "ExpExponentCleanup", "EventStructWrongData", "NestedArrayFunctionCallDecoder" ], @@ -434,6 +464,7 @@ }, "0.4.2": { "bugs": [ + "ExpExponentCleanup", "NestedArrayFunctionCallDecoder", "ZeroFunctionSelector", "DelegateCallReturnValue", @@ -448,6 +479,7 @@ }, "0.4.20": { "bugs": [ + "ExpExponentCleanup", "EventStructWrongData", "NestedArrayFunctionCallDecoder" ], @@ -455,6 +487,7 @@ }, "0.4.21": { "bugs": [ + "ExpExponentCleanup", "EventStructWrongData", "NestedArrayFunctionCallDecoder" ], @@ -462,6 +495,7 @@ }, "0.4.22": { "bugs": [ + "ExpExponentCleanup", "EventStructWrongData", "OneOfTwoConstructorsSkipped" ], @@ -469,18 +503,21 @@ }, "0.4.23": { "bugs": [ + "ExpExponentCleanup", "EventStructWrongData" ], "released": "2018-04-19" }, "0.4.24": { "bugs": [ + "ExpExponentCleanup", "EventStructWrongData" ], "released": "2018-05-16" }, "0.4.3": { "bugs": [ + "ExpExponentCleanup", "NestedArrayFunctionCallDecoder", "ZeroFunctionSelector", "DelegateCallReturnValue", @@ -494,6 +531,7 @@ }, "0.4.4": { "bugs": [ + "ExpExponentCleanup", "NestedArrayFunctionCallDecoder", "ZeroFunctionSelector", "DelegateCallReturnValue", @@ -506,6 +544,7 @@ }, "0.4.5": { "bugs": [ + "ExpExponentCleanup", "NestedArrayFunctionCallDecoder", "ZeroFunctionSelector", "DelegateCallReturnValue", @@ -519,6 +558,7 @@ }, "0.4.6": { "bugs": [ + "ExpExponentCleanup", "NestedArrayFunctionCallDecoder", "ZeroFunctionSelector", "DelegateCallReturnValue", @@ -531,6 +571,7 @@ }, "0.4.7": { "bugs": [ + "ExpExponentCleanup", "NestedArrayFunctionCallDecoder", "ZeroFunctionSelector", "DelegateCallReturnValue", @@ -542,6 +583,7 @@ }, "0.4.8": { "bugs": [ + "ExpExponentCleanup", "NestedArrayFunctionCallDecoder", "ZeroFunctionSelector", "DelegateCallReturnValue", @@ -553,6 +595,7 @@ }, "0.4.9": { "bugs": [ + "ExpExponentCleanup", "NestedArrayFunctionCallDecoder", "ZeroFunctionSelector", "DelegateCallReturnValue", diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp index 4150bc11a..45e58bd07 100644 --- a/libsolidity/codegen/ExpressionCompiler.cpp +++ b/libsolidity/codegen/ExpressionCompiler.cpp @@ -2138,7 +2138,9 @@ bool ExpressionCompiler::cleanupNeededForOp(Type::Category _type, Token::Value _ { if (Token::isCompareOp(_op) || Token::isShiftOp(_op)) return true; - else if (_type == Type::Category::Integer && (_op == Token::Div || _op == Token::Mod)) + else if (_type == Type::Category::Integer && (_op == Token::Div || _op == Token::Mod || _op == Token::Exp)) + // We need cleanup for EXP because 0**0 == 1, but 0**0x100 == 0 + // It would suffice to clean the exponent, though. return true; else return false; diff --git a/test/buglist_test_vectors.md b/test/buglist_test_vectors.md index f15cf1516..e683f4818 100644 --- a/test/buglist_test_vectors.md +++ b/test/buglist_test_vectors.md @@ -68,6 +68,40 @@ function f() m(uint[2][2]) { } function f() returns (uint, uint) { uint[2][2] memory x; } +# ExpExponentCleanup + +## buggy + +x ** y + +-- + +x ** uint8(y) + +-- + +x**y + +## fine + +x ** 2 + +-- + +x**2 + +-- + +x**200 + +-- + +/** bla **/ + +-- + +/**/ + # EventStructWrongData ## buggy diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index 85582ece8..6be9d95bf 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -11870,6 +11870,46 @@ BOOST_AUTO_TEST_CASE(shift_bytes_cleanup) ABI_CHECK(callContractFunction("right(uint8)", 8 * 8), encodeArgs(string(8, 0) + "123456789012")); } +BOOST_AUTO_TEST_CASE(exp_cleanup) +{ + char const* sourceCode = R"( + contract C { + function f() public pure returns (uint8 x) { + uint8 y = uint8(2) ** uint8(8); + return 0 ** y; + } + } + )"; + compileAndRun(sourceCode, 0, "C"); + ABI_CHECK(callContractFunction("f()"), encodeArgs(u256(0x1))); +} + +BOOST_AUTO_TEST_CASE(exp_cleanup_direct) +{ + char const* sourceCode = R"( + contract C { + function f() public pure returns (uint8 x) { + return uint8(0) ** uint8(uint8(2) ** uint8(8)); + } + } + )"; + compileAndRun(sourceCode, 0, "C"); + ABI_CHECK(callContractFunction("f()"), encodeArgs(u256(0x1))); +} + +BOOST_AUTO_TEST_CASE(exp_cleanup_nonzero_base) +{ + char const* sourceCode = R"( + contract C { + function f() public pure returns (uint8 x) { + return uint8(0x166) ** uint8(uint8(2) ** uint8(8)); + } + } + )"; + compileAndRun(sourceCode, 0, "C"); + ABI_CHECK(callContractFunction("f()"), encodeArgs(u256(0x1))); +} + BOOST_AUTO_TEST_CASE(cleanup_in_compound_assign) { char const* sourceCode = R"(