Skip to content

Commit 06b1ce9

Browse files
committed
Merge patch series "mount: handle mount propagation for detached mount trees"
Christian Brauner <[email protected]> says: In commit ee2e3f5 ("mount: fix mounting of detached mounts onto targets that reside on shared mounts") I fixed a bug where propagating the source mount tree of an anonymous mount namespace into a target mount tree of a non-anonymous mount namespace could be used to trigger an integer overflow in the non-anonymous mount namespace causing any new mounts to fail. The cause of this was that the propagation algorithm was unable to recognize mounts from the source mount tree that were already propagated into the target mount tree and then reappeared as propagation targets when walking the destination propagation mount tree. When fixing this I disabled mount propagation into anonymous mount namespaces. Make it possible for anonymous mount namespace to receive mount propagation events correctly. This is no also a correctness issue now that we allow mounting detached mount trees onto detached mount trees. Mark the source anonymous mount namespace with MNTNS_PROPAGATING indicating that all mounts belonging to this mount namespace are currently in the process of being propagated and make the propagation algorithm discard those if they appear as propagation targets. * patches from https://lore.kernel.org/r/[email protected]: selftests: test subdirectory mounting selftests: add test for detached mount tree propagation mount: handle mount propagation for detached mount trees Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Christian Brauner <[email protected]>
2 parents 99b6a1d + e5c10b1 commit 06b1ce9

File tree

5 files changed

+146
-66
lines changed

5 files changed

+146
-66
lines changed

fs/mount.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,12 @@
55
#include <linux/ns_common.h>
66
#include <linux/fs_pin.h>
77

8+
extern struct list_head notify_list;
9+
10+
typedef __u32 __bitwise mntns_flags_t;
11+
12+
#define MNTNS_PROPAGATING ((__force mntns_flags_t)(1 << 0))
13+
814
struct mnt_namespace {
915
struct ns_common ns;
1016
struct mount * root;
@@ -27,6 +33,7 @@ struct mnt_namespace {
2733
struct rb_node mnt_ns_tree_node; /* node in the mnt_ns_tree */
2834
struct list_head mnt_ns_list; /* entry in the sequential list of mounts namespace */
2935
refcount_t passive; /* number references not pinning @mounts */
36+
mntns_flags_t mntns_flags;
3037
} __randomize_layout;
3138

3239
struct mnt_pcp {

fs/namespace.c

Lines changed: 27 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3536,13 +3536,6 @@ static int do_move_mount(struct path *old_path,
35363536
if (!may_use_mount(p))
35373537
goto out;
35383538

3539-
/*
3540-
* Don't allow moving an attached mount tree to an anonymous
3541-
* mount tree.
3542-
*/
3543-
if (!is_anon_ns(ns) && is_anon_ns(p->mnt_ns))
3544-
goto out;
3545-
35463539
/* The thing moved must be mounted... */
35473540
if (!is_mounted(&old->mnt))
35483541
goto out;
@@ -3551,15 +3544,31 @@ static int do_move_mount(struct path *old_path,
35513544
if (!(attached ? check_mnt(old) : is_anon_ns(ns)))
35523545
goto out;
35533546

3554-
/*
3555-
* Ending up with two files referring to the root of the same
3556-
* anonymous mount namespace would cause an error as this would
3557-
* mean trying to move the same mount twice into the mount tree
3558-
* which would be rejected later. But be explicit about it right
3559-
* here.
3560-
*/
3561-
if (is_anon_ns(ns) && is_anon_ns(p->mnt_ns) && ns == p->mnt_ns)
3547+
if (is_anon_ns(ns)) {
3548+
/*
3549+
* Ending up with two files referring to the root of the
3550+
* same anonymous mount namespace would cause an error
3551+
* as this would mean trying to move the same mount
3552+
* twice into the mount tree which would be rejected
3553+
* later. But be explicit about it right here.
3554+
*/
3555+
if ((is_anon_ns(p->mnt_ns) && ns == p->mnt_ns))
3556+
goto out;
3557+
3558+
/*
3559+
* If this is an anonymous mount tree ensure that mount
3560+
* propagation can detect mounts that were just
3561+
* propagated to the target mount tree so we don't
3562+
* propagate onto them.
3563+
*/
3564+
ns->mntns_flags |= MNTNS_PROPAGATING;
3565+
} else if (is_anon_ns(p->mnt_ns)) {
3566+
/*
3567+
* Don't allow moving an attached mount tree to an
3568+
* anonymous mount tree.
3569+
*/
35623570
goto out;
3571+
}
35633572

35643573
if (old->mnt.mnt_flags & MNT_LOCKED)
35653574
goto out;
@@ -3603,6 +3612,9 @@ static int do_move_mount(struct path *old_path,
36033612
if (err)
36043613
goto out;
36053614

3615+
if (is_anon_ns(ns))
3616+
ns->mntns_flags &= ~MNTNS_PROPAGATING;
3617+
36063618
/* if the mount is moved, it should no longer be expire
36073619
* automatically */
36083620
list_del_init(&old->mnt_expire);

fs/pnode.c

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,7 @@ static struct mount *propagation_next(struct mount *m,
150150
struct mount *origin)
151151
{
152152
/* are there any slaves of this mount? */
153-
if (!IS_MNT_NEW(m) && !list_empty(&m->mnt_slave_list))
153+
if (!IS_MNT_PROPAGATED(m) && !list_empty(&m->mnt_slave_list))
154154
return first_slave(m);
155155

156156
while (1) {
@@ -174,7 +174,7 @@ static struct mount *skip_propagation_subtree(struct mount *m,
174174
* Advance m such that propagation_next will not return
175175
* the slaves of m.
176176
*/
177-
if (!IS_MNT_NEW(m) && !list_empty(&m->mnt_slave_list))
177+
if (!IS_MNT_PROPAGATED(m) && !list_empty(&m->mnt_slave_list))
178178
m = last_slave(m);
179179

180180
return m;
@@ -185,7 +185,7 @@ static struct mount *next_group(struct mount *m, struct mount *origin)
185185
while (1) {
186186
while (1) {
187187
struct mount *next;
188-
if (!IS_MNT_NEW(m) && !list_empty(&m->mnt_slave_list))
188+
if (!IS_MNT_PROPAGATED(m) && !list_empty(&m->mnt_slave_list))
189189
return first_slave(m);
190190
next = next_peer(m);
191191
if (m->mnt_group_id == origin->mnt_group_id) {
@@ -226,7 +226,7 @@ static int propagate_one(struct mount *m, struct mountpoint *dest_mp)
226226
struct mount *child;
227227
int type;
228228
/* skip ones added by this propagate_mnt() */
229-
if (IS_MNT_NEW(m))
229+
if (IS_MNT_PROPAGATED(m))
230230
return 0;
231231
/* skip if mountpoint isn't covered by it */
232232
if (!is_subdir(dest_mp->m_dentry, m->mnt.mnt_root))
@@ -380,7 +380,7 @@ bool propagation_would_overmount(const struct mount *from,
380380
if (!IS_MNT_SHARED(from))
381381
return false;
382382

383-
if (IS_MNT_NEW(to))
383+
if (IS_MNT_PROPAGATED(to))
384384
return false;
385385

386386
if (to->mnt.mnt_root != mp->m_dentry)

fs/pnode.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212

1313
#define IS_MNT_SHARED(m) ((m)->mnt.mnt_flags & MNT_SHARED)
1414
#define IS_MNT_SLAVE(m) ((m)->mnt_master)
15-
#define IS_MNT_NEW(m) (!(m)->mnt_ns || is_anon_ns((m)->mnt_ns))
15+
#define IS_MNT_PROPAGATED(m) (!(m)->mnt_ns || ((m)->mnt_ns->mntns_flags & MNTNS_PROPAGATING))
1616
#define CLEAR_MNT_SHARED(m) ((m)->mnt.mnt_flags &= ~MNT_SHARED)
1717
#define IS_MNT_UNBINDABLE(m) ((m)->mnt.mnt_flags & MNT_UNBINDABLE)
1818
#define IS_MNT_MARKED(m) ((m)->mnt.mnt_flags & MNT_MARKED)

tools/testing/selftests/mount_setattr/mount_setattr_test.c

Lines changed: 106 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include <stdarg.h>
2121
#include <linux/mount.h>
2222

23+
#include "../filesystems/overlayfs/wrappers.h"
2324
#include "../kselftest_harness.h"
2425

2526
#ifndef CLONE_NEWNS
@@ -177,51 +178,6 @@ static inline int sys_open_tree(int dfd, const char *filename, unsigned int flag
177178
return syscall(__NR_open_tree, dfd, filename, flags);
178179
}
179180

180-
/* move_mount() flags */
181-
#ifndef MOVE_MOUNT_F_SYMLINKS
182-
#define MOVE_MOUNT_F_SYMLINKS 0x00000001 /* Follow symlinks on from path */
183-
#endif
184-
185-
#ifndef MOVE_MOUNT_F_AUTOMOUNTS
186-
#define MOVE_MOUNT_F_AUTOMOUNTS 0x00000002 /* Follow automounts on from path */
187-
#endif
188-
189-
#ifndef MOVE_MOUNT_F_EMPTY_PATH
190-
#define MOVE_MOUNT_F_EMPTY_PATH 0x00000004 /* Empty from path permitted */
191-
#endif
192-
193-
#ifndef MOVE_MOUNT_T_SYMLINKS
194-
#define MOVE_MOUNT_T_SYMLINKS 0x00000010 /* Follow symlinks on to path */
195-
#endif
196-
197-
#ifndef MOVE_MOUNT_T_AUTOMOUNTS
198-
#define MOVE_MOUNT_T_AUTOMOUNTS 0x00000020 /* Follow automounts on to path */
199-
#endif
200-
201-
#ifndef MOVE_MOUNT_T_EMPTY_PATH
202-
#define MOVE_MOUNT_T_EMPTY_PATH 0x00000040 /* Empty to path permitted */
203-
#endif
204-
205-
#ifndef MOVE_MOUNT_SET_GROUP
206-
#define MOVE_MOUNT_SET_GROUP 0x00000100 /* Set sharing group instead */
207-
#endif
208-
209-
#ifndef MOVE_MOUNT_BENEATH
210-
#define MOVE_MOUNT_BENEATH 0x00000200 /* Mount beneath top mount */
211-
#endif
212-
213-
#ifndef MOVE_MOUNT__MASK
214-
#define MOVE_MOUNT__MASK 0x00000377
215-
#endif
216-
217-
static inline int sys_move_mount(int from_dfd, const char *from_pathname,
218-
int to_dfd, const char *to_pathname,
219-
unsigned int flags)
220-
{
221-
return syscall(__NR_move_mount, from_dfd, from_pathname, to_dfd,
222-
to_pathname, flags);
223-
}
224-
225181
static ssize_t write_nointr(int fd, const void *buf, size_t count)
226182
{
227183
ssize_t ret;
@@ -1789,6 +1745,41 @@ TEST_F(mount_setattr, open_tree_detached_fail3)
17891745
ASSERT_EQ(errno, EINVAL);
17901746
}
17911747

1748+
TEST_F(mount_setattr, open_tree_subfolder)
1749+
{
1750+
int fd_context, fd_tmpfs, fd_tree;
1751+
1752+
fd_context = sys_fsopen("tmpfs", 0);
1753+
ASSERT_GE(fd_context, 0);
1754+
1755+
ASSERT_EQ(sys_fsconfig(fd_context, FSCONFIG_CMD_CREATE, NULL, NULL, 0), 0);
1756+
1757+
fd_tmpfs = sys_fsmount(fd_context, 0, 0);
1758+
ASSERT_GE(fd_tmpfs, 0);
1759+
1760+
EXPECT_EQ(close(fd_context), 0);
1761+
1762+
ASSERT_EQ(mkdirat(fd_tmpfs, "subdir", 0755), 0);
1763+
1764+
fd_tree = sys_open_tree(fd_tmpfs, "subdir",
1765+
AT_NO_AUTOMOUNT | AT_SYMLINK_NOFOLLOW |
1766+
AT_RECURSIVE | OPEN_TREE_CLOEXEC |
1767+
OPEN_TREE_CLONE);
1768+
ASSERT_GE(fd_tree, 0);
1769+
1770+
EXPECT_EQ(close(fd_tmpfs), 0);
1771+
1772+
ASSERT_EQ(mkdirat(-EBADF, "/mnt/open_tree_subfolder", 0755), 0);
1773+
1774+
ASSERT_EQ(sys_move_mount(fd_tree, "", -EBADF, "/mnt/open_tree_subfolder", MOVE_MOUNT_F_EMPTY_PATH), 0);
1775+
1776+
EXPECT_EQ(close(fd_tree), 0);
1777+
1778+
ASSERT_EQ(umount2("/mnt/open_tree_subfolder", 0), 0);
1779+
1780+
EXPECT_EQ(rmdir("/mnt/open_tree_subfolder"), 0);
1781+
}
1782+
17921783
TEST_F(mount_setattr, mount_detached_mount_on_detached_mount_then_close)
17931784
{
17941785
int fd_tree_base = -EBADF, fd_tree_subdir = -EBADF;
@@ -2097,4 +2088,74 @@ TEST_F(mount_setattr, two_detached_subtrees_of_same_anonymous_mount_namespace)
20972088
ASSERT_EQ(move_mount(fd_tree1, "", -EBADF, "/tmp/target1", MOVE_MOUNT_F_EMPTY_PATH), 0);
20982089
}
20992090

2091+
TEST_F(mount_setattr, detached_tree_propagation)
2092+
{
2093+
int fd_tree = -EBADF;
2094+
struct statx stx1, stx2, stx3, stx4;
2095+
2096+
ASSERT_EQ(unshare(CLONE_NEWNS), 0);
2097+
ASSERT_EQ(mount(NULL, "/mnt", NULL, MS_REC | MS_SHARED, NULL), 0);
2098+
2099+
/*
2100+
* Copy the following mount tree:
2101+
*
2102+
* /mnt testing tmpfs
2103+
* |-/mnt/A testing tmpfs
2104+
* | `-/mnt/A/AA testing tmpfs
2105+
* | `-/mnt/A/AA/B testing tmpfs
2106+
* | `-/mnt/A/AA/B/BB testing tmpfs
2107+
* `-/mnt/B testing ramfs
2108+
*/
2109+
fd_tree = sys_open_tree(-EBADF, "/mnt",
2110+
AT_NO_AUTOMOUNT | AT_SYMLINK_NOFOLLOW |
2111+
AT_RECURSIVE | OPEN_TREE_CLOEXEC |
2112+
OPEN_TREE_CLONE);
2113+
ASSERT_GE(fd_tree, 0);
2114+
2115+
ASSERT_EQ(statx(-EBADF, "/mnt/A", 0, 0, &stx1), 0);
2116+
ASSERT_EQ(statx(fd_tree, "A", 0, 0, &stx2), 0);
2117+
2118+
/*
2119+
* Copying the mount namespace like done above doesn't alter the
2120+
* mounts in any way so the filesystem mounted on /mnt must be
2121+
* identical even though the mounts will differ. Use the device
2122+
* information to verify that. Note that tmpfs will have a 0
2123+
* major number so comparing the major number is misleading.
2124+
*/
2125+
ASSERT_EQ(stx1.stx_dev_minor, stx2.stx_dev_minor);
2126+
2127+
/* Mount a tmpfs filesystem over /mnt/A. */
2128+
ASSERT_EQ(mount(NULL, "/mnt/A", "tmpfs", 0, NULL), 0);
2129+
2130+
2131+
ASSERT_EQ(statx(-EBADF, "/mnt/A", 0, 0, &stx3), 0);
2132+
ASSERT_EQ(statx(fd_tree, "A", 0, 0, &stx4), 0);
2133+
2134+
/*
2135+
* A new filesystem has been mounted on top of /mnt/A which
2136+
* means that the device information will be different for any
2137+
* statx() that was taken from /mnt/A before the mount compared
2138+
* to one after the mount.
2139+
*
2140+
* Since we already now that the device information between the
2141+
* stx1 and stx2 samples are identical we also now that stx2 and
2142+
* stx3 device information will necessarily differ.
2143+
*/
2144+
ASSERT_NE(stx1.stx_dev_minor, stx3.stx_dev_minor);
2145+
2146+
/*
2147+
* If mount propagation worked correctly then the tmpfs mount
2148+
* that was created after the mount namespace was unshared will
2149+
* have propagated onto /mnt/A in the detached mount tree.
2150+
*
2151+
* Verify that the device information for stx3 and stx4 are
2152+
* identical. It is already established that stx3 is different
2153+
* from both stx1 and stx2 sampled before the tmpfs mount was
2154+
* done so if stx3 and stx4 are identical the proof is done.
2155+
*/
2156+
ASSERT_EQ(stx3.stx_dev_minor, stx4.stx_dev_minor);
2157+
2158+
EXPECT_EQ(close(fd_tree), 0);
2159+
}
2160+
21002161
TEST_HARNESS_MAIN

0 commit comments

Comments
 (0)