diff options
Diffstat (limited to 'drivers/hid')
| -rw-r--r-- | drivers/hid/hid-apple.c | 18 | ||||
| -rw-r--r-- | drivers/hid/hid-corsair-void.c | 17 | ||||
| -rw-r--r-- | drivers/hid/hid-mcp2221.c | 9 | ||||
| -rw-r--r-- | drivers/hid/hid-picolcd_debugfs.c | 9 | ||||
| -rw-r--r-- | drivers/hid/hid-roccat.c | 13 | ||||
| -rw-r--r-- | drivers/hid/hid-sensor-custom.c | 9 | ||||
| -rw-r--r-- | drivers/hid/hid-sensor-hub.c | 42 | ||||
| -rw-r--r-- | drivers/hid/hid-sony.c | 4 | ||||
| -rw-r--r-- | drivers/hid/hid-universal-pidff.c | 4 | ||||
| -rw-r--r-- | drivers/hid/intel-thc-hid/intel-quicki2c/pci-quicki2c.c | 1 | ||||
| -rw-r--r-- | drivers/hid/intel-thc-hid/intel-quickspi/pci-quickspi.c | 10 | ||||
| -rw-r--r-- | drivers/hid/intel-thc-hid/intel-quickspi/quickspi-dev.h | 1 | ||||
| -rw-r--r-- | drivers/hid/intel-thc-hid/intel-quickspi/quickspi-hid.c | 2 | ||||
| -rw-r--r-- | drivers/hid/intel-thc-hid/intel-quickspi/quickspi-protocol.c | 19 | ||||
| -rw-r--r-- | drivers/hid/intel-thc-hid/intel-quickspi/quickspi-protocol.h | 2 |
15 files changed, 130 insertions, 30 deletions
diff --git a/drivers/hid/hid-apple.c b/drivers/hid/hid-apple.c index cb1bd6dca835..924dab47849e 100644 --- a/drivers/hid/hid-apple.c +++ b/drivers/hid/hid-apple.c @@ -91,6 +91,9 @@ struct apple_sc_backlight { struct hid_device *hdev; }; +/* T2 VHCI re-enumerates the internal keyboard across system resume. */ +static int apple_backlight_resume_brightness = -1; + struct apple_backlight_config_report { u8 report_id; u8 version; @@ -823,6 +826,7 @@ static int apple_backlight_led_set(struct led_classdev *led_cdev, static int apple_backlight_init(struct hid_device *hdev) { int ret; + int brightness; struct apple_sc *asc = hid_get_drvdata(hdev); struct apple_backlight_config_report *rep; @@ -858,13 +862,20 @@ static int apple_backlight_init(struct hid_device *hdev) asc->backlight->cdev.name = "apple::kbd_backlight"; asc->backlight->cdev.max_brightness = rep->backlight_on_max; asc->backlight->cdev.brightness_set_blocking = apple_backlight_led_set; - asc->backlight->cdev.flags = LED_CORE_SUSPENDRESUME; + /* VHCI re-enumeration restores the cached brightness in the next probe. */ + + brightness = READ_ONCE(apple_backlight_resume_brightness); + if (brightness < 0) + brightness = LED_OFF; + else + brightness = min_t(int, brightness, rep->backlight_on_max); - ret = apple_backlight_set(hdev, 0, 0); + ret = apple_backlight_set(hdev, brightness, 0); if (ret < 0) { hid_err(hdev, "backlight set request failed: %d\n", ret); goto cleanup_and_exit; } + asc->backlight->cdev.brightness = brightness; ret = devm_led_classdev_register(&hdev->dev, &asc->backlight->cdev); @@ -997,6 +1008,9 @@ static void apple_remove(struct hid_device *hdev) if (asc->quirks & APPLE_RDESC_BATTERY) timer_delete_sync(&asc->battery_timer); + if (asc->backlight) + WRITE_ONCE(apple_backlight_resume_brightness, + asc->backlight->cdev.brightness); hid_hw_stop(hdev); } diff --git a/drivers/hid/hid-corsair-void.c b/drivers/hid/hid-corsair-void.c index 5e9a5b8f7f16..071a663a6c26 100644 --- a/drivers/hid/hid-corsair-void.c +++ b/drivers/hid/hid-corsair-void.c @@ -92,6 +92,9 @@ #define CORSAIR_VOID_STATUS_REPORT_ID 0x64 #define CORSAIR_VOID_FIRMWARE_REPORT_ID 0x66 +#define CORSAIR_VOID_STATUS_REPORT_SIZE 5 +#define CORSAIR_VOID_FIRMWARE_REPORT_SIZE 5 + #define CORSAIR_VOID_USB_SIDETONE_REQUEST 0x1 #define CORSAIR_VOID_USB_SIDETONE_REQUEST_TYPE 0x21 #define CORSAIR_VOID_USB_SIDETONE_VALUE 0x200 @@ -742,6 +745,13 @@ static int corsair_void_raw_event(struct hid_device *hid_dev, /* Description of packets are documented at the top of this file */ if (hid_report->id == CORSAIR_VOID_STATUS_REPORT_ID) { + if (size < CORSAIR_VOID_STATUS_REPORT_SIZE) { + hid_warn_ratelimited(hid_dev, + "unexpected status report of size %d", + size); + return 1; + } + drvdata->mic_up = FIELD_GET(CORSAIR_VOID_MIC_MASK, data[2]); drvdata->connected = (data[3] == CORSAIR_VOID_WIRELESS_CONNECTED) || drvdata->is_wired; @@ -750,6 +760,13 @@ static int corsair_void_raw_event(struct hid_device *hid_dev, FIELD_GET(CORSAIR_VOID_CAPACITY_MASK, data[2]), data[3], data[4]); } else if (hid_report->id == CORSAIR_VOID_FIRMWARE_REPORT_ID) { + if (size < CORSAIR_VOID_FIRMWARE_REPORT_SIZE) { + hid_warn_ratelimited(hid_dev, + "unexpected firmware report of size %d", + size); + return 1; + } + drvdata->fw_receiver_major = data[1]; drvdata->fw_receiver_minor = data[2]; drvdata->fw_headset_major = data[3]; diff --git a/drivers/hid/hid-mcp2221.c b/drivers/hid/hid-mcp2221.c index ef3b5c77c38e..faebe95b4eb0 100644 --- a/drivers/hid/hid-mcp2221.c +++ b/drivers/hid/hid-mcp2221.c @@ -851,6 +851,9 @@ static int mcp2221_raw_event(struct hid_device *hdev, u8 *buf; struct mcp2221 *mcp = hid_get_drvdata(hdev); + if (size < 4) + return 0; + switch (data[0]) { case MCP2221_I2C_WR_DATA: @@ -912,6 +915,10 @@ static int mcp2221_raw_event(struct hid_device *hdev, mcp->status = -EINVAL; break; } + if (4 + data[3] > size) { + mcp->status = -EINVAL; + break; + } buf = mcp->rxbuf; memcpy(&buf[mcp->rxbuf_idx], &data[4], data[3]); mcp->rxbuf_idx = mcp->rxbuf_idx + data[3]; @@ -1035,6 +1042,8 @@ static void mcp2221_hid_unregister(void *ptr) { struct hid_device *hdev = ptr; + if (hdev->io_started) + hid_device_io_stop(hdev); hid_hw_close(hdev); hid_hw_stop(hdev); } diff --git a/drivers/hid/hid-picolcd_debugfs.c b/drivers/hid/hid-picolcd_debugfs.c index d01176da8896..0d325d69e64b 100644 --- a/drivers/hid/hid-picolcd_debugfs.c +++ b/drivers/hid/hid-picolcd_debugfs.c @@ -98,6 +98,15 @@ static ssize_t picolcd_debug_eeprom_read(struct file *f, char __user *u, ret = resp->raw_data[2]; if (ret > s) ret = s; + /* + * raw_data[2] is a device-supplied length; also clamp it to + * what picolcd_raw_event() actually stored (raw_size), or a + * hostile device overruns the raw_data[] buffer. + */ + if (ret > resp->raw_size - 3) + ret = resp->raw_size - 3; + if (ret < 0) + ret = 0; if (copy_to_user(u, resp->raw_data+3, ret)) ret = -EFAULT; else diff --git a/drivers/hid/hid-roccat.c b/drivers/hid/hid-roccat.c index e413662f7508..e54b6a8a92ef 100644 --- a/drivers/hid/hid-roccat.c +++ b/drivers/hid/hid-roccat.c @@ -70,6 +70,15 @@ static struct roccat_device *devices[ROCCAT_MAX_DEVICES]; /* protects modifications of devices array */ static DEFINE_MUTEX(devices_lock); +static void roccat_free_device(struct roccat_device *device) +{ + int i; + + for (i = 0; i < ROCCAT_CBUF_SIZE; i++) + kfree(device->cbuf[i].value); + kfree(device); +} + static ssize_t roccat_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos) { @@ -226,7 +235,7 @@ static int roccat_release(struct inode *inode, struct file *file) hid_hw_power(device->hid, PM_HINT_NORMAL); hid_hw_close(device->hid); } else { - kfree(device); + roccat_free_device(device); } } @@ -374,7 +383,7 @@ void roccat_disconnect(int minor) hid_hw_close(device->hid); wake_up_interruptible(&device->wait); } else { - kfree(device); + roccat_free_device(device); } } EXPORT_SYMBOL_GPL(roccat_disconnect); diff --git a/drivers/hid/hid-sensor-custom.c b/drivers/hid/hid-sensor-custom.c index 957311e7b638..0b97d61df0c8 100644 --- a/drivers/hid/hid-sensor-custom.c +++ b/drivers/hid/hid-sensor-custom.c @@ -609,7 +609,7 @@ static int hid_sensor_custom_add_attributes(struct hid_sensor_custom &sensor_inst->fields[i]. hid_custom_attribute_group); if (ret) - break; + goto err_remove_groups; /* For power or report field store indexes */ if (sensor_inst->fields[i].attribute.attrib_id == @@ -621,6 +621,13 @@ static int hid_sensor_custom_add_attributes(struct hid_sensor_custom } return ret; + +err_remove_groups: + while (--i >= 0) + sysfs_remove_group(&sensor_inst->pdev->dev.kobj, + &sensor_inst->fields[i].hid_custom_attribute_group); + kfree(sensor_inst->fields); + return ret; } static void hid_sensor_custom_remove_attributes(struct hid_sensor_custom * diff --git a/drivers/hid/hid-sensor-hub.c b/drivers/hid/hid-sensor-hub.c index 1b725c879650..faeb9c4536f5 100644 --- a/drivers/hid/hid-sensor-hub.c +++ b/drivers/hid/hid-sensor-hub.c @@ -239,12 +239,17 @@ int sensor_hub_get_feature(struct hid_sensor_hub_device *hsdev, u32 report_id, u32 field_index, int buffer_size, void *buffer) { struct hid_report *report; + struct hid_field *field; struct sensor_hub_data *data = hid_get_drvdata(hsdev->hdev); - int report_size; + size_t field_size; + size_t report_size; + size_t copied = 0; + size_t to_copy; int ret = 0; - u8 *val_ptr; - int buffer_index = 0; - int i; + unsigned int i; + + if (!buffer || buffer_size <= 0) + return -EINVAL; memset(buffer, 0, buffer_size); @@ -258,26 +263,29 @@ int sensor_hub_get_feature(struct hid_sensor_hub_device *hsdev, u32 report_id, hid_hw_request(hsdev->hdev, report, HID_REQ_GET_REPORT); hid_hw_wait(hsdev->hdev); + field = report->field[field_index]; + /* calculate number of bytes required to read this field */ - report_size = DIV_ROUND_UP(report->field[field_index]->report_size, - 8) * - report->field[field_index]->report_count; - if (!report_size) { + field_size = DIV_ROUND_UP(field->report_size, 8); + /* HID core stores each parsed report value in a __s32 slot. */ + if (!field_size || field_size > sizeof(field->value[0])) { + ret = -EINVAL; + goto done_proc; + } + if (field->report_count > SIZE_MAX / field_size) { ret = -EINVAL; goto done_proc; } - ret = min(report_size, buffer_size); - val_ptr = (u8 *)report->field[field_index]->value; - for (i = 0; i < report->field[field_index]->report_count; ++i) { - if (buffer_index >= ret) - break; + report_size = field_size * field->report_count; + report_size = min_t(size_t, report_size, buffer_size); - memcpy(&((u8 *)buffer)[buffer_index], val_ptr, - report->field[field_index]->report_size / 8); - val_ptr += sizeof(__s32); - buffer_index += (report->field[field_index]->report_size / 8); + for (i = 0; i < field->report_count && copied < report_size; ++i) { + to_copy = min(field_size, report_size - copied); + memcpy(&((u8 *)buffer)[copied], &field->value[i], to_copy); + copied += to_copy; } + ret = copied; done_proc: mutex_unlock(&data->mutex); diff --git a/drivers/hid/hid-sony.c b/drivers/hid/hid-sony.c index b966e4044238..22db29add44e 100644 --- a/drivers/hid/hid-sony.c +++ b/drivers/hid/hid-sony.c @@ -2164,7 +2164,9 @@ static void sony_remove(struct hid_device *hdev) struct sony_sc *sc = hid_get_drvdata(hdev); if (sc->quirks & (GHL_GUITAR_PS3WIIU | GHL_GUITAR_PS4)) { - timer_delete_sync(&sc->ghl_poke_timer); + /* poison, not kill: a pending timer must not re-submit during teardown */ + usb_poison_urb(sc->ghl_urb); + timer_shutdown_sync(&sc->ghl_poke_timer); usb_free_urb(sc->ghl_urb); } diff --git a/drivers/hid/hid-universal-pidff.c b/drivers/hid/hid-universal-pidff.c index 549dac555d40..60180467a0bf 100644 --- a/drivers/hid/hid-universal-pidff.c +++ b/drivers/hid/hid-universal-pidff.c @@ -104,12 +104,14 @@ static int universal_pidff_probe(struct hid_device *hdev, error = init_function(hdev, id->driver_data); if (error) { hid_warn(hdev, "Error initialising force feedback\n"); - goto err; + goto err_stop; } hid_info(hdev, "Universal pidff driver loaded successfully!"); return 0; +err_stop: + hid_hw_stop(hdev); err: return error; } diff --git a/drivers/hid/intel-thc-hid/intel-quicki2c/pci-quicki2c.c b/drivers/hid/intel-thc-hid/intel-quicki2c/pci-quicki2c.c index 0156ab391778..c6ba6886072a 100644 --- a/drivers/hid/intel-thc-hid/intel-quicki2c/pci-quicki2c.c +++ b/drivers/hid/intel-thc-hid/intel-quicki2c/pci-quicki2c.c @@ -771,6 +771,7 @@ static void quicki2c_remove(struct pci_dev *pdev) quicki2c_hid_remove(qcdev); quicki2c_dma_deinit(qcdev); + pm_runtime_dont_use_autosuspend(qcdev->dev); pm_runtime_get_noresume(qcdev->dev); quicki2c_dev_deinit(qcdev); diff --git a/drivers/hid/intel-thc-hid/intel-quickspi/pci-quickspi.c b/drivers/hid/intel-thc-hid/intel-quickspi/pci-quickspi.c index f0830a56d556..1c96d1d85845 100644 --- a/drivers/hid/intel-thc-hid/intel-quickspi/pci-quickspi.c +++ b/drivers/hid/intel-thc-hid/intel-quickspi/pci-quickspi.c @@ -556,7 +556,14 @@ static int quickspi_alloc_report_buf(struct quickspi_device *qsdev) max_report_len = max(le16_to_cpu(qsdev->dev_desc.max_output_len), le16_to_cpu(qsdev->dev_desc.max_input_len)); - qsdev->report_buf = devm_kzalloc(qsdev->dev, max_report_len, GFP_KERNEL); + /* + * write_cmd_to_txdma() writes the output report header ahead of the + * content in this buffer, so it has to hold both. + */ + qsdev->report_buf_size = HIDSPI_OUTPUT_REPORT_SIZE(max_report_len); + + qsdev->report_buf = devm_kzalloc(qsdev->dev, qsdev->report_buf_size, + GFP_KERNEL); if (!qsdev->report_buf) return -ENOMEM; @@ -715,6 +722,7 @@ static void quickspi_remove(struct pci_dev *pdev) quickspi_hid_remove(qsdev); quickspi_dma_deinit(qsdev); + pm_runtime_dont_use_autosuspend(qsdev->dev); pm_runtime_get_noresume(qsdev->dev); quickspi_dev_deinit(qsdev); diff --git a/drivers/hid/intel-thc-hid/intel-quickspi/quickspi-dev.h b/drivers/hid/intel-thc-hid/intel-quickspi/quickspi-dev.h index bf5e18f5a5f4..0ed964bfe3dd 100644 --- a/drivers/hid/intel-thc-hid/intel-quickspi/quickspi-dev.h +++ b/drivers/hid/intel-thc-hid/intel-quickspi/quickspi-dev.h @@ -157,6 +157,7 @@ struct quickspi_device { u8 *report_descriptor; u8 *input_buf; u8 *report_buf; + u32 report_buf_size; u32 report_len; wait_queue_head_t reset_ack_wq; diff --git a/drivers/hid/intel-thc-hid/intel-quickspi/quickspi-hid.c b/drivers/hid/intel-thc-hid/intel-quickspi/quickspi-hid.c index ad52e402c28a..9df948f94d5a 100644 --- a/drivers/hid/intel-thc-hid/intel-quickspi/quickspi-hid.c +++ b/drivers/hid/intel-thc-hid/intel-quickspi/quickspi-hid.c @@ -61,7 +61,7 @@ static int quickspi_hid_raw_request(struct hid_device *hid, switch (reqtype) { case HID_REQ_GET_REPORT: - ret = quickspi_get_report(qsdev, rtype, reportnum, buf); + ret = quickspi_get_report(qsdev, rtype, reportnum, buf, len); break; case HID_REQ_SET_REPORT: ret = quickspi_set_report(qsdev, rtype, reportnum, buf, len); diff --git a/drivers/hid/intel-thc-hid/intel-quickspi/quickspi-protocol.c b/drivers/hid/intel-thc-hid/intel-quickspi/quickspi-protocol.c index cb19057f1191..847c5ec55569 100644 --- a/drivers/hid/intel-thc-hid/intel-quickspi/quickspi-protocol.c +++ b/drivers/hid/intel-thc-hid/intel-quickspi/quickspi-protocol.c @@ -30,6 +30,9 @@ static int write_cmd_to_txdma(struct quickspi_device *qsdev, write_buf = (struct output_report *)qsdev->report_buf; + if (HIDSPI_OUTPUT_REPORT_SIZE(report_buf_len) > qsdev->report_buf_size) + return -EINVAL; + write_buf->output_hdr.report_type = report_type; write_buf->output_hdr.content_len = cpu_to_le16(report_buf_len); write_buf->output_hdr.content_id = report_id; @@ -342,10 +345,12 @@ int reset_tic(struct quickspi_device *qsdev) } int quickspi_get_report(struct quickspi_device *qsdev, - u8 report_type, unsigned int report_id, void *buf) + u8 report_type, unsigned int report_id, void *buf, + u32 buf_len) { int rep_type; int ret; + u32 report_len; if (report_type == HID_INPUT_REPORT) { rep_type = GET_INPUT_REPORT; @@ -372,9 +377,17 @@ int quickspi_get_report(struct quickspi_device *qsdev, } qsdev->get_report_cmpl = false; - memcpy(buf, qsdev->report_buf, qsdev->report_len); + /* quickspi_handle_input_data() updates this from IRQ context. */ + report_len = READ_ONCE(qsdev->report_len); + if (report_len > buf_len) { + dev_err_once(qsdev->dev, "Get report response too big, %u vs %u\n", + report_len, buf_len); + return -EINVAL; + } + + memcpy(buf, qsdev->report_buf, report_len); - return qsdev->report_len; + return report_len; } int quickspi_set_report(struct quickspi_device *qsdev, diff --git a/drivers/hid/intel-thc-hid/intel-quickspi/quickspi-protocol.h b/drivers/hid/intel-thc-hid/intel-quickspi/quickspi-protocol.h index 775e29c1ed13..8a2338bee808 100644 --- a/drivers/hid/intel-thc-hid/intel-quickspi/quickspi-protocol.h +++ b/drivers/hid/intel-thc-hid/intel-quickspi/quickspi-protocol.h @@ -12,7 +12,7 @@ struct quickspi_device; void quickspi_handle_input_data(struct quickspi_device *qsdev, u32 buf_len); int quickspi_get_report(struct quickspi_device *qsdev, u8 report_type, - unsigned int report_id, void *buf); + unsigned int report_id, void *buf, u32 buf_len); int quickspi_set_report(struct quickspi_device *qsdev, u8 report_type, unsigned int report_id, void *buf, u32 buf_len); int quickspi_get_report_descriptor(struct quickspi_device *qsdev); |
