1
2
3
4
5 package bpf
6
7 import (
8 "fmt"
9 "io/ioutil"
10 "reflect"
11 "strconv"
12 "strings"
13 "testing"
14 )
15
16
17
18 var allInstructions = []Instruction{
19 LoadConstant{Dst: RegA, Val: 42},
20 LoadConstant{Dst: RegX, Val: 42},
21
22 LoadScratch{Dst: RegA, N: 3},
23 LoadScratch{Dst: RegX, N: 3},
24
25 LoadAbsolute{Off: 42, Size: 1},
26 LoadAbsolute{Off: 42, Size: 2},
27 LoadAbsolute{Off: 42, Size: 4},
28
29 LoadIndirect{Off: 42, Size: 1},
30 LoadIndirect{Off: 42, Size: 2},
31 LoadIndirect{Off: 42, Size: 4},
32
33 LoadMemShift{Off: 42},
34
35 LoadExtension{Num: ExtLen},
36 LoadExtension{Num: ExtProto},
37 LoadExtension{Num: ExtType},
38 LoadExtension{Num: ExtRand},
39
40 StoreScratch{Src: RegA, N: 3},
41 StoreScratch{Src: RegX, N: 3},
42
43 ALUOpConstant{Op: ALUOpAdd, Val: 42},
44 ALUOpConstant{Op: ALUOpSub, Val: 42},
45 ALUOpConstant{Op: ALUOpMul, Val: 42},
46 ALUOpConstant{Op: ALUOpDiv, Val: 42},
47 ALUOpConstant{Op: ALUOpOr, Val: 42},
48 ALUOpConstant{Op: ALUOpAnd, Val: 42},
49 ALUOpConstant{Op: ALUOpShiftLeft, Val: 42},
50 ALUOpConstant{Op: ALUOpShiftRight, Val: 42},
51 ALUOpConstant{Op: ALUOpMod, Val: 42},
52 ALUOpConstant{Op: ALUOpXor, Val: 42},
53
54 ALUOpX{Op: ALUOpAdd},
55 ALUOpX{Op: ALUOpSub},
56 ALUOpX{Op: ALUOpMul},
57 ALUOpX{Op: ALUOpDiv},
58 ALUOpX{Op: ALUOpOr},
59 ALUOpX{Op: ALUOpAnd},
60 ALUOpX{Op: ALUOpShiftLeft},
61 ALUOpX{Op: ALUOpShiftRight},
62 ALUOpX{Op: ALUOpMod},
63 ALUOpX{Op: ALUOpXor},
64
65 NegateA{},
66
67 Jump{Skip: 17},
68 JumpIf{Cond: JumpEqual, Val: 42, SkipTrue: 15, SkipFalse: 16},
69 JumpIf{Cond: JumpNotEqual, Val: 42, SkipTrue: 15},
70 JumpIf{Cond: JumpLessThan, Val: 42, SkipTrue: 14},
71 JumpIf{Cond: JumpLessOrEqual, Val: 42, SkipTrue: 13},
72 JumpIf{Cond: JumpGreaterThan, Val: 42, SkipTrue: 11, SkipFalse: 12},
73 JumpIf{Cond: JumpGreaterOrEqual, Val: 42, SkipTrue: 10, SkipFalse: 11},
74 JumpIf{Cond: JumpBitsSet, Val: 42, SkipTrue: 9, SkipFalse: 10},
75
76 JumpIfX{Cond: JumpEqual, SkipTrue: 8, SkipFalse: 9},
77 JumpIfX{Cond: JumpNotEqual, SkipTrue: 8},
78 JumpIfX{Cond: JumpLessThan, SkipTrue: 7},
79 JumpIfX{Cond: JumpLessOrEqual, SkipTrue: 6},
80 JumpIfX{Cond: JumpGreaterThan, SkipTrue: 4, SkipFalse: 5},
81 JumpIfX{Cond: JumpGreaterOrEqual, SkipTrue: 3, SkipFalse: 4},
82 JumpIfX{Cond: JumpBitsSet, SkipTrue: 2, SkipFalse: 3},
83
84 TAX{},
85 TXA{},
86
87 RetA{},
88 RetConstant{Val: 42},
89 }
90 var allInstructionsExpected = "testdata/all_instructions.bpf"
91
92
93
94 func TestInterop(t *testing.T) {
95 out, err := Assemble(allInstructions)
96 if err != nil {
97 t.Fatalf("assembly of allInstructions program failed: %s", err)
98 }
99 t.Logf("Assembled program is %d instructions long", len(out))
100
101 bs, err := ioutil.ReadFile(allInstructionsExpected)
102 if err != nil {
103 t.Fatalf("reading %s: %s", allInstructionsExpected, err)
104 }
105
106
107 stmts := strings.Split(string(bs), ",")
108 if len(stmts)-2 != len(out) {
109 t.Fatalf("test program lengths don't match: %s has %d, Go implementation has %d", allInstructionsExpected, len(stmts)-2, len(allInstructions))
110 }
111
112 for i, stmt := range stmts[1 : len(stmts)-2] {
113 nums := strings.Split(stmt, " ")
114 if len(nums) != 4 {
115 t.Fatalf("malformed instruction %d in %s: %s", i+1, allInstructionsExpected, stmt)
116 }
117
118 actual := out[i]
119
120 op, err := strconv.ParseUint(nums[0], 10, 16)
121 if err != nil {
122 t.Fatalf("malformed opcode %s in instruction %d of %s", nums[0], i+1, allInstructionsExpected)
123 }
124 if actual.Op != uint16(op) {
125 t.Errorf("opcode mismatch on instruction %d (%#v): got 0x%02x, want 0x%02x", i+1, allInstructions[i], actual.Op, op)
126 }
127
128 jt, err := strconv.ParseUint(nums[1], 10, 8)
129 if err != nil {
130 t.Fatalf("malformed jt offset %s in instruction %d of %s", nums[1], i+1, allInstructionsExpected)
131 }
132 if actual.Jt != uint8(jt) {
133 t.Errorf("jt mismatch on instruction %d (%#v): got %d, want %d", i+1, allInstructions[i], actual.Jt, jt)
134 }
135
136 jf, err := strconv.ParseUint(nums[2], 10, 8)
137 if err != nil {
138 t.Fatalf("malformed jf offset %s in instruction %d of %s", nums[2], i+1, allInstructionsExpected)
139 }
140 if actual.Jf != uint8(jf) {
141 t.Errorf("jf mismatch on instruction %d (%#v): got %d, want %d", i+1, allInstructions[i], actual.Jf, jf)
142 }
143
144 k, err := strconv.ParseUint(nums[3], 10, 32)
145 if err != nil {
146 t.Fatalf("malformed constant %s in instruction %d of %s", nums[3], i+1, allInstructionsExpected)
147 }
148 if actual.K != uint32(k) {
149 t.Errorf("constant mismatch on instruction %d (%#v): got %d, want %d", i+1, allInstructions[i], actual.K, k)
150 }
151 }
152 }
153
154
155 func TestAsmDisasm(t *testing.T) {
156 prog1, err := Assemble(allInstructions)
157 if err != nil {
158 t.Fatalf("assembly of allInstructions program failed: %s", err)
159 }
160 t.Logf("Assembled program is %d instructions long", len(prog1))
161
162 got, allDecoded := Disassemble(prog1)
163 if !allDecoded {
164 t.Errorf("Disassemble(Assemble(allInstructions)) produced unrecognized instructions:")
165 for i, inst := range got {
166 if r, ok := inst.(RawInstruction); ok {
167 t.Logf(" insn %d, %#v --> %#v", i+1, allInstructions[i], r)
168 }
169 }
170 }
171
172 if len(allInstructions) != len(got) {
173 t.Fatalf("disassembly changed program size: %d insns before, %d insns after", len(allInstructions), len(got))
174 }
175 if !reflect.DeepEqual(allInstructions, got) {
176 t.Errorf("program mutated by disassembly:")
177 for i := range got {
178 if !reflect.DeepEqual(allInstructions[i], got[i]) {
179 t.Logf(" insn %d, s: %#v, p1: %#v, got: %#v", i+1, allInstructions[i], prog1[i], got[i])
180 }
181 }
182 }
183 }
184
185 type InvalidInstruction struct{}
186
187 func (a InvalidInstruction) Assemble() (RawInstruction, error) {
188 return RawInstruction{}, fmt.Errorf("Invalid Instruction")
189 }
190
191 func (a InvalidInstruction) String() string {
192 return fmt.Sprintf("unknown instruction: %#v", a)
193 }
194
195 func TestString(t *testing.T) {
196 testCases := []struct {
197 instruction Instruction
198 assembler string
199 }{
200 {
201 instruction: LoadConstant{Dst: RegA, Val: 42},
202 assembler: "ld #42",
203 },
204 {
205 instruction: LoadConstant{Dst: RegX, Val: 42},
206 assembler: "ldx #42",
207 },
208 {
209 instruction: LoadConstant{Dst: 0xffff, Val: 42},
210 assembler: "unknown instruction: bpf.LoadConstant{Dst:0xffff, Val:0x2a}",
211 },
212 {
213 instruction: LoadScratch{Dst: RegA, N: 3},
214 assembler: "ld M[3]",
215 },
216 {
217 instruction: LoadScratch{Dst: RegX, N: 3},
218 assembler: "ldx M[3]",
219 },
220 {
221 instruction: LoadScratch{Dst: 0xffff, N: 3},
222 assembler: "unknown instruction: bpf.LoadScratch{Dst:0xffff, N:3}",
223 },
224 {
225 instruction: LoadAbsolute{Off: 42, Size: 1},
226 assembler: "ldb [42]",
227 },
228 {
229 instruction: LoadAbsolute{Off: 42, Size: 2},
230 assembler: "ldh [42]",
231 },
232 {
233 instruction: LoadAbsolute{Off: 42, Size: 4},
234 assembler: "ld [42]",
235 },
236 {
237 instruction: LoadAbsolute{Off: 42, Size: -1},
238 assembler: "unknown instruction: bpf.LoadAbsolute{Off:0x2a, Size:-1}",
239 },
240 {
241 instruction: LoadIndirect{Off: 42, Size: 1},
242 assembler: "ldb [x + 42]",
243 },
244 {
245 instruction: LoadIndirect{Off: 42, Size: 2},
246 assembler: "ldh [x + 42]",
247 },
248 {
249 instruction: LoadIndirect{Off: 42, Size: 4},
250 assembler: "ld [x + 42]",
251 },
252 {
253 instruction: LoadIndirect{Off: 42, Size: -1},
254 assembler: "unknown instruction: bpf.LoadIndirect{Off:0x2a, Size:-1}",
255 },
256 {
257 instruction: LoadMemShift{Off: 42},
258 assembler: "ldx 4*([42]&0xf)",
259 },
260 {
261 instruction: LoadExtension{Num: ExtLen},
262 assembler: "ld #len",
263 },
264 {
265 instruction: LoadExtension{Num: ExtProto},
266 assembler: "ld #proto",
267 },
268 {
269 instruction: LoadExtension{Num: ExtType},
270 assembler: "ld #type",
271 },
272 {
273 instruction: LoadExtension{Num: ExtPayloadOffset},
274 assembler: "ld #poff",
275 },
276 {
277 instruction: LoadExtension{Num: ExtInterfaceIndex},
278 assembler: "ld #ifidx",
279 },
280 {
281 instruction: LoadExtension{Num: ExtNetlinkAttr},
282 assembler: "ld #nla",
283 },
284 {
285 instruction: LoadExtension{Num: ExtNetlinkAttrNested},
286 assembler: "ld #nlan",
287 },
288 {
289 instruction: LoadExtension{Num: ExtMark},
290 assembler: "ld #mark",
291 },
292 {
293 instruction: LoadExtension{Num: ExtQueue},
294 assembler: "ld #queue",
295 },
296 {
297 instruction: LoadExtension{Num: ExtLinkLayerType},
298 assembler: "ld #hatype",
299 },
300 {
301 instruction: LoadExtension{Num: ExtRXHash},
302 assembler: "ld #rxhash",
303 },
304 {
305 instruction: LoadExtension{Num: ExtCPUID},
306 assembler: "ld #cpu",
307 },
308 {
309 instruction: LoadExtension{Num: ExtVLANTag},
310 assembler: "ld #vlan_tci",
311 },
312 {
313 instruction: LoadExtension{Num: ExtVLANTagPresent},
314 assembler: "ld #vlan_avail",
315 },
316 {
317 instruction: LoadExtension{Num: ExtVLANProto},
318 assembler: "ld #vlan_tpid",
319 },
320 {
321 instruction: LoadExtension{Num: ExtRand},
322 assembler: "ld #rand",
323 },
324 {
325 instruction: LoadAbsolute{Off: 0xfffff038, Size: 4},
326 assembler: "ld #rand",
327 },
328 {
329 instruction: LoadExtension{Num: 0xfff},
330 assembler: "unknown instruction: bpf.LoadExtension{Num:4095}",
331 },
332 {
333 instruction: StoreScratch{Src: RegA, N: 3},
334 assembler: "st M[3]",
335 },
336 {
337 instruction: StoreScratch{Src: RegX, N: 3},
338 assembler: "stx M[3]",
339 },
340 {
341 instruction: StoreScratch{Src: 0xffff, N: 3},
342 assembler: "unknown instruction: bpf.StoreScratch{Src:0xffff, N:3}",
343 },
344 {
345 instruction: ALUOpConstant{Op: ALUOpAdd, Val: 42},
346 assembler: "add #42",
347 },
348 {
349 instruction: ALUOpConstant{Op: ALUOpSub, Val: 42},
350 assembler: "sub #42",
351 },
352 {
353 instruction: ALUOpConstant{Op: ALUOpMul, Val: 42},
354 assembler: "mul #42",
355 },
356 {
357 instruction: ALUOpConstant{Op: ALUOpDiv, Val: 42},
358 assembler: "div #42",
359 },
360 {
361 instruction: ALUOpConstant{Op: ALUOpOr, Val: 42},
362 assembler: "or #42",
363 },
364 {
365 instruction: ALUOpConstant{Op: ALUOpAnd, Val: 42},
366 assembler: "and #42",
367 },
368 {
369 instruction: ALUOpConstant{Op: ALUOpShiftLeft, Val: 42},
370 assembler: "lsh #42",
371 },
372 {
373 instruction: ALUOpConstant{Op: ALUOpShiftRight, Val: 42},
374 assembler: "rsh #42",
375 },
376 {
377 instruction: ALUOpConstant{Op: ALUOpMod, Val: 42},
378 assembler: "mod #42",
379 },
380 {
381 instruction: ALUOpConstant{Op: ALUOpXor, Val: 42},
382 assembler: "xor #42",
383 },
384 {
385 instruction: ALUOpConstant{Op: 0xffff, Val: 42},
386 assembler: "unknown instruction: bpf.ALUOpConstant{Op:0xffff, Val:0x2a}",
387 },
388 {
389 instruction: ALUOpX{Op: ALUOpAdd},
390 assembler: "add x",
391 },
392 {
393 instruction: ALUOpX{Op: ALUOpSub},
394 assembler: "sub x",
395 },
396 {
397 instruction: ALUOpX{Op: ALUOpMul},
398 assembler: "mul x",
399 },
400 {
401 instruction: ALUOpX{Op: ALUOpDiv},
402 assembler: "div x",
403 },
404 {
405 instruction: ALUOpX{Op: ALUOpOr},
406 assembler: "or x",
407 },
408 {
409 instruction: ALUOpX{Op: ALUOpAnd},
410 assembler: "and x",
411 },
412 {
413 instruction: ALUOpX{Op: ALUOpShiftLeft},
414 assembler: "lsh x",
415 },
416 {
417 instruction: ALUOpX{Op: ALUOpShiftRight},
418 assembler: "rsh x",
419 },
420 {
421 instruction: ALUOpX{Op: ALUOpMod},
422 assembler: "mod x",
423 },
424 {
425 instruction: ALUOpX{Op: ALUOpXor},
426 assembler: "xor x",
427 },
428 {
429 instruction: ALUOpX{Op: 0xffff},
430 assembler: "unknown instruction: bpf.ALUOpX{Op:0xffff}",
431 },
432 {
433 instruction: NegateA{},
434 assembler: "neg",
435 },
436 {
437 instruction: Jump{Skip: 10},
438 assembler: "ja 10",
439 },
440 {
441 instruction: JumpIf{Cond: JumpEqual, Val: 42, SkipTrue: 8, SkipFalse: 9},
442 assembler: "jeq #42,8,9",
443 },
444 {
445 instruction: JumpIf{Cond: JumpEqual, Val: 42, SkipTrue: 8},
446 assembler: "jeq #42,8",
447 },
448 {
449 instruction: JumpIf{Cond: JumpEqual, Val: 42, SkipFalse: 8},
450 assembler: "jneq #42,8",
451 },
452 {
453 instruction: JumpIf{Cond: JumpNotEqual, Val: 42, SkipTrue: 8},
454 assembler: "jneq #42,8",
455 },
456 {
457 instruction: JumpIf{Cond: JumpLessThan, Val: 42, SkipTrue: 7},
458 assembler: "jlt #42,7",
459 },
460 {
461 instruction: JumpIf{Cond: JumpLessOrEqual, Val: 42, SkipTrue: 6},
462 assembler: "jle #42,6",
463 },
464 {
465 instruction: JumpIf{Cond: JumpGreaterThan, Val: 42, SkipTrue: 4, SkipFalse: 5},
466 assembler: "jgt #42,4,5",
467 },
468 {
469 instruction: JumpIf{Cond: JumpGreaterThan, Val: 42, SkipTrue: 4},
470 assembler: "jgt #42,4",
471 },
472 {
473 instruction: JumpIf{Cond: JumpGreaterOrEqual, Val: 42, SkipTrue: 3, SkipFalse: 4},
474 assembler: "jge #42,3,4",
475 },
476 {
477 instruction: JumpIf{Cond: JumpGreaterOrEqual, Val: 42, SkipTrue: 3},
478 assembler: "jge #42,3",
479 },
480 {
481 instruction: JumpIf{Cond: JumpBitsSet, Val: 42, SkipTrue: 2, SkipFalse: 3},
482 assembler: "jset #42,2,3",
483 },
484 {
485 instruction: JumpIf{Cond: JumpBitsSet, Val: 42, SkipTrue: 2},
486 assembler: "jset #42,2",
487 },
488 {
489 instruction: JumpIf{Cond: JumpBitsNotSet, Val: 42, SkipTrue: 2, SkipFalse: 3},
490 assembler: "jset #42,3,2",
491 },
492 {
493 instruction: JumpIf{Cond: JumpBitsNotSet, Val: 42, SkipTrue: 2},
494 assembler: "jset #42,0,2",
495 },
496 {
497 instruction: JumpIf{Cond: 0xffff, Val: 42, SkipTrue: 1, SkipFalse: 2},
498 assembler: "unknown JumpTest 0xffff",
499 },
500 {
501 instruction: JumpIfX{Cond: JumpEqual, SkipTrue: 8, SkipFalse: 9},
502 assembler: "jeq x,8,9",
503 },
504 {
505 instruction: JumpIfX{Cond: JumpEqual, SkipTrue: 8},
506 assembler: "jeq x,8",
507 },
508 {
509 instruction: JumpIfX{Cond: JumpEqual, SkipFalse: 8},
510 assembler: "jneq x,8",
511 },
512 {
513 instruction: JumpIfX{Cond: JumpNotEqual, SkipTrue: 8},
514 assembler: "jneq x,8",
515 },
516 {
517 instruction: JumpIfX{Cond: JumpLessThan, SkipTrue: 7},
518 assembler: "jlt x,7",
519 },
520 {
521 instruction: JumpIfX{Cond: JumpLessOrEqual, SkipTrue: 6},
522 assembler: "jle x,6",
523 },
524 {
525 instruction: JumpIfX{Cond: JumpGreaterThan, SkipTrue: 4, SkipFalse: 5},
526 assembler: "jgt x,4,5",
527 },
528 {
529 instruction: JumpIfX{Cond: JumpGreaterThan, SkipTrue: 4},
530 assembler: "jgt x,4",
531 },
532 {
533 instruction: JumpIfX{Cond: JumpGreaterOrEqual, SkipTrue: 3, SkipFalse: 4},
534 assembler: "jge x,3,4",
535 },
536 {
537 instruction: JumpIfX{Cond: JumpGreaterOrEqual, SkipTrue: 3},
538 assembler: "jge x,3",
539 },
540 {
541 instruction: JumpIfX{Cond: JumpBitsSet, SkipTrue: 2, SkipFalse: 3},
542 assembler: "jset x,2,3",
543 },
544 {
545 instruction: JumpIfX{Cond: JumpBitsSet, SkipTrue: 2},
546 assembler: "jset x,2",
547 },
548 {
549 instruction: JumpIfX{Cond: JumpBitsNotSet, SkipTrue: 2, SkipFalse: 3},
550 assembler: "jset x,3,2",
551 },
552 {
553 instruction: JumpIfX{Cond: JumpBitsNotSet, SkipTrue: 2},
554 assembler: "jset x,0,2",
555 },
556 {
557 instruction: JumpIfX{Cond: 0xffff, SkipTrue: 1, SkipFalse: 2},
558 assembler: "unknown JumpTest 0xffff",
559 },
560 {
561 instruction: TAX{},
562 assembler: "tax",
563 },
564 {
565 instruction: TXA{},
566 assembler: "txa",
567 },
568 {
569 instruction: RetA{},
570 assembler: "ret a",
571 },
572 {
573 instruction: RetConstant{Val: 42},
574 assembler: "ret #42",
575 },
576
577 {
578 instruction: InvalidInstruction{},
579 assembler: "unknown instruction: bpf.InvalidInstruction{}",
580 },
581 }
582
583 for _, testCase := range testCases {
584 if input, ok := testCase.instruction.(fmt.Stringer); ok {
585 got := input.String()
586 if got != testCase.assembler {
587 t.Errorf("String did not return expected assembler notation, expected: %s, got: %s", testCase.assembler, got)
588 }
589 } else {
590 t.Errorf("Instruction %#v is not a fmt.Stringer", testCase.instruction)
591 }
592 }
593 }
594
View as plain text