diff options
author | Eric W. Biederman <ebiederm@xmission.com> | 2013-03-30 04:35:18 -0400 |
---|---|---|
committer | Eric W. Biederman <ebiederm@xmission.com> | 2013-08-26 21:42:15 -0400 |
commit | 4ce5d2b1a8fde84c0eebe70652cf28b9beda6b4e (patch) | |
tree | 08fa563457c4264fe48355e8d28c216c9a11664b | |
parent | 21e851943e31022731cd5fad386ca8fb552dbe64 (diff) |
vfs: Don't copy mount bind mounts of /proc/<pid>/ns/mnt between namespaces
Don't copy bind mounts of /proc/<pid>/ns/mnt between namespaces.
These files hold references to a mount namespace and copying them
between namespaces could result in a reference counting loop.
The current mnt_ns_loop test prevents loops on the assumption that
mounts don't cross between namespaces. Unfortunately unsharing a
mount namespace and shared substrees can both cause mounts to
propogate between mount namespaces.
Add two flags CL_COPY_UNBINDABLE and CL_COPY_MNT_NS_FILE are added to
control this behavior, and CL_COPY_ALL is redefined as both of them.
Signed-off-by: "Eric W. Biederman" <ebiederm@xmission.com>
-rw-r--r-- | fs/namespace.c | 46 | ||||
-rw-r--r-- | fs/pnode.h | 5 |
2 files changed, 38 insertions, 13 deletions
diff --git a/fs/namespace.c b/fs/namespace.c index 7e16a730559c..64627f883bf2 100644 --- a/fs/namespace.c +++ b/fs/namespace.c | |||
@@ -1355,14 +1355,11 @@ SYSCALL_DEFINE1(oldumount, char __user *, name) | |||
1355 | 1355 | ||
1356 | #endif | 1356 | #endif |
1357 | 1357 | ||
1358 | static bool mnt_ns_loop(struct path *path) | 1358 | static bool is_mnt_ns_file(struct dentry *dentry) |
1359 | { | 1359 | { |
1360 | /* Could bind mounting the mount namespace inode cause a | 1360 | /* Is this a proxy for a mount namespace? */ |
1361 | * mount namespace loop? | 1361 | struct inode *inode = dentry->d_inode; |
1362 | */ | ||
1363 | struct inode *inode = path->dentry->d_inode; | ||
1364 | struct proc_ns *ei; | 1362 | struct proc_ns *ei; |
1365 | struct mnt_namespace *mnt_ns; | ||
1366 | 1363 | ||
1367 | if (!proc_ns_inode(inode)) | 1364 | if (!proc_ns_inode(inode)) |
1368 | return false; | 1365 | return false; |
@@ -1371,7 +1368,19 @@ static bool mnt_ns_loop(struct path *path) | |||
1371 | if (ei->ns_ops != &mntns_operations) | 1368 | if (ei->ns_ops != &mntns_operations) |
1372 | return false; | 1369 | return false; |
1373 | 1370 | ||
1374 | mnt_ns = ei->ns; | 1371 | return true; |
1372 | } | ||
1373 | |||
1374 | static bool mnt_ns_loop(struct dentry *dentry) | ||
1375 | { | ||
1376 | /* Could bind mounting the mount namespace inode cause a | ||
1377 | * mount namespace loop? | ||
1378 | */ | ||
1379 | struct mnt_namespace *mnt_ns; | ||
1380 | if (!is_mnt_ns_file(dentry)) | ||
1381 | return false; | ||
1382 | |||
1383 | mnt_ns = get_proc_ns(dentry->d_inode)->ns; | ||
1375 | return current->nsproxy->mnt_ns->seq >= mnt_ns->seq; | 1384 | return current->nsproxy->mnt_ns->seq >= mnt_ns->seq; |
1376 | } | 1385 | } |
1377 | 1386 | ||
@@ -1380,7 +1389,10 @@ struct mount *copy_tree(struct mount *mnt, struct dentry *dentry, | |||
1380 | { | 1389 | { |
1381 | struct mount *res, *p, *q, *r, *parent; | 1390 | struct mount *res, *p, *q, *r, *parent; |
1382 | 1391 | ||
1383 | if (!(flag & CL_COPY_ALL) && IS_MNT_UNBINDABLE(mnt)) | 1392 | if (!(flag & CL_COPY_UNBINDABLE) && IS_MNT_UNBINDABLE(mnt)) |
1393 | return ERR_PTR(-EINVAL); | ||
1394 | |||
1395 | if (!(flag & CL_COPY_MNT_NS_FILE) && is_mnt_ns_file(dentry)) | ||
1384 | return ERR_PTR(-EINVAL); | 1396 | return ERR_PTR(-EINVAL); |
1385 | 1397 | ||
1386 | res = q = clone_mnt(mnt, dentry, flag); | 1398 | res = q = clone_mnt(mnt, dentry, flag); |
@@ -1397,7 +1409,13 @@ struct mount *copy_tree(struct mount *mnt, struct dentry *dentry, | |||
1397 | continue; | 1409 | continue; |
1398 | 1410 | ||
1399 | for (s = r; s; s = next_mnt(s, r)) { | 1411 | for (s = r; s; s = next_mnt(s, r)) { |
1400 | if (!(flag & CL_COPY_ALL) && IS_MNT_UNBINDABLE(s)) { | 1412 | if (!(flag & CL_COPY_UNBINDABLE) && |
1413 | IS_MNT_UNBINDABLE(s)) { | ||
1414 | s = skip_mnt_tree(s); | ||
1415 | continue; | ||
1416 | } | ||
1417 | if (!(flag & CL_COPY_MNT_NS_FILE) && | ||
1418 | is_mnt_ns_file(s->mnt.mnt_root)) { | ||
1401 | s = skip_mnt_tree(s); | 1419 | s = skip_mnt_tree(s); |
1402 | continue; | 1420 | continue; |
1403 | } | 1421 | } |
@@ -1733,7 +1751,7 @@ static int do_loopback(struct path *path, const char *old_name, | |||
1733 | return err; | 1751 | return err; |
1734 | 1752 | ||
1735 | err = -EINVAL; | 1753 | err = -EINVAL; |
1736 | if (mnt_ns_loop(&old_path)) | 1754 | if (mnt_ns_loop(old_path.dentry)) |
1737 | goto out; | 1755 | goto out; |
1738 | 1756 | ||
1739 | mp = lock_mount(path); | 1757 | mp = lock_mount(path); |
@@ -1755,7 +1773,7 @@ static int do_loopback(struct path *path, const char *old_name, | |||
1755 | goto out2; | 1773 | goto out2; |
1756 | 1774 | ||
1757 | if (recurse) | 1775 | if (recurse) |
1758 | mnt = copy_tree(old, old_path.dentry, 0); | 1776 | mnt = copy_tree(old, old_path.dentry, CL_COPY_MNT_NS_FILE); |
1759 | else | 1777 | else |
1760 | mnt = clone_mnt(old, old_path.dentry, 0); | 1778 | mnt = clone_mnt(old, old_path.dentry, 0); |
1761 | 1779 | ||
@@ -2417,7 +2435,7 @@ static struct mnt_namespace *dup_mnt_ns(struct mnt_namespace *mnt_ns, | |||
2417 | 2435 | ||
2418 | namespace_lock(); | 2436 | namespace_lock(); |
2419 | /* First pass: copy the tree topology */ | 2437 | /* First pass: copy the tree topology */ |
2420 | copy_flags = CL_COPY_ALL | CL_EXPIRE; | 2438 | copy_flags = CL_COPY_UNBINDABLE | CL_EXPIRE; |
2421 | if (user_ns != mnt_ns->user_ns) | 2439 | if (user_ns != mnt_ns->user_ns) |
2422 | copy_flags |= CL_SHARED_TO_SLAVE | CL_UNPRIVILEGED; | 2440 | copy_flags |= CL_SHARED_TO_SLAVE | CL_UNPRIVILEGED; |
2423 | new = copy_tree(old, old->mnt.mnt_root, copy_flags); | 2441 | new = copy_tree(old, old->mnt.mnt_root, copy_flags); |
@@ -2452,6 +2470,10 @@ static struct mnt_namespace *dup_mnt_ns(struct mnt_namespace *mnt_ns, | |||
2452 | } | 2470 | } |
2453 | p = next_mnt(p, old); | 2471 | p = next_mnt(p, old); |
2454 | q = next_mnt(q, new); | 2472 | q = next_mnt(q, new); |
2473 | if (!q) | ||
2474 | break; | ||
2475 | while (p->mnt.mnt_root != q->mnt.mnt_root) | ||
2476 | p = next_mnt(p, old); | ||
2455 | } | 2477 | } |
2456 | namespace_unlock(); | 2478 | namespace_unlock(); |
2457 | 2479 | ||
diff --git a/fs/pnode.h b/fs/pnode.h index b091445c1c4a..59e7eda1851e 100644 --- a/fs/pnode.h +++ b/fs/pnode.h | |||
@@ -19,11 +19,14 @@ | |||
19 | 19 | ||
20 | #define CL_EXPIRE 0x01 | 20 | #define CL_EXPIRE 0x01 |
21 | #define CL_SLAVE 0x02 | 21 | #define CL_SLAVE 0x02 |
22 | #define CL_COPY_ALL 0x04 | 22 | #define CL_COPY_UNBINDABLE 0x04 |
23 | #define CL_MAKE_SHARED 0x08 | 23 | #define CL_MAKE_SHARED 0x08 |
24 | #define CL_PRIVATE 0x10 | 24 | #define CL_PRIVATE 0x10 |
25 | #define CL_SHARED_TO_SLAVE 0x20 | 25 | #define CL_SHARED_TO_SLAVE 0x20 |
26 | #define CL_UNPRIVILEGED 0x40 | 26 | #define CL_UNPRIVILEGED 0x40 |
27 | #define CL_COPY_MNT_NS_FILE 0x80 | ||
28 | |||
29 | #define CL_COPY_ALL (CL_COPY_UNBINDABLE | CL_COPY_MNT_NS_FILE) | ||
27 | 30 | ||
28 | static inline void set_mnt_shared(struct mount *mnt) | 31 | static inline void set_mnt_shared(struct mount *mnt) |
29 | { | 32 | { |