diff options
author | Eric Biggers <ebiggers@google.com> | 2019-06-29 13:27:44 -0700 |
---|---|---|
committer | Al Viro <viro@zeniv.linux.org.uk> | 2019-07-01 10:46:36 -0400 |
commit | 570d7a98e7d6d5d8706d94ffd2d40adeaa318332 (patch) | |
tree | 56980b6ac051d46174e62c1919ec14d02d25ad5d | |
parent | d728cf79164bb38e9628d15276e636539f857ef1 (diff) | |
download | linux-stable-570d7a98e7d6d5d8706d94ffd2d40adeaa318332.tar.gz linux-stable-570d7a98e7d6d5d8706d94ffd2d40adeaa318332.tar.bz2 linux-stable-570d7a98e7d6d5d8706d94ffd2d40adeaa318332.zip |
vfs: move_mount: reject moving kernel internal mounts
sys_move_mount() crashes by dereferencing the pointer MNT_NS_INTERNAL,
a.k.a. ERR_PTR(-EINVAL), if the old mount is specified by fd for a
kernel object with an internal mount, such as a pipe or memfd.
Fix it by checking for this case and returning -EINVAL.
[AV: what we want is is_mounted(); use that instead of making the
condition even more convoluted]
Reproducer:
#include <unistd.h>
#define __NR_move_mount 429
#define MOVE_MOUNT_F_EMPTY_PATH 0x00000004
int main()
{
int fds[2];
pipe(fds);
syscall(__NR_move_mount, fds[0], "", -1, "/", MOVE_MOUNT_F_EMPTY_PATH);
}
Reported-by: syzbot+6004acbaa1893ad013f0@syzkaller.appspotmail.com
Fixes: 2db154b3ea8e ("vfs: syscall: Add move_mount(2) to move mounts around")
Signed-off-by: Eric Biggers <ebiggers@google.com>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
-rw-r--r-- | fs/namespace.c | 7 |
1 files changed, 4 insertions, 3 deletions
diff --git a/fs/namespace.c b/fs/namespace.c index 7660c2749c96..6fbc9126367a 100644 --- a/fs/namespace.c +++ b/fs/namespace.c @@ -2596,11 +2596,12 @@ static int do_move_mount(struct path *old_path, struct path *new_path) if (!check_mnt(p)) goto out; - /* The thing moved should be either ours or completely unattached. */ - if (attached && !check_mnt(old)) + /* The thing moved must be mounted... */ + if (!is_mounted(&old->mnt)) goto out; - if (!attached && !(ns && is_anon_ns(ns))) + /* ... and either ours or the root of anon namespace */ + if (!(attached ? check_mnt(old) : is_anon_ns(ns))) goto out; if (old->mnt.mnt_flags & MNT_LOCKED) |