diff options
author | Al Viro <viro@zeniv.linux.org.uk> | 2017-04-15 17:31:22 -0400 |
---|---|---|
committer | Al Viro <viro@zeniv.linux.org.uk> | 2017-04-21 14:05:36 -0400 |
commit | 4f757f3cbf54edef7b75c68d6d6d2f1a0ca08d2e (patch) | |
tree | ea39abd6e1941de602264b24ee18c82f12c01c04 | |
parent | 93893862fb7ba704ec5a6872a294c9cc2b0d4ca3 (diff) |
make sure that mntns_install() doesn't end up with referral for root
new flag: LOOKUP_DOWN. If the starting point is overmounted, cross
into whatever's mounted on top, triggering referrals et.al.
Use that instead of follow_down_one() loop in mntns_install(), handle
errors properly.
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
-rw-r--r-- | fs/namei.c | 38 | ||||
-rw-r--r-- | fs/namespace.c | 18 | ||||
-rw-r--r-- | include/linux/namei.h | 1 |
3 files changed, 50 insertions, 7 deletions
diff --git a/fs/namei.c b/fs/namei.c index 60c0a78ebca7..646db9cf2579 100644 --- a/fs/namei.c +++ b/fs/namei.c | |||
@@ -2252,6 +2252,35 @@ static inline int lookup_last(struct nameidata *nd) | |||
2252 | return walk_component(nd, 0); | 2252 | return walk_component(nd, 0); |
2253 | } | 2253 | } |
2254 | 2254 | ||
2255 | static int handle_lookup_down(struct nameidata *nd) | ||
2256 | { | ||
2257 | struct path path = nd->path; | ||
2258 | struct inode *inode = nd->inode; | ||
2259 | unsigned seq = nd->seq; | ||
2260 | int err; | ||
2261 | |||
2262 | if (nd->flags & LOOKUP_RCU) { | ||
2263 | /* | ||
2264 | * don't bother with unlazy_walk on failure - we are | ||
2265 | * at the very beginning of walk, so we lose nothing | ||
2266 | * if we simply redo everything in non-RCU mode | ||
2267 | */ | ||
2268 | if (unlikely(!__follow_mount_rcu(nd, &path, &inode, &seq))) | ||
2269 | return -ECHILD; | ||
2270 | } else { | ||
2271 | dget(path.dentry); | ||
2272 | err = follow_managed(&path, nd); | ||
2273 | if (unlikely(err < 0)) | ||
2274 | return err; | ||
2275 | inode = d_backing_inode(path.dentry); | ||
2276 | seq = 0; | ||
2277 | } | ||
2278 | path_to_nameidata(&path, nd); | ||
2279 | nd->inode = inode; | ||
2280 | nd->seq = seq; | ||
2281 | return 0; | ||
2282 | } | ||
2283 | |||
2255 | /* Returns 0 and nd will be valid on success; Retuns error, otherwise. */ | 2284 | /* Returns 0 and nd will be valid on success; Retuns error, otherwise. */ |
2256 | static int path_lookupat(struct nameidata *nd, unsigned flags, struct path *path) | 2285 | static int path_lookupat(struct nameidata *nd, unsigned flags, struct path *path) |
2257 | { | 2286 | { |
@@ -2260,6 +2289,15 @@ static int path_lookupat(struct nameidata *nd, unsigned flags, struct path *path | |||
2260 | 2289 | ||
2261 | if (IS_ERR(s)) | 2290 | if (IS_ERR(s)) |
2262 | return PTR_ERR(s); | 2291 | return PTR_ERR(s); |
2292 | |||
2293 | if (unlikely(flags & LOOKUP_DOWN)) { | ||
2294 | err = handle_lookup_down(nd); | ||
2295 | if (unlikely(err < 0)) { | ||
2296 | terminate_walk(nd); | ||
2297 | return err; | ||
2298 | } | ||
2299 | } | ||
2300 | |||
2263 | while (!(err = link_path_walk(s, nd)) | 2301 | while (!(err = link_path_walk(s, nd)) |
2264 | && ((err = lookup_last(nd)) > 0)) { | 2302 | && ((err = lookup_last(nd)) > 0)) { |
2265 | s = trailing_symlink(nd); | 2303 | s = trailing_symlink(nd); |
diff --git a/fs/namespace.c b/fs/namespace.c index cc1375eff88c..0886ef28bff6 100644 --- a/fs/namespace.c +++ b/fs/namespace.c | |||
@@ -3465,8 +3465,9 @@ static void mntns_put(struct ns_common *ns) | |||
3465 | static int mntns_install(struct nsproxy *nsproxy, struct ns_common *ns) | 3465 | static int mntns_install(struct nsproxy *nsproxy, struct ns_common *ns) |
3466 | { | 3466 | { |
3467 | struct fs_struct *fs = current->fs; | 3467 | struct fs_struct *fs = current->fs; |
3468 | struct mnt_namespace *mnt_ns = to_mnt_ns(ns); | 3468 | struct mnt_namespace *mnt_ns = to_mnt_ns(ns), *old_mnt_ns; |
3469 | struct path root; | 3469 | struct path root; |
3470 | int err; | ||
3470 | 3471 | ||
3471 | if (!ns_capable(mnt_ns->user_ns, CAP_SYS_ADMIN) || | 3472 | if (!ns_capable(mnt_ns->user_ns, CAP_SYS_ADMIN) || |
3472 | !ns_capable(current_user_ns(), CAP_SYS_CHROOT) || | 3473 | !ns_capable(current_user_ns(), CAP_SYS_CHROOT) || |
@@ -3477,15 +3478,18 @@ static int mntns_install(struct nsproxy *nsproxy, struct ns_common *ns) | |||
3477 | return -EINVAL; | 3478 | return -EINVAL; |
3478 | 3479 | ||
3479 | get_mnt_ns(mnt_ns); | 3480 | get_mnt_ns(mnt_ns); |
3480 | put_mnt_ns(nsproxy->mnt_ns); | 3481 | old_mnt_ns = nsproxy->mnt_ns; |
3481 | nsproxy->mnt_ns = mnt_ns; | 3482 | nsproxy->mnt_ns = mnt_ns; |
3482 | 3483 | ||
3483 | /* Find the root */ | 3484 | /* Find the root */ |
3484 | root.mnt = &mnt_ns->root->mnt; | 3485 | err = vfs_path_lookup(mnt_ns->root->mnt.mnt_root, &mnt_ns->root->mnt, |
3485 | root.dentry = mnt_ns->root->mnt.mnt_root; | 3486 | "/", LOOKUP_DOWN, &root); |
3486 | path_get(&root); | 3487 | if (err) { |
3487 | while(d_mountpoint(root.dentry) && follow_down_one(&root)) | 3488 | /* revert to old namespace */ |
3488 | ; | 3489 | nsproxy->mnt_ns = old_mnt_ns; |
3490 | put_mnt_ns(mnt_ns); | ||
3491 | return err; | ||
3492 | } | ||
3489 | 3493 | ||
3490 | /* Update the pwd and root */ | 3494 | /* Update the pwd and root */ |
3491 | set_fs_pwd(fs, &root); | 3495 | set_fs_pwd(fs, &root); |
diff --git a/include/linux/namei.h b/include/linux/namei.h index f29abda31e6d..8b4794e83196 100644 --- a/include/linux/namei.h +++ b/include/linux/namei.h | |||
@@ -44,6 +44,7 @@ enum {LAST_NORM, LAST_ROOT, LAST_DOT, LAST_DOTDOT, LAST_BIND}; | |||
44 | #define LOOKUP_JUMPED 0x1000 | 44 | #define LOOKUP_JUMPED 0x1000 |
45 | #define LOOKUP_ROOT 0x2000 | 45 | #define LOOKUP_ROOT 0x2000 |
46 | #define LOOKUP_EMPTY 0x4000 | 46 | #define LOOKUP_EMPTY 0x4000 |
47 | #define LOOKUP_DOWN 0x8000 | ||
47 | 48 | ||
48 | extern int path_pts(struct path *path); | 49 | extern int path_pts(struct path *path); |
49 | 50 | ||