]> git.proxmox.com Git - mirror_zfs.git/commitdiff
Fix arc_prune_task use-after-free
authorChunwei Chen <david.chen@osnexus.com>
Mon, 23 May 2016 18:58:21 +0000 (11:58 -0700)
committerBrian Behlendorf <behlendorf1@llnl.gov>
Wed, 25 May 2016 21:11:53 +0000 (14:11 -0700)
arc_prune_task uses a refcount to protect arc_prune_t, but it doesn't prevent
the underlying zsb from disappearing if there's a concurrent umount. We fix
this by force the caller of arc_remove_prune_callback to wait for
arc_prune_taskq to finish.

Signed-off-by: Chunwei Chen <david.chen@osnexus.com>
Signed-off-by: Brian Behlendorf <behlendorf1@llnl.gov>
Closes #4687
Closes #4690

module/zfs/arc.c

index 716ba5c2d95d6cdff97879c8fa07ebd7ccc25757..18e9a145d3ffafbab31fe1570b1dcf964d5e0f7b 100644 (file)
@@ -2707,12 +2707,7 @@ arc_prune_task(void *ptr)
        if (func != NULL)
                func(ap->p_adjust, ap->p_private);
 
-       /* Callback unregistered concurrently with execution */
-       if (refcount_remove(&ap->p_refcnt, func) == 0) {
-               ASSERT(!list_link_active(&ap->p_node));
-               refcount_destroy(&ap->p_refcnt);
-               kmem_free(ap, sizeof (*ap));
-       }
+       refcount_remove(&ap->p_refcnt, func);
 }
 
 /*
@@ -4628,13 +4623,19 @@ arc_add_prune_callback(arc_prune_func_t *func, void *private)
 void
 arc_remove_prune_callback(arc_prune_t *p)
 {
+       boolean_t wait = B_FALSE;
        mutex_enter(&arc_prune_mtx);
        list_remove(&arc_prune_list, p);
-       if (refcount_remove(&p->p_refcnt, &arc_prune_list) == 0) {
-               refcount_destroy(&p->p_refcnt);
-               kmem_free(p, sizeof (*p));
-       }
+       if (refcount_remove(&p->p_refcnt, &arc_prune_list) > 0)
+               wait = B_TRUE;
        mutex_exit(&arc_prune_mtx);
+
+       /* wait for arc_prune_task to finish */
+       if (wait)
+               taskq_wait_outstanding(arc_prune_taskq, 0);
+       ASSERT0(refcount_count(&p->p_refcnt));
+       refcount_destroy(&p->p_refcnt);
+       kmem_free(p, sizeof (*p));
 }
 
 void