diff --git a/pkg/vm/vm.go b/pkg/vm/vm.go index 3592291a9..a523642d5 100644 --- a/pkg/vm/vm.go +++ b/pkg/vm/vm.go @@ -318,18 +318,22 @@ func (v *VM) execute(ctx *Context, op Instruction) { v.estack.PushVal(ab) case SUBSTR: l := int(v.estack.Pop().BigInt().Int64()) - o := int(v.estack.Pop().BigInt().Int64()) - s := v.estack.Pop().Bytes() if l < 0 { panic("negative length") } + o := int(v.estack.Pop().BigInt().Int64()) if o < 0 { panic("negative index") } - if l+o > len(s) { - panic("out of bounds access") + s := v.estack.Pop().Bytes() + if o > len(s) { + panic("invalid offset") } - v.estack.PushVal(s[o : o+l]) + last := l + o + if last > len(s) { + last = len(s) + } + v.estack.PushVal(s[o:last]) case LEFT: l := int(v.estack.Pop().BigInt().Int64()) if l < 0 { diff --git a/pkg/vm/vm_test.go b/pkg/vm/vm_test.go index 9d8e1ca0f..3856d9d2a 100644 --- a/pkg/vm/vm_test.go +++ b/pkg/vm/vm_test.go @@ -1138,20 +1138,22 @@ func TestSUBSTRBadOffset(t *testing.T) { prog := makeProgram(SUBSTR) vm := load(prog) vm.estack.PushVal([]byte("abcdef")) - vm.estack.PushVal(6) + vm.estack.PushVal(7) vm.estack.PushVal(1) vm.Run() assert.Equal(t, true, vm.state.HasFlag(faultState)) } -func TestSUBSTRBadLen(t *testing.T) { +func TestSUBSTRBigLen(t *testing.T) { prog := makeProgram(SUBSTR) vm := load(prog) vm.estack.PushVal([]byte("abcdef")) vm.estack.PushVal(1) vm.estack.PushVal(6) vm.Run() - assert.Equal(t, true, vm.state.HasFlag(faultState)) + assert.Equal(t, false, vm.state.HasFlag(faultState)) + assert.Equal(t, 1, vm.estack.Len()) + assert.Equal(t, []byte("bcdef"), vm.estack.Pop().Bytes()) } func TestSUBSTRBad387(t *testing.T) { @@ -1163,7 +1165,9 @@ func TestSUBSTRBad387(t *testing.T) { vm.estack.PushVal(1) vm.estack.PushVal(6) vm.Run() - assert.Equal(t, true, vm.state.HasFlag(faultState)) + assert.Equal(t, false, vm.state.HasFlag(faultState)) + assert.Equal(t, 1, vm.estack.Len()) + assert.Equal(t, []byte("bcdef"), vm.estack.Pop().Bytes()) } func TestSUBSTRBadNegativeOffset(t *testing.T) {