Unchecked balance and supply underflow in _burn() allows attackers to mint near-infinite tokens and corrupt total supply
Normal behavior:
A compliant ERC20 _burn() function must revert whenever the burn amount exceeds the caller’s balance or the total supply. Burning tokens should reduce the account’s balance and decrease the total supply while ensuring supply/balance invariants remain valid.
Issue:
The _burn() function in ERC20Internals.sol performs unchecked subtraction in Yul assembly. Since assembly arithmetic does not revert on underflow, burning more tokens than an account owns causes both the user’s balance and the total supply to silently wrap around to extremely large uint256 values, effectively giving the attacker a massive balance and corrupting total supply.
Likelihood:
Any public/external function using _burn() assumes it is safe and does not perform balance checks, which will routinely happen during token burns.
Developers expect primitives to revert on invalid operations (like _transfer and _spendAllowance do), making misuse of _burn() extremely likely in real deployments.
Impact:
The attacker’s balance becomes a massive uint256 number due to wrap-around, effectively giving them “infinite” tokens.
totalSupply also underflows, corrupting the token’s entire economic model and permanently breaking all accounting invariants.
The following Foundry test demonstrates how calling burn() with an amount greater than the user’s balance results in silent underflow, giving the attacker an extremely large token balance and corrupting the token’s totalSupply.
The _burn() function performs unchecked subtraction in assembly, allowing both balanceOf(attacker) and totalSupply to wrap around to near-maximum uint256 values.
The logs clearly show that after the attacker performs a burn operation with an amount far exceeding their 10-token balance:
totalSupply underflows to a massive uint256 number
balanceOf(attacker) underflows to the exact same massive number
The computed “expected underflowed balance” matches perfectly, confirming uncontrolled wrap-around
Because _burn() performs raw sub() operations in Yul without checking balances or total supply, arithmetic underflow does not revert but instead wraps, granting the attacker an effectively infinite token balance and corrupting the token supply permanently.
This confirms a critical accounting and state integrity vulnerability that allows attackers to mint arbitrary amounts of tokens through underflow.
Added supply underflow check
Prevents totalSupply - value from wrapping to a massive uint256 number.
Added balance underflow check using custom error
Ensures accounts cannot burn more tokens than they own, matching ERC20 safety expectations.
Corrected error encoding for invalid sender
Uses the correct account parameter instead of hardcoded 0x00.
Added event emission for burns
Ensures Transfer(account → 0x0) logs are compliant with ERC20 and indexers.
Made state updates safe
Subtractions now only occur after validation, preventing silent arithmetic wrap-around.
Together, these changes eliminate the underflow exploit, enforce ERC20 invariants, and ensure totalSupply and balances remain accurate under all conditions.
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.