* Re: [PATCH v4 bpf-next 2/3] bpf: add bpf_init_inode_xattr kfunc for atomic inode labeling
From: Paul Moore @ 2026-06-30 19:20 UTC (permalink / raw)
To: David Windsor
Cc: Alexei Starovoitov, Daniel Borkmann, Andrii Nakryiko,
Martin KaFai Lau, Eduard Zingerman, Song Liu, Yonghong Song,
John Fastabend, KP Singh, Jiri Olsa, Kumar Kartikeya Dwivedi,
Emil Tsalapatis, Matt Bobrowski, James Morris, Serge E . Hallyn,
Casey Schaufler, Stephen Smalley, Ondrej Mosnacek, Mimi Zohar,
Roberto Sassu, Dmitry Kasatkin, Eric Snowberg, Alexander Viro,
Christian Brauner, Jan Kara, Shuah Khan, bpf,
linux-security-module, linux-fsdevel, linux-integrity, selinux,
linux-kselftest, linux-kernel
In-Reply-To: <20260630183956.281293-3-dwindsor@gmail.com>
On Tue, Jun 30, 2026 at 2:40 PM David Windsor <dwindsor@gmail.com> 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 <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;
> +}
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
^ permalink raw reply
* Re: [PATCH v4 bpf-next 2/3] bpf: add bpf_init_inode_xattr kfunc for atomic inode labeling
From: David Windsor @ 2026-06-30 18:46 UTC (permalink / raw)
To: Alexei Starovoitov, Daniel Borkmann, Andrii Nakryiko,
Martin KaFai Lau, Eduard Zingerman, Song Liu, Yonghong Song,
John Fastabend, KP Singh, Jiri Olsa, Kumar Kartikeya Dwivedi,
Emil Tsalapatis, Matt Bobrowski, Paul Moore, James Morris,
Serge E . Hallyn, Casey Schaufler, Stephen Smalley,
Ondrej Mosnacek, Mimi Zohar, Roberto Sassu, Dmitry Kasatkin,
Eric Snowberg, Alexander Viro, Christian Brauner, Jan Kara,
Shuah Khan
Cc: bpf, linux-security-module, linux-fsdevel, linux-integrity,
selinux, linux-kselftest, linux-kernel
In-Reply-To: <20260630183956.281293-3-dwindsor@gmail.com>
On Thu, Jun 25, 2026 at 7:23 AM Christian Brauner <brauner@kernel.org> wrote:
<snip>
>
> We expose a bunch of VFS heavy operations for various security modules
> and this is really not different. For xattrs we have it all centralized
> in the VFS and in general all VFS related bpf kfuncs should continue
> living there and be registered there. Anything that's just bpf infra
> specific can go to security/bpf/kfuncs.c instead. But anyway, it's a bpf
> specific helper so it's the bpf maintainer's call.
After Alexei's requested changes removing the attach-time checks,
there's really not much left to go in an LSM-specific kfuncs file. The
bpf infra plumbing for registering the kfunc and bpf_xattrs_used()
seem to be the only LSM-specific bits aside from the kfunc.
I am willing to put this code anywhere. I've tried to CC all involved
in all 3 patches, even though there's some split in concerns.
^ permalink raw reply
* [PATCH v4 bpf-next 3/3] selftests/bpf: add tests for bpf_init_inode_xattr kfunc
From: David Windsor @ 2026-06-30 18:39 UTC (permalink / raw)
To: Alexei Starovoitov, Daniel Borkmann, Andrii Nakryiko,
Martin KaFai Lau, Eduard Zingerman, Song Liu, Yonghong Song,
John Fastabend, KP Singh, Jiri Olsa, Kumar Kartikeya Dwivedi,
Emil Tsalapatis, Matt Bobrowski, Paul Moore, James Morris,
Serge E . Hallyn, Casey Schaufler, Stephen Smalley,
Ondrej Mosnacek, Mimi Zohar, Roberto Sassu, Dmitry Kasatkin,
Eric Snowberg, Alexander Viro, Christian Brauner, Jan Kara,
Shuah Khan
Cc: bpf, linux-security-module, linux-fsdevel, linux-integrity,
selinux, linux-kselftest, linux-kernel, David Windsor
In-Reply-To: <20260630183956.281293-1-dwindsor@gmail.com>
Test bpf atomic inode xattr labeling in inode_init_security.
Signed-off-by: David Windsor <dwindsor@gmail.com>
---
tools/testing/selftests/bpf/bpf_kfuncs.h | 5 +
.../selftests/bpf/prog_tests/fs_kfuncs.c | 117 ++++++++++++++++++
.../bpf/progs/test_init_inode_xattr.c | 31 +++++
3 files changed, 153 insertions(+)
create mode 100644 tools/testing/selftests/bpf/progs/test_init_inode_xattr.c
diff --git a/tools/testing/selftests/bpf/bpf_kfuncs.h b/tools/testing/selftests/bpf/bpf_kfuncs.h
index ae71e9b69051..2639f9f94195 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 43a26ec69a8e..8b2e0d433aea 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,116 @@ 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_and_load();
+ if (!ASSERT_OK_PTR(skel[i], "open_and_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 +399,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 000000000000..cb378db957aa
--- /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;
+
+static const char xattr_name[] = "bpf.test_label";
+char xattr_value[] = "unconfined_u:object_r:user_home_t:s0";
+
+SEC("lsm.s/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;
+}
--
2.53.0
^ permalink raw reply related
* [PATCH v4 bpf-next 2/3] bpf: add bpf_init_inode_xattr kfunc for atomic inode labeling
From: David Windsor @ 2026-06-30 18:39 UTC (permalink / raw)
To: Alexei Starovoitov, Daniel Borkmann, Andrii Nakryiko,
Martin KaFai Lau, Eduard Zingerman, Song Liu, Yonghong Song,
John Fastabend, KP Singh, Jiri Olsa, Kumar Kartikeya Dwivedi,
Emil Tsalapatis, Matt Bobrowski, Paul Moore, James Morris,
Serge E . Hallyn, Casey Schaufler, Stephen Smalley,
Ondrej Mosnacek, Mimi Zohar, Roberto Sassu, Dmitry Kasatkin,
Eric Snowberg, Alexander Viro, Christian Brauner, Jan Kara,
Shuah Khan
Cc: bpf, linux-security-module, linux-fsdevel, linux-integrity,
selinux, linux-kselftest, linux-kernel, David Windsor
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
^ permalink raw reply related
* [PATCH v4 bpf-next 1/3] security: pass inode_init_security xattrs via struct lsm_xattrs
From: David Windsor @ 2026-06-30 18:39 UTC (permalink / raw)
To: Alexei Starovoitov, Daniel Borkmann, Andrii Nakryiko,
Martin KaFai Lau, Eduard Zingerman, Song Liu, Yonghong Song,
John Fastabend, KP Singh, Jiri Olsa, Kumar Kartikeya Dwivedi,
Emil Tsalapatis, Matt Bobrowski, Paul Moore, James Morris,
Serge E . Hallyn, Casey Schaufler, Stephen Smalley,
Ondrej Mosnacek, Mimi Zohar, Roberto Sassu, Dmitry Kasatkin,
Eric Snowberg, Alexander Viro, Christian Brauner, Jan Kara,
Shuah Khan
Cc: bpf, linux-security-module, linux-fsdevel, linux-integrity,
selinux, linux-kselftest, linux-kernel, David Windsor
In-Reply-To: <20260630183956.281293-1-dwindsor@gmail.com>
inode_init_security receives the LSM xattr array and its count as
separate parameters. For better compatibility with the bpf verifier,
update inode_init_security and its callers to consolidate these
parameters into a single context object: struct lsm_xattrs.
Signed-off-by: David Windsor <dwindsor@gmail.com>
---
include/linux/evm.h | 9 +++++----
include/linux/lsm_hook_defs.h | 4 ++--
include/linux/lsm_hooks.h | 16 +++++++---------
include/linux/security.h | 5 +++++
security/integrity/evm/evm_main.c | 8 +++++---
security/security.c | 24 ++++++++++++------------
security/selinux/hooks.c | 4 ++--
security/smack/smack_lsm.c | 27 ++++++++++++---------------
8 files changed, 50 insertions(+), 47 deletions(-)
diff --git a/include/linux/evm.h b/include/linux/evm.h
index 913f4573b203..528f360f3308 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 65c9609ec207..5b2de7865ce8 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 b4f8cad53ddb..7afe06a8d4c6 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 153e9043058f..0be590c40689 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 */
diff --git a/security/integrity/evm/evm_main.c b/security/integrity/evm/evm_main.c
index b59e3f121b8a..b7158fc63543 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 71aea8fdf014..2ad7f09c1a61 100644
--- a/security/security.c
+++ b/security/security.c
@@ -1333,8 +1333,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 +1344,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,14 +1364,14 @@ 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);
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index 1a713d96206f..6bba6b212e17 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 ff115068c5c0..4501078430ca 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);
--
2.53.0
^ permalink raw reply related
* [PATCH v4 bpf-next 0/3] bpf: add bpf_init_inode_xattr kfunc for atomic inode labeling
From: David Windsor @ 2026-06-30 18:39 UTC (permalink / raw)
To: Alexei Starovoitov, Daniel Borkmann, Andrii Nakryiko,
Martin KaFai Lau, Eduard Zingerman, Song Liu, Yonghong Song,
John Fastabend, KP Singh, Jiri Olsa, Kumar Kartikeya Dwivedi,
Emil Tsalapatis, Matt Bobrowski, Paul Moore, James Morris,
Serge E . Hallyn, Casey Schaufler, Stephen Smalley,
Ondrej Mosnacek, Mimi Zohar, Roberto Sassu, Dmitry Kasatkin,
Eric Snowberg, Alexander Viro, Christian Brauner, Jan Kara,
Shuah Khan
Cc: bpf, linux-security-module, linux-fsdevel, linux-integrity,
selinux, linux-kselftest, linux-kernel, David Windsor
Many in-kernel LSMs (SELinux, Smack, IMA) store security labels in
extended attributes. For these LSMs, atomic labeling during inode
creation is critical: if the inode becomes accessible before its xattr
is set, it is briefly unlabeled, which can disrupt LSMs making policy
decisions based on file labels.
Existing LSMs solve this by setting xattrs directly in the
inode_init_security hook, which runs before the inode becomes
accessible. BPF LSM programs currently lack this capability because
the hook uses an output parameter (xattr_count) that BPF programs
cannot write to, and existing kfuncs like bpf_set_dentry_xattr
require a dentry that isn't available until after the inode is
accessible.
This series introduces the bpf_init_inode_xattr() kfunc, which takes
the combined inode_init_security xattr context argument to access
xattrs and xattr_count, and internally writes to xattr_count via
lsm_get_xattr_slot().
v4:
- introduce struct lsm_xattrs in separate patch (Alexei, Paul)
- rename struct xattr_ctx to struct lsm_xattrs (Paul)
- make lsm_xattrs.xattr_count unsigned int (Paul)
- drop new_xattrs/xattr_count locals in
security_inode_init_security() (Paul)
- fold __bpf_init_inode_xattr() into bpf_init_inode_xattr() (Paul)
- drop bpf_fs_kfuncs_filter() attach-point check; rely on verifier
type enforcement (Alexei)
- drop attach-time cap; enforce slot budget in the kfunc (Alexei)
- allocate the combined xattr with GFP_NOFS (sashiko-bot)
- replace init_inode_xattr_attach_cap selftest with runtime
init_inode_xattr_slot_limit
v3:
- rename struct lsm_xattr_ctx to struct xattr_ctx (Paul)
- increase BPF_LSM_INODE_INIT_XATTRS to 4 (Song)
- enforce per-hook attachment cap at attach time to prevent
runtime rejection (Paul)
- add init_inode_xattr_attach_cap selftest
v2:
- pass the xattr state as a combined context object and drop the
verifier fixup path (Kumar)
- restrict bpf_init_inode_xattr labels to bpf.* namespace (Matt)
- cap bpf_init_inode_xattr() at BPF_LSM_INODE_INIT_XATTRS slots per
invocation (AI)
David Windsor (3):
security: pass inode_init_security xattrs via struct lsm_xattrs
bpf: add bpf_init_inode_xattr kfunc for atomic inode labeling
selftests/bpf: add tests for bpf_init_inode_xattr kfunc
fs/bpf_fs_kfuncs.c | 79 +++++++++++
include/linux/bpf_lsm.h | 3 +
include/linux/evm.h | 9 +-
include/linux/lsm_hook_defs.h | 4 +-
include/linux/lsm_hooks.h | 16 +--
include/linux/security.h | 5 +
kernel/bpf/bpf_lsm.c | 1 +
security/bpf/hooks.c | 1 +
security/integrity/evm/evm_main.c | 8 +-
security/security.c | 24 ++--
security/selinux/hooks.c | 4 +-
security/smack/smack_lsm.c | 27 ++--
tools/testing/selftests/bpf/bpf_kfuncs.h | 5 +
.../selftests/bpf/prog_tests/lsm_kfuncs.c | 129 ++++++++++++++++++
.../bpf/progs/test_init_inode_xattr.c | 31 +++++
15 files changed, 299 insertions(+), 47 deletions(-)
create mode 100644 tools/testing/selftests/bpf/prog_tests/lsm_kfuncs.c
create mode 100644 tools/testing/selftests/bpf/progs/test_init_inode_xattr.c
base-commit: e771677c937da5808f7b6c1f0e4a97ec1a84f8a8
--
2.53.0
^ permalink raw reply
* Re: [PATCH] lsm: cleanup repeated lsm_blob_size_update() calls in lsm_prepare()
From: Casey Schaufler @ 2026-06-30 16:26 UTC (permalink / raw)
To: Matt Bobrowski, linux-security-module
Cc: Paul Moore, James Morris, Serge E . Hallyn, Casey Schaufler
In-Reply-To: <20260629210150.832576-1-mattbobrowski@google.com>
On 6/29/2026 2:01 PM, Matt Bobrowski wrote:
> Centralize the definition of LSM security blob fields using an X-macro
> (LSM_BLOBS_LIST). This reduces repetitive boilerplate code across
> struct lsm_blob_sizes, blob size registration in lsm_prepare(), and
> debug log printing in security_init().
>
> Signed-off-by: Matt Bobrowski <mattbobrowski@google.com>
> ---
> include/linux/lsm_hooks.h | 42 ++++++++++++++++------------
> security/lsm_init.c | 59 ++++++++++-----------------------------
> 2 files changed, 38 insertions(+), 63 deletions(-)
>
> diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h
> index b4f8cad53ddb..0e73b22bdeea 100644
> --- a/include/linux/lsm_hooks.h
> +++ b/include/linux/lsm_hooks.h
> @@ -98,28 +98,34 @@ struct security_hook_list {
> const struct lsm_id *lsmid;
> } __randomize_layout;
>
> +#define LSM_BLOBS_LIST(X) \
> + X(cred) \
> + X(file) \
> + X(backing_file) \
> + X(ib) \
> + X(inode) \
> + X(sock) \
> + X(superblock) \
> + X(ipc) \
> + X(key) \
> + X(msg_msg) \
> + X(perf_event) \
> + X(task) \
> + X(tun_dev) \
> + X(xattr_count) \
> + X(bdev) \
> + X(bpf_map) \
> + X(bpf_prog) \
> + X(bpf_token)
> +
> /*
> * Security blob size or offset data.
> + * Note: lbs_xattr_count is the number of xattr slots in new_xattrs array.
> */
> struct lsm_blob_sizes {
> - unsigned int lbs_cred;
> - unsigned int lbs_file;
> - unsigned int lbs_backing_file;
> - unsigned int lbs_ib;
> - unsigned int lbs_inode;
> - unsigned int lbs_sock;
> - unsigned int lbs_superblock;
> - unsigned int lbs_ipc;
> - unsigned int lbs_key;
> - unsigned int lbs_msg_msg;
> - unsigned int lbs_perf_event;
> - unsigned int lbs_task;
> - unsigned int lbs_xattr_count; /* num xattr slots in new_xattrs array */
> - unsigned int lbs_tun_dev;
> - unsigned int lbs_bdev;
> - unsigned int lbs_bpf_map;
> - unsigned int lbs_bpf_prog;
> - unsigned int lbs_bpf_token;
> +#define LSM_BLOB_SIZE(name) unsigned int lbs_##name;
> + LSM_BLOBS_LIST(LSM_BLOB_SIZE);
> +#undef LSM_BLOB_SIZE
> };
>
> /*
> diff --git a/security/lsm_init.c b/security/lsm_init.c
> index 7c0fd17f1601..c256f1c33efa 100644
> --- a/security/lsm_init.c
> +++ b/security/lsm_init.c
> @@ -282,40 +282,24 @@ static void __init lsm_blob_size_update(unsigned int *sz_req,
> * lsm_prepare - Prepare the LSM framework for a new LSM
> * @lsm: LSM definition
> */
> -static void __init lsm_prepare(struct lsm_info *lsm)
> +static void __init lsm_prepare(const struct lsm_info *lsm)
This is an unrelated change which should be in a separate patch.
> {
> struct lsm_blob_sizes *blobs = lsm->blobs;
>
> if (!blobs)
> return;
>
> - /* Register the LSM blob sizes. */
> - blobs = lsm->blobs;
> - lsm_blob_size_update(&blobs->lbs_cred, &blob_sizes.lbs_cred);
> - lsm_blob_size_update(&blobs->lbs_file, &blob_sizes.lbs_file);
> - lsm_blob_size_update(&blobs->lbs_backing_file,
> - &blob_sizes.lbs_backing_file);
> - lsm_blob_size_update(&blobs->lbs_ib, &blob_sizes.lbs_ib);
> - /* inode blob gets an rcu_head in addition to LSM blobs. */
> + /* The inode blob (inode->i_security) gets an rcu_head in addition to
> + * LSM blobs.
> + */
Please retain the comment style of the file. Either
/* ... */
/*
* ...
*/
but not
/* ...
*/
> if (blobs->lbs_inode && blob_sizes.lbs_inode == 0)
> blob_sizes.lbs_inode = sizeof(struct rcu_head);
This is where I dislike the macro approach. While this particular
exception is easily handled, others may not be. A blob whose size
isn't represented as an unsigned int (e.g a bool) would not use
lsm_blob_size_update(). While all of the existing blob sizes are
unsigned ints, I have considered bools on more than one occasion.
> - lsm_blob_size_update(&blobs->lbs_inode, &blob_sizes.lbs_inode);
> - lsm_blob_size_update(&blobs->lbs_ipc, &blob_sizes.lbs_ipc);
> - lsm_blob_size_update(&blobs->lbs_key, &blob_sizes.lbs_key);
> - lsm_blob_size_update(&blobs->lbs_msg_msg, &blob_sizes.lbs_msg_msg);
> - lsm_blob_size_update(&blobs->lbs_perf_event,
> - &blob_sizes.lbs_perf_event);
> - lsm_blob_size_update(&blobs->lbs_sock, &blob_sizes.lbs_sock);
> - lsm_blob_size_update(&blobs->lbs_superblock,
> - &blob_sizes.lbs_superblock);
> - lsm_blob_size_update(&blobs->lbs_task, &blob_sizes.lbs_task);
> - lsm_blob_size_update(&blobs->lbs_tun_dev, &blob_sizes.lbs_tun_dev);
> - lsm_blob_size_update(&blobs->lbs_xattr_count,
> - &blob_sizes.lbs_xattr_count);
> - lsm_blob_size_update(&blobs->lbs_bdev, &blob_sizes.lbs_bdev);
> - lsm_blob_size_update(&blobs->lbs_bpf_map, &blob_sizes.lbs_bpf_map);
> - lsm_blob_size_update(&blobs->lbs_bpf_prog, &blob_sizes.lbs_bpf_prog);
> - lsm_blob_size_update(&blobs->lbs_bpf_token, &blob_sizes.lbs_bpf_token);
> +
> + /* Register the LSM blob sizes. */
> +#define UPDATE_LSM_BLOB_SIZE(name) \
> + lsm_blob_size_update(&blobs->lbs_##name, &blob_sizes.lbs_##name);
> + LSM_BLOBS_LIST(UPDATE_LSM_BLOB_SIZE);
> +#undef UPDATE_LSM_BLOB_SIZE
> }
>
> /**
> @@ -441,25 +425,10 @@ int __init security_init(void)
> lsm_prepare(*lsm);
>
> if (lsm_debug) {
> - lsm_pr("blob(cred) size %d\n", blob_sizes.lbs_cred);
> - lsm_pr("blob(file) size %d\n", blob_sizes.lbs_file);
> - lsm_pr("blob(backing_file) size %d\n",
> - blob_sizes.lbs_backing_file);
> - lsm_pr("blob(ib) size %d\n", blob_sizes.lbs_ib);
> - lsm_pr("blob(inode) size %d\n", blob_sizes.lbs_inode);
> - lsm_pr("blob(ipc) size %d\n", blob_sizes.lbs_ipc);
> - lsm_pr("blob(key) size %d\n", blob_sizes.lbs_key);
> - lsm_pr("blob(msg_msg)_size %d\n", blob_sizes.lbs_msg_msg);
> - lsm_pr("blob(sock) size %d\n", blob_sizes.lbs_sock);
> - lsm_pr("blob(superblock) size %d\n", blob_sizes.lbs_superblock);
> - lsm_pr("blob(perf_event) size %d\n", blob_sizes.lbs_perf_event);
> - lsm_pr("blob(task) size %d\n", blob_sizes.lbs_task);
> - lsm_pr("blob(tun_dev) size %d\n", blob_sizes.lbs_tun_dev);
> - lsm_pr("blob(xattr) count %d\n", blob_sizes.lbs_xattr_count);
> - lsm_pr("blob(bdev) size %d\n", blob_sizes.lbs_bdev);
> - lsm_pr("blob(bpf_map) size %d\n", blob_sizes.lbs_bpf_map);
> - lsm_pr("blob(bpf_prog) size %d\n", blob_sizes.lbs_bpf_prog);
> - lsm_pr("blob(bpf_token) size %d\n", blob_sizes.lbs_bpf_token);
> +#define PRINT_LSM_BLOB_SIZE(name) \
> + lsm_pr("blob(" #name ") size %d\n", blob_sizes.lbs_##name);
> + LSM_BLOBS_LIST(PRINT_LSM_BLOB_SIZE);
> +#undef PRINT_LSM_BLOB_SIZE
> }
>
> if (blob_sizes.lbs_file)
^ permalink raw reply
* Re: [PATCH stable/linux-5.10.y 0/7] Backport Fix incorrect overlayfs mmap() and mprotect() LSM access controls
From: Amir Goldstein @ 2026-06-30 11:01 UTC (permalink / raw)
To: Cai Xinchen
Cc: viro, brauner, jack, miklos, paul, jmorris, serge,
stephen.smalley.work, omosnace, gregkh, sashal, bboscaccy,
linux-fsdevel, linux-kernel, linux-unionfs, linux-security-module,
selinux, bpf, stable, lujialin4
In-Reply-To: <f4c8f5fe-30c3-4e7f-8512-7a2befdd1ed3@huawei.com>
On Tue, Jun 30, 2026 at 5:06 AM Cai Xinchen <caixinchen1@huawei.com> wrote:
>
> Thank you for your reply. Regarding the two points of feedback:
>
> First, 6.1 is still in the process of being adapted.
So do not propose for 5.10 please.
>
> Second, this patch set is primarily intended to fix CVE-2026-46054, but
> it seems that for lower versions to implement SELinux checks for overlay
> mmap/mprotect checks, some dependencies are unavoidable. In such cases,
> should we add more tests to reduce the risk and integrate the changes,
> or should we simply not fix this issue? If more tests are needed, are
> there any recommended test suites?
I have concerns.
The burdn of proof is on you.
Thanks,
Amir.
^ permalink raw reply
* Re: [PATCH stable/linux-5.10.y 0/7] Backport Fix incorrect overlayfs mmap() and mprotect() LSM access controls
From: Cai Xinchen @ 2026-06-30 3:06 UTC (permalink / raw)
To: Amir Goldstein
Cc: viro, brauner, jack, miklos, paul, jmorris, serge,
stephen.smalley.work, omosnace, gregkh, sashal, bboscaccy,
linux-fsdevel, linux-kernel, linux-unionfs, linux-security-module,
selinux, bpf, stable, lujialin4
In-Reply-To: <CAOQ4uxjcD0-PHqqmrpEvkLRgtKJGe8-n+6DQyBngjN2TorwU+g@mail.gmail.com>
Thank you for your reply. Regarding the two points of feedback:
First, 6.1 is still in the process of being adapted.
Second, this patch set is primarily intended to fix CVE-2026-46054, but
it seems that for lower versions to implement SELinux checks for overlay
mmap/mprotect checks, some dependencies are unavoidable. In such cases,
should we add more tests to reduce the risk and integrate the changes,
or should we simply not fix this issue? If more tests are needed, are
there any recommended test suites?
On 6/30/2026 1:31 AM, Amir Goldstein wrote:
> On Mon, Jun 29, 2026 at 8:38 AM Cai Xinchen <caixinchen1@huawei.com> wrote:
>> ackport the patch series
>> "Fix incorrect overlayfs mmap() and mprotect() LSM access controls" [1]
>> to 5.10 lts
> Chai,
>
> First of all, I don't think that stable maintainers are picking backports
> to 5.10 that were not backported to 6.1 and 5.15.
>
> Second, backporting backing_file as a dependency to LTS kernels is a pretty
> intrusive change, so your description above is very much lacking.
>
> Please do not backport backing_file to any of the LTS kernels without providing
> detailed explanation to try and convince the vfs maintainers that you
> verified this
> bacport is safe for the LTS kernel, because honestly, this looks a bit
> risky for me.
>
> Thanks,
> Amir.
^ permalink raw reply
* [PATCH] lsm: cleanup repeated lsm_blob_size_update() calls in lsm_prepare()
From: Matt Bobrowski @ 2026-06-29 21:01 UTC (permalink / raw)
To: linux-security-module
Cc: Paul Moore, James Morris, Serge E . Hallyn, Matt Bobrowski
Centralize the definition of LSM security blob fields using an X-macro
(LSM_BLOBS_LIST). This reduces repetitive boilerplate code across
struct lsm_blob_sizes, blob size registration in lsm_prepare(), and
debug log printing in security_init().
Signed-off-by: Matt Bobrowski <mattbobrowski@google.com>
---
include/linux/lsm_hooks.h | 42 ++++++++++++++++------------
security/lsm_init.c | 59 ++++++++++-----------------------------
2 files changed, 38 insertions(+), 63 deletions(-)
diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h
index b4f8cad53ddb..0e73b22bdeea 100644
--- a/include/linux/lsm_hooks.h
+++ b/include/linux/lsm_hooks.h
@@ -98,28 +98,34 @@ struct security_hook_list {
const struct lsm_id *lsmid;
} __randomize_layout;
+#define LSM_BLOBS_LIST(X) \
+ X(cred) \
+ X(file) \
+ X(backing_file) \
+ X(ib) \
+ X(inode) \
+ X(sock) \
+ X(superblock) \
+ X(ipc) \
+ X(key) \
+ X(msg_msg) \
+ X(perf_event) \
+ X(task) \
+ X(tun_dev) \
+ X(xattr_count) \
+ X(bdev) \
+ X(bpf_map) \
+ X(bpf_prog) \
+ X(bpf_token)
+
/*
* Security blob size or offset data.
+ * Note: lbs_xattr_count is the number of xattr slots in new_xattrs array.
*/
struct lsm_blob_sizes {
- unsigned int lbs_cred;
- unsigned int lbs_file;
- unsigned int lbs_backing_file;
- unsigned int lbs_ib;
- unsigned int lbs_inode;
- unsigned int lbs_sock;
- unsigned int lbs_superblock;
- unsigned int lbs_ipc;
- unsigned int lbs_key;
- unsigned int lbs_msg_msg;
- unsigned int lbs_perf_event;
- unsigned int lbs_task;
- unsigned int lbs_xattr_count; /* num xattr slots in new_xattrs array */
- unsigned int lbs_tun_dev;
- unsigned int lbs_bdev;
- unsigned int lbs_bpf_map;
- unsigned int lbs_bpf_prog;
- unsigned int lbs_bpf_token;
+#define LSM_BLOB_SIZE(name) unsigned int lbs_##name;
+ LSM_BLOBS_LIST(LSM_BLOB_SIZE);
+#undef LSM_BLOB_SIZE
};
/*
diff --git a/security/lsm_init.c b/security/lsm_init.c
index 7c0fd17f1601..c256f1c33efa 100644
--- a/security/lsm_init.c
+++ b/security/lsm_init.c
@@ -282,40 +282,24 @@ static void __init lsm_blob_size_update(unsigned int *sz_req,
* lsm_prepare - Prepare the LSM framework for a new LSM
* @lsm: LSM definition
*/
-static void __init lsm_prepare(struct lsm_info *lsm)
+static void __init lsm_prepare(const struct lsm_info *lsm)
{
struct lsm_blob_sizes *blobs = lsm->blobs;
if (!blobs)
return;
- /* Register the LSM blob sizes. */
- blobs = lsm->blobs;
- lsm_blob_size_update(&blobs->lbs_cred, &blob_sizes.lbs_cred);
- lsm_blob_size_update(&blobs->lbs_file, &blob_sizes.lbs_file);
- lsm_blob_size_update(&blobs->lbs_backing_file,
- &blob_sizes.lbs_backing_file);
- lsm_blob_size_update(&blobs->lbs_ib, &blob_sizes.lbs_ib);
- /* inode blob gets an rcu_head in addition to LSM blobs. */
+ /* The inode blob (inode->i_security) gets an rcu_head in addition to
+ * LSM blobs.
+ */
if (blobs->lbs_inode && blob_sizes.lbs_inode == 0)
blob_sizes.lbs_inode = sizeof(struct rcu_head);
- lsm_blob_size_update(&blobs->lbs_inode, &blob_sizes.lbs_inode);
- lsm_blob_size_update(&blobs->lbs_ipc, &blob_sizes.lbs_ipc);
- lsm_blob_size_update(&blobs->lbs_key, &blob_sizes.lbs_key);
- lsm_blob_size_update(&blobs->lbs_msg_msg, &blob_sizes.lbs_msg_msg);
- lsm_blob_size_update(&blobs->lbs_perf_event,
- &blob_sizes.lbs_perf_event);
- lsm_blob_size_update(&blobs->lbs_sock, &blob_sizes.lbs_sock);
- lsm_blob_size_update(&blobs->lbs_superblock,
- &blob_sizes.lbs_superblock);
- lsm_blob_size_update(&blobs->lbs_task, &blob_sizes.lbs_task);
- lsm_blob_size_update(&blobs->lbs_tun_dev, &blob_sizes.lbs_tun_dev);
- lsm_blob_size_update(&blobs->lbs_xattr_count,
- &blob_sizes.lbs_xattr_count);
- lsm_blob_size_update(&blobs->lbs_bdev, &blob_sizes.lbs_bdev);
- lsm_blob_size_update(&blobs->lbs_bpf_map, &blob_sizes.lbs_bpf_map);
- lsm_blob_size_update(&blobs->lbs_bpf_prog, &blob_sizes.lbs_bpf_prog);
- lsm_blob_size_update(&blobs->lbs_bpf_token, &blob_sizes.lbs_bpf_token);
+
+ /* Register the LSM blob sizes. */
+#define UPDATE_LSM_BLOB_SIZE(name) \
+ lsm_blob_size_update(&blobs->lbs_##name, &blob_sizes.lbs_##name);
+ LSM_BLOBS_LIST(UPDATE_LSM_BLOB_SIZE);
+#undef UPDATE_LSM_BLOB_SIZE
}
/**
@@ -441,25 +425,10 @@ int __init security_init(void)
lsm_prepare(*lsm);
if (lsm_debug) {
- lsm_pr("blob(cred) size %d\n", blob_sizes.lbs_cred);
- lsm_pr("blob(file) size %d\n", blob_sizes.lbs_file);
- lsm_pr("blob(backing_file) size %d\n",
- blob_sizes.lbs_backing_file);
- lsm_pr("blob(ib) size %d\n", blob_sizes.lbs_ib);
- lsm_pr("blob(inode) size %d\n", blob_sizes.lbs_inode);
- lsm_pr("blob(ipc) size %d\n", blob_sizes.lbs_ipc);
- lsm_pr("blob(key) size %d\n", blob_sizes.lbs_key);
- lsm_pr("blob(msg_msg)_size %d\n", blob_sizes.lbs_msg_msg);
- lsm_pr("blob(sock) size %d\n", blob_sizes.lbs_sock);
- lsm_pr("blob(superblock) size %d\n", blob_sizes.lbs_superblock);
- lsm_pr("blob(perf_event) size %d\n", blob_sizes.lbs_perf_event);
- lsm_pr("blob(task) size %d\n", blob_sizes.lbs_task);
- lsm_pr("blob(tun_dev) size %d\n", blob_sizes.lbs_tun_dev);
- lsm_pr("blob(xattr) count %d\n", blob_sizes.lbs_xattr_count);
- lsm_pr("blob(bdev) size %d\n", blob_sizes.lbs_bdev);
- lsm_pr("blob(bpf_map) size %d\n", blob_sizes.lbs_bpf_map);
- lsm_pr("blob(bpf_prog) size %d\n", blob_sizes.lbs_bpf_prog);
- lsm_pr("blob(bpf_token) size %d\n", blob_sizes.lbs_bpf_token);
+#define PRINT_LSM_BLOB_SIZE(name) \
+ lsm_pr("blob(" #name ") size %d\n", blob_sizes.lbs_##name);
+ LSM_BLOBS_LIST(PRINT_LSM_BLOB_SIZE);
+#undef PRINT_LSM_BLOB_SIZE
}
if (blob_sizes.lbs_file)
--
2.55.0.rc0.799.gd6f94ed593-goog
^ permalink raw reply related
* Re: [PATCH stable/linux-5.10.y 0/7] Backport Fix incorrect overlayfs mmap() and mprotect() LSM access controls
From: Amir Goldstein @ 2026-06-29 17:31 UTC (permalink / raw)
To: Cai Xinchen
Cc: viro, brauner, jack, miklos, paul, jmorris, serge,
stephen.smalley.work, omosnace, gregkh, sashal, bboscaccy,
linux-fsdevel, linux-kernel, linux-unionfs, linux-security-module,
selinux, bpf, stable, lujialin4
In-Reply-To: <20260629070653.580879-1-caixinchen1@huawei.com>
On Mon, Jun 29, 2026 at 8:38 AM Cai Xinchen <caixinchen1@huawei.com> wrote:
>
> ackport the patch series
> "Fix incorrect overlayfs mmap() and mprotect() LSM access controls" [1]
> to 5.10 lts
Chai,
First of all, I don't think that stable maintainers are picking backports
to 5.10 that were not backported to 6.1 and 5.15.
Second, backporting backing_file as a dependency to LTS kernels is a pretty
intrusive change, so your description above is very much lacking.
Please do not backport backing_file to any of the LTS kernels without providing
detailed explanation to try and convince the vfs maintainers that you
verified this
bacport is safe for the LTS kernel, because honestly, this looks a bit
risky for me.
Thanks,
Amir.
^ permalink raw reply
* Re: [PATCH v6] rust: aref: make `AlwaysRefCounted::inc_ref` an associated function
From: Gary Guo @ 2026-06-29 14:37 UTC (permalink / raw)
To: Trevor Chan, gregkh, rafael, dakr, ojeda, a.hindborg, paul,
aliceryhl, airlied, simona, viro, brauner, igor.korotin, vireshk,
nm, sboyd, m.wilczynski, boqun, gary, axboe, daniel.almeida,
shankari.ak0208, lyude, j, lossin, acourbot, markus.probst,
driver-core, rust-for-linux, linux-kernel, linux-block,
linux-security-module, dri-devel, linux-fsdevel, linux-mm,
linux-pm, linux-pci, linux-pwm
Cc: david.m.ertman, iweiny, leon, bjorn3_gh, tmgross, tamird, work,
sergeh, matthew.brost, thomas.hellstrom, jack, ljs, liam,
bhelgaas, kwilczynski, ptikhomirov
In-Reply-To: <20260628095132.47753-1-trev@trevrosa.dev>
On Sun Jun 28, 2026 at 10:51 AM BST, Trevor Chan wrote:
> `AlwaysRefCounted::inc_ref` is a function that shouldn't be called lightly.
>
> To prevent accidentally calling it, change `inc_ref` to be an associated function.
>
> Modify all `AlwaysRefCounted` implementors to work with this change.
>
> Suggested-by: Benno Lossin <lossin@kernel.org>
> Link: https://github.com/Rust-for-Linux/linux/issues/1177
> Reviewed-by: Alice Ryhl <aliceryhl@google.com>
> Signed-off-by: Trevor Chan <trev@trevrosa.dev>
Reviewed-by: Gary Guo <gary@garyguo.net>
> ---
> Changes in v2:
> - Don't word wrap the patch
> Changes in v3:
> - Make argument name of `Empty::inc_ref` consistent with `Empty::dec_ref`
> Changes in v4:
> - Rebase to new rust-next, change new implementors
> - Reword explanation for change in `AlwaysRefCounted::inc_ref` doc comment
> Changes in v5:
> - Change commit message to be imperative
> Changes in v6:
> - Change all the implementors
> ---
> rust/kernel/auxiliary.rs | 4 ++--
> rust/kernel/block/mq/request.rs | 4 ++--
> rust/kernel/cred.rs | 4 ++--
> rust/kernel/device.rs | 4 ++--
> rust/kernel/device/property.rs | 4 ++--
> rust/kernel/drm/device.rs | 4 ++--
> rust/kernel/drm/gem/mod.rs | 4 ++--
> rust/kernel/drm/gpuvm/mod.rs | 4 ++--
> rust/kernel/drm/gpuvm/vm_bo.rs | 4 ++--
> rust/kernel/fs/file.rs | 8 ++++----
> rust/kernel/i2c.rs | 8 ++++----
> rust/kernel/mm.rs | 8 ++++----
> rust/kernel/mm/mmput_async.rs | 4 ++--
> rust/kernel/opp.rs | 4 ++--
> rust/kernel/pci.rs | 4 ++--
> rust/kernel/pid_namespace.rs | 4 ++--
> rust/kernel/platform.rs | 4 ++--
> rust/kernel/pwm.rs | 2 +-
> rust/kernel/sync/aref.rs | 11 +++++++----
> rust/kernel/task.rs | 4 ++--
> rust/kernel/usb.rs | 8 ++++----
> 21 files changed, 54 insertions(+), 51 deletions(-)
^ permalink raw reply
* Re: [RFC PATCH 1/4] capabily: Add new capable_noaudit
From: Serge E. Hallyn @ 2026-06-29 13:49 UTC (permalink / raw)
To: Christoph Hellwig
Cc: cem, linux-fsdevel, jack, djwong, serge, linux-security-module,
linux-kernel, linux-xfs
In-Reply-To: <20260629122939.GA21958@lst.de>
On Mon, Jun 29, 2026 at 02:29:39PM +0200, Christoph Hellwig wrote:
> On Fri, Jun 26, 2026 at 01:45:20PM +0200, cem@kernel.org wrote:
> > +extern bool capable_noaudit(int cap);
>
> No need for the extern.
>
> Otherwise this does look nice an clean to me:
>
> Reviewed-by: Christoph Hellwig <hch@lst.de>
>
> But if the security folks don't like we can live with the more
> verbose version of it I guess.
Honestly I'm ok either way. If people misunderstand the shortcut,
and ove-ruse it, that's safer than the other way. The one that
scare me more is ns_capable(¤t_user_ns, X). I need to do an
audit of the current users of that.
So I'm happy to put
Reviewed-by: Serge Hallyn <serge@hallyn.com>
on the set.
^ permalink raw reply
* Re: [RFC PATCH 4/4] capability: unexport has_capability_noaudit
From: Christoph Hellwig @ 2026-06-29 12:31 UTC (permalink / raw)
To: Darrick J. Wong
Cc: cem, linux-fsdevel, jack, hch, serge, linux-security-module,
linux-kernel, linux-xfs
In-Reply-To: <20260626152008.GW6078@frogsfrogsfrogs>
On Fri, Jun 26, 2026 at 08:20:08AM -0700, Darrick J. Wong wrote:
> Please update the kerneldoc for this function to mention that it only
> checks real capability, not effective capability. I'd like to prevent
> someone else from making the same mistakes I did with these functions
> that sound the same in documentation but have very different behaviors.
Yes, that would be useful. But make it a separate patch please, it's
not related to the unexporting in any way.
^ permalink raw reply
* Re: [RFC PATCH 4/4] capability: unexport has_capability_noaudit
From: Christoph Hellwig @ 2026-06-29 12:31 UTC (permalink / raw)
To: cem
Cc: linux-fsdevel, jack, djwong, hch, serge, linux-security-module,
linux-kernel, linux-xfs
In-Reply-To: <20260626114533.102138-5-cem@kernel.org>
On Fri, Jun 26, 2026 at 01:45:23PM +0200, cem@kernel.org wrote:
> From: Carlos Maiolino <cem@kernel.org>
>
> This has been originally exported to be used in xfs. Givin we are not
> using it anymore, unexport for consistency.
Looks good:
Reviewed-by: Christoph Hellwig <hch@lst.de>
^ permalink raw reply
* Re: [RFC PATCH 3/4] xfs: replace ns_capable_noaudit()
From: Christoph Hellwig @ 2026-06-29 12:30 UTC (permalink / raw)
To: Darrick J. Wong
Cc: cem, linux-fsdevel, jack, hch, serge, linux-security-module,
linux-kernel, linux-xfs
In-Reply-To: <20260626151900.GV6078@frogsfrogsfrogs>
On Fri, Jun 26, 2026 at 08:19:00AM -0700, Darrick J. Wong wrote:
> On Fri, Jun 26, 2026 at 01:45:22PM +0200, cem@kernel.org wrote:
> > From: Carlos Maiolino <cem@kernel.org>
> >
> > We don't need to use ns_capable_noaudit() as all we care is the initial
> > user namespace, use capable_noaudit() instead.
>
> Might as well do the one in xfs_fsmap.c too, since it was originally a
> capable() call.
Yes, we should do that one as well. But that's a separate patch.
^ permalink raw reply
* Re: [RFC PATCH 3/4] xfs: replace ns_capable_noaudit()
From: Christoph Hellwig @ 2026-06-29 12:30 UTC (permalink / raw)
To: cem
Cc: linux-fsdevel, jack, djwong, hch, serge, linux-security-module,
linux-kernel, linux-xfs
In-Reply-To: <20260626114533.102138-4-cem@kernel.org>
On Fri, Jun 26, 2026 at 01:45:22PM +0200, cem@kernel.org wrote:
> From: Carlos Maiolino <cem@kernel.org>
>
> We don't need to use ns_capable_noaudit() as all we care is the initial
> user namespace, use capable_noaudit() instead.
Looks good:
Reviewed-by: Christoph Hellwig <hch@lst.de>
^ permalink raw reply
* Re: [RFC PATCH 2/4] quota: Don't issue audit messages on quota enforcing
From: Christoph Hellwig @ 2026-06-29 12:30 UTC (permalink / raw)
To: cem
Cc: linux-fsdevel, jack, djwong, hch, serge, linux-security-module,
linux-kernel, linux-xfs
In-Reply-To: <20260626114533.102138-3-cem@kernel.org>
Looks good:
Reviewed-by: Christoph Hellwig <hch@lst.de>
(and that would carry over the the more verbose version if we don't
get the helper)
^ permalink raw reply
* Re: [RFC PATCH 1/4] capabily: Add new capable_noaudit
From: Christoph Hellwig @ 2026-06-29 12:29 UTC (permalink / raw)
To: cem
Cc: linux-fsdevel, jack, djwong, hch, serge, linux-security-module,
linux-kernel, linux-xfs
In-Reply-To: <20260626114533.102138-2-cem@kernel.org>
On Fri, Jun 26, 2026 at 01:45:20PM +0200, cem@kernel.org wrote:
> +extern bool capable_noaudit(int cap);
No need for the extern.
Otherwise this does look nice an clean to me:
Reviewed-by: Christoph Hellwig <hch@lst.de>
But if the security folks don't like we can live with the more
verbose version of it I guess.
^ permalink raw reply
* [PATCH v3 stable/linux-6.12.y 2/3] lsm: add backing_file LSM hooks
From: Cai Xinchen @ 2026-06-29 7:03 UTC (permalink / raw)
To: viro, brauner, jack, miklos, amir73il, paul, jmorris, serge,
stephen.smalley.work, omosnace, gregkh, sashal, bboscaccy,
caixinchen1
Cc: linux-fsdevel, linux-kernel, linux-unionfs, linux-security-module,
selinux, bpf, stable, lujialin4
In-Reply-To: <20260629070338.578858-1-caixinchen1@huawei.com>
From: Paul Moore <paul@paul-moore.com>
[ Upstream commit 6af36aeb147a06dea47c49859cd6ca5659aeb987 ]
Stacked filesystems such as overlayfs do not currently provide the
necessary mechanisms for LSMs to properly enforce access controls on the
mmap() and mprotect() operations. In order to resolve this gap, a LSM
security blob is being added to the backing_file struct and the following
new LSM hooks are being created:
security_backing_file_alloc()
security_backing_file_free()
security_mmap_backing_file()
The first two hooks are to manage the lifecycle of the LSM security blob
in the backing_file struct, while the third provides a new mmap() access
control point for the underlying backing file. It is also expected that
LSMs will likely want to update their security_file_mprotect() callback
to address issues with their mprotect() controls, but that does not
require a change to the security_file_mprotect() LSM hook.
There are a three other small changes to support these new LSM hooks:
* Pass the user file associated with a backing file down to
alloc_empty_backing_file() so it can be included in the
security_backing_file_alloc() hook.
* Add getter and setter functions for the backing_file struct LSM blob
as the backing_file struct remains private to fs/file_table.c.
* Constify the file struct field in the LSM common_audit_data struct to
better support LSMs that need to pass a const file struct pointer into
the common LSM audit code.
Thanks to Arnd Bergmann for identifying the missing EXPORT_SYMBOL_GPL()
and supplying a fixup.
Cc: stable@vger.kernel.org
Cc: linux-fsdevel@vger.kernel.org
Cc: linux-unionfs@vger.kernel.org
Cc: linux-erofs@lists.ozlabs.org
Reviewed-by: Amir Goldstein <amir73il@gmail.com>
Reviewed-by: Serge Hallyn <serge@hallyn.com>
Reviewed-by: Christian Brauner <brauner@kernel.org>
Signed-off-by: Paul Moore <paul@paul-moore.com>
[ Mainline declares lsm_backing_file_cache in security/lsm.h. Linux 6.12.y
does not have security/lsm_init.c or security/lsm.h; the cache variable
is defined locally as static struct kmem_cache *lsm_backing_file_cache in
security/security.c. ]
Signed-off-by: Cai Xinchen <caixinchen1@huawei.com>
---
fs/backing-file.c | 18 ++++--
fs/file_table.c | 27 +++++++--
fs/fuse/passthrough.c | 2 +-
fs/internal.h | 3 +-
fs/overlayfs/dir.c | 2 +-
fs/overlayfs/file.c | 3 +-
include/linux/backing-file.h | 4 +-
include/linux/fs.h | 13 ++++
include/linux/lsm_audit.h | 2 +-
include/linux/lsm_hook_defs.h | 5 ++
include/linux/lsm_hooks.h | 1 +
include/linux/security.h | 22 +++++++
security/security.c | 109 ++++++++++++++++++++++++++++++++++
13 files changed, 195 insertions(+), 16 deletions(-)
diff --git a/fs/backing-file.c b/fs/backing-file.c
index 892361c31c3d..53690754810f 100644
--- a/fs/backing-file.c
+++ b/fs/backing-file.c
@@ -12,6 +12,7 @@
#include <linux/backing-file.h>
#include <linux/splice.h>
#include <linux/mm.h>
+#include <linux/security.h>
#include "internal.h"
@@ -29,14 +30,15 @@
* returned file into a container structure that also stores the stacked
* file's path, which can be retrieved using backing_file_user_path().
*/
-struct file *backing_file_open(const struct path *user_path, int flags,
+struct file *backing_file_open(const struct file *user_file, int flags,
const struct path *real_path,
const struct cred *cred)
{
+ const struct path *user_path = &user_file->f_path;
struct file *f;
int error;
- f = alloc_empty_backing_file(flags, cred);
+ f = alloc_empty_backing_file(flags, cred, user_file);
if (IS_ERR(f))
return f;
@@ -52,15 +54,16 @@ struct file *backing_file_open(const struct path *user_path, int flags,
}
EXPORT_SYMBOL_GPL(backing_file_open);
-struct file *backing_tmpfile_open(const struct path *user_path, int flags,
+struct file *backing_tmpfile_open(const struct file *user_file, int flags,
const struct path *real_parentpath,
umode_t mode, const struct cred *cred)
{
struct mnt_idmap *real_idmap = mnt_idmap(real_parentpath->mnt);
+ const struct path *user_path = &user_file->f_path;
struct file *f;
int error;
- f = alloc_empty_backing_file(flags, cred);
+ f = alloc_empty_backing_file(flags, cred, user_file);
if (IS_ERR(f))
return f;
@@ -326,6 +329,7 @@ EXPORT_SYMBOL_GPL(backing_file_splice_write);
int backing_file_mmap(struct file *file, struct vm_area_struct *vma,
struct backing_file_ctx *ctx)
{
+ struct file *user_file = vma->vm_file;
const struct cred *old_cred;
int ret;
@@ -339,6 +343,12 @@ int backing_file_mmap(struct file *file, struct vm_area_struct *vma,
vma_set_file(vma, file);
old_cred = override_creds(ctx->cred);
+ ret = security_mmap_backing_file(vma, file, user_file);
+ if (ret) {
+ revert_creds(old_cred);
+ return ret;
+ }
+
ret = call_mmap(vma->vm_file, vma);
revert_creds(old_cred);
diff --git a/fs/file_table.c b/fs/file_table.c
index 75a1908d51a9..f78bcff79f1a 100644
--- a/fs/file_table.c
+++ b/fs/file_table.c
@@ -46,6 +46,9 @@ static struct percpu_counter nr_files __cacheline_aligned_in_smp;
struct backing_file {
struct file file;
struct path user_path;
+#ifdef CONFIG_SECURITY
+ void *security;
+#endif
};
#define backing_file(f) container_of(f, struct backing_file, file)
@@ -62,8 +65,21 @@ void backing_file_set_user_path(struct file *f, const struct path *path)
}
EXPORT_SYMBOL_GPL(backing_file_set_user_path);
+#ifdef CONFIG_SECURITY
+void *backing_file_security(const struct file *f)
+{
+ return backing_file(f)->security;
+}
+
+void backing_file_set_security(struct file *f, void *security)
+{
+ backing_file(f)->security = security;
+}
+#endif /* CONFIG_SECURITY */
+
static inline void backing_file_free(struct backing_file *ff)
{
+ security_backing_file_free(&ff->file);
path_put(&ff->user_path);
kfree(ff);
}
@@ -262,10 +278,12 @@ struct file *alloc_empty_file_noaccount(int flags, const struct cred *cred)
return f;
}
-static int init_backing_file(struct backing_file *ff)
+static int init_backing_file(struct backing_file *ff,
+ const struct file *user_file)
{
memset(&ff->user_path, 0, sizeof(ff->user_path));
- return 0;
+ backing_file_set_security(&ff->file, NULL);
+ return security_backing_file_alloc(&ff->file, user_file);
}
/*
@@ -275,7 +293,8 @@ static int init_backing_file(struct backing_file *ff)
* This is only for kernel internal use, and the allocate file must not be
* installed into file tables or such.
*/
-struct file *alloc_empty_backing_file(int flags, const struct cred *cred)
+struct file *alloc_empty_backing_file(int flags, const struct cred *cred,
+ const struct file *user_file)
{
struct backing_file *ff;
int error;
@@ -292,7 +311,7 @@ struct file *alloc_empty_backing_file(int flags, const struct cred *cred)
/* The f_mode flags must be set before fput(). */
ff->file.f_mode |= FMODE_BACKING | FMODE_NOACCOUNT;
- error = init_backing_file(ff);
+ error = init_backing_file(ff, user_file);
if (unlikely(error)) {
fput(&ff->file);
return ERR_PTR(error);
diff --git a/fs/fuse/passthrough.c b/fs/fuse/passthrough.c
index 6bfd09dda9e3..140e150be0de 100644
--- a/fs/fuse/passthrough.c
+++ b/fs/fuse/passthrough.c
@@ -326,7 +326,7 @@ struct fuse_backing *fuse_passthrough_open(struct file *file,
goto out;
/* Allocate backing file per fuse file to store fuse path */
- backing_file = backing_file_open(&file->f_path, file->f_flags,
+ backing_file = backing_file_open(file, file->f_flags,
&fb->file->f_path, fb->cred);
err = PTR_ERR(backing_file);
if (IS_ERR(backing_file)) {
diff --git a/fs/internal.h b/fs/internal.h
index a4352d333c61..997acc721f61 100644
--- a/fs/internal.h
+++ b/fs/internal.h
@@ -99,7 +99,8 @@ extern void chroot_fs_refs(const struct path *, const struct path *);
*/
struct file *alloc_empty_file(int flags, const struct cred *cred);
struct file *alloc_empty_file_noaccount(int flags, const struct cred *cred);
-struct file *alloc_empty_backing_file(int flags, const struct cred *cred);
+struct file *alloc_empty_backing_file(int flags, const struct cred *cred,
+ const struct file *user_file);
void backing_file_set_user_path(struct file *f, const struct path *path);
static inline void file_put_write_access(struct file *file)
diff --git a/fs/overlayfs/dir.c b/fs/overlayfs/dir.c
index ab65e98a1def..1c8009bf194b 100644
--- a/fs/overlayfs/dir.c
+++ b/fs/overlayfs/dir.c
@@ -1320,7 +1320,7 @@ static int ovl_create_tmpfile(struct file *file, struct dentry *dentry,
goto out_revert_creds;
ovl_path_upper(dentry->d_parent, &realparentpath);
- realfile = backing_tmpfile_open(&file->f_path, flags, &realparentpath,
+ realfile = backing_tmpfile_open(file, flags, &realparentpath,
mode, current_cred());
err = PTR_ERR_OR_ZERO(realfile);
pr_debug("tmpfile/open(%pd2, 0%o) = %i\n", realparentpath.dentry, mode, err);
diff --git a/fs/overlayfs/file.c b/fs/overlayfs/file.c
index 94095058da34..3765e1defa19 100644
--- a/fs/overlayfs/file.c
+++ b/fs/overlayfs/file.c
@@ -47,8 +47,7 @@ static struct file *ovl_open_realfile(const struct file *file,
} else {
if (!inode_owner_or_capable(real_idmap, realinode))
flags &= ~O_NOATIME;
-
- realfile = backing_file_open(file_user_path((struct file *) file),
+ realfile = backing_file_open(file,
flags, realpath, current_cred());
}
revert_creds(old_cred);
diff --git a/include/linux/backing-file.h b/include/linux/backing-file.h
index 2eed0ffb5e8f..cd18acd7ac5b 100644
--- a/include/linux/backing-file.h
+++ b/include/linux/backing-file.h
@@ -19,10 +19,10 @@ struct backing_file_ctx {
void (*end_write)(struct file *, loff_t, ssize_t);
};
-struct file *backing_file_open(const struct path *user_path, int flags,
+struct file *backing_file_open(const struct file *user_file, int flags,
const struct path *real_path,
const struct cred *cred);
-struct file *backing_tmpfile_open(const struct path *user_path, int flags,
+struct file *backing_tmpfile_open(const struct file *user_file, int flags,
const struct path *real_parentpath,
umode_t mode, const struct cred *cred);
ssize_t backing_file_read_iter(struct file *file, struct iov_iter *iter,
diff --git a/include/linux/fs.h b/include/linux/fs.h
index 70bbc00a2bd2..0eb43147dc87 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -2740,6 +2740,19 @@ struct file *dentry_create(const struct path *path, int flags, umode_t mode,
const struct cred *cred);
struct path *backing_file_user_path(const struct file *f);
+#ifdef CONFIG_SECURITY
+void *backing_file_security(const struct file *f);
+void backing_file_set_security(struct file *f, void *security);
+#else
+static inline void *backing_file_security(const struct file *f)
+{
+ return NULL;
+}
+static inline void backing_file_set_security(struct file *f, void *security)
+{
+}
+#endif /* CONFIG_SECURITY */
+
/*
* When mmapping a file on a stackable filesystem (e.g., overlayfs), the file
* stored in ->vm_file is a backing file whose f_inode is on the underlying
diff --git a/include/linux/lsm_audit.h b/include/linux/lsm_audit.h
index 97a8b21eb033..c0a2839253fa 100644
--- a/include/linux/lsm_audit.h
+++ b/include/linux/lsm_audit.h
@@ -93,7 +93,7 @@ struct common_audit_data {
#endif
char *kmod_name;
struct lsm_ioctlop_audit *op;
- struct file *file;
+ const struct file *file;
struct lsm_ibpkey_audit *ibpkey;
struct lsm_ibendport_audit *ibendport;
int reason;
diff --git a/include/linux/lsm_hook_defs.h b/include/linux/lsm_hook_defs.h
index 9eca013aa5e1..addb34abffa1 100644
--- a/include/linux/lsm_hook_defs.h
+++ b/include/linux/lsm_hook_defs.h
@@ -188,6 +188,9 @@ LSM_HOOK(int, 0, file_permission, struct file *file, int mask)
LSM_HOOK(int, 0, file_alloc_security, struct file *file)
LSM_HOOK(void, LSM_RET_VOID, file_release, struct file *file)
LSM_HOOK(void, LSM_RET_VOID, file_free_security, struct file *file)
+LSM_HOOK(int, 0, backing_file_alloc, struct file *backing_file,
+ const struct file *user_file)
+LSM_HOOK(void, LSM_RET_VOID, backing_file_free, struct file *backing_file)
LSM_HOOK(int, 0, file_ioctl, struct file *file, unsigned int cmd,
unsigned long arg)
LSM_HOOK(int, 0, file_ioctl_compat, struct file *file, unsigned int cmd,
@@ -195,6 +198,8 @@ LSM_HOOK(int, 0, file_ioctl_compat, struct file *file, unsigned int cmd,
LSM_HOOK(int, 0, mmap_addr, unsigned long addr)
LSM_HOOK(int, 0, mmap_file, struct file *file, unsigned long reqprot,
unsigned long prot, unsigned long flags)
+LSM_HOOK(int, 0, mmap_backing_file, struct vm_area_struct *vma,
+ struct file *backing_file, struct file *user_file)
LSM_HOOK(int, 0, file_mprotect, struct vm_area_struct *vma,
unsigned long reqprot, unsigned long prot)
LSM_HOOK(int, 0, file_lock, struct file *file, unsigned int cmd)
diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h
index 090d1d3e19fe..0876cf11e200 100644
--- a/include/linux/lsm_hooks.h
+++ b/include/linux/lsm_hooks.h
@@ -104,6 +104,7 @@ struct security_hook_list {
struct lsm_blob_sizes {
int lbs_cred;
int lbs_file;
+ int lbs_backing_file;
int lbs_ib;
int lbs_inode;
int lbs_sock;
diff --git a/include/linux/security.h b/include/linux/security.h
index 2c6db949ad1a..e4300f2ff11b 100644
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -421,11 +421,17 @@ int security_file_permission(struct file *file, int mask);
int security_file_alloc(struct file *file);
void security_file_release(struct file *file);
void security_file_free(struct file *file);
+int security_backing_file_alloc(struct file *backing_file,
+ const struct file *user_file);
+void security_backing_file_free(struct file *backing_file);
int security_file_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
int security_file_ioctl_compat(struct file *file, unsigned int cmd,
unsigned long arg);
int security_mmap_file(struct file *file, unsigned long prot,
unsigned long flags);
+int security_mmap_backing_file(struct vm_area_struct *vma,
+ struct file *backing_file,
+ struct file *user_file);
int security_mmap_addr(unsigned long addr);
int security_file_mprotect(struct vm_area_struct *vma, unsigned long reqprot,
unsigned long prot);
@@ -1065,6 +1071,15 @@ static inline void security_file_release(struct file *file)
static inline void security_file_free(struct file *file)
{ }
+static inline int security_backing_file_alloc(struct file *backing_file,
+ const struct file *user_file)
+{
+ return 0;
+}
+
+static inline void security_backing_file_free(struct file *backing_file)
+{ }
+
static inline int security_file_ioctl(struct file *file, unsigned int cmd,
unsigned long arg)
{
@@ -1084,6 +1099,13 @@ static inline int security_mmap_file(struct file *file, unsigned long prot,
return 0;
}
+static inline int security_mmap_backing_file(struct vm_area_struct *vma,
+ struct file *backing_file,
+ struct file *user_file)
+{
+ return 0;
+}
+
static inline int security_mmap_addr(unsigned long addr)
{
return cap_mmap_addr(addr);
diff --git a/security/security.c b/security/security.c
index 6e4deac6ec07..dd6b922c12de 100644
--- a/security/security.c
+++ b/security/security.c
@@ -95,6 +95,7 @@ const char *const lockdown_reasons[LOCKDOWN_CONFIDENTIALITY_MAX + 1] = {
static BLOCKING_NOTIFIER_HEAD(blocking_lsm_notifier_chain);
static struct kmem_cache *lsm_file_cache;
+static struct kmem_cache *lsm_backing_file_cache;
static struct kmem_cache *lsm_inode_cache;
char *lsm_names;
@@ -266,6 +267,7 @@ static void __init lsm_set_blob_sizes(struct lsm_blob_sizes *needed)
lsm_set_blob_size(&needed->lbs_cred, &blob_sizes.lbs_cred);
lsm_set_blob_size(&needed->lbs_file, &blob_sizes.lbs_file);
+ lsm_set_blob_size(&needed->lbs_backing_file, &blob_sizes.lbs_backing_file);
lsm_set_blob_size(&needed->lbs_ib, &blob_sizes.lbs_ib);
/*
* The inode blob gets an rcu_head in addition to
@@ -468,6 +470,7 @@ static void __init ordered_lsm_init(void)
init_debug("cred blob size = %d\n", blob_sizes.lbs_cred);
init_debug("file blob size = %d\n", blob_sizes.lbs_file);
+ init_debug("lsm_backing_file_cache = %d\n", blob_sizes.lbs_backing_file);
init_debug("ib blob size = %d\n", blob_sizes.lbs_ib);
init_debug("inode blob size = %d\n", blob_sizes.lbs_inode);
init_debug("ipc blob size = %d\n", blob_sizes.lbs_ipc);
@@ -490,6 +493,11 @@ static void __init ordered_lsm_init(void)
lsm_file_cache = kmem_cache_create("lsm_file_cache",
blob_sizes.lbs_file, 0,
SLAB_PANIC, NULL);
+ if (blob_sizes.lbs_backing_file)
+ lsm_backing_file_cache = kmem_cache_create(
+ "lsm_backing_file_cache",
+ blob_sizes.lbs_backing_file,
+ 0, SLAB_PANIC, NULL);
if (blob_sizes.lbs_inode)
lsm_inode_cache = kmem_cache_create("lsm_inode_cache",
blob_sizes.lbs_inode, 0,
@@ -666,6 +674,30 @@ int unregister_blocking_lsm_notifier(struct notifier_block *nb)
}
EXPORT_SYMBOL(unregister_blocking_lsm_notifier);
+/**
+ * lsm_backing_file_alloc - allocate a composite backing file blob
+ * @backing_file: the backing file
+ *
+ * Allocate the backing file blob for all the modules.
+ *
+ * Returns 0, or -ENOMEM if memory can't be allocated.
+ */
+static int lsm_backing_file_alloc(struct file *backing_file)
+{
+ void *blob;
+
+ if (!lsm_backing_file_cache) {
+ backing_file_set_security(backing_file, NULL);
+ return 0;
+ }
+
+ blob = kmem_cache_zalloc(lsm_backing_file_cache, GFP_KERNEL);
+ backing_file_set_security(backing_file, blob);
+ if (!blob)
+ return -ENOMEM;
+ return 0;
+}
+
/**
* lsm_blob_alloc - allocate a composite blob
* @dest: the destination for the blob
@@ -2893,6 +2925,57 @@ void security_file_free(struct file *file)
}
}
+/**
+ * security_backing_file_alloc() - Allocate and setup a backing file blob
+ * @backing_file: the backing file
+ * @user_file: the associated user visible file
+ *
+ * Allocate a backing file LSM blob and perform any necessary initialization of
+ * the LSM blob. There will be some operations where the LSM will not have
+ * access to @user_file after this point, so any important state associated
+ * with @user_file that is important to the LSM should be captured in the
+ * backing file's LSM blob.
+ *
+ * LSM's should avoid taking a reference to @user_file in this hook as it will
+ * result in problems later when the system attempts to drop/put the file
+ * references due to a circular dependency.
+ *
+ * Return: Return 0 if the hook is successful, negative values otherwise.
+ */
+int security_backing_file_alloc(struct file *backing_file,
+ const struct file *user_file)
+{
+ int rc;
+
+ rc = lsm_backing_file_alloc(backing_file);
+ if (rc)
+ return rc;
+ rc = call_int_hook(backing_file_alloc, backing_file, user_file);
+ if (unlikely(rc))
+ security_backing_file_free(backing_file);
+
+ return rc;
+}
+
+/**
+ * security_backing_file_free() - Free a backing file blob
+ * @backing_file: the backing file
+ *
+ * Free any LSM state associate with a backing file's LSM blob, including the
+ * blob itself.
+ */
+void security_backing_file_free(struct file *backing_file)
+{
+ void *blob = backing_file_security(backing_file);
+
+ call_void_hook(backing_file_free, backing_file);
+
+ if (blob) {
+ backing_file_set_security(backing_file, NULL);
+ kmem_cache_free(lsm_backing_file_cache, blob);
+ }
+}
+
/**
* security_file_ioctl() - Check if an ioctl is allowed
* @file: associated file
@@ -2981,6 +3064,32 @@ int security_mmap_file(struct file *file, unsigned long prot,
flags);
}
+/**
+ * security_mmap_backing_file - Check if mmap'ing a backing file is allowed
+ * @vma: the vm_area_struct for the mmap'd region
+ * @backing_file: the backing file being mmap'd
+ * @user_file: the user file being mmap'd
+ *
+ * Check permissions for a mmap operation on a stacked filesystem. This hook
+ * is called after the security_mmap_file() and is responsible for authorizing
+ * the mmap on @backing_file. It is important to note that the mmap operation
+ * on @user_file has already been authorized and the @vma->vm_file has been
+ * set to @backing_file.
+ *
+ * Return: Returns 0 if permission is granted.
+ */
+int security_mmap_backing_file(struct vm_area_struct *vma,
+ struct file *backing_file,
+ struct file *user_file)
+{
+ /* recommended by the stackable filesystem devs */
+ if (WARN_ON_ONCE(!(backing_file->f_mode & FMODE_BACKING)))
+ return -EIO;
+
+ return call_int_hook(mmap_backing_file, vma, backing_file, user_file);
+}
+EXPORT_SYMBOL_GPL(security_mmap_backing_file);
+
/**
* security_mmap_addr() - Check if mmap'ing an address is allowed
* @addr: address
--
2.18.0.huawei.25
^ permalink raw reply related
* [PATCH stable/linux-5.10.y 4/7] lsm: constify the 'file' parameter in security_binder_transfer_file()
From: Cai Xinchen @ 2026-06-29 7:06 UTC (permalink / raw)
To: viro, brauner, jack, miklos, amir73il, paul, jmorris, serge,
stephen.smalley.work, omosnace, gregkh, sashal, bboscaccy,
caixinchen1
Cc: linux-fsdevel, linux-kernel, linux-unionfs, linux-security-module,
selinux, bpf, stable, lujialin4
In-Reply-To: <20260629070653.580879-1-caixinchen1@huawei.com>
From: Khadija Kamran <kamrankhadijadj@gmail.com>
[ Upstream commit 8e4672d6f902d5c4db1e87e8aa9f530149d85bc6 ]
SELinux registers the implementation for the "binder_transfer_file"
hook. Looking at the function implementation we observe that the
parameter "file" is not changing.
Mark the "file" parameter of LSM hook security_binder_transfer_file() as
"const" since it will not be changing in the LSM hook.
Signed-off-by: Khadija Kamran <kamrankhadijadj@gmail.com>
[PM: subject line whitespace fix]
Signed-off-by: Paul Moore <paul@paul-moore.com>
Signed-off-by: Cai Xinchen <caixinchen1@huawei.com>
---
include/linux/lsm_hook_defs.h | 2 +-
include/linux/security.h | 4 ++--
security/security.c | 2 +-
security/selinux/hooks.c | 8 ++++----
4 files changed, 8 insertions(+), 8 deletions(-)
diff --git a/include/linux/lsm_hook_defs.h b/include/linux/lsm_hook_defs.h
index 35bb13ce1faf..e34b295bc15a 100644
--- a/include/linux/lsm_hook_defs.h
+++ b/include/linux/lsm_hook_defs.h
@@ -32,7 +32,7 @@ LSM_HOOK(int, 0, binder_transaction, const struct cred *from,
LSM_HOOK(int, 0, binder_transfer_binder, const struct cred *from,
const struct cred *to)
LSM_HOOK(int, 0, binder_transfer_file, const struct cred *from,
- const struct cred *to, struct file *file)
+ const struct cred *to, const struct file *file)
LSM_HOOK(int, 0, ptrace_access_check, struct task_struct *child,
unsigned int mode)
LSM_HOOK(int, 0, ptrace_traceme, struct task_struct *parent)
diff --git a/include/linux/security.h b/include/linux/security.h
index 2b8a00118903..f3c9d640b60b 100644
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -264,7 +264,7 @@ int security_binder_transaction(const struct cred *from,
int security_binder_transfer_binder(const struct cred *from,
const struct cred *to);
int security_binder_transfer_file(const struct cred *from,
- const struct cred *to, struct file *file);
+ const struct cred *to, const struct file *file);
int security_ptrace_access_check(struct task_struct *child, unsigned int mode);
int security_ptrace_traceme(struct task_struct *parent);
int security_capget(struct task_struct *target,
@@ -518,7 +518,7 @@ static inline int security_binder_transfer_binder(const struct cred *from,
static inline int security_binder_transfer_file(const struct cred *from,
const struct cred *to,
- struct file *file)
+ const struct file *file)
{
return 0;
}
diff --git a/security/security.c b/security/security.c
index 6de10b6699a4..d6b1b82094b7 100644
--- a/security/security.c
+++ b/security/security.c
@@ -744,7 +744,7 @@ int security_binder_transfer_binder(const struct cred *from,
}
int security_binder_transfer_file(const struct cred *from,
- const struct cred *to, struct file *file)
+ const struct cred *to, const struct file *file)
{
return call_int_hook(binder_transfer_file, 0, from, to, file);
}
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index 90935ed3d8d8..e1bbdef0bcd3 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -1700,7 +1700,7 @@ static inline int file_path_has_perm(const struct cred *cred,
}
#ifdef CONFIG_BPF_SYSCALL
-static int bpf_fd_pass(struct file *file, u32 sid);
+static int bpf_fd_pass(const struct file *file, u32 sid);
#endif
/* Check whether a task can use an open file descriptor to
@@ -1972,7 +1972,7 @@ static inline u32 file_mask_to_av(int mode, int mask)
}
/* Convert a Linux file to an access vector. */
-static inline u32 file_to_av(struct file *file)
+static inline u32 file_to_av(const struct file *file)
{
u32 av = 0;
@@ -2050,7 +2050,7 @@ static int selinux_binder_transfer_binder(const struct cred *from,
static int selinux_binder_transfer_file(const struct cred *from,
const struct cred *to,
- struct file *file)
+ const struct file *file)
{
u32 sid = cred_sid(to);
struct file_security_struct *fsec = selinux_file(file);
@@ -6799,7 +6799,7 @@ static u32 bpf_map_fmode_to_av(fmode_t fmode)
* access the bpf object and that's why we have to add this additional check in
* selinux_file_receive and selinux_binder_transfer_files.
*/
-static int bpf_fd_pass(struct file *file, u32 sid)
+static int bpf_fd_pass(const struct file *file, u32 sid)
{
struct bpf_security_struct *bpfsec;
struct bpf_prog *prog;
--
2.18.0.huawei.25
^ permalink raw reply related
* [PATCH stable/linux-5.10.y 2/7] fs: move kmem_cache_zalloc() into alloc_empty_file*() helpers
From: Cai Xinchen @ 2026-06-29 7:06 UTC (permalink / raw)
To: viro, brauner, jack, miklos, amir73il, paul, jmorris, serge,
stephen.smalley.work, omosnace, gregkh, sashal, bboscaccy,
caixinchen1
Cc: linux-fsdevel, linux-kernel, linux-unionfs, linux-security-module,
selinux, bpf, stable, lujialin4
In-Reply-To: <20260629070653.580879-1-caixinchen1@huawei.com>
From: Amir Goldstein <amir73il@gmail.com>
[ Upstream commit 8a05a8c31d06c5d0d67b273a4a00f87269adde82 ]
Use a common helper init_file() instead of __alloc_file() for
alloc_empty_file*() helpers and improrve the documentation.
This is needed for a follow up patch that allocates a backing_file
container.
Suggested-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: Amir Goldstein <amir73il@gmail.com>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Message-Id: <20230615112229.2143178-4-amir73il@gmail.com>
Signed-off-by: Christian Brauner <brauner@kernel.org>
Signed-off-by: Cai Xinchen <caixinchen1@huawei.com>
---
fs/file_table.c | 41 ++++++++++++++++++++++++++---------------
1 file changed, 26 insertions(+), 15 deletions(-)
diff --git a/fs/file_table.c b/fs/file_table.c
index 7a3b4a7f6808..be24d724b407 100644
--- a/fs/file_table.c
+++ b/fs/file_table.c
@@ -93,20 +93,15 @@ int proc_nr_files(struct ctl_table *table, int write,
}
#endif
-static struct file *__alloc_file(int flags, const struct cred *cred)
+static int init_file(struct file *f, int flags, const struct cred *cred)
{
- struct file *f;
int error;
- f = kmem_cache_zalloc(filp_cachep, GFP_KERNEL);
- if (unlikely(!f))
- return ERR_PTR(-ENOMEM);
-
f->f_cred = get_cred(cred);
error = security_file_alloc(f);
if (unlikely(error)) {
file_free_rcu(&f->f_u.fu_rcuhead);
- return ERR_PTR(error);
+ return error;
}
atomic_long_set(&f->f_count, 1);
@@ -118,7 +113,7 @@ static struct file *__alloc_file(int flags, const struct cred *cred)
f->f_mode = OPEN_FMODE(flags);
/* f->f_version: 0 */
- return f;
+ return 0;
}
/* Find an unused file structure and return a pointer to it.
@@ -135,6 +130,7 @@ struct file *alloc_empty_file(int flags, const struct cred *cred)
{
static long old_max;
struct file *f;
+ int error;
/*
* Privileged users can go above max_files
@@ -148,9 +144,15 @@ struct file *alloc_empty_file(int flags, const struct cred *cred)
goto over;
}
- f = __alloc_file(flags, cred);
- if (!IS_ERR(f))
- percpu_counter_inc(&nr_files);
+ f = kmem_cache_zalloc(filp_cachep, GFP_KERNEL);
+ if (unlikely(!f))
+ return ERR_PTR(-ENOMEM);
+
+ error = init_file(f, flags, cred);
+ if (unlikely(error))
+ return ERR_PTR(error);
+
+ percpu_counter_inc(&nr_files);
return f;
@@ -166,14 +168,23 @@ struct file *alloc_empty_file(int flags, const struct cred *cred)
/*
* Variant of alloc_empty_file() that doesn't check and modify nr_files.
*
- * Should not be used unless there's a very good reason to do so.
+ * This is only for kernel internal use, and the allocate file must not be
+ * installed into file tables or such.
*/
struct file *alloc_empty_file_noaccount(int flags, const struct cred *cred)
{
- struct file *f = __alloc_file(flags, cred);
+ struct file *f;
+ int error;
+
+ f = kmem_cache_zalloc(filp_cachep, GFP_KERNEL);
+ if (unlikely(!f))
+ return ERR_PTR(-ENOMEM);
+
+ error = init_file(f, flags, cred);
+ if (unlikely(error))
+ return ERR_PTR(error);
- if (!IS_ERR(f))
- f->f_mode |= FMODE_NOACCOUNT;
+ f->f_mode |= FMODE_NOACCOUNT;
return f;
}
--
2.18.0.huawei.25
^ permalink raw reply related
* [PATCH stable/linux-5.10.y 7/7] selinux: fix overlayfs mmap() and mprotect() access checks
From: Cai Xinchen @ 2026-06-29 7:06 UTC (permalink / raw)
To: viro, brauner, jack, miklos, amir73il, paul, jmorris, serge,
stephen.smalley.work, omosnace, gregkh, sashal, bboscaccy,
caixinchen1
Cc: linux-fsdevel, linux-kernel, linux-unionfs, linux-security-module,
selinux, bpf, stable, lujialin4
In-Reply-To: <20260629070653.580879-1-caixinchen1@huawei.com>
From: Paul Moore <paul@paul-moore.com>
[ Upstream commit 82544d36b1729153c8aeb179e84750f0c085d3b1 ]
The existing SELinux security model for overlayfs is to allow access if
the current task is able to access the top level file (the "user" file)
and the mounter's credentials are sufficient to access the lower
level file (the "backing" file). Unfortunately, the current code does
not properly enforce these access controls for both mmap() and mprotect()
operations on overlayfs filesystems.
This patch makes use of the newly created security_mmap_backing_file()
LSM hook to provide the missing backing file enforcement for mmap()
operations, and leverages the backing file API and new LSM blob to
provide the necessary information to properly enforce the mprotect()
access controls.
Cc: stable@vger.kernel.org
Acked-by: Amir Goldstein <amir73il@gmail.com>
Signed-off-by: Paul Moore <paul@paul-moore.com>
[backing_file_user_path() not available
Mainline uses backing_file_user_path(file) to obtain the user-visible path
from a backing file. The 5.10.y version uses &file->f_path directly]
Signed-off-by: Cai Xinchen <caixinchen1@huawei.com>
---
security/selinux/hooks.c | 244 ++++++++++++++++++++++--------
security/selinux/include/objsec.h | 11 ++
2 files changed, 190 insertions(+), 65 deletions(-)
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index e1bbdef0bcd3..76cc0cbd1fd2 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -1703,52 +1703,75 @@ static inline int file_path_has_perm(const struct cred *cred,
static int bpf_fd_pass(const struct file *file, u32 sid);
#endif
-/* Check whether a task can use an open file descriptor to
- access an inode in a given way. Check access to the
- descriptor itself, and then use dentry_has_perm to
- check a particular permission to the file.
- Access to the descriptor is implicitly granted if it
- has the same SID as the process. If av is zero, then
- access to the file is not checked, e.g. for cases
- where only the descriptor is affected like seek. */
-static int file_has_perm(const struct cred *cred,
- struct file *file,
- u32 av)
+static int __file_has_perm(const struct cred *cred, const struct file *file,
+ u32 av, bool bf_user_file)
+
{
- struct file_security_struct *fsec = selinux_file(file);
- struct inode *inode = file_inode(file);
struct common_audit_data ad;
- u32 sid = cred_sid(cred);
+ struct inode *inode;
+ u32 ssid = cred_sid(cred);
+ u32 tsid_fd;
int rc;
- ad.type = LSM_AUDIT_DATA_FILE;
- ad.u.file = file;
+ if (bf_user_file) {
+ struct backing_file_security_struct *bfsec;
+ const struct path *path;
- if (sid != fsec->sid) {
- rc = avc_has_perm(&selinux_state,
- sid, fsec->sid,
- SECCLASS_FD,
- FD__USE,
- &ad);
+ if (WARN_ON(!(file->f_mode & FMODE_BACKING)))
+ return -EIO;
+
+ bfsec = selinux_backing_file(file);
+ path = &file->f_path;
+ tsid_fd = bfsec->uf_sid;
+ inode = d_inode(path->dentry);
+
+ ad.type = LSM_AUDIT_DATA_PATH;
+ ad.u.path = *path;
+ } else {
+ struct file_security_struct *fsec = selinux_file(file);
+
+ tsid_fd = fsec->sid;
+ inode = file_inode(file);
+
+ ad.type = LSM_AUDIT_DATA_FILE;
+ ad.u.file = file;
+ }
+
+ if (ssid != tsid_fd) {
+ rc = avc_has_perm(&selinux_state, ssid, tsid_fd, SECCLASS_FD, FD__USE, &ad);
if (rc)
- goto out;
+ return rc;
}
#ifdef CONFIG_BPF_SYSCALL
- rc = bpf_fd_pass(file, cred_sid(cred));
+ /* regardless of backing vs user file, use the underlying file here */
+ rc = bpf_fd_pass(file, ssid);
if (rc)
return rc;
#endif
/* av is zero if only checking access to the descriptor. */
- rc = 0;
if (av)
- rc = inode_has_perm(cred, inode, av, &ad);
+ return inode_has_perm(cred, inode, av, &ad);
-out:
- return rc;
+ return 0;
}
+/* Check whether a task can use an open file descriptor to
+ access an inode in a given way. Check access to the
+ descriptor itself, and then use dentry_has_perm to
+ check a particular permission to the file.
+ Access to the descriptor is implicitly granted if it
+ has the same SID as the process. If av is zero, then
+ access to the file is not checked, e.g. for cases
+ where only the descriptor is affected like seek. */
+static inline int file_has_perm(const struct cred *cred,
+ const struct file *file, u32 av)
+{
+ return __file_has_perm(cred, file, av, false);
+}
+
+
/*
* Determine the label for an inode that might be unioned.
*/
@@ -3572,6 +3595,17 @@ static int selinux_file_alloc_security(struct file *file)
return 0;
}
+static int selinux_backing_file_alloc(struct file *backing_file,
+ const struct file *user_file)
+{
+ struct backing_file_security_struct *bfsec;
+
+ bfsec = selinux_backing_file(backing_file);
+ bfsec->uf_sid = selinux_file(user_file)->sid;
+
+ return 0;
+}
+
/*
* Check whether a task has the ioctl permission and cmd
* operation to an inode.
@@ -3691,43 +3725,55 @@ static int selinux_file_ioctl_compat(struct file *file, unsigned int cmd,
static int default_noexec __ro_after_init;
-static int file_map_prot_check(struct file *file, unsigned long prot, int shared)
+static int __file_map_prot_check(const struct cred *cred,
+ const struct file *file, unsigned long prot,
+ bool shared, bool bf_user_file)
{
- const struct cred *cred = current_cred();
- u32 sid = cred_sid(cred);
- int rc = 0;
+ struct inode *inode = NULL;
+ bool prot_exec = prot & PROT_EXEC;
+ bool prot_write = prot & PROT_WRITE;
+
+ if (file) {
+ if (bf_user_file)
+ inode = d_inode(file->f_path.dentry);
+ else
+ inode = file_inode(file);
+ }
+
+ if (default_noexec && prot_exec &&
+ (!file || IS_PRIVATE(inode) || (!shared && prot_write))) {
+ int rc;
+ u32 sid = cred_sid(cred);
- if (default_noexec &&
- (prot & PROT_EXEC) && (!file || IS_PRIVATE(file_inode(file)) ||
- (!shared && (prot & PROT_WRITE)))) {
/*
- * We are making executable an anonymous mapping or a
- * private file mapping that will also be writable.
- * This has an additional check.
+ * We are making executable an anonymous mapping or a private
+ * file mapping that will also be writable.
*/
- rc = avc_has_perm(&selinux_state,
- sid, sid, SECCLASS_PROCESS,
- PROCESS__EXECMEM, NULL);
+ rc = avc_has_perm(&selinux_state, sid, sid, SECCLASS_PROCESS, PROCESS__EXECMEM,
+ NULL);
if (rc)
- goto error;
+ return rc;
}
if (file) {
- /* read access is always possible with a mapping */
+ /* "read" always possible, "write" only if shared */
u32 av = FILE__READ;
-
- /* write access only matters if the mapping is shared */
- if (shared && (prot & PROT_WRITE))
+ if (shared && prot_write)
av |= FILE__WRITE;
-
- if (prot & PROT_EXEC)
+ if (prot_exec)
av |= FILE__EXECUTE;
- return file_has_perm(cred, file, av);
+ return __file_has_perm(cred, file, av, bf_user_file);
}
-error:
- return rc;
+ return 0;
+}
+
+static inline int file_map_prot_check(const struct cred *cred,
+ const struct file *file,
+ unsigned long prot, bool shared)
+{
+ return __file_map_prot_check(cred, file, prot, shared, false);
}
static int selinux_mmap_addr(unsigned long addr)
@@ -3744,17 +3790,16 @@ static int selinux_mmap_addr(unsigned long addr)
return rc;
}
-static int selinux_mmap_file(struct file *file, unsigned long reqprot,
- unsigned long prot, unsigned long flags)
+static int selinux_mmap_file_common(const struct cred *cred, struct file *file,
+ unsigned long reqprot, unsigned long prot, bool shared)
{
- struct common_audit_data ad;
- int rc;
-
if (file) {
+ int rc;
+ struct common_audit_data ad;
+
ad.type = LSM_AUDIT_DATA_FILE;
ad.u.file = file;
- rc = inode_has_perm(current_cred(), file_inode(file),
- FILE__MAP, &ad);
+ rc = inode_has_perm(cred, file_inode(file), FILE__MAP, &ad);
if (rc)
return rc;
}
@@ -3762,36 +3807,86 @@ static int selinux_mmap_file(struct file *file, unsigned long reqprot,
if (checkreqprot_get(&selinux_state))
prot = reqprot;
- return file_map_prot_check(file, prot,
- (flags & MAP_TYPE) == MAP_SHARED);
+ return file_map_prot_check(cred, file, prot, shared);
+}
+
+static int selinux_mmap_file(struct file *file,
+ unsigned long reqprot,
+ unsigned long prot, unsigned long flags)
+{
+ return selinux_mmap_file_common(current_cred(), file, reqprot, prot,
+ (flags & MAP_TYPE) == MAP_SHARED);
+}
+
+/**
+ * selinux_mmap_backing_file - Check mmap permissions on a backing file
+ * @vma: memory region
+ * @backing_file: stacked filesystem backing file
+ * @user_file: user visible file
+ *
+ * This is called after selinux_mmap_file() on stacked filesystems, and it
+ * is this function's responsibility to verify access to @backing_file and
+ * setup the SELinux state for possible later use in the mprotect() code path.
+ *
+ * By the time this function is called, mmap() access to @user_file has already
+ * been authorized and @vma->vm_file has been set to point to @backing_file.
+ *
+ * Return zero on success, negative values otherwise.
+ */
+static int selinux_mmap_backing_file(struct vm_area_struct *vma,
+ struct file *backing_file,
+ struct file *user_file __always_unused)
+{
+ unsigned long prot = 0;
+
+ /* translate vma->vm_flags perms into PROT perms */
+ if (vma->vm_flags & VM_READ)
+ prot |= PROT_READ;
+ if (vma->vm_flags & VM_WRITE)
+ prot |= PROT_WRITE;
+ if (vma->vm_flags & VM_EXEC)
+ prot |= PROT_EXEC;
+
+ return selinux_mmap_file_common(backing_file->f_cred, backing_file,
+ prot, prot, vma->vm_flags & VM_SHARED);
}
static int selinux_file_mprotect(struct vm_area_struct *vma,
unsigned long reqprot,
unsigned long prot)
{
+ int rc;
const struct cred *cred = current_cred();
u32 sid = cred_sid(cred);
+ const struct file *file = vma->vm_file;
+ bool backing_file;
+ bool shared = vma->vm_flags & VM_SHARED;
+
+ /* check if we need to trigger the "backing files are awful" mode */
+ backing_file = file && (file->f_mode & FMODE_BACKING);
if (checkreqprot_get(&selinux_state))
prot = reqprot;
if (default_noexec &&
(prot & PROT_EXEC) && !(vma->vm_flags & VM_EXEC)) {
- int rc = 0;
if (vma->vm_start >= vma->vm_mm->start_brk &&
vma->vm_end <= vma->vm_mm->brk) {
rc = avc_has_perm(&selinux_state,
sid, sid, SECCLASS_PROCESS,
PROCESS__EXECHEAP, NULL);
- } else if (!vma->vm_file &&
+ if (rc)
+ return rc;
+ } else if (!file &&
((vma->vm_start <= vma->vm_mm->start_stack &&
vma->vm_end >= vma->vm_mm->start_stack) ||
vma_is_stack_for_current(vma))) {
rc = avc_has_perm(&selinux_state,
sid, sid, SECCLASS_PROCESS,
PROCESS__EXECSTACK, NULL);
- } else if (vma->vm_file && vma->anon_vma) {
+ if (rc)
+ return rc;
+ } else if (file && vma->anon_vma) {
/*
* We are making executable a file mapping that has
* had some COW done. Since pages might have been
@@ -3799,13 +3894,29 @@ static int selinux_file_mprotect(struct vm_area_struct *vma,
* modified content. This typically should only
* occur for text relocations.
*/
- rc = file_has_perm(cred, vma->vm_file, FILE__EXECMOD);
+ rc = __file_has_perm(cred, file, FILE__EXECMOD,
+ backing_file);
+ if (rc)
+ return rc;
+ if (backing_file) {
+ rc = file_has_perm(file->f_cred, file,
+ FILE__EXECMOD);
+ if (rc)
+ return rc;
+ }
}
+ }
+
+ rc = __file_map_prot_check(cred, file, prot, shared, backing_file);
+ if (rc)
+ return rc;
+ if (backing_file) {
+ rc = file_map_prot_check(file->f_cred, file, prot, shared);
if (rc)
return rc;
}
- return file_map_prot_check(vma->vm_file, prot, vma->vm_flags&VM_SHARED);
+ return 0;
}
static int selinux_file_lock(struct file *file, unsigned int cmd)
@@ -6924,6 +7035,7 @@ static int selinux_lockdown(enum lockdown_reason what)
struct lsm_blob_sizes selinux_blob_sizes __lsm_ro_after_init = {
.lbs_cred = sizeof(struct task_security_struct),
.lbs_file = sizeof(struct file_security_struct),
+ .lbs_backing_file = sizeof(struct backing_file_security_struct),
.lbs_inode = sizeof(struct inode_security_struct),
.lbs_ipc = sizeof(struct ipc_security_struct),
.lbs_msg_msg = sizeof(struct msg_security_struct),
@@ -7075,9 +7187,11 @@ static struct security_hook_list selinux_hooks[] __lsm_ro_after_init = {
LSM_HOOK_INIT(file_permission, selinux_file_permission),
LSM_HOOK_INIT(file_alloc_security, selinux_file_alloc_security),
+ LSM_HOOK_INIT(backing_file_alloc, selinux_backing_file_alloc),
LSM_HOOK_INIT(file_ioctl, selinux_file_ioctl),
LSM_HOOK_INIT(file_ioctl_compat, selinux_file_ioctl_compat),
LSM_HOOK_INIT(mmap_file, selinux_mmap_file),
+ LSM_HOOK_INIT(mmap_backing_file, selinux_mmap_backing_file),
LSM_HOOK_INIT(mmap_addr, selinux_mmap_addr),
LSM_HOOK_INIT(file_mprotect, selinux_file_mprotect),
LSM_HOOK_INIT(file_lock, selinux_file_lock),
diff --git a/security/selinux/include/objsec.h b/security/selinux/include/objsec.h
index 330b7b6d44e0..b4be6588827c 100644
--- a/security/selinux/include/objsec.h
+++ b/security/selinux/include/objsec.h
@@ -60,6 +60,10 @@ struct file_security_struct {
u32 pseqno; /* Policy seqno at the time of file open */
};
+struct backing_file_security_struct {
+ u32 uf_sid; /* associated user file fsec->sid */
+};
+
struct superblock_security_struct {
struct super_block *sb; /* back pointer to sb object */
u32 sid; /* SID of file system superblock */
@@ -159,6 +163,13 @@ static inline struct file_security_struct *selinux_file(const struct file *file)
return file->f_security + selinux_blob_sizes.lbs_file;
}
+static inline struct backing_file_security_struct *
+selinux_backing_file(const struct file *backing_file)
+{
+ void *blob = backing_file_security(backing_file);
+ return blob + selinux_blob_sizes.lbs_backing_file;
+}
+
static inline struct inode_security_struct *selinux_inode(
const struct inode *inode)
{
--
2.18.0.huawei.25
^ permalink raw reply related
* [PATCH stable/linux-5.10.y 5/7] fs: prepare for adding LSM blob to backing_file
From: Cai Xinchen @ 2026-06-29 7:06 UTC (permalink / raw)
To: viro, brauner, jack, miklos, amir73il, paul, jmorris, serge,
stephen.smalley.work, omosnace, gregkh, sashal, bboscaccy,
caixinchen1
Cc: linux-fsdevel, linux-kernel, linux-unionfs, linux-security-module,
selinux, bpf, stable, lujialin4
In-Reply-To: <20260629070653.580879-1-caixinchen1@huawei.com>
From: Amir Goldstein <amir73il@gmail.com>
[ Upstream commit 880bd496ec72a6dcb00cb70c430ef752ba242ae7 ]
In preparation to adding LSM blob to backing_file struct, factor out
helpers init_backing_file() and backing_file_free().
Cc: stable@vger.kernel.org
Cc: linux-fsdevel@vger.kernel.org
Cc: linux-unionfs@vger.kernel.org
Cc: linux-erofs@lists.ozlabs.org
Signed-off-by: Amir Goldstein <amir73il@gmail.com>
Reviewed-by: Serge Hallyn <serge@hallyn.com>
[PM: use the term "LSM blob", fix comment style to match file]
Signed-off-by: Paul Moore <paul@paul-moore.com>
[1. The commit def3ae83da02
("fs: store real path instead of fake path in backing file f_path")
is not merged, The 5.10 LTS version accordingly operates on
&ff->real_path instead of &ff->user_path.
2. Mainline's file_free() does both the backing_file cleanup and the
kmem_cache_free() synchronously. Linux 5.10.y defers the actual kfree()
to file_free_rcu() via call_rcu(), so only path_put() is done
synchronously in file_free().]
Signed-off-by: Cai Xinchen <caixinchen1@huawei.com>
---
fs/file_table.c | 20 +++++++++++++++++++-
1 file changed, 19 insertions(+), 1 deletion(-)
diff --git a/fs/file_table.c b/fs/file_table.c
index 6daef2e2b2ad..6792d0bce246 100644
--- a/fs/file_table.c
+++ b/fs/file_table.c
@@ -70,11 +70,16 @@ static void file_free_rcu(struct rcu_head *head)
kmem_cache_free(filp_cachep, f);
}
+static inline void backing_file_free(struct backing_file *ff)
+{
+ path_put(&ff->real_path);
+}
+
static inline void file_free(struct file *f)
{
security_file_free(f);
if (unlikely(f->f_mode & FMODE_BACKING))
- path_put(backing_file_real_path(f));
+ backing_file_free(backing_file(f));
if (likely(!(f->f_mode & FMODE_NOACCOUNT)))
percpu_counter_dec(&nr_files);
call_rcu(&f->f_u.fu_rcuhead, file_free_rcu);
@@ -211,6 +216,12 @@ struct file *alloc_empty_file_noaccount(int flags, const struct cred *cred)
return f;
}
+static int init_backing_file(struct backing_file *ff)
+{
+ memset(&ff->real_path, 0, sizeof(ff->real_path));
+ return 0;
+}
+
/*
* Variant of alloc_empty_file() that allocates a backing_file container
* and doesn't check and modify nr_files.
@@ -231,7 +242,14 @@ struct file *alloc_empty_backing_file(int flags, const struct cred *cred)
if (unlikely(error))
return ERR_PTR(error);
+ /* The f_mode flags must be set before fput(). */
ff->file.f_mode |= FMODE_BACKING | FMODE_NOACCOUNT;
+ error = init_backing_file(ff);
+ if (unlikely(error)) {
+ fput(&ff->file);
+ return ERR_PTR(error);
+ }
+
return &ff->file;
}
--
2.18.0.huawei.25
^ permalink raw reply related
* [PATCH stable/linux-5.10.y 6/7] lsm: add backing_file LSM hooks
From: Cai Xinchen @ 2026-06-29 7:06 UTC (permalink / raw)
To: viro, brauner, jack, miklos, amir73il, paul, jmorris, serge,
stephen.smalley.work, omosnace, gregkh, sashal, bboscaccy,
caixinchen1
Cc: linux-fsdevel, linux-kernel, linux-unionfs, linux-security-module,
selinux, bpf, stable, lujialin4
In-Reply-To: <20260629070653.580879-1-caixinchen1@huawei.com>
From: Paul Moore <paul@paul-moore.com>
[ Upstream commit 6af36aeb147a06dea47c49859cd6ca5659aeb987 ]
Stacked filesystems such as overlayfs do not currently provide the
necessary mechanisms for LSMs to properly enforce access controls on the
mmap() and mprotect() operations. In order to resolve this gap, a LSM
security blob is being added to the backing_file struct and the following
new LSM hooks are being created:
security_backing_file_alloc()
security_backing_file_free()
security_mmap_backing_file()
The first two hooks are to manage the lifecycle of the LSM security blob
in the backing_file struct, while the third provides a new mmap() access
control point for the underlying backing file. It is also expected that
LSMs will likely want to update their security_file_mprotect() callback
to address issues with their mprotect() controls, but that does not
require a change to the security_file_mprotect() LSM hook.
There are a three other small changes to support these new LSM hooks:
* Pass the user file associated with a backing file down to
alloc_empty_backing_file() so it can be included in the
security_backing_file_alloc() hook.
* Add getter and setter functions for the backing_file struct LSM blob
as the backing_file struct remains private to fs/file_table.c.
* Constify the file struct field in the LSM common_audit_data struct to
better support LSMs that need to pass a const file struct pointer into
the common LSM audit code.
Thanks to Arnd Bergmann for identifying the missing EXPORT_SYMBOL_GPL()
and supplying a fixup.
Cc: stable@vger.kernel.org
Cc: linux-fsdevel@vger.kernel.org
Cc: linux-unionfs@vger.kernel.org
Cc: linux-erofs@lists.ozlabs.org
Reviewed-by: Amir Goldstein <amir73il@gmail.com>
Reviewed-by: Serge Hallyn <serge@hallyn.com>
Reviewed-by: Christian Brauner <brauner@kernel.org>
Signed-off-by: Paul Moore <paul@paul-moore.com>
[1. Mainline uses call_int_hook(FUNC, ...) with the default IRC baked
into the macro. Linux 5.10.y uses call_int_hook(FUNC, IRC, ...)
requiring an explicit default return value.
2. fs/backing-file.c does not exist in LTS
Linux 5.10.y places backing_file_open() in fs/open.c and lacks a
dedicated fs/backing-file.c. The backing_file_mmap() function and
scoped_with_creds() do not exist in 5.10.y. Therefore the LTS patch
calls security_mmap_backing_file() directly in ovl_mmap() in
fs/overlayfs/file.c rather than modifying backing_file_mmap().
3. Missing filesystems/modules
Linux 5.10.y does not have backing_tmpfile_open(), fs/fuse/passthrough.c,
or the erofs ishare mmap path that the mainline patch touches. These hunks
are dropped in the 5.10 LTS backport.
4. Use macro backing_file to replace inline function to eliminate the
const warning.
5. Mainline declares lsm_backing_file_cache in security/lsm.h. 5.10.y does
not have security/lsm_init.c or security/lsm.h; the cache variable
is defined locally as static struct kmem_cache *lsm_backing_file_cache
in security/security.c.]
Signed-off-by: Cai Xinchen <caixinchen1@huawei.com>
---
fs/file_table.c | 32 +++++++---
fs/internal.h | 3 +-
fs/open.c | 7 ++-
fs/overlayfs/file.c | 8 ++-
include/linux/fs.h | 15 ++++-
include/linux/lsm_audit.h | 2 +-
include/linux/lsm_hook_defs.h | 5 ++
include/linux/lsm_hooks.h | 1 +
include/linux/security.h | 22 +++++++
security/security.c | 110 ++++++++++++++++++++++++++++++++++
10 files changed, 190 insertions(+), 15 deletions(-)
diff --git a/fs/file_table.c b/fs/file_table.c
index 6792d0bce246..829dfd35365c 100644
--- a/fs/file_table.c
+++ b/fs/file_table.c
@@ -46,12 +46,12 @@ static struct percpu_counter nr_files __cacheline_aligned_in_smp;
struct backing_file {
struct file file;
struct path real_path;
+#ifdef CONFIG_SECURITY
+ void *security;
+#endif
};
-static inline struct backing_file *backing_file(struct file *f)
-{
- return container_of(f, struct backing_file, file);
-}
+#define backing_file(f) container_of(f, struct backing_file, file)
struct path *backing_file_real_path(struct file *f)
{
@@ -70,8 +70,21 @@ static void file_free_rcu(struct rcu_head *head)
kmem_cache_free(filp_cachep, f);
}
+#ifdef CONFIG_SECURITY
+void *backing_file_security(const struct file *f)
+{
+ return backing_file(f)->security;
+}
+
+void backing_file_set_security(struct file *f, void *security)
+{
+ backing_file(f)->security = security;
+}
+#endif /* CONFIG_SECURITY */
+
static inline void backing_file_free(struct backing_file *ff)
{
+ security_backing_file_free(&ff->file);
path_put(&ff->real_path);
}
@@ -216,10 +229,12 @@ struct file *alloc_empty_file_noaccount(int flags, const struct cred *cred)
return f;
}
-static int init_backing_file(struct backing_file *ff)
+static int init_backing_file(struct backing_file *ff,
+ const struct file *user_file)
{
memset(&ff->real_path, 0, sizeof(ff->real_path));
- return 0;
+ backing_file_set_security(&ff->file, NULL);
+ return security_backing_file_alloc(&ff->file, user_file);
}
/*
@@ -229,7 +244,8 @@ static int init_backing_file(struct backing_file *ff)
* This is only for kernel internal use, and the allocate file must not be
* installed into file tables or such.
*/
-struct file *alloc_empty_backing_file(int flags, const struct cred *cred)
+struct file *alloc_empty_backing_file(int flags, const struct cred *cred,
+ const struct file *user_file)
{
struct backing_file *ff;
int error;
@@ -244,7 +260,7 @@ struct file *alloc_empty_backing_file(int flags, const struct cred *cred)
/* The f_mode flags must be set before fput(). */
ff->file.f_mode |= FMODE_BACKING | FMODE_NOACCOUNT;
- error = init_backing_file(ff);
+ error = init_backing_file(ff, user_file);
if (unlikely(error)) {
fput(&ff->file);
return ERR_PTR(error);
diff --git a/fs/internal.h b/fs/internal.h
index 676e8a8da1c7..e74f9b27888e 100644
--- a/fs/internal.h
+++ b/fs/internal.h
@@ -109,7 +109,8 @@ extern void chroot_fs_refs(const struct path *, const struct path *);
*/
struct file *alloc_empty_file(int flags, const struct cred *cred);
struct file *alloc_empty_file_noaccount(int flags, const struct cred *cred);
-struct file *alloc_empty_backing_file(int flags, const struct cred *cred);
+struct file *alloc_empty_backing_file(int flags, const struct cred *cred,
+ const struct file *user_file);
/*
* super.c
diff --git a/fs/open.c b/fs/open.c
index d6d5684dc919..fbcfdb83c02e 100644
--- a/fs/open.c
+++ b/fs/open.c
@@ -1012,18 +1012,19 @@ EXPORT_SYMBOL(dentry_create);
* the backing inode on the underlying filesystem, which can be
* retrieved using backing_file_real_path().
*/
-struct file *backing_file_open(const struct path *path, int flags,
+struct file *backing_file_open(const struct file *user_file, int flags,
const struct path *real_path,
const struct cred *cred)
{
+ const struct path *user_path = &user_file->f_path;
struct file *f;
int error;
- f = alloc_empty_backing_file(flags, cred);
+ f = alloc_empty_backing_file(flags, cred, user_file);
if (IS_ERR(f))
return f;
- f->f_path = *path;
+ f->f_path = *user_path;
path_get(real_path);
*backing_file_real_path(f) = *real_path;
error = do_dentry_open(f, d_inode(real_path->dentry), NULL);
diff --git a/fs/overlayfs/file.c b/fs/overlayfs/file.c
index 460748b63806..318ec1d99aa1 100644
--- a/fs/overlayfs/file.c
+++ b/fs/overlayfs/file.c
@@ -58,7 +58,7 @@ static struct file *ovl_open_realfile(const struct file *file,
if (!inode_owner_or_capable(realinode))
flags &= ~O_NOATIME;
- realfile = backing_file_open(&file->f_path, flags, realpath,
+ realfile = backing_file_open(file, flags, realpath,
current_cred());
}
revert_creds(old_cred);
@@ -491,6 +491,7 @@ static int ovl_fsync(struct file *file, loff_t start, loff_t end, int datasync)
static int ovl_mmap(struct file *file, struct vm_area_struct *vma)
{
+ struct file *user_file = vma->vm_file;
struct file *realfile = file->private_data;
const struct cred *old_cred;
int ret;
@@ -504,6 +505,11 @@ static int ovl_mmap(struct file *file, struct vm_area_struct *vma)
vma->vm_file = get_file(realfile);
old_cred = ovl_override_creds(file_inode(file)->i_sb);
+ ret = security_mmap_backing_file(vma, realfile, user_file);
+ if (ret) {
+ revert_creds(old_cred);
+ return ret;
+ }
ret = call_mmap(vma->vm_file, vma);
revert_creds(old_cred);
diff --git a/include/linux/fs.h b/include/linux/fs.h
index acdc14542cbb..3b1eb8d7d9d7 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -2680,11 +2680,24 @@ struct file *dentry_open(const struct path *path, int flags,
const struct cred *creds);
struct file *dentry_create(const struct path *path, int flags, umode_t mode,
const struct cred *cred);
-struct file *backing_file_open(const struct path *path, int flags,
+struct file *backing_file_open(const struct file *user_file, int flags,
const struct path *real_path,
const struct cred *cred);
struct path *backing_file_real_path(struct file *f);
+#ifdef CONFIG_SECURITY
+void *backing_file_security(const struct file *f);
+void backing_file_set_security(struct file *f, void *security);
+#else
+static inline void *backing_file_security(const struct file *f)
+{
+ return NULL;
+}
+static inline void backing_file_set_security(struct file *f, void *security)
+{
+}
+#endif /* CONFIG_SECURITY */
+
/*
* file_real_path - get the path corresponding to f_inode
*
diff --git a/include/linux/lsm_audit.h b/include/linux/lsm_audit.h
index 28f23b341c1c..8b42e110cb26 100644
--- a/include/linux/lsm_audit.h
+++ b/include/linux/lsm_audit.h
@@ -92,7 +92,7 @@ struct common_audit_data {
#endif
char *kmod_name;
struct lsm_ioctlop_audit *op;
- struct file *file;
+ const struct file *file;
struct lsm_ibpkey_audit *ibpkey;
struct lsm_ibendport_audit *ibendport;
int reason;
diff --git a/include/linux/lsm_hook_defs.h b/include/linux/lsm_hook_defs.h
index e34b295bc15a..473e4d8dafe3 100644
--- a/include/linux/lsm_hook_defs.h
+++ b/include/linux/lsm_hook_defs.h
@@ -156,6 +156,9 @@ LSM_HOOK(int, 0, kernfs_init_security, struct kernfs_node *kn_dir,
LSM_HOOK(int, 0, file_permission, struct file *file, int mask)
LSM_HOOK(int, 0, file_alloc_security, struct file *file)
LSM_HOOK(void, LSM_RET_VOID, file_free_security, struct file *file)
+LSM_HOOK(int, 0, backing_file_alloc, struct file *backing_file,
+ const struct file *user_file)
+LSM_HOOK(void, LSM_RET_VOID, backing_file_free, struct file *backing_file)
LSM_HOOK(int, 0, file_ioctl, struct file *file, unsigned int cmd,
unsigned long arg)
LSM_HOOK(int, 0, file_ioctl_compat, struct file *file, unsigned int cmd,
@@ -163,6 +166,8 @@ LSM_HOOK(int, 0, file_ioctl_compat, struct file *file, unsigned int cmd,
LSM_HOOK(int, 0, mmap_addr, unsigned long addr)
LSM_HOOK(int, 0, mmap_file, struct file *file, unsigned long reqprot,
unsigned long prot, unsigned long flags)
+LSM_HOOK(int, 0, mmap_backing_file, struct vm_area_struct *vma,
+ struct file *backing_file, struct file *user_file)
LSM_HOOK(int, 0, file_mprotect, struct vm_area_struct *vma,
unsigned long reqprot, unsigned long prot)
LSM_HOOK(int, 0, file_lock, struct file *file, unsigned int cmd)
diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h
index bbf9c8c7bd9c..04f08634e552 100644
--- a/include/linux/lsm_hooks.h
+++ b/include/linux/lsm_hooks.h
@@ -1562,6 +1562,7 @@ struct security_hook_list {
struct lsm_blob_sizes {
int lbs_cred;
int lbs_file;
+ int lbs_backing_file;
int lbs_inode;
int lbs_ipc;
int lbs_msg_msg;
diff --git a/include/linux/security.h b/include/linux/security.h
index f3c9d640b60b..c127367026d5 100644
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -369,11 +369,17 @@ int security_kernfs_init_security(struct kernfs_node *kn_dir,
int security_file_permission(struct file *file, int mask);
int security_file_alloc(struct file *file);
void security_file_free(struct file *file);
+int security_backing_file_alloc(struct file *backing_file,
+ const struct file *user_file);
+void security_backing_file_free(struct file *backing_file);
int security_file_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
int security_file_ioctl_compat(struct file *file, unsigned int cmd,
unsigned long arg);
int security_mmap_file(struct file *file, unsigned long prot,
unsigned long flags);
+int security_mmap_backing_file(struct vm_area_struct *vma,
+ struct file *backing_file,
+ struct file *user_file);
int security_mmap_addr(unsigned long addr);
int security_file_mprotect(struct vm_area_struct *vma, unsigned long reqprot,
unsigned long prot);
@@ -923,6 +929,15 @@ static inline int security_file_alloc(struct file *file)
static inline void security_file_free(struct file *file)
{ }
+static inline int security_backing_file_alloc(struct file *backing_file,
+ const struct file *user_file)
+{
+ return 0;
+}
+
+static inline void security_backing_file_free(struct file *backing_file)
+{ }
+
static inline int security_file_ioctl(struct file *file, unsigned int cmd,
unsigned long arg)
{
@@ -942,6 +957,13 @@ static inline int security_mmap_file(struct file *file, unsigned long prot,
return 0;
}
+static inline int security_mmap_backing_file(struct vm_area_struct *vma,
+ struct file *backing_file,
+ struct file *user_file)
+{
+ return 0;
+}
+
static inline int security_mmap_addr(unsigned long addr)
{
return cap_mmap_addr(addr);
diff --git a/security/security.c b/security/security.c
index d6b1b82094b7..50e781b06eb4 100644
--- a/security/security.c
+++ b/security/security.c
@@ -76,6 +76,7 @@ struct security_hook_heads security_hook_heads __lsm_ro_after_init;
static BLOCKING_NOTIFIER_HEAD(blocking_lsm_notifier_chain);
static struct kmem_cache *lsm_file_cache;
+static struct kmem_cache *lsm_backing_file_cache;
static struct kmem_cache *lsm_inode_cache;
char *lsm_names;
@@ -197,6 +198,8 @@ static void __init lsm_set_blob_sizes(struct lsm_blob_sizes *needed)
lsm_set_blob_size(&needed->lbs_cred, &blob_sizes.lbs_cred);
lsm_set_blob_size(&needed->lbs_file, &blob_sizes.lbs_file);
+ lsm_set_blob_size(&needed->lbs_backing_file,
+ &blob_sizes.lbs_backing_file);
/*
* The inode blob gets an rcu_head in addition to
* what the modules might need.
@@ -338,6 +341,7 @@ static void __init ordered_lsm_init(void)
init_debug("cred blob size = %d\n", blob_sizes.lbs_cred);
init_debug("file blob size = %d\n", blob_sizes.lbs_file);
+ init_debug("backing_file blob size = %d\n", blob_sizes.lbs_backing_file);
init_debug("inode blob size = %d\n", blob_sizes.lbs_inode);
init_debug("ipc blob size = %d\n", blob_sizes.lbs_ipc);
init_debug("msg_msg blob size = %d\n", blob_sizes.lbs_msg_msg);
@@ -350,6 +354,11 @@ static void __init ordered_lsm_init(void)
lsm_file_cache = kmem_cache_create("lsm_file_cache",
blob_sizes.lbs_file, 0,
SLAB_PANIC, NULL);
+ if (blob_sizes.lbs_backing_file)
+ lsm_backing_file_cache = kmem_cache_create(
+ "lsm_backing_file_cache",
+ blob_sizes.lbs_backing_file,
+ 0, SLAB_PANIC, NULL);
if (blob_sizes.lbs_inode)
lsm_inode_cache = kmem_cache_create("lsm_inode_cache",
blob_sizes.lbs_inode, 0,
@@ -575,6 +584,30 @@ static int lsm_file_alloc(struct file *file)
return 0;
}
+/**
+ * lsm_backing_file_alloc - allocate a composite backing file blob
+ * @backing_file: the backing file
+ *
+ * Allocate the backing file blob for all the modules.
+ *
+ * Returns 0, or -ENOMEM if memory can't be allocated.
+ */
+static int lsm_backing_file_alloc(struct file *backing_file)
+{
+ void *blob;
+
+ if (!lsm_backing_file_cache) {
+ backing_file_set_security(backing_file, NULL);
+ return 0;
+ }
+
+ blob = kmem_cache_zalloc(lsm_backing_file_cache, GFP_KERNEL);
+ backing_file_set_security(backing_file, blob);
+ if (!blob)
+ return -ENOMEM;
+ return 0;
+}
+
/**
* lsm_inode_alloc - allocate a composite inode blob
* @inode: the inode that needs a blob
@@ -1493,6 +1526,57 @@ void security_file_free(struct file *file)
}
}
+/**
+ * security_backing_file_alloc() - Allocate and setup a backing file blob
+ * @backing_file: the backing file
+ * @user_file: the associated user visible file
+ *
+ * Allocate a backing file LSM blob and perform any necessary initialization of
+ * the LSM blob. There will be some operations where the LSM will not have
+ * access to @user_file after this point, so any important state associated
+ * with @user_file that is important to the LSM should be captured in the
+ * backing file's LSM blob.
+ *
+ * LSM's should avoid taking a reference to @user_file in this hook as it will
+ * result in problems later when the system attempts to drop/put the file
+ * references due to a circular dependency.
+ *
+ * Return: Return 0 if the hook is successful, negative values otherwise.
+ */
+int security_backing_file_alloc(struct file *backing_file,
+ const struct file *user_file)
+{
+ int rc;
+
+ rc = lsm_backing_file_alloc(backing_file);
+ if (rc)
+ return rc;
+ rc = call_int_hook(backing_file_alloc, 0, backing_file, user_file);
+ if (unlikely(rc))
+ security_backing_file_free(backing_file);
+
+ return rc;
+}
+
+/**
+ * security_backing_file_free() - Free a backing file blob
+ * @backing_file: the backing file
+ *
+ * Free any LSM state associate with a backing file's LSM blob, including the
+ * blob itself.
+ */
+void security_backing_file_free(struct file *backing_file)
+{
+ void *blob = backing_file_security(backing_file);
+
+ call_void_hook(backing_file_free, backing_file);
+
+ if (blob) {
+ backing_file_set_security(backing_file, NULL);
+ kmem_cache_free(lsm_backing_file_cache, blob);
+ }
+}
+
int security_file_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
return call_int_hook(file_ioctl, 0, file, cmd, arg);
@@ -1562,6 +1646,32 @@ int security_mmap_file(struct file *file, unsigned long prot,
return ima_file_mmap(file, prot, prot_adj, flags);
}
+/**
+ * security_mmap_backing_file - Check if mmap'ing a backing file is allowed
+ * @vma: the vm_area_struct for the mmap'd region
+ * @backing_file: the backing file being mmap'd
+ * @user_file: the user file being mmap'd
+ *
+ * Check permissions for a mmap operation on a stacked filesystem. This hook
+ * is called after the security_mmap_file() and is responsible for authorizing
+ * the mmap on @backing_file. It is important to note that the mmap operation
+ * on @user_file has already been authorized and the @vma->vm_file has been
+ * set to @backing_file.
+ *
+ * Return: Returns 0 if permission is granted.
+ */
+int security_mmap_backing_file(struct vm_area_struct *vma,
+ struct file *backing_file,
+ struct file *user_file)
+{
+ /* recommended by the stackable filesystem devs */
+ if (WARN_ON_ONCE(!(backing_file->f_mode & FMODE_BACKING)))
+ return -EIO;
+
+ return call_int_hook(mmap_backing_file, 0, vma, backing_file, user_file);
+}
+EXPORT_SYMBOL_GPL(security_mmap_backing_file);
+
int security_mmap_addr(unsigned long addr)
{
return call_int_hook(mmap_addr, 0, addr);
--
2.18.0.huawei.25
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox