1ce88cf466
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>
43 lines
858 B
C
43 lines
858 B
C
#ifndef _NAMESPACE_H_
|
|
#define _NAMESPACE_H_
|
|
#ifdef __KERNEL__
|
|
|
|
#include <linux/mount.h>
|
|
#include <linux/sched.h>
|
|
|
|
struct namespace {
|
|
atomic_t count;
|
|
struct vfsmount * root;
|
|
struct list_head list;
|
|
struct rw_semaphore sem;
|
|
};
|
|
|
|
extern int copy_namespace(int, struct task_struct *);
|
|
extern void __put_namespace(struct namespace *namespace);
|
|
|
|
static inline void put_namespace(struct namespace *namespace)
|
|
{
|
|
if (atomic_dec_and_lock(&namespace->count, &vfsmount_lock))
|
|
/* releases vfsmount_lock */
|
|
__put_namespace(namespace);
|
|
}
|
|
|
|
static inline void exit_namespace(struct task_struct *p)
|
|
{
|
|
struct namespace *namespace = p->namespace;
|
|
if (namespace) {
|
|
task_lock(p);
|
|
p->namespace = NULL;
|
|
task_unlock(p);
|
|
put_namespace(namespace);
|
|
}
|
|
}
|
|
|
|
static inline void get_namespace(struct namespace *namespace)
|
|
{
|
|
atomic_inc(&namespace->count);
|
|
}
|
|
|
|
#endif
|
|
#endif
|