1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Selftest for binfmt_misc bpf-backed ('B') handlers. 4 * 5 * A handler is a struct binfmt_misc_ops struct_ops map with a sleepable match 6 * and a sleepable load program. Attaching it publishes it by name in the 7 * caller's user namespace; a 'B' entry referencing it by name in the 8 * interpreter field activates it: 9 * 10 * echo ':name:B::::<handler>:' > /proc/sys/fs/binfmt_misc/register 11 * 12 * Five self-contained cases are exercised: 13 * 14 * 1. bpf_interp: the match program matches a synthetic aarch64 ELF header 15 * from the prefetched bprm->buf and the load program routes it to a 16 * fixed interpreter of its choosing. 17 * 2. nix_origin: the match program reads the binary's program headers to 18 * commit only to a "$ORIGIN/..."-relative PT_INTERP and the load program 19 * resolves it to an interpreter co-located with the binary (the 20 * relocatable-loader case the kernel ELF loader cannot express). 21 * 3. transparent: the load program sets BPF_BINPRM_TRANSPARENT; the 22 * asserting interpreter (binfmt_transparent_interp) verifies the 23 * identity the kernel constructed (exe link, argv, cmdline, comm, 24 * AT_EXECFD, write denial) from inside the process. 25 * 4. loader: the load program sets BPF_BINPRM_LOADER; the payload 26 * (binfmt_loader_payload) runs as the main image with the selected 27 * interpreter substituted for its PT_INTERP and asserts the native 28 * identity from inside. 29 * 5. interp_bind: an entry registered disabled with 'D' is given its 30 * interpreters one write at a time, and the load program picks one by 31 * name per exec. Replacing what the path holds afterwards changes 32 * nothing, which is the point of binding a file rather than resolving 33 * a name at exec time. Enabling the entry seals it. 34 * 35 * The first two route to a test interpreter that prints BPF_INTERP_RAN, 36 * proving the program's chosen interpreter actually ran. 37 */ 38 #define _GNU_SOURCE 39 #include <elf.h> 40 #include <limits.h> 41 #include <sched.h> 42 #include <stdio.h> 43 #include <stdlib.h> 44 #include <string.h> 45 #include <unistd.h> 46 #include <fcntl.h> 47 48 #include <bpf/btf.h> 49 #include <bpf/libbpf.h> 50 51 #include "binfmt_misc_common.h" 52 #include "kselftest_harness.h" 53 54 #define INTERP_PATH "/tmp/binfmt_bpf_interp" 55 #define AARCH64_PATH "/tmp/binfmt_bpf_aarch64" 56 #define RELOC_TEMPLATE "/tmp/binfmt_relocXXXXXX" 57 #define TRANS_INTERP "/tmp/binfmt_transparent_interp" 58 #define TRANS_PATH "/tmp/binfmt_bpf_riscv" 59 #define EXPECT "BPF_INTERP_RAN" 60 #define TRANS_EXPECT "TRANSPARENT_OK" 61 #define LOADER_INTERP "/tmp/binfmt_loader_interp" 62 #define LOADER_PATH "/tmp/binfmt_bpf_loader.ldrtest" 63 #define BIND_FIRST "/tmp/binfmt_bind_first" 64 #define BIND_SECOND "/tmp/binfmt_bind_second" 65 #define BIND_ARM_PATH "/tmp/binfmt_bind_arm" 66 #define BIND_RISCV_PATH "/tmp/binfmt_bind_riscv" 67 #define BIND_EXPECT "BIND_RAN " 68 #define BIND_MAX 100 69 #define INTERP_LIMIT "/proc/sys/user/max_binfmt_misc_interpreters" 70 /* Exit status of the binding child when it cannot set up a budget of its own. */ 71 #define BIND_NO_BUDGET 200 72 73 /* A minimal 64-bit little-endian ELF header, padded to the read size. */ 74 static int create_fake_elf(const char *path, unsigned short machine) 75 { 76 unsigned char hdr[256] = {0}; 77 int fd; 78 79 hdr[0] = 0x7f; hdr[1] = 'E'; hdr[2] = 'L'; hdr[3] = 'F'; 80 hdr[4] = ELFCLASS64; 81 hdr[5] = ELFDATA2LSB; 82 hdr[6] = EV_CURRENT; 83 hdr[16] = ET_EXEC; 84 hdr[18] = machine & 0xff; /* e_machine, little-endian */ 85 hdr[19] = machine >> 8; 86 hdr[20] = EV_CURRENT; 87 88 unlink(path); 89 fd = open(path, O_WRONLY | O_CREAT | O_EXCL, 0755); 90 if (fd < 0) 91 return -1; 92 if (write(fd, hdr, sizeof(hdr)) != (ssize_t)sizeof(hdr)) { 93 close(fd); 94 return -1; 95 } 96 close(fd); 97 return 0; 98 } 99 100 /* 101 * Register a 'B' entry for @handler. With @flags "D" the entry is created 102 * disabled, which is what leaves it open to being given interpreters. 103 */ 104 static int register_entry(const char *name, const char *handler, 105 const char *flags) 106 { 107 char rule[PATH_MAX]; 108 109 snprintf(rule, sizeof(rule), ":%s:B::::%s:%s", name, handler, 110 flags ? flags : ""); 111 return write_reg(rule); 112 } 113 114 static int check_output(const char *cmd, const char *expected) 115 { 116 char buf[128]; 117 FILE *fp; 118 119 fp = popen(cmd, "r"); 120 if (!fp) 121 return -1; 122 if (!fgets(buf, sizeof(buf), fp)) { 123 pclose(fp); 124 return -1; 125 } 126 pclose(fp); 127 return strncmp(buf, expected, strlen(expected)) ? -1 : 0; 128 } 129 130 /* Does the kernel BTF know struct binfmt_misc_ops (CONFIG_BINFMT_MISC_BPF)? */ 131 static bool have_binfmt_misc_ops(void) 132 { 133 struct btf *btf = btf__load_vmlinux_btf(); 134 bool have; 135 136 have = btf && btf__find_by_name_kind(btf, "binfmt_misc_ops", 137 BTF_KIND_STRUCT) >= 0; 138 btf__free(btf); 139 return have; 140 } 141 142 /* The reason bpf handler cases cannot run here, NULL if they can. */ 143 static const char *bpf_handler_unsupported(void) 144 { 145 if (getuid() != 0) 146 return "test must be run as root"; 147 if (!have_binfmt_misc_ops()) 148 return "no struct binfmt_misc_ops in the kernel BTF (CONFIG_BINFMT_MISC_BPF)"; 149 if (!binfmt_misc_available()) 150 return "no binfmt_misc"; 151 return NULL; 152 } 153 154 /* An attached handler with its 'B' entry activated. */ 155 struct bpf_case { 156 struct bpf_object *obj; 157 struct bpf_link *link; 158 const char *entry; 159 }; 160 161 /* 162 * Load @objfile, attach its struct_ops map @handler (which publishes the 163 * handler) and register a 'B' entry named @entry that references it, with 164 * @flags as the entry's register-string flags. 165 */ 166 static int bpf_case_start_flags(struct bpf_case *c, const char *objfile, 167 const char *handler, const char *entry, 168 const char *flags) 169 { 170 struct bpf_map *map; 171 172 c->obj = NULL; 173 c->link = NULL; 174 c->entry = entry; 175 176 c->obj = bpf_object__open_file(objfile, NULL); 177 if (!c->obj || libbpf_get_error(c->obj)) { 178 fprintf(stderr, "open %s failed\n", objfile); 179 c->obj = NULL; 180 return -1; 181 } 182 if (bpf_object__load(c->obj)) { 183 fprintf(stderr, "load %s failed (check dmesg for the verifier log)\n", 184 objfile); 185 goto fail; 186 } 187 map = bpf_object__find_map_by_name(c->obj, handler); 188 if (!map) { 189 fprintf(stderr, "no struct_ops map '%s' in %s\n", handler, objfile); 190 goto fail; 191 } 192 c->link = bpf_map__attach_struct_ops(map); 193 if (!c->link || libbpf_get_error(c->link)) { 194 fprintf(stderr, "attach struct_ops '%s' failed\n", handler); 195 c->link = NULL; 196 goto fail; 197 } 198 if (register_entry(entry, handler, flags)) { 199 fprintf(stderr, "register 'B' entry '%s' failed\n", entry); 200 goto fail; 201 } 202 return 0; 203 204 fail: 205 bpf_link__destroy(c->link); 206 bpf_object__close(c->obj); 207 c->obj = NULL; 208 c->link = NULL; 209 return -1; 210 } 211 212 static int bpf_case_start(struct bpf_case *c, const char *objfile, 213 const char *handler, const char *entry) 214 { 215 return bpf_case_start_flags(c, objfile, handler, entry, NULL); 216 } 217 218 static void bpf_case_stop(struct bpf_case *c) 219 { 220 unregister(c->entry); 221 bpf_link__destroy(c->link); 222 bpf_object__close(c->obj); 223 } 224 225 /* Activate @handler, run @target and check it produced @expect. */ 226 static int run_case(const char *objfile, const char *handler, 227 const char *entry, const char *target, const char *expect) 228 { 229 struct bpf_case c; 230 int ret; 231 232 if (bpf_case_start(&c, objfile, handler, entry)) 233 return -1; 234 ret = check_output(target, expect); 235 bpf_case_stop(&c); 236 return ret; 237 } 238 239 FIXTURE(bpf_handler) { 240 char obj[PATH_MAX]; /* struct_ops object of the case under test */ 241 }; 242 243 FIXTURE_SETUP(bpf_handler) 244 { 245 char src[PATH_MAX]; 246 const char *why = bpf_handler_unsupported(); 247 248 if (why) 249 SKIP(return, "%s", why); 250 251 /* Shared test interpreter. */ 252 ASSERT_EQ(artifact_path(src, sizeof(src), "binfmt_bpf_interp"), 0); 253 ASSERT_EQ(copy_file(src, INTERP_PATH), 0); 254 } 255 256 FIXTURE_TEARDOWN(bpf_handler) 257 { 258 unlink(INTERP_PATH); 259 } 260 261 /* The match program matches a synthetic header, the load program routes it. */ 262 TEST_F(bpf_handler, fixed_interpreter) 263 { 264 ASSERT_EQ(create_fake_elf(AARCH64_PATH, EM_AARCH64), 0); 265 ASSERT_EQ(artifact_path(self->obj, sizeof(self->obj), 266 "bpf_interp.bpf.o"), 0); 267 EXPECT_EQ(run_case(self->obj, "bpf_interp", "test_bpf_interp", 268 AARCH64_PATH, EXPECT), 0); 269 unlink(AARCH64_PATH); 270 } 271 272 /* A "$ORIGIN/..." PT_INTERP resolved to an interpreter next to the binary. */ 273 TEST_F(bpf_handler, origin_relative_interpreter) 274 { 275 char src[PATH_MAX], app[PATH_MAX], interp[PATH_MAX]; 276 char dir[] = RELOC_TEMPLATE; 277 278 ASSERT_NE(mkdtemp(dir), NULL); 279 snprintf(app, sizeof(app), "%s/app", dir); 280 snprintf(interp, sizeof(interp), "%s/binfmt_bpf_interp", dir); 281 ASSERT_EQ(artifact_path(src, sizeof(src), "binfmt_bpf_app"), 0); 282 ASSERT_EQ(copy_file(src, app), 0); 283 ASSERT_EQ(copy_file(INTERP_PATH, interp), 0); 284 285 ASSERT_EQ(artifact_path(self->obj, sizeof(self->obj), 286 "nix_origin.bpf.o"), 0); 287 EXPECT_EQ(run_case(self->obj, "nix_origin", "test_bpf_origin", 288 app, EXPECT), 0); 289 290 unlink(app); 291 unlink(interp); 292 rmdir(dir); 293 } 294 295 /* A transparent dispatch: the process presents as the binary, not the interp. */ 296 TEST_F(bpf_handler, transparent_dispatch) 297 { 298 char src[PATH_MAX], cmd[PATH_MAX + 16]; 299 300 /* Probe for transparent-mode support via its static counterpart. */ 301 if (!binfmt_flag_supported('T')) 302 SKIP(return, "kernel without transparent mode"); 303 304 ASSERT_EQ(artifact_path(src, sizeof(src), "binfmt_transparent_interp"), 0); 305 ASSERT_EQ(copy_file(src, TRANS_INTERP), 0); 306 ASSERT_EQ(create_fake_elf(TRANS_PATH, EM_RISCV), 0); 307 308 setenv("BINFMT_TEST_BINARY", TRANS_PATH, 1); 309 snprintf(cmd, sizeof(cmd), "%s argone argtwo", TRANS_PATH); 310 ASSERT_EQ(artifact_path(self->obj, sizeof(self->obj), 311 "transparent.bpf.o"), 0); 312 EXPECT_EQ(run_case(self->obj, "transparent", "test_bpf_transparent", 313 cmd, TRANS_EXPECT), 0); 314 315 unlink(TRANS_PATH); 316 unlink(TRANS_INTERP); 317 } 318 319 /* A per-exec loader substitution: the payload runs as a native exec. */ 320 TEST_F(bpf_handler, loader_substitution) 321 { 322 char src[PATH_MAX], loader[PATH_MAX]; 323 struct bpf_case c; 324 int status; 325 326 if (find_loader(loader, sizeof(loader))) 327 SKIP(return, "cannot determine own PT_INTERP"); 328 329 ASSERT_EQ(copy_file(loader, LOADER_INTERP), 0); 330 ASSERT_EQ(artifact_path(src, sizeof(src), "binfmt_loader_payload"), 0); 331 ASSERT_EQ(copy_file(src, LOADER_PATH), 0); 332 ASSERT_EQ(patch_file(LOADER_PATH, EI_PAD, LOADER_MARKER, 333 strlen(LOADER_MARKER)), 0); 334 ASSERT_EQ(artifact_path(self->obj, sizeof(self->obj), 335 "loader.bpf.o"), 0); 336 337 setenv("BINFMT_TEST_BINARY", LOADER_PATH, 1); 338 setenv("BINFMT_TEST_INTERP", LOADER_INTERP, 1); 339 340 ASSERT_EQ(bpf_case_start(&c, self->obj, "loader", "test_bpf_loader"), 0); 341 status = run_payload(LOADER_PATH); 342 bpf_case_stop(&c); 343 EXPECT_EQ(status, 0); 344 345 unsetenv("BINFMT_TEST_INTERP"); 346 unlink(LOADER_PATH); 347 unlink(LOADER_INTERP); 348 } 349 350 /* The errno an exec of @path fails with, 0 if it succeeded. */ 351 static int exec_errno(const char *path) 352 { 353 int status; 354 pid_t pid; 355 356 pid = fork(); 357 if (pid == 0) { 358 execl(path, path, (char *)NULL); 359 _exit(errno); 360 } 361 if (pid < 0 || waitpid(pid, &status, 0) != pid || !WIFEXITED(status)) 362 return -1; 363 return WEXITSTATUS(status); 364 } 365 366 /* Install a copy of the bound-interpreter test binary at @path. */ 367 static int install_interp(const char *path) 368 { 369 char src[PATH_MAX]; 370 371 if (artifact_path(src, sizeof(src), "binfmt_bind_interp")) 372 return -1; 373 return copy_file(src, path); 374 } 375 376 /* Bind @path to @entry under @name, the '+' command of a disabled entry. */ 377 static int entry_bind(const char *entry, const char *name, const char *path) 378 { 379 char cmd[PATH_MAX]; 380 381 snprintf(cmd, sizeof(cmd), "+%s %s\n", name, path); 382 return entry_command(entry, cmd); 383 } 384 385 /* Set the interpreter budget of this namespace. */ 386 static int write_interp_limit(const char *val) 387 { 388 ssize_t n; 389 int fd; 390 391 fd = open(INTERP_LIMIT, O_WRONLY | O_CLOEXEC); 392 if (fd < 0) 393 return -1; 394 n = write(fd, val, strlen(val)); 395 close(fd); 396 return n < 0 ? -1 : 0; 397 } 398 399 /* 400 * The errno a bind is refused with when the writer is a child that has spent 401 * the budget of a user namespace of its own, 0 if it succeeded and -1 if the 402 * child could not set itself up. The fd is opened here and inherited, so the 403 * interpreter is still opened with this process's credentials. 404 */ 405 static int bind_out_of_budget(const char *entry, const char *name, 406 const char *path) 407 { 408 char cmd[PATH_MAX], file[PATH_MAX]; 409 int fd, status, retval; 410 pid_t pid; 411 412 snprintf(file, sizeof(file), BINFMT_DIR "/%s", entry); 413 snprintf(cmd, sizeof(cmd), "+%s %s\n", name, path); 414 415 fd = open(file, O_WRONLY | O_CLOEXEC); 416 if (fd < 0) 417 return -1; 418 419 pid = fork(); 420 if (pid == 0) { 421 ssize_t n; 422 423 /* A namespace of its own, with nothing left in it to spend. */ 424 if (unshare(CLONE_NEWUSER) || write_interp_limit("0")) 425 _exit(BIND_NO_BUDGET); 426 n = write(fd, cmd, strlen(cmd)); 427 _exit(n < 0 ? errno : 0); 428 } 429 close(fd); 430 if (pid < 0 || waitpid(pid, &status, 0) != pid || !WIFEXITED(status)) 431 return -1; 432 retval = WEXITSTATUS(status); 433 return retval == BIND_NO_BUDGET ? -1 : retval; 434 } 435 436 FIXTURE(bound_interp) { 437 char obj[PATH_MAX]; 438 struct bpf_case c; 439 bool started; 440 }; 441 442 FIXTURE_SETUP(bound_interp) 443 { 444 const char *why = bpf_handler_unsupported(); 445 446 if (why) 447 SKIP(return, "%s", why); 448 if (!binfmt_flag_supported('D')) { 449 ASSERT_EQ(errno, EINVAL); 450 SKIP(return, "kernel without the 'D' flag"); 451 } 452 453 ASSERT_EQ(install_interp(BIND_FIRST), 0); 454 ASSERT_EQ(install_interp(BIND_SECOND), 0); 455 456 ASSERT_EQ(artifact_path(self->obj, sizeof(self->obj), 457 "interp_bind.bpf.o"), 0); 458 459 /* 460 * Registered disabled, so it cannot be matched yet and can still be 461 * given interpreters. Each path is resolved once, by its write(2); 462 * from here on the entry holds the files themselves. 463 */ 464 ASSERT_EQ(bpf_case_start_flags(&self->c, self->obj, "interp_bind", 465 "test_interp_bind", "D"), 0); 466 self->started = true; 467 468 ASSERT_EQ(entry_bind("test_interp_bind", "first", BIND_FIRST), 0); 469 ASSERT_EQ(entry_bind("test_interp_bind", "second", BIND_SECOND), 0); 470 } 471 472 FIXTURE_TEARDOWN(bound_interp) 473 { 474 if (self->started) 475 bpf_case_stop(&self->c); 476 unlink(BIND_FIRST); 477 unlink(BIND_SECOND); 478 unlink(AARCH64_PATH); 479 unlink(BIND_RISCV_PATH); 480 unlink(BIND_ARM_PATH); 481 } 482 483 /* Enabling is what makes the configured entry matchable. */ 484 static int activate(const char *entry) 485 { 486 return entry_command(entry, "1\n"); 487 } 488 489 /* One entry, one interpreter per guest architecture, picked per exec. */ 490 TEST_F(bound_interp, selects_by_name) 491 { 492 ASSERT_EQ(create_fake_elf(AARCH64_PATH, EM_AARCH64), 0); 493 ASSERT_EQ(create_fake_elf(BIND_RISCV_PATH, EM_RISCV), 0); 494 495 /* Disabled, so it does not match and no format claims the binary. */ 496 EXPECT_EQ(exec_errno(AARCH64_PATH), ENOEXEC); 497 498 ASSERT_EQ(activate("test_interp_bind"), 0); 499 EXPECT_EQ(check_output(AARCH64_PATH, BIND_EXPECT BIND_FIRST), 0); 500 EXPECT_EQ(check_output(BIND_RISCV_PATH, BIND_EXPECT BIND_SECOND), 0); 501 } 502 503 /* What was bound is what runs, whatever the path holds afterwards. */ 504 TEST_F(bound_interp, path_no_longer_decides) 505 { 506 char other[PATH_MAX]; 507 508 ASSERT_EQ(create_fake_elf(AARCH64_PATH, EM_AARCH64), 0); 509 ASSERT_EQ(activate("test_interp_bind"), 0); 510 511 /* Bound interpreters are pinned against writes, exactly like 'F'. */ 512 EXPECT_TRUE(write_denied(BIND_FIRST)); 513 514 /* Replace the path with a different binary: a new file, new inode. */ 515 ASSERT_EQ(artifact_path(other, sizeof(other), "binfmt_bpf_interp"), 0); 516 ASSERT_EQ(unlink(BIND_FIRST), 0); 517 ASSERT_EQ(copy_file(other, BIND_FIRST), 0); 518 519 EXPECT_EQ(check_output(AARCH64_PATH, BIND_EXPECT BIND_FIRST), 0); 520 } 521 522 /* The entry reports what it bound, under the names it bound them as. */ 523 TEST_F(bound_interp, entry_reports_bindings) 524 { 525 EXPECT_TRUE(entry_shows("test_interp_bind", 526 "bpf-interpreter first " BIND_FIRST)); 527 EXPECT_TRUE(entry_shows("test_interp_bind", 528 "bpf-interpreter second " BIND_SECOND)); 529 } 530 531 /* Selecting a name the entry did not bind fails the exec. */ 532 TEST_F(bound_interp, unbound_name_fails) 533 { 534 ASSERT_EQ(create_fake_elf(BIND_ARM_PATH, EM_ARM), 0); 535 ASSERT_EQ(activate("test_interp_bind"), 0); 536 537 EXPECT_EQ(exec_errno(BIND_ARM_PATH), ENOENT); 538 } 539 540 /* Activating seals it: what can be matched cannot be changed. */ 541 TEST_F(bound_interp, sealed_once_active) 542 { 543 ASSERT_EQ(activate("test_interp_bind"), 0); 544 545 EXPECT_EQ(entry_bind("test_interp_bind", "third", BIND_SECOND), -EBUSY); 546 EXPECT_FALSE(entry_shows("test_interp_bind", 547 "bpf-interpreter third " BIND_SECOND)); 548 } 549 550 /* The seal is for good: disabling the entry again reopens nothing. */ 551 TEST_F(bound_interp, disable_does_not_unseal) 552 { 553 ASSERT_EQ(activate("test_interp_bind"), 0); 554 ASSERT_EQ(entry_command("test_interp_bind", "0\n"), 0); 555 556 EXPECT_EQ(entry_bind("test_interp_bind", "third", BIND_SECOND), -EBUSY); 557 } 558 559 /* An entry registered without 'D' is sealed from the start. */ 560 TEST_F(bound_interp, born_sealed) 561 { 562 /* A second entry for the handler the fixture already published. */ 563 ASSERT_EQ(register_entry("test_born_sealed", "interp_bind", NULL), 0); 564 565 EXPECT_EQ(entry_bind("test_born_sealed", "first", BIND_FIRST), -EBUSY); 566 unregister("test_born_sealed"); 567 } 568 569 /* A name is bound once; a second use of it is refused. */ 570 TEST_F(bound_interp, duplicate_name_refused) 571 { 572 EXPECT_EQ(entry_bind("test_interp_bind", "first", BIND_SECOND), -EEXIST); 573 } 574 575 /* A name is a printable word: the entry file reports 'name path' lines. */ 576 TEST_F(bound_interp, name_must_be_printable) 577 { 578 /* A control character would forge a line into the entry file. */ 579 EXPECT_EQ(entry_bind("test_interp_bind", "a\tb", BIND_FIRST), -EINVAL); 580 EXPECT_EQ(entry_bind("test_interp_bind", "a\nb", BIND_FIRST), -EINVAL); 581 582 /* A space cannot even be spelled: the path starts after the first one. */ 583 EXPECT_EQ(entry_bind("test_interp_bind", "a b", BIND_FIRST), -EINVAL); 584 } 585 586 /* The command ends at the write: bytes past an embedded nul are refused. */ 587 TEST_F(bound_interp, trailing_bytes_refused) 588 { 589 char cmd[PATH_MAX]; 590 size_t len; 591 int fd; 592 593 /* entry_command() cannot spell a nul, so write the buffer raw. */ 594 snprintf(cmd, sizeof(cmd), "+nul %s", BIND_FIRST); 595 len = strlen(cmd) + 1; 596 memcpy(cmd + len, "junk", sizeof("junk")); 597 len += sizeof("junk"); 598 599 fd = open(BINFMT_DIR "/test_interp_bind", O_WRONLY | O_CLOEXEC); 600 ASSERT_GE(fd, 0); 601 EXPECT_EQ(write(fd, cmd, len), -1); 602 EXPECT_EQ(errno, EINVAL); 603 close(fd); 604 605 EXPECT_FALSE(entry_shows("test_interp_bind", 606 "bpf-interpreter nul " BIND_FIRST)); 607 } 608 609 /* An entry binds at most BIND_MAX interpreters. */ 610 TEST_F(bound_interp, capped_bindings) 611 { 612 char name[16]; 613 int i; 614 615 /* The fixture bound "first" and "second" already. */ 616 for (i = 2; i < BIND_MAX; i++) { 617 snprintf(name, sizeof(name), "n%d", i); 618 ASSERT_EQ(entry_bind("test_interp_bind", name, BIND_FIRST), 0); 619 } 620 EXPECT_EQ(entry_bind("test_interp_bind", "over", BIND_FIRST), -ENOSPC); 621 } 622 623 /* A binding pins a file: it is charged, and refused once the budget is out. */ 624 TEST_F(bound_interp, bindings_are_charged) 625 { 626 int err = bind_out_of_budget("test_interp_bind", "third", BIND_FIRST); 627 628 if (err < 0) 629 SKIP(return, "no user namespaces or no " INTERP_LIMIT); 630 631 /* The charge follows the writer, not the entry file it writes to. */ 632 EXPECT_EQ(err, ENOSPC); 633 634 /* The budget was the only thing in the way. */ 635 EXPECT_EQ(entry_bind("test_interp_bind", "third", BIND_FIRST), 0); 636 } 637 638 TEST_HARNESS_MAIN 639