Fix arc_prune_task use-after-free
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
This commit is contained in:
parent
d5b0e7fcf1
commit
9f5f758d77
|
@ -2697,12 +2697,7 @@ arc_prune_task(void *ptr)
|
||||||
if (func != NULL)
|
if (func != NULL)
|
||||||
func(ap->p_adjust, ap->p_private);
|
func(ap->p_adjust, ap->p_private);
|
||||||
|
|
||||||
/* Callback unregistered concurrently with execution */
|
refcount_remove(&ap->p_refcnt, func);
|
||||||
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));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -4568,14 +4563,20 @@ arc_add_prune_callback(arc_prune_func_t *func, void *private)
|
||||||
void
|
void
|
||||||
arc_remove_prune_callback(arc_prune_t *p)
|
arc_remove_prune_callback(arc_prune_t *p)
|
||||||
{
|
{
|
||||||
|
boolean_t wait = B_FALSE;
|
||||||
mutex_enter(&arc_prune_mtx);
|
mutex_enter(&arc_prune_mtx);
|
||||||
list_remove(&arc_prune_list, p);
|
list_remove(&arc_prune_list, p);
|
||||||
if (refcount_remove(&p->p_refcnt, &arc_prune_list) == 0) {
|
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);
|
refcount_destroy(&p->p_refcnt);
|
||||||
kmem_free(p, sizeof (*p));
|
kmem_free(p, sizeof (*p));
|
||||||
}
|
}
|
||||||
mutex_exit(&arc_prune_mtx);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
void
|
||||||
arc_set_callback(arc_buf_t *buf, arc_evict_func_t *func, void *private)
|
arc_set_callback(arc_buf_t *buf, arc_evict_func_t *func, void *private)
|
||||||
|
|
Loading…
Reference in New Issue