linux-security-module.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH bpf-next 0/2] BPF signature hash chains
@ 2025-09-26 20:30 Blaise Boscaccy
  2025-09-26 20:30 ` [PATCH bpf-next 1/2] bpf: Add hash chain signature support for arbitrary maps Blaise Boscaccy
                   ` (2 more replies)
  0 siblings, 3 replies; 6+ messages in thread
From: Blaise Boscaccy @ 2025-09-26 20:30 UTC (permalink / raw)
  To: bpf, linux-security-module, kpsingh, bboscaccy, paul, kys, ast,
	daniel, andrii, James.Bottomley, wufan

This patchset extends the currently proposed signature verification
patchset
https://lore.kernel.org/linux-security-module/20250813205526.2992911-1-kpsingh@kernel.org/
with hash-chain functionality to verify the contents of arbitrary maps.

The currently proposed loader + map signature verification
scheme—requested by Alexei and KP—is simple to implement and
acceptable if users/admins are satisfied with it. However, verifying
both the loader and the maps offers additional benefits beyond just
verifying the loader:

1. Simplified Loader Logic: The lskel loader becomes simpler since it
   doesn’t need to verify program maps—this is already handled by
   bpf_check_signature().

2. Security and Audit Integrity: A key advantage is that the LSM
  (Linux Security Module) hook for authorizing BPF program loads can
  operate after signature verification. This ensures:

  * Access control decisions can be based on verified signature status.
  * Accurate system state measurement and logging.
  * Log events claiming a verified signature are fully truthful,
    avoiding misleading entries that only the loader was verified
    while the actual BPF program verification happens later without
    logging.

This approach addresses concerns from users who require strict audit
trails and verification guarantees, especially in security-sensitive
environments.

A working tree with this patchset is being maintained at
https://github.com/blaiseboscaccy/linux/tree/bpf-hash-chains

bpf CI tests passed as well
https://github.com/kernel-patches/bpf/actions/runs/18021422444/


Blaise Boscaccy (2):
  bpf: Add hash chain signature support for arbitrary maps
  selftests/bpf: Enable map verification for some lskel tests

 include/uapi/linux/bpf.h                      |  6 ++
 kernel/bpf/syscall.c                          | 73 ++++++++++++++++++-
 .../bpf/bpftool/Documentation/bpftool-gen.rst |  7 +-
 tools/bpf/bpftool/gen.c                       | 27 ++++++-
 tools/bpf/bpftool/main.c                      |  9 ++-
 tools/bpf/bpftool/main.h                      |  1 +
 tools/bpf/bpftool/sign.c                      | 17 ++++-
 tools/include/uapi/linux/bpf.h                |  6 ++
 tools/lib/bpf/libbpf.h                        |  3 +-
 tools/lib/bpf/skel_internal.h                 |  6 +-
 tools/testing/selftests/bpf/Makefile          | 18 ++++-
 11 files changed, 159 insertions(+), 14 deletions(-)

-- 
2.48.1


^ permalink raw reply	[flat|nested] 6+ messages in thread

* [PATCH bpf-next 1/2] bpf: Add hash chain signature support for arbitrary maps
  2025-09-26 20:30 [PATCH bpf-next 0/2] BPF signature hash chains Blaise Boscaccy
@ 2025-09-26 20:30 ` Blaise Boscaccy
  2025-09-29  9:25   ` Quentin Monnet
  2025-09-26 20:30 ` [PATCH bpf-next 2/2] selftests/bpf: Enable map verification for some lskel tests Blaise Boscaccy
  2025-09-27  5:47 ` [syzbot ci] Re: BPF signature hash chains syzbot ci
  2 siblings, 1 reply; 6+ messages in thread
From: Blaise Boscaccy @ 2025-09-26 20:30 UTC (permalink / raw)
  To: bpf, linux-security-module, kpsingh, bboscaccy, paul, kys, ast,
	daniel, andrii, James.Bottomley, wufan

This patch introduces hash chain support for signature verification of
arbitrary bpf map objects which was described here:
https://lore.kernel.org/linux-security-module/20250721211958.1881379-1-kpsingh@kernel.org/

The UAPI is extended to allow for in-kernel checking of maps passed in
via the fd_array. A hash chain is constructed from the maps, in order
specified by the signature_maps field. The hash chain is terminated
with the hash of the program itself.

Signed-off-by: Blaise Boscaccy <bboscaccy@linux.microsoft.com>
---
 include/uapi/linux/bpf.h                      |  6 ++
 kernel/bpf/syscall.c                          | 73 ++++++++++++++++++-
 .../bpf/bpftool/Documentation/bpftool-gen.rst |  7 +-
 tools/bpf/bpftool/gen.c                       | 27 ++++++-
 tools/bpf/bpftool/main.c                      |  9 ++-
 tools/bpf/bpftool/main.h                      |  1 +
 tools/bpf/bpftool/sign.c                      | 17 ++++-
 tools/include/uapi/linux/bpf.h                |  6 ++
 tools/lib/bpf/libbpf.h                        |  3 +-
 tools/lib/bpf/skel_internal.h                 |  6 +-
 10 files changed, 143 insertions(+), 12 deletions(-)

diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h
index ae83d8649ef1c..a436a2ff49437 100644
--- a/include/uapi/linux/bpf.h
+++ b/include/uapi/linux/bpf.h
@@ -1621,6 +1621,12 @@ union bpf_attr {
 		 * verification.
 		 */
 		__s32		keyring_id;
+		/* Pointer to a buffer containing the maps used in the signature
+		 * hash chain of the BPF program.
+		 */
+		__aligned_u64   signature_maps;
+		/* Size of the signature maps buffer. */
+		__u32		signature_maps_size;
 	};
 
 	struct { /* anonymous struct used by BPF_OBJ_* commands */
diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c
index adb05d235011f..eb5c210639ccf 100644
--- a/kernel/bpf/syscall.c
+++ b/kernel/bpf/syscall.c
@@ -2800,14 +2800,35 @@ static bool is_perfmon_prog_type(enum bpf_prog_type prog_type)
 	}
 }
 
+static inline int bpf_map_get_hash(int map_fd, void *buffer)
+{
+	struct bpf_map *map;
+
+	CLASS(fd, f)(map_fd);
+	map = __bpf_map_get(f);
+	if (IS_ERR(map))
+		return PTR_ERR(map);
+
+	if (!map->ops->map_get_hash)
+		return -EINVAL;
+
+	return map->ops->map_get_hash(map, SHA256_DIGEST_SIZE, buffer);
+}
+
 static int bpf_prog_verify_signature(struct bpf_prog *prog, union bpf_attr *attr,
 				     bool is_kernel)
 {
 	bpfptr_t usig = make_bpfptr(attr->signature, is_kernel);
-	struct bpf_dynptr_kern sig_ptr, insns_ptr;
+	bpfptr_t umaps;
+	struct bpf_dynptr_kern sig_ptr, insns_ptr, hash_ptr;
 	struct bpf_key *key = NULL;
 	void *sig;
+	int *maps;
+	int map_fd;
 	int err = 0;
+	u64 buffer[SHA256_DIGEST_SIZE * 2 / sizeof(u64)];
+	u64 hash[SHA256_DIGEST_SIZE / sizeof(u64)];
+	int n;
 
 	if (system_keyring_id_check(attr->keyring_id) == 0)
 		key = bpf_lookup_system_key(attr->keyring_id);
@@ -2828,16 +2849,60 @@ static int bpf_prog_verify_signature(struct bpf_prog *prog, union bpf_attr *attr
 	bpf_dynptr_init(&insns_ptr, prog->insnsi, BPF_DYNPTR_TYPE_LOCAL, 0,
 			prog->len * sizeof(struct bpf_insn));
 
-	err = bpf_verify_pkcs7_signature((struct bpf_dynptr *)&insns_ptr,
-					 (struct bpf_dynptr *)&sig_ptr, key);
+	if (!attr->signature_maps_size) {
+		err = bpf_verify_pkcs7_signature((struct bpf_dynptr *)&insns_ptr,
+						 (struct bpf_dynptr *)&sig_ptr, key);
+	} else {
+		bpf_dynptr_init(&hash_ptr, hash, BPF_DYNPTR_TYPE_LOCAL, 0,
+				sizeof(hash));
+		umaps = make_bpfptr(attr->signature_maps, is_kernel);
+		maps = kvmemdup_bpfptr(umaps, attr->signature_maps_size * sizeof(*maps));
+		if (!maps) {
+			err = -ENOMEM;
+			goto out;
+		}
+		/* Process the map array in reverse order to generate a hash chain
+		 * h(n) = sha256(h(n + 1), sha256(map(n)))
+		 * h(n_len) = sha256(map(n_len))
+		 */
+		for (n = attr->signature_maps_size - 1; n >= 0; n--) {
+			err = copy_from_bpfptr_offset(&map_fd,
+						      make_bpfptr(attr->fd_array, is_kernel),
+						      maps[n] * sizeof(map_fd),
+						      sizeof(map_fd));
+			if (err)
+				goto free_maps;
+
+			if (n == attr->signature_maps_size - 1)
+				err = bpf_map_get_hash(map_fd, hash);
+			else {
+				memcpy(buffer, hash, sizeof(hash));
+				err = bpf_map_get_hash(map_fd, buffer + ARRAY_SIZE(hash));
+				sha256((u8 *)buffer, sizeof(buffer), (u8 *)&hash);
+			}
+			if (err)
+				goto free_maps;
+		}
+		/* Calculate final hash with program instructions
+		 * f_hash = sha256(sha256(prog), h(0))
+		 */
+		sha256((u8 *)prog->insnsi, prog->len * sizeof(struct bpf_insn), (u8 *)&buffer);
+		memcpy(buffer + ARRAY_SIZE(hash), hash, sizeof(hash));
+		sha256((u8 *)buffer, sizeof(buffer), (u8 *)&hash);
+		err = bpf_verify_pkcs7_signature((struct bpf_dynptr *)&hash_ptr,
+						 (struct bpf_dynptr *)&sig_ptr, key);
 
+free_maps:
+		kvfree(maps);
+	}
+out:
 	bpf_key_put(key);
 	kvfree(sig);
 	return err;
 }
 
 /* last field in 'union bpf_attr' used by this command */
-#define BPF_PROG_LOAD_LAST_FIELD keyring_id
+#define BPF_PROG_LOAD_LAST_FIELD signature_maps_size
 
 static int bpf_prog_load(union bpf_attr *attr, bpfptr_t uattr, u32 uattr_size)
 {
diff --git a/tools/bpf/bpftool/Documentation/bpftool-gen.rst b/tools/bpf/bpftool/Documentation/bpftool-gen.rst
index d0a36f442db72..b632ab87adf20 100644
--- a/tools/bpf/bpftool/Documentation/bpftool-gen.rst
+++ b/tools/bpf/bpftool/Documentation/bpftool-gen.rst
@@ -16,7 +16,7 @@ SYNOPSIS
 
 **bpftool** [*OPTIONS*] **gen** *COMMAND*
 
-*OPTIONS* := { |COMMON_OPTIONS| | { **-L** | **--use-loader** } | [ { **-S** | **--sign** } {**-k** <private_key.pem>} **-i** <certificate.x509> ] }
+*OPTIONS* := { |COMMON_OPTIONS| | { **-L** | **--use-loader** } | [ { **-S** | **--sign** } { **-M** | **--sign-maps** } {**-k** <private_key.pem>} **-i** <certificate.x509> ] }
 
 *COMMAND* := { **object** | **skeleton** | **help** }
 
@@ -190,6 +190,11 @@ OPTIONS
     For skeletons, generate a signed skeleton. This option must be used with
     **-k** and **-i**. Using this flag implicitly enables **--use-loader**.
 
+-M --sign-maps
+    For skeletons, generate a signed skeleton that includes a hash chain for the
+    skeletons maps. This option must be used with **-k** and **-i**. Using this
+    flag implicitly enables **--use-loader** and **--sign**.
+
 -k <private_key.pem>
     Path to the private key file in PEM format, required for signing.
 
diff --git a/tools/bpf/bpftool/gen.c b/tools/bpf/bpftool/gen.c
index 993c7d9484a46..1c4278e2a662b 100644
--- a/tools/bpf/bpftool/gen.c
+++ b/tools/bpf/bpftool/gen.c
@@ -699,6 +699,9 @@ static int gen_trace(struct bpf_object *obj, const char *obj_name, const char *h
 	if (sign_progs)
 		opts.gen_hash = true;
 
+	if (sign_maps)
+		opts.sign_maps = true;
+
 	err = bpf_object__gen_loader(obj, &opts);
 	if (err)
 		return err;
@@ -793,6 +796,8 @@ static int gen_trace(struct bpf_object *obj, const char *obj_name, const char *h
 	if (sign_progs) {
 		sopts.insns = opts.insns;
 		sopts.insns_sz = opts.insns_sz;
+		sopts.data = opts.data;
+		sopts.data_sz = opts.data_sz;
 		sopts.excl_prog_hash = prog_sha;
 		sopts.excl_prog_hash_sz = sizeof(prog_sha);
 		sopts.signature = sig_buf;
@@ -822,6 +827,13 @@ static int gen_trace(struct bpf_object *obj, const char *obj_name, const char *h
 		\n\
 		\";\n");
 
+		if (sign_maps) {
+			codegen("\
+			\n\
+				static const int opts_signature_maps[1] __attribute__((__aligned__(8))) = {0}; \n\
+			");
+		}
+
 		codegen("\
 		\n\
 			opts.signature = (void *)opts_sig;			\n\
@@ -830,6 +842,19 @@ static int gen_trace(struct bpf_object *obj, const char *obj_name, const char *h
 			opts.excl_prog_hash_sz = sizeof(opts_excl_hash) - 1;	\n\
 			opts.keyring_id = skel->keyring_id;			\n\
 		");
+		if (sign_maps) {
+			codegen("\
+			\n\
+				opts.signature_maps = (void *)opts_signature_maps;	\n\
+				opts.signature_maps_sz = 1; 				\n\
+			");
+		} else {
+			codegen("\
+			\n\
+				opts.signature_maps = (void *)NULL;		\n\
+				opts.signature_maps_sz = 0;			\n\
+			");
+		}
 	}
 
 	codegen("\
@@ -1990,7 +2015,7 @@ static int do_help(int argc, char **argv)
 		"       %1$s %2$s help\n"
 		"\n"
 		"       " HELP_SPEC_OPTIONS " |\n"
-		"                    {-L|--use-loader} | [ {-S|--sign } {-k} <private_key.pem> {-i} <certificate.x509> ]}\n"
+		"                    {-L|--use-loader} | [ {-S|--sign } {-M|--sign-maps } {-k} <private_key.pem> {-i} <certificate.x509> ]}\n"
 		"",
 		bin_name, "gen");
 
diff --git a/tools/bpf/bpftool/main.c b/tools/bpf/bpftool/main.c
index a829a6a49037a..47b14dcbae4ee 100644
--- a/tools/bpf/bpftool/main.c
+++ b/tools/bpf/bpftool/main.c
@@ -34,6 +34,7 @@ bool use_loader;
 struct btf *base_btf;
 struct hashmap *refs_table;
 bool sign_progs;
+bool sign_maps;
 const char *private_key_path;
 const char *cert_path;
 
@@ -452,6 +453,7 @@ int main(int argc, char **argv)
 		{ "debug",	no_argument,	NULL,	'd' },
 		{ "use-loader",	no_argument,	NULL,	'L' },
 		{ "sign",	no_argument,	NULL,	'S' },
+		{ "sign-maps",	no_argument,	NULL,	'M' },
 		{ "base-btf",	required_argument, NULL, 'B' },
 		{ 0 }
 	};
@@ -478,7 +480,7 @@ int main(int argc, char **argv)
 	bin_name = "bpftool";
 
 	opterr = 0;
-	while ((opt = getopt_long(argc, argv, "VhpjfLmndSi:k:B:l",
+	while ((opt = getopt_long(argc, argv, "VhpjfLmndSMi:k:B:l",
 				  options, NULL)) >= 0) {
 		switch (opt) {
 		case 'V':
@@ -528,6 +530,11 @@ int main(int argc, char **argv)
 			sign_progs = true;
 			use_loader = true;
 			break;
+		case 'M':
+			sign_maps = true;
+			sign_progs = true;
+			use_loader = true;
+			break;
 		case 'k':
 			private_key_path = optarg;
 			break;
diff --git a/tools/bpf/bpftool/main.h b/tools/bpf/bpftool/main.h
index 1130299cede0b..d4e8b39d97746 100644
--- a/tools/bpf/bpftool/main.h
+++ b/tools/bpf/bpftool/main.h
@@ -92,6 +92,7 @@ extern bool use_loader;
 extern struct btf *base_btf;
 extern struct hashmap *refs_table;
 extern bool sign_progs;
+extern bool sign_maps;
 extern const char *private_key_path;
 extern const char *cert_path;
 
diff --git a/tools/bpf/bpftool/sign.c b/tools/bpf/bpftool/sign.c
index b29d825bb1d41..50986c716292e 100644
--- a/tools/bpf/bpftool/sign.c
+++ b/tools/bpf/bpftool/sign.c
@@ -24,6 +24,7 @@
 #include <errno.h>
 
 #include <bpf/skel_internal.h>
+#include <bpf/libbpf_internal.h>
 
 #include "main.h"
 
@@ -131,8 +132,18 @@ int bpftool_prog_sign(struct bpf_load_and_run_opts *opts)
 	long actual_sig_len = 0;
 	X509 *x509 = NULL;
 	int err = 0;
-
-	bd_in = BIO_new_mem_buf(opts->insns, opts->insns_sz);
+	unsigned char hash[SHA256_DIGEST_LENGTH  * 2];
+	unsigned char term[SHA256_DIGEST_LENGTH];
+
+	if (sign_maps) {
+		libbpf_sha256(opts->insns, opts->insns_sz, hash, SHA256_DIGEST_LENGTH);
+		libbpf_sha256(opts->data, opts->data_sz, hash + SHA256_DIGEST_LENGTH,
+			      SHA256_DIGEST_LENGTH);
+		libbpf_sha256(hash, sizeof(hash), term, sizeof(term));
+		bd_in = BIO_new_mem_buf(term, sizeof(term));
+	} else {
+		bd_in = BIO_new_mem_buf(opts->insns, opts->insns_sz);
+	}
 	if (!bd_in) {
 		err = -ENOMEM;
 		goto cleanup;
@@ -173,7 +184,7 @@ int bpftool_prog_sign(struct bpf_load_and_run_opts *opts)
 	EVP_Digest(opts->insns, opts->insns_sz, opts->excl_prog_hash,
 		   &opts->excl_prog_hash_sz, EVP_sha256(), NULL);
 
-		bd_out = BIO_new(BIO_s_mem());
+	bd_out = BIO_new(BIO_s_mem());
 	if (!bd_out) {
 		err = -ENOMEM;
 		goto cleanup;
diff --git a/tools/include/uapi/linux/bpf.h b/tools/include/uapi/linux/bpf.h
index ae83d8649ef1c..a436a2ff49437 100644
--- a/tools/include/uapi/linux/bpf.h
+++ b/tools/include/uapi/linux/bpf.h
@@ -1621,6 +1621,12 @@ union bpf_attr {
 		 * verification.
 		 */
 		__s32		keyring_id;
+		/* Pointer to a buffer containing the maps used in the signature
+		 * hash chain of the BPF program.
+		 */
+		__aligned_u64   signature_maps;
+		/* Size of the signature maps buffer. */
+		__u32		signature_maps_size;
 	};
 
 	struct { /* anonymous struct used by BPF_OBJ_* commands */
diff --git a/tools/lib/bpf/libbpf.h b/tools/lib/bpf/libbpf.h
index 5118d0a90e243..63946bdad41ad 100644
--- a/tools/lib/bpf/libbpf.h
+++ b/tools/lib/bpf/libbpf.h
@@ -1858,9 +1858,10 @@ struct gen_loader_opts {
 	__u32 data_sz;
 	__u32 insns_sz;
 	bool gen_hash;
+	bool sign_maps;
 };
 
-#define gen_loader_opts__last_field gen_hash
+#define gen_loader_opts__last_field sign_maps
 LIBBPF_API int bpf_object__gen_loader(struct bpf_object *obj,
 				      struct gen_loader_opts *opts);
 
diff --git a/tools/lib/bpf/skel_internal.h b/tools/lib/bpf/skel_internal.h
index 6a8f5c7a02eb9..11c2c19a5b2a4 100644
--- a/tools/lib/bpf/skel_internal.h
+++ b/tools/lib/bpf/skel_internal.h
@@ -74,6 +74,8 @@ struct bpf_load_and_run_opts {
 	__s32 keyring_id;
 	void *excl_prog_hash;
 	__u32 excl_prog_hash_sz;
+	const int *signature_maps;
+	__u32 signature_maps_sz;
 };
 
 long kern_sys_bpf(__u32 cmd, void *attr, __u32 attr_size);
@@ -352,7 +354,7 @@ static inline int skel_map_freeze(int fd)
 
 static inline int bpf_load_and_run(struct bpf_load_and_run_opts *opts)
 {
-	const size_t prog_load_attr_sz = offsetofend(union bpf_attr, keyring_id);
+	const size_t prog_load_attr_sz = offsetofend(union bpf_attr, signature_maps_size);
 	const size_t test_run_attr_sz = offsetofend(union bpf_attr, test);
 	int map_fd = -1, prog_fd = -1, key = 0, err;
 	union bpf_attr attr;
@@ -395,6 +397,8 @@ static inline int bpf_load_and_run(struct bpf_load_and_run_opts *opts)
 #ifndef __KERNEL__
 	attr.signature = (long) opts->signature;
 	attr.signature_size = opts->signature_sz;
+	attr.signature_maps = (long) opts->signature_maps;
+	attr.signature_maps_size = opts->signature_maps_sz;
 #else
 	if (opts->signature || opts->signature_sz)
 		pr_warn("signatures are not supported from bpf_preload\n");
-- 
2.48.1


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [PATCH bpf-next 2/2] selftests/bpf: Enable map verification for some lskel tests
  2025-09-26 20:30 [PATCH bpf-next 0/2] BPF signature hash chains Blaise Boscaccy
  2025-09-26 20:30 ` [PATCH bpf-next 1/2] bpf: Add hash chain signature support for arbitrary maps Blaise Boscaccy
@ 2025-09-26 20:30 ` Blaise Boscaccy
  2025-09-27  5:47 ` [syzbot ci] Re: BPF signature hash chains syzbot ci
  2 siblings, 0 replies; 6+ messages in thread
From: Blaise Boscaccy @ 2025-09-26 20:30 UTC (permalink / raw)
  To: bpf, linux-security-module, kpsingh, bboscaccy, paul, kys, ast,
	daniel, andrii, James.Bottomley, wufan

Convert an existing signed lskel test to use the newly introduced map
signature hash-chain support added to libbpf.

Signed-off-by: Blaise Boscaccy <bboscaccy@linux.microsoft.com>
---
 tools/testing/selftests/bpf/Makefile | 18 ++++++++++++++++--
 1 file changed, 16 insertions(+), 2 deletions(-)

diff --git a/tools/testing/selftests/bpf/Makefile b/tools/testing/selftests/bpf/Makefile
index 0b6ee902bce51..dab70f6fa6ad8 100644
--- a/tools/testing/selftests/bpf/Makefile
+++ b/tools/testing/selftests/bpf/Makefile
@@ -500,12 +500,14 @@ LSKELS := fexit_sleep.c trace_printk.c trace_vprintk.c map_ptr_kern.c 	\
 	core_kern.c core_kern_overflow.c test_ringbuf.c			\
 	test_ringbuf_n.c test_ringbuf_map_key.c test_ringbuf_write.c
 
-LSKELS_SIGNED := fentry_test.c fexit_test.c atomics.c
+LSKELS_SIGNED := fentry_test.c fexit_test.c
+
+LSKELS_SIGNED_MAPS := atomics.c
 
 # Generate both light skeleton and libbpf skeleton for these
 LSKELS_EXTRA := test_ksyms_module.c test_ksyms_weak.c kfunc_call_test.c \
 	kfunc_call_test_subprog.c
-SKEL_BLACKLIST += $$(LSKELS) $$(LSKELS_SIGNED)
+SKEL_BLACKLIST += $$(LSKELS) $$(LSKELS_SIGNED) $$(LSKELS_SIGNED_MAPS)
 
 test_static_linked.skel.h-deps := test_static_linked1.bpf.o test_static_linked2.bpf.o
 linked_funcs.skel.h-deps := linked_funcs1.bpf.o linked_funcs2.bpf.o
@@ -537,6 +539,7 @@ HEADERS_FOR_BPF_OBJS := $(wildcard $(BPFDIR)/*.bpf.h)		\
 define DEFINE_TEST_RUNNER
 
 LSKEL_SIGN := -S -k $(PRIVATE_KEY) -i $(VERIFICATION_CERT)
+LSKEL_SIGN_MAPS := -S -M -k $(PRIVATE_KEY) -i $(VERIFICATION_CERT)
 TRUNNER_OUTPUT := $(OUTPUT)$(if $2,/)$2
 TRUNNER_BINARY := $1$(if $2,-)$2
 TRUNNER_TEST_OBJS := $$(patsubst %.c,$$(TRUNNER_OUTPUT)/%.test.o,	\
@@ -553,6 +556,7 @@ TRUNNER_BPF_SKELS := $$(patsubst %.c,$$(TRUNNER_OUTPUT)/%.skel.h,	\
 TRUNNER_BPF_LSKELS := $$(patsubst %.c,$$(TRUNNER_OUTPUT)/%.lskel.h, $$(LSKELS) $$(LSKELS_EXTRA))
 TRUNNER_BPF_SKELS_LINKED := $$(addprefix $$(TRUNNER_OUTPUT)/,$(LINKED_SKELS))
 TRUNNER_BPF_LSKELS_SIGNED := $$(patsubst %.c,$$(TRUNNER_OUTPUT)/%.lskel.h, $$(LSKELS_SIGNED))
+TRUNNER_BPF_LSKELS_SIGNED_MAPS := $$(patsubst %.c,$$(TRUNNER_OUTPUT)/%.lskel.h, $$(LSKELS_SIGNED_MAPS))
 TEST_GEN_FILES += $$(TRUNNER_BPF_OBJS)
 
 # Evaluate rules now with extra TRUNNER_XXX variables above already defined
@@ -616,6 +620,15 @@ $(TRUNNER_BPF_LSKELS_SIGNED): %.lskel.h: %.bpf.o $(BPFTOOL) | $(TRUNNER_OUTPUT)
 	$(Q)$$(BPFTOOL) gen skeleton $(LSKEL_SIGN) $$(<:.o=.llinked3.o) name $$(notdir $$(<:.bpf.o=_lskel)) > $$@
 	$(Q)rm -f $$(<:.o=.llinked1.o) $$(<:.o=.llinked2.o) $$(<:.o=.llinked3.o)
 
+$(TRUNNER_BPF_LSKELS_SIGNED_MAPS): %.lskel.h: %.bpf.o $(BPFTOOL) | $(TRUNNER_OUTPUT)
+	$$(call msg,GEN-SKEL,$(TRUNNER_BINARY) (signed),$$@)
+	$(Q)$$(BPFTOOL) gen object $$(<:.o=.llinked1.o) $$<
+	$(Q)$$(BPFTOOL) gen object $$(<:.o=.llinked2.o) $$(<:.o=.llinked1.o)
+	$(Q)$$(BPFTOOL) gen object $$(<:.o=.llinked3.o) $$(<:.o=.llinked2.o)
+	$(Q)diff $$(<:.o=.llinked2.o) $$(<:.o=.llinked3.o)
+	$(Q)$$(BPFTOOL) gen skeleton $(LSKEL_SIGN_MAPS) $$(<:.o=.llinked3.o) name $$(notdir $$(<:.bpf.o=_lskel)) > $$@
+	$(Q)rm -f $$(<:.o=.llinked1.o) $$(<:.o=.llinked2.o) $$(<:.o=.llinked3.o)
+
 $(LINKED_BPF_OBJS): %: $(TRUNNER_OUTPUT)/%
 
 # .SECONDEXPANSION here allows to correctly expand %-deps variables as prerequisites
@@ -666,6 +679,7 @@ $(TRUNNER_TEST_OBJS:.o=.d): $(TRUNNER_OUTPUT)/%.test.d:			\
 			    $(TRUNNER_BPF_SKELS)			\
 			    $(TRUNNER_BPF_LSKELS)			\
 			    $(TRUNNER_BPF_LSKELS_SIGNED)		\
+			    $(TRUNNER_BPF_LSKELS_SIGNED_MAPS)		\
 			    $(TRUNNER_BPF_SKELS_LINKED)			\
 			    $$(BPFOBJ) | $(TRUNNER_OUTPUT)
 
-- 
2.48.1


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [syzbot ci] Re: BPF signature hash chains
  2025-09-26 20:30 [PATCH bpf-next 0/2] BPF signature hash chains Blaise Boscaccy
  2025-09-26 20:30 ` [PATCH bpf-next 1/2] bpf: Add hash chain signature support for arbitrary maps Blaise Boscaccy
  2025-09-26 20:30 ` [PATCH bpf-next 2/2] selftests/bpf: Enable map verification for some lskel tests Blaise Boscaccy
@ 2025-09-27  5:47 ` syzbot ci
  2 siblings, 0 replies; 6+ messages in thread
From: syzbot ci @ 2025-09-27  5:47 UTC (permalink / raw)
  To: andrii, ast, bboscaccy, bpf, daniel, james.bottomley, kpsingh,
	kys, linux-security-module, paul, wufan
  Cc: syzbot, syzkaller-bugs

syzbot ci has tested the following series

[v1] BPF signature hash chains
https://lore.kernel.org/all/20250926203111.1305999-1-bboscaccy@linux.microsoft.com
* [PATCH bpf-next 1/2] bpf: Add hash chain signature support for arbitrary maps
* [PATCH bpf-next 2/2] selftests/bpf: Enable map verification for some lskel tests

and found the following issue:
general protection fault in bpf_prog_verify_signature

Full report is available here:
https://ci.syzbot.org/series/9d93d1cd-d2bd-4967-a631-f3e84ee6fb41

***

general protection fault in bpf_prog_verify_signature

tree:      bpf-next
URL:       https://kernel.googlesource.com/pub/scm/linux/kernel/git/bpf/bpf-next.git
base:      d43029ff7d1b7183dc0cf11b6cc2c12a0b810ad8
arch:      amd64
compiler:  Debian clang version 20.1.8 (++20250708063551+0c9f909b7976-1~exp1~20250708183702.136), Debian LLD 20.1.8
config:    https://ci.syzbot.org/builds/a6b64e3c-6f08-4a6d-b484-ece99910a3f0/config
C repro:   https://ci.syzbot.org/findings/a6c59e03-c4f7-4752-8d4f-526ccf945b7c/c_repro
syz repro: https://ci.syzbot.org/findings/a6c59e03-c4f7-4752-8d4f-526ccf945b7c/syz_repro

Oops: general protection fault, probably for non-canonical address 0xdffffc00020244a9: 0000 [#1] SMP KASAN PTI
KASAN: probably user-memory-access in range [0x0000000010122548-0x000000001012254f]
CPU: 1 UID: 0 PID: 6002 Comm: syz.0.17 Not tainted syzkaller #0 PREEMPT(full) 
Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 1.16.2-debian-1.16.2-1 04/01/2014
RIP: 0010:bpf_prog_verify_signature+0x610/0xc40 kernel/bpf/syscall.c:2873
Code: f7 e8 04 73 52 00 4d 8b 2e 89 d8 25 ff ff ff 7f 48 8b 4c 24 10 4c 8d 34 81 4c 89 f0 48 c1 e8 03 48 b9 00 00 00 00 00 fc ff df <0f> b6 04 08 84 c0 0f 85 c9 03 00 00 49 63 06 4c 8d 34 85 00 00 00
RSP: 0018:ffffc90002bff920 EFLAGS: 00010206
RAX: 00000000020244a9 RBX: 0000000004048956 RCX: dffffc0000000000
RDX: 0000000000000000 RSI: 0000000000000000 RDI: 0000000000000000
RBP: ffffc90002bffb30 R08: ffffffff8fa3b537 R09: 1ffffffff1f476a6
R10: dffffc0000000000 R11: fffffbfff1f476a7 R12: 1ffff9200057ff34
R13: 0000000000000000 R14: 000000001012254a R15: 0000000080000000
FS:  0000555591f35500(0000) GS:ffff8881a3c11000(0000) knlGS:0000000000000000
CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 0000000000000000 CR3: 000000011081a000 CR4: 00000000000006f0
Call Trace:
 <TASK>
 bpf_prog_load+0xcc7/0x19e0 kernel/bpf/syscall.c:3072
 __sys_bpf+0x4ff/0x850 kernel/bpf/syscall.c:6199
 __do_sys_bpf kernel/bpf/syscall.c:6309 [inline]
 __se_sys_bpf kernel/bpf/syscall.c:6307 [inline]
 __x64_sys_bpf+0x7c/0x90 kernel/bpf/syscall.c:6307
 do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
 do_syscall_64+0xfa/0x3b0 arch/x86/entry/syscall_64.c:94
 entry_SYSCALL_64_after_hwframe+0x77/0x7f
RIP: 0033:0x7feeb318ec29
Code: ff ff c3 66 2e 0f 1f 84 00 00 00 00 00 0f 1f 40 00 48 89 f8 48 89 f7 48 89 d6 48 89 ca 4d 89 c2 4d 89 c8 4c 8b 4c 24 08 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 c7 c1 a8 ff ff ff f7 d8 64 89 01 48
RSP: 002b:00007fffd613f7e8 EFLAGS: 00000246 ORIG_RAX: 0000000000000141
RAX: ffffffffffffffda RBX: 00007feeb33d5fa0 RCX: 00007feeb318ec29
RDX: 00000000000000b9 RSI: 0000200000000100 RDI: 0000000000000005
RBP: 00007feeb3211e41 R08: 0000000000000000 R09: 0000000000000000
R10: 0000000000000000 R11: 0000000000000246 R12: 0000000000000000
R13: 00007feeb33d5fa0 R14: 00007feeb33d5fa0 R15: 0000000000000003
 </TASK>
Modules linked in:
---[ end trace 0000000000000000 ]---
RIP: 0010:bpf_prog_verify_signature+0x610/0xc40 kernel/bpf/syscall.c:2873
Code: f7 e8 04 73 52 00 4d 8b 2e 89 d8 25 ff ff ff 7f 48 8b 4c 24 10 4c 8d 34 81 4c 89 f0 48 c1 e8 03 48 b9 00 00 00 00 00 fc ff df <0f> b6 04 08 84 c0 0f 85 c9 03 00 00 49 63 06 4c 8d 34 85 00 00 00
RSP: 0018:ffffc90002bff920 EFLAGS: 00010206
RAX: 00000000020244a9 RBX: 0000000004048956 RCX: dffffc0000000000
RDX: 0000000000000000 RSI: 0000000000000000 RDI: 0000000000000000
RBP: ffffc90002bffb30 R08: ffffffff8fa3b537 R09: 1ffffffff1f476a6
R10: dffffc0000000000 R11: fffffbfff1f476a7 R12: 1ffff9200057ff34
R13: 0000000000000000 R14: 000000001012254a R15: 0000000080000000
FS:  0000555591f35500(0000) GS:ffff8881a3c11000(0000) knlGS:0000000000000000
CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 0000000000000000 CR3: 000000011081a000 CR4: 00000000000006f0
----------------
Code disassembly (best guess):
   0:	f7 e8                	imul   %eax
   2:	04 73                	add    $0x73,%al
   4:	52                   	push   %rdx
   5:	00 4d 8b             	add    %cl,-0x75(%rbp)
   8:	2e 89 d8             	cs mov %ebx,%eax
   b:	25 ff ff ff 7f       	and    $0x7fffffff,%eax
  10:	48 8b 4c 24 10       	mov    0x10(%rsp),%rcx
  15:	4c 8d 34 81          	lea    (%rcx,%rax,4),%r14
  19:	4c 89 f0             	mov    %r14,%rax
  1c:	48 c1 e8 03          	shr    $0x3,%rax
  20:	48 b9 00 00 00 00 00 	movabs $0xdffffc0000000000,%rcx
  27:	fc ff df
* 2a:	0f b6 04 08          	movzbl (%rax,%rcx,1),%eax <-- trapping instruction
  2e:	84 c0                	test   %al,%al
  30:	0f 85 c9 03 00 00    	jne    0x3ff
  36:	49 63 06             	movslq (%r14),%rax
  39:	4c                   	rex.WR
  3a:	8d                   	.byte 0x8d
  3b:	34 85                	xor    $0x85,%al
  3d:	00 00                	add    %al,(%rax)


***

If these findings have caused you to resend the series or submit a
separate fix, please add the following tag to your commit message:
  Tested-by: syzbot@syzkaller.appspotmail.com

---
This report is generated by a bot. It may contain errors.
syzbot ci engineers can be reached at syzkaller@googlegroups.com.

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH bpf-next 1/2] bpf: Add hash chain signature support for arbitrary maps
  2025-09-26 20:30 ` [PATCH bpf-next 1/2] bpf: Add hash chain signature support for arbitrary maps Blaise Boscaccy
@ 2025-09-29  9:25   ` Quentin Monnet
  2025-09-29 19:17     ` Blaise Boscaccy
  0 siblings, 1 reply; 6+ messages in thread
From: Quentin Monnet @ 2025-09-29  9:25 UTC (permalink / raw)
  To: Blaise Boscaccy, bpf, linux-security-module, kpsingh, paul, kys,
	ast, daniel, andrii, James.Bottomley, wufan

2025-09-26 13:30 UTC-0700 ~ Blaise Boscaccy <bboscaccy@linux.microsoft.com>
> This patch introduces hash chain support for signature verification of
> arbitrary bpf map objects which was described here:
> https://lore.kernel.org/linux-security-module/20250721211958.1881379-1-kpsingh@kernel.org/
> 
> The UAPI is extended to allow for in-kernel checking of maps passed in
> via the fd_array. A hash chain is constructed from the maps, in order
> specified by the signature_maps field. The hash chain is terminated
> with the hash of the program itself.
> 
> Signed-off-by: Blaise Boscaccy <bboscaccy@linux.microsoft.com>
> ---
>  include/uapi/linux/bpf.h                      |  6 ++
>  kernel/bpf/syscall.c                          | 73 ++++++++++++++++++-
>  .../bpf/bpftool/Documentation/bpftool-gen.rst |  7 +-
>  tools/bpf/bpftool/gen.c                       | 27 ++++++-
>  tools/bpf/bpftool/main.c                      |  9 ++-
>  tools/bpf/bpftool/main.h                      |  1 +
>  tools/bpf/bpftool/sign.c                      | 17 ++++-
>  tools/include/uapi/linux/bpf.h                |  6 ++
>  tools/lib/bpf/libbpf.h                        |  3 +-
>  tools/lib/bpf/skel_internal.h                 |  6 +-
>  10 files changed, 143 insertions(+), 12 deletions(-)
> 

[...]

> diff --git a/tools/bpf/bpftool/Documentation/bpftool-gen.rst b/tools/bpf/bpftool/Documentation/bpftool-gen.rst
> index d0a36f442db72..b632ab87adf20 100644
> --- a/tools/bpf/bpftool/Documentation/bpftool-gen.rst
> +++ b/tools/bpf/bpftool/Documentation/bpftool-gen.rst
> @@ -16,7 +16,7 @@ SYNOPSIS
>  
>  **bpftool** [*OPTIONS*] **gen** *COMMAND*
>  
> -*OPTIONS* := { |COMMON_OPTIONS| | { **-L** | **--use-loader** } | [ { **-S** | **--sign** } {**-k** <private_key.pem>} **-i** <certificate.x509> ] }
> +*OPTIONS* := { |COMMON_OPTIONS| | { **-L** | **--use-loader** } | [ { **-S** | **--sign** } { **-M** | **--sign-maps** } {**-k** <private_key.pem>} **-i** <certificate.x509> ] }
>  
>  *COMMAND* := { **object** | **skeleton** | **help** }
>  
> @@ -190,6 +190,11 @@ OPTIONS
>      For skeletons, generate a signed skeleton. This option must be used with
>      **-k** and **-i**. Using this flag implicitly enables **--use-loader**.
>  
> +-M --sign-maps
> +    For skeletons, generate a signed skeleton that includes a hash chain for the
> +    skeletons maps. This option must be used with **-k** and **-i**. Using this
> +    flag implicitly enables **--use-loader** and **--sign**.
> +


Hi! Pardon my ignorance, I haven't followed all the details of the
discussions around signing. Is there a use case for signing the programs
only (using -S) without signing the maps (using -M)? Or should we
consider collapsing maps signing under the existing -S option?

If you do keep the new option, would you mind updating the bash
completion file, please? Simply adding the long form like this:

------

diff --git i/tools/bpf/bpftool/bash-completion/bpftool w/tools/bpf/bpftool/bash-completion/bpftool
index 53bcfeb1a76e..f8c217f09989 100644
--- i/tools/bpf/bpftool/bash-completion/bpftool
+++ w/tools/bpf/bpftool/bash-completion/bpftool
@@ -262,7 +262,7 @@ _bpftool()
     # Deal with options
     if [[ ${words[cword]} == -* ]]; then
         local c='--version --json --pretty --bpffs --mapcompat --debug \
-            --use-loader --base-btf --sign -i -k'
+            --use-loader --base-btf --sign --sign-maps -i -k'
         COMPREPLY=( $( compgen -W "$c" -- "$cur" ) )
         return 0
     fi

------

Other than that, the changes for bpftool look OK from my side. I'd
probably split the patch further into kernel/libbpf/bpftool changes, but
that's your call.

Thanks,
Quentin

^ permalink raw reply related	[flat|nested] 6+ messages in thread

* Re: [PATCH bpf-next 1/2] bpf: Add hash chain signature support for arbitrary maps
  2025-09-29  9:25   ` Quentin Monnet
@ 2025-09-29 19:17     ` Blaise Boscaccy
  0 siblings, 0 replies; 6+ messages in thread
From: Blaise Boscaccy @ 2025-09-29 19:17 UTC (permalink / raw)
  To: Quentin Monnet, bpf, linux-security-module, kpsingh, paul, kys,
	ast, daniel, andrii, James.Bottomley, wufan

Quentin Monnet <qmo@kernel.org> writes:

> 2025-09-26 13:30 UTC-0700 ~ Blaise Boscaccy <bboscaccy@linux.microsoft.com>
>> This patch introduces hash chain support for signature verification of
>> arbitrary bpf map objects which was described here:
>> https://lore.kernel.org/linux-security-module/20250721211958.1881379-1-kpsingh@kernel.org/
>> 
>> The UAPI is extended to allow for in-kernel checking of maps passed in
>> via the fd_array. A hash chain is constructed from the maps, in order
>> specified by the signature_maps field. The hash chain is terminated
>> with the hash of the program itself.
>> 
>> Signed-off-by: Blaise Boscaccy <bboscaccy@linux.microsoft.com>
>> ---
>>  include/uapi/linux/bpf.h                      |  6 ++
>>  kernel/bpf/syscall.c                          | 73 ++++++++++++++++++-
>>  .../bpf/bpftool/Documentation/bpftool-gen.rst |  7 +-
>>  tools/bpf/bpftool/gen.c                       | 27 ++++++-
>>  tools/bpf/bpftool/main.c                      |  9 ++-
>>  tools/bpf/bpftool/main.h                      |  1 +
>>  tools/bpf/bpftool/sign.c                      | 17 ++++-
>>  tools/include/uapi/linux/bpf.h                |  6 ++
>>  tools/lib/bpf/libbpf.h                        |  3 +-
>>  tools/lib/bpf/skel_internal.h                 |  6 +-
>>  10 files changed, 143 insertions(+), 12 deletions(-)
>> 
>
> [...]
>
>> diff --git a/tools/bpf/bpftool/Documentation/bpftool-gen.rst b/tools/bpf/bpftool/Documentation/bpftool-gen.rst
>> index d0a36f442db72..b632ab87adf20 100644
>> --- a/tools/bpf/bpftool/Documentation/bpftool-gen.rst
>> +++ b/tools/bpf/bpftool/Documentation/bpftool-gen.rst
>> @@ -16,7 +16,7 @@ SYNOPSIS
>>  
>>  **bpftool** [*OPTIONS*] **gen** *COMMAND*
>>  
>> -*OPTIONS* := { |COMMON_OPTIONS| | { **-L** | **--use-loader** } | [ { **-S** | **--sign** } {**-k** <private_key.pem>} **-i** <certificate.x509> ] }
>> +*OPTIONS* := { |COMMON_OPTIONS| | { **-L** | **--use-loader** } | [ { **-S** | **--sign** } { **-M** | **--sign-maps** } {**-k** <private_key.pem>} **-i** <certificate.x509> ] }
>>  
>>  *COMMAND* := { **object** | **skeleton** | **help** }
>>  
>> @@ -190,6 +190,11 @@ OPTIONS
>>      For skeletons, generate a signed skeleton. This option must be used with
>>      **-k** and **-i**. Using this flag implicitly enables **--use-loader**.
>>  
>> +-M --sign-maps
>> +    For skeletons, generate a signed skeleton that includes a hash chain for the
>> +    skeletons maps. This option must be used with **-k** and **-i**. Using this
>> +    flag implicitly enables **--use-loader** and **--sign**.
>> +
>
>
> Hi! Pardon my ignorance, I haven't followed all the details of the
> discussions around signing. Is there a use case for signing the programs
> only (using -S) without signing the maps (using -M)? Or should we
> consider collapsing maps signing under the existing -S option?
>

Hi Quentin! Yes, there are some use cases where having both map signing
and program signing doesn't make much sense. For the light-skeleton use
case, where the map contains your actual program, it makes a lot of
sense. For other more dynamic use cases, maps can contain dynamically
generated user data or simply be providing program input and
output. Signing of those maps wouldn't provide much benefit, or even be
practical or possible in some scenarios. 

> If you do keep the new option, would you mind updating the bash
> completion file, please? Simply adding the long form like this:
>

> ------
>
> diff --git i/tools/bpf/bpftool/bash-completion/bpftool w/tools/bpf/bpftool/bash-completion/bpftool
> index 53bcfeb1a76e..f8c217f09989 100644
> --- i/tools/bpf/bpftool/bash-completion/bpftool
> +++ w/tools/bpf/bpftool/bash-completion/bpftool
> @@ -262,7 +262,7 @@ _bpftool()
>      # Deal with options
>      if [[ ${words[cword]} == -* ]]; then
>          local c='--version --json --pretty --bpffs --mapcompat --debug \
> -            --use-loader --base-btf --sign -i -k'
> +            --use-loader --base-btf --sign --sign-maps -i -k'
>          COMPREPLY=( $( compgen -W "$c" -- "$cur" ) )
>          return 0
>      fi
>
> ------

Of course. Thanks for the diff. I'll get that incorporated. 

>
> Other than that, the changes for bpftool look OK from my side. I'd
> probably split the patch further into kernel/libbpf/bpftool changes, but
> that's your call.
>

Sure, I'll get the userspace and kernel changes split out.

-blaise

> Thanks,
> Quentin

^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2025-09-29 19:17 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-09-26 20:30 [PATCH bpf-next 0/2] BPF signature hash chains Blaise Boscaccy
2025-09-26 20:30 ` [PATCH bpf-next 1/2] bpf: Add hash chain signature support for arbitrary maps Blaise Boscaccy
2025-09-29  9:25   ` Quentin Monnet
2025-09-29 19:17     ` Blaise Boscaccy
2025-09-26 20:30 ` [PATCH bpf-next 2/2] selftests/bpf: Enable map verification for some lskel tests Blaise Boscaccy
2025-09-27  5:47 ` [syzbot ci] Re: BPF signature hash chains syzbot ci

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).