* [PATCH net 0/2] nsh: limit recursion in nsh_gso_segment()
@ 2026-11-12 10:19 Eric Dumazet
2026-11-12 10:20 ` [PATCH net 1/2] " Eric Dumazet
` (2 more replies)
0 siblings, 3 replies; 7+ messages in thread
From: Eric Dumazet @ 2026-11-12 10:19 UTC (permalink / raw)
To: David S . Miller, Jakub Kicinski, Paolo Abeni
Cc: Willem de Bruijn, Simon Horman, netdev, edumazet
Sashiko found that nsh_gso_segment() can call itself, through
skb_mac_gso_segment(), once per stacked NSH header. Nothing limits the
depth, and a 64KB GSO packet made of 8-byte NSH headers overflows the
kernel stack. It was reported while reviewing an unrelated virtio
series [1].
Such a packet can be sent with a packet socket using PACKET_VNET_HDR,
when the nsh module is loaded.
Patch 1 uses the recursion counter added in commit 006c8b834aed ("net:
gso: limit recursive IP-in-IP segmentation"), with the same limit as
IPv4 and IPv6.
Patch 2 adds a selftest: it panics an unfixed kernel, and passes with
patch 1.
[1] https://lore.kernel.org/netdev/179443691180.2199510.148246238688217560@kernel.org/
Eric Dumazet (2):
nsh: limit recursion in nsh_gso_segment()
selftests: net: add a test for NSH GSO
include/net/gso.h | 2 +-
net/nsh/nsh.c | 7 ++
tools/testing/selftests/net/.gitignore | 1 +
tools/testing/selftests/net/Makefile | 2 +
tools/testing/selftests/net/config | 1 +
tools/testing/selftests/net/nsh_gso.c | 126 +++++++++++++++++++++++++
tools/testing/selftests/net/nsh_gso.sh | 71 ++++++++++++++
7 files changed, 209 insertions(+), 1 deletion(-)
create mode 100644 tools/testing/selftests/net/nsh_gso.c
create mode 100755 tools/testing/selftests/net/nsh_gso.sh
base-commit: 145480b28ca82e9fae789427cc4569e596515a32
--
2.53.0
^ permalink raw reply [flat|nested] 7+ messages in thread* [PATCH net 1/2] nsh: limit recursion in nsh_gso_segment() 2026-11-12 10:19 [PATCH net 0/2] nsh: limit recursion in nsh_gso_segment() Eric Dumazet @ 2026-11-12 10:20 ` Eric Dumazet 2026-11-12 10:33 ` Jiri Benc ` (2 more replies) 2026-11-12 10:20 ` [PATCH net 2/2] selftests: net: add a test for NSH GSO Eric Dumazet 2026-11-12 19:40 ` [PATCH net 0/2] nsh: limit recursion in nsh_gso_segment() patchwork-bot+netdevbpf 2 siblings, 3 replies; 7+ messages in thread From: Eric Dumazet @ 2026-11-12 10:20 UTC (permalink / raw) To: David S . Miller, Jakub Kicinski, Paolo Abeni Cc: Willem de Bruijn, Simon Horman, netdev, edumazet, Sashiko, Jiri Benc nsh_gso_segment() calls skb_mac_gso_segment() for the inner packet, which calls nsh_gso_segment() again when the next protocol is NSH, either directly or behind an inner Ethernet header (ETH_P_TEB). Nothing limits the depth: a GSO packet made of stacked 8-byte NSH headers recurses once per header, and a 64KB packet can hold about 8000 of them, overflowing the kernel stack. Such a packet can be injected with a packet socket using PACKET_VNET_HDR, or with tun/tap or virtio_net, as nothing checks that a GSO packet with VIRTIO_NET_HDR_F_NEEDS_CSUM carries the protocol it claims. It is then segmented in software if the output device does not support the GSO type: BUG: TASK stack guard page was hit at ffffcad5c0ab3ff8 (stack is ffffcad5c0ab4000..ffffcad5c0ab8000) Oops: stack guard page: 0000 [#1] SMP NOPTI CPU: 3 UID: 0 PID: 337 Comm: nsh_gso Tainted: G E 7.4.0-rc1+ #118 PREEMPT(lazy) RIP: 0010:lock_acquire (kernel/locking/lockdep.c:5902) Call Trace: <TASK> skb_mac_gso_segment (include/linux/rcupdate.h:309 include/linux/rcupdate.h:849 net/core/gso.c:50) nsh_gso_segment (net/nsh/nsh.c:110) nsh skb_mac_gso_segment (net/core/gso.c:53) nsh_gso_segment (net/nsh/nsh.c:110) nsh [...] skb_mac_gso_segment (net/core/gso.c:53) __skb_gso_segment (net/core/gso.c:129) validate_xmit_skb (net/core/dev.c:4238) __dev_queue_xmit (net/core/dev.c:5087) packet_sendmsg (net/packet/af_packet.c:3157 net/packet/af_packet.c:3189) __sys_sendto (net/socket.c:800 net/socket.c:818 net/socket.c:2283) __x64_sys_sendto (net/socket.c:2290 net/socket.c:2286 net/socket.c:2286) do_syscall_64 (arch/x86/entry/syscall_64.c:61 arch/x86/entry/syscall_64.c:84) entry_SYSCALL_64_after_hwframe (arch/x86/entry/entry_64.S:121) Use gso_recursion_inc_test(), added in commit 006c8b834aed ("net: gso: limit recursive IP-in-IP segmentation"), with the same limit as IPv4 and IPv6. The counter is shared with them: a packet with four NSH headers in front of an IPv4 header is still segmented, one more NSH header and it is dropped. The NSH -> ETH_P_TEB -> NSH route is covered as well, as each round goes through nsh_gso_segment(). Fixes: c411ed854584 ("nsh: add GSO support") Reported-by: Sashiko <sashiko-bot@kernel.org> Closes: https://lore.kernel.org/netdev/179443691180.2199510.148246238688217560@kernel.org/ Cc: Jiri Benc <jbenc@redhat.com> Signed-off-by: Eric Dumazet <edumazet@kernel.org> --- include/net/gso.h | 2 +- net/nsh/nsh.c | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/include/net/gso.h b/include/net/gso.h index fe3260b381d4..119fc4c170e1 100644 --- a/include/net/gso.h +++ b/include/net/gso.h @@ -19,7 +19,7 @@ struct skb_gso_cb { int encap_level; __wsum csum; __u16 csum_start; - /* Number of IPv4/IPv6 GSO handler entries for this packet. */ + /* Number of IPv4/IPv6/NSH GSO handler entries for this packet. */ u8 recursion_counter; /* Max MSS segs per output skb, 0 = no limit */ u8 max_segs; diff --git a/net/nsh/nsh.c b/net/nsh/nsh.c index bfb7758063f3..dd4de50fc38c 100644 --- a/net/nsh/nsh.c +++ b/net/nsh/nsh.c @@ -9,6 +9,7 @@ #include <linux/netdevice.h> #include <linux/skbuff.h> #include <net/gso.h> +#include <net/ip_tunnels.h> #include <net/nsh.h> #include <net/tun_proto.h> @@ -84,6 +85,12 @@ static struct sk_buff *nsh_gso_segment(struct sk_buff *skb, skb_reset_network_header(skb); + /* NSH can carry NSH (directly, or in an inner Ethernet frame): + * limit the recursion, as IPv4 and IPv6 do. + */ + if (unlikely(gso_recursion_inc_test(skb, IP_TUNNEL_RECURSION_LIMIT))) + goto out; + outer_proto = skb->protocol; outer_hlen = skb_mac_header_len(skb); mac_len = skb->mac_len; -- 2.53.0 ^ permalink raw reply related [flat|nested] 7+ messages in thread
* Re: [PATCH net 1/2] nsh: limit recursion in nsh_gso_segment() 2026-11-12 10:20 ` [PATCH net 1/2] " Eric Dumazet @ 2026-11-12 10:33 ` Jiri Benc 2026-11-12 22:54 ` David Laight 2026-11-13 0:03 ` Willem de Bruijn 2 siblings, 0 replies; 7+ messages in thread From: Jiri Benc @ 2026-11-12 10:33 UTC (permalink / raw) To: Eric Dumazet Cc: David S . Miller, Jakub Kicinski, Paolo Abeni, Willem de Bruijn, Simon Horman, netdev, Sashiko On Thu, 12 Nov 2026 11:20:00 +0100, Eric Dumazet wrote: > Use gso_recursion_inc_test(), added in commit 006c8b834aed ("net: gso: > limit recursive IP-in-IP segmentation"), with the same limit as IPv4 > and IPv6. The counter is shared with them: a packet with four NSH > headers in front of an IPv4 header is still segmented, one more NSH > header and it is dropped. > > The NSH -> ETH_P_TEB -> NSH route is covered as well, as each round > goes through nsh_gso_segment(). > > Fixes: c411ed854584 ("nsh: add GSO support") > Reported-by: Sashiko <sashiko-bot@kernel.org> > Closes: https://lore.kernel.org/netdev/179443691180.2199510.148246238688217560@kernel.org/ > Cc: Jiri Benc <jbenc@redhat.com> > Signed-off-by: Eric Dumazet <edumazet@kernel.org> Reviewed-by: Jiri Benc <jbenc@redhat.com> Thanks for the fix! Jiri ^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH net 1/2] nsh: limit recursion in nsh_gso_segment() 2026-11-12 10:20 ` [PATCH net 1/2] " Eric Dumazet 2026-11-12 10:33 ` Jiri Benc @ 2026-11-12 22:54 ` David Laight 2026-11-13 0:03 ` Willem de Bruijn 2 siblings, 0 replies; 7+ messages in thread From: David Laight @ 2026-11-12 22:54 UTC (permalink / raw) To: Eric Dumazet Cc: David S . Miller, Jakub Kicinski, Paolo Abeni, Willem de Bruijn, Simon Horman, netdev, Sashiko, Jiri Benc On Thu, 12 Nov 2026 11:20:00 +0100 Eric Dumazet <edumazet@kernel.org> wrote: > nsh_gso_segment() calls skb_mac_gso_segment() for the inner packet, > which calls nsh_gso_segment() again when the next protocol is NSH, > either directly or behind an inner Ethernet header (ETH_P_TEB). > > Nothing limits the depth: a GSO packet made of stacked 8-byte NSH > headers recurses once per header, and a 64KB packet can hold about > 8000 of them, overflowing the kernel stack. > > Such a packet can be injected with a packet socket using > PACKET_VNET_HDR, or with tun/tap or virtio_net, as nothing checks > that a GSO packet with VIRTIO_NET_HDR_F_NEEDS_CSUM carries the > protocol it claims. It is then segmented in software if the output > device does not support the GSO type: > > BUG: TASK stack guard page was hit at ffffcad5c0ab3ff8 (stack is ffffcad5c0ab4000..ffffcad5c0ab8000) > Oops: stack guard page: 0000 [#1] SMP NOPTI > CPU: 3 UID: 0 PID: 337 Comm: nsh_gso Tainted: G E 7.4.0-rc1+ #118 PREEMPT(lazy) > RIP: 0010:lock_acquire (kernel/locking/lockdep.c:5902) > Call Trace: > <TASK> > skb_mac_gso_segment (include/linux/rcupdate.h:309 include/linux/rcupdate.h:849 net/core/gso.c:50) > nsh_gso_segment (net/nsh/nsh.c:110) nsh > skb_mac_gso_segment (net/core/gso.c:53) > nsh_gso_segment (net/nsh/nsh.c:110) nsh > [...] > skb_mac_gso_segment (net/core/gso.c:53) > __skb_gso_segment (net/core/gso.c:129) > validate_xmit_skb (net/core/dev.c:4238) > __dev_queue_xmit (net/core/dev.c:5087) > packet_sendmsg (net/packet/af_packet.c:3157 net/packet/af_packet.c:3189) > __sys_sendto (net/socket.c:800 net/socket.c:818 net/socket.c:2283) > __x64_sys_sendto (net/socket.c:2290 net/socket.c:2286 net/socket.c:2286) > do_syscall_64 (arch/x86/entry/syscall_64.c:61 arch/x86/entry/syscall_64.c:84) > entry_SYSCALL_64_after_hwframe (arch/x86/entry/entry_64.S:121) > > Use gso_recursion_inc_test(), added in commit 006c8b834aed ("net: gso: > limit recursive IP-in-IP segmentation"), with the same limit as IPv4 > and IPv6. The counter is shared with them: a packet with four NSH > headers in front of an IPv4 header is still segmented, one more NSH > header and it is dropped. Might it make sense to get the amount of space left on the kernel stack and compare to some architecture defined limit rather than hardcoding a count? In particular sparc64 is much more likely to run out of stack (even with its massive stack size) than anything else. David > > The NSH -> ETH_P_TEB -> NSH route is covered as well, as each round > goes through nsh_gso_segment(). > > Fixes: c411ed854584 ("nsh: add GSO support") > Reported-by: Sashiko <sashiko-bot@kernel.org> > Closes: https://lore.kernel.org/netdev/179443691180.2199510.148246238688217560@kernel.org/ > Cc: Jiri Benc <jbenc@redhat.com> > Signed-off-by: Eric Dumazet <edumazet@kernel.org> > --- > include/net/gso.h | 2 +- > net/nsh/nsh.c | 7 +++++++ > 2 files changed, 8 insertions(+), 1 deletion(-) > > diff --git a/include/net/gso.h b/include/net/gso.h > index fe3260b381d4..119fc4c170e1 100644 > --- a/include/net/gso.h > +++ b/include/net/gso.h > @@ -19,7 +19,7 @@ struct skb_gso_cb { > int encap_level; > __wsum csum; > __u16 csum_start; > - /* Number of IPv4/IPv6 GSO handler entries for this packet. */ > + /* Number of IPv4/IPv6/NSH GSO handler entries for this packet. */ > u8 recursion_counter; > /* Max MSS segs per output skb, 0 = no limit */ > u8 max_segs; > diff --git a/net/nsh/nsh.c b/net/nsh/nsh.c > index bfb7758063f3..dd4de50fc38c 100644 > --- a/net/nsh/nsh.c > +++ b/net/nsh/nsh.c > @@ -9,6 +9,7 @@ > #include <linux/netdevice.h> > #include <linux/skbuff.h> > #include <net/gso.h> > +#include <net/ip_tunnels.h> > #include <net/nsh.h> > #include <net/tun_proto.h> > > @@ -84,6 +85,12 @@ static struct sk_buff *nsh_gso_segment(struct sk_buff *skb, > > skb_reset_network_header(skb); > > + /* NSH can carry NSH (directly, or in an inner Ethernet frame): > + * limit the recursion, as IPv4 and IPv6 do. > + */ > + if (unlikely(gso_recursion_inc_test(skb, IP_TUNNEL_RECURSION_LIMIT))) > + goto out; > + > outer_proto = skb->protocol; > outer_hlen = skb_mac_header_len(skb); > mac_len = skb->mac_len; ^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH net 1/2] nsh: limit recursion in nsh_gso_segment() 2026-11-12 10:20 ` [PATCH net 1/2] " Eric Dumazet 2026-11-12 10:33 ` Jiri Benc 2026-11-12 22:54 ` David Laight @ 2026-11-13 0:03 ` Willem de Bruijn 2 siblings, 0 replies; 7+ messages in thread From: Willem de Bruijn @ 2026-11-13 0:03 UTC (permalink / raw) To: Eric Dumazet, David S . Miller, Jakub Kicinski, Paolo Abeni Cc: Willem de Bruijn, Simon Horman, netdev, edumazet, Sashiko, Jiri Benc Eric Dumazet wrote: > nsh_gso_segment() calls skb_mac_gso_segment() for the inner packet, > which calls nsh_gso_segment() again when the next protocol is NSH, > either directly or behind an inner Ethernet header (ETH_P_TEB). > > Nothing limits the depth: a GSO packet made of stacked 8-byte NSH > headers recurses once per header, and a 64KB packet can hold about > 8000 of them, overflowing the kernel stack. > > Such a packet can be injected with a packet socket using > PACKET_VNET_HDR, or with tun/tap or virtio_net, as nothing checks > that a GSO packet with VIRTIO_NET_HDR_F_NEEDS_CSUM carries the > protocol it claims. It is then segmented in software if the output > device does not support the GSO type: > > BUG: TASK stack guard page was hit at ffffcad5c0ab3ff8 (stack is ffffcad5c0ab4000..ffffcad5c0ab8000) > Oops: stack guard page: 0000 [#1] SMP NOPTI > CPU: 3 UID: 0 PID: 337 Comm: nsh_gso Tainted: G E 7.4.0-rc1+ #118 PREEMPT(lazy) > RIP: 0010:lock_acquire (kernel/locking/lockdep.c:5902) > Call Trace: > <TASK> > skb_mac_gso_segment (include/linux/rcupdate.h:309 include/linux/rcupdate.h:849 net/core/gso.c:50) > nsh_gso_segment (net/nsh/nsh.c:110) nsh > skb_mac_gso_segment (net/core/gso.c:53) > nsh_gso_segment (net/nsh/nsh.c:110) nsh > [...] > skb_mac_gso_segment (net/core/gso.c:53) > __skb_gso_segment (net/core/gso.c:129) > validate_xmit_skb (net/core/dev.c:4238) > __dev_queue_xmit (net/core/dev.c:5087) > packet_sendmsg (net/packet/af_packet.c:3157 net/packet/af_packet.c:3189) > __sys_sendto (net/socket.c:800 net/socket.c:818 net/socket.c:2283) > __x64_sys_sendto (net/socket.c:2290 net/socket.c:2286 net/socket.c:2286) > do_syscall_64 (arch/x86/entry/syscall_64.c:61 arch/x86/entry/syscall_64.c:84) > entry_SYSCALL_64_after_hwframe (arch/x86/entry/entry_64.S:121) > > Use gso_recursion_inc_test(), added in commit 006c8b834aed ("net: gso: > limit recursive IP-in-IP segmentation"), with the same limit as IPv4 > and IPv6. The counter is shared with them: a packet with four NSH > headers in front of an IPv4 header is still segmented, one more NSH > header and it is dropped. > > The NSH -> ETH_P_TEB -> NSH route is covered as well, as each round > goes through nsh_gso_segment(). > > Fixes: c411ed854584 ("nsh: add GSO support") > Reported-by: Sashiko <sashiko-bot@kernel.org> > Closes: https://lore.kernel.org/netdev/179443691180.2199510.148246238688217560@kernel.org/ > Cc: Jiri Benc <jbenc@redhat.com> > Signed-off-by: Eric Dumazet <edumazet@kernel.org> Reviewed-by: Willem de Bruijn <willemb@google.com> ^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH net 2/2] selftests: net: add a test for NSH GSO 2026-11-12 10:19 [PATCH net 0/2] nsh: limit recursion in nsh_gso_segment() Eric Dumazet 2026-11-12 10:20 ` [PATCH net 1/2] " Eric Dumazet @ 2026-11-12 10:20 ` Eric Dumazet 2026-11-12 19:40 ` [PATCH net 0/2] nsh: limit recursion in nsh_gso_segment() patchwork-bot+netdevbpf 2 siblings, 0 replies; 7+ messages in thread From: Eric Dumazet @ 2026-11-12 10:20 UTC (permalink / raw) To: David S . Miller, Jakub Kicinski, Paolo Abeni Cc: Willem de Bruijn, Simon Horman, netdev, edumazet Send TCPv4 GSO packets encapsulated in a stack of NSH headers to a dummy device which has TSO disabled, using a packet socket with PACKET_VNET_HDR, and count the packets the device transmits. Packets with one or four NSH headers must be segmented. Packets with 5, 64 or 8000 NSH headers must be dropped: they exceed the recursion limit of the GSO handlers. Without the limit in nsh_gso_segment(), the last one overflows the kernel stack. # ./nsh_gso.sh PASS: 1 NSH headers: 4 packets sent PASS: 4 NSH headers: 4 packets sent PASS: 5 NSH headers: 0 packets sent PASS: 64 NSH headers: 0 packets sent PASS: 8000 NSH headers: 0 packets sent Signed-off-by: Eric Dumazet <edumazet@kernel.org> --- tools/testing/selftests/net/.gitignore | 1 + tools/testing/selftests/net/Makefile | 2 + tools/testing/selftests/net/config | 1 + tools/testing/selftests/net/nsh_gso.c | 126 +++++++++++++++++++++++++ tools/testing/selftests/net/nsh_gso.sh | 71 ++++++++++++++ 5 files changed, 201 insertions(+) create mode 100644 tools/testing/selftests/net/nsh_gso.c create mode 100755 tools/testing/selftests/net/nsh_gso.sh diff --git a/tools/testing/selftests/net/.gitignore b/tools/testing/selftests/net/.gitignore index ca451bfeda75..27761239d069 100644 --- a/tools/testing/selftests/net/.gitignore +++ b/tools/testing/selftests/net/.gitignore @@ -21,6 +21,7 @@ log.txt msg_zerocopy netlink-dumps nettest +nsh_gso proc_net_pktgen psock_fanout psock_snd diff --git a/tools/testing/selftests/net/Makefile b/tools/testing/selftests/net/Makefile index 85f51a30c6a1..baea3e7f0f8f 100644 --- a/tools/testing/selftests/net/Makefile +++ b/tools/testing/selftests/net/Makefile @@ -76,6 +76,7 @@ TEST_PROGS := \ nk_qlease.py \ nl_netdev.py \ nl_nlctrl.py \ + nsh_gso.sh \ pmtu.sh \ protodown.sh \ psock_snd.sh \ @@ -157,6 +158,7 @@ TEST_GEN_FILES := \ ipv6_flowlabel \ msg_zerocopy \ nettest \ + nsh_gso \ psock_fanout \ psock_snd \ psock_tpacket \ diff --git a/tools/testing/selftests/net/config b/tools/testing/selftests/net/config index d4416f8bdcfd..3e7ce41bd64d 100644 --- a/tools/testing/selftests/net/config +++ b/tools/testing/selftests/net/config @@ -99,6 +99,7 @@ CONFIG_NET_IPVTI=y CONFIG_NETKIT=y CONFIG_NET_L3_MASTER_DEV=y CONFIG_NET_NS=y +CONFIG_NET_NSH=m CONFIG_NET_PKTGEN=m CONFIG_NET_SCH_ETF=m CONFIG_NET_SCH_FQ=m diff --git a/tools/testing/selftests/net/nsh_gso.c b/tools/testing/selftests/net/nsh_gso.c new file mode 100644 index 000000000000..9acd3f755723 --- /dev/null +++ b/tools/testing/selftests/net/nsh_gso.c @@ -0,0 +1,126 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Send one TCPv4 GSO packet encapsulated in a stack of NSH headers, + * through a packet socket with PACKET_VNET_HDR. + * + * Usage: nsh_gso <ifname> <number of NSH headers> + * + * The packet is made of 4 segments. The send() result is not checked: + * the caller looks at the device counters. + */ + +#include <arpa/inet.h> +#include <errno.h> +#include <error.h> +#include <linux/if_ether.h> +#include <linux/if_packet.h> +#include <linux/ip.h> +#include <linux/tcp.h> +#include <linux/virtio_net.h> +#include <net/if.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> +#include <sys/socket.h> +#include <unistd.h> + +#ifndef ETH_P_NSH +#define ETH_P_NSH 0x894F +#endif + +#define NSH_HLEN 8 /* base header + service path header */ +#define NSH_MD_TYPE2 2 /* no mandatory context header */ +#define NSH_NP_IPV4 1 +#define NSH_NP_NSH 4 + +#define GSO_SIZE 500 +#define NR_SEGS 4 + +struct nsh_base { + __be16 ver_flags_ttl_len; + __u8 mdtype; + __u8 np; + __be32 path_hdr; +}; + +int main(int argc, char **argv) +{ + unsigned int depth, hlen, len, i; + struct sockaddr_ll addr = {}; + struct virtio_net_hdr *vnet; + struct nsh_base *nsh; + struct ethhdr *eth; + struct tcphdr *tcp; + int fd, one = 1; + struct iphdr *ip; + char *buf; + + if (argc != 3) + error(1, 0, "usage: %s <ifname> <depth>", argv[0]); + + depth = strtoul(argv[2], NULL, 0); + hlen = sizeof(*eth) + depth * NSH_HLEN + sizeof(*ip) + sizeof(*tcp); + if (!depth || hlen > UINT16_MAX) + error(1, 0, "invalid depth %u", depth); + len = sizeof(*vnet) + hlen + NR_SEGS * GSO_SIZE; + + buf = calloc(1, len); + if (!buf) + error(1, errno, "calloc"); + + vnet = (void *)buf; + vnet->flags = VIRTIO_NET_HDR_F_NEEDS_CSUM; + vnet->gso_type = VIRTIO_NET_HDR_GSO_TCPV4; + vnet->hdr_len = hlen; + vnet->gso_size = GSO_SIZE; + vnet->csum_start = hlen - sizeof(*tcp); + vnet->csum_offset = __builtin_offsetof(struct tcphdr, check); + + eth = (void *)(vnet + 1); + memset(eth->h_dest, 0xff, ETH_ALEN); + eth->h_source[0] = 0x02; + eth->h_proto = htons(ETH_P_NSH); + + nsh = (void *)(eth + 1); + for (i = 0; i < depth; i++, nsh++) { + /* version 0, ttl 63, length in 4 byte words */ + nsh->ver_flags_ttl_len = htons(63 << 6 | NSH_HLEN / 4); + nsh->mdtype = NSH_MD_TYPE2; + nsh->np = i == depth - 1 ? NSH_NP_IPV4 : NSH_NP_NSH; + nsh->path_hdr = htonl(1 << 8 | 255); + } + + ip = (void *)nsh; + ip->version = 4; + ip->ihl = 5; + ip->ttl = 64; + ip->protocol = IPPROTO_TCP; + ip->saddr = htonl(0xc0000201); /* 192.0.2.1 */ + ip->daddr = htonl(0xc0000202); /* 192.0.2.2 */ + + tcp = (void *)(ip + 1); + tcp->source = htons(1234); + tcp->dest = htons(5678); + tcp->doff = 5; + tcp->ack = 1; + + fd = socket(AF_PACKET, SOCK_RAW, 0); + if (fd < 0) + error(1, errno, "socket"); + if (setsockopt(fd, SOL_PACKET, PACKET_VNET_HDR, &one, sizeof(one))) + error(1, errno, "setsockopt PACKET_VNET_HDR"); + + addr.sll_family = AF_PACKET; + addr.sll_protocol = htons(ETH_P_NSH); + addr.sll_ifindex = if_nametoindex(argv[1]); + if (!addr.sll_ifindex) + error(1, errno, "if_nametoindex %s", argv[1]); + + /* A packet dropped at segmentation time is reported as an error, + * this is expected for deep stacks. + */ + sendto(fd, buf, len, 0, (void *)&addr, sizeof(addr)); + + close(fd); + free(buf); + return 0; +} diff --git a/tools/testing/selftests/net/nsh_gso.sh b/tools/testing/selftests/net/nsh_gso.sh new file mode 100755 index 000000000000..d0746cf1d528 --- /dev/null +++ b/tools/testing/selftests/net/nsh_gso.sh @@ -0,0 +1,71 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0 +# +# Check software GSO of NSH packets: a TCP GSO packet behind a few NSH +# headers is segmented, and a deep stack of NSH headers is dropped +# instead of recursing in nsh_gso_segment() for each of them. + +source lib.sh + +NR_SEGS=4 +ret=0 + +cleanup() +{ + cleanup_ns $ns1 +} + +tx_packets() +{ + ip netns exec $ns1 cat /sys/class/net/dummy0/statistics/tx_packets +} + +# $1: number of NSH headers, $2: expected number of transmitted packets +check_nsh_gso() +{ + local depth=$1 + local expected=$2 + local before + local sent + + before=$(tx_packets) + ip netns exec $ns1 ./nsh_gso dummy0 $depth + sent=$(($(tx_packets) - before)) + + if [ $sent -eq $expected ]; then + echo "PASS: $depth NSH headers: $sent packets sent" + else + echo "FAIL: $depth NSH headers: $sent packets sent, expected $expected" + ret=1 + fi +} + +modprobe -q nsh +nsh_loaded=$? + +trap cleanup EXIT +setup_ns ns1 || exit $ksft_skip + +ip -netns $ns1 link add dummy0 type dummy +ip -netns $ns1 link set dummy0 up +# No IPv6 autoconf traffic on the device, to get stable counters. +ip netns exec $ns1 sysctl -qw net.ipv6.conf.dummy0.disable_ipv6=1 +# Force software segmentation in validate_xmit_skb(). +ip netns exec $ns1 ethtool -K dummy0 tso off >/dev/null || exit $ksft_skip + +before=$(tx_packets) +ip netns exec $ns1 ./nsh_gso dummy0 1 +if [ $(($(tx_packets) - before)) -eq 0 ] && [ $nsh_loaded -ne 0 ]; then + echo "SKIP: NSH GSO not available" + exit $ksft_skip +fi + +check_nsh_gso 1 $NR_SEGS +# The recursion limit is shared with the inner IPv4 header. +check_nsh_gso 4 $NR_SEGS +check_nsh_gso 5 0 +check_nsh_gso 64 0 +# The largest stack that fits in a 64KB header. +check_nsh_gso 8000 0 + +exit $ret -- 2.53.0 ^ permalink raw reply related [flat|nested] 7+ messages in thread
* Re: [PATCH net 0/2] nsh: limit recursion in nsh_gso_segment() 2026-11-12 10:19 [PATCH net 0/2] nsh: limit recursion in nsh_gso_segment() Eric Dumazet 2026-11-12 10:20 ` [PATCH net 1/2] " Eric Dumazet 2026-11-12 10:20 ` [PATCH net 2/2] selftests: net: add a test for NSH GSO Eric Dumazet @ 2026-11-12 19:40 ` patchwork-bot+netdevbpf 2 siblings, 0 replies; 7+ messages in thread From: patchwork-bot+netdevbpf @ 2026-11-12 19:40 UTC (permalink / raw) To: Eric Dumazet; +Cc: davem, kuba, pabeni, willemb, horms, netdev Hello: This series was applied to netdev/net.git (main) by Jakub Kicinski <kuba@kernel.org>: On Thu, 12 Nov 2026 11:19:59 +0100 you wrote: > Sashiko found that nsh_gso_segment() can call itself, through > skb_mac_gso_segment(), once per stacked NSH header. Nothing limits the > depth, and a 64KB GSO packet made of 8-byte NSH headers overflows the > kernel stack. It was reported while reviewing an unrelated virtio > series [1]. > > Such a packet can be sent with a packet socket using PACKET_VNET_HDR, > when the nsh module is loaded. > > [...] Here is the summary with links: - [net,1/2] nsh: limit recursion in nsh_gso_segment() https://git.kernel.org/netdev/net/c/3921fb102d78 - [net,2/2] selftests: net: add a test for NSH GSO https://git.kernel.org/netdev/net/c/53e7352d65f7 You are awesome, thank you! -- Deet-doot-dot, I am a bot. https://korg.docs.kernel.org/patchwork/pwbot.html ^ permalink raw reply [flat|nested] 7+ messages in thread
end of thread, other threads:[~2026-11-13 0:03 UTC | newest] Thread overview: 7+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2026-11-12 10:19 [PATCH net 0/2] nsh: limit recursion in nsh_gso_segment() Eric Dumazet 2026-11-12 10:20 ` [PATCH net 1/2] " Eric Dumazet 2026-11-12 10:33 ` Jiri Benc 2026-11-12 22:54 ` David Laight 2026-11-13 0:03 ` Willem de Bruijn 2026-11-12 10:20 ` [PATCH net 2/2] selftests: net: add a test for NSH GSO Eric Dumazet 2026-11-12 19:40 ` [PATCH net 0/2] nsh: limit recursion in nsh_gso_segment() patchwork-bot+netdevbpf
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox