Token-0x

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

totalSupply_ Function Assembly Return Bypassing Solidity Return Mechanism

Author Revealed upon completion

totalSupply_ Function Assembly Return Bypassing Solidity Return Mechanism

Description

Solidity functions declared as internal view returns (uint256) should pass their return values through the stack to callers. However, totalSupply_() uses inline assembly to directly execute return(0x00, 0x20), which terminates the current execution context and returns raw memory data, bypassing Solidity's standard return mechanism. When called by other Solidity functions (e.g., totalSupply() public calling totalSupply_()), callers fail to receive the correct return value, instead getting undefined/zero values, causing logic errors.

function totalSupply_() internal view returns (uint256) {
assembly {
let slot := _totalSupply.slot
let supply := sload(slot)
mstore(0x00, supply)
@> return(0x00, 0x20) // Incorrect: terminates execution and bypasses Solidity return
}
}

Risk

Likelihood:

  • Currently not triggered as the contract doesn't internally call this function; however, its internal visibility makes it vulnerable if extended/reused (e.g., in child contracts or new features)

  • Developers may mistakenly assume it behaves like standard Solidity functions, using it in composite logic (e.g., adding virtual supplies, snapshots)

Impact:

  • Caller functions cannot obtain correct _totalSupply values, potentially causing persistent zero/random results

  • If used in critical logic (token distribution, staking ratios, governance weights), could lead to fund misallocation or protocol state inconsistencies

Proof of Concept

  • If future contract ERC20 implements:

function totalSupply() public view virtual returns (uint256) {
uint256 virSupply = 100;
return totalSupply_() + virSupply;
}
  • Due to the return value issue in totalSupply_(), totalSupply() will permanently lose the + virSupply addition.

  • Add test to Token.t.sol:

function test_1_totalsupply_noSafe() public {
console.log(token.totalSupply(), "token.totalSupply()");
}
[PASS] test_1_totalsupply_noSafe() (gas: 11185)
Logs:
0 token.totalSupply() // Incorrect output (expected: 100)

Recommended Mitigation

function totalSupply_() internal view returns (uint256) {
assembly {
let slot := _totalSupply.slot
let supply := sload(slot)
- mstore(0x00, supply)
- return(0x00, 0x20)
+ // Return via Solidity ABI: store value at return slot (0x00)
+ // DO NOT use `return` opcode - let Solidity handle it
+ mstore(0x00, supply)
}
// Solidity will auto-return the value at 0x00
}

Support

FAQs

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

Give us feedback!