[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