Thread (15 messages) 15 messages, 4 authors, 2020-08-18

Re: [Linux-kernel-mentees] [PATCH v1] usbhid: Fix slab-out-of-bounds write in hiddev_ioctl_usage()

From: Dan Carpenter <hidden>
Date: 2020-07-21 07:16:56
Also in: linux-kernel-mentees, linux-usb, lkml

For some reason the reply-to header on your email is bogus:

Reply-To: 20200720121257.GJ2571@kadam

"kadam" is a system on my home network.

On Mon, Jul 20, 2020 at 03:16:56PM -0400, Peilin Ye wrote:
I made some mistakes in the previous e-mail. Please ignore that. There
are a lot of things going on...Sorry for that.

On Mon, Jul 20, 2020 at 03:12:57PM +0300, Dan Carpenter wrote:
quoted
So another option would be to just add HIDIOCGUSAGE and HIDIOCSUSAGE to
the earlier check.  That risks breaking userspace.  Another option is to
just add a check like you did earlier to the HIDIOCGUSAGE case.
Probably just do option #2 and resend.
Sure, I will just add the same check to the HIDIOCGUSAGE case for the
time being. Thank you for the detailed explanation.

Here's what I found after digging a bit further though:

hid_open_report() calls different functions in order to process
different type of items:

drivers/hid/hid-core.c:1193:

        static int (*dispatch_type[])(struct hid_parser *parser,
                                      struct hid_item *item) = {
                hid_parser_main,
                hid_parser_global,
                hid_parser_local,
                hid_parser_reserved
        };

In this case, hid_parser_main() calls hid_add_field(), which in turn
calls hid_register_field(), which allocates the `field` object as you
mentioned:

drivers/hid/hid-core.c:102:

        field = kzalloc((sizeof(struct hid_field) +
                         usages * sizeof(struct hid_usage) +
                         values * sizeof(unsigned)), GFP_KERNEL);
Yeah.  And in the caller it does:

drivers/hid/hid-core.c
   297          if (!parser->local.usage_index) /* Ignore padding fields */
   298                  return 0;
   299  
   300          usages = max_t(unsigned, parser->local.usage_index,
                ^^^^^^^^^^^^^^
   301                                   parser->global.report_count);
   302  
   303          field = hid_register_field(report, usages, parser->global.report_count);
                                                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
So ->usages is always greater or equal to ->global.report_count.

   304          if (!field)
   305                  return 0;
   306  
   307          field->physical = hid_lookup_collection(parser, HID_COLLECTION_PHYSICAL);
Here, `values` equals to `global.report_count`. See how it is being
called:

drivers/hid/hid-core.c:303:

        field = hid_register_field(report, usages, parser->global.report_count);

In hid_open_report(), `global.report_count` can be set by calling
hid_parser_global().

However, the syzkaller reproducer made hid_open_report() to call
hid_parser_main() __before__ `global.report_count` is properly set. It's
zero. So hid_register_field() allocated `field` with `values` equals to
zero - No room for value[] at all. I believe this caused the bug.
I don't know if zero is valid or not.  I suspect it is valid.  We have
no reason to think that it's invalid.

regards,
dan carpenter
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help