From: Eric Dumazet edumazet@google.com
[ Upstream commit be1b70ab21cbe8f8bb8ecf39eb34880882252773 ]
Qdisc use shinfo->gso_segs for their pkts stats in bstats_update(), but this field needs to be initialized for SKB_GSO_DODGY users.
Signed-off-by: Eric Dumazet edumazet@google.com Link: https://patch.msgid.link/20251121083256.674562-3-edumazet@google.com Signed-off-by: Paolo Abeni pabeni@redhat.com Signed-off-by: Sasha Levin sashal@kernel.org ---
LLM Generated explanations, may be completely bogus:
## Complete Analysis Summary
### What the Commit Fixes
This commit fixes an **incorrect qdisc packet statistics** bug for `SKB_GSO_DODGY` packets (traffic from untrusted sources like VMs, containers via virtio_net, veth, etc.).
**Technical mechanism of the bug:** 1. `qdisc_pkt_len_init()` is called from `__dev_queue_xmit()` to calculate accurate packet lengths for qdisc statistics 2. For `SKB_GSO_DODGY` packets, the function correctly recalculates `gso_segs` because the original value is untrusted 3. However, this recalculated value was only used locally for `pkt_len` calculation and **not stored back** to `shinfo->gso_segs` 4. Later, `bstats_update()` reads `skb_shinfo(skb)->gso_segs` directly, getting the **wrong** (original untrusted) value 5. This results in incorrect packet counts in qdisc statistics (`tc -s qdisc show`)
### Code Change Analysis
The fix is minimal: - **Line 1:** Changed `const struct skb_shared_info *shinfo` to `struct skb_shared_info *shinfo` (removes const to allow writing) - **Line 2:** Added `shinfo->gso_segs = gso_segs;` after calculating the correct value
The fix simply stores the already-calculated value back where `bstats_update()` will read it.
### Stable Kernel Criteria Evaluation
| Criterion | Assessment | |-----------|------------| | Obviously correct | ✅ YES - The calculation already exists; this just stores the result | | Fixes real bug | ✅ YES - Incorrect statistics for VM/container traffic | | Small and contained | ✅ YES - 2 lines changed in 1 file | | No new features | ✅ YES - Bug fix only | | Tested | ✅ YES - Accepted by netdev maintainer |
### User Impact
- **Affected users:** Anyone using qdisc (traffic shaping, rate limiting) with virtualized or containerized workloads - **Severity:** Medium - incorrect statistics, not a crash/corruption - **Common scenarios:** VMs (virtio_net), containers (veth), any traffic marked `SKB_GSO_DODGY`
### Risk Assessment
- **Risk:** LOW - The fix is trivial and the calculation logic is already proven - **Dependency:** None - self-contained fix - **Backport complexity:** Should apply cleanly to any stable tree with this code
### Concerns
1. **No `Cc: stable@vger.kernel.org`** - Maintainer didn't explicitly request 2. **No `Fixes:` tag** - Bug likely dates to 2013 (commit 1def9238d4aa)
However, Eric Dumazet is a prolific netdev maintainer who sometimes doesn't add Cc: stable for straightforward fixes. The fix's correctness is self-evident.
### Conclusion
This is a small, obvious, low-risk bug fix that corrects packet statistics for common virtualized/containerized workloads. It meets all stable kernel criteria: it fixes a real bug affecting users, is small and self-contained, and introduces no new features. The risk of regression is minimal since the fix only stores an already-computed value.
**YES**
net/core/dev.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/net/core/dev.c b/net/core/dev.c index 2acfa44927daa..16cbba09b9627 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -4063,7 +4063,7 @@ EXPORT_SYMBOL_GPL(validate_xmit_skb_list);
static void qdisc_pkt_len_init(struct sk_buff *skb) { - const struct skb_shared_info *shinfo = skb_shinfo(skb); + struct skb_shared_info *shinfo = skb_shinfo(skb);
qdisc_skb_cb(skb)->pkt_len = skb->len;
@@ -4104,6 +4104,7 @@ static void qdisc_pkt_len_init(struct sk_buff *skb) if (payload <= 0) return; gso_segs = DIV_ROUND_UP(payload, shinfo->gso_size); + shinfo->gso_segs = gso_segs; } qdisc_skb_cb(skb)->pkt_len += (gso_segs - 1) * hdr_len; }