BUG: unable to handle kernel paging request in blk_mq_complete_request_remote (2)

Status: moderation: reported on 2026/11/15 18:48
Subsystems: block
Labels: prio:high
[Documentation on labels]
Reported-by: syzbot+e3f20f68978d64d72502@syzkaller.appspotmail.com
First crash: 4d01h, last: 4d01h
✨ AI-generated root cause summary:

During an NVMe controller reset failure, nvme_reset_work invokes nvme_dev_disable a second time after an earlier disable call had already cancelled and freed in-flight requests (clearing rq->mq_hctx to NULL in __blk_mq_free_request). Because request cancellation via nvme_cancel_request does not advance the request generation counter (genctr), and nvme_find_rq in nvme_handle_cqe only checks tag bounds and genctr matching without verifying if the request is active (blk_mq_request_started), nvme_find_rq returns an already-completed and freed struct request. When nvme_try_complete_req passes this request to blk_mq_complete_request_remote, dereferencing rq->mq_hctx->nr_ctx (offset 0x18e) results in a NULL pointer dereference crash.

▶ ▼ ✨ Full AI root cause analysis
Based on kernel commit 58c1eb84e977:

Bug Analysis & Technical Explanation

Overview and Manifestation

The crash occurs in blk_mq_complete_request_remote() (block/blk-mq.c:1317):

bool blk_mq_complete_request_remote(struct request *rq)
{
	WRITE_ONCE(rq->state, MQ_RQ_COMPLETE);

	/*
	 * For request which hctx has only one ctx mapping,
	 * or a polled request, always complete locally,
	 * it's pointless to redirect the completion.
	 */
	if ((rq->mq_hctx->nr_ctx == 1 &&
	     rq->mq_ctx->cpu == raw_smp_processor_id()) ||
	     rq->cmd_flags & REQ_POLLED)
		return false;
...

In blk_mq_complete_request_remote(), the expression rq->mq_hctx->nr_ctx accesses nr_ctx at offset 0x18e within struct blk_mq_hw_ctx. At the time of the fault, rq (0xffff0000c8ca9000) is a valid pointer to a struct request, but rq->mq_hctx is NULL. Consequently, KASAN intercepts an access to 0x000000000000018e (KASAN shadow address dfff800000000031), triggering a NULL-pointer dereference kernel Oops (ESR = 0x96000005: level 1 translation fault).


Root Cause Analysis

  1. Request Lifecycle and Stale Tag Mapping in blk-mq:

    • When a request is queued and started via blk_mq_start_request() (block/blk-mq.c:1375), it sets rq->state = MQ_RQ_IN_FLIGHT and saves the request into the driver tags array: rq->mq_hctx->tags->rqs[rq->tag] = rq;.
    • When a request finishes and is freed via blk_mq_free_request() (block/blk-mq.c:840) $\to$ __blk_mq_free_request() (block/blk-mq.c:828), rq->state transitions to MQ_RQ_IDLE and rq->mq_hctx is explicitly cleared to NULL (rq->mq_hctx = NULL;).
    • However, tags->rqs[tag] is not cleared upon request completion; it retains the pointer to the static request allocation.
  2. NVMe Cancellation vs. Generation Counter (genctr):

    • Under normal request completion (nvme_try_complete_req(), drivers/nvme/host/nvme.h:834), the controller increments rq->genctr (rq->genctr++).
    • When an NVMe controller is disabled, nvme_dev_disable() calls nvme_cancel_tagset(&dev->ctrl) $\to$ blk_mq_tagset_busy_iter() with callback nvme_cancel_request() (drivers/nvme/host/core.c:543).
    • nvme_cancel_request() sets nvme_req(req)->status = NVME_SC_HOST_ABORTED_CMD and calls blk_mq_complete_request(req) directly. It does not call nvme_try_complete_req() and does not advance rq->genctr. The cancelled requests are completed and freed, leaving rq->mq_hctx = NULL, while rq->genctr remains at the previous generation.
  3. Controller Reset Failure and Duplicate nvme_dev_disable():

    • During nvme_reset_work() (drivers/nvme/host/pci.c:3622):
      if (dev->ctrl.ctrl_config & NVME_CC_ENABLE)
          nvme_dev_disable(dev, false);
      nvme_sync_queues(&dev->ctrl);
      
      mutex_lock(&dev->shutdown_lock);
      result = nvme_pci_enable(dev);
      if (result)
          goto out_unlock;
      
    • In the initial nvme_dev_disable(dev, false), IO queues are suspended and nvme_cancel_tagset() terminates and frees all in-flight requests.
    • Next, nvme_pci_enable(dev) re-enables PCI device memory and bus mastering (pci_enable_device_mem(pdev) $\to$ "nvme 0000:00:02.0: enabling device (0201 -> 0203)", pci_set_master(pdev)).
    • If controller initialization fails immediately afterward (e.g., reading NVME_REG_CSTS returns -1, or result = -ENODEV (-19)), nvme_reset_work() branches to out: and prints "nvme nvme0: Disabling device after reset failure: -19".
    • In out:, nvme_dev_disable(dev, true) is called a second time.
  4. Reaping CQEs with Idle/Freed Requests:

    • In nvme_dev_disable(dev, true), line 3565 calls nvme_reap_pending_cqes(dev):
      static void nvme_reap_pending_cqes(struct nvme_dev *dev)
      {
          int i;
          for (i = dev->ctrl.queue_count - 1; i > 0; i--) {
              spin_lock(&dev->queues[i].cq_poll_lock);
              nvme_poll_cq(&dev->queues[i], NULL);
              spin_unlock(&dev->queues[i].cq_poll_lock);
          }
      }
      
    • If any completion queue entry is detected pending (nvme_cqe_pending() matching phase), nvme_handle_cqe() is invoked.
    • nvme_handle_cqe() looks up the request using nvme_find_rq(nvme_queue_tagset(nvmeq), command_id).
    • In nvme_find_rq() (drivers/nvme/host/nvme.h:692-712):
      rq = blk_mq_tag_to_rq(tags, tag);
      if (unlikely(!rq))
          return NULL;
      if (unlikely(nvme_genctr_mask(nvme_req(rq)->genctr) != genctr))
          return NULL;
      return rq;
      
    • Because genctr was never incremented during cancellation and nvme_find_rq() does not verify whether rq is currently active (e.g., via blk_mq_request_started(rq) / rq->state == MQ_RQ_IN_FLIGHT), nvme_find_rq() successfully returns the pointer to the freed, idle request.
    • nvme_handle_cqe() proceeds to complete the request via nvme_try_complete_req(), which calls blk_mq_complete_request_remote(rq).
    • blk_mq_complete_request_remote() dereferences rq->mq_hctx to read rq->mq_hctx->nr_ctx, dereferencing NULL at offset 0x18e and crashing the kernel.
▶ ▼ ✨ AI Jobs (2)
ID Workflow Result Correct Bug Created Started Finished Revision
f75abe1a repro-c Reproduced: ✅ ❓ BUG: unable to handle kernel paging request in blk_mq_complete_request_remote (2) 2026/11/14 00:00 2026/11/14 00:00 2026/11/14 01:30 819ebe34
8a183fcf assessment-security DenialOfService: ✅ Exploitable: ❌ FilesystemTrigger: ❌ FuzzingArtifact: ❌ MemoryCorruption: ❌ NetworkNeedsSetup: ❌ NetworkTrigger: ❌ PeripheralTrigger: ✅ RemoteTrigger: ❌ Unprivileged: ❌ UserNamespace: ❌ VMGuestTrigger: ❌ VMHostTrigger: ❌ ❓ BUG: unable to handle kernel paging request in blk_mq_complete_request_remote (2) 2026/11/11 18:07 2026/11/11 18:07 2026/11/11 18:35 8d68b303
▶ ▼ Similar bugs (2)
Kernel Title Rank 🛈 Repro Cause bisect Fix bisect Count Last Reported Patched Status
upstream BUG: unable to handle kernel paging request in blk_mq_complete_request_remote prio:high block 8 syz, C 71 6d12h 80d 0/29 closed as invalid on 2026/11/11 01:13
linux-6.1 BUG: unable to handle kernel paging request in blk_mq_complete_request_remote 8 3 27d 53d 0/3 upstream: reported on 2026/09/22 23:16
▶ ▼ Last patch testing requests (1)
Created Duration User Patch Repo Result
2026/11/14 05:10 2h42m retest repro git://git.kernel.org/pub/scm/linux/kernel/git/arm64/linux.git ad3fc05c7e65009355effeea06aa629a687acef2 report job log log

Sample crash report:
nvme 0000:00:02.0: enabling device (0201 -> 0203)
nvme nvme0: Disabling device after reset failure: -19
Unable to handle kernel paging request at virtual address dfff800000000031
KASAN: null-ptr-deref in range [0x0000000000000188-0x000000000000018f]
Mem abort info:
  ESR = 0x0000000096000005
  EC = 0x25: DABT (current EL), IL = 32 bits
  SET = 0, FnV = 0
  EA = 0, S1PTW = 0
  FSC = 0x05: level 1 translation fault
Data abort info:
  ISV = 0, ISS = 0x00000005, ISS2 = 0x00000000
  CM = 0, WnR = 0, TnD = 0, TagAccess = 0
  GCS = 0, Overlay = 0, DirtyBit = 0
[dfff800000000031] address between user and kernel address ranges
Internal error: Oops: 0000000096000005 [#1]  SMP
Modules linked in:
CPU: 0 UID: 0 PID: 12 Comm: kworker/u8:0 Not tainted syzkaller #0 PREEMPT 
Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 11/06/2026
Workqueue: nvme-reset-wq nvme_reset_work
pstate: 83400005 (Nzcv daif +PAN -UAO +TCO +DIT -SSBS BTYPE=--)
pc : blk_mq_complete_request_remote+0xc4/0x858 block/blk-mq.c:1317
lr : blk_mq_complete_request_remote+0x88/0x858 block/blk-mq.c:1309
sp : ffff80008fa47900
x29: ffff80008fa47980 x28: ffff80008fa47900 x27: 1fffe00018d0167c
x26: 0000000000000001 x25: ffff0000c680b3ee x24: dfff800000000000
x23: ffff700011f48f20 x22: 1fffe00019195224 x21: ffff0000c8ca9124
x20: 000000000000018e x19: ffff0000c8ca9000 x18: 1fffe000359cf228
x17: 0000000000000003 x16: ffff8000898b7000 x15: ffff80008996b2e0
x14: 00000000001dca26 x13: 0000000000000001 x12: 0000000000000004
x11: 0000000000000018 x10: ffff8000884fd674 x9 : 1fffe00019195202
x8 : 0000000000000031 x7 : ffff8000828b3b38 x6 : 0000000000000000
x5 : 0000000000000000 x4 : 0000000000000001 x3 : ffff8000828b7a4c
x2 : 0000000000000000 x1 : ffff0000c1a00000 x0 : 0000000000000000
Call trace:
 blk_mq_complete_request_remote+0xc4/0x858 block/blk-mq.c:1317 (P)
 nvme_try_complete_req drivers/nvme/host/nvme.h:842 [inline]
 nvme_handle_cqe drivers/nvme/host/pci.c:1822 [inline]
 nvme_poll_cq+0x544/0xfd8 drivers/nvme/host/pci.c:1853
 nvme_reap_pending_cqes drivers/nvme/host/pci.c:2311 [inline]
 nvme_dev_disable+0x384/0x540 drivers/nvme/host/pci.c:3565
 nvme_reset_work+0x510/0x69c drivers/nvme/host/pci.c:3727
 process_one_work kernel/workqueue.c:3495 [inline]
 process_scheduled_works+0x91c/0x1250 kernel/workqueue.c:3578
 worker_thread+0x798/0xbd0 kernel/workqueue.c:3659
 kthread+0x304/0x3d4 kernel/kthread.c:436
 ret_from_fork+0x10/0x20 arch/arm64/kernel/entry.S:854
Code: 97cf97f3 f9400288 91063914 d343fe88 (38f86908) 
---[ end trace 0000000000000000 ]---
----------------
Code disassembly (best guess):
   0:	97cf97f3 	bl	0xffffffffff3e5fcc
   4:	f9400288 	ldr	x8, [x20]
   8:	91063914 	add	x20, x8, #0x18e
   c:	d343fe88 	lsr	x8, x20, #3
* 10:	38f86908 	ldrsb	w8, [x8, x24] <-- trapping instruction

Crashes (1):
Time Kernel Commit Syzkaller Config Log Report Syz repro C repro VM info Assets (help?) Manager Title
2026/11/11 18:07 git://git.kernel.org/pub/scm/linux/kernel/git/arm64/linux.git for-kernelci 58c1eb84e977 8d68b303 .config console log report info [disk image] [vmlinux] [kernel image] ci-upstream-gce-arm64 BUG: unable to handle kernel paging request in blk_mq_complete_request_remote
* Struck through repros no longer work on HEAD.