From nobody Sun Apr 12 00:56:21 2026 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 9C2EAC00140 for ; Tue, 2 Aug 2022 15:49:23 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S237086AbiHBPtU (ORCPT ); Tue, 2 Aug 2022 11:49:20 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:49788 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229659AbiHBPs7 (ORCPT ); Tue, 2 Aug 2022 11:48:59 -0400 Received: from mail.ispras.ru (mail.ispras.ru [83.149.199.84]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 7C0854599F; Tue, 2 Aug 2022 08:48:04 -0700 (PDT) Received: from localhost.localdomain (unknown [83.149.199.65]) by mail.ispras.ru (Postfix) with ESMTPSA id C093540737B1; Tue, 2 Aug 2022 15:47:57 +0000 (UTC) From: Fedor Pchelkin To: stable@vger.kernel.org, gregkh@linuxfoundation.org Cc: Fedor Pchelkin , "David S. Miller" , Jakub Kicinski , netdev@vger.kernel.org, linux-kernel@vger.kernel.org, Alexey Khoroshilov , ldv-project@linuxtesting.org, syzkaller , George Kennedy Subject: [PATCH backport for 5.10] tun: avoid double free in tun_free_netdev Date: Tue, 2 Aug 2022 18:47:11 +0300 Message-Id: <20220802154711.1745023-1-pchelkin@ispras.ru> X-Mailer: git-send-email 2.25.1 MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Type: text/plain; charset="utf-8" From: George Kennedy commit 158b515f703e75e7d68289bf4d98c664e1d632df upstream. Avoid double free in tun_free_netdev() by moving the dev->tstats and tun->security allocs to a new ndo_init routine (tun_net_init()) that will be called by register_netdevice(). ndo_init is paired with the desctructor (tun_free_netdev()), so if there's an error in register_netdevice() the destructor will handle the frees. BUG: KASAN: double-free or invalid-free in selinux_tun_dev_free_security+0x= 1a/0x20 security/selinux/hooks.c:5605 CPU: 0 PID: 25750 Comm: syz-executor416 Not tainted 5.16.0-rc2-syzk #1 Hardware name: Red Hat KVM, BIOS Call Trace: __dump_stack lib/dump_stack.c:88 [inline] dump_stack_lvl+0x89/0xb5 lib/dump_stack.c:106 print_address_description.constprop.9+0x28/0x160 mm/kasan/report.c:247 kasan_report_invalid_free+0x55/0x80 mm/kasan/report.c:372 ____kasan_slab_free mm/kasan/common.c:346 [inline] __kasan_slab_free+0x107/0x120 mm/kasan/common.c:374 kasan_slab_free include/linux/kasan.h:235 [inline] slab_free_hook mm/slub.c:1723 [inline] slab_free_freelist_hook mm/slub.c:1749 [inline] slab_free mm/slub.c:3513 [inline] kfree+0xac/0x2d0 mm/slub.c:4561 selinux_tun_dev_free_security+0x1a/0x20 security/selinux/hooks.c:5605 security_tun_dev_free_security+0x4f/0x90 security/security.c:2342 tun_free_netdev+0xe6/0x150 drivers/net/tun.c:2215 netdev_run_todo+0x4df/0x840 net/core/dev.c:10627 rtnl_unlock+0x13/0x20 net/core/rtnetlink.c:112 __tun_chr_ioctl+0x80c/0x2870 drivers/net/tun.c:3302 tun_chr_ioctl+0x2f/0x40 drivers/net/tun.c:3311 vfs_ioctl fs/ioctl.c:51 [inline] __do_sys_ioctl fs/ioctl.c:874 [inline] __se_sys_ioctl fs/ioctl.c:860 [inline] __x64_sys_ioctl+0x19d/0x220 fs/ioctl.c:860 do_syscall_x64 arch/x86/entry/common.c:50 [inline] do_syscall_64+0x3a/0x80 arch/x86/entry/common.c:80 entry_SYSCALL_64_after_hwframe+0x44/0xae Backport commit info: the original patch logic was not changed. The only modified fragments are related to upstream commit 497a5757ce4e ("tun: switch to net core provided statistics counters"): struct tun_pcpu_stats is still used instead of net core provided statistics counters. Reported-by: syzkaller Signed-off-by: George Kennedy Suggested-by: Jakub Kicinski Link: https://lore.kernel.org/r/1639679132-19884-1-git-send-email-george.ke= nnedy@oracle.com Signed-off-by: Jakub Kicinski Signed-off-by: Fedor Pchelkin --- drivers/net/tun.c | 114 ++++++++++++++++++++++++---------------------- 1 file changed, 59 insertions(+), 55 deletions(-) diff --git a/drivers/net/tun.c b/drivers/net/tun.c index be9ff6a74ecc..a643b2f2f4de 100644 --- a/drivers/net/tun.c +++ b/drivers/net/tun.c @@ -220,6 +220,9 @@ struct tun_struct { struct tun_prog __rcu *steering_prog; struct tun_prog __rcu *filter_prog; struct ethtool_link_ksettings link_ksettings; + /* init args */ + struct file *file; + struct ifreq *ifr; }; =20 struct veth { @@ -227,6 +230,9 @@ struct veth { __be16 h_vlan_TCI; }; =20 +static void tun_flow_init(struct tun_struct *tun); +static void tun_flow_uninit(struct tun_struct *tun); + static int tun_napi_receive(struct napi_struct *napi, int budget) { struct tun_file *tfile =3D container_of(napi, struct tun_file, napi); @@ -975,6 +981,49 @@ static int check_filter(struct tap_filter *filter, con= st struct sk_buff *skb) =20 static const struct ethtool_ops tun_ethtool_ops; =20 +static int tun_net_init(struct net_device *dev) +{ + struct tun_struct *tun =3D netdev_priv(dev); + struct ifreq *ifr =3D tun->ifr; + int err; + + tun->pcpu_stats =3D netdev_alloc_pcpu_stats(struct tun_pcpu_stats); + if (!tun->pcpu_stats) + return -ENOMEM; + + spin_lock_init(&tun->lock); + + err =3D security_tun_dev_alloc_security(&tun->security); + if (err < 0) { + free_percpu(tun->pcpu_stats); + return err; + } + + tun_flow_init(tun); + + dev->hw_features =3D NETIF_F_SG | NETIF_F_FRAGLIST | + TUN_USER_FEATURES | NETIF_F_HW_VLAN_CTAG_TX | + NETIF_F_HW_VLAN_STAG_TX; + dev->features =3D dev->hw_features | NETIF_F_LLTX; + dev->vlan_features =3D dev->features & + ~(NETIF_F_HW_VLAN_CTAG_TX | + NETIF_F_HW_VLAN_STAG_TX); + + tun->flags =3D (tun->flags & ~TUN_FEATURES) | + (ifr->ifr_flags & TUN_FEATURES); + + INIT_LIST_HEAD(&tun->disabled); + err =3D tun_attach(tun, tun->file, false, ifr->ifr_flags & IFF_NAPI, + ifr->ifr_flags & IFF_NAPI_FRAGS, false); + if (err < 0) { + tun_flow_uninit(tun); + security_tun_dev_free_security(tun->security); + free_percpu(tun->pcpu_stats); + return err; + } + return 0; +} + /* Net device detach from fd. */ static void tun_net_uninit(struct net_device *dev) { @@ -1216,6 +1265,7 @@ static int tun_net_change_carrier(struct net_device *= dev, bool new_carrier) } =20 static const struct net_device_ops tun_netdev_ops =3D { + .ndo_init =3D tun_net_init, .ndo_uninit =3D tun_net_uninit, .ndo_open =3D tun_net_open, .ndo_stop =3D tun_net_close, @@ -1296,6 +1346,7 @@ static int tun_xdp_tx(struct net_device *dev, struct = xdp_buff *xdp) } =20 static const struct net_device_ops tap_netdev_ops =3D { + .ndo_init =3D tun_net_init, .ndo_uninit =3D tun_net_uninit, .ndo_open =3D tun_net_open, .ndo_stop =3D tun_net_close, @@ -1336,7 +1387,7 @@ static void tun_flow_uninit(struct tun_struct *tun) #define MAX_MTU 65535 =20 /* Initialize net device. */ -static void tun_net_init(struct net_device *dev) +static void tun_net_initialize(struct net_device *dev) { struct tun_struct *tun =3D netdev_priv(dev); =20 @@ -2268,10 +2319,6 @@ static void tun_free_netdev(struct net_device *dev) BUG_ON(!(list_empty(&tun->disabled))); =20 free_percpu(tun->pcpu_stats); - /* We clear pcpu_stats so that tun_set_iff() can tell if - * tun_free_netdev() has been called from register_netdevice(). - */ - tun->pcpu_stats =3D NULL; =20 tun_flow_uninit(tun); security_tun_dev_free_security(tun->security); @@ -2784,41 +2831,16 @@ static int tun_set_iff(struct net *net, struct file= *file, struct ifreq *ifr) tun->rx_batched =3D 0; RCU_INIT_POINTER(tun->steering_prog, NULL); =20 - tun->pcpu_stats =3D netdev_alloc_pcpu_stats(struct tun_pcpu_stats); - if (!tun->pcpu_stats) { - err =3D -ENOMEM; - goto err_free_dev; - } + tun->ifr =3D ifr; + tun->file =3D file; =20 - spin_lock_init(&tun->lock); - - err =3D security_tun_dev_alloc_security(&tun->security); - if (err < 0) - goto err_free_stat; - - tun_net_init(dev); - tun_flow_init(tun); - - dev->hw_features =3D NETIF_F_SG | NETIF_F_FRAGLIST | - TUN_USER_FEATURES | NETIF_F_HW_VLAN_CTAG_TX | - NETIF_F_HW_VLAN_STAG_TX; - dev->features =3D dev->hw_features | NETIF_F_LLTX; - dev->vlan_features =3D dev->features & - ~(NETIF_F_HW_VLAN_CTAG_TX | - NETIF_F_HW_VLAN_STAG_TX); - - tun->flags =3D (tun->flags & ~TUN_FEATURES) | - (ifr->ifr_flags & TUN_FEATURES); - - INIT_LIST_HEAD(&tun->disabled); - err =3D tun_attach(tun, file, false, ifr->ifr_flags & IFF_NAPI, - ifr->ifr_flags & IFF_NAPI_FRAGS, false); - if (err < 0) - goto err_free_flow; + tun_net_initialize(dev); =20 err =3D register_netdevice(tun->dev); - if (err < 0) - goto err_detach; + if (err < 0) { + free_netdev(dev); + return err; + } /* free_netdev() won't check refcnt, to aovid race * with dev_put() we need publish tun after registration. */ @@ -2835,24 +2857,6 @@ static int tun_set_iff(struct net *net, struct file = *file, struct ifreq *ifr) =20 strcpy(ifr->ifr_name, tun->dev->name); return 0; - -err_detach: - tun_detach_all(dev); - /* We are here because register_netdevice() has failed. - * If register_netdevice() already called tun_free_netdev() - * while dealing with the error, tun->pcpu_stats has been cleared. - */ - if (!tun->pcpu_stats) - goto err_free_dev; - -err_free_flow: - tun_flow_uninit(tun); - security_tun_dev_free_security(tun->security); -err_free_stat: - free_percpu(tun->pcpu_stats); -err_free_dev: - free_netdev(dev); - return err; } =20 static void tun_get_iff(struct tun_struct *tun, struct ifreq *ifr) --=20 2.25.1