Commit 70eda686 authored by Linus Torvalds's avatar Linus Torvalds
Browse files

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

Pull HID fixes from Jiri Kosina:

 - fixes for a few OOB/UAF in several HID drivers (Florian Pradines, Lee
   Jones, Michael Zaidman, Rosalie Wanders, Sangyun Kim and Tomasz
   Pakuła)

 - more general sanitation of input data, dealing with potentially
   malicious hardware in hid-core (Benjamin Tissoires)

 - a few device-specific quirks and fixups

* tag 'hid-for-linus-2026051401' of git://git.kernel.org/pub/scm/linux/kernel/git/hid/hid: (22 commits)
  HID: logitech-hidpp: Add support for newer Bluetooth keyboards
  HID: pidff: Fix integer overflow in pidff_rescale
  HID: i2c-hid: add reset quirk for BLTP7853 touchpad
  HID: core: introduce hid_safe_input_report()
  HID: pass the buffer size to hid_report_raw_event
  HID: google: hammer: stop hardware on devres action failure
  HID: appletb-kbd: run inactivity autodim from workqueues
  HID: appletb-kbd: fix UAF in inactivity-timer cleanup path
  HID: playstation: Clamp num_touch_reports
  HID: magicmouse: Prevent out-of-bounds (OOB) read during DOUBLE_REPORT_ID
  HID: mcp2221: fix OOB write in mcp2221_raw_event()
  HID: quirks: really enable the intended work around for appledisplay
  HID: hid-sjoy: race between init and usage
  HID: uclogic: Fix regression of input name assignment
  HID: intel-thc-hid: Intel-quickspi: Fix some error codes
  HID: hid-lenovo-go-s: restore OS_TYPE after resume from s2idle
  HID: elan: Add support for ELAN SB974D touchpad
  HID: sony: add missing size validation for Rock Band 3 Pro instruments
  HID: sony: add missing size validation for SMK-Link remotes
  HID: sony: remove unneeded WARN_ON() in sony_leds_init()
  ...
parents 48f76a12 64ffa2e5
Loading
Loading
Loading
Loading
+4 −2
Original line number Diff line number Diff line
@@ -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 *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);
}

+36 −20
Original line number Diff line number Diff line
@@ -17,7 +17,7 @@
#include <linux/module.h>
#include <linux/string.h>
#include <linux/backlight.h>
#include <linux/timer.h>
#include <linux/workqueue.h>
#include <linux/input/sparse-keymap.h>

#include "hid-ids.h"
@@ -62,7 +62,8 @@ struct appletb_kbd {
	struct input_handle kbd_handle;
	struct input_handle tpd_handle;
	struct backlight_device *backlight_dev;
	struct timer_list inactivity_timer;
	struct delayed_work inactivity_work;
	struct work_struct restore_brightness_work;
	bool has_dimmed;
	bool has_turned_off;
	u8 saved_mode;
@@ -164,16 +165,18 @@ static int appletb_tb_key_to_slot(unsigned int code)
	}
}

static void appletb_inactivity_timer(struct timer_list *t)
static void appletb_inactivity_work(struct work_struct *work)
{
	struct appletb_kbd *kbd = timer_container_of(kbd, t, inactivity_timer);
	struct appletb_kbd *kbd = container_of(to_delayed_work(work),
					       struct appletb_kbd,
					       inactivity_work);

	if (kbd->backlight_dev && appletb_tb_autodim) {
		if (!kbd->has_dimmed) {
			backlight_device_set_brightness(kbd->backlight_dev, 1);
			kbd->has_dimmed = true;
			mod_timer(&kbd->inactivity_timer,
				jiffies + secs_to_jiffies(appletb_tb_idle_timeout));
			mod_delayed_work(system_wq, &kbd->inactivity_work,
					 secs_to_jiffies(appletb_tb_idle_timeout));
		} else if (!kbd->has_turned_off) {
			backlight_device_set_brightness(kbd->backlight_dev, 0);
			kbd->has_turned_off = true;
@@ -181,16 +184,25 @@ static void appletb_inactivity_timer(struct timer_list *t)
	}
}

static void appletb_restore_brightness_work(struct work_struct *work)
{
	struct appletb_kbd *kbd = container_of(work, struct appletb_kbd,
					       restore_brightness_work);

	if (kbd->backlight_dev)
		backlight_device_set_brightness(kbd->backlight_dev, 2);
}

static void reset_inactivity_timer(struct appletb_kbd *kbd)
{
	if (kbd->backlight_dev && appletb_tb_autodim) {
		if (kbd->has_dimmed || kbd->has_turned_off) {
			backlight_device_set_brightness(kbd->backlight_dev, 2);
			kbd->has_dimmed = false;
			kbd->has_turned_off = false;
			schedule_work(&kbd->restore_brightness_work);
		}
		mod_timer(&kbd->inactivity_timer,
			jiffies + secs_to_jiffies(appletb_tb_dim_timeout));
		mod_delayed_work(system_wq, &kbd->inactivity_work,
				 secs_to_jiffies(appletb_tb_dim_timeout));
	}
}

@@ -408,9 +420,11 @@ static int appletb_kbd_probe(struct hid_device *hdev, const struct hid_device_id
		dev_err_probe(dev, -ENODEV, "Failed to get backlight device\n");
	} else {
		backlight_device_set_brightness(kbd->backlight_dev, 2);
		timer_setup(&kbd->inactivity_timer, appletb_inactivity_timer, 0);
		mod_timer(&kbd->inactivity_timer,
			jiffies + secs_to_jiffies(appletb_tb_dim_timeout));
		INIT_DELAYED_WORK(&kbd->inactivity_work, appletb_inactivity_work);
		INIT_WORK(&kbd->restore_brightness_work,
			  appletb_restore_brightness_work);
		mod_delayed_work(system_wq, &kbd->inactivity_work,
				 secs_to_jiffies(appletb_tb_dim_timeout));
	}

	kbd->inp_handler.event = appletb_kbd_inp_event;
@@ -440,13 +454,14 @@ static int appletb_kbd_probe(struct hid_device *hdev, const struct hid_device_id
unregister_handler:
	input_unregister_handler(&kbd->inp_handler);
close_hw:
	if (kbd->backlight_dev) {
		put_device(&kbd->backlight_dev->dev);
		timer_delete_sync(&kbd->inactivity_timer);
	}
	hid_hw_close(hdev);
stop_hw:
	hid_hw_stop(hdev);
	if (kbd->backlight_dev) {
		cancel_delayed_work_sync(&kbd->inactivity_work);
		cancel_work_sync(&kbd->restore_brightness_work);
		put_device(&kbd->backlight_dev->dev);
	}
	return ret;
}

@@ -457,13 +472,14 @@ static void appletb_kbd_remove(struct hid_device *hdev)
	appletb_kbd_set_mode(kbd, APPLETB_KBD_MODE_OFF);

	input_unregister_handler(&kbd->inp_handler);
	hid_hw_close(hdev);
	hid_hw_stop(hdev);

	if (kbd->backlight_dev) {
		cancel_delayed_work_sync(&kbd->inactivity_work);
		cancel_work_sync(&kbd->restore_brightness_work);
		put_device(&kbd->backlight_dev->dev);
		timer_delete_sync(&kbd->inactivity_timer);
	}

	hid_hw_close(hdev);
	hid_hw_stop(hdev);
}

static int appletb_kbd_suspend(struct hid_device *hdev, pm_message_t msg)
+53 −14
Original line number Diff line number Diff line
@@ -2033,24 +2033,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);
@@ -2063,11 +2071,16 @@ 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) {
		hid_warn_ratelimited(hid, "Event data for report %d was too short (%d vs %d)\n",
				     report->id, rsize, csize);
		ret = -EINVAL;
		goto out;
		dbg_hid("report %d is too short, (%d < %d)\n", report->id,
			csize, rsize);
		memset(cdata + csize, 0, rsize - csize);
	}

	if ((hid->claimed & HID_CLAIMED_HIDDEV) && hid->hiddev_report_event)
@@ -2075,7 +2088,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) {
@@ -2087,15 +2100,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;
@@ -2120,7 +2133,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;
@@ -2149,7 +2163,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)
@@ -2167,16 +2181,41 @@ 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)
{
	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 */);
}
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)
{
+1 −0
Original line number Diff line number Diff line
@@ -513,6 +513,7 @@ static const struct hid_device_id elan_devices[] = {
	{ HID_USB_DEVICE(USB_VENDOR_ID_ELAN, USB_DEVICE_ID_HP_X2_10_COVER),
	  .driver_data = ELAN_HAS_LED },
	{ HID_I2C_DEVICE(USB_VENDOR_ID_ELAN, USB_DEVICE_ID_TOSHIBA_CLICK_L9W) },
	{ HID_I2C_DEVICE(USB_VENDOR_ID_ELAN, USB_DEVICE_ID_SB974D) },
	{ }
};
MODULE_DEVICE_TABLE(hid, elan_devices);
+14 −2
Original line number Diff line number Diff line
@@ -1068,10 +1068,22 @@ static int ft260_raw_event(struct hid_device *hdev, struct hid_report *report,
	struct ft260_device *dev = hid_get_drvdata(hdev);
	struct ft260_i2c_input_report *xfer = (void *)data;

	if (size < offsetof(struct ft260_i2c_input_report, data)) {
		hid_err(hdev, "short report %d\n", size);
		return -1;
	}

	if (xfer->report >= FT260_I2C_REPORT_MIN &&
	    xfer->report <= FT260_I2C_REPORT_MAX) {
		ft260_dbg("i2c resp: rep %#02x len %d\n", xfer->report,
			  xfer->length);
		ft260_dbg("i2c resp: rep %#02x len %d size %d\n",
			  xfer->report, xfer->length, size);

		if (xfer->length > size -
		    offsetof(struct ft260_i2c_input_report, data)) {
			hid_err(hdev, "report %#02x: length %d exceeds HID report size\n",
				xfer->report, xfer->length);
			return -1;
		}

		if ((dev->read_buf == NULL) ||
		    (xfer->length > dev->read_len - dev->read_idx)) {
Loading