Array Length Assignment
Overview
- Severity: High
- Confidence: Medium
- Affected Versions: < 0.5.0
What is the Array Length Assignment vulnerability?
In Ethereum, storage (state) variables associated with a smart contract are indexed by a 256-bit integer key. For dynamically sized arrays, the array values are stored sequentially starting from the hash of the key for the array. In earlier versions of Solidity, the length
propery of a dynamic array could be specified by the programmer, modifying which storage slots were available to be indexed through that array.
If a smart contract exposed functionality to set the length and index the array, an attacker could potentially access any storage slot of the smart contract. If write access were provided, an attacker could subsequently write over sensitive storage variables, such as the owner of the smart contract or other critical data.
Further reading: Maliciously Manipulate Storage Variables in Solidity
Technical example of vulnerable code
// SPDX-License-Identifier: Unlicense
pragma solidity 0.4.0;
contract VulnerableArray {
address[] public users; // A dynamically sized array
address public owner; // This value should not be able to be updated by any user
constructor() public {
users.push(msg.sender); // Initializing the array with some data
owner = msg.sender;
}
function register() public payable {
users.push(msg.sender);
}
function setUsersLength(uint256 _length) public {
// Problematic: Allowing arbitrary changes to array length
users.length = _length;
}
function setUser(uint256 _index, address _value) public {
// Problematic: Accessing elements beyond the intended bounds
data[_index] = _value;
}
function withdrawContractFunds() public {
require(msg.sender == owner);
msg.sender.transfer(address(this).balance);
}
// additional smart contract functionality
}
In the example above, the users
array is dynamic, with the function setUsersLength
allowing the length of the array to be set arbitrarily. The setUser
function allows anyone to set the value of any element in the array. This allows the user to access any storage slot in the smart contract, including the owner
variable, if they can determine the appropriate index to overwrite it. An attacker able to exploit this vulnerability could set themselves as the owner and subsequently call withdrawContractFunds
to drain the contract of its funds.
Technical example of how to fix the vulnerability
// SPDX-License-Identifier: Unlicense
pragma solidity 0.5.0;
contract VulnerableArray {
address[100] public users; // A dynamically sized array
address public owner; // This value should not be able to be updated by any user
constructor() public {
users.push(msg.sender); // Initializing the array with some data
owner = msg.sender;
}
function register() public payable {
users.push(msg.sender);
}
function setUser(uint256 _index, address _value) public {
data[_index] = _value;
}
function withdrawContractFunds() public {
require(msg.sender == owner);
msg.sender.transfer(address(this).balance);
}
// additional smart contract functionality
}
In the updated code example, the compiler version is specified as 0.5.0
, which no longer allows for assignment of the length
property of arrays. In addition, the users
array itself has been set to a fixed length to limit the bounds of its access.