diff options
author | Al Viro <viro@zeniv.linux.org.uk> | 2011-03-18 08:29:36 -0400 |
---|---|---|
committer | Al Viro <viro@zeniv.linux.org.uk> | 2011-03-18 08:54:59 -0400 |
commit | 27cb1572e3e6bb1f8cf6bb3d74c914a87b131792 (patch) | |
tree | 01e6510de480d3595ccff315d777770490e51f9c /fs/namespace.c | |
parent | 9d412a43c3b26e1e549319e5eec26f0829f9f74d (diff) |
fix deadlock in pivot_root()
Don't hold vfsmount_lock over the loop traversing ->mnt_parent;
do check_mnt(new.mnt) under namespace_sem instead; combined with
namespace_sem held over all that code it'll guarantee the stability
of ->mnt_parent chain all the way to the root.
Doing check_mnt() outside of namespace_sem in case of pivot_root()
is wrong anyway.
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Diffstat (limited to 'fs/namespace.c')
-rw-r--r-- | fs/namespace.c | 16 |
1 files changed, 5 insertions, 11 deletions
diff --git a/fs/namespace.c b/fs/namespace.c index 453529f72dff..46cc26b5aaf2 100644 --- a/fs/namespace.c +++ b/fs/namespace.c | |||
@@ -2569,9 +2569,6 @@ SYSCALL_DEFINE2(pivot_root, const char __user *, new_root, | |||
2569 | error = user_path_dir(new_root, &new); | 2569 | error = user_path_dir(new_root, &new); |
2570 | if (error) | 2570 | if (error) |
2571 | goto out0; | 2571 | goto out0; |
2572 | error = -EINVAL; | ||
2573 | if (!check_mnt(new.mnt)) | ||
2574 | goto out1; | ||
2575 | 2572 | ||
2576 | error = user_path_dir(put_old, &old); | 2573 | error = user_path_dir(put_old, &old); |
2577 | if (error) | 2574 | if (error) |
@@ -2591,7 +2588,7 @@ SYSCALL_DEFINE2(pivot_root, const char __user *, new_root, | |||
2591 | IS_MNT_SHARED(new.mnt->mnt_parent) || | 2588 | IS_MNT_SHARED(new.mnt->mnt_parent) || |
2592 | IS_MNT_SHARED(root.mnt->mnt_parent)) | 2589 | IS_MNT_SHARED(root.mnt->mnt_parent)) |
2593 | goto out2; | 2590 | goto out2; |
2594 | if (!check_mnt(root.mnt)) | 2591 | if (!check_mnt(root.mnt) || !check_mnt(new.mnt)) |
2595 | goto out2; | 2592 | goto out2; |
2596 | error = -ENOENT; | 2593 | error = -ENOENT; |
2597 | if (cant_mount(old.dentry)) | 2594 | if (cant_mount(old.dentry)) |
@@ -2615,19 +2612,19 @@ SYSCALL_DEFINE2(pivot_root, const char __user *, new_root, | |||
2615 | goto out2; /* not attached */ | 2612 | goto out2; /* not attached */ |
2616 | /* make sure we can reach put_old from new_root */ | 2613 | /* make sure we can reach put_old from new_root */ |
2617 | tmp = old.mnt; | 2614 | tmp = old.mnt; |
2618 | br_write_lock(vfsmount_lock); | ||
2619 | if (tmp != new.mnt) { | 2615 | if (tmp != new.mnt) { |
2620 | for (;;) { | 2616 | for (;;) { |
2621 | if (tmp->mnt_parent == tmp) | 2617 | if (tmp->mnt_parent == tmp) |
2622 | goto out3; /* already mounted on put_old */ | 2618 | goto out2; /* already mounted on put_old */ |
2623 | if (tmp->mnt_parent == new.mnt) | 2619 | if (tmp->mnt_parent == new.mnt) |
2624 | break; | 2620 | break; |
2625 | tmp = tmp->mnt_parent; | 2621 | tmp = tmp->mnt_parent; |
2626 | } | 2622 | } |
2627 | if (!is_subdir(tmp->mnt_mountpoint, new.dentry)) | 2623 | if (!is_subdir(tmp->mnt_mountpoint, new.dentry)) |
2628 | goto out3; | 2624 | goto out2; |
2629 | } else if (!is_subdir(old.dentry, new.dentry)) | 2625 | } else if (!is_subdir(old.dentry, new.dentry)) |
2630 | goto out3; | 2626 | goto out2; |
2627 | br_write_lock(vfsmount_lock); | ||
2631 | detach_mnt(new.mnt, &parent_path); | 2628 | detach_mnt(new.mnt, &parent_path); |
2632 | detach_mnt(root.mnt, &root_parent); | 2629 | detach_mnt(root.mnt, &root_parent); |
2633 | /* mount old root on put_old */ | 2630 | /* mount old root on put_old */ |
@@ -2650,9 +2647,6 @@ out1: | |||
2650 | path_put(&new); | 2647 | path_put(&new); |
2651 | out0: | 2648 | out0: |
2652 | return error; | 2649 | return error; |
2653 | out3: | ||
2654 | br_write_unlock(vfsmount_lock); | ||
2655 | goto out2; | ||
2656 | } | 2650 | } |
2657 | 2651 | ||
2658 | static void __init init_mount_tree(void) | 2652 | static void __init init_mount_tree(void) |