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 --- 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