You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

594 lines
15 KiB

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