Summary
In storeLastReserves()
, precision loss stems in calculation of iByte
which involves an intermediate division before multiplication.
Vulnerability Details
function storeLastReserves(bytes32 slot, uint40 lastTimestamp, bytes16[] memory reserves) internal {
uint8 n = uint8(reserves.length);
if (n == 1) {
assembly {
sstore(slot, or(or(shl(208, lastTimestamp), shl(248, n)), shl(104, shr(152, mload(add(reserves, 32))))))
}
return;
}
assembly {
sstore(
slot,
or(
or(shl(208, lastTimestamp), shl(248, n)),
or(shl(104, shr(152, mload(add(reserves, 32)))), shr(152, mload(add(reserves, 64))))
)
)
}
if (n > 2) {
uint256 maxI = n / 2;
uint256 iByte;
for (uint256 i = 1; i < maxI; ++i) {
iByte = i * 64;
assembly {
sstore(
add(slot, i),
add(mload(add(reserves, add(iByte, 32))), shr(128, mload(add(reserves, add(iByte, 64)))))
)
}
}
if (reserves.length & 1 == 1) {
iByte = maxI * 64; @audit Issue here
assembly {
sstore(
add(slot, maxI),
add(mload(add(reserves, add(iByte, 32))), shr(128, shl(128, sload(add(slot, maxI)))))
)
}
}
}
}
Here, if n>2
, maxI
is given by n / 2
.
if (n > 2) {
uint256 maxI = n / 2;
Then in calculation of iByte
, this value is multiplied by 64
.
Due to truncation due to the division, the final value obtained is much smaller.
PoC
Now performing multiplication before division yields:
This is more accurate than the initial result.
Impact
Precision loss due to intermediate division before multiplication.
Tools Used
Manual Review
Recommendations
Perform multiplication before division.
function storeLastReserves(bytes32 slot, uint40 lastTimestamp, bytes16[] memory reserves) internal {
uint8 n = uint8(reserves.length);
if (n == 1) {
assembly {
sstore(slot, or(or(shl(208, lastTimestamp), shl(248, n)), shl(104, shr(152, mload(add(reserves, 32))))))
}
return;
}
assembly {
sstore(
slot,
or(
or(shl(208, lastTimestamp), shl(248, n)),
or(shl(104, shr(152, mload(add(reserves, 32)))), shr(152, mload(add(reserves, 64))))
)
)
}
if (n > 2) {
uint256 maxI = n / 2;
uint256 iByte;
for (uint256 i = 1; i < maxI; ++i) {
iByte = n*64/2;
assembly {
sstore(
add(slot, i),
add(mload(add(reserves, add(iByte, 32))), shr(128, mload(add(reserves, add(iByte, 64)))))
)
}
}
if (reserves.length & 1 == 1) {
iByte = n*64/2;
assembly {
sstore(
add(slot, maxI),
add(mload(add(reserves, add(iByte, 32))), shr(128, shl(128, sload(add(slot, maxI)))))
)
}
}
}
}