Vulnerabilities

How a Linux IPv6 fraggap Accounting Error Causes an Out-of-Bounds Write (CVE-2026-53362)

CVE-2026-53362 is a kernel out-of-bounds write in the local UDPv6 splice send path; permanent remediation requires installing the applicable fix, booting that kernel, and accepting it with package identity, running-kernel evidence, boot ID, and a behavioral regression.

Hand-drawn technical cover comparing a vulnerable IPv6 packet build that omits linear carry capacity and writes into skb_shared_info with a fixed build that reserves the carried range.
In this article

Research basisReview of the Linux CNA JSON, pinned upstream and stable Git objects for 773ba4fe9104, ce650a166335, and 736b380e28d0, the matching source tree, RFC 8200, five stable backports, and Red Hat, Ubuntu, Debian, SUSE, and NVD status through Jul 27, 2026

SourceLinux CNA JSON, immutable upstream and stable Git objects, Linux source and networking documentation, RFC 8200, vendor trackers, and SOSEC source review

1 Closure starts with a fixed kernel in service: the flaw is in the local UDPv6 splice send path

CVE-2026-53362 lets a local process assembling a UDPv6 datagram copy fragment carry bytes from an old skb across the new skb's linear boundary and into skb_shared_info. The Linux CNA names 6.1.177, 6.6.144, 6.12.95, 6.18.38, 7.1.3, and 7.2-rc1 as first-fixed upstream releases. Distribution kernels require an exact vendor advisory and package-lineage decision. Installing a fixed package is a rollout event; permanent closure requires that kernel to be running and to pass behavioral acceptance.

The entrance combines local splice(), an ordinary IPv6 UDP socket, a continuing UDP cork, scatter-gather capability, and specific fragment geometry. Red Hat reports that its researchers developed the corruption into arbitrary kernel read and write, credential overwrite, SELinux bypass, and container escape. Hosts that run a shared kernel for untrusted tenants therefore belong in the first remediation wave. An Internet-edge firewall cannot cover this pre-transmission, local construction path.

The patch and pinned source independently establish the out-of-bounds write and its destination structure. The stronger escalation chain is Red Hat's published research result. The analysis below keeps those evidence layers distinct while reconstructing the memory layout, version history, and repair invariant from immutable Git objects.

1.1 Source anchors and response ticket: the paged branch in __ip6_append_data() under-reserved fraggap

The defect is in net/ipv6/ip6_output.c::__ip6_append_data(). In the parent of the fix, lines 1664–1672 leave fraggap in pagedlen without adding it to alloclen. In commit 736b380e28d0, lines 1664–1672 move that length into the linear reservation.

The old branch then permits the splice-page negative-copy state at lines 1683–1692; the fixed source removes the exception at lines 1683–1689. The vulnerable skb_put(), carry copy, and source-skb trim appear together at lines 1717–1733; the repaired sequence is at lines 1714–1730. Together, these three comparisons bind allocation, guard, write, and old-object trim to one reviewable path.

Permanent repair

Object
The kernel running on the host and the exact distribution source package or pinned source commit used to build it.
Action
Apply the applicable vendor-fixed package. Builds that follow upstream stable trees exactly use 6.1.177, 6.6.144, 6.12.95, 6.18.38, 7.1.3, or 7.2-rc1 as their branch-specific floor. Reboot, or use a vendor-supported live patch proven to cover this function change.
Owners
The kernel or platform team owns package, boot, and fallback; the workload owner accepts canary behavior.

Temporary controls and cost

Controls
Restrict unprivileged user namespaces, IPv6 socket access, or splice permission where the environment allows. On a tested route, removing effective NETIF_F_SG can keep traffic out of the paged branch.
Costs
Namespace restrictions disrupt rootless containers and sandboxes; path controls can break applications; removing scatter-gather increases copies, CPU use, and latency while reducing throughput.
Enforcement
The ticket records an owner, host pool, validation method, and absolute due date. Offload and path controls require continuous enforcement and drift monitoring until the fixed kernel boots.

Acceptance evidence

Identity
Retain the vendor advisory or source proof, installed package, repository and signature, running-kernel identifier, boot ID, boot time, and node image.
Behavior
An isolated instrumented test observes linear reservation increase by fraggap, paged ownership decrease by the same amount, and the carry high-water mark remain before skb_shinfo(), with clean KASAN, normal UDPv6 transmission, and negative controls.
Closure gate
Source, installation, runtime, and behavior all indicate the fixed state. Container nodes also show that untrusted workloads returned only to accepted hosts.

Fallback and removal

Fallback
If the candidate fixed kernel causes a service regression, drain the node and boot another known-good fixed build. Keep temporary controls and isolation active while no accepted fixed build is available.
Removal
After fixed-boot identity, boot ID, behavioral regression, and scheduler evidence pass, remove offload, namespace, or path restrictions and verify restored throughput and rootless workflows.
Fallback boundary
Production fallback accepts another known-good fixed build; a known vulnerable kernel reopens the flaw.

1.2 Start with the skb's linear head, nonlinear frags, and skb_shared_info

struct sk_buff holds packet-management state and points to a contiguous head buffer. Bytes from data through tail are current linear content; the interval from tail to end is available capacity. Bulk payload can remain in external pages described by nonlinear frags. Immediately after the head buffer's end, skb_shared_info records fragment counts, offload state, and page descriptors. It is control metadata, outside the packet bytes.

When a fragment boundary falls inside the old skb, the bytes after that boundary form fraggap. The function reads them from the old skb, copies them into the new skb's linear head, and then trims the old skb. If the new object reserves only its headers, the first carry byte beyond that reservation reaches the following skb_shared_info.

[sender] -> [old skb] -> [carry: fraggap] -> [new skb linear head] -> |end| [skb_shared_info]
Five-object map. The sender influences content and length; the old skb supplies the carry; the new skb must reserve linear space for it; shared control metadata begins immediately after end.

1.3 splice_to_socket() creates the internal flag; MSG_MORE keeps one datagram open

The entry starts in fs/splice.c::splice_to_socket(). It walks pipe buffers, takes page references, and describes page, offset, and length in a local bio_vec array of at most sixteen entries per pass. Sixteen bounds one batch. The loop can continue consuming data on the same socket and UDP cork, allowing the logical datagram to grow beyond the path MTU.

The function builds a kernel msghdr and sets MSG_SPLICE_PAGES. The userspace-facing sendmsg() entry clears internal flags, so an application cannot inject the same bit directly. An ordinary splice() operation selects the route; splice_to_socket() then calls sock_sendmsg(), which dispatches to udpv6_sendmsg() for UDPv6 protocol handling. When the call supplies SPLICE_F_MORE or more pipe buffers remain, the kernel also sets MSG_MORE and keeps the same logical datagram open.

Reachability requires local execution and an ordinary IPv6 UDP socket; it does not require a raw socket. Seccomp, LSM, namespace policy, and IPv6 configuration can change exposure. Containers share the host kernel, so asset classification belongs to the node's booted kernel. Incident correlation should retain pipe and socket descriptors, process and namespace identity, actual route device, MTU, and effective interface features.

Hand-drawn splice entry in which pipe pages are organized into bounded bio_vec batches, the kernel adds an internal message flag, and the result enters the UDPv6 socket send path.
Figure 1. The pipe supplies referenced pages, splice_to_socket() batches them and adds the internal flag, and MSG_MORE keeps later batches in the same UDPv6 datagram.

2 The cork turns several page batches into one datagram that no longer fits one frame

UDP is usually introduced as a one-call, one-datagram protocol. Corking lets a sender contribute bytes in several operations while the kernel retains route, header, and length state, then releases one completed datagram. Here MSG_MORE keeps that datagram open long enough for page-backed contributions to accumulate beyond the IPv6 path MTU and force the transition from one skb to a chain of fragments. The stored history also changes header ownership, so two submissions with identical byte counts can reach different allocation states depending on whether the socket queue was empty when each arrived.

udpv6_sendmsg() derives a local corkreq from either the socket's UDP cork option or the current message's MSG_MORE. That value governs whether the write should be completed immediately. The function also distinguishes an already pending datagram from a fresh one. When data is pending, it reuses state established by the first contribution instead of rebuilding every decision from the newest batch. The vulnerability later depends on exactly that continuity.

The first contribution carries the transport header burden. Once the write queue is no longer empty, ip6_append_data() sets transhdrlen to zero for continuation data because the UDP header has already been accounted for. This is the correct way for one logical datagram to avoid reserving a new transport header in every skb. Zero becomes revealing when the vulnerable page formula subtracts it while forgetting the carried fraggap.

A route supplies an effective MTU, and IPv6 output must decide how much of the logical datagram fits into each skb. Header chains, fragmentation policy, and device capabilities all participate. The page-backed bytes remain application payload and are now constrained by protocol geometry. The allocator needs two accurate numbers: bytes to reserve in the skb's linear head and bytes that page descriptors can represent without copying.

The memory error requires accumulated length to cross the multi-skb fragment threshold. A small datagram creates no earlier skb tail to carry forward, so fraggap stays zero. The vulnerable formula is always present in source; triggering it requires continuation, fragmentation, and a nonzero carry to form the relevant state transition.

2.1 udpv6_sendmsg() preserves one logical write across several submissions

For a new datagram, udpv6_sendmsg() resolves destination, flow, route, and cork parameters before calling ip6_append_data(). For an existing pending frame, it enters the append path with a write queue that already owns the earlier bytes. These branches converge on the same lower function, but they supply different history. Reading only the final call signature hides whether the transport header and initial extension headers were consumed on a previous pass.

The pending state belongs to the socket, so concurrency and error handling matter operationally. UDP serializes the corked write path, tracks accumulated length, and tears down partial state when append or final push fails. A fully serialized sender can still trigger the flaw: once continuation, page, and fragment conditions coincide, linear capacity and paged ownership receive deterministic, contradictory classifications.

ip6_append_data() is a wrapper that normalizes the header contribution before it delegates to __ip6_append_data(). If the queue is empty, the wrapper forwards the transport-header length needed at the start of the datagram. If an skb is already queued, it passes zero. The inner routine therefore does not have to infer history from payload contents; the queue state has already encoded whether this skb begins or continues the transport message.

Every later skb in the continuation still contains IPv6 fragment headers and relevant extension-header material. The zero concerns transhdrlen; fragheaderlen remains nonzero. Linear allocation therefore covers headers while still being short by precisely the carried gap.

When the last contribution arrives without MSG_MORE, UDP eventually pushes the pending frames toward output. The corrupting copy has already occurred during append, before a driver or remote peer is involved. A later checksum, segmentation, free, or device handoff may be the first consumer to notice damaged metadata. That delay is why crash stacks can point downstream while the causal stack ran through udpv6_sendmsg() earlier.

Hand-drawn UDPv6 cork transition: the first contribution carries the transport header, later pipe-backed pages extend it, and the combined datagram is divided into several IPv6 fragments according to the path MTU.
Figure 2. Corking preserves one datagram across several page batches. The first contribution owns the UDP header; later contributions extend the same write until IPv6 output must create another skb.

2.2 Five gates keep the message on the specialized paged-allocation branch

Page-backed allocation in __ip6_append_data() requires five gates: useful payload length, a compatible header-inclusion mode, NETIF_F_SG on the output device, the normal ip_generic_getfrag copier, and the internal MSG_SPLICE_PAGES mark. Removing any gate can send the same logical bytes through a different, non-vulnerable allocation calculation.

NETIF_F_SG means the output path can describe data in scattered page fragments without forcing a contiguous copy into the skb head. Virtual and software devices can advertise the feature, regardless of which physical NIC eventually carries the frame. An exposure test should inspect the actual route's device features inside the relevant namespace, because changing route or interface can change which allocator branch executes.

The getfrag == ip_generic_getfrag test narrows the optimization to a known copy contract. Specialized tunnel or transform callbacks that inspect or synthesize bytes take another path. Reachability is the conjunction of socket history, flags, device abilities, helper identity, and fragment geometry.

On the ordinary non-paged branch, alloclen is set from fraglen. Because fraglen already reflects datalen, and datalen already includes fraggap, that branch reserves enough linear storage. The error is confined to the optimized split of a correct total: it assigns the carry to paged length even though the next operation copies those bytes into linear storage.

Only a nonzero carry is now missing. Pipe pages provide the specialized iterator, the kernel provides its provenance bit, corking provides a continuing datagram, the device permits scatter-gather, and the generic helper preserves the shortcut. IPv6 fragmentation next draws an aligned boundary through an skb that was already built. The small overhang beyond that boundary is the fraggap that exposes the classification error.

Feature observation belongs at the skb's selected output device. A management inventory's “primary NIC” is insufficient because routes inside containers often traverse veth, bridge, tunnel, or loopback devices whose NETIF_F_SG sets differ from the host's physical adapter. A route lookup tied to the test destination captures that reality. If offload features are changed during mitigation, verify the effective feature after stacking and software fallbacks, then measure application throughput because forcing linear copies can impose a material cost. Feature toggles can also be reset by reboot, driver reload, or orchestration, so a control that depends on them needs continuous enforcement and a short lifetime until the fixed kernel boots.

3 The old skb leaves an alignment-shaped strip that the new skb must carry

Fragmentation begins with a ruler that cannot cut at every byte. IPv6 expresses a non-final fragment's offset in eight-octet units, so payload boundaries have to respect that grid. The skb already assembled by the cork may extend past the newly selected cut line. Linux has to place those trailing bytes at the front of the next fragment's data, immediately after that fragment's linear headers. This carry bridges two skb layouts; the receiver still sees each datagram byte once, and correctness requires the old and new objects to exchange ownership without altering the datagram sequence.

The source names the overhang fraggap. It represents a temporary ownership transition during packet construction: bytes at the tail of the previous skb become bytes in the new skb, after which the old skb is shortened. Each byte appears once in the logical datagram while its storage location moves between kernel objects.

Page-backed application payload still produces a range that needs linear capacity. Headers enter the new skb head, and Linux copies fraggap into the same region. Input provenance describes the original representation; the impending write operation determines final storage class and required capacity.

The boundary is recalculated with the effective MTU and header lengths available at that point. ip6_append_data_mtu() helps account for route and fragmentation constraints, while the append loop derives a maxfraglen aligned to the protocol's unit. Changes in extension-header composition, route MTU, or existing skb length can therefore alter the carry even when the application submits the same byte count.

There is no universal “magic length.” A nonzero fraggap emerges from the relationship among the previous skb, aligned maximum, and current header geometry. A regression can sweep sizes around that relationship and assert the memory boundary directly. The source invariant is stronger than any single packet fixture: every copied carried byte requires one byte of linear reservation.

3.1 maxfraglen combines MTU, fragment-header space, and the RFC 8200 grid

The append loop obtains the working MTU through ip6_append_data_mtu() and subtracts header obligations before aligning the fragmentable portion. In the reviewed source, the central expression is ((mtu - fragheaderlen) & ~7) + fragheaderlen - sizeof(struct frag_hdr). The mask rounds a byte count down to an eight-byte boundary; the additions and subtraction return from that aligned payload view to the skb length convention used by the loop.

fragheaderlen represents headers that must precede the fragmentable bytes in this construction. sizeof(struct frag_hdr) accounts for the IPv6 Fragment header itself. Treating either term as payload would shift the cut line and make an apparently correct multiple of eight incorrect on the wire. The formula's shape mirrors the distinction in RFC 8200 between unfragmentable headers, the Fragment header, and the fragmentable part whose offset is encoded in eight-octet units.

The source comment observes that fragment-header reservation plus alignment consumes between eight and fifteen octets in the relevant comparison. That range explains the alignment-dependent difference between the usable maximum and a naive MTU-minus-headers estimate. The actual carry comes from the prior object's realized length.

Once maxfraglen is known, the code forms fraggap = skb->len - maxfraglen when the previous skb extends beyond the boundary. A positive result moves excess bytes into the next fragment, zero requires no carry, and the surrounding loop handles a negative result.

This arithmetic runs on the local IPv6 transmit path. The kernel slices one outgoing logical datagram according to RFC 8200 geometry. An untrusted local workload can influence the content and lengths used by the host kernel during that send-side allocation.

Extension headers and route changes can alter the ruler without changing the core defect. An extra unfragmentable header increases fragheaderlen; a tunnel or virtual link can lower the effective MTU; a route refresh between datagrams can select another device. Reproduction notes should therefore preserve the exact header chain and destination route used for each observation. Copying only application length from a successful lab case to another host may produce zero carry and a false claim that its kernel is fixed.

3.2 skb_copy_and_csum_bits() carries the strip, then pskb_trim_unique() closes the old frame

After allocating the next skb and laying out its headers, the function calls skb_copy_and_csum_bits() to read the carried range from the previous skb and write it at the new linear tail. The helper can read across skb storage forms while updating checksum state. Its destination, however, is an ordinary pointer derived from the new skb head. It trusts the caller to have made that destination range writable.

The copy begins just after fragheaderlen and any applicable transhdrlen. On a non-first skb, the transport component is zero, but the fragment headers remain. The carried bytes therefore sit before the new page-backed continuation. The illustration shows that physical order as navy headers, amber carry, and violet pages. Recording the carry in page descriptors does not increase the new skb's linear capacity.

When the carry succeeds, pskb_trim_unique() shortens the previous skb to maxfraglen. The “unique” requirement protects against mutating a shared object whose other owners expect the old length. Trimming after a successful copy ensures the logical datagram loses no bytes. If the copy fails, error unwinding must leave enough state to dispose of the partial construction without pretending the transfer completed.

Checksum handling explains the chosen helper and operation order. The moved range must retain correct checksum semantics as it changes object ownership. The function can accurately read and checksum exactly fraggap bytes while still writing beyond the destination allocation when the caller's size model is wrong. The caller must guarantee destination capacity before the write.

The handoff invariant has two terms. Before skb_copy_and_csum_bits() writes the carry, the new skb's linear tailroom must include fragheaderlen + transhdrlen + fraggap; page descriptors may cover only the remaining datalen - transhdrlen - fraggap. The vulnerable branch preserves the total but violates both classifications. The next chapter compares those calculations directly.

Hand-drawn three-step fragment-gap carry: an eight-tooth alignment comb marks a cut on the old packet frame, careful hands lift the amber overhang, the new frame receives it directly after a navy header, and the old frame is trimmed to the marked line.
Figure 3. The carry is a real linear copy, followed by a trim of the source skb. In the destination, those bytes belong only to the new skb's linear region.

4 Two ledgers preserve the total while assigning carry to different storage classes

datalen already includes fraggap, so the intended outgoing length remains correct. The error appears when that total is split between linear allocation and page-backed length: the linear side omits the carry while the paged side grows by the same amount. Tests concerned only with total bytes or successful output can stay green; a useful oracle inspects storage ownership before the copy and the physical high-water mark afterward.

The function begins the relevant iteration with datalen = length + fraggap. Here length is the new contribution still to place, while the addition acknowledges bytes carried from the previous skb. Later calculations derive fraglen from the amount that can be represented in this fragment. Nothing in that top-level sum says where the carried range will live, so a correct total can coexist with a dangerous storage classification.

Before the fix, the paged branch used alloclen = fragheaderlen + transhdrlen and pagedlen = datalen - transhdrlen. The first expression reserves only headers in the skb head. The second assigns everything else—including the carry—to page descriptors. The implementation then copies the carry by value into the linear area; no new page fragment represents those bytes. The two expressions therefore disagree with the operation that follows.

The code also derives a local copy value from the same terms. In the vulnerable state, copy = datalen - transhdrlen - fraggap - pagedlen. Substituting the old pagedlen reduces the expression to -fraggap. The negative result is an algebraic signal that paged length has claimed bytes the copy path still regards as carried linear material.

Allocation then uses skb_put(skb, fraglen - pagedlen) to extend the writable linear tail. Because pagedlen is too large, the extension covers headers but omits the carry. The later copy uses the real positive fraggap, so its destination advances across every carried byte while the skb tail covers only the shorter range.

The skb interprets physical layout from its linear end marker and trailing metadata; it has no extra type information that can restore omitted capacity. Once the caller requests an allocation short by fraggap, skb_shared_info remains immediately after that short data region. The repair therefore has to reconcile storage classification before allocation.

This is a conservation bug with a location error. Let L be linear ownership and P page ownership. Both old and fixed code can satisfy L + P = total, so aggregate accounting alone proves little. The missing assertion is disjoint placement: every byte copied through a linear pointer must belong to L, every byte represented by a fragment descriptor must belong to P, and no byte may occupy both sets. fraggap violated that partition while leaving the sum intact.

4.1 The vulnerable algebra makes copy negative and the real carry write positive

Writing the old equations in one block exposes the contradiction. The same fraggap is added to datalen, remains inside pagedlen, and is then explicitly subtracted while calculating copy. The negative result describes how much ordinary iterator data remains after page attachment. The separate carry operation still transfers a positive length from the old skb.

datalen = length + fraggap
alloclen = fragheaderlen + transhdrlen
pagedlen = datalen - transhdrlen
copy = datalen - transhdrlen - fraggap - pagedlen
     = -fraggap

Let the carried range be G, headers H, and fresh page payload P. Physical construction needs a linear reservation of H + G and page ownership of P. The old branch reserves H and records P + G as paged. The two storage classes still sum to H + P + G, so a coarse total-length assertion passes while the destination linear area is short by G.

Earlier code treated a negative copy as an error. A later splice-specific change allowed the negative state when MSG_SPLICE_PAGES was present because that path could legitimately consume iterator bytes through page attachment instead of a linear copy. The exception made sense for page accounting in isolation. Combined with the forgotten carry classification, it allowed execution to continue to the independent positive fraggap copy.

The non-paged branch provides an internal control case. It sets alloclen from fraglen, and that length includes the full data contribution selected for the skb. The carry therefore has sufficient capacity when all payload is copied linearly. Comparing sibling branches is a productive review technique: if one route reserves the complete fragment while an optimization splits it, the split should prove that each byte belongs to exactly one storage class.

A review limited to bounds checks around skb_copy_and_csum_bits() can miss this pattern. The requested copy length is valid relative to the source skb, the pointer arithmetic is intentional, and total datagram length is consistent. The broken contract sits one phase earlier. Review must ask both whether length fits the source and whether the allocator reserved the destination range this operation will write.

C exposes those storage classes as scalar byte lengths: alloclen, pagedlen, datalen, and fraglen. The compiler sees no distinction between linear capacity and externally referenced content. Review annotations, helper names, or typed wrappers could make ownership harder to confuse, while the immediate regression belongs at the semantic boundary where the split is created and later consumed by skb_put().

Signedness deserves separate attention. copy needs to represent a negative remainder so the guard can detect contradictory classification, while allocation lengths must avoid wrapping into a huge unsigned request. This failure uses a deterministic negative algebraic result flowing into an exception, so static analysis limited to overflow or truncation may miss it. A semantic assertion such as copy >= 0 after correct page classification captures the design expectation.

4.2 The repair moves fraggap once, preserving total length and restoring physical ownership

Commit 736b380e28d0 changes the classification directly. The fixed branch computes alloclen = fragheaderlen + transhdrlen + fraggap and pagedlen = datalen - transhdrlen - fraggap. Logical datagram length stays unchanged. The same fraggap term moves from paged ownership into linear capacity, matching the actual copy destination.

alloclen = fragheaderlen + transhdrlen + fraggap
pagedlen = datalen - transhdrlen - fraggap

Conservation makes the patch reviewable. Adding the two fixed compartments still yields fragheaderlen + datalen; the repair neither duplicates nor drops payload. What changes is the ownership proof: page descriptors cover only bytes left in pages, while every byte written behind headers contributes to the skb head reservation. This property remains valid whether fraggap is zero or positive and whether transhdrlen belongs to the current skb.

With the corrected pagedlen, fraglen - pagedlen grows by exactly the carried amount. The ensuing skb_put() advances the tail across headers and carry before any destination write, and skb helpers and later checks use that tail to delimit valid linear data. The copy now lands below skb_end_pointer(skb), and the trailing control structure remains outside the packet.

The patch also removes the MSG_SPLICE_PAGES exception from the negative-copy check and deletes the stale explanation for that state. After page ownership excludes fraggap, a valid specialized path no longer needs copy to collapse below zero. Removing an exception is part of the fix's strength: future arithmetic drift is more likely to stop with an error than to reuse the contradictory state as normal.

Hand-drawn repair comparison: the vulnerable state counts carry in paged length while copying it into an undersized linear region; the fixed state reserves carry linearly and reduces paged length by the same amount.
Figure 4. The repair changes storage classification while preserving datagram length, bringing linear capacity into agreement with the actual copy range.

5 skb_shared_info begins immediately after the linear packet boundary

The official networking documentation shows a contiguous skb head-buffer layout: headroom, linear packet data, expandable tailroom, and shared control metadata after the data area's end. The carry crosses this object-internal boundary and reaches the structure that interprets nonlinear payload. The skb layout fixes that adjacency, while architecture and build configuration determine exact field offsets.

skb_end_pointer(skb) identifies the end of the head data area, and skb_shinfo(skb) resolves the shared-info address from the same boundary. This arrangement gives networking a stable place for page-fragment descriptors and offload state while keeping the skb management object compact. When linear reservation is short, the omitted bytes reach that metadata directly.

struct skb_shared_info begins with compact control fields before its fragment array. Current definitions include flags, metadata length, nr_frags, tx_flags, GSO parameters, a frag_list relationship, and page-fragment descriptors. The first out-of-range carry byte enters control state; architecture and configuration determine the first affected field.

The local sender can influence datagram content, and fragment geometry decides which slice becomes the carry, so write content is constrained by size and header state. The source diff establishes a contiguous out-of-bounds write. An arbitrary-address primitive requires additional shaping and a useful consumer of altered metadata. A controllable write into skb interpretation fields already justifies urgent host-kernel repair.

The first visible fault may occur well after __ip6_append_data() returns. A corrupted fragment count can misdirect iteration; altered offload state can affect checksum or segmentation; damaged ownership flags can change release behavior. The eventual task may be a softirq, driver worker, or cleanup path, and the process that issued splice() may already have exited. That time gap follows naturally from delayed consumption of the damaged metadata.

struct sk_buff is a separate management object containing pointers, lengths, protocol fields, and references to the head. skb_shared_info sits at the end of the allocated head buffer so cloned skb headers can share nonlinear data state. This CVE's carry crosses the head-data boundary. Crash reports should preserve both the skb management-object address and the head-buffer address.

5.1 skb_shinfo() maps the first out-of-range byte onto shared packet state

The documented head layout can be read as four consecutive ranges: headroom before data, linear data through tail, unused tailroom up to end, then skb_shared_info. skb_put() normally advances tail only within the allowed range. In the vulnerable branch, the tail advance is too short, and the raw carried-byte destination continues past the range the allocator intended as linear packet storage.

skb_shinfo(skb) is effectively anchored at skb_end_pointer(skb), so the distance from the bad destination to shared metadata is the missing reservation itself. The two arithmetic changes ensure that headers and the full carry receive linear capacity before the physical end pointer.

The leading flags describe properties shared by skb clones and non-linear storage. One relevant bit is SKBFL_SHARED_FRAG, which affects how fragment references are treated. Nearby tx_flags participates in transmit timestamp and related handling. A carry need not reach a pointer field to change later control flow; compact bitfields and counts at the front already influence which cleanup and output operations the networking core performs.

nr_frags tells later code how many entries in the fragment descriptor array are valid. Consumers use it to bound loops over page-backed data, build scatter-gather lists, calculate length, and release references. If corruption raises or lowers that count, a later stage can skip owned pages or interpret storage beyond the initialized descriptors. The exact outcome depends on the overwritten value and consumer, but the security significance follows directly from the field's contract.

GSO size, segment count, type, and linked fragment relationships sit farther into the structure, and reach depends on fraggap, layout, and intervening fields. Source review establishes a contiguous out-of-bounds write into the structure's beginning. Crash forensics should preserve raw shared-info bytes, kernel configuration, and matching layout information, tying every decoded offset to the sampled build.

Clone semantics amplify the importance of the shared region. Several skb headers can refer to one head and its page descriptors, with reference counts and flags deciding when data may be modified or released. A damaged SKBFL_SHARED_FRAG or nearby count can therefore affect an object after the original sender loses sight of it. This establishes the persistence of shared-state effects; a specific use-after-free chain still requires independent evidence.

5.2 Source confirms the overwrite; Red Hat confirms the escalation chain

A KASAN build can catch the write at its origin and provide the cleanest causal stack. Production kernels may show a page fault during fragment iteration, a refcount warning during release, an invalid GSO state, or a driver complaint. Investigators should correlate these downstream symptoms with recent local UDPv6 splice activity, the booted kernel, interface MTU, namespace, and skb allocation details.

Red Hat's bulletin states that the flaw can be chained to arbitrary kernel read and write, credential overwrite, SELinux bypass, and container escape. It labels the issue Important and says its fixes cover every affected Red Hat product. That vendor-published result raises prioritization to local privilege escalation and container-boundary failure. Other distributions still require decisions based on their own layout, patch stack, and deployed package.

The same bulletin ties its demonstrated container route to the ability to create network namespaces, commonly reached through unprivileged user namespaces on default RHEL 10. Setting user.max_user_namespaces=0 reduces that route and breaks rootless Podman and some sandboxes. The setting is a temporary exposure control with business cost; permanent closure still requires a fixed kernel in service.

The source-reviewed entrance begins with local splice(), and the kernel generates MSG_SPLICE_PAGES. On Jul 18, the Linux CNA added a CVSS 3.1 score of 7.8 HIGH with vector AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H. SUSE's product scoring includes 8.5 and 9.0 assessments that use a network attack vector. These scores come from different publishers and product decisions; telemetry and access review should follow the concrete local entrance reconstructed here.

These conclusions rest on different evidence layers. The overwrite into trailing skb_shared_info is confirmed by the fix, CNA, and source. Field meanings come from the fixed kernel definition and official skb documentation. Strong escalation is vendor-confirmed by Red Hat. As of Jul 27, the reviewed CNA, NVD, Red Hat, Ubuntu, Debian, and SUSE pages report no in-the-wild activity; that finding is bounded to those official pages. NVD completed Initial Analysis on Jul 22, adding generic Linux-kernel CPE ranges and NVD-CWE-noinfo, while the NIST CVSS assessment remains N/A.

Where policy allows, a crash-dump extension can print skb head, data, tail, and end offsets plus selected shared-info fields while omitting packet contents. Compare tail with the expected headers-and-carry high-water mark and flag impossible nr_frags values. Compared with broad tracing, this targeted collection materially minimizes sensitive-data capture while preserving the geometry needed to identify the overwrite. Include the symbol build ID and BTF or debug-info identity used for field decoding.

Hand-drawn skb memory cross-section: the vulnerable state carries bytes across the linear end into skb_shared_info, while the fixed state reserves the carry linearly and keeps external pages referenced by fragment descriptors.
Figure 5. Shared fragment and transmit metadata begins where linear packet storage ends, so the missing linear capacity directly determines the overwrite distance.

6 One decision map joins three commits, six upstream floors, and vendor product states

Upstream history assigns three separate roles. Commit 773ba4fe9104 introduced the paged-allocation split in 2022 and omitted the carry's linear share. Commit ce650a166335 allowed the splice-page negative-copy state to proceed in 2023, making the later positive-length carry copy reachable. Commit 736b380e28d0 moved fraggap into alloclen, removed it from pagedlen, and deleted the old exception in 2026. The focused repair preserves zero-copy and valid splice behavior while restoring one physical owner for every byte.

The five stable backports are distinct Git objects: 14200d435af9, 65fb14cbebb0, 46f201f8b4c3, 6374fb9edf72, and e9eacf19281e. With the mainline object, they define six branch-local fixed floors. Distributions can adopt the introducer, reachability change, and repair independently, so a product decision must bind the exact source package, patch stack, architecture, and flavor. A bare version comparison applies only to a build known to follow the corresponding upstream branch exactly.

Hand-drawn three-part commit history: the first part creates the paged-allocation split, the second lets the splice-page negative-copy state proceed, and the third moves fraggap into linear reservation and removes the exception.
Figure 6. Introduction, reachability, and repair belong to three commits. A downstream decision must identify all three in the exact source package.
LayerObject or authoritative entryFinding verified as of Jul 27, 2026Host decision and action
Upstream introduction773ba4fe9104 · 2022-07-18 · before v6.0-rc1The paged optimization omits the carry's linear reservation.Inspect distribution source for an equivalent split; absence can support a code-absent result.
Upstream reachabilityce650a166335 · 2023-08-03 · before v6.6-rc1The splice-page negative-copy state continues.Combined with the introducer, it creates the CNA path; downstreams may backport it independently.
Upstream repair736b380e28d0 plus five stable objectsFirst-fixed releases are 6.1.177, 6.6.144, 6.12.95, 6.18.38, 7.1.3, and 7.2-rc1.Use the branch floor for self-built upstream kernels; use package lineage and reboot evidence for distributions.
Red HatRHSB-2026-009Resolved and Important; RHEL 10 is affected with fixes released, while RHEL 9 is not impacted.Apply the relevant erratum, reboot RHEL 10 hosts, and retain NEVRA, running kernel, and boot ID.
UbuntuOfficial CVE trackerMedium; generic 26.04 and 24.04 are vulnerable, generic 22.04 and older are not affected; flavor states vary.Resolve the exact source package, release, and cloud, HWE, or OEM flavor.
DebianSecurity tracker and DSA-6381-1Bookworm 6.1.176 and trixie 6.12.94 are vulnerable; bookworm-security 6.1.177, current trixie-security 6.12.96, forky 7.1.3, and sid 7.1.4 are fixed. Trixie's fixed floor is 6.12.95, and bullseye lacks the vulnerable code.Retain repository, source-package, and boot evidence; distinguish repaired from code absent.
SUSEProduct trackerOverall Pending and Important, with Released, In progress, and Affected product rows; CVSS 8.5/9.0 are SUSE scores.Select the exact product, module, and kernel row and keep In progress or Affected hosts in the response queue.
NVDNVD entryInitial Analysis completed Jul 22, adding generic Linux-kernel CPEs and NVD-CWE-noinfo. NIST CVSS remains N/A; the Linux CNA score is 7.8 HIGH.Generic NVD CPE ranges do not replace distribution package lineage; retain retrieval time and score publisher.

This is the article's single version and product decision map. Every state is a dated public snapshot. Reopen assets that remain vulnerable, pending, or unknown when vendors change a flavor, repository, or product row. A reviewed source package must still connect to binary signature, installation record, and the booted kernel before it becomes host-level proof.

7 One host passes installed, running, boot ID, and behavior checks in order

A single asset record follows a host through the whole response. It first binds machine ID, node pool, tenant trust, workload, distribution source package, and vendor determination, then advances through the sequence below. Missing evidence leaves the state pending. Rebuilding a container image does not change the host's __ip6_append_data(); pods and tenants must map to the kernel actually booted on their node.

  1. Installed. Package-manager evidence records the full fixed-build identifier, repository, signature, architecture, and flavor and links them to the advisory or reviewed source. Image channel, version locks, phased updates, and initramfs outcome stay in the same record.
  2. Running. After reboot, collect the running-kernel identifier, /proc/version_signature or vendor equivalent, and the image actually selected by the bootloader. A fixed package beside an older running kernel remains pending.
  3. Boot ID. Retain /proc/sys/kernel/random/boot_id, boot time, machine ID, image digest, and rollout revision. The new boot ID separates this runtime from the pre-install kernel and prevents a rebuilt node from inheriting another machine's closure record.
  4. Behavior. A canary on an isolated instrumented kernel sweeps fragment boundaries and requires fraggap to enter linear reservation, paged length to shrink equally, and the carry to remain before skb_shinfo(), with clean KASAN, normal UDPv6, tunnels, container networking, and negative controls.
  5. Return to service. Scheduler evidence shows vulnerable nodes drained and untrusted workloads returning only to nodes that completed all checks. Temporary offload, namespace, and path controls are removed at this point.

A live patch replaces the reboot step only when the vendor explicitly covers this CVE or exact function change, the module is active in the current boot, and its state is auditable. If a candidate fixed build causes a networking regression, drain the node and boot another known-good fixed build. Temporary controls remain active until the replacement fixed boot completes.

Hand-drawn host acceptance chain linking pinned source and vendor determination to installed package, running kernel, boot ID, behavioral regression, and scheduler return-to-service evidence.
Figure 7. One host's closure record links source or advisory, installed package, running kernel, boot ID, behavior, and workload return in order. Any gap keeps the asset pending.

8 Closure retains only owners, evidence, and temporary-control removal

Owners. The kernel or platform team owns the fixed package, booted kernel, bootloader, and fallback. Workload owners accept canary behavior and return nodes to service. Security owners track unknown states, temporary-control expiry, and supplier proof. Each host pool retains the query that generated its asset queue, and any node that boots outside the approved kernel set automatically reopens the incident.

Evidence. The host record joins the Chapter 6 vendor or source decision to Chapter 7's installed package, running kernel, boot ID, behavioral result, and drain and return-to-service records. Fixed, vulnerable, code absent, and unknown describe source state. A missing host-level receipt leaves the asset pending.

Removal. After the fixed kernel is running, the carry high-water mark remains before skb_shinfo(), ordinary UDPv6 and negative controls pass, and untrusted workloads return only to accepted nodes, remove namespace, IPv6, splice, or offload restrictions and confirm restored throughput, latency, rootless containers, and sandboxes. If a candidate kernel regresses service, drain the node and move to another known-good fixed build while temporary controls remain active.

Local pipe-to-socket splice reaches the vulnerable branch through kernel-generated MSG_SPLICE_PAGES, a persistent UDP cork, and IPv6 fragment alignment. The bad classification charges carry to paged length and then writes it through a linear pointer into skb_shared_info. The pinned repair restores linear reservation and the negative-copy guard before allocation in __ip6_append_data(). The incident closes when source object, vendor package, current boot, and behavioral result all identify the repaired state.

Research record

9Evidence, objects, and sources

The material below preserves the identifiers and references used in this report.

9.1Research objects

Products, actors, techniques, affected objects, and control points discussed in the report.

CVECVE-2026-53362

Linux IPv6 paged-allocation overflow into skb_shared_info

Source filenet/ipv6/ip6_output.c

The affected IPv6 output implementation

Entry pathsplice_to_socket -> sock_sendmsg -> udpv6_sendmsg

Local pipe-page route into UDPv6 corking

Introducer773ba4fe9104a64a54d1c00f0fb6ffb95def2b03

Paged zero-copy allocation split present before v6.0-rc1

Reachabilityce650a1663354a6cad7145e7f5131008458b39d4

Negative-copy splice continuation present before v6.6-rc1

Fix736b380e28d0480c7bc3e022f1950f31fe53a7c5

Mainline fraggap accounting repair

Boundaryskb_end_pointer / skb_shared_info

Linear packet storage ends where shared control metadata begins

9.2Event chronology

  1. Paged allocation split merged

    Commit 773ba4fe9104 avoided a partial copy for zero-copy IPv6 output and introduced the mismatched storage classification.

  2. Splice continuation merged

    Commit ce650a166335 allowed the negative-copy splice-page case to proceed, turning dormant arithmetic into a triggerable corruption path.

  3. Mainline repair merged

    Commit 736b380e28d0 charged fraggap to linear allocation and removed it from paged length.

  4. Linux CNA record published

    CVE-2026-53362 documented unprivileged reachability, stable fixes, and the write into trailing skb_shared_info.

  5. NVD completed Initial Analysis

    NVD added generic Linux-kernel CPE ranges and NVD-CWE-noinfo; the NIST CVSS assessment remains N/A.

  6. SOSEC completed source and vendor-state review

    SOSEC reconciled primary-source history, the Linux CNA score, current vendor package state, mechanism diagrams, and repair validation.

9.3Sources and material

  1. Linux CNA record for CVE-2026-53362https://www.cve.org/CVERecord?id=CVE-2026-53362
  2. NVD Initial Analysis, CPE, CWE, and score attributionhttps://nvd.nist.gov/vuln/detail/CVE-2026-53362
  3. Mainline fraggap accounting fix 736b380e28d0https://git.kernel.org/stable/c/736b380e28d0480c7bc3e022f1950f31fe53a7c5
  4. Paged IPv6 zero-copy introducer 773ba4fe9104https://git.kernel.org/linus/773ba4fe9104a64a54d1c00f0fb6ffb95def2b03
  5. Splice-pages reachability change ce650a166335https://git.kernel.org/linus/ce650a1663354a6cad7145e7f5131008458b39d4
  6. Linux 6.1 stable fix objecthttps://git.kernel.org/stable/c/14200d435af9a9eeb444f529fc2f689a236b7962
  7. Linux 6.6 stable fix objecthttps://git.kernel.org/stable/c/65fb14cbebb0cd0eff903a22d33537ddc8b95769
  8. Linux 6.12 stable fix objecthttps://git.kernel.org/stable/c/46f201f8b4c39633a1fa3dc12459f506d470993d
  9. Linux 6.18 stable fix objecthttps://git.kernel.org/stable/c/6374fb9edf72c67a118a2c214a0dddd04c921e0a
  10. Linux 7.1 stable fix objecthttps://git.kernel.org/stable/c/e9eacf19281ea2498b36291b56c9606118c2d74e
  11. Fixed-tree fs/splice.chttps://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/fs/splice.c?id=736b380e28d0480c7bc3e022f1950f31fe53a7c5
  12. Fixed-tree net/socket.chttps://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/net/socket.c?id=736b380e28d0480c7bc3e022f1950f31fe53a7c5
  13. Fixed-tree net/ipv6/udp.chttps://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/net/ipv6/udp.c?id=736b380e28d0480c7bc3e022f1950f31fe53a7c5
  14. Fixed-tree net/ipv6/ip6_output.chttps://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/net/ipv6/ip6_output.c?id=736b380e28d0480c7bc3e022f1950f31fe53a7c5
  15. Fixed-tree internal socket flagshttps://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/include/linux/socket.h?id=736b380e28d0480c7bc3e022f1950f31fe53a7c5
  16. Fixed-tree sk_buff and skb_shared_info definitionshttps://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/include/linux/skbuff.h?id=736b380e28d0480c7bc3e022f1950f31fe53a7c5
  17. Linux sk_buff geometry documentationhttps://docs.kernel.org/networking/skbuff.html
  18. Linux splice(2) interface referencehttps://man7.org/linux/man-pages/man2/splice.2.html
  19. Linux sendmsg(2) interface referencehttps://man7.org/linux/man-pages/man2/sendmsg.2.html
  20. Linux UDP socket referencehttps://man7.org/linux/man-pages/man7/udp.7.html
  21. RFC 8200 IPv6 specificationhttps://datatracker.ietf.org/doc/rfc8200/
  22. Red Hat RHSB-2026-009 security bulletinhttps://access.redhat.com/security/vulnerabilities/RHSB-2026-009
  23. Red Hat kernel erratum RHSA-2026:34911https://access.redhat.com/errata/RHSA-2026:34911
  24. Red Hat severity classification guidehttps://access.redhat.com/security/updates/classification
  25. Ubuntu CVE-2026-53362 package trackerhttps://ubuntu.com/security/CVE-2026-53362
  26. Debian CVE-2026-53362 package trackerhttps://security-tracker.debian.org/tracker/CVE-2026-53362
  27. Debian Security Advisory DSA-6381-1https://www.debian.org/security/2026/dsa-6381
  28. SUSE CVE-2026-53362 product trackerhttps://www.suse.com/security/cve/CVE-2026-53362.html
  29. Public patch discussion for the fraggap fixhttps://patch.msgid.link/[email protected]