The MathMasters::mulWadUp
function should revert if the input parameters uint256 x
and uint256 y
do not satisfy the condition: y == 0 || x <= type(uint256).max / y
. But the function does not revert as expected.
The function MathMasters::mulWadUp
accepts two input parameters (uint256 x
and uint256 y
) and calculates the expression x * y / 1e18
and rounds the result up. The function has check for input values which lead to overflow. In the comment of the function is written that it is required y
to be 0
or x <= type(uint256).max / y
. But the if statement that should check for this conditions does not check correctly:
Let's consider the if
statement: mul(y, gt(x, or(div(not(0), y), x)))
:
not(0)
: This computes the bitwise not
of 0
, which results in a value where all bits are set to 1
. In the context of a uint256
, this is equivalent to the maximum possible value for a uint256
, which is 2^256 - 1
.
div(not(0), y)
: This calculates the maximum value that can be safely multiplied by y
without causing an overflow.
or(div(not(0), y), x)
: This performs a bitwise or
between the result of div(not(0), y)
and x
.
However, this doesn't make sense in the context of an overflow check, as the or
operation here would not serve a purpose in preventing overflow.
gt(x, or(div(not(0), y), x))
: This will return 1
if x
is greater than the result of a bitwise or
operation and 0
otherwise.
mul(y, gt(x, or(div(not(0), y), x)))
: The final operation will multiply y
with the result of gt
operation.
The described overflow check bypasses values for x
and y
which does not meet the requirement: x <= type(uint256).max / y
and result in an overflow.
Let's consider the following scenario:
The value of the input parameter uint256 x
is 2
and the value of the second input parameter uint256 y
is 57896044618658097711785492504343953926634992332820282019728792003956564819968
which is 2^255
.
The requirement: x <= type(uint256).max / y
is not satisfied because type(uint256).max / y
is 1
and x
is 2
. Therefore, the if
statement in MathMasters::mulWadUp
function should be true
and the function should revert.
But the if
statement returns false
and the function continue with the calculations.
Let's consider again the if
statement but this time with values for x
and y
:
not(0)
is equivalent to 2^256 - 1.
div(not(0), y)
divides the maximum uint256
value by y
. If y
is 2^255
, this division results in 2
.
gt(x, or(div(not(0), y), x))
checks if x
is greater than the result of the division. Since x
is 2
, it is not greater than 2
, so the gt
function returns 0
.
mul(y, gt(x, or(div(not(0), y), x)))
multiplies y
by the result of the gt
function. Since gt
returns 0
, the multiplication result is 0
, and the condition inside the if
statement is false
.
In that way the function will not revert as expected.
The following test function testMulWadUpFuzzOverflow
in Foundry
found the described scenario. You can add the test function to the file MathMasters.t.sol
and execute it with the command: forge test --match-test "testMulWadUpFuzzOverflow"
At the end the result of the function MathMasters::mulWadUp
with the provided arguments will be incorrect due to the overflow.
VS Code, Foundry
Change the condition in the if
statement in the MathMasters::mulWadUp
function to ensure that the values of x
and y
satisfy the condition: y == 0 || x <= type(uint256).max / y
.
The contest is live. Earn rewards by submitting a finding.
This is your time to appeal against judgements on your submissions.
Appeals are being carefully reviewed by our judges.