aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/hid/hid-logitech-dj.c
diff options
context:
space:
mode:
authorJiri Kosina <jkosina@suse.cz>2013-07-10 13:56:27 -0400
committerJiri Kosina <jkosina@suse.cz>2013-07-22 10:16:40 -0400
commit27ce405039bfe6d3f4143415c638f56a3df77dca (patch)
tree5419dac5ff32335c81bb1d753a2c0df181ed44c7 /drivers/hid/hid-logitech-dj.c
parent0adb9c2c5ed42f199cb2a630c37d18dee385fae2 (diff)
HID: fix data access in implement()
implement() is setting bytes in LE data stream. In case the data is not aligned to 64bits, it reads past the allocated buffer. It doesn't really change any value there (it's properly bitmasked), but in case that this read past the boundary hits a page boundary, pagefault happens when accessing 64bits of 'x' in implement(), and kernel oopses. This happens much more often when numbered reports are in use, as the initial 8bit skip in the buffer makes the whole process work on values which are not aligned to 64bits. This problem dates back to attempts in 2005 and 2006 to make implement() and extract() as generic as possible, and even back then the problem was realized by Adam Kroperlin, but falsely assumed to be impossible to cause any harm: http://www.mail-archive.com/linux-usb-devel@lists.sourceforge.net/msg47690.html I have made several attempts at fixing it "on the spot" directly in implement(), but the results were horrible; the special casing for processing last 64bit chunk and switching to different math makes it unreadable mess. I therefore took a path to allocate a few bytes more which will never make it into final report, but are there as a cushion for all the 64bit math operations happening in implement() and extract(). All callers of hid_output_report() are converted at the same time to allocate the buffer by newly introduced hid_alloc_report_buf() helper. Bruno noticed that the whole raw_size test can be dropped as well, as hid_alloc_report_buf() makes sure that the buffer is always of a proper size. Reviewed-by: Benjamin Tissoires <benjamin.tissoires@redhat.com> Acked-by: Gustavo Padovan <gustavo.padovan@collabora.co.uk> Signed-off-by: Jiri Kosina <jkosina@suse.cz>
Diffstat (limited to 'drivers/hid/hid-logitech-dj.c')
-rw-r--r--drivers/hid/hid-logitech-dj.c12
1 files changed, 10 insertions, 2 deletions
diff --git a/drivers/hid/hid-logitech-dj.c b/drivers/hid/hid-logitech-dj.c
index 5207591a598c..4d792739dbd1 100644
--- a/drivers/hid/hid-logitech-dj.c
+++ b/drivers/hid/hid-logitech-dj.c
@@ -574,7 +574,7 @@ static int logi_dj_ll_input_event(struct input_dev *dev, unsigned int type,
574 574
575 struct hid_field *field; 575 struct hid_field *field;
576 struct hid_report *report; 576 struct hid_report *report;
577 unsigned char data[8]; 577 unsigned char *data;
578 int offset; 578 int offset;
579 579
580 dbg_hid("%s: %s, type:%d | code:%d | value:%d\n", 580 dbg_hid("%s: %s, type:%d | code:%d | value:%d\n",
@@ -590,6 +590,13 @@ static int logi_dj_ll_input_event(struct input_dev *dev, unsigned int type,
590 return -1; 590 return -1;
591 } 591 }
592 hid_set_field(field, offset, value); 592 hid_set_field(field, offset, value);
593
594 data = hid_alloc_report_buf(field->report, GFP_KERNEL);
595 if (!data) {
596 dev_warn(&dev->dev, "failed to allocate report buf memory\n");
597 return -1;
598 }
599
593 hid_output_report(field->report, &data[0]); 600 hid_output_report(field->report, &data[0]);
594 601
595 output_report_enum = &dj_rcv_hiddev->report_enum[HID_OUTPUT_REPORT]; 602 output_report_enum = &dj_rcv_hiddev->report_enum[HID_OUTPUT_REPORT];
@@ -600,8 +607,9 @@ static int logi_dj_ll_input_event(struct input_dev *dev, unsigned int type,
600 607
601 hid_hw_request(dj_rcv_hiddev, report, HID_REQ_SET_REPORT); 608 hid_hw_request(dj_rcv_hiddev, report, HID_REQ_SET_REPORT);
602 609
603 return 0; 610 kfree(data);
604 611
612 return 0;
605} 613}
606 614
607static int logi_dj_ll_start(struct hid_device *hid) 615static int logi_dj_ll_start(struct hid_device *hid)