# LockingController.startUnwinding() Incorrectly Calculates rewardWeightDecrease

LockingController.startUnwinding rounds rewardWeightDecrease up, drifting globalRewardWeight below the true sum and letting depositRewards over-allocate, causing loss-application reverts (DoS).

- Payout: Contest reward
- Severity: medium
- Program: infiniFi
- Credited: KupiaSec
- Disclosed: 2025-05-23
- Write-up quality: 6/10
- Original: https://cdn.cantina.xyz/reports/cantina_competition_infinifi_april2025.pdf#3.2.25
- Library page: https://bugbountylibrary.com/r/cantina%3Ainfinifi_april2025%3A3.2.25
- Tags: domain:web3 impact:dos kind:contest platform:cantina sev:medium tech:evm tech:solidity technique:source-review vuln:accounting-error vuln:business-logic vuln:dos vuln:precision-loss

**Target:** LockingController / UnwindingModule reward accounting
**Root cause:** rewardWeightDecrease is computed as userReceiptToken.mulDivUp(totalRewardWeight, totalReceiptTokens) instead of the exact difference between old and new totalRewardWeight, so up-rounding makes globalRewardWeight drift below the true sum of totalReceiptTokens*multiplier.
**Steps:**
1. Create a position of 10 receipt tokens across 10 unwinding epochs (globalRewardWeight=12).
2. Repeatedly call `gateway.startUnwinding(1, _unwindingEpochs)` each week for 5 iterations.
3. Observe globalRewardWeight=2 while UnwindingModule.totalRewardWeight()=4 (drift).
4. Call `_depositRewards(15)`; globalReceiptToken becomes 20 while contract holds only 10 iUSD.
5. Call `_applyLosses(21)`; loss share computed as 21*20/(20+15)=12 exceeds held balance 10, reverting with ERC20InsufficientBalance.
**How found:** Code review of startUnwinding's rewardWeightDecrease formula versus the bucket's totalReceiptTokens reduction, noticing mulDivUp rounding diverges from the exact delta and that depositRewards divides by globalRewardWeight.
**Exploitation:** Accumulate rounding drift via many small startUnwinding calls so globalRewardWeight understates true weight; subsequent depositRewards over-allocates receipt tokens, inflating globalReceiptToken beyond actual balance; when a loss is applied, the computed loss share exceeds the contract's real balance and reverts, halting loss socialization.
**Insight:** When a global accumulator is decremented by a rounded fraction of itself rather than the exact delta of the underlying quantity, repeated small operations accumulate drift; check any mulDivUp/mulDivDown used to maintain an invariant sum against the recomputed value.
