vm: restrict max invocation stack size

This commit is contained in:
Evgenii Stratonikov 2019-10-29 11:01:06 +03:00
parent 0ea7568caa
commit c7f0b7bd68
2 changed files with 42 additions and 2 deletions

View file

@ -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")
}
}

View file

@ -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)