Browse Source

rmnet_core: fix race condition in rmnet_get_packets

In rmnet powersave work, rmnet_get_packets() could access NULL dev pointer
if rmnet_dellink() is nullifying the dev pointer at the same time.

18377 [   72.651710][ T1527] Unable to handle kernel NULL pointer dereference
at virtual address 00000000000009d0

18424 [   72.653999][ T1527] Call trace:
18425 [   72.654085][ T1527]  rmnet_get_packets+0xc4/0x11c [rmnet_core]
18426 [   72.654170][ T1527]  qmi_rmnet_check_stats_2+0x80/0x410 [rmnet_core]
18427 [   72.654180][ T1527]  process_one_work+0x260/0x804

This change Uses the rcu variant of the hlist traversal function in
rmnet_get_packet for safe concurrency with the hlist del primitives.
It also checks dev pointer before accessing the dev private structure.
The existing synchronize rcu call in rmnet_dellink ensures that the ep
and dev structure are not freed while being referenced in rcu read
session of rmnet_get_packets.

Change-Id: Ib5f5aff6e76f9fffd9110a2aa924ad6ab090991f
Signed-off-by: Weiyi Chen <[email protected]>
Weiyi Chen 3 years ago
parent
commit
a2ea279ba9
1 changed files with 8 additions and 2 deletions
  1. 8 2
      core/rmnet_config.c

+ 8 - 2
core/rmnet_config.c

@@ -1,4 +1,5 @@
 /* Copyright (c) 2013-2021, The Linux Foundation. All rights reserved.
 /* Copyright (c) 2013-2021, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
  *
  *
  * This program is free software; you can redistribute it and/or modify
  * 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
  * it under the terms of the GNU General Public License version 2 and
@@ -670,6 +671,7 @@ EXPORT_SYMBOL(rmnet_init_qmi_pt);
 
 
 void rmnet_get_packets(void *port, u64 *rx, u64 *tx)
 void rmnet_get_packets(void *port, u64 *rx, u64 *tx)
 {
 {
+	struct net_device *dev;
 	struct rmnet_priv *priv;
 	struct rmnet_priv *priv;
 	struct rmnet_pcpu_stats *ps;
 	struct rmnet_pcpu_stats *ps;
 	unsigned int cpu, start;
 	unsigned int cpu, start;
@@ -683,8 +685,12 @@ void rmnet_get_packets(void *port, u64 *rx, u64 *tx)
 	*tx = 0;
 	*tx = 0;
 	*rx = 0;
 	*rx = 0;
 	rcu_read_lock();
 	rcu_read_lock();
-	hash_for_each(((struct rmnet_port *)port)->muxed_ep, bkt, ep, hlnode) {
-		priv = netdev_priv(ep->egress_dev);
+	hash_for_each_rcu(((struct rmnet_port *)port)->muxed_ep, bkt, ep,
+			  hlnode) {
+		dev = ep->egress_dev;
+		if (!dev)
+			continue;
+		priv = netdev_priv(dev);
 		for_each_possible_cpu(cpu) {
 		for_each_possible_cpu(cpu) {
 			ps = per_cpu_ptr(priv->pcpu_stats, cpu);
 			ps = per_cpu_ptr(priv->pcpu_stats, cpu);
 			do {
 			do {