When the amount of locked tokens is fully removed from SDLPoolPrimary, the associated lockId is immediately erased. However, even if all tokens have been withdrawn from SDLPoolSecondary, the lockId will persist and will be destroyed only when the owner of that lockId calls the executeQueuedOperations() function. Users can still lock additional tokens into the lockId as long as it exists. Users will lose all locked tokens if the order to withdraw all tokens within the lockId comes before any further lock orders for the same lockId.
Vulnerability Steps:
The user initially locks a quantity of tokens in SDLPoolSecondary, and the lockId exists.
The user performs an init and withdraws all tokens within the aforementioned lockId. The lockId is not destroyed at this point, and the withdraw order is added to queuedLockUpdates with lock.amount set to 0.
The user notices that the lockId still exists and locks additional tokens into it. The additional lock order is added to queuedLockUpdates.
Now, queuedLockUpdates contains numerous orders, with the withdraw order at the front of the queue.
Finally, the _executeQueuedLockUpdates() function is called. The lockId falls into the withdrawal case and gets deleted (L471-L473).
lockOwners[lockId] is deleted, additional locks can still be added to that lockId after that, but they will not be owned by anyone and will be permanently stuck within.
This confusion could lead to the permanent loss of tokens that users lock additionally.
Do not add any orders with an existing lockId that has lock.amount = 0 until it is deleted, or
After completing the iteration through the queuedLockUpdates loops, then perform the check and deletion.
The contest is live. Earn rewards by submitting a finding.
This is your time to appeal against judgements on your submissions.
Appeals are being carefully reviewed by our judges.