[PATCH AUTOSEL 6.18-6.6] nvmem: brcm_nvram: reject empty NVRAM partition

From: Sasha Levin

Date: Mon Oct 19 2026 - 10:55:12 EST


From: Rosen Penev <rosenp@xxxxxxxxx>

[ Upstream commit fde46579cf8872138b296349c2bd8a3cd3f132e2 ]

If the partition is completely erased (all padding bytes), the trimming
loop reduces data_len to 0. devm_kzalloc(dev, 0, GFP_KERNEL) returns
ZERO_SIZE_PTR ((void *)16), which is non-NULL and bypasses the NULL
check. The subsequent cast of priv->data to struct brcm_nvram_header *
and dereference of header->magic causes a page fault on address 0x10.

Reject data_len smaller than the header before allocating.

Assisted-by: opencode:big-pickle
Signed-off-by: Rosen Penev <rosenp@xxxxxxxxx>
Signed-off-by: Srinivas Kandagatla <srini@xxxxxxxxxx>
Link: https://patch.msgid.link/20260729094647.111468-13-srini@xxxxxxxxxx
Signed-off-by: Greg Kroah-Hartman <gregkh@xxxxxxxxxxxxxxxxxxx>
Signed-off-by: Sasha Levin <sashal@xxxxxxxxxx>
---

LLM Generated explanations, may be completely bogus:

Local tree is **6.6.157** (`pending-6.6`). The empty-NVRAM oops path is
present here and the 5-line guard is not. This is a real boot-time page
fault on Broadcom Northstar boards, and the patch applies cleanly.

===============================================================
PHASE 1: COMMIT MESSAGE FORENSICS
===============================================================

Step 1.1: PARSE THE SUBJECT LINE
Record: **[nvmem/brcm_nvram] [reject] reject empty NVRAM partition**
- Subsystem: `nvmem: brcm_nvram`
- Verb: **reject** — defensive check, not a feature
- Intent: refuse to probe when the cached NVRAM image is smaller than
the on-flash header

Step 1.2: PARSE ALL COMMIT MESSAGE TAGS
Record:
- **Fixes:** none (expected for this review pipeline)
- **Reported-by / Tested-by / Reviewed-by / Acked-by:** none
- **Assisted-by:** `opencode:big-pickle`
- **Signed-off-by:** Rosen Penev (author); Srinivas Kandagatla (nvmem
maintainer); Greg Kroah-Hartman (char-misc)
- **Link:**
`https://patch.msgid.link/20260729094647.111468-13-srini@xxxxxxxxxx` —
this is patch 12/14 of Srinivas’s later char-misc pull, not a
dependency series
- **Cc: stable:** none (not a negative signal)
- Original submission was a standalone `[PATCH]`
(`20260703222830.408764-1-rosenp@xxxxxxxxx`), applied as mainline
`5f75913830bc`

Step 1.3: ANALYZE THE COMMIT BODY TEXT
Record:
- **Bug:** erased NVRAM (all padding bytes) makes the trim loop set
`data_len = 0`
- **Symptom:** `devm_kzalloc(..., 0)` returns `ZERO_SIZE_PTR` (`(void
*)16`), the `if (!priv->data)` check passes, then `brcm_nvram_parse()`
dereferences `header->magic` and **page-faults at address 0x10**
- **Root cause:** zero-size kmalloc is not NULL; the code treats it as a
valid buffer
- **Fix:** return `-EINVAL` if `data_len < sizeof(struct
brcm_nvram_header)` before allocating
- **Versions:** none named; the buggy trim-and-cache path was added by
`nvmem: brcm_nvram: store a copy of NVRAM content`

Step 1.4: DETECT HIDDEN BUG FIXES
Record: **Not hidden.** This is an explicit crash fix. The wording
matches the code.

===============================================================
PHASE 2: DIFF ANALYSIS - LINE BY LINE
===============================================================

Step 2.1: INVENTORY THE CHANGES
Record:
- **File:** `drivers/nvmem/brcm_nvram.c` — **+5 / −0**
- **Function:** `brcm_nvram_copy_data()`
- **Scope:** single-file surgical error-path check

Step 2.2: UNDERSTAND THE CODE FLOW CHANGE
Record:
- **Before:** trim padding → `WARN` if huge → `devm_kzalloc(data_len)` →
copy → parse
- **After:** same, but if `data_len` is smaller than the 20-byte header,
log and return `-EINVAL`
- **Path:** probe/init only (`subsys_initcall_sync` → `brcm_nvram_probe`
→ `brcm_nvram_copy_data`)
- Probe already returns `copy_data` errors, so this fails the driver
instead of oopsing

Step 2.3: IDENTIFY THE BUG MECHANISM
Record:
- **Category:** memory safety / NULL-check bypass (`ZERO_SIZE_PTR`)
leading to a **NULL-ish deref / page fault**
- **Mechanism:** `devm_kmalloc()` returns `ZERO_SIZE_PTR` for size 0
(verified in this tree). That pointer is non-NULL, so the allocation
“succeeded.” `brcm_nvram_parse()` then does `memcmp(header->magic,
...)` at address `0x10`
- Also covers `1 <= data_len < sizeof(header)`, which would be a short
buffer under the same parse

Step 2.4: ASSESS THE FIX QUALITY
Record:
- **Obviously correct:** you cannot parse a header that is not there
- **Minimal:** 5 lines, no API/locking/structure change
- **Regression risk:** very low. Worst case: probe fails with `-EINVAL`
and NVRAM cells are missing (MAC fallback), instead of a boot oops
- **Red flags:** none. The later sibling “malformed flash OOB” patch is
independent

===============================================================
PHASE 3: GIT HISTORY INVESTIGATION
===============================================================

Step 3.1: BLAME THE CHANGED LINES
Record:
- Trim loop, `devm_kzalloc(priv->data_len)`, and parse were introduced
by **`09283d60bc332`** (`nvmem: brcm_nvram: store a copy of NVRAM
content`, upstream `1e37bf84afacd`)
- That landed in **this tree in v6.6.9** (`git describe --contains` →
`v6.6.9~19`)
- Original v6.6 did **not** have this path (`git merge-base --is-
ancestor 09283d60bc332 v6.6` failed)

Step 3.2: FOLLOW THE FIXES: TAG
Record: **No Fixes: tag.** The buggy commit is nonetheless identified:
`09283d60bc332` / `1e37bf84afacd`, present in this tree. This is a
**stable-to-stable follow-up** to a 6.6.9 backport.

Step 3.3: CHECK FILE HISTORY FOR RELATED CHANGES
Record:
- Last change to this file in *this* tree is the copy-data backport
itself
- Rosen also sent a sibling patch the same day: `nvmem: brcm_nvram: fix
out-of-bounds access on malformed flash data` (pull 13/14). That
tightens `header->len` vs `data_len`. **Not a prerequisite** for this
empty-partition check
- Original author series was **v1 standalone `[PATCH]`**, later batched
as 12/14 in Srinivas→Greg pull

Step 3.4: CHECK THE AUTHOR'S OTHER COMMITS
Record:
- Rosen Penev has no prior `drivers/nvmem/` commits in this tree
- Srinivas Kandagatla is the nvmem maintainer and applied it (`Applied,
thanks!` / commit `5f75913830bc`)
- Original driver author Rafał Miłecki was CCed; no NAK in the thread

Step 3.5: CHECK FOR DEPENDENT/PREREQUISITE COMMITS
Record:
- Depends only on `brcm_nvram_copy_data()` + `struct brcm_nvram_header`,
both in this tree
- **Can apply standalone.** Insertion context matches exactly

===============================================================
PHASE 4: MAILING LIST AND EXTERNAL RESEARCH
===============================================================

Step 4.1: FIND THE ORIGINAL PATCH DISCUSSION
Record:
- **b4 dig -c 5f75913830bc:**
https://patch.msgid.link/20260703222830.408764-1-rosenp@xxxxxxxxx
- **b4 dig -a:** only **v1**; applied version is that v1
- Thread is 2 messages: patch + maintainer apply. **No NAK, no requested
respin**
- lore.kernel.org WebFetch returned 403; thread recovered via b4 mbox +
openwall/lkml mirrors

Step 4.2: CHECK WHO REVIEWED THE PATCH
Record (b4 dig -w):
- To: linux-kernel
- Cc: Srinivas Kandagatla (maintainer), rafal@xxxxxxxxxx (driver author)
- Applied by the subsystem maintainer; no extra Reviewed-by

Step 4.3: SEARCH FOR THE BUG REPORT
Record: **No Reported-by, no syzbot, no bugzilla.** The crash mechanism
is spelled out (fault at `0x10`) and matches the code. Trigger is an
erased NVRAM image (all `0xFF`/`0x00`), which is a real flash state.

Step 4.4: CHECK FOR RELATED PATCHES AND SERIES
Record:
- Standalone v1 from Rosen
- Later 12/14 is only the maintainer pull batch
- Sibling OOB-length fix is related but **not required** for this patch
to be correct

Step 4.5: CHECK STABLE MAILING LIST HISTORY
Record: No useful stable-list hit for this subject. Absence of Cc:
stable is expected for this review.

===============================================================
PHASE 5: CODE SEMANTIC ANALYSIS
===============================================================

Step 5.1: IDENTIFY KEY FUNCTIONS IN THE DIFF
Record: `brcm_nvram_copy_data()` (modified). Crash consumer:
`brcm_nvram_parse()`. Caller: `brcm_nvram_probe()`.

Step 5.2: TRACE CALLERS
Record:
- `brcm_nvram_copy_data` ← only `brcm_nvram_probe`
- `brcm_nvram_probe` ← platform driver, registered from
`brcm_nvram_init` via **`subsys_initcall_sync`**
- Reachable at **boot** on DT nodes `compatible = "brcm,nvram"`

Step 5.3: TRACE CALLEES
Record: `devm_platform_get_and_ioremap_resource`, `readb` trim loop,
`devm_kzalloc`, `memcpy_fromio`, `bcm47xx_nvram_init_from_iomem` (return
value already ignored). `bcm47xx_nvram_is_valid()` reads **iomem**, not
`priv->data`, so it does not crash first; copy_data still returns 0 and
parse faults.

Step 5.4: FOLLOW THE CALL CHAIN
Record:
Boot → `brcm_nvram_init` → probe → copy_data (data_len=0, ZERO_SIZE_PTR)
→ parse → `memcmp(header->magic)` at `0x10` → **oops**
- Userspace syscall not required
- **Yes, reachable** on BCM5301X with an empty/erased NVRAM mapping

Step 5.5: SEARCH FOR SIMILAR PATTERNS
Record: `devm_kmalloc()` in this tree explicitly `return ZERO_SIZE_PTR`
when `!size`. Same class of bug as other “kmalloc(0) is not NULL”
crashes. Sibling OOB check in parse is a related malformed-flash bug,
not a duplicate of this one.

===============================================================
PHASE 6: CROSS-REFERENCING AGAINST THE LOCAL TREE
===============================================================

Step 6.1: DOES THE BUGGY CODE EXIST IN THIS TREE?
Record: **Yes.** `drivers/nvmem/brcm_nvram.c` lines 78–89 (trim +
`devm_kzalloc`) and 177–187 (header deref). Introduced in **v6.6.9**.
Mainline fix `5f75913830bc` is **not** an ancestor of HEAD. Source has
no “NVRAM data too small” check.

Step 6.2: CHECK FOR BACKPORT COMPLICATIONS
Record: **Clean apply.** Context around the `WARN`/`devm_kzalloc` site
matches. `MAC_ADDR_STR_LEN` vs `3 * ETH_ALEN - 1` is a different
function and is not touched.

Step 6.3: CHECK IF RELATED FIXES ARE ALREADY HERE
Record: No “empty NVRAM” / “data too small” commit in this tree. No
alternate fix.

===============================================================
PHASE 7: SUBSYSTEM AND MAINTAINER CONTEXT
===============================================================

Step 7.1: IDENTIFY THE SUBSYSTEM AND ITS CRITICALITY
Record: **nvmem / Broadcom NVRAM driver.** Criticality: **PERIPHERAL**
(ARCH_BCM_5301X routers), but real shipping hardware. 16 DT files in
this tree already have `compatible = "brcm,nvram"` (Luxul, Linksys,
D-Link, Asus, etc.). `CONFIG_NVMEM_BRCM_NVRAM` depends on
`ARCH_BCM_5301X || COMPILE_TEST`.

Step 7.2: ASSESS SUBSYSTEM ACTIVITY
Record: This driver is mature; last local change is the 6.6.9 cache-copy
backport. The bug is in that long-lived path, not in brand-new mainline-
only code.

===============================================================
PHASE 8: IMPACT AND RISK ASSESSMENT
===============================================================

Step 8.1: DETERMINE WHO IS AFFECTED
Record: **Driver-specific / platform-specific** — BCM5301X boards with a
`brcm,nvram` node (OpenWrt and similar). Not universal.

Step 8.2: DETERMINE THE TRIGGER CONDITIONS
Record:
- Trigger: NVRAM window is all padding (erased flash `0xFF`, or all
zeros)
- Happens at **device probe / boot**, not a rare runtime race
- Not an unprivileged syscall exploit; it is a **boot oops** on those
machines
- Realistic after wipe, bad flash, or empty factory partition

Step 8.3: DETERMINE THE FAILURE MODE SEVERITY
Record: **Kernel oops / page fault during probe.** Severity: **HIGH**
(boot crash on affected boards). Not a silent corruption bug, but it
takes the kernel down instead of failing probe.

Step 8.4: CALCULATE RISK-BENEFIT RATIO
Record:
- **Benefit:** high for BCM5301X users with empty/wiped NVRAM; converts
oops → clean `-EINVAL`
- **Risk:** very low (5 lines, error path only, no locking)
- **Ratio:** strongly favors backport. This is also a fix for a bug
**introduced by an earlier stable backport** (6.6.9)

===============================================================
PHASE 9: FINAL SYNTHESIS
===============================================================

Step 9.1: COMPILE THE EVIDENCE

**FOR:**
- Concrete oops: dereference of `ZERO_SIZE_PTR` at `0x10`
- Buggy path exists in this 6.6.157 tree (since 6.6.9)
- 5-line, obviously correct guard; probe already handles `-EINVAL`
- Maintainer-applied; no NAK
- Clean apply; no prerequisites
- Follow-up to a stable backport that added the trim-and-allocate logic

**AGAINST:**
- No public Reported-by / Tested-by
- Driver is platform-specific, not core
- Empty NVRAM is not every-boot on healthy devices

**UNRESOLVED:**
- Direct lore.kernel.org HTTP fetch was 403 (thread recovered via b4)
- No independent user report found beyond the author’s analysis

Step 9.2: APPLY THE STABLE RULES CHECKLIST
1. Obviously correct and tested? **PASS** — mechanism matches
`devm_kmalloc(0)` and parse; maintainer applied v1
2. Fixes a real bug that affects users? **PASS** — oops on erased NVRAM;
DT boards in this tree use the driver
3. Important issue (crash, security, corruption, deadlock)? **PASS** —
boot-time page fault
4. Small and contained? **PASS** — +5 lines, one function
5. No new features or APIs? **PASS**
6. Can apply to the local tree? **PASS** — context identical

Step 9.3: CHECK FOR EXCEPTION CATEGORIES
Record: Not a device-ID/quirk/DT/build/docs exception. Ordinary crash
fix.

Step 9.4: MAKE YOUR DECISION
Backport. The local tree has the 6.6.9 cache-copy code that created this
`kmalloc(0)` oops; the guard is tiny and fails probe instead of taking a
fault.

===============================================================
What this solves for stable users
===============================================================

On this 6.6.y tree, `brcm_nvram_copy_data()` trims trailing padding and
allocates `data_len` bytes. If the mapped NVRAM is fully erased,
`data_len` becomes 0. `devm_kzalloc(dev, 0)` returns `(void *)16`, parse
treats that as a header, and the kernel oopses at boot. The patch
rejects anything smaller than `struct brcm_nvram_header` (20 bytes) and
returns `-EINVAL`, which `brcm_nvram_probe()` already handles.

It meets the stable rules: real crash, tiny, no API change, applies
cleanly, and it closes a hole opened by a prior 6.6.9 stable backport.

Risk vs benefit: very low risk, high benefit on BCM5301X when NVRAM is
empty. The sibling malformed-length OOB patch is useful later but is not
required for this one.

===============================================================
Verification
===============================================================
- [Phase 1] Parsed tags: no Fixes/Reported-by; SOBs Rosen/Srinivas/Greg;
Link is Srinivas 12/14 pull of a standalone v1
- [Phase 2] Diff: +5 lines in `brcm_nvram_copy_data()` before
`devm_kzalloc`
- [Phase 2] `include/linux/device.h`: `devm_kzalloc` → `devm_kmalloc`;
`drivers/base/devres.c:833-834`: size 0 returns `ZERO_SIZE_PTR`;
`include/linux/slab.h:175`: `((void *)16)`
- [Phase 2] `brcm_nvram_parse()` at lines 179–184 dereferences
`header->magic` — matches “page fault on address 0x10”
- [Phase 3] `git blame`: trim/alloc from `09283d60bc332`; `git describe
--contains` → `v6.6.9~19`; not in `v6.6`
- [Phase 3] `git merge-base --is-ancestor 5f75913830bc HEAD` → not
ancestor (fix absent)
- [Phase 3] `git log --grep='empty NVRAM'` on `drivers/nvmem/` → empty
- [Phase 4] `b4 dig -c 5f75913830bc`: original URL
`20260703222830.408764-1-rosenp@xxxxxxxxx`
- [Phase 4] `b4 dig -a`: v1 only
- [Phase 4] `b4 dig -w`: linux-kernel, Srinivas, Rafał; mbox: patch +
“Applied, thanks!”; no NAK
- [Phase 4] lore WebFetch 403; used b4 + openwall/lkml instead
- [Phase 5] Callers: only `brcm_nvram_probe`; `subsys_initcall_sync`
- [Phase 5] `bcm47xx_nvram_init_from_iomem` reads iomem magic; return
ignored so parse still runs
- [Phase 6] `git describe HEAD` → `p-6.6-3-g04810563abb71`; `make
kernelversion` → **6.6.157**
- [Phase 6] Patch context around WARN/kzalloc matches; clean apply
expected
- [Phase 7] 16 `arch/arm/boot/dts/broadcom/*.dts*` files with
`brcm,nvram`; Kconfig `NVMEM_BRCM_NVRAM` depends on `ARCH_BCM_5301X`
- [Phase 8] Failure mode: probe-time oops, severity HIGH
- UNVERIFIED: no independent end-user crash log; mechanism verified from
code anyway

**YES**

drivers/nvmem/brcm_nvram.c | 5 +++++
1 file changed, 5 insertions(+)

diff --git a/drivers/nvmem/brcm_nvram.c b/drivers/nvmem/brcm_nvram.c
index b4cf245fb2467..4b3781a70222a 100644
--- a/drivers/nvmem/brcm_nvram.c
+++ b/drivers/nvmem/brcm_nvram.c
@@ -84,6 +84,11 @@ static int brcm_nvram_copy_data(struct brcm_nvram *priv, struct platform_device
}
WARN(priv->data_len > SZ_128K, "Unexpected (big) NVRAM size: %zu B\n", priv->data_len);

+ if (priv->data_len < sizeof(struct brcm_nvram_header)) {
+ dev_err(priv->dev, "NVRAM data too small (%zu)\n", priv->data_len);
+ return -EINVAL;
+ }
+
priv->data = devm_kzalloc(priv->dev, priv->data_len, GFP_KERNEL);
if (!priv->data)
return -ENOMEM;
--
2.53.0