mirror of
https://github.com/torvalds/linux.git
synced 2026-10-23 15:32:04 +02:00
net: gso: limit recursive IP-in-IP segmentation
IP-in-IP GSO can re-enter inet_gso_segment() or ipv6_gso_segment()
for each nested IP header. encap_level tracks header bytes, not callback
depth, so a deep chain can exhaust the kernel stack. Making
inet_gso_segment() stackable introduced unbounded IPv4 nesting; IPIP
GSO/TSO later made the path reachable. The IPv6 stackable path was
introduced separately and uses the same guard.
Count IPv4 and IPv6 GSO handler entries in skb_gso_cb, initialized once
per top-level GSO operation and preserved across GRE/UDP context changes.
Use the existing IP_TUNNEL_RECURSION_LIMIT for both handlers. The first
five entries pass, and the sixth returns -EINVAL before dispatching
another GSO callback.
Fixes: 3347c96029 ("ipv4: gso: make inet_gso_segment() stackable")
Cc: stable@vger.kernel.org
Reported-by: Vega <vega@nebusec.ai>
Closes: https://lore.kernel.org/all/cover.1790157745.git.zihanx@nebusec.ai/
Assisted-by: LLM
Co-developed-by: Luxing Yin <root@tr0jan.top>
Signed-off-by: Luxing Yin <root@tr0jan.top>
Signed-off-by: Zihan Xi <zihanx@nebusec.ai>
Reviewed-by: Willem de Bruijn <willemb@google.com>
Link: https://patch.msgid.link/20260924051521.32568-2-zihanx@nebusec.ai
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
This commit is contained in:
parent
0f17d2e3dd
commit
006c8b834a
|
|
@ -19,10 +19,18 @@ struct skb_gso_cb {
|
|||
int encap_level;
|
||||
__wsum csum;
|
||||
__u16 csum_start;
|
||||
/* Number of IPv4/IPv6 GSO handler entries for this packet. */
|
||||
u8 recursion_counter;
|
||||
};
|
||||
#define SKB_GSO_CB_OFFSET 32
|
||||
#define SKB_GSO_CB(skb) ((struct skb_gso_cb *)((skb)->cb + SKB_GSO_CB_OFFSET))
|
||||
|
||||
static inline bool gso_recursion_inc_test(struct sk_buff *skb,
|
||||
unsigned int limit)
|
||||
{
|
||||
return ++SKB_GSO_CB(skb)->recursion_counter > limit;
|
||||
}
|
||||
|
||||
static inline int skb_tnl_header_len(const struct sk_buff *inner_skb)
|
||||
{
|
||||
return (skb_mac_header(inner_skb) - inner_skb->head) -
|
||||
|
|
|
|||
|
|
@ -117,6 +117,7 @@ struct sk_buff *__skb_gso_segment(struct sk_buff *skb,
|
|||
|
||||
SKB_GSO_CB(skb)->mac_offset = skb_headroom(skb);
|
||||
SKB_GSO_CB(skb)->encap_level = 0;
|
||||
SKB_GSO_CB(skb)->recursion_counter = 0;
|
||||
|
||||
skb_reset_mac_header(skb);
|
||||
skb_reset_mac_len(skb);
|
||||
|
|
|
|||
|
|
@ -1375,6 +1375,9 @@ struct sk_buff *inet_gso_segment(struct sk_buff *skb,
|
|||
int id;
|
||||
|
||||
skb_reset_network_header(skb);
|
||||
if (unlikely(gso_recursion_inc_test(skb,
|
||||
IP_TUNNEL_RECURSION_LIMIT)))
|
||||
goto out;
|
||||
nhoff = skb_network_header(skb) - skb_mac_header(skb);
|
||||
if (unlikely(!pskb_may_pull(skb, sizeof(*iph))))
|
||||
goto out;
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@
|
|||
#include <net/udp.h>
|
||||
#include <net/gro.h>
|
||||
#include <net/gso.h>
|
||||
#include <net/ip_tunnels.h>
|
||||
|
||||
#include "ip6_offload.h"
|
||||
#include "tcpv6_offload.c"
|
||||
|
|
@ -104,6 +105,9 @@ static struct sk_buff *ipv6_gso_segment(struct sk_buff *skb,
|
|||
bool gso_partial;
|
||||
|
||||
skb_reset_network_header(skb);
|
||||
if (unlikely(gso_recursion_inc_test(skb,
|
||||
IP_TUNNEL_RECURSION_LIMIT)))
|
||||
goto out;
|
||||
nhoff = skb_network_header(skb) - skb_mac_header(skb);
|
||||
if (unlikely(!pskb_may_pull(skb, sizeof(*ipv6h))))
|
||||
goto out;
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user