Commit 38231b7a authored by Eric W. Biederman's avatar Eric W. Biederman Committed by David S. Miller

tun: Make tun_net_xmit atomic wrt tun_attach && tun_detach

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: default avatarEric W. Biederman <ebiederm@aristanetworks.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 36b50bab
...@@ -115,25 +115,33 @@ static int tun_attach(struct tun_struct *tun, struct file *file) ...@@ -115,25 +115,33 @@ static int tun_attach(struct tun_struct *tun, struct file *file)
{ {
struct tun_file *tfile = file->private_data; struct tun_file *tfile = file->private_data;
const struct cred *cred = current_cred(); const struct cred *cred = current_cred();
int err;
ASSERT_RTNL(); ASSERT_RTNL();
if (tfile->tun)
return -EINVAL;
if (tun->tfile)
return -EBUSY;
/* Check permissions */ /* Check permissions */
if (((tun->owner != -1 && cred->euid != tun->owner) || if (((tun->owner != -1 && cred->euid != tun->owner) ||
(tun->group != -1 && cred->egid != tun->group)) && (tun->group != -1 && cred->egid != tun->group)) &&
!capable(CAP_NET_ADMIN)) !capable(CAP_NET_ADMIN))
return -EPERM; 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; tfile->tun = tun;
tun->tfile = tfile; tun->tfile = tfile;
return 0; out:
netif_tx_unlock_bh(tun->dev);
return err;
} }
static void __tun_detach(struct tun_struct *tun) static void __tun_detach(struct tun_struct *tun)
...@@ -141,8 +149,10 @@ 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; struct tun_file *tfile = tun->tfile;
/* Detach from net device */ /* Detach from net device */
netif_tx_lock_bh(tun->dev);
tfile->tun = NULL; tfile->tun = NULL;
tun->tfile = NULL; tun->tfile = NULL;
netif_tx_unlock_bh(tun->dev);
/* Drop read queue */ /* Drop read queue */
skb_queue_purge(&tun->readq); skb_queue_purge(&tun->readq);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment