]> pilppa.org Git - linux-2.6-omap-h63xx.git/commitdiff
tun: Make tun_net_xmit atomic wrt tun_attach && tun_detach
authorEric W. Biederman <ebiederm@xmission.com>
Tue, 20 Jan 2009 11:02:28 +0000 (11:02 +0000)
committerDavid S. Miller <davem@davemloft.net>
Thu, 22 Jan 2009 00:00:45 +0000 (16:00 -0800)
Currently this small race allows for a packet to be received when we
detach from an tun device and still be enqueued.  Not especially
important but not what the code is trying to do.

Signed-off-by: Eric W. Biederman <ebiederm@aristanetworks.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/tun.c

index dfbf586597711a0c5cc1e6f6fa5b24c2b7208963..fa93160bf52244fbf71db7812bf7dccd86cc9278 100644 (file)
@@ -115,25 +115,33 @@ static int tun_attach(struct tun_struct *tun, struct file *file)
 {
        struct tun_file *tfile = file->private_data;
        const struct cred *cred = current_cred();
+       int err;
 
        ASSERT_RTNL();
 
-       if (tfile->tun)
-               return -EINVAL;
-
-       if (tun->tfile)
-               return -EBUSY;
-
        /* Check permissions */
        if (((tun->owner != -1 && cred->euid != tun->owner) ||
             (tun->group != -1 && cred->egid != tun->group)) &&
                !capable(CAP_NET_ADMIN))
                return -EPERM;
 
+       netif_tx_lock_bh(tun->dev);
+
+       err = -EINVAL;
+       if (tfile->tun)
+               goto out;
+
+       err = -EBUSY;
+       if (tun->tfile)
+               goto out;
+
+       err = 0;
        tfile->tun = tun;
        tun->tfile = tfile;
 
-       return 0;
+out:
+       netif_tx_unlock_bh(tun->dev);
+       return err;
 }
 
 static void __tun_detach(struct tun_struct *tun)
@@ -141,8 +149,10 @@ static void __tun_detach(struct tun_struct *tun)
        struct tun_file *tfile = tun->tfile;
 
        /* Detach from net device */
+       netif_tx_lock_bh(tun->dev);
        tfile->tun = NULL;
        tun->tfile = NULL;
+       netif_tx_unlock_bh(tun->dev);
 
        /* Drop read queue */
        skb_queue_purge(&tun->readq);