bpf: fix VM out of bounds LoadMemShift check
The bpf VM did not correctly check the bounds of LoadMemShift
instructions, as it used a size of 0 instead of the correct 1.
A LoadMemShift instruction 1 past the end of the input resulted in a
runtime panic:
panic(0x5c1d40, 0x7cec00)
/usr/local/go/src/runtime/panic.go:522 +0x1b5
golang.org/x/net/bpf.loadMemShift(...)
/home/afabre/go/pkg/mod/golang.org/x/net@v0.0.0-20190603091049-60506f45cf65/bpf/vm_instructions.go:137
golang.org/x/net/bpf.(*VM).Run(0xc00000ec40, 0xc0000173c8, 0x2, 0x8, 0x2, 0xc0000173c8, 0x0)
/home/afabre/go/pkg/mod/golang.org/x/net@v0.0.0-20190603091049-60506f45cf65/bpf/vm.go:131 +0xb0a
Fix this, and rework the out of bounds tests for load instructions to:
* Use an offset one past the end of the input, to catch this
* Use a filter that returns 1, to catch cases were the out of bounds
load does not cause a panic, but does not cause the VM to return 0.
Change-Id: I1e68886915207a34f59765805f907f36dc031f70
Reviewed-on: https://go-review.googlesource.com/c/net/+/180979
Run-TryBot: Matt Layher <mdlayher@gmail.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Matt Layher <mdlayher@gmail.com>
diff --git a/bpf/vm_instructions.go b/bpf/vm_instructions.go
index f0d2e55..cf8947c 100644
--- a/bpf/vm_instructions.go
+++ b/bpf/vm_instructions.go
@@ -129,7 +129,8 @@
func loadMemShift(ins LoadMemShift, in []byte) (uint32, bool) {
offset := int(ins.Off)
- if !inBounds(len(in), offset, 0) {
+ // Size of LoadMemShift is always 1 byte
+ if !inBounds(len(in), offset, 1) {
return 0, false
}
diff --git a/bpf/vm_load_test.go b/bpf/vm_load_test.go
index 04578b6..d57e4af 100644
--- a/bpf/vm_load_test.go
+++ b/bpf/vm_load_test.go
@@ -13,55 +13,61 @@
)
func TestVMLoadAbsoluteOffsetOutOfBounds(t *testing.T) {
+ pkt := []byte{
+ 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff,
+ 0, 1, 2, 3,
+ }
+
vm, done, err := testVM(t, []bpf.Instruction{
bpf.LoadAbsolute{
- Off: 100,
- Size: 2,
+ Off: uint32(len(pkt)),
+ Size: 1,
},
- bpf.RetA{},
+ // Out of bounds should return 0, return 1 to tell if execution continued
+ bpf.RetConstant{Val: 1},
})
if err != nil {
t.Fatalf("failed to load BPF program: %v", err)
}
defer done()
- out, err := vm.Run([]byte{
- 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff,
- 0, 1, 2, 3,
- })
+ out, err := vm.Run(pkt)
if err != nil {
t.Fatalf("unexpected error while running program: %v", err)
}
if want, got := 0, out; want != got {
- t.Fatalf("unexpected number of output bytes:\n- want: %d\n- got: %d",
+ t.Fatalf("unexpected result:\n- want: %d\n- got: %d",
want, got)
}
}
func TestVMLoadAbsoluteOffsetPlusSizeOutOfBounds(t *testing.T) {
+ pkt := []byte{
+ 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff,
+ 0,
+ }
+
vm, done, err := testVM(t, []bpf.Instruction{
bpf.LoadAbsolute{
- Off: 8,
+ Off: uint32(len(pkt) - 1),
Size: 2,
},
- bpf.RetA{},
+ // Out of bounds should return 0, return 1 to tell if execution continued
+ bpf.RetConstant{Val: 1},
})
if err != nil {
t.Fatalf("failed to load BPF program: %v", err)
}
defer done()
- out, err := vm.Run([]byte{
- 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff,
- 0,
- })
+ out, err := vm.Run(pkt)
if err != nil {
t.Fatalf("unexpected error while running program: %v", err)
}
if want, got := 0, out; want != got {
- t.Fatalf("unexpected number of output bytes:\n- want: %d\n- got: %d",
+ t.Fatalf("unexpected result:\n- want: %d\n- got: %d",
want, got)
}
}
@@ -107,54 +113,60 @@
}
func TestVMLoadIndirectOutOfBounds(t *testing.T) {
+ pkt := []byte{
+ 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff,
+ 0,
+ }
+
vm, done, err := testVM(t, []bpf.Instruction{
bpf.LoadIndirect{
- Off: 100,
+ Off: uint32(len(pkt)),
Size: 1,
},
- bpf.RetA{},
+ // Out of bounds should return 0, return 1 to tell if execution continued
+ bpf.RetConstant{Val: 1},
})
if err != nil {
t.Fatalf("failed to load BPF program: %v", err)
}
defer done()
- out, err := vm.Run([]byte{
- 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff,
- 0,
- })
+ out, err := vm.Run(pkt)
if err != nil {
t.Fatalf("unexpected error while running program: %v", err)
}
if want, got := 0, out; want != got {
- t.Fatalf("unexpected number of output bytes:\n- want: %d\n- got: %d",
+ t.Fatalf("unexpected result:\n- want: %d\n- got: %d",
want, got)
}
}
func TestVMLoadMemShiftOutOfBounds(t *testing.T) {
+ pkt := []byte{
+ 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff,
+ 0,
+ }
+
vm, done, err := testVM(t, []bpf.Instruction{
bpf.LoadMemShift{
- Off: 100,
+ Off: uint32(len(pkt)),
},
- bpf.RetA{},
+ // Out of bounds should return 0, return 1 to tell if execution continued
+ bpf.RetConstant{Val: 1},
})
if err != nil {
t.Fatalf("failed to load BPF program: %v", err)
}
defer done()
- out, err := vm.Run([]byte{
- 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff,
- 0,
- })
+ out, err := vm.Run(pkt)
if err != nil {
t.Fatalf("unexpected error while running program: %v", err)
}
if want, got := 0, out; want != got {
- t.Fatalf("unexpected number of output bytes:\n- want: %d\n- got: %d",
+ t.Fatalf("unexpected result:\n- want: %d\n- got: %d",
want, got)
}
}