mirror of
https://github.com/nspcc-dev/neo-go.git
synced 2024-11-27 03:58:06 +00:00
Merge pull request #461 from nspcc-dev/feature/stack_limits
vm: restrict max invocation stack size
This commit is contained in:
commit
9a86b2dc62
2 changed files with 42 additions and 2 deletions
21
pkg/vm/vm.go
21
pkg/vm/vm.go
|
@ -41,6 +41,9 @@ const (
|
||||||
// MaxItemSize is the maximum item size allowed in the VM.
|
// MaxItemSize is the maximum item size allowed in the VM.
|
||||||
MaxItemSize = 1024 * 1024
|
MaxItemSize = 1024 * 1024
|
||||||
|
|
||||||
|
// MaxInvocationStackSize is the maximum size of an invocation stack.
|
||||||
|
MaxInvocationStackSize = 1024
|
||||||
|
|
||||||
maxSHLArg = 256
|
maxSHLArg = 256
|
||||||
minSHLArg = -256
|
minSHLArg = -256
|
||||||
)
|
)
|
||||||
|
@ -995,6 +998,8 @@ func (v *VM) execute(ctx *Context, op Instruction, parameter []byte) (err error)
|
||||||
}
|
}
|
||||||
|
|
||||||
case CALL:
|
case CALL:
|
||||||
|
v.checkInvocationStackSize()
|
||||||
|
|
||||||
newCtx := ctx.Copy()
|
newCtx := ctx.Copy()
|
||||||
newCtx.rvcount = -1
|
newCtx.rvcount = -1
|
||||||
v.istack.PushVal(newCtx)
|
v.istack.PushVal(newCtx)
|
||||||
|
@ -1017,6 +1022,10 @@ func (v *VM) execute(ctx *Context, op Instruction, parameter []byte) (err error)
|
||||||
panic("no getScript callback is set up")
|
panic("no getScript callback is set up")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if op == APPCALL {
|
||||||
|
v.checkInvocationStackSize()
|
||||||
|
}
|
||||||
|
|
||||||
hash, err := util.Uint160DecodeBytes(parameter)
|
hash, err := util.Uint160DecodeBytes(parameter)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
|
@ -1227,9 +1236,13 @@ func (v *VM) execute(ctx *Context, op Instruction, parameter []byte) (err error)
|
||||||
if v.estack.Len() < pcount+addElement {
|
if v.estack.Len() < pcount+addElement {
|
||||||
panic("missing some parameters")
|
panic("missing some parameters")
|
||||||
}
|
}
|
||||||
if tailCall && ctx.rvcount != rvcount {
|
if tailCall {
|
||||||
|
if ctx.rvcount != rvcount {
|
||||||
panic("context and parameter rvcount mismatch")
|
panic("context and parameter rvcount mismatch")
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
v.checkInvocationStackSize()
|
||||||
|
}
|
||||||
|
|
||||||
if op == CALLI {
|
if op == CALLI {
|
||||||
newCtx = ctx.Copy()
|
newCtx = ctx.Copy()
|
||||||
|
@ -1313,3 +1326,9 @@ func validateMapKey(key *Element) {
|
||||||
panic("key can't be a collection")
|
panic("key can't be a collection")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (v *VM) checkInvocationStackSize() {
|
||||||
|
if v.istack.len >= MaxInvocationStackSize {
|
||||||
|
panic("invocation stack is too big")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -197,6 +197,27 @@ func TestPushData4Good(t *testing.T) {
|
||||||
assert.Equal(t, []byte{1, 2, 3}, vm.estack.Pop().Bytes())
|
assert.Equal(t, []byte{1, 2, 3}, vm.estack.Pop().Bytes())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func callNTimes(n uint16) []byte {
|
||||||
|
return makeProgram(
|
||||||
|
PUSHBYTES2, Instruction(n), Instruction(n>>8), // little-endian
|
||||||
|
TOALTSTACK, DUPFROMALTSTACK,
|
||||||
|
JMPIF, 0x4, 0, RET,
|
||||||
|
FROMALTSTACK, DEC,
|
||||||
|
CALL, 0xF8, 0xFF) // -8 -> JMP to TOALTSTACK)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestInvocationLimitGood(t *testing.T) {
|
||||||
|
prog := callNTimes(MaxInvocationStackSize - 1)
|
||||||
|
v := load(prog)
|
||||||
|
runVM(t, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestInvocationLimitBad(t *testing.T) {
|
||||||
|
prog := callNTimes(MaxInvocationStackSize)
|
||||||
|
v := load(prog)
|
||||||
|
checkVMFailed(t, v)
|
||||||
|
}
|
||||||
|
|
||||||
func TestNOTNoArgument(t *testing.T) {
|
func TestNOTNoArgument(t *testing.T) {
|
||||||
prog := makeProgram(NOT)
|
prog := makeProgram(NOT)
|
||||||
vm := load(prog)
|
vm := load(prog)
|
||||||
|
|
Loading…
Reference in a new issue