[PATCH AUTOSEL 6.18-5.15] wifi: cfg80211: validate rx/tx MLME callback frame lengths before access
From: Sasha Levin <sashal@kernel.org>
Date: 2026-08-31 13:42:54
Also in:
linux-patches, lkml, stable
Subsystem:
802.11 (including cfg80211/nl80211), the rest · Maintainers:
Johannes Berg, Linus Torvalds
From: Zhao Li <redacted> [ Upstream commit d5e4586546974179feca305a94e07fac3e9727fe ] cfg80211_rx_mlme_mgmt() and cfg80211_tx_mlme_mgmt() call tracepoints before rejecting frames shorter than the frame-control field. After that, they only require len >= 2 before dispatching into subtype handlers that assume their fixed fields are present. The frames that trip this are not shorter than 2 bytes; they are short relative to their subtype. mwifiex is a concrete in-tree example on the length side: mwifiex_process_mgmt_packet() only requires a 4-address ieee80211_hdr plus the 2-byte firmware length prefix before handing the frame to cfg80211_rx_mlme_mgmt(). After stripping the length prefix and removing addr4, pkt_len can be exactly 24: a bare 3-address management header with no reason-code body. The existing WARN_ON(len < 2) does not fire on such a frame, and cfg80211_process_deauth() then reads u.deauth.reason_code as a two-byte access starting at offset 24, immediately past the 24-byte buffer. Add a frame-control length gate, then validate each subtype's minimum frame size in an if/else-if chain that mirrors the dispatch logic. Trace only after the frame is known to be well-formed. Side effects of this change: - The WARN_ON(len < 2) is dropped. It only guarded the frame_control read, never the subtype fixed fields, and it does not fire on the frames that actually trigger the out-of-bounds read (which are >= 2). The len >= 2 check is kept as the guard before dereferencing frame_control, but without the warning: these are exported callbacks and a malformed frame from a driver should be dropped silently rather than backtraced. - cfg80211_tx_mlme_mgmt() previously routed every non-deauth subtype through disassociation handling; it now silently ignores unrecognised subtypes. Assisted-by: Codex:gpt-5.5 Assisted-by: Claude:claude-opus-4.8 Signed-off-by: Zhao Li <redacted> Link: https://patch.msgid.link/20260707025336.22557-1-enderaoelyther@gmail.com Signed-off-by: Johannes Berg <redacted> Signed-off-by: Sasha Levin <sashal@kernel.org> --- LLM Generated explanations, may be completely bogus: # Stable Backport Analysis: `wifi: cfg80211: validate rx/tx MLME callback frame lengths before access` **Local tree:** `v6.18.44-1-g2736c32da98b9` (Linux **6.18.44**) --- ## PHASE 1: COMMIT MESSAGE FORENSICS ### Step 1.1: PARSE THE SUBJECT LINE **Record:** `[wifi: cfg80211]` `[validate]` — Add length validation for rx/tx MLME callback frames before accessing fixed subtype fields. ### Step 1.2: PARSE ALL COMMIT MESSAGE TAGS **Record:** - **Link:** `https://patch.msgid.link/20260707025336.22557-1- enderaoelyther@gmail.com` - **Assisted-by:** Codex:gpt-5.5, Claude:claude-opus-4.8 - **Signed-off-by:** Zhao Li `[off-list ref]` - **Signed-off-by:** Johannes Berg `[off-list ref]` (cfg80211/mac80211 maintainer) - **No** Fixes:, Reported-by:, Tested-by:, Reviewed-by:, Acked-by:, Cc: stable@vger.kernel.org - **Notable:** Maintainer sign-off; v2 series (patch 1/3) with Johannes Berg review noted in cover letter changelog ### Step 1.3: ANALYZE THE COMMIT BODY TEXT **Record:** - **Bug:** `cfg80211_rx_mlme_mgmt()` and `cfg80211_tx_mlme_mgmt()` only check `len >= 2` before dispatching to subtype handlers that read fixed fields (`reason_code`, `status_code`, etc.) - **Symptom:** Out-of-bounds 2-byte read at offset 24 when a 24-byte management header (no body) is passed for deauth/disassoc - **Concrete trigger:** `mwifiex_process_mgmt_packet()` can pass `pkt_len == 24` after stripping addr4 from a minimal 4-address frame - **Root cause:** Validation guards frame_control (2 bytes) but not per- subtype minimum sizes (26 bytes for deauth/disassoc) - **Version info:** None explicit in message ### Step 1.4: DETECT HIDDEN BUG FIXES **Record:** Not disguised — explicitly fixes out-of-bounds memory access. The WARN_ON removal and tx subtype routing change are documented side effects of the safety fix. --- ## PHASE 2: DIFF ANALYSIS ### Step 2.1: INVENTORY THE CHANGES **Record:** - **File:** `net/wireless/mlme.c` only (+37 / -8 lines) - **Functions modified:** `cfg80211_rx_mlme_mgmt()`, `cfg80211_tx_mlme_mgmt()` - **Scope:** Single-file surgical fix ### Step 2.2: CODE FLOW CHANGE **Record:** **`cfg80211_rx_mlme_mgmt()` — before → after:** - Before: trace → `WARN_ON(len < 2)` → dispatch by subtype → handlers read fixed fields - After: check `len >= sizeof(fc)` → read `fc` → per-subtype `offsetofend()` validation → trace → dispatch - Affected path: RX MLME frames from drivers (notably mwifiex host_mlme) **`cfg80211_tx_mlme_mgmt()` — before → after:** - Before: trace → `WARN_ON(len < 2)` → deauth or **everything else → disassoc** - After: validate deauth/disassoc minimum lengths; unknown subtypes silently dropped - Affected path: TX disconnect notifications from mac80211/mwifiex ### Step 2.3: BUG MECHANISM **Record:** - **Category:** Buffer overflow / out-of-bounds read (memory safety) - **Mechanism:** `cfg80211_process_deauth()` reads `mgmt->u.deauth.reason_code` at offset 24–25: ```109:116:net/wireless/mlme.c static void cfg80211_process_deauth(struct wireless_dev *wdev, const u8 *buf, size_t len, bool reconnect) { ... u16 reason_code = le16_to_cpu(mgmt->u.deauth.reason_code);
Minimum valid deauth frame is 26 bytes (`IEEE80211_DEAUTH_FRAME_LEN`).
A 24-byte buffer causes a read 2 bytes past the end.
### Step 2.4: FIX QUALITY
**Record:**
- Fix is obviously correct: mirrors dispatch logic with `offsetofend()`
checks, same pattern used elsewhere in ieee80211 code
- Minimal and surgical
- Low regression risk: only rejects malformed frames that were already
unsafe to process
- Moving tracepoints after validation is correct
- tx path fix for unrecognized subtypes prevents wrongly routing auth
frames to disassoc handler
---
## PHASE 3: GIT HISTORY INVESTIGATION
### Step 3.1: BLAME THE CHANGED LINES
**Record:** All lines in `cfg80211_rx_mlme_mgmt()` /
`cfg80211_tx_mlme_mgmt()` blame to `5d324e5159d9e` (6.18 merge base in
this tree). Buggy insufficient-length check has been present since these
functions landed in this tree.
### Step 3.2: FOLLOW THE FIXES: TAG
**Record:** No Fixes: tag present. N/A.
### Step 3.3: CHECK FILE HISTORY FOR RELATED CHANGES
**Record:**
- Recent `mlme.c` commits: `c3ab9657866fc` (radar detection fix), merge
base
- This fix is **not** yet in the local tree (buggy code still present at
lines 149–167, 213–230)
- Part of v2 3-patch series; **this commit is standalone** for the two
cfg80211 callback functions (patches 2/3 fix `cfg80211_rx_assoc_resp`
and `ieee80211_rx_mgmt_deauth` separately)
### Step 3.4: CHECK THE AUTHOR'S OTHER COMMITS
**Record:** Zhao Li is not a regular mlme.c contributor in this tree.
Johannes Berg (maintainer) signed off and merged.
### Step 3.5: CHECK FOR DEPENDENT/PREREQUISITE COMMITS
**Record:** No prerequisites. Uses standard `offsetofend(struct
ieee80211_mgmt, ...)` which exists in this tree. Applies cleanly against
current `net/wireless/mlme.c`.
---
## PHASE 4: MAILING LIST AND EXTERNAL RESEARCH
### Step 4.1: ORIGINAL PATCH DISCUSSION
**Record:**
- Local mbox: `v2_20260707_enderaoelyther_wifi_cfg80211_validate_rx_tx_m
lme_callback_frame_lengths_before_access.mbx`
- v2 changelog documents Johannes Berg review feedback (commit message
rewrite)
- lore.kernel.org blocked by Anubis bot protection; `b4 dig -c` could
not be run (commit not in local tree)
- No review replies in local mbox (patches only)
### Step 4.2: WHO REVIEWED THE PATCH
**Record:** Johannes Berg reviewed v1 per v2 changelog ("Per Johannes'
review"). Maintainer Signed-off-by on committed version.
### Step 4.3: BUG REPORT
**Record:** No external bug report or syzbot link. Bug identified via
static/code-path analysis with concrete mwifiex trigger documented by
author.
### Step 4.4: RELATED PATCHES AND SERIES
**Record:** 3-patch v2 series:
1. **This commit** — cfg80211 rx/tx MLME callbacks
2. `cfg80211_rx_assoc_resp()` length validation
3. `ieee80211_rx_mgmt_deauth()` read-before-check fix in mac80211
Patches 2/3 are related but **not required** for this commit to be
correct and self-contained.
### Step 4.5: STABLE MAILING LIST HISTORY
**Record:** Could not search lore (blocked). No stable nomination found
in local mbox.
---
## PHASE 5: CODE SEMANTIC ANALYSIS
### Step 5.1: KEY FUNCTIONS
**Record:** `cfg80211_rx_mlme_mgmt()`, `cfg80211_tx_mlme_mgmt()`,
callees `cfg80211_process_auth/deauth/disassoc()`
### Step 5.2: TRACE CALLERS
**Record:**
| Caller | File | Notes |
|--------|------|-------|
| `ieee80211_report_disconnect()` | `net/mac80211/mlme.c:4547-4549` |
Always uses `IEEE80211_DEAUTH_FRAME_LEN` (26) buffers |
| Auth failure paths | `net/mac80211/mlme.c:4878,4944,5045` | mac80211
validates before calling |
| `mwifiex_process_mgmt_packet()` | `drivers/.../mwifiex/util.c:482` |
**Can pass pkt_len == 24** |
| `mwifiex_host_mlme_disconnect()` | `mwifiex/util.c:385` | Passes 26
bytes (safe) |
### Step 5.3: TRACE CALLEES
**Record:** Subtype handlers call `nl80211_send_*`, `cfg80211_sme_*`,
read `reason_code`/`status_code` from frame body.
### Step 5.4: CALL CHAIN / REACHABILITY
**Record:**
1. WiFi firmware delivers management frame to mwifiex
2. `mwifiex_process_mgmt_packet()` strips 4-addr header → 24-byte 3-addr
frame
3. If `host_mlme_reg` and deauth/disassoc: calls
`cfg80211_rx_mlme_mgmt(dev, skb->data, 24)`
4. `cfg80211_process_deauth()` reads past buffer end
**Reachable from userspace indirectly:** Malicious or buggy AP/firmware
can send short deauth/disassoc frames to mwifiex clients with host MLME
enabled. Config-dependent (`CONFIG_MWIFIEX`, host_mlme firmware
capability).
### Step 5.5: SIMILAR PATTERNS
**Record:** mac80211's `ieee80211_rx_mgmt_deauth()` has the same read-
before-check pattern (reads `reason_code` before `len` check at line
5011 vs 5015) — fixed in patch 3/3 of the series, separate from this
commit.
---
## PHASE 6: CROSS-REFERENCING AGAINST LOCAL TREE
### Step 6.1: DOES THE BUGGY CODE EXIST?
**Record:** **YES.** Current tree at `net/wireless/mlme.c:149-167` and
`213-230` has the insufficient `WARN_ON(len < 2)` check. mwifiex trigger
path exists at `util.c:398-482`.
Verified minimum frame math:
- `ieee80211_mgmt` header = 24 bytes
- `u.deauth.reason_code` ends at byte 26
- mwifiex minimum after addr4 strip: `sizeof(ieee80211_hdr)` (30) −
`ETH_ALEN` (6) = **24 bytes**
### Step 6.2: BACKPORT COMPLICATIONS
**Record:** Clean apply expected. Diff in local mbox matches current
file structure. No conflicting recent changes to these functions.
### Step 6.3: RELATED FIXES ALREADY PRESENT?
**Record:** **No.** `git log --grep` and `-S 'offsetofend(struct
ieee80211_mgmt, u.deauth.reason_code)'` found no matching fix in this
tree.
---
## PHASE 7: SUBSYSTEM AND MAINTAINER CONTEXT
### Step 7.1: SUBSYSTEM AND CRITICALITY
**Record:** `net/wireless` (cfg80211) — **CORE/IMPORTANT**. cfg80211 is
the central wireless configuration API used by all mac80211 drivers and
several fullmac drivers.
### Step 7.2: SUBSYSTEM ACTIVITY
**Record:** Actively maintained; recent stable backport `c3ab9657866fc`
in this tree.
---
## PHASE 8: IMPACT AND RISK ASSESSMENT
### Step 8.1: WHO IS AFFECTED
**Record:** Users of WiFi drivers calling `cfg80211_rx_mlme_mgmt()` /
`cfg80211_tx_mlme_mgmt()`. Primary concrete trigger: **mwifiex with host
MLME** (several SDIO device tables set `host_mlme = true`). mac80211
callers are generally safe due to `IEEE80211_DEAUTH_FRAME_LEN` usage.
### Step 8.2: TRIGGER CONDITIONS
**Record:**
- Receiving deauth/disassoc/auth frame with header only (no body) via
mwifiex host_mlme path
- Requires connected state and matching BSSID checks in mwifiex
- Not every boot, but plausible with malformed AP traffic or firmware
quirks
- Unprivileged remote trigger via WiFi network (AP sends short frame)
### Step 8.3: FAILURE MODE SEVERITY
**Record:** Out-of-bounds kernel memory read — **HIGH** severity
- KASAN: detectable crash
- Production without KASAN: potential info leak or unpredictable
behavior
- Not a typical controlled write primitive, but real memory safety bug
in core wireless path
### Step 8.4: RISK-BENEFIT RATIO
**Record:**
- **Benefit:** HIGH — prevents OOB read in exported cfg80211 API;
defense-in-depth for all drivers
- **Risk:** LOW — ~37 lines, validation-only, maintainer-reviewed
- **Ratio:** Strongly favors backport
---
## PHASE 9: FINAL SYNTHESIS
### Step 9.1: EVIDENCE COMPILED
**FOR backport:**
- Real out-of-bounds read verified in code and frame-size math
- Concrete in-tree trigger path (mwifiex host_mlme, 24-byte frames)
- Small, surgical, maintainer-signed fix
- Buggy code confirmed present in 6.18.44 tree
- Fixes incorrect tx subtype routing (non-deauth → disassoc) that could
cause same class of bug
- Exported API should validate inputs from drivers
**AGAINST backport:**
- No syzbot report or user crash report (theoretical/code-analysis
discovery)
- Trigger requires specific driver config (mwifiex host_mlme)
- Related bugs in patches 2/3 not included (but separate functions)
**UNRESOLVED:**
- Full lore review thread (blocked by Anubis)
- Whether any reviewer explicitly nominated for stable
### Step 9.2: STABLE RULES CHECKLIST
1. Obviously correct and tested? **PASS** — standard offsetofend
validation; maintainer reviewed v2
2. Fixes a real bug? **PASS** — verified OOB read with mwifiex trigger
path
3. Important issue? **PASS** — kernel memory safety (OOB read), HIGH
severity
4. Small and contained? **PASS** — 1 file, ~37 lines
5. No new features or APIs? **PASS** — validation only
6. Can apply to local tree? **PASS** — code present, clean apply
expected
### Step 9.3: EXCEPTION CATEGORIES
**Record:** None (not device ID, quirk, DT, build, or docs). Standard
memory-safety bug fix.
### Step 9.4: DECISION RATIONALE
This commit fixes a verified out-of-bounds read in exported cfg80211
MLME callbacks. The buggy code exists in Linux 6.18.44, the fix is small
and maintainer-approved, and a concrete in-tree caller (mwifiex
host_mlme) can trigger it with 24-byte deauth/disassoc frames. While the
trigger is config-specific, cfg80211 is a core API that should reject
malformed driver input — making this appropriate for the 6.18.y stable
tree.
---
## Verification
- **[Phase 1]** Parsed commit message and tags from user query and local
mbox
- **[Phase 2]** Read current `net/wireless/mlme.c:109-167,213-230`;
confirmed OOB read at `reason_code` with 24-byte buffer
- **[Phase 2]** Computed frame sizes: header=24, deauth offsetofend=26,
auth offsetofend=30
- **[Phase 3]** `git describe HEAD`: v6.18.44; `git blame`: buggy code
at 5d324e5159d9e
- **[Phase 3]** `git log --grep` / `-S offsetofend`: fix not in tree
- **[Phase 4]** Read local mbox `v2_20260707_enderaoelyther_*.mbx`; v2
Johannes Berg review noted
- **[Phase 4]** lore.kernel.org fetch: blocked (Anubis)
- **[Phase 4]** `b4 dig -c`: not attempted (commit not in tree)
- **[Phase 5]** `grep cfg80211_rx_mlme_mgmt`: callers in mac80211 (safe,
26-byte) and mwifiex (unsafe path at util.c:482)
- **[Phase 5]** Read `mwifiex/util.c:398-482`: verified pkt_len can be
24 after addr4 removal
- **[Phase 5]** Read `ieee80211.h:1162-1207`: confirmed struct layout
- **[Phase 5]** `IEEE80211_DEAUTH_FRAME_LEN` = 26 in
`net/mac80211/ieee80211_i.h:85`
- **[Phase 6]** Buggy code confirmed present; fix absent
- **[Phase 6]** `sizeof(struct ieee80211_hdr)` = 30 (includes addr4);
30−6=24 minimum after strip
- **[Phase 8]** Failure mode: OOB read, HIGH severity
**YES**The background `git log -S 'cfg80211_rx_mlme_mgmt'` search
finished. In this 6.18.44 tree it only surfaced merge commits — notably
`5d324e5159d9e` — and `git blame` attributes the current
`cfg80211_rx_mlme_mgmt()` / `cfg80211_tx_mlme_mgmt()` code to that same
point. That’s enough to confirm the buggy validation has been present
since these functions landed here; it doesn’t change the backport call.
**Verdict for this tree: YES** — the out-of-bounds read in cfg80211 MLME
callbacks is real, reachable via mwifiex host_mlme with 24-byte frames,
and the fix is small and maintainer-reviewed.
net/wireless/mlme.c | 45 +++++++++++++++++++++++++++++++++++++--------
1 file changed, 37 insertions(+), 8 deletions(-)
diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c
index bf2c0d26e11c6..a304c0e8736f6 100644
--- a/net/wireless/mlme.c
+++ b/net/wireless/mlme.c
@@ -164,19 +164,35 @@ void cfg80211_rx_mlme_mgmt(struct net_device *dev, const u8 *buf, size_t len)
{
struct wireless_dev *wdev = dev->ieee80211_ptr;
struct ieee80211_mgmt *mgmt = (void *)buf;
+ __le16 fc;
lockdep_assert_wiphy(wdev->wiphy);
- trace_cfg80211_rx_mlme_mgmt(dev, buf, len);
+ if (len < sizeof(fc))
+ return;
+
+ fc = mgmt->frame_control;
- if (WARN_ON(len < 2))
+ if (ieee80211_is_auth(fc)) {
+ if (len < offsetofend(struct ieee80211_mgmt, u.auth.status_code))
+ return;
+ } else if (ieee80211_is_deauth(fc)) {
+ if (len < offsetofend(struct ieee80211_mgmt, u.deauth.reason_code))
+ return;
+ } else if (ieee80211_is_disassoc(fc)) {
+ if (len < offsetofend(struct ieee80211_mgmt, u.disassoc.reason_code))
+ return;
+ } else {
return;
+ }
+
+ trace_cfg80211_rx_mlme_mgmt(dev, buf, len);
- if (ieee80211_is_auth(mgmt->frame_control))
+ if (ieee80211_is_auth(fc))
cfg80211_process_auth(wdev, buf, len);
- else if (ieee80211_is_deauth(mgmt->frame_control))
+ else if (ieee80211_is_deauth(fc))
cfg80211_process_deauth(wdev, buf, len, false);
- else if (ieee80211_is_disassoc(mgmt->frame_control))
+ else
cfg80211_process_disassoc(wdev, buf, len, false);
}
EXPORT_SYMBOL(cfg80211_rx_mlme_mgmt);
@@ -229,15 +245,28 @@ void cfg80211_tx_mlme_mgmt(struct net_device *dev, const u8 *buf, size_t len,
{
struct wireless_dev *wdev = dev->ieee80211_ptr;
struct ieee80211_mgmt *mgmt = (void *)buf;
+ __le16 fc;
lockdep_assert_wiphy(wdev->wiphy);
- trace_cfg80211_tx_mlme_mgmt(dev, buf, len, reconnect);
+ if (len < sizeof(fc))
+ return;
- if (WARN_ON(len < 2))
+ fc = mgmt->frame_control;
+
+ if (ieee80211_is_deauth(fc)) {
+ if (len < offsetofend(struct ieee80211_mgmt, u.deauth.reason_code))
+ return;
+ } else if (ieee80211_is_disassoc(fc)) {
+ if (len < offsetofend(struct ieee80211_mgmt, u.disassoc.reason_code))
+ return;
+ } else {
return;
+ }
+
+ trace_cfg80211_tx_mlme_mgmt(dev, buf, len, reconnect);
- if (ieee80211_is_deauth(mgmt->frame_control))
+ if (ieee80211_is_deauth(fc))
cfg80211_process_deauth(wdev, buf, len, reconnect);
else
cfg80211_process_disassoc(wdev, buf, len, reconnect);
--
2.53.0