diff --git a/Changelog.md b/Changelog.md index 88ae1d1e5..5fe86364c 100644 --- a/Changelog.md +++ b/Changelog.md @@ -2,6 +2,7 @@ Breaking Changes: * Assembler: The artificial ASSIGNIMMUTABLE opcode and the corresponding builtin in the "EVM with object access" dialect of Yul take the base offset of the code to modify as additional argument. + * Code Generator: All arithmetic is checked by default. These checks can be disabled using ``unchecked { ... }``. * Type System: Unary negation can only be used on signed integers, not on unsigned integers. * Type System: Disallow explicit conversions from negative literals and literals larger than ``type(uint160).max`` to ``address`` type. * Parser: Exponentiation is right associative. ``a**b**c`` is parsed as ``a**(b**c)``. diff --git a/docs/control-structures.rst b/docs/control-structures.rst index 9cd670091..b9aab52be 100644 --- a/docs/control-structures.rst +++ b/docs/control-structures.rst @@ -475,6 +475,69 @@ In any case, you will get a warning about the outer variable being shadowed. } } + +.. _unchecked: + +Checked or Unchecked Arithmetic +=============================== + +An overflow or underflow is the situation where the resulting value of an arithmetic operation, +when executed on an unrestricted integer, falls outside the range of the result type. + +Prior to Solidity 0.8.0, arithmetic operations would always wrap in case of +under- or overflow leading to widespread use of libraries that introduce +additional checks. + +Since Solidity 0.8.0, all arithmetic operations revert on over- and underflow by default, +thus making the use of these libraries unnecessary. + +To obtain the previous behaviour, an ``unchecked`` block can be used: + +:: + + // SPDX-License-Identifier: GPL-3.0 + pragma solidity >0.7.99; + contract C { + function f(uint a, uint b) pure public returns (uint) { + // This addition will wrap on underflow. + unchecked { return a - b; } + } + function g(uint a, uint b) pure public returns (uint) { + // This addition will revert on underflow. + return a - b; + } + } + +The call to ``f(2, 3)`` will return ``2**256-1``, while ``g(2, 3)`` will cause +a failing assertion. + +The ``unchecked`` block can be used everywhere inside a block, but not as a replacement +for a block. It also cannot be nested. + +The setting only affects the statements that are syntactically inside the block. +Functions called from within an ``unchecked`` block do not inherit the property. + +.. note:: + To avoid ambiguity, you cannot use ``_;`` inside an ``unchecked`` block. + +The following operators will cause a failing assertion on overflow or underflow +and will wrap without an error if used inside an unchecked block: + +``++``, ``--``, ``+``, binary ``-``, unary ``-``, ``*``, ``/``, ``%``, ``**`` + +``+=``, ``-=``, ``*=``, ``/=``, ``%=`` + +.. warning:: + It is not possible to disable the check for division by zero + or modulo by zero using the ``unchecked`` block. + +.. note:: + The second statement in ``int x = type(int).min; -x;`` will result in an overflow + because the negative range can hold one more value than the positive range. + +Explicit type conversions will always truncate and never cause a failing assertion +with the exception of a conversion from an integer to an enum type. + .. index:: ! exception, ! throw, ! assert, ! require, ! revert, ! errors .. _assert-and-require: @@ -519,6 +582,7 @@ An ``assert``-style exception is generated in the following situations: #. If you access an array or an array slice at a too large or negative index (i.e. ``x[i]`` where ``i >= x.length`` or ``i < 0``). #. If you access a fixed-length ``bytesN`` at a too large or negative index. #. If you divide or modulo by zero (e.g. ``5 / 0`` or ``23 % 0``). +#. If an arithmetic operation results in under- or overflow outside of an ``unchecked { ... }`` block. #. If you convert a value too big or negative into an enum type. #. If you call a zero-initialized variable of internal function type. #. If you call ``assert`` with an argument that evaluates to false. diff --git a/docs/security-considerations.rst b/docs/security-considerations.rst index f3da86594..ce768eb75 100644 --- a/docs/security-considerations.rst +++ b/docs/security-considerations.rst @@ -246,21 +246,32 @@ Two's Complement / Underflows / Overflows ========================================= As in many programming languages, Solidity's integer types are not actually integers. -They resemble integers when the values are small, but behave differently if the numbers are larger. -For example, the following is true: ``uint8(255) + uint8(1) == 0``. This situation is called -an *overflow*. It occurs when an operation is performed that requires a fixed size variable -to store a number (or piece of data) that is outside the range of the variable's data type. -An *underflow* is the converse situation: ``uint8(0) - uint8(1) == 255``. +They resemble integers when the values are small, but cannot represent arbitrarily large numbers. + +The following code causes an overflow because the result of the addition is too large +to be stored in the type ``uint8``: + +:: + + uint8 x = 255; + uint8 y = 1; + return x + y; + +Solidity has two modes in which it deals with these overflows: Checked and Unchecked or "wrapping" mode. + +The default checked mode will detect overflows and cause a failing assertion. You can disable this check +using ``unchecked { ... }``, causing the overflow to be silently ignored. The above code would return +``0`` if wrapped in ``unchecked { ... }``. + +Even in checked mode, do not assume you are protected from overflow bugs. +In this mode, overflows will always revert. If it is not possible to avoid the +overflow, this can lead to a smart contract being stuck in a certain state. In general, read about the limits of two's complement representation, which even has some more special edge cases for signed numbers. Try to use ``require`` to limit the size of inputs to a reasonable range and use the -:ref:`SMT checker` to find potential overflows, or use a library like -`SafeMath `_ -if you want all overflows to cause a revert. - -Code such as ``require((balanceOf[_to] + _value) >= balanceOf[_to])`` can also help you check if values are what you expect. +:ref:`SMT checker` to find potential overflows. .. _clearing-mappings: diff --git a/docs/types/value-types.rst b/docs/types/value-types.rst index e05685c9c..996d88a63 100644 --- a/docs/types/value-types.rst +++ b/docs/types/value-types.rst @@ -46,8 +46,10 @@ access the minimum and maximum value representable by the type. .. warning:: Integers in Solidity are restricted to a certain range. For example, with ``uint32``, this is ``0`` up to ``2**32 - 1``. - If the result of some operation on those numbers does not fit inside this range, it is truncated. These truncations can have - serious consequences that you should :ref:`be aware of and mitigate against`. + There are two modes in which arithmetic is performed on these types: The "wrapping" or "unchecked" mode and the "checked" mode. + By default, arithmetic is always "checked", which mean that if the result of an operation falls outside the value range + of the type, the call is reverted through a :ref:`failing assertion`. You can switch to "unchecked" mode + using ``unchecked { ... }``. More details can be found in the section about :ref:`unchecked `. Comparisons ^^^^^^^^^^^ @@ -77,23 +79,22 @@ Right operand must be unsigned type. Trying to shift by signed type will produce Addition, Subtraction and Multiplication ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Addition, subtraction and multiplication have the usual semantics. -They wrap in two's complement representation, meaning that -for example ``uint256(0) - uint256(1) == 2**256 - 1``. You have to take these overflows -into account when designing safe smart contracts. +Addition, subtraction and multiplication have the usual semantics, with two different +modes in regard to over- and underflow: + +By default, all arithmetic is checked for under- or overflow, but this can be disabled +using the :ref:`unchecked block`, resulting in wrapping arithmetic. More details +can be found in that section. The expression ``-x`` is equivalent to ``(T(0) - x)`` where ``T`` is the type of ``x``. It can only be applied to signed types. The value of ``-x`` can be positive if ``x`` is negative. There is another caveat also resulting -from two's complement representation:: - - int x = -2**255; - assert(-x == x); - -This means that even if a number is negative, you cannot assume that -its negation will be positive. +from two's complement representation: +If you have ``int x = type(int).min;``, then ``-x`` does not fit the positive range. +This means that ``unchecked { assert(-x == x); }`` works, and the expression ``-x`` +when used in checked mode will result in a failing assertion. Division ^^^^^^^^ @@ -106,7 +107,12 @@ Note that in contrast, division on :ref:`literals` results in of arbitrary precision. .. note:: - Division by zero causes a failing assert. + Division by zero causes a failing assert. This check can **not** be disabled through ``unchecked { ... }``. + +.. note:: + The expression ``type(int).min / (-1)`` is the only case where division causes an overflow. + In checked arithmetic mode, this will cause a failing assertion, while in wrapping + mode, the value will be ``type(int).min``. Modulo ^^^^^^ @@ -121,14 +127,19 @@ results in the same sign as its left operand (or zero) and ``a % n == -(-a % n)` * ``int256(-5) % int256(-2) == int256(-1)`` .. note:: - Modulo with zero causes a failing assert. + Modulo with zero causes a failing assert. This check can **not** be disabled through ``unchecked { ... }``. Exponentiation ^^^^^^^^^^^^^^ Exponentiation is only available for unsigned types in the exponent. The resulting type of an exponentiation is always equal to the type of the base. Please take care that it is -large enough to hold the result and prepare for potential wrapping behaviour. +large enough to hold the result and prepare for potential assertion failures or wrapping behaviour. + +.. note:: + In checked mode, exponentiation only uses the comparatively cheap ``exp`` opcode for small bases. + For the cases of ``x**3``, the expression ``x*x*x`` might be cheaper. + In any case, gas cost tests and the use of the optimizer are advisable. .. note:: Note that ``0**0`` is defined by the EVM as ``1``.