Solidity Storage-Pointer – Using Storage in Constructor Allows Contract to Access Previously Allocated Slot

soliditystorage-pointer

Contract 1: donation timestamp and etherAmount are allocated 2nd and 3rd slots when donation struct is declared outside the constructor.

pragma solidity ^0.4.21;

contract DonationChallenge {
    struct Donation {
        uint256 timestamp;
        uint256 etherAmount;
    }
    // Slot 0
    Donation[] public donations;

    // Slot 1
    address public owner;

    Donation donation;
        
    function DonationChallenge() public payable {       
        owner = msg.sender;
        donation.timestamp = now;
        donation.etherAmount = 10;
        
    }
}

Contract 2: The donation struct is declared inside the constructor.
Now, timestamp and etherAmount are allocated 0th and 1st slot.

contract DonationChallenge {
    struct Donation {
        uint256 timestamp;
        uint256 etherAmount;
    }
    Donation[] public donations;

    address public owner;
        
    function DonationChallenge() public payable {       
        owner = msg.sender;
    
        Donation donation;
    
        donation.timestamp = now;
        donation.etherAmount = 10;
        
    }
}

What is the reason for this change (Slot 2 and 3 in first case & Slot 0 and 1 in the second) when I declare the struct outside vs inside the constructor?
Solidity gives a warning in this case, but I am trying to understand the reason for this behaviour.

Best Answer

First of all you are using a very old version of solidity, that behavior is no longer allowed since solidity 0.5.0 :

Uninitialized storage variables are now disallowed.

In your code Donation donation; is an uninitalized storage variable, now it would be more something like Donation storage donation;.

Anyway, a storage variable is quite simple : it's value is actually the storage slot to which it is refering to : from 0 to 2^256 in a format similar to uint256, the way it is used internally by the compiler is only a matter of how to interpret what it is pointing to.

The real problem is that the default value is the zero value.. so an uninitialized storage variable is essentially pointing to storage slot 0 by default and given your structure layout, donation.timestamp is written to slot 0 while donation.etherAmount is written to slot 1.

This could either overwrite existing data (like you did to both donations and owner) with invalid ones or worse, provide a way for attackers to overwrite your owner slot with the address / value of their choice, simply because you relied on an undefined behavior : using an uninitialized storage variable.

So don't do that, and use a more recent / more secure version of solidity such as 0.5.0+ that won't allow you to write such code.

I hope that answers your question.

Related Topic