vm: avoid panic in IsScriptCorrect

Signed-off-by: Evgeniy Stratonikov <evgeniy@nspcc.ru>
This commit is contained in:
Evgeniy Stratonikov 2022-03-17 20:08:24 +03:00
parent f056c9aea8
commit ef28308dbf
2 changed files with 36 additions and 6 deletions

View file

@ -160,23 +160,31 @@ func IsScriptCorrect(script []byte, methods bitfield.Field) error {
opcode.JMPIFNOTL, opcode.JMPEQL, opcode.JMPNEL,
opcode.JMPGTL, opcode.JMPGEL, opcode.JMPLTL, opcode.JMPLEL,
opcode.ENDTRYL, opcode.CALLL, opcode.PUSHA:
off, _, err := calcJumpOffset(ctx, param) // It does bounds checking.
off, _, err := calcJumpOffset(ctx, param)
if err != nil {
return err
}
jumps.Set(off)
// `calcJumpOffset` does bounds checking but can return `len(script)`.
// This check avoids panic in bitset when script length is a multiple of 64.
if off != len(script) {
jumps.Set(off)
}
case opcode.TRY, opcode.TRYL:
catchP, finallyP := getTryParams(op, param)
off, _, err := calcJumpOffset(ctx, catchP)
if err != nil {
return err
}
jumps.Set(off)
if off != len(script) {
jumps.Set(off)
}
off, _, err = calcJumpOffset(ctx, finallyP)
if err != nil {
return err
}
jumps.Set(off)
if off != len(script) {
jumps.Set(off)
}
case opcode.NEWARRAYT, opcode.ISTYPE, opcode.CONVERT:
typ := stackitem.Type(param[0])
if !typ.IsValid() {

View file

@ -191,13 +191,20 @@ func TestIsScriptCorrect(t *testing.T) {
t.Run("out of bounds JMPL 1", func(t *testing.T) {
bad := getScript()
bad[jmplOff+1] = byte(len(bad) - jmplOff)
bad[jmplOff+1] = byte(len(bad)-jmplOff) + 1
bad[jmplOff+2] = 0
bad[jmplOff+3] = 0
bad[jmplOff+4] = 0
require.Error(t, IsScriptCorrect(bad, nil))
})
t.Run("JMP to a len(script)", func(t *testing.T) {
bad := make([]byte, 64) // 64 is the word-size of a bitset.
bad[0] = byte(opcode.JMP)
bad[1] = 64
require.NoError(t, IsScriptCorrect(bad, nil))
})
t.Run("bad JMPL offset", func(t *testing.T) {
bad := getScript()
bad[jmplOff+1] = 0xfe // into JMP
@ -212,10 +219,25 @@ func TestIsScriptCorrect(t *testing.T) {
t.Run("out of bounds TRY 2", func(t *testing.T) {
bad := getScript()
bad[tryOff+2] = byte(len(bad) - tryOff)
bad[tryOff+1] = byte(len(bad)-tryOff) + 1
require.Error(t, IsScriptCorrect(bad, nil))
})
t.Run("out of bounds TRY 2", func(t *testing.T) {
bad := getScript()
bad[tryOff+2] = byte(len(bad)-tryOff) + 1
require.Error(t, IsScriptCorrect(bad, nil))
})
t.Run("TRY with jumps to a len(script)", func(t *testing.T) {
bad := make([]byte, 64) // 64 is the word-size of a bitset.
bad[0] = byte(opcode.TRY)
bad[1] = 64
bad[2] = 64
bad[3] = byte(opcode.RET) // pad so that remaining script (PUSHINT8 0) is even in length.
require.NoError(t, IsScriptCorrect(bad, nil))
})
t.Run("bad TRYL offset 1", func(t *testing.T) {
bad := getScript()
bad[trylOff+1] = byte(-(trylOff - jmpOff) - 1) // into "something"