]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
bpf: Implement signature verification for BPF programs
authorKP Singh <kpsingh@kernel.org>
Sun, 21 Sep 2025 16:01:16 +0000 (18:01 +0200)
committerAlexei Starovoitov <ast@kernel.org>
Tue, 23 Sep 2025 01:58:03 +0000 (18:58 -0700)
This patch extends the BPF_PROG_LOAD command by adding three new fields
to `union bpf_attr` in the user-space API:

  - signature: A pointer to the signature blob.
  - signature_size: The size of the signature blob.
  - keyring_id: The serial number of a loaded kernel keyring (e.g.,
    the user or session keyring) containing the trusted public keys.

When a BPF program is loaded with a signature, the kernel:

1.  Retrieves the trusted keyring using the provided `keyring_id`.
2.  Verifies the supplied signature against the BPF program's
    instruction buffer.
3.  If the signature is valid and was generated by a key in the trusted
    keyring, the program load proceeds.
4.  If no signature is provided, the load proceeds as before, allowing
    for backward compatibility. LSMs can chose to restrict unsigned
    programs and implement a security policy.
5.  If signature verification fails for any reason,
    the program is not loaded.

Tested-by: syzbot@syzkaller.appspotmail.com
Signed-off-by: KP Singh <kpsingh@kernel.org>
Link: https://lore.kernel.org/r/20250921160120.9711-2-kpsingh@kernel.org
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
crypto/asymmetric_keys/pkcs7_verify.c
include/linux/verification.h
include/uapi/linux/bpf.h
kernel/bpf/helpers.c
kernel/bpf/syscall.c
tools/include/uapi/linux/bpf.h
tools/lib/bpf/bpf.c

index f0d4ff3c20a83275d5161e8e5c8d87793aa46ae7..6d6475e3a9bf2bd531b76c53ca00bdd149cd42b9 100644 (file)
@@ -429,6 +429,7 @@ int pkcs7_verify(struct pkcs7_message *pkcs7,
                /* Authattr presence checked in parser */
                break;
        case VERIFYING_UNSPECIFIED_SIGNATURE:
+       case VERIFYING_BPF_SIGNATURE:
                if (pkcs7->data_type != OID_data) {
                        pr_warn("Invalid unspecified sig (not pkcs7-data)\n");
                        return -EKEYREJECTED;
index 4f3022d081c31b9b827d1c492341112151a449ec..dec7f2beabfd4b7c1c1c84a7422bec801d515b40 100644 (file)
@@ -36,6 +36,7 @@ enum key_being_used_for {
        VERIFYING_KEY_SIGNATURE,
        VERIFYING_KEY_SELF_SIGNATURE,
        VERIFYING_UNSPECIFIED_SIGNATURE,
+       VERIFYING_BPF_SIGNATURE,
        NR__KEY_BEING_USED_FOR
 };
 #ifdef CONFIG_SYSTEM_DATA_VERIFICATION
index 0987b52d564842ba3617f4f822779135f358ad9b..f3b173e48b0fa53a5e069646713eaa479d7a79bd 100644 (file)
@@ -1611,6 +1611,16 @@ union bpf_attr {
                 * continuous.
                 */
                __u32           fd_array_cnt;
+               /* Pointer to a buffer containing the signature of the BPF
+                * program.
+                */
+               __aligned_u64   signature;
+               /* Size of the signature buffer in bytes. */
+               __u32           signature_size;
+               /* ID of the kernel keyring to be used for signature
+                * verification.
+                */
+               __s32           keyring_id;
        };
 
        struct { /* anonymous struct used by BPF_OBJ_* commands */
index ef4ede8bb74fd89dd915a63660eec84a6fea29b6..969f63f8ca28f7e1576a950d163d14b5d09d9cde 100644 (file)
@@ -3898,7 +3898,7 @@ __bpf_kfunc int bpf_verify_pkcs7_signature(struct bpf_dynptr *data_p,
 
        return verify_pkcs7_signature(data, data_len, sig, sig_len,
                                      trusted_keyring->key,
-                                     VERIFYING_UNSPECIFIED_SIGNATURE, NULL,
+                                     VERIFYING_BPF_SIGNATURE, NULL,
                                      NULL);
 #else
        return -EOPNOTSUPP;
index cf7173b1bb83dcb62c23677f2cdf271a7be79a0b..8a3c3d26f6e21b72da17e46d478ca0b21f8bd5dc 100644 (file)
@@ -39,6 +39,7 @@
 #include <linux/tracepoint.h>
 #include <linux/overflow.h>
 #include <linux/cookie.h>
+#include <linux/verification.h>
 
 #include <net/netfilter/nf_bpf_link.h>
 #include <net/netkit.h>
@@ -2785,8 +2786,44 @@ static bool is_perfmon_prog_type(enum bpf_prog_type prog_type)
        }
 }
 
+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;
+       struct bpf_key *key = NULL;
+       void *sig;
+       int err = 0;
+
+       if (system_keyring_id_check(attr->keyring_id) == 0)
+               key = bpf_lookup_system_key(attr->keyring_id);
+       else
+               key = bpf_lookup_user_key(attr->keyring_id, 0);
+
+       if (!key)
+               return -EINVAL;
+
+       sig = kvmemdup_bpfptr(usig, attr->signature_size);
+       if (IS_ERR(sig)) {
+               bpf_key_put(key);
+               return -ENOMEM;
+       }
+
+       bpf_dynptr_init(&sig_ptr, sig, BPF_DYNPTR_TYPE_LOCAL, 0,
+                       attr->signature_size);
+       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);
+
+       bpf_key_put(key);
+       kvfree(sig);
+       return err;
+}
+
 /* last field in 'union bpf_attr' used by this command */
-#define BPF_PROG_LOAD_LAST_FIELD fd_array_cnt
+#define BPF_PROG_LOAD_LAST_FIELD keyring_id
 
 static int bpf_prog_load(union bpf_attr *attr, bpfptr_t uattr, u32 uattr_size)
 {
@@ -2950,6 +2987,12 @@ static int bpf_prog_load(union bpf_attr *attr, bpfptr_t uattr, u32 uattr_size)
        /* eBPF programs must be GPL compatible to use GPL-ed functions */
        prog->gpl_compatible = license_is_gpl_compatible(license) ? 1 : 0;
 
+       if (attr->signature) {
+               err = bpf_prog_verify_signature(prog, attr, uattr.is_kernel);
+               if (err)
+                       goto free_prog;
+       }
+
        prog->orig_prog = NULL;
        prog->jited = 0;
 
index 0987b52d564842ba3617f4f822779135f358ad9b..f3b173e48b0fa53a5e069646713eaa479d7a79bd 100644 (file)
@@ -1611,6 +1611,16 @@ union bpf_attr {
                 * continuous.
                 */
                __u32           fd_array_cnt;
+               /* Pointer to a buffer containing the signature of the BPF
+                * program.
+                */
+               __aligned_u64   signature;
+               /* Size of the signature buffer in bytes. */
+               __u32           signature_size;
+               /* ID of the kernel keyring to be used for signature
+                * verification.
+                */
+               __s32           keyring_id;
        };
 
        struct { /* anonymous struct used by BPF_OBJ_* commands */
index 19ad7bcf0c2ff3c2609b90010aa7fc5dbb4be25f..339b197972374f02fffa0a72d39f2921e5fe02de 100644 (file)
@@ -240,7 +240,7 @@ int bpf_prog_load(enum bpf_prog_type prog_type,
                  const struct bpf_insn *insns, size_t insn_cnt,
                  struct bpf_prog_load_opts *opts)
 {
-       const size_t attr_sz = offsetofend(union bpf_attr, fd_array_cnt);
+       const size_t attr_sz = offsetofend(union bpf_attr, keyring_id);
        void *finfo = NULL, *linfo = NULL;
        const char *func_info, *line_info;
        __u32 log_size, log_level, attach_prog_fd, attach_btf_obj_fd;