VYPR
Moderate severityNVD Advisory· Published Nov 25, 2020· Updated Aug 4, 2024

Shallow copy bug in geth

CVE-2020-26241

Description

Go Ethereum, or "Geth", is the official Golang implementation of the Ethereum protocol. This is a Consensus vulnerability in Geth before version 1.9.17 which can be used to cause a chain-split where vulnerable nodes reject the canonical chain. Geth's pre-compiled dataCopy (at 0x00...04) contract did a shallow copy on invocation. An attacker could deploy a contract that writes X to an EVM memory region R, then calls 0x00..04 with R as an argument, then overwrites R to Y, and finally invokes the RETURNDATACOPY opcode. When this contract is invoked, a consensus-compliant node would push X on the EVM stack, whereas Geth would push Y. This is fixed in version 1.9.17.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
github.com/ethereum/go-ethereumGo
>= 1.9.7, < 1.9.171.9.17

Affected products

1

Patches

1
295693759e5d

core/vm: less allocations for various call variants (#21222)

https://github.com/ethereum/go-ethereumMartin Holst SwendeJul 16, 2020via ghsa
21 files changed · +450 191
  • cmd/evm/internal/t8ntool/flags.go+4 0 modified
    @@ -38,6 +38,10 @@ var (
     		Name:  "trace.nostack",
     		Usage: "Disable stack output in traces",
     	}
    +	TraceDisableReturnDataFlag = cli.BoolFlag{
    +		Name:  "trace.noreturndata",
    +		Usage: "Disable return data output in traces",
    +	}
     	OutputAllocFlag = cli.StringFlag{
     		Name: "output.alloc",
     		Usage: "Determines where to put the `alloc` of the post-state.\n" +
    
  • cmd/evm/internal/t8ntool/transition.go+4 3 modified
    @@ -83,9 +83,10 @@ func Main(ctx *cli.Context) error {
     	if ctx.Bool(TraceFlag.Name) {
     		// Configure the EVM logger
     		logConfig := &vm.LogConfig{
    -			DisableStack:  ctx.Bool(TraceDisableStackFlag.Name),
    -			DisableMemory: ctx.Bool(TraceDisableMemoryFlag.Name),
    -			Debug:         true,
    +			DisableStack:      ctx.Bool(TraceDisableStackFlag.Name),
    +			DisableMemory:     ctx.Bool(TraceDisableMemoryFlag.Name),
    +			DisableReturnData: ctx.Bool(TraceDisableReturnDataFlag.Name),
    +			Debug:             true,
     		}
     		var prevFile *os.File
     		// This one closes the last file
    
  • cmd/evm/main.go+11 0 modified
    @@ -121,6 +121,14 @@ var (
     		Name:  "nostack",
     		Usage: "disable stack output",
     	}
    +	DisableStorageFlag = cli.BoolFlag{
    +		Name:  "nostorage",
    +		Usage: "disable storage output",
    +	}
    +	DisableReturnDataFlag = cli.BoolFlag{
    +		Name:  "noreturndata",
    +		Usage: "disable return data output",
    +	}
     	EVMInterpreterFlag = cli.StringFlag{
     		Name:  "vm.evm",
     		Usage: "External EVM configuration (default = built-in interpreter)",
    @@ -137,6 +145,7 @@ var stateTransitionCommand = cli.Command{
     		t8ntool.TraceFlag,
     		t8ntool.TraceDisableMemoryFlag,
     		t8ntool.TraceDisableStackFlag,
    +		t8ntool.TraceDisableReturnDataFlag,
     		t8ntool.OutputAllocFlag,
     		t8ntool.OutputResultFlag,
     		t8ntool.InputAllocFlag,
    @@ -172,6 +181,8 @@ func init() {
     		ReceiverFlag,
     		DisableMemoryFlag,
     		DisableStackFlag,
    +		DisableStorageFlag,
    +		DisableReturnDataFlag,
     		EVMInterpreterFlag,
     	}
     	app.Commands = []cli.Command{
    
  • cmd/evm/runner.go+5 3 modified
    @@ -108,9 +108,11 @@ func runCmd(ctx *cli.Context) error {
     	glogger.Verbosity(log.Lvl(ctx.GlobalInt(VerbosityFlag.Name)))
     	log.Root().SetHandler(glogger)
     	logconfig := &vm.LogConfig{
    -		DisableMemory: ctx.GlobalBool(DisableMemoryFlag.Name),
    -		DisableStack:  ctx.GlobalBool(DisableStackFlag.Name),
    -		Debug:         ctx.GlobalBool(DebugFlag.Name),
    +		DisableMemory:     ctx.GlobalBool(DisableMemoryFlag.Name),
    +		DisableStack:      ctx.GlobalBool(DisableStackFlag.Name),
    +		DisableStorage:    ctx.GlobalBool(DisableStorageFlag.Name),
    +		DisableReturnData: ctx.GlobalBool(DisableReturnDataFlag.Name),
    +		Debug:             ctx.GlobalBool(DebugFlag.Name),
     	}
     
     	var (
    
  • cmd/evm/staterunner.go+4 2 modified
    @@ -59,8 +59,10 @@ func stateTestCmd(ctx *cli.Context) error {
     
     	// Configure the EVM logger
     	config := &vm.LogConfig{
    -		DisableMemory: ctx.GlobalBool(DisableMemoryFlag.Name),
    -		DisableStack:  ctx.GlobalBool(DisableStackFlag.Name),
    +		DisableMemory:     ctx.GlobalBool(DisableMemoryFlag.Name),
    +		DisableStack:      ctx.GlobalBool(DisableStackFlag.Name),
    +		DisableStorage:    ctx.GlobalBool(DisableStorageFlag.Name),
    +		DisableReturnData: ctx.GlobalBool(DisableReturnDataFlag.Name),
     	}
     	var (
     		tracer   vm.Tracer
    
  • core/state/statedb.go+10 5 modified
    @@ -505,7 +505,7 @@ func (s *StateDB) getDeletedStateObject(addr common.Address) *stateObject {
     	}
     	// If no live objects are available, attempt to use snapshots
     	var (
    -		data Account
    +		data *Account
     		err  error
     	)
     	if s.snap != nil {
    @@ -517,11 +517,15 @@ func (s *StateDB) getDeletedStateObject(addr common.Address) *stateObject {
     			if acc == nil {
     				return nil
     			}
    -			data.Nonce, data.Balance, data.CodeHash = acc.Nonce, acc.Balance, acc.CodeHash
    +			data = &Account{
    +				Nonce:    acc.Nonce,
    +				Balance:  acc.Balance,
    +				CodeHash: acc.CodeHash,
    +				Root:     common.BytesToHash(acc.Root),
    +			}
     			if len(data.CodeHash) == 0 {
     				data.CodeHash = emptyCodeHash
     			}
    -			data.Root = common.BytesToHash(acc.Root)
     			if data.Root == (common.Hash{}) {
     				data.Root = emptyRoot
     			}
    @@ -540,13 +544,14 @@ func (s *StateDB) getDeletedStateObject(addr common.Address) *stateObject {
     		if len(enc) == 0 {
     			return nil
     		}
    -		if err := rlp.DecodeBytes(enc, &data); err != nil {
    +		data = new(Account)
    +		if err := rlp.DecodeBytes(enc, data); err != nil {
     			log.Error("Failed to decode state object", "addr", addr, "err", err)
     			return nil
     		}
     	}
     	// Insert into the live set
    -	obj := newObject(s, addr, data)
    +	obj := newObject(s, addr, *data)
     	s.setStateObject(obj)
     	return obj
     }
    
  • core/vm/contracts.go+12 5 modified
    @@ -102,12 +102,18 @@ var PrecompiledContractsYoloV1 = map[common.Address]PrecompiledContract{
     }
     
     // RunPrecompiledContract runs and evaluates the output of a precompiled contract.
    -func RunPrecompiledContract(p PrecompiledContract, input []byte, contract *Contract) (ret []byte, err error) {
    -	gas := p.RequiredGas(input)
    -	if contract.UseGas(gas) {
    -		return p.Run(input)
    +// It returns
    +// - the returned bytes,
    +// - the _remaining_ gas,
    +// - any error that occurred
    +func RunPrecompiledContract(p PrecompiledContract, input []byte, suppliedGas uint64) (ret []byte, remainingGas uint64, err error) {
    +	gasCost := p.RequiredGas(input)
    +	if suppliedGas < gasCost {
    +		return nil, 0, ErrOutOfGas
     	}
    -	return nil, ErrOutOfGas
    +	suppliedGas -= gasCost
    +	output, err := p.Run(input)
    +	return output, suppliedGas, err
     }
     
     // ECRECOVER implemented as a native contract.
    @@ -197,6 +203,7 @@ func (c *dataCopy) Run(in []byte) ([]byte, error) {
     type bigModExp struct{}
     
     var (
    +	big0      = big.NewInt(0)
     	big1      = big.NewInt(1)
     	big4      = big.NewInt(4)
     	big8      = big.NewInt(8)
    
  • core/vm/contracts_test.go+11 18 modified
    @@ -21,7 +21,6 @@ import (
     	"encoding/json"
     	"fmt"
     	"io/ioutil"
    -	"math/big"
     	"testing"
     	"time"
     
    @@ -72,10 +71,9 @@ var blake2FMalformedInputTests = []precompiledFailureTest{
     func testPrecompiled(addr string, test precompiledTest, t *testing.T) {
     	p := allPrecompiles[common.HexToAddress(addr)]
     	in := common.Hex2Bytes(test.Input)
    -	contract := NewContract(AccountRef(common.HexToAddress("1337")),
    -		nil, new(big.Int), p.RequiredGas(in))
    -	t.Run(fmt.Sprintf("%s-Gas=%d", test.Name, contract.Gas), func(t *testing.T) {
    -		if res, err := RunPrecompiledContract(p, in, contract); err != nil {
    +	gas := p.RequiredGas(in)
    +	t.Run(fmt.Sprintf("%s-Gas=%d", test.Name, gas), func(t *testing.T) {
    +		if res, _, err := RunPrecompiledContract(p, in, gas); err != nil {
     			t.Error(err)
     		} else if common.Bytes2Hex(res) != test.Expected {
     			t.Errorf("Expected %v, got %v", test.Expected, common.Bytes2Hex(res))
    @@ -91,10 +89,10 @@ func testPrecompiled(addr string, test precompiledTest, t *testing.T) {
     func testPrecompiledOOG(addr string, test precompiledTest, t *testing.T) {
     	p := allPrecompiles[common.HexToAddress(addr)]
     	in := common.Hex2Bytes(test.Input)
    -	contract := NewContract(AccountRef(common.HexToAddress("1337")),
    -		nil, new(big.Int), p.RequiredGas(in)-1)
    -	t.Run(fmt.Sprintf("%s-Gas=%d", test.Name, contract.Gas), func(t *testing.T) {
    -		_, err := RunPrecompiledContract(p, in, contract)
    +	gas := p.RequiredGas(in) - 1
    +
    +	t.Run(fmt.Sprintf("%s-Gas=%d", test.Name, gas), func(t *testing.T) {
    +		_, _, err := RunPrecompiledContract(p, in, gas)
     		if err.Error() != "out of gas" {
     			t.Errorf("Expected error [out of gas], got [%v]", err)
     		}
    @@ -109,11 +107,9 @@ func testPrecompiledOOG(addr string, test precompiledTest, t *testing.T) {
     func testPrecompiledFailure(addr string, test precompiledFailureTest, t *testing.T) {
     	p := allPrecompiles[common.HexToAddress(addr)]
     	in := common.Hex2Bytes(test.Input)
    -	contract := NewContract(AccountRef(common.HexToAddress("31337")),
    -		nil, new(big.Int), p.RequiredGas(in))
    -
    +	gas := p.RequiredGas(in)
     	t.Run(test.Name, func(t *testing.T) {
    -		_, err := RunPrecompiledContract(p, in, contract)
    +		_, _, err := RunPrecompiledContract(p, in, gas)
     		if err.Error() != test.ExpectedError {
     			t.Errorf("Expected error [%v], got [%v]", test.ExpectedError, err)
     		}
    @@ -132,23 +128,20 @@ func benchmarkPrecompiled(addr string, test precompiledTest, bench *testing.B) {
     	p := allPrecompiles[common.HexToAddress(addr)]
     	in := common.Hex2Bytes(test.Input)
     	reqGas := p.RequiredGas(in)
    -	contract := NewContract(AccountRef(common.HexToAddress("1337")),
    -		nil, new(big.Int), reqGas)
     
     	var (
     		res  []byte
     		err  error
     		data = make([]byte, len(in))
     	)
     
    -	bench.Run(fmt.Sprintf("%s-Gas=%d", test.Name, contract.Gas), func(bench *testing.B) {
    +	bench.Run(fmt.Sprintf("%s-Gas=%d", test.Name, reqGas), func(bench *testing.B) {
     		bench.ReportAllocs()
     		start := time.Now().Nanosecond()
     		bench.ResetTimer()
     		for i := 0; i < bench.N; i++ {
    -			contract.Gas = reqGas
     			copy(data, in)
    -			res, err = RunPrecompiledContract(p, data, contract)
    +			res, _, err = RunPrecompiledContract(p, data, reqGas)
     		}
     		bench.StopTimer()
     		elapsed := float64(time.Now().Nanosecond() - start)
    
  • core/vm/evm.go+109 84 modified
    @@ -25,6 +25,7 @@ import (
     	"github.com/ethereum/go-ethereum/common"
     	"github.com/ethereum/go-ethereum/crypto"
     	"github.com/ethereum/go-ethereum/params"
    +	"github.com/holiman/uint256"
     )
     
     // emptyCodeHash is used by create to ensure deployment is disallowed to already
    @@ -41,23 +42,24 @@ type (
     	GetHashFunc func(uint64) common.Hash
     )
     
    +func (evm *EVM) precompile(addr common.Address) (PrecompiledContract, bool) {
    +	var precompiles map[common.Address]PrecompiledContract
    +	switch {
    +	case evm.chainRules.IsYoloV1:
    +		precompiles = PrecompiledContractsYoloV1
    +	case evm.chainRules.IsIstanbul:
    +		precompiles = PrecompiledContractsIstanbul
    +	case evm.chainRules.IsByzantium:
    +		precompiles = PrecompiledContractsByzantium
    +	default:
    +		precompiles = PrecompiledContractsHomestead
    +	}
    +	p, ok := precompiles[addr]
    +	return p, ok
    +}
    +
     // run runs the given contract and takes care of running precompiles with a fallback to the byte code interpreter.
     func run(evm *EVM, contract *Contract, input []byte, readOnly bool) ([]byte, error) {
    -	if contract.CodeAddr != nil {
    -		precompiles := PrecompiledContractsHomestead
    -		if evm.chainRules.IsByzantium {
    -			precompiles = PrecompiledContractsByzantium
    -		}
    -		if evm.chainRules.IsIstanbul {
    -			precompiles = PrecompiledContractsIstanbul
    -		}
    -		if evm.chainRules.IsYoloV1 {
    -			precompiles = PrecompiledContractsYoloV1
    -		}
    -		if p := precompiles[*contract.CodeAddr]; p != nil {
    -			return RunPrecompiledContract(p, input, contract)
    -		}
    -	}
     	for _, interpreter := range evm.interpreters {
     		if interpreter.CanRun(contract.Code) {
     			if evm.interpreter != interpreter {
    @@ -199,22 +201,14 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas
     		return nil, gas, ErrDepth
     	}
     	// Fail if we're trying to transfer more than the available balance
    -	if !evm.Context.CanTransfer(evm.StateDB, caller.Address(), value) {
    +	if value.Sign() != 0 && !evm.Context.CanTransfer(evm.StateDB, caller.Address(), value) {
     		return nil, gas, ErrInsufficientBalance
     	}
    -	var (
    -		to       = AccountRef(addr)
    -		snapshot = evm.StateDB.Snapshot()
    -	)
    +	snapshot := evm.StateDB.Snapshot()
    +	p, isPrecompile := evm.precompile(addr)
    +
     	if !evm.StateDB.Exist(addr) {
    -		precompiles := PrecompiledContractsHomestead
    -		if evm.chainRules.IsByzantium {
    -			precompiles = PrecompiledContractsByzantium
    -		}
    -		if evm.chainRules.IsIstanbul {
    -			precompiles = PrecompiledContractsIstanbul
    -		}
    -		if precompiles[addr] == nil && evm.chainRules.IsEIP158 && value.Sign() == 0 {
    +		if !isPrecompile && evm.chainRules.IsEIP158 && value.Sign() == 0 {
     			// Calling a non existing account, don't do anything, but ping the tracer
     			if evm.vmConfig.Debug && evm.depth == 0 {
     				evm.vmConfig.Tracer.CaptureStart(caller.Address(), addr, false, input, gas, value)
    @@ -224,35 +218,47 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas
     		}
     		evm.StateDB.CreateAccount(addr)
     	}
    -	evm.Transfer(evm.StateDB, caller.Address(), to.Address(), value)
    -	// 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, to, value, gas)
    -	contract.SetCallCode(&addr, evm.StateDB.GetCodeHash(addr), evm.StateDB.GetCode(addr))
    -
    -	// Even if the account has no code, we need to continue because it might be a precompile
    -	start := time.Now()
    +	evm.Transfer(evm.StateDB, caller.Address(), addr, value)
     
     	// Capture the tracer start/end events in debug mode
     	if evm.vmConfig.Debug && evm.depth == 0 {
     		evm.vmConfig.Tracer.CaptureStart(caller.Address(), addr, false, input, gas, value)
    -
    -		defer func() { // Lazy evaluation of the parameters
    -			evm.vmConfig.Tracer.CaptureEnd(ret, gas-contract.Gas, time.Since(start), err)
    -		}()
    +		defer func(startGas uint64, startTime time.Time) { // Lazy evaluation of the parameters
    +			evm.vmConfig.Tracer.CaptureEnd(ret, startGas-gas, time.Since(startTime), err)
    +		}(gas, time.Now())
     	}
    -	ret, err = run(evm, contract, input, false)
     
    +	if isPrecompile {
    +		ret, gas, err = RunPrecompiledContract(p, input, gas)
    +	} else {
    +		// 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.
    +		code := evm.StateDB.GetCode(addr)
    +		if len(code) == 0 {
    +			ret, err = nil, nil // gas is unchanged
    +		} else {
    +			addrCopy := addr
    +			// If the account has no code, we can abort here
    +			// The depth-check is already done, and precompiles handled above
    +			contract := NewContract(caller, AccountRef(addrCopy), value, gas)
    +			contract.SetCallCode(&addrCopy, evm.StateDB.GetCodeHash(addrCopy), code)
    +			ret, err = run(evm, contract, input, false)
    +			gas = contract.Gas
    +		}
    +	}
     	// 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.StateDB.RevertToSnapshot(snapshot)
     		if err != ErrExecutionReverted {
    -			contract.UseGas(contract.Gas)
    +			gas = 0
     		}
    +		// TODO: consider clearing up unused snapshots:
    +		//} else {
    +		//	evm.StateDB.DiscardSnapshot(snapshot)
     	}
    -	return ret, contract.Gas, err
    +	return ret, gas, err
     }
     
     // CallCode executes the contract associated with the addr with the given input
    @@ -277,23 +283,27 @@ func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte,
     	if !evm.Context.CanTransfer(evm.StateDB, caller.Address(), value) {
     		return nil, gas, ErrInsufficientBalance
     	}
    -	var (
    -		snapshot = evm.StateDB.Snapshot()
    -		to       = AccountRef(caller.Address())
    -	)
    -	// 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, to, value, gas)
    -	contract.SetCallCode(&addr, evm.StateDB.GetCodeHash(addr), evm.StateDB.GetCode(addr))
    -
    -	ret, err = run(evm, contract, input, false)
    +	var snapshot = evm.StateDB.Snapshot()
    +
    +	// It is allowed to call precompiles, even via delegatecall
    +	if p, isPrecompile := evm.precompile(addr); isPrecompile {
    +		ret, gas, err = RunPrecompiledContract(p, input, gas)
    +	} else {
    +		addrCopy := addr
    +		// 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, AccountRef(caller.Address()), value, gas)
    +		contract.SetCallCode(&addrCopy, evm.StateDB.GetCodeHash(addrCopy), evm.StateDB.GetCode(addrCopy))
    +		ret, err = run(evm, contract, input, false)
    +		gas = contract.Gas
    +	}
     	if err != nil {
     		evm.StateDB.RevertToSnapshot(snapshot)
     		if err != ErrExecutionReverted {
    -			contract.UseGas(contract.Gas)
    +			gas = 0
     		}
     	}
    -	return ret, contract.Gas, err
    +	return ret, gas, err
     }
     
     // DelegateCall executes the contract associated with the addr with the given input
    @@ -309,22 +319,26 @@ func (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []by
     	if evm.depth > int(params.CallCreateDepth) {
     		return nil, gas, ErrDepth
     	}
    -	var (
    -		snapshot = evm.StateDB.Snapshot()
    -		to       = AccountRef(caller.Address())
    -	)
    -	// Initialise a new contract and make initialise the delegate values
    -	contract := NewContract(caller, to, nil, gas).AsDelegate()
    -	contract.SetCallCode(&addr, evm.StateDB.GetCodeHash(addr), evm.StateDB.GetCode(addr))
    -
    -	ret, err = run(evm, contract, input, false)
    +	var snapshot = evm.StateDB.Snapshot()
    +
    +	// It is allowed to call precompiles, even via delegatecall
    +	if p, isPrecompile := evm.precompile(addr); isPrecompile {
    +		ret, gas, err = RunPrecompiledContract(p, input, gas)
    +	} else {
    +		addrCopy := addr
    +		// Initialise a new contract and make initialise the delegate values
    +		contract := NewContract(caller, AccountRef(caller.Address()), nil, gas).AsDelegate()
    +		contract.SetCallCode(&addrCopy, evm.StateDB.GetCodeHash(addrCopy), evm.StateDB.GetCode(addrCopy))
    +		ret, err = run(evm, contract, input, false)
    +		gas = contract.Gas
    +	}
     	if err != nil {
     		evm.StateDB.RevertToSnapshot(snapshot)
     		if err != ErrExecutionReverted {
    -			contract.UseGas(contract.Gas)
    +			gas = 0
     		}
     	}
    -	return ret, contract.Gas, err
    +	return ret, gas, err
     }
     
     // StaticCall executes the contract associated with the addr with the given input
    @@ -339,32 +353,43 @@ func (evm *EVM) StaticCall(caller ContractRef, addr common.Address, input []byte
     	if evm.depth > int(params.CallCreateDepth) {
     		return nil, gas, ErrDepth
     	}
    -	var (
    -		to       = AccountRef(addr)
    -		snapshot = evm.StateDB.Snapshot()
    -	)
    -	// 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, to, new(big.Int), gas)
    -	contract.SetCallCode(&addr, evm.StateDB.GetCodeHash(addr), evm.StateDB.GetCode(addr))
    +	// We take a snapshot here. This is a bit counter-intuitive, and could probably be skipped.
    +	// However, even a staticcall is considered a 'touch'. On mainnet, static calls were introduced
    +	// after all empty accounts were deleted, so this is not required. However, if we omit this,
    +	// then certain tests start failing; stRevertTest/RevertPrecompiledTouchExactOOG.json.
    +	// We could change this, but for now it's left for legacy reasons
    +	var snapshot = evm.StateDB.Snapshot()
     
     	// We do an AddBalance of zero here, just in order to trigger a touch.
     	// This doesn't matter on Mainnet, where all empties are gone at the time of Byzantium,
     	// but is the correct thing to do and matters on other networks, in tests, and potential
     	// future scenarios
    -	evm.StateDB.AddBalance(addr, big.NewInt(0))
    -
    -	// 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.
    -	ret, err = run(evm, contract, input, true)
    +	evm.StateDB.AddBalance(addr, big0)
    +
    +	if p, isPrecompile := evm.precompile(addr); isPrecompile {
    +		ret, gas, err = RunPrecompiledContract(p, input, gas)
    +	} else {
    +		// At this point, we use a copy of address. If we don't, the go compiler will
    +		// leak the 'contract' to the outer scope, and make allocation for 'contract'
    +		// even if the actual execution ends on RunPrecompiled above.
    +		addrCopy := addr
    +		// 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, AccountRef(addrCopy), new(big.Int), gas)
    +		contract.SetCallCode(&addrCopy, evm.StateDB.GetCodeHash(addrCopy), evm.StateDB.GetCode(addrCopy))
    +		// 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.
    +		ret, err = run(evm, contract, input, true)
    +		gas = contract.Gas
    +	}
     	if err != nil {
     		evm.StateDB.RevertToSnapshot(snapshot)
     		if err != ErrExecutionReverted {
    -			contract.UseGas(contract.Gas)
    +			gas = 0
     		}
     	}
    -	return ret, contract.Gas, err
    +	return ret, gas, err
     }
     
     type codeAndHash struct {
    @@ -466,9 +491,9 @@ func (evm *EVM) Create(caller ContractRef, code []byte, gas uint64, value *big.I
     //
     // The different between Create2 with Create is Create2 uses sha3(0xff ++ msg.sender ++ salt ++ sha3(init_code))[12:]
     // instead of the usual sender-and-nonce-hash as the address where the contract is initialized at.
    -func (evm *EVM) Create2(caller ContractRef, code []byte, gas uint64, endowment *big.Int, salt *big.Int) (ret []byte, contractAddr common.Address, leftOverGas uint64, err error) {
    +func (evm *EVM) Create2(caller ContractRef, code []byte, gas uint64, endowment *big.Int, salt *uint256.Int) (ret []byte, contractAddr common.Address, leftOverGas uint64, err error) {
     	codeAndHash := &codeAndHash{code: code}
    -	contractAddr = crypto.CreateAddress2(caller.Address(), common.BigToHash(salt), codeAndHash.Hash().Bytes())
    +	contractAddr = crypto.CreateAddress2(caller.Address(), common.Hash(salt.Bytes32()), codeAndHash.Hash().Bytes())
     	return evm.create(caller, codeAndHash, gas, endowment, contractAddr)
     }
     
    
  • core/vm/gen_structlog.go+8 2 modified
    @@ -24,6 +24,7 @@ func (s StructLog) MarshalJSON() ([]byte, error) {
     		MemorySize    int                         `json:"memSize"`
     		Stack         []*math.HexOrDecimal256     `json:"stack"`
     		ReturnStack   []math.HexOrDecimal64       `json:"returnStack"`
    +		ReturnData    []byte                      `json:"returnData"`
     		Storage       map[common.Hash]common.Hash `json:"-"`
     		Depth         int                         `json:"depth"`
     		RefundCounter uint64                      `json:"refund"`
    @@ -50,6 +51,7 @@ func (s StructLog) MarshalJSON() ([]byte, error) {
     			enc.ReturnStack[k] = math.HexOrDecimal64(v)
     		}
     	}
    +	enc.ReturnData = s.ReturnData
     	enc.Storage = s.Storage
     	enc.Depth = s.Depth
     	enc.RefundCounter = s.RefundCounter
    @@ -70,6 +72,7 @@ func (s *StructLog) UnmarshalJSON(input []byte) error {
     		MemorySize    *int                        `json:"memSize"`
     		Stack         []*math.HexOrDecimal256     `json:"stack"`
     		ReturnStack   []math.HexOrDecimal64       `json:"returnStack"`
    +		ReturnData    []byte                      `json:"returnData"`
     		Storage       map[common.Hash]common.Hash `json:"-"`
     		Depth         *int                        `json:"depth"`
     		RefundCounter *uint64                     `json:"refund"`
    @@ -104,11 +107,14 @@ func (s *StructLog) UnmarshalJSON(input []byte) error {
     		}
     	}
     	if dec.ReturnStack != nil {
    -		s.ReturnStack = make([]uint64, len(dec.ReturnStack))
    +		s.ReturnStack = make([]uint32, len(dec.ReturnStack))
     		for k, v := range dec.ReturnStack {
    -			s.ReturnStack[k] = uint64(v)
    +			s.ReturnStack[k] = uint32(v)
     		}
     	}
    +	if dec.ReturnData != nil {
    +		s.ReturnData = dec.ReturnData
    +	}
     	if dec.Storage != nil {
     		s.Storage = dec.Storage
     	}
    
  • core/vm/instructions.go+28 6 modified
    @@ -563,7 +563,7 @@ func opJumpSub(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([
     	if !callContext.contract.validJumpSubdest(posU64) {
     		return nil, ErrInvalidJump
     	}
    -	callContext.rstack.push(*pc)
    +	callContext.rstack.push(uint32(*pc))
     	*pc = posU64 + 1
     	return nil, nil
     }
    @@ -575,7 +575,7 @@ func opReturnSub(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx)
     	// Other than the check that the return stack is not empty, there is no
     	// need to validate the pc from 'returns', since we only ever push valid
     	//values onto it via jumpsub.
    -	*pc = callContext.rstack.pop() + 1
    +	*pc = uint64(callContext.rstack.pop()) + 1
     	return nil, nil
     }
     
    @@ -608,7 +608,13 @@ func opCreate(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]
     	stackvalue := size
     
     	callContext.contract.UseGas(gas)
    -	res, addr, returnGas, suberr := interpreter.evm.Create(callContext.contract, input, gas, value.ToBig())
    +	//TODO: use uint256.Int instead of converting with toBig()
    +	var bigVal = big0
    +	if !value.IsZero() {
    +		bigVal = value.ToBig()
    +	}
    +
    +	res, addr, returnGas, suberr := interpreter.evm.Create(callContext.contract, input, gas, bigVal)
     	// Push item on the stack based on the returned error. If the ruleset is
     	// homestead we must check for CodeStoreOutOfGasError (homestead only
     	// rule) and treat as an error, if the ruleset is frontier we must
    @@ -643,8 +649,13 @@ func opCreate2(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([
     	callContext.contract.UseGas(gas)
     	// reuse size int for stackvalue
     	stackvalue := size
    +	//TODO: use uint256.Int instead of converting with toBig()
    +	bigEndowment := big0
    +	if !endowment.IsZero() {
    +		bigEndowment = endowment.ToBig()
    +	}
     	res, addr, returnGas, suberr := interpreter.evm.Create2(callContext.contract, input, gas,
    -		endowment.ToBig(), salt.ToBig())
    +		bigEndowment, &salt)
     	// Push item on the stack based on the returned error.
     	if suberr != nil {
     		stackvalue.Clear()
    @@ -672,10 +683,17 @@ func opCall(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]by
     	// Get the arguments from the memory.
     	args := callContext.memory.GetPtr(int64(inOffset.Uint64()), int64(inSize.Uint64()))
     
    +	var bigVal = big0
    +	//TODO: use uint256.Int instead of converting with toBig()
    +	// By using big0 here, we save an alloc for the most common case (non-ether-transferring contract calls),
    +	// but it would make more sense to extend the usage of uint256.Int
     	if !value.IsZero() {
     		gas += params.CallStipend
    +		bigVal = value.ToBig()
     	}
    -	ret, returnGas, err := interpreter.evm.Call(callContext.contract, toAddr, args, gas, value.ToBig())
    +
    +	ret, returnGas, err := interpreter.evm.Call(callContext.contract, toAddr, args, gas, bigVal)
    +
     	if err != nil {
     		temp.Clear()
     	} else {
    @@ -702,10 +720,14 @@ func opCallCode(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) (
     	// Get arguments from the memory.
     	args := callContext.memory.GetPtr(int64(inOffset.Uint64()), int64(inSize.Uint64()))
     
    +	//TODO: use uint256.Int instead of converting with toBig()
    +	var bigVal = big0
     	if !value.IsZero() {
     		gas += params.CallStipend
    +		bigVal = value.ToBig()
     	}
    -	ret, returnGas, err := interpreter.evm.CallCode(callContext.contract, toAddr, args, gas, value.ToBig())
    +
    +	ret, returnGas, err := interpreter.evm.CallCode(callContext.contract, toAddr, args, gas, bigVal)
     	if err != nil {
     		temp.Clear()
     	} else {
    
  • core/vm/interpreter.go+10 3 modified
    @@ -182,13 +182,20 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
     		logged  bool   // deferred Tracer should ignore already logged steps
     		res     []byte // result of the opcode execution function
     	)
    +	// Don't move this deferrred function, it's placed before the capturestate-deferred method,
    +	// so that it get's executed _after_: the capturestate needs the stacks before
    +	// they are returned to the pools
    +	defer func() {
    +		returnStack(stack)
    +		returnRStack(returns)
    +	}()
     	contract.Input = input
     
     	if in.cfg.Debug {
     		defer func() {
     			if err != nil {
     				if !logged {
    -					in.cfg.Tracer.CaptureState(in.evm, pcCopy, op, gasCopy, cost, mem, stack, returns, contract, in.evm.depth, err)
    +					in.cfg.Tracer.CaptureState(in.evm, pcCopy, op, gasCopy, cost, mem, stack, returns, in.returnData, contract, in.evm.depth, err)
     				} else {
     					in.cfg.Tracer.CaptureFault(in.evm, pcCopy, op, gasCopy, cost, mem, stack, returns, contract, in.evm.depth, err)
     				}
    @@ -272,7 +279,7 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
     		}
     
     		if in.cfg.Debug {
    -			in.cfg.Tracer.CaptureState(in.evm, pc, op, gasCopy, cost, mem, stack, returns, contract, in.evm.depth, err)
    +			in.cfg.Tracer.CaptureState(in.evm, pc, op, gasCopy, cost, mem, stack, returns, in.returnData, contract, in.evm.depth, err)
     			logged = true
     		}
     
    @@ -281,7 +288,7 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
     		// if the operation clears the return data (e.g. it has returning data)
     		// set the last return to the result of the operation.
     		if operation.returns {
    -			in.returnData = res
    +			in.returnData = common.CopyBytes(res)
     		}
     
     		switch {
    
  • core/vm/logger.go+23 12 modified
    @@ -47,11 +47,12 @@ func (s Storage) Copy() Storage {
     
     // LogConfig are the configuration options for structured logger the EVM
     type LogConfig struct {
    -	DisableMemory  bool // disable memory capture
    -	DisableStack   bool // disable stack capture
    -	DisableStorage bool // disable storage capture
    -	Debug          bool // print output during capture end
    -	Limit          int  // maximum length of output, but zero means unlimited
    +	DisableMemory     bool // disable memory capture
    +	DisableStack      bool // disable stack capture
    +	DisableStorage    bool // disable storage capture
    +	DisableReturnData bool // disable return data capture
    +	Debug             bool // print output during capture end
    +	Limit             int  // maximum length of output, but zero means unlimited
     }
     
     //go:generate gencodec -type StructLog -field-override structLogMarshaling -out gen_structlog.go
    @@ -66,7 +67,8 @@ type StructLog struct {
     	Memory        []byte                      `json:"memory"`
     	MemorySize    int                         `json:"memSize"`
     	Stack         []*big.Int                  `json:"stack"`
    -	ReturnStack   []uint64                    `json:"returnStack"`
    +	ReturnStack   []uint32                    `json:"returnStack"`
    +	ReturnData    []byte                      `json:"returnData"`
     	Storage       map[common.Hash]common.Hash `json:"-"`
     	Depth         int                         `json:"depth"`
     	RefundCounter uint64                      `json:"refund"`
    @@ -104,7 +106,7 @@ func (s *StructLog) ErrorString() string {
     // if you need to retain them beyond the current call.
     type Tracer interface {
     	CaptureStart(from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) error
    -	CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *Stack, rStack *ReturnStack, contract *Contract, depth int, err error) error
    +	CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *Stack, rStack *ReturnStack, rData []byte, contract *Contract, depth int, err error) error
     	CaptureFault(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *Stack, rStack *ReturnStack, contract *Contract, depth int, err error) error
     	CaptureEnd(output []byte, gasUsed uint64, t time.Duration, err error) error
     }
    @@ -142,7 +144,7 @@ func (l *StructLogger) CaptureStart(from common.Address, to common.Address, crea
     // CaptureState logs a new structured log message and pushes it out to the environment
     //
     // CaptureState also tracks SLOAD/SSTORE ops to track storage change.
    -func (l *StructLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *Stack, rStack *ReturnStack, contract *Contract, depth int, err error) error {
    +func (l *StructLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *Stack, rStack *ReturnStack, rData []byte, contract *Contract, depth int, err error) error {
     	// check if already accumulated the specified number of logs
     	if l.cfg.Limit != 0 && l.cfg.Limit <= len(l.logs) {
     		return errTraceLimitReached
    @@ -161,9 +163,9 @@ func (l *StructLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost ui
     			stck[i] = new(big.Int).Set(item.ToBig())
     		}
     	}
    -	var rstack []uint64
    +	var rstack []uint32
     	if !l.cfg.DisableStack && rStack != nil {
    -		rstck := make([]uint64, len(rStack.data))
    +		rstck := make([]uint32, len(rStack.data))
     		copy(rstck, rStack.data)
     	}
     	// Copy a snapshot of the current storage to a new container
    @@ -192,8 +194,13 @@ func (l *StructLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost ui
     		}
     		storage = l.storage[contract.Address()].Copy()
     	}
    +	var rdata []byte
    +	if !l.cfg.DisableReturnData {
    +		rdata = make([]byte, len(rData))
    +		copy(rdata, rData)
    +	}
     	// create a new snapshot of the EVM.
    -	log := StructLog{pc, op, gas, cost, mem, memory.Len(), stck, rstack, storage, depth, env.StateDB.GetRefund(), err}
    +	log := StructLog{pc, op, gas, cost, mem, memory.Len(), stck, rstack, rdata, storage, depth, env.StateDB.GetRefund(), err}
     	l.logs = append(l.logs, log)
     	return nil
     }
    @@ -257,6 +264,10 @@ func WriteTrace(writer io.Writer, logs []StructLog) {
     				fmt.Fprintf(writer, "%x: %x\n", h, item)
     			}
     		}
    +		if len(log.ReturnData) > 0 {
    +			fmt.Fprintln(writer, "ReturnData:")
    +			fmt.Fprint(writer, hex.Dump(log.ReturnData))
    +		}
     		fmt.Fprintln(writer)
     	}
     }
    @@ -308,7 +319,7 @@ func (t *mdLogger) CaptureStart(from common.Address, to common.Address, create b
     	return nil
     }
     
    -func (t *mdLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *Stack, rStack *ReturnStack, contract *Contract, depth int, err error) error {
    +func (t *mdLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *Stack, rStack *ReturnStack, rData []byte, contract *Contract, depth int, err error) error {
     	fmt.Fprintf(t.out, "| %4d  | %10v  |  %3d |", pc, op, cost)
     
     	if !t.cfg.DisableStack { // format stack
    
  • core/vm/logger_json.go+4 1 modified
    @@ -46,7 +46,7 @@ func (l *JSONLogger) CaptureStart(from common.Address, to common.Address, create
     }
     
     // CaptureState outputs state information on the logger.
    -func (l *JSONLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *Stack, rStack *ReturnStack, contract *Contract, depth int, err error) error {
    +func (l *JSONLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *Stack, rStack *ReturnStack, rData []byte, contract *Contract, depth int, err error) error {
     	log := StructLog{
     		Pc:            pc,
     		Op:            op,
    @@ -70,6 +70,9 @@ func (l *JSONLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint
     		log.Stack = logstack
     		log.ReturnStack = rStack.data
     	}
    +	if !l.cfg.DisableReturnData {
    +		log.ReturnData = rData
    +	}
     	return l.encoder.Encode(log)
     }
     
    
  • core/vm/logger_test.go+1 1 modified
    @@ -61,7 +61,7 @@ func TestStoreCapture(t *testing.T) {
     	stack.push(uint256.NewInt().SetUint64(1))
     	stack.push(uint256.NewInt())
     	var index common.Hash
    -	logger.CaptureState(env, 0, SSTORE, 0, 0, mem, stack, rstack, contract, 0, nil)
    +	logger.CaptureState(env, 0, SSTORE, 0, 0, mem, stack, rstack, nil, contract, 0, nil)
     	if len(logger.storage[contract.Address()]) == 0 {
     		t.Fatalf("expected exactly 1 changed value on address %x, got %d", contract.Address(), len(logger.storage[contract.Address()]))
     	}
    
  • core/vm/runtime/runtime.go+14 7 modified
    @@ -52,13 +52,20 @@ type Config struct {
     func setDefaults(cfg *Config) {
     	if cfg.ChainConfig == nil {
     		cfg.ChainConfig = &params.ChainConfig{
    -			ChainID:        big.NewInt(1),
    -			HomesteadBlock: new(big.Int),
    -			DAOForkBlock:   new(big.Int),
    -			DAOForkSupport: false,
    -			EIP150Block:    new(big.Int),
    -			EIP155Block:    new(big.Int),
    -			EIP158Block:    new(big.Int),
    +			ChainID:             big.NewInt(1),
    +			HomesteadBlock:      new(big.Int),
    +			DAOForkBlock:        new(big.Int),
    +			DAOForkSupport:      false,
    +			EIP150Block:         new(big.Int),
    +			EIP150Hash:          common.Hash{},
    +			EIP155Block:         new(big.Int),
    +			EIP158Block:         new(big.Int),
    +			ByzantiumBlock:      new(big.Int),
    +			ConstantinopleBlock: new(big.Int),
    +			PetersburgBlock:     new(big.Int),
    +			IstanbulBlock:       new(big.Int),
    +			MuirGlacierBlock:    new(big.Int),
    +			YoloV1Block:         nil,
     		}
     	}
     
    
  • core/vm/runtime/runtime_test.go+158 29 modified
    @@ -321,34 +321,6 @@ func TestBlockhash(t *testing.T) {
     	}
     }
     
    -// BenchmarkSimpleLoop test a pretty simple loop which loops
    -// 1M (1 048 575) times.
    -// Takes about 200 ms
    -func BenchmarkSimpleLoop(b *testing.B) {
    -	// 0xfffff = 1048575 loops
    -	code := []byte{
    -		byte(vm.PUSH3), 0x0f, 0xff, 0xff,
    -		byte(vm.JUMPDEST), //  [ count ]
    -		byte(vm.PUSH1), 1, // [count, 1]
    -		byte(vm.SWAP1),    // [1, count]
    -		byte(vm.SUB),      // [ count -1 ]
    -		byte(vm.DUP1),     //  [ count -1 , count-1]
    -		byte(vm.PUSH1), 4, // [count-1, count -1, label]
    -		byte(vm.JUMPI), // [ 0 ]
    -		byte(vm.STOP),
    -	}
    -	//tracer := vm.NewJSONLogger(nil, os.Stdout)
    -	//Execute(code, nil, &Config{
    -	//	EVMConfig: vm.Config{
    -	//		Debug:  true,
    -	//		Tracer: tracer,
    -	//	}})
    -
    -	for i := 0; i < b.N; i++ {
    -		Execute(code, nil, nil)
    -	}
    -}
    -
     type stepCounter struct {
     	inner *vm.JSONLogger
     	steps int
    @@ -358,7 +330,7 @@ func (s *stepCounter) CaptureStart(from common.Address, to common.Address, creat
     	return nil
     }
     
    -func (s *stepCounter) CaptureState(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost uint64, memory *vm.Memory, stack *vm.Stack, rStack *vm.ReturnStack, contract *vm.Contract, depth int, err error) error {
    +func (s *stepCounter) CaptureState(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost uint64, memory *vm.Memory, stack *vm.Stack, rStack *vm.ReturnStack, rData []byte, contract *vm.Contract, depth int, err error) error {
     	s.steps++
     	// Enable this for more output
     	//s.inner.CaptureState(env, pc, op, gas, cost, memory, stack, rStack, contract, depth, err)
    @@ -593,3 +565,160 @@ func DisabledTestEipExampleCases(t *testing.T) {
     			"allowed, and causes an error", code)
     	}
     }
    +
    +// benchmarkNonModifyingCode benchmarks code, but if the code modifies the
    +// state, this should not be used, since it does not reset the state between runs.
    +func benchmarkNonModifyingCode(gas uint64, code []byte, name string, b *testing.B) {
    +	cfg := new(Config)
    +	setDefaults(cfg)
    +	cfg.State, _ = state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()), nil)
    +	cfg.GasLimit = gas
    +	var (
    +		destination = common.BytesToAddress([]byte("contract"))
    +		vmenv       = NewEnv(cfg)
    +		sender      = vm.AccountRef(cfg.Origin)
    +	)
    +	cfg.State.CreateAccount(destination)
    +	eoa := common.HexToAddress("E0")
    +	{
    +		cfg.State.CreateAccount(eoa)
    +		cfg.State.SetNonce(eoa, 100)
    +	}
    +	reverting := common.HexToAddress("EE")
    +	{
    +		cfg.State.CreateAccount(reverting)
    +		cfg.State.SetCode(reverting, []byte{
    +			byte(vm.PUSH1), 0x00,
    +			byte(vm.PUSH1), 0x00,
    +			byte(vm.REVERT),
    +		})
    +	}
    +
    +	//cfg.State.CreateAccount(cfg.Origin)
    +	// set the receiver's (the executing contract) code for execution.
    +	cfg.State.SetCode(destination, code)
    +	vmenv.Call(sender, destination, nil, gas, cfg.Value)
    +
    +	b.Run(name, func(b *testing.B) {
    +		b.ReportAllocs()
    +		for i := 0; i < b.N; i++ {
    +			vmenv.Call(sender, destination, nil, gas, cfg.Value)
    +		}
    +	})
    +}
    +
    +// BenchmarkSimpleLoop test a pretty simple loop which loops until OOG
    +// 55 ms
    +func BenchmarkSimpleLoop(b *testing.B) {
    +
    +	staticCallIdentity := []byte{
    +		byte(vm.JUMPDEST), //  [ count ]
    +		// push args for the call
    +		byte(vm.PUSH1), 0, // out size
    +		byte(vm.DUP1),       // out offset
    +		byte(vm.DUP1),       // out insize
    +		byte(vm.DUP1),       // in offset
    +		byte(vm.PUSH1), 0x4, // address of identity
    +		byte(vm.GAS), // gas
    +		byte(vm.STATICCALL),
    +		byte(vm.POP),      // pop return value
    +		byte(vm.PUSH1), 0, // jumpdestination
    +		byte(vm.JUMP),
    +	}
    +
    +	callIdentity := []byte{
    +		byte(vm.JUMPDEST), //  [ count ]
    +		// push args for the call
    +		byte(vm.PUSH1), 0, // out size
    +		byte(vm.DUP1),       // out offset
    +		byte(vm.DUP1),       // out insize
    +		byte(vm.DUP1),       // in offset
    +		byte(vm.DUP1),       // value
    +		byte(vm.PUSH1), 0x4, // address of identity
    +		byte(vm.GAS), // gas
    +		byte(vm.CALL),
    +		byte(vm.POP),      // pop return value
    +		byte(vm.PUSH1), 0, // jumpdestination
    +		byte(vm.JUMP),
    +	}
    +
    +	callInexistant := []byte{
    +		byte(vm.JUMPDEST), //  [ count ]
    +		// push args for the call
    +		byte(vm.PUSH1), 0, // out size
    +		byte(vm.DUP1),        // out offset
    +		byte(vm.DUP1),        // out insize
    +		byte(vm.DUP1),        // in offset
    +		byte(vm.DUP1),        // value
    +		byte(vm.PUSH1), 0xff, // address of existing contract
    +		byte(vm.GAS), // gas
    +		byte(vm.CALL),
    +		byte(vm.POP),      // pop return value
    +		byte(vm.PUSH1), 0, // jumpdestination
    +		byte(vm.JUMP),
    +	}
    +
    +	callEOA := []byte{
    +		byte(vm.JUMPDEST), //  [ count ]
    +		// push args for the call
    +		byte(vm.PUSH1), 0, // out size
    +		byte(vm.DUP1),        // out offset
    +		byte(vm.DUP1),        // out insize
    +		byte(vm.DUP1),        // in offset
    +		byte(vm.DUP1),        // value
    +		byte(vm.PUSH1), 0xE0, // address of EOA
    +		byte(vm.GAS), // gas
    +		byte(vm.CALL),
    +		byte(vm.POP),      // pop return value
    +		byte(vm.PUSH1), 0, // jumpdestination
    +		byte(vm.JUMP),
    +	}
    +
    +	loopingCode := []byte{
    +		byte(vm.JUMPDEST), //  [ count ]
    +		// push args for the call
    +		byte(vm.PUSH1), 0, // out size
    +		byte(vm.DUP1),       // out offset
    +		byte(vm.DUP1),       // out insize
    +		byte(vm.DUP1),       // in offset
    +		byte(vm.PUSH1), 0x4, // address of identity
    +		byte(vm.GAS), // gas
    +
    +		byte(vm.POP), byte(vm.POP), byte(vm.POP), byte(vm.POP), byte(vm.POP), byte(vm.POP),
    +		byte(vm.PUSH1), 0, // jumpdestination
    +		byte(vm.JUMP),
    +	}
    +
    +	calllRevertingContractWithInput := []byte{
    +		byte(vm.JUMPDEST), //
    +		// push args for the call
    +		byte(vm.PUSH1), 0, // out size
    +		byte(vm.DUP1),        // out offset
    +		byte(vm.PUSH1), 0x20, // in size
    +		byte(vm.PUSH1), 0x00, // in offset
    +		byte(vm.PUSH1), 0x00, // value
    +		byte(vm.PUSH1), 0xEE, // address of reverting contract
    +		byte(vm.GAS), // gas
    +		byte(vm.CALL),
    +		byte(vm.POP),      // pop return value
    +		byte(vm.PUSH1), 0, // jumpdestination
    +		byte(vm.JUMP),
    +	}
    +
    +	//tracer := vm.NewJSONLogger(nil, os.Stdout)
    +	//Execute(loopingCode, nil, &Config{
    +	//	EVMConfig: vm.Config{
    +	//		Debug:  true,
    +	//		Tracer: tracer,
    +	//	}})
    +	// 100M gas
    +	benchmarkNonModifyingCode(100000000, staticCallIdentity, "staticcall-identity-100M", b)
    +	benchmarkNonModifyingCode(100000000, callIdentity, "call-identity-100M", b)
    +	benchmarkNonModifyingCode(100000000, loopingCode, "loop-100M", b)
    +	benchmarkNonModifyingCode(100000000, callInexistant, "call-nonexist-100M", b)
    +	benchmarkNonModifyingCode(100000000, callEOA, "call-EOA-100M", b)
    +	benchmarkNonModifyingCode(100000000, calllRevertingContractWithInput, "call-reverting-100M", b)
    +
    +	//benchmarkNonModifyingCode(10000000, staticCallIdentity, "staticcall-identity-10M", b)
    +	//benchmarkNonModifyingCode(10000000, loopingCode, "loop-10M", b)
    +}
    
  • core/vm/stack.go+29 5 modified
    @@ -18,10 +18,17 @@ package vm
     
     import (
     	"fmt"
    +	"sync"
     
     	"github.com/holiman/uint256"
     )
     
    +var stackPool = sync.Pool{
    +	New: func() interface{} {
    +		return &Stack{data: make([]uint256.Int, 0, 16)}
    +	},
    +}
    +
     // Stack is an object for basic stack operations. Items popped to the stack are
     // expected to be changed and modified. stack does not take care of adding newly
     // initialised objects.
    @@ -30,7 +37,12 @@ type Stack struct {
     }
     
     func newstack() *Stack {
    -	return &Stack{data: make([]uint256.Int, 0, 16)}
    +	return stackPool.Get().(*Stack)
    +}
    +
    +func returnStack(s *Stack) {
    +	s.data = s.data[:0]
    +	stackPool.Put(s)
     }
     
     // Data returns the underlying uint256.Int array.
    @@ -87,20 +99,32 @@ func (st *Stack) Print() {
     	fmt.Println("#############")
     }
     
    +var rStackPool = sync.Pool{
    +	New: func() interface{} {
    +		return &ReturnStack{data: make([]uint32, 0, 10)}
    +	},
    +}
    +
     // ReturnStack is an object for basic return stack operations.
     type ReturnStack struct {
    -	data []uint64
    +	data []uint32
     }
     
     func newReturnStack() *ReturnStack {
    -	return &ReturnStack{data: make([]uint64, 0, 1024)}
    +	return rStackPool.Get().(*ReturnStack)
    +}
    +
    +func returnRStack(rs *ReturnStack) {
    +	rs.data = rs.data[:0]
    +	rStackPool.Put(rs)
     }
     
    -func (st *ReturnStack) push(d uint64) {
    +func (st *ReturnStack) push(d uint32) {
     	st.data = append(st.data, d)
     }
     
    -func (st *ReturnStack) pop() (ret uint64) {
    +// A uint32 is sufficient as for code below 4.2G
    +func (st *ReturnStack) pop() (ret uint32) {
     	ret = st.data[len(st.data)-1]
     	st.data = st.data[:len(st.data)-1]
     	return
    
  • eth/tracers/tracer.go+1 1 modified
    @@ -541,7 +541,7 @@ func (jst *Tracer) CaptureStart(from common.Address, to common.Address, create b
     }
     
     // CaptureState implements the Tracer interface to trace a single step of VM execution.
    -func (jst *Tracer) CaptureState(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost uint64, memory *vm.Memory, stack *vm.Stack, rStack *vm.ReturnStack, contract *vm.Contract, depth int, err error) error {
    +func (jst *Tracer) CaptureState(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost uint64, memory *vm.Memory, stack *vm.Stack, rStack *vm.ReturnStack, rdata []byte, contract *vm.Contract, depth int, err error) error {
     	if jst.err == nil {
     		// Initialize the context if it wasn't done yet
     		if !jst.inited {
    
  • eth/tracers/tracer_test.go+2 2 modified
    @@ -169,10 +169,10 @@ func TestHaltBetweenSteps(t *testing.T) {
     	env := vm.NewEVM(vm.Context{BlockNumber: big.NewInt(1)}, &dummyStatedb{}, params.TestChainConfig, vm.Config{Debug: true, Tracer: tracer})
     	contract := vm.NewContract(&account{}, &account{}, big.NewInt(0), 0)
     
    -	tracer.CaptureState(env, 0, 0, 0, 0, nil, nil, nil, contract, 0, nil)
    +	tracer.CaptureState(env, 0, 0, 0, 0, nil, nil, nil, nil, contract, 0, nil)
     	timeout := errors.New("stahp")
     	tracer.Stop(timeout)
    -	tracer.CaptureState(env, 0, 0, 0, 0, nil, nil, nil, contract, 0, nil)
    +	tracer.CaptureState(env, 0, 0, 0, 0, nil, nil, nil, nil, contract, 0, nil)
     
     	if _, err := tracer.GetResult(); err.Error() != timeout.Error() {
     		t.Errorf("Expected timeout error, got %v", err)
    
  • trie/secure_trie.go+2 2 modified
    @@ -179,9 +179,9 @@ func (t *SecureTrie) hashKey(key []byte) []byte {
     	h := newHasher(false)
     	h.sha.Reset()
     	h.sha.Write(key)
    -	buf := h.sha.Sum(t.hashKeyBuf[:0])
    +	h.sha.Read(t.hashKeyBuf[:])
     	returnHasherToPool(h)
    -	return buf
    +	return t.hashKeyBuf[:]
     }
     
     // getSecKeyCache returns the current secure key cache, creating a new one if
    

Vulnerability mechanics

Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

6

News mentions

0

No linked articles in our index yet.