blkmq: Fix NULL pointer deref when all reserved tags in use
PATCH 1/1 blkmq: Fix NULL pointer deref when all reserved tags in use
When allocating from the reserved tags pool, bt_get() is called with a NULL hctx. If all tags are in use, the hw queue is kicked to push out any pending IO, potentially freeing tags, and tag allocation is retried. The problem is that blk_mq_run_hw_queue() doesn't check for a NULL hctx. This patch fixes that bug.
An alternative implementation might skip kicking the queue for reserved tags and go right to io_schedule() but we chose to keep it simple.
Tested by hammering mtip32xx with concurrent smartctl/hdparm.
--- block/blk-mq.c | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-)
block/blk-mq.c +1 -1
@@ -887,7 +887,7 @@ static int blk_mq_hctx_next_cpu(struct blk_mq_hw_ctx *hctx)void blk_mq_run_hw_queue (struct blk_mq_hw_ctx *hctx,bool async) { -if (unlikely (test_bit (BLK_MQ_S_STOPPED , &hctx->state) || +if (unlikely (!hctx ||test_bit (BLK_MQ_S_STOPPED , &hctx->state) || !blk_mq_hw_queue_mapped (hctx)))return ;
Signature · 1 line
1.7.1
Jens Axboe
maintainerQuoted 22 lines · Sam Bradshaw
When allocating from the reserved tags pool,
bt_get()is called with a NULL hctx. If all tags are in use, the hw queue is kicked to push out any pending IO, potentially freeing tags, and tag allocation is retried. The problem is thatblk_mq_run_hw_queue()doesn't check for a NULL hctx. This patch fixes that bug.An alternative implementation might skip kicking the queue for reserved tags and go right to
io_schedule()but we chose to keep it simple.Tested by hammering mtip32xx with concurrent smartctl/hdparm.
Signed-off-by Sam Bradshaw <[email protected]>Signed-off-by Selvan Mani <[email protected]>--- block/blk-mq.c | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-)block/blk-mq.c +0
-0
block/blk-mq.c static int blk_mq_hctx_next_cpu(struct blk_mq_hw_ctx *hctx) · line 892
Quoted 7 lines
block/blk-mq.c +1
-1@@ -887,7 +887,7 @@ static int blk_mq_hctx_next_cpu(struct blk_mq_hw_ctx *hctx)void blk_mq_run_hw_queue (struct blk_mq_hw_ctx *hctx,bool async) { -if (unlikely (test_bit (BLK_MQ_S_STOPPED , &hctx->state) || +if (unlikely (!hctx ||test_bit (BLK_MQ_S_STOPPED , &hctx->state) || !blk_mq_hw_queue_mapped (hctx)))return ;
Good catch! But why not put the hctx == NULL check in as a conditional in bt_get() before running the queue? I can't imagine other cases where calling blk_mq_run_hw_queue() with hctx == NULL would be a valid scenario.
The bug was introduced with commit b32232073e80, so that should probably be indicated too.
Signature · 1 line
Jens Axboe
Quoted 4 lines
Good catch! But why not put the hctx == NULL check in as a conditional in
bt_get()before running the queue? I can't imagine other cases where callingblk_mq_run_hw_queue()with hctx == NULL would be a valid scenario.
The change was meant to be broad in scope. A runtime NULL deref is a rather unfortunate way to determine that there are other invalid scenarios.
But given that both approaches fix the immediate problem, I'd be happy to change the patch as you suggest.
Jens Axboe
maintainerQuoted 8 lines · Sam Bradshaw
Quoted 5 lines
Good catch! But why not put the hctx == NULL check in as a conditional in
bt_get()before running the queue? I can't imagine other cases where callingblk_mq_run_hw_queue()with hctx == NULL would be a valid scenario.The change was meant to be broad in scope. A runtime NULL deref is a rather unfortunate way to determine that there are other invalid scenarios.
Normally those kinds of issues would trigger quickly and it'd be obvious. Granted, this was an error handling case for the special case of reserved tags, so it persisted longer than it should have... But in general, I would not be too worried.
Quoted 2 lines
But given that both approaches fix the immediate problem, I'd be happy to change the patch as you suggest.
Thanks!
Signature · 1 line
Jens Axboe