Skip to content

Commit 5a65e78

Browse files
kerneltoastclaude
authored andcommitted
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>
1 parent 01e4dfc commit 5a65e78

1 file changed

Lines changed: 4 additions & 0 deletions

File tree

net/core/gro.c

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

203+
pinfo->flags |= skbinfo->flags & SKBFL_SHARED_FRAG;
204+
203205
/* all fragments truesize : remove (head size + sk_buff) */
204206
new_truesize = SKB_TRUESIZE(skb_end_offset(skb));
205207
delta_truesize = skb->truesize - new_truesize;
@@ -233,6 +235,8 @@ int skb_gro_receive(struct sk_buff *p, struct sk_buff *skb)
233235
memcpy(frag + 1, skbinfo->frags, sizeof(*frag) * skbinfo->nr_frags);
234236
/* We dont need to clear skbinfo->nr_frags here */
235237

238+
pinfo->flags |= skbinfo->flags & SKBFL_SHARED_FRAG;
239+
236240
new_truesize = SKB_DATA_ALIGN(sizeof(struct sk_buff));
237241
delta_truesize = skb->truesize - new_truesize;
238242
skb->truesize = new_truesize;

0 commit comments

Comments
 (0)