Token-0x

First Flight #54
Beginner FriendlyDeFi
100 EXP
Submission Details
Impact: high
Likelihood: high

Arithmetic Overflow/Underflow Risk in Inline Assembly

Author Revealed upon completion

Arithmetic Overflow/Underflow Risk in Inline Assembly + High

Description

  • Normal behavior: Solidity ^0.8.x automatically reverts on overflow/underflow for normal arithmetic.

Issue: In ERC20Internals, the following assembly operations do not revert automatically:

  • Problem: Using add/sub in assembly bypasses Solidity's built-in safety, potentially allowing wraparounds if very large numbers are involved.

function _mint(address account, uint256 value) internal {
assembly ("memory-safe") {
let supply := sload(supplySlot) // LOC 118
sstore(supplySlot, add(supply, value)) // LOC 118 - ⚠ overflow unchecked
let accountBalance := sload(accountBalanceSlot) // LOC 124
sstore(accountBalanceSlot, add(accountBalance, value)) // LOC 124 - ⚠ overflow unchecked
}
}
function _burn(address account, uint256 value) internal {
assembly ("memory-safe") {
let supply := sload(supplySlot) // LOC 133
sstore(supplySlot, sub(supply, value)) // LOC 133 - ⚠ underflow unchecked
let accountBalance := sload(accountBalanceSlot) // LOC 139
sstore(accountBalanceSlot, sub(accountBalance, value)) // LOC 139 - ⚠ underflow unchecked
}
}
function _transfer(address from, address to, uint256 value) internal returns (bool success) {
assembly ("memory-safe") {
sstore(fromSlot, sub(fromAmount, value)) // LOC 92 - underflow partially checked
sstore(toSlot, add(toAmount, value)) // LOC 93 - ⚠ overflow unchecked
}
}

Risk

Likelihood: High

  • Extremely large token amounts can trigger overflow/underflow.

  • Attackers or integration errors could exploit this if unchecked.

Impact: High

  • _mint overflow → total supply or balances wrap unexpectedly.

  • _burn underflow → total supply or balances wrap to huge values.

  • _transfer overflow → recipient balances could wrap, inflating tokens.

Proof of Concept

If I try mint or burn huge amount, that can potentially cause overflow.

// _mint overflow example, if I provide type(uint256).max to mint.
let supply := sload(_totalSupply.slot) // LOC 118
sstore(_totalSupply.slot, add(supply, type(uint256).max)) // wraps around silently
// _burn underflow example, if I provide type(uint256).max to burn
let supply := sload(_totalSupply.slot) // LOC 133
sstore(_totalSupply.slot, sub(supply, type(uint256).max)) // wraps to huge number

Recommended Mitigation

Please try manual assembly checks if you want to use inline assembly.

Or use standard solidity arithmetic.

- sstore(supplySlot, add(supply, value)) // LOC 118
+ if gt(add(supply, value), type(uint256).max) { revert(0, 0) }
+ sstore(supplySlot, add(supply, value))
- sstore(accountBalanceSlot, add(accountBalance, value)) // LOC 124
+ if lt(add(accountBalance, value), accountBalance) { revert(0,0) }
+ sstore(accountBalanceSlot, add(accountBalance, value))
- sstore(supplySlot, sub(supply, value)) // LOC 133
+ if lt(supply, value) { revert(0, 0) }
+ sstore(supplySlot, sub(supply, value))
- sstore(accountBalanceSlot, sub(accountBalance, value)) // LOC 139
+ if lt(accountBalance, value) { revert(0,0) }
+ sstore(accountBalanceSlot, sub(accountBalance, value))
- sstore(toSlot, add(toAmount, value)) // LOC 93
+ if lt(add(toAmount, value), toAmount) { revert(0,0) }
+ sstore(toSlot, add(toAmount, value))

Support

FAQs

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

Give us feedback!