| From 1a83f4a7c156fa6bbd6b530e89fa3270bf3d9d1b Mon Sep 17 00:00:00 2001 |
| From: Jakub Kicinski <kuba@kernel.org> |
| Date: Tue, 17 Oct 2023 18:38:15 -0700 |
| Subject: net: avoid UAF on deleted altname |
| |
| From: Jakub Kicinski <kuba@kernel.org> |
| |
| commit 1a83f4a7c156fa6bbd6b530e89fa3270bf3d9d1b upstream. |
| |
| Altnames are accessed under RCU (dev_get_by_name_rcu()) |
| but freed by kfree() with no synchronization point. |
| |
| Each node has one or two allocations (node and a variable-size |
| name, sometimes the name is netdev->name). Adding rcu_heads |
| here is a bit tedious. Besides most code which unlists the names |
| already has rcu barriers - so take the simpler approach of adding |
| synchronize_rcu(). Note that the one on the unregistration path |
| (which matters more) is removed by the next fix. |
| |
| Fixes: ff92741270bf ("net: introduce name_node struct to be used in hashlist") |
| Reviewed-by: Jiri Pirko <jiri@nvidia.com> |
| Signed-off-by: Jakub Kicinski <kuba@kernel.org> |
| Signed-off-by: Paolo Abeni <pabeni@redhat.com> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| --- |
| net/core/dev.c | 7 ++++++- |
| 1 file changed, 6 insertions(+), 1 deletion(-) |
| |
| --- a/net/core/dev.c |
| +++ b/net/core/dev.c |
| @@ -343,7 +343,6 @@ int netdev_name_node_alt_create(struct n |
| static void __netdev_name_node_alt_destroy(struct netdev_name_node *name_node) |
| { |
| list_del(&name_node->list); |
| - netdev_name_node_del(name_node); |
| kfree(name_node->name); |
| netdev_name_node_free(name_node); |
| } |
| @@ -362,6 +361,8 @@ int netdev_name_node_alt_destroy(struct |
| if (name_node == dev->name_node || name_node->dev != dev) |
| return -EINVAL; |
| |
| + netdev_name_node_del(name_node); |
| + synchronize_rcu(); |
| __netdev_name_node_alt_destroy(name_node); |
| |
| return 0; |
| @@ -10838,6 +10839,7 @@ void unregister_netdevice_many_notify(st |
| synchronize_net(); |
| |
| list_for_each_entry(dev, head, unreg_list) { |
| + struct netdev_name_node *name_node; |
| struct sk_buff *skb = NULL; |
| |
| /* Shutdown queueing discipline. */ |
| @@ -10865,6 +10867,9 @@ void unregister_netdevice_many_notify(st |
| dev_uc_flush(dev); |
| dev_mc_flush(dev); |
| |
| + netdev_for_each_altname(dev, name_node) |
| + netdev_name_node_del(name_node); |
| + synchronize_rcu(); |
| netdev_name_node_alt_flush(dev); |
| netdev_name_node_free(dev->name_node); |
| |