NCSI keeps VLAN filter entries on ndp->vlan_vids and updates the list
with RCU primitives. The configuration workqueue reads the list in
set_one_vid() under rcu_read_lock(), then dereferences vlan->vid while
constructing a Set VLAN Filter command.
ncsi_vlan_rx_kill_vid() removes matching entries with list_del_rcu(),
but it frees the object immediately with kfree(). VLAN add/delete
callbacks are serialized by RTNL, but RTNL does not serialize the NCSI
configuration workqueue reader. A reader can therefore keep a pointer to
a struct vlan_vid across list_del_rcu() and race with the immediate
free.
Give struct vlan_vid an rcu_head and release removed entries with
kfree_rcu(). This keeps the existing list structure and makes the
list_del_rcu() lifetime contract match the real set_one_vid() reader.
This was found by our static analysis tool and then manually reviewed
against the current tree. CONFIG_PROVE_RCU_LIST was used as
target-matched triage evidence; the lifetime change is based on the
matching source-level reader and updater paths rather than on the
dynamic warning alone.
Signed-off-by: Runyu Xiao <redacted>
---
net/ncsi/internal.h | 1 +
net/ncsi/ncsi-manage.c | 2 +-
2 files changed, 2 insertions(+), 1 deletion(-)
diff --git a/net/ncsi/internal.h b/net/ncsi/internal.h
index adee6dcabdc3..d9f0eadc7a24 100644
--- a/net/ncsi/internal.h
+++ b/net/ncsi/internal.h
@@ -310,6 +310,7 @@ enum {
struct vlan_vid {
struct list_head list;
+ struct rcu_head rcu;
__be16 proto;
u16 vid;
};diff --git a/net/ncsi/ncsi-manage.c b/net/ncsi/ncsi-manage.c
index 446e4e3b9553..5316eadd8ce4 100644
--- a/net/ncsi/ncsi-manage.c
+++ b/net/ncsi/ncsi-manage.c
@@ -1737,7 +1737,7 @@ int ncsi_vlan_rx_kill_vid(struct net_device *dev, __be16 proto, u16 vid)
netdev_dbg(dev, "NCSI: vid %u found, removing\n", vid);
list_del_rcu(&vlan->list);
found = true;
- kfree(vlan);
+ kfree_rcu(vlan, rcu);
}
if (!found) {--
2.34.1