From d3db1ce9bf8f4a8e03d3bd19000b32290bca7d74 Mon Sep 17 00:00:00 2001 From: Zihuan Zhang Date: Fri, 9 May 2025 10:25:54 +0800 Subject: [PATCH 1/5] HID: debug: Use the __set_current_state() When detecting an invalid list->hdev, the process needs to manually set its state to TASK_RUNNING and exit. In the original code, set_current_state() (which includes a memory barrier) is used here, but it is immediately followed by a mutex_unlock() call. Since mutex_unlock() internally includes a memory barrier, this ensures that all modifications within the critical section (including the process state) are visible to other CPUs. Therefore, replacing it with __set_current_state() (without an implicit barrier) avoids redundant memory barriers. Signed-off-by: Zihuan Zhang Signed-off-by: Jiri Kosina --- drivers/hid/hid-debug.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/hid/hid-debug.c b/drivers/hid/hid-debug.c index 8433306148d5..00a73983ef19 100644 --- a/drivers/hid/hid-debug.c +++ b/drivers/hid/hid-debug.c @@ -3726,7 +3726,7 @@ static ssize_t hid_debug_events_read(struct file *file, char __user *buffer, */ if (!list->hdev || !list->hdev->debug) { ret = -EIO; - set_current_state(TASK_RUNNING); + __set_current_state(TASK_RUNNING); goto out; } From 37a9acb971c2f338e7a1b602b0ee40ad70668e81 Mon Sep 17 00:00:00 2001 From: Chelsy Ratnawat Date: Thu, 19 Jun 2025 08:46:27 -0700 Subject: [PATCH 2/5] HID: replace scnprintf() with sysfs_emit() Documentation/filesystems/sysfs.rst mentions that show() should only use sysfs_emit() or sysfs_emit_at() when formating the value to be returned to user space. So replace scnprintf() with sysfs_emit(). Signed-off-by: Chelsy Ratnawat Signed-off-by: Jiri Kosina --- drivers/hid/hid-core.c | 2 +- drivers/hid/hid-lg4ff.c | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index b348d0464314..4a00bd4a4224 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -2797,7 +2797,7 @@ static ssize_t modalias_show(struct device *dev, struct device_attribute *a, { struct hid_device *hdev = container_of(dev, struct hid_device, dev); - return scnprintf(buf, PAGE_SIZE, "hid:b%04Xg%04Xv%08Xp%08X\n", + return sysfs_emit(buf, "hid:b%04Xg%04Xv%08Xp%08X\n", hdev->bus, hdev->group, hdev->vendor, hdev->product); } static DEVICE_ATTR_RO(modalias); diff --git a/drivers/hid/hid-lg4ff.c b/drivers/hid/hid-lg4ff.c index 445623dd1bd6..32b711723f2a 100644 --- a/drivers/hid/hid-lg4ff.c +++ b/drivers/hid/hid-lg4ff.c @@ -956,7 +956,7 @@ static ssize_t lg4ff_combine_show(struct device *dev, struct device_attribute *a return 0; } - count = scnprintf(buf, PAGE_SIZE, "%u\n", entry->wdata.combine); + count = sysfs_emit(buf, "%u\n", entry->wdata.combine); return count; } @@ -1009,7 +1009,7 @@ static ssize_t lg4ff_range_show(struct device *dev, struct device_attribute *att return 0; } - count = scnprintf(buf, PAGE_SIZE, "%u\n", entry->wdata.range); + count = sysfs_emit(buf, "%u\n", entry->wdata.range); return count; } @@ -1073,7 +1073,7 @@ static ssize_t lg4ff_real_id_show(struct device *dev, struct device_attribute *a return 0; } - count = scnprintf(buf, PAGE_SIZE, "%s: %s\n", entry->wdata.real_tag, entry->wdata.real_name); + count = sysfs_emit(buf, "%s: %s\n", entry->wdata.real_tag, entry->wdata.real_name); return count; } From 4051ead99888f101be92c7ce90d2de09aac6fd1c Mon Sep 17 00:00:00 2001 From: Li Chen Date: Fri, 20 Jun 2025 20:02:31 +0800 Subject: [PATCH 3/5] HID: rate-limit hid_warn to prevent log flooding Syzkaller can create many uhid devices that trigger repeated warnings like: "hid-generic xxxx: unknown main item tag 0x0" These messages can flood the system log, especially if a crash occurs (e.g., with a slow UART console, leading to soft lockups). To mitigate this, convert `hid_warn()` to use `dev_warn_ratelimited()`. This helps reduce log noise and improves system stability under fuzzing or faulty device scenarios. Signed-off-by: Li Chen Signed-off-by: Jiri Kosina --- drivers/hid/hid-core.c | 4 ++-- include/linux/hid.h | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 4a00bd4a4224..ef1f79951d9b 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -659,9 +659,9 @@ static int hid_parser_main(struct hid_parser *parser, struct hid_item *item) default: if (item->tag >= HID_MAIN_ITEM_TAG_RESERVED_MIN && item->tag <= HID_MAIN_ITEM_TAG_RESERVED_MAX) - hid_warn(parser->device, "reserved main item tag 0x%x\n", item->tag); + hid_warn_ratelimited(parser->device, "reserved main item tag 0x%x\n", item->tag); else - hid_warn(parser->device, "unknown main item tag 0x%x\n", item->tag); + hid_warn_ratelimited(parser->device, "unknown main item tag 0x%x\n", item->tag); ret = 0; } diff --git a/include/linux/hid.h b/include/linux/hid.h index 568a9d8c749b..7f260e0e2049 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -1239,6 +1239,8 @@ void hid_quirks_exit(__u16 bus); dev_notice(&(hid)->dev, fmt, ##__VA_ARGS__) #define hid_warn(hid, fmt, ...) \ dev_warn(&(hid)->dev, fmt, ##__VA_ARGS__) +#define hid_warn_ratelimited(hid, fmt, ...) \ + dev_warn_ratelimited(&(hid)->dev, fmt, ##__VA_ARGS__) #define hid_info(hid, fmt, ...) \ dev_info(&(hid)->dev, fmt, ##__VA_ARGS__) #define hid_dbg(hid, fmt, ...) \ From 12f33ef6c2aaa410b7ccf039289fe2b04ab2252f Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Sun, 13 Jul 2025 11:36:12 -0400 Subject: [PATCH 4/5] HID: core: Improve the kerneldoc for hid_report_len() The kerneldoc for hid_report_len() needs to be improved. The description of the @report argument is ungrammatical, and the documentation does not explain under what circumstances the report length will include the byte reserved for the report ID. Let's fix up the kerneldoc. Signed-off-by: Alan Stern Link: https://patch.msgid.link/1c8416cb-7347-4a06-b00a-20518069d263@rowland.harvard.edu Signed-off-by: Benjamin Tissoires --- include/linux/hid.h | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/include/linux/hid.h b/include/linux/hid.h index 7f260e0e2049..2cc4f1e4ea96 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -1216,7 +1216,11 @@ static inline void hid_hw_wait(struct hid_device *hdev) /** * hid_report_len - calculate the report length * - * @report: the report we want to know the length + * @report: the report whose length we want to know + * + * The length counts the report ID byte, but only if the ID is nonzero + * and therefore is included in the report. Reports whose ID is zero + * never include an ID byte. */ static inline u32 hid_report_len(struct hid_report *report) { From a6b87bfc2ab5bccb7ad953693c85d9062aef3fdd Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Wed, 23 Jul 2025 10:37:04 -0400 Subject: [PATCH 5/5] HID: core: Harden s32ton() against conversion to 0 bits Testing by the syzbot fuzzer showed that the HID core gets a shift-out-of-bounds exception when it tries to convert a 32-bit quantity to a 0-bit quantity. Ideally this should never occur, but there are buggy devices and some might have a report field with size set to zero; we shouldn't reject the report or the device just because of that. Instead, harden the s32ton() routine so that it returns a reasonable result instead of crashing when it is called with the number of bits set to 0 -- the same as what snto32() does. Signed-off-by: Alan Stern Reported-by: syzbot+b63d677d63bcac06cf90@syzkaller.appspotmail.com Closes: https://lore.kernel.org/linux-usb/68753a08.050a0220.33d347.0008.GAE@google.com/ Tested-by: syzbot+b63d677d63bcac06cf90@syzkaller.appspotmail.com Fixes: dde5845a529f ("[PATCH] Generic HID layer - code split") Cc: stable@vger.kernel.org Link: https://patch.msgid.link/613a66cd-4309-4bce-a4f7-2905f9bce0c9@rowland.harvard.edu Signed-off-by: Benjamin Tissoires --- drivers/hid/hid-core.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index ef1f79951d9b..f7d4efcae603 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -66,8 +66,12 @@ static s32 snto32(__u32 value, unsigned int n) static u32 s32ton(__s32 value, unsigned int n) { - s32 a = value >> (n - 1); + s32 a; + if (!value || !n) + return 0; + + a = value >> (n - 1); if (a && a != -1) return value < 0 ? 1 << (n - 1) : (1 << (n - 1)) - 1; return value & ((1 << n) - 1);