# Unstaking from LP pools will cause underflow and lock user funds

Cabal's pool_router::unlock dumps all remaining undelegate amount on the last pool, underflowing when that pool is underfunded and locking staked LP tokens.

- Payout: Contest reward
- Severity: medium
- Program: Cabal
- Credited: givn
- Disclosed: 2025-05-28
- Write-up quality: 6/10
- Original: https://code4rena.com/reports/2025-04-cabal-liquid-staking-token#m-04
- Library page: https://bugbountylibrary.com/r/c4%3A2025-04-cabal-liquid-staking-token%3AM-04
- Tags: domain:web3 impact:funds-loss kind:contest platform:code4rena sev:medium tech:solidity technique:source-review vuln:accounting-error vuln:dos

**Target:** Move smart contract pool_router::unlock / batch_undelegate_pending_lps
**Root cause:** unlock computes a ratio per pool but assigns the entire remaining amount to the last pool; if that pool holds less than remain_amount, `temp_pool.amount - temp_amount` underflows.
**Steps:**
1. Call initiate_unstake to create UnbondingEntry and increase unstaked_pending_amounts.
2. Admin calls batch_undelegate_pending_lps() which loops pools calling pool_router::unlock.
3. unlock computes ratio = unlock_amount/total_stakes and for each pool subtracts mul_by_u64_truncate(ratio, pool.amount), except the last pool which absorbs all remain_amount.
4. If last pool's amount < remain_amount, `temp_pool.amount - temp_amount` underflows and reverts.
5. Reproduce with test_unlock_lp_amounts: pools [20M,20M,10k], unlock 2M repeatedly until last pool hits 0, then unlock 1000 twice -> underflow.
**How found:** Code review of the unlock loop noticed the special-case branch assigning remain_amount to the last pool, then reasoned about imbalance scenarios (large deposit to underutilized pool, new pool, mass withdrawals) and wrote a test replicating the calculation to trigger underflow.
**Insight:** When a pro-rata distribution loop dumps the rounding remainder on the final element, check whether that element can hold the remainder; imbalance between pools (not just total balance) is the trigger, so test with skewed pool sizes.
