mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-12-27 15:51:30 -05:00
take freeing of emptied mnt_namespace to namespace_unlock()
Freeing of a namespace must be delayed until after we'd dealt with mount notifications (in namespace_unlock()). The reasons are not immediately obvious (they are buried in ->prev_ns handling in mnt_notify()), and having that free_mnt_ns() explicitly called after namespace_unlock() is asking for trouble - it does feel like they should be OK to free as soon as they've been emptied. Make the things more explicit by setting 'emptied_ns' under namespace_sem and having namespace_unlock() free the sucker as soon as it's safe to free. Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
This commit is contained in:
@@ -79,6 +79,7 @@ static struct kmem_cache *mnt_cache __ro_after_init;
|
||||
static DECLARE_RWSEM(namespace_sem);
|
||||
static HLIST_HEAD(unmounted); /* protected by namespace_sem */
|
||||
static LIST_HEAD(ex_mountpoints); /* protected by namespace_sem */
|
||||
static struct mnt_namespace *emptied_ns; /* protected by namespace_sem */
|
||||
static DEFINE_SEQLOCK(mnt_ns_tree_lock);
|
||||
|
||||
#ifdef CONFIG_FSNOTIFY
|
||||
@@ -1730,15 +1731,18 @@ static bool need_notify_mnt_list(void)
|
||||
}
|
||||
#endif
|
||||
|
||||
static void free_mnt_ns(struct mnt_namespace *);
|
||||
static void namespace_unlock(void)
|
||||
{
|
||||
struct hlist_head head;
|
||||
struct hlist_node *p;
|
||||
struct mount *m;
|
||||
struct mnt_namespace *ns = emptied_ns;
|
||||
LIST_HEAD(list);
|
||||
|
||||
hlist_move_list(&unmounted, &head);
|
||||
list_splice_init(&ex_mountpoints, &list);
|
||||
emptied_ns = NULL;
|
||||
|
||||
if (need_notify_mnt_list()) {
|
||||
/*
|
||||
@@ -1752,6 +1756,11 @@ static void namespace_unlock(void)
|
||||
} else {
|
||||
up_write(&namespace_sem);
|
||||
}
|
||||
if (unlikely(ns)) {
|
||||
/* Make sure we notice when we leak mounts. */
|
||||
VFS_WARN_ON_ONCE(!mnt_ns_empty(ns));
|
||||
free_mnt_ns(ns);
|
||||
}
|
||||
|
||||
shrink_dentry_list(&list);
|
||||
|
||||
@@ -2335,12 +2344,10 @@ void drop_collected_paths(struct path *paths, struct path *prealloc)
|
||||
kfree(paths);
|
||||
}
|
||||
|
||||
static void free_mnt_ns(struct mnt_namespace *);
|
||||
static struct mnt_namespace *alloc_mnt_ns(struct user_namespace *, bool);
|
||||
|
||||
void dissolve_on_fput(struct vfsmount *mnt)
|
||||
{
|
||||
struct mnt_namespace *ns;
|
||||
struct mount *m = real_mount(mnt);
|
||||
|
||||
/*
|
||||
@@ -2362,15 +2369,11 @@ void dissolve_on_fput(struct vfsmount *mnt)
|
||||
if (!anon_ns_root(m))
|
||||
return;
|
||||
|
||||
ns = m->mnt_ns;
|
||||
emptied_ns = m->mnt_ns;
|
||||
lock_mount_hash();
|
||||
umount_tree(m, UMOUNT_CONNECTED);
|
||||
unlock_mount_hash();
|
||||
}
|
||||
|
||||
/* Make sure we notice when we leak mounts. */
|
||||
VFS_WARN_ON_ONCE(!mnt_ns_empty(ns));
|
||||
free_mnt_ns(ns);
|
||||
}
|
||||
|
||||
static bool __has_locked_children(struct mount *mnt, struct dentry *dentry)
|
||||
@@ -2678,6 +2681,7 @@ static int attach_recursive_mnt(struct mount *source_mnt,
|
||||
} else {
|
||||
if (source_mnt->mnt_ns) {
|
||||
/* move from anon - the caller will destroy */
|
||||
emptied_ns = source_mnt->mnt_ns;
|
||||
for (p = source_mnt; p; p = next_mnt(p, source_mnt))
|
||||
move_from_ns(p);
|
||||
}
|
||||
@@ -3656,13 +3660,6 @@ static int do_move_mount(struct path *old_path,
|
||||
err = attach_recursive_mnt(old, p, mp.mp);
|
||||
out:
|
||||
unlock_mount(&mp);
|
||||
if (!err) {
|
||||
if (is_anon_ns(ns)) {
|
||||
/* Make sure we notice when we leak mounts. */
|
||||
VFS_WARN_ON_ONCE(!mnt_ns_empty(ns));
|
||||
free_mnt_ns(ns);
|
||||
}
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -6153,11 +6150,11 @@ void put_mnt_ns(struct mnt_namespace *ns)
|
||||
if (!refcount_dec_and_test(&ns->ns.count))
|
||||
return;
|
||||
namespace_lock();
|
||||
emptied_ns = ns;
|
||||
lock_mount_hash();
|
||||
umount_tree(ns->root, 0);
|
||||
unlock_mount_hash();
|
||||
namespace_unlock();
|
||||
free_mnt_ns(ns);
|
||||
}
|
||||
|
||||
struct vfsmount *kern_mount(struct file_system_type *type)
|
||||
|
||||
Reference in New Issue
Block a user