Summary
The current implementation of the L1LPTDataCache.cacheTotalSupply and its counterpart L2LPTDataCache.finalizeCacheTotalSupply does not implement a freshness check of the received _totalSupply amount via a cross-chain message. This is a problem, since cross-chain messages sent via Arbitrum's native retryable tickets can fail immediate automatic redemption, but can be redeemed manually much later, when bridged totalSupply is severely outdated. This can, in the worst case, affect the amount of rewards delegates will receive on L2 – this number is currently used only by the Minter to determine rewards at the round start.
Root causes
- Preventable automatic redemption. Permissionless
L1LPTDataCache.cacheTotalSupply function accepts arbitrary gasLimit value used by the automatic redeemer.
- A potential for severely outdated
totalSupply amount. Native retryable ticket can be kept alive indefinitely by calling ArbRetryableTx.keepalive(bytes32 ticketId)) every 7 days.
- Missing freshness check. The receiving L2 method
finalizeCacheTotalSupply does not validate that the message was sent recently; no expiration is implemented at the application level.
Why it's not an issue
- This hasn't been a problem so far: the
totalSupply on L1 hasn't changed since the migration to Arbitrum (and the deployment of the contracts in question).
- Triggering the attack currently requires either burning LPT tokens on L1 or bridging a massive amount of LPT from Arbitrum back to the Ethereum Mainnet. This makes the likelihood extremely low.
- The issue is monitored offchain. If an outdated ticket is found, it can be pemissionlessly redeemed by anyone, effectively cancelling the attack even before the value gets outdated.
Out of scope for the bug bounty
Reports targeting missing freshness check inside L2LPTDataCache.finalizeCacheTotalSupply method will be closed as known issues and are not eligible for rewards under the Livepeer Immunefi bug bounty program, unless the report shows how the severity can be escalated beyond what is already shown above.
References
Summary
The current implementation of the L1LPTDataCache.cacheTotalSupply and its counterpart L2LPTDataCache.finalizeCacheTotalSupply does not implement a freshness check of the received
_totalSupplyamount via a cross-chain message. This is a problem, since cross-chain messages sent via Arbitrum's native retryable tickets can fail immediate automatic redemption, but can be redeemed manually much later, when bridgedtotalSupplyis severely outdated. This can, in the worst case, affect the amount of rewards delegates will receive on L2 – this number is currently used only by the Minter to determine rewards at the round start.Root causes
L1LPTDataCache.cacheTotalSupplyfunction accepts arbitrarygasLimitvalue used by the automatic redeemer.totalSupplyamount. Native retryable ticket can be kept alive indefinitely by calling ArbRetryableTx.keepalive(bytes32 ticketId)) every 7 days.finalizeCacheTotalSupplydoes not validate that the message was sent recently; no expiration is implemented at the application level.Why it's not an issue
totalSupplyon L1 hasn't changed since the migration to Arbitrum (and the deployment of the contracts in question).Out of scope for the bug bounty
Reports targeting missing freshness check inside L2LPTDataCache.finalizeCacheTotalSupply method will be closed as known issues and are not eligible for rewards under the Livepeer Immunefi bug bounty program, unless the report shows how the severity can be escalated beyond what is already shown above.
References