The k=2 bonus accounting expands amount * (T - entryTime)^2 using absolute Unix timestamps. Although each stake-time accumulator can fit within uint256, the intermediate additions in _bonusShare() can overflow during claim settlement.
A malicious staker can use a sufficiently high-unit standard ERC20 deposit to make every claimSurvived() and claimExpired() call revert, permanently locking both principal and bonus funds.
_bonusShare() computes the user and global scores as:
The Math.mulDiv() call only protects the final userScore * snapshotTotalBonus / globalScore operation. It does not protect the preceding multiplication and addition operations.
The issue is caused by catastrophic intermediate growth from absolute timestamps. A stake can be accepted because amount * entryTime^2 fits in uint256, while the later expression T^2 * totalStake + sum(amount * entryTime^2) exceeds uint256, even though the intended mathematical score, amount * (T - entryTime)^2, is small.
Likelihood:
The attack requires a token with sufficiently large raw-unit supply, but the factory supports arbitrary standard ERC20 tokens and does not impose a maximum token decimal count, supply, stake amount, or aggregate stake amount. A high-decimal standard ERC20 can make the required raw amount economically feasible.
No privileged role is required after such a token has been allowlisted: an attacker only needs to stake before resolution and can then cause claims to fail for all stakers.
Impact:
Once a pool resolves as SURVIVED or EXPIRED, every staker claim calls _bonusShare() and reverts with Panic(0x11). Since sweepUnclaimedBonus() must reserve unclaimed principal and bonus, it cannot recover the locked funds either.
This permanently freezes all outstanding staker principal and bonus in the affected pool. A corruption outcome is not a recovery mechanism because it redirects the pool to the recovery address or whitehat rather than returning principal to stakers.
Create test/unit/ConfidencePool.overflow.t.sol:
The test reverts at the global score calculation:
Both terms fit independently, but their sum exceeds type(uint256).max.
Avoid expanding the quadratic formula with absolute Unix timestamps. Store and calculate time offsets relative to riskWindowStart, and use overflow-safe full-precision arithmetic for all intermediate terms.
Additionally, enforce an explicit aggregate-stake bound before accepting a deposit. The bound must guarantee that the largest possible values of T^2 * totalEligibleStake, snapshotSumStakeTimeSq, and 2 * T * snapshotSumStakeTime remain within the arithmetic domain used by the implementation.
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.
The contest is complete and the rewards are being distributed.