Skip to content

Commit

Permalink
Fix arc_prune_task use-after-free
Browse files Browse the repository at this point in the history
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 openzfs#4687
Closes openzfs#4690
  • Loading branch information
Chunwei Chen authored and nedbass committed Sep 9, 2016
1 parent d5b0e7f commit 9f5f758
Showing 1 changed file with 11 additions and 10 deletions.
21 changes: 11 additions & 10 deletions module/zfs/arc.c
Original file line number Diff line number Diff line change
Expand Up @@ -2697,12 +2697,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);
}

/*
Expand Down Expand Up @@ -4568,13 +4563,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
Expand Down

0 comments on commit 9f5f758

Please sign in to comment.