Token-0x

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

[H-01] Unchecked Arithmetic Overflow in _mint Function (Missing Overflow Protection in Yul Assembly)

Author Revealed upon completion

Impact

Description

The _mint function performs two addition operations in Yul assembly without overflow checks: updating _totalSupply and the recipient's balance. Since Solidity 0.8.x's automatic overflow protection does not apply to inline assembly, an attacker can mint tokens that cause integer overflow, corrupting the token's total supply and account balances.

Root Cause

The function uses Yul assembly for gas optimization but omits mandatory overflow checks for addition operations. The vulnerability occurs because the add opcode in Yul silently wraps on overflow, unlike Solidity's checked arithmetic.


function _mint(address account, uint256 value) internal {
assembly ("memory-safe") {
if iszero(account) {
mstore(0x00, shl(224, 0xec442f05))
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, add(supply, value))// <@ Missing overflow check: supply + value may exceed max(uint256)
mstore(ptr, account)
mstore(add(ptr, 0x20), balanceSlot)
let accountBalanceSlot := keccak256(ptr, 0x40)
let accountBalance := sload(accountBalanceSlot)
sstore(accountBalanceSlot, add(accountBalance, value))// <@ Missing overflow check: accountBalance + value may exceed max(uint256)
}
}

Risk

Likelihood:

  • High: Any address with minting privileges (or any user if _mint is exposed) can intentionally cause overflow by minting when totalSupply or a balance is close to type(uint256).max


  • Easy Exploitation: The attack requires only a single transaction with calculated parameters, and the overflow condition is trivial to compute

Impact:

  • Direct Fund Loss: Account balances can be reduced from near-maximum values to near-zero through overflow corruption

  • Total Supply Corruption: _totalSupply becomes mathematically incorrect, breaking all supply-dependent calculations (inflation rates, percentages, etc.)

  • Protocol-Wide Disruption: The token's economic model becomes unusable, requiring emergency migration or abandonment

Proof of Concept

function test_mint_totalSupply_overflow() public {
uint256 max = type(uint256).max;
uint256 almostMax = max - 1000;
token.mint(alice,almostMax);
uint256 overflowAmount = 2000;
token.mint(alice, overflowAmount);
uint256 newSupply = token.totalSupply();
console.log("TotalSupply after overflow:", newSupply);
}

Result:

Ran 1 test for test/Token.t.sol:TokenTest
[PASS] test_mint_totalSupply_overflow() (gas: 59465)
Logs:
TotalSupply after overflow: 999


Recommended Mitigation

Add explicit overflow checks before both addition operations in the _mint function

Alternative Solution: Create reusable safe arithmetic functions to avoid code duplication and ensure consistent overflow protection across the entire contract.

function _mint(address account, uint256 value) internal {
assembly ("memory-safe") {
if iszero(account) {
mstore(0x00, shl(224, 0xec442f05))
mstore(add(0x00, 4), 0x00)
revert(0x00, 0x24)
}
let ptr := mload(0x40)
let balanceSlot := _balances.slot
let supplySlot := _totalSupply.slot
let supply := sload(supplySlot)
+ if gt(value, sub(not(0), supply)) {
+ // Panic: arithmetic overflow (error code 0x11)
+ mstore(0x00, shl(224, 0x4e487b71))
+ mstore(0x04, 0x11)
+ revert(0x00, 0x24)
+ }
sstore(supplySlot, add(supply, value))
mstore(ptr, account)
mstore(add(ptr, 0x20), balanceSlot)
let accountBalanceSlot := keccak256(ptr, 0x40)
let accountBalance := sload(accountBalanceSlot)
+ if gt(value, sub(not(0), accountBalance)) {
+ mstore(0x00, shl(224, 0x4e487b71))
+ mstore(0x04, 0x11)
+ revert(0x00, 0x24)
+ }
sstore(accountBalanceSlot, add(accountBalance, value))
}
}

Support

FAQs

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

Give us feedback!