#38958 [BC-Low] EELS cant handle overflow gas calculation in modexp precompile
Submitted on Jan 18th 2025 at 22:13:02 UTC by @Omik for Attackathon | Ethereum Protocol
Report ID: #38958
Report Type: Blockchain/DLT
Report severity: Low
Target: https://github.com/ethereum/execution-specs
Impacts:
(Specifications) A bug in specifications with no direct impact on client implementations
Description
Brief/Intro
Hi Team, I was testing the gas calculation for modexp precompile for GETH, Nethermind, and comparing it with EELS. And I found out that EELS can't handle a very large base length like GETH, and Nethermind.
Vulnerability Details
When executing a precompile contract there are some steps that the EL takes before actually executing the precompile contract, those steps were:
calculate the gas
check if the gas didn't exceed the forwarded gas
if the gas check passes then it will execute the precompile based on the input that was given.
The bug was found when I noticed that Nethermind and GETH calculate the gas cost for modexp precompile using different type of integer. GETH calculates the gas using big number, which can't get overflow when the number goes bigger than uint256.max. And, Nethermind calculates the gas using UINT256 type, which will overflow if the number goes bigger than uint256.max.
Gas Calculation:
GETH: https://github.com/ethereum/go-ethereum/blob/master/core/vm/contracts.go#L367-L444
Nethermind: https://github.com/NethermindEth/nethermind/blob/master/src/Nethermind/Nethermind.Evm/Precompiles/ModExpPrecompile.cs#L43-L76
Since Nethermind modexp gas calculation allows us to overflow its calculation, I try to overflow this part of the code: https://github.com/NethermindEth/nethermind/blob/master/src/Nethermind/Nethermind.Evm/Precompiles/ModExpPrecompile.cs#L65
by setting the base length value as 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa0
or 115792089237316195423570985008687907853269984665640564039457584007913129639840
which when added to 96 it becomes 0
Thankfully, this didn't cause a consensus issue, because stateroot for both GETH and Nethermind is still the same, and Nethermind threw an exception when trying to cast the UINT256 number to the int data type in steps 3 (referring from executing precompile steps above). this leads to a slight difference between GETH and Nethermind, Geth exits the execution at step 2 with "out of gas", and Nethermind exits the execution at step 3 (https://github.com/NethermindEth/nethermind/blob/master/src/Nethermind/Nethermind.Evm/Precompiles/ModExpPrecompile.cs#L97) when trying to cast a large base length as an int. Both executions consume all of the gas and become a failed execution.
However, when I try to test this case against EELS it just throws an error and the program stops with a "raise OverflowError()", and this fails while trying to do the same thing as Nethermind did, which is adding a large base length with 96. (https://github.com/ethereum/execution-specs/blob/master/src/ethereum/cancun/vm/precompiled_contracts/modexp.py#L36)
Impact Details
Although there is no real issue with the two of the largest Ethereum clients, This might cause ambiguous interpretations for any new implementation that follows the Spec EELS
References
Add any relevant links to documentation or code
Link to Proof of Concept
https://gist.github.com/GibranAkbaromiL/edbf9c124b252b12f0a8a7eb6395d32e
Proof of Concept
Proof of Concept
To reproduce the behavior:
clone the goevmlab repo https://github.com/holiman/goevmlab
build the docker image, this is to prepare the binary for each evms. (I'm getting errors for building a lot of evms, that's why I only test the most common evm client which are GETH and Nethermind, and the eels to know the intended spec)
get the state test from this gist link https://gist.github.com/GibranAkbaromiL/edbf9c124b252b12f0a8a7eb6395d32e
run the docker image
run these commands
Was this helpful?