Summary
Performing symbolic execution in the MathMasters::sqrt
function and solmateSqrt
results in different outcomes.
Vulnerability Details
Using Helmos we can determine that comparing sqrt calculation of MathMasters
and Solmate
fails in formal verification.
POC
Add the following contract to the MathMasters.t.sol
file, which contains the differing parts of the square root calculation of MathMasters
and SolMate
contract sqrTest {
function mathMastersSqrt(uint256 x) public pure returns (uint256 z) {
assembly {
z := 181
let r := shl(7, lt(87112285931760246646623899502532662132735, x))
r := or(r, shl(6, lt(4722366482869645213695, shr(r, x))))
r := or(r, shl(5, lt(1099511627775, shr(r, x))))
r := or(r, shl(4, lt(16777002, shr(r, x))))
z := shl(shr(1, r), z)
z := shr(18, mul(z, add(shr(r, x), 65536)))
}
}
function solmateSqrt(uint256 x) public pure returns (uint256 z) {
assembly {
let y := x
z := 181
if iszero(lt(y, 0x10000000000000000000000000000000000)) {
y := shr(128, y)
z := shl(64, z)
}
if iszero(lt(y, 0x1000000000000000000)) {
y := shr(64, y)
z := shl(32, z)
}
if iszero(lt(y, 0x10000000000)) {
y := shr(32, y)
z := shl(16, z)
}
if iszero(lt(y, 0x1000000)) {
y := shr(16, y)
z := shl(8, z)
}
z := shr(18, mul(z, add(y, 65536)))
}
}
}
And add the following function to MathMasters.t.sol::MathMastersTest
function testSqrtEquivalence(uint256 x) public {
sqrTest t = new sqrTest();
uint256 solmate = t.solmateSqrt(x);
uint256 mathMasters = t.mathMastersSqrt(x);
assertEq(solmate, mathMasters);
}
Then run the following command:
halmos --function testSqrtEquivalence
Impact
The calculation result leads to a different result.
Tools Used
Foundry and Halmos.
Recommendations
Make the following change in MathMasters::sqrt
:
- r := or(r, shl(4, lt(16777002, shr(r, x))))
+ r := or(r, shl(4, lt(16777215, shr(r, x))))