Add cryptographic hash and signature verification kfuncs to BPF
Subsystems (6)
v5 cover Add cryptographic hash and signature verification kfuncs to BPF
This patch series enhances BPF's cryptographic functionality by introducing kernel functions for SHA hashing and ECDSA signature verification. The changes enable BPF programs to verify data integrity and authenticity across networking, security, and observability use cases.
The series addresses two gaps in BPF's cryptographic toolkit:
1. Cryptographic hashing - supports content verification and message digest preparation
2. Asymmetric signature verification - allows validation of signed data without requiring private keys in the datapath
Use cases include:
- Verifying signed network packets or application data in XDP/TC programs
- Integrity checks within tracing and security monitoring
- Zero-trust security models with BPF-based credential verification
- Content-addressed storage in BPF-based filesystems
The implementation leverages existing BPF patterns: it uses bpf_dynptr for memory safety, reuses kernel crypto libraries (lib/crypto/sha256.c and crypto/ecdsa.c) rather than reimplementing algorithms, and provides context-based APIs supporting multiple program types.
v1: https://lore.kernel.org/bpf/[email protected]/
v2: https://lore.kernel.org/bpf/[email protected]/
- Fixed redundant __bpf_dynptr_is_rdonly() checks (Vadim)
- Added BPF hash algorithm type registration module in crypto/ subsystem
- Added CONFIG_CRYPTO_HASH2 guards around bpf_crypto_hash() kfunc and its BTF registration, matching the pattern used for CONFIG_CRYPTO_ECDSA
- Added mandatory digestsize validation for hash operations
v3: https://lore.kernel.org/bpf/[email protected]/
- Fixed patch ordering - header changes now in separate first commit before crypto module to ensure bisectability (bot+bpf-ci)
- Fixed type mismatch - changed u32 to u64 for dynptr sizes in bpf_crypto_hash() to match __bpf_dynptr_size() return type (Mykyta)
- Added CONFIG_CRYPTO_ECDSA to selftest config (Song)
- Refactored test code duplication with setup_skel() helper (Song)
- Added copyright notices to all new files
v4: https://lore.kernel.org/bpf/[email protected]/
- Reused common bpf_crypto_ctx structure for hash and signature operations instead of separate context types (Song)
- Fixed integer truncation in bpf_crypto_hash when data_len > UINT_MAX
- Corrected KF_RCU flags for ECDSA kfuncs (only bpf_ecdsa_verify needs KF_RCU)
- Updated MAINTAINERS file in test patches
- Refactored selftests to use crypto_common.h for kfunc declarations
v5:
- Fixed bisectability: moved bpf_crypto_type_id enum and type_id field introduction to the hash module commit, before it's used by hash kfunc
- Renamed kfuncs from bpf_ecdsa_* to bpf_sig_* since signature verification is not ECDSA-specific (Vadim)
- Added NULL checks in bpf_crypto_sig wrapper functions for optional digest_size and max_size callbacks to prevent NULL pointer dereference
- Added extra validation in bpf_sig_digestsize/bpf_sig_maxsize kfuncs to return -EOPNOTSUPP when underlying algorithm returns 0
- Renamed test files from ecdsa_verify to sig_verify for consistency
Daniel Hodges (7):
bpf: Extend bpf_crypto_type with hash operations
crypto: Add BPF hash algorithm type registration module
crypto: Add BPF signature algorithm type registration module
bpf: Add hash kfunc for cryptographic hashing selftests/bpf: Add tests for bpf_crypto_hash kfunc
bpf: Add signature verification kfuncs selftests/bpf: Add tests for signature verification kfuncs
MAINTAINERS | 6 + crypto/Makefile | 6 + crypto/bpf_crypto_shash.c | 96 ++++++ crypto/bpf_crypto_sig.c | 89 ++++++ crypto/bpf_crypto_skcipher.c | 1 + include/linux/bpf_crypto.h | 13 + kernel/bpf/crypto.c | 204 ++++++++++++- tools/testing/selftests/bpf/config | 2 + .../selftests/bpf/prog_tests/crypto_hash.c | 210 +++++++++++++ .../selftests/bpf/prog_tests/sig_verify.c | 163 ++++++++++ .../selftests/bpf/progs/crypto_common.h | 8 + .../testing/selftests/bpf/progs/crypto_hash.c | 235 ++++++++++++++ .../testing/selftests/bpf/progs/sig_verify.c | 286 ++++++++++++++++++ 13 files changed, 1310 insertions(+), 9 deletions(-) create mode 100644 crypto/bpf_crypto_shash.c create mode 100644 crypto/bpf_crypto_sig.c create mode 100644 tools/testing/selftests/bpf/prog_tests/crypto_hash.c create mode 100644 tools/testing/selftests/bpf/prog_tests/sig_verify.c create mode 100644 tools/testing/selftests/bpf/progs/crypto_hash.c create mode 100644 tools/testing/selftests/bpf/progs/sig_verify.c
Signature · 1 line
2.52.0
PATCH v5 1/7 bpf: Extend bpf_crypto_type with hash operations
Add hash operation callbacks to bpf_crypto_type structure:
- hash(): Performs hashing operation on input data
- digestsize(): Returns the output size for the hash algorithm
These additions enable BPF programs to use cryptographic hash functions through the unified bpf_crypto_type interface, supporting use cases such as content verification, integrity checking, and data authentication.
--- include/linux/bpf_crypto.h | 2 ++ 1 file changed, 2 insertions(+)
include/linux/bpf_crypto.h +2 -0
@@ -11,8 +11,10 @@ struct bpf_crypto_type {int (*setauthsize)(void *tfm,unsigned int authsize);int (*encrypt)(void *tfm,const u8 *src,u8 *dst,unsigned int len,u8 *iv);int (*decrypt)(void *tfm,const u8 *src,u8 *dst,unsigned int len,u8 *iv); +int (*hash)(void *tfm,const u8 *data,u8 *out,unsigned int len);unsigned int (*ivsize)(void *tfm);unsigned int (*statesize)(void *tfm); +unsigned int (*digestsize)(void *tfm);u32 (*get_flags)(void *tfm);struct module *owner;char name[14 ];
Signature · 1 line
2.52.0
PATCH v5 2/7 crypto: Add BPF hash algorithm type registration module
In reply to Daniel HodgesAdd bpf_crypto_shash module that registers a hash type with the BPF crypto infrastructure, enabling BPF programs to access kernel hash algorithms through a unified interface.
Update the bpf_crypto_type interface with hash-specific callbacks:
- alloc_tfm: Allocates crypto_shash context with proper descriptor size
- free_tfm: Releases hash transform and context memory
- has_algo: Checks algorithm availability via crypto_has_shash()
- hash: Performs single-shot hashing via crypto_shash_digest()
- digestsize: Returns the output size for the hash algorithm
- get_flags: Exposes transform flags to BPF programs
Update bpf_shash_ctx to contain crypto_shash transform and shash_desc descriptor to accommodate algorithm-specific descriptor requirements.
--- MAINTAINERS | 1 + crypto/Makefile | 3 ++ crypto/bpf_crypto_shash.c | 96 ++++++++++++++++++++++++++++++++++++++ include/linux/bpf_crypto.h | 7 +++ 4 files changed, 107 insertions(+) create mode 100644 crypto/bpf_crypto_shash.c
MAINTAINERS +1 -0
@@ -4713,6 +4713,7 @@ BPF [CRYPTO] M: Vadim Fedorenko <[email protected]> L: [email protected] S: Maintained +F: crypto/bpf_crypto_shash.c F: crypto/bpf_crypto_skcipher.c F: include/linux/bpf_crypto.h F: kernel/bpf/crypto.c
crypto/Makefile +3 -0
@@ -30,6 +30,9 @@ obj-$(CONFIG_CRYPTO_ECHAINIV) += echainiv.o crypto_hash-y += ahash.o crypto_hash-y += shash.o obj-$(CONFIG_CRYPTO_HASH2) += crypto_hash.o +ifeq ($(CONFIG_BPF_SYSCALL),y) +obj-$(CONFIG_CRYPTO_HASH2) += bpf_crypto_shash.o +endif obj-$(CONFIG_CRYPTO_AKCIPHER2) += akcipher.o obj-$(CONFIG_CRYPTO_SIG2) += sig.o
crypto/bpf_crypto_shash.c new file +96 -0
96 lines not on this page · raw message
include/linux/bpf_crypto.h +7 -0
@@ -3,6 +3,12 @@ #ifndef _BPF_CRYPTO_H #define _BPF_CRYPTO_H +enum bpf_crypto_type_id { + BPF_CRYPTO_TYPE_SKCIPHER = 1, + BPF_CRYPTO_TYPE_HASH, + BPF_CRYPTO_TYPE_SIG, +}; + struct bpf_crypto_type { void *(*alloc_tfm)(const char *algo); void (*free_tfm)(void *tfm);
@@ -17,6 +23,7 @@ struct bpf_crypto_type {unsigned int (*digestsize)(void *tfm);u32 (*get_flags)(void *tfm);struct module *owner; +enum bpf_crypto_type_id type_id;char name[14 ]; };
Signature · 1 line
2.52.0
[email protected]
AI reviewRe: [PATCH bpf-next v5 2/7] crypto: Add BPF hash algorithm type registration module
Can this cause a NULL pointer dereference when a BPF program calls bpf_crypto_ctx_create() with type="hash"?
Quoted 5 lines
crypto/bpf_crypto_shash.c new file +0
-0
[ ... ]
crypto/bpf_crypto_shash.c line 79
Quoted 11 lines
crypto/bpf_crypto_shash.c new file +11
-0+static const struct bpf_crypto_type bpf_crypto_shash_type = { + .alloc_tfm = bpf_crypto_shash_alloc_tfm, + .free_tfm = bpf_crypto_shash_free_tfm, + .has_algo = bpf_crypto_shash_has_algo, + .hash = bpf_crypto_shash_hash, + .digestsize = bpf_crypto_shash_digestsize, + .get_flags = bpf_crypto_shash_get_flags, + .owner =THIS_MODULE , + .type_id =BPF_CRYPTO_TYPE_HASH , + .name ="hash" , +};
Can this cause a NULL pointer dereference when a BPF program calls bpf_crypto_ctx_create() with type="hash"?
The bpf_crypto_ctx_create() function in kernel/bpf/crypto.c unconditionally calls type->setkey(), type->ivsize(), and type->statesize():
*err = type->setkey(ctx->tfm, params->key, params->key_len);
...
ctx->siv_len = type->ivsize(ctx->tfm) + type->statesize(ctx->tfm);But bpf_crypto_shash_type does not implement these callbacks, leaving them as NULL.
Note: This appears to be fixed later in the series by commit 76d771a64b50 ("bpf: Add hash kfunc for cryptographic hashing") which adds NULL checks before calling these function pointers. Should this commit be squashed with 76d771a64b50 to ensure each patch in the series is bisectable without introducing crashes?
crypto/bpf_crypto_shash.c line 96
Quoted 4 lines
crypto/bpf_crypto_shash.c new file +4
-0+module_init (bpf_crypto_shash_init); +module_exit (bpf_crypto_shash_exit); +MODULE_LICENSE ("GPL" ); +MODULE_DESCRIPTION ("Hash algorithm support for BPF" );
---
AI reviewed your patch. Please fix the bug or email reply why it's not a bug.
See: https://github.com/kernel-patches/vmtest/blob/master/ci/claude/README.md
CI run summary: https://github.com/kernel-patches/bpf/actions/runs/21183820118
Re: [PATCH bpf-next v5 2/7] crypto: Add BPF hash algorithm type registration module
Hi,
Quoted 11 lines
Can this cause a NULL pointer dereference when a BPF program calls
bpf_crypto_ctx_create()with type="hash"?The
bpf_crypto_ctx_create()function inkernel/bpf/crypto.cunconditionally callstype->setkey(),type->ivsize(), andtype->statesize():*err = type->setkey(ctx->tfm, params->key, params->key_len); ... ctx->siv_len = type->ivsize(ctx->tfm) + type->statesize(ctx->tfm);
Quoted 8 lines
But
bpf_crypto_shash_typedoes not implement these callbacks, leaving them as NULL.Note: This appears to be fixed later in the series by commit
76d771a64b50("bpf: Add hash kfunc for cryptographic hashing") which adds NULL checks before calling these function pointers. Should this commit be squashed with76d771a64b50to ensure each patch in the series is bisectable without introducing crashes?
Yes, confirmed.
I reproduced this on x86_64 with a sleepable BPF syscall program that calls bpf_crypto_ctx_create() with:
- type = "hash"
- algo = "sha256"
- key_len = 1
That reaches the path where type->setkey/type->ivsize/type->statesize are used without NULL checks for the hash type, and triggers the NULL dereference as pointed out.
Below is the reproducer (inlined, no attachment):
--8<--
#define _GNU_SOURCE
#include <errno.h>
#include <fcntl.h>
#include <linux/bpf.h>
#include <linux/btf.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/syscall.h>
#include <unistd.h>
#ifndef __NR_bpf
#define __NR_bpf 321
#endif
#define LOG_BUF_SIZE (1 << 20)
static int bpf_sys(enum bpf_cmd cmd, union bpf_attr *attr, unsigned int
size) {
return syscall(__NR_bpf, cmd, attr, size);
}static size_t btf_type_size(const struct btf_type *t)
{
__u16 vlen = BTF_INFO_VLEN(t->info);
__u32 kind = BTF_INFO_KIND(t->info);
size_t sz = sizeof(*t);
switch (kind) {
case BTF_KIND_INT: sz += sizeof(__u32); break;
case BTF_KIND_ARRAY: sz += sizeof(struct btf_array); break;
case BTF_KIND_STRUCT:
case BTF_KIND_UNION: sz += (size_t)vlen * sizeof(struct
btf_member); break; case BTF_KIND_ENUM: sz += (size_t)vlen *
sizeof(struct btf_enum); break; case BTF_KIND_FUNC_PROTO: sz +=
(size_t)vlen * sizeof(struct btf_param); break; case
BTF_KIND_VAR: sz += sizeof(struct btf_var); break; case
BTF_KIND_DATASEC: sz += (size_t)vlen * sizeof(struct
btf_var_secinfo); break; case BTF_KIND_DECL_TAG: sz +=
sizeof(struct btf_decl_tag); break; case BTF_KIND_ENUM64: sz +=
(size_t)vlen * sizeof(struct btf_enum64); break; default:
break; } return sz;
}static int find_vmlinux_func_btf_id(const char *func_name)
{
int fd = -1, ret = -1;
struct stat st;
unsigned char *blob = NULL;
struct btf_header *hdr;
char *types, *strs;
__u32 off, id;
ssize_t n;
size_t got = 0;
fd = open("/sys/kernel/btf/vmlinux", O_RDONLY);
if (fd < 0) goto out;
if (fstat(fd, &st) < 0 || st.st_size <= 0) goto out;
blob = malloc(st.st_size);
if (!blob) goto out;
while (got < (size_t)st.st_size) {
n = read(fd, blob + got, (size_t)st.st_size - got);
if (n < 0) {
if (errno == EINTR) continue;
goto out;
}
if (n == 0) break;
got += (size_t)n;
}
if (got < sizeof(*hdr)) goto out;
hdr = (struct btf_header *)blob;
if (hdr->magic != BTF_MAGIC || hdr->version != BTF_VERSION)
goto out; if ((size_t)hdr->hdr_len + hdr->type_off +
hdr->type_len > got) goto out; if ((size_t)hdr->hdr_len +
hdr->str_off + hdr->str_len > got) goto out;
types = (char *)blob + hdr->hdr_len + hdr->type_off;
strs = (char *)blob + hdr->hdr_len + hdr->str_off;
for (off = 0, id = 1; off < hdr->type_len; id++) {
struct btf_type *t = (struct btf_type *)(types + off);
const char *name = t->name_off ? (strs + t->name_off) :
""; size_t sz = btf_type_size(t);
if (sz == 0 || off + sz > hdr->type_len) goto out;
if (BTF_INFO_KIND(t->info) == BTF_KIND_FUNC &&
strcmp(name, func_name) == 0) { ret = (int)id;
goto out;
}
off += sz;
}out:
free(blob); if (fd >= 0) close(fd); return ret; }
int main(void)
{
int create_id =find_vmlinux_func_btf_id("bpf_crypto_ctx_create"); int release_id = find_vmlinux_func_btf_id("bpf_crypto_ctx_release"); if (create_id <= 0 || release_id <= 0) { fprintf(stderr, "failed resolving BTF IDs:
create=%d release=%d\n", create_id, release_id); return 1;
}
enum { PARAMS_BASE = 424, ERR_BASE = 16, PARAMS_SIZE = 408 };
struct bpf_insn insn[128];
int pc = 0, off;
for (off = -8; off >= -424; off -= 8)
insn[pc++] = (struct bpf_insn){
.code = BPF_ST | BPF_MEM | BPF_DW,
.dst_reg = BPF_REG_10,
.off = off,
.imm = 0,
};
insn[pc++] = (struct bpf_insn){ .code = BPF_ST | BPF_MEM |
BPF_W, .dst_reg = BPF_REG_10, .off = -PARAMS_BASE + 0, .imm =
0x68736168 }; insn[pc++] = (struct bpf_insn){ .code = BPF_ST |
BPF_MEM | BPF_W, .dst_reg = BPF_REG_10, .off = -PARAMS_BASE +
16, .imm = 0x32616873 }; insn[pc++] = (struct bpf_insn){ .code
= BPF_ST | BPF_MEM | BPF_W, .dst_reg = BPF_REG_10, .off =
-PARAMS_BASE + 20, .imm = 0x00003635 }; insn[pc++] = (struct
bpf_insn){ .code = BPF_ST | BPF_MEM | BPF_B, .dst_reg =
BPF_REG_10, .off = -PARAMS_BASE + 144, .imm = 0x11 };
insn[pc++] = (struct bpf_insn){ .code = BPF_ST | BPF_MEM |
BPF_W, .dst_reg = BPF_REG_10, .off = -PARAMS_BASE + 400, .imm =
1 };
insn[pc++] = (struct bpf_insn){ .code = BPF_ALU64 | BPF_MOV |
BPF_X, .dst_reg = BPF_REG_1, .src_reg = BPF_REG_10 };
insn[pc++] = (struct bpf_insn){ .code = BPF_ALU64 | BPF_ADD |
BPF_K, .dst_reg = BPF_REG_1, .imm = -PARAMS_BASE }; insn[pc++]
= (struct bpf_insn){ .code = BPF_ALU64 | BPF_MOV | BPF_K,
.dst_reg = BPF_REG_2, .imm = PARAMS_SIZE }; insn[pc++] =
(struct bpf_insn){ .code = BPF_ALU64 | BPF_MOV | BPF_X,
.dst_reg = BPF_REG_3, .src_reg = BPF_REG_10 }; insn[pc++] =
(struct bpf_insn){ .code = BPF_ALU64 | BPF_ADD | BPF_K,
.dst_reg = BPF_REG_3, .imm = -ERR_BASE };
insn[pc++] = (struct bpf_insn){ .code = BPF_JMP | BPF_CALL,
.src_reg = BPF_PSEUDO_KFUNC_CALL, .imm = create_id };
insn[pc++] = (struct bpf_insn){ .code = BPF_JMP | BPF_JEQ |
BPF_K, .dst_reg = BPF_REG_0, .off = 2, .imm = 0 }; insn[pc++] =
(struct bpf_insn){ .code = BPF_ALU64 | BPF_MOV | BPF_X,
.dst_reg = BPF_REG_1, .src_reg = BPF_REG_0 }; insn[pc++] =
(struct bpf_insn){ .code = BPF_JMP | BPF_CALL, .src_reg =
BPF_PSEUDO_KFUNC_CALL, .imm = release_id };
insn[pc++] = (struct bpf_insn){ .code = BPF_ALU64 | BPF_MOV |
BPF_K, .dst_reg = BPF_REG_0, .imm = 0 }; insn[pc++] = (struct
bpf_insn){ .code = BPF_JMP | BPF_EXIT };
char logbuf[LOG_BUF_SIZE];
char lic[] = "GPL";
union bpf_attr attr;
memset(logbuf, 0, sizeof(logbuf));
memset(&attr, 0, sizeof(attr));
attr.prog_type = BPF_PROG_TYPE_SYSCALL;
attr.prog_flags = BPF_F_SLEEPABLE;
attr.insn_cnt = pc;
attr.insns = (uint64_t)(uintptr_t)insn;
attr.license = (uint64_t)(uintptr_t)lic;
attr.log_level = 1;
attr.log_size = sizeof(logbuf);
attr.log_buf = (uint64_t)(uintptr_t)logbuf;
memcpy(attr.prog_name, "poc_hash_bug", 12);
int prog_fd = bpf_sys(BPF_PROG_LOAD, &attr, sizeof(attr));
if (prog_fd < 0) {
fprintf(stderr, "BPF_PROG_LOAD failed: errno=%d
(%s)\n", errno, strerror(errno)); fprintf(stderr, "Verifier
log:\n%s\n", logbuf); return 2;
}
union bpf_attr run;
memset(&run, 0, sizeof(run));
run.test.prog_fd = prog_fd;
(void)bpf_sys(BPF_PROG_TEST_RUN, &run, sizeof(run));
close(prog_fd);
return 0;
}--8<--
I agree this should be fixed at the patch granularity level. I will squash the NULL-check fix into patch 2 in v6 so each patch remains bisectable and does not introduce a crash window.
As this issue is already publicly discussed on bpf-next and raised by CI review, replying on-list is appropriate.
Thanks
PATCH v5 3/7 crypto: Add BPF signature algorithm type registration module
In reply to Daniel HodgesAdd a new bpf_crypto_sig module that registers signature verification algorithms with the BPF crypto type system. This enables signature operations (like ECDSA) to use the unified bpf_crypto_ctx structure instead of requiring separate context types.
Changes:
- Add verify() callback to bpf_crypto_type for signature verification
- Add bpf_crypto_sig module with:
- alloc_tfm/free_tfm for crypto_sig transform lifecycle
- has_algo to check algorithm availability
- setkey for public key configuration
- verify for signature verification
- get_flags for crypto API flags
This allows ECDSA and other signature verification operations to integrate with the existing BPF crypto infrastructure.
--- MAINTAINERS | 1 + crypto/Makefile | 3 ++ crypto/bpf_crypto_sig.c | 67 ++++++++++++++++++++++++++++++++++++++ include/linux/bpf_crypto.h | 2 ++ 4 files changed, 73 insertions(+) create mode 100644 crypto/bpf_crypto_sig.c
MAINTAINERS +1 -0
@@ -4714,6 +4714,7 @@ M: Vadim Fedorenko <[email protected]> L: [email protected] S: Maintained F: crypto/bpf_crypto_shash.c +F: crypto/bpf_crypto_sig.c F: crypto/bpf_crypto_skcipher.c F: include/linux/bpf_crypto.h F: kernel/bpf/crypto.c
crypto/Makefile +3 -0
@@ -36,6 +36,9 @@ endif obj-$(CONFIG_CRYPTO_AKCIPHER2) += akcipher.o obj-$(CONFIG_CRYPTO_SIG2) += sig.o +ifeq ($(CONFIG_BPF_SYSCALL),y) +obj-$(CONFIG_CRYPTO_SIG2) += bpf_crypto_sig.o +endif obj-$(CONFIG_CRYPTO_KPP2) += kpp.o obj-$(CONFIG_CRYPTO_HKDF) += hkdf.o
crypto/bpf_crypto_sig.c new file +67 -0
67 lines not on this page · raw message
include/linux/bpf_crypto.h +2 -0
@@ -18,6 +18,8 @@ struct bpf_crypto_type {int (*encrypt)(void *tfm,const u8 *src,u8 *dst,unsigned int len,u8 *iv);int (*decrypt)(void *tfm,const u8 *src,u8 *dst,unsigned int len,u8 *iv);int (*hash)(void *tfm,const u8 *data,u8 *out,unsigned int len); +int (*verify)(void *tfm,const u8 *sig,unsigned int sig_len, +const u8 *msg,unsigned int msg_len);unsigned int (*ivsize)(void *tfm);unsigned int (*statesize)(void *tfm);unsigned int (*digestsize)(void *tfm);
Signature · 1 line
2.52.0
[email protected]
AI reviewRe: [PATCH bpf-next v5 3/7] crypto: Add BPF signature algorithm type registration module
The bpf_crypto_sig_type struct does not set .ivsize or .statesize callbacks. In bpf_crypto_ctx_create() in kernel/bpf/crypto.c, there is an unconditional call:
Quoted 5 lines
crypto/bpf_crypto_sig.c new file +0
-0
[ ... ]
crypto/bpf_crypto_sig.c line 50
Quoted 11 lines
crypto/bpf_crypto_sig.c new file +11
-0+static const struct bpf_crypto_type bpf_crypto_sig_type = { + .alloc_tfm = bpf_crypto_sig_alloc_tfm, + .free_tfm = bpf_crypto_sig_free_tfm, + .has_algo = bpf_crypto_sig_has_algo, + .get_flags = bpf_crypto_sig_get_flags, + .setkey = bpf_crypto_sig_setkey, + .verify = bpf_crypto_sig_verify, + .owner =THIS_MODULE , + .type_id =BPF_CRYPTO_TYPE_SIG , + .name ="sig" , +};
The bpf_crypto_sig_type struct does not set .ivsize or .statesize callbacks. In bpf_crypto_ctx_create() in kernel/bpf/crypto.c, there is an unconditional call:
ctx->siv_len = type->ivsize(ctx->tfm) + type->statesize(ctx->tfm);
If a BPF program creates a crypto context with type="sig", this will call NULL function pointers and crash the kernel.
I see this is fixed later in the series by commit 76d771a64b50 ("bpf: Add hash kfunc for cryptographic hashing") which adds NULL checks:
if (type->ivsize && type->statesize)
ctx->siv_len = type->ivsize(ctx->tfm) + type->statesize(ctx->tfm);Should this fix be folded into this commit to avoid the intermediate crash?
[ ... ]
---
AI reviewed your patch. Please fix the bug or email reply why it's not a bug.
See: https://github.com/kernel-patches/vmtest/blob/master/ci/claude/README.md
CI run summary: https://github.com/kernel-patches/bpf/actions/runs/21183820118
PATCH v5 4/7 bpf: Add hash kfunc for cryptographic hashing
In reply to Daniel HodgesExtend bpf_crypto_type structure with hash operations:
- hash(): Performs hashing operation
- digestsize(): Returns hash output size
Update bpf_crypto_ctx_create() to support keyless operations:
- Hash algorithms don't require keys, unlike ciphers
- Only validates key presence if type->setkey is defined
- Conditionally sets IV/state length for cipher operations only
Add bpf_crypto_hash() kfunc that works with any hash algorithm registered in the kernel's crypto API through the BPF crypto type system. This enables BPF programs to compute cryptographic hashes for use cases such as content verification, integrity checking, and data authentication.
--- kernel/bpf/crypto.c | 87 ++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 78 insertions(+), 9 deletions(-)
kernel/bpf/crypto.c +78 -9
@@ -139,7 +139,7 @@ __bpf_kfunc_start_defs();* It may return NULL if no memory is available. * @params: pointer to struct bpf_crypto_params which contains all the * details needed to initialise crypto context. -* @params__sz: size of steuct bpf_crypto_params usef by bpf program +* @params__sz: size of struct bpf_crypto_params used by bpf program * @err: integer to store error code when NULL is returned. */ __bpf_kfunc struct bpf_crypto_ctx *
@@ -171,7 +171,12 @@ bpf_crypto_ctx_create(const struct bpf_crypto_params *params, u32 params__sz, goto err_module_put; } -if (!params->key_len || params->key_len >sizeof (params->key)) { +/* Hash operations don't require a key, but cipher operations do */ +if (params->key_len >sizeof (params->key)) { + *err = -EINVAL ; + goto err_module_put; + } +if (!params->key_len && type->setkey) { *err = -EINVAL ; goto err_module_put; }
@@ -195,16 +200,23 @@ bpf_crypto_ctx_create(const struct bpf_crypto_params *params, u32 params__sz, goto err_free_tfm; } - *err = type->setkey (ctx->tfm, params->key, params->key_len); -if (*err) - goto err_free_tfm; +if (params->key_len) { +if (!type->setkey) { + *err = -EINVAL ; + goto err_free_tfm; + } + *err = type->setkey (ctx->tfm, params->key, params->key_len); +if (*err) + goto err_free_tfm; -if (type->get_flags (ctx->tfm) &CRYPTO_TFM_NEED_KEY ) { - *err = -EINVAL ; - goto err_free_tfm; +if (type->get_flags (ctx->tfm) &CRYPTO_TFM_NEED_KEY ) { + *err = -EINVAL ; + goto err_free_tfm; + } } - ctx->siv_len = type->ivsize (ctx->tfm) + type->statesize (ctx->tfm); +if (type->ivsize && type->statesize) + ctx->siv_len = type->ivsize (ctx->tfm) + type->statesize (ctx->tfm);refcount_set (&ctx->usage,1 );
@@ -349,6 +361,58 @@ __bpf_kfunc int bpf_crypto_encrypt(struct bpf_crypto_ctx *ctx,return bpf_crypto_crypt (ctx, src_kern, dst_kern, siv_kern, false); } +#if IS_ENABLED (CONFIG_CRYPTO_HASH2 ) +/** +* bpf_crypto_hash() - Compute hash using configured context +* @ctx: The crypto context being used. The ctx must be a trusted pointer. +* @data: bpf_dynptr to the input data to hash. Must be a trusted pointer. +* @out: bpf_dynptr to the output buffer. Must be a trusted pointer. +* +* Computes hash of the input data using the crypto context. The output buffer +* must be at least as large as the digest size of the hash algorithm. +*/ +__bpf_kfunc int bpf_crypto_hash(struct bpf_crypto_ctx *ctx, +const struct bpf_dynptr *data, +const struct bpf_dynptr *out) +{ +const struct bpf_dynptr_kern *data_kern = (struct bpf_dynptr_kern *)data; +const struct bpf_dynptr_kern *out_kern = (struct bpf_dynptr_kern *)out; +unsigned int digestsize; +u64 data_len, out_len; +const u8 *data_ptr; +u8 *out_ptr; + +if (ctx->type->type_id !=BPF_CRYPTO_TYPE_HASH ) +return -EINVAL ; + +if (!ctx->type->hash) +return -EOPNOTSUPP ; + + data_len =__bpf_dynptr_size (data_kern); + out_len =__bpf_dynptr_size (out_kern); + +if (data_len ==0 || data_len >UINT_MAX ) +return -EINVAL ; + +if (!ctx->type->digestsize) +return -EOPNOTSUPP ; + + digestsize = ctx->type->digestsize (ctx->tfm); +if (out_len < digestsize) +return -EINVAL ; + + data_ptr =__bpf_dynptr_data (data_kern, data_len); +if (!data_ptr) +return -EINVAL ; + + out_ptr =__bpf_dynptr_data_rw (out_kern, out_len); +if (!out_ptr) +return -EINVAL ; + +return ctx->type->hash (ctx->tfm, data_ptr, out_ptr, data_len); +} +#endif /* CONFIG_CRYPTO_HASH2 */ +__bpf_kfunc_end_defs ();BTF_KFUNCS_START (crypt_init_kfunc_btf_ids)
@@ -365,6 +429,9 @@ static const struct btf_kfunc_id_set crypt_init_kfunc_set = {BTF_KFUNCS_START (crypt_kfunc_btf_ids)BTF_ID_FLAGS (func ,bpf_crypto_decrypt ,KF_RCU )BTF_ID_FLAGS (func, bpf_crypto_encrypt,KF_RCU ) +#if IS_ENABLED(CONFIG_CRYPTO_HASH2) +BTF_ID_FLAGS (func, bpf_crypto_hash,KF_RCU ) +#endifBTF_KFUNCS_END (crypt_kfunc_btf_ids) static const struct btf_kfunc_id_set crypt_kfunc_set = {
@@ -389,6 +456,8 @@ static int __init crypto_kfunc_init(void) ret =register_btf_kfunc_id_set (BPF_PROG_TYPE_SCHED_CLS , &crypt_kfunc_set); ret = ret ?:register_btf_kfunc_id_set (BPF_PROG_TYPE_SCHED_ACT , &crypt_kfunc_set); ret = ret ?:register_btf_kfunc_id_set (BPF_PROG_TYPE_XDP , &crypt_kfunc_set); +/* Register for SYSCALL programs to enable testing and debugging */ + ret = ret ?:register_btf_kfunc_id_set (BPF_PROG_TYPE_SYSCALL , &crypt_kfunc_set); ret = ret ?:register_btf_kfunc_id_set (BPF_PROG_TYPE_SYSCALL , &crypt_init_kfunc_set); return ret ?: register_btf_id_dtor_kfuncs(bpf_crypto_dtors,
Signature · 1 line
2.52.0
PATCH v5 5/7 selftests/bpf: Add tests for bpf_crypto_hash kfunc
In reply to Daniel HodgesAdd selftests to validate the bpf_crypto_hash works properly. The tests verify both correct functionality and proper error handling.
Test Data:
All tests use the well-known NIST test vector input "abc" and validate against the standardized expected outputs for each algorithm. This ensures the BPF kfunc wrappers correctly delegate to the kernel crypto library.
--- MAINTAINERS | 2 + tools/testing/selftests/bpf/config | 2 + .../selftests/bpf/prog_tests/crypto_hash.c | 210 ++++++++++++++++ .../selftests/bpf/progs/crypto_common.h | 2 + .../testing/selftests/bpf/progs/crypto_hash.c | 235 ++++++++++++++++++ 5 files changed, 451 insertions(+) create mode 100644 tools/testing/selftests/bpf/prog_tests/crypto_hash.c create mode 100644 tools/testing/selftests/bpf/progs/crypto_hash.c
MAINTAINERS +2 -0
@@ -4718,6 +4718,8 @@ F: crypto/bpf_crypto_sig.c F: crypto/bpf_crypto_skcipher.c F: include/linux/bpf_crypto.h F: kernel/bpf/crypto.c +F: tools/testing/selftests/bpf/prog_tests/crypto_hash.c +F: tools/testing/selftests/bpf/progs/crypto_hash.c BPF [DOCUMENTATION] (Related to Standardization) R: David Vernet <[email protected]>
tools/testing/selftests/bpf/config +2 -0
@@ -13,6 +13,8 @@ CONFIG_BPF_SYSCALL=y CONFIG_CGROUP_BPF=y CONFIG_CRYPTO_HMAC=y CONFIG_CRYPTO_SHA256=y +CONFIG_CRYPTO_SHA512=y +CONFIG_CRYPTO_HASH2=y CONFIG_CRYPTO_USER_API=y CONFIG_CRYPTO_USER_API_HASH=y CONFIG_CRYPTO_USER_API_SKCIPHER=y
tools/testing/selftests/bpf/prog_tests/crypto_hash.c new file +210 -0
210 lines not on this page · raw message
tools/testing/selftests/bpf/progs/crypto_common.h +2 -0
@@ -15,6 +15,8 @@ int bpf_crypto_encrypt(struct bpf_crypto_ctx *ctx, const struct bpf_dynptr *src, const struct bpf_dynptr *dst, const struct bpf_dynptr *iv) __ksym; int bpf_crypto_decrypt(struct bpf_crypto_ctx *ctx, const struct bpf_dynptr *src, const struct bpf_dynptr *dst, const struct bpf_dynptr *iv) __ksym; +int bpf_crypto_hash(struct bpf_crypto_ctx *ctx, const struct bpf_dynptr *data, + const struct bpf_dynptr *out) __ksym;struct __crypto_ctx_value { struct bpf_crypto_ctx __kptr * ctx;
tools/testing/selftests/bpf/progs/crypto_hash.c new file +235 -0
235 lines not on this page · raw message
Signature · 1 line
2.52.0
PATCH v5 6/7 bpf: Add signature verification kfuncs
In reply to Daniel HodgesIntroduce bpf_sig_verify, bpf_sig_keysize, bpf_sig_digestsize, and bpf_sig_maxsize kfuncs enabling BPF programs to verify digital signatures using the kernel's crypto infrastructure.
This adds enum bpf_crypto_type_id for runtime type checking to ensure operations are performed on the correct crypto context type. The enum values are assigned to all crypto type modules (skcipher, hash, sig).
The verify kfunc takes a crypto context (initialized with the "sig" type and appropriate algorithm like "ecdsa-nist-p256"), a message digest, and a signature. It uses dynptr for memory access.
These kfuncs support any signature algorithm registered with the crypto subsystem (e.g., ECDSA, RSA).
--- crypto/bpf_crypto_sig.c | 22 +++++++ crypto/bpf_crypto_skcipher.c | 1 + include/linux/bpf_crypto.h | 2 + kernel/bpf/crypto.c | 117 +++++++++++++++++++++++++++++++++++ 4 files changed, 142 insertions(+)
crypto/bpf_crypto_sig.c +22 -0
@@ -37,6 +37,25 @@ static int bpf_crypto_sig_verify(void *tfm, const u8 *sig, unsigned int sig_len,return crypto_sig_verify (tfm, sig, sig_len, msg, msg_len); } +static unsigned int bpf_crypto_sig_keysize (void *tfm) +{ +return crypto_sig_keysize (tfm); +} + +static unsigned int bpf_crypto_sig_digestsize (void *tfm) +{ +struct sig_alg *alg =crypto_sig_alg (tfm); + +return alg->digest_size ? alg->digest_size (tfm) :0 ; +} + +static unsigned int bpf_crypto_sig_maxsize (void *tfm) +{ +struct sig_alg *alg =crypto_sig_alg (tfm); + +return alg->max_size ? alg->max_size (tfm) :0 ; +} +static const struct bpf_crypto_type bpf_crypto_sig_type = { .alloc_tfm = bpf_crypto_sig_alloc_tfm, .free_tfm = bpf_crypto_sig_free_tfm,
@@ -44,6 +63,9 @@ static const struct bpf_crypto_type bpf_crypto_sig_type = { .get_flags = bpf_crypto_sig_get_flags, .setkey = bpf_crypto_sig_setkey, .verify = bpf_crypto_sig_verify, + .keysize = bpf_crypto_sig_keysize, + .digestsize = bpf_crypto_sig_digestsize, + .maxsize = bpf_crypto_sig_maxsize, .owner = THIS_MODULE, .type_id = BPF_CRYPTO_TYPE_SIG, .name = "sig",
crypto/bpf_crypto_skcipher.c +1 -0
@@ -63,6 +63,7 @@ static const struct bpf_crypto_type bpf_crypto_lskcipher_type = { .statesize = bpf_crypto_lskcipher_statesize, .get_flags = bpf_crypto_lskcipher_get_flags, .owner = THIS_MODULE, + .type_id = BPF_CRYPTO_TYPE_SKCIPHER, .name = "skcipher", };
include/linux/bpf_crypto.h +2 -0
@@ -23,6 +23,8 @@ struct bpf_crypto_type {unsigned int (*ivsize)(void *tfm);unsigned int (*statesize)(void *tfm);unsigned int (*digestsize)(void *tfm); +unsigned int (*keysize)(void *tfm); +unsigned int (*maxsize)(void *tfm);u32 (*get_flags)(void *tfm);struct module *owner;enum bpf_crypto_type_id type_id;
kernel/bpf/crypto.c +117 -0
129 lines not on this page · raw message
Signature · 1 line
2.52.0
PATCH v5 7/7 selftests/bpf: Add tests for signature verification kfuncs
In reply to Daniel HodgesAdd tests for the signature verification kfuncs:
1. test_ecdsa_verify_valid_signature: Verifies that a valid ECDSA signature over a known message hash is correctly verified using the P-256 curve with a test vector.
2. test_ecdsa_verify_invalid_signature: Verifies that an invalid signature (with modified r component) is correctly rejected.
3. test_ecdsa_size_queries: Tests the bpf_sig_keysize(), bpf_sig_digestsize(), and bpf_sig_maxsize() kfuncs to ensure they return valid positive values for a P-256 ECDSA context.
4. test_ecdsa_on_hash_ctx: Tests that calling bpf_sig_verify on a hash context fails with -EINVAL due to type mismatch.
5. test_ecdsa_keysize_on_hash_ctx: Tests that calling bpf_sig_keysize on a hash context fails with -EINVAL due to type mismatch.
6. test_ecdsa_zero_len_msg: Tests that zero-length message is rejected.
7. test_ecdsa_zero_len_sig: Tests that zero-length signature is rejected.
The test uses the p1363(ecdsa-nist-p256) algorithm with a known NIST P-256 test vector for reliable and reproducible testing.
--- MAINTAINERS | 2 + .../selftests/bpf/prog_tests/sig_verify.c | 163 ++++++++++ .../selftests/bpf/progs/crypto_common.h | 6 + .../testing/selftests/bpf/progs/sig_verify.c | 286 ++++++++++++++++++ 4 files changed, 457 insertions(+) create mode 100644 tools/testing/selftests/bpf/prog_tests/sig_verify.c create mode 100644 tools/testing/selftests/bpf/progs/sig_verify.c
MAINTAINERS +2 -0
@@ -4719,7 +4719,9 @@ F: crypto/bpf_crypto_skcipher.c F: include/linux/bpf_crypto.h F: kernel/bpf/crypto.c F: tools/testing/selftests/bpf/prog_tests/crypto_hash.c +F: tools/testing/selftests/bpf/prog_tests/sig_verify.c F: tools/testing/selftests/bpf/progs/crypto_hash.c +F: tools/testing/selftests/bpf/progs/sig_verify.c BPF [DOCUMENTATION] (Related to Standardization) R: David Vernet <[email protected]>
tools/testing/selftests/bpf/prog_tests/sig_verify.c new file +163 -0
163 lines not on this page · raw message
tools/testing/selftests/bpf/progs/crypto_common.h +6 -0
@@ -17,6 +17,12 @@ int bpf_crypto_decrypt(struct bpf_crypto_ctx *ctx, const struct bpf_dynptr *src, const struct bpf_dynptr *dst, const struct bpf_dynptr *iv) __ksym; int bpf_crypto_hash(struct bpf_crypto_ctx *ctx, const struct bpf_dynptr *data, const struct bpf_dynptr *out) __ksym; +int bpf_sig_verify(struct bpf_crypto_ctx *ctx, + const struct bpf_dynptr *message, + const struct bpf_dynptr *signature) __ksym; +int bpf_sig_keysize(struct bpf_crypto_ctx *ctx) __ksym; +int bpf_sig_digestsize(struct bpf_crypto_ctx *ctx) __ksym; +int bpf_sig_maxsize(struct bpf_crypto_ctx *ctx) __ksym;struct __crypto_ctx_value { struct bpf_crypto_ctx __kptr * ctx;
tools/testing/selftests/bpf/progs/sig_verify.c new file +286 -0
286 lines not on this page · raw message
Signature · 1 line
2.52.0