HID: Proper fix for OOM in hid-core
Subsystems (11)
Benjamin Tissoires
maintainercover HID: Proper fix for OOM in hid-core
Commit 0a3fe972a7cb ("HID: core: Mitigate potential OOB by removing bogus memset()") enforced the provided data to be at least the size of the declared buffer in the report descriptor to prevent a buffer overflow.
We only had corner cases of malicious devices exposing the OOM because in most cases, the buffer provided by the transport layer needs to be allocated at probe time and is large enough to handle all the possible reports.
However, the patch from above, which enforces the spec a little bit more introduced both regressions for devices not following the spec (not necesserally malicious), but also a stream of errors for those devices.
Let's revert to the old behavior by giving more information to HID core to be able to decide whether it can or not memset the rest of the buffer to 0 and continue the processing.
Note that the first commit makes an API change, but the callers are relatively limited, so it should be fine on its own. The second patch can't really make the same kind of API change because we have too many callers in various subsystems. We can switch them one by one to the safe approach when needed.
The last 2 patches are small cleanups I initially put together with the 2 first patches, but they can be applied on their own and don't need to be pulled in stable like the first 2.
Cheers,
Benjamin
---
Benjamin Tissoires (4):
HID: pass the buffer size to hid_report_raw_event
HID: core: introduce hid_safe_input_report()
HID: multitouch: use __free(kfree) to clean up temporary buffers
HID: wacom: use __free(kfree) to clean up temporary buffers
drivers/hid/bpf/hid_bpf_dispatch.c | 6 ++-- drivers/hid/hid-core.c | 63 +++++++++++++++++++++++++++++--------- drivers/hid/hid-gfrm.c | 4 +-- drivers/hid/hid-logitech-hidpp.c | 2 +- drivers/hid/hid-multitouch.c | 18 +++++------ drivers/hid/hid-primax.c | 2 +- drivers/hid/hid-vivaldi-common.c | 2 +- drivers/hid/i2c-hid/i2c-hid-core.c | 7 +++-- drivers/hid/usbhid/hid-core.c | 11 ++++--- drivers/hid/wacom_sys.c | 46 ++++++++++------------------ drivers/staging/greybus/hid.c | 2 +- include/linux/hid.h | 6 ++-- include/linux/hid_bpf.h | 14 ++++++--- 13 files changed, 105 insertions(+), 78 deletions(-)
---
base-commit: 7df6572f1cb381d6b89ceed58e3b076c233c2cd0
change-id: 20260415-wip-fix-core-7d85c8516ed0
Best regards,
Signature · 1 line
Benjamin Tissoires <[email protected]>
Benjamin Tissoires
maintainerPATCH 1/4 HID: pass the buffer size to hid_report_raw_event
commit 0a3fe972a7cb ("HID: core: Mitigate potential OOB by removing bogus memset()") enforced the provided data to be at least the size of the declared buffer in the report descriptor to prevent a buffer overflow. However, we can try to be smarter by providing both the buffer size and the data size, meaning that hid_report_raw_event() can make better decision whether we should plaining reject the buffer (buffer overflow attempt) or if we can safely memset it to 0 and pass it to the rest of the stack.
--- drivers/hid/bpf/hid_bpf_dispatch.c | 6 ++++-- drivers/hid/hid-core.c | 42 +++++++++++++++++++++++++------------- drivers/hid/hid-gfrm.c | 4 ++-- drivers/hid/hid-logitech-hidpp.c | 2 +- drivers/hid/hid-multitouch.c | 2 +- drivers/hid/hid-primax.c | 2 +- drivers/hid/hid-vivaldi-common.c | 2 +- drivers/hid/wacom_sys.c | 6 +++--- drivers/staging/greybus/hid.c | 2 +- include/linux/hid.h | 4 ++-- include/linux/hid_bpf.h | 14 ++++++++----- 11 files changed, 53 insertions(+), 33 deletions(-)
drivers/hid/bpf/hid_bpf_dispatch.c +4 -2
@@ -24,7 +24,8 @@ EXPORT_SYMBOL(hid_ops);u8 *dispatch_hid_bpf_device_event (struct hid_device *hdev,enum hid_report_type type,u8 *data, -u32 *size,int interrupt,u64 source,bool from_bpf) +size_t *buf_size,u32 *size,int interrupt,u64 source, +bool from_bpf) {struct hid_bpf_ctx_kern ctx_kern = { .ctx = {
@@ -74,6 +75,7 @@ dispatch_hid_bpf_device_event(struct hid_device *hdev, enum hid_report_type type *size = ret; } + *buf_size = ctx_kern.ctx.allocated_size;return ctx_kern.data; }EXPORT_SYMBOL_GPL (dispatch_hid_bpf_device_event);
@@ -505,7 +507,7 @@ __hid_bpf_input_report(struct hid_bpf_ctx *ctx, enum hid_report_type type, u8 *bif (ret)return ret; -return hid_ops->hid_input_report (ctx->hid, type, buf, size,0 , (u64 )(long )ctx, true, +return hid_ops->hid_input_report (ctx->hid, type, buf, size, size,0 , (u64 )(long )ctx, true, lock_already_taken); }
drivers/hid/hid-core.c +28 -14
106 lines not on this page · raw message
drivers/hid/hid-gfrm.c +2 -2
@@ -66,7 +66,7 @@ static int gfrm_raw_event(struct hid_device *hdev, struct hid_report *report,switch (data[1 ]) {case GFRM100_SEARCH_KEY_DOWN : ret =hid_report_raw_event (hdev,HID_INPUT_REPORT , search_key_dn, -sizeof (search_key_dn),1 ); +sizeof (search_key_dn),sizeof (search_key_dn),1 );break ;case GFRM100_SEARCH_KEY_AUDIO_DATA :
@@ -74,7 +74,7 @@ static int gfrm_raw_event(struct hid_device *hdev, struct hid_report *report,case GFRM100_SEARCH_KEY_UP : ret =hid_report_raw_event (hdev,HID_INPUT_REPORT , search_key_up, -sizeof (search_key_up),1 ); +sizeof (search_key_up),sizeof (search_key_up),1 );break ;default :
drivers/hid/hid-logitech-hidpp.c +1 -1
@@ -3673,7 +3673,7 @@ static int hidpp10_consumer_keys_raw_event(struct hidpp_device *hidpp,memcpy (&consumer_report[1 ], &data[3 ],4 );/* We are called from atomic context */ hid_report_raw_event (hidpp->hid_dev,HID_INPUT_REPORT , - consumer_report,5 ,1 ); + consumer_report,sizeof (consumer_report),5 ,1 );return 1 ; }
drivers/hid/hid-multitouch.c +1 -1
@@ -533,7 +533,7 @@ static void mt_get_feature(struct hid_device *hdev, struct hid_report *report) } ret =hid_report_raw_event (hdev,HID_FEATURE_REPORT , buf, - size,0 ); + size, size,0 );if (ret)dev_warn (&hdev->dev,"failed to report feature\n" ); }
drivers/hid/hid-primax.c +1 -1
@@ -44,7 +44,7 @@ static int px_raw_event(struct hid_device *hid, struct hid_report *report, data[0 ] |= (1 << (data[idx] -0xE0 )); data[idx] =0 ; } -hid_report_raw_event (hid,HID_INPUT_REPORT , data, size,0 ); +hid_report_raw_event (hid,HID_INPUT_REPORT , data, size, size,0 );return 1 ;default :/* unknown report */
drivers/hid/hid-vivaldi-common.c +1 -1
@@ -85,7 +85,7 @@ void vivaldi_feature_mapping(struct hid_device *hdev, } ret =hid_report_raw_event (hdev,HID_FEATURE_REPORT , report_data, - report_len,0 ); + report_len, report_len,0 );if (ret) {dev_warn (&hdev->dev,"failed to report feature %d\n" , field->report->id);
drivers/hid/wacom_sys.c +3 -3
@@ -90,7 +90,7 @@ static void wacom_wac_queue_flush(struct hid_device *hdev,kfree (buf);continue ; } - err =hid_report_raw_event (hdev,HID_INPUT_REPORT , buf, size, false); + err =hid_report_raw_event (hdev,HID_INPUT_REPORT , buf, size, size, false);if (err) {hid_warn (hdev,"%s: unable to flush event due to error %d\n" , __func__, err);
@@ -334,7 +334,7 @@ static void wacom_feature_mapping(struct hid_device *hdev, data, n, WAC_CMD_RETRIES);if (ret == n && features->type ==HID_GENERIC ) { ret =hid_report_raw_event (hdev, -HID_FEATURE_REPORT , data, n,0 ); +HID_FEATURE_REPORT , data, n, n,0 ); }else if (ret ==2 && features->type !=HID_GENERIC ) { features->touch_max = data[1 ]; }else {
@@ -395,7 +395,7 @@ static void wacom_feature_mapping(struct hid_device *hdev, data, n, WAC_CMD_RETRIES);if (ret == n) { ret =hid_report_raw_event (hdev,HID_FEATURE_REPORT , - data, n,0 ); + data, n, n,0 ); }else {hid_warn (hdev,"%s: could not retrieve sensor offsets\n" , __func__);
drivers/staging/greybus/hid.c +1 -1
@@ -201,7 +201,7 @@ static void gb_hid_init_report(struct gb_hid *ghid, struct hid_report *report)* we just need to setup the input fields, so using * hid_report_raw_event is safe. */ -hid_report_raw_event (ghid->hid, report->type, ghid->inbuf, size,1 ); +hid_report_raw_event (ghid->hid, report->type, ghid->inbuf, ghib->bufsize, size,1 ); }static void gb_hid_init_reports (struct gb_hid *ghid)
include/linux/hid.h +2 -2
@@ -1298,8 +1298,8 @@ static inline u32 hid_report_len(struct hid_report *report)return DIV_ROUND_UP (report->size,8 ) + (report->id >0 ); } -int hid_report_raw_event (struct hid_device *hid,enum hid_report_type type,u8 *data,u32 size, -int interrupt); +int hid_report_raw_event (struct hid_device *hid,enum hid_report_type type,u8 *data, +size_t bufsize,u32 size,int interrupt);/* HID quirks API */ unsigned long hid_lookup_quirk (const struct hid_device *hdev);
include/linux/hid_bpf.h +9 -5
@@ -72,8 +72,8 @@ struct hid_ops {int (*hid_hw_output_report)(struct hid_device *hdev,__u8 *buf,size_t len,u64 source,bool from_bpf);int (*hid_input_report)(struct hid_device *hid,enum hid_report_type type, -u8 *data,u32 size,int interrupt,u64 source,bool from_bpf, -bool lock_already_taken); +u8 *data,size_t bufsize,u32 size,int interrupt,u64 source, +bool from_bpf,bool lock_already_taken);struct module *owner;const struct bus_type *bus_type; };
@@ -200,7 +200,8 @@ struct hid_bpf {#ifdef CONFIG_HID_BPF u8 *dispatch_hid_bpf_device_event (struct hid_device *hid,enum hid_report_type type,u8 *data, -u32 *size,int interrupt,u64 source,bool from_bpf); +size_t *buf_size,u32 *size,int interrupt,u64 source, +bool from_bpf); int dispatch_hid_bpf_raw_requests(struct hid_device *hdev, unsigned char reportnum, __u8 *buf, u32 size, enum hid_report_type rtype,
@@ -215,8 +216,11 @@ int hid_bpf_device_init(struct hid_device *hid);const u8 *call_hid_bpf_rdesc_fixup (struct hid_device *hdev,const u8 *rdesc,unsigned int *size);#else /* CONFIG_HID_BPF */ static inline u8 *dispatch_hid_bpf_device_event (struct hid_device *hid,enum hid_report_type type, -u8 *data,u32 *size,int interrupt, -u64 source,bool from_bpf) {return data; } +u8 *data,size_t *buf_size,u32 *size, +int interrupt,u64 source,bool from_bpf) +{ +return data; +} static inline int dispatch_hid_bpf_raw_requests(struct hid_device *hdev, unsigned char reportnum, u8 *buf, u32 size, enum hid_report_type rtype,
Signature · 1 line
2.53.0
Benjamin Tissoires
maintainerPATCH 2/4 HID: core: introduce hid_safe_input_report()
In reply to Benjamin Tissoireshid_input_report() is used in too many places to have a commit that doesn't cross subsystem borders. Instead of changing the API, introduce a new one when things matters in the transport layers:
- usbhid
- i2chid
This effectively revert to the old behavior for those two transport layers.
--- drivers/hid/hid-core.c | 21 +++++++++++++++++++++ drivers/hid/i2c-hid/i2c-hid-core.c | 7 ++++--- drivers/hid/usbhid/hid-core.c | 11 ++++++----- include/linux/hid.h | 2 ++ 4 files changed, 33 insertions(+), 8 deletions(-)
drivers/hid/hid-core.c +21 -0
@@ -2191,6 +2191,27 @@ int hid_input_report(struct hid_device *hid, enum hid_report_type type, u8 *data }EXPORT_SYMBOL_GPL (hid_input_report); +/** +* hid_safe_input_report - report data from lower layer (usb, bt...) +* +* @hid: hid device +* @type: HID report type (HID_*_REPORT) +* @data: report contents +* @bufsize: allocated size of the data buffer +* @size: useful size of data parameter +* @interrupt: distinguish between interrupt and control transfers +* +* This is data entry for lower layers. +*/ +int hid_safe_input_report (struct hid_device *hid,enum hid_report_type type,u8 *data, +size_t bufsize,u32 size,int interrupt) +{ +return __hid_input_report (hid, type, data, bufsize, size, interrupt,0 , + false,/* from_bpf */ + false/* lock_already_taken */ ); +} +EXPORT_SYMBOL_GPL (hid_safe_input_report); +bool hid_match_one_id (const struct hid_device *hdev,const struct hid_device_id *id) {
drivers/hid/i2c-hid/i2c-hid-core.c +4 -3
@@ -574,9 +574,10 @@ static void i2c_hid_get_input(struct i2c_hid *ihid)if (ihid->hid->group !=HID_GROUP_RMI )pm_wakeup_event (&ihid->client->dev,0 ); -hid_input_report (ihid->hid,HID_INPUT_REPORT , - ihid->inbuf +sizeof (__le16), - ret_size -sizeof (__le16),1 ); +hid_safe_input_report (ihid->hid,HID_INPUT_REPORT , + ihid->inbuf +sizeof (__le16), + ihid->bufsize -sizeof (__le16), + ret_size -sizeof (__le16),1 ); }return ;
drivers/hid/usbhid/hid-core.c +6 -5
@@ -283,9 +283,9 @@ static void hid_irq_in(struct urb *urb)break ;usbhid_mark_busy (usbhid);if (!test_bit (HID_RESUME_RUNNING , &usbhid->iofl)) { -hid_input_report (urb->context,HID_INPUT_REPORT , - urb->transfer_buffer, - urb->actual_length,1 ); +hid_safe_input_report (urb->context,HID_INPUT_REPORT , + urb->transfer_buffer, urb->transfer_buffer_length, + urb->actual_length,1 );/* * autosuspend refused while keys are pressed * because most keyboards don't wake up when
@@ -482,9 +482,10 @@ static void hid_ctrl(struct urb *urb)switch (status) {case 0 :/* success */ if (usbhid->ctrl[usbhid->ctrltail].dir ==USB_DIR_IN ) -hid_input_report (urb->context, +hid_safe_input_report (urb->context, usbhid->ctrl[usbhid->ctrltail].report->type, - urb->transfer_buffer, urb->actual_length,0 ); + urb->transfer_buffer, urb->transfer_buffer_length, + urb->actual_length,0 );break ;case -ESHUTDOWN :/* unplug */ unplug =1 ;
include/linux/hid.h +2 -0
@@ -1030,6 +1030,8 @@ struct hid_field *hid_find_field(struct hid_device *hdev, unsigned int report_tyint hid_set_field (struct hid_field *,unsigned ,__s32 );int hid_input_report (struct hid_device *hid,enum hid_report_type type,u8 *data,u32 size,int interrupt); +int hid_safe_input_report (struct hid_device *hid,enum hid_report_type type,u8 *data, +size_t bufsize,u32 size,int interrupt);struct hid_field *hidinput_get_led_field (struct hid_device *hid);unsigned int hidinput_count_leds (struct hid_device *hid);__s32 hidinput_calc_abs_res (const struct hid_field *field,__u16 code);
Signature · 1 line
2.53.0
Bastien Nocera
reviewerRe: [PATCH 2/4] HID: core: introduce hid_safe_input_report()
Quoted 33 lines · Benjamin Tissoires
hid_input_report()is used in too many places to have a commit that doesn't cross subsystem borders. Instead of changing the API, introduce
a new one when things matters in the transport layers:
- usbhid
- i2chidThis effectively revert to the old behavior for those two transport layers.
Fixes 0a3fe972a7cb ("HID: core: Mitigate potential OOB by removingbogus
memset()")Signed-off-by Benjamin Tissoires <[email protected]>---
drivers/hid/hid-core.c | 21 +++++++++++++++++++++drivers/hid/i2c-hid/i2c-hid-core.c| 7 ++++--- drivers/hid/usbhid/hid-core.c | 11 ++++++----- include/linux/hid.h | 2 ++4 files changed, 33 insertions(+), 8 deletions(-)drivers/hid/hid-core.c +0
-0@@ -2191,6 +2191,27 @@ int hid_input_report(struct hid_device *hid,enum
hid_report_typetype, u8 *data
}
EXPORT_SYMBOL_GPL(hid_input_report);
+/**
+ *hid_safe_input_report- report data from lower layer (usb, bt...)
+ *
Quoted 8 lines
+ * @hid: hid device
+ * @type: HID report type (HID_*_REPORT)
+ * @data: report contents
+ * @bufsize: allocated size of the data buffer
+ * @size: useful size of data parameter
+ * @interrupt: distinguish between interrupt and control transfers
+ *
+ * This is data entry for lower layers.
You probably want to explain why it should be used instead of hid_input_report() in this doc blurb, and modify the hid_input_report() docs to mention that this should be used.
Maybe hid_input_report() should also be marked as deprecated somehow, to avoid new users?
Cheers
Quoted 93 lines
+ */
+int hid_safe_input_report(structhid_device*hid, enumhid_report_typetype, u8 *data,drivers/hid/hid-core.c +3
-0+ size_t bufsize, u32 size, int interrupt) +{ + return __hid_input_report(hid, type, data, bufsize, size,interrupt, 0,
drivers/hid/hid-core.c +5
-0+ false, /* from_bpf */ + false /* lock_already_taken */); +} +EXPORT_SYMBOL_GPL (hid_safe_input_report); +bool
hid_match_one_id(const struct hid_device *hdev, const struct hid_device_id *id){drivers/hid/i2c- renamed from drivers/hid/i2c-hid/i2c-hid-core.c +0
-0
hid/i2c-hid-core.c
index5a183af3d5c6..e0a302544cef100644drivers/hid/i2c-hid/i2c-hid-core.c +0
-0@@ -574,9 +574,10 @@ static void i2c_hid_get_input(struct i2c_hid *ihid) if (ihid->hid->group != HID_GROUP_RMI) pm_wakeup_event(&ihid->client->dev, 0);drivers/hid/i2c-hid/i2c-hid-core.c +3
-3-hid_input_report (ihid->hid,HID_INPUT_REPORT , - ihid->inbuf +sizeof (__le16), - ret_size -sizeof (__le16),1 ); + hid_safe_input_report(ihid->hid, HID_INPUT_REPORT, + ihid->inbuf + sizeof(__le16), + ihid->bufsize -
sizeof(__le16),drivers/hid/i2c-hid/i2c-hid-core.c +1
-0+ ret_size - sizeof(__le16), 1);}
return;drivers/hid/usbhid/hid- renamed from drivers/hid/usbhid/hid-core.c +0
-0
core.c
indexfbbfc0f60829..5af93b9b1fb5100644drivers/hid/usbhid/hid-core.c +3
-3@@ -283,9 +283,9 @@ static void hid_irq_in(struct urb *urb) break; usbhid_mark_busy(usbhid); if (!test_bit(HID_RESUME_RUNNING, &usbhid->iofl)) { -hid_input_report (urb->context,HID_INPUT_REPORT , - urb->transfer_buffer, - urb->actual_length,1 ); + hid_safe_input_report(urb->context, HID_INPUT_REPORT, + urb->transfer_buffer, urb->transfer_buffer_length, + urb->actual_length,1);
/*
* autosuspend refused while keys are pressed
* because most keyboards don't wake up whendrivers/hid/usbhid/hid-core.c +0
-0@@ -482,9 +482,10 @@ static void hid_ctrl(struct urb *urb) switch (status) {case 0: /* success */
if (usbhid->ctrl[usbhid->ctrltail].dir ==USB_DIR_IN)drivers/hid/usbhid/hid-core.c +1
-1- hid_input_report(urb->context, + hid_safe_input_report(urb->context,usbhid->ctrl[usbhid-
Quoted 1 line
ctrltail].report->type,
drivers/hid/usbhid/hid-core.c +0
-1- urb->transfer_buffer, urb-Quoted 1 line
actual_length, 0);drivers/hid/usbhid/hid-core.c +1
-0+ urb->transfer_buffer, urb-Quoted 1 line
transfer_buffer_length,drivers/hid/usbhid/hid-core.c +1
-0+ urb->actual_length, 0);break;
case-ESHUTDOWN: /* unplug */
unplug = 1;include/linux/hid.h +0
-0@@ -1030,6 +1030,8 @@ struct hid_field *hid_find_field(struct
hid_device*hdev, unsigned intreport_ty
inthid_set_field(struct hid_field *, unsigned, __s32); int hid_input_report(structhid_device*hid, enumhid_report_typetype, u8 *data, u32 size,
int interrupt);
+int hid_safe_input_report(structhid_device*hid, enumhid_report_typetype, u8 *data,include/linux/hid.h +1
-0+ size_t bufsize, u32 size, int interrupt);struct
hid_field*hidinput_get_led_field(structhid_device*hid); unsigned inthidinput_count_leds(struct hid_device *hid);__s32hidinput_calc_abs_res(const struct hid_field *field, __u16 code);
Re: [PATCH 2/4] HID: core: introduce hid_safe_input_report()
Quoted 32 lines · Bastien Nocera
Quoted 30 lines · Benjamin Tissoires
hid_input_report()is used in too many places to have a commit that doesn't cross subsystem borders. Instead of changing the API, introduce
a new one when things matters in the transport layers:
- usbhid
- i2chidThis effectively revert to the old behavior for those two transport layers.
Fixes 0a3fe972a7cb ("HID: core: Mitigate potential OOB by removingbogus
memset()")Signed-off-by Benjamin Tissoires <[email protected]>--- drivers/hid/hid-core.c | 21 +++++++++++++++++++++ drivers/hid/i2c-hid/i2c-hid-core.c | 7 ++++--- drivers/hid/usbhid/hid-core.c | 11 ++++++----- include/linux/hid.h | 2 ++ 4 files changed, 33 insertions(+), 8 deletions(-)drivers/hid/hid-core.c +0
-0@@ -2191,6 +2191,27 @@ int hid_input_report(struct hid_device *hid,enum
hid_report_typetype, u8 *data
}
EXPORT_SYMBOL_GPL(hid_input_report);
Quoted 15 lines
Quoted 11 lines
+/**
+ *hid_safe_input_report- report data from lower layer (usb, bt...)
+ *
+ * @hid: hid device
+ * @type: HID report type (HID_*_REPORT)
+ * @data: report contents
+ * @bufsize: allocated size of the data buffer
+ * @size: useful size of data parameter
+ * @interrupt: distinguish between interrupt and control transfers
+ *
+ * This is data entry for lower layers.You probably want to explain why it should be used instead of
hid_input_report()in this doc blurb, and modify thehid_input_report()docs to mention that this should be used.
Good point. Sending v2 ASAP.
Quoted 3 lines
Maybe
hid_input_report()should also be marked as deprecated somehow, to avoid new users?
Well, it's not entirely deprecated because, for instance, in uhid we only have the buffer with the provided size around. So we can't be less restrictive in that precise case, and then switching to _safe will not change a bit.
Cheers,
Benjamin
Quoted 97 lines
Cheers
Quoted 93 lines
+ */
+int hid_safe_input_report(structhid_device*hid, enumhid_report_typetype, u8 *data,drivers/hid/hid-core.c +3
-0+ size_t bufsize, u32 size, int interrupt) +{ + return __hid_input_report(hid, type, data, bufsize, size,interrupt, 0,
drivers/hid/hid-core.c +5
-0+ false,/* from_bpf */ + false /* lock_already_taken */); +} +EXPORT_SYMBOL_GPL (hid_safe_input_report); +bool hid_match_one_id (const struct hid_device *hdev,const struct hid_device_id *id) {drivers/hid/i2c- renamed from drivers/hid/i2c-hid/i2c-hid-core.c +0
-0
hid/i2c-hid-core.c
index5a183af3d5c6..e0a302544cef100644drivers/hid/i2c-hid/i2c-hid-core.c +4
-3@@ -574,9 +574,10 @@ static void i2c_hid_get_input(struct i2c_hid *ihid)if (ihid->hid->group !=HID_GROUP_RMI )pm_wakeup_event (&ihid->client->dev,0 ); -hid_input_report (ihid->hid,HID_INPUT_REPORT , - ihid->inbuf +sizeof (__le16), - ret_size -sizeof (__le16),1 ); +hid_safe_input_report (ihid->hid,HID_INPUT_REPORT , + ihid->inbuf +sizeof (__le16), + ihid->bufsize -sizeof (__le16), + ret_size -sizeof (__le16),1 ); }return;drivers/hid/usbhid/hid- renamed from drivers/hid/usbhid/hid-core.c +0
-0
core.c
indexfbbfc0f60829..5af93b9b1fb5100644drivers/hid/usbhid/hid-core.c +3
-3@@ -283,9 +283,9 @@ static void hid_irq_in(struct urb *urb)break ;usbhid_mark_busy (usbhid);if (!test_bit (HID_RESUME_RUNNING , &usbhid->iofl)) { -hid_input_report (urb->context,HID_INPUT_REPORT , - urb->transfer_buffer, - urb->actual_length,1 ); + hid_safe_input_report(urb->context, HID_INPUT_REPORT, + urb->transfer_buffer, urb->transfer_buffer_length, + urb->actual_length,1);
/*
* autosuspend refused while keys are
pressed
* because most keyboards don't wake up whendrivers/hid/usbhid/hid-core.c +2
-2@@ -482,9 +482,10 @@ static void hid_ctrl(struct urb *urb)switch (status) {case 0 :/* success */ if (usbhid->ctrl[usbhid->ctrltail].dir ==USB_DIR_IN ) -hid_input_report (urb->context, +hid_safe_input_report (urb->context, usbhid->ctrl[usbhid- >ctrltail].report->type, - urb->transfer_buffer, urb- >actual_length, 0); + urb->transfer_buffer, urb-Quoted 1 line
transfer_buffer_length,drivers/hid/usbhid/hid-core.c +1
-0+ urb->actual_length, 0);break ;case -ESHUTDOWN :/* unplug */ unplug =1 ;include/linux/hid.h +0
-0@@ -1030,6 +1030,8 @@ struct hid_field *hid_find_field(struct
hid_device*hdev, unsigned intreport_ty
inthid_set_field(struct hid_field *, unsigned, __s32); int hid_input_report(structhid_device*hid, enumhid_report_type
type, u8 *data, u32 size,int interrupt);+int hid_safe_input_report(struct
hid_device*hid, enumhid_report_typetype, u8 *data,
+size_tbufsize, u32 size, int interrupt);
structhid_field*hidinput_get_led_field(structhid_device*hid); unsigned inthidinput_count_leds(struct hid_device *hid);__s32hidinput_calc_abs_res(const structhid_field*field,__u16
code);
Re: [PATCH 2/4] HID: core: introduce hid_safe_input_report()
Quoted 55 lines · Benjamin Tissoires
Quoted 32 lines · Bastien Nocera
Quoted 30 lines · Benjamin Tissoires
hid_input_report()is used in too many places to have a commit that doesn't cross subsystem borders. Instead of changing the API, introduce
a new one when things matters in the transport layers:
- usbhid
- i2chidThis effectively revert to the old behavior for those two transport layers.
Fixes 0a3fe972a7cb ("HID: core: Mitigate potential OOB by removingbogus
memset()")Signed-off-by Benjamin Tissoires <[email protected]>--- drivers/hid/hid-core.c | 21 +++++++++++++++++++++ drivers/hid/i2c-hid/i2c-hid-core.c | 7 ++++--- drivers/hid/usbhid/hid-core.c | 11 ++++++----- include/linux/hid.h | 2 ++ 4 files changed, 33 insertions(+), 8 deletions(-)drivers/hid/hid-core.c +0
-0@@ -2191,6 +2191,27 @@ int hid_input_report(struct hid_device *hid,enum
hid_report_typetype, u8 *data
}
EXPORT_SYMBOL_GPL(hid_input_report);Quoted 15 lines
Quoted 11 lines
+/**
+ *hid_safe_input_report- report data from lower layer (usb, bt...)
+ *
+ * @hid: hid device
+ * @type: HID report type (HID_*_REPORT)
+ * @data: report contents
+ * @bufsize: allocated size of the data buffer
+ * @size: useful size of data parameter
+ * @interrupt: distinguish between interrupt and control transfers
+ *
+ * This is data entry for lower layers.You probably want to explain why it should be used instead of
hid_input_report()in this doc blurb, and modify thehid_input_report()docs to mention that this should be used.Good point. Sending v2 ASAP.
Quoted 3 lines
Maybe
hid_input_report()should also be marked as deprecated somehow, to avoid new users?
Quoted 7 lines
Well, it's not entirely deprecated because, for instance, in uhid we only have the buffer with the provided size around. So we can't be less restrictive in that precise case, and then switching to
_safewill not change a bit.Cheers,
Benjamin
Hi Benjamin, our CI started failing with commit 0a3fe972a7cb ("HID:
core: Mitigate potential OOB by removing bogus memset()"), so I was hoping your patchset would fix this.
However, I just realized our call path goes through uhid precisely, which still triggers the EINVAL error since uhid as not converted to hid_safe_input_report().
My vague understanding though, is that uhid_event uses a static buffer in ev->data[UHID_DATA_MAX], so maybe we can use that through uhid_dev_input{2}()?
I ran the following path through our CI and it fixed our issue, so I wanted to get your thoughts on this.
Carlos Llamas
--- drivers/hid/uhid.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-)
drivers/hid/uhid.c +4 -4
@@ -595,8 +595,8 @@ static int uhid_dev_input(struct uhid_device *uhid, struct uhid_event *ev)if (!READ_ONCE (uhid->running))return -EINVAL ; -hid_input_report (uhid->hid,HID_INPUT_REPORT , ev->u.input.data, -min_t (size_t, ev->u.input.size,UHID_DATA_MAX ),0 ); +hid_safe_input_report (uhid->hid,HID_INPUT_REPORT , ev->u.input.data,UHID_DATA_MAX , +min_t (size_t, ev->u.input.size,UHID_DATA_MAX ),0 );return 0 ; }
@@ -606,8 +606,8 @@ static int uhid_dev_input2(struct uhid_device *uhid, struct uhid_event *ev)if (!READ_ONCE (uhid->running))return -EINVAL ; -hid_input_report (uhid->hid,HID_INPUT_REPORT , ev->u.input2.data, -min_t (size_t, ev->u.input2.size,UHID_DATA_MAX ),0 ); +hid_safe_input_report (uhid->hid,HID_INPUT_REPORT , ev->u.input2.data,UHID_DATA_MAX , +min_t (size_t, ev->u.input2.size,UHID_DATA_MAX ),0 );return 0 ; }
Benjamin Tissoires
maintainerRe: [PATCH 2/4] HID: core: introduce hid_safe_input_report()
Hi Carlos,
Quoted 19 lines · Carlos Llamas
Quoted 18 lines · Benjamin Tissoires
Quoted 17 lines · Bastien Nocera
Quoted 15 lines · Benjamin Tissoires
hid_input_report()is used in too many places to have a commit that doesn't cross subsystem borders. Instead of changing the API, introduce
a new one when things matters in the transport layers:
- usbhid
- i2chidThis effectively revert to the old behavior for those two transport layers.
Fixes 0a3fe972a7cb ("HID: core: Mitigate potential OOB by removingbogus
memset()")Signed-off-by Benjamin Tissoires <[email protected]>---
[...]
Quoted 9 lines
Hi Benjamin, our CI started failing with commit
0a3fe972a7cb("HID:
core: Mitigate potential OOB by removing bogusmemset()"), so I was hoping your patchset would fix this.However, I just realized our call path goes through uhid precisely, which still triggers the EINVAL error since uhid as not converted to
hid_safe_input_report().
Quoted 6 lines
My vague understanding though, is that
uhid_eventuses a static buffer in ev->data[UHID_DATA_MAX], so maybe we can use that through uhid_dev_input{2}()?I ran the following path through our CI and it fixed our issue, so I wanted to get your thoughts on this.
Oh, yes, you are correct. Sorry with all the back and forth on this paritcular topic, my brain assumed that uhid was only allocating the useful part of the payload and was not safe.
For the future me: the problem with uhid was that we were emultaing devices that would trigger a bug elsewhere in the stack not in uhid_dev_input*().
Patch looks good, please send it normally to the ML with your SoB :)
Cheers,
Benjamin
Quoted 34 lines
Carlos Llamas
--- drivers/hid/uhid.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-)drivers/hid/uhid.c +4
-4@@ -595,8 +595,8 @@ static int uhid_dev_input(struct uhid_device *uhid, struct uhid_event *ev)if (!READ_ONCE (uhid->running))return -EINVAL ; -hid_input_report (uhid->hid,HID_INPUT_REPORT , ev->u.input.data, -min_t (size_t, ev->u.input.size,UHID_DATA_MAX ),0 ); +hid_safe_input_report (uhid->hid,HID_INPUT_REPORT , ev->u.input.data,UHID_DATA_MAX , +min_t (size_t, ev->u.input.size,UHID_DATA_MAX ),0 );return 0 ; }@@ -606,8 +606,8 @@ static int uhid_dev_input2(struct uhid_device *uhid, struct uhid_event *ev)if (!READ_ONCE (uhid->running))return -EINVAL ; -hid_input_report (uhid->hid,HID_INPUT_REPORT , ev->u.input2.data, -min_t (size_t, ev->u.input2.size,UHID_DATA_MAX ),0 ); +hid_safe_input_report (uhid->hid,HID_INPUT_REPORT , ev->u.input2.data,UHID_DATA_MAX , +min_t (size_t, ev->u.input2.size,UHID_DATA_MAX ),0 );return 0 ; }
Benjamin Tissoires
maintainerPATCH 3/4 HID: multitouch: use __free(kfree) to clean up temporary buffers
In reply to Benjamin TissoiresThis simplifies error handling and protects against memory leaks.
--- drivers/hid/hid-multitouch.c | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-)
drivers/hid/hid-multitouch.c +6 -10
@@ -507,7 +507,6 @@ static void mt_get_feature(struct hid_device *hdev, struct hid_report *report) {int ret;u32 size =hid_report_len (report); -u8 *buf;/* * Do not fetch the feature report if the device has been explicitly
@@ -516,7 +515,7 @@ static void mt_get_feature(struct hid_device *hdev, struct hid_report *report)if (hdev->quirks &HID_QUIRK_NO_INIT_REPORTS )return ; - buf =hid_alloc_report_buf (report,GFP_KERNEL ); + u8 *buf __free(kfree) = hid_alloc_report_buf(report, GFP_KERNEL);if (!buf)return ;
@@ -529,7 +528,7 @@ static void mt_get_feature(struct hid_device *hdev, struct hid_report *report)/* The report ID in the request and the response should match */ if (report->id != buf[0 ]) {hid_err (hdev,"Returned feature report did not match the request\n" ); - goto free; +return ; } ret = hid_report_raw_event(hdev, HID_FEATURE_REPORT, buf,
@@ -537,9 +536,6 @@ static void mt_get_feature(struct hid_device *hdev, struct hid_report *report)if (ret)dev_warn (&hdev->dev,"failed to report feature\n" ); } - -free: -kfree (buf); } static void mt_feature_mapping(struct hid_device *hdev,
@@ -1658,7 +1654,6 @@ static bool mt_need_to_apply_feature(struct hid_device *hdev,struct mt_class *cls = &td->mtclass;struct hid_report *report = field->report;unsigned int index = usage->usage_index; -char *buf;u32 report_len;int max;
@@ -1673,17 +1668,18 @@ static bool mt_need_to_apply_feature(struct hid_device *hdev,return false;if (cls->quirks &MT_QUIRK_FORCE_GET_FEATURE ) { - report_len =hid_report_len (report); - buf =hid_alloc_report_buf (report,GFP_KERNEL ); + char *buf __free(kfree) = hid_alloc_report_buf(report, GFP_KERNEL); +if (!buf) {hid_err (hdev,"failed to allocate buffer for report\n" );return false; } + + report_len =hid_report_len (report);hid_hw_raw_request (hdev, report->id, buf, report_len,HID_FEATURE_REPORT ,HID_REQ_GET_REPORT ); -kfree (buf); } field->value[index] = td->inputmode_value;
Signature · 1 line
2.53.0
Benjamin Tissoires
maintainerPATCH 4/4 HID: wacom: use __free(kfree) to clean up temporary buffers
In reply to Benjamin TissoiresThis simplifies error handling and protects against memory leaks.
--- drivers/hid/wacom_sys.c | 40 +++++++++++++--------------------------- 1 file changed, 13 insertions(+), 27 deletions(-)
drivers/hid/wacom_sys.c +13 -27
@@ -70,11 +70,10 @@ static void wacom_wac_queue_flush(struct hid_device *hdev, {while (!kfifo_is_empty (fifo)) {int size =kfifo_peek_len (fifo); -u8 *buf;unsigned int count;int err; - buf =kzalloc (size,GFP_KERNEL ); + u8 *buf __free(kfree) = kzalloc(size, GFP_KERNEL);if (!buf) {kfifo_skip (fifo);continue ;
@@ -87,7 +86,6 @@ static void wacom_wac_queue_flush(struct hid_device *hdev,// to flush seems reasonable enough, however. hid_warn (hdev,"%s: removed fifo entry with unexpected size\n" , __func__); -kfree (buf);continue ; } err =hid_report_raw_event (hdev,HID_INPUT_REPORT , buf, size, size, false);
@@ -95,8 +93,6 @@ static void wacom_wac_queue_flush(struct hid_device *hdev,hid_warn (hdev,"%s: unable to flush event due to error %d\n" , __func__, err); } - -kfree (buf); } }
@@ -311,7 +307,6 @@ static void wacom_feature_mapping(struct hid_device *hdev,struct wacom_features *features = &wacom->wacom_wac.features;struct hid_data *hid_data = &wacom->wacom_wac.hid_data;unsigned int equivalent_usage =wacom_equivalent_usage (usage->hid); -u8 *data;int ret;u32 n;
@@ -325,10 +320,11 @@ static void wacom_feature_mapping(struct hid_device *hdev,/* leave touch_max as is if predefined */ if (!features->touch_max) {/* read manually */ - n =hid_report_len (field->report); - data =hid_alloc_report_buf (field->report,GFP_KERNEL ); + u8 *data __free(kfree) = hid_alloc_report_buf(field->report, GFP_KERNEL); +if (!data)break ; + n =hid_report_len (field->report); data[0 ] = field->report->id; ret =wacom_get_report (hdev,HID_FEATURE_REPORT , data, n,WAC_CMD_RETRIES );
@@ -344,7 +340,6 @@ static void wacom_feature_mapping(struct hid_device *hdev,"defaulting to %d\n" , features->touch_max); } -kfree (data); }break ;case HID_DG_INPUTMODE :
@@ -386,10 +381,11 @@ static void wacom_feature_mapping(struct hid_device *hdev,case WACOM_HID_WD_OFFSETRIGHT :case WACOM_HID_WD_OFFSETBOTTOM :/* read manually */ - n =hid_report_len (field->report); - data =hid_alloc_report_buf (field->report,GFP_KERNEL ); + u8 *data __free(kfree) = hid_alloc_report_buf(field->report, GFP_KERNEL); +if (!data)break ; + n =hid_report_len (field->report); data[0 ] = field->report->id; ret =wacom_get_report (hdev,HID_FEATURE_REPORT , data, n,WAC_CMD_RETRIES );
@@ -400,7 +396,6 @@ static void wacom_feature_mapping(struct hid_device *hdev,hid_warn (hdev,"%s: could not retrieve sensor offsets\n" , __func__); } -kfree (data);break ; } }
@@ -581,7 +576,6 @@ static int wacom_hid_set_device_mode(struct hid_device *hdev)static int wacom_set_device_mode (struct hid_device *hdev,struct wacom_wac *wacom_wac) { -u8 *rep_data;struct hid_report *r;struct hid_report_enum *re;u32 length;
@@ -595,7 +589,7 @@ static int wacom_set_device_mode(struct hid_device *hdev,if (!r)return -EINVAL ; - rep_data =hid_alloc_report_buf (r,GFP_KERNEL ); + u8 *rep_data __free(kfree) = hid_alloc_report_buf(r, GFP_KERNEL);if (!rep_data)return -ENOMEM ;
@@ -614,8 +608,6 @@ static int wacom_set_device_mode(struct hid_device *hdev, rep_data[1 ] != wacom_wac->mode_report && limit++ < WAC_MSG_RETRIES); -kfree (rep_data); -return error <0 ? error :0 ; }
@@ -921,7 +913,6 @@ static int wacom_add_shared_data(struct hid_device *hdev)static int wacom_led_control (struct wacom *wacom) { -unsigned char *buf;int retval;unsigned char report_id =WAC_CMD_LED_CONTROL ;int buf_size =9 ;
@@ -940,7 +931,8 @@ static int wacom_led_control(struct wacom *wacom) report_id =WAC_CMD_WL_INTUOSP2 ; buf_size =51 ; } - buf =kzalloc (buf_size,GFP_KERNEL ); + + unsigned char *buf __free(kfree) = kzalloc(buf_size, GFP_KERNEL);if (!buf)return -ENOMEM ;
@@ -996,7 +988,6 @@ static int wacom_led_control(struct wacom *wacom) retval =wacom_set_report (wacom->hdev,HID_FEATURE_REPORT , buf, buf_size,WAC_CMD_RETRIES ); -kfree (buf);return retval; }
@@ -1004,11 +995,10 @@ static int wacom_led_control(struct wacom *wacom)static int wacom_led_putimage (struct wacom *wacom,int button_id,u8 xfer_id,const unsigned len,const void *img) { -unsigned char *buf;int i, retval;const unsigned chunk_len = len /4 ;/* 4 chunks are needed to be sent */ - buf =kzalloc (chunk_len +3 ,GFP_KERNEL ); + unsigned char *buf __free(kfree) = kzalloc(chunk_len + 3, GFP_KERNEL);if (!buf)return -ENOMEM ;
@@ -1018,7 +1008,7 @@ static int wacom_led_putimage(struct wacom *wacom, int button_id, u8 xfer_id, retval =wacom_set_report (wacom->hdev,HID_FEATURE_REPORT , buf,2 ,WAC_CMD_RETRIES );if (retval <0 ) - goto out; +return retval; buf[0 ] = xfer_id; buf[1 ] = button_id &0x07 ;
@@ -1038,8 +1028,6 @@ static int wacom_led_putimage(struct wacom *wacom, int button_id, u8 xfer_id,wacom_set_report (wacom->hdev,HID_FEATURE_REPORT , buf,2 ,WAC_CMD_RETRIES ); -out: -kfree (buf);return retval; }
@@ -1948,10 +1936,9 @@ static int wacom_remote_create_attr_group(struct wacom *wacom, __u32 serial,static int wacom_cmd_unpair_remote (struct wacom *wacom,unsigned char selector) {const size_t buf_size =2 ; -unsigned char *buf;int retval; - buf =kzalloc (buf_size,GFP_KERNEL ); + unsigned char *buf __free(kfree) = kzalloc(buf_size, GFP_KERNEL);if (!buf)return -ENOMEM ;
@@ -1960,7 +1947,6 @@ static int wacom_cmd_unpair_remote(struct wacom *wacom, unsigned char selector) retval =wacom_set_report (wacom->hdev,HID_OUTPUT_REPORT , buf, buf_size,WAC_CMD_RETRIES ); -kfree (buf);return retval; }
Signature · 1 line
2.53.0