mirror of
https://github.com/torvalds/linux.git
synced 2026-09-09 09:22:02 +02:00
hid-for-linus-2026090401
-----BEGIN PGP SIGNATURE----- iQIzBAABCgAdFiEEoEVH9lhNrxiMPSyI7MXwXhnZSjYFAmqa4YwACgkQ7MXwXhnZ SjaNsQ/9Ff0KeKgaQUZHLE47SpOlXKWQaDJmrodDkngQh+9KaZj3NgmZD5BR2Z2p 5v/6dhs4gFoFzQtXjuR0GvDTWzu0bx1IV3IsXhqTdhQ2fLAeu8IxUL0DEOtg3lUO Vlf67UagvmC+K01UWkbloS3f8dEt8tg3CXyg0Uy7f23QgBFa/TbtQpXTNGlMHOqv G6qE1PPBqGmPI74E/5uusI8L3tw4t8A4ylHi3UcQhTxGaUGK+Ew8GCeDsIUrwrzM A8Um5GHdBCWZAqluT8HPnBI2wgnUR+pvda4UdqMSYkBJW2Rz1FFaOhgLkHt+azRX F7RhjuxcBlaZsXIaCmIZEW6rEr0QIeUPeFK6ML3uswLtFdh/yWASUMo84Ev08Z9N iB7qm0+S9AZSDknINAtRRcOXsOgjvug00xMf6zcUvcP66mP1Rj/PnOGb5Lqm4icp SiXBF+CpN0qn3h8TWG5+GvEX0AnGcmkpL0Vx7noVHJeK8Z+Yroozv+vGi1/29pxo ML4QEUIYV3Uj0rU1Azgd/rKiaxnizpczeJ5ViW4+ozpT4nPHjTxcz5kVsqJ6gGV3 XTsV9YW13xgjuB0objDDeGjYRku7MtTWUfdQiKCE71a+L9nYWr5/q2mp+8QTKWqy /XnK5I8o2dGWhMWCPmwoqCNO/f4FGT+J2Ok6yisG8jJ/9j6Dovk= =a+wk -----END PGP SIGNATURE----- Merge tag 'hid-for-linus-2026090401' of git://git.kernel.org/pub/scm/linux/kernel/git/hid/hid Pull HID fixes from Benjamin Tissoires: - hid-hyperv build fixes on certain configs (Jiri Kosina) - HID-BPF fix and selftests now that the bpf verifier is more restrictive (Benjamin Tissoires) - Some AI detected fixes for OOB, errors and validation (Ibrahim Hashimov, Shen Yongchao, Wei Jie Law) - various device fixes (Dave Carey and Vadim Klishko) * tag 'hid-for-linus-2026090401' of git://git.kernel.org/pub/scm/linux/kernel/git/hid/hid: HID: bpf: serialize device reference release in struct_ops destroy path HID: rmi: fix OOB access with undersized RMI reports selftests/hid: prepare test_rdesc_fixup_get_data_overflow for the new verifier selftests/hid: Add a test to ensure we can write fields in hid_device HID: bpf: mark struct hid_device as safe BPF pointer HID: wacom: validate report length in wacom_intuos_pro2_bt_irq HID: multitouch: Fix stale MT slots when contact count drops to zero HID: i2c-hid: Add a quirk for a Cirque I2C device. HID: hyperv: make pointer arithmetics understandable for FORTIFY_SOURCE HID: hyperv: fix build breakage with certain configs
This commit is contained in:
commit
986c24e0fe
|
|
@ -1253,7 +1253,7 @@ config HID_HYPERV_MOUSE
|
|||
|
||||
config HID_HYPERV_MOUSE_KUNIT_TEST
|
||||
bool "KUnit tests for Hyper-V mouse driver" if !KUNIT_ALL_TESTS
|
||||
depends on KUNIT && HID_HYPERV_MOUSE
|
||||
depends on KUNIT && (HID_HYPERV_MOUSE = KUNIT || KUNIT = y)
|
||||
default KUNIT_ALL_TESTS
|
||||
help
|
||||
Builds unit tests for the Hyper-V synthetic HID driver.
|
||||
|
|
|
|||
|
|
@ -62,6 +62,10 @@ struct hid_bpf_offset_write_range {
|
|||
u32 end;
|
||||
};
|
||||
|
||||
struct hid_bpf_ctx__safe_trusted {
|
||||
struct hid_device *hid;
|
||||
};
|
||||
|
||||
static int hid_bpf_ops_btf_struct_access(struct bpf_verifier_log *log,
|
||||
const struct bpf_reg_state *reg,
|
||||
int off, int size)
|
||||
|
|
@ -86,6 +90,8 @@ static int hid_bpf_ops_btf_struct_access(struct bpf_verifier_log *log,
|
|||
const char *cur = NULL;
|
||||
int i;
|
||||
|
||||
BTF_TYPE_EMIT(struct hid_bpf_ctx__safe_trusted);
|
||||
|
||||
t = btf_type_by_id(reg->btf, reg->btf_id);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(write_ranges); i++) {
|
||||
|
|
@ -250,6 +256,11 @@ static void hid_bpf_unreg(void *kdata, struct bpf_link *link)
|
|||
|
||||
mutex_lock(&hdev->bpf.prog_list_lock);
|
||||
|
||||
if (!ops->hdev) {
|
||||
mutex_unlock(&hdev->bpf.prog_list_lock);
|
||||
return;
|
||||
}
|
||||
|
||||
list_del_rcu(&ops->list);
|
||||
synchronize_srcu(&hdev->bpf.srcu);
|
||||
ops->hdev = NULL;
|
||||
|
|
@ -310,13 +321,17 @@ static struct bpf_struct_ops bpf_hid_bpf_ops = {
|
|||
void __hid_bpf_ops_destroy_device(struct hid_device *hdev)
|
||||
{
|
||||
struct hid_bpf_ops *e;
|
||||
int count = 0;
|
||||
|
||||
rcu_read_lock();
|
||||
list_for_each_entry_rcu(e, &hdev->bpf.prog_list, list) {
|
||||
hid_put_device(hdev);
|
||||
mutex_lock(&hdev->bpf.prog_list_lock);
|
||||
list_for_each_entry(e, &hdev->bpf.prog_list, list) {
|
||||
e->hdev = NULL;
|
||||
count++;
|
||||
}
|
||||
rcu_read_unlock();
|
||||
mutex_unlock(&hdev->bpf.prog_list_lock);
|
||||
|
||||
while (count--)
|
||||
hid_put_device(hdev);
|
||||
}
|
||||
|
||||
static int __init hid_bpf_struct_ops_init(void)
|
||||
|
|
|
|||
|
|
@ -687,7 +687,7 @@ static void mousevsc_device_info_valid_descriptor(struct kunit *test)
|
|||
|
||||
info->hid_descriptor.bLength = sizeof(struct hid_descriptor);
|
||||
info->hid_descriptor.rpt_desc.wDescriptorLength = cpu_to_le16(4);
|
||||
report = ((u8 *)&info->hid_descriptor) + info->hid_descriptor.bLength;
|
||||
report = (u8 *)(info + 1);
|
||||
memset(report, 0x42, 4);
|
||||
|
||||
mousevsc_on_receive_device_info(input_dev, info, sizeof(*info) + 4);
|
||||
|
|
@ -713,7 +713,7 @@ static void mousevsc_device_info_report_desc_oob(struct kunit *test)
|
|||
|
||||
info->hid_descriptor.bLength = sizeof(struct hid_descriptor);
|
||||
info->hid_descriptor.rpt_desc.wDescriptorLength = cpu_to_le16(64);
|
||||
report = ((u8 *)&info->hid_descriptor) + info->hid_descriptor.bLength;
|
||||
report = (u8 *)(info + 1);
|
||||
memset(report, 0x42, 8);
|
||||
|
||||
mousevsc_on_receive_device_info(input_dev, info, sizeof(*info) + 8);
|
||||
|
|
|
|||
|
|
@ -338,6 +338,7 @@
|
|||
|
||||
#define I2C_VENDOR_ID_CIRQUE 0x0488
|
||||
#define I2C_PRODUCT_ID_CIRQUE_1063 0x1063
|
||||
#define I2C_PRODUCT_ID_CIRQUE_D0C1 0xD0C1
|
||||
|
||||
#define USB_VENDOR_ID_CJTOUCH 0x24b8
|
||||
#define USB_DEVICE_ID_CJTOUCH_MULTI_TOUCH_0020 0x0020
|
||||
|
|
|
|||
|
|
@ -1321,21 +1321,18 @@ static void mt_touch_report(struct hid_device *hid,
|
|||
* Includes multi-packet support where subsequent
|
||||
* packets are sent with zero contactcount.
|
||||
*/
|
||||
if (contact_count >= 0) {
|
||||
if (contact_count > 0)
|
||||
app->num_expected = contact_count;
|
||||
else if (app->num_received == 0 && app->prev_scantime != scantime) {
|
||||
/*
|
||||
* New multi-report frame:
|
||||
*
|
||||
* For Win8 PTPs the first packet (td->num_received == 0) may
|
||||
* have a contactcount of 0 if there only is a button event.
|
||||
* We double check that this is not a continuation packet
|
||||
* of a possible multi-packet frame be checking that the
|
||||
* timestamp has changed.
|
||||
*
|
||||
* Some other devices use a sentinel frame with 0 to release all contacts
|
||||
*/
|
||||
if ((app->quirks & MT_QUIRK_WIN8_PTP_BUTTONS) &&
|
||||
app->num_received == 0 &&
|
||||
app->prev_scantime != scantime)
|
||||
app->num_expected = contact_count;
|
||||
/* A non 0 contact count always indicates a first packet */
|
||||
else if (contact_count)
|
||||
app->num_expected = contact_count;
|
||||
app->num_expected = 0;
|
||||
}
|
||||
app->prev_scantime = scantime;
|
||||
|
||||
|
|
|
|||
|
|
@ -235,7 +235,23 @@ static int rmi_hid_read_block(struct rmi_transport_dev *xport, u16 addr,
|
|||
break;
|
||||
}
|
||||
|
||||
read_input_count = data->readReport[1];
|
||||
read_input_count = min_t(int, data->readReport[1],
|
||||
data->input_report_size - 2);
|
||||
if (!read_input_count) {
|
||||
/*
|
||||
* A zero length reply advances neither
|
||||
* bytes_read nor bytes_needed, and because a
|
||||
* reply did arrive the wait above does not
|
||||
* time out either, so a device answering 0
|
||||
* forever would spin here indefinitely with
|
||||
* page_mutex held.
|
||||
*/
|
||||
hid_warn(hdev, "%s: zero-length read reply\n",
|
||||
__func__);
|
||||
clear_bit(RMI_READ_DATA_PENDING, &data->flags);
|
||||
ret = -EIO;
|
||||
break;
|
||||
}
|
||||
memcpy(buf + bytes_read, &data->readReport[2],
|
||||
min(read_input_count, bytes_needed));
|
||||
|
||||
|
|
@ -271,6 +287,11 @@ static int rmi_hid_write_block(struct rmi_transport_dev *xport, u16 addr,
|
|||
goto exit;
|
||||
}
|
||||
|
||||
if (len + 4 > data->output_report_size) {
|
||||
ret = -EINVAL;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
data->writeReport[0] = RMI_WRITE_REPORT_ID;
|
||||
data->writeReport[1] = len;
|
||||
data->writeReport[2] = addr & 0xFF;
|
||||
|
|
@ -666,8 +687,16 @@ static int rmi_probe(struct hid_device *hdev, const struct hid_device_id *id)
|
|||
return ret;
|
||||
}
|
||||
|
||||
if (id->driver_data)
|
||||
data->device_flags = id->driver_data;
|
||||
/*
|
||||
* RMI_DEVICE can only mean "this probe validated the RMI reports and
|
||||
* allocated writeReport": every bail-out to start below skips that
|
||||
* allocation, and device_flags left carrying RMI_DEVICE from
|
||||
* driver_data would send rmi_input_configured() into rmi_set_page()
|
||||
* with writeReport still NULL. A bind through the new_id sysfs
|
||||
* attribute can supply driver_data with the bit set, so do not let
|
||||
* driver_data grant it.
|
||||
*/
|
||||
data->device_flags = id->driver_data & ~RMI_DEVICE;
|
||||
|
||||
/*
|
||||
* Check for the RMI specific report ids. If they are misisng
|
||||
|
|
@ -696,6 +725,17 @@ static int rmi_probe(struct hid_device *hdev, const struct hid_device_id *id)
|
|||
|
||||
data->output_report_size = hid_report_len(output_report);
|
||||
|
||||
/*
|
||||
* The write reports built by this driver occupy 6 bytes and the read
|
||||
* handshake looks at the first 3 bytes of an input report, so refuse
|
||||
* to drive a device whose reports cannot hold them.
|
||||
*/
|
||||
if (data->output_report_size < 6 || data->input_report_size < 3) {
|
||||
hid_err(hdev, "rmi reports too small (out=%u in=%u)\n",
|
||||
data->output_report_size, data->input_report_size);
|
||||
goto start;
|
||||
}
|
||||
|
||||
data->device_flags |= RMI_DEVICE;
|
||||
alloc_size = data->output_report_size + data->input_report_size;
|
||||
|
||||
|
|
|
|||
|
|
@ -136,6 +136,8 @@ static const struct i2c_hid_quirks {
|
|||
I2C_HID_QUIRK_BAD_INPUT_SIZE },
|
||||
{ I2C_VENDOR_ID_CIRQUE, I2C_PRODUCT_ID_CIRQUE_1063,
|
||||
I2C_HID_QUIRK_NO_SLEEP_ON_SUSPEND },
|
||||
{ I2C_VENDOR_ID_CIRQUE, I2C_PRODUCT_ID_CIRQUE_D0C1,
|
||||
I2C_HID_QUIRK_NO_IRQ_AFTER_RESET },
|
||||
/*
|
||||
* Without additional power on command, at least some QTEC devices send garbage
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -1550,6 +1550,19 @@ static int wacom_intuos_pro2_bt_irq(struct wacom_wac *wacom, size_t len)
|
|||
return 0;
|
||||
}
|
||||
|
||||
if (wacom->features.type == INTUOSP2_BT ||
|
||||
wacom->features.type == INTUOSP2S_BT) {
|
||||
if (len < 286) {
|
||||
dev_warn(wacom->pen_input->dev.parent,
|
||||
"Pro2 BT report too short: %zu bytes\n", len);
|
||||
return 0;
|
||||
}
|
||||
} else if (len < 46) {
|
||||
dev_warn(wacom->pen_input->dev.parent,
|
||||
"Pro2 BT report too short: %zu bytes\n", len);
|
||||
return 0;
|
||||
}
|
||||
|
||||
wacom_intuos_pro2_bt_pen(wacom);
|
||||
if (wacom->features.type == INTUOSP2_BT ||
|
||||
wacom->features.type == INTUOSP2S_BT) {
|
||||
|
|
|
|||
|
|
@ -67,14 +67,17 @@ struct test_program {
|
|||
int insert_head;
|
||||
};
|
||||
#define LOAD_PROGRAMS(progs) \
|
||||
load_programs(progs, ARRAY_SIZE(progs), _metadata, self, variant)
|
||||
load_programs(progs, ARRAY_SIZE(progs), false, _metadata, self, variant)
|
||||
#define LOAD_PROGRAMS_MAY_FAIL(progs) \
|
||||
load_programs(progs, ARRAY_SIZE(progs), true, _metadata, self, variant)
|
||||
#define LOAD_BPF \
|
||||
load_programs(NULL, 0, _metadata, self, variant)
|
||||
static void load_programs(const struct test_program programs[],
|
||||
const size_t progs_count,
|
||||
struct __test_metadata *_metadata,
|
||||
FIXTURE_DATA(hid_bpf) * self,
|
||||
const FIXTURE_VARIANT(hid_bpf) * variant)
|
||||
load_programs(NULL, 0, false, _metadata, self, variant)
|
||||
static int load_programs(const struct test_program programs[],
|
||||
const size_t progs_count,
|
||||
bool load_may_fail,
|
||||
struct __test_metadata *_metadata,
|
||||
FIXTURE_DATA(hid_bpf) * self,
|
||||
const FIXTURE_VARIANT(hid_bpf) * variant)
|
||||
{
|
||||
struct bpf_map *iter_map;
|
||||
int err = -EINVAL;
|
||||
|
|
@ -128,6 +131,9 @@ static void load_programs(const struct test_program programs[],
|
|||
}
|
||||
|
||||
err = hid__load(self->skel);
|
||||
if (err && load_may_fail)
|
||||
return err;
|
||||
|
||||
ASSERT_OK(err) TH_LOG("hid_skel_load failed: %d", err);
|
||||
|
||||
for (int i = 0; i < progs_count; i++) {
|
||||
|
|
@ -147,6 +153,7 @@ static void load_programs(const struct test_program programs[],
|
|||
|
||||
self->hidraw_fd = open_hidraw(&self->hid);
|
||||
ASSERT_GE(self->hidraw_fd, 0) TH_LOG("open_hidraw");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -904,11 +911,39 @@ TEST_F(hid_bpf, test_rdesc_fixup_get_data_overflow)
|
|||
{ .name = "hid_rdesc_fixup_get_data_overflow" },
|
||||
};
|
||||
|
||||
LOAD_PROGRAMS(progs);
|
||||
/* newer verifier can detect the overflow at load time */
|
||||
if (LOAD_PROGRAMS_MAY_FAIL(progs))
|
||||
return;
|
||||
|
||||
ASSERT_EQ(self->skel->bss->get_data_overflow_check, 1);
|
||||
}
|
||||
|
||||
TEST_F(hid_bpf, test_rdesc_fixup_change_uniq_name_phys)
|
||||
{
|
||||
const struct test_program progs[] = {
|
||||
{ .name = "hid_rdesc_fixup_change_uniq_name_phys" },
|
||||
};
|
||||
char expected[256], buf[256] = {};
|
||||
int err;
|
||||
|
||||
LOAD_PROGRAMS(progs);
|
||||
|
||||
err = ioctl(self->hidraw_fd, HIDIOCGRAWNAME(sizeof(buf)), buf);
|
||||
ASSERT_GE(err, 0) TH_LOG("HIDIOCGRAWNAME");
|
||||
ASSERT_STREQ("name coming from bpf", buf);
|
||||
|
||||
snprintf(expected, sizeof(expected), "%d phys:coming:from:bpf", self->hid.dev_id);
|
||||
|
||||
err = ioctl(self->hidraw_fd, HIDIOCGRAWPHYS(sizeof(buf)), buf);
|
||||
ASSERT_GE(err, 0) TH_LOG("HIDIOCGRAWPHYS");
|
||||
ASSERT_STREQ(expected, buf);
|
||||
|
||||
err = ioctl(self->hidraw_fd, HIDIOCGRAWUNIQ(sizeof(buf)), buf);
|
||||
ASSERT_GE(err, 0) TH_LOG("HIDIOCGRAWUNIQ");
|
||||
ASSERT_STREQ("uniq:coming:from:bpf", buf);
|
||||
|
||||
}
|
||||
|
||||
static int libbpf_print_fn(enum libbpf_print_level level,
|
||||
const char *format, va_list args)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -255,6 +255,32 @@ struct hid_bpf_ops rdesc_fixup_get_data_overflow = {
|
|||
.hid_rdesc_fixup = (void *)hid_rdesc_fixup_get_data_overflow,
|
||||
};
|
||||
|
||||
SEC("?struct_ops.s/hid_rdesc_fixup")
|
||||
int BPF_PROG(hid_rdesc_fixup_change_uniq_name_phys, struct hid_bpf_ctx *hid_ctx)
|
||||
{
|
||||
#define HID_BPF_MEMCPY(target, str) \
|
||||
__builtin_memcpy(target, str, sizeof(str))
|
||||
|
||||
HID_BPF_MEMCPY(hid_ctx->hid->name, "name coming from bpf");
|
||||
HID_BPF_MEMCPY(hid_ctx->hid->uniq, "uniq:coming:from:bpf");
|
||||
/* hid_bpf relies on a phys being a rand % 1024 */
|
||||
for (int i = 0; i < 5; i++) {
|
||||
if (!hid_ctx->hid->phys[i]) {
|
||||
HID_BPF_MEMCPY(hid_ctx->hid->phys + i, " phys:coming:from:bpf");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#undef HID_BPF_MEMCPY
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
SEC(".struct_ops.link")
|
||||
struct hid_bpf_ops rdesc_fixup_change_uniq_name_phys = {
|
||||
.hid_rdesc_fixup = (void *)hid_rdesc_fixup_change_uniq_name_phys,
|
||||
};
|
||||
|
||||
SEC("?struct_ops/hid_device_event")
|
||||
int BPF_PROG(hid_test_insert1, struct hid_bpf_ctx *hid_ctx, enum hid_report_type type)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -61,6 +61,9 @@ enum hid_report_type {
|
|||
|
||||
struct hid_device {
|
||||
unsigned int id;
|
||||
char name[128];
|
||||
char phys[64];
|
||||
char uniq[64];
|
||||
} __attribute__((preserve_access_index));
|
||||
|
||||
struct bpf_wq {
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user