gvisor.dev/gvisor@v0.0.0-20240520182842-f9d4d51c7e0f/pkg/seccomp/seccomp_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 seccomp 16 17 import ( 18 "bytes" 19 _ "embed" 20 "fmt" 21 "io" 22 "io/ioutil" 23 "math" 24 "math/rand" 25 "os" 26 "os/exec" 27 "reflect" 28 "strings" 29 "testing" 30 "time" 31 32 "golang.org/x/sys/unix" 33 "gvisor.dev/gvisor/pkg/abi/linux" 34 "gvisor.dev/gvisor/pkg/bpf" 35 ) 36 37 //go:embed victim 38 var victimData []byte 39 40 // newVictim makes a victim binary. 41 func newVictim() (string, error) { 42 f, err := ioutil.TempFile("", "victim") 43 if err != nil { 44 return "", err 45 } 46 defer f.Close() 47 path := f.Name() 48 if _, err := io.Copy(f, bytes.NewBuffer(victimData)); err != nil { 49 os.Remove(path) 50 return "", err 51 } 52 if err := os.Chmod(path, 0755); err != nil { 53 os.Remove(path) 54 return "", err 55 } 56 return path, nil 57 } 58 59 func TestBasic(t *testing.T) { 60 buf := make([]byte, (&linux.SeccompData{}).SizeBytes()) 61 62 type spec struct { 63 // desc is the test's description. 64 desc string 65 66 // data is the input data. 67 data linux.SeccompData 68 69 // want is the expected return value of the BPF program. 70 want linux.BPFAction 71 } 72 73 for _, test := range []struct { 74 name string 75 ruleSets []RuleSet 76 wantPanic bool 77 options ProgramOptions 78 specs []spec 79 }{ 80 { 81 name: "Single syscall", 82 ruleSets: []RuleSet{ 83 { 84 Rules: MakeSyscallRules(map[uintptr]SyscallRule{1: MatchAll{}}), 85 Action: linux.SECCOMP_RET_ALLOW, 86 }, 87 }, 88 options: ProgramOptions{ 89 DefaultAction: linux.SECCOMP_RET_TRAP, 90 BadArchAction: linux.SECCOMP_RET_KILL_THREAD, 91 }, 92 specs: []spec{ 93 { 94 desc: "syscall allowed", 95 data: linux.SeccompData{Nr: 1, Arch: LINUX_AUDIT_ARCH}, 96 want: linux.SECCOMP_RET_ALLOW, 97 }, 98 { 99 desc: "syscall disallowed", 100 data: linux.SeccompData{Nr: 2, Arch: LINUX_AUDIT_ARCH}, 101 want: linux.SECCOMP_RET_TRAP, 102 }, 103 }, 104 }, 105 { 106 name: "Multiple rulesets", 107 ruleSets: []RuleSet{ 108 { 109 Rules: MakeSyscallRules(map[uintptr]SyscallRule{ 110 1: PerArg{ 111 EqualTo(0x1), 112 }, 113 }), 114 Action: linux.SECCOMP_RET_ALLOW, 115 }, 116 { 117 Rules: MakeSyscallRules(map[uintptr]SyscallRule{ 118 1: MatchAll{}, 119 2: MatchAll{}, 120 }), 121 Action: linux.SECCOMP_RET_TRAP, 122 }, 123 }, 124 options: ProgramOptions{ 125 DefaultAction: linux.SECCOMP_RET_KILL_THREAD, 126 BadArchAction: linux.SECCOMP_RET_KILL_THREAD, 127 }, 128 specs: []spec{ 129 { 130 desc: "allowed (1a)", 131 data: linux.SeccompData{Nr: 1, Arch: LINUX_AUDIT_ARCH, Args: [6]uint64{0x1}}, 132 want: linux.SECCOMP_RET_ALLOW, 133 }, 134 { 135 desc: "allowed (1b)", 136 data: linux.SeccompData{Nr: 1, Arch: LINUX_AUDIT_ARCH}, 137 want: linux.SECCOMP_RET_TRAP, 138 }, 139 { 140 desc: "syscall 1 matched 2nd rule", 141 data: linux.SeccompData{Nr: 1, Arch: LINUX_AUDIT_ARCH}, 142 want: linux.SECCOMP_RET_TRAP, 143 }, 144 { 145 desc: "no match", 146 data: linux.SeccompData{Nr: 0, Arch: LINUX_AUDIT_ARCH}, 147 want: linux.SECCOMP_RET_KILL_THREAD, 148 }, 149 }, 150 }, 151 { 152 name: "Multiple syscalls", 153 ruleSets: []RuleSet{ 154 { 155 Rules: MakeSyscallRules(map[uintptr]SyscallRule{ 156 1: MatchAll{}, 157 3: MatchAll{}, 158 5: MatchAll{}, 159 }), 160 Action: linux.SECCOMP_RET_ALLOW, 161 }, 162 }, 163 options: ProgramOptions{ 164 DefaultAction: linux.SECCOMP_RET_TRAP, 165 BadArchAction: linux.SECCOMP_RET_KILL_THREAD, 166 }, 167 specs: []spec{ 168 { 169 desc: "allowed (1)", 170 data: linux.SeccompData{Nr: 1, Arch: LINUX_AUDIT_ARCH}, 171 want: linux.SECCOMP_RET_ALLOW, 172 }, 173 { 174 desc: "allowed (3)", 175 data: linux.SeccompData{Nr: 3, Arch: LINUX_AUDIT_ARCH}, 176 want: linux.SECCOMP_RET_ALLOW, 177 }, 178 { 179 desc: "allowed (5)", 180 data: linux.SeccompData{Nr: 5, Arch: LINUX_AUDIT_ARCH}, 181 want: linux.SECCOMP_RET_ALLOW, 182 }, 183 { 184 desc: "disallowed (0)", 185 data: linux.SeccompData{Nr: 0, Arch: LINUX_AUDIT_ARCH}, 186 want: linux.SECCOMP_RET_TRAP, 187 }, 188 { 189 desc: "disallowed (2)", 190 data: linux.SeccompData{Nr: 2, Arch: LINUX_AUDIT_ARCH}, 191 want: linux.SECCOMP_RET_TRAP, 192 }, 193 { 194 desc: "disallowed (4)", 195 data: linux.SeccompData{Nr: 4, Arch: LINUX_AUDIT_ARCH}, 196 want: linux.SECCOMP_RET_TRAP, 197 }, 198 { 199 desc: "disallowed (6)", 200 data: linux.SeccompData{Nr: 6, Arch: LINUX_AUDIT_ARCH}, 201 want: linux.SECCOMP_RET_TRAP, 202 }, 203 { 204 desc: "disallowed (100)", 205 data: linux.SeccompData{Nr: 100, Arch: LINUX_AUDIT_ARCH}, 206 want: linux.SECCOMP_RET_TRAP, 207 }, 208 }, 209 }, 210 { 211 name: "Wrong architecture", 212 ruleSets: []RuleSet{ 213 { 214 Rules: MakeSyscallRules(map[uintptr]SyscallRule{ 215 1: MatchAll{}, 216 }), 217 Action: linux.SECCOMP_RET_ALLOW, 218 }, 219 }, 220 options: ProgramOptions{ 221 DefaultAction: linux.SECCOMP_RET_TRAP, 222 BadArchAction: linux.SECCOMP_RET_KILL_THREAD, 223 }, 224 specs: []spec{ 225 { 226 desc: "arch (123)", 227 data: linux.SeccompData{Nr: 1, Arch: 123}, 228 want: linux.SECCOMP_RET_KILL_THREAD, 229 }, 230 }, 231 }, 232 { 233 name: "Syscall disallowed", 234 ruleSets: []RuleSet{ 235 { 236 Rules: MakeSyscallRules(map[uintptr]SyscallRule{ 237 1: MatchAll{}, 238 }), 239 Action: linux.SECCOMP_RET_ALLOW, 240 }, 241 }, 242 options: ProgramOptions{ 243 DefaultAction: linux.SECCOMP_RET_TRAP, 244 BadArchAction: linux.SECCOMP_RET_KILL_THREAD, 245 }, 246 specs: []spec{ 247 { 248 desc: "action trap", 249 data: linux.SeccompData{Nr: 2, Arch: LINUX_AUDIT_ARCH}, 250 want: linux.SECCOMP_RET_TRAP, 251 }, 252 }, 253 }, 254 { 255 name: "Syscall arguments", 256 ruleSets: []RuleSet{ 257 { 258 Rules: MakeSyscallRules(map[uintptr]SyscallRule{ 259 1: PerArg{ 260 AnyValue{}, 261 EqualTo(0xf), 262 }, 263 }), 264 Action: linux.SECCOMP_RET_ALLOW, 265 }, 266 }, 267 options: ProgramOptions{ 268 DefaultAction: linux.SECCOMP_RET_TRAP, 269 BadArchAction: linux.SECCOMP_RET_KILL_THREAD, 270 }, 271 specs: []spec{ 272 { 273 desc: "allowed", 274 data: linux.SeccompData{Nr: 1, Arch: LINUX_AUDIT_ARCH, Args: [6]uint64{0xf, 0xf}}, 275 want: linux.SECCOMP_RET_ALLOW, 276 }, 277 { 278 desc: "disallowed", 279 data: linux.SeccompData{Nr: 1, Arch: LINUX_AUDIT_ARCH, Args: [6]uint64{0xf, 0xe}}, 280 want: linux.SECCOMP_RET_TRAP, 281 }, 282 }, 283 }, 284 { 285 name: "Multiple arguments", 286 ruleSets: []RuleSet{ 287 { 288 Rules: MakeSyscallRules(map[uintptr]SyscallRule{ 289 1: Or{ 290 PerArg{ 291 EqualTo(0xf), 292 }, 293 PerArg{ 294 EqualTo(0xe), 295 }, 296 }, 297 }), 298 Action: linux.SECCOMP_RET_ALLOW, 299 }, 300 }, 301 options: ProgramOptions{ 302 DefaultAction: linux.SECCOMP_RET_TRAP, 303 BadArchAction: linux.SECCOMP_RET_KILL_THREAD, 304 }, 305 specs: []spec{ 306 { 307 desc: "match first rule", 308 data: linux.SeccompData{Nr: 1, Arch: LINUX_AUDIT_ARCH, Args: [6]uint64{0xf}}, 309 want: linux.SECCOMP_RET_ALLOW, 310 }, 311 { 312 desc: "match 2nd rule", 313 data: linux.SeccompData{Nr: 1, Arch: LINUX_AUDIT_ARCH, Args: [6]uint64{0xe}}, 314 want: linux.SECCOMP_RET_ALLOW, 315 }, 316 { 317 desc: "match neither rule", 318 data: linux.SeccompData{Nr: 1, Arch: LINUX_AUDIT_ARCH, Args: [6]uint64{0xd}}, 319 want: linux.SECCOMP_RET_TRAP, 320 }, 321 }, 322 }, 323 { 324 name: "empty Or is invalid", 325 ruleSets: []RuleSet{ 326 { 327 Rules: MakeSyscallRules(map[uintptr]SyscallRule{ 328 1: Or{}, 329 }), 330 Action: linux.SECCOMP_RET_ALLOW, 331 }, 332 }, 333 wantPanic: true, 334 }, 335 { 336 name: "And of multiple rules", 337 ruleSets: []RuleSet{ 338 { 339 Rules: MakeSyscallRules(map[uintptr]SyscallRule{ 340 1: And{ 341 PerArg{ 342 NotEqual(0xf), 343 }, 344 PerArg{ 345 NotEqual(0xe), 346 }, 347 }, 348 }), 349 Action: linux.SECCOMP_RET_ALLOW, 350 }, 351 }, 352 options: ProgramOptions{ 353 DefaultAction: linux.SECCOMP_RET_TRAP, 354 BadArchAction: linux.SECCOMP_RET_KILL_THREAD, 355 }, 356 specs: []spec{ 357 { 358 desc: "hit first rule", 359 data: linux.SeccompData{Nr: 1, Arch: LINUX_AUDIT_ARCH, Args: [6]uint64{0xf}}, 360 want: linux.SECCOMP_RET_TRAP, 361 }, 362 { 363 desc: "hit 2nd rule", 364 data: linux.SeccompData{Nr: 1, Arch: LINUX_AUDIT_ARCH, Args: [6]uint64{0xe}}, 365 want: linux.SECCOMP_RET_TRAP, 366 }, 367 { 368 desc: "hit neither rule", 369 data: linux.SeccompData{Nr: 1, Arch: LINUX_AUDIT_ARCH, Args: [6]uint64{0xd}}, 370 want: linux.SECCOMP_RET_ALLOW, 371 }, 372 }, 373 }, 374 { 375 name: "empty And is invalid", 376 ruleSets: []RuleSet{ 377 { 378 Rules: MakeSyscallRules(map[uintptr]SyscallRule{ 379 1: And{}, 380 }), 381 Action: linux.SECCOMP_RET_ALLOW, 382 }, 383 }, 384 wantPanic: true, 385 }, 386 { 387 name: "EqualTo", 388 ruleSets: []RuleSet{ 389 { 390 Rules: MakeSyscallRules(map[uintptr]SyscallRule{ 391 1: PerArg{ 392 EqualTo(0), 393 EqualTo(math.MaxUint64 - 1), 394 EqualTo(math.MaxUint32), 395 }, 396 }), 397 Action: linux.SECCOMP_RET_ALLOW, 398 }, 399 }, 400 options: ProgramOptions{ 401 DefaultAction: linux.SECCOMP_RET_TRAP, 402 BadArchAction: linux.SECCOMP_RET_KILL_THREAD, 403 }, 404 specs: []spec{ 405 { 406 desc: "argument allowed (all match)", 407 data: linux.SeccompData{ 408 Nr: 1, 409 Arch: LINUX_AUDIT_ARCH, 410 Args: [6]uint64{0, math.MaxUint64 - 1, math.MaxUint32}, 411 }, 412 want: linux.SECCOMP_RET_ALLOW, 413 }, 414 { 415 desc: "argument disallowed (one mismatch)", 416 data: linux.SeccompData{ 417 Nr: 1, 418 Arch: LINUX_AUDIT_ARCH, 419 Args: [6]uint64{0, math.MaxUint64, math.MaxUint32}, 420 }, 421 want: linux.SECCOMP_RET_TRAP, 422 }, 423 { 424 desc: "argument disallowed (multiple mismatch)", 425 data: linux.SeccompData{ 426 Nr: 1, 427 Arch: LINUX_AUDIT_ARCH, 428 Args: [6]uint64{0, math.MaxUint64, math.MaxUint32 - 1}, 429 }, 430 want: linux.SECCOMP_RET_TRAP, 431 }, 432 }, 433 }, 434 { 435 name: "NotEqual", 436 ruleSets: []RuleSet{ 437 { 438 Rules: MakeSyscallRules(map[uintptr]SyscallRule{ 439 1: PerArg{ 440 NotEqual(0x7aabbccdd), 441 NotEqual(math.MaxUint64 - 1), 442 NotEqual(math.MaxUint32), 443 }, 444 }), 445 Action: linux.SECCOMP_RET_ALLOW, 446 }, 447 }, 448 options: ProgramOptions{ 449 DefaultAction: linux.SECCOMP_RET_TRAP, 450 BadArchAction: linux.SECCOMP_RET_KILL_THREAD, 451 }, 452 specs: []spec{ 453 { 454 desc: "arg allowed", 455 data: linux.SeccompData{ 456 Nr: 1, 457 Arch: LINUX_AUDIT_ARCH, 458 Args: [6]uint64{0, math.MaxUint64, math.MaxUint32 - 1}, 459 }, 460 want: linux.SECCOMP_RET_ALLOW, 461 }, 462 { 463 desc: "arg disallowed (one equal)", 464 data: linux.SeccompData{ 465 Nr: 1, 466 Arch: LINUX_AUDIT_ARCH, 467 Args: [6]uint64{0x7aabbccdd, math.MaxUint64, math.MaxUint32 - 1}, 468 }, 469 want: linux.SECCOMP_RET_TRAP, 470 }, 471 { 472 desc: "arg disallowed (all equal)", 473 data: linux.SeccompData{ 474 Nr: 1, 475 Arch: LINUX_AUDIT_ARCH, 476 Args: [6]uint64{0x7aabbccdd, math.MaxUint64 - 1, math.MaxUint32}, 477 }, 478 want: linux.SECCOMP_RET_TRAP, 479 }, 480 }, 481 }, 482 { 483 name: "GreaterThan", 484 ruleSets: []RuleSet{ 485 { 486 Rules: MakeSyscallRules(map[uintptr]SyscallRule{ 487 1: PerArg{ 488 // 4294967298 489 // Both upper 32 bits and lower 32 bits are non-zero. 490 // 00000000000000000000000000000010 491 // 00000000000000000000000000000010 492 GreaterThan(0x00000002_00000002), 493 }, 494 }), 495 Action: linux.SECCOMP_RET_ALLOW, 496 }, 497 }, 498 options: ProgramOptions{ 499 DefaultAction: linux.SECCOMP_RET_TRAP, 500 BadArchAction: linux.SECCOMP_RET_KILL_THREAD, 501 }, 502 specs: []spec{ 503 { 504 desc: "high 32bits greater", 505 data: linux.SeccompData{Nr: 1, Arch: LINUX_AUDIT_ARCH, Args: [6]uint64{0x00000003_00000002}}, 506 want: linux.SECCOMP_RET_ALLOW, 507 }, 508 { 509 desc: "high 32bits equal, low 32bits greater", 510 data: linux.SeccompData{Nr: 1, Arch: LINUX_AUDIT_ARCH, Args: [6]uint64{0x00000002_00000003}}, 511 want: linux.SECCOMP_RET_ALLOW, 512 }, 513 { 514 desc: "high 32bits equal, low 32bits equal", 515 data: linux.SeccompData{Nr: 1, Arch: LINUX_AUDIT_ARCH, Args: [6]uint64{0x00000002_00000002}}, 516 want: linux.SECCOMP_RET_TRAP, 517 }, 518 { 519 desc: "high 32bits equal, low 32bits less", 520 data: linux.SeccompData{Nr: 1, Arch: LINUX_AUDIT_ARCH, Args: [6]uint64{0x00000002_00000001}}, 521 want: linux.SECCOMP_RET_TRAP, 522 }, 523 { 524 desc: "high 32bits less", 525 data: linux.SeccompData{Nr: 1, Arch: LINUX_AUDIT_ARCH, Args: [6]uint64{0x00000001_00000003}}, 526 want: linux.SECCOMP_RET_TRAP, 527 }, 528 }, 529 }, 530 { 531 name: "GreaterThan (multi)", 532 ruleSets: []RuleSet{ 533 { 534 Rules: MakeSyscallRules(map[uintptr]SyscallRule{ 535 1: PerArg{ 536 GreaterThan(0xf), 537 GreaterThan(0xabcd000d), 538 }, 539 }), 540 Action: linux.SECCOMP_RET_ALLOW, 541 }, 542 }, 543 options: ProgramOptions{ 544 DefaultAction: linux.SECCOMP_RET_TRAP, 545 BadArchAction: linux.SECCOMP_RET_KILL_THREAD, 546 }, 547 specs: []spec{ 548 { 549 desc: "arg allowed", 550 data: linux.SeccompData{Nr: 1, Arch: LINUX_AUDIT_ARCH, Args: [6]uint64{0x10, 0xffffffff}}, 551 want: linux.SECCOMP_RET_ALLOW, 552 }, 553 { 554 desc: "arg disallowed (first arg equal)", 555 data: linux.SeccompData{Nr: 1, Arch: LINUX_AUDIT_ARCH, Args: [6]uint64{0xf, 0xffffffff}}, 556 want: linux.SECCOMP_RET_TRAP, 557 }, 558 { 559 desc: "arg disallowed (first arg smaller)", 560 data: linux.SeccompData{Nr: 1, Arch: LINUX_AUDIT_ARCH, Args: [6]uint64{0x0, 0xffffffff}}, 561 want: linux.SECCOMP_RET_TRAP, 562 }, 563 { 564 desc: "arg disallowed (second arg equal)", 565 data: linux.SeccompData{Nr: 1, Arch: LINUX_AUDIT_ARCH, Args: [6]uint64{0x10, 0xabcd000d}}, 566 want: linux.SECCOMP_RET_TRAP, 567 }, 568 { 569 desc: "arg disallowed (second arg smaller)", 570 data: linux.SeccompData{Nr: 1, Arch: LINUX_AUDIT_ARCH, Args: [6]uint64{0x10, 0xa000ffff}}, 571 want: linux.SECCOMP_RET_TRAP, 572 }, 573 }, 574 }, 575 { 576 name: "GreaterThanOrEqual", 577 ruleSets: []RuleSet{ 578 { 579 Rules: MakeSyscallRules(map[uintptr]SyscallRule{ 580 1: PerArg{ 581 // 4294967298 582 // Both upper 32 bits and lower 32 bits are non-zero. 583 // 00000000000000000000000000000010 584 // 00000000000000000000000000000010 585 GreaterThanOrEqual(0x00000002_00000002), 586 }, 587 }), 588 Action: linux.SECCOMP_RET_ALLOW, 589 }, 590 }, 591 options: ProgramOptions{ 592 DefaultAction: linux.SECCOMP_RET_TRAP, 593 BadArchAction: linux.SECCOMP_RET_KILL_THREAD, 594 }, 595 specs: []spec{ 596 { 597 desc: "high 32bits greater", 598 data: linux.SeccompData{Nr: 1, Arch: LINUX_AUDIT_ARCH, Args: [6]uint64{0x00000003_00000002}}, 599 want: linux.SECCOMP_RET_ALLOW, 600 }, 601 { 602 desc: "high 32bits equal, low 32bits greater", 603 data: linux.SeccompData{Nr: 1, Arch: LINUX_AUDIT_ARCH, Args: [6]uint64{0x00000002_00000003}}, 604 want: linux.SECCOMP_RET_ALLOW, 605 }, 606 { 607 desc: "high 32bits equal, low 32bits equal", 608 data: linux.SeccompData{Nr: 1, Arch: LINUX_AUDIT_ARCH, Args: [6]uint64{0x00000002_00000002}}, 609 want: linux.SECCOMP_RET_ALLOW, 610 }, 611 { 612 desc: "high 32bits equal, low 32bits less", 613 data: linux.SeccompData{Nr: 1, Arch: LINUX_AUDIT_ARCH, Args: [6]uint64{0x00000002_00000001}}, 614 want: linux.SECCOMP_RET_TRAP, 615 }, 616 { 617 desc: "high 32bits less", 618 data: linux.SeccompData{Nr: 1, Arch: LINUX_AUDIT_ARCH, Args: [6]uint64{0x00000001_00000002}}, 619 want: linux.SECCOMP_RET_TRAP, 620 }, 621 }, 622 }, 623 { 624 name: "GreaterThanOrEqual (multi)", 625 ruleSets: []RuleSet{ 626 { 627 Rules: MakeSyscallRules(map[uintptr]SyscallRule{ 628 1: PerArg{ 629 GreaterThanOrEqual(0xf), 630 GreaterThanOrEqual(0xabcd000d), 631 }, 632 }), 633 Action: linux.SECCOMP_RET_ALLOW, 634 }, 635 }, 636 options: ProgramOptions{ 637 DefaultAction: linux.SECCOMP_RET_TRAP, 638 BadArchAction: linux.SECCOMP_RET_KILL_THREAD, 639 }, 640 specs: []spec{ 641 { 642 desc: "arg allowed (both greater)", 643 data: linux.SeccompData{Nr: 1, Arch: LINUX_AUDIT_ARCH, Args: [6]uint64{0x10, 0xffffffff}}, 644 want: linux.SECCOMP_RET_ALLOW, 645 }, 646 { 647 desc: "arg allowed (first arg equal)", 648 data: linux.SeccompData{Nr: 1, Arch: LINUX_AUDIT_ARCH, Args: [6]uint64{0xf, 0xffffffff}}, 649 want: linux.SECCOMP_RET_ALLOW, 650 }, 651 { 652 desc: "arg disallowed (first arg smaller)", 653 data: linux.SeccompData{Nr: 1, Arch: LINUX_AUDIT_ARCH, Args: [6]uint64{0x0, 0xffffffff}}, 654 want: linux.SECCOMP_RET_TRAP, 655 }, 656 { 657 desc: "arg allowed (second arg equal)", 658 data: linux.SeccompData{Nr: 1, Arch: LINUX_AUDIT_ARCH, Args: [6]uint64{0x10, 0xabcd000d}}, 659 want: linux.SECCOMP_RET_ALLOW, 660 }, 661 { 662 desc: "arg disallowed (second arg smaller)", 663 data: linux.SeccompData{Nr: 1, Arch: LINUX_AUDIT_ARCH, Args: [6]uint64{0x10, 0xa000ffff}}, 664 want: linux.SECCOMP_RET_TRAP, 665 }, 666 { 667 desc: "arg disallowed (both arg smaller)", 668 data: linux.SeccompData{Nr: 1, Arch: LINUX_AUDIT_ARCH, Args: [6]uint64{0x0, 0xa000ffff}}, 669 want: linux.SECCOMP_RET_TRAP, 670 }, 671 }, 672 }, 673 { 674 name: "LessThan", 675 ruleSets: []RuleSet{ 676 { 677 Rules: MakeSyscallRules(map[uintptr]SyscallRule{ 678 1: PerArg{ 679 // 4294967298 680 // Both upper 32 bits and lower 32 bits are non-zero. 681 // 00000000000000000000000000000010 682 // 00000000000000000000000000000010 683 LessThan(0x00000002_00000002), 684 }, 685 }), 686 Action: linux.SECCOMP_RET_ALLOW, 687 }, 688 }, 689 options: ProgramOptions{ 690 DefaultAction: linux.SECCOMP_RET_TRAP, 691 BadArchAction: linux.SECCOMP_RET_KILL_THREAD, 692 }, 693 specs: []spec{ 694 { 695 desc: "high 32bits greater", 696 data: linux.SeccompData{Nr: 1, Arch: LINUX_AUDIT_ARCH, Args: [6]uint64{0x00000003_00000002}}, 697 want: linux.SECCOMP_RET_TRAP, 698 }, 699 { 700 desc: "high 32bits equal, low 32bits greater", 701 data: linux.SeccompData{Nr: 1, Arch: LINUX_AUDIT_ARCH, Args: [6]uint64{0x00000002_00000003}}, 702 want: linux.SECCOMP_RET_TRAP, 703 }, 704 { 705 desc: "high 32bits equal, low 32bits equal", 706 data: linux.SeccompData{Nr: 1, Arch: LINUX_AUDIT_ARCH, Args: [6]uint64{0x00000002_00000002}}, 707 want: linux.SECCOMP_RET_TRAP, 708 }, 709 { 710 desc: "high 32bits equal, low 32bits less", 711 data: linux.SeccompData{Nr: 1, Arch: LINUX_AUDIT_ARCH, Args: [6]uint64{0x00000002_00000001}}, 712 want: linux.SECCOMP_RET_ALLOW, 713 }, 714 { 715 desc: "high 32bits less", 716 data: linux.SeccompData{Nr: 1, Arch: LINUX_AUDIT_ARCH, Args: [6]uint64{0x00000001_00000002}}, 717 want: linux.SECCOMP_RET_ALLOW, 718 }, 719 }, 720 }, 721 { 722 name: "LessThan (multi)", 723 ruleSets: []RuleSet{ 724 { 725 Rules: MakeSyscallRules(map[uintptr]SyscallRule{ 726 1: PerArg{ 727 LessThan(0x1), 728 LessThan(0xabcd000d), 729 }, 730 }), 731 Action: linux.SECCOMP_RET_ALLOW, 732 }, 733 }, 734 options: ProgramOptions{ 735 DefaultAction: linux.SECCOMP_RET_TRAP, 736 BadArchAction: linux.SECCOMP_RET_KILL_THREAD, 737 }, 738 specs: []spec{ 739 { 740 desc: "arg allowed", 741 data: linux.SeccompData{Nr: 1, Arch: LINUX_AUDIT_ARCH, Args: [6]uint64{0x0, 0x0}}, 742 want: linux.SECCOMP_RET_ALLOW, 743 }, 744 { 745 desc: "arg disallowed (first arg equal)", 746 data: linux.SeccompData{Nr: 1, Arch: LINUX_AUDIT_ARCH, Args: [6]uint64{0x1, 0x0}}, 747 want: linux.SECCOMP_RET_TRAP, 748 }, 749 { 750 desc: "arg disallowed (first arg greater)", 751 data: linux.SeccompData{Nr: 1, Arch: LINUX_AUDIT_ARCH, Args: [6]uint64{0x2, 0x0}}, 752 want: linux.SECCOMP_RET_TRAP, 753 }, 754 { 755 desc: "arg disallowed (second arg equal)", 756 data: linux.SeccompData{Nr: 1, Arch: LINUX_AUDIT_ARCH, Args: [6]uint64{0x0, 0xabcd000d}}, 757 want: linux.SECCOMP_RET_TRAP, 758 }, 759 { 760 desc: "arg disallowed (second arg greater)", 761 data: linux.SeccompData{Nr: 1, Arch: LINUX_AUDIT_ARCH, Args: [6]uint64{0x0, 0xffffffff}}, 762 want: linux.SECCOMP_RET_TRAP, 763 }, 764 { 765 desc: "arg disallowed (both arg greater)", 766 data: linux.SeccompData{Nr: 1, Arch: LINUX_AUDIT_ARCH, Args: [6]uint64{0x2, 0xffffffff}}, 767 want: linux.SECCOMP_RET_TRAP, 768 }, 769 }, 770 }, 771 { 772 name: "LessThanOrEqual", 773 ruleSets: []RuleSet{ 774 { 775 Rules: MakeSyscallRules(map[uintptr]SyscallRule{ 776 1: PerArg{ 777 // 4294967298 778 // Both upper 32 bits and lower 32 bits are non-zero. 779 // 00000000000000000000000000000010 780 // 00000000000000000000000000000010 781 LessThanOrEqual(0x00000002_00000002), 782 }, 783 }), 784 Action: linux.SECCOMP_RET_ALLOW, 785 }, 786 }, 787 options: ProgramOptions{ 788 DefaultAction: linux.SECCOMP_RET_TRAP, 789 BadArchAction: linux.SECCOMP_RET_KILL_THREAD, 790 }, 791 specs: []spec{ 792 { 793 desc: "high 32bits greater", 794 data: linux.SeccompData{Nr: 1, Arch: LINUX_AUDIT_ARCH, Args: [6]uint64{0x00000003_00000002}}, 795 want: linux.SECCOMP_RET_TRAP, 796 }, 797 { 798 desc: "high 32bits equal, low 32bits greater", 799 data: linux.SeccompData{Nr: 1, Arch: LINUX_AUDIT_ARCH, Args: [6]uint64{0x00000002_00000003}}, 800 want: linux.SECCOMP_RET_TRAP, 801 }, 802 { 803 desc: "high 32bits equal, low 32bits equal", 804 data: linux.SeccompData{Nr: 1, Arch: LINUX_AUDIT_ARCH, Args: [6]uint64{0x00000002_00000002}}, 805 want: linux.SECCOMP_RET_ALLOW, 806 }, 807 { 808 desc: "high 32bits equal, low 32bits less", 809 data: linux.SeccompData{Nr: 1, Arch: LINUX_AUDIT_ARCH, Args: [6]uint64{0x00000002_00000001}}, 810 want: linux.SECCOMP_RET_ALLOW, 811 }, 812 { 813 desc: "high 32bits less", 814 data: linux.SeccompData{Nr: 1, Arch: LINUX_AUDIT_ARCH, Args: [6]uint64{0x00000001_00000002}}, 815 want: linux.SECCOMP_RET_ALLOW, 816 }, 817 }, 818 }, 819 820 { 821 name: "LessThanOrEqual (multi)", 822 ruleSets: []RuleSet{ 823 { 824 Rules: MakeSyscallRules(map[uintptr]SyscallRule{ 825 1: PerArg{ 826 LessThanOrEqual(0x1), 827 LessThanOrEqual(0xabcd000d), 828 }, 829 }), 830 Action: linux.SECCOMP_RET_ALLOW, 831 }, 832 }, 833 options: ProgramOptions{ 834 DefaultAction: linux.SECCOMP_RET_TRAP, 835 BadArchAction: linux.SECCOMP_RET_KILL_THREAD, 836 }, 837 specs: []spec{ 838 { 839 desc: "arg allowed", 840 data: linux.SeccompData{Nr: 1, Arch: LINUX_AUDIT_ARCH, Args: [6]uint64{0x0, 0x0}}, 841 want: linux.SECCOMP_RET_ALLOW, 842 }, 843 { 844 desc: "arg allowed (first arg equal)", 845 data: linux.SeccompData{Nr: 1, Arch: LINUX_AUDIT_ARCH, Args: [6]uint64{0x1, 0x0}}, 846 want: linux.SECCOMP_RET_ALLOW, 847 }, 848 { 849 desc: "arg disallowed (first arg greater)", 850 data: linux.SeccompData{Nr: 1, Arch: LINUX_AUDIT_ARCH, Args: [6]uint64{0x2, 0x0}}, 851 want: linux.SECCOMP_RET_TRAP, 852 }, 853 { 854 desc: "arg allowed (second arg equal)", 855 data: linux.SeccompData{Nr: 1, Arch: LINUX_AUDIT_ARCH, Args: [6]uint64{0x0, 0xabcd000d}}, 856 want: linux.SECCOMP_RET_ALLOW, 857 }, 858 { 859 desc: "arg disallowed (second arg greater)", 860 data: linux.SeccompData{Nr: 1, Arch: LINUX_AUDIT_ARCH, Args: [6]uint64{0x0, 0xffffffff}}, 861 want: linux.SECCOMP_RET_TRAP, 862 }, 863 { 864 desc: "arg disallowed (both arg greater)", 865 data: linux.SeccompData{Nr: 1, Arch: LINUX_AUDIT_ARCH, Args: [6]uint64{0x2, 0xffffffff}}, 866 want: linux.SECCOMP_RET_TRAP, 867 }, 868 }, 869 }, 870 { 871 name: "MaskedEqual", 872 ruleSets: []RuleSet{ 873 { 874 Rules: MakeSyscallRules(map[uintptr]SyscallRule{ 875 1: PerArg{ 876 // x & 00000001 00000011 (0x103) == 00000000 00000001 (0x1) 877 // Input x must have lowest order bit set and 878 // must *not* have 8th or second lowest order bit set. 879 MaskedEqual(0x103, 0x1), 880 }, 881 }), 882 Action: linux.SECCOMP_RET_ALLOW, 883 }, 884 }, 885 options: ProgramOptions{ 886 DefaultAction: linux.SECCOMP_RET_TRAP, 887 BadArchAction: linux.SECCOMP_RET_KILL_THREAD, 888 }, 889 specs: []spec{ 890 { 891 desc: "arg allowed (low order mandatory bit)", 892 data: linux.SeccompData{ 893 Nr: 1, 894 Arch: LINUX_AUDIT_ARCH, 895 // 00000000 00000000 00000000 00000001 896 Args: [6]uint64{0x1}, 897 }, 898 want: linux.SECCOMP_RET_ALLOW, 899 }, 900 { 901 desc: "arg allowed (low order optional bit)", 902 data: linux.SeccompData{ 903 Nr: 1, 904 Arch: LINUX_AUDIT_ARCH, 905 // 00000000 00000000 00000000 00000101 906 Args: [6]uint64{0x5}, 907 }, 908 want: linux.SECCOMP_RET_ALLOW, 909 }, 910 { 911 desc: "arg disallowed (lowest order bit not set)", 912 data: linux.SeccompData{ 913 Nr: 1, 914 Arch: LINUX_AUDIT_ARCH, 915 // 00000000 00000000 00000000 00000010 916 Args: [6]uint64{0x2}, 917 }, 918 want: linux.SECCOMP_RET_TRAP, 919 }, 920 { 921 desc: "arg disallowed (second lowest order bit set)", 922 data: linux.SeccompData{ 923 Nr: 1, 924 Arch: LINUX_AUDIT_ARCH, 925 // 00000000 00000000 00000000 00000011 926 Args: [6]uint64{0x3}, 927 }, 928 want: linux.SECCOMP_RET_TRAP, 929 }, 930 { 931 desc: "arg disallowed (8th bit set)", 932 data: linux.SeccompData{ 933 Nr: 1, 934 Arch: LINUX_AUDIT_ARCH, 935 // 00000000 00000000 00000001 00000000 936 Args: [6]uint64{0x100}, 937 }, 938 want: linux.SECCOMP_RET_TRAP, 939 }, 940 }, 941 }, 942 { 943 name: "NonNegativeFD", 944 ruleSets: []RuleSet{ 945 { 946 Rules: MakeSyscallRules(map[uintptr]SyscallRule{ 947 1: PerArg{ 948 NonNegativeFD{}, 949 }, 950 }), 951 Action: linux.SECCOMP_RET_ALLOW, 952 }, 953 }, 954 options: ProgramOptions{ 955 DefaultAction: linux.SECCOMP_RET_TRAP, 956 BadArchAction: linux.SECCOMP_RET_KILL_THREAD, 957 }, 958 specs: []spec{ 959 { 960 desc: "zero allowed", 961 data: linux.SeccompData{Nr: 1, Arch: LINUX_AUDIT_ARCH, Args: [6]uint64{0x0}}, 962 want: linux.SECCOMP_RET_ALLOW, 963 }, 964 { 965 desc: "one allowed", 966 data: linux.SeccompData{Nr: 1, Arch: LINUX_AUDIT_ARCH, Args: [6]uint64{0x0}}, 967 want: linux.SECCOMP_RET_ALLOW, 968 }, 969 { 970 desc: "seven allowed", 971 data: linux.SeccompData{Nr: 1, Arch: LINUX_AUDIT_ARCH, Args: [6]uint64{0x7}}, 972 want: linux.SECCOMP_RET_ALLOW, 973 }, 974 { 975 desc: "largest int32 allowed", 976 data: linux.SeccompData{Nr: 1, Arch: LINUX_AUDIT_ARCH, Args: [6]uint64{0x7fffffff}}, 977 want: linux.SECCOMP_RET_ALLOW, 978 }, 979 { 980 desc: "negative 1 not allowed", 981 data: linux.SeccompData{Nr: 1, Arch: LINUX_AUDIT_ARCH, Args: [6]uint64{0x80000000}}, 982 want: linux.SECCOMP_RET_TRAP, 983 }, 984 { 985 desc: "largest uint32 not allowed", 986 data: linux.SeccompData{Nr: 1, Arch: LINUX_AUDIT_ARCH, Args: [6]uint64{0xffffffff}}, 987 want: linux.SECCOMP_RET_TRAP, 988 }, 989 { 990 desc: "a positive int64 larger than max uint32 is not allowed", 991 data: linux.SeccompData{Nr: 1, Arch: LINUX_AUDIT_ARCH, Args: [6]uint64{0x100000000}}, 992 want: linux.SECCOMP_RET_TRAP, 993 }, 994 { 995 desc: "largest int64 not allowed", 996 data: linux.SeccompData{Nr: 1, Arch: LINUX_AUDIT_ARCH, Args: [6]uint64{0x7fffffffffffffff}}, 997 want: linux.SECCOMP_RET_TRAP, 998 }, 999 { 1000 desc: "largest uint64 not allowed", 1001 data: linux.SeccompData{Nr: 1, Arch: LINUX_AUDIT_ARCH, Args: [6]uint64{0xffffffffffffffff}}, 1002 want: linux.SECCOMP_RET_TRAP, 1003 }, 1004 }, 1005 }, 1006 { 1007 name: "Instruction Pointer", 1008 ruleSets: []RuleSet{ 1009 { 1010 Rules: MakeSyscallRules(map[uintptr]SyscallRule{ 1011 1: PerArg{ 1012 RuleIP: EqualTo(0x7aabbccdd), 1013 }, 1014 }), 1015 Action: linux.SECCOMP_RET_ALLOW, 1016 }, 1017 }, 1018 options: ProgramOptions{ 1019 DefaultAction: linux.SECCOMP_RET_TRAP, 1020 BadArchAction: linux.SECCOMP_RET_KILL_THREAD, 1021 }, 1022 specs: []spec{ 1023 { 1024 desc: "allowed", 1025 data: linux.SeccompData{Nr: 1, Arch: LINUX_AUDIT_ARCH, Args: [6]uint64{}, InstructionPointer: 0x7aabbccdd}, 1026 want: linux.SECCOMP_RET_ALLOW, 1027 }, 1028 { 1029 desc: "disallowed", 1030 data: linux.SeccompData{Nr: 1, Arch: LINUX_AUDIT_ARCH, Args: [6]uint64{}, InstructionPointer: 0x711223344}, 1031 want: linux.SECCOMP_RET_TRAP, 1032 }, 1033 }, 1034 }, 1035 } { 1036 t.Run(test.name, func(t *testing.T) { 1037 var instrs []bpf.Instruction 1038 var panicErr any 1039 func() { 1040 t.Helper() 1041 defer func() { 1042 panicErr = recover() 1043 t.Helper() 1044 }() 1045 var err error 1046 instrs, _, err = BuildProgram(test.ruleSets, test.options) 1047 if err != nil { 1048 t.Fatalf("BuildProgram() got error: %v", err) 1049 } 1050 }() 1051 if test.wantPanic { 1052 if panicErr == nil { 1053 t.Fatal("BuildProgram did not panick") 1054 } 1055 return 1056 } 1057 if panicErr != nil { 1058 t.Fatalf("BuildProgram unexpectedly panicked: %v", panicErr) 1059 } 1060 p, err := bpf.Compile(instrs, true /* optimize */) 1061 if err != nil { 1062 t.Fatalf("bpf.Compile got error: %v", err) 1063 } 1064 for _, spec := range test.specs { 1065 got, err := bpf.Exec[bpf.NativeEndian](p, DataAsBPFInput(&spec.data, buf)) 1066 if err != nil { 1067 t.Fatalf("%s: bpf.Exec got error: %v", spec.desc, err) 1068 } 1069 if got != uint32(spec.want) { 1070 // Include a decoded version of the program in output for debugging purposes. 1071 decoded, _ := bpf.DecodeInstructions(instrs) 1072 t.Fatalf("%s: got: %d, want: %d\nBPF Program\n%s", spec.desc, got, spec.want, decoded) 1073 } 1074 } 1075 }) 1076 } 1077 } 1078 1079 // TestRandom tests that randomly generated rules are encoded correctly. 1080 func TestRandom(t *testing.T) { 1081 rand.Seed(time.Now().UnixNano()) 1082 size := rand.Intn(50) + 1 1083 syscallRules := NewSyscallRules() 1084 for syscallRules.Size() < size { 1085 n := uintptr(rand.Intn(200)) 1086 if !syscallRules.Has(n) { 1087 syscallRules.Set(n, MatchAll{}) 1088 } 1089 } 1090 1091 t.Logf("Testing filters: %v", syscallRules) 1092 instrs, _, err := BuildProgram([]RuleSet{ 1093 { 1094 Rules: syscallRules, 1095 Action: linux.SECCOMP_RET_ALLOW, 1096 }, 1097 }, ProgramOptions{ 1098 DefaultAction: linux.SECCOMP_RET_TRAP, 1099 BadArchAction: linux.SECCOMP_RET_KILL_THREAD, 1100 }) 1101 if err != nil { 1102 t.Fatalf("buildProgram() got error: %v", err) 1103 } 1104 p, err := bpf.Compile(instrs, true /* optimize */) 1105 if err != nil { 1106 t.Fatalf("bpf.Compile got error: %v", err) 1107 } 1108 buf := make([]byte, (&linux.SeccompData{}).SizeBytes()) 1109 for i := uint32(0); i < 200; i++ { 1110 data := linux.SeccompData{Nr: int32(i), Arch: LINUX_AUDIT_ARCH} 1111 got, err := bpf.Exec[bpf.NativeEndian](p, DataAsBPFInput(&data, buf)) 1112 if err != nil { 1113 t.Errorf("bpf.Exec got error: %v, for syscall %d", err, i) 1114 continue 1115 } 1116 want := linux.SECCOMP_RET_TRAP 1117 if syscallRules.Has(uintptr(i)) { 1118 want = linux.SECCOMP_RET_ALLOW 1119 } 1120 if got != uint32(want) { 1121 t.Errorf("bpf.Exec = %d, want: %d, for syscall %d", got, want, i) 1122 } 1123 } 1124 } 1125 1126 // TestReadDeal checks that a process dies when it trips over the filter and 1127 // that it doesn't die when the filter is not triggered. 1128 func TestRealDeal(t *testing.T) { 1129 for _, test := range []struct { 1130 name string 1131 die bool 1132 want string 1133 }{ 1134 {name: "bad syscall", die: true, want: "bad system call"}, 1135 {name: "allowed syscall", die: false, want: "Syscall was allowed!!!"}, 1136 } { 1137 t.Run(test.name, func(t *testing.T) { 1138 victim, err := newVictim() 1139 if err != nil { 1140 t.Fatalf("unable to get victim: %v", err) 1141 } 1142 defer func() { 1143 if err := os.Remove(victim); err != nil { 1144 t.Fatalf("Unable to remove victim: %v", err) 1145 } 1146 }() 1147 1148 dieFlag := fmt.Sprintf("-die=%v", test.die) 1149 cmd := exec.Command(victim, dieFlag) 1150 out, err := cmd.CombinedOutput() 1151 if test.die { 1152 if err == nil { 1153 t.Fatalf("Victim was not killed as expected, output: %s", out) 1154 } 1155 // Depending on kernel version, either RET_TRAP or RET_KILL_PROCESS is 1156 // used. RET_TRAP dumps reason for exit in output, while RET_KILL_PROCESS 1157 // returns SIGSYS as exit status. 1158 if !strings.Contains(string(out), test.want) && 1159 !strings.Contains(err.Error(), test.want) { 1160 t.Fatalf("Victim error is wrong, got: %v, err: %v, want: %v", string(out), err, test.want) 1161 } 1162 return 1163 } 1164 // test.die is false 1165 if err != nil { 1166 t.Logf("out: %s", string(out)) 1167 t.Fatalf("Victim failed to execute, err: %v", err) 1168 } 1169 if !strings.Contains(string(out), test.want) { 1170 t.Fatalf("Victim output is wrong, got: %v, want: %v", string(out), test.want) 1171 } 1172 }) 1173 } 1174 } 1175 1176 // TestMerge ensures that empty rules are not erased when rules are merged. 1177 func TestMerge(t *testing.T) { 1178 for _, tst := range []struct { 1179 name string 1180 main SyscallRule 1181 merge SyscallRule 1182 want SyscallRule 1183 }{ 1184 { 1185 name: "MatchAll both", 1186 main: MatchAll{}, 1187 merge: MatchAll{}, 1188 want: Or{MatchAll{}, MatchAll{}}, 1189 }, 1190 { 1191 name: "MatchAll and Or", 1192 main: MatchAll{}, 1193 merge: Or{PerArg{EqualTo(0)}}, 1194 want: Or{MatchAll{}, Or{PerArg{EqualTo(0)}}}, 1195 }, 1196 { 1197 name: "Or and MatchAll", 1198 main: Or{PerArg{EqualTo(0)}}, 1199 merge: MatchAll{}, 1200 want: Or{Or{PerArg{EqualTo(0)}}, MatchAll{}}, 1201 }, 1202 { 1203 name: "2 Ors", 1204 main: Or{PerArg{EqualTo(0)}}, 1205 merge: Or{PerArg{EqualTo(1)}}, 1206 want: Or{Or{PerArg{EqualTo(0)}}, Or{PerArg{EqualTo(1)}}}, 1207 }, 1208 } { 1209 t.Run(tst.name, func(t *testing.T) { 1210 mainRules := MakeSyscallRules(map[uintptr]SyscallRule{ 1211 1: tst.main, 1212 }).Merge(MakeSyscallRules(map[uintptr]SyscallRule{ 1213 1: tst.merge, 1214 })) 1215 wantRules := MakeSyscallRules(map[uintptr]SyscallRule{1: tst.want}) 1216 if !reflect.DeepEqual(mainRules, wantRules) { 1217 t.Errorf("got rules:\n%v\nwant rules:\n%v\n", mainRules, wantRules) 1218 } 1219 }) 1220 } 1221 } 1222 1223 // TestOptimizeSyscallRule tests the behavior of syscall rule optimizers. 1224 func TestOptimizeSyscallRule(t *testing.T) { 1225 // av is a shorthand for `AnyValue{}`, used below to keep `PerArg` 1226 // structs short enough to comfortably fit on one line. 1227 av := AnyValue{} 1228 1229 // Some useful constants that are larger than uint32. 1230 const ( 1231 a1 = 0xA1A1A1A1A1A1A1A1 1232 a2 = 0xA2A2A2A2A2A2A2A2 1233 b1 = 0xB1B1B1B1B1B1B1B1 1234 b2 = 0xB2B2B2B2B2B2B2B2 1235 b3 = 0xB3B3B3B3B3B3B3B3 1236 c1 = 0xC1C1C1C1C1C1C1C1 1237 c2 = 0xC2C2C2C2C2C2C2C2 1238 c3 = 0xC3C3C3C3C3C3C3C3 1239 d0 = 0xD0D0D0D0D0D0D0D0 1240 d1 = 0xD1D1D1D1D1D1D1D1 1241 ) 1242 1243 // split replaces a `splittableValueMatcher` rule with its `splitMatcher` 1244 // version. 1245 s := func(matcher splittableValueMatcher) ValueMatcher { 1246 return matcher.split() 1247 } 1248 1249 highEq := func(val uintptr) splitMatcher { 1250 return high32BitsMatch(halfEqualTo(val)) 1251 } 1252 lowEq := func(val uintptr) splitMatcher { 1253 return low32BitsMatch(halfEqualTo(val)) 1254 } 1255 1256 for _, test := range []struct { 1257 name string 1258 rule SyscallRule 1259 optimizers []ruleOptimizerFunc 1260 want SyscallRule 1261 }{ 1262 { 1263 name: "do nothing to a simple rule", 1264 rule: PerArg{NotEqual(0xff), av, av, av, av, av, av}, 1265 want: PerArg{NotEqual(0xff), av, av, av, av, av, av}, 1266 }, 1267 { 1268 name: "flatten Or rule", 1269 rule: Or{ 1270 Or{ 1271 PerArg{EqualTo(a1)}, 1272 Or{ 1273 PerArg{EqualTo(b1)}, 1274 PerArg{EqualTo(b2)}, 1275 }, 1276 PerArg{EqualTo(c1)}, 1277 }, 1278 Or{ 1279 PerArg{EqualTo(d0)}, 1280 PerArg{EqualTo(d1)}, 1281 }, 1282 }, 1283 want: Or{ 1284 PerArg{s(EqualTo(a1)), av, av, av, av, av, av}, 1285 PerArg{s(EqualTo(b1)), av, av, av, av, av, av}, 1286 PerArg{s(EqualTo(b2)), av, av, av, av, av, av}, 1287 PerArg{s(EqualTo(c1)), av, av, av, av, av, av}, 1288 PerArg{s(EqualTo(d0)), av, av, av, av, av, av}, 1289 PerArg{s(EqualTo(d1)), av, av, av, av, av, av}, 1290 }, 1291 }, 1292 { 1293 name: "flatten And rule", 1294 rule: And{ 1295 And{ 1296 PerArg{NotEqual(0x11)}, 1297 And{ 1298 PerArg{NotEqual(0x22)}, 1299 PerArg{NotEqual(0x33)}, 1300 }, 1301 PerArg{NotEqual(0x44)}, 1302 }, 1303 And{ 1304 PerArg{NotEqual(0x55)}, 1305 PerArg{NotEqual(0x66)}, 1306 }, 1307 }, 1308 want: And{ 1309 PerArg{NotEqual(0x11), av, av, av, av, av, av}, 1310 PerArg{NotEqual(0x22), av, av, av, av, av, av}, 1311 PerArg{NotEqual(0x33), av, av, av, av, av, av}, 1312 PerArg{NotEqual(0x44), av, av, av, av, av, av}, 1313 PerArg{NotEqual(0x55), av, av, av, av, av, av}, 1314 PerArg{NotEqual(0x66), av, av, av, av, av, av}, 1315 }, 1316 }, 1317 { 1318 name: "simplify Or with single rule", 1319 rule: Or{ 1320 PerArg{EqualTo(0x11)}, 1321 }, 1322 want: PerArg{s(EqualTo(0x11)), av, av, av, av, av, av}, 1323 }, 1324 { 1325 name: "simplify And with single rule", 1326 rule: And{ 1327 PerArg{EqualTo(0x11)}, 1328 }, 1329 want: PerArg{s(EqualTo(0x11)), av, av, av, av, av, av}, 1330 }, 1331 { 1332 name: "simplify Or with MatchAll", 1333 rule: Or{ 1334 PerArg{EqualTo(0x11)}, 1335 Or{ 1336 MatchAll{}, 1337 }, 1338 PerArg{EqualTo(0x22)}, 1339 }, 1340 want: MatchAll{}, 1341 }, 1342 { 1343 name: "single MatchAll in Or is not an empty rule", 1344 rule: Or{ 1345 MatchAll{}, 1346 MatchAll{}, 1347 }, 1348 optimizers: []ruleOptimizerFunc{ 1349 convertMatchAllOrXToMatchAll, 1350 }, 1351 want: MatchAll{}, 1352 }, 1353 { 1354 name: "simplify And with MatchAll", 1355 rule: And{ 1356 PerArg{NotEqual(0x11)}, 1357 And{ 1358 MatchAll{}, 1359 }, 1360 PerArg{NotEqual(0x22)}, 1361 }, 1362 want: And{ 1363 PerArg{NotEqual(0x11), av, av, av, av, av, av}, 1364 PerArg{NotEqual(0x22), av, av, av, av, av, av}, 1365 }, 1366 }, 1367 { 1368 name: "single MatchAll in And is not optimized to an empty rule", 1369 rule: And{ 1370 MatchAll{}, 1371 MatchAll{}, 1372 }, 1373 optimizers: []ruleOptimizerFunc{ 1374 convertMatchAllAndXToX, 1375 }, 1376 want: MatchAll{}, 1377 }, 1378 { 1379 name: "PerArg nil to AnyValue", 1380 rule: PerArg{av, EqualTo(0)}, 1381 optimizers: []ruleOptimizerFunc{ 1382 nilInPerArgToAnyValue, 1383 }, 1384 want: PerArg{av, EqualTo(0), av, av, av, av, av}, 1385 }, 1386 { 1387 name: "Useless PerArg is MatchAll", 1388 rule: PerArg{av, av}, 1389 optimizers: []ruleOptimizerFunc{ 1390 nilInPerArgToAnyValue, 1391 convertUselessPerArgToMatchAll, 1392 }, 1393 want: MatchAll{}, 1394 }, 1395 { 1396 name: "halfValueMatchers are simplified", 1397 rule: PerArg{ 1398 EqualTo(0), 1399 splitMatcher{ 1400 highMatcher: halfNotSet(0), 1401 lowMatcher: halfMaskedEqual{ 1402 mask: 0, 1403 value: 0x89abcdef, 1404 }, 1405 }, 1406 splitMatcher{ 1407 highMatcher: halfNotSet(1 << 4), 1408 lowMatcher: halfMaskedEqual{ 1409 mask: 0xffffffff, 1410 value: 0x89abcdef, 1411 }, 1412 }, 1413 splitMatcher{ 1414 highMatcher: halfNotSet(0), 1415 lowMatcher: halfMaskedEqual{ 1416 mask: 0xffffffff, 1417 value: 0x89abcdef, 1418 }, 1419 }, 1420 splitMatcher{ 1421 highMatcher: halfNotSet(0), 1422 lowMatcher: halfMaskedEqual{ 1423 mask: 0, 1424 value: 0, 1425 }, 1426 }, 1427 splitMatcher{ 1428 highMatcher: halfAnyValue{}, 1429 lowMatcher: halfMaskedEqual{ 1430 mask: 0x89abcdef, 1431 value: 0x89abcdef, 1432 }, 1433 }, 1434 BitsAllowlist(0xabcdef), 1435 }, 1436 want: PerArg{ 1437 s(EqualTo(0)), 1438 splitMatcher{ 1439 highMatcher: halfAnyValue{}, 1440 lowMatcher: halfMaskedEqual{ 1441 mask: 0, 1442 value: 0x89abcdef, 1443 }, 1444 }, 1445 splitMatcher{ 1446 highMatcher: halfNotSet(1 << 4), 1447 lowMatcher: halfEqualTo(0x89abcdef), 1448 }, 1449 splitMatcher{ 1450 highMatcher: halfAnyValue{}, 1451 lowMatcher: halfEqualTo(0x89abcdef), 1452 }, 1453 av, // Both halves are simplified to `halfAnyValue`. 1454 splitMatcher{ 1455 highMatcher: halfAnyValue{}, 1456 lowMatcher: halfMaskedEqual{ 1457 mask: 0x89abcdef, 1458 value: 0x89abcdef, 1459 }, 1460 }, 1461 splitMatcher{ 1462 // High half gets simplified to equaling zero. 1463 highMatcher: halfEqualTo(0), 1464 // Low half gets simplified to a bit-not-set check. 1465 lowMatcher: halfNotSet(^uint32(0xabcdef)), 1466 // Keep user-friendly representation. 1467 repr: fmt.Sprintf("& %#x == %#x", ^uint64(0xabcdef), 0), 1468 }, 1469 }, 1470 }, 1471 { 1472 name: "Common value matchers in PerArg are extracted", 1473 rule: Or{ 1474 PerArg{EqualTo(a1), EqualTo(b1), EqualTo(c1), EqualTo(d0)}, 1475 PerArg{EqualTo(a2), EqualTo(b1), EqualTo(c1), EqualTo(d0)}, 1476 PerArg{EqualTo(a1), EqualTo(b2), EqualTo(c2), EqualTo(d0)}, 1477 PerArg{EqualTo(a2), EqualTo(b2), EqualTo(c2), EqualTo(d0)}, 1478 PerArg{EqualTo(a1), EqualTo(b3), EqualTo(c3), EqualTo(d0)}, 1479 PerArg{EqualTo(a2), EqualTo(b3), EqualTo(c3), EqualTo(d0)}, 1480 }, 1481 want: And{ 1482 Or{ 1483 PerArg{s(EqualTo(a1)), av, av, av, av, av, av}, 1484 PerArg{s(EqualTo(a2)), av, av, av, av, av, av}, 1485 }, 1486 PerArg{av, av, av, s(EqualTo(d0)), av, av, av}, 1487 Or{ 1488 PerArg{av, s(EqualTo(b1)), s(EqualTo(c1)), av, av, av, av}, 1489 PerArg{av, s(EqualTo(b2)), s(EqualTo(c2)), av, av, av, av}, 1490 PerArg{av, s(EqualTo(b3)), s(EqualTo(c3)), av, av, av, av}, 1491 }, 1492 }, 1493 }, 1494 { 1495 name: "Common halfValueMatchers in splitMatchers are extracted", 1496 rule: Or{ 1497 PerArg{EqualTo(0xA1), EqualTo(0xB1), EqualTo(c1), EqualTo(d0)}, 1498 PerArg{EqualTo(0xA1), EqualTo(0xB1), EqualTo(c2), EqualTo(d0)}, 1499 PerArg{EqualTo(0xA2), EqualTo(0xB2), EqualTo(c1), EqualTo(d0)}, 1500 PerArg{EqualTo(0xA2), EqualTo(0xB2), EqualTo(c2), EqualTo(d0)}, 1501 }, 1502 want: And{ 1503 PerArg{highEq(0), av, av, av, av, av, av}, 1504 PerArg{av, highEq(0), av, av, av, av, av}, 1505 Or{ 1506 PerArg{av, av, s(EqualTo(c1)), av, av, av, av}, 1507 PerArg{av, av, s(EqualTo(c2)), av, av, av, av}, 1508 }, 1509 PerArg{av, av, av, s(EqualTo(d0)), av, av, av}, 1510 Or{ 1511 PerArg{lowEq(0xA1), lowEq(0xB1), av, av, av, av, av}, 1512 PerArg{lowEq(0xA2), lowEq(0xB2), av, av, av, av, av}, 1513 }, 1514 }, 1515 }, 1516 } { 1517 t.Run(test.name, func(t *testing.T) { 1518 var got SyscallRule 1519 if len(test.optimizers) == 0 { 1520 got = optimizeSyscallRule(test.rule) 1521 } else { 1522 got = (&optimizationRun{funcs: test.optimizers}).optimize(test.rule) 1523 } 1524 if !reflect.DeepEqual(got, test.want) { 1525 t.Errorf("got rule:\n%v\nwant rule:\n%v\n", got, test.want) 1526 } 1527 }) 1528 } 1529 } 1530 1531 func TestOrderRuleSets(t *testing.T) { 1532 for _, test := range []struct { 1533 name string 1534 ruleSets []RuleSet 1535 options ProgramOptions // Optimizations are always enabled regardless of this 1536 want orderedRuleSets 1537 wantErr bool 1538 }{ 1539 { 1540 name: "no RuleSets", 1541 options: DefaultProgramOptions(), 1542 want: orderedRuleSets{}, 1543 }, 1544 { 1545 name: "inconsistent vsyscall", 1546 options: DefaultProgramOptions(), 1547 ruleSets: []RuleSet{ 1548 { 1549 Rules: NewSyscallRules().Add(unix.SYS_READ, MatchAll{}), 1550 Action: linux.SECCOMP_RET_TRACE, 1551 Vsyscall: false, 1552 }, 1553 { 1554 Rules: NewSyscallRules().Add(unix.SYS_READ, MatchAll{}), 1555 Action: linux.SECCOMP_RET_TRACE, 1556 Vsyscall: true, 1557 }, 1558 }, 1559 wantErr: true, 1560 }, 1561 { 1562 name: "single trivial rule", 1563 ruleSets: []RuleSet{ 1564 { 1565 Rules: NewSyscallRules().Add(unix.SYS_READ, MatchAll{}), 1566 Action: linux.SECCOMP_RET_TRACE, 1567 Vsyscall: false, 1568 }, 1569 }, 1570 options: DefaultProgramOptions(), 1571 want: orderedRuleSets{ 1572 trivial: map[uintptr]singleSyscallRuleSet{ 1573 unix.SYS_READ: { 1574 sysno: unix.SYS_READ, 1575 rules: []syscallRuleAction{ 1576 { 1577 rule: MatchAll{}, 1578 action: linux.SECCOMP_RET_TRACE, 1579 }, 1580 }, 1581 vsyscall: false, 1582 }, 1583 }, 1584 }, 1585 }, 1586 { 1587 name: "hot single trivial rule still ends up as trivial", 1588 ruleSets: []RuleSet{ 1589 { 1590 Rules: NewSyscallRules().Add(unix.SYS_READ, MatchAll{}), 1591 Action: linux.SECCOMP_RET_TRACE, 1592 Vsyscall: false, 1593 }, 1594 }, 1595 options: ProgramOptions{ 1596 HotSyscalls: []uintptr{unix.SYS_READ}, 1597 }, 1598 want: orderedRuleSets{ 1599 trivial: map[uintptr]singleSyscallRuleSet{ 1600 unix.SYS_READ: { 1601 sysno: unix.SYS_READ, 1602 rules: []syscallRuleAction{ 1603 { 1604 rule: MatchAll{}, 1605 action: linux.SECCOMP_RET_TRACE, 1606 }, 1607 }, 1608 vsyscall: false, 1609 }, 1610 }, 1611 }, 1612 }, 1613 { 1614 name: "hot single non-trivial rule", 1615 ruleSets: []RuleSet{ 1616 { 1617 Rules: NewSyscallRules().Add( 1618 unix.SYS_READ, PerArg{EqualTo(0)}, 1619 ), 1620 Action: linux.SECCOMP_RET_TRACE, 1621 Vsyscall: false, 1622 }, 1623 }, 1624 options: ProgramOptions{ 1625 HotSyscalls: []uintptr{unix.SYS_READ}, 1626 }, 1627 want: orderedRuleSets{ 1628 hotNonTrivial: map[uintptr]singleSyscallRuleSet{ 1629 unix.SYS_READ: { 1630 sysno: unix.SYS_READ, 1631 rules: []syscallRuleAction{ 1632 { 1633 rule: PerArg{EqualTo(0)}, 1634 action: linux.SECCOMP_RET_TRACE, 1635 }, 1636 }, 1637 vsyscall: false, 1638 }, 1639 }, 1640 hotNonTrivialOrder: []uintptr{unix.SYS_READ}, 1641 }, 1642 }, 1643 { 1644 name: "hot rule ordering", 1645 ruleSets: []RuleSet{ 1646 { 1647 Rules: NewSyscallRules().Add( 1648 unix.SYS_FLOCK, PerArg{EqualTo(0)}, 1649 ), 1650 Action: linux.SECCOMP_RET_TRACE, 1651 Vsyscall: false, 1652 }, 1653 { 1654 Rules: NewSyscallRules().Add( 1655 unix.SYS_WRITE, PerArg{EqualTo(1)}, 1656 ).Add( 1657 unix.SYS_READ, PerArg{EqualTo(2)}, 1658 ), 1659 Action: linux.SECCOMP_RET_TRACE, 1660 Vsyscall: false, 1661 }, 1662 }, 1663 options: ProgramOptions{ 1664 HotSyscalls: []uintptr{ 1665 unix.SYS_READ, 1666 unix.SYS_WRITE, 1667 unix.SYS_FLOCK, 1668 }, 1669 }, 1670 want: orderedRuleSets{ 1671 hotNonTrivial: map[uintptr]singleSyscallRuleSet{ 1672 unix.SYS_READ: { 1673 sysno: unix.SYS_READ, 1674 rules: []syscallRuleAction{ 1675 { 1676 rule: PerArg{EqualTo(2)}, 1677 action: linux.SECCOMP_RET_TRACE, 1678 }, 1679 }, 1680 vsyscall: false, 1681 }, 1682 unix.SYS_WRITE: { 1683 sysno: unix.SYS_WRITE, 1684 rules: []syscallRuleAction{ 1685 { 1686 rule: PerArg{EqualTo(1)}, 1687 action: linux.SECCOMP_RET_TRACE, 1688 }, 1689 }, 1690 vsyscall: false, 1691 }, 1692 unix.SYS_FLOCK: { 1693 sysno: unix.SYS_FLOCK, 1694 rules: []syscallRuleAction{ 1695 { 1696 rule: PerArg{EqualTo(0)}, 1697 action: linux.SECCOMP_RET_TRACE, 1698 }, 1699 }, 1700 vsyscall: false, 1701 }, 1702 }, 1703 hotNonTrivialOrder: []uintptr{ 1704 unix.SYS_READ, 1705 unix.SYS_WRITE, 1706 unix.SYS_FLOCK, 1707 }, 1708 }, 1709 }, 1710 { 1711 name: "cold single non-trivial non-vsyscall rule", 1712 ruleSets: []RuleSet{ 1713 { 1714 Rules: NewSyscallRules().Add( 1715 unix.SYS_READ, PerArg{EqualTo(0)}, 1716 ), 1717 Action: linux.SECCOMP_RET_TRACE, 1718 Vsyscall: false, 1719 }, 1720 }, 1721 options: ProgramOptions{ 1722 HotSyscalls: []uintptr{unix.SYS_WRITE}, 1723 }, 1724 want: orderedRuleSets{ 1725 coldNonTrivial: map[uintptr]singleSyscallRuleSet{ 1726 unix.SYS_READ: { 1727 sysno: unix.SYS_READ, 1728 rules: []syscallRuleAction{ 1729 { 1730 rule: PerArg{EqualTo(0)}, 1731 action: linux.SECCOMP_RET_TRACE, 1732 }, 1733 }, 1734 vsyscall: false, 1735 }, 1736 }, 1737 hotNonTrivialOrder: nil, // Empty 1738 }, 1739 }, 1740 { 1741 name: "cold single non-trivial yes-vsyscall rule", 1742 ruleSets: []RuleSet{ 1743 { 1744 Rules: NewSyscallRules().Add( 1745 unix.SYS_READ, PerArg{EqualTo(0)}, 1746 ), 1747 Action: linux.SECCOMP_RET_TRACE, 1748 Vsyscall: true, 1749 }, 1750 }, 1751 options: ProgramOptions{ 1752 HotSyscalls: []uintptr{unix.SYS_WRITE}, 1753 }, 1754 want: orderedRuleSets{ 1755 coldNonTrivial: map[uintptr]singleSyscallRuleSet{ 1756 unix.SYS_READ: { 1757 sysno: unix.SYS_READ, 1758 rules: []syscallRuleAction{ 1759 { 1760 rule: PerArg{EqualTo(0)}, 1761 action: linux.SECCOMP_RET_TRACE, 1762 }, 1763 }, 1764 vsyscall: true, 1765 }, 1766 }, 1767 hotNonTrivialOrder: nil, // Empty 1768 }, 1769 }, 1770 { 1771 name: "all rule types at once", 1772 ruleSets: []RuleSet{ 1773 { 1774 Rules: NewSyscallRules().Add( 1775 // Hot, non-vsyscall, trivial 1776 unix.SYS_READ, MatchAll{}, 1777 ).Add( 1778 // Hot, non-vsyscall, non-trivial 1779 unix.SYS_WRITE, PerArg{EqualTo(4)}, 1780 ).Add( 1781 // Hot, non-vsyscall, non-trivial 1782 unix.SYS_FLOCK, PerArg{EqualTo(131)}, 1783 ), 1784 Action: linux.SECCOMP_RET_TRACE, 1785 Vsyscall: false, 1786 }, 1787 { 1788 Rules: NewSyscallRules().Add( 1789 // Hot, vsyscall, trivial (after optimizations) 1790 unix.SYS_FUTEX, PerArg{AnyValue{}}, 1791 ).Add( 1792 // Hot, vsyscall, non-trivial 1793 unix.SYS_GETPID, PerArg{EqualTo(20)}, 1794 ).Add( 1795 // Cold, vsyscall, trivial (after optimizations) 1796 unix.SYS_GETTIMEOFDAY, PerArg{AnyValue{}}, 1797 ).Add( 1798 // Cold, vsyscall, non-trivial 1799 unix.SYS_MINCORE, PerArg{EqualTo(78)}, 1800 ), 1801 Action: linux.SECCOMP_RET_ERRNO, 1802 Vsyscall: true, 1803 }, 1804 { 1805 Rules: NewSyscallRules().Add( 1806 // Hot, non-vsyscall, trivial (after optimizations) 1807 unix.SYS_CLOSE, PerArg{AnyValue{}}, 1808 ).Add( 1809 // Hot, non-vsyscall, non-trivial 1810 unix.SYS_OPENAT, PerArg{EqualTo(463)}, 1811 ).Add( 1812 // Cold, non-vsyscall, non-trivial 1813 unix.SYS_LINKAT, PerArg{EqualTo(471)}, 1814 ).Add( 1815 // Cold, non-vsyscall, trivial here but 1816 // a later RuleSet will make it non-trivial 1817 // overall. 1818 unix.SYS_UNLINKAT, MatchAll{}, 1819 ).Add( 1820 // Cold, non-vsyscall, trivial and another 1821 // later RuleSet will keep it trivial later. 1822 unix.SYS_CHDIR, MatchAll{}, 1823 ), 1824 Action: linux.SECCOMP_RET_KILL_THREAD, 1825 Vsyscall: false, 1826 }, 1827 { 1828 Rules: NewSyscallRules().Add( 1829 // Hot, non-vsyscall, non-trivial 1830 unix.SYS_OPENAT, PerArg{EqualTo(463463)}, 1831 ).Add( 1832 // Cold, non-vsyscall, non-trivial 1833 unix.SYS_LINKAT, PerArg{EqualTo(471471)}, 1834 ).Add( 1835 // Cold, non-vsyscall, no longer trivial. 1836 unix.SYS_UNLINKAT, PerArg{EqualTo(472)}, 1837 ).Add( 1838 // Cold, non-vsyscall, remains trivial. 1839 unix.SYS_FCHDIR, PerArg{AnyValue{}}, 1840 ), 1841 Action: linux.SECCOMP_RET_KILL_PROCESS, 1842 Vsyscall: false, 1843 }, 1844 { 1845 Rules: NewSyscallRules().Add( 1846 // Cold, non-vsyscall, adds to previous rule 1847 // after optimizations because it has the 1848 // same action. 1849 unix.SYS_UNLINKAT, PerArg{EqualTo(472472)}, 1850 ), 1851 Action: linux.SECCOMP_RET_KILL_PROCESS, 1852 Vsyscall: false, 1853 }, 1854 { 1855 Rules: NewSyscallRules().Add( 1856 // Cold, non-vsyscall, does not add to 1857 // previous rule because it has a different 1858 // action. 1859 unix.SYS_UNLINKAT, PerArg{EqualTo(472472472)}, 1860 ), 1861 Action: linux.SECCOMP_RET_TRAP, 1862 Vsyscall: false, 1863 }, 1864 }, 1865 options: ProgramOptions{ 1866 HotSyscalls: []uintptr{ 1867 unix.SYS_READ, 1868 unix.SYS_WRITE, 1869 unix.SYS_FLOCK, 1870 unix.SYS_CLOSE, 1871 unix.SYS_OPENAT, 1872 unix.SYS_GETPID, 1873 // Note: not used in any RuleSet, so it should not 1874 // appear in `want`: 1875 unix.SYS_GETTID, 1876 }, 1877 }, 1878 want: orderedRuleSets{ 1879 hotNonTrivial: map[uintptr]singleSyscallRuleSet{ 1880 unix.SYS_WRITE: { 1881 sysno: unix.SYS_WRITE, 1882 rules: []syscallRuleAction{ 1883 { 1884 rule: PerArg{EqualTo(4)}, 1885 action: linux.SECCOMP_RET_TRACE, 1886 }, 1887 }, 1888 vsyscall: false, 1889 }, 1890 unix.SYS_FLOCK: { 1891 sysno: unix.SYS_FLOCK, 1892 rules: []syscallRuleAction{ 1893 { 1894 rule: PerArg{EqualTo(131)}, 1895 action: linux.SECCOMP_RET_TRACE, 1896 }, 1897 }, 1898 vsyscall: false, 1899 }, 1900 unix.SYS_GETPID: { 1901 sysno: unix.SYS_GETPID, 1902 rules: []syscallRuleAction{ 1903 { 1904 rule: PerArg{EqualTo(20)}, 1905 action: linux.SECCOMP_RET_ERRNO, 1906 }, 1907 }, 1908 vsyscall: true, 1909 }, 1910 unix.SYS_OPENAT: { 1911 sysno: unix.SYS_OPENAT, 1912 rules: []syscallRuleAction{ 1913 { 1914 rule: PerArg{EqualTo(463)}, 1915 action: linux.SECCOMP_RET_KILL_THREAD, 1916 }, 1917 { 1918 rule: PerArg{EqualTo(463463)}, 1919 action: linux.SECCOMP_RET_KILL_PROCESS, 1920 }, 1921 }, 1922 vsyscall: false, 1923 }, 1924 }, 1925 coldNonTrivial: map[uintptr]singleSyscallRuleSet{ 1926 unix.SYS_LINKAT: { 1927 sysno: unix.SYS_LINKAT, 1928 rules: []syscallRuleAction{ 1929 { 1930 rule: PerArg{EqualTo(471)}, 1931 action: linux.SECCOMP_RET_KILL_THREAD, 1932 }, 1933 { 1934 rule: PerArg{EqualTo(471471)}, 1935 action: linux.SECCOMP_RET_KILL_PROCESS, 1936 }, 1937 }, 1938 vsyscall: false, 1939 }, 1940 unix.SYS_UNLINKAT: { 1941 sysno: unix.SYS_UNLINKAT, 1942 rules: []syscallRuleAction{ 1943 { 1944 rule: MatchAll{}, 1945 action: linux.SECCOMP_RET_KILL_THREAD, 1946 }, 1947 { 1948 rule: Or{ 1949 PerArg{EqualTo(472)}, 1950 PerArg{EqualTo(472472)}, 1951 }, 1952 action: linux.SECCOMP_RET_KILL_PROCESS, 1953 }, 1954 { 1955 rule: PerArg{EqualTo(472472472)}, 1956 action: linux.SECCOMP_RET_TRAP, 1957 }, 1958 }, 1959 vsyscall: false, 1960 }, 1961 unix.SYS_MINCORE: { 1962 sysno: unix.SYS_MINCORE, 1963 rules: []syscallRuleAction{ 1964 { 1965 rule: PerArg{EqualTo(78)}, 1966 action: linux.SECCOMP_RET_ERRNO, 1967 }, 1968 }, 1969 vsyscall: true, 1970 }, 1971 unix.SYS_FUTEX: { 1972 sysno: unix.SYS_FUTEX, 1973 rules: []syscallRuleAction{ 1974 { 1975 rule: MatchAll{}, 1976 action: linux.SECCOMP_RET_ERRNO, 1977 }, 1978 }, 1979 vsyscall: true, 1980 }, 1981 unix.SYS_GETTIMEOFDAY: { 1982 sysno: unix.SYS_GETTIMEOFDAY, 1983 rules: []syscallRuleAction{ 1984 { 1985 rule: MatchAll{}, 1986 action: linux.SECCOMP_RET_ERRNO, 1987 }, 1988 }, 1989 vsyscall: true, 1990 }, 1991 }, 1992 trivial: map[uintptr]singleSyscallRuleSet{ 1993 unix.SYS_READ: { 1994 sysno: unix.SYS_READ, 1995 rules: []syscallRuleAction{ 1996 { 1997 rule: MatchAll{}, 1998 action: linux.SECCOMP_RET_TRACE, 1999 }, 2000 }, 2001 vsyscall: false, 2002 }, 2003 unix.SYS_CHDIR: { 2004 sysno: unix.SYS_CHDIR, 2005 rules: []syscallRuleAction{ 2006 { 2007 rule: MatchAll{}, 2008 action: linux.SECCOMP_RET_KILL_THREAD, 2009 }, 2010 }, 2011 vsyscall: false, 2012 }, 2013 unix.SYS_CLOSE: { 2014 sysno: unix.SYS_CLOSE, 2015 rules: []syscallRuleAction{ 2016 { 2017 rule: MatchAll{}, 2018 action: linux.SECCOMP_RET_KILL_THREAD, 2019 }, 2020 }, 2021 vsyscall: false, 2022 }, 2023 unix.SYS_FCHDIR: { 2024 sysno: unix.SYS_FCHDIR, 2025 rules: []syscallRuleAction{ 2026 { 2027 rule: MatchAll{}, 2028 action: linux.SECCOMP_RET_KILL_PROCESS, 2029 }, 2030 }, 2031 vsyscall: false, 2032 }, 2033 }, 2034 hotNonTrivialOrder: []uintptr{ 2035 // SYS_READ becomes trivial so it does not show up here. 2036 unix.SYS_WRITE, 2037 unix.SYS_FLOCK, 2038 // SYS_CLOSE also becomes trivial. 2039 unix.SYS_OPENAT, 2040 unix.SYS_GETPID, 2041 // SYS_GETTID does not show up in any RuleSet so it 2042 // also does not show up here. 2043 }, 2044 }, 2045 }, 2046 } { 2047 t.Run(test.name, func(t *testing.T) { 2048 test.options.Optimize = true 2049 got, _, gotErr := orderRuleSets(test.ruleSets, test.options) 2050 if (gotErr != nil) != test.wantErr { 2051 t.Errorf("got error: %v, want error: %v", gotErr, test.wantErr) 2052 } 2053 if gotErr != nil || t.Failed() { 2054 return 2055 } 2056 // Replace empty maps with nil for simpler comparison output. 2057 for _, ors := range []*orderedRuleSets{&got, &test.want} { 2058 if len(ors.hotNonTrivial) == 0 { 2059 ors.hotNonTrivial = nil 2060 } 2061 if len(ors.hotNonTrivialOrder) == 0 { 2062 ors.hotNonTrivialOrder = nil 2063 } 2064 if len(ors.coldNonTrivial) == 0 { 2065 ors.coldNonTrivial = nil 2066 } 2067 if len(ors.trivial) == 0 { 2068 ors.trivial = nil 2069 } 2070 } 2071 // Run optimizers on all rules of `test.want`. 2072 for _, m := range []map[uintptr]singleSyscallRuleSet{ 2073 test.want.hotNonTrivial, 2074 test.want.coldNonTrivial, 2075 test.want.trivial, 2076 } { 2077 for sysno, ssrs := range m { 2078 for i, r := range ssrs.rules { 2079 ssrs.rules[i].rule = optimizeSyscallRule(r.rule) 2080 } 2081 m[sysno] = ssrs 2082 } 2083 } 2084 if !reflect.DeepEqual(got, test.want) { 2085 t.Errorf("got orderedRuleSets:\n%v\nwant orderedRuleSets:\n%v\n", got, test.want) 2086 t.Error("got:") 2087 got.log(t.Errorf) 2088 t.Errorf("want:") 2089 test.want.log(t.Errorf) 2090 return 2091 } 2092 // Log the orderedRuleSet, if only to make sure the log function 2093 // actually works and doesn't panic: 2094 t.Log("orderedRuleSets matched expectations:") 2095 got.log(t.Logf) 2096 2097 // Attempt to render the `orderedRuleSets`. 2098 // We don't check the instructions it generates, but the rendering 2099 // code checks itself and panics if it fails an assertion. 2100 program := &syscallProgram{bpf.NewProgramBuilder()} 2101 if err := got.render(program); err != nil { 2102 t.Fatalf("got unexpected error while rendering: %v", err) 2103 } 2104 }) 2105 } 2106 }