[PATCH AUTOSEL 6.18] nvme: validate FDP configuration descriptor sizes

Sasha Levin <[email protected]>
Newsgroups org.infradead.lists.linux-nvme,dev.linux.lists.patches,org.kernel.vger.linux-kernel,org.kernel.vger.stable
Message-ID <[email protected]>
From: liuxixin <[email protected]>

[ Upstream commit 0ef4daa6534a510d61ea67c8ad9bb5097b0dd5f8 ]

Validate descriptor sizes while walking the FDP configurations log so
dsze == 0 or a descriptor past the log end cannot cause unbounded
iteration or reads past the buffer.

Reviewed-by: Nitesh Shetty <[email protected]>
Reviewed-by: Christoph Hellwig <[email protected]>
Signed-off-by: liuxixin <[email protected]>
Signed-off-by: Keith Busch <[email protected]>
Signed-off-by: Sasha Levin <[email protected]>
---

LLM Generated explanations, may be completely bogus:

# Stable Backport Analysis: `nvme: validate FDP configuration descriptor
sizes`

**Local tree:** Linux **6.18.44** (`stable/linux-6.18.y`, `HEAD`
detached at `v6.18.44`)
**Mainline commit:** `0ef4daa6534a5` — **not present** in this tree;
buggy code **is present**

---

## PHASE 1: COMMIT MESSAGE FORENSICS

### Step 1.1: Subject line
**Record:** `[nvme]` `[validate]` — Validate FDP configuration
descriptor sizes while walking the FDP configurations log.

### Step 1.2: Tags
**Record:**
- **Reviewed-by:** Nitesh Shetty `<[email protected]>`
- **Reviewed-by:** Christoph Hellwig `<[email protected]>`
- **Signed-off-by:** liuxixin `<[email protected]>`
- **Signed-off-by:** Keith Busch `<[email protected]>`
- No `Fixes:`, `Reported-by:`, `Link:`, `Cc: [email protected]`, or
  `Tested-by:` tags
- Notable: reviewed by NVMe/block maintainers; no syzbot report

### Step 1.3: Body analysis
**Record:**
- **Bug:** Walking FDP configuration descriptors uses `dsze` from
  device-provided log data without validating each descriptor size
  before advancing.
- **Symptoms:** `dsze == 0` or a descriptor extending past the log end
  can cause invalid iteration over the log buffer and reads past the
  allocated buffer.
- **Root cause:** Size is applied (`log += dsze`) before bounds are
  checked; zero-sized descriptors are not rejected.
- **Version info:** None in commit message.

### Step 1.4: Hidden bug fix?
**Record:** Yes — this is a defensive parsing fix for malformed or
malicious device log data, not cosmetic cleanup. It prevents out-of-
bounds reads and incorrect descriptor traversal during namespace setup
on FDP-capable NVMe controllers.

---

## PHASE 2: DIFF ANALYSIS

### Step 2.1: Inventory
**Record:**
- **Files:** `drivers/nvme/host/core.c` (+6/−4 lines)
- **Function:** `nvme_query_fdp_granularity()`
- **Scope:** Single-file, surgical fix in one loop

### Step 2.2: Code flow change
**Record:**

| Hunk | Before | After |
|------|--------|-------|
| Descriptor walk loop | Advance `log` by `desc->dsze`, then check `log
>= end` | Read `dsze`, reject `!dsze` or `log + dsze > end`, then
advance |
| Error message | Generic `"FDP invalid config descriptor list"` |
Specific `"FDP invalid config descriptor at index %d"` |

**Path affected:** Error/validation path during FDP granularity query at
namespace enumeration.

### Step 2.3: Bug mechanism
**Record:**
- **Category:** Buffer overflow / out-of-bounds read (memory safety);
  logic error on malformed descriptors
- **Mechanism:**
  1. **`dsze == 0`:** Pointer never advances; walk does not reach the
     intended `fdp_idx` descriptor; subsequent reads of `desc->nrg` and
     `desc->runs` use wrong data.
  2. **`log + dsze > end`:** Old code advanced first, then checked. A
     large `dsze` sets `desc` past the buffer before the check;
     depending on iteration count and layout, subsequent field reads
     (`nrg` at offset 4, `runs` at offset 12 in `struct
     nvme_fdp_config_desc`) can access memory beyond the `kvmalloc`'d
     log buffer.
  3. New code validates **before** advancing, rejecting zero or over-
     length descriptors.

### Step 2.4: Fix quality
**Record:**
- Fix is minimal, obviously correct, and matches standard kernel parsing
  practice.
- Reviewed by Christoph Hellwig and Keith Busch (applied to `nvme-7.2`).
- **Regression risk:** Very low — only affects the error path for
  invalid FDP log data; valid devices unchanged.
- v5 cover letter notes removal of redundant `log >= end` check per
  maintainer feedback.

---

## PHASE 3: GIT HISTORY INVESTIGATION

### Step 3.1: Blame
**Record:** Buggy loop introduced in `30b5f20bb2dda` ("nvme: register
fdp parameters with the block layer", Keith Busch, 2025-05-06). First
appeared in **v6.16**. Present in this 6.18.44 tree.

### Step 3.2: Fixes: tag
**Record:** N/A — no `Fixes:` tag. Introducing commit `30b5f20bb2dda` is
in this tree.

### Step 3.3: Related file history
**Record:**
- `5e406928404d6` — "nvme: fix FDP fdpcidx bounds check" (same author) —
  **already in 6.18.y**
- `0ef4daa6534a5` — this descriptor-size validation — **not in 6.18.y**
  (only on `master`)
- Companion fix from v4 series; v5 split out descriptor validation
  separately per maintainer feedback

### Step 3.4: Author context
**Record:** liuxixin contributed the related fdpcidx bounds fix already
backported to 6.18.y. Keith Busch (NVMe maintainer) committed both
fixes.

### Step 3.5: Dependencies
**Record:** Standalone — no series dependencies. Requires FDP code from
`30b5f20bb2dda`, which is present. `git apply --check` confirms clean
apply to current tree.

---

## PHASE 4: MAILING LIST AND EXTERNAL RESEARCH

### Step 4.1: Original discussion
**Record:**
- **b4 dig URL:** https://patch.msgid.link/e6f7a8b9c0d1e2f3a4b5c6d7e8f9a
  [email protected]
- **Series:** v1→v2 (combined parsing fix) → v4 (split: fdpcidx +
  descriptor validation) → v5 (descriptor validation only)
- Keith Busch applied v5 to `nvme-7.2`; no NAKs found in thread

### Step 4.2: Reviewers
**Record:** CC'd: `[email protected]`, `[email protected]`,
`[email protected]`, `[email protected]`, `[email protected]`, `linux-
[email protected]`

### Step 4.3: Bug report
**Record:** No external bug report or syzbot link. v5 cover letter
documents testing with **fdp-lab**: `dsze==0` and walk-past-end cases
produce `"FDP invalid config descriptor at index %d"`.

### Step 4.4: Related patches
**Record:** Descriptor validation was originally v4 2/2; split to v5
after fdpcidx fix (v4 1/2) was applied separately. fdpcidx fix is
already in 6.18.y; this patch is the remaining half.

### Step 4.5: Stable list
**Record:** No `Cc: stable` discussion found in mbox thread.

---

## PHASE 5: CODE SEMANTIC ANALYSIS

### Step 5.1: Key functions
**Record:** `nvme_query_fdp_granularity()` (modified); callers via
`nvme_query_fdp_info()`.

### Step 5.2: Callers
**Record:**
- `nvme_query_fdp_info()` ← `nvme_update_ns_info_block()` (line 2373)
- `nvme_update_ns_info()` ← `nvme_alloc_ns()` (line 4163),
  `nvme_validate_ns()` (line 4300)
- `nvme_scan_ns()` → `nvme_alloc_ns()` / `nvme_validate_ns()` during
  namespace scan

### Step 5.3: Callees
**Record:** `nvme_get_log_lsi()`, `kvmalloc()`, `kvfree()`,
`le16_to_cpu()`, `le32_to_cpu()`, `le64_to_cpu()`, `dev_warn()`.

### Step 5.4: Reachability
**Record:**
- Triggered when `ns->ctrl->ctratt & NVME_CTRL_ATTR_FDPS` and FDP
  feature enabled (`FDPCFG_FDPE`)
- Runs during NVMe namespace enumeration (probe/rescan/AEN paths)
- Device-controlled log data from PCIe NVMe hardware — reachable
  whenever an FDP-capable controller is attached
- Not a general syscall path, but attacker with physical PCIe access or
  VFIO passthrough of a malicious device can supply crafted log data

### Step 5.5: Similar patterns
**Record:** Related fdpcidx bounds fix (`5e406928404d6`, already in
tree) addresses a separate off-by-one in the same function. This patch
completes FDP log parsing hardening.

---

## PHASE 6: CROSS-REFERENCE WITH LOCAL TREE (6.18.44)

### Step 6.1: Buggy code present?
**Record:** **Yes.** Current tree at lines 2243–2251 still has the pre-
fix loop:

```2243:2251:drivers/nvme/host/core.c
        for (i = 0; i < fdp_idx; i++) {
                log += le16_to_cpu(desc->dsze);
                desc = log;
                if (log >= end) {
                        dev_warn(ctrl->device,
                                 "FDP invalid config descriptor
list\n");
                        ret = 0;
                        goto out;
                }
        }
```

FDP support landed in v6.16; present throughout 6.18.y.

### Step 6.2: Backport complications
**Record:** **Clean apply** — `git apply --check` succeeds with no
conflicts. No rework needed.

### Step 6.3: Related fixes already present?
**Record:** `5e406928404d6` (fdpcidx bounds check) is in 6.18.y.
Descriptor-size validation (`0ef4daa6534a5`) is **not**.

---

## PHASE 7: SUBSYSTEM CONTEXT

### Step 7.1: Subsystem criticality
**Record:** `drivers/nvme/host/` — **IMPORTANT** (block storage, widely
deployed; niche FDP subset)

### Step 7.2: Activity
**Record:** NVMe/FDP code is actively maintained; FDP registration added
in 6.16, with follow-up fixes in 2026.

---

## PHASE 8: IMPACT AND RISK ASSESSMENT

### Step 8.1: Who is affected
**Record:** Users of NVMe controllers reporting `NVME_CTRL_ATTR_FDPS`
with FDP enabled. Growing but still limited hardware base (datacenter
SSDs with Flexible Data Placement). All such users on 6.18.y without
this fix.

### Step 8.2: Trigger conditions
**Record:**
- Controller advertises FDPS; namespace scan queries FDP configuration
  log
- Malformed firmware response or malicious device provides `dsze == 0`
  or oversized `dsze`
- Triggered at device attach/rescan — not everyday, but automatic on
  enumeration
- Physical attacker or compromised passthrough device can trigger

### Step 8.3: Failure mode severity
**Record:**
- **Out-of-bounds read** of kernel heap buffer → potential oops/crash or
  information leak — **HIGH**
- **Incorrect parsing** with zero `dsze` → wrong granularity registered
  — **MEDIUM**
- Not a typical soft-lockup (loop bounded by `u8 fdp_idx`, max 255),
  despite "unbounded iteration" wording referring to buffer traversal
- **Overall severity: HIGH** (memory safety on device-driven kernel
  parsing)

### Step 8.4: Risk-benefit
**Record:**
- **Benefit:** HIGH for FDP users; prevents OOB reads from device-
  controlled data
- **Risk:** VERY LOW — 6-line validation in error path, reviewed by
  maintainers, tested with fdp-lab
- **Ratio:** Strong benefit, minimal risk

---

## PHASE 9: FINAL SYNTHESIS

### Step 9.1: Evidence summary

**FOR backport:**
- Real memory-safety bug in kernel parsing of device-provided data
- Buggy code present in 6.18.44; fix not yet applied
- Small, surgical, maintainer-reviewed fix
- Clean apply to current tree
- Companion fdpcidx fix already backported — this is the natural follow-
  up
- fdp-lab test coverage documented

**AGAINST backport:**
- Only affects FDP-capable NVMe hardware (limited audience)
- No syzbot/CVE report
- No explicit stable nomination on mailing list

**Unresolved:** None blocking decision.

### Step 9.2: Stable rules checklist
1. Obviously correct and tested? **PASS** — reviewed by Hellwig/Shetty;
   fdp-lab tested
2. Fixes a real bug? **PASS** — OOB read / invalid descriptor walk on
   malformed FDP log
3. Important issue? **PASS** — memory safety, potential crash/info leak
   from device data
4. Small and contained? **PASS** — ~6 lines, one function
5. No new features/APIs? **PASS** — validation only
6. Can apply to local tree? **PASS** — clean apply verified

### Step 9.3: Exception categories
**Record:** None (not device ID, quirk, DT, build, or docs). Standard
bug-fix category.

### Step 9.4: Decision rationale
For **Linux 6.18.y** specifically: FDP support and the buggy descriptor-
walk code are both present; the fix is absent; the patch applies
cleanly; and it closes a real memory-safety hole in device log parsing
during namespace enumeration. The limited hardware scope lowers urgency
slightly, but stable rules favor small, correct fixes for crash/OOB-read
bugs in production drivers — especially when a related fix from the same
series is already in the tree.

---

## Verification

- **[Phase 1]** Parsed commit message and tags from `git show
  0ef4daa6534a5`
- **[Phase 2]** Diff analysis: 6 lines changed in
  `nvme_query_fdp_granularity()` loop
- **[Phase 3]** `git blame -L 2240,2252`: buggy code from
  `30b5f20bb2dda` (2025-05-06)
- **[Phase 3]** `git merge-base --is-ancestor 30b5f20bb2dda HEAD`: FDP
  code in 6.18.y
- **[Phase 3]** `git merge-base --is-ancestor 0ef4daa6534a5 HEAD`: fix
  **not** in tree
- **[Phase 3]** `git tag -l 'v6.16*' --contains 30b5f20bb2dda`: FDP
  introduced in v6.16
- **[Phase 3]** `git apply --check`: patch applies cleanly
- **[Phase 3]** `5e406928404d6` already in 6.18.y (related fdpcidx fix)
- **[Phase 4]** `b4 dig -c 0ef4daa6534a5`: lore URL found
- **[Phase 4]** `b4 dig -a`: v1→v5 series evolution documented
- **[Phase 4]** `b4 dig -w`: maintainers CC'd (Busch, Axboe, Hellwig,
  Shetty)
- **[Phase 4]** `b4 dig -m /tmp/fdp_validate.mbox`: v5 cover letter with
  fdp-lab test plan
- **[Phase 4]** No stable-list nomination in mbox
- **[Phase 5]** `grep nvme_query_fdp_info`: call chain through
  `nvme_update_ns_info_block` → `nvme_scan_ns`
- **[Phase 5]** Read `struct nvme_fdp_config_desc` in
  `include/linux/nvme.h` (lines 705–717)
- **[Phase 6]** `git describe HEAD` / `make kernelversion`: **6.18.44**
- **[Phase 6]** Read current `core.c` lines 2243–2251: pre-fix code
  confirmed
- **[Phase 6]** `git merge-base --is-ancestor 30b5f20bb2dda
  stable/linux-6.12.y`: FDP **not** in 6.12.y (noted; verdict is for
  6.18.y only)
- **[Phase 8]** Failure mode: OOB read from device-controlled FDP log →
  HIGH severity

**YES**

 drivers/nvme/host/core.c | 10 ++++++----
 1 file changed, 6 insertions(+), 4 deletions(-)

diff --git a/drivers/nvme/host/core.c b/drivers/nvme/host/core.c
index 43cbbcd3796b1..5ea331e933c55 100644
--- a/drivers/nvme/host/core.c
+++ b/drivers/nvme/host/core.c
@@ -2241,14 +2241,16 @@ static int nvme_query_fdp_granularity(struct nvme_ctrl *ctrl,
 	desc = log;
 	end = log + size - sizeof(*h);
 	for (i = 0; i < fdp_idx; i++) {
-		log += le16_to_cpu(desc->dsze);
-		desc = log;
-		if (log >= end) {
+		u16 dsze = le16_to_cpu(desc->dsze);
+
+		if (!dsze || log + dsze > end) {
 			dev_warn(ctrl->device,
-				 "FDP invalid config descriptor list\n");
+				 "FDP invalid config descriptor at index %d\n", i);
 			ret = 0;
 			goto out;
 		}
+		log += dsze;
+		desc = log;
 	}
 
 	if (le32_to_cpu(desc->nrg) > 1) {
-- 
2.53.0
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.