Skip to content

Validate the NSID before indexing nvmev_vdev->ns[]#74

Open
jongukc wants to merge 1 commit into
snu-csl:mainfrom
jongukc:fix/nsid-bounds-check
Open

Validate the NSID before indexing nvmev_vdev->ns[]#74
jongukc wants to merge 1 commit into
snu-csl:mainfrom
jongukc:fix/nsid-bounds-check

Conversation

@jongukc

@jongukc jongukc commented Jun 18, 2026

Copy link
Copy Markdown

Summary

Some command handlers use the NSID from the host command to index nvmev_vdev->ns[] before checking whether the NSID is valid.

nvmev_vdev->ns[] has only nvmev_vdev->nr_ns entries.
So an invalid NSID can access the array out of bounds.

This can happen in two cases:

  • NSID 0
    • 0 - 1 becomes (size_t)-1
  • NSID greater than nr_ns

Affected paths:

  • admin.c __nvmev_admin_identify_namespace
    • reads ns[nsid].size
  • admin.c __nvmev_admin_identify_namespace_desc
    • reads ns[nsid].csi
  • admin.c __nvmev_admin_identify_zns_namespace
    • reads ns[nsid].ftls and ns[nsid].csi
    • also calls NS_SSD_TYPE(nsid), which indexes a 2-entry array
  • io.c __nvmev_proc_io
    • uses ns = &ns[nsid]
    • then calls ns->proc_io_cmd(...)

A valid NVMe controller should not index its internal namespace table with an invalid NSID.

For Identify, it should return a zero-filled structure for an inactive or invalid NSID.
For I/O, it should return Invalid Namespace.

Description

With KASAN enabled, the admin Identify path triggers a slab out-of-bounds read.

The I/O path is more dangerous. It can also call a garbage function pointer.
This happens because ns->proc_io_cmd is read from an invalid struct nvmev_ns.

This was reproduced on unpatched Linux 7.1.0 by running Identify Namespace with NSID 2.
In this setup, only NSID 1 exists, so NSID 2 accesses ns[1].

BUG: KASAN: slab-out-of-bounds in nvmev_proc_admin_sq+0x2b31/0x3120 [nvmev]
Read of size 8 at addr ff1100015c0cadc8 by task nvmev_dispatche/309
CPU: 2 UID: 0 PID: 309 Comm: nvmev_dispatche Tainted: G           O        7.1.0
  nvmev_proc_admin_sq+0x2b31/0x3120 [nvmev]
  nvmev_dispatcher+0x221/0x9f0 [nvmev]
Kernel panic - not syncing: KASAN: panic_on_warn set

This bug was found during fuzzing.

Linux 7.1.0 x86_64, CONFIG_KASAN=y, panic_on_warn=1, kasan.fault=panic, running under QEMU/KVM.

Reproduction

On the test machine, with NVMeVirt probed as /dev/nvme0 (block device /dev/nvme0n1).
See nsid-oob-repro.sh:

# (1) Admin Identify Namespace for NSID 0  -> ns[(size_t)-1]
#     opcode 0x06 = Identify, CDW10 (CNS) = 0 = Identify Namespace
nvme admin-passthru /dev/nvme0 --opcode=0x06 --namespace-id=0 \
     --data-len=4096 --read --cdw10=0

# (2) Admin Identify Namespace for an NSID beyond nr_ns (e.g. 2) -> ns[1]
nvme admin-passthru /dev/nvme0 --opcode=0x06 --namespace-id=2 \
     --data-len=4096 --read --cdw10=0

Either command crashes the unpatched dispatcher kthread.

Fix

Validate the 1-based NSID against nvmev_vdev->nr_ns before every ns[] access:

  • Identify Namespace / Namespace Descriptor: an inactive/invalid NSID returns a zero-filled structure (the host treats NSZE 0 as "not present"), per the spec.
  • Identify ZNS Namespace: also moves the validity check ahead of the NS_SSD_TYPE(nsid) lookup (itself a fixed 2-element array).
  • I/O dispatch: an I/O to an unbacked NSID is completed with NVME_SC_INVALID_NS | NVME_SC_DNR instead of dereferencing ns[].

Validated on Linux 7.1.0.

The Identify Namespace / Identify Namespace Descriptor / Identify ZNS
Namespace handlers and the I/O dispatch path index nvmev_vdev->ns[] with
(cmd->nsid - 1) taken straight from the host command, with no bounds
check. nvmev_vdev->ns[] only holds nvmev_vdev->nr_ns elements, so an
Identify or I/O for NSID 0 (-> (size_t)-1) or for an NSID greater than
nr_ns indexes the array out of bounds.

This is reachable whenever the host issues Identify/I-O for an NSID the
device does not back -- e.g. during a controller rescan, or directly via
an admin/I-O passthrough command -- and leads to a KASAN slab-out-of-
bounds read of ns[].size and, for the I/O path, a call through a garbage
ns->proc_io_cmd function pointer (general protection fault).

  BUG: KASAN: slab-out-of-bounds in nvmev_proc_admin_sq [nvmev]
  Read of size 8 ... 8 bytes to the right of the 64-byte region (ns[1])
  general protection fault in nvmev_proc_io_sq  (ns[n].proc_io_cmd)

Validate the 1-based NSID against nr_ns before every ns[] access:
  - Identify Namespace / Namespace Descriptor: an inactive/invalid NSID
    returns a zero-filled structure, as the spec requires (the host
    treats a zero NSZE as "namespace not present").
  - Identify ZNS Namespace: also reorders the check ahead of the
    NS_SSD_TYPE(nsid) lookup, which itself indexes a 2-element array.
  - I/O dispatch: an I/O to an unbacked NSID is completed with
    NVME_SC_INVALID_NS | NVME_SC_DNR instead of dereferencing ns[].

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant