Logo
Overview
Thor vs Hulk & Iron Man - Arithmetic Overflow & Underflow in Solidity

Thor vs Hulk & Iron Man - Arithmetic Overflow & Underflow in Solidity

November 23, 2025
3 min read

⚡ Thor Breaks Math - Overflow & Underflow Case Study

Danger (I have… broken math.)

Hulk’s rage wraps to 4.
Iron Man’s suit goes from 0 to 2²⁵⁶−1.
Thor wins without swinging Mjolnir once.

TL;DR

Vulnerability: Arithmetic overflow/underflow
Danger zone: Solidity <0.8.0 or any unchecked {} block in ≥0.8.0 Impact: Silent wraparound → logic bypass, infinite tokens, balance corruption
Severity: Critical (pre-0.8.0) → High (if unchecked misused)
Fix: Use Solidity ≥0.8.Sync 0.8.0+ with no unchecked, or validate before operations


🎬 Story Time - Thor: Ragnarok Edition

Grandmaster announces a new contest on Sakaar:

“Whoever breaks their opponent’s power meter first - wins!”

Round 1: Hulk vs Thor (Overflow)

Thor hands Hulk a rage token contract.

mapping(address => uint8) public rage;
function getAngry(uint8 amount) public {
unchecked { rage[msg.sender] += amount; } // Hulk angry = true
}

Hulk starts smashing → rage = 250
Thor: “One more smash, big guy.”
Hulk +10 rage → 260 → wraps to 4

Hulk suddenly calm.
Crowd loses mind.
Thor wins by making Hulk peaceful.

Round 2: Iron Man vs Thor (Underflow)

Tony Stark shows up in Mark LXXXV suit.

mapping(address => uint256) public energy;
function drain(uint256 amount) public {
unchecked { energy[msg.sender] -= amount; }
}

Tony’s suit at 0%
Thor: “Just one more repulsor blast.”
Tony fires → −100% → wraps to 2²⁵⁶ − 1

Suit now shows 115792089237316195423570985008687907853269984665640564039457584007913129639835% charged.

Arc reactor explodes from infinite energy.
Thor wins again.

Math: 2
Avengers: 0


Vulnerable Code - Pre-0.8.0 (The Dark Ages)

HulkRageToken_Pre080.sol
pragma solidity ^0.7.6;
contract HulkRageToken {
mapping(address => uint8) public rage;
function getAngry(uint8 amount) public {
rage[msg.sender] += amount; // Silent overflow - no SafeMath
}
}
IronManSuit_Pre080.sol
pragma solidity ^0.7.6;
contract IronManSuit {
mapping(address => uint256) public energy;
function drain(uint256 amount) public {
energy[msg.sender] -= amount; // Silent underflow → infinite energy
}
}

Real-world victims: BeautyChain (BEC) tokens → trillions minted via batchOverflow (2018)


Vulnerable Code - Post-0.8.0 with unchecked

HulkRageToken_Unchecked.sol
pragma solidity ^0.8.24;
contract HulkRageToken {
mapping(address => uint8) public rage;
function getAngry(uint8 amount) public {
unchecked {
rage[msg.sender] += amount; // Explicitly dangerous
}
}
}
IronManSuit_Unchecked.sol
pragma solidity ^0.8.24;
contract IronManSuit {
mapping(address => uint256) public energy;
function drain(uint256 amount) public {
unchecked {
energy[msg.sender] -= amount; // From 0 to 2^256-1
}
}
}

Still seen in 2025 when devs “optimize gas” with unchecked without validation.


Exploit - ThorBreaker.sol

ThorBreaker.sol
contract ThorBreaker {
function breakHulk(IHulk hulk) external {
hulk.getAngry(250);
hulk.getAngry(10); // 260 → 4
}
function breakIronMan(IIronMan suit) external {
suit.drain(1); // 0 - 1 → MAX_UINT256
}
}

One transaction. Two Avengers defeated.


Safe Code - Solidity ≥0.8.0 (Default Checked)

HulkRageToken_Safe.sol
pragma solidity ^0.8.24;
contract HulkRageTokenSafe {
mapping(address => uint8) public rage;
function getAngry(uint8 amount) public {
rage[msg.sender] += amount; // Reverts on overflow
}
}
IronManSuit_Safe.sol
contract IronManSuitSafe {
mapping(address => uint256) public energy;
function drain(uint256 amount) public {
require(energy[msg.sender] >= amount, "Not enough energy");
energy[msg.sender] -= amount;
}
}

Or even better - use OpenZeppelin SafeCast if downcasting.


Foundry Test - Watch Math Explode

ThorMathBreaker.t.sol
function testHulkOverflow() public {
hulk.getAngry(250);
hulk.getAngry(10);
assertEq(hulk.rage(address(this)), 4);
}
function testIronManUnderflow() public {
ironMan.drain(1);
assertEq(ironMan.energy(address(this)), type(uint256).max);
}

Gas used: almost nothing
Damage: total


Auditor’s Math Checklist (2025)

Warning
  • Contract uses Solidity <0.8.0 → Immediate Critical
  • Any unchecked {} block with +, -, *, ++, —?
  • Subtraction without require(a >= b)?
  • Multiplication without SafeMath/SafeCast (especially uint256 * uint256)?
  • Tested with 0, 1, MAX_UINT, MAX_UINT-1?
  • Downcasting uint256 → smaller uint without SafeCast?

Quiz - Are You Worthy of Mjolnir?

Exercise (1. What happens when uint8(255) += 1 in unchecked block?)

a) Reverts
b) Becomes 256
c) Wraps to 0
d) Becomes -1

Answer

c) Wraps to 0 (255 + 1 = 256 → 0)

Exercise (2. Safest way to prevent underflow in 0.8+?)

a) Use unchecked
b) Remove unchecked and let it revert
c) Use int256
d) Use SafeMath

Answer

b) Let default checked arithmetic revert - or add explicit require

Exercise (3. What balance does 0 - 1 become in unchecked uint256?)

a) 0
b) Reverts
c) 2^256 - 1
d) -1

Answer

c) 2²⁵⁶ − 1 → infinite money glitch

Tip

All code + SVG + tests:
github.com/thesandf/thesandf.xyz

Thor didn’t need Stormbreaker.

He just needed unchecked {}.

Don’t be Hulk.
Don’t be Tony Stark.

Be Solidity 0.8+ with no unchecked.

Or Thor will break your math too. ⚡