diff options
author | Miklos Szeredi <miklos@szeredi.hu> | 2005-07-07 20:57:24 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@g5.osdl.org> | 2005-07-07 21:23:51 -0400 |
commit | 1ce88cf466f7b6078b14d67d186a3d7c19dd5609 (patch) | |
tree | b9716f3a71e3285a998da9614cfbc132ca605542 /include/linux | |
parent | 202322e6f7cd12e82b5ff0fa92bbdf517fcf0947 (diff) |
[PATCH] namespace.c: fix race in mark_mounts_for_expiry()
This patch fixes a race found by Ram in mark_mounts_for_expiry() in
fs/namespace.c.
The bug can only be triggered with simultaneous exiting of a process having
a private namespace, and expiry of a mount from within that namespace.
It's practically impossible to trigger, and I haven't even tried. But
still, a bug is a bug.
The race happens when put_namespace() is called by another task, while
mark_mounts_for_expiry() is between atomic_read() and get_namespace(). In
that case get_namespace() will be called on an already dead namespace with
unforeseeable results.
The solution was suggested by Al Viro, with his own words:
Instead of screwing with atomic_read() in there, why don't we
simply do the following:
a) atomic_dec_and_lock() in put_namespace()
b) __put_namespace() called without dropping lock
c) the first thing done by __put_namespace would be
struct vfsmount *root = namespace->root;
namespace->root = NULL;
spin_unlock(...);
....
umount_tree(root);
...
d) check in mark_... would be simply namespace && namespace->root.
And we are all set; no screwing around with atomic_read(), no magic
at all. Dying namespace gets NULL ->root.
All changes of ->root happen under spinlock.
If under a spinlock we see non-NULL ->mnt_namespace, it won't be
freed until we drop the lock (we will set ->mnt_namespace to NULL
under that lock before we get to freeing namespace).
If under a spinlock we see non-NULL ->mnt_namespace and
->mnt_namespace->root, we can grab a reference to namespace and be
sure that it won't go away.
Signed-off-by: Miklos Szeredi <miklos@szeredi.hu>
Acked-by: Al Viro <viro@parcelfarce.linux.theplanet.co.uk>
Acked-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Diffstat (limited to 'include/linux')
-rw-r--r-- | include/linux/namespace.h | 3 |
1 files changed, 2 insertions, 1 deletions
diff --git a/include/linux/namespace.h b/include/linux/namespace.h index 697991b69f9b..0e5a86f13b2f 100644 --- a/include/linux/namespace.h +++ b/include/linux/namespace.h | |||
@@ -17,7 +17,8 @@ extern void __put_namespace(struct namespace *namespace); | |||
17 | 17 | ||
18 | static inline void put_namespace(struct namespace *namespace) | 18 | static inline void put_namespace(struct namespace *namespace) |
19 | { | 19 | { |
20 | if (atomic_dec_and_test(&namespace->count)) | 20 | if (atomic_dec_and_lock(&namespace->count, &vfsmount_lock)) |
21 | /* releases vfsmount_lock */ | ||
21 | __put_namespace(namespace); | 22 | __put_namespace(namespace); |
22 | } | 23 | } |
23 | 24 | ||