summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDenys Fedoryshchenko <denys.f@collabora.com>2026-08-08 17:20:32 +0300
committerDenys Fedoryshchenko <denys.f@collabora.com>2026-08-08 17:20:32 +0300
commit59124bbc26bf0211723b3eb5b5186d3dc7b72b0c (patch)
treeb31edd0945c4783ae8569e33a43b648f5564c153
parent11271e9019ef1511b127b75e1c69e9c57f9cba49 (diff)
downloadaccel-ppp-59124bbc26bf0211723b3eb5b5186d3dc7b72b0c.tar.gz
accel-ppp-59124bbc26bf0211723b3eb5b5186d3dc7b72b0c.zip
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.
-rw-r--r--drivers/ipoe/ipoe.c4
1 files changed, 3 insertions, 1 deletions
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);