1 // SPDX-License-Identifier: GPL-2.0 2 /* Copyright (c) 2026 Isovalent */ 3 4 #include <test_progs.h> 5 #include <sys/syscall.h> 6 #include <sys/mman.h> 7 #include <sys/wait.h> 8 #include <sys/stat.h> 9 #include <fcntl.h> 10 #include <limits.h> 11 #include <linux/keyctl.h> 12 #include <linux/bpf.h> 13 14 #include <bpf/btf.h> 15 16 #include "bpf/libbpf_internal.h" /* for libbpf_sha256() */ 17 #include "bpf/skel_internal.h" /* for loader ctx layout (bpf_loader_ctx etc) */ 18 19 #include "test_signed_loader.skel.h" 20 #include "test_signed_loader_map.skel.h" 21 #include "test_signed_loader_data.skel.h" 22 #include "test_signed_loader_lsm.skel.h" 23 24 enum { 25 BPF_SIG_UNSIGNED = 0, 26 BPF_SIG_VERIFIED, 27 }; 28 29 enum { 30 BPF_SIG_KEYRING_NONE = 0, 31 BPF_SIG_KEYRING_BUILTIN, 32 BPF_SIG_KEYRING_SECONDARY, 33 BPF_SIG_KEYRING_PLATFORM, 34 BPF_SIG_KEYRING_USER, 35 }; 36 37 static int load_loader(const void *insns, __u32 insns_sz, int map_fd, 38 const void *sig, __u32 sig_sz, __s32 keyring_id, 39 __u32 fd_array_cnt) 40 { 41 union bpf_attr attr; 42 int fd; 43 44 memset(&attr, 0, sizeof(attr)); 45 attr.prog_type = BPF_PROG_TYPE_SYSCALL; 46 attr.insns = ptr_to_u64(insns); 47 attr.insn_cnt = insns_sz / sizeof(struct bpf_insn); 48 attr.license = ptr_to_u64("Dual BSD/GPL"); 49 attr.prog_flags = BPF_F_SLEEPABLE; 50 attr.fd_array = ptr_to_u64(&map_fd); 51 if (sig) { 52 attr.signature = ptr_to_u64(sig); 53 attr.signature_size = sig_sz; 54 attr.keyring_id = keyring_id; 55 } 56 attr.fd_array_cnt = fd_array_cnt; 57 memcpy(attr.prog_name, "__loader.prog", sizeof("__loader.prog")); 58 fd = syscall(__NR_bpf, BPF_PROG_LOAD, &attr, 59 offsetofend(union bpf_attr, keyring_id)); 60 return fd < 0 ? -errno : fd; 61 } 62 63 static int run_gen_loader(const void *insns, __u32 insns_sz, 64 const void *data, __u32 data_sz, 65 const void *excl, __u32 excl_sz, 66 const void *sig, __u32 sig_sz, 67 void *ctx, __u32 ctx_sz, bool *loader_ran) 68 { 69 LIBBPF_OPTS(bpf_map_create_opts, mopts, 70 .excl_prog_hash = excl, 71 .excl_prog_hash_size = excl_sz); 72 __u32 key = 0; 73 union bpf_attr attr; 74 int map_fd, prog_fd, ret; 75 76 *loader_ran = false; 77 78 map_fd = bpf_map_create(BPF_MAP_TYPE_ARRAY, "__loader.map", 79 4, data_sz, 1, &mopts); 80 if (map_fd < 0) 81 return -errno; 82 if (bpf_map_update_elem(map_fd, &key, data, 0)) { 83 ret = -errno; 84 goto out_map; 85 } 86 if (bpf_map_freeze(map_fd)) { 87 ret = -errno; 88 goto out_map; 89 } 90 91 memset(&attr, 0, sizeof(attr)); 92 attr.prog_type = BPF_PROG_TYPE_SYSCALL; 93 attr.insns = ptr_to_u64(insns); 94 attr.insn_cnt = insns_sz / sizeof(struct bpf_insn); 95 attr.license = ptr_to_u64("Dual BSD/GPL"); 96 attr.prog_flags = BPF_F_SLEEPABLE; 97 attr.fd_array = ptr_to_u64(&map_fd); 98 if (sig) { 99 attr.signature = ptr_to_u64(sig); 100 attr.signature_size = sig_sz; 101 attr.keyring_id = KEY_SPEC_SESSION_KEYRING; 102 attr.fd_array_cnt = 1; 103 } 104 memcpy(attr.prog_name, "__loader.prog", sizeof("__loader.prog")); 105 prog_fd = syscall(__NR_bpf, BPF_PROG_LOAD, &attr, 106 offsetofend(union bpf_attr, keyring_id)); 107 if (prog_fd < 0) { 108 ret = -errno; 109 goto out_map; 110 } 111 112 memset(&attr, 0, sizeof(attr)); 113 attr.test.prog_fd = prog_fd; 114 attr.test.ctx_in = ptr_to_u64(ctx); 115 attr.test.ctx_size_in = ctx_sz; 116 if (syscall(__NR_bpf, BPF_PROG_RUN, &attr, 117 offsetofend(union bpf_attr, test)) < 0) { 118 ret = -errno; 119 goto out_prog; 120 } 121 *loader_ran = true; 122 ret = (int)attr.test.retval; 123 out_prog: 124 close(prog_fd); 125 out_map: 126 close(map_fd); 127 return ret; 128 } 129 130 static void close_loader_ctx_fds(void *ctx, int nr_maps, int nr_progs) 131 { 132 struct bpf_map_desc *md = (struct bpf_map_desc *)((char *)ctx + 133 sizeof(struct bpf_loader_ctx)); 134 struct bpf_prog_desc *pd = (struct bpf_prog_desc *)(md + nr_maps); 135 int i; 136 137 for (i = 0; i < nr_maps; i++) 138 if (md[i].map_fd > 0) 139 close(md[i].map_fd); 140 for (i = 0; i < nr_progs; i++) 141 if (pd[i].prog_fd > 0) 142 close(pd[i].prog_fd); 143 } 144 145 static int run_setup(const char *cmd, const char *dir) 146 { 147 int pid, status; 148 149 pid = fork(); 150 if (pid < 0) 151 return -errno; 152 if (pid == 0) { 153 execlp("./verify_sig_setup.sh", "./verify_sig_setup.sh", 154 cmd, dir, NULL); 155 exit(1); 156 } 157 if (waitpid(pid, &status, 0) < 0) 158 return -errno; 159 return (WIFEXITED(status) && 160 WEXITSTATUS(status) == 0) ? 0 : -EINVAL; 161 } 162 163 static int sign_buf(const char *dir, const void *buf, __u32 len, 164 void *sig, __u32 *sig_sz) 165 { 166 char data_tmpl[PATH_MAX], key[PATH_MAX]; 167 char sigpath[PATH_MAX + sizeof(".p7s")]; 168 int fd, pid, status, ret; 169 struct stat st; 170 171 ret = snprintf(data_tmpl, sizeof(data_tmpl), "%s/dataXXXXXX", dir); 172 if (ret < 0 || ret >= (int)sizeof(data_tmpl)) 173 return -ENAMETOOLONG; 174 ret = 0; 175 176 fd = mkstemp(data_tmpl); 177 if (fd < 0) 178 return -errno; 179 if (write(fd, buf, len) != (ssize_t)len) { 180 close(fd); 181 ret = -EIO; 182 goto out; 183 } 184 close(fd); 185 186 pid = fork(); 187 if (pid < 0) { 188 ret = -errno; 189 goto out; 190 } 191 if (pid == 0) { 192 snprintf(key, sizeof(key), "%s/signing_key.pem", dir); 193 execlp("./sign-file", "./sign-file", "-d", "sha256", 194 key, key, data_tmpl, NULL); 195 exit(1); 196 } 197 if (waitpid(pid, &status, 0) < 0 || 198 !WIFEXITED(status) || WEXITSTATUS(status)) { 199 ret = -EINVAL; 200 goto out; 201 } 202 203 snprintf(sigpath, sizeof(sigpath), "%s.p7s", data_tmpl); 204 if (stat(sigpath, &st) < 0) { 205 ret = -errno; 206 goto out; 207 } 208 if (st.st_size > (off_t)*sig_sz) { 209 ret = -E2BIG; 210 goto out_sig; 211 } 212 fd = open(sigpath, O_RDONLY); 213 if (fd < 0) { 214 ret = -errno; 215 goto out_sig; 216 } 217 if (read(fd, sig, st.st_size) != st.st_size) { 218 close(fd); 219 ret = -EIO; 220 goto out_sig; 221 } 222 close(fd); 223 *sig_sz = st.st_size; 224 out_sig: 225 unlink(sigpath); 226 out: 227 unlink(data_tmpl); 228 return ret; 229 } 230 231 struct gen_loader_fixture { 232 struct test_signed_loader *skel; 233 struct gen_loader_opts gopts; 234 unsigned char *blob; 235 void *ctx; 236 __u32 data_sz; 237 __u32 ctx_sz; 238 int nr_maps; 239 int nr_progs; 240 __u8 excl[SHA256_DIGEST_LENGTH]; 241 }; 242 243 static int gen_loader_fixture_init(struct gen_loader_fixture *f) 244 { 245 LIBBPF_OPTS(gen_loader_opts, gopts, .gen_hash = true); 246 int nr_maps = 0, nr_progs = 0; 247 struct bpf_program *p; 248 struct bpf_map *m; 249 250 memset(f, 0, sizeof(*f)); 251 f->skel = test_signed_loader__open(); 252 if (!ASSERT_OK_PTR(f->skel, "skel_open")) 253 return -1; 254 if (!ASSERT_OK(bpf_object__gen_loader(f->skel->obj, &gopts), "gen_loader")) 255 return -1; 256 if (!ASSERT_OK(bpf_object__load(f->skel->obj), "gen_load")) 257 return -1; 258 f->gopts = gopts; 259 260 bpf_object__for_each_program(p, f->skel->obj) 261 nr_progs++; 262 bpf_object__for_each_map(m, f->skel->obj) 263 nr_maps++; 264 f->nr_maps = nr_maps; 265 f->nr_progs = nr_progs; 266 f->ctx_sz = sizeof(struct bpf_loader_ctx) + 267 nr_maps * sizeof(struct bpf_map_desc) + 268 nr_progs * sizeof(struct bpf_prog_desc); 269 f->ctx = calloc(1, f->ctx_sz); 270 if (!ASSERT_OK_PTR(f->ctx, "ctx_alloc")) 271 return -1; 272 ((struct bpf_loader_ctx *)f->ctx)->sz = f->ctx_sz; 273 274 f->data_sz = gopts.data_sz; 275 f->blob = malloc(f->data_sz); 276 if (!ASSERT_OK_PTR(f->blob, "blob_alloc")) 277 return -1; 278 memcpy(f->blob, gopts.data, f->data_sz); 279 280 /* excl_prog_hash = SHA256(loader insns) == the loader's prog->digest. */ 281 libbpf_sha256(gopts.insns, gopts.insns_sz, f->excl); 282 return 0; 283 } 284 285 static void gen_loader_fixture_fini(struct gen_loader_fixture *f) 286 { 287 if (f->ctx) 288 close_loader_ctx_fds(f->ctx, f->nr_maps, f->nr_progs); 289 free(f->blob); 290 free(f->ctx); 291 test_signed_loader__destroy(f->skel); 292 } 293 294 static void metadata_match(void) 295 { 296 struct gen_loader_fixture f; 297 bool ran; 298 int r; 299 300 if (gen_loader_fixture_init(&f) == 0) { 301 r = run_gen_loader(f.gopts.insns, f.gopts.insns_sz, f.blob, 302 f.data_sz, f.excl, sizeof(f.excl), NULL, 0, 303 f.ctx, f.ctx_sz, &ran); 304 ASSERT_TRUE(ran, "loader ran"); 305 ASSERT_EQ(r, 0, "honest loader retval"); 306 } 307 gen_loader_fixture_fini(&f); 308 } 309 310 static void signature_enforced(void) 311 { 312 static const __u8 junk[64] = { 0x30, 0x42, 0x13, 0x37, }; 313 struct gen_loader_fixture f; 314 int fd; 315 316 if (gen_loader_fixture_init(&f) == 0) { 317 /* 318 * A present-but-invalid signature (the cert bytes are not a 319 * PKCS#7 signature) must be rejected at load: the signature 320 * path is honored, not ignored. (The valid path is covered by 321 * the signed lskels.) Pin -EBADMSG, the PKCS#7 parse failure: 322 * a looser fd < 0 check could also be satisfied by the sparse 323 * fd_array rejection (-EACCES) that the loader's map reference 324 * would trip even if the signature were silently ignored. 325 */ 326 fd = load_loader(f.gopts.insns, f.gopts.insns_sz, -1, junk, 327 sizeof(junk), KEY_SPEC_SESSION_KEYRING, 0); 328 ASSERT_EQ(fd, -EBADMSG, "invalid signature rejected at load"); 329 if (fd >= 0) 330 close(fd); 331 } 332 gen_loader_fixture_fini(&f); 333 } 334 335 static void signed_nonexcl_fd_array_rejected(void) 336 { 337 static const __u8 junk[64] = { 0x30, 0x42, 0x13, 0x37, }; 338 struct gen_loader_fixture f; 339 int map_fd, fd; 340 341 if (gen_loader_fixture_init(&f) == 0) { 342 /* 343 * A signed program may only bind exclusive maps through fd_array 344 * (their contents are folded into the signature). Binding a 345 * non-exclusive map is rejected, before the signature is even 346 * examined. 347 */ 348 map_fd = bpf_map_create(BPF_MAP_TYPE_ARRAY, "nonexcl", 4, 349 f.data_sz, 1, NULL); 350 if (ASSERT_OK_FD(map_fd, "nonexcl_map")) { 351 if (ASSERT_OK(bpf_map_freeze(map_fd), "freeze")) { 352 fd = load_loader(f.gopts.insns, f.gopts.insns_sz, 353 map_fd, junk, sizeof(junk), 354 KEY_SPEC_SESSION_KEYRING, 1); 355 ASSERT_EQ(fd, -EPERM, 356 "non-exclusive map in signed fd_array rejected"); 357 if (fd >= 0) 358 close(fd); 359 } 360 close(map_fd); 361 } 362 } 363 gen_loader_fixture_fini(&f); 364 } 365 366 static void signed_unfrozen_fd_array_rejected(void) 367 { 368 static const __u8 junk[64] = { 0x30, 0x42, 0x13, 0x37, }; 369 LIBBPF_OPTS(bpf_map_create_opts, mopts); 370 struct gen_loader_fixture f; 371 __u32 key = 0; 372 int map_fd, fd; 373 374 if (gen_loader_fixture_init(&f) == 0) { 375 /* 376 * The metadata map must be frozen before a signed load so the 377 * folded bytes cannot change afterwards. Bind an exclusive map 378 * with matching contents but skip the freeze: the load must be 379 * rejected by the frozen check with -EPERM. The exclusivity 380 * check right after it would pass, so the errno uniquely pins 381 * the freeze requirement. 382 */ 383 mopts.excl_prog_hash = f.excl; 384 mopts.excl_prog_hash_size = sizeof(f.excl); 385 map_fd = bpf_map_create(BPF_MAP_TYPE_ARRAY, "unfrozen", 4, 386 f.data_sz, 1, &mopts); 387 if (ASSERT_OK_FD(map_fd, "unfrozen_map")) { 388 if (ASSERT_OK(bpf_map_update_elem(map_fd, &key, f.blob, 0), 389 "update")) { 390 fd = load_loader(f.gopts.insns, f.gopts.insns_sz, 391 map_fd, junk, sizeof(junk), 392 KEY_SPEC_SESSION_KEYRING, 1); 393 ASSERT_EQ(fd, -EPERM, 394 "unfrozen map in signed fd_array rejected"); 395 if (fd >= 0) 396 close(fd); 397 } 398 close(map_fd); 399 } 400 } 401 gen_loader_fixture_fini(&f); 402 } 403 404 static void signed_nonarray_fd_array_rejected(void) 405 { 406 static const __u8 junk[64] = { 0x30, 0x42, 0x13, 0x37, }; 407 LIBBPF_OPTS(bpf_map_create_opts, mopts); 408 struct gen_loader_fixture f; 409 int map_fd, fd; 410 411 if (gen_loader_fixture_init(&f) == 0) { 412 /* 413 * Only a plain BPF_MAP_TYPE_ARRAY may be folded into the 414 * signature. An exclusive map of any other type is rejected 415 * (-EINVAL) rather than folded - this is the type gate that 416 * keeps arena maps (map_direct_value_addr() returns a user 417 * address) and insn-array maps (buffer smaller than value_size) 418 * out of the hashed region, where the old code would have 419 * memcpy()'d from them. A hash map stands in here: it is 420 * exclusive (bound to the loader digest) but not an array. 421 */ 422 mopts.excl_prog_hash = f.excl; 423 mopts.excl_prog_hash_size = sizeof(f.excl); 424 map_fd = bpf_map_create(BPF_MAP_TYPE_HASH, "excl_hash", 4, 4, 1, 425 &mopts); 426 if (ASSERT_OK_FD(map_fd, "excl_hash_map")) { 427 fd = load_loader(f.gopts.insns, f.gopts.insns_sz, map_fd, 428 junk, sizeof(junk), 429 KEY_SPEC_SESSION_KEYRING, 1); 430 ASSERT_EQ(fd, -EINVAL, 431 "non-array map in signed fd_array rejected"); 432 if (fd >= 0) 433 close(fd); 434 close(map_fd); 435 } 436 } 437 gen_loader_fixture_fini(&f); 438 } 439 440 static int setup_meta_map(const struct gen_loader_fixture *f); 441 442 static void signed_btf_fd_array_rejected(void) 443 { 444 char dir_tmpl[] = "/tmp/signed_loader_btfXXXXXX", *dir = NULL; 445 __u32 sig_sz = 8192; 446 int map_fd = -1, prog_fd = -1; 447 unsigned char *buf = NULL; 448 struct gen_loader_fixture f; 449 bool have_fixture = false; 450 struct btf *btf = NULL; 451 union bpf_attr attr; 452 int fds[2]; 453 __u8 sig[8192]; 454 455 syscall(__NR_request_key, "keyring", "_uid.0", NULL, 456 KEY_SPEC_SESSION_KEYRING); 457 dir = mkdtemp(dir_tmpl); 458 if (!ASSERT_OK_PTR(dir, "mkdtemp")) 459 return; 460 if (!ASSERT_OK(run_setup("setup", dir), "verify_sig_setup")) { 461 rmdir(dir); 462 return; 463 } 464 have_fixture = true; 465 if (gen_loader_fixture_init(&f) != 0) 466 goto out; 467 468 /* 469 * fd_array binds maps and BTFs alike, but only exclusive array maps are 470 * folded into the signature. Build an otherwise genuinely signed load - 471 * insns || metadata, exclusive frozen map at fd_array[0] - then smuggle 472 * an extra BTF into fd_array[1]. A signed program may not bind any BTF, 473 * so resolving the fd_array entries rejects the BTF with -EACCES (in 474 * __add_used_btf(), before the signature is even verified). 475 */ 476 buf = malloc((size_t)f.gopts.insns_sz + f.data_sz); 477 if (!ASSERT_OK_PTR(buf, "signbuf")) 478 goto out; 479 memcpy(buf, f.gopts.insns, f.gopts.insns_sz); 480 memcpy(buf + f.gopts.insns_sz, f.blob, f.data_sz); 481 if (!ASSERT_OK(sign_buf(dir, buf, f.gopts.insns_sz + f.data_sz, sig, 482 &sig_sz), "sign insns||metadata")) 483 goto out; 484 485 map_fd = setup_meta_map(&f); 486 if (!ASSERT_OK_FD(map_fd, "meta_map")) 487 goto out; 488 btf = btf__new_empty(); 489 if (!ASSERT_OK_PTR(btf, "btf_new_empty")) 490 goto out; 491 btf__add_int(btf, "int", 4, BTF_INT_SIGNED); 492 if (!ASSERT_OK(btf__load_into_kernel(btf), "btf_load")) 493 goto out; 494 495 fds[0] = map_fd; 496 fds[1] = btf__fd(btf); 497 memset(&attr, 0, sizeof(attr)); 498 attr.prog_type = BPF_PROG_TYPE_SYSCALL; 499 attr.insns = ptr_to_u64(f.gopts.insns); 500 attr.insn_cnt = f.gopts.insns_sz / sizeof(struct bpf_insn); 501 attr.license = ptr_to_u64("Dual BSD/GPL"); 502 attr.prog_flags = BPF_F_SLEEPABLE; 503 attr.fd_array = ptr_to_u64(fds); 504 attr.fd_array_cnt = 2; 505 attr.signature = ptr_to_u64(sig); 506 attr.signature_size = sig_sz; 507 attr.keyring_id = KEY_SPEC_SESSION_KEYRING; 508 memcpy(attr.prog_name, "__loader.prog", sizeof("__loader.prog")); 509 prog_fd = syscall(__NR_bpf, BPF_PROG_LOAD, &attr, 510 offsetofend(union bpf_attr, keyring_id)); 511 ASSERT_EQ(prog_fd < 0 ? -errno : prog_fd, -EACCES, 512 "BTF in signed fd_array rejected"); 513 if (prog_fd >= 0) 514 close(prog_fd); 515 out: 516 if (btf) 517 btf__free(btf); 518 if (map_fd >= 0) 519 close(map_fd); 520 if (have_fixture) 521 gen_loader_fixture_fini(&f); 522 if (dir) 523 run_setup("cleanup", dir); 524 free(buf); 525 } 526 527 static void signature_failure_logs(void) 528 { 529 static const __u8 junk[64] = { 0x30, 0x42, 0x13, 0x37, }; 530 char log_buf[1024] = {}; 531 struct gen_loader_fixture f; 532 union bpf_attr attr; 533 int fd; 534 535 if (gen_loader_fixture_init(&f) == 0) { 536 /* 537 * Signature verification now runs inside bpf_check(), so a 538 * failure is reported through the verifier log. A present-but- 539 * invalid signature is rejected and the log says why. 540 */ 541 memset(&attr, 0, sizeof(attr)); 542 attr.prog_type = BPF_PROG_TYPE_SYSCALL; 543 attr.insns = ptr_to_u64(f.gopts.insns); 544 attr.insn_cnt = f.gopts.insns_sz / sizeof(struct bpf_insn); 545 attr.license = ptr_to_u64("Dual BSD/GPL"); 546 attr.prog_flags = BPF_F_SLEEPABLE; 547 attr.signature = ptr_to_u64(junk); 548 attr.signature_size = sizeof(junk); 549 attr.keyring_id = KEY_SPEC_SESSION_KEYRING; 550 attr.log_level = 1; 551 attr.log_buf = ptr_to_u64(log_buf); 552 attr.log_size = sizeof(log_buf); 553 memcpy(attr.prog_name, "__loader.prog", sizeof("__loader.prog")); 554 555 fd = syscall(__NR_bpf, BPF_PROG_LOAD, &attr, 556 offsetofend(union bpf_attr, keyring_id)); 557 ASSERT_LT(fd, 0, "invalid signature rejected at load"); 558 if (fd >= 0) 559 close(fd); 560 ASSERT_HAS_SUBSTR(log_buf, "signature verification failed", 561 "verifier logs signature failure"); 562 } 563 gen_loader_fixture_fini(&f); 564 } 565 566 static void signature_too_large(void) 567 { 568 static const __u8 junk[64] = {}; 569 struct gen_loader_fixture f; 570 int fd; 571 572 if (gen_loader_fixture_init(&f) == 0) { 573 /* 574 * signature_size beyond the kernel's bound (KMALLOC_MAX_CACHE_SIZE) 575 * is rejected before the buffer is read. 576 */ 577 fd = load_loader(f.gopts.insns, f.gopts.insns_sz, -1, junk, 578 64 << 20, KEY_SPEC_SESSION_KEYRING, 0); 579 ASSERT_EQ(fd, -EINVAL, "oversized signature rejected"); 580 if (fd >= 0) 581 close(fd); 582 } 583 gen_loader_fixture_fini(&f); 584 } 585 586 static void signature_zero_size(void) 587 { 588 static const __u8 junk[64] = {}; 589 struct gen_loader_fixture f; 590 int fd; 591 592 if (gen_loader_fixture_init(&f) == 0) { 593 /* 594 * A present signature with signature_size == 0 is rejected 595 * up front, before the keyring is resolved or the signature 596 * buffer is read. 597 */ 598 fd = load_loader(f.gopts.insns, f.gopts.insns_sz, -1, junk, 599 0, KEY_SPEC_SESSION_KEYRING, 0); 600 ASSERT_EQ(fd, -EINVAL, "zero-size signature rejected"); 601 if (fd >= 0) 602 close(fd); 603 } 604 gen_loader_fixture_fini(&f); 605 } 606 607 static void signature_bad_keyring(void) 608 { 609 static const __u8 junk[64] = {}; 610 struct gen_loader_fixture f; 611 int fd; 612 613 if (gen_loader_fixture_init(&f) == 0) { 614 /* 615 * A present signature with a keyring_id that resolves to no key is 616 * rejected up front: bpf_prog_verify_signature() fails the keyring 617 * lookup (-EINVAL) before it ever looks at the signature bytes. A 618 * large positive serial takes the user-keyring path and won't exist. 619 */ 620 fd = load_loader(f.gopts.insns, f.gopts.insns_sz, -1, junk, 621 sizeof(junk), INT_MAX, 0); 622 ASSERT_EQ(fd, -EINVAL, "signature with bad keyring_id rejected"); 623 if (fd >= 0) 624 close(fd); 625 } 626 gen_loader_fixture_fini(&f); 627 } 628 629 /* 630 * A signed loader must ignore ctx-supplied map dimensions: the host cannot 631 * resize a signed program's maps via the loader ctx. Drive a one-map program 632 * through gen_loader, ask (via ctx) for every map to be resized to a bogus 633 * value, and confirm the created maps keep their attested size. 634 */ 635 #define GATING_BOGUS_MAX 0x4000 636 637 static void metadata_ctx_max_entries_ignored(void) 638 { 639 LIBBPF_OPTS(gen_loader_opts, gopts, .gen_hash = true); 640 struct test_signed_loader_map *skel; 641 __u8 excl[SHA256_DIGEST_LENGTH]; 642 int nr_maps = 0, nr_progs = 0, i, checked = 0, r; 643 struct bpf_program *p; 644 struct bpf_map *m; 645 struct bpf_map_desc *md; 646 unsigned char *blob; 647 __u32 ctx_sz, data_sz; 648 void *ctx; 649 bool ran; 650 651 skel = test_signed_loader_map__open(); 652 if (!ASSERT_OK_PTR(skel, "skel_open")) 653 return; 654 if (!ASSERT_OK(bpf_object__gen_loader(skel->obj, &gopts), "gen_loader")) 655 goto destroy; 656 if (!ASSERT_OK(bpf_object__load(skel->obj), "gen_load")) 657 goto destroy; 658 659 bpf_object__for_each_program(p, skel->obj) 660 nr_progs++; 661 bpf_object__for_each_map(m, skel->obj) 662 nr_maps++; 663 ctx_sz = sizeof(struct bpf_loader_ctx) + 664 nr_maps * sizeof(struct bpf_map_desc) + 665 nr_progs * sizeof(struct bpf_prog_desc); 666 ctx = calloc(1, ctx_sz); 667 if (!ASSERT_OK_PTR(ctx, "ctx_alloc")) 668 goto destroy; 669 ((struct bpf_loader_ctx *)ctx)->sz = ctx_sz; 670 671 md = (struct bpf_map_desc *)((char *)ctx + sizeof(struct bpf_loader_ctx)); 672 for (i = 0; i < nr_maps; i++) 673 md[i].max_entries = GATING_BOGUS_MAX; 674 675 libbpf_sha256(gopts.insns, gopts.insns_sz, excl); 676 data_sz = gopts.data_sz; 677 blob = malloc(data_sz); 678 if (!ASSERT_OK_PTR(blob, "blob_alloc")) 679 goto free_ctx; 680 memcpy(blob, gopts.data, data_sz); 681 682 r = run_gen_loader(gopts.insns, gopts.insns_sz, blob, data_sz, 683 excl, sizeof(excl), NULL, 0, ctx, ctx_sz, &ran); 684 if (!ASSERT_TRUE(ran, "loader ran") || 685 !ASSERT_EQ(r, 0, "loader retval")) 686 goto free_blob; 687 688 for (i = 0; i < nr_maps; i++) { 689 struct bpf_map_info info; 690 __u32 ilen = sizeof(info); 691 int fd = md[i].map_fd; 692 693 if (fd <= 0) 694 continue; 695 memset(&info, 0, sizeof(info)); 696 if (ASSERT_OK(bpf_map_get_info_by_fd(fd, &info, &ilen), "map_info")) { 697 ASSERT_NEQ(info.max_entries, GATING_BOGUS_MAX, 698 "ctx max_entries ignored for signed loader"); 699 checked++; 700 } 701 } 702 ASSERT_GT(checked, 0, "inspected a created map"); 703 704 free_blob: 705 free(blob); 706 free_ctx: 707 close_loader_ctx_fds(ctx, nr_maps, nr_progs); 708 free(ctx); 709 destroy: 710 test_signed_loader_map__destroy(skel); 711 } 712 713 /* 714 * A signed loader must also ignore ctx-supplied initial_value: the host cannot 715 * re-seed a signed program's map contents through the loader ctx. Drive a 716 * program with one initialized global (a .data map) through gen_loader, point 717 * every map's ctx initial_value at an adversarial buffer, and confirm the 718 * created map still holds the attested value, never the ctx bytes. 719 */ 720 #define DATA_MAGIC 0x5eed1234abad1deaULL 721 722 static void metadata_ctx_initial_value_ignored(void) 723 { 724 LIBBPF_OPTS(gen_loader_opts, gopts, .gen_hash = true); 725 struct test_signed_loader_data *skel; 726 __u8 excl[SHA256_DIGEST_LENGTH], evil[64]; 727 int nr_maps = 0, nr_progs = 0, i, found = 0, r; 728 struct bpf_program *p; 729 struct bpf_map *m; 730 struct bpf_map_desc *md; 731 unsigned char *blob; 732 __u32 ctx_sz, data_sz; 733 void *ctx; 734 bool ran; 735 736 skel = test_signed_loader_data__open(); 737 if (!ASSERT_OK_PTR(skel, "skel_open")) 738 return; 739 if (!ASSERT_OK(bpf_object__gen_loader(skel->obj, &gopts), "gen_loader")) 740 goto destroy; 741 if (!ASSERT_OK(bpf_object__load(skel->obj), "gen_load")) 742 goto destroy; 743 744 bpf_object__for_each_program(p, skel->obj) 745 nr_progs++; 746 bpf_object__for_each_map(m, skel->obj) 747 nr_maps++; 748 ctx_sz = sizeof(struct bpf_loader_ctx) + 749 nr_maps * sizeof(struct bpf_map_desc) + 750 nr_progs * sizeof(struct bpf_prog_desc); 751 ctx = calloc(1, ctx_sz); 752 if (!ASSERT_OK_PTR(ctx, "ctx_alloc")) 753 goto destroy; 754 ((struct bpf_loader_ctx *)ctx)->sz = ctx_sz; 755 756 memset(evil, 0xAA, sizeof(evil)); 757 md = (struct bpf_map_desc *)((char *)ctx + sizeof(struct bpf_loader_ctx)); 758 for (i = 0; i < nr_maps; i++) 759 md[i].initial_value = ptr_to_u64(evil); 760 761 libbpf_sha256(gopts.insns, gopts.insns_sz, excl); 762 data_sz = gopts.data_sz; 763 blob = malloc(data_sz); 764 if (!ASSERT_OK_PTR(blob, "blob_alloc")) 765 goto free_ctx; 766 memcpy(blob, gopts.data, data_sz); 767 768 r = run_gen_loader(gopts.insns, gopts.insns_sz, blob, data_sz, 769 excl, sizeof(excl), NULL, 0, ctx, ctx_sz, &ran); 770 if (!ASSERT_TRUE(ran, "loader ran") || 771 !ASSERT_EQ(r, 0, "loader retval")) 772 goto free_blob; 773 774 for (i = 0; i < nr_maps; i++) { 775 struct bpf_map_info info; 776 __u32 ilen = sizeof(info), key = 0; 777 __u8 value[64] = {}; 778 __u64 got; 779 int fd = md[i].map_fd; 780 781 if (fd <= 0) 782 continue; 783 memset(&info, 0, sizeof(info)); 784 if (!ASSERT_OK(bpf_map_get_info_by_fd(fd, &info, &ilen), "map_info")) 785 continue; 786 if (info.value_size <= sizeof(value) && 787 bpf_map_lookup_elem(fd, &key, value) == 0) { 788 memcpy(&got, value, sizeof(got)); 789 /* attested .data survives; ctx bytes (0xAA..) ignored */ 790 if (got == DATA_MAGIC) 791 found = 1; 792 ASSERT_NEQ(got, 0xAAAAAAAAAAAAAAAAULL, 793 "ctx initial_value ignored for signed loader"); 794 } 795 } 796 ASSERT_EQ(found, 1, "attested .data value preserved"); 797 798 free_blob: 799 free(blob); 800 free_ctx: 801 close_loader_ctx_fds(ctx, nr_maps, nr_progs); 802 free(ctx); 803 destroy: 804 test_signed_loader_data__destroy(skel); 805 } 806 807 /* 808 * The load-time signature must authenticate the loader instructions: a valid 809 * signature loads, and the very same signature over one-byte-tampered insns is 810 * rejected. Uses ./verify_sig_setup.sh + ./sign-file at runtime, like 811 * verify_pkcs7_sig, and verifies against the session keyring the key was added 812 * to. (signature_enforced/_too_large only cover a malformed signature.) 813 */ 814 static void signature_authenticates_insns(void) 815 { 816 LIBBPF_OPTS(gen_loader_opts, gopts, .gen_hash = true); 817 char dir_tmpl[] = "/tmp/signed_loaderXXXXXX", *dir; 818 struct test_signed_loader *skel = NULL; 819 __u8 excl[SHA256_DIGEST_LENGTH], sig[8192]; 820 __u32 sig_sz = sizeof(sig), insns_sz, data_sz, ctx_sz; 821 unsigned char *insns = NULL, *tampered = NULL, *blob = NULL; 822 unsigned char *signbuf = NULL; 823 int nr_maps = 0, nr_progs = 0, r; 824 struct bpf_program *p; 825 struct bpf_map *m; 826 void *ctx = NULL; 827 bool ran; 828 829 syscall(__NR_request_key, "keyring", "_uid.0", NULL, 830 KEY_SPEC_SESSION_KEYRING); 831 dir = mkdtemp(dir_tmpl); 832 if (!ASSERT_OK_PTR(dir, "mkdtemp")) 833 return; 834 if (!ASSERT_OK(run_setup("setup", dir), "verify_sig_setup")) { 835 rmdir(dir); 836 return; 837 } 838 839 skel = test_signed_loader__open(); 840 if (!ASSERT_OK_PTR(skel, "skel_open")) 841 goto cleanup; 842 if (!ASSERT_OK(bpf_object__gen_loader(skel->obj, &gopts), "gen_loader")) 843 goto cleanup; 844 if (!ASSERT_OK(bpf_object__load(skel->obj), "gen_load")) 845 goto cleanup; 846 847 bpf_object__for_each_program(p, skel->obj) 848 nr_progs++; 849 bpf_object__for_each_map(m, skel->obj) 850 nr_maps++; 851 ctx_sz = sizeof(struct bpf_loader_ctx) + 852 nr_maps * sizeof(struct bpf_map_desc) + 853 nr_progs * sizeof(struct bpf_prog_desc); 854 insns_sz = gopts.insns_sz; 855 data_sz = gopts.data_sz; 856 ctx = calloc(1, ctx_sz); 857 insns = malloc(insns_sz); 858 tampered = malloc(insns_sz); 859 blob = malloc(data_sz); 860 if (!ASSERT_OK_PTR(ctx, "ctx") || 861 !ASSERT_OK_PTR(insns, "insns") || 862 !ASSERT_OK_PTR(tampered, "tampered") || 863 !ASSERT_OK_PTR(blob, "blob")) 864 goto cleanup; 865 memcpy(insns, gopts.insns, insns_sz); 866 memcpy(blob, gopts.data, data_sz); 867 libbpf_sha256(insns, insns_sz, excl); 868 869 signbuf = malloc((size_t)insns_sz + data_sz); 870 if (!ASSERT_OK_PTR(signbuf, "signbuf")) 871 goto cleanup; 872 memcpy(signbuf, insns, insns_sz); 873 memcpy(signbuf + insns_sz, blob, data_sz); 874 if (!ASSERT_OK(sign_buf(dir, signbuf, insns_sz + data_sz, sig, &sig_sz), 875 "sign-file")) 876 goto cleanup; 877 878 memset(ctx, 0, ctx_sz); 879 ((struct bpf_loader_ctx *)ctx)->sz = ctx_sz; 880 r = run_gen_loader(insns, insns_sz, blob, data_sz, excl, sizeof(excl), 881 sig, sig_sz, ctx, ctx_sz, &ran); 882 ASSERT_TRUE(ran, "valid signature: loader loaded and ran"); 883 ASSERT_EQ(r, 0, "valid signature accepted"); 884 close_loader_ctx_fds(ctx, nr_maps, nr_progs); 885 886 memcpy(tampered, insns, insns_sz); 887 tampered[insns_sz / 2] ^= 0xff; 888 /* 889 * Bind the metadata map to the tampered loader's own digest, so the 890 * verifier's exclusive-map check (excl_prog_sha == prog->digest) passes 891 * and the signature - verified after the maps are resolved - is what 892 * rejects the load. This is the attacker's best case: even after 893 * re-binding the exclusive map to their tampered loader, the signature 894 * over the original insns || metadata still fails. (Leaving the map 895 * bound to the original digest would instead trip the excl check first.) 896 */ 897 libbpf_sha256(tampered, insns_sz, excl); 898 memset(ctx, 0, ctx_sz); 899 ((struct bpf_loader_ctx *)ctx)->sz = ctx_sz; 900 r = run_gen_loader(tampered, insns_sz, blob, data_sz, excl, sizeof(excl), 901 sig, sig_sz, ctx, ctx_sz, &ran); 902 ASSERT_FALSE(ran, "tampered loader rejected before run"); 903 ASSERT_EQ(r, -EKEYREJECTED, "signature is bound to the instructions"); 904 cleanup: 905 free(insns); 906 free(tampered); 907 free(blob); 908 free(signbuf); 909 free(ctx); 910 test_signed_loader__destroy(skel); 911 run_setup("cleanup", dir); 912 } 913 914 static void signature_authenticates_metadata(void) 915 { 916 LIBBPF_OPTS(gen_loader_opts, gopts, .gen_hash = true); 917 char dir_tmpl[] = "/tmp/signed_loaderXXXXXX", *dir; 918 struct test_signed_loader *skel = NULL; 919 __u8 excl[SHA256_DIGEST_LENGTH], sig[8192]; 920 __u32 sig_sz = sizeof(sig), insns_sz, data_sz, ctx_sz; 921 unsigned char *insns = NULL, *blob = NULL; 922 unsigned char *signbuf = NULL; 923 int nr_maps = 0, nr_progs = 0, r; 924 struct bpf_program *p; 925 struct bpf_map *m; 926 void *ctx = NULL; 927 bool ran; 928 929 syscall(__NR_request_key, "keyring", "_uid.0", NULL, 930 KEY_SPEC_SESSION_KEYRING); 931 dir = mkdtemp(dir_tmpl); 932 if (!ASSERT_OK_PTR(dir, "mkdtemp")) 933 return; 934 if (!ASSERT_OK(run_setup("setup", dir), "verify_sig_setup")) { 935 rmdir(dir); 936 return; 937 } 938 939 skel = test_signed_loader__open(); 940 if (!ASSERT_OK_PTR(skel, "skel_open")) 941 goto cleanup; 942 if (!ASSERT_OK(bpf_object__gen_loader(skel->obj, &gopts), "gen_loader")) 943 goto cleanup; 944 if (!ASSERT_OK(bpf_object__load(skel->obj), "gen_load")) 945 goto cleanup; 946 947 bpf_object__for_each_program(p, skel->obj) 948 nr_progs++; 949 bpf_object__for_each_map(m, skel->obj) 950 nr_maps++; 951 ctx_sz = sizeof(struct bpf_loader_ctx) + 952 nr_maps * sizeof(struct bpf_map_desc) + 953 nr_progs * sizeof(struct bpf_prog_desc); 954 insns_sz = gopts.insns_sz; 955 data_sz = gopts.data_sz; 956 ctx = calloc(1, ctx_sz); 957 insns = malloc(insns_sz); 958 blob = malloc(data_sz); 959 if (!ASSERT_OK_PTR(ctx, "ctx") || 960 !ASSERT_OK_PTR(insns, "insns") || 961 !ASSERT_OK_PTR(blob, "blob")) 962 goto cleanup; 963 memcpy(insns, gopts.insns, insns_sz); 964 memcpy(blob, gopts.data, data_sz); 965 libbpf_sha256(insns, insns_sz, excl); 966 967 signbuf = malloc((size_t)insns_sz + data_sz); 968 if (!ASSERT_OK_PTR(signbuf, "signbuf")) 969 goto cleanup; 970 memcpy(signbuf, insns, insns_sz); 971 memcpy(signbuf + insns_sz, blob, data_sz); 972 if (!ASSERT_OK(sign_buf(dir, signbuf, insns_sz + data_sz, sig, &sig_sz), 973 "sign-file")) 974 goto cleanup; 975 976 memset(ctx, 0, ctx_sz); 977 ((struct bpf_loader_ctx *)ctx)->sz = ctx_sz; 978 r = run_gen_loader(insns, insns_sz, blob, data_sz, excl, sizeof(excl), 979 sig, sig_sz, ctx, ctx_sz, &ran); 980 ASSERT_TRUE(ran, "valid signature: loader loaded and ran"); 981 ASSERT_EQ(r, 0, "valid signature accepted"); 982 close_loader_ctx_fds(ctx, nr_maps, nr_progs); 983 984 /* 985 * Tamper the metadata after signing while leaving the instructions 986 * and thus the exclusive hash binding untouched: the map freezes 987 * fine and excl_prog_sha still matches the loader's digest, so the 988 * load reaches signature verification, which folds the live frozen 989 * map bytes into the checked payload and must reject the modified 990 * blob. A kernel folding anything but the map contents themselves 991 * would wrongly accept this load. 992 */ 993 blob[data_sz / 2] ^= 0xff; 994 memset(ctx, 0, ctx_sz); 995 ((struct bpf_loader_ctx *)ctx)->sz = ctx_sz; 996 r = run_gen_loader(insns, insns_sz, blob, data_sz, excl, sizeof(excl), 997 sig, sig_sz, ctx, ctx_sz, &ran); 998 ASSERT_FALSE(ran, "tampered metadata rejected before run"); 999 ASSERT_EQ(r, -EKEYREJECTED, "signature is bound to the metadata"); 1000 cleanup: 1001 free(insns); 1002 free(blob); 1003 free(signbuf); 1004 free(ctx); 1005 test_signed_loader__destroy(skel); 1006 run_setup("cleanup", dir); 1007 } 1008 1009 static int make_excl_map(__u32 flags, __u32 value_size) 1010 { 1011 LIBBPF_OPTS(bpf_map_create_opts, opts); 1012 __u8 hash[SHA256_DIGEST_LENGTH] = { 1 }; /* any 32-byte value */ 1013 1014 opts.excl_prog_hash = hash; 1015 opts.excl_prog_hash_size = sizeof(hash); 1016 opts.map_flags = flags; 1017 return bpf_map_create(BPF_MAP_TYPE_ARRAY, "md", 4, value_size, 1, &opts); 1018 } 1019 1020 static void hash_requires_frozen(void) 1021 { 1022 __u8 hbuf[SHA256_DIGEST_LENGTH], val[64] = {}; 1023 struct bpf_map_info info; 1024 __u32 ilen, key = 0; 1025 int fd; 1026 1027 fd = make_excl_map(0, sizeof(val)); 1028 if (!ASSERT_OK_FD(fd, "excl_map")) 1029 return; 1030 ASSERT_OK(bpf_map_update_elem(fd, &key, val, 0), "update"); 1031 1032 memset(&info, 0, sizeof(info)); 1033 info.hash = ptr_to_u64(hbuf); 1034 info.hash_size = sizeof(hbuf); 1035 ilen = sizeof(info); 1036 ASSERT_EQ(bpf_map_get_info_by_fd(fd, &info, &ilen), -EPERM, 1037 "hash of unfrozen map rejected"); 1038 close(fd); 1039 } 1040 1041 static void no_update_after_freeze(void) 1042 { 1043 __u8 val[64] = {}; 1044 __u32 key = 0; 1045 int fd; 1046 1047 fd = make_excl_map(0, sizeof(val)); 1048 if (!ASSERT_OK_FD(fd, "excl_map")) 1049 return; 1050 ASSERT_OK(bpf_map_update_elem(fd, &key, val, 0), "update"); 1051 ASSERT_OK(bpf_map_freeze(fd), "freeze"); 1052 ASSERT_EQ(bpf_map_update_elem(fd, &key, val, 0), -EPERM, 1053 "update after freeze rejected"); 1054 close(fd); 1055 } 1056 1057 static void freeze_writable_mmap(void) 1058 { 1059 void *w; 1060 int fd; 1061 1062 fd = make_excl_map(BPF_F_MMAPABLE, 4096); 1063 if (!ASSERT_OK_FD(fd, "excl_mmapable_map")) 1064 return; 1065 w = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); 1066 if (ASSERT_OK_PTR(w, "writable_mmap")) { 1067 ASSERT_EQ(bpf_map_freeze(fd), -EBUSY, 1068 "freeze rejected while writable mmap held"); 1069 munmap(w, 4096); 1070 } 1071 close(fd); 1072 } 1073 1074 static void no_writable_mmap_frozen(void) 1075 { 1076 void *w; 1077 int fd; 1078 1079 fd = make_excl_map(BPF_F_MMAPABLE, 4096); 1080 if (!ASSERT_OK_FD(fd, "excl_mmapable_map")) 1081 return; 1082 ASSERT_OK(bpf_map_freeze(fd), "freeze"); 1083 w = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); 1084 ASSERT_EQ(w, MAP_FAILED, "writable mmap of frozen map rejected"); 1085 if (w != MAP_FAILED) 1086 munmap(w, 4096); 1087 close(fd); 1088 } 1089 1090 static void map_hash_matches_libbpf(void) 1091 { 1092 __u8 kbuf[SHA256_DIGEST_LENGTH], lbuf[SHA256_DIGEST_LENGTH], val[64] = {}; 1093 struct bpf_map_info info; 1094 __u32 ilen, key = 0; 1095 int fd, i; 1096 1097 /* 1098 * The signing scheme assumes the kernel's map hash equals what libbpf 1099 * computes over the same bytes (gen_loader bakes libbpf_sha256(blob); 1100 * the kernel recomputes via array_map_get_hash). Pin that they agree. 1101 */ 1102 for (i = 0; i < (int)sizeof(val); i++) 1103 val[i] = i * 7 + 1; 1104 fd = bpf_map_create(BPF_MAP_TYPE_ARRAY, "h", 4, sizeof(val), 1, NULL); 1105 if (!ASSERT_OK_FD(fd, "array_map")) 1106 return; 1107 ASSERT_OK(bpf_map_update_elem(fd, &key, val, 0), "update"); 1108 ASSERT_OK(bpf_map_freeze(fd), "freeze"); 1109 memset(&info, 0, sizeof(info)); 1110 info.hash = ptr_to_u64(kbuf); 1111 info.hash_size = sizeof(kbuf); 1112 ilen = sizeof(info); 1113 if (ASSERT_OK(bpf_map_get_info_by_fd(fd, &info, &ilen), "get_hash")) { 1114 libbpf_sha256(val, sizeof(val), lbuf); 1115 ASSERT_EQ(memcmp(kbuf, lbuf, sizeof(kbuf)), 0, 1116 "kernel map hash matches libbpf_sha256"); 1117 } 1118 close(fd); 1119 } 1120 1121 static void map_hash_multi_element(void) 1122 { 1123 const __u32 nr = 8, value_size = 64; 1124 __u8 kbuf[SHA256_DIGEST_LENGTH], lbuf[SHA256_DIGEST_LENGTH]; 1125 struct bpf_map_info info; 1126 __u32 ilen, i, j; 1127 __u8 *full; 1128 int fd; 1129 1130 /* 1131 * array_map_get_hash() hashes elem_size * max_entries (the whole value 1132 * area), not just element 0. With an 8-aligned value_size elem_size has 1133 * no padding, so pin that a >1-entry array's kernel hash equals 1134 * libbpf_sha256() over the full, concatenated element contents. 1135 */ 1136 fd = bpf_map_create(BPF_MAP_TYPE_ARRAY, "h", 4, value_size, nr, NULL); 1137 if (!ASSERT_OK_FD(fd, "array_map")) 1138 return; 1139 full = calloc(nr, value_size); 1140 if (!ASSERT_OK_PTR(full, "buf")) 1141 goto close_fd; 1142 for (i = 0; i < nr; i++) { 1143 __u8 *v = full + i * value_size; 1144 1145 for (j = 0; j < value_size; j++) 1146 v[j] = i * 31 + j * 7 + 1; 1147 ASSERT_OK(bpf_map_update_elem(fd, &i, v, 0), "update"); 1148 } 1149 ASSERT_OK(bpf_map_freeze(fd), "freeze"); 1150 memset(&info, 0, sizeof(info)); 1151 info.hash = ptr_to_u64(kbuf); 1152 info.hash_size = sizeof(kbuf); 1153 ilen = sizeof(info); 1154 if (ASSERT_OK(bpf_map_get_info_by_fd(fd, &info, &ilen), "get_hash")) { 1155 libbpf_sha256(full, (size_t)nr * value_size, lbuf); 1156 ASSERT_EQ(memcmp(kbuf, lbuf, sizeof(kbuf)), 0, 1157 "kernel hash covers full multi-element value area"); 1158 } 1159 free(full); 1160 close_fd: 1161 close(fd); 1162 } 1163 1164 static void map_hash_bad_size(void) 1165 { 1166 __u8 kbuf[SHA256_DIGEST_LENGTH], val[64] = {}; 1167 struct bpf_map_info info; 1168 __u32 ilen, key = 0; 1169 int fd; 1170 1171 fd = bpf_map_create(BPF_MAP_TYPE_ARRAY, "h", 4, sizeof(val), 1, NULL); 1172 if (!ASSERT_OK_FD(fd, "array_map")) 1173 return; 1174 ASSERT_OK(bpf_map_update_elem(fd, &key, val, 0), "update"); 1175 ASSERT_OK(bpf_map_freeze(fd), "freeze"); 1176 memset(&info, 0, sizeof(info)); 1177 info.hash = ptr_to_u64(kbuf); 1178 info.hash_size = sizeof(kbuf) / 2; 1179 ilen = sizeof(info); 1180 ASSERT_EQ(bpf_map_get_info_by_fd(fd, &info, &ilen), -EINVAL, 1181 "wrong hash_size rejected"); 1182 close(fd); 1183 } 1184 1185 static void map_hash_unsupported_type(void) 1186 { 1187 __u8 kbuf[SHA256_DIGEST_LENGTH]; 1188 struct bpf_map_info info; 1189 __u32 ilen; 1190 int fd; 1191 1192 /* Only arrays implement map_get_hash; a hash map must be refused. */ 1193 fd = bpf_map_create(BPF_MAP_TYPE_HASH, "h", 4, 8, 4, NULL); 1194 if (!ASSERT_OK_FD(fd, "hash_map")) 1195 return; 1196 memset(&info, 0, sizeof(info)); 1197 info.hash = ptr_to_u64(kbuf); 1198 info.hash_size = sizeof(kbuf); 1199 ilen = sizeof(info); 1200 ASSERT_EQ(bpf_map_get_info_by_fd(fd, &info, &ilen), -EINVAL, 1201 "hash unsupported for non-array map"); 1202 close(fd); 1203 } 1204 1205 static int setup_meta_map(const struct gen_loader_fixture *f) 1206 { 1207 LIBBPF_OPTS(bpf_map_create_opts, mopts, 1208 .excl_prog_hash = f->excl, 1209 .excl_prog_hash_size = sizeof(f->excl)); 1210 __u32 key = 0; 1211 int fd; 1212 1213 fd = bpf_map_create(BPF_MAP_TYPE_ARRAY, "__loader.map", 4, 1214 f->data_sz, 1, &mopts); 1215 if (fd < 0) 1216 return -errno; 1217 if (bpf_map_update_elem(fd, &key, f->blob, 0) || bpf_map_freeze(fd)) { 1218 close(fd); 1219 return -errno; 1220 } 1221 return fd; 1222 } 1223 1224 static void lsm_signature_verdict(void) 1225 { 1226 char dir_tmpl[] = "/tmp/signed_loader_lsmXXXXXX", *dir = NULL; 1227 struct test_signed_loader_lsm *lsm = NULL; 1228 __u32 sig_sz = 8192, msig_sz = 8192; 1229 int map_fd = -1, prog_fd = -1; 1230 bool have_fixture = false; 1231 struct gen_loader_fixture f; 1232 unsigned char *buf; 1233 __s32 ses_serial; 1234 __u8 sig[8192]; 1235 1236 lsm = test_signed_loader_lsm__open_and_load(); 1237 if (!ASSERT_OK_PTR(lsm, "lsm_skel_load")) 1238 return; 1239 lsm->bss->monitored_tid = sys_gettid(); 1240 if (!ASSERT_OK(test_signed_loader_lsm__attach(lsm), "lsm_attach")) 1241 goto out; 1242 1243 have_fixture = true; 1244 if (gen_loader_fixture_init(&f) != 0) 1245 goto out; 1246 1247 map_fd = setup_meta_map(&f); 1248 if (!ASSERT_OK_FD(map_fd, "meta_map_unsigned")) 1249 goto out; 1250 lsm->bss->seen = 0; 1251 prog_fd = load_loader(f.gopts.insns, f.gopts.insns_sz, map_fd, NULL, 0, 0, 0); 1252 close(map_fd); 1253 map_fd = -1; 1254 if (!ASSERT_OK_FD(prog_fd, "unsigned loader load")) 1255 goto out; 1256 close(prog_fd); 1257 prog_fd = -1; 1258 if (!ASSERT_NEQ(lsm->bss->seen, 0, "bpf LSM in the active LSM set")) 1259 goto out; 1260 ASSERT_EQ(lsm->bss->seen, 1, "unsigned: one observed load"); 1261 ASSERT_EQ(lsm->bss->sig_verdict, BPF_SIG_UNSIGNED, "unsigned verdict"); 1262 ASSERT_EQ(lsm->bss->sig_keyring_type, BPF_SIG_KEYRING_NONE, "unsigned keyring type"); 1263 ASSERT_EQ(lsm->bss->sig_keyring_serial, 0, "unsigned: no keyring serial"); 1264 1265 syscall(__NR_request_key, "keyring", "_uid.0", NULL, 1266 KEY_SPEC_SESSION_KEYRING); 1267 dir = mkdtemp(dir_tmpl); 1268 if (!ASSERT_OK_PTR(dir, "mkdtemp")) 1269 goto out; 1270 if (!ASSERT_OK(run_setup("setup", dir), "verify_sig_setup")) { 1271 rmdir(dir); 1272 dir = NULL; 1273 goto out; 1274 } 1275 if (!ASSERT_OK(sign_buf(dir, f.gopts.insns, f.gopts.insns_sz, sig, 1276 &sig_sz), "sign-file")) 1277 goto out; 1278 1279 map_fd = setup_meta_map(&f); 1280 if (!ASSERT_OK_FD(map_fd, "meta_map_signed")) 1281 goto out; 1282 lsm->bss->seen = 0; 1283 prog_fd = load_loader(f.gopts.insns, f.gopts.insns_sz, map_fd, sig, 1284 sig_sz, KEY_SPEC_SESSION_KEYRING, 0); 1285 close(map_fd); 1286 map_fd = -1; 1287 ASSERT_EQ(prog_fd, -EACCES, "unfolded metadata rejected"); 1288 if (prog_fd >= 0) 1289 close(prog_fd); 1290 prog_fd = -1; 1291 1292 ses_serial = syscall(__NR_keyctl, KEYCTL_GET_KEYRING_ID, 1293 KEY_SPEC_SESSION_KEYRING, 0); 1294 ASSERT_EQ(lsm->bss->seen, 1, "signed: one observed load"); 1295 ASSERT_EQ(lsm->bss->sig_verdict, BPF_SIG_VERIFIED, 1296 "admission saw a valid signature"); 1297 ASSERT_EQ(lsm->bss->sig_keyring_type, BPF_SIG_KEYRING_USER, "signed keyring type"); 1298 ASSERT_GT(ses_serial, 0, "session keyring serial resolved"); 1299 ASSERT_EQ(lsm->bss->sig_keyring_serial, ses_serial, 1300 "signed: validated against session keyring"); 1301 1302 buf = malloc((size_t)f.gopts.insns_sz + f.data_sz); 1303 if (!ASSERT_OK_PTR(buf, "meta_signbuf")) 1304 goto out; 1305 memcpy(buf, f.gopts.insns, f.gopts.insns_sz); 1306 memcpy(buf + f.gopts.insns_sz, f.blob, f.data_sz); 1307 if (!ASSERT_OK(sign_buf(dir, buf, f.gopts.insns_sz + f.data_sz, 1308 sig, &msig_sz), "sign insns||metadata")) { 1309 free(buf); 1310 goto out; 1311 } 1312 free(buf); 1313 1314 map_fd = setup_meta_map(&f); 1315 if (!ASSERT_OK_FD(map_fd, "meta_map_bound")) 1316 goto out; 1317 lsm->bss->seen = 0; 1318 prog_fd = load_loader(f.gopts.insns, f.gopts.insns_sz, map_fd, sig, 1319 msig_sz, KEY_SPEC_SESSION_KEYRING, 1); 1320 close(map_fd); 1321 map_fd = -1; 1322 if (!ASSERT_OK_FD(prog_fd, "metadata-bound loader load")) 1323 goto out; 1324 close(prog_fd); 1325 prog_fd = -1; 1326 ASSERT_EQ(lsm->bss->seen, 1, "metadata: one observed load"); 1327 ASSERT_EQ(lsm->bss->sig_verdict, BPF_SIG_VERIFIED, 1328 "metadata-bound verdict"); 1329 out: 1330 if (map_fd >= 0) 1331 close(map_fd); 1332 if (prog_fd >= 0) 1333 close(prog_fd); 1334 if (have_fixture) 1335 gen_loader_fixture_fini(&f); 1336 if (dir) 1337 run_setup("cleanup", dir); 1338 test_signed_loader_lsm__destroy(lsm); 1339 } 1340 1341 /* 1342 * Load-time metadata verification: the kernel folds the frozen metadata map 1343 * into the signature (insns || metadata) and checks it at BPF_PROG_LOAD via 1344 * fd_array_cnt, rather than the loader checking from within BPF. Sign that 1345 * concatenation, hand the kernel the map, and confirm the signed loader loads, 1346 * runs, and installs its target. 1347 */ 1348 static int loadtime_drive(const char *dir, const void *insns, __u32 insns_sz, 1349 const void *data, __u32 data_sz, const __u8 *excl, 1350 void *ctx, __u32 ctx_sz, int *load_ret, bool *ran) 1351 { 1352 LIBBPF_OPTS(bpf_map_create_opts, mopts, 1353 .excl_prog_hash = excl, 1354 .excl_prog_hash_size = SHA256_DIGEST_LENGTH); 1355 __u32 sig_sz = 8192, key = 0; 1356 unsigned char *buf = NULL; 1357 int map_fd, prog_fd, ret = 0; 1358 union bpf_attr attr; 1359 __u8 sig[8192]; 1360 1361 *ran = false; 1362 *load_ret = 0; 1363 1364 /* 1365 * Metadata map, bound to the loader digest and frozen, exactly as 1366 * skel_internal.h's bpf_load_and_run() sets it up. 1367 */ 1368 map_fd = bpf_map_create(BPF_MAP_TYPE_ARRAY, "__loader.map", 4, 1369 data_sz, 1, &mopts); 1370 if (map_fd < 0) { 1371 ret = -errno; 1372 goto out_load; 1373 } 1374 if (bpf_map_update_elem(map_fd, &key, data, 0) || bpf_map_freeze(map_fd)) { 1375 ret = -errno; 1376 goto out_load; 1377 } 1378 1379 /* Sign insns || metadata, the same bytes the kernel reconstructs. */ 1380 buf = malloc((size_t)insns_sz + data_sz); 1381 if (!buf) { 1382 ret = -ENOMEM; 1383 goto out_load; 1384 } 1385 memcpy(buf, insns, insns_sz); 1386 memcpy(buf + insns_sz, data, data_sz); 1387 ret = sign_buf(dir, buf, insns_sz + data_sz, sig, &sig_sz); 1388 if (ret) 1389 goto out_load; 1390 1391 memset(&attr, 0, sizeof(attr)); 1392 attr.prog_type = BPF_PROG_TYPE_SYSCALL; 1393 attr.insns = ptr_to_u64(insns); 1394 attr.insn_cnt = insns_sz / sizeof(struct bpf_insn); 1395 attr.license = ptr_to_u64("Dual BSD/GPL"); 1396 attr.prog_flags = BPF_F_SLEEPABLE; 1397 attr.fd_array = ptr_to_u64(&map_fd); 1398 attr.signature = ptr_to_u64(sig); 1399 attr.signature_size = sig_sz; 1400 attr.keyring_id = KEY_SPEC_SESSION_KEYRING; 1401 attr.fd_array_cnt = 1; 1402 memcpy(attr.prog_name, "__loader.prog", sizeof("__loader.prog")); 1403 prog_fd = syscall(__NR_bpf, BPF_PROG_LOAD, &attr, 1404 offsetofend(union bpf_attr, keyring_id)); 1405 if (prog_fd < 0) { 1406 ret = -errno; 1407 goto out_load; 1408 } 1409 1410 memset(&attr, 0, sizeof(attr)); 1411 attr.test.prog_fd = prog_fd; 1412 attr.test.ctx_in = ptr_to_u64(ctx); 1413 attr.test.ctx_size_in = ctx_sz; 1414 if (syscall(__NR_bpf, BPF_PROG_RUN, &attr, 1415 offsetofend(union bpf_attr, test)) < 0) { 1416 ret = -errno; 1417 goto out_prog; 1418 } 1419 *ran = true; 1420 ret = (int)attr.test.retval; 1421 out_prog: 1422 close(prog_fd); 1423 goto out_map; 1424 out_load: 1425 *load_ret = ret; 1426 out_map: 1427 free(buf); 1428 if (map_fd >= 0) 1429 close(map_fd); 1430 return ret; 1431 } 1432 1433 static void loadtime_verify(struct bpf_object *obj, int expect_maps) 1434 { 1435 LIBBPF_OPTS(gen_loader_opts, gopts, .gen_hash = true); 1436 char dir_tmpl[] = "/tmp/signed_loader_ltXXXXXX", *dir = NULL; 1437 int nr_maps = 0, nr_progs = 0, load_ret = 0, r; 1438 __u8 excl[SHA256_DIGEST_LENGTH]; 1439 struct bpf_prog_desc *pd; 1440 struct bpf_map_desc *md; 1441 unsigned char *blob = NULL; 1442 struct bpf_program *p; 1443 struct bpf_map *m; 1444 __u32 ctx_sz, data_sz; 1445 void *ctx = NULL; 1446 bool ran = false; 1447 1448 syscall(__NR_request_key, "keyring", "_uid.0", NULL, 1449 KEY_SPEC_SESSION_KEYRING); 1450 dir = mkdtemp(dir_tmpl); 1451 if (!ASSERT_OK_PTR(dir, "mkdtemp")) 1452 return; 1453 if (!ASSERT_OK(run_setup("setup", dir), "verify_sig_setup")) { 1454 rmdir(dir); 1455 return; 1456 } 1457 1458 if (!ASSERT_OK(bpf_object__gen_loader(obj, &gopts), "gen_loader")) 1459 goto out; 1460 if (!ASSERT_OK(bpf_object__load(obj), "gen_load")) 1461 goto out; 1462 1463 bpf_object__for_each_program(p, obj) 1464 nr_progs++; 1465 bpf_object__for_each_map(m, obj) 1466 nr_maps++; 1467 if (!ASSERT_EQ(nr_maps, expect_maps, "fixture map count")) 1468 goto out; 1469 1470 ctx_sz = sizeof(struct bpf_loader_ctx) + 1471 nr_maps * sizeof(struct bpf_map_desc) + 1472 nr_progs * sizeof(struct bpf_prog_desc); 1473 ctx = calloc(1, ctx_sz); 1474 if (!ASSERT_OK_PTR(ctx, "ctx_alloc")) 1475 goto out; 1476 ((struct bpf_loader_ctx *)ctx)->sz = ctx_sz; 1477 1478 data_sz = gopts.data_sz; 1479 blob = malloc(data_sz); 1480 if (!ASSERT_OK_PTR(blob, "blob_alloc")) 1481 goto out; 1482 memcpy(blob, gopts.data, data_sz); 1483 1484 /* excl_prog_hash = SHA256(loader insns) == the loader's prog->digest. */ 1485 libbpf_sha256(gopts.insns, gopts.insns_sz, excl); 1486 1487 r = loadtime_drive(dir, gopts.insns, gopts.insns_sz, blob, data_sz, 1488 excl, ctx, ctx_sz, &load_ret, &ran); 1489 ASSERT_OK(load_ret, "signed loader loaded (insns || metadata)"); 1490 ASSERT_TRUE(ran, "loader ran"); 1491 ASSERT_EQ(r, 0, "loader installed its target"); 1492 1493 md = (struct bpf_map_desc *)((char *)ctx + sizeof(struct bpf_loader_ctx)); 1494 pd = (struct bpf_prog_desc *)(md + nr_maps); 1495 ASSERT_GT(pd[0].prog_fd, 0, "target program installed"); 1496 if (nr_maps) 1497 ASSERT_GT(md[0].map_fd, 0, "target map installed"); 1498 1499 close_loader_ctx_fds(ctx, nr_maps, nr_progs); 1500 out: 1501 free(blob); 1502 free(ctx); 1503 if (dir) 1504 run_setup("cleanup", dir); 1505 } 1506 1507 static void loadtime_no_map(void) 1508 { 1509 struct test_signed_loader *skel = test_signed_loader__open(); 1510 1511 if (!ASSERT_OK_PTR(skel, "skel_open")) 1512 return; 1513 loadtime_verify(skel->obj, 0); 1514 test_signed_loader__destroy(skel); 1515 } 1516 1517 static void loadtime_with_map(void) 1518 { 1519 struct test_signed_loader_map *skel = test_signed_loader_map__open(); 1520 1521 if (!ASSERT_OK_PTR(skel, "skel_open")) 1522 return; 1523 loadtime_verify(skel->obj, 1); 1524 test_signed_loader_map__destroy(skel); 1525 } 1526 1527 /* 1528 * A signed program need not bind any map. A plain BPF_PROG_TYPE_SYSCALL 1529 * program with no fd_array is signed over its instructions alone: the kernel 1530 * verifies the signature, folds no metadata, and the program loads. Exercise 1531 * the fd_array == NULL / fd_array_cnt == 0 path, and confirm the signature 1532 * still authenticates the instructions (a tampered copy is rejected). 1533 */ 1534 static void signed_no_fd_array(void) 1535 { 1536 struct bpf_insn insns[] = { 1537 BPF_MOV64_IMM(BPF_REG_0, 0), 1538 BPF_EXIT_INSN(), 1539 }; 1540 char dir_tmpl[] = "/tmp/signed_loaderXXXXXX", *dir; 1541 __u32 sig_sz = 8192; 1542 union bpf_attr attr; 1543 __u8 sig[8192]; 1544 int prog_fd, err; 1545 1546 syscall(__NR_request_key, "keyring", "_uid.0", NULL, 1547 KEY_SPEC_SESSION_KEYRING); 1548 dir = mkdtemp(dir_tmpl); 1549 if (!ASSERT_OK_PTR(dir, "mkdtemp")) 1550 return; 1551 if (!ASSERT_OK(run_setup("setup", dir), "verify_sig_setup")) { 1552 rmdir(dir); 1553 return; 1554 } 1555 1556 /* No metadata map: the signed payload is the instructions alone. */ 1557 if (!ASSERT_OK(sign_buf(dir, insns, sizeof(insns), sig, &sig_sz), 1558 "sign-file")) 1559 goto cleanup; 1560 1561 memset(&attr, 0, sizeof(attr)); 1562 attr.prog_type = BPF_PROG_TYPE_SYSCALL; 1563 attr.insns = ptr_to_u64(insns); 1564 attr.insn_cnt = ARRAY_SIZE(insns); 1565 attr.license = ptr_to_u64("Dual BSD/GPL"); 1566 attr.prog_flags = BPF_F_SLEEPABLE; 1567 attr.signature = ptr_to_u64(sig); 1568 attr.signature_size = sig_sz; 1569 attr.keyring_id = KEY_SPEC_SESSION_KEYRING; 1570 /* fd_array and fd_array_cnt deliberately left NULL/0. */ 1571 memcpy(attr.prog_name, "signed_nomap", sizeof("signed_nomap")); 1572 1573 prog_fd = syscall(__NR_bpf, BPF_PROG_LOAD, &attr, 1574 offsetofend(union bpf_attr, keyring_id)); 1575 if (!ASSERT_GE(prog_fd, 0, "map-less signed program loaded")) { 1576 if (prog_fd >= 0) 1577 close(prog_fd); 1578 goto cleanup; 1579 } 1580 close(prog_fd); 1581 1582 /* The signature covers the instructions, so tampering must be rejected. */ 1583 insns[0].imm = 1; 1584 prog_fd = syscall(__NR_bpf, BPF_PROG_LOAD, &attr, 1585 offsetofend(union bpf_attr, keyring_id)); 1586 err = prog_fd < 0 ? -errno : prog_fd; 1587 ASSERT_EQ(err, -EKEYREJECTED, "tampered map-less program rejected"); 1588 if (prog_fd >= 0) 1589 close(prog_fd); 1590 cleanup: 1591 run_setup("cleanup", dir); 1592 } 1593 1594 /* 1595 * A signed program may reach maps only through fd_array indices, so the kernel 1596 * folds (and thus attests) them. A direct BPF_PSEUDO_MAP_FD reference - a raw, 1597 * unfolded fd baked into the signed instructions - is rejected by the verifier. 1598 */ 1599 static void signed_map_by_fd_rejected(void) 1600 { 1601 struct bpf_insn insns[] = { 1602 BPF_LD_MAP_FD(BPF_REG_1, 0), 1603 BPF_MOV64_IMM(BPF_REG_0, 0), 1604 BPF_EXIT_INSN(), 1605 }; 1606 char dir_tmpl[] = "/tmp/signed_loaderXXXXXX", *dir; 1607 __u32 sig_sz = 8192; 1608 union bpf_attr attr; 1609 __u8 sig[8192]; 1610 int map_fd, prog_fd, err; 1611 1612 map_fd = bpf_map_create(BPF_MAP_TYPE_ARRAY, "sig_mapfd", 4, 4, 1, NULL); 1613 if (!ASSERT_GE(map_fd, 0, "map_create")) 1614 return; 1615 insns[0].imm = map_fd; /* bake the raw map fd into the ld_imm64 */ 1616 1617 syscall(__NR_request_key, "keyring", "_uid.0", NULL, 1618 KEY_SPEC_SESSION_KEYRING); 1619 dir = mkdtemp(dir_tmpl); 1620 if (!ASSERT_OK_PTR(dir, "mkdtemp")) 1621 goto out_map; 1622 if (!ASSERT_OK(run_setup("setup", dir), "verify_sig_setup")) { 1623 rmdir(dir); 1624 goto out_map; 1625 } 1626 1627 /* Sign the instructions, raw map fd and all. */ 1628 if (!ASSERT_OK(sign_buf(dir, insns, sizeof(insns), sig, &sig_sz), 1629 "sign-file")) 1630 goto cleanup; 1631 1632 memset(&attr, 0, sizeof(attr)); 1633 attr.prog_type = BPF_PROG_TYPE_SYSCALL; 1634 attr.insns = ptr_to_u64(insns); 1635 attr.insn_cnt = ARRAY_SIZE(insns); 1636 attr.license = ptr_to_u64("Dual BSD/GPL"); 1637 attr.prog_flags = BPF_F_SLEEPABLE; 1638 attr.signature = ptr_to_u64(sig); 1639 attr.signature_size = sig_sz; 1640 attr.keyring_id = KEY_SPEC_SESSION_KEYRING; 1641 /* No fd_array: the map is reached by a raw fd in the instructions. */ 1642 memcpy(attr.prog_name, "signed_mapfd", sizeof("signed_mapfd")); 1643 1644 prog_fd = syscall(__NR_bpf, BPF_PROG_LOAD, &attr, 1645 offsetofend(union bpf_attr, keyring_id)); 1646 err = prog_fd < 0 ? -errno : prog_fd; 1647 ASSERT_EQ(err, -EINVAL, "signed program referencing a map by fd rejected"); 1648 if (prog_fd >= 0) 1649 close(prog_fd); 1650 cleanup: 1651 run_setup("cleanup", dir); 1652 out_map: 1653 close(map_fd); 1654 } 1655 1656 /* 1657 * A signed program may reach maps only through the continuous fd_array, so the 1658 * kernel folds (and thus attests) them. Referencing a map by fd_array *index* 1659 * while leaving fd_array_cnt at 0 selects the sparse path, which resolves a map 1660 * the signature never covered; the verifier rejects it up front with -EACCES. 1661 */ 1662 static void signed_sparse_fd_array_rejected(void) 1663 { 1664 struct bpf_insn insns[] = { 1665 BPF_LD_IMM64_RAW(BPF_REG_1, BPF_PSEUDO_MAP_IDX, 0), 1666 BPF_MOV64_IMM(BPF_REG_0, 0), 1667 BPF_EXIT_INSN(), 1668 }; 1669 char dir_tmpl[] = "/tmp/signed_loader_spXXXXXX", *dir; 1670 __u32 sig_sz = 8192; 1671 union bpf_attr attr; 1672 __u8 sig[8192]; 1673 int map_fd, prog_fd, err; 1674 1675 map_fd = bpf_map_create(BPF_MAP_TYPE_ARRAY, "sig_sparse", 4, 4, 1, NULL); 1676 if (!ASSERT_GE(map_fd, 0, "map_create")) 1677 return; 1678 1679 syscall(__NR_request_key, "keyring", "_uid.0", NULL, 1680 KEY_SPEC_SESSION_KEYRING); 1681 dir = mkdtemp(dir_tmpl); 1682 if (!ASSERT_OK_PTR(dir, "mkdtemp")) 1683 goto out_map; 1684 if (!ASSERT_OK(run_setup("setup", dir), "verify_sig_setup")) { 1685 rmdir(dir); 1686 goto out_map; 1687 } 1688 1689 /* Sign the instructions alone; the sparse map is not folded. */ 1690 if (!ASSERT_OK(sign_buf(dir, insns, sizeof(insns), sig, &sig_sz), 1691 "sign-file")) 1692 goto cleanup; 1693 1694 memset(&attr, 0, sizeof(attr)); 1695 attr.prog_type = BPF_PROG_TYPE_SYSCALL; 1696 attr.insns = ptr_to_u64(insns); 1697 attr.insn_cnt = ARRAY_SIZE(insns); 1698 attr.license = ptr_to_u64("Dual BSD/GPL"); 1699 attr.prog_flags = BPF_F_SLEEPABLE; 1700 attr.fd_array = ptr_to_u64(&map_fd); 1701 attr.fd_array_cnt = 0; /* sparse: force lazy map resolution */ 1702 attr.signature = ptr_to_u64(sig); 1703 attr.signature_size = sig_sz; 1704 attr.keyring_id = KEY_SPEC_SESSION_KEYRING; 1705 memcpy(attr.prog_name, "signed_sparse", sizeof("signed_sparse")); 1706 1707 prog_fd = syscall(__NR_bpf, BPF_PROG_LOAD, &attr, 1708 offsetofend(union bpf_attr, keyring_id)); 1709 err = prog_fd < 0 ? -errno : prog_fd; 1710 ASSERT_EQ(err, -EACCES, "signed program binding a sparse fd_array map rejected"); 1711 if (prog_fd >= 0) 1712 close(prog_fd); 1713 cleanup: 1714 run_setup("cleanup", dir); 1715 out_map: 1716 close(map_fd); 1717 } 1718 1719 static void signed_module_kfunc_rejected(void) 1720 { 1721 struct bpf_insn insns[] = { 1722 BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, BPF_PSEUDO_KFUNC_CALL, 1, 1), 1723 BPF_MOV64_IMM(BPF_REG_0, 0), 1724 BPF_EXIT_INSN(), 1725 }; 1726 char dir_tmpl[] = "/tmp/signed_loader_kfnXXXXXX", *dir; 1727 int prog_fd, err, fds[2]; 1728 struct btf *btf = NULL; 1729 __u32 sig_sz = 8192; 1730 union bpf_attr attr; 1731 __u8 sig[8192]; 1732 1733 syscall(__NR_request_key, "keyring", "_uid.0", NULL, 1734 KEY_SPEC_SESSION_KEYRING); 1735 dir = mkdtemp(dir_tmpl); 1736 if (!ASSERT_OK_PTR(dir, "mkdtemp")) 1737 return; 1738 if (!ASSERT_OK(run_setup("setup", dir), "verify_sig_setup")) { 1739 rmdir(dir); 1740 return; 1741 } 1742 if (!ASSERT_OK(sign_buf(dir, insns, sizeof(insns), sig, &sig_sz), 1743 "sign-file")) 1744 goto cleanup; 1745 btf = btf__new_empty(); 1746 if (!ASSERT_OK_PTR(btf, "btf_new_empty")) 1747 goto cleanup; 1748 btf__add_int(btf, "int", 4, BTF_INT_SIGNED); 1749 if (!ASSERT_OK(btf__load_into_kernel(btf), "btf_load")) 1750 goto cleanup; 1751 fds[0] = -1; 1752 fds[1] = btf__fd(btf); 1753 1754 memset(&attr, 0, sizeof(attr)); 1755 attr.prog_type = BPF_PROG_TYPE_SYSCALL; 1756 attr.insns = ptr_to_u64(insns); 1757 attr.insn_cnt = ARRAY_SIZE(insns); 1758 attr.license = ptr_to_u64("Dual BSD/GPL"); 1759 attr.prog_flags = BPF_F_SLEEPABLE; 1760 attr.fd_array = ptr_to_u64(fds); 1761 attr.fd_array_cnt = 0; /* sparse: force lazy kfunc BTF resolution */ 1762 attr.signature = ptr_to_u64(sig); 1763 attr.signature_size = sig_sz; 1764 attr.keyring_id = KEY_SPEC_SESSION_KEYRING; 1765 memcpy(attr.prog_name, "signed_kfunc", sizeof("signed_kfunc")); 1766 1767 prog_fd = syscall(__NR_bpf, BPF_PROG_LOAD, &attr, 1768 offsetofend(union bpf_attr, keyring_id)); 1769 err = prog_fd < 0 ? -errno : prog_fd; 1770 if (prog_fd >= 0) 1771 close(prog_fd); 1772 1773 ASSERT_EQ(err, -EACCES, "module kfunc BTF in signed program rejected"); 1774 cleanup: 1775 if (btf) 1776 btf__free(btf); 1777 run_setup("cleanup", dir); 1778 } 1779 1780 void test_signed_loader(void) 1781 { 1782 if (test__start_subtest("loadtime_no_map")) 1783 loadtime_no_map(); 1784 if (test__start_subtest("loadtime_with_map")) 1785 loadtime_with_map(); 1786 if (test__start_subtest("metadata_match")) 1787 metadata_match(); 1788 if (test__start_subtest("signature_enforced")) 1789 signature_enforced(); 1790 if (test__start_subtest("signed_nonexcl_fd_array_rejected")) 1791 signed_nonexcl_fd_array_rejected(); 1792 if (test__start_subtest("signed_unfrozen_fd_array_rejected")) 1793 signed_unfrozen_fd_array_rejected(); 1794 if (test__start_subtest("signed_nonarray_fd_array_rejected")) 1795 signed_nonarray_fd_array_rejected(); 1796 if (test__start_subtest("signed_btf_fd_array_rejected")) 1797 signed_btf_fd_array_rejected(); 1798 if (test__start_subtest("signed_module_kfunc_rejected")) 1799 signed_module_kfunc_rejected(); 1800 if (test__start_subtest("signature_failure_logs")) 1801 signature_failure_logs(); 1802 if (test__start_subtest("signature_too_large")) 1803 signature_too_large(); 1804 if (test__start_subtest("signature_zero_size")) 1805 signature_zero_size(); 1806 if (test__start_subtest("signature_bad_keyring")) 1807 signature_bad_keyring(); 1808 if (test__start_subtest("metadata_ctx_max_entries_ignored")) 1809 metadata_ctx_max_entries_ignored(); 1810 if (test__start_subtest("metadata_ctx_initial_value_ignored")) 1811 metadata_ctx_initial_value_ignored(); 1812 if (test__start_subtest("signature_authenticates_insns")) 1813 signature_authenticates_insns(); 1814 if (test__start_subtest("signature_authenticates_metadata")) 1815 signature_authenticates_metadata(); 1816 if (test__start_subtest("hash_requires_frozen")) 1817 hash_requires_frozen(); 1818 if (test__start_subtest("no_update_after_freeze")) 1819 no_update_after_freeze(); 1820 if (test__start_subtest("freeze_writable_mmap")) 1821 freeze_writable_mmap(); 1822 if (test__start_subtest("no_writable_mmap_frozen")) 1823 no_writable_mmap_frozen(); 1824 if (test__start_subtest("map_hash_matches_libbpf")) 1825 map_hash_matches_libbpf(); 1826 if (test__start_subtest("map_hash_multi_element")) 1827 map_hash_multi_element(); 1828 if (test__start_subtest("map_hash_bad_size")) 1829 map_hash_bad_size(); 1830 if (test__start_subtest("map_hash_unsupported_type")) 1831 map_hash_unsupported_type(); 1832 if (test__start_subtest("lsm_signature_verdict")) 1833 lsm_signature_verdict(); 1834 if (test__start_subtest("signed_no_fd_array")) 1835 signed_no_fd_array(); 1836 if (test__start_subtest("signed_map_by_fd_rejected")) 1837 signed_map_by_fd_rejected(); 1838 if (test__start_subtest("signed_sparse_fd_array_rejected")) 1839 signed_sparse_fd_array_rejected(); 1840 } 1841