Earlier this week, ChainSecurity responsibly disclosed a security issue in the proposed EIP 1283 feature of Constantinople. This new feature lowers the gas costs for the so-called dirty storage writes. The cheaper storage writes, however, enable reentrancy attacks for the transfer and send Solidity statements, which are safe in the current version due to the limited constant amount of gas they provide to the callee. Full details on the new reentrancy vulnerability, including an example exploit can be found in our previous article. Following the disclosure and discussions with key stakeholders, a decision to delay the Constantinople upgrade was made.
After the decision, the ChainSecurity team focused on scanning deployed smart contracts to assess if there are any relevant contracts that could be vulnerable to the new reentrancy attack. We joined forces with Trail of Bits and Tomasz Kolinko to help each other with this task and complement our efforts. Our summary report and recommendations regarding EIP 1283 can be found here.
To discover vulnerable contracts, we formulated a generic property that captures dangerous behaviors that can be exploited (see here for an example exploit):
For our experiment, we used a generic property that captures the exploit from our previous article; we illustrate the two functions used in the exploit in Fig. 1. Namely, we searched for contracts that have two functions f() and g() such that:
- Function f() executes a CALL, followed by an SLOAD, followed by a state changing operation (gas-restricted CALL, SSTORE, or LOG) that depends on the SLOAD.
- Function g() can be executed within 1600 gas and executes an SSTORE that conflicts with the SLOAD in f().
Here, f() is the first function called by the attacker and g() is the one she invokes to re-enter the contract and modify its storage via the SSTORE instruction. In our analysis, we assumed the contracts are in an arbitrary initial state (thus we over-approximate the contract’s space of feasible states, which may result in false positives).
We remark that even if all deployed contracts satisfy the generic property describe above, this does not entail that EIP 1283 is safe to adopt since it could affect other contract-specific properties, not captured by this generic property.
Results and findings
We selected contracts that hold ETH or have processed transactions and pre-filtered those that have low-gas functions (<1600 gas). We evaluated our dataset using different security tools and manually inspected several hundred contracts that matched the exploit pattern described above:
- A small fraction of these turned out to be false positives because the SSTORE in g() does not overlap with the SLOAD in f(). Such false positives can be filtered using a more precise symbolic analysis.
- The majority of the contract were confirmed as reentrant but are not exposed to a major risk. This is because the low-gas function g() can be invoked only by privileged users (such as the contract’s owner), which means that only privileged users can trigger the reentrancy.
- One of the matched contracts, identified by Tomasz, was confirmed to be a true positive. Instances of this contract are deployed at the following addresses:
We remark that our findings are neither exhaustive nor complete. The exploit pattern we have considered does not capture all possible cases that could endanger a contract. For example, DELEGATECALL must be also considered as a state-changing operation. Further, we have not considered all possible contracts, as this would be very time consuming to inspect all contracts manually.
The main lesson is that seemingly benign changes to the semantics of the Ethereum Virtual Machine (EVM) inevitably affect the semantics of all deployed contracts. The reentrancy vulnerability described above is one example of what could go wrong if EIP 1283 is adopted. To ensure that all contracts would be secure with respect to the change, we would need to consider all contract-specific properties, which is infeasible to do in practice.
Therefore, we strongly believe that changes that affect the semantics of deployed contracts must be avoided. Security firms typically audit clients with respect to the current EVM specification, and any changes would invalidate such efforts and could expose relevant contracts.
After conducting the above experiments, ChainSecurity, Trail of Bits, and Tomasz Kolinko prepared an incident report to summarize all findings and describe important recommendations. You can read the full report here.