From 90582faacd2b0355d17afc43c7a4e38562fea385 Mon Sep 17 00:00:00 2001 From: Roman Khimov Date: Tue, 15 Nov 2022 23:48:02 +0300 Subject: [PATCH 1/2] vm: save current stack slice when loading new context v.estack is used throughout the code to work with estack, while ctx.sc.estack is (theoretically) just a reference to it that is saved on script load and restored to v.estack on context unload. The problem is that v.estack can grow as we use it and can be reallocated away from its original slice (saved in the ctx.sc.estack), so either ctx.sc.estack should be a pointer or we need to ensure that it's correct when loading a new script. The second approach is a bit safer for now and it fixes #2798. --- pkg/vm/vm.go | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/pkg/vm/vm.go b/pkg/vm/vm.go index 3a348d530..5e672ccd5 100644 --- a/pkg/vm/vm.go +++ b/pkg/vm/vm.go @@ -335,18 +335,19 @@ func (v *VM) loadScriptWithCallingHash(b []byte, exe *nef.File, caller util.Uint hash util.Uint160, f callflag.CallFlag, rvcount int, offset int, onContextUnload ContextUnloadCallback) { v.checkInvocationStackSize() ctx := NewContextWithParams(b, rvcount, offset) + parent := v.Context() + if parent != nil { + ctx.sc.callingContext = parent.sc + parent.sc.estack = v.estack + } if rvcount != -1 || v.estack.Len() != 0 { v.estack = subStack(v.estack) } - parent := v.Context() ctx.sc.estack = v.estack initStack(&ctx.tryStack, "exception", nil) ctx.sc.callFlag = f ctx.sc.scriptHash = hash ctx.sc.callingScriptHash = caller - if parent != nil { - ctx.sc.callingContext = parent.sc - } ctx.sc.NEF = exe if v.invTree != nil { curTree := v.invTree From aef01bf6637f782479b289dfa555e2e3ac1e845b Mon Sep 17 00:00:00 2001 From: Roman Khimov Date: Wed, 16 Nov 2022 00:40:12 +0300 Subject: [PATCH 2/2] vm: fix istack marshaling, fix #2799 --- pkg/vm/context.go | 18 ++++++++++++++++++ pkg/vm/vm.go | 3 ++- 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/pkg/vm/context.go b/pkg/vm/context.go index 6c086249f..cc16ac867 100644 --- a/pkg/vm/context.go +++ b/pkg/vm/context.go @@ -72,6 +72,13 @@ type Context struct { retCount int } +type contextAux struct { + Script string + IP int + NextIP int + Caller string +} + // ContextUnloadCallback is a callback method used on context unloading from istack. type ContextUnloadCallback func(ctx *Context, commit bool) error @@ -357,3 +364,14 @@ func (c *Context) HasTryBlock() bool { } return false } + +// MarshalJSON implements the JSON marshalling interface. +func (c *Context) MarshalJSON() ([]byte, error) { + var aux = contextAux{ + Script: c.ScriptHash().StringLE(), + IP: c.ip, + NextIP: c.nextip, + Caller: c.sc.callingScriptHash.StringLE(), + } + return json.Marshal(aux) +} diff --git a/pkg/vm/vm.go b/pkg/vm/vm.go index 5e672ccd5..7cc768d0f 100644 --- a/pkg/vm/vm.go +++ b/pkg/vm/vm.go @@ -382,7 +382,8 @@ func (v *VM) PopResult() interface{} { // DumpIStack returns json formatted representation of the invocation stack. func (v *VM) DumpIStack() string { - return dumpStack(&v.istack) + b, _ := json.MarshalIndent(v.istack.ToArray(), "", " ") + return string(b) } // DumpEStack returns json formatted representation of the execution stack.