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.

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