Merge pull request #461 from nspcc-dev/feature/stack_limits

vm: restrict max invocation stack size
This commit is contained in:
Roman Khimov 2019-10-29 15:49:43 +03:00 committed by GitHub
commit 9a86b2dc62
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
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 = 1024 * 1024
// MaxInvocationStackSize is the maximum size of an invocation stack.
MaxInvocationStackSize = 1024
maxSHLArg = 256
minSHLArg = -256
)
@ -995,6 +998,8 @@ func (v *VM) execute(ctx *Context, op Instruction, parameter []byte) (err error)
}
case CALL:
v.checkInvocationStackSize()
newCtx := ctx.Copy()
newCtx.rvcount = -1
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")
}
if op == APPCALL {
v.checkInvocationStackSize()
}
hash, err := util.Uint160DecodeBytes(parameter)
if err != nil {
panic(err)
@ -1227,9 +1236,13 @@ func (v *VM) execute(ctx *Context, op Instruction, parameter []byte) (err error)
if v.estack.Len() < pcount+addElement {
panic("missing some parameters")
}
if tailCall && ctx.rvcount != rvcount {
if tailCall {
if ctx.rvcount != rvcount {
panic("context and parameter rvcount mismatch")
}
} else {
v.checkInvocationStackSize()
}
if op == CALLI {
newCtx = ctx.Copy()
@ -1313,3 +1326,9 @@ func validateMapKey(key *Element) {
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())
}
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) {
prog := makeProgram(NOT)
vm := load(prog)