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

[PATCH] usb: Patch for USBDEVFS_IOCTL from 32-bit programs

Dell supplied me with the following test:

#include<stdio.h>
#include<errno.h>
#include<sys/ioctl.h>
#include<fcntl.h>
#include<linux/usbdevice_fs.h>

main(int argc,char*argv[])
{
struct usbdevfs_hub_portinfo hubPortInfo = {0};
struct usbdevfs_ioctl command = {0};
command.ifno = 0;
command.ioctl_code = USBDEVFS_HUB_PORTINFO;
command.data = (void*)&hubPortInfo;
int fd, ret;
if(argc != 2) {
fprintf(stderr,"Usage: %s /proc/bus/usb/<BusNo>/<HubID>\n",argv[0]);
fprintf(stderr,"Example: %s /proc/bus/usb/001/001\n",argv[0]);
exit(1);
}
errno = 0;
fd = open(argv[1],O_RDWR);
if(fd < 0) {
perror("open failed:");
exit(errno);
}
errno = 0;
ret = ioctl(fd,USBDEVFS_IOCTL,&command);
printf("IOCTL return status:%d\n",ret);
if(ret<0) {
perror("IOCTL failed:");
close(fd);
exit(3);
} else {
printf("IOCTL passed:Num of ports %d\n",hubPortInfo.nports);
close(fd);
exit(0);
}
return 0;
}

I have verified that it breaks if built in 32 bit mode on x86_64 and that
the patch below fixes it.

Signed-off-by: Pete Zaitcev <zaitcev@redhat.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>

authored by

Pete Zaitcev and committed by
Greg Kroah-Hartman
c36fc889 72adaa96

+50 -14
+42 -14
drivers/usb/core/devio.c
··· 1301 1301 return 0; 1302 1302 } 1303 1303 1304 - static int proc_ioctl (struct dev_state *ps, void __user *arg) 1304 + static int proc_ioctl(struct dev_state *ps, struct usbdevfs_ioctl *ctl) 1305 1305 { 1306 - struct usbdevfs_ioctl ctrl; 1307 1306 int size; 1308 1307 void *buf = NULL; 1309 1308 int retval = 0; 1310 1309 struct usb_interface *intf = NULL; 1311 1310 struct usb_driver *driver = NULL; 1312 1311 1313 - /* get input parameters and alloc buffer */ 1314 - if (copy_from_user(&ctrl, arg, sizeof (ctrl))) 1315 - return -EFAULT; 1316 - if ((size = _IOC_SIZE (ctrl.ioctl_code)) > 0) { 1312 + /* alloc buffer */ 1313 + if ((size = _IOC_SIZE (ctl->ioctl_code)) > 0) { 1317 1314 if ((buf = kmalloc (size, GFP_KERNEL)) == NULL) 1318 1315 return -ENOMEM; 1319 - if ((_IOC_DIR(ctrl.ioctl_code) & _IOC_WRITE)) { 1320 - if (copy_from_user (buf, ctrl.data, size)) { 1316 + if ((_IOC_DIR(ctl->ioctl_code) & _IOC_WRITE)) { 1317 + if (copy_from_user (buf, ctl->data, size)) { 1321 1318 kfree(buf); 1322 1319 return -EFAULT; 1323 1320 } ··· 1330 1333 1331 1334 if (ps->dev->state != USB_STATE_CONFIGURED) 1332 1335 retval = -EHOSTUNREACH; 1333 - else if (!(intf = usb_ifnum_to_if (ps->dev, ctrl.ifno))) 1336 + else if (!(intf = usb_ifnum_to_if (ps->dev, ctl->ifno))) 1334 1337 retval = -EINVAL; 1335 - else switch (ctrl.ioctl_code) { 1338 + else switch (ctl->ioctl_code) { 1336 1339 1337 1340 /* disconnect kernel driver from interface */ 1338 1341 case USBDEVFS_DISCONNECT: ··· 1364 1367 if (driver == NULL || driver->ioctl == NULL) { 1365 1368 retval = -ENOTTY; 1366 1369 } else { 1367 - retval = driver->ioctl (intf, ctrl.ioctl_code, buf); 1370 + retval = driver->ioctl (intf, ctl->ioctl_code, buf); 1368 1371 if (retval == -ENOIOCTLCMD) 1369 1372 retval = -ENOTTY; 1370 1373 } ··· 1373 1376 1374 1377 /* cleanup and return */ 1375 1378 if (retval >= 0 1376 - && (_IOC_DIR (ctrl.ioctl_code) & _IOC_READ) != 0 1379 + && (_IOC_DIR (ctl->ioctl_code) & _IOC_READ) != 0 1377 1380 && size > 0 1378 - && copy_to_user (ctrl.data, buf, size) != 0) 1381 + && copy_to_user (ctl->data, buf, size) != 0) 1379 1382 retval = -EFAULT; 1380 1383 1381 1384 kfree(buf); 1382 1385 return retval; 1383 1386 } 1387 + 1388 + static int proc_ioctl_default(struct dev_state *ps, void __user *arg) 1389 + { 1390 + struct usbdevfs_ioctl ctrl; 1391 + 1392 + if (copy_from_user(&ctrl, arg, sizeof (ctrl))) 1393 + return -EFAULT; 1394 + return proc_ioctl(ps, &ctrl); 1395 + } 1396 + 1397 + #ifdef CONFIG_COMPAT 1398 + static int proc_ioctl_compat(struct dev_state *ps, void __user *arg) 1399 + { 1400 + struct usbdevfs_ioctl32 __user *uioc; 1401 + struct usbdevfs_ioctl ctrl; 1402 + u32 udata; 1403 + 1404 + uioc = compat_ptr(arg); 1405 + if (get_user(ctrl.ifno, &uioc->ifno) || 1406 + get_user(ctrl.ioctl_code, &uioc->ioctl_code) || 1407 + __get_user(udata, &uioc->data)) 1408 + return -EFAULT; 1409 + ctrl.data = compat_ptr(udata); 1410 + 1411 + return proc_ioctl(ps, &ctrl); 1412 + } 1413 + #endif 1384 1414 1385 1415 /* 1386 1416 * NOTE: All requests here that have interface numbers as parameters ··· 1509 1485 ret = proc_reapurbnonblock_compat(ps, p); 1510 1486 break; 1511 1487 1488 + case USBDEVFS_IOCTL32: 1489 + snoop(&dev->dev, "%s: IOCTL\n", __FUNCTION__); 1490 + ret = proc_ioctl_compat(ps, p); 1491 + break; 1512 1492 #endif 1513 1493 1514 1494 case USBDEVFS_DISCARDURB: ··· 1547 1519 1548 1520 case USBDEVFS_IOCTL: 1549 1521 snoop(&dev->dev, "%s: IOCTL\n", __FUNCTION__); 1550 - ret = proc_ioctl(ps, p); 1522 + ret = proc_ioctl_default(ps, p); 1551 1523 break; 1552 1524 } 1553 1525 usb_unlock_device(dev);
+1
fs/compat_ioctl.c
··· 3050 3050 HANDLE_IOCTL(USBDEVFS_CONTROL32, do_usbdevfs_control) 3051 3051 HANDLE_IOCTL(USBDEVFS_BULK32, do_usbdevfs_bulk) 3052 3052 HANDLE_IOCTL(USBDEVFS_DISCSIGNAL32, do_usbdevfs_discsignal) 3053 + COMPATIBLE_IOCTL(USBDEVFS_IOCTL32) 3053 3054 /* i2c */ 3054 3055 HANDLE_IOCTL(I2C_FUNCS, w_long) 3055 3056 HANDLE_IOCTL(I2C_RDWR, do_i2c_rdwr_ioctl)
+7
include/linux/usbdevice_fs.h
··· 140 140 compat_caddr_t usercontext; /* unused */ 141 141 struct usbdevfs_iso_packet_desc iso_frame_desc[0]; 142 142 }; 143 + 144 + struct usbdevfs_ioctl32 { 145 + s32 ifno; 146 + s32 ioctl_code; 147 + compat_caddr_t data; 148 + }; 143 149 #endif 144 150 145 151 #define USBDEVFS_CONTROL _IOWR('U', 0, struct usbdevfs_ctrltransfer) ··· 166 160 #define USBDEVFS_RELEASEINTERFACE _IOR('U', 16, unsigned int) 167 161 #define USBDEVFS_CONNECTINFO _IOW('U', 17, struct usbdevfs_connectinfo) 168 162 #define USBDEVFS_IOCTL _IOWR('U', 18, struct usbdevfs_ioctl) 163 + #define USBDEVFS_IOCTL32 _IOWR('U', 18, struct usbdevfs_ioctl32) 169 164 #define USBDEVFS_HUB_PORTINFO _IOR('U', 19, struct usbdevfs_hub_portinfo) 170 165 #define USBDEVFS_RESET _IO('U', 20) 171 166 #define USBDEVFS_CLEAR_HALT _IOR('U', 21, unsigned int)