AI Trajectory


Trajectory: (Expand/Collapse All)
Seq Timestamp Type Name Duration
0/0 2026/09/25 08:16 flow patch-triage
4m Args:
null

Results:
{
  "EnableConfigs": [
    "BPF_SYSCALL",
    "BPF_CRYPTO",
    "CRYPTO_AES",
    "CRYPTO_CBC",
    "CRYPTO_ECB",
    "CRYPTO_LIB_AES_CBC",
    "CRYPTO_LIB_AES_ECB"
  ],
  "FocusSymbols": [
    "bpf_crypto_ctx_create",
    "bpf_crypto_encrypt",
    "bpf_crypto_decrypt",
    "bpf_crypto_crypt",
    "bpf_aes_cbc_crypt",
    "bpf_aes_ecb_crypt"
  ],
  "KMSANReasoning": "The patch refactors BPF crypto support by replacing the dynamic skcipher registration mechanism (using crypto_alloc_lskcipher) with direct calls to AES library routines (AES-CBC and AES-ECB via crypto/aes-cbc.h and crypto/aes-ecb.h).\n\nEvaluating KMSAN vs KASAN applicability:\n1. Heap Allocation \u0026 Initialization: The context structure `bpf_crypto_ctx` is allocated using `kzalloc_obj(*ctx)` which completely zeroes all memory. Key setup is performed via `aes_preparekey()`, and the context is freed via `kfree_sensitive()`.\n2. Stack \u0026 Local Variables: All local variables in `bpf_crypto_ctx_create()`, `bpf_aes_cbc_crypt()`, `bpf_aes_ecb_crypt()`, and `bpf_crypto_crypt()` are explicitly initialized before branching or reading. The return error code pointer `err_ret` is populated on all code paths.\n3. No User-Space Info Leaks: The BPF kfuncs operate internally on BPF dynptr buffers and context pointers within the kernel. There are no data structures, socket buffers, netlink attributes, or ioctls copied to user space.\n4. Bounds Checking vs Out-of-Bounds: The buffers accessed (`src`, `dst`, `iv`) are bounded by explicit length checks (`src_len % AES_BLOCK_SIZE`, `dst_len \u003c src_len`, and `iv_len == AES_BLOCK_SIZE` or `iv_len == 0`). Any potential bounds issues or lifetime/refcounting errors would be detected by KASAN, not KMSAN.\n\nTherefore, the changes do not introduce or expose uninitialized memory access risks or info-leaks, and a dedicated KMSAN session is not needed.",
  "NeedsKMSAN": false,
  "Reasoning": "The patch refactors BPF crypto support by removing the skcipher dynamic registration layer and directly utilizing the AES-CBC and AES-ECB library routines. It alters context initialization, buffer size validations, memory management (using kfree_sensitive), and adds a new defensive WARN_ON_ONCE assertion. These changes modify reachable BPF subsystem kfuncs accessible from userspace.",
  "WorthFuzzing": true
}

1/1 2026/09/25 08:16 action read-patch-diff
0m Args:
null

Results:
{
  "PatchDiff": "commit 6839221c9b518d62ce33806fcb8c4b7daa59b85f\nAuthor: syz-cluster \u003ctriage@syzkaller.com\u003e\nDate:   Fri Sep 25 08:16:27 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..784e2bd5ca924 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,53 @@ 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:\tThe crypto key\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+\tunion {\n+\t\tstruct aes_key aes;\n+\t} 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 +85,69 @@ __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\telse\n+\t\t\terr = aes_preparekey(\u0026ctx-\u003ekey.aes, params-\u003ekey,\n+\t\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);\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 +155,7 @@ 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);\n }\n \n /**\n@@ -267,27 +195,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,\n+\t\t\t     const struct bpf_crypto_ctx *ctx, 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, \u0026ctx-\u003ekey.aes);\n+\telse\n+\t\taes_cbc_encrypt(dst, src, src_len, iv, \u0026ctx-\u003ekey.aes);\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,\n+\t\t\t     const struct bpf_crypto_ctx *ctx, 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, \u0026ctx-\u003ekey.aes);\n+\telse\n+\t\taes_ecb_encrypt(dst, src, src_len, \u0026ctx-\u003ekey.aes);\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 +251,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, 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, decrypt);\n+\tdefault:\n+\t\treturn -EINVAL;\n+\t}\n }\n \n /**\n@@ -312,20 +272,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 +293,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 a7c9e2d8965d5..e9c8f2444a8a0 100644\n--- a/kernel/bpf/verifier.c\n+++ b/kernel/bpf/verifier.c\n@@ -4669,7 +4669,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/25 08:16 agent patch-evaluator
0m Model:
core

Args:
null

Results:
{
  "EnableConfigs": [
    "BPF_SYSCALL",
    "BPF_CRYPTO",
    "CRYPTO_AES",
    "CRYPTO_CBC",
    "CRYPTO_ECB",
    "CRYPTO_LIB_AES_CBC",
    "CRYPTO_LIB_AES_ECB"
  ],
  "FocusSymbols": [
    "bpf_crypto_ctx_create",
    "bpf_crypto_encrypt",
    "bpf_crypto_decrypt",
    "bpf_crypto_crypt",
    "bpf_aes_cbc_crypt",
    "bpf_aes_ecb_crypt"
  ],
  "Reasoning": "The patch refactors BPF crypto support by removing the skcipher dynamic registration layer and directly utilizing the AES-CBC and AES-ECB library routines. It alters context initialization, buffer size validations, memory management (using kfree_sensitive), and adds a new defensive WARN_ON_ONCE assertion. These changes modify reachable BPF subsystem kfuncs accessible from userspace.",
  "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 6839221c9b518d62ce33806fcb8c4b7daa59b85f
Author: syz-cluster <triage@syzkaller.com>
Date:   Fri Sep 25 08:16:27 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..784e2bd5ca924 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,53 @@ 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:	The crypto key
  * @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;
+	union {
+		struct aes_key aes;
+	} 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 +85,69 @@ __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;
+		else
+			err = aes_preparekey(&ctx->key.aes, 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);
+		*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 +155,7 @@ 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);
 }
 
 /**
@@ -267,27 +195,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 bpf_crypto_ctx *ctx, 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, &ctx->key.aes);
+	else
+		aes_cbc_encrypt(dst, src, src_len, iv, &ctx->key.aes);
+	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 bpf_crypto_ctx *ctx, 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, &ctx->key.aes);
+	else
+		aes_ecb_encrypt(dst, src, src_len, &ctx->key.aes);
+	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 +251,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, decrypt);
+	case BPF_ALGO_AES_ECB:
+		return bpf_aes_ecb_crypt(pdst, dst_len, psrc, src_len, piv,
+					 iv_len, ctx, decrypt);
+	default:
+		return -EINVAL;
+	}
 }
 
 /**
@@ -312,20 +272,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 +293,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 a7c9e2d8965d5..e9c8f2444a8a0 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -4669,7 +4669,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/25 08:16 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 10637
							output: 34
							thoughts: 717
						

Thoughts:
**Fuzzing Evaluation: BPF Crypto Patch Series**

Okay, let's break this down. My primary goal is to determine if this kernel patch series is a good candidate for syzkaller fuzzing, specifically on the amd64 architecture. I need to apply a systematic approach to ensure I'm making a sound judgment based on reachability and impact.

First, I've got the triage philosophy in mind: Reachability is king! The code needs to be accessible through virtualized environments – think QEMU, GCE, virtio, syscalls... that's the primary avenue for me. I'm not blindly trusting claims, especially refactoring claims. Any new or modified assertions within reachable code are *definite* targets. And I'm avoiding the usual suspects for negative criteria: non-kernel changes, unreachable hardware, and other architectures. Core subsystems like mm, locking, BPF, scheduler, networking, VFS, syscalls, refactorings and assertions get special attention, along with any reachable drivers. I'll need to use `FocusSymbols` to find target kernel functions to focus fuzzing on, and identifying necessary `EnableConfigs` options through the Kconfig and `#ifdef` guards. I need to make sure to use `set-results` tool exactly once.

Now, let's dig into the patch itself.

It's touching `MAINTAINERS`, `crypto/Makefile`, and the main action seems to be happening in `kernel/bpf/`. There's a deleted `bpf_crypto_skcipher.c` and `linux/bpf_crypto.h`, which is usually a red flag. The patch is *adding* `CONFIG_BPF_CRYPTO` in `Kconfig`, and modifying `Makefile` to use that config instead of `CONFIG_CRYPTO` for crypto.o. This is a refactoring, but a substantial one. Looking at `crypto.c`, it's a *significant* rewrite.  They're ditching the dynamic `crypto_alloc_lskcipher` approach and going directly to `aes_cbc_encrypt`, `aes_cbc_decrypt`, `aes_ecb_encrypt`, and so on. It looks like they're simplifying the crypto part. There are also changes in `verifier.c`, replacing `CONFIG_CRYPTO` with `CONFIG_BPF_CRYPTO`, specifically in association with `BTF_ID(struct, bpf_crypto_ctx)`.

Is this reachable code? Absolutely. BPF is a core subsystem, completely user-accessible via the `bpf()` syscall. Importantly, BPF programs can call the relevant BPF kfuncs: `bpf_crypto_ctx_create`, `bpf_crypto_ctx_release`, `bpf_crypto_encrypt`, `bpf_crypto_decrypt`. This isn't just a minor tweak; it's a refactoring of *how* BPF crypto contexts are created, keys are set, and encryption/decryption works. Functions like `bpf_aes_cbc_crypt` and `bpf_crypto_crypt` have clearly changed.

And then I saw it: `WARN_ON_ONCE(1)` in the `bpf_crypto_ctx_create`! That's a huge flag!  Specifically, it's a `WARN_ON_ONCE(1)` in the *default* case of a switch statement based on the `ctx->algo` member. This is practically screaming for fuzzing. The changes to `bpf_crypto_ctx_create` and the `crypt` functions are substantial enough and the new assertion means `WorthFuzzing = true!` This is a clear target!

Now, let's dive deeper into the Kconfig dependencies and symbol identification using those tools. This looks like a solid candidate to fuzz!



4/2 2026/09/25 08:16 tool read-file
0m Args:
{
  "File": "kernel/bpf/Kconfig",
  "FirstLine": 90,
  "LineCount": 20
}

Results:
{
  "Contents": "  90:\t\t  affected hardware.\n  91:\t\n  92:\t\t  If you are unsure how to answer this question, answer Y.\n  93:\t\n  94:\tconfig BPF_CRYPTO\n  95:\t\tdef_bool y\n  96:\t\tdepends on BPF_SYSCALL\n  97:\t\tdepends on CRYPTO_LIB_AES_CBC\n  98:\t\tdepends on CRYPTO_LIB_AES_ECB\n  99:\t\thelp\n 100:\t\t  Provide the kfuncs needed for BPF programs to encrypt and decrypt\n 101:\t\t  data. The supported algorithms are AES-CBC and AES-ECB.\n 102:\t\n 103:\tsource \"kernel/bpf/preload/Kconfig\"\n 104:\t\n 105:\tconfig BPF_LSM\n 106:\t\tbool \"Enable BPF LSM Instrumentation\"\n 107:\t\tdepends on BPF_EVENTS\n 108:\t\tdepends on BPF_SYSCALL\n 109:\t\tdepends on SECURITY\n"
}

5/2 2026/09/25 08:16 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 12473
							output: 22
							thoughts: 30
						

Thoughts:
Okay, here's my take on those thoughts, tailored for an expert audience:

**Examining AES Implementation Options**

Alright, let's get down to brass tacks. I'm taking a look at how we're handling AES in the current configuration. Specifically, I'm zeroing in on `CRYPTO_LIB_AES_CBC` and `CRYPTO_LIB_AES_ECB` within the Kconfig system. I need to understand what's enabled, what's disabled, and how these modes are being treated within the build process. I'm mentally parsing the implications of each option; CBC versus ECB, their respective strengths and weaknesses concerning security and performance. I need to make sure we've got the most appropriate configurations for this project and the target platform. I'm probably thinking about whether there's any unnecessary code bloat, dependencies, or potential vulnerabilities introduced by these settings. Ultimately, I'm trying to ensure a robust, efficient, and secure AES implementation based on the current context.



6/2 2026/09/25 08:16 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"
}

7/2 2026/09/25 08:16 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 13430
							output: 33
							thoughts: 21
						

Thoughts:
Here's a summary of my thought process, tailored for an expert like you:

**Diving into the Cryptographic Configuration**

Okay, I need to get a handle on the cryptographic configuration. I'm going to head straight into the `lib/crypto/Kconfig` file, specifically targeting lines 30 through 65. That section is usually where they define the core cryptographic feature options or at least the framework for how they are implemented. It's likely I'll find the major top-level choices and maybe see some conditional dependencies. I'll need to rapidly scan for the relevant options. I want to see how the system is put together, looking for any immediate red flags or dependencies that might influence my next steps. I'm hoping to quickly grasp the overarching design and branching structure of these configurations. I'm already anticipating the typical challenges of Kconfig - trying to understand what is being conditionally enabled/disabled based on platform or other configuration choices. Time to dig in.



8/2 2026/09/25 08:16 tool read-file
0m Args:
{
  "File": "lib/crypto/Kconfig",
  "FirstLine": 30,
  "LineCount": 40
}

Results:
{
  "Contents": "  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"
}

9/2 2026/09/25 08:16 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 14199
							output: 27
							thoughts: 58
						

Thoughts:
Okay, here's my assessment of the situation:

**Investigating the Kconfig for CRYPTO_LIB_AES_* Options**

Hold on a second! I've just spotted something that's piquing my interest. I'm seeing that `CRYPTO_LIB_AES_CBC` and `CRYPTO_LIB_AES_ECB` are defined as tristate options. That's not entirely unusual, but what's immediately striking is that they don't seem to have associated prompts. Or, perhaps they have *implicit* prompts that aren't immediately visible? I need to clarify that. This lack of a direct prompt is a key detail, and I need to understand how these options are actually being selected or enabled within the Kconfig system. Without prompts, it suggests they are either being controlled by other dependencies or are behaving in an unusual, potentially automatic, way. Time to delve into the Kconfig files and trace their dependencies to figure out exactly what's driving their behavior. I have to understand what controls their configuration.



10/2 2026/09/25 08:16 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"
}

11/2 2026/09/25 08:16 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 15593
							output: 32
							thoughts: 534
						

Thoughts:
**BPF Crypto Dependencies: A Configuration Deep Dive**

Okay, let's break this down. First, I'm looking at `crypto/Kconfig`. I see the `CRYPTO_AES` config option and how it's defined: `select CRYPTO_LIB_AES`, and then it *selects* `CRYPTO_LIB_AES_CBC` if either `CRYPTO_CBC` or `CRYPTO_CTS` isn't set to 'n'.  Crucially, it *selects* `CRYPTO_LIB_AES_ECB` if `CRYPTO_ECB` isn't 'n'. Got it. AES relies on those underlying library implementations when the corresponding modes are enabled.

Now, glancing at the diff for `tools/testing/selftests/bpf/config`, I notice a critical change: `CONFIG_CRYPTO_SKCIPHER` is *gone*, and `CONFIG_CRYPTO_CBC=y` has been added directly. *That's* the key. I can immediately infer that this change is making sure the selftests have access to CBC mode. It's likely related to some BPF crypto testing.

So, when `CRYPTO_AES=y` and `CRYPTO_CBC=y` (as seems to be the case in this selftest configuration), `CRYPTO_LIB_AES_CBC` gets automatically selected. Similarly, with `CRYPTO_AES=y` and `CRYPTO_ECB=y`,  `CRYPTO_LIB_AES_ECB` is selected. This is the foundation.

Now let's consider `BPF_CRYPTO`. That depends on `BPF_SYSCALL`, and crucially, it *depends on* both `CRYPTO_LIB_AES_CBC` and `CRYPTO_LIB_AES_ECB`. So, to ensure `BPF_CRYPTO` is enabled, the dependency chain means we *must* have:

*   `BPF_SYSCALL` (that's obvious)
*   `CRYPTO_AES` (because the libraries are selected by it)
*   `CRYPTO_CBC` (to get `CRYPTO_LIB_AES_CBC`)
*   `CRYPTO_ECB` (to get `CRYPTO_LIB_AES_ECB`)

Alternatively, we could directly enable `CRYPTO_LIB_AES_CBC` and `CRYPTO_LIB_AES_ECB`, but that's less idiomatic within the kernel configuration system, right? Let's quickly double-check that this is accurate in `crypto/Kconfig`, around line 357. I want to confirm that there isn't a conditional dependency or override that I'm missing, because it's always worth it to be thorough.



12/2 2026/09/25 08:17 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"
}

13/2 2026/09/25 08:17 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 17317
							output: 36
							thoughts: 158
						

Thoughts:
**My Analysis of Crypto Configuration in the Kernel**

Okay, so I'm looking at how the kernel's cryptographic options are configured. Specifically, I see that defining `CRYPTO_AES`, `CRYPTO_CBC`, or `CRYPTO_ECB` directly influences the selection of their respective library implementations, namely `CRYPTO_LIB_AES_CBC` and `CRYPTO_LIB_AES_ECB`. That's straightforward enough. The implication here is that these top-level options are essentially macros that enable the underlying library components.

Now, here's an interesting dependency. If `CRYPTO_LIB_AES_CBC` *and* `CRYPTO_LIB_AES_ECB` are both explicitly set, either directly or through the broader `CRYPTO_AES`, `CRYPTO_CBC`, and `CRYPTO_ECB` options, and if `BPF_SYSCALL` is also enabled, then `BPF_CRYPTO` defaults to enabled. This suggests a direct integration between the BPF subsystem and the AES crypto libraries when both are present and the BPF syscall interface is enabled. It makes perfect sense, of course - leveraging BPF for accelerated crypto operations is a common and highly performant paradigm.

To get a better handle on this, I'll need to dig into the actual implementation. My next step is to examine `kernel/bpf/crypto.c`. That should give me the details on how the kernel functions (kfuncs) are set up and registered for these BPF-based crypto operations. I'm keen to see the specific interfaces and any optimizations or security considerations they've built into that code.



14/2 2026/09/25 08:17 tool read-file
0m Args:
{
  "File": "kernel/bpf/crypto.c",
  "FirstLine": 310,
  "LineCount": 55
}

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

15/2 2026/09/25 08:17 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 18875
							output: 229
							thoughts: 2248
						

Thoughts:
**BPF Crypto Kfunc Analysis for Fuzzing**

Okay, so this is initialized using `late_initcall(crypto_kfunc_init)`, meaning it's registering BTF kfuncs for BPF programs, which is interesting. The kfuncs are: `bpf_crypto_ctx_create`, `bpf_crypto_ctx_release`, `bpf_crypto_ctx_acquire`, `bpf_crypto_decrypt`, and `bpf_crypto_encrypt`. Let's dig into `kernel/bpf/crypto.c` to see what's going on.

Looks like we've got `bpf_crypto_find_algo` (static), `bpf_crypto_ctx_create` (kfunc, the one we care about), `crypto_free_cb` (static), the AES crypto functions `bpf_aes_cbc_crypt` (static), `bpf_aes_ecb_crypt` (static) and also `bpf_crypto_crypt` (static), along with `bpf_crypto_decrypt` and `bpf_crypto_encrypt` (kfuncs). So the kfuncs exposed are `bpf_crypto_ctx_create`, `bpf_crypto_encrypt`, `bpf_crypto_decrypt`, `bpf_crypto_ctx_release`, and `bpf_crypto_ctx_acquire`.

Alright, let's confirm whether `bpf_aes_cbc_crypt` and `bpf_aes_ecb_crypt` are inlined or separate functions by looking at the code. Yup, they are there in `kernel/bpf/crypto.c`, and they might be inlined. But `bpf_crypto_ctx_create`, `bpf_crypto_crypt`, `bpf_crypto_encrypt`, and `bpf_crypto_decrypt` are definitely the main players here, along with `bpf_crypto_find_algo`. And `bpf_aes_cbc_crypt`, `bpf_aes_ecb_crypt` are also important.

Now, is this worth fuzzing? Definitely. BPF is core, and syzkaller already heavily fuzzed it, so that's a plus. The changes here are significant: replacing crypto skcipher allocations with direct calls to AES routines, parameter validation changes, removing dynamic registration, altering context management with `kfree_sensitive`, and modifying buffer size checks. Also, there's `WARN_ON_ONCE(1)` in an unexpected algo case in `bpf_crypto_ctx_create`, which makes it a good target. It's definitely worth fuzzing.

For our focus symbols, we want the specific logic being added or altered. That points us to: `bpf_crypto_ctx_create`, `bpf_crypto_encrypt`, `bpf_crypto_decrypt`, `bpf_crypto_crypt`, `bpf_aes_cbc_crypt`, and `bpf_aes_ecb_crypt`.

Let's also quickly confirm that `bpf_crypto_ctx_release` and `crypto_free_cb` have been modified, and yes, `crypto_free_cb` does have a change. It's using `kfree_sensitive`. These functions we identified are non-ubiquitous, so good targets.

Now, for configuration options, let's see what `kernel/bpf/Kconfig` tells us. We need `BPF_SYSCALL`, `CRYPTO_LIB_AES_CBC`, and `CRYPTO_LIB_AES_ECB`, and `BPF_CRYPTO`. Wait, should `BPF_CRYPTO` be explicitly enabled? It's `def_bool y`, but it might be safe to include it explicitly. Let's make sure by checking how syzkaller handles things. OK, no `CONFIG_` prefix. We need `BPF_SYSCALL`, `BPF_CRYPTO`, `CRYPTO_LIB_AES_CBC`, and `CRYPTO_LIB_AES_ECB`. But, does `CRYPTO_LIB_AES_CBC` have a prompt? No! It's selected by `CRYPTO_AES` with `CRYPTO_CBC != n`. Re-verifying, `CRYPTO_AES`, `CRYPTO_CBC`, and `CRYPTO_ECB` all select `CRYPTO_LIB_AES_CBC` and `CRYPTO_LIB_AES_ECB`.  Okay, according to `tools/testing/selftests/bpf/config`, `CONFIG_CRYPTO_CBC=y`, `CONFIG_CRYPTO_ECB=y`, and `CONFIG_CRYPTO_AES=y`. So providing these would cover the dependencies.

So, in summary, we


16/2 2026/09/25 08:17 tool set-results
0m Args:
{
  "EnableConfigs": [
    "BPF_SYSCALL",
    "BPF_CRYPTO",
    "CRYPTO_AES",
    "CRYPTO_CBC",
    "CRYPTO_ECB",
    "CRYPTO_LIB_AES_CBC",
    "CRYPTO_LIB_AES_ECB"
  ],
  "FocusSymbols": [
    "bpf_crypto_ctx_create",
    "bpf_crypto_encrypt",
    "bpf_crypto_decrypt",
    "bpf_crypto_crypt",
    "bpf_aes_cbc_crypt",
    "bpf_aes_ecb_crypt"
  ],
  "Reasoning": "The patch refactors BPF crypto support by removing the skcipher dynamic registration layer and directly utilizing the AES-CBC and AES-ECB library routines. It alters context initialization, buffer size validations, memory management (using kfree_sensitive), and adds a new defensive WARN_ON_ONCE assertion. These changes modify reachable BPF subsystem kfuncs accessible from userspace.",
  "WorthFuzzing": true
}

Results:
{
  "EnableConfigs": [
    "BPF_SYSCALL",
    "BPF_CRYPTO",
    "CRYPTO_AES",
    "CRYPTO_CBC",
    "CRYPTO_ECB",
    "CRYPTO_LIB_AES_CBC",
    "CRYPTO_LIB_AES_ECB"
  ],
  "FocusSymbols": [
    "bpf_crypto_ctx_create",
    "bpf_crypto_encrypt",
    "bpf_crypto_decrypt",
    "bpf_crypto_crypt",
    "bpf_aes_cbc_crypt",
    "bpf_aes_ecb_crypt"
  ],
  "Reasoning": "The patch refactors BPF crypto support by removing the skcipher dynamic registration layer and directly utilizing the AES-CBC and AES-ECB library routines. It alters context initialization, buffer size validations, memory management (using kfree_sensitive), and adds a new defensive WARN_ON_ONCE assertion. These changes modify reachable BPF subsystem kfuncs accessible from userspace.",
  "WorthFuzzing": true
}

17/1 2026/09/25 08:17 action If
3m Args:
{
  "WorthFuzzing": true
}

Results:
null

18/2 2026/09/25 08:17 agent kmsan-evaluator
3m Model:
core

Args:
null

Results:
{
  "KMSANReasoning": "The patch refactors BPF crypto support by replacing the dynamic skcipher registration mechanism (using crypto_alloc_lskcipher) with direct calls to AES library routines (AES-CBC and AES-ECB via crypto/aes-cbc.h and crypto/aes-ecb.h).\n\nEvaluating KMSAN vs KASAN applicability:\n1. Heap Allocation \u0026 Initialization: The context structure `bpf_crypto_ctx` is allocated using `kzalloc_obj(*ctx)` which completely zeroes all memory. Key setup is performed via `aes_preparekey()`, and the context is freed via `kfree_sensitive()`.\n2. Stack \u0026 Local Variables: All local variables in `bpf_crypto_ctx_create()`, `bpf_aes_cbc_crypt()`, `bpf_aes_ecb_crypt()`, and `bpf_crypto_crypt()` are explicitly initialized before branching or reading. The return error code pointer `err_ret` is populated on all code paths.\n3. No User-Space Info Leaks: The BPF kfuncs operate internally on BPF dynptr buffers and context pointers within the kernel. There are no data structures, socket buffers, netlink attributes, or ioctls copied to user space.\n4. Bounds Checking vs Out-of-Bounds: The buffers accessed (`src`, `dst`, `iv`) are bounded by explicit length checks (`src_len % AES_BLOCK_SIZE`, `dst_len \u003c src_len`, and `iv_len == AES_BLOCK_SIZE` or `iv_len == 0`). Any potential bounds issues or lifetime/refcounting errors would be detected by KASAN, not KMSAN.\n\nTherefore, the changes do not introduce or expose uninitialized memory access risks or info-leaks, and a dedicated KMSAN session is not needed.",
  "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 6839221c9b518d62ce33806fcb8c4b7daa59b85f
Author: syz-cluster <triage@syzkaller.com>
Date:   Fri Sep 25 08:16:27 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..784e2bd5ca924 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,53 @@ 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:	The crypto key
  * @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;
+	union {
+		struct aes_key aes;
+	} 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 +85,69 @@ __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;
+		else
+			err = aes_preparekey(&ctx->key.aes, 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);
+		*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 +155,7 @@ 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);
 }
 
 /**
@@ -267,27 +195,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 bpf_crypto_ctx *ctx, 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, &ctx->key.aes);
+	else
+		aes_cbc_encrypt(dst, src, src_len, iv, &ctx->key.aes);
+	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 bpf_crypto_ctx *ctx, 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, &ctx->key.aes);
+	else
+		aes_ecb_encrypt(dst, src, src_len, &ctx->key.aes);
+	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 +251,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, decrypt);
+	case BPF_ALGO_AES_ECB:
+		return bpf_aes_ecb_crypt(pdst, dst_len, psrc, src_len, piv,
+					 iv_len, ctx, decrypt);
+	default:
+		return -EINVAL;
+	}
 }
 
 /**
@@ -312,20 +272,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 +293,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 a7c9e2d8965d5..e9c8f2444a8a0 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -4669,7 +4669,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

19/3 2026/09/25 08:17 llm kmsan-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 9849
							output: 19
							thoughts: 2178
						

Thoughts:
**KMSAN Fuzzing Session Justification: BPF Crypto Refactor**

Okay, let's break this down systematically. I need to determine if this patch series warrants a dedicated KMSAN fuzzing session. The rules are clear: KMSAN is for detecting reads of uninitialized memory and kernel-to-user information leaks that KASAN, LOCKDEP, or other standard detectors would miss.  If this patch primarily deals with out-of-bounds access, use-after-free, or locking issues, then KMSAN is not the right tool.

First, let's look at what the patch does. It's refactoring BPF crypto support, specifically removing the generic crypto API (`lskcipher`) and using direct library calls (`crypto/aes-cbc.h`, `crypto/aes-ecb.h`) for AES.  This involves removing some registration/unregistration mechanisms and defining a new `bpf_crypto_algos` array. The core change seems to be in `bpf_crypto_ctx_create`. This function allocates and initializes the crypto context, checking parameters. The patch defines `struct bpf_crypto_ctx`.

Now, the red flag - `bpf_crypto_ctx_create` and its local variable `err`. The code declares `int err;` but it looks like `err` is initialized on all paths before the `out:` label, and `*err_ret` is *always* initialized. I am reviewing all paths to `out:` to verify.

Now, let's look at `bpf_aes_cbc_crypt`. There are checks on `src_len` and `dst_len`. It calls functions `aes_cbc_decrypt` and `aes_cbc_encrypt`. I need to review those functions. I will use grepper. I don't see any obvious risk of uninitialized memory in this section.

So, *does* this patch introduce any risks of uninitialized memory usage or information leaks?

`bpf_crypto_ctx_create` looks okay. The `err` variable is initialized everywhere and the return path uses `*err_ret` correctly. The crypto operations themselves are straightforward and have length/size checks. The structure changes are minor and seem to be fully initialized on creation. There isn't any obvious scenario where an uninitialized variable is read, or an information leak could occur. I've re-reviewed all paths in this patch, and there are checks on all lengths/sizes to avoid out-of-bounds accesses.

Based on this analysis, I'm leaning towards **NeedsKMSAN = false**. This patch is refactoring code, and there is no evidence of uninitialized memory exposure that would be specifically caught by KMSAN but not by other standard checkers. A full KASAN build might be sufficient here, and a KMSAN session might be overkill.



20/3 2026/09/25 08:17 tool grepper
0m Args:
{
  "Expression": "void aes_cbc_"
}

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,\narch/riscv/crypto/aes-riscv64-glue.c-32-\t\t\t\t       u8 iv[AES_BLOCK_SIZE]);\narch/riscv/crypto/aes-riscv64-glue.c:33:asmlinkage void aes_cbc_decrypt_zvkned(const struct crypto_aes_ctx *key,\narch/riscv/crypto/aes-riscv64-glue.c-34-\t\t\t\t       const u8 *in, u8 *out, size_t len,\n--\narch/riscv/crypto/aes-riscv64-glue.c-36-\narch/riscv/crypto/aes-riscv64-glue.c:37:asmlinkage void aes_cbc_cts_crypt_zvkned(const struct crypto_aes_ctx *key,\narch/riscv/crypto/aes-riscv64-glue.c-38-\t\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--\narch/riscv/crypto/aes-riscv64-zvkned.S=159=SYM_FUNC_END(aes_cbc_decrypt_zvkned)\n--\narch/riscv/crypto/aes-riscv64-zvkned.S-298-\narch/riscv/crypto/aes-riscv64-zvkned.S:299:// void aes_cbc_cts_crypt_zvkned(const struct crypto_aes_ctx *key,\narch/riscv/crypto/aes-riscv64-zvkned.S-300-//\t\t\t         const u8 *in, u8 *out, size_t len,\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--\ninclude/crypto/aes-cbc.h-44- */\ninclude/crypto/aes-cbc.h:45:void aes_cbc_decrypt(u8 *dst, const u8 *src, size_t len,\ninclude/crypto/aes-cbc.h-46-\t\t     u8 iv[at_least AES_BLOCK_SIZE], const struct aes_key *key);\n--\ninclude/crypto/aes-cbc.h-58- */\ninclude/crypto/aes-cbc.h:59:void aes_cbc_cts_encrypt(u8 *dst, const u8 *src, size_t len,\ninclude/crypto/aes-cbc.h-60-\t\t\t u8 iv[at_least AES_BLOCK_SIZE], aes_encrypt_arg key);\n--\ninclude/crypto/aes-cbc.h-72- */\ninclude/crypto/aes-cbc.h:73:void aes_cbc_cts_decrypt(u8 *dst, const u8 *src, size_t len,\ninclude/crypto/aes-cbc.h-74-\t\t\t u8 iv[at_least AES_BLOCK_SIZE],\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--\nlib/crypto/aes.c=885=EXPORT_SYMBOL_GPL(aes_cbc_encrypt);\nlib/crypto/aes.c-886-\nlib/crypto/aes.c:887:void aes_cbc_decrypt(u8 *dst, const u8 *src, size_t len, u8 iv[AES_BLOCK_SIZE],\nlib/crypto/aes.c-888-\t\t     const struct aes_key *key)\n--\nlib/crypto/aes.c=935=static bool aes_cbc_cts_decrypt_arch(u8 *dst, const u8 *src, size_t len,\n--\nlib/crypto/aes.c-942-\nlib/crypto/aes.c:943:void aes_cbc_cts_encrypt(u8 *dst, const u8 *src, size_t len,\nlib/crypto/aes.c-944-\t\t\t u8 iv[AES_BLOCK_SIZE], aes_encrypt_arg key)\n--\nlib/crypto/aes.c=980=EXPORT_SYMBOL_GPL(aes_cbc_cts_encrypt);\nlib/crypto/aes.c-981-\nlib/crypto/aes.c:982:void aes_cbc_cts_decrypt(u8 *dst, const u8 *src, size_t len,\nlib/crypto/aes.c-983-\t\t\t u8 iv[AES_BLOCK_SIZE], const struct aes_key *key)\n--\nlib/crypto/aes.c=1047=static void __init aes_cbc_cts_fips_test(void)\n--\nlib/crypto/aes.c-1074-#else /* CONFIG_CRYPTO_LIB_AES_CBC */\nlib/crypto/aes.c:1075:static inline void aes_cbc_fips_test(void)\nlib/crypto/aes.c-1076-{\nlib/crypto/aes.c-1077-}\nlib/crypto/aes.c:1078:static inline void aes_cbc_cts_fips_test(void)\nlib/crypto/aes.c-1079-{\n"
}

21/3 2026/09/25 08:17 llm kmsan-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 14373
							output: 35
							thoughts: 21
						

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

**Diving into the AES-CBC Implementation**

Okay, time to get my hands dirty. I need to take a look at the `include/crypto/aes-cbc.h` header file. Specifically, I need to focus in on around line 27. It's likely where the core data structures or function prototypes related to AES-CBC encryption or decryption initialization might be. I need to refresh my mental model of how the CBC mode is handled within this specific implementation. I'll be looking for things like the block size definition, structure definitions related to the cipher context, key scheduling aspects, or potentially a function declaration that sets up the algorithm. This is all standard stuff, but seeing the implementation-specific details is always important.



22/3 2026/09/25 08:17 tool read-file
0m Args:
{
  "File": "include/crypto/aes-cbc.h",
  "FirstLine": 1,
  "LineCount": 60
}

Results:
{
  "Contents": "   1:\t/* SPDX-License-Identifier: GPL-2.0 */\n   2:\t/*\n   3:\t * AES-CBC and AES-CBC-CTS unauthenticated encryption and decryption\n   4:\t *\n   5:\t * Copyright 2026 Google LLC\n   6:\t */\n   7:\t#ifndef _CRYPTO_AES_CBC_H\n   8:\t#define _CRYPTO_AES_CBC_H\n   9:\t\n  10:\t#include \u003ccrypto/aes.h\u003e\n  11:\t\n  12:\t/**\n  13:\t * aes_cbc_encrypt() - Encrypt data using AES-CBC\n  14:\t * @dst: The destination buffer.  Can be in-place or out-of-place.  For other\n  15:\t *\t overlaps the behavior is unspecified.\n  16:\t * @src: The source data\n  17:\t * @len: Number of bytes to encrypt.  Must be a multiple of AES_BLOCK_SIZE.\n  18:\t * @iv: The initialization vector.  It is updated with the next value, i.e. the\n  19:\t *\tlast ciphertext block (or left unchanged if @len == 0).\n  20:\t * @key: The key, already prepared using aes_preparekey() or aes_prepareenckey()\n  21:\t *\n  22:\t * This supports incremental encryption.  The length of each chunk must be a\n  23:\t * multiple of AES_BLOCK_SIZE, and the updated @iv must be passed in each time.\n  24:\t *\n  25:\t * Context: Any context.\n  26:\t */\n  27:\tvoid aes_cbc_encrypt(u8 *dst, const u8 *src, size_t len,\n  28:\t\t\t     u8 iv[at_least AES_BLOCK_SIZE], aes_encrypt_arg key);\n  29:\t\n  30:\t/**\n  31:\t * aes_cbc_decrypt() - Decrypt data using AES-CBC\n  32:\t * @dst: The destination buffer.  Can be in-place or out-of-place.  For other\n  33:\t *\t overlaps the behavior is unspecified.\n  34:\t * @src: The source data\n  35:\t * @len: Number of bytes to decrypt.  Must be a multiple of AES_BLOCK_SIZE.\n  36:\t * @iv: The initialization vector.  It is updated with the next value, i.e. the\n  37:\t *\tlast ciphertext block (or left unchanged if @len == 0).\n  38:\t * @key: The key, already prepared using aes_preparekey()\n  39:\t *\n  40:\t * This supports incremental decryption.  The length of each chunk must be a\n  41:\t * multiple of AES_BLOCK_SIZE, and the updated @iv must be passed in each time.\n  42:\t *\n  43:\t * Context: Any context.\n  44:\t */\n  45:\tvoid aes_cbc_decrypt(u8 *dst, const u8 *src, size_t len,\n  46:\t\t\t     u8 iv[at_least AES_BLOCK_SIZE], const struct aes_key *key);\n  47:\t\n  48:\t/**\n  49:\t * aes_cbc_cts_encrypt() - Encrypt data using AES-CBC-CTS (CS3 variant)\n  50:\t * @dst: The destination buffer.  Can be in-place or out-of-place.  For other\n  51:\t *\t overlaps the behavior is unspecified.\n  52:\t * @src: The source data\n  53:\t * @len: Number of bytes to encrypt, at least AES_BLOCK_SIZE\n  54:\t * @iv: The initialization vector, clobbered by this function\n  55:\t * @key: The key, already prepared using aes_preparekey() or aes_prepareenckey()\n  56:\t *\n  57:\t * Context: Any context.\n  58:\t */\n  59:\tvoid aes_cbc_cts_encrypt(u8 *dst, const u8 *src, size_t len,\n  60:\t\t\t\t u8 iv[at_least AES_BLOCK_SIZE], aes_encrypt_arg key);\n"
}

23/3 2026/09/25 08:17 llm kmsan-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 15644
							output: 29
							thoughts: 20
						

Thoughts:
Okay, let's dive into this next header file.

**Diving into `include/crypto/aes-ecb.h`**

Alright, having wrapped up the initial file, it's time to understand the role of `include/crypto/aes-ecb.h`. I'm going to carefully read this to pinpoint how the ECB (Electronic Codebook) mode of AES (Advanced Encryption Standard) is implemented or referenced within this system. I need to understand the data structures, function prototypes, and any macros related to AES-ECB. I'm looking for clues about key sizes, block sizes, padding schemes if any are used implicitly, and how the ECB mode interacts with the underlying AES implementation. I'll need to know if it's a software-based implementation, hardware acceleration, or perhaps a wrapper around a more general AES library. I'm also going to check for error handling and any security considerations specific to ECB. Understanding these elements is important because ECB, while simple, has some well-known security limitations, so I'll be alert to how this is addressed in this context.



24/3 2026/09/25 08:17 tool grepper
0m Args:
{
  "Expression": "aes_ecb_",
  "PathPrefix": "include/crypto/"
}

Results:
{
  "Output": "include/crypto/aes-ecb.h-12-/**\ninclude/crypto/aes-ecb.h:13: * aes_ecb_encrypt() - Encrypt data using AES-ECB\ninclude/crypto/aes-ecb.h-14- * @dst: The destination buffer.  Can be in-place or out-of-place.  For other\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-\ninclude/crypto/aes-ecb.h-30-/**\ninclude/crypto/aes-ecb.h:31: * aes_ecb_decrypt() - Decrypt data using AES-ECB\ninclude/crypto/aes-ecb.h-32- * @dst: The destination buffer.  Can be in-place or out-of-place.  For other\n--\ninclude/crypto/aes-ecb.h-45- */\ninclude/crypto/aes-ecb.h:46:void aes_ecb_decrypt(u8 *dst, const u8 *src, size_t len,\ninclude/crypto/aes-ecb.h-47-\t\t     const struct aes_key *key);\n--\ninclude/crypto/aes.h=168=int ce_aes_expandkey(struct crypto_aes_ctx *ctx, const u8 *in_key,\ninclude/crypto/aes.h-169-\t\t     unsigned int key_len);\ninclude/crypto/aes.h:170:asmlinkage void neon_aes_ecb_encrypt(u8 out[], u8 const in[], u32 const rk[],\ninclude/crypto/aes.h-171-\t\t\t\t     int rounds, int blocks);\ninclude/crypto/aes.h:172:asmlinkage void neon_aes_ecb_decrypt(u8 out[], u8 const in[], u32 const rk[],\ninclude/crypto/aes.h-173-\t\t\t\t     int rounds, int blocks);\n--\ninclude/crypto/aes.h=199=asmlinkage void neon_aes_essiv_cbc_decrypt(u8 out[], u8 const in[],\n--\ninclude/crypto/aes.h-203-\ninclude/crypto/aes.h:204:asmlinkage void ce_aes_ecb_encrypt(u8 out[], u8 const in[], u32 const rk[],\ninclude/crypto/aes.h-205-\t\t\t\t   int rounds, int blocks);\ninclude/crypto/aes.h:206:asmlinkage void ce_aes_ecb_decrypt(u8 out[], u8 const in[], u32 const rk[],\ninclude/crypto/aes.h-207-\t\t\t\t   int rounds, int blocks);\n"
}

25/3 2026/09/25 08:17 llm kmsan-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 16644
							output: 35
							thoughts: 22
						

Thoughts:
Okay, here's my summary, taking the perspective of someone deeply familiar with kernel BPF and cryptography, as requested:

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

Alright, time to get my hands dirty. I need to comprehensively understand the evolution of this cryptography code within the BPF framework. That means a full, line-by-line inspection of `kernel/bpf/crypto.c` is absolutely essential. I'm not just looking for surface-level changes; I need the complete context. What's the rationale behind these modifications? Are there performance considerations? Any security implications? What's the interplay with other parts of the BPF system or the kernel's cryptographic infrastructure as a whole? I need to build a mental map of every alteration and its surrounding code to grasp the true picture. This granular level of analysis is the only way to ensure I'm getting the complete picture. Let's get to work.



26/3 2026/09/25 08:17 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:\tThe crypto key\n  66:\t * @rcu:\tThe RCU head used to free the crypto context with RCU safety.\n  67:\t * @usage:\tObject reference counter. When the refcount goes to 0, the\n  68:\t *\t\tmemory is released back to the BPF allocator, which provides\n  69:\t *\t\tRCU safety.\n  70:\t */\n  71:\tstruct bpf_crypto_ctx {\n  72:\t\tenum bpf_crypto_algo_id algo;\n  73:\t\tunion {\n  74:\t\t\tstruct aes_key aes;\n  75:\t\t} key;\n  76:\t\tstruct rcu_head rcu;\n  77:\t\trefcount_t usage;\n  78:\t};\n  79:\t\n  80:\t__bpf_kfunc_start_defs();\n  81:\t\n  82:\t/**\n  83:\t * bpf_crypto_ctx_create() - Create a mutable BPF crypto context.\n  84:\t *\n  85:\t * Allocates a crypto context that can be used, acquired, and released by\n  86:\t * a BPF program. The crypto context returned by this function must either\n  87:\t * be embedded in a map as a kptr, or freed with bpf_crypto_ctx_release().\n  88:\t * As this uses a GFP_KERNEL allocation, this function can only be used in\n  89:\t * sleepable BPF programs.\n  90:\t *\n  91:\t * bpf_crypto_ctx_create() allocates memory for crypto context.\n  92:\t * It may return NULL if no memory is available.\n  93:\t * @params:\tpointer to struct bpf_crypto_params which contains all the\n  94:\t *\t\tdetails needed to initialise crypto context.\n  95:\t * @params__sz:\tsize of struct bpf_crypto_params used by bpf program\n  96:\t * @err_ret:\tinteger to store error code when NULL is returned.\n  97:\t */\n  98:\t__bpf_kfunc struct bpf_crypto_ctx *\n  99:\tbpf_crypto_ctx_create(const struct bpf_crypto_params *params, u32 params__sz,\n 100:\t\t\t      int *err_ret)\n"
}

27/3 2026/09/25 08:17 llm kmsan-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 18451
							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, let's get down to brass tacks. I need to get a better handle on the kernel's BPF implementation, specifically how it interfaces with crypto operations. Time to dive into the rest of `kernel/bpf/crypto.c`. I expect to see the meat of how BPF programs can leverage cryptographic primitives, probably through helper functions and associated structures. I'm keen to understand the nuances of how these operations are made secure and efficient within the BPF context. No doubt there are important considerations around key handling, potential side-channel vulnerabilities, and resource limitations that I'll need to dissect. I'll be looking for how they are avoiding common pitfalls, and what tradeoffs have been made in terms of performance versus generality. I'm also anticipating that some interesting design decisions regarding the interaction between the BPF verifier and these crypto functions will emerge. It is always important to see if they're relying on the hardware crypto offload, or perhaps doing everything in software. This will determine how much performance overhead is involved.



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

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

29/3 2026/09/25 08:17 llm kmsan-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 20223
							output: 37
							thoughts: 16
						

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

**Analyzing the Remainder: Strategic Review**

Alright, I've got the context, now let's dive into the rest of the file, specifically from line 198 onwards. Given what I've already processed, my immediate thought is to approach this section with a strategic mindset. I'm not just going to read it linearly. Instead, I'll be looking for specific indicators based on the context that has already been analyzed. It's a targeted scan, prioritizing elements that might relate to key concepts, outliers or unexpected outcomes given the initial data set and my expert knowledge. This is where the real insights often lie, at the intersection of expected patterns and novel information. I'll be particularly vigilant for anything that:

*   **Supports or refutes my initial hypotheses**. I need evidence to solidify (or completely revise) my current understanding.
*   **Introduces new variables or considerations**. Unexpected elements are almost always more interesting.
*   **Highlights areas requiring further investigation**. This might involve following up on a specific reference or correlating data to an external source.

The goal at this stage is to identify *key nuggets of information* that drive the conclusion, rather than a thorough and linear line-by-line examination of the text. I'm ready to find the interesting bits.



30/3 2026/09/25 08:18 tool read-file
0m Args:
{
  "File": "kernel/bpf/crypto.c",
  "FirstLine": 198,
  "LineCount": 120
}

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

31/3 2026/09/25 08:18 llm kmsan-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 22398
							output: 36
							thoughts: 18
						

Thoughts:
Okay, here's the summary, rendered as if I am the one thinking these thoughts:

**Checking Contingencies**

Alright, I've just processed section 297. While it seems to be in good shape, I'm going to quickly scan the remainder of the file, just as a precaution. It's rare, but sometimes there are cascading issues, or perhaps a minor inconsistency introduced later that impacts earlier segments. I need to ensure a complete, holistic understanding. It's a bit of extra due diligence, but it is better to be safe than sorry and this will allow me to be confident in my assessment of the file. No point in making assumptions. Just a quick run-through to confirm everything downstream is as it should be.



32/3 2026/09/25 08:18 tool read-file
0m Args:
{
  "File": "kernel/bpf/crypto.c",
  "FirstLine": 298,
  "LineCount": 50
}

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

33/3 2026/09/25 08:18 llm kmsan-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 23607
							output: 19
							thoughts: 182
						

Thoughts:
**Analyzing Memory Handling in this BPF Crypto Context**

Okay, so let's break down the memory management in this context. It seems we're dealing with a `bpf_crypto_ctx` structure. First, the memory is allocated using `kzalloc_obj(*ctx)`. Good, that means we start with a zero-initialized heap, which is generally a safe approach to prevent information leaks. Now, let's look at the fields we populate: the algorithm, which is set to either AES-CBC or AES-ECB; the key, specifically, an `aes_key` struct within `ctx->key.aes`, prepared using `aes_preparekey`. This is where I need to focus: I need to verify that `aes_preparekey` doesn't inadvertently leave any sensitive data, remnants from previous memory allocations, or uninitialized values in `aes_key`. The other fields, the RCU head and the refcount are less critical from a direct data-leak perspective, but still important for correctness. Finally, the context is freed using `kfree_sensitive(ctx)`, which is appropriate given the potential for sensitive key material. The key question is, what happens within `aes_preparekey` in terms of memory initialization? That's what I need to investigate next.



34/3 2026/09/25 08:18 tool grepper
0m Args:
{
  "Expression": "int aes_preparekey"
}

Results:
{
  "Output": "include/crypto/aes.h=308=void aes_sparc64_ctr_crypt_256(const u64 *key, const u64 *input, u64 *output,\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--\nlib/crypto/aes.c=485=static int __aes_preparekey(struct aes_enckey *enc_key,\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-{\n"
}

35/3 2026/09/25 08:18 llm kmsan-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 24313
							output: 35
							thoughts: 19
						

Thoughts:
Here's a summary of my thought process, tailored for an expert like you:

**Diving into the AES Implementation**

Okay, so we need to zero in on a specific area within the AES implementation. My initial focus is the `lib/crypto/aes.c` file. Based on previous issues and typical performance bottlenecks, I'm going to start my investigation around line 498. I'm not making assumptions, but it's often a key area for potential inefficiencies or perhaps an interesting implementation detail that warrants a closer look, especially concerning key scheduling, data processing, or potential side-channel vulnerabilities. This is just a starting point, of course – the deeper dive begins *after* examining the code at that location. Let's see what that section reveals!



36/3 2026/09/25 08:18 tool read-file
0m Args:
{
  "File": "lib/crypto/aes.c",
  "FirstLine": 498,
  "LineCount": 40
}

Results:
{
  "Contents": " 498:\tint aes_preparekey(struct aes_key *key, const u8 *in_key, size_t key_len)\n 499:\t{\n 500:\t\treturn __aes_preparekey((struct aes_enckey *)key, \u0026key-\u003einv_k,\n 501:\t\t\t\t\tin_key, key_len);\n 502:\t}\n 503:\tEXPORT_SYMBOL(aes_preparekey);\n 504:\t\n 505:\tint aes_prepareenckey(struct aes_enckey *key, const u8 *in_key, size_t key_len)\n 506:\t{\n 507:\t\treturn __aes_preparekey(key, NULL, in_key, key_len);\n 508:\t}\n 509:\tEXPORT_SYMBOL(aes_prepareenckey);\n 510:\t\n 511:\tvoid aes_encrypt(aes_encrypt_arg key, u8 out[AES_BLOCK_SIZE],\n 512:\t\t\t const u8 in[AES_BLOCK_SIZE])\n 513:\t{\n 514:\t\taes_encrypt_arch(key.enc_key, out, in);\n 515:\t}\n 516:\tEXPORT_SYMBOL(aes_encrypt);\n 517:\t\n 518:\tvoid aes_decrypt(const struct aes_key *key, u8 out[AES_BLOCK_SIZE],\n 519:\t\t\t const u8 in[AES_BLOCK_SIZE])\n 520:\t{\n 521:\t\taes_decrypt_arch(key, out, in);\n 522:\t}\n 523:\tEXPORT_SYMBOL(aes_decrypt);\n 524:\t\n 525:\t/* FIPS cryptographic algorithm self-test for \"bare\" AES */\n 526:\tstatic void __init aes_fips_test(void)\n 527:\t{\n 528:\t\tstruct aes_key key;\n 529:\t\tu8 data[AES_BLOCK_SIZE];\n 530:\t\n 531:\t\tif (aes_preparekey(\u0026key, fips_test_key, sizeof(fips_test_key)) != 0)\n 532:\t\t\tpanic(\"aes: FIPS self-test failed (preparekey)\\n\");\n 533:\t\n 534:\t\taes_encrypt(\u0026key, data, fips_test_data);\n 535:\t\tif (memcmp(fips_test_aes_ecb_ctext, data, sizeof(data)) != 0)\n 536:\t\t\tpanic(\"aes: FIPS self-test failed (wrong ciphertext)\\n\");\n 537:\t\n"
}

37/3 2026/09/25 08:18 llm kmsan-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 25216
							output: 35
							thoughts: 21
						

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

**Diving into AES Key Preparation**

Okay, let's get down to brass tacks. I need to understand how the key is prepped for AES encryption in this library. The logical starting point is the `__aes_preparekey` function within `lib/crypto/aes.c`. I expect this is where the key scheduling, expansion, or any other necessary pre-processing happens before the actual encryption or decryption routines are called. This is the foundation; understanding this function will give me crucial insights into the overall performance and security properties of the AES implementation. I'm going to dig in, analyzing the arguments it takes, the internal operations, and any potential vulnerabilities or performance bottlenecks it might introduce. Time to get my hands dirty.



38/3 2026/09/25 08:18 tool read-file
0m Args:
{
  "File": "lib/crypto/aes.c",
  "FirstLine": 460,
  "LineCount": 40
}

Results:
{
  "Contents": " 460:\t/* No arch-specific implementation of AES is available.  Use generic code. */\n 461:\t\n 462:\tstatic void aes_preparekey_arch(union aes_enckey_arch *k,\n 463:\t\t\t\t\tunion aes_invkey_arch *inv_k,\n 464:\t\t\t\t\tconst u8 *in_key, int key_len, int nrounds)\n 465:\t{\n 466:\t\taes_expandkey_generic(k-\u003erndkeys, inv_k ? inv_k-\u003einv_rndkeys : NULL,\n 467:\t\t\t\t      in_key, key_len);\n 468:\t}\n 469:\t\n 470:\tstatic void aes_encrypt_arch(const struct aes_enckey *key,\n 471:\t\t\t\t     u8 out[AES_BLOCK_SIZE],\n 472:\t\t\t\t     const u8 in[AES_BLOCK_SIZE])\n 473:\t{\n 474:\t\taes_encrypt_generic(key-\u003ek.rndkeys, key-\u003enrounds, out, in);\n 475:\t}\n 476:\t\n 477:\tstatic void aes_decrypt_arch(const struct aes_key *key,\n 478:\t\t\t\t     u8 out[AES_BLOCK_SIZE],\n 479:\t\t\t\t     const u8 in[AES_BLOCK_SIZE])\n 480:\t{\n 481:\t\taes_decrypt_generic(key-\u003einv_k.inv_rndkeys, key-\u003enrounds, out, in);\n 482:\t}\n 483:\t#endif\n 484:\t\n 485:\tstatic int __aes_preparekey(struct aes_enckey *enc_key,\n 486:\t\t\t\t    union aes_invkey_arch *inv_k,\n 487:\t\t\t\t    const u8 *in_key, size_t key_len)\n 488:\t{\n 489:\t\tif (aes_check_keylen(key_len) != 0)\n 490:\t\t\treturn -EINVAL;\n 491:\t\tenc_key-\u003elen = key_len;\n 492:\t\tenc_key-\u003enrounds = 6 + key_len / 4;\n 493:\t\taes_preparekey_arch(\u0026enc_key-\u003ek, inv_k, in_key, key_len,\n 494:\t\t\t\t    enc_key-\u003enrounds);\n 495:\t\treturn 0;\n 496:\t}\n 497:\t\n 498:\tint aes_preparekey(struct aes_key *key, const u8 *in_key, size_t key_len)\n 499:\t{\n"
}

39/3 2026/09/25 08:18 llm kmsan-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 26129
							output: 29
							thoughts: 766
						

Thoughts:
**Analyzing AES CBC Encryption/Decryption within the BPF Context**

Okay, so I'm looking at this BPF code, and the first thing that jumps out is that `ctx` was initialized with `kzalloc_obj`. That means any unused portions of the context structure, like potentially unused round keys in the `aes_key` field (because we might be using 128-bit AES even though the structure is sized for 256-bit), are guaranteed to be zeroed out. That's good, prevents any leaks of memory contents.

Now, diving into `bpf_aes_cbc_crypt` and `bpf_aes_ecb_crypt`, I'm seeing the CBC implementation. The code has some sanity checks: `iv_len` must be AES_BLOCK_SIZE, `src_len` has to be a multiple of AES_BLOCK_SIZE, and `dst_len` must be at least as large as `src_len`. Then, it calls either `aes_cbc_decrypt` or `aes_cbc_encrypt`.

Hold on... what if `dst_len` *is* actually greater than `src_len`? The code uses `src_len` in calls to underlying ciphers. Those functions write `src_len` bytes to `dst`, but I don't see anything explicitly initializing the rest of `dst` in `bpf_aes_cbc_crypt` if `dst_len` is larger. Is there any initialization happening in the higher-level functions, `bpf_crypto_crypt`, `bpf_crypto_encrypt`, or `bpf_crypto_decrypt`?

Back to the original code I was looking at: it calculates `siv_len`, `src_len`, and `dst_len`, and has a check that `src_len` is not greater than `dst_len`. It seems like `crypto_lskcipher_encrypt(tfm, src, dst, len, siv)` or `crypto_lskcipher_decrypt` uses the value passed as `len`, which is `src_len`, so only encrypting/decrypting up to `src_len` bytes.

The `dst` is a `bpf_dynptr`. So, let's remember BPF handles these dynptrs – the BPF program provides `dst` by allocating memory or providing a pointer to existing memory in a map value, a ringbuf, or even the skb. The BPF verifier checks them.

My bigger question is, does the BPF verifier have any mechanisms to track or enforce the initialization of memory within a `bpf_dynptr`? I need to understand if there is uninitialized memory tracking related to dynptrs. Where would I check that? It's time to check the verifier code, specifically `kernel/bpf/verifier.c`. Let me search for `bpf_crypto_decrypt` or `bpf_crypto_encrypt` in the verifier code. I need to understand how the verifier interacts with dynptrs and their memory management to confirm if `dst` is ever fully initialized or if only `src_len` bytes are considered valid.



40/3 2026/09/25 08:18 tool grepper
0m Args:
{
  "Expression": "bpf_crypto",
  "PathPrefix": "kernel/bpf/"
}

Results:
{
  "Output": "kernel/bpf/crypto.c-13-/**\nkernel/bpf/crypto.c:14: * struct bpf_crypto_params - BPF crypto initialization parameters structure\nkernel/bpf/crypto.c-15- * @type:\tThe string of crypto operation type.\n--\nkernel/bpf/crypto.c-23- */\nkernel/bpf/crypto.c:24:struct bpf_crypto_params {\nkernel/bpf/crypto.c-25-\tchar type[14];\n--\nkernel/bpf/crypto.c-32-\nkernel/bpf/crypto.c:33:enum bpf_crypto_algo_id {\nkernel/bpf/crypto.c-34-\tBPF_ALGO_AES_CBC,\n--\nkernel/bpf/crypto.c=38=static const struct {\n--\nkernel/bpf/crypto.c-40-\tconst char *algo_name;\nkernel/bpf/crypto.c:41:\tenum bpf_crypto_algo_id algo;\nkernel/bpf/crypto.c:42:} bpf_crypto_algos[] = {\nkernel/bpf/crypto.c-43-\t{ \"skcipher\", \"cbc(aes)\", BPF_ALGO_AES_CBC },\n--\nkernel/bpf/crypto.c-46-\nkernel/bpf/crypto.c:47:static bool bpf_crypto_find_algo(const struct bpf_crypto_params *params,\nkernel/bpf/crypto.c:48:\t\t\t\t enum bpf_crypto_algo_id *id_ret)\nkernel/bpf/crypto.c-49-{\nkernel/bpf/crypto.c:50:\tfor (size_t i = 0; i \u003c ARRAY_SIZE(bpf_crypto_algos); i++) {\nkernel/bpf/crypto.c:51:\t\tif (strncmp(bpf_crypto_algos[i].type_name, params-\u003etype,\nkernel/bpf/crypto.c-52-\t\t\t    sizeof(params-\u003etype)) == 0 \u0026\u0026\nkernel/bpf/crypto.c:53:\t\t    strncmp(bpf_crypto_algos[i].algo_name, params-\u003ealgo,\nkernel/bpf/crypto.c-54-\t\t\t    sizeof(params-\u003ealgo)) == 0) {\nkernel/bpf/crypto.c:55:\t\t\t*id_ret = bpf_crypto_algos[i].algo;\nkernel/bpf/crypto.c-56-\t\t\treturn true;\n--\nkernel/bpf/crypto.c-62-/**\nkernel/bpf/crypto.c:63: * struct bpf_crypto_ctx - refcounted BPF crypto context structure\nkernel/bpf/crypto.c-64- * @algo:\tThe crypto algorithm ID\n--\nkernel/bpf/crypto.c-70- */\nkernel/bpf/crypto.c:71:struct bpf_crypto_ctx {\nkernel/bpf/crypto.c:72:\tenum bpf_crypto_algo_id algo;\nkernel/bpf/crypto.c-73-\tunion {\n--\nkernel/bpf/crypto.c=80=__bpf_kfunc_start_defs();\n--\nkernel/bpf/crypto.c-82-/**\nkernel/bpf/crypto.c:83: * bpf_crypto_ctx_create() - Create a mutable BPF crypto context.\nkernel/bpf/crypto.c-84- *\n--\nkernel/bpf/crypto.c-86- * a BPF program. The crypto context returned by this function must either\nkernel/bpf/crypto.c:87: * be embedded in a map as a kptr, or freed with bpf_crypto_ctx_release().\nkernel/bpf/crypto.c-88- * As this uses a GFP_KERNEL allocation, this function can only be used in\n--\nkernel/bpf/crypto.c-90- *\nkernel/bpf/crypto.c:91: * bpf_crypto_ctx_create() allocates memory for crypto context.\nkernel/bpf/crypto.c-92- * It may return NULL if no memory is available.\nkernel/bpf/crypto.c:93: * @params:\tpointer to struct bpf_crypto_params which contains all the\nkernel/bpf/crypto.c-94- *\t\tdetails needed to initialise crypto context.\nkernel/bpf/crypto.c:95: * @params__sz:\tsize of struct bpf_crypto_params used by bpf program\nkernel/bpf/crypto.c-96- * @err_ret:\tinteger to store error code when NULL is returned.\nkernel/bpf/crypto.c-97- */\nkernel/bpf/crypto.c:98:__bpf_kfunc struct bpf_crypto_ctx *\nkernel/bpf/crypto.c:99:bpf_crypto_ctx_create(const struct bpf_crypto_params *params, u32 params__sz,\nkernel/bpf/crypto.c-100-\t\t      int *err_ret)\nkernel/bpf/crypto.c-101-{\nkernel/bpf/crypto.c:102:\tstruct bpf_crypto_ctx *ctx;\nkernel/bpf/crypto.c-103-\tint err;\n--\nkernel/bpf/crypto.c-105-\tif (!params || params-\u003ereserved[0] || params-\u003ereserved[1] ||\nkernel/bpf/crypto.c:106:\t    params__sz != sizeof(struct bpf_crypto_params)) {\nkernel/bpf/crypto.c-107-\t\t*err_ret = -EINVAL;\n--\nkernel/bpf/crypto.c-121-\nkernel/bpf/crypto.c:122:\tif (!bpf_crypto_find_algo(params, \u0026ctx-\u003ealgo)) {\nkernel/bpf/crypto.c-123-\t\terr = -EOPNOTSUPP;\n--\nkernel/bpf/crypto.c=153=static void crypto_free_cb(struct rcu_head *head)\nkernel/bpf/crypto.c-154-{\nkernel/bpf/crypto.c:155:\tstruct bpf_crypto_ctx *ctx;\nkernel/bpf/crypto.c-156-\nkernel/bpf/crypto.c:157:\tctx = container_of(head, struct bpf_crypto_ctx, rcu);\nkernel/bpf/crypto.c-158-\tkfree_sensitive(ctx);\n--\nkernel/bpf/crypto.c-161-/**\nkernel/bpf/crypto.c:162: * bpf_crypto_ctx_acquire() - Acquire a reference to a BPF crypto context.\nkernel/bpf/crypto.c-163- * @ctx: The BPF crypto context being acquired. The ctx must be a trusted\n--\nkernel/bpf/crypto.c-167- * must either be embedded in a map as a kptr, or freed with\nkernel/bpf/crypto.c:168: * bpf_crypto_ctx_release().\nkernel/bpf/crypto.c-169- */\nkernel/bpf/crypto.c:170:__bpf_kfunc struct bpf_crypto_ctx *\nkernel/bpf/crypto.c:171:bpf_crypto_ctx_acquire(struct bpf_crypto_ctx *ctx)\nkernel/bpf/crypto.c-172-{\n--\nkernel/bpf/crypto.c-178-/**\nkernel/bpf/crypto.c:179: * bpf_crypto_ctx_release() - Release a previously acquired BPF crypto context.\nkernel/bpf/crypto.c-180- * @ctx: The crypto context being released.\n--\nkernel/bpf/crypto.c-185- */\nkernel/bpf/crypto.c:186:__bpf_kfunc void bpf_crypto_ctx_release(struct bpf_crypto_ctx *ctx)\nkernel/bpf/crypto.c-187-{\n--\nkernel/bpf/crypto.c-191-\nkernel/bpf/crypto.c:192:__bpf_kfunc void bpf_crypto_ctx_release_dtor(void *ctx)\nkernel/bpf/crypto.c-193-{\nkernel/bpf/crypto.c:194:\tbpf_crypto_ctx_release(ctx);\nkernel/bpf/crypto.c-195-}\nkernel/bpf/crypto.c:196:CFI_NOSEAL(bpf_crypto_ctx_release_dtor);\nkernel/bpf/crypto.c-197-\nkernel/bpf/crypto.c=198=static int bpf_aes_cbc_crypt(u8 *dst, u32 dst_len, const u8 *src, u32 src_len,\nkernel/bpf/crypto.c-199-\t\t\t     u8 *iv, u32 iv_len,\nkernel/bpf/crypto.c:200:\t\t\t     const struct bpf_crypto_ctx *ctx, bool decrypt)\nkernel/bpf/crypto.c-201-{\n--\nkernel/bpf/crypto.c=213=static int bpf_aes_ecb_crypt(u8 *dst, u32 dst_len, const u8 *src, u32 src_len,\nkernel/bpf/crypto.c-214-\t\t\t     u8 *iv, u32 iv_len,\nkernel/bpf/crypto.c:215:\t\t\t     const struct bpf_crypto_ctx *ctx, bool decrypt)\nkernel/bpf/crypto.c-216-{\n--\nkernel/bpf/crypto.c-227-\nkernel/bpf/crypto.c:228:static int bpf_crypto_crypt(const struct bpf_crypto_ctx *ctx,\nkernel/bpf/crypto.c-229-\t\t\t    const struct bpf_dynptr_kern *src,\n--\nkernel/bpf/crypto.c-270-/**\nkernel/bpf/crypto.c:271: * bpf_crypto_decrypt() - Decrypt buffer using configured context and IV provided.\nkernel/bpf/crypto.c-272- * @ctx:\t\tThe crypto context being used. The ctx must be a trusted pointer.\n--\nkernel/bpf/crypto.c-278- */\nkernel/bpf/crypto.c:279:__bpf_kfunc int bpf_crypto_decrypt(struct bpf_crypto_ctx *ctx,\nkernel/bpf/crypto.c-280-\t\t\t\t   const struct bpf_dynptr *src,\n--\nkernel/bpf/crypto.c-287-\nkernel/bpf/crypto.c:288:\treturn bpf_crypto_crypt(ctx, src_kern, dst_kern, iv_kern, true);\nkernel/bpf/crypto.c-289-}\n--\nkernel/bpf/crypto.c-291-/**\nkernel/bpf/crypto.c:292: * bpf_crypto_encrypt() - Encrypt buffer using configured context and IV provided.\nkernel/bpf/crypto.c-293- * @ctx:\t\tThe crypto context being used. The ctx must be a trusted pointer.\n--\nkernel/bpf/crypto.c-299- */\nkernel/bpf/crypto.c:300:__bpf_kfunc int bpf_crypto_encrypt(struct bpf_crypto_ctx *ctx,\nkernel/bpf/crypto.c-301-\t\t\t\t   const struct bpf_dynptr *src,\n--\nkernel/bpf/crypto.c-308-\nkernel/bpf/crypto.c:309:\treturn bpf_crypto_crypt(ctx, src_kern, dst_kern, iv_kern, false);\nkernel/bpf/crypto.c-310-}\n--\nkernel/bpf/crypto.c=314=BTF_KFUNCS_START(crypt_init_kfunc_btf_ids)\nkernel/bpf/crypto.c:315:BTF_ID_FLAGS(func, bpf_crypto_ctx_create, KF_ACQUIRE | KF_RET_NULL | KF_SLEEPABLE)\nkernel/bpf/crypto.c:316:BTF_ID_FLAGS(func, bpf_crypto_ctx_release, KF_RELEASE)\nkernel/bpf/crypto.c:317:BTF_ID_FLAGS(func, bpf_crypto_ctx_acquire, KF_ACQUIRE | KF_RCU | KF_RET_NULL)\nkernel/bpf/crypto.c-318-BTF_KFUNCS_END(crypt_init_kfunc_btf_ids)\n--\nkernel/bpf/crypto.c=325=BTF_KFUNCS_START(crypt_kfunc_btf_ids)\nkernel/bpf/crypto.c:326:BTF_ID_FLAGS(func, bpf_crypto_decrypt, KF_RCU)\nkernel/bpf/crypto.c:327:BTF_ID_FLAGS(func, bpf_crypto_encrypt, KF_RCU)\nkernel/bpf/crypto.c-328-BTF_KFUNCS_END(crypt_kfunc_btf_ids)\n--\nkernel/bpf/crypto.c=330=static const struct btf_kfunc_id_set crypt_kfunc_set = {\n--\nkernel/bpf/crypto.c-334-\nkernel/bpf/crypto.c:335:BTF_ID_LIST(bpf_crypto_dtor_ids)\nkernel/bpf/crypto.c:336:BTF_ID(struct, bpf_crypto_ctx)\nkernel/bpf/crypto.c:337:BTF_ID(func, bpf_crypto_ctx_release_dtor)\nkernel/bpf/crypto.c-338-\nkernel/bpf/crypto.c=339=static int __init crypto_kfunc_init(void)\n--\nkernel/bpf/crypto.c-341-\tint ret;\nkernel/bpf/crypto.c:342:\tconst struct btf_id_dtor_kfunc bpf_crypto_dtors[] = {\nkernel/bpf/crypto.c-343-\t\t{\nkernel/bpf/crypto.c:344:\t\t\t.btf_id\t      = bpf_crypto_dtor_ids[0],\nkernel/bpf/crypto.c:345:\t\t\t.kfunc_btf_id = bpf_crypto_dtor_ids[1]\nkernel/bpf/crypto.c-346-\t\t},\n--\nkernel/bpf/crypto.c-353-\t\t\t\t\t       \u0026crypt_init_kfunc_set);\nkernel/bpf/crypto.c:354:\treturn  ret ?: register_btf_id_dtor_kfuncs(bpf_crypto_dtors,\nkernel/bpf/crypto.c:355:\t\t\t\t\t\t   ARRAY_SIZE(bpf_crypto_dtors),\nkernel/bpf/crypto.c-356-\t\t\t\t\t\t   THIS_MODULE);\n--\nkernel/bpf/verifier.c=4671=BTF_ID(struct, task_struct)\nkernel/bpf/verifier.c-4672-#ifdef CONFIG_BPF_CRYPTO\nkernel/bpf/verifier.c:4673:BTF_ID(struct, bpf_crypto_ctx)\nkernel/bpf/verifier.c-4674-#endif\n"
}

41/3 2026/09/25 08:18 llm kmsan-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 31432
							output: 33
							thoughts: 568
						

Thoughts:
**Analyzing the bpf_crypto_find_algo Function**

Okay, let's dive into these changes in `kernel/bpf/crypto.c`. Specifically, I need to focus on `bpf_crypto_find_algo`. I'm immediately interested in how the `params` struct is handled. Right, `bpf_crypto_find_algo` takes a `const struct bpf_crypto_params *params` as input.

Now, where is this `params` coming from? Ah, it's passed from a BPF program into the `bpf_crypto_ctx_create` function. The BPF verifier is responsible for ensuring that `params` points to a valid memory region of size `params__sz`, and it looks like `params__sz` is equivalent to `sizeof(struct bpf_crypto_params)`. Good, good. That seems sound so far.

Wait a second. Going back to `bpf_crypto_find_algo`, I see this line: `strncmp(bpf_crypto_algos[i].type_name, params->type, sizeof(params->type))`. This is where I start to see a potential vulnerability. Does the verifier actually guarantee that `params->type` is null-terminated? `strncmp` will compare at most `sizeof(params->type)`, which is 14 bytes here.

If it's *not* null-terminated, the comparison will proceed for the full 14 bytes, even if the actual string in `params->type` is shorter. The comparison could return zero as long as the start of the strings matches. The comparison could also start reading past the end of the memory if the string is longer, but not if `params` points to a valid memory region. It doesn't appear to be reading out-of-bounds, since the verifier ensures `params` is valid. I'm wondering if KMSAN would catch uninitialized memory. Wait... does the verifier require kfunc struct params to be initialized?

Okay, time to check how the BPF verifier handles kfunc pointers to struct arguments in more detail. I need to search the verifier code for how it deals with this. I'll search for `KF_SLEEPABLE`, or even specifically for references to `bpf_crypto_ctx_create`, to see what verification steps are in place. This will reveal if the verifier has any checks on initialization of the `params` struct or if this could be an exploitable information leak or potential security vulnerability.



42/3 2026/09/25 08:19 tool grepper
0m Args:
{
  "Expression": "bpf_crypto_ctx_create",
  "PathPrefix": "kernel/bpf/"
}

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

43/3 2026/09/25 08:19 llm kmsan-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 32955
							output: 32
							thoughts: 95
						

Thoughts:
**Analyzing Error Handling in the BPF Verifier**

Okay, let's dive into how the `err_ret` variable is managed within the verifier. First, I need to confirm the change – I see that it’s now `int *err_ret`, which used to be `int *err`. Hmm, I wonder if this naming convention, specifically the `__ret` suffix, signifies anything specific in the context of BPF kernel functions. I should investigate that further. To get a handle on this, I'll need to scrutinize `kernel/bpf/verifier.c`. I'll be looking for instances of `_ret`, `err_ret`, or potentially any occurrences of `KF_` as that might be related to kfuncs in general. This should clarify how this error return variable is treated, and whether the naming holds any significance.



44/3 2026/09/25 08:19 tool grepper
0m Args:
{
  "Expression": "_ret\\b",
  "PathPrefix": "kernel/bpf/verifier.c"
}

Results:
{
  "Output": "kernel/bpf/verifier.c=11432=static int check_get_func_ip(struct bpf_verifier_env *env)\n--\nkernel/bpf/verifier.c-11438-\t\tif (!bpf_prog_has_trampoline(env-\u003eprog)) {\nkernel/bpf/verifier.c:11439:\t\t\tverbose(env, \"func %s#%d supported only for fentry/fexit/fsession/fmod_ret programs\\n\",\nkernel/bpf/verifier.c-11440-\t\t\t\tfunc_id_name(func_id), func_id);\n--\nkernel/bpf/verifier.c=12503=static int resolve_func_arg_type(struct bpf_verifier_env *env,\n--\nkernel/bpf/verifier.c-12508-\tconst struct btf_param *args;\nkernel/bpf/verifier.c:12509:\tconst struct btf_type *ref_t, *resolve_ret;\nkernel/bpf/verifier.c-12510-\tconst struct btf *btf;\n--\nkernel/bpf/verifier.c-12533-\nkernel/bpf/verifier.c:12534:\tresolve_ret = btf_resolve_size(btf, ref_t, arg_size);\nkernel/bpf/verifier.c:12535:\tif (IS_ERR(resolve_ret)) {\nkernel/bpf/verifier.c-12536-\t\tverbose(env, \"%s reference type('%s %s') size cannot be determined: %ld\\n\",\nkernel/bpf/verifier.c-12537-\t\t\treg_arg_name(env, argno), btf_type_str(ref_t), ref_tname,\nkernel/bpf/verifier.c:12538:\t\t\tPTR_ERR(resolve_ret));\nkernel/bpf/verifier.c-12539-\t\treturn -EINVAL;\n--\nkernel/bpf/verifier.c=12897=get_kfunc_arg_type(struct bpf_verifier_env *env, struct bpf_call_arg_meta *meta,\n--\nkernel/bpf/verifier.c-12900-{\nkernel/bpf/verifier.c:12901:\tconst struct btf_type *t, *ref_t = NULL, *resolve_ret;\nkernel/bpf/verifier.c-12902-\tconst u32 *ref_id_ptr = NULL;\n--\nkernel/bpf/verifier.c-13022-\t\t}\nkernel/bpf/verifier.c:13023:\t\tresolve_ret = btf_resolve_size(meta-\u003ebtf, ref_t, \u0026type_size);\nkernel/bpf/verifier.c:13024:\t\tif (IS_ERR(resolve_ret)) {\nkernel/bpf/verifier.c-13025-\t\t\tverbose(env,\n--\nkernel/bpf/verifier.c-13027-\t\t\t\treg_arg_name(env, argno), btf_type_str(ref_t),\nkernel/bpf/verifier.c:13028:\t\t\t\tref_tname, PTR_ERR(resolve_ret));\nkernel/bpf/verifier.c-13029-\t\t\treturn -EINVAL;\n--\nkernel/bpf/verifier.c-13082-\t\t}\nkernel/bpf/verifier.c:13083:\t\tresolve_ret = btf_resolve_size(meta-\u003ebtf, ref_t, \u0026type_size);\nkernel/bpf/verifier.c:13084:\t\tif (IS_ERR(resolve_ret)) {\nkernel/bpf/verifier.c-13085-\t\t\tverbose(env,\n--\nkernel/bpf/verifier.c-13087-\t\t\t\treg_arg_name(env, argno), btf_type_str(ref_t),\nkernel/bpf/verifier.c:13088:\t\t\t\tref_tname, PTR_ERR(resolve_ret));\nkernel/bpf/verifier.c-13089-\t\t\treturn -EINVAL;\n--\nkernel/bpf/verifier.c=15036=static int adjust_ptr_min_max_vals(struct bpf_verifier_env *env, struct bpf_insn *insn,\n--\nkernel/bpf/verifier.c-15049-\tconst char *reason;\nkernel/bpf/verifier.c:15050:\tint ret, bounds_ret;\nkernel/bpf/verifier.c-15051-\n--\nkernel/bpf/verifier.c-15288-\treg_bounds_sync(dst_reg);\nkernel/bpf/verifier.c:15289:\tbounds_ret = sanitize_check_bounds(env, insn, dst_reg);\nkernel/bpf/verifier.c:15290:\tif (bounds_ret == -EACCES)\nkernel/bpf/verifier.c:15291:\t\treturn bounds_ret;\nkernel/bpf/verifier.c-15292-\tif (sanitize_needed(opcode)) {\n--\nkernel/bpf/verifier.c-15296-\t\t\t\t    \u0026\u0026 !env-\u003ecur_state-\u003especulative\nkernel/bpf/verifier.c:15297:\t\t\t\t    \u0026\u0026 bounds_ret\nkernel/bpf/verifier.c-15298-\t\t\t\t    \u0026\u0026 !ret,\n--\nkernel/bpf/verifier.c=20372=static int check_attach_sleepable(u32 btf_id, unsigned long addr, const char *func_name)\nkernel/bpf/verifier.c-20373-{\nkernel/bpf/verifier.c:20374:\t/* fentry/fexit/fmod_ret progs can be sleepable if they are\nkernel/bpf/verifier.c-20375-\t * attached to ALLOW_ERROR_INJECTION and are not in denylist.\n--\nkernel/bpf/verifier.c=20399=static bool has_arch_syscall_prefix(const char *func_name)\n--\nkernel/bpf/verifier.c-20419-\nkernel/bpf/verifier.c:20420:/* Without error injection, allow sleepable and fmod_ret progs on syscalls. */\nkernel/bpf/verifier.c-20421-\n--\nkernel/bpf/verifier.c=20446=static int btf_id_allow_sleepable(u32 btf_id, unsigned long addr, const struct bpf_prog *prog,\n--\nkernel/bpf/verifier.c-20473-\t\t/*\nkernel/bpf/verifier.c:20474:\t\t * fentry/fexit/fmod_ret progs can also be sleepable if they are\nkernel/bpf/verifier.c-20475-\t\t * in the fmodret id set with the KF_SLEEPABLE flag.\n--\nkernel/bpf/verifier.c=20921=BTF_SET_END(btf_id_deny)\nkernel/bpf/verifier.c-20922-\nkernel/bpf/verifier.c:20923:/* fexit and fmod_ret can't be used to attach to __noreturn functions.\nkernel/bpf/verifier.c-20924- * Currently, we must manually list all __noreturn functions here. Once a more\n--\nkernel/bpf/verifier.c=20976=static int check_attach_btf_id(struct bpf_verifier_env *env)\n--\nkernel/bpf/verifier.c-21050-\t\t   btf_id_set_contains(\u0026noreturn_deny, btf_id)) {\nkernel/bpf/verifier.c:21051:\t\tverbose(env, \"Attaching fexit/fsession/fmod_ret to __noreturn function '%s' is rejected.\\n\",\nkernel/bpf/verifier.c-21052-\t\t\ttgt_info.tgt_name);\n"
}

45/3 2026/09/25 08:19 llm kmsan-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 35288
							output: 35
							thoughts: 20
						

Thoughts:
Okay, here's my summary, tailored for an expert audience, as if I'm thinking these things myself:

**Investigating the `is_kfunc_arg_` Verification Logic**

Alright, time to dive into this. I need to understand how the verifier handles kernel function arguments. My gut feeling is that there might be some nuance or a potential vulnerability I'm interested in exploring related to argument validation within `verifier.c`. So, first things first: I'm going to jump right into the code. Let's start with a focused search. Let's look for the function or macro name I'm interested in, which is clearly: `is_kfunc_arg_`. That seems like the right jumping off point to understand its use and how it validates these arguments. I'm expecting to use this function to get a good handle of how function arguments are validated by the verifier. I'll need to know: what checks are performed, under what conditions does this return true, and false, and how that value is ultimately used.



46/3 2026/09/25 08:19 tool grepper
0m Args:
{
  "Expression": "is_kfunc_arg_",
  "PathPrefix": "kernel/bpf/verifier.c"
}

Results:
{
  "Output": "kernel/bpf/verifier.c=7828=static bool is_iter_destroy_kfunc(struct bpf_call_arg_meta *meta)\n--\nkernel/bpf/verifier.c-7832-\nkernel/bpf/verifier.c:7833:static bool is_kfunc_arg_iter(struct bpf_call_arg_meta *meta, int arg_idx,\nkernel/bpf/verifier.c-7834-\t\t\t      const struct btf_param *arg)\n--\nkernel/bpf/verifier.c=8270=static int process_arg_ptr_to_btf_id(struct bpf_verifier_env *env, struct bpf_reg_state *reg,\n--\nkernel/bpf/verifier.c-8273-\t\t\t\t     struct bpf_call_arg_meta *meta, int insn_idx);\nkernel/bpf/verifier.c:8274:static bool is_kfunc_arg_nonown_allowed(const struct btf *btf,\nkernel/bpf/verifier.c-8275-\t\t\t\t\tconst struct btf_param *arg);\nkernel/bpf/verifier.c:8276:static bool is_kfunc_arg_scalar_with_name(const struct btf *btf,\nkernel/bpf/verifier.c-8277-\t\t\t\t\t  const struct btf_param *arg,\n--\nkernel/bpf/verifier.c=8934=static int check_func_arg(struct bpf_verifier_env *env, u32 arg, u32 slot, u32 prev_slot,\n--\nkernel/bpf/verifier.c-9358-\tcase ARG_PTR_TO_LIST_NODE:\nkernel/bpf/verifier.c:9359:\t\tif (!(is_kfunc_arg_nonown_allowed(meta-\u003ebtf, btf_arg) \u0026\u0026\nkernel/bpf/verifier.c-9360-\t\t      type_is_non_owning_ref(reg-\u003etype) \u0026\u0026 !reg_is_referenced(env, reg))) {\n--\nkernel/bpf/verifier.c-9402-\tcase ARG_CONST_ALLOC_SIZE_OR_ZERO:\nkernel/bpf/verifier.c:9403:\t\tif (meta-\u003ebtf \u0026\u0026 is_kfunc_arg_scalar_with_name(meta-\u003ebtf, btf_arg,\nkernel/bpf/verifier.c-9404-\t\t\t\t\t\t\t       \"rdonly_buf_size\"))\n--\nkernel/bpf/verifier.c=12144=static bool is_kfunc_rcu_protected(struct bpf_call_arg_meta *meta)\n--\nkernel/bpf/verifier.c-12148-\nkernel/bpf/verifier.c:12149:static bool is_kfunc_arg_mem_size(const struct btf *btf,\nkernel/bpf/verifier.c-12150-\t\t\t\t  const struct btf_param *arg)\n--\nkernel/bpf/verifier.c-12160-\nkernel/bpf/verifier.c:12161:static bool is_kfunc_arg_const_mem_size(const struct btf *btf,\nkernel/bpf/verifier.c-12162-\t\t\t\t\tconst struct btf_param *arg)\n--\nkernel/bpf/verifier.c-12172-\nkernel/bpf/verifier.c:12173:static bool is_kfunc_arg_constant(const struct btf *btf, const struct btf_param *arg)\nkernel/bpf/verifier.c-12174-{\n--\nkernel/bpf/verifier.c-12177-\nkernel/bpf/verifier.c:12178:static bool is_kfunc_arg_ignore(const struct btf *btf, const struct btf_param *arg)\nkernel/bpf/verifier.c-12179-{\n--\nkernel/bpf/verifier.c-12182-\nkernel/bpf/verifier.c:12183:static bool is_kfunc_arg_map(const struct btf *btf, const struct btf_param *arg)\nkernel/bpf/verifier.c-12184-{\n--\nkernel/bpf/verifier.c-12187-\nkernel/bpf/verifier.c:12188:static bool is_kfunc_arg_const_map(const struct btf *btf, const struct btf_param *arg)\nkernel/bpf/verifier.c-12189-{\n--\nkernel/bpf/verifier.c-12192-\nkernel/bpf/verifier.c:12193:static bool is_kfunc_arg_alloc_obj(const struct btf *btf, const struct btf_param *arg)\nkernel/bpf/verifier.c-12194-{\n--\nkernel/bpf/verifier.c-12197-\nkernel/bpf/verifier.c:12198:static bool is_kfunc_arg_uninit(const struct btf *btf, const struct btf_param *arg)\nkernel/bpf/verifier.c-12199-{\n--\nkernel/bpf/verifier.c-12202-\nkernel/bpf/verifier.c:12203:static bool is_kfunc_arg_refcounted_kptr(const struct btf *btf, const struct btf_param *arg)\nkernel/bpf/verifier.c-12204-{\n--\nkernel/bpf/verifier.c-12207-\nkernel/bpf/verifier.c:12208:static bool is_kfunc_arg_nullable(const struct btf *btf, const struct btf_param *arg)\nkernel/bpf/verifier.c-12209-{\n--\nkernel/bpf/verifier.c-12213-\nkernel/bpf/verifier.c:12214:static bool is_kfunc_arg_nonown_allowed(const struct btf *btf, const struct btf_param *arg)\nkernel/bpf/verifier.c-12215-{\n--\nkernel/bpf/verifier.c-12218-\nkernel/bpf/verifier.c:12219:static bool is_kfunc_arg_const_str(const struct btf *btf, const struct btf_param *arg)\nkernel/bpf/verifier.c-12220-{\n--\nkernel/bpf/verifier.c-12223-\nkernel/bpf/verifier.c:12224:static bool is_kfunc_arg_irq_flag(const struct btf *btf, const struct btf_param *arg)\nkernel/bpf/verifier.c-12225-{\n--\nkernel/bpf/verifier.c-12228-\nkernel/bpf/verifier.c:12229:static bool is_kfunc_arg_ctx_out(const struct btf *btf, const struct btf_param *arg)\nkernel/bpf/verifier.c-12230-{\n--\nkernel/bpf/verifier.c-12233-\nkernel/bpf/verifier.c:12234:static bool is_kfunc_arg_arena(const struct btf *btf, const struct btf_param *arg)\nkernel/bpf/verifier.c-12235-{\n--\nkernel/bpf/verifier.c-12239-\nkernel/bpf/verifier.c:12240:static bool is_kfunc_arg_scalar_with_name(const struct btf *btf,\nkernel/bpf/verifier.c-12241-\t\t\t\t\t  const struct btf_param *arg,\n--\nkernel/bpf/verifier.c=12286=static bool __is_kfunc_ptr_arg_type(const struct btf *btf,\n--\nkernel/bpf/verifier.c-12302-\nkernel/bpf/verifier.c:12303:static bool is_kfunc_arg_dynptr(const struct btf *btf, const struct btf_param *arg)\nkernel/bpf/verifier.c-12304-{\n--\nkernel/bpf/verifier.c-12307-\nkernel/bpf/verifier.c:12308:static bool is_kfunc_arg_list_head(const struct btf *btf, const struct btf_param *arg)\nkernel/bpf/verifier.c-12309-{\n--\nkernel/bpf/verifier.c-12312-\nkernel/bpf/verifier.c:12313:static bool is_kfunc_arg_list_node(const struct btf *btf, const struct btf_param *arg)\nkernel/bpf/verifier.c-12314-{\n--\nkernel/bpf/verifier.c-12317-\nkernel/bpf/verifier.c:12318:static bool is_kfunc_arg_rbtree_root(const struct btf *btf, const struct btf_param *arg)\nkernel/bpf/verifier.c-12319-{\n--\nkernel/bpf/verifier.c-12322-\nkernel/bpf/verifier.c:12323:static bool is_kfunc_arg_rbtree_node(const struct btf *btf, const struct btf_param *arg)\nkernel/bpf/verifier.c-12324-{\n--\nkernel/bpf/verifier.c-12327-\nkernel/bpf/verifier.c:12328:static bool is_kfunc_arg_timer(const struct btf *btf, const struct btf_param *arg)\nkernel/bpf/verifier.c-12329-{\n--\nkernel/bpf/verifier.c-12332-\nkernel/bpf/verifier.c:12333:static bool is_kfunc_arg_wq(const struct btf *btf, const struct btf_param *arg)\nkernel/bpf/verifier.c-12334-{\n--\nkernel/bpf/verifier.c-12337-\nkernel/bpf/verifier.c:12338:static bool is_kfunc_arg_task_work(const struct btf *btf, const struct btf_param *arg)\nkernel/bpf/verifier.c-12339-{\n--\nkernel/bpf/verifier.c-12342-\nkernel/bpf/verifier.c:12343:static bool is_kfunc_arg_rcu_head(const struct btf *btf, const struct btf_param *arg)\nkernel/bpf/verifier.c-12344-{\n--\nkernel/bpf/verifier.c-12347-\nkernel/bpf/verifier.c:12348:static bool is_kfunc_arg_res_spin_lock(const struct btf *btf, const struct btf_param *arg)\nkernel/bpf/verifier.c-12349-{\n--\nkernel/bpf/verifier.c=12358=static bool is_list_node_type(const struct btf_type *t)\n--\nkernel/bpf/verifier.c-12362-\nkernel/bpf/verifier.c:12363:static bool is_kfunc_arg_callback(struct bpf_verifier_env *env, const struct btf *btf,\nkernel/bpf/verifier.c-12364-\t\t\t\t  const struct btf_param *arg)\n--\nkernel/bpf/verifier.c-12374-\nkernel/bpf/verifier.c:12375:static bool is_kfunc_arg_prog_aux(const struct btf *btf, const struct btf_param *arg)\nkernel/bpf/verifier.c-12376-{\n--\nkernel/bpf/verifier.c-12386- */\nkernel/bpf/verifier.c:12387:static bool is_kfunc_arg_implicit(const struct bpf_call_arg_meta *meta, u32 arg_idx)\nkernel/bpf/verifier.c-12388-{\n--\nkernel/bpf/verifier.c=12503=static int resolve_func_arg_type(struct bpf_verifier_env *env,\n--\nkernel/bpf/verifier.c-12526-\tref_id = *meta-\u003efn-\u003earg_btf_id[arg];\nkernel/bpf/verifier.c:12527:\tbtf = is_kfunc_arg_map(meta-\u003ebtf, \u0026args[arg]) ? btf_vmlinux : meta-\u003ebtf;\nkernel/bpf/verifier.c-12528-\tref_t = btf_type_skip_modifiers(btf, ref_id, \u0026ref_id);\n--\nkernel/bpf/verifier.c=12897=get_kfunc_arg_type(struct bpf_verifier_env *env, struct bpf_call_arg_meta *meta,\n--\nkernel/bpf/verifier.c-12909-\nkernel/bpf/verifier.c:12910:\tif (is_kfunc_arg_prog_aux(meta-\u003ebtf, \u0026args[arg]))\nkernel/bpf/verifier.c-12911-\t\treturn ARG_PTR_TO_PROG_AUX;\nkernel/bpf/verifier.c-12912-\nkernel/bpf/verifier.c:12913:\tif (is_kfunc_arg_ignore(meta-\u003ebtf, \u0026args[arg]) || is_kfunc_arg_implicit(meta, arg))\nkernel/bpf/verifier.c-12914-\t\treturn ARG_IGNORE;\n--\nkernel/bpf/verifier.c-12919-\tif (btf_type_is_scalar(t)) {\nkernel/bpf/verifier.c:12920:\t\tif (is_kfunc_arg_constant(meta-\u003ebtf, \u0026args[arg]))\nkernel/bpf/verifier.c-12921-\t\t\treturn ARG_CONST_SCALAR;\nkernel/bpf/verifier.c:12922:\t\tif (is_kfunc_arg_const_mem_size(meta-\u003ebtf, \u0026args[arg]))\nkernel/bpf/verifier.c-12923-\t\t\treturn ARG_CONST_MEM_SIZE;\nkernel/bpf/verifier.c:12924:\t\tif (is_kfunc_arg_mem_size(meta-\u003ebtf, \u0026args[arg]))\nkernel/bpf/verifier.c-12925-\t\t\treturn ARG_MEM_SIZE;\nkernel/bpf/verifier.c:12926:\t\tif (is_kfunc_arg_scalar_with_name(meta-\u003ebtf, \u0026args[arg], \"rdonly_buf_size\") ||\nkernel/bpf/verifier.c:12927:\t\t    is_kfunc_arg_scalar_with_name(meta-\u003ebtf, \u0026args[arg], \"rdwr_buf_size\"))\nkernel/bpf/verifier.c-12928-\t\t\treturn ARG_CONST_ALLOC_SIZE_OR_ZERO;\n--\nkernel/bpf/verifier.c-12971-\t\targ_type = ARG_PTR_TO_CTX;\nkernel/bpf/verifier.c:12972:\telse if (is_kfunc_arg_alloc_obj(meta-\u003ebtf, \u0026args[arg]))\nkernel/bpf/verifier.c-12973-\t\targ_type = ARG_PTR_TO_ALLOC_BTF_ID;\nkernel/bpf/verifier.c:12974:\telse if (is_kfunc_arg_refcounted_kptr(meta-\u003ebtf, \u0026args[arg]))\nkernel/bpf/verifier.c-12975-\t\targ_type = ARG_PTR_TO_REFCOUNTED_KPTR;\nkernel/bpf/verifier.c:12976:\telse if (is_kfunc_arg_dynptr(meta-\u003ebtf, \u0026args[arg])) {\nkernel/bpf/verifier.c-12977-\t\targ_type = ARG_PTR_TO_DYNPTR;\n--\nkernel/bpf/verifier.c-12988-\t\t\targ_type |= DYNPTR_TYPE_FILE;\nkernel/bpf/verifier.c:12989:\t} else if (is_kfunc_arg_iter(meta, arg, \u0026args[arg]))\nkernel/bpf/verifier.c-12990-\t\targ_type = ARG_PTR_TO_ITER;\nkernel/bpf/verifier.c:12991:\telse if (is_kfunc_arg_list_head(meta-\u003ebtf, \u0026args[arg]))\nkernel/bpf/verifier.c-12992-\t\targ_type = ARG_PTR_TO_LIST_HEAD;\nkernel/bpf/verifier.c:12993:\telse if (is_kfunc_arg_list_node(meta-\u003ebtf, \u0026args[arg]))\nkernel/bpf/verifier.c-12994-\t\targ_type = ARG_PTR_TO_LIST_NODE;\nkernel/bpf/verifier.c:12995:\telse if (is_kfunc_arg_rbtree_root(meta-\u003ebtf, \u0026args[arg]))\nkernel/bpf/verifier.c-12996-\t\targ_type = ARG_PTR_TO_RB_ROOT;\nkernel/bpf/verifier.c:12997:\telse if (is_kfunc_arg_rbtree_node(meta-\u003ebtf, \u0026args[arg]))\nkernel/bpf/verifier.c-12998-\t\targ_type = ARG_PTR_TO_RB_NODE;\nkernel/bpf/verifier.c:12999:\telse if (is_kfunc_arg_const_str(meta-\u003ebtf, \u0026args[arg]))\nkernel/bpf/verifier.c-13000-\t\targ_type = ARG_PTR_TO_CONST_STR;\nkernel/bpf/verifier.c:13001:\telse if (is_kfunc_arg_const_map(meta-\u003ebtf, \u0026args[arg]))\nkernel/bpf/verifier.c-13002-\t\targ_type = ARG_CONST_MAP_PTR;\nkernel/bpf/verifier.c:13003:\telse if (is_kfunc_arg_map(meta-\u003ebtf, \u0026args[arg]))\nkernel/bpf/verifier.c-13004-\t\targ_type = ARG_PTR_TO_BTF_ID;\nkernel/bpf/verifier.c:13005:\telse if (is_kfunc_arg_wq(meta-\u003ebtf, \u0026args[arg]))\nkernel/bpf/verifier.c-13006-\t\targ_type = ARG_PTR_TO_WORKQUEUE;\nkernel/bpf/verifier.c:13007:\telse if (is_kfunc_arg_timer(meta-\u003ebtf, \u0026args[arg]))\nkernel/bpf/verifier.c-13008-\t\targ_type = ARG_PTR_TO_TIMER;\nkernel/bpf/verifier.c:13009:\telse if (is_kfunc_arg_task_work(meta-\u003ebtf, \u0026args[arg]))\nkernel/bpf/verifier.c-13010-\t\targ_type = ARG_PTR_TO_TASK_WORK;\nkernel/bpf/verifier.c:13011:\telse if (is_kfunc_arg_rcu_head(meta-\u003ebtf, \u0026args[arg]))\nkernel/bpf/verifier.c-13012-\t\targ_type = ARG_PTR_TO_RCU_HEAD;\nkernel/bpf/verifier.c:13013:\telse if (is_kfunc_arg_irq_flag(meta-\u003ebtf, \u0026args[arg]))\nkernel/bpf/verifier.c-13014-\t\targ_type = ARG_PTR_TO_IRQ_FLAG;\nkernel/bpf/verifier.c:13015:\telse if (is_kfunc_arg_res_spin_lock(meta-\u003ebtf, \u0026args[arg]))\nkernel/bpf/verifier.c-13016-\t\targ_type = ARG_PTR_TO_RES_SPIN_LOCK;\nkernel/bpf/verifier.c:13017:\telse if (is_kfunc_arg_ctx_out(meta-\u003ebtf, \u0026args[arg])) {\nkernel/bpf/verifier.c-13018-\t\tif (!btf_type_is_scalar(ref_t)) {\n--\nkernel/bpf/verifier.c-13032-\t\targ_type = ARG_PTR_TO_CTX_OUT | MEM_FIXED_SIZE;\nkernel/bpf/verifier.c:13033:\t} else if (is_kfunc_arg_callback(env, meta-\u003ebtf, \u0026args[arg]))\nkernel/bpf/verifier.c-13034-\t\targ_type = ARG_PTR_TO_FUNC;\nkernel/bpf/verifier.c:13035:\telse if (is_kfunc_arg_arena(meta-\u003ebtf, \u0026args[arg])) {\nkernel/bpf/verifier.c-13036-\t\tif (!bpf_jit_supports_arena_args()) {\n--\nkernel/bpf/verifier.c-13058-\t} else if (arg + 1 \u003c nargs \u0026\u0026\nkernel/bpf/verifier.c:13059:\t\t (is_kfunc_arg_mem_size(meta-\u003ebtf, \u0026args[arg + 1]) ||\nkernel/bpf/verifier.c:13060:\t\t  is_kfunc_arg_const_mem_size(meta-\u003ebtf, \u0026args[arg + 1]))) {\nkernel/bpf/verifier.c-13061-\t\tif (!btf_type_is_void(ref_t) \u0026\u0026 !btf_type_is_scalar(ref_t) \u0026\u0026\n--\nkernel/bpf/verifier.c-13094-\nkernel/bpf/verifier.c:13095:\tif (is_kfunc_arg_uninit(meta-\u003ebtf, \u0026args[arg]))\nkernel/bpf/verifier.c-13096-\t\targ_type |= MEM_UNINIT;\nkernel/bpf/verifier.c-13097-\nkernel/bpf/verifier.c:13098:\tif (is_kfunc_arg_nullable(meta-\u003ebtf, \u0026args[arg]))\nkernel/bpf/verifier.c-13099-\t\targ_type |= PTR_MAYBE_NULL;\n--\nkernel/bpf/verifier.c-13108-\tif (base_type(arg_type) == ARG_PTR_TO_BTF_ID) {\nkernel/bpf/verifier.c:13109:\t\tif (is_kfunc_arg_map(meta-\u003ebtf, \u0026args[arg]))\nkernel/bpf/verifier.c-13110-\t\t\tproto-\u003earg_btf_id[arg] = reg2btf_ids[CONST_PTR_TO_MAP];\n--\nkernel/bpf/verifier.c=13936=s64 bpf_kfunc_stack_access_bytes(struct bpf_verifier_env *env, struct bpf_insn *insn,\n--\nkernel/bpf/verifier.c-13969-\t/* dynptr: fixed 16-byte on-stack representation */\nkernel/bpf/verifier.c:13970:\tif (is_kfunc_arg_dynptr(btf, \u0026args[i])) {\nkernel/bpf/verifier.c-13971-\t\tsize = BPF_DYNPTR_SIZE;\n--\nkernel/bpf/verifier.c-14000-\t\treturn -size;\nkernel/bpf/verifier.c:14001:\tif (is_kfunc_arg_uninit(btf, \u0026args[i]) \u0026\u0026\nkernel/bpf/verifier.c:14002:\t    (is_kfunc_arg_dynptr(btf, \u0026args[i]) || env-\u003eallow_uninit_stack))\nkernel/bpf/verifier.c-14003-\t\treturn -size;\n"
}

47/3 2026/09/25 08:19 llm kmsan-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 41675
							output: 38
							thoughts: 21
						

Thoughts:
Here's my summary, as if I were thinking these thoughts:

**Diving into the `is_kfunc_arg_uninit` Check**

Okay, time to zero in on the `is_kfunc_arg_uninit` function within `verifier.c`. I need to understand its logic and how it’s handling uninitialized kernel function arguments. This is critical for ensuring memory safety and preventing potential vulnerabilities. I need to refresh my memory on the surrounding context. What specific scenarios is this check addressing? Are there any known areas of concern or recent patches that touch this code? Let’s trace through its implementation and see how it determines if an argument might be used before initialization. I'll be looking for any potential blind spots or edge cases that might allow for a bypass of the check. Understanding this is going to be vital for maintaining code robustness.



48/3 2026/09/25 08:19 tool read-file
0m Args:
{
  "File": "kernel/bpf/verifier.c",
  "FirstLine": 12195,
  "LineCount": 10
}

Results:
{
  "Contents": "12195:\t\treturn btf_param_match_suffix(btf, arg, \"__alloc\");\n12196:\t}\n12197:\t\n12198:\tstatic bool is_kfunc_arg_uninit(const struct btf *btf, const struct btf_param *arg)\n12199:\t{\n12200:\t\treturn btf_param_match_suffix(btf, arg, \"__uninit\");\n12201:\t}\n12202:\t\n12203:\tstatic bool is_kfunc_arg_refcounted_kptr(const struct btf *btf, const struct btf_param *arg)\n12204:\t{\n"
}

49/3 2026/09/25 08:19 llm kmsan-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 42110
							output: 36
							thoughts: 61
						

Thoughts:
Okay, let's see...

**Analyzing `btf_param_match_suffix` and `err_ret`**

Alright, I'm looking at this `btf_param_match_suffix(btf, arg, "__uninit")` call. Interesting, so it's checking for a suffix on a BTF parameter. But now I'm thinking, what about that `err_ret` variable? Is it also getting matched against something, or used in this suffix matching? My gut says there's a connection here I need to explore.

To understand this better, I need to trace this back. I should dive into where `btf_param_match_suffix` is *actually* being used within verifier.c. That will give me the context I need to fully grasp the intent and the surrounding logic. Time to dig in and see what's happening.



50/3 2026/09/25 08:19 tool grepper
0m Args:
{
  "Expression": "btf_param_match_suffix",
  "PathPrefix": "kernel/bpf/verifier.c"
}

Results:
{
  "Output": "kernel/bpf/verifier.c=7833=static bool is_kfunc_arg_iter(struct bpf_call_arg_meta *meta, int arg_idx,\n--\nkernel/bpf/verifier.c-7842-\t/* iter passed as an argument to a generic kfunc */\nkernel/bpf/verifier.c:7843:\treturn btf_param_match_suffix(meta-\u003ebtf, arg, \"__iter\");\nkernel/bpf/verifier.c-7844-}\n--\nkernel/bpf/verifier.c=12149=static bool is_kfunc_arg_mem_size(const struct btf *btf,\n--\nkernel/bpf/verifier.c-12157-\nkernel/bpf/verifier.c:12158:\treturn btf_param_match_suffix(btf, arg, \"__sz\");\nkernel/bpf/verifier.c-12159-}\n--\nkernel/bpf/verifier.c=12161=static bool is_kfunc_arg_const_mem_size(const struct btf *btf,\n--\nkernel/bpf/verifier.c-12169-\nkernel/bpf/verifier.c:12170:\treturn btf_param_match_suffix(btf, arg, \"__szk\");\nkernel/bpf/verifier.c-12171-}\n--\nkernel/bpf/verifier.c=12173=static bool is_kfunc_arg_constant(const struct btf *btf, const struct btf_param *arg)\nkernel/bpf/verifier.c-12174-{\nkernel/bpf/verifier.c:12175:\treturn btf_param_match_suffix(btf, arg, \"__k\");\nkernel/bpf/verifier.c-12176-}\n--\nkernel/bpf/verifier.c=12178=static bool is_kfunc_arg_ignore(const struct btf *btf, const struct btf_param *arg)\nkernel/bpf/verifier.c-12179-{\nkernel/bpf/verifier.c:12180:\treturn btf_param_match_suffix(btf, arg, \"__ign\");\nkernel/bpf/verifier.c-12181-}\n--\nkernel/bpf/verifier.c=12183=static bool is_kfunc_arg_map(const struct btf *btf, const struct btf_param *arg)\nkernel/bpf/verifier.c-12184-{\nkernel/bpf/verifier.c:12185:\treturn btf_param_match_suffix(btf, arg, \"__map\");\nkernel/bpf/verifier.c-12186-}\n--\nkernel/bpf/verifier.c=12188=static bool is_kfunc_arg_const_map(const struct btf *btf, const struct btf_param *arg)\nkernel/bpf/verifier.c-12189-{\nkernel/bpf/verifier.c:12190:\treturn btf_param_match_suffix(btf, arg, \"__const_map\");\nkernel/bpf/verifier.c-12191-}\n--\nkernel/bpf/verifier.c=12193=static bool is_kfunc_arg_alloc_obj(const struct btf *btf, const struct btf_param *arg)\nkernel/bpf/verifier.c-12194-{\nkernel/bpf/verifier.c:12195:\treturn btf_param_match_suffix(btf, arg, \"__alloc\");\nkernel/bpf/verifier.c-12196-}\n--\nkernel/bpf/verifier.c=12198=static bool is_kfunc_arg_uninit(const struct btf *btf, const struct btf_param *arg)\nkernel/bpf/verifier.c-12199-{\nkernel/bpf/verifier.c:12200:\treturn btf_param_match_suffix(btf, arg, \"__uninit\");\nkernel/bpf/verifier.c-12201-}\n--\nkernel/bpf/verifier.c=12203=static bool is_kfunc_arg_refcounted_kptr(const struct btf *btf, const struct btf_param *arg)\nkernel/bpf/verifier.c-12204-{\nkernel/bpf/verifier.c:12205:\treturn btf_param_match_suffix(btf, arg, \"__refcounted_kptr\");\nkernel/bpf/verifier.c-12206-}\n--\nkernel/bpf/verifier.c=12208=static bool is_kfunc_arg_nullable(const struct btf *btf, const struct btf_param *arg)\nkernel/bpf/verifier.c-12209-{\nkernel/bpf/verifier.c:12210:\treturn btf_param_match_suffix(btf, arg, \"__nullable\") ||\nkernel/bpf/verifier.c:12211:\t       btf_param_match_suffix(btf, arg, \"__arena\");\nkernel/bpf/verifier.c-12212-}\n--\nkernel/bpf/verifier.c=12214=static bool is_kfunc_arg_nonown_allowed(const struct btf *btf, const struct btf_param *arg)\nkernel/bpf/verifier.c-12215-{\nkernel/bpf/verifier.c:12216:\treturn btf_param_match_suffix(btf, arg, \"__nonown_allowed\");\nkernel/bpf/verifier.c-12217-}\n--\nkernel/bpf/verifier.c=12219=static bool is_kfunc_arg_const_str(const struct btf *btf, const struct btf_param *arg)\nkernel/bpf/verifier.c-12220-{\nkernel/bpf/verifier.c:12221:\treturn btf_param_match_suffix(btf, arg, \"__str\");\nkernel/bpf/verifier.c-12222-}\n--\nkernel/bpf/verifier.c=12224=static bool is_kfunc_arg_irq_flag(const struct btf *btf, const struct btf_param *arg)\nkernel/bpf/verifier.c-12225-{\nkernel/bpf/verifier.c:12226:\treturn btf_param_match_suffix(btf, arg, \"__irq_flag\");\nkernel/bpf/verifier.c-12227-}\n--\nkernel/bpf/verifier.c=12229=static bool is_kfunc_arg_ctx_out(const struct btf *btf, const struct btf_param *arg)\nkernel/bpf/verifier.c-12230-{\nkernel/bpf/verifier.c:12231:\treturn btf_param_match_suffix(btf, arg, \"__ctx_out\");\nkernel/bpf/verifier.c-12232-}\n--\nkernel/bpf/verifier.c=12234=static bool is_kfunc_arg_arena(const struct btf *btf, const struct btf_param *arg)\nkernel/bpf/verifier.c-12235-{\nkernel/bpf/verifier.c:12236:\treturn btf_param_match_suffix(btf, arg, \"__arena__nullable\") ||\nkernel/bpf/verifier.c:12237:\t       btf_param_match_suffix(btf, arg, \"__arena\");\nkernel/bpf/verifier.c-12238-}\n--\nkernel/bpf/verifier.c=13936=s64 bpf_kfunc_stack_access_bytes(struct bpf_verifier_env *env, struct bpf_insn *insn,\n--\nkernel/bpf/verifier.c-13976-\tif (i + 1 \u003c nargs \u0026\u0026\nkernel/bpf/verifier.c:13977:\t    (btf_param_match_suffix(btf, \u0026args[i + 1], \"__sz\") ||\nkernel/bpf/verifier.c:13978:\t     btf_param_match_suffix(btf, \u0026args[i + 1], \"__szk\"))) {\nkernel/bpf/verifier.c-13979-\t\tint size_reg = BPF_REG_1 + arg + 1;\n"
}

51/3 2026/09/25 08:19 llm kmsan-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 44530
							output: 26
							thoughts: 3027
						

Thoughts:
**Deep Dive into a Crypto Patch**

Okay, so the initial observation was a simple variable rename, `err` to `err_ret`, to avoid shadowing a local variable within `bpf_crypto_ctx_create`. Makes sense, good for readability. I'd have to look into the commit history to ensure the actual *reason* for this simple change.

The commit header, from a syz-cluster report, tells me this is likely a fuzzing-driven patch. The diff is focused on reworking BPF crypto implementations. Seems like the code removed dynamic crypto type registration and implemented a static table for AES-CBC and AES-ECB algorithms. This immediately raises a few questions.

Firstly, the use of `strncmp` in `bpf_crypto_find_algo` with `params->type` and `params->algo` is concerning. While there's a size check, there's no null-termination enforcement. If those strings aren't null-terminated in the `params` struct, `strncmp` could read beyond the intended buffer bounds, though this is likely mitigated by the check of `params__sz`.

Secondly, the handling of `params->key_len` in `bpf_crypto_ctx_create` and its use with `aes_preparekey`. It's good that there's a check to make sure `params->key_len` is a valid AES key length, which prevents a potential crash and also makes sure `kfree_sensitive` wipes the key.

Thirdly, the `bpf_aes_cbc_crypt` function using `aes_cbc_encrypt` is a little concerning. The `aes_cbc_encrypt` updates the IV *in place*, which is unexpected. This is a very important point since this happens inside `bpf_crypto_crypt`, so that the IV can be modified on the application side. The checks for `iv_len` and `src_len` seem robust, preventing issues if `src_len` is not a multiple of AES block size, or `dst_len` is less than `src_len`. The `bpf_aes_ecb_crypt` function seems clean since it doesn't utilize any IV data.

Finally, the movement of `dst_len < src_len` check to individual crypto functions is a bit odd, though it makes sense in the context of specific algorithm requirements. The original code already check that `dst_len >= src_len`. This means the code can't perform in-place encryption or decryption.

Okay, overall, a thorough review. Seems this patch moved to a more static approach for AES crypto, but required careful attention to IV handling and key length validation. The null-termination of the type and algo strings within the params still nags at me, but seems to be handled by the size check, unless a crafty user can somehow overflow or cause unintended read.



52/3 2026/09/25 08:19 tool grepper
0m Args:
{
  "Expression": "void \\*__bpf_dynptr_data_rw"
}

Results:
{
  "Output": "include/linux/bpf.h=1536=const void *__bpf_dynptr_data(const struct bpf_dynptr_kern *ptr, u64 len);\ninclude/linux/bpf.h:1537:void *__bpf_dynptr_data_rw(const struct bpf_dynptr_kern *ptr, u64 len);\ninclude/linux/bpf.h-1538-bool __bpf_dynptr_is_rdonly(const struct bpf_dynptr_kern *ptr);\n--\nkernel/bpf/helpers.c=5248=const void *__bpf_dynptr_data(const struct bpf_dynptr_kern *ptr, u64 len)\n--\nkernel/bpf/helpers.c-5258- */\nkernel/bpf/helpers.c:5259:void *__bpf_dynptr_data_rw(const struct bpf_dynptr_kern *ptr, u64 len)\nkernel/bpf/helpers.c-5260-{\n"
}

53/3 2026/09/25 08:19 llm kmsan-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 48460
							output: 37
							thoughts: 21
						

Thoughts:
Here's a summary of my thought process, keeping in mind my field of expertise:

**Diving into the BPF Helper Implementation**

Okay, time to troubleshoot this BPF issue. I'm going to start by looking at a core piece of the BPF infrastructure: `kernel/bpf/helpers.c`. Specifically, I want to jump directly to line 5259. That's where I have a hunch the problem might be rooted, given the symptoms I'm seeing. This helper function is likely involved in the area of…[*your area of expertise related to BPF - e.g., memory management, network processing, tracepoint handling, etc.*]…and knowing this codebase, line 5259 likely involves [ *mention a specific function, data structure, or logical flow from your expert knowledge - e.g. the allocation of bpf_map_entry*]. By pinpointing that line, I can immediately start tracing the execution path and understand how the code behaves and how it fits into the broader picture. My past experience tells me that this is often where the issue may be.



54/3 2026/09/25 08:19 tool read-file
0m Args:
{
  "File": "kernel/bpf/helpers.c",
  "FirstLine": 5250,
  "LineCount": 30
}

Results:
{
  "Contents": "5250:\t\tconst struct bpf_dynptr *p = (struct bpf_dynptr *)ptr;\n5251:\t\n5252:\t\treturn bpf_dynptr_slice(p, 0, NULL, len);\n5253:\t}\n5254:\t\n5255:\t/* Get a pointer to dynptr data up to len bytes for read write access. If\n5256:\t * the dynptr doesn't have continuous data up to len bytes, or the dynptr\n5257:\t * is read only, return NULL.\n5258:\t */\n5259:\tvoid *__bpf_dynptr_data_rw(const struct bpf_dynptr_kern *ptr, u64 len)\n5260:\t{\n5261:\t\tif (__bpf_dynptr_is_rdonly(ptr))\n5262:\t\t\treturn NULL;\n5263:\t\treturn (void *)__bpf_dynptr_data(ptr, len);\n5264:\t}\n5265:\t\n5266:\tvoid bpf_map_free_internal_structs(struct bpf_map *map, void *val)\n5267:\t{\n5268:\t\tif (btf_record_has_field(map-\u003erecord, BPF_TIMER))\n5269:\t\t\tbpf_obj_free_timer(map-\u003erecord, val);\n5270:\t\tif (btf_record_has_field(map-\u003erecord, BPF_WORKQUEUE))\n5271:\t\t\tbpf_obj_free_workqueue(map-\u003erecord, val);\n5272:\t\tif (btf_record_has_field(map-\u003erecord, BPF_TASK_WORK))\n5273:\t\t\tbpf_obj_free_task_work(map-\u003erecord, val);\n5274:\t}\n"
}

55/3 2026/09/25 08:19 llm kmsan-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 49231
							output: 33
							thoughts: 24
						

Thoughts:
Okay, here's my take on those thoughts, tailored for an expert audience, and written as if they were my own:

**Investigating `bpf_dynptr_slice`**

Alright, time to dig into `bpf_dynptr_slice`. I need to get a solid understanding of how this helper functions within the BPF ecosystem. Let me pull up `kernel/bpf/helpers.c` and start dissecting the code. I need to refresh my memory on the implementation details and how it interacts with the dynamic pointer mechanism. I need to see how the slicing is handled and what checks are in place to prevent out-of-bounds access or other potential vulnerabilities. Specifically, I'm interested in the interplay between the `dynptr` structure, the offset, and length parameters. I'll be looking for any potential optimization opportunities and edge case behaviors that might be relevant for performance or security. Let's see what we can find.



56/3 2026/09/25 08:19 tool grepper
0m Args:
{
  "Expression": "bpf_dynptr_slice\\(",
  "PathPrefix": "kernel/bpf/"
}

Results:
{
  "Output": "kernel/bpf/helpers.c=3047=__bpf_kfunc struct task_struct *bpf_task_from_vpid(s32 vpid)\n--\nkernel/bpf/helpers.c-3062-/**\nkernel/bpf/helpers.c:3063: * bpf_dynptr_slice() - Obtain a read-only pointer to the dynptr data.\nkernel/bpf/helpers.c-3064- * @p: The dynptr whose data slice to retrieve\n--\nkernel/bpf/helpers.c-3089- */\nkernel/bpf/helpers.c:3090:__bpf_kfunc void *bpf_dynptr_slice(const struct bpf_dynptr *p, u64 offset,\nkernel/bpf/helpers.c-3091-\t\t\t\t   void *buffer__nullable, u64 buffer__szk)\n--\nkernel/bpf/helpers.c=3180=__bpf_kfunc void *bpf_dynptr_slice_rdwr(const struct bpf_dynptr *p, u64 offset,\n--\nkernel/bpf/helpers.c-3209-\t */\nkernel/bpf/helpers.c:3210:\treturn bpf_dynptr_slice(p, offset, buffer__nullable, buffer__szk);\nkernel/bpf/helpers.c-3211-}\n--\nkernel/bpf/helpers.c=3286=__bpf_kfunc int bpf_dynptr_copy(const struct bpf_dynptr *dst_ptr, u64 dst_off,\n--\nkernel/bpf/helpers.c-3294-\nkernel/bpf/helpers.c:3295:\tsrc_slice = bpf_dynptr_slice(src_ptr, src_off, NULL, size);\nkernel/bpf/helpers.c-3296-\tdst_slice = bpf_dynptr_slice_rdwr(dst_ptr, dst_off, NULL, size);\n--\nkernel/bpf/helpers.c=5248=const void *__bpf_dynptr_data(const struct bpf_dynptr_kern *ptr, u64 len)\n--\nkernel/bpf/helpers.c-5251-\nkernel/bpf/helpers.c:5252:\treturn bpf_dynptr_slice(p, 0, NULL, len);\nkernel/bpf/helpers.c-5253-}\n--\nkernel/bpf/verifier.c=14012=static int check_special_kfunc(struct bpf_verifier_env *env, struct bpf_call_arg_meta *meta,\n--\nkernel/bpf/verifier.c-14149-\t\tif (!meta-\u003earg_constant.found) {\nkernel/bpf/verifier.c:14150:\t\t\tverifier_bug(env, \"bpf_dynptr_slice(_rdwr) no constant size\");\nkernel/bpf/verifier.c-14151-\t\t\treturn -EFAULT;\n"
}

57/3 2026/09/25 08:19 llm kmsan-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 50245
							output: 37
							thoughts: 32
						

Thoughts:
Here's my analysis of the provided thought process, tailored for an expert audience:

**Diving into `bpf_dynptr_slice` Implementation**

Okay, so we're talking about the `bpf_dynptr_slice` implementation. My initial focus is going to be around line 3090 of `kernel/bpf/helpers.c`. That's usually a good starting point to get a handle on the core logic, or where some initial error checking will be. I need to refresh my memory, since I haven't looked at the implementation in a little while, and I want to double check the most relevant code. From this I can figure out whether this is dealing with slice calculations or memory management, and determine if it applies to an upcoming project. I might be able to find any optimization opportunities as well.



58/3 2026/09/25 08:19 tool read-file
0m Args:
{
  "File": "kernel/bpf/helpers.c",
  "FirstLine": 3090,
  "LineCount": 45
}

Results:
{
  "Contents": "3090:\t__bpf_kfunc void *bpf_dynptr_slice(const struct bpf_dynptr *p, u64 offset,\n3091:\t\t\t\t\t   void *buffer__nullable, u64 buffer__szk)\n3092:\t{\n3093:\t\tconst struct bpf_dynptr_kern *ptr = (struct bpf_dynptr_kern *)p;\n3094:\t\tenum bpf_dynptr_type type;\n3095:\t\tu64 len = buffer__szk;\n3096:\t\tint err;\n3097:\t\n3098:\t\tif (!ptr-\u003edata)\n3099:\t\t\treturn NULL;\n3100:\t\n3101:\t\terr = bpf_dynptr_check_off_len(ptr, offset, len);\n3102:\t\tif (err)\n3103:\t\t\treturn NULL;\n3104:\t\n3105:\t\ttype = bpf_dynptr_get_type(ptr);\n3106:\t\n3107:\t\tswitch (type) {\n3108:\t\tcase BPF_DYNPTR_TYPE_LOCAL:\n3109:\t\tcase BPF_DYNPTR_TYPE_RINGBUF:\n3110:\t\t\treturn ptr-\u003edata + ptr-\u003eoffset + offset;\n3111:\t\tcase BPF_DYNPTR_TYPE_SKB:\n3112:\t\t\tif (buffer__nullable)\n3113:\t\t\t\treturn skb_header_pointer(ptr-\u003edata, ptr-\u003eoffset + offset, len, buffer__nullable);\n3114:\t\t\telse\n3115:\t\t\t\treturn skb_pointer_if_linear(ptr-\u003edata, ptr-\u003eoffset + offset, len);\n3116:\t\tcase BPF_DYNPTR_TYPE_XDP:\n3117:\t\t{\n3118:\t\t\tvoid *xdp_ptr = bpf_xdp_pointer(ptr-\u003edata, ptr-\u003eoffset + offset, len);\n3119:\t\t\tif (!IS_ERR_OR_NULL(xdp_ptr))\n3120:\t\t\t\treturn xdp_ptr;\n3121:\t\n3122:\t\t\tif (!buffer__nullable)\n3123:\t\t\t\treturn NULL;\n3124:\t\t\tbpf_xdp_copy_buf(ptr-\u003edata, ptr-\u003eoffset + offset, buffer__nullable, len, false);\n3125:\t\t\treturn buffer__nullable;\n3126:\t\t}\n3127:\t\tcase BPF_DYNPTR_TYPE_SKB_META:\n3128:\t\t\treturn bpf_skb_meta_pointer(ptr-\u003edata, ptr-\u003eoffset + offset);\n3129:\t\tcase BPF_DYNPTR_TYPE_FILE:\n3130:\t\t\terr = bpf_file_fetch_bytes(ptr-\u003edata, offset, buffer__nullable, buffer__szk);\n3131:\t\t\treturn err ? NULL : buffer__nullable;\n3132:\t\tdefault:\n3133:\t\t\tWARN_ONCE(true, \"unknown dynptr type %d\\n\", type);\n3134:\t\t\treturn NULL;\n"
}

59/3 2026/09/25 08:19 llm kmsan-evaluator
1m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 51282
							output: 413
							thoughts: 3430
						

Thoughts:
**Analysis of KMSAN Needs for BPF Crypto Implementation**

Okay, so I'm diving into this BPF crypto code, specifically looking at whether we need to enable KMSAN. My first thought is to trace the execution of `__bpf_dynptr_data` and all the associated functions to ensure safety. I'm focusing on the crypto operations: `bpf_crypto_crypt`, `bpf_aes_cbc_crypt`, and `bpf_aes_ecb_crypt`, scrutinizing the use of the `dynptr` buffer. It looks like the core logic revolves around the retrieval of the data pointer within a dynamic pointer for the source (`src`), destination (`dst`), and initialization vector (`iv`). I've gone through the key functions and confirmed that the arguments and local variables are properly initialized before being accessed.

My main concern is whether uninitialized data could be accessed or leaked. I've dissected the `bpf_crypto_ctx_create` function, particularly the branching logic, and verified that any variables used in those branches are initialized before any conditional statement is evaluated. I've considered that the `kzalloc_obj` call will zero the whole `struct bpf_crypto_ctx` object. Furthermore, I've checked every function call for bounds checks and the possibility that an allocation of a data structure may have uninitialized fields that can be read later, making sure no code paths rely on uninitialized values. Specifically, I'm checking that the lengths and sizes of source, destination, and IV buffers are properly validated before any cryptographic operations.

Next, I turned my attention to the replacement of the generic crypto API calls with direct AES library calls. This code uses the `aes_cbc_encrypt/decrypt` and `aes_ecb_encrypt/decrypt` functions. These functions are safe because they depend on validated size and length parameters. I'm also ensuring that any changes involving the `params` struct, like those for key setup, don't introduce vulnerabilities due to uninitialized memory. `aes_preparekey` is safe as it reads at most `key_len` bytes.

My conclusion is that this patch doesn't introduce any new KMSAN-specific risks. Everything this patch touches falls squarely into the realm of KASAN: out-of-bounds, heap corruption, use-after-free, etc. I've confirmed that the code doesn't leak kernel data to user space or expose uninitialized struct padding, nor does it rely on uninitialized local variables.
So, I'm confident in setting `NeedsKMSAN=false`.



60/3 2026/09/25 08:21 tool set-results
0m Args:
{
  "KMSANReasoning": "The patch refactors BPF crypto support by replacing the dynamic skcipher registration mechanism (using crypto_alloc_lskcipher) with direct calls to AES library routines (AES-CBC and AES-ECB via crypto/aes-cbc.h and crypto/aes-ecb.h).\n\nEvaluating KMSAN vs KASAN applicability:\n1. Heap Allocation \u0026 Initialization: The context structure `bpf_crypto_ctx` is allocated using `kzalloc_obj(*ctx)` which completely zeroes all memory. Key setup is performed via `aes_preparekey()`, and the context is freed via `kfree_sensitive()`.\n2. Stack \u0026 Local Variables: All local variables in `bpf_crypto_ctx_create()`, `bpf_aes_cbc_crypt()`, `bpf_aes_ecb_crypt()`, and `bpf_crypto_crypt()` are explicitly initialized before branching or reading. The return error code pointer `err_ret` is populated on all code paths.\n3. No User-Space Info Leaks: The BPF kfuncs operate internally on BPF dynptr buffers and context pointers within the kernel. There are no data structures, socket buffers, netlink attributes, or ioctls copied to user space.\n4. Bounds Checking vs Out-of-Bounds: The buffers accessed (`src`, `dst`, `iv`) are bounded by explicit length checks (`src_len % AES_BLOCK_SIZE`, `dst_len \u003c src_len`, and `iv_len == AES_BLOCK_SIZE` or `iv_len == 0`). Any potential bounds issues or lifetime/refcounting errors would be detected by KASAN, not KMSAN.\n\nTherefore, the changes do not introduce or expose uninitialized memory access risks or info-leaks, and a dedicated KMSAN session is not needed.",
  "NeedsKMSAN": false
}

Results:
{
  "KMSANReasoning": "The patch refactors BPF crypto support by replacing the dynamic skcipher registration mechanism (using crypto_alloc_lskcipher) with direct calls to AES library routines (AES-CBC and AES-ECB via crypto/aes-cbc.h and crypto/aes-ecb.h).\n\nEvaluating KMSAN vs KASAN applicability:\n1. Heap Allocation \u0026 Initialization: The context structure `bpf_crypto_ctx` is allocated using `kzalloc_obj(*ctx)` which completely zeroes all memory. Key setup is performed via `aes_preparekey()`, and the context is freed via `kfree_sensitive()`.\n2. Stack \u0026 Local Variables: All local variables in `bpf_crypto_ctx_create()`, `bpf_aes_cbc_crypt()`, `bpf_aes_ecb_crypt()`, and `bpf_crypto_crypt()` are explicitly initialized before branching or reading. The return error code pointer `err_ret` is populated on all code paths.\n3. No User-Space Info Leaks: The BPF kfuncs operate internally on BPF dynptr buffers and context pointers within the kernel. There are no data structures, socket buffers, netlink attributes, or ioctls copied to user space.\n4. Bounds Checking vs Out-of-Bounds: The buffers accessed (`src`, `dst`, `iv`) are bounded by explicit length checks (`src_len % AES_BLOCK_SIZE`, `dst_len \u003c src_len`, and `iv_len == AES_BLOCK_SIZE` or `iv_len == 0`). Any potential bounds issues or lifetime/refcounting errors would be detected by KASAN, not KMSAN.\n\nTherefore, the changes do not introduce or expose uninitialized memory access risks or info-leaks, and a dedicated KMSAN session is not needed.",
  "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)