# Controller.sol::lockwithdraw() - it's impossible to withdraw of fsof = 0

When a user's fsOf returns 0 after burning all Earning tokens, lockWithdraw divides by zero in calDuration, permanently blocking withdrawal of locked funds.

- Payout: Contest reward
- Severity: medium
- Program: Goat
- Credited: deth
- Disclosed: 2024-06-12
- Write-up quality: 6/10
- Original: https://cdn.cantina.xyz/reports/cantina_competition_goat_mar2024.pdf#3.2.11
- Library page: https://bugbountylibrary.com/r/cantina%3Agoat_mar2024%3A3.2.11
- Tags: domain:web3 impact:dos kind:contest platform:cantina sev:medium tech:evm tech:solidity technique:source-review vuln:dos

**Target:** Controller.sol / Locker._withdraw and calDuration (Solidity smart contract)
**Root cause:** fsOf inverts ifs; when a user's Earning balance is 0 but maxEarningOf is nonzero, ifs becomes 10000 and fsOf returns 0. calDuration then computes duration = lockData.duration * 0 / DEMI = 0, and receivedA = total * pastTime / duration panics on divide-by-zero.
**Steps:**
1. Alice locks 100 Geth for 100 days; ifs=0 so fsOf returns 10000 (inverted).
2. Alice receives 5 Earning tokens (transfer or sponsor reward) and calls `Earning.update` to set _sharedA and _maxEarningOf.
3. Alice calls `earningWithdraw` -> `withdraw` burns 5 tokens, _sharedA=0, maxEarningOf stays 5.
4. reCalFs computes ifs = 0 * 10000 / 5 = 10000, so fsOf now returns 0.
5. Alice calls `lockWithdraw` for her 100 locked tokens.
6. `_withdraw` reads fs = fsOf(poolOwner_) = 0; `calDuration` computes mFactor = 0 (non-owner), duration = 100 days * 0 / 10000 = 0.
7. `receivedA = total * pastTime / duration` divides by zero and panics, reverting the withdrawal.
**How found:** Traced the fsOf/ifs inversion logic and noticed the edge case where burning all Earning tokens leaves maxEarningOf nonzero, driving fsOf to 0; followed the value into calDuration to find the divide-by-zero.
**Insight:** When a protocol inverts a ratio (ifs -> fsOf) and later multiplies by that ratio as a factor, check the zero and boundary values of the inverted quantity; a stale maxEarningOf after full burn is a classic way to pin the factor at 0 and trigger division-by-zero DoS on unrelated flows.
