]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
geneve: validate inner network offset in geneve_gro_complete()
authorXiang Mei <xmei5@asu.edu>
Thu, 18 Jun 2026 03:26:22 +0000 (20:26 -0700)
committerJakub Kicinski <kuba@kernel.org>
Thu, 25 Jun 2026 01:59:29 +0000 (18:59 -0700)
Even with both paths gated on gs->gro_hint, geneve_gro_complete()
re-derives the inner dispatch type and length from the packet and the
current gs->gro_hint, independently of geneve_gro_receive(). The two can
disagree if gs->gro_hint flips under a concurrent geneve_quiesce()/
geneve_unquiesce() (sk_user_data is NULL across a synchronize_net()), or if
the re-read option bytes differ from the ones receive parsed.

geneve_gro_receive() already records the inner network header position in
NAPI_GRO_CB()->inner_network_offset. Have geneve_gro_complete() compute the
offset it is about to dispatch at, adding ETH_HLEN in the ETH_P_TEB case
where eth_gro_complete() steps over the inner MAC header, and bail out if
it lands past inner_network_offset.

Use a lower bound rather than exact equality: between gh_len and the inner
L3 header, geneve_gro_receive() may also have pulled an inner VLAN tag
(vlan_gro_receive() advances the recorded offset past it), which only moves
inner_network_offset further out. A valid frame therefore always satisfies
inner_nh <= inner_network_offset, while a gh_len inflated by a hint
gro_receive() did not honour dispatches past the validated inner header,
i.e. the out-of-bounds completion. Only the latter is rejected.

Fixes: fd0dd796576e ("geneve: use GRO hint option in the RX path")
Suggested-by: Paolo Abeni <pabeni@redhat.com>
Co-developed-by: Weiming Shi <bestswngs@gmail.com>
Signed-off-by: Xiang Mei <xmei5@asu.edu>
Link: https://patch.msgid.link/20260618032622.484720-2-xmei5@asu.edu
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
drivers/net/geneve.c

index 7cf7aaac8ee1a3f46435359c26b642fa4a31244a..396e1a113cd49e9a43bfe6c7cd78f15ca8963906 100644 (file)
@@ -962,6 +962,20 @@ static int geneve_gro_complete(struct sock *sk, struct sk_buff *skb,
        type = gh->proto_type;
        geneve_sk_gro_hint_off(sk, gh, &type, &gh_len);
 
+       /* Bail out if we are about to dispatch past the inner network header
+        * gro_receive() validated. An inner VLAN tag only pushes
+        * inner_network_offset out, so use a lower bound.
+        */
+       if (skb->encapsulation) {
+               unsigned int inner_nh = nhoff + gh_len;
+
+               if (type == htons(ETH_P_TEB))
+                       inner_nh += ETH_HLEN;
+
+               if (unlikely(inner_nh > NAPI_GRO_CB(skb)->inner_network_offset))
+                       return -EINVAL;
+       }
+
        /* since skb->encapsulation is set, eth_gro_complete() sets the inner mac header */
        if (likely(type == htons(ETH_P_TEB)))
                return eth_gro_complete(skb, nhoff + gh_len);