Size Limit in smart contracts

by ImmuneBytes
stack size limit

We are once again back with the next part of All About Smart Contract Bugs & Security – A cakewalk series, discussing Stack Size Limit in the context of smart contracts. Don’t forget to check out the other parts in this series to know about Integer Overflow and Underflow and Timestamp Dependence and how these bugs affect smart contracts.

The EVM is not a register machine but a stack machine, so all computations are performed on a data area called the stack. It has a maximum size of 1024 elements and contains words of 256 bits. The call stack is utilized when a contract invokes another contract, or itself by means of this.f(), is bounded to 1024 frames. If this limit is reached, then a further invocation will throw an exception.

An attacker contract typically generates a recurrent sequence of nested calls to create an almost full call stack. Once the call stack is almost full, the vulnerable contract is invoked.

Now, what is the stack size limit and how is it exploited?

Every time a contract invokes an external contract (or even itself) the call stack associated with that transaction grows by one frame. The call stack is limited to 1024 frames: when this limit is reached, a further invocation throws an exception. 

An adversary begins by initiating an almost-full call stack by making a sequence of nested calls and then invokes the victim’s function, which will fail upon a further invocation. If the exception is not properly handled by the victim’s contract, the adversary could manage to succeed in his attack. This vulnerability could be exploited together with others: e.g., exploiting the “exception disorder” and “stack size limit” vulnerabilities. 

Let’s take a look on how an attacker can exploit a stack size vulnerability in your smart contract through a real-world implementation:

contract Governmental{

address public owner;

address public lastInvestor;

uint public jackpot = 1 eth;

uint public lastInvestmentTimestamp;

uint public ONE_MINUTE = 1 minutes;

 

function Governmental(){

owner = msg.sender;

if(msg.value < 1 eth ) throw;

}

 

function invest(){

if (msg < jackpot/2) throw;

lastInvestor = msg.sender;

jackpot += msg.value/2;

lastInvestmentTimestamp = block.timestamp;

}

function resetInvestment(){

if(block.timestamp > lastInvestmentTimestamp + ONE_MINUTE) throw;

lastInvestor(jackpot);

owner.send(this.balance – 1 eth);

lastInvestor = 0;

jackpot = 1 eth;

lastInvestmentTimestamp = 0;

 

}

}

 

contract Mallory{

function attack(address target, uint count){

if(0 <= count && count<1023)

this.attack.gas(msg.gas – 2000) (target, count+1);

else

Governmental(target).resetInvestment();

}

}

In the above-given example, the Governmental contract allows for users to make investments in the contract and the last one to make an investment without another investment occurring for 1 min receives the jackpot and sends the remaining ether to the contract owner. 

Here Governmental is the vulnerable contract that is being exploited by the malicious contract called Mallory. Essentially, Governmental is a honey pot trap that collects ether and only pays out to the owner. Mallory exploits the vulnerability stack size limit by recursively calling itself and in turn growing the stack. Resulting in the stack being enlarged.

This failure allows the balance of the Governmental contract to grow every time this attack is executed due to the actual winner not being paid. For the owner to siphon the pooled ether from Governmental, the owner would just allow for another round to terminate correctly.

 

How to make it better?

This cause of vulnerability has been addressed by a hard-fork of the Ethereum blockchain. The fork changed the cost of several EVM instructions and redefined the way to compute the gas consumption of call and delegatecall. After the fork, a caller can allocate at most 63/64 of its gas: since, currently, the gas limit per block is ∼4,7M units, this implies that the maximum reachable depth of the call stack is always less than 1024 frames.

This was ImmuneBytes’ take on the Stack Size Limit vulnerability to show you what consequences one can face when deciding to go forth with an unaudited code. Connect with the team of security professionals at ImmuneBytes today to get your smart contract free of any vulnerabilities and loopholes.

Tune in next Thursday for Part-VI of this series wherein we discuss the Transaction Ordering Dependency in smart contracts.

Spread the love

You may also like