[PATCH 08/12] bpf: Implement signature verification for BPF programs
Blaise Boscaccy
bboscaccy at linux.microsoft.com
Tue Jun 10 16:37:54 UTC 2025
KP Singh <kpsingh at kernel.org> writes:
> 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.
>
> Signed-off-by: KP Singh <kpsingh at kernel.org>
> ---
> include/linux/bpf.h | 9 +++++++-
> include/uapi/linux/bpf.h | 10 +++++++++
> kernel/bpf/syscall.c | 39 +++++++++++++++++++++++++++++++++-
> kernel/trace/bpf_trace.c | 6 ++++--
> tools/include/uapi/linux/bpf.h | 10 +++++++++
> tools/lib/bpf/bpf.c | 2 +-
> 6 files changed, 71 insertions(+), 5 deletions(-)
>
> diff --git a/include/linux/bpf.h b/include/linux/bpf.h
> index 35f1a633d87a..32a41803d61c 100644
> --- a/include/linux/bpf.h
> +++ b/include/linux/bpf.h
> @@ -2778,7 +2778,14 @@ bpf_jit_find_kfunc_model(const struct bpf_prog *prog,
> int bpf_get_kfunc_addr(const struct bpf_prog *prog, u32 func_id,
> u16 btf_fd_idx, u8 **func_addr);
>
> -struct bpf_core_ctx {
> +__bpf_kfunc struct bpf_key *bpf_lookup_user_key(u32 serial, u64 flags);
> +__bpf_kfunc struct bpf_key *bpf_lookup_system_key(u64 id);
> +__bpf_kfunc void bpf_key_put(struct bpf_key *bkey);
> +__bpf_kfunc int bpf_verify_pkcs7_signature(struct bpf_dynptr *data_p,
> + struct bpf_dynptr *sig_p,
> + struct bpf_key *trusted_keyring);
> +
> + struct bpf_core_ctx {
> struct bpf_verifier_log *log;
> const struct btf *btf;
> };
> diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h
> index ffd9e11befc2..5f7c82ebe10a 100644
> --- a/include/uapi/linux/bpf.h
> +++ b/include/uapi/linux/bpf.h
> @@ -1589,6 +1589,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.
> + */
> + __u32 keyring_id;
> };
>
> struct { /* anonymous struct used by BPF_OBJ_* commands */
> diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c
> index c81be07fa4fa..6cd5ba42d946 100644
> --- a/kernel/bpf/syscall.c
> +++ b/kernel/bpf/syscall.c
> @@ -2782,8 +2782,39 @@ 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, bpfptr_t uattr)
> +{
> + bpfptr_t usig = make_bpfptr(attr->signature, uattr.is_kernel);
> + struct bpf_dynptr_kern sig_ptr, insns_ptr;
> + struct bpf_key *key = NULL;
> + void *sig;
> + int err = 0;
> +
> + key = bpf_lookup_user_key(attr->keyring_id, 0);
> + if (!key)
> + return -ENOKEY;
> +
> + sig = kvmemdup_bpfptr(usig, attr->signature_size);
> + if (!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)
> {
> @@ -2947,6 +2978,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);
> + if (err)
> + goto free_prog;
> + }
> +
> prog->orig_prog = NULL;
> prog->jited = 0;
>
> diff --git a/kernel/trace/bpf_trace.c b/kernel/trace/bpf_trace.c
> index 132c8be6f635..0cce39e1a9ee 100644
> --- a/kernel/trace/bpf_trace.c
> +++ b/kernel/trace/bpf_trace.c
> @@ -1351,7 +1351,6 @@ __bpf_kfunc void bpf_key_put(struct bpf_key *bkey)
> kfree(bkey);
> }
>
> -#ifdef CONFIG_SYSTEM_DATA_VERIFICATION
> /**
> * bpf_verify_pkcs7_signature - verify a PKCS#7 signature
> * @data_p: data to verify
> @@ -1367,6 +1366,7 @@ __bpf_kfunc int bpf_verify_pkcs7_signature(struct bpf_dynptr *data_p,
> struct bpf_dynptr *sig_p,
> struct bpf_key *trusted_keyring)
> {
> +#ifdef CONFIG_SYSTEM_DATA_VERIFICATION
> struct bpf_dynptr_kern *data_ptr = (struct bpf_dynptr_kern *)data_p;
> struct bpf_dynptr_kern *sig_ptr = (struct bpf_dynptr_kern *)sig_p;
> const void *data, *sig;
> @@ -1396,8 +1396,10 @@ __bpf_kfunc int bpf_verify_pkcs7_signature(struct bpf_dynptr *data_p,
> trusted_keyring->key,
> VERIFYING_UNSPECIFIED_SIGNATURE, NULL,
> NULL);
> -}
The usage for this is no longer unspecified. VERIFYING_BPF_SIGNATURE or
similar would add clarity here.
> +#else
> + return -EOPNOTSUPP;
> #endif /* CONFIG_SYSTEM_DATA_VERIFICATION */
> +}
>
> __bpf_kfunc_end_defs();
>
> diff --git a/tools/include/uapi/linux/bpf.h b/tools/include/uapi/linux/bpf.h
> index ffd9e11befc2..5f7c82ebe10a 100644
> --- a/tools/include/uapi/linux/bpf.h
> +++ b/tools/include/uapi/linux/bpf.h
> @@ -1589,6 +1589,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.
> + */
> + __u32 keyring_id;
> };
>
> struct { /* anonymous struct used by BPF_OBJ_* commands */
> diff --git a/tools/lib/bpf/bpf.c b/tools/lib/bpf/bpf.c
> index 11fa2d64ccca..1a85cfa4282c 100644
> --- a/tools/lib/bpf/bpf.c
> +++ b/tools/lib/bpf/bpf.c
> @@ -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;
> --
> 2.43.0
More information about the Linux-security-module-archive
mailing list