* [PATCH AUTOSEL 6.18-5.10] net/rds: don't use unpin_user_pages_dirty_lock() from atomic context
2026-10-19 13:44 [PATCH AUTOSEL 6.18-6.12] net: dsa: mt7530: check CORE_PLL_GROUP4 access in mt7531_setup() Sasha Levin
@ 2026-10-19 13:44 ` Sasha Levin
2026-10-19 13:45 ` [PATCH AUTOSEL 6.18-6.1] selftests: net: reuseport_bpf_numa: consider cpuless numa node Sasha Levin
` (28 subsequent siblings)
29 siblings, 0 replies; 32+ messages in thread
From: Sasha Levin @ 2026-10-19 13:44 UTC (permalink / raw)
To: patches, stable
Cc: Allison Henderson, Jakub Kicinski, Sasha Levin, davem, edumazet,
pabeni, netdev, linux-rdma, rds-devel, linux-kernel
From: Allison Henderson <achender@kernel.org>
[ Upstream commit d100966325f782aa7775e5f0e8fb04f66f56308e ]
rds_rdma_free_op() and rds_atomic_free_op() are reached from the IB
send completion path via
rds_ib_tasklet_fn_send()
rds_ib_send_cqe_handler()
rds_message_put()
rds_message_purge()
rds_rdma_free_op() / rds_atomic_free_op()
which runs in tasklet (softirq) context. Both functions unpin the
user pages of the op with unpin_user_pages_dirty_lock(), which uses
set_page_dirty_lock() and thus may take the folio lock and sleep.
Sleeping in softirq context is not allowed and can deadlock or crash.
Dirtying the pages with the non-sleeping set_page_dirty() instead
would just trade one bug for another, as pointed out during review:
the pinned range can be file-backed. rds_pin_pages() pins with
FOLL_LONGTERM, which refuses fs-dax but takes the page-cache pages
of a MAP_SHARED file mapping just fine, and RDS does not restrict
what memory the caller registers as an RDMA destination.
For a file-backed page, set_page_dirty() from a tasklet can take
non-irq-safe filesystem locks (e.g. mapping->i_private_lock and
inode->i_lock in block_dirty_folio()) and deadlock against the task
it interrupted. Without the folio lock, it races with truncation
clearing folio->mapping, which is the race set_page_dirty_lock()
exists to close. The pre-pin_user_pages() version of this code
dirtied pages that way from the tasklet, so that bug is older than
the sleeping unpin.
The page dirtying therefore has to move to process context, not
merely avoid the folio lock. When the final rds_message_put() runs
in atomic context, rds_rdma_free_op() and rds_atomic_free_op() now
leave the op's pages pinned and flag the op. Later, rds_message_put()
hands the message to a work item that unpins the flagged ops' pages
and frees the message from process context. Here,
unpin_user_pages_dirty_lock() is safe outside the atomic context.
Everything else keeps running in the caller's context exactly as
before: the rest of the purge - the zerocopy completion, the socket
put and the MR reference drops - as well as RDMA writes, whose pages
the remote side only reads and which unpin without dirtying,
everything on rds_tcp, and final puts that already happen in process
context (socket close, connection teardown).
Deferring only the unpin means the work item touches nothing but the
pinned pages and the rds module's own memory: it cannot call back
into a transport module, so it changes nothing about the transports'
shutdown and unload ordering. rds_exit() drains any pending unpin
work via destroy_workqueue(rds_wq) before the module goes away.
The Oracle UEK kernel avoids the sleeping unpin by calling
set_page_dirty() directly from the tasklet, which is subject to the
file-backed page problem above, so this deliberately does not follow
UEK here.
Signed-off-by: Allison Henderson <achender@kernel.org>
Link: https://patch.msgid.link/20260730041629.3512480-2-achender@kernel.org
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
LLM Generated explanations, may be completely bogus:
This is a real sleep-in-softirq bug on the RDS IB completion path, and
the buggy code is present in this 5.10.270 tree. The fix is self-
contained and should be backported.
===============================================================
PHASE 1: COMMIT MESSAGE FORENSICS
===============================================================
Step 1.1: PARSE THE SUBJECT LINE
Record: [net/rds] [don't use] don't call `unpin_user_pages_dirty_lock()`
from atomic/softirq context. "don't use" is a prevent/avoid-style fix.
Step 1.2: PARSE ALL COMMIT MESSAGE TAGS
Record:
- Signed-off-by: Allison Henderson `<achender@kernel.org>` (author;
ignore pipeline SOB)
- Signed-off-by: Jakub Kicinski `<kuba@kernel.org>` (netdev maintainer)
- Link: `https://patch.msgid.link/20260730041629.3512480-2-
achender@kernel.org`
- No Reported-by, Tested-by, Reviewed-by, Acked-by, or Cc: stable in the
provided message
- Mailing-list v4 **did** include `Fixes: 0d4597c8c5ab ("net/rds: Track
user mapped pages through special API")`; that tag is in this tree
(verified with `git merge-base --is-ancestor`)
- Message-ID `-2` is patch 1/4 of `[PATCH net v4 0/4] net/rds: Bug fix
ports` (cover letter is `-1`)
- Assisted-by Claude appears on the lore submission only
Step 1.3: ANALYZE THE COMMIT BODY TEXT
Record:
- Bug: `rds_rdma_free_op()` / `rds_atomic_free_op()` run from the IB
send tasklet and call `unpin_user_pages_dirty_lock()`, which uses
`set_page_dirty_lock()` → `lock_page()`, which may sleep.
- Symptom: sleep in softirq → deadlock or crash.
- Switching to `set_page_dirty()` from the tasklet is also wrong for
file-backed pins (`FOLL_LONGTERM` MAP_SHARED): non-irq-safe fs locks
and truncation races.
- Fix: if the final put is atomic and pages need dirtying, leave them
pinned, set `op_unpin_deferred`, and finish unpin+free on `rds_wq`.
- Author notes this is older than the pin API (pre-pin code already
dirtied from the tasklet) and that UEK’s `set_page_dirty()`-from-
tasklet approach is intentionally not followed.
Step 1.4: DETECT HIDDEN BUG FIXES
Record: Not hidden. Explicit sleep-in-atomic / deadlock fix.
===============================================================
PHASE 2: DIFF ANALYSIS - LINE BY LINE
===============================================================
Step 2.1: INVENTORY THE CHANGES
Record:
- `net/rds/message.c`: add `rds_message_unpin_worker()`, defer `kfree`
from `rds_message_put()` when unpin was deferred
- `net/rds/rdma.c`: split unpin helpers; `rds_rdma_free_op()` /
`rds_atomic_free_op()` unpin inline only when safe (`in_task()` or
RDMA write)
- `net/rds/rds.h`: `m_unpin_work`, `op_unpin_deferred` bitfields, helper
prototypes
- Scope: ~74 insertions / 11 deletions, three RDS files, surgical
Step 2.2: UNDERSTAND THE CODE FLOW CHANGE
Record:
- Before: final `rds_message_put()` always purges and unpins (possibly
dirtying) in the caller’s context, including the send CQ tasklet.
- After: purge still runs in the caller (zerocopy, sock_put, MR krefs).
Dirtying unpin is deferred to process context. RDMA writes still unpin
immediately (no dirty). Process-context final puts still unpin
immediately.
- Error path `rds_cmsg_rdma_args()` → `rds_rdma_free_op()` is
sendmsg/process context, so `in_task()` is true and pages are not
leaked.
Step 2.3: IDENTIFY THE BUG MECHANISM
Record: [Deadlock / sleep-in-atomic] `lock_page()` from tasklet/softirq.
Workqueue deferral is the correct class of fix; dirtying cannot stay in
the tasklet.
Step 2.4: ASSESS THE FIX QUALITY
Record: v4 is the review-hardened design (v1 used `set_page_dirty()` in
the tasklet; v2/v3 deferred the whole purge and needed extra IB-module
flushes). Risk is the usual workqueue lifetime issues; mitigated because
the worker only unpins pages and `kfree`s the message, and `rds_exit()`
→ `rds_threads_exit()` → `destroy_workqueue(rds_wq)` drains it. Messages
are `kzalloc`’d, so new flags/work_struct start zeroed.
===============================================================
PHASE 3: GIT HISTORY INVESTIGATION
===============================================================
Step 3.1: BLAME THE CHANGED LINES
Record:
- Sleeping unpin: `0d4597c8c5abd` ("net/rds: Track user mapped pages
through special API", Leon Romanovsky, 2020) — **in this tree**
- Call from tasklet: `rds_rdma_free_op` / `rds_message_put` date to
2009–2010 (Andy Grover)
- Pre-pin code already dirtied from this path
Step 3.2: FOLLOW THE FIXES: TAG
Record: `0d4597c8c5ab` is an ancestor of HEAD (`git merge-base --is-
ancestor` exit 0). No later in-tree substitute for this unpin-from-
tasklet bug. `338a3ba30c5ef` ("net:rds: Fix possible deadlock in
rds_message_put") is a different lock-order bug in `recv.c`.
Step 3.3: CHECK FILE HISTORY FOR RELATED CHANGES
Record: Recent `net/rds/` work here is unrelated (zerocopy, MR cleanup,
tcp corruption). This is standalone, not patch 2-of-N that needs earlier
series members.
Step 3.4: CHECK THE AUTHOR'S OTHER COMMITS
Record: Allison Henderson is a long-time RDS contributor; this tree
already has several of her RDS fixes. Author is the right subsystem
person.
Step 3.5: CHECK FOR DEPENDENT/PREREQUISITE COMMITS
Record: Series mates (socket ref on MR, send_xmit leak, MR teardown
unpin) are independent bugs. Needed APIs exist here: `in_task()`,
`unpin_user_pages_dirty_lock()`, `rds_wq`, `pin_user_pages_fast()` +
`FOLL_LONGTERM`. No missing helpers.
===============================================================
PHASE 4: MAILING LIST AND EXTERNAL RESEARCH
===============================================================
Step 4.1: FIND THE ORIGINAL PATCH DISCUSSION
Record:
- `b4 mbox` on msgid `20260730041629.3512480-2-achender@kernel.org`:
8-message thread
- Lore/WebFetch of lore.kernel.org and patch.msgid.link returned 403;
discussion recovered via b4 + openwall
- Revisions: v1 2026-07-11, v2 2026-07-25, v3 2026-07-26, v4 2026-07-29
(applied)
- v1 NAK’d conceptually: `set_page_dirty()` from softirq can deadlock on
fs locks and race truncation
- v3 extra `rds_ib_exit()` flushes dropped in v4 by deferring only unpin
- Applied to netdev/net-next as `d100966325f7` (not this tree)
Step 4.2: CHECK WHO REVIEWED THE PATCH
Record: To/Cc included netdev, linux-rdma, Kicinski, Abeni, Dumazet,
Horms. No subsystem Reviewed-by on v4. Kicinski: “I don't have much
confidence in my ability to review this, so let's queue this for -next.”
Author agreed they are old, non-simple bugs.
Step 4.3: SEARCH FOR THE BUG REPORT
Record: No syzbot/bugzilla in the thread. Motivation is code analysis +
UEK carrying a related (weaker) fix. Oracle RDS/IB users are the
affected population.
Step 4.4: CHECK FOR RELATED PATCHES AND SERIES
Record: 4-patch “Bug fix ports” series. This patch is 1/4 and does not
depend on 2–4. Patch 4 is a separate pin-vs-put_page leak in
`__rds_ib_teardown_mr()`.
Step 4.5: CHECK STABLE MAILING LIST HISTORY
Record: No useful hit on lore.kernel.org/stable for this subject (search
was noisy/unrelated). Originally aimed at `net` because of Fixes:; moved
to net-next. Absence of stable-list discussion is not a NAK.
===============================================================
PHASE 5: CODE SEMANTIC ANALYSIS
===============================================================
Step 5.1: IDENTIFY KEY FUNCTIONS IN THE DIFF
Record: `rds_message_unpin_worker` (new), `rds_message_put`,
`rds_rdma_op_unpin_pages` (new), `rds_rdma_free_op`,
`rds_atomic_op_unpin_page` (new), `rds_atomic_free_op`
Step 5.2: TRACE CALLERS
Record:
- `rds_message_put()`: send.c, ib_send.c, ib_cm.c, message.c, loop.c,
connection.c
- Confirmed atomic path: `rds_ib_tasklet_fn_send()` → `poll_scq()` →
`rds_ib_send_cqe_handler()` → `rds_message_put()` (`net/rds/ib_cm.c`,
`ib_send.c`)
- `rds_rdma_free_op()`: `rds_message_purge()` and `rds_cmsg_rdma_args()`
error path (process context)
- `rds_atomic_free_op()`: only `rds_message_purge()`
Step 5.3: TRACE CALLEES
Record: `unpin_user_pages_dirty_lock()` → `set_page_dirty_lock()` →
`lock_page()` (sleeping). Non-dirty path uses `unpin_user_pages()` only.
Worker uses `queue_work(rds_wq)` / `kfree`.
Step 5.4: FOLLOW THE CALL CHAIN
Record: Userspace `sendmsg` with `RDS_CMSG_RDMA_ARGS` / atomic cmsg → IB
RDMA READ or atomic WR → CQ completion in tasklet → final put. Reachable
from sockets if `CONFIG_RDS_RDMA`. Not a theoretical path.
Step 5.5: SEARCH FOR SIMILAR PATTERNS
Record: Local `__rds_ib_teardown_mr()` still does `set_page_dirty()` +
`put_page()` (series patch 4). `rds_info.c` unpins from getsockopt
(process context) — OK. Same class of bug is already treated as
backport-worthy in this tree (`88fdf5948f538` Don't sleep inside
`rds_ib_conn_path_shutdown`).
===============================================================
PHASE 6: CROSS-REFERENCING AGAINST THE LOCAL TREE
===============================================================
Step 6.1: DOES THE BUGGY CODE EXIST IN THIS TREE?
Record: Yes. This checkout is **5.10.270** (`pending-5.10`, `git
describe` `p-5.10-1-gce56b7a73b64b`).
- `net/rds/rdma.c:500` and `:517` call `unpin_user_pages_dirty_lock()`
- `rds_ib_tasklet_fn_send` still a tasklet
- `rds_pin_pages()` uses `FOLL_LONGTERM`
- Introduced by `0d4597c8c5ab` (v5.7-era), present on this branch
Step 6.2: CHECK FOR BACKPORT COMPLICATIONS
Record: Semantic apply is straightforward. `rdma.c` / `rds.h` match the
old side of the diff. `message.c` context after `rds_message_put()`
still assigns `hdr->h_exthdr[0]` instead of `memset` of the ext header —
**minor context conflict**, not a rework. `work_struct` / `queue_work` /
`in_task()` are available (`rds.h` → `sock.h` → `netdevice.h` →
`workqueue.h`; `hardirq.h` → `preempt.h`). `rds_wq` is destroyed on
module exit.
Step 6.3: CHECK IF RELATED FIXES ARE ALREADY HERE
Record: No equivalent deferred-unpin fix. `338a3ba30c5ef` is a different
deadlock.
===============================================================
PHASE 7: SUBSYSTEM AND MAINTAINER CONTEXT
===============================================================
Step 7.1: IDENTIFY THE SUBSYSTEM AND ITS CRITICALITY
Record: [net/rds] IMPORTANT for RDS-over-IB (Oracle RAC / HPC). Not core
networking; production users of 5.10 LTS do run this.
Step 7.2: ASSESS SUBSYSTEM ACTIVITY
Record: Mature. Core free/put path has been stable for years; this is a
long-standing latent bug, not churn on new code.
===============================================================
PHASE 8: IMPACT AND RISK ASSESSMENT
===============================================================
Step 8.1: DETERMINE WHO IS AFFECTED
Record: Config-specific / driver-specific: `CONFIG_RDS` +
`CONFIG_RDS_RDMA`, IB completions of RDMA READ / atomic ops against
pinned user memory (including MAP_SHARED file pages).
Step 8.2: DETERMINE THE TRIGGER CONDITIONS
Record: Common on RDS RDMA READ completion. Timing-dependent on folio
already locked (writeback). Unprivileged user with an RDS socket and
registered memory can hit it. Not every boot.
Step 8.3: DETERMINE THE FAILURE MODE SEVERITY
Record: Sleep in softirq / `lock_page()` from tasklet → deadlock or
crash. **CRITICAL**. File-backed `set_page_dirty()` alternative would
also be CRITICAL (fs lock deadlock / mapping UAF).
Step 8.4: CALCULATE RISK-BENEFIT RATIO
Record:
- Benefit: high for RDS/IB users (prevents deadlock/oops on a real
completion path)
- Risk: low–medium (workqueue deferral, ~70 lines, one subsystem; worker
does not call into the IB module)
- Ratio: benefit outweighs risk. Larger than a 5-line NULL check, but
the pattern is standard and v4 was specifically reduced to avoid
unload races.
===============================================================
PHASE 9: FINAL SYNTHESIS
===============================================================
Step 9.1: COMPILE THE EVIDENCE
FOR:
- Sleep in softirq is a real, important bug
- Path exists on this 5.10 tree and is userspace-reachable via RDS RDMA
- Fixes target `0d4597c8c5ab` is in the tree
- Small, RDS-only, no new uAPI
- Needed kernel APIs exist
- v4 design addresses earlier review NAKs
- `destroy_workqueue(rds_wq)` drains deferred work
- RDS maintainer-authored; netdev applied it upstream
AGAINST:
- ~70 lines and a new work item (not a one-liner)
- Queued to net-next; Kicinski was uneasy reviewing it
- No syzbot/Tested-by on the committed message
- Trivial `message.c` context tweak for `rds_message_populate_header()`
UNRESOLVED:
- Could not fetch lore.kernel.org in-browser (403); used b4 mbox instead
- No evidence of a later revert or follow-up for `d100966325f7` (search
did not turn one up)
Step 9.2: APPLY THE STABLE RULES CHECKLIST
1. Obviously correct and tested? **PASS** — v4 is the review-fixed
design; mechanism matches the bug. (Confidence is “good”, not
“trivial one-liner”.)
2. Fixes a real bug that affects users? **PASS**
3. Important issue? **PASS** — deadlock/crash, CRITICAL
4. Small and contained? **PASS** — 3 files, RDS only
5. No new features or APIs? **PASS**
6. Can apply to the local tree? **PASS** — code present; at most a minor
context adjustment
Step 9.3: CHECK FOR EXCEPTION CATEGORIES
Record: None (not a device ID/quirk/DT/build/docs exception). Qualifies
as a normal important bugfix.
Step 9.4: MAKE YOUR DECISION
This tree still unpins and dirties RDMA/atomic pages in the IB send
tasklet. That can sleep. Deferring only that unpin onto existing
`rds_wq` is the right fix here and does not need the rest of the v4
series.
What it solves for 5.10 users: RDS-over-InfiniBand RDMA READ/atomic
completion can call `lock_page()` in softirq, which can deadlock or
crash. That is exactly the class of bug stable is for.
Risk vs benefit: contained RDS change, process-context unpin, workqueue
drained on unload. Benefit is preventing a serious completion-path
failure.
Concerns: expect a small `message.c` context edit around
`rds_message_populate_header()`; do not pull series patches 2–4 as
prerequisites.
Verification:
- [Phase 1] Tags: SOB Henderson/Kicinski; Link msgid; lore v4 had Fixes:
0d4597c8c5ab
- [Phase 2] Diff: defer dirtying unpin via work item; RDMA write still
unpins in caller
- [Phase 3] `git describe` / Makefile: 5.10.270; blame unpin lines →
0d4597c8c5ab; ancestor check exit 0
- [Phase 3] `338a3ba30c5ef` is a different recv.c deadlock
- [Phase 4] `b4 mbox`: 8 msgs, v1–v4, applied as d100966325f7; Kicinski
queued net-next
- [Phase 4] WebFetch lore/msgid.link: 403; discussion from b4 + openwall
- [Phase 5] Call chain verified in ib_cm.c / ib_send.c;
`set_page_dirty_lock()` uses `lock_page()` in mm/page-writeback.c
- [Phase 5] Other `rds_rdma_free_op()` caller is process-context error
path in rdma.c:796
- [Phase 6] Local rdma.c still has sleeping unpin; `in_task()` and
`rds_wq` exist; messages kzalloc’d
- [Phase 6] Header context differs (`h_exthdr[0]` vs memset) — minor
backport
- [Phase 8] Failure mode: sleep-in-softirq / deadlock, CRITICAL
- UNVERIFIED: live lore HTML; no later revert found
**YES**
net/rds/message.c | 26 +++++++++++++++++++++++++
net/rds/rdma.c | 49 ++++++++++++++++++++++++++++++++++++-----------
net/rds/rds.h | 10 ++++++++++
3 files changed, 74 insertions(+), 11 deletions(-)
diff --git a/net/rds/message.c b/net/rds/message.c
index 19202aa33f6f4..bdd340f4cce65 100644
--- a/net/rds/message.c
+++ b/net/rds/message.c
@@ -180,6 +180,19 @@ static void rds_message_purge(struct rds_message *rm)
kref_put(&rm->atomic.op_rdma_mr->r_kref, __rds_put_mr_final);
}
+static void rds_message_unpin_worker(struct work_struct *work)
+{
+ struct rds_message *rm = container_of(work, struct rds_message,
+ m_unpin_work);
+
+ if (rm->rdma.op_unpin_deferred)
+ rds_rdma_op_unpin_pages(&rm->rdma);
+ if (rm->atomic.op_unpin_deferred)
+ rds_atomic_op_unpin_page(&rm->atomic);
+
+ kfree(rm);
+}
+
void rds_message_put(struct rds_message *rm)
{
rdsdebug("put rm %p ref %d\n", rm, refcount_read(&rm->m_refcount));
@@ -187,8 +200,21 @@ void rds_message_put(struct rds_message *rm)
if (refcount_dec_and_test(&rm->m_refcount)) {
BUG_ON(!list_empty(&rm->m_sock_item));
BUG_ON(!list_empty(&rm->m_conn_item));
+
rds_message_purge(rm);
+ /* A final put in atomic context cannot dirty the ops'
+ * user pages on unpin, so rds_rdma_free_op() and
+ * rds_atomic_free_op() deferred it. Finish the unpin,
+ * and the free, from process context.
+ */
+ if (rm->rdma.op_unpin_deferred ||
+ rm->atomic.op_unpin_deferred) {
+ INIT_WORK(&rm->m_unpin_work, rds_message_unpin_worker);
+ queue_work(rds_wq, &rm->m_unpin_work);
+ return;
+ }
+
kfree(rm);
}
}
diff --git a/net/rds/rdma.c b/net/rds/rdma.c
index 34d9333e4229f..37695056756be 100644
--- a/net/rds/rdma.c
+++ b/net/rds/rdma.c
@@ -483,22 +483,36 @@ void rds_rdma_unuse(struct rds_sock *rs, u32 r_key, int force)
kref_put(&mr->r_kref, __rds_put_mr_final);
}
-void rds_rdma_free_op(struct rm_rdma_op *ro)
+void rds_rdma_op_unpin_pages(struct rm_rdma_op *ro)
{
unsigned int i;
+ for (i = 0; i < ro->op_nents; i++) {
+ struct page *page = sg_page(&ro->op_sg[i]);
+
+ /* Mark page dirty if it was possibly modified, which
+ * is the case for a RDMA_READ which copies from remote
+ * to local memory
+ */
+ unpin_user_pages_dirty_lock(&page, 1, !ro->op_write);
+ }
+}
+
+void rds_rdma_free_op(struct rm_rdma_op *ro)
+{
if (ro->op_odp_mr) {
kref_put(&ro->op_odp_mr->r_kref, __rds_put_mr_final);
+ } else if (in_task() || ro->op_write) {
+ /* An RDMA write's pages are only read by the remote
+ * side; unpinning without dirtying does not sleep.
+ */
+ rds_rdma_op_unpin_pages(ro);
} else {
- for (i = 0; i < ro->op_nents; i++) {
- struct page *page = sg_page(&ro->op_sg[i]);
-
- /* Mark page dirty if it was possibly modified, which
- * is the case for a RDMA_READ which copies from remote
- * to local memory
- */
- unpin_user_pages_dirty_lock(&page, 1, !ro->op_write);
- }
+ /* Dirtying the pages on unpin can sleep; leave them
+ * pinned and have rds_message_put() finish the unpin
+ * from process context.
+ */
+ ro->op_unpin_deferred = 1;
}
kfree(ro->op_notifier);
@@ -507,7 +521,7 @@ void rds_rdma_free_op(struct rm_rdma_op *ro)
ro->op_odp_mr = NULL;
}
-void rds_atomic_free_op(struct rm_atomic_op *ao)
+void rds_atomic_op_unpin_page(struct rm_atomic_op *ao)
{
struct page *page = sg_page(ao->op_sg);
@@ -515,6 +529,19 @@ void rds_atomic_free_op(struct rm_atomic_op *ao)
* is the case for a RDMA_READ which copies from remote
* to local memory */
unpin_user_pages_dirty_lock(&page, 1, true);
+}
+
+void rds_atomic_free_op(struct rm_atomic_op *ao)
+{
+ if (in_task()) {
+ rds_atomic_op_unpin_page(ao);
+ } else {
+ /* Dirtying the page on unpin can sleep; leave it
+ * pinned and have rds_message_put() finish the unpin
+ * from process context.
+ */
+ ao->op_unpin_deferred = 1;
+ }
kfree(ao->op_notifier);
ao->op_notifier = NULL;
diff --git a/net/rds/rds.h b/net/rds/rds.h
index a029e5fcdea72..a4816da162930 100644
--- a/net/rds/rds.h
+++ b/net/rds/rds.h
@@ -424,6 +424,12 @@ struct rds_message {
void *m_final_op;
+ /* Unpins the ops' user pages and frees the message from
+ * process context when the final put happens in atomic
+ * context: dirtying the pages on unpin can sleep.
+ */
+ struct work_struct m_unpin_work;
+
struct {
struct rm_atomic_op {
int op_type;
@@ -447,6 +453,7 @@ struct rds_message {
unsigned int op_mapped:1;
unsigned int op_silent:1;
unsigned int op_active:1;
+ unsigned int op_unpin_deferred:1;
struct scatterlist *op_sg;
struct rds_notifier *op_notifier;
@@ -462,6 +469,7 @@ struct rds_message {
unsigned int op_mapped:1;
unsigned int op_silent:1;
unsigned int op_active:1;
+ unsigned int op_unpin_deferred:1;
unsigned int op_bytes;
unsigned int op_nents;
unsigned int op_count;
@@ -938,6 +946,8 @@ int rds_cmsg_rdma_map(struct rds_sock *rs, struct rds_message *rm,
struct cmsghdr *cmsg);
void rds_rdma_free_op(struct rm_rdma_op *ro);
void rds_atomic_free_op(struct rm_atomic_op *ao);
+void rds_rdma_op_unpin_pages(struct rm_rdma_op *ro);
+void rds_atomic_op_unpin_page(struct rm_atomic_op *ao);
void rds_rdma_send_complete(struct rds_message *rm, int wc_status);
void rds_atomic_send_complete(struct rds_message *rm, int wc_status);
int rds_cmsg_atomic(struct rds_sock *rs, struct rds_message *rm,
--
2.53.0
^ permalink raw reply related [flat|nested] 32+ messages in thread* [PATCH AUTOSEL 6.18-6.1] selftests: net: reuseport_bpf_numa: consider cpuless numa node
2026-10-19 13:44 [PATCH AUTOSEL 6.18-6.12] net: dsa: mt7530: check CORE_PLL_GROUP4 access in mt7531_setup() Sasha Levin
2026-10-19 13:44 ` [PATCH AUTOSEL 6.18-5.10] net/rds: don't use unpin_user_pages_dirty_lock() from atomic context Sasha Levin
@ 2026-10-19 13:45 ` Sasha Levin
2026-10-19 13:45 ` [PATCH AUTOSEL 6.18-6.1] net: stmmac: fix device node reference leaks in stmmac_mtl_setup() Sasha Levin
` (27 subsequent siblings)
29 siblings, 0 replies; 32+ messages in thread
From: Sasha Levin @ 2026-10-19 13:45 UTC (permalink / raw)
To: patches, stable
Cc: Feng Tang, Simon Horman, Paolo Abeni, Sasha Levin, davem,
edumazet, kuba, shuah, netdev, linux-kselftest, linux-kernel
From: Feng Tang <feng.tang@linux.alibaba.com>
[ Upstream commit 88b8d85e889e2fe1cce3e66e50c3029dda94842a ]
reuseport_bpf_numa case failed when testing on a platform with CXL
memory:
#./reuseport_bpf_numa
---- IPv4 UDP ----
send node 0, receive socket 0
./reuseport_bpf_numa: failed to pin to node: Invalid argument
The root cause is that the platform has 2 numa nodes: node 0 has
both cpu and memory, while node 1 is a CXL node which only has
memory, and caused numa_run_on_node() to fail.
Add sanity check to skip cpuless numa node for the numa binding test.
Signed-off-by: Feng Tang <feng.tang@linux.alibaba.com>
Reviewed-by: Simon Horman <horms@kernel.org>
Link: https://patch.msgid.link/20260807002436.43991-1-feng.tang@linux.alibaba.com
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
LLM Generated explanations, may be completely bogus:
## Phase 1: Commit message forensics
**Step 1.1: Subject line**
- Record: `[selftests: net]` `[consider]` skip CPU-less NUMA nodes in
`reuseport_bpf_numa` so the test does not abort when
`numa_run_on_node()` cannot pin to a memory-only node.
**Step 1.2: Tags**
- Record: `Reviewed-by: Simon Horman <horms@kernel.org>`; `Link: https:/
/patch.msgid.link/20260807002436.43991-1-feng.tang@linux.alibaba.com`;
`Signed-off-by: Feng Tang <feng.tang@linux.alibaba.com>`; `Signed-off-
by: Paolo Abeni <pabeni@redhat.com>`.
- No `Fixes:`, no `Reported-by:`, no `Tested-by:`, no `Cc: stable`
(absence is expected). Ignore any pipeline `Signed-off-by: Sasha
Levin` / `[Upstream commit ...]` markers.
**Step 1.3: Body**
- Record: On a 2-node CXL box, node 0 has CPU+memory and node 1 is
memory-only. The test prints `send node 0, receive socket 0` then dies
with `failed to pin to node: Invalid argument` from
`numa_run_on_node()`. Root cause is treating every bit in
`numa_nodes_ptr` as a pin-able CPU node. Fix: skip CPU-less nodes.
**Step 1.4: Hidden bug fix?**
- Record: Yes. “Consider” is test-robustness language, but the program
currently `error(1, ...)` and fails. This is a real false failure, not
style cleanup.
## Phase 2: Diff analysis
**Step 2.1: Inventory**
- Record: One file, `tools/testing/selftests/net/reuseport_bpf_numa.c`.
About +24 lines. New helper `is_cpuless_node()`; `test()` gains two
`continue`s. Surgical, single-file, userspace selftest only.
**Step 2.2: Flow**
- Record: Before: both forward and reverse loops skip only nodes unset
in `numa_nodes_ptr`, then call `send_from_node()` which does
`numa_run_on_node()` and aborts on failure. After: also skip nodes
whose libnuma CPU mask has weight 0. Normal send/receive path for CPU-
bearing nodes is unchanged.
**Step 2.3: Bug mechanism**
- Record: Logic/correctness in a selftest. Memory-only NUMA nodes are
present in `numa_nodes_ptr` (the 2021 skip does not drop them) but
have an empty CPU mask, so pinning returns EINVAL. `is_cpuless_node()`
uses `numa_allocate_cpumask()` / `numa_node_to_cpus()` /
`numa_bitmask_weight()`; on helper error it returns false (do not
skip), which is conservative.
**Step 2.4: Fix quality**
- Record: Obviously correct and minimal. Skipping a CPU-less node is
right: `bpf_get_numa_node_id()` is the current CPU’s node, so a
memory-only node can never be the sender. Regression risk is very low
(userspace test only). Helper allocation failure still hits the old
abort path.
## Phase 3: Git history
**Step 3.1: Blame**
- Record: Loops come from `3c2c3c16aaf6a` (Daniel Borkmann, 2016-10-21,
“reuseport, bpf: add test case for bpf_get_numa_node_id”), first
contained in v4.10-rc1. The existing skip is `a38bc45a08e97` (Kleber
Sacilotto de Souza, 2021-11-01), contained in v5.16-rc1: skip nodes
not in `numa_nodes_ptr` (non-consecutive IDs). That does not cover
CPU-less memory nodes.
**Step 3.2: Fixes: tag**
- Record: None. Buggy assumption is in the original 2016 test, still
present here.
**Step 3.3: File history**
- Record: This tree only has later `0273d10182ec4` (ARRAY_SIZE warning),
then the 2021 skip, 2018 numa-skip-if-unavailable, SPDX, and the
original test. No `setup_netns()` in this file (0 matches).
Standalone; not patch X/Y.
**Step 3.4: Author**
- Record: Feng Tang has other tools/selftests work in this history (e.g.
mm `compaction_test`) but no other net `reuseport_bpf_numa` commits.
Not the net maintainer. Applied by Paolo Abeni after Simon Horman’s
review.
**Step 3.5: Prerequisites**
- Record: Needs existing libnuma usage (`numa_nodes_ptr`,
`numa_run_on_node`, `-lnuma`). All present. Makefile already has
`$(OUTPUT)/reuseport_bpf_numa: LDLIBS += -lnuma`. No missing kernel
APIs. Can apply standalone.
## Phase 4: Mailing list / external
**Step 4.1: Original discussion**
- Record: `b4 mbox` for msgid
`20260807002436.43991-1-feng.tang@linux.alibaba.com` returned 404;
lore.kernel.org fetches returned 403. Recovered from
Openwall/LKML/Patchew.
- v1 (`20260730113040.75718-1-...`): check inside `send_from_node()`,
return -1, no NULL check on `numa_allocate_cpumask()`.
- v2 (`[PATCH net v2]`): lift check into `test()`, add alloc-failure
handling; typo “unlikeyly”; unnecessary `inline`.
- v3 (`[PATCH net-next v3]`): spelling + drop `inline` (Jakub, Sashiko).
Committed version matches v3.
- No NAKs. No reviewer `Cc: stable` found.
**Step 4.2: Reviewers**
- Record: To/Cc included Jakub Kicinski, David Miller, Eric Dumazet,
Paolo Abeni, Simon Horman, Shuah Khan, Andi Kleen, netdev, linux-
kselftest, bpf. Jakub reviewed v2 (inline). Simon Horman `Reviewed-by`
on v3. Paolo applied.
**Step 4.3: Bug report**
- Record: Author reproduced on CXL hardware; commit body has the exact
abort. No syzbot/bugzilla. Severity for testers: hard FAIL after the
first successful node-0 send.
**Step 4.4: Series**
- Record: Single patch, three revisions. No sibling patches required.
**Step 4.5: Stable list**
- Record: No stable-list hit found. UNVERIFIED that nobody asked for it
on stable@ (lore 403).
## Phase 5: Semantic analysis
**Step 5.1: Functions**
- Record: Adds `is_cpuless_node()`. Touches `test()`. `send_from_node()`
unchanged.
**Step 5.2: Callers**
- Record: `is_cpuless_node` only from `test()`. `test()` only from
`main()` (IPv4/IPv6 × UDP/TCP). Userspace kselftest, not kernel.
**Step 5.3: Callees**
- Record: libnuma `numa_allocate_cpumask`, `numa_node_to_cpus`,
`numa_bitmask_weight`, `numa_bitmask_free`. Already linked with
`-lnuma`.
**Step 5.4: Reachability**
- Record: Triggered by running `./reuseport_bpf_numa` on NUMA
(`CONFIG_NUMA=y` in `tools/testing/selftests/net/config`) with at
least one CPU-less node. Not a syscall/kernel path.
**Step 5.5: Similar patterns**
- Record: Same file already skips unavailable nodes (`a38bc45a08e97`).
This is the CPU-less counterpart. No other `numa_run_on_node`
selftests in this tree.
## Phase 6: This tree (6.12.111)
**Step 6.1: Buggy code present?**
- Record: Yes. Local tree is `pending-6.12` / `p-6.12-3-gc504cf0baa3d8`,
`make kernelversion` = **6.12.111**. File has the 2016 loops and the
2021 `numa_nodes_ptr` skip, and still calls `send_from_node()` without
a CPU-less check (`git merge-base --is-ancestor` for both historical
commits succeeded). `git log --grep="consider cpuless"` on this file
is empty. CXL is in this tree (`drivers/cxl/`).
**Step 6.2: Backport difficulty**
- Record: Clean apply. The three functional contexts exist verbatim
(helper insertion after `attach_bpf()`, two loop sites). Mainline
trailing `setup_netns()` context is **not** in 6.12 and is not part of
the v3 hunks. `bool` is already used in other net selftests here
without extra headers.
**Step 6.3: Related fixes already here?**
- Record: Only the 2021 unavailable-node skip. It does not fix CXL
memory-only nodes. No equivalent `is_cpuless_node` fix.
## Phase 7: Subsystem context
**Step 7.1:** Record: `tools/testing/selftests/net` — kselftest for
SO_REUSEPORT + `bpf_get_numa_node_id`. Criticality: PERIPHERAL for
production runtime; IMPORTANT for people who run net kselftests (this
6.12.y tree already took ~100 `tools/testing/selftests/net` commits
since `v6.12`).
**Step 7.2:** Record: Net selftests are actively backported here (UAF in
tests, br_netfilter false failures, HZ=100 skips, portable timeouts,
etc.).
## Phase 8: Impact and risk
**Step 8.1:** Record: Testers running this binary on NUMA machines with
CPU-less nodes (CXL memory, similar memory-only nodes). Not a production
kernel path.
**Step 8.2:** Record: Trigger is running the selftest on that topology.
First CPU node can succeed; the next CPU-less node aborts. Unprivileged
if the test is run that way; not a kernel exploit path.
**Step 8.3:** Record: Failure mode = kselftest abort (`error(1, errno,
"failed to pin to node")`). Severity LOW–MEDIUM for the kernel (no
oops/corruption), HIGH for CI on those machines (hard fail).
**Step 8.4:** Record: Benefit medium for CXL/NUMA testers of 6.12.y;
risk very low (userspace, +24 lines, no kernel change). Ratio favors
backport on this tree, which already takes kselftest false-fail fixes.
## Phase 9: Synthesis
**For**
- Real, reproduced FAIL on CXL memory-only NUMA; 6.12 has both the test
and CXL.
- Completes the 2021 skip: `numa_nodes_ptr` still includes CPU-less
nodes.
- Small, reviewed, v3 is the applied form; no series deps.
- Applies cleanly here; zero kernel runtime risk.
- This tree regularly backports the same class (e.g. `selftests:
netfilter: packetdrill: avoid failure on HZ=100 kernel`,
vxlan/br_netfilter false-fail fixes).
- v2 was sent to `net` as a bugfix.
**Against**
- Not a kernel crash, security hole, or data-corruption fix.
- Affects one selftest on a subset of NUMA topologies.
- Final submission was `net-next`; no `Cc: stable` in the thread I could
read.
**Unresolved**
- Exact mainline SHA (not on this branch).
- lore.kernel.org / `b4` thread dump (403/404).
- Whether stable@ ever discussed it.
**Stable rules checklist**
1. Obviously correct and tested? **PASS** (libnuma weight 0; Reviewed-by
Horman; author reproduced).
2. Real bug that bothers people? **PASS** (hard selftest FAIL on real
CXL hardware).
3. Important (crash/security/corruption/deadlock)? **PASS** under this
tree’s documented “oh, that’s not good” / kselftest-false-fail
practice; not a production oops.
4. Small and contained? **PASS** (~24 lines, one file).
5. No new features/APIs? **PASS**.
6. Can apply to this tree? **PASS** (contexts verified).
**Exception category:** none of ID/quirk/DT/build/docs; it is a
kselftest correctness/robustness fix, which this 6.12.y tree already
accepts.
The test in 6.12.111 still assumes every exposed NUMA node has CPUs.
That is false on CXL memory-only nodes, so `reuseport_bpf_numa` aborts.
The patch only skips those nodes and does not change kernel behavior.
That is a contained, low-risk fix worth taking on this tree.
### Verification
- [Phase 1] Parsed tags: Reviewed-by Simon Horman; Link msgid; SOBs Feng
Tang / Paolo Abeni; no Fixes/Reported-by/Cc:stable.
- [Phase 2] Diff: +24 lines, `is_cpuless_node()` + two `continue`s in
`test()`; `send_from_node()` still `error(1)` on pin failure.
- [Phase 3] `git blame` loops: `3c2c3c16aaf6a` (2016) and
`a38bc45a08e97` (2021 skip). Both are ancestors of HEAD.
- [Phase 3] `git describe --contains`: original test v4.10-rc1; 2021
skip v5.16-rc1.
- [Phase 3] `git log --grep="consider cpuless"` on this file: empty (fix
not in tree).
- [Phase 4] `b4 mbox` msgid: 404; lore WebFetch: 403; used
Openwall/LKML/Patchew for v1–v3.
- [Phase 4] v1→v3: Sashiko NULL-check; lift skip out of
`send_from_node`; Jakub spelling/`inline`; Horman Reviewed-by; no NAK.
- [Phase 5] Callers: `main` → `test` → `send_from_node`; userspace only.
`grep numa_run_on_node` only this file under selftests.
- [Phase 6] `git describe HEAD` / `make kernelversion`: pending-6.12,
6.12.111. File matches patch contexts (`hunk1 present`, two loop
sites). No `setup_netns`. `drivers/cxl/` present. `CONFIG_NUMA=y` in
net selftest config. `-lnuma` already in Makefile.
- [Phase 6] `v6.12..HEAD -- tools/testing/selftests/net/`: 100 commits,
including false-fail/skip fixes.
- [Phase 8] Failure: selftest abort `failed to pin to node: Invalid
argument`; not a kernel oops.
- UNVERIFIED: lore.kernel.org full thread; stable@ discussion; mainline
SHA.
**YES**
.../selftests/net/reuseport_bpf_numa.c | 24 +++++++++++++++++++
1 file changed, 24 insertions(+)
diff --git a/tools/testing/selftests/net/reuseport_bpf_numa.c b/tools/testing/selftests/net/reuseport_bpf_numa.c
index c9ba36aa688e2..b04b511809ad2 100644
--- a/tools/testing/selftests/net/reuseport_bpf_numa.c
+++ b/tools/testing/selftests/net/reuseport_bpf_numa.c
@@ -104,6 +104,26 @@ static void attach_bpf(int fd)
close(bpf_fd);
}
+/*
+ * Return true if it is a cpuless node. Return false if it isn't or any
+ * error (very unlikely) happens during the libnuma calls.
+ */
+static bool is_cpuless_node(int node_id)
+{
+ struct bitmask *cpumask;
+ bool ret = false;
+
+ cpumask = numa_allocate_cpumask();
+ if (!cpumask)
+ return ret;
+
+ if (!numa_node_to_cpus(node_id, cpumask) && !numa_bitmask_weight(cpumask))
+ ret = true;
+
+ numa_bitmask_free(cpumask);
+ return ret;
+}
+
static void send_from_node(int node_id, int family, int proto)
{
struct sockaddr_storage saddr, daddr;
@@ -213,6 +233,8 @@ static void test(int *rcv_fd, int len, int family, int proto)
for (node = 0; node < len; ++node) {
if (!numa_bitmask_isbitset(numa_nodes_ptr, node))
continue;
+ if (is_cpuless_node(node))
+ continue;
send_from_node(node, family, proto);
receive_on_node(rcv_fd, len, epfd, node, proto);
}
@@ -221,6 +243,8 @@ static void test(int *rcv_fd, int len, int family, int proto)
for (node = len - 1; node >= 0; --node) {
if (!numa_bitmask_isbitset(numa_nodes_ptr, node))
continue;
+ if (is_cpuless_node(node))
+ continue;
send_from_node(node, family, proto);
receive_on_node(rcv_fd, len, epfd, node, proto);
}
--
2.53.0
^ permalink raw reply related [flat|nested] 32+ messages in thread* [PATCH AUTOSEL 6.18-6.1] net: stmmac: fix device node reference leaks in stmmac_mtl_setup()
2026-10-19 13:44 [PATCH AUTOSEL 6.18-6.12] net: dsa: mt7530: check CORE_PLL_GROUP4 access in mt7531_setup() Sasha Levin
2026-10-19 13:44 ` [PATCH AUTOSEL 6.18-5.10] net/rds: don't use unpin_user_pages_dirty_lock() from atomic context Sasha Levin
2026-10-19 13:45 ` [PATCH AUTOSEL 6.18-6.1] selftests: net: reuseport_bpf_numa: consider cpuless numa node Sasha Levin
@ 2026-10-19 13:45 ` Sasha Levin
2026-10-19 13:46 ` [PATCH AUTOSEL 6.18-5.10] netfilter: conntrack: tcp: use UNACK timeout for non-closing RST packets Sasha Levin
` (26 subsequent siblings)
29 siblings, 0 replies; 32+ messages in thread
From: Sasha Levin @ 2026-10-19 13:45 UTC (permalink / raw)
To: patches, stable
Cc: Md Rabbani, Jakub Kicinski, Sasha Levin, maxime.chevallier,
andrew+netdev, davem, edumazet, pabeni, mcoquelin.stm32,
alexandre.torgue, netdev, linux-stm32, linux-arm-kernel,
linux-kernel
From: Md Rabbani <rabbanyhmm@gmail.com>
[ Upstream commit 317fe168f4c0fc30e5c375c22d9c8911940aea9b ]
In stmmac_mtl_setup(), q_node is shared across the RX and TX queue
parsing loops. When the RX queue loop breaks early because the number
of parsed queues reaches plat->rx_queues_to_use, q_node retains an
acquired reference count. If the error check passes
(queue == plat->rx_queues_to_use), execution proceeds directly to the
TX queue loop, where of_get_next_child() immediately overwrites q_node
with the first TX child, permanently leaking the retained RX child
device node reference.
Switch both loops to for_each_child_of_node_scoped() so child node
references are automatically dropped upon loop exit or early break,
and remove the now-unnecessary function-scoped q_node variable and
its manual of_node_put() at the exit label.
Signed-off-by: Md Rabbani <rabbanyhmm@gmail.com>
Link: https://patch.msgid.link/20260821055718.57-1-rabbanyhmm@gmail.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
LLM Generated explanations, may be completely bogus:
The leak is real in this 6.12 tree: `stmmac_mtl_setup()` can drop an OF
child reference when RX queue parsing breaks early, and in-tree DTs
actually hit that path.
## PHASE 1: COMMIT MESSAGE FORENSICS
**Step 1.1: PARSE THE SUBJECT LINE**
- Record: `[net: stmmac:] [fix] [OF device-node reference leaks in
stmmac_mtl_setup()]`
**Step 1.2: PARSE ALL COMMIT MESSAGE TAGS**
- Record:
- `Signed-off-by: Md Rabbani <rabbanyhmm@gmail.com>` (author)
- `Link:
https://patch.msgid.link/20260821055718.57-1-rabbanyhmm@gmail.com`
- `Signed-off-by: Jakub Kicinski <kuba@kernel.org>` (netdev
maintainer; applied to `netdev/net`)
- No `Fixes:`, `Reported-by:`, `Tested-by:`, `Reviewed-by:`, `Acked-
by:`, or `Cc: stable` (absence of Fixes/Cc-stable is expected for
this review)
- Upstream SHA (from patchwork bot):
`317fe168f4c0fc30e5c375c22d9c8911940aea9b`
**Step 1.3: ANALYZE THE COMMIT BODY TEXT**
- Record: [Bug] Shared `q_node` is used by both RX and TX
`for_each_child_of_node()` loops. An early `break` in the RX loop
leaves `q_node` holding a live OF reference. If `queue ==
plat->rx_queues_to_use`, the function continues into the TX loop,
which calls `of_get_next_child(tx_node, NULL)` and overwrites `q_node`
without putting the RX child.
- [Symptom] Permanent `device_node` refcount leak (kobject/`device_node`
never released).
- [Version info] None in the message. The loops date to 2017
(`d976a525c37127`, v4.12-rc1).
- [Root cause] Early-break + reused iterator; `of_get_next_child(parent,
NULL)` does not drop the previous pointer.
**Step 1.4: DETECT HIDDEN BUG FIXES**
- Record: Not hidden. Subject/body explicitly describe a refcount leak.
The `_scoped` conversion is the fix mechanism, not a style-only
cleanup.
## PHASE 2: DIFF ANALYSIS
**Step 2.1: INVENTORY THE CHANGES**
- Record: Single file
`drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c`. Actual commit
is **+2 / -4**. Functions: `stmmac_mtl_setup()` only. Surgical,
single-file.
**Step 2.2: UNDERSTAND THE CODE FLOW CHANGE**
- Record:
- RX loop: `for_each_child_of_node(rx_node, q_node)` →
`for_each_child_of_node_scoped(...)`. Early `break` now auto-drops
the current child via `__free(device_node)`.
- TX loop: same conversion.
- Function-scope `struct device_node *q_node` removed;
`of_node_put(q_node)` at `out:` removed (would not compile with the
loop-scoped iterator).
- Probe/DT parse path only; no datapath change.
**Step 2.3: IDENTIFY THE BUG MECHANISM**
- Record: [Category] Reference counting / resource leak.
- [Mechanism] `for_each_child_of_node` is `of_get_next_child()`, which
gets `next` and puts `prev`. `break` skips the put of the current
node. Reusing the same pointer in the TX loop with `prev=NULL` leaks
that RX child. TX extra children were previously dropped by the
trailing `of_node_put(q_node)`; RX extra children were not.
**Step 2.4: ASSESS THE FIX QUALITY**
- Record: Obviously correct. `for_each_child_of_node_scoped` exists in
this tree and is already used widely. `__free(device_node)` is `if
(_T) of_node_put(_T)`, so NULL at loop end is safe. Regression risk is
very low: no locking, no ABI, no behavior change besides dropping the
leaked ref. The `_scoped` form is stronger than a one-off
`of_node_put()` before the TX loop because it also covers TX early-
break without a manual put.
## PHASE 3: GIT HISTORY INVESTIGATION
**Step 3.1: BLAME THE CHANGED LINES**
- Record: Buggy loops introduced by `d976a525c37127` ("net: stmmac:
multiple queues dt configuration", Joao Pinto, 2017-03-10), first in
**v4.12-rc1**. Present for many years. `goto out` / extra
`of_node_put(q_node)` added by `2ee2132ffb83e` (2018), which still
leaves the RX-then-TX overwrite leak.
**Step 3.2: FOLLOW THE FIXES: TAG**
- Record: No `Fixes:` tag. Introducer is `d976a525c37127`, and `git
merge-base --is-ancestor d976a525c37127 HEAD` succeeds. That code is
in this tree.
**Step 3.3: CHECK FILE HISTORY FOR RELATED CHANGES**
- Record: Recent `stmmac_platform.c` work is unrelated (bus_id, probe
error path, IRQ). Historical OF leak fixes in this driver:
`a249708bc2aa1`, `d2ed0a7755fe1`, `4613b279bee79`. Standalone
`[PATCH]`, not `X/Y`.
**Step 3.4: CHECK THE AUTHOR'S OTHER COMMITS**
- Record: `git log --author="Md Rabbani"` on this path is empty in this
tree (new contributor). Applied by Jakub Kicinski to `netdev/net.git`
(fixes tree), which is a strong maintainer signal.
**Step 3.5: CHECK FOR DEPENDENT/PREREQUISITE COMMITS**
- Record: Depends on `for_each_child_of_node_scoped` /
`DEFINE_FREE(device_node)` (`34af4554fb0ce1` / `9448e55d032d99`,
2024-02-25). Both are already in this 6.12 tree (`git merge-base --is-
ancestor` succeeded). Can apply standalone. `stmmac_platform.c`
already includes `<linux/of.h>`, which includes `<linux/cleanup.h>`.
## PHASE 4: MAILING LIST AND EXTERNAL RESEARCH
**Step 4.1: FIND THE ORIGINAL PATCH DISCUSSION**
- Record: `b4 dig -c <sha>` cannot match because `317fe168f4c0` is not
an ancestor of HEAD. Used msgid instead. Lore thread: `https://lore.ke
rnel.org/netdev/20260821055718.57-1-rabbanyhmm@gmail.com/`
- Series: single v1 `[PATCH]` (2 messages: submission + patchwork-bot).
Applied to `netdev/net.git (main)` as
`https://git.kernel.org/netdev/net/c/317fe168f4c0`. No v2, no NAK.
**Step 4.2: CHECK WHO REVIEWED THE PATCH**
- Record: To/Cc included STMMAC maintainers (Torgue, Coquelin,
Chevallier), netdev maintainers (Miller, Dumazet, Kicinski, Abeni),
`netdev@`, `linux-stm32@`, `linux-arm-kernel@`. No written review
comments; Kicinski applied it to the **net fixes** tree.
**Step 4.3: SEARCH FOR THE BUG REPORT**
- Record: No syzbot/bugzilla. Bug is from code inspection. Real in-tree
DTs trigger it (see Phase 6/8).
**Step 4.4: CHECK FOR RELATED PATCHES AND SERIES**
- Record: Standalone. No other patches required.
**Step 4.5: CHECK STABLE MAILING LIST HISTORY**
- Record: lore.kernel.org/stable search for `stmmac_mtl_setup` shows
older stmmac stable backports (probe error-path, MDIO, Tx COE), not
this leak. No prior rejection of this fix.
## PHASE 5: CODE SEMANTIC ANALYSIS
**Step 5.1: KEY FUNCTIONS**
- Record: `stmmac_mtl_setup()` (modified). Helpers:
`for_each_child_of_node_scoped`, `of_get_next_child` /
`__of_get_next_child`, `of_node_put`.
**Step 5.2: TRACE CALLERS**
- Record: Only caller is `stmmac_probe_config_dt()` in the same file
(line 603). That is used via `devm_stmmac_probe_config_dt()` from many
glue drivers: `dwmac-rk`, `dwmac-qcom-ethqos`, `dwmac-imx`, `dwmac-
stm32`, `dwmac-sun8i`, `dwmac-mediatek`, `dwmac-generic`, and others.
Probe/init context, not IRQ/hot path.
**Step 5.3: TRACE CALLEES**
- Record: `of_parse_phandle`, `of_property_read_*`, `of_get_next_child`,
`of_node_put`. DT parse only.
**Step 5.4: FOLLOW THE CALL CHAIN**
- Record: Userspace cannot trigger it directly. It runs on every DT-
based stmmac probe. Reachable on common ARM/ARM64 SoCs (Rockchip,
i.MX, STM32, Qualcomm, MediaTek). Leak fires when the RX config node
has more children than `snps,rx-queues-to-use`.
**Step 5.5: SEARCH FOR SIMILAR PATTERNS**
- Record: Same file still has `for_each_child_of_node(np, mdio_node)` in
`stmmac_of_get_mdio()` (line 324); not the same shared-iterator bug.
In-tree DTs with extra RX children:
`arch/arm64/boot/dts/qcom/sa8540p-ride.dts` uses `snps,rx-queues-to-
use = <1>` with `queue0`–`queue3` on both ethernet0 and ethernet1.
## PHASE 6: CROSS-REFERENCING AGAINST THE LOCAL TREE
**Step 6.1: DOES THE BUGGY CODE EXIST IN THIS TREE?**
- Record: Yes. This tree is **6.12.111** (`pending-6.12`,
`p-6.12-3-gc504cf0baa3d8`). Current code at `stmmac_platform.c:177` /
`:240` still uses unscoped loops and `of_node_put(q_node)` at `:293`.
`317fe168f4c0` is **not** an ancestor of HEAD. Bug present since
v4.12.
**Step 6.2: CHECK FOR BACKPORT COMPLICATIONS**
- Record: Expected difficulty: **minor context conflicts**. Upstream
context has later `stmmac_axi_blen_to_mask` / `MTL_MAX_*` clamping
that 6.12 does not have. The three functional edits (two loop macros,
drop `q_node` + trailing put) match 6.12 1:1. Needs a small context-
adjusted backport, not a rewrite.
**Step 6.3: CHECK IF RELATED FIXES ARE ALREADY HERE**
- Record: No commit with this subject in this tree. Older stmmac
`of_node_put` fixes do not cover this iterator reuse.
## PHASE 7: SUBSYSTEM AND MAINTAINER CONTEXT
**Step 7.1: IDENTIFY THE SUBSYSTEM AND ITS CRITICALITY**
- Record: [Subsystem] `drivers/net/ethernet/stmicro/stmmac` (Synopsys
DWMAC). [Criticality] **IMPORTANT** — common platform Ethernet on many
SoCs, not core mm/VFS, not staging.
**Step 7.2: ASSESS SUBSYSTEM ACTIVITY**
- Record: Actively maintained (many recent stmmac fixes on this branch).
The leak itself is old (2017), not a new-feature bug.
## PHASE 8: IMPACT AND RISK ASSESSMENT
**Step 8.1: DETERMINE WHO IS AFFECTED**
- Record: Driver/config-specific: `CONFIG_STMMAC_ETH` +
`CONFIG_STMMAC_PLATFORM` + DT `snps,mtl-rx-config` with extra child
nodes. Confirmed affected in-tree board: Qualcomm SA8540P Ride
(`sa8540p-ride.dts`). Many other boards have matching child counts and
do not leak.
**Step 8.2: DETERMINE THE TRIGGER CONDITIONS**
- Record: Device probe (and every unbind/rebind). Common on boards whose
DT lists more queue children than `snps,rx-queues-to-use`. Not a
userspace syscall; not a race. Extra TX children were already put at
`out:`; extra RX children leak.
**Step 8.3: DETERMINE THE FAILURE MODE SEVERITY**
- Record: [Failure] One `device_node` ref leak per probe when extra RX
children exist. [Severity] **MEDIUM** — not oops/deadlock/corruption,
but a real lifetime leak of an OF kobject. Repeatable on module
reload. Can pin overlay/node teardown.
**Step 8.4: CALCULATE RISK-BENEFIT RATIO**
- Record: Benefit: medium (fixes a real, DT-triggered leak on a widely
used NIC driver, including an in-tree Qualcomm board). Risk: **very
low** (2 insertions / 4 deletions, existing helper, probe-only). Ratio
favors backport.
## PHASE 9: FINAL SYNTHESIS
**Step 9.1: COMPILE THE EVIDENCE**
FOR:
- Real OF refcount leak, mechanism verified against
`of_get_next_child()` / `__of_get_next_child()`.
- Buggy code is in this 6.12 tree and has been since v4.12.
- In-tree DT `sa8540p-ride.dts` has 4 RX queue children with `rx-queues-
to-use = <1>` — the leak path is not theoretical.
- Tiny, obviously correct fix; helper already in 6.12.
- Applied to `netdev/net` (fixes) by Kicinski.
- Same class of bug as prior stmmac `of_node_put` fixes already in
history.
- No new API/feature; no series dependency.
AGAINST:
- Probe-time leak of a single node, not a crash/UAF/security issue.
- Surrounding file context differs slightly from mainline, so `git am`
of the raw mainline diff may need a trivial refresh.
- No user crash report / syzbot.
UNRESOLVED:
- No written mailing-list review beyond maintainer application (v1
accepted as-is).
**Step 9.2: APPLY THE STABLE RULES CHECKLIST**
1. Obviously correct and tested? **PASS** — scoped put on break/exit is
the intended helper; applied to netdev/net.
2. Fixes a real bug that affects users? **PASS** — leak on probe;
triggered by in-tree Qualcomm DT.
3. Important issue? **PASS** at medium importance — resource leak, not
panic, but a genuine lifetime bug in a common Ethernet driver.
4. Small and contained? **PASS** — +2/-4, one function.
5. No new features or APIs? **PASS**
6. Can apply to the local tree? **PASS** — same loops exist; scoped
macro exists; minor context tweak only.
**Step 9.3: CHECK FOR EXCEPTION CATEGORIES**
- Record: Not a device-ID/quirk/DT/build/docs exception. Ordinary small
bugfix.
**Step 9.4: DECISION**
Backport to this 6.12 tree. The bug exists here, the helper exists here,
the fix is surgical, and at least one in-tree board hits it.
---
### What the commit solves and why it matters here
`stmmac_mtl_setup()` parses `snps,mtl-rx-config` / `snps,mtl-tx-config`
at probe. The RX iterator `break`s once `queue >=
plat->rx_queues_to_use`, leaving `q_node` referenced. The TX loop then
starts with `of_get_next_child(tx_node, NULL)`, which never puts that RX
child. That is a permanent OF node leak.
`arch/arm64/boot/dts/qcom/sa8540p-ride.dts` is written exactly that way
(1 queue in use, 4 queue child nodes).
### Stable rules / risk
Meets the rules: small, obviously correct, no new API, real leak, helper
already in 6.12. Risk is a possible trivial context conflict around
later mainline-only `stmmac_platform.c` edits; the functional change
does not depend on those.
### Verification
- [Phase 1] Parsed tags: author SOB, msgid Link, Kicinski SOB; no
Fixes/Reported-by; upstream `317fe168f4c0`
- [Phase 2] Diff: +2/-4 in `stmmac_mtl_setup()`; two loops to `_scoped`;
drop `q_node` + `of_node_put(q_node)`
- [Phase 2] `__of_get_next_child()` does `of_node_get(next);
of_node_put(prev)` (`drivers/of/base.c:600-603`); `prev=NULL` does not
put the overwritten pointer
- [Phase 3] Blame: loops from `d976a525c37127` (v4.12-rc1); ancestor of
HEAD
- [Phase 3] `34af4554fb0ce1` / `DEFINE_FREE(device_node)` are ancestors
of HEAD
- [Phase 3] `git merge-base --is-ancestor 317fe168f4c0 HEAD` failed (fix
not in this tree)
- [Phase 4] `b4 mbox` msgid: 2-message thread; applied to netdev/net; no
NAK
- [Phase 4] stable lore search: no discussion of this leak
- [Phase 5] Caller: `stmmac_probe_config_dt()` → many `dwmac-*` platform
probes
- [Phase 6] `git describe HEAD` / `make kernelversion`: 6.12.111
`pending-6.12`; buggy loops still present at lines 177 and 240
- [Phase 6] `sa8540p-ride.dts` `ethernet0_mtl_rx_setup` /
`ethernet1_mtl_rx_setup`: `rx-queues-to-use = <1>` with queue0–queue3
- [Phase 8] Failure mode: OF `device_node` leak at probe, severity
MEDIUM
- UNVERIFIED: Could not use `b4 dig -c` against HEAD (commit not in this
checkout); lore HTML fetch returned 403, raw/mbox succeeded
**YES**
drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c | 6 ++----
1 file changed, 2 insertions(+), 4 deletions(-)
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c
index 0cb51935c4056..ceb913a3a6c69 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c
@@ -131,7 +131,6 @@ static struct stmmac_axi *stmmac_axi_setup(struct platform_device *pdev)
static int stmmac_mtl_setup(struct platform_device *pdev,
struct plat_stmmacenet_data *plat)
{
- struct device_node *q_node;
struct device_node *rx_node;
struct device_node *tx_node;
u8 queue = 0;
@@ -174,7 +173,7 @@ static int stmmac_mtl_setup(struct platform_device *pdev,
plat->rx_sched_algorithm = MTL_RX_ALGORITHM_SP;
/* Processing individual RX queue config */
- for_each_child_of_node(rx_node, q_node) {
+ for_each_child_of_node_scoped(rx_node, q_node) {
if (queue >= plat->rx_queues_to_use)
break;
@@ -237,7 +236,7 @@ static int stmmac_mtl_setup(struct platform_device *pdev,
queue = 0;
/* Processing individual TX queue config */
- for_each_child_of_node(tx_node, q_node) {
+ for_each_child_of_node_scoped(tx_node, q_node) {
if (queue >= plat->tx_queues_to_use)
break;
@@ -290,7 +289,6 @@ static int stmmac_mtl_setup(struct platform_device *pdev,
out:
of_node_put(rx_node);
of_node_put(tx_node);
- of_node_put(q_node);
return ret;
}
--
2.53.0
^ permalink raw reply related [flat|nested] 32+ messages in thread* [PATCH AUTOSEL 6.18-5.10] netfilter: conntrack: tcp: use UNACK timeout for non-closing RST packets
2026-10-19 13:44 [PATCH AUTOSEL 6.18-6.12] net: dsa: mt7530: check CORE_PLL_GROUP4 access in mt7531_setup() Sasha Levin
` (2 preceding siblings ...)
2026-10-19 13:45 ` [PATCH AUTOSEL 6.18-6.1] net: stmmac: fix device node reference leaks in stmmac_mtl_setup() Sasha Levin
@ 2026-10-19 13:46 ` Sasha Levin
2026-10-19 13:46 ` [PATCH AUTOSEL 6.18-5.10] net: usb: qmi_wwan: add Rolling Wireless RN947R Sasha Levin
` (25 subsequent siblings)
29 siblings, 0 replies; 32+ messages in thread
From: Sasha Levin @ 2026-10-19 13:46 UTC (permalink / raw)
To: patches, stable
Cc: Minghao Zhang, Florian Westphal, Jianjun Chen, Pablo Neira Ayuso,
Sasha Levin, davem, edumazet, kuba, pabeni, netfilter-devel,
coreteam, netdev, linux-kernel
From: Minghao Zhang <zhangmh25@mails.tsinghua.edu.cn>
[ Upstream commit bf80e6802273a900311b44e47c9b1a59c6d2473c ]
Commit be0502a3f2e9 ("netfilter: conntrack: tcp: only close if RST
matches exact sequence") keeps an established conntrack entry in
ESTABLISHED when an in-window RST does not match the expected sequence
number exactly, so the endpoint can validate the RST with a challenge
ACK.
The timeout selection nevertheless uses the CLOSE timeout for every RST
packet. The bug is that timeout selection is based on the packet type,
not on the state transition result: even when RST validation keeps
new_state in ESTABLISHED, the timeout is still forced to
TCP_CONNTRACK_CLOSE.
Linux TCP independently rate limits challenge ACKs per socket. A second
non-exact RST can therefore arrive after the first challenge ACK has
restored the timeout but before the rate limit expires. The second RST
lowers the timeout to 10 seconds again while the endpoint suppresses the
second challenge ACK, allowing the conntrack entry to expire while both
TCP endpoints remain established.
Using the ESTABLISHED timeout for such RSTs would avoid this short
expiration window, but it could also retain stale entries for the
five-day default because conntrack cannot reliably match the endpoint's
exact TCP state.
Use the UNACK timeout for RST packets that leave the conntrack entry in
TCP_CONNTRACK_ESTABLISHED. Exact-match RSTs and accepted RST packet
trains still fall through to timeouts[new_state], which preserves the
CLOSE timeout when conntrack accepts the RST as closing the flow.
This avoids the aggressive 10-second expiration window for non-exact
RSTs while preserving the short timeout for RSTs that conntrack accepts
as closing the flow.
Suggested-by: Florian Westphal <fw@strlen.de>
Reported-by: Minghao Zhang <zhangmh25@mails.tsinghua.edu.cn>
Reported-by: Jianjun Chen <jianjun@tsinghua.edu.cn>
Signed-off-by: Minghao Zhang <zhangmh25@mails.tsinghua.edu.cn>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
LLM Generated explanations, may be completely bogus:
The evaluation is for the **6.6.y** tree this session opened on
(`pending-6.6` / 6.6.157). The bug is present there; the patch is a
small, real conntrack timeout fix.
---
## PHASE 1: COMMIT MESSAGE FORENSICS
**Step 1.1: PARSE THE SUBJECT LINE**
- Record: **[netfilter/conntrack/tcp] [use] use UNACK timeout for non-
closing RST packets**
- Subsystem: `netfilter: conntrack: tcp:`
- Verb: “use” here is a timeout-policy correction after RST validation
already keeps the entry ESTABLISHED.
**Step 1.2: PARSE ALL COMMIT MESSAGE TAGS**
- Record:
- **Suggested-by:** Florian Westphal `<fw@strlen.de>` (conntrack
maintainer)
- **Reported-by:** Minghao Zhang `<zhangmh25@mails.tsinghua.edu.cn>`
- **Reported-by:** Jianjun Chen `<jianjun@tsinghua.edu.cn>` (two
reporters)
- **Signed-off-by:** Minghao Zhang; Pablo Neira Ayuso (netfilter
maintainer)
- **No Fixes: / Cc: stable in the committed text** (expected for this
review queue)
- **v1 and v2 on the list both had** `Fixes: be0502a3f2e9 ("netfilter:
conntrack: tcp: only close if RST matches exact sequence")`. That
tag was dropped when the patch was folded into an nf-next pull.
**Step 1.3: ANALYZE THE COMMIT BODY TEXT**
- Record:
- **Bug:** After `be0502a3f2e9`, an in-window RST that does not match
`td_maxack` leaves conntrack in ESTABLISHED so the peer can
challenge-ACK. Timeout selection still keys off packet type
(`TCP_RST_SET`) and always applies **CLOSE (10s)**.
- **Symptom:** A second non-exact RST can land after the first
challenge ACK restored the timer, but while Linux still rate-limits
challenge ACKs. The second RST drops the timer to 10s again; the
suppressed challenge ACK never refreshes it; the conntrack entry
expires while both TCP endpoints stay established. NAT/firewall then
drop a live flow.
- **Root cause:** Timeout is chosen from the RST flag, not from the
resulting state.
- **Fix:** RST that leaves `new_state == TCP_CONNTRACK_ESTABLISHED`
uses **UNACK (5 min)**. Exact-match / RST-train RSTs still fall
through to `timeouts[new_state]` (CLOSE).
- **Version info:** Tied to `be0502a3f2e9` (2019-03-01), which is in
this 6.6 tree.
**Step 1.4: DETECT HIDDEN BUG FIXES**
- Record: **Not hidden.** It is a real timeout/state mismatch. v1/v2
called the 10s window **“exploitable”**. Pablo’s nf-next cover letter
softened that to “more conservative timeout”; the failure mode is
still live-session teardown through conntrack/NAT.
---
## PHASE 2: DIFF ANALYSIS
**Step 2.1: INVENTORY THE CHANGES**
- Record: **one file**, `net/netfilter/nf_conntrack_proto_tcp.c`; **~3
lines** in `nf_conntrack_tcp_packet()`. Surgical single-file logic
fix.
**Step 2.2: UNDERSTAND THE CODE FLOW CHANGE**
- Record: After window validation, timeout selection:
- **Before:** any `index == TCP_RST_SET` →
`timeouts[TCP_CONNTRACK_CLOSE]` (10s), even if `new_state` was
forced back to ESTABLISHED (`new_state = old_state` at the non-exact
RST path).
- **After:** only RST **and** `new_state == TCP_CONNTRACK_ESTABLISHED`
→ UNACK (5 min). Closing RSTs (`tcp_conntracks[][]` maps
ESTABLISHED+RST → CLOSE, exact-seq / RST-train `break` without
restoring ESTABLISHED) still get CLOSE.
**Step 2.3: IDENTIFY THE BUG MECHANISM**
- Record: **[Logic / correctness]** plus a **security-relevant NAT
teardown window**. Timeout no longer contradicts the RFC 5961 “stay
ESTABLISHED for challenge ACK” path from `be0502a3f2e9`.
**Step 2.4: ASSESS THE FIX QUALITY**
- Record: **Obviously correct** for defaults (ESTABLISHED=5 days,
UNACK=5 min, CLOSE=10s). Minimal. Regression risk is low:
- Dead flows after a bogus RST linger **5 min instead of 10s** (same
UNACK used for unacked data / out-of-window FIN|RST).
- If sysctl sets ESTABLISHED **below** UNACK (e.g. 120s NAT boxes),
this commit can **extend** the timer. Follow-up `73df81b38cb7f`
(`timeouts[new_state] > timeouts[TCP_CONNTRACK_UNACK]`) closes that.
Pablo: “Not a big deal… only affect timeout configurations where
unack timeout is longer than established.” Defaults on 6.6 are fine.
---
## PHASE 3: GIT HISTORY INVESTIGATION
**Step 3.1: BLAME THE CHANGED LINES**
- Record: The unconditional RST→CLOSE timeout was added in
**`be0502a3f2e9`** (Florian Westphal, 2019-03-01, v5.1 era). It is an
ancestor of `pending-6.6`. Long-lived 6.6 code, not a new-mainline-
only bug.
**Step 3.2: FOLLOW THE FIXES: TAG**
- Record: Committed message has **no Fixes:**. Submissions targeted
**`be0502a3f2e9`**, which **is** in this tree (`git merge-base --is-
ancestor be0502a3f2e9 pending-6.6` → YES). That commit documented
CLOSE timeout for non-exact RSTs as intentional; this patch revises
that because challenge-ACK rate limiting makes the 10s window drop
live flows.
**Step 3.3: CHECK FILE HISTORY**
- Record (on `pending-6.6`): related RST work includes `2bb6d82b586ea`
(invalid-seq RST direction check) and `628d694344a02` (UNACK for out-
of-window FIN/RST). **Standalone** timeout change; not patch 2/N.
Follow-up `73df81b38cb7f` is **not** in `pending-6.6` and should ride
along once this is applied (it has `Fixes: bf80e6802273`).
**Step 3.4: CHECK THE AUTHOR'S OTHER COMMITS**
- Record: Minghao Zhang / Jianjun Chen (Tsinghua) —
reporters/researchers, not netfilter maintainers. Weight comes from
**Suggested-by Florian** and **SOB Pablo**. No other Minghao commits
on this file in 6.6.
**Step 3.5: DEPENDENT/PREREQUISITE COMMITS**
- Record: Needs the `be0502` non-exact-RST ESTABLISHED path (present).
Uses existing `TCP_CONNTRACK_UNACK`. **Applies standalone** on 6.6.
Optional follow-up for inverted sysctl timeouts.
---
## PHASE 4: MAILING LIST AND EXTERNAL RESEARCH
**Step 4.1: FIND THE ORIGINAL PATCH DISCUSSION**
- Record:
- `b4 dig -c bf80e6802273a` → v2: https://patch.msgid.link/20260729154
654.3599616-1-zhangmh25@mails.tsinghua.edu.cn
- **v1** (2026-07-29): explicit CLOSE branch + UNACK fallback;
**Fixes:** + “exploitable 10-second expiration window”
- **v2:** Pablo/Florian simplification; UNACK only if `new_state ==
ESTABLISHED`
- Florian (https://patch.msgid.link/amoXj6UGRW8SQXBT@strlen.de): asked
to drop redundant CLOSE assignment and test a narrower condition.
Author tested ESTABLISHED-only UNACK and sent v2.
- **No NAK.** Queued in Pablo’s nf-next pull (patch 9/9), 2026-07-31:
https://lists.openwall.net/netdev/2026/07/31/313
- Jakub forwarded an AI review
(https://lists.openwall.net/netdev/2026/08/05/12): should have
Fixes:; CLOSE-for-inexact-RST was intentional in 2019; missing “only
lower, never extend” guard; closing-RST CLOSE timeout not preserved
for some non-default sysctls. The **extend** issue is exactly what
`73df81b38cb7f` later fixed.
**Step 4.2: CHECK WHO REVIEWED THE PATCH**
- Record (`b4 dig -w`): To/Cc **netfilter-devel**,
**pablo@netfilter.org**, **fw@strlen.de**, Jianjun Chen. Right
maintainers. Florian reviewed v1; Pablo merged.
**Step 4.3: SEARCH FOR THE BUG REPORT**
- Record: Dual Reported-by from Tsinghua. v1/v2 text: **exploitable**
10s window via RST + challenge-ACK rate limit. Concrete trigger, not
syzbot. Syzbot CI on the series: **passed**
(https://ci.syzbot.org/series/36445e46-2abc-4e64-93d2-bb558ec1f53b).
**Step 4.4: CHECK FOR RELATED PATCHES AND SERIES**
- Record: Standalone nf-next patch (not a 9-patch dependency; the other
8 were unrelated flowtable/conncount work). Follow-up: `73df81b38cb7f`
/ https://lists.openwall.net/netdev/2026/08/10/387 — “Reported by
sashiko”.
**Step 4.5: CHECK STABLE MAILING LIST HISTORY**
- Record: **No useful stable-list discussion found** for this subject
(lore.kernel.org/stable search did not surface this patch). Not
previously rejected for 6.6.
---
## PHASE 5: CODE SEMANTIC ANALYSIS
**Step 5.1: KEY FUNCTIONS**
- Record: **`nf_conntrack_tcp_packet()`** only.
**Step 5.2: TRACE CALLERS**
- Record: `pending-6.6:net/netfilter/nf_conntrack_core.c:1897` — every
tracked TCP packet. Softirq/hook path, not init-only.
**Step 5.3: TRACE CALLEES**
- Record: `nf_ct_timeout_lookup()`, `tn->timeouts[]`, later
`nf_ct_refresh_acct()`. Writes entry expiry.
**Step 5.4: FOLLOW THE CALL CHAIN**
- Record: Userspace/network reachable whenever `CONFIG_NF_CONNTRACK`
tracks TCP (iptables/nftables/NAT, Docker, Kubernetes, firewalls).
**High impact.** An on-path or in-window RST injector can hit it.
Unprivileged remote: needs in-window (not exact) SEQ; that is the RFC
5961 threat model.
**Step 5.5: SEARCH FOR SIMILAR PATTERNS**
- Record: Sibling branches already only **lower** timeout
(RETRANS/UNACK/zero-window). This RST branch was the odd one-out.
`nf_tcp_handle_invalid()` also uses UNACK for out-of-window FIN/RST
(`628d694`).
---
## PHASE 6: CROSS-REFERENCING AGAINST THE LOCAL TREE
**Step 6.1: DOES THE BUGGY CODE EXIST IN THIS TREE?**
- Record: **Yes on `pending-6.6` / 6.6.157:**
```
else if (unlikely(index == TCP_RST_SET))
timeout = timeouts[TCP_CONNTRACK_CLOSE];
```
at `nf_conntrack_proto_tcp.c` ~1327–1328. Non-exact RST still does
`new_state = old_state` (~1260). `be0502a3f2e9` is an ancestor.
Candidate **`bf80e6802273a` is not** an ancestor of `pending-6.6`.
Introduced with `be0502` (2019), present for the whole 6.6 lifetime.
*(HEAD later detached at `v7.3-rc4-70` during this session; that
mainline tree already contains the fix. The stable tree this review is
for remains 6.6.157 / `pending-6.6`, where the bug is still open.)*
**Step 6.2: CHECK FOR BACKPORT COMPLICATIONS**
- Record: **Clean apply.** Exact before-image on `pending-6.6`. No
refactor of this hunk vs the candidate.
**Step 6.3: CHECK IF RELATED FIXES ARE ALREADY HERE**
- Record: `2bb6d82b586ea` is a **different** RST CLOSE issue (SYN/RST
direction). It does **not** change RST timeout selection. No earlier
UNACK-for-non-closing-RST fix in 6.6.
---
## PHASE 7: SUBSYSTEM AND MAINTAINER CONTEXT
**Step 7.1: SUBSYSTEM AND CRITICALITY**
- Record: **netfilter conntrack TCP** — **IMPORTANT**. Core firewall/NAT
path on virtually every router, distro firewall, and container host.
**Step 7.2: SUBSYSTEM ACTIVITY**
- Record: Mature tracker; this file still gets targeted RST/timeout
fixes. Bug is old (`be0502`), not churn from a new subsystem.
---
## PHASE 8: IMPACT AND RISK ASSESSMENT
**Step 8.1: WHO IS AFFECTED**
- Record: **Conntrack/NAT users** (`CONFIG_NF_CONNTRACK`) — default on
most distro kernels. Not arch-specific.
**Step 8.2: TRIGGER CONDITIONS**
- Record: In-window, non-exact RST on an ESTABLISHED tracked flow. 6.6
still **per-socket** rate-limits challenge ACKs via
`__tcp_oow_rate_limited()` and `sysctl_tcp_invalid_ratelimit = HZ/2`
(500ms), even with global `tcp_challenge_ack_limit = INT_MAX`. A
second RST inside that window matches the commit’s scenario. Also:
single RST + lost/unseen challenge ACK (asymmetric routing) leaves a
10s expiry. **On-path attacker or sequence-in-window injection.**
**Step 8.3: FAILURE MODE SEVERITY**
- Record: **Live TCP dropped through NAT/firewall** after ~10s;
endpoints stay ESTABLISHED. Not a panic. **HIGH** (session disruption
/ DoS on middleboxes). Authors called it **exploitable**.
**Step 8.4: RISK-BENEFIT RATIO**
- Record: **Benefit high** (stops silent NAT teardown of established
sessions). **Risk low** (3-line, default UNACK < ESTABLISHED).
Residual risk: short ESTABLISHED sysctl vs longer UNACK — mitigated by
a one-line follow-up, not a reason to skip this fix.
---
## PHASE 9: FINAL SYNTHESIS
**Step 9.1: EVIDENCE**
**FOR:**
- Real, user-visible bug: conntrack expires while TCP stays up.
- Security-relevant (in-window RST + challenge-ACK throttle); v1/v2 said
“exploitable”; had `Fixes: be0502a3f2e9`.
- `be0502` and the buggy CLOSE timeout are in 6.6.
- Challenge-ACK **per-socket** throttle exists in 6.6 (`HZ/2`).
- Tiny, obviously correct for default timeouts; Florian suggested, Pablo
merged, author tested v2.
- Applies cleanly to `pending-6.6`.
**AGAINST:**
- Merged via **nf-next**, not nf; committed text dropped Fixes: and
“exploitable”.
- `be0502` originally **meant** CLOSE timeout for non-exact RSTs (policy
shift 10s → 5 min).
- Follow-up needed for ESTABLISHED < UNACK sysctl.
- Not a crash/corruption/deadlock.
**UNRESOLVED:** Full lore.kernel.org HTML was 403 (used `b4` + openwall
+ local mbox instead). No CVE number found.
**Step 9.2: STABLE RULES CHECKLIST**
1. Obviously correct and tested? **PASS** — v2 tested by author;
Florian/Pablo; syzbot CI passed.
2. Fixes a real bug that affects users? **PASS** — NAT/firewall drop of
live TCP.
3. Important (crash/security/corruption/deadlock)? **PASS** —
exploitable middlebox teardown / DoS, not a panic.
4. Small and contained? **PASS** — ~3 lines, one function.
5. No new features or APIs? **PASS** — existing UNACK timeout.
6. Can apply to this tree? **PASS** — exact match on `pending-6.6`.
**Step 9.3: EXCEPTION CATEGORIES**
- Record: None (not a device ID/quirk/DT/build/doc exception). Ordinary
netfilter bugfix.
**Step 9.4: DECISION**
Backport. 6.6 has the 2019 RST-validation change **and** the 10s CLOSE
timeout that undermines it. The 5-minute UNACK value is the same
compromise already used for other “conntrack and endpoints disagree”
cases. Take follow-up `73df81b38cb7f` with it when possible; this commit
is still right on default 6.6 timeouts without it.
---
## What the commit solves for stable users
On 6.6 firewalls/NAT, an in-window RST that is **not** the exact next
SEQ leaves conntrack ESTABLISHED (RFC 5961) but still arms a **10s**
timer. Linux then rate-limits challenge ACKs (~500ms per socket). A
second such RST re-arms 10s with no refresh ACK, so conntrack deletes a
flow that both endpoints still consider established. That is a real
production failure and a plausible DoS on NAT.
## Stable rules / risk vs benefit
Meets the rules: small, tested, no new API, applies cleanly, fixes an
important conntrack bug. Benefit is high for a common config; regression
risk is a slightly longer hold of *stale* entries (5 min vs 10s), which
is acceptable.
## Concerns
- Pair with `73df81b38cb7f` so sysctl ESTABLISHED < UNACK cannot extend
lifetime.
- Behavior change vs 2019 “intentional CLOSE timeout” is the point of
the fix, not a blocker.
---
## Verification
- **[Phase 1]** Tags: Suggested-by Florian, two Reported-by, Pablo SOB;
committed text has no Fixes:/Cc:stable; v1/v2 mbox **did** include
`Fixes: be0502a3f2e9` and “exploitable”.
- **[Phase 2]** Diff: 3-line change in `nf_conntrack_tcp_packet()` RST
timeout arm; CLOSE preserved when `new_state != ESTABLISHED`.
- **[Phase 3]** `be0502a3f2e9` is ancestor of `pending-6.6`; candidate
is not. Blame: CLOSE-on-every-RST from `be0502`. Follow-up
`73df81b38cb7f` not in 6.6.
- **[Phase 3]** `git show
pending-6.6:net/netfilter/nf_conntrack_proto_tcp.c` lines 1327–1328:
buggy `index == TCP_RST_SET` → CLOSE.
- **[Phase 4]** `b4 dig -c bf80e6802273a`: v1+v2; Florian review
`amoXj6UGRW8SQXBT@strlen.de`; Pablo nf-next 9/9; Jakub AI review
2026-08-05; follow-up 2026-08-10 “Reported by sashiko”.
- **[Phase 4]** `b4 dig -w`: netfilter-devel, pablo, fw. `b4 dig -a`:
v1→v2 only.
- **[Phase 4]** lore.kernel.org WebFetch: **403**; used b4 mbox +
openwall. Stable list: no hit on this subject.
- **[Phase 5]** Caller: `nf_conntrack_core.c:1897` on every tracked TCP
packet.
- **[Phase 6]** First probe: `pending-6.6`, `make kernelversion`
6.6.157. Later HEAD detached at `v7.3-rc4-70` (mainline already has
the fix). Re-checked **`pending-6.6`**: VERSION 6.6.157, buggy timeout
present, exact before-image, candidate not ancestor.
- **[Phase 6]** 6.6 challenge ACK: `__tcp_oow_rate_limited` +
`sysctl_tcp_invalid_ratelimit = HZ/2`; global limit INT_MAX does
**not** disable per-socket limit.
- **[Phase 6]** Defaults on 6.6: ESTABLISHED 5 days, UNACK 5 min, CLOSE
10s.
- **[Phase 8]** Failure: NAT/conntrack drop of live TCP; severity HIGH /
security-relevant.
- **UNVERIFIED:** CVE id (none found). Could not fetch lore HTML
directly.
**YES**
net/netfilter/nf_conntrack_proto_tcp.c | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/net/netfilter/nf_conntrack_proto_tcp.c b/net/netfilter/nf_conntrack_proto_tcp.c
index 91da35e2b0d8d..456721e40ed0c 100644
--- a/net/netfilter/nf_conntrack_proto_tcp.c
+++ b/net/netfilter/nf_conntrack_proto_tcp.c
@@ -1325,8 +1325,9 @@ int nf_conntrack_tcp_packet(struct nf_conn *ct,
if (ct->proto.tcp.retrans >= tn->tcp_max_retrans &&
timeouts[new_state] > timeouts[TCP_CONNTRACK_RETRANS])
timeout = timeouts[TCP_CONNTRACK_RETRANS];
- else if (unlikely(index == TCP_RST_SET))
- timeout = timeouts[TCP_CONNTRACK_CLOSE];
+ else if (unlikely(index == TCP_RST_SET &&
+ new_state == TCP_CONNTRACK_ESTABLISHED))
+ timeout = timeouts[TCP_CONNTRACK_UNACK];
else if ((ct->proto.tcp.seen[0].flags | ct->proto.tcp.seen[1].flags) &
IP_CT_TCP_FLAG_DATA_UNACKNOWLEDGED &&
timeouts[new_state] > timeouts[TCP_CONNTRACK_UNACK])
--
2.53.0
^ permalink raw reply related [flat|nested] 32+ messages in thread* [PATCH AUTOSEL 6.18-5.10] net: usb: qmi_wwan: add Rolling Wireless RN947R
2026-10-19 13:44 [PATCH AUTOSEL 6.18-6.12] net: dsa: mt7530: check CORE_PLL_GROUP4 access in mt7531_setup() Sasha Levin
` (3 preceding siblings ...)
2026-10-19 13:46 ` [PATCH AUTOSEL 6.18-5.10] netfilter: conntrack: tcp: use UNACK timeout for non-closing RST packets Sasha Levin
@ 2026-10-19 13:46 ` Sasha Levin
2026-10-19 13:46 ` [PATCH AUTOSEL 6.18-5.10] net: usb: rtl8150: handle link status read failures Sasha Levin
` (24 subsequent siblings)
29 siblings, 0 replies; 32+ messages in thread
From: Sasha Levin @ 2026-10-19 13:46 UTC (permalink / raw)
To: patches, stable
Cc: Reinhard Speyerer, Jakub Kicinski, Sasha Levin, bjorn,
andrew+netdev, davem, edumazet, pabeni, netdev, linux-usb,
linux-kernel
From: Reinhard Speyerer <rspmn@arcor.de>
[ Upstream commit 7375d38364a9aa66fb31716bcefef38aecad75d8 ]
Add Rolling Wireless RN947R 0x9300 composition:
DIAG + ADB + NMEA + MODEM + RMNET + QDSS + ADPL
T: Bus=01 Lev=01 Prnt=01 Port=02 Cnt=02 Dev#= 6 Spd=480 MxCh= 0
D: Ver= 2.10 Cls=ef(misc ) Sub=02 Prot=01 MxPS=64 #Cfgs= 1
P: Vendor=33f8 ProdID=9300 Rev= 0.00
S: Manufacturer=Rolling Wireless
S: Product=RN947R
S: SerialNumber=xxxxxxxxxxx
C:* #Ifs= 7 Cfg#= 1 Atr=a0 MxPwr=500mA
I:* If#= 0 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=ff Prot=30 Driver=option
E: Ad=01(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms
E: Ad=81(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms
I:* If#= 1 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=42 Prot=01 Driver=usbfs
E: Ad=02(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms
E: Ad=82(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms
I:* If#= 2 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=ff Prot=40 Driver=option
E: Ad=84(I) Atr=03(Int.) MxPS= 10 Ivl=32ms
E: Ad=83(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms
E: Ad=03(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms
I:* If#= 3 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=ff Prot=40 Driver=option
E: Ad=86(I) Atr=03(Int.) MxPS= 10 Ivl=32ms
E: Ad=85(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms
E: Ad=04(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms
I:* If#= 8 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=ff Prot=50 Driver=qmi_wwan
E: Ad=88(I) Atr=03(Int.) MxPS= 8 Ivl=32ms
E: Ad=87(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms
E: Ad=05(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms
I:* If#=21 Alt= 0 #EPs= 1 Cls=ff(vend.) Sub=ff Prot=70 Driver=(none)
E: Ad=89(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms
I:* If#=22 Alt= 0 #EPs= 1 Cls=ff(vend.) Sub=ff Prot=80 Driver=(none)
E: Ad=8a(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms
Signed-off-by: Reinhard Speyerer <rspmn@arcor.de>
Link: https://patch.msgid.link/arZzuOOvLUZtdOlv@arcor.de
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
LLM Generated explanations, may be completely bogus:
This is a one-line USB ID addition to the existing `qmi_wwan` driver for
Rolling Wireless RN947R (`33f8:9300`). That is a standard stable
exception, and the ID table plus DTR quirk already exist in this 6.18.y
tree.
===============================================================
PHASE 1: COMMIT MESSAGE FORENSICS
===============================================================
Step 1.1: PARSE THE SUBJECT LINE
Record: [net: usb: qmi_wwan] [add] [USB device ID for Rolling Wireless
RN947R]
Step 1.2: PARSE ALL COMMIT MESSAGE TAGS
Record:
- Link: https://patch.msgid.link/arZzuOOvLUZtdOlv@arcor.de
- Signed-off-by: Reinhard Speyerer <rspmn@arcor.de> (author)
- Signed-off-by: Jakub Kicinski <kuba@kernel.org> (netdev maintainer,
applied)
- No Fixes: tag (expected)
- No Reported-by / Tested-by / Reviewed-by / Acked-by
- No Cc: stable (expected)
- Ignored pipeline SOB / Upstream commit marker
Step 1.3: ANALYZE THE COMMIT BODY TEXT
Record:
- Bug description: RN947R USB WWAN modem (`Vendor=33f8 ProdID=9300`) is
not in `qmi_wwan`’s ID table, so the RMNET/QMI network function does
not bind.
- Symptom: no `qmi_wwan` netdev for this modem; cellular data does not
come up out of the box.
- Version information: none stated.
- Root cause: missing USB ID / interface mapping. `lsusb -t`-style dump
shows If#=8 Cls=ff Sub=ff Prot=50 as the QMI/RMNET function.
Composition is DIAG + ADB + NMEA + MODEM + RMNET + QDSS + ADPL.
Step 1.4: DETECT HIDDEN BUG FIXES
Record: Not a hidden crash/leak fix. It is an explicit device-ID
enablement, which stable rules treat as a first-class exception.
===============================================================
PHASE 2: DIFF ANALYSIS - LINE BY LINE
===============================================================
Step 2.1: INVENTORY THE CHANGES
Record:
- File: `drivers/net/usb/qmi_wwan.c` (+1 / -0)
- Function/table: `products[]` (`static const struct usb_device_id`)
- Scope: single-file, one-line surgical ID addition
Step 2.2: UNDERSTAND THE CODE FLOW CHANGE
Record:
- Before: `33f8:9300` is unmatched; USB core does not probe `qmi_wwan`
for interface 8.
- After: `QMI_QUIRK_SET_DTR(0x33f8, 0x9300, 8)` matches
vendor/product/interface 8 and sets `qmi_wwan_info_quirk_dtr`.
- Path: USB enumeration / driver probe only. No error-path or hot-path
logic change.
Step 2.3: IDENTIFY THE BUG MECHANISM
Record:
- Category: hardware enablement / USB device ID (stable exception 1).
- Mechanism: `QMI_QUIRK_SET_DTR` expands to
`USB_DEVICE_INTERFACE_NUMBER` plus DTR quirk `driver_info`. Interface
8 matches the dump (`Prot=50`, QMI/RMNET). DTR quirk is the same
pattern as Rolling RW101 (`33f8:0104`) and other Qualcomm USB WWAN
entries.
Step 2.4: ASSESS THE FIX QUALITY
Record:
- Obviously correct: VID/PID/interface taken from a real device dump;
match is interface-number-specific so DIAG/ADB/NMEA/MODEM/QDSS/ADPL
are not claimed.
- Minimal: one table entry, no API or structure change.
- Regression risk: very low; only `33f8:9300` is affected. Wrong
interface number would be the main risk, and the dump confirms If#=8.
===============================================================
PHASE 3: GIT HISTORY INVESTIGATION
===============================================================
Step 3.1: BLAME THE CHANGED LINES
Record:
- Insertion sits next to Rolling RW101: `d362046021ea12`
(“net:usb:qmi_wwan: support Rolling modules”, Vanillan Wang,
2024-04-16).
- That RW101 ID is in this tree’s v6.18.57 base (`git merge-base --is-
ancestor d362046021ea1 v6.18.57` succeeded).
- This is extending an existing vendor block, not introducing a new
driver.
Step 3.2: FOLLOW THE FIXES: TAG
Record: No Fixes: tag. Not applicable. This does not revert or repair a
prior logic bug.
Step 3.3: CHECK FILE HISTORY FOR RELATED CHANGES
Record:
- Recent `qmi_wwan` history is dominated by the same class of ID
additions (Quectel, Telit, MeiG, Fibocom, SIMCom).
- No prerequisite refactor is required; `products[]` and
`QMI_QUIRK_SET_DTR` are long-standing.
- Standalone: subject is not “patch X/Y”. `b4 dig -a` shows a single v1
patch.
Step 3.4: CHECK THE AUTHOR'S OTHER COMMITS
Record:
- Reinhard Speyerer has prior `qmi_wwan` ID/work in this history (e.g.
Fibocom FG132 `64761c980cbf7`).
- Not the listed maintainer; maintainer is Bjørn Mork (`MAINTAINERS`:
USB QMI WWAN NETWORK DRIVER). The patch was mailed to `bjorn@mork.no`.
Step 3.5: CHECK FOR DEPENDENT/PREREQUISITE COMMITS
Record:
- Depends only on `QMI_QUIRK_SET_DTR` / `USB_DEVICE_INTERFACE_NUMBER`,
both present.
- No companion `option.c` change is in this series. Serial AT/NMEA/DIAG
auto-bind is separate; this patch alone still enables the WWAN netdev.
Can apply standalone.
===============================================================
PHASE 4: MAILING LIST AND EXTERNAL RESEARCH
===============================================================
Step 4.1: FIND THE ORIGINAL PATCH DISCUSSION
Record:
- `b4 dig -c 7375d38364a9aa66fb31716bcefef38aecad75d8` matched
https://patch.msgid.link/arZzuOOvLUZtdOlv@arcor.de
- `b4 dig -a`: only v1, 2026-09-25, `[PATCH net] net: usb: qmi_wwan: add
Rolling Wireless RN947R`
- Thread (saved via `b4 dig -m`): submission, then patchwork-bot
“applied to netdev/net.git (main) by Jakub Kicinski”. No NAK, no
review objections, no later revision.
- No reviewer Cc: stable in the thread.
Step 4.2: CHECK WHO REVIEWED THE PATCH
Record (`b4 dig -w`): To `bjorn@mork.no`, Cc `netdev@vger.kernel.org`.
Subsystem maintainer was on To. Applied by netdev maintainer. No
explicit Reviewed-by in the committed message.
Step 4.3: SEARCH FOR THE BUG REPORT
Record: No syzbot/bugzilla. Evidence of a real device is the author’s
usb-devices dump (Manufacturer=Rolling Wireless, Product=RN947R,
33f8:9300). User population is owners of that modem.
Step 4.4: CHECK FOR RELATED PATCHES AND SERIES
Record: Single-patch series. No `option.c` RN947R commit in this tree
(`git log --grep=RN947R -- drivers/` only finds this qmi_wwan change).
Web search did not find a matching option serial patch.
Step 4.5: CHECK STABLE MAILING LIST HISTORY
Record: UNVERIFIED. `WebFetch` of lore.kernel.org/stable/?q=RN947R
returned 403. No local evidence this ID was rejected for stable.
===============================================================
PHASE 5: CODE SEMANTIC ANALYSIS
===============================================================
Step 5.1: IDENTIFY KEY FUNCTIONS IN THE DIFF
Record: No function body edited. Data change in `products[]`, consumed
as `qmi_wwan_driver.id_table`.
Step 5.2: TRACE CALLERS
Record:
- `products` -> `qmi_wwan_driver.id_table` -> USB core match ->
`qmi_wwan_probe()` -> `usbnet` bind path (`qmi_wwan_bind`).
- Reachable on device plug-in / boot with the modem attached. Not
interrupt or syscall hot path for other devices.
Step 5.3: TRACE CALLEES
Record: Unchanged. Bind still uses existing `qmi_wwan_bind` / DTR
handling (`qmi_wwan_change_dtr` when `QMI_WWAN_QUIRK_DTR` or `bcdUSB >=
0x0201`). Dump has `Ver= 2.10`, so DTR would be set even with
`QMI_FIXED_INTF`; `QMI_QUIRK_SET_DTR` matches sibling Rolling entries.
Step 5.4: FOLLOW THE CALL CHAIN
Record: Userspace-visible: plugging the modem should create a QMI WWAN
netdev and cdc-wdm control node. Trigger is ordinary USB enumeration,
not an obscure debug path.
Step 5.5: SEARCH FOR SIMILAR PATTERNS
Record: Same `QMI_QUIRK_SET_DTR(vend, prod, intf)` pattern is used
throughout `products[]`, including `33f8:0104` RW101. This is the
established way to add Qualcomm RMNET compositions.
===============================================================
PHASE 6: CROSS-REFERENCING AGAINST THE LOCAL TREE
===============================================================
Step 6.1: DOES THE BUGGY CODE EXIST IN THIS TREE?
Record:
- Local tree: `git describe HEAD` / `make kernelversion` => **6.18.57**
(`VERSION=6 PATCHLEVEL=18 SUBLEVEL=57`).
- `drivers/net/usb/qmi_wwan.c` exists; `CONFIG_USB_NET_QMI_WWAN` exists;
`QMI_QUIRK_SET_DTR` exists.
- `git show v6.18.57:drivers/net/usb/qmi_wwan.c` has Rolling RW101
(`33f8:0104`) and **does not** have `33f8:9300` / RN947R. The missing-
ID problem exists on this 6.18.y base.
Step 6.2: CHECK FOR BACKPORT COMPLICATIONS
Record: Clean apply. v6.18.57 context is identical around the insertion
point:
```
{QMI_QUIRK_SET_DTR(0x33f8, 0x0104, 4)}, /* Rolling RW101 RMNET */
{QMI_FIXED_INTF(0x2dee, 0x4d22, 5)}, /* MeiG Smart SRM825L */
```
No refactor between mainline hunk and this tree.
Step 6.3: CHECK IF RELATED FIXES ARE ALREADY HERE
Record: No other RN947R ID in this tree’s `option.c` or `qmi_wwan.c` on
the v6.18.57 base. No alternate fix for 33f8:9300.
===============================================================
PHASE 7: SUBSYSTEM AND MAINTAINER CONTEXT
===============================================================
Step 7.1: IDENTIFY THE SUBSYSTEM AND ITS CRITICALITY
Record: [drivers/net/usb/qmi_wwan] [IMPORTANT for WWAN users, peripheral
for everyone else]. Mature, non-staging driver used by laptops and
embedded LTE/5G modems.
Step 7.2: ASSESS SUBSYSTEM ACTIVITY
Record: Actively maintained via ID-table additions. Maintainer Bjørn
Mork; netdev applies these routinely. Mature ID-table design.
===============================================================
PHASE 8: IMPACT AND RISK ASSESSMENT
===============================================================
Step 8.1: DETERMINE WHO IS AFFECTED
Record: Driver-specific / hardware-specific. Only Rolling Wireless
RN947R (`33f8:9300`). Other USB devices unchanged.
Step 8.2: DETERMINE THE TRIGGER CONDITIONS
Record: Plug in the modem or boot with it attached. Common for those
users. Unprivileged users do not get a new attack surface; this is bind-
table matching.
Step 8.3: DETERMINE THE FAILURE MODE SEVERITY
Record: Without the ID, the RMNET function stays unbound; WWAN
networking does not work. Not a panic/UAF. Severity MEDIUM as a
functional hardware-support miss, and it still qualifies under the
device-ID exception.
Step 8.4: CALCULATE RISK-BENEFIT RATIO
Record:
- Benefit: HIGH for RN947R users (modem network interface works); zero
effect on others.
- Risk: VERY LOW (1 line, existing macro, existing driver,
VID:PID:interface match).
- Ratio: strongly favors backport.
===============================================================
PHASE 9: FINAL SYNTHESIS
===============================================================
Step 9.1: COMPILE THE EVIDENCE
FOR:
- Explicit stable exception: USB ID added to an existing driver.
- One line, obviously correct, matches a real device dump (If#=8,
Prot=50).
- Driver, macros, and Rolling vendor support already in 6.18.57.
- Applies cleanly next to RW101.
- Mailed to qmi_wwan maintainer, applied by netdev maintainer, v1 with
no NAK.
- Same pattern as many other qmi_wwan ID additions.
AGAINST:
- Does not fix a crash, UAF, or corruption.
- No Tested-by/Reviewed-by tags.
- `option.c` still lacks `33f8:9300`, so serial ports may need a
separate ID or `new_id`; that does not block the netdev ID.
UNRESOLVED:
- lore.kernel.org WebFetch 403 for stable@ search.
- Whether an option serial companion exists upstream later than this
tree.
Step 9.2: APPLY THE STABLE RULES CHECKLIST
1. Obviously correct and tested? PASS — dump-backed ID, applied to
netdev/net.
2. Fixes a real bug that affects users? PASS — modem otherwise has no
QMI netdev.
3. Important issue? PASS under device-ID exception (hardware enablement
for existing driver).
4. Small and contained? PASS — +1 line in one table.
5. No new features or APIs? PASS — no new driver, syscall, or userspace
API; ID only.
6. Can apply to the local tree? PASS — v6.18.57 has identical context.
Step 9.3: CHECK FOR EXCEPTION CATEGORIES
Record: Device ID addition to existing driver (USB ID in `qmi_wwan`).
Step 9.4: MAKE YOUR DECISION
This should be backported to this 6.18.y tree. It is the standard, low-
risk USB ID pattern stable already takes for `qmi_wwan`.
What problem it solves: RN947R owners on 6.18.y get a working QMI/RMNET
interface instead of an unbound USB function.
Risk vs benefit: one matching table entry vs. a non-working WWAN modem.
Benefit dominates.
Concerns: serial `option` binding is incomplete without a separate ID;
that is independent and not a reason to drop this netdev ID.
Verification:
- [Phase 1] Parsed subject/tags/body: device-ID add; Link to
patch.msgid.link; SOB Speyerer + Kicinski; no Fixes/syzbot.
- [Phase 2] Diff: +1 line in `products[]`; `QMI_QUIRK_SET_DTR(0x33f8,
0x9300, 8)` matches dump If#=8.
- [Phase 2] Read `QMI_QUIRK_SET_DTR` / `qmi_wwan_info_quirk_dtr` / DTR
logic at bind (lines 804–825, 938–958).
- [Phase 3] `git blame` around insertion: RW101 from `d362046021ea12`
(2024); ancestor of v6.18.57.
- [Phase 3] `git log --oneline -20 -- drivers/net/usb/qmi_wwan.c`: many
similar ID adds; this is standalone.
- [Phase 3] `git log --author='Reinhard Speyerer' -- drivers/net/usb/`:
prior qmi_wwan ID work.
- [Phase 4] `b4 dig -c 7375d38364a9…`: lore URL found; `-a` v1 only;
`-w` To bjorn@mork.no Cc netdev; `-m` thread is submit + applied-bot,
no NAK.
- [Phase 4] lore WebFetch 403; used b4 mbox instead.
- [Phase 4] WebSearch: no option.c RN947R companion found.
- [Phase 5] `products` -> `qmi_wwan_driver.id_table` ->
`qmi_wwan_probe`; `MODULE_DEVICE_TABLE(usb, products)`.
- [Phase 5] `option.c` has other 33f8 IDs, not 0x9300.
- [Phase 6] `git describe HEAD` / Makefile: 6.18.57. `git show
v6.18.57:drivers/net/usb/qmi_wwan.c` has RW101, lacks RN947R. Apply
context matches.
- [Phase 6] `git merge-base --is-ancestor 7375d38364a9 HEAD` is false
(upstream SHA not this history); evaluate against local 6.18.y code,
not that SHA.
- [Phase 7] MAINTAINERS: USB QMI WWAN NETWORK DRIVER, M: Bjørn Mork.
- [Phase 8] Failure mode: unbound RMNET on this VID:PID only; risk
confined to that device.
- UNVERIFIED: stable@ lore discussion (HTTP 403).
**YES**
drivers/net/usb/qmi_wwan.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/drivers/net/usb/qmi_wwan.c b/drivers/net/usb/qmi_wwan.c
index aa66a30e56cdc..85d2daae805fd 100644
--- a/drivers/net/usb/qmi_wwan.c
+++ b/drivers/net/usb/qmi_wwan.c
@@ -1453,6 +1453,7 @@ static const struct usb_device_id products[] = {
{QMI_QUIRK_SET_DTR(0x1546, 0x1312, 4)}, /* u-blox LARA-R6 01B */
{QMI_QUIRK_SET_DTR(0x1546, 0x1342, 4)}, /* u-blox LARA-L6 */
{QMI_QUIRK_SET_DTR(0x33f8, 0x0104, 4)}, /* Rolling RW101 RMNET */
+ {QMI_QUIRK_SET_DTR(0x33f8, 0x9300, 8)}, /* Rolling RN947R RMNET */
{QMI_FIXED_INTF(0x2dee, 0x4d22, 5)}, /* MeiG Smart SRM825L */
{QMI_QUIRK_SET_DTR(0x2dee, 0x4d63, 0)}, /* MeiG SRM813Q w/ Modem(PPP) */
{QMI_QUIRK_SET_DTR(0x2dee, 0x4d64, 0)}, /* MeiG SRM813Q w/ NMEA */
--
2.53.0
^ permalink raw reply related [flat|nested] 32+ messages in thread* [PATCH AUTOSEL 6.18-5.10] net: usb: rtl8150: handle link status read failures
2026-10-19 13:44 [PATCH AUTOSEL 6.18-6.12] net: dsa: mt7530: check CORE_PLL_GROUP4 access in mt7531_setup() Sasha Levin
` (4 preceding siblings ...)
2026-10-19 13:46 ` [PATCH AUTOSEL 6.18-5.10] net: usb: qmi_wwan: add Rolling Wireless RN947R Sasha Levin
@ 2026-10-19 13:46 ` Sasha Levin
2026-10-19 13:47 ` [PATCH AUTOSEL 6.18-6.1] net: prestera: ignore duplicate RIF destruction events Sasha Levin
` (23 subsequent siblings)
29 siblings, 0 replies; 32+ messages in thread
From: Sasha Levin @ 2026-10-19 13:46 UTC (permalink / raw)
To: patches, stable
Cc: Yousef Alhouseen, syzbot+9db6c624635564ad813c, Petko Manolov,
Andrew Lunn, Paolo Abeni, Sasha Levin, andrew+netdev, davem,
edumazet, kuba, linux-usb, netdev, linux-kernel
From: Yousef Alhouseen <alhouseenyousef@gmail.com>
[ Upstream commit 8c9c5b9a689612dcb92a04a4218c975cd19f19d8 ]
set_carrier() ignores the result of the USB control transfer and tests
the stack variable supplied as its receive buffer. If the device rejects
or aborts the request, that variable remains uninitialized and the driver
chooses an arbitrary carrier state.
Leave the existing carrier state unchanged when the link status cannot be
read. A transient USB error should not be treated as link loss.
Reported-by: syzbot+9db6c624635564ad813c@syzkaller.appspotmail.com
Closes: https://syzkaller.appspot.com/bug?extid=9db6c624635564ad813c
Suggested-by: Petko Manolov <petkan@nucleusys.com>
Signed-off-by: Yousef Alhouseen <alhouseenyousef@gmail.com>
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
Link: https://patch.msgid.link/20260630101216.10365-1-alhouseenyousef@gmail.com
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
LLM Generated explanations, may be completely bogus:
## Phase 1: Commit message forensics
**Step 1.1 — Subject**
Record: `[net: usb: rtl8150] [handle] ignore USB register-read errors in
set_carrier() so an uninitialized stack value is not used as link
status`
**Step 1.2 — Tags**
Record:
- `Reported-by: syzbot+9db6c624635564ad813c@syzkaller.appspotmail.com`
(KMSAN, reproducible)
- `Closes: https://syzkaller.appspot.com/bug?extid=9db6c624635564ad813c`
- `Suggested-by: Petko Manolov <petkan@nucleusys.com>` (original rtl8150
author)
- `Reviewed-by: Andrew Lunn <andrew@lunn.ch>`
- `Link: https://patch.msgid.link/20260630101216.10365-1-
alhouseenyousef@gmail.com`
- `Signed-off-by: Yousef Alhouseen` (author), `Paolo Abeni` (netdev)
- No `Fixes:` on the committed form (dropped in v4 at reviewer request;
v1/v2 had `Fixes: 1da177e4c3f4` and `Cc: stable`)
- Sasha Levin SOB ignored as pipeline noise
**Step 1.3 — Body**
Record: `set_carrier()` ignores `get_registers()` and then tests stack
`tmp`. On USB reject/abort, `tmp` is uninitialized, so carrier is chosen
from garbage. Failure mode: arbitrary `netif_carrier_on/off`. Fix: keep
the current carrier if the CSCR read fails. No version range given.
**Step 1.4 — Hidden bug fix?**
Record: Not hidden. This is an explicit uninitialized-use / ignored-
error fix, confirmed by KMSAN.
## Phase 2: Diff analysis
**Step 2.1 — Inventory**
Record: one file, `drivers/net/usb/rtl8150.c`; `set_carrier()` only; +3
/ −1; surgical single-function change.
**Step 2.2 — Flow**
Record: Before: always `get_registers(dev, CSCR, 2, &tmp)` then `tmp &
CSCR_LINK_STATUS` → carrier on/off. After: non-zero return from
`get_registers()` returns immediately; carrier is unchanged. Path:
`rtl8150_open()` (interface up).
**Step 2.3 — Bug mechanism**
Record: **uninitialized data** (KMSAN). `usb_control_msg_recv()` only
`memcpy`s into the caller buffer on a full successful transfer
(`drivers/usb/core/message.c`); on error it returns negative and leaves
`tmp` untouched. `short tmp` is uninitialized, then used as a bit test.
**Step 2.4 — Fix quality**
Record: Obviously correct. Three lines. No API/lock/refcount change.
Regression risk is low: a failed CSCR read no longer flips carrier. That
matches the original author’s review (transient USB errors should not
look like link loss).
## Phase 3: Git history
**Step 3.1 — Blame**
Record: Unchecked `get_registers()` + `tmp & CSCR_LINK_STATUS` dates to
`1da177e4c3f4` (Linux 2.6.12-rc2, 2005). The uninit became hard after
`b2a0f274e3f7e` (“Use the new usb control message API”, in `v5.10-rc1`):
`usb_control_msg_recv()` does not fill the buffer on failure.
**Step 3.2 — Fixes tag**
Record: Committed patch has no `Fixes:`. v1/v2 used `Fixes:
1da177e4c3f4`. That SHA is in this tree. The USB API switch
`b2a0f274e3f7e` is also in this tree (`v5.10-rc1`).
**Step 3.3 — Related file history**
Record: Same driver already has `fba383985354e` (“fix unintiatilzed
variables in rtl8150_get_link_ksettings”), a sibling KMSAN
ignored-`get_registers()` fix, present in `v6.18.53`. Other recent
rtl8150 work is UAF/leak/padding, not a substitute for this carrier
check. Standalone, not patch X/Y.
**Step 3.4 — Author**
Record: Yousef Alhouseen; this is their only rtl8150 commit here.
Suggested-by Petko Manolov (driver author). Reviewed-by Andrew Lunn.
**Step 3.5 — Dependencies**
Record: None. Uses existing `get_registers()` return value. `git diff
v6.18.53 6a6d945b860b3^ -- drivers/net/usb/rtl8150.c` is empty —
preimage matches 6.18.y.
## Phase 4: Mailing list / external
**Step 4.1 — Discussion**
Record: `b4 dig` → https://patch.msgid.link/20260628165033.17842-1-
alhouseenyousef@gmail.com
Revisions: v1 (carrier-off on error) → v2 (return on error) → net-next
v4 (applied).
Petko: do not force carrier off; USB messages can be lost while the
device still works.
Andrew Lunn (v2): pointed at stable-kernel-rules, asked “Does this issue
bother people?”, asked for net-next, Suggested-by, and no version
threading.
v4 changelog: “Target net-next and drop the Fixes tag as requested.”
Andrew Reviewed-by v4. Applied to netdev/net-next as `8c9c5b9a6896`. No
NAK of the code.
**Step 4.2 — Recipients**
Record: Petko, Lunn, davem, Dumazet, Kicinski, Abeni, linux-usb, netdev,
syzbot. Right lists and maintainers.
**Step 4.3 — Bug report**
Record: syzbot “KMSAN: uninit-value in rtl8150_open (2)”, prio:high.
Stack: `set_carrier` ← `rtl8150_open` ← `__dev_open` ← ioctl (dhcpcd).
Local `tmp`. Console: `usb 2-1: device reset failed`. First crash ~186d
before the report page, last ~113d. Security workflow: PeripheralTrigger
yes; DoS/exploitable/unprivileged no. Five KMSAN crashes on dummy USB.
**Step 4.4 — Series**
Record: Single patch. Not part of a multi-patch series.
**Step 4.5 — Stable list**
Record: lore.kernel.org HTTP 403; no extra stable-list thread retrieved.
v1/v2 were Cc: stable; Andrew redirected to net-next. That is process,
not a technical NAK.
## Phase 5: Semantics / call graph
**Step 5.1**
Record: `set_carrier()` only.
**Step 5.2**
Record: Only caller is `rtl8150_open()` (`drivers/net/usb/rtl8150.c`).
Link is also updated later from `intr_callback()` via `MSR_LINK`
(independent path, 100 ms interrupt URB).
**Step 5.3**
Record: `get_registers()` → `usb_control_msg_recv()`; then
`netif_carrier_on` / `netif_carrier_off`.
**Step 5.4**
Record: Reachable from userspace bringing the iface up (`SIOCSIFFLAGS` /
`dev_change_flags` / `__dev_open`). Needs the USB device (or a gadget).
Typically `CAP_NET_ADMIN`. Syzbot hit it via dhcpcd on a dummy device.
**Step 5.5**
Record: Same ignored-return pattern exists elsewhere in this file
(`read_mii_word`, `rtl8150_reset`, `enable_net_traffic`,
`get_link_ksettings`). `get_link_ksettings` was already hardened by
initializing locals (`fba383985354e`). This commit only fixes
`set_carrier()`.
## Phase 6: This tree (6.18.y)
**Step 6.1**
Record: Local tree is **6.18.53** (`git describe HEAD` →
`v6.18.53-5049-g0f0bdc2b0dffb`, `VERSION=6 PATCHLEVEL=18 SUBLEVEL=53`).
`v6.18.53:drivers/net/usb/rtl8150.c` still has the unchecked
`get_registers(dev, CSCR, 2, &tmp)`. Bug is present. Introduced in
practice with `usb_control_msg_recv` in v5.10; the ignore-return pattern
is ancient.
**Step 6.2**
Record: Clean apply. Parent of the candidate matches `v6.18.53` for this
file (0-line diff).
**Step 6.3**
Record: No other commit in 6.18.y checks `set_carrier()`’s CSCR read.
`fba383985354e` is related but only ethtool ksettings.
## Phase 7: Subsystem
**Step 7.1**
Record: USB net driver `CONFIG_USB_RTL8150`. Criticality: **PERIPHERAL**
(legacy Realtek USB 10/100 dongle; not core mm/net). Still a supported
in-tree driver, not staging.
**Step mar 7.2**
Record: Low-churn mature driver. Occasional syzbot fixes. Stable-
relevant bugs do still land here.
## Phase 8: Impact / risk
**Step 8.1**
Record: Users of `CONFIG_USB_RTL8150` only, plus KMSAN/syzbot coverage.
**Step 8.2**
Record: Trigger: CSCR USB control read fails during `rtl8150_open()`
(device reset failure, disconnect, bus noise). Uncommon on working
hardware; easy with a dummy/failing USB device. Not a general
unprivileged attack path.
**Step 8.3**
Record: Production: wrong carrier (link up/down from stack garbage). Not
a panic/UAF. Under KMSAN: `BUG: KMSAN: uninit-value`. Interrupt path can
correct carrier within ~100 ms if the device still works. Severity:
**HIGH** as uninitialized memory (KMSAN); **MEDIUM** as user-visible
wrong link state on USB error.
**Step 8.4**
Record: Benefit: stops UB and random carrier on open failure; matches a
prior stable fix in the same file. Risk: very low (3 lines, error path
only, no locking). Ratio favors backport.
## Phase 9: Synthesis
**For backporting**
- Real ignored-error / uninitialized-stack bug, not cleanup.
- Syzbot + KMSAN, reproducible from iface open.
- Same class as `fba383985354e`, already in this 6.18.y tree.
- Three-line, reviewed, Suggested-by the driver author.
- Buggy code is in `v6.18.53`; applies cleanly; no prerequisites.
- Wrong carrier on `ifup` is user-visible if the control read fails.
**Against**
- Andrew Lunn asked whether it “bothers people” and sent it to net-next,
not `net`.
- Not a production oops, deadlock, or security hole (syzbot: not
exploitable/DoS).
- Legacy USB adapter; trigger is a failed control transfer.
- Interrupt callback often repairs carrier shortly after.
**Unresolved**
- Could not fetch lore.kernel.org/stable (HTTP 403). Discussion is taken
from `b4` mboxes instead.
**Stable-rules checklist**
1. Obviously correct and tested? **PASS** — 3 lines, Reviewed-by Lunn,
Suggested-by Petko, syzbot-confirmed.
2. Real bug affecting users? **PASS** — uninit `tmp` used for carrier;
KMSAN + ioctl path.
3. Important (crash/security/corruption/deadlock)? **PASS** — KMSAN
uninitialized use is a real memory-safety bug; production symptom is
incorrect carrier, not a panic. Bar is met for sanitizer-class fixes
this pipeline treats as backport-worthy.
4. Small and contained? **PASS** — one function, +3/−1.
5. No new features/APIs? **PASS**.
6. Applies to this tree? **PASS** — 6.18.y has the bug and matching
preimage.
**Exception category:** none (not an ID/quirk/DT/build/doc exception).
It is a normal small bugfix.
The net-next routing only dropped tags for netdev process. It does not
remove the bug from 6.18.y. The patch is self-contained, applies
cleanly, and is the same error-check this driver already needed in
ethtool.
Verification:
- [Phase 1] Tags: syzbot Reported-by, Closes syzkaller URL, Suggested-by
Petko, Reviewed-by Andrew Lunn, Paolo Abeni SOB.
- [Phase 2] Diff: `set_carrier()` adds `if (get_registers(...))
return;`.
- [Phase 2] `usb_control_msg_recv()` in `drivers/usb/core/message.c`
only copies into the caller buffer when `ret == size`; errors skip
that copy.
- [Phase 3] Blame: logic since `1da177e4c3f4`; `usb_control_msg_recv`
since `b2a0f274e3f7e` (`v5.10-rc1`).
- [Phase 3] `git diff v6.18.53 6a6d945b860b3^ --
drivers/net/usb/rtl8150.c` is empty.
- [Phase 3] Sibling KMSAN fix `fba383985354e` is in `v6.18.53`.
- [Phase 4] `b4 dig -c 8c9c5b9a6896`: v1–v4; Petko requested leave-
carrier-unchanged; Lunn asked for net-next; v4 applied to net-next.
- [Phase 4] Syzkaller: KMSAN uninit-value in `rtl8150_open`, prio:high,
dhcpcd ioctl, `device reset failed`.
- [Phase 5] Only caller: `rtl8150_open`; `intr_callback` updates carrier
separately.
- [Phase 6] `git describe HEAD` = `v6.18.53-5049-g0f0bdc2b0dffb`; `make
kernelversion` = 6.18.53; buggy code present at `v6.18.53`.
- [Phase 6] `git merge-base --is-ancestor 6a6d945b860b3 v6.18.53` → not
in the release tag (candidate only).
- [Phase 8] Failure: uninitialized stack → arbitrary carrier; KMSAN BUG
under sanitizer.
- UNVERIFIED: lore.kernel.org WebFetch 403; stable@ archive not searched
via HTTP.
**YES**
drivers/net/usb/rtl8150.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/drivers/net/usb/rtl8150.c b/drivers/net/usb/rtl8150.c
index 647f28b367b99..8c739d898e57d 100644
--- a/drivers/net/usb/rtl8150.c
+++ b/drivers/net/usb/rtl8150.c
@@ -734,7 +734,9 @@ static void set_carrier(struct net_device *netdev)
rtl8150_t *dev = netdev_priv(netdev);
short tmp;
- get_registers(dev, CSCR, 2, &tmp);
+ if (get_registers(dev, CSCR, 2, &tmp))
+ return;
+
if (tmp & CSCR_LINK_STATUS)
netif_carrier_on(netdev);
else
--
2.53.0
^ permalink raw reply related [flat|nested] 32+ messages in thread* [PATCH AUTOSEL 6.18-6.1] net: prestera: ignore duplicate RIF destruction events
2026-10-19 13:44 [PATCH AUTOSEL 6.18-6.12] net: dsa: mt7530: check CORE_PLL_GROUP4 access in mt7531_setup() Sasha Levin
` (5 preceding siblings ...)
2026-10-19 13:46 ` [PATCH AUTOSEL 6.18-5.10] net: usb: rtl8150: handle link status read failures Sasha Levin
@ 2026-10-19 13:47 ` Sasha Levin
2026-10-19 13:47 ` [PATCH AUTOSEL 6.18-5.10] net: dsa: mt7530: error out on failed PHY_IAC command writes Sasha Levin
` (22 subsequent siblings)
29 siblings, 0 replies; 32+ messages in thread
From: Sasha Levin @ 2026-10-19 13:47 UTC (permalink / raw)
To: patches, stable
Cc: Yuyang Huang, Ido Schimmel, Kuniyuki Iwashima, Jakub Kicinski,
Sasha Levin, enachman, andrew+netdev, davem, edumazet, pabeni,
netdev, linux-kernel
From: Yuyang Huang <yuyanghuang@google.com>
[ Upstream commit 3cbbc9fa33382d03f2813118e672eb318eba9cde ]
During address teardown, the inetaddr notifier may be called multiple
times for the same interface. Ignore NETDEV_DOWN events if the RIF has
already been destroyed, rather than returning -EEXIST, which aborts the
notifier chain.
Cc: Ido Schimmel <idosch@nvidia.com>
Cc: Kuniyuki Iwashima <kuniyu@google.com>
Signed-off-by: Yuyang Huang <yuyanghuang@google.com>
Link: https://patch.msgid.link/20260711005405.2861680-2-yuyanghuang@google.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
LLM Generated explanations, may be completely bogus:
I have enough to decide: this tree already has the IPv4 teardown change
that makes prestera abort the inetaddr chain, so the two-line companion
fix belongs here.
## Local tree
This checkout is **v7.3-rc4-27** (`7.3.0-rc4`, `VERSION=7 PATCHLEVEL=3
SUBLEVEL=0`). The candidate is `3cbbc9fa33382` (“net: prestera: ignore
duplicate RIF destruction events”), already an ancestor of HEAD (merged
for 7.3-rc1). The verdict is whether this tree’s code needs that fix on
technical merit, not whether a pipeline already picked it.
---
### PHASE 1: COMMIT MESSAGE FORENSICS
**Step 1.1 — Subject**
Record: **[net: prestera]** **[ignore]** duplicate RIF destruction
events. “Ignore” here means treat a second destroy as success instead of
failing.
**Step 1.2 — Tags**
Record:
- `Cc: Ido Schimmel <idosch@nvidia.com>`
- `Cc: Kuniyuki Iwashima <kuniyu@google.com>`
- `Signed-off-by: Yuyang Huang <yuyanghuang@google.com>` (author)
- `Link: https://patch.msgid.link/20260711005405.2861680-2-
yuyanghuang@google.com`
- `Signed-off-by: Jakub Kicinski <kuba@kernel.org>` (netdev maintainer)
- No `Fixes:`, no `Reported-by:`, no `Cc: stable` (expected for this
review)
- Ignore any Sasha Levin SOB (none on the real commit)
Later trailers (mbox): `Acked-by: Elad Nachman <enachman@marvell.com>`
(Marvell prestera).
**Step 1.3 — Body**
Record: During address teardown the inetaddr notifier can run more than
once for the same interface. Returning `-EEXIST` when the RIF is already
gone **aborts the notifier chain**. Symptom: later `inetaddr` listeners
miss `NETDEV_DOWN`. Root cause: destroy is not idempotent.
**Step 1.4 — Hidden bug fix?**
Record: Not hidden. It is a real error-path fix: missing RIF on
`NETDEV_DOWN` was treated as a hard error.
---
### PHASE 2: DIFF ANALYSIS
**Step 2.1 — Inventory**
Record: `drivers/net/ethernet/marvell/prestera/prestera_router.c` — **+2
/ −4** (6 lines net). Function: `__prestera_inetaddr_port_event()`.
Surgical, single-file.
**Step 2.2 — Flow**
Before (`NETDEV_DOWN`, RIF already gone): set extack `"Can't find RIF"`,
return `-EEXIST`.
After: `if (!re) return 0;` then only destroy/put when the RIF still
exists.
**Step 2.3 — Bug category**
Record: **Logic / correctness** (idempotent teardown). Secondary:
**notifier-chain abort**. Not UAF/leak/lock.
Mechanism: `__prestera_inetaddr_cb` uses `notifier_from_errno(err)`.
Non-zero errno becomes `NOTIFY_STOP_MASK`, so
`blocking_notifier_call_chain()` stops. Confirmed in
`include/linux/notifier.h`.
**Step 2.4 — Fix quality**
Record: Obviously correct: destroy should be idempotent. No new
locks/APIs. Regression risk is low: a true “RIF missing on first DOWN”
is now silent success instead of `-EEXIST`. That matches mlxsw-style
handling Ido sketched on v1.
---
### PHASE 3: GIT HISTORY
**Step 3.1 — Blame**
Record: Buggy `-EEXIST` on missing RIF came from `15fa9e8c5ffbe0`
(“Implement initial inetaddr notifiers”, 2021-12-27) / `32d098bb2e494c`
(refactor, 2022-01-11). Prestera router dates to `da3c16398602a` /
`69204174cc5cb` (~v5.17). Long-lived driver code.
**Step 3.2 — Fixes: tag**
Record: None. Trigger is **not** that old RIF code alone; it is
companion series patch 3:
`aa22336b76b73` — `net: ipv4: clear dev->ip_ptr before destroying
inetdev`
That commit **is in this tree** (`git merge-base --is-ancestor`
succeeded). It moves `RCU_INIT_POINTER(dev->ip_ptr, NULL)` **before**
the `ifa_list` teardown loop in `inetdev_destroy()`.
**Step 3.3 — File history**
Record: Recent prestera_router.c churn is unrelated (kmalloc_obj, IPv4
nhid, NULL vs IS_ERR). Standalone vs the series: this is **patch 1/3**,
ordered so drivers are fixed **before** the core IPv4 change for
bisectability.
**Step 3.4 — Author**
Record: Yuyang Huang (Google); two commits in this series. Not the
prestera maintainer. Elad Nachman (Marvell) acked patch 1. Ido Schimmel
**wrote the exact diff** in v1 review.
**Step 3.5 — Dependencies**
Record: Applies standalone on the 2022 RIF code. **Semantically
required** once `aa22336b76b73` is present. Patch 2 (`e5b14e9ae82b`,
mac80211) is a sibling, not a prereq for prestera.
Without patch 3, `__prestera_inetaddr_cb` still uses
`__in_dev_get_rtnl(dev)` and skips unless `ifa_list` is empty, so
duplicate destroy on inetdev teardown does not happen. **This tree has
patch 3**, so the duplicate path is live.
---
### PHASE 4: MAILING LIST
**Step 4.1 — Original thread**
Record: `b4 dig -c 3cbbc9fa33382` →
https://patch.msgid.link/20260711005405.2861680-2-yuyanghuang@google.com
v1: single net-next patch only moving `ip_ptr` clear.
v2: `[PATCH net-next v2 0/3] align IPv4 teardown with IPv6 and fix
driver regressions`.
Ido (v1): sashiko was right; prestera must ignore missing-RIF
`NETDEV_DOWN`; he pasted this exact hunk; mlxsw already OK; audit other
listeners.
Cover letter: clearing `ip_ptr` early makes `__in_dev_get_rtnl()` NULL
in the destroy loop → prestera/mac80211 regressions. Drivers first, then
core, for bisectability.
No NAKs. Applied to net-next by Kicinski (2026-07-21). Elad Acked-by
after merge.
**Step 4.2 — Recipients (`b4 dig -w`)**
Record: davem, kuba, pabeni, dsahern, horms, edumazet, Ido, Elad
Nachman, Johannes Berg, netdev, linux-wireless. Right lists/maintainers.
**Step 4.3 — Bug report**
Record: No syzbot/bugzilla. Found in review of the IPv4 teardown change.
**Step 4.4 — Series**
1. This prestera fix
2. mac80211: use `ifa->ifa_dev`
3. IPv4 `ip_ptr` clear
All three are in this tree.
**Step 4.5 — stable@**
Record: lore.kernel.org returned 403 via HTTP; `b4` did get the netdev
thread. No `Cc: stable` in the series (sent to **net-next**, not
**net**). No evidence it was rejected for stable.
---
### PHASE 5: CODE SEMANTIC ANALYSIS
**Step 5.1 — Functions**
Record: `__prestera_inetaddr_port_event()` only.
**Step 5.2 — Callers**
`__prestera_inetaddr_event()` → `__prestera_inetaddr_cb` (`NETDEV_DOWN`,
last-address intent) and `__prestera_inetaddr_valid_cb` (`NETDEV_UP`
only). Registered via `register_inetaddr_notifier()` in
`prestera_router_init()`.
**Step 5.3 — Callees**
`prestera_rif_entry_find/create/destroy`, `dev_hold`/`dev_put`.
**Step 5.4 — Reachability**
Yes, from normal IPv4 teardown: `inetdev_destroy()` (`NETDEV_UNREGISTER`
or invalid MTU) → `inet_del_ifa()` → `inetaddr_chain` `NETDEV_DOWN`.
After patch 3, `__in_dev_get_rtnl(dev)` is NULL, so the “not last
address” skip is false and **every** remaining address generates a
destroy attempt. First succeeds; later ones hit missing RIF.
`inetaddr_chain` also includes `fib_inetaddr_event`, SCTP, nf_nat
masquerade, nfsd, lockd, mac80211, RDMA GID, ipvlan, etc. Default
notifier priority 0: boot-time FIB typically runs before prestera;
later-registered listeners after prestera **do not run** if prestera
returns `-EEXIST`.
**Step 5.5 — Similar patterns**
mlxsw already treats missing RIF as “no config”
(`mlxsw_sp_rif_should_config`). Ido: “mlxsw is OK.” No remaining `"Can't
find RIF"` in prestera.
---
### PHASE 6: THIS TREE
**Step 6.1 — Buggy code**
The pre-fix `return -EEXIST` is in `3cbbc9fa33382^`. Trigger
`aa22336b76b73` is in HEAD. Without this patch, 7.3-rc4 prestera would
abort the chain. Current HEAD already has the idempotent `return 0`.
**Step 6.2 — Backport difficulty**
Clean: same function shape since 2022. This tree already contains the
result.
**Step 6.3 — Other fixes**
No earlier equivalent. This is the fix.
---
### PHASE 7: SUBSYSTEM CONTEXT
**Step 7.1**
Record: Marvell Prestera switchdev Ethernet (`CONFIG_PRESTERA`).
**PERIPHERAL** hardware, but the failure is on the **global inetaddr
chain** (IMPORTANT impact surface).
**Step 7.2**
Record: Mature driver (v5.17+). This change is a companion to 7.3 IPv4
teardown, not random refactor.
---
### PHASE 8: IMPACT AND RISK
**Step 8.1 — Who**
Prestera switch users with IPv4 on ports, especially **multiple
addresses**, during unregister / inetdev destroy. Other `inetaddr`
listeners on those events.
**Step 8.2 — Trigger**
`inetdev_destroy()` after `ip_ptr` is cleared, more than one IPv4
address. Unprivileged users typically cannot unload the switch; admin
teardown/unregister can. Not a race; deterministic.
**Step 8.3 — Failure mode**
Not an oops/UAF. `-EEXIST` → notifier stop → later listeners miss
`NETDEV_DOWN` (SCTP local addrs, nfsd/lockd, RDMA GIDs, etc.).
`inet_del_ifa()` ignores the chain return, so teardown continues, but
**sidecar state can go stale**. Severity: **HIGH** for correctness of
address teardown, not CRITICAL crash.
**Step 8.4 — Risk/benefit**
Benefit: high for this tree (trigger commit is present). Risk: very low
(2 lines, maintainer-supplied, Acked). Ratio: strongly in favor.
---
### PHASE 9: SYNTHESIS
**For backporting**
- Real bug once `aa22336b76b73` is present (this tree).
- Aborts `inetaddr_chain`; not cosmetic.
- 2-line, obviously correct, Ido-authored hunk, Elad ack, Kicinski
merge.
- Applies cleanly; no new API.
- Patch 2 is not required for this file.
**Against**
- Niche `CONFIG_PRESTERA`.
- net-next companion, not a crash/CVE.
- No production `Reported-by`.
- Independent value is weak **without** the IPv4 `ip_ptr` move — but
that move **is here**.
**Unresolved**
- HTTP 403 on lore/stable search; thread recovered via `b4`. No sign of
a stable NAK.
**Stable rules**
1. Obviously correct and tested? **PASS** (Ido diff + Elad ack + net-
next)
2. Real user-visible bug? **PASS** (notifier abort on port teardown)
3. Important? **PASS** (broken global `NETDEV_DOWN` delivery; not a
panic)
4. Small/contained? **PASS** (+2/−4, one function)
5. No new features/APIs? **PASS**
6. Applies to this tree? **PASS** (same code; trigger present)
Exception category: none (not an ID/quirk/DT/build/doc add). Still a
contained correctness fix.
This tree is 7.3-rc4 **with** the IPv4 teardown change. Without this
patch, prestera turns duplicate RIF destroy into `-EEXIST` and stops the
inetaddr chain. The fix is minimal and already reviewed. It should be in
this tree.
Verification:
- [Phase 1] Tags: Cc Ido/Kuniyuki, Link msgid `...2861680-2`, SOB Huang
+ Kicinski; no Fixes/syzbot
- [Phase 2] Diff: `NETDEV_DOWN` `!re` path `-EEXIST` → `return 0` in
`__prestera_inetaddr_port_event`
- [Phase 3] Blame: RIF notifier from `15fa9e8c5ffbe0` / `32d098bb2e494c`
(~v5.17)
- [Phase 3] `git merge-base --is-ancestor aa22336b76b73 HEAD` → trigger
**in tree**; same for `e5b14e9ae82b` and `3cbbc9fa33382`
- [Phase 3] `git describe HEAD` → `v7.3-rc4-27-gf0100363d8c37`; `make
kernelversion` → `7.3.0-rc4`
- [Phase 3] `git show aa22336b76b73`: `ip_ptr` cleared **before** `ifa`
loop
- [Phase 4] `b4 dig -c 3cbbc9fa33382`: v2 1/3 net-next; v1 was core-only
- [Phase 4] Ido v1: exact prestera hunk; Elad Acked-by
- [Phase 4] lore HTTP 403; mbox via b4 succeeded
- [Phase 5] Callers: `__prestera_inetaddr_cb` → `notifier_from_errno`;
chain includes fib, sctp, nfsd, rdma, mac80211
- [Phase 5] `notifier_from_errno` sets `NOTIFY_STOP_MASK` on any errno
- [Phase 6] Pre-image at `3cbbc9fa33382^` still has `-EEXIST`; HEAD has
`return 0`
- [Phase 8] Failure: notifier abort, not oops; trigger = inetdev_destroy
+ multiple IPv4 addrs after `ip_ptr` clear
- UNVERIFIED: whether anyone asked for this on stable@ (lore 403)
**YES**
drivers/net/ethernet/marvell/prestera/prestera_router.c | 6 ++----
1 file changed, 2 insertions(+), 4 deletions(-)
diff --git a/drivers/net/ethernet/marvell/prestera/prestera_router.c b/drivers/net/ethernet/marvell/prestera/prestera_router.c
index de317179a7dcc..e36d023fe9c30 100644
--- a/drivers/net/ethernet/marvell/prestera/prestera_router.c
+++ b/drivers/net/ethernet/marvell/prestera/prestera_router.c
@@ -1302,10 +1302,8 @@ static int __prestera_inetaddr_port_event(struct net_device *port_dev,
dev_hold(port_dev);
break;
case NETDEV_DOWN:
- if (!re) {
- NL_SET_ERR_MSG_MOD(extack, "Can't find RIF");
- return -EEXIST;
- }
+ if (!re)
+ return 0;
prestera_rif_entry_destroy(port->sw, re);
dev_put(port_dev);
break;
--
2.53.0
^ permalink raw reply related [flat|nested] 32+ messages in thread* [PATCH AUTOSEL 6.18-5.10] net: dsa: mt7530: error out on failed PHY_IAC command writes
2026-10-19 13:44 [PATCH AUTOSEL 6.18-6.12] net: dsa: mt7530: check CORE_PLL_GROUP4 access in mt7531_setup() Sasha Levin
` (6 preceding siblings ...)
2026-10-19 13:47 ` [PATCH AUTOSEL 6.18-6.1] net: prestera: ignore duplicate RIF destruction events Sasha Levin
@ 2026-10-19 13:47 ` Sasha Levin
2026-10-19 13:47 ` [PATCH AUTOSEL 6.18-6.6] tap: fix incorrect variable used for USO check in set_offload() Sasha Levin
` (21 subsequent siblings)
29 siblings, 0 replies; 32+ messages in thread
From: Sasha Levin @ 2026-10-19 13:47 UTC (permalink / raw)
To: patches, stable
Cc: Daniel Golle, Andrew Lunn, Jakub Kicinski, Sasha Levin,
chester.a.unal, olteanv, davem, edumazet, pabeni, matthias.bgg,
angelogioacchino.delregno, netdev, linux-kernel, linux-arm-kernel,
linux-mediatek
From: Daniel Golle <daniel@makrotopia.org>
[ Upstream commit 1c95dcb7e923beebccdc79adc3dc1ec8c52295d0 ]
MT7531_PHY_ACS_ST is only ever set by the command write that precedes
each poll in the MT7531 indirect PHY access functions, and that
write's return value is discarded. A failed write leaves ACS_ST at 0
from the previous access, so the poll succeeds on its first iteration
and the functions return stale IAC contents as if they were fresh PHY
data. Check the writes and bail out before polling.
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
Link: https://patch.msgid.link/c34602e63a20ebbfb97babd145c82832d7a0b523.1785811140.git.daniel@makrotopia.org
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
LLM Generated explanations, may be completely bogus:
## Phase 1: Commit message forensics
**Step 1.1: Subject line**
Record: **[net/dsa/mt7530] [error out] Check PHY_IAC command writes and
abort on failure instead of polling stale IAC contents.**
**Step 1.2: Tags**
Record (candidate message, ignoring pipeline SOBs):
- **Reviewed-by:** Andrew Lunn `<andrew@lunn.ch>` (DSA reviewer)
- **Link:** `https://patch.msgid.link/c34602e63a20ebbfb97babd145c82832d7
a0b523.1785811140.git.daniel@makrotopia.org`
- **Signed-off-by:** Daniel Golle (author; MediaTek switch co-
maintainer)
- **Signed-off-by:** Jakub Kicinski (netdev)
- No Reported-by / Tested-by / syzbot / Cc: stable in the candidate text
Independent of the candidate text, the original LKML submission also had
**`Fixes: c288575f7810 ("net: dsa: mt7530: Add the support of MT7531
switch")`**. That tag is not used as a “already selected” signal; it
only identifies the introducing commit.
**Step 1.3: Body**
Record:
- **Bug:** `MT7531_PHY_ACS_ST` is set only by the command write before
each poll. The write return is discarded.
- **Failure mode:** A failed write leaves ACS_ST at 0 from the previous
access. The poll condition `!(val & MT7531_PHY_ACS_ST)` is already
true, so the poll returns immediately and the helpers treat leftover
IAC contents as a completed PHY access.
- **Symptom:** Reads return stale IAC data as PHY register values;
writes return poll-success (`0`) even though the PHY was not written.
- **Root cause:** Missing error check on `mt7530_mii_write()` of
`MT7531_PHY_IAC`.
- **Version info in body:** none. Introducing commit is from 2020.
**Step 1.4: Hidden bug fix?**
Record: **Not hidden.** This is an explicit error-propagation fix, not a
cleanup.
## Phase 2: Diff analysis
**Step 2.1: Inventory**
Record:
- **File:** `drivers/net/dsa/mt7530.c` only
- **Scope:** ~+12 / −6 (six identical two-line checks)
- **Functions:** `mt7531_ind_c45_phy_read`, `mt7531_ind_c45_phy_write`,
`mt7531_ind_c22_phy_read`, `mt7531_ind_c22_phy_write`
- **Classification:** single-file surgical error-path fix
**Step 2.2: Code flow**
Record: each helper already polls until ACS_ST is clear, writes a
command with ACS_ST set, then polls again. **Before:**
`mt7530_mii_write()` result is ignored; polling always runs. **After:**
a negative write return jumps to `out`, which already unlocks the MDIO
mutex and returns `ret`.
**Step 2.3: Bug mechanism**
Record: **logic / error-path correctness.** ACS_ST is `BIT(31)` of
`MT7531_PHY_IAC` (`0x701C`). Poll success is “bit clear.” A failed
command write never sets that bit, so the poll succeeds on the first
iteration.
Verified on the write helpers: after a successful poll, `ret` is `0`
from `regmap_read_poll_timeout()`, so a failed PHY write is currently
reported as **success**. On reads, `ret = val &
MT7531_MDIO_RW_DATA_MASK` returns leftover IAC data as a 16-bit PHY
value.
**Step 2.4: Fix quality**
Record: **obviously correct.** Same `goto out` path already used for
poll failures. No new locks, APIs, or data structures. Regression risk
is low: only failed bus writes change behavior, and they now return the
real errno instead of fake success / stale data.
## Phase 3: Git history
**Step 3.1: Blame**
Record: the unchecked `mt7530_mii_write(..., MT7531_PHY_IAC, ...)` lines
were introduced in **`c288575f7810` (Landen Chao, 2020-09-11), “Add the
support of MT7531 switch”**. `mt7530_mii_write()` has returned `int`
since **`a08c045580e060` (2023-04-03, regmap conversion)**. Both are in
this tree.
**Step 3.2: Fixes: target**
Record: `git merge-base --is-ancestor c288575f7810 HEAD` succeeded. The
bug is in this 6.12 tree. No later local commit already checks these
writes (`ret = mt7530_mii_write(priv, MT7531_PHY_IAC` count is 0).
**Step 3.3: File history / related commits**
Record: this tree already contains the sibling **`96e0f5184af69`**
(`v6.12.103~217`), “error out on failed reads in MT7531 PHY polling”
(upstream `77a9ebe8818cf6`). That converted the polls to
`regmap_read_poll_timeout()` but left command writes unchecked. Also
present: **`93d46870c5440`**, “check bus->read() errors in the MDIO
regmap backend” — same author, same class of swallowed MDIO errors.
**Step 3.4: Author**
Record: Daniel Golle is listed as a **MEDIATEK SWITCH DRIVER**
maintainer. Other mt7530 error-propagation fixes by him are already in
this tree.
**Step 3.5: Dependencies**
Record: **standalone.** Needs `mt7530_mii_write()` returning `int` and
the existing `goto out` + mutex unlock — all present. It does **not**
need the rest of the later 6-patch series (`core_rmw`, CORE_PLL_GROUP4,
FDB/VLAN writes, IRQ serialization).
## Phase 4: Mailing list
**Step 4.1:** Candidate is not a local commit, so `b4 dig -c` on this
SHA was not possible. `b4 dig -c 96e0f5184af69` found the earlier poll-
timeout series. This patch was submitted as **`[PATCH net 3/4]`** then
**`[PATCH net v2 3/6]`** in “fix remaining swallowed MDIO access
errors.”
Record: lore/msgid `c34602e63a20ebbfb97babd145c82832d7a0b523.1785811140.
git.daniel@makrotopia.org`; LKML copy at
https://lkml.iu.edu/hypermail/linux/kernel/2608.0/04802.html. **v2: no
changes** to this patch. Cover letter: this write check was a companion
that had been folded into the already-applied PHY-polling patch and was
split out. No NAKs found.
**Step 4.2:** Recipients included DSA/netdev maintainers (Lunn, Oltean,
Kicinski, netdev). Andrew Lunn replied **Reviewed-by** with no requested
changes (https://www.spinics.net/lists/kernel/msg6350713.html).
**Step 4.3:** No syzbot or bugzilla. Mechanism is from code review
(cover letter: Sashiko AI review of the earlier series).
**Step 4.4:** Series mates are independent swallowed-error checks. This
hunk set does not depend on them.
**Step 4.5:** Search of lore.kernel.org/stable for this subject found
**no** prior stable discussion or rejection.
## Phase 5: Semantic / call graph
semcode MCP is not available; callers were found with grep.
**Step 5.1:** Functions listed in 2.1.
**Step 5.2: Callers**
- Wired as `phy_read/write_c22/c45` for **ID_MT7531, ID_MT7988,
ID_EN7581** in `mt753x_table`.
- MDIO bus ops: `mt7530_setup_mdio()` sets
`bus->read/write/read_c45/write_c45` to wrappers that call these
helpers. That is the phylib/phylink path (link polling, PHY config).
- Direct: `mt7531_setup()` uses `mt7531_ind_c45_phy_read/write` for
CORE_PLL_GROUP4 / EEE (those call sites still ignore the return; a
later series patch addresses that and is **not** required for this
MDIO-bus fix).
**Step 5.3: Callees**
`mt7530_mutex_lock/unlock`, `mt7530_mii_write` → `regmap_write`,
`regmap_read_poll_timeout`.
**Step 5.4: Reachability**
Yes, from userspace-visible networking: phylib MDIO reads/writes on
MT7531/MT7988/EN7581 switch PHYs. Trigger is a failed switch register
write (MDIO/MMIO/regmap error), not a rare debug ioctl.
**Step 5.5:** Same swallowed-error pattern already fixed for MDIO
backend reads and PHY_IAC **polls** in this tree. This is the remaining
write side of that PHY_IAC path.
## Phase 6: This tree (6.12.y)
**Step 6.1:** Local tree is **`pending-6.12` / 6.12.111**. All six
PHY_IAC command writes are still unchecked. Bug exists here.
**Step 6.2:** Current source matches the patch “before” context
(including `regmap_read_poll_timeout` from `96e0f5184af69`). **Expected
apply: clean.**
**Step 6.3:** No other local commit already checks these writes. The
poll-timeout backport left this hole open; this is the companion.
## Phase 7: Subsystem
**Step 7.1:** `drivers/net/dsa/mt7530.c` — MediaTek DSA switch.
**IMPORTANT** (not core mm/VFS, but common on MT7988/MT7531 routers,
OpenWrt, embedded). Config: `NET_DSA_MT7530`.
**Step 7.2:** File is actively maintained; several mt7530 fixes already
landed in this 6.12 branch.
## Phase 8: Impact and risk
**Step 8.1:** Users of MT7531, MT7988, and EN7581 switches (not
MT7530/MT7621, which use nested `mdiobus_*` instead of these indirect
helpers).
**Step 8.2:** Triggers on failed `regmap_write` of PHY_IAC. Unprivileged
users do not need a special exploit; phylib link/config traffic hits
this path. Frequency is error-path, but MDIO/MMIO glitches on these
boards are real.
**Step 8.3:** Failure mode: **silent wrong PHY data / false write
success**, not an oops. Phylib can see a live link or apply settings
that never reached the PHY. Severity: **HIGH** for networking
correctness on error; not a panic.
**Step 8.4:**
- **Benefit:** Completes the already-backported PHY_IAC poll hardening;
stops fake-success writes and stale reads.
- **Risk:** Very low (12 lines, error path only).
- **Ratio:** Favorable.
## Phase 9: Synthesis
**For backporting**
- Real bug with a verified mechanism (ACS_ST only set by the discarded
write).
- Write helpers currently return **0 on a failed PHY write**.
- Small, obviously correct, maintainer-authored, Reviewed-by Andrew
Lunn.
- Buggy code and `Fixes:` target are in this 6.12 tree.
- Sibling poll-timeout fix is already here; this closes the remaining
hole that series called out.
- Applies cleanly; no new API.
**Against**
- Error-path only (healthy bus never hits it).
- Driver-specific, not a universal crash/UAF.
- No user crash report; found by review.
**Unresolved**
- Could not `b4 dig` this exact SHA (not in the checkout); lore/LKML
used instead.
- lore.kernel.org WebFetch returned 403; discussion taken from
LKML/spinics.
**Stable rules**
1. Obviously correct and tested? **PASS** (Reviewed-by Lunn; merged via
netdev)
2. Real user-visible bug? **PASS** (false MDIO success / stale PHY data)
3. Important? **PASS** (silent PHY misconfiguration; same class as
already-backported mt7530 MDIO error fixes)
4. Small/contained? **PASS** (~12 lines, one file)
5. No new features/APIs? **PASS**
6. Applies to this tree? **PASS**
Not an ID/quirk/DT/docs exception; it qualifies as a normal contained
bugfix.
This should be backported to this 6.12 tree: the bug is present, the fix
is surgical, and it finishes error handling that 6.12 already took for
the same helpers.
Verification:
- [Phase 1] Parsed candidate tags: Reviewed-by Andrew Lunn, msgid link,
Golle/Kicinski SOBs; original LKML also had Fixes: c288575f7810
- [Phase 2] Diff: six `ret = mt7530_mii_write(); if (ret < 0) goto out;`
in four PHY_IAC helpers
- [Phase 2] Header: ACS_ST is BIT(31); poll waits for it clear —
confirmed in mt7530.h
- [Phase 2] Write helpers return poll `ret` (0 on success) — failed
write currently looks successful
- [Phase 3] Blame: writes from c288575f7810 (2020); ancestor of HEAD
- [Phase 3] Sibling 96e0f5184af69 already in tree (v6.12.103~217);
93d46870c5440 MDIO read-error fix also in tree
- [Phase 3] `git log --grep='PHY_IAC command writes'` on this file:
empty
- [Phase 4] LKML v2 3/6: “v2: no changes”; Lunn Reviewed-by, no NAK
- [Phase 4] Cover letter: companion to already-applied PHY polling patch
- [Phase 4] stable@ search: no hits
- [Phase 5] Callers: mt753x_table ID_MT7531/MT7988/EN7581;
bus->read/write in mt7530_setup_mdio
- [Phase 6] `git describe HEAD` / Makefile: 6.12.111 pending-6.12; six
unchecked PHY_IAC writes remain
- [Phase 6] Current lines match the patch context (including
regmap_read_poll_timeout)
- [Phase 7] MAINTAINERS: Daniel Golle is MediaTek switch co-maintainer
- [Phase 8] Failure: stale PHY data / false write success, not oops
- UNVERIFIED: b4 dig of this exact mainline SHA (commit not in
checkout); lore.kernel.org 403
**YES**
drivers/net/dsa/mt7530.c | 24 ++++++++++++++++++------
1 file changed, 18 insertions(+), 6 deletions(-)
diff --git a/drivers/net/dsa/mt7530.c b/drivers/net/dsa/mt7530.c
index bd0a37ead0dec..24c7cb225da1a 100644
--- a/drivers/net/dsa/mt7530.c
+++ b/drivers/net/dsa/mt7530.c
@@ -557,7 +557,9 @@ mt7531_ind_c45_phy_read(struct mt7530_priv *priv, int port, int devad,
reg = MT7531_MDIO_CL45_ADDR | MT7531_MDIO_PHY_ADDR(port) |
MT7531_MDIO_DEV_ADDR(devad) | regnum;
- mt7530_mii_write(priv, MT7531_PHY_IAC, reg | MT7531_PHY_ACS_ST);
+ ret = mt7530_mii_write(priv, MT7531_PHY_IAC, reg | MT7531_PHY_ACS_ST);
+ if (ret < 0)
+ goto out;
ret = regmap_read_poll_timeout(priv->regmap, MT7531_PHY_IAC, val,
!(val & MT7531_PHY_ACS_ST), 20, 100000);
@@ -568,7 +570,9 @@ mt7531_ind_c45_phy_read(struct mt7530_priv *priv, int port, int devad,
reg = MT7531_MDIO_CL45_READ | MT7531_MDIO_PHY_ADDR(port) |
MT7531_MDIO_DEV_ADDR(devad);
- mt7530_mii_write(priv, MT7531_PHY_IAC, reg | MT7531_PHY_ACS_ST);
+ ret = mt7530_mii_write(priv, MT7531_PHY_IAC, reg | MT7531_PHY_ACS_ST);
+ if (ret < 0)
+ goto out;
ret = regmap_read_poll_timeout(priv->regmap, MT7531_PHY_IAC, val,
!(val & MT7531_PHY_ACS_ST), 20, 100000);
@@ -602,7 +606,9 @@ mt7531_ind_c45_phy_write(struct mt7530_priv *priv, int port, int devad,
reg = MT7531_MDIO_CL45_ADDR | MT7531_MDIO_PHY_ADDR(port) |
MT7531_MDIO_DEV_ADDR(devad) | regnum;
- mt7530_mii_write(priv, MT7531_PHY_IAC, reg | MT7531_PHY_ACS_ST);
+ ret = mt7530_mii_write(priv, MT7531_PHY_IAC, reg | MT7531_PHY_ACS_ST);
+ if (ret < 0)
+ goto out;
ret = regmap_read_poll_timeout(priv->regmap, MT7531_PHY_IAC, val,
!(val & MT7531_PHY_ACS_ST), 20, 100000);
@@ -613,7 +619,9 @@ mt7531_ind_c45_phy_write(struct mt7530_priv *priv, int port, int devad,
reg = MT7531_MDIO_CL45_WRITE | MT7531_MDIO_PHY_ADDR(port) |
MT7531_MDIO_DEV_ADDR(devad) | data;
- mt7530_mii_write(priv, MT7531_PHY_IAC, reg | MT7531_PHY_ACS_ST);
+ ret = mt7530_mii_write(priv, MT7531_PHY_IAC, reg | MT7531_PHY_ACS_ST);
+ if (ret < 0)
+ goto out;
ret = regmap_read_poll_timeout(priv->regmap, MT7531_PHY_IAC, val,
!(val & MT7531_PHY_ACS_ST), 20, 100000);
@@ -646,7 +654,9 @@ mt7531_ind_c22_phy_read(struct mt7530_priv *priv, int port, int regnum)
val = MT7531_MDIO_CL22_READ | MT7531_MDIO_PHY_ADDR(port) |
MT7531_MDIO_REG_ADDR(regnum);
- mt7530_mii_write(priv, MT7531_PHY_IAC, val | MT7531_PHY_ACS_ST);
+ ret = mt7530_mii_write(priv, MT7531_PHY_IAC, val | MT7531_PHY_ACS_ST);
+ if (ret < 0)
+ goto out;
ret = regmap_read_poll_timeout(priv->regmap, MT7531_PHY_IAC, val,
!(val & MT7531_PHY_ACS_ST), 20, 100000);
@@ -681,7 +691,9 @@ mt7531_ind_c22_phy_write(struct mt7530_priv *priv, int port, int regnum,
reg = MT7531_MDIO_CL22_WRITE | MT7531_MDIO_PHY_ADDR(port) |
MT7531_MDIO_REG_ADDR(regnum) | data;
- mt7530_mii_write(priv, MT7531_PHY_IAC, reg | MT7531_PHY_ACS_ST);
+ ret = mt7530_mii_write(priv, MT7531_PHY_IAC, reg | MT7531_PHY_ACS_ST);
+ if (ret < 0)
+ goto out;
ret = regmap_read_poll_timeout(priv->regmap, MT7531_PHY_IAC, reg,
!(reg & MT7531_PHY_ACS_ST), 20, 100000);
--
2.53.0
^ permalink raw reply related [flat|nested] 32+ messages in thread* [PATCH AUTOSEL 6.18-6.6] tap: fix incorrect variable used for USO check in set_offload()
2026-10-19 13:44 [PATCH AUTOSEL 6.18-6.12] net: dsa: mt7530: check CORE_PLL_GROUP4 access in mt7531_setup() Sasha Levin
` (7 preceding siblings ...)
2026-10-19 13:47 ` [PATCH AUTOSEL 6.18-5.10] net: dsa: mt7530: error out on failed PHY_IAC command writes Sasha Levin
@ 2026-10-19 13:47 ` Sasha Levin
2026-10-19 13:47 ` [PATCH AUTOSEL 6.18-6.12] net: dsa: mt7530: check bus->read() error in core_rmw() Sasha Levin
` (20 subsequent siblings)
29 siblings, 0 replies; 32+ messages in thread
From: Sasha Levin @ 2026-10-19 13:47 UTC (permalink / raw)
To: patches, stable
Cc: Rongguang Wei, Willem de Bruijn, Jakub Kicinski, Sasha Levin,
willemdebruijn.kernel, jasowangio, andrew+netdev, davem, edumazet,
pabeni, netdev, linux-kernel
From: Rongguang Wei <weirongguang@kylinos.cn>
[ Upstream commit fac7973f004de6dd51fb15a33e33ed37718e58bb ]
The USO features in set_offload() incorrectly uses feature_mask and
features argument.
The USO feature was written to the local features variable instead of
feature_mask. All other offload bits (TSO, TSO_ECN) are stored in
feature_mask which becomes tap->tap_features and is used by
tap_handle_frame() for GSO segmentation. Without NETIF_F_GSO_UDP_L4
in tap->tap_features, making USO on tap effectively non-functional.
Keeping the USO handling inside the TUN_F_CSUM block avoids enabling
GRO/LRO when userspace requests USO without CSUM.
This has not worked since the beginning, so
commit 399e0827642f ("driver/net/tun: Added features for USO.")
Signed-off-by: Rongguang Wei <weirongguang@kylinos.cn>
Reviewed-by: Willem de Bruijn <willemb@google.com>
Link: https://patch.msgid.link/20260807070914.112698-1-clementwei90@163.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
LLM Generated explanations, may be completely bogus:
I have enough to decide: the buggy USO assignment is in this 6.12 tree,
the patch applies, and it is a real offload correctness bug rather than
a crash fix.
===============================================================
PHASE 1: COMMIT MESSAGE FORENSICS
===============================================================
**Step 1.1: PARSE THE SUBJECT LINE**
Record: `[tap] [fix] incorrect variable used for USO check in
set_offload()`
- Subsystem: `tap` (macvtap/ipvtap shared driver, `drivers/net/tap.c`)
- Verb: **fix** — explicit bug fix, not cleanup/refactor
**Step 1.2: PARSE ALL COMMIT MESSAGE TAGS**
Record (from the commit under review /
`fac7973f004de6dd51fb15a33e33ed37718e58bb`):
- No `Fixes:` tag in the applied commit (the mailing-list v2 had `Fixes:
399e0827642f`; Jakub rewrote it to prose)
- No `Reported-by:`, `Tested-by:`, `Acked-by:`, `Cc:
stable@vger.kernel.org`
- `Reviewed-by: Willem de Bruijn <willemb@google.com>` (TUN/TAP
maintainer)
- `Link:
https://patch.msgid.link/20260807070914.112698-1-clementwei90@163.com`
- `Signed-off-by: Rongguang Wei <weirongguang@kylinos.cn>` (author,
KylinOS)
- `Signed-off-by: Jakub Kicinski <kuba@kernel.org>` (netdev maintainer)
- Pipeline `Signed-off-by: Sasha Levin` / `[Upstream commit ...]`
ignored per instructions
Notable: original `[PATCH net v2]` submission **did** carry `Fixes:
399e0827642f`. Absence of that tag in the applied message is editorial,
not evidence the bug is fictional.
**Step 1.3: ANALYZE THE COMMIT BODY TEXT**
Record:
- **Bug:** `set_offload()` writes `NETIF_F_GSO_UDP_L4` into the local
`features` copy instead of `feature_mask`. `tap->tap_features =
feature_mask`, and `tap_handle_frame()` uses `tap_features` for GSO.
USO therefore never lands in the forward feature mask.
- **Second bug (same hunk):** the GRO/LRO (`RX_OFFLOADS`) test compares
`feature_mask` (NETIF_F_* bits) against `TUN_F_USO4|TUN_F_USO6` (ioctl
flags 0x20/0x40). That comparison is always false.
- **Symptom:** TUNSETOFFLOAD with USO is a no-op. UDP GSO superframes
are software-segmented before userspace; USO on tap/macvtap/ipvtap has
never worked.
- **Version info:** “has not worked since the beginning” — introduced by
`399e0827642f` (v6.2-rc1).
- **Root cause:** wrong destination variable plus type-confused bit
test.
**Step 1.4: DETECT HIDDEN BUG FIXES**
Record: Not hidden. Subject, body, and diff all describe a real logic
bug. Not style/cleanup.
===============================================================
PHASE 2: DIFF ANALYSIS - LINE BY LINE
===============================================================
**Step 2.1: INVENTORY THE CHANGES**
Record:
- File: `drivers/net/tap.c` only
- Scope: **single-file surgical fix**, 2 insertions / 3 deletions (net
+2 / −3)
- Function: `set_offload()`
**Step 2.2: UNDERSTAND THE CODE FLOW CHANGE**
Record, verified against local `set_offload()` at
`drivers/net/tap.c:952`:
Hunk 1 (USO assignment, inside `if (arg & TUN_F_CSUM)`):
- **Before:** `features |= NETIF_F_GSO_UDP_L4` (`features` is a copy of
`tap->dev->features`)
- **After:** `feature_mask |= NETIF_F_GSO_UDP_L4` (same place
TSO/TSO6/CSUM are recorded)
- Path: TUNSETOFFLOAD ioctl, not a hot RX path itself; result is
consumed on every `tap_handle_frame()` RX.
Hunk 2 (RX_OFFLOADS / GRO+LRO decision):
- **Before:** enable `RX_OFFLOADS` if TSO is in `feature_mask` **or** if
`feature_mask` has both TUN_F_USO* bits (impossible: those are ioctl
flags, not netdev features)
- **After:** enable `RX_OFFLOADS` if `feature_mask` has TSO, TSO6,
**or** `NETIF_F_GSO_UDP_L4`
- USO still only set inside the TUN_F_CSUM block, so USO-without-CSUM
does not turn on GRO/LRO (v1 regression that v2 closed)
**Step 2.3: IDENTIFY THE BUG MECHANISM**
Record: **Logic / correctness** (wrong variable + type-confused bit
test).
- Mechanism: USO bit stored where `update_features()` later sees a
device-feature snapshot, not in `tap_features`. `tap_handle_frame()`
then calls `netif_needs_gso()` without `NETIF_F_GSO_UDP_L4`, so every
`SKB_GSO_UDP_L4` frame is split by `__skb_gso_segment()`.
- The TUN_F_* test vs `feature_mask` is always false: `TUN_F_USO4=0x20`
is bit 5 (`NETIF_F_HIGHDMA`), `TUN_F_USO6=0x40` is bit 6
(`NETIF_F_FRAGLIST`). `feature_mask` only ever holds
HW_CSUM/TSO/TSO_ECN/TSO6.
**Step 2.4: ASSESS THE FIX QUALITY**
Record: Obviously correct; mirrors TSO handling and tun.c’s working USO
path. Minimal, no API change. Regression risk is low: typical QEMU
already sets CSUM+TSO+USO together, so GRO/LRO was already on via TSO.
After the fix, userspace that **asked** for USO actually receives UDP
GSO frames — the documented TUNSETOFFLOAD contract. v2 specifically
avoided enabling GRO/LRO for USO without CSUM.
===============================================================
PHASE 3: GIT HISTORY INVESTIGATION
===============================================================
**Step 3.1: BLAME THE CHANGED LINES**
Record: Both buggy lines introduced by `399e0827642f6` (“driver/net/tun:
Added features for USO.”), Andrew Melnychenko, 2022-12-07. Contained in
v6.2-rc1 (`git describe --contains` → `v6.2-rc1~99^2~30^2~3`). Present
and unchanged in this tree since then (`git merge-base --is-ancestor
399e0827642f HEAD` → yes).
**Step 3.2: FOLLOW THE FIXES: TAG**
Record: Applied commit has no `Fixes:` tag; mailing-list v2 named
`399e0827642f`. That commit **is** in this tree. It added USO to both
`tun.c` and `tap.c`; tun.c stores USO in the mask that becomes
`tun->set_features` (works). tap.c used the wrong variable (broken). No
later same-issue fix in `399e0827642f..HEAD -- drivers/net/tap.c`.
**Step 3.3: CHECK FILE HISTORY FOR RELATED CHANGES**
Record: Recent tap.c history in this tree is unrelated (XDP skb->dev,
page leaks, short-frame check). On origin/master after this fix: only an
unrelated VLAN `transport_header` change. **Standalone**, not patch N/M.
**Step 3.4: CHECK THE AUTHOR'S OTHER COMMITS**
Record: Author is not the TUN/TAP maintainer (those are Willem de Bruijn
and Jason Wang). `git log --author='Rongguang Wei' -- drivers/net/` in
this tree only shows an unrelated stmmac commit. Reviewer **is** the
subsystem maintainer.
**Step 3.5: CHECK FOR DEPENDENT/PREREQUISITE COMMITS**
Record: None. Diff uses existing `feature_mask`, `NETIF_F_GSO_UDP_L4`,
`TUN_F_USO4/6`, `RX_OFFLOADS`. `git apply --check` of `fac7973f004de`
succeeded (hunks at offset 92, matching local line numbers). Can apply
standalone.
===============================================================
PHASE 4: MAILING LIST AND EXTERNAL RESEARCH
===============================================================
**Step 4.1: FIND THE ORIGINAL PATCH DISCUSSION**
Record:
- `b4 dig -c fac7973f004de` →
https://patch.msgid.link/20260807070914.112698-1-clementwei90@163.com
- Series: **v1** (2026-08-05) then **v2** (2026-08-07); v2 is what was
applied.
- v1 only flipped the RX_OFFLOADS test from `feature_mask` to `arg`
(incomplete; would enable GRO/LRO while still re-segmenting USO).
- Jakub’s review of v1 (https://lore.kernel.org/netdev/20260806163949.28
07698-1-kuba@kernel.org/) flagged the **second** wrong-variable bug
(`features |= NETIF_F_GSO_UDP_L4` vs `feature_mask`) and explicitly
warned: *“a backporter reading the changelog would likely conclude USO
on tap works afterwards.”* v2 is the complete fix those backporters
would expect.
- Willem: Reviewed-by on v1 and again on v2 (“this patch LGTM”). No NAK.
- Patchwork applied it to **netdev/net-next** (`fac7973f004d`), not the
`net` fixes tree, despite `[PATCH net]`. No reviewer `Cc: stable`.
**Step 4.2: CHECK WHO REVIEWED THE PATCH**
Record (`b4 dig -w`): To/Cc included Willem de Bruijn (TUN/TAP
maintainer), Jakub Kicinski, Jason Wang, Andrew Lunn, netdev@. Right
people. Willem re-acked v2.
**Step 4.3: SEARCH FOR THE BUG REPORT**
Record: No syzbot, bugzilla, or stack trace. Author is at kylinos.cn
(distribution). Impact described by code analysis (Jakub): every USO
superframe is segmented before userspace. Lore WebFetch to
lore.kernel.org returned 403; thread recovered via `b4` mbox instead.
**Step 4.4: CHECK FOR RELATED PATCHES AND SERIES**
Record: v1→v2 only; v2 supersedes v1. Willem asked about tun.c; author
checked and tun.c is fine (all offload bits go into one `features`
mask). No companion tun patch required. Later origin/master tap.c change
is unrelated VLAN header handling.
**Step 4.5: CHECK STABLE MAILING LIST HISTORY**
Record: No stable@ discussion found. UNVERIFIED that nobody requested
this on stable@ (lore.kernel.org/stable WebFetch 403). Absence of
discussion is not a NAK.
===============================================================
PHASE 5: CODE SEMANTIC ANALYSIS
===============================================================
**Step 5.1: IDENTIFY KEY FUNCTIONS IN THE DIFF**
Record: `set_offload()` only.
**Step 5.2: TRACE CALLERS**
Record:
- `set_offload()` ← `tap_ioctl()` `TUNSETOFFLOAD`
(`drivers/net/tap.c:1111-1121`), under `rtnl_lock()`. Userspace ioctl
on the tap/macvtap/ipvtap fd (QEMU/libvirt typical).
- Consumers of `tap->tap_features`: `tap_handle_frame()`
(`drivers/net/tap.c:320`), registered as RX handler from
`macvtap_newlink()` and `ipvtap_newlink()`.
**Step 5.3: TRACE CALLEES**
Record: `rtnl_dereference`, `tap->update_features()`
(`macvtap_update_features` / `ipvtap_update_features` →
`netdev_update_features`). No alloc/lock added by this patch.
**Step 5.4: FOLLOW THE CALL CHAIN**
Record: Userspace TUNSETOFFLOAD → `set_offload` → `tap_features` → host
RX `tap_handle_frame` → `netif_needs_gso` / `__skb_gso_segment`.
Reachable from QEMU virtio-net on macvtap/ipvtap with IFF_VNET_HDR.
**Yes, userspace-reachable.** Not an interrupt-only or obscure-config
path; `CONFIG_MACVTAP`/`CONFIG_IPVTAP` are common on virt hosts.
**Step 5.5: SEARCH FOR SIMILAR PATTERNS**
Record: tun.c `set_offload()` correctly does `features |=
NETIF_F_GSO_UDP_L4` then `tun->set_features = features`. macvtap/ipvtap
`TUN_OFFLOADS` still omit `NETIF_F_GSO_UDP_L4` at `newlink`; that only
matters until TUNSETOFFLOAD overwrites `tap_features`. The ioctl path is
the complete fix. No sibling leftover once this lands.
===============================================================
PHASE 6: CROSS-REFERENCING AGAINST THE LOCAL TREE
===============================================================
**Step 6.1: DOES THE BUGGY CODE EXIST IN THIS TREE?**
Record: **Yes.** Local tree is **pending-6.12** / `make kernelversion`
**6.12.111** (`git describe HEAD` → `p-6.12-3-gc504cf0baa3d8`).
```977:978:drivers/net/tap.c
if ((arg & (TUN_F_USO4 | TUN_F_USO6)) == (TUN_F_USO4 |
TUN_F_USO6))
features |= NETIF_F_GSO_UDP_L4;
```
```989:990:drivers/net/tap.c
if (feature_mask & (NETIF_F_TSO | NETIF_F_TSO6) ||
(feature_mask & (TUN_F_USO4 | TUN_F_USO6)) == (TUN_F_USO4 |
TUN_F_USO6))
```
`git merge-base --is-ancestor fac7973f004de HEAD` → **not an ancestor**
(fix not present). Bug introduced v6.2-rc1, well before 6.12.
**Step 6.2: CHECK FOR BACKPORT COMPLICATIONS**
Record: **Clean apply** (`git apply --check` succeeded; line offset
only). Surrounding `set_offload()` body matches mainline. No conflicting
6.12 refactor.
**Step 6.3: CHECK IF RELATED FIXES ARE ALREADY HERE**
Record: `git log --grep='incorrect variable used for USO' --
drivers/net/tap.c` empty. No alternate USO tap fix in this tree.
===============================================================
PHASE 7: SUBSYSTEM AND MAINTAINER CONTEXT
===============================================================
**Step 7.1: IDENTIFY THE SUBSYSTEM AND ITS CRITICALITY**
Record: Networking TAP/MACVTAP/IPVTAP (`drivers/net/tap.c`).
**IMPORTANT** for virtualization (libvirt macvtap, KVM virtio-net), not
core mm/VFS. Not staging.
**Step 7.2: ASSESS SUBSYSTEM ACTIVITY**
Record: Mature. USO addition is from 2022; `set_offload()` logic dates
to the old macvtap path (2013). Low churn in the offload function
itself.
===============================================================
PHASE 8: IMPACT AND RISK ASSESSMENT
===============================================================
**Step 8.1: DETERMINE WHO IS AFFECTED**
Record: **Driver-specific / config-specific:** users of macvtap or
ipvtap who issue TUNSETOFFLOAD with both USO4 and USO6 (typical QEMU
virtio-net USO). tun/tap (`drivers/net/tun.c`) already works. Not
universal.
**Step 8.2: DETERMINE THE TRIGGER CONDITIONS**
Record: Userspace sets TUN_F_CSUM|TUN_F_USO4|TUN_F_USO6 (usually with
TSO). Then a GSO UDP packet arrives on the macvtap/ipvtap netdev with
IFF_VNET_HDR. Not a race. Unprivileged user needs the tap fd (VM
process). Common on virt hosts that enable guest USO; silent if nobody
requests USO.
**Step 8.3: DETERMINE THE FAILURE MODE SEVERITY**
Record: **Failure mode:** USO advertised via ioctl does not work; kernel
always software-segments UDP GSO before queueing to userspace. Packets
are still delivered (not a drop/crash on the happy path).
`__skb_gso_segment()` failure can drop a frame
(`SKB_DROP_REASON_SKB_GSO_SEG`). No oops, deadlock, or filesystem
corruption.
**Severity: MEDIUM** — functional/performance offload bug, “oh, that’s
not good,” not CRITICAL.
**Step 8.4: CALCULATE RISK-BENEFIT RATIO**
Record:
- **Benefit:** Medium. Restores a userspace-requested virt offload that
6.12 already advertises (`TUN_F_USO4/6` accepted by TUNSETOFFLOAD).
Helps macvtap UDP throughput (VXLAN/QUIC-style workloads). Distro
author.
- **Risk:** Very low. Two-line logic fix, no locking/API/data-structure
change, applies cleanly. Behavior change only for fds that **opt in**
to USO.
- **Ratio:** Benefit outweighs risk for this LTS virt-host kernel.
===============================================================
PHASE 9: FINAL SYNTHESIS
===============================================================
**Step 9.1: COMPILE THE EVIDENCE**
FOR backporting:
- Real, verified logic bug; USO on tap has been a no-op since v6.2, and
that code is in 6.12.111
- Tiny, obviously correct; matches working tun.c and TSO handling
- TUN/TAP maintainer Reviewed-by; netdev maintainer applied it
- v2 is complete (v1 would have been a bad backport)
- Userspace ioctl already accepts USO flags; this makes the existing ABI
work
- Applies cleanly; no prerequisites
- KylinOS engineer / virt-relevant path (macvtap)
AGAINST:
- Not a crash, security hole, deadlock, or data-corruption bug
- Feature never worked; this is the first time 6.12 would actually pass
UDP GSO to tap userspace
- Applied to net-next; Fixes: tag stripped in the git message
- No syzbot/user crash report; four years without a flood of reports
- Performance/offload; segmented delivery still works
UNRESOLVED:
- Could not fetch lore.kernel.org over HTTP (403); used `b4` mbox
instead
- Could not search stable@ archives the same way (UNVERIFIED no prior
stable NAK)
**Step 9.2: APPLY THE STABLE RULES CHECKLIST**
1. Obviously correct and tested? **PASS** — two-line logic fix,
maintainer Reviewed-by, applied upstream.
2. Fixes a real bug that affects users? **PASS** — TUNSETOFFLOAD USO is
silently ignored; virt userspace that requests it does not get USO
frames.
3. Important issue? **PASS** (as “oh, that’s not good” / notable virt
offload failure) — not crash/security. Official rules also allow that
class.
4. Small and contained? **PASS** — one function, ~5 lines.
5. No new features or APIs? **PASS** — does not add ioctls or flags;
implements flags this tree already accepts.
6. Can apply to the local tree? **PASS** — buggy code present; `git
apply --check` clean.
**Step 9.3: CHECK FOR EXCEPTION CATEGORIES**
Record: None (not a device ID, quirk, DT, build, or doc fix). Qualifies
as a normal small correctness fix.
**Step 9.4: MAKE YOUR DECISION**
This tree is 6.12.y. The bug exists here, the introducing commit is
here, and the fix is self-contained. Stable users of macvtap/ipvtap who
already request USO get a no-op today; backporting restores the intended
offload with negligible regression risk. That meets the 6.12 bar even
though it is not a panic-class bug.
Verification:
- [Phase 1] Parsed subject/tags/body: fix of wrong USO variable;
Reviewed-by Willem; Link msgid 20260807070914.112698-1; no Cc: stable
in applied message
- [Phase 2] Diff: `features |= NETIF_F_GSO_UDP_L4` → `feature_mask |=
...`; RX_OFFLOADS test uses `NETIF_F_GSO_UDP_L4`. Confirmed against
`drivers/net/tap.c:952-1003`
- [Phase 2] TUN_F_USO4/6 = 0x20/0x40 vs netdev bits 5/6
(HIGHDMA/FRAGLIST): check vs feature_mask always false. Confirmed in
`include/uapi/linux/if_tun.h` and `include/linux/netdev_features.h`
- [Phase 3] `git blame -L 976,990 drivers/net/tap.c`: both lines from
`399e0827642f6`; `git describe --contains` → v6.2-rc1; ancestor of
HEAD
- [Phase 3] `git merge-base --is-ancestor fac7973f004de HEAD` → no (fix
not in this tree)
- [Phase 3] `git show 399e0827642f6 -- drivers/net/tap.c`: original USO
addition used the wrong variable from day one
- [Phase 3] tun.c `set_offload` correctly stores USO in `features` /
`set_features` (lines 2938-2950)
- [Phase 4] `b4 dig -c fac7973f004de`: lore thread found; `-a` showed v1
and v2; `-w` listed Willem/Jakub/Jason/netdev
- [Phase 4] `b4 dig -m /tmp/tap-uso-thread.mbox`: Willem LGTM; tun.c
checked, no extra patch
- [Phase 4] v1 mbox: only the RX_OFFLOADS `arg` vs `feature_mask` flip;
incomplete
- [Phase 4] Kuba 20260806163949: identified missing `feature_mask |=
NETIF_F_GSO_UDP_L4` and warned backporters about a half-fix
- [Phase 5] Callers: `tap_ioctl` TUNSETOFFLOAD; `tap_handle_frame` uses
`tap_features`; registered from macvtap.c:101 and ipvtap.c:93
- [Phase 5] `netif_needs_gso()` in `include/linux/netdevice.h:5115` —
GSO skb segmented when features lack that GSO type
- [Phase 6] `git describe HEAD` / `make kernelversion`: p-6.12-3 /
6.12.111; buggy assignment present at tap.c:978
- [Phase 6] `git show fac7973f004de -- drivers/net/tap.c | git apply
--check` → hunks succeeded at offset 92
- [Phase 7] MAINTAINERS: TUN/TAP driver, Willem de Bruijn + Jason Wang
- [Phase 8] Failure mode: USO non-functional / extra software GSO, not
oops; severity MEDIUM
- UNVERIFIED: HTTP lore.kernel.org and lore.kernel.org/stable searches
(403); content recovered via b4 instead
The problem is a two-line mix-up that makes tap/macvtap/ipvtap USO a no-
op on this 6.12 tree. It meets stable rules: obviously correct, real
bug, small, no new API, applies cleanly. Risk is very low because only
USO-opt-in userspace changes behavior, and that change is the documented
ioctl contract.
**YES**
drivers/net/tap.c | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/drivers/net/tap.c b/drivers/net/tap.c
index 1aeaecd4030fa..9eea418afe2fc 100644
--- a/drivers/net/tap.c
+++ b/drivers/net/tap.c
@@ -883,7 +883,7 @@ static int set_offload(struct tap_queue *q, unsigned long arg)
/* TODO: for now USO4 and USO6 should work simultaneously */
if ((arg & (TUN_F_USO4 | TUN_F_USO6)) == (TUN_F_USO4 | TUN_F_USO6))
- features |= NETIF_F_GSO_UDP_L4;
+ feature_mask |= NETIF_F_GSO_UDP_L4;
}
/* tun/tap driver inverts the usage for TSO offloads, where
@@ -894,8 +894,7 @@ static int set_offload(struct tap_queue *q, unsigned long arg)
* When user space turns off TSO, we turn off GSO/LRO so that
* user-space will not receive TSO frames.
*/
- if (feature_mask & (NETIF_F_TSO | NETIF_F_TSO6) ||
- (feature_mask & (TUN_F_USO4 | TUN_F_USO6)) == (TUN_F_USO4 | TUN_F_USO6))
+ if (feature_mask & (NETIF_F_TSO | NETIF_F_TSO6 | NETIF_F_GSO_UDP_L4))
features |= RX_OFFLOADS;
else
features &= ~RX_OFFLOADS;
--
2.53.0
^ permalink raw reply related [flat|nested] 32+ messages in thread* [PATCH AUTOSEL 6.18-6.12] net: dsa: mt7530: check bus->read() error in core_rmw()
2026-10-19 13:44 [PATCH AUTOSEL 6.18-6.12] net: dsa: mt7530: check CORE_PLL_GROUP4 access in mt7531_setup() Sasha Levin
` (8 preceding siblings ...)
2026-10-19 13:47 ` [PATCH AUTOSEL 6.18-6.6] tap: fix incorrect variable used for USO check in set_offload() Sasha Levin
@ 2026-10-19 13:47 ` Sasha Levin
2026-10-19 13:47 ` [PATCH AUTOSEL 6.18-5.10] net/rds: unpin MR pages with unpin_user_pages_dirty_lock() Sasha Levin
` (19 subsequent siblings)
29 siblings, 0 replies; 32+ messages in thread
From: Sasha Levin @ 2026-10-19 13:47 UTC (permalink / raw)
To: patches, stable
Cc: Daniel Golle, Andrew Lunn, Jakub Kicinski, Sasha Levin,
chester.a.unal, olteanv, davem, edumazet, pabeni, matthias.bgg,
angelogioacchino.delregno, netdev, linux-kernel, linux-arm-kernel,
linux-mediatek
From: Daniel Golle <daniel@makrotopia.org>
[ Upstream commit 573d6e3afe3d01e34dc82c77bf22ca96a74d805e ]
core_rmw() accesses the MMD core registers directly rather than
through the regmap and has the same unchecked bus->read() as the
one just fixed in the MDIO regmap backend: a negative errno is
consumed as register data, modified and written back to the switch.
Check the read and bail out like the surrounding bus accesses do.
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
Link: https://patch.msgid.link/48bb9f0b311a9efeda2a6b24a7e05d4792393a3b.1785811140.git.daniel@makrotopia.org
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
LLM Generated explanations, may be completely bogus:
## Phase 1: Commit message forensics
**Step 1.1: Subject line**
Record: `[net: dsa: mt7530]` [`check`] [detect `bus->read()` failure in
`core_rmw()` instead of treating errno as register data]
**Step 1.2: Tags**
Record:
- `Signed-off-by: Daniel Golle <daniel@makrotopia.org>` (author)
- `Reviewed-by: Andrew Lunn <andrew@lunn.ch>` (DSA co-maintainer)
- `Link: https://patch.msgid.link/48bb9f0b311a9efeda2a6b24a7e05d4792393a
3b.1785811140.git.daniel@makrotopia.org`
- `Signed-off-by: Jakub Kicinski <kuba@kernel.org>` (netdev maintainer)
- No `Fixes:` in the committed message
- Mailing-list submission **did** include `Fixes: b8f126a8d543 ("net-
next: dsa: add dsa support for Mediatek MT7530 switch")` (verified in
the saved mbox); it was dropped before mainline
- No `Reported-by:`, `Tested-by:`, `Cc: stable` in the committed message
(absence is expected)
**Step 1.3: Body**
Record:
- **Bug:** `core_rmw()` talks to MMD core registers via raw
`bus->read()`/`bus->write()`, not regmap. A failed `bus->read()`
returns a negative errno, which is used as the current register value,
masked/ORed, and written back.
- **Symptom:** garbage RMW into switch MMD registers (clock-gate bits on
this path).
- **Root cause:** same unchecked-read pattern as the MDIO regmap
backend, which this tree already has as `93d46870c5440`.
- **Version info:** none in the body. The inlined form is from
`7c5e37d7ee78d` (v6.11 era, present here). The mailing-list `Fixes:`
points at the 2017 driver introduction; the old
`core_read_mmd_indirect()` had the same unchecked read.
**Step 1.4: Hidden bug fix?**
Record: Not hidden. Explicit error-path correctness fix: missing check →
errno treated as data → written to hardware.
---
## Phase 2: Diff analysis
**Step 2.1: Inventory**
Record:
- File: `drivers/net/dsa/mt7530.c`
- Scope: ~5 insertions / 1 deletion in one function
- Function: `core_rmw()`
- Classification: single-file surgical fix
**Step 2.2: Code flow**
Record:
- **Before:** after three checked MMD address writes, `val =
bus->read(...)`. No check. `val &= ~mask; val |= set;` then
`bus->write(...)`.
- **After:** `ret = bus->read(...)`; if `ret < 0` goto the existing
`err` path (log + unlock); else `val = ret` and continue the RMW.
- Path: error path of an MDIO read during RMW. Normal success path
unchanged.
**Step 2.3: Bug mechanism**
Record:
- Category: logic / correctness on an error path (errno consumed as
register data, then written back). Same class as the already-present
MDIO regmap fix.
- `mii_bus->read` returns a 16-bit value or a negative errno
(`include/linux/mdio.h`: “returns value or negative error code”).
`val` is `u32`, so `-ETIMEDOUT` (-110) becomes `0xffffff92`.
`bus->write()` takes `u16`, so the switch sees `0xff92` with the
intended bit tweaks, not the real register contents.
- Mechanism: check `ret < 0` and skip the write, matching the
surrounding writes.
**Step 2.4: Fix quality**
Record:
- Obviously correct; copies the existing write-error pattern in the same
function.
- Minimal; no unrelated edits, no API change.
- Regression risk: very low. On read failure it no longer writes.
`core_rmw()` is still `void` and still only logs (pre-existing); this
patch does not invent a new return convention.
- `val = ret` after the check is safe: success values are 0..0xffff.
---
## Phase 3: Git history
**Step 3.1: Blame**
Record:
- Unchecked read at lines 155–156: `7c5e37d7ee78d` (“simplify core
operations”, Arınç ÜNAL, 2024-04-18).
- That commit inlined `core_read_mmd_indirect()`, which already returned
`bus->read()` without checking. The defect dates to `b8f126a8d543`
(2017), driver introduction.
- Inlined form is in this tree (`git merge-base --is-ancestor
7c5e37d7ee78d HEAD` succeeded). `git describe --contains` places it
around v6.11-rc1, so it is in 6.12.
**Step 3.2: Fixes: tag**
Record: No `Fixes:` on the committed patch. Submission tagged
`b8f126a8d543`, which **is** an ancestor of this tree. Intermediate:
`7c5e37d7ee78d` inlined the helper and kept the bug.
**Step 3.3: File history**
Record:
- This tree already has the sibling: `93d46870c5440` “check bus->read()
errors in the MDIO regmap backend”.
- Also: `96e0f5184af69` “error out on failed reads in MT7531 PHY
polling”.
- Candidate `573d6e3afe3d0` is **not** an ancestor of HEAD.
- Standalone vs series: mailing-list subject is `[PATCH net v2 2/6]`.
Diff does not depend on 1/6 or 3–6/6.
**Step 3.4: Author**
Record: Daniel Golle is a regular MT7530 contributor (OpenWrt/MediaTek).
Nearby commits by him in this tree are the other MDIO-error fixes.
Reviewed by Andrew Lunn.
**Step 3.5: Dependencies**
Record: None for this hunk. It uses existing `ret`, `goto err`, and
`bus->read`/`bus->write`. `git apply --check` succeeded (hunk offset 28
lines only).
---
## Phase 4: Mailing list / external research
**Step 4.1: Original discussion**
Record:
- `b4 dig -c 573d6e3afe3d0` → https://patch.msgid.link/48bb9f0b311a9efed
a2a6b24a7e05d4792393a3b.1785811140.git.daniel@makrotopia.org
- Series: `[PATCH net v2 0/6] net: dsa: mt7530: fix remaining swallowed
MDIO access errors`
- v2: “no changes” for this patch; Andrew’s `Reviewed-by` kept.
- Cover letter: leftover companion of the first “swallowed MDIO read
errors” series (already on `net`); this is the unchecked `bus->read()`
in `core_rmw()`.
- Sent to `net` (fixes), not `net-next`.
- No NAK in the saved mbox. Applied by Jakub Kicinski (`0200d48477602`
merge).
**Step 4.2: Recipients (`b4 dig -w`)**
Record: Andrew Lunn, Vladimir Oltean, netdev maintainers, MediaTek,
`netdev@`, `linux-mediatek@`. Right audience. Lunn reviewed.
**Step 4.3: Bug report**
Record: No syzbot/bugzilla. Found by review of the same MDIO-error class
(cover letter cites Sashiko AI review of the parent series).
**Step 4.4: Series**
Record: 6 patches. This is 2/6 and is self-contained. 3–5/6 are other
swallowed-error sites; 1/6 is `pcs-mtk-lynxi`; 6/6 is IRQ-chip
serialization. Not required for this hunk.
**Step 4.5: Stable list**
Record: **UNVERIFIED** — lore.kernel.org returned 403/503 to
WebFetch/curl. No local evidence this was rejected for stable.
---
## Phase 5: Code semantics
**Step 5.1: Functions**
Record: `core_rmw()` only.
**Step 5.2: Callers**
Record:
- `core_set()` / `core_clear()` → `core_rmw()`
- `mt7530_pll_setup()`: clear/set `REG_GSWCK_EN` on
`CORE_TRGMII_GSW_CLK_CG` (switch setup, 40 MHz XTAL)
- `mt7530_setup_port6()`: clear/set `REG_TRGMIICK_EN` (port 6 TRGMII
clock)
- `mt7530_pll_setup()` from `mt7530_setup()` (~line 2422)
- `mt7530_setup_port6()` from `mt7530_mac_config()` when `port == 6`
**Step 5.3: Callees**
Record: `mt7530_mutex_lock/unlock`, `bus->write`, `bus->read`,
`dev_err`.
**Step 5.4: Reachability**
Record: Yes, from DSA probe/setup and phylink MAC config on MT7530
(common in MediaTek routers). Not a syscall hot path; hit on bring-up
and port-6 mode changes. Userspace/netlink can trigger `mac_config`.
**Step 5.5: Similar patterns**
Record: Same bug already fixed in `mt7530_regmap_read()` in this tree.
`core_write()` only writes (no analogous read). Remaining siblings
(PHY_IAC writes, CORE_PLL_GROUP4, FDB/VLAN cmd) are other series
patches, not prerequisites.
---
## Phase 6: This tree (6.12.y)
**Step 6.1: Buggy code present?**
Record: Yes. Tree is `pending-6.12` / `6.12.111`
(`p-6.12-4-gbf6fcdf761abd`). Current `core_rmw()` still does `val =
bus->read(...)` with no check (`drivers/net/dsa/mt7530.c:155-156`).
`573d6e3afe3d0` is not an ancestor of HEAD.
**Step 6.2: Backport difficulty**
Record: Clean apply (`git apply --check`: hunk succeeded at offset 28).
Surrounding `core_rmw()` matches the pre-image.
**Step 6.3: Related fixes already here?**
Record: Regmap-backend check is here; PHY polling check is here. This
leftover `core_rmw()` instance is not. No alternate fix of this
function.
---
## Phase 7: Subsystem context
**Step 7.1**
Record: `drivers/net/dsa/mt7530.c` — MediaTek MT7530 DSA. **IMPORTANT**
(common embedded/router switch), not core mm/VFS, not staging.
**Step 7.2**
Record: Actively maintained; recent 6.12 backports already include other
MT7530 MDIO-error and hardware fixes.
---
## Phase 8: Impact and risk
**Step 8.1: Who is affected**
Record: Driver-specific: `CONFIG_NET_DSA_MT7530` (MDIO-attached
MT7530/MT7531, common on MT7621/MT798x-class boards).
**Step 8.2: Trigger**
Record: `bus->read()` failure during PLL/clock RMW (timeout, NACK, flaky
MDIO). Not every boot if the bus is healthy. Unprivileged users do not
trigger MDIO failure directly; they can hit the RMW via interface/port
config. Timing/hardware dependent.
**Step 8.3: Failure mode**
Record: On MDIO read failure, a truncated errno (e.g. `0xff92`) is
written to `CORE_TRGMII_GSW_CLK_CG` (core / TRGMII clock gates) instead
of toggling bit 0 or 1. That can leave clocks misconfigured (no link /
switch not forwarding) rather than failing closed. Severity: **HIGH** on
that error path (hardware misconfiguration), not a panic/UAF.
**Step 8.4: Risk-benefit**
Record:
- Benefit: stops garbage writes to switch clock-gate MMDs; completes the
same class already backported via the regmap patch.
- Risk: very low (4–5 lines, existing `err` path, no locking/API
change).
- Ratio: favorable.
---
## Phase 9: Synthesis
**For backporting**
- Real bug: negative MDIO errno used as register data and written back.
- Consequence: corrupt RMW of switch clock-gate registers.
- Small, obviously correct, reviewed by Andrew Lunn, taken into `net` as
a fix.
- Buggy code is in this 6.12 tree; patch applies cleanly.
- Same class as `93d46870c5440`, already in this tree; this is the
leftover `core_rmw()` site.
**Against**
- Error path only (MDIO must already be failing).
- Still `void`; errors are only logged (pre-existing).
- No syzbot/user crash report.
- Labelled 2/6, but this hunk does not need the rest.
**Unresolved**
- Stable-list discussion (lore 403/503). Does not change the technical
case.
**Stable rules**
1. Obviously correct and tested? **PASS** — matches surrounding checks;
Lunn `Reviewed-by`; in mainline `net`.
2. Real user-affecting bug? **PASS** — garbage written to MT7530 clock-
gate MMDs on read failure.
3. Important (crash/security/corruption/deadlock)? **PASS** — hardware
misconfiguration of switch clocks; same class already accepted in
this tree.
4. Small and contained? **PASS** — ~5 lines, one function.
5. No new features/APIs? **PASS**.
6. Applies to this tree? **PASS** — `git apply --check` clean.
**Exception category:** none (not an ID/quirk/DT/build/doc exception).
Qualifies as a normal small driver bugfix.
---
### What the commit solves for this tree
In 6.12, `core_rmw()` still assigns `bus->read()` to `u32 val` without
checking. On timeout/NACK that errno is masked, OR’d, and written to
`CORE_TRGMII_GSW_CLK_CG` during PLL setup and port-6 TRGMII clock
enable. The fix bails out like the three preceding writes. It is the
companion to the regmap-backend check already present here.
Risk is negligible; benefit is avoiding a bad clock-gate RMW when MDIO
fails on a widely used MediaTek DSA switch.
Verification:
- [Phase 1] Parsed tags: Lunn `Reviewed-by`, msgid link; no committed
`Fixes:`; mbox had `Fixes: b8f126a8d543`
- [Phase 2] Diff: 5 insertions in `core_rmw()`; check `ret < 0` before
RMW write
- [Phase 3] Blame: inlined by `7c5e37d7ee78d` (v6.11, in this tree);
original unchecked read in `b8f126a8d543` (2017)
- [Phase 3] `git merge-base --is-ancestor 573d6e3afe3d0 HEAD` → not in
tree; `7c5e37d7ee78d` is in tree
- [Phase 3] Sibling `93d46870c5440` is in this tree
- [Phase 4] `b4 dig -c 573d6e3afe3d0`: v2 2/6 of “fix remaining
swallowed MDIO access errors”; v2 no changes; Lunn review on 1–3 and 6
- [Phase 4] Cover letter: leftover `core_rmw()` companion of the first
MDIO-error series
- [Phase 4] `b4 dig -w`: netdev + DSA + MediaTek CCs
- [Phase 4] lore WebFetch/curl 403/503; thread recovered via `b4 dig -m`
- [Phase 5] Callers: `mt7530_pll_setup`, `mt7530_setup_port6` → DSA
setup / `mac_config`
- [Phase 5] `bus->read` contract: value or negative errno
(`include/linux/mdio.h`)
- [Phase 6] `git describe HEAD`: `p-6.12-4-gbf6fcdf761abd`; `make
kernelversion`: 6.12.111; buggy `val = bus->read` still at
mt7530.c:155
- [Phase 6] `git apply --check` of `573d6e3afe3d0`: succeeded (offset
28)
- [Phase 8] Failure mode: truncated errno written to
`CORE_TRGMII_GSW_CLK_CG`; severity HIGH on that error path
- UNVERIFIED: whether stable@ discussed this commit (lore unreachable)
**YES**
drivers/net/dsa/mt7530.c | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/drivers/net/dsa/mt7530.c b/drivers/net/dsa/mt7530.c
index 24c7cb225da1a..d2dcc2f1522f4 100644
--- a/drivers/net/dsa/mt7530.c
+++ b/drivers/net/dsa/mt7530.c
@@ -124,8 +124,12 @@ core_rmw(struct mt7530_priv *priv, u32 reg, u32 mask, u32 set)
goto err;
/* Read the content of the MMD's selected register */
- val = bus->read(bus, MT753X_CTRL_PHY_ADDR(priv->mdiodev->addr),
+ ret = bus->read(bus, MT753X_CTRL_PHY_ADDR(priv->mdiodev->addr),
MII_MMD_DATA);
+ if (ret < 0)
+ goto err;
+ val = ret;
+
val &= ~mask;
val |= set;
/* Write the data into MMD's selected register */
--
2.53.0
^ permalink raw reply related [flat|nested] 32+ messages in thread* [PATCH AUTOSEL 6.18-5.10] net/rds: unpin MR pages with unpin_user_pages_dirty_lock()
2026-10-19 13:44 [PATCH AUTOSEL 6.18-6.12] net: dsa: mt7530: check CORE_PLL_GROUP4 access in mt7531_setup() Sasha Levin
` (9 preceding siblings ...)
2026-10-19 13:47 ` [PATCH AUTOSEL 6.18-6.12] net: dsa: mt7530: check bus->read() error in core_rmw() Sasha Levin
@ 2026-10-19 13:47 ` Sasha Levin
2026-10-19 13:47 ` [PATCH AUTOSEL 6.18] netpoll: do not warn when the best-effort pool refill fails Sasha Levin
` (18 subsequent siblings)
29 siblings, 0 replies; 32+ messages in thread
From: Sasha Levin @ 2026-10-19 13:47 UTC (permalink / raw)
To: patches, stable
Cc: Allison Henderson, Jakub Kicinski, Sasha Levin, davem, edumazet,
pabeni, netdev, linux-rdma, rds-devel, linux-kernel
From: Allison Henderson <achender@kernel.org>
[ Upstream commit a507023e6ff1d0b4e7aac49b2f547fed0c21ea4e ]
The pages backing an RDS memory region are pinned in __rds_rdma_map()
with rds_pin_pages(), which uses pin_user_pages_fast(): each page's
refcount is biased by GUP_PIN_COUNTING_BIAS to account the pin. The
scatterlist is then handed to the IB transport, and the transport
releases the pages in __rds_ib_teardown_mr() with
set_page_dirty(page);
put_page(page);
put_page() drops a single reference instead of removing the pin bias,
so every MR teardown permanently strands the remaining references and
the pages are never freed - a userspace-triggerable memory leak of up
to RDS_MAX_MSG_SIZE per RDS_GET_MR/RDS_GET_MR_FOR_DEST call.
The conversion to the pin API updated the unpin sites in rdma.c but
missed this one on the transport side. Release the pages with
unpin_user_pages_dirty_lock(), which removes the pin bias and also
dirties the page under the folio lock, closing the truncation race
that a bare set_page_dirty() leaves open.
Dirtying under the folio lock can sleep, which is safe in every path
that reaches __rds_ib_teardown_mr(): the registration-reuse path
(rds_ib_map_frmr()) runs in syscall context, and the pool flush
(rds_ib_unreg_frmr()) runs under pool->flush_lock, a mutex, and
already sleeps in rds_ib_post_inv(). The WARN_ON that guarded the
old irq-context set_page_dirty() case is dropped along with it.
Signed-off-by: Allison Henderson <achender@kernel.org>
Link: https://patch.msgid.link/20260730041629.3512480-5-achender@kernel.org
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
LLM Generated explanations, may be completely bogus:
The local tree is **5.10.270** (`pending-5.10`). The buggy RDS IB
teardown path is present here, the pin API mismatch is real, and the fix
applies cleanly.
---
## PHASE 1: COMMIT MESSAGE FORENSICS
**Step 1.1: PARSE THE SUBJECT LINE**
Record: **[net/rds] [unpin] [release MR pages with the pin-API unpin
helper instead of put_page()]**
Subsystem prefix `net/rds`. Action verb is `unpin` (correctness of page
release, not a feature). Claimed intent: MR pages pinned with
`pin_user_pages_fast()` must be released with
`unpin_user_pages_dirty_lock()`, not `put_page()`.
**Step 1.2: PARSE ALL COMMIT MESSAGE TAGS**
From the candidate message:
- `Signed-off-by: Allison Henderson <achender@kernel.org>` (author;
Oracle RDS)
- `Link:
https://patch.msgid.link/20260730041629.3512480-5-achender@kernel.org`
- `Signed-off-by: Jakub Kicinski <kuba@kernel.org>` (netdev merge)
- No `Reported-by`, `Tested-by`, `Reviewed-by`, `Acked-by`, `Cc: stable`
in the candidate text
From the original lore submission (msgid
`20260730041629.3512480-5-achender@kernel.org`), these additional tags
were present and stripped by candidate prep:
- `Fixes: 0d4597c8c5ab ("net/rds: Track user mapped pages through
special API")`
- `Assisted-by: Claude-Code:claude-fable-5`
Record: Author is the RDS contributor who submitted a 4-patch bug-fix
series. Netdev maintainer merged it. Original submission carried a
`Fixes:` tag pointing at the 2020 pin-API conversion. Absence of `Cc:
stable` on the candidate is expected.
**Step 1.3: ANALYZE THE COMMIT BODY TEXT**
Record:
- **Bug:** `__rds_rdma_map()` pins pages with `rds_pin_pages()` →
`pin_user_pages_fast()`. Teardown in `__rds_ib_teardown_mr()` does
`set_page_dirty()` + `put_page()`. `put_page()` drops one ref; a
FOLL_PIN pin adds `GUP_PIN_COUNTING_BIAS` (1024). Remaining refs are
stranded.
- **Symptom:** Userspace-triggerable unreclaimable page leak, up to
`RDS_MAX_MSG_SIZE` (1 MiB) per `RDS_GET_MR` / `RDS_GET_MR_FOR_DEST`.
Same leak on every MR reuse/flush, not only the ioctl.
- **Root cause:** Pin-API conversion updated `net/rds/rdma.c` unpin
sites and missed the IB transport teardown path.
- **Secondary fix:** `unpin_user_pages_dirty_lock()` dirties under the
page/folio lock, closing the truncation race of bare
`set_page_dirty()`.
- **Sleeping:** Author claims every caller of `__rds_ib_teardown_mr()`
is process context (syscall reuse path, or pool flush under
`flush_lock` mutex which already `wait_event`s in
`rds_ib_post_inv()`).
- **Version info:** Bug introduced by the 2020 pin conversion
(`0d4597c8c5ab`).
**Step 1.4: DETECT HIDDEN BUG FIXES**
Record: Not hidden. Explicit memory-leak fix with a clear pin vs put
mismatch. Not cleanup/refactor.
---
## PHASE 2: DIFF ANALYSIS
**Step 2.1: INVENTORY THE CHANGES**
Record:
- File: `net/rds/ib_rdma.c` only (−3 / +1)
- Function: `__rds_ib_teardown_mr()`
- Scope: single-file surgical fix
**Step 2.2: UNDERSTAND THE CODE FLOW CHANGE**
Record: In the scatterlist-release loop, before: `WARN_ON(!page->mapping
&& irqs_disabled()); set_page_dirty(page); put_page(page);`. After:
`unpin_user_pages_dirty_lock(&page, 1, true);`. DMA unmap,
`kfree(ibmr->sg)`, and pool accounting in `rds_ib_teardown_mr()` are
unchanged. Normal teardown path, not an error-only path.
**Step 2.3: IDENTIFY THE BUG MECHANISM**
Record:
- **Category:** reference-counting / resource leak (FOLL_PIN bias not
removed), plus initialization/dirtying correctness
(`set_page_dirty_lock` vs `set_page_dirty`)
- **Mechanism:** `pin_user_pages_fast()` sets `FOLL_PIN` and bumps
refcount by `GUP_PIN_COUNTING_BIAS`. `put_page()` subtracts 1, leaving
1023 refs. Pages never return to the allocator. `unpin_user_page()` /
`unpin_user_pages_dirty_lock()` call `put_compound_head(...,
FOLL_PIN)`, which multiplies refs by the bias (or uses
`hpage_pincount_sub`) and actually releases the pin.
**Step 2.4: ASSESS THE FIX QUALITY**
Record: Obviously correct against the pin-API contract (`mm/gup.c`
documents that FOLL_PIN pages must be released via `unpin_user_page()`).
Same helper is already used in this tree in `net/rds/rdma.c` and
`net/rds/info.c`. Minimal. Regression risk is sleep-in-atomic if
teardown ever runs with IRQs off; verified callers already sleep
(`wait_event` / `mutex_lock`). The dropped `WARN_ON` was a leftover from
a 2015 `BUG_ON(irqs_disabled())` downgrade; current callers take a mutex
or run in syscall context.
---
## PHASE 3: GIT HISTORY INVESTIGATION
**Step 3.1: BLAME THE CHANGED LINES**
Record:
- `set_page_dirty` + `put_page` in this function: `08b48a1ed84b19` (Andy
Grover, 2009) — originally paired with `get_user_pages`.
- `WARN_ON(!page->mapping && irqs_disabled())`: `5c240fa2ab394a` (2015),
downgraded from `BUG_ON(irqs_disabled())` because that assertion fired
on “normal execution paths” (FMR-era). FMR support is already removed
in this tree (`07549ee21ce52`).
- Pin conversion that made `put_page` wrong: `0d4597c8c5abd`
(2020-02-16), present in this tree.
**Step 3.2: FOLLOW THE FIXES: TAG**
Record: `0d4597c8c5abdeeaf50774066c16683f30184dc8` (“net/rds: Track user
mapped pages through special API”) converted `rds_pin_pages()` to
`pin_user_pages_fast()` and updated unpin sites in `rdma.c` only
(`rds_rdma_free_op`, `rds_atomic_free_op`, error paths). It did **not**
touch `ib_rdma.c`. `git merge-base --is-ancestor 0d4597c8c5abd HEAD`
succeeded — the buggy conversion is in this 5.10 tree. Intermediate
related fix already here: `91a44b406bc1f` (“net: rds: fix MR cleanup on
copy error”) — different bug (double-unpin on `put_user` failure), not a
substitute for this leak.
**Step 3.3: CHECK FILE HISTORY FOR RELATED CHANGES**
Record: Recent `net/rds/ib_rdma.c` history is unrelated (laddr check,
FRMR reject, null deref). This is standalone vs local history. Upstream
it is v4 4/4 of Allison Henderson’s series; patches 1–3 fix different
RDS bugs (sleeping unpin from tasklet in `rdma.c`, socket UAF via MR,
send-path message leak) and are **not** prerequisites for this hunk.
**Step 3.4: CHECK THE AUTHOR'S OTHER COMMITS**
Record: Allison Henderson has many RDS commits in this tree (locking,
shutdown, zerocopy). Oracle RDS maintainer-side contributor.
`MAINTAINERS` lists Santosh Shilimkar; Allison is a long-time RDS
author. Related contemporaneous work is the rest of the v4 series, not a
dependency of this patch.
**Step 3.5: CHECK FOR DEPENDENT/PREREQUISITE COMMITS**
Record: Needs `unpin_user_pages_dirty_lock()` and
`pin_user_pages_fast()` — both exist in 5.10 (`mm/gup.c`,
`EXPORT_SYMBOL`). No new helper, no struct change. Can apply standalone.
`git apply --check` succeeded (hunk offset +4 only).
---
## PHASE 4: MAILING LIST AND EXTERNAL RESEARCH
**Step 4.1: FIND THE ORIGINAL PATCH DISCUSSION**
Record:
- Lore/thread via `b4 am` / `b4 mbox`: cover `https://patch.msgid.link/2
0260730041629.3512480-1-achender@kernel.org`
- Patch: msgid `20260730041629.3512480-5-achender@kernel.org` (`[PATCH
net v4 4/4]`)
- Openwall mirror: https://lists.openwall.net/netdev/2026/07/30/66
- Revisions: v1–v3 were a 3-patch series; **this leak fix is new in
v4**. Applied version is v4.
- Merged to `netdev/net-next.git` as `a507023e6ff1` (patchwork-bot,
2026-08-05)
- Jakub Kicinski: not a NAK — he said he lacked confidence reviewing RDS
and queued the series for **-next** rather than `net`. Allison agreed
(Fixes tags, but “not simple bugs” / “really old bugs”).
- No reviewer NAK, no request to drop patch 4, no flag that teardown can
sleep in IRQ on current code.
**Step 4.2: CHECK WHO REVIEWED THE PATCH**
Record: `b4` recipients: netdev, linux-rdma, Pabeni, Edumazet, Kicinski,
Horms; Cc John Hubbard and Leon Romanovsky (original pin-API authors).
No `Reviewed-by` landed. Kicinski merged without a deep RDS review.
Technical correctness of this hunk does not depend on that: it matches
the pin API the Cc list designed.
**Step 4.3: SEARCH FOR THE BUG REPORT**
Record: No syzbot/bugzilla. Bug is from code inspection / UEK porting.
Trigger is real (`setsockopt(RDS_GET_MR)` / MR reuse), not theoretical.
No user-count from public reports.
**Step 4.4: CHECK FOR RELATED PATCHES AND SERIES**
Record: Standalone vs 1/4–3/4. Patch 1 is about `rds_rdma_free_op()`
sleeping from the **send tasklet** — a different function already using
`unpin_user_pages_dirty_lock()` in `rdma.c`. Backporting 4/4 does not
require 1/4; it does not widen that tasklet bug.
**Step 4.5: CHECK STABLE MAILING LIST HISTORY**
Record: lore.kernel.org/stable search for this subject returned no
discussion that this fix was rejected or already taken. UNVERIFIED that
no private stable thread exists.
---
## PHASE 5: CODE SEMANTIC ANALYSIS
**Step 5.1: KEY FUNCTIONS**
Record: `__rds_ib_teardown_mr()` (changed); `rds_ib_teardown_mr()`
(wrapper); `rds_ib_map_frmr()`, `rds_ib_unreg_frmr()` (callers).
**Step 5.2: TRACE CALLERS**
Record:
- `rds_ib_map_frmr()` → `rds_ib_teardown_mr()` — reuse/remap. Called
from `rds_ib_reg_frmr()` → `rds_ib_get_mr()` → `__rds_rdma_map()` /
cmsg RDMA map. Syscall (`setsockopt` / `sendmsg`). Already
`wait_event`s in `rds_ib_post_reg_frmr()`.
- `rds_ib_unreg_frmr()` → `__rds_ib_teardown_mr()` — only from
`rds_ib_flush_mr_pool()`, which takes `pool->flush_lock` (mutex).
Callers of flush: delayed work `rds_ib_mr_pool_flush_worker`,
`rds_ib_try_reuse_ibmr` (GFP_KERNEL alloc path), `rds_ib_free_mr` only
if `!in_interrupt()`, `rds_ib_flush_mrs()` (socket close /
`RDS_FREE_MR`), `rds_ib_destroy_mr_pool()`,
`rds_ib_conn_path_shutdown()` (already `wait_event_timeout`).
Interrupt `free_mr` queues `flush_worker` instead of flushing inline.
**Step 5.3: TRACE CALLEES**
Record: After the change, the loop calls `unpin_user_pages_dirty_lock()`
→ `set_page_dirty_lock()` (can sleep) + `unpin_user_page()`. Previously
`set_page_dirty()` + `put_page()`. DMA unmap stays first.
**Step 5.4: FOLLOW THE CALL CHAIN**
Record: Reachable from userspace: `rds_setsockopt` → `RDS_GET_MR` /
`RDS_GET_MR_FOR_DEST` → `rds_get_mr()` → `__rds_rdma_map()` →
`rds_pin_pages()` → transport `get_mr`. Also RDMA cmsg on send. Requires
`CONFIG_RDS=y/m`, `CONFIG_RDS_RDMA`, InfiniBand, and `can_do_mlock()`.
Not universal, but a real syscall path. Unprivileged users typically
need `RLIMIT_MEMLOCK`/CAP_IPC_LOCK to pin; RDS/IB cluster users have
that.
**Step 5.5: SEARCH FOR SIMILAR PATTERNS**
Record: Same missed-unpin class was why `rdma.c` was converted in 2020.
Remaining `put_page` in `net/rds/message.c` is for message payload pages
(`__free_page` / zerocopy), not FOLL_PIN MR pages. IB umem/qib/hfi1
already use `unpin_user_pages_dirty_lock()`. This is the leftover RDS IB
MR site.
---
## PHASE 6: CROSS-REFERENCING AGAINST THE LOCAL TREE
**Step 6.1: DOES THE BUGGY CODE EXIST IN THIS TREE?**
Record: Yes. `net/rds/ib_rdma.c` lines 253–260 still have `WARN_ON` +
`set_page_dirty` + `put_page`. `rds_pin_pages()` already uses
`pin_user_pages_fast()` (`net/rds/rdma.c:163`). `GUP_PIN_COUNTING_BIAS`
is `(1U << 10)` in `include/linux/mm.h`. Bug present since this series
branched (v5.10 includes `0d4597c8c5ab` from Feb 2020; v5.10 release is
Dec 2020).
**Step 6.2: CHECK FOR BACKPORT COMPLICATIONS**
Record: **Clean apply** (`git apply --check`: hunk succeeded at offset
4). Surrounding function matches the mainline hunk. 5.10’s
`unpin_user_pages_dirty_lock()` uses `set_page_dirty_lock()`, not
folios; the public API and sleep/lock properties match what the patch
needs. Commit-message “folio lock” is mainline wording; behavior here is
the page lock, which is correct for 5.10.
**Step 6.3: CHECK IF RELATED FIXES ARE ALREADY HERE**
Record: No equivalent unpin in `__rds_ib_teardown_mr()`. `91a44b406bc1f`
is a different MR cleanup. `git log --grep='unpin MR pages' -- net/rds`
is empty in this tree.
---
## PHASE 7: SUBSYSTEM AND MAINTAINER CONTEXT
**Step 7.1: IDENTIFY THE SUBSYSTEM AND ITS CRITICALITY**
Record: **net/rds** (Reliable Datagram Sockets), IB RDMA MR pool.
Criticality: **IMPORTANT** for RDS/IB users (Oracle RAC-style clusters);
**PERIPHERAL** for everyone else (`CONFIG_RDS_RDMA` + InfiniBand). Not
core mm/VFS, but pinned-page leaks are mm-visible (unreclaimable pages).
**Step 7.2: ASSESS SUBSYSTEM ACTIVITY**
Record: Mature. The teardown loop dates to 2009; the pin mismatch has
been wrong for the entire 5.10 lifetime. Low churn on `ib_rdma.c`
relative to netdev core.
---
## PHASE 8: IMPACT AND RISK ASSESSMENT
**Step 8.1: WHO IS AFFECTED**
Record: Driver/config-specific: `CONFIG_RDS` + `CONFIG_RDS_RDMA` + IB
hardware. TCP RDS (`CONFIG_RDS_TCP`) has no RDMA MRs and is unaffected.
**Step 8.2: TRIGGER CONDITIONS**
Record: Common for RDS RDMA: every MR map/reuse/teardown (`RDS_GET_MR`,
RDMA cmsg, pool flush). Not a rare error path. Needs mlock capability.
Not a timing race.
**Step 8.3: FAILURE MODE SEVERITY**
Record: Unreclaimable page leak (pin bias left behind). Repeated
GET_MR/reuse can pin large amounts of RAM until OOM. **Severity: HIGH**
(not a panic, but a userspace-triggerable leak of pinned pages; same
class as other GUP pin leaks taken into stable). Secondary: dirty-
without-lock truncation race on file-backed MRs (**MEDIUM** on that
subset).
**Step 8.4: RISK-BENEFIT RATIO**
Record:
- **Benefit:** High for RDS/IB; stops a leak that has existed for the
whole 5.10 life. Zero functional change for other configs.
- **Risk:** Very low — one call-site, existing exported helper, callers
already sleepable. Does not add APIs or change userspace.
- **Ratio:** Strongly favors backport.
---
## PHASE 9: FINAL SYNTHESIS
**Step 9.1: COMPILE THE EVIDENCE**
**FOR:**
- Real FOLL_PIN vs `put_page()` leak; verified in 5.10 `mm/gup.c` and
`net/rds/rdma.c`/`ib_rdma.c`
- Userspace-reachable (`RDS_GET_MR` / RDMA map)
- Fixes commit `0d4597c8c5ab` is in this tree
- 1-file, 4-line change; `unpin_user_pages_dirty_lock()` already used
elsewhere in RDS here
- Applies cleanly
- Author is RDS; merged by netdev
- Sleeping unpin is safe on verified 5.10 call chains
**AGAINST:**
- RDS/IB is not a universal path
- Kicinski did not deeply review and put the series in net-next
- No public crash/syzbot report
- Series mates 1–3 are separate bugs (not blockers)
**UNRESOLVED:** No stable@ thread found; WebFetch to lore HTML returned
403 (thread recovered via `b4` + openwall + curl).
**Step 9.2: STABLE RULES CHECKLIST**
1. Obviously correct and tested? **PASS** — pin-API contract; same
helper already in-tree RDS; merged to net-next
2. Fixes a real bug that affects users? **PASS** — pinned pages never
freed on MR teardown
3. Important (crash/security/corruption/deadlock)? **PASS** —
unreclaimable leak on a hot RDS RDMA path, up to 1 MiB per map plus
reuse; eventual OOM
4. Small and contained? **PASS** — one function, −3/+1
5. No new features or APIs? **PASS**
6. Can apply to the local tree? **PASS** — `git apply --check` clean;
helpers present
**Step 9.3: EXCEPTION CATEGORIES**
Record: None. Ordinary bug fix (not ID/quirk/DT/build/docs).
**Step 9.4: DECISION**
This is a small, obviously correct leak fix for a bug that has been in
this 5.10 tree since day one. It does not need the rest of the v4
series. Benefit for RDS/IB users outweighs the low regression risk.
---
### What the commit solves for this tree
`rds_pin_pages()` already pins with `pin_user_pages_fast()` (`FOLL_PIN`,
+1024 refs). IB teardown still `put_page()`s once, so every MR teardown
permanently leaks those pages. Replacing that with
`unpin_user_pages_dirty_lock()` restores the pin/unpin pairing and
dirties under the page lock. That matters to 5.10 RDS-over-InfiniBand
users (clusters using `RDS_GET_MR` / RDMA ops): memory is not optional
reclaim, it is stuck until reboot.
### Risk vs benefit
Benefit is a real, repeating leak on a syscall-visible path. Risk is a
one-line helper swap on paths that already take a mutex and
`wait_event`. The patch applies without rework.
---
### Verification
- [Phase 1] Parsed candidate tags: Allison Henderson SOB, msgid.link,
Kicinski SOB; lore copy also has `Fixes: 0d4597c8c5ab`
- [Phase 2] Diff: `__rds_ib_teardown_mr()` in `net/rds/ib_rdma.c`,
replace WARN/`set_page_dirty`/`put_page` with
`unpin_user_pages_dirty_lock(&page, 1, true)`
- [Phase 3] `git describe`/Makefile: local tree **5.10.270**, branch
`pending-5.10`
- [Phase 3] `git blame`: put_page since 2009; WARN_ON from
`5c240fa2ab394a` (2015)
- [Phase 3] `git show 0d4597c8c5abd`: pin conversion of `rdma.c` only;
`git merge-base --is-ancestor` → in HEAD
- [Phase 3] `git show dbfe7d74376e1`: only `info.c`, not this leak
- [Phase 3] Read `mm/gup.c`: `pin_user_pages_fast()` sets `FOLL_PIN`;
`unpin_user_pages_dirty_lock()` / `put_compound_head(..., FOLL_PIN)`
apply `GUP_PIN_COUNTING_BIAS` (1024)
- [Phase 3] Read local `net/rds/rdma.c` `rds_pin_pages()` and
`ib_rdma.c` teardown: mismatch confirmed
- [Phase 4] `b4 am`/`b4 mbox`: v4 4/4 series; merged as `a507023e6ff1`;
Kicinski queued for net-next; no NAK
- [Phase 4] Openwall https://lists.openwall.net/netdev/2026/07/30/66
matches the diff
- [Phase 4] lore HTML WebFetch: 403; thread recovered via b4
- [Phase 5] Callers: `rds_ib_map_frmr` (syscall), `rds_ib_unreg_frmr`
via `rds_ib_flush_mr_pool` (mutex + workqueue); `rds_ib_free_mr`
defers flush when `in_interrupt()`
- [Phase 5] `RDS_MAX_MSG_SIZE` = 1 MiB in `net/rds/rds.h`
- [Phase 6] Buggy code present at `ib_rdma.c:258-260`; `git apply
--check` succeeded
- [Phase 6] `unpin_user_pages_dirty_lock` already used in
`net/rds/rdma.c` and `net/rds/info.c`
- [Phase 7] `MAINTAINERS`: RDS supported (Santosh Shilimkar / Oracle)
- [Phase 8] Failure mode: unreclaimable FOLL_PIN leak, HIGH for RDS_RDMA
- UNVERIFIED: no public syzbot/user report; no stable@ thread found
**YES**
net/rds/ib_rdma.c | 4 +---
1 file changed, 1 insertion(+), 3 deletions(-)
diff --git a/net/rds/ib_rdma.c b/net/rds/ib_rdma.c
index 751755cd069e0..36fd8dedbce56 100644
--- a/net/rds/ib_rdma.c
+++ b/net/rds/ib_rdma.c
@@ -251,9 +251,7 @@ void __rds_ib_teardown_mr(struct rds_ib_mr *ibmr)
/* FIXME we need a way to tell a r/w MR
* from a r/o MR */
- WARN_ON(!page->mapping && irqs_disabled());
- set_page_dirty(page);
- put_page(page);
+ unpin_user_pages_dirty_lock(&page, 1, true);
}
kfree(ibmr->sg);
--
2.53.0
^ permalink raw reply related [flat|nested] 32+ messages in thread* [PATCH AUTOSEL 6.18] netpoll: do not warn when the best-effort pool refill fails
2026-10-19 13:44 [PATCH AUTOSEL 6.18-6.12] net: dsa: mt7530: check CORE_PLL_GROUP4 access in mt7531_setup() Sasha Levin
` (10 preceding siblings ...)
2026-10-19 13:47 ` [PATCH AUTOSEL 6.18-5.10] net/rds: unpin MR pages with unpin_user_pages_dirty_lock() Sasha Levin
@ 2026-10-19 13:47 ` Sasha Levin
2026-10-19 13:48 ` [PATCH AUTOSEL 6.18] net: libwx: disable TX VLAN offload for packets with >2 VLAN tags Sasha Levin
` (17 subsequent siblings)
29 siblings, 0 replies; 32+ messages in thread
From: Sasha Levin @ 2026-10-19 13:47 UTC (permalink / raw)
To: patches, stable
Cc: Breno Leitao, Jakub Kicinski, Sasha Levin, davem, edumazet,
pabeni, netdev, linux-kernel
From: Breno Leitao <leitao@debian.org>
[ Upstream commit 84c0ff1efb62b0053aa265b8deb13842f68f1a74 ]
refill_skbs() tops up the per-netpoll skb pool with GFP_ATOMIC and
simply stops on the first allocation failure, leaving the pool partially
filled; a later refill tops it up once memory frees up. The allocation
failing is therefore an expected and fully handled condition, but
without __GFP_NOWARN the page allocator emits a warn_alloc() splat with
a full stack trace on every miss.
Pass __GFP_NOWARN so the best-effort refill stays quiet, mirroring the
same change in netconsole's find_skb().
Signed-off-by: Breno Leitao <leitao@debian.org>
Link: https://patch.msgid.link/20260629-netpoll_no_warn-v1-2-f380f0b2cd0c@debian.org
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
LLM Generated explanations, may be completely bogus:
The local tree is **6.18.y** (`v6.18.53`). At that tag, `refill_skbs()`
still allocates with plain `GFP_ATOMIC`, so the bug exists here. This is
a one-line `__GFP_NOWARN` change on a best-effort refill that already
treats allocation failure as success-with-partial-fill.
===============================================================
PHASE 1: COMMIT MESSAGE FORENSICS
===============================================================
Step 1.1: PARSE THE SUBJECT LINE
Record: **[netpoll] [do not warn] [silence warn_alloc on expected
GFP_ATOMIC skb-pool refill failure]**
Step 1.2: PARSE ALL COMMIT MESSAGE TAGS
Record:
- **Fixes:** none (expected for this review pipeline)
- **Reported-by / Tested-by / Reviewed-by / Acked-by / Cc: stable:**
none
- **Link:** https://patch.msgid.link/20260629-netpoll_no_warn-v1-2-
f380f0b2cd0c@debian.org (netdev series, patch 2/2)
- **Signed-off-by:** Breno Leitao (author; NETCONSOLE maintainer); Jakub
Kicinski (netdev maintainer)
- Ignored pipeline tags: `[Upstream commit 84c0ff1efb62…]`, `Signed-off-
by: Sasha Levin`
Step 1.3: ANALYZE THE COMMIT BODY TEXT
Record:
- **Bug:** `refill_skbs()` tops up a per-netpoll pool of 32 skbs with
`GFP_ATOMIC`. Failure is expected: the loop `break`s, leaving a
partial pool, and a later refill tries again.
- **Symptom:** without `__GFP_NOWARN`, the page allocator still fires
`warn_alloc()` (`pr_warn` + `dump_stack()` + `__show_mem()`), a large
splat on every miss.
- **Root cause:** best-effort allocation used a GFP mask that still
warns.
- **Version info:** none in the commit text. Author date 2026-06-29;
merged to net-next 2026-06-30.
- Cover letter (series 0/2, verified in the mbox): this class of splat
was seen on the **Meta fleet**. `warn_alloc` emits 100+ printk lines
which then go out through netconsole, **consuming the scarce skb
pool** and crowding out the real diagnostic. Stack in the cover letter
is the send path (`__alloc_skb` → `send_udp` → `netconsole_write` →
`nbcon_kthread_func`), i.e. the sibling `find_skb()` site; this commit
is the matching refill-path silence.
Step 1.4: DETECT HIDDEN BUG FIXES
Record: Not a disguised cleanup. It is explicitly a warning-suppression
for an expected, already-handled allocation failure. The functional
extra is that those warnings themselves eat the emergency pool they
exist to protect.
===============================================================
PHASE 2: DIFF ANALYSIS - LINE BY LINE
===============================================================
Step 2.1: INVENTORY THE CHANGES
Record:
- **File:** `net/core/netpoll.c` — 1 insertion, 1 deletion
- **Function:** `refill_skbs()`
- **Scope:** single-file surgical one-liner
Step 2.2: UNDERSTAND THE CODE FLOW CHANGE
Record:
- **Before:** `alloc_skb(MAX_SKB_SIZE, GFP_ATOMIC)`; on NULL, `break`
(partial fill).
- **After:** same, with `GFP_ATOMIC | __GFP_NOWARN`.
- Path: best-effort refill from `__netpoll_setup()` and from
`refill_skbs_work_handler()` (workqueue scheduled when `find_skb()`
dequeues from the pool). Not a new error path; allocation behavior is
unchanged.
Step 2.3: IDENTIFY THE BUG MECHANISM
Record:
- **Category:** logic/correctness of GFP flags (expected GFP_ATOMIC
failure must not warn). Secondary effect: resource starvation of the
32-skb emergency pool via printk/netconsole.
- **Mechanism:** `warn_alloc()` in `mm/page_alloc.c` returns immediately
if `__GFP_NOWARN` is set; otherwise it rate-limits to 1/10s but still
dumps stack + meminfo. Those printks are exactly what the pool is
supposed to carry under OOM.
Step 2.4: ASSESS THE FIX QUALITY
Record:
- Obviously correct: `__GFP_NOWARN` is the established pattern for best-
effort `GFP_ATOMIC` (many net/ core users already do this).
- Minimal: one GFP flag bit.
- **Regression risk: very low.** Allocation success/failure, pool
length, locking, and refill scheduling are unchanged. `warn_alloc`
does **not** use `WARN_ON` / `panic_on_warn` (verified: it is
`pr_warn` + `dump_stack` + `warn_alloc_show_mem`).
- Red flags: none on this hunk. Series was `[PATCH net-next 2/2]` (not
`net`), which is a process signal, not a code risk.
===============================================================
PHASE 3: GIT HISTORY INVESTIGATION
===============================================================
Step 3.1: BLAME THE CHANGED LINES
Record:
- `alloc_skb(..., GFP_ATOMIC)` in this refill loop dates to
**`1da177e4c3f41` (Linux-2.6.12-rc2)** — present for the entire git
history.
- Per-target pool: `221a9c1df790fa` (“Individualize the skb pool”,
2024-11-14) — **in v6.18.53**.
- Refill moved off the spinlock: `327c20c21d80e0` (“Fix deadlock in
memory allocation under spinlock”, 2025-11-03, Fixes:
`248f6571fd4c51`) — **in v6.18.53**. That commit even notes `printk`
from OOM-in-refill can re-enter netpoll; after it, allocation is
outside the lock, so this NOWARN change does not fight a deadlock, it
only stops the splat.
Step 3.2: FOLLOW THE FIXES: TAG
Record: No `Fixes:` tag. Not applicable. The GFP_ATOMIC refill is
original netpoll behavior, not a recent regression.
Step 3.3: CHECK FILE HISTORY FOR RELATED CHANGES
Record (on `v6.18.53`, `-- net/core/netpoll.c`):
- Recent real fixes already in this tree: UAF on shutdown, IPv6 local-
address corruption, MAC truncation, workqueue init order, refcount
cleanup, the refill deadlock fix.
- **This NOWARN change is not in v6.18.53.**
- Standalone vs series: **patch 2/2**. Sibling 1/2 is `09f7a613a14fd`
“netconsole: do not warn when the best-effort skb allocation fails”
(touches `drivers/net/netconsole.c` `find_skb()` **after** that
function was moved out of netpoll). No code dependency between the two
hunks.
Step 3.4: CHECK THE AUTHOR'S OTHER COMMITS
Record: Breno Leitao is listed as **NETCONSOLE** maintainer in
`MAINTAINERS`. He authored the per-target pool and the refill-deadlock
fix. High-confidence author for this code.
Step 3.5: CHECK FOR DEPENDENT/PREREQUISITE COMMITS
Record:
- Needs the existing `refill_skbs()` + `skb_pool` (present since 2.6 /
individualized in 6.18).
- Does **not** need patch 1/2 to compile or run.
- Cover letter mentioned “reschedule the workqueue to try later”; **that
was not in either committed patch**. Current code already
`schedule_work(&np->refill_wq)` from `find_skb()` on pool dequeue. No
missing follow-up required for this hunk.
- **Standalone; can apply on this tree.**
===============================================================
PHASE 4: MAILING LIST AND EXTERNAL RESEARCH
===============================================================
Step 4.1: FIND THE ORIGINAL PATCH DISCUSSION
Record:
- `b4 dig -c 84c0ff1efb62b`: https://patch.msgid.link/20260629-
netpoll_no_warn-v1-2-f380f0b2cd0c@debian.org
- `b4 dig -a`: **v1 only** (no v2/v3).
- Mbox (`/tmp/netpoll-nowarn.mbox`): cover letter + 1/2 + 2/2 +
patchwork “applied to netdev/net-next.git” by Jakub Kicinski. **No
NAK, no reviewer comments, no Cc: stable in the thread.**
- Submitted as **`[PATCH net-next]`**, not `net`. Merged immediately (29
Jun → applied 1 Jul 2026).
Step 4.2: CHECK WHO REVIEWED THE PATCH
Record (`b4 dig -w`): To/Cc netdev maintainers (Kicinski, Dumazet,
Miller, Abeni, Horman, Lunn), `netdev@`, `linux-kernel@`, `kernel-
team@meta.com`. No extra Reviewed-by on the git commit; maintainer merge
is the review signal.
Step 4.3: SEARCH FOR THE BUG REPORT
Record: No bugzilla/syzbot. Evidence is the cover letter: Meta fleet,
`page allocation failure: order:0, mode:0x40820(GFP_ATOMIC|__GFP_COMP)`,
stack through netconsole send. That stack is the **`find_skb`/send_udp**
site, not `refill_skbs` itself.
Step 4.4: CHECK FOR RELATED PATCHES AND SERIES
Record: Two independent one-liners. In **this** 6.18 tree, `find_skb()`
is still in `net/core/netpoll.c:279` (`alloc_skb(len, GFP_ATOMIC)`
without NOWARN). Patch 1/2 would not apply as written (wrong file). This
2/2 patch does not fix that hot path; it only silences the async refill.
Step 4.5: CHECK STABLE MAILING LIST HISTORY
Record: **UNVERIFIED** — `lore.kernel.org/stable/` and WebFetch both
returned 403.
===============================================================
PHASE 5: CODE SEMANTIC ANALYSIS
===============================================================
Step 5.1: IDENTIFY KEY FUNCTIONS IN THE DIFF
Record: `refill_skbs()` only.
Step 5.2: TRACE CALLERS
Record (verified in this tree):
- `refill_skbs_work_handler()` → process-context workqueue, scheduled
from `find_skb()` after `skb_dequeue(&np->skb_pool)`.
- `__netpoll_setup()` → setup/open path (netconsole `netpoll_setup()`,
plus bond/team/macvlan/vlan/bridge/dsa netpoll setup).
Step 5.3: TRACE CALLEES
Record: `alloc_skb()` → `skb_queue_tail()`. On failure, `break`. No new
locks, frees, or I/O.
Step 5.4: FOLLOW THE CALL CHAIN
Record:
- User-reachable via **netconsole** (module/`netconsole=`), kgdb-style
netpoll users, and stacked devices’ `ndo_netpoll_setup`.
- Typical trigger: memory pressure while netconsole is logging →
`find_skb()` falls back to the pool → `schedule_work(refill_wq)` →
`refill_skbs()` `GFP_ATOMIC` fails → `warn_alloc` splat → those lines
go through netconsole again.
- Unprivileged userspace cannot generally fire this unless netconsole is
enabled; operators enable it for crash/OOM debugging — exactly when
GFP_ATOMIC fails.
Step 5.5: SEARCH FOR SIMILAR PATTERNS
Record: Hot-path twin still present here: `find_skb()` at
`net/core/netpoll.c:279`. Same GFP_ATOMIC-without-NOWARN. Many other
net/ callers already use `GFP_ATOMIC | __GFP_NOWARN`.
===============================================================
PHASE 6: CROSS-REFERENCING AGAINST THE LOCAL TREE
===============================================================
Step 6.1: DOES THE BUGGY CODE EXIST IN THIS TREE?
Record: **Yes.** `git describe HEAD` → `v6.18.53-5049-g0f0bdc2b0dffb`;
`make kernelversion` → **6.18.53**.
At `v6.18.53:net/core/netpoll.c`:
```
skb = alloc_skb(MAX_SKB_SIZE, GFP_ATOMIC);
```
`git merge-base --is-ancestor 84c0ff1efb62b v6.18.53` → **not in
v6.18.53**.
Step 6.2: CHECK FOR BACKPORT COMPLICATIONS
Record: `git show 84c0ff1efb62b -- net/core/netpoll.c | git apply
--check` on **v6.18.53** → **CLEAN_APPLY**. Surrounding `refill_skbs()`
matches. No refactor needed.
Step 6.3: CHECK IF RELATED FIXES ARE ALREADY HERE
Record: No NOWARN/`best-effort` commits on these files in `v6.18.53`
history. Deadlock fix is already here and complementary, not a
substitute.
===============================================================
PHASE 7: SUBSYSTEM AND MAINTAINER CONTEXT
===============================================================
Step 7.1: IDENTIFY THE SUBSYSTEM AND ITS CRITICALITY
Record: **net/core netpoll** used by netconsole and stacked netdevs.
**IMPORTANT** (not core mm/scheduler; not obscure staging). Netconsole
is the production debug path for machines that cannot use a serial
console.
Step 7.2: ASSESS SUBSYSTEM ACTIVITY
Record: Actively maintained by Breno; several 6.18-era netpoll fixes
already in this tree. Mature mechanism (skb pool since 2.6).
===============================================================
PHASE 8: IMPACT AND RISK ASSESSMENT
===============================================================
Step 8.1: DETERMINE WHO IS AFFECTED
Record: **Config/driver-specific** — `CONFIG_NETPOLL` / netconsole /
bonding/team/vlan/macvlan/bridge/dsa netpoll. Not universal, but
netconsole is widely used in production fleets (explicit Meta report).
Step 8.2: DETERMINE THE TRIGGER CONDITIONS
Record: GFP_ATOMIC miss while the pool is not full — setup under
pressure, or workqueue refill after the pool was used. Common under
OOM/memory pressure with netconsole on. Not a userspace-exploitable
crash.
Step 8.3: DETERMINE THE FAILURE MODE SEVERITY
Record:
- Direct: `warn_alloc` log flood (stack + `show_mem`), rate-limited
1/10s, **not** `panic_on_warn`.
- Indirect (the reason the pool exists: “make sure the message gets out
even in extreme OOM”): splat printks consume the 32-skb pool and can
hide the original incident.
- **Severity: HIGH for netconsole-under-OOM (lost emergency logs);
otherwise MEDIUM log noise.** The refill path is the lesser of the two
sites; the cover-letter stack is `find_skb`.
Step 8.4: CALCULATE RISK-BENEFIT RATIO
Record:
- **Benefit:** medium-high for netconsole users; stops expected refill
failures from dumping meminfo into the emergency path.
- **Risk:** very low (one GFP bit).
- **Ratio:** favorable. Incomplete vs the hot-path sibling, but this
hunk is still a correct, self-contained fix.
===============================================================
PHASE 9: FINAL SYNTHESIS
===============================================================
Step 9.1: COMPILE THE EVIDENCE
**FOR:**
- Buggy `GFP_ATOMIC` refill is in this 6.18.53 tree and applies cleanly.
- Failure is already handled; the splat is wrong and can starve the
32-skb OOM pool.
- Production class of bug documented (Meta fleet cover letter).
- 1 line, established `__GFP_NOWARN` pattern, maintainer-authored,
merged by Kicinski.
- Zero change to allocation success/locking/API.
**AGAINST:**
- Sent to **net-next**, not `net`; no `Fixes:` / `Cc: stable` /
Reviewed-by in the thread.
- Cover-letter stack is **`find_skb`/send_udp**, not `refill_skbs`. This
tree still warns in `find_skb()`; backporting only 2/2 does not close
the reported hot path.
- `warn_alloc` is rate-limited and does not panic.
- Not a crash/UAF/deadlock by itself.
**UNRESOLVED:** stable@ lore search (HTTP 403). Whether 6.18 netconsole
uses the same nbcon kthread as the 2026 stack (send path still is
`netconsole → netpoll_send_udp → find_skb` here).
Step 9.2: APPLY THE STABLE RULES CHECKLIST
1. Obviously correct and tested? **PASS** — one GFP flag; merged to net-
next; fleet report for the series.
2. Fixes a real bug that affects users? **PASS** — expected alloc
failure must not dump; dump can eat the emergency pool.
3. Important issue (crash, security, corruption, deadlock)? **PASS**
(narrow) — not a panic, but it undermines netpoll’s OOM delivery
guarantee. That is the point of this subsystem.
4. Small and contained? **PASS** — 1 line, one function.
5. No new features or APIs? **PASS**.
6. Can apply to the local tree? **PASS** — clean on v6.18.53.
Step 9.3: CHECK FOR EXCEPTION CATEGORIES
Record: None (not an ID/quirk/DT/build/docs add). Ordinary small bugfix.
Step 9.4: MAKE YOUR DECISION
Backport. The local 6.18.y tree has the buggy refill; the patch is a
zero-risk one-liner that stops an expected GFP_ATOMIC miss from
generating a huge `warn_alloc` that can drain the netconsole skb pool.
The hotter sibling (`find_skb` in `net/core/netpoll.c` on this tree) is
a separate candidate and is not required for this hunk to be correct.
What it solves for stable users: under memory pressure with
netconsole/netpoll, pool refill already gives up on the first
`alloc_skb` failure. Today that still prints `warn_alloc` (stack +
meminfo). Those lines compete for the 32 preallocated skbs that exist
specifically so messages still get out in OOM. Adding `__GFP_NOWARN`
leaves refill semantics identical and keeps that path quiet.
Risk vs benefit: benefit is real for netconsole fleets; risk is
negligible. The only caveat is that this does not silence `find_skb()`
on 6.18 (still `GFP_ATOMIC` without NOWARN); that does not make this
commit wrong or unsafe.
===============================================================
Verification:
===============================================================
- [Phase 1] Parsed tags: Link to msgid `…v1-2…`; SOBs Breno + Kicinski;
no Fixes/Reported-by/Cc:stable
- [Phase 1] Cover letter from mbox: Meta fleet, 100+ line splat
consuming skb pool
- [Phase 2] Diff: 1 line `GFP_ATOMIC` → `GFP_ATOMIC | __GFP_NOWARN` in
`refill_skbs()`
- [Phase 2] `warn_alloc()` confirmed: respects `__GFP_NOWARN`;
`pr_warn`+`dump_stack`+`__show_mem`; ratelimit 1/10s; not
`panic_on_warn`
- [Phase 3] Blame: GFP_ATOMIC refill since `1da177e4c3f41` (2.6.12-rc2);
individualize `221a9c1df790fa` in 6.18.53; deadlock fix
`327c20c21d80e0` in 6.18.53
- [Phase 3] `git merge-base --is-ancestor 84c0ff1efb62b v6.18.53` → not
present
- [Phase 3] Series sibling `09f7a613a14fd` is netconsole `find_skb`
post-move; not a compile dependency
- [Phase 4] `b4 dig -c 84c0ff1efb62b`: lore msgid; v1 only; applied to
net-next; no NAK
- [Phase 4] Recipients: netdev maintainers + kernel-team@meta.com
- [Phase 4] UNVERIFIED: stable@ mailing list (HTTP 403)
- [Phase 5] Callers: `__netpoll_setup()` and
`refill_skbs_work_handler()`; `find_skb()` still `GFP_ATOMIC` at line
279 in this tree
- [Phase 5] `MAX_SKBS` is 32; comment documents OOM pool purpose
- [Phase 6] `git describe HEAD` / `make kernelversion`: 6.18.53; buggy
line present at v6.18.53; `git apply --check` on v6.18.53: CLEAN_APPLY
- [Phase 7] Breno is NETCONSOLE maintainer
- [Phase 8] Failure mode: warn_alloc noise + emergency pool depletion
under OOM, not a kernel oops
- UNVERIFIED: whether 6.18 netconsole uses nbcon kthread identically to
the 2026 cover-letter stack (send path is still `netpoll_send_udp` →
`find_skb` here)
**YES**
net/core/netpoll.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/net/core/netpoll.c b/net/core/netpoll.c
index 9ef344a6932f9..a22de992c8024 100644
--- a/net/core/netpoll.c
+++ b/net/core/netpoll.c
@@ -232,7 +232,7 @@ static void refill_skbs(struct netpoll *np)
skb_pool = &np->skb_pool;
while (READ_ONCE(skb_pool->qlen) < MAX_SKBS) {
- skb = alloc_skb(MAX_SKB_SIZE, GFP_ATOMIC);
+ skb = alloc_skb(MAX_SKB_SIZE, GFP_ATOMIC | __GFP_NOWARN);
if (!skb)
break;
--
2.53.0
^ permalink raw reply related [flat|nested] 32+ messages in thread* [PATCH AUTOSEL 6.18] net: libwx: disable TX VLAN offload for packets with >2 VLAN tags
2026-10-19 13:44 [PATCH AUTOSEL 6.18-6.12] net: dsa: mt7530: check CORE_PLL_GROUP4 access in mt7531_setup() Sasha Levin
` (11 preceding siblings ...)
2026-10-19 13:47 ` [PATCH AUTOSEL 6.18] netpoll: do not warn when the best-effort pool refill fails Sasha Levin
@ 2026-10-19 13:48 ` Sasha Levin
2026-10-19 13:48 ` [PATCH AUTOSEL 6.18-6.6] net: sfp: add quirk for HORACO copper SFP+ module Sasha Levin
` (16 subsequent siblings)
29 siblings, 0 replies; 32+ messages in thread
From: Sasha Levin @ 2026-10-19 13:48 UTC (permalink / raw)
To: patches, stable
Cc: Jiawen Wu, Jakub Kicinski, Sasha Levin, mengyuanlou,
andrew+netdev, davem, edumazet, pabeni, netdev, linux-kernel
From: Jiawen Wu <jiawenwu@trustnetic.com>
[ Upstream commit f5cfb576ce39ba5647024c6d6eeb5b7a822fab6e ]
The current hardware does not support TX VLAN offload for packets with
three or more VLAN tags. When such packets are transmitted with hardware
VLAN offload enabled, the hardware may malfunction or produce corrupted
frames.
Add a check in wx_features_check() to parse the VLAN depth of the
skb. If more than two VLAN tags are detected (including both the
hardware tag and in-band tags), strip NETIF_F_HW_VLAN_CTAG_TX and
NETIF_F_HW_VLAN_STAG_TX from the feature set. This forces the
kernel networking stack to handle VLAN insertion in software for
these specific packets, ensuring correct transmission.
Signed-off-by: Jiawen Wu <jiawenwu@trustnetic.com>
Link: https://patch.msgid.link/069DF89AA8029189+20260713060441.276612-1-jiawenwu@trustnetic.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
LLM Generated explanations, may be completely bogus:
The candidate is a hardware-limitation workaround in Wangxun `libwx`: TX
VLAN offload is unsafe for more than two tags and can corrupt frames.
This 7.3-rc4 tree already has `wx_features_check` and the VLAN TX path,
so the rest of the write-up is whether that fix belongs here as a stable
backport.
## PHASE 1: COMMIT MESSAGE FORENSICS
**Step 1.1: PARSE THE SUBJECT LINE**
- Record: `[net/libwx] [disable] disable TX VLAN offload for packets
with >2 VLAN tags`
- Subsystem: `net: libwx` (Wangxun shared NIC library used by
txgbe/ngbe)
- Verb `disable` is a hardware-workaround action, not a feature or
cleanup
**Step 1.2: PARSE ALL COMMIT MESSAGE TAGS**
- Record:
- `Signed-off-by: Jiawen Wu <jiawenwu@trustnetic.com>` (author;
Wangxun maintainer)
- `Link: https://patch.msgid.link/069DF89AA8029189+20260713060441.2766
12-1-jiawenwu@trustnetic.com`
- `Signed-off-by: Jakub Kicinski <kuba@kernel.org>` (netdev
maintainer, applied)
- No `Fixes:` tag (expected for this review pipeline)
- No `Cc: stable@vger.kernel.org`
- No `Reported-by:`, `Tested-by:`, `Reviewed-by:`, `Acked-by:`
- Ignore any pipeline `Signed-off-by: Sasha Levin` / `[Upstream commit
...]` (none in the real commit)
**Step 1.3: ANALYZE THE COMMIT BODY TEXT**
- Record:
- **Bug:** Wangxun TX VLAN offload does not support three or more VLAN
tags
- **Symptom / failure mode:** hardware may malfunction or produce
**corrupted frames**
- **Root cause (author):** HW offload still used on >2-tag packets
- **Fix:** in `wx_features_check()`, count hwaccel + in-band VLAN
tags; if `vlan_num > 2`, drop `NETIF_F_HW_VLAN_CTAG_TX` and
`NETIF_F_HW_VLAN_STAG_TX` so the stack inserts in software
- **Version info in message:** none
**Step 1.4: DETECT HIDDEN BUG FIXES**
- Record: Not a disguised cleanup. This is an explicit hardware
workaround for incorrect TX offload. Intel reviewer Jacob Keller
treated it as a bug fix that should have gone to `net` with a `Fixes:`
tag (see Phase 4).
## PHASE 2: DIFF ANALYSIS - LINE BY LINE
**Step 2.1: INVENTORY THE CHANGES**
- Record:
- File: `drivers/net/ethernet/wangxun/libwx/wx_lib.c` — **+24 / -0**
- Function: `wx_features_check()`
- Scope: single-file surgical addition inside an existing
`ndo_features_check` hook
**Step 2.2: UNDERSTAND THE CODE FLOW CHANGE**
- Record:
- **Before:** `wx_features_check()` only restricted checksum/GSO for
encapsulated packets; VLAN TX offload was left on for every packet
- **After:** count `skb_vlan_tag_present()` plus in-band
`eth_type_vlan()` headers via `skb_header_pointer()`. If more than
two tags, clear HW VLAN TX features and break
- Path: per-packet TX feature check (`netif_skb_features()` →
`ndo_features_check`). Normal TX path, not probe/error-only
**Step 2.3: IDENTIFY THE BUG MECHANISM**
- Record:
- Category: **hardware workaround / logic correctness** (not
UAF/leak/race)
- Mechanism: HW can insert/offload at most two VLAN tags. With a third
tag and HW VLAN TX still enabled, the NIC may emit corrupted frames
or malfunction
- How the fix works: `validate_xmit_vlan()` in `net/core/dev.c` sees
VLAN TX offload cleared and calls `__vlan_hwaccel_push_inside()`, so
the extra tag is inserted in software
**Step 2.4: ASSESS THE FIX QUALITY**
- Record:
- Obviously correct: this is the standard `ndo_features_check`
pattern; `skb_header_pointer()` is NULL-safe; loop stops at >2 tags
- v1 skipped the in-band walk when no hwaccel tag; v2 (applied) always
walks in-band tags — better
- Minimal, no API/data-structure change
- Regression risk: **low**. Extra work only while
`eth_type_vlan(skb->protocol)` is true. Worst case is extra software
VLAN insertion, not a new crash path
- Residual gap (not a reason to reject): VF drivers
(`txgbevf`/`ngbevf`) use `wx_xmit_frame` but do **not** set
`ndo_features_check`, so VFs are not covered
## PHASE 3: GIT HISTORY INVESTIGATION
**Step 3.1: BLAME THE CHANGED LINES**
- Record:
- New lines are entirely from `f5cfb576ce39b` (this commit)
- Surrounding `wx_features_check()` introduced by `3b05aa997c491`
(“net: wangxun: restrict feature flags for tunnel packets”,
2025-04-22)
- TX VLAN offload itself: `3403960cdf86c` (“net: wangxun: libwx add tx
offload functions”, 2023-05-31, in v6.5-rc1)
**Step 3.2: FOLLOW THE FIXES: TAG**
- Record: No `Fixes:` tag. Author told Jacob Keller they could not
identify one. The limitation dates to original TX VLAN offload
(`3403960cdf86c`), not a later regression. That commit **is** an
ancestor of this tree.
**Step 3.3: CHECK FILE HISTORY FOR RELATED CHANGES**
- Record:
- Immediate follow-up: `732ed8f75ce58` “net: libwx: fix FDIR ATR queue
mismatch for software VLAN packets” — sets `WX_TX_FLAGS_SW_VLAN`
when VLAN is in-band; has its own `Fixes: b501d261a5b3`. Related
when more packets take the SW VLAN path, but standalone
- Not labeled patch X/Y. Standalone 24-line addition
- Prerequisite in this tree: `wx_features_check()` exists
(`3b05aa997c491` is an ancestor)
**Step 3.4: CHECK THE AUTHOR'S OTHER COMMITS**
- Record: Jiawen Wu is the Wangxun maintainer (`MAINTAINERS`: `M: Jiawen
Wu <jiawenwu@trustnetic.com>`). Regular libwx/txgbe/ngbe fixes around
the same time.
**Step 3.5: CHECK FOR DEPENDENT/PREREQUISITE COMMITS**
- Record: Depends on `wx_features_check()` / `ndo_features_check`
(`3b05aa997c491`). Both exist here. Helpers used (`eth_type_vlan`,
`skb_header_pointer`, `skb_vlan_tag_present`) are long-standing core
APIs. Can apply standalone on this tree.
## PHASE 4: MAILING LIST AND EXTERNAL RESEARCH
**Step 4.1: FIND THE ORIGINAL PATCH DISCUSSION**
- Record:
- `b4 dig -c f5cfb576ce39b`: [PATCH net-next v2] https://patch.msgid.l
ink/069DF89AA8029189+20260713060441.276612-1-jiawenwu@trustnetic.com
- `b4 dig -a`: v1 (2026-07-10) → v2 (2026-07-13, applied)
- v1 review (Przemek Kitszel): style/logic nits — don’t skip the loop
when no hwaccel tag; don’t use a dummy parse depth of 8; move the
`vlan_num > 2` test inside the loop. All addressed in v2
- v2 review (Jacob Keller): **asked why this went to net-next instead
of net with a Fixes: tag to the commit that introduced VLAN
tagging**
- Author reply: “Probably because I couldn't find a Fixes tag for
it...”
- Applied to **netdev/net-next.git** by Jakub Kicinski (patchwork bot,
2026-07-22)
- No NAKs; no reviewer objection to the fix itself
- No explicit `Cc: stable` in the thread (Jacob’s question is the
stable-relevant signal)
**Step 4.2: CHECK WHO REVIEWED THE PATCH**
- Record (`b4 dig -w`): netdev maintainers (Kicinski, Abeni, Dumazet,
Miller, Horman, Lunn), Wangxun (Jiawen Wu, Duanqiang Wen, Mengyuan
Lou), Intel NIC reviewers (Jacob Keller, Przemek Kitszel). Right
audience.
**Step 4.3: SEARCH FOR THE BUG REPORT**
- Record: No `Reported-by`, syzbot, or bugzilla. Vendor/maintainer
hardware-limitation report. Severity from message: corrupted frames /
hardware malfunction.
**Step 4.4: CHECK FOR RELATED PATCHES AND SERIES**
- Record: Standalone v1/v2 of one patch, not a multi-patch series.
Follow-up ATR SW-VLAN fix is separate and already in this tree.
**Step 4.5: CHECK STABLE MAILING LIST HISTORY**
- Record: **UNVERIFIED** — lore.kernel.org HTTP 403; no local stable@
discussion found. No evidence it was rejected for stable.
## PHASE 5: CODE SEMANTIC ANALYSIS
**Step 5.1: IDENTIFY KEY FUNCTIONS IN THE DIFF**
- Record: `wx_features_check()` only (exported)
**Step 5.2: TRACE CALLERS**
- Record:
- `txgbe_netdev_ops.ndo_features_check = wx_features_check`
(`txgbe_main.c`)
- `ngbe_netdev_ops.ndo_features_check = wx_features_check`
(`ngbe_main.c`)
- Core: `netif_skb_features()` → `dev->netdev_ops->ndo_features_check`
(`net/core/dev.c`)
- Called from the generic TX validation path (`validate_xmit_skb` /
`xmit_one`) — userspace-reachable on every transmit for those NICs
- VF ops use `wx_xmit_frame` but **no** `ndo_features_check` (coverage
gap)
**Step 5.3: TRACE CALLEES**
- Record: `skb_vlan_tag_present`, `eth_type_vlan`, `skb_header_pointer`
— read-only skb inspection; no alloc/lock/I/O
**Step 5.4: FOLLOW THE CALL CHAIN**
- Record: syscall/send path → qdisc → `validate_xmit_skb()` →
`netif_skb_features()` → `wx_features_check()` → if VLAN TX bits
dropped, `validate_xmit_vlan()` software-inserts the tag →
`wx_xmit_frame()`. Reachable from userspace on Wangxun PF devices.
Triple-tag is uncommon (QinQ + extra tag) but real.
**Step 5.5: SEARCH FOR SIMILAR PATTERNS**
- Record: Many NICs call `vlan_features_check()`, which **keeps** HW
VLAN TX on multi-tag skbs and only tightens csum features. Wangxun’s
extra restriction is device-specific (max two tags). Same idea as
other `ndo_features_check` HW-limit hooks, not a copy of
`vlan_features_check()`.
## PHASE 6: CROSS-REFERENCING AGAINST THE LOCAL TREE
**Step 6.1: DOES THE BUGGY CODE EXIST IN THIS TREE?**
- Record:
- Local tree: **v7.3-rc4-27-gf0100363d8c37**, `make kernelversion` =
**7.3.0-rc4**
- `wx_features_check()` exists; pre-patch body had no VLAN-depth check
(`git show f5cfb576ce39b^:...`)
- TX VLAN flags (`WX_TXD_VLE`, `skb_vlan_tag_present` in
`wx_xmit_frame`) exist since `3403960cdf86c`
- Candidate **is** already an ancestor of HEAD (already merged here).
Evaluation is still whether the fix has stable merit for this
codebase; the pre-fix function is the buggy code
**Step 6.2: CHECK FOR BACKPORT COMPLICATIONS**
- Record: **Clean apply** on this tree. Context is the existing
`wx_features_check()`; no conflicting refactor of that function
besides this commit and later unrelated wangxun work
**Step 6.3: CHECK IF RELATED FIXES ARE ALREADY HERE**
- Record: This fix is in the tree. Related ATR SW-VLAN fix
`732ed8f75ce58` is also here. No alternate fix of the >2-tag HW limit.
## PHASE 7: SUBSYSTEM AND MAINTAINER CONTEXT
**Step 7.1: IDENTIFY THE SUBSYSTEM AND ITS CRITICALITY**
- Record: `drivers/net/ethernet/wangxun/libwx` — **IMPORTANT** for
Wangxun NIC users (txgbe/ngbe PFs), **PERIPHERAL** vs core mm/net. Not
staging.
**Step 7.2: ASSESS SUBSYSTEM ACTIVITY**
- Record: Actively developed (2026 wangxun commits: Tx timeout, PCIe
AER, FDIR, PTP). Author is the maintainer.
## PHASE 8: IMPACT AND RISK ASSESSMENT
**Step 8.1: DETERMINE WHO IS AFFECTED**
- Record: **driver-specific** — CONFIG_TXGBE / CONFIG_NGBE Wangxun PF
users. Not universal. VFs not covered by this hook.
**Step 8.2: DETERMINE THE TRIGGER CONDITIONS**
- Record: TX of a frame with **three or more** VLAN tags (hwaccel + in-
band) while HW VLAN TX is on. Uncommon vs plain Ethernet; used in
QinQ/provider/NFV setups. Unprivileged users can send tagged frames if
the interface/bridge allows it. Not a pure timing race.
**Step 8.3: DETERMINE THE FAILURE MODE SEVERITY**
- Record: Corrupted TX frames and possible hardware malfunction.
**HIGH** (data corruption on the wire; possible device misbehavior).
Not a kernel oops, but not cosmetic.
**Step 8.4: CALCULATE RISK-BENEFIT RATIO**
- Record:
- **Benefit:** high for affected Wangxun users (correct frames; avoid
HW malfunction)
- **Risk:** very low (24 lines, feature-bit clear, standard stack
fallback)
- **Ratio:** favors backporting
## PHASE 9: FINAL SYNTHESIS
**Step 9.1: COMPILE THE EVIDENCE**
**FOR:**
- Real HW limit: >2 VLAN tags + HW VLAN TX → corrupted frames /
malfunction
- Jacob Keller explicitly framed it as a `net` bugfix that needed
`Fixes:`
- Author is the Wangxun maintainer; netdev maintainers applied it
- Small (+24), contained, no new APIs
- Uses the intended `ndo_features_check` mechanism
- Hardware quirk/workaround class (stable exception)
- `wx_features_check` and VLAN TX offload exist in this tree
- v2 addressed review nits; no NAKs
**AGAINST:**
- Sent to net-next, not `net` (author could not pick a `Fixes:` SHA)
- No public reproducer, crash log, or extra `Reported-by`
- Triple-tag TX is a relatively rare path
- VF devices sharing `wx_xmit_frame` still lack this hook
**UNRESOLVED:**
- No stable@ lore search (HTTP 403)
- “Malfunction” not independently characterized beyond the vendor commit
message
**Step 9.2: APPLY THE STABLE RULES CHECKLIST**
1. Obviously correct and tested? **PASS** — v2 after review; applied by
Kicinski; logic matches core `validate_xmit_vlan()`
2. Fixes a real bug that affects users? **PASS** — HW TX of >2 VLAN tags
is wrong and user-triggerable
3. Important issue? **PASS** — on-wire corruption / HW malfunction
(HIGH, not a core oops)
4. Small and contained? **PASS** — +24 lines, one function
5. No new features or APIs? **PASS** — disables an unsafe offload for
specific packets
6. Can apply to the local tree? **PASS** — `wx_features_check()`
present; patch is a clean addition
**Step 9.3: CHECK FOR EXCEPTION CATEGORIES**
- Record: **Hardware quirk/workaround** — disable an offload the NIC
cannot perform. Same class as USB/PCI/codec quirks.
**Step 9.4: DECISION**
This tree is 7.3.0-rc4 and already contains the Wangxun TX offload path
and `wx_features_check()`. The patch is a small, reviewed workaround for
a vendor-confirmed HW limit that otherwise yields corrupted frames.
Missing `Fixes:`/`Cc: stable` is why it needs manual review, not a
reason to drop it. Jacob Keller’s net-vs-net-next question is the right
classification: this is a bugfix, not a feature.
### What the commit solves for stable users
Wangxun PFs will no longer ask the NIC to hardware-insert/offload a
third VLAN tag. Those packets fall back to software VLAN insertion,
which avoids bad frames and possible device malfunction.
### Risk vs benefit
Benefit is correctness on a real (if uncommon) TX path. Risk is a few
extra header walks and software VLAN push only when tags are already
present.
### Concerns
- Needs `wx_features_check()` (present here)
- Optional companion `732ed8f75ce58` for FDIR ATR on SW-VLAN packets is
already in this tree
- VFs still omit `ndo_features_check`; that is a remaining gap, not a
regression from this patch
Verification:
- [Phase 1] Parsed subject/tags/body from `git log -1` / `git show
f5cfb576ce39b`; no Fixes/Reported-by/Cc: stable
- [Phase 2] Diff: +24 in `wx_features_check()` only; confirmed current
file at wx_lib.c:3265-3313
- [Phase 2] Confirmed stack fallback: `validate_xmit_vlan()` in
net/core/dev.c:3971-3977
- [Phase 3] `git blame` on wx_features_check: body from 3b05aa997c491;
new VLAN loop from f5cfb576ce39b
- [Phase 3] TX offload intro `3403960cdf86c` (v6.5-rc1); `git merge-base
--is-ancestor` both 3b05aa997c491 and 3403960cdf86c vs HEAD: yes
- [Phase 3] Follow-up 732ed8f75ce58 exists; not a required prereq for
this hunk
- [Phase 3] Author Jiawen Wu listed as Wangxun maintainer in MAINTAINERS
- [Phase 4] `b4 dig -c/-a/-w/-m`: v1→v2, lore msgid
069DF89AA8029189+...; Przemek nits on v1; Jacob Keller asked why not
net+Fixes; author said no Fixes SHA; applied to net-next
- [Phase 4] lore WebFetch 403; v1 retrieved via `b4 mbox`
- [Phase 5] Callers: txgbe_main.c:705, ngbe_main.c:681;
netif_skb_features in net/core/dev.c:3895-3924
- [Phase 5] VF netdev_ops: ndo_start_xmit=wx_xmit_frame, no
ndo_features_check
- [Phase 6] `git describe HEAD` = v7.3-rc4-27-gf0100363d8c37; `make
kernelversion` = 7.3.0-rc4; pre-patch function confirmed via `git show
f5cfb576ce39b^`
- [Phase 8] Failure mode from commit text + HW offload path: corrupted
frames / malfunction, severity HIGH
- UNVERIFIED: stable@ mailing-list history (lore 403); independent
crash/reproducer beyond vendor description
**YES**
drivers/net/ethernet/wangxun/libwx/wx_lib.c | 24 +++++++++++++++++++++
1 file changed, 24 insertions(+)
diff --git a/drivers/net/ethernet/wangxun/libwx/wx_lib.c b/drivers/net/ethernet/wangxun/libwx/wx_lib.c
index 4a6851612368d..9be339965a683 100644
--- a/drivers/net/ethernet/wangxun/libwx/wx_lib.c
+++ b/drivers/net/ethernet/wangxun/libwx/wx_lib.c
@@ -3193,6 +3193,30 @@ netdev_features_t wx_features_check(struct sk_buff *skb,
netdev_features_t features)
{
struct wx *wx = netdev_priv(netdev);
+ __be16 type = skb->protocol;
+ u16 vlan_depth = ETH_HLEN;
+ u32 vlan_num = 0;
+
+ if (skb_vlan_tag_present(skb))
+ vlan_num++;
+
+ while (eth_type_vlan(type)) {
+ struct vlan_hdr vhdr, *vh;
+
+ vh = skb_header_pointer(skb, vlan_depth, sizeof(vhdr), &vhdr);
+ if (unlikely(!vh))
+ break;
+
+ type = vh->h_vlan_encapsulated_proto;
+ vlan_depth += VLAN_HLEN;
+ vlan_num++;
+
+ if (vlan_num > 2) {
+ features &= ~(NETIF_F_HW_VLAN_CTAG_TX |
+ NETIF_F_HW_VLAN_STAG_TX);
+ break;
+ }
+ }
if (!skb->encapsulation)
return features;
--
2.53.0
^ permalink raw reply related [flat|nested] 32+ messages in thread* [PATCH AUTOSEL 6.18-6.6] net: sfp: add quirk for HORACO copper SFP+ module
2026-10-19 13:44 [PATCH AUTOSEL 6.18-6.12] net: dsa: mt7530: check CORE_PLL_GROUP4 access in mt7531_setup() Sasha Levin
` (12 preceding siblings ...)
2026-10-19 13:48 ` [PATCH AUTOSEL 6.18] net: libwx: disable TX VLAN offload for packets with >2 VLAN tags Sasha Levin
@ 2026-10-19 13:48 ` Sasha Levin
2026-10-19 13:48 ` [PATCH AUTOSEL 6.18-5.15] net: usb: cdc_ether: add quirk for AMI BMC stale link events Sasha Levin
` (15 subsequent siblings)
29 siblings, 0 replies; 32+ messages in thread
From: Sasha Levin @ 2026-10-19 13:48 UTC (permalink / raw)
To: patches, stable
Cc: Aleksander Jan Bajkowski, Andrew Lunn, Jakub Kicinski,
Sasha Levin, linux, hkallweit1, davem, edumazet, pabeni, netdev,
linux-kernel
From: Aleksander Jan Bajkowski <olek2@wp.pl>
[ Upstream commit 1bb028baab541900d3603d4f93c1c0fb5aba5401 ]
Add quirk for a copper SFP+ module that identifies itself as "OEM"
"HC-10GE-113C". It uses RollBall protocol to talk to the PHY.
Signed-off-by: Aleksander Jan Bajkowski <olek2@wp.pl>
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
Link: https://patch.msgid.link/20260719100158.874882-1-olek2@wp.pl
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
LLM Generated explanations, may be completely bogus:
Local tree is **v7.3-rc4** (mainline `7.3.0-rc4`). The candidate is
`1bb028baab541` (`net: sfp: add quirk for HORACO copper SFP+ module`).
The SFP quirk table and `sfp_fixup_rollball()` are present here, so this
is evaluated as a hardware-quirk backport on technical merit.
===============================================================
PHASE 1: COMMIT MESSAGE FORENSICS
===============================================================
**Step 1.1: PARSE THE SUBJECT LINE**
Record: **[net: sfp:] [add] [quirk for HORACO copper SFP+ module]**
- Subsystem: `net: sfp` (SFP cage / phylink)
- Verb: **add** — here it is a hardware quirk table entry, not a new
driver or API
**Step 1.2: PARSE ALL COMMIT MESSAGE TAGS**
Record:
- **Fixes:** none (expected; this is not a regression from a prior
kernel commit)
- **Reported-by:** none
- **Tested-by:** none
- **Reviewed-by:** Andrew Lunn `<andrew@lunn.ch>` (PHY/SFP reviewer)
- **Acked-by:** none
- **Link:**
`https://patch.msgid.link/20260719100158.874882-1-olek2@wp.pl`
- **Cc: stable:** none (expected)
- **Signed-off-by:** Aleksander Jan Bajkowski `<olek2@wp.pl>` (author);
Jakub Kicinski `<kuba@kernel.org>` (netdev maintainer)
- Ignored: any pipeline “Upstream commit” / Sasha Levin SOB (none on
this commit)
Notable: reviewed by Lunn, applied by Kicinski. No syzbot, no multi-user
reports.
**Step 1.3: ANALYZE THE COMMIT BODY TEXT**
Record:
- **Bug:** copper SFP+ identifying as vendor `"OEM"` / part
`"HC-10GE-113C"` talks to its PHY with the RollBall I2C-to-MDIO
protocol, which the EEPROM does not describe in a way the generic path
understands.
- **Symptom:** PHY probe uses the wrong MDIO protocol (typically
`MDIO_I2C_NONE` or C45), so the copper PHY is not found and the link
does not come up.
- **Version info:** none in the message. Patch was sent as `[PATCH net-
next]` on 2026-07-19; applied 2026-07-21; `git describe --contains` →
**v7.3-rc1**.
- **Root cause (author):** module uses RollBall; needs an `sfp_quirks[]`
match so `sfp_fixup_rollball()` runs.
**Step 1.4: DETECT HIDDEN BUG FIXES**
Record: **Not a hidden cleanup.** It is an explicit hardware quirk. That
is an allowed stable class (SFP_QUIRK_* / `stable-kernel-rules.rst`
“hardware quirk”), not a disguised leak/race.
===============================================================
PHASE 2: DIFF ANALYSIS - LINE BY LINE
===============================================================
**Step 2.1: INVENTORY THE CHANGES**
Record:
- File: `drivers/net/phy/sfp.c` — **+3 / −0**
- Data only: `sfp_quirks[]` (static const table)
- Scope: **single-file surgical quirk addition** (one `SFP_QUIRK_F` +
comment)
**Step 2.2: UNDERSTAND THE CODE FLOW CHANGE**
Record:
- **Before:** EEPROM vendor `"OEM"` + PN `"HC-10GE-113C"` does not match
any quirk. `sfp_sm_mod_probe()` sets `mdio_protocol` from EEPROM
(`MDIO_I2C_C45` if 10GBASE-T ECC, else `MDIO_I2C_NONE` for a module
that does not identify as copper). `sfp_sm_probe_for_phy()` then
either skips PHY probe or probes at the wrong address/protocol.
- **After:** `sfp_lookup_quirk()` matches exact vendor+part,
`sfp_fixup_rollball()` sets `sfp->mdio_protocol = MDIO_I2C_ROLLBALL`
and `phy_t_retry` to 1s. Probe uses `SFP_PHY_ADDR_ROLLBALL` and
C45-over-RollBall.
- Path: **module insert / probe** (normal path for this SKU only).
**Step 2.3: IDENTIFY THE BUG MECHANISM**
Record:
- **Category:** **(h) Hardware workaround** — SFP quirk
- **Mechanism:** broken/non-standard EEPROM vs actual RollBall PHY
access. Fix is vendor/PN table entry calling existing
`sfp_fixup_rollball()`.
**Step 2.4: ASSESS THE FIX QUALITY**
Record:
- **Quality:** obviously correct. Same pattern as FLYPRO `SFP-10GT-
CS-30M`, Turris RTSFP-*, OEM SFP-10G-T. Match is exact (`sfp_match`
requires equal length; this entry is not `SFP_QUIRK_F_PREFIX`).
- **Minimal:** 3 lines, no unrelated edits.
- **Regression risk:** very low. Only this vendor+PN pair.
`sfp_fixup_rollball()` only sets protocol + retry interval.
- **Caveat (verified, not blocking):** some other OEM 10G copper entries
use `sfp_fixup_rollball_cc` (also patches `extended_cc`). Author chose
plain `sfp_fixup_rollball`; Lunn reviewed. If this SKU already has a
valid ECC, CC extra is unnecessary.
===============================================================
PHASE 3: GIT HISTORY INVESTIGATION
===============================================================
**Step 3.1: BLAME THE CHANGED LINES**
Record:
- New lines authored in **1bb028baab541** (2026-07-19).
- Neighbors: YV XGSPON quirk (`dfec1c14aecee`, 2025) and OEM SFP-GE-T
(`0805d67bc0ef95`, 2024).
- **Infrastructure:** `sfp_fixup_rollball()` introduced in
**324e88cbe3b7b** (“net: sfp: add support for multigig RollBall
transceivers”, 2022-10-03) — **v6.1-rc1**. Present in this tree.
**Step 3.2: FOLLOW THE FIXES: TAG**
Record: **No Fixes: tag.** Not a regression from a specific prior
commit. The module never worked without the quirk.
**Step 3.3: CHECK FILE HISTORY FOR RELATED CHANGES**
Record:
- Same author, same pattern: `ddbf0e78a8b20` “net: sfp: add quirk for
FLYPRO copper SFP+ module” (v6.17-rc5).
- Later on same file: hwmon leak fix; prefix-match for *other* OEM PNs
(`f53167e29b8e9`); more ONT quirks. **None change the HORACO entry.**
- **Standalone**, not patch X/Y.
**Step 3.4: CHECK THE AUTHOR'S OTHER COMMITS**
Record: Aleksander Jan Bajkowski is a regular PHY/SFP contributor
(FLYPRO quirk, intel-xway, realtek, mediatek). Not the SFP maintainer;
Lunn’s Reviewed-by covers that.
**Step 3.5: CHECK FOR DEPENDENT/PREREQUISITE COMMITS**
Record:
- Needs: `SFP_QUIRK_F`, `sfp_fixup_rollball()`, `MDIO_I2C_ROLLBALL` —
all in this tree (`sfp.c`, `include/linux/mdio/mdio-i2c.h`).
- **Applies standalone.** No new helpers.
===============================================================
PHASE 4: MAILING LIST AND EXTERNAL RESEARCH
===============================================================
**Step 4.1: FIND THE ORIGINAL PATCH DISCUSSION**
Record:
- `b4 dig -c 1bb028baab541`:
**https://patch.msgid.link/20260719100158.874882-1-olek2@wp.pl**
- `b4 dig -a`: **v1 only** (`[PATCH net-next]`), 2026-07-19
- Thread (mbox): (1) patch, (2) Lunn **Reviewed-by**, (3) patchwork-bot
applied to **netdev/net-next.git** as `1bb028baab54`
- **No NAK**, no requested respin, **no Cc: stable** in review
**Step 4.2: CHECK WHO REVIEWED THE PATCH**
Record (`b4 dig -w`): To/Cc included Russell King
(`linux@armlinux.org.uk`), Andrew Lunn, Heiner Kallweit, netdev
maintainers (David, Edumazet, Kicinski, Pabeni),
`netdev@vger.kernel.org`. Right people; Lunn reviewed.
**Step 4.3: SEARCH FOR THE BUG REPORT**
Record: no Reported-by, bugzilla, or syzbot. Author describes a real
module SKU. Severity from reporter: **module does not work** (PHY
protocol). User count: **unverified beyond the author**.
**Step 4.4: CHECK FOR RELATED PATCHES AND SERIES**
Record: **standalone**, not a series. Related only as the same quirk
pattern as FLYPRO.
**Step 4.5: CHECK STABLE MAILING LIST HISTORY**
Record: lore.kernel.org search returned **403**. Web search for “HORACO
… stable” found the net-next thread (LKML, Patchew, spinics), **not** a
stable-list NAK. **UNVERIFIED:** no stable@ thread found; no evidence it
was rejected.
===============================================================
PHASE 5: CODE SEMANTIC ANALYSIS
===============================================================
**Step 5.1: IDENTIFY KEY FUNCTIONS IN THE DIFF**
Record: **No function bodies changed.** New table entry consumed by
`sfp_lookup_quirk()` → `sfp_fixup_rollball()`.
**Step 5.2: TRACE CALLERS**
Record (from `sfp.c`):
- `sfp_lookup_quirk()` called from module-ID setup (~line 2677)
- If `quirk->fixup`, called under `st_mutex` (~2686–2687)
- `sfp_sm_probe_for_phy()` switches on `mdio_protocol`;
`MDIO_I2C_ROLLBALL` probes `SFP_PHY_ADDR_ROLLBALL`
- Context: **SFP insert**, process/workqueue, not IRQ. Userspace-visible
as “plug copper SFP+, no link”
**Step 5.3: TRACE CALLEES**
Record: `sfp_fixup_rollball()` assigns `MDIO_I2C_ROLLBALL` and
`phy_t_retry = 1s`. No alloc/lock.
**Step 5.4: FOLLOW THE CALL CHAIN**
Record: SFP cage driver (`CONFIG_SFP`, depends on I2C+PHYLINK) → EEPROM
read → quirk → PHY probe. **Reachable** when this module is inserted.
Not a syscall fuzzer path; **hardware + SFP cage**.
**Step 5.5: SEARCH FOR SIMILAR PATTERNS**
Record: many RollBall quirks already: FLYPRO, FS, Walsun, OEM SFP-10G-T
/ RTSFP-*, Turris. Same bug class; this SKU was missing.
===============================================================
PHASE 6: CROSS-REFERENCING AGAINST THE LOCAL TREE
===============================================================
**Step 6.1: DOES THE BUGGY CODE EXIST IN THIS TREE?**
Record:
- `git describe HEAD`: **v7.3-rc4-27-gf0100363d8c37**
- `make kernelversion`: **7.3.0-rc4**
- `drivers/net/phy/sfp.c` exists; `sfp_quirks[]` exists;
`sfp_fixup_rollball()` exists (since v6.1).
- Without this entry, this SKU would not match. `git merge-base --is-
ancestor 1bb028baab541 HEAD` → **yes** (already in 7.3-rc1+).
Evaluation is still: would this tree *need* the quirk for the hardware
— **yes, the driver is here**.
**Step 6.2: CHECK FOR BACKPORT COMPLICATIONS**
Record: **clean apply** on this tree (already applied as a 3-line insert
next to other OEM entries). No conflicting refactor of the table around
that site beyond later prefix-match macros unused by this entry.
**Step 6.3: CHECK IF RELATED FIXES ARE ALREADY HERE**
Record: this exact quirk is the fix. No alternate HORACO workaround.
Follow-ups do not replace it.
===============================================================
PHASE 7: SUBSYSTEM AND MAINTAINER CONTEXT
===============================================================
**Step 7.1: IDENTIFY THE SUBSYSTEM AND ITS CRITICALITY**
Record:
- Subsystem: **drivers/net/phy** — SFP cage (`CONFIG_SFP`)
- Criticality: **IMPORTANT** for machines with SFP cages (routers, NICs,
embedded); **PERIPHERAL** as a single OEM copper module SKU. Not
mm/VFS/core.
**Step 7.2: ASSESS SUBSYSTEM ACTIVITY**
Record: active (many 2025–2026 quirk and RollBall commits). Quirk table
is the long-standing way to handle broken modules.
===============================================================
PHASE 8: IMPACT AND RISK ASSESSMENT
===============================================================
**Step 8.1: DETERMINE WHO IS AFFECTED**
Record: **driver-specific + hardware-specific** — users of HORACO
HC-10GE-113C (EEPROM `OEM` / `HC-10GE-113C`) in an SFP cage. No effect
on other modules.
**Step 8.2: DETERMINE THE TRIGGER CONDITIONS**
Record: insert/probe that module. Common for owners of that SKU,
**never** otherwise. Unprivileged users cannot trigger it without the
hardware. Not a race.
**Step 8.3: DETERMINE THE FAILURE MODE SEVERITY**
Record:
- Failure: PHY not probed / no copper link
- Not oops/UAF/corruption
- **Severity: MEDIUM** for that hardware (link dead), **LOW** globally
- Still matches the documented **hardware quirk** stable exception
**Step 8.4: CALCULATE RISK-BENEFIT RATIO**
Record:
- **Benefit:** module works on stable kernels that already have
SFP+RollBall (this tree does)
- **Risk:** very low (3 lines, exact match, existing fixup)
- **Ratio:** favorable — classic stable quirk
===============================================================
PHASE 9: FINAL SYNTHESIS
===============================================================
**Step 9.1: COMPILE THE EVIDENCE**
FOR:
- `Documentation/process/stable-kernel-rules.rst` explicitly allows
**hardware quirks**
- Prompt class: **SFP_QUIRK_*** — “YES for stable”
- Real hardware: RollBall PHY, wrong/missing EEPROM description
- 3-line table add, existing `sfp_fixup_rollball()`
- Reviewed by Andrew Lunn; applied by Kicinski
- RollBall stack in this tree since v6.1
- Exact vendor+PN match; no new API/feature surface
- Same pattern as FLYPRO and other OEM 10G copper quirks
AGAINST:
- net-next, not `net`; no Cc: stable / Fixes (expected for new IDs)
- No public crash/CVE; only this SKU
- Submitted as “add quirk”, not a core bugfix
- Whether every OEM HC-10GE-113C also needs `rollball_cc` was not
independently tested here (Lunn review is the available evidence)
UNRESOLVED:
- lore.kernel.org HTTP 403; stable@ history only via web search (no hit)
- No independent Tested-by besides implied author testing
- How many users have this module: unverified
**Step 9.2: APPLY THE STABLE RULES CHECKLIST**
1. Obviously correct and tested? **PASS** — table entry + Lunn Reviewed-
by; applied to net-next
2. Fixes a real bug that affects users? **PASS** — module cannot talk to
PHY / no link
3. Important issue? **PASS** under the **hardware quirk** exception (not
a kernel oops)
4. Small and contained? **PASS** — +3 lines, one file
5. No new features or APIs? **PASS** — existing quirk hook only
6. Can apply to the local tree? **PASS** — SFP + `sfp_fixup_rollball`
present; applies cleanly
**Step 9.3: CHECK FOR EXCEPTION CATEGORIES**
Record: **Hardware quirk / SFP quirk** (and analogous to device-ID
addition on an existing driver). Qualifies automatically.
**Step 9.4: MAKE YOUR DECISION**
This tree has the SFP driver and RollBall fixup. The patch is a 3-line
SKU quirk so that copper module gets `MDIO_I2C_ROLLBALL`. That is the
stable exception, low risk, self-contained. Backport **yes**.
------------------------------------------------
What it solves for stable users
------------------------------------------------
Without the quirk, a HORACO HC-10GE-113C (`OEM` / `HC-10GE-113C`) never
uses RollBall, so the PHY is not probed and the 10G copper link stays
down. With it, insert matches the table and uses the existing RollBall
path (protocol + 1s PHY retry).
Risk vs benefit
------------------------------------------------
Benefit is that specific module working. Risk is negligible: exact
string match, no locking/API change. Later prefix-match work does not
apply to this exact PN.
Concerns
------------------------------------------------
None that block backport: no series, no missing helpers on this tree, no
review objections. Optional `rollball_cc` is unused; reviewers accepted
`sfp_fixup_rollball`.
Verification:
- [Phase 1] Tags: Reviewed-by Andrew Lunn; Link msgid
`20260719100158.874882-1-olek2@wp.pl`; SOB Bajkowski + Kicinski; no
Fixes/Reported-by/Cc:stable
- [Phase 1] Subject `[PATCH net-next]`; body states OEM HC-10GE-113C
uses RollBall
- [Phase 2] Diff: +3 lines in `sfp_quirks[]`; `SFP_QUIRK_F("OEM",
"HC-10GE-113C", sfp_fixup_rollball)`
- [Phase 2] `sfp_fixup_rollball()` sets `MDIO_I2C_ROLLBALL` and 1s
`phy_t_retry` (sfp.c:412–421)
- [Phase 2] `sfp_sm_probe_for_phy()` uses `SFP_PHY_ADDR_ROLLBALL` for
that protocol (sfp.c:2244–2246)
- [Phase 2] `sfp_lookup_quirk()` exact vendor+part match
(sfp.c:668–684); fixup at 2686–2687
- [Phase 3] `git describe HEAD` = v7.3-rc4-27-gf0100363d8c37;
kernelversion 7.3.0-rc4
- [Phase 3] Commit `1bb028baab541`; `git describe --contains` =
v7.3-rc1~133^2~221
- [Phase 3] `git merge-base --is-ancestor 1bb028baab541 HEAD` succeeded
- [Phase 3] `sfp_fixup_rollball` from `324e88cbe3b7b` (v6.1-rc1)
- [Phase 3] Similar FLYPRO quirk `ddbf0e78a8b20` (v6.17-rc5); author has
two sfp.c quirk commits
- [Phase 3] No later commit edits the HORACO lines (`git blame` 613–615
= 1bb028baab541)
- [Phase 4] `b4 dig -c/-a/-w/-m`: v1 only; Lunn Reviewed-by; applied to
net-next; CC list includes RMK, Lunn, Kallweit, netdev
- [Phase 4] lore.kernel.org WebFetch 403; mbox from b4 used instead
- [Phase 4] Web search: LKML/Patchew/spinics confirm review; no stable-
list discussion found
- [Phase 5] Call path: module probe → lookup_quirk → fixup →
sm_probe_for_phy; CONFIG_SFP (I2C+PHYLINK)
- [Phase 6] Quirk present in this checkout at sfp.c:613–614; RollBall
enum in mdio-i2c.h
- [Phase 7] `stable-kernel-rules.rst` line 19: hardware quirk is in-
scope
- [Phase 8] Failure mode: no PHY/no link for that SKU; not an oops
- UNVERIFIED: stable@ mailing list archives (HTTP 403); number of field
users; whether this SKU also needs `sfp_fixup_rollball_cc`
**YES**
drivers/net/phy/sfp.c | 3 +++
1 file changed, 3 insertions(+)
diff --git a/drivers/net/phy/sfp.c b/drivers/net/phy/sfp.c
index af43530ffb2c3..195468bde2f29 100644
--- a/drivers/net/phy/sfp.c
+++ b/drivers/net/phy/sfp.c
@@ -576,6 +576,9 @@ static const struct sfp_quirk sfp_quirks[] = {
SFP_QUIRK_F("YV", "SFP+ONU-XGSPON", sfp_fixup_potron),
+ // HORACO HC-10GE-113C uses Rollball protocol to talk to the PHY.
+ SFP_QUIRK_F("OEM", "HC-10GE-113C", sfp_fixup_rollball),
+
// OEM SFP-GE-T is a 1000Base-T module with broken TX_FAULT indicator
SFP_QUIRK_F("OEM", "SFP-GE-T", sfp_fixup_ignore_tx_fault),
--
2.53.0
^ permalink raw reply related [flat|nested] 32+ messages in thread* [PATCH AUTOSEL 6.18-5.15] net: usb: cdc_ether: add quirk for AMI BMC stale link events
2026-10-19 13:44 [PATCH AUTOSEL 6.18-6.12] net: dsa: mt7530: check CORE_PLL_GROUP4 access in mt7531_setup() Sasha Levin
` (13 preceding siblings ...)
2026-10-19 13:48 ` [PATCH AUTOSEL 6.18-6.6] net: sfp: add quirk for HORACO copper SFP+ module Sasha Levin
@ 2026-10-19 13:48 ` Sasha Levin
2026-10-19 13:49 ` [PATCH AUTOSEL 6.18-5.10] sctp: auth: propagate HMAC calculation errors to callers Sasha Levin
` (14 subsequent siblings)
29 siblings, 0 replies; 32+ messages in thread
From: Sasha Levin @ 2026-10-19 13:48 UTC (permalink / raw)
To: patches, stable
Cc: Jinhui Guo, Jakub Kicinski, Sasha Levin, oliver, andrew+netdev,
davem, edumazet, pabeni, linux-usb, netdev, linux-kernel
From: Jinhui Guo <guojinhui.liam@bytedance.com>
[ Upstream commit 089ca284afb0f3b2870a5ce41524cbc91ed350d4 ]
On AMD Genoa/Turin platforms the BMC-provided USB-Ethernet gadget
(American Megatrends, VID 0x046b PID 0xffb0) intermittently fails to
respond to ARP after AC cold boot. usbmon captures a stale
NETWORK_CONNECTION(off) immediately followed by
NETWORK_CONNECTION(on) on the interrupt endpoint (~130us apart)
after enumeration. Because alloc_netdev() leaves
__LINK_STATE_NOCARRIER cleared, netif_carrier_ok() returns true
when the spurious OFF arrives, so usbnet_cdc_status() cannot
recognise it as redundant and schedules EVENT_LINK_CHANGE.
__handle_link_change() then calls unlink_urbs(), killing ~60 rx
URBs whose payload has already been DMA'd into memory — xHCI trace
confirms them completing as -ECONNRESET with non-zero residual
length. rx_complete() drops these unconditionally. The following
ON restores the carrier and re-submits URBs, but the ARP reply is
already lost; the interface looks "up but silent" until ifdown/ifup.
Fix this by adding a device-specific quirk with FLAG_LINK_INTR set,
which makes usbnet_probe() call netif_carrier_off() after bind.
With initial carrier == OFF, usbnet_cdc_status() recognises the
spurious OFF as matching the current state and drops it; the
subsequent ON is the first real event and brings the link up
cleanly without ever tearing down the rx queue. The scheduled
link-change kevent is harmless because EVENT_DEV_OPEN is not yet
set at probe time.
This is applied as a device-specific quirk rather than a change to
the shared cdc_info driver_info because some CDC devices never
send NETWORK_CONNECTION notifications; forcing carrier off for
them would leave the link permanently DOWN. Restricting the change
to this VID/PID keeps that class of device untouched.
Tested on Genoa and Turin across 100+ AC cold boot cycles; ping
first-packet success rate went from intermittent to 100%.
Signed-off-by: Jinhui Guo <guojinhui.liam@bytedance.com>
Link: https://patch.msgid.link/20260730051341.24930-1-guojinhui.liam@bytedance.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
LLM Generated explanations, may be completely bogus:
Local tree is **6.12.111**. The buggy `cdc_ether` path is present here,
and this is a device-specific hardware quirk for an existing driver.
## PHASE 1: COMMIT MESSAGE FORENSICS
**Step 1.1 PARSE THE SUBJECT LINE**
- Record: `[net: usb: cdc_ether] [add quirk] [AMI BMC VID 0x046b PID
0xffb0 stale NETWORK_CONNECTION events]`
**Step 1.2 PARSE ALL COMMIT MESSAGE TAGS**
- Record:
- `Signed-off-by: Jinhui Guo <guojinhui.liam@bytedance.com>` (author)
- `Link: https://patch.msgid.link/20260730051341.24930-1-
guojinhui.liam@bytedance.com`
- `Signed-off-by: Jakub Kicinski <kuba@kernel.org>` (netdev maintainer
applied it)
- No `Fixes:`, no `Cc: stable`, no `Reported-by`, no `Reviewed-by`, no
`Acked-by`, no `Tested-by` (absence of Fixes/Cc-stable is expected
for this review)
- Ignore pipeline `Signed-off-by: Sasha Levin` / `[Upstream commit
...]` if present
**Step 1.3 ANALYZE THE COMMIT BODY TEXT**
- Record:
- **Bug:** AMI BMC USB-Ethernet gadget on AMD Genoa/Turin (VID
`0x046b` PID `0xffb0`) intermittently fails ARP after AC cold boot.
- **Symptom:** Interface looks “up but silent” until `ifdown`/`ifup`.
- **Root cause:** After enumeration, firmware emits stale
`NETWORK_CONNECTION(off)` then `(on)` ~130µs apart. `alloc_netdev()`
leaves `__LINK_STATE_NOCARRIER` clear, so `netif_carrier_ok()` is
true; `usbnet_cdc_status()` treats OFF as a real transition,
schedules `EVENT_LINK_CHANGE`, and `__handle_link_change()`
`unlink_urbs()`s ~60 RX URBs. Completions come back as `-ECONNRESET`
with payload already DMA’d; `rx_complete()` drops them, including
the ARP reply.
- **Fix:** Device-specific `driver_info` with `FLAG_LINK_INTR` so
probe starts carrier OFF; redundant OFF is dropped; ON is the first
real event.
- **Why not global:** Some CDC devices never send
`NETWORK_CONNECTION`; forcing carrier off would leave them
permanently DOWN.
- **Testing:** 100+ AC cold boots on Genoa and Turin; first-packet
ping went from intermittent to 100%.
**Step 1.4 DETECT HIDDEN BUG FIXES**
- Record: Not hidden. This is an explicit hardware quirk for a real
connectivity failure.
## PHASE 2: DIFF ANALYSIS
**Step 2.1 INVENTORY THE CHANGES**
- Record:
- File: `drivers/net/usb/cdc_ether.c` only (~+16 lines, 0 removed)
- Adds `ami_bmc_info` (clone of `cdc_info` plus `FLAG_LINK_INTR`)
- Adds `USB_DEVICE_AND_INTERFACE_INFO(0x046b, 0xffb0, ...)` **before**
the generic CDC Ethernet catch-all
- Scope: single-file surgical quirk / device ID
**Step 2.2 UNDERSTAND THE CODE FLOW CHANGE**
- Record:
- **Before:** `046b:ffb0` matches `USB_INTERFACE_INFO(...,
USB_CDC_SUBCLASS_ETHERNET, ...)` → `cdc_info` (`FLAG_ETHER |
FLAG_POINTTOPOINT`, no `FLAG_LINK_INTR`). Carrier starts ON. Stale
OFF is a state change.
- **After:** Same bind/status/unbind/filter/power callbacks, plus
`FLAG_LINK_INTR`. `usbnet_probe()` calls `usbnet_link_change(dev, 0,
0)` → `netif_carrier_off()`. Stale OFF matches current state and is
ignored.
**Step 2.3 IDENTIFY THE BUG MECHANISM**
- Record: **Hardware workaround / logic correctness.** Stale firmware
notifications plus default carrier-ON cause a false link-down that
tears down RX URBs and drops in-flight frames (ARP).
**Step 2.4 ASSESS THE FIX QUALITY**
- Record: Minimal and obviously correct. `FLAG_LINK_INTR` is the
established usbnet pattern (asix, smsc, dm9601, cdc_ncm, …).
Restricted to one VID/PID. Regression risk is confined to `046b:ffb0`:
initial carrier is OFF until the first ON notification. Author
captured that ON always follows. No public API / data-structure
change.
## PHASE 3: GIT HISTORY INVESTIGATION
**Step 3.1 BLAME THE CHANGED LINES**
- Record:
- Generic `cdc_info` has existed since early usbnet.
- `FLAG_LINK_INTR` added in `37e8273cd3059` (“usbnet: Set link down
initially for drivers that update link state”, 2009).
- Global FLAG on `cdc_ether` was removed in `71cc1fa9f2d71` (2010)
because CDC Ethernet notifications are optional; devices that never
send them stayed DOWN (`bugzilla.kernel.org/14791`). That is why
this must be device-specific.
- Carrier comparison in `usbnet_cdc_status()` added by `2d5c9dd2cde33`
(“net: usb: cdc_ether: don't spew notifications”, 2024-08-08). That
check is what lets a redundant OFF be dropped. Confirmed ancestor of
HEAD.
**Step 3.2 FOLLOW THE FIXES: TAG**
- Record: No `Fixes:` tag. Not applicable. The interaction is long-
standing default carrier-ON plus this BMC’s cold-boot notifications.
**Step 3.3 CHECK FILE HISTORY**
- Record: Recent `cdc_ether.c` history is other device IDs/quirks
(`zte_cdc_info`, Cinterion WWAN IDs, ThinkPad dock). Same pattern.
Standalone, not `patch X/Y`.
**Step 3.4 CHECK THE AUTHOR'S OTHER COMMITS**
- Record: `git log --author="Jinhui Guo" -- drivers/net/usb/` is empty
in this tree. Author is not the subsystem maintainer. Applied by Jakub
Kicinski. USB net maintainers were on To/Cc (Oliver Neukum, netdev).
**Step 3.5 CHECK FOR DEPENDENT/PREREQUISITE COMMITS**
- Record: Can apply standalone. This tree already has:
- `FLAG_LINK_INTR` and `usbnet_probe()` handling
- `usbnet_cdc_status()` carrier filter (`2d5c9dd2cde33`)
- `usbnet_cdc_bind` / `unbind` / `usbnet_cdc_update_filter` /
`usbnet_manage_power`
- Products table with the same Cinterion-then-catch-all context
## PHASE 4: MAILING LIST AND EXTERNAL RESEARCH
**Step 4.1 FIND THE ORIGINAL PATCH DISCUSSION**
- Record:
- `b4 dig -c 46651395...` failed (SHA not in this repo).
- Lore WebFetch returned 403 (Anubis bot-check).
- Openwall/Patchew/LKML recovered the thread:
- https://lists.openwall.net/linux-kernel/2026/07/30/380 (v1,
standalone `[PATCH]`)
- https://lists.openwall.net/linux-kernel/2026/07/30/387 (author
follow-up with usbmon + xHCI evidence)
- Single revision. No v2/v3 found. No NAK found.
**Step 4.2 CHECK WHO REVIEWED THE PATCH**
- Record: To: Oliver Neukum, Andrew Lunn, David Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni. Cc: linux-usb, netdev. No `Reviewed-
by`/`Acked-by` in the recovered thread. Kicinski’s SOB is the apply
signal. Recipients were the right maintainers.
**Step 4.3 SEARCH FOR THE BUG REPORT**
- Record: No syzbot/bugzilla. Author is the reporter, with usbmon, CDC
status traces, and xHCI Stop Ring / `-ECONNRESET` givebacks. Failure
is AC-cold-boot only; DC reboot preserves gadget state. 100+ cycle
test.
**Step 4.4 CHECK FOR RELATED PATCHES AND SERIES**
- Record: Standalone 16-line patch. Author’s follow-up mentions not
changing shared `cdc_info` because a global `FLAG_LINK_INTR` attempt
was reverted (the 2010 revert `71cc1fa9f2d71` is in this tree; the
cited subject string does not match that commit — likely a
misremembered title — but the technical reason matches the 2010
revert).
**Step 4.5 CHECK STABLE MAILING LIST HISTORY**
- Record: lore.kernel.org/stable search was blocked (403). Openwall/web
search found no stable-specific NAK or “do not backport” discussion.
UNVERIFIED: whether anyone already requested this on stable@.
## PHASE 5: CODE SEMANTIC ANALYSIS
**Step 5.1 KEY FUNCTIONS**
- Record: No function bodies changed. New `ami_bmc_info`; new
`products[]` entry. Runtime path: `usbnet_probe()` → `FLAG_LINK_INTR`
→ `usbnet_link_change()`; status path remains `usbnet_cdc_status()`.
**Step 5.2 TRACE CALLERS**
- Record:
- `usbnet_probe` is `cdc_driver.probe` (USB enumeration).
- `usbnet_cdc_status` is the interrupt completion status callback.
- `usbnet_open` submits the interrupt URB and RX URBs
(`EVENT_DEV_OPEN`).
- `__handle_link_change` unlinks RX URBs if carrier is down **and**
`EVENT_DEV_OPEN` is set.
**Step 5.3 TRACE CALLEES**
- Record: `usbnet_link_change(dev, 0, 0)` synchronously
`netif_carrier_off()` then defers `EVENT_LINK_CHANGE`. Probe-time
kevent is a no-op because `EVENT_DEV_OPEN` is clear
(`__handle_link_change` returns immediately).
**Step 5.4 FOLLOW THE CALL CHAIN**
- Record: Reachable on any host with this BMC gadget and
`CONFIG_USB_NET_CDCETHER`. After boot, NetworkManager/`ip link set up`
opens the netdev; stale notifications then hit the status path.
Userspace-visible: lost ARP, silent interface.
**Step 5.5 SEARCH FOR SIMILAR PATTERNS**
- Record: Same file already has `zte_cdc_info` (device-specific
status/bind). Many usbnet drivers use `FLAG_LINK_INTR`.
`drivers/hid/hid-ids.h` defines `USB_VENDOR_ID_AMI 0x046b`;
`unusual_devs.h` has another AMI BMC ID `0x046b:0xff40`. Vendor ID is
consistent.
## PHASE 6: CROSS-REFERENCING AGAINST THE LOCAL TREE
**Step 6.1 DOES THE BUGGY CODE EXIST IN THIS TREE?**
- Record: Yes. Tree is `p-6.12-2-gd38d58a4e0359`, `make kernelversion` =
**6.12.111**.
- `cdc_info.flags = FLAG_ETHER | FLAG_POINTTOPOINT` (no
`FLAG_LINK_INTR`) at `cdc_ether.c:525`.
- Catch-all `USB_INTERFACE_INFO(USB_CLASS_COMM,
USB_CDC_SUBCLASS_ETHERNET, USB_CDC_PROTO_NONE)` at line 944.
- No `0x046b` / `0xffb0` / `ami_bmc` in this driver.
- `usbnet_cdc_status()` has the `netif_carrier_ok() !=
!!event->wValue` filter (`2d5c9dd2cde33`).
- `rx_complete()` drops `-ECONNRESET`/`-ESHUTDOWN` via `goto block`.
- `usbnet_probe()` applies `FLAG_LINK_INTR` after `register_netdev()`
(author said “before register_netdev”; in this tree it is after, but
`netif_carrier_off()` is still synchronous and status URBs start at
`usbnet_open`, so the quirk still works).
**Step 6.2 CHECK FOR BACKPORT COMPLICATIONS**
- Record: Clean apply expected. Surrounding `cdc_info` / Cinterion
`0x1e2d:0x0069` / generic catch-all match the patch context. Line
numbers differ slightly (6.12 vs later mainline); context is
identical.
**Step 6.3 CHECK IF RELATED FIXES ARE ALREADY HERE**
- Record: No AMI BMC quirk. `2d5c9dd2cde33` is the prerequisite filter,
already present. No alternate fix for this VID/PID.
## PHASE 7: SUBSYSTEM AND MAINTAINER CONTEXT
**Step 7.1 IDENTIFY THE SUBSYSTEM AND ITS CRITICALITY**
- Record: `drivers/net/usb/cdc_ether.c` — USB CDC Ethernet.
**IMPORTANT** for hosts using this gadget (AMD Genoa/Turin BMC USB
NIC), not core mm/VFS. Common on enterprise servers.
**Step 7.2 ASSESS SUBSYSTEM ACTIVITY**
- Record: Mature driver (2005). Ongoing ID/quirk additions. USB
NETWORKING DRIVERS in MAINTAINERS is “Odd Fixes”, `drivers/net/usb/`.
## PHASE 8: IMPACT AND RISK ASSESSMENT
**Step 8.1 WHO IS AFFECTED**
- Record: Driver-specific / config-specific: `CONFIG_USB_NET_CDCETHER`
and AMI BMC gadget `046b:ffb0` (AMD Genoa/Turin and similar AMI BMC
platforms). Other CDC devices unchanged.
**Step 8.2 TRIGGER CONDITIONS**
- Record: AC cold boot; firmware queues stale OFF/ON; interface already
open so RX URBs exist. Intermittent race with ARP DMA vs Stop Ring.
Unprivileged users do not trigger it; it is a boot/enumeration path.
Common on affected servers that use the BMC USB NIC.
**Step 8.3 FAILURE MODE SEVERITY**
- Record: Lost RX frames (ARP) → interface up but no L2 connectivity
until ifdown/ifup or peer re-ARP. Not a panic. **HIGH** operational
impact on affected hardware (host-BMC or provisioning network dead
after cold boot).
**Step 8.4 RISK-BENEFIT RATIO**
- Record:
- **Benefit:** High for AMI BMC USB-Ethernet users on 6.12; fixes a
real post-boot connectivity failure.
- **Risk:** Very low — 16 lines, one VID/PID, existing flag, no
locking/API change.
- **Ratio:** Strongly favors backport.
## PHASE 9: FINAL SYNTHESIS
**Step 9.1 COMPILE THE EVIDENCE**
FOR:
- Real, reproduced connectivity bug with usbmon/xHCI evidence
- Hardware quirk + device ID on an existing driver (stable exception)
- 16-line contained change
- `FLAG_LINK_INTR` is a long-standing, well-understood usbnet mechanism
- Does not change generic `cdc_info` (avoids the 2010 permanent-DOWN
regression)
- Prerequisite carrier filter already in 6.12
- Tested 100+ AC cold boots; netdev maintainer applied it
- Affects enterprise AMD server BMC networking
AGAINST:
- Not a crash/security issue
- No `Reviewed-by` in recovered mail (Kicinski still applied it)
- Affects one VID/PID, not all users
- Author slightly misstated probe vs `register_netdev` order (does not
break the fix here)
UNRESOLVED:
- Could not use `b4 dig` (SHA absent) or lore.kernel.org (403)
- Could not confirm stable@ discussion
- Author’s other `71cc1fa9f2d7` subject string does not match this
tree’s commit of that SHA (2010 revert) — rationale still holds
**Step 9.2 STABLE RULES CHECKLIST**
1. Obviously correct and tested? **PASS** — standard flag, 100+ boot
test, maintainer SOB
2. Fixes a real bug that affects users? **PASS** — silent NIC after AC
cold boot
3. Important issue? **PASS** — connectivity loss / dropped RX (HIGH, not
panic)
4. Small and contained? **PASS** — +16 lines, one file, one VID/PID
5. No new features or APIs? **PASS** — quirk/ID only
6. Can apply to the local tree? **PASS** — code and prerequisites
present; clean context
**Step 9.3 EXCEPTION CATEGORIES**
- Record: **Hardware quirk/workaround** and **device ID addition to an
existing driver**.
**Step 9.4 DECISION**
This should be backported to this 6.12 tree. It is a small, device-
scoped quirk that fixes a real post-boot network failure on AMI BMC USB-
Ethernet, using infrastructure that already exists here.
### What the commit solves
On AMD Genoa/Turin, the AMI BMC USB CDC gadget emits a stale link-OFF
then link-ON right after cold-boot enumeration. Because CDC Ethernet
starts with carrier on, that OFF is treated as a real link-down, RX URBs
are unlinked, and in-flight ARP is dropped. The interface stays up but
silent. Starting carrier off for this VID/PID only makes the stale OFF a
no-op.
### Risk vs benefit
Benefit is restoring first-packet connectivity after AC cold boot on
affected servers. Risk is limited to `046b:ffb0` and is the intended
`FLAG_LINK_INTR` behavior.
### Concerns
None material for 6.12. Apply should be clean. Do not change generic
`cdc_info`.
### Verification
- [Phase 1] Parsed tags: author SOB, msgid Link, Kicinski SOB; no
Fixes/Cc-stable/Reported-by
- [Phase 2] Diff: +16 lines in `cdc_ether.c`; `ami_bmc_info` with
`FLAG_LINK_INTR`; ID `0x046b:0xffb0` before catch-all
- [Phase 3] `git describe HEAD` / `make kernelversion`:
`p-6.12-2-gd38d58a4e0359`, **6.12.111**
- [Phase 3] `git merge-base --is-ancestor 46651395… HEAD`: commit not in
this repo
- [Phase 3] `git merge-base --is-ancestor 2d5c9dd2cde33 HEAD`: yes
(carrier filter present)
- [Phase 3] `git show 71cc1fa9f2d71`: 2010 partial revert removing
`FLAG_LINK_INTR` from `cdc_info`
- [Phase 3] `git blame` `usbnet_cdc_status`: carrier check from
`2d5c9dd2cde33`
- [Phase 3] `git log --oneline -20 -- drivers/net/usb/cdc_ether.c`:
ID/quirk history; no AMI entry
- [Phase 3] `git log --author="Jinhui Guo" -- drivers/net/usb/`: empty
in this tree
- [Phase 4] `b4 dig -c <sha>`: no match (SHA not local)
- [Phase 4] lore WebFetch: 403; used Openwall + Patchew instead
- [Phase 4] Author follow-up: usbmon OFF/ON ~130µs, xHCI `-ECONNRESET`
length 1514, AC vs DC explanation
- [Phase 4] No NAK found; no v2 found
- [Phase 5] `usbnet_probe` FLAG_LINK_INTR →
`usbnet_link_change(dev,0,0)` after `register_netdev` (lines
1861–1877)
- [Phase 5] `__handle_link_change` returns if `!EVENT_DEV_OPEN`; else
`unlink_urbs` when carrier down
- [Phase 5] `rx_complete` `-ECONNRESET`/`-ESHUTDOWN` → `goto block`
- [Phase 5] `usbnet_open` starts interrupt URB then sets
`EVENT_DEV_OPEN`
- [Phase 6] No `ami_bmc`/`0x046b`/`0xffb0` in `cdc_ether.c`; catch-all
still binds this device to `cdc_info`
- [Phase 6] `FLAG_LINK_INTR` defined in `include/linux/usb/usbnet.h:122`
- [Phase 6] `USB_DEVICE_AND_INTERFACE_INFO` exists in
`include/linux/usb.h`
- [Phase 6] `CONFIG_USB_NET_CDCETHER` builds `cdc_ether.o`
- [Phase 7] HID confirms `USB_VENDOR_ID_AMI 0x046b`
- [Phase 8] Failure mode: dropped RX/ARP, silent interface — HIGH for
this hardware
- UNVERIFIED: stable@ discussion (lore 403); exact mainline SHA not in
this checkout
The patch is a textbook stable quirk: small, obviously correct, already
tested, and the buggy code is in this tree.
**YES**
drivers/net/usb/cdc_ether.c | 16 ++++++++++++++++
1 file changed, 16 insertions(+)
diff --git a/drivers/net/usb/cdc_ether.c b/drivers/net/usb/cdc_ether.c
index a032c1ded4063..05e625a461b49 100644
--- a/drivers/net/usb/cdc_ether.c
+++ b/drivers/net/usb/cdc_ether.c
@@ -532,6 +532,16 @@ static const struct driver_info cdc_info = {
.manage_power = usbnet_manage_power,
};
+static const struct driver_info ami_bmc_info = {
+ .description = "AMI BMC USB Ethernet",
+ .flags = FLAG_ETHER | FLAG_POINTTOPOINT | FLAG_LINK_INTR,
+ .bind = usbnet_cdc_bind,
+ .unbind = usbnet_cdc_unbind,
+ .status = usbnet_cdc_status,
+ .set_rx_mode = usbnet_cdc_update_filter,
+ .manage_power = usbnet_manage_power,
+};
+
static const struct driver_info zte_cdc_info = {
.description = "ZTE CDC Ethernet Device",
.flags = FLAG_ETHER | FLAG_POINTTOPOINT,
@@ -940,6 +950,12 @@ static const struct usb_device_id products[] = {
USB_CDC_SUBCLASS_ETHERNET,
USB_CDC_PROTO_NONE),
.driver_info = (unsigned long)&wwan_info,
+}, {
+ /* AMI BMC gadget */
+ USB_DEVICE_AND_INTERFACE_INFO(0x046b, 0xffb0, USB_CLASS_COMM,
+ USB_CDC_SUBCLASS_ETHERNET,
+ USB_CDC_PROTO_NONE),
+ .driver_info = (unsigned long)&ami_bmc_info,
}, {
USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ETHERNET,
USB_CDC_PROTO_NONE),
--
2.53.0
^ permalink raw reply related [flat|nested] 32+ messages in thread* [PATCH AUTOSEL 6.18-5.10] sctp: auth: propagate HMAC calculation errors to callers
2026-10-19 13:44 [PATCH AUTOSEL 6.18-6.12] net: dsa: mt7530: check CORE_PLL_GROUP4 access in mt7531_setup() Sasha Levin
` (14 preceding siblings ...)
2026-10-19 13:48 ` [PATCH AUTOSEL 6.18-5.15] net: usb: cdc_ether: add quirk for AMI BMC stale link events Sasha Levin
@ 2026-10-19 13:49 ` Sasha Levin
2026-10-19 17:28 ` Eric Biggers
2026-10-19 13:49 ` [PATCH AUTOSEL 6.18-5.10] net/rds: fix rds_message leak in the rds_send_xmit() drop path Sasha Levin
` (13 subsequent siblings)
29 siblings, 1 reply; 32+ messages in thread
From: Sasha Levin @ 2026-10-19 13:49 UTC (permalink / raw)
To: patches, stable
Cc: Qing Luo, Xin Long, Paolo Abeni, Sasha Levin, marcelo.leitner,
davem, edumazet, kuba, linux-sctp, netdev, linux-kernel
From: Qing Luo <luoqing@kylinos.cn>
[ Upstream commit 5d3ae80ecddeb82b492a2cf31ac3e44412b426f4 ]
sctp_auth_calculate_hmac() can fail when building the association secret
under memory pressure, but its void return silently leaves the HMAC digest
zeroed. On the receive path, sctp_sf_authenticate() compares this zeroed
digest against the peer-supplied one using crypto_memneq(), potentially
accepting an all-zero HMAC from the peer if the allocation failed. On the
send path, sctp_packet_pack() transmits a packet with a zeroed HMAC that
the peer would reject.
Improve error handling by making sctp_auth_calculate_hmac() return int:
- sctp_sf_authenticate() returns SCTP_IERROR_NOMEM instead of accepting
a zero HMAC.
- sctp_packet_pack() drops the packet on failure instead of transmitting
a zeroed HMAC.
Update the declaration in auth.h accordingly.
Assisted-by: LLM
Signed-off-by: Qing Luo <luoqing@kylinos.cn>
Acked-by: Xin Long <lucien.xin@gmail.com>
Link: https://patch.msgid.link/20260807064314.500742-1-l1138897701@163.com
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
LLM Generated explanations, may be completely bogus:
This is a real SCTP-AUTH fail-open under memory pressure, and the buggy
`void` HMAC helper is present in this 6.12 tree. The fix is small,
reviewer-acked, and backportable with a trivial `auth.c` context tweak.
===============================================================
PHASE 1: COMMIT MESSAGE FORENSICS
===============================================================
Step 1.1: PARSE THE SUBJECT LINE
Record: `[sctp/auth] [propagate] HMAC calculation errors to callers` —
turn `sctp_auth_calculate_hmac()` from `void` into an `int` so ENOMEM is
not swallowed.
Step 1.2: PARSE ALL COMMIT MESSAGE TAGS
Record:
- `Assisted-by: LLM` (author used an LLM)
- `Signed-off-by: Qing Luo <luoqing@kylinos.cn>` (author)
- `Acked-by: Xin Long <lucien.xin@gmail.com>` (SCTP maintainer)
- `Link:
https://patch.msgid.link/20260807064314.500742-1-l1138897701@163.com`
- `Signed-off-by: Paolo Abeni <pabeni@redhat.com>` (netdev)
- No `Reported-by`, `Tested-by`, `Reviewed-by` on the committed message
- No `Fixes:` / `Cc: stable` on the committed v4 text (expected for this
review queue; see Phase 4 — they were present on v2/v3, and Xin Long
re-added `Fixes:` when acking v4)
Step 1.3: ANALYZE THE COMMIT BODY TEXT
Record:
- Bug: `sctp_auth_calculate_hmac()` can fail while building the
association secret; it still returns void and leaves the HMAC field
zeroed.
- Receive symptom: `sctp_sf_authenticate()` compares that zero digest to
the peer HMAC with `crypto_memneq()`, so an all-zero HMAC can be
accepted.
- Send symptom: `sctp_packet_pack()` ships a packet with a zero HMAC,
which the peer rejects.
- Root cause: silent early return on `sctp_auth_asoc_create_secret()`
failure.
- No kernel-version range in the message.
Step 1.4: DETECT HIDDEN BUG FIXES
Record: Not a disguised cleanup. It is an explicit error-propagation fix
for a fail-open AUTH path. v1 understated it as “wrong error code
(BAD_SIG vs NOMEM)”; later revisions correctly describe acceptance of a
zero HMAC.
===============================================================
PHASE 2: DIFF ANALYSIS - LINE BY LINE
===============================================================
Step 2.1: INVENTORY THE CHANGES
Record:
- `include/net/sctp/auth.h`: `void` → `int` for
`sctp_auth_calculate_hmac()`
- `net/sctp/auth.c`: return `-ENOMEM` on secret allocation failure;
`return 0` on success
- `net/sctp/output.c`: drop packed skb on HMAC failure; treat `pkt_count
<= 0` as transmit failure
- `net/sctp/sm_statefuns.c`: on HMAC failure, `kfree(save_digest)` and
return `SCTP_IERROR_NOMEM`
- Scope: ~24 insertions / 13 deletions, four files, surgical, SCTP-only
- Functions: `sctp_auth_calculate_hmac`, `sctp_packet_pack`,
`sctp_packet_transmit`, `sctp_sf_authenticate`
Step 2.2: UNDERSTAND THE CODE FLOW CHANGE
Record:
- HMAC helper: allocation failure used to `return;` with digest
untouched → now `-ENOMEM`.
- Receive: digest is `memset` to 0, then HMAC is computed into it. On
failure, compare is skipped; NOMEM is returned instead of comparing
against zeros.
- Send: HMAC failure frees `packet->auth`, frees the GSO fragment if
needed, returns `-ENOMEM`. Non-GSO `nskb == head`, so `nskb` is not
freed there (avoids double-free); `sctp_packet_transmit` frees `head`
when `pkt_count <= 0`.
Step 2.3: IDENTIFY THE BUG MECHANISM
Record:
- Category: logic / correctness with security impact (AUTH fail-open),
plus missing error-path handling.
- Mechanism: RFC 4895 requires the AUTH HMAC field to be zero during
computation. The receive path zeros it, then writes the computed MAC.
If computation is skipped, the field stays zero, so
`crypto_memneq(peer_hmac, zeros)` succeeds when the peer sent zeros.
- Trigger: `key_id != asoc->active_key_id` (non-active configured shared
key) plus `sctp_auth_asoc_create_secret()` GFP_ATOMIC failure.
Step 2.4: ASSESS THE FIX QUALITY
Record:
- Obviously correct: callers already have NOMEM handling
(`sctp_sf_eat_auth` → `SCTP_DISPOSITION_NOMEM`; transmit already drops
on pack failure).
- Minimal: no API for userspace, no new features.
- Regression risk: low. Send path drops the packet instead of emitting a
MAC the peer would reject. Receive path no longer accept-on-OOM.
- Residual in this tree: `crypto_shash_setkey()` failure still `goto
free` without an error (mainline HMAC helpers no longer have that
path). That is preexisting and outside this commit.
===============================================================
PHASE 3: GIT HISTORY INVESTIGATION
===============================================================
Step 3.1: BLAME THE CHANGED LINES
Record: Silent `return` on failed `sctp_auth_asoc_create_secret()` is
from `1f485649f52929` (“[SCTP]: Implement SCTP-AUTH internals”, Vlad
Yasevich, 2007-10-09), first in `v2.6.24-rc1`. Present for the entire
life of SCTP-AUTH.
Step 3.2: FOLLOW THE FIXES: TAG
Record: Committed v4 has no `Fixes:` line. Xin Long added `Fixes:
1f485649f529 ("[SCTP]: Implement SCTP-AUTH internals")` on ack. That
commit **is** an ancestor of this tree (`git merge-base --is-ancestor
1f485649f52929 HEAD` succeeded). Mainline fix `5d3ae80ecdde` is **not**
in this tree.
Step 3.3: CHECK FILE HISTORY FOR RELATED CHANGES
Record: Recent SCTP work here is unrelated (INIT leaks, ASCONF, TOCTOU,
STALE_COOKIE). No prior attempt to propagate HMAC errors.
`hmac_sha1_usingrawkey` is **not** in this tree; this tree still uses
`crypto_shash`.
Step 3.4: CHECK THE AUTHOR'S OTHER COMMITS
Record: Qing Luo is not the SCTP maintainer. Xin Long **is** (recent
`net/sctp` commits, and he acked this). That ack is the quality signal.
Step 3.5: CHECK FOR DEPENDENT/PREREQUISITE COMMITS
Record: No functional prerequisite. Mainline `auth.c` later switched to
`hmac_sha*_usingrawkey`; this tree still has `crypto_shash`. The error-
return change does not need that conversion. Standalone after a small
context adapt in `auth.c`.
===============================================================
PHASE 4: MAILING LIST AND EXTERNAL RESEARCH
===============================================================
Step 4.1: FIND THE ORIGINAL PATCH DISCUSSION
Record:
- `b4 dig -c 5d3ae80ecdde` →
https://patch.msgid.link/20260807064314.500742-1-l1138897701@163.com
- Series: v1 net-next (2026-07-30), v2 net (2026-08-04), v3 net
(2026-08-04), v4 net-next (2026-08-07, applied)
- v1 changelog only mentioned BAD_SIG vs NOMEM.
- Jakub Kicinski forwarded an AI review he treated as valid: the real
bug is **accepting an all-zero HMAC**, reachable from the wire via a
configured non-active key id, including delayed COOKIE-ECHO AUTH;
asked for `Fixes:` and `Cc: stable`.
- v2/v3 retargeted to `net`, added security wording, `Fixes:`, `Cc:
stable`, and a `sctp_auth_chunk_verify()` refactor.
- Xin Long on v3: “This patch is intended as a **fix**”; drop the
`sctp_auth_chunk_verify()` churn (send that kind of cleanup to net-
next). Returning `-ENOMEM` from pack is fine because transmit already
treats non-positive pack results as drop.
- v4 dropped the verify refactor and was sent to net-next; Xin Long
acked and re-added `Fixes:`. Paolo applied it to net-next
(`5d3ae80ecdde`).
- No NAKs. Applied version is v4 (latest).
Step 4.2: CHECK WHO REVIEWED THE PATCH
Record (`b4 dig -w`): netdev maintainers (davem, edumazet, kuba, pabeni,
horms), `linux-sctp@`, `lucien.xin@gmail.com`. Xin Long acked. Jakub’s
review drove the security description.
Step 4.3: SEARCH FOR THE BUG REPORT
Record: No syzbot/bugzilla/user crash report. Bug was found by code
review (LLM-assisted, then confirmed on the list). Unverified: whether
any production user has hit the zero-HMAC accept path.
Step 4.4: CHECK FOR RELATED PATCHES AND SERIES
Record: Single patch, not 2/N. v3’s `sctp_auth_chunk_verify()` NOMEM vs
BAD_SIG split was deliberately **not** merged. Without it, delayed
COOKIE-ECHO treats HMAC-calc ENOMEM as auth failure
(`sctp_auth_chunk_verify` still returns bool / false) and discards —
fail-closed, which is the safe direction.
Step 4.5: CHECK STABLE MAILING LIST HISTORY
Record: lore.kernel.org/stable search returned 403. From the netdev
thread, v2/v3 carried `Cc: stable`; v4 dropped it only because the
author retargeted after Xin Long asked to strip the verify refactor.
Unverified: no separate stable@ thread found.
===============================================================
PHASE 5: CODE SEMANTIC ANALYSIS
===============================================================
Step 5.1: IDENTIFY KEY FUNCTIONS IN THE DIFF
Record: `sctp_auth_calculate_hmac`, `sctp_packet_pack`,
`sctp_packet_transmit`, `sctp_sf_authenticate`. (semcode MCP not
available; used grep + reads.)
Step 5.2: TRACE CALLERS
Record: `sctp_auth_calculate_hmac` has exactly two call sites in this
tree:
- `sctp_sf_authenticate()` — receive AUTH, GFP_ATOMIC, reachable from
the wire
- `sctp_packet_pack()` — transmit path
`sctp_sf_authenticate` is used by `sctp_sf_eat_auth` and by
`sctp_auth_chunk_verify` (delayed COOKIE-ECHO). Softirq/packet receive
context.
Step 5.3: TRACE CALLEES
Record: `sctp_auth_asoc_create_secret()` allocates key vectors
(`sctp_auth_make_local_vector` / `peer_vector` /
`sctp_auth_asoc_set_secret`) with the caller’s `gfp`. Receive uses
`GFP_ATOMIC`.
Step 5.4: FOLLOW THE CALL CHAIN
Record: Userspace/peer can trigger this: enable SCTP AUTH
(`net.sctp.auth_enable` is **0 by default**, but applications/sysctl can
turn it on; ADD-IP needs AUTH), configure more than one shared key, send
AUTH with a non-active key id and an all-zero HMAC while the host is
under atomic-allocation pressure. Reachable from the network, not a
local-only path.
Step 5.5: SEARCH FOR SIMILAR PATTERNS
Record: No other `sctp_auth_calculate_hmac` copies. Same fail-open
exists on both send and receive via the single helper.
===============================================================
PHASE 6: CROSS-REFERENCING AGAINST THE LOCAL TREE
===============================================================
Step 6.1: DOES THE BUGGY CODE EXIST IN THIS TREE?
Record: Yes. Tree is `pending-6.12` / `6.12.111`
(`p-6.12-3-gc504cf0baa3d8`).
```707:731:net/sctp/auth.c
void sctp_auth_calculate_hmac(...)
{
...
asoc_key = sctp_auth_asoc_create_secret(asoc, ep_key,
gfp);
if (!asoc_key)
return;
```
Receive still zeros the digest then compares unconditionally
(`net/sctp/sm_statefuns.c` ~4426–4433). Bug introduced in v2.6.24, long
before 6.12.
Step 6.2: CHECK FOR BACKPORT COMPLICATIONS
Record: Mainline `auth.c` uses `hmac_sha1_usingrawkey` /
`hmac_sha256_usingrawkey`; this tree uses `crypto_shash_setkey` +
`crypto_shash_tfm_digest`. Exact mainline hunk context in `auth.c` will
not match. The signature change, `-ENOMEM` return, `return 0`, and both
caller sites match this tree. An adapted patch (`void`→`int`, `return
-ENOMEM`, `return 0` after the existing `free:` label, plus the
output/statefuns hunks) applies (`git apply --check` succeeded on that
adaptation). Difficulty: **minor conflicts / trivial rework**, not a
rewrite.
Step 6.3: CHECK IF RELATED FIXES ARE ALREADY HERE
Record: `git merge-base --is-ancestor 5d3ae80ecdde HEAD` failed. No
“propagate HMAC” commit. `sctp_auth_calculate_hmac` is still `void`. No
substitute fix.
===============================================================
PHASE 7: SUBSYSTEM AND MAINTAINER CONTEXT
===============================================================
Step 7.1: IDENTIFY THE SUBSYSTEM AND ITS CRITICALITY
Record: `net/sctp` — IMPORTANT (transport protocol; not mm/core). AUTH
is optional but is the integrity mechanism for SCTP-AUTH / ADD-IP.
Step 7.2: ASSESS SUBSYSTEM ACTIVITY
Record: Actively maintained (Xin Long and others still landing SCTP
fixes on this branch). Mature protocol; this bug is old, not a new-
subsystem artifact.
===============================================================
PHASE 8: IMPACT AND RISK ASSESSMENT
===============================================================
Step 8.1: DETERMINE WHO IS AFFECTED
Record: Config/protocol-specific: `CONFIG_IP_SCTP` plus AUTH enabled
(`net->sctp.auth_enable` defaults to 0 in `net/sctp/protocol.c`). Users
who enable AUTH (telecom, WebRTC data-channel stacks, ADD-IP) are the
population that matters.
Step 8.2: DETERMINE THE TRIGGER CONDITIONS
Record:
- AUTH enabled and a non-active shared key configured
- Peer uses that key id
- `sctp_auth_asoc_create_secret()` fails (`GFP_ATOMIC` on receive)
- Peer HMAC is all zeros
Allocation failure is not attacker-controlled; key id and zero HMAC are.
Unprivileged remote peer on an AUTH-enabled association can attempt
this. Commonness: uncommon; severity if it hits: high.
Step 8.3: DETERMINE THE FAILURE MODE SEVERITY
Record:
- Receive: AUTH bypass (`chunk->auth = 1`) for chunks that should have
been rejected — **HIGH** (security / integrity). Not a panic, but
fail-open authentication.
- Send: packet with zero HMAC dropped by peer — **LOW/MEDIUM** (failed
send under OOM; already broken).
Overall: **HIGH** because of the receive fail-open.
Step 8.4: CALCULATE RISK-BENEFIT RATIO
Record:
- Benefit: high for AUTH users — stops accepting unauthenticated chunks
under OOM.
- Risk: very low — ~20 lines, error paths only, existing NOMEM/drop
handling.
- Ratio: strongly favors backport.
===============================================================
PHASE 9: FINAL SYNTHESIS
===============================================================
Step 9.1: COMPILE THE EVIDENCE
FOR:
- Real AUTH fail-open: zeroed digest compared to peer HMAC after silent
HMAC-calc failure (verified in `sctp_sf_authenticate`).
- Present in this 6.12 tree since `1f485649f529` (v2.6.24).
- Small, obviously correct; NOMEM already handled on the eat-AUTH path.
- SCTP maintainer acked it as a fix and supplied `Fixes:`.
- Jakub’s review explicitly called for stable.
- Two call sites only; no new APIs.
AGAINST:
- AUTH off by default; needs extra keys + atomic ENOMEM + zero HMAC.
- No syzbot/user crash report.
- Merged via net-next as v4 without `Cc: stable` in the commit text.
- Exact mainline `auth.c` context differs (`crypto_shash` vs
`hmac_sha*`).
- Delayed COOKIE-ECHO still maps NOMEM to “auth failed” (fail-closed;
acceptable, and what was merged).
UNRESOLVED:
- No stable@ archive confirmation (lore 403).
- No evidence of a real-world exploit/report.
Step 9.2: APPLY THE STABLE RULES CHECKLIST
1. Obviously correct and tested? **PASS** — logic is local; acked by Xin
Long; applied to net-next.
2. Fixes a real bug that affects users? **PASS** — fail-open AUTH on a
wire-reachable path when AUTH is enabled.
3. Important issue? **PASS** — authentication bypass / integrity failure
(HIGH), not a crash. Narrow trigger, but security-class.
4. Small and contained? **PASS** — four files, ~24 lines, one helper +
two callers.
5. No new features or APIs? **PASS** — return type only, internal.
6. Can apply to the local tree? **PASS** — buggy code is here; caller
hunks match; `auth.c` needs a 3-line adapt around `crypto_shash` (not
a missing subsystem).
Step 9.3: CHECK FOR EXCEPTION CATEGORIES
Record: None (not a device ID/quirk/DT/build/doc exception). It
qualifies as a normal security/correctness fix.
Step 9.4: MAKE YOUR DECISION
Backport to this 6.12 tree. The bug exists here, the fix is surgical,
and leaving AUTH fail-open under OOM is worse than dropping the packet /
returning NOMEM.
Verification:
- [Phase 1] Tags parsed from the provided message and from the lore
mbox: Acked-by Xin Long; no Fixes on v4 commit text; v2/v3 had Fixes +
Cc: stable
- [Phase 2] Diff: void→int, -ENOMEM on secret alloc, receive NOMEM +
kfree(save_digest), send drop + `pkt_count <= 0`
- [Phase 2] Confirmed receive zeros digest then `crypto_memneq` in
`sm_statefuns.c` 4426–4433
- [Phase 3] `git blame net/sctp/auth.c`: silent return from
`1f485649f52929` (2007); `git describe --contains` → v2.6.24-rc1
- [Phase 3] `git merge-base --is-ancestor 1f485649f52929 HEAD`
succeeded; `5d3ae80ecdde` is not an ancestor
- [Phase 3] `hmac_sha1_usingrawkey` absent in this tree; HMAC still via
`crypto_shash`
- [Phase 4] `b4 dig -c 5d3ae80ecdde`: v1–v4; Jakub review identified
zero-HMAC accept and asked for stable; Xin Long: “intended as a fix”,
drop verify refactor; acked v4 with Fixes
- [Phase 4] lore.kernel.org/stable WebFetch 403 — UNVERIFIED whether
stable@ discussed it separately
- [Phase 5] grep: only two `sctp_auth_calculate_hmac` call sites;
`sctp_sf_eat_auth` already handles `SCTP_IERROR_NOMEM`
- [Phase 5] `net->sctp.auth_enable = 0` by default (`protocol.c:1382`)
- [Phase 6] `git describe HEAD` → `p-6.12-3-gc504cf0baa3d8`; `make
kernelversion` → 6.12.111
- [Phase 6] Adapted patch `git apply --check` succeeded; exact mainline
`auth.c` context would conflict
- [Phase 8] Failure mode: AUTH bypass under ENOMEM + zero HMAC + non-
active key — HIGH; send of zero HMAC — LOW
- UNVERIFIED: real-world exploitation; whether `crypto_shash_setkey`
failure is practically reachable for HMAC-SHA1/256 (not required for
this verdict)
**YES**
include/net/sctp/auth.h | 6 +++---
net/sctp/auth.c | 10 ++++++----
net/sctp/output.c | 12 +++++++++---
net/sctp/sm_statefuns.c | 9 ++++++---
4 files changed, 24 insertions(+), 13 deletions(-)
diff --git a/include/net/sctp/auth.h b/include/net/sctp/auth.h
index 3d5879e08e78a..27a098411e4cc 100644
--- a/include/net/sctp/auth.h
+++ b/include/net/sctp/auth.h
@@ -84,9 +84,9 @@ int sctp_auth_send_cid(enum sctp_cid chunk,
const struct sctp_association *asoc);
int sctp_auth_recv_cid(enum sctp_cid chunk,
const struct sctp_association *asoc);
-void sctp_auth_calculate_hmac(const struct sctp_association *asoc,
- struct sk_buff *skb, struct sctp_auth_chunk *auth,
- struct sctp_shared_key *ep_key, gfp_t gfp);
+int sctp_auth_calculate_hmac(const struct sctp_association *asoc,
+ struct sk_buff *skb, struct sctp_auth_chunk *auth,
+ struct sctp_shared_key *ep_key, gfp_t gfp);
void sctp_auth_shkey_release(struct sctp_shared_key *sh_key);
void sctp_auth_shkey_hold(struct sctp_shared_key *sh_key);
diff --git a/net/sctp/auth.c b/net/sctp/auth.c
index fbbfa0c279552..6971cb00f5e30 100644
--- a/net/sctp/auth.c
+++ b/net/sctp/auth.c
@@ -613,9 +613,9 @@ int sctp_auth_recv_cid(enum sctp_cid chunk, const struct sctp_association *asoc)
* zero (as shown in Figure 6) followed by all chunks that are placed
* after the AUTH chunk in the SCTP packet.
*/
-void sctp_auth_calculate_hmac(const struct sctp_association *asoc,
- struct sk_buff *skb, struct sctp_auth_chunk *auth,
- struct sctp_shared_key *ep_key, gfp_t gfp)
+int sctp_auth_calculate_hmac(const struct sctp_association *asoc,
+ struct sk_buff *skb, struct sctp_auth_chunk *auth,
+ struct sctp_shared_key *ep_key, gfp_t gfp)
{
struct sctp_auth_bytes *asoc_key;
__u16 key_id, hmac_id;
@@ -636,7 +636,7 @@ void sctp_auth_calculate_hmac(const struct sctp_association *asoc,
/* ep_key can't be NULL here */
asoc_key = sctp_auth_asoc_create_secret(asoc, ep_key, gfp);
if (!asoc_key)
- return;
+ return -ENOMEM;
free_key = 1;
}
@@ -654,6 +654,8 @@ void sctp_auth_calculate_hmac(const struct sctp_association *asoc,
if (free_key)
sctp_auth_key_put(asoc_key);
+
+ return 0;
}
/* API Helpers */
diff --git a/net/sctp/output.c b/net/sctp/output.c
index 23e96305cad7c..3d7ead9d40e19 100644
--- a/net/sctp/output.c
+++ b/net/sctp/output.c
@@ -517,8 +517,14 @@ static int sctp_packet_pack(struct sctp_packet *packet,
}
if (auth) {
- sctp_auth_calculate_hmac(tp->asoc, nskb, auth,
- packet->auth->shkey, gfp);
+ if (sctp_auth_calculate_hmac(tp->asoc, nskb, auth,
+ packet->auth->shkey, gfp)) {
+ sctp_chunk_free(packet->auth);
+ packet->auth = NULL;
+ if (gso)
+ kfree_skb(nskb);
+ return -ENOMEM;
+ }
/* free auth if no more chunks, or add it back */
if (list_empty(&packet->chunk_list))
sctp_chunk_free(packet->auth);
@@ -619,7 +625,7 @@ int sctp_packet_transmit(struct sctp_packet *packet, gfp_t gfp)
/* pack up chunks */
pkt_count = sctp_packet_pack(packet, head, gso, gfp);
- if (!pkt_count) {
+ if (pkt_count <= 0) {
kfree_skb(head);
goto out;
}
diff --git a/net/sctp/sm_statefuns.c b/net/sctp/sm_statefuns.c
index 86f26b8b39934..8dc65498763cc 100644
--- a/net/sctp/sm_statefuns.c
+++ b/net/sctp/sm_statefuns.c
@@ -4457,9 +4457,12 @@ static enum sctp_ierror sctp_sf_authenticate(
memset(digest, 0, sig_len);
- sctp_auth_calculate_hmac(asoc, chunk->skb,
- (struct sctp_auth_chunk *)chunk->chunk_hdr,
- sh_key, GFP_ATOMIC);
+ if (sctp_auth_calculate_hmac(asoc, chunk->skb,
+ (struct sctp_auth_chunk *)chunk->chunk_hdr,
+ sh_key, GFP_ATOMIC)) {
+ kfree(save_digest);
+ return SCTP_IERROR_NOMEM;
+ }
/* Discard the packet if the digests do not match */
if (crypto_memneq(save_digest, digest, sig_len)) {
--
2.53.0
^ permalink raw reply related [flat|nested] 32+ messages in thread* Re: [PATCH AUTOSEL 6.18-5.10] sctp: auth: propagate HMAC calculation errors to callers
2026-10-19 13:49 ` [PATCH AUTOSEL 6.18-5.10] sctp: auth: propagate HMAC calculation errors to callers Sasha Levin
@ 2026-10-19 17:28 ` Eric Biggers
0 siblings, 0 replies; 32+ messages in thread
From: Eric Biggers @ 2026-10-19 17:28 UTC (permalink / raw)
To: Sasha Levin
Cc: patches, stable, Qing Luo, Xin Long, Paolo Abeni, marcelo.leitner,
davem, edumazet, kuba, linux-sctp, netdev, linux-kernel
On Mon, Oct 19, 2026 at 09:49:03AM -0400, Sasha Levin wrote:
> - Residual in this tree: `crypto_shash_setkey()` failure still `goto
> free` without an error (mainline HMAC helpers no longer have that
> path). That is preexisting and outside this commit.
I don't think this backport can be considered complete without handling
that case.
- Eric
^ permalink raw reply [flat|nested] 32+ messages in thread
* [PATCH AUTOSEL 6.18-5.10] net/rds: fix rds_message leak in the rds_send_xmit() drop path
2026-10-19 13:44 [PATCH AUTOSEL 6.18-6.12] net: dsa: mt7530: check CORE_PLL_GROUP4 access in mt7531_setup() Sasha Levin
` (15 preceding siblings ...)
2026-10-19 13:49 ` [PATCH AUTOSEL 6.18-5.10] sctp: auth: propagate HMAC calculation errors to callers Sasha Levin
@ 2026-10-19 13:49 ` Sasha Levin
2026-10-19 13:49 ` [PATCH AUTOSEL 6.18-5.10] selftests/net: fin_ack_lat: fix latency threshold typo Sasha Levin
` (12 subsequent siblings)
29 siblings, 0 replies; 32+ messages in thread
From: Sasha Levin @ 2026-10-19 13:49 UTC (permalink / raw)
To: patches, stable
Cc: Sharath Srinivasan, Gerd Rausch, Allison Henderson,
Jakub Kicinski, Sasha Levin, davem, edumazet, pabeni, netdev,
linux-rdma, rds-devel, linux-kernel
From: Sharath Srinivasan <sharath.srinivasan@oracle.com>
[ Upstream commit eb8a59a17f4a8b6f2afb4a8063e99dd96cc3fab0 ]
When rds_send_xmit() picks the next message off cp_send_queue it takes
its own reference with rds_message_addref(). If the message then hits
the never-retransmit check (RDS_MSG_FLUSH, or an RDMA op that was
already retransmitted), it is moved to the local to_be_dropped list and
that reference is dropped after the batch.
However, if RDS_MSG_ON_CONN has already been cleared, the message is
not added to to_be_dropped and the reference taken above is never
dropped: cp_xmit_rm has not been set at this point, so the loop simply
abandons rm and the rds_message (and everything it pins: pages, MRs,
notifiers) leaks after an RDMA error.
The only other places that clear RDS_MSG_ON_CONN are
rds_send_path_drop_acked() and rds_send_drop_to(), and both can run
while rds_send_xmit() has dropped cp_lock between moving the message
to cp_retrans and re-taking the lock in the never-retransmit check:
rds_send_path_drop_acked() can ack away a message that already sat on
cp_retrans - the RDS_MSG_RETRANSMITTED case above - and
rds_send_drop_to() runs on socket close. Both unlink the message
under cp_lock and put their own reference, leaving the xmit-path
reference stranded.
Drop the reference directly in that case.
This mirrors Oracle UEK commit "net/rds: fix rds_message memleak in
rds_send_xmit".
Signed-off-by: Gerd Rausch <gerd.rausch@oracle.com>
Signed-off-by: Sharath Srinivasan <sharath.srinivasan@oracle.com>
[achender: port to net-next; update commit message, checkpatch nits]
Signed-off-by: Allison Henderson <achender@kernel.org>
Link: https://patch.msgid.link/20260730041629.3512480-4-achender@kernel.org
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
LLM Generated explanations, may be completely bogus:
This is a real, long-standing `rds_message` leak in `rds_send_xmit()`
that exists in this 5.10.270 tree. The fix is small, matches the local
code, and does not need the rest of its series.
---
## PHASE 1: COMMIT MESSAGE FORENSICS
**Step 1.1: PARSE THE SUBJECT LINE**
Record: **[net/rds] [fix] [rds_message leak in rds_send_xmit() drop
path]**
**Step 1.2: PARSE ALL COMMIT MESSAGE TAGS**
From the candidate message:
- `Signed-off-by: Gerd Rausch <gerd.rausch@oracle.com>` (original
author)
- `Signed-off-by: Sharath Srinivasan <sharath.srinivasan@oracle.com>`
(UEK/port author)
- `[achender: port to net-next; update commit message, checkpatch nits]`
- `Signed-off-by: Allison Henderson <achender@kernel.org>` (RDS
maintainer-side porter)
- `Link:
https://patch.msgid.link/20260730041629.3512480-4-achender@kernel.org`
- `Signed-off-by: Jakub Kicinski <kuba@kernel.org>` (netdev merge)
No `Reported-by`, `Tested-by`, `Reviewed-by`, or `Acked-by` in the
candidate text. Absence of `Fixes:` / `Cc: stable` is expected here.
Mailing-list v4 (same msgid) **did** carry:
- `Fixes: 2ad8099b58f2 ("RDS: rds_send_xmit() locking/irq fixes")`
Oracle UEK source (`94549e4732d8`) carried:
- `Fixes: fbbe0610d057 ("RDS: add support for atomic messages over the
wire")` — that SHA is **not** in this tree
- `Reviewed-by: William Kucharski <william.kucharski@oracle.com>`
- `Orabug: 38923495` (internal production bug)
**Step 1.3: ANALYZE THE COMMIT BODY TEXT**
Record:
- **Bug:** `rds_send_xmit()` takes an extra `rds_message_addref()` when
dequeuing. On the never-retransmit path (`RDS_MSG_FLUSH` or already-
retransmitted RDMA), that ref is only dropped if the message is moved
onto `to_be_dropped`. If `RDS_MSG_ON_CONN` was already cleared, the
extra ref is abandoned.
- **Symptom:** leak of `rds_message` plus pinned pages, MRs, and
notifiers after an RDMA error / reconnect / socket close.
- **Root cause:** `rds_send_path_drop_acked()` (ACK of a message already
on `cp_retrans`) and `rds_send_drop_to()` (socket close) can clear
`RDS_MSG_ON_CONN` and put **their** refs in the window where xmit has
dropped `cp_lock`. They do not put the xmit-path extra ref.
`cp_xmit_rm` is not set yet, so the success-path put never runs.
- **Version info:** bug traces to 2010 locking change; present in this
5.10 tree.
**Step 1.4: DETECT HIDDEN BUG FIXES**
Record: **Not hidden.** Subject and body are an explicit leak/refcount
fix.
---
## PHASE 2: DIFF ANALYSIS
**Step 2.1: INVENTORY THE CHANGES**
Record:
- File: `net/rds/send.c` only (~+12/−3 in `rds_send_xmit()`)
- Scope: **single-file surgical fix**
**Step 2.2: UNDERSTAND THE CODE FLOW CHANGE**
Record:
- **Before:** `test_and_clear_bit(RDS_MSG_ON_CONN)` → if set,
`list_move` to `to_be_dropped`; always unlock; `continue`. If the bit
was already clear, no put.
- **After:** same if the bit is set; **else** unlock then
`rds_message_put(rm)` for the extra xmit ref.
**Step 2.3: IDENTIFY THE BUG MECHANISM**
Record: **Reference-counting / resource leak.** Extra
`rds_message_addref()` at dequeue (local tree `net/rds/send.c:265`) is
not balanced when `RDS_MSG_ON_CONN` is already clear. Verified in this
tree: only `rds_send_path_drop_acked()` (`clear_bit` at line 707) and
`rds_send_drop_to()` (`test_and_clear_bit` at line 774) plus this drop
path itself clear that bit. `rds_send_path_reset()` does **not** (v4
commit message correction vs v1–v3).
**Step 2.4: ASSESS THE FIX QUALITY**
Record:
- **Obviously correct.** Queueing takes a conn-list ref
(`rds_send_queue_rm` addref + `RDS_MSG_ON_CONN`). Xmit takes a second
ref. Concurrent drop/ack puts the conn-list ref. Xmit must put its
own.
- `rds_send_remove_from_sock()` documents that it puts the list-position
ref; drop_acked uses that. Xmit’s extra ref is separate.
- After `continue`, the next loop iteration does `rm = cp->cp_xmit_rm`
(still NULL), so the local pointer is not used after put.
- Put happens after `spin_unlock_irqrestore`, same as the existing
success-path put at line 423. The “irqs on here, so we can put()”
comment is about not putting **under** `cp_lock`.
- **Regression risk: low.** Does not change locking order or APIs.
Follow-up `reinitialize to_be_dropped on restart` is documented
hardening with **no known user-visible bug**; this else-path does not
even enqueue on `to_be_dropped`.
---
## PHASE 3: GIT HISTORY INVESTIGATION
**Step 3.1: BLAME THE CHANGED LINES**
Record:
- Drop-path structure from `5c11559046c4b3` (“RDS: send.c”, 2009).
- Leak introduced by `2ad8099b58f27` (“RDS: rds_send_xmit() locking/irq
fixes”, 2010): that commit **removed** an unconditional
`rds_message_put(rm)` on this path and only put messages that made it
onto `to_be_dropped`.
- FLUSH clause: `905dd4184e0732` (2016). Multipath `cp_` conversion:
`1f9ecd7eacfd9e` (2016).
- Buggy pattern has been in-tree for ~16 years; first appeared long
before 5.10.
**Step 3.2: FOLLOW THE FIXES: TAG**
Record:
- Mailing-list `Fixes: 2ad8099b58f2` — **is an ancestor of this HEAD**
(`git merge-base --is-ancestor` exit 0).
- `git show 2ad8099b58f27` confirms the put was moved to the batch-end
`to_be_dropped` walk only.
- UEK `Fixes: fbbe0610d057` is **not** in this tree (exit 128); that tag
is a red herring for 5.10. The real introducer here is `2ad8099b58f2`.
**Step 3.3: CHECK FILE HISTORY**
Record: Recent `net/rds/send.c` work in this tree is locking/xmit
serialization (`acquire_in_xmit`, shutdown races), not this drop path.
No prior fix for this leak (`rg` for “already off the conn list” is
empty). **Standalone**, not a refactor prerequisite.
**Step 3.4: CHECK THE AUTHOR'S OTHER COMMITS**
Record: Gerd Rausch / Sharath Srinivasan / Allison Henderson are
established RDS contributors. Henderson has many RDS stable backports in
this tree. This is a UEK→upstream port of a production Orabug.
**Step 3.5: CHECK FOR DEPENDENT/PREREQUISITE COMMITS**
Record: v4 series is four **independent** bugs (sleep-in-atomic unpin,
MR socket UAF, **this leak**, IB MR pin leak). This hunk only needs
existing `rds_message_put()` / `RDS_MSG_ON_CONN` / `to_be_dropped`, all
present. **Applies standalone.**
---
## PHASE 4: MAILING LIST AND EXTERNAL RESEARCH
**Step 4.1: FIND THE ORIGINAL PATCH DISCUSSION**
Record:
- `b4 dig -c` on the candidate SHA was not possible (commit is not in
this checkout). `b4 dig -c 5c11559046c4b3` only found the 2009
original.
- lore.kernel.org fetches returned **403**. Used openwall mirrors.
- Thread: https://lists.openwall.net/netdev/2026/07/30/65 (v4 3/4),
cover https://lists.openwall.net/netdev/2026/07/30/62
- Revisions: v1 (2026-07-11) → v3 → v4 (2026-07-30). **Code of this
patch unchanged after v3**; v4 only corrected the commit message to
name `drop_acked`/`drop_to` instead of `rds_send_path_reset()`.
- Merged by Jakub Kicinski. No NAK found.
- v1–v3 incorrectly named `rds_send_path_reset()` as a clearer of
`RDS_MSG_ON_CONN`; verified **false** in this tree
(`rds_send_path_reset` only sets `RDS_MSG_RETRANSMITTED` and splices
queues).
**Step 4.2: CHECK WHO REVIEWED THE PATCH**
Record: Sent to netdev, linux-rdma, Kicinski, Dumazet, Abeni, Horms. RDS
lists historically include `rds-devel@oss.oracle.com`. No extra
`Reviewed-by` on the upstream version; UEK had William Kucharski.
Maintainer-side porter is Allison Henderson.
**Step 4.3: SEARCH FOR THE BUG REPORT**
Record: Oracle UEK Orabug 38923495; no public syzbot/bugzilla report
found. Production bug inside Oracle RDS, not theoretical.
**Step 4.4: CHECK FOR RELATED PATCHES AND SERIES**
Record: Independent of series 1/4, 2/4, 4/4. Later “reinitialize
`to_be_dropped` on restart” is **hardening only** (“no user-visible bug
is known”).
**Step 4.5: CHECK STABLE MAILING LIST HISTORY**
Record: lore.kernel.org/stable search **403**; web search found **no**
stable-list discussion of this leak. UNVERIFIED whether anyone already
requested/rejected it for 5.10.y.
---
## PHASE 5: CODE SEMANTIC ANALYSIS
semcode MCP is **not** available; used read/grep.
**Step 5.1: KEY FUNCTIONS**
Record: `rds_send_xmit()` only.
**Step 5.2: TRACE CALLERS**
Record (this tree):
- `rds_sendmsg()` → `rds_send_xmit()` (`send.c:1385`) — syscall
- `rds_send_worker()` (`threads.c:200`) — `rds_wq` work
- `rds_ib_tasklet_fn_send()` (`ib_cm.c:334`) — IB send tasklet
**Step 5.3: TRACE CALLEES**
Record: `rds_message_addref` / `rds_message_put` (`message.c`); last put
runs `rds_message_purge()` (pages, `rds_rdma_free_op`, MR krefs).
**Step 5.4: FOLLOW THE CALL CHAIN**
Record: **Userspace-reachable** via RDS `sendmsg`. Also on IB completion
and reconnect. Race: xmit drops `cp_lock` after moving to `cp_retrans`;
recv/ACK path (`rds_send_path_drop_acked`) or
`close`/`RDS_CANCEL_SENT_TO` (`rds_send_drop_to`) can run then.
**Step 5.5: SEARCH FOR SIMILAR PATTERNS**
Record: Same file already balances other refs carefully (`drop_to` skips
if `ON_CONN` already clear). This path is the missing counterpart. No
duplicate of this exact leak elsewhere in `net/rds/`.
---
## PHASE 6: CROSS-REFERENCING AGAINST THE LOCAL TREE
**Step 6.1: DOES THE BUGGY CODE EXIST IN THIS TREE?**
Record: **Yes.** `pending-5.10`, `make kernelversion` = **5.10.270**,
`git describe` = `p-5.10-1-gce56b7a73b64b`.
```287:294:net/rds/send.c
if (test_bit(RDS_MSG_FLUSH, &rm->m_flags) ||
(rm->rdma.op_active &&
test_bit(RDS_MSG_RETRANSMITTED,
&rm->m_flags))) {
spin_lock_irqsave(&cp->cp_lock, flags);
if (test_and_clear_bit(RDS_MSG_ON_CONN,
&rm->m_flags))
list_move(&rm->m_conn_item,
&to_be_dropped);
spin_unlock_irqrestore(&cp->cp_lock,
flags);
continue;
}
```
Introduced in 2010 (`2ad8099b58f2`); present for the entire 5.10
lifetime.
**Step 6.2: CHECK FOR BACKPORT COMPLICATIONS**
Record: **Clean apply** (trivial fuzz at most). Local drop-path text
matches the hunk exactly. Nearby comment still has the old “We cold
possibly” typo; that line is context only.
**Step 6.3: CHECK IF RELATED FIXES ARE ALREADY HERE**
Record: Fix **not** present. No other send.c commit in this tree plugs
this hole.
---
## PHASE 7: SUBSYSTEM AND MAINTAINER CONTEXT
**Step 7.1: SUBSYSTEM AND CRITICALITY**
Record: **net/rds** — Reliable Datagram Sockets (Oracle RAC / InfiniBand
and TCP). MAINTAINERS: Supported (Santosh Shilimkar / Oracle).
**IMPORTANT** (not core mm/VFS; not staging). `CONFIG_RDS` depends on
`INET`; RDMA ops need `CONFIG_RDS_RDMA`.
**Step 7.2: SUBSYSTEM ACTIVITY**
Record: Actively maintained in this 5.10 tree (recent shutdown/xmit
locking, TCP corruption, NULL-deref fixes). Mature send path; this leak
is old, not a new-feature bug.
---
## PHASE 8: IMPACT AND RISK ASSESSMENT
**Step 8.1: WHO IS AFFECTED**
Record: **Config/driver-specific** — `CONFIG_RDS` users, especially
RDMA/IB reconnect and TCP peer-generation flush (`RDS_MSG_FLUSH` in
`recv.c`). Enterprise RDS/RAC deployments.
**Step 8.2: TRIGGER CONDITIONS**
Record:
1. Xmit dequeues a message (addref, move to `cp_retrans`, drop
`cp_lock`).
2. Never-retransmit: `RDS_MSG_FLUSH` (peer gen change) or RDMA +
`RDS_MSG_RETRANSMITTED` (after `rds_send_path_reset`).
3. Concurrent ACK (`drop_acked`) or socket close (`drop_to`) already
cleared `ON_CONN`.
Not every packet; **error/reconnect/close**. Unprivileged RDS sockets
can drive send/close; RDMA/reconnect is the realistic production case
(Orabug).
**Step 8.3: FAILURE MODE SEVERITY**
Record: **Memory/resource leak** of message, pages, MRs, notifiers. Not
a panic. Repeated reconnects accumulate pinned memory → **HIGH** for RDS
users, not a universal crash.
**Step 8.4: RISK-BENEFIT RATIO**
Record:
- **Benefit:** high for RDS/RDMA users; stops leaking pinned pages/MRs
after errors that already happen in production.
- **Risk:** very low (~12 lines, one else-put, same pattern as existing
puts).
- **Ratio:** favor backport.
---
## PHASE 9: FINAL SYNTHESIS
**Step 9.1: COMPILE THE EVIDENCE**
**FOR:**
- Real refcount leak; objects pin pages/MRs/notifiers
- Present in this 5.10 tree since 2010 (`2ad8099b58f2`)
- Small, obviously correct, one function
- Clean apply; no prerequisites
- RDS maintainer port; netdev merge; Oracle production Orabug
- Userspace-reachable via RDS send + close/reconnect
**AGAINST:**
- RDS is not universal
- Error-path race, not every send
- Not a crash/CVE
- No public syzbot report
**UNRESOLVED:**
- Stable-list history (lore 403)
- Whether last-ref `rds_message_put()` from IB **tasklet** can sleep via
`unpin_user_pages_dirty_lock()` — **already true** for the success-
path and `to_be_dropped` puts in this tree; this patch does not create
that class of bug (series 1/4 is a separate sleep-in-atomic fix)
**Step 9.2: STABLE RULES CHECKLIST**
1. Obviously correct and tested? **PASS** — refcount math checks out;
UEK + netdev merge
2. Real bug affecting users? **PASS** — Orabug 38923495; leak is in this
tree
3. Important (crash/security/corruption/deadlock)? **PASS** — leak of
pinned pages/MRs after RDMA error/reconnect; severity HIGH for RDS
4. Small and contained? **PASS** — ~12 lines, one file
5. No new features/APIs? **PASS**
6. Can apply to this tree? **PASS**
**Step 9.3: EXCEPTION CATEGORIES**
Record: None (not an ID/quirk/DT/build/docs exception). Qualifies as a
normal leak/refcount fix.
**Step 9.4: DECISION**
Backport to **this** 5.10.270 tree. The buggy code is here, the fix is
surgical, and there is no missing prerequisite.
What it fixes for 5.10 users: after RDMA QP error / reconnect
(`RDS_MSG_RETRANSMITTED`) or peer generation flush (`RDS_MSG_FLUSH`),
racing ACK or socket close can clear `RDS_MSG_ON_CONN` in the xmit lock
gap. The extra `rds_message_addref()` is then never dropped, leaking the
message and everything it pins. Adding `rds_message_put(rm)` in that
else branch restores the balance `2ad8099b58f2` broke.
---
## Verification
- **[Phase 1]** Tags: SOBs Rausch/Srinivasan/Henderson/Kicinski; Link
msgid `3512480-4`; ML `Fixes: 2ad8099b58f2`; UEK Orabug 38923495
- **[Phase 2]** Diff: else-branch `rds_message_put(rm)` in
`rds_send_xmit()` never-retransmit path
- **[Phase 2]** Confirmed `rds_send_remove_from_sock()` puts the conn-
list ref; xmit extra ref is separate (`send.c:605-674`)
- **[Phase 2]** Confirmed only drop_acked (707) and drop_to (774) plus
this path clear `RDS_MSG_ON_CONN`
- **[Phase 3]** `git blame`: leak from `2ad8099b58f27` (2010), which
removed unconditional put
- **[Phase 3]** `git merge-base --is-ancestor 2ad8099b58f2 HEAD` → yes;
`fbbe0610d057` → no
- **[Phase 3]** `git show 2ad8099b58f27`: put moved to `to_be_dropped`
only
- **[Phase 4]** openwall v4 0/4 and 3/4; v4 commit-message correction;
Kicinski merge
- **[Phase 4]** Follow-up `to_be_dropped` reinit is hardening only, not
required
- **[Phase 4]** UNVERIFIED: lore.kernel.org 403; `b4 dig` could not
target the 2026 SHA (not in this tree)
- **[Phase 5]** Callers: sendmsg, `rds_send_worker`,
`rds_ib_tasklet_fn_send`
- **[Phase 5]** No semcode MCP
- **[Phase 6]** `git describe HEAD` = `p-5.10-1-gce56b7a73b64b`; `make
kernelversion` = 5.10.270
- **[Phase 6]** Buggy hunk present at `send.c:287-294`; fix text absent
- **[Phase 8]** Failure mode: leak of message + pages + MRs; HIGH for
RDS, not a panic
- **UNVERIFIED:** stable@ discussion of this specific backport
**YES**
net/rds/send.c | 18 +++++++++++++++---
1 file changed, 15 insertions(+), 3 deletions(-)
diff --git a/net/rds/send.c b/net/rds/send.c
index 5d09c7654f136..0a541cfc90185 100644
--- a/net/rds/send.c
+++ b/net/rds/send.c
@@ -288,9 +288,21 @@ int rds_send_xmit(struct rds_conn_path *cp)
(rm->rdma.op_active &&
test_bit(RDS_MSG_RETRANSMITTED, &rm->m_flags))) {
spin_lock_irqsave(&cp->cp_lock, flags);
- if (test_and_clear_bit(RDS_MSG_ON_CONN, &rm->m_flags))
- list_move(&rm->m_conn_item, &to_be_dropped);
- spin_unlock_irqrestore(&cp->cp_lock, flags);
+ if (test_and_clear_bit(RDS_MSG_ON_CONN,
+ &rm->m_flags)) {
+ /* our ref is put after the batch */
+ list_move(&rm->m_conn_item,
+ &to_be_dropped);
+ spin_unlock_irqrestore(&cp->cp_lock,
+ flags);
+ } else {
+ /* already off the conn list; drop
+ * the ref taken above ourselves
+ */
+ spin_unlock_irqrestore(&cp->cp_lock,
+ flags);
+ rds_message_put(rm);
+ }
continue;
}
--
2.53.0
^ permalink raw reply related [flat|nested] 32+ messages in thread* [PATCH AUTOSEL 6.18-5.10] selftests/net: fin_ack_lat: fix latency threshold typo
2026-10-19 13:44 [PATCH AUTOSEL 6.18-6.12] net: dsa: mt7530: check CORE_PLL_GROUP4 access in mt7531_setup() Sasha Levin
` (16 preceding siblings ...)
2026-10-19 13:49 ` [PATCH AUTOSEL 6.18-5.10] net/rds: fix rds_message leak in the rds_send_xmit() drop path Sasha Levin
@ 2026-10-19 13:49 ` Sasha Levin
2026-10-19 13:50 ` [PATCH AUTOSEL 6.18-5.10] netfilter: ebtables: bound num_counters like nentries in do_replace() Sasha Levin
` (11 subsequent siblings)
29 siblings, 0 replies; 32+ messages in thread
From: Sasha Levin @ 2026-10-19 13:49 UTC (permalink / raw)
To: patches, stable
Cc: Brian Grech, Simon Horman, Jakub Kicinski, Sasha Levin, davem,
edumazet, pabeni, shuah, netdev, linux-kselftest, linux-kernel
From: Brian Grech <bgrech@redhat.com>
[ Upstream commit 2373107782a98ba15931a37bbd9dd46d7213e5aa ]
The commit message for af8c8a450bf4 ("selftests: net: Add FIN_ACK
processing order related latency spike test") states: "if the latency
is larger than 1 second (spike), print a message". However the code
uses a threshold of 100000 us (100 ms), not 1000000 us (1 s).
The lower threshold causes false positives on slower hardware where
normal connection latency occasionally exceeds 100 ms but never
approaches the 1 s spike that indicates the actual FIN/ACK race bug.
Fix the threshold to match the documented intent.
Reviewed-by: Simon Horman <horms@kernel.org>
Signed-off-by: Brian Grech <bgrech@redhat.com>
Link: https://patch.msgid.link/20260806151645.4172900-1-bgrech@redhat.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
LLM Generated explanations, may be completely bogus:
I have the local 6.12 tree, the one-line typo, mailing-list review, and
a clean apply. Compiling the nine-phase analysis next.
---
## PHASE 1: COMMIT MESSAGE FORENSICS
**Step 1.1: PARSE THE SUBJECT LINE**
- Record: `[selftests/net] [fix] latency threshold typo in fin_ack_lat`
**Step 1.2: PARSE ALL COMMIT MESSAGE TAGS** (from the presented message
and from `git show 2373107782a9`)
- `Reviewed-by: Simon Horman <horms@kernel.org>`
- `Signed-off-by: Brian Grech <bgrech@redhat.com>` (author)
- `Link:
https://patch.msgid.link/20260806151645.4172900-1-bgrech@redhat.com`
- `Signed-off-by: Jakub Kicinski <kuba@kernel.org>` (netdev maintainer,
applied it)
- No `Reported-by`, `Tested-by`, `Acked-by` in the committed message
- Committed message has no `Fixes:` / `Cc: stable` (those tags were on
mailing-list v3; they are not treated as “already selected”)
- Record: Reviewed by a netdev reviewer; applied by the net maintainer.
Author is Red Hat.
**Step 1.3: ANALYZE THE COMMIT BODY TEXT**
- Record: [Bug] `fin_ack_lat` treats latency ≥ 100000 µs (100 ms) as a
spike, while the original test commit says the spike is **1 second**.
[Symptom] false FAIL printouts on slower/loaded hardware when normal
localhost connect latency is 100 ms–1 s. [Version] original test is
`af8c8a450bf4` (2020). [Root cause] missing zero: 100000 vs 1000000.
**Step 1.4: DETECT HIDDEN BUG FIXES**
- Record: Not hidden. Explicit typo/threshold bug in a kselftest. “fix”
is accurate.
---
## PHASE 2: DIFF ANALYSIS
**Step 2.1: INVENTORY THE CHANGES**
- Record: `tools/testing/selftests/net/fin_ack_lat.c` — 1 line changed
(`100000` → `1000000`). Function: `client()`. Scope: single-file, one-
line surgical fix. `git show --stat`: `1 file changed, 1 insertion(+),
1 deletion(-)`.
**Step 2.2: UNDERSTAND THE CODE FLOW CHANGE**
- Record: After `connect`/`send`/`read`, `timediff()` returns
microseconds (`tv_sec * 1000000 + tv_usec`). If `lat < threshold`,
skip the printf and close. Before: print (and the wrapper script
counts a FAIL) at ≥ 100 ms. After: only at ≥ 1 s.
**Step 2.3: IDENTIFY THE BUG MECHANISM**
- Record: [Category] logic/correctness (wrong constant). [Mechanism]
threshold was 10× too low vs documented 1 s spike that matches
`TCP_TIMEOUT_INIT` (1×HZ). Wrapper `fin_ack_lat.sh` fails if any spike
line is printed.
**Step 2.4: ASSESS THE FIX QUALITY**
- Record: Obviously correct vs the original commit text and vs
`timediff()` units. Minimal. Regression risk is that 100 ms–1 s events
would no longer FAIL; that is intended, because those are not the
FIN/ACK race (which waits a full initial RTO of 1 s). No
API/locking/kernel-runtime change.
---
## PHASE 3: GIT HISTORY INVESTIGATION
**Step 3.1: BLAME THE CHANGED LINES**
- Record: Line 72 (`if (lat < 100000)`) blamed to `af8c8a450bf469`
(SeongJae Park, 2020-02-02). Introduced with the test; never changed
since.
**Step 3.2: FOLLOW THE FIXES: TAG**
- Committed message has no `Fixes:` tag. Mailing-list v3 cited `Fixes:
af8c8a450bf4`.
- `git merge-base --is-ancestor af8c8a450bf4 HEAD` → yes (exit 0).
- Original test message: “if the latency is larger than 1 second
(spike), print a message.”
- Related kernel fix `9603d47bad464` (“tcp: Reduce SYN resend delay if a
suspicous ACK is received”) is also an ancestor; it describes the 1 s
`TIMEOUT_INIT` reconnection spike this test is for.
**Step 3.3: CHECK FILE HISTORY**
- Record: Only one commit on this file in this tree: `af8c8a450bf46`. No
later refactor. Standalone, not patch X/Y.
**Step 3.4: CHECK THE AUTHOR'S OTHER COMMITS**
- Record: `git log --author="Brian Grech" --
tools/testing/selftests/net/` is empty in this tree (author is not a
6.12 net-selftest regular). Reviewer Horman and committer Kicinski are
netdev maintainers/reviewers.
**Step 3.5: CHECK FOR DEPENDENT/PREREQUISITE COMMITS**
- Record: None. Only changes a numeric constant. `git apply --check` of
`2373107782a9` succeeds on this tree.
---
## PHASE 4: MAILING LIST AND EXTERNAL RESEARCH
**Step 4.1: FIND THE ORIGINAL PATCH DISCUSSION**
- Record: `b4 dig -c 2373107782a9` →
https://patch.msgid.link/20260806151645.4172900-1-bgrech@redhat.com
- Revisions (`b4 dig -a`):
- v1: 2026-07-31,
https://patch.msgid.link/20260731145914.4014912-1-bgrech@redhat.com
- v2: 2026-08-04 (added maintainer CCs)
- v3: 2026-08-06 (applied)
- Horman (2026-08-06): looks good; “might be nice” to explain why 1 s;
“if this is a fix to be backported (I am unsure)” then add `Fixes:`
and target `net`. **Reviewed-by** given; no NAK.
- Author replied and sent v3 with original-commit wording, `Fixes:`, and
`Cc: stable`.
- Applied to netdev/net-next by Jakub Kicinski (`2373107782a9`). No NAKs
in the saved thread.
**Step 4.2: CHECK WHO REVIEWED THE PATCH**
- Record (`b4 dig -w`): netdev, linux-kselftest, Shuah Khan, Eric
Dumazet, Paolo Abeni, Simon Horman, Jakub Kicinski. Right lists and
maintainers.
**Step 4.3: SEARCH FOR THE BUG REPORT**
- Record: No syzbot/bugzilla. Evidence is the commit text plus a Red Hat
author describing loaded/slower-hardware false positives. Severity:
incorrect kselftest FAIL, not a kernel oops.
**Step 4.4: CHECK FOR RELATED PATCHES AND SERIES**
- Record: Standalone 1-patch series. No sibling patches required.
**Step 4.5: CHECK STABLE MAILING LIST HISTORY**
- Record: lore.kernel.org/stable search via WebFetch returned 403. Web
search did not find a stable-list rejection or discussion of this fix.
UNVERIFIED: no confirmed stable@ thread beyond v3 CCing stable.
---
## PHASE 5: CODE SEMANTIC ANALYSIS
**Step 5.1: KEY FUNCTIONS**
- Record: `client()` only.
**Step 5.2: TRACE CALLERS**
- Record: `main()` forks and the child calls `client(port)`. Userspace-
only. `fin_ack_lat.sh` runs `./fin_ack_lat` for 30s and FAILs if any
stdout spike line appears. Makefile: `TEST_PROGS += fin_ack_lat.sh`,
`TEST_GEN_FILES += fin_ack_lat` — part of default net selftests.
**Step 5.3: TRACE CALLEES**
- Record: socket/connect/send/read, `timediff()`, `printf` on spike. No
kernel helpers.
**Step 5.4: FOLLOW THE CALL CHAIN**
- Record: Reachable by running the net kselftest (localhost TCP). Not a
syscall bug; it is the test harness for the 2020 FIN/ACK race.
**Step 5.5: SEARCH FOR SIMILAR PATTERNS**
- Record: Only this one comparison. `timediff()` already uses `*
1000000` (µs), which confirms 1000000 = 1 s.
---
## PHASE 6: CROSS-REFERENCING AGAINST THE LOCAL TREE
**Step 6.1: DOES THE BUGGY CODE EXIST IN THIS TREE?**
- Record: Yes. This checkout is **6.12.111** (`pending-6.12`,
`p-6.12-3-gc504cf0baa3d8`). `fin_ack_lat.c:72` is still `if (lat <
100000)`. Introduced 2020-02 (`af8c8a450bf4`); present throughout
6.12. Candidate `2373107782a9` is **not** an ancestor of HEAD.
**Step 6.2: CHECK FOR BACKPORT COMPLICATIONS**
- Record: `git diff af8c8a450bf4 HEAD --
tools/testing/selftests/net/fin_ack_lat.c` is empty. `git apply
--check` of the candidate succeeds. Expected: **clean apply**.
**Step 6.3: CHECK IF RELATED FIXES ARE ALREADY HERE**
- Record: `git log --grep="fin_ack_lat: fix latency"` and
`--grep="latency threshold typo"` on that path: empty. No alternate
fix.
---
## PHASE 7: SUBSYSTEM AND MAINTAINER CONTEXT
**Step 7.1: SUBSYSTEM AND CRITICALITY**
- Record: `tools/testing/selftests/net/` — kselftest, not kernel
runtime. Criticality: **PERIPHERAL** for production machines;
**IMPORTANT** for people who run net selftests on 6.12 (distro CI, LTS
validation).
**Step 7.2: SUBSYSTEM ACTIVITY**
- Record: Net selftests are active in 6.12; this **file** is untouched
since 2020 (mature, long-standing typo).
---
## PHASE 8: IMPACT AND RISK ASSESSMENT
**Step 8.1: WHO IS AFFECTED**
- Record: Anyone running `tools/testing/selftests/net`
(`fin_ack_lat.sh`). Not production TCP. Config: standard net
kselftests.
**Step 8.2: TRIGGER CONDITIONS**
- Record: 30 s of localhost connect/send/read. False FAIL when a loop
exceeds 100 ms (loaded CI, slow VM, embedded). Unprivileged users can
run the test; this is not a security trigger.
**Step 8.3: FAILURE MODE SEVERITY**
- Record: Incorrect kselftest FAIL (script: `FAIL: N spikes detected`).
No oops, hang, or corruption. Severity: **MEDIUM** for testers/CI,
**LOW** for production runtime. The kernel bug the test is meant to
catch is a ~1 s SYN retry (`TCP_TIMEOUT_INIT` = `1*HZ` in
`include/net/tcp.h`).
**Step 8.4: RISK-BENEFIT RATIO**
- Record: Benefit: stops false FAILs on 6.12 net selftests; restores the
1 s meaning the test was written for. Risk: one constant in userspace;
no kernel change. Ratio: **high benefit for testers / very low risk**.
---
## PHASE 9: FINAL SYNTHESIS
**Step 9.1: EVIDENCE**
FOR:
- Real, documented mismatch: commit `af8c8a450bf4` says 1 s; code uses
100 ms.
- `timediff()` is in microseconds; 1000000 µs is 1 s.
- Kernel symptom being tested is a 1 s initial RTO (`TCP_TIMEOUT_INIT`),
not 100 ms.
- Wrapper treats any printed spike as FAIL, so the typo causes false
failures.
- File and bug exist in this 6.12 tree unchanged since 2020.
- One-line, applies cleanly, userspace-only.
- Reviewed-by Horman; applied by Kicinski.
- Author (Red Hat) describes slower/loaded-system false positives — a
real “bothers people” case for LTS testing.
AGAINST:
- Not a kernel crash, UAF, deadlock, or CVE.
- Horman was unsure about backport before v3.
- Applied via net-next; committed message dropped `Fixes:`/`Cc: stable`.
- Only testers are affected.
UNRESOLVED:
- Could not read lore.kernel.org/stable (403). No evidence of a stable
rejection.
**Step 9.2: STABLE RULES CHECKLIST**
1. Obviously correct and tested? **PASS** — units, original docs, and
TCP_TIMEOUT_INIT all agree; reviewed.
2. Fixes a real bug that affects users? **PASS** — false kselftest FAILs
on slower/loaded hardware; Red Hat author.
3. Important issue? **PASS** (as “oh, that's not good” / broken default
net selftest), not as a production crash. Official `stable-kernel-
rules.rst` allows a real bug that bothers people, not only
oops/hang/CVE.
4. Small and contained? **PASS** — 1 line, 1 file.
5. No new features or APIs? **PASS**.
6. Can apply to this tree? **PASS** — clean apply; buggy code present.
**Step 9.3: EXCEPTION CATEGORIES**
- Record: Not a device-ID/quirk/DT/build/docs exception. Closest
analogue: **test/tooling correctness** (same spirit as a build fix:
helps people who build and test this kernel).
**Step 9.4: DECISION**
This tree is **6.12.111**. The typo is in-tree, the 1 s threshold is
what the test and the TCP bug both mean, and the patch is a one-line
userspace correction with no kernel regression surface. That is
appropriate for 6.12 stable: it stops false net-selftest failures
without changing runtime behavior.
---
## What the commit solves for stable users
On 6.12, `fin_ack_lat` is a default net selftest. It is supposed to flag
the ~1 s reconnect spike from FIN/ACK processing order
(`TCP_TIMEOUT_INIT`). A missing zero makes it flag ≥100 ms instead, so
loaded or slow machines can FAIL without the actual TCP bug. Distros and
anyone running kselftests on 6.12 get noisy, wrong results.
## Stable rules / risk vs benefit
It is obviously correct, tiny, already reviewed, and applies cleanly.
Benefit is reliable tests; risk is negligible. It does not fix a
production crash, but it does fix a real, user-visible test bug in this
tree.
## Concerns
None that block a backport: no missing prerequisites, no file drift, no
kernel ABI change. Raising the threshold will ignore 100 ms–1 s events;
that matches the original design.
---
## Verification
- [Phase 1] Parsed committed tags on `2373107782a9`; Horman Reviewed-by;
Kicinski SOB; no Fixes in committed text
- [Phase 1] Original test commit `af8c8a450bf4` text: “larger than 1
second (spike)”
- [Phase 2] Diff: one comparison in `client()`, 100000 → 1000000
- [Phase 2] `timediff()` uses `tv_sec * 1000000` (microseconds) —
verified in `fin_ack_lat.c`
- [Phase 2] `fin_ack_lat.sh` FAILs if any spike line is printed —
verified
- [Phase 3] `git blame` line 72 → `af8c8a450bf469` (2020-02-02)
- [Phase 3] `git merge-base --is-ancestor af8c8a450bf4 HEAD` → yes;
`2373107782a9` → no
- [Phase 3] `9603d47bad464` (TCP SYN resend / 1 s spike) is an ancestor
- [Phase 3] File unmodified after introduction (`git diff af8c8a450bf4
HEAD -- fin_ack_lat.c` empty)
- [Phase 3] `git apply --check` of candidate → success
- [Phase 4] `b4 dig -c 2373107782a9` → lore msgid
`20260806151645.4172900-1-bgrech@redhat.com`
- [Phase 4] `b4 dig -a`: v1 (2026-07-31), v3 (2026-08-06, applied)
- [Phase 4] `b4 dig -m`: Horman Reviewed-by; unsure about backport; no
NAK; v3 added Fixes + Cc: stable on the list
- [Phase 4] Patchwork: applied to net-next as `2373107782a9`
- [Phase 4] UNVERIFIED: stable@ archive (lore 403)
- [Phase 5] `client()` called only from forked `main()`; Makefile lists
the test in `TEST_PROGS`
- [Phase 5] `TCP_TIMEOUT_INIT` is `1*HZ` in `include/net/tcp.h`
- [Phase 6] `git describe HEAD` → `p-6.12-3-gc504cf0baa3d8`; `make
kernelversion` → 6.12.111; line 72 still `100000`
- [Phase 8] Failure mode: kselftest false FAIL, not kernel oops;
severity MEDIUM for testers
**YES**
tools/testing/selftests/net/fin_ack_lat.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/tools/testing/selftests/net/fin_ack_lat.c b/tools/testing/selftests/net/fin_ack_lat.c
index 70187494b57af..4117332eb1a9c 100644
--- a/tools/testing/selftests/net/fin_ack_lat.c
+++ b/tools/testing/selftests/net/fin_ack_lat.c
@@ -69,7 +69,7 @@ static void client(int port)
lat = timediff(start, end);
sum_lat += lat;
nr_lat++;
- if (lat < 100000)
+ if (lat < 1000000)
goto close;
if (getsockname(sock, (struct sockaddr *)&laddr, &len) == -1)
--
2.53.0
^ permalink raw reply related [flat|nested] 32+ messages in thread* [PATCH AUTOSEL 6.18-5.10] netfilter: ebtables: bound num_counters like nentries in do_replace()
2026-10-19 13:44 [PATCH AUTOSEL 6.18-6.12] net: dsa: mt7530: check CORE_PLL_GROUP4 access in mt7531_setup() Sasha Levin
` (17 preceding siblings ...)
2026-10-19 13:49 ` [PATCH AUTOSEL 6.18-5.10] selftests/net: fin_ack_lat: fix latency threshold typo Sasha Levin
@ 2026-10-19 13:50 ` Sasha Levin
2026-10-19 13:50 ` [PATCH AUTOSEL 6.18-6.6] xsk: account ring allocations to memcg Sasha Levin
` (10 subsequent siblings)
29 siblings, 0 replies; 32+ messages in thread
From: Sasha Levin @ 2026-10-19 13:50 UTC (permalink / raw)
To: patches, stable
Cc: Jiayuan Chen, Florian Westphal, Sasha Levin, pablo, razor, idosch,
davem, edumazet, kuba, pabeni, netfilter-devel, coreteam, bridge,
netdev, linux-kernel
From: Jiayuan Chen <jiayuan.chen@linux.dev>
[ Upstream commit 43ae85af154b27f9a0ff602f7a01e3a7583ffdab ]
do_replace_finish() allocates the counter buffer before it is validated:
counterstmp = vmalloc_array(repl->num_counters, sizeof(*counterstmp));
do_replace() only checks num_counters against INT_MAX / sizeof(struct
ebt_counter), so vmalloc_array() can be asked for up to 134217726 * 16 =
2147483616 bytes (~2 GiB).
num_counters must in fact equal nentries: do_replace_finish() later
rejects the request when repl->num_counters != t->private->nentries.
get_counters() folds the per-CPU counters back into one entry per rule,
so what userspace gets is bounded by nentries, never by nentries *
nr_cpus. Apply the same upper bound used for nentries (MAX_EBT_ENTRIES)
to the incoming num_counters so the over-sized allocation can no longer
be requested.
The allocation is still kept outside the ebt_mutex, since vmalloc() may
sleep and trigger reclaim; only the bound is tightened.
Signed-off-by: Jiayuan Chen <jiayuan.chen@linux.dev>
Signed-off-by: Florian Westphal <fw@strlen.de>
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
LLM Generated explanations, may be completely bogus:
This is a real allocate-before-validate bound bug in legacy ebtables
`do_replace()`. The local tree is **6.18.53**; the buggy checks are
present here and the fix is small enough to apply cleanly.
===============================================================
PHASE 1: COMMIT MESSAGE FORENSICS
===============================================================
Step 1.1: PARSE THE SUBJECT LINE
- Subsystem: `netfilter: ebtables`
- Action verb: **bound** (limit an existing userspace-controlled size)
- Intent: cap `num_counters` with the same ceiling already used for
`nentries` in `do_replace()`
Record: [netfilter/ebtables] [bound] [tighten `num_counters` so
`do_replace()` cannot request a ~2 GiB counter `vmalloc`]
Step 1.2: PARSE ALL COMMIT MESSAGE TAGS
- Signed-off-by: Jiayuan Chen \<jiayuan.chen@linux.dev\> (author)
- Signed-off-by: Florian Westphal \<fw@strlen.de\> (netfilter/ebtables
maintainer; applied the patch)
- Signed-off-by: Sasha Levin — **ignored** (pipeline)
- `[Upstream commit 43ae85af154b…]` — **ignored** (pipeline)
- No Fixes:, Reported-by:, Tested-by:, Reviewed-by:, Acked-by:, Link:,
or Cc: stable
Record: Author SOB + maintainer SOB only. Absence of Fixes/Cc-stable is
expected and not a negative.
Step 1.3: ANALYZE THE COMMIT BODY TEXT
Bug: `do_replace_finish()` does `vmalloc_array(repl->num_counters,
sizeof(*counterstmp))` **before** checking `repl->num_counters !=
t->private->nentries`. `do_replace()` only rejects `num_counters >=
INT_MAX / sizeof(struct ebt_counter)`, so the allocation can be up to
`134217726 * 16 = 2147483616` bytes (~2 GiB). Semantically
`num_counters` must equal the current table’s `nentries`;
`get_counters()` returns one summed entry per rule, not per-CPU.
Symptom: a crafted `EBT_SO_SET_ENTRIES` sockopt can force a huge
`vmalloc` and reclaim/OOM, then the request is rejected.
Root cause: allocate-then-validate; the `nentries` overflow formula is
much tighter than the `num_counters` formula.
Record: [oversized counter vmalloc before validation] [memory exhaustion
/ reclaim DoS] [bug present since the 2010 overflow checks] [same
MAX_EBT_ENTRIES ceiling as nentries]
Step 1.4: DETECT HIDDEN BUG FIXES
Not disguised. Explicit bound-check fix for a userspace-controlled
allocation. Extracting the existing `nentries` formula into
`MAX_EBT_ENTRIES` is mechanical, not a hidden refactor.
Record: [Direct bug fix, not a hidden cleanup]
===============================================================
PHASE 2: DIFF ANALYSIS - LINE BY LINE
===============================================================
Step 2.1: INVENTORY THE CHANGES
File: `net/bridge/netfilter/ebtables.c` — 6 insertions, 6 deletions.
Functions: file-scope `#define MAX_EBT_ENTRIES`; `do_replace()`;
`compat_copy_ebt_replace_from_user()`.
Record: [1 file, ±6 lines] [do_replace,
compat_copy_ebt_replace_from_user] [single-file surgical fix]
Step 2.2: UNDERSTAND THE CODE FLOW CHANGE
Hunk 1: existing `nentries` overflow expression becomes
`MAX_EBT_ENTRIES` (same formula).
Hunk 2 (`do_replace`): `num_counters >= INT_MAX / sizeof(struct
ebt_counter)` → `num_counters >= MAX_EBT_ENTRIES`.
Hunk 3 (compat): same `num_counters` change.
Normal sockopt replace path, before any allocation of `newinfo` /
`counterstmp`.
Record: [before: num_counters allowed up to ~134M; after: same ceiling
as nentries]
Step 2.3: IDENTIFY THE BUG MECHANISM
Category: memory-safety / resource-exhaustion bound check (not UAF,
leak, or race).
Mechanism: `num_counters` is independent of sockopt `len` (`len` only
covers `sizeof(ebt_replace) + entries_size`). A small blob plus a huge
`num_counters` still reaches `vmalloc_array()`. `vmalloc_array()` uses
`check_mul_overflow`, so this is not an integer-wrap heap overflow; it
is a ~2 GiB allocation request. Later `do_replace_finish()` rejects
mismatched `num_counters` and `vfree`s the buffer.
Record: [resource exhaustion] [userspace size used for vmalloc before
semantic validation]
Step 2.4: ASSESS THE FIX QUALITY
Obviously correct: `num_counters` must equal current `nentries`, and
`nentries` was already capped by this formula. Kernel-registered initial
tables (`ebtable_filter` etc.) have a handful of entries. No API/ABI
change; oversized requests now get `-ENOMEM` earlier instead of
allocating then `-EINVAL`. Allocation stays outside `ebt_mutex` (vmalloc
may sleep). Regression risk is very low.
Record: [minimal, maintainer-applied] [very low regression risk]
===============================================================
PHASE 3: GIT HISTORY INVESTIGATION
===============================================================
Step 3.1: BLAME THE CHANGED LINES
The pair of overflow checks was added in `e788759f44b2` (“netfilter:
ebtables: split do_replace into two functions”, Florian Westphal,
2010-02-04), first appearing around **v2.6.34-rc1**. The loose
`num_counters` bound has been unchanged since then (aside from a 2015
revert of an unrelated `num_counters == 0` check).
Record: [e788759f44b2, v2.6.34-rc1] [bug ~16 years old; present in this
6.18.y tree]
Step 3.2: FOLLOW THE FIXES: TAG
No Fixes: tag. The introducing commit is an ancestor of HEAD and of
`v6.18.53`.
Record: [no Fixes tag] [introducing commit is in this tree] [no
intermediate tightening of this bound]
Step 3.3: CHECK FILE HISTORY FOR RELATED CHANGES
Nearby ebtables work (`terminate table name`, `zero chainstack`, `module
names must be null-terminated`) is independent. This commit is not patch
N/M.
Record: [standalone] [no prerequisites beyond the 2010 overflow checks]
Step 3.4: CHECK THE AUTHOR'S OTHER COMMITS
Jiayuan Chen is not the ebtables maintainer. Florian Westphal applied
it. Same-period netfilter work from this author is unrelated (`nft_ct`).
Record: [regular contributor; maintainer SOB] [no dependent series]
Step 3.5: CHECK FOR DEPENDENT/PREREQUISITE COMMITS
`MAX_EBT_ENTRIES` is introduced here from an in-tree formula.
`vmalloc_array()` exists (`mm/util.c`). Compat path exists under
`CONFIG_NETFILTER_XTABLES_COMPAT`. Applies standalone.
Record: [none] [standalone apply]
===============================================================
PHASE 4: MAILING LIST AND EXTERNAL RESEARCH
===============================================================
Step 4.1: FIND THE ORIGINAL PATCH DISCUSSION
`b4 dig -c 43ae85af154b` →
https://patch.msgid.link/20260610030259.194456-1-jiayuan.chen@linux.dev
Subject: `[PATCH nf-next] netfilter: ebtables: bound num_counters like
nentries in do_replace()`
v1 only (`b4 dig -a`). Mbox is the patch itself; **no replies, NAKs, or
Cc: stable**. Applied as-is by Florian on 2026-07-02. Merged via `nf-
next-26-07-02`; pull text: “Bound num_counters in ebtables: do_replace()
by MAX_EBT_ENTRIES to prevent oversized vmalloc_array() allocations.”
Record: [msgid 20260610030259.194456-1] [v1 only] [no review comments]
[sent to nf-next, not nf] [no stable nomination in thread]
Step 4.2: CHECK WHO REVIEWED THE PATCH
`b4 dig -w`: To netfilter-devel@vger.kernel.org and the author only.
Florian’s SOB is the review.
Record: [netfilter-devel + maintainer applied]
Step 4.3: SEARCH FOR THE BUG REPORT
No Reported-by, syzbot, bugzilla, or CVE in the commit or thread. Bug is
derived from code analysis in the message (exact 2 GiB arithmetic).
Record: [no external bug report] [mechanism documented in the patch]
Step 4.4: CHECK FOR RELATED PATCHES AND SERIES
Standalone v1. iptables still uses `INT_MAX / sizeof(xt_counters)` plus
`xt_counters_alloc()`’s extra `XT_MAX_TABLE_SIZE` (512 MiB) cap.
ebtables had no equivalent extra cap before this patch.
Record: [standalone] [iptables already had a tighter allocator cap]
Step 4.5: CHECK STABLE MAILING LIST HISTORY
lore.kernel.org HTTP fetch returned 403. `b4 dig` mbox has no stable@
traffic. No evidence it was rejected for stable.
Record: [no stable-list discussion found; lore HTTP 403]
===============================================================
PHASE 5: CODE SEMANTIC ANALYSIS
===============================================================
Step 5.1: IDENTIFY KEY FUNCTIONS IN THE DIFF
Record: [`do_replace`, `compat_copy_ebt_replace_from_user`; allocation
in `do_replace_finish`]
Step 5.2: TRACE CALLERS
`do_ebt_set_ctl()` (CAP_NET_ADMIN via `ns_capable(net->user_ns,
CAP_NET_ADMIN)`) → `EBT_SO_SET_ENTRIES` → `do_replace()` /
`compat_do_replace()` → `compat_copy_ebt_replace_from_user()` then
`do_replace_finish()`.
Record: [privileged sockopt; user_ns capable] [not IRQ; process context]
Step 5.3: TRACE CALLEES
`vmalloc_array()` → overflow-checked `__vmalloc`. `get_counters()`
memcpy/sum into `nentries` slots. `copy_to_user` of `num_counters`
entries only after the match check.
Record: [vmalloc_array, get_counters, copy_to_user]
Step 5.4: FOLLOW THE CALL CHAIN
Reachable from `setsockopt` with `EBT_SO_SET_ENTRIES`. `len` need not
include the counter array. Unprivileged user namespaces that have
CAP_NET_ADMIN in that netns can call it if
`CONFIG_BRIDGE_NF_EBTABLES_LEGACY` is loaded. `find_table_lock` failure
still happens **after** the `counterstmp` allocation.
Record: [userspace sockopt] [reachable; userns-relevant]
Step 5.5: SEARCH FOR SIMILAR PATTERNS
`do_update_counters()` also `vmalloc_array(num_counters)` but `len` must
equal `sizeof(hlp) + num_counters * sizeof(ebt_counter)`, so the sockopt
size bounds it. `copy_counters_to_user()` allocates from validated
`nentries`. Sibling not the same bug; this commit does not need to wait
for a series.
Record: [replace path is the unbounded one]
===============================================================
PHASE 6: CROSS-REFERENCING AGAINST THE LOCAL TREE
===============================================================
Step 6.1: DOES THE BUGGY CODE EXIST IN THIS TREE?
Local tree: `git describe HEAD` = `v6.18.53-5049-g0f0bdc2b0dffb`; `make
kernelversion` = **6.18.53**; Makefile VERSION/PATCHLEVEL/SUBLEVEL =
6.18.53.
At `v6.18.53`, `do_replace()` still has the old `INT_MAX / sizeof(struct
ebt_counter)` check. `git merge-base --is-ancestor 43ae85af154b
v6.18.53` fails (fix not in 6.18.53). Introducing commit **is** in
6.18.53.
Record: [yes, both native and compat replace] [introduced v2.6.34-rc1]
Step 6.2: CHECK FOR BACKPORT COMPLICATIONS
The overflow-check lines at `v6.18.53` match the pre-image of this
patch. `MAX_EBT_ENTRIES` is new here. Clean apply expected.
Record: [clean apply]
Step 6.3: CHECK IF RELATED FIXES ARE ALREADY HERE
No earlier equivalent bound on `num_counters`. Name-termination commits
nearby do not fix this.
Record: [no alternate fix in 6.18.y]
===============================================================
PHASE 7: SUBSYSTEM AND MAINTAINER CONTEXT
===============================================================
Step 7.1: IDENTIFY THE SUBSYSTEM AND ITS CRITICALITY
`net/bridge/netfilter/ebtables.c`, `CONFIG_BRIDGE_NF_EBTABLES_LEGACY`
(default n). Still used by docker/libvirt/firewalld-style bridge
filtering on distro kernels that enable it.
Record: [netfilter/ebtables legacy] [IMPORTANT for those configs, not
core mm/VFS]
Step 7.2: ASSESS SUBSYSTEM ACTIVITY
Mature (2002-era) with occasional correctness/security fixes. Not churny
new code.
Record: [mature/stable]
===============================================================
PHASE 8: IMPACT AND RISK ASSESSMENT
===============================================================
Step 8.1: DETERMINE WHO IS AFFECTED
Systems with legacy ebtables loaded. Trigger is a sockopt, not data-
plane forwarding.
Record: [config-specific / legacy-ebtables users]
Step 8.2: DETERMINE THE TRIGGER CONDITIONS
Crafted `EBT_SO_SET_ENTRIES`: small `entries_size`, `num_counters` near
`INT_MAX/16`. Cheap. Needs CAP_NET_ADMIN in the socket’s user_ns
(`ns_capable`), so unprivileged userns can hit it when the module is
present. Not every boot; attacker- or test-triggered.
Record: [malicious/buggy replace] [easy if capable] [userns-capable, not
world-unprivileged without userns]
Step 8.3: DETERMINE THE FAILURE MODE SEVERITY
Success: ~2 GiB kernel vmalloc held through verify/translate, then freed
— OOM/reclaim. Failure: `-ENOMEM`. Not UAF, not silent corruption, not a
panic by itself. iptables caps similar allocations at 512 MiB; ebtables
could request ~4× that.
Record: [local memory-exhaustion DoS] [HIGH, not crash/corruption
CRITICAL]
Step 8.4: CALCULATE RISK-BENEFIT RATIO
Benefit: stops a documented ~2 GiB sockopt vmalloc on a long-lived path;
high for hosts that ship legacy ebtables. Risk: 6-line bound change,
same formula as `nentries`; very low.
Record: [high benefit for ebtables users] [very low risk] [favorable]
===============================================================
PHASE 9: FINAL SYNTHESIS
===============================================================
Step 9.1: COMPILE THE EVIDENCE
FOR:
- Real allocate-before-validate bug; commit shows the 2 GiB request
- Small (6/6), obviously correct, maintainer-applied
- Bug in this 6.18.y tree since v2.6.34; patch applies cleanly
- Reachable from `setsockopt`; `ns_capable` makes userns relevant
- Matches stable “oh, that’s not good” / security-DoS class
- Does not add features or APIs
AGAINST:
- Sent to **nf-next**, not `nf` (not treated as an urgent -rc fix)
- No user/syzbot/CVE report
- Legacy ebtables, Kconfig default n
- Requires CAP_NET_ADMIN in the netns user_ns
- Transient allocation (freed on the later `-EINVAL` path), not a leak
UNRESOLVED:
- lore.kernel.org WebFetch 403; thread recovered via `b4 dig -m` (patch-
only)
- No stable@ discussion found
Step 9.2: APPLY THE STABLE RULES CHECKLIST
1. Obviously correct and tested? **PASS** — same ceiling as `nentries`;
maintainer applied v1
2. Fixes a real bug that affects users? **PASS** — oversized vmalloc
from a sockopt field that is later rejected
3. Important (crash/security/corruption/deadlock)? **PASS** — local DoS
/ “oh, that’s not good” memory exhaustion, including userns
CAP_NET_ADMIN; not a panic
4. Small and contained? **PASS** — 6 lines, one file
5. No new features or APIs? **PASS**
6. Can apply to the local tree? **PASS** — pre-image matches v6.18.53
Step 9.3: CHECK FOR EXCEPTION CATEGORIES
Not a device ID, quirk, DT, build, or docs exception. Ordinary small
bugfix.
Step 9.4: MAKE YOUR DECISION
nf-next targeting and lack of a reporter argue it is hardening rather
than an emergency, but the defect is concrete, old, reachable, and the
patch is the lowest-risk way to close a ~2 GiB kernel allocation. That
is appropriate for 6.18.y.
What the commit solves for stable users: a crafted ebtables replace can
ask `vmalloc` for up to ~2 GiB of counter scratch space before the
kernel checks that `num_counters` equals the table’s `nentries`.
Tightening that field to `MAX_EBT_ENTRIES` stops the request without
moving vmalloc under `ebt_mutex`.
Risk vs benefit: benefit is preventing reclaim/OOM on a privileged (and
userns-capable) sockopt; risk is a stricter `-ENOMEM` on values that
could never succeed the later equality check.
Verification:
- [Phase 1] Parsed tags: author + Florian Westphal SOB only; no
Fixes/Reported-by/Cc-stable
- [Phase 1] Body: 134217726 * 16 = 2147483616 documented; verified
`sizeof(struct ebt_counter)` is two `__u64` in `ebtables.h`
- [Phase 2] Diff: 6/6 in `ebtables.c`; `do_replace` + compat copy;
`MAX_EBT_ENTRIES` is the old nentries formula
- [Phase 2] Pre-fix at `c06e4b417f61^` and `v6.18.53`: `num_counters >=
INT_MAX / sizeof(struct ebt_counter)`
- [Phase 2] `do_replace_finish()` still allocates then checks
`num_counters != t->private->nentries` (lines 1025–1056)
- [Phase 2] `get_counters()` copies/sums `nentries` entries only (lines
992–1011)
- [Phase 3] `git log -L` on the overflow checks: introduced
`e788759f44b2` (2010-02-04); `git describe --contains` → v2.6.34-rc1
- [Phase 3] `git merge-base --is-ancestor e788759f44b2 v6.18.53` exit 0;
`43ae85af154b` not ancestor of v6.18.53
- [Phase 3] No MAX_EBT_ENTRIES before this commit (`git log -S
MAX_EBT_ENTRIES`)
- [Phase 4] `b4 dig -c 43ae85af154b`: msgid 20260610030259.194456-1; nf-
next; v1 only
- [Phase 4] `b4 dig -w`: netfilter-devel only; `b4 dig -m`: 110-line
mbox, no replies
- [Phase 4] Florian nf-next pull `b73bc9ca3686b` item 11 describes
oversized vmalloc_array prevention
- [Phase 4] UNVERIFIED: stable@ lore search (HTTP 403)
- [Phase 5] Callers: `do_ebt_set_ctl` requires `ns_capable(...,
CAP_NET_ADMIN)`; `EBT_SO_SET_ENTRIES`
- [Phase 5] `vmalloc_array` uses `check_mul_overflow`
(`mm/util.c:656-662`) — overflow-safe, still ~2 GiB
- [Phase 5] `xt_counters_alloc` extra cap `XT_MAX_TABLE_SIZE` 512 MiB;
ebtables lacked that
- [Phase 6] `git describe HEAD` v6.18.53-5049; kernelversion 6.18.53
- [Phase 6] `git show v6.18.53:net/bridge/netfilter/ebtables.c` still
has the loose num_counters checks at 1132 and 2275
- [Phase 7] `obj-$(CONFIG_BRIDGE_NF_EBTABLES_LEGACY) += ebtables.o`;
Kconfig default n
- [Phase 8] Failure mode: vmalloc/reclaim/OOM DoS, not oops/corruption
- UNVERIFIED: whether a specific distro 6.18 config enables
BRIDGE_NF_EBTABLES_LEGACY
The patch meets the stable rules for this 6.18.y tree: real, important,
small, and it applies.
**YES**
net/bridge/netfilter/ebtables.c | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/net/bridge/netfilter/ebtables.c b/net/bridge/netfilter/ebtables.c
index 96176aa881117..5266f4d021c6d 100644
--- a/net/bridge/netfilter/ebtables.c
+++ b/net/bridge/netfilter/ebtables.c
@@ -39,6 +39,8 @@
#define COUNTER_OFFSET(n) (SMP_ALIGN(n * sizeof(struct ebt_counter)))
#define COUNTER_BASE(c, n, cpu) ((struct ebt_counter *)(((char *)c) + \
COUNTER_OFFSET(n) * cpu))
+#define MAX_EBT_ENTRIES (((INT_MAX - sizeof(struct ebt_table_info)) / \
+ NR_CPUS - SMP_CACHE_BYTES) / sizeof(struct ebt_counter))
struct ebt_pernet {
struct list_head tables;
@@ -1126,10 +1128,9 @@ static int do_replace(struct net *net, sockptr_t arg, unsigned int len)
return -EINVAL;
/* overflow check */
- if (tmp.nentries >= ((INT_MAX - sizeof(struct ebt_table_info)) /
- NR_CPUS - SMP_CACHE_BYTES) / sizeof(struct ebt_counter))
+ if (tmp.nentries >= MAX_EBT_ENTRIES)
return -ENOMEM;
- if (tmp.num_counters >= INT_MAX / sizeof(struct ebt_counter))
+ if (tmp.num_counters >= MAX_EBT_ENTRIES)
return -ENOMEM;
tmp.name[sizeof(tmp.name) - 1] = 0;
@@ -2269,10 +2270,9 @@ static int compat_copy_ebt_replace_from_user(struct ebt_replace *repl,
if (tmp.entries_size == 0)
return -EINVAL;
- if (tmp.nentries >= ((INT_MAX - sizeof(struct ebt_table_info)) /
- NR_CPUS - SMP_CACHE_BYTES) / sizeof(struct ebt_counter))
+ if (tmp.nentries >= MAX_EBT_ENTRIES)
return -ENOMEM;
- if (tmp.num_counters >= INT_MAX / sizeof(struct ebt_counter))
+ if (tmp.num_counters >= MAX_EBT_ENTRIES)
return -ENOMEM;
memcpy(repl, &tmp, offsetof(struct ebt_replace, hook_entry));
--
2.53.0
^ permalink raw reply related [flat|nested] 32+ messages in thread* [PATCH AUTOSEL 6.18-6.6] xsk: account ring allocations to memcg
2026-10-19 13:44 [PATCH AUTOSEL 6.18-6.12] net: dsa: mt7530: check CORE_PLL_GROUP4 access in mt7531_setup() Sasha Levin
` (18 preceding siblings ...)
2026-10-19 13:50 ` [PATCH AUTOSEL 6.18-5.10] netfilter: ebtables: bound num_counters like nentries in do_replace() Sasha Levin
@ 2026-10-19 13:50 ` Sasha Levin
2026-10-19 13:51 ` [PATCH AUTOSEL 6.18-5.10] net: mvneta_bm: add suspend/resume support to prevent crash after resume Sasha Levin
` (9 subsequent siblings)
29 siblings, 0 replies; 32+ messages in thread
From: Sasha Levin @ 2026-10-19 13:50 UTC (permalink / raw)
To: patches, stable
Cc: Zihan Xi, Stanislav Fomichev, Jakub Kicinski, Sasha Levin,
magnus.karlsson, maciej.fijalkowski, davem, edumazet, pabeni,
netdev, bpf, linux-kernel
From: Zihan Xi <zihanx@nebusec.ai>
[ Upstream commit b0057c68df711bf6a62033c072ac61c4f9d3cbc1 ]
AF_XDP rings are allocated from setsockopt() and can be mapped into user
space. The shared xskq_create() helper allocates the ring backing memory,
but the user-controlled and long-lived allocation is not charged as kmem
to the allocating memory cgroup.
The current implementation uses vmalloc_user(), which allocates the
backing pages with GFP_KERNEL | __GFP_ZERO. Use the same VM_USERMAP
vmalloc path, but pass GFP_KERNEL_ACCOUNT so the ring backing pages are
attributed to memcg/kmem and can be constrained by existing cgroup memory
limits. This keeps the existing zeroing and mmap semantics while avoiding
AF_XDP-specific optmem or RLIMIT_MEMLOCK accounting.
Signed-off-by: Zihan Xi <zihanx@nebusec.ai>
Acked-by: Stanislav Fomichev <sdf@fomichev.me>
Link: https://patch.msgid.link/20260731164623.4694-2-zihanx@nebusec.ai
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
LLM Generated explanations, may be completely bogus:
The local tree is **6.12.111** (`pending-6.12`). The AF_XDP ring path
still uses unaccounted `vmalloc_user()`, so this is a real memcg/DoS
hole in this checkout, not a no-op.
===============================================================
PHASE 1: COMMIT MESSAGE FORENSICS
===============================================================
**Step 1.1: PARSE THE SUBJECT LINE**
- Subsystem: `xsk` (AF_XDP / `net/xdp`)
- Action verb: **account** (resource-accounting fix, not a refactor)
- Claimed intent: charge AF_XDP ring backing pages to memcg/kmem
Record: `[xsk] [account] charge AF_XDP ring allocations to memcg`
**Step 1.2: PARSE ALL COMMIT MESSAGE TAGS**
From the candidate message:
- `Acked-by: Stanislav Fomichev <sdf@fomichev.me>`
- `Link:
https://patch.msgid.link/20260731164623.4694-2-zihanx@nebusec.ai`
- `Signed-off-by: Zihan Xi <zihanx@nebusec.ai>`
- `Signed-off-by: Jakub Kicinski <kuba@kernel.org>`
- Ignore: any `Signed-off-by: Sasha Levin` (pipeline)
From the original v3 submission (mailing list, not treated as “already
selected”):
- `Fixes: 423f38329d26 ("xsk: add umem fill queue support and mmap")`
- `Cc: stable@vger.kernel.org`
- `Reported-by: Vega <vega@nebusec.ai>`
- `Assisted-by: Codex:gpt-5.4`
No syzbot, no Tested-by. Absence of Fixes/Cc:stable on the candidate
copy is expected and is not a NO signal.
Record: Ack from XDP/BPF reviewer; original submission had Fixes +
Cc:stable + Reported-by; applied by netdev maintainer.
**Step 1.3: ANALYZE THE COMMIT BODY TEXT**
Bug: `xskq_create()` allocates mmapable ring memory from `setsockopt()`
with `vmalloc_user()` (`GFP_KERNEL | __GFP_ZERO`). That allocation is
user-controlled, long-lived, and **not charged to memcg/kmem**.
Symptom: a task with `CAP_NET_RAW` in a user+net namespace can request
very large RX/TX/fill/completion rings **before bind or packet I/O**.
Cover letter includes a PoC (`unshare -Urn ./poc`, 1<<25 entries) and an
OOM panic on **6.12.95** with `panic_on_oom`.
Root cause: missing `__GFP_ACCOUNT` on the VM_USERMAP vmalloc path.
Author explicitly says panic_on_oom is environment-specific; the defect
is missing kmem attribution.
Record: `[Bug: unaccounted userspace-triggered vmalloc of AF_XDP rings]
[Symptom: memcg-limit bypass / global OOM] [Version: repro on 6.12.95]
[Cause: vmalloc_user uses GFP_KERNEL, not GFP_KERNEL_ACCOUNT]`
**Step 1.4: DETECT HIDDEN BUG FIXES**
Not disguised. It is a missing-accounting fix. Kernel policy in
`Documentation/core-api/memory-allocation.rst` says untrusted userspace
allocations **must** set `__GFP_ACCOUNT`.
Record: `[Not hidden; explicit resource-accounting / isolation bug]`
===============================================================
PHASE 2: DIFF ANALYSIS - LINE BY LINE
===============================================================
**Step 2.1: INVENTORY THE CHANGES**
File: `net/xdp/xsk_queue.c` only (~+11 / -1)
- Add `#include <asm/shmparam.h>`
- Add `xskq_vmalloc_user()`
- In `xskq_create()`: `vmalloc_user(size)` → `xskq_vmalloc_user(size)`
`xskq_destroy()` still `vfree()`s the ring (uncharge is automatic on
page free).
Record: `[1 file, ~12 lines] [xskq_vmalloc_user added; xskq_create
modified] [single-file surgical]`
**Step 2.2: UNDERSTAND THE CODE FLOW CHANGE**
Before: `vmalloc_user()` → `__vmalloc_node_range(..., GFP_KERNEL |
__GFP_ZERO, PAGE_KERNEL, VM_USERMAP, ...)`
After: same VM_USERMAP/SHMLBA/zeroing/mmap path, GFP flag is
`GFP_KERNEL_ACCOUNT | __GFP_ZERO`.
Affected path: `setsockopt(SOL_XDP,
XDP_{RX,TX,UMEM_FILL,UMEM_COMPLETION}_RING)` → `xsk_setsockopt` →
`xsk_init_queue` → `xskq_create`. Normal allocation path, not an error-
path-only change.
Record: `[before: unaccounted vmalloc_user] [after: identical vmalloc,
charged to memcg]`
**Step 2.3: IDENTIFY THE BUG MECHANISM**
Category: resource accounting / isolation (userspace-triggered kernel
memory not charged to kmemcg). Secondary failure mode: OOM /
panic_on_oom.
Mechanism: `GFP_KERNEL` vs `GFP_KERNEL_ACCOUNT` (`GFP_KERNEL |
__GFP_ACCOUNT`). In this tree, `__vmalloc_node_range_noprof()` honors
`__GFP_ACCOUNT` (page alloc + `MEMCG_VMALLOC` stat).
Not UAF, leak, race, or bounds check.
Record: `[resource accounting] [uncharged mmapable ring pages; fix is
GFP_KERNEL_ACCOUNT]`
**Step 2.4: ASSESS THE FIX QUALITY**
Obviously correct relative to `vmalloc_user()` in `mm/vmalloc.c` (same
args except GFP). No new uAPI. `vfree()` already drops the pages, so no
extra unaccount helper (unlike rejected v2 `mm_account_pinned_pages`).
Regression risk: AF_XDP in a **tight memcg** can now get `-ENOMEM` where
it previously escaped the limit. That is the intended isolation. No
lock/API/lifetime change.
Stanislav noted dislike of open-coding `__vmalloc_node_range`; same
pattern already used for mmapable BPF maps after
`vmalloc_user_node_flags` was removed. Acceptable.
Record: `[minimal, matches vmalloc_user] [low regression risk; memcg
ENOMEM is intended]`
===============================================================
PHASE 3: GIT HISTORY INVESTIGATION
===============================================================
**Step 3.1: BLAME THE CHANGED LINES**
- Ring helper/mmap: `423f38329d267` (2018, Magnus Karlsson) — “xsk: add
umem fill queue support and mmap”
- `vmalloc_user` switch: `9f78bf330a66c` (2023, Xuan Zhuo) — “xsk:
support use vaddr as ring”
- Overflow guard: `a12bbb3cccf03` (2023)
Buggy missing accounting dates to the original mmapable queue (2018).
`vmalloc_user` only changed the allocator.
Record: `[introduced conceptually in 423f38329d267 (v4.18-era);
vmalloc_user since 9f78bf330a66c / v6.3]`
**Step 3.2: FOLLOW THE FIXES: TAG**
`423f38329d267` **is an ancestor of this HEAD** (`git merge-base --is-
ancestor` exit 0). Original code used `__get_free_pages`; accounting was
missing then too.
Record: `[Fixes target in this tree] [no later in-tree substitute for
ring memcg charge]`
**Step 3.3: CHECK FILE HISTORY FOR RELATED CHANGES**
Recent `net/xdp/xsk_queue.c`: overflow check, vmalloc conversion, header
moves. No existing memcg/optmem/RLIMIT charge on rings.
Series history (mailing list): v1 optmem + socket lifetime coupling; v2
RLIMIT_MEMLOCK; **v3 is standalone 1/1** memcg-only.
Record: `[standalone v3; no in-tree prereq refactor]`
**Step 3.4: CHECK THE AUTHOR'S OTHER COMMITS**
Zihan Xi is not an XDP maintainer. Reviewer Stanislav Fomichev is a
regular XDP/BPF reviewer and Acked v3. Applied by Jakub Kicinski.
Record: `[author: reporter/fixer; reviewer: subsystem regular;
maintainer applied]`
**Step 3.5: CHECK FOR DEPENDENT/PREREQUISITE COMMITS**
Needs `__vmalloc_node_range`, `SHMLBA`, `VM_USERMAP`,
`GFP_KERNEL_ACCOUNT`. All present in 6.12.
Local `xskq_create` uses `kzalloc(sizeof(*q), GFP_KERNEL)` vs mainline
`kzalloc_obj(*q)` — **not in the changed hunks**. Functional diff
applies.
Record: `[no functional dependencies] [applies standalone with trivial
context]`
===============================================================
PHASE 4: MAILING LIST AND EXTERNAL RESEARCH
===============================================================
**Step 4.1: FIND THE ORIGINAL PATCH DISCUSSION**
`b4 dig -c <msgid>` could not match a local commit (patch not in this
tree). `b4 mbox` retrieved the 4-message thread.
- Cover: https://lists.openwall.net/netdev/2026/07/31/395
- Patch: https://lists.openwall.net/netdev/2026/07/31/392
- Review: https://lists.openwall.net/netdev/2026/07/31/437
v1 optmem, v2 RLIMIT_MEMLOCK, v3 memcg. Applied version is v3 (latest).
No NAK. Stanislav Acked. Patchwork-bot accepted it.
Author nominated `Cc: stable` on v3. Reviewer did not object.
Record: `[lore msgid 20260731164623.4694-2] [v1→v3, v3 applied] [Ack, no
NAK] [author Cc:stable]`
**Step 4.2: CHECK WHO REVIEWED THE PATCH**
To/Cc: netdev, bpf, Magnus Karlsson, Maciej Fijalkowski, Stanislav
Fomichev, davem, edumazet, pabeni, horms, ast, daniel, hawk,
john.fastabend.
Stanislav reviewed v2 (pushed memcg over RLIMIT_MEMLOCK) and Acked v3.
Record: `[Stanislav Fomichev Ack; XDP maintainers were CC'd]`
**Step 4.3: SEARCH FOR THE BUG REPORT**
Internal report (`Reported-by: Vega`) plus full PoC and panic log on
6.12.95. Trigger: `socket(AF_XDP)` + `setsockopt` with 2^25 entries,
`unshare -Urn`. No syzbot/bugzilla.
Record: `[reproducible OOM/panic on 6.12.95; userns+CAP_NET_RAW]`
**Step 4.4: CHECK FOR RELATED PATCHES AND SERIES**
v3 is 0/1 + 1/1. No extra patches required. v1/v2 approaches were
dropped.
Record: `[self-contained; earlier series versions superseded]`
**Step 4.5: CHECK STABLE MAILING LIST HISTORY**
No useful hit on lore.kernel.org/stable for this subject. No recorded
rejection for 6.12.y.
Record: `[no stable-list rejection found]`
===============================================================
PHASE 5: CODE SEMANTIC ANALYSIS
===============================================================
**Step 5.1: IDENTIFY KEY FUNCTIONS IN THE DIFF**
Record: `[xskq_vmalloc_user (new), xskq_create (modified)]`
**Step 5.2: TRACE CALLERS**
`xskq_create` is only called from `xsk_init_queue()` in `net/xdp/xsk.c`,
from `xsk_setsockopt` for the four ring options. Process context,
syscall.
`xsk_create()` requires `ns_capable(net->user_ns, CAP_NET_RAW)` —
**available to an unprivileged user inside a user namespace**.
Record: `[setsockopt path; userns CAP_NET_RAW]`
**Step 5.3: TRACE CALLEES**
`__vmalloc_node_range` → page alloc with GFP; `vfree` on destroy. No new
locks.
Record: `[vmalloc/vfree only]`
**Step 5.4: FOLLOW THE CALL CHAIN**
`socket(AF_XDP)` → `setsockopt(SOL_XDP, XDP_*_RING, &entries)` →
`xskq_create`.
`xsk_init_queue` only checks `entries != 0`, power of two, and queue not
already set. **No upper bound.** `xdp_desc` is 16 bytes; 2^25 entries ≈
512 MiB per RX/TX ring (fill/completion similar with `u64` descs). Eight
sockets × four rings is multi-GB kernel vmalloc.
Reachable from userspace: **yes**.
Record: `[userspace-reachable unbounded vmalloc]`
**Step 5.5: SEARCH FOR SIMILAR PATTERNS**
UMEM pages are charged via `user->locked_vm`
(`xdp_umem_unaccount_pages`). BPF mmapable maps use
`__vmalloc_node_range` with accounting flags. Rings were the hole.
`vmalloc_user()` elsewhere (kcov, perf, videobuf) is a separate
question; this patch only fixes AF_XDP.
Record: `[UMEM already bounded; rings were not; BPF already accounts
mmapable vmalloc]`
===============================================================
PHASE 6: CROSS-REFERENCING AGAINST THE LOCAL TREE
===============================================================
**Step 6.1: DOES THE BUGGY CODE EXIST IN THIS TREE?**
Yes. `net/xdp/xsk_queue.c:49` is `q->ring = vmalloc_user(size);`. `make
kernelversion` = **6.12.111**. Repro log is 6.12.95. `423f38329d267` and
`9f78bf330a66c` are ancestors. No `xskq_vmalloc_user` /
`GFP_KERNEL_ACCOUNT` in `net/xdp`.
Record: `[buggy code present; bug since 2018 / vmalloc path since ~6.3]`
**Step 6.2: CHECK FOR BACKPORT COMPLICATIONS**
Expected apply: **clean / trivial context**. Only surrounding difference
is `kzalloc` vs `kzalloc_obj`. `asm/shmparam.h` exists
(`arch/x86/include/asm/shmparam.h`, `include/asm-generic/shmparam.h`).
`__vmalloc_node_range` macro exists in `include/linux/vmalloc.h`.
Record: `[clean apply or one-hunk context tweak]`
**Step 6.3: CHECK IF RELATED FIXES ARE ALREADY HERE**
No memcg/optmem/RLIMIT accounting on ring create in this tree.
Record: `[no substitute fix present]`
===============================================================
PHASE 7: SUBSYSTEM AND MAINTAINER CONTEXT
===============================================================
**Step 7.1: IDENTIFY THE SUBSYSTEM AND ITS CRITICALITY**
`net/xdp` — AF_XDP. Config: `CONFIG_XDP_SOCKETS` (depends on
`BPF_SYSCALL`, default n).
Criticality: **IMPORTANT** (not core mm/VFS). Used for high-performance
networking; also a userns attack surface when enabled.
Record: `[net/xdp AF_XDP] [IMPORTANT]`
**Step 7.2: ASSESS SUBSYSTEM ACTIVITY**
Mature since 2018; ongoing XDP work. This is an old hole, not a bug in
brand-new code.
Record: `[mature; hole is long-standing]`
===============================================================
PHASE 8: IMPACT AND RISK ASSESSMENT
===============================================================
**Step 8.1: DETERMINE WHO IS AFFECTED**
Config-specific (`CONFIG_XDP_SOCKETS`) plus anyone who can create AF_XDP
sockets. Isolation impact is highest for **containers / memcg-limited
workloads** on LTS kernels.
Record: `[config-specific AF_XDP; high impact for cgroup/container
hosts]`
**Step 8.2: DETERMINE THE TRIGGER CONDITIONS**
`setsockopt` of a large power-of-two ring. No bind, no traffic.
Unprivileged in a user ns with `CAP_NET_RAW`. Easy and deterministic.
Caveat (verified from author): **without a memcg limit**,
`GFP_KERNEL_ACCOUNT` does not stop global OOM (`CONSTRAINT_NONE` in
their prototype). Enforcement is cgroup memory policy.
Record: `[easy syscall trigger] [fix enforces memcg, not a global hard
cap]`
**Step 8.3: DETERMINE THE FAILURE MODE SEVERITY**
- With memcg limits (typical k8s/LTS): bypass of memory.max / kmem →
**container isolation hole** (HIGH/CRITICAL for multi-tenant)
- With panic_on_oom: **kernel panic** (CRITICAL in that config)
- Without memcg limits: still global OOM killer after the patch
Record: `[memcg bypass + OOM] [HIGH; CRITICAL under panic_on_oom /
multi-tenant]`
**Step 8.4: CALCULATE RISK-BENEFIT RATIO**
Benefit: closes documented-required kmem accounting; lets existing
cgroup limits constrain a userspace-triggered multi-hundred-MB vmalloc;
matches BPF-style mmapable alloc policy.
Risk: ~12 lines, GFP flag only, one file. Possible `-ENOMEM` for
oversized rings inside a small memcg — correct.
Record: `[benefit high for container/LTS] [risk very low] [favorable]`
===============================================================
PHASE 9: FINAL SYNTHESIS
===============================================================
**Step 9.1: COMPILE THE EVIDENCE**
FOR:
- Real, documented policy violation: untrusted userspace alloc without
`__GFP_ACCOUNT`
- Concrete PoC and panic on **6.12.95**, this series
- Reachable via userns `CAP_NET_RAW` + `setsockopt`, no bind needed
- Unbounded ring size (power-of-two only)
- Small, obviously equivalent to `vmalloc_user` + account flag
- APIs and buggy code exist in this 6.12.111 tree
- Acked by Stanislav Fomichev; applied by Jakub Kicinski
- Standalone (v3 dropped optmem/RLIMIT machinery)
- UMEM already resource-bounded; rings were the gap
- Author asked for stable; reviewer did not object
AGAINST:
- Does **not** by itself stop global OOM if there is no memcg limit
(author confirmed)
- `CONFIG_XDP_SOCKETS` is not default-on
- Open-codes `__vmalloc_node_range` (reviewer accepted)
- Can change success/failure of huge rings inside tight memcgs
UNRESOLVED:
- Exact mainline SHA of the applied commit (not in this checkout; thread
was retrieved by msgid)
- No stable-list discussion found
**Step 9.2: APPLY THE STABLE RULES CHECKLIST**
1. Obviously correct and tested? **PASS** — same vmalloc path as
`vmalloc_user`; PoC/discussion; maintainer Ack
2. Fixes a real bug that affects users? **PASS** — missing kmem charge;
OOM/isolation on 6.12
3. Important issue? **PASS** — memcg isolation hole and OOM
(panic_on_oom in the report)
4. Small and contained? **PASS** — one file, ~12 lines
5. No new features or APIs? **PASS** — GFP flag only, not a new uAPI
6. Can apply to the local tree? **PASS** — code and helpers present
**Step 9.3: CHECK FOR EXCEPTION CATEGORIES**
Not a device-ID/quirk/DT/docs exception. Ordinary resource-accounting
bug fix.
Record: `[no exception category; qualifies on primary rules]`
**Step 9.4: MAKE YOUR DECISION**
This is a small, correct fix for a userspace-reachable hole that this
6.12 tree still has. LTS hosts that run AF_XDP and cgroups (containers)
need the charge so memory.max actually applies. Incomplete as a *global*
OOM cap is not a reason to keep rings unaccounted; kernel policy already
requires `__GFP_ACCOUNT`, and that is exactly what this patch adds.
===============================================================
DETAILED EXPLANATION
===============================================================
**Problem:** `xskq_create()` backs AF_XDP rings with `vmalloc_user()`
(`GFP_KERNEL`). `setsockopt()` can request an arbitrary power-of-two
entry count with no memcg charge. In a user namespace that grant
`CAP_NET_RAW`, that is a large, long-lived kernel allocation that
bypasses cgroup kmem limits. The report includes a panic on 6.12.95
under `panic_on_oom`.
**Why it matters here:** This checkout is 6.12.111; the `vmalloc_user()`
call is still present. 6.12.y is an LTS line used with cgroups. Charging
the pages is what makes existing memory limits apply to this path.
**Rules:** Surgical, no new API, no series prereq, applies with at most
trivial `kzalloc` context. Reviewer-directed design (memcg, not
optmem/RLIMIT).
**Risk vs benefit:** Benefit is isolation/OOM under memcg; risk is a GFP
flag on one alloc. `vfree()` already releases pages. The remaining gap
(no memcg → still global OOM) is a property of memcg, not a reason to
skip the charge.
**Concerns:** Incomplete without cgroup limits; XDP sockets are
optional. Neither outweighs backporting the accounting the kernel
already requires for untrusted userspace allocations.
===============================================================
Verification:
===============================================================
- [Phase 1] Parsed subject/body/tags from the candidate message;
mailing-list v3 also had Fixes: 423f38329d26, Cc: stable, Reported-by:
Vega
- [Phase 2] Diff is one file: helper wrapping `__vmalloc_node_range`
with `GFP_KERNEL_ACCOUNT | __GFP_ZERO`, VM_USERMAP, SHMLBA — compared
to `vmalloc_user_noprof()` in mm/vmalloc.c:4063-4068
- [Phase 2] Confirmed `GFP_KERNEL_ACCOUNT` is `GFP_KERNEL |
__GFP_ACCOUNT` in include/linux/gfp_types.h:379
- [Phase 2] Confirmed vmalloc honors `__GFP_ACCOUNT` in
mm/vmalloc.c:3734-3738
- [Phase 3] `git blame net/xdp/xsk_queue.c`: vmalloc_user from
9f78bf330a66c; helper from 423f38329d267
- [Phase 3] `git merge-base --is-ancestor 423f38329d267 HEAD` → 0;
`9f78bf330a66c` → 0
- [Phase 3] `git log --oneline -20 -- net/xdp/xsk_queue.c`: no memcg
accounting already present
- [Phase 3] No `GFP_KERNEL_ACCOUNT` under net/xdp in this tree
- [Phase 4] `b4 dig -c <msgid>`: no local commit match; `b4 mbox` saved
4-message thread
- [Phase 4] Thread: v3 patch, Stanislav Ack, patchwork-bot, cover
letter; no NAK
- [Phase 4] Openwall cover+review: PoC, 6.12.95 panic, memcg vs RLIMIT
debate
- [Phase 4] WebSearch of lore.kernel.org/stable: no rejection thread
found (UNVERIFIED: lore search 403 on some URLs)
- [Phase 5] Callers: only `xsk_init_queue` ← `xsk_setsockopt` for four
ring optnames
- [Phase 5] `xsk_create`: `ns_capable(net->user_ns, CAP_NET_RAW)` at
net/xdp/xsk.c:1742
- [Phase 5] `xsk_init_queue`: no max entries, only power-of-two
(net/xdp/xsk.c:1061-1062)
- [Phase 5] `struct xdp_desc` is 16 bytes
(include/uapi/linux/if_xdp.h:155-159)
- [Phase 6] `git describe HEAD` → p-6.12-2-gd38d58a4e0359; `make
kernelversion` → 6.12.111
- [Phase 6] Current file still has `q->ring = vmalloc_user(size)` at
line 49
- [Phase 6] `__vmalloc_node_range` and `asm/shmparam.h` exist in this
tree
- [Phase 7] CONFIG_XDP_SOCKETS in net/xdp/Kconfig, default n, depends on
BPF_SYSCALL
- [Phase 8] UMEM uses locked_vm in xdp_umem.c; rings do not
- [Phase 8] Documentation/core-api/memory-allocation.rst:53-56 requires
__GFP_ACCOUNT for untrusted userspace allocs
- UNVERIFIED: mainline commit SHA (patch not in this checkout)
- UNVERIFIED: whether a later mainline follow-up added a hard ring-size
cap (not in this tree)
The patch is a small, correct memcg charge for a userspace-reachable
AF_XDP allocation that this 6.12 tree still leaves unaccounted.
**YES**
net/xdp/xsk_queue.c | 12 +++++++++++-
1 file changed, 11 insertions(+), 1 deletion(-)
diff --git a/net/xdp/xsk_queue.c b/net/xdp/xsk_queue.c
index d2c2640300171..7069830943ea8 100644
--- a/net/xdp/xsk_queue.c
+++ b/net/xdp/xsk_queue.c
@@ -9,6 +9,8 @@
#include <linux/vmalloc.h>
#include <net/xdp_sock_drv.h>
+#include <asm/shmparam.h>
+
#include "xsk_queue.h"
static size_t xskq_get_ring_size(struct xsk_queue *q, bool umem_queue)
@@ -21,6 +23,14 @@ static size_t xskq_get_ring_size(struct xsk_queue *q, bool umem_queue)
return struct_size(rxtx_ring, desc, q->nentries);
}
+static void *xskq_vmalloc_user(unsigned long size)
+{
+ return __vmalloc_node_range(size, SHMLBA, VMALLOC_START, VMALLOC_END,
+ GFP_KERNEL_ACCOUNT | __GFP_ZERO, PAGE_KERNEL,
+ VM_USERMAP, NUMA_NO_NODE,
+ __builtin_return_address(0));
+}
+
struct xsk_queue *xskq_create(u32 nentries, bool umem_queue)
{
struct xsk_queue *q;
@@ -46,7 +56,7 @@ struct xsk_queue *xskq_create(u32 nentries, bool umem_queue)
size = PAGE_ALIGN(size);
- q->ring = vmalloc_user(size);
+ q->ring = xskq_vmalloc_user(size);
if (!q->ring) {
kfree(q);
return NULL;
--
2.53.0
^ permalink raw reply related [flat|nested] 32+ messages in thread* [PATCH AUTOSEL 6.18-5.10] net: mvneta_bm: add suspend/resume support to prevent crash after resume
2026-10-19 13:44 [PATCH AUTOSEL 6.18-6.12] net: dsa: mt7530: check CORE_PLL_GROUP4 access in mt7531_setup() Sasha Levin
` (19 preceding siblings ...)
2026-10-19 13:50 ` [PATCH AUTOSEL 6.18-6.6] xsk: account ring allocations to memcg Sasha Levin
@ 2026-10-19 13:51 ` Sasha Levin
2026-10-19 13:51 ` [PATCH AUTOSEL 6.18-5.10] net: stmmac: use dma_addr_t for DMA addresses Sasha Levin
` (8 subsequent siblings)
29 siblings, 0 replies; 32+ messages in thread
From: Sasha Levin @ 2026-10-19 13:51 UTC (permalink / raw)
To: patches, stable
Cc: Yun Zhou, Paolo Abeni, Sasha Levin, marcin.s.wojtas,
andrew+netdev, davem, edumazet, kuba, netdev, linux-kernel
From: Yun Zhou <yun.zhou@windriver.com>
[ Upstream commit 140be217df577961bea1ca36240439a463026cbd ]
The mvneta driver uses the hardware Buffer Manager (BM) for RX buffer
allocation. During suspend, mvneta disables its clock, causing BM to
lose all buffer address state. On resume, mvneta_bm_port_init() re-
attaches the BM pool to the NIC, but BM hardware returns stale/garbage
buffer addresses. When NAPI poll processes these buffers, DMA cache
sync hits an invalid virtual address causing a kernel panic:
Unable to handle kernel paging request at virtual address b0000080
PC is at v7_dma_inv_range
Call trace:
v7_dma_inv_range from arch_sync_dma_for_cpu+0x94/0x158
arch_sync_dma_for_cpu from __dma_sync_single_for_cpu+0xc4/0x15c
__dma_sync_single_for_cpu from mvneta_rx_swbm+0x6c8/0xf48
mvneta_rx_swbm from mvneta_poll+0x6fc/0x70c
mvneta_poll from __napi_poll.constprop.0+0x2c/0x1e0
__napi_poll.constprop.0 from net_rx_action+0x160/0x2c4
net_rx_action from handle_softirqs+0xd8/0x2b8
handle_softirqs from run_ksoftirqd+0x30/0x94
run_ksoftirqd from smpboot_thread_fn+0x100/0x204
smpboot_thread_fn from kthread+0xf4/0x110
kthread from ret_from_fork+0x14/0x28
Fix by adding suspend/resume callbacks to the BM driver:
- suspend: drain all buffers (with DMA unmapping), free the BPPE
regions, and reset pool state to FREE before stopping BM and gating
the clock.
- resume: enable the clock, reinitialize BM defaults, and restore pool
read/write pointers and size registers. Pool allocation and buffer
refill are handled by mvneta_resume() through the normal
mvneta_bm_port_init() path, which sees pools as FREE and performs
full initialization identical to probe.
Add a device_link (DL_FLAG_AUTOREMOVE_CONSUMER) in mvneta_probe to
guarantee BM resumes before mvneta and suspends after mvneta. If the
link cannot be created, fall back to SW buffer management to avoid a
potential crash on resume due to unordered PM transitions.
Signed-off-by: Yun Zhou <yun.zhou@windriver.com>
Link: https://patch.msgid.link/20260630060311.4072140-1-yun.zhou@windriver.com
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
LLM Generated explanations, may be completely bogus:
This is a real resume panic in an existing driver, and the local 7.3-rc4
tree already has the affected `mvneta`/`mvneta_bm` code. The patch is a
contained PM completion, not a new API.
===============================================================
PHASE 1: COMMIT MESSAGE FORENSICS
===============================================================
**Step 1.1: PARSE THE SUBJECT LINE**
Record: `[net/mvneta_bm] [add] add suspend/resume support to prevent
crash after resume`
Subsystem prefix is `net: mvneta_bm`. The verb is “add”, but the rest of
the subject names a crash after resume. That is a bug fix framed as
adding the missing PM callbacks.
**Step 1.2: PARSE ALL COMMIT MESSAGE TAGS**
Record:
- `Signed-off-by: Yun Zhou <yun.zhou@windriver.com>` (author)
- `Link: https://patch.msgid.link/20260630060311.4072140-1-
yun.zhou@windriver.com`
- `Signed-off-by: Paolo Abeni <pabeni@redhat.com>` (netdev maintainer,
applied it)
- No `Fixes:` tag (expected for this review)
- No `Cc: stable@vger.kernel.org` (expected)
- No `Reported-by:`, `Tested-by:`, `Reviewed-by:`, or `Acked-by:`
- Notable: author is Wind River, so this came from a commercial embedded
product, not a theoretical cleanup
**Step 1.3: ANALYZE THE COMMIT BODY TEXT**
Record:
- **Bug:** Marvell NETA hardware Buffer Manager (BM) has no
suspend/resume. `mvneta_suspend()` gates the NETA clock; BM loses
buffer-address state. On resume, `mvneta_bm_port_init()` reattaches
pools while BM still returns stale/garbage addresses.
- **Symptom:** kernel panic: `Unable to handle kernel paging request at
virtual address b0000080` in `v7_dma_inv_range` ←
`arch_sync_dma_for_cpu` ← `__dma_sync_single_for_cpu` ←
`mvneta_rx_swbm` ← `mvneta_poll`.
- **Version info:** none in the message. BM exists since v4.6; `mvneta`
PM exists since v4.12.
- **Root cause (author):** clock gating drops BM register/pool state;
resume reuses that garbage in NAPI RX DMA sync.
- **Fix:** BM suspend drains/unmaps/frees pools and sets type `FREE`; BM
resume re-clocks and restores registers; `mvneta_resume()` then re-
inits pools via the existing `mvneta_bm_port_init()` path. A
`device_link` orders BM before `mvneta` on resume.
**Step 1.4: DETECT HIDDEN BUG FIXES**
Record: Not a hidden fix. The subject says “add … support”, but the body
is an oops-on-resume report with a full ARM stack. Adding PM callbacks
here is completing missing power-management, which is an allowed stable
class (hardware/PM workaround), not a new userspace feature.
===============================================================
PHASE 2: DIFF ANALYSIS - LINE BY LINE
===============================================================
**Step 2.1: INVENTORY THE CHANGES**
Record:
- `drivers/net/ethernet/marvell/mvneta.c`: +18 (probe only)
- `drivers/net/ethernet/marvell/mvneta_bm.c`: +72
- Total: **90 insertions, 0 deletions** (under the stable ~100-line
guideline)
- Functions: `mvneta_probe`; new `mvneta_bm_suspend`,
`mvneta_bm_resume`; `mvneta_bm_pool_create` error paths;
`mvneta_bm_driver.driver.pm`
- Scope: single-subsystem surgical change (Marvell NETA + its BM
companion)
**Step 2.2: UNDERSTAND THE CODE FLOW CHANGE**
Record:
1. **`mvneta_probe` BM attach:** after successful
`mvneta_bm_port_init()`, add `device_link_add(mvneta, BM,
DL_FLAG_AUTOREMOVE_CONSUMER)`. On failure, destroy long/short pools,
`mvneta_bm_put()`, clear `pp->bm_priv` and fall back to SW BM.
Before: HW BM could be used with unordered PM vs BM.
2. **`mvneta_bm_pool_create` error paths:** after `dma_free_coherent()`,
set `bm_pool->virt_addr = NULL`. Before: dangling pointer; suspend’s
`dma_free_coherent()` could double-free.
3. **New `mvneta_bm_suspend`:** for each non-FREE pool,
`mvneta_bm_bufs_free()`, disable pool, free BPPE,
`mutex_destroy(&buf_lock)`, set type `FREE`; then
`MVNETA_BM_STOP_MASK` and `clk_disable_unprepare()`. Before: BM had
no suspend; clock/state left live or stale.
4. **New `mvneta_bm_resume`:** `clk_prepare_enable()`,
`mvneta_bm_default_set()`, restore per-pool read/write pointers and
size, `MVNETA_BM_START_MASK`. Pool refill stays in existing
`mvneta_resume()` → `mvneta_bm_port_init()`.
5. **Driver `.pm`:** `DEFINE_SIMPLE_DEV_PM_OPS` + `pm_sleep_ptr()`.
Before: no PM ops on the BM platform driver.
**Step 2.3: IDENTIFY THE BUG MECHANISM**
Record: `[Hardware PM / stale DMA addresses after resume]` plus a small
`[double-free hardening]` on `virt_addr`.
Mechanism: BM hardware state does not survive clock/power loss. Resume
still programmed the NIC to consume BM buffers, so NAPI DMA-synced
garbage (`b0000080`) and oopsed. Suspend now tears pools down to `FREE`;
resume brings BM up first (device_link); `mvneta_bm_pool_use()` sees
`FREE` and does full probe-style alloc/fill.
**Step 2.4: ASSESS THE FIX QUALITY**
Record: Quality is good after v2–v6. Drain + unmap, BPPE free, type
reset, clock error check, register restore, device_link with SW-BM
fallback, `mutex_destroy` paired with `mutex_init` in
`mvneta_bm_pool_use()`, and `virt_addr` NULL on alloc failure are all
consistent with existing `mvneta_bm_pool_destroy()` / probe paths.
Regression risk is **low–medium**: new PM callbacks can leak if drain is
incomplete (`dev_warn` if `buf_num` remains), and `mutex_destroy`
assumes no concurrent `hwbm_pool_add()` (the device_link is what makes
that true). No public API or core-kernel change. Paolo applied it; no
NAK in the thread.
Verified call order with the link (consumer=`mvneta`, supplier=`BM`):
mvneta suspends first, BM suspends second; BM resumes first, mvneta
resumes second. That matches the code: `mvneta_suspend()` does **not**
destroy BM pools; `mvneta_bm_suspend()` does; `mvneta_resume()` calls
`mvneta_bm_port_init()` only after BM resume.
===============================================================
PHASE 3: GIT HISTORY INVESTIGATION
===============================================================
**Step 3.1: BLAME THE CHANGED LINES**
Record:
- BM attach in probe: `dc35a10f68d37` (Marcin Wojtas, 2016-03-14) “net:
mvneta: bm: add support for hardware buffer management”, **v4.6-rc1**
- Probe BM get/init structure: `965cbbec7f207` (Gregory CLEMENT,
2018-07-18)
- `mvneta` suspend/resume: `9768b45ceb0bc` “net: mvneta: support suspend
and resume”, **v4.12-rc1**
- Buggy situation (PM on neta, none on BM) has been in tree since
**v4.12** (~2017)
**Step 3.2: FOLLOW THE FIXES: TAG**
Record: No `Fixes:` tag. The missing BM PM has been there since
`9768b45ceb0bc` (v4.12) against BM from `dc35a10f68d37` (v4.6). Both are
ancestors of this checkout (`git merge-base --is-ancestor` not needed
for those; they are in file history here).
**Step 3.3: CHECK FILE HISTORY FOR RELATED CHANGES**
Record:
- Same author, nearby: `fd398d6480987` “net: mvneta: re-enable percpu
interrupt on resume” (independent resume IRQ bug, already in this
tree)
- After this commit: `ede0f99cef53a` “net: mvneta: bm: fix device
reference leak on failed lookup” — different `mvneta_bm_get()` leak,
not a revert/fix of this PM code
- No “patch X/Y” on this commit. Standalone.
**Step 3.4: CHECK THE AUTHOR'S OTHER COMMITS**
Record: Yun Zhou is not the mvneta maintainer; two recent mvneta resume
fixes in this tree. Original BM author Marcin Wojtas was on Cc. Applied
by Paolo Abeni.
**Step 3.5: CHECK FOR DEPENDENT/PREREQUISITE COMMITS**
Record:
- `mutex_destroy` on `hwbm_pool.buf_lock` needs the mutex conversion
`6dcdd884e2a4b` (**v5.3-rc1**). Present here (`include/net/hwbm.h` has
`struct mutex buf_lock`).
- `DEFINE_SIMPLE_DEV_PM_OPS` / `pm_sleep_ptr`: `1a3c7bb088266`
(**v5.17**). Present here.
- `DL_FLAG_AUTOREMOVE_CONSUMER`: renamed in **v4.19**. Present here.
- `priv->pdev` is set in `mvneta_bm_probe()` before drvdata is
published, so `pp->bm_priv->pdev->dev` in `device_link_add` is valid.
- Can apply standalone **in this tree**. No missing helpers.
===============================================================
PHASE 4: MAILING LIST AND EXTERNAL RESEARCH
===============================================================
**Step 4.1: FIND THE ORIGINAL PATCH DISCUSSION**
Record:
- `b4 dig -c 140be217df577`: https://patch.msgid.link/20260630060311.407
2140-1-yun.zhou@windriver.com
- Series: **v2 → v6** (no v1 in b4’s match list)
- v2 2026-06-14, v3 06-16, v4 06-18, v5 06-25, v6 06-30
- Changelog (from v6 mbox): v2 added drain/device_link; v3 restored pool
registers and `DEFINE_SIMPLE_DEV_PM_OPS`; v4 SW-BM fallback if link
fails; v5 disable-before-free; v6 `mutex_destroy` + `virt_addr = NULL`
- Applied to **netdev/net-next.git**, not `net` (feature-sized PM
addition, but still a crash fix)
- No NAK. Paolo’s only follow-up: sashiko flagged **pre-existing**
issues, asked for a later follow-up — not a NAK of this patch
- No reviewer `Cc: stable` in the v6 thread
**Step 4.2: CHECK WHO REVIEWED THE PATCH**
Record (`b4 dig -w`): Yun Zhou; Marcin Wojtas; Andrew Lunn; Dave Miller;
Eric Dumazet; Jakub Kicinski; Paolo Abeni; netdev; linux-kernel. Right
lists/maintainers. Human review of earlier versions was mostly sashiko
notes forwarded by Simon Horman (v2) and Jakub Kicinski (v5) as
“plausible”. Paolo applied v6.
**Step 4.3: SEARCH FOR THE BUG REPORT**
Record: No bugzilla/syzbot link. Evidence is the ARM oops in the commit
and Wind River authorship. lore.kernel.org HTTP fetch returned 403;
thread was recovered via `b4 dig -m`.
**Step 4.4: CHECK FOR RELATED PATCHES AND SERIES**
Record: Single-patch series. Related but independent: `fd398d6480987`
(percpu IRQ on resume). Not a prerequisite for this diff.
**Step 4.5: CHECK STABLE MAILING LIST HISTORY**
Record: lore.kernel.org/stable HTTP 403. Local `git log` on these files
shows no stable-list discussion of this subject. UNVERIFIED whether
anyone already requested/rejected it on stable@.
===============================================================
PHASE 5: CODE SEMANTIC ANALYSIS
===============================================================
**Step 5.1: IDENTIFY KEY FUNCTIONS IN THE DIFF**
Record: `mvneta_probe`, `mvneta_bm_pool_create`, `mvneta_bm_suspend`
(new), `mvneta_bm_resume` (new), `mvneta_bm_driver` pm pointer.
**Step 5.2: TRACE CALLERS**
Record:
- `mvneta_bm_suspend`/`resume`: platform PM via `dev_pm_ops` (system
suspend/hibernate)
- `mvneta_bm_port_init` ← `mvneta_probe` and `mvneta_resume` (verified
in `mvneta.c`)
- `mvneta_bm_pool_use` ← only `mvneta_bm_port_init` (long and short
pools)
- `mvneta_poll` ← NAPI; chooses `mvneta_rx_hwbm` if `pp->bm_priv` else
`mvneta_rx_swbm`
**Step 5.3: TRACE CALLEES**
Record: suspend uses `mvneta_bm_bufs_free` (DMA unmap +
`hwbm_buf_free`), `mvneta_bm_pool_disable`, `dma_free_coherent`,
`mutex_destroy`, `clk_disable_unprepare`. Resume uses
`clk_prepare_enable`, `mvneta_bm_default_set`, register writes, BM
START.
**Step 5.4: FOLLOW THE CALL CHAIN**
Record: Userspace/system suspend on Armada 38x/XP with
`CONFIG_MVNETA_BM` and a DT `buffer-manager` phandle. Reachable on real
HW (Wind River). Not a syscall fuzz path; it is a common S2R path on
those boards.
Note on the stack naming: `mvneta_poll` uses `mvneta_rx_hwbm` when
`bm_priv` is set. The oops shows `mvneta_rx_swbm`. Verified that
`mvneta_swbm_rx_frame()` does `dma_sync_single_for_cpu(...,
rx_desc->buf_phys_addr, ...)` and can be inlined into `mvneta_rx_swbm`.
That matches DMA-sync-of-garbage if resume fell back to SW BM
(`mvneta_resume` clears `bm_priv` when `mvneta_bm_port_init` fails)
while RX descriptors still held stale phys addrs, or if the reporter’s
kernel named the RX path differently. The BM clock-loss story is still
consistent with “invalid address in DMA sync after resume”.
**Step 5.5: SEARCH FOR SIMILAR PATTERNS**
Record: `mvneta` already had `SIMPLE_DEV_PM_OPS(mvneta_pm_ops, ...)`. BM
was the missing half. No sibling Marvell BM driver in this path.
`device_link_add` is used only in this probe site under
`drivers/net/ethernet/marvell/`.
===============================================================
PHASE 6: CROSS-REFERENCING AGAINST THE LOCAL TREE
===============================================================
**Step 6.1: DOES THE BUGGY CODE EXIST IN THIS TREE?**
Record: Yes. Tree is **v7.3-rc4** (`git describe HEAD` =
`v7.3-rc4-27-gf0100363d8c37`, `VERSION=7 PATCHLEVEL=3 SUBLEVEL=0
EXTRAVERSION=-rc4`).
- `140be217df577` **is** an ancestor of HEAD (`git merge-base --is-
ancestor` succeeded).
- Files `drivers/net/ethernet/marvell/mvneta.c` and `mvneta_bm.c` exist;
BM probe/remove, `mvneta_suspend`/`mvneta_resume`, and
`mvneta_bm_port_init` are present.
- Bug class (BM without PM while neta has PM) dates to v4.12.
**Step 6.2: CHECK FOR BACKPORT COMPLICATIONS**
Record: **Clean in this tree** — the commit is already applied. Probe BM
block has been stable in shape since 2018. Helpers used by the patch
exist here.
**Step 6.3: CHECK IF RELATED FIXES ARE ALREADY HERE**
Record: This fix is already on HEAD. No alternate older fix for BM
resume. Follow-up `ede0f99cef53a` is a different `mvneta_bm_get()` ref
leak, not a replacement for this change.
===============================================================
PHASE 7: SUBSYSTEM AND MAINTAINER CONTEXT
===============================================================
**Step 7.1: IDENTIFY THE SUBSYSTEM AND ITS CRITICALITY**
Record: `[net/ethernet/marvell mvneta + mvneta_bm] [IMPORTANT]` — not
core mm/VFS, but the onboard NIC on Armada 370/38x/XP/3700. BM is
**32-bit only** (`depends on !64BIT`), Armada 38x/XP. Crash takes down
networking after resume on those SoCs.
**Step 7.2: ASSESS SUBSYSTEM ACTIVITY**
Record: Mature driver (2016 BM, 2017 PM). Recent churn is resume fixes
(`fd398d6480987`, this commit), not a rewrite. Long-present bug, not a
brand-new subsystem.
===============================================================
PHASE 8: IMPACT AND RISK ASSESSMENT
===============================================================
**Step 8.1: DETERMINE WHO IS AFFECTED**
Record: **driver-specific / config-specific / platform-specific** —
`CONFIG_MVNETA` + `CONFIG_MVNETA_BM` on 32-bit Armada 38x/XP with DT
`buffer-manager`. That is exactly Wind River / NAS / router class
hardware. SW-BM-only boards are unaffected except for the new
device_link failure path (falls back to SW BM).
**Step 8.2: DETERMINE THE TRIGGER CONDITIONS**
Record: System suspend/resume with HW BM in use. Common on embedded
devices that sleep. Not unprivileged-syscall; still a production resume
path. Not a tiny race window.
**Step 8.3: DETERMINE THE FAILURE MODE SEVERITY**
Record: `[Kernel paging request / panic in NAPI RX] [CRITICAL]`
**Step 8.4: CALCULATE RISK-BENEFIT RATIO**
Record:
- **Benefit: high** — prevents oops and dead NIC after resume on
affected Marvell SoCs.
- **Risk: low–medium** — 90 lines, two files, new PM callbacks but they
mirror existing destroy/probe; device_link fallback is fail-safe (SW
BM).
- **Ratio:** benefit outweighs risk. Fits “oops / hardware PM” stable
policy.
===============================================================
PHASE 9: FINAL SYNTHESIS
===============================================================
**Step 9.1: COMPILE THE EVIDENCE**
FOR:
- Documented kernel panic with stack after resume
- Real product kernel (Wind River)
- Bug present since v4.12; code is in this tree
- 90 lines, two files, no new UAPI
- Completes PM that `mvneta` already had; allowed hardware/PM class
- v6 addressed drain, ordering, mutex, dangling `virt_addr`
- Accepted by Paolo Abeni; no NAK
- Under the documented ~100-line stable size cap
AGAINST:
- Landed in **net-next**, not `net` (looks feature-sized)
- “add support” wording; new callbacks rather than a 5-line tweak
- Six revisions; not trivial at v1
- No `Tested-by` besides the author
- Oops symbol is `mvneta_rx_swbm` while HW BM poll uses `mvneta_rx_hwbm`
(explained above, not a reason to drop a panic fix)
- Sashiko noted other pre-existing issues (not shown to break this
patch)
UNRESOLVED:
- Could not read lore.kernel.org/stable (HTTP 403)
- Could not open sashiko’s full pre-existing-issue list (Paolo linked
it; described as pre-existing)
**Step 9.2: APPLY THE STABLE RULES CHECKLIST**
1. Obviously correct and tested? **PASS** — v6 matches existing
destroy/probe; author hit the panic; maintainer applied it.
2. Fixes a real bug that affects users? **PASS** — resume oops on Armada
+ BM.
3. Important issue? **PASS** — kernel panic / paging request (CRITICAL).
4. Small and contained? **PASS** — 90 insertions, 2 files, one driver
pair.
5. No new features or APIs? **PASS** — no UAPI; PM ops are the missing
half of existing suspend/resume (hardware/PM workaround exception).
6. Can apply to the local tree? **PASS** — already in v7.3-rc4; helpers
exist.
**Step 9.3: CHECK FOR EXCEPTION CATEGORIES**
Record: Hardware/PM workaround (missing suspend/resume causing a crash).
Not a new driver, not a new userspace API.
**Step 9.4: MAKE YOUR DECISION**
This tree is v7.3-rc4 and already contains the mvneta BM driver, mvneta
PM, and this commit’s helpers. The change fixes a user-visible panic on
resume, is contained, and is the correct completion of PM for a driver
that already suspends. It should be backported.
------------------------------------------------
What problem it solves for stable users
------------------------------------------------
On 32-bit Marvell Armada boards that use hardware buffer management,
suspend gates clocks and the BM forgets buffer addresses. Resume
reattaches the NIC to that hardware and NAPI DMA-syncs garbage,
panicking in `v7_dma_inv_range`. After resume, Ethernet is dead and the
kernel has oopsed. Stable users of these SoCs (embedded, NAS, Wind
River-style products) hit this on ordinary S2R.
------------------------------------------------
Risk vs benefit
------------------------------------------------
Benefit is a panic fix on a real resume path. Risk is a modest amount of
new PM code, already iterated through v6 and aligned with
`mvneta_bm_pool_destroy()` / `mvneta_bm_pool_use()`. Device-link failure
falls back to SW BM instead of leaving a crashy configuration.
------------------------------------------------
Verification
------------------------------------------------
- [Phase 1] Parsed tags: Yun Zhou SOB, Paolo Abeni SOB, msgid.link; no
Fixes/Cc-stable/Reported-by
- [Phase 1] Body: ARM paging oops in DMA sync during `mvneta_poll` after
resume
- [Phase 2] Diff: +18 `mvneta.c` (device_link + SW BM fallback), +72
`mvneta_bm.c` (PM ops + virt_addr NULL)
- [Phase 2] `mvneta_poll` HW vs SW split and `dma_sync_single_for_cpu`
in `mvneta_swbm_rx_frame` verified in source
- [Phase 3] BM introduced `dc35a10f68d37` (v4.6-rc1); mvneta PM
`9768b45ceb0bc` (v4.12-rc1)
- [Phase 3] `git merge-base --is-ancestor 140be217df577 HEAD` → IS
ancestor
- [Phase 3] hwbm mutex: `6dcdd884e2a4b` (v5.3);
DEFINE_SIMPLE_DEV_PM_OPS: `1a3c7bb088266` (v5.17)
- [Phase 3] Follow-up `ede0f99cef53a` is a different leak, not a
fix/revert of this patch
- [Phase 4] `b4 dig -c 140be217df577`: v2–v6, applied to net-next; no
NAK; Paolo asked about pre-existing sashiko issues
- [Phase 4] `b4 dig -w`: netdev maintainers on To/Cc
- [Phase 4] `b4 dig -m`: v6 changelog + Paolo reply parsed from mbox
- [Phase 4] lore HTTP 403; stable@ search UNVERIFIED via WebFetch
- [Phase 5] Callers: `mvneta_bm_port_init` from probe and
`mvneta_resume` only
- [Phase 6] `git describe HEAD` = v7.3-rc4-27-gf0100363d8c37; `make
kernelversion` = 7.3.0-rc4
- [Phase 6] BM and mvneta PM files present; commit already on this
branch
- [Phase 7] Kconfig: `MVNETA_BM` depends on `!64BIT`; Armada 38x/XP
- [Phase 8] Failure mode: kernel paging request in RX NAPI after resume,
CRITICAL
- UNVERIFIED: full sashiko finding list; whether stable@ already
discussed this patch
**YES**
drivers/net/ethernet/marvell/mvneta.c | 18 ++++++
drivers/net/ethernet/marvell/mvneta_bm.c | 72 ++++++++++++++++++++++++
2 files changed, 90 insertions(+)
diff --git a/drivers/net/ethernet/marvell/mvneta.c b/drivers/net/ethernet/marvell/mvneta.c
index 71ace291a8bff..2cb6723fe801c 100644
--- a/drivers/net/ethernet/marvell/mvneta.c
+++ b/drivers/net/ethernet/marvell/mvneta.c
@@ -5684,6 +5684,24 @@ static int mvneta_probe(struct platform_device *pdev)
"use SW buffer management\n");
mvneta_bm_put(pp->bm_priv);
pp->bm_priv = NULL;
+ } else if (!device_link_add(&pdev->dev,
+ &pp->bm_priv->pdev->dev,
+ DL_FLAG_AUTOREMOVE_CONSUMER)) {
+ /*
+ * Link guarantees BM resumes before mvneta.
+ * Without it, BM may not be ready when
+ * mvneta_bm_port_init() runs on resume,
+ * causing stale buffer addresses and a crash.
+ * Fall back to SW management to be safe.
+ */
+ dev_warn(&pdev->dev,
+ "failed to link to BM, use SW buffer management\n");
+ mvneta_bm_pool_destroy(pp->bm_priv,
+ pp->pool_long, 1 << pp->id);
+ mvneta_bm_pool_destroy(pp->bm_priv,
+ pp->pool_short, 1 << pp->id);
+ mvneta_bm_put(pp->bm_priv);
+ pp->bm_priv = NULL;
}
}
/* Set RX packet offset correction for platforms, whose
diff --git a/drivers/net/ethernet/marvell/mvneta_bm.c b/drivers/net/ethernet/marvell/mvneta_bm.c
index 6bb3804949190..e0c693c0a9106 100644
--- a/drivers/net/ethernet/marvell/mvneta_bm.c
+++ b/drivers/net/ethernet/marvell/mvneta_bm.c
@@ -129,6 +129,7 @@ static int mvneta_bm_pool_create(struct mvneta_bm *priv,
if (!IS_ALIGNED((u32)bm_pool->virt_addr, MVNETA_BM_POOL_PTR_ALIGN)) {
dma_free_coherent(&pdev->dev, size_bytes, bm_pool->virt_addr,
bm_pool->phys_addr);
+ bm_pool->virt_addr = NULL;
dev_err(&pdev->dev, "BM pool %d is not %d bytes aligned\n",
bm_pool->id, MVNETA_BM_POOL_PTR_ALIGN);
return -ENOMEM;
@@ -139,6 +140,7 @@ static int mvneta_bm_pool_create(struct mvneta_bm *priv,
if (err < 0) {
dma_free_coherent(&pdev->dev, size_bytes, bm_pool->virt_addr,
bm_pool->phys_addr);
+ bm_pool->virt_addr = NULL;
return err;
}
@@ -477,6 +479,75 @@ static void mvneta_bm_remove(struct platform_device *pdev)
clk_disable_unprepare(priv->clk);
}
+static int mvneta_bm_suspend(struct device *dev)
+{
+ struct mvneta_bm *priv = dev_get_drvdata(dev);
+ int i;
+
+ /* Drain buffers and free pool resources while BM is still clocked */
+ for (i = 0; i < MVNETA_BM_POOLS_NUM; i++) {
+ struct mvneta_bm_pool *bm_pool = &priv->bm_pools[i];
+ int size_bytes;
+
+ if (bm_pool->type == MVNETA_BM_FREE)
+ continue;
+
+ mvneta_bm_bufs_free(priv, bm_pool, bm_pool->port_map);
+ if (bm_pool->hwbm_pool.buf_num)
+ dev_warn(&priv->pdev->dev,
+ "pool %d: %d buffers not freed\n",
+ bm_pool->id, bm_pool->hwbm_pool.buf_num);
+
+ mvneta_bm_pool_disable(priv, bm_pool->id);
+
+ if (bm_pool->virt_addr) {
+ size_bytes = sizeof(u32) * bm_pool->hwbm_pool.size;
+ dma_free_coherent(&priv->pdev->dev, size_bytes,
+ bm_pool->virt_addr,
+ bm_pool->phys_addr);
+ bm_pool->virt_addr = NULL;
+ }
+ /*
+ * Safe to destroy: device_link guarantees all mvneta ports
+ * have already suspended, so no hwbm_pool_add() can be in
+ * progress holding buf_lock. Pairs with mutex_init() in
+ * mvneta_bm_pool_use() on resume.
+ */
+ mutex_destroy(&bm_pool->hwbm_pool.buf_lock);
+ bm_pool->type = MVNETA_BM_FREE;
+ }
+
+ mvneta_bm_write(priv, MVNETA_BM_COMMAND_REG, MVNETA_BM_STOP_MASK);
+ clk_disable_unprepare(priv->clk);
+ return 0;
+}
+
+static int mvneta_bm_resume(struct device *dev)
+{
+ struct mvneta_bm *priv = dev_get_drvdata(dev);
+ int i, err;
+
+ err = clk_prepare_enable(priv->clk);
+ if (err)
+ return err;
+
+ /* Reinitialize BM hardware; pools are refilled by mvneta_resume() */
+ mvneta_bm_default_set(priv);
+
+ /* Restore pool registers lost during clock gating */
+ for (i = 0; i < MVNETA_BM_POOLS_NUM; i++) {
+ mvneta_bm_write(priv, MVNETA_BM_POOL_READ_PTR_REG(i), 0);
+ mvneta_bm_write(priv, MVNETA_BM_POOL_WRITE_PTR_REG(i), 0);
+ mvneta_bm_write(priv, MVNETA_BM_POOL_SIZE_REG(i),
+ priv->bm_pools[i].hwbm_pool.size);
+ }
+
+ mvneta_bm_write(priv, MVNETA_BM_COMMAND_REG, MVNETA_BM_START_MASK);
+ return 0;
+}
+
+static DEFINE_SIMPLE_DEV_PM_OPS(mvneta_bm_pm_ops, mvneta_bm_suspend, mvneta_bm_resume);
+
static const struct of_device_id mvneta_bm_match[] = {
{ .compatible = "marvell,armada-380-neta-bm" },
{ }
@@ -489,6 +560,7 @@ static struct platform_driver mvneta_bm_driver = {
.driver = {
.name = MVNETA_BM_DRIVER_NAME,
.of_match_table = mvneta_bm_match,
+ .pm = pm_sleep_ptr(&mvneta_bm_pm_ops),
},
};
--
2.53.0
^ permalink raw reply related [flat|nested] 32+ messages in thread* [PATCH AUTOSEL 6.18-5.10] net: stmmac: use dma_addr_t for DMA addresses
2026-10-19 13:44 [PATCH AUTOSEL 6.18-6.12] net: dsa: mt7530: check CORE_PLL_GROUP4 access in mt7531_setup() Sasha Levin
` (20 preceding siblings ...)
2026-10-19 13:51 ` [PATCH AUTOSEL 6.18-5.10] net: mvneta_bm: add suspend/resume support to prevent crash after resume Sasha Levin
@ 2026-10-19 13:51 ` Sasha Levin
2026-10-19 13:51 ` [PATCH AUTOSEL 6.18-6.12] gre: fix ERSPAN o_flags race/corruption in xmit and fill_info Sasha Levin
` (7 subsequent siblings)
29 siblings, 0 replies; 32+ messages in thread
From: Sasha Levin @ 2026-10-19 13:51 UTC (permalink / raw)
To: patches, stable
Cc: Alex Elder, Sashiko, Maxime Chevallier, Jakub Kicinski,
Sasha Levin, andrew+netdev, davem, edumazet, pabeni,
mcoquelin.stm32, alexandre.torgue, netdev, linux-stm32,
linux-arm-kernel, linux-kernel
From: Alex Elder <elder@riscstar.com>
[ Upstream commit 0c1f9020d2ada55aed8a2e055903ad9cd5d49dab ]
In jumbo_frm() (implemented in both "chain_mode.c" and "ring_mode.c"),
an unsigned integer local variable is used to hold the value returned
by dma_map_single(). On systems where a dma_addr_t is 64 bits, the
subsequent dma_mapping_error() check of the returned value operates
only on the low 32 bits (whose high bit won't be sign-extended). In
this case, dma_mapping_error() would return 0 (no error) even if there
were one.
Fix this in both spots by using a dma_addr_t for the local variable.
Reported-by: Sashiko <sashiko-bot@kernel.org>
Link: https://lore.kernel.org/linux-devicetree/20260606010122.21A211F00899@smtp.kernel.org/
Reviewed-by: Maxime Chevallier <maxime.chevallier@bootlin.com>
Signed-off-by: Alex Elder <elder@riscstar.com>
Link: https://patch.msgid.link/20260812163832.271742-2-elder@riscstar.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
LLM Generated explanations, may be completely bogus:
The local tree is **6.12.111** (`pending-6.12`). The buggy `unsigned int
des2` path is present here, and this type-correctness fix should be
backported.
## PHASE 1: COMMIT MESSAGE FORENSICS
**Step 1.1: PARSE THE SUBJECT LINE**
Record: `[net: stmmac] [use] [store dma_map_single() results as
dma_addr_t in jumbo_frm()]`
**Step 1.2: PARSE ALL COMMIT MESSAGE TAGS**
Record:
- `Reported-by: Sashiko <sashiko-bot@kernel.org>` — static-analysis bot,
not a user crash
- `Link: https://lore.kernel.org/linux-
devicetree/20260606010122.21A211F00899@smtp.kernel.org/` — Sashiko
report
- `Reviewed-by: Maxime Chevallier <maxime.chevallier@bootlin.com>` —
stmmac/netdev reviewer
- `Signed-off-by: Alex Elder <elder@riscstar.com>` — author
- `Link:
https://patch.msgid.link/20260812163832.271742-2-elder@riscstar.com` —
v2 1/2 of a net-next series
- `Signed-off-by: Jakub Kicinski <kuba@kernel.org>` — net maintainer
- No `Fixes:` tag (expected)
- No `Cc: stable` (expected)
- Ignore pipeline `Signed-off-by: Sasha Levin` / `[Upstream commit ...]`
if present on a candidate wrapper
**Step 1.3: ANALYZE THE COMMIT BODY TEXT**
Record:
- **Bug:** `jumbo_frm()` in `chain_mode.c` and `ring_mode.c` stores
`dma_map_single()` in `unsigned int des2`. On 64-bit `dma_addr_t`,
`dma_mapping_error()` only sees the low 32 bits.
- **Symptom:** a failed mapping is treated as success
(`dma_mapping_error()` returns 0).
- **Root cause:** `DMA_MAPPING_ERROR` is `~(dma_addr_t)0`
(`0xFFFFFFFFFFFFFFFF` on 64-bit). Truncating to `unsigned int` yields
`0xFFFFFFFF`, which promotes to `0x00000000FFFFFFFF` and does not
match all-ones.
- **Version info:** none in the message. The pattern dates to at least
2016 (`f8be0d78be6e7`).
**Step 1.4: DETECT HIDDEN BUG FIXES**
Record: Not disguised. It is an explicit correctness fix for a truncated
DMA error check. Changing `des2` to `dma_addr_t` also stops truncating
the address stored in `tx_skbuff_dma[].buf` (`dma_addr_t`) used for
later unmap.
## PHASE 2: DIFF ANALYSIS
**Step 2.1: INVENTORY THE CHANGES**
Record:
- `drivers/net/ethernet/stmicro/stmmac/chain_mode.c`: +2/−1
- `drivers/net/ethernet/stmicro/stmmac/ring_mode.c`: +2/−1
- Functions: `jumbo_frm()` in both files
- Scope: single-function surgical type change in two parallel
implementations (4 insertions, 2 deletions)
**Step 2.2: UNDERSTAND THE CODE FLOW CHANGE**
Record:
- **Before:** `unsigned int des2 = dma_map_single(...)`; then
`cpu_to_le32(des2)`, `dma_mapping_error(..., des2)`,
`tx_skbuff_dma[entry].buf = des2`.
- **After:** `dma_addr_t des2` so the error check and `buf` store use
the full DMA address.
- Path: jumbo TX mapping in `stmmac_xmit()` when `enh_desc` is set and
the frame is large enough (`>= 4KiB` ring / `> 8KiB` chain).
- Same local is reused for later `dma_map_single()` calls in those
functions, so all mapping sites in `jumbo_frm()` are fixed.
**Step 2.3: IDENTIFY THE BUG MECHANISM**
Record:
- **Category:** type / width bug that breaks error detection (and
truncates unmap addresses).
- **Mechanism:** 32-bit truncation of a 64-bit `dma_addr_t` so
`dma_mapping_error()` never sees `DMA_MAPPING_ERROR`.
- Verified in this tree:
```92:98:include/linux/dma-mapping.h
static inline int dma_mapping_error(struct device *dev, dma_addr_t
dma_addr)
{
debug_dma_mapping_error(dev, dma_addr);
if (unlikely(dma_addr == DMA_MAPPING_ERROR))
return -ENOMEM;
return 0;
}
```
`DMA_MAPPING_ERROR` is `~(dma_addr_t)0`. The current buggy locals:
```23:41:drivers/net/ethernet/stmicro/stmmac/chain_mode.c
unsigned int bmax, buf_len, des2;
...
des2 = dma_map_single(priv->device, skb->data,
buf_len, DMA_TO_DEVICE);
desc->des2 = cpu_to_le32(des2);
if (dma_mapping_error(priv->device, des2))
return -1;
tx_q->tx_skbuff_dma[entry].buf = des2;
```
**Step 2.4: ASSESS THE FIX QUALITY**
Record:
- Obviously correct: use the type `dma_map_single()` actually returns.
- Minimal; no API or behavior change on the success path with a 32-bit
DMA mask.
- Regression risk is low. `desc->des2` is still `__le32`, so hardware
still gets the low 32 bits (same as before). Maxime asked to make that
truncation explicit; that is the follow-up `c11c497af866c`
(`lower_32_bits()`), not required for this fix to be correct.
- `cpu_to_le32()` already casts to `__u32` on little-endian; standalone
apply should not break the build.
## PHASE 3: GIT HISTORY INVESTIGATION
**Step 3.1: BLAME THE CHANGED LINES**
Record:
- `unsigned int ... des2` in ring_mode: `f8be0d78be6e7` (2016-11-15,
“change dma descriptors to `__le32`”).
- chain_mode declaration last touched by `2c91b39912278` (already in
this tree: “fix integer underflow in chain mode”); that commit kept
`unsigned int des2`.
- `jumbo_frm()` itself: `286a837217204` (2011).
- Bug is long-standing, not new in 6.12.
**Step 3.2: FOLLOW THE FIXES: TAG**
Record: No `Fixes:` tag. Introducer is the 2011/2016 DMA-descriptor
code, present in this tree.
**Step 3.3: CHECK FILE HISTORY FOR RELATED CHANGES**
Record (this tree):
- `2c91b39912278` net: stmmac: fix integer underflow in chain mode
(already here)
- `133160135a6d7` STMMAC_GET_ENTRY rename
- Mainline follow-up: `c11c497af866c` net: stmmac: convert DMA address
to lower 32 before assignment (clarity; not in this tree; not a
functional prerequisite)
- This is standalone 1/2 of “DMA address handling fixes”; 2/2 is
style/clarity.
**Step 3.4: CHECK THE AUTHOR'S OTHER COMMITS**
Record: Alex Elder is not the stmmac maintainer; two origin stmmac
commits are this pair. Reviewed by Maxime Chevallier; applied by Jakub
Kicinski.
**Step 3.5: CHECK FOR DEPENDENT/PREREQUISITE COMMITS**
Record: No new helpers. `dma_addr_t` and `dma_mapping_error()` exist
here. `git apply --check` of `0c1f9020d2ada` succeeded on this tree.
Parent-tree vs this tree only differs in `is_jumbo_frm()` (untouched).
Standalone apply: yes.
## PHASE 4: MAILING LIST AND EXTERNAL RESEARCH
**Step 4.1: FIND THE ORIGINAL PATCH DISCUSSION**
Record:
- `b4 dig -c 0c1f9020d2ada` →
https://patch.msgid.link/20260812163832.271742-2-elder@riscstar.com
- Series: v1 2026-08-11; v2 2026-08-12 `[PATCH net-next v2 0/2] net:
stmmac: DMA address handling fixes`
- Applied to **net-next** (not `net`) by Jakub on 2026-08-17
- Cover: “ensure DMA errors don't get lost due to casting the result to
an integer of the wrong size”
- No NAKs. Maxime: “I think this is good as-is” + `Reviewed-by`
- Maxime noted `cpu_to_le32(des2)` still truncates into `__le32` and
doubted jumbo on true 64-bit descriptor addresses; that is pre-
existing hardware-descriptor width, not a regression from this patch
- No reviewer `Cc: stable`
**Step 4.2: CHECK WHO REVIEWED THE PATCH**
Record (`b4 dig -w`): netdev maintainers (Kicinski, Dal, Pabeni, Lunn),
STM32/stmmac (Torgue, Coquelin), Maxime Chevallier. Appropriate
audience.
**Step 4.3: SEARCH FOR THE BUG REPORT**
Record: Sashiko static analysis, not syzbot and not a user oops. The
lore `linux-devicetree` link is a bot dump; no independent user
reproduction was found. The bug is still visible in the code.
**Step 4.4: CHECK FOR RELATED PATCHES AND SERIES**
Record: Companion 2/2 (`c11c497af866c`) only wraps
`cpu_to_le32(lower_32_bits(des2))`. This commit is the actual fix.
**Step 4.5: CHECK STABLE MAILING LIST HISTORY**
Record: lore.kernel.org returned HTTP 403 via WebFetch. `b4` thread has
no stable discussion. UNVERIFIED: whether this hash was already
requested on stable@; that does not change the technical verdict.
## PHASE 5: CODE SEMANTIC ANALYSIS
**Step 5.1: IDENTIFY KEY FUNCTIONS**
Record: `jumbo_frm()` in `chain_mode.c` and `ring_mode.c`.
**Step 5.2: TRACE CALLERS**
Record:
- `stmmac_jumbo_frm()` macro → `hw->mode->jumbo_frm`
- Only call site: `stmmac_xmit()` when `priv->plat->enh_desc` and
`is_jumbo_frm(skb->len)`
- Context: process / NAPI TX, not IRQ
- `dwmac4_ring_mode_ops` (DWMAC ≥ 4.00) has no `jumbo_frm`; those chips
do not use this path
- Affected: DWMAC100 / DWMAC1000 (and early DWMAC4 that still install
`ring_mode_ops`/`chain_mode_ops`) with enhanced descriptors
**Step 5.3: TRACE CALLEES**
Record: `dma_map_single()`, `dma_mapping_error()`, `cpu_to_le32()`,
`stmmac_prepare_tx_desc()`. On error, `return -1` → `stmmac_xmit` `goto
dma_map_err`.
**Step 5.4: FOLLOW THE CALL CHAIN**
Record: userspace send / jumbo MTU → `ndo_start_xmit` = `stmmac_xmit` →
`jumbo_frm`. TSO/GSO with `priv->tso` uses `stmmac_tso_xmit` instead.
Reachable from userspace on jumbo MTU. Mapping failure is an error path
(IOMMU / swiotlb pressure).
**Step 5.5: SEARCH FOR SIMILAR PATTERNS**
Record: non-jumbo TX in `stmmac_xmit` already uses `dma_addr_t des`.
This is leftover jumbo-path width mismatch. Historical related fix in-
tree: `06a80a7d09cc3` (descriptor addresses >32-bit).
## PHASE 6: CROSS-REFERENCING AGAINST THE LOCAL TREE
**Step 6.1: DOES THE BUGGY CODE EXIST IN THIS TREE?**
Record: Yes. Local tree is **6.12.111** (`p-6.12-3-gc504cf0baa3d8`).
Both files still have `unsigned int ... des2`. Introduced no later than
2016; present since this series branched. `git merge-base --is-ancestor
0c1f9020d2ada HEAD` is false (fix not here).
**Step 6.2: CHECK FOR BACKPORT COMPLICATIONS**
Record: Expected **clean apply**. `git show 0c1f9020d2ada | git apply
--check` succeeded. Only unrelated `is_jumbo_frm()` signature drift vs
mainline parent.
**Step 6.3: CHECK IF RELATED FIXES ARE ALREADY HERE**
Record: No `dma_addr_t` jumbo_frm fix in this tree. `2c91b39912278` is a
different jumbo underflow fix and left `des2` as `unsigned int`.
## PHASE 7: SUBSYSTEM AND MAINTAINER CONTEXT
**Step 7.1: IDENTIFY THE SUBSYSTEM AND ITS CRITICALITY**
Record: `drivers/net/ethernet/stmicro/stmmac` — Synopsys DWMAC, used on
STM32, Rockchip, Allwinner, i.MX, Qualcomm, Intel, etc. Criticality:
**IMPORTANT** (common Ethernet), this path **PERIPHERAL-to-IMPORTANT**
(older GMAC + jumbo).
**Step 7.2: ASSESS SUBSYSTEM ACTIVITY**
Record: Actively maintained, but `chain_mode.c`/`ring_mode.c` jumbo
logic is mature (2011). This is an old bug, not churn from new code.
## PHASE 8: IMPACT AND RISK ASSESSMENT
**Step 8.1: DETERMINE WHO IS AFFECTED**
Record: Driver-specific, config `CONFIG_STMMAC_ETH`, older DWMAC with
`enh_desc`, jumbo frames, **and** 64-bit `dma_addr_t` (typical
arm64/x86_64). 32-bit `dma_addr_t` already compared equal to
`DMA_MAPPING_ERROR`.
**Step 8.2: DETERMINE THE TRIGGER CONDITIONS**
Record: Jumbo TX (`skb->len >= 4KiB` ring / `> 8KiB` chain) plus
`dma_map_single()` failure. Not every packet. Unprivileged users can
send jumbo frames if MTU allows; they cannot force IOMMU exhaustion
easily. Mapping failure is uncommon but real.
**Step 8.3: DETERMINE THE FAILURE MODE SEVERITY**
Record:
- Failed mapping treated as success
- Descriptor programmed with `0xFFFFFFFF`
- NIC DMAs from that address (wrong memory / bus error / possible leak
of physical memory onto the wire)
- `tx_skbuff_dma[].buf` also truncated, so unmap can be wrong
- Severity: **HIGH** (wrong DMA address / potential corruption). Not a
guaranteed panic on every jumbo packet.
**Step 8.4: CALCULATE RISK-BENEFIT RATIO**
Record:
- **Benefit:** medium-high for affected 64-bit DWMAC jumbo users; closes
a real DMA error-handling hole
- **Risk:** very low (2-line type change per file, no locking/API
change)
- **Ratio:** favorable
## PHASE 9: FINAL SYNTHESIS
**Step 9.1: COMPILE THE EVIDENCE**
FOR:
- Real, verifiable type bug: truncated `dma_mapping_error()` on 64-bit
`dma_addr_t`
- Failure can program the NIC with a bogus DMA address
- Also stores a full address in `tx_skbuff_dma[].buf`
- ~6 lines, obviously correct, reviewed, applied by net maintainer
- Buggy code is in this 6.12 tree and has been for years
- Patch applies cleanly
- Same driver already uses `dma_addr_t` on the normal TX path
AGAINST:
- Submitted to **net-next**, not `net` (not treated as an urgent
regression)
- Sashiko/static analysis, no user oops
- Jumbo + older GMAC + mapping failure is a narrow trigger
- Reviewer noted 32-bit descriptors cannot represent full 64-bit
addresses (pre-existing)
- Companion `lower_32_bits()` patch is not included (optional clarity)
UNRESOLVED:
- Stable@ archives not readable via WebFetch (403)
- Exact Sashiko warning text in the large 47-message dump was not
isolated beyond the type issue this patch names
**Step 9.2: APPLY THE STABLE RULES CHECKLIST**
1. Obviously correct and tested? **PASS** — type matches DMA API;
reviewed; in mainline since 2026-08-17
2. Fixes a real bug that affects users? **PASS** — missed DMA mapping
errors on 64-bit `dma_addr_t`
3. Important issue? **PASS** — wrong DMA address on mapping failure
(corruption / crash / possible leak), even if the window is uncommon
4. Small and contained? **PASS** — 6 lines, two files, one function
5. No new features or APIs? **PASS**
6. Can apply to the local tree? **PASS** — `git apply --check` clean on
6.12.111
**Step 9.3: CHECK FOR EXCEPTION CATEGORIES**
Record: None (not a device ID/quirk/DT/build/docs exception). Qualifies
as a normal small bugfix.
**Step 9.4: MAKE YOUR DECISION**
The local 6.12 tree still truncates `dma_map_single()` to `unsigned int`
in `jumbo_frm()`, so `dma_mapping_error()` cannot see
`DMA_MAPPING_ERROR` on 64-bit `dma_addr_t`. That is a real DMA
correctness bug, the fix is surgical, and it applies here without
prerequisites. Narrow trigger does not outweigh the severity of
programming a failed mapping into the NIC.
### What the commit solves and why it matters to stable users
On 64-bit `dma_addr_t` platforms, a failed jumbo TX mapping is
misclassified as success, and the MAC may DMA using `0xFFFFFFFF`. 6.12
users of DWMAC1000-class stmmac with jumbo MTU on arm64/x86_64 are
exposed.
### Stable rules / risk
Meets the stable bar: real bug, small, no new API, clean apply. Risk is
a type change of a local. Benefit is correct mapping-error handling and
a full-width unmap address.
### Concerns
Optional follow-up `c11c497af866c` only makes 32-bit descriptor
truncation explicit; not required. No need to wait for other series
patches.
## Verification
- [Phase 1] Parsed tags: Sashiko Reported-by, Maxime Reviewed-by, Jakub
SOB; no Fixes/Cc stable
- [Phase 1] Body: truncated `dma_mapping_error()` on 64-bit `dma_addr_t`
- [Phase 2] Diff: `unsigned int des2` → `dma_addr_t des2` in both
`jumbo_frm()` implementations (+4/−2)
- [Phase 2] `include/linux/dma-mapping.h`: `dma_mapping_error()`
compares to `DMA_MAPPING_ERROR` (`~(dma_addr_t)0`)
- [Phase 2] Local `chain_mode.c`/`ring_mode.c` still use `unsigned int
des2`; `tx_skbuff_dma[].buf` is `dma_addr_t`
- [Phase 3] Blame: `f8be0d78be6e7` (2016) / `286a837217204` (2011); in
this tree
- [Phase 3] Mainline SHA `0c1f9020d2ada`; follow-up `c11c497af866c` is
clarity-only
- [Phase 3] `git merge-base --is-ancestor 0c1f9020d2ada HEAD` → not in
this tree
- [Phase 4] `b4 dig -c 0c1f9020d2ada`: lore/msgid v2 1/2 net-next
series; applied to netdev/net-next
- [Phase 4] `b4 dig -a`: v1 then v2; Maxime suggested `lower_32_bits()`,
still Reviewed-by on the type fix
- [Phase 4] `b4 dig -w`: netdev + STM32/stmmac CCs
- [Phase 4] mbox cover: “DMA errors don't get lost due to casting”
- [Phase 4] UNVERIFIED: stable@ mailing list (lore 403)
- [Phase 5] Callers: `stmmac_xmit()` → `stmmac_jumbo_frm()`; DWMAC4 ≥
4.00 ops have no `jumbo_frm`
- [Phase 5] Non-jumbo TX already uses `dma_addr_t des`
- [Phase 6] `git describe HEAD` / `make kernelversion`: 6.12.111
- [Phase 6] `git apply --check` of `0c1f9020d2ada`: success
- [Phase 6] `git diff 0c1f9020d2ada^ -- chain_mode.c ring_mode.c`: only
`is_jumbo_frm()` drift
- [Phase 8] Failure mode: missed mapping error → NIC DMA at
`0xFFFFFFFF`; severity HIGH
- UNVERIFIED: isolated full text of the original Sashiko diagnostic line
**YES**
drivers/net/ethernet/stmicro/stmmac/chain_mode.c | 3 ++-
drivers/net/ethernet/stmicro/stmmac/ring_mode.c | 3 ++-
2 files changed, 4 insertions(+), 2 deletions(-)
diff --git a/drivers/net/ethernet/stmicro/stmmac/chain_mode.c b/drivers/net/ethernet/stmicro/stmmac/chain_mode.c
index 2515608878235..5b0bab33ce4b6 100644
--- a/drivers/net/ethernet/stmicro/stmmac/chain_mode.c
+++ b/drivers/net/ethernet/stmicro/stmmac/chain_mode.c
@@ -20,9 +20,10 @@ static int jumbo_frm(struct stmmac_tx_queue *tx_q, struct sk_buff *skb,
unsigned int nopaged_len = skb_headlen(skb);
struct stmmac_priv *priv = tx_q->priv_data;
unsigned int entry = tx_q->cur_tx;
- unsigned int bmax, buf_len, des2;
+ unsigned int bmax, buf_len;
unsigned int i = 1, len;
struct dma_desc *desc;
+ dma_addr_t des2;
desc = tx_q->dma_tx + entry;
diff --git a/drivers/net/ethernet/stmicro/stmmac/ring_mode.c b/drivers/net/ethernet/stmicro/stmmac/ring_mode.c
index 88a8d22812f5d..1e6541937cb27 100644
--- a/drivers/net/ethernet/stmicro/stmmac/ring_mode.c
+++ b/drivers/net/ethernet/stmicro/stmmac/ring_mode.c
@@ -20,8 +20,9 @@ static int jumbo_frm(struct stmmac_tx_queue *tx_q, struct sk_buff *skb,
unsigned int nopaged_len = skb_headlen(skb);
struct stmmac_priv *priv = tx_q->priv_data;
unsigned int entry = tx_q->cur_tx;
- unsigned int bmax, len, des2;
+ unsigned int bmax, len;
struct dma_desc *desc;
+ dma_addr_t des2;
if (priv->extend_desc)
desc = (struct dma_desc *)(tx_q->dma_etx + entry);
--
2.53.0
^ permalink raw reply related [flat|nested] 32+ messages in thread* [PATCH AUTOSEL 6.18-6.12] gre: fix ERSPAN o_flags race/corruption in xmit and fill_info
2026-10-19 13:44 [PATCH AUTOSEL 6.18-6.12] net: dsa: mt7530: check CORE_PLL_GROUP4 access in mt7531_setup() Sasha Levin
` (21 preceding siblings ...)
2026-10-19 13:51 ` [PATCH AUTOSEL 6.18-5.10] net: stmmac: use dma_addr_t for DMA addresses Sasha Levin
@ 2026-10-19 13:51 ` Sasha Levin
2026-10-19 13:51 ` [PATCH AUTOSEL 6.18-5.10] netfilter: nf_tables: call skb_valid_dst() before skb_dst() Sasha Levin
` (6 subsequent siblings)
29 siblings, 0 replies; 32+ messages in thread
From: Sasha Levin @ 2026-10-19 13:51 UTC (permalink / raw)
To: patches, stable
Cc: Eric Dumazet, Simon Horman, Jakub Kicinski, Sasha Levin, dsahern,
idosch, davem, edumazet, pabeni, netdev, linux-kernel
From: Eric Dumazet <edumazet@google.com>
[ Upstream commit 9958e69b98930834a576e156f6458166d1db1c02 ]
For IPv4 ERSPAN:
In erspan_xmit(), the driver clears IP_TUNNEL_SEQ_BIT (for version 0)
and IP_TUNNEL_KEY_BIT directly in the shared tunnel->parms.o_flags
structure. Since transmit paths can run locklessly and concurrently,
this leads to a data race.
Furthermore, modifying tunnel->parms.o_flags permanently alters the
tunnel configuration. To work around this, erspan_fill_info() (which
reports config to userspace) was setting IP_TUNNEL_KEY_BIT back. If
erspan_fill_info (running under RTNL) and erspan_xmit (running locklessly)
race, erspan_xmit might see IP_TUNNEL_KEY_BIT set when it shouldn't,
leading to GRE header corruption (injecting a key field into the ERSPAN
GRE header).
Fix this by:
1) Passing flags as an argument to __gre_xmit().
2) Using local stack flags in ipgre_xmit(), gre_tap_xmit(), and erspan_xmit()
to prevent TOCTOU data races with concurrent configuration updates,
and passing them to __gre_xmit().
3) Removing the racy modification of t->parms.o_flags in erspan_fill_info().
4) Forcing IP_TUNNEL_KEY_BIT in the reported flags for ERSPAN locally
in ipgre_fill_info().
For IPv6 ERSPAN:
ip6erspan_tunnel_xmit() was locklessly clearing IP_TUNNEL_KEY_BIT in
t->parms.o_flags even though it does not use these flags for building
the GRE header (it uses local flags). This permanently corrupts the
configuration and races with ip6gre_fill_info() which reads it.
Remove the redundant and racy modification.
This should remove false sharing in a fast path.
Add const qualifiers in ipgre_fill_info(), erspan_fill_info()
and ip6gre_fill_info() to clarify that these methods are not
supposed to write any live parameters.
Signed-off-by: Eric Dumazet <edumazet@google.com>
Reviewed-by: Simon Horman <horms@kernel.org>
Link: https://patch.msgid.link/20260812142257.21283-1-edumazet@google.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
LLM Generated explanations, may be completely bogus:
I have enough to decide: the buggy shared `o_flags` mutation is in this
6.12 tree, the APIs match, and the failure mode is GRE header
corruption. Compiling the full analysis.
===============================================================
PHASE 1: COMMIT MESSAGE FORENSICS
===============================================================
Step 1.1: PARSE THE SUBJECT LINE
Record: [gre] [fix] [ERSPAN o_flags race/corruption in xmit and
fill_info]
Subsystem: GRE/ERSPAN tunnels (`net/ipv4/ip_gre.c`,
`net/ipv6/ip6_gre.c`). Action verb is explicitly **fix**.
Step 1.2: PARSE ALL COMMIT MESSAGE TAGS
Record (from the candidate text and from the original mailing-list
submission):
- Signed-off-by: Eric Dumazet <edumazet@google.com> (author; networking
core maintainer)
- Reviewed-by: Simon Horman <horms@kernel.org>
- Link:
https://patch.msgid.link/20260812142257.21283-1-edumazet@google.com
- Signed-off-by: Jakub Kicinski <kuba@kernel.org> (netdev maintainer,
applied to net-next)
- Ignore: pipeline "Signed-off-by: Sasha Levin" / "[Upstream commit
...]" if present
Original v4 submission (verified in `/tmp/erspan_thread.mbox`) also had:
- Fixes: 84e54fe0a5ea ("gre: introduce native tunnel support for
ERSPAN")
- Fixes: ee496694b9ee ("ip_gre: do not report erspan version on GRE
interface")
- Fixes: 5a963eb61b7c ("ip6_gre: Add ERSPAN native tunnel support")
v2 was sent to **net** with **Cc: stable@vger.kernel.org**. v4 dropped
Cc: stable and went to net-next after review. Absence of Cc: stable on
the committed v4 is expected for this review pipeline and is not a
negative signal.
No Reported-by / syzbot / Tested-by on this specific patch. Author found
it while working on RTNL-less fill_info.
Step 1.3: ANALYZE THE COMMIT BODY TEXT
Record:
- Bug: IPv4 `erspan_xmit()` locklessly clears `IP_TUNNEL_SEQ_BIT` (v0)
and `IP_TUNNEL_KEY_BIT` in the **shared** `tunnel->parms.o_flags`.
That permanently mutates tunnel config. `erspan_fill_info()` then
writes `IP_TUNNEL_KEY_BIT` back so userspace dumps look correct.
- Symptom: TOCTOU data race between lockless xmit and RTNL fill_info. If
xmit observes KEY_BIT set, `gre_build_header()` injects a GRE key
field into the ERSPAN header (malformed GRE / header corruption).
- IPv6: `ip6erspan_tunnel_xmit()` also locklessly clears KEY_BIT on
shared `t->parms.o_flags` even though it builds the GRE header from
**local** flags. That is redundant, races with `ip6gre_fill_info()`,
and causes false sharing on a fast path.
- Root cause: treating per-tunnel `o_flags` as scratch space from
ndo_start_xmit.
- Versions: Fixes tags point at ERSPAN introduction (2017) and the
fill_info KEY_BIT workaround (2022). All three are ancestors of this
6.12 tree.
Step 1.4: DETECT HIDDEN BUG FIXES
Record: Not disguised. This is an explicit race + packet-corruption fix.
Extra `const` on fill_info is documentation-only. Snapshotting flags in
`ipgre_xmit()`/`gre_tap_xmit()` is extra TOCTOU hardening requested by
Ido Schimmel, still a correctness fix, not a feature.
===============================================================
PHASE 2: DIFF ANALYSIS - LINE BY LINE
===============================================================
Step 2.1: INVENTORY THE CHANGES
Record:
- `net/ipv4/ip_gre.c`: ~+23/-19 in `__gre_xmit`, `ipgre_xmit`,
`erspan_xmit`, `gre_tap_xmit`, `ipgre_fill_info`, `erspan_fill_info`
- `net/ipv6/ip6_gre.c`: ~+2/-3 in `ip6erspan_tunnel_xmit`,
`ip6gre_fill_info`
- Scope: single-purpose, two-file surgical fix. `__gre_xmit` is file-
static; no public API.
Step 2.2: UNDERSTAND THE CODE FLOW CHANGE
Record:
- `__gre_xmit`: no longer copies `tunnel->parms.o_flags` itself; callers
pass a local flags snapshot.
- `ipgre_xmit` / `gre_tap_xmit`: copy `o_flags` once, use that copy for
CSUM offload check and header build (closes TOCTOU vs concurrent
changelink).
- `erspan_xmit`: copy flags, then `__clear_bit` KEY (and SEQ for v0) on
the **stack copy**, pass that to `__gre_xmit`. Shared `parms.o_flags`
is no longer written from xmit.
- `erspan_fill_info`: stop writing KEY_BIT into live `t->parms.o_flags`.
- `ipgre_fill_info`: if `erspan_ver != 0 && !collect_md`, set KEY_BIT on
a **local** dump copy. Regular GRE/GRETAP keep `erspan_ver == 0`
(`erspan_setup()` is the only path that sets it to 1).
- `ip6erspan_tunnel_xmit`: delete the unused shared
`__clear_bit(IP_TUNNEL_KEY_BIT, t->parms.o_flags)`.
- fill_info functions: `const` on tunnel/parm pointers.
Step 2.3: IDENTIFY THE BUG MECHANISM
Record: [Race + packet corruption + config corruption]
Mechanism: RMW of a shared bitmap from xmit (`__clear_bit`) vs fill_info
(`__set_bit`). `gre_build_header()` then uses `tun_hlen` (8 for ERSPAN
v1/v2) **and** the flags word. If KEY_BIT is set with SEQ, it writes key
then seq into an 8-byte push, and the key write lands on the GRE
flags/protocol word:
```102:144:include/net/gre.h
static inline void gre_build_header(struct sk_buff *skb, int hdr_len,
const unsigned long *flags, __be16
proto,
__be32 key, __be32 seq)
{
...
skb_push(skb, hdr_len);
...
if (ip_tunnel_flags_intersect(flags, cond)) {
__be32 *ptr = (__be32 *)(((u8 *)greh) + hdr_len - 4);
if (test_bit(IP_TUNNEL_SEQ_BIT, flags)) {
*ptr = seq;
ptr--;
}
if (test_bit(IP_TUNNEL_KEY_BIT, flags)) {
*ptr = key;
ptr--;
}
```
With `hdr_len == 8` and both SEQ and KEY, KEY overwrites the GRE base
header. That is real on-wire corruption of mirrored traffic.
Step 2.4: ASSESS THE FIX QUALITY
Record: Obviously correct. Local snapshot + mutate the copy is the
standard pattern (IPv6 xmit already used local flags). v4 dropped v2's
`gre_calc_hlen(flags)` substitution after review showed it could emit a
4-byte GRE header when SEQ was absent from o_flags; v4 keeps
`tunnel->tun_hlen`, which is the safer stable-friendly approach.
Regression risk is low: no new locks, no API change, fill_info dump
semantics preserved for ERSPAN KEY reporting.
===============================================================
PHASE 3: GIT HISTORY INVESTIGATION
===============================================================
Step 3.1: BLAME THE CHANGED LINES
Record: Shared-flag mutation dates to ERSPAN introduction:
- `f551c91de262ba` (2017-12-13) / `84e54fe0a5ea` (2017-08-22): IPv4
ERSPAN xmit
- `5a963eb61b7c` / `e41c7c68ea7716` (2017): IPv6 ERSPAN
- `ee496694b9ee` (2022-12-03): `erspan_fill_info` KEY_BIT restore
workaround
- `5832c4a77d693` (2024-03-27): `__be16` flags → bitmaps
(`__clear_bit`/`__set_bit`); **in this tree**
Bug has been in the driver since 2017; bitmap conversion only changed
the accessor.
Step 3.2: FOLLOW THE FIXES: TAG
Record: All three Fixes SHAs **are ancestors of HEAD**:
- `84e54fe0a5ea` IS ancestor
- `ee496694b9ee` IS ancestor
- `5a963eb61b7c` IS ancestor
Mainline commit `9958e69b9893` is **not** an ancestor of this checkout.
Step 3.3: CHECK FILE HISTORY FOR RELATED CHANGES
Record: Recent GRE work in this tree includes `9f948e9aede96` (restore
GRE/GRETAP `lltx`) and capability checks. No prior fix of this o_flags
xmit/fill_info race. Standalone commit (not patch X/Y). v1–v4 were
revisions of the **same** patch.
Step 3.4: CHECK THE AUTHOR'S OTHER COMMITS
Record: Eric Dumazet is a net core maintainer with many GRE/tunnel
commits here (`erspan: make sure erspan_base_hdr is present`, header
robustness, etc.). High-confidence author.
Step 3.5: CHECK FOR DEPENDENT/PREREQUISITE COMMITS
Record: Needs `IP_TUNNEL_DECLARE_FLAGS` / `ip_tunnel_flags_copy` /
`ip_tunnel_parm_kern` — all present (`5832c4a77d693`, `117aef12a7b1b`).
`__gre_xmit` is static to `ip_gre.c`. Can apply standalone.
Later mainline discussion (Sept 2026, `net: erspan: set lltx to avoid
sch_direct_xmit deadlock`) names **this commit as a stable
prerequisite** before enabling ERSPAN `lltx`. That later patch is not in
this tree (`erspan_tunnel_init` still does not set `dev->lltx`). This
fix is still valuable on its own because fill_info (RTNL) vs xmit (TX
lock) already race.
===============================================================
PHASE 4: MAILING LIST AND EXTERNAL RESEARCH
===============================================================
Step 4.1: FIND THE ORIGINAL PATCH DISCUSSION
Record:
- b4 dig matched v4:
https://patch.msgid.link/20260812142257.21283-1-edumazet@google.com
- Revisions: v1 net-next (2026-06-15), v2 **net + Cc: stable**
(2026-07-20), v3 net-next (2026-08-11), v4 net-next (2026-08-12,
applied)
- v1 was sent to net-next because “7.1 was just released” (merge-window
timing), not because it was considered a feature.
- v2 review (Sashiko): using `gre_calc_hlen(flags)` could emit a 4-byte
GRE header when SEQ is missing from o_flags; `tun_hlen` previously
forced 8 bytes. Eric marked CR (“What a mess”).
- v4 dropped that `gre_calc_hlen` change and kept `tun_hlen`.
- No NAK. Horman Reviewed-by. Jakub applied to netdev/net-next as
`9958e69b9893`.
Step 4.2: CHECK WHO REVIEWED THE PATCH
Record: Recipients: davem, kuba, pabeni, horms, idosch, netdev. Ido
asked v1 to snapshot flags in ipgre/gretap too (done in v4). Horman
reviewed v4. Appropriate maintainers.
Step 4.3: SEARCH FOR THE BUG REPORT
Record: No syzbot/bugzilla for this race. Author discovered it while
working on RTNL-less fill_info. Trigger is ordinary: ERSPAN TX plus `ip
-d link show` / RTNL dump.
Step 4.4: CHECK FOR RELATED PATCHES AND SERIES
Record: Single-patch series. Follow-up ERSPAN `lltx` deadlock fix
depends on this for “no shared mutable o_flags in xmit.” Author agreed
to list this as a stable prerequisite for that later patch.
Step 4.5: CHECK STABLE MAILING LIST HISTORY
Record: No lore.kernel.org/stable thread found that rejected this fix.
v2 was explicitly Cc'd to stable. The later lltx discussion notes this
commit was **not auto-queued** because v4 lacked Cc: stable — which is
why it is in this evaluation pipeline.
===============================================================
PHASE 5: CODE SEMANTIC ANALYSIS
===============================================================
Step 5.1: IDENTIFY KEY FUNCTIONS IN THE DIFF
Record: `__gre_xmit`, `ipgre_xmit`, `erspan_xmit`, `gre_tap_xmit`,
`ipgre_fill_info`, `erspan_fill_info`, `ip6erspan_tunnel_xmit`,
`ip6gre_fill_info`.
Step 5.2: TRACE CALLERS
Record:
- `erspan_xmit` / `ipgre_xmit` / `gre_tap_xmit` /
`ip6erspan_tunnel_xmit`: `ndo_start_xmit` (hot path).
- `ipgre_fill_info`: GRE and GRETAP `rtnl_link_ops.fill_info`; also
called from `erspan_fill_info`.
- `erspan_fill_info` / `ip6gre_fill_info`: netlink dump.
Step 5.3: TRACE CALLEES
Record: `gre_build_header`, `gre_handle_offloads` →
`iptunnel_handle_offloads`, `ip_tunnel_xmit` / `ip6_tnl_xmit`,
`ip_tunnel_flags_copy`, `__clear_bit`/`__set_bit`.
Step 5.4: FOLLOW THE CALL CHAIN
Record: Userspace-reachable: send on an ERSPAN/GRE/GRETAP netdev; dump
via RTNL (`ip link`). Unprivileged users typically cannot create tunnels
(needs CAP_NET_ADMIN), but once created, TX is a normal data path. Not
theoretical.
Step 5.5: SEARCH FOR SIMILAR PATTERNS
Record: IPv6 native GRE already snapshots flags in `__gre6_xmit`. IPv6
fill_info already set KEY_BIT on a **local** copy. IPv4 ERSPAN was the
inconsistent leftover that mutated live parms.
===============================================================
PHASE 6: CROSS-REFERENCING AGAINST THE LOCAL TREE
===============================================================
Step 6.1: DOES THE BUGGY CODE EXIST IN THIS TREE?
Record: **Yes.** Local tree is **6.12.111** (`pending-6.12`,
`p-6.12-3-gc504cf0baa3d8`).
Verified present:
```746:747:net/ipv4/ip_gre.c
__clear_bit(IP_TUNNEL_KEY_BIT, tunnel->parms.o_flags);
__gre_xmit(skb, dev, &tunnel->parms.iph, proto);
```
```1603:1604:net/ipv4/ip_gre.c
if (t->erspan_ver != 0 && !t->collect_md)
__set_bit(IP_TUNNEL_KEY_BIT, t->parms.o_flags);
```
```999:999:net/ipv6/ip6_gre.c
__clear_bit(IP_TUNNEL_KEY_BIT, t->parms.o_flags);
```
Bug introduced 2017; fill_info KEY restore 2022. Both long before 6.12.
Step 6.2: CHECK FOR BACKPORT COMPLICATIONS
Record: Expected apply: **minor context fuzz**, not a rewrite.
This tree’s `gre_rcv` drop path lacks `dev_core_stats_rx_dropped_inc()`
that appears as context in the mainline hunk; `__gre_xmit` itself
matches. All other hunks match current 6.12 code
(`IP_TUNNEL_DECLARE_FLAGS`, `ip_tunnel_parm_kern`, function layout). No
conflicting refactor.
Note: IPv4/IPv6 ERSPAN **do not** set `dev->lltx` here
(`erspan_tunnel_init` / `ip6erspan_tap_init`). Concurrent xmit on one
ERSPAN device is still serialized by `_xmit_lock`. The **fill_info
(RTNL) vs xmit (TX lock)** race remains, and any future ERSPAN lltx
backport would make concurrent xmit racy without this patch.
Step 6.3: CHECK IF RELATED FIXES ARE ALREADY HERE
Record: `git log --grep="fix ERSPAN o_flags race"` on these files:
empty. No equivalent fix in this tree.
===============================================================
PHASE 7: SUBSYSTEM AND MAINTAINER CONTEXT
===============================================================
Step 7.1: IDENTIFY THE SUBSYSTEM AND ITS CRITICALITY
Record: [net/ipv4 GRE + net/ipv6 GRE ERSPAN] [IMPORTANT]
Not core mm/VFS, but a widely used tunnel type (SPAN/ERSPAN mirroring,
cloud TAP, Kubernetes). Packet corruption on the mirror path is user-
visible.
Step 7.2: ASSESS SUBSYSTEM ACTIVITY
Record: Mature GRE/ERSPAN (since 4.x). Still receiving correctness fixes
(lltx, headroom, capability checks). This is a long-standing bug, not
churn from new code.
===============================================================
PHASE 8: IMPACT AND RISK ASSESSMENT
===============================================================
Step 8.1: DETERMINE WHO IS AFFECTED
Record: Driver/config-specific: IPv4 ERSPAN (primary), IPv6 ERSPAN
(shared o_flags clear), plus GRE/GRETAP get a small TOCTOU snapshot.
Anyone running ERSPAN mirrors plus netlink dumps.
Step 8.2: DETERMINE THE TRIGGER CONDITIONS
Record: Trigger: ERSPAN transmit concurrent with RTNL dump (`ip -d link
show`, monitoring agents). Common in production TAP setups. Creating the
tunnel needs CAP_NET_ADMIN; TX after that is the normal data path.
Timing-dependent, but the window is every dump-vs-packet overlap, not a
one-in-a-billion theoretical race.
Step 8.3: DETERMINE THE FAILURE MODE SEVERITY
Record: [GRE header corruption / wrong optional fields / overwrite of
GRE flags+protocol when KEY is injected into an 8-byte header] [HIGH]
Also: permanent clear of KEY_BIT in live config until fill_info writes
it back; data race on a bitmap word (KCSAN-class). Not a panic by
itself, but corrupted mirrored packets and broken GRE layout. Stable
rules treat this class of correctness bugs as important.
Step 8.4: CALCULATE RISK-BENEFIT RATIO
Record:
- Benefit: HIGH for ERSPAN users (stops header corruption and config
clobber; unblocks a correct lltx backport later).
- Risk: LOW (~25 net lines, local stack flags, no locking change, no
uAPI change, reviewed by Horman/Ido, written by Dumazet).
- Ratio: strongly favors backport.
===============================================================
PHASE 9: FINAL SYNTHESIS
===============================================================
Step 9.1: COMPILE THE EVIDENCE
FOR:
- Real race: xmit mutates shared `o_flags`; fill_info writes KEY_BIT
back under a different lock (RTNL vs TX lock).
- Concrete failure: KEY_BIT observed in `gre_build_header()` with
`tun_hlen==8` overwrites the GRE base header.
- Buggy code is in this 6.12 tree; Fixes commits are ancestors.
- Small, obviously correct, no new APIs.
- Author is a net maintainer; Horman Reviewed-by; Ido requested the
GRE/GRETAP snapshot.
- v2 was Cc: stable; later ERSPAN lltx work calls this a stable
prerequisite.
- v4 kept `tun_hlen` after review found v2’s `gre_calc_hlen` unsafe.
AGAINST:
- v4 landed in net-next without Cc: stable (timing/review, not a NAK).
- IPv4 ERSPAN is not yet lltx here, so concurrent xmit is serialized;
the remaining race is dump vs TX (still real).
- First hunk may need trivial context adjustment around `gre_rcv`.
- Extra snapshots in `ipgre_xmit`/`gre_tap_xmit` are slightly broader
than the ERSPAN-only bug, but they are the same pattern and low risk.
UNRESOLVED: none that affect the verdict. Could not fetch
lore.kernel.org HTML (403); used b4 mbox + openwall mirrors instead.
Step 9.2: APPLY THE STABLE RULES CHECKLIST
1. Obviously correct and tested? **PASS** — local snapshot, reviewed,
applied upstream.
2. Fixes a real bug that affects users? **PASS** — ERSPAN GRE
header/config corruption.
3. Important issue? **PASS** — HIGH: on-wire header corruption (not a
panic, but not cosmetic).
4. Small and contained? **PASS** — ~25 lines, two files, static helper
signature only.
5. No new features or APIs? **PASS**.
6. Can apply to the local tree? **PASS** — buggy code and bitmap APIs
present; minor gre_rcv context at most.
Step 9.3: CHECK FOR EXCEPTION CATEGORIES
Record: Not a device-ID/quirk/DT/build/docs exception. Ordinary
networking correctness fix.
Step 9.4: MAKE YOUR DECISION
This belongs in **this** 6.12 tree. The bug is here, the fix is small
and correct, and it prevents GRE header corruption on ERSPAN plus
shared-state races that a later lltx change would make worse.
What the commit solves for stable users: ERSPAN xmit must not clear
KEY/SEQ in the live tunnel parms. Doing so races with netlink fill_info
and can inject a GRE key into an 8-byte ERSPAN GRE header, corrupting
mirrored packets. Snapshot flags on the stack and report KEY only on the
dump copy.
Risk vs benefit: low risk, high benefit for ERSPAN. Apply with at most a
small context tweak on the `__gre_xmit` hunk.
Verification:
- [Phase 1] Parsed subject/tags; original v4 mbox has Fixes:
84e54fe0a5ea, ee496694b9ee, 5a963eb61b7c; v2 had Cc: stable
- [Phase 2] Diff: local flags to `__gre_xmit`; stop mutating
`parms.o_flags` in erspan_xmit/fill_info and ip6erspan xmit
- [Phase 2] `gre_build_header()` with hdr_len=8 + KEY+SEQ overwrites GRE
base header — verified in `include/net/gre.h`
- [Phase 3] blame: mutation since 2017 ERSPAN; bitmap conversion
5832c4a77d693 in this tree
- [Phase 3] `git merge-base --is-ancestor` all three Fixes SHAs: yes;
9958e69b9893: no
- [Phase 4] `b4 dig -c 9958e69b9893`: lore msgid 20260812142257.21283-1;
v2/v3/v4; Horman Reviewed-by; no NAK
- [Phase 4] Ido asked for ipgre/gretap snapshots; Sashiko caught v2
gre_calc_hlen malformation; v4 dropped that
- [Phase 4] Later lltx deadlock thread names this commit as stable
prerequisite
- [Phase 5] Callers are ndo_start_xmit and RTNL fill_info
- [Phase 6] `git describe HEAD`: p-6.12-3-gc504cf0baa3d8; `make
kernelversion`: 6.12.111; buggy `__clear_bit`/`__set_bit` present
- [Phase 6] ERSPAN init does not set lltx in this tree; fill_info vs
xmit still races
- [Phase 8] Failure mode: GRE header corruption, severity HIGH
- UNVERIFIED: lore.kernel.org HTML 403 (used b4 + openwall); no stable-
list rejection found
**YES**
net/ipv4/ip_gre.c | 42 +++++++++++++++++++++++-------------------
net/ipv6/ip6_gre.c | 5 ++---
2 files changed, 25 insertions(+), 22 deletions(-)
diff --git a/net/ipv4/ip_gre.c b/net/ipv4/ip_gre.c
index b15cc4ee0d920..b1ec4f92d3462 100644
--- a/net/ipv4/ip_gre.c
+++ b/net/ipv4/ip_gre.c
@@ -474,12 +474,9 @@ static int gre_rcv(struct sk_buff *skb)
static void __gre_xmit(struct sk_buff *skb, struct net_device *dev,
const struct iphdr *tnl_params,
- __be16 proto)
+ __be16 proto, const unsigned long *flags)
{
struct ip_tunnel *tunnel = netdev_priv(dev);
- IP_TUNNEL_DECLARE_FLAGS(flags);
-
- ip_tunnel_flags_copy(flags, tunnel->parms.o_flags);
/* Push GRE header. */
gre_build_header(skb, tunnel->tun_hlen,
@@ -652,6 +649,7 @@ static netdev_tx_t ipgre_xmit(struct sk_buff *skb,
struct net_device *dev)
{
struct ip_tunnel *tunnel = netdev_priv(dev);
+ IP_TUNNEL_DECLARE_FLAGS(flags);
const struct iphdr *tnl_params;
if (!pskb_inet_may_pull(skb))
@@ -687,11 +685,12 @@ static netdev_tx_t ipgre_xmit(struct sk_buff *skb,
tnl_params = &tunnel->parms.iph;
}
- if (gre_handle_offloads(skb, test_bit(IP_TUNNEL_CSUM_BIT,
- tunnel->parms.o_flags)))
+ ip_tunnel_flags_copy(flags, tunnel->parms.o_flags);
+
+ if (gre_handle_offloads(skb, test_bit(IP_TUNNEL_CSUM_BIT, flags)))
goto free_skb;
- __gre_xmit(skb, dev, tnl_params, skb->protocol);
+ __gre_xmit(skb, dev, tnl_params, skb->protocol, flags);
return NETDEV_TX_OK;
free_skb:
@@ -704,6 +703,7 @@ static netdev_tx_t erspan_xmit(struct sk_buff *skb,
struct net_device *dev)
{
struct ip_tunnel *tunnel = netdev_priv(dev);
+ IP_TUNNEL_DECLARE_FLAGS(flags);
bool truncate = false;
__be16 proto;
@@ -727,10 +727,12 @@ static netdev_tx_t erspan_xmit(struct sk_buff *skb,
truncate = true;
}
+ ip_tunnel_flags_copy(flags, tunnel->parms.o_flags);
+
/* Push ERSPAN header */
if (tunnel->erspan_ver == 0) {
proto = htons(ETH_P_ERSPAN);
- __clear_bit(IP_TUNNEL_SEQ_BIT, tunnel->parms.o_flags);
+ __clear_bit(IP_TUNNEL_SEQ_BIT, flags);
} else if (tunnel->erspan_ver == 1) {
erspan_build_header(skb, ntohl(tunnel->parms.o_key),
tunnel->index,
@@ -745,8 +747,8 @@ static netdev_tx_t erspan_xmit(struct sk_buff *skb,
goto free_skb;
}
- __clear_bit(IP_TUNNEL_KEY_BIT, tunnel->parms.o_flags);
- __gre_xmit(skb, dev, &tunnel->parms.iph, proto);
+ __clear_bit(IP_TUNNEL_KEY_BIT, flags);
+ __gre_xmit(skb, dev, &tunnel->parms.iph, proto, flags);
return NETDEV_TX_OK;
free_skb:
@@ -759,6 +761,7 @@ static netdev_tx_t gre_tap_xmit(struct sk_buff *skb,
struct net_device *dev)
{
struct ip_tunnel *tunnel = netdev_priv(dev);
+ IP_TUNNEL_DECLARE_FLAGS(flags);
if (!pskb_inet_may_pull(skb))
goto free_skb;
@@ -768,14 +771,15 @@ static netdev_tx_t gre_tap_xmit(struct sk_buff *skb,
return NETDEV_TX_OK;
}
- if (gre_handle_offloads(skb, test_bit(IP_TUNNEL_CSUM_BIT,
- tunnel->parms.o_flags)))
+ ip_tunnel_flags_copy(flags, tunnel->parms.o_flags);
+
+ if (gre_handle_offloads(skb, test_bit(IP_TUNNEL_CSUM_BIT, flags)))
goto free_skb;
if (skb_cow_head(skb, dev->needed_headroom))
goto free_skb;
- __gre_xmit(skb, dev, &tunnel->parms.iph, htons(ETH_P_TEB));
+ __gre_xmit(skb, dev, &tunnel->parms.iph, htons(ETH_P_TEB), flags);
return NETDEV_TX_OK;
free_skb:
@@ -1571,12 +1575,15 @@ static size_t ipgre_get_size(const struct net_device *dev)
static int ipgre_fill_info(struct sk_buff *skb, const struct net_device *dev)
{
- struct ip_tunnel *t = netdev_priv(dev);
- struct ip_tunnel_parm_kern *p = &t->parms;
+ const struct ip_tunnel *t = netdev_priv(dev);
+ const struct ip_tunnel_parm_kern *p = &t->parms;
IP_TUNNEL_DECLARE_FLAGS(o_flags);
ip_tunnel_flags_copy(o_flags, p->o_flags);
+ if (t->erspan_ver != 0 && !t->collect_md)
+ __set_bit(IP_TUNNEL_KEY_BIT, o_flags);
+
if (nla_put_u32(skb, IFLA_GRE_LINK, p->link) ||
nla_put_be16(skb, IFLA_GRE_IFLAGS,
gre_tnl_flags_to_gre_flags(p->i_flags)) ||
@@ -1619,12 +1626,9 @@ static int ipgre_fill_info(struct sk_buff *skb, const struct net_device *dev)
static int erspan_fill_info(struct sk_buff *skb, const struct net_device *dev)
{
- struct ip_tunnel *t = netdev_priv(dev);
+ const struct ip_tunnel *t = netdev_priv(dev);
if (t->erspan_ver <= 2) {
- if (t->erspan_ver != 0 && !t->collect_md)
- __set_bit(IP_TUNNEL_KEY_BIT, t->parms.o_flags);
-
if (nla_put_u8(skb, IFLA_GRE_ERSPAN_VER, t->erspan_ver))
goto nla_put_failure;
diff --git a/net/ipv6/ip6_gre.c b/net/ipv6/ip6_gre.c
index ebd0198458cd3..905c22e6990b8 100644
--- a/net/ipv6/ip6_gre.c
+++ b/net/ipv6/ip6_gre.c
@@ -967,7 +967,6 @@ static netdev_tx_t ip6erspan_tunnel_xmit(struct sk_buff *skb,
if (skb_cow_head(skb, dev->needed_headroom ?: t->hlen))
goto tx_err;
- __clear_bit(IP_TUNNEL_KEY_BIT, t->parms.o_flags);
IPCB(skb)->flags = 0;
/* For collect_md mode, derive fl6 from the tunnel key,
@@ -2116,8 +2115,8 @@ static size_t ip6gre_get_size(const struct net_device *dev)
static int ip6gre_fill_info(struct sk_buff *skb, const struct net_device *dev)
{
- struct ip6_tnl *t = netdev_priv(dev);
- struct __ip6_tnl_parm *p = &t->parms;
+ const struct ip6_tnl *t = netdev_priv(dev);
+ const struct __ip6_tnl_parm *p = &t->parms;
IP_TUNNEL_DECLARE_FLAGS(o_flags);
ip_tunnel_flags_copy(o_flags, p->o_flags);
--
2.53.0
^ permalink raw reply related [flat|nested] 32+ messages in thread* [PATCH AUTOSEL 6.18-5.10] netfilter: nf_tables: call skb_valid_dst() before skb_dst()
2026-10-19 13:44 [PATCH AUTOSEL 6.18-6.12] net: dsa: mt7530: check CORE_PLL_GROUP4 access in mt7531_setup() Sasha Levin
` (22 preceding siblings ...)
2026-10-19 13:51 ` [PATCH AUTOSEL 6.18-6.12] gre: fix ERSPAN o_flags race/corruption in xmit and fill_info Sasha Levin
@ 2026-10-19 13:51 ` Sasha Levin
2026-10-19 13:51 ` [PATCH AUTOSEL 6.18-5.15] net: stmmac: raise TX completion interrupt at the end of an xmit burst Sasha Levin
` (5 subsequent siblings)
29 siblings, 0 replies; 32+ messages in thread
From: Sasha Levin @ 2026-10-19 13:51 UTC (permalink / raw)
To: patches, stable
Cc: Pablo Neira Ayuso, Sasha Levin, fw, davem, edumazet, kuba, pabeni,
netfilter-devel, coreteam, netdev, linux-kernel
From: Pablo Neira Ayuso <pablo@netfilter.org>
[ Upstream commit 689db98e535bf8efb5bc9c36f4fd3de3bd715eb6 ]
When fetching the dst_entry from the skb, check if it valid, ie. this is
not a template dst, for extensions that can be used from the netdev
ingress and egress chains.
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
LLM Generated explanations, may be completely bogus:
The evaluation target is **pending-6.6 (Linux 6.6.157)**. The live
checkout later moved to mainline v7.3-rc4; all 6.6 findings below are
from `refs/heads/pending-6.6` (`p-6.6-3-g04810563abb71`).
Original commit: `689db98e535bf8efb5bc9c36f4fd3de3bd715eb6` (Pablo Neira
Ayuso, 2026-07-31). It is **not** an ancestor of pending-6.6.
---
## PHASE 1: COMMIT MESSAGE FORENSICS
**Step 1.1: PARSE THE SUBJECT LINE**
- Record: **[netfilter/nf_tables] [call/check] [require a real routing
dst, not a metadata template, before using skb_dst()]**
**Step 1.2: PARSE ALL COMMIT MESSAGE TAGS**
- Record:
- **Fixes:** none (expected for this review)
- **Reported-by / Tested-by / Reviewed-by / Acked-by:** none
- **Link:** none in the commit; mailing list found separately
- **Cc: stable:** none (expected)
- **Signed-off-by:** Pablo Neira Ayuso (netfilter maintainer). Ignore
Sasha Levin SOB if present in a candidate wrapper.
**Step 1.3: ANALYZE THE COMMIT BODY TEXT**
- Record:
- **Bug:** `skb_dst()` is used as if it were a real
`rtable`/`rt6_info`. Packets on netdev/inet ingress/egress often
carry a **DST_METADATA** template (tunnels, TC) instead.
- **Symptom:** nft `rt` / `meta rtclassid` / `xfrm` evaluate a
template dst; `nf_send_reset`/`nf_send_unreach` skip filling a real
route and treat the template as an `rtable`.
- **Version info:** none in the message. Buggy pattern is old; 6.6
still has it.
- **Root cause (author):** template dst is not a valid routing dst;
must use `skb_valid_dst()` first.
**Step 1.4: DETECT HIDDEN BUG FIXES**
- Record: **Not hidden.** It is a real validity check. “Call X before Y”
is a missing-check fix, not cleanup.
---
## PHASE 2: DIFF ANALYSIS
**Step 2.1: INVENTORY THE CHANGES**
- Record:
- `net/ipv4/netfilter/nf_reject_ipv4.c` (+4/−2)
- `net/ipv6/netfilter/nf_reject_ipv6.c` (+6/−2)
- `net/netfilter/nft_meta.c` (+4/−2)
- `net/netfilter/nft_rt.c` (+4/−2)
- `net/netfilter/nft_xfrm.c` (+8/−1)
- **~26 insertions, 9 deletions, 5 files**
- Functions: `nf_reject_fill_skb_dst`, `nf_send_reset`,
`nf_send_unreach`, `nf_reject6_fill_skb_dst`, `nf_send_reset6`,
`nf_send_unreach6`, `nft_meta_get_eval_rtclassid`,
`nft_rt_get_eval`, `nft_xfrm_get_eval_out`
- **Scope:** small, same-subsystem, surgical
**Step 2.2: UNDERSTAND THE CODE FLOW CHANGE**
- Record:
- **Reject fill:** `skb_dst_set()` over a possible existing template →
`skb_dst_drop()` then `skb_dst_set()` of a real route.
- **Reject send:** `if (!skb_dst()) fill` → `if (!skb_valid_dst())
fill`. Template dst no longer counts as “already routed.”
- **nft_meta / nft_rt / nft_xfrm:** `skb_dst()` then NULL check →
`skb_valid_dst()` then `skb_dst()`. Template dst is treated as
missing.
**Step 2.3: IDENTIFY THE BUG MECHANISM**
- Record:
- **Category:** logic/correctness + type confusion (metadata dst used
as `rtable`/`xfrm_dst`).
- **Mechanism:** `skb_valid_dst()` is `dst && !(dst->flags &
DST_METADATA)`. Metadata dst is initialized with `dst_blackhole_ops`
and **`dev == NULL`**. Using it as `skb_rtable()` reads
`rt_flags`/`rt_gw*` from the metadata union; `nft_xfrm` would walk
`((xfrm_dst *)dst)->child` if `dst->xfrm` were set.
**Step 2.4: ASSESS THE FIX QUALITY**
- Record:
- **Quality:** obviously correct; same helper already used in
`ip_input`, `ip_tunnel`, wireguard, xfrm.
- **Minimal:** yes. Include of `net/dst_metadata.h` is required for
the helper.
- **Regression risk:** low. `meta rtclassid` on a metadata dst
currently succeeds with `tclassid == 0`; after the fix it fails
closed (`NFT_BREAK`). That is stricter and correct.
- **No API/feature additions.**
---
## PHASE 3: GIT HISTORY INVESTIGATION
**Step 3.1: BLAME THE CHANGED LINES**
- Record:
- Reject `!skb_dst() && fill`: `a0a3ace2a5788` (Florian Westphal,
2025-08-20) already **in 6.6** — “don’t leak dst refcount for
loopback packets.” That made “any dst present” skip fill, including
metadata dst.
- `nft_rt_get_eval` `skb_dst` check: `2fa841938c648` (2016), present
since nft_rt was added.
- **Bug is in 6.6, not new mainline-only code.**
**Step 3.2: FOLLOW THE FIXES TAG**
- Record: **No Fixes: tag.** Not applicable. Introducer of the reject
skip-fill logic **is** in this tree (`a0a3ace2a5788`).
**Step 3.3: CHECK FILE HISTORY**
- Record: Related 6.6 history includes `a0a3ace2a5788` (reject dst leak)
and `e64a48c50a1ff` (`nf_conntrack_sip`: validate `skb_dst()` for
tc/OVS). Same class of bug already accepted into 6.6. This commit is
standalone, not patch 2/N of a required series (it was 8/9 of an nf-
next pull, but the diff does not depend on patches 1–7).
**Step 3.4: CHECK THE AUTHOR**
- Record: **Pablo Neira Ayuso is the netfilter maintainer.** Nearby work
is normal nf-next flowtable/conncount material; this hunk is
independent.
**Step 3.5: DEPENDENT/PREREQUISITE COMMITS**
- Record:
- `skb_valid_dst()` exists in 6.6 `include/net/dst_metadata.h`.
- No new helpers required.
- **Can apply standalone** with minor context (see Phase 6).
- No follow-ups on origin/master after `689db98e535bf`.
---
## PHASE 4: MAILING LIST AND EXTERNAL RESEARCH
**Step 4.1: FIND THE ORIGINAL PATCH DISCUSSION**
- Record:
- `b4 dig -c 689db98e535bf`: **failed** (lore.kernel.org 503).
- Fallback: [PATCH net-next 8/9]
https://lists.openwall.net/netdev/2026/07/31/304
- Cover: [PATCH net-next 0/9]
https://lists.openwall.net/netdev/2026/07/31/319
- Submitted to **nf-next / net-next**, not `net` (fixes). Cover: “Call
skb_valid_dst() before accessing skb_dst() to ensure dst_entry is
not a template.”
- Syzbot CI on the nf-next posting: **passed**
(https://ci.syzbot.org/series/250b78fc-95ac-4f6f-8746-9f8d8777688b).
Not a syzbot crash report.
- No NAK found. No reviewer “Cc: stable” found.
**Step 4.2: CHECK WHO REVIEWED**
- Record: To/Cc: netfilter-devel, davem, kuba, pabeni, edumazet,
fw@strlen.de, horms. **Florian Westphal (nf core) was CC’d.** Applied
by Pablo himself. No extra Reviewed-by on the commit.
**Step 4.3: SEARCH FOR THE BUG REPORT**
- Record: **No user/syzbot/bugzilla report.** Author-driven correctness
fix.
**Step 4.4: RELATED PATCHES AND SERIES**
- Record: Series 1–7 is flowtable/`fill_forward_path` work; patch 9 is
conntrack TCP timeout. **This patch does not depend on them.** Same-
class SIP helper fix already in 6.6.
**Step 4.5: STABLE MAILING LIST HISTORY**
- Record: lore.kernel.org/stable fetch returned **403**. **UNVERIFIED**
whether stable@ discussed this SHA. No evidence it was rejected for
stable.
---
## PHASE 5: CODE SEMANTIC ANALYSIS
**Step 5.1: KEY FUNCTIONS**
- Record: listed in 2.1.
**Step 5.2: TRACE CALLERS**
- Record (6.6):
- `nf_send_unreach` / `nf_send_reset`: `nft_reject_ipv4_eval`,
`nft_reject_inet_eval`, `ipt_REJECT`.
- IPv6 equivalents: nft reject ipv6/inet, ip6t_REJECT.
- **netdev reject does not use these**; it uses `nf_reject_skb_v4_*` /
`nf_reject_skb_v6_*` (unchanged).
- `nft_rt_get_eval`: packet path for the `rt` expression; **family
limited to ipv4/ipv6/inet** (not netdev).
- `nft_xfrm_get_eval_out`: ipv4/ipv6/inet, OUT hooks only.
- `nft_meta_get_eval_rtclassid`: **allowed on netdev** (no hook
restriction for this key).
**Step 5.3: TRACE CALLEES**
- Record: `skb_valid_dst`, `skb_dst_drop`/`skb_dst_set`,
`nf_ip_route`/`nf_ip6_route`, `skb_rtable`, `icmp_send`/`icmpv6_send`,
`ip_route_me_harder`.
**Step 5.4: FOLLOW THE CALL CHAIN**
- Record: Reachable from nftables/iptables **REJECT** and from
`rt`/`xfrm`/`meta rtclassid` eval. Userspace (`nft`/`iptables`)
installs the rules. Overlay tunnels (VXLAN/GENEVE collect-md, TC
tunnel_key) attach metadata dst, then the inner packet hits **inet
ingress / PRE_ROUTING**. **Yes, reachable.**
**Step 5.5: SIMILAR PATTERNS**
- Record: Same `skb_valid_dst` pattern in `net/ipv4/ip_input.c`,
`ip_tunnel.c`, `xfrm_input.c`. 6.6 already has `nf_conntrack_sip` dst
validation for tc/OVS.
---
## PHASE 6: CROSS-REFERENCING AGAINST THE LOCAL TREE (6.6.157)
**Step 6.1: DOES THE BUGGY CODE EXIST?**
- Record: **Yes.**
- `nf_reject_ipv4.c`: `if (!skb_dst(oldskb) &&
nf_reject_fill_skb_dst...)` and no `skb_dst_drop` before
`skb_dst_set`.
- Same for IPv6 reject.
- `nft_rt.c`: `dst = skb_dst(skb); if (!dst) goto err;`
- `nft_meta.c`: `const struct dst_entry *dst = skb_dst(skb); if (!dst)
return false;`
- `nft_xfrm.c`: `const struct dst_entry *dst = skb_dst(pkt->skb);`
then walk `dst->xfrm`.
- 6.6 **has** netdev **ingress and egress**
(`nft_chain_filter_netdev`), **NF_INET_INGRESS** (inet reject
validates it), and `skb_valid_dst()`.
**Step 6.2: BACKPORT COMPLICATIONS**
- Record: **Minor context, not rework.**
- 6.6 `nft_rt` uses `(const struct rtable *)dst` not `dst_rtable()`
(unchanged context).
- 6.6 `nf_send_reset6` uses `skb_dst(oldskb)->dev` not `skb_dst_dev()`
(unchanged context). `skb_dst_dev()` exists in 6.6
`include/net/dst.h` but is not used in this file.
- Functional hunks match 6.6.
**Step 6.3: RELATED FIXES ALREADY HERE**
- Record: `a0a3ace2a5788` (reject dst leak) and `e64a48c50a1ff` (SIP
skb_dst). **This metadata-vs-real-dst check is not already applied.**
`git merge-base --is-ancestor 689db98e535bf pending-6.6` → not
ancestor.
---
## PHASE 7: SUBSYSTEM AND MAINTAINER CONTEXT
**Step 7.1: SUBSYSTEM AND CRITICALITY**
- Record: **netfilter / nf_tables + IPv4/IPv6 reject.** **IMPORTANT**
(firewall/NAT on almost every server/router/container host). Not core
mm/scheduler, but production-critical networking.
**Step 7.2: SUBSYSTEM ACTIVITY**
- Record: Mature and actively fixed. nft_rt dates to 2016; reject-at-
prerouting to 2020 (`f53b9b0bdc59`). Long-lived code.
---
## PHASE 8: IMPACT AND RISK ASSESSMENT
**Step 8.1: WHO IS AFFECTED**
- Record: **Config/driver-specific but common:** `nftables`/`iptables`
REJECT, `rt`, `meta rtclassid`, `xfrm`, plus overlay/TC metadata dst
(Kubernetes, OVN, VXLAN/GENEVE collect-md).
**Step 8.2: TRIGGER CONDITIONS**
- Record: Packet carries DST_METADATA **and** hits reject or those
expressions. Common on inet ingress/PRE_ROUTING of collect-md tunnels.
Unprivileged users cannot install nft rules (CAP_NET_ADMIN); they
**can** send traffic that hits existing rules. Timing race: no.
**Step 8.3: FAILURE MODE SEVERITY**
- Record:
- **Verified:** type confusion (`skb_rtable()` on metadata dst);
`rt_flags`/`nexthop` taken from the metadata union; reject may skip
`nf_reject_fill_skb_dst` and feed `icmp_send`/`ip_route_me_harder` a
template dst.
- **6.6 `__icmp_send`:** `if (!rt) return` is **not** enough (template
is non-NULL); `rt->dst.dev` NULL is handled, then `rt->rt_flags` is
still type-confused.
- **Not a proven KASAN UAF.** Metadata dst uses `dst_blackhole_ops`,
so `ops->mtu` is not NULL. `dst->xfrm` is NULL on metadata dst, so
the xfrm walk typically does not follow a garbage `child`.
- **Severity: HIGH for firewall correctness** (wrong `rt`/`meta`
match; REJECT may not emit RST/ICMP). **MEDIUM as a crash**
(plausible UB/type confusion, not a demonstrated oops). Still a real
bug in a hot firewall path.
**Step 8.4: RISK-BENEFIT**
- Record:
- **Benefit:** high for nft/iptables + overlay users; restores
intended “no real route → lookup or fail closed.”
- **Risk:** low (26 lines, existing helper, fail closed).
- **Ratio:** favorable.
---
## PHASE 9: FINAL SYNTHESIS
**Step 9.1: EVIDENCE**
**FOR:**
- Real bug: template dst treated as a route.
- Present in 6.6; `skb_valid_dst()` already exists.
- Reject skip-fill was introduced/worsened by a commit **already in
6.6**.
- Small, maintainer-authored, obviously correct.
- Same class as SIP `skb_dst` validation already in 6.6 stable.
- Realistic trigger: collect-md tunnels + nft/iptables at
ingress/prerouting.
- No follow-up reverting or rewriting this change.
**AGAINST:**
- Posted as net-next 8/9, not `net`.
- No Fixes/Cc: stable, no crash report.
- netdev-family `reject` does not call `nf_send_*`; `nft_rt`/`nft_xfrm`
cannot be used on netdev family even in mainline.
- Crash is not as hard as a NULL `ops` deref (blackhole ops).
**UNRESOLVED:**
- lore.kernel.org was 503/403; no full review thread beyond Openwall. No
stable@ discussion verified.
**Step 9.2: STABLE RULES CHECKLIST**
1. Obviously correct and tested? **PASS** — helper semantics are clear;
syzbot CI passed; in mainline since 2026-07.
2. Fixes a real bug that affects users? **PASS** — metadata dst on
ingress/tunnel paths is real; expressions/reject mishandle it.
3. Important (crash/security/corruption/deadlock)? **PASS** — firewall
mis-eval and failed REJECT replies on overlay/ingress; type-confused
`rtable` use. Not a proven panic, but important networking
correctness.
4. Small and contained? **PASS** — 26/9 lines, five netfilter files.
5. No new features or APIs? **PASS**
6. Can apply to this tree? **PASS** — minor context (`dst_rtable` /
`skb_dst_dev` in unchanged lines only).
**Step 9.3: EXCEPTION CATEGORIES**
- Record: none (not a device ID/quirk/DT/build/docs exception).
Qualifies as a normal bugfix.
**Step 9.4: DECISION**
This is a small, correct check that 6.6 needs. The tree has the buggy
`!skb_dst()` reject path, nft `rt`/`meta`/`xfrm` eval, inet ingress, and
netdev ingress/egress. Benefit outweighs risk.
---
### What the commit solves for 6.6 users
Packets with a **metadata/template dst** (VXLAN/GENEVE collect-md, TC)
make `skb_dst()` non-NULL, so reject never installs a real route and
`rt`/`meta rtclassid`/`xfrm` inspect a non-route. Users see wrong nft
matches and missing TCP RST / ICMP unreachable. The fix uses the
existing `skb_valid_dst()` helper and drops any template dst before
attaching a real one.
### Risk vs benefit
Low risk, high correctness value on firewall + overlay hosts. Apply may
need a one-line context tweak in `nft_rt.c` and `nf_reject_ipv6.c`.
### Verification
- **[Phase 1]** Parsed subject/body; tags: only Pablo SOB; no
Fixes/Reported-by/stable.
- **[Phase 2]** Diff: 5 files, +26/−9; `skb_valid_dst` + `skb_dst_drop`
before `skb_dst_set`.
- **[Phase 3]** `git describe` of pending-6.6: `p-6.6-3-g04810563abb71`,
Makefile 6.6.157.
- **[Phase 3]** `git merge-base --is-ancestor 689db98e535bf pending-6.6`
→ not present.
- **[Phase 3]** Blame: reject condition `a0a3ace2a5788` (in 6.6); nft_rt
`2fa841938c648` (2016).
- **[Phase 3]** `git show pending-6.6:include/net/dst_metadata.h`:
`skb_valid_dst()` present.
- **[Phase 4]** `b4 dig -c 689db98e535bf`: lore 503, no match.
- **[Phase 4]** Openwall: net-next 8/9 and 0/9 cover; no NAK.
- **[Phase 4]** Syzbot CI series passed; not a syzbot bug report.
- **[Phase 4]** lore.kernel.org/stable: 403, UNVERIFIED.
- **[Phase 5]** Callers verified via `git grep` on pending-6.6:
ipt_REJECT, nft_reject_ipv4/inet; netdev reject uses `nf_reject_skb_*`
(not this path).
- **[Phase 5]** `nft_rt_validate` / `nft_xfrm_validate` reject
NFPROTO_NETDEV in both 6.6 and mainline.
- **[Phase 6]** Buggy `!skb_dst()` / raw `skb_dst()` still in
pending-6.6 sources.
- **[Phase 6]** Context drift: `dst_rtable` and `skb_dst_dev` not used
in 6.6 versions of those unchanged lines.
- **[Phase 7]** nf_tables + reject; important.
- **[Phase 8]** `__icmp_send` in 6.6 handles NULL `dst->dev` but still
reads `rt->rt_flags` from a metadata object.
- **[Phase 8]** Metadata dst: `dst_init(..., NULL, ...)` +
`DST_METADATA` + `dst_blackhole_ops`; `xfrm == NULL`.
- **UNVERIFIED:** full lore review replies (503); stable@ archive (403).
**YES**
net/ipv4/netfilter/nf_reject_ipv4.c | 6 ++++--
net/ipv6/netfilter/nf_reject_ipv6.c | 8 ++++++--
net/netfilter/nft_meta.c | 6 ++++--
net/netfilter/nft_rt.c | 6 ++++--
net/netfilter/nft_xfrm.c | 9 ++++++++-
5 files changed, 26 insertions(+), 9 deletions(-)
diff --git a/net/ipv4/netfilter/nf_reject_ipv4.c b/net/ipv4/netfilter/nf_reject_ipv4.c
index 4626dc46808f3..59ec465a9df98 100644
--- a/net/ipv4/netfilter/nf_reject_ipv4.c
+++ b/net/ipv4/netfilter/nf_reject_ipv4.c
@@ -8,6 +8,7 @@
#include <net/tcp.h>
#include <net/route.h>
#include <net/dst.h>
+#include <net/dst_metadata.h>
#include <net/netfilter/ipv4/nf_reject.h>
#include <linux/netfilter_ipv4.h>
#include <linux/netfilter_bridge.h>
@@ -263,6 +264,7 @@ static int nf_reject_fill_skb_dst(struct sk_buff *skb_in)
if (!dst)
return -1;
+ skb_dst_drop(skb_in);
skb_dst_set(skb_in, dst);
return 0;
}
@@ -279,7 +281,7 @@ void nf_send_reset(struct net *net, struct sock *sk, struct sk_buff *oldskb,
if (!oth)
return;
- if (!skb_dst(oldskb) && nf_reject_fill_skb_dst(oldskb) < 0)
+ if (!skb_valid_dst(oldskb) && nf_reject_fill_skb_dst(oldskb) < 0)
return;
if (skb_rtable(oldskb)->rt_flags & (RTCF_BROADCAST | RTCF_MULTICAST))
@@ -352,7 +354,7 @@ void nf_send_unreach(struct sk_buff *skb_in, int code, int hook)
if (iph->frag_off & htons(IP_OFFSET))
return;
- if (!skb_dst(skb_in) && nf_reject_fill_skb_dst(skb_in) < 0)
+ if (!skb_valid_dst(skb_in) && nf_reject_fill_skb_dst(skb_in) < 0)
return;
if (skb_csum_unnecessary(skb_in) ||
diff --git a/net/ipv6/netfilter/nf_reject_ipv6.c b/net/ipv6/netfilter/nf_reject_ipv6.c
index ef5b7e85cffa8..07cdaa10da0d0 100644
--- a/net/ipv6/netfilter/nf_reject_ipv6.c
+++ b/net/ipv6/netfilter/nf_reject_ipv6.c
@@ -8,6 +8,7 @@
#include <net/ip6_route.h>
#include <net/ip6_fib.h>
#include <net/ip6_checksum.h>
+#include <net/dst_metadata.h>
#include <net/netfilter/ipv6/nf_reject.h>
#include <linux/netfilter_ipv6.h>
#include <linux/netfilter_bridge.h>
@@ -304,6 +305,7 @@ static int nf_reject6_fill_skb_dst(struct sk_buff *skb_in)
if (!dst)
return -1;
+ skb_dst_drop(skb_in);
skb_dst_set(skb_in, dst);
return 0;
}
@@ -336,10 +338,12 @@ void nf_send_reset6(struct net *net, struct sock *sk, struct sk_buff *oldskb,
fl6.fl6_sport = otcph->dest;
fl6.fl6_dport = otcph->source;
- if (!skb_dst(oldskb)) {
+ if (!skb_valid_dst(oldskb)) {
nf_ip6_route(net, &dst, flowi6_to_flowi(&fl6), false);
if (!dst)
return;
+
+ skb_dst_drop(oldskb);
skb_dst_set(oldskb, dst);
}
@@ -440,7 +444,7 @@ void nf_send_unreach6(struct net *net, struct sk_buff *skb_in,
if (hooknum == NF_INET_LOCAL_OUT && skb_in->dev == NULL)
skb_in->dev = net->loopback_dev;
- if (!skb_dst(skb_in) && nf_reject6_fill_skb_dst(skb_in) < 0)
+ if (!skb_valid_dst(skb_in) && nf_reject6_fill_skb_dst(skb_in) < 0)
return;
icmpv6_send(skb_in, ICMPV6_DEST_UNREACH, code, 0);
diff --git a/net/netfilter/nft_meta.c b/net/netfilter/nft_meta.c
index 19e6d1c2436af..f3c7b7edb10c0 100644
--- a/net/netfilter/nft_meta.c
+++ b/net/netfilter/nft_meta.c
@@ -20,6 +20,7 @@
#include <net/dst.h>
#include <net/ip.h>
#include <net/sock.h>
+#include <net/dst_metadata.h>
#include <net/tcp_states.h> /* for TCP_TIME_WAIT */
#include <net/netfilter/nf_tables.h>
#include <net/netfilter/nf_tables_core.h>
@@ -274,11 +275,12 @@ static bool nft_meta_get_eval_ifname(enum nft_meta_keys key, u32 *dest,
static noinline bool
nft_meta_get_eval_rtclassid(const struct sk_buff *skb, u32 *dest)
{
- const struct dst_entry *dst = skb_dst(skb);
+ const struct dst_entry *dst;
- if (!dst)
+ if (!skb_valid_dst(skb))
return false;
+ dst = skb_dst(skb);
*dest = dst->tclassid;
return true;
}
diff --git a/net/netfilter/nft_rt.c b/net/netfilter/nft_rt.c
index ad527f3596c03..cb7dff336bbdc 100644
--- a/net/netfilter/nft_rt.c
+++ b/net/netfilter/nft_rt.c
@@ -8,6 +8,7 @@
#include <linux/netfilter.h>
#include <linux/netfilter/nf_tables.h>
#include <net/dst.h>
+#include <net/dst_metadata.h>
#include <net/ip6_route.h>
#include <net/route.h>
#include <net/netfilter/nf_tables.h>
@@ -59,10 +60,11 @@ void nft_rt_get_eval(const struct nft_expr *expr,
u32 *dest = ®s->data[priv->dreg];
const struct dst_entry *dst;
- dst = skb_dst(skb);
- if (!dst)
+ if (!skb_valid_dst(skb))
goto err;
+ dst = skb_dst(skb);
+
switch (priv->key) {
#ifdef CONFIG_IP_ROUTE_CLASSID
case NFT_RT_CLASSID:
diff --git a/net/netfilter/nft_xfrm.c b/net/netfilter/nft_xfrm.c
index 7ffe6a2690d13..9c9ac27122ad9 100644
--- a/net/netfilter/nft_xfrm.c
+++ b/net/netfilter/nft_xfrm.c
@@ -12,6 +12,7 @@
#include <linux/netfilter/nf_tables.h>
#include <net/netfilter/nf_tables_core.h>
#include <net/netfilter/nf_tables.h>
+#include <net/dst_metadata.h>
#include <linux/in.h>
#include <net/xfrm.h>
@@ -177,9 +178,15 @@ static void nft_xfrm_get_eval_out(const struct nft_xfrm *priv,
struct nft_regs *regs,
const struct nft_pktinfo *pkt)
{
- const struct dst_entry *dst = skb_dst(pkt->skb);
+ const struct dst_entry *dst;
int i;
+ if (!skb_valid_dst(pkt->skb)) {
+ regs->verdict.code = NFT_BREAK;
+ return;
+ }
+
+ dst = skb_dst(pkt->skb);
for (i = 0; dst && dst->xfrm;
dst = ((const struct xfrm_dst *)dst)->child, i++) {
if (i < priv->spnum)
--
2.53.0
^ permalink raw reply related [flat|nested] 32+ messages in thread* [PATCH AUTOSEL 6.18-5.15] net: stmmac: raise TX completion interrupt at the end of an xmit burst
2026-10-19 13:44 [PATCH AUTOSEL 6.18-6.12] net: dsa: mt7530: check CORE_PLL_GROUP4 access in mt7531_setup() Sasha Levin
` (23 preceding siblings ...)
2026-10-19 13:51 ` [PATCH AUTOSEL 6.18-5.10] netfilter: nf_tables: call skb_valid_dst() before skb_dst() Sasha Levin
@ 2026-10-19 13:51 ` Sasha Levin
2026-10-19 13:51 ` [PATCH AUTOSEL 6.18-6.6] net: pcs: mtk-lynxi: check regmap reads in mtk_pcs_lynxi_get_state() Sasha Levin
` (4 subsequent siblings)
29 siblings, 0 replies; 32+ messages in thread
From: Sasha Levin @ 2026-10-19 13:51 UTC (permalink / raw)
To: patches, stable
Cc: Johan Alvarado, Jakub Kicinski, Sasha Levin, maxime.chevallier,
andrew+netdev, davem, edumazet, pabeni, mcoquelin.stm32,
alexandre.torgue, netdev, linux-stm32, linux-arm-kernel,
linux-kernel
From: Johan Alvarado <contact@c127.dev>
[ Upstream commit d21a374995df9d7cfd193427748a19dcc87956a9 ]
The TX mitigation logic only sets the Interrupt on Completion bit once
every tx_coal_frames descriptors (STMMAC_TX_FRAMES = 25), with the
tx_coal_timer hrtimer (STMMAC_COAL_TX_TIMER = 5000 us) as the only
fallback. TX skbs are freed exclusively from the TX completion path,
so any flow that keeps fewer than 25 frames in flight has all of its
skbs held for up to 5 ms after transmission.
Paced flows never queue enough frames to reach the frame threshold:
TCP Small Queues caps the amount of unfreed data at roughly two pacing
intervals worth, which at moderate pacing rates is only a couple of
packets. Every small burst then stalls until the coalesce timer fires,
and throughput collapses to approximately tsq_limit / tx_coal_timer
regardless of link capacity.
This is easily reproducible with BBR, which paces its output and thus
keeps only a few frames in flight at a time. On a YT6801
(dwmac-motorcomm) equipped Orange Pi 5 Pro, a BBR upload over a ~23 ms
RTT path is capped at 5.24 Mbit/s, while CUBIC reaches 207 Mbit/s on
the same path. BBR measures the stalled send rate as the path
bandwidth and locks its estimate near the floor, so the connection
never recovers. Lowering the coalesce settings with ethtool -C
(tx-usecs 100 tx-frames 1) lifts the same transfer to 447 Mbit/s,
confirming the mechanism.
Fix this by setting the IC bit on the last descriptor of every xmit
burst, i.e. whenever netdev_xmit_more() reports that no further frames
are pending in the current dequeue batch. Frame-based coalescing still
applies within a burst, bulk traffic keeps batching through qdisc bulk
dequeue and NAPI polling, and the coalesce timer becomes a pure
fallback instead of the primary completion mechanism for lightly
queued flows.
tx-frames 0 keeps its meaning of timer-based mitigation only.
Signed-off-by: Johan Alvarado <contact@c127.dev>
Link: https://patch.msgid.link/20260731194522.55069-1-contact@c127.dev
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
LLM Generated explanations, may be completely bogus:
The local tree is **6.12.111**. The TX-coalesce stall is in this
checkout; the four-line IC-on-burst-end change is a real, tested fix for
a severe paced-TCP collapse on stmmac.
===============================================================
PHASE 1: COMMIT MESSAGE FORENSICS
===============================================================
Step 1.1: PARSE THE SUBJECT LINE
Record: [net: stmmac] [raise] [set TX Interrupt-on-Completion on the
last descriptor of each xmit burst so lightly queued/paced flows are not
stuck on the 5 ms coalesce timer]
Step 1.2: PARSE ALL COMMIT MESSAGE TAGS
Record:
- Link: https://patch.msgid.link/20260731194522.55069-1-contact@c127.dev
- Signed-off-by: Johan Alvarado \<contact@c127.dev\> (author)
- Signed-off-by: Jakub Kicinski \<kuba@kernel.org\> (netdev maintainer,
applied)
- No Reported-by / Tested-by / Reviewed-by / Acked-by / Cc: stable in
the committed message
- No Fixes: in the committed message (expected for this pipeline)
Original mailing-list submission (v1 and RESEND) DID carry:
- Fixes: da2024510031 ("net: stmmac: Tune-up default coalesce settings")
That tag is treated as technical evidence of the introducing commit, not
as a “already selected for stable” marker.
Step 1.3: ANALYZE THE COMMIT BODY TEXT
Record:
- Bug: TX IC is set only every `tx_coal_frames` descriptors (default
25). The 5 ms `tx_coal_timer` is the only fallback. TX skbs are freed
only from the completion path, so a flow with fewer than 25 frames in
flight holds those skbs for up to 5 ms.
- Symptom: TCP Small Queues + pacing (BBR) never queues 25 frames. Each
small burst waits for the timer. Throughput collapses to ~tsq_limit /
tx_coal_timer. BBR then treats that stalled rate as path bandwidth and
never recovers.
- Measured on Orange Pi 5 Pro / YT6801: BBR 5.24 Mbit/s vs CUBIC 207
Mbit/s; ethtool `-C tx-usecs 100 tx-frames 1` raises the same transfer
to 447 Mbit/s.
- Root cause: coalescing designed for deep queues; lightly queued flows
only complete via the timer.
- Fix: set IC when `!netdev_xmit_more()` (end of current dequeue batch).
Frame coalescing still applies inside a burst; `tx-frames 0` stays
timer-only.
Step 1.4: DETECT HIDDEN BUG FIXES
Record: Not a disguised cleanup. Submitted as `[PATCH net]` (bugfix
tree, not net-next). It is a logic/correctness fix for TX completion
starvation interacting with TSQ, not a style change.
===============================================================
PHASE 2: DIFF ANALYSIS - LINE BY LINE
===============================================================
Step 2.1: INVENTORY THE CHANGES
Record:
- File: `drivers/net/ethernet/stmicro/stmmac/stmmac_main.c`
- +4 / −0 (two identical two-line insertions)
- Functions: `stmmac_tso_xmit()`, `stmmac_xmit()`
- Scope: single-file surgical fix
Step 2.2: UNDERSTAND THE CODE FLOW CHANGE
Record:
- Before (both functions): set_ic if HW timestamp, else if
`tx_coal_frames==0` never set IC, else if this skb’s descriptor count
exceeds the threshold, else if the running modulo wrapped, else false.
Then arm the 5 ms timer.
- After: same, plus `else if (!netdev_xmit_more()) set_ic = true`
immediately after the `tx_coal_frames==0` check, so the last skb of a
dequeue batch gets IC unless the user asked for timer-only (`tx-frames
0`).
- Path: normal `ndo_start_xmit`, not an error/init path.
Step 2.3: IDENTIFY THE BUG MECHANISM
Record:
- Category: logic / correctness (TX completion starvation vs TCP Small
Queues)
- Mechanism: IC density was bounded by frame count only. Paced flows
never reach 25 in-flight frames, so completion waits for the hrtimer.
TSQ will not queue more until skbs are freed, so send rate is timer-
capped. Setting IC on burst end completes those skbs promptly; intra-
burst coalescing and the timer remain as fallback.
Step 2.4: ASSESS THE FIX QUALITY
Record:
- Obviously correct: `netdev_xmit_more()` is the standard “more packets
in this dequeue” signal; many other NIC drivers already use it this
way. Placement after `!tx_coal_frames` preserves timer-only mode.
- Minimal: four lines, no API/data-structure change.
- Regression risk: more TX IRQs on short bursts. Author measured ~3.3k
IRQs/s (~12 packets/IRQ) at 447 Mbit/s. Bulk traffic still coalesces
inside qdisc bulk dequeue / TSO. XSK/XDP paths intentionally unchanged
(`netdev_xmit_more()` is not meaningful there; no TSQ). Maciej
Fijalkowski asked whether coalescing could be dropped entirely; the
author kept the frame bound so a bulk batch larger than the ring
cannot drain with zero IC bits. Low regression risk.
===============================================================
PHASE 3: GIT HISTORY INVESTIGATION
===============================================================
Step 3.1: BLAME THE CHANGED LINES
Record:
- Core IC logic: c2837423cb5455 (“net: stmmac: Rework TX Coalesce
logic”, 2019-11-14)
- `!tx_coal_frames` / per-queue threshold: db2f2842e6f560 (2021-03-17)
- Default TX_FRAMES 1→25: da2024510031 (v5.5-rc1, 2019-11) — this is
what made “every frame gets IC” into “every 25 descriptors”
- Timer 1 ms→5 ms: 039550960a223 (2023-10-19), present in this tree —
same stall, worse ceiling
- Buggy pattern has been in tree since ~v5.5; this is long-standing, not
new-in-6.13+ code
Step 3.2: FOLLOW THE FIXES: TAG
Record:
- Original submission tagged Fixes: da2024510031
- `git merge-base --is-ancestor da2024510031 HEAD` → yes (exit 0)
- That commit only retuned defaults (`STMMAC_TX_FRAMES` 1→25). Combined
with timer-only fallback, that is exactly the stall.
- Intermediate related work already in this tree: c2837423cb5455
(rework), 2d1a42cf7f77c (timer arm vs NAPI, 2023), 039550960a223 (5 ms
timer). None of those set IC at burst end.
Step 3.3: CHECK FILE HISTORY FOR RELATED CHANGES
Record:
- Recent `stmmac_main.c` work is TSO descriptor checks, PTP lock init,
XDP, etc. Nothing equivalent to this IC-on-burst-end change.
- `git log --grep=netdev_xmit_more --
drivers/net/ethernet/stmicro/stmmac/` → empty; the helper is unused in
stmmac here.
- Standalone 4-line fix, not patch N/M.
Step 3.4: CHECK THE AUTHOR'S OTHER COMMITS
Record:
- Johan Alvarado has no prior stmmac commits in this tree (end-user
reporter/fixer, not the subsystem maintainer).
- Maintainers CC’d on the list: Alexandre Torgue, Jose Abreu. Applied by
Jakub Kicinski.
Step 3.5: CHECK FOR DEPENDENT/PREREQUISITE COMMITS
Record:
- Needs `netdev_xmit_more()` — present in `include/linux/netdevice.h`
(this tree).
- Needs the existing `set_ic` / `stmmac_set_tx_ic` / `tx_coal_frames`
block — present in both xmit paths.
- Mainline context uses `CIRC_CNT` / `stmmac_get_tx_desc`; those helpers
are not required. The added lines sit in context that is identical
here:
`else if (!priv->tx_coal_frames[queue]) set_ic = false;`
- Applies standalone.
===============================================================
PHASE 4: MAILING LIST AND EXTERNAL RESEARCH
===============================================================
Step 4.1: FIND THE ORIGINAL PATCH DISCUSSION
Record:
- b4 dig on the msgid did not resolve a local commit (this SHA is not in
the 6.12 checkout). Fallback: openwall + patchew.
- v1: 2026-07-06, Message-ID `0100019f35ea26e0-42ad009c-01ab-4a8f-b126-
fa65fbacae5c-000000@email.amazonses.com`
- RESEND: 2026-07-31, msgid `20260731194522.55069-1-contact@c127.dev`
after netdev queue overflow (Jakub asked for a repost). Author: no
code changes vs v1.
- Reviewer Maciej Fijalkowski: agreed each TX batch should get IC at the
end; asked if coalescing could be dropped. Author explained why the
frame bound must stay (ring-full with xmit_more set on every frame
until the queue stops → zero IC → timer stall on the bulk path). Kept
the patch minimal for `net`.
- No NAK. No reviewer concern that the change is unsafe for the IC-on-
burst-end approach.
- No explicit “Cc: stable” in the thread. Submitted to `net` as a
bugfix.
Step 4.2: CHECK WHO REVIEWED THE PATCH
Record:
- To/Cc: netdev maintainers (Kicinski, Abeni, Dumazet, Daleck, Lunn),
stmmac (Torgue, Abreu, Coquelin), Pavel Machek (historical reporter),
linux-stm32, linux-arm-kernel.
- Substantive review: Maciej Fijalkowski. Applied by Jakub Kicinski.
Step 4.3: SEARCH FOR THE BUG REPORT
Record:
- Concrete iperf3 numbers from the author on real hardware (Orange Pi 5
Pro).
- Historical same class of bug: Pavel Machek 2016 UDP burst pauses (40
ms low-res timer); his “disable coalescing” patch was rejected in
favor of a real solution. hrtimer conversion later fixed resolution
but left timer-as-only-completion for light flows.
- Severity from reporter: BBR unusable (~5 Mbit/s vs hundreds).
Step 4.4: CHECK FOR RELATED PATCHES AND SERIES
Record: Single standalone patch. Author deferred any coalescing rework
to net-next. XSK/XDP IC logic deliberately not included.
Step 4.5: CHECK STABLE MAILING LIST HISTORY
Record: lore.kernel.org/stable search returned 403. No evidence on
openwall/patchew that stable rejected this. UNVERIFIED that it was
discussed on stable@ specifically.
===============================================================
PHASE 5: CODE SEMANTIC ANALYSIS
===============================================================
Step 5.1: IDENTIFY KEY FUNCTIONS IN THE DIFF
Record: `stmmac_tso_xmit`, `stmmac_xmit`. Helper used:
`netdev_xmit_more`. Callee already present: `stmmac_set_tx_ic`.
Step 5.2: TRACE CALLERS
Record:
- `stmmac_xmit` is `ndo_start_xmit` (`stmmac_netdev_ops` at line 7155).
- `stmmac_tso_xmit` is called from `stmmac_xmit` for TSO/GSO packets
(lines 4529/4531).
- Context: process / softirq xmit (BH off). Userspace sockets, not
probe-only.
Step 5.3: TRACE CALLEES
Record: `stmmac_set_tx_ic` sets the hardware IC bit;
`stmmac_tx_timer_arm` still runs after xmit (lines 4461 and 4750). Timer
remains the fallback if IC is not set or NAPI is not scheduled.
Step 5.4: FOLLOW THE CALL CHAIN
Record: userspace send/write → TCP → qdisc → `ndo_start_xmit` →
`stmmac_xmit` / `stmmac_tso_xmit`. Reachable on every stmmac Ethernet
device. Unprivileged users who can send on the interface (typical
networked host) trigger it. BBR/pacing makes it easy to hit.
Step 5.5: SEARCH FOR SIMILAR PATTERNS
Record: `netdev_xmit_more()` is already used for doorbell/IC batching in
many drivers in this tree (r8169, mlx5, virtio_net, qede, ionic, sfc,
forcedeth, …). stmmac is the outlier. XSK/XDP in the same file still use
frame-count-only IC; that is intentional and not a missed copy of this
bug (no TSQ).
===============================================================
PHASE 6: CROSS-REFERENCING AGAINST THE LOCAL TREE
===============================================================
Step 6.1: DOES THE BUGGY CODE EXIST IN THIS TREE?
Record: Yes. `stmmac_tso_xmit` ~4380–4390 and `stmmac_xmit` ~4645–4655:
same if/else IC logic, no `netdev_xmit_more()` branch. Defaults in this
tree: `STMMAC_TX_FRAMES` 25, `STMMAC_COAL_TX_TIMER` 5000 (`common.h`).
`git describe HEAD` → `p-6.12-2-gd38d58a4e0359`; `make kernelversion` →
6.12.111.
dwmac-motorcomm / YT6801 is not in this tree; that was only the
reporter’s NIC. The bug lives in common `stmmac_main.c`, which 6.12
already uses for dwmac-rk, dwmac-sunxi, dwmac-stm32, dwmac-intel, dwmac-
imx, dwmac-qcom-ethqos, dwmac-ipq806x, dwmac-meson, etc.
Step 6.2: CHECK FOR BACKPORT COMPLICATIONS
Record: Expected apply: clean for the four added lines. Surrounding
mainline helpers (`CIRC_CNT`, `stmmac_get_tx_desc`) differ, but they are
not part of this hunk. Insertion context matches 6.12 exactly.
Step 6.3: CHECK IF RELATED FIXES ARE ALREADY HERE
Record: 2d1a42cf7f77c (don’t re-arm timer while NAPI is scheduled) and
039550960a223 (5 ms timer) are in this tree. They do not set burst-end
IC. No equivalent fix present.
===============================================================
PHASE 7: SUBSYSTEM AND MAINTAINER CONTEXT
===============================================================
Step 7.1: IDENTIFY THE SUBSYSTEM AND ITS CRITICALITY
Record: [net / stmmac Ethernet] [IMPORTANT — not mm/core, but the common
DWMAC driver for a large set of ARM/x86 embedded and laptop NICs]
Step 7.2: ASSESS SUBSYSTEM ACTIVITY
Record: Actively maintained (recent TSO, XDP, PTP, FPE fixes). Coalesce
core has been stable since 2019 with only timer-arm tweaks in 2023.
===============================================================
PHASE 8: IMPACT AND RISK ASSESSMENT
===============================================================
Step 8.1: DETERMINE WHO IS AFFECTED
Record: Driver-specific, but a widely used driver. Anyone using stmmac
with default coalescing and paced/TSQ-limited TCP (BBR is
`CONFIG_TCP_CONG_BBR` in this tree). CUBIC is less affected because it
queues more deeply (207 vs 5 Mbit/s in the report).
Step 8.2: DETERMINE THE TRIGGER CONDITIONS
Record: Normal TCP upload with pacing/TSQ and default ethtool coalesce
(tx-frames 25, tx-usecs 5000). Common on WAN/RTT paths, not a rare error
path. Unprivileged users sending data can trigger it. Workaround exists
(`ethtool -C`) but is not discoverable for most users.
Step 8.3: DETERMINE THE FAILURE MODE SEVERITY
Record: [TCP TX completion stall / order-of-magnitude throughput
collapse; BBR bandwidth lock-in] [HIGH for affected workloads — not a
panic/UAF/corruption, but the NIC is effectively unusable for paced TCP]
Step 8.4: CALCULATE RISK-BENEFIT RATIO
Record:
- Benefit: high for stmmac + BBR/pacing (5 → 447 Mbit/s in the report);
restores TSQ-friendly completion.
- Risk: low (4 lines, well-known helper, coalescing preserved in-burst,
`tx-frames 0` unchanged). 2023 ipq806x pain was timer-arm frequency
under bulk load; this patch does not change timer arming and keeps the
25-descriptor IC bound so a full ring cannot drain with zero IC.
- Ratio: benefit clearly outweighs risk.
===============================================================
PHASE 9: FINAL SYNTHESIS
===============================================================
Step 9.1: COMPILE THE EVIDENCE
FOR:
- Real, measured bug: BBR ~5 Mbit/s vs CUBIC ~207 Mbit/s; ethtool
experiment confirms mechanism.
- TSQ/pacing vs delayed TX completion is a known correctness class, not
a micro-optimization.
- Introducing change da2024510031 (TX_FRAMES 1→25) is in this tree since
v5.5; 5 ms timer (039550960a223) is also here.
- Four-line, obviously correct change; `netdev_xmit_more()` already in
this tree.
- Applies on identical context in `stmmac_xmit` / `stmmac_tso_xmit`.
- `[PATCH net]`, reviewed, applied by netdev maintainer; reviewer agreed
IC belongs at batch end.
- Workaround requires non-default ethtool; default shipped settings are
the broken ones.
AGAINST:
- Not a crash, UAF, deadlock, or security bug.
- Changes IRQ pattern (more interrupts on short bursts).
- Test NIC (dwmac-motorcomm) is not in 6.12 (common-path bug still is).
- Original thread did not Cc stable@.
- 2023 work raised the timer specifically to cut IRQ rate on some SoCs —
theoretical regression if burst-end IC is too aggressive under bulk
load. Mitigated by remaining in-burst coalescing and author’s IRQ
measurements.
UNRESOLVED:
- Could not fetch lore.kernel.org (403) or spinics (500); used
openwall/patchew instead.
- Could not search stable@ archives (403).
- No follow-up fix SHA in this tree (expected; this commit is not here).
Step 9.2: APPLY THE STABLE RULES CHECKLIST
1. Obviously correct and tested? PASS — 4 lines, list review,
before/after iperf3, IRQ rate reported.
2. Fixes a real bug that affects users? PASS — paced TCP/TSQ stall with
measured multi-10x drop.
3. Important issue? PASS — HIGH functional failure of a standard
congestion controller on a common NIC; not a panic, but the device is
unusable for that workload at default settings.
4. Small and contained? PASS — +4 lines, one file, two functions.
5. No new features or APIs? PASS — uses existing `netdev_xmit_more()` /
IC bit.
6. Can apply to the local tree? PASS — buggy code and helper exist;
insertion context matches.
Step 9.3: CHECK FOR EXCEPTION CATEGORIES
Record: None of device-ID / quirk / DT / build / docs. Qualifies as a
normal small net driver correctness fix.
Step 9.4: MAKE YOUR DECISION
This is a small, tested fix for a long-standing TX completion stall that
makes paced TCP (notably BBR) collapse on stmmac at default coalesce
settings. The buggy path and the only dependency (`netdev_xmit_more`)
are in 6.12.y. Benefit to stable users is large; regression risk is low.
Verification:
- [Phase 1] Parsed subject/body/tags from the candidate message; Link
msgid used to find the original `[PATCH net]` / RESEND threads.
- [Phase 1] Original list post (openwall/patchew) still had Fixes:
da2024510031; committed copy in the pipeline does not.
- [Phase 2] Diff is +4 lines in `stmmac_tso_xmit` and `stmmac_xmit`
only; both insertions are `else if (!netdev_xmit_more()) set_ic =
true` after the `tx_coal_frames==0` check.
- [Phase 2] Read local `stmmac_main.c` 4380–4390 and 4645–4667: same IC
ladder, no xmit_more branch; timer still armed at 4461 and 4750.
- [Phase 2] `STMMAC_TX_FRAMES` 25 and `STMMAC_COAL_TX_TIMER` 5000
confirmed in `common.h`.
- [Phase 3] `git blame` on those lines: c2837423cb5455 (2019) and
db2f2842e6f560 (2021).
- [Phase 3] `git merge-base --is-ancestor da2024510031 HEAD` exit 0;
`git show da2024510031` changed TX_FRAMES 1→25.
- [Phase 3] `git merge-base --is-ancestor 039550960a223 HEAD` exit 0 (5
ms timer present).
- [Phase 3] `git log --grep=netdev_xmit_more --
drivers/net/ethernet/stmicro/stmmac/` empty.
- [Phase 3] `git describe HEAD` → p-6.12-2-gd38d58a4e0359; kernelversion
6.12.111.
- [Phase 4] b4 dig on msgid: no local matching commit (expected).
- [Phase 4] Openwall RESEND and patchew v1: Maciej Fijalkowski review,
Jakub apply-after-RESEND, no NAK; author kept frame coalescing for
ring-full IC bound.
- [Phase 4] lore.kernel.org and stable@ fetches: 403. UNVERIFIED on
stable@ specifically.
- [Phase 5] `stmmac_xmit` is `ndo_start_xmit`; `stmmac_tso_xmit` called
from it; `netdev_xmit_more()` defined in `include/linux/netdevice.h`
4989–5002.
- [Phase 5] Grep: many other NIC drivers already use
`netdev_xmit_more()`; stmmac does not.
- [Phase 6] Grep `netdev_xmit_more` under stmmac: no matches. Grep
YT6801/dwmac-motorcomm: no matches. Glob `dwmac-*.c`: 25 platform
files including dwmac-rk, dwmac-ipq806x, dwmac-intel, dwmac-stm32.
- [Phase 6] Insertion context in 6.12 matches the patch;
CIRC_CNT/get_tx_desc differences are mainline-only context, not
required.
- [Phase 7] MAINTAINERS: STMMAC ETHERNET DRIVER, Alexandre Torgue / Jose
Abreu.
- [Phase 8] `CONFIG_TCP_CONG_BBR` exists in `net/ipv4/Kconfig`.
- UNVERIFIED: whether a later mainline follow-up refined this patch (not
in this tree; lore search blocked).
The stall is in this 6.12 tree, the fix is four obviously correct lines,
and paced TCP is a real user-visible failure at default coalesce
settings.
**YES**
drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
index 63c045616c9b7..7dbb5e9bcb54b 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
@@ -4400,6 +4400,8 @@ static netdev_tx_t stmmac_tso_xmit(struct sk_buff *skb, struct net_device *dev)
set_ic = true;
else if (!priv->tx_coal_frames[queue])
set_ic = false;
+ else if (!netdev_xmit_more())
+ set_ic = true;
else if (tx_packets > priv->tx_coal_frames[queue])
set_ic = true;
else if ((tx_q->tx_count_frames %
@@ -4687,6 +4689,8 @@ static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev)
set_ic = true;
else if (!priv->tx_coal_frames[queue])
set_ic = false;
+ else if (!netdev_xmit_more())
+ set_ic = true;
else if (tx_packets > priv->tx_coal_frames[queue])
set_ic = true;
else if ((tx_q->tx_count_frames %
--
2.53.0
^ permalink raw reply related [flat|nested] 32+ messages in thread* [PATCH AUTOSEL 6.18-6.6] net: pcs: mtk-lynxi: check regmap reads in mtk_pcs_lynxi_get_state()
2026-10-19 13:44 [PATCH AUTOSEL 6.18-6.12] net: dsa: mt7530: check CORE_PLL_GROUP4 access in mt7531_setup() Sasha Levin
` (24 preceding siblings ...)
2026-10-19 13:51 ` [PATCH AUTOSEL 6.18-5.15] net: stmmac: raise TX completion interrupt at the end of an xmit burst Sasha Levin
@ 2026-10-19 13:51 ` Sasha Levin
2026-10-19 13:52 ` [PATCH AUTOSEL 6.18-5.10] net/rds: hold the socket while an rds_mr references it Sasha Levin
` (3 subsequent siblings)
29 siblings, 0 replies; 32+ messages in thread
From: Sasha Levin @ 2026-10-19 13:51 UTC (permalink / raw)
To: patches, stable
Cc: Daniel Golle, Andrew Lunn, Jakub Kicinski, Sasha Levin, lynxis,
hkallweit1, davem, edumazet, pabeni, matthias.bgg,
angelogioacchino.delregno, netdev, linux-kernel, linux-arm-kernel,
linux-mediatek
From: Daniel Golle <daniel@makrotopia.org>
[ Upstream commit 5cc65c01cdc577621d1320f4aa03a1382c5a4a45 ]
mtk_pcs_lynxi_get_state() ignores regmap_read()'s return value; a
failed read leaves bm and adv holding uninitialized stack values
which are then decoded into the reported link state. The regmaps
backing the MT7531 SGMII PCS instances sit on an MDIO bus where
reads can fail. Check both reads and report the link as down on
error; phylink presets state->link before the callback, so a bare
return would leave a failed read reported as link-up.
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
Link: https://patch.msgid.link/fce70657fc03bbaf60a04c0fbf2f418531135c4f.1785811140.git.daniel@makrotopia.org
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
LLM Generated explanations, may be completely bogus:
This is a real uninitialized-data bug in MediaTek LynxI PCS link
polling, and the buggy path exists in this 6.12 tree. It should be
backported.
## Phase 1: Commit message forensics
**Step 1.1:** Record: `[net: pcs: mtk-lynxi:]` `[check]` `[validate
regmap_read() in mtk_pcs_lynxi_get_state() so failed MDIO reads are not
decoded as link state]`
**Step 1.2:** Record: tags in the candidate message:
- `Signed-off-by: Daniel Golle <daniel@makrotopia.org>` (author; driver
co-maintainer)
- `Reviewed-by: Andrew Lunn <andrew@lunn.ch>` (netdev/DSA reviewer)
- `Link: https://patch.msgid.link/fce70657fc03bbaf60a04c0fbf2f418531135c
4f.1785811140.git.daniel@makrotopia.org`
- `Signed-off-by: Jakub Kicinski <kuba@kernel.org>` (netdev maintainer)
- No `Reported-by`, `Tested-by`, `Cc: stable` in the candidate text
(absence is expected)
- Original mailing-list submission **did** include `Fixes: 4765a9722e09
("net: pcs: add driver for MediaTek SGMII PCS")`; that tag is not in
the candidate body
**Step 1.3:** Record: `[Bug: ignored regmap_read() errors leave
`bm`/`adv` uninitialized and phylink decodes them as BMSR/LPA]`
`[Symptom: false or garbage link/speed/duplex; failed read can be
reported as link-up because phylink presets `state->link`]` `[Version:
bug dates to driver introduction]` `[Root cause: MDIO-backed MT7531
SGMII PCS regmaps can fail; output is not written on error]`
**Step 1.4:** Record: `[Not a disguised cleanup. This is an explicit
missing-check / uninitialized-use fix. v2 of the patch added
`state->link = false` after review, because a bare return left phylink's
preset link-up.]`
## Phase 2: Diff analysis
**Step 2.1:** Record: `[drivers/net/pcs/pcs-mtk-lynxi.c: +5 / -2 in
`mtk_pcs_lynxi_get_state()`]` `[single-file surgical fix]`
**Step 2.2:** Record: `[Before: two unchecked `regmap_read()`s, then
`phylink_mii_c22_pcs_decode_state()` on `bm`/`adv`. After: if either
read fails, set `state->link = false` and return; otherwise decode as
before. Error path only.]`
**Step 2.3:** Record: `[Category: initialization / missing return-value
check]` `[Mechanism: failed `regmap_read()` does not write `*val`;
`unsigned int bm, adv` stay uninitialized;
`phylink_mii_c22_pcs_decode_state()` does `state->link = !!(bmsr &
BMSR_LSTATUS)` on that garbage. Fix matches
`phylink_mii_c22_pcs_get_state()`, which already treats read errors as
link-down.]`
**Step 2.4:** Record: `[Obviously correct, 5-line change, no
API/locking/refcount changes.]` `[Regression risk: very low. Worst case
is reporting link-down on a failed register read, which is the intended
fail-safe.]`
## Phase 3: Git history
**Step 3.1:** Record: `[Buggy reads introduced in 4765a9722e0976 ("net:
pcs: add driver for MediaTek SGMII PCS", Daniel Golle, 2023-03-19)]`
`[First appeared in v6.4-rc1; present for the life of this driver]`
**Step 3.2:** Record: `[Fixes target 4765a9722e0976 is an ancestor of
HEAD. No later lynxi get_state error-handling in this tree.]`
**Step 3.3:** Record: `[File history on this tree: 4765a9722e097 (add
driver), 3b2de56a146f3 (neg_mode), 90308679c297f (pcs_disable),
be884c15de370 (MODULE_DESCRIPTION).]` `[Standalone functionally; series
patch 1/6, other patches touch `mt7530.c` only.]` `[Prerequisite already
here: 93d46870c5440 ("net: dsa: mt7530: check bus->read() errors in the
MDIO regmap backend") — after that, a failed MDIO read returns an error
and leaves the output unwritten instead of truncated garbage.]`
**Step 3.4:** Record: `[Daniel Golle is listed as maintainer of MEDIATEK
ETHERNET PCS DRIVER. Related MDIO error-propagation work on mt7530 is by
the same author.]`
**Step 3.5:** Record: `[No missing functions. This tree's
`mtk_pcs_lynxi_get_state()` still has the 6.12 signature (no
`neg_mode`); the check itself is independent. Can apply standalone with
a small context adaptation.]`
## Phase 4: Mailing list
**Step 4.1:** Record: `[Lore thread retrieved via b4 mbox: msgid fce7065
7fc03bbaf60a04c0fbf2f418531135c4f.1785811140.git.daniel@makrotopia.org]`
`[v1 of this remaining-errors series was `[PATCH net 1/4]`; v2 is
`[PATCH net v2 1/6]`. Applied version is v2.]` `[v2 change: set
`state->link = false` because phylink presets link-up (Sashiko
review).]` `[Andrew Lunn Reviewed-by on v1; author kept it on v2 for the
small in-spirit change. No NAKs.]` `[Patchwork bot: applied by Jakub
Kicinski as netdev commit 5cc65c01cdc5.]`
**Step 4.2:** Record: `[To/Cc included Andrew Lunn, Vladimir Oltean,
Russell King, netdev, linux-mediatek, DSA and MediaTek maintainers.
Right audience.]`
**Step 4.3:** Record: `[No syzbot/bugzilla user report. Found during
mt7530 MDIO error-handling review. Still a concrete, reachable bug:
`pcs.poll = true` so this runs from phylink's 1 Hz poll.]`
**Step 4.4:** Record: `[Series: "fix remaining swallowed MDIO access
errors". Patch 1 is the only pcs-mtk-lynxi change and does not depend on
patches 2–6.]` `[Earlier companion 93d46870c5440 / 96e0f5184af69 are
already in this tree.]`
**Step 4.5:** Record: `[UNVERIFIED: lore.kernel.org/stable search
blocked by Anubis. No stable-list discussion found via b4.]`
## Phase 5: Semantic analysis
**Step 5.1:** Record: `[Modified: `mtk_pcs_lynxi_get_state`. Wired as
`.pcs_get_state`.]`
**Step 5.2:** Record: `[Callers: `phylink_mac_pcs_get_state()` → in-band
resolve (`MLO_AN_INBAND`), ethtool `phylink_ethtool_ksettings_get()`,
and MII emulation. `mpcs->pcs.poll = true` so `phylink_fixed_poll` runs
`phylink_run_resolve()` every HZ.]`
**Step 5.3:** Record: `[Callees: `regmap_read`,
`phylink_mii_c22_pcs_decode_state`.]`
**Step 5.4:** Record: `[Reachable from userspace: ethtool link settings,
and continuously while the netdev is up in in-band
SGMII/1000Base-X/2500Base-X.]` `[Hardware: MT7531 DSA ports 5/6
(`mt7531_create_sgmii()` uses an MDIO-backed regmap) and MediaTek SoC
SGMII (`mtk_eth_soc` syscon/MMIO, rarely fails). The MDIO path is the
one that matters.]`
**Step 5.5:** Record: `[Same pattern already handled in
`phylink_mii_c22_pcs_get_state()` and `phylink_mii_c45_pcs_get_state()`
in this tree. Lynx PCS uses those helpers; lynxi did not.]`
## Phase 6: Local tree (6.12.111, `pending-6.12`)
**Step 6.1:** Record: `[Yes. `drivers/net/pcs/pcs-mtk-lynxi.c:98-99`
still has unchecked `regmap_read()` into uninitialized `bm`/`adv`.
Introduced v6.4, present here.]` `[Verified: `git describe HEAD` →
p-6.12-4-gbf6fcdf761abd; `make kernelversion` → 6.12.111.]`
**Step 6.2:** Record: `[Minor context rework, not a semantic fork. This
tree has no `mtk_pcs_lynxi_inband_caps`, no `neg_mode` argument, and
`phylink_mii_c22_pcs_decode_state(state, bmsr, lpa)` not the later 4-arg
form. The 5-line check ports 1:1 into the existing function body.]`
**Step 6.3:** Record: `[No lynxi get_state error-check already present.
Prerequisite MDIO backend error propagation is already here, which is
why this follow-up is needed: failed reads no longer write `*val`.]`
## Phase 7: Subsystem context
**Step 7.1:** Record: `[drivers/net/pcs + consumers in net/dsa/mt7530
and net/ethernet/mediatek]` `[IMPORTANT: common OpenWrt/router SGMII CPU
ports, not core mm/VFS, but production networking]`
**Step 7.2:** Record: `[Driver is mature since v6.4; this file has only
a handful of commits on this tree. Bug is long-standing, newly exposed
as true uninitialized use after the MDIO backend fix.]`
## Phase 8: Impact and risk
**Step 8.1:** Record: `[Driver-specific: MT7531 SGMII (MDIO) and
MediaTek SoC SGMII (MMIO).]`
**Step 8.2:** Record: `[Trigger: failed `regmap_read` on the PCS map.
Common on flaky/timeout MDIO; not every boot. Unprivileged userspace can
observe the result via ethtool/carrier; the poll path runs while the
interface is up.]`
**Step 8.3:** Record: `[Failure mode: uninitialized stack decoded as
link/speed/duplex; false carrier possible because phylink presets
`state->link = 1`.]` `[Severity: HIGH for wrong datapath/carrier on MDIO
error; not a panic. Uninitialized use is the same class as KMSAN-class
bugs.]`
**Step 8.4:** Record: `[Benefit: high for MT7531 in-band SGMII — stops
garbage link state after a bus error, especially now that the backend
returns real errors.]` `[Risk: very low — 5 lines, fail-closed to link-
down.]` `[Ratio: strongly favors backport.]`
## Phase 9: Synthesis
**For backporting:**
- Real bug: uninitialized `bm`/`adv` decoded into phylink link state
- User-visible: false/garbage carrier, speed, duplex; ethtool reports it
- Tiny, obviously correct, matches phylink's own C22 helper
- Reviewed by Andrew Lunn, applied by Jakub Kicinski, written by the PCS
maintainer
- Buggy code has been in this driver since v6.4 and is in 6.12.111
- Companion to MDIO error-propagation already in this tree
(93d46870c5440); without this, a failed MT7531 PCS read now leaves
stack garbage instead of even truncated-but-defined data
- No new API/feature
**Against:**
- Not a crash/UAF/deadlock
- Raw upstream hunk will not apply cleanly (later `neg_mode` /
`inband_caps` context)
- No external user/syzbot report
- Series 1/6, but this patch does not need 2–6
**Unresolved:** stable-list discussion (lore/stable fetch blocked)
**Stable rules:**
1. Obviously correct and tested? **PASS** — same pattern as
`phylink_mii_c22_pcs_get_state()`; Reviewed-by Andrew Lunn; merged
2. Fixes a real bug that affects users? **PASS** — wrong link state /
uninitialized data on MDIO failure for MT7531 SGMII
3. Important issue? **PASS** — uninitialized memory used as live link
state on a 1 Hz poll; can keep carrier up after a failed read
4. Small and contained? **PASS** — 5 lines, one function
5. No new features or APIs? **PASS**
6. Can apply to this tree? **PASS** — needs a trivial context backport,
not missing code
**Exception category:** none (normal small correctness fix)
The problem for 6.12 users is that `mtk_pcs_lynxi_get_state()` still
consumes uninitialized stack as BMSR/LPA. Phylink presets `state->link =
1` (`phylink.c:1328`) and this PCS polls every second. After the
already-landed MT7530 MDIO backend fix, a failed read returns an error
and does not fill `*val`, so this is the necessary follow-up. Benefit is
preventing false/garbage link state; risk is a fail-closed link-down on
I/O error.
Verification:
- [Phase 1] Parsed candidate tags; original lore patch had `Fixes:
4765a9722e09`
- [Phase 2] Diff is 5 lines in `mtk_pcs_lynxi_get_state()` wrapping two
`regmap_read()`s
- [Phase 2] Confirmed `phylink_mac_pcs_get_state()` sets `state->link =
1` before the callback
- [Phase 2] Confirmed `phylink_mii_c22_pcs_decode_state()` uses `bmsr`
for `state->link` and that `phylink_mii_c22_pcs_get_state()` already
sets `state->link = false` on read failure
- [Phase 3] `git blame` on lines 98–99: 4765a9722e0976; `git describe
--contains` → v6.4-rc1
- [Phase 3] `git merge-base --is-ancestor 4765a9722e0976 HEAD` succeeded
- [Phase 3] `git merge-base --is-ancestor 93d46870c5440 HEAD` succeeded
(MDIO backend now returns errors without writing `*val`)
- [Phase 4] `b4 mbox` on the msgid: 8-message thread; series v2 1/6;
Andrew Lunn Reviewed-by; no NAK
- [Phase 4] Cover letter: this check was added because a failed read can
now leave output unwritten
- [Phase 4] UNVERIFIED: lore.kernel.org/stable search (Anubis 403)
- [Phase 5] Callers: phylink in-band resolve, ethtool get, 1 Hz
`link_poll`; consumers `mt7530-mdio.c` and `mtk_eth_soc.c`
- [Phase 6] `git describe HEAD` / `make kernelversion`: 6.12.111 on
`pending-6.12`; unchecked reads still at `pcs-mtk-lynxi.c:98-99`
- [Phase 6] Function signature here lacks `neg_mode`; backport is a
local 5-line edit
- [Phase 8] Failure mode: uninitialized link state / possible false
carrier, severity HIGH, not a panic
- UNVERIFIED: whether this SHA is already in some other stable series
(decision is for this tree only)
**YES**
drivers/net/pcs/pcs-mtk-lynxi.c | 7 +++++--
1 file changed, 5 insertions(+), 2 deletions(-)
diff --git a/drivers/net/pcs/pcs-mtk-lynxi.c b/drivers/net/pcs/pcs-mtk-lynxi.c
index 87df3a9dfc9bc..64fb17c189afc 100644
--- a/drivers/net/pcs/pcs-mtk-lynxi.c
+++ b/drivers/net/pcs/pcs-mtk-lynxi.c
@@ -110,8 +110,11 @@ static void mtk_pcs_lynxi_get_state(struct phylink_pcs *pcs,
unsigned int bm, adv;
/* Read the BMSR and LPA */
- regmap_read(mpcs->regmap, SGMSYS_PCS_CONTROL_1, &bm);
- regmap_read(mpcs->regmap, SGMSYS_PCS_ADVERTISE, &adv);
+ if (regmap_read(mpcs->regmap, SGMSYS_PCS_CONTROL_1, &bm) ||
+ regmap_read(mpcs->regmap, SGMSYS_PCS_ADVERTISE, &adv)) {
+ state->link = false;
+ return;
+ }
phylink_mii_c22_pcs_decode_state(state, neg_mode,
FIELD_GET(SGMII_BMSR, bm),
--
2.53.0
^ permalink raw reply related [flat|nested] 32+ messages in thread* [PATCH AUTOSEL 6.18-5.10] net/rds: hold the socket while an rds_mr references it
2026-10-19 13:44 [PATCH AUTOSEL 6.18-6.12] net: dsa: mt7530: check CORE_PLL_GROUP4 access in mt7531_setup() Sasha Levin
` (25 preceding siblings ...)
2026-10-19 13:51 ` [PATCH AUTOSEL 6.18-6.6] net: pcs: mtk-lynxi: check regmap reads in mtk_pcs_lynxi_get_state() Sasha Levin
@ 2026-10-19 13:52 ` Sasha Levin
2026-10-19 13:52 ` [PATCH AUTOSEL 6.18-6.12] sfc: add X4D PF support Sasha Levin
` (2 subsequent siblings)
29 siblings, 0 replies; 32+ messages in thread
From: Sasha Levin @ 2026-10-19 13:52 UTC (permalink / raw)
To: patches, stable
Cc: Håkon Bugge, Allison Henderson, Jakub Kicinski, Sasha Levin,
davem, edumazet, pabeni, netdev, linux-rdma, rds-devel,
linux-kernel
From: Håkon Bugge <haakon.bugge@oracle.com>
[ Upstream commit 9079adef042c1e7a6141dcdfd3041acd30607297 ]
Each rds_mr stores a bare back pointer to the socket that created it
(mr->r_sock) but takes no reference on it. When the mr is destroyed it
references the rs. Hence, provisions must be made to avoid the rs
being destroyed before all mrs referencing it have been destroyed.
The MR itself is refcounted, and in-flight messages legitimately hold
MR krefs that can outlive the socket: rds_release() drops the rb-tree
references via rds_rdma_drop_keys(), but a send completion arriving
afterwards drops the final message reference from the CQ handler and
ends up in
rds_message_purge()
__rds_put_mr_final()
rds_destroy_mr() -> takes rs->rs_rdma_lock
dereferencing a socket that may already have been freed.
Oracle UEK fixed the same use-after-free ("rds: Add proper refcnt when
an RDS MR references an RDS Socket") after seeing crashes of the form:
PF: supervisor write access in kernel mode
_raw_spin_lock_irqsave+0x4a/0x6a
__rds_put_mr_final+0x2c/0xe0 [rds]
rds_message_purge+0x13c/0x150 [rds]
rds_message_put+0x39/0x54 [rds]
rds_ib_send_cqe_handler+0x147/0x3dd [rds_rdma]
To fix this, take a socket reference when an MR is created and drop it
when the final MR kref goes away. The reference cycle is broken by
rds_release(), which always runs rds_rdma_drop_keys() on close. So the
socket reference held by an MR never prevents release, it only delays
sk_free() until the last MR user is done.
The hold sits next to kref_init() at both allocation sites -
__rds_rdma_map() and the on-demand-paging path in
rds_cmsg_rdma_args() - so every MR owns exactly one socket reference
from the moment it becomes kref-managed. For that to work on the ODP
path, its get_mr() error handling is converted from a bare kfree() to
kref_put(..., __rds_put_mr_final), with r_trans_private cleared first
since it holds an ERR_PTR there; both sites then tear down through
the same path and a future error-path change cannot silently leak or
double-drop the reference.
Signed-off-by: Håkon Bugge <haakon.bugge@oracle.com>
[achender: port to net-next (sock_hold/sock_put in place of the UEK
rds_sock_addref/rds_sock_put helpers); also balance the reference on
the rds_cmsg_rdma_args() ODP path and unify its error path with
__rds_put_mr_final(); update commit message]
Signed-off-by: Allison Henderson <achender@kernel.org>
Link: https://patch.msgid.link/20260730041629.3512480-3-achender@kernel.org
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
LLM Generated explanations, may be completely bogus:
This is a use-after-free in RDS MR teardown. The buggy path is in this
5.10 tree, the fix is small, and it should be backported.
===============================================================
PHASE 1: COMMIT MESSAGE FORENSICS
===============================================================
Step 1.1: PARSE THE SUBJECT LINE
Record: **[net/rds] [hold] hold the socket while an rds_mr references
it**
- Subsystem: `net/rds`
- Action verb: “hold” (keep a missing reference so the socket outlives
the MR)
- Claimed intent: an `rds_mr` keeps a counted socket reference for its
lifetime
Step 1.2: PARSE ALL COMMIT MESSAGE TAGS
Record (from the candidate / merged `9079adef042c`):
- **Fixes:** absent in the git commit. The v4 mailing-list post had
`Fixes: eff5f53bef75 ("RDS: RDMA support")`. Absence in git is
expected here and is not a negative.
- **Reported-by:** none. The body cites an Oracle UEK production crash.
- **Tested-by:** none
- **Reviewed-by / Acked-by:** none on the commit
- **Link:** `https://patch.msgid.link/20260730041629.3512480-3-
achender@kernel.org`
- **Cc: stable:** absent (expected)
- **Signed-off-by:** Håkon Bugge (Oracle, original); Allison Henderson
(RDS, port); Jakub Kicinski (net maintainer, merge)
- Notable: Allison’s trailer says this is a UEK port
(`sock_hold`/`sock_put` instead of UEK
`rds_sock_addref`/`rds_sock_put`). This tree already has those helpers
as thin wrappers around `sock_hold`/`sock_put`.
Step 1.3: ANALYZE THE COMMIT BODY TEXT
Record:
- **Bug:** `mr->r_sock` is a bare back-pointer. `rds_destroy_mr()` takes
`rs->rs_rdma_lock` after the socket may already be freed.
- **Symptom:** kernel oops: `PF: supervisor write access` in
`_raw_spin_lock_irqsave` from `__rds_put_mr_final` ←
`rds_message_purge` ← `rds_message_put` ← `rds_ib_send_cqe_handler`.
- **Root cause:** `rds_release()` drops rb-tree MR refs via
`rds_rdma_drop_keys()`, but in-flight send completions still hold MR
krefs. The CQ/tasklet path then destroys the MR against a freed
socket.
- **Version info:** none beyond “Oracle UEK saw this.” The mechanism is
as old as RDS RDMA (2009).
- **Fix:** `sock_hold()` at both MR alloc sites next to `kref_init()`;
`sock_put()` in `__rds_put_mr_final()`. Cycle is broken because
`rds_release()` always runs `rds_rdma_drop_keys()`, so the MR ref only
delays `sk_free()`, it does not block `release`.
Step 1.4: DETECT HIDDEN BUG FIXES
Record: Not disguised. Explicit UAF fix with a crash stack. The ODP
`kfree` → `kref_put` change is part of making the hold/put pairing
correct on the error path.
===============================================================
PHASE 2: DIFF ANALYSIS - LINE BY LINE
===============================================================
Step 2.1: INVENTORY THE CHANGES
Record:
- `net/rds/rdma.c`: +13 / −1 (functional)
- `net/rds/rds.h`: +4 / −1 (comment on `r_sock`)
- **Functions:** `__rds_put_mr_final()`, `__rds_rdma_map()`,
`rds_cmsg_rdma_args()`
- **Scope:** single-subsystem, surgical (~17 lines)
Step 2.2: UNDERSTAND THE CODE FLOW CHANGE
Record:
- **`__rds_put_mr_final`:** after `rds_destroy_mr(mr)`, add
`sock_put(rds_rs_to_sk(mr->r_sock))` before `kfree(mr)`.
- **`__rds_rdma_map`:** after `mr->r_sock = rs`, add `sock_hold()`. This
tree already `kref_put`s `mr` on every `out:` path (success and
error), so the hold is paired.
- **ODP path in `rds_cmsg_rdma_args`:** `sock_hold()` next to
`kref_init()`. On `get_mr()` failure, clear `r_trans_private` (it is
an `ERR_PTR`) and `kref_put(..., __rds_put_mr_final)` instead of a
bare `kfree`, so `free_mr()` is not called with an error pointer and
the socket ref is dropped.
Step 2.3: IDENTIFY THE BUG MECHANISM
Record:
- **Category:** use-after-free / missing reference count
- **Mechanism:** MR outlives socket; destructor uses `rs->rs_rdma_lock`.
Fix is one `sock_hold` per kref-managed MR, one `sock_put` on final
kref.
Step 2.4: ASSESS THE FIX QUALITY
Record:
- Obviously correct: classic missing get/put; both alloc sites covered
(only two `kref_init` of `rds_mr` in `net/rds/`).
- `__rds_rdma_map` already uses `kref_put` on `out:`; ODP error path is
converted to the same destructor.
- Regression risk is low. `sock_put` from the send tasklet is already
done in `rds_message_purge()` for `rm->m_rs`. `rds_sock_destruct()` is
only a `WARN_ON` on an empty list.
- No API or data-structure layout change beyond a comment.
===============================================================
PHASE 3: GIT HISTORY INVESTIGATION
===============================================================
Step 3.1: BLAME THE CHANGED LINES
Record:
- `mr->r_sock = rs` and `rds_destroy_mr()` using that pointer:
`eff5f53bef75` (“RDS: RDMA support”), 2009-02-26. Present in 5.10.
- `__rds_put_mr_final` / `kref_init`: `e228a5d05e9ee` (2020-04-09), in
this tree.
- ODP alloc in `rds_cmsg_rdma_args`: `2eafa1746f178` (2020-01-18), in
this tree.
Step 3.2: FOLLOW THE FIXES: TAG
Record: No `Fixes:` on the git commit. Mailing-list tag `eff5f53bef75`
**is** an ancestor of HEAD (`git merge-base --is-ancestor` exit 0).
Merged mainline SHA `9079adef042c` is **not** an ancestor of this tree
(exit 1). No later sock-hold-on-MR commit exists in this tree.
Step 3.3: CHECK FILE HISTORY FOR RELATED CHANGES
Record:
- `91a44b406bc1f` (“net: rds: fix MR cleanup on copy error”) is already
here; it only stops double-free of pages/sg on `put_user` failure. It
does not hold the socket.
- `2fabef4f65b46` (“Fix MR reference counting problem”) is about
`rds_free_mr()` bypassing kref, not socket lifetime.
- This patch is standalone vs those.
Step 3.4: CHECK THE AUTHOR'S OTHER COMMITS
Record: Håkon Bugge has multiple RDS commits in this history. Allison
Henderson is the RDS maintainer and ported the UEK fix. The original
crash was seen in Oracle production.
Step 3.5: CHECK FOR DEPENDENT/PREREQUISITE COMMITS
Record: Submitted as **v4 2/4**. Siblings:
1. `d100966325f7` — don’t `unpin_user_pages_dirty_lock()` from atomic
context
3. message leak in `rds_send_xmit()` drop path
4. unpin MR pages with `unpin_user_pages_dirty_lock()`
Those are separate bugs. This diff only needs `sock_hold`/`sock_put`,
`kref_put`, and `__rds_put_mr_final`, all present here. **Can apply
standalone.**
===============================================================
PHASE 4: MAILING LIST AND EXTERNAL RESEARCH
===============================================================
Step 4.1: FIND THE ORIGINAL PATCH DISCUSSION
Record:
- `b4 dig -c 9079adef042c`: v4 2/4,
https://patch.msgid.link/20260730041629.3512480-3-achender@kernel.org
- Revisions: v1 (2026-07-10), v2, v3 (ODP hold after successful
`get_mr`), v4 (hold next to `kref_init`, error path via
`__rds_put_mr_final`) — applied version is v4.
- Cover letter: explicit UAF of the RDS socket through long-lived MR
refs; UEK port https://github.com/oracle/linux-uek/commit/94549e4732d8
- No NAK found on this patch. Jakub applied the series to net-next on
2026-08-04 (`9079adef042c`).
- v4 changelog: ODP ref moved next to `kref_init` so both alloc sites
share one ownership rule.
Step 4.2: CHECK WHO REVIEWED THE PATCH
Record (`b4 dig -w`): netdev, linux-rdma, Paolo Abeni, Eric Dumazet,
Jakub Kicinski, Simon Horman, John Hubbard, Leon Romanovsky. Applied by
the net maintainer. No NAK located.
Step 4.3: SEARCH FOR THE BUG REPORT
Record: No syzbot/bugzilla link. Evidence is the UEK oops in the commit
message, matching this tree’s call chain (`rds_ib_tasklet_fn_send` →
`rds_ib_send_cqe_handler` → `rds_message_put` → `rds_message_purge` →
`__rds_put_mr_final`).
Step 4.4: CHECK FOR RELATED PATCHES AND SERIES
Record: 4 independent RDS bug-fix ports. This one does not need 1/3/4.
Step 4.5: CHECK STABLE MAILING LIST HISTORY
Record: **UNVERIFIED** — `lore.kernel.org/stable` fetch returned 403. No
local evidence this was rejected for 5.10.
===============================================================
PHASE 5: CODE SEMANTIC ANALYSIS
===============================================================
Step 5.1: IDENTIFY KEY FUNCTIONS IN THE DIFF
Record: `__rds_put_mr_final`, `__rds_rdma_map`, `rds_cmsg_rdma_args`
(plus `rds_destroy_mr` as callee).
Step 5.2: TRACE CALLERS
Record:
- `__rds_put_mr_final`: `kref_put` from `rds_rdma_drop_keys`,
`rds_free_mr`, `rds_rdma_unuse`, `rds_rdma_free_op` (ODP),
`rds_message_purge` (`op_rdma_mr` / atomic MR), and `__rds_rdma_map`
`out:`.
- `__rds_rdma_map`: `rds_get_mr`, `rds_get_mr_for_dest`,
`rds_cmsg_rdma_map` (sendmsg cmsg). Userspace RDMA registration.
- `rds_cmsg_rdma_args`: `rds_sendmsg` path for `RDS_CMSG_RDMA_ARGS`.
Step 5.3: TRACE CALLEES
Record: `rds_destroy_mr` → `spin_lock_irqsave(&rs->rs_rdma_lock)` (the
UAF); `free_mr()`; `sock_hold`/`sock_put`.
Step 5.4: FOLLOW THE CALL CHAIN
Record: Userspace maps an MR or sends RDMA, then closes the socket while
a send is in flight. Completions run in `rds_ib_tasklet_fn_send`
(softirq). Reachable from AF_RDS sockets with `CONFIG_RDS` +
`CONFIG_RDS_RDMA` (InfiniBand). Typical Oracle RAC / IB RDS workload.
Step 5.5: SEARCH FOR SIMILAR PATTERNS
Record: Only two `kref_init` sites for `struct rds_mr`. Both patched.
`rds_cmsg_rdma_dest` only `kref_get`s an existing tree MR.
===============================================================
PHASE 6: CROSS-REFERENCING AGAINST THE LOCAL TREE
===============================================================
Step 6.1: DOES THE BUGGY CODE EXIST IN THIS TREE?
Record: **Yes.**
- Tree: `pending-5.10`, `5.10.270` (`p-5.10-1-gce56b7a73b64b`)
- `net/rds/rdma.c`: `mr->r_sock = rs` with no `sock_hold` (lines 243–246
and 735–738)
- `__rds_put_mr_final` has no `sock_put` (115–121)
- `rds_release()` still does `rds_rdma_drop_keys()` then `sock_put(sk)`
(`af_rds.c`)
- Introduced 2009; ODP path 2020. Both in 5.10.
Step 6.2: CHECK FOR BACKPORT COMPLICATIONS
Record: **Minor context only.** Mainline uses `kzalloc_obj` /
`kzalloc_objs`; this tree uses `kzalloc` / `kcalloc`. The hold/put lines
sit on identical `kref_init` / `mr->r_sock` / ODP `get_mr` error code.
Local ODP error uses `ret = IS_ERR(...)` then overwrites with
`-EOPNOTSUPP`; the patch uses `PTR_ERR` then the same overwrite.
Semantic apply is straightforward.
Step 6.3: CHECK IF RELATED FIXES ARE ALREADY HERE
Record: No socket-hold-on-MR fix. `91a44b406bc1f` is a different MR
cleanup bug.
===============================================================
PHASE 7: SUBSYSTEM AND MAINTAINER CONTEXT
===============================================================
Step 7.1: IDENTIFY THE SUBSYSTEM AND ITS CRITICALITY
Record: **net/rds** — IMPORTANT, not core. RDS over IB is production for
Oracle RAC and similar clusters. UAF is still a kernel oops for those
users.
Step 7.2: ASSESS SUBSYSTEM ACTIVITY
Record: Mature (2009). Occasional stable fixes in this tree
(`91a44b406bc1f` in 2026). Not a new subsystem.
===============================================================
PHASE 8: IMPACT AND RISK ASSESSMENT
===============================================================
Step 8.1: DETERMINE WHO IS AFFECTED
Record: **config/driver-specific:** `CONFIG_RDS` + RDMA
(`CONFIG_RDS_RDMA`). Not all 5.10 boxes, but all RDS/IB RDMA users who
close a socket with in-flight MRs.
Step 8.2: DETERMINE THE TRIGGER CONDITIONS
Record: Map MR or send RDMA, close socket before CQ completion. Timing-
dependent but realistic (UEK crash). Needs an RDS socket; typical RDS
users are cluster services.
Step 8.3: DETERMINE THE FAILURE MODE SEVERITY
Record: **Use-after-free → oops on spinlock of freed `rds_sock`.**
Severity **CRITICAL/HIGH**. Same class as KASAN UAF; crash stack is
real.
Step 8.4: CALCULATE RISK-BENEFIT RATIO
Record:
- **Benefit:** high for RDS/IB — prevents a known UAF oops.
- **Risk:** low — ~17 lines, standard `sock_hold`/`sock_put`, both alloc
sites, error path unified.
- **Ratio:** strongly favors backport.
===============================================================
PHASE 9: FINAL SYNTHESIS
===============================================================
Step 9.1: COMPILE THE EVIDENCE
**FOR:**
- Real UAF with production oops (Oracle UEK).
- Same call path exists in this 5.10 tree.
- Bug dates to 2009 RDMA support; ODP path also here.
- Small, obviously correct refcount fix; v4 review then net-maintainer
merge.
- Standalone vs the rest of the series.
- `__rds_rdma_map` already drops the MR via `kref_put` on all exits, so
the new hold is balanced.
**AGAINST:**
- RDS/IB is not universal.
- Patch was 2/4 of a series (siblings are independent).
- Minor `kzalloc_obj` vs `kzalloc` context when applying.
**UNRESOLVED:**
- Stable-list discussion (lore 403).
- No public syzbot repro (crash is still documented).
Step 9.2: APPLY THE STABLE RULES CHECKLIST
1. Obviously correct and tested? **PASS** — hold at `kref_init`, put in
finalizer; merged to net-next; UEK precedent.
2. Fixes a real bug that affects users? **PASS** — production oops.
3. Important issue? **PASS** — UAF / kernel oops, HIGH/CRITICAL.
4. Small and contained? **PASS** — 2 files, ~17 lines.
5. No new features or APIs? **PASS**.
6. Can apply to the local tree? **PASS** — buggy code present; minor
context only.
Step 9.3: CHECK FOR EXCEPTION CATEGORIES
Record: Not a device-ID/quirk/DT/build/docs exception. Ordinary memory-
safety fix.
Step 9.4: MAKE YOUR DECISION
This 5.10 tree has the bare `r_sock` pointer, the CQ/tasklet free path,
and no socket hold on MRs. The fix is the missing refcount. It should be
backported.
===============================================================
What this solves for stable users
===============================================================
Closing an RDS socket while an RDMA MR is still referenced by an in-
flight send can free `rds_sock` before `__rds_put_mr_final` →
`rds_destroy_mr` takes `rs->rs_rdma_lock`. That is an oops. Holding the
socket until the last MR kref drops keeps the lock valid;
`rds_rdma_drop_keys()` in `rds_release()` still breaks the tree↔socket
cycle.
Risk vs benefit: high benefit for RDS/IB, low regression risk.
`sock_put` from this tasklet is already used for `rm->m_rs`. Backport
may need trivial context adjustment (`kzalloc` vs `kzalloc_obj`); no
missing helpers or types.
Verification:
- [Phase 1] Tags: Link msgid `20260730041629.3512480-3`; SOB Bugge /
Henderson / Kicinski; no Fixes in git; ML had `Fixes: eff5f53bef75`
- [Phase 2] Diff: `sock_put` in `__rds_put_mr_final`; `sock_hold` in
`__rds_rdma_map` and ODP alloc; ODP error `kfree` → `kref_put` after
NULL-out of `ERR_PTR`
- [Phase 2] Local `__rds_rdma_map` `out:` already `kref_put`s if `mr`
non-NULL (`rdma.c` 348–351)
- [Phase 3] `git blame`: `r_sock` / `rds_destroy_mr` from `eff5f53bef75`
(2009); kref from `e228a5d05e9ee` (2020)
- [Phase 3] `git merge-base --is-ancestor eff5f53bef75c0 HEAD` → 0;
`2eafa1746f178` → 0; `9079adef042c` → 1 (not in this tree)
- [Phase 3] `git describe HEAD` / `make kernelversion`: pending-5.10,
5.10.270
- [Phase 3] Only two `kref_init(&...r_kref)` sites in `net/rds/`
- [Phase 4] `b4 dig -c 9079adef042c`: v4 2/4; recipients
netdev/rdma/Kicinski/Dumazet/Abeni/Horman
- [Phase 4] `b4 dig -a`: v1→v4; v4 unifies ODP error path
- [Phase 4] Cover letter: UEK UAF port; applied as `9079adef042c`
- [Phase 4] UNVERIFIED: stable@ thread (403)
- [Phase 5] Call chain confirmed: `rds_ib_tasklet_fn_send` →
`rds_ib_send_cqe_handler` → `rds_message_put` → `rds_message_purge` →
`__rds_put_mr_final`
- [Phase 5] `rds_release` calls `rds_rdma_drop_keys` then `sock_put`
(`af_rds.c` 78, 89)
- [Phase 6] Local `rdma.c` has no `sock_hold`/`sock_put`; comment still
says “back pointer”
- [Phase 8] Failure mode: UAF oops on `rs_rdma_lock`, HIGH/CRITICAL
- UNVERIFIED: whether an unprivileged process can open AF_RDS on a given
distro; crash still confirmed for RDS/IB users
**YES**
net/rds/rdma.c | 14 +++++++++++++-
net/rds/rds.h | 5 ++++-
2 files changed, 17 insertions(+), 2 deletions(-)
diff --git a/net/rds/rdma.c b/net/rds/rdma.c
index 37695056756be..062c12d9318e9 100644
--- a/net/rds/rdma.c
+++ b/net/rds/rdma.c
@@ -117,6 +117,7 @@ void __rds_put_mr_final(struct kref *kref)
struct rds_mr *mr = container_of(kref, struct rds_mr, r_kref);
rds_destroy_mr(mr);
+ sock_put(rds_rs_to_sk(mr->r_sock));
kfree(mr);
}
@@ -243,7 +244,11 @@ static int __rds_rdma_map(struct rds_sock *rs, struct rds_get_mr_args *args,
kref_init(&mr->r_kref);
RB_CLEAR_NODE(&mr->r_rb_node);
mr->r_trans = rs->rs_transport;
+ /* The MR can outlive its socket: a socket reference is held
+ * until the final kref is dropped in __rds_put_mr_final().
+ */
mr->r_sock = rs;
+ sock_hold(rds_rs_to_sk(rs));
if (args->flags & RDS_RDMA_USE_ONCE)
mr->r_use_once = 1;
@@ -762,7 +767,12 @@ int rds_cmsg_rdma_args(struct rds_sock *rs, struct rds_message *rm,
RB_CLEAR_NODE(&local_odp_mr->r_rb_node);
kref_init(&local_odp_mr->r_kref);
local_odp_mr->r_trans = rs->rs_transport;
+ /* The MR can outlive its socket: a socket
+ * reference is held until the final kref is
+ * dropped in __rds_put_mr_final().
+ */
local_odp_mr->r_sock = rs;
+ sock_hold(rds_rs_to_sk(rs));
local_odp_mr->r_trans_private =
rs->rs_transport->get_mr(
NULL, 0, rs, &local_odp_mr->r_key, NULL,
@@ -771,7 +781,9 @@ int rds_cmsg_rdma_args(struct rds_sock *rs, struct rds_message *rm,
ret = PTR_ERR(local_odp_mr->r_trans_private);
rdsdebug("get_mr ret %d %p\"", ret,
local_odp_mr->r_trans_private);
- kfree(local_odp_mr);
+ local_odp_mr->r_trans_private = NULL;
+ kref_put(&local_odp_mr->r_kref,
+ __rds_put_mr_final);
ret = -EOPNOTSUPP;
goto out_pages;
}
diff --git a/net/rds/rds.h b/net/rds/rds.h
index a4816da162930..dcbc4610735d5 100644
--- a/net/rds/rds.h
+++ b/net/rds/rds.h
@@ -299,7 +299,10 @@ struct rds_mr {
unsigned int r_invalidate:1;
unsigned int r_write:1;
- struct rds_sock *r_sock; /* back pointer to the socket that owns us */
+ struct rds_sock *r_sock; /* socket that owns us; counted
+ * reference, dropped by
+ * __rds_put_mr_final()
+ */
struct rds_transport *r_trans;
void *r_trans_private;
};
--
2.53.0
^ permalink raw reply related [flat|nested] 32+ messages in thread* [PATCH AUTOSEL 6.18-6.12] sfc: add X4D PF support
2026-10-19 13:44 [PATCH AUTOSEL 6.18-6.12] net: dsa: mt7530: check CORE_PLL_GROUP4 access in mt7531_setup() Sasha Levin
` (26 preceding siblings ...)
2026-10-19 13:52 ` [PATCH AUTOSEL 6.18-5.10] net/rds: hold the socket while an rds_mr references it Sasha Levin
@ 2026-10-19 13:52 ` Sasha Levin
2026-10-19 13:52 ` [PATCH AUTOSEL 6.18-6.1] netfilter: tproxy: use DEBUG_NET_WARN_ON_ONCE for protocol fallbacks Sasha Levin
2026-10-19 13:53 ` [PATCH AUTOSEL 6.18-6.12] net: ethernet: mtk_wed: increase WED v2 WDMA RESV_BUFF to 0x80 Sasha Levin
29 siblings, 0 replies; 32+ messages in thread
From: Sasha Levin @ 2026-10-19 13:52 UTC (permalink / raw)
To: patches, stable
Cc: Andy Moreton, Pieter Jansen van Vuuren, Alejandro Lucero,
Simon Horman, Jakub Kicinski, Sasha Levin, ecree.xilinx,
andrew+netdev, davem, edumazet, pabeni, netdev, linux-net-drivers,
linux-kernel
From: Andy Moreton <andy.moreton@amd.com>
[ Upstream commit 24fedc7a569bce181728f0dd616504bef9f1513b ]
X4D is an X4 controller instance as an IP block in an SoC.
It has the same feature set as X4.
Signed-off-by: Andy Moreton <andy.moreton@amd.com>
Reviewed-by: Pieter Jansen van Vuuren <pieter.jansen-van-vuuren@amd.com>
Reviewed-by: Alejandro Lucero <alucerop@amd.com>
Reviewed-by: Simon Horman <horms@kernel.org>
Link: https://patch.msgid.link/20260916125641.12238-1-alucerop@amd.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
LLM Generated explanations, may be completely bogus:
The local tree is **6.18.57**. This commit is a four-line PCI ID
addition that maps X4D PFs onto the existing X4 nic type, which is
already in this tree.
===============================================================
PHASE 1: COMMIT MESSAGE FORENSICS
===============================================================
Step 1.1: PARSE THE SUBJECT LINE
Record: [subsystem: sfc (Solarflare/AMD ethernet)] [action verb: add]
[intent: enable X4D PF PCI devices in the existing sfc driver]
Step 1.2: PARSE ALL COMMIT MESSAGE TAGS
Record:
- Fixes: none (expected for a device-ID addition)
- Reported-by: none
- Tested-by: none
- Reviewed-by: Pieter Jansen van Vuuren (AMD), Alejandro Lucero (AMD),
Simon Horman (netdev)
- Acked-by: none
- Link: https://patch.msgid.link/20260916125641.12238-1-alucerop@amd.com
- Cc: stable: none (expected; not a negative signal)
- Signed-off-by: Andy Moreton (author, AMD); Jakub Kicinski (netdev
maintainer)
- Ignored pipeline tags: Upstream commit
24fedc7a569bce181728f0dd616504bef9f1513b; Signed-off-by: Sasha Levin
Notable: three Reviewed-by tags including a netdev regular (Horman) and
the subsystem maintainer path via Kicinski.
Step 1.3: ANALYZE THE COMMIT BODY TEXT
Record:
- [Bug description] Not a crash/corruption bug. X4D is an X4 controller
instantiated as an SoC IP block; without these PCI IDs the in-tree sfc
driver will not bind to that hardware.
- [Symptom/failure mode] X4D PF devices (PCI IDs 0x8c03 and 0xac03,
vendor Solarflare 0x1924) are unrecognized; the NIC does not come up.
- [Version information] None in the message. Author date 2026-09-16; not
in v6.18 or v6.18.57.
- [Root cause] Missing PCI ID table entries. Feature set is stated to be
identical to existing X4, so no new nic_type is required.
Step 1.4: DETECT HIDDEN BUG FIXES
Record: [Not a hidden bug fix.] This is an explicit hardware-enablement
/ device-ID addition. The subject says “add … support,” but the body and
diff make clear it is only IDs, not a new driver or API.
===============================================================
PHASE 2: DIFF ANALYSIS - LINE BY LINE
===============================================================
Step 2.1: INVENTORY THE CHANGES
Record:
- [Files] drivers/net/ethernet/sfc/efx.c: +4 / -0
- [Functions] none modified; only the static `efx_pci_table[]`
initializer
- [Scope] single-file surgical PCI ID addition
Step 2.2: UNDERSTAND THE CODE FLOW CHANGE
Record: [hunk] Before: table lists X4 PF IDs 0x0c03 (FF/LL) and 0x2c03
(FF only) bound to `efx_x4_nic_type`, then terminator. After: two
additional IDs 0x8c03 and 0xac03 also bound to `efx_x4_nic_type`. Probe
path is unchanged; PCI matching is the only difference. Normal bind path
for new devices only.
Step 2.3: IDENTIFY THE BUG MECHANISM
Record: [category: hardware enablement / PCI device ID addition]
[mechanism: devices 0x1924:0x8c03 and 0x1924:0xac03 would not match
`efx_pci_table`, so `efx_pci_probe()` never runs. Mapping them to
existing `efx_x4_nic_type` reuses the X4 implementation already in this
tree.]
Step 2.4: ASSESS THE FIX QUALITY
Record: [Quality: obviously correct; same pattern as the 2024 X4 ID
entries.] [Regression risk: very low. New IDs cannot match existing X4
or older Solarflare cards. No locking, no API, no data-structure change.
MODULE_DEVICE_TABLE already points at this table.]
===============================================================
PHASE 3: GIT HISTORY INVESTIGATION
===============================================================
Step 3.1: BLAME THE CHANGED LINES
Record: Adjacent X4 ID rows at v6.18.57 come from `cf06766f1525d` (“sfc:
Add X4 PF support”, Jonathan Cooper, 2024-09-10). That commit added
`efx_x4_nic_type` plus IDs 0x0c03/0x2c03. The table terminator dates to
2008. X4 support has been in-tree since well before 6.18.
Step 3.2: FOLLOW THE FIXES: TAG
Record: [No Fixes: tag.] Not applicable; this did not regress existing
code.
Step 3.3: CHECK FILE HISTORY FOR RELATED CHANGES
Record:
- Related: `cf06766f1525d` added X4 PF support (prerequisite; present in
v6.18.57).
- Only X4D-related commit under `drivers/net/ethernet/sfc/` is this one;
no follow-up X4D fixes.
- Standalone: not patch X/Y; single net-next patch.
Step 3.4: CHECK THE AUTHOR'S OTHER COMMITS
Record: Andy Moreton has other sfc commits in this history (EEPROM
reporting, EF10 filter_table_remove). Submitter on-list was Alejandro
Lucero (AMD). This is vendor driver work, not a random drive-by.
Step 3.5: CHECK FOR DEPENDENT/PREREQUISITE COMMITS
Record: Depends on `efx_x4_nic_type`, which exists at v6.18.57 in
`drivers/net/ethernet/sfc/ef10.c` and is already used by 0x0c03/0x2c03.
Context around the table in v6.18.57 matches the patch. [Can apply
standalone: yes.]
===============================================================
PHASE 4: MAILING LIST AND EXTERNAL RESEARCH
===============================================================
Step 4.1: FIND THE ORIGINAL PATCH DISCUSSION
Record:
- [Lore/msgid]
https://patch.msgid.link/20260916125641.12238-1-alucerop@amd.com
- Direct lore.kernel.org fetch returned 403; thread recovered via `b4
dig` and openwall mirrors.
- [Revisions] `b4 dig -a`: single revision, v1 `[PATCH net-next] sfc:
add X4D PF support` (2026-09-16).
- [Review] Simon Horman Reviewed-by with no requested changes
(2026-09-17).
- [Stable nomination] none in the thread.
- [NAKs] none.
- Applied by Jakub Kicinski; patchwork-bot announced netdev/net.git main
commit `24fedc7a569b` on 2026-09-19.
Step 4.2: CHECK WHO REVIEWED THE PATCH
Record: Recipients (`b4 dig -w`): netdev@vger.kernel.org, davem, kuba,
pabeni, edumazet, ecree.xilinx@gmail.com, Andy Moreton, Pieter Jansen
van Vuuren. Appropriate netdev maintainers and sfc/AMD people were on
Cc.
Step 4.3: SEARCH FOR THE BUG REPORT
Record: No bugzilla/syzbot report. This is vendor hardware enablement,
not a user-reported crash. Syzbot CI skipped the series as having no
functional impact on generic fuzzing (expected for an ID table).
Step 4.4: CHECK FOR RELATED PATCHES AND SERIES
Record: Standalone one-patch series. No other X4D patches in
`drivers/net/ethernet/sfc/`. No siena/ef100 table changes needed; X4/X4D
live in the main `efx_pci_table`.
Step 4.5: CHECK STABLE MAILING LIST HISTORY
Record: lore.kernel.org/stable search returned 403. No evidence on the
public netdev thread that this was requested for or rejected from
stable. UNVERIFIED: whether anyone mailed stable@ about it.
===============================================================
PHASE 5: CODE SEMANTIC ANALYSIS
===============================================================
Step 5.1: IDENTIFY KEY FUNCTIONS IN THE DIFF
Record: No functions edited. Data: `efx_pci_table[]`. Consumers:
`efx_pci_driver.id_table` and `MODULE_DEVICE_TABLE(pci, efx_pci_table)`.
Step 5.2: TRACE CALLERS
Record: `pci_register_driver(&efx_pci_driver)` from `efx_init_module()`.
Match leads to `efx_pci_probe()` → `efx_pci_probe_post_io()` →
`efx_pci_probe_main()`. Interrupt/hot path unchanged. Impact surface:
PCI enumeration of the two new IDs only.
Step 5.3: TRACE CALLEES
Record: Probe uses `efx_x4_nic_type` (already defined in ef10.c):
EF10-like ops, `efx_ef10_probe_pf`, MCDI, etc. Same as discrete X4 PFs.
Step 5.4: FOLLOW THE CALL CHAIN
Record: Userspace cannot trigger this except by plugging/enumerating the
NIC. Reachable on any machine with an X4D PF and CONFIG_SFC. Not a
syscall-fuzz path.
Step 5.5: SEARCH FOR SIMILAR PATTERNS
Record: Same two-ID (FF/LL vs FF-only) pattern as X4 0x0c03/0x2c03.
Older SFC9120/9140/9220/9250 entries use the same table style. No other
copies of 0x8c03/0xac03 in this driver.
===============================================================
PHASE 6: CROSS-REFERENCING AGAINST THE LOCAL TREE
===============================================================
Step 6.1: DOES THE BUGGY CODE EXIST IN THIS TREE?
Record: Local tree is **v6.18.57** (`Makefile` VERSION=6 PATCHLEVEL=18
SUBLEVEL=57). At v6.18.57, `efx_pci_table` has X4 IDs but not X4D
0x8c03/0xac03. `efx_x4_nic_type` is present. The “bug” (missing IDs)
exists here. X4 support was added in 2024 and is in v6.18.
Step 6.2: CHECK FOR BACKPORT COMPLICATIONS
Record: [Expected backport difficulty: clean apply.] v6.18.57 context is
identical to the hunk. No conflicting refactor of the ID table between
v6.18.57 and this patch.
Step 6.3: CHECK IF RELATED FIXES ARE ALREADY HERE
Record: No other X4D ID or quirk in v6.18.57. Candidate overlay commit
`6fd5947dd3d9d` is ignored per pipeline rules; it is not part of
v6.18.57.
===============================================================
PHASE 7: SUBSYSTEM AND MAINTAINER CONTEXT
===============================================================
Step 7.1: IDENTIFY THE SUBSYSTEM AND ITS CRITICALITY
Record: [Subsystem: drivers/net/ethernet/sfc] [Criticality: IMPORTANT
for users of Solarflare/AMD X4-family NICs; PERIPHERAL relative to core
mm/fs. Not staging.]
Step 7.2: ASSESS SUBSYSTEM ACTIVITY
Record: sfc is actively maintained (X4 in 2024, stats/MDIO churn, AMD
authors). Mature driver; this is incremental ID adding on a stable
nic_type.
===============================================================
PHASE 8: IMPACT AND RISK ASSESSMENT
===============================================================
Step 8.1: DETERMINE WHO IS AFFECTED
Record: [Affected population: driver-specific / hardware-specific.] Only
Solarflare X4D PF devices (SoC IP block). Existing X4 and older SFC
users are unaffected.
Step 8.2: DETERMINE THE TRIGGER CONDITIONS
Record: [Trigger: PCI probe of 0x1924:0x8c03 or 0x1924:0xac03.] Common
on systems that ship that SoC NIC; never on others. Not an unprivileged
software trigger.
Step 8.3: DETERMINE THE FAILURE MODE SEVERITY
Record: [Failure mode: device never bound; no Ethernet for that port.]
[Severity: MEDIUM for those platforms (hardware dead in this kernel),
LOW for everyone else.] Not a crash/UAF, but it is user-visible hardware
enablement of the kind stable explicitly accepts.
Step 8.4: CALCULATE RISK-BENEFIT RATIO
Record:
- [Benefit] High for X4D SoC users on 6.18.y (NIC actually works).
Documented same feature set as X4 already in this tree.
- [Risk] Very low: 4 lines, ID table only, no behavioral change for
other IDs.
- [Ratio] Strongly favors backport.
===============================================================
PHASE 9: FINAL SYNTHESIS
===============================================================
Step 9.1: COMPILE THE EVIDENCE
FOR:
- Official rule in this tree: “must either fix a real bug that bothers
people or just add a device ID.”
- User-instruction exception: new PCI IDs in an existing driver are YES.
- 4-line change; maps to existing `efx_x4_nic_type` already in 6.18.57.
- Author states X4D has the same feature set as X4.
- Reviewed by Horman and AMD; applied by Kicinski.
- Clean apply; no prerequisites missing; no follow-up X4D patches.
- No risk to existing devices.
AGAINST:
- Submitted to net-next, not as a “fix.”
- No crash/security/corruption.
- Niche SoC NIC; no public user bug report.
- UNVERIFIED: stable@ discussion (lore 403).
Step 9.2: APPLY THE STABLE RULES CHECKLIST
1. Obviously correct and tested? [PASS — identical to existing X4 ID
rows; reviewed; merged upstream]
2. Fixes a real bug that affects users? [PASS under the device-ID
clause; without it X4D NICs do not probe]
3. Important issue (crash, security, corruption, deadlock)? [PASS via
device-ID exception, not via crash]
4. Small and contained? [PASS — 4 lines, one table]
5. No new features or APIs? [PASS — no new nic_type, no new sysfs/uAPI;
IDs only]
6. Can apply to the local tree? [PASS — v6.18.57 has X4 type and
matching table context]
Step 9.3: CHECK FOR EXCEPTION CATEGORIES
Record: [Exception category: NEW DEVICE IDs to an existing driver.]
Driver and X4 nic type already exist in 6.18.57.
Step 9.4: MAKE YOUR DECISION
This should be backported to this 6.18.y tree. It is the textbook PCI ID
addition: small, obviously correct, no new APIs, and the implementation
it binds to is already present.
===============================================================
What the commit solves for stable users
===============================================================
On 6.18.y, sfc already drives X4 PFs (`0x0c03` / `0x2c03`) via
`efx_x4_nic_type`. X4D is the same controller as an SoC IP with
different PCI IDs. Without this patch those devices never match, so the
interface never appears. Adding the two IDs is the entire change.
Risk vs benefit: benefit is hardware enablement for X4D PFs; risk is
confined to those IDs. No locking, no API, no shared-path logic change.
Concerns: none material. Not a series; X4 support is already here; apply
is clean. Absence of Cc: stable is expected for this review queue.
===============================================================
Verification
===============================================================
- [Phase 1] Parsed tags: Reviewed-by Pieter Jansen van Vuuren, Alejandro
Lucero, Simon Horman; Link msgid
20260916125641.12238-1-alucerop@amd.com; no Fixes/Reported-
by/Cc:stable
- [Phase 1] Body: “X4D is an X4 controller instance as an IP block in an
SoC. It has the same feature set as X4.”
- [Phase 2] Diff: +4 lines in `efx_pci_table[]` only; IDs 0x8c03 and
0xac03 → `efx_x4_nic_type`
- [Phase 3] `git describe HEAD` / `make kernelversion` / Makefile:
v6.18.57
- [Phase 3] `git merge-base --is-ancestor 6fd5947dd3d9d v6.18.57` → NOT
in v6.18.57 (candidate overlay ignored)
- [Phase 3] `git merge-base --is-ancestor cf06766f1525d v6.18.57` → X4
support IS in this tree
- [Phase 3] `git show v6.18.57:drivers/net/ethernet/sfc/efx.c` table has
X4 IDs, not X4D; terminator immediately after 0x2c03
- [Phase 3] `git blame -L 900,911 v6.18.57 --
drivers/net/ethernet/sfc/efx.c`: X4 rows from cf06766f1525d (2024)
- [Phase 3] `git grep efx_x4_nic_type v6.18.57 --
drivers/net/ethernet/sfc/`: ef10.c:4308, efx.c X4 IDs, nic.h extern
- [Phase 3] `git log --grep=X4D -- drivers/net/ethernet/sfc/`: only this
commit
- [Phase 4] `b4 dig -c 24fedc7a569bce181728f0dd616504bef9f1513b`: msgid
match; v1 only
- [Phase 4] `b4 dig -a`: single series, no v2+
- [Phase 4] `b4 dig -w`: netdev maintainers + AMD + Edward Cree on Cc
- [Phase 4] Openwall: Horman Reviewed-by; patchwork applied
24fedc7a569b; no NAK
- [Phase 4] lore.kernel.org WebFetch: 403 — used openwall/b4 instead
- [Phase 5] `efx_pci_table` used as `efx_pci_driver.id_table` and
MODULE_DEVICE_TABLE; probe is `efx_pci_probe`
- [Phase 5] `PCI_VENDOR_ID_SOLARFLARE` is 0x1924 in v6.18.57 pci_ids.h
- [Phase 6] Context at v6.18.57 matches patch; clean apply
- [Phase 7] CONFIG_SFC present: “Solarflare SFC9100/EF100-family
support”
- [Phase 8] Failure mode verified as non-bind of new PCI IDs, not a
crash
- [Phase 9] Documentation/process/stable-kernel-rules.rst line 15: “or
just add a device ID”
- UNVERIFIED: stable@ mailing list (lore 403)
This is a contained PCI ID addition to a driver and nic type that
already exist in 6.18.y, which stable policy explicitly allows.
**YES**
drivers/net/ethernet/sfc/efx.c | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/drivers/net/ethernet/sfc/efx.c b/drivers/net/ethernet/sfc/efx.c
index 112e55b98ed3b..62df7b6a26fa5 100644
--- a/drivers/net/ethernet/sfc/efx.c
+++ b/drivers/net/ethernet/sfc/efx.c
@@ -903,6 +903,10 @@ static const struct pci_device_id efx_pci_table[] = {
.driver_data = (unsigned long)&efx_x4_nic_type},
{PCI_DEVICE(PCI_VENDOR_ID_SOLARFLARE, 0x2c03), /* X4 PF (FF only) */
.driver_data = (unsigned long)&efx_x4_nic_type},
+ {PCI_DEVICE(PCI_VENDOR_ID_SOLARFLARE, 0x8c03), /* X4D PF (FF/LL) */
+ .driver_data = (unsigned long)&efx_x4_nic_type},
+ {PCI_DEVICE(PCI_VENDOR_ID_SOLARFLARE, 0xac03), /* X4D PF (FF only) */
+ .driver_data = (unsigned long)&efx_x4_nic_type},
{0} /* end of list */
};
--
2.53.0
^ permalink raw reply related [flat|nested] 32+ messages in thread* [PATCH AUTOSEL 6.18-6.1] netfilter: tproxy: use DEBUG_NET_WARN_ON_ONCE for protocol fallbacks
2026-10-19 13:44 [PATCH AUTOSEL 6.18-6.12] net: dsa: mt7530: check CORE_PLL_GROUP4 access in mt7531_setup() Sasha Levin
` (27 preceding siblings ...)
2026-10-19 13:52 ` [PATCH AUTOSEL 6.18-6.12] sfc: add X4D PF support Sasha Levin
@ 2026-10-19 13:52 ` Sasha Levin
2026-10-19 13:53 ` [PATCH AUTOSEL 6.18-6.12] net: ethernet: mtk_wed: increase WED v2 WDMA RESV_BUFF to 0x80 Sasha Levin
29 siblings, 0 replies; 32+ messages in thread
From: Sasha Levin @ 2026-10-19 13:52 UTC (permalink / raw)
To: patches, stable
Cc: Fernando Fernandez Mancera, Pablo Neira Ayuso, Sasha Levin, fw,
davem, edumazet, kuba, pabeni, netfilter-devel, coreteam, netdev,
linux-kernel
From: Fernando Fernandez Mancera <fmancera@suse.de>
[ Upstream commit 1f0391aea883e99d402d0f77ba3530e4e879350d ]
Replace WARN_ON calls with DEBUG_NET_WARN_ON_ONCE in the default switch
blocks of nf_tproxy_get_sock_v4 and v6. Unsupported transport protocols
are already safely handled by returning a NULL socket pointer. This
prevents unnecessary system panics when panic_on_warn=1 is enabled in
production systems.
Link: https://patch.msgid.link/cover.1786968834.git.zhilinz@nebusec.ai/
Signed-off-by: Fernando Fernandez Mancera <fmancera@suse.de>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
LLM Generated explanations, may be completely bogus:
This is a two-line datapath `WARN_ON` demotion in netfilter tproxy. The
`WARN_ON(1)` is present in this 6.12 tree, `DEBUG_NET_WARN_ON_ONCE`
already exists here, and Pablo later took the change into `-net` because
that warning is reachable and can panic under `panic_on_warn=1`.
===============================================================
PHASE 1: COMMIT MESSAGE FORENSICS
===============================================================
Step 1.1: PARSE THE SUBJECT LINE
Record: [netfilter / tproxy] [use] [replace `WARN_ON` with
`DEBUG_NET_WARN_ON_ONCE` on unsupported-protocol fallbacks in IPv4/IPv6
tproxy socket lookup]
Step 1.2: PARSE ALL COMMIT MESSAGE TAGS
Record:
- Link:
https://patch.msgid.link/cover.1786968834.git.zhilinz@nebusec.ai/
(cover from Zhiling Zou / nebusec.ai, 2026-08-17)
- Signed-off-by: Fernando Fernandez Mancera <fmancera@suse.de> (author)
- Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org> (netfilter
maintainer)
- No Fixes:, Reported-by:, Tested-by:, Reviewed-by:, Acked-by:, or Cc:
stable (absence expected)
- Ignore any pipeline "Upstream commit" / Sasha Levin SOB if present
Notable: the msgid points at an external bug-finding series, not at
Fernando’s original June nf-next cover. Pablo later described a concrete
datapath reachability bug.
Step 1.3: ANALYZE THE COMMIT BODY TEXT
Record:
- Bug: `WARN_ON(1)` in the default arm of the L4 protocol switch in
`nf_tproxy_get_sock_v4` / `nf_tproxy_get_sock_v6`.
- Symptom: kernel warning splat; with `panic_on_warn=1`, a full panic.
- Failure mode: “unnecessary system panics when panic_on_warn=1”.
- Root cause (author): unsupported transport protocols are already
handled by returning `sk = NULL`; the `WARN_ON` is not needed for
correctness.
- Version info in the message: none.
Step 1.4: DETECT HIDDEN BUG FIXES
Record: Not a disguised cleanup in the sense of a leak/UAF. It is a real
defensive fix: a reachable `WARN_ON` in the packet path that can panic
production/hardened kernels. Originally patch 6/9 of an nf-next “replace
raw warnings” series; Pablo later moved this specific change to `-net`
because the warning is reachable.
===============================================================
PHASE 2: DIFF ANALYSIS - LINE BY LINE
===============================================================
Step 2.1: INVENTORY THE CHANGES
Record:
- `net/ipv4/netfilter/nf_tproxy_ipv4.c`: 1 line changed (`WARN_ON(1)` →
`DEBUG_NET_WARN_ON_ONCE(1)`)
- `net/ipv6/netfilter/nf_tproxy_ipv6.c`: same 1-line change
- Functions: `nf_tproxy_get_sock_v4`, `nf_tproxy_get_sock_v6`
- Scope: single-function surgical fix, two files, +0/−0 net (2
modifications)
Step 2.2: UNDERSTAND THE CODE FLOW CHANGE
Record:
- Before: protocol switch handles TCP and UDP; any other protocol hits
`default: WARN_ON(1); sk = NULL;` then returns NULL.
- After: same NULL return; warning only if `CONFIG_DEBUG_NET=y`, and
only once.
- Path: packet/socket-lookup path for TPROXY, not init. Already a
handled fallback, not a crash-without-WARN bug.
Step 2.3: IDENTIFY THE BUG MECHANISM
Record:
- Category: logic/correctness of assertions in the datapath (WARN-in-
packet-path / panic_on_warn DoS), not UAF/leak/race.
- Mechanism: `WARN_ON(1)` treats “non-TCP/UDP protocol in tproxy lookup”
as a kernel bug. Callers can still pass a non-TCP/UDP protocol. The
function already returns NULL and callers drop/break. The WARN is the
harmful part under `panic_on_warn`.
Step 2.4: ASSESS THE FIX QUALITY
Record:
- Obviously correct: fallback already returns NULL; only the assertion
changes.
- Minimal: two identical one-line edits.
- Regression risk: very low. Production kernels (`CONFIG_DEBUG_NET=n`)
compile this to `BUILD_BUG_ON_INVALID(1)`, a compile-time sizeof no-op
(`include/linux/build_bug.h`). Debug kernels still warn once.
- No API, locking, or structure changes.
- Red flag that does not block backport: this does not fix checkentry
itself; it makes the already-safe fallback non-fatal.
===============================================================
PHASE 3: GIT HISTORY INVESTIGATION
===============================================================
Step 3.1: BLAME THE CHANGED LINES
Record:
- Introduced by `45ca4e0cf2734` (“netfilter: Libify xt_TPROXY”, Máté
Eckl, 2018-06-01).
- `git merge-base --is-ancestor 45ca4e0cf2734 HEAD` succeeded. Present
since the tproxy libification, long before 6.12.
Step 3.2: FOLLOW THE FIXES: TAG
Record: No Fixes: tag. Not applicable. Upstream applied commit is
`1f0391aea883` (netdev `-net`); `git merge-base --is-ancestor
1f0391aea883 HEAD` failed, so this tree does not already contain the
fix.
Step 3.3: CHECK FILE HISTORY FOR RELATED CHANGES
Record: Recent local history on these files: `21a673bddc8fd` (NULL
indev), `4a02426787bf0` (BH deadlock), hashinfo via net. No competing
tproxy WARN change. Standalone; not patch 2/3 of a required series for
this tree.
Step 3.4: CHECK THE AUTHOR'S OTHER COMMITS
Record: Fernando Fernandez Mancera is a regular netfilter contributor in
this tree (conncount, synproxy, osf, NFQUEUE). Pablo signed off as
maintainer. Original June 2026 series was a broad WARN→DEBUG_NET
conversion; only tproxy (and separately conncount) were later taken into
`-net`.
Step 3.5: CHECK FOR DEPENDENT/PREREQUISITE COMMITS
Record: Depends only on `DEBUG_NET_WARN_ON_ONCE` in
`include/net/net_debug.h`, present in this tree. Both files include
`linux/skbuff.h` (IPv4 directly; IPv6 via `nf_tproxy.h` → `net/tcp.h` →
`skbuff.h`), and `skbuff.h` includes `net/net_debug.h`. Applies
standalone.
===============================================================
PHASE 4: MAILING LIST AND EXTERNAL RESEARCH
===============================================================
Step 4.1: FIND THE ORIGINAL PATCH DISCUSSION
Record:
- `b4 dig -c` not usable: candidate SHA is not in this checkout.
- lore.kernel.org fetches returned 403.
- Original submission: [PATCH 6/9 nf-next] 2026-06-01, cover “replace
raw warnings with DEBUG_NET_WARN_ON_ONCE”
(https://ratatoskr.run/netfilter-devel/2026/06/17074549 and 17074557).
Intent: nf-next hardening.
- Re-posted as [PATCH net 01/10] then [PATCH net,v2 1/9] by Pablo,
2026-08-21 / 2026-08-27:
https://lists.openwall.net/netdev/2026/08/21/202 and
https://lists.openwall.net/netdev/2026/08/27/160
- Pablo’s cover: “a recent bug found a way to reach WARN_ON from
datapath due to insufficient validation of xt_TPROTO checkentry.”
- Applied to netdev `-net` as `1f0391aea883`; included in the v7.3-rc1
networking pull.
- No NAK found. Florian commented on other patches in the June series,
not a NAK of this tproxy hunk.
- Commit Link cover.1786968834.git.zhilinz@nebusec.ai is dated
2026-08-17 (same reporter class as the sch_cake WARN backport already
in this tree).
Step 4.2: CHECK WHO REVIEWED THE PATCH
Record: Recipients on Pablo’s net posting: netfilter-devel, davem, kuba,
pabeni, edumazet, horms, fw@strlen.de (Florian Westphal), ja@ssi.bg.
Pablo applied it. No separate Reviewed-by on the commit.
Step 4.3: SEARCH FOR THE BUG REPORT
Record: Direct zhilinz cover fetch from lore 403’d. Independent
maintainer statement in the `-net` cover is the verified evidence of
datapath reachability. Same reporter’s analogous `WARN_ON` panic_on_warn
issue was already backported here as `2504a76e5c069` (“sch_cake: drop
WARN_ON(1) for malformed packets”).
Step 4.4: CHECK FOR RELATED PATCHES AND SERIES
Record: June 9-patch nf-next series is bulk hardening. Pablo cherry-
picked tproxy into `-net` because it was shown reachable; conncount in
the same `-net` pull was explicitly “not known issue”. This tproxy
commit does not need the rest of that series.
Step 4.5: CHECK STABLE MAILING LIST HISTORY
Record: lore.kernel.org/stable search 403. UNVERIFIED whether anyone
already requested/rejected this on stable@. No evidence in-tree that it
was already applied.
===============================================================
PHASE 5: CODE SEMANTIC ANALYSIS
===============================================================
Step 5.1: IDENTIFY KEY FUNCTIONS IN THE DIFF
Record: `nf_tproxy_get_sock_v4`, `nf_tproxy_get_sock_v6`.
Step 5.2: TRACE CALLERS
Record (from grep in this tree):
- `net/netfilter/xt_TPROXY.c`: `tproxy_tg4`, `tproxy_tg6_v1` (mangle
PREROUTING)
- `net/netfilter/nft_tproxy.c`: `nft_tproxy_eval_v4`,
`nft_tproxy_eval_v6`
- Re-entered from `nf_tproxy_handle_time_wait4` / v6 for TIME_WAIT
reopen
- Exported (`EXPORT_SYMBOL_GPL`)
xt_TPROXY checkentry (`tproxy_tg4_check` / `tproxy_tg6_check`) rejects
rules that are not `-p tcp`/`-p udp`. Native nft_tproxy returns
`NFT_BREAK` unless `pkt->tprot` is TCP or UDP. TPROXY target has no
`.proto` in the `xt_target` struct; protocol restriction is only
checkentry + nft eval. Pablo’s cover says that checkentry validation is
insufficient, so the default arm can still run.
Step 5.3: TRACE CALLEES
Record: TCP/UDP hash lookups (`inet_lookup_listener`, `udp4_lib_lookup`,
IPv6 equivalents), `sock_put` on mismatch. Default arm only sets `sk =
NULL`. No alloc/lock in the changed path.
Step 5.4: FOLLOW THE CALL CHAIN
Record: Reachable from netfilter PREROUTING when TPROXY/nft tproxy is
loaded. Not a syscall on every box, but it is a packet path. With user
namespaces + CAP_NET_ADMIN in a netns, and with syzbot-style fuzzing of
iptables/nft compat, it is userspace-reachable once the module is
present. Native nft IPv4 sets `pkt->tprot = ip->protocol` then checks
tprot, so that path is consistent; xt_TPROXY uses `iph->protocol` /
`ipv6_find_hdr()` after a checkentry that Pablo says can be bypassed.
Step 5.5: SEARCH FOR SIMILAR PATTERNS
Record: Same files still `BUG()` on invalid `lookup_type` (kernel enum,
not packet proto). Unrelated. Networking policy is to use
`DEBUG_NET_WARN_ON_ONCE` on datapath asserts (`include/net/net_debug.h`,
`CONFIG_DEBUG_NET`). This tree already backported a sibling pattern in
sch_cake.
===============================================================
PHASE 6: CROSS-REFERENCING AGAINST THE LOCAL TREE
===============================================================
Step 6.1: DOES THE BUGGY CODE EXIST IN THIS TREE?
Record: Yes.
- Tree: `pending-6.12`, `git describe` p-6.12-3-gc504cf0baa3d8, `make
kernelversion` 6.12.111 (Makefile 6.12.111).
- `WARN_ON(1)` at `nf_tproxy_ipv4.c:141` and `nf_tproxy_ipv6.c:140`.
- Bug introduced 2018 (`45ca4e0cf2734`), present in this branch.
Step 6.2: CHECK FOR BACKPORT COMPLICATIONS
Record: Surrounding code matches the submitted diff context exactly.
Expected apply: clean. `DEBUG_NET_WARN_ON_ONCE` already defined. No
extra include needed.
Step 6.3: CHECK IF RELATED FIXES ARE ALREADY HERE
Record: Upstream `1f0391aea883` is not an ancestor. No other commit on
these files replaces this `WARN_ON`. sch_cake WARN demotion is a
precedent, not a substitute.
===============================================================
PHASE 7: SUBSYSTEM AND MAINTAINER CONTEXT
===============================================================
Step 7.1: IDENTIFY THE SUBSYSTEM AND ITS CRITICALITY
Record: netfilter tproxy (`net/ipv4/netfilter`, `net/ipv6/netfilter`,
used by xt_TPROXY / NFT_TPROXY). Criticality: IMPORTANT (transparent
proxy / service-mesh / squid / k8s-style TPROXY), not core mm/VFS, not
staging.
Step 7.2: ASSESS SUBSYSTEM ACTIVITY
Record: Mature. TPROXY lookup logic has been stable for years; recent
commits are bugfixes (NULL indev, BH deadlock), not refactors. Fits “old
assertion, newly shown reachable.”
===============================================================
PHASE 8: IMPACT AND RISK ASSESSMENT
===============================================================
Step 8.1: DETERMINE WHO IS AFFECTED
Record: Config-specific: `NF_TPROXY_IPV4`/`IPV6`,
`NETFILTER_XT_TARGET_TPROXY`, `NFT_TPROXY`. Production transparent-proxy
users, and fuzzers/hardened kernels that load those modules with
`panic_on_warn=1`.
Step 8.2: DETERMINE THE TRIGGER CONDITIONS
Record: Packet hits tproxy lookup with protocol ≠ TCP/UDP. Native
nft_tproxy tries to gate on tprot; xt_TPROXY checkentry tries to require
`-p tcp|udp`. Pablo stated checkentry is insufficient, so the default
arm is reachable from the datapath. Not every boot; requires TPROXY in
the ruleset (or a checkentry bypass). Unprivileged trigger: not from raw
packets alone without a TPROXY rule; with a loaded rule or netns admin,
yes. Syzbot-class environments typically have both.
Step 8.3: DETERMINE THE FAILURE MODE SEVERITY
Record:
- Default: `WARN_ON` splat in dmesg, lookup returns NULL, packet
dropped/broken — MEDIUM.
- `panic_on_warn=1` (syzbot, some hardened/Android/debug distros): panic
— HIGH/CRITICAL for those users.
- Not data corruption or UAF.
Step 8.4: CALCULATE RISK-BENEFIT RATIO
Record:
- Benefit: medium-high for TPROXY + panic_on_warn; prevents a panic on a
path that already handles the case. Low-medium for default 6.12
(warning only).
- Risk: very low (2 lines, no-op when `CONFIG_DEBUG_NET=n`).
- Ratio: favorable. Same class as the sch_cake WARN removal already in
this 6.12 tree.
===============================================================
PHASE 9: FINAL SYNTHESIS
===============================================================
Step 9.1: COMPILE THE EVIDENCE
FOR:
- `WARN_ON(1)` is in this 6.12 tree since 2018; fix is not.
- Maintainer placed this in `-net` because the warning is reachable from
the datapath.
- Fallback already returns NULL; the WARN is the only dangerous part
under `panic_on_warn`.
- Two lines, clean apply, `DEBUG_NET_WARN_ON_ONCE` already exists.
- Same reporter/class as sch_cake WARN backport `2504a76e5c069` already
in this tree.
- TPROXY is a real production feature, not staging.
AGAINST:
- Started as nf-next bulk hardening, not a one-off crash fix.
- Does not repair checkentry; only demotes the assertion.
- Default kernels do not panic on WARN.
- Affects only TPROXY users / fuzzers, not all 6.12 systems.
UNRESOLVED:
- Exact zhilinz reproducer (lore 403). Relied on Pablo’s `-net` cover
instead.
- Whether stable@ already discussed this (lore stable 403).
- Precise checkentry-bypass mechanics in 6.12 (nft_compat proto vs
actual matches is a plausible hole; not fully reproduced here).
Decision does not depend on re-deriving that hole because the
maintainer already stated reachability and the fallback is already
correct.
Step 9.2: APPLY THE STABLE RULES CHECKLIST
1. Obviously correct and tested? PASS — two-line assert demotion; Pablo
merged to `-net`.
2. Fixes a real bug that affects users? PASS — reachable datapath WARN;
panic with `panic_on_warn=1`.
3. Important issue? PASS — panic/DoS on hardened and fuzzer configs;
warning noise otherwise. Severity HIGH with `panic_on_warn`, MEDIUM
without.
4. Small and contained? PASS — 2 lines, 2 files.
5. No new features or APIs? PASS.
6. Can apply to the local tree? PASS — buggy code present, macro
present, context matches.
Step 9.3: CHECK FOR EXCEPTION CATEGORIES
Record: Not a device-ID/quirk/DT/build/docs exception. Ordinary small
bugfix.
Step 9.4: MAKE YOUR DECISION
The local tree is 6.12.111. The bug exists here. The fix is a minimal,
obviously correct datapath change that stops a handled protocol fallback
from taking down `panic_on_warn` systems. That matches stable policy and
an existing 6.12 precedent for the same WARN class.
What it solves for stable users: a tproxy socket lookup for an
unsupported L4 protocol currently fires `WARN_ON(1)` even though it
already returns NULL. On kernels with `panic_on_warn=1` that is a panic;
otherwise a splat. Backporting only changes the assertion, not lookup
behavior.
Risk vs benefit: benefit is avoiding a panic/warning on a real packet
path; risk is negligible (`CONFIG_DEBUG_NET=n` compiles to a no-op). No
prerequisites missing in 6.12.
===============================================================
Verification:
- [Phase 1] Tags: Link to zhilinz cover (2026-08-17), SOB Fernando +
Pablo; no Fixes/syzbot/Cc:stable
- [Phase 2] Diff: 2 files, `WARN_ON(1)` → `DEBUG_NET_WARN_ON_ONCE(1)` in
`nf_tproxy_get_sock_v4/v6` default arms; `sk = NULL` unchanged
- [Phase 2] Confirmed current 6.12 code still has `WARN_ON(1)` at
ipv4:141 and ipv6:140
- [Phase 3] `git blame`: 45ca4e0cf2734 (2018 Libify xt_TPROXY); ancestor
of HEAD
- [Phase 3] `git merge-base --is-ancestor 1f0391aea883 HEAD`: not in
this tree
- [Phase 3] `DEBUG_NET_WARN_ON_ONCE` in `include/net/net_debug.h` gated
on `CONFIG_DEBUG_NET`; `BUILD_BUG_ON_INVALID` is sizeof no-op
- [Phase 3] `linux/skbuff.h` includes `net/net_debug.h`; both tproxy
files get that include
- [Phase 4] lore/b4: lore 403; used openwall + ratatoskr. Original: nf-
next 6/9 2026-06-01; Pablo `-net` 2026-08-21/27 stating datapath
reachability via weak xt protocol checkentry
- [Phase 4] No NAK found; applied as 1f0391aea883 in netdev `-net`
- [Phase 4] UNVERIFIED: full zhilinz thread body; stable@ discussion
- [Phase 5] Callers: xt_TPROXY, nft_tproxy, timewait helpers; xt
checkentry requires TCP/UDP; nft eval checks tprot; TPROXY `xt_target`
has no `.proto`
- [Phase 6] `git describe`/Makefile: pending-6.12 / 6.12.111; context
matches patch; clean apply
- [Phase 6] Precedent in this tree: 2504a76e5c069 sch_cake WARN_ON drop,
Reported-by Zhiling Zou, panic_on_warn
- [Phase 7] netfilter tproxy, IMPORTANT, mature
- [Phase 8] Failure: WARN splat or panic_on_warn panic; not
UAF/corruption
- UNVERIFIED: exact checkentry-bypass PoC in 6.12; not used as the sole
driver (maintainer statement + existing WARN + safe NULL return are
verified)
The change is small, already handled correctly aside from the warning,
and belongs on this 6.12 tree.
**YES**
net/ipv4/netfilter/nf_tproxy_ipv4.c | 2 +-
net/ipv6/netfilter/nf_tproxy_ipv6.c | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/net/ipv4/netfilter/nf_tproxy_ipv4.c b/net/ipv4/netfilter/nf_tproxy_ipv4.c
index 041c3f37f237b..5eab7a2dc8ef2 100644
--- a/net/ipv4/netfilter/nf_tproxy_ipv4.c
+++ b/net/ipv4/netfilter/nf_tproxy_ipv4.c
@@ -137,7 +137,7 @@ nf_tproxy_get_sock_v4(struct net *net, struct sk_buff *skb,
}
break;
default:
- WARN_ON(1);
+ DEBUG_NET_WARN_ON_ONCE(1);
sk = NULL;
}
diff --git a/net/ipv6/netfilter/nf_tproxy_ipv6.c b/net/ipv6/netfilter/nf_tproxy_ipv6.c
index b2f59ed9d7cc0..12ec36a6be2e1 100644
--- a/net/ipv6/netfilter/nf_tproxy_ipv6.c
+++ b/net/ipv6/netfilter/nf_tproxy_ipv6.c
@@ -136,7 +136,7 @@ nf_tproxy_get_sock_v6(struct net *net, struct sk_buff *skb, int thoff,
}
break;
default:
- WARN_ON(1);
+ DEBUG_NET_WARN_ON_ONCE(1);
sk = NULL;
}
--
2.53.0
^ permalink raw reply related [flat|nested] 32+ messages in thread* [PATCH AUTOSEL 6.18-6.12] net: ethernet: mtk_wed: increase WED v2 WDMA RESV_BUFF to 0x80
2026-10-19 13:44 [PATCH AUTOSEL 6.18-6.12] net: dsa: mt7530: check CORE_PLL_GROUP4 access in mt7531_setup() Sasha Levin
` (28 preceding siblings ...)
2026-10-19 13:52 ` [PATCH AUTOSEL 6.18-6.1] netfilter: tproxy: use DEBUG_NET_WARN_ON_ONCE for protocol fallbacks Sasha Levin
@ 2026-10-19 13:53 ` Sasha Levin
29 siblings, 0 replies; 32+ messages in thread
From: Sasha Levin @ 2026-10-19 13:53 UTC (permalink / raw)
To: patches, stable
Cc: Shiji Yang, Simon Horman, Lorenzo Bianconi, Jakub Kicinski,
Sasha Levin, nbd, andrew+netdev, davem, edumazet, pabeni,
matthias.bgg, angelogioacchino.delregno, netdev, linux-kernel,
linux-arm-kernel, linux-mediatek
From: Shiji Yang <yangshiji66@outlook.com>
[ Upstream commit c0ef04232f9fac083c3d8a77ce58ba32243e6734 ]
Change WDMA RESV_BUFF from 0x40 to 0x80 to avoid CDM TX FIFO overflow.
Without this patch mt7986 and mt7981 may have WDMA TX hang issue. This
patch was pulled from mtk-openwrt-feeds GPL open source project.
Link: https://github.com/mediatek/mtk-openwrt-feeds/commit/07c87502e854b68b48544d101b6fe17ec059b97b
Signed-off-by: Shiji Yang <yangshiji66@outlook.com>
Reviewed-by: Simon Horman <horms@kernel.org>
Acked-by: Lorenzo Bianconi <lorenzo@kernel.org>
Link: https://patch.msgid.link/OSZPR01MB779537889255E2F606E47EABBCA52@OSZPR01MB7795.jpnprd01.prod.outlook.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
LLM Generated explanations, may be completely bogus:
This 6.12.111 tree has the WED v2 DMA path without the RESV_BUFF bump.
The change is a small MediaTek hardware workaround for a WDMA TX hang on
mt7986/mt7981.
## Phase 1: Commit message forensics
**Step 1.1 — Subject**
Record: `[net: ethernet: mtk_wed:] [increase] [WED v2 WDMA RESV_BUFF
0x40 -> 0x80]`
**Step 1.2 — Tags**
Record:
- `Link: https://github.com/mediatek/mtk-openwrt-
feeds/commit/07c87502e854b68b48544d101b6fe17ec059b97b` (vendor source)
- `Signed-off-by: Shiji Yang <yangshiji66@outlook.com>` (author)
- `Reviewed-by: Simon Horman <horms@kernel.org>`
- `Acked-by: Lorenzo Bianconi <lorenzo@kernel.org>` (mtk_wed maintainer)
- `Link: https://patch.msgid.link/OSZPR01MB779...` (netdev submission)
- `Signed-off-by: Jakub Kicinski <kuba@kernel.org>` (applied to
netdev/net)
- No `Fixes:` and no `Cc: stable` on the committed version (expected for
this pipeline)
- No `Reported-by:` / `Tested-by:` / syzbot
**Step 1.3 — Body**
Record:
- Bug: default WDMA `RESV_BUFF` of `0x40` can overflow the CDM TX FIFO
on WED v2.
- Symptom: WDMA TX hang on mt7986 and mt7981.
- Root cause: reserved WDMA buffer too small for v2 hardware.
- Source: MediaTek OpenWrt GPL feeds (`Fix WED 2.0 WDMA TX hang issue`).
- Version info: v2 only (mt7986/mt7981). Rex Lu later said v3 dropped
this register.
**Step 1.4 — Hidden bug fix?**
Record: Not hidden. Explicit hang fix / hardware workaround. Vendor
subject was also a hang fix.
## Phase 2: Diff analysis
**Step 2.1 — Inventory**
Record:
- `drivers/net/ethernet/mediatek/mtk_wed.c`: +5 in
`mtk_wed_dma_enable()`
- `drivers/net/ethernet/mediatek/mtk_wed_regs.h`: +1
(`MTK_WDMA_GLO_CFG_RESV_BUFF` = `GENMASK(23, 16)`)
- Scope: single-driver surgical change (~6 lines)
**Step 2.2 — Flow change**
Record: After the existing v1/v2 vs v3 `MTK_WDMA_GLO_CFG` setup, and
before enabling WED GLO DMA, v2 now RMWs bits 23:16 of
`MTK_WDMA_GLO_CFG` to `0x80`. v1 and v3 are unchanged. Init/enable path
only.
**Step 2.3 — Bug mechanism**
Record: Hardware workaround. Default `RESV_BUFF` is `0x40`; raising it
to `0x80` leaves more WDMA reserved buffer so CDM TX FIFO does not
overflow and stall TX. `wdma_m32()` already does `(read & ~mask) | val`.
**Step 2.4 — Fix quality**
Record: Minimal, gated by `mtk_wed_is_v2()`, uses existing helpers.
Regression risk is low: only WED v2 register bits 23:16. MediaTek
confirmed v3 must not get this write because the field was removed.
## Phase 3: Git history
**Step 3.1 — Blame**
Record: `mtk_wed_dma_enable()` came from `cf26df8833cc` (2022-09-22,
“add mtk_wed_configure_irq and mtk_wed_dma_{enable/disable}”).
Surrounding v2/v3 split is `e2f64db13aa1d` (2023-09-18, MT7988 WED). The
missing programming is a long-standing omission, not new 6.12 code.
**Step 3.2 — Fixes: tag**
Record: Committed patch has none. Simon Horman and Lorenzo Bianconi both
named `cf26df8833cc`. That commit **is** an ancestor of this tree (`git
merge-base --is-ancestor` succeeded).
**Step 3.3 — File history**
Record: Recent 6.12 `mtk_wed.c` work is leaks/UAF/reset, not RESV_BUFF.
No competing local fix. Standalone, not patch N/M.
**Step 3.4 — Author**
Record: Shiji Yang has no other `drivers/net/ethernet/mediatek/` commits
in this tree. Original vendor author is Rex Lu (MediaTek). Lorenzo
(maintainer) acked.
**Step 3.5 — Prerequisites**
Record: Needs `mtk_wed_is_v2()`, `wdma_m32()`, `FIELD_PREP`,
`MTK_WDMA_GLO_CFG`. All present. Upstream SHA `c0ef04232f9f` is **not**
in this tree (ancestor check failed). Can apply standalone.
## Phase 4: Mailing list / external
**Step 4.1 — Original discussion**
Record:
- `b4 dig -c c0ef04232f9f` → https://patch.msgid.link/OSZPR01MB779537889
255E2F606E47EABBCA52@OSZPR01MB7795.jpnprd01.prod.outlook.com
- Single revision (v1). Applied as-is to netdev/net.git.
- Simon: target `net`, **CC stable**, add Fixes; still `Reviewed-by`.
- Jakub asked Lorenzo to review, including the Fixes suggestion.
- Lorenzo asked if v3 needs it; Rex Lu: v2 only, v3 removed the
register.
- Lorenzo: `Acked-by`.
- No NAK.
**Step 4.2 — Recipients (`b4 dig -w`)**
Record: netdev, linux-mediatek, Lorenzo Bianconi, Felix Fietkau, Rex Lu,
netdev maintainers. Right people.
**Step 4.3 — Bug report**
Record: Vendor feeds commit `07c87502e854`: “Fix WED 2.0 WDMA TX hang
issue” / “without this patch mt7986 and mt7981 may have WDMA TX hang”.
Same 6-line change. No public syzbot/bugzilla thread found.
**Step 4.4 — Series**
Record: Standalone 1/1. No companion patches.
**Step 4.5 — Stable list**
Record: UNVERIFIED for lore.kernel.org/stable (HTTP 403). Simon’s review
is an explicit stable nomination. No evidence of a stable NAK.
## Phase 5: Semantic / call graph
**Step 5.1 — Functions**
Record: `mtk_wed_dma_enable()` plus a register `#define`.
**Step 5.2 — Callers**
Record: Only `mtk_wed_start()` → `mtk_wed_dma_enable()`. That is
`wed_ops.start`, invoked as `mtk_wed_device_start()` from mt76
(`mt7915/dma.c`, `mt7996/dma.c`). mt7915 is the usual WiFi partner for
mt7986/mt7981 (WED v2).
**Step 5.3 — Callees**
Record: `wdma_m32` → `wdma_r32`/`wdma_w32` (MMIO RMW). No alloc, no
locks.
**Step 5.4 — Reachability**
Record: Yes. Bringing WiFi up with WED offload on Filogic 820/830 hits
this. Not a rare debug path.
**Step 5.5 — Similar patterns**
Record: No other `RESV_BUFF` users. Nearby `wdma_set(MTK_WDMA_GLO_CFG,
...)` already programs this register for TX enable / prerest bits.
## Phase 6: This tree (6.12.111)
**Step 6.1 — Buggy code present?**
Record: Yes. `make kernelversion` / `git describe` → **6.12.111**
(`p-6.12-3-gc504cf0baa3d8`). `mtk_wed_dma_enable()` has the v2/v3 split
and does **not** set RESV_BUFF. `mt7981_data` / `mt7986_data` have
`.version = 2`. `hw->version = eth->soc->version`. Bug dates to WED
dma_enable (2022), well before 6.12.
**Step 6.2 — Backport difficulty**
Record: Clean apply. Insertion context around the v3 `else` branch and
`MTK_WDMA_GLO_CFG` bit defines matches the upstream hunk.
**Step 6.3 — Already fixed?**
Record: No. `git log --grep=RESV_BUFF` / `WDMA TX hang` / `increase WED
v2` on these files is empty. `c0ef04232f9f` is not an ancestor.
## Phase 7: Subsystem
**Step 7.1**
Record: MediaTek ethernet WED (WiFi/Ethernet dispatch offload).
**IMPORTANT** for Filogic routers, not core-mm/VFS. OpenWrt 6.12 devices
(BPI-R3, GL-MT6000, many AX3000/AX6000 boards) use this.
**Step 7.2**
Record: Mature driver; v2 support is years old. This is a vendor hang
workaround, not churn from a new API.
## Phase 8: Impact / risk
**Step 8.1 — Who**
Record: Driver/SoC specific: `CONFIG_NET_MEDIATEK_SOC` + WED,
mt7981/mt7986, WiFi WED offload.
**Step 8.2 — Trigger**
Record: WED DMA enable when WiFi starts. Can be common on those APs.
Unprivileged users do not poke the register; they can trigger it by
using the AP. Timing-dependent FIFO overflow under TX load is plausible.
**Step 8.3 — Severity**
Record: WDMA TX hang → WiFi/Ethernet TX stall. **HIGH** (hang / loss of
forwarding). Not a KASAN UAF, but a real production hang on shipping
routers.
**Step 8.4 — Benefit vs risk**
Record: Benefit high for Filogic v2 users (fixes hang). Risk very low (6
lines, v2-only, maintainer + MediaTek + netdev review). Ratio strongly
favors backport.
## Phase 9: Synthesis
**For backporting**
- Real hang: CDM TX FIFO overflow → WDMA TX stall on mt7986/mt7981.
- Hardware quirk/workaround (stable exception).
- Tiny, obviously correct RMW of a documented field.
- Code and helpers exist in 6.12; applies cleanly.
- Simon Horman asked for `Cc: stable`; Lorenzo acked; MediaTek confirmed
v2-only.
- Vendor already shipped the same change in OpenWrt feeds.
**Against**
- No kernel.org `Reported-by` / crash log (vendor description only).
- Affects one SoC family, not all machines.
- Committed patch omitted `Fixes:` / `Cc: stable` (Simon noted that; not
a technical reason to skip).
**Unresolved**
- Could not query lore.kernel.org/stable (403). No sign it was rejected.
**Stable rules**
1. Obviously correct and tested — **PASS** (review + vendor feeds +
MediaTek confirmation)
2. Real user bug — **PASS** (TX hang)
3. Important (hang) — **PASS** (HIGH)
4. Small/contained — **PASS** (6 lines, two files)
5. No new features/APIs — **PASS** (register programming)
6. Applies to this tree — **PASS** (clean context on 6.12.111)
**Exception:** hardware workaround / quirk.
This should be backported to this 6.12.y tree.
Verification:
- [Phase 1] Parsed tags: vendor GitHub link, Reviewed-by Horman, Acked-
by Bianconi, applied by Kicinski; no Fixes/Cc: stable on the commit
- [Phase 2] Diff: +5 in `mtk_wed_dma_enable()`, +1 GENMASK; v2-only
`wdma_m32` of bits 23:16 to 0x80
- [Phase 2] `wdma_m32()` in this tree is `(read & ~mask) | val` at
`mtk_wed.c:111-114`
- [Phase 3] Blame: `mtk_wed_dma_enable` from `cf26df8833cc`
(2022-09-22); v2/v3 split from `e2f64db13aa1d`
- [Phase 3] `git merge-base --is-ancestor cf26df8833cc HEAD` succeeded;
`c0ef04232f9f` is not in this tree
- [Phase 3] `mtk_wed_is_v2` from `d274d523c71c82` is in this tree
- [Phase 4] `b4 dig -c c0ef04232f9f`: v1 only; lore msgid
OSZPR01MB7795...
- [Phase 4] Horman review (openwall 2026/08/21/128): “Be CCed to
stable”, Fixes `cf26df8833cc`, Reviewed-by
- [Phase 4] Rex Lu: v2 only; v3 removed RESV_BUFF
- [Phase 4] Lorenzo Acked-by after that confirmation
- [Phase 4] GitHub `07c87502e854`: same 6-line hang fix
- [Phase 4] lore.kernel.org/stable: UNVERIFIED (403)
- [Phase 5] Caller: `mtk_wed_start` → `mtk_wed_dma_enable`;
`mtk_wed_device_start` from mt76 mt7915/mt7996
- [Phase 6] `git describe HEAD` / `make kernelversion`: 6.12.111;
RESV_BUFF define absent; insertion context matches
- [Phase 6] `mt7981_data`/`mt7986_data`.version = 2; `hw->version =
eth->soc->version`
- [Phase 8] Failure mode: WDMA TX hang, severity HIGH
- UNVERIFIED: whether anyone already requested this on stable@ (lore
403)
The hang is real on WED v2 hardware this tree supports, the fix is a
six-line register quirk, and it applies cleanly here.
**YES**
drivers/net/ethernet/mediatek/mtk_wed.c | 5 +++++
drivers/net/ethernet/mediatek/mtk_wed_regs.h | 1 +
2 files changed, 6 insertions(+)
diff --git a/drivers/net/ethernet/mediatek/mtk_wed.c b/drivers/net/ethernet/mediatek/mtk_wed.c
index 1ed1f88dd7f8b..fdb487e5d46e1 100644
--- a/drivers/net/ethernet/mediatek/mtk_wed.c
+++ b/drivers/net/ethernet/mediatek/mtk_wed.c
@@ -2071,6 +2071,11 @@ mtk_wed_dma_enable(struct mtk_wed_device *dev)
wdma_set(dev, MTK_WDMA_GLO_CFG, MTK_WDMA_GLO_CFG_TX_DMA_EN);
}
+ if (mtk_wed_is_v2(dev->hw))
+ wdma_m32(dev, MTK_WDMA_GLO_CFG,
+ MTK_WDMA_GLO_CFG_RESV_BUFF,
+ FIELD_PREP(MTK_WDMA_GLO_CFG_RESV_BUFF, 0x80));
+
wed_set(dev, MTK_WED_GLO_CFG,
MTK_WED_GLO_CFG_TX_DMA_EN |
MTK_WED_GLO_CFG_RX_DMA_EN);
diff --git a/drivers/net/ethernet/mediatek/mtk_wed_regs.h b/drivers/net/ethernet/mediatek/mtk_wed_regs.h
index c71190924816c..e5f83100ded52 100644
--- a/drivers/net/ethernet/mediatek/mtk_wed_regs.h
+++ b/drivers/net/ethernet/mediatek/mtk_wed_regs.h
@@ -433,6 +433,7 @@ struct mtk_wdma_desc {
#define MTK_WDMA_GLO_CFG_TX_DMA_BUSY BIT(1)
#define MTK_WDMA_GLO_CFG_RX_DMA_EN BIT(2)
#define MTK_WDMA_GLO_CFG_RX_DMA_BUSY BIT(3)
+#define MTK_WDMA_GLO_CFG_RESV_BUFF GENMASK(23, 16)
#define MTK_WDMA_GLO_CFG_RX_INFO3_PRERES BIT(26)
#define MTK_WDMA_GLO_CFG_RX_INFO2_PRERES BIT(27)
#define MTK_WDMA_GLO_CFG_RX_INFO1_PRERES BIT(28)
--
2.53.0
^ permalink raw reply related [flat|nested] 32+ messages in thread