diff options
author | Alan Stern <stern@rowland.harvard.edu> | 2009-06-29 11:04:54 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2009-07-12 18:16:41 -0400 |
commit | 9180135bc80ab11199d482b6111e23f74d65af4a (patch) | |
tree | 570d7079252a6a7b821e42383913ec0bfa09e4eb /drivers/usb | |
parent | ec6d67e39f5638c792eb7490bf32586ccb9d8005 (diff) |
USB: handle zero-length usbfs submissions correctly
This patch (as1262) fixes a bug in usbfs: It refuses to accept
zero-length transfers, and it insists that the buffer pointer be valid
even if there is no data being transferred.
The patch also consolidates a bunch of repetitive access_ok() checks
into a single check, which incidentally fixes the lack of such a check
for Isochronous URBs.
Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
Cc: stable <stable@kernel.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/usb')
-rw-r--r-- | drivers/usb/core/devio.c | 41 |
1 files changed, 20 insertions, 21 deletions
diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c index 46ca2af5ef1c..38b8bce782d6 100644 --- a/drivers/usb/core/devio.c +++ b/drivers/usb/core/devio.c | |||
@@ -995,7 +995,7 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb, | |||
995 | USBDEVFS_URB_ZERO_PACKET | | 995 | USBDEVFS_URB_ZERO_PACKET | |
996 | USBDEVFS_URB_NO_INTERRUPT)) | 996 | USBDEVFS_URB_NO_INTERRUPT)) |
997 | return -EINVAL; | 997 | return -EINVAL; |
998 | if (!uurb->buffer) | 998 | if (uurb->buffer_length > 0 && !uurb->buffer) |
999 | return -EINVAL; | 999 | return -EINVAL; |
1000 | if (!(uurb->type == USBDEVFS_URB_TYPE_CONTROL && | 1000 | if (!(uurb->type == USBDEVFS_URB_TYPE_CONTROL && |
1001 | (uurb->endpoint & ~USB_ENDPOINT_DIR_MASK) == 0)) { | 1001 | (uurb->endpoint & ~USB_ENDPOINT_DIR_MASK) == 0)) { |
@@ -1051,11 +1051,6 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb, | |||
1051 | is_in = 0; | 1051 | is_in = 0; |
1052 | uurb->endpoint &= ~USB_DIR_IN; | 1052 | uurb->endpoint &= ~USB_DIR_IN; |
1053 | } | 1053 | } |
1054 | if (!access_ok(is_in ? VERIFY_WRITE : VERIFY_READ, | ||
1055 | uurb->buffer, uurb->buffer_length)) { | ||
1056 | kfree(dr); | ||
1057 | return -EFAULT; | ||
1058 | } | ||
1059 | snoop(&ps->dev->dev, "control urb: bRequest=%02x " | 1054 | snoop(&ps->dev->dev, "control urb: bRequest=%02x " |
1060 | "bRrequestType=%02x wValue=%04x " | 1055 | "bRrequestType=%02x wValue=%04x " |
1061 | "wIndex=%04x wLength=%04x\n", | 1056 | "wIndex=%04x wLength=%04x\n", |
@@ -1075,9 +1070,6 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb, | |||
1075 | uurb->number_of_packets = 0; | 1070 | uurb->number_of_packets = 0; |
1076 | if (uurb->buffer_length > MAX_USBFS_BUFFER_SIZE) | 1071 | if (uurb->buffer_length > MAX_USBFS_BUFFER_SIZE) |
1077 | return -EINVAL; | 1072 | return -EINVAL; |
1078 | if (!access_ok(is_in ? VERIFY_WRITE : VERIFY_READ, | ||
1079 | uurb->buffer, uurb->buffer_length)) | ||
1080 | return -EFAULT; | ||
1081 | snoop(&ps->dev->dev, "bulk urb\n"); | 1073 | snoop(&ps->dev->dev, "bulk urb\n"); |
1082 | break; | 1074 | break; |
1083 | 1075 | ||
@@ -1119,28 +1111,35 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb, | |||
1119 | return -EINVAL; | 1111 | return -EINVAL; |
1120 | if (uurb->buffer_length > MAX_USBFS_BUFFER_SIZE) | 1112 | if (uurb->buffer_length > MAX_USBFS_BUFFER_SIZE) |
1121 | return -EINVAL; | 1113 | return -EINVAL; |
1122 | if (!access_ok(is_in ? VERIFY_WRITE : VERIFY_READ, | ||
1123 | uurb->buffer, uurb->buffer_length)) | ||
1124 | return -EFAULT; | ||
1125 | snoop(&ps->dev->dev, "interrupt urb\n"); | 1114 | snoop(&ps->dev->dev, "interrupt urb\n"); |
1126 | break; | 1115 | break; |
1127 | 1116 | ||
1128 | default: | 1117 | default: |
1129 | return -EINVAL; | 1118 | return -EINVAL; |
1130 | } | 1119 | } |
1131 | as = alloc_async(uurb->number_of_packets); | 1120 | if (uurb->buffer_length > 0 && |
1132 | if (!as) { | 1121 | !access_ok(is_in ? VERIFY_WRITE : VERIFY_READ, |
1122 | uurb->buffer, uurb->buffer_length)) { | ||
1133 | kfree(isopkt); | 1123 | kfree(isopkt); |
1134 | kfree(dr); | 1124 | kfree(dr); |
1135 | return -ENOMEM; | 1125 | return -EFAULT; |
1136 | } | 1126 | } |
1137 | as->urb->transfer_buffer = kmalloc(uurb->buffer_length, GFP_KERNEL); | 1127 | as = alloc_async(uurb->number_of_packets); |
1138 | if (!as->urb->transfer_buffer) { | 1128 | if (!as) { |
1139 | kfree(isopkt); | 1129 | kfree(isopkt); |
1140 | kfree(dr); | 1130 | kfree(dr); |
1141 | free_async(as); | ||
1142 | return -ENOMEM; | 1131 | return -ENOMEM; |
1143 | } | 1132 | } |
1133 | if (uurb->buffer_length > 0) { | ||
1134 | as->urb->transfer_buffer = kmalloc(uurb->buffer_length, | ||
1135 | GFP_KERNEL); | ||
1136 | if (!as->urb->transfer_buffer) { | ||
1137 | kfree(isopkt); | ||
1138 | kfree(dr); | ||
1139 | free_async(as); | ||
1140 | return -ENOMEM; | ||
1141 | } | ||
1142 | } | ||
1144 | as->urb->dev = ps->dev; | 1143 | as->urb->dev = ps->dev; |
1145 | as->urb->pipe = (uurb->type << 30) | | 1144 | as->urb->pipe = (uurb->type << 30) | |
1146 | __create_pipe(ps->dev, uurb->endpoint & 0xf) | | 1145 | __create_pipe(ps->dev, uurb->endpoint & 0xf) | |
@@ -1182,7 +1181,7 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb, | |||
1182 | kfree(isopkt); | 1181 | kfree(isopkt); |
1183 | as->ps = ps; | 1182 | as->ps = ps; |
1184 | as->userurb = arg; | 1183 | as->userurb = arg; |
1185 | if (uurb->endpoint & USB_DIR_IN) | 1184 | if (is_in && uurb->buffer_length > 0) |
1186 | as->userbuffer = uurb->buffer; | 1185 | as->userbuffer = uurb->buffer; |
1187 | else | 1186 | else |
1188 | as->userbuffer = NULL; | 1187 | as->userbuffer = NULL; |
@@ -1192,9 +1191,9 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb, | |||
1192 | as->uid = cred->uid; | 1191 | as->uid = cred->uid; |
1193 | as->euid = cred->euid; | 1192 | as->euid = cred->euid; |
1194 | security_task_getsecid(current, &as->secid); | 1193 | security_task_getsecid(current, &as->secid); |
1195 | if (!is_in) { | 1194 | if (!is_in && uurb->buffer_length > 0) { |
1196 | if (copy_from_user(as->urb->transfer_buffer, uurb->buffer, | 1195 | if (copy_from_user(as->urb->transfer_buffer, uurb->buffer, |
1197 | as->urb->transfer_buffer_length)) { | 1196 | uurb->buffer_length)) { |
1198 | free_async(as); | 1197 | free_async(as); |
1199 | return -EFAULT; | 1198 | return -EFAULT; |
1200 | } | 1199 | } |