aurora-is-near / aurora-engine

⚙️ Aurora Engine implements an Ethereum Virtual Machine (EVM) on the NEAR Protocol.
https://doc.aurora.dev/develop/compat/evm
322 stars 79 forks source link

Fix underflow in modexp gas calculation #883

Closed guidovranken closed 6 months ago

guidovranken commented 7 months ago

Description

In calc_iter_count(), which is part of the modexp precompile gas calculation, the most-significant bit of the exponent is added to the intermediate result. The MSB is computed using U256::from(exp.bits()) - U256::from(1)) in:

Ok(U256::from(8) * U256::from(exp_len - 32) + U256::from(exp.bits()) - U256::from(1))

This works only if the exponent is non-zero; subtracting 1 from zero results in an underflow, leading to inaccurate gas requirements for specific inputs.

Geth computes the MSB as follows; notice the condition bitlen > 0:

    var msb int
    if bitlen := expHead.BitLen(); bitlen > 0 {
        msb = bitlen - 1
    }

This PR changes the underflowing subtraction into a saturating_sub so that an underflow never occurs.

Performance / NEAR gas cost considerations

Testing

Fuzzing.

How should this be reviewed

A test has been added to modexp.rs which asserts that the required gas for the specified input is 65536; an equivalent Go program which uses Geth to demonstrate this is provided below:

package main

import (
    "github.com/ethereum/go-ethereum/core/vm"
    "github.com/ethereum/go-ethereum/common"
    "fmt"
)

func main() {
    input := []byte{
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x26, 0x00}
    modexp_address := common.BytesToAddress([]byte{0x05})
    gas := vm.PrecompiledContractsBerlin[modexp_address].RequiredGas(input)
    fmt.Println(gas)
}

Additional information