Skip to content

Commit 29c6325

Browse files
net: gro: propagate SKBFL_SHARED_FRAG in skb_gro_receive()
skb_gro_receive() moves frag descriptors from the incoming skb to the GRO accumulator through two frag-transfer paths (the direct frag-move loop and the head_frag + memcpy path) without propagating the SKBFL_SHARED_FRAG flag from the incoming skb's shinfo->flags. As a result, the accumulator ends up holding references to externally owned or page-cache-backed pages while reporting skb_has_shared_frag() as false. This is the same bug class as CVE-2026-46300 (d8cfbcd, "net: skbuff: propagate shared-frag marker through frag-transfer helpers"), which fixed the identical omission in __pskb_copy_fclone(), skb_try_coalesce(), and skb_shift(). skb_gro_receive() was missed in that fix since it lives in net/core/gro.c rather than net/core/skbuff.c. The impact is observable through ESP-over-UDP with UDP GRO: splice() attaches page-cache pages to a UDP skb, setting SKBFL_SHARED_FRAG via ip_append_page(). When two such datagrams are GRO-merged via skb_gro_receive(), the flag is dropped. After udp_rcv_segment() re-segments the merged GSO skb, the fresh segments carry the page-cache frags without the shared-frag marker. esp_input() then sees \!skb_cloned() && \!skb_has_shared_frag() and takes the skip_cow fast path, decrypting in place over the page-cache pages. Because AES-GCM CTR decryption runs before the authentication tag is verified, the page cache is corrupted even though the tag check subsequently fails. Fix it by propagating SKBFL_SHARED_FRAG from the incoming skb to the accumulator in both frag-transfer paths, matching what the skbuff.c helpers already do. The third path (frag_list merge at the "merge:" label) chains the entire incoming skb onto the accumulator's frag_list without moving individual frag descriptors, so each sub-skb retains its own flags and no propagation is needed there. Fixes: cef401d ("net: fix possible wrong checksum generation") Fixes: f4c50a4 ("xfrm: esp: avoid in-place decrypt on shared skb frags") Cc: stable@vger.kernel.org Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Signed-off-by: Sultan Alsawaf <sultan@ciq.com> Signed-off-by: Shreeya Patel <spatel@ciq.com>
1 parent cc78744 commit 29c6325

1 file changed

Lines changed: 4 additions & 0 deletions

File tree

net/core/skbuff.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4242,6 +4242,8 @@ int skb_gro_receive(struct sk_buff *p, struct sk_buff *skb)
42424242
skb_frag_off_add(frag, offset);
42434243
skb_frag_size_sub(frag, offset);
42444244

4245+
pinfo->tx_flags |= skbinfo->tx_flags & SKBTX_SHARED_FRAG;
4246+
42454247
/* all fragments truesize : remove (head size + sk_buff) */
42464248
new_truesize = SKB_TRUESIZE(skb_end_offset(skb));
42474249
delta_truesize = skb->truesize - new_truesize;
@@ -4275,6 +4277,8 @@ int skb_gro_receive(struct sk_buff *p, struct sk_buff *skb)
42754277
memcpy(frag + 1, skbinfo->frags, sizeof(*frag) * skbinfo->nr_frags);
42764278
/* We dont need to clear skbinfo->nr_frags here */
42774279

4280+
pinfo->tx_flags |= skbinfo->tx_flags & SKBTX_SHARED_FRAG;
4281+
42784282
new_truesize = SKB_DATA_ALIGN(sizeof(struct sk_buff));
42794283
delta_truesize = skb->truesize - new_truesize;
42804284
skb->truesize = new_truesize;

0 commit comments

Comments
 (0)