From b708b41121ba2af21d783f54e88045060a37184a Mon Sep 17 00:00:00 2001 From: Al Viro Date: Tue, 14 Oct 2025 16:03:58 +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 468e4e65a615..2b5f65d2bed9 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 d0121e3c748ff983749cbedbdb9f72015059e050 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Tue, 14 Oct 2025 16:03:59 +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 2b5f65d2bed9..3255e1bd74ae 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 41343a1706b65224840286a58ffd9d77a7b907fe Mon Sep 17 00:00:00 2001 From: Al Viro Date: Tue, 14 Oct 2025 16:04:00 +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 3255e1bd74ae..1a5d2449d731 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