Re: [PATCH v4 bpf-next 2/3] bpf: add bpf_init_inode_xattr kfunc for atomic inode labeling
From: Paul Moore <paul@paul-moore.com>
Date: 2026-06-30 19:20:50
Also in:
bpf, linux-fsdevel, linux-integrity, linux-kselftest, lkml, selinux
On Tue, Jun 30, 2026 at 2:40 PM David Windsor [off-list ref] wrote:
quoted hunk ↗ jump to hunk
Add bpf_init_inode_xattr() kfunc for BPF LSM programs to atomically set xattrs via the inode_init_security hook using lsm_get_xattr_slot(). The hook now passes its xattr state as a single struct lsm_xattrs object, which the kfunc takes directly. The kfunc is only usable from lsm/inode_init_security programs: no other hook exposes a struct lsm_xattrs argument, so the verifier rejects calls from elsewhere. Restrict the xattr names that may be set via this kfunc to the bpf.* namespace. BPF reserves BPF_LSM_INODE_INIT_XATTRS slots via lbs_xattr_count, and the kfunc enforces that BPF never consumes more slots than it reserved, returning -ENOSPC once the budget is exhausted. A previous attempt [1] required a kmalloc string output protocol for the xattr name. Since commit 6bcdfd2cac55 ("security: Allow all LSMs to provide xattrs for inode_init_security hook") [2], the xattr name is no longer allocated; it is a static constant. Link: https://kernsec.org/pipermail/linux-security-module-archive/2022-October/034878.html [1] Link: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=6bcdfd2cac55 [2] Signed-off-by: David Windsor <redacted> --- fs/bpf_fs_kfuncs.c | 79 +++++++++++++++++++++++++++++++++++++++++ include/linux/bpf_lsm.h | 3 ++ kernel/bpf/bpf_lsm.c | 1 + security/bpf/hooks.c | 1 + 4 files changed, 84 insertions(+)diff --git a/fs/bpf_fs_kfuncs.c b/fs/bpf_fs_kfuncs.c index 768aca2dc0f0..c4023c82f21e 100644 --- a/fs/bpf_fs_kfuncs.c +++ b/fs/bpf_fs_kfuncs.c@@ -10,6 +10,7 @@ #include <linux/fsnotify.h> #include <linux/file.h> #include <linux/kernfs.h> +#include <linux/lsm_hooks.h> #include <linux/mm.h> #include <linux/xattr.h>@@ -374,6 +375,83 @@ __bpf_kfunc struct inode *bpf_real_inode(struct dentry *dentry) return d_real_inode(dentry); } +static int bpf_xattrs_used(const struct lsm_xattrs *ctx) +{ + const size_t prefix_len = sizeof(XATTR_BPF_LSM_SUFFIX) - 1; + unsigned int i, n = 0; + + for (i = 0; i < ctx->xattr_count; i++) { + const char *name = ctx->xattrs[i].name; + + if (name && !strncmp(name, XATTR_BPF_LSM_SUFFIX, prefix_len)) + n++; + } + return n; +} + +/** + * 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; + size_t name_len; + void *xattr_value; + struct xattr *xattr; + const void *value; + u32 value_len; + + if (!xattrs || !xattrs->xattrs || !name__str) + return -EINVAL; + if (bpf_xattrs_used(xattrs) >= BPF_LSM_INODE_INIT_XATTRS) + return -ENOSPC; + + name_len = strlen(name__str); + if (name_len == 0 || name_len > XATTR_NAME_MAX) + 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); + if (value_len == 0 || value_len > XATTR_SIZE_MAX) + return -EINVAL; + + value = __bpf_dynptr_data(value_ptr, value_len); + if (!value) + return -EINVAL; + + /* Combine xattr value + name into one allocation. */ + xattr_value = kmalloc(value_len + name_len + 1, GFP_NOFS); + if (!xattr_value) + return -ENOMEM; + + memcpy(xattr_value, value, value_len); + memcpy(xattr_value + value_len, name__str, 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; +}
This is not a generic VFS function, it is a LSM specific function, it belongs under security/, please move the code as discussed previously. -- paul-moore.com