* [PATCH 0/7] lsm: Replace security_sb_mount with granular mount hooks
@ 2026-03-18 18:43 Song Liu
2026-03-18 18:43 ` [PATCH 1/7] lsm: Add granular mount hooks to replace security_sb_mount Song Liu
` (6 more replies)
0 siblings, 7 replies; 21+ messages in thread
From: Song Liu @ 2026-03-18 18:43 UTC (permalink / raw)
To: linux-security-module, linux-fsdevel, selinux, apparmor
Cc: paul, jmorris, serge, viro, brauner, jack, john.johansen,
stephen.smalley.work, omosnace, mic, gnoack, takedakn,
penguin-kernel, herton, kernel-team, Song Liu
This series replaces the monolithic security_sb_mount() hook with
per-operation mount hooks, addressing two main issues:
1. TOCTOU: security_sb_mount() receives dev_name as a string, which
LSMs like AppArmor and Tomoyo re-resolve via kern_path(). The new
hooks pass pre-resolved struct path pointers where possible (bind
mount, move mount), eliminating the double-resolution.
2. Conflation: security_sb_mount() handles bind, new mount, remount,
move, propagation changes, and mount reconfiguration through a
single hook, requiring LSMs to dispatch on flags internally. The
new hooks are called at the operation level with appropriate
context.
The new hooks are:
mount_bind - bind mount (pre-resolved source path)
mount_new - new filesystem mount (with fs_context)
mount_remount - filesystem remount (with fs_context)
mount_reconfigure - mount flag reconfiguration (MS_REMOUNT|MS_BIND)
mount_move - move mount (pre-resolved paths)
mount_change_type - propagation type changes
mount_new and mount_remount are called after parse_monolithic_mount_data(),
so LSMs have access to the fs_context with parsed mount options. They also
receive the original mount(2) flags and data pointer for LSMs (AppArmor,
Tomoyo) that need them for policy matching.
The series also replaces security_move_mount() with the new mount_move
hook, unifying the old mount(2) MS_MOVE path with the move_mount(2)
syscall path.
All existing LSM behaviors are preserved:
AppArmor: same policy matching, TOCTOU fixed for bind/move
SELinux: same permission checks (FILE__MOUNTON, FILESYSTEM__REMOUNT)
Landlock: same deny-all for sandboxed processes
Tomoyo: same policy matching, TOCTOU fixed for bind/move, unused
data_page parameter removed
This work is inspired by earlier discussions:
[1] https://lore.kernel.org/bpf/20251127005011.1872209-1-song@kernel.org/
[2] https://lore.kernel.org/linux-security-module/20250708230504.3994335-1-song@kernel.org/
Song Liu (7):
lsm: Add granular mount hooks to replace security_sb_mount
apparmor: Remove redundant MS_MGC_MSK stripping in apparmor_sb_mount
apparmor: Convert from sb_mount to granular mount hooks
selinux: Convert from sb_mount to granular mount hooks
landlock: Convert from sb_mount to granular mount hooks
tomoyo: Convert from sb_mount to granular mount hooks
lsm: Remove security_sb_mount and security_move_mount
fs/namespace.c | 41 +++++++---
include/linux/lsm_hook_defs.h | 14 +++-
include/linux/security.h | 56 +++++++++++---
kernel/bpf/bpf_lsm.c | 7 +-
security/apparmor/include/mount.h | 5 +-
security/apparmor/lsm.c | 102 ++++++++++++++++++-------
security/apparmor/mount.c | 37 ++--------
security/landlock/fs.c | 41 ++++++++--
security/security.c | 119 +++++++++++++++++++++++-------
security/selinux/hooks.c | 49 ++++++++----
security/tomoyo/common.h | 2 +-
security/tomoyo/mount.c | 31 +++++---
security/tomoyo/tomoyo.c | 63 ++++++++++++----
13 files changed, 406 insertions(+), 161 deletions(-)
--
2.52.0
^ permalink raw reply [flat|nested] 21+ messages in thread
* [PATCH 1/7] lsm: Add granular mount hooks to replace security_sb_mount
2026-03-18 18:43 [PATCH 0/7] lsm: Replace security_sb_mount with granular mount hooks Song Liu
@ 2026-03-18 18:43 ` Song Liu
2026-03-18 18:43 ` [PATCH 2/7] apparmor: Remove redundant MS_MGC_MSK stripping in apparmor_sb_mount Song Liu
` (5 subsequent siblings)
6 siblings, 0 replies; 21+ messages in thread
From: Song Liu @ 2026-03-18 18:43 UTC (permalink / raw)
To: linux-security-module, linux-fsdevel, selinux, apparmor
Cc: paul, jmorris, serge, viro, brauner, jack, john.johansen,
stephen.smalley.work, omosnace, mic, gnoack, takedakn,
penguin-kernel, herton, kernel-team, Song Liu
Add six new LSM hooks for mount operations:
- mount_bind(from, to, recurse): bind mount with pre-resolved
struct path for source and destination.
- mount_new(fc, mp, mnt_flags, flags, data): new mount, called after
mount options are parsed. The flags and data parameters carry the
original mount(2) flags and data for LSMs that need them (AppArmor,
Tomoyo).
- mount_remount(fc, mp, mnt_flags, flags, data): filesystem remount,
called after mount options are parsed into the fs_context.
- mount_reconfigure(mp, mnt_flags, flags): mount flag reconfiguration
(MS_REMOUNT|MS_BIND path).
- mount_move(from, to): move mount with pre-resolved paths.
- mount_change_type(mp, ms_flags): propagation type changes.
These replace the monolithic security_sb_mount() which conflates
multiple distinct operations into a single hook, and suffers from
TOCTOU issues where LSMs re-resolve string-based dev_name via
kern_path().
The mount_move hook is added alongside the existing move_mount hook.
During the transition, LSMs register for both hooks. The move_mount
hook will be removed once all LSMs have been converted.
Some LSMs, such as apparmor and tomoyo, audit the original input passed
in the mount syscall. To keep the same behavior, argument data and flags
are passed in do_* functions. These can be removed if these LSMs no
longer need these information.
All new hooks are registered as sleepable BPF LSM hooks.
Code generated with the assistance of Claude, reviewed by human.
Signed-off-by: Song Liu <song@kernel.org>
---
fs/namespace.c | 35 ++++++++++--
include/linux/lsm_hook_defs.h | 12 ++++
include/linux/security.h | 50 +++++++++++++++++
kernel/bpf/bpf_lsm.c | 7 +++
security/security.c | 101 ++++++++++++++++++++++++++++++++++
5 files changed, 199 insertions(+), 6 deletions(-)
diff --git a/fs/namespace.c b/fs/namespace.c
index 854f4fc66469..de33070e514a 100644
--- a/fs/namespace.c
+++ b/fs/namespace.c
@@ -2875,6 +2875,10 @@ static int do_change_type(const struct path *path, int ms_flags)
if (!type)
return -EINVAL;
+ err = security_mount_change_type(path, ms_flags);
+ if (err)
+ return err;
+
guard(namespace_excl)();
err = may_change_propagation(mnt);
@@ -3007,6 +3011,10 @@ static int do_loopback(const struct path *path, const char *old_name,
if (err)
return err;
+ err = security_mount_bind(&old_path, path, recurse);
+ if (err)
+ return err;
+
if (mnt_ns_loop(old_path.dentry))
return -EINVAL;
@@ -3319,7 +3327,8 @@ static void mnt_warn_timestamp_expiry(const struct path *mountpoint,
* superblock it refers to. This is triggered by specifying MS_REMOUNT|MS_BIND
* to mount(2).
*/
-static int do_reconfigure_mnt(const struct path *path, unsigned int mnt_flags)
+static int do_reconfigure_mnt(const struct path *path, unsigned int mnt_flags,
+ unsigned long flags)
{
struct super_block *sb = path->mnt->mnt_sb;
struct mount *mnt = real_mount(path->mnt);
@@ -3334,6 +3343,10 @@ static int do_reconfigure_mnt(const struct path *path, unsigned int mnt_flags)
if (!can_change_locked_flags(mnt, mnt_flags))
return -EPERM;
+ ret = security_mount_reconfigure(path, mnt_flags, flags);
+ if (ret)
+ return ret;
+
/*
* We're only checking whether the superblock is read-only not
* changing it, so only take down_read(&sb->s_umount).
@@ -3357,7 +3370,7 @@ static int do_reconfigure_mnt(const struct path *path, unsigned int mnt_flags)
* on it - tough luck.
*/
static int do_remount(const struct path *path, int sb_flags,
- int mnt_flags, void *data)
+ int mnt_flags, void *data, unsigned long flags)
{
int err;
struct super_block *sb = path->mnt->mnt_sb;
@@ -3384,6 +3397,9 @@ static int do_remount(const struct path *path, int sb_flags,
fc->oldapi = true;
err = parse_monolithic_mount_data(fc, data);
+ if (!err)
+ err = security_mount_remount(fc, path, mnt_flags, flags,
+ data);
if (!err) {
down_write(&sb->s_umount);
err = -EPERM;
@@ -3713,6 +3729,10 @@ static int do_move_mount_old(const struct path *path, const char *old_name)
if (err)
return err;
+ err = security_mount_move(&old_path, path);
+ if (err)
+ return err;
+
return do_move_mount(&old_path, path, 0);
}
@@ -3791,7 +3811,7 @@ static int do_new_mount_fc(struct fs_context *fc, const struct path *mountpoint,
*/
static int do_new_mount(const struct path *path, const char *fstype,
int sb_flags, int mnt_flags,
- const char *name, void *data)
+ const char *name, void *data, unsigned long flags)
{
struct file_system_type *type;
struct fs_context *fc;
@@ -3835,6 +3855,9 @@ static int do_new_mount(const struct path *path, const char *fstype,
err = parse_monolithic_mount_data(fc, data);
if (!err && !mount_capable(fc))
err = -EPERM;
+
+ if (!err)
+ err = security_mount_new(fc, path, mnt_flags, flags, data);
if (!err)
err = do_new_mount_fc(fc, path, mnt_flags);
@@ -4146,9 +4169,9 @@ int path_mount(const char *dev_name, const struct path *path,
SB_I_VERSION);
if ((flags & (MS_REMOUNT | MS_BIND)) == (MS_REMOUNT | MS_BIND))
- return do_reconfigure_mnt(path, mnt_flags);
+ return do_reconfigure_mnt(path, mnt_flags, flags);
if (flags & MS_REMOUNT)
- return do_remount(path, sb_flags, mnt_flags, data_page);
+ return do_remount(path, sb_flags, mnt_flags, data_page, flags);
if (flags & MS_BIND)
return do_loopback(path, dev_name, flags & MS_REC);
if (flags & (MS_SHARED | MS_PRIVATE | MS_SLAVE | MS_UNBINDABLE))
@@ -4157,7 +4180,7 @@ int path_mount(const char *dev_name, const struct path *path,
return do_move_mount_old(path, dev_name);
return do_new_mount(path, type_page, sb_flags, mnt_flags, dev_name,
- data_page);
+ data_page, flags);
}
int do_mount(const char *dev_name, const char __user *dir_name,
diff --git a/include/linux/lsm_hook_defs.h b/include/linux/lsm_hook_defs.h
index 8c42b4bde09c..6bb67059fb43 100644
--- a/include/linux/lsm_hook_defs.h
+++ b/include/linux/lsm_hook_defs.h
@@ -81,6 +81,18 @@ LSM_HOOK(int, 0, sb_clone_mnt_opts, const struct super_block *oldsb,
unsigned long *set_kern_flags)
LSM_HOOK(int, 0, move_mount, const struct path *from_path,
const struct path *to_path)
+LSM_HOOK(int, 0, mount_bind, const struct path *from, const struct path *to,
+ bool recurse)
+LSM_HOOK(int, 0, mount_new, struct fs_context *fc, const struct path *mp,
+ int mnt_flags, unsigned long flags, void *data)
+LSM_HOOK(int, 0, mount_remount, struct fs_context *fc,
+ const struct path *mp, int mnt_flags, unsigned long flags,
+ void *data)
+LSM_HOOK(int, 0, mount_reconfigure, const struct path *mp,
+ unsigned int mnt_flags, unsigned long flags)
+LSM_HOOK(int, 0, mount_move, const struct path *from_path,
+ const struct path *to_path)
+LSM_HOOK(int, 0, mount_change_type, const struct path *mp, int ms_flags)
LSM_HOOK(int, -EOPNOTSUPP, dentry_init_security, struct dentry *dentry,
int mode, const struct qstr *name, const char **xattr_name,
struct lsm_context *cp)
diff --git a/include/linux/security.h b/include/linux/security.h
index 83a646d72f6f..6e31de9b3d68 100644
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -385,6 +385,17 @@ int security_sb_clone_mnt_opts(const struct super_block *oldsb,
unsigned long kern_flags,
unsigned long *set_kern_flags);
int security_move_mount(const struct path *from_path, const struct path *to_path);
+int security_mount_bind(const struct path *from, const struct path *to,
+ bool recurse);
+int security_mount_new(struct fs_context *fc, const struct path *mp,
+ int mnt_flags, unsigned long flags, void *data);
+int security_mount_remount(struct fs_context *fc, const struct path *mp,
+ int mnt_flags, unsigned long flags, void *data);
+int security_mount_reconfigure(const struct path *mp, unsigned int mnt_flags,
+ unsigned long flags);
+int security_mount_move(const struct path *from_path,
+ const struct path *to_path);
+int security_mount_change_type(const struct path *mp, int ms_flags);
int security_dentry_init_security(struct dentry *dentry, int mode,
const struct qstr *name,
const char **xattr_name,
@@ -847,6 +858,45 @@ static inline int security_move_mount(const struct path *from_path,
return 0;
}
+static inline int security_mount_bind(const struct path *from,
+ const struct path *to, bool recurse)
+{
+ return 0;
+}
+
+static inline int security_mount_new(struct fs_context *fc,
+ const struct path *mp, int mnt_flags,
+ unsigned long flags, void *data)
+{
+ return 0;
+}
+
+static inline int security_mount_remount(struct fs_context *fc,
+ const struct path *mp, int mnt_flags,
+ unsigned long flags, void *data)
+{
+ return 0;
+}
+
+static inline int security_mount_reconfigure(const struct path *mp,
+ unsigned int mnt_flags,
+ unsigned long flags)
+{
+ return 0;
+}
+
+static inline int security_mount_move(const struct path *from_path,
+ const struct path *to_path)
+{
+ return 0;
+}
+
+static inline int security_mount_change_type(const struct path *mp,
+ int ms_flags)
+{
+ return 0;
+}
+
static inline int security_path_notify(const struct path *path, u64 mask,
unsigned int obj_type)
{
diff --git a/kernel/bpf/bpf_lsm.c b/kernel/bpf/bpf_lsm.c
index 0c4a0c8e6f70..65235d70ee23 100644
--- a/kernel/bpf/bpf_lsm.c
+++ b/kernel/bpf/bpf_lsm.c
@@ -383,6 +383,13 @@ BTF_ID(func, bpf_lsm_task_prctl)
BTF_ID(func, bpf_lsm_task_setscheduler)
BTF_ID(func, bpf_lsm_task_to_inode)
BTF_ID(func, bpf_lsm_userns_create)
+BTF_ID(func, bpf_lsm_move_mount)
+BTF_ID(func, bpf_lsm_mount_bind)
+BTF_ID(func, bpf_lsm_mount_new)
+BTF_ID(func, bpf_lsm_mount_remount)
+BTF_ID(func, bpf_lsm_mount_reconfigure)
+BTF_ID(func, bpf_lsm_mount_move)
+BTF_ID(func, bpf_lsm_mount_change_type)
BTF_SET_END(sleepable_lsm_hooks)
BTF_SET_START(untrusted_lsm_hooks)
diff --git a/security/security.c b/security/security.c
index 67af9228c4e9..356ef228d5de 100644
--- a/security/security.c
+++ b/security/security.c
@@ -1156,6 +1156,107 @@ int security_move_mount(const struct path *from_path,
return call_int_hook(move_mount, from_path, to_path);
}
+/**
+ * security_mount_bind() - Check permissions for a bind mount
+ * @from: source path
+ * @to: destination mount point
+ * @recurse: whether this is a recursive bind mount
+ *
+ * Check permission before a bind mount is performed. Called with the
+ * source path already resolved, eliminating TOCTOU issues with
+ * string-based dev_name in security_sb_mount().
+ *
+ * Return: Returns 0 if permission is granted.
+ */
+int security_mount_bind(const struct path *from, const struct path *to,
+ bool recurse)
+{
+ return call_int_hook(mount_bind, from, to, recurse);
+}
+
+/**
+ * security_mount_new() - Check permissions for a new mount
+ * @fc: filesystem context with parsed options
+ * @mp: mount point path
+ * @mnt_flags: mount flags (MNT_*)
+ * @flags: original mount flags (MS_*, used by AppArmor/Tomoyo)
+ * @data: filesystem specific data (used by AppArmor)
+ *
+ * Check permission before a new filesystem is mounted. Called after
+ * mount options are parsed, providing access to the fs_context.
+ *
+ * Return: Returns 0 if permission is granted.
+ */
+int security_mount_new(struct fs_context *fc, const struct path *mp,
+ int mnt_flags, unsigned long flags, void *data)
+{
+ return call_int_hook(mount_new, fc, mp, mnt_flags, flags, data);
+}
+
+/**
+ * security_mount_remount() - Check permissions for a remount
+ * @fc: filesystem context with parsed options
+ * @mp: mount point path
+ * @mnt_flags: mount flags (MNT_*)
+ * @flags: original mount flags (MS_*, used by AppArmor/Tomoyo)
+ * @data: filesystem specific data (used by AppArmor)
+ *
+ * Check permission before a filesystem is remounted. Called after
+ * mount options are parsed, providing access to the fs_context.
+ *
+ * Return: Returns 0 if permission is granted.
+ */
+int security_mount_remount(struct fs_context *fc, const struct path *mp,
+ int mnt_flags, unsigned long flags, void *data)
+{
+ return call_int_hook(mount_remount, fc, mp, mnt_flags, flags, data);
+}
+
+/**
+ * security_mount_reconfigure() - Check permissions for mount reconfiguration
+ * @mp: mount point path
+ * @mnt_flags: new mount flags (MNT_*)
+ * @flags: original mount flags (MS_*, used by AppArmor/Tomoyo)
+ *
+ * Check permission before mount flags are reconfigured (MS_REMOUNT|MS_BIND).
+ *
+ * Return: Returns 0 if permission is granted.
+ */
+int security_mount_reconfigure(const struct path *mp, unsigned int mnt_flags,
+ unsigned long flags)
+{
+ return call_int_hook(mount_reconfigure, mp, mnt_flags, flags);
+}
+
+/**
+ * security_mount_move() - Check permissions for moving a mount
+ * @from_path: source mount path
+ * @to_path: destination mount point path
+ *
+ * Check permission before a mount is moved.
+ *
+ * Return: Returns 0 if permission is granted.
+ */
+int security_mount_move(const struct path *from_path,
+ const struct path *to_path)
+{
+ return call_int_hook(mount_move, from_path, to_path);
+}
+
+/**
+ * security_mount_change_type() - Check permissions for propagation changes
+ * @mp: mount point path
+ * @ms_flags: propagation flags (MS_SHARED, MS_PRIVATE, etc.)
+ *
+ * Check permission before mount propagation type is changed.
+ *
+ * Return: Returns 0 if permission is granted.
+ */
+int security_mount_change_type(const struct path *mp, int ms_flags)
+{
+ return call_int_hook(mount_change_type, mp, ms_flags);
+}
+
/**
* security_path_notify() - Check if setting a watch is allowed
* @path: file path
--
2.52.0
^ permalink raw reply related [flat|nested] 21+ messages in thread
* [PATCH 2/7] apparmor: Remove redundant MS_MGC_MSK stripping in apparmor_sb_mount
2026-03-18 18:43 [PATCH 0/7] lsm: Replace security_sb_mount with granular mount hooks Song Liu
2026-03-18 18:43 ` [PATCH 1/7] lsm: Add granular mount hooks to replace security_sb_mount Song Liu
@ 2026-03-18 18:43 ` Song Liu
2026-03-18 18:43 ` [PATCH 3/7] apparmor: Convert from sb_mount to granular mount hooks Song Liu
` (4 subsequent siblings)
6 siblings, 0 replies; 21+ messages in thread
From: Song Liu @ 2026-03-18 18:43 UTC (permalink / raw)
To: linux-security-module, linux-fsdevel, selinux, apparmor
Cc: paul, jmorris, serge, viro, brauner, jack, john.johansen,
stephen.smalley.work, omosnace, mic, gnoack, takedakn,
penguin-kernel, herton, kernel-team, Song Liu
path_mount() already strips the magic number from flags before
calling security_sb_mount(), so this check in apparmor_sb_mount()
is a no-op. Remove it.
Code generated with the assistance of Claude, reviewed by human.
Signed-off-by: Song Liu <song@kernel.org>
---
security/apparmor/lsm.c | 4 ----
1 file changed, 4 deletions(-)
diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c
index c1d42fc72fdb..48a8655af11e 100644
--- a/security/apparmor/lsm.c
+++ b/security/apparmor/lsm.c
@@ -704,10 +704,6 @@ static int apparmor_sb_mount(const char *dev_name, const struct path *path,
int error = 0;
bool needput;
- /* Discard magic */
- if ((flags & MS_MGC_MSK) == MS_MGC_VAL)
- flags &= ~MS_MGC_MSK;
-
flags &= ~AA_MS_IGNORE_MASK;
label = __begin_current_label_crit_section(&needput);
--
2.52.0
^ permalink raw reply related [flat|nested] 21+ messages in thread
* [PATCH 3/7] apparmor: Convert from sb_mount to granular mount hooks
2026-03-18 18:43 [PATCH 0/7] lsm: Replace security_sb_mount with granular mount hooks Song Liu
2026-03-18 18:43 ` [PATCH 1/7] lsm: Add granular mount hooks to replace security_sb_mount Song Liu
2026-03-18 18:43 ` [PATCH 2/7] apparmor: Remove redundant MS_MGC_MSK stripping in apparmor_sb_mount Song Liu
@ 2026-03-18 18:43 ` Song Liu
2026-03-18 18:43 ` [PATCH 4/7] selinux: " Song Liu
` (3 subsequent siblings)
6 siblings, 0 replies; 21+ messages in thread
From: Song Liu @ 2026-03-18 18:43 UTC (permalink / raw)
To: linux-security-module, linux-fsdevel, selinux, apparmor
Cc: paul, jmorris, serge, viro, brauner, jack, john.johansen,
stephen.smalley.work, omosnace, mic, gnoack, takedakn,
penguin-kernel, herton, kernel-team, Song Liu
Replace AppArmor's monolithic apparmor_sb_mount() with granular
mount hooks.
Key changes:
- mount_bind: uses the pre-resolved struct path from VFS instead of
re-resolving dev_name via kern_path(), eliminating a TOCTOU
vulnerability. aa_bind_mount() now takes a struct path instead of
a string for the source.
- mount_new, mount_remount: receive the original mount(2) flags and
data parameters for policy matching via match_mnt_flags() and
AA_MNT_CONT_MATCH data matching.
- mount_reconfigure: handles MS_REMOUNT|MS_BIND (mount attribute
reconfiguration) which was previously handled as a remount.
- mount_move: reuses apparmor_move_mount() which already handles
pre-resolved paths.
- mount_change_type: propagation type changes.
aa_move_mount_old() is removed since move mounts now go through
security_mount_move() with pre-resolved struct path pointers for
both the old mount(2) and new move_mount(2) APIs.
Code generated with the assistance of Claude, reviewed by human.
Signed-off-by: Song Liu <song@kernel.org>
---
security/apparmor/include/mount.h | 5 +-
security/apparmor/lsm.c | 99 ++++++++++++++++++++++++-------
security/apparmor/mount.c | 37 ++----------
3 files changed, 83 insertions(+), 58 deletions(-)
diff --git a/security/apparmor/include/mount.h b/security/apparmor/include/mount.h
index 46834f828179..088e2f938cc1 100644
--- a/security/apparmor/include/mount.h
+++ b/security/apparmor/include/mount.h
@@ -31,16 +31,13 @@ int aa_remount(const struct cred *subj_cred,
int aa_bind_mount(const struct cred *subj_cred,
struct aa_label *label, const struct path *path,
- const char *old_name, unsigned long flags);
+ const struct path *old_path, bool recurse);
int aa_mount_change_type(const struct cred *subj_cred,
struct aa_label *label, const struct path *path,
unsigned long flags);
-int aa_move_mount_old(const struct cred *subj_cred,
- struct aa_label *label, const struct path *path,
- const char *old_name);
int aa_move_mount(const struct cred *subj_cred,
struct aa_label *label, const struct path *from_path,
const struct path *to_path);
diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c
index 48a8655af11e..7fe774535992 100644
--- a/security/apparmor/lsm.c
+++ b/security/apparmor/lsm.c
@@ -13,6 +13,7 @@
#include <linux/mm.h>
#include <linux/mman.h>
#include <linux/mount.h>
+#include <linux/fs_context.h>
#include <linux/namei.h>
#include <linux/ptrace.h>
#include <linux/ctype.h>
@@ -697,34 +698,83 @@ static int apparmor_uring_sqpoll(void)
}
#endif /* CONFIG_IO_URING */
-static int apparmor_sb_mount(const char *dev_name, const struct path *path,
- const char *type, unsigned long flags, void *data)
+static int apparmor_mount_bind(const struct path *from, const struct path *to,
+ bool recurse)
{
struct aa_label *label;
int error = 0;
bool needput;
- flags &= ~AA_MS_IGNORE_MASK;
+ label = __begin_current_label_crit_section(&needput);
+ if (!unconfined(label))
+ error = aa_bind_mount(current_cred(), label, to, from,
+ recurse);
+ __end_current_label_crit_section(label, needput);
+ return error;
+}
+
+static int apparmor_mount_new(struct fs_context *fc, const struct path *mp,
+ int mnt_flags, unsigned long flags, void *data)
+{
+ struct aa_label *label;
+ int error = 0;
+ bool needput;
+
+ /* flags and data are from the original mount(2) call */
label = __begin_current_label_crit_section(&needput);
- if (!unconfined(label)) {
- if (flags & MS_REMOUNT)
- error = aa_remount(current_cred(), label, path, flags,
- data);
- else if (flags & MS_BIND)
- error = aa_bind_mount(current_cred(), label, path,
- dev_name, flags);
- else if (flags & (MS_SHARED | MS_PRIVATE | MS_SLAVE |
- MS_UNBINDABLE))
- error = aa_mount_change_type(current_cred(), label,
- path, flags);
- else if (flags & MS_MOVE)
- error = aa_move_mount_old(current_cred(), label, path,
- dev_name);
- else
- error = aa_new_mount(current_cred(), label, dev_name,
- path, type, flags, data);
- }
+ if (!unconfined(label))
+ error = aa_new_mount(current_cred(), label, fc->source,
+ mp, fc->fs_type->name, flags, data);
+ __end_current_label_crit_section(label, needput);
+
+ return error;
+}
+
+static int apparmor_mount_remount(struct fs_context *fc, const struct path *mp,
+ int mnt_flags, unsigned long flags,
+ void *data)
+{
+ struct aa_label *label;
+ int error = 0;
+ bool needput;
+
+ /* flags and data are from the original mount(2) call */
+ label = __begin_current_label_crit_section(&needput);
+ if (!unconfined(label))
+ error = aa_remount(current_cred(), label, mp, flags, data);
+ __end_current_label_crit_section(label, needput);
+
+ return error;
+}
+
+static int apparmor_mount_reconfigure(const struct path *mp,
+ unsigned int mnt_flags,
+ unsigned long flags)
+{
+ struct aa_label *label;
+ int error = 0;
+ bool needput;
+
+ /* flags are from the original mount(2) call */
+ label = __begin_current_label_crit_section(&needput);
+ if (!unconfined(label))
+ error = aa_remount(current_cred(), label, mp, flags, NULL);
+ __end_current_label_crit_section(label, needput);
+
+ return error;
+}
+
+static int apparmor_mount_change_type(const struct path *mp, int ms_flags)
+{
+ struct aa_label *label;
+ int error = 0;
+ bool needput;
+
+ label = __begin_current_label_crit_section(&needput);
+ if (!unconfined(label))
+ error = aa_mount_change_type(current_cred(), label, mp,
+ ms_flags);
__end_current_label_crit_section(label, needput);
return error;
@@ -1664,7 +1714,12 @@ static struct security_hook_list apparmor_hooks[] __ro_after_init = {
LSM_HOOK_INIT(capable, apparmor_capable),
LSM_HOOK_INIT(move_mount, apparmor_move_mount),
- LSM_HOOK_INIT(sb_mount, apparmor_sb_mount),
+ LSM_HOOK_INIT(mount_bind, apparmor_mount_bind),
+ LSM_HOOK_INIT(mount_new, apparmor_mount_new),
+ LSM_HOOK_INIT(mount_remount, apparmor_mount_remount),
+ LSM_HOOK_INIT(mount_reconfigure, apparmor_mount_reconfigure),
+ LSM_HOOK_INIT(mount_move, apparmor_move_mount),
+ LSM_HOOK_INIT(mount_change_type, apparmor_mount_change_type),
LSM_HOOK_INIT(sb_umount, apparmor_sb_umount),
LSM_HOOK_INIT(sb_pivotroot, apparmor_sb_pivotroot),
diff --git a/security/apparmor/mount.c b/security/apparmor/mount.c
index 523570aa1a5a..38b40e16014f 100644
--- a/security/apparmor/mount.c
+++ b/security/apparmor/mount.c
@@ -418,25 +418,17 @@ int aa_remount(const struct cred *subj_cred,
}
int aa_bind_mount(const struct cred *subj_cred,
- struct aa_label *label, const struct path *path,
- const char *dev_name, unsigned long flags)
+ struct aa_label *label, const struct path *path,
+ const struct path *old_path, bool recurse)
{
struct aa_profile *profile;
char *buffer = NULL, *old_buffer = NULL;
- struct path old_path;
+ unsigned long flags = MS_BIND | (recurse ? MS_REC : 0);
int error;
AA_BUG(!label);
AA_BUG(!path);
-
- if (!dev_name || !*dev_name)
- return -EINVAL;
-
- flags &= MS_REC | MS_BIND;
-
- error = kern_path(dev_name, LOOKUP_FOLLOW|LOOKUP_AUTOMOUNT, &old_path);
- if (error)
- return error;
+ AA_BUG(!old_path);
buffer = aa_get_buffer(false);
old_buffer = aa_get_buffer(false);
@@ -445,12 +437,11 @@ int aa_bind_mount(const struct cred *subj_cred,
goto out;
error = fn_for_each_confined(label, profile,
- match_mnt(subj_cred, profile, path, buffer, &old_path,
+ match_mnt(subj_cred, profile, path, buffer, old_path,
old_buffer, NULL, flags, NULL, false));
out:
aa_put_buffer(buffer);
aa_put_buffer(old_buffer);
- path_put(&old_path);
return error;
}
@@ -514,24 +505,6 @@ int aa_move_mount(const struct cred *subj_cred,
return error;
}
-int aa_move_mount_old(const struct cred *subj_cred, struct aa_label *label,
- const struct path *path, const char *orig_name)
-{
- struct path old_path;
- int error;
-
- if (!orig_name || !*orig_name)
- return -EINVAL;
- error = kern_path(orig_name, LOOKUP_FOLLOW, &old_path);
- if (error)
- return error;
-
- error = aa_move_mount(subj_cred, label, &old_path, path);
- path_put(&old_path);
-
- return error;
-}
-
int aa_new_mount(const struct cred *subj_cred, struct aa_label *label,
const char *dev_name, const struct path *path,
const char *type, unsigned long flags, void *data)
--
2.52.0
^ permalink raw reply related [flat|nested] 21+ messages in thread
* [PATCH 4/7] selinux: Convert from sb_mount to granular mount hooks
2026-03-18 18:43 [PATCH 0/7] lsm: Replace security_sb_mount with granular mount hooks Song Liu
` (2 preceding siblings ...)
2026-03-18 18:43 ` [PATCH 3/7] apparmor: Convert from sb_mount to granular mount hooks Song Liu
@ 2026-03-18 18:43 ` Song Liu
2026-03-18 18:43 ` [PATCH 5/7] landlock: " Song Liu
` (2 subsequent siblings)
6 siblings, 0 replies; 21+ messages in thread
From: Song Liu @ 2026-03-18 18:43 UTC (permalink / raw)
To: linux-security-module, linux-fsdevel, selinux, apparmor
Cc: paul, jmorris, serge, viro, brauner, jack, john.johansen,
stephen.smalley.work, omosnace, mic, gnoack, takedakn,
penguin-kernel, herton, kernel-team, Song Liu
Replace selinux_mount() with granular mount hooks, preserving the
same permission checks:
- mount_bind, mount_new, mount_change_type: FILE__MOUNTON
- mount_remount, mount_reconfigure: FILESYSTEM__REMOUNT
- mount_move: FILE__MOUNTON (reuses selinux_move_mount)
The flags and data parameters are unused by SELinux.
Code generated with the assistance of Claude, reviewed by human.
Signed-off-by: Song Liu <song@kernel.org>
---
security/selinux/hooks.c | 47 ++++++++++++++++++++++++++++++----------
1 file changed, 35 insertions(+), 12 deletions(-)
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index d8224ea113d1..415b5541ab9e 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -2778,19 +2778,37 @@ static int selinux_sb_statfs(struct dentry *dentry)
return superblock_has_perm(cred, dentry->d_sb, FILESYSTEM__GETATTR, &ad);
}
-static int selinux_mount(const char *dev_name,
- const struct path *path,
- const char *type,
- unsigned long flags,
- void *data)
+static int selinux_mount_bind(const struct path *from, const struct path *to,
+ bool recurse)
{
- const struct cred *cred = current_cred();
+ return path_has_perm(current_cred(), to, FILE__MOUNTON);
+}
- if (flags & MS_REMOUNT)
- return superblock_has_perm(cred, path->dentry->d_sb,
- FILESYSTEM__REMOUNT, NULL);
- else
- return path_has_perm(cred, path, FILE__MOUNTON);
+static int selinux_mount_new(struct fs_context *fc, const struct path *mp,
+ int mnt_flags, unsigned long flags, void *data)
+{
+ return path_has_perm(current_cred(), mp, FILE__MOUNTON);
+}
+
+static int selinux_mount_remount(struct fs_context *fc, const struct path *mp,
+ int mnt_flags, unsigned long flags,
+ void *data)
+{
+ return superblock_has_perm(current_cred(), fc->root->d_sb,
+ FILESYSTEM__REMOUNT, NULL);
+}
+
+static int selinux_mount_reconfigure(const struct path *mp,
+ unsigned int mnt_flags,
+ unsigned long flags)
+{
+ return superblock_has_perm(current_cred(), mp->dentry->d_sb,
+ FILESYSTEM__REMOUNT, NULL);
+}
+
+static int selinux_mount_change_type(const struct path *mp, int ms_flags)
+{
+ return path_has_perm(current_cred(), mp, FILE__MOUNTON);
}
static int selinux_move_mount(const struct path *from_path,
@@ -7449,7 +7467,12 @@ static struct security_hook_list selinux_hooks[] __ro_after_init = {
LSM_HOOK_INIT(sb_kern_mount, selinux_sb_kern_mount),
LSM_HOOK_INIT(sb_show_options, selinux_sb_show_options),
LSM_HOOK_INIT(sb_statfs, selinux_sb_statfs),
- LSM_HOOK_INIT(sb_mount, selinux_mount),
+ LSM_HOOK_INIT(mount_bind, selinux_mount_bind),
+ LSM_HOOK_INIT(mount_new, selinux_mount_new),
+ LSM_HOOK_INIT(mount_remount, selinux_mount_remount),
+ LSM_HOOK_INIT(mount_reconfigure, selinux_mount_reconfigure),
+ LSM_HOOK_INIT(mount_change_type, selinux_mount_change_type),
+ LSM_HOOK_INIT(mount_move, selinux_move_mount),
LSM_HOOK_INIT(sb_umount, selinux_umount),
LSM_HOOK_INIT(sb_set_mnt_opts, selinux_set_mnt_opts),
LSM_HOOK_INIT(sb_clone_mnt_opts, selinux_sb_clone_mnt_opts),
--
2.52.0
^ permalink raw reply related [flat|nested] 21+ messages in thread
* [PATCH 5/7] landlock: Convert from sb_mount to granular mount hooks
2026-03-18 18:43 [PATCH 0/7] lsm: Replace security_sb_mount with granular mount hooks Song Liu
` (3 preceding siblings ...)
2026-03-18 18:43 ` [PATCH 4/7] selinux: " Song Liu
@ 2026-03-18 18:43 ` Song Liu
2026-03-18 18:43 ` [PATCH 6/7] tomoyo: " Song Liu
2026-03-18 18:44 ` [PATCH 7/7] lsm: Remove security_sb_mount and security_move_mount Song Liu
6 siblings, 0 replies; 21+ messages in thread
From: Song Liu @ 2026-03-18 18:43 UTC (permalink / raw)
To: linux-security-module, linux-fsdevel, selinux, apparmor
Cc: paul, jmorris, serge, viro, brauner, jack, john.johansen,
stephen.smalley.work, omosnace, mic, gnoack, takedakn,
penguin-kernel, herton, kernel-team, Song Liu
Replace hook_sb_mount() with granular mount hooks. Landlock denies
all mount operations for sandboxed processes regardless of flags,
so all new hooks share a common hook_mount_deny() helper. The
mount_move hook reuses hook_move_mount().
Code generated with the assistance of Claude, reviewed by human.
Signed-off-by: Song Liu <song@kernel.org>
---
security/landlock/fs.c | 40 ++++++++++++++++++++++++++++++++++++----
1 file changed, 36 insertions(+), 4 deletions(-)
diff --git a/security/landlock/fs.c b/security/landlock/fs.c
index e764470f588c..6e810550efcb 100644
--- a/security/landlock/fs.c
+++ b/security/landlock/fs.c
@@ -1417,9 +1417,7 @@ static void log_fs_change_topology_dentry(
* inherit these new constraints. Anyway, for backward compatibility reasons,
* a dedicated user space option would be required (e.g. as a ruleset flag).
*/
-static int hook_sb_mount(const char *const dev_name,
- const struct path *const path, const char *const type,
- const unsigned long flags, void *const data)
+static int hook_mount_deny(const struct path *const path)
{
size_t handle_layer;
const struct landlock_cred_security *const subject =
@@ -1433,6 +1431,35 @@ static int hook_sb_mount(const char *const dev_name,
return -EPERM;
}
+static int hook_mount_bind(const struct path *const from,
+ const struct path *const to, bool recurse)
+{
+ return hook_mount_deny(to);
+}
+
+static int hook_mount_new(struct fs_context *fc, const struct path *const mp,
+ int mnt_flags, unsigned long flags, void *data)
+{
+ return hook_mount_deny(mp);
+}
+
+static int hook_mount_remount(struct fs_context *fc, const struct path *mp,
+ int mnt_flags, unsigned long flags, void *data)
+{
+ return hook_mount_deny(mp);
+}
+
+static int hook_mount_reconfigure(const struct path *const mp,
+ unsigned int mnt_flags, unsigned long flags)
+{
+ return hook_mount_deny(mp);
+}
+
+static int hook_mount_change_type(const struct path *const mp, int ms_flags)
+{
+ return hook_mount_deny(mp);
+}
+
static int hook_move_mount(const struct path *const from_path,
const struct path *const to_path)
{
@@ -1824,7 +1851,12 @@ static struct security_hook_list landlock_hooks[] __ro_after_init = {
LSM_HOOK_INIT(inode_free_security_rcu, hook_inode_free_security_rcu),
LSM_HOOK_INIT(sb_delete, hook_sb_delete),
- LSM_HOOK_INIT(sb_mount, hook_sb_mount),
+ LSM_HOOK_INIT(mount_bind, hook_mount_bind),
+ LSM_HOOK_INIT(mount_new, hook_mount_new),
+ LSM_HOOK_INIT(mount_remount, hook_mount_remount),
+ LSM_HOOK_INIT(mount_reconfigure, hook_mount_reconfigure),
+ LSM_HOOK_INIT(mount_change_type, hook_mount_change_type),
+ LSM_HOOK_INIT(mount_move, hook_move_mount),
LSM_HOOK_INIT(move_mount, hook_move_mount),
LSM_HOOK_INIT(sb_umount, hook_sb_umount),
LSM_HOOK_INIT(sb_remount, hook_sb_remount),
--
2.52.0
^ permalink raw reply related [flat|nested] 21+ messages in thread
* [PATCH 6/7] tomoyo: Convert from sb_mount to granular mount hooks
2026-03-18 18:43 [PATCH 0/7] lsm: Replace security_sb_mount with granular mount hooks Song Liu
` (4 preceding siblings ...)
2026-03-18 18:43 ` [PATCH 5/7] landlock: " Song Liu
@ 2026-03-18 18:43 ` Song Liu
2026-03-21 12:54 ` Tetsuo Handa
2026-03-18 18:44 ` [PATCH 7/7] lsm: Remove security_sb_mount and security_move_mount Song Liu
6 siblings, 1 reply; 21+ messages in thread
From: Song Liu @ 2026-03-18 18:43 UTC (permalink / raw)
To: linux-security-module, linux-fsdevel, selinux, apparmor
Cc: paul, jmorris, serge, viro, brauner, jack, john.johansen,
stephen.smalley.work, omosnace, mic, gnoack, takedakn,
penguin-kernel, herton, kernel-team, Song Liu
Replace tomoyo_sb_mount() with granular mount hooks. Each hook
reconstructs the MS_* flags expected by tomoyo_mount_permission()
using the original flags parameter where available.
Key changes:
- mount_bind: passes the pre-resolved source path to
tomoyo_mount_acl() via a new dev_path parameter, instead of
re-resolving dev_name via kern_path(). This eliminates a TOCTOU
vulnerability.
- mount_new, mount_remount, mount_reconfigure: use the original
mount(2) flags for policy matching.
- mount_move: passes pre-resolved paths for both source and
destination.
- mount_change_type: passes raw ms_flags directly.
Also removes the unused data_page parameter from
tomoyo_mount_permission().
Code generated with the assistance of Claude, reviewed by human.
Signed-off-by: Song Liu <song@kernel.org>
---
security/tomoyo/common.h | 2 +-
security/tomoyo/mount.c | 31 +++++++++++++-------
security/tomoyo/tomoyo.c | 63 ++++++++++++++++++++++++++++++----------
3 files changed, 70 insertions(+), 26 deletions(-)
diff --git a/security/tomoyo/common.h b/security/tomoyo/common.h
index 4f1704c911ef..e40441844eab 100644
--- a/security/tomoyo/common.h
+++ b/security/tomoyo/common.h
@@ -1013,7 +1013,7 @@ int tomoyo_mkdev_perm(const u8 operation, const struct path *path,
const unsigned int mode, unsigned int dev);
int tomoyo_mount_permission(const char *dev_name, const struct path *path,
const char *type, unsigned long flags,
- void *data_page);
+ const struct path *dev_path);
int tomoyo_open_control(const u8 type, struct file *file);
int tomoyo_path2_perm(const u8 operation, const struct path *path1,
const struct path *path2);
diff --git a/security/tomoyo/mount.c b/security/tomoyo/mount.c
index 322dfd188ada..82ffe7d02814 100644
--- a/security/tomoyo/mount.c
+++ b/security/tomoyo/mount.c
@@ -70,6 +70,7 @@ static bool tomoyo_check_mount_acl(struct tomoyo_request_info *r,
* @dir: Pointer to "struct path".
* @type: Name of filesystem type.
* @flags: Mount options.
+ * @dev_path: Pre-resolved device/source path. Maybe NULL.
*
* Returns 0 on success, negative value otherwise.
*
@@ -78,11 +79,11 @@ static bool tomoyo_check_mount_acl(struct tomoyo_request_info *r,
static int tomoyo_mount_acl(struct tomoyo_request_info *r,
const char *dev_name,
const struct path *dir, const char *type,
- unsigned long flags)
+ unsigned long flags,
+ const struct path *dev_path)
__must_hold_shared(&tomoyo_ss)
{
struct tomoyo_obj_info obj = { };
- struct path path;
struct file_system_type *fstype = NULL;
const char *requested_type = NULL;
const char *requested_dir_name = NULL;
@@ -134,13 +135,23 @@ static int tomoyo_mount_acl(struct tomoyo_request_info *r,
need_dev = 1;
}
if (need_dev) {
- /* Get mount point or device file. */
- if (!dev_name || kern_path(dev_name, LOOKUP_FOLLOW, &path)) {
+ if (dev_path) {
+ /* Use pre-resolved path to avoid TOCTOU issues. */
+ obj.path1 = *dev_path;
+ path_get(&obj.path1);
+ } else if (!dev_name) {
error = -ENOENT;
goto out;
+ } else {
+ struct path path;
+
+ if (kern_path(dev_name, LOOKUP_FOLLOW, &path)) {
+ error = -ENOENT;
+ goto out;
+ }
+ obj.path1 = path;
}
- obj.path1 = path;
- requested_dev_name = tomoyo_realpath_from_path(&path);
+ requested_dev_name = tomoyo_realpath_from_path(&obj.path1);
if (!requested_dev_name) {
error = -ENOENT;
goto out;
@@ -173,7 +184,7 @@ static int tomoyo_mount_acl(struct tomoyo_request_info *r,
if (fstype)
put_filesystem(fstype);
kfree(requested_type);
- /* Drop refcount obtained by kern_path(). */
+ /* Drop refcount obtained by kern_path() or path_get(). */
if (obj.path1.dentry)
path_put(&obj.path1);
return error;
@@ -186,13 +197,13 @@ static int tomoyo_mount_acl(struct tomoyo_request_info *r,
* @path: Pointer to "struct path".
* @type: Name of filesystem type. Maybe NULL.
* @flags: Mount options.
- * @data_page: Optional data. Maybe NULL.
+ * @dev_path: Pre-resolved device/source path. Maybe NULL.
*
* Returns 0 on success, negative value otherwise.
*/
int tomoyo_mount_permission(const char *dev_name, const struct path *path,
const char *type, unsigned long flags,
- void *data_page)
+ const struct path *dev_path)
{
struct tomoyo_request_info r;
int error;
@@ -236,7 +247,7 @@ int tomoyo_mount_permission(const char *dev_name, const struct path *path,
if (!type)
type = "<NULL>";
idx = tomoyo_read_lock();
- error = tomoyo_mount_acl(&r, dev_name, path, type, flags);
+ error = tomoyo_mount_acl(&r, dev_name, path, type, flags, dev_path);
tomoyo_read_unlock(idx);
return error;
}
diff --git a/security/tomoyo/tomoyo.c b/security/tomoyo/tomoyo.c
index c66e02ed8ee3..ac84e1f03d5e 100644
--- a/security/tomoyo/tomoyo.c
+++ b/security/tomoyo/tomoyo.c
@@ -6,6 +6,8 @@
*/
#include <linux/lsm_hooks.h>
+#include <linux/fs_context.h>
+#include <uapi/linux/mount.h>
#include <uapi/linux/lsm.h>
#include "common.h"
@@ -398,21 +400,47 @@ static int tomoyo_path_chroot(const struct path *path)
return tomoyo_path_perm(TOMOYO_TYPE_CHROOT, path, NULL);
}
-/**
- * tomoyo_sb_mount - Target for security_sb_mount().
- *
- * @dev_name: Name of device file. Maybe NULL.
- * @path: Pointer to "struct path".
- * @type: Name of filesystem type. Maybe NULL.
- * @flags: Mount options.
- * @data: Optional data. Maybe NULL.
- *
- * Returns 0 on success, negative value otherwise.
- */
-static int tomoyo_sb_mount(const char *dev_name, const struct path *path,
- const char *type, unsigned long flags, void *data)
+static int tomoyo_mount_bind(const struct path *from, const struct path *to,
+ bool recurse)
+{
+ unsigned long flags = MS_BIND | (recurse ? MS_REC : 0);
+
+ return tomoyo_mount_permission(NULL, to, NULL, flags, from);
+}
+
+static int tomoyo_mount_new(struct fs_context *fc, const struct path *mp,
+ int mnt_flags, unsigned long flags, void *data)
+{
+ /* Use original MS_* flags for policy matching */
+ return tomoyo_mount_permission(fc->source, mp, fc->fs_type->name,
+ flags, NULL);
+}
+
+static int tomoyo_mount_remount(struct fs_context *fc, const struct path *mp,
+ int mnt_flags, unsigned long flags, void *data)
+{
+ /* Use original MS_* flags for policy matching */
+ return tomoyo_mount_permission(NULL, mp, NULL, flags, NULL);
+}
+
+static int tomoyo_mount_reconfigure(const struct path *mp,
+ unsigned int mnt_flags,
+ unsigned long flags)
+{
+ /* Use original MS_* flags for policy matching */
+ return tomoyo_mount_permission(NULL, mp, NULL, flags, NULL);
+}
+
+static int tomoyo_mount_change_type(const struct path *mp, int ms_flags)
+{
+ return tomoyo_mount_permission(NULL, mp, NULL, ms_flags, NULL);
+}
+
+static int tomoyo_move_mount(const struct path *from_path,
+ const struct path *to_path)
{
- return tomoyo_mount_permission(dev_name, path, type, flags, data);
+ return tomoyo_mount_permission(NULL, to_path, NULL, MS_MOVE,
+ from_path);
}
/**
@@ -576,7 +604,12 @@ static struct security_hook_list tomoyo_hooks[] __ro_after_init = {
LSM_HOOK_INIT(path_chmod, tomoyo_path_chmod),
LSM_HOOK_INIT(path_chown, tomoyo_path_chown),
LSM_HOOK_INIT(path_chroot, tomoyo_path_chroot),
- LSM_HOOK_INIT(sb_mount, tomoyo_sb_mount),
+ LSM_HOOK_INIT(mount_bind, tomoyo_mount_bind),
+ LSM_HOOK_INIT(mount_new, tomoyo_mount_new),
+ LSM_HOOK_INIT(mount_remount, tomoyo_mount_remount),
+ LSM_HOOK_INIT(mount_reconfigure, tomoyo_mount_reconfigure),
+ LSM_HOOK_INIT(mount_change_type, tomoyo_mount_change_type),
+ LSM_HOOK_INIT(mount_move, tomoyo_move_mount),
LSM_HOOK_INIT(sb_umount, tomoyo_sb_umount),
LSM_HOOK_INIT(sb_pivotroot, tomoyo_sb_pivotroot),
LSM_HOOK_INIT(socket_bind, tomoyo_socket_bind),
--
2.52.0
^ permalink raw reply related [flat|nested] 21+ messages in thread
* [PATCH 7/7] lsm: Remove security_sb_mount and security_move_mount
2026-03-18 18:43 [PATCH 0/7] lsm: Replace security_sb_mount with granular mount hooks Song Liu
` (5 preceding siblings ...)
2026-03-18 18:43 ` [PATCH 6/7] tomoyo: " Song Liu
@ 2026-03-18 18:44 ` Song Liu
6 siblings, 0 replies; 21+ messages in thread
From: Song Liu @ 2026-03-18 18:44 UTC (permalink / raw)
To: linux-security-module, linux-fsdevel, selinux, apparmor
Cc: paul, jmorris, serge, viro, brauner, jack, john.johansen,
stephen.smalley.work, omosnace, mic, gnoack, takedakn,
penguin-kernel, herton, kernel-team, Song Liu
Now that all LSMs have been converted to granular mount hooks,
remove the old hooks:
- security_sb_mount(): removed from lsm_hook_defs.h, security.h,
security.c, and its call in path_mount().
- security_move_mount(): removed and replaced by security_mount_move()
in do_move_mount(). All LSMs now use mount_move exclusively.
Code generated with the assistance of Claude, reviewed by human.
Signed-off-by: Song Liu <song@kernel.org>
---
fs/namespace.c | 6 +-----
include/linux/lsm_hook_defs.h | 4 ----
include/linux/security.h | 16 ---------------
kernel/bpf/bpf_lsm.c | 2 --
security/apparmor/lsm.c | 1 -
security/landlock/fs.c | 1 -
security/security.c | 38 -----------------------------------
security/selinux/hooks.c | 2 --
8 files changed, 1 insertion(+), 69 deletions(-)
diff --git a/fs/namespace.c b/fs/namespace.c
index de33070e514a..ba5baccdde67 100644
--- a/fs/namespace.c
+++ b/fs/namespace.c
@@ -4108,7 +4108,6 @@ int path_mount(const char *dev_name, const struct path *path,
const char *type_page, unsigned long flags, void *data_page)
{
unsigned int mnt_flags = 0, sb_flags;
- int ret;
/* Discard magic */
if ((flags & MS_MGC_MSK) == MS_MGC_VAL)
@@ -4121,9 +4120,6 @@ int path_mount(const char *dev_name, const struct path *path,
if (flags & MS_NOUSER)
return -EINVAL;
- ret = security_sb_mount(dev_name, path, type_page, flags, data_page);
- if (ret)
- return ret;
if (!may_mount())
return -EPERM;
if (flags & SB_MANDLOCK)
@@ -4538,7 +4534,7 @@ static inline int vfs_move_mount(const struct path *from_path,
{
int ret;
- ret = security_move_mount(from_path, to_path);
+ ret = security_mount_move(from_path, to_path);
if (ret)
return ret;
diff --git a/include/linux/lsm_hook_defs.h b/include/linux/lsm_hook_defs.h
index 6bb67059fb43..95537574c40b 100644
--- a/include/linux/lsm_hook_defs.h
+++ b/include/linux/lsm_hook_defs.h
@@ -69,8 +69,6 @@ LSM_HOOK(int, 0, sb_remount, struct super_block *sb, void *mnt_opts)
LSM_HOOK(int, 0, sb_kern_mount, const struct super_block *sb)
LSM_HOOK(int, 0, sb_show_options, struct seq_file *m, struct super_block *sb)
LSM_HOOK(int, 0, sb_statfs, struct dentry *dentry)
-LSM_HOOK(int, 0, sb_mount, const char *dev_name, const struct path *path,
- const char *type, unsigned long flags, void *data)
LSM_HOOK(int, 0, sb_umount, struct vfsmount *mnt, int flags)
LSM_HOOK(int, 0, sb_pivotroot, const struct path *old_path,
const struct path *new_path)
@@ -79,8 +77,6 @@ LSM_HOOK(int, 0, sb_set_mnt_opts, struct super_block *sb, void *mnt_opts,
LSM_HOOK(int, 0, sb_clone_mnt_opts, const struct super_block *oldsb,
struct super_block *newsb, unsigned long kern_flags,
unsigned long *set_kern_flags)
-LSM_HOOK(int, 0, move_mount, const struct path *from_path,
- const struct path *to_path)
LSM_HOOK(int, 0, mount_bind, const struct path *from, const struct path *to,
bool recurse)
LSM_HOOK(int, 0, mount_new, struct fs_context *fc, const struct path *mp,
diff --git a/include/linux/security.h b/include/linux/security.h
index 6e31de9b3d68..3610a49304c6 100644
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -372,8 +372,6 @@ int security_sb_remount(struct super_block *sb, void *mnt_opts);
int security_sb_kern_mount(const struct super_block *sb);
int security_sb_show_options(struct seq_file *m, struct super_block *sb);
int security_sb_statfs(struct dentry *dentry);
-int security_sb_mount(const char *dev_name, const struct path *path,
- const char *type, unsigned long flags, void *data);
int security_sb_umount(struct vfsmount *mnt, int flags);
int security_sb_pivotroot(const struct path *old_path, const struct path *new_path);
int security_sb_set_mnt_opts(struct super_block *sb,
@@ -384,7 +382,6 @@ int security_sb_clone_mnt_opts(const struct super_block *oldsb,
struct super_block *newsb,
unsigned long kern_flags,
unsigned long *set_kern_flags);
-int security_move_mount(const struct path *from_path, const struct path *to_path);
int security_mount_bind(const struct path *from, const struct path *to,
bool recurse);
int security_mount_new(struct fs_context *fc, const struct path *mp,
@@ -818,13 +815,6 @@ static inline int security_sb_statfs(struct dentry *dentry)
return 0;
}
-static inline int security_sb_mount(const char *dev_name, const struct path *path,
- const char *type, unsigned long flags,
- void *data)
-{
- return 0;
-}
-
static inline int security_sb_umount(struct vfsmount *mnt, int flags)
{
return 0;
@@ -852,12 +842,6 @@ static inline int security_sb_clone_mnt_opts(const struct super_block *oldsb,
return 0;
}
-static inline int security_move_mount(const struct path *from_path,
- const struct path *to_path)
-{
- return 0;
-}
-
static inline int security_mount_bind(const struct path *from,
const struct path *to, bool recurse)
{
diff --git a/kernel/bpf/bpf_lsm.c b/kernel/bpf/bpf_lsm.c
index 65235d70ee23..3e61c54f9b48 100644
--- a/kernel/bpf/bpf_lsm.c
+++ b/kernel/bpf/bpf_lsm.c
@@ -350,7 +350,6 @@ BTF_ID(func, bpf_lsm_release_secctx)
BTF_ID(func, bpf_lsm_sb_alloc_security)
BTF_ID(func, bpf_lsm_sb_eat_lsm_opts)
BTF_ID(func, bpf_lsm_sb_kern_mount)
-BTF_ID(func, bpf_lsm_sb_mount)
BTF_ID(func, bpf_lsm_sb_remount)
BTF_ID(func, bpf_lsm_sb_set_mnt_opts)
BTF_ID(func, bpf_lsm_sb_show_options)
@@ -383,7 +382,6 @@ BTF_ID(func, bpf_lsm_task_prctl)
BTF_ID(func, bpf_lsm_task_setscheduler)
BTF_ID(func, bpf_lsm_task_to_inode)
BTF_ID(func, bpf_lsm_userns_create)
-BTF_ID(func, bpf_lsm_move_mount)
BTF_ID(func, bpf_lsm_mount_bind)
BTF_ID(func, bpf_lsm_mount_new)
BTF_ID(func, bpf_lsm_mount_remount)
diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c
index 7fe774535992..13a8049b1b59 100644
--- a/security/apparmor/lsm.c
+++ b/security/apparmor/lsm.c
@@ -1713,7 +1713,6 @@ static struct security_hook_list apparmor_hooks[] __ro_after_init = {
LSM_HOOK_INIT(capget, apparmor_capget),
LSM_HOOK_INIT(capable, apparmor_capable),
- LSM_HOOK_INIT(move_mount, apparmor_move_mount),
LSM_HOOK_INIT(mount_bind, apparmor_mount_bind),
LSM_HOOK_INIT(mount_new, apparmor_mount_new),
LSM_HOOK_INIT(mount_remount, apparmor_mount_remount),
diff --git a/security/landlock/fs.c b/security/landlock/fs.c
index 6e810550efcb..5f723a70baa4 100644
--- a/security/landlock/fs.c
+++ b/security/landlock/fs.c
@@ -1857,7 +1857,6 @@ static struct security_hook_list landlock_hooks[] __ro_after_init = {
LSM_HOOK_INIT(mount_reconfigure, hook_mount_reconfigure),
LSM_HOOK_INIT(mount_change_type, hook_mount_change_type),
LSM_HOOK_INIT(mount_move, hook_move_mount),
- LSM_HOOK_INIT(move_mount, hook_move_mount),
LSM_HOOK_INIT(sb_umount, hook_sb_umount),
LSM_HOOK_INIT(sb_remount, hook_sb_remount),
LSM_HOOK_INIT(sb_pivotroot, hook_sb_pivotroot),
diff --git a/security/security.c b/security/security.c
index 356ef228d5de..af95868af34d 100644
--- a/security/security.c
+++ b/security/security.c
@@ -1039,29 +1039,6 @@ int security_sb_statfs(struct dentry *dentry)
return call_int_hook(sb_statfs, dentry);
}
-/**
- * security_sb_mount() - Check permission for mounting a filesystem
- * @dev_name: filesystem backing device
- * @path: mount point
- * @type: filesystem type
- * @flags: mount flags
- * @data: filesystem specific data
- *
- * Check permission before an object specified by @dev_name is mounted on the
- * mount point named by @nd. For an ordinary mount, @dev_name identifies a
- * device if the file system type requires a device. For a remount
- * (@flags & MS_REMOUNT), @dev_name is irrelevant. For a loopback/bind mount
- * (@flags & MS_BIND), @dev_name identifies the pathname of the object being
- * mounted.
- *
- * Return: Returns 0 if permission is granted.
- */
-int security_sb_mount(const char *dev_name, const struct path *path,
- const char *type, unsigned long flags, void *data)
-{
- return call_int_hook(sb_mount, dev_name, path, type, flags, data);
-}
-
/**
* security_sb_umount() - Check permission for unmounting a filesystem
* @mnt: mounted filesystem
@@ -1141,21 +1118,6 @@ int security_sb_clone_mnt_opts(const struct super_block *oldsb,
}
EXPORT_SYMBOL(security_sb_clone_mnt_opts);
-/**
- * security_move_mount() - Check permissions for moving a mount
- * @from_path: source mount point
- * @to_path: destination mount point
- *
- * Check permission before a mount is moved.
- *
- * Return: Returns 0 if permission is granted.
- */
-int security_move_mount(const struct path *from_path,
- const struct path *to_path)
-{
- return call_int_hook(move_mount, from_path, to_path);
-}
-
/**
* security_mount_bind() - Check permissions for a bind mount
* @from: source path
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index 415b5541ab9e..446e9e242134 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -7477,8 +7477,6 @@ static struct security_hook_list selinux_hooks[] __ro_after_init = {
LSM_HOOK_INIT(sb_set_mnt_opts, selinux_set_mnt_opts),
LSM_HOOK_INIT(sb_clone_mnt_opts, selinux_sb_clone_mnt_opts),
- LSM_HOOK_INIT(move_mount, selinux_move_mount),
-
LSM_HOOK_INIT(dentry_init_security, selinux_dentry_init_security),
LSM_HOOK_INIT(dentry_create_files_as, selinux_dentry_create_files_as),
--
2.52.0
^ permalink raw reply related [flat|nested] 21+ messages in thread
* Re: [PATCH 6/7] tomoyo: Convert from sb_mount to granular mount hooks
2026-03-18 18:43 ` [PATCH 6/7] tomoyo: " Song Liu
@ 2026-03-21 12:54 ` Tetsuo Handa
2026-03-22 1:06 ` Song Liu
0 siblings, 1 reply; 21+ messages in thread
From: Tetsuo Handa @ 2026-03-21 12:54 UTC (permalink / raw)
To: Song Liu, viro
Cc: paul, jmorris, serge, brauner, jack, john.johansen,
stephen.smalley.work, omosnace, mic, gnoack, takedakn, herton,
kernel-team, selinux, apparmor, linux-fsdevel,
linux-security-module
On 2026/03/19 3:43, Song Liu wrote:
> Replace tomoyo_sb_mount() with granular mount hooks. Each hook
> reconstructs the MS_* flags expected by tomoyo_mount_permission()
> using the original flags parameter where available.
>
> Key changes:
> - mount_bind: passes the pre-resolved source path to
> tomoyo_mount_acl() via a new dev_path parameter, instead of
> re-resolving dev_name via kern_path(). This eliminates a TOCTOU
> vulnerability.
> - mount_new, mount_remount, mount_reconfigure: use the original
> mount(2) flags for policy matching.
> - mount_move: passes pre-resolved paths for both source and
> destination.
> - mount_change_type: passes raw ms_flags directly.
>
> Also removes the unused data_page parameter from
> tomoyo_mount_permission().
>
> Code generated with the assistance of Claude, reviewed by human.
>
> Signed-off-by: Song Liu <song@kernel.org>
Basically OK. One question to Al Viro.
Do you still refuse the idea of resolving dev_path argument before calling LSM hooks
(the proposal you NAKed at https://lkml.kernel.org/r/20250709102410.GU1880847@ZenIV )
despite this series removes security_sb_mount() and security_move_mount() hooks?
> diff --git a/security/tomoyo/mount.c b/security/tomoyo/mount.c
> index 322dfd188ada..82ffe7d02814 100644
> --- a/security/tomoyo/mount.c
> +++ b/security/tomoyo/mount.c
> @@ -70,6 +70,7 @@ static bool tomoyo_check_mount_acl(struct tomoyo_request_info *r,
> * @dir: Pointer to "struct path".
> * @type: Name of filesystem type.
> * @flags: Mount options.
> + * @dev_path: Pre-resolved device/source path. Maybe NULL.
I guess that we can avoid passing maybe-NULL dev_name if Al Viro can accept
resolving maybe-NULL dev_path argument before calling LSM hooks.
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH 6/7] tomoyo: Convert from sb_mount to granular mount hooks
2026-03-21 12:54 ` Tetsuo Handa
@ 2026-03-22 1:06 ` Song Liu
2026-03-22 10:46 ` Tetsuo Handa
0 siblings, 1 reply; 21+ messages in thread
From: Song Liu @ 2026-03-22 1:06 UTC (permalink / raw)
To: Tetsuo Handa
Cc: Song Liu, viro@zeniv.linux.org.uk, paul@paul-moore.com,
jmorris@namei.org, serge@hallyn.com, brauner@kernel.org,
jack@suse.cz, john.johansen@canonical.com,
stephen.smalley.work@gmail.com, omosnace@redhat.com,
mic@digikod.net, gnoack@google.com, takedakn@nttdata.co.jp,
herton@canonical.com, Kernel Team, selinux@vger.kernel.org,
apparmor@lists.ubuntu.com, linux-fsdevel@vger.kernel.org,
linux-security-module@vger.kernel.org
> On Mar 21, 2026, at 5:54 AM, Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp> wrote:
>
> On 2026/03/19 3:43, Song Liu wrote:
>> Replace tomoyo_sb_mount() with granular mount hooks. Each hook
>> reconstructs the MS_* flags expected by tomoyo_mount_permission()
>> using the original flags parameter where available.
>>
>> Key changes:
>> - mount_bind: passes the pre-resolved source path to
>> tomoyo_mount_acl() via a new dev_path parameter, instead of
>> re-resolving dev_name via kern_path(). This eliminates a TOCTOU
>> vulnerability.
>> - mount_new, mount_remount, mount_reconfigure: use the original
>> mount(2) flags for policy matching.
>> - mount_move: passes pre-resolved paths for both source and
>> destination.
>> - mount_change_type: passes raw ms_flags directly.
>>
>> Also removes the unused data_page parameter from
>> tomoyo_mount_permission().
>>
>> Code generated with the assistance of Claude, reviewed by human.
>>
>> Signed-off-by: Song Liu <song@kernel.org>
>
> Basically OK. One question to Al Viro.
>
> Do you still refuse the idea of resolving dev_path argument before calling LSM hooks
> (the proposal you NAKed at https://lkml.kernel.org/r/20250709102410.GU1880847@ZenIV )
> despite this series removes security_sb_mount() and security_move_mount() hooks?
>
>> diff --git a/security/tomoyo/mount.c b/security/tomoyo/mount.c
>> index 322dfd188ada..82ffe7d02814 100644
>> --- a/security/tomoyo/mount.c
>> +++ b/security/tomoyo/mount.c
>> @@ -70,6 +70,7 @@ static bool tomoyo_check_mount_acl(struct tomoyo_request_info *r,
>> * @dir: Pointer to "struct path".
>> * @type: Name of filesystem type.
>> * @flags: Mount options.
>> + * @dev_path: Pre-resolved device/source path. Maybe NULL.
>
> I guess that we can avoid passing maybe-NULL dev_name if Al Viro can accept
> resolving maybe-NULL dev_path argument before calling LSM hooks.
If I understand the code correctly, tomoyo records requested_dev_name for
new mount. namespace.c, OTOH, does NOT do kern_path() for new mount. This
is why we keep the maybe-NULL dev_name here. I personally think this is
the best approach without changing tomoyo behavior.
Thanks,
Song
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH 6/7] tomoyo: Convert from sb_mount to granular mount hooks
2026-03-22 1:06 ` Song Liu
@ 2026-03-22 10:46 ` Tetsuo Handa
2026-03-23 3:32 ` Song Liu
0 siblings, 1 reply; 21+ messages in thread
From: Tetsuo Handa @ 2026-03-22 10:46 UTC (permalink / raw)
To: Song Liu, viro@zeniv.linux.org.uk
Cc: Song Liu, paul@paul-moore.com, jmorris@namei.org,
serge@hallyn.com, brauner@kernel.org, jack@suse.cz,
john.johansen@canonical.com, stephen.smalley.work@gmail.com,
omosnace@redhat.com, mic@digikod.net, gnoack@google.com,
takedakn@nttdata.co.jp, herton@canonical.com, Kernel Team,
selinux@vger.kernel.org, apparmor@lists.ubuntu.com,
linux-fsdevel@vger.kernel.org,
linux-security-module@vger.kernel.org
On 2026/03/22 10:06, Song Liu wrote:
>>> @@ -70,6 +70,7 @@ static bool tomoyo_check_mount_acl(struct tomoyo_request_info *r,
>>> * @dir: Pointer to "struct path".
>>> * @type: Name of filesystem type.
>>> * @flags: Mount options.
>>> + * @dev_path: Pre-resolved device/source path. Maybe NULL.
>>
>> I guess that we can avoid passing maybe-NULL dev_name if Al Viro can accept
>> resolving maybe-NULL dev_path argument before calling LSM hooks.
>
> If I understand the code correctly, tomoyo records requested_dev_name for
> new mount. namespace.c, OTOH, does NOT do kern_path() for new mount. This
> is why we keep the maybe-NULL dev_name here. I personally think this is
> the best approach without changing tomoyo behavior.
Well, namespace.c does kern_path() for new mount, but it happens a bit later after
security_mount_new() was called.
do_new_mount_fc() => fc_mount() => vfs_get_tree() => fc->ops->get_tree() == e.g. ext4_get_tree()
=> get_tree_bdev() => get_tree_bdev_flags() => lookup_bdev() => kern_path()
@@ -3835,6 +3855,9 @@ static int do_new_mount(const struct path *path, const char *fstype,
err = parse_monolithic_mount_data(fc, data);
if (!err && !mount_capable(fc))
err = -EPERM;
+
+ if (!err)
+ err = security_mount_new(fc, path, mnt_flags, flags, data);
if (!err)
err = do_new_mount_fc(fc, path, mnt_flags);
Since not all filesystems need to resolve dev_name argument, conversion from dev_name
to "struct path" is up to individual filesystem. If we can use a flag like FS_REQUIRES_DEV
that tells whether that filesystem will resolve dev_name argument, I think we can resolve
the dev_name argument before security_mount_new() is called (and can avoid TOCTOU).
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH 6/7] tomoyo: Convert from sb_mount to granular mount hooks
2026-03-22 10:46 ` Tetsuo Handa
@ 2026-03-23 3:32 ` Song Liu
2026-03-23 10:16 ` Christian Brauner
0 siblings, 1 reply; 21+ messages in thread
From: Song Liu @ 2026-03-23 3:32 UTC (permalink / raw)
To: Tetsuo Handa
Cc: viro@zeniv.linux.org.uk, Song Liu, paul@paul-moore.com,
jmorris@namei.org, serge@hallyn.com, brauner@kernel.org,
jack@suse.cz, john.johansen@canonical.com,
stephen.smalley.work@gmail.com, omosnace@redhat.com,
mic@digikod.net, gnoack@google.com, takedakn@nttdata.co.jp,
herton@canonical.com, Kernel Team, selinux@vger.kernel.org,
apparmor@lists.ubuntu.com, linux-fsdevel@vger.kernel.org,
linux-security-module@vger.kernel.org
> On Mar 22, 2026, at 3:46 AM, Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp> wrote:
>
> On 2026/03/22 10:06, Song Liu wrote:
>>>> @@ -70,6 +70,7 @@ static bool tomoyo_check_mount_acl(struct tomoyo_request_info *r,
>>>> * @dir: Pointer to "struct path".
>>>> * @type: Name of filesystem type.
>>>> * @flags: Mount options.
>>>> + * @dev_path: Pre-resolved device/source path. Maybe NULL.
>>>
>>> I guess that we can avoid passing maybe-NULL dev_name if Al Viro can accept
>>> resolving maybe-NULL dev_path argument before calling LSM hooks.
>>
>> If I understand the code correctly, tomoyo records requested_dev_name for
>> new mount. namespace.c, OTOH, does NOT do kern_path() for new mount. This
>> is why we keep the maybe-NULL dev_name here. I personally think this is
>> the best approach without changing tomoyo behavior.
>
> Well, namespace.c does kern_path() for new mount, but it happens a bit later after
> security_mount_new() was called.
>
> do_new_mount_fc() => fc_mount() => vfs_get_tree() => fc->ops->get_tree() == e.g. ext4_get_tree()
> => get_tree_bdev() => get_tree_bdev_flags() => lookup_bdev() => kern_path()
>
> @@ -3835,6 +3855,9 @@ static int do_new_mount(const struct path *path, const char *fstype,
> err = parse_monolithic_mount_data(fc, data);
> if (!err && !mount_capable(fc))
> err = -EPERM;
> +
> + if (!err)
> + err = security_mount_new(fc, path, mnt_flags, flags, data);
> if (!err)
> err = do_new_mount_fc(fc, path, mnt_flags);
>
>
> Since not all filesystems need to resolve dev_name argument, conversion from dev_name
> to "struct path" is up to individual filesystem. If we can use a flag like FS_REQUIRES_DEV
> that tells whether that filesystem will resolve dev_name argument, I think we can resolve
> the dev_name argument before security_mount_new() is called (and can avoid TOCTOU).
I guess we can add dev_path to fs_context?
Thanks,
Song
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH 6/7] tomoyo: Convert from sb_mount to granular mount hooks
2026-03-23 3:32 ` Song Liu
@ 2026-03-23 10:16 ` Christian Brauner
2026-03-23 10:32 ` Tetsuo Handa
0 siblings, 1 reply; 21+ messages in thread
From: Christian Brauner @ 2026-03-23 10:16 UTC (permalink / raw)
To: Song Liu
Cc: Tetsuo Handa, viro@zeniv.linux.org.uk, Song Liu,
paul@paul-moore.com, jmorris@namei.org, serge@hallyn.com,
jack@suse.cz, john.johansen@canonical.com,
stephen.smalley.work@gmail.com, omosnace@redhat.com,
mic@digikod.net, gnoack@google.com, takedakn@nttdata.co.jp,
herton@canonical.com, Kernel Team, selinux@vger.kernel.org,
apparmor@lists.ubuntu.com, linux-fsdevel@vger.kernel.org,
linux-security-module@vger.kernel.org
On Mon, Mar 23, 2026 at 03:32:14AM +0000, Song Liu wrote:
>
>
> > On Mar 22, 2026, at 3:46 AM, Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp> wrote:
> >
> > On 2026/03/22 10:06, Song Liu wrote:
> >>>> @@ -70,6 +70,7 @@ static bool tomoyo_check_mount_acl(struct tomoyo_request_info *r,
> >>>> * @dir: Pointer to "struct path".
> >>>> * @type: Name of filesystem type.
> >>>> * @flags: Mount options.
> >>>> + * @dev_path: Pre-resolved device/source path. Maybe NULL.
> >>>
> >>> I guess that we can avoid passing maybe-NULL dev_name if Al Viro can accept
> >>> resolving maybe-NULL dev_path argument before calling LSM hooks.
> >>
> >> If I understand the code correctly, tomoyo records requested_dev_name for
> >> new mount. namespace.c, OTOH, does NOT do kern_path() for new mount. This
> >> is why we keep the maybe-NULL dev_name here. I personally think this is
> >> the best approach without changing tomoyo behavior.
> >
> > Well, namespace.c does kern_path() for new mount, but it happens a bit later after
> > security_mount_new() was called.
> >
> > do_new_mount_fc() => fc_mount() => vfs_get_tree() => fc->ops->get_tree() == e.g. ext4_get_tree()
> > => get_tree_bdev() => get_tree_bdev_flags() => lookup_bdev() => kern_path()
> >
> > @@ -3835,6 +3855,9 @@ static int do_new_mount(const struct path *path, const char *fstype,
> > err = parse_monolithic_mount_data(fc, data);
> > if (!err && !mount_capable(fc))
> > err = -EPERM;
> > +
> > + if (!err)
> > + err = security_mount_new(fc, path, mnt_flags, flags, data);
> > if (!err)
> > err = do_new_mount_fc(fc, path, mnt_flags);
> >
> >
> > Since not all filesystems need to resolve dev_name argument, conversion from dev_name
> > to "struct path" is up to individual filesystem. If we can use a flag like FS_REQUIRES_DEV
> > that tells whether that filesystem will resolve dev_name argument, I think we can resolve
> > the dev_name argument before security_mount_new() is called (and can avoid TOCTOU).
>
> I guess we can add dev_path to fs_context?
No, when and how the path is resolved is entirely up to the individual
filesystem and we're not hoisting the block-based specific path lookup
up into the VFS while leaving the other stuff per-filesystem. And it's
not as trivial as you want it to be either.
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH 6/7] tomoyo: Convert from sb_mount to granular mount hooks
2026-03-23 10:16 ` Christian Brauner
@ 2026-03-23 10:32 ` Tetsuo Handa
2026-03-23 19:31 ` Song Liu
0 siblings, 1 reply; 21+ messages in thread
From: Tetsuo Handa @ 2026-03-23 10:32 UTC (permalink / raw)
To: Christian Brauner, Song Liu
Cc: viro@zeniv.linux.org.uk, Song Liu, paul@paul-moore.com,
jmorris@namei.org, serge@hallyn.com, jack@suse.cz,
john.johansen@canonical.com, stephen.smalley.work@gmail.com,
omosnace@redhat.com, mic@digikod.net, gnoack@google.com,
takedakn@nttdata.co.jp, herton@canonical.com, Kernel Team,
selinux@vger.kernel.org, apparmor@lists.ubuntu.com,
linux-fsdevel@vger.kernel.org,
linux-security-module@vger.kernel.org
On 2026/03/23 19:16, Christian Brauner wrote:
>>> Since not all filesystems need to resolve dev_name argument, conversion from dev_name
>>> to "struct path" is up to individual filesystem. If we can use a flag like FS_REQUIRES_DEV
>>> that tells whether that filesystem will resolve dev_name argument, I think we can resolve
>>> the dev_name argument before security_mount_new() is called (and can avoid TOCTOU).
>>
I was expecting that "struct file_system_type"->fs_flags containing FS_REQUIRES_DEV
is a sign that the dev_name argument is a pathname. But it seems that such assumption
no longer holds true. For example, cramfs started treating dev_name like "mtd$num"
as if /dev/mtdblock$num since 4.15. So, current TOMOYO logic causes mount request of
cramfs with dev_name like "mtd0" to fail.
>> I guess we can add dev_path to fs_context?
>
> No, when and how the path is resolved is entirely up to the individual
> filesystem and we're not hoisting the block-based specific path lookup
> up into the VFS while leaving the other stuff per-filesystem. And it's
> not as trivial as you want it to be either.
Then, how can LSM modules know that how the requested filesystem resolves
the dev_name argument, without embedding filesystem specific resolution
logic into individual LSM module?
I want some flag like FS_REQUIRES_DEV that tells individual LSM module
whether the dev_name argument should be interpreted as a pathname.
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH 6/7] tomoyo: Convert from sb_mount to granular mount hooks
2026-03-23 10:32 ` Tetsuo Handa
@ 2026-03-23 19:31 ` Song Liu
2026-03-24 6:12 ` Tetsuo Handa
0 siblings, 1 reply; 21+ messages in thread
From: Song Liu @ 2026-03-23 19:31 UTC (permalink / raw)
To: Tetsuo Handa
Cc: Christian Brauner, viro@zeniv.linux.org.uk, Song Liu,
paul@paul-moore.com, jmorris@namei.org, serge@hallyn.com,
jack@suse.cz, john.johansen@canonical.com,
stephen.smalley.work@gmail.com, omosnace@redhat.com,
mic@digikod.net, gnoack@google.com, takedakn@nttdata.co.jp,
herton@canonical.com, Kernel Team, selinux@vger.kernel.org,
apparmor@lists.ubuntu.com, linux-fsdevel@vger.kernel.org,
linux-security-module@vger.kernel.org
On Mon, Mar 23, 2026 at 3:33 AM Tetsuo Handa
<penguin-kernel@i-love.sakura.ne.jp> wrote:
>
> On 2026/03/23 19:16, Christian Brauner wrote:
> >>> Since not all filesystems need to resolve dev_name argument, conversion from dev_name
> >>> to "struct path" is up to individual filesystem. If we can use a flag like FS_REQUIRES_DEV
> >>> that tells whether that filesystem will resolve dev_name argument, I think we can resolve
> >>> the dev_name argument before security_mount_new() is called (and can avoid TOCTOU).
> >>
>
> I was expecting that "struct file_system_type"->fs_flags containing FS_REQUIRES_DEV
> is a sign that the dev_name argument is a pathname. But it seems that such assumption
> no longer holds true. For example, cramfs started treating dev_name like "mtd$num"
> as if /dev/mtdblock$num since 4.15. So, current TOMOYO logic causes mount request of
> cramfs with dev_name like "mtd0" to fail.
>
> >> I guess we can add dev_path to fs_context?
> >
> > No, when and how the path is resolved is entirely up to the individual
> > filesystem and we're not hoisting the block-based specific path lookup
> > up into the VFS while leaving the other stuff per-filesystem. And it's
> > not as trivial as you want it to be either.
>
> Then, how can LSM modules know that how the requested filesystem resolves
> the dev_name argument, without embedding filesystem specific resolution
> logic into individual LSM module?
IIUC, if an LSM cares about the dev_name of a new mount, it will have to look
into each individual filesystem. We can add a LSM hook for the filesystems to
call. But this will require changes to individual filesystem code. OTOH,
dev_name can probably bridge the gap as we change filesystems.
Would this work?
Thanks,
Song
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH 6/7] tomoyo: Convert from sb_mount to granular mount hooks
2026-03-23 19:31 ` Song Liu
@ 2026-03-24 6:12 ` Tetsuo Handa
2026-03-24 7:46 ` Song Liu
0 siblings, 1 reply; 21+ messages in thread
From: Tetsuo Handa @ 2026-03-24 6:12 UTC (permalink / raw)
To: Song Liu
Cc: Christian Brauner, viro@zeniv.linux.org.uk, Song Liu,
paul@paul-moore.com, jmorris@namei.org, serge@hallyn.com,
jack@suse.cz, john.johansen@canonical.com,
stephen.smalley.work@gmail.com, omosnace@redhat.com,
mic@digikod.net, gnoack@google.com, takedakn@nttdata.co.jp,
herton@canonical.com, Kernel Team, selinux@vger.kernel.org,
apparmor@lists.ubuntu.com, linux-fsdevel@vger.kernel.org,
linux-security-module@vger.kernel.org
On 2026/03/24 4:31, Song Liu wrote:
>> Then, how can LSM modules know that how the requested filesystem resolves
>> the dev_name argument, without embedding filesystem specific resolution
>> logic into individual LSM module?
>
> IIUC, if an LSM cares about the dev_name of a new mount, it will have to look
> into each individual filesystem. We can add a LSM hook for the filesystems to
> call. But this will require changes to individual filesystem code. OTOH,
> dev_name can probably bridge the gap as we change filesystems.
>
> Would this work?
I guess something like untested diff shown below would work.
block/bdev.c | 26 ++++++++++++++------------
fs/fs_context.c | 4 ++++
fs/namespace.c | 10 ++++++----
fs/super.c | 2 +-
include/linux/blkdev.h | 12 +++++++++++-
include/linux/fs_context.h | 1 +
security/tomoyo/mount.c | 26 ++------------------------
security/tomoyo/tomoyo.c | 2 +-
8 files changed, 40 insertions(+), 43 deletions(-)
diff --git a/block/bdev.c b/block/bdev.c
index ed022f8c48c7..35707a6144fa 100644
--- a/block/bdev.c
+++ b/block/bdev.c
@@ -1199,44 +1199,46 @@ void bdev_fput(struct file *bdev_file)
EXPORT_SYMBOL(bdev_fput);
/**
- * lookup_bdev() - Look up a struct block_device by name.
+ * lookup_bdev_path() - Look up a struct block_device by name.
* @pathname: Name of the block device in the filesystem.
* @dev: Pointer to the block device's dev_t, if found.
+ * @path: Pointer to the block device's path, if found.
*
- * Lookup the block device's dev_t at @pathname in the current
- * namespace if possible and return it in @dev.
+ * Lookup the block device's dev_t and path at @pathname in the current
+ * namespace if possible and return these in @dev and @path
*
* Context: May sleep.
* Return: 0 if succeeded, negative errno otherwise.
+ * Caller must call path_put(@path) if this function returned 0.
*/
-int lookup_bdev(const char *pathname, dev_t *dev)
+int lookup_bdev_path(const char *pathname, dev_t *dev, struct path *path)
{
struct inode *inode;
- struct path path;
int error;
if (!pathname || !*pathname)
return -EINVAL;
- error = kern_path(pathname, LOOKUP_FOLLOW, &path);
+ error = kern_path(pathname, LOOKUP_FOLLOW, path);
if (error)
return error;
- inode = d_backing_inode(path.dentry);
+ inode = d_backing_inode(path->dentry);
error = -ENOTBLK;
if (!S_ISBLK(inode->i_mode))
goto out_path_put;
error = -EACCES;
- if (!may_open_dev(&path))
+ if (!may_open_dev(path))
goto out_path_put;
-
*dev = inode->i_rdev;
- error = 0;
+ return 0;
out_path_put:
- path_put(&path);
+ path_put(path);
+ path->dentry = NULL;
+ path->mnt = NULL;
return error;
}
-EXPORT_SYMBOL(lookup_bdev);
+EXPORT_SYMBOL(lookup_bdev_path);
/**
* bdev_mark_dead - mark a block device as dead
diff --git a/fs/fs_context.c b/fs/fs_context.c
index a37b0a093505..e5294f48eb32 100644
--- a/fs/fs_context.c
+++ b/fs/fs_context.c
@@ -377,6 +377,8 @@ struct fs_context *vfs_dup_fs_context(struct fs_context *src_fc)
fc->fs_private = NULL;
fc->s_fs_info = NULL;
fc->source = NULL;
+ fc->source_path.dentry = NULL;
+ fc->source_path.mnt = NULL;
fc->security = NULL;
get_filesystem(fc->fs_type);
get_net(fc->net_ns);
@@ -504,6 +506,8 @@ void put_fs_context(struct fs_context *fc)
put_cred(fc->cred);
put_fc_log(fc);
put_filesystem(fc->fs_type);
+ if (fc->source_path.dentry)
+ path_put(&fc->source_path);
kfree(fc->source);
kfree(fc);
}
diff --git a/fs/namespace.c b/fs/namespace.c
index ba5baccdde67..621b8205a0af 100644
--- a/fs/namespace.c
+++ b/fs/namespace.c
@@ -3777,7 +3777,7 @@ static bool mount_too_revealing(const struct super_block *sb, int *new_mnt_flags
* be added to the namespace tree.
*/
static int do_new_mount_fc(struct fs_context *fc, const struct path *mountpoint,
- unsigned int mnt_flags)
+ unsigned int mnt_flags, void *data, unsigned long flags)
{
struct super_block *sb;
struct vfsmount *mnt __free(mntput) = fc_mount(fc);
@@ -3786,6 +3786,10 @@ static int do_new_mount_fc(struct fs_context *fc, const struct path *mountpoint,
if (IS_ERR(mnt))
return PTR_ERR(mnt);
+ error = security_mount_new(fc, mountpoint, mnt_flags, flags, data);
+ if (error)
+ return error;
+
sb = fc->root->d_sb;
error = security_sb_kern_mount(sb);
if (unlikely(error))
@@ -3857,9 +3861,7 @@ static int do_new_mount(const struct path *path, const char *fstype,
err = -EPERM;
if (!err)
- err = security_mount_new(fc, path, mnt_flags, flags, data);
- if (!err)
- err = do_new_mount_fc(fc, path, mnt_flags);
+ err = do_new_mount_fc(fc, path, mnt_flags, data, flags);
put_fs_context(fc);
return err;
diff --git a/fs/super.c b/fs/super.c
index 378e81efe643..588f207f26ae 100644
--- a/fs/super.c
+++ b/fs/super.c
@@ -1670,7 +1670,7 @@ int get_tree_bdev_flags(struct fs_context *fc,
if (!fc->source)
return invalf(fc, "No source specified");
- error = lookup_bdev(fc->source, &dev);
+ error = lookup_bdev_path(fc->source, &dev, &fc->source_path);
if (error) {
if (!(flags & GET_TREE_BDEV_QUIET_LOOKUP))
errorf(fc, "%s: Can't lookup blockdev", fc->source);
diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h
index d463b9b5a0a5..c38d538f2a07 100644
--- a/include/linux/blkdev.h
+++ b/include/linux/blkdev.h
@@ -1723,7 +1723,17 @@ static inline void bio_end_io_acct(struct bio *bio, unsigned long start_time)
int bdev_validate_blocksize(struct block_device *bdev, int block_size);
int set_blocksize(struct file *file, int size);
-int lookup_bdev(const char *pathname, dev_t *dev);
+int lookup_bdev_path(const char *pathname, dev_t *dev, struct path *path);
+static inline int lookup_bdev(const char *pathname, dev_t *dev)
+{
+ struct path path = {};
+ int ret = lookup_bdev_path(pathname, dev, &path);
+
+ if (!ret)
+ path_put(&path);
+ return ret;
+}
+
void blkdev_show(struct seq_file *seqf, off_t offset);
diff --git a/include/linux/fs_context.h b/include/linux/fs_context.h
index 0d6c8a6d7be2..0dfa6b6fc256 100644
--- a/include/linux/fs_context.h
+++ b/include/linux/fs_context.h
@@ -99,6 +99,7 @@ struct fs_context {
const struct cred *cred; /* The mounter's credentials */
struct p_log log; /* Logging buffer */
const char *source; /* The source name (eg. dev path) */
+ struct path source_path; /* Fields are NULL unless resolved from the source name. */
void *security; /* LSM options */
void *s_fs_info; /* Proposed s_fs_info */
unsigned int sb_flags; /* Proposed superblock flags (SB_*) */
diff --git a/security/tomoyo/mount.c b/security/tomoyo/mount.c
index 82ffe7d02814..3a384b698557 100644
--- a/security/tomoyo/mount.c
+++ b/security/tomoyo/mount.c
@@ -84,7 +84,6 @@ static int tomoyo_mount_acl(struct tomoyo_request_info *r,
__must_hold_shared(&tomoyo_ss)
{
struct tomoyo_obj_info obj = { };
- struct file_system_type *fstype = NULL;
const char *requested_type = NULL;
const char *requested_dir_name = NULL;
const char *requested_dev_name = NULL;
@@ -124,32 +123,16 @@ static int tomoyo_mount_acl(struct tomoyo_request_info *r,
} else if (type == tomoyo_mounts[TOMOYO_MOUNT_BIND] ||
type == tomoyo_mounts[TOMOYO_MOUNT_MOVE]) {
need_dev = -1; /* dev_name is a directory */
- } else {
- fstype = get_fs_type(type);
- if (!fstype) {
- error = -ENODEV;
- goto out;
- }
- if (fstype->fs_flags & FS_REQUIRES_DEV)
- /* dev_name is a block device file. */
- need_dev = 1;
+ } else if (dev_path) {
+ need_dev = 1; /* dev_name is a block device file. */
}
if (need_dev) {
if (dev_path) {
/* Use pre-resolved path to avoid TOCTOU issues. */
obj.path1 = *dev_path;
- path_get(&obj.path1);
} else if (!dev_name) {
error = -ENOENT;
goto out;
- } else {
- struct path path;
-
- if (kern_path(dev_name, LOOKUP_FOLLOW, &path)) {
- error = -ENOENT;
- goto out;
- }
- obj.path1 = path;
}
requested_dev_name = tomoyo_realpath_from_path(&obj.path1);
if (!requested_dev_name) {
@@ -181,12 +164,7 @@ static int tomoyo_mount_acl(struct tomoyo_request_info *r,
out:
kfree(requested_dev_name);
kfree(requested_dir_name);
- if (fstype)
- put_filesystem(fstype);
kfree(requested_type);
- /* Drop refcount obtained by kern_path() or path_get(). */
- if (obj.path1.dentry)
- path_put(&obj.path1);
return error;
}
diff --git a/security/tomoyo/tomoyo.c b/security/tomoyo/tomoyo.c
index ac84e1f03d5e..6235e527cc20 100644
--- a/security/tomoyo/tomoyo.c
+++ b/security/tomoyo/tomoyo.c
@@ -413,7 +413,7 @@ static int tomoyo_mount_new(struct fs_context *fc, const struct path *mp,
{
/* Use original MS_* flags for policy matching */
return tomoyo_mount_permission(fc->source, mp, fc->fs_type->name,
- flags, NULL);
+ flags, &fc->source_path);
}
static int tomoyo_mount_remount(struct fs_context *fc, const struct path *mp,
^ permalink raw reply related [flat|nested] 21+ messages in thread
* Re: [PATCH 6/7] tomoyo: Convert from sb_mount to granular mount hooks
2026-03-24 6:12 ` Tetsuo Handa
@ 2026-03-24 7:46 ` Song Liu
2026-03-24 9:58 ` Tetsuo Handa
0 siblings, 1 reply; 21+ messages in thread
From: Song Liu @ 2026-03-24 7:46 UTC (permalink / raw)
To: Tetsuo Handa
Cc: Song Liu, Christian Brauner, viro@zeniv.linux.org.uk,
paul@paul-moore.com, jmorris@namei.org, serge@hallyn.com,
jack@suse.cz, john.johansen@canonical.com,
stephen.smalley.work@gmail.com, omosnace@redhat.com,
mic@digikod.net, gnoack@google.com, takedakn@nttdata.co.jp,
herton@canonical.com, Kernel Team, selinux@vger.kernel.org,
apparmor@lists.ubuntu.com, linux-fsdevel@vger.kernel.org,
linux-security-module@vger.kernel.org
On Mon, Mar 23, 2026 at 11:12 PM Tetsuo Handa
<penguin-kernel@i-love.sakura.ne.jp> wrote:
>
> On 2026/03/24 4:31, Song Liu wrote:
> >> Then, how can LSM modules know that how the requested filesystem resolves
> >> the dev_name argument, without embedding filesystem specific resolution
> >> logic into individual LSM module?
> >
> > IIUC, if an LSM cares about the dev_name of a new mount, it will have to look
> > into each individual filesystem. We can add a LSM hook for the filesystems to
> > call. But this will require changes to individual filesystem code. OTOH,
> > dev_name can probably bridge the gap as we change filesystems.
> >
> > Would this work?
>
> I guess something like untested diff shown below would work.
I think this doesn't work with erofs on file (requires
CONFIG_EROFS_FS_BACKED_BY_FILE). erofs may not be the
only one that has this problem.
Thanks,
Song
>
> block/bdev.c | 26 ++++++++++++++------------
> fs/fs_context.c | 4 ++++
> fs/namespace.c | 10 ++++++----
> fs/super.c | 2 +-
> include/linux/blkdev.h | 12 +++++++++++-
> include/linux/fs_context.h | 1 +
> security/tomoyo/mount.c | 26 ++------------------------
> security/tomoyo/tomoyo.c | 2 +-
> 8 files changed, 40 insertions(+), 43 deletions(-)
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH 6/7] tomoyo: Convert from sb_mount to granular mount hooks
2026-03-24 7:46 ` Song Liu
@ 2026-03-24 9:58 ` Tetsuo Handa
2026-03-24 19:03 ` Song Liu
0 siblings, 1 reply; 21+ messages in thread
From: Tetsuo Handa @ 2026-03-24 9:58 UTC (permalink / raw)
To: Song Liu
Cc: Song Liu, Christian Brauner, viro@zeniv.linux.org.uk,
paul@paul-moore.com, jmorris@namei.org, serge@hallyn.com,
jack@suse.cz, john.johansen@canonical.com,
stephen.smalley.work@gmail.com, omosnace@redhat.com,
mic@digikod.net, gnoack@google.com, takedakn@nttdata.co.jp,
herton@canonical.com, Kernel Team, selinux@vger.kernel.org,
apparmor@lists.ubuntu.com, linux-fsdevel@vger.kernel.org,
linux-security-module@vger.kernel.org
On 2026/03/24 16:46, Song Liu wrote:
> On Mon, Mar 23, 2026 at 11:12 PM Tetsuo Handa
> <penguin-kernel@i-love.sakura.ne.jp> wrote:
>>
>> On 2026/03/24 4:31, Song Liu wrote:
>>>> Then, how can LSM modules know that how the requested filesystem resolves
>>>> the dev_name argument, without embedding filesystem specific resolution
>>>> logic into individual LSM module?
>>>
>>> IIUC, if an LSM cares about the dev_name of a new mount, it will have to look
>>> into each individual filesystem. We can add a LSM hook for the filesystems to
>>> call. But this will require changes to individual filesystem code. OTOH,
>>> dev_name can probably bridge the gap as we change filesystems.
>>>
>>> Would this work?
>>
>> I guess something like untested diff shown below would work.
>
> I think this doesn't work with erofs on file (requires
> CONFIG_EROFS_FS_BACKED_BY_FILE). erofs may not be the
> only one that has this problem.
This is incomplete but I think this is better than now because currently
mount() operation likely fails with -ENOENT if the requested filesystem
does not interpret fc->source as a pathname despite tomoyo_mount_acl()
always interprets fc->source as a pathname when FS_REQUIRES_DEV is set.
Also, mount() operation might by error succeed because tomoyo_mount_acl()
checks permission against unintended file when e.g. "mtd0" by chance exists
in the current directory.
We could add filesystem-specific logic to e.g. CONFIG_EROFS_FS_BACKED_BY_FILE
case that copies the resolved "struct path" to fc->source_path. But even without
adding filesystem-specific logic, mount() operation will succeed because
tomoyo_mount_acl() can handle fc->source as a string rather than a canonicalized
pathname.
Being able to know whether tomoyo_mount_acl() should interpret dev_name as a
pathname (which is subjected to canonicalization, and also subjected to more
complicated permission checks based on e.g. inode's uid/gid) is appreciated.
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH 6/7] tomoyo: Convert from sb_mount to granular mount hooks
2026-03-24 9:58 ` Tetsuo Handa
@ 2026-03-24 19:03 ` Song Liu
2026-03-25 1:01 ` Tetsuo Handa
0 siblings, 1 reply; 21+ messages in thread
From: Song Liu @ 2026-03-24 19:03 UTC (permalink / raw)
To: Tetsuo Handa
Cc: Song Liu, Christian Brauner, viro@zeniv.linux.org.uk,
paul@paul-moore.com, jmorris@namei.org, serge@hallyn.com,
jack@suse.cz, john.johansen@canonical.com,
stephen.smalley.work@gmail.com, omosnace@redhat.com,
mic@digikod.net, gnoack@google.com, takedakn@nttdata.co.jp,
herton@canonical.com, Kernel Team, selinux@vger.kernel.org,
apparmor@lists.ubuntu.com, linux-fsdevel@vger.kernel.org,
linux-security-module@vger.kernel.org
On Tue, Mar 24, 2026 at 2:59 AM Tetsuo Handa
<penguin-kernel@i-love.sakura.ne.jp> wrote:
>
> On 2026/03/24 16:46, Song Liu wrote:
> > On Mon, Mar 23, 2026 at 11:12 PM Tetsuo Handa
> > <penguin-kernel@i-love.sakura.ne.jp> wrote:
> >>
> >> On 2026/03/24 4:31, Song Liu wrote:
> >>>> Then, how can LSM modules know that how the requested filesystem resolves
> >>>> the dev_name argument, without embedding filesystem specific resolution
> >>>> logic into individual LSM module?
> >>>
> >>> IIUC, if an LSM cares about the dev_name of a new mount, it will have to look
> >>> into each individual filesystem. We can add a LSM hook for the filesystems to
> >>> call. But this will require changes to individual filesystem code. OTOH,
> >>> dev_name can probably bridge the gap as we change filesystems.
> >>>
> >>> Would this work?
> >>
> >> I guess something like untested diff shown below would work.
> >
> > I think this doesn't work with erofs on file (requires
> > CONFIG_EROFS_FS_BACKED_BY_FILE). erofs may not be the
> > only one that has this problem.
>
> This is incomplete but I think this is better than now because currently
> mount() operation likely fails with -ENOENT if the requested filesystem
> does not interpret fc->source as a pathname despite tomoyo_mount_acl()
> always interprets fc->source as a pathname when FS_REQUIRES_DEV is set.
If I understand Christian correctly, the main challenge here is that
FS_REQUIRES_DEV doesn't imply fc->source is the path of a device.
Changing this assumption is a major change between VFS and many
filesystems.
I was thinking about something like:
diff --git i/fs/super.c w/fs/super.c
index 378e81efe643..91ce3003bc23 100644
--- i/fs/super.c
+++ w/fs/super.c
@@ -1676,6 +1676,9 @@ int get_tree_bdev_flags(struct fs_context *fc,
errorf(fc, "%s: Can't lookup blockdev", fc->source);
return error;
}
+ error = security_mount_dev(fc, dev);
+ if (error)
+ return error;
fc->sb_flags |= SB_NOSEC;
s = sget_dev(fc, dev);
if (IS_ERR(s))
This allows the LSMs to monitor the dev being mounted in a new mount.
If a filesystem doesn't use get_tree_bdev*(), we will need something else
to cover this specific filesystem. I am not sure whether this is acceptable
for VFS and LSM, specifically tomoyo and apparmor.
Also, before we go too deep into the hook for new mounts, can we focus
on this set, which will fix some existing TOCTOU issues?
Thanks,
Song
^ permalink raw reply related [flat|nested] 21+ messages in thread
* Re: [PATCH 6/7] tomoyo: Convert from sb_mount to granular mount hooks
2026-03-24 19:03 ` Song Liu
@ 2026-03-25 1:01 ` Tetsuo Handa
2026-03-25 1:35 ` Song Liu
0 siblings, 1 reply; 21+ messages in thread
From: Tetsuo Handa @ 2026-03-25 1:01 UTC (permalink / raw)
To: Song Liu
Cc: Song Liu, Christian Brauner, viro@zeniv.linux.org.uk,
paul@paul-moore.com, jmorris@namei.org, serge@hallyn.com,
jack@suse.cz, john.johansen@canonical.com,
stephen.smalley.work@gmail.com, omosnace@redhat.com,
mic@digikod.net, gnoack@google.com, takedakn@nttdata.co.jp,
herton@canonical.com, Kernel Team, selinux@vger.kernel.org,
apparmor@lists.ubuntu.com, linux-fsdevel@vger.kernel.org,
linux-security-module@vger.kernel.org
On 2026/03/25 4:03, Song Liu wrote:
> On Tue, Mar 24, 2026 at 2:59 AM Tetsuo Handa
> <penguin-kernel@i-love.sakura.ne.jp> wrote:
>>
>> On 2026/03/24 16:46, Song Liu wrote:
>>> On Mon, Mar 23, 2026 at 11:12 PM Tetsuo Handa
>>> <penguin-kernel@i-love.sakura.ne.jp> wrote:
>>>>
>>>> On 2026/03/24 4:31, Song Liu wrote:
>>>>>> Then, how can LSM modules know that how the requested filesystem resolves
>>>>>> the dev_name argument, without embedding filesystem specific resolution
>>>>>> logic into individual LSM module?
>>>>>
>>>>> IIUC, if an LSM cares about the dev_name of a new mount, it will have to look
>>>>> into each individual filesystem. We can add a LSM hook for the filesystems to
>>>>> call. But this will require changes to individual filesystem code. OTOH,
>>>>> dev_name can probably bridge the gap as we change filesystems.
>>>>>
>>>>> Would this work?
>>>>
>>>> I guess something like untested diff shown below would work.
>>>
>>> I think this doesn't work with erofs on file (requires
>>> CONFIG_EROFS_FS_BACKED_BY_FILE). erofs may not be the
>>> only one that has this problem.
>>
>> This is incomplete but I think this is better than now because currently
>> mount() operation likely fails with -ENOENT if the requested filesystem
>> does not interpret fc->source as a pathname despite tomoyo_mount_acl()
>> always interprets fc->source as a pathname when FS_REQUIRES_DEV is set.
>
> If I understand Christian correctly, the main challenge here is that
> FS_REQUIRES_DEV doesn't imply fc->source is the path of a device.
Correct. FS_REQUIRES_DEV no longer implies that fc->source is a pathname.
> Changing this assumption is a major change between VFS and many
> filesystems.
Wrong. I'm not trying to change this assumption. I'm trying to move LSM hook
to a location after fc->source was interpreted by individual filesystem.
>
> I was thinking about something like:
>
> diff --git i/fs/super.c w/fs/super.c
> index 378e81efe643..91ce3003bc23 100644
> --- i/fs/super.c
> +++ w/fs/super.c
> @@ -1676,6 +1676,9 @@ int get_tree_bdev_flags(struct fs_context *fc,
> errorf(fc, "%s: Can't lookup blockdev", fc->source);
> return error;
> }
> + error = security_mount_dev(fc, dev);
> + if (error)
> + return error;
> fc->sb_flags |= SB_NOSEC;
> s = sget_dev(fc, dev);
> if (IS_ERR(s))
>
> This allows the LSMs to monitor the dev being mounted in a new mount.
Splitting into multiple LSM hooks does not work, for TOMOYO wants to check
all parameters (parameters currently passed to security_mount_new() + the
"struct path" which was resolved by individual filesystem from fc->source
parameter) in one location.
I'm not sure how security_mount_new() is called for fsconfig() case.
Does https://man7.org/linux/man-pages/man2/fsconfig.2.html#EXAMPLES mean
TOMOYO cannot check all parameters until move_mount() is called?
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH 6/7] tomoyo: Convert from sb_mount to granular mount hooks
2026-03-25 1:01 ` Tetsuo Handa
@ 2026-03-25 1:35 ` Song Liu
0 siblings, 0 replies; 21+ messages in thread
From: Song Liu @ 2026-03-25 1:35 UTC (permalink / raw)
To: Tetsuo Handa
Cc: Song Liu, Christian Brauner, viro@zeniv.linux.org.uk,
paul@paul-moore.com, jmorris@namei.org, serge@hallyn.com,
jack@suse.cz, john.johansen@canonical.com,
stephen.smalley.work@gmail.com, omosnace@redhat.com,
mic@digikod.net, gnoack@google.com, takedakn@nttdata.co.jp,
herton@canonical.com, Kernel Team, selinux@vger.kernel.org,
apparmor@lists.ubuntu.com, linux-fsdevel@vger.kernel.org,
linux-security-module@vger.kernel.org
On Tue, Mar 24, 2026 at 6:02 PM Tetsuo Handa
<penguin-kernel@i-love.sakura.ne.jp> wrote:
[...]
> >>>> I guess something like untested diff shown below would work.
> >>>
> >>> I think this doesn't work with erofs on file (requires
> >>> CONFIG_EROFS_FS_BACKED_BY_FILE). erofs may not be the
> >>> only one that has this problem.
> >>
> >> This is incomplete but I think this is better than now because currently
> >> mount() operation likely fails with -ENOENT if the requested filesystem
> >> does not interpret fc->source as a pathname despite tomoyo_mount_acl()
> >> always interprets fc->source as a pathname when FS_REQUIRES_DEV is set.
> >
> > If I understand Christian correctly, the main challenge here is that
> > FS_REQUIRES_DEV doesn't imply fc->source is the path of a device.
>
> Correct. FS_REQUIRES_DEV no longer implies that fc->source is a pathname.
>
> > Changing this assumption is a major change between VFS and many
> > filesystems.
>
> Wrong. I'm not trying to change this assumption. I'm trying to move LSM hook
> to a location after fc->source was interpreted by individual filesystem.
OK, I can understand your point now. And I don't see a big red flag with it.
> >
> > I was thinking about something like:
> >
> > diff --git i/fs/super.c w/fs/super.c
> > index 378e81efe643..91ce3003bc23 100644
> > --- i/fs/super.c
> > +++ w/fs/super.c
> > @@ -1676,6 +1676,9 @@ int get_tree_bdev_flags(struct fs_context *fc,
> > errorf(fc, "%s: Can't lookup blockdev", fc->source);
> > return error;
> > }
> > + error = security_mount_dev(fc, dev);
> > + if (error)
> > + return error;
> > fc->sb_flags |= SB_NOSEC;
> > s = sget_dev(fc, dev);
> > if (IS_ERR(s))
> >
> > This allows the LSMs to monitor the dev being mounted in a new mount.
>
> Splitting into multiple LSM hooks does not work, for TOMOYO wants to check
> all parameters (parameters currently passed to security_mount_new() + the
> "struct path" which was resolved by individual filesystem from fc->source
> parameter) in one location.
>
> I'm not sure how security_mount_new() is called for fsconfig() case.
> Does https://man7.org/linux/man-pages/man2/fsconfig.2.html#EXAMPLES mean
> TOMOYO cannot check all parameters until move_mount() is called?
We need to add hooks for fsopen(), fsconfig(), etc. I have some basic code
for these. But I would rather we address this set first. After this, the other
hooks should be more straightforward.
Thanks,
Song
^ permalink raw reply [flat|nested] 21+ messages in thread
end of thread, other threads:[~2026-03-25 1:35 UTC | newest]
Thread overview: 21+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-03-18 18:43 [PATCH 0/7] lsm: Replace security_sb_mount with granular mount hooks Song Liu
2026-03-18 18:43 ` [PATCH 1/7] lsm: Add granular mount hooks to replace security_sb_mount Song Liu
2026-03-18 18:43 ` [PATCH 2/7] apparmor: Remove redundant MS_MGC_MSK stripping in apparmor_sb_mount Song Liu
2026-03-18 18:43 ` [PATCH 3/7] apparmor: Convert from sb_mount to granular mount hooks Song Liu
2026-03-18 18:43 ` [PATCH 4/7] selinux: " Song Liu
2026-03-18 18:43 ` [PATCH 5/7] landlock: " Song Liu
2026-03-18 18:43 ` [PATCH 6/7] tomoyo: " Song Liu
2026-03-21 12:54 ` Tetsuo Handa
2026-03-22 1:06 ` Song Liu
2026-03-22 10:46 ` Tetsuo Handa
2026-03-23 3:32 ` Song Liu
2026-03-23 10:16 ` Christian Brauner
2026-03-23 10:32 ` Tetsuo Handa
2026-03-23 19:31 ` Song Liu
2026-03-24 6:12 ` Tetsuo Handa
2026-03-24 7:46 ` Song Liu
2026-03-24 9:58 ` Tetsuo Handa
2026-03-24 19:03 ` Song Liu
2026-03-25 1:01 ` Tetsuo Handa
2026-03-25 1:35 ` Song Liu
2026-03-18 18:44 ` [PATCH 7/7] lsm: Remove security_sb_mount and security_move_mount Song Liu
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox