Solidity Storage Array Error Announcement
This blog post is about two errors connected to storage arrays that are otherwise unrelated. Both have been around in the compiler for a long time and have only now been discovered, although a contract containing them is very likely to show test failure.
kim daenam with the help of nguyen phamboth of gridcurve discovered an issue where invalid data is being stored relative to arrays of signed integers.
This bug has been around since Solidity 0.4.7 and we consider it the more serious of the two. If these arrays use negative integers in a certain situation, it will lead to data corruption and therefore the error should be easy to catch.
Through the Ethereum bug bounty program, we received a report about a bug in the new experimental ABI encoder (known as ABIEncoderV2). The new ABI encoder is still marked as experimental, but nevertheless we think it deserves a prominent announcement as it is already in use on the mainnet. Credits to Ming Chuan Lin (from https://www.segundoestado.io) to discover and correct the error.
He version 0.5.10 Contains bug fixes. At this time, we do not plan to release a fix for the Solidity 0.4.x legacy series, but we may if there is popular demand.
Both bugs should be easily visible in tests that touch the relevant code paths.
Details about the two bugs can be found below.
Signed integer array error
Who should be worried?
If you have implemented contracts that use arrays of signed integers in storage and directly allocate
- an array literal with at least one negative value in it (x = (-1, -2, -3);) either
- an existing array of a different signed integer type
to it, this will lead to data corruption in the storage array.
Contracts that only assign individual array elements (i.e., with x(2) = -1;) are not affected.
How to check if the contract is vulnerable
If you are using arrays of signed integers in storage, try running tests using negative values. The effect should be that the actual value stored is positive instead of negative.
If you have a contract that meets these conditions and you want to check if the contract is really vulnerable, you can contact us via security@ethereum.org.
Technical details
Storage arrays can be allocated from arrays of different types. During this copy and assign operation, a type conversion is performed on each of the elements. In addition to conversion, especially if the signed integer type is shorter than 256 bits, certain bits of the value must be set to zero in preparation for storing multiple values in the same storage slot.
The zero bits were incorrectly determined from the source and not the destination type. This leads to too many bits being set to zero. In particular, the sign bit will be zero, making the value positive.
ABIEncoderV2 matrix error
Who should be worried?
If you have deployed contracts that use the experimental ABI V2 encoder, they may be affected. This means that only contracts that use the following directive within the source code can be affected:
pragma experimental ABIEncoderV2;
In addition, there are a number of requirements for the error to be triggered. See technical details below for more information.
How to check if the contract is vulnerable
The error is only manifested when all of the following conditions are met:
- Storage data involving arrays or structures is sent directly to an external function call, to abi.encode or to event data without prior assignment to a local (memory) variable AND
- this data contains an array of structures or an array of arrays of static size (ie, at least two-dimensional).
Besides that, in the following situation, your code is NOT affected:
- if you only return such data and do not use it in abi.encodeexternal calls or event data.
Possible consequences
Naturally, any bug can have widely varying consequences depending on the control flow of the program, but we expect this to be more likely to cause a malfunction than an exploitability.
The bug, when triggered, will under certain circumstances send corrupted parameters in method invocations to other contracts.
Technical details
During the encoding process, the experimental ABI encoder does not correctly advance to the next element in an array if the elements occupy more than a single storage slot.
This is only the case for elements that are structures or arrays of static size. Arrays of dynamically sized arrays or of elementary data types are not affected.
The specific effect you’ll see is that the data is “scrolled” in the encoded array: if you have an array of type uint(2)() and contains the data
((1, 2), (3, 4), (5, 6))then it will be coded as ((1, 2), (2, 3), (3, 4)) because the encoder only advances through a single slot between elements instead of two.
This post was co-composed by @axic, @chriseth, @holiman