Skip to content

Commit b19a97d

Browse files
committed
Merge tag 'pull-fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs
Pull mount fixes from Al Viro: "Fixes for several recent mount-related regressions" * tag 'pull-fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs: change_mnt_propagation(): calculate propagation source only if we'll need it use uniform permission checks for all mount propagation changes propagate_umount(): only surviving overmounts should be reparented fix the softlockups in attach_recursive_mnt()
2 parents 7cca555 + fb924b7 commit b19a97d

File tree

2 files changed

+28
-23
lines changed

2 files changed

+28
-23
lines changed

fs/namespace.c

Lines changed: 22 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1197,10 +1197,7 @@ static void commit_tree(struct mount *mnt)
11971197

11981198
if (!mnt_ns_attached(mnt)) {
11991199
for (struct mount *m = mnt; m; m = next_mnt(m, mnt))
1200-
if (unlikely(mnt_ns_attached(m)))
1201-
m = skip_mnt_tree(m);
1202-
else
1203-
mnt_add_to_ns(n, m);
1200+
mnt_add_to_ns(n, m);
12041201
n->nr_mounts += n->pending_mounts;
12051202
n->pending_mounts = 0;
12061203
}
@@ -2704,6 +2701,7 @@ static int attach_recursive_mnt(struct mount *source_mnt,
27042701
lock_mnt_tree(child);
27052702
q = __lookup_mnt(&child->mnt_parent->mnt,
27062703
child->mnt_mountpoint);
2704+
commit_tree(child);
27072705
if (q) {
27082706
struct mountpoint *mp = root.mp;
27092707
struct mount *r = child;
@@ -2713,7 +2711,6 @@ static int attach_recursive_mnt(struct mount *source_mnt,
27132711
mp = shorter;
27142712
mnt_change_mountpoint(r, mp, q);
27152713
}
2716-
commit_tree(child);
27172714
}
27182715
unpin_mountpoint(&root);
27192716
unlock_mount_hash();
@@ -2862,6 +2859,19 @@ static int graft_tree(struct mount *mnt, struct mount *p, struct mountpoint *mp)
28622859
return attach_recursive_mnt(mnt, p, mp);
28632860
}
28642861

2862+
static int may_change_propagation(const struct mount *m)
2863+
{
2864+
struct mnt_namespace *ns = m->mnt_ns;
2865+
2866+
// it must be mounted in some namespace
2867+
if (IS_ERR_OR_NULL(ns)) // is_mounted()
2868+
return -EINVAL;
2869+
// and the caller must be admin in userns of that namespace
2870+
if (!ns_capable(ns->user_ns, CAP_SYS_ADMIN))
2871+
return -EPERM;
2872+
return 0;
2873+
}
2874+
28652875
/*
28662876
* Sanity check the flags to change_mnt_propagation.
28672877
*/
@@ -2898,10 +2908,10 @@ static int do_change_type(struct path *path, int ms_flags)
28982908
return -EINVAL;
28992909

29002910
namespace_lock();
2901-
if (!check_mnt(mnt)) {
2902-
err = -EINVAL;
2911+
err = may_change_propagation(mnt);
2912+
if (err)
29032913
goto out_unlock;
2904-
}
2914+
29052915
if (type == MS_SHARED) {
29062916
err = invent_group_ids(mnt, recurse);
29072917
if (err)
@@ -3347,18 +3357,11 @@ static int do_set_group(struct path *from_path, struct path *to_path)
33473357

33483358
namespace_lock();
33493359

3350-
err = -EINVAL;
3351-
/* To and From must be mounted */
3352-
if (!is_mounted(&from->mnt))
3353-
goto out;
3354-
if (!is_mounted(&to->mnt))
3355-
goto out;
3356-
3357-
err = -EPERM;
3358-
/* We should be allowed to modify mount namespaces of both mounts */
3359-
if (!ns_capable(from->mnt_ns->user_ns, CAP_SYS_ADMIN))
3360+
err = may_change_propagation(from);
3361+
if (err)
33603362
goto out;
3361-
if (!ns_capable(to->mnt_ns->user_ns, CAP_SYS_ADMIN))
3363+
err = may_change_propagation(to);
3364+
if (err)
33623365
goto out;
33633366

33643367
err = -EINVAL;

fs/pnode.c

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,8 @@ void change_mnt_propagation(struct mount *mnt, int type)
111111
return;
112112
}
113113
if (IS_MNT_SHARED(mnt)) {
114-
m = propagation_source(mnt);
114+
if (type == MS_SLAVE || !hlist_empty(&mnt->mnt_slave_list))
115+
m = propagation_source(mnt);
115116
if (list_empty(&mnt->mnt_share)) {
116117
mnt_release_group_id(mnt);
117118
} else {
@@ -637,10 +638,11 @@ void propagate_umount(struct list_head *set)
637638
}
638639

639640
// now to_umount consists of all acceptable candidates
640-
// deal with reparenting of remaining overmounts on those
641+
// deal with reparenting of surviving overmounts on those
641642
list_for_each_entry(m, &to_umount, mnt_list) {
642-
if (m->overmount)
643-
reparent(m->overmount);
643+
struct mount *over = m->overmount;
644+
if (over && !will_be_unmounted(over))
645+
reparent(over);
644646
}
645647

646648
// and fold them into the set

0 commit comments

Comments
 (0)