github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/pkg/bpf/interpreter_test.go (about) 1 // Copyright 2018 The gVisor Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package bpf 16 17 import ( 18 "encoding/binary" 19 "testing" 20 21 "github.com/SagerNet/gvisor/pkg/abi/linux" 22 "github.com/SagerNet/gvisor/pkg/hostarch" 23 "github.com/SagerNet/gvisor/pkg/marshal" 24 ) 25 26 func TestCompilationErrors(t *testing.T) { 27 for _, test := range []struct { 28 // desc is the test's description. 29 desc string 30 31 // insns is the BPF instructions to be compiled. 32 insns []linux.BPFInstruction 33 34 // expectedErr is the expected compilation error. 35 expectedErr error 36 }{ 37 { 38 desc: "Instructions must not be nil", 39 expectedErr: Error{InvalidInstructionCount, 0}, 40 }, 41 { 42 desc: "Instructions must not be empty", 43 insns: []linux.BPFInstruction{}, 44 expectedErr: Error{InvalidInstructionCount, 0}, 45 }, 46 { 47 desc: "A program must end with a return", 48 insns: make([]linux.BPFInstruction, MaxInstructions), 49 expectedErr: Error{InvalidEndOfProgram, MaxInstructions - 1}, 50 }, 51 { 52 desc: "A program must have MaxInstructions or fewer instructions", 53 insns: append(make([]linux.BPFInstruction, MaxInstructions), Stmt(Ret|K, 0)), 54 expectedErr: Error{InvalidInstructionCount, MaxInstructions + 1}, 55 }, 56 { 57 desc: "A load from an invalid M register is a compilation error", 58 insns: []linux.BPFInstruction{ 59 Stmt(Ld|Mem|W, ScratchMemRegisters), // A = M[16] 60 Stmt(Ret|K, 0), // return 0 61 }, 62 expectedErr: Error{InvalidRegister, 0}, 63 }, 64 { 65 desc: "A store to an invalid M register is a compilation error", 66 insns: []linux.BPFInstruction{ 67 Stmt(St, ScratchMemRegisters), // M[16] = A 68 Stmt(Ret|K, 0), // return 0 69 }, 70 expectedErr: Error{InvalidRegister, 0}, 71 }, 72 { 73 desc: "Division by literal zero is a compilation error", 74 insns: []linux.BPFInstruction{ 75 Stmt(Alu|Div|K, 0), // A /= 0 76 Stmt(Ret|K, 0), // return 0 77 }, 78 expectedErr: Error{DivisionByZero, 0}, 79 }, 80 { 81 desc: "An unconditional jump outside of the program is a compilation error", 82 insns: []linux.BPFInstruction{ 83 Jump(Jmp|Ja, 1, 0, 0), // jmp nextpc+1 84 Stmt(Ret|K, 0), // return 0 85 }, 86 expectedErr: Error{InvalidJumpTarget, 0}, 87 }, 88 { 89 desc: "A conditional jump outside of the program in the true case is a compilation error", 90 insns: []linux.BPFInstruction{ 91 Jump(Jmp|Jeq|K, 0, 1, 0), // if (A == K) jmp nextpc+1 92 Stmt(Ret|K, 0), // return 0 93 }, 94 expectedErr: Error{InvalidJumpTarget, 0}, 95 }, 96 { 97 desc: "A conditional jump outside of the program in the false case is a compilation error", 98 insns: []linux.BPFInstruction{ 99 Jump(Jmp|Jeq|K, 0, 0, 1), // if (A != K) jmp nextpc+1 100 Stmt(Ret|K, 0), // return 0 101 }, 102 expectedErr: Error{InvalidJumpTarget, 0}, 103 }, 104 } { 105 _, err := Compile(test.insns) 106 if err != test.expectedErr { 107 t.Errorf("%s: expected error %q, got error %q", test.desc, test.expectedErr, err) 108 } 109 } 110 } 111 112 func TestExecErrors(t *testing.T) { 113 for _, test := range []struct { 114 // desc is the test's description. 115 desc string 116 117 // insns is the BPF instructions to be executed. 118 insns []linux.BPFInstruction 119 120 // expectedErr is the expected execution error. 121 expectedErr error 122 }{ 123 { 124 desc: "An out-of-bounds load of input data is an execution error", 125 insns: []linux.BPFInstruction{ 126 Stmt(Ld|Abs|B, 0), // A = input[0] 127 Stmt(Ret|K, 0), // return 0 128 }, 129 expectedErr: Error{InvalidLoad, 0}, 130 }, 131 { 132 desc: "Division by zero at runtime is an execution error", 133 insns: []linux.BPFInstruction{ 134 Stmt(Alu|Div|X, 0), // A /= X 135 Stmt(Ret|K, 0), // return 0 136 }, 137 expectedErr: Error{DivisionByZero, 0}, 138 }, 139 { 140 desc: "Modulo zero at runtime is an execution error", 141 insns: []linux.BPFInstruction{ 142 Stmt(Alu|Mod|X, 0), // A %= X 143 Stmt(Ret|K, 0), // return 0 144 }, 145 expectedErr: Error{DivisionByZero, 0}, 146 }, 147 } { 148 p, err := Compile(test.insns) 149 if err != nil { 150 t.Errorf("%s: unexpected compilation error: %v", test.desc, err) 151 continue 152 } 153 ret, err := Exec(p, InputBytes{nil, binary.BigEndian}) 154 if err != test.expectedErr { 155 t.Errorf("%s: expected execution error %q, got (%d, %v)", test.desc, test.expectedErr, ret, err) 156 } 157 } 158 } 159 160 func TestValidInstructions(t *testing.T) { 161 for _, test := range []struct { 162 // desc is the test's description. 163 desc string 164 165 // insns is the BPF instructions to be compiled. 166 insns []linux.BPFInstruction 167 168 // input is the input data. Note that input will be read as big-endian. 169 input []byte 170 171 // expectedRet is the expected return value of the BPF program. 172 expectedRet uint32 173 }{ 174 { 175 desc: "Return of immediate", 176 insns: []linux.BPFInstruction{ 177 Stmt(Ret|K, 42), // return 42 178 }, 179 expectedRet: 42, 180 }, 181 { 182 desc: "Load of immediate into A", 183 insns: []linux.BPFInstruction{ 184 Stmt(Ld|Imm|W, 42), // A = 42 185 Stmt(Ret|A, 0), // return A 186 }, 187 expectedRet: 42, 188 }, 189 { 190 desc: "Load of immediate into X and copying of X into A", 191 insns: []linux.BPFInstruction{ 192 Stmt(Ldx|Imm|W, 42), // X = 42 193 Stmt(Misc|Tax, 0), // A = X 194 Stmt(Ret|A, 0), // return A 195 }, 196 expectedRet: 42, 197 }, 198 { 199 desc: "Copying of A into X and back", 200 insns: []linux.BPFInstruction{ 201 Stmt(Ld|Imm|W, 42), // A = 42 202 Stmt(Misc|Txa, 0), // X = A 203 Stmt(Ld|Imm|W, 0), // A = 0 204 Stmt(Misc|Tax, 0), // A = X 205 Stmt(Ret|A, 0), // return A 206 }, 207 expectedRet: 42, 208 }, 209 { 210 desc: "Load of 32-bit input by absolute offset into A", 211 insns: []linux.BPFInstruction{ 212 Stmt(Ld|Abs|W, 1), // A = input[1..4] 213 Stmt(Ret|A, 0), // return A 214 }, 215 input: []byte{0x00, 0x11, 0x22, 0x33, 0x44}, 216 expectedRet: 0x11223344, 217 }, 218 { 219 desc: "Load of 16-bit input by absolute offset into A", 220 insns: []linux.BPFInstruction{ 221 Stmt(Ld|Abs|H, 1), // A = input[1..2] 222 Stmt(Ret|A, 0), // return A 223 }, 224 input: []byte{0x00, 0x11, 0x22}, 225 expectedRet: 0x1122, 226 }, 227 { 228 desc: "Load of 8-bit input by absolute offset into A", 229 insns: []linux.BPFInstruction{ 230 Stmt(Ld|Abs|B, 1), // A = input[1] 231 Stmt(Ret|A, 0), // return A 232 }, 233 input: []byte{0x00, 0x11}, 234 expectedRet: 0x11, 235 }, 236 { 237 desc: "Load of 32-bit input by relative offset into A", 238 insns: []linux.BPFInstruction{ 239 Stmt(Ldx|Imm|W, 1), // X = 1 240 Stmt(Ld|Ind|W, 1), // A = input[X+1..X+4] 241 Stmt(Ret|A, 0), // return A 242 }, 243 input: []byte{0x00, 0x11, 0x22, 0x33, 0x44, 0x55}, 244 expectedRet: 0x22334455, 245 }, 246 { 247 desc: "Load of 16-bit input by relative offset into A", 248 insns: []linux.BPFInstruction{ 249 Stmt(Ldx|Imm|W, 1), // X = 1 250 Stmt(Ld|Ind|H, 1), // A = input[X+1..X+2] 251 Stmt(Ret|A, 0), // return A 252 }, 253 input: []byte{0x00, 0x11, 0x22, 0x33}, 254 expectedRet: 0x2233, 255 }, 256 { 257 desc: "Load of 8-bit input by relative offset into A", 258 insns: []linux.BPFInstruction{ 259 Stmt(Ldx|Imm|W, 1), // X = 1 260 Stmt(Ld|Ind|B, 1), // A = input[X+1] 261 Stmt(Ret|A, 0), // return A 262 }, 263 input: []byte{0x00, 0x11, 0x22}, 264 expectedRet: 0x22, 265 }, 266 { 267 desc: "Load/store between A and scratch memory", 268 insns: []linux.BPFInstruction{ 269 Stmt(Ld|Imm|W, 42), // A = 42 270 Stmt(St, 2), // M[2] = A 271 Stmt(Ld|Imm|W, 0), // A = 0 272 Stmt(Ld|Mem|W, 2), // A = M[2] 273 Stmt(Ret|A, 0), // return A 274 }, 275 expectedRet: 42, 276 }, 277 { 278 desc: "Load/store between X and scratch memory", 279 insns: []linux.BPFInstruction{ 280 Stmt(Ldx|Imm|W, 42), // X = 42 281 Stmt(Stx, 3), // M[3] = X 282 Stmt(Ldx|Imm|W, 0), // X = 0 283 Stmt(Ldx|Mem|W, 3), // X = M[3] 284 Stmt(Misc|Tax, 0), // A = X 285 Stmt(Ret|A, 0), // return A 286 }, 287 expectedRet: 42, 288 }, 289 { 290 desc: "Load of input length into A", 291 insns: []linux.BPFInstruction{ 292 Stmt(Ld|Len|W, 0), // A = len(input) 293 Stmt(Ret|A, 0), // return A 294 }, 295 input: []byte{1, 2, 3}, 296 expectedRet: 3, 297 }, 298 { 299 desc: "Load of input length into X", 300 insns: []linux.BPFInstruction{ 301 Stmt(Ldx|Len|W, 0), // X = len(input) 302 Stmt(Misc|Tax, 0), // A = X 303 Stmt(Ret|A, 0), // return A 304 }, 305 input: []byte{1, 2, 3}, 306 expectedRet: 3, 307 }, 308 { 309 desc: "Load of MSH (?) into X", 310 insns: []linux.BPFInstruction{ 311 Stmt(Ldx|Msh|B, 0), // X = 4*(input[0]&0xf) 312 Stmt(Misc|Tax, 0), // A = X 313 Stmt(Ret|A, 0), // return A 314 }, 315 input: []byte{0xf1}, 316 expectedRet: 4, 317 }, 318 { 319 desc: "Addition of immediate", 320 insns: []linux.BPFInstruction{ 321 Stmt(Ld|Imm|W, 10), // A = 10 322 Stmt(Alu|Add|K, 20), // A += 20 323 Stmt(Ret|A, 0), // return A 324 }, 325 expectedRet: 30, 326 }, 327 { 328 desc: "Addition of X", 329 insns: []linux.BPFInstruction{ 330 Stmt(Ld|Imm|W, 10), // A = 10 331 Stmt(Ldx|Imm|W, 20), // X = 20 332 Stmt(Alu|Add|X, 0), // A += X 333 Stmt(Ret|A, 0), // return A 334 }, 335 expectedRet: 30, 336 }, 337 { 338 desc: "Subtraction of immediate", 339 insns: []linux.BPFInstruction{ 340 Stmt(Ld|Imm|W, 30), // A = 30 341 Stmt(Alu|Sub|K, 20), // A -= 20 342 Stmt(Ret|A, 0), // return A 343 }, 344 expectedRet: 10, 345 }, 346 { 347 desc: "Subtraction of X", 348 insns: []linux.BPFInstruction{ 349 Stmt(Ld|Imm|W, 30), // A = 30 350 Stmt(Ldx|Imm|W, 20), // X = 20 351 Stmt(Alu|Sub|X, 0), // A -= X 352 Stmt(Ret|A, 0), // return A 353 }, 354 expectedRet: 10, 355 }, 356 { 357 desc: "Multiplication of immediate", 358 insns: []linux.BPFInstruction{ 359 Stmt(Ld|Imm|W, 2), // A = 2 360 Stmt(Alu|Mul|K, 3), // A *= 3 361 Stmt(Ret|A, 0), // return A 362 }, 363 expectedRet: 6, 364 }, 365 { 366 desc: "Multiplication of X", 367 insns: []linux.BPFInstruction{ 368 Stmt(Ld|Imm|W, 2), // A = 2 369 Stmt(Ldx|Imm|W, 3), // X = 3 370 Stmt(Alu|Mul|X, 0), // A *= X 371 Stmt(Ret|A, 0), // return A 372 }, 373 expectedRet: 6, 374 }, 375 { 376 desc: "Division by immediate", 377 insns: []linux.BPFInstruction{ 378 Stmt(Ld|Imm|W, 6), // A = 6 379 Stmt(Alu|Div|K, 3), // A /= 3 380 Stmt(Ret|A, 0), // return A 381 }, 382 expectedRet: 2, 383 }, 384 { 385 desc: "Division by X", 386 insns: []linux.BPFInstruction{ 387 Stmt(Ld|Imm|W, 6), // A = 6 388 Stmt(Ldx|Imm|W, 3), // X = 3 389 Stmt(Alu|Div|X, 0), // A /= X 390 Stmt(Ret|A, 0), // return A 391 }, 392 expectedRet: 2, 393 }, 394 { 395 desc: "Modulo immediate", 396 insns: []linux.BPFInstruction{ 397 Stmt(Ld|Imm|W, 17), // A = 17 398 Stmt(Alu|Mod|K, 7), // A %= 7 399 Stmt(Ret|A, 0), // return A 400 }, 401 expectedRet: 3, 402 }, 403 { 404 desc: "Modulo X", 405 insns: []linux.BPFInstruction{ 406 Stmt(Ld|Imm|W, 17), // A = 17 407 Stmt(Ldx|Imm|W, 7), // X = 7 408 Stmt(Alu|Mod|X, 0), // A %= X 409 Stmt(Ret|A, 0), // return A 410 }, 411 expectedRet: 3, 412 }, 413 { 414 desc: "Arithmetic negation", 415 insns: []linux.BPFInstruction{ 416 Stmt(Ld|Imm|W, 1), // A = 1 417 Stmt(Alu|Neg, 0), // A = -A 418 Stmt(Ret|A, 0), // return A 419 }, 420 expectedRet: 0xffffffff, 421 }, 422 { 423 desc: "Bitwise OR with immediate", 424 insns: []linux.BPFInstruction{ 425 Stmt(Ld|Imm|W, 0xff00aa55), // A = 0xff00aa55 426 Stmt(Alu|Or|K, 0xff0055aa), // A |= 0xff0055aa 427 Stmt(Ret|A, 0), // return A 428 }, 429 expectedRet: 0xff00ffff, 430 }, 431 { 432 desc: "Bitwise OR with X", 433 insns: []linux.BPFInstruction{ 434 Stmt(Ld|Imm|W, 0xff00aa55), // A = 0xff00aa55 435 Stmt(Ldx|Imm|W, 0xff0055aa), // X = 0xff0055aa 436 Stmt(Alu|Or|X, 0), // A |= X 437 Stmt(Ret|A, 0), // return A 438 }, 439 expectedRet: 0xff00ffff, 440 }, 441 { 442 desc: "Bitwise AND with immediate", 443 insns: []linux.BPFInstruction{ 444 Stmt(Ld|Imm|W, 0xff00aa55), // A = 0xff00aa55 445 Stmt(Alu|And|K, 0xff0055aa), // A &= 0xff0055aa 446 Stmt(Ret|A, 0), // return A 447 }, 448 expectedRet: 0xff000000, 449 }, 450 { 451 desc: "Bitwise AND with X", 452 insns: []linux.BPFInstruction{ 453 Stmt(Ld|Imm|W, 0xff00aa55), // A = 0xff00aa55 454 Stmt(Ldx|Imm|W, 0xff0055aa), // X = 0xff0055aa 455 Stmt(Alu|And|X, 0), // A &= X 456 Stmt(Ret|A, 0), // return A 457 }, 458 expectedRet: 0xff000000, 459 }, 460 { 461 desc: "Bitwise XOR with immediate", 462 insns: []linux.BPFInstruction{ 463 Stmt(Ld|Imm|W, 0xff00aa55), // A = 0xff00aa55 464 Stmt(Alu|Xor|K, 0xff0055aa), // A ^= 0xff0055aa 465 Stmt(Ret|A, 0), // return A 466 }, 467 expectedRet: 0x0000ffff, 468 }, 469 { 470 desc: "Bitwise XOR with X", 471 insns: []linux.BPFInstruction{ 472 Stmt(Ld|Imm|W, 0xff00aa55), // A = 0xff00aa55 473 Stmt(Ldx|Imm|W, 0xff0055aa), // X = 0xff0055aa 474 Stmt(Alu|Xor|X, 0), // A ^= X 475 Stmt(Ret|A, 0), // return A 476 }, 477 expectedRet: 0x0000ffff, 478 }, 479 { 480 desc: "Left shift by immediate", 481 insns: []linux.BPFInstruction{ 482 Stmt(Ld|Imm|W, 1), // A = 1 483 Stmt(Alu|Lsh|K, 5), // A <<= 5 484 Stmt(Ret|A, 0), // return A 485 }, 486 expectedRet: 32, 487 }, 488 { 489 desc: "Left shift by X", 490 insns: []linux.BPFInstruction{ 491 Stmt(Ld|Imm|W, 1), // A = 1 492 Stmt(Ldx|Imm|W, 5), // X = 5 493 Stmt(Alu|Lsh|X, 0), // A <<= X 494 Stmt(Ret|A, 0), // return A 495 }, 496 expectedRet: 32, 497 }, 498 { 499 desc: "Right shift by immediate", 500 insns: []linux.BPFInstruction{ 501 Stmt(Ld|Imm|W, 0xffffffff), // A = 0xffffffff 502 Stmt(Alu|Rsh|K, 31), // A >>= 31 503 Stmt(Ret|A, 0), // return A 504 }, 505 expectedRet: 1, 506 }, 507 { 508 desc: "Right shift by X", 509 insns: []linux.BPFInstruction{ 510 Stmt(Ld|Imm|W, 0xffffffff), // A = 0xffffffff 511 Stmt(Ldx|Imm|W, 31), // X = 31 512 Stmt(Alu|Rsh|X, 0), // A >>= X 513 Stmt(Ret|A, 0), // return A 514 }, 515 expectedRet: 1, 516 }, 517 { 518 desc: "Unconditional jump", 519 insns: []linux.BPFInstruction{ 520 Jump(Jmp|Ja, 1, 0, 0), // jmp nextpc+1 521 Stmt(Ret|K, 0), // return 0 522 Stmt(Ret|K, 1), // return 1 523 }, 524 expectedRet: 1, 525 }, 526 { 527 desc: "Jump when A == immediate", 528 insns: []linux.BPFInstruction{ 529 Stmt(Ld|Imm|W, 42), // A = 42 530 Jump(Jmp|Jeq|K, 42, 1, 2), // if (A == 42) jmp nextpc+1 else jmp nextpc+2 531 Stmt(Ret|K, 0), // return 0 532 Stmt(Ret|K, 1), // return 1 533 Stmt(Ret|K, 2), // return 2 534 }, 535 expectedRet: 1, 536 }, 537 { 538 desc: "Jump when A != immediate", 539 insns: []linux.BPFInstruction{ 540 Jump(Jmp|Jeq|K, 42, 1, 2), // if (A == 42) jmp nextpc+1 else jmp nextpc+2 541 Stmt(Ret|K, 0), // return 0 542 Stmt(Ret|K, 1), // return 1 543 Stmt(Ret|K, 2), // return 2 544 }, 545 expectedRet: 2, 546 }, 547 { 548 desc: "Jump when A == X", 549 insns: []linux.BPFInstruction{ 550 Stmt(Ld|Imm|W, 42), // A = 42 551 Stmt(Ldx|Imm|W, 42), // X = 42 552 Jump(Jmp|Jeq|X, 0, 1, 2), // if (A == X) jmp nextpc+1 else jmp nextpc+2 553 Stmt(Ret|K, 0), // return 0 554 Stmt(Ret|K, 1), // return 1 555 Stmt(Ret|K, 2), // return 2 556 }, 557 expectedRet: 1, 558 }, 559 { 560 desc: "Jump when A != X", 561 insns: []linux.BPFInstruction{ 562 Stmt(Ld|Imm|W, 42), // A = 42 563 Jump(Jmp|Jeq|X, 0, 1, 2), // if (A == X) jmp nextpc+1 else jmp nextpc+2 564 Stmt(Ret|K, 0), // return 0 565 Stmt(Ret|K, 1), // return 1 566 Stmt(Ret|K, 2), // return 2 567 }, 568 expectedRet: 2, 569 }, 570 { 571 desc: "Jump when A > immediate", 572 insns: []linux.BPFInstruction{ 573 Stmt(Ld|Imm|W, 10), // A = 10 574 Jump(Jmp|Jgt|K, 9, 1, 2), // if (A > 9) jmp nextpc+1 else jmp nextpc+2 575 Stmt(Ret|K, 0), // return 0 576 Stmt(Ret|K, 1), // return 1 577 Stmt(Ret|K, 2), // return 2 578 }, 579 expectedRet: 1, 580 }, 581 { 582 desc: "Jump when A <= immediate", 583 insns: []linux.BPFInstruction{ 584 Stmt(Ld|Imm|W, 10), // A = 10 585 Jump(Jmp|Jgt|K, 10, 1, 2), // if (A > 10) jmp nextpc+1 else jmp nextpc+2 586 Stmt(Ret|K, 0), // return 0 587 Stmt(Ret|K, 1), // return 1 588 Stmt(Ret|K, 2), // return 2 589 }, 590 expectedRet: 2, 591 }, 592 { 593 desc: "Jump when A > X", 594 insns: []linux.BPFInstruction{ 595 Stmt(Ld|Imm|W, 10), // A = 10 596 Stmt(Ldx|Imm|W, 9), // X = 9 597 Jump(Jmp|Jgt|X, 0, 1, 2), // if (A > X) jmp nextpc+1 else jmp nextpc+2 598 Stmt(Ret|K, 0), // return 0 599 Stmt(Ret|K, 1), // return 1 600 Stmt(Ret|K, 2), // return 2 601 }, 602 expectedRet: 1, 603 }, 604 { 605 desc: "Jump when A <= X", 606 insns: []linux.BPFInstruction{ 607 Stmt(Ld|Imm|W, 10), // A = 10 608 Stmt(Ldx|Imm|W, 10), // X = 10 609 Jump(Jmp|Jgt|X, 0, 1, 2), // if (A > X) jmp nextpc+1 else jmp nextpc+2 610 Stmt(Ret|K, 0), // return 0 611 Stmt(Ret|K, 1), // return 1 612 Stmt(Ret|K, 2), // return 2 613 }, 614 expectedRet: 2, 615 }, 616 { 617 desc: "Jump when A >= immediate", 618 insns: []linux.BPFInstruction{ 619 Stmt(Ld|Imm|W, 10), // A = 10 620 Jump(Jmp|Jge|K, 10, 1, 2), // if (A >= 10) jmp nextpc+1 else jmp nextpc+2 621 Stmt(Ret|K, 0), // return 0 622 Stmt(Ret|K, 1), // return 1 623 Stmt(Ret|K, 2), // return 2 624 }, 625 expectedRet: 1, 626 }, 627 { 628 desc: "Jump when A < immediate", 629 insns: []linux.BPFInstruction{ 630 Stmt(Ld|Imm|W, 10), // A = 10 631 Jump(Jmp|Jge|K, 11, 1, 2), // if (A >= 11) jmp nextpc+1 else jmp nextpc+2 632 Stmt(Ret|K, 0), // return 0 633 Stmt(Ret|K, 1), // return 1 634 Stmt(Ret|K, 2), // return 2 635 }, 636 expectedRet: 2, 637 }, 638 { 639 desc: "Jump when A >= X", 640 insns: []linux.BPFInstruction{ 641 Stmt(Ld|Imm|W, 10), // A = 10 642 Stmt(Ldx|Imm|W, 10), // X = 10 643 Jump(Jmp|Jge|X, 0, 1, 2), // if (A >= X) jmp nextpc+1 else jmp nextpc+2 644 Stmt(Ret|K, 0), // return 0 645 Stmt(Ret|K, 1), // return 1 646 Stmt(Ret|K, 2), // return 2 647 }, 648 expectedRet: 1, 649 }, 650 { 651 desc: "Jump when A < X", 652 insns: []linux.BPFInstruction{ 653 Stmt(Ld|Imm|W, 10), // A = 10 654 Stmt(Ldx|Imm|W, 11), // X = 11 655 Jump(Jmp|Jge|X, 0, 1, 2), // if (A >= X) jmp nextpc+1 else jmp nextpc+2 656 Stmt(Ret|K, 0), // return 0 657 Stmt(Ret|K, 1), // return 1 658 Stmt(Ret|K, 2), // return 2 659 }, 660 expectedRet: 2, 661 }, 662 { 663 desc: "Jump when A & immediate != 0", 664 insns: []linux.BPFInstruction{ 665 Stmt(Ld|Imm|W, 0xff), // A = 0xff 666 Jump(Jmp|Jset|K, 0x101, 1, 2), // if (A & 0x101) jmp nextpc+1 else jmp nextpc+2 667 Stmt(Ret|K, 0), // return 0 668 Stmt(Ret|K, 1), // return 1 669 Stmt(Ret|K, 2), // return 2 670 }, 671 expectedRet: 1, 672 }, 673 { 674 desc: "Jump when A & immediate == 0", 675 insns: []linux.BPFInstruction{ 676 Stmt(Ld|Imm|W, 0xfe), // A = 0xfe 677 Jump(Jmp|Jset|K, 0x101, 1, 2), // if (A & 0x101) jmp nextpc+1 else jmp nextpc+2 678 Stmt(Ret|K, 0), // return 0 679 Stmt(Ret|K, 1), // return 1 680 Stmt(Ret|K, 2), // return 2 681 }, 682 expectedRet: 2, 683 }, 684 { 685 desc: "Jump when A & X != 0", 686 insns: []linux.BPFInstruction{ 687 Stmt(Ld|Imm|W, 0xff), // A = 0xff 688 Stmt(Ldx|Imm|W, 0x101), // X = 0x101 689 Jump(Jmp|Jset|X, 0, 1, 2), // if (A & X) jmp nextpc+1 else jmp nextpc+2 690 Stmt(Ret|K, 0), // return 0 691 Stmt(Ret|K, 1), // return 1 692 Stmt(Ret|K, 2), // return 2 693 }, 694 expectedRet: 1, 695 }, 696 { 697 desc: "Jump when A & X == 0", 698 insns: []linux.BPFInstruction{ 699 Stmt(Ld|Imm|W, 0xfe), // A = 0xfe 700 Stmt(Ldx|Imm|W, 0x101), // X = 0x101 701 Jump(Jmp|Jset|X, 0, 1, 2), // if (A & X) jmp nextpc+1 else jmp nextpc+2 702 Stmt(Ret|K, 0), // return 0 703 Stmt(Ret|K, 1), // return 1 704 Stmt(Ret|K, 2), // return 2 705 }, 706 expectedRet: 2, 707 }, 708 } { 709 p, err := Compile(test.insns) 710 if err != nil { 711 t.Errorf("%s: unexpected compilation error: %v", test.desc, err) 712 continue 713 } 714 ret, err := Exec(p, InputBytes{test.input, binary.BigEndian}) 715 if err != nil { 716 t.Errorf("%s: expected return value of %d, got execution error: %v", test.desc, test.expectedRet, err) 717 continue 718 } 719 if ret != test.expectedRet { 720 t.Errorf("%s: expected return value of %d, got value %d", test.desc, test.expectedRet, ret) 721 } 722 } 723 } 724 725 func TestSimpleFilter(t *testing.T) { 726 // Seccomp filter example given in Linux's 727 // Documentation/networking/filter.txt, translated to bytecode using the 728 // Linux kernel tree's tools/net/bpf_asm. 729 filter := []linux.BPFInstruction{ 730 {0x20, 0, 0, 0x00000004}, // ld [4] /* offsetof(struct seccomp_data, arch) */ 731 {0x15, 0, 11, 0xc000003e}, // jne #0xc000003e, bad /* AUDIT_ARCH_X86_64 */ 732 {0x20, 0, 0, 0000000000}, // ld [0] /* offsetof(struct seccomp_data, nr) */ 733 {0x15, 10, 0, 0x0000000f}, // jeq #15, good /* __NR_rt_sigreturn */ 734 {0x15, 9, 0, 0x000000e7}, // jeq #231, good /* __NR_exit_group */ 735 {0x15, 8, 0, 0x0000003c}, // jeq #60, good /* __NR_exit */ 736 {0x15, 7, 0, 0000000000}, // jeq #0, good /* __NR_read */ 737 {0x15, 6, 0, 0x00000001}, // jeq #1, good /* __NR_write */ 738 {0x15, 5, 0, 0x00000005}, // jeq #5, good /* __NR_fstat */ 739 {0x15, 4, 0, 0x00000009}, // jeq #9, good /* __NR_mmap */ 740 {0x15, 3, 0, 0x0000000e}, // jeq #14, good /* __NR_rt_sigprocmask */ 741 {0x15, 2, 0, 0x0000000d}, // jeq #13, good /* __NR_rt_sigaction */ 742 {0x15, 1, 0, 0x00000023}, // jeq #35, good /* __NR_nanosleep */ 743 {0x06, 0, 0, 0000000000}, // bad: ret #0 /* SECCOMP_RET_KILL */ 744 {0x06, 0, 0, 0x7fff0000}, // good: ret #0x7fff0000 /* SECCOMP_RET_ALLOW */ 745 } 746 p, err := Compile(filter) 747 if err != nil { 748 t.Fatalf("Unexpected compilation error: %v", err) 749 } 750 751 for _, test := range []struct { 752 // desc is the test's description. 753 desc string 754 755 // SeccompData is the input data. 756 data linux.SeccompData 757 758 // expectedRet is the expected return value of the BPF program. 759 expectedRet uint32 760 }{ 761 { 762 desc: "Invalid arch is rejected", 763 data: linux.SeccompData{Nr: 1 /* x86 exit */, Arch: 0x40000003 /* AUDIT_ARCH_I386 */}, 764 expectedRet: 0, 765 }, 766 { 767 desc: "Disallowed syscall is rejected", 768 data: linux.SeccompData{Nr: 105 /* __NR_setuid */, Arch: 0xc000003e}, 769 expectedRet: 0, 770 }, 771 { 772 desc: "Allowed syscall is indeed allowed", 773 data: linux.SeccompData{Nr: 231 /* __NR_exit_group */, Arch: 0xc000003e}, 774 expectedRet: 0x7fff0000, 775 }, 776 } { 777 ret, err := Exec(p, dataAsInput(&test.data)) 778 if err != nil { 779 t.Errorf("%s: expected return value of %d, got execution error: %v", test.desc, test.expectedRet, err) 780 continue 781 } 782 if ret != test.expectedRet { 783 t.Errorf("%s: expected return value of %d, got value %d", test.desc, test.expectedRet, ret) 784 } 785 } 786 } 787 788 // seccompData is equivalent to struct seccomp_data. 789 type seccompData struct { 790 nr uint32 791 arch uint32 792 instructionPointer uint64 793 args [6]uint64 794 } 795 796 // asInput converts a seccompData to a bpf.Input. 797 func dataAsInput(data *linux.SeccompData) Input { 798 return InputBytes{marshal.Marshal(data), hostarch.ByteOrder} 799 }