* [PATCH v4] net/ncsi: Fix Use-After-Free in NCSI channel and package removal
@ 2026-09-05 14:59 Wong Boon Jhee
2026-09-08 13:49 ` Simon Horman
2026-09-09 0:53 ` Jakub Kicinski
0 siblings, 2 replies; 3+ messages in thread
From: Wong Boon Jhee @ 2026-09-05 14:59 UTC (permalink / raw)
To: netdev; +Cc: sam, wongboonjhee52
In net/ncsi/ncsi-manage.c, ncsi_remove_channel() and
ncsi_remove_package() remove objects from an RCU-protected linked list
using list_del_rcu() and immediately free them using kfree().
Because there is no call to synchronize_rcu() or kfree_rcu(), concurrent
readers traversing these lists under rcu_read_lock() (such as Netlink
dump handlers) can still hold a valid pointer to the object. When kfree()
executes, the reader is left holding a dangling pointer to freed memory,
resulting in a slab-use-after-free.
This patch fixes the issue by implementing a proper kref and RCU lifetime
model for NCSI objects. It replaces kfree() with kfree_rcu() / call_rcu()
to defer memory freeing until all pre-existing RCU readers have finished
their critical sections. It also introduces a kref for ncsi_dev_priv to
prevent the device structure from being freed while netlink handlers are
still operating on it. Concurrent lockless readers are now explicitly
protected with rcu_read_lock().
RCU read-side critical sections are added around lockless package and
channel traversals that may race with object removal. The KASAN
reproducer no longer reports the slab-use-after-free.
Fixes: 2d283bdd079c ("net/ncsi: Resource management")
Signed-off-by: Wong Boon Jhee <wongboonjhee52@gmail.com>
---
v3 -> v4:
- Reworked NCSI object lifetime to pin the underlying net_device using
dev_hold()/dev_put() inside ncsi_dev_get()/ncsi_dev_put(), preventing
UAF if the driver unbinds while netlink handlers are active.
- Moved package/channel destruction and request sweeping into the
ncsi_dev_release() kref callback to guarantee all asynchronous
producers are fully stopped before teardown begins.
- Fixed pre-existing get_net() namespace leaks in all netlink handlers.
- Fixed RCU lock leaks on early returns in ncsi_check_hwa() and
ncsi_set_channel_mask_nl().
- Unified channel_queue semantics by removing incorrect RCU usage and
protecting traversals with ndp->lock.
- Extracted channel IDs inside the RCU read section in
ncsi_update_tx_channel() to prevent pointer escapes.
- Fixed memory corruption in ncsi_vlan_rx_kill_vid() by using a
temporary pointer for the freed VLAN object.
net/ncsi/internal.h | 8 +-
net/ncsi/ncsi-aen.c | 2 +
net/ncsi/ncsi-manage.c | 260 +++++++++++++++++++++++++++++++---------
net/ncsi/ncsi-netlink.c | 69 ++++++++---
net/ncsi/ncsi-rsp.c | 17 ++-
5 files changed, 275 insertions(+), 81 deletions(-)
diff --git a/net/ncsi/internal.h b/net/ncsi/internal.h
index adee6dcabdc3..fbd90f270f5e 100644
--- a/net/ncsi/internal.h
+++ b/net/ncsi/internal.h
@@ -239,6 +239,7 @@ struct ncsi_channel {
} monitor;
struct list_head node;
struct list_head link;
+ struct rcu_head rcu; /* RCU cleanup */
};
struct ncsi_package {
@@ -253,6 +254,7 @@ struct ncsi_package {
bool multi_channel; /* Enable multiple channels */
u32 channel_whitelist; /* Channels to configure */
struct ncsi_channel *preferred_channel; /* Primary channel */
+ struct rcu_head rcu; /* RCU cleanup */
};
struct ncsi_request {
@@ -315,6 +317,8 @@ struct vlan_vid {
};
struct ncsi_dev_priv {
+ struct rcu_head rcu; /* RCU cleanup */
+ struct kref ref;
struct ncsi_dev ndev; /* Associated NCSI device */
unsigned int flags; /* NCSI device flags */
#define NCSI_DEV_PROBED 1 /* Finalized NCSI topology */
@@ -337,6 +341,7 @@ struct ncsi_dev_priv {
struct ncsi_channel *active_channel; /* Currently handled channel */
struct list_head channel_queue; /* Config queue of channels */
struct work_struct work; /* For channel management */
+ bool work_cancelled; /* Work has been cancelled */
struct packet_type ptype; /* NCSI packet Rx handler */
struct list_head node; /* Form NCSI device list */
#define NCSI_MAX_VLAN_VIDS 15
@@ -406,7 +411,8 @@ int ncsi_update_tx_channel(struct ncsi_dev_priv *ndp,
struct ncsi_package *np,
struct ncsi_channel *disable,
struct ncsi_channel *enable);
-
+struct ncsi_dev_priv *ncsi_dev_get(struct net_device *dev);
+void ncsi_dev_put(struct ncsi_dev_priv *ndp);
/* Packet handlers */
u32 ncsi_calculate_checksum(unsigned char *data, int len);
int ncsi_xmit_cmd(struct ncsi_cmd_arg *nca);
diff --git a/net/ncsi/ncsi-aen.c b/net/ncsi/ncsi-aen.c
index 040a31557201..b88bbfd349cb 100644
--- a/net/ncsi/ncsi-aen.c
+++ b/net/ncsi/ncsi-aen.c
@@ -115,6 +115,7 @@ static int ncsi_aen_handler_lsc(struct ncsi_dev_priv *ndp,
/* Return Tx to preferred channel */
ncsi_update_tx_channel(ndp, nc->package, NULL, nc);
} else if (has_link) {
+ rcu_read_lock();
NCSI_FOR_EACH_PACKAGE(ndp, np) {
NCSI_FOR_EACH_CHANNEL(np, tmp) {
/* Enable Tx on this channel if the current Tx
@@ -129,6 +130,7 @@ static int ncsi_aen_handler_lsc(struct ncsi_dev_priv *ndp,
}
}
}
+ rcu_read_unlock();
}
/* Leave configured channels active in a multi-channel scenario so
diff --git a/net/ncsi/ncsi-manage.c b/net/ncsi/ncsi-manage.c
index 54d0df0a9efe..ce99eff33c3c 100644
--- a/net/ncsi/ncsi-manage.c
+++ b/net/ncsi/ncsi-manage.c
@@ -10,6 +10,7 @@
#include <linux/skbuff.h>
#include <linux/of.h>
#include <linux/platform_device.h>
+#include <linux/rcupdate.h>
#include <net/ncsi.h>
#include <net/net_namespace.h>
@@ -25,6 +26,12 @@
LIST_HEAD(ncsi_dev_list);
DEFINE_SPINLOCK(ncsi_dev_lock);
+static void ncsi_schedule_work(struct ncsi_dev_priv *ndp)
+{
+ if (!READ_ONCE(ndp->work_cancelled))
+ schedule_work(&ndp->work);
+}
+
bool ncsi_channel_has_link(struct ncsi_channel *channel)
{
return !!(channel->modes[NCSI_MODE_LINK].data[2] & 0x1);
@@ -35,17 +42,24 @@ bool ncsi_channel_is_last(struct ncsi_dev_priv *ndp,
{
struct ncsi_package *np;
struct ncsi_channel *nc;
+ unsigned long flags;
+ bool is_last = true;
- NCSI_FOR_EACH_PACKAGE(ndp, np)
+ rcu_read_lock();
+ NCSI_FOR_EACH_PACKAGE(ndp, np) {
NCSI_FOR_EACH_CHANNEL(np, nc) {
if (nc == channel)
continue;
if (nc->state == NCSI_CHANNEL_ACTIVE &&
- ncsi_channel_has_link(nc))
- return false;
+ ncsi_channel_has_link(nc)) {
+ is_last = false;
+ goto out;
+ }
}
-
- return true;
+ }
+ out:
+ rcu_read_unlock();
+ return is_last;
}
static void ncsi_report_link(struct ncsi_dev_priv *ndp, bool force_down)
@@ -62,6 +76,7 @@ static void ncsi_report_link(struct ncsi_dev_priv *ndp, bool force_down)
}
nd->link_up = 0;
+ rcu_read_lock();
NCSI_FOR_EACH_PACKAGE(ndp, np) {
NCSI_FOR_EACH_CHANNEL(np, nc) {
spin_lock_irqsave(&nc->lock, flags);
@@ -75,12 +90,14 @@ static void ncsi_report_link(struct ncsi_dev_priv *ndp, bool force_down)
if (ncsi_channel_has_link(nc)) {
spin_unlock_irqrestore(&nc->lock, flags);
nd->link_up = 1;
+ rcu_read_unlock();
goto report;
}
spin_unlock_irqrestore(&nc->lock, flags);
}
}
+ rcu_read_unlock();
report:
nd->handler(nd);
@@ -242,28 +259,37 @@ struct ncsi_channel *ncsi_add_channel(struct ncsi_package *np, unsigned char id)
return nc;
}
+static void ncsi_channel_rcu_free(struct rcu_head *head)
+{
+ struct ncsi_channel *nc = container_of(head, struct ncsi_channel, rcu);
+
+ kfree(nc->mac_filter.addrs);
+ kfree(nc->vlan_filter.vids);
+ kfree(nc);
+}
+
static void ncsi_remove_channel(struct ncsi_channel *nc)
{
struct ncsi_package *np = nc->package;
+ struct ncsi_dev_priv *ndp = np->ndp;
unsigned long flags;
- spin_lock_irqsave(&nc->lock, flags);
-
- /* Release filters */
- kfree(nc->mac_filter.addrs);
- kfree(nc->vlan_filter.vids);
+ ncsi_stop_channel_monitor(nc);
+ spin_lock_irqsave(&nc->lock, flags);
nc->state = NCSI_CHANNEL_INACTIVE;
spin_unlock_irqrestore(&nc->lock, flags);
- ncsi_stop_channel_monitor(nc);
- /* Remove and free channel */
+ spin_lock_irqsave(&ndp->lock, flags);
+ list_del_rcu(&nc->link);
+ spin_unlock_irqrestore(&ndp->lock, flags);
+
spin_lock_irqsave(&np->lock, flags);
list_del_rcu(&nc->node);
np->channel_num--;
spin_unlock_irqrestore(&np->lock, flags);
- kfree(nc);
+ call_rcu(&nc->rcu, ncsi_channel_rcu_free);
}
struct ncsi_package *ncsi_find_package(struct ncsi_dev_priv *ndp,
@@ -326,7 +352,7 @@ void ncsi_remove_package(struct ncsi_package *np)
ndp->package_num--;
spin_unlock_irqrestore(&ndp->lock, flags);
- kfree(np);
+ kfree_rcu(np, rcu);
}
void ncsi_find_package_and_channel(struct ncsi_dev_priv *ndp,
@@ -408,8 +434,12 @@ void ncsi_free_request(struct ncsi_request *nr)
driven = !!(nr->flags & NCSI_REQ_FLAG_EVENT_DRIVEN);
spin_unlock_irqrestore(&ndp->lock, flags);
- if (driven && cmd && --ndp->pending_req_num == 0)
- schedule_work(&ndp->work);
+ if (driven && cmd) {
+ spin_lock_irqsave(&ndp->lock, flags);
+ if (--ndp->pending_req_num == 0 && !ndp->work_cancelled)
+ ncsi_schedule_work(ndp);
+ spin_unlock_irqrestore(&ndp->lock, flags);
+ }
/* Release command and response */
consume_skb(cmd);
@@ -552,6 +582,7 @@ static void ncsi_suspend_channel(struct ncsi_dev_priv *ndp)
if (ret)
goto error;
+ rcu_read_lock();
NCSI_FOR_EACH_CHANNEL(np, tmp) {
/* If there is another channel active on this package
* do not deselect the package.
@@ -561,6 +592,7 @@ static void ncsi_suspend_channel(struct ncsi_dev_priv *ndp)
break;
}
}
+ rcu_read_unlock();
break;
case ncsi_dev_state_suspend_deselect:
ndp->pending_req_num = 1;
@@ -859,7 +891,10 @@ static bool ncsi_channel_is_tx(struct ncsi_dev_priv *ndp,
struct ncsi_channel_mode *ncm;
struct ncsi_channel *channel;
struct ncsi_package *np;
+ unsigned long flags;
+ bool ret;
+ rcu_read_lock();
/* Check if any other channel has Tx enabled; a channel may have already
* been configured and removed from the channel queue.
*/
@@ -869,29 +904,43 @@ static bool ncsi_channel_is_tx(struct ncsi_dev_priv *ndp,
NCSI_FOR_EACH_CHANNEL(np, channel) {
ncm = &channel->modes[NCSI_MODE_TX_ENABLE];
if (ncm->enable)
- return false;
+ goto out_false;
}
}
/* This channel is the preferred channel and has link */
- list_for_each_entry_rcu(channel, &ndp->channel_queue, link) {
+ spin_lock_irqsave(&ndp->lock, flags);
+ list_for_each_entry(channel, &ndp->channel_queue, link) {
np = channel->package;
if (np->preferred_channel &&
ncsi_channel_has_link(np->preferred_channel)) {
- return np->preferred_channel == nc;
+ ret = np->preferred_channel == nc;
+ spin_unlock_irqrestore(&ndp->lock, flags);
+ rcu_read_unlock();
+ return ret;
}
}
/* This channel has link */
- if (ncsi_channel_has_link(nc))
+ if (ncsi_channel_has_link(nc)) {
+ spin_unlock_irqrestore(&ndp->lock, flags);
+ rcu_read_unlock();
return true;
+ }
- list_for_each_entry_rcu(channel, &ndp->channel_queue, link)
+ list_for_each_entry(channel, &ndp->channel_queue, link)
if (ncsi_channel_has_link(channel))
- return false;
+ goto out_false;
/* No other channel has link; default to this one */
+ spin_unlock_irqrestore(&ndp->lock, flags);
+ rcu_read_unlock();
return true;
+
+out_false:
+ spin_unlock_irqrestore(&ndp->lock, flags);
+ rcu_read_unlock();
+ return false;
}
/* Change the active Tx channel in a multi-channel setup */
@@ -904,6 +953,8 @@ int ncsi_update_tx_channel(struct ncsi_dev_priv *ndp,
struct ncsi_channel *nc;
struct ncsi_package *np;
int ret = 0;
+ u8 disable_id = 0, disable_package = 0;
+ u8 enable_id = 0, enable_package = 0;
if (!package->multi_channel && !ndp->multi_package)
netdev_warn(ndp->ndev.dev,
@@ -912,6 +963,7 @@ int ncsi_update_tx_channel(struct ncsi_dev_priv *ndp,
nca.req_flags = 0;
/* Find current channel with Tx enabled */
+ rcu_read_lock();
NCSI_FOR_EACH_PACKAGE(ndp, np) {
if (disable)
break;
@@ -924,8 +976,14 @@ int ncsi_update_tx_channel(struct ncsi_dev_priv *ndp,
break;
}
}
+ if (disable) {
+ disable_id = disable->id;
+ disable_package = disable->package->id;
+ }
+ rcu_read_unlock();
/* Find a suitable channel for Tx */
+ rcu_read_lock();
NCSI_FOR_EACH_PACKAGE(ndp, np) {
if (enable)
break;
@@ -949,8 +1007,13 @@ int ncsi_update_tx_channel(struct ncsi_dev_priv *ndp,
enable = nc;
break;
}
- }
+ }
}
+ if (enable) {
+ enable_id = enable->id;
+ enable_package = enable->package->id;
+ }
+ rcu_read_unlock();
if (disable == enable)
return -1;
@@ -959,8 +1022,8 @@ int ncsi_update_tx_channel(struct ncsi_dev_priv *ndp,
return -1;
if (disable) {
- nca.channel = disable->id;
- nca.package = disable->package->id;
+ nca.channel = disable_id;
+ nca.package = disable_package;
nca.type = NCSI_PKT_CMD_DCNT;
ret = ncsi_xmit_cmd(&nca);
if (ret)
@@ -971,8 +1034,8 @@ int ncsi_update_tx_channel(struct ncsi_dev_priv *ndp,
netdev_info(ndp->ndev.dev, "NCSI: channel %u enables Tx\n", enable->id);
- nca.channel = enable->id;
- nca.package = enable->package->id;
+ nca.channel = enable_id;
+ nca.package = enable_package;
nca.type = NCSI_PKT_CMD_ECNT;
ret = ncsi_xmit_cmd(&nca);
if (ret)
@@ -1052,7 +1115,7 @@ static void ncsi_configure_channel(struct ncsi_dev_priv *ndp)
}
if (ret < 0) {
nd->state = ncsi_dev_state_config_clear_vids;
- schedule_work(&ndp->work);
+ ncsi_schedule_work(ndp);
}
break;
@@ -1086,7 +1149,7 @@ static void ncsi_configure_channel(struct ncsi_dev_priv *ndp)
ret = clear_one_vid(ndp, nc, &nca);
if (ret) {
nd->state = ncsi_dev_state_config_svf;
- schedule_work(&ndp->work);
+ ncsi_schedule_work(ndp);
break;
}
/* Repeat */
@@ -1096,7 +1159,7 @@ static void ncsi_configure_channel(struct ncsi_dev_priv *ndp)
ret = set_one_vid(ndp, nc, &nca);
if (ret) {
nd->state = ncsi_dev_state_config_ev;
- schedule_work(&ndp->work);
+ ncsi_schedule_work(ndp);
break;
}
/* Repeat */
@@ -1253,6 +1316,7 @@ static int ncsi_choose_active_channel(struct ncsi_dev_priv *ndp)
*/
found = NULL;
with_link = false;
+ rcu_read_lock();
NCSI_FOR_EACH_PACKAGE(ndp, np) {
if (!(ndp->package_whitelist & (0x1 << np->id)))
continue;
@@ -1304,20 +1368,22 @@ static int ncsi_choose_active_channel(struct ncsi_dev_priv *ndp)
if (with_link && !ndp->multi_package)
break;
}
+ rcu_read_unlock();
+ spin_lock_irqsave(&ndp->lock, flags);
if (list_empty(&ndp->channel_queue) && found) {
netdev_info(ndp->ndev.dev,
"NCSI: No channel with link found, configuring channel %u\n",
found->id);
- spin_lock_irqsave(&ndp->lock, flags);
list_add_tail_rcu(&found->link, &ndp->channel_queue);
- spin_unlock_irqrestore(&ndp->lock, flags);
} else if (!found) {
+ spin_unlock_irqrestore(&ndp->lock, flags);
netdev_warn(ndp->ndev.dev,
"NCSI: No channel found to configure!\n");
ncsi_report_link(ndp, true);
return -ENODEV;
}
+ spin_unlock_irqrestore(&ndp->lock, flags);
return ncsi_process_next_channel(ndp);
}
@@ -1328,10 +1394,12 @@ static bool ncsi_check_hwa(struct ncsi_dev_priv *ndp)
struct ncsi_channel *nc;
unsigned int cap;
bool has_channel = false;
+ bool supported = true;
/* The hardware arbitration is disabled if any one channel
* doesn't support explicitly.
*/
+ rcu_read_lock();
NCSI_FOR_EACH_PACKAGE(ndp, np) {
NCSI_FOR_EACH_CHANNEL(np, nc) {
has_channel = true;
@@ -1341,10 +1409,15 @@ static bool ncsi_check_hwa(struct ncsi_dev_priv *ndp)
(cap & NCSI_CAP_GENERIC_HWA_MASK) !=
NCSI_CAP_GENERIC_HWA_SUPPORT) {
ndp->flags &= ~NCSI_DEV_HWA;
- return false;
+ supported = false;
+ goto out;
}
}
}
+out:
+ rcu_read_unlock();
+ if (!supported)
+ return false;
if (has_channel) {
ndp->flags |= NCSI_DEV_HWA;
@@ -1408,7 +1481,7 @@ static void ncsi_probe_channel(struct ncsi_dev_priv *ndp)
if (!ndp->active_package) {
/* No response */
nd->state = ncsi_dev_state_probe_dp;
- schedule_work(&ndp->work);
+ ncsi_schedule_work(ndp);
break;
}
nd->state = ncsi_dev_state_probe_cis;
@@ -1416,7 +1489,7 @@ static void ncsi_probe_channel(struct ncsi_dev_priv *ndp)
ndp->mlx_multi_host)
nd->state = ncsi_dev_state_probe_mlx_gma;
- schedule_work(&ndp->work);
+ ncsi_schedule_work(ndp);
break;
case ncsi_dev_state_probe_mlx_gma:
ndp->pending_req_num = 1;
@@ -1620,6 +1693,7 @@ static int ncsi_kick_channels(struct ncsi_dev_priv *ndp)
unsigned long flags;
unsigned int n = 0;
+ rcu_read_lock();
NCSI_FOR_EACH_PACKAGE(ndp, np) {
NCSI_FOR_EACH_CHANNEL(np, nc) {
spin_lock_irqsave(&nc->lock, flags);
@@ -1659,6 +1733,7 @@ static int ncsi_kick_channels(struct ncsi_dev_priv *ndp)
}
}
+ rcu_read_unlock();
return n;
}
@@ -1667,26 +1742,27 @@ int ncsi_vlan_rx_add_vid(struct net_device *dev, __be16 proto, u16 vid)
struct ncsi_dev_priv *ndp;
unsigned int n_vids = 0;
struct vlan_vid *vlan;
- struct ncsi_dev *nd;
bool found = false;
+ int ret;
if (vid == 0)
return 0;
- nd = ncsi_find_dev(dev);
- if (!nd) {
+ ndp = ncsi_dev_get(dev);
+ if (!ndp) {
netdev_warn(dev, "NCSI: No net_device?\n");
return 0;
}
- ndp = TO_NCSI_DEV_PRIV(nd);
-
/* Add the VLAN id to our internal list */
+ rcu_read_lock();
list_for_each_entry_rcu(vlan, &ndp->vlan_vids, list) {
n_vids++;
if (vlan->vid == vid) {
netdev_dbg(dev, "NCSI: vid %u already registered\n",
vid);
+ rcu_read_unlock();
+ ncsi_dev_put(ndp);
return 0;
}
}
@@ -1694,12 +1770,17 @@ int ncsi_vlan_rx_add_vid(struct net_device *dev, __be16 proto, u16 vid)
netdev_warn(dev,
"tried to add vlan id %u but NCSI max already registered (%u)\n",
vid, NCSI_MAX_VLAN_VIDS);
+ rcu_read_unlock();
+ ncsi_dev_put(ndp);
return -ENOSPC;
}
+ rcu_read_unlock();
vlan = kzalloc_obj(*vlan);
- if (!vlan)
+ if (!vlan) {
+ ncsi_dev_put(ndp);
return -ENOMEM;
+ }
vlan->proto = proto;
vlan->vid = vid;
@@ -1708,46 +1789,54 @@ int ncsi_vlan_rx_add_vid(struct net_device *dev, __be16 proto, u16 vid)
netdev_dbg(dev, "NCSI: Added new vid %u\n", vid);
found = ncsi_kick_channels(ndp) != 0;
-
- return found ? ncsi_process_next_channel(ndp) : 0;
+ ret = found ? ncsi_process_next_channel(ndp) : 0;
+ ncsi_dev_put(ndp);
+ return ret;
}
EXPORT_SYMBOL_GPL(ncsi_vlan_rx_add_vid);
int ncsi_vlan_rx_kill_vid(struct net_device *dev, __be16 proto, u16 vid)
{
struct vlan_vid *vlan, *tmp;
+ struct vlan_vid *found_vlan = NULL;
struct ncsi_dev_priv *ndp;
- struct ncsi_dev *nd;
bool found = false;
+ int ret;
if (vid == 0)
return 0;
- nd = ncsi_find_dev(dev);
- if (!nd) {
+ ndp = ncsi_dev_get(dev);
+ if (!ndp) {
netdev_warn(dev, "NCSI: no net_device?\n");
return 0;
}
- ndp = TO_NCSI_DEV_PRIV(nd);
-
/* Remove the VLAN id from our internal list */
+ rcu_read_lock();
list_for_each_entry_safe(vlan, tmp, &ndp->vlan_vids, list)
if (vlan->vid == vid) {
netdev_dbg(dev, "NCSI: vid %u found, removing\n", vid);
list_del_rcu(&vlan->list);
+ found_vlan = vlan;
found = true;
- kfree(vlan);
+ break;
}
+ rcu_read_unlock();
if (!found) {
netdev_err(dev, "NCSI: vid %u wasn't registered!\n", vid);
+ ncsi_dev_put(ndp);
return -EINVAL;
}
+ synchronize_rcu();
+ kfree(found_vlan);
found = ncsi_kick_channels(ndp) != 0;
- return found ? ncsi_process_next_channel(ndp) : 0;
+ ret = found ? ncsi_process_next_channel(ndp) : 0;
+ ncsi_dev_put(ndp);
+ return ret;
}
EXPORT_SYMBOL_GPL(ncsi_vlan_rx_kill_vid);
@@ -1762,7 +1851,9 @@ struct ncsi_dev *ncsi_register_dev(struct net_device *dev,
int i;
/* Check if the device has been registered or not */
+ rcu_read_lock();
nd = ncsi_find_dev(dev);
+ rcu_read_unlock();
if (nd)
return nd;
@@ -1782,6 +1873,7 @@ struct ncsi_dev *ncsi_register_dev(struct net_device *dev,
ndp->package_whitelist = UINT_MAX;
/* Initialize private NCSI device */
+ kref_init(&ndp->ref);
spin_lock_init(&ndp->lock);
INIT_LIST_HEAD(&ndp->packages);
ndp->request_id = NCSI_REQ_START_IDX;
@@ -1826,7 +1918,7 @@ int ncsi_start_dev(struct ncsi_dev *nd)
ndp->package_probe_id = 0;
ndp->channel_probe_id = 0;
nd->state = ncsi_dev_state_probe;
- schedule_work(&ndp->work);
+ ncsi_schedule_work(ndp);
return 0;
}
@@ -1842,6 +1934,7 @@ void ncsi_stop_dev(struct ncsi_dev *nd)
bool chained;
int old_state;
unsigned long flags;
+ rcu_read_lock();
/* Stop the channel monitor on any active channels. Don't reset the
* channel state so we know which were active when ncsi_start_dev()
@@ -1860,6 +1953,7 @@ void ncsi_stop_dev(struct ncsi_dev *nd)
old_state == NCSI_CHANNEL_INVISIBLE);
}
}
+ rcu_read_unlock();
netdev_dbg(ndp->ndev.dev, "NCSI: Stopping device\n");
ncsi_report_link(ndp, true);
@@ -1915,6 +2009,7 @@ int ncsi_reset_dev(struct ncsi_dev *nd)
spin_unlock_irqrestore(&ndp->lock, flags);
active = NULL;
+ rcu_read_lock();
NCSI_FOR_EACH_PACKAGE(ndp, np) {
NCSI_FOR_EACH_CHANNEL(np, nc) {
spin_lock_irqsave(&nc->lock, flags);
@@ -1932,6 +2027,7 @@ int ncsi_reset_dev(struct ncsi_dev *nd)
if (active)
break;
}
+ rcu_read_unlock();
if (!active) {
/* Done */
@@ -1948,27 +2044,77 @@ int ncsi_reset_dev(struct ncsi_dev *nd)
spin_unlock_irqrestore(&ndp->lock, flags);
nd->state = ncsi_dev_state_suspend;
- schedule_work(&ndp->work);
+ ncsi_schedule_work(ndp);
return 0;
}
-void ncsi_unregister_dev(struct ncsi_dev *nd)
+static void ncsi_dev_release(struct kref *ref)
{
- struct ncsi_dev_priv *ndp = TO_NCSI_DEV_PRIV(nd);
+ struct ncsi_dev_priv *ndp;
struct ncsi_package *np, *tmp;
- unsigned long flags;
+ int i;
- dev_remove_pack(&ndp->ptype);
+ ndp = container_of(ref, struct ncsi_dev_priv, ref);
+ /* All producers have been stopped before the last reference is dropped. */
list_for_each_entry_safe(np, tmp, &ndp->packages, node)
ncsi_remove_package(np);
+ for (i = 0; i < ARRAY_SIZE(ndp->requests); i++) {
+ struct ncsi_request *nr = &ndp->requests[i];
+
+ timer_delete_sync(&nr->timer);
+ if (nr->used || nr->cmd || nr->rsp)
+ ncsi_free_request(nr);
+ }
+
+ kfree_rcu(ndp, rcu);
+}
+
+struct ncsi_dev_priv *ncsi_dev_get(struct net_device *dev)
+{
+ struct ncsi_dev_priv *ndp = NULL;
+ struct ncsi_dev *nd;
+
+ rcu_read_lock();
+ nd = ncsi_find_dev(dev);
+ if (nd) {
+ ndp = TO_NCSI_DEV_PRIV(nd);
+ /* Safely grab a reference while under RCU lock */
+ if (!kref_get_unless_zero(&ndp->ref))
+ ndp = NULL;
+ else
+ dev_hold(dev);
+ }
+ rcu_read_unlock();
+
+ return ndp;
+}
+
+void ncsi_dev_put(struct ncsi_dev_priv *ndp)
+{
+ if (ndp) {
+ dev_put(ndp->ndev.dev);
+ kref_put(&ndp->ref, ncsi_dev_release);
+ }
+}
+
+void ncsi_unregister_dev(struct ncsi_dev *nd)
+{
+ struct ncsi_dev_priv *ndp = TO_NCSI_DEV_PRIV(nd);
+ unsigned long flags;
+
spin_lock_irqsave(&ncsi_dev_lock, flags);
list_del_rcu(&ndp->node);
spin_unlock_irqrestore(&ncsi_dev_lock, flags);
+ spin_lock_irqsave(&ndp->lock, flags);
+ ndp->work_cancelled = true;
+ spin_unlock_irqrestore(&ndp->lock, flags);
+
+ dev_remove_pack(&ndp->ptype);
disable_work_sync(&ndp->work);
- kfree(ndp);
+ kref_put(&ndp->ref, ncsi_dev_release);
}
EXPORT_SYMBOL_GPL(ncsi_unregister_dev);
diff --git a/net/ncsi/ncsi-netlink.c b/net/ncsi/ncsi-netlink.c
index 8cc538358f6a..a7094b124753 100644
--- a/net/ncsi/ncsi-netlink.c
+++ b/net/ncsi/ncsi-netlink.c
@@ -35,8 +35,6 @@ static struct ncsi_dev_priv *ndp_from_ifindex(struct net *net, u32 ifindex)
{
struct ncsi_dev_priv *ndp;
struct net_device *dev;
- struct ncsi_dev *nd;
- struct ncsi_dev;
if (!net)
return NULL;
@@ -47,9 +45,7 @@ static struct ncsi_dev_priv *ndp_from_ifindex(struct net *net, u32 ifindex)
return NULL;
}
- nd = ncsi_find_dev(dev);
- ndp = nd ? TO_NCSI_DEV_PRIV(nd) : NULL;
-
+ ndp = ncsi_dev_get(dev);
dev_put(dev);
return ndp;
}
@@ -175,13 +171,16 @@ static int ncsi_pkg_info_nl(struct sk_buff *msg, struct genl_info *info)
return -ENODEV;
skb = genlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
- if (!skb)
+ if (!skb) {
+ ncsi_dev_put(ndp);
return -ENOMEM;
+ }
hdr = genlmsg_put(skb, info->snd_portid, info->snd_seq,
&ncsi_genl_family, 0, NCSI_CMD_PKG_INFO);
if (!hdr) {
kfree_skb(skb);
+ ncsi_dev_put(ndp);
return -EMSGSIZE;
}
@@ -190,9 +189,13 @@ static int ncsi_pkg_info_nl(struct sk_buff *msg, struct genl_info *info)
attr = nla_nest_start_noflag(skb, NCSI_ATTR_PACKAGE_LIST);
if (!attr) {
kfree_skb(skb);
+ ncsi_dev_put(ndp);
return -EMSGSIZE;
}
+
+ rcu_read_lock();
rc = ncsi_write_package_info(skb, ndp, package_id);
+ rcu_read_unlock();
if (rc) {
nla_nest_cancel(skb, attr);
@@ -202,10 +205,12 @@ static int ncsi_pkg_info_nl(struct sk_buff *msg, struct genl_info *info)
nla_nest_end(skb, attr);
genlmsg_end(skb, hdr);
+ ncsi_dev_put(ndp);
return genlmsg_reply(skb, info);
err:
kfree_skb(skb);
+ ncsi_dev_put(ndp);
return rc;
}
@@ -228,21 +233,24 @@ static int ncsi_pkg_info_all_nl(struct sk_buff *skb,
if (!attrs[NCSI_ATTR_IFINDEX])
return -EINVAL;
- ndp = ndp_from_ifindex(get_net(sock_net(skb->sk)),
+ ndp = ndp_from_ifindex(sock_net(skb->sk),
nla_get_u32(attrs[NCSI_ATTR_IFINDEX]));
if (!ndp)
return -ENODEV;
package_id = cb->args[0];
+ rcu_read_lock();
package = NULL;
NCSI_FOR_EACH_PACKAGE(ndp, np)
if (np->id == package_id)
package = np;
- if (!package)
+ if (!package) {
+ rcu_read_unlock();
+ ncsi_dev_put(ndp);
return 0; /* done */
-
+ }
hdr = genlmsg_put(skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq,
&ncsi_genl_family, NLM_F_MULTI, NCSI_CMD_PKG_INFO);
if (!hdr) {
@@ -255,7 +263,9 @@ static int ncsi_pkg_info_all_nl(struct sk_buff *skb,
rc = -EMSGSIZE;
goto err;
}
+
rc = ncsi_write_package_info(skb, ndp, package->id);
+
if (rc) {
nla_nest_cancel(skb, attr);
goto err;
@@ -266,9 +276,13 @@ static int ncsi_pkg_info_all_nl(struct sk_buff *skb,
cb->args[0] = package_id + 1;
+ rcu_read_unlock();
+ ncsi_dev_put(ndp);
return skb->len;
err:
+ rcu_read_unlock();
genlmsg_cancel(skb, hdr);
+ ncsi_dev_put(ndp);
return rc;
}
@@ -289,7 +303,7 @@ static int ncsi_set_interface_nl(struct sk_buff *msg, struct genl_info *info)
if (!info->attrs[NCSI_ATTR_PACKAGE_ID])
return -EINVAL;
- ndp = ndp_from_ifindex(get_net(sock_net(msg->sk)),
+ ndp = ndp_from_ifindex(sock_net(msg->sk),
nla_get_u32(info->attrs[NCSI_ATTR_IFINDEX]));
if (!ndp)
return -ENODEV;
@@ -297,11 +311,15 @@ static int ncsi_set_interface_nl(struct sk_buff *msg, struct genl_info *info)
package_id = nla_get_u32(info->attrs[NCSI_ATTR_PACKAGE_ID]);
package = NULL;
+ rcu_read_lock();
NCSI_FOR_EACH_PACKAGE(ndp, np)
if (np->id == package_id)
package = np;
+
if (!package) {
/* The user has set a package that does not exist */
+ rcu_read_unlock();
+ ncsi_dev_put(ndp);
return -ERANGE;
}
@@ -317,6 +335,8 @@ static int ncsi_set_interface_nl(struct sk_buff *msg, struct genl_info *info)
netdev_info(ndp->ndev.dev,
"NCSI: Channel %u does not exist!\n",
channel_id);
+ rcu_read_unlock();
+ ncsi_dev_put(ndp);
return -ERANGE;
}
}
@@ -337,6 +357,7 @@ static int ncsi_set_interface_nl(struct sk_buff *msg, struct genl_info *info)
package->preferred_channel = NULL;
}
spin_unlock_irqrestore(&package->lock, flags);
+ rcu_read_unlock();
if (channel)
netdev_info(ndp->ndev.dev,
@@ -350,6 +371,7 @@ static int ncsi_set_interface_nl(struct sk_buff *msg, struct genl_info *info)
if (!(ndp->flags & NCSI_DEV_RESET))
ncsi_reset_dev(&ndp->ndev);
+ ncsi_dev_put(ndp);
return 0;
}
@@ -365,7 +387,7 @@ static int ncsi_clear_interface_nl(struct sk_buff *msg, struct genl_info *info)
if (!info->attrs[NCSI_ATTR_IFINDEX])
return -EINVAL;
- ndp = ndp_from_ifindex(get_net(sock_net(msg->sk)),
+ ndp = ndp_from_ifindex(sock_net(msg->sk),
nla_get_u32(info->attrs[NCSI_ATTR_IFINDEX]));
if (!ndp)
return -ENODEV;
@@ -376,6 +398,7 @@ static int ncsi_clear_interface_nl(struct sk_buff *msg, struct genl_info *info)
ndp->multi_package = false;
spin_unlock_irqrestore(&ndp->lock, flags);
+ rcu_read_lock();
NCSI_FOR_EACH_PACKAGE(ndp, np) {
spin_lock_irqsave(&np->lock, flags);
np->multi_channel = false;
@@ -383,18 +406,21 @@ static int ncsi_clear_interface_nl(struct sk_buff *msg, struct genl_info *info)
np->preferred_channel = NULL;
spin_unlock_irqrestore(&np->lock, flags);
}
+ rcu_read_unlock();
+
netdev_info(ndp->ndev.dev, "NCSI: Cleared preferred package/channel\n");
/* Update channel configuration */
if (!(ndp->flags & NCSI_DEV_RESET))
ncsi_reset_dev(&ndp->ndev);
+ ncsi_dev_put(ndp);
return 0;
}
static int ncsi_send_cmd_nl(struct sk_buff *msg, struct genl_info *info)
{
- struct ncsi_dev_priv *ndp;
+ struct ncsi_dev_priv *ndp = NULL;
struct ncsi_pkt_hdr *hdr;
struct ncsi_cmd_arg nca;
unsigned char *data;
@@ -427,7 +453,7 @@ static int ncsi_send_cmd_nl(struct sk_buff *msg, struct genl_info *info)
goto out;
}
- ndp = ndp_from_ifindex(get_net(sock_net(msg->sk)),
+ ndp = ndp_from_ifindex(sock_net(msg->sk),
nla_get_u32(info->attrs[NCSI_ATTR_IFINDEX]));
if (!ndp) {
ret = -ENODEV;
@@ -480,6 +506,7 @@ static int ncsi_send_cmd_nl(struct sk_buff *msg, struct genl_info *info)
ret);
}
out:
+ ncsi_dev_put(ndp);
return ret;
}
@@ -608,7 +635,7 @@ static int ncsi_set_package_mask_nl(struct sk_buff *msg,
if (!info->attrs[NCSI_ATTR_PACKAGE_MASK])
return -EINVAL;
- ndp = ndp_from_ifindex(get_net(sock_net(msg->sk)),
+ ndp = ndp_from_ifindex(sock_net(msg->sk),
nla_get_u32(info->attrs[NCSI_ATTR_IFINDEX]));
if (!ndp)
return -ENODEV;
@@ -639,6 +666,7 @@ static int ncsi_set_package_mask_nl(struct sk_buff *msg,
ncsi_reset_dev(&ndp->ndev);
}
+ ncsi_dev_put(ndp);
return rc;
}
@@ -663,21 +691,24 @@ static int ncsi_set_channel_mask_nl(struct sk_buff *msg,
if (!info->attrs[NCSI_ATTR_CHANNEL_MASK])
return -EINVAL;
- ndp = ndp_from_ifindex(get_net(sock_net(msg->sk)),
+ ndp = ndp_from_ifindex(sock_net(msg->sk),
nla_get_u32(info->attrs[NCSI_ATTR_IFINDEX]));
if (!ndp)
return -ENODEV;
package_id = nla_get_u32(info->attrs[NCSI_ATTR_PACKAGE_ID]);
package = NULL;
+ rcu_read_lock();
NCSI_FOR_EACH_PACKAGE(ndp, np)
if (np->id == package_id) {
package = np;
break;
}
- if (!package)
+ if (!package) {
+ rcu_read_unlock();
+ ncsi_dev_put(ndp);
return -ERANGE;
-
+ }
spin_lock_irqsave(&package->lock, flags);
channel = NULL;
@@ -690,6 +721,8 @@ static int ncsi_set_channel_mask_nl(struct sk_buff *msg,
}
if (!channel) {
spin_unlock_irqrestore(&package->lock, flags);
+ rcu_read_unlock();
+ ncsi_dev_put(ndp);
return -ERANGE;
}
netdev_dbg(ndp->ndev.dev,
@@ -716,11 +749,13 @@ static int ncsi_set_channel_mask_nl(struct sk_buff *msg,
}
spin_unlock_irqrestore(&package->lock, flags);
+ rcu_read_unlock();
/* Update channel configuration */
if (!(ndp->flags & NCSI_DEV_RESET))
ncsi_reset_dev(&ndp->ndev);
+ ncsi_dev_put(ndp);
return 0;
}
diff --git a/net/ncsi/ncsi-rsp.c b/net/ncsi/ncsi-rsp.c
index fbd84bc8026a..1fb73bc2e846 100644
--- a/net/ncsi/ncsi-rsp.c
+++ b/net/ncsi/ncsi-rsp.c
@@ -149,11 +149,13 @@ static int ncsi_rsp_handler_dp(struct ncsi_request *nr)
return -ENODEV;
/* Change state of all channels attached to the package */
+ rcu_read_lock();
NCSI_FOR_EACH_CHANNEL(np, nc) {
spin_lock_irqsave(&nc->lock, flags);
nc->state = NCSI_CHANNEL_INACTIVE;
spin_unlock_irqrestore(&nc->lock, flags);
}
+ rcu_read_unlock();
return 0;
}
@@ -1166,7 +1168,6 @@ int ncsi_rcv_rsp(struct sk_buff *skb, struct net_device *dev,
struct packet_type *pt, struct net_device *orig_dev)
{
struct ncsi_rsp_handler *nrh = NULL;
- struct ncsi_dev *nd;
struct ncsi_dev_priv *ndp;
struct ncsi_request *nr;
struct ncsi_pkt_hdr *hdr;
@@ -1174,8 +1175,7 @@ int ncsi_rcv_rsp(struct sk_buff *skb, struct net_device *dev,
int payload, i, ret;
/* Find the NCSI device */
- nd = ncsi_find_dev(orig_dev);
- ndp = nd ? TO_NCSI_DEV_PRIV(nd) : NULL;
+ ndp = ncsi_dev_get(orig_dev);
if (!ndp) {
ret = -ENODEV;
goto err_free_skb;
@@ -1183,8 +1183,11 @@ int ncsi_rcv_rsp(struct sk_buff *skb, struct net_device *dev,
/* Check if it is AEN packet */
hdr = (struct ncsi_pkt_hdr *)skb_network_header(skb);
- if (hdr->type == NCSI_PKT_AEN)
- return ncsi_aen_handler(ndp, skb);
+ if (hdr->type == NCSI_PKT_AEN) {
+ ret = ncsi_aen_handler(ndp, skb);
+ ncsi_dev_put(ndp);
+ return ret;
+ }
/* Find the handler */
for (i = 0; i < ARRAY_SIZE(ncsi_rsp_handlers); i++) {
@@ -1199,7 +1202,7 @@ int ncsi_rcv_rsp(struct sk_buff *skb, struct net_device *dev,
}
if (!nrh) {
- netdev_err(nd->dev, "Received unrecognized packet (0x%x)\n",
+ netdev_err(ndp->ndev.dev, "Received unrecognized packet (0x%x)\n",
hdr->type);
ret = -ENOENT;
goto err_free_skb;
@@ -1264,9 +1267,11 @@ int ncsi_rcv_rsp(struct sk_buff *skb, struct net_device *dev,
out:
ncsi_free_request(nr);
+ ncsi_dev_put(ndp);
return ret;
err_free_skb:
kfree_skb(skb);
+ ncsi_dev_put(ndp);
return ret;
}
--
2.55.0
^ permalink raw reply related [flat|nested] 3+ messages in thread
* Re: [PATCH v4] net/ncsi: Fix Use-After-Free in NCSI channel and package removal
2026-09-05 14:59 [PATCH v4] net/ncsi: Fix Use-After-Free in NCSI channel and package removal Wong Boon Jhee
@ 2026-09-08 13:49 ` Simon Horman
2026-09-09 0:53 ` Jakub Kicinski
1 sibling, 0 replies; 3+ messages in thread
From: Simon Horman @ 2026-09-08 13:49 UTC (permalink / raw)
To: Wong Boon Jhee; +Cc: netdev, sam
On Sat, Sep 05, 2026 at 10:59:02PM +0800, Wong Boon Jhee wrote:
> In net/ncsi/ncsi-manage.c, ncsi_remove_channel() and
> ncsi_remove_package() remove objects from an RCU-protected linked list
> using list_del_rcu() and immediately free them using kfree().
>
> Because there is no call to synchronize_rcu() or kfree_rcu(), concurrent
> readers traversing these lists under rcu_read_lock() (such as Netlink
> dump handlers) can still hold a valid pointer to the object. When kfree()
> executes, the reader is left holding a dangling pointer to freed memory,
> resulting in a slab-use-after-free.
>
> This patch fixes the issue by implementing a proper kref and RCU lifetime
> model for NCSI objects. It replaces kfree() with kfree_rcu() / call_rcu()
> to defer memory freeing until all pre-existing RCU readers have finished
> their critical sections. It also introduces a kref for ncsi_dev_priv to
> prevent the device structure from being freed while netlink handlers are
> still operating on it. Concurrent lockless readers are now explicitly
> protected with rcu_read_lock().
>
> RCU read-side critical sections are added around lockless package and
> channel traversals that may race with object removal. The KASAN
> reproducer no longer reports the slab-use-after-free.
>
> Fixes: 2d283bdd079c ("net/ncsi: Resource management")
> Signed-off-by: Wong Boon Jhee <wongboonjhee52@gmail.com>
> ---
> v3 -> v4:
> - Reworked NCSI object lifetime to pin the underlying net_device using
> dev_hold()/dev_put() inside ncsi_dev_get()/ncsi_dev_put(), preventing
> UAF if the driver unbinds while netlink handlers are active.
> - Moved package/channel destruction and request sweeping into the
> ncsi_dev_release() kref callback to guarantee all asynchronous
> producers are fully stopped before teardown begins.
> - Fixed pre-existing get_net() namespace leaks in all netlink handlers.
> - Fixed RCU lock leaks on early returns in ncsi_check_hwa() and
> ncsi_set_channel_mask_nl().
> - Unified channel_queue semantics by removing incorrect RCU usage and
> protecting traversals with ndp->lock.
> - Extracted channel IDs inside the RCU read section in
> ncsi_update_tx_channel() to prevent pointer escapes.
> - Fixed memory corruption in ncsi_vlan_rx_kill_vid() by using a
> temporary pointer for the freed VLAN object.
>
> net/ncsi/internal.h | 8 +-
> net/ncsi/ncsi-aen.c | 2 +
> net/ncsi/ncsi-manage.c | 260 +++++++++++++++++++++++++++++++---------
> net/ncsi/ncsi-netlink.c | 69 ++++++++---
> net/ncsi/ncsi-rsp.c | 17 ++-
Hi Wong Boon,
I am concerned that as a fix this patch is quite large and complex. And on
the current course likely to be come more so in order to address concerns
such as those raised by AI-generated review here:
https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20260830064337.327128-2-wongboonjhee52%40gmail.com
It does seem to me that while the architectural direction this patch is
taking is the right long term solution. But, given it's complexity, I
wonder if it would be best to implement a simpler fix for the bug, and then
follow-up with the architectural changes.
Reviewing the problem described in the patch description, I see that
ncsi_remove_channel() and ncsi_remove_package() are only called during
device tear-down. So I wonder if focussing on tear-down would lead us to a
simpler bug-fix.
For example in ncsi_unregister_dev: stop the producers; flush the readers
using synchronize_rcu() and then flush all the callbacks using rcu_barrier().
I'm no fan of synchronize_rcu(), but this may lead to a far more compact
and thus less risky bug-fix.
LMKWYT
^ permalink raw reply [flat|nested] 3+ messages in thread
* Re: [PATCH v4] net/ncsi: Fix Use-After-Free in NCSI channel and package removal
2026-09-05 14:59 [PATCH v4] net/ncsi: Fix Use-After-Free in NCSI channel and package removal Wong Boon Jhee
2026-09-08 13:49 ` Simon Horman
@ 2026-09-09 0:53 ` Jakub Kicinski
1 sibling, 0 replies; 3+ messages in thread
From: Jakub Kicinski @ 2026-09-09 0:53 UTC (permalink / raw)
To: Wong Boon Jhee; +Cc: netdev, sam
On Sat, 5 Sep 2026 22:59:02 +0800 Wong Boon Jhee wrote:
> In net/ncsi/ncsi-manage.c, ncsi_remove_channel() and
> ncsi_remove_package() remove objects from an RCU-protected linked list
> using list_del_rcu() and immediately free them using kfree().
../net/ncsi/ncsi-manage.c:45:16: warning: unused variable 'flags' [-Wunused-variable]
45 | unsigned long flags;
| ^~~~~
../net/ncsi/ncsi-manage.c:906:8: warning: variable 'flags' is used uninitialized whenever 'if' condition is true [-Wsometimes-uninitialized]
906 | if (ncm->enable)
| ^~~~~~~~~~~
../net/ncsi/ncsi-manage.c:941:37: note: uninitialized use occurs here
941 | spin_unlock_irqrestore(&ndp->lock, flags);
| ^~~~~
../net/ncsi/ncsi-manage.c:906:4: note: remove the 'if' if its condition is always false
906 | if (ncm->enable)
| ^~~~~~~~~~~~~~~~
907 | goto out_false;
| ~~~~~~~~~~~~~~
../net/ncsi/ncsi-manage.c:894:21: note: initialize the variable 'flags' to silence this warning
894 | unsigned long flags;
| ^
| = 0
^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2026-09-09 0:53 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-09-05 14:59 [PATCH v4] net/ncsi: Fix Use-After-Free in NCSI channel and package removal Wong Boon Jhee
2026-09-08 13:49 ` Simon Horman
2026-09-09 0:53 ` Jakub Kicinski
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox