The Linux Kernel Mailing List
 help / color / mirror / Atom feed
* [PATCH net v2] net: qualcomm: rmnet: validate MAP frame length before ingress parsing
@ 2026-06-30 17:41 Xiang Mei
  2026-06-30 17:43 ` Xiang Mei
  2026-07-01  7:03 ` subash.a.kasiviswanathan
  0 siblings, 2 replies; 3+ messages in thread
From: Xiang Mei @ 2026-06-30 17:41 UTC (permalink / raw)
  To: subash.a.kasiviswanathan, sean.tranchetti, netdev
  Cc: andrew+netdev, davem, edumazet, kuba, pabeni, linux-kernel,
	bestswngs, Xiang Mei

When ingress deaggregation is disabled, rmnet_map_ingress_handler() passes
the skb straight to __rmnet_map_ingress_handler(), skipping the length
validation that rmnet_map_deaggregate() performs on the aggregated path.
The parser then dereferences the MAP header and csum header/trailer based on
the on-wire pkt_len without checking skb->len, so a short frame is read out
of bounds:

  BUG: KASAN: slab-out-of-bounds in rmnet_map_checksum_downlink_packet
  Read of size 1 at addr ffff88801118ed00 by task exploit/147
  Call Trace:
   ...
   rmnet_map_checksum_downlink_packet (drivers/net/ethernet/qualcomm/rmnet/rmnet_map_data.c:413)
   __rmnet_map_ingress_handler (drivers/net/ethernet/qualcomm/rmnet/rmnet_handlers.c:96)
   rmnet_rx_handler (drivers/net/ethernet/qualcomm/rmnet/rmnet_handlers.c:129)
   __netif_receive_skb_core.constprop.0 (net/core/dev.c:6089)
   netif_receive_skb (net/core/dev.c:6460)
   tun_get_user (drivers/net/tun.c:1955)
   tun_chr_write_iter (drivers/net/tun.c:2001)
   vfs_write (fs/read_write.c:688)
   ksys_write (fs/read_write.c:740)
   do_syscall_64 (arch/x86/entry/syscall_64.c:94)
   ...

Factor that validation out of rmnet_map_deaggregate() into
rmnet_map_validate_packet_len() and run it on the no-aggregation path too.
The MAP header is bounds-checked first, since this path can receive a frame
shorter than the header.

Fixes: ceed73a2cf4a ("drivers: net: ethernet: qualcomm: rmnet: Initial implementation")
Reported-by: Weiming Shi <bestswngs@gmail.com>
Suggested-by: Subash Abhinov Kasiviswanathan <subash.a.kasiviswanathan@oss.qualcomm.com>
Signed-off-by: Xiang Mei <xmei5@asu.edu>
---
v2: Validate on the no-aggregation path by reusing the deaggregation
    length checks (factored into rmnet_map_validate_packet_len()) instead
    of adding separate pskb_may_pull() guards in __rmnet_map_ingress_handler().

 .../ethernet/qualcomm/rmnet/rmnet_handlers.c  |  5 +-
 .../net/ethernet/qualcomm/rmnet/rmnet_map.h   |  1 +
 .../ethernet/qualcomm/rmnet/rmnet_map_data.c  | 72 ++++++++++---------
 3 files changed, 45 insertions(+), 33 deletions(-)

diff --git a/drivers/net/ethernet/qualcomm/rmnet/rmnet_handlers.c b/drivers/net/ethernet/qualcomm/rmnet/rmnet_handlers.c
index 9f3479500f85..d055a2628d8c 100644
--- a/drivers/net/ethernet/qualcomm/rmnet/rmnet_handlers.c
+++ b/drivers/net/ethernet/qualcomm/rmnet/rmnet_handlers.c
@@ -126,7 +126,10 @@ rmnet_map_ingress_handler(struct sk_buff *skb,
 
 		consume_skb(skb);
 	} else {
-		__rmnet_map_ingress_handler(skb, port);
+		if (rmnet_map_validate_packet_len(skb, port))
+			__rmnet_map_ingress_handler(skb, port);
+		else
+			kfree_skb(skb);
 	}
 }
 
diff --git a/drivers/net/ethernet/qualcomm/rmnet/rmnet_map.h b/drivers/net/ethernet/qualcomm/rmnet/rmnet_map.h
index b70284095568..60ca8b780c88 100644
--- a/drivers/net/ethernet/qualcomm/rmnet/rmnet_map.h
+++ b/drivers/net/ethernet/qualcomm/rmnet/rmnet_map.h
@@ -59,5 +59,6 @@ void rmnet_map_tx_aggregate_init(struct rmnet_port *port);
 void rmnet_map_tx_aggregate_exit(struct rmnet_port *port);
 void rmnet_map_update_ul_agg_config(struct rmnet_port *port, u32 size,
 				    u32 count, u32 time);
+u32 rmnet_map_validate_packet_len(struct sk_buff *skb, struct rmnet_port *port);
 
 #endif /* _RMNET_MAP_H_ */
diff --git a/drivers/net/ethernet/qualcomm/rmnet/rmnet_map_data.c b/drivers/net/ethernet/qualcomm/rmnet/rmnet_map_data.c
index 8b4640c5d61e..305ae15ae8f3 100644
--- a/drivers/net/ethernet/qualcomm/rmnet/rmnet_map_data.c
+++ b/drivers/net/ethernet/qualcomm/rmnet/rmnet_map_data.c
@@ -333,54 +333,62 @@ struct rmnet_map_header *rmnet_map_add_map_header(struct sk_buff *skb,
 	return map_header;
 }
 
-/* Deaggregates a single packet
- * A whole new buffer is allocated for each portion of an aggregated frame.
- * Caller should keep calling deaggregate() on the source skb until 0 is
- * returned, indicating that there are no more packets to deaggregate. Caller
- * is responsible for freeing the original skb.
- */
-struct sk_buff *rmnet_map_deaggregate(struct sk_buff *skb,
-				      struct rmnet_port *port)
+u32 rmnet_map_validate_packet_len(struct sk_buff *skb, struct rmnet_port *port)
 {
 	struct rmnet_map_v5_csum_header *next_hdr = NULL;
 	struct rmnet_map_header *maph;
 	void *data = skb->data;
-	struct sk_buff *skbn;
-	u8 nexthdr_type;
 	u32 packet_len;
 
-	if (skb->len == 0)
-		return NULL;
+	if (skb->len < sizeof(*maph))
+		return 0;
 
 	maph = (struct rmnet_map_header *)skb->data;
+
+	/* Some hardware can send us empty frames. Catch them */
+	if (!maph->pkt_len)
+		return 0;
+
 	packet_len = ntohs(maph->pkt_len) + sizeof(*maph);
 
 	if (port->data_format & RMNET_FLAGS_INGRESS_MAP_CKSUMV4) {
 		packet_len += sizeof(struct rmnet_map_dl_csum_trailer);
-	} else if (port->data_format & RMNET_FLAGS_INGRESS_MAP_CKSUMV5) {
-		if (!(maph->flags & MAP_CMD_FLAG)) {
-			packet_len += sizeof(*next_hdr);
-			if (maph->flags & MAP_NEXT_HEADER_FLAG)
-				next_hdr = data + sizeof(*maph);
-			else
-				/* Mapv5 data pkt without csum hdr is invalid */
-				return NULL;
-		}
+	} else if ((port->data_format & RMNET_FLAGS_INGRESS_MAP_CKSUMV5) &&
+		   !(maph->flags & MAP_CMD_FLAG)) {
+		/* Mapv5 data pkt without csum hdr is invalid */
+		if (!(maph->flags & MAP_NEXT_HEADER_FLAG))
+			return 0;
+
+		packet_len += sizeof(*next_hdr);
+		next_hdr = data + sizeof(*maph);
 	}
 
-	if (((int)skb->len - (int)packet_len) < 0)
-		return NULL;
+	if (skb->len < packet_len)
+		return 0;
 
-	/* Some hardware can send us empty frames. Catch them */
-	if (!maph->pkt_len)
-		return NULL;
+	if (next_hdr &&
+	    u8_get_bits(next_hdr->header_info, MAPV5_HDRINFO_HDR_TYPE_FMASK) !=
+	    RMNET_MAP_HEADER_TYPE_CSUM_OFFLOAD)
+		return 0;
 
-	if (next_hdr) {
-		nexthdr_type = u8_get_bits(next_hdr->header_info,
-					   MAPV5_HDRINFO_HDR_TYPE_FMASK);
-		if (nexthdr_type != RMNET_MAP_HEADER_TYPE_CSUM_OFFLOAD)
-			return NULL;
-	}
+	return packet_len;
+}
+
+/* Deaggregates a single packet
+ * A whole new buffer is allocated for each portion of an aggregated frame.
+ * Caller should keep calling deaggregate() on the source skb until 0 is
+ * returned, indicating that there are no more packets to deaggregate. Caller
+ * is responsible for freeing the original skb.
+ */
+struct sk_buff *rmnet_map_deaggregate(struct sk_buff *skb,
+				      struct rmnet_port *port)
+{
+	struct sk_buff *skbn;
+	u32 packet_len;
+
+	packet_len = rmnet_map_validate_packet_len(skb, port);
+	if (!packet_len)
+		return NULL;
 
 	skbn = alloc_skb(packet_len + RMNET_MAP_DEAGGR_SPACING, GFP_ATOMIC);
 	if (!skbn)
-- 
2.43.0


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

* Re: [PATCH net v2] net: qualcomm: rmnet: validate MAP frame length before ingress parsing
  2026-06-30 17:41 [PATCH net v2] net: qualcomm: rmnet: validate MAP frame length before ingress parsing Xiang Mei
@ 2026-06-30 17:43 ` Xiang Mei
  2026-07-01  7:03 ` subash.a.kasiviswanathan
  1 sibling, 0 replies; 3+ messages in thread
From: Xiang Mei @ 2026-06-30 17:43 UTC (permalink / raw)
  To: subash.a.kasiviswanathan
  Cc: sean.tranchetti, netdev, andrew+netdev, davem, edumazet, kuba,
	pabeni, linux-kernel, bestswngs

Thanks so much for your refactor. It's better! I took it for v2 with
two small changes:

- Dropped the goto err1/err0; the helper has no cleanup, so it just returns
  0 or packet_len directly. (err0 was the success path anyway.)

- The no-agg caller leaks the skb when validation fails, since
  __rmnet_map_ingress_handler() is the one that owns and frees it:

if (rmnet_map_validate_packet_len(skb, port))
__rmnet_map_ingress_handler(skb, port);
else
kfree_skb(skb);

I also bounds-check the MAP header up front (skb->len < sizeof(*maph)),
since the no-agg path can get a frame shorter than the header before
maph->pkt_len is read.

v2 sent with you as Suggested-by. Tested with the original repro: OOB gone
on the no-agg path, valid frames and the deagg path unaffected.

Xiang

On Tue, Jun 30, 2026 at 10:41 AM Xiang Mei <xmei5@asu.edu> wrote:
>
> When ingress deaggregation is disabled, rmnet_map_ingress_handler() passes
> the skb straight to __rmnet_map_ingress_handler(), skipping the length
> validation that rmnet_map_deaggregate() performs on the aggregated path.
> The parser then dereferences the MAP header and csum header/trailer based on
> the on-wire pkt_len without checking skb->len, so a short frame is read out
> of bounds:
>
>   BUG: KASAN: slab-out-of-bounds in rmnet_map_checksum_downlink_packet
>   Read of size 1 at addr ffff88801118ed00 by task exploit/147
>   Call Trace:
>    ...
>    rmnet_map_checksum_downlink_packet (drivers/net/ethernet/qualcomm/rmnet/rmnet_map_data.c:413)
>    __rmnet_map_ingress_handler (drivers/net/ethernet/qualcomm/rmnet/rmnet_handlers.c:96)
>    rmnet_rx_handler (drivers/net/ethernet/qualcomm/rmnet/rmnet_handlers.c:129)
>    __netif_receive_skb_core.constprop.0 (net/core/dev.c:6089)
>    netif_receive_skb (net/core/dev.c:6460)
>    tun_get_user (drivers/net/tun.c:1955)
>    tun_chr_write_iter (drivers/net/tun.c:2001)
>    vfs_write (fs/read_write.c:688)
>    ksys_write (fs/read_write.c:740)
>    do_syscall_64 (arch/x86/entry/syscall_64.c:94)
>    ...
>
> Factor that validation out of rmnet_map_deaggregate() into
> rmnet_map_validate_packet_len() and run it on the no-aggregation path too.
> The MAP header is bounds-checked first, since this path can receive a frame
> shorter than the header.
>
> Fixes: ceed73a2cf4a ("drivers: net: ethernet: qualcomm: rmnet: Initial implementation")
> Reported-by: Weiming Shi <bestswngs@gmail.com>
> Suggested-by: Subash Abhinov Kasiviswanathan <subash.a.kasiviswanathan@oss.qualcomm.com>
> Signed-off-by: Xiang Mei <xmei5@asu.edu>
> ---
> v2: Validate on the no-aggregation path by reusing the deaggregation
>     length checks (factored into rmnet_map_validate_packet_len()) instead
>     of adding separate pskb_may_pull() guards in __rmnet_map_ingress_handler().
>
>  .../ethernet/qualcomm/rmnet/rmnet_handlers.c  |  5 +-
>  .../net/ethernet/qualcomm/rmnet/rmnet_map.h   |  1 +
>  .../ethernet/qualcomm/rmnet/rmnet_map_data.c  | 72 ++++++++++---------
>  3 files changed, 45 insertions(+), 33 deletions(-)
>
> diff --git a/drivers/net/ethernet/qualcomm/rmnet/rmnet_handlers.c b/drivers/net/ethernet/qualcomm/rmnet/rmnet_handlers.c
> index 9f3479500f85..d055a2628d8c 100644
> --- a/drivers/net/ethernet/qualcomm/rmnet/rmnet_handlers.c
> +++ b/drivers/net/ethernet/qualcomm/rmnet/rmnet_handlers.c
> @@ -126,7 +126,10 @@ rmnet_map_ingress_handler(struct sk_buff *skb,
>
>                 consume_skb(skb);
>         } else {
> -               __rmnet_map_ingress_handler(skb, port);
> +               if (rmnet_map_validate_packet_len(skb, port))
> +                       __rmnet_map_ingress_handler(skb, port);
> +               else
> +                       kfree_skb(skb);
>         }
>  }
>
> diff --git a/drivers/net/ethernet/qualcomm/rmnet/rmnet_map.h b/drivers/net/ethernet/qualcomm/rmnet/rmnet_map.h
> index b70284095568..60ca8b780c88 100644
> --- a/drivers/net/ethernet/qualcomm/rmnet/rmnet_map.h
> +++ b/drivers/net/ethernet/qualcomm/rmnet/rmnet_map.h
> @@ -59,5 +59,6 @@ void rmnet_map_tx_aggregate_init(struct rmnet_port *port);
>  void rmnet_map_tx_aggregate_exit(struct rmnet_port *port);
>  void rmnet_map_update_ul_agg_config(struct rmnet_port *port, u32 size,
>                                     u32 count, u32 time);
> +u32 rmnet_map_validate_packet_len(struct sk_buff *skb, struct rmnet_port *port);
>
>  #endif /* _RMNET_MAP_H_ */
> diff --git a/drivers/net/ethernet/qualcomm/rmnet/rmnet_map_data.c b/drivers/net/ethernet/qualcomm/rmnet/rmnet_map_data.c
> index 8b4640c5d61e..305ae15ae8f3 100644
> --- a/drivers/net/ethernet/qualcomm/rmnet/rmnet_map_data.c
> +++ b/drivers/net/ethernet/qualcomm/rmnet/rmnet_map_data.c
> @@ -333,54 +333,62 @@ struct rmnet_map_header *rmnet_map_add_map_header(struct sk_buff *skb,
>         return map_header;
>  }
>
> -/* Deaggregates a single packet
> - * A whole new buffer is allocated for each portion of an aggregated frame.
> - * Caller should keep calling deaggregate() on the source skb until 0 is
> - * returned, indicating that there are no more packets to deaggregate. Caller
> - * is responsible for freeing the original skb.
> - */
> -struct sk_buff *rmnet_map_deaggregate(struct sk_buff *skb,
> -                                     struct rmnet_port *port)
> +u32 rmnet_map_validate_packet_len(struct sk_buff *skb, struct rmnet_port *port)
>  {
>         struct rmnet_map_v5_csum_header *next_hdr = NULL;
>         struct rmnet_map_header *maph;
>         void *data = skb->data;
> -       struct sk_buff *skbn;
> -       u8 nexthdr_type;
>         u32 packet_len;
>
> -       if (skb->len == 0)
> -               return NULL;
> +       if (skb->len < sizeof(*maph))
> +               return 0;
>
>         maph = (struct rmnet_map_header *)skb->data;
> +
> +       /* Some hardware can send us empty frames. Catch them */
> +       if (!maph->pkt_len)
> +               return 0;
> +
>         packet_len = ntohs(maph->pkt_len) + sizeof(*maph);
>
>         if (port->data_format & RMNET_FLAGS_INGRESS_MAP_CKSUMV4) {
>                 packet_len += sizeof(struct rmnet_map_dl_csum_trailer);
> -       } else if (port->data_format & RMNET_FLAGS_INGRESS_MAP_CKSUMV5) {
> -               if (!(maph->flags & MAP_CMD_FLAG)) {
> -                       packet_len += sizeof(*next_hdr);
> -                       if (maph->flags & MAP_NEXT_HEADER_FLAG)
> -                               next_hdr = data + sizeof(*maph);
> -                       else
> -                               /* Mapv5 data pkt without csum hdr is invalid */
> -                               return NULL;
> -               }
> +       } else if ((port->data_format & RMNET_FLAGS_INGRESS_MAP_CKSUMV5) &&
> +                  !(maph->flags & MAP_CMD_FLAG)) {
> +               /* Mapv5 data pkt without csum hdr is invalid */
> +               if (!(maph->flags & MAP_NEXT_HEADER_FLAG))
> +                       return 0;
> +
> +               packet_len += sizeof(*next_hdr);
> +               next_hdr = data + sizeof(*maph);
>         }
>
> -       if (((int)skb->len - (int)packet_len) < 0)
> -               return NULL;
> +       if (skb->len < packet_len)
> +               return 0;
>
> -       /* Some hardware can send us empty frames. Catch them */
> -       if (!maph->pkt_len)
> -               return NULL;
> +       if (next_hdr &&
> +           u8_get_bits(next_hdr->header_info, MAPV5_HDRINFO_HDR_TYPE_FMASK) !=
> +           RMNET_MAP_HEADER_TYPE_CSUM_OFFLOAD)
> +               return 0;
>
> -       if (next_hdr) {
> -               nexthdr_type = u8_get_bits(next_hdr->header_info,
> -                                          MAPV5_HDRINFO_HDR_TYPE_FMASK);
> -               if (nexthdr_type != RMNET_MAP_HEADER_TYPE_CSUM_OFFLOAD)
> -                       return NULL;
> -       }
> +       return packet_len;
> +}
> +
> +/* Deaggregates a single packet
> + * A whole new buffer is allocated for each portion of an aggregated frame.
> + * Caller should keep calling deaggregate() on the source skb until 0 is
> + * returned, indicating that there are no more packets to deaggregate. Caller
> + * is responsible for freeing the original skb.
> + */
> +struct sk_buff *rmnet_map_deaggregate(struct sk_buff *skb,
> +                                     struct rmnet_port *port)
> +{
> +       struct sk_buff *skbn;
> +       u32 packet_len;
> +
> +       packet_len = rmnet_map_validate_packet_len(skb, port);
> +       if (!packet_len)
> +               return NULL;
>
>         skbn = alloc_skb(packet_len + RMNET_MAP_DEAGGR_SPACING, GFP_ATOMIC);
>         if (!skbn)
> --
> 2.43.0
>

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

* RE: [PATCH net v2] net: qualcomm: rmnet: validate MAP frame length before ingress parsing
  2026-06-30 17:41 [PATCH net v2] net: qualcomm: rmnet: validate MAP frame length before ingress parsing Xiang Mei
  2026-06-30 17:43 ` Xiang Mei
@ 2026-07-01  7:03 ` subash.a.kasiviswanathan
  1 sibling, 0 replies; 3+ messages in thread
From: subash.a.kasiviswanathan @ 2026-07-01  7:03 UTC (permalink / raw)
  To: 'Xiang Mei', sean.tranchetti, netdev
  Cc: andrew+netdev, davem, edumazet, kuba, pabeni, linux-kernel,
	bestswngs

> -----Original Message-----
> From: Xiang Mei <xmei5@asu.edu>
> Sent: Tuesday, June 30, 2026 11:41 AM
> To: subash.a.kasiviswanathan@oss.qualcomm.com;
> sean.tranchetti@oss.qualcomm.com; netdev@vger.kernel.org
> Cc: andrew+netdev@lunn.ch; davem@davemloft.net;
> edumazet@google.com; kuba@kernel.org; pabeni@redhat.com; linux-
> kernel@vger.kernel.org; bestswngs@gmail.com; Xiang Mei <xmei5@asu.edu>
> Subject: [PATCH net v2] net: qualcomm: rmnet: validate MAP frame length
> before ingress parsing
> 
> When ingress deaggregation is disabled, rmnet_map_ingress_handler() passes
> the skb straight to __rmnet_map_ingress_handler(), skipping the length
> validation that rmnet_map_deaggregate() performs on the aggregated path.
> The parser then dereferences the MAP header and csum header/trailer based
> on
> the on-wire pkt_len without checking skb->len, so a short frame is read
out
> of bounds:
> 
>   BUG: KASAN: slab-out-of-bounds in
> rmnet_map_checksum_downlink_packet
>   Read of size 1 at addr ffff88801118ed00 by task exploit/147
>   Call Trace:
>    ...
>    rmnet_map_checksum_downlink_packet
> (drivers/net/ethernet/qualcomm/rmnet/rmnet_map_data.c:413)
>    __rmnet_map_ingress_handler
> (drivers/net/ethernet/qualcomm/rmnet/rmnet_handlers.c:96)
>    rmnet_rx_handler
> (drivers/net/ethernet/qualcomm/rmnet/rmnet_handlers.c:129)
>    __netif_receive_skb_core.constprop.0 (net/core/dev.c:6089)
>    netif_receive_skb (net/core/dev.c:6460)
>    tun_get_user (drivers/net/tun.c:1955)
>    tun_chr_write_iter (drivers/net/tun.c:2001)
>    vfs_write (fs/read_write.c:688)
>    ksys_write (fs/read_write.c:740)
>    do_syscall_64 (arch/x86/entry/syscall_64.c:94)
>    ...
> 
> Factor that validation out of rmnet_map_deaggregate() into
> rmnet_map_validate_packet_len() and run it on the no-aggregation path too.
> The MAP header is bounds-checked first, since this path can receive a
frame
> shorter than the header.
> 
> Fixes: ceed73a2cf4a ("drivers: net: ethernet: qualcomm: rmnet: Initial
> implementation")
> Reported-by: Weiming Shi <bestswngs@gmail.com>
> Suggested-by: Subash Abhinov Kasiviswanathan
> <subash.a.kasiviswanathan@oss.qualcomm.com>
> Signed-off-by: Xiang Mei <xmei5@asu.edu>
> ---
> v2: Validate on the no-aggregation path by reusing the deaggregation
>     length checks (factored into rmnet_map_validate_packet_len()) instead
>     of adding separate pskb_may_pull() guards in
> __rmnet_map_ingress_handler().
> 
>  .../ethernet/qualcomm/rmnet/rmnet_handlers.c  |  5 +-
>  .../net/ethernet/qualcomm/rmnet/rmnet_map.h   |  1 +
>  .../ethernet/qualcomm/rmnet/rmnet_map_data.c  | 72 ++++++++++---------
>  3 files changed, 45 insertions(+), 33 deletions(-)
> 
> diff --git a/drivers/net/ethernet/qualcomm/rmnet/rmnet_handlers.c
> b/drivers/net/ethernet/qualcomm/rmnet/rmnet_handlers.c
> index 9f3479500f85..d055a2628d8c 100644
> --- a/drivers/net/ethernet/qualcomm/rmnet/rmnet_handlers.c
> +++ b/drivers/net/ethernet/qualcomm/rmnet/rmnet_handlers.c
> @@ -126,7 +126,10 @@ rmnet_map_ingress_handler(struct sk_buff *skb,
> 
>  		consume_skb(skb);
>  	} else {
> -		__rmnet_map_ingress_handler(skb, port);
> +		if (rmnet_map_validate_packet_len(skb, port))
> +			__rmnet_map_ingress_handler(skb, port);
> +		else
> +			kfree_skb(skb);
>  	}
>  }
> 
> diff --git a/drivers/net/ethernet/qualcomm/rmnet/rmnet_map.h
> b/drivers/net/ethernet/qualcomm/rmnet/rmnet_map.h
> index b70284095568..60ca8b780c88 100644
> --- a/drivers/net/ethernet/qualcomm/rmnet/rmnet_map.h
> +++ b/drivers/net/ethernet/qualcomm/rmnet/rmnet_map.h
> @@ -59,5 +59,6 @@ void rmnet_map_tx_aggregate_init(struct rmnet_port
> *port);
>  void rmnet_map_tx_aggregate_exit(struct rmnet_port *port);
>  void rmnet_map_update_ul_agg_config(struct rmnet_port *port, u32 size,
>  				    u32 count, u32 time);
> +u32 rmnet_map_validate_packet_len(struct sk_buff *skb, struct rmnet_port
> *port);
> 
>  #endif /* _RMNET_MAP_H_ */
> diff --git a/drivers/net/ethernet/qualcomm/rmnet/rmnet_map_data.c
> b/drivers/net/ethernet/qualcomm/rmnet/rmnet_map_data.c
> index 8b4640c5d61e..305ae15ae8f3 100644
> --- a/drivers/net/ethernet/qualcomm/rmnet/rmnet_map_data.c
> +++ b/drivers/net/ethernet/qualcomm/rmnet/rmnet_map_data.c
> @@ -333,54 +333,62 @@ struct rmnet_map_header
> *rmnet_map_add_map_header(struct sk_buff *skb,
>  	return map_header;
>  }
> 
> -/* Deaggregates a single packet
> - * A whole new buffer is allocated for each portion of an aggregated
frame.
> - * Caller should keep calling deaggregate() on the source skb until 0 is
> - * returned, indicating that there are no more packets to deaggregate.
Caller
> - * is responsible for freeing the original skb.
> - */
> -struct sk_buff *rmnet_map_deaggregate(struct sk_buff *skb,
> -				      struct rmnet_port *port)
> +u32 rmnet_map_validate_packet_len(struct sk_buff *skb, struct rmnet_port
> *port)
>  {
>  	struct rmnet_map_v5_csum_header *next_hdr = NULL;
>  	struct rmnet_map_header *maph;
>  	void *data = skb->data;
> -	struct sk_buff *skbn;
> -	u8 nexthdr_type;
>  	u32 packet_len;
> 
> -	if (skb->len == 0)
> -		return NULL;
> +	if (skb->len < sizeof(*maph))
> +		return 0;
> 
>  	maph = (struct rmnet_map_header *)skb->data;
> +
> +	/* Some hardware can send us empty frames. Catch them */
> +	if (!maph->pkt_len)
> +		return 0;
> +
>  	packet_len = ntohs(maph->pkt_len) + sizeof(*maph);
> 
>  	if (port->data_format & RMNET_FLAGS_INGRESS_MAP_CKSUMV4) {
>  		packet_len += sizeof(struct rmnet_map_dl_csum_trailer);
> -	} else if (port->data_format &
> RMNET_FLAGS_INGRESS_MAP_CKSUMV5) {
> -		if (!(maph->flags & MAP_CMD_FLAG)) {
> -			packet_len += sizeof(*next_hdr);
> -			if (maph->flags & MAP_NEXT_HEADER_FLAG)
> -				next_hdr = data + sizeof(*maph);
> -			else
> -				/* Mapv5 data pkt without csum hdr is
invalid
> */
> -				return NULL;
> -		}
> +	} else if ((port->data_format &
> RMNET_FLAGS_INGRESS_MAP_CKSUMV5) &&
> +		   !(maph->flags & MAP_CMD_FLAG)) {
> +		/* Mapv5 data pkt without csum hdr is invalid */
> +		if (!(maph->flags & MAP_NEXT_HEADER_FLAG))
> +			return 0;
> +
> +		packet_len += sizeof(*next_hdr);
> +		next_hdr = data + sizeof(*maph);
>  	}
> 
> -	if (((int)skb->len - (int)packet_len) < 0)
> -		return NULL;
> +	if (skb->len < packet_len)
> +		return 0;
> 
> -	/* Some hardware can send us empty frames. Catch them */
> -	if (!maph->pkt_len)
> -		return NULL;
> +	if (next_hdr &&
> +	    u8_get_bits(next_hdr->header_info,
> MAPV5_HDRINFO_HDR_TYPE_FMASK) !=
> +	    RMNET_MAP_HEADER_TYPE_CSUM_OFFLOAD)
> +		return 0;
> 
> -	if (next_hdr) {
> -		nexthdr_type = u8_get_bits(next_hdr->header_info,
> -
> MAPV5_HDRINFO_HDR_TYPE_FMASK);
> -		if (nexthdr_type !=
> RMNET_MAP_HEADER_TYPE_CSUM_OFFLOAD)
> -			return NULL;
> -	}
> +	return packet_len;
> +}
> +
> +/* Deaggregates a single packet
> + * A whole new buffer is allocated for each portion of an aggregated
frame.
> + * Caller should keep calling deaggregate() on the source skb until 0 is
> + * returned, indicating that there are no more packets to deaggregate.
Caller
> + * is responsible for freeing the original skb.
> + */
> +struct sk_buff *rmnet_map_deaggregate(struct sk_buff *skb,
> +				      struct rmnet_port *port)
> +{
> +	struct sk_buff *skbn;
> +	u32 packet_len;
> +
> +	packet_len = rmnet_map_validate_packet_len(skb, port);
> +	if (!packet_len)
> +		return NULL;
> 
>  	skbn = alloc_skb(packet_len + RMNET_MAP_DEAGGR_SPACING,
> GFP_ATOMIC);
>  	if (!skbn)
> --
> 2.43.0

Reviewed-by: Subash Abhinov Kasiviswanathan
<subash.a.kasiviswanathan@oss.qualcomm.com>


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

end of thread, other threads:[~2026-07-01  7:03 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-06-30 17:41 [PATCH net v2] net: qualcomm: rmnet: validate MAP frame length before ingress parsing Xiang Mei
2026-06-30 17:43 ` Xiang Mei
2026-07-01  7:03 ` subash.a.kasiviswanathan

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox