Solidity Optimizer and ABIEncoderV2 Bug Announcement
We have received a report through the Ethereum bug bounty program regarding a flaw in the new experimental ABI encoder, ABIEncoderV2. After investigation, we discovered that there are multiple variations of this bug. While the ABI encoder is still experimental, it is already being used on the mainnet, so we believe it is important to make a prominent announcement about it. Additionally, we have identified two low-impact bugs in the optimizer, one of which has already been fixed with Solidity version 0.5.6. Both bugs were introduced in version 0.5.5. More details about these bugs can be found in the second part of this announcement. The fixes for all of these bugs can be found in the 0.5.7 release. We would like to give credit to the Melonport team (Travis Jacobs & Jenna Zenk) and the Melon Council (Nick Munoz-McDonald, Martin Lundfall, Matt di Ferrante & Adam Kolar) for reporting this through the Ethereum bug bounty program.
If you have deployed contracts that use the experimental ABI encoder V2, they may be affected by these bugs. To be affected, the contracts must include the following directive in the source code:
pragma experimental ABIEncoderV2;
There are specific conditions that need to be met for the bug to trigger, which are explained in detail below in the technical details section. We estimate that there are around 2500 contracts on the mainnet that use the experimental ABIEncoderV2, but it is unclear how many of them contain the bug.
To check if your contract is vulnerable, the bug will only manifest itself when the following conditions are met:
– Storage data involving arrays or structs is directly sent to an external function call or to abi.encode/event data without prior assignment to a local (memory) variable.
– There is an array that contains elements with a size less than 32 bytes or a struct that has elements that share a storage slot or members of type bytesNN shorter than 32 bytes.
If your contract meets these conditions and you want to verify if it is indeed vulnerable, you can reach out to us via email@example.com.
To prevent these types of flaws in the future, we suggest having a rigorous set of end-to-end tests for your contracts that verify all code paths. Bugs in the Solidity compiler can be difficult to detect with vulnerability detectors, so thorough testing is key.
While any bug can have varying consequences depending on program control flow, we expect that these bugs are more likely to cause malfunctions rather than exploitability. The bugs may send corrupt parameters on method invocations to other contracts under certain circumstances.
Please see the timeline below for a summary of the bug discovery and our actions taken:
– 2019-03-16: Bug reported through bug bounty program regarding corruption caused when reading from arrays of booleans directly from storage into ABI encoder.
– 2019-03-16 to 2019-03-21: Investigation of the root cause and analysis of affected contracts. Discovered a high count of contracts compiled with the experimental encoder on the mainnet without verified source code. Found additional ways to trigger the bug, including using structs. Also discovered an array overflow bug in the same routine. Checked a handful of contracts on Github and found none were affected. Made a bugfix to the ABI encoder.
– 2019-03-20: Decision made to make information public to prevent further vulnerable contracts from being deployed on mainnet.
– 2019-03-26: New compiler release, version 0.5.7. Announcement post released.
The Contract ABI is a specification for data exchange with contracts. It supports various data types, including simple values, arrays, and structs. When a contract receives input data, it decodes it using the ABI decoder, and before returning or sending data to another contract, it encodes it using the ABI encoder. The Solidity compiler generates the encoder and decoder for each defined function in a contract. The experimental ABI encoder, called ABI Encoder V2, was introduced in 2017 with the goal of being more flexible, safe, performant, and auditable.
The flaw in the experimental ABI encoder is that it does not handle non-integer values shorter than 32 bytes correctly. This applies to types like bytesNN, bool, enum, and others when they are part of an array or a struct and directly encoded from storage. The bug occurs when these storage references are used directly inside abi.encode(…), as arguments in external function calls, or in event data without prior assignment to a local variable. Using return does not trigger the bug. The types bytesNN and bool will result in corrupted data, while enum might lead to an invalid revert. Arrays with elements shorter than 32 bytes may not be handled correctly, even if the base type is an integer type. Encoding such arrays in the aforementioned way can lead to other data in the encoding being overwritten if the number of elements encoded is not a multiple of the number of elements that fit in a single slot.
In addition to the ABI encoder bug, two bugs have been found in the optimizer. These bugs are unlikely to occur unless inline assembly is used. The bugs have been identified through the use of Solidity in OSS-Fuzz, a security toolkit for finding issues in projects. The optimizer bugs involve mishandling of overflow in arithmetic operations and incorrect handling of the byte opcode in certain cases.
We apologize for any inconvenience caused by these bugs and appreciate your understanding as we work to address and fix them. Please update your Solidity compiler to version 0.5.7 to ensure that you have the fixes for all of these bugs.