Browse Source

Added Program.Verify method

master
maze 7 months ago
parent
commit
4cc007b945
4 changed files with 96 additions and 63 deletions
  1. 0
    7
      assembler.go
  2. 9
    0
      cmd/bpfasm/main.go
  3. 76
    0
      program.go
  4. 11
    56
      vm.go

+ 0
- 7
assembler.go View File

@@ -16,13 +16,6 @@ import (
16 16
 	"maze.io/x/bpf/internal/parser"
17 17
 )
18 18
 
19
-// Program is an assembled program.
20
-type Program []bpf.Instruction
21
-
22
-func (p Program) Assemble() ([]bpf.RawInstruction, error) {
23
-	return bpf.Assemble(p)
24
-}
25
-
26 19
 // Assemble BPF instructions from source.
27 20
 func Assemble(source string) (Program, error) {
28 21
 	var (

+ 9
- 0
cmd/bpfasm/main.go View File

@@ -22,6 +22,12 @@ The C output format pretty prints a C-like construct.
22 22
 Disassembler
23 23
 
24 24
 Disassembles a Berkeley Packet Filter binary to assembler or C format.
25
+
26
+
27
+A note on the use of labels
28
+
29
+The Berkeley Packet Filter only allows for positive offsets in jumps, so
30
+jumping to a previously defined label will result in an error.
25 31
 */
26 32
 package main
27 33
 
@@ -186,6 +192,9 @@ func assemble(ctx *cli.Context) error {
186 192
 	if err != nil {
187 193
 		fmt.Fprintf(os.Stderr, "error assembling %s: %v\n", sourcePath, err)
188 194
 		os.Exit(1)
195
+	} else if err = program.Verify(); err != nil {
196
+		fmt.Fprintf(os.Stderr, "error verifying program: %v\n", err)
197
+		os.Exit(1)
189 198
 	}
190 199
 
191 200
 	var output io.WriteCloser

+ 76
- 0
program.go View File

@@ -0,0 +1,76 @@
1
+package bpf
2
+
3
+import (
4
+	"errors"
5
+	"fmt"
6
+
7
+	"golang.org/x/net/bpf"
8
+)
9
+
10
+// Program is an assembled program.
11
+type Program []bpf.Instruction
12
+
13
+func (p Program) Assemble() ([]bpf.RawInstruction, error) {
14
+	return bpf.Assemble(p)
15
+}
16
+
17
+// Verify the program doing bounary checks and detecting division by zero
18
+// errors.
19
+func (p Program) Verify() error {
20
+	l := len(p)
21
+	if l == 0 {
22
+		return errors.New("bpf: no instructions in program")
23
+	}
24
+
25
+	for i, ins := range p {
26
+		check := l - (i + 1)
27
+		switch ins := ins.(type) {
28
+		// Check for out-of-bounds jumps in instructions
29
+		case bpf.Jump:
30
+			if check <= int(ins.Skip) {
31
+				return fmt.Errorf("bpf: cannot jump %d instructions; jumping past program bounds", ins.Skip)
32
+			}
33
+
34
+		case bpf.JumpIf:
35
+			if check <= int(ins.SkipTrue) {
36
+				return fmt.Errorf("bpf: cannot jump %d instructions in true case; jumping past program bounds", ins.SkipTrue)
37
+			}
38
+			if check <= int(ins.SkipFalse) {
39
+				return fmt.Errorf("bpf: cannot jump %d instructions in false case; jumping past program bounds", ins.SkipFalse)
40
+			}
41
+
42
+		case bpf.JumpIfX:
43
+			if check <= int(ins.SkipTrue) {
44
+				return fmt.Errorf("bpf: cannot jump %d instructions in true case; jumping past program bounds", ins.SkipTrue)
45
+			}
46
+			if check <= int(ins.SkipFalse) {
47
+				return fmt.Errorf("bpf: cannot jump %d instructions in false case; jumping past program bounds", ins.SkipFalse)
48
+			}
49
+
50
+		// Check for division or modulus by zero
51
+		case bpf.ALUOpConstant:
52
+			if ins.Val != 0 {
53
+				break
54
+			}
55
+
56
+			switch ins.Op {
57
+			case bpf.ALUOpDiv, bpf.ALUOpMod:
58
+				return errors.New("cannot divide by zero using ALUOpConstant")
59
+			}
60
+		}
61
+	}
62
+
63
+	// Make sure last instruction is a return instruction
64
+	switch p[l-1].(type) {
65
+	case bpf.RetA, bpf.RetConstant:
66
+	default:
67
+		return errors.New("bpf: program must end with RetA or RetConstant")
68
+	}
69
+
70
+	// Though our VM works using disassembled instructions, we
71
+	// attempt to assemble the input filter anyway to ensure it is compatible
72
+	// with an operating system VM.
73
+	_, err := bpf.Assemble(p)
74
+
75
+	return err
76
+}

+ 11
- 56
vm.go View File

@@ -6,7 +6,6 @@
6 6
 package bpf
7 7
 
8 8
 import (
9
-	"errors"
10 9
 	"fmt"
11 10
 
12 11
 	"golang.org/x/net/bpf"
@@ -29,70 +28,26 @@ func NewVM(program Program) *VM {
29 28
 
30 29
 // Verify runs sanity checks on the loaded program.
31 30
 func (vm *VM) Verify() error {
32
-	if len(vm.program) == 0 {
33
-		return errors.New("bpf: no instructions in program")
31
+	if err := vm.program.Verify(); err != nil {
32
+		return err
34 33
 	}
35 34
 
36
-	for i, ins := range vm.program {
37
-		check := len(vm.program) - (i + 1)
38
-		switch ins := ins.(type) {
39
-		// Check for out-of-bounds jumps in instructions
40
-		case bpf.Jump:
41
-			if check <= int(ins.Skip) {
42
-				return fmt.Errorf("bpf: cannot jump %d instructions; jumping past program bounds", ins.Skip)
43
-			}
44
-		case bpf.JumpIf:
45
-			if check <= int(ins.SkipTrue) {
46
-				return fmt.Errorf("bpf: cannot jump %d instructions in true case; jumping past program bounds", ins.SkipTrue)
47
-			}
48
-			if check <= int(ins.SkipFalse) {
49
-				return fmt.Errorf("bpf: cannot jump %d instructions in false case; jumping past program bounds", ins.SkipFalse)
50
-			}
51
-		case bpf.JumpIfX:
52
-			if check <= int(ins.SkipTrue) {
53
-				return fmt.Errorf("bpf: cannot jump %d instructions in true case; jumping past program bounds", ins.SkipTrue)
54
-			}
55
-			if check <= int(ins.SkipFalse) {
56
-				return fmt.Errorf("bpf: cannot jump %d instructions in false case; jumping past program bounds", ins.SkipFalse)
57
-			}
58
-		// Check for division or modulus by zero
59
-		case bpf.ALUOpConstant:
60
-			if ins.Val != 0 {
61
-				break
62
-			}
63
-
64
-			switch ins.Op {
65
-			case bpf.ALUOpDiv, bpf.ALUOpMod:
66
-				return errors.New("cannot divide by zero using ALUOpConstant")
67
-			}
68
-		// Check for unknown extensions
69
-		case bpf.LoadExtension:
70
-			switch ins.Num {
35
+	// Verify extensions.
36
+	for _, ins := range vm.program {
37
+		if ext, ok := ins.(bpf.LoadExtension); ok {
38
+			switch ext.Num {
71 39
 			case bpf.ExtLen:
40
+				continue
41
+
72 42
 			default:
73
-				if vm.Extensions != nil {
74
-					if _, ok := vm.Extensions[ins.Num]; ok {
75
-						continue
76
-					}
43
+				if _, ok = vm.Extensions[ext.Num]; !ok {
44
+					return fmt.Errorf("extension %d is not available", ext.Num)
77 45
 				}
78
-				return fmt.Errorf("extension %d not implemented", ins.Num)
79 46
 			}
80 47
 		}
81 48
 	}
82 49
 
83
-	// Make sure last instruction is a return instruction
84
-	switch vm.program[len(vm.program)-1].(type) {
85
-	case bpf.RetA, bpf.RetConstant:
86
-	default:
87
-		return errors.New("bpf: program must end with RetA or RetConstant")
88
-	}
89
-
90
-	// Though our VM works using disassembled instructions, we
91
-	// attempt to assemble the input filter anyway to ensure it is compatible
92
-	// with an operating system VM.
93
-	_, err := bpf.Assemble(vm.program)
94
-
95
-	return err
50
+	return nil
96 51
 }
97 52
 
98 53
 func (vm *VM) Run(in []byte) (verdict uint32, err error) {

Loading…
Cancel
Save