diff --git a/libsolidity/codegen/YulUtilFunctions.cpp b/libsolidity/codegen/YulUtilFunctions.cpp index 2ecf64183..2fe363e1b 100644 --- a/libsolidity/codegen/YulUtilFunctions.cpp +++ b/libsolidity/codegen/YulUtilFunctions.cpp @@ -374,6 +374,33 @@ string YulUtilFunctions::overflowCheckedUIntMulFunction(size_t _bits) }); } +string YulUtilFunctions::overflowCheckedIntDivFunction(IntegerType const& _type) +{ + unsigned bits = _type.numBits(); + solAssert(0 < bits && bits <= 256 && bits % 8 == 0, ""); + string functionName = "checked_div_" + _type.identifier(); + return m_functionCollector->createFunction(functionName, [&]() { + return + Whiskers(R"( + function (x, y) -> r { + if iszero(y) { revert(0, 0) } + + // x / -1 == x + if and( + eq(x, ), + eq(y, sub(0, 1)) + ) { revert(0, 0) } + + r := sdiv(x, y) + } + )") + ("functionName", functionName) + ("signed", _type.isSigned()) + ("minVal", (0 - (u256(1) << (bits - 1))).str()) + .render(); + }); +} + string YulUtilFunctions::overflowCheckedUIntSubFunction() { string functionName = "checked_sub_uint"; diff --git a/libsolidity/codegen/YulUtilFunctions.h b/libsolidity/codegen/YulUtilFunctions.h index 7128ecd3a..245d29c12 100644 --- a/libsolidity/codegen/YulUtilFunctions.h +++ b/libsolidity/codegen/YulUtilFunctions.h @@ -36,6 +36,7 @@ namespace solidity class Type; class ArrayType; class MappingType; +class IntegerType; /** * Component that can generate various useful Yul functions. @@ -88,6 +89,11 @@ public: std::string overflowCheckedUIntMulFunction(size_t _bits); + /// @returns name of function to perform division on integers. + /// Checks for division by zero and the special case of + /// signed division of the smallest number by -1. + std::string overflowCheckedIntDivFunction(IntegerType const& _type); + /// @returns computes the difference between two values. /// Assumes the input to be in range for the type. std::string overflowCheckedUIntSubFunction(); diff --git a/libsolidity/codegen/ir/IRGeneratorForStatements.cpp b/libsolidity/codegen/ir/IRGeneratorForStatements.cpp index bee036d38..d5975b717 100644 --- a/libsolidity/codegen/ir/IRGeneratorForStatements.cpp +++ b/libsolidity/codegen/ir/IRGeneratorForStatements.cpp @@ -391,7 +391,6 @@ bool IRGeneratorForStatements::visit(BinaryOperation const& _binOp) { string left = expressionAsType(_binOp.leftExpression(), *commonType); string right = expressionAsType(_binOp.rightExpression(), *commonType); - defineExpression(_binOp) << binaryOperation(_binOp.getOperator(), *commonType, left, right); } return false; @@ -1097,16 +1096,20 @@ string IRGeneratorForStatements::binaryOperation( { if (IntegerType const* type = dynamic_cast(&_type)) { - solUnimplementedAssert(!type->isSigned(), ""); string fun; - if (_operator == Token::Add) - fun = m_utils.overflowCheckedUIntAddFunction(type->numBits()); - else if (_operator == Token::Sub) - fun = m_utils.overflowCheckedUIntSubFunction(); - else if (_operator == Token::Mul) - fun = m_utils.overflowCheckedUIntMulFunction(type->numBits()); - else - solUnimplementedAssert(false, ""); + // TODO: Only division is implemented for signed integers for now. + if (!type->isSigned()) + { + if (_operator == Token::Add) + fun = m_utils.overflowCheckedUIntAddFunction(type->numBits()); + else if (_operator == Token::Sub) + fun = m_utils.overflowCheckedUIntSubFunction(); + else if (_operator == Token::Mul) + fun = m_utils.overflowCheckedUIntMulFunction(type->numBits()); + } + if (_operator == Token::Div) + fun = m_utils.overflowCheckedIntDivFunction(*type); + solUnimplementedAssert(!fun.empty(), ""); return fun + "(" + _left + ", " + _right + ")\n"; } else diff --git a/test/libsolidity/semanticTests/viaYul/detect_div_overflow.sol b/test/libsolidity/semanticTests/viaYul/detect_div_overflow.sol new file mode 100644 index 000000000..1de251b61 --- /dev/null +++ b/test/libsolidity/semanticTests/viaYul/detect_div_overflow.sol @@ -0,0 +1,27 @@ +contract C { + function f(uint a, uint b) public pure returns (uint x) { + x = a / b; + } + function g(int8 a, int8 b) public pure returns (int8 x) { + x = a / b; + } + function h(uint256 a, uint256 b) public pure returns (uint256 x) { + x = a / b; + } +} +// ==== +// compileViaYul: true +// ---- +// f(uint256,uint256): 10, 3 -> 3 +// f(uint256,uint256): 1, 0 -> FAILURE +// f(uint256,uint256): 0, 0 -> FAILURE +// f(uint256,uint256): 0, 1 -> 0 +// g(int8,int8): -10, 3 -> -3 +// g(int8,int8): -10, -3 -> 3 +// g(int8,int8): -10, 0 -> FAILURE +// g(int8,int8): -128, 1 -> -128 +// g(int8,int8): -128, -2 -> 64 +// g(int8,int8): -128, 2 -> -64 +// g(int8,int8): -128, -1 -> FAILURE +// g(int8,int8): -127, -1 -> 127 +// h(uint256,uint256): 0x8000000000000000000000000000000000000000000000000000000000000000, -1 -> 0