From 59124bbc26bf0211723b3eb5b5186d3dc7b72b0c Mon Sep 17 00:00:00 2001 From: Denys Fedoryshchenko Date: Sat, 8 Aug 2026 17:20:32 +0300 Subject: ipoe: fix ipoe_wlock double release in IPOE_CMD_DELETE ipoe_nl_cmd_delete() drops ipoe_wlock before sleeping in synchronize_rcu() and taking rtnl via unregister_netdev(), then falls through into the out_unlock label and releases it a second time. Every successful session delete therefore increments the semaphore count by one. Since the count only ever grows, ipoe_wlock stops providing mutual exclusion after a handful of teardowns: with the count at N, up to N+1 writers may hold it simultaneously. This is currently masked because ipoe_nl_family does not set parallel_ops, so genetlink serialises every .doit/.dumpit under genl_mutex and no two writers can overlap in practice. It turns into a real race on the session hash lists as soon as that changes, or as soon as a writer is introduced outside the genl handlers. It also silently defeats the barrier in ipoe_fini(): the down()/up() pair meant to wait for an in-flight writer is satisfied immediately by the leaked count and waits for nothing. Return directly after unregister_netdev() so the success path releases the lock exactly once. The early up() is deliberate and stays where it is - holding a sleeping semaphore across unregister_netdev() would nest ipoe_wlock inside rtnl, while ipoe_create() takes rtnl first and ipoe_wlock afterwards. Error paths, return values and lock ordering are unchanged. --- drivers/ipoe/ipoe.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers/ipoe') diff --git a/drivers/ipoe/ipoe.c b/drivers/ipoe/ipoe.c index 2829e6ac..3bf8fef7 100644 --- a/drivers/ipoe/ipoe.c +++ b/drivers/ipoe/ipoe.c @@ -1399,6 +1399,8 @@ static int ipoe_nl_cmd_delete(struct sk_buff *skb, struct genl_info *info) if (ses->u.hwaddr_u) list_del_rcu(&ses->entry3); + /* drop the lock before sleeping in synchronize_rcu() and taking rtnl + * in unregister_netdev() */ up(&ipoe_wlock); synchronize_rcu(); @@ -1411,7 +1413,7 @@ static int ipoe_nl_cmd_delete(struct sk_buff *skb, struct genl_info *info) unregister_netdev(ses->dev); - ret = 0; + return 0; out_unlock: up(&ipoe_wlock); -- cgit v1.2.3