Skip to content

[lts86] tun: avoid double free in tun_free_netdev #451

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jul 31, 2025
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
113 changes: 59 additions & 54 deletions drivers/net/tun.c
Original file line number Diff line number Diff line change
Expand Up @@ -251,13 +251,19 @@ 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;
};

struct veth {
__be16 h_vlan_proto;
__be16 h_vlan_TCI;
};

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 = container_of(napi, struct tun_file, napi);
Expand Down Expand Up @@ -996,6 +1002,49 @@ static int check_filter(struct tap_filter *filter, const struct sk_buff *skb)

static const struct ethtool_ops tun_ethtool_ops;

static int tun_net_init(struct net_device *dev)
{
struct tun_struct *tun = netdev_priv(dev);
struct ifreq *ifr = tun->ifr;
int err;

tun->pcpu_stats = netdev_alloc_pcpu_stats(struct tun_pcpu_stats);
if (!tun->pcpu_stats)
return -ENOMEM;

spin_lock_init(&tun->lock);

err = security_tun_dev_alloc_security(&tun->security);
if (err < 0) {
free_percpu(tun->pcpu_stats);
return err;
}

tun_flow_init(tun);

dev->hw_features = NETIF_F_SG | NETIF_F_FRAGLIST |
TUN_USER_FEATURES | NETIF_F_HW_VLAN_CTAG_TX |
NETIF_F_HW_VLAN_STAG_TX;
dev->features = dev->hw_features | NETIF_F_LLTX;
dev->vlan_features = dev->features &
~(NETIF_F_HW_VLAN_CTAG_TX |
NETIF_F_HW_VLAN_STAG_TX);

tun->flags = (tun->flags & ~TUN_FEATURES) |
(ifr->ifr_flags & TUN_FEATURES);

INIT_LIST_HEAD(&tun->disabled);
err = 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)
{
Expand Down Expand Up @@ -1274,6 +1323,7 @@ static int tun_net_change_carrier(struct net_device *dev, bool new_carrier)
}

static const struct net_device_ops tun_netdev_ops = {
.ndo_init = tun_net_init,
.ndo_uninit = tun_net_uninit,
.ndo_open = tun_net_open,
.ndo_stop = tun_net_close,
Expand Down Expand Up @@ -1360,6 +1410,7 @@ static int tun_xdp_tx(struct net_device *dev, struct xdp_buff *xdp)
}

static const struct net_device_ops tap_netdev_ops = {
.ndo_init = tun_net_init,
.ndo_uninit = tun_net_uninit,
.ndo_open = tun_net_open,
.ndo_stop = tun_net_close,
Expand Down Expand Up @@ -1403,7 +1454,7 @@ static void tun_flow_uninit(struct tun_struct *tun)
#define MAX_MTU 65535

/* 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 = netdev_priv(dev);

Expand Down Expand Up @@ -2825,9 +2876,6 @@ static int tun_set_iff(struct net *net, struct file *file, struct ifreq *ifr)

if (!dev)
return -ENOMEM;
err = dev_get_valid_name(net, dev, name);
if (err < 0)
goto err_free_dev;

dev_net_set(dev, net);
dev->rtnl_link_ops = &tun_link_ops;
Expand All @@ -2846,41 +2894,16 @@ static int tun_set_iff(struct net *net, struct file *file, struct ifreq *ifr)
tun->rx_batched = 0;
RCU_INIT_POINTER(tun->steering_prog, NULL);

tun->pcpu_stats = netdev_alloc_pcpu_stats(struct tun_pcpu_stats);
if (!tun->pcpu_stats) {
err = -ENOMEM;
goto err_free_dev;
}
tun->ifr = ifr;
tun->file = file;

spin_lock_init(&tun->lock);

err = 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 = NETIF_F_SG | NETIF_F_FRAGLIST |
TUN_USER_FEATURES | NETIF_F_HW_VLAN_CTAG_TX |
NETIF_F_HW_VLAN_STAG_TX;
dev->features = dev->hw_features | NETIF_F_LLTX;
dev->vlan_features = dev->features &
~(NETIF_F_HW_VLAN_CTAG_TX |
NETIF_F_HW_VLAN_STAG_TX);

tun->flags = (tun->flags & ~TUN_FEATURES) |
(ifr->ifr_flags & TUN_FEATURES);

INIT_LIST_HEAD(&tun->disabled);
err = 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);

err = 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.
*/
Expand All @@ -2899,24 +2922,6 @@ static int tun_set_iff(struct net *net, struct file *file, struct ifreq *ifr)

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;
}

static void tun_get_iff(struct tun_struct *tun, struct ifreq *ifr)
Expand Down