Direct theft of any user funds, whether at-rest or in-motion, other than unclaimed yield
Description
Brief/Intro
In the AlchemistV3 protocol's _subDebt function, an issue overstates per-account rawLocked collateral by using pre-subtraction debt for calculations but subtracting a globally clamped toFree amount without recomputing on the updated debt, causing excessive pro-rata deductions from users' collateralBalance during _sync in stressed scenarios like burns or liquidations, which could lead to unfair permanent loss of withdrawable funds, unwarranted liquidations, user attrition, and broader protocol insolvency.
Vulnerability Details
function_subDebt(uint256tokenId,uint256amount)internal{ Account storage account = _accounts[tokenId];// Update collateral variables uint256 toFree =convertDebtTokensToYield(amount)* minimumCollateralization / FIXED_POINT_SCALAR; @>uint256 lockedCollateral =convertDebtTokensToYield(account.debt)* minimumCollateralization / FIXED_POINT_SCALAR;// For cases when someone above minimum LTV gets liquidated. @>if(toFree > _totalLocked){ toFree = _totalLocked;} account.debt -= amount; totalDebt -= amount; _totalLocked -= toFree;@> account.rawLocked = lockedCollateral - toFree;// <-- Bug: Uses pre-subtraction lockedCollateral minus clamped toFree, overstating rawLocked when clamp triggers // Clamp to avoid underflow due to rounding later at a later time if(cumulativeEarmarked > totalDebt){ cumulativeEarmarked = totalDebt;}}
The lockedCollateral is computed on the pre-subtraction account.debt (correct for estimating the delta toFree), but after clamping toFree to _totalLocked (to protect the global from underflow), the per-account account.rawLocked is set to lockedCollateral - toFree (using the reduced toFree), which overstates the true required locked collateral for the new post-subtraction debt when the clamp activates, as it doesn't recompute based on the updated debt.
Impact Details
This issue overstates per-account locked collateral baselines during global clamps, leading to excessive pro-rata deductions from users' collateralBalance in future _sync calls and unfairly amplifying their share of protocol-wide redemptions and fees.
Mitigation
The best solution is to recompute account.rawLocked after the debt subtraction using the updated account.debt to ensure it exactly matches the new required minimum locked collateral, decoupling per-account accuracy from the global clamp on toFree without altering the global protections.
Patched Code (replace the update block in _subDebt):
Impact confirmed: Without bug (recompute rawLocked=5.55e18), deduction ~2.08e18, collateral=8.92e18, ratio ~1.784 > 1.111 (healthy, no revert). User unfairly loses ~1.25e18 extra withdrawable collateral + risks liquidation during global redemptions.
This shows the bug's severity: overstated per-account rawLocked amplifies pro-rata deductions unfairly, especially in stressed (clamped) scenarios, harming innocent users.
POC 2
Proof Summary:
Setup: User deposits 20e18 yield shares (collateral), mints 10e18 debt (healthy ratio ~2.0 > 1.111 min). Partial burn of 5e18 debt triggers _subDebt: expected toFree ~5.55e18, but clamped to 2e18 (global protection). Bug: rawLocked set to pre-burn locked (22.22e18) - clamped 2e18 = 20.22e18 (overstated; correct post-burn should be 11.11e18 for remaining 5e18 debt).
Pre-sync healthy: getCDP returns full 20e18 collateral, 5e18 debt (no deduction yet, ratio ~4.0).
Poke triggers _sync: Computes collateralToRemove = ScaleByWeightDelta(rawLocked=20.22e18, large_delta) → 5e18 deduction (excessive; correct would be ~2.78e18 based on true rawLocked=11.11e18).
Post-sync: Collateral drops to 15e18 (from 20e18), debt 5e18 (ratio ~3.0, still healthy but user lost 5e18 withdrawable collateral unfairly).
Unfair pro-rata penalty: Users with clamped burns (e.g., during global undercollateralization/liquidations) get overstated rawLocked, amplifying their share of future protocol-wide deductions (redemptions/fees) in _sync. Innocent users lose extra withdrawable collateral (~80% more deduction here vs. correct).