Skip to content

Commit 8eb43d6

Browse files
gkennedy12gregkh
authored andcommitted
tun: avoid double free in tun_free_netdev
commit 158b515 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+0x1a/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: <TASK> __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 Reported-by: syzkaller <[email protected]> Signed-off-by: George Kennedy <[email protected]> Suggested-by: Jakub Kicinski <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Jakub Kicinski <[email protected]> [DP: adjusted context for 4.19 stable] Signed-off-by: Dragos-Marian Panait <[email protected]> Signed-off-by: Greg Kroah-Hartman <[email protected]>
1 parent 30d0a53 commit 8eb43d6

File tree

1 file changed

+59
-50
lines changed

1 file changed

+59
-50
lines changed

drivers/net/tun.c

Lines changed: 59 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,9 @@ struct tun_struct {
256256
struct tun_prog __rcu *steering_prog;
257257
struct tun_prog __rcu *filter_prog;
258258
struct ethtool_link_ksettings link_ksettings;
259+
/* init args */
260+
struct file *file;
261+
struct ifreq *ifr;
259262
};
260263

261264
struct veth {
@@ -281,6 +284,9 @@ void *tun_ptr_to_xdp(void *ptr)
281284
}
282285
EXPORT_SYMBOL(tun_ptr_to_xdp);
283286

287+
static void tun_flow_init(struct tun_struct *tun);
288+
static void tun_flow_uninit(struct tun_struct *tun);
289+
284290
static int tun_napi_receive(struct napi_struct *napi, int budget)
285291
{
286292
struct tun_file *tfile = container_of(napi, struct tun_file, napi);
@@ -1038,6 +1044,49 @@ static int check_filter(struct tap_filter *filter, const struct sk_buff *skb)
10381044

10391045
static const struct ethtool_ops tun_ethtool_ops;
10401046

1047+
static int tun_net_init(struct net_device *dev)
1048+
{
1049+
struct tun_struct *tun = netdev_priv(dev);
1050+
struct ifreq *ifr = tun->ifr;
1051+
int err;
1052+
1053+
tun->pcpu_stats = netdev_alloc_pcpu_stats(struct tun_pcpu_stats);
1054+
if (!tun->pcpu_stats)
1055+
return -ENOMEM;
1056+
1057+
spin_lock_init(&tun->lock);
1058+
1059+
err = security_tun_dev_alloc_security(&tun->security);
1060+
if (err < 0) {
1061+
free_percpu(tun->pcpu_stats);
1062+
return err;
1063+
}
1064+
1065+
tun_flow_init(tun);
1066+
1067+
dev->hw_features = NETIF_F_SG | NETIF_F_FRAGLIST |
1068+
TUN_USER_FEATURES | NETIF_F_HW_VLAN_CTAG_TX |
1069+
NETIF_F_HW_VLAN_STAG_TX;
1070+
dev->features = dev->hw_features | NETIF_F_LLTX;
1071+
dev->vlan_features = dev->features &
1072+
~(NETIF_F_HW_VLAN_CTAG_TX |
1073+
NETIF_F_HW_VLAN_STAG_TX);
1074+
1075+
tun->flags = (tun->flags & ~TUN_FEATURES) |
1076+
(ifr->ifr_flags & TUN_FEATURES);
1077+
1078+
INIT_LIST_HEAD(&tun->disabled);
1079+
err = tun_attach(tun, tun->file, false, ifr->ifr_flags & IFF_NAPI,
1080+
ifr->ifr_flags & IFF_NAPI_FRAGS, false);
1081+
if (err < 0) {
1082+
tun_flow_uninit(tun);
1083+
security_tun_dev_free_security(tun->security);
1084+
free_percpu(tun->pcpu_stats);
1085+
return err;
1086+
}
1087+
return 0;
1088+
}
1089+
10411090
/* Net device detach from fd. */
10421091
static void tun_net_uninit(struct net_device *dev)
10431092
{
@@ -1268,6 +1317,7 @@ static int tun_xdp(struct net_device *dev, struct netdev_bpf *xdp)
12681317
}
12691318

12701319
static const struct net_device_ops tun_netdev_ops = {
1320+
.ndo_init = tun_net_init,
12711321
.ndo_uninit = tun_net_uninit,
12721322
.ndo_open = tun_net_open,
12731323
.ndo_stop = tun_net_close,
@@ -1347,6 +1397,7 @@ static int tun_xdp_tx(struct net_device *dev, struct xdp_buff *xdp)
13471397
}
13481398

13491399
static const struct net_device_ops tap_netdev_ops = {
1400+
.ndo_init = tun_net_init,
13501401
.ndo_uninit = tun_net_uninit,
13511402
.ndo_open = tun_net_open,
13521403
.ndo_stop = tun_net_close,
@@ -1386,7 +1437,7 @@ static void tun_flow_uninit(struct tun_struct *tun)
13861437
#define MAX_MTU 65535
13871438

13881439
/* Initialize net device. */
1389-
static void tun_net_init(struct net_device *dev)
1440+
static void tun_net_initialize(struct net_device *dev)
13901441
{
13911442
struct tun_struct *tun = netdev_priv(dev);
13921443

@@ -2658,9 +2709,6 @@ static int tun_set_iff(struct net *net, struct file *file, struct ifreq *ifr)
26582709

26592710
if (!dev)
26602711
return -ENOMEM;
2661-
err = dev_get_valid_name(net, dev, name);
2662-
if (err < 0)
2663-
goto err_free_dev;
26642712

26652713
dev_net_set(dev, net);
26662714
dev->rtnl_link_ops = &tun_link_ops;
@@ -2679,41 +2727,16 @@ static int tun_set_iff(struct net *net, struct file *file, struct ifreq *ifr)
26792727
tun->rx_batched = 0;
26802728
RCU_INIT_POINTER(tun->steering_prog, NULL);
26812729

2682-
tun->pcpu_stats = netdev_alloc_pcpu_stats(struct tun_pcpu_stats);
2683-
if (!tun->pcpu_stats) {
2684-
err = -ENOMEM;
2685-
goto err_free_dev;
2686-
}
2687-
2688-
spin_lock_init(&tun->lock);
2689-
2690-
err = security_tun_dev_alloc_security(&tun->security);
2691-
if (err < 0)
2692-
goto err_free_stat;
2693-
2694-
tun_net_init(dev);
2695-
tun_flow_init(tun);
2696-
2697-
dev->hw_features = NETIF_F_SG | NETIF_F_FRAGLIST |
2698-
TUN_USER_FEATURES | NETIF_F_HW_VLAN_CTAG_TX |
2699-
NETIF_F_HW_VLAN_STAG_TX;
2700-
dev->features = dev->hw_features | NETIF_F_LLTX;
2701-
dev->vlan_features = dev->features &
2702-
~(NETIF_F_HW_VLAN_CTAG_TX |
2703-
NETIF_F_HW_VLAN_STAG_TX);
2730+
tun->ifr = ifr;
2731+
tun->file = file;
27042732

2705-
tun->flags = (tun->flags & ~TUN_FEATURES) |
2706-
(ifr->ifr_flags & TUN_FEATURES);
2707-
2708-
INIT_LIST_HEAD(&tun->disabled);
2709-
err = tun_attach(tun, file, false, ifr->ifr_flags & IFF_NAPI,
2710-
ifr->ifr_flags & IFF_NAPI_FRAGS, false);
2711-
if (err < 0)
2712-
goto err_free_flow;
2733+
tun_net_initialize(dev);
27132734

27142735
err = register_netdevice(tun->dev);
2715-
if (err < 0)
2716-
goto err_detach;
2736+
if (err < 0) {
2737+
free_netdev(dev);
2738+
return err;
2739+
}
27172740
/* free_netdev() won't check refcnt, to aovid race
27182741
* with dev_put() we need publish tun after registration.
27192742
*/
@@ -2732,20 +2755,6 @@ static int tun_set_iff(struct net *net, struct file *file, struct ifreq *ifr)
27322755

27332756
strcpy(ifr->ifr_name, tun->dev->name);
27342757
return 0;
2735-
2736-
err_detach:
2737-
tun_detach_all(dev);
2738-
/* register_netdevice() already called tun_free_netdev() */
2739-
goto err_free_dev;
2740-
2741-
err_free_flow:
2742-
tun_flow_uninit(tun);
2743-
security_tun_dev_free_security(tun->security);
2744-
err_free_stat:
2745-
free_percpu(tun->pcpu_stats);
2746-
err_free_dev:
2747-
free_netdev(dev);
2748-
return err;
27492758
}
27502759

27512760
static void tun_get_iff(struct net *net, struct tun_struct *tun,

0 commit comments

Comments
 (0)