On Thu, May 28, 2026 at 11:26:06AM -0700, Song Liu wrote:
> Replace the monolithic security_sb_mount() call in path_mount() and
> security_move_mount() in vfs_move_mount() with the new granular mount
> hooks:
> 
> - do_loopback(): call security_mount_bind()
> - do_new_mount(): call security_mount_new()
> - do_remount(): call security_mount_remount()
> - do_reconfigure_mnt(): call security_mount_reconfigure()
> - do_move_mount_old(): call security_mount_move()
> - do_change_type(): call security_mount_change_type()
> - vfs_move_mount(): replace security_move_mount() with
>   security_mount_move()
> 
> The new hooks are called at the individual operation level with
> appropriate context (resolved paths, fs_context), rather than at
> the top of path_mount() with raw string arguments.
> 
> Code generated with the assistance of Claude, reviewed by human.
> 
> Reviewed-by: Stephen Smalley <[email protected]>
> Tested-by: Stephen Smalley <[email protected]> # for selinux only
> Signed-off-by: Song Liu <[email protected]>
> ---
>  fs/namespace.c | 41 ++++++++++++++++++++++++++++++-----------
>  1 file changed, 30 insertions(+), 11 deletions(-)
> 
> diff --git a/fs/namespace.c b/fs/namespace.c
> index fe919abd2f01..43f22c5e2bf4 100644
> --- a/fs/namespace.c
> +++ b/fs/namespace.c
> @@ -2888,6 +2888,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);
> @@ -3006,6 +3010,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;

This again is racy as it is called outside of the namespace semaphore:

        err = security_mount_bind(&old_path, path, recurse);
        if (err)
                return err;

        if (mnt_ns_loop(old_path.dentry))
                return -EINVAL;

        LOCK_MOUNT(mp, path);
        if (IS_ERR(mp.parent))
                return PTR_ERR(mp.parent);

After LOCK_MOUNT @path might point to a completely different mount then
the one you performed your security checks on.

> +
>       if (mnt_ns_loop(old_path.dentry))
>               return -EINVAL;
>  
> @@ -3328,7 +3336,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);
> @@ -3343,6 +3352,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).
> @@ -3366,7 +3379,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;
> @@ -3393,6 +3406,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;
> @@ -3708,6 +3724,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;

Placement of this hook suffers from the same issue as the bind mount
hook. Here it's worse because the security layer isn't even informed
about MOVE_MOUNT_BENEATH which completely alters the mount relationship.

Reply via email to