Linux-Security-Module Archive on lore.kernel.org
 help / color / Atom feed
From: Alan Jenkins <alan.christopher.jenkins@gmail.com>
To: David Howells <dhowells@redhat.com>, viro@zeniv.linux.org.uk
Cc: linux-api@vger.kernel.org, linux-fsdevel@vger.kernel.org,
	torvalds@linux-foundation.org, ebiederm@xmission.com,
	linux-security-module@vger.kernel.org
Subject: Re: [PATCH 02/10] vfs: syscall: Add move_mount(2) to move mounts around
Date: Wed, 20 Feb 2019 12:32:00 +0000
Message-ID: <57a6b419-1541-6f97-6810-d0d376580def@gmail.com> (raw)
In-Reply-To: <155059611887.17079.12991580316407924257.stgit@warthog.procyon.org.uk>

On 19/02/2019 17:08, David Howells wrote:
> Add a move_mount() system call that will move a mount from one place to
> another and, in the next commit, allow to attach an unattached mount tree.
>
> The new system call looks like the following:
>
> 	int move_mount(int from_dfd, const char *from_path,
> 		       int to_dfd, const char *to_path,
> 		       unsigned int flags);
>
> Signed-off-by: David Howells <dhowells@redhat.com>
> cc: linux-api@vger.kernel.org
> Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
> ---
>
>   arch/x86/entry/syscalls/syscall_32.tbl |    1
>   arch/x86/entry/syscalls/syscall_64.tbl |    1
>   fs/namespace.c                         |  126 ++++++++++++++++++++++++--------
>   include/linux/lsm_hooks.h              |    6 ++
>   include/linux/security.h               |    7 ++
>   include/linux/syscalls.h               |    3 +
>   include/uapi/linux/mount.h             |   11 +++
>   security/security.c                    |    5 +
>   8 files changed, 129 insertions(+), 31 deletions(-)

> diff --git a/fs/namespace.c b/fs/namespace.c
> index 112d46f26fc3..f10122028a11 100644
> --- a/fs/namespace.c
> +++ b/fs/namespace.c
> @@ -2537,72 +2537,81 @@ static inline int tree_contains_unbindable(struct mount *mnt)
>   	return 0;
>   }
>   
> -static int do_move_mount(struct path *path, const char *old_name)
> +static int do_move_mount(struct path *old_path, struct path *new_path)
>   {
> -	struct path old_path, parent_path;
> +	struct path parent_path = {.mnt = NULL, .dentry = NULL};
>   	struct mount *p;
>   	struct mount *old;
>   	struct mountpoint *mp;
>   	int err;
> -	if (!old_name || !*old_name)
> -		return -EINVAL;
> -	err = kern_path(old_name, LOOKUP_FOLLOW, &old_path);
> -	if (err)
> -		return err;
>   
> -	mp = lock_mount(path);
> -	err = PTR_ERR(mp);
> +	mp = lock_mount(new_path);
>   	if (IS_ERR(mp))
> -		goto out;
> +		return PTR_ERR(mp);
>   
> -	old = real_mount(old_path.mnt);
> -	p = real_mount(path->mnt);
> +	old = real_mount(old_path->mnt);
> +	p = real_mount(new_path->mnt);
>   
>   	err = -EINVAL;
>   	if (!check_mnt(p) || !check_mnt(old))
> -		goto out1;
> +		goto out;
>   
> -	if (old->mnt.mnt_flags & MNT_LOCKED)
> -		goto out1;
> +	if (!mnt_has_parent(old))
> +		goto out;
>   
> -	err = -EINVAL;
> -	if (old_path.dentry != old_path.mnt->mnt_root)
> -		goto out1;
> +	if (old->mnt.mnt_flags & MNT_LOCKED)
> +		goto out;
>   
> -	if (!mnt_has_parent(old))
> -		goto out1;
> +	if (old_path->dentry != old_path->mnt->mnt_root)
> +		goto out;
>   
> -	if (d_is_dir(path->dentry) !=
> -	      d_is_dir(old_path.dentry))
> -		goto out1;
> +	if (d_is_dir(new_path->dentry) !=
> +	    d_is_dir(old_path->dentry))
> +		goto out;
>   	/*
>   	 * Don't move a mount residing in a shared parent.
>   	 */
>   	if (IS_MNT_SHARED(old->mnt_parent))
> -		goto out1;
> +		goto out;
>   	/*
>   	 * Don't move a mount tree containing unbindable mounts to a destination
>   	 * mount which is shared.
>   	 */
>   	if (IS_MNT_SHARED(p) && tree_contains_unbindable(old))
> -		goto out1;
> +		goto out;
>   	err = -ELOOP;
>   	for (; mnt_has_parent(p); p = p->mnt_parent)
>   		if (p == old)
> -			goto out1;
> +			goto out;
>   
> -	err = attach_recursive_mnt(old, real_mount(path->mnt), mp, &parent_path);
> +	err = attach_recursive_mnt(old, real_mount(new_path->mnt), mp,
> +				   &parent_path);
>   	if (err)
> -		goto out1;
> +		goto out;
>   
>   	/* if the mount is moved, it should no longer be expire
>   	 * automatically */
>   	list_del_init(&old->mnt_expire);
> -out1:
> -	unlock_mount(mp);
>   out:
> +	unlock_mount(mp);
>   	if (!err)
>   		path_put(&parent_path);
> +	return err;
> +}
> +
> +static int do_move_mount_old(struct path *path, const char *old_name)
> +{
> +	struct path old_path;
> +	int err;
> +
> +	if (!old_name || !*old_name)
> +		return -EINVAL;
> +
> +	err = kern_path(old_name, LOOKUP_FOLLOW, &old_path);
> +	if (err)
> +		return err;
> +
> +	err = do_move_mount(&old_path, path);
>   	path_put(&old_path);
>   	return err;
>   }
> @@ -3050,7 +3059,7 @@ long do_mount(const char *dev_name, const char __user *dir_name,
>   	else if (flags & (MS_SHARED | MS_PRIVATE | MS_SLAVE | MS_UNBINDABLE))
>   		retval = do_change_type(&path, flags);
>   	else if (flags & MS_MOVE)
> -		retval = do_move_mount(&path, dev_name);
> +		retval = do_move_mount_old(&path, dev_name);
>   	else
>   		retval = do_new_mount(&path, type_page, sb_flags, mnt_flags,
>   				      dev_name, data_page);
> @@ -3278,6 +3287,61 @@ SYSCALL_DEFINE5(mount, char __user *, dev_name, char __user *, dir_name,
>   	return ksys_mount(dev_name, dir_name, type, flags, data);
>   }
>   
> +/*
> + * Move a mount from one place to another.
> + *
> + * Note the flags value is a combination of MOVE_MOUNT_* flags.
> + */
> +SYSCALL_DEFINE5(move_mount,
> +		int, from_dfd, const char *, from_pathname,
> +		int, to_dfd, const char *, to_pathname,
> +		unsigned int, flags)
> +{
> +	struct path from_path, to_path;
> +	unsigned int lflags;
> +	int ret = 0;
> +
> +	if (!may_mount())
> +		return -EPERM;
> +
> +	if (flags & ~MOVE_MOUNT__MASK)
> +		return -EINVAL;
> +
> +	/* If someone gives a pathname, they aren't permitted to move
> +	 * from an fd that requires unmount as we can't get at the flag
> +	 * to clear it afterwards.
> +	 */

Comment is incorrect.

* FMODE_NEED_UNMOUNT is never cleared.

* Technically I don't see anything preventing them giving a pathname, 
but it needs to be "." or equivalent.  Otherwise it will fail the 
"!attached" check in the next patch.

* The only argument I remember for preventing this, was that it might 
confuse users (not the kernel).  If you are allowed to move from a 
sub-mount, then in certain programming styles - like my shell script 
test cases - you might accidentally close the original file too early.  
Then you won't be able to do move_mount() from the tree, because the 
tree was unmounted ("dissolved") when you closed it.

I think the description in the previous patch, for open_tree(), makes it 
clear though. "The detached tree will be dissolved on the final close of 
obtained file".

If there is a good reason, I expect we can simply remove the "!attached" 
part of the check.  If the constraint is generating more confusion than 
the added flexibility, I think that would be a good reason :-).

> +	lflags = 0;
> +	if (flags & MOVE_MOUNT_F_SYMLINKS)	lflags |= LOOKUP_FOLLOW;
> +	if (flags & MOVE_MOUNT_F_AUTOMOUNTS)	lflags |= LOOKUP_AUTOMOUNT;
> +	if (flags & MOVE_MOUNT_F_EMPTY_PATH)	lflags |= LOOKUP_EMPTY;
> +
> +	ret = user_path_at(from_dfd, from_pathname, lflags, &from_path);
> +	if (ret < 0)
> +		return ret;
> +
> +	lflags = 0;
> +	if (flags & MOVE_MOUNT_T_SYMLINKS)	lflags |= LOOKUP_FOLLOW;
> +	if (flags & MOVE_MOUNT_T_AUTOMOUNTS)	lflags |= LOOKUP_AUTOMOUNT;
> +	if (flags & MOVE_MOUNT_T_EMPTY_PATH)	lflags |= LOOKUP_EMPTY;
> +
> +	ret = user_path_at(to_dfd, to_pathname, lflags, &to_path);
> +	if (ret < 0)
> +		goto out_from;
> +
> +	ret = security_move_mount(&from_path, &to_path);
> +	if (ret < 0)
> +		goto out_to;
> +
> +	ret = do_move_mount(&from_path, &to_path);
> +
> +out_to:
> +	path_put(&to_path);
> +out_from:
> +	path_put(&from_path);
> +	return ret;
> +}
> +
>   /*
>    * Return true if path is reachable from root
>    *
>

  reply index

Thread overview: 33+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2019-02-19 17:08 [PATCH 00/10] VFS: Provide new mount UAPI David Howells
2019-02-19 17:08 ` [PATCH 01/10] vfs: syscall: Add open_tree(2) to reference or clone a mount David Howells
2019-02-19 17:08 ` [PATCH 02/10] vfs: syscall: Add move_mount(2) to move mounts around David Howells
2019-02-20 12:32   ` Alan Jenkins [this message]
2019-02-20 12:41     ` Alan Jenkins
2019-02-20 16:23   ` Jann Horn
2019-07-08 12:02   ` Tetsuo Handa
2019-07-08 13:18     ` Al Viro
2019-07-08 17:12       ` Eric W. Biederman
2019-07-08 18:01         ` Al Viro
2019-07-08 18:13           ` Al Viro
2019-07-08 20:21           ` Al Viro
2019-07-09  0:13             ` Eric W. Biederman
2019-07-09 10:51               ` Tetsuo Handa
2019-07-22 10:12                 ` Tetsuo Handa
2019-07-23  4:16                   ` John Johansen
2019-07-23 13:45                     ` Tetsuo Handa
2019-08-06 10:43                       ` Tetsuo Handa
2019-08-09 15:44                         ` [PATCH] LSM: Disable move_mount() syscall when TOMOYO or AppArmor is enabled Tetsuo Handa
2019-08-22  3:51                         ` [RFC][PATCH] fix d_absolute_path() interplay with fsmount() Al Viro
2019-08-30 10:11                           ` Tetsuo Handa
2019-07-23 21:45             ` [PATCH 02/10] vfs: syscall: Add move_mount(2) to move mounts around James Morris
2019-07-23 23:30               ` Al Viro
2019-02-19 17:08 ` [PATCH 03/10] teach move_mount(2) to work with OPEN_TREE_CLONE David Howells
2019-02-20 18:59   ` Alan Jenkins
2019-02-26 17:45   ` Alan Jenkins
2019-02-19 17:08 ` [PATCH 04/10] Make anon_inodes unconditional David Howells
2019-02-19 17:09 ` [PATCH 05/10] vfs: syscall: Add fsopen() to prepare for superblock creation David Howells
2019-02-19 17:09 ` [PATCH 06/10] vfs: Implement logging through fs_context David Howells
2019-02-19 17:09 ` [PATCH 07/10] vfs: syscall: Add fsconfig() for configuring and managing a context David Howells
2019-02-19 17:09 ` [PATCH 08/10] vfs: syscall: Add fsmount() to create a mount for a superblock David Howells
2019-02-19 17:09 ` [PATCH 09/10] vfs: syscall: Add fspick() to select a superblock for reconfiguration David Howells
2019-02-19 17:09 ` [PATCH 10/10] vfs: Add a sample program for the new mount API David Howells

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=57a6b419-1541-6f97-6810-d0d376580def@gmail.com \
    --to=alan.christopher.jenkins@gmail.com \
    --cc=dhowells@redhat.com \
    --cc=ebiederm@xmission.com \
    --cc=linux-api@vger.kernel.org \
    --cc=linux-fsdevel@vger.kernel.org \
    --cc=linux-security-module@vger.kernel.org \
    --cc=torvalds@linux-foundation.org \
    --cc=viro@zeniv.linux.org.uk \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link

Linux-Security-Module Archive on lore.kernel.org

Archives are clonable:
	git clone --mirror https://lore.kernel.org/linux-security-module/0 linux-security-module/git/0.git

	# If you have public-inbox 1.1+ installed, you may
	# initialize and index your mirror using the following commands:
	public-inbox-init -V2 linux-security-module linux-security-module/ https://lore.kernel.org/linux-security-module \
		linux-security-module@vger.kernel.org
	public-inbox-index linux-security-module

Example config snippet for mirrors

Newsgroup available over NNTP:
	nntp://nntp.lore.kernel.org/org.kernel.vger.linux-security-module


AGPL code for this site: git clone https://public-inbox.org/public-inbox.git