HID: usbhid: Eliminate recurrent out-of-bounds bug in usbhid_parse()
commit fe7f7ac8e0c708446ff017453add769ffc15deed upstream.
Update struct hid_descriptor to better reflect the mandatory and
optional parts of the HID Descriptor as per USB HID 1.11 specification.
Note: the kernel currently does not parse any optional HID class
descriptors, only the mandatory report descriptor.
Update all references to member element desc[0] to rpt_desc.
Add test to verify bLength and bNumDescriptors values are valid.
Replace the for loop with direct access to the mandatory HID class
descriptor member for the report descriptor. This eliminates the
possibility of getting an out-of-bounds fault.
Add a warning message if the HID descriptor contains any unsupported
optional HID class descriptors.
Reported-by: syzbot+c52569baf0c843f35495@syzkaller.appspotmail.com
Closes: https://syzkaller.appspot.com/bug?extid=c52569baf0c843f35495
Fixes: f043bfc98c
("HID: usbhid: fix out-of-bounds bug")
Cc: stable@vger.kernel.org
Signed-off-by: Terry Junge <linuxhid@cosmicgizmosystems.com>
Reviewed-by: Michael Kelley <mhklinux@outlook.com>
Signed-off-by: Jiri Kosina <jkosina@suse.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
committed by
Greg Kroah-Hartman
parent
dd4b8e05f2
commit
4fa7831cf0
@@ -192,7 +192,7 @@ static void mousevsc_on_receive_device_info(struct mousevsc_dev *input_device,
|
|||||||
goto cleanup;
|
goto cleanup;
|
||||||
|
|
||||||
input_device->report_desc_size = le16_to_cpu(
|
input_device->report_desc_size = le16_to_cpu(
|
||||||
desc->desc[0].wDescriptorLength);
|
desc->rpt_desc.wDescriptorLength);
|
||||||
if (input_device->report_desc_size == 0) {
|
if (input_device->report_desc_size == 0) {
|
||||||
input_device->dev_info_status = -EINVAL;
|
input_device->dev_info_status = -EINVAL;
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
@@ -210,7 +210,7 @@ static void mousevsc_on_receive_device_info(struct mousevsc_dev *input_device,
|
|||||||
|
|
||||||
memcpy(input_device->report_desc,
|
memcpy(input_device->report_desc,
|
||||||
((unsigned char *)desc) + desc->bLength,
|
((unsigned char *)desc) + desc->bLength,
|
||||||
le16_to_cpu(desc->desc[0].wDescriptorLength));
|
le16_to_cpu(desc->rpt_desc.wDescriptorLength));
|
||||||
|
|
||||||
/* Send the ack */
|
/* Send the ack */
|
||||||
memset(&ack, 0, sizeof(struct mousevsc_prt_msg));
|
memset(&ack, 0, sizeof(struct mousevsc_prt_msg));
|
||||||
|
@@ -982,12 +982,11 @@ static int usbhid_parse(struct hid_device *hid)
|
|||||||
struct usb_host_interface *interface = intf->cur_altsetting;
|
struct usb_host_interface *interface = intf->cur_altsetting;
|
||||||
struct usb_device *dev = interface_to_usbdev (intf);
|
struct usb_device *dev = interface_to_usbdev (intf);
|
||||||
struct hid_descriptor *hdesc;
|
struct hid_descriptor *hdesc;
|
||||||
|
struct hid_class_descriptor *hcdesc;
|
||||||
u32 quirks = 0;
|
u32 quirks = 0;
|
||||||
unsigned int rsize = 0;
|
unsigned int rsize = 0;
|
||||||
char *rdesc;
|
char *rdesc;
|
||||||
int ret, n;
|
int ret;
|
||||||
int num_descriptors;
|
|
||||||
size_t offset = offsetof(struct hid_descriptor, desc);
|
|
||||||
|
|
||||||
quirks = hid_lookup_quirk(hid);
|
quirks = hid_lookup_quirk(hid);
|
||||||
|
|
||||||
@@ -1009,20 +1008,19 @@ static int usbhid_parse(struct hid_device *hid)
|
|||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hdesc->bLength < sizeof(struct hid_descriptor)) {
|
if (!hdesc->bNumDescriptors ||
|
||||||
dbg_hid("hid descriptor is too short\n");
|
hdesc->bLength != sizeof(*hdesc) +
|
||||||
|
(hdesc->bNumDescriptors - 1) * sizeof(*hcdesc)) {
|
||||||
|
dbg_hid("hid descriptor invalid, bLen=%hhu bNum=%hhu\n",
|
||||||
|
hdesc->bLength, hdesc->bNumDescriptors);
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
hid->version = le16_to_cpu(hdesc->bcdHID);
|
hid->version = le16_to_cpu(hdesc->bcdHID);
|
||||||
hid->country = hdesc->bCountryCode;
|
hid->country = hdesc->bCountryCode;
|
||||||
|
|
||||||
num_descriptors = min_t(int, hdesc->bNumDescriptors,
|
if (hdesc->rpt_desc.bDescriptorType == HID_DT_REPORT)
|
||||||
(hdesc->bLength - offset) / sizeof(struct hid_class_descriptor));
|
rsize = le16_to_cpu(hdesc->rpt_desc.wDescriptorLength);
|
||||||
|
|
||||||
for (n = 0; n < num_descriptors; n++)
|
|
||||||
if (hdesc->desc[n].bDescriptorType == HID_DT_REPORT)
|
|
||||||
rsize = le16_to_cpu(hdesc->desc[n].wDescriptorLength);
|
|
||||||
|
|
||||||
if (!rsize || rsize > HID_MAX_DESCRIPTOR_SIZE) {
|
if (!rsize || rsize > HID_MAX_DESCRIPTOR_SIZE) {
|
||||||
dbg_hid("weird size of report descriptor (%u)\n", rsize);
|
dbg_hid("weird size of report descriptor (%u)\n", rsize);
|
||||||
@@ -1050,6 +1048,11 @@ static int usbhid_parse(struct hid_device *hid)
|
|||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (hdesc->bNumDescriptors > 1)
|
||||||
|
hid_warn(intf,
|
||||||
|
"%u unsupported optional hid class descriptors\n",
|
||||||
|
(int)(hdesc->bNumDescriptors - 1));
|
||||||
|
|
||||||
hid->quirks |= quirks;
|
hid->quirks |= quirks;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@@ -118,8 +118,8 @@ static struct hid_descriptor hidg_desc = {
|
|||||||
.bcdHID = cpu_to_le16(0x0101),
|
.bcdHID = cpu_to_le16(0x0101),
|
||||||
.bCountryCode = 0x00,
|
.bCountryCode = 0x00,
|
||||||
.bNumDescriptors = 0x1,
|
.bNumDescriptors = 0x1,
|
||||||
/*.desc[0].bDescriptorType = DYNAMIC */
|
/*.rpt_desc.bDescriptorType = DYNAMIC */
|
||||||
/*.desc[0].wDescriptorLenght = DYNAMIC */
|
/*.rpt_desc.wDescriptorLength = DYNAMIC */
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Super-Speed Support */
|
/* Super-Speed Support */
|
||||||
@@ -728,8 +728,8 @@ static int hidg_setup(struct usb_function *f,
|
|||||||
struct hid_descriptor hidg_desc_copy = hidg_desc;
|
struct hid_descriptor hidg_desc_copy = hidg_desc;
|
||||||
|
|
||||||
VDBG(cdev, "USB_REQ_GET_DESCRIPTOR: HID\n");
|
VDBG(cdev, "USB_REQ_GET_DESCRIPTOR: HID\n");
|
||||||
hidg_desc_copy.desc[0].bDescriptorType = HID_DT_REPORT;
|
hidg_desc_copy.rpt_desc.bDescriptorType = HID_DT_REPORT;
|
||||||
hidg_desc_copy.desc[0].wDescriptorLength =
|
hidg_desc_copy.rpt_desc.wDescriptorLength =
|
||||||
cpu_to_le16(hidg->report_desc_length);
|
cpu_to_le16(hidg->report_desc_length);
|
||||||
|
|
||||||
length = min_t(unsigned short, length,
|
length = min_t(unsigned short, length,
|
||||||
@@ -970,8 +970,8 @@ static int hidg_bind(struct usb_configuration *c, struct usb_function *f)
|
|||||||
* We can use hidg_desc struct here but we should not relay
|
* We can use hidg_desc struct here but we should not relay
|
||||||
* that its content won't change after returning from this function.
|
* that its content won't change after returning from this function.
|
||||||
*/
|
*/
|
||||||
hidg_desc.desc[0].bDescriptorType = HID_DT_REPORT;
|
hidg_desc.rpt_desc.bDescriptorType = HID_DT_REPORT;
|
||||||
hidg_desc.desc[0].wDescriptorLength =
|
hidg_desc.rpt_desc.wDescriptorLength =
|
||||||
cpu_to_le16(hidg->report_desc_length);
|
cpu_to_le16(hidg->report_desc_length);
|
||||||
|
|
||||||
hidg_hs_in_ep_desc.bEndpointAddress =
|
hidg_hs_in_ep_desc.bEndpointAddress =
|
||||||
|
@@ -736,8 +736,9 @@ struct hid_descriptor {
|
|||||||
__le16 bcdHID;
|
__le16 bcdHID;
|
||||||
__u8 bCountryCode;
|
__u8 bCountryCode;
|
||||||
__u8 bNumDescriptors;
|
__u8 bNumDescriptors;
|
||||||
|
struct hid_class_descriptor rpt_desc;
|
||||||
|
|
||||||
struct hid_class_descriptor desc[1];
|
struct hid_class_descriptor opt_descs[];
|
||||||
} __attribute__ ((packed));
|
} __attribute__ ((packed));
|
||||||
|
|
||||||
#define HID_DEVICE(b, g, ven, prod) \
|
#define HID_DEVICE(b, g, ven, prod) \
|
||||||
|
Reference in New Issue
Block a user