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 <quic_weiyic@quicinc.com>
This commit is contained in:
Weiyi Chen
2022-01-19 11:13:43 -08:00
committed by Gerrit - the friendly Code Review server
parent 48a12a6f04
commit a2ea279ba9

View File

@@ -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) { hash_for_each_rcu(((struct rmnet_port *)port)->muxed_ep, bkt, ep,
priv = netdev_priv(ep->egress_dev); 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 {