diff options
author | Eric W. Biederman <ebiederm@xmission.com> | 2009-01-20 06:02:28 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2009-01-21 19:00:45 -0500 |
commit | 38231b7a8d1b74c920822640d1ce8eb8046377fb (patch) | |
tree | fc7f1c3cc8ef34a64a740a2938934dbd598f0d93 /drivers/net | |
parent | 36b50bab53207daf34be63ca62fb8b0b08dc6e6b (diff) |
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: Eric W. Biederman <ebiederm@aristanetworks.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net')
-rw-r--r-- | drivers/net/tun.c | 24 |
1 files changed, 17 insertions, 7 deletions
diff --git a/drivers/net/tun.c b/drivers/net/tun.c index dfbf58659771..fa93160bf522 100644 --- a/drivers/net/tun.c +++ b/drivers/net/tun.c | |||
@@ -115,25 +115,33 @@ static int tun_attach(struct tun_struct *tun, struct file *file) | |||
115 | { | 115 | { |
116 | struct tun_file *tfile = file->private_data; | 116 | struct tun_file *tfile = file->private_data; |
117 | const struct cred *cred = current_cred(); | 117 | const struct cred *cred = current_cred(); |
118 | int err; | ||
118 | 119 | ||
119 | ASSERT_RTNL(); | 120 | ASSERT_RTNL(); |
120 | 121 | ||
121 | if (tfile->tun) | ||
122 | return -EINVAL; | ||
123 | |||
124 | if (tun->tfile) | ||
125 | return -EBUSY; | ||
126 | |||
127 | /* Check permissions */ | 122 | /* Check permissions */ |
128 | if (((tun->owner != -1 && cred->euid != tun->owner) || | 123 | if (((tun->owner != -1 && cred->euid != tun->owner) || |
129 | (tun->group != -1 && cred->egid != tun->group)) && | 124 | (tun->group != -1 && cred->egid != tun->group)) && |
130 | !capable(CAP_NET_ADMIN)) | 125 | !capable(CAP_NET_ADMIN)) |
131 | return -EPERM; | 126 | return -EPERM; |
132 | 127 | ||
128 | netif_tx_lock_bh(tun->dev); | ||
129 | |||
130 | err = -EINVAL; | ||
131 | if (tfile->tun) | ||
132 | goto out; | ||
133 | |||
134 | err = -EBUSY; | ||
135 | if (tun->tfile) | ||
136 | goto out; | ||
137 | |||
138 | err = 0; | ||
133 | tfile->tun = tun; | 139 | tfile->tun = tun; |
134 | tun->tfile = tfile; | 140 | tun->tfile = tfile; |
135 | 141 | ||
136 | return 0; | 142 | out: |
143 | netif_tx_unlock_bh(tun->dev); | ||
144 | return err; | ||
137 | } | 145 | } |
138 | 146 | ||
139 | static void __tun_detach(struct tun_struct *tun) | 147 | static void __tun_detach(struct tun_struct *tun) |
@@ -141,8 +149,10 @@ static void __tun_detach(struct tun_struct *tun) | |||
141 | struct tun_file *tfile = tun->tfile; | 149 | struct tun_file *tfile = tun->tfile; |
142 | 150 | ||
143 | /* Detach from net device */ | 151 | /* Detach from net device */ |
152 | netif_tx_lock_bh(tun->dev); | ||
144 | tfile->tun = NULL; | 153 | tfile->tun = NULL; |
145 | tun->tfile = NULL; | 154 | tun->tfile = NULL; |
155 | netif_tx_unlock_bh(tun->dev); | ||
146 | 156 | ||
147 | /* Drop read queue */ | 157 | /* Drop read queue */ |
148 | skb_queue_purge(&tun->readq); | 158 | skb_queue_purge(&tun->readq); |