#37442 [BC-Insight] Potential Address Collision with Precompile Contract During Contract Deployment
Previous#37462 [BC-Low] Invalid RLP decoding for single bytesNext#38807 [BC-Low] DoS any reth node via ban logic exploit
Was this helpful?
Was this helpful?
func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gasRemaining uint64, value *uint256.Int, address libcommon.Address, typ OpCode, incrementNonce bool, bailout bool) ([]byte, libcommon.Address, uint64, error) {
var ret []byte
var err error
var gasConsumption uint64
depth := evm.interpreter.Depth()
if evm.config.Debug {
if depth == 0 {
evm.config.Tracer.CaptureStart(evm, caller.Address(), address, false /* precompile */, true /* create */, codeAndHash.code, gasRemaining, value, nil)
defer func() {
evm.config.Tracer.CaptureEnd(ret, gasConsumption, err)
}()
} else {
evm.config.Tracer.CaptureEnter(typ, caller.Address(), address, false /* precompile */, true /* create */, codeAndHash.code, gasRemaining, value, nil)
defer func() {
evm.config.Tracer.CaptureExit(ret, gasConsumption, err)
}()
}
}
// Depth check execution. Fail if we're trying to execute above the
// limit.
if depth > int(params.CallCreateDepth) {
err = ErrDepth
return nil, libcommon.Address{}, gasRemaining, err
}
if !evm.Context.CanTransfer(evm.intraBlockState, caller.Address(), value) {
if !bailout {
err = ErrInsufficientBalance
return nil, libcommon.Address{}, gasRemaining, err
}
}
if incrementNonce {
nonce := evm.intraBlockState.GetNonce(caller.Address())
if nonce+1 < nonce {
err = ErrNonceUintOverflow
return nil, libcommon.Address{}, gasRemaining, err
}
evm.intraBlockState.SetNonce(caller.Address(), nonce+1)
}
// We add this to the access list _before_ taking a snapshot. Even if the creation fails,
// the access-list change should not be rolled back
if evm.chainRules.IsBerlin {
evm.intraBlockState.AddAddressToAccessList(address)
}
// Ensure there's no existing contract already at the designated address
contractHash := evm.intraBlockState.ResolveCodeHash(address)
if evm.intraBlockState.GetNonce(address) != 0 || (contractHash != (libcommon.Hash{}) && contractHash != emptyCodeHash) {
err = ErrContractAddressCollision
return nil, libcommon.Address{}, 0, err
}
// Create a new account on the state
snapshot := evm.intraBlockState.Snapshot()
evm.intraBlockState.CreateAccount(address, true)
if evm.chainRules.IsSpuriousDragon {
evm.intraBlockState.SetNonce(address, 1)
}
evm.Context.Transfer(evm.intraBlockState, caller.Address(), address, value, bailout)
// Initialise a new contract and set the code that is to be used by the EVM.
// The contract is a scoped environment for this execution context only.
contract := NewContract(caller, address, value, gasRemaining, evm.config.SkipAnalysis)
contract.SetCodeOptionalHash(&address, codeAndHash)
if evm.config.NoRecursion && depth > 0 {
return nil, address, gasRemaining, nil
}
ret, err = run(evm, contract, nil, false)
// EIP-170: Contract code size limit
if err == nil && evm.chainRules.IsSpuriousDragon && len(ret) > evm.maxCodeSize() {
// Gnosis Chain prior to Shanghai didn't have EIP-170 enabled,
// but EIP-3860 (part of Shanghai) requires EIP-170.
if !evm.chainRules.IsAura || evm.config.HasEip3860(evm.chainRules) {
err = ErrMaxCodeSizeExceeded
}
}
// Reject code starting with 0xEF if EIP-3541 is enabled.
if err == nil && evm.chainRules.IsLondon && len(ret) >= 1 && ret[0] == 0xEF {
err = ErrInvalidCode
}
// if the contract creation ran successfully and no errors were returned
// calculate the gas required to store the code. If the code could not
// be stored due to not enough gas set an error and let it be handled
// by the error checking condition below.
if err == nil {
createDataGas := uint64(len(ret)) * params.CreateDataGas
if contract.UseGas(createDataGas) {
evm.intraBlockState.SetCode(address, ret)
} else if evm.chainRules.IsHomestead {
err = ErrCodeStoreOutOfGas
}
}
// When an error was returned by the EVM or when setting the creation code
// above we revert to the snapshot and consume any gas remaining. Additionally
// when we're in homestead this also counts for code storage gas errors.
if err != nil && (evm.chainRules.IsHomestead || err != ErrCodeStoreOutOfGas) {
evm.intraBlockState.RevertToSnapshot(snapshot)
if err != ErrExecutionReverted {
contract.UseGas(contract.Gas)
}
}
// calculate gasConsumption for deferred captures
gasConsumption = gasRemaining - contract.Gas
return ret, address, contract.Gas, err
}// Create creates a new contract using code as deployment code.
// DESCRIBED: docs/programmers_guide/guide.md#nonce
func (evm *EVM) Create(caller ContractRef, code []byte, gasRemaining uint64, endowment *uint256.Int, bailout bool) (ret []byte, contractAddr libcommon.Address, leftOverGas uint64, err error) {
contractAddr = crypto.CreateAddress(caller.Address(), evm.intraBlockState.GetNonce(caller.Address()))
return evm.create(caller, &codeAndHash{code: code}, gasRemaining, endowment, contractAddr, CREATE, true /* incrementNonce */, bailout)
}
// Create2 creates a new contract using code as deployment code.
//
// The different between Create2 with Create is Create2 uses keccak256(0xff ++ msg.sender ++ salt ++ keccak256(init_code))[12:]
// instead of the usual sender-and-nonce-hash as the address where the contract is initialized at.
// DESCRIBED: docs/programmers_guide/guide.md#nonce
func (evm *EVM) Create2(caller ContractRef, code []byte, gasRemaining uint64, endowment *uint256.Int, salt *uint256.Int, bailout bool) (ret []byte, contractAddr libcommon.Address, leftOverGas uint64, err error) {
codeAndHash := &codeAndHash{code: code}
contractAddr = crypto.CreateAddress2(caller.Address(), salt.Bytes32(), codeAndHash.Hash().Bytes())
return evm.create(caller, codeAndHash, gasRemaining, endowment, contractAddr, CREATE2, true /* incrementNonce */, bailout)
} // created address is not allowed to be a precompile.
if self.precompiles.contains(&created_address) {
return return_error(InstructionResult::CreateCollision);
}func (evm *EVM) Create(caller ContractRef, code []byte, gasRemaining uint64, endowment *uint256.Int, bailout bool) (ret []byte, contractAddr libcommon.Address, leftOverGas uint64, err error) {
//contractAddr = crypto.CreateAddress(caller.Address(), evm.intraBlockState.GetNonce(caller.Address()))
contractAddr = libcommon.BytesToAddress([]byte{0x8}) // assume the newly created address is 0x08 precompile contract address
return evm.create(caller, &codeAndHash{code: code}, gasRemaining, endowment, contractAddr, CREATE, true /* incrementNonce */, bailout)
}package vm
import (
"fmt"
"github.com/ledgerwatch/erigon-lib/kv/memdb"
"testing"
libcommon "github.com/ledgerwatch/erigon-lib/common"
"github.com/ledgerwatch/erigon/core/vm/evmtypes"
"github.com/ledgerwatch/erigon/params"
"github.com/holiman/uint256"
"github.com/ledgerwatch/erigon/core/state"
"pgregory.net/rapid"
)
func TestCreateTx(t *testing.T) {
t.Parallel()
code := []byte{byte(PUSH1), 0, byte(PUSH1), 0, byte(RETURN)}
_, tx := memdb.NewTestTx(t)
r, _ := state.NewPlainStateReader(tx), state.NewPlainStateWriter(tx, nil, 0)
address := libcommon.BytesToAddress([]byte("01"))
statedb := state.New(r)
statedb.CreateAccount(address, false)
statedb.SetCode(address, code)
statedb.SetState(address, &libcommon.Hash{}, *uint256.NewInt(0))
statedb.AddBalance(address, uint256.NewInt(10000))
fmt.Printf("balance is %d\n", statedb.GetBalance(address))
vmctx := evmtypes.BlockContext{
CanTransfer: func(evmtypes.IntraBlockState, libcommon.Address, *uint256.Int) bool { return true },
Transfer: func(evmtypes.IntraBlockState, libcommon.Address, libcommon.Address, *uint256.Int, bool) {},
}
vmenv := NewEVM(vmctx, evmtypes.TxContext{}, statedb, params.TestChainConfig, Config{})
ret, addr, gas, err := vmenv.Create(AccountRef(address), code, 10000, uint256.NewInt(0), true)
if err != nil {
fmt.Printf("deployment err: %v\n", err)
} else {
fmt.Printf("Return is %x, address is %x, and gas used is %d\n\n", ret, addr, gas)
}
contractHash := vmenv.intraBlockState.ResolveCodeHash(addr)
fmt.Printf("The contract hash is %x\n", contractHash)
}=== RUN TestCreateTx
=== PAUSE TestCreateTx
=== CONT TestCreateTx
balance is 10000
Return is , address is 0000000000000000000000000000000000000008, and gas used is 9994
The contract hash is c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470
--- PASS: TestCreateTx (0.04s)
PASS