Token-0x

First Flight #54
Beginner FriendlyDeFi
100 EXP
View results
Submission Details
Severity: high
Valid

Arithmetic underflow in `ERC20Internals::_burn`

Root + Impact

Description

  • _burn function does not check if burned amount is less than or equal the balance of the burner

  • _burn function performs Unchecked Subtraction in Inline Assembly — Silent Underflow Possible

function _burn(address account, uint256 value) internal {
assembly ("memory-safe") {
if iszero(account) {
mstore(0x00, shl(224, 0x96c6fd1e))
mstore(add(0x00, 4), 0x00)
revert(0x00, 0x24)
}
let ptr := mload(0x40)
let balanceSlot := _balances.slot
let supplySlot := _totalSupply.slot
let supply := sload(supplySlot)
sstore(supplySlot, sub(supply, value))
mstore(ptr, account)
mstore(add(ptr, 0x20), balanceSlot)
let accountBalanceSlot := keccak256(ptr, 0x40)
let accountBalance := sload(accountBalanceSlot)
sstore(accountBalanceSlot, sub(accountBalance, value))
}
}

Risk

Likelihood:

  • Reason 1 // Describe WHEN this will occur (avoid using "if" statements)

  • Reason 2

Impact:

  • Can arbitrarily inflate anyone's balance or destroy totalSupply


Proof of Concept

  • Add test_burn_underflow function to Token.t.sol

  • Run forge test --mt test_burn_underflow command

function test_burn_underflow() public{
address attacker = makeAddr("attacker");
token.burn(attacker , 1);
assertEq(token.balanceOf(attacker),type(uint256).max);
assertEq(token.totalSupply() , type(uint).max);
}

Recommended Mitigation

  • Add check if balance is less than value -> revert.

function _burn(address account, uint256 value) internal {
assembly ("memory-safe") {
if iszero(account) {
mstore(0x00, shl(224, 0x96c6fd1e))
mstore(add(0x00, 4), 0x00)
revert(0x00, 0x24)
}
let ptr := mload(0x40)
let balanceSlot := _balances.slot
let supplySlot := _totalSupply.slot
+ mstore(ptr, account)
+ mstore(add(ptr, 0x20), balanceSlot)
+ let accountBalanceSlot := keccak256(ptr, 0x40)
+ let accountBalance := sload(accountBalanceSlot)
+ if lt(accountBalance, value) {
+ mstore(0x00, shl(224, 0xe450d38c))
+ mstore(add(0x00, 4), from)
+ mstore(add(0x00, 0x24), fromAmount)
+ mstore(add(0x00, 0x44), value)
+ revert(0x00, 0x64)
+ }
let supply := sload(supplySlot)
sstore(supplySlot, sub(supply, value))
- mstore(ptr, account)
- mstore(add(ptr, 0x20), balanceSlot)
- let accountBalanceSlot := keccak256(ptr, 0x40)
- let accountBalance := sload(accountBalanceSlot)
sstore(accountBalanceSlot, sub(accountBalance, value))
}
}
Updates

Lead Judging Commences

gaurangbrdv Lead Judge 19 days ago
Submission Judgement Published
Validated
Assigned finding tags:

overflow & underflow

missing checks for overflow and underflow.

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.

Give us feedback!