* [linux-6.12.y 1/4] HID: core: Add printk_ratelimited variants to hid_warn() etc
@ 2026-06-01 8:36 Lee Jones
2026-06-01 8:36 ` [linux-6.12.y 2/4] HID: pass the buffer size to hid_report_raw_event Lee Jones
` (2 more replies)
0 siblings, 3 replies; 6+ messages in thread
From: Lee Jones @ 2026-06-01 8:36 UTC (permalink / raw)
To: lee, Jiri Kosina, Benjamin Tissoires, Filipe Laíns,
Bastien Nocera, Ping Cheng, Jason Gerecke, Viresh Kumar,
Johan Hovold, Alex Elder, Greg Kroah-Hartman, Sasha Levin,
Mario Limonciello (AMD), Kwok Kin Ming, linux-input, linux-kernel,
linux-usb, greybus-dev, linux-staging, bpf
Cc: stable, Vicki Pfau, Jiri Kosina
From: Vicki Pfau <vi@endrift.com>
hid_warn_ratelimited() is needed. Add the others as part of the block.
Signed-off-by: Vicki Pfau <vi@endrift.com>
Signed-off-by: Jiri Kosina <jkosina@suse.com>
(cherry picked from commit 1d64624243af8329b4b219d8c39e28ea448f9929)
Signed-off-by: Lee Jones <lee@kernel.org>
---
include/linux/hid.h | 11 +++++++++++
1 file changed, 11 insertions(+)
diff --git a/include/linux/hid.h b/include/linux/hid.h
index 7d8d09318fa9..bef017d6b440 100644
--- a/include/linux/hid.h
+++ b/include/linux/hid.h
@@ -1245,4 +1245,15 @@ void hid_quirks_exit(__u16 bus);
#define hid_dbg_once(hid, fmt, ...) \
dev_dbg_once(&(hid)->dev, fmt, ##__VA_ARGS__)
+#define hid_err_ratelimited(hid, fmt, ...) \
+ dev_err_ratelimited(&(hid)->dev, fmt, ##__VA_ARGS__)
+#define hid_notice_ratelimited(hid, fmt, ...) \
+ dev_notice_ratelimited(&(hid)->dev, fmt, ##__VA_ARGS__)
+#define hid_warn_ratelimited(hid, fmt, ...) \
+ dev_warn_ratelimited(&(hid)->dev, fmt, ##__VA_ARGS__)
+#define hid_info_ratelimited(hid, fmt, ...) \
+ dev_info_ratelimited(&(hid)->dev, fmt, ##__VA_ARGS__)
+#define hid_dbg_ratelimited(hid, fmt, ...) \
+ dev_dbg_ratelimited(&(hid)->dev, fmt, ##__VA_ARGS__)
+
#endif
--
2.54.0.823.g6e5bcc1fc9-goog
^ permalink raw reply related [flat|nested] 6+ messages in thread
* [linux-6.12.y 2/4] HID: pass the buffer size to hid_report_raw_event
2026-06-01 8:36 [linux-6.12.y 1/4] HID: core: Add printk_ratelimited variants to hid_warn() etc Lee Jones
@ 2026-06-01 8:36 ` Lee Jones
2026-06-01 8:50 ` sashiko-bot
2026-06-01 8:36 ` [linux-6.12.y 3/4] HID: core: introduce hid_safe_input_report() Lee Jones
2026-06-01 8:36 ` [linux-6.12.y 4/4] HID: core: Fix size_t specifier in hid_report_raw_event() Lee Jones
2 siblings, 1 reply; 6+ messages in thread
From: Lee Jones @ 2026-06-01 8:36 UTC (permalink / raw)
To: lee, Jiri Kosina, Benjamin Tissoires, Filipe Laíns,
Bastien Nocera, Ping Cheng, Jason Gerecke, Viresh Kumar,
Johan Hovold, Alex Elder, Greg Kroah-Hartman, Sasha Levin,
Kwok Kin Ming, Mario Limonciello (AMD), linux-input, linux-kernel,
linux-usb, greybus-dev, linux-staging, bpf
Cc: stable, Jiri Kosina
From: Benjamin Tissoires <bentiss@kernel.org>
[ Upstream commit 2c85c61d1332e1e16f020d76951baf167dcb6f7a ]
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.
Fixes: 0a3fe972a7cb ("HID: core: Mitigate potential OOB by removing bogus memset()")
Cc: stable@vger.kernel.org
Signed-off-by: Benjamin Tissoires <bentiss@kernel.org>
Acked-by: Johan Hovold <johan@kernel.org>
Reviewed-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Signed-off-by: Jiri Kosina <jkosina@suse.com>
Stable-dep-of: 206342541fc8 ("HID: core: introduce hid_safe_input_report()")
Signed-off-by: Sasha Levin <sashal@kernel.org>
(cherry picked from commit 509c2605065004fc4cd86ee50a9350d402785307)
[Lee: Backported to linux-6.12.y and beyond]
Signed-off-by: Lee Jones <lee@kernel.org>
---
drivers/hid/bpf/hid_bpf_dispatch.c | 6 +++--
drivers/hid/hid-core.c | 37 +++++++++++++++++++++---------
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, 51 insertions(+), 30 deletions(-)
diff --git a/drivers/hid/bpf/hid_bpf_dispatch.c b/drivers/hid/bpf/hid_bpf_dispatch.c
index 284861c166d9..b711d83dfde1 100644
--- a/drivers/hid/bpf/hid_bpf_dispatch.c
+++ b/drivers/hid/bpf/hid_bpf_dispatch.c
@@ -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);
@@ -514,7 +516,7 @@ __hid_bpf_input_report(struct hid_bpf_ctx *ctx, enum hid_report_type type, u8 *b
if (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);
}
diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c
index 294a25330ed0..ceff91722c3c 100644
--- a/drivers/hid/hid-core.c
+++ b/drivers/hid/hid-core.c
@@ -1998,24 +1998,32 @@ int __hid_request(struct hid_device *hid, struct hid_report *report,
}
EXPORT_SYMBOL_GPL(__hid_request);
-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)
{
struct hid_report_enum *report_enum = hid->report_enum + type;
struct hid_report *report;
struct hid_driver *hdrv;
int max_buffer_size = HID_MAX_BUFFER_SIZE;
u32 rsize, csize = size;
+ size_t bsize = bufsize;
u8 *cdata = data;
int ret = 0;
report = hid_get_report(report_enum, data);
if (!report)
- goto out;
+ return 0;
+
+ if (unlikely(bsize < csize)) {
+ hid_warn_ratelimited(hid, "Event data for report %d is incorrect (%d vs %ld)\n",
+ report->id, csize, bsize);
+ return -EINVAL;
+ }
if (report_enum->numbered) {
cdata++;
csize--;
+ bsize--;
}
rsize = hid_compute_report_size(report);
@@ -2028,9 +2036,15 @@ int hid_report_raw_event(struct hid_device *hid, enum hid_report_type type, u8 *
else if (rsize > max_buffer_size)
rsize = max_buffer_size;
+ if (bsize < rsize) {
+ hid_warn_ratelimited(hid, "Event data for report %d was too short (%d vs %ld)\n",
+ report->id, rsize, bsize);
+ return -EINVAL;
+ }
+
if (csize < rsize) {
dbg_hid("report %d is too short, (%d < %d)\n", report->id,
- csize, rsize);
+ csize, rsize);
memset(cdata + csize, 0, rsize - csize);
}
@@ -2039,7 +2053,7 @@ int hid_report_raw_event(struct hid_device *hid, enum hid_report_type type, u8 *
if (hid->claimed & HID_CLAIMED_HIDRAW) {
ret = hidraw_report_event(hid, data, size);
if (ret)
- goto out;
+ return ret;
}
if (hid->claimed != HID_CLAIMED_HIDRAW && report->maxfield) {
@@ -2051,15 +2065,15 @@ int hid_report_raw_event(struct hid_device *hid, enum hid_report_type type, u8 *
if (hid->claimed & HID_CLAIMED_INPUT)
hidinput_report_event(hid, report);
-out:
+
return ret;
}
EXPORT_SYMBOL_GPL(hid_report_raw_event);
static 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 hid_report_enum *report_enum;
struct hid_driver *hdrv;
@@ -2084,7 +2098,8 @@ static int __hid_input_report(struct hid_device *hid, enum hid_report_type type,
report_enum = hid->report_enum + type;
hdrv = hid->driver;
- data = dispatch_hid_bpf_device_event(hid, type, data, &size, interrupt, source, from_bpf);
+ data = dispatch_hid_bpf_device_event(hid, type, data, &bufsize, &size, interrupt,
+ source, from_bpf);
if (IS_ERR(data)) {
ret = PTR_ERR(data);
goto unlock;
@@ -2113,7 +2128,7 @@ static int __hid_input_report(struct hid_device *hid, enum hid_report_type type,
goto unlock;
}
- ret = hid_report_raw_event(hid, type, data, size, interrupt);
+ ret = hid_report_raw_event(hid, type, data, bufsize, size, interrupt);
unlock:
if (!lock_already_taken)
@@ -2135,7 +2150,7 @@ static int __hid_input_report(struct hid_device *hid, enum hid_report_type type,
int hid_input_report(struct hid_device *hid, enum hid_report_type type, u8 *data, u32 size,
int interrupt)
{
- return __hid_input_report(hid, type, data, size, interrupt, 0,
+ return __hid_input_report(hid, type, data, size, size, interrupt, 0,
false, /* from_bpf */
false /* lock_already_taken */);
}
diff --git a/drivers/hid/hid-gfrm.c b/drivers/hid/hid-gfrm.c
index 699186ff2349..d2a56bf92b41 100644
--- a/drivers/hid/hid-gfrm.c
+++ b/drivers/hid/hid-gfrm.c
@@ -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:
diff --git a/drivers/hid/hid-logitech-hidpp.c b/drivers/hid/hid-logitech-hidpp.c
index d60cd4379e86..858ac2ab46bd 100644
--- a/drivers/hid/hid-logitech-hidpp.c
+++ b/drivers/hid/hid-logitech-hidpp.c
@@ -3691,7 +3691,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;
}
diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c
index fcf9a806f109..760f9db44c9e 100644
--- a/drivers/hid/hid-multitouch.c
+++ b/drivers/hid/hid-multitouch.c
@@ -500,7 +500,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");
}
diff --git a/drivers/hid/hid-primax.c b/drivers/hid/hid-primax.c
index e44d79dff8de..8db054280afb 100644
--- a/drivers/hid/hid-primax.c
+++ b/drivers/hid/hid-primax.c
@@ -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 */
diff --git a/drivers/hid/hid-vivaldi-common.c b/drivers/hid/hid-vivaldi-common.c
index bf734055d4b6..b12bb5cc091a 100644
--- a/drivers/hid/hid-vivaldi-common.c
+++ b/drivers/hid/hid-vivaldi-common.c
@@ -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);
diff --git a/drivers/hid/wacom_sys.c b/drivers/hid/wacom_sys.c
index 1b1112772777..ffcf65dcf713 100644
--- a/drivers/hid/wacom_sys.c
+++ b/drivers/hid/wacom_sys.c
@@ -74,7 +74,7 @@ static void wacom_wac_queue_flush(struct hid_device *hdev,
int err;
size = kfifo_out(fifo, buf, sizeof(buf));
- 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);
@@ -319,7 +319,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 {
@@ -380,7 +380,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__);
diff --git a/drivers/staging/greybus/hid.c b/drivers/staging/greybus/hid.c
index 63c77a3df591..afa78c96ede8 100644
--- a/drivers/staging/greybus/hid.c
+++ b/drivers/staging/greybus/hid.c
@@ -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, ghid->bufsize, size, 1);
}
static void gb_hid_init_reports(struct gb_hid *ghid)
diff --git a/include/linux/hid.h b/include/linux/hid.h
index bef017d6b440..fdd401e4ebde 100644
--- a/include/linux/hid.h
+++ b/include/linux/hid.h
@@ -1213,8 +1213,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);
diff --git a/include/linux/hid_bpf.h b/include/linux/hid_bpf.h
index 6a47223e6460..aa87513acbcd 100644
--- a/include/linux/hid_bpf.h
+++ b/include/linux/hid_bpf.h
@@ -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);
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,
--
2.54.0.823.g6e5bcc1fc9-goog
^ permalink raw reply related [flat|nested] 6+ messages in thread
* [linux-6.12.y 3/4] HID: core: introduce hid_safe_input_report()
2026-06-01 8:36 [linux-6.12.y 1/4] HID: core: Add printk_ratelimited variants to hid_warn() etc Lee Jones
2026-06-01 8:36 ` [linux-6.12.y 2/4] HID: pass the buffer size to hid_report_raw_event Lee Jones
@ 2026-06-01 8:36 ` Lee Jones
2026-06-01 8:46 ` sashiko-bot
2026-06-01 8:36 ` [linux-6.12.y 4/4] HID: core: Fix size_t specifier in hid_report_raw_event() Lee Jones
2 siblings, 1 reply; 6+ messages in thread
From: Lee Jones @ 2026-06-01 8:36 UTC (permalink / raw)
To: lee, Jiri Kosina, Benjamin Tissoires, Filipe Laíns,
Bastien Nocera, Ping Cheng, Jason Gerecke, Viresh Kumar,
Johan Hovold, Alex Elder, Greg Kroah-Hartman, Sasha Levin,
Mario Limonciello (AMD), Kwok Kin Ming, linux-input, linux-kernel,
linux-usb, greybus-dev, linux-staging, bpf
Cc: stable, Jiri Kosina
From: Benjamin Tissoires <bentiss@kernel.org>
[ Upstream commit 206342541fc887ae919774a43942dc883161fece ]
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
- i2chid
This effectively revert to the old behavior for those two transport
layers.
Fixes: 0a3fe972a7cb ("HID: core: Mitigate potential OOB by removing bogus memset()")
Cc: stable@vger.kernel.org
Signed-off-by: Benjamin Tissoires <bentiss@kernel.org>
Signed-off-by: Jiri Kosina <jkosina@suse.com>
Signed-off-by: Sasha Levin <sashal@kernel.org>
(cherry picked from commit 301338b8edadc67a42b1c86add975091e66768d9)
Signed-off-by: Lee Jones <lee@kernel.org>
---
drivers/hid/hid-core.c | 25 +++++++++++++++++++++++++
drivers/hid/i2c-hid/i2c-hid-core.c | 7 ++++---
drivers/hid/usbhid/hid-core.c | 11 ++++++-----
include/linux/hid.h | 2 ++
4 files changed, 37 insertions(+), 8 deletions(-)
diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c
index ceff91722c3c..d9ea99cdb68e 100644
--- a/drivers/hid/hid-core.c
+++ b/drivers/hid/hid-core.c
@@ -2146,6 +2146,7 @@ static int __hid_input_report(struct hid_device *hid, enum hid_report_type type,
* @interrupt: distinguish between interrupt and control transfers
*
* This is data entry for lower layers.
+ * Legacy, please use hid_safe_input_report() instead.
*/
int hid_input_report(struct hid_device *hid, enum hid_report_type type, u8 *data, u32 size,
int interrupt)
@@ -2156,6 +2157,30 @@ 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.
+ * Please use this function instead of the non safe version because we provide
+ * here the size of the buffer, allowing hid-core to make smarter decisions
+ * regarding the incoming buffer.
+ */
+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)
{
diff --git a/drivers/hid/i2c-hid/i2c-hid-core.c b/drivers/hid/i2c-hid/i2c-hid-core.c
index cf8ae0df0cda..8ce0535fc42d 100644
--- a/drivers/hid/i2c-hid/i2c-hid-core.c
+++ b/drivers/hid/i2c-hid/i2c-hid-core.c
@@ -568,9 +568,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;
diff --git a/drivers/hid/usbhid/hid-core.c b/drivers/hid/usbhid/hid-core.c
index f14b46ce00cb..336ad7cf3d48 100644
--- a/drivers/hid/usbhid/hid-core.c
+++ b/drivers/hid/usbhid/hid-core.c
@@ -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;
diff --git a/include/linux/hid.h b/include/linux/hid.h
index fdd401e4ebde..7d05b1edacd8 100644
--- a/include/linux/hid.h
+++ b/include/linux/hid.h
@@ -949,6 +949,8 @@ struct hid_field *hid_find_field(struct hid_device *hdev, unsigned int report_ty
int 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);
--
2.54.0.823.g6e5bcc1fc9-goog
^ permalink raw reply related [flat|nested] 6+ messages in thread
* [linux-6.12.y 4/4] HID: core: Fix size_t specifier in hid_report_raw_event()
2026-06-01 8:36 [linux-6.12.y 1/4] HID: core: Add printk_ratelimited variants to hid_warn() etc Lee Jones
2026-06-01 8:36 ` [linux-6.12.y 2/4] HID: pass the buffer size to hid_report_raw_event Lee Jones
2026-06-01 8:36 ` [linux-6.12.y 3/4] HID: core: introduce hid_safe_input_report() Lee Jones
@ 2026-06-01 8:36 ` Lee Jones
2 siblings, 0 replies; 6+ messages in thread
From: Lee Jones @ 2026-06-01 8:36 UTC (permalink / raw)
To: lee, Jiri Kosina, Benjamin Tissoires, Filipe Laíns,
Bastien Nocera, Ping Cheng, Jason Gerecke, Viresh Kumar,
Johan Hovold, Alex Elder, Greg Kroah-Hartman, Sasha Levin,
Mario Limonciello (AMD), Kwok Kin Ming, linux-input, linux-kernel,
linux-usb, greybus-dev, linux-staging, bpf
Cc: stable, Nathan Chancellor, Miguel Ojeda, Linus Torvalds
From: Nathan Chancellor <nathan@kernel.org>
[ Upstream commit 4d3a2a466b8d68d852a1f3bbf11204b718428dc4 ]
When building for 32-bit platforms, for which 'size_t' is
'unsigned int', there are warnings around using the incorrect format
specifier to print bsize in hid_report_raw_event():
drivers/hid/hid-core.c:2054:29: error: format specifies type 'long' but the argument has type 'size_t' (aka 'unsigned int') [-Werror,-Wformat]
2053 | hid_warn_ratelimited(hid, "Event data for report %d is incorrect (%d vs %ld)\n",
| ~~~
| %zu
2054 | report->id, csize, bsize);
| ^~~~~
drivers/hid/hid-core.c:2076:29: error: format specifies type 'long' but the argument has type 'size_t' (aka 'unsigned int') [-Werror,-Wformat]
2075 | hid_warn_ratelimited(hid, "Event data for report %d was too short (%d vs %ld)\n",
| ~~~
| %zu
2076 | report->id, rsize, bsize);
| ^~~~~
Use the proper 'size_t' format specifier, '%zu', to clear up the
warnings.
Cc: stable@vger.kernel.org
Fixes: 2c85c61d1332 ("HID: pass the buffer size to hid_report_raw_event")
Reported-by: Miguel Ojeda <ojeda@kernel.org>
Closes: https://lore.kernel.org/20260516020430.110135-1-ojeda@kernel.org/
Signed-off-by: Nathan Chancellor <nathan@kernel.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: Sasha Levin <sashal@kernel.org>
(cherry picked from commit 3ab135238832446399614e7a4bb796d620717806)
Signed-off-by: Lee Jones <lee@kernel.org>
---
drivers/hid/hid-core.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c
index d9ea99cdb68e..87d990ada868 100644
--- a/drivers/hid/hid-core.c
+++ b/drivers/hid/hid-core.c
@@ -2015,7 +2015,7 @@ int hid_report_raw_event(struct hid_device *hid, enum hid_report_type type, u8 *
return 0;
if (unlikely(bsize < csize)) {
- hid_warn_ratelimited(hid, "Event data for report %d is incorrect (%d vs %ld)\n",
+ hid_warn_ratelimited(hid, "Event data for report %d is incorrect (%d vs %zu)\n",
report->id, csize, bsize);
return -EINVAL;
}
@@ -2037,7 +2037,7 @@ int hid_report_raw_event(struct hid_device *hid, enum hid_report_type type, u8 *
rsize = max_buffer_size;
if (bsize < rsize) {
- hid_warn_ratelimited(hid, "Event data for report %d was too short (%d vs %ld)\n",
+ hid_warn_ratelimited(hid, "Event data for report %d was too short (%d vs %zu)\n",
report->id, rsize, bsize);
return -EINVAL;
}
--
2.54.0.823.g6e5bcc1fc9-goog
^ permalink raw reply related [flat|nested] 6+ messages in thread
* Re: [linux-6.12.y 3/4] HID: core: introduce hid_safe_input_report()
2026-06-01 8:36 ` [linux-6.12.y 3/4] HID: core: introduce hid_safe_input_report() Lee Jones
@ 2026-06-01 8:46 ` sashiko-bot
0 siblings, 0 replies; 6+ messages in thread
From: sashiko-bot @ 2026-06-01 8:46 UTC (permalink / raw)
To: Lee Jones; +Cc: linux-input, bpf, dmitry.torokhov
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
New issues:
- [Low] The patch introduces a hard build failure by passing 9 arguments to a function expecting 8.
Pre-existing issues:
- [High] Integer underflow in `i2c_hid_get_input()` when computing the remaining buffer size for bogus HID descriptors.
--
commit d54718e72e548c203a2c42b46b69bf352c081a00
Author: Benjamin Tissoires <bentiss@kernel.org>
HID: core: introduce hid_safe_input_report()
This patch introduces hid_safe_input_report() to be used in transport
layers like usbhid and i2chid. This provides the buffer size allowing
the hid-core to make smarter decisions regarding incoming buffers.
> diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c
> index 294a25330ed03..2c5583818421e 100644
> --- a/drivers/hid/hid-core.c
> +++ b/drivers/hid/hid-core.c
[ ... ]
> @@ -2141,6 +2142,30 @@ 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.
> + * Please use this function instead of the non safe version because we provide
> + * here the size of the buffer, allowing hid-core to make smarter decisions
> + * regarding the incoming buffer.
> + */
> +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 */);
> +}
[Severity: Low]
Does this cause a build failure? Looking at the definition of
__hid_input_report() in this file, it only accepts 8 arguments:
static 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)
This call passes 9 arguments, including the new bufsize parameter. Were some
prerequisite upstream patches updating the signature of __hid_input_report()
omitted from this backport?
> +EXPORT_SYMBOL_GPL(hid_safe_input_report);
> +
> bool hid_match_one_id(const struct hid_device *hdev,
> const struct hid_device_id *id)
[ ... ]
> diff --git a/drivers/hid/i2c-hid/i2c-hid-core.c b/drivers/hid/i2c-hid/i2c-hid-core.c
> index cf8ae0df0cda9..8ce0535fc42d6 100644
> --- a/drivers/hid/i2c-hid/i2c-hid-core.c
> +++ b/drivers/hid/i2c-hid/i2c-hid-core.c
> @@ -568,9 +568,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);
[Severity: High]
This is a pre-existing issue, but can this calculation underflow and pass a
massive size to the HID core?
If a device has the I2C_HID_QUIRK_BAD_INPUT_SIZE quirk and provides a bogus
descriptor with wMaxInputLength < 2, the size variable will be < 2, and the
code earlier in i2c_hid_get_input() forces ret_size = size.
When ret_size and ihid->bufsize are less than sizeof(__le16) (which is 2),
subtracting sizeof(__le16) will result in an unsigned integer underflow.
This large underflowed value would then be passed into the HID core here and
could lead to out-of-bounds reads in functions like hid_dump_report().
> }
>
> return;
[ ... ]
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260601083642.908433-3-lee@kernel.org?part=1
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [linux-6.12.y 2/4] HID: pass the buffer size to hid_report_raw_event
2026-06-01 8:36 ` [linux-6.12.y 2/4] HID: pass the buffer size to hid_report_raw_event Lee Jones
@ 2026-06-01 8:50 ` sashiko-bot
0 siblings, 0 replies; 6+ messages in thread
From: sashiko-bot @ 2026-06-01 8:50 UTC (permalink / raw)
To: Lee Jones; +Cc: dmitry.torokhov, linux-input, bpf
Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:
New issues:
- [High] The patch fails to resolve the short-report regression for standard transport drivers because APIs do not propagate the real buffer size, bypassing the newly introduced padding logic.
- [Low] Format string mismatch for `size_t` variable.
Pre-existing issues:
- [Critical] Heap buffer overflow in `dispatch_hid_bpf_device_event` when processing incoming events.
- [High] A BPF program calling the `bpf_hid_input_report` helper causes unhandled re-entrancy that silently corrupts the shared per-device BPF data buffer.
--
commit e46292c21905a8e10fe2c446a47f211679b0d355
Author: Benjamin Tissoires <bentiss@kernel.org>
HID: pass the buffer size to hid_report_raw_event
This commit attempts to fix an OOB mitigation by passing both the buffer
capacity and the actual data size to hid_report_raw_event(). The goal is
to allow safe zero-padding of short reports if the backing buffer is large
enough, rather than unconditionally rejecting them.
[ Upstream commit 2c85c61d1332e1e16f020d76951baf167dcb6f7a ]
[Lee: Backported to linux-6.12.y and beyond]
> diff --git a/drivers/hid/bpf/hid_bpf_dispatch.c b/drivers/hid/bpf/hid_bpf_dispatch.c
> index 284861c166d9c..b711d83dfde1d 100644
> --- a/drivers/hid/bpf/hid_bpf_dispatch.c
> +++ b/drivers/hid/bpf/hid_bpf_dispatch.c
> @@ -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)
> {
[Severity: Critical]
This is a pre-existing issue, but does dispatch_hid_bpf_device_event() lack
bounds checking on the event size?
When a HID event arrives, the function unconditionally copies the data into the
BPF context buffer:
memset(ctx_kern.data, 0, hdev->bpf.allocated_data);
memcpy(ctx_kern.data, data, *size);
If a malicious or compromised HID device (or userspace via uhid) sends an
event where *size is larger than hdev->bpf.allocated_data, could this cause a
heap buffer overflow?
[Severity: High]
This is also a pre-existing issue, but does this code suffer from unhandled
re-entrancy corrupting shared state?
If a BPF program injects an input report by calling the bpf_hid_input_report()
helper, execution re-enters dispatch_hid_bpf_device_event(). The inner call
will unconditionally execute the memset() and memcpy() block shown above on
the exact same shared hdev->bpf.device_data buffer.
If the injected report uses ctx->data as its buffer, the memset wipes the data
before it can be copied. Will this silently corrupt the execution state of the
outer BPF program upon return?
[ ... ]
> diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c
> index 294a25330ed03..ceff91722c3c8 100644
> --- a/drivers/hid/hid-core.c
> +++ b/drivers/hid/hid-core.c
[ ... ]
> @@ -2028,9 +2036,15 @@ int hid_report_raw_event(struct hid_device *hid, enum hid_report_type type, u8 *
> else if (rsize > max_buffer_size)
> rsize = max_buffer_size;
>
> + if (bsize < rsize) {
> + hid_warn_ratelimited(hid, "Event data for report %d was too short (%d vs %ld)\n",
> + report->id, rsize, bsize);
[Severity: Low]
Can we use the %zu format specifier instead of %ld for bsize?
bsize is a size_t, which might trigger a compiler warning on 32-bit platforms
where size_t is unsigned int.
> + return -EINVAL;
> + }
> +
[ ... ]
> @@ -2135,7 +2150,7 @@ static int __hid_input_report(struct hid_device *hid, enum hid_report_type type,
> int hid_input_report(struct hid_device *hid, enum hid_report_type type, u8 *data, u32 size,
> int interrupt)
> {
> - return __hid_input_report(hid, type, data, size, interrupt, 0,
> + return __hid_input_report(hid, type, data, size, size, interrupt, 0,
[Severity: High]
Does this leave the short-report regression unfixed for standard transport
drivers?
By blindly passing the received data size as both the size and bufsize capacity
arguments to __hid_input_report(), bsize will always equal size.
If the report is short (size < rsize), the new bsize < rsize condition in
hid_report_raw_event() evaluates to true and the event is dropped, bypassing
the padding logic completely.
> false, /* from_bpf */
> false /* lock_already_taken */);
> }
[ ... ]
> diff --git a/drivers/hid/wacom_sys.c b/drivers/hid/wacom_sys.c
> index 1b1112772777c..ffcf65dcf7134 100644
> --- a/drivers/hid/wacom_sys.c
> +++ b/drivers/hid/wacom_sys.c
> @@ -74,7 +74,7 @@ static void wacom_wac_queue_flush(struct hid_device *hdev,
> int err;
>
> size = kfifo_out(fifo, buf, sizeof(buf));
> - 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);
[Severity: High]
Does passing size instead of sizeof(buf) for the buffer capacity prevent safe
padding for wacom devices?
Similar to the issue above, because bsize becomes equal to size, any short
report will fail the bsize < rsize check in hid_report_raw_event() and be
rejected instead of zeroes being safely padded to the end of the buffer.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260601083642.908433-2-lee@kernel.org?part=1
^ permalink raw reply [flat|nested] 6+ messages in thread
end of thread, other threads:[~2026-06-01 8:50 UTC | newest]
Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-06-01 8:36 [linux-6.12.y 1/4] HID: core: Add printk_ratelimited variants to hid_warn() etc Lee Jones
2026-06-01 8:36 ` [linux-6.12.y 2/4] HID: pass the buffer size to hid_report_raw_event Lee Jones
2026-06-01 8:50 ` sashiko-bot
2026-06-01 8:36 ` [linux-6.12.y 3/4] HID: core: introduce hid_safe_input_report() Lee Jones
2026-06-01 8:46 ` sashiko-bot
2026-06-01 8:36 ` [linux-6.12.y 4/4] HID: core: Fix size_t specifier in hid_report_raw_event() Lee Jones
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox