Open sherlock-admin3 opened 6 months ago
The protocol team fixed this issue in the following PRs/commits: https://github.com/rio-org/rio-sherlock-audit/pull/10
The protocol team fixed this issue in PR/commit rio-org/rio-sherlock-audit#10.
Fixed Heap is updated inside the reportOutOfOrderValidatorExits function
The Lead Senior Watson signed off on the fix.
mstpr-brainbot
high
reportOutOfOrderValidatorExits
does not updates the heap orderSummary
When an operator's validator exits without a withdrawal request, the owner can invoke the
reportOutOfOrderValidatorExits
function to increase theexited
portion of the operator validators. However, this action does not update the heap. Consequently, during subsequent allocation or deallocation processes, the heap may incorrectly mark validators as exited.Vulnerability Detail
First, let's see how the utilization is determined for native ETH deposits for operators which is calculated as:
operatorShares.allocation.divWad(operatorShares.cap)
where as the allocation is the totaldeposited
validators and thecap
is predetermined value by the owner of the registry.When the heap is retrieved from the storage, here how it is fetched:
as we can see, the heap is always assumed to be order in the storage when the registry fetches it initially. There are no ordering of the heap when requesting the heap initially.
When, say the deallocation happens via an user withdrawal request, the queue can exit early if the operator in the heap has "0" room:
reportOutOfOrderValidatorExits
increases the "exited" part of the operators validator:Now, knowing all these above, let's do an example where calling
reportOutOfOrderValidatorExits
can make the heap work wrongly and exit prematurely.Assume there are 3 operators which has native ETH deposits. operatorId 1 -> utilization 5% operatorId 2 -> utilization 10% operatorId 3 -> utilization 15%
such operators would be ordered in the heap as: heap.operators[1] -> operatorId: 1, utilization: 5 heap.operators[2] -> operatorId: 2, utilization: 10 heap.operators[3] -> operatorId: 3, utilization: 15 heap.getMin() -> operatorId: 1, utilization: 5 heap.getMax() -> operatorId:3, utilization 15
now, let's say the "cap" is 100 for all of the operators which means that: operatorId 1 -> validator.deposits = 5, validator.exit = 0 operatorId 2 -> validator.deposits = 10, validator.exit = 0 operatorId 3 -> validator.deposits = 15, validator.exit = 0
Let's assume that the operator 3 exits 15 validator from beacon chain without prior to a user request, which is a reason for owner to call
reportOutOfOrderValidatorExits
to increase the exited validators.When the owner calls
reportOutOfOrderValidatorExits
for the operatorId 3, the exited will be 15 for the operatorId 3. After the call the operators validator balances will be: operatorId 1 -> validator.deposits = 5, validator.exit = 0 operatorId 2 -> validator.deposits = 10, validator.exit = 8 operatorId 3 -> validator.deposits = 15, validator.exit = 15hence, the utilizations will be: operatorId 1 -> utilization 5% operatorId 2 -> utilization 10% operatorId 3 -> utilization 0%
which means now the operatorId 3 has the lowest utilization and should be the first to get deposits and last to unwind deposits from. However, the heap is not re-ordered meaning that the minimum in the heap is still opeartorId 1 and the maximum is still operatorId 3!
Now, when a user tries to withdraw, the first deallocation target will be the operatorId 3 because the heap thinks that it is the most utilized still.
However, since the active utilization for operatorId 3 is "0" the loop will exit early hence, the withdrawals will not go through https://github.com/sherlock-audit/2024-02-rio-network-core-protocol/blob/4f01e065c1ed346875cf5b05d2b43e0bcdb4c849/rio-sherlock-audit/contracts/restaking/RioLRTOperatorRegistry.sol#L556-L560
Hence, the user will not be able to request the withdrawal!
Coded PoC:
Impact
Heap can be mixed, withdrawals and deposits can fail, hence I will label this as high.
Code Snippet
https://github.com/sherlock-audit/2024-02-rio-network-core-protocol/blob/4f01e065c1ed346875cf5b05d2b43e0bcdb4c849/rio-sherlock-audit/contracts/utils/OperatorRegistryV1Admin.sol#L357C5-L386C6
https://github.com/sherlock-audit/2024-02-rio-network-core-protocol/blob/4f01e065c1ed346875cf5b05d2b43e0bcdb4c849/rio-sherlock-audit/contracts/restaking/RioLRTOperatorRegistry.sol#L541-L594
https://github.com/sherlock-audit/2024-02-rio-network-core-protocol/blob/4f01e065c1ed346875cf5b05d2b43e0bcdb4c849/rio-sherlock-audit/contracts/restaking/RioLRTOperatorRegistry.sol#L310-L336
Tool used
Manual Review
Recommendation
update the utilization in the reportOutOfOrderValidatorExits function