Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

fix rawctl compat ioctls breakage on amd64 and itanic

RAW_SETBIND and RAW_GETBIND 32bit versions are fscked in interesting ways.

1) fs/compat_ioctl.c has COMPATIBLE_IOCTL(RAW_SETBIND) followed by
HANDLE_IOCTL(RAW_SETBIND, raw_ioctl). The latter is ignored.

2) on amd64 (and itanic) the damn thing is broken - we have int + u64 + u64
and layouts on i386 and amd64 are _not_ the same. raw_ioctl() would
work there, but it's never called due to (1). As it is, i386 /sbin/raw
definitely doesn't work on amd64 boxen.

3) switching to raw_ioctl() as is would *not* work on e.g. sparc64 and ppc64,
which would be rather sad, seeing that normal userland there is 32bit.
The thing is, slapping __packed on the struct in question does not DTRT -
it eliminates *all* padding. The real solution is to use compat_u64.

4) of course, all that stuff has no business being outside of raw.c in the
first place - there should be ->compat_ioctl() for /dev/rawctl instead of
messing with compat_ioctl.c.

[akpm@linux-foundation.org: coding-style fixes]
[arnd@arndb.de: port to 2.6.36]
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Arnd Bergmann <arnd@arndb.de>

authored by

Al Viro and committed by
Arnd Bergmann
c4a04727 9a181c58

+141 -174
+141 -104
drivers/char/raw.c
··· 19 19 #include <linux/cdev.h> 20 20 #include <linux/device.h> 21 21 #include <linux/mutex.h> 22 - #include <linux/smp_lock.h> 23 22 #include <linux/gfp.h> 23 + #include <linux/compat.h> 24 24 25 25 #include <asm/uaccess.h> 26 26 ··· 55 55 return 0; 56 56 } 57 57 58 - lock_kernel(); 59 58 mutex_lock(&raw_mutex); 60 59 61 60 /* ··· 81 82 bdev->bd_inode->i_mapping; 82 83 filp->private_data = bdev; 83 84 mutex_unlock(&raw_mutex); 84 - unlock_kernel(); 85 85 return 0; 86 86 87 87 out2: ··· 89 91 blkdev_put(bdev, filp->f_mode); 90 92 out: 91 93 mutex_unlock(&raw_mutex); 92 - unlock_kernel(); 93 94 return err; 94 95 } 95 96 ··· 122 125 raw_ioctl(struct file *filp, unsigned int command, unsigned long arg) 123 126 { 124 127 struct block_device *bdev = filp->private_data; 125 - int ret; 126 - 127 - lock_kernel(); 128 - ret = blkdev_ioctl(bdev, 0, command, arg); 129 - unlock_kernel(); 130 - 131 - return ret; 128 + return blkdev_ioctl(bdev, 0, command, arg); 132 129 } 133 130 134 - static void bind_device(struct raw_config_request *rq) 131 + static int bind_set(int number, u64 major, u64 minor) 135 132 { 136 - device_destroy(raw_class, MKDEV(RAW_MAJOR, rq->raw_minor)); 137 - device_create(raw_class, NULL, MKDEV(RAW_MAJOR, rq->raw_minor), NULL, 138 - "raw%d", rq->raw_minor); 133 + dev_t dev = MKDEV(major, minor); 134 + struct raw_device_data *rawdev; 135 + int err = 0; 136 + 137 + if (number <= 0 || number >= MAX_RAW_MINORS) 138 + return -EINVAL; 139 + 140 + if (MAJOR(dev) != major || MINOR(dev) != minor) 141 + return -EINVAL; 142 + 143 + rawdev = &raw_devices[number]; 144 + 145 + /* 146 + * This is like making block devices, so demand the 147 + * same capability 148 + */ 149 + if (!capable(CAP_SYS_ADMIN)) 150 + return -EPERM; 151 + 152 + /* 153 + * For now, we don't need to check that the underlying 154 + * block device is present or not: we can do that when 155 + * the raw device is opened. Just check that the 156 + * major/minor numbers make sense. 157 + */ 158 + 159 + if (MAJOR(dev) == 0 && dev != 0) 160 + return -EINVAL; 161 + 162 + mutex_lock(&raw_mutex); 163 + if (rawdev->inuse) { 164 + mutex_unlock(&raw_mutex); 165 + return -EBUSY; 166 + } 167 + if (rawdev->binding) { 168 + bdput(rawdev->binding); 169 + module_put(THIS_MODULE); 170 + } 171 + if (!dev) { 172 + /* unbind */ 173 + rawdev->binding = NULL; 174 + device_destroy(raw_class, MKDEV(RAW_MAJOR, number)); 175 + } else { 176 + rawdev->binding = bdget(dev); 177 + if (rawdev->binding == NULL) { 178 + err = -ENOMEM; 179 + } else { 180 + dev_t raw = MKDEV(RAW_MAJOR, number); 181 + __module_get(THIS_MODULE); 182 + device_destroy(raw_class, raw); 183 + device_create(raw_class, NULL, raw, NULL, 184 + "raw%d", number); 185 + } 186 + } 187 + mutex_unlock(&raw_mutex); 188 + return err; 189 + } 190 + 191 + static int bind_get(int number, dev_t *dev) 192 + { 193 + struct raw_device_data *rawdev; 194 + struct block_device *bdev; 195 + 196 + if (number <= 0 || number >= MAX_RAW_MINORS) 197 + return -EINVAL; 198 + 199 + rawdev = &raw_devices[number]; 200 + 201 + mutex_lock(&raw_mutex); 202 + bdev = rawdev->binding; 203 + *dev = bdev ? bdev->bd_dev : 0; 204 + mutex_unlock(&raw_mutex); 205 + return 0; 139 206 } 140 207 141 208 /* ··· 210 149 unsigned long arg) 211 150 { 212 151 struct raw_config_request rq; 213 - struct raw_device_data *rawdev; 214 - int err = 0; 152 + dev_t dev; 153 + int err; 215 154 216 - lock_kernel(); 217 155 switch (command) { 218 156 case RAW_SETBIND: 157 + if (copy_from_user(&rq, (void __user *) arg, sizeof(rq))) 158 + return -EFAULT; 159 + 160 + return bind_set(rq.raw_minor, rq.block_major, rq.block_minor); 161 + 219 162 case RAW_GETBIND: 163 + if (copy_from_user(&rq, (void __user *) arg, sizeof(rq))) 164 + return -EFAULT; 220 165 221 - /* First, find out which raw minor we want */ 166 + err = bind_get(rq.raw_minor, &dev); 167 + if (err) 168 + return err; 222 169 223 - if (copy_from_user(&rq, (void __user *) arg, sizeof(rq))) { 224 - err = -EFAULT; 225 - goto out; 226 - } 170 + rq.block_major = MAJOR(dev); 171 + rq.block_minor = MINOR(dev); 227 172 228 - if (rq.raw_minor <= 0 || rq.raw_minor >= MAX_RAW_MINORS) { 229 - err = -EINVAL; 230 - goto out; 231 - } 232 - rawdev = &raw_devices[rq.raw_minor]; 173 + if (copy_to_user((void __user *)arg, &rq, sizeof(rq))) 174 + return -EFAULT; 233 175 234 - if (command == RAW_SETBIND) { 235 - dev_t dev; 236 - 237 - /* 238 - * This is like making block devices, so demand the 239 - * same capability 240 - */ 241 - if (!capable(CAP_SYS_ADMIN)) { 242 - err = -EPERM; 243 - goto out; 244 - } 245 - 246 - /* 247 - * For now, we don't need to check that the underlying 248 - * block device is present or not: we can do that when 249 - * the raw device is opened. Just check that the 250 - * major/minor numbers make sense. 251 - */ 252 - 253 - dev = MKDEV(rq.block_major, rq.block_minor); 254 - if ((rq.block_major == 0 && rq.block_minor != 0) || 255 - MAJOR(dev) != rq.block_major || 256 - MINOR(dev) != rq.block_minor) { 257 - err = -EINVAL; 258 - goto out; 259 - } 260 - 261 - mutex_lock(&raw_mutex); 262 - if (rawdev->inuse) { 263 - mutex_unlock(&raw_mutex); 264 - err = -EBUSY; 265 - goto out; 266 - } 267 - if (rawdev->binding) { 268 - bdput(rawdev->binding); 269 - module_put(THIS_MODULE); 270 - } 271 - if (rq.block_major == 0 && rq.block_minor == 0) { 272 - /* unbind */ 273 - rawdev->binding = NULL; 274 - device_destroy(raw_class, 275 - MKDEV(RAW_MAJOR, rq.raw_minor)); 276 - } else { 277 - rawdev->binding = bdget(dev); 278 - if (rawdev->binding == NULL) 279 - err = -ENOMEM; 280 - else { 281 - __module_get(THIS_MODULE); 282 - bind_device(&rq); 283 - } 284 - } 285 - mutex_unlock(&raw_mutex); 286 - } else { 287 - struct block_device *bdev; 288 - 289 - mutex_lock(&raw_mutex); 290 - bdev = rawdev->binding; 291 - if (bdev) { 292 - rq.block_major = MAJOR(bdev->bd_dev); 293 - rq.block_minor = MINOR(bdev->bd_dev); 294 - } else { 295 - rq.block_major = rq.block_minor = 0; 296 - } 297 - mutex_unlock(&raw_mutex); 298 - if (copy_to_user((void __user *)arg, &rq, sizeof(rq))) { 299 - err = -EFAULT; 300 - goto out; 301 - } 302 - } 303 - break; 304 - default: 305 - err = -EINVAL; 306 - break; 176 + return 0; 307 177 } 308 - out: 309 - unlock_kernel(); 310 - return err; 178 + 179 + return -EINVAL; 311 180 } 181 + 182 + #ifdef CONFIG_COMPAT 183 + struct raw32_config_request { 184 + compat_int_t raw_minor; 185 + compat_u64 block_major; 186 + compat_u64 block_minor; 187 + }; 188 + 189 + static long raw_ctl_compat_ioctl(struct file *file, unsigned int cmd, 190 + unsigned long arg) 191 + { 192 + struct raw32_config_request __user *user_req = compat_ptr(arg); 193 + struct raw32_config_request rq; 194 + dev_t dev; 195 + int err = 0; 196 + 197 + switch (cmd) { 198 + case RAW_SETBIND: 199 + if (copy_from_user(&rq, user_req, sizeof(rq))) 200 + return -EFAULT; 201 + 202 + return bind_set(rq.raw_minor, rq.block_major, rq.block_minor); 203 + 204 + case RAW_GETBIND: 205 + if (copy_from_user(&rq, user_req, sizeof(rq))) 206 + return -EFAULT; 207 + 208 + err = bind_get(rq.raw_minor, &dev); 209 + if (err) 210 + return err; 211 + 212 + rq.block_major = MAJOR(dev); 213 + rq.block_minor = MINOR(dev); 214 + 215 + if (copy_to_user(user_req, &rq, sizeof(rq))) 216 + return -EFAULT; 217 + 218 + return 0; 219 + } 220 + 221 + return -EINVAL; 222 + } 223 + #endif 312 224 313 225 static const struct file_operations raw_fops = { 314 226 .read = do_sync_read, ··· 297 263 298 264 static const struct file_operations raw_ctl_fops = { 299 265 .unlocked_ioctl = raw_ctl_ioctl, 266 + #ifdef CONFIG_COMPAT 267 + .compat_ioctl = raw_ctl_compat_ioctl, 268 + #endif 300 269 .open = raw_open, 301 270 .owner = THIS_MODULE, 302 271 };
-70
fs/compat_ioctl.c
··· 599 599 #define HIDPGETCONNLIST _IOR('H', 210, int) 600 600 #define HIDPGETCONNINFO _IOR('H', 211, int) 601 601 602 - #ifdef CONFIG_BLOCK 603 - struct raw32_config_request 604 - { 605 - compat_int_t raw_minor; 606 - __u64 block_major; 607 - __u64 block_minor; 608 - } __attribute__((packed)); 609 - 610 - static int get_raw32_request(struct raw_config_request *req, struct raw32_config_request __user *user_req) 611 - { 612 - int ret; 613 - 614 - if (!access_ok(VERIFY_READ, user_req, sizeof(struct raw32_config_request))) 615 - return -EFAULT; 616 - 617 - ret = __get_user(req->raw_minor, &user_req->raw_minor); 618 - ret |= __get_user(req->block_major, &user_req->block_major); 619 - ret |= __get_user(req->block_minor, &user_req->block_minor); 620 - 621 - return ret ? -EFAULT : 0; 622 - } 623 - 624 - static int set_raw32_request(struct raw_config_request *req, struct raw32_config_request __user *user_req) 625 - { 626 - int ret; 627 - 628 - if (!access_ok(VERIFY_WRITE, user_req, sizeof(struct raw32_config_request))) 629 - return -EFAULT; 630 - 631 - ret = __put_user(req->raw_minor, &user_req->raw_minor); 632 - ret |= __put_user(req->block_major, &user_req->block_major); 633 - ret |= __put_user(req->block_minor, &user_req->block_minor); 634 - 635 - return ret ? -EFAULT : 0; 636 - } 637 - 638 - static int raw_ioctl(unsigned fd, unsigned cmd, 639 - struct raw32_config_request __user *user_req) 640 - { 641 - int ret; 642 - 643 - switch (cmd) { 644 - case RAW_SETBIND: 645 - default: { /* RAW_GETBIND */ 646 - struct raw_config_request req; 647 - mm_segment_t oldfs = get_fs(); 648 - 649 - if ((ret = get_raw32_request(&req, user_req))) 650 - return ret; 651 - 652 - set_fs(KERNEL_DS); 653 - ret = sys_ioctl(fd,cmd,(unsigned long)&req); 654 - set_fs(oldfs); 655 - 656 - if ((!ret) && (cmd == RAW_GETBIND)) { 657 - ret = set_raw32_request(&req, user_req); 658 - } 659 - break; 660 - } 661 - } 662 - return ret; 663 - } 664 - #endif /* CONFIG_BLOCK */ 665 602 666 603 struct serial_struct32 { 667 604 compat_int_t type; ··· 1199 1262 COMPATIBLE_IOCTL(SOUND_MIXER_GETLEVELS) 1200 1263 COMPATIBLE_IOCTL(SOUND_MIXER_SETLEVELS) 1201 1264 COMPATIBLE_IOCTL(OSS_GETVERSION) 1202 - /* Raw devices */ 1203 - COMPATIBLE_IOCTL(RAW_SETBIND) 1204 - COMPATIBLE_IOCTL(RAW_GETBIND) 1205 1265 /* SMB ioctls which do not need any translations */ 1206 1266 COMPATIBLE_IOCTL(SMB_IOC_NEWCONN) 1207 1267 /* Watchdog */ ··· 1457 1523 case MTIOCGET32: 1458 1524 case MTIOCPOS32: 1459 1525 return mt_ioctl_trans(fd, cmd, argp); 1460 - /* Raw devices */ 1461 - case RAW_SETBIND: 1462 - case RAW_GETBIND: 1463 - return raw_ioctl(fd, cmd, argp); 1464 1526 #endif 1465 1527 /* One SMB ioctl needs translations. */ 1466 1528 #define SMB_IOC_GETMOUNTUID_32 _IOR('u', 1, compat_uid_t)