From 1da9dcace3a598a152f99a03df353cf94deee195 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Tue, 14 Oct 2025 16:04:48 +0800 Subject: [PATCH 1/3] change_mnt_propagation() cleanups, step 1 mainline inclusion from mainline-v6.17-rc1 commit d5f15047f13b86b74d1ac4f39036ccae2078c492 category: bugfix bugzilla: https://gitee.com/openeuler/kernel/issues/ID192S CVE: NA Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=d5f15047f13b86b74d1ac4f39036ccae2078c492 -------------------------------- Lift changing ->mnt_slave from do_make_slave() into the caller. Simplifies the next steps... Signed-off-by: Al Viro Conflicts: fs/pnode.c [context diff] Signed-off-by: Yang Erkun --- fs/pnode.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/fs/pnode.c b/fs/pnode.c index e4d0340393d5..8bd564528687 100644 --- a/fs/pnode.c +++ b/fs/pnode.c @@ -109,7 +109,6 @@ static int do_make_slave(struct mount *mnt) } list_for_each_entry(slave_mnt, &mnt->mnt_slave_list, mnt_slave) slave_mnt->mnt_master = master; - list_move(&mnt->mnt_slave, &master->mnt_slave_list); list_splice(&mnt->mnt_slave_list, master->mnt_slave_list.prev); INIT_LIST_HEAD(&mnt->mnt_slave_list); mnt->mnt_master = master; @@ -126,8 +125,12 @@ void change_mnt_propagation(struct mount *mnt, int type) return; } do_make_slave(mnt); - if (type != MS_SLAVE) { - list_del_init(&mnt->mnt_slave); + list_del_init(&mnt->mnt_slave); + if (type == MS_SLAVE) { + if (mnt->mnt_master) + list_add(&mnt->mnt_slave, + &mnt->mnt_master->mnt_slave_list); + } else { mnt->mnt_master = NULL; if (type == MS_UNBINDABLE) mnt->mnt.mnt_flags |= MNT_UNBINDABLE; -- Gitee From 662acd4377c43b29128d8b1cb0ad9ede65e81a20 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Tue, 14 Oct 2025 16:04:49 +0800 Subject: [PATCH 2/3] change_mnt_propagation(): do_make_slave() is a no-op unless IS_MNT_SHARED() mainline inclusion from mainline-v6.17-rc1 commit ef86251194de9b31f7efcf70ea480ebe736e9e60 category: bugfix bugzilla: https://gitee.com/openeuler/kernel/issues/ID192S CVE: NA Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=ef86251194de9b31f7efcf70ea480ebe736e9e60 -------------------------------- ... since mnt->mnt_share and mnt->mnt_slave_list are guaranteed to be empty unless IS_MNT_SHARED(mnt). Signed-off-by: Al Viro Signed-off-by: Yang Erkun --- fs/pnode.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/fs/pnode.c b/fs/pnode.c index 8bd564528687..fedf32730109 100644 --- a/fs/pnode.c +++ b/fs/pnode.c @@ -75,10 +75,8 @@ static int do_make_slave(struct mount *mnt) struct mount *master, *slave_mnt; if (list_empty(&mnt->mnt_share)) { - if (IS_MNT_SHARED(mnt)) { - mnt_release_group_id(mnt); - CLEAR_MNT_SHARED(mnt); - } + mnt_release_group_id(mnt); + CLEAR_MNT_SHARED(mnt); master = mnt->mnt_master; if (!master) { struct list_head *p = &mnt->mnt_slave_list; @@ -124,7 +122,8 @@ void change_mnt_propagation(struct mount *mnt, int type) set_mnt_shared(mnt); return; } - do_make_slave(mnt); + if (IS_MNT_SHARED(mnt)) + do_make_slave(mnt); list_del_init(&mnt->mnt_slave); if (type == MS_SLAVE) { if (mnt->mnt_master) -- Gitee From 08c3cfeaed65128549a935ad9aeb0da2325be339 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Tue, 14 Oct 2025 16:04:50 +0800 Subject: [PATCH 3/3] do_make_slave(): choose new master sanely mainline inclusion from mainline-v6.17-rc1 commit 955336e204ab59301ff8b1f75a98a226f5a98782 category: bugfix bugzilla: https://gitee.com/openeuler/kernel/issues/ID192S CVE: NA Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=955336e204ab59301ff8b1f75a98a226f5a98782 -------------------------------- When mount changes propagation type so that it doesn't propagate events any more (MS_PRIVATE, MS_SLAVE, MS_UNBINDABLE), we need to make sure that event propagation between other mounts is unaffected. We need to make sure that events from peers and master of that mount (if any) still reach everything that used to be on its ->mnt_slave_list. If mount has neither peers nor master, we simply need to dissolve its ->mnt_slave_list and clear ->mnt_master of everything in there. If mount has peers, we transfer everything in ->mnt_slave_list of this mount into that of some of those peers (and adjust ->mnt_master accordingly). If mount has a master but no peers, we transfer everything in ->mnt_slave_list of this mount into that of its master (adjusting ->mnt_master, etc.). There are two problems with the current implementation: * there's a long-obsolete logics in choosing the peer - once upon a time it made sense to prefer the peer that had the same ->mnt_root as our mount, but that had been pointless since 2014 ("smarter propagate_mnt()") * the most common caller of that thing is umount_tree() taking the mounts out of propagation graph. In that case it's possible to have ->mnt_slave_list contents moved many times, since the replacement master is likely to be taken out by the same umount_tree(), etc. Take the choice of replacement master into a separate function (propagation_source()) and teach it to skip the candidates that are going to be taken out. Signed-off-by: Al Viro Conflicts: fs/pnode.c [adapt for will_be_unmounted] Signed-off-by: Yang Erkun --- fs/pnode.c | 57 +++++++++++++++++++++++++++++------------------------- 1 file changed, 31 insertions(+), 26 deletions(-) diff --git a/fs/pnode.c b/fs/pnode.c index fedf32730109..8b60c51c2a82 100644 --- a/fs/pnode.c +++ b/fs/pnode.c @@ -70,40 +70,45 @@ int get_dominating_id(struct mount *mnt, const struct path *root) return 0; } +static inline bool will_be_unmounted(struct mount *m) +{ + return m->mnt.mnt_flags & MNT_UMOUNT; +} + +static struct mount *propagation_source(struct mount *mnt) +{ + do { + struct mount *m; + for (m = next_peer(mnt); m != mnt; m = next_peer(m)) { + if (!will_be_unmounted(m)) + return m; + } + mnt = mnt->mnt_master; + } while (mnt && will_be_unmounted(mnt)); + return mnt; +} + static int do_make_slave(struct mount *mnt) { - struct mount *master, *slave_mnt; + struct mount *master = propagation_source(mnt); + struct mount *slave_mnt; if (list_empty(&mnt->mnt_share)) { mnt_release_group_id(mnt); - CLEAR_MNT_SHARED(mnt); - master = mnt->mnt_master; - if (!master) { - struct list_head *p = &mnt->mnt_slave_list; - while (!list_empty(p)) { - slave_mnt = list_first_entry(p, - struct mount, mnt_slave); - list_del_init(&slave_mnt->mnt_slave); - slave_mnt->mnt_master = NULL; - } - return 0; - } } else { - struct mount *m; - /* - * slave 'mnt' to a peer mount that has the - * same root dentry. If none is available then - * slave it to anything that is available. - */ - for (m = master = next_peer(mnt); m != mnt; m = next_peer(m)) { - if (m->mnt.mnt_root == mnt->mnt.mnt_root) { - master = m; - break; - } - } list_del_init(&mnt->mnt_share); mnt->mnt_group_id = 0; - CLEAR_MNT_SHARED(mnt); + } + CLEAR_MNT_SHARED(mnt); + if (!master) { + struct list_head *p = &mnt->mnt_slave_list; + while (!list_empty(p)) { + slave_mnt = list_first_entry(p, + struct mount, mnt_slave); + list_del_init(&slave_mnt->mnt_slave); + slave_mnt->mnt_master = NULL; + } + return 0; } list_for_each_entry(slave_mnt, &mnt->mnt_slave_list, mnt_slave) slave_mnt->mnt_master = master; -- Gitee