Closed sherlock-admin2 closed 6 months ago
request poc
LSW comments:
it doesn't mark the parent as invalid, it's just the variable name called parent. It resolved the MAX_DEPTH game
PoC requested from @CodeWasp
Requests remaining: 10
I don't quite understand the request... There is already a coded PoC included in the submission. Could you please clarify?
I was trying to decipher this:
LSW comments:
it doesn't mark the parent as invalid, it's just the variable name called parent. It resolved the MAX_DEPTH game
It is difficult to understand what's being meant; my current understanding of the above is that it's two separate assertions:
parent
is just a variable, so updating it has no effectMAX_GAME_DEPTH
is being correctly resolvedI will address these two assertions individually.
parent
is just a variable, so updating it has no effectAs pointed in the finding the parent
is updated here:
// Set the parent claim as countered. We do not need to append a new claim to the game;
// instead, we can just set the existing parent as countered.
parent.counteredBy = msg.sender;
and is defined previously here:
ClaimData storage parent = claimData[_claimIndex];
As it's storage
, it's not simply a local variable, it's a pointer, so the storage is updated, and the claim at MAX_GAME_DEPTH
is being marked as countered permanently.
MAX_GAME_DEPTH
is being correctly resolvedAs pointed above, the claim at MAX_GAME_DEPTH
is being marked as countered. It was not resolved, because for correctly resolving it the resolveClaim() should be called, which in particular distributes the bonds; this has not been done. Moreover, as the finding (and the coded PoC) further demonstrates, there is no time point where resolveClaim()
can be called without reverting, and the game as a whole (incorrectly) resolves, with the claim at MAX_GAME_DEPTH
staying unresolved.
I hope this sufficiently answers the comment by LSW. If my understanding of it was incorrect, please let me know: I am ready to provide further input.
@nevillehuang I've noticed that this issue was marked as a duplicate of #218, with which it has nothing in common, and closed; could you please clarify why? Please notice that the coded PoC provided with this issue is valid, which can be easily verified by running it.
@smartcontracts Is this related to and a potential duplicate of #164?
parent
is just a variable, so updating it has no effectAs pointed in the finding the
parent
is updated here:// Set the parent claim as countered. We do not need to append a new claim to the game; // instead, we can just set the existing parent as countered. parent.counteredBy = msg.sender;
and is defined previously here:
ClaimData storage parent = claimData[_claimIndex];
In your submission, you stated the parent of the last-but-one claim is labeled as countered, but the last claim remains unresolved.
. But what is being countered is the last claim, hence the confusion.
The claim at
MAX_GAME_DEPTH
is being correctly resolvedAs pointed above, the claim at
MAX_GAME_DEPTH
is being marked as countered. It was not resolved, because for correctly resolving it the resolveClaim() should be called, which in particular distributes the bonds; this has not been done. Moreover, as the finding (and the coded PoC) further demonstrates, there is no time point whereresolveClaim()
can be called without reverting, and the game as a whole (incorrectly) resolves, with the claim atMAX_GAME_DEPTH
staying unresolved.
My intended meaning was that the game at MAX_GAME_DEPTH is being countered correctly, and hence can be resolved correctly. It is true that due to the issue at #164 , the leaf can remain unresolved. If that was your intention, which now seems to be so, the issue is a clear dup.
I think you are right about this being a duplicate of #164. The secondary vulnerability that I describe, and which leads to the non-existence of the time period when the claim can be resolved is separate though. This part seems to be a duplicate of #36, which is in turn a dup of #8. Though my articulation of that aspect in this finding is not precise enough: I wanted to create a separate finding, but was short on time. I leave it to the judge to decide.
The protocol team fixed this issue in the following PRs/commits: https://github.com/ethereum-optimism/optimism/pull/10182
CodeWasp
high
Bonds at `MAX_GAME_DEPTH`` can be irrevocably locked
Summary
Scenarios are possible, when the
FaultDisputeGame
as a whole resolves, but claims atMAX_GAME_DEPTH
remain unresolved. As a result, all bonds associated with these unresolved claims become permanently locked.Vulnerability Detail
When the game DAG is extended with moves up to the last level (at
MAX_GAME_DEPTH
), and the claim at this level is countered by a step, the parent of the last-but-one claim is labeled as countered, but the last claim remains unresolved. As a result, it becomes possible to resolve all claims, excluding the claims atMAX_GAME_DEPTH
, as well as to resolve the game as a whole, while the most deep claims remain unresolved.As the associated bonds are unlocked only when the claim is resolved, and it's not possible to resolve the claim when the whole game is resolved, the bonds associated with the most deep claims will become permanently locked.
The secondary vulnerability is associated with calculations of the clock duration. When grandparent clock is shifted, the clock of the deepest claim becomes shifted in such a way that it is not possible to resolve it before the whole game is resolved. Combined with the previous, there is not time period when the most deep claim may be resolved, leading to guaranteed locking of user funds, as the below coded PoC demonstrates.
Impact
Permanent locking of user funds in unresolvable claims.
Coded PoC
The below PoC is a slight adaptation of the test test_resolve_bondPayoutsSeveralActors_succeeds. Copy-paste the PoC after this test, and execute with
forge test -vvvv --match-test test_exploit_lockFundsAtMaxGameDepth
. As can be seen, in case of a single unresolvable claim the amount of permanently locked funds is 39.99 Eth.Code Snippet
https://github.com/sherlock-audit/2024-02-optimism-2024/blob/main/optimism/packages/contracts-bedrock/src/dispute/FaultDisputeGame.sol#L217-L219
https://github.com/sherlock-audit/2024-02-optimism-2024/blob/main/optimism/packages/contracts-bedrock/src/dispute/FaultDisputeGame.sol#L262-L280
Tool used
Manual Review; Foundry
Recommendation
We recommend to fix the
step
function, in that when updating the parent claim as countered, it should also resolve the current claim. We further recommend to redesign the clock computations when grandparent clocks are involved.Duplicate of #164