Re: [PATCH 11/14] HID: multitouch: validate feature report details

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Thu, Aug 29, 2013 at 1:59 AM, Benjamin Tissoires
<benjamin.tissoires@xxxxxxxxxx> wrote:
> Hi Kees,
>
> I would be curious to have the HID report descriptors (maybe off list)
> to understand how things can be that bad.

Certainly! I'll send them your way. I did have to get pretty creative
to tickle these conditions.

> On overall, I'd prefer all those checks to be in hid-core so that we
> have the guarantee that we don't have to open a new CVE each time a
> specific hid driver do not check for these ranges.

I pondered doing this, but it seemed like something that needed wider
discussion, so I thought I'd start with just the dump of fixes. It
seems like the entire HID report interface should use access functions
to enforce range checking -- perhaps further enforced by making the
structure opaque to the drivers.

> More specific comments inlined:
>
> On 28/08/13 22:31, Jiri Kosina wrote:
>> From: Kees Cook <keescook@xxxxxxxxxxxx>
>>
>> When working on report indexes, always validate that they are in bounds.
>> Without this, a HID device could report a malicious feature report that
>> could trick the driver into a heap overflow:
>>
>> [  634.885003] usb 1-1: New USB device found, idVendor=0596, idProduct=0500
>> ...
>> [  676.469629] BUG kmalloc-192 (Tainted: G        W   ): Redzone overwritten
>>
>> CVE-2013-2897
>>
>> Signed-off-by: Kees Cook <keescook@xxxxxxxxxxxx>
>> Cc: stable@xxxxxxxxxx
>> ---
>>  drivers/hid/hid-multitouch.c |   25 ++++++++++++++++++++-----
>>  1 file changed, 20 insertions(+), 5 deletions(-)
>>
>> diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c
>> index cb0e361..2aa275e 100644
>> --- a/drivers/hid/hid-multitouch.c
>> +++ b/drivers/hid/hid-multitouch.c
>> @@ -330,9 +330,18 @@ static void mt_feature_mapping(struct hid_device *hdev,
>>                               break;
>>                       }
>>               }
>> +             /* Ignore if value index is out of bounds. */
>> +             if (td->inputmode_index < 0 ||
>
> td->inputmode_index can not be less than 0

Well, it certainly _shouldn't_ be less than zero. :) However, it is
defined as s8, and gets set from an int:

                for (i=0; i < field->maxusage; i++) {
                        if (field->usage[i].hid == usage->hid) {
                                td->inputmode_index = i;
                                break;
                        }
                }

Both "i" and "maxusage" are int, and I can generate a large maxusage
and usage array where the first matching hid equality happens when i
is >127, causing inputmode_index to wrap.

>> +                 td->inputmode_index >= field->report_count) {
>
> if this is really required, we could just change the for loop above to
> go from 0 to field->report_count instead.

That's certainly true, but since usage count and report count are not
directly associated, I don't know if there are devices that will freak
out with this restriction.

> However, I think we could just rely on usage->usage_index to get the
> actual index.
> I'll do more tests today and tell you later.
>
>> +                     dev_err(&hdev->dev, "HID_DG_INPUTMODE out of range\n");
>> +                     td->inputmode = -1;
>> +             }
>>
>>               break;
>>       case HID_DG_CONTACTMAX:
>> +             /* Ignore if value count is out of bounds. */
>> +             if (field->report_count < 1)
>> +                     break;
>
> If you can trigger this, I would say that the fix should be in hid-core,
> not in every driver. A null report_count should not be allowed by the
> HID protocol.

Again, I have no problem with that idea, but there seem to be lots of
general cases where this may not be possible (i.e. a HID with 0 OUTPUT
or FEATURE reports). I didn't see a sensible way to approach this in
core without declaring a "I require $foo many OUTPUT reports, $bar
many INPUT, and $baz many FEATURE" for each driver. I instead opted
for the report validation function instead.

>>               td->maxcontact_report_id = field->report->id;
>>               td->maxcontacts = field->value[0];
>>               if (!td->maxcontacts &&
>> @@ -743,15 +752,21 @@ static void mt_touch_report(struct hid_device *hid, struct hid_report *report)
>>       unsigned count;
>>       int r, n;
>>
>> +     if (report->maxfield == 0)
>> +             return;
>> +
>>       /*
>>        * Includes multi-packet support where subsequent
>>        * packets are sent with zero contactcount.
>>        */
>> -     if (td->cc_index >= 0) {
>> -             struct hid_field *field = report->field[td->cc_index];
>> -             int value = field->value[td->cc_value_index];
>> -             if (value)
>> -                     td->num_expected = value;
>> +     if (td->cc_index >= 0 && td->cc_index < report->maxfield) {
>> +             field = report->field[td->cc_index];
>
> looks like we previously overwrote the definition of field :(
>
>> +             if (td->cc_value_index >= 0 &&
>> +                 td->cc_value_index < field->report_count) {
>> +                     int value = field->value[td->cc_value_index];
>> +                     if (value)
>> +                             td->num_expected = value;
>> +             }
>
> I can not see why td->cc_index and td->cc_value_index could have bad
> values. They are initially created by hid-core, and are not provided by
> the device.
> Anyway, if you are able to produce bad values with them, I'd rather have
> those checks during the assignment of td->cc_index (in
> mt_touch_input_mapping()), instead of checking them for each report.

Well, the problem comes again from there not being a hard link between
the usage array and the value array:

                        td->cc_value_index = usage->usage_index;
...
                        int value = field->value[td->cc_value_index];

And all of this would be irrelevant if there was an access function
for field->value[].

Thanks for the review!

-Kees

>
> Cheers,
> Benjamin
>
>>       }
>>
>>       for (r = 0; r < report->maxfield; r++) {
>>
>



-- 
Kees Cook
Chrome OS Security
--
To unsubscribe from this list: send the line "unsubscribe linux-input" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html




[Index of Archives]     [Linux Media Devel]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Linux Wireless Networking]     [Linux Omap]

  Powered by Linux