Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

wireguard: device: avoid circular netns references

Before, we took a reference to the creating netns if the new netns was
different. This caused issues with circular references, with two
wireguard interfaces swapping namespaces. The solution is to rather not
take any extra references at all, but instead simply invalidate the
creating netns pointer when that netns is deleted.

In order to prevent this from happening again, this commit improves the
rough object leak tracking by allowing it to account for created and
destroyed interfaces, aside from just peers and keys. That then makes it
possible to check for the object leak when having two interfaces take a
reference to each others' namespaces.

Fixes: e7096c131e51 ("net: WireGuard secure network tunnel")
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

Jason A. Donenfeld and committed by
David S. Miller
900575aa 558b353c

+67 -46
+27 -31
drivers/net/wireguard/device.c
··· 45 45 if (dev_v6) 46 46 dev_v6->cnf.addr_gen_mode = IN6_ADDR_GEN_MODE_NONE; 47 47 48 + mutex_lock(&wg->device_update_lock); 48 49 ret = wg_socket_init(wg, wg->incoming_port); 49 50 if (ret < 0) 50 - return ret; 51 - mutex_lock(&wg->device_update_lock); 51 + goto out; 52 52 list_for_each_entry(peer, &wg->peer_list, peer_list) { 53 53 wg_packet_send_staged_packets(peer); 54 54 if (peer->persistent_keepalive_interval) 55 55 wg_packet_send_keepalive(peer); 56 56 } 57 + out: 57 58 mutex_unlock(&wg->device_update_lock); 58 - return 0; 59 + return ret; 59 60 } 60 61 61 62 #ifdef CONFIG_PM_SLEEP ··· 226 225 list_del(&wg->device_list); 227 226 rtnl_unlock(); 228 227 mutex_lock(&wg->device_update_lock); 228 + rcu_assign_pointer(wg->creating_net, NULL); 229 229 wg->incoming_port = 0; 230 230 wg_socket_reinit(wg, NULL, NULL); 231 231 /* The final references are cleared in the below calls to destroy_workqueue. */ ··· 242 240 skb_queue_purge(&wg->incoming_handshakes); 243 241 free_percpu(dev->tstats); 244 242 free_percpu(wg->incoming_handshakes_worker); 245 - if (wg->have_creating_net_ref) 246 - put_net(wg->creating_net); 247 243 kvfree(wg->index_hashtable); 248 244 kvfree(wg->peer_hashtable); 249 245 mutex_unlock(&wg->device_update_lock); 250 246 251 - pr_debug("%s: Interface deleted\n", dev->name); 247 + pr_debug("%s: Interface destroyed\n", dev->name); 252 248 free_netdev(dev); 253 249 } 254 250 ··· 292 292 struct wg_device *wg = netdev_priv(dev); 293 293 int ret = -ENOMEM; 294 294 295 - wg->creating_net = src_net; 295 + rcu_assign_pointer(wg->creating_net, src_net); 296 296 init_rwsem(&wg->static_identity.lock); 297 297 mutex_init(&wg->socket_update_lock); 298 298 mutex_init(&wg->device_update_lock); ··· 393 393 .newlink = wg_newlink, 394 394 }; 395 395 396 - static int wg_netdevice_notification(struct notifier_block *nb, 397 - unsigned long action, void *data) 396 + static void wg_netns_pre_exit(struct net *net) 398 397 { 399 - struct net_device *dev = ((struct netdev_notifier_info *)data)->dev; 400 - struct wg_device *wg = netdev_priv(dev); 398 + struct wg_device *wg; 401 399 402 - ASSERT_RTNL(); 403 - 404 - if (action != NETDEV_REGISTER || dev->netdev_ops != &netdev_ops) 405 - return 0; 406 - 407 - if (dev_net(dev) == wg->creating_net && wg->have_creating_net_ref) { 408 - put_net(wg->creating_net); 409 - wg->have_creating_net_ref = false; 410 - } else if (dev_net(dev) != wg->creating_net && 411 - !wg->have_creating_net_ref) { 412 - wg->have_creating_net_ref = true; 413 - get_net(wg->creating_net); 400 + rtnl_lock(); 401 + list_for_each_entry(wg, &device_list, device_list) { 402 + if (rcu_access_pointer(wg->creating_net) == net) { 403 + pr_debug("%s: Creating namespace exiting\n", wg->dev->name); 404 + netif_carrier_off(wg->dev); 405 + mutex_lock(&wg->device_update_lock); 406 + rcu_assign_pointer(wg->creating_net, NULL); 407 + wg_socket_reinit(wg, NULL, NULL); 408 + mutex_unlock(&wg->device_update_lock); 409 + } 414 410 } 415 - return 0; 411 + rtnl_unlock(); 416 412 } 417 413 418 - static struct notifier_block netdevice_notifier = { 419 - .notifier_call = wg_netdevice_notification 414 + static struct pernet_operations pernet_ops = { 415 + .pre_exit = wg_netns_pre_exit 420 416 }; 421 417 422 418 int __init wg_device_init(void) ··· 425 429 return ret; 426 430 #endif 427 431 428 - ret = register_netdevice_notifier(&netdevice_notifier); 432 + ret = register_pernet_device(&pernet_ops); 429 433 if (ret) 430 434 goto error_pm; 431 435 432 436 ret = rtnl_link_register(&link_ops); 433 437 if (ret) 434 - goto error_netdevice; 438 + goto error_pernet; 435 439 436 440 return 0; 437 441 438 - error_netdevice: 439 - unregister_netdevice_notifier(&netdevice_notifier); 442 + error_pernet: 443 + unregister_pernet_device(&pernet_ops); 440 444 error_pm: 441 445 #ifdef CONFIG_PM_SLEEP 442 446 unregister_pm_notifier(&pm_notifier); ··· 447 451 void wg_device_uninit(void) 448 452 { 449 453 rtnl_link_unregister(&link_ops); 450 - unregister_netdevice_notifier(&netdevice_notifier); 454 + unregister_pernet_device(&pernet_ops); 451 455 #ifdef CONFIG_PM_SLEEP 452 456 unregister_pm_notifier(&pm_notifier); 453 457 #endif
+1 -2
drivers/net/wireguard/device.h
··· 40 40 struct net_device *dev; 41 41 struct crypt_queue encrypt_queue, decrypt_queue; 42 42 struct sock __rcu *sock4, *sock6; 43 - struct net *creating_net; 43 + struct net __rcu *creating_net; 44 44 struct noise_static_identity static_identity; 45 45 struct workqueue_struct *handshake_receive_wq, *handshake_send_wq; 46 46 struct workqueue_struct *packet_crypt_wq; ··· 56 56 unsigned int num_peers, device_update_gen; 57 57 u32 fwmark; 58 58 u16 incoming_port; 59 - bool have_creating_net_ref; 60 59 }; 61 60 62 61 int wg_device_init(void);
+9 -5
drivers/net/wireguard/netlink.c
··· 511 511 if (flags & ~__WGDEVICE_F_ALL) 512 512 goto out; 513 513 514 - ret = -EPERM; 515 - if ((info->attrs[WGDEVICE_A_LISTEN_PORT] || 516 - info->attrs[WGDEVICE_A_FWMARK]) && 517 - !ns_capable(wg->creating_net->user_ns, CAP_NET_ADMIN)) 518 - goto out; 514 + if (info->attrs[WGDEVICE_A_LISTEN_PORT] || info->attrs[WGDEVICE_A_FWMARK]) { 515 + struct net *net; 516 + rcu_read_lock(); 517 + net = rcu_dereference(wg->creating_net); 518 + ret = !net || !ns_capable(net->user_ns, CAP_NET_ADMIN) ? -EPERM : 0; 519 + rcu_read_unlock(); 520 + if (ret) 521 + goto out; 522 + } 519 523 520 524 ++wg->device_update_gen; 521 525
+18 -7
drivers/net/wireguard/socket.c
··· 347 347 348 348 int wg_socket_init(struct wg_device *wg, u16 port) 349 349 { 350 + struct net *net; 350 351 int ret; 351 352 struct udp_tunnel_sock_cfg cfg = { 352 353 .sk_user_data = wg, ··· 372 371 }; 373 372 #endif 374 373 374 + rcu_read_lock(); 375 + net = rcu_dereference(wg->creating_net); 376 + net = net ? maybe_get_net(net) : NULL; 377 + rcu_read_unlock(); 378 + if (unlikely(!net)) 379 + return -ENONET; 380 + 375 381 #if IS_ENABLED(CONFIG_IPV6) 376 382 retry: 377 383 #endif 378 384 379 - ret = udp_sock_create(wg->creating_net, &port4, &new4); 385 + ret = udp_sock_create(net, &port4, &new4); 380 386 if (ret < 0) { 381 387 pr_err("%s: Could not create IPv4 socket\n", wg->dev->name); 382 - return ret; 388 + goto out; 383 389 } 384 390 set_sock_opts(new4); 385 - setup_udp_tunnel_sock(wg->creating_net, new4, &cfg); 391 + setup_udp_tunnel_sock(net, new4, &cfg); 386 392 387 393 #if IS_ENABLED(CONFIG_IPV6) 388 394 if (ipv6_mod_enabled()) { 389 395 port6.local_udp_port = inet_sk(new4->sk)->inet_sport; 390 - ret = udp_sock_create(wg->creating_net, &port6, &new6); 396 + ret = udp_sock_create(net, &port6, &new6); 391 397 if (ret < 0) { 392 398 udp_tunnel_sock_release(new4); 393 399 if (ret == -EADDRINUSE && !port && retries++ < 100) 394 400 goto retry; 395 401 pr_err("%s: Could not create IPv6 socket\n", 396 402 wg->dev->name); 397 - return ret; 403 + goto out; 398 404 } 399 405 set_sock_opts(new6); 400 - setup_udp_tunnel_sock(wg->creating_net, new6, &cfg); 406 + setup_udp_tunnel_sock(net, new6, &cfg); 401 407 } 402 408 #endif 403 409 404 410 wg_socket_reinit(wg, new4->sk, new6 ? new6->sk : NULL); 405 - return 0; 411 + ret = 0; 412 + out: 413 + put_net(net); 414 + return ret; 406 415 } 407 416 408 417 void wg_socket_reinit(struct wg_device *wg, struct sock *new4,
+12 -1
tools/testing/selftests/wireguard/netns.sh
··· 587 587 kill $ncat_pid 588 588 ip0 link del wg0 589 589 590 + # Ensure there aren't circular reference loops 591 + ip1 link add wg1 type wireguard 592 + ip2 link add wg2 type wireguard 593 + ip1 link set wg1 netns $netns2 594 + ip2 link set wg2 netns $netns1 595 + pp ip netns delete $netns1 596 + pp ip netns delete $netns2 597 + pp ip netns add $netns1 598 + pp ip netns add $netns2 599 + 600 + sleep 2 # Wait for cleanup and grace periods 590 601 declare -A objects 591 602 while read -t 0.1 -r line 2>/dev/null || [[ $? -ne 142 ]]; do 592 - [[ $line =~ .*(wg[0-9]+:\ [A-Z][a-z]+\ [0-9]+)\ .*(created|destroyed).* ]] || continue 603 + [[ $line =~ .*(wg[0-9]+:\ [A-Z][a-z]+\ ?[0-9]*)\ .*(created|destroyed).* ]] || continue 593 604 objects["${BASH_REMATCH[1]}"]+="${BASH_REMATCH[2]}" 594 605 done < /dev/kmsg 595 606 alldeleted=1