core/vm: use 64-bit memory calculations

This commit is contained in:
Martin Holst Swende 2019-03-05 09:18:40 +01:00
parent 6a4bf5d697
commit e17df3ebce
No known key found for this signature in database
GPG key ID: 683B438C05A5DDF0
4 changed files with 107 additions and 58 deletions

View file

@ -23,13 +23,47 @@ import (
"github.com/ethereum/go-ethereum/common/math" "github.com/ethereum/go-ethereum/common/math"
) )
// calculates the memory size required for a step // calcMemSize64 calculates the required memory size, and returns
func calcMemSize(off, l *big.Int) *big.Int { // the size and whether the result overflowed uint64
func calcMemSize64(off, l *big.Int) (uint64, bool) {
// if length is zero, memsize is always zero, regardless of offset
if l.Sign() == 0 { if l.Sign() == 0 {
return common.Big0 return 0, true
}
// Check that neither offset nor length overflows
if off.BitLen() > 64 || l.BitLen() > 64 {
return 0, true
} }
return new(big.Int).Add(off, l) offset64 := off.Uint64()
length64 := l.Uint64()
val := offset64 + length64
// Check that the total doesn't overflow
if val < offset64 {
return 0, true
}
return val, false
}
// calcMemSize64WithUint calculates the required memory size, and returns
// the size and whether the result overflowed uint64
// Identical to calcMemSize64, but length is a uint64
func calcMemSize64WithUint(off *big.Int, length64 uint64) (uint64, bool) {
// if length is zero, memsize is always zero, regardless of offset
if length64 == 0 {
return 0, true
}
// Check that offset doesn't overflow
if off.BitLen() > 64 {
return 0, true
}
offset64 := off.Uint64()
val := offset64 + length64
// Check that the total doesn't overflow
if val < offset64 {
return 0, true
}
return val, false
} }
// getData returns a slice from the data based on the start and size and pads // getData returns a slice from the data based on the start and size and pads

View file

@ -229,7 +229,7 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
// calculate the new memory size and expand the memory to fit // calculate the new memory size and expand the memory to fit
// the operation // the operation
if operation.memorySize != nil { if operation.memorySize != nil {
memSize, overflow := bigUint64(operation.memorySize(stack)) memSize, overflow := operation.memorySize(stack)
if overflow { if overflow {
return nil, errGasUintOverflow return nil, errGasUintOverflow
} }

View file

@ -18,7 +18,6 @@ package vm
import ( import (
"errors" "errors"
"math/big"
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
) )
@ -26,8 +25,8 @@ import (
type ( type (
executionFunc func(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) executionFunc func(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error)
gasFunc func(params.GasTable, *EVM, *Contract, *Stack, *Memory, uint64) (uint64, error) // last parameter is the requested memory size as a uint64 gasFunc func(params.GasTable, *EVM, *Contract, *Stack, *Memory, uint64) (uint64, error) // last parameter is the requested memory size as a uint64
//stackValidationFunc func(*Stack) error // memorySizeFunc returns the required size, and whether the operation overflowed a uint64
memorySizeFunc func(*Stack) *big.Int memorySizeFunc func(*Stack) (size uint64, overflow bool)
) )
var errGasUintOverflow = errors.New("gas uint64 overflow") var errGasUintOverflow = errors.New("gas uint64 overflow")

View file

@ -16,82 +16,98 @@
package vm package vm
import ( func memorySha3(stack *Stack) (uint64, bool) {
"math/big" return calcMemSize64(stack.Back(0), stack.Back(1))
"github.com/ethereum/go-ethereum/common/math"
)
func memorySha3(stack *Stack) *big.Int {
return calcMemSize(stack.Back(0), stack.Back(1))
} }
func memoryCallDataCopy(stack *Stack) *big.Int { func memoryCallDataCopy(stack *Stack) (uint64, bool) {
return calcMemSize(stack.Back(0), stack.Back(2)) return calcMemSize64(stack.Back(0), stack.Back(2))
} }
func memoryReturnDataCopy(stack *Stack) *big.Int { func memoryReturnDataCopy(stack *Stack) (uint64, bool) {
return calcMemSize(stack.Back(0), stack.Back(2)) return calcMemSize64(stack.Back(0), stack.Back(2))
} }
func memoryCodeCopy(stack *Stack) *big.Int { func memoryCodeCopy(stack *Stack) (uint64, bool) {
return calcMemSize(stack.Back(0), stack.Back(2)) return calcMemSize64(stack.Back(0), stack.Back(2))
} }
func memoryExtCodeCopy(stack *Stack) *big.Int { func memoryExtCodeCopy(stack *Stack) (uint64, bool) {
return calcMemSize(stack.Back(1), stack.Back(3)) return calcMemSize64(stack.Back(1), stack.Back(3))
} }
func memoryMLoad(stack *Stack) *big.Int { func memoryMLoad(stack *Stack) (uint64, bool) {
return calcMemSize(stack.Back(0), big.NewInt(32)) return calcMemSize64WithUint(stack.Back(0), 32)
} }
func memoryMStore8(stack *Stack) *big.Int { func memoryMStore8(stack *Stack) (uint64, bool) {
return calcMemSize(stack.Back(0), big.NewInt(1)) return calcMemSize64WithUint(stack.Back(0), 1)
} }
func memoryMStore(stack *Stack) *big.Int { func memoryMStore(stack *Stack) (uint64, bool) {
return calcMemSize(stack.Back(0), big.NewInt(32)) return calcMemSize64WithUint(stack.Back(0), 32)
} }
func memoryCreate(stack *Stack) *big.Int { func memoryCreate(stack *Stack) (uint64, bool) {
return calcMemSize(stack.Back(1), stack.Back(2)) return calcMemSize64(stack.Back(1), stack.Back(2))
} }
func memoryCreate2(stack *Stack) *big.Int { func memoryCreate2(stack *Stack) (uint64, bool) {
return calcMemSize(stack.Back(1), stack.Back(2)) return calcMemSize64(stack.Back(1), stack.Back(2))
} }
func memoryCall(stack *Stack) *big.Int { func memoryCall(stack *Stack) (uint64, bool) {
x := calcMemSize(stack.Back(5), stack.Back(6)) x, overflow := calcMemSize64(stack.Back(5), stack.Back(6))
y := calcMemSize(stack.Back(3), stack.Back(4)) if overflow {
return 0, true
return math.BigMax(x, y) }
y, overflow := calcMemSize64(stack.Back(3), stack.Back(4))
if overflow {
return 0, true
}
if x > y {
return x, false
}
return y, false
}
func memoryDelegateCall(stack *Stack) (uint64, bool) {
x, overflow := calcMemSize64(stack.Back(4), stack.Back(5))
if overflow {
return 0, true
}
y, overflow := calcMemSize64(stack.Back(2), stack.Back(3))
if overflow {
return 0, true
}
if x > y {
return x, false
}
return y, false
} }
func memoryDelegateCall(stack *Stack) *big.Int { func memoryStaticCall(stack *Stack) (uint64, bool) {
x := calcMemSize(stack.Back(4), stack.Back(5)) x, overflow := calcMemSize64(stack.Back(4), stack.Back(5))
y := calcMemSize(stack.Back(2), stack.Back(3)) if overflow {
return 0, true
return math.BigMax(x, y) }
y, overflow := calcMemSize64(stack.Back(2), stack.Back(3))
if overflow {
return 0, true
}
if x > y {
return x, false
}
return y, false
} }
func memoryStaticCall(stack *Stack) *big.Int { func memoryReturn(stack *Stack) (uint64, bool) {
x := calcMemSize(stack.Back(4), stack.Back(5)) return calcMemSize64(stack.Back(0), stack.Back(1))
y := calcMemSize(stack.Back(2), stack.Back(3))
return math.BigMax(x, y)
} }
func memoryReturn(stack *Stack) *big.Int { func memoryRevert(stack *Stack) (uint64, bool) {
return calcMemSize(stack.Back(0), stack.Back(1)) return calcMemSize64(stack.Back(0), stack.Back(1))
} }
func memoryRevert(stack *Stack) *big.Int { func memoryLog(stack *Stack) (uint64, bool) {
return calcMemSize(stack.Back(0), stack.Back(1)) return calcMemSize64(stack.Back(0), stack.Back(1))
}
func memoryLog(stack *Stack) *big.Int {
mSize, mStart := stack.Back(1), stack.Back(0)
return calcMemSize(mStart, mSize)
} }