| Seq | Timestamp | Type | Name | Duration |
|---|---|---|---|---|
| 0/0 | 2026/07/08 10:32 | flow | patch-triage |
0mArgs:null Results: {
"EnableConfigs": [
"BPF_LSM"
],
"FocusSymbols": [
"bpf_init_inode_xattr",
"security_lsmxattr_add",
"security_inode_init_security"
],
"Reasoning": "The patch introduces a new BPF kfunc `bpf_init_inode_xattr` which allows BPF LSM programs to set xattrs on a new inode during the `inode_init_security` hook. It modifies the LSM hook signature and implements `security_lsmxattr_add` to handle the xattr additions. This is a functional change to the kernel's LSM and BPF subsystems that can be reached from userspace by loading a BPF program and creating files. Fuzzing should focus on the new kfunc and the modified security functions.",
"WorthFuzzing": true
} |
| 1/1 | 2026/07/08 10:32 | action | read-patch-diff |
0mArgs:null Results: {
"PatchDiff": "commit 7db8b42fcf6f6a52289838b4beea597d3f786e96\nAuthor: syz-cluster \u003ctriage@syzkaller.com\u003e\nDate: Wed Jul 8 10:32:31 2026 +0000\n\n syz-cluster: applied patch under review\n\ndiff --git a/fs/bpf_fs_kfuncs.c b/fs/bpf_fs_kfuncs.c\nindex 768aca2dc0f034..ad0025f2264a8b 100644\n--- a/fs/bpf_fs_kfuncs.c\n+++ b/fs/bpf_fs_kfuncs.c\n@@ -11,7 +11,9 @@\n #include \u003clinux/file.h\u003e\n #include \u003clinux/kernfs.h\u003e\n #include \u003clinux/mm.h\u003e\n+#include \u003clinux/security.h\u003e\n #include \u003clinux/xattr.h\u003e\n+#include \u003cuapi/linux/lsm.h\u003e\n \n __bpf_kfunc_start_defs();\n \n@@ -374,6 +376,39 @@ __bpf_kfunc struct inode *bpf_real_inode(struct dentry *dentry)\n \treturn d_real_inode(dentry);\n }\n \n+/**\n+ * bpf_init_inode_xattr - set an xattr on a new inode from inode_init_security\n+ * @xattrs: inode_init_security xattr state from the hook context\n+ * @name__str: xattr name (e.g., \"bpf.file_label\")\n+ * @value_p: dynptr containing the xattr value\n+ *\n+ * Only callable from lsm/inode_init_security programs.\n+ *\n+ * Return: 0 on success, negative error on failure.\n+ */\n+__bpf_kfunc int bpf_init_inode_xattr(struct lsm_xattrs *xattrs,\n+\t\t\t\t const char *name__str,\n+\t\t\t\t const struct bpf_dynptr *value_p)\n+{\n+\tstruct bpf_dynptr_kern *value_ptr = (struct bpf_dynptr_kern *)value_p;\n+\tconst void *value;\n+\tu32 value_len;\n+\n+\tif (!name__str)\n+\t\treturn -EINVAL;\n+\tif (strncmp(name__str, XATTR_BPF_LSM_SUFFIX,\n+\t\t sizeof(XATTR_BPF_LSM_SUFFIX) - 1))\n+\t\treturn -EPERM;\n+\n+\tvalue_len = __bpf_dynptr_size(value_ptr);\n+\tvalue = __bpf_dynptr_data(value_ptr, value_len);\n+\tif (!value)\n+\t\treturn -EINVAL;\n+\n+\treturn security_lsmxattr_add(xattrs, LSM_ID_BPF, name__str, value,\n+\t\t\t\t value_len);\n+}\n+\n __bpf_kfunc_end_defs();\n \n BTF_KFUNCS_START(bpf_fs_kfunc_set_ids)\n@@ -385,6 +420,7 @@ BTF_ID_FLAGS(func, bpf_get_file_xattr, KF_SLEEPABLE)\n BTF_ID_FLAGS(func, bpf_set_dentry_xattr, KF_SLEEPABLE)\n BTF_ID_FLAGS(func, bpf_remove_dentry_xattr, KF_SLEEPABLE)\n BTF_ID_FLAGS(func, bpf_real_inode, KF_SLEEPABLE | KF_RET_NULL)\n+BTF_ID_FLAGS(func, bpf_init_inode_xattr)\n BTF_KFUNCS_END(bpf_fs_kfunc_set_ids)\n \n static int bpf_fs_kfuncs_filter(const struct bpf_prog *prog, u32 kfunc_id)\ndiff --git a/include/linux/bpf_lsm.h b/include/linux/bpf_lsm.h\nindex dda272d78f013f..374c50801a8c6d 100644\n--- a/include/linux/bpf_lsm.h\n+++ b/include/linux/bpf_lsm.h\n@@ -21,6 +21,9 @@ extern bool bpf_lsm_initialized __ro_after_init;\n #include \u003clinux/lsm_hook_defs.h\u003e\n #undef LSM_HOOK\n \n+/* max bpf xattrs per inode */\n+#define BPF_LSM_INODE_INIT_XATTRS 4\n+\n struct bpf_storage_blob {\n \tstruct bpf_local_storage __rcu *storage;\n };\ndiff --git a/include/linux/evm.h b/include/linux/evm.h\nindex 913f4573b2038e..528f360f3308de 100644\n--- a/include/linux/evm.h\n+++ b/include/linux/evm.h\n@@ -12,6 +12,8 @@\n #include \u003clinux/integrity.h\u003e\n #include \u003clinux/xattr.h\u003e\n \n+struct lsm_xattrs;\n+\n #ifdef CONFIG_EVM\n extern int evm_set_key(void *key, size_t keylen);\n extern enum integrity_status evm_verifyxattr(struct dentry *dentry,\n@@ -21,8 +23,8 @@ extern enum integrity_status evm_verifyxattr(struct dentry *dentry,\n int evm_fix_hmac(struct dentry *dentry, const char *xattr_name,\n \t\t const char *xattr_value, size_t xattr_value_len);\n int evm_inode_init_security(struct inode *inode, struct inode *dir,\n-\t\t\t const struct qstr *qstr, struct xattr *xattrs,\n-\t\t\t int *xattr_count);\n+\t\t\t const struct qstr *qstr,\n+\t\t\t struct lsm_xattrs *xattrs);\n extern bool evm_revalidate_status(const char *xattr_name);\n extern int evm_protected_xattr_if_enabled(const char *req_xattr_name);\n extern int evm_read_protected_xattrs(struct dentry *dentry, u8 *buffer,\n@@ -63,8 +65,7 @@ static inline int evm_fix_hmac(struct dentry *dentry, const char *xattr_name,\n \n static inline int evm_inode_init_security(struct inode *inode, struct inode *dir,\n \t\t\t\t\t const struct qstr *qstr,\n-\t\t\t\t\t struct xattr *xattrs,\n-\t\t\t\t\t int *xattr_count)\n+\t\t\t\t\t struct lsm_xattrs *xattrs)\n {\n \treturn 0;\n }\ndiff --git a/include/linux/lsm_hook_defs.h b/include/linux/lsm_hook_defs.h\nindex 65c9609ec20770..5b2de7865ce81a 100644\n--- a/include/linux/lsm_hook_defs.h\n+++ b/include/linux/lsm_hook_defs.h\n@@ -116,8 +116,8 @@ LSM_HOOK(int, 0, inode_alloc_security, struct inode *inode)\n LSM_HOOK(void, LSM_RET_VOID, inode_free_security, struct inode *inode)\n LSM_HOOK(void, LSM_RET_VOID, inode_free_security_rcu, void *inode_security)\n LSM_HOOK(int, -EOPNOTSUPP, inode_init_security, struct inode *inode,\n-\t struct inode *dir, const struct qstr *qstr, struct xattr *xattrs,\n-\t int *xattr_count)\n+\t struct inode *dir, const struct qstr *qstr,\n+\t struct lsm_xattrs *xattrs)\n LSM_HOOK(int, 0, inode_init_security_anon, struct inode *inode,\n \t const struct qstr *name, const struct inode *context_inode)\n LSM_HOOK(int, 0, inode_create, struct inode *dir, struct dentry *dentry,\ndiff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h\nindex b4f8cad53ddb03..7afe06a8d4c6c5 100644\n--- a/include/linux/lsm_hooks.h\n+++ b/include/linux/lsm_hooks.h\n@@ -200,20 +200,18 @@ extern struct lsm_static_calls_table static_calls_table __ro_after_init;\n \n /**\n * lsm_get_xattr_slot - Return the next available slot and increment the index\n- * @xattrs: array storing LSM-provided xattrs\n- * @xattr_count: number of already stored xattrs (updated)\n+ * @ctx: xattr state shared by inode_init_security hooks\n *\n- * Retrieve the first available slot in the @xattrs array to fill with an xattr,\n- * and increment @xattr_count.\n+ * Retrieve the first available slot in the @ctx-\u003exattrs array to fill with an\n+ * xattr, and increment @ctx-\u003exattr_count.\n *\n- * Return: The slot to fill in @xattrs if non-NULL, NULL otherwise.\n+ * Return: The slot to fill in @ctx-\u003exattrs if non-NULL, NULL otherwise.\n */\n-static inline struct xattr *lsm_get_xattr_slot(struct xattr *xattrs,\n-\t\t\t\t\t int *xattr_count)\n+static inline struct xattr *lsm_get_xattr_slot(struct lsm_xattrs *ctx)\n {\n-\tif (unlikely(!xattrs))\n+\tif (unlikely(!ctx || !ctx-\u003exattrs))\n \t\treturn NULL;\n-\treturn \u0026xattrs[(*xattr_count)++];\n+\treturn \u0026ctx-\u003exattrs[ctx-\u003exattr_count++];\n }\n \n #endif /* ! __LINUX_LSM_HOOKS_H */\ndiff --git a/include/linux/security.h b/include/linux/security.h\nindex 153e9043058f88..647f7b88358b2c 100644\n--- a/include/linux/security.h\n+++ b/include/linux/security.h\n@@ -68,6 +68,11 @@ struct watch;\n struct watch_notification;\n struct lsm_ctx;\n \n+struct lsm_xattrs {\n+\tstruct xattr *xattrs;\n+\tunsigned int xattr_count;\n+};\n+\n /* Default (no) options for the capable function */\n #define CAP_OPT_NONE 0x0\n /* If capable should audit the security request */\n@@ -401,6 +406,9 @@ void security_inode_free(struct inode *inode);\n int security_inode_init_security(struct inode *inode, struct inode *dir,\n \t\t\t\t const struct qstr *qstr,\n \t\t\t\t initxattrs initxattrs, void *fs_data);\n+int security_lsmxattr_add(struct lsm_xattrs *xattrs, u64 lsm_id,\n+\t\t\t const char *name, const void *value,\n+\t\t\t size_t value_len);\n int security_inode_init_security_anon(struct inode *inode,\n \t\t\t\t const struct qstr *name,\n \t\t\t\t const struct inode *context_inode);\n@@ -895,6 +903,13 @@ static inline int security_inode_init_security(struct inode *inode,\n \treturn 0;\n }\n \n+static inline int security_lsmxattr_add(struct lsm_xattrs *xattrs, u64 lsm_id,\n+\t\t\t\t\tconst char *name, const void *value,\n+\t\t\t\t\tsize_t value_len)\n+{\n+\treturn -EOPNOTSUPP;\n+}\n+\n static inline int security_inode_init_security_anon(struct inode *inode,\n \t\t\t\t\t\t const struct qstr *name,\n \t\t\t\t\t\t const struct inode *context_inode)\ndiff --git a/security/bpf/hooks.c b/security/bpf/hooks.c\nindex 7b98f5d1e2be83..8f8c3de3035f01 100644\n--- a/security/bpf/hooks.c\n+++ b/security/bpf/hooks.c\n@@ -33,6 +33,7 @@ static int __init bpf_lsm_init(void)\n \n struct lsm_blob_sizes bpf_lsm_blob_sizes __ro_after_init = {\n \t.lbs_inode = sizeof(struct bpf_storage_blob),\n+\t.lbs_xattr_count = BPF_LSM_INODE_INIT_XATTRS,\n };\n \n DEFINE_LSM(bpf) = {\ndiff --git a/security/integrity/evm/evm_main.c b/security/integrity/evm/evm_main.c\nindex b59e3f121b8a89..b7158fc63543e0 100644\n--- a/security/integrity/evm/evm_main.c\n+++ b/security/integrity/evm/evm_main.c\n@@ -1062,14 +1062,16 @@ static int evm_inode_copy_up_xattr(struct dentry *src, const char *name)\n * evm_inode_init_security - initializes security.evm HMAC value\n */\n int evm_inode_init_security(struct inode *inode, struct inode *dir,\n-\t\t\t const struct qstr *qstr, struct xattr *xattrs,\n-\t\t\t int *xattr_count)\n+\t\t\t const struct qstr *qstr,\n+\t\t\t struct lsm_xattrs *lsm_xattrs)\n {\n \tstruct evm_xattr *xattr_data;\n \tstruct xattr *xattr, *evm_xattr;\n+\tstruct xattr *xattrs;\n \tbool evm_protected_xattrs = false;\n \tint rc;\n \n+\txattrs = lsm_xattrs ? lsm_xattrs-\u003exattrs : NULL;\n \tif (!(evm_initialized \u0026 EVM_INIT_HMAC) || !xattrs)\n \t\treturn 0;\n \n@@ -1087,7 +1089,7 @@ int evm_inode_init_security(struct inode *inode, struct inode *dir,\n \tif (!evm_protected_xattrs)\n \t\treturn 0;\n \n-\tevm_xattr = lsm_get_xattr_slot(xattrs, xattr_count);\n+\tevm_xattr = lsm_get_xattr_slot(lsm_xattrs);\n \t/*\n \t * Array terminator (xattr name = NULL) must be the first non-filled\n \t * xattr slot.\ndiff --git a/security/security.c b/security/security.c\nindex 71aea8fdf01429..261f68e17cfda0 100644\n--- a/security/security.c\n+++ b/security/security.c\n@@ -12,6 +12,7 @@\n #define pr_fmt(fmt) \"LSM: \" fmt\n \n #include \u003clinux/bpf.h\u003e\n+#include \u003clinux/bpf_lsm.h\u003e\n #include \u003clinux/capability.h\u003e\n #include \u003clinux/dcache.h\u003e\n #include \u003clinux/export.h\u003e\n@@ -1333,8 +1334,8 @@ int security_inode_init_security(struct inode *inode, struct inode *dir,\n \t\t\t\t const initxattrs initxattrs, void *fs_data)\n {\n \tstruct lsm_static_call *scall;\n-\tstruct xattr *new_xattrs = NULL;\n-\tint ret = -EOPNOTSUPP, xattr_count = 0;\n+\tstruct lsm_xattrs xattrs = {};\n+\tint ret = -EOPNOTSUPP;\n \n \tif (unlikely(IS_PRIVATE(inode)))\n \t\treturn 0;\n@@ -1344,15 +1345,15 @@ int security_inode_init_security(struct inode *inode, struct inode *dir,\n \n \tif (initxattrs) {\n \t\t/* Allocate +1 as terminator. */\n-\t\tnew_xattrs = kcalloc(blob_sizes.lbs_xattr_count + 1,\n-\t\t\t\t sizeof(*new_xattrs), GFP_NOFS);\n-\t\tif (!new_xattrs)\n+\t\txattrs.xattrs = kcalloc(blob_sizes.lbs_xattr_count + 1,\n+\t\t\t\t\tsizeof(*xattrs.xattrs), GFP_NOFS);\n+\t\tif (!xattrs.xattrs)\n \t\t\treturn -ENOMEM;\n \t}\n \n \tlsm_for_each_hook(scall, inode_init_security) {\n-\t\tret = scall-\u003ehl-\u003ehook.inode_init_security(inode, dir, qstr, new_xattrs,\n-\t\t\t\t\t\t \u0026xattr_count);\n+\t\tret = scall-\u003ehl-\u003ehook.inode_init_security(inode, dir, qstr,\n+\t\t\t\t\t\t\t \u0026xattrs);\n \t\tif (ret \u0026\u0026 ret != -EOPNOTSUPP)\n \t\t\tgoto out;\n \t\t/*\n@@ -1364,18 +1365,101 @@ int security_inode_init_security(struct inode *inode, struct inode *dir,\n \t}\n \n \t/* If initxattrs() is NULL, xattr_count is zero, skip the call. */\n-\tif (!xattr_count)\n+\tif (!xattrs.xattr_count)\n \t\tgoto out;\n \n-\tret = initxattrs(inode, new_xattrs, fs_data);\n+\tret = initxattrs(inode, xattrs.xattrs, fs_data);\n out:\n-\tfor (; xattr_count \u003e 0; xattr_count--)\n-\t\tkfree(new_xattrs[xattr_count - 1].value);\n-\tkfree(new_xattrs);\n+\tfor (; xattrs.xattr_count \u003e 0; xattrs.xattr_count--)\n+\t\tkfree(xattrs.xattrs[xattrs.xattr_count - 1].value);\n+\tkfree(xattrs.xattrs);\n \treturn (ret == -EOPNOTSUPP) ? 0 : ret;\n }\n EXPORT_SYMBOL(security_inode_init_security);\n \n+#ifdef CONFIG_BPF_LSM\n+static unsigned int lsm_xattrs_used(const struct lsm_xattrs *xattrs,\n+\t\t\t\t const char *prefix)\n+{\n+\tsize_t prefix_len = strlen(prefix);\n+\tunsigned int i, n = 0;\n+\n+\tfor (i = 0; i \u003c xattrs-\u003exattr_count; i++) {\n+\t\tconst char *name = xattrs-\u003exattrs[i].name;\n+\n+\t\tif (name \u0026\u0026 !strncmp(name, prefix, prefix_len))\n+\t\t\tn++;\n+\t}\n+\treturn n;\n+}\n+#endif /* CONFIG_BPF_LSM */\n+\n+/**\n+ * security_lsmxattr_add() - Add an xattr during inode_init_security\n+ * @xattrs: xattr state shared by inode_init_security hooks\n+ * @lsm_id: LSM_ID_* value identifying the calling LSM\n+ * @name: xattr name suffix\n+ * @value: xattr value\n+ * @value_len: length of @value\n+ *\n+ * Claim an xattr slot in @xattrs on behalf of the LSM identified by\n+ * @lsm_id and fill it with a copy of @name and @value. Callers can invoke\n+ * this function from non-sleepable context.\n+ *\n+ * Return: Returns 0 on success, -ENOSPC if the calling LSM's slot budget\n+ * is exhausted, negative values on other errors.\n+ */\n+int security_lsmxattr_add(struct lsm_xattrs *xattrs, u64 lsm_id,\n+\t\t\t const char *name, const void *value,\n+\t\t\t size_t value_len)\n+{\n+\tstruct xattr *xattr;\n+\tvoid *xattr_value;\n+\tsize_t name_len;\n+\n+\tif (!xattrs || !xattrs-\u003exattrs || !name || !value)\n+\t\treturn -EINVAL;\n+\n+\tname_len = strlen(name);\n+\tif (name_len == 0 || name_len \u003e XATTR_NAME_MAX)\n+\t\treturn -EINVAL;\n+\tif (value_len == 0 || value_len \u003e XATTR_SIZE_MAX)\n+\t\treturn -EINVAL;\n+\n+\tswitch (lsm_id) {\n+#ifdef CONFIG_BPF_LSM\n+\tcase LSM_ID_BPF:\n+\t\tif (lsm_xattrs_used(xattrs, XATTR_BPF_LSM_SUFFIX) \u003e=\n+\t\t BPF_LSM_INODE_INIT_XATTRS)\n+\t\t\treturn -ENOSPC;\n+\t\tbreak;\n+#endif /* CONFIG_BPF_LSM */\n+\tdefault:\n+\t\treturn -EINVAL;\n+\t}\n+\n+\t/* Combine xattr value + name into one allocation. */\n+\txattr_value = kmalloc(value_len + name_len + 1, GFP_NOWAIT);\n+\tif (!xattr_value)\n+\t\treturn -ENOMEM;\n+\n+\tmemcpy(xattr_value, value, value_len);\n+\tmemcpy(xattr_value + value_len, name, name_len);\n+\t((char *)xattr_value)[value_len + name_len] = '\\0';\n+\n+\txattr = lsm_get_xattr_slot(xattrs);\n+\tif (!xattr) {\n+\t\tkfree(xattr_value);\n+\t\treturn -ENOSPC;\n+\t}\n+\n+\txattr-\u003evalue = xattr_value;\n+\txattr-\u003ename = (const char *)xattr_value + value_len;\n+\txattr-\u003evalue_len = value_len;\n+\n+\treturn 0;\n+}\n+\n /**\n * security_inode_init_security_anon() - Initialize an anonymous inode\n * @inode: the inode\ndiff --git a/security/selinux/hooks.c b/security/selinux/hooks.c\nindex 1a713d96206f59..6bba6b212e17c5 100644\n--- a/security/selinux/hooks.c\n+++ b/security/selinux/hooks.c\n@@ -2962,7 +2962,7 @@ static int selinux_dentry_create_files_as(struct dentry *dentry, int mode,\n \n static int selinux_inode_init_security(struct inode *inode, struct inode *dir,\n \t\t\t\t const struct qstr *qstr,\n-\t\t\t\t struct xattr *xattrs, int *xattr_count)\n+\t\t\t\t struct lsm_xattrs *xattrs)\n {\n \tconst struct cred_security_struct *crsec = selinux_cred(current_cred());\n \tstruct superblock_security_struct *sbsec;\n@@ -2992,7 +2992,7 @@ static int selinux_inode_init_security(struct inode *inode, struct inode *dir,\n \t !(sbsec-\u003eflags \u0026 SBLABEL_MNT))\n \t\treturn -EOPNOTSUPP;\n \n-\txattr = lsm_get_xattr_slot(xattrs, xattr_count);\n+\txattr = lsm_get_xattr_slot(xattrs);\n \tif (xattr) {\n \t\trc = security_sid_to_context_force(newsid,\n \t\t\t\t\t\t \u0026context, \u0026clen);\ndiff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c\nindex ff115068c5c06f..4501078430ca05 100644\n--- a/security/smack/smack_lsm.c\n+++ b/security/smack/smack_lsm.c\n@@ -981,10 +981,10 @@ smk_rule_transmutes(struct smack_known *subject,\n }\n \n static int\n-xattr_dupval(struct xattr *xattrs, int *xattr_count,\n+xattr_dupval(struct lsm_xattrs *xattrs,\n \t const char *name, const void *value, unsigned int vallen)\n {\n-\tstruct xattr * const xattr = lsm_get_xattr_slot(xattrs, xattr_count);\n+\tstruct xattr * const xattr = lsm_get_xattr_slot(xattrs);\n \n \tif (!xattr)\n \t\treturn 0;\n@@ -1003,14 +1003,13 @@ xattr_dupval(struct xattr *xattrs, int *xattr_count,\n * @inode: the newly created inode\n * @dir: containing directory object\n * @qstr: unused\n- * @xattrs: where to put the attributes\n- * @xattr_count: current number of LSM-provided xattrs (updated)\n+ * @xattrs: where to put attributes and update count\n *\n * Returns 0 if it all works out, -ENOMEM if there's no memory\n */\n static int smack_inode_init_security(struct inode *inode, struct inode *dir,\n \t\t\t\t const struct qstr *qstr,\n-\t\t\t\t struct xattr *xattrs, int *xattr_count)\n+\t\t\t\t struct lsm_xattrs *xattrs)\n {\n \tstruct task_smack *tsp = smack_cred(current_cred());\n \tstruct inode_smack * const issp = smack_inode(inode);\n@@ -1057,21 +1056,19 @@ static int smack_inode_init_security(struct inode *inode, struct inode *dir,\n \t\tif (S_ISDIR(inode-\u003ei_mode)) {\n \t\t\ttransflag = SMK_INODE_TRANSMUTE;\n \n-\t\t\tif (xattr_dupval(xattrs, xattr_count,\n-\t\t\t\tXATTR_SMACK_TRANSMUTE,\n-\t\t\t\tTRANS_TRUE,\n-\t\t\t\tTRANS_TRUE_SIZE\n-\t\t\t))\n+\t\t\tif (xattr_dupval(xattrs,\n+\t\t\t\t\t XATTR_SMACK_TRANSMUTE,\n+\t\t\t\t\t TRANS_TRUE,\n+\t\t\t\t\t TRANS_TRUE_SIZE))\n \t\t\t\trc = -ENOMEM;\n \t\t}\n \t}\n \n \tif (rc == 0)\n-\t\tif (xattr_dupval(xattrs, xattr_count,\n-\t\t\t XATTR_SMACK_SUFFIX,\n-\t\t\t issp-\u003esmk_inode-\u003esmk_known,\n-\t\t strlen(issp-\u003esmk_inode-\u003esmk_known)\n-\t\t))\n+\t\tif (xattr_dupval(xattrs,\n+\t\t\t\t XATTR_SMACK_SUFFIX,\n+\t\t\t\t issp-\u003esmk_inode-\u003esmk_known,\n+\t\t\t\t strlen(issp-\u003esmk_inode-\u003esmk_known)))\n \t\t\trc = -ENOMEM;\n instant_inode:\n \tissp-\u003esmk_flags |= (SMK_INODE_INSTANT | transflag);\ndiff --git a/tools/testing/selftests/bpf/bpf_kfuncs.h b/tools/testing/selftests/bpf/bpf_kfuncs.h\nindex ae71e9b6905186..2639f9f941955b 100644\n--- a/tools/testing/selftests/bpf/bpf_kfuncs.h\n+++ b/tools/testing/selftests/bpf/bpf_kfuncs.h\n@@ -92,4 +92,9 @@ extern int bpf_set_dentry_xattr(struct dentry *dentry, const char *name__str,\n \t\t\t\tconst struct bpf_dynptr *value_p, int flags) __ksym __weak;\n extern int bpf_remove_dentry_xattr(struct dentry *dentry, const char *name__str) __ksym __weak;\n \n+struct lsm_xattrs;\n+extern int bpf_init_inode_xattr(struct lsm_xattrs *xattrs,\n+\t\t\t\tconst char *name__str,\n+\t\t\t\tconst struct bpf_dynptr *value_p) __ksym __weak;\n+\n #endif\ndiff --git a/tools/testing/selftests/bpf/prog_tests/fs_kfuncs.c b/tools/testing/selftests/bpf/prog_tests/fs_kfuncs.c\nindex 43a26ec69a8e36..10be0ab91e7061 100644\n--- a/tools/testing/selftests/bpf/prog_tests/fs_kfuncs.c\n+++ b/tools/testing/selftests/bpf/prog_tests/fs_kfuncs.c\n@@ -10,6 +10,7 @@\n #include \"test_get_xattr.skel.h\"\n #include \"test_set_remove_xattr.skel.h\"\n #include \"test_fsverity.skel.h\"\n+#include \"test_init_inode_xattr.skel.h\"\n \n static const char testfile[] = \"/tmp/test_progs_fs_kfuncs\";\n \n@@ -268,6 +269,123 @@ static void test_fsverity(void)\n \tremove(testfile);\n }\n \n+static void test_init_inode_xattr(void)\n+{\n+\tstruct test_init_inode_xattr *skel = NULL;\n+\tint fd = -1, err;\n+\tchar value_out[64];\n+\tconst char *testfile_new = \"/tmp/test_progs_fs_kfuncs_new\";\n+\n+\tskel = test_init_inode_xattr__open_and_load();\n+\tif (!ASSERT_OK_PTR(skel, \"test_init_inode_xattr__open_and_load\"))\n+\t\treturn;\n+\n+\tskel-\u003ebss-\u003emonitored_pid = getpid();\n+\terr = test_init_inode_xattr__attach(skel);\n+\tif (!ASSERT_OK(err, \"test_init_inode_xattr__attach\"))\n+\t\tgoto out;\n+\n+\t/* Trigger inode_init_security */\n+\tfd = open(testfile_new, O_CREAT | O_RDWR, 0644);\n+\tif (!ASSERT_GE(fd, 0, \"create_file\"))\n+\t\tgoto out;\n+\n+\tASSERT_EQ(skel-\u003edata-\u003einit_result, 0, \"init_result\");\n+\n+\t/* initxattrs prepends \"security.\" to the name. */\n+\terr = getxattr(testfile_new, \"security.bpf.test_label\", value_out,\n+\t\t sizeof(value_out));\n+\tif (err \u003c 0 \u0026\u0026 errno == ENODATA) {\n+\t\tprintf(\"%s:SKIP:filesystem did not apply LSM xattrs\\n\",\n+\t\t __func__);\n+\t\ttest__skip();\n+\t\tgoto out;\n+\t}\n+\tif (!ASSERT_GE(err, 0, \"getxattr\"))\n+\t\tgoto out;\n+\n+\tASSERT_EQ(err, (int)sizeof(skel-\u003edata-\u003exattr_value), \"xattr_size\");\n+\tASSERT_EQ(strncmp(value_out, \"unconfined_u:object_r:user_home_t:s0\",\n+\t\t\t sizeof(\"unconfined_u:object_r:user_home_t:s0\")), 0,\n+\t\t \"xattr_value\");\n+\n+out:\n+\tclose(fd);\n+\ttest_init_inode_xattr__destroy(skel);\n+\tremove(testfile_new);\n+}\n+\n+/* Keep in sync with BPF_LSM_INODE_INIT_XATTRS in include/linux/bpf_lsm.h. */\n+#define INIT_INODE_XATTR_MAX 4\n+\n+/*\n+ * Programs may attach to inode_init_security without an attach-time limit, but\n+ * the kfunc only lets BPF claim INIT_INODE_XATTR_MAX xattr slots per inode.\n+ * Calls beyond that budget are rejected at runtime with -ENOSPC.\n+ */\n+static void test_init_inode_xattr_slot_limit(void)\n+{\n+\tstruct test_init_inode_xattr *skel[INIT_INODE_XATTR_MAX + 1] = {};\n+\tstruct bpf_link *link[INIT_INODE_XATTR_MAX + 1] = {};\n+\tconst char *testfile_slot = \"/tmp/test_progs_fs_kfuncs_slot\";\n+\tint ok = 0, nospc = 0, other = 0;\n+\tint i, fd = -1;\n+\n+\t/* All programs attach successfully; there is no attach-time cap. */\n+\tfor (i = 0; i \u003c= INIT_INODE_XATTR_MAX; i++) {\n+\t\tskel[i] = test_init_inode_xattr__open();\n+\t\tif (!ASSERT_OK_PTR(skel[i], \"open\"))\n+\t\t\tgoto out;\n+\n+\t\tsnprintf(skel[i]-\u003erodata-\u003exattr_name,\n+\t\t\t sizeof(skel[i]-\u003erodata-\u003exattr_name), \"bpf.label_%d\",\n+\t\t\t i);\n+\n+\t\tif (!ASSERT_OK(test_init_inode_xattr__load(skel[i]), \"load\"))\n+\t\t\tgoto out;\n+\n+\t\tskel[i]-\u003ebss-\u003emonitored_pid = getpid();\n+\n+\t\tlink[i] = bpf_program__attach_lsm(skel[i]-\u003eprogs.test_init_inode_xattr);\n+\t\tif (!ASSERT_OK_PTR(link[i], \"attach\"))\n+\t\t\tgoto out;\n+\t}\n+\n+\t/* Trigger inode_init_security once with all programs attached. */\n+\tfd = open(testfile_slot, O_CREAT | O_RDWR, 0644);\n+\tif (!ASSERT_GE(fd, 0, \"create_file\"))\n+\t\tgoto out;\n+\n+\t/*\n+\t * Exactly INIT_INODE_XATTR_MAX programs claim a slot; the program past\n+\t * the budget gets -ENOSPC. The order in which programs run is not\n+\t * guaranteed, so count results instead of indexing.\n+\t */\n+\tfor (i = 0; i \u003c= INIT_INODE_XATTR_MAX; i++) {\n+\t\tint res = skel[i]-\u003edata-\u003einit_result;\n+\n+\t\tif (res == 0)\n+\t\t\tok++;\n+\t\telse if (res == -ENOSPC)\n+\t\t\tnospc++;\n+\t\telse\n+\t\t\tother++;\n+\t}\n+\n+\tASSERT_EQ(ok, INIT_INODE_XATTR_MAX, \"slots_within_budget\");\n+\tASSERT_EQ(nospc, 1, \"slot_over_budget\");\n+\tASSERT_EQ(other, 0, \"unexpected_result\");\n+\n+out:\n+\tif (fd \u003e= 0)\n+\t\tclose(fd);\n+\tfor (i = 0; i \u003c= INIT_INODE_XATTR_MAX; i++) {\n+\t\tbpf_link__destroy(link[i]);\n+\t\ttest_init_inode_xattr__destroy(skel[i]);\n+\t}\n+\tremove(testfile_slot);\n+}\n+\n void test_fs_kfuncs(void)\n {\n \t/* Matches xattr_names in progs/test_get_xattr.c */\n@@ -288,4 +406,10 @@ void test_fs_kfuncs(void)\n \n \tif (test__start_subtest(\"fsverity\"))\n \t\ttest_fsverity();\n+\n+\tif (test__start_subtest(\"init_inode_xattr\"))\n+\t\ttest_init_inode_xattr();\n+\n+\tif (test__start_subtest(\"init_inode_xattr_slot_limit\"))\n+\t\ttest_init_inode_xattr_slot_limit();\n }\ndiff --git a/tools/testing/selftests/bpf/progs/test_init_inode_xattr.c b/tools/testing/selftests/bpf/progs/test_init_inode_xattr.c\nnew file mode 100644\nindex 00000000000000..caade53b87e3a3\n--- /dev/null\n+++ b/tools/testing/selftests/bpf/progs/test_init_inode_xattr.c\n@@ -0,0 +1,31 @@\n+// SPDX-License-Identifier: GPL-2.0\n+/* Copyright (c) 2026 Cisco Systems, Inc. */\n+\n+#include \"vmlinux.h\"\n+#include \u003cbpf/bpf_tracing.h\u003e\n+#include \"bpf_kfuncs.h\"\n+\n+char _license[] SEC(\"license\") = \"GPL\";\n+\n+__u32 monitored_pid;\n+int init_result = -1;\n+\n+const char xattr_name[16] = \"bpf.test_label\";\n+char xattr_value[] = \"unconfined_u:object_r:user_home_t:s0\";\n+\n+SEC(\"lsm/inode_init_security\")\n+int BPF_PROG(test_init_inode_xattr, struct inode *inode, struct inode *dir,\n+\t const struct qstr *qstr, struct lsm_xattrs *xattrs)\n+{\n+\tstruct bpf_dynptr value_ptr;\n+\t__u32 pid;\n+\n+\tpid = bpf_get_current_pid_tgid() \u003e\u003e 32;\n+\tif (pid != monitored_pid)\n+\t\treturn 0;\n+\n+\tbpf_dynptr_from_mem(xattr_value, sizeof(xattr_value), 0, \u0026value_ptr);\n+\tinit_result = bpf_init_inode_xattr(xattrs, xattr_name, \u0026value_ptr);\n+\n+\treturn 0;\n+}\n"
} |
| 2/1 | 2026/07/08 10:32 | agent | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: {
"EnableConfigs": [
"BPF_LSM"
],
"FocusSymbols": [
"bpf_init_inode_xattr",
"security_lsmxattr_add",
"security_inode_init_security"
],
"Reasoning": "The patch introduces a new BPF kfunc `bpf_init_inode_xattr` which allows BPF LSM programs to set xattrs on a new inode during the `inode_init_security` hook. It modifies the LSM hook signature and implements `security_lsmxattr_add` to handle the xattr additions. This is a functional change to the kernel's LSM and BPF subsystems that can be reached from userspace by loading a BPF program and creating files. Fuzzing should focus on the new kfunc and the modified security functions.",
"WorthFuzzing": true
}Instruction: You are an expert Linux kernel maintainer. Your job is to review a provided patch series and determine if it makes functional changes to the kernel that should be fuzzed. IMPORTANT: The changes have ALREADY been applied and committed as the HEAD commit in your workspace. Do NOT rely on your internal knowledge of the kernel. You must actively use your code access tools to examine the actual source code and confirm any assumptions. Return WorthFuzzing=false if the patch only contains: - Modifications to Documentation/, Kconfig files, or code comments. - Purely decorative changes, such as logging (e.g., pr_err, printk) or tracepoints. - Changes to numeric constants or macros that do not functionally alter execution flow. - Code paths that are impossible to reach in virtualized environments like GCE or QEMU, even when utilizing software-emulated hardware (e.g., usb gadget, mac80211_hwsim). If it modifies reachable core kernel logic, drivers, or architectures, use your code search tools to verify the code can be executed, then return WorthFuzzing=true. When returning WorthFuzzing=true, you MUST ALSO: 1. Extract any specific kernel functions that should be heavily fuzzed into FocusSymbols. Avoid listing generic hot-path functions to prevent skewed test distributions. 2. Identify any specific CONFIG_ options required to properly test this new/modified feature. Go and look into the Kconfig files and check for ifdefs around the code, do not make assumptions. Do not list too generic configs (we already have them enabled). Only list those that specifically cover the modified code. List them in the EnableConfigs output array, and DO NOT add a '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: For your convenience, here is the diff of the changes:
commit 7db8b42fcf6f6a52289838b4beea597d3f786e96
Author: syz-cluster <triage@syzkaller.com>
Date: Wed Jul 8 10:32:31 2026 +0000
syz-cluster: applied patch under review
diff --git a/fs/bpf_fs_kfuncs.c b/fs/bpf_fs_kfuncs.c
index 768aca2dc0f034..ad0025f2264a8b 100644
--- a/fs/bpf_fs_kfuncs.c
+++ b/fs/bpf_fs_kfuncs.c
@@ -11,7 +11,9 @@
#include <linux/file.h>
#include <linux/kernfs.h>
#include <linux/mm.h>
+#include <linux/security.h>
#include <linux/xattr.h>
+#include <uapi/linux/lsm.h>
__bpf_kfunc_start_defs();
@@ -374,6 +376,39 @@ __bpf_kfunc struct inode *bpf_real_inode(struct dentry *dentry)
return d_real_inode(dentry);
}
+/**
+ * bpf_init_inode_xattr - set an xattr on a new inode from inode_init_security
+ * @xattrs: inode_init_security xattr state from the hook context
+ * @name__str: xattr name (e.g., "bpf.file_label")
+ * @value_p: dynptr containing the xattr value
+ *
+ * Only callable from lsm/inode_init_security programs.
+ *
+ * Return: 0 on success, negative error on failure.
+ */
+__bpf_kfunc int bpf_init_inode_xattr(struct lsm_xattrs *xattrs,
+ const char *name__str,
+ const struct bpf_dynptr *value_p)
+{
+ struct bpf_dynptr_kern *value_ptr = (struct bpf_dynptr_kern *)value_p;
+ const void *value;
+ u32 value_len;
+
+ if (!name__str)
+ return -EINVAL;
+ if (strncmp(name__str, XATTR_BPF_LSM_SUFFIX,
+ sizeof(XATTR_BPF_LSM_SUFFIX) - 1))
+ return -EPERM;
+
+ value_len = __bpf_dynptr_size(value_ptr);
+ value = __bpf_dynptr_data(value_ptr, value_len);
+ if (!value)
+ return -EINVAL;
+
+ return security_lsmxattr_add(xattrs, LSM_ID_BPF, name__str, value,
+ value_len);
+}
+
__bpf_kfunc_end_defs();
BTF_KFUNCS_START(bpf_fs_kfunc_set_ids)
@@ -385,6 +420,7 @@ BTF_ID_FLAGS(func, bpf_get_file_xattr, KF_SLEEPABLE)
BTF_ID_FLAGS(func, bpf_set_dentry_xattr, KF_SLEEPABLE)
BTF_ID_FLAGS(func, bpf_remove_dentry_xattr, KF_SLEEPABLE)
BTF_ID_FLAGS(func, bpf_real_inode, KF_SLEEPABLE | KF_RET_NULL)
+BTF_ID_FLAGS(func, bpf_init_inode_xattr)
BTF_KFUNCS_END(bpf_fs_kfunc_set_ids)
static int bpf_fs_kfuncs_filter(const struct bpf_prog *prog, u32 kfunc_id)
diff --git a/include/linux/bpf_lsm.h b/include/linux/bpf_lsm.h
index dda272d78f013f..374c50801a8c6d 100644
--- a/include/linux/bpf_lsm.h
+++ b/include/linux/bpf_lsm.h
@@ -21,6 +21,9 @@ extern bool bpf_lsm_initialized __ro_after_init;
#include <linux/lsm_hook_defs.h>
#undef LSM_HOOK
+/* max bpf xattrs per inode */
+#define BPF_LSM_INODE_INIT_XATTRS 4
+
struct bpf_storage_blob {
struct bpf_local_storage __rcu *storage;
};
diff --git a/include/linux/evm.h b/include/linux/evm.h
index 913f4573b2038e..528f360f3308de 100644
--- a/include/linux/evm.h
+++ b/include/linux/evm.h
@@ -12,6 +12,8 @@
#include <linux/integrity.h>
#include <linux/xattr.h>
+struct lsm_xattrs;
+
#ifdef CONFIG_EVM
extern int evm_set_key(void *key, size_t keylen);
extern enum integrity_status evm_verifyxattr(struct dentry *dentry,
@@ -21,8 +23,8 @@ extern enum integrity_status evm_verifyxattr(struct dentry *dentry,
int evm_fix_hmac(struct dentry *dentry, const char *xattr_name,
const char *xattr_value, size_t xattr_value_len);
int evm_inode_init_security(struct inode *inode, struct inode *dir,
- const struct qstr *qstr, struct xattr *xattrs,
- int *xattr_count);
+ const struct qstr *qstr,
+ struct lsm_xattrs *xattrs);
extern bool evm_revalidate_status(const char *xattr_name);
extern int evm_protected_xattr_if_enabled(const char *req_xattr_name);
extern int evm_read_protected_xattrs(struct dentry *dentry, u8 *buffer,
@@ -63,8 +65,7 @@ static inline int evm_fix_hmac(struct dentry *dentry, const char *xattr_name,
static inline int evm_inode_init_security(struct inode *inode, struct inode *dir,
const struct qstr *qstr,
- struct xattr *xattrs,
- int *xattr_count)
+ struct lsm_xattrs *xattrs)
{
return 0;
}
diff --git a/include/linux/lsm_hook_defs.h b/include/linux/lsm_hook_defs.h
index 65c9609ec20770..5b2de7865ce81a 100644
--- a/include/linux/lsm_hook_defs.h
+++ b/include/linux/lsm_hook_defs.h
@@ -116,8 +116,8 @@ LSM_HOOK(int, 0, inode_alloc_security, struct inode *inode)
LSM_HOOK(void, LSM_RET_VOID, inode_free_security, struct inode *inode)
LSM_HOOK(void, LSM_RET_VOID, inode_free_security_rcu, void *inode_security)
LSM_HOOK(int, -EOPNOTSUPP, inode_init_security, struct inode *inode,
- struct inode *dir, const struct qstr *qstr, struct xattr *xattrs,
- int *xattr_count)
+ struct inode *dir, const struct qstr *qstr,
+ struct lsm_xattrs *xattrs)
LSM_HOOK(int, 0, inode_init_security_anon, struct inode *inode,
const struct qstr *name, const struct inode *context_inode)
LSM_HOOK(int, 0, inode_create, struct inode *dir, struct dentry *dentry,
diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h
index b4f8cad53ddb03..7afe06a8d4c6c5 100644
--- a/include/linux/lsm_hooks.h
+++ b/include/linux/lsm_hooks.h
@@ -200,20 +200,18 @@ extern struct lsm_static_calls_table static_calls_table __ro_after_init;
/**
* lsm_get_xattr_slot - Return the next available slot and increment the index
- * @xattrs: array storing LSM-provided xattrs
- * @xattr_count: number of already stored xattrs (updated)
+ * @ctx: xattr state shared by inode_init_security hooks
*
- * Retrieve the first available slot in the @xattrs array to fill with an xattr,
- * and increment @xattr_count.
+ * Retrieve the first available slot in the @ctx->xattrs array to fill with an
+ * xattr, and increment @ctx->xattr_count.
*
- * Return: The slot to fill in @xattrs if non-NULL, NULL otherwise.
+ * Return: The slot to fill in @ctx->xattrs if non-NULL, NULL otherwise.
*/
-static inline struct xattr *lsm_get_xattr_slot(struct xattr *xattrs,
- int *xattr_count)
+static inline struct xattr *lsm_get_xattr_slot(struct lsm_xattrs *ctx)
{
- if (unlikely(!xattrs))
+ if (unlikely(!ctx || !ctx->xattrs))
return NULL;
- return &xattrs[(*xattr_count)++];
+ return &ctx->xattrs[ctx->xattr_count++];
}
#endif /* ! __LINUX_LSM_HOOKS_H */
diff --git a/include/linux/security.h b/include/linux/security.h
index 153e9043058f88..647f7b88358b2c 100644
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -68,6 +68,11 @@ struct watch;
struct watch_notification;
struct lsm_ctx;
+struct lsm_xattrs {
+ struct xattr *xattrs;
+ unsigned int xattr_count;
+};
+
/* Default (no) options for the capable function */
#define CAP_OPT_NONE 0x0
/* If capable should audit the security request */
@@ -401,6 +406,9 @@ void security_inode_free(struct inode *inode);
int security_inode_init_security(struct inode *inode, struct inode *dir,
const struct qstr *qstr,
initxattrs initxattrs, void *fs_data);
+int security_lsmxattr_add(struct lsm_xattrs *xattrs, u64 lsm_id,
+ const char *name, const void *value,
+ size_t value_len);
int security_inode_init_security_anon(struct inode *inode,
const struct qstr *name,
const struct inode *context_inode);
@@ -895,6 +903,13 @@ static inline int security_inode_init_security(struct inode *inode,
return 0;
}
+static inline int security_lsmxattr_add(struct lsm_xattrs *xattrs, u64 lsm_id,
+ const char *name, const void *value,
+ size_t value_len)
+{
+ return -EOPNOTSUPP;
+}
+
static inline int security_inode_init_security_anon(struct inode *inode,
const struct qstr *name,
const struct inode *context_inode)
diff --git a/security/bpf/hooks.c b/security/bpf/hooks.c
index 7b98f5d1e2be83..8f8c3de3035f01 100644
--- a/security/bpf/hooks.c
+++ b/security/bpf/hooks.c
@@ -33,6 +33,7 @@ static int __init bpf_lsm_init(void)
struct lsm_blob_sizes bpf_lsm_blob_sizes __ro_after_init = {
.lbs_inode = sizeof(struct bpf_storage_blob),
+ .lbs_xattr_count = BPF_LSM_INODE_INIT_XATTRS,
};
DEFINE_LSM(bpf) = {
diff --git a/security/integrity/evm/evm_main.c b/security/integrity/evm/evm_main.c
index b59e3f121b8a89..b7158fc63543e0 100644
--- a/security/integrity/evm/evm_main.c
+++ b/security/integrity/evm/evm_main.c
@@ -1062,14 +1062,16 @@ static int evm_inode_copy_up_xattr(struct dentry *src, const char *name)
* evm_inode_init_security - initializes security.evm HMAC value
*/
int evm_inode_init_security(struct inode *inode, struct inode *dir,
- const struct qstr *qstr, struct xattr *xattrs,
- int *xattr_count)
+ const struct qstr *qstr,
+ struct lsm_xattrs *lsm_xattrs)
{
struct evm_xattr *xattr_data;
struct xattr *xattr, *evm_xattr;
+ struct xattr *xattrs;
bool evm_protected_xattrs = false;
int rc;
+ xattrs = lsm_xattrs ? lsm_xattrs->xattrs : NULL;
if (!(evm_initialized & EVM_INIT_HMAC) || !xattrs)
return 0;
@@ -1087,7 +1089,7 @@ int evm_inode_init_security(struct inode *inode, struct inode *dir,
if (!evm_protected_xattrs)
return 0;
- evm_xattr = lsm_get_xattr_slot(xattrs, xattr_count);
+ evm_xattr = lsm_get_xattr_slot(lsm_xattrs);
/*
* Array terminator (xattr name = NULL) must be the first non-filled
* xattr slot.
diff --git a/security/security.c b/security/security.c
index 71aea8fdf01429..261f68e17cfda0 100644
--- a/security/security.c
+++ b/security/security.c
@@ -12,6 +12,7 @@
#define pr_fmt(fmt) "LSM: " fmt
#include <linux/bpf.h>
+#include <linux/bpf_lsm.h>
#include <linux/capability.h>
#include <linux/dcache.h>
#include <linux/export.h>
@@ -1333,8 +1334,8 @@ int security_inode_init_security(struct inode *inode, struct inode *dir,
const initxattrs initxattrs, void *fs_data)
{
struct lsm_static_call *scall;
- struct xattr *new_xattrs = NULL;
- int ret = -EOPNOTSUPP, xattr_count = 0;
+ struct lsm_xattrs xattrs = {};
+ int ret = -EOPNOTSUPP;
if (unlikely(IS_PRIVATE(inode)))
return 0;
@@ -1344,15 +1345,15 @@ int security_inode_init_security(struct inode *inode, struct inode *dir,
if (initxattrs) {
/* Allocate +1 as terminator. */
- new_xattrs = kcalloc(blob_sizes.lbs_xattr_count + 1,
- sizeof(*new_xattrs), GFP_NOFS);
- if (!new_xattrs)
+ xattrs.xattrs = kcalloc(blob_sizes.lbs_xattr_count + 1,
+ sizeof(*xattrs.xattrs), GFP_NOFS);
+ if (!xattrs.xattrs)
return -ENOMEM;
}
lsm_for_each_hook(scall, inode_init_security) {
- ret = scall->hl->hook.inode_init_security(inode, dir, qstr, new_xattrs,
- &xattr_count);
+ ret = scall->hl->hook.inode_init_security(inode, dir, qstr,
+ &xattrs);
if (ret && ret != -EOPNOTSUPP)
goto out;
/*
@@ -1364,18 +1365,101 @@ int security_inode_init_security(struct inode *inode, struct inode *dir,
}
/* If initxattrs() is NULL, xattr_count is zero, skip the call. */
- if (!xattr_count)
+ if (!xattrs.xattr_count)
goto out;
- ret = initxattrs(inode, new_xattrs, fs_data);
+ ret = initxattrs(inode, xattrs.xattrs, fs_data);
out:
- for (; xattr_count > 0; xattr_count--)
- kfree(new_xattrs[xattr_count - 1].value);
- kfree(new_xattrs);
+ for (; xattrs.xattr_count > 0; xattrs.xattr_count--)
+ kfree(xattrs.xattrs[xattrs.xattr_count - 1].value);
+ kfree(xattrs.xattrs);
return (ret == -EOPNOTSUPP) ? 0 : ret;
}
EXPORT_SYMBOL(security_inode_init_security);
+#ifdef CONFIG_BPF_LSM
+static unsigned int lsm_xattrs_used(const struct lsm_xattrs *xattrs,
+ const char *prefix)
+{
+ size_t prefix_len = strlen(prefix);
+ unsigned int i, n = 0;
+
+ for (i = 0; i < xattrs->xattr_count; i++) {
+ const char *name = xattrs->xattrs[i].name;
+
+ if (name && !strncmp(name, prefix, prefix_len))
+ n++;
+ }
+ return n;
+}
+#endif /* CONFIG_BPF_LSM */
+
+/**
+ * security_lsmxattr_add() - Add an xattr during inode_init_security
+ * @xattrs: xattr state shared by inode_init_security hooks
+ * @lsm_id: LSM_ID_* value identifying the calling LSM
+ * @name: xattr name suffix
+ * @value: xattr value
+ * @value_len: length of @value
+ *
+ * Claim an xattr slot in @xattrs on behalf of the LSM identified by
+ * @lsm_id and fill it with a copy of @name and @value. Callers can invoke
+ * this function from non-sleepable context.
+ *
+ * Return: Returns 0 on success, -ENOSPC if the calling LSM's slot budget
+ * is exhausted, negative values on other errors.
+ */
+int security_lsmxattr_add(struct lsm_xattrs *xattrs, u64 lsm_id,
+ const char *name, const void *value,
+ size_t value_len)
+{
+ struct xattr *xattr;
+ void *xattr_value;
+ size_t name_len;
+
+ if (!xattrs || !xattrs->xattrs || !name || !value)
+ return -EINVAL;
+
+ name_len = strlen(name);
+ if (name_len == 0 || name_len > XATTR_NAME_MAX)
+ return -EINVAL;
+ if (value_len == 0 || value_len > XATTR_SIZE_MAX)
+ return -EINVAL;
+
+ switch (lsm_id) {
+#ifdef CONFIG_BPF_LSM
+ case LSM_ID_BPF:
+ if (lsm_xattrs_used(xattrs, XATTR_BPF_LSM_SUFFIX) >=
+ BPF_LSM_INODE_INIT_XATTRS)
+ return -ENOSPC;
+ break;
+#endif /* CONFIG_BPF_LSM */
+ default:
+ return -EINVAL;
+ }
+
+ /* Combine xattr value + name into one allocation. */
+ xattr_value = kmalloc(value_len + name_len + 1, GFP_NOWAIT);
+ if (!xattr_value)
+ return -ENOMEM;
+
+ memcpy(xattr_value, value, value_len);
+ memcpy(xattr_value + value_len, name, name_len);
+ ((char *)xattr_value)[value_len + name_len] = '\0';
+
+ xattr = lsm_get_xattr_slot(xattrs);
+ if (!xattr) {
+ kfree(xattr_value);
+ return -ENOSPC;
+ }
+
+ xattr->value = xattr_value;
+ xattr->name = (const char *)xattr_value + value_len;
+ xattr->value_len = value_len;
+
+ return 0;
+}
+
/**
* security_inode_init_security_anon() - Initialize an anonymous inode
* @inode: the inode
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index 1a713d96206f59..6bba6b212e17c5 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -2962,7 +2962,7 @@ static int selinux_dentry_create_files_as(struct dentry *dentry, int mode,
static int selinux_inode_init_security(struct inode *inode, struct inode *dir,
const struct qstr *qstr,
- struct xattr *xattrs, int *xattr_count)
+ struct lsm_xattrs *xattrs)
{
const struct cred_security_struct *crsec = selinux_cred(current_cred());
struct superblock_security_struct *sbsec;
@@ -2992,7 +2992,7 @@ static int selinux_inode_init_security(struct inode *inode, struct inode *dir,
!(sbsec->flags & SBLABEL_MNT))
return -EOPNOTSUPP;
- xattr = lsm_get_xattr_slot(xattrs, xattr_count);
+ xattr = lsm_get_xattr_slot(xattrs);
if (xattr) {
rc = security_sid_to_context_force(newsid,
&context, &clen);
diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c
index ff115068c5c06f..4501078430ca05 100644
--- a/security/smack/smack_lsm.c
+++ b/security/smack/smack_lsm.c
@@ -981,10 +981,10 @@ smk_rule_transmutes(struct smack_known *subject,
}
static int
-xattr_dupval(struct xattr *xattrs, int *xattr_count,
+xattr_dupval(struct lsm_xattrs *xattrs,
const char *name, const void *value, unsigned int vallen)
{
- struct xattr * const xattr = lsm_get_xattr_slot(xattrs, xattr_count);
+ struct xattr * const xattr = lsm_get_xattr_slot(xattrs);
if (!xattr)
return 0;
@@ -1003,14 +1003,13 @@ xattr_dupval(struct xattr *xattrs, int *xattr_count,
* @inode: the newly created inode
* @dir: containing directory object
* @qstr: unused
- * @xattrs: where to put the attributes
- * @xattr_count: current number of LSM-provided xattrs (updated)
+ * @xattrs: where to put attributes and update count
*
* Returns 0 if it all works out, -ENOMEM if there's no memory
*/
static int smack_inode_init_security(struct inode *inode, struct inode *dir,
const struct qstr *qstr,
- struct xattr *xattrs, int *xattr_count)
+ struct lsm_xattrs *xattrs)
{
struct task_smack *tsp = smack_cred(current_cred());
struct inode_smack * const issp = smack_inode(inode);
@@ -1057,21 +1056,19 @@ static int smack_inode_init_security(struct inode *inode, struct inode *dir,
if (S_ISDIR(inode->i_mode)) {
transflag = SMK_INODE_TRANSMUTE;
- if (xattr_dupval(xattrs, xattr_count,
- XATTR_SMACK_TRANSMUTE,
- TRANS_TRUE,
- TRANS_TRUE_SIZE
- ))
+ if (xattr_dupval(xattrs,
+ XATTR_SMACK_TRANSMUTE,
+ TRANS_TRUE,
+ TRANS_TRUE_SIZE))
rc = -ENOMEM;
}
}
if (rc == 0)
- if (xattr_dupval(xattrs, xattr_count,
- XATTR_SMACK_SUFFIX,
- issp->smk_inode->smk_known,
- strlen(issp->smk_inode->smk_known)
- ))
+ if (xattr_dupval(xattrs,
+ XATTR_SMACK_SUFFIX,
+ issp->smk_inode->smk_known,
+ strlen(issp->smk_inode->smk_known)))
rc = -ENOMEM;
instant_inode:
issp->smk_flags |= (SMK_INODE_INSTANT | transflag);
diff --git a/tools/testing/selftests/bpf/bpf_kfuncs.h b/tools/testing/selftests/bpf/bpf_kfuncs.h
index ae71e9b6905186..2639f9f941955b 100644
--- a/tools/testing/selftests/bpf/bpf_kfuncs.h
+++ b/tools/testing/selftests/bpf/bpf_kfuncs.h
@@ -92,4 +92,9 @@ extern int bpf_set_dentry_xattr(struct dentry *dentry, const char *name__str,
const struct bpf_dynptr *value_p, int flags) __ksym __weak;
extern int bpf_remove_dentry_xattr(struct dentry *dentry, const char *name__str) __ksym __weak;
+struct lsm_xattrs;
+extern int bpf_init_inode_xattr(struct lsm_xattrs *xattrs,
+ const char *name__str,
+ const struct bpf_dynptr *value_p) __ksym __weak;
+
#endif
diff --git a/tools/testing/selftests/bpf/prog_tests/fs_kfuncs.c b/tools/testing/selftests/bpf/prog_tests/fs_kfuncs.c
index 43a26ec69a8e36..10be0ab91e7061 100644
--- a/tools/testing/selftests/bpf/prog_tests/fs_kfuncs.c
+++ b/tools/testing/selftests/bpf/prog_tests/fs_kfuncs.c
@@ -10,6 +10,7 @@
#include "test_get_xattr.skel.h"
#include "test_set_remove_xattr.skel.h"
#include "test_fsverity.skel.h"
+#include "test_init_inode_xattr.skel.h"
static const char testfile[] = "/tmp/test_progs_fs_kfuncs";
@@ -268,6 +269,123 @@ static void test_fsverity(void)
remove(testfile);
}
+static void test_init_inode_xattr(void)
+{
+ struct test_init_inode_xattr *skel = NULL;
+ int fd = -1, err;
+ char value_out[64];
+ const char *testfile_new = "/tmp/test_progs_fs_kfuncs_new";
+
+ skel = test_init_inode_xattr__open_and_load();
+ if (!ASSERT_OK_PTR(skel, "test_init_inode_xattr__open_and_load"))
+ return;
+
+ skel->bss->monitored_pid = getpid();
+ err = test_init_inode_xattr__attach(skel);
+ if (!ASSERT_OK(err, "test_init_inode_xattr__attach"))
+ goto out;
+
+ /* Trigger inode_init_security */
+ fd = open(testfile_new, O_CREAT | O_RDWR, 0644);
+ if (!ASSERT_GE(fd, 0, "create_file"))
+ goto out;
+
+ ASSERT_EQ(skel->data->init_result, 0, "init_result");
+
+ /* initxattrs prepends "security." to the name. */
+ err = getxattr(testfile_new, "security.bpf.test_label", value_out,
+ sizeof(value_out));
+ if (err < 0 && errno == ENODATA) {
+ printf("%s:SKIP:filesystem did not apply LSM xattrs\n",
+ __func__);
+ test__skip();
+ goto out;
+ }
+ if (!ASSERT_GE(err, 0, "getxattr"))
+ goto out;
+
+ ASSERT_EQ(err, (int)sizeof(skel->data->xattr_value), "xattr_size");
+ ASSERT_EQ(strncmp(value_out, "unconfined_u:object_r:user_home_t:s0",
+ sizeof("unconfined_u:object_r:user_home_t:s0")), 0,
+ "xattr_value");
+
+out:
+ close(fd);
+ test_init_inode_xattr__destroy(skel);
+ remove(testfile_new);
+}
+
+/* Keep in sync with BPF_LSM_INODE_INIT_XATTRS in include/linux/bpf_lsm.h. */
+#define INIT_INODE_XATTR_MAX 4
+
+/*
+ * Programs may attach to inode_init_security without an attach-time limit, but
+ * the kfunc only lets BPF claim INIT_INODE_XATTR_MAX xattr slots per inode.
+ * Calls beyond that budget are rejected at runtime with -ENOSPC.
+ */
+static void test_init_inode_xattr_slot_limit(void)
+{
+ struct test_init_inode_xattr *skel[INIT_INODE_XATTR_MAX + 1] = {};
+ struct bpf_link *link[INIT_INODE_XATTR_MAX + 1] = {};
+ const char *testfile_slot = "/tmp/test_progs_fs_kfuncs_slot";
+ int ok = 0, nospc = 0, other = 0;
+ int i, fd = -1;
+
+ /* All programs attach successfully; there is no attach-time cap. */
+ for (i = 0; i <= INIT_INODE_XATTR_MAX; i++) {
+ skel[i] = test_init_inode_xattr__open();
+ if (!ASSERT_OK_PTR(skel[i], "open"))
+ goto out;
+
+ snprintf(skel[i]->rodata->xattr_name,
+ sizeof(skel[i]->rodata->xattr_name), "bpf.label_%d",
+ i);
+
+ if (!ASSERT_OK(test_init_inode_xattr__load(skel[i]), "load"))
+ goto out;
+
+ skel[i]->bss->monitored_pid = getpid();
+
+ link[i] = bpf_program__attach_lsm(skel[i]->progs.test_init_inode_xattr);
+ if (!ASSERT_OK_PTR(link[i], "attach"))
+ goto out;
+ }
+
+ /* Trigger inode_init_security once with all programs attached. */
+ fd = open(testfile_slot, O_CREAT | O_RDWR, 0644);
+ if (!ASSERT_GE(fd, 0, "create_file"))
+ goto out;
+
+ /*
+ * Exactly INIT_INODE_XATTR_MAX programs claim a slot; the program past
+ * the budget gets -ENOSPC. The order in which programs run is not
+ * guaranteed, so count results instead of indexing.
+ */
+ for (i = 0; i <= INIT_INODE_XATTR_MAX; i++) {
+ int res = skel[i]->data->init_result;
+
+ if (res == 0)
+ ok++;
+ else if (res == -ENOSPC)
+ nospc++;
+ else
+ other++;
+ }
+
+ ASSERT_EQ(ok, INIT_INODE_XATTR_MAX, "slots_within_budget");
+ ASSERT_EQ(nospc, 1, "slot_over_budget");
+ ASSERT_EQ(other, 0, "unexpected_result");
+
+out:
+ if (fd >= 0)
+ close(fd);
+ for (i = 0; i <= INIT_INODE_XATTR_MAX; i++) {
+ bpf_link__destroy(link[i]);
+ test_init_inode_xattr__destroy(skel[i]);
+ }
+ remove(testfile_slot);
+}
+
void test_fs_kfuncs(void)
{
/* Matches xattr_names in progs/test_get_xattr.c */
@@ -288,4 +406,10 @@ void test_fs_kfuncs(void)
if (test__start_subtest("fsverity"))
test_fsverity();
+
+ if (test__start_subtest("init_inode_xattr"))
+ test_init_inode_xattr();
+
+ if (test__start_subtest("init_inode_xattr_slot_limit"))
+ test_init_inode_xattr_slot_limit();
}
diff --git a/tools/testing/selftests/bpf/progs/test_init_inode_xattr.c b/tools/testing/selftests/bpf/progs/test_init_inode_xattr.c
new file mode 100644
index 00000000000000..caade53b87e3a3
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/test_init_inode_xattr.c
@@ -0,0 +1,31 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) 2026 Cisco Systems, Inc. */
+
+#include "vmlinux.h"
+#include <bpf/bpf_tracing.h>
+#include "bpf_kfuncs.h"
+
+char _license[] SEC("license") = "GPL";
+
+__u32 monitored_pid;
+int init_result = -1;
+
+const char xattr_name[16] = "bpf.test_label";
+char xattr_value[] = "unconfined_u:object_r:user_home_t:s0";
+
+SEC("lsm/inode_init_security")
+int BPF_PROG(test_init_inode_xattr, struct inode *inode, struct inode *dir,
+ const struct qstr *qstr, struct lsm_xattrs *xattrs)
+{
+ struct bpf_dynptr value_ptr;
+ __u32 pid;
+
+ pid = bpf_get_current_pid_tgid() >> 32;
+ if (pid != monitored_pid)
+ return 0;
+
+ bpf_dynptr_from_mem(xattr_value, sizeof(xattr_value), 0, &value_ptr);
+ init_result = bpf_init_inode_xattr(xattrs, xattr_name, &value_ptr);
+
+ return 0;
+}
|
| 3/2 | 2026/07/08 10:32 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 11154 output: 31 thoughts: 402 Thoughts: **My Assessment of the New BPF xattr Patch** Okay, so this patch introduces some pretty interesting functionality. Essentially, it allows BPF LSM programs to set an extended attribute (xattr) on a newly created inode, all through the `inode_init_security` hook. This seems like a powerful tool for enhanced security enforcement at inode creation. My initial thoughts are that this is a significant functional change within the kernel. The patch rewrites the `inode_init_security` LSM hook signature to deal with `struct lsm_xattrs` instead of the old `struct xattr` setup, and updates various security modules like `evm`, `selinux`, and `smack` to work with the updated signature. It also looks like a new function `security_lsmxattr_add` has been added for adding the xattr to this new struct. This change also includes the `bpf_init_inode_xattr` BPF kfunc. The user-space interaction is the key aspect, really. A BPF program loaded into the kernel, attaching to the `lsm/inode_init_security` hook, could then leverage this `bpf_init_inode_xattr` kfunc. This makes the new functionality easily accessible. I'm starting to think about the configuration implications. Clearly, `CONFIG_BPF_LSM` is a must-have, and `CONFIG_SECURITY` is essential, since it involves LSM hooks. I see that the patch modifies files related to `CONFIG_SECURITY_SELINUX`, `CONFIG_SECURITY_SMACK`, and `CONFIG_EVM`. The core logic is likely in `security/security.c` and `fs/bpf_fs_kfuncs.c`. It's worth double-checking that `fs/bpf_fs_kfuncs.c` is compiled conditionally, as that makes sense. I'll want to take a closer look at the unit tests provided in the `selftests` directory – specifically `fs_kfuncs.c` and `test_init_inode_xattr.c` – to get a better sense of how this works and what sort of scenarios are being tested. That'll be a key data point. |
| 4/2 | 2026/07/08 10:32 | tool | grepper |
0mArgs:{
"Expression": "bpf_fs_kfuncs.o",
"PathPrefix": "fs/"
}Results: {
"Output": "fs/Makefile=129=obj-$(CONFIG_ZONEFS_FS)\t\t+= zonefs/\nfs/Makefile:130:obj-$(CONFIG_BPF_LSM)\t\t+= bpf_fs_kfuncs.o\nfs/Makefile-131-obj-$(CONFIG_RESCTRL_FS)\t+= resctrl/\n"
} |
| 5/2 | 2026/07/08 10:32 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 12154 output: 30 thoughts: 36 Thoughts: Okay, here's my take on those thoughts, framed as an expert's internal monologue: **Checking BPF LSM Integration** Right, so `CONFIG_BPF_LSM` is a hard requirement, as expected. Now, I need to understand how this ties into the broader security landscape. My next step is to dive into `security/security.c`. I need to zero in on `security_lsmxattr_add`, or anything related to extended attributes (xattrs) and the LSM integration. This function is where the BPF hook should be. I'll analyze how the BPF logic is being invoked in the xattr addition path. I'm looking for the integration point. |
| 6/2 | 2026/07/08 10:32 | tool | grepper |
0mArgs:{
"Expression": "security_lsmxattr_add",
"PathPrefix": "security/"
}Results: {
"Output": "security/security.c=1381=static unsigned int lsm_xattrs_used(const struct lsm_xattrs *xattrs,\n--\nsecurity/security.c-1397-/**\nsecurity/security.c:1398: * security_lsmxattr_add() - Add an xattr during inode_init_security\nsecurity/security.c-1399- * @xattrs: xattr state shared by inode_init_security hooks\n--\nsecurity/security.c-1411- */\nsecurity/security.c:1412:int security_lsmxattr_add(struct lsm_xattrs *xattrs, u64 lsm_id,\nsecurity/security.c-1413-\t\t\t const char *name, const void *value,\n"
} |
| 7/2 | 2026/07/08 10:32 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 12570 output: 28 thoughts: 117 Thoughts: **Investigating the Interaction Between LSM and BPF Extended Attributes** Okay, so I'm looking into how Linux Security Modules (LSM) interact with BPF-extended attributes, specifically focusing on the `security_lsmxattr_add` function. I know that lives in `security/security.c`, so I'll keep that in mind. The related kfunc, `bpf_init_inode_xattr`, resides in `fs/bpf_fs_kfuncs.c`. Now, I need to see what other configurations might come into play here. Obviously, `CONFIG_BPF_SYSCALL` is a prerequisite for any BPF usage, but if I'm dealing with LSM and BPF together, `CONFIG_BPF_LSM` is the key. Since `CONFIG_BPF_LSM` probably relies on `CONFIG_BPF_SYSCALL` (or implies it), I'll double-check `CONFIG_BPF_LSM`'s definition in the Kconfig files. Understanding the dependencies is paramount to grasping the complete picture. |
| 8/2 | 2026/07/08 10:32 | tool | grepper |
0mError:no matches Args: {
"Expression": "config BPF_LSM",
"PathPrefix": "security/"
}Results: {
"error": "no matches"
} |
| 9/2 | 2026/07/08 10:32 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 12962 output: 26 thoughts: 17 Thoughts: Okay, here's my attempt at summarizing the thought process, as if I were thinking it: **Digging into LSM Integration with BPF** Right, time to dive into this BPF and LSM integration. I need to figure out where the kernel configuration options related to `BPF_LSM` are defined. That's usually the first step when trying to understand how a feature is enabled and built within the kernel. Kconfig files are the place to start – they're the source of truth for kernel configuration options. Let's get to searching for `BPF_LSM`. I need to figure out which Kconfig files define it to get a clearer picture of its dependencies and implications. |
| 10/2 | 2026/07/08 10:32 | tool | grepper |
0mArgs:{
"Expression": "config BPF_LSM",
"PathPrefix": ""
}Results: {
"Output": "kernel/bpf/Kconfig=90=source \"kernel/bpf/preload/Kconfig\"\nkernel/bpf/Kconfig-91-\nkernel/bpf/Kconfig:92:config BPF_LSM\nkernel/bpf/Kconfig-93-\tbool \"Enable BPF LSM Instrumentation\"\n"
} |
| 11/2 | 2026/07/08 10:32 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 13239 output: 34 thoughts: 15 Thoughts: Okay, let's dive into this. Here's my understanding, presented as my own thought process: **Investigating the Dependencies of BPF_LSM** Alright, so the task at hand is to understand the dependencies of `BPF_LSM`. My initial thought is, let's not just take this at face value, but instead, let's trace this like a proper investigation. Where does this live? What other subsystems does it rely upon? I need to understand the implications of using this; any potential conflicts or bottlenecks. A good understanding of the dependencies will allow me to properly integrate it. Let's start by mapping it all out. |
| 12/2 | 2026/07/08 10:32 | tool | read-file |
0mArgs:{
"File": "kernel/bpf/Kconfig",
"FirstLine": 90,
"LineCount": 20
}Results: {
"Contents": " 90:\tsource \"kernel/bpf/preload/Kconfig\"\n 91:\t\n 92:\tconfig BPF_LSM\n 93:\t\tbool \"Enable BPF LSM Instrumentation\"\n 94:\t\tdepends on BPF_EVENTS\n 95:\t\tdepends on BPF_SYSCALL\n 96:\t\tdepends on SECURITY\n 97:\t\tdepends on BPF_JIT\n 98:\t\thelp\n 99:\t\t Enables instrumentation of the security hooks with BPF programs for\n 100:\t\t implementing dynamic MAC and Audit Policies.\n 101:\t\n 102:\t\t If you are unsure how to answer this question, answer N.\n 103:\t\n 104:\tendmenu # \"BPF subsystem\"\n"
} |
| 13/2 | 2026/07/08 10:32 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 13630 output: 188 thoughts: 822 Thoughts: **Analyzing the BPF LSM xattr Patch** Okay, so I'm looking at this patch and my initial thought is that `BPF_LSM` is the key configuration option. I need to figure out how this is going to affect things, so I'm thinking about the implications. What about `CONFIG_SECURITY`? I'm pretty sure that is required for the `security/security.c` code to compile, I'll need to double-check that. And what about the SELinux, SMACK, and EVM configurations: `CONFIG_SECURITY_SELINUX`, `CONFIG_SECURITY_SMACK`, and `CONFIG_EVM`? The patch *does* modify them to use this new signature, but the core functionality of `bpf_init_inode_xattr` should still work, even if those are *not* enabled, as long as `CONFIG_BPF_LSM` is enabled. Right, so the `inode_init_security` hook is called by filesystem code like `ext4` or `btrfs` when a new inode is created. If I'm going to fuzz this effectively, I'm pretty sure I need to focus on enabling `CONFIG_BPF_LSM`. That's the priority. Now, let's nail down the focus symbols. I'm seeing `bpf_init_inode_xattr`, `security_lsmxattr_add`, and `security_inode_init_security` as the primary functions of interest. Let me take another look to ensure I have all the functions. I see `lsm_get_xattr_slot` which is an inline function. And I also see `evm_inode_init_security`, `selinux_inode_init_security`, and `smack_inode_init_security` are available as well. I need to confirm there aren't any other crucial configurations that impact this. I'm pretty sure `CONFIG_BPF_LSM` is the main one. I *think* `CONFIG_SECURITY` is required for `BPF_LSM`, so that is probably the main dependency. Let me go back to the patch again. ```c +__bpf_kfunc int bpf_init_inode_xattr(struct lsm_xattrs *xattrs, + const char *name__str, + const struct bpf_dynptr *value_p) ``` ```c +int security_lsmxattr_add(struct lsm_xattrs *xattrs, u64 lsm_id, + const char *name, const void *value, + size_t value_len) ``` ```c int security_inode_init_security(struct inode *inode, struct inode *dir, const struct qstr *qstr, const initxattrs initxattrs, void *fs_data) ``` The key focus is on those new signatures. So, just to summarize: * **Focus Symbols**: `bpf_init_inode_xattr`, `security_lsmxattr_add`, and `security_inode_init_security`. * **Configs**: `BPF_LSM` (with an implied dependency on `SECURITY`). Essentially, this patch introduces a new BPF kfunc, `bpf_init_inode_xattr`, allowing BPF LSM programs to set xattrs on newly created inodes during the `inode_init_security` hook. It also modifies the LSM hook's signature and implements `security_lsmxattr_add` to actually handle the addition of the xattr. This is a pretty significant functional change affecting both the kernel's LSM and BPF subsystems and is reachable from userspace by loading a BPF program and creating files, therefore, fuzzing will need to focus on the new kfunc, and those modified security functions. I need to double-check that `security_inode_init_security` is actually reachable -- yes, it's invoked by filesystems during inode creation, which is good. Is `security_lsmxattr_add` reachable? Yes, through `bpf_init_inode_xattr`. Do I need other configurations? Perhaps, `CONFIG_EVM`, `CONFIG_SECURITY_SELINUX`, or `CONFIG_SECURITY_SMACK`? They are modified, yes, but the *core* logic I need to fuzz is the BPF kfunc and `security_lsmxattr_add`, which means enabling `BPF_LSM` *should* be sufficient to test the new feature properly. Alright, time to use that `set-results` tool. |
| 14/2 | 2026/07/08 10:33 | tool | set-results |
0mArgs:{
"EnableConfigs": [
"BPF_LSM"
],
"FocusSymbols": [
"bpf_init_inode_xattr",
"security_lsmxattr_add",
"security_inode_init_security"
],
"Reasoning": "The patch introduces a new BPF kfunc `bpf_init_inode_xattr` which allows BPF LSM programs to set xattrs on a new inode during the `inode_init_security` hook. It modifies the LSM hook signature and implements `security_lsmxattr_add` to handle the xattr additions. This is a functional change to the kernel's LSM and BPF subsystems that can be reached from userspace by loading a BPF program and creating files. Fuzzing should focus on the new kfunc and the modified security functions.",
"WorthFuzzing": true
}Results: {
"EnableConfigs": [
"BPF_LSM"
],
"FocusSymbols": [
"bpf_init_inode_xattr",
"security_lsmxattr_add",
"security_inode_init_security"
],
"Reasoning": "The patch introduces a new BPF kfunc `bpf_init_inode_xattr` which allows BPF LSM programs to set xattrs on a new inode during the `inode_init_security` hook. It modifies the LSM hook signature and implements `security_lsmxattr_add` to handle the xattr additions. This is a functional change to the kernel's LSM and BPF subsystems that can be reached from userspace by loading a BPF program and creating files. Fuzzing should focus on the new kfunc and the modified security functions.",
"WorthFuzzing": true
} |
| Total Calls | Total Tokens | Avg Tokens | Total Duration (Seconds) | Avg Duration (Seconds) |
|---|
| Total Calls | Total Duration (Seconds) | Avg Duration (Seconds) |
|---|