diff mbox

[01/14] HID: validate HID report id size

Message ID alpine.LNX.2.00.1308282158220.22181@pobox.suse.cz (mailing list archive)
State New, archived
Delegated to: Jiri Kosina
Headers show

Commit Message

Jiri Kosina Aug. 28, 2013, 8:29 p.m. UTC
From: Kees Cook <keescook@chromium.org>

The "Report ID" field of a HID report is used to build indexes of
reports. The kernel's index of these is limited to 256 entries, so any
malicious device that sets a Report ID greater than 255 will trigger
memory corruption on the host:

[ 1347.156239] BUG: unable to handle kernel paging request at ffff88094958a878
[ 1347.156261] IP: [<ffffffff813e4da0>] hid_register_report+0x2a/0x8b

CVE-2013-2888

Signed-off-by: Kees Cook <keescook@chromium.org>
Cc: stable@kernel.org
---
 drivers/hid/hid-core.c |   10 +++++++---
 include/linux/hid.h    |    4 +++-
 2 files changed, 10 insertions(+), 4 deletions(-)

Comments

Jiri Kosina Aug. 29, 2013, 9:03 a.m. UTC | #1
On Wed, 28 Aug 2013, Jiri Kosina wrote:

> From: Kees Cook <keescook@chromium.org>
> 
> The "Report ID" field of a HID report is used to build indexes of
> reports. The kernel's index of these is limited to 256 entries, so any
> malicious device that sets a Report ID greater than 255 will trigger
> memory corruption on the host:
> 
> [ 1347.156239] BUG: unable to handle kernel paging request at ffff88094958a878
> [ 1347.156261] IP: [<ffffffff813e4da0>] hid_register_report+0x2a/0x8b
> 
> CVE-2013-2888
> 
> Signed-off-by: Kees Cook <keescook@chromium.org>
> Cc: stable@kernel.org

Applied this one to hid.git#for-3.11/CVE-2013-2888

Thanks,
Benjamin Tissoires Aug. 29, 2013, 9:21 a.m. UTC | #2
On Thu, Aug 29, 2013 at 11:03 AM, Jiri Kosina <jkosina@suse.cz> wrote:
> On Wed, 28 Aug 2013, Jiri Kosina wrote:
>
>> From: Kees Cook <keescook@chromium.org>
>>
>> The "Report ID" field of a HID report is used to build indexes of
>> reports. The kernel's index of these is limited to 256 entries, so any
>> malicious device that sets a Report ID greater than 255 will trigger
>> memory corruption on the host:
>>
>> [ 1347.156239] BUG: unable to handle kernel paging request at ffff88094958a878
>> [ 1347.156261] IP: [<ffffffff813e4da0>] hid_register_report+0x2a/0x8b
>>
>> CVE-2013-2888
>>
>> Signed-off-by: Kees Cook <keescook@chromium.org>
>> Cc: stable@kernel.org
>
> Applied this one to hid.git#for-3.11/CVE-2013-2888

Well, I am a little bit lost here. I know the patch will not break any
existing things, but still, I really can not understand how this can
happen:

- Report IDs are specifically specified to be 1-byte fields. That
means that the max they can have is 0xFF, which is 255 :-/
- If I look at the implementation, hid_get_report() is where we fetch
the report ID from the data. But the report ID is declared as an u8...
so with a max of 255.

So the only problem might comes when specific hid drivers call
manually hid_register_report(), and in this case, we can easily
prevent this by looking at their code. No? Or we could also
specifically change the type of the argument id in
hid_register_report() to be an u8.

Basically, I'd be eager to have proper hid report descriptors and
events that can produce them, so I can add them to my HID regressions
test suite.

Cheers,
Benjamin

>
> Thanks,
>
> --
> Jiri Kosina
> SUSE Labs
> --
> To unsubscribe from this list: send the line "unsubscribe linux-input" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
--
To unsubscribe from this list: send the line "unsubscribe linux-input" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Jiri Kosina Aug. 29, 2013, 9:36 a.m. UTC | #3
On Thu, 29 Aug 2013, Benjamin Tissoires wrote:

> >> The "Report ID" field of a HID report is used to build indexes of
> >> reports. The kernel's index of these is limited to 256 entries, so any
> >> malicious device that sets a Report ID greater than 255 will trigger
> >> memory corruption on the host:
> >>
> >> [ 1347.156239] BUG: unable to handle kernel paging request at ffff88094958a878
> >> [ 1347.156261] IP: [<ffffffff813e4da0>] hid_register_report+0x2a/0x8b
> >>
> >> CVE-2013-2888
> >>
> >> Signed-off-by: Kees Cook <keescook@chromium.org>
> >> Cc: stable@kernel.org
> >
> > Applied this one to hid.git#for-3.11/CVE-2013-2888
> 
> Well, I am a little bit lost here. I know the patch will not break any
> existing things, but still, I really can not understand how this can
> happen:
> 
> - Report IDs are specifically specified to be 1-byte fields. That
> means that the max they can have is 0xFF, which is 255 :-/

Hmm, with a specially crafted device, you can end up with item->size > 1 
coming out from fetch_item() for report ID, no?
Benjamin Tissoires Aug. 29, 2013, 9:43 a.m. UTC | #4
On Thu, Aug 29, 2013 at 11:36 AM, Jiri Kosina <jkosina@suse.cz> wrote:
> On Thu, 29 Aug 2013, Benjamin Tissoires wrote:
>
>> >> The "Report ID" field of a HID report is used to build indexes of
>> >> reports. The kernel's index of these is limited to 256 entries, so any
>> >> malicious device that sets a Report ID greater than 255 will trigger
>> >> memory corruption on the host:
>> >>
>> >> [ 1347.156239] BUG: unable to handle kernel paging request at ffff88094958a878
>> >> [ 1347.156261] IP: [<ffffffff813e4da0>] hid_register_report+0x2a/0x8b
>> >>
>> >> CVE-2013-2888
>> >>
>> >> Signed-off-by: Kees Cook <keescook@chromium.org>
>> >> Cc: stable@kernel.org
>> >
>> > Applied this one to hid.git#for-3.11/CVE-2013-2888
>>
>> Well, I am a little bit lost here. I know the patch will not break any
>> existing things, but still, I really can not understand how this can
>> happen:
>>
>> - Report IDs are specifically specified to be 1-byte fields. That
>> means that the max they can have is 0xFF, which is 255 :-/
>
> Hmm, with a specially crafted device, you can end up with item->size > 1
> coming out from fetch_item() for report ID, no?

Then the problem is in the parser, and if (item->size > 1) for
HID_GLOBAL_ITEM_TAG_REPORT_ID, we should directly bail out and reject
the device instead of continuing... which is exactly Kees' patch :)

Sorry for the noise :)

Cheers,
Benjamin
--
To unsubscribe from this list: send the line "unsubscribe linux-input" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Jiri Kosina Sept. 4, 2013, 10:11 a.m. UTC | #5
So I have now queued subset of the patches that don't deal with 
hid_validate_report(); waiting for v2 based on Benjamin's comments with 
the rest.

The patches are public for distributions to apply them if they feel urgent 
need, but for upstream I'd rather do things properly, and Benjamin had 
quite some valuable feedback.

Thanks again,
diff mbox

Patch

diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c
index 36668d1..5ea7d51 100644
--- a/drivers/hid/hid-core.c
+++ b/drivers/hid/hid-core.c
@@ -63,6 +63,8 @@  struct hid_report *hid_register_report(struct hid_device *device, unsigned type,
 	struct hid_report_enum *report_enum = device->report_enum + type;
 	struct hid_report *report;
 
+	if (id >= HID_MAX_IDS)
+		return NULL;
 	if (report_enum->report_id_hash[id])
 		return report_enum->report_id_hash[id];
 
@@ -404,8 +406,10 @@  static int hid_parser_global(struct hid_parser *parser, struct hid_item *item)
 
 	case HID_GLOBAL_ITEM_TAG_REPORT_ID:
 		parser->global.report_id = item_udata(item);
-		if (parser->global.report_id == 0) {
-			hid_err(parser->device, "report_id 0 is invalid\n");
+		if (parser->global.report_id == 0 ||
+		    parser->global.report_id >= HID_MAX_IDS) {
+			hid_err(parser->device, "report_id %u is invalid\n",
+				parser->global.report_id);
 			return -1;
 		}
 		return 0;
@@ -575,7 +579,7 @@  static void hid_close_report(struct hid_device *device)
 	for (i = 0; i < HID_REPORT_TYPES; i++) {
 		struct hid_report_enum *report_enum = device->report_enum + i;
 
-		for (j = 0; j < 256; j++) {
+		for (j = 0; j < HID_MAX_IDS; j++) {
 			struct hid_report *report = report_enum->report_id_hash[j];
 			if (report)
 				hid_free_report(report);
diff --git a/include/linux/hid.h b/include/linux/hid.h
index 0c48991..ff545cc 100644
--- a/include/linux/hid.h
+++ b/include/linux/hid.h
@@ -393,10 +393,12 @@  struct hid_report {
 	struct hid_device *device;			/* associated device */
 };
 
+#define HID_MAX_IDS 256
+
 struct hid_report_enum {
 	unsigned numbered;
 	struct list_head report_list;
-	struct hid_report *report_id_hash[256];
+	struct hid_report *report_id_hash[HID_MAX_IDS];
 };
 
 #define HID_REPORT_TYPES 3