Message ID | 1583923070-22245-1-git-send-email-stummala@codeaurora.org (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | block: Fix use-after-free issue accessing struct io_cq | expand |
On 3/11/20 4:37 AM, Sahitya Tummala wrote: > There is a potential race between ioc_release_fn() and > ioc_clear_queue() as shown below, due to which below kernel > crash is observed. It also can result into use-after-free > issue. > > context#1: context#2: > ioc_release_fn() __ioc_clear_queue() gets the same icq > ->spin_lock(&ioc->lock); ->spin_lock(&ioc->lock); > ->ioc_destroy_icq(icq); > ->list_del_init(&icq->q_node); > ->call_rcu(&icq->__rcu_head, > icq_free_icq_rcu); > ->spin_unlock(&ioc->lock); > ->ioc_destroy_icq(icq); > ->hlist_del_init(&icq->ioc_node); > This results into below crash as this memory > is now used by icq->__rcu_head in context#1. > There is a chance that icq could be free'd > as well. > > 22150.386550: <6> Unable to handle kernel write to read-only memory > at virtual address ffffffaa8d31ca50 Fix looks good to me, applied.
diff --git a/block/blk-ioc.c b/block/blk-ioc.c index 5ed59ac..9df50fb 100644 --- a/block/blk-ioc.c +++ b/block/blk-ioc.c @@ -84,6 +84,7 @@ static void ioc_destroy_icq(struct io_cq *icq) * making it impossible to determine icq_cache. Record it in @icq. */ icq->__rcu_icq_cache = et->icq_cache; + icq->flags |= ICQ_DESTROYED; call_rcu(&icq->__rcu_head, icq_free_icq_rcu); } @@ -212,15 +213,21 @@ static void __ioc_clear_queue(struct list_head *icq_list) { unsigned long flags; + rcu_read_lock(); while (!list_empty(icq_list)) { struct io_cq *icq = list_entry(icq_list->next, struct io_cq, q_node); struct io_context *ioc = icq->ioc; spin_lock_irqsave(&ioc->lock, flags); + if (icq->flags & ICQ_DESTROYED) { + spin_unlock_irqrestore(&ioc->lock, flags); + continue; + } ioc_destroy_icq(icq); spin_unlock_irqrestore(&ioc->lock, flags); } + rcu_read_unlock(); } /** diff --git a/include/linux/iocontext.h b/include/linux/iocontext.h index dba15ca..1dcd919 100644 --- a/include/linux/iocontext.h +++ b/include/linux/iocontext.h @@ -8,6 +8,7 @@ enum { ICQ_EXITED = 1 << 2, + ICQ_DESTROYED = 1 << 3, }; /*