AI Trajectory


Trajectory: (Expand/Collapse All)
Seq Timestamp Type Name Duration
0/0 2026/09/24 05:36 flow patch-triage
2m Args:
null

Results:
{
  "EnableConfigs": [
    "BPF_CRYPTO",
    "BPF_SYSCALL",
    "CRYPTO_AES",
    "CRYPTO_CBC",
    "CRYPTO_ECB"
  ],
  "FocusSymbols": [
    "bpf_crypto_ctx_create",
    "bpf_crypto_encrypt",
    "bpf_crypto_decrypt",
    "bpf_crypto_crypt",
    "bpf_aes_cbc_crypt",
    "bpf_aes_ecb_crypt",
    "bpf_crypto_find_algo"
  ],
  "KMSANReasoning": "The patch refactors the BPF crypto subsystem, eliminating the modular dynamic crypto type registration in favor of direct calls to in-kernel AES-CBC and AES-ECB library routines (crypto_lib_aes_*). \n\nFrom a KMSAN perspective:\n1. Heap allocations: Both `struct bpf_crypto_ctx` and `struct aes_key` are allocated via `kzalloc_obj()`, ensuring all fields are zero-initialized upon creation.\n2. Control flow \u0026 branching: The error code variables (`err` and `*err_ret`) are initialized on every execution path before any branching or returning.\n3. Buffer operations: `bpf_aes_cbc_crypt()` and `bpf_aes_ecb_crypt()` enforce strict length and alignment checks (`iv_len`, `src_len % AES_BLOCK_SIZE`, and `dst_len \u003c src_len`), ensuring that `aes_cbc_*` and `aes_ecb_*` only process valid bytes within the bounds of the provided BPF dynptr buffers.\n4. User space info-leaks: No structures, padding, or uninitialized buffers are copied to user space.\n\nAny memory issues that could arise (such as refcounting bugs, use-after-free, or out-of-bounds buffer indexing) fall under the purview of KASAN and standard kernel debuggers. There are no uninitialized memory read risks or info-leaks that specifically require KMSAN detection.",
  "NeedsKMSAN": false,
  "Reasoning": "The patch modifies core BPF crypto functionality, replacing the crypto skcipher API tfm indirection with direct AES-CBC and AES-ECB library routines. It alters context allocation and lifetime management, key setup, input/output validation, and encryption/decryption routines. This code is reachable from userspace via the BPF syscall and execution of BPF programs calling the BPF crypto kfuncs, making it an ideal target for fuzzing.",
  "WorthFuzzing": true
}

1/1 2026/09/24 05:36 action read-patch-diff
0m Args:
null

Results:
{
  "PatchDiff": "commit 26717ed6fe6d35c327012a827650ef4b400e489f\nAuthor: syz-cluster \u003ctriage@syzkaller.com\u003e\nDate:   Thu Sep 24 05:36:58 2026 +0000\n\n    syz-cluster: applied patch under review\n\ndiff --git a/MAINTAINERS b/MAINTAINERS\nindex 6215fcb077705..0fcdfe48c62a0 100644\n--- a/MAINTAINERS\n+++ b/MAINTAINERS\n@@ -4936,8 +4936,6 @@ BPF [CRYPTO]\n M:\tVadim Fedorenko \u003cvadim.fedorenko@linux.dev\u003e\n L:\tbpf@vger.kernel.org\n S:\tMaintained\n-F:\tcrypto/bpf_crypto_skcipher.c\n-F:\tinclude/linux/bpf_crypto.h\n F:\tkernel/bpf/crypto.c\n \n BPF [DOCUMENTATION] (Related to Standardization)\ndiff --git a/crypto/Makefile b/crypto/Makefile\nindex 8386d55a9755e..33bb5ad595e8f 100644\n--- a/crypto/Makefile\n+++ b/crypto/Makefile\n@@ -22,9 +22,6 @@ crypto_skcipher-y += lskcipher.o\n crypto_skcipher-y += skcipher.o\n \n obj-$(CONFIG_CRYPTO_SKCIPHER2) += crypto_skcipher.o\n-ifeq ($(CONFIG_BPF_SYSCALL),y)\n-obj-$(CONFIG_CRYPTO_SKCIPHER2) += bpf_crypto_skcipher.o\n-endif\n \n obj-$(CONFIG_CRYPTO_SEQIV) += seqiv.o\n obj-$(CONFIG_CRYPTO_ECHAINIV) += echainiv.o\ndiff --git a/crypto/bpf_crypto_skcipher.c b/crypto/bpf_crypto_skcipher.c\ndeleted file mode 100644\nindex a88798d3e8c87..0000000000000\n--- a/crypto/bpf_crypto_skcipher.c\n+++ /dev/null\n@@ -1,83 +0,0 @@\n-// SPDX-License-Identifier: GPL-2.0-only\n-/* Copyright (c) 2024 Meta, Inc */\n-#include \u003clinux/types.h\u003e\n-#include \u003clinux/module.h\u003e\n-#include \u003clinux/bpf_crypto.h\u003e\n-#include \u003ccrypto/skcipher.h\u003e\n-\n-static void *bpf_crypto_lskcipher_alloc_tfm(const char *algo)\n-{\n-\treturn crypto_alloc_lskcipher(algo, 0, 0);\n-}\n-\n-static void bpf_crypto_lskcipher_free_tfm(void *tfm)\n-{\n-\tcrypto_free_lskcipher(tfm);\n-}\n-\n-static int bpf_crypto_lskcipher_has_algo(const char *algo)\n-{\n-\treturn crypto_has_skcipher(algo, CRYPTO_ALG_TYPE_LSKCIPHER, CRYPTO_ALG_TYPE_MASK);\n-}\n-\n-static int bpf_crypto_lskcipher_setkey(void *tfm, const u8 *key, unsigned int keylen)\n-{\n-\treturn crypto_lskcipher_setkey(tfm, key, keylen);\n-}\n-\n-static u32 bpf_crypto_lskcipher_get_flags(void *tfm)\n-{\n-\treturn crypto_lskcipher_get_flags(tfm);\n-}\n-\n-static unsigned int bpf_crypto_lskcipher_ivsize(void *tfm)\n-{\n-\treturn crypto_lskcipher_ivsize(tfm);\n-}\n-\n-static unsigned int bpf_crypto_lskcipher_statesize(void *tfm)\n-{\n-\treturn crypto_lskcipher_statesize(tfm);\n-}\n-\n-static int bpf_crypto_lskcipher_encrypt(void *tfm, const u8 *src, u8 *dst,\n-\t\t\t\t\tunsigned int len, u8 *siv)\n-{\n-\treturn crypto_lskcipher_encrypt(tfm, src, dst, len, siv);\n-}\n-\n-static int bpf_crypto_lskcipher_decrypt(void *tfm, const u8 *src, u8 *dst,\n-\t\t\t\t\tunsigned int len, u8 *siv)\n-{\n-\treturn crypto_lskcipher_decrypt(tfm, src, dst, len, siv);\n-}\n-\n-static const struct bpf_crypto_type bpf_crypto_lskcipher_type = {\n-\t.alloc_tfm\t= bpf_crypto_lskcipher_alloc_tfm,\n-\t.free_tfm\t= bpf_crypto_lskcipher_free_tfm,\n-\t.has_algo\t= bpf_crypto_lskcipher_has_algo,\n-\t.setkey\t\t= bpf_crypto_lskcipher_setkey,\n-\t.encrypt\t= bpf_crypto_lskcipher_encrypt,\n-\t.decrypt\t= bpf_crypto_lskcipher_decrypt,\n-\t.ivsize\t\t= bpf_crypto_lskcipher_ivsize,\n-\t.statesize\t= bpf_crypto_lskcipher_statesize,\n-\t.get_flags\t= bpf_crypto_lskcipher_get_flags,\n-\t.owner\t\t= THIS_MODULE,\n-\t.name\t\t= \"skcipher\",\n-};\n-\n-static int __init bpf_crypto_skcipher_init(void)\n-{\n-\treturn bpf_crypto_register_type(\u0026bpf_crypto_lskcipher_type);\n-}\n-\n-static void __exit bpf_crypto_skcipher_exit(void)\n-{\n-\tint err = bpf_crypto_unregister_type(\u0026bpf_crypto_lskcipher_type);\n-\tWARN_ON_ONCE(err);\n-}\n-\n-module_init(bpf_crypto_skcipher_init);\n-module_exit(bpf_crypto_skcipher_exit);\n-MODULE_LICENSE(\"GPL\");\n-MODULE_DESCRIPTION(\"Symmetric key cipher support for BPF\");\ndiff --git a/include/linux/bpf_crypto.h b/include/linux/bpf_crypto.h\ndeleted file mode 100644\nindex a41e71d4e2d9f..0000000000000\n--- a/include/linux/bpf_crypto.h\n+++ /dev/null\n@@ -1,24 +0,0 @@\n-/* SPDX-License-Identifier: GPL-2.0-only */\n-/* Copyright (c) 2024 Meta Platforms, Inc. and affiliates. */\n-#ifndef _BPF_CRYPTO_H\n-#define _BPF_CRYPTO_H\n-\n-struct bpf_crypto_type {\n-\tvoid *(*alloc_tfm)(const char *algo);\n-\tvoid (*free_tfm)(void *tfm);\n-\tint (*has_algo)(const char *algo);\n-\tint (*setkey)(void *tfm, const u8 *key, unsigned int keylen);\n-\tint (*setauthsize)(void *tfm, unsigned int authsize);\n-\tint (*encrypt)(void *tfm, const u8 *src, u8 *dst, unsigned int len, u8 *iv);\n-\tint (*decrypt)(void *tfm, const u8 *src, u8 *dst, unsigned int len, u8 *iv);\n-\tunsigned int (*ivsize)(void *tfm);\n-\tunsigned int (*statesize)(void *tfm);\n-\tu32 (*get_flags)(void *tfm);\n-\tstruct module *owner;\n-\tchar name[14];\n-};\n-\n-int bpf_crypto_register_type(const struct bpf_crypto_type *type);\n-int bpf_crypto_unregister_type(const struct bpf_crypto_type *type);\n-\n-#endif /* _BPF_CRYPTO_H */\ndiff --git a/kernel/bpf/Kconfig b/kernel/bpf/Kconfig\nindex d7d25477ef481..a44ecfa3e9ef5 100644\n--- a/kernel/bpf/Kconfig\n+++ b/kernel/bpf/Kconfig\n@@ -91,6 +91,15 @@ config BPF_UNPRIV_DEFAULT_OFF\n \n \t  If you are unsure how to answer this question, answer Y.\n \n+config BPF_CRYPTO\n+\tdef_bool y\n+\tdepends on BPF_SYSCALL\n+\tdepends on CRYPTO_LIB_AES_CBC\n+\tdepends on CRYPTO_LIB_AES_ECB\n+\thelp\n+\t  Provide the kfuncs needed for BPF programs to encrypt and decrypt\n+\t  data. The supported algorithms are AES-CBC and AES-ECB.\n+\n source \"kernel/bpf/preload/Kconfig\"\n \n config BPF_LSM\ndiff --git a/kernel/bpf/Makefile b/kernel/bpf/Makefile\nindex 9a92c348bbda6..c1f9b0d3468d3 100644\n--- a/kernel/bpf/Makefile\n+++ b/kernel/bpf/Makefile\n@@ -58,9 +58,7 @@ obj-$(CONFIG_BPF_SYSCALL) += cpumask.o\n # semantics within pahole are revisited accordingly.\n obj-${CONFIG_BPF_LSM} += bpf_lsm_proto.o bpf_lsm.o\n endif\n-ifneq ($(CONFIG_CRYPTO),)\n-obj-$(CONFIG_BPF_SYSCALL) += crypto.o\n-endif\n+obj-$(CONFIG_BPF_CRYPTO) += crypto.o\n obj-$(CONFIG_BPF_PRELOAD) += preload/\n \n obj-$(CONFIG_BPF_SYSCALL) += relo_core.o\ndiff --git a/kernel/bpf/crypto.c b/kernel/bpf/crypto.c\nindex 51f89cecefb4d..b812204a4d93b 100644\n--- a/kernel/bpf/crypto.c\n+++ b/kernel/bpf/crypto.c\n@@ -1,19 +1,13 @@\n // SPDX-License-Identifier: GPL-2.0-only\n /* Copyright (c) 2024 Meta, Inc */\n #include \u003clinux/bpf.h\u003e\n-#include \u003clinux/bpf_crypto.h\u003e\n #include \u003clinux/bpf_mem_alloc.h\u003e\n #include \u003clinux/btf.h\u003e\n #include \u003clinux/btf_ids.h\u003e\n #include \u003clinux/filter.h\u003e\n-#include \u003clinux/scatterlist.h\u003e\n #include \u003clinux/skbuff.h\u003e\n-#include \u003ccrypto/skcipher.h\u003e\n-\n-struct bpf_crypto_type_list {\n-\tconst struct bpf_crypto_type *type;\n-\tstruct list_head list;\n-};\n+#include \u003ccrypto/aes-cbc.h\u003e\n+#include \u003ccrypto/aes-ecb.h\u003e\n \n /* BPF crypto initialization parameters struct */\n /**\n@@ -36,94 +30,52 @@ struct bpf_crypto_params {\n \tu32 authsize;\n };\n \n-static LIST_HEAD(bpf_crypto_types);\n-static DECLARE_RWSEM(bpf_crypto_types_sem);\n+enum bpf_crypto_algo_id {\n+\tBPF_ALGO_AES_CBC,\n+\tBPF_ALGO_AES_ECB,\n+};\n+\n+static const struct {\n+\tconst char *type_name;\n+\tconst char *algo_name;\n+\tenum bpf_crypto_algo_id algo;\n+} bpf_crypto_algos[] = {\n+\t{ \"skcipher\", \"cbc(aes)\", BPF_ALGO_AES_CBC },\n+\t{ \"skcipher\", \"ecb(aes)\", BPF_ALGO_AES_ECB },\n+};\n+\n+static bool bpf_crypto_find_algo(const struct bpf_crypto_params *params,\n+\t\t\t\t enum bpf_crypto_algo_id *id_ret)\n+{\n+\tfor (size_t i = 0; i \u003c ARRAY_SIZE(bpf_crypto_algos); i++) {\n+\t\tif (strncmp(bpf_crypto_algos[i].type_name, params-\u003etype,\n+\t\t\t    sizeof(params-\u003etype)) == 0 \u0026\u0026\n+\t\t    strncmp(bpf_crypto_algos[i].algo_name, params-\u003ealgo,\n+\t\t\t    sizeof(params-\u003ealgo)) == 0) {\n+\t\t\t*id_ret = bpf_crypto_algos[i].algo;\n+\t\t\treturn true;\n+\t\t}\n+\t}\n+\treturn false;\n+}\n \n /**\n  * struct bpf_crypto_ctx - refcounted BPF crypto context structure\n- * @type:\tThe pointer to bpf crypto type\n- * @tfm:\tThe pointer to instance of crypto API struct.\n- * @siv_len:    Size of IV and state storage for cipher\n+ * @algo:\tThe crypto algorithm ID\n+ * @key:\tPointer to the struct aes_key.  'void *' is used so that the\n+ *\t\tkey isn't readable from BPF programs via BTF.\n  * @rcu:\tThe RCU head used to free the crypto context with RCU safety.\n  * @usage:\tObject reference counter. When the refcount goes to 0, the\n  *\t\tmemory is released back to the BPF allocator, which provides\n  *\t\tRCU safety.\n  */\n struct bpf_crypto_ctx {\n-\tconst struct bpf_crypto_type *type;\n-\tvoid *tfm;\n-\tu32 siv_len;\n+\tenum bpf_crypto_algo_id algo;\n+\tvoid *key;\n \tstruct rcu_head rcu;\n \trefcount_t usage;\n };\n \n-int bpf_crypto_register_type(const struct bpf_crypto_type *type)\n-{\n-\tstruct bpf_crypto_type_list *node;\n-\tint err = -EBUSY;\n-\n-\tdown_write(\u0026bpf_crypto_types_sem);\n-\tlist_for_each_entry(node, \u0026bpf_crypto_types, list) {\n-\t\tif (!strcmp(node-\u003etype-\u003ename, type-\u003ename))\n-\t\t\tgoto unlock;\n-\t}\n-\n-\tnode = kmalloc_obj(*node);\n-\terr = -ENOMEM;\n-\tif (!node)\n-\t\tgoto unlock;\n-\n-\tnode-\u003etype = type;\n-\tlist_add(\u0026node-\u003elist, \u0026bpf_crypto_types);\n-\terr = 0;\n-\n-unlock:\n-\tup_write(\u0026bpf_crypto_types_sem);\n-\n-\treturn err;\n-}\n-EXPORT_SYMBOL_GPL(bpf_crypto_register_type);\n-\n-int bpf_crypto_unregister_type(const struct bpf_crypto_type *type)\n-{\n-\tstruct bpf_crypto_type_list *node;\n-\tint err = -ENOENT;\n-\n-\tdown_write(\u0026bpf_crypto_types_sem);\n-\tlist_for_each_entry(node, \u0026bpf_crypto_types, list) {\n-\t\tif (strcmp(node-\u003etype-\u003ename, type-\u003ename))\n-\t\t\tcontinue;\n-\n-\t\tlist_del(\u0026node-\u003elist);\n-\t\tkfree(node);\n-\t\terr = 0;\n-\t\tbreak;\n-\t}\n-\tup_write(\u0026bpf_crypto_types_sem);\n-\n-\treturn err;\n-}\n-EXPORT_SYMBOL_GPL(bpf_crypto_unregister_type);\n-\n-static const struct bpf_crypto_type *bpf_crypto_get_type(const char *name)\n-{\n-\tconst struct bpf_crypto_type *type = ERR_PTR(-ENOENT);\n-\tstruct bpf_crypto_type_list *node;\n-\n-\tdown_read(\u0026bpf_crypto_types_sem);\n-\tlist_for_each_entry(node, \u0026bpf_crypto_types, list) {\n-\t\tif (strcmp(node-\u003etype-\u003ename, name))\n-\t\t\tcontinue;\n-\n-\t\tif (try_module_get(node-\u003etype-\u003eowner))\n-\t\t\ttype = node-\u003etype;\n-\t\tbreak;\n-\t}\n-\tup_read(\u0026bpf_crypto_types_sem);\n-\n-\treturn type;\n-}\n-\n __bpf_kfunc_start_defs();\n \n /**\n@@ -132,92 +84,76 @@ __bpf_kfunc_start_defs();\n  * Allocates a crypto context that can be used, acquired, and released by\n  * a BPF program. The crypto context returned by this function must either\n  * be embedded in a map as a kptr, or freed with bpf_crypto_ctx_release().\n- * As crypto API functions use GFP_KERNEL allocations, this function can\n- * only be used in sleepable BPF programs.\n+ * As this uses a GFP_KERNEL allocation, this function can only be used in\n+ * sleepable BPF programs.\n  *\n  * bpf_crypto_ctx_create() allocates memory for crypto context.\n  * It may return NULL if no memory is available.\n  * @params:\tpointer to struct bpf_crypto_params which contains all the\n  *\t\tdetails needed to initialise crypto context.\n- * @params__sz:\tsize of steuct bpf_crypto_params usef by bpf program\n- * @err:\tinteger to store error code when NULL is returned.\n+ * @params__sz:\tsize of struct bpf_crypto_params used by bpf program\n+ * @err_ret:\tinteger to store error code when NULL is returned.\n  */\n __bpf_kfunc struct bpf_crypto_ctx *\n bpf_crypto_ctx_create(const struct bpf_crypto_params *params, u32 params__sz,\n-\t\t      int *err)\n+\t\t      int *err_ret)\n {\n-\tconst struct bpf_crypto_type *type;\n \tstruct bpf_crypto_ctx *ctx;\n+\tint err;\n \n \tif (!params || params-\u003ereserved[0] || params-\u003ereserved[1] ||\n \t    params__sz != sizeof(struct bpf_crypto_params)) {\n-\t\t*err = -EINVAL;\n+\t\t*err_ret = -EINVAL;\n \t\treturn NULL;\n \t}\n \n-\ttype = bpf_crypto_get_type(params-\u003etype);\n-\tif (IS_ERR(type)) {\n-\t\t*err = PTR_ERR(type);\n-\t\treturn NULL;\n-\t}\n-\n-\tif (!type-\u003ehas_algo(params-\u003ealgo)) {\n-\t\t*err = -EOPNOTSUPP;\n-\t\tgoto err_module_put;\n-\t}\n-\n-\tif (!!params-\u003eauthsize ^ !!type-\u003esetauthsize) {\n-\t\t*err = -EOPNOTSUPP;\n-\t\tgoto err_module_put;\n-\t}\n-\n \tif (!params-\u003ekey_len || params-\u003ekey_len \u003e sizeof(params-\u003ekey)) {\n-\t\t*err = -EINVAL;\n-\t\tgoto err_module_put;\n+\t\t*err_ret = -EINVAL;\n+\t\treturn NULL;\n \t}\n \n \tctx = kzalloc_obj(*ctx);\n \tif (!ctx) {\n-\t\t*err = -ENOMEM;\n-\t\tgoto err_module_put;\n+\t\t*err_ret = -ENOMEM;\n+\t\treturn NULL;\n \t}\n \n-\tctx-\u003etype = type;\n-\tctx-\u003etfm = type-\u003ealloc_tfm(params-\u003ealgo);\n-\tif (IS_ERR(ctx-\u003etfm)) {\n-\t\t*err = PTR_ERR(ctx-\u003etfm);\n-\t\tgoto err_free_ctx;\n+\tif (!bpf_crypto_find_algo(params, \u0026ctx-\u003ealgo)) {\n+\t\terr = -EOPNOTSUPP;\n+\t\tgoto out;\n \t}\n \n-\tif (params-\u003eauthsize) {\n-\t\t*err = type-\u003esetauthsize(ctx-\u003etfm, params-\u003eauthsize);\n-\t\tif (*err)\n-\t\t\tgoto err_free_tfm;\n+\tswitch (ctx-\u003ealgo) {\n+\tcase BPF_ALGO_AES_CBC:\n+\tcase BPF_ALGO_AES_ECB:\n+\t\tif (params-\u003eauthsize) {\n+\t\t\terr = -EOPNOTSUPP;\n+\t\t\tgoto out;\n+\t\t}\n+\t\tctx-\u003ekey = kzalloc_obj(struct aes_key);\n+\t\tif (!ctx-\u003ekey) {\n+\t\t\terr = -ENOMEM;\n+\t\t\tgoto out;\n+\t\t}\n+\t\terr = aes_preparekey((struct aes_key *)ctx-\u003ekey, params-\u003ekey,\n+\t\t\t\t     params-\u003ekey_len);\n+\t\tbreak;\n+\tdefault:\n+\t\tWARN_ON_ONCE(1);\n+\t\terr = -EOPNOTSUPP;\n+\t\tbreak;\n \t}\n \n-\t*err = type-\u003esetkey(ctx-\u003etfm, params-\u003ekey, params-\u003ekey_len);\n-\tif (*err)\n-\t\tgoto err_free_tfm;\n-\n-\tif (type-\u003eget_flags(ctx-\u003etfm) \u0026 CRYPTO_TFM_NEED_KEY) {\n-\t\t*err = -EINVAL;\n-\t\tgoto err_free_tfm;\n+out:\n+\tif (err) {\n+\t\tkfree_sensitive(ctx-\u003ekey);\n+\t\tkfree_sensitive(ctx);\n+\t\t*err_ret = err;\n+\t\treturn NULL;\n \t}\n-\n-\tctx-\u003esiv_len = type-\u003eivsize(ctx-\u003etfm) + type-\u003estatesize(ctx-\u003etfm);\n-\n \trefcount_set(\u0026ctx-\u003eusage, 1);\n-\n+\t*err_ret = 0;\n \treturn ctx;\n-\n-err_free_tfm:\n-\ttype-\u003efree_tfm(ctx-\u003etfm);\n-err_free_ctx:\n-\tkfree(ctx);\n-err_module_put:\n-\tmodule_put(type-\u003eowner);\n-\n-\treturn NULL;\n }\n \n static void crypto_free_cb(struct rcu_head *head)\n@@ -225,9 +161,8 @@ static void crypto_free_cb(struct rcu_head *head)\n \tstruct bpf_crypto_ctx *ctx;\n \n \tctx = container_of(head, struct bpf_crypto_ctx, rcu);\n-\tctx-\u003etype-\u003efree_tfm(ctx-\u003etfm);\n-\tmodule_put(ctx-\u003etype-\u003eowner);\n-\tkfree(ctx);\n+\tkfree_sensitive(ctx-\u003ekey);\n+\tkfree_sensitive(ctx);\n }\n \n /**\n@@ -267,27 +202,53 @@ __bpf_kfunc void bpf_crypto_ctx_release_dtor(void *ctx)\n }\n CFI_NOSEAL(bpf_crypto_ctx_release_dtor);\n \n+static int bpf_aes_cbc_crypt(u8 *dst, u32 dst_len, const u8 *src, u32 src_len,\n+\t\t\t     u8 *iv, u32 iv_len, const struct aes_key *key,\n+\t\t\t     bool decrypt)\n+{\n+\tif (iv_len != AES_BLOCK_SIZE)\n+\t\treturn -EINVAL;\n+\tif (src_len % AES_BLOCK_SIZE || dst_len \u003c src_len)\n+\t\treturn -EINVAL;\n+\tif (decrypt)\n+\t\taes_cbc_decrypt(dst, src, src_len, iv, key);\n+\telse\n+\t\taes_cbc_encrypt(dst, src, src_len, iv, key);\n+\treturn 0;\n+}\n+\n+static int bpf_aes_ecb_crypt(u8 *dst, u32 dst_len, const u8 *src, u32 src_len,\n+\t\t\t     u8 *iv, u32 iv_len, const struct aes_key *key,\n+\t\t\t     bool decrypt)\n+{\n+\tif (iv_len != 0)\n+\t\treturn -EINVAL;\n+\tif (src_len % AES_BLOCK_SIZE || dst_len \u003c src_len)\n+\t\treturn -EINVAL;\n+\tif (decrypt)\n+\t\taes_ecb_decrypt(dst, src, src_len, key);\n+\telse\n+\t\taes_ecb_encrypt(dst, src, src_len, key);\n+\treturn 0;\n+}\n+\n static int bpf_crypto_crypt(const struct bpf_crypto_ctx *ctx,\n \t\t\t    const struct bpf_dynptr_kern *src,\n \t\t\t    const struct bpf_dynptr_kern *dst,\n-\t\t\t    const struct bpf_dynptr_kern *siv,\n+\t\t\t    const struct bpf_dynptr_kern *iv,\n \t\t\t    bool decrypt)\n {\n-\tu32 src_len, dst_len, siv_len;\n+\tu32 src_len, dst_len, iv_len;\n \tconst u8 *psrc;\n \tu8 *pdst, *piv;\n-\tint err;\n \n \tif (__bpf_dynptr_is_rdonly(dst))\n \t\treturn -EINVAL;\n \n-\tsiv_len = siv ? __bpf_dynptr_size(siv) : 0;\n+\tiv_len = iv ? __bpf_dynptr_size(iv) : 0;\n \tsrc_len = __bpf_dynptr_size(src);\n \tdst_len = __bpf_dynptr_size(dst);\n-\tif (!src_len || !dst_len || src_len \u003e dst_len)\n-\t\treturn -EINVAL;\n-\n-\tif (siv_len != ctx-\u003esiv_len)\n+\tif (!src_len || !dst_len)\n \t\treturn -EINVAL;\n \n \tpsrc = __bpf_dynptr_data(src, src_len);\n@@ -297,14 +258,20 @@ static int bpf_crypto_crypt(const struct bpf_crypto_ctx *ctx,\n \tif (!pdst)\n \t\treturn -EINVAL;\n \n-\tpiv = siv_len ? __bpf_dynptr_data_rw(siv, siv_len) : NULL;\n-\tif (siv_len \u0026\u0026 !piv)\n+\tpiv = iv_len ? __bpf_dynptr_data_rw(iv, iv_len) : NULL;\n+\tif (iv_len \u0026\u0026 !piv)\n \t\treturn -EINVAL;\n \n-\terr = decrypt ? ctx-\u003etype-\u003edecrypt(ctx-\u003etfm, psrc, pdst, src_len, piv)\n-\t\t      : ctx-\u003etype-\u003eencrypt(ctx-\u003etfm, psrc, pdst, src_len, piv);\n-\n-\treturn err;\n+\tswitch (ctx-\u003ealgo) {\n+\tcase BPF_ALGO_AES_CBC:\n+\t\treturn bpf_aes_cbc_crypt(pdst, dst_len, psrc, src_len, piv,\n+\t\t\t\t\t iv_len, ctx-\u003ekey, decrypt);\n+\tcase BPF_ALGO_AES_ECB:\n+\t\treturn bpf_aes_ecb_crypt(pdst, dst_len, psrc, src_len, piv,\n+\t\t\t\t\t iv_len, ctx-\u003ekey, decrypt);\n+\tdefault:\n+\t\treturn -EINVAL;\n+\t}\n }\n \n /**\n@@ -312,20 +279,20 @@ static int bpf_crypto_crypt(const struct bpf_crypto_ctx *ctx,\n  * @ctx:\t\tThe crypto context being used. The ctx must be a trusted pointer.\n  * @src:\t\tbpf_dynptr to the encrypted data. Must be a trusted pointer.\n  * @dst:\t\tbpf_dynptr to the buffer where to store the result. Must be a trusted pointer.\n- * @siv__nullable:\tbpf_dynptr to IV data and state data to be used by decryptor. May be NULL.\n+ * @iv__nullable:\tbpf_dynptr to the initialization vector. May be NULL.\n  *\n  * Decrypts provided buffer using IV data and the crypto context. Crypto context must be configured.\n  */\n __bpf_kfunc int bpf_crypto_decrypt(struct bpf_crypto_ctx *ctx,\n \t\t\t\t   const struct bpf_dynptr *src,\n \t\t\t\t   const struct bpf_dynptr *dst,\n-\t\t\t\t   const struct bpf_dynptr *siv__nullable)\n+\t\t\t\t   const struct bpf_dynptr *iv__nullable)\n {\n \tconst struct bpf_dynptr_kern *src_kern = (struct bpf_dynptr_kern *)src;\n \tconst struct bpf_dynptr_kern *dst_kern = (struct bpf_dynptr_kern *)dst;\n-\tconst struct bpf_dynptr_kern *siv_kern = (struct bpf_dynptr_kern *)siv__nullable;\n+\tconst struct bpf_dynptr_kern *iv_kern = (struct bpf_dynptr_kern *)iv__nullable;\n \n-\treturn bpf_crypto_crypt(ctx, src_kern, dst_kern, siv_kern, true);\n+\treturn bpf_crypto_crypt(ctx, src_kern, dst_kern, iv_kern, true);\n }\n \n /**\n@@ -333,20 +300,20 @@ __bpf_kfunc int bpf_crypto_decrypt(struct bpf_crypto_ctx *ctx,\n  * @ctx:\t\tThe crypto context being used. The ctx must be a trusted pointer.\n  * @src:\t\tbpf_dynptr to the plain data. Must be a trusted pointer.\n  * @dst:\t\tbpf_dynptr to the buffer where to store the result. Must be a trusted pointer.\n- * @siv__nullable:\tbpf_dynptr to IV data and state data to be used by decryptor. May be NULL.\n+ * @iv__nullable:\tbpf_dynptr to the initialization vector. May be NULL.\n  *\n  * Encrypts provided buffer using IV data and the crypto context. Crypto context must be configured.\n  */\n __bpf_kfunc int bpf_crypto_encrypt(struct bpf_crypto_ctx *ctx,\n \t\t\t\t   const struct bpf_dynptr *src,\n \t\t\t\t   const struct bpf_dynptr *dst,\n-\t\t\t\t   const struct bpf_dynptr *siv__nullable)\n+\t\t\t\t   const struct bpf_dynptr *iv__nullable)\n {\n \tconst struct bpf_dynptr_kern *src_kern = (struct bpf_dynptr_kern *)src;\n \tconst struct bpf_dynptr_kern *dst_kern = (struct bpf_dynptr_kern *)dst;\n-\tconst struct bpf_dynptr_kern *siv_kern = (struct bpf_dynptr_kern *)siv__nullable;\n+\tconst struct bpf_dynptr_kern *iv_kern = (struct bpf_dynptr_kern *)iv__nullable;\n \n-\treturn bpf_crypto_crypt(ctx, src_kern, dst_kern, siv_kern, false);\n+\treturn bpf_crypto_crypt(ctx, src_kern, dst_kern, iv_kern, false);\n }\n \n __bpf_kfunc_end_defs();\ndiff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c\nindex d62c0f74cff5e..c6957ef12d763 100644\n--- a/kernel/bpf/verifier.c\n+++ b/kernel/bpf/verifier.c\n@@ -4664,7 +4664,7 @@ BTF_ID(struct, cgroup)\n BTF_ID(struct, bpf_cpumask)\n #endif\n BTF_ID(struct, task_struct)\n-#ifdef CONFIG_CRYPTO\n+#ifdef CONFIG_BPF_CRYPTO\n BTF_ID(struct, bpf_crypto_ctx)\n #endif\n #ifdef CONFIG_INET\ndiff --git a/tools/testing/selftests/bpf/config b/tools/testing/selftests/bpf/config\nindex d292cb60a5a4e..2b883b388f90c 100644\n--- a/tools/testing/selftests/bpf/config\n+++ b/tools/testing/selftests/bpf/config\n@@ -18,7 +18,7 @@ CONFIG_CRYPTO_SHA512=y\n CONFIG_CRYPTO_USER_API=y\n CONFIG_CRYPTO_USER_API_HASH=y\n CONFIG_CRYPTO_USER_API_SKCIPHER=y\n-CONFIG_CRYPTO_SKCIPHER=y\n+CONFIG_CRYPTO_CBC=y\n CONFIG_CRYPTO_ECB=y\n CONFIG_CRYPTO_AES=y\n CONFIG_DEBUG_INFO=y\n"
}

2/1 2026/09/24 05:37 agent patch-evaluator
1m Model:
core

Args:
null

Results:
{
  "EnableConfigs": [
    "BPF_CRYPTO",
    "BPF_SYSCALL",
    "CRYPTO_AES",
    "CRYPTO_CBC",
    "CRYPTO_ECB"
  ],
  "FocusSymbols": [
    "bpf_crypto_ctx_create",
    "bpf_crypto_encrypt",
    "bpf_crypto_decrypt",
    "bpf_crypto_crypt",
    "bpf_aes_cbc_crypt",
    "bpf_aes_ecb_crypt",
    "bpf_crypto_find_algo"
  ],
  "Reasoning": "The patch modifies core BPF crypto functionality, replacing the crypto skcipher API tfm indirection with direct AES-CBC and AES-ECB library routines. It alters context allocation and lifetime management, key setup, input/output validation, and encryption/decryption routines. This code is reachable from userspace via the BPF syscall and execution of BPF programs calling the BPF crypto kfuncs, making it an ideal target for fuzzing.",
  "WorthFuzzing": true
}

Instruction:
You are an expert Linux kernel maintainer and security engineer.
Your job is to review a provided patch series and evaluate whether it warrants fuzzing with syzkaller.

IMPORTANT: The changes have ALREADY been applied and committed as the HEAD commit in
your workspace. Do NOT rely on internal assumptions. You must actively use your code access
tools to inspect the actual source code, callers, and surrounding context.

================================================================================
1. CORE TRIAGE PHILOSOPHY
================================================================================
The goal of patch fuzzing is to discover crashes, regressions, exposed latent bugs,
and newly triggered assertions introduced by the patch series.

- REACHABILITY IS THE PRIMARY GATE:
  Fuzzing can only discover bugs in code that can actually execute in standard virtualized
  environments (GCE or QEMU, utilizing software-emulated devices like USB gadgets, netdev, tun/tap).
  If the modified code is structurally unreachable (see Section 2), it MUST NOT be fuzzed,
  regardless of whether it adds assertions or complex logic.

- DO NOT BLINDLY TRUST "NO FUNCTIONAL CHANGE" (NFCI) OR "REFACTORING" CLAIMS:
  Patch authors routinely label changes as "cleanups", "refactorings", or state
  "No functional change intended". Do NOT take these claims at face value.
  Code refactorings that rearrange logic, introduce helper functions, or alter state management
  in core subsystems frequently introduce subtle semantic shifts or uncover latent kernel bugs.
  If reachable executable code is modified or refactored, it MUST be fuzzed.

- NEW OR MODIFIED ASSERTIONS IN REACHABLE CODE MUST BE FUZZED:
  When a patch introduces or modifies runtime checks or assertions (e.g., WARN_ON*, VM_WARN_ON*,
  BUG_ON*, lockdep_assert*) in reachable code paths, it enforces new or stricter invariants.
  Even if the author believes the invariant always holds, fuzzing is essential to verify whether
  an unusual sequence of operations can violate it.

================================================================================
2. WHEN TO RETURN WorthFuzzing=false (NEGATIVE CRITERIA)
================================================================================
Return WorthFuzzing=false ONLY IF all modified code falls strictly into one or more of these categories:

- Non-kernel and non-executable changes:
  * Modifications to Documentation/, comments, or spelling fixes.
  * User-space directories, self-tests, samples, or scripts (e.g., tools/, samples/, scripts/, usr/)
    that do not affect the compiled kernel image (vmlinux) or kernel modules.
  * Purely decorative logging (e.g., message strings in pr_err, printk, dev_info) or tracepoints
    that do not alter control flow or data structures.
  * Build system or Kconfig changes that do not alter compiled C logic.
- Structurally unreachable hardware:
  * Vendor-specific PCIe switches, SmartNICs, or GPU drivers (e.g., mlxsw, pds_core, qed,
    ionic, amdgpu) requiring physical ASIC/PCIe cards not emulated in standard QEMU.
- Unreachable execution paths:
  * Driver teardown callbacks (.remove, .shutdown, pci_unregister_driver) executed only during
    physical PCI hot-unplug or manual sysfs driver unbinding.
  * Code paths exclusive to architectures other than the target architecture.

================================================================================
3. WHEN TO RETURN WorthFuzzing=true (POSITIVE CRITERIA)
================================================================================
Return WorthFuzzing=true whenever the patch touches reachable executable code, including:
- Core Subsystems:
  * Any logic modifications in memory management (mm/), synchronization/locking (kernel/locking/),
    BPF, scheduler, core networking, VFS, or syscall handling.
- Refactorings and Code Cleanups:
  * Any restructuring of reachable data structures, helper abstractions, or algorithm flows.
- Runtime Assertions and Defensive Checks:
  * Any introduction or alteration of assertions (WARN_ON*, VM_WARN_ON*, BUG_ON*, etc.) in reachable paths.
- Reachable Drivers and Protocols:
  * Drivers accessible via virtual buses (virtio, USB gadget, loopback, netlink, binder, sockets, etc.).

================================================================================
4. EXTRACTING FocusSymbols (PREVENTING DILUTION)
================================================================================
When WorthFuzzing=true, you must extract specific kernel functions into FocusSymbols to guide the fuzzer:

- AVOID UBIQUITOUS LIFECYCLE HOT-PATHS:
  Do NOT list generic, ubiquitous functions called by almost every program in the corpus
  (including, but not limited to: general memory allocators and deallocators, page fault
  and trap handlers, or core synchronization primitives; this is not an exhaustive list).
  Listing ubiquitous functions causes the fuzzer to classify thousands of unrelated tests as "focused",
  which severely dilutes fuzzing effort away from the actual changes.

- TARGET SPECIFIC FEATURE LOGIC AND ENTRYPOINTS:
  List functions that specifically implement the logic being added or altered, or direct API entrypoints
  for the subsystem feature under review.

- HANDLING STATIC INLINE FUNCTIONS IN HEADERS (.h):
  Compiler-inlined static functions (such as static inlines in mm/*.h or include/linux/*.h) lack
  distinct symbol addresses in vmlinux and cannot be targeted directly by symbol coverage filters.
  If the changes are primarily in static inline helpers, identify non-static, feature-specific caller
  functions in .c files that exercise them (avoiding ubiquitous lifecycle wrappers).

================================================================================
5. IDENTIFYING EnableConfigs
================================================================================
Identify any specific CONFIG_ options required to properly compile and reach the modified code:
- Inspect Kconfig files and #ifdef guards; do not make assumptions.
- Check "depends on" lines in Kconfig to include any non-standard parent subsystem configs needed.
- Strip any 'CONFIG_' prefix (e.g., return "NET_IPV4" instead of "CONFIG_NET_IPV4").
Prefer calling several tools at the same time to save round-trips.


Use set-results tool to provide results of the analysis.
It must be called exactly once before the final reply.
Ignore results of this tool.

Prompt:
Target architecture: amd64

For your convenience, here is the diff of the changes:
commit 26717ed6fe6d35c327012a827650ef4b400e489f
Author: syz-cluster <triage@syzkaller.com>
Date:   Thu Sep 24 05:36:58 2026 +0000

    syz-cluster: applied patch under review

diff --git a/MAINTAINERS b/MAINTAINERS
index 6215fcb077705..0fcdfe48c62a0 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -4936,8 +4936,6 @@ BPF [CRYPTO]
 M:	Vadim Fedorenko <vadim.fedorenko@linux.dev>
 L:	bpf@vger.kernel.org
 S:	Maintained
-F:	crypto/bpf_crypto_skcipher.c
-F:	include/linux/bpf_crypto.h
 F:	kernel/bpf/crypto.c
 
 BPF [DOCUMENTATION] (Related to Standardization)
diff --git a/crypto/Makefile b/crypto/Makefile
index 8386d55a9755e..33bb5ad595e8f 100644
--- a/crypto/Makefile
+++ b/crypto/Makefile
@@ -22,9 +22,6 @@ crypto_skcipher-y += lskcipher.o
 crypto_skcipher-y += skcipher.o
 
 obj-$(CONFIG_CRYPTO_SKCIPHER2) += crypto_skcipher.o
-ifeq ($(CONFIG_BPF_SYSCALL),y)
-obj-$(CONFIG_CRYPTO_SKCIPHER2) += bpf_crypto_skcipher.o
-endif
 
 obj-$(CONFIG_CRYPTO_SEQIV) += seqiv.o
 obj-$(CONFIG_CRYPTO_ECHAINIV) += echainiv.o
diff --git a/crypto/bpf_crypto_skcipher.c b/crypto/bpf_crypto_skcipher.c
deleted file mode 100644
index a88798d3e8c87..0000000000000
--- a/crypto/bpf_crypto_skcipher.c
+++ /dev/null
@@ -1,83 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/* Copyright (c) 2024 Meta, Inc */
-#include <linux/types.h>
-#include <linux/module.h>
-#include <linux/bpf_crypto.h>
-#include <crypto/skcipher.h>
-
-static void *bpf_crypto_lskcipher_alloc_tfm(const char *algo)
-{
-	return crypto_alloc_lskcipher(algo, 0, 0);
-}
-
-static void bpf_crypto_lskcipher_free_tfm(void *tfm)
-{
-	crypto_free_lskcipher(tfm);
-}
-
-static int bpf_crypto_lskcipher_has_algo(const char *algo)
-{
-	return crypto_has_skcipher(algo, CRYPTO_ALG_TYPE_LSKCIPHER, CRYPTO_ALG_TYPE_MASK);
-}
-
-static int bpf_crypto_lskcipher_setkey(void *tfm, const u8 *key, unsigned int keylen)
-{
-	return crypto_lskcipher_setkey(tfm, key, keylen);
-}
-
-static u32 bpf_crypto_lskcipher_get_flags(void *tfm)
-{
-	return crypto_lskcipher_get_flags(tfm);
-}
-
-static unsigned int bpf_crypto_lskcipher_ivsize(void *tfm)
-{
-	return crypto_lskcipher_ivsize(tfm);
-}
-
-static unsigned int bpf_crypto_lskcipher_statesize(void *tfm)
-{
-	return crypto_lskcipher_statesize(tfm);
-}
-
-static int bpf_crypto_lskcipher_encrypt(void *tfm, const u8 *src, u8 *dst,
-					unsigned int len, u8 *siv)
-{
-	return crypto_lskcipher_encrypt(tfm, src, dst, len, siv);
-}
-
-static int bpf_crypto_lskcipher_decrypt(void *tfm, const u8 *src, u8 *dst,
-					unsigned int len, u8 *siv)
-{
-	return crypto_lskcipher_decrypt(tfm, src, dst, len, siv);
-}
-
-static const struct bpf_crypto_type bpf_crypto_lskcipher_type = {
-	.alloc_tfm	= bpf_crypto_lskcipher_alloc_tfm,
-	.free_tfm	= bpf_crypto_lskcipher_free_tfm,
-	.has_algo	= bpf_crypto_lskcipher_has_algo,
-	.setkey		= bpf_crypto_lskcipher_setkey,
-	.encrypt	= bpf_crypto_lskcipher_encrypt,
-	.decrypt	= bpf_crypto_lskcipher_decrypt,
-	.ivsize		= bpf_crypto_lskcipher_ivsize,
-	.statesize	= bpf_crypto_lskcipher_statesize,
-	.get_flags	= bpf_crypto_lskcipher_get_flags,
-	.owner		= THIS_MODULE,
-	.name		= "skcipher",
-};
-
-static int __init bpf_crypto_skcipher_init(void)
-{
-	return bpf_crypto_register_type(&bpf_crypto_lskcipher_type);
-}
-
-static void __exit bpf_crypto_skcipher_exit(void)
-{
-	int err = bpf_crypto_unregister_type(&bpf_crypto_lskcipher_type);
-	WARN_ON_ONCE(err);
-}
-
-module_init(bpf_crypto_skcipher_init);
-module_exit(bpf_crypto_skcipher_exit);
-MODULE_LICENSE("GPL");
-MODULE_DESCRIPTION("Symmetric key cipher support for BPF");
diff --git a/include/linux/bpf_crypto.h b/include/linux/bpf_crypto.h
deleted file mode 100644
index a41e71d4e2d9f..0000000000000
--- a/include/linux/bpf_crypto.h
+++ /dev/null
@@ -1,24 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-only */
-/* Copyright (c) 2024 Meta Platforms, Inc. and affiliates. */
-#ifndef _BPF_CRYPTO_H
-#define _BPF_CRYPTO_H
-
-struct bpf_crypto_type {
-	void *(*alloc_tfm)(const char *algo);
-	void (*free_tfm)(void *tfm);
-	int (*has_algo)(const char *algo);
-	int (*setkey)(void *tfm, const u8 *key, unsigned int keylen);
-	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);
-	unsigned int (*ivsize)(void *tfm);
-	unsigned int (*statesize)(void *tfm);
-	u32 (*get_flags)(void *tfm);
-	struct module *owner;
-	char name[14];
-};
-
-int bpf_crypto_register_type(const struct bpf_crypto_type *type);
-int bpf_crypto_unregister_type(const struct bpf_crypto_type *type);
-
-#endif /* _BPF_CRYPTO_H */
diff --git a/kernel/bpf/Kconfig b/kernel/bpf/Kconfig
index d7d25477ef481..a44ecfa3e9ef5 100644
--- a/kernel/bpf/Kconfig
+++ b/kernel/bpf/Kconfig
@@ -91,6 +91,15 @@ config BPF_UNPRIV_DEFAULT_OFF
 
 	  If you are unsure how to answer this question, answer Y.
 
+config BPF_CRYPTO
+	def_bool y
+	depends on BPF_SYSCALL
+	depends on CRYPTO_LIB_AES_CBC
+	depends on CRYPTO_LIB_AES_ECB
+	help
+	  Provide the kfuncs needed for BPF programs to encrypt and decrypt
+	  data. The supported algorithms are AES-CBC and AES-ECB.
+
 source "kernel/bpf/preload/Kconfig"
 
 config BPF_LSM
diff --git a/kernel/bpf/Makefile b/kernel/bpf/Makefile
index 9a92c348bbda6..c1f9b0d3468d3 100644
--- a/kernel/bpf/Makefile
+++ b/kernel/bpf/Makefile
@@ -58,9 +58,7 @@ obj-$(CONFIG_BPF_SYSCALL) += cpumask.o
 # semantics within pahole are revisited accordingly.
 obj-${CONFIG_BPF_LSM} += bpf_lsm_proto.o bpf_lsm.o
 endif
-ifneq ($(CONFIG_CRYPTO),)
-obj-$(CONFIG_BPF_SYSCALL) += crypto.o
-endif
+obj-$(CONFIG_BPF_CRYPTO) += crypto.o
 obj-$(CONFIG_BPF_PRELOAD) += preload/
 
 obj-$(CONFIG_BPF_SYSCALL) += relo_core.o
diff --git a/kernel/bpf/crypto.c b/kernel/bpf/crypto.c
index 51f89cecefb4d..b812204a4d93b 100644
--- a/kernel/bpf/crypto.c
+++ b/kernel/bpf/crypto.c
@@ -1,19 +1,13 @@
 // SPDX-License-Identifier: GPL-2.0-only
 /* Copyright (c) 2024 Meta, Inc */
 #include <linux/bpf.h>
-#include <linux/bpf_crypto.h>
 #include <linux/bpf_mem_alloc.h>
 #include <linux/btf.h>
 #include <linux/btf_ids.h>
 #include <linux/filter.h>
-#include <linux/scatterlist.h>
 #include <linux/skbuff.h>
-#include <crypto/skcipher.h>
-
-struct bpf_crypto_type_list {
-	const struct bpf_crypto_type *type;
-	struct list_head list;
-};
+#include <crypto/aes-cbc.h>
+#include <crypto/aes-ecb.h>
 
 /* BPF crypto initialization parameters struct */
 /**
@@ -36,94 +30,52 @@ struct bpf_crypto_params {
 	u32 authsize;
 };
 
-static LIST_HEAD(bpf_crypto_types);
-static DECLARE_RWSEM(bpf_crypto_types_sem);
+enum bpf_crypto_algo_id {
+	BPF_ALGO_AES_CBC,
+	BPF_ALGO_AES_ECB,
+};
+
+static const struct {
+	const char *type_name;
+	const char *algo_name;
+	enum bpf_crypto_algo_id algo;
+} bpf_crypto_algos[] = {
+	{ "skcipher", "cbc(aes)", BPF_ALGO_AES_CBC },
+	{ "skcipher", "ecb(aes)", BPF_ALGO_AES_ECB },
+};
+
+static bool bpf_crypto_find_algo(const struct bpf_crypto_params *params,
+				 enum bpf_crypto_algo_id *id_ret)
+{
+	for (size_t i = 0; i < ARRAY_SIZE(bpf_crypto_algos); i++) {
+		if (strncmp(bpf_crypto_algos[i].type_name, params->type,
+			    sizeof(params->type)) == 0 &&
+		    strncmp(bpf_crypto_algos[i].algo_name, params->algo,
+			    sizeof(params->algo)) == 0) {
+			*id_ret = bpf_crypto_algos[i].algo;
+			return true;
+		}
+	}
+	return false;
+}
 
 /**
  * struct bpf_crypto_ctx - refcounted BPF crypto context structure
- * @type:	The pointer to bpf crypto type
- * @tfm:	The pointer to instance of crypto API struct.
- * @siv_len:    Size of IV and state storage for cipher
+ * @algo:	The crypto algorithm ID
+ * @key:	Pointer to the struct aes_key.  'void *' is used so that the
+ *		key isn't readable from BPF programs via BTF.
  * @rcu:	The RCU head used to free the crypto context with RCU safety.
  * @usage:	Object reference counter. When the refcount goes to 0, the
  *		memory is released back to the BPF allocator, which provides
  *		RCU safety.
  */
 struct bpf_crypto_ctx {
-	const struct bpf_crypto_type *type;
-	void *tfm;
-	u32 siv_len;
+	enum bpf_crypto_algo_id algo;
+	void *key;
 	struct rcu_head rcu;
 	refcount_t usage;
 };
 
-int bpf_crypto_register_type(const struct bpf_crypto_type *type)
-{
-	struct bpf_crypto_type_list *node;
-	int err = -EBUSY;
-
-	down_write(&bpf_crypto_types_sem);
-	list_for_each_entry(node, &bpf_crypto_types, list) {
-		if (!strcmp(node->type->name, type->name))
-			goto unlock;
-	}
-
-	node = kmalloc_obj(*node);
-	err = -ENOMEM;
-	if (!node)
-		goto unlock;
-
-	node->type = type;
-	list_add(&node->list, &bpf_crypto_types);
-	err = 0;
-
-unlock:
-	up_write(&bpf_crypto_types_sem);
-
-	return err;
-}
-EXPORT_SYMBOL_GPL(bpf_crypto_register_type);
-
-int bpf_crypto_unregister_type(const struct bpf_crypto_type *type)
-{
-	struct bpf_crypto_type_list *node;
-	int err = -ENOENT;
-
-	down_write(&bpf_crypto_types_sem);
-	list_for_each_entry(node, &bpf_crypto_types, list) {
-		if (strcmp(node->type->name, type->name))
-			continue;
-
-		list_del(&node->list);
-		kfree(node);
-		err = 0;
-		break;
-	}
-	up_write(&bpf_crypto_types_sem);
-
-	return err;
-}
-EXPORT_SYMBOL_GPL(bpf_crypto_unregister_type);
-
-static const struct bpf_crypto_type *bpf_crypto_get_type(const char *name)
-{
-	const struct bpf_crypto_type *type = ERR_PTR(-ENOENT);
-	struct bpf_crypto_type_list *node;
-
-	down_read(&bpf_crypto_types_sem);
-	list_for_each_entry(node, &bpf_crypto_types, list) {
-		if (strcmp(node->type->name, name))
-			continue;
-
-		if (try_module_get(node->type->owner))
-			type = node->type;
-		break;
-	}
-	up_read(&bpf_crypto_types_sem);
-
-	return type;
-}
-
 __bpf_kfunc_start_defs();
 
 /**
@@ -132,92 +84,76 @@ __bpf_kfunc_start_defs();
  * Allocates a crypto context that can be used, acquired, and released by
  * a BPF program. The crypto context returned by this function must either
  * be embedded in a map as a kptr, or freed with bpf_crypto_ctx_release().
- * As crypto API functions use GFP_KERNEL allocations, this function can
- * only be used in sleepable BPF programs.
+ * As this uses a GFP_KERNEL allocation, this function can only be used in
+ * sleepable BPF programs.
  *
  * bpf_crypto_ctx_create() allocates memory for crypto context.
  * 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
- * @err:	integer to store error code when NULL is returned.
+ * @params__sz:	size of struct bpf_crypto_params used by bpf program
+ * @err_ret:	integer to store error code when NULL is returned.
  */
 __bpf_kfunc struct bpf_crypto_ctx *
 bpf_crypto_ctx_create(const struct bpf_crypto_params *params, u32 params__sz,
-		      int *err)
+		      int *err_ret)
 {
-	const struct bpf_crypto_type *type;
 	struct bpf_crypto_ctx *ctx;
+	int err;
 
 	if (!params || params->reserved[0] || params->reserved[1] ||
 	    params__sz != sizeof(struct bpf_crypto_params)) {
-		*err = -EINVAL;
+		*err_ret = -EINVAL;
 		return NULL;
 	}
 
-	type = bpf_crypto_get_type(params->type);
-	if (IS_ERR(type)) {
-		*err = PTR_ERR(type);
-		return NULL;
-	}
-
-	if (!type->has_algo(params->algo)) {
-		*err = -EOPNOTSUPP;
-		goto err_module_put;
-	}
-
-	if (!!params->authsize ^ !!type->setauthsize) {
-		*err = -EOPNOTSUPP;
-		goto err_module_put;
-	}
-
 	if (!params->key_len || params->key_len > sizeof(params->key)) {
-		*err = -EINVAL;
-		goto err_module_put;
+		*err_ret = -EINVAL;
+		return NULL;
 	}
 
 	ctx = kzalloc_obj(*ctx);
 	if (!ctx) {
-		*err = -ENOMEM;
-		goto err_module_put;
+		*err_ret = -ENOMEM;
+		return NULL;
 	}
 
-	ctx->type = type;
-	ctx->tfm = type->alloc_tfm(params->algo);
-	if (IS_ERR(ctx->tfm)) {
-		*err = PTR_ERR(ctx->tfm);
-		goto err_free_ctx;
+	if (!bpf_crypto_find_algo(params, &ctx->algo)) {
+		err = -EOPNOTSUPP;
+		goto out;
 	}
 
-	if (params->authsize) {
-		*err = type->setauthsize(ctx->tfm, params->authsize);
-		if (*err)
-			goto err_free_tfm;
+	switch (ctx->algo) {
+	case BPF_ALGO_AES_CBC:
+	case BPF_ALGO_AES_ECB:
+		if (params->authsize) {
+			err = -EOPNOTSUPP;
+			goto out;
+		}
+		ctx->key = kzalloc_obj(struct aes_key);
+		if (!ctx->key) {
+			err = -ENOMEM;
+			goto out;
+		}
+		err = aes_preparekey((struct aes_key *)ctx->key, params->key,
+				     params->key_len);
+		break;
+	default:
+		WARN_ON_ONCE(1);
+		err = -EOPNOTSUPP;
+		break;
 	}
 
-	*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;
+out:
+	if (err) {
+		kfree_sensitive(ctx->key);
+		kfree_sensitive(ctx);
+		*err_ret = err;
+		return NULL;
 	}
-
-	ctx->siv_len = type->ivsize(ctx->tfm) + type->statesize(ctx->tfm);
-
 	refcount_set(&ctx->usage, 1);
-
+	*err_ret = 0;
 	return ctx;
-
-err_free_tfm:
-	type->free_tfm(ctx->tfm);
-err_free_ctx:
-	kfree(ctx);
-err_module_put:
-	module_put(type->owner);
-
-	return NULL;
 }
 
 static void crypto_free_cb(struct rcu_head *head)
@@ -225,9 +161,8 @@ static void crypto_free_cb(struct rcu_head *head)
 	struct bpf_crypto_ctx *ctx;
 
 	ctx = container_of(head, struct bpf_crypto_ctx, rcu);
-	ctx->type->free_tfm(ctx->tfm);
-	module_put(ctx->type->owner);
-	kfree(ctx);
+	kfree_sensitive(ctx->key);
+	kfree_sensitive(ctx);
 }
 
 /**
@@ -267,27 +202,53 @@ __bpf_kfunc void bpf_crypto_ctx_release_dtor(void *ctx)
 }
 CFI_NOSEAL(bpf_crypto_ctx_release_dtor);
 
+static int bpf_aes_cbc_crypt(u8 *dst, u32 dst_len, const u8 *src, u32 src_len,
+			     u8 *iv, u32 iv_len, const struct aes_key *key,
+			     bool decrypt)
+{
+	if (iv_len != AES_BLOCK_SIZE)
+		return -EINVAL;
+	if (src_len % AES_BLOCK_SIZE || dst_len < src_len)
+		return -EINVAL;
+	if (decrypt)
+		aes_cbc_decrypt(dst, src, src_len, iv, key);
+	else
+		aes_cbc_encrypt(dst, src, src_len, iv, key);
+	return 0;
+}
+
+static int bpf_aes_ecb_crypt(u8 *dst, u32 dst_len, const u8 *src, u32 src_len,
+			     u8 *iv, u32 iv_len, const struct aes_key *key,
+			     bool decrypt)
+{
+	if (iv_len != 0)
+		return -EINVAL;
+	if (src_len % AES_BLOCK_SIZE || dst_len < src_len)
+		return -EINVAL;
+	if (decrypt)
+		aes_ecb_decrypt(dst, src, src_len, key);
+	else
+		aes_ecb_encrypt(dst, src, src_len, key);
+	return 0;
+}
+
 static int bpf_crypto_crypt(const struct bpf_crypto_ctx *ctx,
 			    const struct bpf_dynptr_kern *src,
 			    const struct bpf_dynptr_kern *dst,
-			    const struct bpf_dynptr_kern *siv,
+			    const struct bpf_dynptr_kern *iv,
 			    bool decrypt)
 {
-	u32 src_len, dst_len, siv_len;
+	u32 src_len, dst_len, iv_len;
 	const u8 *psrc;
 	u8 *pdst, *piv;
-	int err;
 
 	if (__bpf_dynptr_is_rdonly(dst))
 		return -EINVAL;
 
-	siv_len = siv ? __bpf_dynptr_size(siv) : 0;
+	iv_len = iv ? __bpf_dynptr_size(iv) : 0;
 	src_len = __bpf_dynptr_size(src);
 	dst_len = __bpf_dynptr_size(dst);
-	if (!src_len || !dst_len || src_len > dst_len)
-		return -EINVAL;
-
-	if (siv_len != ctx->siv_len)
+	if (!src_len || !dst_len)
 		return -EINVAL;
 
 	psrc = __bpf_dynptr_data(src, src_len);
@@ -297,14 +258,20 @@ static int bpf_crypto_crypt(const struct bpf_crypto_ctx *ctx,
 	if (!pdst)
 		return -EINVAL;
 
-	piv = siv_len ? __bpf_dynptr_data_rw(siv, siv_len) : NULL;
-	if (siv_len && !piv)
+	piv = iv_len ? __bpf_dynptr_data_rw(iv, iv_len) : NULL;
+	if (iv_len && !piv)
 		return -EINVAL;
 
-	err = decrypt ? ctx->type->decrypt(ctx->tfm, psrc, pdst, src_len, piv)
-		      : ctx->type->encrypt(ctx->tfm, psrc, pdst, src_len, piv);
-
-	return err;
+	switch (ctx->algo) {
+	case BPF_ALGO_AES_CBC:
+		return bpf_aes_cbc_crypt(pdst, dst_len, psrc, src_len, piv,
+					 iv_len, ctx->key, decrypt);
+	case BPF_ALGO_AES_ECB:
+		return bpf_aes_ecb_crypt(pdst, dst_len, psrc, src_len, piv,
+					 iv_len, ctx->key, decrypt);
+	default:
+		return -EINVAL;
+	}
 }
 
 /**
@@ -312,20 +279,20 @@ static int bpf_crypto_crypt(const struct bpf_crypto_ctx *ctx,
  * @ctx:		The crypto context being used. The ctx must be a trusted pointer.
  * @src:		bpf_dynptr to the encrypted data. Must be a trusted pointer.
  * @dst:		bpf_dynptr to the buffer where to store the result. Must be a trusted pointer.
- * @siv__nullable:	bpf_dynptr to IV data and state data to be used by decryptor. May be NULL.
+ * @iv__nullable:	bpf_dynptr to the initialization vector. May be NULL.
  *
  * Decrypts provided buffer using IV data and the crypto context. Crypto context must be configured.
  */
 __bpf_kfunc int bpf_crypto_decrypt(struct bpf_crypto_ctx *ctx,
 				   const struct bpf_dynptr *src,
 				   const struct bpf_dynptr *dst,
-				   const struct bpf_dynptr *siv__nullable)
+				   const struct bpf_dynptr *iv__nullable)
 {
 	const struct bpf_dynptr_kern *src_kern = (struct bpf_dynptr_kern *)src;
 	const struct bpf_dynptr_kern *dst_kern = (struct bpf_dynptr_kern *)dst;
-	const struct bpf_dynptr_kern *siv_kern = (struct bpf_dynptr_kern *)siv__nullable;
+	const struct bpf_dynptr_kern *iv_kern = (struct bpf_dynptr_kern *)iv__nullable;
 
-	return bpf_crypto_crypt(ctx, src_kern, dst_kern, siv_kern, true);
+	return bpf_crypto_crypt(ctx, src_kern, dst_kern, iv_kern, true);
 }
 
 /**
@@ -333,20 +300,20 @@ __bpf_kfunc int bpf_crypto_decrypt(struct bpf_crypto_ctx *ctx,
  * @ctx:		The crypto context being used. The ctx must be a trusted pointer.
  * @src:		bpf_dynptr to the plain data. Must be a trusted pointer.
  * @dst:		bpf_dynptr to the buffer where to store the result. Must be a trusted pointer.
- * @siv__nullable:	bpf_dynptr to IV data and state data to be used by decryptor. May be NULL.
+ * @iv__nullable:	bpf_dynptr to the initialization vector. May be NULL.
  *
  * Encrypts provided buffer using IV data and the crypto context. Crypto context must be configured.
  */
 __bpf_kfunc int bpf_crypto_encrypt(struct bpf_crypto_ctx *ctx,
 				   const struct bpf_dynptr *src,
 				   const struct bpf_dynptr *dst,
-				   const struct bpf_dynptr *siv__nullable)
+				   const struct bpf_dynptr *iv__nullable)
 {
 	const struct bpf_dynptr_kern *src_kern = (struct bpf_dynptr_kern *)src;
 	const struct bpf_dynptr_kern *dst_kern = (struct bpf_dynptr_kern *)dst;
-	const struct bpf_dynptr_kern *siv_kern = (struct bpf_dynptr_kern *)siv__nullable;
+	const struct bpf_dynptr_kern *iv_kern = (struct bpf_dynptr_kern *)iv__nullable;
 
-	return bpf_crypto_crypt(ctx, src_kern, dst_kern, siv_kern, false);
+	return bpf_crypto_crypt(ctx, src_kern, dst_kern, iv_kern, false);
 }
 
 __bpf_kfunc_end_defs();
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index d62c0f74cff5e..c6957ef12d763 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -4664,7 +4664,7 @@ BTF_ID(struct, cgroup)
 BTF_ID(struct, bpf_cpumask)
 #endif
 BTF_ID(struct, task_struct)
-#ifdef CONFIG_CRYPTO
+#ifdef CONFIG_BPF_CRYPTO
 BTF_ID(struct, bpf_crypto_ctx)
 #endif
 #ifdef CONFIG_INET
diff --git a/tools/testing/selftests/bpf/config b/tools/testing/selftests/bpf/config
index d292cb60a5a4e..2b883b388f90c 100644
--- a/tools/testing/selftests/bpf/config
+++ b/tools/testing/selftests/bpf/config
@@ -18,7 +18,7 @@ CONFIG_CRYPTO_SHA512=y
 CONFIG_CRYPTO_USER_API=y
 CONFIG_CRYPTO_USER_API_HASH=y
 CONFIG_CRYPTO_USER_API_SKCIPHER=y
-CONFIG_CRYPTO_SKCIPHER=y
+CONFIG_CRYPTO_CBC=y
 CONFIG_CRYPTO_ECB=y
 CONFIG_CRYPTO_AES=y
 CONFIG_DEBUG_INFO=y

3/2 2026/09/24 05:37 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 10716
							output: 33
							thoughts: 591
						

Thoughts:
**Analyzing the BPF Crypto Patch for Fuzzing Potential**

Okay, let's break this down. My initial thought is: this patch seems like a pretty significant overhaul of the BPF crypto subsystem. It's clearly a refactoring, moving from the generic crypto API to direct library calls for AES. Let's dig in.

First, I see a clear shift in the dependencies. The old code relied on dynamic registration, which is now gone. The `MAINTAINERS` file has been updated, and key files, like `bpf_crypto_skcipher.c` and `bpf_crypto.h`, are completely deleted, which is a big indicator of the scope of this change. It's now driven by `CONFIG_BPF_CRYPTO` and depends on `BPF_SYSCALL`, `CRYPTO_LIB_AES_CBC`, and `CRYPTO_LIB_AES_ECB`. Interesting.

Looking at `kernel/bpf/crypto.c`, the heart of the change, it's a complete rewrite. They've moved from `crypto_alloc_lskcipher` and that dynamic registration scheme to directly calling `aes_cbc_encrypt/decrypt` and `aes_ecb_encrypt/decrypt`. This implies potentially simpler code, but also a tighter coupling with the specific AES implementations. Any changes in those libraries could directly impact the BPF crypto functionality now. The `bpf_crypto_ctx_*` functions have also been reworked.

The verifier change with the `BTF_ID(struct, bpf_crypto_ctx)` and the selftest config update are signals. These files are updated, so it is necessary to test all this.

The crucial question is: is this reachable? BPF syscalls are standard in QEMU/syzkaller, and this modifies `kernel/bpf/crypto.c`, so it's a very good candidate for fuzzing. Since BPF programs can call the kfuncs involved, it is highly likely it can be tested in a virtualized environment. That means we have a chance to get a good test coverage.

Next, I need to use some tools and look at the symbols and configs. Grepper is going to be my friend to understand what is available and see the surface of this new code. Understanding the argument checks, key setup, and buffer usage will be key to designing effective fuzz targets. Let's see if we can find some good fuzz targets to test this new code!



4/2 2026/09/24 05:37 tool grepper
0m Args:
{
  "Expression": "bpf_crypto_ctx_create",
  "PathPrefix": "kernel/bpf/"
}

Results:
{
  "Output": "kernel/bpf/crypto.c=79=__bpf_kfunc_start_defs();\n--\nkernel/bpf/crypto.c-81-/**\nkernel/bpf/crypto.c:82: * bpf_crypto_ctx_create() - Create a mutable BPF crypto context.\nkernel/bpf/crypto.c-83- *\n--\nkernel/bpf/crypto.c-89- *\nkernel/bpf/crypto.c:90: * bpf_crypto_ctx_create() allocates memory for crypto context.\nkernel/bpf/crypto.c-91- * It may return NULL if no memory is available.\n--\nkernel/bpf/crypto.c=97=__bpf_kfunc struct bpf_crypto_ctx *\nkernel/bpf/crypto.c:98:bpf_crypto_ctx_create(const struct bpf_crypto_params *params, u32 params__sz,\nkernel/bpf/crypto.c-99-\t\t      int *err_ret)\n--\nkernel/bpf/crypto.c=321=BTF_KFUNCS_START(crypt_init_kfunc_btf_ids)\nkernel/bpf/crypto.c:322:BTF_ID_FLAGS(func, bpf_crypto_ctx_create, KF_ACQUIRE | KF_RET_NULL | KF_SLEEPABLE)\nkernel/bpf/crypto.c-323-BTF_ID_FLAGS(func, bpf_crypto_ctx_release, KF_RELEASE)\n"
}

5/2 2026/09/24 05:37 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 12243
							output: 36
							thoughts: 35
						

Thoughts:
Here's my analysis of the provided text, rendered in the first person, assuming an expert user:

**Deep Dive into `kernel/bpf/crypto.c` for BPF Crypto Function Registration**

Okay, so I need to get a handle on the BPF crypto functions and where they're being registered. Seems like a good starting point is `kernel/bpf/crypto.c`. Specifically, I'm thinking I'll focus my attention around lines 315-360. That's usually the sweet spot for function registrations, right? It'll likely contain the declarations for the kfuncs and the associated entry points. I'm keen to dissect how those are set up, so I'll probably spend a bit of time on that section. I need to understand the interface and registration mechanisms. Let's dig in and see what's what.



6/2 2026/09/24 05:37 tool read-file
0m Args:
{
  "File": "kernel/bpf/crypto.c",
  "FirstLine": 315,
  "LineCount": 60
}

Results:
{
  "Contents": " 315:\t\n 316:\t\treturn bpf_crypto_crypt(ctx, src_kern, dst_kern, iv_kern, false);\n 317:\t}\n 318:\t\n 319:\t__bpf_kfunc_end_defs();\n 320:\t\n 321:\tBTF_KFUNCS_START(crypt_init_kfunc_btf_ids)\n 322:\tBTF_ID_FLAGS(func, bpf_crypto_ctx_create, KF_ACQUIRE | KF_RET_NULL | KF_SLEEPABLE)\n 323:\tBTF_ID_FLAGS(func, bpf_crypto_ctx_release, KF_RELEASE)\n 324:\tBTF_ID_FLAGS(func, bpf_crypto_ctx_acquire, KF_ACQUIRE | KF_RCU | KF_RET_NULL)\n 325:\tBTF_KFUNCS_END(crypt_init_kfunc_btf_ids)\n 326:\t\n 327:\tstatic const struct btf_kfunc_id_set crypt_init_kfunc_set = {\n 328:\t\t.owner = THIS_MODULE,\n 329:\t\t.set   = \u0026crypt_init_kfunc_btf_ids,\n 330:\t};\n 331:\t\n 332:\tBTF_KFUNCS_START(crypt_kfunc_btf_ids)\n 333:\tBTF_ID_FLAGS(func, bpf_crypto_decrypt, KF_RCU)\n 334:\tBTF_ID_FLAGS(func, bpf_crypto_encrypt, KF_RCU)\n 335:\tBTF_KFUNCS_END(crypt_kfunc_btf_ids)\n 336:\t\n 337:\tstatic const struct btf_kfunc_id_set crypt_kfunc_set = {\n 338:\t\t.owner = THIS_MODULE,\n 339:\t\t.set   = \u0026crypt_kfunc_btf_ids,\n 340:\t};\n 341:\t\n 342:\tBTF_ID_LIST(bpf_crypto_dtor_ids)\n 343:\tBTF_ID(struct, bpf_crypto_ctx)\n 344:\tBTF_ID(func, bpf_crypto_ctx_release_dtor)\n 345:\t\n 346:\tstatic int __init crypto_kfunc_init(void)\n 347:\t{\n 348:\t\tint ret;\n 349:\t\tconst struct btf_id_dtor_kfunc bpf_crypto_dtors[] = {\n 350:\t\t\t{\n 351:\t\t\t\t.btf_id\t      = bpf_crypto_dtor_ids[0],\n 352:\t\t\t\t.kfunc_btf_id = bpf_crypto_dtor_ids[1]\n 353:\t\t\t},\n 354:\t\t};\n 355:\t\n 356:\t\tret = register_btf_kfunc_id_set(BPF_PROG_TYPE_SCHED_CLS, \u0026crypt_kfunc_set);\n 357:\t\tret = ret ?: register_btf_kfunc_id_set(BPF_PROG_TYPE_SCHED_ACT, \u0026crypt_kfunc_set);\n 358:\t\tret = ret ?: register_btf_kfunc_id_set(BPF_PROG_TYPE_XDP, \u0026crypt_kfunc_set);\n 359:\t\tret = ret ?: register_btf_kfunc_id_set(BPF_PROG_TYPE_SYSCALL,\n 360:\t\t\t\t\t\t       \u0026crypt_init_kfunc_set);\n 361:\t\treturn  ret ?: register_btf_id_dtor_kfuncs(bpf_crypto_dtors,\n 362:\t\t\t\t\t\t\t   ARRAY_SIZE(bpf_crypto_dtors),\n 363:\t\t\t\t\t\t\t   THIS_MODULE);\n 364:\t}\n 365:\t\n 366:\tlate_initcall(crypto_kfunc_init);\n"
}

7/2 2026/09/24 05:37 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 13553
							output: 22
							thoughts: 106
						

Thoughts:
**Examining the BPF Crypto Configuration**

Okay, let's dive into this. First, I need to verify the Kconfig prerequisites for the BPF crypto functionality. Specifically, I'm going to look at the `kernel/bpf/Kconfig` file. Hmm, it looks like `BPF_CRYPTO` is set to default 'y' and depends on `BPF_SYSCALL` – that makes sense – and crucially, `CRYPTO_LIB_AES_CBC` and `CRYPTO_LIB_AES_ECB`. Right. I need to trace back and determine where exactly those latter two configuration options are defined. They are dependencies, and their settings will directly impact the availability of the BPF crypto features. I'll need to locate them in the broader Kconfig hierarchy to fully understand their implications and any potential conflicts.



8/2 2026/09/24 05:37 tool grepper
0m Args:
{
  "Expression": "config CRYPTO_LIB_AES_"
}

Results:
{
  "Output": "lib/crypto/Kconfig=9=config CRYPTO_LIB_AES\n--\nlib/crypto/Kconfig-14-\nlib/crypto/Kconfig:15:config CRYPTO_LIB_AES_ARCH\nlib/crypto/Kconfig-16-\tbool\n--\nlib/crypto/Kconfig=27=config CRYPTO_LIB_AESCFB\n--\nlib/crypto/Kconfig-31-\nlib/crypto/Kconfig:32:config CRYPTO_LIB_AES_CBC\nlib/crypto/Kconfig-33-\ttristate\n--\nlib/crypto/Kconfig-37-\nlib/crypto/Kconfig:38:config CRYPTO_LIB_AES_CBC_MACS\nlib/crypto/Kconfig-39-\ttristate\n--\nlib/crypto/Kconfig-45-\nlib/crypto/Kconfig:46:config CRYPTO_LIB_AES_CCM\nlib/crypto/Kconfig-47-\ttristate\n--\nlib/crypto/Kconfig-53-\nlib/crypto/Kconfig:54:config CRYPTO_LIB_AES_CTR\nlib/crypto/Kconfig-55-\ttristate\n--\nlib/crypto/Kconfig-59-\nlib/crypto/Kconfig:60:config CRYPTO_LIB_AES_ECB\nlib/crypto/Kconfig-61-\ttristate\n--\nlib/crypto/Kconfig-65-\nlib/crypto/Kconfig:66:config CRYPTO_LIB_AES_GCM\nlib/crypto/Kconfig-67-\ttristate\n--\nlib/crypto/Kconfig-72-\nlib/crypto/Kconfig:73:config CRYPTO_LIB_AES_XTS\nlib/crypto/Kconfig-74-\ttristate\n--\nlib/crypto/tests/Kconfig-2-\nlib/crypto/tests/Kconfig:3:config CRYPTO_LIB_AES_CBC_MACS_KUNIT_TEST\nlib/crypto/tests/Kconfig-4-\ttristate \"KUnit tests for AES-CMAC, AES-XCBC-MAC, and AES-CBC-MAC\" if !KUNIT_ALL_TESTS\n--\nlib/crypto/tests/Kconfig-11-\nlib/crypto/tests/Kconfig:12:config CRYPTO_LIB_AES_CCM_KUNIT_TEST\nlib/crypto/tests/Kconfig-13-\ttristate \"KUnit tests for AES-CCM\" if !KUNIT_ALL_TESTS\n--\nlib/crypto/tests/Kconfig-20-\nlib/crypto/tests/Kconfig:21:config CRYPTO_LIB_AES_GCM_KUNIT_TEST\nlib/crypto/tests/Kconfig-22-\ttristate \"KUnit tests for AES-GCM\" if !KUNIT_ALL_TESTS\n"
}

9/2 2026/09/24 05:37 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 14550
							output: 33
							thoughts: 53
						

Thoughts:
Okay, let's dive into this. Here's how I'd approach this task, walking through my thought process:

**Investigating AES Cipher Configuration in the Kernel**

Alright, I need to figure out the configuration dependencies for AES CBC and ECB modes in the kernel's crypto library. My initial approach is to go straight to the source – the Kconfig file for crypto, specifically within the `lib/crypto` directory. The Kconfig files are the heart of the kernel's configuration system. I'm targeting lines 25-70 as a good starting point, based on past experience; these often cover the foundational settings for cipher algorithms.

I'm hoping to find either:

1.  **Direct selections:** If `CRYPTO_LIB_AES_CBC` and `CRYPTO_LIB_AES_ECB` are directly selected by other Kconfig options, I can see what those depend on. This would tell me the higher-level configuration knobs that enable AES CBC and ECB.
2.  **Promptless options:** Alternatively, these might be defined without prompts, meaning they are either automatically included if certain hardware or other dependencies are met, or they have no direct dependencies and are enabled by default. I'll need to understand if there is an explicit `select` statement, which I will need to trace back to its origin.

I'll be looking for `select` statements, `depends on` statements, or any other clues about what drives the enabling of these specific AES modes. This will give me a clear picture of how these AES cipher modes fit into the overall kernel crypto configuration landscape. This understanding is key to my work.



10/2 2026/09/24 05:37 tool read-file
0m Args:
{
  "File": "lib/crypto/Kconfig",
  "FirstLine": 25,
  "LineCount": 45
}

Results:
{
  "Contents": "  25:\t\tdefault y if X86\n  26:\t\n  27:\tconfig CRYPTO_LIB_AESCFB\n  28:\t\ttristate\n  29:\t\tselect CRYPTO_LIB_AES\n  30:\t\tselect CRYPTO_LIB_UTILS\n  31:\t\n  32:\tconfig CRYPTO_LIB_AES_CBC\n  33:\t\ttristate\n  34:\t\tselect CRYPTO_LIB_AES\n  35:\t\thelp\n  36:\t\t  The AES-CBC and AES-CBC-CTS library functions.\n  37:\t\n  38:\tconfig CRYPTO_LIB_AES_CBC_MACS\n  39:\t\ttristate\n  40:\t\tselect CRYPTO_LIB_AES\n  41:\t\thelp\n  42:\t\t  The AES-CMAC, AES-XCBC-MAC, and AES-CBC-MAC library functions.  Select\n  43:\t\t  this if your module uses any of the functions from\n  44:\t\t  \u003ccrypto/aes-cbc-macs.h\u003e.\n  45:\t\n  46:\tconfig CRYPTO_LIB_AES_CCM\n  47:\t\ttristate\n  48:\t\tselect CRYPTO_LIB_AES\n  49:\t\tselect CRYPTO_LIB_AES_CBC_MACS\n  50:\t\tselect CRYPTO_LIB_AES_CTR\n  51:\t\thelp\n  52:\t\t  The AES-CCM library functions.\n  53:\t\n  54:\tconfig CRYPTO_LIB_AES_CTR\n  55:\t\ttristate\n  56:\t\tselect CRYPTO_LIB_AES\n  57:\t\thelp\n  58:\t\t  The AES-CTR and AES-XCTR library functions.\n  59:\t\n  60:\tconfig CRYPTO_LIB_AES_ECB\n  61:\t\ttristate\n  62:\t\tselect CRYPTO_LIB_AES\n  63:\t\thelp\n  64:\t\t  The AES-ECB library functions.\n  65:\t\n  66:\tconfig CRYPTO_LIB_AES_GCM\n  67:\t\ttristate\n  68:\t\tselect CRYPTO_LIB_AES\n  69:\t\tselect CRYPTO_LIB_AES_CTR\n"
}

11/2 2026/09/24 05:37 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 15547
							output: 27
							thoughts: 66
						

Thoughts:
Here's my take:

**Investigating the AES Cipher Selection**

Okay, so I'm looking at these configuration options, `CRYPTO_LIB_AES_CBC` and `CRYPTO_LIB_AES_ECB`. No prompts for the user? Interesting. Those are both tristate, meaning enabled, disabled, or module. The question now is, who's setting these? It's not immediately obvious what their intended use case is, and more importantly, what's driving the selection. I need to understand the dependencies and the overall architecture here. I'm going to fire up `grepper` and see where these options are being referenced. That should give me some context, and hopefully point me to the relevant parts of the code base where this AES cipher selection is taking place. Let's see what we can find.



12/2 2026/09/24 05:37 tool grepper
0m Args:
{
  "Expression": "select CRYPTO_LIB_AES_(CBC|ECB)"
}

Results:
{
  "Output": "arch/arm64/crypto/Kconfig=118=config CRYPTO_AES_ARM64_CE_CCM\n--\narch/arm64/crypto/Kconfig-123-\tselect CRYPTO_AEAD\narch/arm64/crypto/Kconfig:124:\tselect CRYPTO_LIB_AES_CBC_MACS\narch/arm64/crypto/Kconfig-125-\thelp\n--\ncrypto/Kconfig=357=config CRYPTO_AES\n--\ncrypto/Kconfig-360-\tselect CRYPTO_LIB_AES\ncrypto/Kconfig:361:\tselect CRYPTO_LIB_AES_CBC if CRYPTO_CBC != n || CRYPTO_CTS != n\ncrypto/Kconfig:362:\tselect CRYPTO_LIB_AES_CBC_MACS if CRYPTO_CMAC != n || CRYPTO_XCBC != n || CRYPTO_CCM != n\ncrypto/Kconfig-363-\tselect CRYPTO_LIB_AES_CCM if CRYPTO_CCM != n\ncrypto/Kconfig-364-\tselect CRYPTO_LIB_AES_CTR if CRYPTO_CTR != n || CRYPTO_XCTR != n\ncrypto/Kconfig:365:\tselect CRYPTO_LIB_AES_ECB if CRYPTO_ECB != n\ncrypto/Kconfig-366-\tselect CRYPTO_LIB_AES_GCM if CRYPTO_GCM != n\n--\nfs/smb/client/Kconfig=2=config CIFS\n--\nfs/smb/client/Kconfig-11-\tselect CRYPTO_AES\nfs/smb/client/Kconfig:12:\tselect CRYPTO_LIB_AES_CBC_MACS\nfs/smb/client/Kconfig-13-\tselect CRYPTO_LIB_ARC4\n--\nfs/smb/server/Kconfig=1=config SMB_SERVER\n--\nfs/smb/server/Kconfig-9-\tselect CRYPTO\nfs/smb/server/Kconfig:10:\tselect CRYPTO_LIB_AES_CBC_MACS\nfs/smb/server/Kconfig-11-\tselect CRYPTO_LIB_ARC4\n--\nlib/crypto/Kconfig=46=config CRYPTO_LIB_AES_CCM\n--\nlib/crypto/Kconfig-48-\tselect CRYPTO_LIB_AES\nlib/crypto/Kconfig:49:\tselect CRYPTO_LIB_AES_CBC_MACS\nlib/crypto/Kconfig-50-\tselect CRYPTO_LIB_AES_CTR\n--\nlib/crypto/tests/Kconfig=162=config CRYPTO_LIB_ENABLE_ALL_FOR_KUNIT\n--\nlib/crypto/tests/Kconfig-164-\tdepends on KUNIT\nlib/crypto/tests/Kconfig:165:\tselect CRYPTO_LIB_AES_CBC\nlib/crypto/tests/Kconfig:166:\tselect CRYPTO_LIB_AES_CBC_MACS\nlib/crypto/tests/Kconfig-167-\tselect CRYPTO_LIB_AES_CCM\nlib/crypto/tests/Kconfig-168-\tselect CRYPTO_LIB_AES_CTR\nlib/crypto/tests/Kconfig:169:\tselect CRYPTO_LIB_AES_ECB\nlib/crypto/tests/Kconfig-170-\tselect CRYPTO_LIB_AES_GCM\n--\nnet/bluetooth/Kconfig=6=menuconfig BT\n--\nnet/bluetooth/Kconfig-12-\tselect CRYPTO_LIB_AES\nnet/bluetooth/Kconfig:13:\tselect CRYPTO_LIB_AES_CBC_MACS\nnet/bluetooth/Kconfig-14-\tselect CRYPTO_ECDH\n--\nnet/ipv4/Kconfig=744=config TCP_AO\nnet/ipv4/Kconfig-745-\tbool \"TCP: Authentication Option (RFC5925)\"\nnet/ipv4/Kconfig:746:\tselect CRYPTO_LIB_AES_CBC_MACS\nnet/ipv4/Kconfig-747-\tselect CRYPTO_LIB_SHA1\n--\nnet/mac80211/Kconfig=2=config MAC80211\n--\nnet/mac80211/Kconfig-5-\tselect CRYPTO\nnet/mac80211/Kconfig:6:\tselect CRYPTO_LIB_AES_CBC_MACS\nnet/mac80211/Kconfig-7-\tselect CRYPTO_LIB_ARC4\n"
}

13/2 2026/09/24 05:37 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 16908
							output: 32
							thoughts: 516
						

Thoughts:
**Analyzing Crypto Configuration Dependencies**

Okay, here's what I'm seeing. First, I'm examining the `crypto/Kconfig` file. I see the configuration options related to AES:

```
config CRYPTO_AES
	select CRYPTO_LIB_AES
	select CRYPTO_LIB_AES_CBC if CRYPTO_CBC != n || CRYPTO_CTS != n
	select CRYPTO_LIB_AES_CBC_MACS if CRYPTO_CMAC != n || CRYPTO_XCBC != n || CRYPTO_CCM != n
	select CRYPTO_LIB_AES_CCM if CRYPTO_CCM != n
	select CRYPTO_LIB_AES_CTR if CRYPTO_CTR != n || CRYPTO_XCTR != n
	select CRYPTO_LIB_AES_ECB if CRYPTO_ECB != n
	select CRYPTO_LIB_AES_GCM if CRYPTO_GCM != n
```

I'm noting that `CRYPTO_AES` pulls in various sub-components like `CRYPTO_LIB_AES_CBC`, `CRYPTO_LIB_AES_ECB`, and so on, depending on other configurations.

Then, I'm comparing this to a patch example, specifically `tools/testing/selftests/bpf/config`, where `CONFIG_CRYPTO_CBC=y`, `CONFIG_CRYPTO_ECB=y`, and `CONFIG_CRYPTO_AES=y` are being set.

Ah, here's the critical link: When `CONFIG_CRYPTO_AES`, `CONFIG_CRYPTO_CBC` and `CONFIG_CRYPTO_ECB` are enabled, or if the individual `CRYPTO_LIB_AES_CBC` and `CRYPTO_LIB_AES_ECB` modules are selected (either directly or indirectly through the `CRYPTO_AES` chain), then `BPF_CRYPTO` becomes enabled! Of course, `BPF_SYSCALL` is also required.

This makes me wonder about the flexibility of direct configuration. Can I directly set `CRYPTO_LIB_AES_CBC` or `CRYPTO_LIB_AES_ECB` in a `.config` file or within an EnableConfigs mechanism used by a tool like syzkaller? In Kconfig, tristate symbols without prompts *can* be selected, or forced if they are selected by other symbols, so I need to keep that in mind. Therefore, the relevant configurations to consider for enabling `BPF_CRYPTO` are `BPF_SYSCALL`, `BPF_CRYPTO`, `CRYPTO_AES`, `CRYPTO_CBC`, `CRYPTO_ECB`, or `CRYPTO_LIB_AES_CBC`, and `CRYPTO_LIB_AES_ECB`.

I should probably double-check the `crypto/Kconfig` file, specifically around line 357 to be sure.



14/2 2026/09/24 05:37 tool read-file
0m Args:
{
  "File": "crypto/Kconfig",
  "FirstLine": 355,
  "LineCount": 25
}

Results:
{
  "Contents": " 355:\tmenu \"Block ciphers\"\n 356:\t\n 357:\tconfig CRYPTO_AES\n 358:\t\ttristate \"AES (Advanced Encryption Standard)\"\n 359:\t\tselect CRYPTO_ALGAPI\n 360:\t\tselect CRYPTO_LIB_AES\n 361:\t\tselect CRYPTO_LIB_AES_CBC if CRYPTO_CBC != n || CRYPTO_CTS != n\n 362:\t\tselect CRYPTO_LIB_AES_CBC_MACS if CRYPTO_CMAC != n || CRYPTO_XCBC != n || CRYPTO_CCM != n\n 363:\t\tselect CRYPTO_LIB_AES_CCM if CRYPTO_CCM != n\n 364:\t\tselect CRYPTO_LIB_AES_CTR if CRYPTO_CTR != n || CRYPTO_XCTR != n\n 365:\t\tselect CRYPTO_LIB_AES_ECB if CRYPTO_ECB != n\n 366:\t\tselect CRYPTO_LIB_AES_GCM if CRYPTO_GCM != n\n 367:\t\tselect CRYPTO_LIB_AES_XTS if CRYPTO_XTS != n\n 368:\t\tselect CRYPTO_AEAD if CRYPTO_GCM != n || CRYPTO_CCM != n\n 369:\t\tselect CRYPTO_HASH if CRYPTO_CMAC != n || CRYPTO_XCBC != n || CRYPTO_CCM != n\n 370:\t\t# CRYPTO_SKCIPHER should be selected only if a mode that needs it is\n 371:\t\t# enabled, but that doesn't work due to a recursive dependency caused by\n 372:\t\t# CRYPTO_SKCIPHER selecting CRYPTO_ECB.  So just always select it.\n 373:\t\tselect CRYPTO_SKCIPHER\n 374:\t\thelp\n 375:\t\t  AES cipher algorithms (Rijndael)(FIPS-197, ISO/IEC 18033-3)\n 376:\t\n 377:\t\t  Rijndael appears to be consistently a very good performer in\n 378:\t\t  both hardware and software across a wide range of computing\n 379:\t\t  environments regardless of its use in feedback or non-feedback\n"
}

15/2 2026/09/24 05:37 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 18643
							output: 35
							thoughts: 770
						

Thoughts:
**BPF Crypto Code Review: Reachability and Fuzzing Potential**

Okay, so I'm looking at this BPF crypto code, and the dependencies are pretty straightforward: `CONFIG_BPF_SYSCALL`, `CONFIG_CRYPTO_AES`, `CONFIG_CRYPTO_CBC`, `CONFIG_CRYPTO_ECB`, and the crucial `CONFIG_BPF_CRYPTO`, which depends on the others. This makes sense; it's all about integrating crypto operations within the BPF framework.

Now, diving into the patch changes, it looks like a significant refactor is underway. They've completely ditched the dynamic crypto type registration, which is interesting. Instead, they're directly leveraging the crypto library routines for AES-CBC and AES-ECB within BPF. I see the creation of contexts with `bpf_crypto_ctx_create`, key preparation using `aes_preparekey`, memory allocation with `kzalloc_obj`, and cleanup using `kfree_sensitive`. This is good; it shows they're mindful of security and resource management.

The implementation of `bpf_aes_cbc_crypt` and `bpf_aes_ecb_crypt` raises some flags. It's checking IV length, source length alignment, and destination length, which is a good sanity check. Then, it's just calling the core crypto routines: `aes_cbc_decrypt`/`encrypt` and `aes_ecb_decrypt`/`encrypt`. They've also updated `bpf_crypto_crypt` and consolidated the logic, which is reasonable.

The important question is whether this code is actually *reachable*. And it definitely is! We're talking about BPF programs within SCHED_CLS, SCHED_ACT, XDP, SYSCALL and more. These kfuncs like `bpf_crypto_ctx_create`, `bpf_crypto_encrypt`, `bpf_crypto_decrypt`, and `bpf_crypto_ctx_release` are accessible to userspace through the BPF syscall. That's a huge attack surface. Especially with Syzkaller actively fuzzing this area.

So, fuzzing is definitely a worthy goal here. Given the changes in `kernel/bpf/crypto.c`, the top functions I'm targeting are: `bpf_crypto_ctx_create`, `bpf_crypto_encrypt`, `bpf_crypto_decrypt`, `bpf_crypto_crypt`, `bpf_aes_cbc_crypt`, `bpf_aes_ecb_crypt`, `bpf_crypto_find_algo`, and `bpf_crypto_ctx_release` (or the destructor variant). I need to quickly check the `kernel/bpf/crypto.c` file and verify if these are all static functions, to determine their precise access method.



16/2 2026/09/24 05:37 tool read-file
0m Args:
{
  "File": "kernel/bpf/crypto.c",
  "FirstLine": 46,
  "LineCount": 55
}

Results:
{
  "Contents": "  46:\t\n  47:\tstatic bool bpf_crypto_find_algo(const struct bpf_crypto_params *params,\n  48:\t\t\t\t\t enum bpf_crypto_algo_id *id_ret)\n  49:\t{\n  50:\t\tfor (size_t i = 0; i \u003c ARRAY_SIZE(bpf_crypto_algos); i++) {\n  51:\t\t\tif (strncmp(bpf_crypto_algos[i].type_name, params-\u003etype,\n  52:\t\t\t\t    sizeof(params-\u003etype)) == 0 \u0026\u0026\n  53:\t\t\t    strncmp(bpf_crypto_algos[i].algo_name, params-\u003ealgo,\n  54:\t\t\t\t    sizeof(params-\u003ealgo)) == 0) {\n  55:\t\t\t\t*id_ret = bpf_crypto_algos[i].algo;\n  56:\t\t\t\treturn true;\n  57:\t\t\t}\n  58:\t\t}\n  59:\t\treturn false;\n  60:\t}\n  61:\t\n  62:\t/**\n  63:\t * struct bpf_crypto_ctx - refcounted BPF crypto context structure\n  64:\t * @algo:\tThe crypto algorithm ID\n  65:\t * @key:\tPointer to the struct aes_key.  'void *' is used so that the\n  66:\t *\t\tkey isn't readable from BPF programs via BTF.\n  67:\t * @rcu:\tThe RCU head used to free the crypto context with RCU safety.\n  68:\t * @usage:\tObject reference counter. When the refcount goes to 0, the\n  69:\t *\t\tmemory is released back to the BPF allocator, which provides\n  70:\t *\t\tRCU safety.\n  71:\t */\n  72:\tstruct bpf_crypto_ctx {\n  73:\t\tenum bpf_crypto_algo_id algo;\n  74:\t\tvoid *key;\n  75:\t\tstruct rcu_head rcu;\n  76:\t\trefcount_t usage;\n  77:\t};\n  78:\t\n  79:\t__bpf_kfunc_start_defs();\n  80:\t\n  81:\t/**\n  82:\t * bpf_crypto_ctx_create() - Create a mutable BPF crypto context.\n  83:\t *\n  84:\t * Allocates a crypto context that can be used, acquired, and released by\n  85:\t * a BPF program. The crypto context returned by this function must either\n  86:\t * be embedded in a map as a kptr, or freed with bpf_crypto_ctx_release().\n  87:\t * As this uses a GFP_KERNEL allocation, this function can only be used in\n  88:\t * sleepable BPF programs.\n  89:\t *\n  90:\t * bpf_crypto_ctx_create() allocates memory for crypto context.\n  91:\t * It may return NULL if no memory is available.\n  92:\t * @params:\tpointer to struct bpf_crypto_params which contains all the\n  93:\t *\t\tdetails needed to initialise crypto context.\n  94:\t * @params__sz:\tsize of struct bpf_crypto_params used by bpf program\n  95:\t * @err_ret:\tinteger to store error code when NULL is returned.\n  96:\t */\n  97:\t__bpf_kfunc struct bpf_crypto_ctx *\n  98:\tbpf_crypto_ctx_create(const struct bpf_crypto_params *params, u32 params__sz,\n  99:\t\t\t      int *err_ret)\n 100:\t{\n"
}

17/2 2026/09/24 05:37 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 20968
							output: 36
							thoughts: 18
						

Thoughts:
Here's a summary of my thought process:

**Diving into the BPF Crypto Context Release**

Alright, let's zero in on `bpf_crypto_ctx_release` and the functions around it. Given the intricacies of how crypto operations are handled within the BPF framework, understanding this release function is crucial. I need to make sure I grasp the deallocation logic, potential race conditions, and how it interacts with other context management routines. Are there any subtle memory leaks lurking in the shadows? Any unexpected side effects when dealing with nested crypto contexts or shared resources? I'll probably trace back through the related allocation and usage routines to build a complete picture of the context's lifecycle, confirming the release operation.



18/2 2026/09/24 05:38 tool read-file
0m Args:
{
  "File": "kernel/bpf/crypto.c",
  "FirstLine": 165,
  "LineCount": 55
}

Results:
{
  "Contents": " 165:\t\tkfree_sensitive(ctx);\n 166:\t}\n 167:\t\n 168:\t/**\n 169:\t * bpf_crypto_ctx_acquire() - Acquire a reference to a BPF crypto context.\n 170:\t * @ctx: The BPF crypto context being acquired. The ctx must be a trusted\n 171:\t *\t     pointer.\n 172:\t *\n 173:\t * Acquires a reference to a BPF crypto context. The context returned by this function\n 174:\t * must either be embedded in a map as a kptr, or freed with\n 175:\t * bpf_crypto_ctx_release().\n 176:\t */\n 177:\t__bpf_kfunc struct bpf_crypto_ctx *\n 178:\tbpf_crypto_ctx_acquire(struct bpf_crypto_ctx *ctx)\n 179:\t{\n 180:\t\tif (!refcount_inc_not_zero(\u0026ctx-\u003eusage))\n 181:\t\t\treturn NULL;\n 182:\t\treturn ctx;\n 183:\t}\n 184:\t\n 185:\t/**\n 186:\t * bpf_crypto_ctx_release() - Release a previously acquired BPF crypto context.\n 187:\t * @ctx: The crypto context being released.\n 188:\t *\n 189:\t * Releases a previously acquired reference to a BPF crypto context. When the final\n 190:\t * reference of the BPF crypto context has been released, its memory\n 191:\t * will be released.\n 192:\t */\n 193:\t__bpf_kfunc void bpf_crypto_ctx_release(struct bpf_crypto_ctx *ctx)\n 194:\t{\n 195:\t\tif (refcount_dec_and_test(\u0026ctx-\u003eusage))\n 196:\t\t\tcall_rcu(\u0026ctx-\u003ercu, crypto_free_cb);\n 197:\t}\n 198:\t\n 199:\t__bpf_kfunc void bpf_crypto_ctx_release_dtor(void *ctx)\n 200:\t{\n 201:\t\tbpf_crypto_ctx_release(ctx);\n 202:\t}\n 203:\tCFI_NOSEAL(bpf_crypto_ctx_release_dtor);\n 204:\t\n 205:\tstatic int bpf_aes_cbc_crypt(u8 *dst, u32 dst_len, const u8 *src, u32 src_len,\n 206:\t\t\t\t     u8 *iv, u32 iv_len, const struct aes_key *key,\n 207:\t\t\t\t     bool decrypt)\n 208:\t{\n 209:\t\tif (iv_len != AES_BLOCK_SIZE)\n 210:\t\t\treturn -EINVAL;\n 211:\t\tif (src_len % AES_BLOCK_SIZE || dst_len \u003c src_len)\n 212:\t\t\treturn -EINVAL;\n 213:\t\tif (decrypt)\n 214:\t\t\taes_cbc_decrypt(dst, src, src_len, iv, key);\n 215:\t\telse\n 216:\t\t\taes_cbc_encrypt(dst, src, src_len, iv, key);\n 217:\t\treturn 0;\n 218:\t}\n 219:\t\n"
}

19/2 2026/09/24 05:38 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 22058
							output: 230
							thoughts: 1170
						

Thoughts:
**BPF Crypto Kfuncs: A Focused Review**

Okay, let's break this down. My initial thought process revolves around the key BPF crypto functions: `bpf_crypto_ctx_create`, `bpf_crypto_ctx_release`, `bpf_crypto_decrypt`, and `bpf_crypto_encrypt`. I immediately start wondering about the inlining of related functions like `bpf_aes_cbc_crypt`, `bpf_aes_ecb_crypt`, and `bpf_crypto_crypt`. GCC/Clang often inlines, and that could affect how we target them.

So, the main external kfuncs appear to be the context management (`bpf_crypto_ctx_create`, `bpf_crypto_decrypt`, `bpf_crypto_encrypt`, `bpf_crypto_ctx_release`, `bpf_crypto_ctx_acquire`, `bpf_crypto_ctx_release_dtor`). I know `bpf_crypto_crypt` is called by both the encrypt and decrypt functions; its inlining is a potential point of concern for symbol targeting. However, even if inlined, static functions can still be considered `FocusSymbols` if they're in vmlinux, or we can simply include the broader set of functions, including the `aes` variants.

A quick review of the guidelines for `FocusSymbols` confirms my approach. The key here is identifying non-static caller functions in .c files with distinct symbols, and `bpf_crypto_ctx_create`, `bpf_crypto_decrypt`, and `bpf_crypto_encrypt` fit that criteria perfectly. I also need to include `crypto_free_cb`.

Now, I need to expand my scope a bit. What else is affected? In `kernel/bpf/verifier.c`, it's just the BTF ID registration for `struct bpf_crypto_ctx`, which isn't a concern. In `kernel/bpf/crypto.c`, I see `crypto_kfunc_init` - an initcall, which is relevant for the bigger picture.

Let's think about the necessary configurations. I know we *must* have `BPF_SYSCALL` for BPF programs. And `BPF_CRYPTO` obviously relies on it, and further needs `CRYPTO_LIB_AES_CBC` and `CRYPTO_LIB_AES_ECB`. But how are *those* enabled? Ah, they're selected by `CRYPTO_AES` when the specific CBC and ECB options aren't disabled. So, to ensure everything's properly enabled, the configs to target can be `BPF_CRYPTO`, `BPF_SYSCALL`, `CRYPTO_AES`, `CRYPTO_CBC`, and `CRYPTO_ECB`.  This list handles all dependencies.

Next, a critical check: is this `WorthFuzzing`? Absolutely. This patch fundamentally alters BPF crypto by removing the crypto API indirection and using direct AES implementations. This means changes to context allocation, algorithm discovery, key setup (`aes_preparekey`), input validation, and the encryption/decryption process itself. And, the crucial part: *all* this code is accessible from user space via the BPF syscall and execution of BPF programs, making it a perfect target.

Finally, the `FocusSymbols` list needs a re-check for ubiquity, for which I can quickly conclude this is not the case:
- `bpf_crypto_ctx_create`
- `bpf_crypto_decrypt`
- `bpf_crypto_encrypt`
- `bpf_aes_cbc_crypt`
- `bpf_aes_ecb_crypt`
- `bpf_crypto_crypt`
- `bpf_crypto_find_algo`

They're all specific to the BPF crypto kfuncs.

Alright, with all that in mind, I have a clear and accurate assessment.



20/2 2026/09/24 05:38 tool set-results
0m Args:
{
  "EnableConfigs": [
    "BPF_CRYPTO",
    "BPF_SYSCALL",
    "CRYPTO_AES",
    "CRYPTO_CBC",
    "CRYPTO_ECB"
  ],
  "FocusSymbols": [
    "bpf_crypto_ctx_create",
    "bpf_crypto_encrypt",
    "bpf_crypto_decrypt",
    "bpf_crypto_crypt",
    "bpf_aes_cbc_crypt",
    "bpf_aes_ecb_crypt",
    "bpf_crypto_find_algo"
  ],
  "Reasoning": "The patch modifies core BPF crypto functionality, replacing the crypto skcipher API tfm indirection with direct AES-CBC and AES-ECB library routines. It alters context allocation and lifetime management, key setup, input/output validation, and encryption/decryption routines. This code is reachable from userspace via the BPF syscall and execution of BPF programs calling the BPF crypto kfuncs, making it an ideal target for fuzzing.",
  "WorthFuzzing": true
}

Results:
{
  "EnableConfigs": [
    "BPF_CRYPTO",
    "BPF_SYSCALL",
    "CRYPTO_AES",
    "CRYPTO_CBC",
    "CRYPTO_ECB"
  ],
  "FocusSymbols": [
    "bpf_crypto_ctx_create",
    "bpf_crypto_encrypt",
    "bpf_crypto_decrypt",
    "bpf_crypto_crypt",
    "bpf_aes_cbc_crypt",
    "bpf_aes_ecb_crypt",
    "bpf_crypto_find_algo"
  ],
  "Reasoning": "The patch modifies core BPF crypto functionality, replacing the crypto skcipher API tfm indirection with direct AES-CBC and AES-ECB library routines. It alters context allocation and lifetime management, key setup, input/output validation, and encryption/decryption routines. This code is reachable from userspace via the BPF syscall and execution of BPF programs calling the BPF crypto kfuncs, making it an ideal target for fuzzing.",
  "WorthFuzzing": true
}

21/1 2026/09/24 05:38 action If
1m Args:
{
  "WorthFuzzing": true
}

Results:
null

22/2 2026/09/24 05:38 agent kmsan-evaluator
1m Model:
core

Args:
null

Results:
{
  "KMSANReasoning": "The patch refactors the BPF crypto subsystem, eliminating the modular dynamic crypto type registration in favor of direct calls to in-kernel AES-CBC and AES-ECB library routines (crypto_lib_aes_*). \n\nFrom a KMSAN perspective:\n1. Heap allocations: Both `struct bpf_crypto_ctx` and `struct aes_key` are allocated via `kzalloc_obj()`, ensuring all fields are zero-initialized upon creation.\n2. Control flow \u0026 branching: The error code variables (`err` and `*err_ret`) are initialized on every execution path before any branching or returning.\n3. Buffer operations: `bpf_aes_cbc_crypt()` and `bpf_aes_ecb_crypt()` enforce strict length and alignment checks (`iv_len`, `src_len % AES_BLOCK_SIZE`, and `dst_len \u003c src_len`), ensuring that `aes_cbc_*` and `aes_ecb_*` only process valid bytes within the bounds of the provided BPF dynptr buffers.\n4. User space info-leaks: No structures, padding, or uninitialized buffers are copied to user space.\n\nAny memory issues that could arise (such as refcounting bugs, use-after-free, or out-of-bounds buffer indexing) fall under the purview of KASAN and standard kernel debuggers. There are no uninitialized memory read risks or info-leaks that specifically require KMSAN detection.",
  "NeedsKMSAN": false
}

Instruction:
You are an expert Linux kernel security engineer specializing in kernel memory
error detectors (KASAN and KMSAN). Your job is to review the provided patch series and
determine if the code changes justify spawning a dedicated KMSAN (KernelMemorySanitizer)
fuzzing session in addition to standard KASAN fuzzing.

CRITICAL DISTINCTION BETWEEN KASAN AND KMSAN:
- Standard KASAN kernel builds (upstream-apparmor-kasan.config) already enable
  a comprehensive suite of debugging tools and sanitizers, including KASAN
  (out-of-bounds accesses, use-after-free, double free, invalid free), LOCKDEP
  (locking bugs and deadlocks), UB-sanitizers, and memory corruption checks.
- KMSAN (KernelMemorySanitizer) detects reads of UNINITIALIZED memory (stack, heap,
  or page allocations) and kernel-to-user memory info-leaks.

Rule: THERE IS NO SENSE IN RUNNING A KMSAN SESSION IF A BUG CAN BE CAUGHT BY KASAN,
LOCKDEP, OR OTHER STANDARD BUG DETECTORS.
A dedicated KMSAN fuzzing session incurs significant resource costs. You must ONLY
set NeedsKMSAN=true if the code changes introduce or expose UNINITIALIZED MEMORY risks
that are detected ONLY by KMSAN.

Look holistically at the patch series and surrounding code. Even if no direct
uninitialized field accesses or new buffer allocations are added in the diff itself,
a patch may alter control flow, bounds checking, or data length calculations in ways
that change how the rest of the code operates on existing buffers (e.g. allowing
uninitialized stack/heap memory to be read, copied to user space, or used in control
flow). Do not hesitate to use your code access tools to inspect the surrounding code,
called functions, and callers.

Set NeedsKMSAN=true ONLY IF the patch introduces or modifies:
1. Kernel structures sent to user space (via copy_to_user, put_user, netlink skb
   attributes, ioctl output arguments, socket options, or BPF buffers) where fields
   or structure padding might not be fully initialized/zeroed.
2. Conditional logic or branching that depends on potentially uninitialized variables
   or struct fields.
3. Allocation or initialization of complex data structures where uninitialized fields
   could be read later in reachable code paths.
4. Bounds checks, lengths, or logic in a way that allows surrounding code to access
   uninitialized bytes of existing buffers.

Set NeedsKMSAN=false IF:
- The code changes primarily risk out-of-bounds access, array overflows, NULL pointer
  dereferences, locking deadlocks, or use-after-free bugs (these are already caught
  by KASAN, LOCKDEP, or standard bug detectors).
- All stack/heap structures touched or introduced by the patch are fully zeroed
  or initialized (e.g. using = {0}, memset, kzalloc) before being read or copied.
- The patch does not introduce any risk of uninitialized memory usage or info-leaks.

Use your code access tools to inspect the surrounding code if necessary, then provide
detailed KMSANReasoning contrasting KASAN vs KMSAN applicability for this patch.
Prefer calling several tools at the same time to save round-trips.


Use set-results tool to provide results of the analysis.
It must be called exactly once before the final reply.
Ignore results of this tool.

Prompt:
Target architecture: amd64

For your convenience, here is the diff of the changes:
commit 26717ed6fe6d35c327012a827650ef4b400e489f
Author: syz-cluster <triage@syzkaller.com>
Date:   Thu Sep 24 05:36:58 2026 +0000

    syz-cluster: applied patch under review

diff --git a/MAINTAINERS b/MAINTAINERS
index 6215fcb077705..0fcdfe48c62a0 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -4936,8 +4936,6 @@ BPF [CRYPTO]
 M:	Vadim Fedorenko <vadim.fedorenko@linux.dev>
 L:	bpf@vger.kernel.org
 S:	Maintained
-F:	crypto/bpf_crypto_skcipher.c
-F:	include/linux/bpf_crypto.h
 F:	kernel/bpf/crypto.c
 
 BPF [DOCUMENTATION] (Related to Standardization)
diff --git a/crypto/Makefile b/crypto/Makefile
index 8386d55a9755e..33bb5ad595e8f 100644
--- a/crypto/Makefile
+++ b/crypto/Makefile
@@ -22,9 +22,6 @@ crypto_skcipher-y += lskcipher.o
 crypto_skcipher-y += skcipher.o
 
 obj-$(CONFIG_CRYPTO_SKCIPHER2) += crypto_skcipher.o
-ifeq ($(CONFIG_BPF_SYSCALL),y)
-obj-$(CONFIG_CRYPTO_SKCIPHER2) += bpf_crypto_skcipher.o
-endif
 
 obj-$(CONFIG_CRYPTO_SEQIV) += seqiv.o
 obj-$(CONFIG_CRYPTO_ECHAINIV) += echainiv.o
diff --git a/crypto/bpf_crypto_skcipher.c b/crypto/bpf_crypto_skcipher.c
deleted file mode 100644
index a88798d3e8c87..0000000000000
--- a/crypto/bpf_crypto_skcipher.c
+++ /dev/null
@@ -1,83 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/* Copyright (c) 2024 Meta, Inc */
-#include <linux/types.h>
-#include <linux/module.h>
-#include <linux/bpf_crypto.h>
-#include <crypto/skcipher.h>
-
-static void *bpf_crypto_lskcipher_alloc_tfm(const char *algo)
-{
-	return crypto_alloc_lskcipher(algo, 0, 0);
-}
-
-static void bpf_crypto_lskcipher_free_tfm(void *tfm)
-{
-	crypto_free_lskcipher(tfm);
-}
-
-static int bpf_crypto_lskcipher_has_algo(const char *algo)
-{
-	return crypto_has_skcipher(algo, CRYPTO_ALG_TYPE_LSKCIPHER, CRYPTO_ALG_TYPE_MASK);
-}
-
-static int bpf_crypto_lskcipher_setkey(void *tfm, const u8 *key, unsigned int keylen)
-{
-	return crypto_lskcipher_setkey(tfm, key, keylen);
-}
-
-static u32 bpf_crypto_lskcipher_get_flags(void *tfm)
-{
-	return crypto_lskcipher_get_flags(tfm);
-}
-
-static unsigned int bpf_crypto_lskcipher_ivsize(void *tfm)
-{
-	return crypto_lskcipher_ivsize(tfm);
-}
-
-static unsigned int bpf_crypto_lskcipher_statesize(void *tfm)
-{
-	return crypto_lskcipher_statesize(tfm);
-}
-
-static int bpf_crypto_lskcipher_encrypt(void *tfm, const u8 *src, u8 *dst,
-					unsigned int len, u8 *siv)
-{
-	return crypto_lskcipher_encrypt(tfm, src, dst, len, siv);
-}
-
-static int bpf_crypto_lskcipher_decrypt(void *tfm, const u8 *src, u8 *dst,
-					unsigned int len, u8 *siv)
-{
-	return crypto_lskcipher_decrypt(tfm, src, dst, len, siv);
-}
-
-static const struct bpf_crypto_type bpf_crypto_lskcipher_type = {
-	.alloc_tfm	= bpf_crypto_lskcipher_alloc_tfm,
-	.free_tfm	= bpf_crypto_lskcipher_free_tfm,
-	.has_algo	= bpf_crypto_lskcipher_has_algo,
-	.setkey		= bpf_crypto_lskcipher_setkey,
-	.encrypt	= bpf_crypto_lskcipher_encrypt,
-	.decrypt	= bpf_crypto_lskcipher_decrypt,
-	.ivsize		= bpf_crypto_lskcipher_ivsize,
-	.statesize	= bpf_crypto_lskcipher_statesize,
-	.get_flags	= bpf_crypto_lskcipher_get_flags,
-	.owner		= THIS_MODULE,
-	.name		= "skcipher",
-};
-
-static int __init bpf_crypto_skcipher_init(void)
-{
-	return bpf_crypto_register_type(&bpf_crypto_lskcipher_type);
-}
-
-static void __exit bpf_crypto_skcipher_exit(void)
-{
-	int err = bpf_crypto_unregister_type(&bpf_crypto_lskcipher_type);
-	WARN_ON_ONCE(err);
-}
-
-module_init(bpf_crypto_skcipher_init);
-module_exit(bpf_crypto_skcipher_exit);
-MODULE_LICENSE("GPL");
-MODULE_DESCRIPTION("Symmetric key cipher support for BPF");
diff --git a/include/linux/bpf_crypto.h b/include/linux/bpf_crypto.h
deleted file mode 100644
index a41e71d4e2d9f..0000000000000
--- a/include/linux/bpf_crypto.h
+++ /dev/null
@@ -1,24 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-only */
-/* Copyright (c) 2024 Meta Platforms, Inc. and affiliates. */
-#ifndef _BPF_CRYPTO_H
-#define _BPF_CRYPTO_H
-
-struct bpf_crypto_type {
-	void *(*alloc_tfm)(const char *algo);
-	void (*free_tfm)(void *tfm);
-	int (*has_algo)(const char *algo);
-	int (*setkey)(void *tfm, const u8 *key, unsigned int keylen);
-	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);
-	unsigned int (*ivsize)(void *tfm);
-	unsigned int (*statesize)(void *tfm);
-	u32 (*get_flags)(void *tfm);
-	struct module *owner;
-	char name[14];
-};
-
-int bpf_crypto_register_type(const struct bpf_crypto_type *type);
-int bpf_crypto_unregister_type(const struct bpf_crypto_type *type);
-
-#endif /* _BPF_CRYPTO_H */
diff --git a/kernel/bpf/Kconfig b/kernel/bpf/Kconfig
index d7d25477ef481..a44ecfa3e9ef5 100644
--- a/kernel/bpf/Kconfig
+++ b/kernel/bpf/Kconfig
@@ -91,6 +91,15 @@ config BPF_UNPRIV_DEFAULT_OFF
 
 	  If you are unsure how to answer this question, answer Y.
 
+config BPF_CRYPTO
+	def_bool y
+	depends on BPF_SYSCALL
+	depends on CRYPTO_LIB_AES_CBC
+	depends on CRYPTO_LIB_AES_ECB
+	help
+	  Provide the kfuncs needed for BPF programs to encrypt and decrypt
+	  data. The supported algorithms are AES-CBC and AES-ECB.
+
 source "kernel/bpf/preload/Kconfig"
 
 config BPF_LSM
diff --git a/kernel/bpf/Makefile b/kernel/bpf/Makefile
index 9a92c348bbda6..c1f9b0d3468d3 100644
--- a/kernel/bpf/Makefile
+++ b/kernel/bpf/Makefile
@@ -58,9 +58,7 @@ obj-$(CONFIG_BPF_SYSCALL) += cpumask.o
 # semantics within pahole are revisited accordingly.
 obj-${CONFIG_BPF_LSM} += bpf_lsm_proto.o bpf_lsm.o
 endif
-ifneq ($(CONFIG_CRYPTO),)
-obj-$(CONFIG_BPF_SYSCALL) += crypto.o
-endif
+obj-$(CONFIG_BPF_CRYPTO) += crypto.o
 obj-$(CONFIG_BPF_PRELOAD) += preload/
 
 obj-$(CONFIG_BPF_SYSCALL) += relo_core.o
diff --git a/kernel/bpf/crypto.c b/kernel/bpf/crypto.c
index 51f89cecefb4d..b812204a4d93b 100644
--- a/kernel/bpf/crypto.c
+++ b/kernel/bpf/crypto.c
@@ -1,19 +1,13 @@
 // SPDX-License-Identifier: GPL-2.0-only
 /* Copyright (c) 2024 Meta, Inc */
 #include <linux/bpf.h>
-#include <linux/bpf_crypto.h>
 #include <linux/bpf_mem_alloc.h>
 #include <linux/btf.h>
 #include <linux/btf_ids.h>
 #include <linux/filter.h>
-#include <linux/scatterlist.h>
 #include <linux/skbuff.h>
-#include <crypto/skcipher.h>
-
-struct bpf_crypto_type_list {
-	const struct bpf_crypto_type *type;
-	struct list_head list;
-};
+#include <crypto/aes-cbc.h>
+#include <crypto/aes-ecb.h>
 
 /* BPF crypto initialization parameters struct */
 /**
@@ -36,94 +30,52 @@ struct bpf_crypto_params {
 	u32 authsize;
 };
 
-static LIST_HEAD(bpf_crypto_types);
-static DECLARE_RWSEM(bpf_crypto_types_sem);
+enum bpf_crypto_algo_id {
+	BPF_ALGO_AES_CBC,
+	BPF_ALGO_AES_ECB,
+};
+
+static const struct {
+	const char *type_name;
+	const char *algo_name;
+	enum bpf_crypto_algo_id algo;
+} bpf_crypto_algos[] = {
+	{ "skcipher", "cbc(aes)", BPF_ALGO_AES_CBC },
+	{ "skcipher", "ecb(aes)", BPF_ALGO_AES_ECB },
+};
+
+static bool bpf_crypto_find_algo(const struct bpf_crypto_params *params,
+				 enum bpf_crypto_algo_id *id_ret)
+{
+	for (size_t i = 0; i < ARRAY_SIZE(bpf_crypto_algos); i++) {
+		if (strncmp(bpf_crypto_algos[i].type_name, params->type,
+			    sizeof(params->type)) == 0 &&
+		    strncmp(bpf_crypto_algos[i].algo_name, params->algo,
+			    sizeof(params->algo)) == 0) {
+			*id_ret = bpf_crypto_algos[i].algo;
+			return true;
+		}
+	}
+	return false;
+}
 
 /**
  * struct bpf_crypto_ctx - refcounted BPF crypto context structure
- * @type:	The pointer to bpf crypto type
- * @tfm:	The pointer to instance of crypto API struct.
- * @siv_len:    Size of IV and state storage for cipher
+ * @algo:	The crypto algorithm ID
+ * @key:	Pointer to the struct aes_key.  'void *' is used so that the
+ *		key isn't readable from BPF programs via BTF.
  * @rcu:	The RCU head used to free the crypto context with RCU safety.
  * @usage:	Object reference counter. When the refcount goes to 0, the
  *		memory is released back to the BPF allocator, which provides
  *		RCU safety.
  */
 struct bpf_crypto_ctx {
-	const struct bpf_crypto_type *type;
-	void *tfm;
-	u32 siv_len;
+	enum bpf_crypto_algo_id algo;
+	void *key;
 	struct rcu_head rcu;
 	refcount_t usage;
 };
 
-int bpf_crypto_register_type(const struct bpf_crypto_type *type)
-{
-	struct bpf_crypto_type_list *node;
-	int err = -EBUSY;
-
-	down_write(&bpf_crypto_types_sem);
-	list_for_each_entry(node, &bpf_crypto_types, list) {
-		if (!strcmp(node->type->name, type->name))
-			goto unlock;
-	}
-
-	node = kmalloc_obj(*node);
-	err = -ENOMEM;
-	if (!node)
-		goto unlock;
-
-	node->type = type;
-	list_add(&node->list, &bpf_crypto_types);
-	err = 0;
-
-unlock:
-	up_write(&bpf_crypto_types_sem);
-
-	return err;
-}
-EXPORT_SYMBOL_GPL(bpf_crypto_register_type);
-
-int bpf_crypto_unregister_type(const struct bpf_crypto_type *type)
-{
-	struct bpf_crypto_type_list *node;
-	int err = -ENOENT;
-
-	down_write(&bpf_crypto_types_sem);
-	list_for_each_entry(node, &bpf_crypto_types, list) {
-		if (strcmp(node->type->name, type->name))
-			continue;
-
-		list_del(&node->list);
-		kfree(node);
-		err = 0;
-		break;
-	}
-	up_write(&bpf_crypto_types_sem);
-
-	return err;
-}
-EXPORT_SYMBOL_GPL(bpf_crypto_unregister_type);
-
-static const struct bpf_crypto_type *bpf_crypto_get_type(const char *name)
-{
-	const struct bpf_crypto_type *type = ERR_PTR(-ENOENT);
-	struct bpf_crypto_type_list *node;
-
-	down_read(&bpf_crypto_types_sem);
-	list_for_each_entry(node, &bpf_crypto_types, list) {
-		if (strcmp(node->type->name, name))
-			continue;
-
-		if (try_module_get(node->type->owner))
-			type = node->type;
-		break;
-	}
-	up_read(&bpf_crypto_types_sem);
-
-	return type;
-}
-
 __bpf_kfunc_start_defs();
 
 /**
@@ -132,92 +84,76 @@ __bpf_kfunc_start_defs();
  * Allocates a crypto context that can be used, acquired, and released by
  * a BPF program. The crypto context returned by this function must either
  * be embedded in a map as a kptr, or freed with bpf_crypto_ctx_release().
- * As crypto API functions use GFP_KERNEL allocations, this function can
- * only be used in sleepable BPF programs.
+ * As this uses a GFP_KERNEL allocation, this function can only be used in
+ * sleepable BPF programs.
  *
  * bpf_crypto_ctx_create() allocates memory for crypto context.
  * 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
- * @err:	integer to store error code when NULL is returned.
+ * @params__sz:	size of struct bpf_crypto_params used by bpf program
+ * @err_ret:	integer to store error code when NULL is returned.
  */
 __bpf_kfunc struct bpf_crypto_ctx *
 bpf_crypto_ctx_create(const struct bpf_crypto_params *params, u32 params__sz,
-		      int *err)
+		      int *err_ret)
 {
-	const struct bpf_crypto_type *type;
 	struct bpf_crypto_ctx *ctx;
+	int err;
 
 	if (!params || params->reserved[0] || params->reserved[1] ||
 	    params__sz != sizeof(struct bpf_crypto_params)) {
-		*err = -EINVAL;
+		*err_ret = -EINVAL;
 		return NULL;
 	}
 
-	type = bpf_crypto_get_type(params->type);
-	if (IS_ERR(type)) {
-		*err = PTR_ERR(type);
-		return NULL;
-	}
-
-	if (!type->has_algo(params->algo)) {
-		*err = -EOPNOTSUPP;
-		goto err_module_put;
-	}
-
-	if (!!params->authsize ^ !!type->setauthsize) {
-		*err = -EOPNOTSUPP;
-		goto err_module_put;
-	}
-
 	if (!params->key_len || params->key_len > sizeof(params->key)) {
-		*err = -EINVAL;
-		goto err_module_put;
+		*err_ret = -EINVAL;
+		return NULL;
 	}
 
 	ctx = kzalloc_obj(*ctx);
 	if (!ctx) {
-		*err = -ENOMEM;
-		goto err_module_put;
+		*err_ret = -ENOMEM;
+		return NULL;
 	}
 
-	ctx->type = type;
-	ctx->tfm = type->alloc_tfm(params->algo);
-	if (IS_ERR(ctx->tfm)) {
-		*err = PTR_ERR(ctx->tfm);
-		goto err_free_ctx;
+	if (!bpf_crypto_find_algo(params, &ctx->algo)) {
+		err = -EOPNOTSUPP;
+		goto out;
 	}
 
-	if (params->authsize) {
-		*err = type->setauthsize(ctx->tfm, params->authsize);
-		if (*err)
-			goto err_free_tfm;
+	switch (ctx->algo) {
+	case BPF_ALGO_AES_CBC:
+	case BPF_ALGO_AES_ECB:
+		if (params->authsize) {
+			err = -EOPNOTSUPP;
+			goto out;
+		}
+		ctx->key = kzalloc_obj(struct aes_key);
+		if (!ctx->key) {
+			err = -ENOMEM;
+			goto out;
+		}
+		err = aes_preparekey((struct aes_key *)ctx->key, params->key,
+				     params->key_len);
+		break;
+	default:
+		WARN_ON_ONCE(1);
+		err = -EOPNOTSUPP;
+		break;
 	}
 
-	*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;
+out:
+	if (err) {
+		kfree_sensitive(ctx->key);
+		kfree_sensitive(ctx);
+		*err_ret = err;
+		return NULL;
 	}
-
-	ctx->siv_len = type->ivsize(ctx->tfm) + type->statesize(ctx->tfm);
-
 	refcount_set(&ctx->usage, 1);
-
+	*err_ret = 0;
 	return ctx;
-
-err_free_tfm:
-	type->free_tfm(ctx->tfm);
-err_free_ctx:
-	kfree(ctx);
-err_module_put:
-	module_put(type->owner);
-
-	return NULL;
 }
 
 static void crypto_free_cb(struct rcu_head *head)
@@ -225,9 +161,8 @@ static void crypto_free_cb(struct rcu_head *head)
 	struct bpf_crypto_ctx *ctx;
 
 	ctx = container_of(head, struct bpf_crypto_ctx, rcu);
-	ctx->type->free_tfm(ctx->tfm);
-	module_put(ctx->type->owner);
-	kfree(ctx);
+	kfree_sensitive(ctx->key);
+	kfree_sensitive(ctx);
 }
 
 /**
@@ -267,27 +202,53 @@ __bpf_kfunc void bpf_crypto_ctx_release_dtor(void *ctx)
 }
 CFI_NOSEAL(bpf_crypto_ctx_release_dtor);
 
+static int bpf_aes_cbc_crypt(u8 *dst, u32 dst_len, const u8 *src, u32 src_len,
+			     u8 *iv, u32 iv_len, const struct aes_key *key,
+			     bool decrypt)
+{
+	if (iv_len != AES_BLOCK_SIZE)
+		return -EINVAL;
+	if (src_len % AES_BLOCK_SIZE || dst_len < src_len)
+		return -EINVAL;
+	if (decrypt)
+		aes_cbc_decrypt(dst, src, src_len, iv, key);
+	else
+		aes_cbc_encrypt(dst, src, src_len, iv, key);
+	return 0;
+}
+
+static int bpf_aes_ecb_crypt(u8 *dst, u32 dst_len, const u8 *src, u32 src_len,
+			     u8 *iv, u32 iv_len, const struct aes_key *key,
+			     bool decrypt)
+{
+	if (iv_len != 0)
+		return -EINVAL;
+	if (src_len % AES_BLOCK_SIZE || dst_len < src_len)
+		return -EINVAL;
+	if (decrypt)
+		aes_ecb_decrypt(dst, src, src_len, key);
+	else
+		aes_ecb_encrypt(dst, src, src_len, key);
+	return 0;
+}
+
 static int bpf_crypto_crypt(const struct bpf_crypto_ctx *ctx,
 			    const struct bpf_dynptr_kern *src,
 			    const struct bpf_dynptr_kern *dst,
-			    const struct bpf_dynptr_kern *siv,
+			    const struct bpf_dynptr_kern *iv,
 			    bool decrypt)
 {
-	u32 src_len, dst_len, siv_len;
+	u32 src_len, dst_len, iv_len;
 	const u8 *psrc;
 	u8 *pdst, *piv;
-	int err;
 
 	if (__bpf_dynptr_is_rdonly(dst))
 		return -EINVAL;
 
-	siv_len = siv ? __bpf_dynptr_size(siv) : 0;
+	iv_len = iv ? __bpf_dynptr_size(iv) : 0;
 	src_len = __bpf_dynptr_size(src);
 	dst_len = __bpf_dynptr_size(dst);
-	if (!src_len || !dst_len || src_len > dst_len)
-		return -EINVAL;
-
-	if (siv_len != ctx->siv_len)
+	if (!src_len || !dst_len)
 		return -EINVAL;
 
 	psrc = __bpf_dynptr_data(src, src_len);
@@ -297,14 +258,20 @@ static int bpf_crypto_crypt(const struct bpf_crypto_ctx *ctx,
 	if (!pdst)
 		return -EINVAL;
 
-	piv = siv_len ? __bpf_dynptr_data_rw(siv, siv_len) : NULL;
-	if (siv_len && !piv)
+	piv = iv_len ? __bpf_dynptr_data_rw(iv, iv_len) : NULL;
+	if (iv_len && !piv)
 		return -EINVAL;
 
-	err = decrypt ? ctx->type->decrypt(ctx->tfm, psrc, pdst, src_len, piv)
-		      : ctx->type->encrypt(ctx->tfm, psrc, pdst, src_len, piv);
-
-	return err;
+	switch (ctx->algo) {
+	case BPF_ALGO_AES_CBC:
+		return bpf_aes_cbc_crypt(pdst, dst_len, psrc, src_len, piv,
+					 iv_len, ctx->key, decrypt);
+	case BPF_ALGO_AES_ECB:
+		return bpf_aes_ecb_crypt(pdst, dst_len, psrc, src_len, piv,
+					 iv_len, ctx->key, decrypt);
+	default:
+		return -EINVAL;
+	}
 }
 
 /**
@@ -312,20 +279,20 @@ static int bpf_crypto_crypt(const struct bpf_crypto_ctx *ctx,
  * @ctx:		The crypto context being used. The ctx must be a trusted pointer.
  * @src:		bpf_dynptr to the encrypted data. Must be a trusted pointer.
  * @dst:		bpf_dynptr to the buffer where to store the result. Must be a trusted pointer.
- * @siv__nullable:	bpf_dynptr to IV data and state data to be used by decryptor. May be NULL.
+ * @iv__nullable:	bpf_dynptr to the initialization vector. May be NULL.
  *
  * Decrypts provided buffer using IV data and the crypto context. Crypto context must be configured.
  */
 __bpf_kfunc int bpf_crypto_decrypt(struct bpf_crypto_ctx *ctx,
 				   const struct bpf_dynptr *src,
 				   const struct bpf_dynptr *dst,
-				   const struct bpf_dynptr *siv__nullable)
+				   const struct bpf_dynptr *iv__nullable)
 {
 	const struct bpf_dynptr_kern *src_kern = (struct bpf_dynptr_kern *)src;
 	const struct bpf_dynptr_kern *dst_kern = (struct bpf_dynptr_kern *)dst;
-	const struct bpf_dynptr_kern *siv_kern = (struct bpf_dynptr_kern *)siv__nullable;
+	const struct bpf_dynptr_kern *iv_kern = (struct bpf_dynptr_kern *)iv__nullable;
 
-	return bpf_crypto_crypt(ctx, src_kern, dst_kern, siv_kern, true);
+	return bpf_crypto_crypt(ctx, src_kern, dst_kern, iv_kern, true);
 }
 
 /**
@@ -333,20 +300,20 @@ __bpf_kfunc int bpf_crypto_decrypt(struct bpf_crypto_ctx *ctx,
  * @ctx:		The crypto context being used. The ctx must be a trusted pointer.
  * @src:		bpf_dynptr to the plain data. Must be a trusted pointer.
  * @dst:		bpf_dynptr to the buffer where to store the result. Must be a trusted pointer.
- * @siv__nullable:	bpf_dynptr to IV data and state data to be used by decryptor. May be NULL.
+ * @iv__nullable:	bpf_dynptr to the initialization vector. May be NULL.
  *
  * Encrypts provided buffer using IV data and the crypto context. Crypto context must be configured.
  */
 __bpf_kfunc int bpf_crypto_encrypt(struct bpf_crypto_ctx *ctx,
 				   const struct bpf_dynptr *src,
 				   const struct bpf_dynptr *dst,
-				   const struct bpf_dynptr *siv__nullable)
+				   const struct bpf_dynptr *iv__nullable)
 {
 	const struct bpf_dynptr_kern *src_kern = (struct bpf_dynptr_kern *)src;
 	const struct bpf_dynptr_kern *dst_kern = (struct bpf_dynptr_kern *)dst;
-	const struct bpf_dynptr_kern *siv_kern = (struct bpf_dynptr_kern *)siv__nullable;
+	const struct bpf_dynptr_kern *iv_kern = (struct bpf_dynptr_kern *)iv__nullable;
 
-	return bpf_crypto_crypt(ctx, src_kern, dst_kern, siv_kern, false);
+	return bpf_crypto_crypt(ctx, src_kern, dst_kern, iv_kern, false);
 }
 
 __bpf_kfunc_end_defs();
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index d62c0f74cff5e..c6957ef12d763 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -4664,7 +4664,7 @@ BTF_ID(struct, cgroup)
 BTF_ID(struct, bpf_cpumask)
 #endif
 BTF_ID(struct, task_struct)
-#ifdef CONFIG_CRYPTO
+#ifdef CONFIG_BPF_CRYPTO
 BTF_ID(struct, bpf_crypto_ctx)
 #endif
 #ifdef CONFIG_INET
diff --git a/tools/testing/selftests/bpf/config b/tools/testing/selftests/bpf/config
index d292cb60a5a4e..2b883b388f90c 100644
--- a/tools/testing/selftests/bpf/config
+++ b/tools/testing/selftests/bpf/config
@@ -18,7 +18,7 @@ CONFIG_CRYPTO_SHA512=y
 CONFIG_CRYPTO_USER_API=y
 CONFIG_CRYPTO_USER_API_HASH=y
 CONFIG_CRYPTO_USER_API_SKCIPHER=y
-CONFIG_CRYPTO_SKCIPHER=y
+CONFIG_CRYPTO_CBC=y
 CONFIG_CRYPTO_ECB=y
 CONFIG_CRYPTO_AES=y
 CONFIG_DEBUG_INFO=y

23/3 2026/09/24 05:38 llm kmsan-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 9928
							output: 18
							thoughts: 1789
						

Thoughts:
**KMSAN Fuzzing Session Justification: Analyzing a BPF Crypto Patch**

Okay, let's break this down. My goal here is to determine if this patch series warrants a dedicated KMSAN fuzzing run. I need to understand the changes and potential risk areas.

First, I see a significant shift. The patch removes the old `crypto/bpf_crypto_skcipher.c` and associated header, which suggests a substantial refactoring of the BPF crypto implementation. The core of the change appears to be in how the BPF crypto functionality interacts with the kernel's cryptographic primitives. It looks like the indirect calls through the kernel crypto API, `crypto_lskcipher`, are gone, replaced with direct calls to AES-CBC and AES-ECB routines from `crypto/aes-cbc.h` and `crypto/aes-ecb.h`. This is a crucial area to assess. It's a textbook riskier move, removing an intermediate layer.

The new `CONFIG_BPF_CRYPTO` config option and associated dependencies (`BPF_SYSCALL`, `CRYPTO_LIB_AES_CBC`, `CRYPTO_LIB_AES_ECB`) confirm the BPF crypto functionality. The code seems to be focused on providing kfuncs (kernel functions) for BPF programs to handle AES encryption and decryption (CBC and ECB modes).

Looking at the code, particularly `kernel/bpf/crypto.c`, I see that the patch allocates `struct bpf_crypto_ctx` and `struct aes_key` using `kzalloc_obj`. Error handling within `bpf_crypto_ctx_create` uses `kfree_sensitive` on both, which is a good sign. The `aes_preparekey` function is used for key preparation. The key itself is allocated and freed in a sensitive manner with `kfree_sensitive`, which is vital, but still a code path to monitor with KMSAN.

The `bpf_aes_cbc_crypt` and `bpf_aes_ecb_crypt` functions now perform the actual encryption and decryption. They validate the lengths of the input/output buffers and the IV (for CBC) before calling the corresponding AES functions. The interaction with `bpf_dynptr_kern` in `bpf_crypto_crypt` for buffer handling is also something that catches my eye; dynamic pointers can be a source of problems, and the checks are important.

The `aes_cbc_encrypt`, `aes_cbc_decrypt`, `aes_ecb_encrypt`, `aes_ecb_decrypt` and `aes_preparekey` functions themselves, where the cryptographic operations actually occur, will need scrutiny. I'm going to quickly check their definitions using `grepper` to see what memory they touch and how they interact. This is important to determine what kind of KMSAN coverage will be useful. This is an immediate red flag! AES routines are complex, and subtle memory errors can have security implications. KMSAN can expose potential issues related to uninitialized reads, out-of-bounds accesses, or other memory corruption. Given the direct calls to these functions, the BPF context, and dynamic pointers, a KMSAN session targeting these new functions looks warranted to me.



24/3 2026/09/24 05:38 tool grepper
0m Args:
{
  "Expression": "aes_preparekey"
}

Results:
{
  "Output": "arch/arm/crypto/aes-neonbs-glue.c=234=static int aesbs_xts_setkey(struct crypto_skcipher *tfm, const u8 *in_key,\n--\narch/arm/crypto/aes-neonbs-glue.c-244-\tkey_len /= 2;\narch/arm/crypto/aes-neonbs-glue.c:245:\terr = aes_preparekey(\u0026ctx-\u003efallback, in_key, key_len);\narch/arm/crypto/aes-neonbs-glue.c-246-\tif (err)\n--\ncrypto/aes.c=26=static int crypto_aes_setkey(struct crypto_tfm *tfm, const u8 *in_key,\n--\ncrypto/aes.c-30-\ncrypto/aes.c:31:\treturn aes_preparekey(key, in_key, key_len);\ncrypto/aes.c-32-}\n--\ncrypto/aes.c=213=crypto_aes_skcipher_setkey(struct crypto_skcipher *tfm, const u8 *in_key,\n--\ncrypto/aes.c-217-\ncrypto/aes.c:218:\treturn aes_preparekey(key, in_key, key_len);\ncrypto/aes.c-219-}\n--\ndrivers/crypto/chelsio/chcr_algo.c=1025=static int chcr_update_tweak(struct skcipher_request *req, u8 *iv,\n--\ndrivers/crypto/chelsio/chcr_algo.c-1046-\t\t\t== CHCR_KEYCTX_CIPHER_KEY_SIZE_192)\ndrivers/crypto/chelsio/chcr_algo.c:1047:\t\tret = aes_preparekey(\u0026aes, key, keylen - 8);\ndrivers/crypto/chelsio/chcr_algo.c-1048-\telse\ndrivers/crypto/chelsio/chcr_algo.c:1049:\t\tret = aes_preparekey(\u0026aes, key, keylen);\ndrivers/crypto/chelsio/chcr_algo.c-1050-\tif (ret)\n--\ninclude/crypto/aes-cbc.h-19- *\tlast ciphertext block (or left unchanged if @len == 0).\ninclude/crypto/aes-cbc.h:20: * @key: The key, already prepared using aes_preparekey() or aes_prepareenckey()\ninclude/crypto/aes-cbc.h-21- *\n--\ninclude/crypto/aes-cbc.h=27=void aes_cbc_encrypt(u8 *dst, const u8 *src, size_t len,\n--\ninclude/crypto/aes-cbc.h-37- *\tlast ciphertext block (or left unchanged if @len == 0).\ninclude/crypto/aes-cbc.h:38: * @key: The key, already prepared using aes_preparekey()\ninclude/crypto/aes-cbc.h-39- *\n--\ninclude/crypto/aes-cbc.h=45=void aes_cbc_decrypt(u8 *dst, const u8 *src, size_t len,\n--\ninclude/crypto/aes-cbc.h-54- * @iv: The initialization vector, clobbered by this function\ninclude/crypto/aes-cbc.h:55: * @key: The key, already prepared using aes_preparekey() or aes_prepareenckey()\ninclude/crypto/aes-cbc.h-56- *\n--\ninclude/crypto/aes-cbc.h=59=void aes_cbc_cts_encrypt(u8 *dst, const u8 *src, size_t len,\n--\ninclude/crypto/aes-cbc.h-68- * @iv: The initialization vector, clobbered by this function\ninclude/crypto/aes-cbc.h:69: * @key: The key, already prepared using aes_preparekey()\ninclude/crypto/aes-cbc.h-70- *\n--\ninclude/crypto/aes-ctr.h-18- * @ctr: The counter.  It will be incremented by ceil(@len / AES_BLOCK_SIZE).\ninclude/crypto/aes-ctr.h:19: * @key: The key, already prepared using aes_preparekey() or aes_prepareenckey()\ninclude/crypto/aes-ctr.h-20- *\n--\ninclude/crypto/aes-ctr.h=34=void aes_ctr(u8 *dst, const u8 *src, size_t len,\n--\ninclude/crypto/aes-ctr.h-45- * @iv: The initialization vector\ninclude/crypto/aes-ctr.h:46: * @key: The key, already prepared using aes_preparekey() or aes_prepareenckey()\ninclude/crypto/aes-ctr.h-47- *\n--\ninclude/crypto/aes-ecb.h-17- * @len: Number of bytes to encrypt.  Must be a multiple of AES_BLOCK_SIZE.\ninclude/crypto/aes-ecb.h:18: * @key: The key, already prepared using aes_preparekey() or aes_prepareenckey()\ninclude/crypto/aes-ecb.h-19- *\n--\ninclude/crypto/aes-ecb.h=28=void aes_ecb_encrypt(u8 *dst, const u8 *src, size_t len, aes_encrypt_arg key);\n--\ninclude/crypto/aes-ecb.h-35- * @len: Number of bytes to decrypt.  Must be a multiple of AES_BLOCK_SIZE.\ninclude/crypto/aes-ecb.h:36: * @key: The key, already prepared using aes_preparekey()\ninclude/crypto/aes-ecb.h-37- *\n--\ninclude/crypto/aes.h=308=void aes_sparc64_ctr_crypt_256(const u64 *key, const u64 *input, u64 *output,\n--\ninclude/crypto/aes.h-312-/**\ninclude/crypto/aes.h:313: * aes_preparekey() - Prepare an AES key for encryption and decryption\ninclude/crypto/aes.h-314- * @key: (output) The key structure to initialize\n--\ninclude/crypto/aes.h-334- */\ninclude/crypto/aes.h:335:int aes_preparekey(struct aes_key *key, const u8 *in_key, size_t key_len);\ninclude/crypto/aes.h-336-\n--\ninclude/crypto/aes.h=377=void aes_encrypt(aes_encrypt_arg key, u8 out[at_least AES_BLOCK_SIZE],\n--\ninclude/crypto/aes.h-381- * aes_decrypt() - Decrypt a single AES block\ninclude/crypto/aes.h:382: * @key: The AES key, previously initialized by aes_preparekey()\ninclude/crypto/aes.h-383- * @out: Buffer to store the plaintext block\n--\nkernel/bpf/crypto.c=98=bpf_crypto_ctx_create(const struct bpf_crypto_params *params, u32 params__sz,\n--\nkernel/bpf/crypto.c-137-\t\t}\nkernel/bpf/crypto.c:138:\t\terr = aes_preparekey((struct aes_key *)ctx-\u003ekey, params-\u003ekey,\nkernel/bpf/crypto.c-139-\t\t\t\t     params-\u003ekey_len);\n--\nlib/crypto/aes.c=411=static void __maybe_unused aes_decrypt_generic(const u32 inv_rndkeys[],\n--\nlib/crypto/aes.c-450- *\nlib/crypto/aes.c:451: * aes_preparekey_arch() is passed an optional pointer 'inv_k' which points to\nlib/crypto/aes.c-452- * the area to store the prepared decryption key.  It will be NULL if the user\nlib/crypto/aes.c:453: * is requesting encryption-only.  aes_preparekey_arch() is also passed a valid\nlib/crypto/aes.c-454- * 'key_len' and 'nrounds', corresponding to AES-128, AES-192, or AES-256.\n--\nlib/crypto/aes.c-461-\nlib/crypto/aes.c:462:static void aes_preparekey_arch(union aes_enckey_arch *k,\nlib/crypto/aes.c-463-\t\t\t\tunion aes_invkey_arch *inv_k,\n--\nlib/crypto/aes.c=477=static void aes_decrypt_arch(const struct aes_key *key,\n--\nlib/crypto/aes.c-484-\nlib/crypto/aes.c:485:static int __aes_preparekey(struct aes_enckey *enc_key,\nlib/crypto/aes.c-486-\t\t\t    union aes_invkey_arch *inv_k,\n--\nlib/crypto/aes.c-492-\tenc_key-\u003enrounds = 6 + key_len / 4;\nlib/crypto/aes.c:493:\taes_preparekey_arch(\u0026enc_key-\u003ek, inv_k, in_key, key_len,\nlib/crypto/aes.c-494-\t\t\t    enc_key-\u003enrounds);\n--\nlib/crypto/aes.c-497-\nlib/crypto/aes.c:498:int aes_preparekey(struct aes_key *key, const u8 *in_key, size_t key_len)\nlib/crypto/aes.c-499-{\nlib/crypto/aes.c:500:\treturn __aes_preparekey((struct aes_enckey *)key, \u0026key-\u003einv_k,\nlib/crypto/aes.c-501-\t\t\t\tin_key, key_len);\nlib/crypto/aes.c-502-}\nlib/crypto/aes.c:503:EXPORT_SYMBOL(aes_preparekey);\nlib/crypto/aes.c-504-\nlib/crypto/aes.c=505=int aes_prepareenckey(struct aes_enckey *key, const u8 *in_key, size_t key_len)\nlib/crypto/aes.c-506-{\nlib/crypto/aes.c:507:\treturn __aes_preparekey(key, NULL, in_key, key_len);\nlib/crypto/aes.c-508-}\n--\nlib/crypto/aes.c=526=static void __init aes_fips_test(void)\n--\nlib/crypto/aes.c-530-\nlib/crypto/aes.c:531:\tif (aes_preparekey(\u0026key, fips_test_key, sizeof(fips_test_key)) != 0)\nlib/crypto/aes.c-532-\t\tpanic(\"aes: FIPS self-test failed (preparekey)\\n\");\n--\nlib/crypto/aes.c=814=static void __init aes_ecb_fips_test(void)\n--\nlib/crypto/aes.c-818-\nlib/crypto/aes.c:819:\tif (aes_preparekey(\u0026key, fips_test_key, sizeof(fips_test_key)) != 0)\nlib/crypto/aes.c-820-\t\tpanic(\"aes: ECB FIPS self-test failed (preparekey)\\n\");\n--\nlib/crypto/aes.c=1024=static void __init aes_cbc_fips_test(void)\n--\nlib/crypto/aes.c-1029-\nlib/crypto/aes.c:1030:\tif (aes_preparekey(\u0026key, fips_test_key, sizeof(fips_test_key)) != 0)\nlib/crypto/aes.c-1031-\t\tpanic(\"aes: CBC FIPS self-test failed (preparekey)\\n\");\n--\nlib/crypto/aes.c=1047=static void __init aes_cbc_cts_fips_test(void)\n--\nlib/crypto/aes.c-1058-\nlib/crypto/aes.c:1059:\tif (aes_preparekey(\u0026key, fips_test_key, sizeof(fips_test_key)) != 0)\nlib/crypto/aes.c-1060-\t\tpanic(\"aes: CBC-CTS FIPS self-test failed (preparekey)\\n\");\n--\nlib/crypto/aes.c=1206=int aes_xts_preparekey(struct aes_xts_key *key, const u8 *in_key,\n--\nlib/crypto/aes.c-1214-\t/* First half of XTS key is the main key */\nlib/crypto/aes.c:1215:\terr = aes_preparekey(\u0026key-\u003emain_key, in_key, key_len / 2);\nlib/crypto/aes.c-1216-\tif (unlikely(err))\n--\nlib/crypto/arm/aes.h=12=asmlinkage void __aes_arm_decrypt(const u32 inv_rk[], int rounds,\n--\nlib/crypto/arm/aes.h-15-\nlib/crypto/arm/aes.h:16:static void aes_preparekey_arch(union aes_enckey_arch *k,\nlib/crypto/arm/aes.h-17-\t\t\t\tunion aes_invkey_arch *inv_k,\n--\nlib/crypto/arm64/aes.h=41=static void aes_expandkey_arm64(u32 rndkeys[], u32 *inv_rndkeys,\n--\nlib/crypto/arm64/aes.h-107-\nlib/crypto/arm64/aes.h:108:static void aes_preparekey_arch(union aes_enckey_arch *k,\nlib/crypto/arm64/aes.h-109-\t\t\t\tunion aes_invkey_arch *inv_k,\n--\nlib/crypto/powerpc/aes.h=38=static void spe_end(void)\n--\nlib/crypto/powerpc/aes.h-44-\nlib/crypto/powerpc/aes.h:45:static void aes_preparekey_arch(union aes_enckey_arch *k,\nlib/crypto/powerpc/aes.h-46-\t\t\t\tunion aes_invkey_arch *inv_k,\n--\nlib/crypto/powerpc/aes.h=109=static void rndkey_from_vsx(u32 out[4], const u32 in[4], bool apply_inv_mix)\n--\nlib/crypto/powerpc/aes.h-128-\nlib/crypto/powerpc/aes.h:129:static void aes_preparekey_arch(union aes_enckey_arch *k,\nlib/crypto/powerpc/aes.h-130-\t\t\t\tunion aes_invkey_arch *inv_k,\n--\nlib/crypto/riscv/aes.h=15=void aes_decrypt_zvkned(const u32 rndkeys[], int key_len,\n--\nlib/crypto/riscv/aes.h-17-\nlib/crypto/riscv/aes.h:18:static void aes_preparekey_arch(union aes_enckey_arch *k,\nlib/crypto/riscv/aes.h-19-\t\t\t\tunion aes_invkey_arch *inv_k,\n--\nlib/crypto/s390/aes.h=12=static __ro_after_init DEFINE_STATIC_KEY_FALSE(have_cpacf_aes256);\n--\nlib/crypto/s390/aes.h-19- */\nlib/crypto/s390/aes.h:20:static void aes_preparekey_arch(union aes_enckey_arch *k,\nlib/crypto/s390/aes.h-21-\t\t\t\tunion aes_invkey_arch *inv_k,\n--\nlib/crypto/sparc/aes.h=45=void aes_sparc64_decrypt_256(const u64 *key, const u32 *input, u32 *output);\nlib/crypto/sparc/aes.h-46-\nlib/crypto/sparc/aes.h:47:static void aes_preparekey_arch(union aes_enckey_arch *k,\nlib/crypto/sparc/aes.h-48-\t\t\t\tunion aes_invkey_arch *inv_k,\n--\nlib/crypto/x86/aes.h=18=void aes_decrypt_aesni(const u32 inv_rndkeys[], int nrounds,\n--\nlib/crypto/x86/aes.h-31- */\nlib/crypto/x86/aes.h:32:static void aes_preparekey_arch(union aes_enckey_arch *k,\nlib/crypto/x86/aes.h-33-\t\t\t\tunion aes_invkey_arch *inv_k,\n"
}

25/3 2026/09/24 05:38 llm kmsan-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 16585
							output: 35
							thoughts: 30
						

Thoughts:
Okay, here's my take on those thoughts, tailored for an expert audience, presented in the first person:

**Deep Dive into `kernel/bpf/crypto.c` for BPF Dynamic Pointers and Error Handling**

Alright, let's get down to brass tacks. I need to refresh my understanding of how dynamic pointers (`dynptrs`) are managed within the BPF crypto context, specifically in `kernel/bpf/crypto.c`. I want to see a holistic picture, not just snippets. I'm especially interested in how they integrate with return codes and buffer handling within the crypto functions. It's crucial to understand the lifecycle of these pointers – allocation, use, deallocation, and the implications for error propagation. I need to map out how they're passed, dereferenced, and whether there are any potential race conditions or memory leaks that might arise. Furthermore, I want to observe the return code strategies. For example, are errors carefully propagated, or are there places where errors could be silently masked? The same is true for buffer usage: are bounds checks performed, how are allocations handled and what is the general strategy used for buffer size management? I need a deep dive into the code to ensure the design is sound and that potential vulnerabilities are mitigated.



26/3 2026/09/24 05:38 tool read-file
0m Args:
{
  "File": "kernel/bpf/crypto.c",
  "FirstLine": 1,
  "LineCount": 100
}

Results:
{
  "Contents": "   1:\t// SPDX-License-Identifier: GPL-2.0-only\n   2:\t/* Copyright (c) 2024 Meta, Inc */\n   3:\t#include \u003clinux/bpf.h\u003e\n   4:\t#include \u003clinux/bpf_mem_alloc.h\u003e\n   5:\t#include \u003clinux/btf.h\u003e\n   6:\t#include \u003clinux/btf_ids.h\u003e\n   7:\t#include \u003clinux/filter.h\u003e\n   8:\t#include \u003clinux/skbuff.h\u003e\n   9:\t#include \u003ccrypto/aes-cbc.h\u003e\n  10:\t#include \u003ccrypto/aes-ecb.h\u003e\n  11:\t\n  12:\t/* BPF crypto initialization parameters struct */\n  13:\t/**\n  14:\t * struct bpf_crypto_params - BPF crypto initialization parameters structure\n  15:\t * @type:\tThe string of crypto operation type.\n  16:\t * @reserved:\tReserved member, will be reused for more options in future\n  17:\t *\t\tValues:\n  18:\t *\t\t  0\n  19:\t * @algo:\tThe string of algorithm to initialize.\n  20:\t * @key:\tThe cipher key used to init crypto algorithm.\n  21:\t * @key_len:\tThe length of cipher key.\n  22:\t * @authsize:\tThe length of authentication tag used by algorithm.\n  23:\t */\n  24:\tstruct bpf_crypto_params {\n  25:\t\tchar type[14];\n  26:\t\tu8 reserved[2];\n  27:\t\tchar algo[128];\n  28:\t\tu8 key[256];\n  29:\t\tu32 key_len;\n  30:\t\tu32 authsize;\n  31:\t};\n  32:\t\n  33:\tenum bpf_crypto_algo_id {\n  34:\t\tBPF_ALGO_AES_CBC,\n  35:\t\tBPF_ALGO_AES_ECB,\n  36:\t};\n  37:\t\n  38:\tstatic const struct {\n  39:\t\tconst char *type_name;\n  40:\t\tconst char *algo_name;\n  41:\t\tenum bpf_crypto_algo_id algo;\n  42:\t} bpf_crypto_algos[] = {\n  43:\t\t{ \"skcipher\", \"cbc(aes)\", BPF_ALGO_AES_CBC },\n  44:\t\t{ \"skcipher\", \"ecb(aes)\", BPF_ALGO_AES_ECB },\n  45:\t};\n  46:\t\n  47:\tstatic bool bpf_crypto_find_algo(const struct bpf_crypto_params *params,\n  48:\t\t\t\t\t enum bpf_crypto_algo_id *id_ret)\n  49:\t{\n  50:\t\tfor (size_t i = 0; i \u003c ARRAY_SIZE(bpf_crypto_algos); i++) {\n  51:\t\t\tif (strncmp(bpf_crypto_algos[i].type_name, params-\u003etype,\n  52:\t\t\t\t    sizeof(params-\u003etype)) == 0 \u0026\u0026\n  53:\t\t\t    strncmp(bpf_crypto_algos[i].algo_name, params-\u003ealgo,\n  54:\t\t\t\t    sizeof(params-\u003ealgo)) == 0) {\n  55:\t\t\t\t*id_ret = bpf_crypto_algos[i].algo;\n  56:\t\t\t\treturn true;\n  57:\t\t\t}\n  58:\t\t}\n  59:\t\treturn false;\n  60:\t}\n  61:\t\n  62:\t/**\n  63:\t * struct bpf_crypto_ctx - refcounted BPF crypto context structure\n  64:\t * @algo:\tThe crypto algorithm ID\n  65:\t * @key:\tPointer to the struct aes_key.  'void *' is used so that the\n  66:\t *\t\tkey isn't readable from BPF programs via BTF.\n  67:\t * @rcu:\tThe RCU head used to free the crypto context with RCU safety.\n  68:\t * @usage:\tObject reference counter. When the refcount goes to 0, the\n  69:\t *\t\tmemory is released back to the BPF allocator, which provides\n  70:\t *\t\tRCU safety.\n  71:\t */\n  72:\tstruct bpf_crypto_ctx {\n  73:\t\tenum bpf_crypto_algo_id algo;\n  74:\t\tvoid *key;\n  75:\t\tstruct rcu_head rcu;\n  76:\t\trefcount_t usage;\n  77:\t};\n  78:\t\n  79:\t__bpf_kfunc_start_defs();\n  80:\t\n  81:\t/**\n  82:\t * bpf_crypto_ctx_create() - Create a mutable BPF crypto context.\n  83:\t *\n  84:\t * Allocates a crypto context that can be used, acquired, and released by\n  85:\t * a BPF program. The crypto context returned by this function must either\n  86:\t * be embedded in a map as a kptr, or freed with bpf_crypto_ctx_release().\n  87:\t * As this uses a GFP_KERNEL allocation, this function can only be used in\n  88:\t * sleepable BPF programs.\n  89:\t *\n  90:\t * bpf_crypto_ctx_create() allocates memory for crypto context.\n  91:\t * It may return NULL if no memory is available.\n  92:\t * @params:\tpointer to struct bpf_crypto_params which contains all the\n  93:\t *\t\tdetails needed to initialise crypto context.\n  94:\t * @params__sz:\tsize of struct bpf_crypto_params used by bpf program\n  95:\t * @err_ret:\tinteger to store error code when NULL is returned.\n  96:\t */\n  97:\t__bpf_kfunc struct bpf_crypto_ctx *\n  98:\tbpf_crypto_ctx_create(const struct bpf_crypto_params *params, u32 params__sz,\n  99:\t\t\t      int *err_ret)\n 100:\t{\n"
}

27/3 2026/09/24 05:38 llm kmsan-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 18488
							output: 37
							thoughts: 17
						

Thoughts:
Okay, here's my attempt at summarizing the thought process, tailored for an expert audience, and written in the first person:

**Diving into `kernel/bpf/crypto.c`**

Alright, time to dig into the remaining parts of `kernel/bpf/crypto.c`. I need to get a comprehensive view of how the BPF infrastructure interacts with the kernel's cryptographic routines. I know the basics, but I'm looking for the nuances – the specific function calls, the data structures used, and, most importantly, how BPF programs can leverage (or potentially misuse) cryptographic primitives within the kernel. I'm especially interested in whether or not there are any particular potential vulnerabilities related to this.

I'll be paying close attention to any access control mechanisms. Does BPF have tight restrictions on which crypto functions it can call? Are there any limitations on the types of keys it can use or modify? Any implications with regard to user-space key management? The kernel crypto API is complex, so I’m looking for areas where BPF could introduce new attack vectors.

I'm mentally preparing to look for things like:

*   **Helper functions**: What are the specific helper functions provided to BPF programs for cryptographic operations?
*   **Key management**: How are keys handled? Are there any specific key derivation functions or secure key storage mechanisms exposed to BPF?
*   **Error handling**: How does the code handle errors related to cryptographic operations within the BPF context? Are there potential information leaks or denial-of-service vulnerabilities?
*   **Performance**: Are there any performance considerations or optimizations that are specifically relevant to BPF-based crypto operations?
*   **ABI stability**: Is the exposed interface stable enough to be used without issues?

I anticipate finding a carefully controlled interface, but I want to be thorough. I'll be looking for any potential blind spots or clever exploitations. Let's see what we uncover.



28/3 2026/09/24 05:38 tool read-file
0m Args:
{
  "File": "kernel/bpf/crypto.c",
  "FirstLine": 101,
  "LineCount": 100
}

Results:
{
  "Contents": " 101:\t\tstruct bpf_crypto_ctx *ctx;\n 102:\t\tint err;\n 103:\t\n 104:\t\tif (!params || params-\u003ereserved[0] || params-\u003ereserved[1] ||\n 105:\t\t    params__sz != sizeof(struct bpf_crypto_params)) {\n 106:\t\t\t*err_ret = -EINVAL;\n 107:\t\t\treturn NULL;\n 108:\t\t}\n 109:\t\n 110:\t\tif (!params-\u003ekey_len || params-\u003ekey_len \u003e sizeof(params-\u003ekey)) {\n 111:\t\t\t*err_ret = -EINVAL;\n 112:\t\t\treturn NULL;\n 113:\t\t}\n 114:\t\n 115:\t\tctx = kzalloc_obj(*ctx);\n 116:\t\tif (!ctx) {\n 117:\t\t\t*err_ret = -ENOMEM;\n 118:\t\t\treturn NULL;\n 119:\t\t}\n 120:\t\n 121:\t\tif (!bpf_crypto_find_algo(params, \u0026ctx-\u003ealgo)) {\n 122:\t\t\terr = -EOPNOTSUPP;\n 123:\t\t\tgoto out;\n 124:\t\t}\n 125:\t\n 126:\t\tswitch (ctx-\u003ealgo) {\n 127:\t\tcase BPF_ALGO_AES_CBC:\n 128:\t\tcase BPF_ALGO_AES_ECB:\n 129:\t\t\tif (params-\u003eauthsize) {\n 130:\t\t\t\terr = -EOPNOTSUPP;\n 131:\t\t\t\tgoto out;\n 132:\t\t\t}\n 133:\t\t\tctx-\u003ekey = kzalloc_obj(struct aes_key);\n 134:\t\t\tif (!ctx-\u003ekey) {\n 135:\t\t\t\terr = -ENOMEM;\n 136:\t\t\t\tgoto out;\n 137:\t\t\t}\n 138:\t\t\terr = aes_preparekey((struct aes_key *)ctx-\u003ekey, params-\u003ekey,\n 139:\t\t\t\t\t     params-\u003ekey_len);\n 140:\t\t\tbreak;\n 141:\t\tdefault:\n 142:\t\t\tWARN_ON_ONCE(1);\n 143:\t\t\terr = -EOPNOTSUPP;\n 144:\t\t\tbreak;\n 145:\t\t}\n 146:\t\n 147:\tout:\n 148:\t\tif (err) {\n 149:\t\t\tkfree_sensitive(ctx-\u003ekey);\n 150:\t\t\tkfree_sensitive(ctx);\n 151:\t\t\t*err_ret = err;\n 152:\t\t\treturn NULL;\n 153:\t\t}\n 154:\t\trefcount_set(\u0026ctx-\u003eusage, 1);\n 155:\t\t*err_ret = 0;\n 156:\t\treturn ctx;\n 157:\t}\n 158:\t\n 159:\tstatic void crypto_free_cb(struct rcu_head *head)\n 160:\t{\n 161:\t\tstruct bpf_crypto_ctx *ctx;\n 162:\t\n 163:\t\tctx = container_of(head, struct bpf_crypto_ctx, rcu);\n 164:\t\tkfree_sensitive(ctx-\u003ekey);\n 165:\t\tkfree_sensitive(ctx);\n 166:\t}\n 167:\t\n 168:\t/**\n 169:\t * bpf_crypto_ctx_acquire() - Acquire a reference to a BPF crypto context.\n 170:\t * @ctx: The BPF crypto context being acquired. The ctx must be a trusted\n 171:\t *\t     pointer.\n 172:\t *\n 173:\t * Acquires a reference to a BPF crypto context. The context returned by this function\n 174:\t * must either be embedded in a map as a kptr, or freed with\n 175:\t * bpf_crypto_ctx_release().\n 176:\t */\n 177:\t__bpf_kfunc struct bpf_crypto_ctx *\n 178:\tbpf_crypto_ctx_acquire(struct bpf_crypto_ctx *ctx)\n 179:\t{\n 180:\t\tif (!refcount_inc_not_zero(\u0026ctx-\u003eusage))\n 181:\t\t\treturn NULL;\n 182:\t\treturn ctx;\n 183:\t}\n 184:\t\n 185:\t/**\n 186:\t * bpf_crypto_ctx_release() - Release a previously acquired BPF crypto context.\n 187:\t * @ctx: The crypto context being released.\n 188:\t *\n 189:\t * Releases a previously acquired reference to a BPF crypto context. When the final\n 190:\t * reference of the BPF crypto context has been released, its memory\n 191:\t * will be released.\n 192:\t */\n 193:\t__bpf_kfunc void bpf_crypto_ctx_release(struct bpf_crypto_ctx *ctx)\n 194:\t{\n 195:\t\tif (refcount_dec_and_test(\u0026ctx-\u003eusage))\n 196:\t\t\tcall_rcu(\u0026ctx-\u003ercu, crypto_free_cb);\n 197:\t}\n 198:\t\n 199:\t__bpf_kfunc void bpf_crypto_ctx_release_dtor(void *ctx)\n 200:\t{\n"
}

29/3 2026/09/24 05:38 llm kmsan-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 20373
							output: 37
							thoughts: 26
						

Thoughts:
Okay, let's dive into the remaining sections of `kernel/bpf/crypto.c`, specifically from line 200 onward. I'm keen to see how the code handles the cryptographic aspects within the BPF context. I expect to find the implementation details of the cryptographic functions available for BPF programs. I'm looking for the underlying mechanisms to see how these functions interact with the kernel's crypto APIs, and the potential security implications. I'll focus on how data is handled and potential performance bottlenecks. I'll also try and find the core logic that defines the function calls available to BPF programs. Specifically, I'm interested in how keys are managed, how the code handles side channel attacks, and what optimizations are in place. The goal is to fully understand the crypto operations available for use in BPF, from the very core of its interaction.



30/3 2026/09/24 05:38 tool read-file
0m Args:
{
  "File": "kernel/bpf/crypto.c",
  "FirstLine": 201,
  "LineCount": 100
}

Results:
{
  "Contents": " 201:\t\tbpf_crypto_ctx_release(ctx);\n 202:\t}\n 203:\tCFI_NOSEAL(bpf_crypto_ctx_release_dtor);\n 204:\t\n 205:\tstatic int bpf_aes_cbc_crypt(u8 *dst, u32 dst_len, const u8 *src, u32 src_len,\n 206:\t\t\t\t     u8 *iv, u32 iv_len, const struct aes_key *key,\n 207:\t\t\t\t     bool decrypt)\n 208:\t{\n 209:\t\tif (iv_len != AES_BLOCK_SIZE)\n 210:\t\t\treturn -EINVAL;\n 211:\t\tif (src_len % AES_BLOCK_SIZE || dst_len \u003c src_len)\n 212:\t\t\treturn -EINVAL;\n 213:\t\tif (decrypt)\n 214:\t\t\taes_cbc_decrypt(dst, src, src_len, iv, key);\n 215:\t\telse\n 216:\t\t\taes_cbc_encrypt(dst, src, src_len, iv, key);\n 217:\t\treturn 0;\n 218:\t}\n 219:\t\n 220:\tstatic int bpf_aes_ecb_crypt(u8 *dst, u32 dst_len, const u8 *src, u32 src_len,\n 221:\t\t\t\t     u8 *iv, u32 iv_len, const struct aes_key *key,\n 222:\t\t\t\t     bool decrypt)\n 223:\t{\n 224:\t\tif (iv_len != 0)\n 225:\t\t\treturn -EINVAL;\n 226:\t\tif (src_len % AES_BLOCK_SIZE || dst_len \u003c src_len)\n 227:\t\t\treturn -EINVAL;\n 228:\t\tif (decrypt)\n 229:\t\t\taes_ecb_decrypt(dst, src, src_len, key);\n 230:\t\telse\n 231:\t\t\taes_ecb_encrypt(dst, src, src_len, key);\n 232:\t\treturn 0;\n 233:\t}\n 234:\t\n 235:\tstatic int bpf_crypto_crypt(const struct bpf_crypto_ctx *ctx,\n 236:\t\t\t\t    const struct bpf_dynptr_kern *src,\n 237:\t\t\t\t    const struct bpf_dynptr_kern *dst,\n 238:\t\t\t\t    const struct bpf_dynptr_kern *iv,\n 239:\t\t\t\t    bool decrypt)\n 240:\t{\n 241:\t\tu32 src_len, dst_len, iv_len;\n 242:\t\tconst u8 *psrc;\n 243:\t\tu8 *pdst, *piv;\n 244:\t\n 245:\t\tif (__bpf_dynptr_is_rdonly(dst))\n 246:\t\t\treturn -EINVAL;\n 247:\t\n 248:\t\tiv_len = iv ? __bpf_dynptr_size(iv) : 0;\n 249:\t\tsrc_len = __bpf_dynptr_size(src);\n 250:\t\tdst_len = __bpf_dynptr_size(dst);\n 251:\t\tif (!src_len || !dst_len)\n 252:\t\t\treturn -EINVAL;\n 253:\t\n 254:\t\tpsrc = __bpf_dynptr_data(src, src_len);\n 255:\t\tif (!psrc)\n 256:\t\t\treturn -EINVAL;\n 257:\t\tpdst = __bpf_dynptr_data_rw(dst, dst_len);\n 258:\t\tif (!pdst)\n 259:\t\t\treturn -EINVAL;\n 260:\t\n 261:\t\tpiv = iv_len ? __bpf_dynptr_data_rw(iv, iv_len) : NULL;\n 262:\t\tif (iv_len \u0026\u0026 !piv)\n 263:\t\t\treturn -EINVAL;\n 264:\t\n 265:\t\tswitch (ctx-\u003ealgo) {\n 266:\t\tcase BPF_ALGO_AES_CBC:\n 267:\t\t\treturn bpf_aes_cbc_crypt(pdst, dst_len, psrc, src_len, piv,\n 268:\t\t\t\t\t\t iv_len, ctx-\u003ekey, decrypt);\n 269:\t\tcase BPF_ALGO_AES_ECB:\n 270:\t\t\treturn bpf_aes_ecb_crypt(pdst, dst_len, psrc, src_len, piv,\n 271:\t\t\t\t\t\t iv_len, ctx-\u003ekey, decrypt);\n 272:\t\tdefault:\n 273:\t\t\treturn -EINVAL;\n 274:\t\t}\n 275:\t}\n 276:\t\n 277:\t/**\n 278:\t * bpf_crypto_decrypt() - Decrypt buffer using configured context and IV provided.\n 279:\t * @ctx:\t\tThe crypto context being used. The ctx must be a trusted pointer.\n 280:\t * @src:\t\tbpf_dynptr to the encrypted data. Must be a trusted pointer.\n 281:\t * @dst:\t\tbpf_dynptr to the buffer where to store the result. Must be a trusted pointer.\n 282:\t * @iv__nullable:\tbpf_dynptr to the initialization vector. May be NULL.\n 283:\t *\n 284:\t * Decrypts provided buffer using IV data and the crypto context. Crypto context must be configured.\n 285:\t */\n 286:\t__bpf_kfunc int bpf_crypto_decrypt(struct bpf_crypto_ctx *ctx,\n 287:\t\t\t\t\t   const struct bpf_dynptr *src,\n 288:\t\t\t\t\t   const struct bpf_dynptr *dst,\n 289:\t\t\t\t\t   const struct bpf_dynptr *iv__nullable)\n 290:\t{\n 291:\t\tconst struct bpf_dynptr_kern *src_kern = (struct bpf_dynptr_kern *)src;\n 292:\t\tconst struct bpf_dynptr_kern *dst_kern = (struct bpf_dynptr_kern *)dst;\n 293:\t\tconst struct bpf_dynptr_kern *iv_kern = (struct bpf_dynptr_kern *)iv__nullable;\n 294:\t\n 295:\t\treturn bpf_crypto_crypt(ctx, src_kern, dst_kern, iv_kern, true);\n 296:\t}\n 297:\t\n 298:\t/**\n 299:\t * bpf_crypto_encrypt() - Encrypt buffer using configured context and IV provided.\n 300:\t * @ctx:\t\tThe crypto context being used. The ctx must be a trusted pointer.\n"
}

31/3 2026/09/24 05:38 llm kmsan-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 22384
							output: 36
							thoughts: 13
						

Thoughts:
Okay, here's my interpretation of that thought process, tailored for an expert audience, and written in the first person:

**Navigating `crypto.c` - The Next Move**

Alright, time to dive back into `crypto.c`.  I'm curious to see what the next logical operation or function call is within this file. Given my understanding of the larger system architecture and the existing code within `crypto.c`, I'm hoping to identify the critical path and anticipate the flow. I'm looking for the immediate next logical operation, perhaps a call to a lower-level primitive, a state transition, or a data transformation within this cryptographic context. What piece of the puzzle does this code represent and how does it fit into the broader narrative? This next step is going to be key, and I’m ready to dissect it.



32/3 2026/09/24 05:38 tool read-file
0m Args:
{
  "File": "kernel/bpf/crypto.c",
  "FirstLine": 301,
  "LineCount": 50
}

Results:
{
  "Contents": " 301:\t * @src:\t\tbpf_dynptr to the plain data. Must be a trusted pointer.\n 302:\t * @dst:\t\tbpf_dynptr to the buffer where to store the result. Must be a trusted pointer.\n 303:\t * @iv__nullable:\tbpf_dynptr to the initialization vector. May be NULL.\n 304:\t *\n 305:\t * Encrypts provided buffer using IV data and the crypto context. Crypto context must be configured.\n 306:\t */\n 307:\t__bpf_kfunc int bpf_crypto_encrypt(struct bpf_crypto_ctx *ctx,\n 308:\t\t\t\t\t   const struct bpf_dynptr *src,\n 309:\t\t\t\t\t   const struct bpf_dynptr *dst,\n 310:\t\t\t\t\t   const struct bpf_dynptr *iv__nullable)\n 311:\t{\n 312:\t\tconst struct bpf_dynptr_kern *src_kern = (struct bpf_dynptr_kern *)src;\n 313:\t\tconst struct bpf_dynptr_kern *dst_kern = (struct bpf_dynptr_kern *)dst;\n 314:\t\tconst struct bpf_dynptr_kern *iv_kern = (struct bpf_dynptr_kern *)iv__nullable;\n 315:\t\n 316:\t\treturn bpf_crypto_crypt(ctx, src_kern, dst_kern, iv_kern, false);\n 317:\t}\n 318:\t\n 319:\t__bpf_kfunc_end_defs();\n 320:\t\n 321:\tBTF_KFUNCS_START(crypt_init_kfunc_btf_ids)\n 322:\tBTF_ID_FLAGS(func, bpf_crypto_ctx_create, KF_ACQUIRE | KF_RET_NULL | KF_SLEEPABLE)\n 323:\tBTF_ID_FLAGS(func, bpf_crypto_ctx_release, KF_RELEASE)\n 324:\tBTF_ID_FLAGS(func, bpf_crypto_ctx_acquire, KF_ACQUIRE | KF_RCU | KF_RET_NULL)\n 325:\tBTF_KFUNCS_END(crypt_init_kfunc_btf_ids)\n 326:\t\n 327:\tstatic const struct btf_kfunc_id_set crypt_init_kfunc_set = {\n 328:\t\t.owner = THIS_MODULE,\n 329:\t\t.set   = \u0026crypt_init_kfunc_btf_ids,\n 330:\t};\n 331:\t\n 332:\tBTF_KFUNCS_START(crypt_kfunc_btf_ids)\n 333:\tBTF_ID_FLAGS(func, bpf_crypto_decrypt, KF_RCU)\n 334:\tBTF_ID_FLAGS(func, bpf_crypto_encrypt, KF_RCU)\n 335:\tBTF_KFUNCS_END(crypt_kfunc_btf_ids)\n 336:\t\n 337:\tstatic const struct btf_kfunc_id_set crypt_kfunc_set = {\n 338:\t\t.owner = THIS_MODULE,\n 339:\t\t.set   = \u0026crypt_kfunc_btf_ids,\n 340:\t};\n 341:\t\n 342:\tBTF_ID_LIST(bpf_crypto_dtor_ids)\n 343:\tBTF_ID(struct, bpf_crypto_ctx)\n 344:\tBTF_ID(func, bpf_crypto_ctx_release_dtor)\n 345:\t\n 346:\tstatic int __init crypto_kfunc_init(void)\n 347:\t{\n 348:\t\tint ret;\n 349:\t\tconst struct btf_id_dtor_kfunc bpf_crypto_dtors[] = {\n 350:\t\t\t{\n"
}

33/3 2026/09/24 05:38 llm kmsan-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 23632
							output: 20
							thoughts: 1045
						

Thoughts:
**Analyzing the BPF Crypto Subsystem Refactor**

Okay, let's break this down. This patch is a significant refactor of the BPF crypto subsystem. I see they're moving away from the kernel's crypto framework, specifically the `struct bpf_crypto_type` and `bpf_crypto_register_type` mechanisms, along with the `crypto_alloc_lskcipher` calls, in favor of direct calls to AES-CBC and AES-ECB library routines: `aes_cbc_encrypt`, `aes_cbc_decrypt`, `aes_ecb_encrypt`, and `aes_ecb_decrypt`. That's interesting; it likely gives them more control and potentially better performance, or at least a more streamlined implementation within the BPF context.

The memory allocation strategy is now using `kzalloc_obj` for `ctx` and `ctx->key`. This is good; we have guaranteed zero-initialization, which avoids potential issues with uninitialized memory. And the use of `kfree_sensitive` on destruction shows they are thinking about security. Always a good sign.

In `bpf_crypto_ctx_create`, they're meticulously setting `*err_ret` on all paths, both success and failure.  It's a subtle but important detail for ensuring error handling is consistent.

Moving on to `bpf_aes_cbc_crypt` and `bpf_aes_ecb_crypt`, I notice thorough input validation: `iv_len` is being checked against the expected `AES_BLOCK_SIZE` or zero, and they're verifying the block size alignment for `src_len`.  The `dst_len < src_len` check is crucial to prevent buffer overflows. They return 0 on success, which is standard, and a negative error code on failure. All expected behavior.

Now, let's dig into `bpf_crypto_crypt`.  They're validating `src_len`, `dst_len`, and `iv_len`, and using `__bpf_dynptr_data` and `__bpf_dynptr_data_rw` to get raw pointers.  No new uninitialized stack variables, no uninitialized struct fields. This is all looking pretty solid, but let's check for info leaks and potential issues. Crucially, this is a BPF kfunc, so there's no direct copy_to_user or info leak from this code. The data is encrypted or decrypted between BPF dynptrs.

The question that remains is: Does it leave uninitialized bytes in the `dst`?  Let's circle back to that `dst_len < src_len` check. Since this is validated,  and `src_len` bytes are written to `dst` when `aes_cbc_encrypt` is called, there is no potential for uninitialized memory being leaked. The old code had a similar `src_len > dst_len` check and passed `src_len` to the encryption routine, which also writes only up to the `src_len` bytes.

However, the bigger question I am trying to answer is the dynptr initialization. Are the destination buffers properly initialized before being passed into the crypto routines? In BPF, dynptrs are initialized by the BPF program or the verifier in other places. Think ringbuf, local stack, skb, etc.. The dynptr memory is managed by BPF, which means we can be sure memory is not uninitialized at this point.

Ok, now, let's check the declaration and implementation of `aes_cbc_encrypt`.

This all seems very well-thought-out, security-conscious, and the data flow looks sound.



34/3 2026/09/24 05:38 tool grepper
0m Args:
{
  "Expression": "void aes_cbc_encrypt"
}

Results:
{
  "Output": "arch/riscv/crypto/aes-riscv64-glue.c=27=asmlinkage void aes_ecb_decrypt_zvkned(const struct crypto_aes_ctx *key,\n--\narch/riscv/crypto/aes-riscv64-glue.c-29-\narch/riscv/crypto/aes-riscv64-glue.c:30:asmlinkage void aes_cbc_encrypt_zvkned(const struct crypto_aes_ctx *key,\narch/riscv/crypto/aes-riscv64-glue.c-31-\t\t\t\t       const u8 *in, u8 *out, size_t len,\n--\narch/riscv/crypto/aes-riscv64-zvkned.S=96=SYM_FUNC_END(aes_ecb_decrypt_zvkned)\n--\narch/riscv/crypto/aes-riscv64-zvkned.S-137-\narch/riscv/crypto/aes-riscv64-zvkned.S:138:// void aes_cbc_encrypt_zvkned(const struct crypto_aes_ctx *key,\narch/riscv/crypto/aes-riscv64-zvkned.S-139-//\t\t\t       const u8 *in, u8 *out, size_t len, u8 iv[16]);\n--\ninclude/crypto/aes-cbc.h-26- */\ninclude/crypto/aes-cbc.h:27:void aes_cbc_encrypt(u8 *dst, const u8 *src, size_t len,\ninclude/crypto/aes-cbc.h-28-\t\t     u8 iv[at_least AES_BLOCK_SIZE], aes_encrypt_arg key);\n--\nlib/crypto/aes.c=853=static bool aes_cbc_decrypt_arch(u8 *dst, const u8 *src, size_t len,\n--\nlib/crypto/aes.c-860-\nlib/crypto/aes.c:861:void aes_cbc_encrypt(u8 *dst, const u8 *src, size_t len, u8 iv[AES_BLOCK_SIZE],\nlib/crypto/aes.c-862-\t\t     aes_encrypt_arg key)\n"
}

35/3 2026/09/24 05:38 llm kmsan-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 26036
							output: 35
							thoughts: 20
						

Thoughts:
Here's my take on the thought process, presented as a first-person summary suitable for an expert:

**Investigating a Suspect Function Call**

Alright, I need to zero in on a specific area within the crypto library. My gut feeling is telling me there might be an issue, or perhaps just something interesting, around the Advanced Encryption Standard (AES) implementation. Specifically, I need to check out `lib/crypto/aes.c`. I'm going to jump straight to line 861. This line or the lines surrounding it could be the place where the key scheduling happens, or maybe the actual encryption/decryption loop itself. There could be an off-by-one error, an improper memory access, or some kind of unexpected behavior related to padding or key handling. Let's see what's actually going on in that section. Time to dig in.



36/3 2026/09/24 05:39 tool read-file
0m Args:
{
  "File": "lib/crypto/aes.c",
  "FirstLine": 860,
  "LineCount": 40
}

Results:
{
  "Contents": " 860:\t\n 861:\tvoid aes_cbc_encrypt(u8 *dst, const u8 *src, size_t len, u8 iv[AES_BLOCK_SIZE],\n 862:\t\t\t     aes_encrypt_arg key)\n 863:\t{\n 864:\t\tconst u8 *prev = iv;\n 865:\t\n 866:\t\tif (WARN_ON_ONCE(len % AES_BLOCK_SIZE))\n 867:\t\t\tlen = round_down(len, AES_BLOCK_SIZE);\n 868:\t\n 869:\t\tif (unlikely(len == 0))\n 870:\t\t\treturn;\n 871:\t\n 872:\t\tif (likely(aes_cbc_encrypt_arch(dst, src, len, iv, key.enc_key)))\n 873:\t\t\treturn;\n 874:\t\n 875:\t\tdo {\n 876:\t\t\tcrypto_xor_cpy(dst, src, prev, AES_BLOCK_SIZE);\n 877:\t\t\taes_encrypt(key, dst, dst);\n 878:\t\t\tprev = dst;\n 879:\t\t\tdst += AES_BLOCK_SIZE;\n 880:\t\t\tsrc += AES_BLOCK_SIZE;\n 881:\t\t\tlen -= AES_BLOCK_SIZE;\n 882:\t\t} while (len);\n 883:\t\tmemcpy(iv, prev, AES_BLOCK_SIZE);\n 884:\t}\n 885:\tEXPORT_SYMBOL_GPL(aes_cbc_encrypt);\n 886:\t\n 887:\tvoid aes_cbc_decrypt(u8 *dst, const u8 *src, size_t len, u8 iv[AES_BLOCK_SIZE],\n 888:\t\t\t     const struct aes_key *key)\n 889:\t{\n 890:\t\tu8 next_iv[AES_BLOCK_SIZE];\n 891:\t\n 892:\t\tif (WARN_ON_ONCE(len % AES_BLOCK_SIZE))\n 893:\t\t\tlen = round_down(len, AES_BLOCK_SIZE);\n 894:\t\n 895:\t\tif (unlikely(len == 0))\n 896:\t\t\treturn;\n 897:\t\n 898:\t\tif (likely(aes_cbc_decrypt_arch(dst, src, len, iv, key)))\n 899:\t\t\treturn;\n"
}

37/3 2026/09/24 05:39 llm kmsan-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 26900
							output: 21
							thoughts: 22
						

Thoughts:
Okay, let's think this through. I need to make sure I haven't missed anything in this analysis. 

**Reviewing the AES ECB Functions**

Hmm, I should probably also check the `aes_ecb_encrypt` and `aes_ecb_decrypt` functions to ensure they're behaving as expected. It's easy to focus on more complex modes, but ECB can be a weak point if not properly implemented and understood. A quick look at those will provide a good sanity check. I want to be certain the basic building blocks are sound, and that I haven't overlooked any edge cases related to block alignment or key handling within those simpler functions. It's all about due diligence and ensuring nothing slipped through.



38/3 2026/09/24 05:39 tool grepper
0m Args:
{
  "Expression": "void aes_ecb_encrypt"
}

Results:
{
  "Output": "arch/riscv/crypto/aes-riscv64-glue.c-24-\narch/riscv/crypto/aes-riscv64-glue.c:25:asmlinkage void aes_ecb_encrypt_zvkned(const struct crypto_aes_ctx *key,\narch/riscv/crypto/aes-riscv64-glue.c-26-\t\t\t\t       const u8 *in, u8 *out, size_t len);\n--\narch/riscv/crypto/aes-riscv64-zvkned.S-84-\narch/riscv/crypto/aes-riscv64-zvkned.S:85:// void aes_ecb_encrypt_zvkned(const struct crypto_aes_ctx *key,\narch/riscv/crypto/aes-riscv64-zvkned.S-86-//\t\t\t       const u8 *in, u8 *out, size_t len);\n--\ninclude/crypto/aes-ecb.h-27- */\ninclude/crypto/aes-ecb.h:28:void aes_ecb_encrypt(u8 *dst, const u8 *src, size_t len, aes_encrypt_arg key);\ninclude/crypto/aes-ecb.h-29-\n--\nlib/crypto/aes.c=773=static bool aes_ecb_decrypt_arch(u8 *dst, const u8 *src, size_t len,\n--\nlib/crypto/aes.c-779-\nlib/crypto/aes.c:780:void aes_ecb_encrypt(u8 *dst, const u8 *src, size_t len, aes_encrypt_arg key)\nlib/crypto/aes.c-781-{\n"
}

39/3 2026/09/24 05:39 llm kmsan-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 27527
							output: 35
							thoughts: 20
						

Thoughts:
Here's a first-person summary, keeping in mind the audience is an expert:

**Digging into AES Implementation**

Okay, time to get my hands dirty. I need to take a look at the AES implementation, specifically within `lib/crypto/aes.c`. I'm going to jump straight to around line 780 – that's likely where the key scheduling or possibly the initial rounds are handled, given the typical structure of these AES libraries. I'm expecting to see the core transformations and state manipulations happening around that area. Let's see if this is where the interesting bits are, or if it has been refactored at this point.



40/3 2026/09/24 05:39 tool read-file
0m Args:
{
  "File": "lib/crypto/aes.c",
  "FirstLine": 780,
  "LineCount": 35
}

Results:
{
  "Contents": " 780:\tvoid aes_ecb_encrypt(u8 *dst, const u8 *src, size_t len, aes_encrypt_arg key)\n 781:\t{\n 782:\t\tif (WARN_ON_ONCE(len % AES_BLOCK_SIZE))\n 783:\t\t\tlen = round_down(len, AES_BLOCK_SIZE);\n 784:\t\n 785:\t\tif (unlikely(len == 0))\n 786:\t\t\treturn;\n 787:\t\n 788:\t\tif (likely(aes_ecb_encrypt_arch(dst, src, len, key.enc_key)))\n 789:\t\t\treturn;\n 790:\t\n 791:\t\tfor (size_t i = 0; i \u003c len; i += AES_BLOCK_SIZE)\n 792:\t\t\taes_encrypt(key, \u0026dst[i], \u0026src[i]);\n 793:\t}\n 794:\tEXPORT_SYMBOL_GPL(aes_ecb_encrypt);\n 795:\t\n 796:\tvoid aes_ecb_decrypt(u8 *dst, const u8 *src, size_t len,\n 797:\t\t\t     const struct aes_key *key)\n 798:\t{\n 799:\t\tif (WARN_ON_ONCE(len % AES_BLOCK_SIZE))\n 800:\t\t\tlen = round_down(len, AES_BLOCK_SIZE);\n 801:\t\n 802:\t\tif (unlikely(len == 0))\n 803:\t\t\treturn;\n 804:\t\n 805:\t\tif (likely(aes_ecb_decrypt_arch(dst, src, len, key)))\n 806:\t\t\treturn;\n 807:\t\n 808:\t\tfor (size_t i = 0; i \u003c len; i += AES_BLOCK_SIZE)\n 809:\t\t\taes_decrypt(key, \u0026dst[i], \u0026src[i]);\n 810:\t}\n 811:\tEXPORT_SYMBOL_GPL(aes_ecb_decrypt);\n 812:\t\n 813:\t/* FIPS cryptographic algorithm self-test for AES-ECB */\n 814:\tstatic void __init aes_ecb_fips_test(void)\n"
}

41/3 2026/09/24 05:39 llm kmsan-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 28319
							output: 335
							thoughts: 1128
						

Thoughts:
**KMSAN Assessment of BPF Crypto Refactor**

Okay, let's break this down. My goal is to determine if this refactoring of the BPF crypto support, specifically the shift from the generic skcipher API and dynamic registration to direct library routines like `aes_cbc_*` and `aes_ecb_*`, introduces any risks that necessitate a KMSAN fuzzing session. I need to be precise, because running a KMSAN session is costly.

First, I've gone through the code, meticulously checking against the KMSAN criteria. 

1.  **Kernel-to-User Space Leaks:** There is no direct copying of kernel structures to user space. These are BPF kfuncs. This is good.
2.  **Conditional Logic & Uninitialized Variables:** I've carefully traced the initialization and usage of variables, particularly within `bpf_crypto_ctx_create`. Specifically, the code uses `kzalloc_obj`, so both `ctx` and `ctx->key` are zeroed upon allocation. The error code `err` is also set on all execution paths. This is also good. There is no usage of uninitialized variables in any conditional logic.
3.  **Complex Data Structure Initialization:** All complex data structures, specifically `ctx` and `ctx->key`, are allocated using `kzalloc_obj`, therefore, all fields are zeroed. This is great.
4.  **Bounds Checks and Potential for Uninitialized Byte Access:** The code in `bpf_aes_cbc_crypt` and `bpf_aes_ecb_crypt` has very explicit bounds checks on input buffer lengths and IV lengths. It strictly adheres to `src_len` and `dst_len` bounds. They only read and write valid data. No uninitialized memory is read or written. This is excellent.

I then went through a critical comparison between KASAN and KMSAN. KASAN is the workhorse for detecting out-of-bounds accesses, use-after-free, etc. KMSAN is the specialized tool that catches reads of *uninitialized* memory and kernel-to-user memory info-leaks. Given my analysis, it seems like any potential errors would be logic errors, refcounting issues, or memory bugs that KASAN or standard assertions would cover.

Therefore, my conclusion is that `NeedsKMSAN` should be set to `false`.

I can now formulate the reasoning for the `KMSANReasoning` field:

*   The patch refactors the BPF crypto support from the generic skcipher crypto API and dynamic registration framework, to direct library routines (`aes_cbc_*`, `aes_ecb_*`).
*   The `bpf_crypto_ctx` and `struct aes_key` are allocated using `kzalloc_obj`, ensuring all heap memory is zero-initialized.
*   Error codes (`err` and `*err_ret`) are explicitly initialized on all control paths; no conditional branching relies on uninitialized data.
*   The crypt routines perform explicit buffer length bounds checks (`dst_len < src_len`, `src_len % AES_BLOCK_SIZE`, and `iv_len` requirements) and only read and write valid data lengths without exposing uninitialized buffer regions.
*   Any potential errors in refcounting, dynamic pointer management, or buffer bounds are covered by KASAN, with no uninitialized memory reads or kernel info-leaks present. Therefore, no KMSAN fuzzing is needed.

I'm confident in this assessment. Time to call the `set-results` tool and move on.



42/3 2026/09/24 05:39 tool set-results
0m Args:
{
  "KMSANReasoning": "The patch refactors the BPF crypto subsystem, eliminating the modular dynamic crypto type registration in favor of direct calls to in-kernel AES-CBC and AES-ECB library routines (crypto_lib_aes_*). \n\nFrom a KMSAN perspective:\n1. Heap allocations: Both `struct bpf_crypto_ctx` and `struct aes_key` are allocated via `kzalloc_obj()`, ensuring all fields are zero-initialized upon creation.\n2. Control flow \u0026 branching: The error code variables (`err` and `*err_ret`) are initialized on every execution path before any branching or returning.\n3. Buffer operations: `bpf_aes_cbc_crypt()` and `bpf_aes_ecb_crypt()` enforce strict length and alignment checks (`iv_len`, `src_len % AES_BLOCK_SIZE`, and `dst_len \u003c src_len`), ensuring that `aes_cbc_*` and `aes_ecb_*` only process valid bytes within the bounds of the provided BPF dynptr buffers.\n4. User space info-leaks: No structures, padding, or uninitialized buffers are copied to user space.\n\nAny memory issues that could arise (such as refcounting bugs, use-after-free, or out-of-bounds buffer indexing) fall under the purview of KASAN and standard kernel debuggers. There are no uninitialized memory read risks or info-leaks that specifically require KMSAN detection.",
  "NeedsKMSAN": false
}

Results:
{
  "KMSANReasoning": "The patch refactors the BPF crypto subsystem, eliminating the modular dynamic crypto type registration in favor of direct calls to in-kernel AES-CBC and AES-ECB library routines (crypto_lib_aes_*). \n\nFrom a KMSAN perspective:\n1. Heap allocations: Both `struct bpf_crypto_ctx` and `struct aes_key` are allocated via `kzalloc_obj()`, ensuring all fields are zero-initialized upon creation.\n2. Control flow \u0026 branching: The error code variables (`err` and `*err_ret`) are initialized on every execution path before any branching or returning.\n3. Buffer operations: `bpf_aes_cbc_crypt()` and `bpf_aes_ecb_crypt()` enforce strict length and alignment checks (`iv_len`, `src_len % AES_BLOCK_SIZE`, and `dst_len \u003c src_len`), ensuring that `aes_cbc_*` and `aes_ecb_*` only process valid bytes within the bounds of the provided BPF dynptr buffers.\n4. User space info-leaks: No structures, padding, or uninitialized buffers are copied to user space.\n\nAny memory issues that could arise (such as refcounting bugs, use-after-free, or out-of-bounds buffer indexing) fall under the purview of KASAN and standard kernel debuggers. There are no uninitialized memory read risks or info-leaks that specifically require KMSAN detection.",
  "NeedsKMSAN": false
}

LLM Calls Summary:
Total Calls Total Tokens Avg Tokens Total Duration (Seconds) Avg Duration (Seconds)
Tool Calls Summary:
Total Calls Total Duration (Seconds) Avg Duration (Seconds)