Commit 0dcccf6a authored by Inphi's avatar Inphi Committed by GitHub

cannon: Fix stack patching (#11632)

* cannon: Fix stack patching

And add `memprofilerate=0` to envp

* Update cannon/mipsevm/program/patch.go
Co-authored-by: default avatarprotolambda <proto@protolambda.com>

* cleanup argv/envp string ptrs

* nit

* fix envar name

* Update cannon/mipsevm/program/patch.go
Co-authored-by: default avatarmbaxter <meredith@oplabs.co>

* align op-program arg0

---------
Co-authored-by: default avatarprotolambda <proto@protolambda.com>
Co-authored-by: default avatarmbaxter <meredith@oplabs.co>
parent 36f093a1
...@@ -47,16 +47,11 @@ func PatchGo(f *elf.File, st mipsevm.FPVMState) error { ...@@ -47,16 +47,11 @@ func PatchGo(f *elf.File, st mipsevm.FPVMState) error {
})); err != nil { })); err != nil {
return fmt.Errorf("failed to patch Go runtime.gcenable: %w", err) return fmt.Errorf("failed to patch Go runtime.gcenable: %w", err)
} }
case "runtime.MemProfileRate":
if err := st.GetMemory().SetMemoryRange(uint32(s.Value), bytes.NewReader(make([]byte, 4))); err != nil { // disable mem profiling, to avoid a lot of unnecessary floating point ops
return err
}
} }
} }
return nil return nil
} }
// TODO(cp-903) Consider setting envar "GODEBUG=memprofilerate=0" for go programs to disable memprofiling, instead of patching it out in PatchGo()
func PatchStack(st mipsevm.FPVMState) error { func PatchStack(st mipsevm.FPVMState) error {
// setup stack pointer // setup stack pointer
sp := uint32(0x7f_ff_d0_00) sp := uint32(0x7f_ff_d0_00)
...@@ -73,16 +68,27 @@ func PatchStack(st mipsevm.FPVMState) error { ...@@ -73,16 +68,27 @@ func PatchStack(st mipsevm.FPVMState) error {
} }
// init argc, argv, aux on stack // init argc, argv, aux on stack
storeMem(sp+4*1, 0x42) // argc = 0 (argument count) storeMem(sp+4*0, 1) // argc = 1 (argument count)
storeMem(sp+4*2, 0x35) // argv[n] = 0 (terminating argv) storeMem(sp+4*1, sp+4*21) // argv[0]
storeMem(sp+4*3, 0) // envp[term] = 0 (no env vars) storeMem(sp+4*2, 0) // argv[1] = terminating
storeMem(sp+4*4, 6) // auxv[0] = _AT_PAGESZ = 6 (key) storeMem(sp+4*3, sp+4*14) // envp[0] = x (offset to first env var)
storeMem(sp+4*5, 4096) // auxv[1] = page size of 4 KiB (value) - (== minPhysPageSize) storeMem(sp+4*4, 0) // envp[1] = terminating
storeMem(sp+4*6, 25) // auxv[2] = AT_RANDOM storeMem(sp+4*5, 6) // auxv[0] = _AT_PAGESZ = 6 (key)
storeMem(sp+4*7, sp+4*9) // auxv[3] = address of 16 bytes containing random value storeMem(sp+4*6, 4096) // auxv[1] = page size of 4 KiB (value) - (== minPhysPageSize)
storeMem(sp+4*8, 0) // auxv[term] = 0 storeMem(sp+4*7, 25) // auxv[2] = AT_RANDOM
storeMem(sp+4*8, sp+4*10) // auxv[3] = address of 16 bytes containing random value
storeMem(sp+4*9, 0) // auxv[term] = 0
_ = st.GetMemory().SetMemoryRange(sp+4*10, bytes.NewReader([]byte("4;byfairdiceroll"))) // 16 bytes of "randomness"
// append 4 extra zero bytes to end at 4-byte alignment
envar := append([]byte("GODEBUG=memprofilerate=0"), 0x0, 0x0, 0x0, 0x0)
_ = st.GetMemory().SetMemoryRange(sp+4*14, bytes.NewReader(envar))
_ = st.GetMemory().SetMemoryRange(sp+4*9, bytes.NewReader([]byte("4;byfairdiceroll"))) // 16 bytes of "randomness" // 24 bytes for GODEBUG=memprofilerate=0 + 4 null bytes
// Then append program name + 2 null bytes for 4-byte alignment
programName := append([]byte("op-program"), 0x0, 0x0)
_ = st.GetMemory().SetMemoryRange(sp+4*21, bytes.NewReader(programName))
return nil return nil
} }
...@@ -591,3 +591,47 @@ func TestClaimEVM(t *testing.T) { ...@@ -591,3 +591,47 @@ func TestClaimEVM(t *testing.T) {
}) })
} }
} }
func TestEntryEVM(t *testing.T) {
var tracer *tracing.Hooks // no-tracer by default, but see test_util.MarkdownTracer
versions := GetMipsVersionTestCases(t)
for _, v := range versions {
t.Run(v.Name, func(t *testing.T) {
evm := testutil.NewMIPSEVM(v.Contracts)
evm.SetTracer(tracer)
testutil.LogStepFailureAtCleanup(t, evm)
var stdOutBuf, stdErrBuf bytes.Buffer
elfFile := "../../testdata/example/bin/entry.elf"
goVm := v.ElfVMFactory(t, elfFile, nil, io.MultiWriter(&stdOutBuf, os.Stdout), io.MultiWriter(&stdErrBuf, os.Stderr), testutil.CreateLogger())
state := goVm.GetState()
start := time.Now()
for i := 0; i < 400_000; i++ {
curStep := goVm.GetState().GetStep()
if goVm.GetState().GetExited() {
break
}
insn := state.GetMemory().GetMemory(state.GetPC())
if i%10_000 == 0 { // avoid spamming test logs, we are executing many steps
t.Logf("step: %4d pc: 0x%08x insn: 0x%08x", state.GetStep(), state.GetPC(), insn)
}
stepWitness, err := goVm.Step(true)
require.NoError(t, err)
evmPost := evm.Step(t, stepWitness, curStep, v.StateHashFn)
// verify the post-state matches.
goPost, _ := goVm.GetState().EncodeWitness()
require.Equal(t, hexutil.Bytes(goPost).String(), hexutil.Bytes(evmPost).String(),
"mipsevm produced different state than EVM")
}
end := time.Now()
delta := end.Sub(start)
t.Logf("test took %s, %d instructions, %s per instruction", delta, state.GetStep(), delta/time.Duration(state.GetStep()))
require.True(t, state.GetExited(), "must complete program")
require.Equal(t, uint8(0), state.GetExitCode(), "exit with 0")
})
}
}
package main
import (
"os"
"runtime"
)
func main() {
if len(os.Args) != 1 {
panic("expected 1 arg")
}
if os.Args[0] != "op-program" {
panic("unexpected arg0")
}
var memProfileRate bool
env := os.Environ()
for _, env := range env {
if env != "GODEBUG=memprofilerate=0" {
panic("invalid envar")
}
memProfileRate = true
}
if !memProfileRate {
panic("memProfileRate env is not set")
}
if runtime.MemProfileRate != 0 {
panic("runtime.MemProfileRate is non-zero")
}
}
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