Selaa lähdekoodia

rmnet_core: Strengthen IPv6 fragment check in rmnet_frag_skip_exthdr()

Apparently, 4-6 XLAT scenarios can insert an empty fragment header into the
IPv6 header chain to indicate that fragmentation is supported in the host
IPv4 network. This header contains 0 for both the fragment_offset and MF
fields, so simply checking the value of frag_off passed to the
rmnet_frag_ipv6_skip_exthdr() function is not sufficient to properly catch
this rogue header. Instead, we need to implement a "less clever" version.

kernel oops at net/core/skbuff.c:4217!
Call trace:
 skb_segment+0xcf0/0xd2c
 __udp_gso_segment+0xa4/0x544
 udp6_ufo_fragment+0x2dc/0x344
 ipv6_gso_segment+0x170/0x350
 skb_mac_gso_segment+0xd4/0x1b0
 __skb_gso_segment+0xcc/0x12c
 udp_rcv_segment.76914+0x54/0x16c
 udpv6_queue_rcv_skb+0x78/0x148
 __udp6_lib_rcv+0x38c/0x4cc
 udpv6_rcv+0x20/0x30
 ip6_protocol_deliver_rcu+0x3c0/0x63c
 ip6_input+0x60/0x184
 ip6_rcv_finish+0x84/0x150
 ipv6_rcv+0x5c/0x14c
 __netif_receive_skb+0x80/0x184

CRs-Fixed: 3358773
Change-Id: Ica2779b1da17bc46d397b42283369f3750edbf82
Signed-off-by: Sean Tranchetti <[email protected]>
Sean Tranchetti 2 vuotta sitten
vanhempi
sitoutus
6606b6d6cf
2 muutettua tiedostoa jossa 25 lisäystä ja 9 poistoa
  1. 22 8
      core/rmnet_descriptor.c
  2. 3 1
      core/rmnet_descriptor.h

+ 22 - 8
core/rmnet_descriptor.c

@@ -339,12 +339,13 @@ int rmnet_frag_descriptor_add_frags_from(struct rmnet_frag_descriptor *to,
 EXPORT_SYMBOL(rmnet_frag_descriptor_add_frags_from);
 
 int rmnet_frag_ipv6_skip_exthdr(struct rmnet_frag_descriptor *frag_desc,
-				int start, u8 *nexthdrp, __be16 *fragp)
+				int start, u8 *nexthdrp, __be16 *frag_offp,
+				bool *frag_hdrp)
 {
 	u8 nexthdr = *nexthdrp;
 
-	*fragp = 0;
-
+	*frag_offp = 0;
+	*frag_hdrp = false;
 	while (ipv6_ext_hdr(nexthdr)) {
 		struct ipv6_opt_hdr *hp, __hp;
 		int hdrlen;
@@ -366,8 +367,9 @@ int rmnet_frag_ipv6_skip_exthdr(struct rmnet_frag_descriptor *frag_desc,
 			if (!fp)
 				return -EINVAL;
 
-			*fragp = *fp;
-			if (ntohs(*fragp) & ~0x7)
+			*frag_offp = *fp;
+			*frag_hdrp = true;
+			if (ntohs(*frag_offp) & ~0x7)
 				break;
 			hdrlen = 8;
 		} else if (nexthdr == NEXTHDR_AUTH) {
@@ -1335,6 +1337,7 @@ rmnet_frag_segment_coal_data(struct rmnet_frag_descriptor *coal_desc,
 		struct ipv6hdr *ip6h, __ip6h;
 		int ip_len;
 		__be16 frag_off;
+		bool frag_hdr;
 		u8 protocol;
 
 		ip6h = rmnet_frag_header_ptr(coal_desc, 0, sizeof(*ip6h),
@@ -1347,10 +1350,11 @@ rmnet_frag_segment_coal_data(struct rmnet_frag_descriptor *coal_desc,
 		ip_len = rmnet_frag_ipv6_skip_exthdr(coal_desc,
 						     sizeof(*ip6h),
 						     &protocol,
-						     &frag_off);
+						     &frag_off,
+						     &frag_hdr);
 		coal_desc->trans_proto = protocol;
 
-		/* If we run into a problem, or this has a fragment header
+		/* If we run into a problem, or this is fragmented packet
 		 * (which should technically not be possible, if the HW
 		 * works as intended...), bail.
 		 */
@@ -1359,6 +1363,14 @@ rmnet_frag_segment_coal_data(struct rmnet_frag_descriptor *coal_desc,
 			return;
 		}
 
+		if (frag_hdr) {
+			/* There is a fragment header, but this is not a
+			 * fragmented packet. We can handle this, but it
+			 * cannot be coalesced because of kernel limitations.
+			 */
+			gro = false;
+		}
+
 		coal_desc->ip_len = (u16)ip_len;
 		if (coal_desc->ip_len > sizeof(*ip6h)) {
 			/* Don't allow coalescing of any packets with IPv6
@@ -1644,6 +1656,7 @@ static int rmnet_frag_checksum_pkt(struct rmnet_frag_descriptor *frag_desc)
 		struct ipv6hdr *ip6h, __ip6h;
 		int ip_len;
 		__be16 frag_off;
+		bool frag_hdr;
 		u8 protocol;
 
 		ip6h = rmnet_frag_header_ptr(frag_desc, offset, sizeof(*ip6h),
@@ -1655,7 +1668,8 @@ static int rmnet_frag_checksum_pkt(struct rmnet_frag_descriptor *frag_desc)
 		protocol = ip6h->nexthdr;
 		ip_len = rmnet_frag_ipv6_skip_exthdr(frag_desc,
 						     offset + sizeof(*ip6h),
-						     &protocol, &frag_off);
+						     &protocol, &frag_off,
+						     &frag_hdr);
 		if (ip_len < 0 || frag_off)
 			return -EINVAL;
 

+ 3 - 1
core/rmnet_descriptor.h

@@ -1,4 +1,5 @@
 /* Copyright (c) 2013-2021, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -78,7 +79,8 @@ int rmnet_frag_descriptor_add_frags_from(struct rmnet_frag_descriptor *to,
 					 struct rmnet_frag_descriptor *from,
 					 u32 off, u32 len);
 int rmnet_frag_ipv6_skip_exthdr(struct rmnet_frag_descriptor *frag_desc,
-				int start, u8 *nexthdrp, __be16 *fragp);
+				int start, u8 *nexthdrp, __be16 *frag_offp,
+				bool *frag_hdrp);
 
 /* QMAP command packets */
 void rmnet_frag_command(struct rmnet_frag_descriptor *frag_desc,