Arithmetic Overflow/Underflow Risk in Inline Assembly + High
Description
Issue: In ERC20Internals, the following assembly operations do not revert automatically:
function _mint(address account, uint256 value) internal {
assembly ("memory-safe") {
let supply := sload(supplySlot)
sstore(supplySlot, add(supply, value))
let accountBalance := sload(accountBalanceSlot)
sstore(accountBalanceSlot, add(accountBalance, value))
}
}
function _burn(address account, uint256 value) internal {
assembly ("memory-safe") {
let supply := sload(supplySlot)
sstore(supplySlot, sub(supply, value))
let accountBalance := sload(accountBalanceSlot)
sstore(accountBalanceSlot, sub(accountBalance, value))
}
}
function _transfer(address from, address to, uint256 value) internal returns (bool success) {
assembly ("memory-safe") {
sstore(fromSlot, sub(fromAmount, value))
sstore(toSlot, add(toAmount, value))
}
}
Risk
Likelihood: High
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.
let supply := sload(_totalSupply.slot)
sstore(_totalSupply.slot, add(supply, type(uint256).max))
let supply := sload(_totalSupply.slot)
sstore(_totalSupply.slot, sub(supply, type(uint256).max))
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))