xref: /linux/tools/testing/selftests/bpf/prog_tests/signed_loader.c (revision 5a8cd539ac19f7a68e68e1d25ef9ca2ff55b8500)
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 
load_loader(const void * insns,__u32 insns_sz,int map_fd,const void * sig,__u32 sig_sz,__s32 keyring_id,__u32 fd_array_cnt)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 
run_gen_loader(const void * insns,__u32 insns_sz,const void * data,__u32 data_sz,const void * excl,__u32 excl_sz,const void * sig,__u32 sig_sz,void * ctx,__u32 ctx_sz,bool * loader_ran)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 
close_loader_ctx_fds(void * ctx,int nr_maps,int nr_progs)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 
run_setup(const char * cmd,const char * dir)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 
sign_buf(const char * dir,const void * buf,__u32 len,void * sig,__u32 * sig_sz)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 
gen_loader_fixture_init(struct gen_loader_fixture * f)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 
gen_loader_fixture_fini(struct gen_loader_fixture * f)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 
metadata_match(void)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 
signature_enforced(void)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 
signed_nonexcl_fd_array_rejected(void)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 
signed_unfrozen_fd_array_rejected(void)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 
signed_nonarray_fd_array_rejected(void)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 
signed_btf_fd_array_rejected(void)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 
signature_failure_logs(void)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 
signature_too_large(void)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 
signature_zero_size(void)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 
signature_bad_keyring(void)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 
metadata_ctx_max_entries_ignored(void)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 
metadata_ctx_initial_value_ignored(void)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  */
signature_authenticates_insns(void)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 
signature_authenticates_metadata(void)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 
make_excl_map(__u32 flags,__u32 value_size)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 
hash_requires_frozen(void)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 
no_update_after_freeze(void)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 
freeze_writable_mmap(void)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 
no_writable_mmap_frozen(void)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 
map_hash_matches_libbpf(void)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 
map_hash_multi_element(void)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 
map_hash_bad_size(void)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 
map_hash_unsupported_type(void)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 
setup_meta_map(const struct gen_loader_fixture * f)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 
lsm_signature_verdict(void)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  */
loadtime_drive(const char * dir,const void * insns,__u32 insns_sz,const void * data,__u32 data_sz,const __u8 * excl,void * ctx,__u32 ctx_sz,int * load_ret,bool * ran)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 
loadtime_verify(struct bpf_object * obj,int expect_maps)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 
loadtime_no_map(void)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 
loadtime_with_map(void)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  */
signed_no_fd_array(void)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  */
signed_map_by_fd_rejected(void)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  */
signed_sparse_fd_array_rejected(void)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 
signed_module_kfunc_rejected(void)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 
test_signed_loader(void)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