On Tue, Jun 30, 2026 at 2:40 PM David Windsor <[email protected]> wrote: > > 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 <[email protected]> > --- > 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

