golang.org/x/net@v0.25.1-0.20240516223405-c87a5b62e243/bpf/instructions_test.go (about) 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 5 package bpf 6 7 import ( 8 "fmt" 9 "io/ioutil" 10 "reflect" 11 "strconv" 12 "strings" 13 "testing" 14 ) 15 16 // This is a direct translation of the program in 17 // testdata/all_instructions.txt. 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 // Check that we produce the same output as the canonical bpf_asm 93 // linux kernel tool. 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 // First statement is the number of statements, last statement is 106 // empty. We just ignore both and rely on slice length. 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 // Check that assembly and disassembly match each other. 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 // Invalid instruction 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 }