Commit 64649169 authored by OptimismBot's avatar OptimismBot Committed by GitHub

Merge pull request #6284 from ethereum-optimism/inphi/cannon-vmfuzz

cannon: Differential Fuzzing syscalls
parents 51fea1ff bc24831c
......@@ -861,6 +861,18 @@ jobs:
command: make fuzz
working_directory: op-chain-ops
fuzz-cannon:
docker:
- image: us-docker.pkg.dev/oplabs-tools-artifacts/images/ci-builder:latest
steps:
- checkout
- check-changed:
patterns: cannon,packages/contracts-bedrock/contracts/cannon
- run:
name: Fuzz
command: make fuzz
working_directory: cannon
depcheck:
docker:
- image: us-docker.pkg.dev/oplabs-tools-artifacts/images/ci-builder:latest
......@@ -1415,6 +1427,7 @@ workflows:
- go-mod-tidy
- fuzz-op-node
- fuzz-op-chain-ops
- fuzz-cannon
- bedrock-markdown
- go-lint:
name: op-batcher-lint
......
......@@ -23,8 +23,20 @@ test: elf
lint:
golangci-lint run -E goimports,sqlclosecheck,bodyclose,asciicheck,misspell,errorlint --timeout 5m -e "errors.As" -e "errors.Is"
fuzz:
go test -run NOTAREALTEST -v -fuzztime 10s -fuzz=FuzzStateSyscallBrk ./mipsevm
go test -run NOTAREALTEST -v -fuzztime 10s -fuzz=FuzzStateSyscallClone ./mipsevm
go test -run NOTAREALTEST -v -fuzztime 10s -fuzz=FuzzStateSyscallMmap ./mipsevm
go test -run NOTAREALTEST -v -fuzztime 10s -fuzz=FuzzStateSyscallExitGroup ./mipsevm
go test -run NOTAREALTEST -v -fuzztime 10s -fuzz=FuzzStateSyscallFnctl ./mipsevm
go test -run NOTAREALTEST -v -fuzztime 10s -fuzz=FuzzStateHintRead ./mipsevm
go test -run NOTAREALTEST -v -fuzztime 20s -fuzz=FuzzStatePreimageRead ./mipsevm
go test -run NOTAREALTEST -v -fuzztime 10s -fuzz=FuzzStateHintWrite ./mipsevm
go test -run NOTAREALTEST -v -fuzztime 20s -fuzz=FuzzStatePreimageWrite ./mipsevm
.PHONY: \
cannon \
clean \
test \
lint
lint \
fuzz
......@@ -13,6 +13,7 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/eth/tracers/logger"
......@@ -21,7 +22,7 @@ import (
"github.com/ethereum-optimism/optimism/op-chain-ops/srcmap"
)
func testContractsSetup(t *testing.T) (*Contracts, *Addresses) {
func testContractsSetup(t require.TestingT) (*Contracts, *Addresses) {
contracts, err := LoadContracts()
require.NoError(t, err)
......@@ -48,6 +49,53 @@ func MarkdownTracer() vm.EVMLogger {
return logger.NewMarkdownLogger(&logger.Config{}, os.Stdout)
}
type MIPSEVM struct {
env *vm.EVM
evmState *state.StateDB
addrs *Addresses
}
func NewMIPSEVM(contracts *Contracts, addrs *Addresses) *MIPSEVM {
env, evmState := NewEVMEnv(contracts, addrs)
return &MIPSEVM{env, evmState, addrs}
}
func (m *MIPSEVM) SetTracer(tracer vm.EVMLogger) {
m.env.Config.Tracer = tracer
}
// Step is a pure function that computes the poststate from the VM state encoded in the StepWitness.
func (m *MIPSEVM) Step(t *testing.T, stepWitness *StepWitness) []byte {
sender := common.Address{0x13, 0x37}
startingGas := uint64(30_000_000)
// we take a snapshot so we can clean up the state, and isolate the logs of this instruction run.
snap := m.env.StateDB.Snapshot()
if stepWitness.HasPreimage() {
t.Logf("reading preimage key %x at offset %d", stepWitness.PreimageKey, stepWitness.PreimageOffset)
poInput, err := stepWitness.EncodePreimageOracleInput()
require.NoError(t, err, "encode preimage oracle input")
_, leftOverGas, err := m.env.Call(vm.AccountRef(m.addrs.Sender), m.addrs.Oracle, poInput, startingGas, big.NewInt(0))
require.NoErrorf(t, err, "evm should not fail, took %d gas", startingGas-leftOverGas)
}
input := stepWitness.EncodeStepInput()
ret, leftOverGas, err := m.env.Call(vm.AccountRef(sender), m.addrs.MIPS, input, startingGas, big.NewInt(0))
require.NoError(t, err, "evm should not fail")
require.Len(t, ret, 32, "expecting 32-byte state hash")
// remember state hash, to check it against state
postHash := common.Hash(*(*[32]byte)(ret))
logs := m.evmState.Logs()
require.Equal(t, 1, len(logs), "expecting a log with post-state")
evmPost := logs[0].Data
require.Equal(t, crypto.Keccak256Hash(evmPost), postHash, "logged state must be accurate")
m.env.StateDB.RevertToSnapshot(snap)
t.Logf("EVM step took %d gas, and returned stateHash %s", startingGas-leftOverGas, postHash)
return evmPost
}
func TestEVM(t *testing.T) {
testFiles, err := os.ReadDir("open_mips_tests/test/bin")
require.NoError(t, err)
......@@ -55,7 +103,6 @@ func TestEVM(t *testing.T) {
contracts, addrs := testContractsSetup(t)
var tracer vm.EVMLogger // no-tracer by default, but see SourceMapTracer and MarkdownTracer
//tracer = SourceMapTracer(t, contracts, addrs)
sender := common.Address{0x13, 0x37}
for _, f := range testFiles {
t.Run(f.Name(), func(t *testing.T) {
......@@ -66,8 +113,8 @@ func TestEVM(t *testing.T) {
// Short-circuit early for exit_group.bin
exitGroup := f.Name() == "exit_group.bin"
env, evmState := NewEVMEnv(contracts, addrs)
env.Config.Tracer = tracer
evm := NewMIPSEVM(contracts, addrs)
evm.SetTracer(tracer)
fn := path.Join("open_mips_tests/test/bin", f.Name())
programMem, err := os.ReadFile(fn)
......@@ -79,58 +126,31 @@ func TestEVM(t *testing.T) {
// set the return address ($ra) to jump into when test completes
state.Registers[31] = endAddr
us := NewInstrumentedState(state, oracle, os.Stdout, os.Stderr)
goState := NewInstrumentedState(state, oracle, os.Stdout, os.Stderr)
for i := 0; i < 1000; i++ {
if us.state.PC == endAddr {
if goState.state.PC == endAddr {
break
}
if exitGroup && us.state.Exited {
if exitGroup && goState.state.Exited {
break
}
insn := state.Memory.GetMemory(state.PC)
t.Logf("step: %4d pc: 0x%08x insn: 0x%08x", state.Step, state.PC, insn)
stepWitness, err := us.Step(true)
stepWitness, err := goState.Step(true)
require.NoError(t, err)
input := stepWitness.EncodeStepInput()
startingGas := uint64(30_000_000)
// we take a snapshot so we can clean up the state, and isolate the logs of this instruction run.
snap := env.StateDB.Snapshot()
// prepare pre-image oracle data, if any
if stepWitness.HasPreimage() {
t.Logf("reading preimage key %x at offset %d", stepWitness.PreimageKey, stepWitness.PreimageOffset)
poInput, err := stepWitness.EncodePreimageOracleInput()
require.NoError(t, err, "encode preimage oracle input")
_, leftOverGas, err := env.Call(vm.AccountRef(addrs.Sender), addrs.Oracle, poInput, startingGas, big.NewInt(0))
require.NoErrorf(t, err, "evm should not fail, took %d gas", startingGas-leftOverGas)
}
ret, leftOverGas, err := env.Call(vm.AccountRef(sender), addrs.MIPS, input, startingGas, big.NewInt(0))
require.NoError(t, err, "evm should not fail")
require.Len(t, ret, 32, "expecting 32-byte state hash")
// remember state hash, to check it against state
postHash := common.Hash(*(*[32]byte)(ret))
logs := evmState.Logs()
require.Equal(t, 1, len(logs), "expecting a log with post-state")
evmPost := logs[0].Data
require.Equal(t, crypto.Keccak256Hash(evmPost), postHash, "logged state must be accurate")
env.StateDB.RevertToSnapshot(snap)
t.Logf("EVM step took %d gas, and returned stateHash %s", startingGas-leftOverGas, postHash)
evmPost := evm.Step(t, stepWitness)
// verify the post-state matches.
// TODO: maybe more readable to decode the evmPost state, and do attribute-wise comparison.
uniPost := us.state.EncodeWitness()
require.Equal(t, hexutil.Bytes(uniPost).String(), hexutil.Bytes(evmPost).String(),
goPost := goState.state.EncodeWitness()
require.Equal(t, hexutil.Bytes(goPost).String(), hexutil.Bytes(evmPost).String(),
"mipsevm produced different state than EVM")
}
if exitGroup {
require.NotEqual(t, uint32(endAddr), us.state.PC, "must not reach end")
require.True(t, us.state.Exited, "must set exited state")
require.Equal(t, uint8(1), us.state.ExitCode, "must exit with 1")
require.NotEqual(t, uint32(endAddr), goState.state.PC, "must not reach end")
require.True(t, goState.state.Exited, "must set exited state")
require.Equal(t, uint8(1), goState.state.ExitCode, "must exit with 1")
} else {
require.Equal(t, uint32(endAddr), state.PC, "must reach end")
// inspect test result
......@@ -160,8 +180,8 @@ func TestEVMFault(t *testing.T) {
// set the return address ($ra) to jump into when test completes
state.Registers[31] = endAddr
us := NewInstrumentedState(state, nil, os.Stdout, os.Stderr)
require.Panics(t, func() { _, _ = us.Step(true) }, "must panic on illegal instruction")
goState := NewInstrumentedState(state, nil, os.Stdout, os.Stderr)
require.Panics(t, func() { _, _ = goState.Step(true) }, "must panic on illegal instruction")
insnProof := initialState.Memory.MerkleProof(0)
stepWitness := &StepWitness{
......@@ -181,7 +201,6 @@ func TestHelloEVM(t *testing.T) {
contracts, addrs := testContractsSetup(t)
var tracer vm.EVMLogger // no-tracer by default, but see SourceMapTracer and MarkdownTracer
//tracer = SourceMapTracer(t, contracts, addrs)
sender := common.Address{0x13, 0x37}
elfProgram, err := elf.Open("../example/bin/hello.elf")
require.NoError(t, err, "open ELF file")
......@@ -194,14 +213,11 @@ func TestHelloEVM(t *testing.T) {
require.NoError(t, PatchStack(state), "add initial stack")
var stdOutBuf, stdErrBuf bytes.Buffer
us := NewInstrumentedState(state, nil, io.MultiWriter(&stdOutBuf, os.Stdout), io.MultiWriter(&stdErrBuf, os.Stderr))
env, evmState := NewEVMEnv(contracts, addrs)
env.Config.Tracer = tracer
goState := NewInstrumentedState(state, nil, io.MultiWriter(&stdOutBuf, os.Stdout), io.MultiWriter(&stdErrBuf, os.Stderr))
start := time.Now()
for i := 0; i < 400_000; i++ {
if us.state.Exited {
if goState.state.Exited {
break
}
insn := state.Memory.GetMemory(state.PC)
......@@ -209,30 +225,16 @@ func TestHelloEVM(t *testing.T) {
t.Logf("step: %4d pc: 0x%08x insn: 0x%08x", state.Step, state.PC, insn)
}
stepWitness, err := us.Step(true)
require.NoError(t, err)
input := stepWitness.EncodeStepInput()
startingGas := uint64(30_000_000)
// we take a snapshot so we can clean up the state, and isolate the logs of this instruction run.
snap := env.StateDB.Snapshot()
ret, leftOverGas, err := env.Call(vm.AccountRef(sender), addrs.MIPS, input, startingGas, big.NewInt(0))
require.NoErrorf(t, err, "evm should not fail, took %d gas", startingGas-leftOverGas)
require.Len(t, ret, 32, "expecting 32-byte state hash")
// remember state hash, to check it against state
postHash := common.Hash(*(*[32]byte)(ret))
logs := evmState.Logs()
require.Equal(t, 1, len(logs), "expecting a log with post-state")
evmPost := logs[0].Data
require.Equal(t, crypto.Keccak256Hash(evmPost), postHash, "logged state must be accurate")
env.StateDB.RevertToSnapshot(snap)
//t.Logf("EVM step took %d gas, and returned stateHash %s", startingGas-leftOverGas, postHash)
evm := NewMIPSEVM(contracts, addrs)
evm.SetTracer(tracer)
stepWitness, err := goState.Step(true)
require.NoError(t, err)
evmPost := evm.Step(t, stepWitness)
// verify the post-state matches.
// TODO: maybe more readable to decode the evmPost state, and do attribute-wise comparison.
uniPost := us.state.EncodeWitness()
require.Equal(t, hexutil.Bytes(uniPost).String(), hexutil.Bytes(evmPost).String(),
goPost := goState.state.EncodeWitness()
require.Equal(t, hexutil.Bytes(goPost).String(), hexutil.Bytes(evmPost).String(),
"mipsevm produced different state than EVM")
}
end := time.Now()
......@@ -264,13 +266,10 @@ func TestClaimEVM(t *testing.T) {
oracle, expectedStdOut, expectedStdErr := claimTestOracle(t)
var stdOutBuf, stdErrBuf bytes.Buffer
us := NewInstrumentedState(state, oracle, io.MultiWriter(&stdOutBuf, os.Stdout), io.MultiWriter(&stdErrBuf, os.Stderr))
env, evmState := NewEVMEnv(contracts, addrs)
env.Config.Tracer = tracer
goState := NewInstrumentedState(state, oracle, io.MultiWriter(&stdOutBuf, os.Stdout), io.MultiWriter(&stdErrBuf, os.Stderr))
for i := 0; i < 2000_000; i++ {
if us.state.Exited {
if goState.state.Exited {
break
}
......@@ -279,32 +278,16 @@ func TestClaimEVM(t *testing.T) {
t.Logf("step: %4d pc: 0x%08x insn: 0x%08x", state.Step, state.PC, insn)
}
stepWitness, err := us.Step(true)
stepWitness, err := goState.Step(true)
require.NoError(t, err)
input := stepWitness.EncodeStepInput()
startingGas := uint64(30_000_000)
// we take a snapshot so we can clean up the state, and isolate the logs of this instruction run.
snap := env.StateDB.Snapshot()
// prepare pre-image oracle data, if any
if stepWitness.HasPreimage() {
poInput, err := stepWitness.EncodePreimageOracleInput()
require.NoError(t, err, "encode preimage oracle input")
_, leftOverGas, err := env.Call(vm.AccountRef(addrs.Sender), addrs.Oracle, poInput, startingGas, big.NewInt(0))
require.NoErrorf(t, err, "evm should not fail, took %d gas", startingGas-leftOverGas)
}
evm := NewMIPSEVM(contracts, addrs)
evm.SetTracer(tracer)
evmPost := evm.Step(t, stepWitness)
ret, leftOverGas, err := env.Call(vm.AccountRef(addrs.Sender), addrs.MIPS, input, startingGas, big.NewInt(0))
require.NoErrorf(t, err, "evm should not fail, took %d gas", startingGas-leftOverGas)
require.Len(t, ret, 32, "expecting 32-byte state hash")
// remember state hash, to check it against state
postHash := common.Hash(*(*[32]byte)(ret))
logs := evmState.Logs()
require.Equal(t, 1, len(logs), "expecting a log with post-state")
evmPost := logs[0].Data
require.Equal(t, crypto.Keccak256Hash(evmPost), postHash, "logged state must be accurate")
env.StateDB.RevertToSnapshot(snap)
goPost := goState.state.EncodeWitness()
require.Equal(t, hexutil.Bytes(goPost).String(), hexutil.Bytes(evmPost).String(),
"mipsevm produced different state than EVM")
}
require.True(t, state.Exited, "must complete program")
......
package mipsevm
import (
"os"
"testing"
preimage "github.com/ethereum-optimism/optimism/op-preimage"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/crypto"
"github.com/stretchr/testify/require"
)
const syscallInsn = uint32(0x00_00_00_0c)
func FuzzStateSyscallBrk(f *testing.F) {
contracts, addrs := testContractsSetup(f)
f.Fuzz(func(t *testing.T, pc uint32, step uint64, preimageOffset uint32) {
pc = pc & 0xFF_FF_FF_FC // align PC
nextPC := pc + 4
state := &State{
PC: pc,
NextPC: nextPC,
LO: 0,
HI: 0,
Heap: 0,
ExitCode: 0,
Exited: false,
Memory: NewMemory(),
Registers: [32]uint32{2: sysBrk},
Step: step,
PreimageKey: common.Hash{},
PreimageOffset: preimageOffset,
}
state.Memory.SetMemory(pc, syscallInsn)
preStateRoot := state.Memory.MerkleRoot()
expectedRegisters := state.Registers
expectedRegisters[2] = 0x4000_0000
goState := NewInstrumentedState(state, nil, os.Stdout, os.Stderr)
stepWitness, err := goState.Step(true)
require.NoError(t, err)
require.False(t, stepWitness.HasPreimage())
require.Equal(t, pc+4, state.PC)
require.Equal(t, nextPC+4, state.NextPC)
require.Equal(t, uint32(0), state.LO)
require.Equal(t, uint32(0), state.HI)
require.Equal(t, uint32(0), state.Heap)
require.Equal(t, uint8(0), state.ExitCode)
require.Equal(t, false, state.Exited)
require.Equal(t, preStateRoot, state.Memory.MerkleRoot())
require.Equal(t, expectedRegisters, state.Registers)
require.Equal(t, step+1, state.Step)
require.Equal(t, common.Hash{}, state.PreimageKey)
require.Equal(t, preimageOffset, state.PreimageOffset)
evm := NewMIPSEVM(contracts, addrs)
evmPost := evm.Step(t, stepWitness)
goPost := goState.state.EncodeWitness()
require.Equal(t, hexutil.Bytes(goPost).String(), hexutil.Bytes(evmPost).String(),
"mipsevm produced different state than EVM")
})
}
func FuzzStateSyscallClone(f *testing.F) {
contracts, addrs := testContractsSetup(f)
f.Fuzz(func(t *testing.T, pc uint32, step uint64, preimageOffset uint32) {
pc = pc & 0xFF_FF_FF_FC // align PC
nextPC := pc + 4
state := &State{
PC: pc,
NextPC: nextPC,
LO: 0,
HI: 0,
Heap: 0,
ExitCode: 0,
Exited: false,
Memory: NewMemory(),
Registers: [32]uint32{2: sysClone},
Step: step,
PreimageOffset: preimageOffset,
}
state.Memory.SetMemory(pc, syscallInsn)
preStateRoot := state.Memory.MerkleRoot()
expectedRegisters := state.Registers
expectedRegisters[2] = 0x1
goState := NewInstrumentedState(state, nil, os.Stdout, os.Stderr)
stepWitness, err := goState.Step(true)
require.NoError(t, err)
require.False(t, stepWitness.HasPreimage())
require.Equal(t, pc+4, state.PC)
require.Equal(t, nextPC+4, state.NextPC)
require.Equal(t, uint32(0), state.LO)
require.Equal(t, uint32(0), state.HI)
require.Equal(t, uint32(0), state.Heap)
require.Equal(t, uint8(0), state.ExitCode)
require.Equal(t, false, state.Exited)
require.Equal(t, preStateRoot, state.Memory.MerkleRoot())
require.Equal(t, expectedRegisters, state.Registers)
require.Equal(t, step+1, state.Step)
require.Equal(t, common.Hash{}, state.PreimageKey)
require.Equal(t, preimageOffset, state.PreimageOffset)
evm := NewMIPSEVM(contracts, addrs)
evmPost := evm.Step(t, stepWitness)
goPost := goState.state.EncodeWitness()
require.Equal(t, hexutil.Bytes(goPost).String(), hexutil.Bytes(evmPost).String(),
"mipsevm produced different state than EVM")
})
}
func FuzzStateSyscallMmap(f *testing.F) {
contracts, addrs := testContractsSetup(f)
f.Fuzz(func(t *testing.T, addr uint32, siz uint32, heap uint32) {
state := &State{
PC: 0,
NextPC: 4,
LO: 0,
HI: 0,
Heap: heap,
ExitCode: 0,
Exited: false,
Memory: NewMemory(),
Registers: [32]uint32{2: sysMmap, 4: addr, 5: siz},
Step: 0,
PreimageOffset: 0,
}
state.Memory.SetMemory(0, syscallInsn)
preStateRoot := state.Memory.MerkleRoot()
preStateRegisters := state.Registers
goState := NewInstrumentedState(state, nil, os.Stdout, os.Stderr)
stepWitness, err := goState.Step(true)
require.NoError(t, err)
require.False(t, stepWitness.HasPreimage())
require.Equal(t, uint32(4), state.PC)
require.Equal(t, uint32(8), state.NextPC)
require.Equal(t, uint32(0), state.LO)
require.Equal(t, uint32(0), state.HI)
require.Equal(t, uint8(0), state.ExitCode)
require.Equal(t, false, state.Exited)
require.Equal(t, preStateRoot, state.Memory.MerkleRoot())
require.Equal(t, uint64(1), state.Step)
require.Equal(t, common.Hash{}, state.PreimageKey)
require.Equal(t, uint32(0), state.PreimageOffset)
if addr == 0 {
expectedRegisters := preStateRegisters
expectedRegisters[2] = heap
require.Equal(t, expectedRegisters, state.Registers)
sizAlign := siz
if sizAlign&PageAddrMask != 0 { // adjust size to align with page size
sizAlign = siz + PageSize - (siz & PageAddrMask)
}
require.Equal(t, uint32(heap+sizAlign), state.Heap)
} else {
expectedRegisters := preStateRegisters
expectedRegisters[2] = addr
require.Equal(t, expectedRegisters, state.Registers)
require.Equal(t, uint32(heap), state.Heap)
}
evm := NewMIPSEVM(contracts, addrs)
evmPost := evm.Step(t, stepWitness)
goPost := goState.state.EncodeWitness()
require.Equal(t, hexutil.Bytes(goPost).String(), hexutil.Bytes(evmPost).String(),
"mipsevm produced different state than EVM")
})
}
func FuzzStateSyscallExitGroup(f *testing.F) {
contracts, addrs := testContractsSetup(f)
f.Fuzz(func(t *testing.T, exitCode uint8, pc uint32, step uint64) {
pc = pc & 0xFF_FF_FF_FC // align PC
nextPC := pc + 4
state := &State{
PC: pc,
NextPC: nextPC,
LO: 0,
HI: 0,
Heap: 0,
ExitCode: 0,
Exited: false,
Memory: NewMemory(),
Registers: [32]uint32{2: sysExitGroup, 4: uint32(exitCode)},
Step: step,
PreimageOffset: 0,
}
state.Memory.SetMemory(pc, syscallInsn)
preStateRoot := state.Memory.MerkleRoot()
preStateRegisters := state.Registers
goState := NewInstrumentedState(state, nil, os.Stdout, os.Stderr)
stepWitness, err := goState.Step(true)
require.NoError(t, err)
require.False(t, stepWitness.HasPreimage())
require.Equal(t, pc, state.PC)
require.Equal(t, nextPC, state.NextPC)
require.Equal(t, uint32(0), state.LO)
require.Equal(t, uint32(0), state.HI)
require.Equal(t, uint32(0), state.Heap)
require.Equal(t, uint8(exitCode), state.ExitCode)
require.Equal(t, true, state.Exited)
require.Equal(t, preStateRoot, state.Memory.MerkleRoot())
require.Equal(t, preStateRegisters, state.Registers)
require.Equal(t, step+1, state.Step)
require.Equal(t, common.Hash{}, state.PreimageKey)
require.Equal(t, uint32(0), state.PreimageOffset)
evm := NewMIPSEVM(contracts, addrs)
evmPost := evm.Step(t, stepWitness)
goPost := goState.state.EncodeWitness()
require.Equal(t, hexutil.Bytes(goPost).String(), hexutil.Bytes(evmPost).String(),
"mipsevm produced different state than EVM")
})
}
func FuzzStateSyscallFnctl(f *testing.F) {
contracts, addrs := testContractsSetup(f)
f.Fuzz(func(t *testing.T, fd uint32, cmd uint32) {
state := &State{
PC: 0,
NextPC: 4,
LO: 0,
HI: 0,
Heap: 0,
ExitCode: 0,
Exited: false,
Memory: NewMemory(),
Registers: [32]uint32{2: sysFcntl, 4: fd, 5: cmd},
Step: 0,
PreimageOffset: 0,
}
state.Memory.SetMemory(0, syscallInsn)
preStateRoot := state.Memory.MerkleRoot()
preStateRegisters := state.Registers
goState := NewInstrumentedState(state, nil, os.Stdout, os.Stderr)
stepWitness, err := goState.Step(true)
require.NoError(t, err)
require.False(t, stepWitness.HasPreimage())
require.Equal(t, uint32(4), state.PC)
require.Equal(t, uint32(8), state.NextPC)
require.Equal(t, uint32(0), state.LO)
require.Equal(t, uint32(0), state.HI)
require.Equal(t, uint32(0), state.Heap)
require.Equal(t, uint8(0), state.ExitCode)
require.Equal(t, false, state.Exited)
require.Equal(t, preStateRoot, state.Memory.MerkleRoot())
require.Equal(t, uint64(1), state.Step)
require.Equal(t, common.Hash{}, state.PreimageKey)
require.Equal(t, uint32(0), state.PreimageOffset)
if cmd == 3 {
expectedRegisters := preStateRegisters
switch fd {
case fdStdin, fdPreimageRead, fdHintRead:
expectedRegisters[2] = 0
case fdStdout, fdStderr, fdPreimageWrite, fdHintWrite:
expectedRegisters[2] = 1
default:
expectedRegisters[2] = 0xFF_FF_FF_FF
expectedRegisters[7] = MipsEBADF
}
require.Equal(t, expectedRegisters, state.Registers)
} else {
expectedRegisters := preStateRegisters
expectedRegisters[2] = 0xFF_FF_FF_FF
expectedRegisters[7] = MipsEINVAL
require.Equal(t, expectedRegisters, state.Registers)
}
evm := NewMIPSEVM(contracts, addrs)
evmPost := evm.Step(t, stepWitness)
goPost := goState.state.EncodeWitness()
require.Equal(t, hexutil.Bytes(goPost).String(), hexutil.Bytes(evmPost).String(),
"mipsevm produced different state than EVM")
})
}
func FuzzStateHintRead(f *testing.F) {
contracts, addrs := testContractsSetup(f)
f.Fuzz(func(t *testing.T, addr uint32, count uint32) {
preimageData := []byte("hello world")
state := &State{
PC: 0,
NextPC: 4,
LO: 0,
HI: 0,
Heap: 0,
ExitCode: 0,
Exited: false,
Memory: NewMemory(),
Registers: [32]uint32{2: sysRead, 4: fdHintRead, 5: addr, 6: count},
Step: 0,
PreimageKey: preimage.Keccak256Key(crypto.Keccak256Hash(preimageData)).PreimageKey(),
PreimageOffset: 0,
}
state.Memory.SetMemory(0, syscallInsn)
preStatePreimageKey := state.PreimageKey
preStateRoot := state.Memory.MerkleRoot()
expectedRegisters := state.Registers
expectedRegisters[2] = count
oracle := staticOracle(t, preimageData) // only used for hinting
goState := NewInstrumentedState(state, oracle, os.Stdout, os.Stderr)
stepWitness, err := goState.Step(true)
require.NoError(t, err)
require.False(t, stepWitness.HasPreimage())
require.Equal(t, uint32(4), state.PC)
require.Equal(t, uint32(8), state.NextPC)
require.Equal(t, uint32(0), state.LO)
require.Equal(t, uint32(0), state.HI)
require.Equal(t, uint32(0), state.Heap)
require.Equal(t, uint8(0), state.ExitCode)
require.Equal(t, false, state.Exited)
require.Equal(t, preStateRoot, state.Memory.MerkleRoot())
require.Equal(t, uint64(1), state.Step)
require.Equal(t, preStatePreimageKey, state.PreimageKey)
evm := NewMIPSEVM(contracts, addrs)
evmPost := evm.Step(t, stepWitness)
goPost := goState.state.EncodeWitness()
require.Equal(t, hexutil.Bytes(goPost).String(), hexutil.Bytes(evmPost).String(),
"mipsevm produced different state than EVM")
})
}
func FuzzStatePreimageRead(f *testing.F) {
contracts, addrs := testContractsSetup(f)
f.Fuzz(func(t *testing.T, addr uint32, count uint32, preimageOffset uint32) {
preimageData := []byte("hello world")
if preimageOffset >= uint32(len(preimageData)) {
t.SkipNow()
}
state := &State{
PC: 0,
NextPC: 4,
LO: 0,
HI: 0,
Heap: 0,
ExitCode: 0,
Exited: false,
Memory: NewMemory(),
Registers: [32]uint32{2: sysRead, 4: fdPreimageRead, 5: addr, 6: count},
Step: 0,
PreimageKey: preimage.Keccak256Key(crypto.Keccak256Hash(preimageData)).PreimageKey(),
PreimageOffset: preimageOffset,
}
state.Memory.SetMemory(0, syscallInsn)
preStatePreimageKey := state.PreimageKey
preStateRoot := state.Memory.MerkleRoot()
writeLen := count
if writeLen > 4 {
writeLen = 4
}
if preimageOffset+writeLen > uint32(8+len(preimageData)) {
writeLen = uint32(8+len(preimageData)) - preimageOffset
}
oracle := staticOracle(t, preimageData)
goState := NewInstrumentedState(state, oracle, os.Stdout, os.Stderr)
stepWitness, err := goState.Step(true)
require.NoError(t, err)
require.True(t, stepWitness.HasPreimage())
require.Equal(t, uint32(4), state.PC)
require.Equal(t, uint32(8), state.NextPC)
require.Equal(t, uint32(0), state.LO)
require.Equal(t, uint32(0), state.HI)
require.Equal(t, uint32(0), state.Heap)
require.Equal(t, uint8(0), state.ExitCode)
require.Equal(t, false, state.Exited)
if writeLen > 0 {
// Memory may be unchanged if we're writing the first zero-valued 7 bytes of the pre-image.
//require.NotEqual(t, preStateRoot, state.Memory.MerkleRoot())
require.Greater(t, state.PreimageOffset, preimageOffset)
} else {
require.Equal(t, preStateRoot, state.Memory.MerkleRoot())
require.Equal(t, state.PreimageOffset, preimageOffset)
}
require.Equal(t, uint64(1), state.Step)
require.Equal(t, preStatePreimageKey, state.PreimageKey)
evm := NewMIPSEVM(contracts, addrs)
evmPost := evm.Step(t, stepWitness)
goPost := goState.state.EncodeWitness()
require.Equal(t, hexutil.Bytes(goPost).String(), hexutil.Bytes(evmPost).String(),
"mipsevm produced different state than EVM")
})
}
func FuzzStateHintWrite(f *testing.F) {
contracts, addrs := testContractsSetup(f)
f.Fuzz(func(t *testing.T, addr uint32, count uint32) {
preimageData := []byte("hello world")
state := &State{
PC: 0,
NextPC: 4,
LO: 0,
HI: 0,
Heap: 0,
ExitCode: 0,
Exited: false,
Memory: NewMemory(),
Registers: [32]uint32{2: sysWrite, 4: fdHintWrite, 5: addr, 6: count},
Step: 0,
PreimageKey: preimage.Keccak256Key(crypto.Keccak256Hash(preimageData)).PreimageKey(),
PreimageOffset: 0,
// This is only used by mips.go. The reads a zeroed page-sized buffer when reading hint data from memory.
// We pre-allocate a buffer for the read hint data to be copied into.
LastHint: make(hexutil.Bytes, PageSize),
}
state.Memory.SetMemory(0, syscallInsn)
preStatePreimageKey := state.PreimageKey
preStateRoot := state.Memory.MerkleRoot()
expectedRegisters := state.Registers
expectedRegisters[2] = count
oracle := staticOracle(t, preimageData) // only used for hinting
goState := NewInstrumentedState(state, oracle, os.Stdout, os.Stderr)
stepWitness, err := goState.Step(true)
require.NoError(t, err)
require.False(t, stepWitness.HasPreimage())
require.Equal(t, uint32(4), state.PC)
require.Equal(t, uint32(8), state.NextPC)
require.Equal(t, uint32(0), state.LO)
require.Equal(t, uint32(0), state.HI)
require.Equal(t, uint32(0), state.Heap)
require.Equal(t, uint8(0), state.ExitCode)
require.Equal(t, false, state.Exited)
require.Equal(t, preStateRoot, state.Memory.MerkleRoot())
require.Equal(t, uint64(1), state.Step)
require.Equal(t, preStatePreimageKey, state.PreimageKey)
evm := NewMIPSEVM(contracts, addrs)
evmPost := evm.Step(t, stepWitness)
goPost := goState.state.EncodeWitness()
require.Equal(t, hexutil.Bytes(goPost).String(), hexutil.Bytes(evmPost).String(),
"mipsevm produced different state than EVM")
})
}
func FuzzStatePreimageWrite(f *testing.F) {
contracts, addrs := testContractsSetup(f)
f.Fuzz(func(t *testing.T, addr uint32, count uint32) {
preimageData := []byte("hello world")
state := &State{
PC: 0,
NextPC: 4,
LO: 0,
HI: 0,
Heap: 0,
ExitCode: 0,
Exited: false,
Memory: NewMemory(),
Registers: [32]uint32{2: sysWrite, 4: fdPreimageWrite, 5: addr, 6: count},
Step: 0,
PreimageKey: preimage.Keccak256Key(crypto.Keccak256Hash(preimageData)).PreimageKey(),
PreimageOffset: 128,
}
state.Memory.SetMemory(0, syscallInsn)
preStateRoot := state.Memory.MerkleRoot()
expectedRegisters := state.Registers
sz := 4 - (addr & 0x3)
if sz < count {
sz = count
}
expectedRegisters[2] = sz
oracle := staticOracle(t, preimageData)
goState := NewInstrumentedState(state, oracle, os.Stdout, os.Stderr)
stepWitness, err := goState.Step(true)
require.NoError(t, err)
require.False(t, stepWitness.HasPreimage())
require.Equal(t, uint32(4), state.PC)
require.Equal(t, uint32(8), state.NextPC)
require.Equal(t, uint32(0), state.LO)
require.Equal(t, uint32(0), state.HI)
require.Equal(t, uint32(0), state.Heap)
require.Equal(t, uint8(0), state.ExitCode)
require.Equal(t, false, state.Exited)
require.Equal(t, preStateRoot, state.Memory.MerkleRoot())
require.Equal(t, uint64(1), state.Step)
require.Equal(t, uint32(0), state.PreimageOffset)
evm := NewMIPSEVM(contracts, addrs)
evmPost := evm.Step(t, stepWitness)
goPost := goState.state.EncodeWitness()
require.Equal(t, hexutil.Bytes(goPost).String(), hexutil.Bytes(evmPost).String(),
"mipsevm produced different state than EVM")
})
}
......@@ -6,6 +6,16 @@ import (
"io"
)
const (
sysMmap = 4090
sysBrk = 4045
sysClone = 4120
sysExitGroup = 4246
sysRead = 4003
sysWrite = 4004
sysFcntl = 4055
)
func (m *InstrumentedState) readPreimage(key [32]byte, offset uint32) (dat [32]byte, datLen uint32) {
preimage := m.lastPreimage
if key != m.lastPreimageKey {
......@@ -43,7 +53,7 @@ func (m *InstrumentedState) handleSyscall() error {
//fmt.Printf("syscall: %d\n", syscallNum)
switch syscallNum {
case 4090: // mmap
case sysMmap:
sz := a1
if sz&PageAddrMask != 0 { // adjust size to align with page size
sz += PageSize - (sz & PageAddrMask)
......@@ -56,15 +66,15 @@ func (m *InstrumentedState) handleSyscall() error {
v0 = a0
//fmt.Printf("mmap hint 0x%x size 0x%x\n", v0, sz)
}
case 4045: // brk
case sysBrk:
v0 = 0x40000000
case 4120: // clone (not supported)
case sysClone: // clone (not supported)
v0 = 1
case 4246: // exit_group
case sysExitGroup:
m.state.Exited = true
m.state.ExitCode = uint8(a0)
return nil
case 4003: // read
case sysRead:
// args: a0 = fd, a1 = addr, a2 = count
// returns: v0 = read, v1 = err code
switch a0 {
......@@ -98,7 +108,7 @@ func (m *InstrumentedState) handleSyscall() error {
v0 = 0xFFffFFff
v1 = MipsEBADF
}
case 4004: // write
case sysWrite:
// args: a0 = fd, a1 = addr, a2 = count
// returns: v0 = written, v1 = err code
switch a0 {
......@@ -144,7 +154,7 @@ func (m *InstrumentedState) handleSyscall() error {
v0 = 0xFFffFFff
v1 = MipsEBADF
}
case 4055: // fcntl
case sysFcntl:
// args: a0 = fd, a1 = cmd
if a1 == 3 { // F_GETFL: get file descriptor flags
switch a0 {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment