xref: /linux/tools/testing/selftests/bpf/prog_tests/signed_loader.c (revision 8ddce416797b7454ba1df855821b02c6e43b5a0e)
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/libbpf_internal.h" /* for libbpf_sha256() */
15 #include "bpf/skel_internal.h"	 /* for loader ctx layout (bpf_loader_ctx etc) */
16 
17 #include "test_signed_loader.skel.h"
18 #include "test_signed_loader_map.skel.h"
19 #include "test_signed_loader_data.skel.h"
20 #include "test_signed_loader_lsm.skel.h"
21 
22 #define SIG_MATCH_INSNS 33 /* excl (5) + 4 * sha-dword (7) */
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 {
40 	union bpf_attr attr;
41 	int fd;
42 
43 	memset(&attr, 0, sizeof(attr));
44 	attr.prog_type = BPF_PROG_TYPE_SYSCALL;
45 	attr.insns = ptr_to_u64(insns);
46 	attr.insn_cnt = insns_sz / sizeof(struct bpf_insn);
47 	attr.license = ptr_to_u64("Dual BSD/GPL");
48 	attr.prog_flags = BPF_F_SLEEPABLE;
49 	attr.fd_array = ptr_to_u64(&map_fd);
50 	if (sig) {
51 		attr.signature = ptr_to_u64(sig);
52 		attr.signature_size = sig_sz;
53 		attr.keyring_id = keyring_id;
54 	}
55 	memcpy(attr.prog_name, "__loader.prog", sizeof("__loader.prog"));
56 	fd = syscall(__NR_bpf, BPF_PROG_LOAD, &attr,
57 		     offsetofend(union bpf_attr, keyring_id));
58 	return fd < 0 ? -errno : fd;
59 }
60 
61 static int run_gen_loader(const void *insns, __u32 insns_sz,
62 			  const void *data, __u32 data_sz,
63 			  const void *excl, __u32 excl_sz,
64 			  const void *sig, __u32 sig_sz,
65 			  bool get_hash, void *ctx, __u32 ctx_sz, bool *loader_ran)
66 {
67 	LIBBPF_OPTS(bpf_map_create_opts, mopts,
68 		    .excl_prog_hash = excl,
69 		    .excl_prog_hash_size = excl_sz);
70 	__u8 hbuf[SHA256_DIGEST_LENGTH];
71 	struct bpf_map_info info;
72 	__u32 ilen = sizeof(info), 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 	if (get_hash) {
91 		memset(&info, 0, sizeof(info));
92 		info.hash = ptr_to_u64(hbuf);
93 		info.hash_size = sizeof(hbuf);
94 		if (bpf_map_get_info_by_fd(map_fd, &info, &ilen)) {
95 			ret = -errno;
96 			goto out_map;
97 		}
98 	}
99 
100 	memset(&attr, 0, sizeof(attr));
101 	attr.prog_type = BPF_PROG_TYPE_SYSCALL;
102 	attr.insns = ptr_to_u64(insns);
103 	attr.insn_cnt = insns_sz / sizeof(struct bpf_insn);
104 	attr.license = ptr_to_u64("Dual BSD/GPL");
105 	attr.prog_flags = BPF_F_SLEEPABLE;
106 	attr.fd_array = ptr_to_u64(&map_fd);
107 	if (sig) {
108 		attr.signature = ptr_to_u64(sig);
109 		attr.signature_size = sig_sz;
110 		attr.keyring_id = KEY_SPEC_SESSION_KEYRING;
111 	}
112 	memcpy(attr.prog_name, "__loader.prog", sizeof("__loader.prog"));
113 	prog_fd = syscall(__NR_bpf, BPF_PROG_LOAD, &attr,
114 			  offsetofend(union bpf_attr, keyring_id));
115 	if (prog_fd < 0) {
116 		ret = -errno;
117 		goto out_map;
118 	}
119 
120 	memset(&attr, 0, sizeof(attr));
121 	attr.test.prog_fd = prog_fd;
122 	attr.test.ctx_in = ptr_to_u64(ctx);
123 	attr.test.ctx_size_in = ctx_sz;
124 	if (syscall(__NR_bpf, BPF_PROG_RUN, &attr,
125 		    offsetofend(union bpf_attr, test)) < 0) {
126 		ret = -errno;
127 		goto out_prog;
128 	}
129 	*loader_ran = true;
130 	ret = (int)attr.test.retval;
131 out_prog:
132 	close(prog_fd);
133 out_map:
134 	close(map_fd);
135 	return ret;
136 }
137 
138 static void close_loader_ctx_fds(void *ctx, int nr_maps, int nr_progs)
139 {
140 	struct bpf_map_desc *md = (struct bpf_map_desc *)((char *)ctx +
141 				  sizeof(struct bpf_loader_ctx));
142 	struct bpf_prog_desc *pd = (struct bpf_prog_desc *)(md + nr_maps);
143 	int i;
144 
145 	for (i = 0; i < nr_maps; i++)
146 		if (md[i].map_fd > 0)
147 			close(md[i].map_fd);
148 	for (i = 0; i < nr_progs; i++)
149 		if (pd[i].prog_fd > 0)
150 			close(pd[i].prog_fd);
151 }
152 
153 static int run_setup(const char *cmd, const char *dir)
154 {
155 	int pid, status;
156 
157 	pid = fork();
158 	if (pid < 0)
159 		return -errno;
160 	if (pid == 0) {
161 		execlp("./verify_sig_setup.sh", "./verify_sig_setup.sh",
162 		       cmd, dir, NULL);
163 		exit(1);
164 	}
165 	if (waitpid(pid, &status, 0) < 0)
166 		return -errno;
167 	return (WIFEXITED(status) &&
168 		WEXITSTATUS(status) == 0) ? 0 : -EINVAL;
169 }
170 
171 static int sign_buf(const char *dir, const void *buf, __u32 len,
172 		    void *sig, __u32 *sig_sz)
173 {
174 	char data_tmpl[PATH_MAX], key[PATH_MAX];
175 	char sigpath[PATH_MAX + sizeof(".p7s")];
176 	int fd, pid, status, ret;
177 	struct stat st;
178 
179 	ret = snprintf(data_tmpl, sizeof(data_tmpl), "%s/dataXXXXXX", dir);
180 	if (ret < 0 || ret >= (int)sizeof(data_tmpl))
181 		return -ENAMETOOLONG;
182 	ret = 0;
183 
184 	fd = mkstemp(data_tmpl);
185 	if (fd < 0)
186 		return -errno;
187 	if (write(fd, buf, len) != (ssize_t)len) {
188 		close(fd);
189 		ret = -EIO;
190 		goto out;
191 	}
192 	close(fd);
193 
194 	pid = fork();
195 	if (pid < 0) {
196 		ret = -errno;
197 		goto out;
198 	}
199 	if (pid == 0) {
200 		snprintf(key, sizeof(key), "%s/signing_key.pem", dir);
201 		execlp("./sign-file", "./sign-file", "-d", "sha256",
202 		       key, key, data_tmpl, NULL);
203 		exit(1);
204 	}
205 	if (waitpid(pid, &status, 0) < 0 ||
206 	    !WIFEXITED(status) || WEXITSTATUS(status)) {
207 		ret = -EINVAL;
208 		goto out;
209 	}
210 
211 	snprintf(sigpath, sizeof(sigpath), "%s.p7s", data_tmpl);
212 	if (stat(sigpath, &st) < 0) {
213 		ret = -errno;
214 		goto out;
215 	}
216 	if (st.st_size > (off_t)*sig_sz) {
217 		ret = -E2BIG;
218 		goto out_sig;
219 	}
220 	fd = open(sigpath, O_RDONLY);
221 	if (fd < 0) {
222 		ret = -errno;
223 		goto out_sig;
224 	}
225 	if (read(fd, sig, st.st_size) != st.st_size) {
226 		close(fd);
227 		ret = -EIO;
228 		goto out_sig;
229 	}
230 	close(fd);
231 	*sig_sz = st.st_size;
232 out_sig:
233 	unlink(sigpath);
234 out:
235 	unlink(data_tmpl);
236 	return ret;
237 }
238 
239 static void check_sig_match_shape(const struct bpf_insn *in, int n)
240 {
241 	int a = -1, cleanup = -1, i, base, t, br[5], nb = 0;
242 
243 	/* BPF_PSEUDO_MAP_IDX (the struct bpf_map * form) is used only here. */
244 	for (i = 0; i + 1 < n; i++) {
245 		if (in[i].code == (BPF_LD | BPF_IMM | BPF_DW) &&
246 		    in[i].src_reg == BPF_PSEUDO_MAP_IDX) {
247 			a = i;
248 			break;
249 		}
250 	}
251 	if (!ASSERT_GE(a, 0, "emit_signature_match present"))
252 		return;
253 	if (!ASSERT_LE(a + SIG_MATCH_INSNS, n, "block fits in program"))
254 		return;
255 
256 	/* excl check: r2 = *(u32 *)(map + 32); if r2 != 1 goto cleanup */
257 	ASSERT_EQ(in[a + 2].code, (BPF_LDX | BPF_MEM | BPF_W), "excl load width");
258 	ASSERT_EQ(in[a + 2].off, SHA256_DIGEST_LENGTH, "excl field offset");
259 	ASSERT_EQ(in[a + 4].code, (BPF_JMP | BPF_JNE | BPF_K), "excl branch op");
260 	ASSERT_EQ(in[a + 4].imm, 1, "excl compared to 1");
261 	br[nb++] = a + 4;
262 
263 	/* 4 sha-dword checks: r2 = *(u64 *)(map + i*8); if r2 != r3 goto cleanup */
264 	for (i = 0; i < 4; i++) {
265 		base = a + 5 + i * 7;
266 		ASSERT_EQ(in[base + 2].code, (BPF_LDX | BPF_MEM | BPF_DW), "sha load width");
267 		ASSERT_EQ(in[base + 2].off, i * 8, "sha dword offset");
268 		ASSERT_EQ(in[base + 3].code, (BPF_LD | BPF_IMM | BPF_DW), "sha imm64 (H_meta)");
269 		ASSERT_EQ(in[base + 6].code, (BPF_JMP | BPF_JNE | BPF_X), "sha branch op");
270 		br[nb++] = base + 6;
271 	}
272 
273 	/*
274 	 * Locate the real cleanup label so we can pin the exact jump target,
275 	 * not just "some backward label". bpf_gen__init() emits the cleanup
276 	 * block as a prog-fd close loop whose first instruction is the label
277 	 * every error branch jumps to.
278 	 */
279 	for (i = 0; i + 2 < a; i++) {
280 		if (in[i].code == (BPF_LDX | BPF_MEM | BPF_W) &&
281 		    in[i].dst_reg == BPF_REG_1 && in[i].src_reg == BPF_REG_10 &&
282 		    in[i + 1].code == (BPF_JMP | BPF_JSLE | BPF_K) &&
283 		    in[i + 1].dst_reg == BPF_REG_1 && in[i + 1].imm == 0 &&
284 		    in[i + 1].off == 1 &&
285 		    in[i + 2].code == (BPF_JMP | BPF_CALL) &&
286 		    in[i + 2].imm == BPF_FUNC_sys_close) {
287 			cleanup = i;
288 			break;
289 		}
290 	}
291 	if (!ASSERT_GE(cleanup, 0, "cleanup label located"))
292 		return;
293 	for (i = 0; i < nb; i++) {
294 		t = br[i] + 1 + in[br[i]].off;
295 		ASSERT_EQ(t, cleanup, "sig-match lands on cleanup");
296 	}
297 	/*
298 	 * Same invariant for every other cleanup-bound jump in the program:
299 	 * emit_check_err() is the only source of "if (r7 < 0) goto cleanup",
300 	 * so each of those must also resolve exactly to cleanup.
301 	 */
302 	for (i = 0, t = 0; i < n; i++) {
303 		if (in[i].code != (BPF_JMP | BPF_JSLT | BPF_K) ||
304 		    in[i].dst_reg != BPF_REG_7 || in[i].imm != 0 || in[i].off >= 0)
305 			continue;
306 		ASSERT_EQ(i + 1 + in[i].off, cleanup, "err-check lands on cleanup");
307 		t++;
308 	}
309 	ASSERT_GT(t, 0, "found emit_check_err jumps");
310 }
311 
312 struct gen_loader_fixture {
313 	struct test_signed_loader *skel;
314 	struct gen_loader_opts gopts;
315 	unsigned char *blob;
316 	void *ctx;
317 	__u32 data_sz;
318 	__u32 ctx_sz;
319 	int nr_maps;
320 	int nr_progs;
321 	__u8 excl[SHA256_DIGEST_LENGTH];
322 };
323 
324 static int gen_loader_fixture_init(struct gen_loader_fixture *f)
325 {
326 	LIBBPF_OPTS(gen_loader_opts, gopts, .gen_hash = true);
327 	int nr_maps = 0, nr_progs = 0;
328 	struct bpf_program *p;
329 	struct bpf_map *m;
330 
331 	memset(f, 0, sizeof(*f));
332 	f->skel = test_signed_loader__open();
333 	if (!ASSERT_OK_PTR(f->skel, "skel_open"))
334 		return -1;
335 	if (!ASSERT_OK(bpf_object__gen_loader(f->skel->obj, &gopts), "gen_loader"))
336 		return -1;
337 	if (!ASSERT_OK(bpf_object__load(f->skel->obj), "gen_load"))
338 		return -1;
339 	f->gopts = gopts;
340 
341 	bpf_object__for_each_program(p, f->skel->obj)
342 		nr_progs++;
343 	bpf_object__for_each_map(m, f->skel->obj)
344 		nr_maps++;
345 	f->nr_maps = nr_maps;
346 	f->nr_progs = nr_progs;
347 	f->ctx_sz = sizeof(struct bpf_loader_ctx) +
348 		    nr_maps * sizeof(struct bpf_map_desc) +
349 		    nr_progs * sizeof(struct bpf_prog_desc);
350 	f->ctx = calloc(1, f->ctx_sz);
351 	if (!ASSERT_OK_PTR(f->ctx, "ctx_alloc"))
352 		return -1;
353 	((struct bpf_loader_ctx *)f->ctx)->sz = f->ctx_sz;
354 
355 	f->data_sz = gopts.data_sz;
356 	f->blob = malloc(f->data_sz);
357 	if (!ASSERT_OK_PTR(f->blob, "blob_alloc"))
358 		return -1;
359 	memcpy(f->blob, gopts.data, f->data_sz);
360 
361 	/* excl_prog_hash = SHA256(loader insns) == the loader's prog->digest. */
362 	libbpf_sha256(gopts.insns, gopts.insns_sz, f->excl);
363 	return 0;
364 }
365 
366 static void gen_loader_fixture_fini(struct gen_loader_fixture *f)
367 {
368 	if (f->ctx)
369 		close_loader_ctx_fds(f->ctx, f->nr_maps, f->nr_progs);
370 	free(f->blob);
371 	free(f->ctx);
372 	test_signed_loader__destroy(f->skel);
373 }
374 
375 static void metadata_check_shape(void)
376 {
377 	struct gen_loader_fixture f;
378 
379 	if (gen_loader_fixture_init(&f) == 0)
380 		check_sig_match_shape((const struct bpf_insn *)f.gopts.insns,
381 				      f.gopts.insns_sz / sizeof(struct bpf_insn));
382 	gen_loader_fixture_fini(&f);
383 }
384 
385 static void metadata_match(void)
386 {
387 	struct gen_loader_fixture f;
388 	bool ran;
389 	int r;
390 
391 	if (gen_loader_fixture_init(&f) == 0) {
392 		r = run_gen_loader(f.gopts.insns, f.gopts.insns_sz, f.blob,
393 				   f.data_sz, f.excl, sizeof(f.excl), NULL, 0,
394 				   true, f.ctx, f.ctx_sz, &ran);
395 		ASSERT_TRUE(ran, "loader ran");
396 		ASSERT_EQ(r, 0, "honest loader retval");
397 	}
398 	gen_loader_fixture_fini(&f);
399 }
400 
401 static void metadata_sha_mismatch(void)
402 {
403 	struct gen_loader_fixture f;
404 	bool ran;
405 	int r;
406 
407 	if (gen_loader_fixture_init(&f) == 0) {
408 		/*
409 		 * blob[0] lives in the loader's fd_array scratch (first add_data in
410 		 * bpf_gen__init); a 0-map program never reads it, so flipping it
411 		 * changes only map->sha. The metadata check is the only thing that
412 		 * can notice -> isolates emit_signature_match.
413 		 */
414 		f.blob[0] ^= 0xff;
415 		r = run_gen_loader(f.gopts.insns, f.gopts.insns_sz, f.blob,
416 				   f.data_sz, f.excl, sizeof(f.excl), NULL, 0,
417 				   true, f.ctx, f.ctx_sz, &ran);
418 		ASSERT_TRUE(ran, "loader ran");
419 		ASSERT_EQ(r, -EINVAL, "tampered blob rejected by emit_signature_match");
420 	}
421 	gen_loader_fixture_fini(&f);
422 }
423 
424 static void metadata_not_exclusive(void)
425 {
426 	struct gen_loader_fixture f;
427 	bool ran;
428 	int r;
429 
430 	if (gen_loader_fixture_init(&f) == 0) {
431 		/*
432 		 * Correct blob but a non-exclusive metadata map: the verifier does
433 		 * not reject (excl_prog_sha unset), so the runtime map->excl == 1
434 		 * check in the loader must.
435 		 */
436 		r = run_gen_loader(f.gopts.insns, f.gopts.insns_sz, f.blob,
437 				   f.data_sz, NULL, 0, NULL, 0, true, f.ctx,
438 				   f.ctx_sz, &ran);
439 		ASSERT_TRUE(ran, "loader ran");
440 		ASSERT_EQ(r, -EINVAL, "non-exclusive metadata map rejected");
441 	}
442 	gen_loader_fixture_fini(&f);
443 }
444 
445 static void metadata_hash_not_computed(void)
446 {
447 	struct gen_loader_fixture f;
448 	bool ran;
449 	int r;
450 
451 	if (gen_loader_fixture_init(&f) == 0) {
452 		/*
453 		 * Correct, exclusive, frozen map, but its hash was never computed
454 		 * (no OBJ_GET_INFO_BY_FD), so map->sha stays zero. The loader must
455 		 * fail closed rather than treat an unset hash as a match.
456 		 */
457 		r = run_gen_loader(f.gopts.insns, f.gopts.insns_sz, f.blob,
458 				   f.data_sz, f.excl, sizeof(f.excl), NULL, 0,
459 				   false, f.ctx, f.ctx_sz, &ran);
460 		ASSERT_TRUE(ran, "loader ran");
461 		ASSERT_EQ(r, -EINVAL, "uncomputed metadata hash rejected");
462 	}
463 	gen_loader_fixture_fini(&f);
464 }
465 
466 static void signature_enforced(void)
467 {
468 	static const __u8 junk[64] = { 0x30, 0x42, 0x13, 0x37, };
469 	struct gen_loader_fixture f;
470 	int fd;
471 
472 	if (gen_loader_fixture_init(&f) == 0) {
473 		/*
474 		 * A present-but-invalid signature (the cert bytes are not a
475 		 * PKCS#7 signature) must be rejected at load: the signature
476 		 * path is honored, not ignored. (The valid path is covered by
477 		 * the signed lskels.)
478 		 */
479 		fd = load_loader(f.gopts.insns, f.gopts.insns_sz, -1, junk,
480 				 sizeof(junk), KEY_SPEC_SESSION_KEYRING);
481 		ASSERT_LT(fd, 0, "invalid signature rejected at load");
482 	}
483 	gen_loader_fixture_fini(&f);
484 }
485 
486 static void signature_too_large(void)
487 {
488 	static const __u8 junk[64] = {};
489 	struct gen_loader_fixture f;
490 	int fd;
491 
492 	if (gen_loader_fixture_init(&f) == 0) {
493 		/*
494 		 * signature_size beyond the kernel's bound (KMALLOC_MAX_CACHE_SIZE)
495 		 * is rejected before the buffer is read.
496 		 */
497 		fd = load_loader(f.gopts.insns, f.gopts.insns_sz, -1, junk,
498 				 64 << 20, KEY_SPEC_SESSION_KEYRING);
499 		ASSERT_EQ(fd, -EINVAL, "oversized signature rejected");
500 	}
501 	gen_loader_fixture_fini(&f);
502 }
503 
504 static void signature_bad_keyring(void)
505 {
506 	static const __u8 junk[64] = {};
507 	struct gen_loader_fixture f;
508 	int fd;
509 
510 	if (gen_loader_fixture_init(&f) == 0) {
511 		/*
512 		 * A present signature with a keyring_id that resolves to no key is
513 		 * rejected up front: bpf_prog_verify_signature() fails the keyring
514 		 * lookup (-EINVAL) before it ever looks at the signature bytes. A
515 		 * large positive serial takes the user-keyring path and won't exist.
516 		 */
517 		fd = load_loader(f.gopts.insns, f.gopts.insns_sz, -1, junk,
518 				 sizeof(junk), INT_MAX);
519 		ASSERT_EQ(fd, -EINVAL, "signature with bad keyring_id rejected");
520 	}
521 	gen_loader_fixture_fini(&f);
522 }
523 
524 /*
525  * A signed loader must ignore ctx-supplied map dimensions: the host cannot
526  * resize a signed program's maps via the loader ctx. Drive a one-map program
527  * through gen_loader, ask (via ctx) for every map to be resized to a bogus
528  * value, and confirm the created maps keep their attested size.
529  */
530 #define GATING_BOGUS_MAX 0x4000
531 
532 static void metadata_ctx_max_entries_ignored(void)
533 {
534 	LIBBPF_OPTS(gen_loader_opts, gopts, .gen_hash = true);
535 	struct test_signed_loader_map *skel;
536 	__u8 excl[SHA256_DIGEST_LENGTH];
537 	int nr_maps = 0, nr_progs = 0, i, checked = 0, r;
538 	struct bpf_program *p;
539 	struct bpf_map *m;
540 	struct bpf_map_desc *md;
541 	unsigned char *blob;
542 	__u32 ctx_sz, data_sz;
543 	void *ctx;
544 	bool ran;
545 
546 	skel = test_signed_loader_map__open();
547 	if (!ASSERT_OK_PTR(skel, "skel_open"))
548 		return;
549 	if (!ASSERT_OK(bpf_object__gen_loader(skel->obj, &gopts), "gen_loader"))
550 		goto destroy;
551 	if (!ASSERT_OK(bpf_object__load(skel->obj), "gen_load"))
552 		goto destroy;
553 
554 	bpf_object__for_each_program(p, skel->obj)
555 		nr_progs++;
556 	bpf_object__for_each_map(m, skel->obj)
557 		nr_maps++;
558 	ctx_sz = sizeof(struct bpf_loader_ctx) +
559 		 nr_maps * sizeof(struct bpf_map_desc) +
560 		 nr_progs * sizeof(struct bpf_prog_desc);
561 	ctx = calloc(1, ctx_sz);
562 	if (!ASSERT_OK_PTR(ctx, "ctx_alloc"))
563 		goto destroy;
564 	((struct bpf_loader_ctx *)ctx)->sz = ctx_sz;
565 
566 	md = (struct bpf_map_desc *)((char *)ctx + sizeof(struct bpf_loader_ctx));
567 	for (i = 0; i < nr_maps; i++)
568 		md[i].max_entries = GATING_BOGUS_MAX;
569 
570 	libbpf_sha256(gopts.insns, gopts.insns_sz, excl);
571 	data_sz = gopts.data_sz;
572 	blob = malloc(data_sz);
573 	if (!ASSERT_OK_PTR(blob, "blob_alloc"))
574 		goto free_ctx;
575 	memcpy(blob, gopts.data, data_sz);
576 
577 	r = run_gen_loader(gopts.insns, gopts.insns_sz, blob, data_sz,
578 			   excl, sizeof(excl), NULL, 0, true, ctx, ctx_sz, &ran);
579 	if (!ASSERT_TRUE(ran, "loader ran") ||
580 	    !ASSERT_EQ(r, 0, "loader retval"))
581 		goto free_blob;
582 
583 	for (i = 0; i < nr_maps; i++) {
584 		struct bpf_map_info info;
585 		__u32 ilen = sizeof(info);
586 		int fd = md[i].map_fd;
587 
588 		if (fd <= 0)
589 			continue;
590 		memset(&info, 0, sizeof(info));
591 		if (ASSERT_OK(bpf_map_get_info_by_fd(fd, &info, &ilen), "map_info")) {
592 			ASSERT_NEQ(info.max_entries, GATING_BOGUS_MAX,
593 				   "ctx max_entries ignored for signed loader");
594 			checked++;
595 		}
596 	}
597 	ASSERT_GT(checked, 0, "inspected a created map");
598 
599 free_blob:
600 	free(blob);
601 free_ctx:
602 	close_loader_ctx_fds(ctx, nr_maps, nr_progs);
603 	free(ctx);
604 destroy:
605 	test_signed_loader_map__destroy(skel);
606 }
607 
608 /*
609  * A signed loader must also ignore ctx-supplied initial_value: the host cannot
610  * re-seed a signed program's map contents through the loader ctx. Drive a
611  * program with one initialized global (a .data map) through gen_loader, point
612  * every map's ctx initial_value at an adversarial buffer, and confirm the
613  * created map still holds the attested value, never the ctx bytes.
614  */
615 #define DATA_MAGIC 0x5eed1234abad1deaULL
616 
617 static void metadata_ctx_initial_value_ignored(void)
618 {
619 	LIBBPF_OPTS(gen_loader_opts, gopts, .gen_hash = true);
620 	struct test_signed_loader_data *skel;
621 	__u8 excl[SHA256_DIGEST_LENGTH], evil[64];
622 	int nr_maps = 0, nr_progs = 0, i, found = 0, r;
623 	struct bpf_program *p;
624 	struct bpf_map *m;
625 	struct bpf_map_desc *md;
626 	unsigned char *blob;
627 	__u32 ctx_sz, data_sz;
628 	void *ctx;
629 	bool ran;
630 
631 	skel = test_signed_loader_data__open();
632 	if (!ASSERT_OK_PTR(skel, "skel_open"))
633 		return;
634 	if (!ASSERT_OK(bpf_object__gen_loader(skel->obj, &gopts), "gen_loader"))
635 		goto destroy;
636 	if (!ASSERT_OK(bpf_object__load(skel->obj), "gen_load"))
637 		goto destroy;
638 
639 	bpf_object__for_each_program(p, skel->obj)
640 		nr_progs++;
641 	bpf_object__for_each_map(m, skel->obj)
642 		nr_maps++;
643 	ctx_sz = sizeof(struct bpf_loader_ctx) +
644 		 nr_maps * sizeof(struct bpf_map_desc) +
645 		 nr_progs * sizeof(struct bpf_prog_desc);
646 	ctx = calloc(1, ctx_sz);
647 	if (!ASSERT_OK_PTR(ctx, "ctx_alloc"))
648 		goto destroy;
649 	((struct bpf_loader_ctx *)ctx)->sz = ctx_sz;
650 
651 	memset(evil, 0xAA, sizeof(evil));
652 	md = (struct bpf_map_desc *)((char *)ctx + sizeof(struct bpf_loader_ctx));
653 	for (i = 0; i < nr_maps; i++)
654 		md[i].initial_value = ptr_to_u64(evil);
655 
656 	libbpf_sha256(gopts.insns, gopts.insns_sz, excl);
657 	data_sz = gopts.data_sz;
658 	blob = malloc(data_sz);
659 	if (!ASSERT_OK_PTR(blob, "blob_alloc"))
660 		goto free_ctx;
661 	memcpy(blob, gopts.data, data_sz);
662 
663 	r = run_gen_loader(gopts.insns, gopts.insns_sz, blob, data_sz,
664 			   excl, sizeof(excl), NULL, 0, true, ctx, ctx_sz, &ran);
665 	if (!ASSERT_TRUE(ran, "loader ran") ||
666 	    !ASSERT_EQ(r, 0, "loader retval"))
667 		goto free_blob;
668 
669 	for (i = 0; i < nr_maps; i++) {
670 		struct bpf_map_info info;
671 		__u32 ilen = sizeof(info), key = 0;
672 		__u8 value[64] = {};
673 		__u64 got;
674 		int fd = md[i].map_fd;
675 
676 		if (fd <= 0)
677 			continue;
678 		memset(&info, 0, sizeof(info));
679 		if (!ASSERT_OK(bpf_map_get_info_by_fd(fd, &info, &ilen), "map_info"))
680 			continue;
681 		if (info.value_size <= sizeof(value) &&
682 		    bpf_map_lookup_elem(fd, &key, value) == 0) {
683 			memcpy(&got, value, sizeof(got));
684 			/* attested .data survives; ctx bytes (0xAA..) ignored */
685 			if (got == DATA_MAGIC)
686 				found = 1;
687 			ASSERT_NEQ(got, 0xAAAAAAAAAAAAAAAAULL,
688 				   "ctx initial_value ignored for signed loader");
689 		}
690 	}
691 	ASSERT_EQ(found, 1, "attested .data value preserved");
692 
693 free_blob:
694 	free(blob);
695 free_ctx:
696 	close_loader_ctx_fds(ctx, nr_maps, nr_progs);
697 	free(ctx);
698 destroy:
699 	test_signed_loader_data__destroy(skel);
700 }
701 
702 /*
703  * The load-time signature must authenticate the loader instructions: a valid
704  * signature loads, and the very same signature over one-byte-tampered insns is
705  * rejected. Uses ./verify_sig_setup.sh + ./sign-file at runtime, like
706  * verify_pkcs7_sig, and verifies against the session keyring the key was added
707  * to. (signature_enforced/_too_large only cover a malformed signature.)
708  */
709 static void signature_authenticates_insns(void)
710 {
711 	LIBBPF_OPTS(gen_loader_opts, gopts, .gen_hash = true);
712 	char dir_tmpl[] = "/tmp/signed_loaderXXXXXX", *dir;
713 	struct test_signed_loader *skel = NULL;
714 	__u8 excl[SHA256_DIGEST_LENGTH], sig[8192];
715 	__u32 sig_sz = sizeof(sig), insns_sz, data_sz, ctx_sz;
716 	unsigned char *insns = NULL, *tampered = NULL, *blob = NULL;
717 	int nr_maps = 0, nr_progs = 0, r;
718 	struct bpf_program *p;
719 	struct bpf_map *m;
720 	void *ctx = NULL;
721 	bool ran;
722 
723 	syscall(__NR_request_key, "keyring", "_uid.0", NULL,
724 		KEY_SPEC_SESSION_KEYRING);
725 	dir = mkdtemp(dir_tmpl);
726 	if (!ASSERT_OK_PTR(dir, "mkdtemp"))
727 		return;
728 	if (!ASSERT_OK(run_setup("setup", dir), "verify_sig_setup")) {
729 		rmdir(dir);
730 		return;
731 	}
732 
733 	skel = test_signed_loader__open();
734 	if (!ASSERT_OK_PTR(skel, "skel_open"))
735 		goto cleanup;
736 	if (!ASSERT_OK(bpf_object__gen_loader(skel->obj, &gopts), "gen_loader"))
737 		goto cleanup;
738 	if (!ASSERT_OK(bpf_object__load(skel->obj), "gen_load"))
739 		goto cleanup;
740 
741 	bpf_object__for_each_program(p, skel->obj)
742 		nr_progs++;
743 	bpf_object__for_each_map(m, skel->obj)
744 		nr_maps++;
745 	ctx_sz = sizeof(struct bpf_loader_ctx) +
746 		 nr_maps * sizeof(struct bpf_map_desc) +
747 		 nr_progs * sizeof(struct bpf_prog_desc);
748 	insns_sz = gopts.insns_sz;
749 	data_sz = gopts.data_sz;
750 	ctx = calloc(1, ctx_sz);
751 	insns = malloc(insns_sz);
752 	tampered = malloc(insns_sz);
753 	blob = malloc(data_sz);
754 	if (!ASSERT_OK_PTR(ctx, "ctx") ||
755 	    !ASSERT_OK_PTR(insns, "insns") ||
756 	    !ASSERT_OK_PTR(tampered, "tampered") ||
757 	    !ASSERT_OK_PTR(blob, "blob"))
758 		goto cleanup;
759 	memcpy(insns, gopts.insns, insns_sz);
760 	memcpy(blob, gopts.data, data_sz);
761 	libbpf_sha256(insns, insns_sz, excl);
762 
763 	if (!ASSERT_OK(sign_buf(dir, insns, insns_sz, sig, &sig_sz), "sign-file"))
764 		goto cleanup;
765 
766 	memset(ctx, 0, ctx_sz);
767 	((struct bpf_loader_ctx *)ctx)->sz = ctx_sz;
768 	r = run_gen_loader(insns, insns_sz, blob, data_sz, excl, sizeof(excl),
769 			   sig, sig_sz, true, ctx, ctx_sz, &ran);
770 	ASSERT_TRUE(ran, "valid signature: loader loaded and ran");
771 	ASSERT_EQ(r, 0, "valid signature accepted");
772 	close_loader_ctx_fds(ctx, nr_maps, nr_progs);
773 
774 	memcpy(tampered, insns, insns_sz);
775 	tampered[insns_sz / 2] ^= 0xff;
776 	memset(ctx, 0, ctx_sz);
777 	((struct bpf_loader_ctx *)ctx)->sz = ctx_sz;
778 	r = run_gen_loader(tampered, insns_sz, blob, data_sz, excl, sizeof(excl),
779 			   sig, sig_sz, true, ctx, ctx_sz, &ran);
780 	ASSERT_FALSE(ran, "tampered loader rejected before run");
781 	ASSERT_EQ(r, -EKEYREJECTED, "signature is bound to the instructions");
782 cleanup:
783 	free(insns);
784 	free(tampered);
785 	free(blob);
786 	free(ctx);
787 	test_signed_loader__destroy(skel);
788 	run_setup("cleanup", dir);
789 }
790 
791 static int make_excl_map(__u32 flags, __u32 value_size)
792 {
793 	LIBBPF_OPTS(bpf_map_create_opts, opts);
794 	__u8 hash[SHA256_DIGEST_LENGTH] = { 1 };	/* any 32-byte value */
795 
796 	opts.excl_prog_hash = hash;
797 	opts.excl_prog_hash_size = sizeof(hash);
798 	opts.map_flags = flags;
799 	return bpf_map_create(BPF_MAP_TYPE_ARRAY, "md", 4, value_size, 1, &opts);
800 }
801 
802 static void hash_requires_frozen(void)
803 {
804 	__u8 hbuf[SHA256_DIGEST_LENGTH], val[64] = {};
805 	struct bpf_map_info info;
806 	__u32 ilen, key = 0;
807 	int fd;
808 
809 	fd = make_excl_map(0, sizeof(val));
810 	if (!ASSERT_OK_FD(fd, "excl_map"))
811 		return;
812 	ASSERT_OK(bpf_map_update_elem(fd, &key, val, 0), "update");
813 
814 	memset(&info, 0, sizeof(info));
815 	info.hash = ptr_to_u64(hbuf);
816 	info.hash_size = sizeof(hbuf);
817 	ilen = sizeof(info);
818 	ASSERT_EQ(bpf_map_get_info_by_fd(fd, &info, &ilen), -EPERM,
819 		  "hash of unfrozen map rejected");
820 	close(fd);
821 }
822 
823 static void no_update_after_freeze(void)
824 {
825 	__u8 val[64] = {};
826 	__u32 key = 0;
827 	int fd;
828 
829 	fd = make_excl_map(0, sizeof(val));
830 	if (!ASSERT_OK_FD(fd, "excl_map"))
831 		return;
832 	ASSERT_OK(bpf_map_update_elem(fd, &key, val, 0), "update");
833 	ASSERT_OK(bpf_map_freeze(fd), "freeze");
834 	ASSERT_EQ(bpf_map_update_elem(fd, &key, val, 0), -EPERM,
835 		  "update after freeze rejected");
836 	close(fd);
837 }
838 
839 static void freeze_writable_mmap(void)
840 {
841 	void *w;
842 	int fd;
843 
844 	fd = make_excl_map(BPF_F_MMAPABLE, 4096);
845 	if (!ASSERT_OK_FD(fd, "excl_mmapable_map"))
846 		return;
847 	w = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
848 	if (ASSERT_OK_PTR(w, "writable_mmap")) {
849 		ASSERT_EQ(bpf_map_freeze(fd), -EBUSY,
850 			  "freeze rejected while writable mmap held");
851 		munmap(w, 4096);
852 	}
853 	close(fd);
854 }
855 
856 static void no_writable_mmap_frozen(void)
857 {
858 	void *w;
859 	int fd;
860 
861 	fd = make_excl_map(BPF_F_MMAPABLE, 4096);
862 	if (!ASSERT_OK_FD(fd, "excl_mmapable_map"))
863 		return;
864 	ASSERT_OK(bpf_map_freeze(fd), "freeze");
865 	w = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
866 	ASSERT_EQ(w, MAP_FAILED, "writable mmap of frozen map rejected");
867 	if (w != MAP_FAILED)
868 		munmap(w, 4096);
869 	close(fd);
870 }
871 
872 static void map_hash_matches_libbpf(void)
873 {
874 	__u8 kbuf[SHA256_DIGEST_LENGTH], lbuf[SHA256_DIGEST_LENGTH], val[64] = {};
875 	struct bpf_map_info info;
876 	__u32 ilen, key = 0;
877 	int fd, i;
878 
879 	/*
880 	 * The signing scheme assumes the kernel's map hash equals what libbpf
881 	 * computes over the same bytes (gen_loader bakes libbpf_sha256(blob);
882 	 * the kernel recomputes via array_map_get_hash). Pin that they agree.
883 	 */
884 	for (i = 0; i < (int)sizeof(val); i++)
885 		val[i] = i * 7 + 1;
886 	fd = bpf_map_create(BPF_MAP_TYPE_ARRAY, "h", 4, sizeof(val), 1, NULL);
887 	if (!ASSERT_OK_FD(fd, "array_map"))
888 		return;
889 	ASSERT_OK(bpf_map_update_elem(fd, &key, val, 0), "update");
890 	ASSERT_OK(bpf_map_freeze(fd), "freeze");
891 	memset(&info, 0, sizeof(info));
892 	info.hash = ptr_to_u64(kbuf);
893 	info.hash_size = sizeof(kbuf);
894 	ilen = sizeof(info);
895 	if (ASSERT_OK(bpf_map_get_info_by_fd(fd, &info, &ilen), "get_hash")) {
896 		libbpf_sha256(val, sizeof(val), lbuf);
897 		ASSERT_EQ(memcmp(kbuf, lbuf, sizeof(kbuf)), 0,
898 			  "kernel map hash matches libbpf_sha256");
899 	}
900 	close(fd);
901 }
902 
903 static void map_hash_multi_element(void)
904 {
905 	const __u32 nr = 8, value_size = 64;
906 	__u8 kbuf[SHA256_DIGEST_LENGTH], lbuf[SHA256_DIGEST_LENGTH];
907 	struct bpf_map_info info;
908 	__u32 ilen, i, j;
909 	__u8 *full;
910 	int fd;
911 
912 	/*
913 	 * array_map_get_hash() hashes elem_size * max_entries (the whole value
914 	 * area), not just element 0. With an 8-aligned value_size elem_size has
915 	 * no padding, so pin that a >1-entry array's kernel hash equals
916 	 * libbpf_sha256() over the full, concatenated element contents.
917 	 */
918 	fd = bpf_map_create(BPF_MAP_TYPE_ARRAY, "h", 4, value_size, nr, NULL);
919 	if (!ASSERT_OK_FD(fd, "array_map"))
920 		return;
921 	full = calloc(nr, value_size);
922 	if (!ASSERT_OK_PTR(full, "buf"))
923 		goto close_fd;
924 	for (i = 0; i < nr; i++) {
925 		__u8 *v = full + i * value_size;
926 
927 		for (j = 0; j < value_size; j++)
928 			v[j] = i * 31 + j * 7 + 1;
929 		ASSERT_OK(bpf_map_update_elem(fd, &i, v, 0), "update");
930 	}
931 	ASSERT_OK(bpf_map_freeze(fd), "freeze");
932 	memset(&info, 0, sizeof(info));
933 	info.hash = ptr_to_u64(kbuf);
934 	info.hash_size = sizeof(kbuf);
935 	ilen = sizeof(info);
936 	if (ASSERT_OK(bpf_map_get_info_by_fd(fd, &info, &ilen), "get_hash")) {
937 		libbpf_sha256(full, (size_t)nr * value_size, lbuf);
938 		ASSERT_EQ(memcmp(kbuf, lbuf, sizeof(kbuf)), 0,
939 			  "kernel hash covers full multi-element value area");
940 	}
941 	free(full);
942 close_fd:
943 	close(fd);
944 }
945 
946 static void map_hash_bad_size(void)
947 {
948 	__u8 kbuf[SHA256_DIGEST_LENGTH], val[64] = {};
949 	struct bpf_map_info info;
950 	__u32 ilen, key = 0;
951 	int fd;
952 
953 	fd = bpf_map_create(BPF_MAP_TYPE_ARRAY, "h", 4, sizeof(val), 1, NULL);
954 	if (!ASSERT_OK_FD(fd, "array_map"))
955 		return;
956 	ASSERT_OK(bpf_map_update_elem(fd, &key, val, 0), "update");
957 	ASSERT_OK(bpf_map_freeze(fd), "freeze");
958 	memset(&info, 0, sizeof(info));
959 	info.hash = ptr_to_u64(kbuf);
960 	info.hash_size = sizeof(kbuf) / 2;
961 	ilen = sizeof(info);
962 	ASSERT_EQ(bpf_map_get_info_by_fd(fd, &info, &ilen), -EINVAL,
963 		  "wrong hash_size rejected");
964 	close(fd);
965 }
966 
967 static void map_hash_unsupported_type(void)
968 {
969 	__u8 kbuf[SHA256_DIGEST_LENGTH];
970 	struct bpf_map_info info;
971 	__u32 ilen;
972 	int fd;
973 
974 	/* Only arrays implement map_get_hash; a hash map must be refused. */
975 	fd = bpf_map_create(BPF_MAP_TYPE_HASH, "h", 4, 8, 4, NULL);
976 	if (!ASSERT_OK_FD(fd, "hash_map"))
977 		return;
978 	memset(&info, 0, sizeof(info));
979 	info.hash = ptr_to_u64(kbuf);
980 	info.hash_size = sizeof(kbuf);
981 	ilen = sizeof(info);
982 	ASSERT_EQ(bpf_map_get_info_by_fd(fd, &info, &ilen), -EINVAL,
983 		  "hash unsupported for non-array map");
984 	close(fd);
985 }
986 
987 static int setup_meta_map(const struct gen_loader_fixture *f)
988 {
989 	LIBBPF_OPTS(bpf_map_create_opts, mopts,
990 		    .excl_prog_hash = f->excl,
991 		    .excl_prog_hash_size = sizeof(f->excl));
992 	__u32 key = 0;
993 	int fd;
994 
995 	fd = bpf_map_create(BPF_MAP_TYPE_ARRAY, "__loader.map", 4,
996 			    f->data_sz, 1, &mopts);
997 	if (fd < 0)
998 		return -errno;
999 	if (bpf_map_update_elem(fd, &key, f->blob, 0) || bpf_map_freeze(fd)) {
1000 		close(fd);
1001 		return -errno;
1002 	}
1003 	return fd;
1004 }
1005 
1006 static void lsm_signature_verdict(void)
1007 {
1008 	char dir_tmpl[] = "/tmp/signed_loader_lsmXXXXXX", *dir = NULL;
1009 	struct test_signed_loader_lsm *lsm = NULL;
1010 	int map_fd = -1, prog_fd = -1;
1011 	bool have_fixture = false;
1012 	struct gen_loader_fixture f;
1013 	__u32 sig_sz = 8192;
1014 	__s32 ses_serial;
1015 	__u8 sig[8192];
1016 
1017 	lsm = test_signed_loader_lsm__open_and_load();
1018 	if (!ASSERT_OK_PTR(lsm, "lsm_skel_load"))
1019 		return;
1020 	lsm->bss->monitored_tid = sys_gettid();
1021 	if (!ASSERT_OK(test_signed_loader_lsm__attach(lsm), "lsm_attach"))
1022 		goto out;
1023 
1024 	have_fixture = true;
1025 	if (gen_loader_fixture_init(&f) != 0)
1026 		goto out;
1027 
1028 	map_fd = setup_meta_map(&f);
1029 	if (!ASSERT_OK_FD(map_fd, "meta_map_unsigned"))
1030 		goto out;
1031 	lsm->bss->seen = 0;
1032 	prog_fd = load_loader(f.gopts.insns, f.gopts.insns_sz, map_fd, NULL, 0, 0);
1033 	close(map_fd);
1034 	map_fd = -1;
1035 	if (!ASSERT_OK_FD(prog_fd, "unsigned loader load"))
1036 		goto out;
1037 	close(prog_fd);
1038 	prog_fd = -1;
1039 	if (!ASSERT_NEQ(lsm->bss->seen, 0, "bpf LSM in the active LSM set"))
1040 		goto out;
1041 	ASSERT_EQ(lsm->bss->seen, 1, "unsigned: one observed load");
1042 	ASSERT_EQ(lsm->bss->sig_verdict, BPF_SIG_UNSIGNED, "unsigned verdict");
1043 	ASSERT_EQ(lsm->bss->sig_keyring_type, BPF_SIG_KEYRING_NONE, "unsigned keyring type");
1044 	ASSERT_EQ(lsm->bss->sig_keyring_serial, 0, "unsigned: no keyring serial");
1045 
1046 	syscall(__NR_request_key, "keyring", "_uid.0", NULL,
1047 		KEY_SPEC_SESSION_KEYRING);
1048 	dir = mkdtemp(dir_tmpl);
1049 	if (!ASSERT_OK_PTR(dir, "mkdtemp"))
1050 		goto out;
1051 	if (!ASSERT_OK(run_setup("setup", dir), "verify_sig_setup")) {
1052 		rmdir(dir);
1053 		dir = NULL;
1054 		goto out;
1055 	}
1056 	if (!ASSERT_OK(sign_buf(dir, f.gopts.insns, f.gopts.insns_sz, sig,
1057 				&sig_sz), "sign-file"))
1058 		goto out;
1059 
1060 	map_fd = setup_meta_map(&f);
1061 	if (!ASSERT_OK_FD(map_fd, "meta_map_signed"))
1062 		goto out;
1063 	lsm->bss->seen = 0;
1064 	prog_fd = load_loader(f.gopts.insns, f.gopts.insns_sz, map_fd, sig,
1065 			      sig_sz, KEY_SPEC_SESSION_KEYRING);
1066 	close(map_fd);
1067 	map_fd = -1;
1068 	if (!ASSERT_OK_FD(prog_fd, "signed loader load"))
1069 		goto out;
1070 	close(prog_fd);
1071 	prog_fd = -1;
1072 
1073 	ses_serial = syscall(__NR_keyctl, KEYCTL_GET_KEYRING_ID,
1074 			     KEY_SPEC_SESSION_KEYRING, 0);
1075 	ASSERT_EQ(lsm->bss->seen, 1, "signed: one observed load");
1076 	ASSERT_EQ(lsm->bss->sig_verdict, BPF_SIG_VERIFIED, "signed verdict");
1077 	ASSERT_EQ(lsm->bss->sig_keyring_type, BPF_SIG_KEYRING_USER, "signed keyring type");
1078 	ASSERT_GT(ses_serial, 0, "session keyring serial resolved");
1079 	ASSERT_EQ(lsm->bss->sig_keyring_serial, ses_serial,
1080 		  "signed: validated against session keyring");
1081 out:
1082 	if (map_fd >= 0)
1083 		close(map_fd);
1084 	if (prog_fd >= 0)
1085 		close(prog_fd);
1086 	if (have_fixture)
1087 		gen_loader_fixture_fini(&f);
1088 	if (dir)
1089 		run_setup("cleanup", dir);
1090 	test_signed_loader_lsm__destroy(lsm);
1091 }
1092 
1093 void test_signed_loader(void)
1094 {
1095 	if (test__start_subtest("metadata_check_shape"))
1096 		metadata_check_shape();
1097 	if (test__start_subtest("metadata_match"))
1098 		metadata_match();
1099 	if (test__start_subtest("metadata_sha_mismatch"))
1100 		metadata_sha_mismatch();
1101 	if (test__start_subtest("metadata_not_exclusive"))
1102 		metadata_not_exclusive();
1103 	if (test__start_subtest("metadata_hash_not_computed"))
1104 		metadata_hash_not_computed();
1105 	if (test__start_subtest("signature_enforced"))
1106 		signature_enforced();
1107 	if (test__start_subtest("signature_too_large"))
1108 		signature_too_large();
1109 	if (test__start_subtest("signature_bad_keyring"))
1110 		signature_bad_keyring();
1111 	if (test__start_subtest("metadata_ctx_max_entries_ignored"))
1112 		metadata_ctx_max_entries_ignored();
1113 	if (test__start_subtest("metadata_ctx_initial_value_ignored"))
1114 		metadata_ctx_initial_value_ignored();
1115 	if (test__start_subtest("signature_authenticates_insns"))
1116 		signature_authenticates_insns();
1117 	if (test__start_subtest("hash_requires_frozen"))
1118 		hash_requires_frozen();
1119 	if (test__start_subtest("no_update_after_freeze"))
1120 		no_update_after_freeze();
1121 	if (test__start_subtest("freeze_writable_mmap"))
1122 		freeze_writable_mmap();
1123 	if (test__start_subtest("no_writable_mmap_frozen"))
1124 		no_writable_mmap_frozen();
1125 	if (test__start_subtest("map_hash_matches_libbpf"))
1126 		map_hash_matches_libbpf();
1127 	if (test__start_subtest("map_hash_multi_element"))
1128 		map_hash_multi_element();
1129 	if (test__start_subtest("map_hash_bad_size"))
1130 		map_hash_bad_size();
1131 	if (test__start_subtest("map_hash_unsupported_type"))
1132 		map_hash_unsupported_type();
1133 	if (test__start_subtest("lsm_signature_verdict"))
1134 		lsm_signature_verdict();
1135 }
1136