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: fd0dd79657 ("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>
This commit is contained in:
Xiang Mei
2026-06-17 20:26:22 -07:00
committed by Jakub Kicinski
parent 2651c17444
commit cbb0d30a1a

View 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);