From: David Windsor <dwindsor@gmail.com>
To: Alexei Starovoitov <ast@kernel.org>,
Daniel Borkmann <daniel@iogearbox.net>,
Andrii Nakryiko <andrii@kernel.org>,
Martin KaFai Lau <martin.lau@linux.dev>,
Eduard Zingerman <eddyz87@gmail.com>, Song Liu <song@kernel.org>,
Yonghong Song <yonghong.song@linux.dev>,
John Fastabend <john.fastabend@gmail.com>,
KP Singh <kpsingh@kernel.org>, Jiri Olsa <jolsa@kernel.org>,
Kumar Kartikeya Dwivedi <memxor@gmail.com>,
Emil Tsalapatis <emil@etsalapatis.com>,
Matt Bobrowski <mattbobrowski@google.com>,
Paul Moore <paul@paul-moore.com>,
James Morris <jmorris@namei.org>,
"Serge E . Hallyn" <serge@hallyn.com>,
Casey Schaufler <casey@schaufler-ca.com>,
Stephen Smalley <stephen.smalley.work@gmail.com>,
Ondrej Mosnacek <omosnace@redhat.com>,
Mimi Zohar <zohar@linux.ibm.com>,
Roberto Sassu <roberto.sassu@huawei.com>,
Dmitry Kasatkin <dmitry.kasatkin@gmail.com>,
Eric Snowberg <eric.snowberg@oracle.com>,
Alexander Viro <viro@zeniv.linux.org.uk>,
Christian Brauner <brauner@kernel.org>, Jan Kara <jack@suse.cz>,
Shuah Khan <shuah@kernel.org>
Cc: bpf@vger.kernel.org, linux-security-module@vger.kernel.org,
linux-fsdevel@vger.kernel.org, linux-integrity@vger.kernel.org,
selinux@vger.kernel.org, linux-kselftest@vger.kernel.org,
linux-kernel@vger.kernel.org, David Windsor <dwindsor@gmail.com>
Subject: [PATCH v4 bpf-next 2/3] bpf: add bpf_init_inode_xattr kfunc for atomic inode labeling
Date: Tue, 30 Jun 2026 14:39:54 -0400 [thread overview]
Message-ID: <20260630183956.281293-3-dwindsor@gmail.com> (raw)
In-Reply-To: <20260630183956.281293-1-dwindsor@gmail.com>
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 <dwindsor@gmail.com>
---
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;
+}
+
__bpf_kfunc_end_defs();
BTF_KFUNCS_START(bpf_fs_kfunc_set_ids)
@@ -385,6 +463,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, KF_SLEEPABLE)
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 143775a27a2a..b655c708818e 100644
--- a/include/linux/bpf_lsm.h
+++ b/include/linux/bpf_lsm.h
@@ -19,6 +19,9 @@
#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/kernel/bpf/bpf_lsm.c b/kernel/bpf/bpf_lsm.c
index 564071a92d7d..1c3f84a92420 100644
--- a/kernel/bpf/bpf_lsm.c
+++ b/kernel/bpf/bpf_lsm.c
@@ -315,6 +315,7 @@ BTF_ID(func, bpf_lsm_inode_create)
BTF_ID(func, bpf_lsm_inode_free_security)
BTF_ID(func, bpf_lsm_inode_getattr)
BTF_ID(func, bpf_lsm_inode_getxattr)
+BTF_ID(func, bpf_lsm_inode_init_security)
BTF_ID(func, bpf_lsm_inode_mknod)
BTF_ID(func, bpf_lsm_inode_need_killpriv)
BTF_ID(func, bpf_lsm_inode_post_setxattr)
diff --git a/security/bpf/hooks.c b/security/bpf/hooks.c
index 40efde233f3a..d7c44c5c0e30 100644
--- a/security/bpf/hooks.c
+++ b/security/bpf/hooks.c
@@ -30,6 +30,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) = {
--
2.53.0
next prev parent reply other threads:[~2026-06-30 18:40 UTC|newest]
Thread overview: 15+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-06-30 18:39 [PATCH v4 bpf-next 0/3] bpf: add bpf_init_inode_xattr kfunc for atomic inode labeling David Windsor
2026-06-30 18:39 ` [PATCH v4 bpf-next 1/3] security: pass inode_init_security xattrs via struct lsm_xattrs David Windsor
2026-06-30 18:46 ` sashiko-bot
2026-06-30 18:39 ` David Windsor [this message]
2026-06-30 18:46 ` [PATCH v4 bpf-next 2/3] bpf: add bpf_init_inode_xattr kfunc for atomic inode labeling David Windsor
2026-06-30 18:54 ` sashiko-bot
2026-07-01 22:54 ` David Windsor
2026-06-30 19:20 ` Paul Moore
2026-07-01 6:09 ` Alexei Starovoitov
2026-07-01 12:55 ` Paul Moore
2026-07-01 15:09 ` Paul Moore
2026-07-01 22:58 ` David Windsor
2026-07-01 23:21 ` Paul Moore
2026-06-30 18:39 ` [PATCH v4 bpf-next 3/3] selftests/bpf: add tests for bpf_init_inode_xattr kfunc David Windsor
2026-06-30 18:51 ` sashiko-bot
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260630183956.281293-3-dwindsor@gmail.com \
--to=dwindsor@gmail.com \
--cc=andrii@kernel.org \
--cc=ast@kernel.org \
--cc=bpf@vger.kernel.org \
--cc=brauner@kernel.org \
--cc=casey@schaufler-ca.com \
--cc=daniel@iogearbox.net \
--cc=dmitry.kasatkin@gmail.com \
--cc=eddyz87@gmail.com \
--cc=emil@etsalapatis.com \
--cc=eric.snowberg@oracle.com \
--cc=jack@suse.cz \
--cc=jmorris@namei.org \
--cc=john.fastabend@gmail.com \
--cc=jolsa@kernel.org \
--cc=kpsingh@kernel.org \
--cc=linux-fsdevel@vger.kernel.org \
--cc=linux-integrity@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-kselftest@vger.kernel.org \
--cc=linux-security-module@vger.kernel.org \
--cc=martin.lau@linux.dev \
--cc=mattbobrowski@google.com \
--cc=memxor@gmail.com \
--cc=omosnace@redhat.com \
--cc=paul@paul-moore.com \
--cc=roberto.sassu@huawei.com \
--cc=selinux@vger.kernel.org \
--cc=serge@hallyn.com \
--cc=shuah@kernel.org \
--cc=song@kernel.org \
--cc=stephen.smalley.work@gmail.com \
--cc=viro@zeniv.linux.org.uk \
--cc=yonghong.song@linux.dev \
--cc=zohar@linux.ibm.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.