Open howlbot-integration[bot] opened 1 month ago
Going to mark this one as acknowledged. The reporter is correct that the spec currently states that no moves can be made after a claim has been resolved. However, this specific case does not impact valid game resolution.
In the case where the parent claim is already resolved, and the counteredBy
is updated with a step
, this can only be done if the step is actually a valid challenge (otherwise, it'd revert.) In this case, bubbling up that result would actually serve to inform correct global resolution of the game, though it was "out of bounds" of the game clock.
We also have an honest challenger liveness assumption, where the challenger should always be online to make these claims. It is unlikely that in a real world scenario, the challenger would fail to complete the instruction step in the time allowed to them.
This does diverge from what the spec says, though.
This is a divergence from the spec, but doesn't appear to have any serious consequences. I will be downgrading from High but am still deciding whether Medium or QA is more appropriate.
@clabby makes a few very important points:
1) It requires breaking our assumption that we have liveness for challengers. In this case, we assume that Alice didn't call step()
when it was necessary for her to win the game. In this situation, Bob could have resolved to win, which is much worse.
2) In the case laid out, the payouts were actually correct for the actions that were taken. If Alice was paid, that would be more incorrect.
For these reasons, I'm downgrading to QA for the idea that step()
should be locked by game clock to match spec, but with the understanding that there are not sufficient negative consequences to justify Medium severity.
zobront changed the severity to QA (Quality Assurance)
zobront marked the issue as grade-a
Lines of code
https://github.com/code-423n4/2024-07-optimism/blob/70556044e5e080930f686c4e5acde420104bb2c4/packages/contracts-bedrock/src/dispute/FaultDisputeGame.sol#L311
Vulnerability details
As per the specs, one of the invariants:
The outcome of subgames moves upwards in the gametree by resolving the subgames from bottom to up.
This invariant, however, does not always hold up.
Let's first look at the first scenario where this invariant does hold up, a regular resolution of a FDG:
MIPS.sol
, which makes her the winner.Run this PoC in
FaultDisputeGame.t.sol
:If we look at the output, we see that Alice got the funds, which is correct:
Now, let's look at the second scenario, where this invariant does not hold up:
resolveClaim()
function, we see the following check:resolveClaim(8,0)
- the subgame has successfully been resolved.step()
, this call should not be able to succeed, the deepest subgame has already been resolved.Run this second PoC in
FaultDisputeGame.t.sol
:If we look at the output, we see that Alice got the robbed this time:
As we can see, even though Alice was the winner, the payout got skewed, she got played out of:
1074074857400000000000-1014074857600000000000=59999999800000000000
rewards stolen by Bob because of Charlie stepping.This means that the variant that is set in the specs does not hold up. Furthermore, it should not be possible for a party to step when a game has been resolved on the most bottom depth, since from that resolve on, we go upwards and resolve the rest.
Recommended Mitigation Steps
When the lowest level is resolved, do not allow steps.
Assessed type
Other