xref: /linux/tools/testing/selftests/bpf/prog_tests/signed_loader.c (revision 5a8cd539ac19f7a68e68e1d25ef9ca2ff55b8500)
15b88319eSDaniel Borkmann // SPDX-License-Identifier: GPL-2.0
25b88319eSDaniel Borkmann /* Copyright (c) 2026 Isovalent */
35b88319eSDaniel Borkmann 
45b88319eSDaniel Borkmann #include <test_progs.h>
55b88319eSDaniel Borkmann #include <sys/syscall.h>
65b88319eSDaniel Borkmann #include <sys/mman.h>
75b88319eSDaniel Borkmann #include <sys/wait.h>
85b88319eSDaniel Borkmann #include <sys/stat.h>
95b88319eSDaniel Borkmann #include <fcntl.h>
105b88319eSDaniel Borkmann #include <limits.h>
115b88319eSDaniel Borkmann #include <linux/keyctl.h>
125b88319eSDaniel Borkmann #include <linux/bpf.h>
135b88319eSDaniel Borkmann 
1499b321ddSDaniel Borkmann #include <bpf/btf.h>
1599b321ddSDaniel Borkmann 
165b88319eSDaniel Borkmann #include "bpf/libbpf_internal.h" /* for libbpf_sha256() */
175b88319eSDaniel Borkmann #include "bpf/skel_internal.h"	 /* for loader ctx layout (bpf_loader_ctx etc) */
185b88319eSDaniel Borkmann 
195b88319eSDaniel Borkmann #include "test_signed_loader.skel.h"
205b88319eSDaniel Borkmann #include "test_signed_loader_map.skel.h"
215b88319eSDaniel Borkmann #include "test_signed_loader_data.skel.h"
228ddce416SDaniel Borkmann #include "test_signed_loader_lsm.skel.h"
235b88319eSDaniel Borkmann 
248ddce416SDaniel Borkmann enum {
258ddce416SDaniel Borkmann 	BPF_SIG_UNSIGNED = 0,
268ddce416SDaniel Borkmann 	BPF_SIG_VERIFIED,
278ddce416SDaniel Borkmann };
288ddce416SDaniel Borkmann 
298ddce416SDaniel Borkmann enum {
308ddce416SDaniel Borkmann 	BPF_SIG_KEYRING_NONE = 0,
318ddce416SDaniel Borkmann 	BPF_SIG_KEYRING_BUILTIN,
328ddce416SDaniel Borkmann 	BPF_SIG_KEYRING_SECONDARY,
338ddce416SDaniel Borkmann 	BPF_SIG_KEYRING_PLATFORM,
348ddce416SDaniel Borkmann 	BPF_SIG_KEYRING_USER,
358ddce416SDaniel Borkmann };
368ddce416SDaniel Borkmann 
load_loader(const void * insns,__u32 insns_sz,int map_fd,const void * sig,__u32 sig_sz,__s32 keyring_id,__u32 fd_array_cnt)375b88319eSDaniel Borkmann static int load_loader(const void *insns, __u32 insns_sz, int map_fd,
3899b321ddSDaniel Borkmann 		       const void *sig, __u32 sig_sz, __s32 keyring_id,
3999b321ddSDaniel Borkmann 		       __u32 fd_array_cnt)
405b88319eSDaniel Borkmann {
415b88319eSDaniel Borkmann 	union bpf_attr attr;
425b88319eSDaniel Borkmann 	int fd;
435b88319eSDaniel Borkmann 
445b88319eSDaniel Borkmann 	memset(&attr, 0, sizeof(attr));
455b88319eSDaniel Borkmann 	attr.prog_type = BPF_PROG_TYPE_SYSCALL;
465b88319eSDaniel Borkmann 	attr.insns = ptr_to_u64(insns);
475b88319eSDaniel Borkmann 	attr.insn_cnt = insns_sz / sizeof(struct bpf_insn);
485b88319eSDaniel Borkmann 	attr.license = ptr_to_u64("Dual BSD/GPL");
495b88319eSDaniel Borkmann 	attr.prog_flags = BPF_F_SLEEPABLE;
505b88319eSDaniel Borkmann 	attr.fd_array = ptr_to_u64(&map_fd);
515b88319eSDaniel Borkmann 	if (sig) {
525b88319eSDaniel Borkmann 		attr.signature = ptr_to_u64(sig);
535b88319eSDaniel Borkmann 		attr.signature_size = sig_sz;
545b88319eSDaniel Borkmann 		attr.keyring_id = keyring_id;
555b88319eSDaniel Borkmann 	}
5699b321ddSDaniel Borkmann 	attr.fd_array_cnt = fd_array_cnt;
575b88319eSDaniel Borkmann 	memcpy(attr.prog_name, "__loader.prog", sizeof("__loader.prog"));
585b88319eSDaniel Borkmann 	fd = syscall(__NR_bpf, BPF_PROG_LOAD, &attr,
595b88319eSDaniel Borkmann 		     offsetofend(union bpf_attr, keyring_id));
605b88319eSDaniel Borkmann 	return fd < 0 ? -errno : fd;
615b88319eSDaniel Borkmann }
625b88319eSDaniel Borkmann 
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)635b88319eSDaniel Borkmann static int run_gen_loader(const void *insns, __u32 insns_sz,
645b88319eSDaniel Borkmann 			  const void *data, __u32 data_sz,
655b88319eSDaniel Borkmann 			  const void *excl, __u32 excl_sz,
665b88319eSDaniel Borkmann 			  const void *sig, __u32 sig_sz,
6799b321ddSDaniel Borkmann 			  void *ctx, __u32 ctx_sz, bool *loader_ran)
685b88319eSDaniel Borkmann {
695b88319eSDaniel Borkmann 	LIBBPF_OPTS(bpf_map_create_opts, mopts,
705b88319eSDaniel Borkmann 		    .excl_prog_hash = excl,
715b88319eSDaniel Borkmann 		    .excl_prog_hash_size = excl_sz);
7299b321ddSDaniel Borkmann 	__u32 key = 0;
735b88319eSDaniel Borkmann 	union bpf_attr attr;
745b88319eSDaniel Borkmann 	int map_fd, prog_fd, ret;
755b88319eSDaniel Borkmann 
765b88319eSDaniel Borkmann 	*loader_ran = false;
775b88319eSDaniel Borkmann 
785b88319eSDaniel Borkmann 	map_fd = bpf_map_create(BPF_MAP_TYPE_ARRAY, "__loader.map",
795b88319eSDaniel Borkmann 				4, data_sz, 1, &mopts);
805b88319eSDaniel Borkmann 	if (map_fd < 0)
815b88319eSDaniel Borkmann 		return -errno;
825b88319eSDaniel Borkmann 	if (bpf_map_update_elem(map_fd, &key, data, 0)) {
835b88319eSDaniel Borkmann 		ret = -errno;
845b88319eSDaniel Borkmann 		goto out_map;
855b88319eSDaniel Borkmann 	}
865b88319eSDaniel Borkmann 	if (bpf_map_freeze(map_fd)) {
875b88319eSDaniel Borkmann 		ret = -errno;
885b88319eSDaniel Borkmann 		goto out_map;
895b88319eSDaniel Borkmann 	}
905b88319eSDaniel Borkmann 
915b88319eSDaniel Borkmann 	memset(&attr, 0, sizeof(attr));
925b88319eSDaniel Borkmann 	attr.prog_type = BPF_PROG_TYPE_SYSCALL;
935b88319eSDaniel Borkmann 	attr.insns = ptr_to_u64(insns);
945b88319eSDaniel Borkmann 	attr.insn_cnt = insns_sz / sizeof(struct bpf_insn);
955b88319eSDaniel Borkmann 	attr.license = ptr_to_u64("Dual BSD/GPL");
965b88319eSDaniel Borkmann 	attr.prog_flags = BPF_F_SLEEPABLE;
975b88319eSDaniel Borkmann 	attr.fd_array = ptr_to_u64(&map_fd);
985b88319eSDaniel Borkmann 	if (sig) {
995b88319eSDaniel Borkmann 		attr.signature = ptr_to_u64(sig);
1005b88319eSDaniel Borkmann 		attr.signature_size = sig_sz;
1015b88319eSDaniel Borkmann 		attr.keyring_id = KEY_SPEC_SESSION_KEYRING;
10299b321ddSDaniel Borkmann 		attr.fd_array_cnt = 1;
1035b88319eSDaniel Borkmann 	}
1045b88319eSDaniel Borkmann 	memcpy(attr.prog_name, "__loader.prog", sizeof("__loader.prog"));
1055b88319eSDaniel Borkmann 	prog_fd = syscall(__NR_bpf, BPF_PROG_LOAD, &attr,
1065b88319eSDaniel Borkmann 			  offsetofend(union bpf_attr, keyring_id));
1075b88319eSDaniel Borkmann 	if (prog_fd < 0) {
1085b88319eSDaniel Borkmann 		ret = -errno;
1095b88319eSDaniel Borkmann 		goto out_map;
1105b88319eSDaniel Borkmann 	}
1115b88319eSDaniel Borkmann 
1125b88319eSDaniel Borkmann 	memset(&attr, 0, sizeof(attr));
1135b88319eSDaniel Borkmann 	attr.test.prog_fd = prog_fd;
1145b88319eSDaniel Borkmann 	attr.test.ctx_in = ptr_to_u64(ctx);
1155b88319eSDaniel Borkmann 	attr.test.ctx_size_in = ctx_sz;
1165b88319eSDaniel Borkmann 	if (syscall(__NR_bpf, BPF_PROG_RUN, &attr,
1175b88319eSDaniel Borkmann 		    offsetofend(union bpf_attr, test)) < 0) {
1185b88319eSDaniel Borkmann 		ret = -errno;
1195b88319eSDaniel Borkmann 		goto out_prog;
1205b88319eSDaniel Borkmann 	}
1215b88319eSDaniel Borkmann 	*loader_ran = true;
1225b88319eSDaniel Borkmann 	ret = (int)attr.test.retval;
1235b88319eSDaniel Borkmann out_prog:
1245b88319eSDaniel Borkmann 	close(prog_fd);
1255b88319eSDaniel Borkmann out_map:
1265b88319eSDaniel Borkmann 	close(map_fd);
1275b88319eSDaniel Borkmann 	return ret;
1285b88319eSDaniel Borkmann }
1295b88319eSDaniel Borkmann 
close_loader_ctx_fds(void * ctx,int nr_maps,int nr_progs)1305b88319eSDaniel Borkmann static void close_loader_ctx_fds(void *ctx, int nr_maps, int nr_progs)
1315b88319eSDaniel Borkmann {
1325b88319eSDaniel Borkmann 	struct bpf_map_desc *md = (struct bpf_map_desc *)((char *)ctx +
1335b88319eSDaniel Borkmann 				  sizeof(struct bpf_loader_ctx));
1345b88319eSDaniel Borkmann 	struct bpf_prog_desc *pd = (struct bpf_prog_desc *)(md + nr_maps);
1355b88319eSDaniel Borkmann 	int i;
1365b88319eSDaniel Borkmann 
1375b88319eSDaniel Borkmann 	for (i = 0; i < nr_maps; i++)
1385b88319eSDaniel Borkmann 		if (md[i].map_fd > 0)
1395b88319eSDaniel Borkmann 			close(md[i].map_fd);
1405b88319eSDaniel Borkmann 	for (i = 0; i < nr_progs; i++)
1415b88319eSDaniel Borkmann 		if (pd[i].prog_fd > 0)
1425b88319eSDaniel Borkmann 			close(pd[i].prog_fd);
1435b88319eSDaniel Borkmann }
1445b88319eSDaniel Borkmann 
run_setup(const char * cmd,const char * dir)1455b88319eSDaniel Borkmann static int run_setup(const char *cmd, const char *dir)
1465b88319eSDaniel Borkmann {
1475b88319eSDaniel Borkmann 	int pid, status;
1485b88319eSDaniel Borkmann 
1495b88319eSDaniel Borkmann 	pid = fork();
1505b88319eSDaniel Borkmann 	if (pid < 0)
1515b88319eSDaniel Borkmann 		return -errno;
1525b88319eSDaniel Borkmann 	if (pid == 0) {
1535b88319eSDaniel Borkmann 		execlp("./verify_sig_setup.sh", "./verify_sig_setup.sh",
1545b88319eSDaniel Borkmann 		       cmd, dir, NULL);
1555b88319eSDaniel Borkmann 		exit(1);
1565b88319eSDaniel Borkmann 	}
1575b88319eSDaniel Borkmann 	if (waitpid(pid, &status, 0) < 0)
1585b88319eSDaniel Borkmann 		return -errno;
1595b88319eSDaniel Borkmann 	return (WIFEXITED(status) &&
1605b88319eSDaniel Borkmann 		WEXITSTATUS(status) == 0) ? 0 : -EINVAL;
1615b88319eSDaniel Borkmann }
1625b88319eSDaniel Borkmann 
sign_buf(const char * dir,const void * buf,__u32 len,void * sig,__u32 * sig_sz)1635b88319eSDaniel Borkmann static int sign_buf(const char *dir, const void *buf, __u32 len,
1645b88319eSDaniel Borkmann 		    void *sig, __u32 *sig_sz)
1655b88319eSDaniel Borkmann {
1665b88319eSDaniel Borkmann 	char data_tmpl[PATH_MAX], key[PATH_MAX];
1675b88319eSDaniel Borkmann 	char sigpath[PATH_MAX + sizeof(".p7s")];
1685b88319eSDaniel Borkmann 	int fd, pid, status, ret;
1695b88319eSDaniel Borkmann 	struct stat st;
1705b88319eSDaniel Borkmann 
1715b88319eSDaniel Borkmann 	ret = snprintf(data_tmpl, sizeof(data_tmpl), "%s/dataXXXXXX", dir);
1725b88319eSDaniel Borkmann 	if (ret < 0 || ret >= (int)sizeof(data_tmpl))
1735b88319eSDaniel Borkmann 		return -ENAMETOOLONG;
1745b88319eSDaniel Borkmann 	ret = 0;
1755b88319eSDaniel Borkmann 
1765b88319eSDaniel Borkmann 	fd = mkstemp(data_tmpl);
1775b88319eSDaniel Borkmann 	if (fd < 0)
1785b88319eSDaniel Borkmann 		return -errno;
1795b88319eSDaniel Borkmann 	if (write(fd, buf, len) != (ssize_t)len) {
1805b88319eSDaniel Borkmann 		close(fd);
1815b88319eSDaniel Borkmann 		ret = -EIO;
1825b88319eSDaniel Borkmann 		goto out;
1835b88319eSDaniel Borkmann 	}
1845b88319eSDaniel Borkmann 	close(fd);
1855b88319eSDaniel Borkmann 
1865b88319eSDaniel Borkmann 	pid = fork();
1875b88319eSDaniel Borkmann 	if (pid < 0) {
1885b88319eSDaniel Borkmann 		ret = -errno;
1895b88319eSDaniel Borkmann 		goto out;
1905b88319eSDaniel Borkmann 	}
1915b88319eSDaniel Borkmann 	if (pid == 0) {
1925b88319eSDaniel Borkmann 		snprintf(key, sizeof(key), "%s/signing_key.pem", dir);
1935b88319eSDaniel Borkmann 		execlp("./sign-file", "./sign-file", "-d", "sha256",
1945b88319eSDaniel Borkmann 		       key, key, data_tmpl, NULL);
1955b88319eSDaniel Borkmann 		exit(1);
1965b88319eSDaniel Borkmann 	}
1975b88319eSDaniel Borkmann 	if (waitpid(pid, &status, 0) < 0 ||
1985b88319eSDaniel Borkmann 	    !WIFEXITED(status) || WEXITSTATUS(status)) {
1995b88319eSDaniel Borkmann 		ret = -EINVAL;
2005b88319eSDaniel Borkmann 		goto out;
2015b88319eSDaniel Borkmann 	}
2025b88319eSDaniel Borkmann 
2035b88319eSDaniel Borkmann 	snprintf(sigpath, sizeof(sigpath), "%s.p7s", data_tmpl);
2045b88319eSDaniel Borkmann 	if (stat(sigpath, &st) < 0) {
2055b88319eSDaniel Borkmann 		ret = -errno;
2065b88319eSDaniel Borkmann 		goto out;
2075b88319eSDaniel Borkmann 	}
2085b88319eSDaniel Borkmann 	if (st.st_size > (off_t)*sig_sz) {
2095b88319eSDaniel Borkmann 		ret = -E2BIG;
2105b88319eSDaniel Borkmann 		goto out_sig;
2115b88319eSDaniel Borkmann 	}
2125b88319eSDaniel Borkmann 	fd = open(sigpath, O_RDONLY);
2135b88319eSDaniel Borkmann 	if (fd < 0) {
2145b88319eSDaniel Borkmann 		ret = -errno;
2155b88319eSDaniel Borkmann 		goto out_sig;
2165b88319eSDaniel Borkmann 	}
2175b88319eSDaniel Borkmann 	if (read(fd, sig, st.st_size) != st.st_size) {
2185b88319eSDaniel Borkmann 		close(fd);
2195b88319eSDaniel Borkmann 		ret = -EIO;
2205b88319eSDaniel Borkmann 		goto out_sig;
2215b88319eSDaniel Borkmann 	}
2225b88319eSDaniel Borkmann 	close(fd);
2235b88319eSDaniel Borkmann 	*sig_sz = st.st_size;
2245b88319eSDaniel Borkmann out_sig:
2255b88319eSDaniel Borkmann 	unlink(sigpath);
2265b88319eSDaniel Borkmann out:
2275b88319eSDaniel Borkmann 	unlink(data_tmpl);
2285b88319eSDaniel Borkmann 	return ret;
2295b88319eSDaniel Borkmann }
2305b88319eSDaniel Borkmann 
2315b88319eSDaniel Borkmann struct gen_loader_fixture {
2325b88319eSDaniel Borkmann 	struct test_signed_loader *skel;
2335b88319eSDaniel Borkmann 	struct gen_loader_opts gopts;
2345b88319eSDaniel Borkmann 	unsigned char *blob;
2355b88319eSDaniel Borkmann 	void *ctx;
2365b88319eSDaniel Borkmann 	__u32 data_sz;
2375b88319eSDaniel Borkmann 	__u32 ctx_sz;
2385b88319eSDaniel Borkmann 	int nr_maps;
2395b88319eSDaniel Borkmann 	int nr_progs;
2405b88319eSDaniel Borkmann 	__u8 excl[SHA256_DIGEST_LENGTH];
2415b88319eSDaniel Borkmann };
2425b88319eSDaniel Borkmann 
gen_loader_fixture_init(struct gen_loader_fixture * f)2435b88319eSDaniel Borkmann static int gen_loader_fixture_init(struct gen_loader_fixture *f)
2445b88319eSDaniel Borkmann {
2455b88319eSDaniel Borkmann 	LIBBPF_OPTS(gen_loader_opts, gopts, .gen_hash = true);
2465b88319eSDaniel Borkmann 	int nr_maps = 0, nr_progs = 0;
2475b88319eSDaniel Borkmann 	struct bpf_program *p;
2485b88319eSDaniel Borkmann 	struct bpf_map *m;
2495b88319eSDaniel Borkmann 
2505b88319eSDaniel Borkmann 	memset(f, 0, sizeof(*f));
2515b88319eSDaniel Borkmann 	f->skel = test_signed_loader__open();
2525b88319eSDaniel Borkmann 	if (!ASSERT_OK_PTR(f->skel, "skel_open"))
2535b88319eSDaniel Borkmann 		return -1;
2545b88319eSDaniel Borkmann 	if (!ASSERT_OK(bpf_object__gen_loader(f->skel->obj, &gopts), "gen_loader"))
2555b88319eSDaniel Borkmann 		return -1;
2565b88319eSDaniel Borkmann 	if (!ASSERT_OK(bpf_object__load(f->skel->obj), "gen_load"))
2575b88319eSDaniel Borkmann 		return -1;
2585b88319eSDaniel Borkmann 	f->gopts = gopts;
2595b88319eSDaniel Borkmann 
2605b88319eSDaniel Borkmann 	bpf_object__for_each_program(p, f->skel->obj)
2615b88319eSDaniel Borkmann 		nr_progs++;
2625b88319eSDaniel Borkmann 	bpf_object__for_each_map(m, f->skel->obj)
2635b88319eSDaniel Borkmann 		nr_maps++;
2645b88319eSDaniel Borkmann 	f->nr_maps = nr_maps;
2655b88319eSDaniel Borkmann 	f->nr_progs = nr_progs;
2665b88319eSDaniel Borkmann 	f->ctx_sz = sizeof(struct bpf_loader_ctx) +
2675b88319eSDaniel Borkmann 		    nr_maps * sizeof(struct bpf_map_desc) +
2685b88319eSDaniel Borkmann 		    nr_progs * sizeof(struct bpf_prog_desc);
2695b88319eSDaniel Borkmann 	f->ctx = calloc(1, f->ctx_sz);
2705b88319eSDaniel Borkmann 	if (!ASSERT_OK_PTR(f->ctx, "ctx_alloc"))
2715b88319eSDaniel Borkmann 		return -1;
2725b88319eSDaniel Borkmann 	((struct bpf_loader_ctx *)f->ctx)->sz = f->ctx_sz;
2735b88319eSDaniel Borkmann 
2745b88319eSDaniel Borkmann 	f->data_sz = gopts.data_sz;
2755b88319eSDaniel Borkmann 	f->blob = malloc(f->data_sz);
2765b88319eSDaniel Borkmann 	if (!ASSERT_OK_PTR(f->blob, "blob_alloc"))
2775b88319eSDaniel Borkmann 		return -1;
2785b88319eSDaniel Borkmann 	memcpy(f->blob, gopts.data, f->data_sz);
2795b88319eSDaniel Borkmann 
2805b88319eSDaniel Borkmann 	/* excl_prog_hash = SHA256(loader insns) == the loader's prog->digest. */
2815b88319eSDaniel Borkmann 	libbpf_sha256(gopts.insns, gopts.insns_sz, f->excl);
2825b88319eSDaniel Borkmann 	return 0;
2835b88319eSDaniel Borkmann }
2845b88319eSDaniel Borkmann 
gen_loader_fixture_fini(struct gen_loader_fixture * f)2855b88319eSDaniel Borkmann static void gen_loader_fixture_fini(struct gen_loader_fixture *f)
2865b88319eSDaniel Borkmann {
2875b88319eSDaniel Borkmann 	if (f->ctx)
2885b88319eSDaniel Borkmann 		close_loader_ctx_fds(f->ctx, f->nr_maps, f->nr_progs);
2895b88319eSDaniel Borkmann 	free(f->blob);
2905b88319eSDaniel Borkmann 	free(f->ctx);
2915b88319eSDaniel Borkmann 	test_signed_loader__destroy(f->skel);
2925b88319eSDaniel Borkmann }
2935b88319eSDaniel Borkmann 
metadata_match(void)2945b88319eSDaniel Borkmann static void metadata_match(void)
2955b88319eSDaniel Borkmann {
2965b88319eSDaniel Borkmann 	struct gen_loader_fixture f;
2975b88319eSDaniel Borkmann 	bool ran;
2985b88319eSDaniel Borkmann 	int r;
2995b88319eSDaniel Borkmann 
3005b88319eSDaniel Borkmann 	if (gen_loader_fixture_init(&f) == 0) {
3015b88319eSDaniel Borkmann 		r = run_gen_loader(f.gopts.insns, f.gopts.insns_sz, f.blob,
3025b88319eSDaniel Borkmann 				   f.data_sz, f.excl, sizeof(f.excl), NULL, 0,
30399b321ddSDaniel Borkmann 				   f.ctx, f.ctx_sz, &ran);
3045b88319eSDaniel Borkmann 		ASSERT_TRUE(ran, "loader ran");
3055b88319eSDaniel Borkmann 		ASSERT_EQ(r, 0, "honest loader retval");
3065b88319eSDaniel Borkmann 	}
3075b88319eSDaniel Borkmann 	gen_loader_fixture_fini(&f);
3085b88319eSDaniel Borkmann }
3095b88319eSDaniel Borkmann 
signature_enforced(void)3105b88319eSDaniel Borkmann static void signature_enforced(void)
3115b88319eSDaniel Borkmann {
3125b88319eSDaniel Borkmann 	static const __u8 junk[64] = { 0x30, 0x42, 0x13, 0x37, };
3135b88319eSDaniel Borkmann 	struct gen_loader_fixture f;
3145b88319eSDaniel Borkmann 	int fd;
3155b88319eSDaniel Borkmann 
3165b88319eSDaniel Borkmann 	if (gen_loader_fixture_init(&f) == 0) {
3175b88319eSDaniel Borkmann 		/*
3185b88319eSDaniel Borkmann 		 * A present-but-invalid signature (the cert bytes are not a
3195b88319eSDaniel Borkmann 		 * PKCS#7 signature) must be rejected at load: the signature
3205b88319eSDaniel Borkmann 		 * path is honored, not ignored. (The valid path is covered by
32199b321ddSDaniel Borkmann 		 * the signed lskels.) Pin -EBADMSG, the PKCS#7 parse failure:
32299b321ddSDaniel Borkmann 		 * a looser fd < 0 check could also be satisfied by the sparse
32399b321ddSDaniel Borkmann 		 * fd_array rejection (-EACCES) that the loader's map reference
32499b321ddSDaniel Borkmann 		 * would trip even if the signature were silently ignored.
3255b88319eSDaniel Borkmann 		 */
3265b88319eSDaniel Borkmann 		fd = load_loader(f.gopts.insns, f.gopts.insns_sz, -1, junk,
32799b321ddSDaniel Borkmann 				 sizeof(junk), KEY_SPEC_SESSION_KEYRING, 0);
32899b321ddSDaniel Borkmann 		ASSERT_EQ(fd, -EBADMSG, "invalid signature rejected at load");
329*43f129d2SDaniel Borkmann 		if (fd >= 0)
330*43f129d2SDaniel Borkmann 			close(fd);
33199b321ddSDaniel Borkmann 	}
33299b321ddSDaniel Borkmann 	gen_loader_fixture_fini(&f);
33399b321ddSDaniel Borkmann }
33499b321ddSDaniel Borkmann 
signed_nonexcl_fd_array_rejected(void)33599b321ddSDaniel Borkmann static void signed_nonexcl_fd_array_rejected(void)
33699b321ddSDaniel Borkmann {
33799b321ddSDaniel Borkmann 	static const __u8 junk[64] = { 0x30, 0x42, 0x13, 0x37, };
33899b321ddSDaniel Borkmann 	struct gen_loader_fixture f;
33999b321ddSDaniel Borkmann 	int map_fd, fd;
34099b321ddSDaniel Borkmann 
34199b321ddSDaniel Borkmann 	if (gen_loader_fixture_init(&f) == 0) {
34299b321ddSDaniel Borkmann 		/*
34399b321ddSDaniel Borkmann 		 * A signed program may only bind exclusive maps through fd_array
34499b321ddSDaniel Borkmann 		 * (their contents are folded into the signature). Binding a
34599b321ddSDaniel Borkmann 		 * non-exclusive map is rejected, before the signature is even
34699b321ddSDaniel Borkmann 		 * examined.
34799b321ddSDaniel Borkmann 		 */
34899b321ddSDaniel Borkmann 		map_fd = bpf_map_create(BPF_MAP_TYPE_ARRAY, "nonexcl", 4,
34999b321ddSDaniel Borkmann 					f.data_sz, 1, NULL);
35099b321ddSDaniel Borkmann 		if (ASSERT_OK_FD(map_fd, "nonexcl_map")) {
35199b321ddSDaniel Borkmann 			if (ASSERT_OK(bpf_map_freeze(map_fd), "freeze")) {
35299b321ddSDaniel Borkmann 				fd = load_loader(f.gopts.insns, f.gopts.insns_sz,
35399b321ddSDaniel Borkmann 						 map_fd, junk, sizeof(junk),
35499b321ddSDaniel Borkmann 						 KEY_SPEC_SESSION_KEYRING, 1);
35599b321ddSDaniel Borkmann 				ASSERT_EQ(fd, -EPERM,
35699b321ddSDaniel Borkmann 					  "non-exclusive map in signed fd_array rejected");
35799b321ddSDaniel Borkmann 				if (fd >= 0)
35899b321ddSDaniel Borkmann 					close(fd);
35999b321ddSDaniel Borkmann 			}
36099b321ddSDaniel Borkmann 			close(map_fd);
36199b321ddSDaniel Borkmann 		}
36299b321ddSDaniel Borkmann 	}
36399b321ddSDaniel Borkmann 	gen_loader_fixture_fini(&f);
36499b321ddSDaniel Borkmann }
36599b321ddSDaniel Borkmann 
signed_unfrozen_fd_array_rejected(void)36699b321ddSDaniel Borkmann static void signed_unfrozen_fd_array_rejected(void)
36799b321ddSDaniel Borkmann {
36899b321ddSDaniel Borkmann 	static const __u8 junk[64] = { 0x30, 0x42, 0x13, 0x37, };
36999b321ddSDaniel Borkmann 	LIBBPF_OPTS(bpf_map_create_opts, mopts);
37099b321ddSDaniel Borkmann 	struct gen_loader_fixture f;
37199b321ddSDaniel Borkmann 	__u32 key = 0;
37299b321ddSDaniel Borkmann 	int map_fd, fd;
37399b321ddSDaniel Borkmann 
37499b321ddSDaniel Borkmann 	if (gen_loader_fixture_init(&f) == 0) {
37599b321ddSDaniel Borkmann 		/*
37699b321ddSDaniel Borkmann 		 * The metadata map must be frozen before a signed load so the
37799b321ddSDaniel Borkmann 		 * folded bytes cannot change afterwards. Bind an exclusive map
37899b321ddSDaniel Borkmann 		 * with matching contents but skip the freeze: the load must be
37999b321ddSDaniel Borkmann 		 * rejected by the frozen check with -EPERM. The exclusivity
38099b321ddSDaniel Borkmann 		 * check right after it would pass, so the errno uniquely pins
38199b321ddSDaniel Borkmann 		 * the freeze requirement.
38299b321ddSDaniel Borkmann 		 */
38399b321ddSDaniel Borkmann 		mopts.excl_prog_hash = f.excl;
38499b321ddSDaniel Borkmann 		mopts.excl_prog_hash_size = sizeof(f.excl);
38599b321ddSDaniel Borkmann 		map_fd = bpf_map_create(BPF_MAP_TYPE_ARRAY, "unfrozen", 4,
38699b321ddSDaniel Borkmann 					f.data_sz, 1, &mopts);
38799b321ddSDaniel Borkmann 		if (ASSERT_OK_FD(map_fd, "unfrozen_map")) {
38899b321ddSDaniel Borkmann 			if (ASSERT_OK(bpf_map_update_elem(map_fd, &key, f.blob, 0),
38999b321ddSDaniel Borkmann 				      "update")) {
39099b321ddSDaniel Borkmann 				fd = load_loader(f.gopts.insns, f.gopts.insns_sz,
39199b321ddSDaniel Borkmann 						 map_fd, junk, sizeof(junk),
39299b321ddSDaniel Borkmann 						 KEY_SPEC_SESSION_KEYRING, 1);
39399b321ddSDaniel Borkmann 				ASSERT_EQ(fd, -EPERM,
39499b321ddSDaniel Borkmann 					  "unfrozen map in signed fd_array rejected");
39599b321ddSDaniel Borkmann 				if (fd >= 0)
39699b321ddSDaniel Borkmann 					close(fd);
39799b321ddSDaniel Borkmann 			}
39899b321ddSDaniel Borkmann 			close(map_fd);
39999b321ddSDaniel Borkmann 		}
40099b321ddSDaniel Borkmann 	}
40199b321ddSDaniel Borkmann 	gen_loader_fixture_fini(&f);
40299b321ddSDaniel Borkmann }
40399b321ddSDaniel Borkmann 
signed_nonarray_fd_array_rejected(void)40499b321ddSDaniel Borkmann static void signed_nonarray_fd_array_rejected(void)
40599b321ddSDaniel Borkmann {
40699b321ddSDaniel Borkmann 	static const __u8 junk[64] = { 0x30, 0x42, 0x13, 0x37, };
40799b321ddSDaniel Borkmann 	LIBBPF_OPTS(bpf_map_create_opts, mopts);
40899b321ddSDaniel Borkmann 	struct gen_loader_fixture f;
40999b321ddSDaniel Borkmann 	int map_fd, fd;
41099b321ddSDaniel Borkmann 
41199b321ddSDaniel Borkmann 	if (gen_loader_fixture_init(&f) == 0) {
41299b321ddSDaniel Borkmann 		/*
41399b321ddSDaniel Borkmann 		 * Only a plain BPF_MAP_TYPE_ARRAY may be folded into the
41499b321ddSDaniel Borkmann 		 * signature. An exclusive map of any other type is rejected
41599b321ddSDaniel Borkmann 		 * (-EINVAL) rather than folded - this is the type gate that
41699b321ddSDaniel Borkmann 		 * keeps arena maps (map_direct_value_addr() returns a user
41799b321ddSDaniel Borkmann 		 * address) and insn-array maps (buffer smaller than value_size)
41899b321ddSDaniel Borkmann 		 * out of the hashed region, where the old code would have
41999b321ddSDaniel Borkmann 		 * memcpy()'d from them. A hash map stands in here: it is
42099b321ddSDaniel Borkmann 		 * exclusive (bound to the loader digest) but not an array.
42199b321ddSDaniel Borkmann 		 */
42299b321ddSDaniel Borkmann 		mopts.excl_prog_hash = f.excl;
42399b321ddSDaniel Borkmann 		mopts.excl_prog_hash_size = sizeof(f.excl);
42499b321ddSDaniel Borkmann 		map_fd = bpf_map_create(BPF_MAP_TYPE_HASH, "excl_hash", 4, 4, 1,
42599b321ddSDaniel Borkmann 					&mopts);
42699b321ddSDaniel Borkmann 		if (ASSERT_OK_FD(map_fd, "excl_hash_map")) {
42799b321ddSDaniel Borkmann 			fd = load_loader(f.gopts.insns, f.gopts.insns_sz, map_fd,
42899b321ddSDaniel Borkmann 					 junk, sizeof(junk),
42999b321ddSDaniel Borkmann 					 KEY_SPEC_SESSION_KEYRING, 1);
43099b321ddSDaniel Borkmann 			ASSERT_EQ(fd, -EINVAL,
43199b321ddSDaniel Borkmann 				  "non-array map in signed fd_array rejected");
43299b321ddSDaniel Borkmann 			if (fd >= 0)
43399b321ddSDaniel Borkmann 				close(fd);
43499b321ddSDaniel Borkmann 			close(map_fd);
43599b321ddSDaniel Borkmann 		}
43699b321ddSDaniel Borkmann 	}
43799b321ddSDaniel Borkmann 	gen_loader_fixture_fini(&f);
43899b321ddSDaniel Borkmann }
43999b321ddSDaniel Borkmann 
44099b321ddSDaniel Borkmann static int setup_meta_map(const struct gen_loader_fixture *f);
44199b321ddSDaniel Borkmann 
signed_btf_fd_array_rejected(void)44299b321ddSDaniel Borkmann static void signed_btf_fd_array_rejected(void)
44399b321ddSDaniel Borkmann {
44499b321ddSDaniel Borkmann 	char dir_tmpl[] = "/tmp/signed_loader_btfXXXXXX", *dir = NULL;
44599b321ddSDaniel Borkmann 	__u32 sig_sz = 8192;
44699b321ddSDaniel Borkmann 	int map_fd = -1, prog_fd = -1;
44799b321ddSDaniel Borkmann 	unsigned char *buf = NULL;
44899b321ddSDaniel Borkmann 	struct gen_loader_fixture f;
44999b321ddSDaniel Borkmann 	bool have_fixture = false;
45099b321ddSDaniel Borkmann 	struct btf *btf = NULL;
45199b321ddSDaniel Borkmann 	union bpf_attr attr;
45299b321ddSDaniel Borkmann 	int fds[2];
45399b321ddSDaniel Borkmann 	__u8 sig[8192];
45499b321ddSDaniel Borkmann 
45599b321ddSDaniel Borkmann 	syscall(__NR_request_key, "keyring", "_uid.0", NULL,
45699b321ddSDaniel Borkmann 		KEY_SPEC_SESSION_KEYRING);
45799b321ddSDaniel Borkmann 	dir = mkdtemp(dir_tmpl);
45899b321ddSDaniel Borkmann 	if (!ASSERT_OK_PTR(dir, "mkdtemp"))
45999b321ddSDaniel Borkmann 		return;
46099b321ddSDaniel Borkmann 	if (!ASSERT_OK(run_setup("setup", dir), "verify_sig_setup")) {
46199b321ddSDaniel Borkmann 		rmdir(dir);
46299b321ddSDaniel Borkmann 		return;
46399b321ddSDaniel Borkmann 	}
46499b321ddSDaniel Borkmann 	have_fixture = true;
46599b321ddSDaniel Borkmann 	if (gen_loader_fixture_init(&f) != 0)
46699b321ddSDaniel Borkmann 		goto out;
46799b321ddSDaniel Borkmann 
46899b321ddSDaniel Borkmann 	/*
46999b321ddSDaniel Borkmann 	 * fd_array binds maps and BTFs alike, but only exclusive array maps are
47099b321ddSDaniel Borkmann 	 * folded into the signature. Build an otherwise genuinely signed load -
47199b321ddSDaniel Borkmann 	 * insns || metadata, exclusive frozen map at fd_array[0] - then smuggle
47299b321ddSDaniel Borkmann 	 * an extra BTF into fd_array[1]. A signed program may not bind any BTF,
47399b321ddSDaniel Borkmann 	 * so resolving the fd_array entries rejects the BTF with -EACCES (in
47499b321ddSDaniel Borkmann 	 * __add_used_btf(), before the signature is even verified).
47599b321ddSDaniel Borkmann 	 */
47699b321ddSDaniel Borkmann 	buf = malloc((size_t)f.gopts.insns_sz + f.data_sz);
47799b321ddSDaniel Borkmann 	if (!ASSERT_OK_PTR(buf, "signbuf"))
47899b321ddSDaniel Borkmann 		goto out;
47999b321ddSDaniel Borkmann 	memcpy(buf, f.gopts.insns, f.gopts.insns_sz);
48099b321ddSDaniel Borkmann 	memcpy(buf + f.gopts.insns_sz, f.blob, f.data_sz);
48199b321ddSDaniel Borkmann 	if (!ASSERT_OK(sign_buf(dir, buf, f.gopts.insns_sz + f.data_sz, sig,
48299b321ddSDaniel Borkmann 			       &sig_sz), "sign insns||metadata"))
48399b321ddSDaniel Borkmann 		goto out;
48499b321ddSDaniel Borkmann 
48599b321ddSDaniel Borkmann 	map_fd = setup_meta_map(&f);
48699b321ddSDaniel Borkmann 	if (!ASSERT_OK_FD(map_fd, "meta_map"))
48799b321ddSDaniel Borkmann 		goto out;
48899b321ddSDaniel Borkmann 	btf = btf__new_empty();
48999b321ddSDaniel Borkmann 	if (!ASSERT_OK_PTR(btf, "btf_new_empty"))
49099b321ddSDaniel Borkmann 		goto out;
49199b321ddSDaniel Borkmann 	btf__add_int(btf, "int", 4, BTF_INT_SIGNED);
49299b321ddSDaniel Borkmann 	if (!ASSERT_OK(btf__load_into_kernel(btf), "btf_load"))
49399b321ddSDaniel Borkmann 		goto out;
49499b321ddSDaniel Borkmann 
49599b321ddSDaniel Borkmann 	fds[0] = map_fd;
49699b321ddSDaniel Borkmann 	fds[1] = btf__fd(btf);
49799b321ddSDaniel Borkmann 	memset(&attr, 0, sizeof(attr));
49899b321ddSDaniel Borkmann 	attr.prog_type = BPF_PROG_TYPE_SYSCALL;
49999b321ddSDaniel Borkmann 	attr.insns = ptr_to_u64(f.gopts.insns);
50099b321ddSDaniel Borkmann 	attr.insn_cnt = f.gopts.insns_sz / sizeof(struct bpf_insn);
50199b321ddSDaniel Borkmann 	attr.license = ptr_to_u64("Dual BSD/GPL");
50299b321ddSDaniel Borkmann 	attr.prog_flags = BPF_F_SLEEPABLE;
50399b321ddSDaniel Borkmann 	attr.fd_array = ptr_to_u64(fds);
50499b321ddSDaniel Borkmann 	attr.fd_array_cnt = 2;
50599b321ddSDaniel Borkmann 	attr.signature = ptr_to_u64(sig);
50699b321ddSDaniel Borkmann 	attr.signature_size = sig_sz;
50799b321ddSDaniel Borkmann 	attr.keyring_id = KEY_SPEC_SESSION_KEYRING;
50899b321ddSDaniel Borkmann 	memcpy(attr.prog_name, "__loader.prog", sizeof("__loader.prog"));
50999b321ddSDaniel Borkmann 	prog_fd = syscall(__NR_bpf, BPF_PROG_LOAD, &attr,
51099b321ddSDaniel Borkmann 			  offsetofend(union bpf_attr, keyring_id));
51199b321ddSDaniel Borkmann 	ASSERT_EQ(prog_fd < 0 ? -errno : prog_fd, -EACCES,
51299b321ddSDaniel Borkmann 		  "BTF in signed fd_array rejected");
51399b321ddSDaniel Borkmann 	if (prog_fd >= 0)
51499b321ddSDaniel Borkmann 		close(prog_fd);
51599b321ddSDaniel Borkmann out:
51699b321ddSDaniel Borkmann 	if (btf)
51799b321ddSDaniel Borkmann 		btf__free(btf);
51899b321ddSDaniel Borkmann 	if (map_fd >= 0)
51999b321ddSDaniel Borkmann 		close(map_fd);
52099b321ddSDaniel Borkmann 	if (have_fixture)
52199b321ddSDaniel Borkmann 		gen_loader_fixture_fini(&f);
52299b321ddSDaniel Borkmann 	if (dir)
52399b321ddSDaniel Borkmann 		run_setup("cleanup", dir);
52499b321ddSDaniel Borkmann 	free(buf);
52599b321ddSDaniel Borkmann }
52699b321ddSDaniel Borkmann 
signature_failure_logs(void)52799b321ddSDaniel Borkmann static void signature_failure_logs(void)
52899b321ddSDaniel Borkmann {
52999b321ddSDaniel Borkmann 	static const __u8 junk[64] = { 0x30, 0x42, 0x13, 0x37, };
53099b321ddSDaniel Borkmann 	char log_buf[1024] = {};
53199b321ddSDaniel Borkmann 	struct gen_loader_fixture f;
53299b321ddSDaniel Borkmann 	union bpf_attr attr;
53399b321ddSDaniel Borkmann 	int fd;
53499b321ddSDaniel Borkmann 
53599b321ddSDaniel Borkmann 	if (gen_loader_fixture_init(&f) == 0) {
53699b321ddSDaniel Borkmann 		/*
53799b321ddSDaniel Borkmann 		 * Signature verification now runs inside bpf_check(), so a
53899b321ddSDaniel Borkmann 		 * failure is reported through the verifier log. A present-but-
53999b321ddSDaniel Borkmann 		 * invalid signature is rejected and the log says why.
54099b321ddSDaniel Borkmann 		 */
54199b321ddSDaniel Borkmann 		memset(&attr, 0, sizeof(attr));
54299b321ddSDaniel Borkmann 		attr.prog_type = BPF_PROG_TYPE_SYSCALL;
54399b321ddSDaniel Borkmann 		attr.insns = ptr_to_u64(f.gopts.insns);
54499b321ddSDaniel Borkmann 		attr.insn_cnt = f.gopts.insns_sz / sizeof(struct bpf_insn);
54599b321ddSDaniel Borkmann 		attr.license = ptr_to_u64("Dual BSD/GPL");
54699b321ddSDaniel Borkmann 		attr.prog_flags = BPF_F_SLEEPABLE;
54799b321ddSDaniel Borkmann 		attr.signature = ptr_to_u64(junk);
54899b321ddSDaniel Borkmann 		attr.signature_size = sizeof(junk);
54999b321ddSDaniel Borkmann 		attr.keyring_id = KEY_SPEC_SESSION_KEYRING;
55099b321ddSDaniel Borkmann 		attr.log_level = 1;
55199b321ddSDaniel Borkmann 		attr.log_buf = ptr_to_u64(log_buf);
55299b321ddSDaniel Borkmann 		attr.log_size = sizeof(log_buf);
55399b321ddSDaniel Borkmann 		memcpy(attr.prog_name, "__loader.prog", sizeof("__loader.prog"));
55499b321ddSDaniel Borkmann 
55599b321ddSDaniel Borkmann 		fd = syscall(__NR_bpf, BPF_PROG_LOAD, &attr,
55699b321ddSDaniel Borkmann 			     offsetofend(union bpf_attr, keyring_id));
5575b88319eSDaniel Borkmann 		ASSERT_LT(fd, 0, "invalid signature rejected at load");
55899b321ddSDaniel Borkmann 		if (fd >= 0)
55999b321ddSDaniel Borkmann 			close(fd);
56099b321ddSDaniel Borkmann 		ASSERT_HAS_SUBSTR(log_buf, "signature verification failed",
56199b321ddSDaniel Borkmann 				  "verifier logs signature failure");
5625b88319eSDaniel Borkmann 	}
5635b88319eSDaniel Borkmann 	gen_loader_fixture_fini(&f);
5645b88319eSDaniel Borkmann }
5655b88319eSDaniel Borkmann 
signature_too_large(void)5665b88319eSDaniel Borkmann static void signature_too_large(void)
5675b88319eSDaniel Borkmann {
5685b88319eSDaniel Borkmann 	static const __u8 junk[64] = {};
5695b88319eSDaniel Borkmann 	struct gen_loader_fixture f;
5705b88319eSDaniel Borkmann 	int fd;
5715b88319eSDaniel Borkmann 
5725b88319eSDaniel Borkmann 	if (gen_loader_fixture_init(&f) == 0) {
5735b88319eSDaniel Borkmann 		/*
5745b88319eSDaniel Borkmann 		 * signature_size beyond the kernel's bound (KMALLOC_MAX_CACHE_SIZE)
5755b88319eSDaniel Borkmann 		 * is rejected before the buffer is read.
5765b88319eSDaniel Borkmann 		 */
5775b88319eSDaniel Borkmann 		fd = load_loader(f.gopts.insns, f.gopts.insns_sz, -1, junk,
57899b321ddSDaniel Borkmann 				 64 << 20, KEY_SPEC_SESSION_KEYRING, 0);
5795b88319eSDaniel Borkmann 		ASSERT_EQ(fd, -EINVAL, "oversized signature rejected");
580*43f129d2SDaniel Borkmann 		if (fd >= 0)
581*43f129d2SDaniel Borkmann 			close(fd);
5825b88319eSDaniel Borkmann 	}
5835b88319eSDaniel Borkmann 	gen_loader_fixture_fini(&f);
5845b88319eSDaniel Borkmann }
5855b88319eSDaniel Borkmann 
signature_zero_size(void)58699b321ddSDaniel Borkmann static void signature_zero_size(void)
58799b321ddSDaniel Borkmann {
58899b321ddSDaniel Borkmann 	static const __u8 junk[64] = {};
58999b321ddSDaniel Borkmann 	struct gen_loader_fixture f;
59099b321ddSDaniel Borkmann 	int fd;
59199b321ddSDaniel Borkmann 
59299b321ddSDaniel Borkmann 	if (gen_loader_fixture_init(&f) == 0) {
59399b321ddSDaniel Borkmann 		/*
59499b321ddSDaniel Borkmann 		 * A present signature with signature_size == 0 is rejected
59599b321ddSDaniel Borkmann 		 * up front, before the keyring is resolved or the signature
59699b321ddSDaniel Borkmann 		 * buffer is read.
59799b321ddSDaniel Borkmann 		 */
59899b321ddSDaniel Borkmann 		fd = load_loader(f.gopts.insns, f.gopts.insns_sz, -1, junk,
59999b321ddSDaniel Borkmann 				 0, KEY_SPEC_SESSION_KEYRING, 0);
60099b321ddSDaniel Borkmann 		ASSERT_EQ(fd, -EINVAL, "zero-size signature rejected");
601*43f129d2SDaniel Borkmann 		if (fd >= 0)
602*43f129d2SDaniel Borkmann 			close(fd);
60399b321ddSDaniel Borkmann 	}
60499b321ddSDaniel Borkmann 	gen_loader_fixture_fini(&f);
60599b321ddSDaniel Borkmann }
60699b321ddSDaniel Borkmann 
signature_bad_keyring(void)6075b88319eSDaniel Borkmann static void signature_bad_keyring(void)
6085b88319eSDaniel Borkmann {
6095b88319eSDaniel Borkmann 	static const __u8 junk[64] = {};
6105b88319eSDaniel Borkmann 	struct gen_loader_fixture f;
6115b88319eSDaniel Borkmann 	int fd;
6125b88319eSDaniel Borkmann 
6135b88319eSDaniel Borkmann 	if (gen_loader_fixture_init(&f) == 0) {
6145b88319eSDaniel Borkmann 		/*
6155b88319eSDaniel Borkmann 		 * A present signature with a keyring_id that resolves to no key is
6165b88319eSDaniel Borkmann 		 * rejected up front: bpf_prog_verify_signature() fails the keyring
6175b88319eSDaniel Borkmann 		 * lookup (-EINVAL) before it ever looks at the signature bytes. A
6185b88319eSDaniel Borkmann 		 * large positive serial takes the user-keyring path and won't exist.
6195b88319eSDaniel Borkmann 		 */
6205b88319eSDaniel Borkmann 		fd = load_loader(f.gopts.insns, f.gopts.insns_sz, -1, junk,
62199b321ddSDaniel Borkmann 				 sizeof(junk), INT_MAX, 0);
6225b88319eSDaniel Borkmann 		ASSERT_EQ(fd, -EINVAL, "signature with bad keyring_id rejected");
623*43f129d2SDaniel Borkmann 		if (fd >= 0)
624*43f129d2SDaniel Borkmann 			close(fd);
6255b88319eSDaniel Borkmann 	}
6265b88319eSDaniel Borkmann 	gen_loader_fixture_fini(&f);
6275b88319eSDaniel Borkmann }
6285b88319eSDaniel Borkmann 
6295b88319eSDaniel Borkmann /*
6305b88319eSDaniel Borkmann  * A signed loader must ignore ctx-supplied map dimensions: the host cannot
6315b88319eSDaniel Borkmann  * resize a signed program's maps via the loader ctx. Drive a one-map program
6325b88319eSDaniel Borkmann  * through gen_loader, ask (via ctx) for every map to be resized to a bogus
6335b88319eSDaniel Borkmann  * value, and confirm the created maps keep their attested size.
6345b88319eSDaniel Borkmann  */
6355b88319eSDaniel Borkmann #define GATING_BOGUS_MAX 0x4000
6365b88319eSDaniel Borkmann 
metadata_ctx_max_entries_ignored(void)6375b88319eSDaniel Borkmann static void metadata_ctx_max_entries_ignored(void)
6385b88319eSDaniel Borkmann {
6395b88319eSDaniel Borkmann 	LIBBPF_OPTS(gen_loader_opts, gopts, .gen_hash = true);
6405b88319eSDaniel Borkmann 	struct test_signed_loader_map *skel;
6415b88319eSDaniel Borkmann 	__u8 excl[SHA256_DIGEST_LENGTH];
6425b88319eSDaniel Borkmann 	int nr_maps = 0, nr_progs = 0, i, checked = 0, r;
6435b88319eSDaniel Borkmann 	struct bpf_program *p;
6445b88319eSDaniel Borkmann 	struct bpf_map *m;
6455b88319eSDaniel Borkmann 	struct bpf_map_desc *md;
6465b88319eSDaniel Borkmann 	unsigned char *blob;
6475b88319eSDaniel Borkmann 	__u32 ctx_sz, data_sz;
6485b88319eSDaniel Borkmann 	void *ctx;
6495b88319eSDaniel Borkmann 	bool ran;
6505b88319eSDaniel Borkmann 
6515b88319eSDaniel Borkmann 	skel = test_signed_loader_map__open();
6525b88319eSDaniel Borkmann 	if (!ASSERT_OK_PTR(skel, "skel_open"))
6535b88319eSDaniel Borkmann 		return;
6545b88319eSDaniel Borkmann 	if (!ASSERT_OK(bpf_object__gen_loader(skel->obj, &gopts), "gen_loader"))
6555b88319eSDaniel Borkmann 		goto destroy;
6565b88319eSDaniel Borkmann 	if (!ASSERT_OK(bpf_object__load(skel->obj), "gen_load"))
6575b88319eSDaniel Borkmann 		goto destroy;
6585b88319eSDaniel Borkmann 
6595b88319eSDaniel Borkmann 	bpf_object__for_each_program(p, skel->obj)
6605b88319eSDaniel Borkmann 		nr_progs++;
6615b88319eSDaniel Borkmann 	bpf_object__for_each_map(m, skel->obj)
6625b88319eSDaniel Borkmann 		nr_maps++;
6635b88319eSDaniel Borkmann 	ctx_sz = sizeof(struct bpf_loader_ctx) +
6645b88319eSDaniel Borkmann 		 nr_maps * sizeof(struct bpf_map_desc) +
6655b88319eSDaniel Borkmann 		 nr_progs * sizeof(struct bpf_prog_desc);
6665b88319eSDaniel Borkmann 	ctx = calloc(1, ctx_sz);
6675b88319eSDaniel Borkmann 	if (!ASSERT_OK_PTR(ctx, "ctx_alloc"))
6685b88319eSDaniel Borkmann 		goto destroy;
6695b88319eSDaniel Borkmann 	((struct bpf_loader_ctx *)ctx)->sz = ctx_sz;
6705b88319eSDaniel Borkmann 
6715b88319eSDaniel Borkmann 	md = (struct bpf_map_desc *)((char *)ctx + sizeof(struct bpf_loader_ctx));
6725b88319eSDaniel Borkmann 	for (i = 0; i < nr_maps; i++)
6735b88319eSDaniel Borkmann 		md[i].max_entries = GATING_BOGUS_MAX;
6745b88319eSDaniel Borkmann 
6755b88319eSDaniel Borkmann 	libbpf_sha256(gopts.insns, gopts.insns_sz, excl);
6765b88319eSDaniel Borkmann 	data_sz = gopts.data_sz;
6775b88319eSDaniel Borkmann 	blob = malloc(data_sz);
6785b88319eSDaniel Borkmann 	if (!ASSERT_OK_PTR(blob, "blob_alloc"))
6795b88319eSDaniel Borkmann 		goto free_ctx;
6805b88319eSDaniel Borkmann 	memcpy(blob, gopts.data, data_sz);
6815b88319eSDaniel Borkmann 
6825b88319eSDaniel Borkmann 	r = run_gen_loader(gopts.insns, gopts.insns_sz, blob, data_sz,
68399b321ddSDaniel Borkmann 			   excl, sizeof(excl), NULL, 0, ctx, ctx_sz, &ran);
6845b88319eSDaniel Borkmann 	if (!ASSERT_TRUE(ran, "loader ran") ||
6855b88319eSDaniel Borkmann 	    !ASSERT_EQ(r, 0, "loader retval"))
6865b88319eSDaniel Borkmann 		goto free_blob;
6875b88319eSDaniel Borkmann 
6885b88319eSDaniel Borkmann 	for (i = 0; i < nr_maps; i++) {
6895b88319eSDaniel Borkmann 		struct bpf_map_info info;
6905b88319eSDaniel Borkmann 		__u32 ilen = sizeof(info);
6915b88319eSDaniel Borkmann 		int fd = md[i].map_fd;
6925b88319eSDaniel Borkmann 
6935b88319eSDaniel Borkmann 		if (fd <= 0)
6945b88319eSDaniel Borkmann 			continue;
6955b88319eSDaniel Borkmann 		memset(&info, 0, sizeof(info));
6965b88319eSDaniel Borkmann 		if (ASSERT_OK(bpf_map_get_info_by_fd(fd, &info, &ilen), "map_info")) {
6975b88319eSDaniel Borkmann 			ASSERT_NEQ(info.max_entries, GATING_BOGUS_MAX,
6985b88319eSDaniel Borkmann 				   "ctx max_entries ignored for signed loader");
6995b88319eSDaniel Borkmann 			checked++;
7005b88319eSDaniel Borkmann 		}
7015b88319eSDaniel Borkmann 	}
7025b88319eSDaniel Borkmann 	ASSERT_GT(checked, 0, "inspected a created map");
7035b88319eSDaniel Borkmann 
7045b88319eSDaniel Borkmann free_blob:
7055b88319eSDaniel Borkmann 	free(blob);
7065b88319eSDaniel Borkmann free_ctx:
7075b88319eSDaniel Borkmann 	close_loader_ctx_fds(ctx, nr_maps, nr_progs);
7085b88319eSDaniel Borkmann 	free(ctx);
7095b88319eSDaniel Borkmann destroy:
7105b88319eSDaniel Borkmann 	test_signed_loader_map__destroy(skel);
7115b88319eSDaniel Borkmann }
7125b88319eSDaniel Borkmann 
7135b88319eSDaniel Borkmann /*
7145b88319eSDaniel Borkmann  * A signed loader must also ignore ctx-supplied initial_value: the host cannot
7155b88319eSDaniel Borkmann  * re-seed a signed program's map contents through the loader ctx. Drive a
7165b88319eSDaniel Borkmann  * program with one initialized global (a .data map) through gen_loader, point
7175b88319eSDaniel Borkmann  * every map's ctx initial_value at an adversarial buffer, and confirm the
7185b88319eSDaniel Borkmann  * created map still holds the attested value, never the ctx bytes.
7195b88319eSDaniel Borkmann  */
7205b88319eSDaniel Borkmann #define DATA_MAGIC 0x5eed1234abad1deaULL
7215b88319eSDaniel Borkmann 
metadata_ctx_initial_value_ignored(void)7225b88319eSDaniel Borkmann static void metadata_ctx_initial_value_ignored(void)
7235b88319eSDaniel Borkmann {
7245b88319eSDaniel Borkmann 	LIBBPF_OPTS(gen_loader_opts, gopts, .gen_hash = true);
7255b88319eSDaniel Borkmann 	struct test_signed_loader_data *skel;
7265b88319eSDaniel Borkmann 	__u8 excl[SHA256_DIGEST_LENGTH], evil[64];
7275b88319eSDaniel Borkmann 	int nr_maps = 0, nr_progs = 0, i, found = 0, r;
7285b88319eSDaniel Borkmann 	struct bpf_program *p;
7295b88319eSDaniel Borkmann 	struct bpf_map *m;
7305b88319eSDaniel Borkmann 	struct bpf_map_desc *md;
7315b88319eSDaniel Borkmann 	unsigned char *blob;
7325b88319eSDaniel Borkmann 	__u32 ctx_sz, data_sz;
7335b88319eSDaniel Borkmann 	void *ctx;
7345b88319eSDaniel Borkmann 	bool ran;
7355b88319eSDaniel Borkmann 
7365b88319eSDaniel Borkmann 	skel = test_signed_loader_data__open();
7375b88319eSDaniel Borkmann 	if (!ASSERT_OK_PTR(skel, "skel_open"))
7385b88319eSDaniel Borkmann 		return;
7395b88319eSDaniel Borkmann 	if (!ASSERT_OK(bpf_object__gen_loader(skel->obj, &gopts), "gen_loader"))
7405b88319eSDaniel Borkmann 		goto destroy;
7415b88319eSDaniel Borkmann 	if (!ASSERT_OK(bpf_object__load(skel->obj), "gen_load"))
7425b88319eSDaniel Borkmann 		goto destroy;
7435b88319eSDaniel Borkmann 
7445b88319eSDaniel Borkmann 	bpf_object__for_each_program(p, skel->obj)
7455b88319eSDaniel Borkmann 		nr_progs++;
7465b88319eSDaniel Borkmann 	bpf_object__for_each_map(m, skel->obj)
7475b88319eSDaniel Borkmann 		nr_maps++;
7485b88319eSDaniel Borkmann 	ctx_sz = sizeof(struct bpf_loader_ctx) +
7495b88319eSDaniel Borkmann 		 nr_maps * sizeof(struct bpf_map_desc) +
7505b88319eSDaniel Borkmann 		 nr_progs * sizeof(struct bpf_prog_desc);
7515b88319eSDaniel Borkmann 	ctx = calloc(1, ctx_sz);
7525b88319eSDaniel Borkmann 	if (!ASSERT_OK_PTR(ctx, "ctx_alloc"))
7535b88319eSDaniel Borkmann 		goto destroy;
7545b88319eSDaniel Borkmann 	((struct bpf_loader_ctx *)ctx)->sz = ctx_sz;
7555b88319eSDaniel Borkmann 
7565b88319eSDaniel Borkmann 	memset(evil, 0xAA, sizeof(evil));
7575b88319eSDaniel Borkmann 	md = (struct bpf_map_desc *)((char *)ctx + sizeof(struct bpf_loader_ctx));
7585b88319eSDaniel Borkmann 	for (i = 0; i < nr_maps; i++)
7595b88319eSDaniel Borkmann 		md[i].initial_value = ptr_to_u64(evil);
7605b88319eSDaniel Borkmann 
7615b88319eSDaniel Borkmann 	libbpf_sha256(gopts.insns, gopts.insns_sz, excl);
7625b88319eSDaniel Borkmann 	data_sz = gopts.data_sz;
7635b88319eSDaniel Borkmann 	blob = malloc(data_sz);
7645b88319eSDaniel Borkmann 	if (!ASSERT_OK_PTR(blob, "blob_alloc"))
7655b88319eSDaniel Borkmann 		goto free_ctx;
7665b88319eSDaniel Borkmann 	memcpy(blob, gopts.data, data_sz);
7675b88319eSDaniel Borkmann 
7685b88319eSDaniel Borkmann 	r = run_gen_loader(gopts.insns, gopts.insns_sz, blob, data_sz,
76999b321ddSDaniel Borkmann 			   excl, sizeof(excl), NULL, 0, ctx, ctx_sz, &ran);
7705b88319eSDaniel Borkmann 	if (!ASSERT_TRUE(ran, "loader ran") ||
7715b88319eSDaniel Borkmann 	    !ASSERT_EQ(r, 0, "loader retval"))
7725b88319eSDaniel Borkmann 		goto free_blob;
7735b88319eSDaniel Borkmann 
7745b88319eSDaniel Borkmann 	for (i = 0; i < nr_maps; i++) {
7755b88319eSDaniel Borkmann 		struct bpf_map_info info;
7765b88319eSDaniel Borkmann 		__u32 ilen = sizeof(info), key = 0;
7775b88319eSDaniel Borkmann 		__u8 value[64] = {};
7785b88319eSDaniel Borkmann 		__u64 got;
7795b88319eSDaniel Borkmann 		int fd = md[i].map_fd;
7805b88319eSDaniel Borkmann 
7815b88319eSDaniel Borkmann 		if (fd <= 0)
7825b88319eSDaniel Borkmann 			continue;
7835b88319eSDaniel Borkmann 		memset(&info, 0, sizeof(info));
7845b88319eSDaniel Borkmann 		if (!ASSERT_OK(bpf_map_get_info_by_fd(fd, &info, &ilen), "map_info"))
7855b88319eSDaniel Borkmann 			continue;
7865b88319eSDaniel Borkmann 		if (info.value_size <= sizeof(value) &&
7875b88319eSDaniel Borkmann 		    bpf_map_lookup_elem(fd, &key, value) == 0) {
7885b88319eSDaniel Borkmann 			memcpy(&got, value, sizeof(got));
7895b88319eSDaniel Borkmann 			/* attested .data survives; ctx bytes (0xAA..) ignored */
7905b88319eSDaniel Borkmann 			if (got == DATA_MAGIC)
7915b88319eSDaniel Borkmann 				found = 1;
7925b88319eSDaniel Borkmann 			ASSERT_NEQ(got, 0xAAAAAAAAAAAAAAAAULL,
7935b88319eSDaniel Borkmann 				   "ctx initial_value ignored for signed loader");
7945b88319eSDaniel Borkmann 		}
7955b88319eSDaniel Borkmann 	}
7965b88319eSDaniel Borkmann 	ASSERT_EQ(found, 1, "attested .data value preserved");
7975b88319eSDaniel Borkmann 
7985b88319eSDaniel Borkmann free_blob:
7995b88319eSDaniel Borkmann 	free(blob);
8005b88319eSDaniel Borkmann free_ctx:
8015b88319eSDaniel Borkmann 	close_loader_ctx_fds(ctx, nr_maps, nr_progs);
8025b88319eSDaniel Borkmann 	free(ctx);
8035b88319eSDaniel Borkmann destroy:
8045b88319eSDaniel Borkmann 	test_signed_loader_data__destroy(skel);
8055b88319eSDaniel Borkmann }
8065b88319eSDaniel Borkmann 
8075b88319eSDaniel Borkmann /*
8085b88319eSDaniel Borkmann  * The load-time signature must authenticate the loader instructions: a valid
8095b88319eSDaniel Borkmann  * signature loads, and the very same signature over one-byte-tampered insns is
8105b88319eSDaniel Borkmann  * rejected. Uses ./verify_sig_setup.sh + ./sign-file at runtime, like
8115b88319eSDaniel Borkmann  * verify_pkcs7_sig, and verifies against the session keyring the key was added
8125b88319eSDaniel Borkmann  * to. (signature_enforced/_too_large only cover a malformed signature.)
8135b88319eSDaniel Borkmann  */
signature_authenticates_insns(void)8145b88319eSDaniel Borkmann static void signature_authenticates_insns(void)
8155b88319eSDaniel Borkmann {
8165b88319eSDaniel Borkmann 	LIBBPF_OPTS(gen_loader_opts, gopts, .gen_hash = true);
8175b88319eSDaniel Borkmann 	char dir_tmpl[] = "/tmp/signed_loaderXXXXXX", *dir;
8185b88319eSDaniel Borkmann 	struct test_signed_loader *skel = NULL;
8195b88319eSDaniel Borkmann 	__u8 excl[SHA256_DIGEST_LENGTH], sig[8192];
8205b88319eSDaniel Borkmann 	__u32 sig_sz = sizeof(sig), insns_sz, data_sz, ctx_sz;
8215b88319eSDaniel Borkmann 	unsigned char *insns = NULL, *tampered = NULL, *blob = NULL;
82299b321ddSDaniel Borkmann 	unsigned char *signbuf = NULL;
8235b88319eSDaniel Borkmann 	int nr_maps = 0, nr_progs = 0, r;
8245b88319eSDaniel Borkmann 	struct bpf_program *p;
8255b88319eSDaniel Borkmann 	struct bpf_map *m;
8265b88319eSDaniel Borkmann 	void *ctx = NULL;
8275b88319eSDaniel Borkmann 	bool ran;
8285b88319eSDaniel Borkmann 
8295b88319eSDaniel Borkmann 	syscall(__NR_request_key, "keyring", "_uid.0", NULL,
8305b88319eSDaniel Borkmann 		KEY_SPEC_SESSION_KEYRING);
8315b88319eSDaniel Borkmann 	dir = mkdtemp(dir_tmpl);
8325b88319eSDaniel Borkmann 	if (!ASSERT_OK_PTR(dir, "mkdtemp"))
8335b88319eSDaniel Borkmann 		return;
8345b88319eSDaniel Borkmann 	if (!ASSERT_OK(run_setup("setup", dir), "verify_sig_setup")) {
8355b88319eSDaniel Borkmann 		rmdir(dir);
8365b88319eSDaniel Borkmann 		return;
8375b88319eSDaniel Borkmann 	}
8385b88319eSDaniel Borkmann 
8395b88319eSDaniel Borkmann 	skel = test_signed_loader__open();
8405b88319eSDaniel Borkmann 	if (!ASSERT_OK_PTR(skel, "skel_open"))
8415b88319eSDaniel Borkmann 		goto cleanup;
8425b88319eSDaniel Borkmann 	if (!ASSERT_OK(bpf_object__gen_loader(skel->obj, &gopts), "gen_loader"))
8435b88319eSDaniel Borkmann 		goto cleanup;
8445b88319eSDaniel Borkmann 	if (!ASSERT_OK(bpf_object__load(skel->obj), "gen_load"))
8455b88319eSDaniel Borkmann 		goto cleanup;
8465b88319eSDaniel Borkmann 
8475b88319eSDaniel Borkmann 	bpf_object__for_each_program(p, skel->obj)
8485b88319eSDaniel Borkmann 		nr_progs++;
8495b88319eSDaniel Borkmann 	bpf_object__for_each_map(m, skel->obj)
8505b88319eSDaniel Borkmann 		nr_maps++;
8515b88319eSDaniel Borkmann 	ctx_sz = sizeof(struct bpf_loader_ctx) +
8525b88319eSDaniel Borkmann 		 nr_maps * sizeof(struct bpf_map_desc) +
8535b88319eSDaniel Borkmann 		 nr_progs * sizeof(struct bpf_prog_desc);
8545b88319eSDaniel Borkmann 	insns_sz = gopts.insns_sz;
8555b88319eSDaniel Borkmann 	data_sz = gopts.data_sz;
8565b88319eSDaniel Borkmann 	ctx = calloc(1, ctx_sz);
8575b88319eSDaniel Borkmann 	insns = malloc(insns_sz);
8585b88319eSDaniel Borkmann 	tampered = malloc(insns_sz);
8595b88319eSDaniel Borkmann 	blob = malloc(data_sz);
8605b88319eSDaniel Borkmann 	if (!ASSERT_OK_PTR(ctx, "ctx") ||
8615b88319eSDaniel Borkmann 	    !ASSERT_OK_PTR(insns, "insns") ||
8625b88319eSDaniel Borkmann 	    !ASSERT_OK_PTR(tampered, "tampered") ||
8635b88319eSDaniel Borkmann 	    !ASSERT_OK_PTR(blob, "blob"))
8645b88319eSDaniel Borkmann 		goto cleanup;
8655b88319eSDaniel Borkmann 	memcpy(insns, gopts.insns, insns_sz);
8665b88319eSDaniel Borkmann 	memcpy(blob, gopts.data, data_sz);
8675b88319eSDaniel Borkmann 	libbpf_sha256(insns, insns_sz, excl);
8685b88319eSDaniel Borkmann 
86999b321ddSDaniel Borkmann 	signbuf = malloc((size_t)insns_sz + data_sz);
87099b321ddSDaniel Borkmann 	if (!ASSERT_OK_PTR(signbuf, "signbuf"))
87199b321ddSDaniel Borkmann 		goto cleanup;
87299b321ddSDaniel Borkmann 	memcpy(signbuf, insns, insns_sz);
87399b321ddSDaniel Borkmann 	memcpy(signbuf + insns_sz, blob, data_sz);
87499b321ddSDaniel Borkmann 	if (!ASSERT_OK(sign_buf(dir, signbuf, insns_sz + data_sz, sig, &sig_sz),
87599b321ddSDaniel Borkmann 		       "sign-file"))
8765b88319eSDaniel Borkmann 		goto cleanup;
8775b88319eSDaniel Borkmann 
8785b88319eSDaniel Borkmann 	memset(ctx, 0, ctx_sz);
8795b88319eSDaniel Borkmann 	((struct bpf_loader_ctx *)ctx)->sz = ctx_sz;
8805b88319eSDaniel Borkmann 	r = run_gen_loader(insns, insns_sz, blob, data_sz, excl, sizeof(excl),
88199b321ddSDaniel Borkmann 			   sig, sig_sz, ctx, ctx_sz, &ran);
8825b88319eSDaniel Borkmann 	ASSERT_TRUE(ran, "valid signature: loader loaded and ran");
8835b88319eSDaniel Borkmann 	ASSERT_EQ(r, 0, "valid signature accepted");
8845b88319eSDaniel Borkmann 	close_loader_ctx_fds(ctx, nr_maps, nr_progs);
8855b88319eSDaniel Borkmann 
8865b88319eSDaniel Borkmann 	memcpy(tampered, insns, insns_sz);
8875b88319eSDaniel Borkmann 	tampered[insns_sz / 2] ^= 0xff;
88899b321ddSDaniel Borkmann 	/*
88999b321ddSDaniel Borkmann 	 * Bind the metadata map to the tampered loader's own digest, so the
89099b321ddSDaniel Borkmann 	 * verifier's exclusive-map check (excl_prog_sha == prog->digest) passes
89199b321ddSDaniel Borkmann 	 * and the signature - verified after the maps are resolved - is what
89299b321ddSDaniel Borkmann 	 * rejects the load. This is the attacker's best case: even after
89399b321ddSDaniel Borkmann 	 * re-binding the exclusive map to their tampered loader, the signature
89499b321ddSDaniel Borkmann 	 * over the original insns || metadata still fails. (Leaving the map
89599b321ddSDaniel Borkmann 	 * bound to the original digest would instead trip the excl check first.)
89699b321ddSDaniel Borkmann 	 */
89799b321ddSDaniel Borkmann 	libbpf_sha256(tampered, insns_sz, excl);
8985b88319eSDaniel Borkmann 	memset(ctx, 0, ctx_sz);
8995b88319eSDaniel Borkmann 	((struct bpf_loader_ctx *)ctx)->sz = ctx_sz;
9005b88319eSDaniel Borkmann 	r = run_gen_loader(tampered, insns_sz, blob, data_sz, excl, sizeof(excl),
90199b321ddSDaniel Borkmann 			   sig, sig_sz, ctx, ctx_sz, &ran);
9025b88319eSDaniel Borkmann 	ASSERT_FALSE(ran, "tampered loader rejected before run");
9035b88319eSDaniel Borkmann 	ASSERT_EQ(r, -EKEYREJECTED, "signature is bound to the instructions");
9045b88319eSDaniel Borkmann cleanup:
9055b88319eSDaniel Borkmann 	free(insns);
9065b88319eSDaniel Borkmann 	free(tampered);
9075b88319eSDaniel Borkmann 	free(blob);
90899b321ddSDaniel Borkmann 	free(signbuf);
90999b321ddSDaniel Borkmann 	free(ctx);
91099b321ddSDaniel Borkmann 	test_signed_loader__destroy(skel);
91199b321ddSDaniel Borkmann 	run_setup("cleanup", dir);
91299b321ddSDaniel Borkmann }
91399b321ddSDaniel Borkmann 
signature_authenticates_metadata(void)91499b321ddSDaniel Borkmann static void signature_authenticates_metadata(void)
91599b321ddSDaniel Borkmann {
91699b321ddSDaniel Borkmann 	LIBBPF_OPTS(gen_loader_opts, gopts, .gen_hash = true);
91799b321ddSDaniel Borkmann 	char dir_tmpl[] = "/tmp/signed_loaderXXXXXX", *dir;
91899b321ddSDaniel Borkmann 	struct test_signed_loader *skel = NULL;
91999b321ddSDaniel Borkmann 	__u8 excl[SHA256_DIGEST_LENGTH], sig[8192];
92099b321ddSDaniel Borkmann 	__u32 sig_sz = sizeof(sig), insns_sz, data_sz, ctx_sz;
92199b321ddSDaniel Borkmann 	unsigned char *insns = NULL, *blob = NULL;
92299b321ddSDaniel Borkmann 	unsigned char *signbuf = NULL;
92399b321ddSDaniel Borkmann 	int nr_maps = 0, nr_progs = 0, r;
92499b321ddSDaniel Borkmann 	struct bpf_program *p;
92599b321ddSDaniel Borkmann 	struct bpf_map *m;
92699b321ddSDaniel Borkmann 	void *ctx = NULL;
92799b321ddSDaniel Borkmann 	bool ran;
92899b321ddSDaniel Borkmann 
92999b321ddSDaniel Borkmann 	syscall(__NR_request_key, "keyring", "_uid.0", NULL,
93099b321ddSDaniel Borkmann 		KEY_SPEC_SESSION_KEYRING);
93199b321ddSDaniel Borkmann 	dir = mkdtemp(dir_tmpl);
93299b321ddSDaniel Borkmann 	if (!ASSERT_OK_PTR(dir, "mkdtemp"))
93399b321ddSDaniel Borkmann 		return;
93499b321ddSDaniel Borkmann 	if (!ASSERT_OK(run_setup("setup", dir), "verify_sig_setup")) {
93599b321ddSDaniel Borkmann 		rmdir(dir);
93699b321ddSDaniel Borkmann 		return;
93799b321ddSDaniel Borkmann 	}
93899b321ddSDaniel Borkmann 
93999b321ddSDaniel Borkmann 	skel = test_signed_loader__open();
94099b321ddSDaniel Borkmann 	if (!ASSERT_OK_PTR(skel, "skel_open"))
94199b321ddSDaniel Borkmann 		goto cleanup;
94299b321ddSDaniel Borkmann 	if (!ASSERT_OK(bpf_object__gen_loader(skel->obj, &gopts), "gen_loader"))
94399b321ddSDaniel Borkmann 		goto cleanup;
94499b321ddSDaniel Borkmann 	if (!ASSERT_OK(bpf_object__load(skel->obj), "gen_load"))
94599b321ddSDaniel Borkmann 		goto cleanup;
94699b321ddSDaniel Borkmann 
94799b321ddSDaniel Borkmann 	bpf_object__for_each_program(p, skel->obj)
94899b321ddSDaniel Borkmann 		nr_progs++;
94999b321ddSDaniel Borkmann 	bpf_object__for_each_map(m, skel->obj)
95099b321ddSDaniel Borkmann 		nr_maps++;
95199b321ddSDaniel Borkmann 	ctx_sz = sizeof(struct bpf_loader_ctx) +
95299b321ddSDaniel Borkmann 		 nr_maps * sizeof(struct bpf_map_desc) +
95399b321ddSDaniel Borkmann 		 nr_progs * sizeof(struct bpf_prog_desc);
95499b321ddSDaniel Borkmann 	insns_sz = gopts.insns_sz;
95599b321ddSDaniel Borkmann 	data_sz = gopts.data_sz;
95699b321ddSDaniel Borkmann 	ctx = calloc(1, ctx_sz);
95799b321ddSDaniel Borkmann 	insns = malloc(insns_sz);
95899b321ddSDaniel Borkmann 	blob = malloc(data_sz);
95999b321ddSDaniel Borkmann 	if (!ASSERT_OK_PTR(ctx, "ctx") ||
96099b321ddSDaniel Borkmann 	    !ASSERT_OK_PTR(insns, "insns") ||
96199b321ddSDaniel Borkmann 	    !ASSERT_OK_PTR(blob, "blob"))
96299b321ddSDaniel Borkmann 		goto cleanup;
96399b321ddSDaniel Borkmann 	memcpy(insns, gopts.insns, insns_sz);
96499b321ddSDaniel Borkmann 	memcpy(blob, gopts.data, data_sz);
96599b321ddSDaniel Borkmann 	libbpf_sha256(insns, insns_sz, excl);
96699b321ddSDaniel Borkmann 
96799b321ddSDaniel Borkmann 	signbuf = malloc((size_t)insns_sz + data_sz);
96899b321ddSDaniel Borkmann 	if (!ASSERT_OK_PTR(signbuf, "signbuf"))
96999b321ddSDaniel Borkmann 		goto cleanup;
97099b321ddSDaniel Borkmann 	memcpy(signbuf, insns, insns_sz);
97199b321ddSDaniel Borkmann 	memcpy(signbuf + insns_sz, blob, data_sz);
97299b321ddSDaniel Borkmann 	if (!ASSERT_OK(sign_buf(dir, signbuf, insns_sz + data_sz, sig, &sig_sz),
97399b321ddSDaniel Borkmann 		       "sign-file"))
97499b321ddSDaniel Borkmann 		goto cleanup;
97599b321ddSDaniel Borkmann 
97699b321ddSDaniel Borkmann 	memset(ctx, 0, ctx_sz);
97799b321ddSDaniel Borkmann 	((struct bpf_loader_ctx *)ctx)->sz = ctx_sz;
97899b321ddSDaniel Borkmann 	r = run_gen_loader(insns, insns_sz, blob, data_sz, excl, sizeof(excl),
97999b321ddSDaniel Borkmann 			   sig, sig_sz, ctx, ctx_sz, &ran);
98099b321ddSDaniel Borkmann 	ASSERT_TRUE(ran, "valid signature: loader loaded and ran");
98199b321ddSDaniel Borkmann 	ASSERT_EQ(r, 0, "valid signature accepted");
98299b321ddSDaniel Borkmann 	close_loader_ctx_fds(ctx, nr_maps, nr_progs);
98399b321ddSDaniel Borkmann 
98499b321ddSDaniel Borkmann 	/*
98599b321ddSDaniel Borkmann 	 * Tamper the metadata after signing while leaving the instructions
98699b321ddSDaniel Borkmann 	 * and thus the exclusive hash binding untouched: the map freezes
98799b321ddSDaniel Borkmann 	 * fine and excl_prog_sha still matches the loader's digest, so the
98899b321ddSDaniel Borkmann 	 * load reaches signature verification, which folds the live frozen
98999b321ddSDaniel Borkmann 	 * map bytes into the checked payload and must reject the modified
99099b321ddSDaniel Borkmann 	 * blob. A kernel folding anything but the map contents themselves
99199b321ddSDaniel Borkmann 	 * would wrongly accept this load.
99299b321ddSDaniel Borkmann 	 */
99399b321ddSDaniel Borkmann 	blob[data_sz / 2] ^= 0xff;
99499b321ddSDaniel Borkmann 	memset(ctx, 0, ctx_sz);
99599b321ddSDaniel Borkmann 	((struct bpf_loader_ctx *)ctx)->sz = ctx_sz;
99699b321ddSDaniel Borkmann 	r = run_gen_loader(insns, insns_sz, blob, data_sz, excl, sizeof(excl),
99799b321ddSDaniel Borkmann 			   sig, sig_sz, ctx, ctx_sz, &ran);
99899b321ddSDaniel Borkmann 	ASSERT_FALSE(ran, "tampered metadata rejected before run");
99999b321ddSDaniel Borkmann 	ASSERT_EQ(r, -EKEYREJECTED, "signature is bound to the metadata");
100099b321ddSDaniel Borkmann cleanup:
100199b321ddSDaniel Borkmann 	free(insns);
100299b321ddSDaniel Borkmann 	free(blob);
100399b321ddSDaniel Borkmann 	free(signbuf);
10045b88319eSDaniel Borkmann 	free(ctx);
10055b88319eSDaniel Borkmann 	test_signed_loader__destroy(skel);
10065b88319eSDaniel Borkmann 	run_setup("cleanup", dir);
10075b88319eSDaniel Borkmann }
10085b88319eSDaniel Borkmann 
make_excl_map(__u32 flags,__u32 value_size)10095b88319eSDaniel Borkmann static int make_excl_map(__u32 flags, __u32 value_size)
10105b88319eSDaniel Borkmann {
10115b88319eSDaniel Borkmann 	LIBBPF_OPTS(bpf_map_create_opts, opts);
10125b88319eSDaniel Borkmann 	__u8 hash[SHA256_DIGEST_LENGTH] = { 1 };	/* any 32-byte value */
10135b88319eSDaniel Borkmann 
10145b88319eSDaniel Borkmann 	opts.excl_prog_hash = hash;
10155b88319eSDaniel Borkmann 	opts.excl_prog_hash_size = sizeof(hash);
10165b88319eSDaniel Borkmann 	opts.map_flags = flags;
10175b88319eSDaniel Borkmann 	return bpf_map_create(BPF_MAP_TYPE_ARRAY, "md", 4, value_size, 1, &opts);
10185b88319eSDaniel Borkmann }
10195b88319eSDaniel Borkmann 
hash_requires_frozen(void)10205b88319eSDaniel Borkmann static void hash_requires_frozen(void)
10215b88319eSDaniel Borkmann {
10225b88319eSDaniel Borkmann 	__u8 hbuf[SHA256_DIGEST_LENGTH], val[64] = {};
10235b88319eSDaniel Borkmann 	struct bpf_map_info info;
10245b88319eSDaniel Borkmann 	__u32 ilen, key = 0;
10255b88319eSDaniel Borkmann 	int fd;
10265b88319eSDaniel Borkmann 
10275b88319eSDaniel Borkmann 	fd = make_excl_map(0, sizeof(val));
10285b88319eSDaniel Borkmann 	if (!ASSERT_OK_FD(fd, "excl_map"))
10295b88319eSDaniel Borkmann 		return;
10305b88319eSDaniel Borkmann 	ASSERT_OK(bpf_map_update_elem(fd, &key, val, 0), "update");
10315b88319eSDaniel Borkmann 
10325b88319eSDaniel Borkmann 	memset(&info, 0, sizeof(info));
10335b88319eSDaniel Borkmann 	info.hash = ptr_to_u64(hbuf);
10345b88319eSDaniel Borkmann 	info.hash_size = sizeof(hbuf);
10355b88319eSDaniel Borkmann 	ilen = sizeof(info);
10365b88319eSDaniel Borkmann 	ASSERT_EQ(bpf_map_get_info_by_fd(fd, &info, &ilen), -EPERM,
10375b88319eSDaniel Borkmann 		  "hash of unfrozen map rejected");
10385b88319eSDaniel Borkmann 	close(fd);
10395b88319eSDaniel Borkmann }
10405b88319eSDaniel Borkmann 
no_update_after_freeze(void)10415b88319eSDaniel Borkmann static void no_update_after_freeze(void)
10425b88319eSDaniel Borkmann {
10435b88319eSDaniel Borkmann 	__u8 val[64] = {};
10445b88319eSDaniel Borkmann 	__u32 key = 0;
10455b88319eSDaniel Borkmann 	int fd;
10465b88319eSDaniel Borkmann 
10475b88319eSDaniel Borkmann 	fd = make_excl_map(0, sizeof(val));
10485b88319eSDaniel Borkmann 	if (!ASSERT_OK_FD(fd, "excl_map"))
10495b88319eSDaniel Borkmann 		return;
10505b88319eSDaniel Borkmann 	ASSERT_OK(bpf_map_update_elem(fd, &key, val, 0), "update");
10515b88319eSDaniel Borkmann 	ASSERT_OK(bpf_map_freeze(fd), "freeze");
10525b88319eSDaniel Borkmann 	ASSERT_EQ(bpf_map_update_elem(fd, &key, val, 0), -EPERM,
10535b88319eSDaniel Borkmann 		  "update after freeze rejected");
10545b88319eSDaniel Borkmann 	close(fd);
10555b88319eSDaniel Borkmann }
10565b88319eSDaniel Borkmann 
freeze_writable_mmap(void)10575b88319eSDaniel Borkmann static void freeze_writable_mmap(void)
10585b88319eSDaniel Borkmann {
10595b88319eSDaniel Borkmann 	void *w;
10605b88319eSDaniel Borkmann 	int fd;
10615b88319eSDaniel Borkmann 
10625b88319eSDaniel Borkmann 	fd = make_excl_map(BPF_F_MMAPABLE, 4096);
10635b88319eSDaniel Borkmann 	if (!ASSERT_OK_FD(fd, "excl_mmapable_map"))
10645b88319eSDaniel Borkmann 		return;
10655b88319eSDaniel Borkmann 	w = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
10665b88319eSDaniel Borkmann 	if (ASSERT_OK_PTR(w, "writable_mmap")) {
10675b88319eSDaniel Borkmann 		ASSERT_EQ(bpf_map_freeze(fd), -EBUSY,
10685b88319eSDaniel Borkmann 			  "freeze rejected while writable mmap held");
10695b88319eSDaniel Borkmann 		munmap(w, 4096);
10705b88319eSDaniel Borkmann 	}
10715b88319eSDaniel Borkmann 	close(fd);
10725b88319eSDaniel Borkmann }
10735b88319eSDaniel Borkmann 
no_writable_mmap_frozen(void)10745b88319eSDaniel Borkmann static void no_writable_mmap_frozen(void)
10755b88319eSDaniel Borkmann {
10765b88319eSDaniel Borkmann 	void *w;
10775b88319eSDaniel Borkmann 	int fd;
10785b88319eSDaniel Borkmann 
10795b88319eSDaniel Borkmann 	fd = make_excl_map(BPF_F_MMAPABLE, 4096);
10805b88319eSDaniel Borkmann 	if (!ASSERT_OK_FD(fd, "excl_mmapable_map"))
10815b88319eSDaniel Borkmann 		return;
10825b88319eSDaniel Borkmann 	ASSERT_OK(bpf_map_freeze(fd), "freeze");
10835b88319eSDaniel Borkmann 	w = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
10845b88319eSDaniel Borkmann 	ASSERT_EQ(w, MAP_FAILED, "writable mmap of frozen map rejected");
10855b88319eSDaniel Borkmann 	if (w != MAP_FAILED)
10865b88319eSDaniel Borkmann 		munmap(w, 4096);
10875b88319eSDaniel Borkmann 	close(fd);
10885b88319eSDaniel Borkmann }
10895b88319eSDaniel Borkmann 
map_hash_matches_libbpf(void)10905b88319eSDaniel Borkmann static void map_hash_matches_libbpf(void)
10915b88319eSDaniel Borkmann {
10925b88319eSDaniel Borkmann 	__u8 kbuf[SHA256_DIGEST_LENGTH], lbuf[SHA256_DIGEST_LENGTH], val[64] = {};
10935b88319eSDaniel Borkmann 	struct bpf_map_info info;
10945b88319eSDaniel Borkmann 	__u32 ilen, key = 0;
10955b88319eSDaniel Borkmann 	int fd, i;
10965b88319eSDaniel Borkmann 
10975b88319eSDaniel Borkmann 	/*
10985b88319eSDaniel Borkmann 	 * The signing scheme assumes the kernel's map hash equals what libbpf
10995b88319eSDaniel Borkmann 	 * computes over the same bytes (gen_loader bakes libbpf_sha256(blob);
11005b88319eSDaniel Borkmann 	 * the kernel recomputes via array_map_get_hash). Pin that they agree.
11015b88319eSDaniel Borkmann 	 */
11025b88319eSDaniel Borkmann 	for (i = 0; i < (int)sizeof(val); i++)
11035b88319eSDaniel Borkmann 		val[i] = i * 7 + 1;
11045b88319eSDaniel Borkmann 	fd = bpf_map_create(BPF_MAP_TYPE_ARRAY, "h", 4, sizeof(val), 1, NULL);
11055b88319eSDaniel Borkmann 	if (!ASSERT_OK_FD(fd, "array_map"))
11065b88319eSDaniel Borkmann 		return;
11075b88319eSDaniel Borkmann 	ASSERT_OK(bpf_map_update_elem(fd, &key, val, 0), "update");
11085b88319eSDaniel Borkmann 	ASSERT_OK(bpf_map_freeze(fd), "freeze");
11095b88319eSDaniel Borkmann 	memset(&info, 0, sizeof(info));
11105b88319eSDaniel Borkmann 	info.hash = ptr_to_u64(kbuf);
11115b88319eSDaniel Borkmann 	info.hash_size = sizeof(kbuf);
11125b88319eSDaniel Borkmann 	ilen = sizeof(info);
11135b88319eSDaniel Borkmann 	if (ASSERT_OK(bpf_map_get_info_by_fd(fd, &info, &ilen), "get_hash")) {
11145b88319eSDaniel Borkmann 		libbpf_sha256(val, sizeof(val), lbuf);
11155b88319eSDaniel Borkmann 		ASSERT_EQ(memcmp(kbuf, lbuf, sizeof(kbuf)), 0,
11165b88319eSDaniel Borkmann 			  "kernel map hash matches libbpf_sha256");
11175b88319eSDaniel Borkmann 	}
11185b88319eSDaniel Borkmann 	close(fd);
11195b88319eSDaniel Borkmann }
11205b88319eSDaniel Borkmann 
map_hash_multi_element(void)11215b88319eSDaniel Borkmann static void map_hash_multi_element(void)
11225b88319eSDaniel Borkmann {
11235b88319eSDaniel Borkmann 	const __u32 nr = 8, value_size = 64;
11245b88319eSDaniel Borkmann 	__u8 kbuf[SHA256_DIGEST_LENGTH], lbuf[SHA256_DIGEST_LENGTH];
11255b88319eSDaniel Borkmann 	struct bpf_map_info info;
11265b88319eSDaniel Borkmann 	__u32 ilen, i, j;
11275b88319eSDaniel Borkmann 	__u8 *full;
11285b88319eSDaniel Borkmann 	int fd;
11295b88319eSDaniel Borkmann 
11305b88319eSDaniel Borkmann 	/*
11315b88319eSDaniel Borkmann 	 * array_map_get_hash() hashes elem_size * max_entries (the whole value
11325b88319eSDaniel Borkmann 	 * area), not just element 0. With an 8-aligned value_size elem_size has
11335b88319eSDaniel Borkmann 	 * no padding, so pin that a >1-entry array's kernel hash equals
11345b88319eSDaniel Borkmann 	 * libbpf_sha256() over the full, concatenated element contents.
11355b88319eSDaniel Borkmann 	 */
11365b88319eSDaniel Borkmann 	fd = bpf_map_create(BPF_MAP_TYPE_ARRAY, "h", 4, value_size, nr, NULL);
11375b88319eSDaniel Borkmann 	if (!ASSERT_OK_FD(fd, "array_map"))
11385b88319eSDaniel Borkmann 		return;
11395b88319eSDaniel Borkmann 	full = calloc(nr, value_size);
11405b88319eSDaniel Borkmann 	if (!ASSERT_OK_PTR(full, "buf"))
11415b88319eSDaniel Borkmann 		goto close_fd;
11425b88319eSDaniel Borkmann 	for (i = 0; i < nr; i++) {
11435b88319eSDaniel Borkmann 		__u8 *v = full + i * value_size;
11445b88319eSDaniel Borkmann 
11455b88319eSDaniel Borkmann 		for (j = 0; j < value_size; j++)
11465b88319eSDaniel Borkmann 			v[j] = i * 31 + j * 7 + 1;
11475b88319eSDaniel Borkmann 		ASSERT_OK(bpf_map_update_elem(fd, &i, v, 0), "update");
11485b88319eSDaniel Borkmann 	}
11495b88319eSDaniel Borkmann 	ASSERT_OK(bpf_map_freeze(fd), "freeze");
11505b88319eSDaniel Borkmann 	memset(&info, 0, sizeof(info));
11515b88319eSDaniel Borkmann 	info.hash = ptr_to_u64(kbuf);
11525b88319eSDaniel Borkmann 	info.hash_size = sizeof(kbuf);
11535b88319eSDaniel Borkmann 	ilen = sizeof(info);
11545b88319eSDaniel Borkmann 	if (ASSERT_OK(bpf_map_get_info_by_fd(fd, &info, &ilen), "get_hash")) {
11555b88319eSDaniel Borkmann 		libbpf_sha256(full, (size_t)nr * value_size, lbuf);
11565b88319eSDaniel Borkmann 		ASSERT_EQ(memcmp(kbuf, lbuf, sizeof(kbuf)), 0,
11575b88319eSDaniel Borkmann 			  "kernel hash covers full multi-element value area");
11585b88319eSDaniel Borkmann 	}
11595b88319eSDaniel Borkmann 	free(full);
11605b88319eSDaniel Borkmann close_fd:
11615b88319eSDaniel Borkmann 	close(fd);
11625b88319eSDaniel Borkmann }
11635b88319eSDaniel Borkmann 
map_hash_bad_size(void)11645b88319eSDaniel Borkmann static void map_hash_bad_size(void)
11655b88319eSDaniel Borkmann {
11665b88319eSDaniel Borkmann 	__u8 kbuf[SHA256_DIGEST_LENGTH], val[64] = {};
11675b88319eSDaniel Borkmann 	struct bpf_map_info info;
11685b88319eSDaniel Borkmann 	__u32 ilen, key = 0;
11695b88319eSDaniel Borkmann 	int fd;
11705b88319eSDaniel Borkmann 
11715b88319eSDaniel Borkmann 	fd = bpf_map_create(BPF_MAP_TYPE_ARRAY, "h", 4, sizeof(val), 1, NULL);
11725b88319eSDaniel Borkmann 	if (!ASSERT_OK_FD(fd, "array_map"))
11735b88319eSDaniel Borkmann 		return;
11745b88319eSDaniel Borkmann 	ASSERT_OK(bpf_map_update_elem(fd, &key, val, 0), "update");
11755b88319eSDaniel Borkmann 	ASSERT_OK(bpf_map_freeze(fd), "freeze");
11765b88319eSDaniel Borkmann 	memset(&info, 0, sizeof(info));
11775b88319eSDaniel Borkmann 	info.hash = ptr_to_u64(kbuf);
11785b88319eSDaniel Borkmann 	info.hash_size = sizeof(kbuf) / 2;
11795b88319eSDaniel Borkmann 	ilen = sizeof(info);
11805b88319eSDaniel Borkmann 	ASSERT_EQ(bpf_map_get_info_by_fd(fd, &info, &ilen), -EINVAL,
11815b88319eSDaniel Borkmann 		  "wrong hash_size rejected");
11825b88319eSDaniel Borkmann 	close(fd);
11835b88319eSDaniel Borkmann }
11845b88319eSDaniel Borkmann 
map_hash_unsupported_type(void)11855b88319eSDaniel Borkmann static void map_hash_unsupported_type(void)
11865b88319eSDaniel Borkmann {
11875b88319eSDaniel Borkmann 	__u8 kbuf[SHA256_DIGEST_LENGTH];
11885b88319eSDaniel Borkmann 	struct bpf_map_info info;
11895b88319eSDaniel Borkmann 	__u32 ilen;
11905b88319eSDaniel Borkmann 	int fd;
11915b88319eSDaniel Borkmann 
11925b88319eSDaniel Borkmann 	/* Only arrays implement map_get_hash; a hash map must be refused. */
11935b88319eSDaniel Borkmann 	fd = bpf_map_create(BPF_MAP_TYPE_HASH, "h", 4, 8, 4, NULL);
11945b88319eSDaniel Borkmann 	if (!ASSERT_OK_FD(fd, "hash_map"))
11955b88319eSDaniel Borkmann 		return;
11965b88319eSDaniel Borkmann 	memset(&info, 0, sizeof(info));
11975b88319eSDaniel Borkmann 	info.hash = ptr_to_u64(kbuf);
11985b88319eSDaniel Borkmann 	info.hash_size = sizeof(kbuf);
11995b88319eSDaniel Borkmann 	ilen = sizeof(info);
12005b88319eSDaniel Borkmann 	ASSERT_EQ(bpf_map_get_info_by_fd(fd, &info, &ilen), -EINVAL,
12015b88319eSDaniel Borkmann 		  "hash unsupported for non-array map");
12025b88319eSDaniel Borkmann 	close(fd);
12035b88319eSDaniel Borkmann }
12045b88319eSDaniel Borkmann 
setup_meta_map(const struct gen_loader_fixture * f)12058ddce416SDaniel Borkmann static int setup_meta_map(const struct gen_loader_fixture *f)
12068ddce416SDaniel Borkmann {
12078ddce416SDaniel Borkmann 	LIBBPF_OPTS(bpf_map_create_opts, mopts,
12088ddce416SDaniel Borkmann 		    .excl_prog_hash = f->excl,
12098ddce416SDaniel Borkmann 		    .excl_prog_hash_size = sizeof(f->excl));
12108ddce416SDaniel Borkmann 	__u32 key = 0;
12118ddce416SDaniel Borkmann 	int fd;
12128ddce416SDaniel Borkmann 
12138ddce416SDaniel Borkmann 	fd = bpf_map_create(BPF_MAP_TYPE_ARRAY, "__loader.map", 4,
12148ddce416SDaniel Borkmann 			    f->data_sz, 1, &mopts);
12158ddce416SDaniel Borkmann 	if (fd < 0)
12168ddce416SDaniel Borkmann 		return -errno;
12178ddce416SDaniel Borkmann 	if (bpf_map_update_elem(fd, &key, f->blob, 0) || bpf_map_freeze(fd)) {
12188ddce416SDaniel Borkmann 		close(fd);
12198ddce416SDaniel Borkmann 		return -errno;
12208ddce416SDaniel Borkmann 	}
12218ddce416SDaniel Borkmann 	return fd;
12228ddce416SDaniel Borkmann }
12238ddce416SDaniel Borkmann 
lsm_signature_verdict(void)12248ddce416SDaniel Borkmann static void lsm_signature_verdict(void)
12258ddce416SDaniel Borkmann {
12268ddce416SDaniel Borkmann 	char dir_tmpl[] = "/tmp/signed_loader_lsmXXXXXX", *dir = NULL;
12278ddce416SDaniel Borkmann 	struct test_signed_loader_lsm *lsm = NULL;
122899b321ddSDaniel Borkmann 	__u32 sig_sz = 8192, msig_sz = 8192;
12298ddce416SDaniel Borkmann 	int map_fd = -1, prog_fd = -1;
12308ddce416SDaniel Borkmann 	bool have_fixture = false;
12318ddce416SDaniel Borkmann 	struct gen_loader_fixture f;
123299b321ddSDaniel Borkmann 	unsigned char *buf;
12338ddce416SDaniel Borkmann 	__s32 ses_serial;
12348ddce416SDaniel Borkmann 	__u8 sig[8192];
12358ddce416SDaniel Borkmann 
12368ddce416SDaniel Borkmann 	lsm = test_signed_loader_lsm__open_and_load();
12378ddce416SDaniel Borkmann 	if (!ASSERT_OK_PTR(lsm, "lsm_skel_load"))
12388ddce416SDaniel Borkmann 		return;
12398ddce416SDaniel Borkmann 	lsm->bss->monitored_tid = sys_gettid();
12408ddce416SDaniel Borkmann 	if (!ASSERT_OK(test_signed_loader_lsm__attach(lsm), "lsm_attach"))
12418ddce416SDaniel Borkmann 		goto out;
12428ddce416SDaniel Borkmann 
12438ddce416SDaniel Borkmann 	have_fixture = true;
12448ddce416SDaniel Borkmann 	if (gen_loader_fixture_init(&f) != 0)
12458ddce416SDaniel Borkmann 		goto out;
12468ddce416SDaniel Borkmann 
12478ddce416SDaniel Borkmann 	map_fd = setup_meta_map(&f);
12488ddce416SDaniel Borkmann 	if (!ASSERT_OK_FD(map_fd, "meta_map_unsigned"))
12498ddce416SDaniel Borkmann 		goto out;
12508ddce416SDaniel Borkmann 	lsm->bss->seen = 0;
125199b321ddSDaniel Borkmann 	prog_fd = load_loader(f.gopts.insns, f.gopts.insns_sz, map_fd, NULL, 0, 0, 0);
12528ddce416SDaniel Borkmann 	close(map_fd);
12538ddce416SDaniel Borkmann 	map_fd = -1;
12548ddce416SDaniel Borkmann 	if (!ASSERT_OK_FD(prog_fd, "unsigned loader load"))
12558ddce416SDaniel Borkmann 		goto out;
12568ddce416SDaniel Borkmann 	close(prog_fd);
12578ddce416SDaniel Borkmann 	prog_fd = -1;
12588ddce416SDaniel Borkmann 	if (!ASSERT_NEQ(lsm->bss->seen, 0, "bpf LSM in the active LSM set"))
12598ddce416SDaniel Borkmann 		goto out;
12608ddce416SDaniel Borkmann 	ASSERT_EQ(lsm->bss->seen, 1, "unsigned: one observed load");
12618ddce416SDaniel Borkmann 	ASSERT_EQ(lsm->bss->sig_verdict, BPF_SIG_UNSIGNED, "unsigned verdict");
12628ddce416SDaniel Borkmann 	ASSERT_EQ(lsm->bss->sig_keyring_type, BPF_SIG_KEYRING_NONE, "unsigned keyring type");
12638ddce416SDaniel Borkmann 	ASSERT_EQ(lsm->bss->sig_keyring_serial, 0, "unsigned: no keyring serial");
12648ddce416SDaniel Borkmann 
12658ddce416SDaniel Borkmann 	syscall(__NR_request_key, "keyring", "_uid.0", NULL,
12668ddce416SDaniel Borkmann 		KEY_SPEC_SESSION_KEYRING);
12678ddce416SDaniel Borkmann 	dir = mkdtemp(dir_tmpl);
12688ddce416SDaniel Borkmann 	if (!ASSERT_OK_PTR(dir, "mkdtemp"))
12698ddce416SDaniel Borkmann 		goto out;
12708ddce416SDaniel Borkmann 	if (!ASSERT_OK(run_setup("setup", dir), "verify_sig_setup")) {
12718ddce416SDaniel Borkmann 		rmdir(dir);
12728ddce416SDaniel Borkmann 		dir = NULL;
12738ddce416SDaniel Borkmann 		goto out;
12748ddce416SDaniel Borkmann 	}
12758ddce416SDaniel Borkmann 	if (!ASSERT_OK(sign_buf(dir, f.gopts.insns, f.gopts.insns_sz, sig,
12768ddce416SDaniel Borkmann 				&sig_sz), "sign-file"))
12778ddce416SDaniel Borkmann 		goto out;
12788ddce416SDaniel Borkmann 
12798ddce416SDaniel Borkmann 	map_fd = setup_meta_map(&f);
12808ddce416SDaniel Borkmann 	if (!ASSERT_OK_FD(map_fd, "meta_map_signed"))
12818ddce416SDaniel Borkmann 		goto out;
12828ddce416SDaniel Borkmann 	lsm->bss->seen = 0;
12838ddce416SDaniel Borkmann 	prog_fd = load_loader(f.gopts.insns, f.gopts.insns_sz, map_fd, sig,
128499b321ddSDaniel Borkmann 			      sig_sz, KEY_SPEC_SESSION_KEYRING, 0);
12858ddce416SDaniel Borkmann 	close(map_fd);
12868ddce416SDaniel Borkmann 	map_fd = -1;
128799b321ddSDaniel Borkmann 	ASSERT_EQ(prog_fd, -EACCES, "unfolded metadata rejected");
128899b321ddSDaniel Borkmann 	if (prog_fd >= 0)
12898ddce416SDaniel Borkmann 		close(prog_fd);
12908ddce416SDaniel Borkmann 	prog_fd = -1;
12918ddce416SDaniel Borkmann 
12928ddce416SDaniel Borkmann 	ses_serial = syscall(__NR_keyctl, KEYCTL_GET_KEYRING_ID,
12938ddce416SDaniel Borkmann 			     KEY_SPEC_SESSION_KEYRING, 0);
12948ddce416SDaniel Borkmann 	ASSERT_EQ(lsm->bss->seen, 1, "signed: one observed load");
129599b321ddSDaniel Borkmann 	ASSERT_EQ(lsm->bss->sig_verdict, BPF_SIG_VERIFIED,
129699b321ddSDaniel Borkmann 		  "admission saw a valid signature");
12978ddce416SDaniel Borkmann 	ASSERT_EQ(lsm->bss->sig_keyring_type, BPF_SIG_KEYRING_USER, "signed keyring type");
12988ddce416SDaniel Borkmann 	ASSERT_GT(ses_serial, 0, "session keyring serial resolved");
12998ddce416SDaniel Borkmann 	ASSERT_EQ(lsm->bss->sig_keyring_serial, ses_serial,
13008ddce416SDaniel Borkmann 		  "signed: validated against session keyring");
130199b321ddSDaniel Borkmann 
130299b321ddSDaniel Borkmann 	buf = malloc((size_t)f.gopts.insns_sz + f.data_sz);
130399b321ddSDaniel Borkmann 	if (!ASSERT_OK_PTR(buf, "meta_signbuf"))
130499b321ddSDaniel Borkmann 		goto out;
130599b321ddSDaniel Borkmann 	memcpy(buf, f.gopts.insns, f.gopts.insns_sz);
130699b321ddSDaniel Borkmann 	memcpy(buf + f.gopts.insns_sz, f.blob, f.data_sz);
130799b321ddSDaniel Borkmann 	if (!ASSERT_OK(sign_buf(dir, buf, f.gopts.insns_sz + f.data_sz,
130899b321ddSDaniel Borkmann 				sig, &msig_sz), "sign insns||metadata")) {
130999b321ddSDaniel Borkmann 		free(buf);
131099b321ddSDaniel Borkmann 		goto out;
131199b321ddSDaniel Borkmann 	}
131299b321ddSDaniel Borkmann 	free(buf);
131399b321ddSDaniel Borkmann 
131499b321ddSDaniel Borkmann 	map_fd = setup_meta_map(&f);
131599b321ddSDaniel Borkmann 	if (!ASSERT_OK_FD(map_fd, "meta_map_bound"))
131699b321ddSDaniel Borkmann 		goto out;
131799b321ddSDaniel Borkmann 	lsm->bss->seen = 0;
131899b321ddSDaniel Borkmann 	prog_fd = load_loader(f.gopts.insns, f.gopts.insns_sz, map_fd, sig,
131999b321ddSDaniel Borkmann 			      msig_sz, KEY_SPEC_SESSION_KEYRING, 1);
132099b321ddSDaniel Borkmann 	close(map_fd);
132199b321ddSDaniel Borkmann 	map_fd = -1;
132299b321ddSDaniel Borkmann 	if (!ASSERT_OK_FD(prog_fd, "metadata-bound loader load"))
132399b321ddSDaniel Borkmann 		goto out;
132499b321ddSDaniel Borkmann 	close(prog_fd);
132599b321ddSDaniel Borkmann 	prog_fd = -1;
132699b321ddSDaniel Borkmann 	ASSERT_EQ(lsm->bss->seen, 1, "metadata: one observed load");
132799b321ddSDaniel Borkmann 	ASSERT_EQ(lsm->bss->sig_verdict, BPF_SIG_VERIFIED,
132899b321ddSDaniel Borkmann 		  "metadata-bound verdict");
13298ddce416SDaniel Borkmann out:
13308ddce416SDaniel Borkmann 	if (map_fd >= 0)
13318ddce416SDaniel Borkmann 		close(map_fd);
13328ddce416SDaniel Borkmann 	if (prog_fd >= 0)
13338ddce416SDaniel Borkmann 		close(prog_fd);
13348ddce416SDaniel Borkmann 	if (have_fixture)
13358ddce416SDaniel Borkmann 		gen_loader_fixture_fini(&f);
13368ddce416SDaniel Borkmann 	if (dir)
13378ddce416SDaniel Borkmann 		run_setup("cleanup", dir);
13388ddce416SDaniel Borkmann 	test_signed_loader_lsm__destroy(lsm);
13398ddce416SDaniel Borkmann }
13408ddce416SDaniel Borkmann 
134199b321ddSDaniel Borkmann /*
134299b321ddSDaniel Borkmann  * Load-time metadata verification: the kernel folds the frozen metadata map
134399b321ddSDaniel Borkmann  * into the signature (insns || metadata) and checks it at BPF_PROG_LOAD via
134499b321ddSDaniel Borkmann  * fd_array_cnt, rather than the loader checking from within BPF. Sign that
134599b321ddSDaniel Borkmann  * concatenation, hand the kernel the map, and confirm the signed loader loads,
134699b321ddSDaniel Borkmann  * runs, and installs its target.
134799b321ddSDaniel Borkmann  */
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)134899b321ddSDaniel Borkmann static int loadtime_drive(const char *dir, const void *insns, __u32 insns_sz,
134999b321ddSDaniel Borkmann 			  const void *data, __u32 data_sz, const __u8 *excl,
135099b321ddSDaniel Borkmann 			  void *ctx, __u32 ctx_sz, int *load_ret, bool *ran)
135199b321ddSDaniel Borkmann {
135299b321ddSDaniel Borkmann 	LIBBPF_OPTS(bpf_map_create_opts, mopts,
135399b321ddSDaniel Borkmann 		    .excl_prog_hash = excl,
135499b321ddSDaniel Borkmann 		    .excl_prog_hash_size = SHA256_DIGEST_LENGTH);
135599b321ddSDaniel Borkmann 	__u32 sig_sz = 8192, key = 0;
135699b321ddSDaniel Borkmann 	unsigned char *buf = NULL;
135799b321ddSDaniel Borkmann 	int map_fd, prog_fd, ret = 0;
135899b321ddSDaniel Borkmann 	union bpf_attr attr;
135999b321ddSDaniel Borkmann 	__u8 sig[8192];
136099b321ddSDaniel Borkmann 
136199b321ddSDaniel Borkmann 	*ran = false;
136299b321ddSDaniel Borkmann 	*load_ret = 0;
136399b321ddSDaniel Borkmann 
136499b321ddSDaniel Borkmann 	/*
136599b321ddSDaniel Borkmann 	 * Metadata map, bound to the loader digest and frozen, exactly as
136699b321ddSDaniel Borkmann 	 * skel_internal.h's bpf_load_and_run() sets it up.
136799b321ddSDaniel Borkmann 	 */
136899b321ddSDaniel Borkmann 	map_fd = bpf_map_create(BPF_MAP_TYPE_ARRAY, "__loader.map", 4,
136999b321ddSDaniel Borkmann 				data_sz, 1, &mopts);
137099b321ddSDaniel Borkmann 	if (map_fd < 0) {
137199b321ddSDaniel Borkmann 		ret = -errno;
137299b321ddSDaniel Borkmann 		goto out_load;
137399b321ddSDaniel Borkmann 	}
137499b321ddSDaniel Borkmann 	if (bpf_map_update_elem(map_fd, &key, data, 0) || bpf_map_freeze(map_fd)) {
137599b321ddSDaniel Borkmann 		ret = -errno;
137699b321ddSDaniel Borkmann 		goto out_load;
137799b321ddSDaniel Borkmann 	}
137899b321ddSDaniel Borkmann 
137999b321ddSDaniel Borkmann 	/* Sign insns || metadata, the same bytes the kernel reconstructs. */
138099b321ddSDaniel Borkmann 	buf = malloc((size_t)insns_sz + data_sz);
138199b321ddSDaniel Borkmann 	if (!buf) {
138299b321ddSDaniel Borkmann 		ret = -ENOMEM;
138399b321ddSDaniel Borkmann 		goto out_load;
138499b321ddSDaniel Borkmann 	}
138599b321ddSDaniel Borkmann 	memcpy(buf, insns, insns_sz);
138699b321ddSDaniel Borkmann 	memcpy(buf + insns_sz, data, data_sz);
138799b321ddSDaniel Borkmann 	ret = sign_buf(dir, buf, insns_sz + data_sz, sig, &sig_sz);
138899b321ddSDaniel Borkmann 	if (ret)
138999b321ddSDaniel Borkmann 		goto out_load;
139099b321ddSDaniel Borkmann 
139199b321ddSDaniel Borkmann 	memset(&attr, 0, sizeof(attr));
139299b321ddSDaniel Borkmann 	attr.prog_type = BPF_PROG_TYPE_SYSCALL;
139399b321ddSDaniel Borkmann 	attr.insns = ptr_to_u64(insns);
139499b321ddSDaniel Borkmann 	attr.insn_cnt = insns_sz / sizeof(struct bpf_insn);
139599b321ddSDaniel Borkmann 	attr.license = ptr_to_u64("Dual BSD/GPL");
139699b321ddSDaniel Borkmann 	attr.prog_flags = BPF_F_SLEEPABLE;
139799b321ddSDaniel Borkmann 	attr.fd_array = ptr_to_u64(&map_fd);
139899b321ddSDaniel Borkmann 	attr.signature = ptr_to_u64(sig);
139999b321ddSDaniel Borkmann 	attr.signature_size = sig_sz;
140099b321ddSDaniel Borkmann 	attr.keyring_id = KEY_SPEC_SESSION_KEYRING;
140199b321ddSDaniel Borkmann 	attr.fd_array_cnt = 1;
140299b321ddSDaniel Borkmann 	memcpy(attr.prog_name, "__loader.prog", sizeof("__loader.prog"));
140399b321ddSDaniel Borkmann 	prog_fd = syscall(__NR_bpf, BPF_PROG_LOAD, &attr,
140499b321ddSDaniel Borkmann 			  offsetofend(union bpf_attr, keyring_id));
140599b321ddSDaniel Borkmann 	if (prog_fd < 0) {
140699b321ddSDaniel Borkmann 		ret = -errno;
140799b321ddSDaniel Borkmann 		goto out_load;
140899b321ddSDaniel Borkmann 	}
140999b321ddSDaniel Borkmann 
141099b321ddSDaniel Borkmann 	memset(&attr, 0, sizeof(attr));
141199b321ddSDaniel Borkmann 	attr.test.prog_fd = prog_fd;
141299b321ddSDaniel Borkmann 	attr.test.ctx_in = ptr_to_u64(ctx);
141399b321ddSDaniel Borkmann 	attr.test.ctx_size_in = ctx_sz;
141499b321ddSDaniel Borkmann 	if (syscall(__NR_bpf, BPF_PROG_RUN, &attr,
141599b321ddSDaniel Borkmann 		    offsetofend(union bpf_attr, test)) < 0) {
141699b321ddSDaniel Borkmann 		ret = -errno;
141799b321ddSDaniel Borkmann 		goto out_prog;
141899b321ddSDaniel Borkmann 	}
141999b321ddSDaniel Borkmann 	*ran = true;
142099b321ddSDaniel Borkmann 	ret = (int)attr.test.retval;
142199b321ddSDaniel Borkmann out_prog:
142299b321ddSDaniel Borkmann 	close(prog_fd);
142399b321ddSDaniel Borkmann 	goto out_map;
142499b321ddSDaniel Borkmann out_load:
142599b321ddSDaniel Borkmann 	*load_ret = ret;
142699b321ddSDaniel Borkmann out_map:
142799b321ddSDaniel Borkmann 	free(buf);
142899b321ddSDaniel Borkmann 	if (map_fd >= 0)
142999b321ddSDaniel Borkmann 		close(map_fd);
143099b321ddSDaniel Borkmann 	return ret;
143199b321ddSDaniel Borkmann }
143299b321ddSDaniel Borkmann 
loadtime_verify(struct bpf_object * obj,int expect_maps)143399b321ddSDaniel Borkmann static void loadtime_verify(struct bpf_object *obj, int expect_maps)
143499b321ddSDaniel Borkmann {
143599b321ddSDaniel Borkmann 	LIBBPF_OPTS(gen_loader_opts, gopts, .gen_hash = true);
143699b321ddSDaniel Borkmann 	char dir_tmpl[] = "/tmp/signed_loader_ltXXXXXX", *dir = NULL;
143799b321ddSDaniel Borkmann 	int nr_maps = 0, nr_progs = 0, load_ret = 0, r;
143899b321ddSDaniel Borkmann 	__u8 excl[SHA256_DIGEST_LENGTH];
143999b321ddSDaniel Borkmann 	struct bpf_prog_desc *pd;
144099b321ddSDaniel Borkmann 	struct bpf_map_desc *md;
144199b321ddSDaniel Borkmann 	unsigned char *blob = NULL;
144299b321ddSDaniel Borkmann 	struct bpf_program *p;
144399b321ddSDaniel Borkmann 	struct bpf_map *m;
144499b321ddSDaniel Borkmann 	__u32 ctx_sz, data_sz;
144599b321ddSDaniel Borkmann 	void *ctx = NULL;
144699b321ddSDaniel Borkmann 	bool ran = false;
144799b321ddSDaniel Borkmann 
144899b321ddSDaniel Borkmann 	syscall(__NR_request_key, "keyring", "_uid.0", NULL,
144999b321ddSDaniel Borkmann 		KEY_SPEC_SESSION_KEYRING);
145099b321ddSDaniel Borkmann 	dir = mkdtemp(dir_tmpl);
145199b321ddSDaniel Borkmann 	if (!ASSERT_OK_PTR(dir, "mkdtemp"))
145299b321ddSDaniel Borkmann 		return;
145399b321ddSDaniel Borkmann 	if (!ASSERT_OK(run_setup("setup", dir), "verify_sig_setup")) {
145499b321ddSDaniel Borkmann 		rmdir(dir);
145599b321ddSDaniel Borkmann 		return;
145699b321ddSDaniel Borkmann 	}
145799b321ddSDaniel Borkmann 
145899b321ddSDaniel Borkmann 	if (!ASSERT_OK(bpf_object__gen_loader(obj, &gopts), "gen_loader"))
145999b321ddSDaniel Borkmann 		goto out;
146099b321ddSDaniel Borkmann 	if (!ASSERT_OK(bpf_object__load(obj), "gen_load"))
146199b321ddSDaniel Borkmann 		goto out;
146299b321ddSDaniel Borkmann 
146399b321ddSDaniel Borkmann 	bpf_object__for_each_program(p, obj)
146499b321ddSDaniel Borkmann 		nr_progs++;
146599b321ddSDaniel Borkmann 	bpf_object__for_each_map(m, obj)
146699b321ddSDaniel Borkmann 		nr_maps++;
146799b321ddSDaniel Borkmann 	if (!ASSERT_EQ(nr_maps, expect_maps, "fixture map count"))
146899b321ddSDaniel Borkmann 		goto out;
146999b321ddSDaniel Borkmann 
147099b321ddSDaniel Borkmann 	ctx_sz = sizeof(struct bpf_loader_ctx) +
147199b321ddSDaniel Borkmann 		 nr_maps * sizeof(struct bpf_map_desc) +
147299b321ddSDaniel Borkmann 		 nr_progs * sizeof(struct bpf_prog_desc);
147399b321ddSDaniel Borkmann 	ctx = calloc(1, ctx_sz);
147499b321ddSDaniel Borkmann 	if (!ASSERT_OK_PTR(ctx, "ctx_alloc"))
147599b321ddSDaniel Borkmann 		goto out;
147699b321ddSDaniel Borkmann 	((struct bpf_loader_ctx *)ctx)->sz = ctx_sz;
147799b321ddSDaniel Borkmann 
147899b321ddSDaniel Borkmann 	data_sz = gopts.data_sz;
147999b321ddSDaniel Borkmann 	blob = malloc(data_sz);
148099b321ddSDaniel Borkmann 	if (!ASSERT_OK_PTR(blob, "blob_alloc"))
148199b321ddSDaniel Borkmann 		goto out;
148299b321ddSDaniel Borkmann 	memcpy(blob, gopts.data, data_sz);
148399b321ddSDaniel Borkmann 
148499b321ddSDaniel Borkmann 	/* excl_prog_hash = SHA256(loader insns) == the loader's prog->digest. */
148599b321ddSDaniel Borkmann 	libbpf_sha256(gopts.insns, gopts.insns_sz, excl);
148699b321ddSDaniel Borkmann 
148799b321ddSDaniel Borkmann 	r = loadtime_drive(dir, gopts.insns, gopts.insns_sz, blob, data_sz,
148899b321ddSDaniel Borkmann 			   excl, ctx, ctx_sz, &load_ret, &ran);
148999b321ddSDaniel Borkmann 	ASSERT_OK(load_ret, "signed loader loaded (insns || metadata)");
149099b321ddSDaniel Borkmann 	ASSERT_TRUE(ran, "loader ran");
149199b321ddSDaniel Borkmann 	ASSERT_EQ(r, 0, "loader installed its target");
149299b321ddSDaniel Borkmann 
149399b321ddSDaniel Borkmann 	md = (struct bpf_map_desc *)((char *)ctx + sizeof(struct bpf_loader_ctx));
149499b321ddSDaniel Borkmann 	pd = (struct bpf_prog_desc *)(md + nr_maps);
149599b321ddSDaniel Borkmann 	ASSERT_GT(pd[0].prog_fd, 0, "target program installed");
149699b321ddSDaniel Borkmann 	if (nr_maps)
149799b321ddSDaniel Borkmann 		ASSERT_GT(md[0].map_fd, 0, "target map installed");
149899b321ddSDaniel Borkmann 
149999b321ddSDaniel Borkmann 	close_loader_ctx_fds(ctx, nr_maps, nr_progs);
150099b321ddSDaniel Borkmann out:
150199b321ddSDaniel Borkmann 	free(blob);
150299b321ddSDaniel Borkmann 	free(ctx);
150399b321ddSDaniel Borkmann 	if (dir)
150499b321ddSDaniel Borkmann 		run_setup("cleanup", dir);
150599b321ddSDaniel Borkmann }
150699b321ddSDaniel Borkmann 
loadtime_no_map(void)150799b321ddSDaniel Borkmann static void loadtime_no_map(void)
150899b321ddSDaniel Borkmann {
150999b321ddSDaniel Borkmann 	struct test_signed_loader *skel = test_signed_loader__open();
151099b321ddSDaniel Borkmann 
151199b321ddSDaniel Borkmann 	if (!ASSERT_OK_PTR(skel, "skel_open"))
151299b321ddSDaniel Borkmann 		return;
151399b321ddSDaniel Borkmann 	loadtime_verify(skel->obj, 0);
151499b321ddSDaniel Borkmann 	test_signed_loader__destroy(skel);
151599b321ddSDaniel Borkmann }
151699b321ddSDaniel Borkmann 
loadtime_with_map(void)151799b321ddSDaniel Borkmann static void loadtime_with_map(void)
151899b321ddSDaniel Borkmann {
151999b321ddSDaniel Borkmann 	struct test_signed_loader_map *skel = test_signed_loader_map__open();
152099b321ddSDaniel Borkmann 
152199b321ddSDaniel Borkmann 	if (!ASSERT_OK_PTR(skel, "skel_open"))
152299b321ddSDaniel Borkmann 		return;
152399b321ddSDaniel Borkmann 	loadtime_verify(skel->obj, 1);
152499b321ddSDaniel Borkmann 	test_signed_loader_map__destroy(skel);
152599b321ddSDaniel Borkmann }
152699b321ddSDaniel Borkmann 
152799b321ddSDaniel Borkmann /*
152899b321ddSDaniel Borkmann  * A signed program need not bind any map. A plain BPF_PROG_TYPE_SYSCALL
152999b321ddSDaniel Borkmann  * program with no fd_array is signed over its instructions alone: the kernel
153099b321ddSDaniel Borkmann  * verifies the signature, folds no metadata, and the program loads. Exercise
153199b321ddSDaniel Borkmann  * the fd_array == NULL / fd_array_cnt == 0 path, and confirm the signature
153299b321ddSDaniel Borkmann  * still authenticates the instructions (a tampered copy is rejected).
153399b321ddSDaniel Borkmann  */
signed_no_fd_array(void)153499b321ddSDaniel Borkmann static void signed_no_fd_array(void)
153599b321ddSDaniel Borkmann {
153699b321ddSDaniel Borkmann 	struct bpf_insn insns[] = {
153799b321ddSDaniel Borkmann 		BPF_MOV64_IMM(BPF_REG_0, 0),
153899b321ddSDaniel Borkmann 		BPF_EXIT_INSN(),
153999b321ddSDaniel Borkmann 	};
154099b321ddSDaniel Borkmann 	char dir_tmpl[] = "/tmp/signed_loaderXXXXXX", *dir;
154199b321ddSDaniel Borkmann 	__u32 sig_sz = 8192;
154299b321ddSDaniel Borkmann 	union bpf_attr attr;
154399b321ddSDaniel Borkmann 	__u8 sig[8192];
154499b321ddSDaniel Borkmann 	int prog_fd, err;
154599b321ddSDaniel Borkmann 
154699b321ddSDaniel Borkmann 	syscall(__NR_request_key, "keyring", "_uid.0", NULL,
154799b321ddSDaniel Borkmann 		KEY_SPEC_SESSION_KEYRING);
154899b321ddSDaniel Borkmann 	dir = mkdtemp(dir_tmpl);
154999b321ddSDaniel Borkmann 	if (!ASSERT_OK_PTR(dir, "mkdtemp"))
155099b321ddSDaniel Borkmann 		return;
155199b321ddSDaniel Borkmann 	if (!ASSERT_OK(run_setup("setup", dir), "verify_sig_setup")) {
155299b321ddSDaniel Borkmann 		rmdir(dir);
155399b321ddSDaniel Borkmann 		return;
155499b321ddSDaniel Borkmann 	}
155599b321ddSDaniel Borkmann 
155699b321ddSDaniel Borkmann 	/* No metadata map: the signed payload is the instructions alone. */
155799b321ddSDaniel Borkmann 	if (!ASSERT_OK(sign_buf(dir, insns, sizeof(insns), sig, &sig_sz),
155899b321ddSDaniel Borkmann 		       "sign-file"))
155999b321ddSDaniel Borkmann 		goto cleanup;
156099b321ddSDaniel Borkmann 
156199b321ddSDaniel Borkmann 	memset(&attr, 0, sizeof(attr));
156299b321ddSDaniel Borkmann 	attr.prog_type = BPF_PROG_TYPE_SYSCALL;
156399b321ddSDaniel Borkmann 	attr.insns = ptr_to_u64(insns);
156499b321ddSDaniel Borkmann 	attr.insn_cnt = ARRAY_SIZE(insns);
156599b321ddSDaniel Borkmann 	attr.license = ptr_to_u64("Dual BSD/GPL");
156699b321ddSDaniel Borkmann 	attr.prog_flags = BPF_F_SLEEPABLE;
156799b321ddSDaniel Borkmann 	attr.signature = ptr_to_u64(sig);
156899b321ddSDaniel Borkmann 	attr.signature_size = sig_sz;
156999b321ddSDaniel Borkmann 	attr.keyring_id = KEY_SPEC_SESSION_KEYRING;
157099b321ddSDaniel Borkmann 	/* fd_array and fd_array_cnt deliberately left NULL/0. */
157199b321ddSDaniel Borkmann 	memcpy(attr.prog_name, "signed_nomap", sizeof("signed_nomap"));
157299b321ddSDaniel Borkmann 
157399b321ddSDaniel Borkmann 	prog_fd = syscall(__NR_bpf, BPF_PROG_LOAD, &attr,
157499b321ddSDaniel Borkmann 			  offsetofend(union bpf_attr, keyring_id));
157599b321ddSDaniel Borkmann 	if (!ASSERT_GE(prog_fd, 0, "map-less signed program loaded")) {
157699b321ddSDaniel Borkmann 		if (prog_fd >= 0)
157799b321ddSDaniel Borkmann 			close(prog_fd);
157899b321ddSDaniel Borkmann 		goto cleanup;
157999b321ddSDaniel Borkmann 	}
158099b321ddSDaniel Borkmann 	close(prog_fd);
158199b321ddSDaniel Borkmann 
158299b321ddSDaniel Borkmann 	/* The signature covers the instructions, so tampering must be rejected. */
158399b321ddSDaniel Borkmann 	insns[0].imm = 1;
158499b321ddSDaniel Borkmann 	prog_fd = syscall(__NR_bpf, BPF_PROG_LOAD, &attr,
158599b321ddSDaniel Borkmann 			  offsetofend(union bpf_attr, keyring_id));
158699b321ddSDaniel Borkmann 	err = prog_fd < 0 ? -errno : prog_fd;
158799b321ddSDaniel Borkmann 	ASSERT_EQ(err, -EKEYREJECTED, "tampered map-less program rejected");
158899b321ddSDaniel Borkmann 	if (prog_fd >= 0)
158999b321ddSDaniel Borkmann 		close(prog_fd);
159099b321ddSDaniel Borkmann cleanup:
159199b321ddSDaniel Borkmann 	run_setup("cleanup", dir);
159299b321ddSDaniel Borkmann }
159399b321ddSDaniel Borkmann 
159499b321ddSDaniel Borkmann /*
159599b321ddSDaniel Borkmann  * A signed program may reach maps only through fd_array indices, so the kernel
159699b321ddSDaniel Borkmann  * folds (and thus attests) them. A direct BPF_PSEUDO_MAP_FD reference - a raw,
159799b321ddSDaniel Borkmann  * unfolded fd baked into the signed instructions - is rejected by the verifier.
159899b321ddSDaniel Borkmann  */
signed_map_by_fd_rejected(void)159999b321ddSDaniel Borkmann static void signed_map_by_fd_rejected(void)
160099b321ddSDaniel Borkmann {
160199b321ddSDaniel Borkmann 	struct bpf_insn insns[] = {
160299b321ddSDaniel Borkmann 		BPF_LD_MAP_FD(BPF_REG_1, 0),
160399b321ddSDaniel Borkmann 		BPF_MOV64_IMM(BPF_REG_0, 0),
160499b321ddSDaniel Borkmann 		BPF_EXIT_INSN(),
160599b321ddSDaniel Borkmann 	};
160699b321ddSDaniel Borkmann 	char dir_tmpl[] = "/tmp/signed_loaderXXXXXX", *dir;
160799b321ddSDaniel Borkmann 	__u32 sig_sz = 8192;
160899b321ddSDaniel Borkmann 	union bpf_attr attr;
160999b321ddSDaniel Borkmann 	__u8 sig[8192];
161099b321ddSDaniel Borkmann 	int map_fd, prog_fd, err;
161199b321ddSDaniel Borkmann 
161299b321ddSDaniel Borkmann 	map_fd = bpf_map_create(BPF_MAP_TYPE_ARRAY, "sig_mapfd", 4, 4, 1, NULL);
161399b321ddSDaniel Borkmann 	if (!ASSERT_GE(map_fd, 0, "map_create"))
161499b321ddSDaniel Borkmann 		return;
161599b321ddSDaniel Borkmann 	insns[0].imm = map_fd;	/* bake the raw map fd into the ld_imm64 */
161699b321ddSDaniel Borkmann 
161799b321ddSDaniel Borkmann 	syscall(__NR_request_key, "keyring", "_uid.0", NULL,
161899b321ddSDaniel Borkmann 		KEY_SPEC_SESSION_KEYRING);
161999b321ddSDaniel Borkmann 	dir = mkdtemp(dir_tmpl);
162099b321ddSDaniel Borkmann 	if (!ASSERT_OK_PTR(dir, "mkdtemp"))
162199b321ddSDaniel Borkmann 		goto out_map;
162299b321ddSDaniel Borkmann 	if (!ASSERT_OK(run_setup("setup", dir), "verify_sig_setup")) {
162399b321ddSDaniel Borkmann 		rmdir(dir);
162499b321ddSDaniel Borkmann 		goto out_map;
162599b321ddSDaniel Borkmann 	}
162699b321ddSDaniel Borkmann 
162799b321ddSDaniel Borkmann 	/* Sign the instructions, raw map fd and all. */
162899b321ddSDaniel Borkmann 	if (!ASSERT_OK(sign_buf(dir, insns, sizeof(insns), sig, &sig_sz),
162999b321ddSDaniel Borkmann 		       "sign-file"))
163099b321ddSDaniel Borkmann 		goto cleanup;
163199b321ddSDaniel Borkmann 
163299b321ddSDaniel Borkmann 	memset(&attr, 0, sizeof(attr));
163399b321ddSDaniel Borkmann 	attr.prog_type = BPF_PROG_TYPE_SYSCALL;
163499b321ddSDaniel Borkmann 	attr.insns = ptr_to_u64(insns);
163599b321ddSDaniel Borkmann 	attr.insn_cnt = ARRAY_SIZE(insns);
163699b321ddSDaniel Borkmann 	attr.license = ptr_to_u64("Dual BSD/GPL");
163799b321ddSDaniel Borkmann 	attr.prog_flags = BPF_F_SLEEPABLE;
163899b321ddSDaniel Borkmann 	attr.signature = ptr_to_u64(sig);
163999b321ddSDaniel Borkmann 	attr.signature_size = sig_sz;
164099b321ddSDaniel Borkmann 	attr.keyring_id = KEY_SPEC_SESSION_KEYRING;
164199b321ddSDaniel Borkmann 	/* No fd_array: the map is reached by a raw fd in the instructions. */
164299b321ddSDaniel Borkmann 	memcpy(attr.prog_name, "signed_mapfd", sizeof("signed_mapfd"));
164399b321ddSDaniel Borkmann 
164499b321ddSDaniel Borkmann 	prog_fd = syscall(__NR_bpf, BPF_PROG_LOAD, &attr,
164599b321ddSDaniel Borkmann 			  offsetofend(union bpf_attr, keyring_id));
164699b321ddSDaniel Borkmann 	err = prog_fd < 0 ? -errno : prog_fd;
164799b321ddSDaniel Borkmann 	ASSERT_EQ(err, -EINVAL, "signed program referencing a map by fd rejected");
164899b321ddSDaniel Borkmann 	if (prog_fd >= 0)
164999b321ddSDaniel Borkmann 		close(prog_fd);
165099b321ddSDaniel Borkmann cleanup:
165199b321ddSDaniel Borkmann 	run_setup("cleanup", dir);
165299b321ddSDaniel Borkmann out_map:
165399b321ddSDaniel Borkmann 	close(map_fd);
165499b321ddSDaniel Borkmann }
165599b321ddSDaniel Borkmann 
165699b321ddSDaniel Borkmann /*
165799b321ddSDaniel Borkmann  * A signed program may reach maps only through the continuous fd_array, so the
165899b321ddSDaniel Borkmann  * kernel folds (and thus attests) them. Referencing a map by fd_array *index*
165999b321ddSDaniel Borkmann  * while leaving fd_array_cnt at 0 selects the sparse path, which resolves a map
166099b321ddSDaniel Borkmann  * the signature never covered; the verifier rejects it up front with -EACCES.
166199b321ddSDaniel Borkmann  */
signed_sparse_fd_array_rejected(void)166299b321ddSDaniel Borkmann static void signed_sparse_fd_array_rejected(void)
166399b321ddSDaniel Borkmann {
166499b321ddSDaniel Borkmann 	struct bpf_insn insns[] = {
166599b321ddSDaniel Borkmann 		BPF_LD_IMM64_RAW(BPF_REG_1, BPF_PSEUDO_MAP_IDX, 0),
166699b321ddSDaniel Borkmann 		BPF_MOV64_IMM(BPF_REG_0, 0),
166799b321ddSDaniel Borkmann 		BPF_EXIT_INSN(),
166899b321ddSDaniel Borkmann 	};
166999b321ddSDaniel Borkmann 	char dir_tmpl[] = "/tmp/signed_loader_spXXXXXX", *dir;
167099b321ddSDaniel Borkmann 	__u32 sig_sz = 8192;
167199b321ddSDaniel Borkmann 	union bpf_attr attr;
167299b321ddSDaniel Borkmann 	__u8 sig[8192];
167399b321ddSDaniel Borkmann 	int map_fd, prog_fd, err;
167499b321ddSDaniel Borkmann 
167599b321ddSDaniel Borkmann 	map_fd = bpf_map_create(BPF_MAP_TYPE_ARRAY, "sig_sparse", 4, 4, 1, NULL);
167699b321ddSDaniel Borkmann 	if (!ASSERT_GE(map_fd, 0, "map_create"))
167799b321ddSDaniel Borkmann 		return;
167899b321ddSDaniel Borkmann 
167999b321ddSDaniel Borkmann 	syscall(__NR_request_key, "keyring", "_uid.0", NULL,
168099b321ddSDaniel Borkmann 		KEY_SPEC_SESSION_KEYRING);
168199b321ddSDaniel Borkmann 	dir = mkdtemp(dir_tmpl);
168299b321ddSDaniel Borkmann 	if (!ASSERT_OK_PTR(dir, "mkdtemp"))
168399b321ddSDaniel Borkmann 		goto out_map;
168499b321ddSDaniel Borkmann 	if (!ASSERT_OK(run_setup("setup", dir), "verify_sig_setup")) {
168599b321ddSDaniel Borkmann 		rmdir(dir);
168699b321ddSDaniel Borkmann 		goto out_map;
168799b321ddSDaniel Borkmann 	}
168899b321ddSDaniel Borkmann 
168999b321ddSDaniel Borkmann 	/* Sign the instructions alone; the sparse map is not folded. */
169099b321ddSDaniel Borkmann 	if (!ASSERT_OK(sign_buf(dir, insns, sizeof(insns), sig, &sig_sz),
169199b321ddSDaniel Borkmann 		       "sign-file"))
169299b321ddSDaniel Borkmann 		goto cleanup;
169399b321ddSDaniel Borkmann 
169499b321ddSDaniel Borkmann 	memset(&attr, 0, sizeof(attr));
169599b321ddSDaniel Borkmann 	attr.prog_type = BPF_PROG_TYPE_SYSCALL;
169699b321ddSDaniel Borkmann 	attr.insns = ptr_to_u64(insns);
169799b321ddSDaniel Borkmann 	attr.insn_cnt = ARRAY_SIZE(insns);
169899b321ddSDaniel Borkmann 	attr.license = ptr_to_u64("Dual BSD/GPL");
169999b321ddSDaniel Borkmann 	attr.prog_flags = BPF_F_SLEEPABLE;
170099b321ddSDaniel Borkmann 	attr.fd_array = ptr_to_u64(&map_fd);
170199b321ddSDaniel Borkmann 	attr.fd_array_cnt = 0; /* sparse: force lazy map resolution */
170299b321ddSDaniel Borkmann 	attr.signature = ptr_to_u64(sig);
170399b321ddSDaniel Borkmann 	attr.signature_size = sig_sz;
170499b321ddSDaniel Borkmann 	attr.keyring_id = KEY_SPEC_SESSION_KEYRING;
170599b321ddSDaniel Borkmann 	memcpy(attr.prog_name, "signed_sparse", sizeof("signed_sparse"));
170699b321ddSDaniel Borkmann 
170799b321ddSDaniel Borkmann 	prog_fd = syscall(__NR_bpf, BPF_PROG_LOAD, &attr,
170899b321ddSDaniel Borkmann 			  offsetofend(union bpf_attr, keyring_id));
170999b321ddSDaniel Borkmann 	err = prog_fd < 0 ? -errno : prog_fd;
171099b321ddSDaniel Borkmann 	ASSERT_EQ(err, -EACCES, "signed program binding a sparse fd_array map rejected");
171199b321ddSDaniel Borkmann 	if (prog_fd >= 0)
171299b321ddSDaniel Borkmann 		close(prog_fd);
171399b321ddSDaniel Borkmann cleanup:
171499b321ddSDaniel Borkmann 	run_setup("cleanup", dir);
171599b321ddSDaniel Borkmann out_map:
171699b321ddSDaniel Borkmann 	close(map_fd);
171799b321ddSDaniel Borkmann }
171899b321ddSDaniel Borkmann 
signed_module_kfunc_rejected(void)171999b321ddSDaniel Borkmann static void signed_module_kfunc_rejected(void)
172099b321ddSDaniel Borkmann {
172199b321ddSDaniel Borkmann 	struct bpf_insn insns[] = {
172299b321ddSDaniel Borkmann 		BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, BPF_PSEUDO_KFUNC_CALL, 1, 1),
172399b321ddSDaniel Borkmann 		BPF_MOV64_IMM(BPF_REG_0, 0),
172499b321ddSDaniel Borkmann 		BPF_EXIT_INSN(),
172599b321ddSDaniel Borkmann 	};
172699b321ddSDaniel Borkmann 	char dir_tmpl[] = "/tmp/signed_loader_kfnXXXXXX", *dir;
172799b321ddSDaniel Borkmann 	int prog_fd, err, fds[2];
172899b321ddSDaniel Borkmann 	struct btf *btf = NULL;
172999b321ddSDaniel Borkmann 	__u32 sig_sz = 8192;
173099b321ddSDaniel Borkmann 	union bpf_attr attr;
173199b321ddSDaniel Borkmann 	__u8 sig[8192];
173299b321ddSDaniel Borkmann 
173399b321ddSDaniel Borkmann 	syscall(__NR_request_key, "keyring", "_uid.0", NULL,
173499b321ddSDaniel Borkmann 		KEY_SPEC_SESSION_KEYRING);
173599b321ddSDaniel Borkmann 	dir = mkdtemp(dir_tmpl);
173699b321ddSDaniel Borkmann 	if (!ASSERT_OK_PTR(dir, "mkdtemp"))
173799b321ddSDaniel Borkmann 		return;
173899b321ddSDaniel Borkmann 	if (!ASSERT_OK(run_setup("setup", dir), "verify_sig_setup")) {
173999b321ddSDaniel Borkmann 		rmdir(dir);
174099b321ddSDaniel Borkmann 		return;
174199b321ddSDaniel Borkmann 	}
174299b321ddSDaniel Borkmann 	if (!ASSERT_OK(sign_buf(dir, insns, sizeof(insns), sig, &sig_sz),
174399b321ddSDaniel Borkmann 		       "sign-file"))
174499b321ddSDaniel Borkmann 		goto cleanup;
174599b321ddSDaniel Borkmann 	btf = btf__new_empty();
174699b321ddSDaniel Borkmann 	if (!ASSERT_OK_PTR(btf, "btf_new_empty"))
174799b321ddSDaniel Borkmann 		goto cleanup;
174899b321ddSDaniel Borkmann 	btf__add_int(btf, "int", 4, BTF_INT_SIGNED);
174999b321ddSDaniel Borkmann 	if (!ASSERT_OK(btf__load_into_kernel(btf), "btf_load"))
175099b321ddSDaniel Borkmann 		goto cleanup;
175199b321ddSDaniel Borkmann 	fds[0] = -1;
175299b321ddSDaniel Borkmann 	fds[1] = btf__fd(btf);
175399b321ddSDaniel Borkmann 
175499b321ddSDaniel Borkmann 	memset(&attr, 0, sizeof(attr));
175599b321ddSDaniel Borkmann 	attr.prog_type = BPF_PROG_TYPE_SYSCALL;
175699b321ddSDaniel Borkmann 	attr.insns = ptr_to_u64(insns);
175799b321ddSDaniel Borkmann 	attr.insn_cnt = ARRAY_SIZE(insns);
175899b321ddSDaniel Borkmann 	attr.license = ptr_to_u64("Dual BSD/GPL");
175999b321ddSDaniel Borkmann 	attr.prog_flags = BPF_F_SLEEPABLE;
176099b321ddSDaniel Borkmann 	attr.fd_array = ptr_to_u64(fds);
176199b321ddSDaniel Borkmann 	attr.fd_array_cnt = 0; /* sparse: force lazy kfunc BTF resolution */
176299b321ddSDaniel Borkmann 	attr.signature = ptr_to_u64(sig);
176399b321ddSDaniel Borkmann 	attr.signature_size = sig_sz;
176499b321ddSDaniel Borkmann 	attr.keyring_id = KEY_SPEC_SESSION_KEYRING;
176599b321ddSDaniel Borkmann 	memcpy(attr.prog_name, "signed_kfunc", sizeof("signed_kfunc"));
176699b321ddSDaniel Borkmann 
176799b321ddSDaniel Borkmann 	prog_fd = syscall(__NR_bpf, BPF_PROG_LOAD, &attr,
176899b321ddSDaniel Borkmann 			  offsetofend(union bpf_attr, keyring_id));
176999b321ddSDaniel Borkmann 	err = prog_fd < 0 ? -errno : prog_fd;
177099b321ddSDaniel Borkmann 	if (prog_fd >= 0)
177199b321ddSDaniel Borkmann 		close(prog_fd);
177299b321ddSDaniel Borkmann 
177399b321ddSDaniel Borkmann 	ASSERT_EQ(err, -EACCES, "module kfunc BTF in signed program rejected");
177499b321ddSDaniel Borkmann cleanup:
177599b321ddSDaniel Borkmann 	if (btf)
177699b321ddSDaniel Borkmann 		btf__free(btf);
177799b321ddSDaniel Borkmann 	run_setup("cleanup", dir);
177899b321ddSDaniel Borkmann }
177999b321ddSDaniel Borkmann 
test_signed_loader(void)17805b88319eSDaniel Borkmann void test_signed_loader(void)
17815b88319eSDaniel Borkmann {
178299b321ddSDaniel Borkmann 	if (test__start_subtest("loadtime_no_map"))
178399b321ddSDaniel Borkmann 		loadtime_no_map();
178499b321ddSDaniel Borkmann 	if (test__start_subtest("loadtime_with_map"))
178599b321ddSDaniel Borkmann 		loadtime_with_map();
17865b88319eSDaniel Borkmann 	if (test__start_subtest("metadata_match"))
17875b88319eSDaniel Borkmann 		metadata_match();
17885b88319eSDaniel Borkmann 	if (test__start_subtest("signature_enforced"))
17895b88319eSDaniel Borkmann 		signature_enforced();
179099b321ddSDaniel Borkmann 	if (test__start_subtest("signed_nonexcl_fd_array_rejected"))
179199b321ddSDaniel Borkmann 		signed_nonexcl_fd_array_rejected();
179299b321ddSDaniel Borkmann 	if (test__start_subtest("signed_unfrozen_fd_array_rejected"))
179399b321ddSDaniel Borkmann 		signed_unfrozen_fd_array_rejected();
179499b321ddSDaniel Borkmann 	if (test__start_subtest("signed_nonarray_fd_array_rejected"))
179599b321ddSDaniel Borkmann 		signed_nonarray_fd_array_rejected();
179699b321ddSDaniel Borkmann 	if (test__start_subtest("signed_btf_fd_array_rejected"))
179799b321ddSDaniel Borkmann 		signed_btf_fd_array_rejected();
179899b321ddSDaniel Borkmann 	if (test__start_subtest("signed_module_kfunc_rejected"))
179999b321ddSDaniel Borkmann 		signed_module_kfunc_rejected();
180099b321ddSDaniel Borkmann 	if (test__start_subtest("signature_failure_logs"))
180199b321ddSDaniel Borkmann 		signature_failure_logs();
18025b88319eSDaniel Borkmann 	if (test__start_subtest("signature_too_large"))
18035b88319eSDaniel Borkmann 		signature_too_large();
180499b321ddSDaniel Borkmann 	if (test__start_subtest("signature_zero_size"))
180599b321ddSDaniel Borkmann 		signature_zero_size();
18065b88319eSDaniel Borkmann 	if (test__start_subtest("signature_bad_keyring"))
18075b88319eSDaniel Borkmann 		signature_bad_keyring();
18085b88319eSDaniel Borkmann 	if (test__start_subtest("metadata_ctx_max_entries_ignored"))
18095b88319eSDaniel Borkmann 		metadata_ctx_max_entries_ignored();
18105b88319eSDaniel Borkmann 	if (test__start_subtest("metadata_ctx_initial_value_ignored"))
18115b88319eSDaniel Borkmann 		metadata_ctx_initial_value_ignored();
18125b88319eSDaniel Borkmann 	if (test__start_subtest("signature_authenticates_insns"))
18135b88319eSDaniel Borkmann 		signature_authenticates_insns();
181499b321ddSDaniel Borkmann 	if (test__start_subtest("signature_authenticates_metadata"))
181599b321ddSDaniel Borkmann 		signature_authenticates_metadata();
18165b88319eSDaniel Borkmann 	if (test__start_subtest("hash_requires_frozen"))
18175b88319eSDaniel Borkmann 		hash_requires_frozen();
18185b88319eSDaniel Borkmann 	if (test__start_subtest("no_update_after_freeze"))
18195b88319eSDaniel Borkmann 		no_update_after_freeze();
18205b88319eSDaniel Borkmann 	if (test__start_subtest("freeze_writable_mmap"))
18215b88319eSDaniel Borkmann 		freeze_writable_mmap();
18225b88319eSDaniel Borkmann 	if (test__start_subtest("no_writable_mmap_frozen"))
18235b88319eSDaniel Borkmann 		no_writable_mmap_frozen();
18245b88319eSDaniel Borkmann 	if (test__start_subtest("map_hash_matches_libbpf"))
18255b88319eSDaniel Borkmann 		map_hash_matches_libbpf();
18265b88319eSDaniel Borkmann 	if (test__start_subtest("map_hash_multi_element"))
18275b88319eSDaniel Borkmann 		map_hash_multi_element();
18285b88319eSDaniel Borkmann 	if (test__start_subtest("map_hash_bad_size"))
18295b88319eSDaniel Borkmann 		map_hash_bad_size();
18305b88319eSDaniel Borkmann 	if (test__start_subtest("map_hash_unsupported_type"))
18315b88319eSDaniel Borkmann 		map_hash_unsupported_type();
18328ddce416SDaniel Borkmann 	if (test__start_subtest("lsm_signature_verdict"))
18338ddce416SDaniel Borkmann 		lsm_signature_verdict();
183499b321ddSDaniel Borkmann 	if (test__start_subtest("signed_no_fd_array"))
183599b321ddSDaniel Borkmann 		signed_no_fd_array();
183699b321ddSDaniel Borkmann 	if (test__start_subtest("signed_map_by_fd_rejected"))
183799b321ddSDaniel Borkmann 		signed_map_by_fd_rejected();
183899b321ddSDaniel Borkmann 	if (test__start_subtest("signed_sparse_fd_array_rejected"))
183999b321ddSDaniel Borkmann 		signed_sparse_fd_array_rejected();
18405b88319eSDaniel Borkmann }
1841