Closed code423n4 closed 1 year ago
0xRobocop marked the issue as primary issue
0xRobocop marked the issue as sufficient quality report
viraj124 marked the issue as disagree with severity
this should be low/med at best IMO we're adding the balance check but note _getUtility
is a internal method and there are input check of the reserve balances values passed so that is a invalid argument
viraj124 (sponsor) acknowledged
we checked this further with some other members of the team and agreed this is a high and we've fixed this in a pr
viraj124 (sponsor) confirmed
JustDravee marked the issue as satisfactory
JustDravee marked issue #57 as primary and marked this issue as a duplicate of 57
Lines of code
https://github.com/code-423n4/2023-08-shell/blob/c61cf0e01bada04c3d6055acb81f61955ed600aa/src/proteus/EvolvingProteus.sol#L563-L595 https://github.com/code-423n4/2023-08-shell/blob/c61cf0e01bada04c3d6055acb81f61955ed600aa/src/proteus/EvolvingProteus.sol#L701-L724 https://github.com/code-423n4/2023-08-shell/blob/c61cf0e01bada04c3d6055acb81f61955ed600aa/src/proteus/EvolvingProteus.sol#L716
Vulnerability details
Impact
There is a missing balance check in
_reserveTokenSpecified()
, which allows reserve tokens to be withdrawn or deposited into illegal balances, which may cause subsequent transactions to revert. It also allows the pool to be killed by withdrawing all balances which prevents any further deposit.Proof of Concept
The allowed region of possible reserve token balances is constrained by
_checkBalances()
, which requires that each balance be at leastMIN_BALANCE
and that the ratio of balances be in the interval [MIN_M
,MAX_M
).The functions available for swapping are
swapGivenInputAmount()
andswapGivenOutputAmount()
, both of which make use of_swap()
for calculating the unknown value (output and input amounts) which in turn checks the resulting balance after the swap with_checkBalances()
. Thus it is impossible to swap out of the allowed region.The functions
depositGivenOutputAmount()
andwithdrawGivenInputAmount()
both make use of_lpTokenSpecified()
for the calculation which likewise checks the resulting balance after the deposit or withdrawal with_checkBalances()
. Thus it is impossible to deposit or withdraw, using these functions, out of the allowed region.However, the remaining functions
depositGivenInputAmount()
andwithdrawGivenOutputAmount()
instead make use of_reserveTokenSpecified()
which fails to perform the balance checks. The balances may therefore end up outside of the allowed region when using these functions. Specifically, one may withdraw too much such thatMIN_BALANCE
is violated or one may deposit too much such that the ratio of balances is violated.By entering this forbidden region, some transactions will be reverted if they fail to leave the forbidden region. It also makes it possible to kill the pool by withdrawing all of both reserve tokens. Then all deposits will revert due to division by zero in
_reserveTokenSpecified()
at L589 and in_getUtilityFinalLp()
at L660 (called by_lpTokenSpecified()
).Also note that
_getUtility()
accepts negative balances as parameters. This means (depending on the integration of EvolvingProteus) that one can withdraw into a negative balance. Further note the unsafe casting of an integer into an uint256 at L716 in the calculation ofdisc
. This is an issue precisely when we have this freedom with the balances. The utility may thus underflow into an invalid and much higher value, breaking an important invariant.Recommended Mitigation Steps
Add balance checks in
_reserveTokenSpecified()
, as in_swap()
and_lpTokenSpecified()
.Consider also checking that
x
andy
in_getUtility()
are non-negative and that the uint256 casting in the calculation ofdisc
doesn't underflow.Assessed type
Invalid Validation