vm: protect PUSHDATA from short reads

Same thing done in a2a8981979 for PUSHBYTES,
failing to read the amount of bytes specified should lead to FAULT. Also
makes readUint16() and readUint32() panic as this is the behavior we want in
these cases. Add some tests along the way.
This commit is contained in:
Roman Khimov 2019-09-11 11:52:39 +03:00
parent 14c02cb3b1
commit fc1075bf75
3 changed files with 87 additions and 7 deletions

View file

@ -89,7 +89,7 @@ func (c *Context) String() string {
func (c *Context) readUint32() uint32 {
start, end := c.IP(), c.IP()+4
if end > len(c.prog) {
return 0
panic("failed to read uint32 parameter")
}
val := binary.LittleEndian.Uint32(c.prog[start:end])
c.ip += 4
@ -99,7 +99,7 @@ func (c *Context) readUint32() uint32 {
func (c *Context) readUint16() uint16 {
start, end := c.IP(), c.IP()+2
if end > len(c.prog) {
return 0
panic("failed to read uint16 parameter")
}
val := binary.LittleEndian.Uint16(c.prog[start:end])
c.ip += 2

View file

@ -262,16 +262,25 @@ func (v *VM) execute(ctx *Context, op Instruction) {
case PUSHDATA1:
n := ctx.readByte()
b := ctx.readBytes(int(n))
if b == nil {
panic("failed to read instruction parameter")
}
v.estack.PushVal(b)
case PUSHDATA2:
n := ctx.readUint16()
b := ctx.readBytes(int(n))
if b == nil {
panic("failed to read instruction parameter")
}
v.estack.PushVal(b)
case PUSHDATA4:
n := ctx.readUint32()
b := ctx.readBytes(int(n))
if b == nil {
panic("failed to read instruction parameter")
}
v.estack.PushVal(b)
// Stack operations.

View file

@ -99,16 +99,87 @@ func TestPushm1to16(t *testing.T) {
}
}
func TestPushData1(t *testing.T) {
func TestPushData1BadNoN(t *testing.T) {
prog := []byte{byte(PUSHDATA1)}
vm := load(prog)
vm.Run()
assert.Equal(t, true, vm.state.HasFlag(faultState))
}
func TestPushData2(t *testing.T) {
func TestPushData1BadN(t *testing.T) {
prog := []byte{byte(PUSHDATA1), 1}
vm := load(prog)
vm.Run()
assert.Equal(t, true, vm.state.HasFlag(faultState))
}
func TestPushData4(t *testing.T) {
func TestPushData1Good(t *testing.T) {
prog := makeProgram(PUSHDATA1, 3, 1, 2, 3)
vm := load(prog)
vm.Run()
assert.Equal(t, false, vm.state.HasFlag(faultState))
assert.Equal(t, 1, vm.estack.Len())
assert.Equal(t, []byte{1, 2, 3}, vm.estack.Pop().Bytes())
}
func TestPushData2BadNoN(t *testing.T) {
prog := []byte{byte(PUSHDATA2)}
vm := load(prog)
vm.Run()
assert.Equal(t, true, vm.state.HasFlag(faultState))
}
func TestPushData2ShortN(t *testing.T) {
prog := []byte{byte(PUSHDATA2), 0}
vm := load(prog)
vm.Run()
assert.Equal(t, true, vm.state.HasFlag(faultState))
}
func TestPushData2BadN(t *testing.T) {
prog := []byte{byte(PUSHDATA2), 1, 0}
vm := load(prog)
vm.Run()
assert.Equal(t, true, vm.state.HasFlag(faultState))
}
func TestPushData2Good(t *testing.T) {
prog := makeProgram(PUSHDATA2, 3, 0, 1, 2, 3)
vm := load(prog)
vm.Run()
assert.Equal(t, false, vm.state.HasFlag(faultState))
assert.Equal(t, 1, vm.estack.Len())
assert.Equal(t, []byte{1, 2, 3}, vm.estack.Pop().Bytes())
}
func TestPushData4BadNoN(t *testing.T) {
prog := []byte{byte(PUSHDATA4)}
vm := load(prog)
vm.Run()
assert.Equal(t, true, vm.state.HasFlag(faultState))
}
func TestPushData4BadN(t *testing.T) {
prog := []byte{byte(PUSHDATA4), 1, 0, 0, 0}
vm := load(prog)
vm.Run()
assert.Equal(t, true, vm.state.HasFlag(faultState))
}
func TestPushData4ShortN(t *testing.T) {
prog := []byte{byte(PUSHDATA4), 0, 0, 0}
vm := load(prog)
vm.Run()
assert.Equal(t, true, vm.state.HasFlag(faultState))
}
func TestPushData4Good(t *testing.T) {
prog := makeProgram(PUSHDATA4, 3, 0, 0, 0, 1, 2, 3)
vm := load(prog)
vm.Run()
assert.Equal(t, false, vm.state.HasFlag(faultState))
assert.Equal(t, 1, vm.estack.Len())
assert.Equal(t, []byte{1, 2, 3}, vm.estack.Pop().Bytes())
}
func TestNOTNoArgument(t *testing.T) {