Skip to content

Commit a92c322

Browse files
committed
Merge branch 'net-fix-race-of-rtnl_net_lock-dev_net-dev'
Kuniyuki Iwashima says: ==================== net: Fix race of rtnl_net_lock(dev_net(dev)). Yael Chemla reported that commit 7fb1073 ("net: Hold rtnl_net_lock() in (un)?register_netdevice_notifier_dev_net().") started to trigger KASAN's use-after-free splat. The problem is that dev_net(dev) fetched before rtnl_net_lock() might be different after rtnl_net_lock(). The patch 2 fixes the issue by checking dev_net(dev) after rtnl_net_lock(), and the patch 3 fixes the same potential issue that would emerge once RTNL is removed. v4: https://lore.kernel.org/[email protected] v3: https://lore.kernel.org/[email protected] v2: https://lore.kernel.org/[email protected] v1: https://lore.kernel.org/[email protected] ==================== Link: https://patch.msgid.link/[email protected] Signed-off-by: Jakub Kicinski <[email protected]>
2 parents f6093c5 + d4c6bfc commit a92c322

File tree

3 files changed

+61
-12
lines changed

3 files changed

+61
-12
lines changed

include/net/net_namespace.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -297,6 +297,7 @@ static inline int check_net(const struct net *net)
297297
}
298298

299299
void net_drop_ns(void *);
300+
void net_passive_dec(struct net *net);
300301

301302
#else
302303

@@ -326,8 +327,18 @@ static inline int check_net(const struct net *net)
326327
}
327328

328329
#define net_drop_ns NULL
330+
331+
static inline void net_passive_dec(struct net *net)
332+
{
333+
refcount_dec(&net->passive);
334+
}
329335
#endif
330336

337+
static inline void net_passive_inc(struct net *net)
338+
{
339+
refcount_inc(&net->passive);
340+
}
341+
331342
/* Returns true if the netns initialization is completed successfully */
332343
static inline bool net_initialized(const struct net *net)
333344
{

net/core/dev.c

Lines changed: 46 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2070,13 +2070,54 @@ static void __move_netdevice_notifier_net(struct net *src_net,
20702070
__register_netdevice_notifier_net(dst_net, nb, true);
20712071
}
20722072

2073+
static void rtnl_net_dev_lock(struct net_device *dev)
2074+
{
2075+
bool again;
2076+
2077+
do {
2078+
struct net *net;
2079+
2080+
again = false;
2081+
2082+
/* netns might be being dismantled. */
2083+
rcu_read_lock();
2084+
net = dev_net_rcu(dev);
2085+
net_passive_inc(net);
2086+
rcu_read_unlock();
2087+
2088+
rtnl_net_lock(net);
2089+
2090+
#ifdef CONFIG_NET_NS
2091+
/* dev might have been moved to another netns. */
2092+
if (!net_eq(net, rcu_access_pointer(dev->nd_net.net))) {
2093+
rtnl_net_unlock(net);
2094+
net_passive_dec(net);
2095+
again = true;
2096+
}
2097+
#endif
2098+
} while (again);
2099+
}
2100+
2101+
static void rtnl_net_dev_unlock(struct net_device *dev)
2102+
{
2103+
struct net *net = dev_net(dev);
2104+
2105+
rtnl_net_unlock(net);
2106+
net_passive_dec(net);
2107+
}
2108+
20732109
int register_netdevice_notifier_dev_net(struct net_device *dev,
20742110
struct notifier_block *nb,
20752111
struct netdev_net_notifier *nn)
20762112
{
20772113
struct net *net = dev_net(dev);
20782114
int err;
20792115

2116+
/* rtnl_net_lock() assumes dev is not yet published by
2117+
* register_netdevice().
2118+
*/
2119+
DEBUG_NET_WARN_ON_ONCE(!list_empty(&dev->dev_list));
2120+
20802121
rtnl_net_lock(net);
20812122
err = __register_netdevice_notifier_net(net, nb, false);
20822123
if (!err) {
@@ -2093,13 +2134,12 @@ int unregister_netdevice_notifier_dev_net(struct net_device *dev,
20932134
struct notifier_block *nb,
20942135
struct netdev_net_notifier *nn)
20952136
{
2096-
struct net *net = dev_net(dev);
20972137
int err;
20982138

2099-
rtnl_net_lock(net);
2139+
rtnl_net_dev_lock(dev);
21002140
list_del(&nn->list);
2101-
err = __unregister_netdevice_notifier_net(net, nb);
2102-
rtnl_net_unlock(net);
2141+
err = __unregister_netdevice_notifier_net(dev_net(dev), nb);
2142+
rtnl_net_dev_unlock(dev);
21032143

21042144
return err;
21052145
}
@@ -11880,11 +11920,9 @@ EXPORT_SYMBOL(unregister_netdevice_many);
1188011920
*/
1188111921
void unregister_netdev(struct net_device *dev)
1188211922
{
11883-
struct net *net = dev_net(dev);
11884-
11885-
rtnl_net_lock(net);
11923+
rtnl_net_dev_lock(dev);
1188611924
unregister_netdevice(dev);
11887-
rtnl_net_unlock(net);
11925+
rtnl_net_dev_unlock(dev);
1188811926
}
1188911927
EXPORT_SYMBOL(unregister_netdev);
1189011928

net/core/net_namespace.c

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -464,7 +464,7 @@ static void net_complete_free(void)
464464

465465
}
466466

467-
static void net_free(struct net *net)
467+
void net_passive_dec(struct net *net)
468468
{
469469
if (refcount_dec_and_test(&net->passive)) {
470470
kfree(rcu_access_pointer(net->gen));
@@ -482,7 +482,7 @@ void net_drop_ns(void *p)
482482
struct net *net = (struct net *)p;
483483

484484
if (net)
485-
net_free(net);
485+
net_passive_dec(net);
486486
}
487487

488488
struct net *copy_net_ns(unsigned long flags,
@@ -523,7 +523,7 @@ struct net *copy_net_ns(unsigned long flags,
523523
key_remove_domain(net->key_domain);
524524
#endif
525525
put_user_ns(user_ns);
526-
net_free(net);
526+
net_passive_dec(net);
527527
dec_ucounts:
528528
dec_net_namespaces(ucounts);
529529
return ERR_PTR(rv);
@@ -672,7 +672,7 @@ static void cleanup_net(struct work_struct *work)
672672
key_remove_domain(net->key_domain);
673673
#endif
674674
put_user_ns(net->user_ns);
675-
net_free(net);
675+
net_passive_dec(net);
676676
}
677677
cleanup_net_task = NULL;
678678
}

0 commit comments

Comments
 (0)