Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

bpf: Implement signature verification for BPF programs

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>

authored by

KP Singh and committed by
Alexei Starovoitov
34927156 5a427fdd

+68 -3
+1
crypto/asymmetric_keys/pkcs7_verify.c
··· 429 429 /* Authattr presence checked in parser */ 430 430 break; 431 431 case VERIFYING_UNSPECIFIED_SIGNATURE: 432 + case VERIFYING_BPF_SIGNATURE: 432 433 if (pkcs7->data_type != OID_data) { 433 434 pr_warn("Invalid unspecified sig (not pkcs7-data)\n"); 434 435 return -EKEYREJECTED;
+1
include/linux/verification.h
··· 36 36 VERIFYING_KEY_SIGNATURE, 37 37 VERIFYING_KEY_SELF_SIGNATURE, 38 38 VERIFYING_UNSPECIFIED_SIGNATURE, 39 + VERIFYING_BPF_SIGNATURE, 39 40 NR__KEY_BEING_USED_FOR 40 41 }; 41 42 #ifdef CONFIG_SYSTEM_DATA_VERIFICATION
+10
include/uapi/linux/bpf.h
··· 1611 1611 * continuous. 1612 1612 */ 1613 1613 __u32 fd_array_cnt; 1614 + /* Pointer to a buffer containing the signature of the BPF 1615 + * program. 1616 + */ 1617 + __aligned_u64 signature; 1618 + /* Size of the signature buffer in bytes. */ 1619 + __u32 signature_size; 1620 + /* ID of the kernel keyring to be used for signature 1621 + * verification. 1622 + */ 1623 + __s32 keyring_id; 1614 1624 }; 1615 1625 1616 1626 struct { /* anonymous struct used by BPF_OBJ_* commands */
+1 -1
kernel/bpf/helpers.c
··· 3898 3898 3899 3899 return verify_pkcs7_signature(data, data_len, sig, sig_len, 3900 3900 trusted_keyring->key, 3901 - VERIFYING_UNSPECIFIED_SIGNATURE, NULL, 3901 + VERIFYING_BPF_SIGNATURE, NULL, 3902 3902 NULL); 3903 3903 #else 3904 3904 return -EOPNOTSUPP;
+44 -1
kernel/bpf/syscall.c
··· 39 39 #include <linux/tracepoint.h> 40 40 #include <linux/overflow.h> 41 41 #include <linux/cookie.h> 42 + #include <linux/verification.h> 42 43 43 44 #include <net/netfilter/nf_bpf_link.h> 44 45 #include <net/netkit.h> ··· 2786 2785 } 2787 2786 } 2788 2787 2788 + static int bpf_prog_verify_signature(struct bpf_prog *prog, union bpf_attr *attr, 2789 + bool is_kernel) 2790 + { 2791 + bpfptr_t usig = make_bpfptr(attr->signature, is_kernel); 2792 + struct bpf_dynptr_kern sig_ptr, insns_ptr; 2793 + struct bpf_key *key = NULL; 2794 + void *sig; 2795 + int err = 0; 2796 + 2797 + if (system_keyring_id_check(attr->keyring_id) == 0) 2798 + key = bpf_lookup_system_key(attr->keyring_id); 2799 + else 2800 + key = bpf_lookup_user_key(attr->keyring_id, 0); 2801 + 2802 + if (!key) 2803 + return -EINVAL; 2804 + 2805 + sig = kvmemdup_bpfptr(usig, attr->signature_size); 2806 + if (IS_ERR(sig)) { 2807 + bpf_key_put(key); 2808 + return -ENOMEM; 2809 + } 2810 + 2811 + bpf_dynptr_init(&sig_ptr, sig, BPF_DYNPTR_TYPE_LOCAL, 0, 2812 + attr->signature_size); 2813 + bpf_dynptr_init(&insns_ptr, prog->insnsi, BPF_DYNPTR_TYPE_LOCAL, 0, 2814 + prog->len * sizeof(struct bpf_insn)); 2815 + 2816 + err = bpf_verify_pkcs7_signature((struct bpf_dynptr *)&insns_ptr, 2817 + (struct bpf_dynptr *)&sig_ptr, key); 2818 + 2819 + bpf_key_put(key); 2820 + kvfree(sig); 2821 + return err; 2822 + } 2823 + 2789 2824 /* last field in 'union bpf_attr' used by this command */ 2790 - #define BPF_PROG_LOAD_LAST_FIELD fd_array_cnt 2825 + #define BPF_PROG_LOAD_LAST_FIELD keyring_id 2791 2826 2792 2827 static int bpf_prog_load(union bpf_attr *attr, bpfptr_t uattr, u32 uattr_size) 2793 2828 { ··· 2986 2949 2987 2950 /* eBPF programs must be GPL compatible to use GPL-ed functions */ 2988 2951 prog->gpl_compatible = license_is_gpl_compatible(license) ? 1 : 0; 2952 + 2953 + if (attr->signature) { 2954 + err = bpf_prog_verify_signature(prog, attr, uattr.is_kernel); 2955 + if (err) 2956 + goto free_prog; 2957 + } 2989 2958 2990 2959 prog->orig_prog = NULL; 2991 2960 prog->jited = 0;
+10
tools/include/uapi/linux/bpf.h
··· 1611 1611 * continuous. 1612 1612 */ 1613 1613 __u32 fd_array_cnt; 1614 + /* Pointer to a buffer containing the signature of the BPF 1615 + * program. 1616 + */ 1617 + __aligned_u64 signature; 1618 + /* Size of the signature buffer in bytes. */ 1619 + __u32 signature_size; 1620 + /* ID of the kernel keyring to be used for signature 1621 + * verification. 1622 + */ 1623 + __s32 keyring_id; 1614 1624 }; 1615 1625 1616 1626 struct { /* anonymous struct used by BPF_OBJ_* commands */
+1 -1
tools/lib/bpf/bpf.c
··· 240 240 const struct bpf_insn *insns, size_t insn_cnt, 241 241 struct bpf_prog_load_opts *opts) 242 242 { 243 - const size_t attr_sz = offsetofend(union bpf_attr, fd_array_cnt); 243 + const size_t attr_sz = offsetofend(union bpf_attr, keyring_id); 244 244 void *finfo = NULL, *linfo = NULL; 245 245 const char *func_info, *line_info; 246 246 __u32 log_size, log_level, attach_prog_fd, attach_btf_obj_fd;