Skip to content

Commit e227831

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 de8be54 commit e227831

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
@@ -4519,6 +4519,8 @@ int skb_gro_receive(struct sk_buff *p, struct sk_buff *skb)
45194519
skb_frag_off_add(frag, offset);
45204520
skb_frag_size_sub(frag, offset);
45214521

4522+
pinfo->tx_flags |= skbinfo->tx_flags & SKBTX_SHARED_FRAG;
4523+
45224524
/* all fragments truesize : remove (head size + sk_buff) */
45234525
new_truesize = SKB_TRUESIZE(skb_end_offset(skb));
45244526
delta_truesize = skb->truesize - new_truesize;
@@ -4552,6 +4554,8 @@ int skb_gro_receive(struct sk_buff *p, struct sk_buff *skb)
45524554
memcpy(frag + 1, skbinfo->frags, sizeof(*frag) * skbinfo->nr_frags);
45534555
/* We dont need to clear skbinfo->nr_frags here */
45544556

4557+
pinfo->tx_flags |= skbinfo->tx_flags & SKBTX_SHARED_FRAG;
4558+
45554559
new_truesize = SKB_DATA_ALIGN(sizeof(struct sk_buff));
45564560
delta_truesize = skb->truesize - new_truesize;
45574561
skb->truesize = new_truesize;

0 commit comments

Comments
 (0)