diff options
author | Arnd Bergmann <arnd@arndb.de> | 2009-11-07 01:52:32 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2009-11-07 01:52:32 -0500 |
commit | 50857e2a59d8beddc6bb76137df026d67f30d5ca (patch) | |
tree | 7b6d244ad6fa07ff199c7d1436750d535b6b8dcd /drivers/net/tun.c | |
parent | 2dceba14ef0e62738d58777a1bd4018130d47a74 (diff) |
net/tun: handle compat_ioctl directly
The tun driver is the only code in the kernel that operates
on a character device with struct ifreq. Change the driver
to handle the conversion itself so we can contain the
remaining ifreq handling in the socket layer.
This also fixes a bug in the handling of invalid ioctl
numbers on an unbound tun device. The driver treats this
as a TUNSETIFF in native mode, but there is no way for
the generic compat_ioctl() function to emulate this
behaviour. Possibly the driver was only doing this
accidentally anyway, but if any code relies on this
misfeature, it now also works in compat mode.
Signed-off-by: Arnd Bergmann <arnd@arndb.de>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net/tun.c')
-rw-r--r-- | drivers/net/tun.c | 53 |
1 files changed, 46 insertions, 7 deletions
diff --git a/drivers/net/tun.c b/drivers/net/tun.c index 9c59a82784dc..01e99f22210e 100644 --- a/drivers/net/tun.c +++ b/drivers/net/tun.c | |||
@@ -53,6 +53,7 @@ | |||
53 | #include <linux/miscdevice.h> | 53 | #include <linux/miscdevice.h> |
54 | #include <linux/ethtool.h> | 54 | #include <linux/ethtool.h> |
55 | #include <linux/rtnetlink.h> | 55 | #include <linux/rtnetlink.h> |
56 | #include <linux/compat.h> | ||
56 | #include <linux/if.h> | 57 | #include <linux/if.h> |
57 | #include <linux/if_arp.h> | 58 | #include <linux/if_arp.h> |
58 | #include <linux/if_ether.h> | 59 | #include <linux/if_ether.h> |
@@ -1109,8 +1110,8 @@ static int set_offload(struct net_device *dev, unsigned long arg) | |||
1109 | return 0; | 1110 | return 0; |
1110 | } | 1111 | } |
1111 | 1112 | ||
1112 | static long tun_chr_ioctl(struct file *file, unsigned int cmd, | 1113 | static long __tun_chr_ioctl(struct file *file, unsigned int cmd, |
1113 | unsigned long arg) | 1114 | unsigned long arg, int ifreq_len) |
1114 | { | 1115 | { |
1115 | struct tun_file *tfile = file->private_data; | 1116 | struct tun_file *tfile = file->private_data; |
1116 | struct tun_struct *tun; | 1117 | struct tun_struct *tun; |
@@ -1120,7 +1121,7 @@ static long tun_chr_ioctl(struct file *file, unsigned int cmd, | |||
1120 | int ret; | 1121 | int ret; |
1121 | 1122 | ||
1122 | if (cmd == TUNSETIFF || _IOC_TYPE(cmd) == 0x89) | 1123 | if (cmd == TUNSETIFF || _IOC_TYPE(cmd) == 0x89) |
1123 | if (copy_from_user(&ifr, argp, sizeof ifr)) | 1124 | if (copy_from_user(&ifr, argp, ifreq_len)) |
1124 | return -EFAULT; | 1125 | return -EFAULT; |
1125 | 1126 | ||
1126 | if (cmd == TUNGETFEATURES) { | 1127 | if (cmd == TUNGETFEATURES) { |
@@ -1143,7 +1144,7 @@ static long tun_chr_ioctl(struct file *file, unsigned int cmd, | |||
1143 | if (ret) | 1144 | if (ret) |
1144 | goto unlock; | 1145 | goto unlock; |
1145 | 1146 | ||
1146 | if (copy_to_user(argp, &ifr, sizeof(ifr))) | 1147 | if (copy_to_user(argp, &ifr, ifreq_len)) |
1147 | ret = -EFAULT; | 1148 | ret = -EFAULT; |
1148 | goto unlock; | 1149 | goto unlock; |
1149 | } | 1150 | } |
@@ -1161,7 +1162,7 @@ static long tun_chr_ioctl(struct file *file, unsigned int cmd, | |||
1161 | if (ret) | 1162 | if (ret) |
1162 | break; | 1163 | break; |
1163 | 1164 | ||
1164 | if (copy_to_user(argp, &ifr, sizeof(ifr))) | 1165 | if (copy_to_user(argp, &ifr, ifreq_len)) |
1165 | ret = -EFAULT; | 1166 | ret = -EFAULT; |
1166 | break; | 1167 | break; |
1167 | 1168 | ||
@@ -1235,7 +1236,7 @@ static long tun_chr_ioctl(struct file *file, unsigned int cmd, | |||
1235 | /* Get hw addres */ | 1236 | /* Get hw addres */ |
1236 | memcpy(ifr.ifr_hwaddr.sa_data, tun->dev->dev_addr, ETH_ALEN); | 1237 | memcpy(ifr.ifr_hwaddr.sa_data, tun->dev->dev_addr, ETH_ALEN); |
1237 | ifr.ifr_hwaddr.sa_family = tun->dev->type; | 1238 | ifr.ifr_hwaddr.sa_family = tun->dev->type; |
1238 | if (copy_to_user(argp, &ifr, sizeof ifr)) | 1239 | if (copy_to_user(argp, &ifr, ifreq_len)) |
1239 | ret = -EFAULT; | 1240 | ret = -EFAULT; |
1240 | break; | 1241 | break; |
1241 | 1242 | ||
@@ -1274,6 +1275,41 @@ unlock: | |||
1274 | return ret; | 1275 | return ret; |
1275 | } | 1276 | } |
1276 | 1277 | ||
1278 | static long tun_chr_ioctl(struct file *file, | ||
1279 | unsigned int cmd, unsigned long arg) | ||
1280 | { | ||
1281 | return __tun_chr_ioctl(file, cmd, arg, sizeof (struct ifreq)); | ||
1282 | } | ||
1283 | |||
1284 | #ifdef CONFIG_COMPAT | ||
1285 | static long tun_chr_compat_ioctl(struct file *file, | ||
1286 | unsigned int cmd, unsigned long arg) | ||
1287 | { | ||
1288 | switch (cmd) { | ||
1289 | case TUNSETIFF: | ||
1290 | case TUNGETIFF: | ||
1291 | case TUNSETTXFILTER: | ||
1292 | case TUNGETSNDBUF: | ||
1293 | case TUNSETSNDBUF: | ||
1294 | case SIOCGIFHWADDR: | ||
1295 | case SIOCSIFHWADDR: | ||
1296 | arg = (unsigned long)compat_ptr(arg); | ||
1297 | break; | ||
1298 | default: | ||
1299 | arg = (compat_ulong_t)arg; | ||
1300 | break; | ||
1301 | } | ||
1302 | |||
1303 | /* | ||
1304 | * compat_ifreq is shorter than ifreq, so we must not access beyond | ||
1305 | * the end of that structure. All fields that are used in this | ||
1306 | * driver are compatible though, we don't need to convert the | ||
1307 | * contents. | ||
1308 | */ | ||
1309 | return __tun_chr_ioctl(file, cmd, arg, sizeof(struct compat_ifreq)); | ||
1310 | } | ||
1311 | #endif /* CONFIG_COMPAT */ | ||
1312 | |||
1277 | static int tun_chr_fasync(int fd, struct file *file, int on) | 1313 | static int tun_chr_fasync(int fd, struct file *file, int on) |
1278 | { | 1314 | { |
1279 | struct tun_struct *tun = tun_get(file); | 1315 | struct tun_struct *tun = tun_get(file); |
@@ -1356,7 +1392,10 @@ static const struct file_operations tun_fops = { | |||
1356 | .write = do_sync_write, | 1392 | .write = do_sync_write, |
1357 | .aio_write = tun_chr_aio_write, | 1393 | .aio_write = tun_chr_aio_write, |
1358 | .poll = tun_chr_poll, | 1394 | .poll = tun_chr_poll, |
1359 | .unlocked_ioctl = tun_chr_ioctl, | 1395 | .unlocked_ioctl = tun_chr_ioctl, |
1396 | #ifdef CONFIG_COMPAT | ||
1397 | .compat_ioctl = tun_chr_compat_ioctl, | ||
1398 | #endif | ||
1360 | .open = tun_chr_open, | 1399 | .open = tun_chr_open, |
1361 | .release = tun_chr_close, | 1400 | .release = tun_chr_close, |
1362 | .fasync = tun_chr_fasync | 1401 | .fasync = tun_chr_fasync |