Commit 9750b3b0 authored by mbaxter's avatar mbaxter Committed by Adrian Sutton

cannon: Limit mmap allocations (#285)

* cannon: Basic memory protections

Add memory protections against high memory allocations to the VM.
This prevents large allocations from causing the `heap` to overflow and
wrap into low memory, which could overwrite code and cause arbitrary code execution.

---------
Co-authored-by: default avatarinphi <mlaw2501@gmail.com>
parent 33429582
...@@ -9,6 +9,7 @@ import ( ...@@ -9,6 +9,7 @@ import (
"github.com/ethereum-optimism/optimism/cannon/mipsevm" "github.com/ethereum-optimism/optimism/cannon/mipsevm"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/memory" "github.com/ethereum-optimism/optimism/cannon/mipsevm/memory"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/program"
) )
// Syscall codes // Syscall codes
...@@ -132,7 +133,6 @@ const ( ...@@ -132,7 +133,6 @@ const (
// Other constants // Other constants
const ( const (
SchedQuantum = 100_000 SchedQuantum = 100_000
BrkStart = 0x40000000
) )
func GetSyscallArgs(registers *[32]uint32) (syscallNum, a0, a1, a2, a3 uint32) { func GetSyscallArgs(registers *[32]uint32) (syscallNum, a0, a1, a2, a3 uint32) {
...@@ -158,6 +158,12 @@ func HandleSysMmap(a0, a1, heap uint32) (v0, v1, newHeap uint32) { ...@@ -158,6 +158,12 @@ func HandleSysMmap(a0, a1, heap uint32) (v0, v1, newHeap uint32) {
v0 = heap v0 = heap
//fmt.Printf("mmap heap 0x%x size 0x%x\n", v0, sz) //fmt.Printf("mmap heap 0x%x size 0x%x\n", v0, sz)
newHeap += sz newHeap += sz
// Fail if new heap exceeds memory limit, newHeap overflows around to low memory, or sz overflows
if newHeap > program.HEAP_END || newHeap < heap || sz < a1 {
v0 = SysErrorSignal
v1 = MipsEINVAL
return v0, v1, heap
}
} else { } else {
v0 = a0 v0 = a0
//fmt.Printf("mmap hint 0x%x size 0x%x\n", v0, sz) //fmt.Printf("mmap hint 0x%x size 0x%x\n", v0, sz)
......
...@@ -10,6 +10,7 @@ import ( ...@@ -10,6 +10,7 @@ import (
"github.com/ethereum-optimism/optimism/cannon/mipsevm" "github.com/ethereum-optimism/optimism/cannon/mipsevm"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/exec" "github.com/ethereum-optimism/optimism/cannon/mipsevm/exec"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/program"
) )
func (m *InstrumentedState) handleSyscall() error { func (m *InstrumentedState) handleSyscall() error {
...@@ -26,7 +27,7 @@ func (m *InstrumentedState) handleSyscall() error { ...@@ -26,7 +27,7 @@ func (m *InstrumentedState) handleSyscall() error {
v0, v1, newHeap = exec.HandleSysMmap(a0, a1, m.state.Heap) v0, v1, newHeap = exec.HandleSysMmap(a0, a1, m.state.Heap)
m.state.Heap = newHeap m.state.Heap = newHeap
case exec.SysBrk: case exec.SysBrk:
v0 = exec.BrkStart v0 = program.PROGRAM_BREAK
case exec.SysClone: // clone case exec.SysClone: // clone
// a0 = flag bitmask, a1 = stack pointer // a0 = flag bitmask, a1 = stack pointer
if exec.ValidCloneFlags != a0 { if exec.ValidCloneFlags != a0 {
......
...@@ -9,7 +9,11 @@ import ( ...@@ -9,7 +9,11 @@ import (
"github.com/ethereum-optimism/optimism/cannon/mipsevm" "github.com/ethereum-optimism/optimism/cannon/mipsevm"
) )
const HEAP_START = 0x05000000 const (
HEAP_START = 0x05_00_00_00
HEAP_END = 0x60_00_00_00
PROGRAM_BREAK = 0x40_00_00_00
)
type CreateInitialFPVMState[T mipsevm.FPVMState] func(pc, heapStart uint32) T type CreateInitialFPVMState[T mipsevm.FPVMState] func(pc, heapStart uint32) T
......
...@@ -5,6 +5,7 @@ import ( ...@@ -5,6 +5,7 @@ import (
"github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/exec" "github.com/ethereum-optimism/optimism/cannon/mipsevm/exec"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/program"
) )
func (m *InstrumentedState) handleSyscall() error { func (m *InstrumentedState) handleSyscall() error {
...@@ -20,7 +21,7 @@ func (m *InstrumentedState) handleSyscall() error { ...@@ -20,7 +21,7 @@ func (m *InstrumentedState) handleSyscall() error {
v0, v1, newHeap = exec.HandleSysMmap(a0, a1, m.state.Heap) v0, v1, newHeap = exec.HandleSysMmap(a0, a1, m.state.Heap)
m.state.Heap = newHeap m.state.Heap = newHeap
case exec.SysBrk: case exec.SysBrk:
v0 = exec.BrkStart v0 = program.PROGRAM_BREAK
case exec.SysClone: // clone (not supported) case exec.SysClone: // clone (not supported)
v0 = 1 v0 = 1
case exec.SysExitGroup: case exec.SysExitGroup:
......
...@@ -19,6 +19,7 @@ import ( ...@@ -19,6 +19,7 @@ import (
"github.com/ethereum-optimism/optimism/cannon/mipsevm/exec" "github.com/ethereum-optimism/optimism/cannon/mipsevm/exec"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/memory" "github.com/ethereum-optimism/optimism/cannon/mipsevm/memory"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/multithreaded" "github.com/ethereum-optimism/optimism/cannon/mipsevm/multithreaded"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/program"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/singlethreaded" "github.com/ethereum-optimism/optimism/cannon/mipsevm/singlethreaded"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/testutil" "github.com/ethereum-optimism/optimism/cannon/mipsevm/testutil"
) )
...@@ -208,6 +209,88 @@ func TestEVMSingleStep(t *testing.T) { ...@@ -208,6 +209,88 @@ func TestEVMSingleStep(t *testing.T) {
} }
} }
func TestEVM_MMap(t *testing.T) {
contracts, addrs := testContractsSetup(t)
var tracer *tracing.Hooks
cases := []struct {
name string
heap uint32
address uint32
size uint32
shouldFail bool
expectedHeap uint32
}{
{name: "Increment heap by max value", heap: program.HEAP_START, address: 0, size: ^uint32(0), shouldFail: true},
{name: "Increment heap to 0", heap: program.HEAP_START, address: 0, size: ^uint32(0) - program.HEAP_START + 1, shouldFail: true},
{name: "Increment heap to previous page", heap: program.HEAP_START, address: 0, size: ^uint32(0) - program.HEAP_START - memory.PageSize + 1, shouldFail: true},
{name: "Increment max page size", heap: program.HEAP_START, address: 0, size: ^uint32(0) & ^uint32(memory.PageAddrMask), shouldFail: true},
{name: "Increment max page size from 0", heap: 0, address: 0, size: ^uint32(0) & ^uint32(memory.PageAddrMask), shouldFail: true},
{name: "Increment heap at limit", heap: program.HEAP_END, address: 0, size: 1, shouldFail: true},
{name: "Increment heap to limit", heap: program.HEAP_END - memory.PageSize, address: 0, size: 1, shouldFail: false, expectedHeap: program.HEAP_END},
{name: "Increment heap within limit", heap: program.HEAP_END - 2*memory.PageSize, address: 0, size: 1, shouldFail: false, expectedHeap: program.HEAP_END - memory.PageSize},
{name: "Request specific address", heap: program.HEAP_START, address: 0x50_00_00_00, size: 0, shouldFail: false, expectedHeap: program.HEAP_START},
}
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
state := singlethreaded.CreateEmptyState()
state.Heap = c.heap
state.Memory.SetMemory(state.GetPC(), syscallInsn)
state.Registers = testutil.RandomRegisters(77)
state.Registers[2] = exec.SysMmap
state.Registers[4] = c.address
state.Registers[5] = c.size
step := state.Step
expectedRegisters := state.Registers
expectedHeap := state.Heap
expectedMemoryRoot := state.Memory.MerkleRoot()
if c.shouldFail {
expectedRegisters[2] = exec.SysErrorSignal
expectedRegisters[7] = exec.MipsEINVAL
} else {
expectedHeap = c.expectedHeap
if c.address == 0 {
expectedRegisters[2] = state.Heap
expectedRegisters[7] = 0
} else {
expectedRegisters[2] = c.address
expectedRegisters[7] = 0
}
}
us := singlethreaded.NewInstrumentedState(state, nil, os.Stdout, os.Stderr, nil)
stepWitness, err := us.Step(true)
require.NoError(t, err)
// Check expectations
require.Equal(t, step+1, state.Step)
require.Equal(t, expectedHeap, state.Heap)
require.Equal(t, expectedRegisters, state.Registers)
require.Equal(t, expectedMemoryRoot, state.Memory.MerkleRoot())
require.Equal(t, common.Hash{}, state.PreimageKey)
require.Equal(t, uint32(0), state.PreimageOffset)
require.Equal(t, uint32(4), state.Cpu.PC)
require.Equal(t, uint32(8), state.Cpu.NextPC)
require.Equal(t, uint32(0), state.Cpu.HI)
require.Equal(t, uint32(0), state.Cpu.LO)
require.Equal(t, false, state.Exited)
require.Equal(t, uint8(0), state.ExitCode)
require.Equal(t, hexutil.Bytes(nil), state.LastHint)
evm := testutil.NewMIPSEVM(contracts, addrs)
evm.SetTracer(tracer)
testutil.LogStepFailureAtCleanup(t, evm)
evmPost := evm.Step(t, stepWitness, step, singlethreaded.GetStateHashFn())
goPost, _ := us.GetState().EncodeWitness()
require.Equal(t, hexutil.Bytes(goPost).String(), hexutil.Bytes(evmPost).String(),
"mipsevm produced different state than EVM")
})
}
}
func TestEVMSysWriteHint(t *testing.T) { func TestEVMSysWriteHint(t *testing.T) {
contracts, addrs := testContractsSetup(t) contracts, addrs := testContractsSetup(t)
var tracer *tracing.Hooks var tracer *tracing.Hooks
......
...@@ -14,6 +14,7 @@ import ( ...@@ -14,6 +14,7 @@ import (
"github.com/ethereum-optimism/optimism/cannon/mipsevm" "github.com/ethereum-optimism/optimism/cannon/mipsevm"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/exec" "github.com/ethereum-optimism/optimism/cannon/mipsevm/exec"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/memory" "github.com/ethereum-optimism/optimism/cannon/mipsevm/memory"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/program"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/singlethreaded" "github.com/ethereum-optimism/optimism/cannon/mipsevm/singlethreaded"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/testutil" "github.com/ethereum-optimism/optimism/cannon/mipsevm/testutil"
preimage "github.com/ethereum-optimism/optimism/op-preimage" preimage "github.com/ethereum-optimism/optimism/op-preimage"
...@@ -45,7 +46,7 @@ func FuzzStateSyscallBrk(f *testing.F) { ...@@ -45,7 +46,7 @@ func FuzzStateSyscallBrk(f *testing.F) {
state.Memory.SetMemory(pc, syscallInsn) state.Memory.SetMemory(pc, syscallInsn)
preStateRoot := state.Memory.MerkleRoot() preStateRoot := state.Memory.MerkleRoot()
expectedRegisters := state.Registers expectedRegisters := state.Registers
expectedRegisters[2] = 0x4000_0000 expectedRegisters[2] = program.PROGRAM_BREAK
goState := singlethreaded.NewInstrumentedState(state, nil, os.Stdout, os.Stderr, nil) goState := singlethreaded.NewInstrumentedState(state, nil, os.Stdout, os.Stderr, nil)
stepWitness, err := goState.Step(true) stepWitness, err := goState.Step(true)
...@@ -127,7 +128,14 @@ func FuzzStateSyscallClone(f *testing.F) { ...@@ -127,7 +128,14 @@ func FuzzStateSyscallClone(f *testing.F) {
func FuzzStateSyscallMmap(f *testing.F) { func FuzzStateSyscallMmap(f *testing.F) {
contracts, addrs := testContractsSetup(f) contracts, addrs := testContractsSetup(f)
step := uint64(0) step := uint64(0)
f.Fuzz(func(t *testing.T, addr uint32, siz uint32, heap uint32) {
// Add special cases for large memory allocation
f.Add(uint32(0), uint32(0x1000), uint32(program.HEAP_END), int64(1))
f.Add(uint32(0), uint32(1<<31), uint32(program.HEAP_START), int64(2))
// Check edge case - just within bounds
f.Add(uint32(0), uint32(0x1000), uint32(program.HEAP_END-4096), int64(3))
f.Fuzz(func(t *testing.T, addr uint32, siz uint32, heap uint32, seed int64) {
state := &singlethreaded.State{ state := &singlethreaded.State{
Cpu: mipsevm.CpuScalars{ Cpu: mipsevm.CpuScalars{
PC: 0, PC: 0,
...@@ -139,11 +147,14 @@ func FuzzStateSyscallMmap(f *testing.F) { ...@@ -139,11 +147,14 @@ func FuzzStateSyscallMmap(f *testing.F) {
ExitCode: 0, ExitCode: 0,
Exited: false, Exited: false,
Memory: memory.NewMemory(), Memory: memory.NewMemory(),
Registers: [32]uint32{2: exec.SysMmap, 4: addr, 5: siz}, Registers: testutil.RandomRegisters(seed),
Step: step, Step: step,
PreimageOffset: 0, PreimageOffset: 0,
} }
state.Memory.SetMemory(0, syscallInsn) state.Memory.SetMemory(0, syscallInsn)
state.Registers[2] = exec.SysMmap
state.Registers[4] = addr
state.Registers[5] = siz
preStateRoot := state.Memory.MerkleRoot() preStateRoot := state.Memory.MerkleRoot()
preStateRegisters := state.Registers preStateRegisters := state.Registers
...@@ -152,32 +163,42 @@ func FuzzStateSyscallMmap(f *testing.F) { ...@@ -152,32 +163,42 @@ func FuzzStateSyscallMmap(f *testing.F) {
require.NoError(t, err) require.NoError(t, err)
require.False(t, stepWitness.HasPreimage()) require.False(t, stepWitness.HasPreimage())
require.Equal(t, uint32(4), state.Cpu.PC) var expectedHeap uint32
require.Equal(t, uint32(8), state.Cpu.NextPC) expectedRegisters := preStateRegisters
require.Equal(t, uint32(0), state.Cpu.LO)
require.Equal(t, uint32(0), state.Cpu.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 { if addr == 0 {
expectedRegisters := preStateRegisters
expectedRegisters[2] = heap
require.Equal(t, expectedRegisters, state.Registers)
sizAlign := siz sizAlign := siz
if sizAlign&memory.PageAddrMask != 0 { // adjust size to align with page size if sizAlign&memory.PageAddrMask != 0 { // adjust size to align with page size
sizAlign = siz + memory.PageSize - (siz & memory.PageAddrMask) sizAlign = siz + memory.PageSize - (siz & memory.PageAddrMask)
} }
require.Equal(t, uint32(heap+sizAlign), state.Heap) newHeap := heap + sizAlign
if newHeap > program.HEAP_END || newHeap < heap || sizAlign < siz {
expectedHeap = heap
expectedRegisters[2] = exec.SysErrorSignal
expectedRegisters[7] = exec.MipsEINVAL
} else {
expectedRegisters[2] = heap
expectedRegisters[7] = 0 // no error
expectedHeap = heap + sizAlign
}
} else { } else {
expectedRegisters := preStateRegisters
expectedRegisters[2] = addr expectedRegisters[2] = addr
require.Equal(t, expectedRegisters, state.Registers) expectedRegisters[7] = 0 // no error
require.Equal(t, uint32(heap), state.Heap) expectedHeap = heap
} }
require.Equal(t, uint32(4), state.Cpu.PC)
require.Equal(t, uint32(8), state.Cpu.NextPC)
require.Equal(t, uint32(0), state.Cpu.LO)
require.Equal(t, uint32(0), state.Cpu.HI)
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)
require.Equal(t, expectedHeap, state.Heap)
require.Equal(t, uint8(0), state.ExitCode)
require.Equal(t, false, state.Exited)
require.Equal(t, expectedRegisters, state.Registers)
evm := testutil.NewMIPSEVM(contracts, addrs) evm := testutil.NewMIPSEVM(contracts, addrs)
evmPost := evm.Step(t, stepWitness, step, singlethreaded.GetStateHashFn()) evmPost := evm.Step(t, stepWitness, step, singlethreaded.GetStateHashFn())
goPost, _ := goState.GetState().EncodeWitness() goPost, _ := goState.GetState().EncodeWitness()
......
package testutil
import "math/rand"
func RandomRegisters(seed int64) [32]uint32 {
r := rand.New(rand.NewSource(seed))
var registers [32]uint32
for i := 0; i < 32; i++ {
registers[i] = r.Uint32()
}
return registers
}
...@@ -140,12 +140,12 @@ ...@@ -140,12 +140,12 @@
"sourceCodeHash": "0x3ff4a3f21202478935412d47fd5ef7f94a170402ddc50e5c062013ce5544c83f" "sourceCodeHash": "0x3ff4a3f21202478935412d47fd5ef7f94a170402ddc50e5c062013ce5544c83f"
}, },
"src/cannon/MIPS.sol": { "src/cannon/MIPS.sol": {
"initCodeHash": "0xe16e212156c372a95dd73d05c16fbc312ad0fe4fec4aa822fb8f25fbbb6beb1c", "initCodeHash": "0x468236ddc5c995a086747da2a4197716a50ed54cb79378188e6e60e5a1e48d5e",
"sourceCodeHash": "0xfa6e6eacba6be2c9489f828f8a50dda40565eef5c73a2cf8e274e1fd4410d38a" "sourceCodeHash": "0xcf8b9665b8ec1d940ecc7cdef9e32704f38015304b5dbb60447921024e1ceafe"
}, },
"src/cannon/MIPS2.sol": { "src/cannon/MIPS2.sol": {
"initCodeHash": "0x75fe220dd34f1ca045f9c965ee6f9a489b23f1cf53ed4611af702e5d6fd04620", "initCodeHash": "0xbb425bd1c3cad13a77f5c9676b577606e2f8f320687739f529b257a042f58d85",
"sourceCodeHash": "0x115bd6a4c4d77ed210dfd468675b409fdae9f79b932063c138f0765ba9063462" "sourceCodeHash": "0xe66f19942947f53ccd658b94c1ef6db39e947419d4ec7436067c6cc44452ff73"
}, },
"src/cannon/PreimageOracle.sol": { "src/cannon/PreimageOracle.sol": {
"initCodeHash": "0x967d730bb1b10a36ee625179734cccd6b6826ce480bad0419272663c460603bd", "initCodeHash": "0x967d730bb1b10a36ee625179734cccd6b6826ce480bad0419272663c460603bd",
......
...@@ -44,8 +44,8 @@ contract MIPS is ISemver { ...@@ -44,8 +44,8 @@ contract MIPS is ISemver {
} }
/// @notice The semantic version of the MIPS contract. /// @notice The semantic version of the MIPS contract.
/// @custom:semver 1.0.1 /// @custom:semver 1.1.0-beta.7
string public constant version = "1.1.0-beta.6"; string public constant version = "1.1.0-beta.7";
/// @notice The preimage oracle contract. /// @notice The preimage oracle contract.
IPreimageOracle internal immutable ORACLE; IPreimageOracle internal immutable ORACLE;
...@@ -157,7 +157,7 @@ contract MIPS is ISemver { ...@@ -157,7 +157,7 @@ contract MIPS is ISemver {
(v0, v1, state.heap) = sys.handleSysMmap(a0, a1, state.heap); (v0, v1, state.heap) = sys.handleSysMmap(a0, a1, state.heap);
} else if (syscall_no == sys.SYS_BRK) { } else if (syscall_no == sys.SYS_BRK) {
// brk: Returns a fixed address for the program break at 0x40000000 // brk: Returns a fixed address for the program break at 0x40000000
v0 = sys.BRK_START; v0 = sys.PROGRAM_BREAK;
} else if (syscall_no == sys.SYS_CLONE) { } else if (syscall_no == sys.SYS_CLONE) {
// clone (not supported) returns 1 // clone (not supported) returns 1
v0 = 1; v0 = 1;
......
...@@ -51,8 +51,8 @@ contract MIPS2 is ISemver { ...@@ -51,8 +51,8 @@ contract MIPS2 is ISemver {
} }
/// @notice The semantic version of the MIPS2 contract. /// @notice The semantic version of the MIPS2 contract.
/// @custom:semver 0.0.2-beta /// @custom:semver 1.0.0-beta.4
string public constant version = "1.0.0-beta.3"; string public constant version = "1.0.0-beta.4";
/// @notice The preimage oracle contract. /// @notice The preimage oracle contract.
IPreimageOracle internal immutable ORACLE; IPreimageOracle internal immutable ORACLE;
...@@ -256,7 +256,7 @@ contract MIPS2 is ISemver { ...@@ -256,7 +256,7 @@ contract MIPS2 is ISemver {
(v0, v1, state.heap) = sys.handleSysMmap(a0, a1, state.heap); (v0, v1, state.heap) = sys.handleSysMmap(a0, a1, state.heap);
} else if (syscall_no == sys.SYS_BRK) { } else if (syscall_no == sys.SYS_BRK) {
// brk: Returns a fixed address for the program break at 0x40000000 // brk: Returns a fixed address for the program break at 0x40000000
v0 = sys.BRK_START; v0 = sys.PROGRAM_BREAK;
} else if (syscall_no == sys.SYS_CLONE) { } else if (syscall_no == sys.SYS_CLONE) {
if (sys.VALID_SYS_CLONE_FLAGS != a0) { if (sys.VALID_SYS_CLONE_FLAGS != a0) {
state.exited = true; state.exited = true;
......
...@@ -99,7 +99,8 @@ library MIPSSyscalls { ...@@ -99,7 +99,8 @@ library MIPSSyscalls {
uint32 internal constant SCHED_QUANTUM = 100_000; uint32 internal constant SCHED_QUANTUM = 100_000;
/// @notice Start of the data segment. /// @notice Start of the data segment.
uint32 internal constant BRK_START = 0x40000000; uint32 internal constant PROGRAM_BREAK = 0x40000000;
uint32 internal constant HEAP_END = 0x60000000;
// SYS_CLONE flags // SYS_CLONE flags
uint32 internal constant CLONE_VM = 0x100; uint32 internal constant CLONE_VM = 0x100;
...@@ -175,6 +176,12 @@ library MIPSSyscalls { ...@@ -175,6 +176,12 @@ library MIPSSyscalls {
if (_a0 == 0) { if (_a0 == 0) {
v0_ = _heap; v0_ = _heap;
newHeap_ += sz; newHeap_ += sz;
// Fail if new heap exceeds memory limit, newHeap overflows to low memory, or sz overflows
if (newHeap_ > HEAP_END || newHeap_ < _heap || sz < _a1) {
v0_ = SYS_ERROR_SIGNAL;
v1_ = EINVAL;
return (v0_, v1_, _heap);
}
} else { } else {
v0_ = _a0; v0_ = _a0;
} }
......
...@@ -5,6 +5,7 @@ import { CommonTest } from "test/setup/CommonTest.sol"; ...@@ -5,6 +5,7 @@ import { CommonTest } from "test/setup/CommonTest.sol";
import { MIPS } from "src/cannon/MIPS.sol"; import { MIPS } from "src/cannon/MIPS.sol";
import { PreimageOracle } from "src/cannon/PreimageOracle.sol"; import { PreimageOracle } from "src/cannon/PreimageOracle.sol";
import { MIPSInstructions } from "src/cannon/libraries/MIPSInstructions.sol"; import { MIPSInstructions } from "src/cannon/libraries/MIPSInstructions.sol";
import { MIPSSyscalls as sys } from "src/cannon/libraries/MIPSSyscalls.sol";
import { InvalidExitedValue } from "src/cannon/libraries/CannonErrors.sol"; import { InvalidExitedValue } from "src/cannon/libraries/CannonErrors.sol";
import "src/dispute/lib/Types.sol"; import "src/dispute/lib/Types.sol";
...@@ -1483,6 +1484,64 @@ contract MIPS_Test is CommonTest { ...@@ -1483,6 +1484,64 @@ contract MIPS_Test is CommonTest {
assertEq(postState, outputState(expect), "unexpected post state"); assertEq(postState, outputState(expect), "unexpected post state");
} }
function test_mmap_succeeds_justWithinMemLimit() external {
uint32 insn = 0x0000000c; // syscall
(bytes32 memRoot, bytes memory proof) = ffi.getCannonMemoryProof(0, insn);
MIPS.State memory state;
state.memRoot = memRoot;
state.nextPC = 4;
state.heap = sys.HEAP_END - 4096; // Set up to increase heap to its limit
state.registers[2] = 4090; // mmap syscall
state.registers[4] = 0x0; // a0
state.registers[5] = 4095; // a1
bytes memory encodedState = encodeState(state);
MIPS.State memory expect;
expect.memRoot = state.memRoot;
// assert page allocation is aligned to 4k
expect.step = state.step + 1;
expect.pc = state.nextPC;
expect.nextPC = state.nextPC + 4;
expect.heap = sys.HEAP_END;
expect.registers[2] = state.heap; // Return the old heap value
expect.registers[7] = 0; // No error
expect.registers[4] = state.registers[4]; // a0
expect.registers[5] = state.registers[5]; // a1
bytes32 postState = mips.step(encodedState, proof, 0);
assertEq(postState, outputState(expect), "unexpected post state");
}
function test_mmap_fails() external {
uint32 insn = 0x0000000c; // syscall
(bytes32 memRoot, bytes memory proof) = ffi.getCannonMemoryProof(0, insn);
MIPS.State memory state;
state.memRoot = memRoot;
state.nextPC = 4;
state.heap = sys.HEAP_END - 4096; // Set up to increase heap beyond its limit
state.registers[2] = 4090; // mmap syscall
state.registers[4] = 0x0; // a0
state.registers[5] = 4097; // a1
bytes memory encodedState = encodeState(state);
MIPS.State memory expect;
expect.memRoot = state.memRoot;
// assert page allocation is aligned to 4k
expect.step = state.step + 1;
expect.pc = state.nextPC;
expect.nextPC = state.nextPC + 4;
expect.heap = state.heap;
expect.registers[2] = sys.SYS_ERROR_SIGNAL; // signal an stdError
expect.registers[7] = sys.EINVAL; // Return error value
expect.registers[4] = state.registers[4]; // a0
expect.registers[5] = state.registers[5]; // a1
bytes32 postState = mips.step(encodedState, proof, 0);
assertEq(postState, outputState(expect), "unexpected post state");
}
function test_brk_succeeds() external { function test_brk_succeeds() external {
uint32 insn = 0x0000000c; // syscall uint32 insn = 0x0000000c; // syscall
(MIPS.State memory state, bytes memory proof) = constructMIPSState(0, insn, 0x4, 0); (MIPS.State memory state, bytes memory proof) = constructMIPSState(0, insn, 0x4, 0);
......
...@@ -27,7 +27,7 @@ contract DeploymentSummary is DeploymentSummaryCode { ...@@ -27,7 +27,7 @@ contract DeploymentSummary is DeploymentSummaryCode {
address internal constant l1StandardBridgeProxyAddress = 0x20A42a5a785622c6Ba2576B2D6e924aA82BFA11D; address internal constant l1StandardBridgeProxyAddress = 0x20A42a5a785622c6Ba2576B2D6e924aA82BFA11D;
address internal constant l2OutputOracleAddress = 0x19652082F846171168Daf378C4fD3ee85a0D4A60; address internal constant l2OutputOracleAddress = 0x19652082F846171168Daf378C4fD3ee85a0D4A60;
address internal constant l2OutputOracleProxyAddress = 0x39Af23E00F1e662025aA01b0cEdA19542B78DF99; address internal constant l2OutputOracleProxyAddress = 0x39Af23E00F1e662025aA01b0cEdA19542B78DF99;
address internal constant mipsAddress = 0xF796d8522b9B3E67724b185C0876736C8D3df6bf; address internal constant mipsAddress = 0x59272D1cd178Ee40F8a05AA6aEF87e9faeD718DC;
address internal constant optimismMintableERC20FactoryAddress = 0x39Aea2Dd53f2d01c15877aCc2791af6BDD7aD567; address internal constant optimismMintableERC20FactoryAddress = 0x39Aea2Dd53f2d01c15877aCc2791af6BDD7aD567;
address internal constant optimismMintableERC20FactoryProxyAddress = 0xc7B87b2b892EA5C3CfF47168881FE168C00377FB; address internal constant optimismMintableERC20FactoryProxyAddress = 0xc7B87b2b892EA5C3CfF47168881FE168C00377FB;
address internal constant optimismPortalAddress = 0xbdD90485FCbcac869D5b5752179815a3103d8131; address internal constant optimismPortalAddress = 0xbdD90485FCbcac869D5b5752179815a3103d8131;
......
This source diff could not be displayed because it is too large. You can view the blob instead.
...@@ -27,7 +27,7 @@ contract DeploymentSummaryFaultProofs is DeploymentSummaryFaultProofsCode { ...@@ -27,7 +27,7 @@ contract DeploymentSummaryFaultProofs is DeploymentSummaryFaultProofsCode {
address internal constant l1StandardBridgeProxyAddress = 0x20A42a5a785622c6Ba2576B2D6e924aA82BFA11D; address internal constant l1StandardBridgeProxyAddress = 0x20A42a5a785622c6Ba2576B2D6e924aA82BFA11D;
address internal constant l2OutputOracleAddress = 0x19652082F846171168Daf378C4fD3ee85a0D4A60; address internal constant l2OutputOracleAddress = 0x19652082F846171168Daf378C4fD3ee85a0D4A60;
address internal constant l2OutputOracleProxyAddress = 0x39Af23E00F1e662025aA01b0cEdA19542B78DF99; address internal constant l2OutputOracleProxyAddress = 0x39Af23E00F1e662025aA01b0cEdA19542B78DF99;
address internal constant mipsAddress = 0xF796d8522b9B3E67724b185C0876736C8D3df6bf; address internal constant mipsAddress = 0x59272D1cd178Ee40F8a05AA6aEF87e9faeD718DC;
address internal constant optimismMintableERC20FactoryAddress = 0x39Aea2Dd53f2d01c15877aCc2791af6BDD7aD567; address internal constant optimismMintableERC20FactoryAddress = 0x39Aea2Dd53f2d01c15877aCc2791af6BDD7aD567;
address internal constant optimismMintableERC20FactoryProxyAddress = 0xc7B87b2b892EA5C3CfF47168881FE168C00377FB; address internal constant optimismMintableERC20FactoryProxyAddress = 0xc7B87b2b892EA5C3CfF47168881FE168C00377FB;
address internal constant optimismPortalAddress = 0xbdD90485FCbcac869D5b5752179815a3103d8131; address internal constant optimismPortalAddress = 0xbdD90485FCbcac869D5b5752179815a3103d8131;
......
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