|
@@ -831,6 +831,10 @@ static struct mount *clone_mnt(struct mount *old, struct dentry *root,
|
|
|
if ((flag & CL_UNPRIVILEGED) && (mnt->mnt.mnt_flags & MNT_READONLY))
|
|
|
mnt->mnt.mnt_flags |= MNT_LOCK_READONLY;
|
|
|
|
|
|
+ /* Don't allow unprivileged users to reveal what is under a mount */
|
|
|
+ if ((flag & CL_UNPRIVILEGED) && list_empty(&old->mnt_expire))
|
|
|
+ mnt->mnt.mnt_flags |= MNT_LOCKED;
|
|
|
+
|
|
|
atomic_inc(&sb->s_active);
|
|
|
mnt->mnt.mnt_sb = sb;
|
|
|
mnt->mnt.mnt_root = dget(root);
|
|
@@ -1327,6 +1331,8 @@ SYSCALL_DEFINE2(umount, char __user *, name, int, flags)
|
|
|
goto dput_and_out;
|
|
|
if (!check_mnt(mnt))
|
|
|
goto dput_and_out;
|
|
|
+ if (mnt->mnt.mnt_flags & MNT_LOCKED)
|
|
|
+ goto dput_and_out;
|
|
|
|
|
|
retval = do_umount(mnt, flags);
|
|
|
dput_and_out:
|
|
@@ -1381,6 +1387,7 @@ struct mount *copy_tree(struct mount *mnt, struct dentry *dentry,
|
|
|
if (IS_ERR(q))
|
|
|
return q;
|
|
|
|
|
|
+ q->mnt.mnt_flags &= ~MNT_LOCKED;
|
|
|
q->mnt_mountpoint = mnt->mnt_mountpoint;
|
|
|
|
|
|
p = mnt;
|
|
@@ -1696,6 +1703,19 @@ static int do_change_type(struct path *path, int flag)
|
|
|
return err;
|
|
|
}
|
|
|
|
|
|
+static bool has_locked_children(struct mount *mnt, struct dentry *dentry)
|
|
|
+{
|
|
|
+ struct mount *child;
|
|
|
+ list_for_each_entry(child, &mnt->mnt_mounts, mnt_child) {
|
|
|
+ if (!is_subdir(child->mnt_mountpoint, dentry))
|
|
|
+ continue;
|
|
|
+
|
|
|
+ if (child->mnt.mnt_flags & MNT_LOCKED)
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ return false;
|
|
|
+}
|
|
|
+
|
|
|
/*
|
|
|
* do loopback mount.
|
|
|
*/
|
|
@@ -1731,6 +1751,9 @@ static int do_loopback(struct path *path, const char *old_name,
|
|
|
if (!check_mnt(parent) || !check_mnt(old))
|
|
|
goto out2;
|
|
|
|
|
|
+ if (!recurse && has_locked_children(old, old_path.dentry))
|
|
|
+ goto out2;
|
|
|
+
|
|
|
if (recurse)
|
|
|
mnt = copy_tree(old, old_path.dentry, 0);
|
|
|
else
|
|
@@ -1741,6 +1764,8 @@ static int do_loopback(struct path *path, const char *old_name,
|
|
|
goto out2;
|
|
|
}
|
|
|
|
|
|
+ mnt->mnt.mnt_flags &= ~MNT_LOCKED;
|
|
|
+
|
|
|
err = graft_tree(mnt, parent, mp);
|
|
|
if (err) {
|
|
|
br_write_lock(&vfsmount_lock);
|
|
@@ -1853,6 +1878,9 @@ static int do_move_mount(struct path *path, const char *old_name)
|
|
|
if (!check_mnt(p) || !check_mnt(old))
|
|
|
goto out1;
|
|
|
|
|
|
+ if (old->mnt.mnt_flags & MNT_LOCKED)
|
|
|
+ goto out1;
|
|
|
+
|
|
|
err = -EINVAL;
|
|
|
if (old_path.dentry != old_path.mnt->mnt_root)
|
|
|
goto out1;
|
|
@@ -2630,6 +2658,8 @@ SYSCALL_DEFINE2(pivot_root, const char __user *, new_root,
|
|
|
goto out4;
|
|
|
if (!check_mnt(root_mnt) || !check_mnt(new_mnt))
|
|
|
goto out4;
|
|
|
+ if (new_mnt->mnt.mnt_flags & MNT_LOCKED)
|
|
|
+ goto out4;
|
|
|
error = -ENOENT;
|
|
|
if (d_unlinked(new.dentry))
|
|
|
goto out4;
|
|
@@ -2653,6 +2683,10 @@ SYSCALL_DEFINE2(pivot_root, const char __user *, new_root,
|
|
|
br_write_lock(&vfsmount_lock);
|
|
|
detach_mnt(new_mnt, &parent_path);
|
|
|
detach_mnt(root_mnt, &root_parent);
|
|
|
+ if (root_mnt->mnt.mnt_flags & MNT_LOCKED) {
|
|
|
+ new_mnt->mnt.mnt_flags |= MNT_LOCKED;
|
|
|
+ root_mnt->mnt.mnt_flags &= ~MNT_LOCKED;
|
|
|
+ }
|
|
|
/* mount old root on put_old */
|
|
|
attach_mnt(root_mnt, old_mnt, old_mp);
|
|
|
/* mount new_root on / */
|