netdev.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [RFC PATCH net v2] net: drop gso udp packets in udp_rcv_segment()
@ 2025-07-29 12:39 Wang Liang
  2025-07-29 17:09 ` Willem de Bruijn
  0 siblings, 1 reply; 3+ messages in thread
From: Wang Liang @ 2025-07-29 12:39 UTC (permalink / raw)
  To: willemdebruijn.kernel, davem, edumazet, kuba, pabeni, horms,
	atenart
  Cc: yuehaibing, zhangchangzhong, wangliang74, netdev, linux-kernel

When sending a packet with virtio_net_hdr to tun device, if the gso_type
in virtio_net_hdr is SKB_GSO_UDP and the gso_size is less than udphdr
size, below crash may happen.

  ------------[ cut here ]------------
  kernel BUG at net/core/skbuff.c:4572!
  Oops: invalid opcode: 0000 [#1] SMP NOPTI
  CPU: 0 UID: 0 PID: 62 Comm: mytest Not tainted 6.16.0-rc7 #203 PREEMPT(voluntary)
  Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.15.0-1 04/01/2014
  RIP: 0010:skb_pull_rcsum+0x8e/0xa0
  Code: 00 00 5b c3 cc cc cc cc 8b 93 88 00 00 00 f7 da e8 37 44 38 00 f7 d8 89 83 88 00 00 00 48 8b 83 c8 00 00 00 5b c3 cc cc cc cc <0f> 0b 0f 0b 66 66 2e 0f 1f 84 00 000
  RSP: 0018:ffffc900001fba38 EFLAGS: 00000297
  RAX: 0000000000000004 RBX: ffff8880040c1000 RCX: ffffc900001fb948
  RDX: ffff888003e6d700 RSI: 0000000000000008 RDI: ffff88800411a062
  RBP: ffff8880040c1000 R08: 0000000000000000 R09: 0000000000000001
  R10: ffff888003606c00 R11: 0000000000000001 R12: 0000000000000000
  R13: ffff888004060900 R14: ffff888004050000 R15: ffff888004060900
  FS:  000000002406d3c0(0000) GS:ffff888084a19000(0000) knlGS:0000000000000000
  CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
  CR2: 0000000020000040 CR3: 0000000004007000 CR4: 00000000000006f0
  Call Trace:
   <TASK>
   udp_queue_rcv_one_skb+0x176/0x4b0 net/ipv4/udp.c:2445
   udp_queue_rcv_skb+0x155/0x1f0 net/ipv4/udp.c:2475
   udp_unicast_rcv_skb+0x71/0x90 net/ipv4/udp.c:2626
   __udp4_lib_rcv+0x433/0xb00 net/ipv4/udp.c:2690
   ip_protocol_deliver_rcu+0xa6/0x160 net/ipv4/ip_input.c:205
   ip_local_deliver_finish+0x72/0x90 net/ipv4/ip_input.c:233
   ip_sublist_rcv_finish+0x5f/0x70 net/ipv4/ip_input.c:579
   ip_sublist_rcv+0x122/0x1b0 net/ipv4/ip_input.c:636
   ip_list_rcv+0xf7/0x130 net/ipv4/ip_input.c:670
   __netif_receive_skb_list_core+0x21d/0x240 net/core/dev.c:6067
   netif_receive_skb_list_internal+0x186/0x2b0 net/core/dev.c:6210
   napi_complete_done+0x78/0x180 net/core/dev.c:6580
   tun_get_user+0xa63/0x1120 drivers/net/tun.c:1909
   tun_chr_write_iter+0x65/0xb0 drivers/net/tun.c:1984
   vfs_write+0x300/0x420 fs/read_write.c:593
   ksys_write+0x60/0xd0 fs/read_write.c:686
   do_syscall_64+0x50/0x1c0 arch/x86/entry/syscall_64.c:63
   </TASK>

To trigger gso segment in udp_queue_rcv_skb(), we should also set option
UDP_ENCAP_ESPINUDP to enable udp_sk(sk)->encap_rcv. When the encap_rcv
hook return 1 in udp_queue_rcv_one_skb(), udp_csum_pull_header() will try
to pull udphdr, but the skb size has been segmented to gso size, which
leads to this crash.

Previous commit cf329aa42b66 ("udp: cope with UDP GRO packet misdirection")
introduces segmentation in UDP receive path only for GRO, which was never
intended to be used for UFO, so drop gso udp packets in udp_rcv_segment().

Fixes: cf329aa42b66 ("udp: cope with UDP GRO packet misdirection")
Fixes: 3d010c8031e3 ("udp: do not accept non-tunnel GSO skbs landing in a tunnel")
Suggested-by: Willem de Bruijn <willemdebruijn.kernel@gmail.com>
Signed-off-by: Wang Liang <wangliang74@huawei.com>
---
v1: https://lore.kernel.org/netdev/20250724083005.3918375-1-wangliang74@huawei.com/
v2: Drop ufo packets instead of checking min gso size.
---
 include/net/udp.h | 22 ++++++++++++++++------
 1 file changed, 16 insertions(+), 6 deletions(-)

diff --git a/include/net/udp.h b/include/net/udp.h
index a772510b2aa5..e3fcda71f6c1 100644
--- a/include/net/udp.h
+++ b/include/net/udp.h
@@ -587,6 +587,14 @@ static inline struct sk_buff *udp_rcv_segment(struct sock *sk,
 {
 	netdev_features_t features = NETIF_F_SG;
 	struct sk_buff *segs;
+	int drop_count = 1;
+
+	/*
+	 * Segmentation in UDP receive path is only for UDP GRO, drop udp
+	 * fragmentation offload (UFO) packets.
+	 */
+	if (skb_shinfo(skb)->gso_type & (SKB_GSO_UDP | SKB_GSO_UDP_L4))
+		goto drop;
 
 	/* Avoid csum recalculation by skb_segment unless userspace explicitly
 	 * asks for the final checksum values
@@ -610,16 +618,18 @@ static inline struct sk_buff *udp_rcv_segment(struct sock *sk,
 	 */
 	segs = __skb_gso_segment(skb, features, false);
 	if (IS_ERR_OR_NULL(segs)) {
-		int segs_nr = skb_shinfo(skb)->gso_segs;
-
-		atomic_add(segs_nr, &sk->sk_drops);
-		SNMP_ADD_STATS(__UDPX_MIB(sk, ipv4), UDP_MIB_INERRORS, segs_nr);
-		kfree_skb(skb);
-		return NULL;
+		drop_count = skb_shinfo(skb)->gso_segs;
+		goto drop;
 	}
 
 	consume_skb(skb);
 	return segs;
+
+drop:
+	atomic_add(drop_count, &sk->sk_drops);
+	SNMP_ADD_STATS(__UDPX_MIB(sk, ipv4), UDP_MIB_INERRORS, drop_count);
+	kfree_skb(skb);
+	return NULL;
 }
 
 static inline void udp_post_segment_fix_csum(struct sk_buff *skb)
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 3+ messages in thread

* Re: [RFC PATCH net v2] net: drop gso udp packets in udp_rcv_segment()
  2025-07-29 12:39 [RFC PATCH net v2] net: drop gso udp packets in udp_rcv_segment() Wang Liang
@ 2025-07-29 17:09 ` Willem de Bruijn
  2025-07-30 10:08   ` Wang Liang
  0 siblings, 1 reply; 3+ messages in thread
From: Willem de Bruijn @ 2025-07-29 17:09 UTC (permalink / raw)
  To: Wang Liang, willemdebruijn.kernel, davem, edumazet, kuba, pabeni,
	horms, atenart
  Cc: yuehaibing, zhangchangzhong, wangliang74, netdev, linux-kernel

Can limit to drop UFO packets, as other GSO packets will get segmented
correctly.

Wang Liang wrote:
> When sending a packet with virtio_net_hdr to tun device, if the gso_type
> in virtio_net_hdr is SKB_GSO_UDP and the gso_size is less than udphdr
> size, below crash may happen.
> 
>   ------------[ cut here ]------------
>   kernel BUG at net/core/skbuff.c:4572!
>   Oops: invalid opcode: 0000 [#1] SMP NOPTI
>   CPU: 0 UID: 0 PID: 62 Comm: mytest Not tainted 6.16.0-rc7 #203 PREEMPT(voluntary)
>   Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.15.0-1 04/01/2014
>   RIP: 0010:skb_pull_rcsum+0x8e/0xa0
>   Code: 00 00 5b c3 cc cc cc cc 8b 93 88 00 00 00 f7 da e8 37 44 38 00 f7 d8 89 83 88 00 00 00 48 8b 83 c8 00 00 00 5b c3 cc cc cc cc <0f> 0b 0f 0b 66 66 2e 0f 1f 84 00 000
>   RSP: 0018:ffffc900001fba38 EFLAGS: 00000297
>   RAX: 0000000000000004 RBX: ffff8880040c1000 RCX: ffffc900001fb948
>   RDX: ffff888003e6d700 RSI: 0000000000000008 RDI: ffff88800411a062
>   RBP: ffff8880040c1000 R08: 0000000000000000 R09: 0000000000000001
>   R10: ffff888003606c00 R11: 0000000000000001 R12: 0000000000000000
>   R13: ffff888004060900 R14: ffff888004050000 R15: ffff888004060900
>   FS:  000000002406d3c0(0000) GS:ffff888084a19000(0000) knlGS:0000000000000000
>   CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
>   CR2: 0000000020000040 CR3: 0000000004007000 CR4: 00000000000006f0
>   Call Trace:
>    <TASK>
>    udp_queue_rcv_one_skb+0x176/0x4b0 net/ipv4/udp.c:2445
>    udp_queue_rcv_skb+0x155/0x1f0 net/ipv4/udp.c:2475
>    udp_unicast_rcv_skb+0x71/0x90 net/ipv4/udp.c:2626
>    __udp4_lib_rcv+0x433/0xb00 net/ipv4/udp.c:2690
>    ip_protocol_deliver_rcu+0xa6/0x160 net/ipv4/ip_input.c:205
>    ip_local_deliver_finish+0x72/0x90 net/ipv4/ip_input.c:233
>    ip_sublist_rcv_finish+0x5f/0x70 net/ipv4/ip_input.c:579
>    ip_sublist_rcv+0x122/0x1b0 net/ipv4/ip_input.c:636
>    ip_list_rcv+0xf7/0x130 net/ipv4/ip_input.c:670
>    __netif_receive_skb_list_core+0x21d/0x240 net/core/dev.c:6067
>    netif_receive_skb_list_internal+0x186/0x2b0 net/core/dev.c:6210
>    napi_complete_done+0x78/0x180 net/core/dev.c:6580
>    tun_get_user+0xa63/0x1120 drivers/net/tun.c:1909
>    tun_chr_write_iter+0x65/0xb0 drivers/net/tun.c:1984
>    vfs_write+0x300/0x420 fs/read_write.c:593
>    ksys_write+0x60/0xd0 fs/read_write.c:686
>    do_syscall_64+0x50/0x1c0 arch/x86/entry/syscall_64.c:63
>    </TASK>
> 
> To trigger gso segment in udp_queue_rcv_skb(), we should also set option
> UDP_ENCAP_ESPINUDP to enable udp_sk(sk)->encap_rcv. When the encap_rcv
> hook return 1 in udp_queue_rcv_one_skb(), udp_csum_pull_header() will try
> to pull udphdr, but the skb size has been segmented to gso size, which
> leads to this crash.
> 
> Previous commit cf329aa42b66 ("udp: cope with UDP GRO packet misdirection")
> introduces segmentation in UDP receive path only for GRO, which was never
> intended to be used for UFO, so drop gso udp packets in udp_rcv_segment().
> 
> Fixes: cf329aa42b66 ("udp: cope with UDP GRO packet misdirection")
> Fixes: 3d010c8031e3 ("udp: do not accept non-tunnel GSO skbs landing in a tunnel")

Only one Fixes tag, to know where to backport too.

The segmentation on receive is introduced in the first commit. I'd
keep only that.

And please add a Link: to the email thread with the analysis of the
bug, your v1 (above the ---).

> Suggested-by: Willem de Bruijn <willemdebruijn.kernel@gmail.com>
> Signed-off-by: Wang Liang <wangliang74@huawei.com>
> ---
> v1: https://lore.kernel.org/netdev/20250724083005.3918375-1-wangliang74@huawei.com/
> v2: Drop ufo packets instead of checking min gso size.
> ---
>  include/net/udp.h | 22 ++++++++++++++++------
>  1 file changed, 16 insertions(+), 6 deletions(-)
> 
> diff --git a/include/net/udp.h b/include/net/udp.h
> index a772510b2aa5..e3fcda71f6c1 100644
> --- a/include/net/udp.h
> +++ b/include/net/udp.h
> @@ -587,6 +587,14 @@ static inline struct sk_buff *udp_rcv_segment(struct sock *sk,
>  {
>  	netdev_features_t features = NETIF_F_SG;
>  	struct sk_buff *segs;
> +	int drop_count = 1;

This is rare, can move initialization into the branch.
> +
> +	/*
> +	 * Segmentation in UDP receive path is only for UDP GRO, drop udp
> +	 * fragmentation offload (UFO) packets.
> +	 */
> +	if (skb_shinfo(skb)->gso_type & (SKB_GSO_UDP | SKB_GSO_UDP_L4))

Only SKB_GSO_UDP.

The purpose of this function is to correctly segment SKB_GSO_UDP_L4
packets.

> +		goto drop;
>  
>  	/* Avoid csum recalculation by skb_segment unless userspace explicitly
>  	 * asks for the final checksum values
> @@ -610,16 +618,18 @@ static inline struct sk_buff *udp_rcv_segment(struct sock *sk,
>  	 */
>  	segs = __skb_gso_segment(skb, features, false);
>  	if (IS_ERR_OR_NULL(segs)) {
> -		int segs_nr = skb_shinfo(skb)->gso_segs;
> -
> -		atomic_add(segs_nr, &sk->sk_drops);
> -		SNMP_ADD_STATS(__UDPX_MIB(sk, ipv4), UDP_MIB_INERRORS, segs_nr);
> -		kfree_skb(skb);
> -		return NULL;
> +		drop_count = skb_shinfo(skb)->gso_segs;
> +		goto drop;
>  	}
>  
>  	consume_skb(skb);
>  	return segs;
> +
> +drop:
> +	atomic_add(drop_count, &sk->sk_drops);
> +	SNMP_ADD_STATS(__UDPX_MIB(sk, ipv4), UDP_MIB_INERRORS, drop_count);
> +	kfree_skb(skb);
> +	return NULL;
>  }
>  
>  static inline void udp_post_segment_fix_csum(struct sk_buff *skb)
> -- 
> 2.34.1
> 



^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: [RFC PATCH net v2] net: drop gso udp packets in udp_rcv_segment()
  2025-07-29 17:09 ` Willem de Bruijn
@ 2025-07-30 10:08   ` Wang Liang
  0 siblings, 0 replies; 3+ messages in thread
From: Wang Liang @ 2025-07-30 10:08 UTC (permalink / raw)
  To: Willem de Bruijn, davem, edumazet, kuba, pabeni, horms, atenart
  Cc: yuehaibing, zhangchangzhong, netdev, linux-kernel


在 2025/7/30 1:09, Willem de Bruijn 写道:
> Can limit to drop UFO packets, as other GSO packets will get segmented
> correctly.
>
> Wang Liang wrote:
>> When sending a packet with virtio_net_hdr to tun device, if the gso_type
>> in virtio_net_hdr is SKB_GSO_UDP and the gso_size is less than udphdr
>> size, below crash may happen.
>>
>>    ------------[ cut here ]------------
>>    kernel BUG at net/core/skbuff.c:4572!
>>    Oops: invalid opcode: 0000 [#1] SMP NOPTI
>>    CPU: 0 UID: 0 PID: 62 Comm: mytest Not tainted 6.16.0-rc7 #203 PREEMPT(voluntary)
>>    Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.15.0-1 04/01/2014
>>    RIP: 0010:skb_pull_rcsum+0x8e/0xa0
>>    Code: 00 00 5b c3 cc cc cc cc 8b 93 88 00 00 00 f7 da e8 37 44 38 00 f7 d8 89 83 88 00 00 00 48 8b 83 c8 00 00 00 5b c3 cc cc cc cc <0f> 0b 0f 0b 66 66 2e 0f 1f 84 00 000
>>    RSP: 0018:ffffc900001fba38 EFLAGS: 00000297
>>    RAX: 0000000000000004 RBX: ffff8880040c1000 RCX: ffffc900001fb948
>>    RDX: ffff888003e6d700 RSI: 0000000000000008 RDI: ffff88800411a062
>>    RBP: ffff8880040c1000 R08: 0000000000000000 R09: 0000000000000001
>>    R10: ffff888003606c00 R11: 0000000000000001 R12: 0000000000000000
>>    R13: ffff888004060900 R14: ffff888004050000 R15: ffff888004060900
>>    FS:  000000002406d3c0(0000) GS:ffff888084a19000(0000) knlGS:0000000000000000
>>    CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
>>    CR2: 0000000020000040 CR3: 0000000004007000 CR4: 00000000000006f0
>>    Call Trace:
>>     <TASK>
>>     udp_queue_rcv_one_skb+0x176/0x4b0 net/ipv4/udp.c:2445
>>     udp_queue_rcv_skb+0x155/0x1f0 net/ipv4/udp.c:2475
>>     udp_unicast_rcv_skb+0x71/0x90 net/ipv4/udp.c:2626
>>     __udp4_lib_rcv+0x433/0xb00 net/ipv4/udp.c:2690
>>     ip_protocol_deliver_rcu+0xa6/0x160 net/ipv4/ip_input.c:205
>>     ip_local_deliver_finish+0x72/0x90 net/ipv4/ip_input.c:233
>>     ip_sublist_rcv_finish+0x5f/0x70 net/ipv4/ip_input.c:579
>>     ip_sublist_rcv+0x122/0x1b0 net/ipv4/ip_input.c:636
>>     ip_list_rcv+0xf7/0x130 net/ipv4/ip_input.c:670
>>     __netif_receive_skb_list_core+0x21d/0x240 net/core/dev.c:6067
>>     netif_receive_skb_list_internal+0x186/0x2b0 net/core/dev.c:6210
>>     napi_complete_done+0x78/0x180 net/core/dev.c:6580
>>     tun_get_user+0xa63/0x1120 drivers/net/tun.c:1909
>>     tun_chr_write_iter+0x65/0xb0 drivers/net/tun.c:1984
>>     vfs_write+0x300/0x420 fs/read_write.c:593
>>     ksys_write+0x60/0xd0 fs/read_write.c:686
>>     do_syscall_64+0x50/0x1c0 arch/x86/entry/syscall_64.c:63
>>     </TASK>
>>
>> To trigger gso segment in udp_queue_rcv_skb(), we should also set option
>> UDP_ENCAP_ESPINUDP to enable udp_sk(sk)->encap_rcv. When the encap_rcv
>> hook return 1 in udp_queue_rcv_one_skb(), udp_csum_pull_header() will try
>> to pull udphdr, but the skb size has been segmented to gso size, which
>> leads to this crash.
>>
>> Previous commit cf329aa42b66 ("udp: cope with UDP GRO packet misdirection")
>> introduces segmentation in UDP receive path only for GRO, which was never
>> intended to be used for UFO, so drop gso udp packets in udp_rcv_segment().
>>
>> Fixes: cf329aa42b66 ("udp: cope with UDP GRO packet misdirection")
>> Fixes: 3d010c8031e3 ("udp: do not accept non-tunnel GSO skbs landing in a tunnel")
> Only one Fixes tag, to know where to backport too.
>
> The segmentation on receive is introduced in the first commit. I'd
> keep only that.
>
> And please add a Link: to the email thread with the analysis of the
> bug, your v1 (above the ---).


Thanks very much for your suggestions! They are helpful and clear.

I have send a v3 patch, please check it. Thanks.

------
Best regards
Wang Liang

>> Suggested-by: Willem de Bruijn <willemdebruijn.kernel@gmail.com>
>> Signed-off-by: Wang Liang <wangliang74@huawei.com>
>> ---
>> v1: https://lore.kernel.org/netdev/20250724083005.3918375-1-wangliang74@huawei.com/
>> v2: Drop ufo packets instead of checking min gso size.
>> ---
>>   include/net/udp.h | 22 ++++++++++++++++------
>>   1 file changed, 16 insertions(+), 6 deletions(-)
>>
>> diff --git a/include/net/udp.h b/include/net/udp.h
>> index a772510b2aa5..e3fcda71f6c1 100644
>> --- a/include/net/udp.h
>> +++ b/include/net/udp.h
>> @@ -587,6 +587,14 @@ static inline struct sk_buff *udp_rcv_segment(struct sock *sk,
>>   {
>>   	netdev_features_t features = NETIF_F_SG;
>>   	struct sk_buff *segs;
>> +	int drop_count = 1;
> This is rare, can move initialization into the branch.
>> +
>> +	/*
>> +	 * Segmentation in UDP receive path is only for UDP GRO, drop udp
>> +	 * fragmentation offload (UFO) packets.
>> +	 */
>> +	if (skb_shinfo(skb)->gso_type & (SKB_GSO_UDP | SKB_GSO_UDP_L4))
> Only SKB_GSO_UDP.
>
> The purpose of this function is to correctly segment SKB_GSO_UDP_L4
> packets.
>
>> +		goto drop;
>>   
>>   	/* Avoid csum recalculation by skb_segment unless userspace explicitly
>>   	 * asks for the final checksum values
>> @@ -610,16 +618,18 @@ static inline struct sk_buff *udp_rcv_segment(struct sock *sk,
>>   	 */
>>   	segs = __skb_gso_segment(skb, features, false);
>>   	if (IS_ERR_OR_NULL(segs)) {
>> -		int segs_nr = skb_shinfo(skb)->gso_segs;
>> -
>> -		atomic_add(segs_nr, &sk->sk_drops);
>> -		SNMP_ADD_STATS(__UDPX_MIB(sk, ipv4), UDP_MIB_INERRORS, segs_nr);
>> -		kfree_skb(skb);
>> -		return NULL;
>> +		drop_count = skb_shinfo(skb)->gso_segs;
>> +		goto drop;
>>   	}
>>   
>>   	consume_skb(skb);
>>   	return segs;
>> +
>> +drop:
>> +	atomic_add(drop_count, &sk->sk_drops);
>> +	SNMP_ADD_STATS(__UDPX_MIB(sk, ipv4), UDP_MIB_INERRORS, drop_count);
>> +	kfree_skb(skb);
>> +	return NULL;
>>   }
>>   
>>   static inline void udp_post_segment_fix_csum(struct sk_buff *skb)
>> -- 
>> 2.34.1
>>
>

^ permalink raw reply	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2025-07-30 10:08 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-07-29 12:39 [RFC PATCH net v2] net: drop gso udp packets in udp_rcv_segment() Wang Liang
2025-07-29 17:09 ` Willem de Bruijn
2025-07-30 10:08   ` Wang Liang

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).