From daa69df447e167fe75d57a5bbbabee4d637218a4 Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Mon, 9 Apr 2018 15:22:45 +0200 Subject: Error on invalid arithmetic with constant expressions. --- Changelog.md | 1 + libsolidity/analysis/StaticAnalyzer.cpp | 42 ++++++++++++++++++++++ libsolidity/analysis/StaticAnalyzer.h | 2 ++ libsolidity/ast/Types.h | 3 ++ test/libsolidity/SolidityEndToEndTest.cpp | 12 +++---- test/libsolidity/SolidityNameAndTypeResolution.cpp | 14 -------- .../constants/addmod_mulmod_rational.sol | 7 ++++ .../syntaxTests/constants/addmod_zero.sol | 11 ++++++ .../syntaxTests/constants/division_by_zero.sol | 9 +++++ .../syntaxTests/constants/mod_div_rational.sol | 6 ++++ .../libsolidity/syntaxTests/constants/mod_zero.sol | 9 +++++ .../syntaxTests/constants/mulmod_zero.sol | 11 ++++++ .../syntaxTests/signed_rational_modulus.sol | 8 +++++ 13 files changed, 115 insertions(+), 20 deletions(-) create mode 100644 test/libsolidity/syntaxTests/constants/addmod_mulmod_rational.sol create mode 100644 test/libsolidity/syntaxTests/constants/addmod_zero.sol create mode 100644 test/libsolidity/syntaxTests/constants/division_by_zero.sol create mode 100644 test/libsolidity/syntaxTests/constants/mod_div_rational.sol create mode 100644 test/libsolidity/syntaxTests/constants/mod_zero.sol create mode 100644 test/libsolidity/syntaxTests/constants/mulmod_zero.sol create mode 100644 test/libsolidity/syntaxTests/signed_rational_modulus.sol diff --git a/Changelog.md b/Changelog.md index 1612fe3b..58ca2705 100644 --- a/Changelog.md +++ b/Changelog.md @@ -35,6 +35,7 @@ Bugfixes: * Type System: Make external library functions accessible. * Type System: Prevent encoding of weird types. * Static Analyzer: Fix non-deterministic order of unused variable warnings. + * Static Analyzer: Invalid arithmetic with constant expressions causes errors. ### 0.4.21 (2018-03-07) diff --git a/libsolidity/analysis/StaticAnalyzer.cpp b/libsolidity/analysis/StaticAnalyzer.cpp index 33b0e296..51aa0b28 100644 --- a/libsolidity/analysis/StaticAnalyzer.cpp +++ b/libsolidity/analysis/StaticAnalyzer.cpp @@ -21,6 +21,7 @@ */ #include +#include #include #include #include @@ -231,6 +232,47 @@ bool StaticAnalyzer::visit(InlineAssembly const& _inlineAssembly) return true; } +bool StaticAnalyzer::visit(BinaryOperation const& _operation) +{ + if ( + _operation.rightExpression().annotation().isPure && + (_operation.getOperator() == Token::Div || _operation.getOperator() == Token::Mod) + ) + if (auto rhs = dynamic_pointer_cast( + ConstantEvaluator(m_errorReporter).evaluate(_operation.rightExpression()) + )) + if (rhs->isZero()) + m_errorReporter.typeError( + _operation.location(), + (_operation.getOperator() == Token::Div) ? "Division by zero." : "Modulo zero." + ); + + return true; +} + +bool StaticAnalyzer::visit(FunctionCall const& _functionCall) +{ + if (_functionCall.annotation().kind == FunctionCallKind::FunctionCall) + { + auto functionType = dynamic_pointer_cast(_functionCall.expression().annotation().type); + solAssert(functionType, ""); + if (functionType->kind() == FunctionType::Kind::AddMod || functionType->kind() == FunctionType::Kind::MulMod) + { + solAssert(_functionCall.arguments().size() == 3, ""); + if (_functionCall.arguments()[2]->annotation().isPure) + if (auto lastArg = dynamic_pointer_cast( + ConstantEvaluator(m_errorReporter).evaluate(*(_functionCall.arguments())[2]) + )) + if (lastArg->isZero()) + m_errorReporter.typeError( + _functionCall.location(), + "Arithmetic modulo zero." + ); + } + } + return true; +} + bigint StaticAnalyzer::structureSizeEstimate(Type const& _type, set& _structsSeen) { switch (_type.category()) diff --git a/libsolidity/analysis/StaticAnalyzer.h b/libsolidity/analysis/StaticAnalyzer.h index 0a806bbd..2a62e391 100644 --- a/libsolidity/analysis/StaticAnalyzer.h +++ b/libsolidity/analysis/StaticAnalyzer.h @@ -64,6 +64,8 @@ private: virtual bool visit(Return const& _return) override; virtual bool visit(MemberAccess const& _memberAccess) override; virtual bool visit(InlineAssembly const& _inlineAssembly) override; + virtual bool visit(BinaryOperation const& _operation) override; + virtual bool visit(FunctionCall const& _functionCall) override; /// @returns the size of this type in storage, including all sub-types. static bigint structureSizeEstimate(Type const& _type, std::set& _structsSeen); diff --git a/libsolidity/ast/Types.h b/libsolidity/ast/Types.h index 05f506f1..ecfc2333 100644 --- a/libsolidity/ast/Types.h +++ b/libsolidity/ast/Types.h @@ -446,6 +446,9 @@ public: /// @returns true if the value is negative. bool isNegative() const { return m_value < 0; } + /// @returns true if the value is zero. + bool isZero() const { return m_value == 0; } + private: rational m_value; diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index 39f4b03e..f7f1062d 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -7847,12 +7847,12 @@ BOOST_AUTO_TEST_CASE(addmod_mulmod_zero) { char const* sourceCode = R"( contract C { - function f() pure returns (uint) { - addmod(1, 2, 0); + function f(uint d) pure returns (uint) { + addmod(1, 2, d); return 2; } - function g() pure returns (uint) { - mulmod(1, 2, 0); + function g(uint d) pure returns (uint) { + mulmod(1, 2, d); return 2; } function h() pure returns (uint) { @@ -7865,8 +7865,8 @@ BOOST_AUTO_TEST_CASE(addmod_mulmod_zero) } )"; compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("f()"), encodeArgs()); - ABI_CHECK(callContractFunction("g()"), encodeArgs()); + ABI_CHECK(callContractFunction("f(uint)", 0), encodeArgs()); + ABI_CHECK(callContractFunction("g(uint)", 0), encodeArgs()); ABI_CHECK(callContractFunction("h()"), encodeArgs(2)); } diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index 293f5f44..9847ecae 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -4920,20 +4920,6 @@ BOOST_AUTO_TEST_CASE(integer_and_fixed_interaction) CHECK_SUCCESS(text); } -BOOST_AUTO_TEST_CASE(signed_rational_modulus) -{ - char const* text = R"( - contract test { - function f() public { - fixed a = 0.42578125 % -0.4271087646484375; - fixed b = .5 % a; - fixed c = a % b; - } - } - )"; - CHECK_SUCCESS(text); -} - BOOST_AUTO_TEST_CASE(one_divided_by_three_integer_conversion) { char const* text = R"( diff --git a/test/libsolidity/syntaxTests/constants/addmod_mulmod_rational.sol b/test/libsolidity/syntaxTests/constants/addmod_mulmod_rational.sol new file mode 100644 index 00000000..26712735 --- /dev/null +++ b/test/libsolidity/syntaxTests/constants/addmod_mulmod_rational.sol @@ -0,0 +1,7 @@ +contract C { + uint constant a = addmod(3, 4, 0.1); + uint constant b = mulmod(3, 4, 0.1); +} +// ---- +// TypeError: (48-51): Invalid type for argument in function call. Invalid implicit conversion from rational_const 1 / 10 to uint256 requested. +// TypeError: (89-92): Invalid type for argument in function call. Invalid implicit conversion from rational_const 1 / 10 to uint256 requested. diff --git a/test/libsolidity/syntaxTests/constants/addmod_zero.sol b/test/libsolidity/syntaxTests/constants/addmod_zero.sol new file mode 100644 index 00000000..18f7d64a --- /dev/null +++ b/test/libsolidity/syntaxTests/constants/addmod_zero.sol @@ -0,0 +1,11 @@ +contract c { + uint constant a1 = 0; + uint constant a2 = 1; + uint constant b1 = addmod(3, 4, 0); + uint constant b2 = addmod(3, 4, a1); + uint constant b3 = addmod(3, 4, a2 - 1); +} +// ---- +// TypeError: (88-103): Arithmetic modulo zero. +// TypeError: (128-144): Arithmetic modulo zero. +// TypeError: (169-189): Arithmetic modulo zero. diff --git a/test/libsolidity/syntaxTests/constants/division_by_zero.sol b/test/libsolidity/syntaxTests/constants/division_by_zero.sol new file mode 100644 index 00000000..bf6000ec --- /dev/null +++ b/test/libsolidity/syntaxTests/constants/division_by_zero.sol @@ -0,0 +1,9 @@ +contract c { + uint constant a1 = 0; + uint constant a2 = 1; + uint constant b1 = 7 / a1; + uint constant b2 = 7 / (a2 - 1); +} +// ---- +// TypeError: (88-94): Division by zero. +// TypeError: (119-131): Division by zero. diff --git a/test/libsolidity/syntaxTests/constants/mod_div_rational.sol b/test/libsolidity/syntaxTests/constants/mod_div_rational.sol new file mode 100644 index 00000000..f8b6ce31 --- /dev/null +++ b/test/libsolidity/syntaxTests/constants/mod_div_rational.sol @@ -0,0 +1,6 @@ +contract C { + fixed a1 = 0.1 % -0.4271087646484375; + fixed a2 = 0.1 % 0.4271087646484375; + fixed a3 = 0 / 0.123; + fixed a4 = 0 / -0.123; +} diff --git a/test/libsolidity/syntaxTests/constants/mod_zero.sol b/test/libsolidity/syntaxTests/constants/mod_zero.sol new file mode 100644 index 00000000..f5e4a23a --- /dev/null +++ b/test/libsolidity/syntaxTests/constants/mod_zero.sol @@ -0,0 +1,9 @@ +contract c { + uint constant a1 = 0; + uint constant a2 = 1; + uint constant b1 = 3 % a1; + uint constant b2 = 3 % (a2 - 1); +} +// ---- +// TypeError: (88-94): Modulo zero. +// TypeError: (119-131): Modulo zero. diff --git a/test/libsolidity/syntaxTests/constants/mulmod_zero.sol b/test/libsolidity/syntaxTests/constants/mulmod_zero.sol new file mode 100644 index 00000000..856d01eb --- /dev/null +++ b/test/libsolidity/syntaxTests/constants/mulmod_zero.sol @@ -0,0 +1,11 @@ +contract c { + uint constant a1 = 0; + uint constant a2 = 1; + uint constant b1 = mulmod(3, 4, 0); + uint constant b2 = mulmod(3, 4, a1); + uint constant b3 = mulmod(3, 4, a2 - 1); +} +// ---- +// TypeError: (88-103): Arithmetic modulo zero. +// TypeError: (128-144): Arithmetic modulo zero. +// TypeError: (169-189): Arithmetic modulo zero. diff --git a/test/libsolidity/syntaxTests/signed_rational_modulus.sol b/test/libsolidity/syntaxTests/signed_rational_modulus.sol new file mode 100644 index 00000000..b37d33d0 --- /dev/null +++ b/test/libsolidity/syntaxTests/signed_rational_modulus.sol @@ -0,0 +1,8 @@ +contract test { + function f() public pure { + fixed a = 0.42578125 % -0.4271087646484375; + fixed b = .5 % a; + fixed c = a % b; + a; b; c; + } +} -- cgit v1.2.3 From 932915633bb7f5985023f80a8c704cf8c5f979fa Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Wed, 11 Apr 2018 23:20:40 +0200 Subject: Add tests for literal operations as well. --- test/libsolidity/syntaxTests/literalOperations/division_by_zero.sol | 5 +++++ .../syntaxTests/literalOperations/division_by_zero_complex.sol | 5 +++++ test/libsolidity/syntaxTests/literalOperations/mod_zero.sol | 5 +++++ test/libsolidity/syntaxTests/literalOperations/mod_zero_complex.sol | 5 +++++ 4 files changed, 20 insertions(+) create mode 100644 test/libsolidity/syntaxTests/literalOperations/division_by_zero.sol create mode 100644 test/libsolidity/syntaxTests/literalOperations/division_by_zero_complex.sol create mode 100644 test/libsolidity/syntaxTests/literalOperations/mod_zero.sol create mode 100644 test/libsolidity/syntaxTests/literalOperations/mod_zero_complex.sol diff --git a/test/libsolidity/syntaxTests/literalOperations/division_by_zero.sol b/test/libsolidity/syntaxTests/literalOperations/division_by_zero.sol new file mode 100644 index 00000000..b52b4c51 --- /dev/null +++ b/test/libsolidity/syntaxTests/literalOperations/division_by_zero.sol @@ -0,0 +1,5 @@ +contract C { + uint constant a = 1 / 0; +} +// ---- +// TypeError: (35-40): Operator / not compatible with types int_const 1 and int_const 0 diff --git a/test/libsolidity/syntaxTests/literalOperations/division_by_zero_complex.sol b/test/libsolidity/syntaxTests/literalOperations/division_by_zero_complex.sol new file mode 100644 index 00000000..8cc3b6f2 --- /dev/null +++ b/test/libsolidity/syntaxTests/literalOperations/division_by_zero_complex.sol @@ -0,0 +1,5 @@ +contract C { + uint constant a = 1 / ((1+3)-4); +} +// ---- +// TypeError: (35-48): Operator / not compatible with types int_const 1 and int_const 0 diff --git a/test/libsolidity/syntaxTests/literalOperations/mod_zero.sol b/test/libsolidity/syntaxTests/literalOperations/mod_zero.sol new file mode 100644 index 00000000..1bbbc3fc --- /dev/null +++ b/test/libsolidity/syntaxTests/literalOperations/mod_zero.sol @@ -0,0 +1,5 @@ +contract C { + uint constant b3 = 1 % 0; +} +// ---- +// TypeError: (36-41): Operator % not compatible with types int_const 1 and int_const 0 diff --git a/test/libsolidity/syntaxTests/literalOperations/mod_zero_complex.sol b/test/libsolidity/syntaxTests/literalOperations/mod_zero_complex.sol new file mode 100644 index 00000000..4899cac3 --- /dev/null +++ b/test/libsolidity/syntaxTests/literalOperations/mod_zero_complex.sol @@ -0,0 +1,5 @@ +contract C { + uint constant b3 = 1 % (-4+((2)*2)); +} +// ---- +// TypeError: (36-52): Operator % not compatible with types int_const 1 and int_const 0 -- cgit v1.2.3 From d42476e241489447e5dc4f5b1fafc8000e635fbc Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Thu, 12 Apr 2018 10:39:13 +0200 Subject: Add test to check ConstantEvaluator for pure non-rational functions. --- test/libsolidity/syntaxTests/constants/pure_non_rational.sol | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 test/libsolidity/syntaxTests/constants/pure_non_rational.sol diff --git a/test/libsolidity/syntaxTests/constants/pure_non_rational.sol b/test/libsolidity/syntaxTests/constants/pure_non_rational.sol new file mode 100644 index 00000000..4b96f1c7 --- /dev/null +++ b/test/libsolidity/syntaxTests/constants/pure_non_rational.sol @@ -0,0 +1,11 @@ +// Tests that the ConstantEvaluator does not crash for pure non-rational functions. +// Currently it does not evaluate such functions, but this may change in the future +// causing a division by zero error for a. +contract C { + uint constant a = 1 / (uint(keccak256([0])[0]) - uint(keccak256([0])[0])); + uint constant b = 1 / uint(keccak256([0])); + uint constant c = uint(keccak256([0])); + uint[c] mem; +} +// ---- +// TypeError: (392-393): Invalid array length, expected integer literal or constant expression. -- cgit v1.2.3