core/vm: fix opReturnDataLoad

This commit is contained in:
Marius van der Wijden 2024-06-25 14:53:25 +02:00
parent fa6b1b9fa1
commit a1b359f310
6 changed files with 77 additions and 9 deletions

View file

@ -725,10 +725,10 @@ func enableEOF(jt *JumpTable) {
jt[EXTCALL] = &operation{ jt[EXTCALL] = &operation{
execute: opExtCall, execute: opExtCall,
constantGas: params.CallGasEIP150, constantGas: params.CallGasEIP150,
dynamicGas: gasCall, dynamicGas: gasCallExt,
minStack: minStack(4, 1), minStack: minStack(4, 1),
maxStack: maxStack(4, 1), maxStack: maxStack(4, 1),
memorySize: memoryCall, memorySize: memoryExtCall,
} }
jt[EXTDELEGATECALL] = &operation{ jt[EXTDELEGATECALL] = &operation{
execute: opExtDelegateCall, execute: opExtDelegateCall,
@ -1073,17 +1073,18 @@ func opReturnDataLoad(pc *uint64, interpreter *EVMInterpreter, scope *ScopeConte
var ( var (
offset = scope.Stack.pop() offset = scope.Stack.pop()
) )
if offset.Uint64()+32 > uint64(len(interpreter.returnData)) { offset64, overflow := offset.Uint64WithOverflow()
return nil, errors.New("return buffer overflow") if overflow {
offset64 = math.MaxUint64
} }
scope.Stack.push(offset.SetBytes(interpreter.returnData[offset.Uint64() : offset.Uint64()+32])) scope.Stack.push(offset.SetBytes(getData(interpreter.returnData, offset64, 32)))
return nil, nil return nil, nil
} }
func opExtCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { func opExtCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
stack := scope.Stack stack := scope.Stack
// Use all available gas // Use all available gas
gas := (scope.Contract.Gas / 64) * 63 gas := interpreter.evm.callGasTemp
// Pop other call parameters. // Pop other call parameters.
addr, value, inOffset, inSize := stack.pop(), stack.pop(), stack.pop(), stack.pop() addr, value, inOffset, inSize := stack.pop(), stack.pop(), stack.pop(), stack.pop()
toAddr := common.Address(addr.Bytes20()) toAddr := common.Address(addr.Bytes20())
@ -1106,7 +1107,7 @@ func opExtCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]
temp.SetOne() temp.SetOne()
} }
stack.push(&temp) stack.push(&temp)
fmt.Println(returnGas)
scope.Contract.RefundGas(returnGas, interpreter.evm.Config.Tracer, tracing.GasChangeCallLeftOverRefunded) scope.Contract.RefundGas(returnGas, interpreter.evm.Config.Tracer, tracing.GasChangeCallLeftOverRefunded)
interpreter.returnData = ret interpreter.returnData = ret

View file

@ -689,7 +689,7 @@ func (evm *EVM) GetVMContext() *tracing.VMContext {
// parseContainer tries to parse an EOF container if the Shanghai fork is active. It expects the code to already be validated. // parseContainer tries to parse an EOF container if the Shanghai fork is active. It expects the code to already be validated.
func (evm *EVM) parseContainer(b []byte) *Container { func (evm *EVM) parseContainer(b []byte) *Container {
if evm.chainRules.IsShanghai { if evm.chainRules.IsPrague {
var c Container var c Container
if err := c.UnmarshalBinary(b); err != nil && strings.HasPrefix(err.Error(), "invalid magic") { if err := c.UnmarshalBinary(b); err != nil && strings.HasPrefix(err.Error(), "invalid magic") {
return nil return nil

View file

@ -43,7 +43,7 @@ func callGas(isEip150 bool, availableGas, base uint64, callCost *uint256.Int) (u
// If the bit length exceeds 64 bit we know that the newly calculated "gas" for EIP150 // If the bit length exceeds 64 bit we know that the newly calculated "gas" for EIP150
// is smaller than the requested amount. Therefore we return the new gas instead // is smaller than the requested amount. Therefore we return the new gas instead
// of returning an error. // of returning an error.
if !callCost.IsUint64() || gas < callCost.Uint64() { if !callCost.IsUint64() || gas <= callCost.Uint64() {
return gas, nil return gas, nil
} }
} }

View file

@ -23,6 +23,7 @@ import (
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/math" "github.com/ethereum/go-ethereum/common/math"
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
"github.com/holiman/uint256"
) )
// memoryGasCost calculates the quadratic gas for memory expansion. It does so // memoryGasCost calculates the quadratic gas for memory expansion. It does so
@ -413,6 +414,51 @@ func gasCall(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize
return gas, nil return gas, nil
} }
func gasCallExt(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
var (
gas uint64
transfersValue = !stack.Back(2).IsZero()
address = common.Address(stack.Back(1).Bytes20())
)
if evm.chainRules.IsEIP158 {
if transfersValue && evm.StateDB.Empty(address) {
gas += params.CallNewAccountGas
}
} else if !evm.StateDB.Exist(address) {
gas += params.CallNewAccountGas
}
if transfersValue && !evm.chainRules.IsEIP4762 {
gas += params.CallValueTransferGas
}
memoryGas, err := memoryGasCost(mem, memorySize)
if err != nil {
return 0, err
}
var overflow bool
if gas, overflow = math.SafeAdd(gas, memoryGas); overflow {
return 0, ErrGasUintOverflow
}
if evm.chainRules.IsEIP4762 {
if transfersValue {
gas, overflow = math.SafeAdd(gas, evm.AccessEvents.ValueTransferGas(contract.Address(), address))
if overflow {
return 0, ErrGasUintOverflow
}
}
}
evm.callGasTemp, err = callGas(true, contract.Gas, gas, new(uint256.Int).SetUint64(contract.Gas))
if err != nil {
return 0, err
}
if gas, overflow = math.SafeAdd(gas, evm.callGasTemp); overflow {
return 0, ErrGasUintOverflow
}
return gas, nil
}
func gasCallCode(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { func gasCallCode(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
memoryGas, err := memoryGasCost(mem, memorySize) memoryGas, err := memoryGasCost(mem, memorySize)
if err != nil { if err != nil {

View file

@ -323,6 +323,18 @@ func opReturnDataCopy(pc *uint64, interpreter *EVMInterpreter, scope *ScopeConte
length = scope.Stack.pop() length = scope.Stack.pop()
) )
if scope.Contract.IsEOF() {
dataOffset64, overflow := dataOffset.Uint64WithOverflow()
if overflow {
dataOffset64 = math.MaxUint64
}
// These values are checked for overflow during gas cost calculation
memOffset64 := memOffset.Uint64()
length64 := length.Uint64()
scope.Memory.Set(memOffset64, length64, getData(interpreter.returnData, dataOffset64, length64))
return nil, nil
}
offset64, overflow := dataOffset.Uint64WithOverflow() offset64, overflow := dataOffset.Uint64WithOverflow()
if overflow { if overflow {
return nil, ErrReturnDataOutOfBounds return nil, ErrReturnDataOutOfBounds

View file

@ -86,6 +86,7 @@ func memoryCall(stack *Stack) (uint64, bool) {
} }
return y, false return y, false
} }
func memoryDelegateCall(stack *Stack) (uint64, bool) { func memoryDelegateCall(stack *Stack) (uint64, bool) {
x, overflow := calcMemSize64(stack.Back(4), stack.Back(5)) x, overflow := calcMemSize64(stack.Back(4), stack.Back(5))
if overflow { if overflow {
@ -116,6 +117,14 @@ func memoryStaticCall(stack *Stack) (uint64, bool) {
return y, false return y, false
} }
func memoryExtCall(stack *Stack) (uint64, bool) {
x, overflow := calcMemSize64(stack.Back(2), stack.Back(3))
if overflow {
return 0, true
}
return x, false
}
func memoryReturn(stack *Stack) (uint64, bool) { func memoryReturn(stack *Stack) (uint64, bool) {
return calcMemSize64(stack.Back(0), stack.Back(1)) return calcMemSize64(stack.Back(0), stack.Back(1))
} }