From: Xiang Mei Date: Thu, 18 Jun 2026 03:26:22 +0000 (-0700) Subject: geneve: validate inner network offset in geneve_gro_complete() X-Git-Tag: v7.2-rc1~29^2~24 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=cbb0d30a1ad6fc9439b1dc9b4f5a7a9140d3b11f;p=thirdparty%2Flinux.git geneve: validate inner network offset in geneve_gro_complete() 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 Co-developed-by: Weiming Shi Signed-off-by: Xiang Mei Link: https://patch.msgid.link/20260618032622.484720-2-xmei5@asu.edu Signed-off-by: Jakub Kicinski --- diff --git a/drivers/net/geneve.c b/drivers/net/geneve.c index 7cf7aaac8ee1a..396e1a113cd49 100644 --- a/drivers/net/geneve.c +++ b/drivers/net/geneve.c @@ -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);