New version (unchanged for patches 1-3), with a test added so we can detect this.
Followup of https://lore.kernel.org/linux-input/c75433e0-9b47-4072-bbe8-b1d14ea97b13@row...
This initial series attempt at fixing the various bugs discovered by Alan regarding __hid_request().
Syzbot managed to create a report descriptor which presents a feature request of size 0 (still trying to extract it) and this exposed the fact that __hid_request() was incorrectly handling the case when the report ID is not used.
Send a first batch of fixes now so we get the feedback from syzbot ASAP.
Note: in the series, I also mentioned that the report of size 0 should be stripped out of the HID device, but I'm not entirely sure this would be a good idea in the end.
Signed-off-by: Benjamin Tissoires bentiss@kernel.org --- Changes in v2: - added Tested-by from syzbot (https://lore.kernel.org/r/686e9113.050a0220.385921.0008.GAE@google.com) - added a python test for it - Link to v1: https://lore.kernel.org/r/20250709-report-size-null-v1-0-194912215cbc@kernel...
--- Benjamin Tissoires (4): HID: core: ensure the allocated report buffer can contain the reserved report ID HID: core: ensure __hid_request reserves the report ID as the first byte HID: core: do not bypass hid_hw_raw_request selftests/hid: add a test case for the recent syzbot underflow
drivers/hid/hid-core.c | 19 +++++-- tools/testing/selftests/hid/tests/test_mouse.py | 70 +++++++++++++++++++++++++ 2 files changed, 84 insertions(+), 5 deletions(-) --- base-commit: 1f988d0788f50d8464f957e793fab356e2937369 change-id: 20250709-report-size-null-37619ea20288
Best regards,
When the report ID is not used, the low level transport drivers expect the first byte to be 0. However, currently the allocated buffer not account for that extra byte, meaning that instead of having 8 guaranteed bytes for implement to be working, we only have 7.
Reported-by: Alan Stern stern@rowland.harvard.edu Closes: https://lore.kernel.org/linux-input/c75433e0-9b47-4072-bbe8-b1d14ea97b13@row... Cc: stable@vger.kernel.org Suggested-by: Alan Stern stern@rowland.harvard.edu Signed-off-by: Benjamin Tissoires bentiss@kernel.org --- drivers/hid/hid-core.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index b348d0464314ca331da073128f0ec4e0a6a91ed1..1a231dd9e4bc83202f2cbcd8b3a21e8c82b9deec 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -1883,9 +1883,12 @@ u8 *hid_alloc_report_buf(struct hid_report *report, gfp_t flags) /* * 7 extra bytes are necessary to achieve proper functionality * of implement() working on 8 byte chunks + * 1 extra byte for the report ID if it is null (not used) so + * we can reserve that extra byte in the first position of the buffer + * when sending it to .raw_request() */
- u32 len = hid_report_len(report) + 7; + u32 len = hid_report_len(report) + 7 + (report->id == 0);
return kzalloc(len, flags); }
The low level transport driver expects the first byte to be the report ID, even when the report ID is not use (in which case they just shift the buffer).
However, __hid_request() whas not offsetting the buffer it used by one in this case, meaning that the raw_request() callback emitted by the transport driver would be stripped of the first byte.
Reported-by: Alan Stern stern@rowland.harvard.edu Closes: https://lore.kernel.org/linux-input/c75433e0-9b47-4072-bbe8-b1d14ea97b13@row... Reported-by: syzbot+8258d5439c49d4c35f43@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=8258d5439c49d4c35f43 Tested-by: syzbot+8258d5439c49d4c35f43@syzkaller.appspotmail.com Fixes: 4fa5a7f76cc7 ("HID: core: implement generic .request()") Cc: stable@vger.kernel.org Signed-off-by: Benjamin Tissoires bentiss@kernel.org --- drivers/hid/hid-core.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-)
diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 1a231dd9e4bc83202f2cbcd8b3a21e8c82b9deec..320887c365f7a36f7376556ffd19f99e52b7d732 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -1976,7 +1976,7 @@ static struct hid_report *hid_get_report(struct hid_report_enum *report_enum, int __hid_request(struct hid_device *hid, struct hid_report *report, enum hid_class_request reqtype) { - char *buf; + char *buf, *data_buf; int ret; u32 len;
@@ -1984,10 +1984,17 @@ int __hid_request(struct hid_device *hid, struct hid_report *report, if (!buf) return -ENOMEM;
+ data_buf = buf; len = hid_report_len(report);
+ if (report->id == 0) { + /* reserve the first byte for the report ID */ + data_buf++; + len++; + } + if (reqtype == HID_REQ_SET_REPORT) - hid_output_report(report, buf); + hid_output_report(report, data_buf);
ret = hid->ll_driver->raw_request(hid, report->id, buf, len, report->type, reqtype);
hid_hw_raw_request() is actually useful to ensure the provided buffer and length are valid. Directly calling in the low level transport driver function bypassed those checks and allowed invalid paramto be used.
Reported-by: Alan Stern stern@rowland.harvard.edu Closes: https://lore.kernel.org/linux-input/c75433e0-9b47-4072-bbe8-b1d14ea97b13@row... Cc: stable@vger.kernel.org Signed-off-by: Benjamin Tissoires bentiss@kernel.org --- drivers/hid/hid-core.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 320887c365f7a36f7376556ffd19f99e52b7d732..b31b8a2fd540bd5ed66599020824726e69d10d75 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -1996,8 +1996,7 @@ int __hid_request(struct hid_device *hid, struct hid_report *report, if (reqtype == HID_REQ_SET_REPORT) hid_output_report(report, data_buf);
- ret = hid->ll_driver->raw_request(hid, report->id, buf, len, - report->type, reqtype); + ret = hid_hw_raw_request(hid, report->id, buf, len, report->type, reqtype); if (ret < 0) { dbg_hid("unable to complete request: %d\n", ret); goto out;
linux-stable-mirror@lists.linaro.org