CVE-2026-22979 Linux GRO fraglist memory leak in skb_segment_list

  • Thread Author
Neon diagram illustrating Linux kernel fragment handling and upstream fix (skb).
A recently assigned CVE, CVE-2026-22979, fixes a subtle but operationally meaningful memory-leak in the Linux network stack where skb_segment_list() mishandles socket memory accounting for GRO-aggregated packets, a bug that can leave per-socket memory counters non-zero and prevent sockets from ever being destroyed — producing a persistent kernel memory leak and a local-denial-of-service vector until the kernel is patched.

Background​

The Linux networking stack relies on a set of tightly coupled abstractions — sk_buffs (SKBs), offload mechanisms such as Generic Receive Offload (GRO) and Generic Segmentation Offload (GSO), and per-socket memory accounting — to achieve high throughput while keeping overhead low. SKBs encapsulate packet buffers and metadata; GRO aggregates multiple received packets into a larger logical packet to reduce per-packet processing; later in the receive/forwarding path, those aggregated packets can be segmented for forwarding or transmission. The functions that perform segmentation (including skb_segment_list()) historically had to be careful about who “owns” the memory charge for each SKB and whether individual fragments carry their own socket pointer (skb->sk).
That ownership model changed in 2024 when a commit (commonly referenced by its short hash ed4cccef64c1) altered how fraglist entries created by GRO are treated: they were explicitly orphaned (skb->sk = NULL) to avoid illegal orphaning later in the stack. That change left the socket memory charge on the head SKB rather than distributed across fragments, and the downstream accounting in skb_segment_list() was not updated to match — producing the leak corrected by CVE-2026-22979.

What exactly went wrong: technical root cause​

SKB ownership, truesize, and sk_wmem_alloc — a quick primer​

  • Each SKB has a truesize value that represents the kernel memory charged for that buffer.
  • Socket memory accounting (sk_wmem_alloc and related counters) tracks how much kernel memory a given socket is charged for; when per-SKB ownership is transferred, the code must adjust these counters accordingly.
  • When GRO builds fraglist/GSO chains, it can create a head SKB with a chain of fragment SKBs; historically each fragment could retain skb->sk and therefore be considered charged to the socket independently.

The mismatch introduced by the earlier change​

  • A 2024 fix (ed4cccef64c1) changed the fraglist/GRO ownership pattern so that fragments are orphaned — they do not retain skb->sk and therefore are not charged to the socket.
  • However, skb_segment_list() still executed logic that assumed fragments carried their own socket charge: it accumulated each fragment’s truesize into delta_truesize and subtracted that amount from the parent SKB when splitting/segmenting.
  • Because the fragments were no longer charged to the socket, subtracting their truesize from the head produced an under-count in the head’s accounting; later, when the head was freed, sk_wmem_alloc could remain non-zero.
  • The net effect: socket destruction could be blocked because the kernel believed the socket still had outstanding memory charged to it even though the real references had been orphaned — leaving leaked kernel memory permanently attributed to a now-dead or unreachable socket.

Observable symptom: kmemleak / unreferenced objects​

The leak was visible using the kernel’s kmemleak detector as “unreferenced object” entries tied to networking operations (for example, during teardown of networking environments or repeated packet-forwarding workloads). The kernel’s debug infrastructure reported unreferenced SKB-sized allocations and backtraces pointing to socket creation/allocation paths — a tell-tale sign of misstated memory accounting rather than a classic double-free or null-dereference crash.

The fix applied upstream​

The upstream resolution for CVE-2026-22979 is surgical and conservative: because skb_segment_list() is only reached for SKB_GSO_FRAGLIST packets that were constructed by GRO, the patch removes the truesize adjustment logic in that code path while preserving necessary cleanup calls (notably, the call to skb_release_head_state()) that correctly drop references to SKB extensions and maintain head-state correctness. In short: stop subtracting fragment truesize from the parent for GRO-constructed fraglist chains, but keep the head-state release in place.
Upstream commit notes and the kernel discussion make two things clear:
  • This is a logic/ownership mismatch — not a fundamental design flaw — and the proper fix is to adjust the accounting to match the updated ownership semantics introduced by the earlier GRO ownership transfer change.
  • The fix intentionally avoids touching broader segmentation machinery unless necessary, reducing the risk of regressions in heavily optimized packet-path code.

Impact assessment: who should care and why​

Severity and scoring​

Public advisories list this defect with a moderatesness/medium severity profile. One common score reported is CVSS v3.1 Base Score 5.5 (AV:L/AC:L/PR:L/UI:N/S:U/C:N/I:N/A:H), reflecting a local-only attack vector with high availability impact (denial-of-service potential) but no confidentiality or integrity breach. The leak does not directly lead to arbitrary code execution but can produce persistent kernel memory exhaustion in the presence of a repeated local trigger.

Attack surface and exploitation feasibility​

  • The bug is local: an attacker must be able to generate or cause sustained GRO-aggregated fraglist traffic that is later segmented by skb_segment_list() during forwarding or packet handling.
  • The most exposed hosts are routers, gateways, or systems that forward packets and maintain high packet churn with GRO enabled — environments where fraglist/GRO interactions are common.
  • There is no clear public exploit proof-of-concept or known in-the-wild exploitation at the time of publication; the likely worst-case scenario is a local user or compromised container/process that repeatedly triggers forwarding behavior, slowly leaking kernel memory until sockets become unsalvageably charged and resources are exhausted. This weaponizes availability rather than confidentiality or integrity.

Real-world consequences​

  • For single-host boxes the leak might be small and only noticeable during heavy forwarding or teardown operations, but for high-scale routers, edge appliances, or network appliances (including cloud host virtual routers), the leak can accumulate and eventually cause service outages or require reboots.
  • Because some environments (cloud or multi-tenant hosts) rely on fine-grained socket lifecycle control, leaked sk_wmem_alloc counters can complicate resource reclamation and automated orchestration: sockets that appear still in use block resource cleanup. The operational burden includes increased monitoring and potentially unexpected reboots.

Detection and diagnostics​

Use kmemleak to spot leaking allocations​

Kmemleak — the kernel memory-leak detector — will list suspected unreferenced objects under /sys/kernel/debug/kmemleak if the kernel was built with CONFIG_DEBUG_KMEMLEAK and debugfs is mounted. When the skb_segment_list leak is triggered, kmemleak output commonly includes “unreferenced object” entries with sizes consistent with SKB allocations and backtraces associated with socket and SKB allocation call paths. Operators should:
  1. Ensure debugfs is mounted: mount -t debugfs nodev /sys/kernel/debug.
  2. Trigger a scan or examine current results: cat /sys/kernel/debug/kmemleak; echo scan > /sys/kernel/debug/kmemleak.
  3. Use the kmemleak “dump” capability to inspect specific addresses if necessary.
Note: enabling KMEMLEAK in production kernels has overhead and, in some cases, is not feasible. However, it is a highly useful troubleshooting tool in lab or staging environments to confirm the presence of this class of leak.

Watch socket memory counters​

Inspecting per-socket memory counters and kernel logs can highlight anomalous sk_wmem_alloc values that never return to zero after socket teardown. Logging and telemetry that capture socket lifecycle metrics (or kernel memory counters exposed by telemetry agents) will help detect the slow accumulation symptomatic of this bug. The patch’s upstream discussion explicitly references sk_wmem_alloc remaining non-zero as the failure mode.

Mitigation and remediation​

1) Apply vendor or upstream kernel patches (primary fix)​

The authoritative remediation is to update to a kernel that includes the upstream fix for CVE-2026-22979. Vendors and distributions have begun mapping and backporting the fix; operators should prioritize patching systems that:
  • Perform packet forwarding or routing,
  • Run workloads that rely heavily on GRO/GSO (virtual routers, gateways, NFV appliances),
  • Host multi-tenant or untrusted containers/users where a local process could repeatedly trigger the problematic code path.
Upstream commit notes and vendor advisories document the fix: remove truesize adjustment in skb_segment_list for GRO-produced fraglist chains, while preserving skb_release_head_state semantics.

2) Short-term operational mitigation: consider disabling GRO where appropriate​

If patching is not immediately possible, a practical mitigation is to disable Generic Receive Offload (GRO) on affected interfaces — especially on hosts that only need to process simple traffic or where TCP performance is not critical. Disabling GRO prevents the GRO-to-segmentation path from being exercised and therefore removes the immediate trigger for this specific leak.
Common, widely used commands and mechanisms to disable GRO safely in production include:
  • Using ethtool to disable GRO on an interface: ethtool -K <iface> gro off.
  • For persistent network-manager controlled profiles: nmcli connection modify <profile> ethtool.feature-gro off and reactivate the profile.
  • For systemd-networkd or init scripts: place an ethtool -K ... gro off in the interface-up hooks or in a startup dispatcher so the setting survives reboots.
A cautionary note: disabling GRO reduces TCP throughput and can have significant performance impact on heavy TCP servers; only apply this as an emergency mitigation on hosts whose throughput profile tolerates it. See vendor documentation and test for regression.

3) Monitoring and operational hygiene​

  • Enable or periodically use kmemleak in staging to validate whether a given workload triggers the leak and to verify remediation efficacy.
  • Monitor socket memory metrics and kernel memory utilization; include alerts for unusual non-transient sk_wmem_alloc counts and anomalously high numbers of unreferenced objects if debug artifacts are accessible.
  • Where possible, move high-risk forwarding workloads to patched hosts or appliances with vendor-verified kernels while you roll patches across your fleet.

Risk analysis and likely false positives​

Strengths of the fix​

  • The upstream patch is narrowly scoped and addresses the precise mismatch between fragment ownership and memory-accounting logic. By restricting changes to the GRO-specific path and leaving broader segmentation code alone, the maintainers reduce the chance of regression in the performance-sensitive packet-path.
  • The root cause is well-understood and reproducible in kmemleak output — the failure mode (sk_wmem_alloc left non-zero) is concrete and consistently described in public advisories.

Limitations and residual risk​

  • The vulnerability requires local access or a privileged process capable of generating forwarding/GRO traffic patterns, so its exploitation profile is limited compared to remote, unauthenticated kernel bugs. However, in multi-tenant or containerized environments, local triggers are realistic.
  • Disabling GRO as a short-term mitigation can materially reduce TCP performance; operators must weigh availability/DoS risk against throughput degradation.
  • As with any change in the core networking codepath, there is a small risk that backports or local patches could introduce regressions. Administrators should test patched kernels in canary or staging environments before broad rollout.

Practical checklist for administrators​

  1. Inventory: Identify hosts that act as routers, gateways, NAT boxes, or otherwise forward packets at scale. These have the highest exposure.
  2. Vendor advisories: Check your distribution and vendor advisories for patches and backports that contain the fix for CVE-2026-22979 and schedule upgrades accordingly. Prioritize network-path hosts.
  3. Short-term mitigation: If you cannot immediately patch, and if the host’s workload tolerates it, disable GRO temporarily on in-scope interfaces (ethtool -K <iface> gro off). Test performance impact before wide deployment.
  4. Monitoring: Enable kmemleak on test hosts or in a staging environment and monitor /sys/kernel/debug/kmemleak for “unreferenced object” traces tied to socket creation paths; instrument socket memory counters in telemetry.
  5. Post-patch validation: After applying vendor or upstream patches, re-run kmemleak scans and workload tests to confirm the issue is resolved and that no new regressions appear.
  6. Documentation and runbooks: Update operational runbooks to include this CVE, mitigation steps, and rollback instructions for any GRO changes you make.

Broader context: why this class of bug matters​

Memory-accounting errors in kernel subsystems are a frequent source of operational impact because they can quietly accumulate without immediate, loud failure modes like crashes. The networking stack is particularly sensitive: it's highly optimized, routinely relies on offload and aggregation features to scale, and often runs in multi-tenant environments where a single misbehaving process can produce prolonged pressure on shared kernel resources.
This CVE exemplifies how a seemingly straightforward ownership semantics change — orphaning fraglist entries to prevent illegal orphaning — can ripple through and require complementary accounting changes elsewhere. The maintainers’ conservative, surgical patch approach and the clear diagnostic signal (kmemleak) reduce the long-term risk, but the short-term operational burden for those running unpatched network-forwarding hosts remains material.

Community signals and chatter​

Community monitoring and forum activity tracking show active attention to related kernel CVEs and patches for similar memory-leak or socket lifecycle issues; operators and kernel maintainers continue to discuss the balance between correctness and risk of regressions when changing packet-path ownership semantics. These community signals suggest that the wider ecosystem is cognizant of the issue and treating it as an operational priority where forwarding workloads are involved.

Conclusion​

CVE-2026-22979 is a clear, fixable example of a memory-accounting mismatch introduced by a prior behavior change in the GRO/fraglist ownership model. The vulnerability’s impact is availability-focused and local in nature, but it matters for routers, gateways, and forwarding hosts — especially in multi-tenant or high-throughput environments. The upstream fix is targeted and sensible: remove an incorrect truesize adjustment for GRO-created fraglist packets while preserving head-state cleanup. Operators should prioritize vendor kernel updates, use kmemleak and socket-memory telemetry to detect active leaks, and, as a temporary stop-gap, consider disabling GRO on critical interfaces only where the performance trade-off is acceptable.

Source: MSRC Security Update Guide - Microsoft Security Response Center
 

Back
Top