On Mon, 7 Oct 2019 12:21:05 -0700, Eric Dumazet wrote: > syzbot reported a warning [1] that triggered after recent Jiri patch. > > This exposes a bug that we hit already in the past (see commit > ff244c6b29b1 ("tun: handle register_netdevice() failures properly") > for details) > > tun uses priv->destructor without an ndo_init() method. > > register_netdevice() can return an error, but will > not call priv->destructor() in some cases. Jiri recent > patch added one more. > > A long term fix would be to transfer the initialization > of what we destroy in ->destructor() in the ndo_init() > > This looks a bit risky given the complexity of tun driver. > > A simpler fix is to detect after the failed register_netdevice() > if the tun_free_netdev() function was called already. > > [...] > > Fixes: ff92741270bf ("net: introduce name_node struct to be used in hashlist") > Signed-off-by: Eric Dumazet <eduma...@google.com> > Cc: Jiri Pirko <j...@mellanox.com> > Reported-by: syzbot <syzkal...@googlegroups.com>
Looks good, obviously. Presumably we could remove the workaround added by commit 0ad646c81b21 ("tun: call dev_get_valid_name() before register_netdevice()") at this point? What are your thoughts on that? > diff --git a/drivers/net/tun.c b/drivers/net/tun.c > index > 812dc3a65efbb9d1ee2724e73978dbc4803ec171..1e541b08b136364302aa524e31efb62062c43faa > 100644 > --- a/drivers/net/tun.c > +++ b/drivers/net/tun.c > @@ -2290,7 +2290,13 @@ static void tun_free_netdev(struct net_device *dev) > struct tun_struct *tun = netdev_priv(dev); > > BUG_ON(!(list_empty(&tun->disabled))); > + > 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 = NULL; > + > tun_flow_uninit(tun); > security_tun_dev_free_security(tun->security); > __tun_set_ebpf(tun, &tun->steering_prog, NULL); > @@ -2859,8 +2865,12 @@ static int tun_set_iff(struct net *net, struct file > *file, struct ifreq *ifr) > > err_detach: > tun_detach_all(dev); > - /* register_netdevice() already called tun_free_netdev() */ > - goto err_free_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);