Summary
The MultiFlowPump._getSlotsOffset
function correctly calculates "the starting byte of the slot that contains the n
th element of an array". But this is not the correct value for the use cases where the function is invoked. So storage values are stored in unexpected slots.
Vulnerability Details
The MultiFlowPump._getSlotsOffset
function is invoked to calculate a slot number after reserves
.
function update(uint256[] calldata reserves, bytes calldata data) external {
<...>
pumpState.lastReserves = _capReserves(msg.sender, pumpState.lastReserves, reserves, capExponent, crp);
>>
>> uint256 numSlots = _getSlotsOffset(numberOfReserves);
assembly {
slot := add(slot, numSlots)
}
pumpState.emaReserves = slot.readBytes16(numberOfReserves);
assembly {
slot := add(slot, numSlots)
}
But the _getSlotsOffset
returns an offset in the bytes number instead of the slots number which is necessary for the store. So the actual offset is 32 times more than the expected one:
* @dev Get the starting byte of the slot that contains the `n`th element of an array.
*/
function _getSlotsOffset(uint256 numberOfReserves) internal pure returns (uint256 _slotsOffset) {
_slotsOffset = ((numberOfReserves - 1) / 2 + 1) << 5;
}
Impact
Unintended behavior
Tools used
Manual Review
Recommendations
Consider using slots number instead of bytes
/**
- * @dev Get the starting byte of the slot that contains the `n`th element of an array.
+ * @dev Get the slot number that contains the `n`th element of an array.
*/
function _getSlotsOffset(uint256 numberOfReserves) internal pure returns (uint256 _slotsOffset) {
- _slotsOffset = ((numberOfReserves - 1) / 2 + 1) << 5;
+ _slotsOffset = ((numberOfReserves - 1) / 2 + 1);
}