Merge tag 'hid-for-linus-2026031701' of git://git.kernel.org/pub/scm/linux/kernel/git/hid/hid

Pull HID fixes from Jiri Kosina:

 - various fixes dealing with (intentionally) broken devices in HID
   core, logitech-hidpp and multitouch drivers (Lee Jones)

 - fix for OOB in wacom driver (Benoît Sevens)

 - fix for potentialy HID-bpf-induced buffer overflow in () (Benjamin
   Tissoires)

 - various other small fixes and device ID / quirk additions

* tag 'hid-for-linus-2026031701' of git://git.kernel.org/pub/scm/linux/kernel/git/hid/hid:
  HID: multitouch: Check to ensure report responses match the request
  HID: logitech-hidpp: Prevent use-after-free on force feedback initialisation failure
  HID: bpf: prevent buffer overflow in hid_hw_request
  selftests/hid: fix compilation when bpf_wq and hid_device are not exported
  HID: core: Mitigate potential OOB by removing bogus memset()
  HID: intel-thc-hid: Set HID_PHYS with PCI BDF
  HID: appletb-kbd: add .resume method in PM
  HID: logitech-hidpp: Enable MX Master 4 over bluetooth
  HID: input: Add HID_BATTERY_QUIRK_DYNAMIC for Elan touchscreens
  HID: input: Drop Asus UX550* touchscreen ignore battery quirks
  HID: asus: add xg mobile 2022 external hardware support
  HID: wacom: fix out-of-bounds read in wacom_intuos_bt_irq
This commit is contained in:
Linus Torvalds
2026-03-17 13:55:51 -07:00
13 changed files with 61 additions and 15 deletions

View File

@@ -444,6 +444,8 @@ hid_bpf_hw_request(struct hid_bpf_ctx *ctx, __u8 *buf, size_t buf__sz,
(u64)(long)ctx, (u64)(long)ctx,
true); /* prevent infinite recursions */ true); /* prevent infinite recursions */
if (ret > size)
ret = size;
if (ret > 0) if (ret > 0)
memcpy(buf, dma_data, ret); memcpy(buf, dma_data, ret);

View File

@@ -476,7 +476,7 @@ static int appletb_kbd_suspend(struct hid_device *hdev, pm_message_t msg)
return 0; return 0;
} }
static int appletb_kbd_reset_resume(struct hid_device *hdev) static int appletb_kbd_resume(struct hid_device *hdev)
{ {
struct appletb_kbd *kbd = hid_get_drvdata(hdev); struct appletb_kbd *kbd = hid_get_drvdata(hdev);
@@ -500,7 +500,8 @@ static struct hid_driver appletb_kbd_hid_driver = {
.event = appletb_kbd_hid_event, .event = appletb_kbd_hid_event,
.input_configured = appletb_kbd_input_configured, .input_configured = appletb_kbd_input_configured,
.suspend = pm_ptr(appletb_kbd_suspend), .suspend = pm_ptr(appletb_kbd_suspend),
.reset_resume = pm_ptr(appletb_kbd_reset_resume), .resume = pm_ptr(appletb_kbd_resume),
.reset_resume = pm_ptr(appletb_kbd_resume),
.driver.dev_groups = appletb_kbd_groups, .driver.dev_groups = appletb_kbd_groups,
}; };
module_hid_driver(appletb_kbd_hid_driver); module_hid_driver(appletb_kbd_hid_driver);

View File

@@ -1497,6 +1497,9 @@ static const struct hid_device_id asus_devices[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK, { HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK,
USB_DEVICE_ID_ASUSTEK_ROG_NKEY_ALLY_X), USB_DEVICE_ID_ASUSTEK_ROG_NKEY_ALLY_X),
QUIRK_USE_KBD_BACKLIGHT | QUIRK_ROG_NKEY_KEYBOARD | QUIRK_ROG_ALLY_XPAD }, QUIRK_USE_KBD_BACKLIGHT | QUIRK_ROG_NKEY_KEYBOARD | QUIRK_ROG_ALLY_XPAD },
{ HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK,
USB_DEVICE_ID_ASUSTEK_XGM_2022),
},
{ HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK, { HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK,
USB_DEVICE_ID_ASUSTEK_XGM_2023), USB_DEVICE_ID_ASUSTEK_XGM_2023),
}, },

View File

@@ -2057,9 +2057,10 @@ int hid_report_raw_event(struct hid_device *hid, enum hid_report_type type, u8 *
rsize = max_buffer_size; rsize = max_buffer_size;
if (csize < rsize) { if (csize < rsize) {
dbg_hid("report %d is too short, (%d < %d)\n", report->id, hid_warn_ratelimited(hid, "Event data for report %d was too short (%d vs %d)\n",
csize, rsize); report->id, rsize, csize);
memset(cdata + csize, 0, rsize - csize); ret = -EINVAL;
goto out;
} }
if ((hid->claimed & HID_CLAIMED_HIDDEV) && hid->hiddev_report_event) if ((hid->claimed & HID_CLAIMED_HIDDEV) && hid->hiddev_report_event)

View File

@@ -229,6 +229,7 @@
#define USB_DEVICE_ID_ASUSTEK_ROG_NKEY_ALLY_X 0x1b4c #define USB_DEVICE_ID_ASUSTEK_ROG_NKEY_ALLY_X 0x1b4c
#define USB_DEVICE_ID_ASUSTEK_ROG_CLAYMORE_II_KEYBOARD 0x196b #define USB_DEVICE_ID_ASUSTEK_ROG_CLAYMORE_II_KEYBOARD 0x196b
#define USB_DEVICE_ID_ASUSTEK_FX503VD_KEYBOARD 0x1869 #define USB_DEVICE_ID_ASUSTEK_FX503VD_KEYBOARD 0x1869
#define USB_DEVICE_ID_ASUSTEK_XGM_2022 0x1970
#define USB_DEVICE_ID_ASUSTEK_XGM_2023 0x1a9a #define USB_DEVICE_ID_ASUSTEK_XGM_2023 0x1a9a
#define USB_VENDOR_ID_ATEN 0x0557 #define USB_VENDOR_ID_ATEN 0x0557
@@ -454,8 +455,6 @@
#define USB_DEVICE_ID_TOSHIBA_CLICK_L9W 0x0401 #define USB_DEVICE_ID_TOSHIBA_CLICK_L9W 0x0401
#define USB_DEVICE_ID_HP_X2 0x074d #define USB_DEVICE_ID_HP_X2 0x074d
#define USB_DEVICE_ID_HP_X2_10_COVER 0x0755 #define USB_DEVICE_ID_HP_X2_10_COVER 0x0755
#define USB_DEVICE_ID_ASUS_UX550VE_TOUCHSCREEN 0x2544
#define USB_DEVICE_ID_ASUS_UX550_TOUCHSCREEN 0x2706
#define I2C_DEVICE_ID_CHROMEBOOK_TROGDOR_POMPOM 0x2F81 #define I2C_DEVICE_ID_CHROMEBOOK_TROGDOR_POMPOM 0x2F81
#define USB_VENDOR_ID_ELECOM 0x056e #define USB_VENDOR_ID_ELECOM 0x056e

View File

@@ -354,6 +354,7 @@ static enum power_supply_property hidinput_battery_props[] = {
#define HID_BATTERY_QUIRK_FEATURE (1 << 1) /* ask for feature report */ #define HID_BATTERY_QUIRK_FEATURE (1 << 1) /* ask for feature report */
#define HID_BATTERY_QUIRK_IGNORE (1 << 2) /* completely ignore the battery */ #define HID_BATTERY_QUIRK_IGNORE (1 << 2) /* completely ignore the battery */
#define HID_BATTERY_QUIRK_AVOID_QUERY (1 << 3) /* do not query the battery */ #define HID_BATTERY_QUIRK_AVOID_QUERY (1 << 3) /* do not query the battery */
#define HID_BATTERY_QUIRK_DYNAMIC (1 << 4) /* report present only after life signs */
static const struct hid_device_id hid_battery_quirks[] = { static const struct hid_device_id hid_battery_quirks[] = {
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE,
@@ -386,10 +387,6 @@ static const struct hid_device_id hid_battery_quirks[] = {
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH,
USB_DEVICE_ID_LOGITECH_DINOVO_EDGE_KBD), USB_DEVICE_ID_LOGITECH_DINOVO_EDGE_KBD),
HID_BATTERY_QUIRK_IGNORE }, HID_BATTERY_QUIRK_IGNORE },
{ HID_USB_DEVICE(USB_VENDOR_ID_ELAN, USB_DEVICE_ID_ASUS_UX550_TOUCHSCREEN),
HID_BATTERY_QUIRK_IGNORE },
{ HID_USB_DEVICE(USB_VENDOR_ID_ELAN, USB_DEVICE_ID_ASUS_UX550VE_TOUCHSCREEN),
HID_BATTERY_QUIRK_IGNORE },
{ HID_USB_DEVICE(USB_VENDOR_ID_UGEE, USB_DEVICE_ID_UGEE_XPPEN_TABLET_DECO_L), { HID_USB_DEVICE(USB_VENDOR_ID_UGEE, USB_DEVICE_ID_UGEE_XPPEN_TABLET_DECO_L),
HID_BATTERY_QUIRK_AVOID_QUERY }, HID_BATTERY_QUIRK_AVOID_QUERY },
{ HID_USB_DEVICE(USB_VENDOR_ID_UGEE, USB_DEVICE_ID_UGEE_XPPEN_TABLET_DECO_PRO_MW), { HID_USB_DEVICE(USB_VENDOR_ID_UGEE, USB_DEVICE_ID_UGEE_XPPEN_TABLET_DECO_PRO_MW),
@@ -402,8 +399,8 @@ static const struct hid_device_id hid_battery_quirks[] = {
* Elan HID touchscreens seem to all report a non present battery, * Elan HID touchscreens seem to all report a non present battery,
* set HID_BATTERY_QUIRK_IGNORE for all Elan I2C and USB HID devices. * set HID_BATTERY_QUIRK_IGNORE for all Elan I2C and USB HID devices.
*/ */
{ HID_I2C_DEVICE(USB_VENDOR_ID_ELAN, HID_ANY_ID), HID_BATTERY_QUIRK_IGNORE }, { HID_I2C_DEVICE(USB_VENDOR_ID_ELAN, HID_ANY_ID), HID_BATTERY_QUIRK_DYNAMIC },
{ HID_USB_DEVICE(USB_VENDOR_ID_ELAN, HID_ANY_ID), HID_BATTERY_QUIRK_IGNORE }, { HID_USB_DEVICE(USB_VENDOR_ID_ELAN, HID_ANY_ID), HID_BATTERY_QUIRK_DYNAMIC },
{} {}
}; };
@@ -460,11 +457,14 @@ static int hidinput_get_battery_property(struct power_supply *psy,
int ret = 0; int ret = 0;
switch (prop) { switch (prop) {
case POWER_SUPPLY_PROP_PRESENT:
case POWER_SUPPLY_PROP_ONLINE: case POWER_SUPPLY_PROP_ONLINE:
val->intval = 1; val->intval = 1;
break; break;
case POWER_SUPPLY_PROP_PRESENT:
val->intval = dev->battery_present;
break;
case POWER_SUPPLY_PROP_CAPACITY: case POWER_SUPPLY_PROP_CAPACITY:
if (dev->battery_status != HID_BATTERY_REPORTED && if (dev->battery_status != HID_BATTERY_REPORTED &&
!dev->battery_avoid_query) { !dev->battery_avoid_query) {
@@ -577,6 +577,8 @@ static int hidinput_setup_battery(struct hid_device *dev, unsigned report_type,
if (quirks & HID_BATTERY_QUIRK_AVOID_QUERY) if (quirks & HID_BATTERY_QUIRK_AVOID_QUERY)
dev->battery_avoid_query = true; dev->battery_avoid_query = true;
dev->battery_present = (quirks & HID_BATTERY_QUIRK_DYNAMIC) ? false : true;
dev->battery = power_supply_register(&dev->dev, psy_desc, &psy_cfg); dev->battery = power_supply_register(&dev->dev, psy_desc, &psy_cfg);
if (IS_ERR(dev->battery)) { if (IS_ERR(dev->battery)) {
error = PTR_ERR(dev->battery); error = PTR_ERR(dev->battery);
@@ -632,6 +634,7 @@ static void hidinput_update_battery(struct hid_device *dev, unsigned int usage,
return; return;
if (hidinput_update_battery_charge_status(dev, usage, value)) { if (hidinput_update_battery_charge_status(dev, usage, value)) {
dev->battery_present = true;
power_supply_changed(dev->battery); power_supply_changed(dev->battery);
return; return;
} }
@@ -647,6 +650,7 @@ static void hidinput_update_battery(struct hid_device *dev, unsigned int usage,
if (dev->battery_status != HID_BATTERY_REPORTED || if (dev->battery_status != HID_BATTERY_REPORTED ||
capacity != dev->battery_capacity || capacity != dev->battery_capacity ||
ktime_after(ktime_get_coarse(), dev->battery_ratelimit_time)) { ktime_after(ktime_get_coarse(), dev->battery_ratelimit_time)) {
dev->battery_present = true;
dev->battery_capacity = capacity; dev->battery_capacity = capacity;
dev->battery_status = HID_BATTERY_REPORTED; dev->battery_status = HID_BATTERY_REPORTED;
dev->battery_ratelimit_time = dev->battery_ratelimit_time =

View File

@@ -4487,10 +4487,12 @@ static int hidpp_probe(struct hid_device *hdev, const struct hid_device_id *id)
if (!ret) if (!ret)
ret = hidpp_ff_init(hidpp, &data); ret = hidpp_ff_init(hidpp, &data);
if (ret) if (ret) {
hid_warn(hidpp->hid_dev, hid_warn(hidpp->hid_dev,
"Unable to initialize force feedback support, errno %d\n", "Unable to initialize force feedback support, errno %d\n",
ret); ret);
ret = 0;
}
} }
/* /*
@@ -4668,6 +4670,8 @@ static const struct hid_device_id hidpp_devices[] = {
HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, 0xb038) }, HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, 0xb038) },
{ /* Slim Solar+ K980 Keyboard over Bluetooth */ { /* Slim Solar+ K980 Keyboard over Bluetooth */
HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, 0xb391) }, HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, 0xb391) },
{ /* MX Master 4 mouse over Bluetooth */
HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, 0xb042) },
{} {}
}; };

View File

@@ -526,12 +526,19 @@ static void mt_get_feature(struct hid_device *hdev, struct hid_report *report)
dev_warn(&hdev->dev, "failed to fetch feature %d\n", dev_warn(&hdev->dev, "failed to fetch feature %d\n",
report->id); report->id);
} else { } else {
/* 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;
}
ret = hid_report_raw_event(hdev, HID_FEATURE_REPORT, buf, ret = hid_report_raw_event(hdev, HID_FEATURE_REPORT, buf,
size, 0); size, 0);
if (ret) if (ret)
dev_warn(&hdev->dev, "failed to report feature\n"); dev_warn(&hdev->dev, "failed to report feature\n");
} }
free:
kfree(buf); kfree(buf);
} }

View File

@@ -127,6 +127,7 @@ int quicki2c_hid_probe(struct quicki2c_device *qcdev)
hid->product = le16_to_cpu(qcdev->dev_desc.product_id); hid->product = le16_to_cpu(qcdev->dev_desc.product_id);
snprintf(hid->name, sizeof(hid->name), "%s %04X:%04X", "quicki2c-hid", snprintf(hid->name, sizeof(hid->name), "%s %04X:%04X", "quicki2c-hid",
hid->vendor, hid->product); hid->vendor, hid->product);
strscpy(hid->phys, dev_name(qcdev->dev), sizeof(hid->phys));
ret = hid_add_device(hid); ret = hid_add_device(hid);
if (ret) { if (ret) {

View File

@@ -118,6 +118,7 @@ int quickspi_hid_probe(struct quickspi_device *qsdev)
hid->product = le16_to_cpu(qsdev->dev_desc.product_id); hid->product = le16_to_cpu(qsdev->dev_desc.product_id);
snprintf(hid->name, sizeof(hid->name), "%s %04X:%04X", "quickspi-hid", snprintf(hid->name, sizeof(hid->name), "%s %04X:%04X", "quickspi-hid",
hid->vendor, hid->product); hid->vendor, hid->product);
strscpy(hid->phys, dev_name(qsdev->dev), sizeof(hid->phys));
ret = hid_add_device(hid); ret = hid_add_device(hid);
if (ret) { if (ret) {

View File

@@ -1208,10 +1208,20 @@ static int wacom_intuos_bt_irq(struct wacom_wac *wacom, size_t len)
switch (data[0]) { switch (data[0]) {
case 0x04: case 0x04:
if (len < 32) {
dev_warn(wacom->pen_input->dev.parent,
"Report 0x04 too short: %zu bytes\n", len);
break;
}
wacom_intuos_bt_process_data(wacom, data + i); wacom_intuos_bt_process_data(wacom, data + i);
i += 10; i += 10;
fallthrough; fallthrough;
case 0x03: case 0x03:
if (i == 1 && len < 22) {
dev_warn(wacom->pen_input->dev.parent,
"Report 0x03 too short: %zu bytes\n", len);
break;
}
wacom_intuos_bt_process_data(wacom, data + i); wacom_intuos_bt_process_data(wacom, data + i);
i += 10; i += 10;
wacom_intuos_bt_process_data(wacom, data + i); wacom_intuos_bt_process_data(wacom, data + i);

View File

@@ -682,6 +682,7 @@ struct hid_device {
__s32 battery_charge_status; __s32 battery_charge_status;
enum hid_battery_status battery_status; enum hid_battery_status battery_status;
bool battery_avoid_query; bool battery_avoid_query;
bool battery_present;
ktime_t battery_ratelimit_time; ktime_t battery_ratelimit_time;
#endif #endif

View File

@@ -6,8 +6,10 @@
#define __HID_BPF_HELPERS_H #define __HID_BPF_HELPERS_H
/* "undefine" structs and enums in vmlinux.h, because we "override" them below */ /* "undefine" structs and enums in vmlinux.h, because we "override" them below */
#define bpf_wq bpf_wq___not_used
#define hid_bpf_ctx hid_bpf_ctx___not_used #define hid_bpf_ctx hid_bpf_ctx___not_used
#define hid_bpf_ops hid_bpf_ops___not_used #define hid_bpf_ops hid_bpf_ops___not_used
#define hid_device hid_device___not_used
#define hid_report_type hid_report_type___not_used #define hid_report_type hid_report_type___not_used
#define hid_class_request hid_class_request___not_used #define hid_class_request hid_class_request___not_used
#define hid_bpf_attach_flags hid_bpf_attach_flags___not_used #define hid_bpf_attach_flags hid_bpf_attach_flags___not_used
@@ -27,8 +29,10 @@
#include "vmlinux.h" #include "vmlinux.h"
#undef bpf_wq
#undef hid_bpf_ctx #undef hid_bpf_ctx
#undef hid_bpf_ops #undef hid_bpf_ops
#undef hid_device
#undef hid_report_type #undef hid_report_type
#undef hid_class_request #undef hid_class_request
#undef hid_bpf_attach_flags #undef hid_bpf_attach_flags
@@ -55,6 +59,14 @@ enum hid_report_type {
HID_REPORT_TYPES, HID_REPORT_TYPES,
}; };
struct hid_device {
unsigned int id;
} __attribute__((preserve_access_index));
struct bpf_wq {
__u64 __opaque[2];
};
struct hid_bpf_ctx { struct hid_bpf_ctx {
struct hid_device *hid; struct hid_device *hid;
__u32 allocated_size; __u32 allocated_size;