@@ -61,9 +61,6 @@ static int rmnet_unregister_real_device(struct net_device *real_dev,
kfree(port);
- /* release reference on real_dev */
- dev_put(real_dev);
-
netdev_dbg(real_dev, "Removed from rmnet\n");
return 0;
}
@@ -89,9 +86,6 @@ static int rmnet_register_real_device(struct net_device *real_dev)
return -EBUSY;
}
- /* hold on to real dev for MAP data */
- dev_hold(real_dev);
-
for (entry = 0; entry < RMNET_MAX_LOGICAL_EP; entry++)
INIT_HLIST_HEAD(&port->muxed_ep[entry]);
@@ -161,6 +155,10 @@ static int rmnet_newlink(struct net *src_net, struct net_device *dev,
if (err)
goto err1;
+ err = netdev_upper_dev_link(real_dev, dev, extack);
+ if (err < 0)
+ goto err2;
+
port->rmnet_mode = mode;
hlist_add_head_rcu(&ep->hlnode, &port->muxed_ep[mux_id]);
@@ -177,6 +175,8 @@ static int rmnet_newlink(struct net *src_net, struct net_device *dev,
return 0;
+err2:
+ unregister_netdevice(dev);
err1:
rmnet_unregister_real_device(real_dev, port);
err0:
@@ -208,33 +208,30 @@ static void rmnet_dellink(struct net_device *dev, struct list_head *head)
rmnet_vnd_dellink(mux_id, port, ep);
kfree(ep);
}
+ netdev_upper_dev_unlink(real_dev, dev);
rmnet_unregister_real_device(real_dev, port);
unregister_netdevice_queue(dev, head);
}
-static void rmnet_force_unassociate_device(struct net_device *dev)
+static void rmnet_force_unassociate_device(struct net_device *real_dev)
{
- struct net_device *real_dev = dev;
struct hlist_node *tmp_ep;
struct rmnet_endpoint *ep;
struct rmnet_port *port;
unsigned long bkt_ep;
LIST_HEAD(list);
- if (!rmnet_is_real_dev_registered(real_dev))
- return;
-
ASSERT_RTNL();
- port = rmnet_get_port_rtnl(dev);
+ port = rmnet_get_port_rtnl(real_dev);
- rmnet_unregister_bridge(dev, port);
+ rmnet_unregister_bridge(real_dev, port);
hash_for_each_safe(port->muxed_ep, bkt_ep, tmp_ep, ep, hlnode) {
+ netdev_upper_dev_unlink(real_dev, ep->egress_dev);
unregister_netdevice_queue(ep->egress_dev, &list);
rmnet_vnd_dellink(ep->mux_id, port, ep);
-
hlist_del_init_rcu(&ep->hlnode);
kfree(ep);
}
@@ -247,15 +244,15 @@ static void rmnet_force_unassociate_device(struct net_device *dev)
static int rmnet_config_notify_cb(struct notifier_block *nb,
unsigned long event, void *data)
{
- struct net_device *dev = netdev_notifier_info_to_dev(data);
+ struct net_device *real_dev = netdev_notifier_info_to_dev(data);
- if (!dev)
+ if (!rmnet_is_real_dev_registered(real_dev))
return NOTIFY_DONE;
switch (event) {
case NETDEV_UNREGISTER:
- netdev_dbg(dev, "Kernel unregister\n");
- rmnet_force_unassociate_device(dev);
+ netdev_dbg(real_dev, "Kernel unregister\n");
+ rmnet_force_unassociate_device(real_dev);
break;
default:
@@ -485,8 +482,8 @@ static int __init rmnet_init(void)
static void __exit rmnet_exit(void)
{
- unregister_netdevice_notifier(&rmnet_dev_notifier);
rtnl_link_unregister(&rmnet_link_ops);
+ unregister_netdevice_notifier(&rmnet_dev_notifier);
}
module_init(rmnet_init)
netdev_upper_dev_link() is useful to manage lower/upper interfaces. And this function internally validates looping, maximum depth. All or most virtual interfaces that could have a real interface (e.g. macsec, macvlan, ipvlan etc.) use lower/upper infrastructure. Test commands: ip link add dummy0 type dummy ip link add rmnet1 link dummy0 type rmnet mux_id 1 for i in {2..100} do let A=$i-1 ip link add rmnet$i link rmnet$A type rmnet mux_id $i done ip link del dummy0 The purpose of the test commands is to make stack overflow. Splat looks like: [ 54.352441][ T2169] BUG: KASAN: use-after-free in usage_match+0x85/0xa0 [ 54.353332][ T2169] Read of size 8 at addr ffff8880a6eae420 by task ip/2169 [ 54.404290][ T2169] [ 54.404615][ T2169] CPU: 1 PID: 2169 Comm: ip Not tainted 5.5.0+ #422 [ 54.409543][ T2169] Hardware name: innotek GmbH VirtualBox/VirtualBox, BIOS VirtualBox 12/01/2006 [ 54.410619][ T2169] Call Trace: [ 54.411051][ T2169] [ 54.411345][ T2169] Allocated by task 1: [ 54.793962][ T2169] (stack is not available) [ 54.794566][ T2169] [ 54.794880][ T2169] Freed by task 2519193708: [ 54.795487][ T2169] BUG: unable to handle page fault for address: ffffffff9b9dbe58 [ 54.796508][ T2169] #PF: supervisor read access in kernel mode [ 54.797274][ T2169] #PF: error_code(0x0000) - not-present page [ 54.798058][ T2169] PGD 11b818067 P4D 11b818067 PUD 11b819063 PMD 0 [ 54.798879][ T2169] Thread overran stack, or stack corrupted [ 54.799451][ T2169] Oops: 0000 [#1] SMP DEBUG_PAGEALLOC KASAN PTI [ 55.293798][ T2169] CPU: 1 PID: 2169 Comm: ip Not tainted 5.5.0+ #422 [ 55.294661][ T2169] Hardware name: innotek GmbH VirtualBox/VirtualBox, BIOS VirtualBox 12/01/2006 [ 55.296407][ T2169] RIP: 0010:stack_depot_fetch+0x10/0x30 [ 55.297122][ T2169] Code: ff e9 f9 fe ff ff 48 89 df e8 9c 1d 91 ff e9 ca fe ff ff cc cc cc cc cc cc cc 89 f8 0 [ 55.299622][ T2169] RSP: 0018:ffff8880a6eae2c8 EFLAGS: 00010006 [ 55.798954][ T2169] RAX: 00000000001fffff RBX: ffff8880a6eaea48 RCX: 0000000000000000 [ 55.799997][ T2169] RDX: 0000000000000019 RSI: ffff8880a6eae2d0 RDI: 0000000000003ff0 [ 55.801075][ T2169] RBP: ffffea00029baa00 R08: ffffed101aabe403 R09: ffffed101aabe403 [ 55.802147][ T2169] R10: 0000000000000001 R11: ffffed101aabe402 R12: ffff8880d0660400 [ 55.803174][ T2169] R13: ffff8880a6ead900 R14: ffff8880a6eae4f0 R15: ffff8880a6eae610 [ 55.804192][ T2169] FS: 00007fc124f030c0(0000) GS:ffff8880d5400000(0000) knlGS:0000000000000000 [ 55.805328][ T2169] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 [ 55.806193][ T2169] CR2: ffffffff9b9dbe58 CR3: 00000000a9a20004 CR4: 00000000000606e0 [ 55.807223][ T2169] Call Trace: [ 55.807642][ T2169] Modules linked in: rmnet dummy veth openvswitch nsh nf_conncount nf_nat nf_conntrack nf_dex [ 56.293411][ T2169] CR2: ffffffff9b9dbe58 [ 56.294041][ T2169] ---[ end trace 0c069624f07c5c8c ]--- [ 56.294761][ T2169] RIP: 0010:stack_depot_fetch+0x10/0x30 [ 56.295494][ T2169] Code: ff e9 f9 fe ff ff 48 89 df e8 9c 1d 91 ff e9 ca fe ff ff cc cc cc cc cc cc cc 89 f8 0 [ 56.298122][ T2169] RSP: 0018:ffff8880a6eae2c8 EFLAGS: 00010006 [ 56.298728][ T2169] RAX: 00000000001fffff RBX: ffff8880a6eaea48 RCX: 0000000000000000 [ 56.299770][ T2169] RDX: 0000000000000019 RSI: ffff8880a6eae2d0 RDI: 0000000000003ff0 [ 56.799064][ T2169] RBP: ffffea00029baa00 R08: ffffed101aabe403 R09: ffffed101aabe403 [ 56.800117][ T2169] R10: 0000000000000001 R11: ffffed101aabe402 R12: ffff8880d0660400 [ 56.801137][ T2169] R13: ffff8880a6ead900 R14: ffff8880a6eae4f0 R15: ffff8880a6eae610 [ 56.802181][ T2169] FS: 00007fc124f030c0(0000) GS:ffff8880d5400000(0000) knlGS:0000000000000000 [ 56.803325][ T2169] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 [ 56.804173][ T2169] CR2: ffffffff9b9dbe58 CR3: 00000000a9a20004 CR4: 00000000000606e0 [ 56.805198][ T2169] Kernel panic - not syncing: Fatal exception Fixes: b37f78f234bf ("net: qualcomm: rmnet: Fix crash on real dev unregistration") Signed-off-by: Taehee Yoo <ap420073@gmail.com> --- .../ethernet/qualcomm/rmnet/rmnet_config.c | 35 +++++++++---------- 1 file changed, 16 insertions(+), 19 deletions(-)