[PATCH v2 10/12] HID: wacom: cleanup usage of 'driver_data'

From: Pawel Zalewski (The Capable Hub)

Date: Wed Jun 10 2026 - 11:24:39 EST


This module is using the 'hid_device_id::driver_data' field
as a valid pointer to dereference. The hid subsystem allows
for injecting 'new_id' device via the command line. It follows
that it is more convenient to have 'driver_data' to be an integer
to allow the userspace to select a valid pointer via an index.

Lets enable this and store an index to the table of pointers
'wacom_features_table' instead, the access to this table is
bounded and checked at runtime via the 'wacom_features_table_index'
enum to avoid OOB dereferencing of objects.

The implementation relies heavily on the preprocessor - but
given the large amount of required entries (162) this is justified,
more robust and more resilient to user error than manual input.

Signed-off-by: Pawel Zalewski (The Capable Hub) <pzalewski@xxxxxxxxxxxxxxxxxxxx>
---
drivers/hid/wacom.h | 177 ++++++++++++++++++++++++++++++++++++++++++++++++
drivers/hid/wacom_sys.c | 22 ++++--
drivers/hid/wacom_wac.c | 25 +++++--
3 files changed, 214 insertions(+), 10 deletions(-)

diff --git a/drivers/hid/wacom.h b/drivers/hid/wacom.h
index 1deacb4568cb..115d0eb23277 100644
--- a/drivers/hid/wacom.h
+++ b/drivers/hid/wacom.h
@@ -228,6 +228,182 @@ static inline u32 wacom_rescale(u32 value, u32 in_max, u32 out_max)

extern const struct hid_device_id wacom_ids[];

+/* This table lists all unique product IDs that are matched with
+ * a corresponging wacom_features_##prod struct. So any new product
+ * should be added to this table.
+ */
+#define HID_WACOM_PRODUCT_TABLE() \
+ table_entry(0x00) \
+ table_entry(0x03) \
+ table_entry(0x10) \
+ table_entry(0x11) \
+ table_entry(0x12) \
+ table_entry(0x13) \
+ table_entry(0x14) \
+ table_entry(0x15) \
+ table_entry(0x16) \
+ table_entry(0x17) \
+ table_entry(0x18) \
+ table_entry(0x19) \
+ table_entry(0x20) \
+ table_entry(0x21) \
+ table_entry(0x22) \
+ table_entry(0x23) \
+ table_entry(0x24) \
+ table_entry(0x26) \
+ table_entry(0x27) \
+ table_entry(0x28) \
+ table_entry(0x29) \
+ table_entry(0x2A) \
+ table_entry(0x30) \
+ table_entry(0x31) \
+ table_entry(0x32) \
+ table_entry(0x33) \
+ table_entry(0x34) \
+ table_entry(0x35) \
+ table_entry(0x37) \
+ table_entry(0x38) \
+ table_entry(0x39) \
+ table_entry(0x3F) \
+ table_entry(0x41) \
+ table_entry(0x42) \
+ table_entry(0x43) \
+ table_entry(0x44) \
+ table_entry(0x45) \
+ table_entry(0x47) \
+ table_entry(0x57) \
+ table_entry(0x59) \
+ table_entry(0x5B) \
+ table_entry(0x5D) \
+ table_entry(0x5E) \
+ table_entry(0x60) \
+ table_entry(0x61) \
+ table_entry(0x62) \
+ table_entry(0x63) \
+ table_entry(0x64) \
+ table_entry(0x65) \
+ table_entry(0x69) \
+ table_entry(0x6A) \
+ table_entry(0x6B) \
+ table_entry(0x81) \
+ table_entry(0x84) \
+ table_entry(0x90) \
+ table_entry(0x93) \
+ table_entry(0x94) \
+ table_entry(0x97) \
+ table_entry(0x9A) \
+ table_entry(0x9F) \
+ table_entry(0xB0) \
+ table_entry(0xB1) \
+ table_entry(0xB2) \
+ table_entry(0xB3) \
+ table_entry(0xB4) \
+ table_entry(0xB5) \
+ table_entry(0xB7) \
+ table_entry(0xB8) \
+ table_entry(0xB9) \
+ table_entry(0xBA) \
+ table_entry(0xBB) \
+ table_entry(0xBC) \
+ table_entry(0xBD) \
+ table_entry(0xC0) \
+ table_entry(0xC2) \
+ table_entry(0xC4) \
+ table_entry(0xC5) \
+ table_entry(0xC6) \
+ table_entry(0xC7) \
+ table_entry(0xCC) \
+ table_entry(0xCE) \
+ table_entry(0xD0) \
+ table_entry(0xD1) \
+ table_entry(0xD2) \
+ table_entry(0xD3) \
+ table_entry(0xD4) \
+ table_entry(0xD5) \
+ table_entry(0xD6) \
+ table_entry(0xD7) \
+ table_entry(0xD8) \
+ table_entry(0xDA) \
+ table_entry(0xDB) \
+ table_entry(0xDD) \
+ table_entry(0xDE) \
+ table_entry(0xDF) \
+ table_entry(0xE2) \
+ table_entry(0xE3) \
+ table_entry(0xE5) \
+ table_entry(0xE6) \
+ table_entry(0xEC) \
+ table_entry(0xED) \
+ table_entry(0xEF) \
+ table_entry(0xF0) \
+ table_entry(0xF4) \
+ table_entry(0xF6) \
+ table_entry(0xF8) \
+ table_entry(0xFA) \
+ table_entry(0xFB) \
+ table_entry(0x100) \
+ table_entry(0x101) \
+ table_entry(0x10D) \
+ table_entry(0x10E) \
+ table_entry(0x10F) \
+ table_entry(0x116) \
+ table_entry(0x12C) \
+ table_entry(0x300) \
+ table_entry(0x301) \
+ table_entry(0x302) \
+ table_entry(0x303) \
+ table_entry(0x304) \
+ table_entry(0x307) \
+ table_entry(0x309) \
+ table_entry(0x30A) \
+ table_entry(0x30C) \
+ table_entry(0x30E) \
+ table_entry(0x314) \
+ table_entry(0x315) \
+ table_entry(0x317) \
+ table_entry(0x318) \
+ table_entry(0x319) \
+ table_entry(0x323) \
+ table_entry(0x325) \
+ table_entry(0x326) \
+ table_entry(0x32A) \
+ table_entry(0x32B) \
+ table_entry(0x32C) \
+ table_entry(0x32F) \
+ table_entry(0x331) \
+ table_entry(0x333) \
+ table_entry(0x335) \
+ table_entry(0x336) \
+ table_entry(0x33B) \
+ table_entry(0x33C) \
+ table_entry(0x33D) \
+ table_entry(0x33E) \
+ table_entry(0x343) \
+ table_entry(0x360) \
+ table_entry(0x361) \
+ table_entry(0x377) \
+ table_entry(0x379) \
+ table_entry(0x37A) \
+ table_entry(0x37B) \
+ table_entry(0x393) \
+ table_entry(0x3c6) \
+ table_entry(0x3c8) \
+ table_entry(0x3dd) \
+ table_entry(0x4001) \
+ table_entry(0x4004) \
+ table_entry(0x5000) \
+ table_entry(0x5002) \
+ table_entry(0x6004) \
+ table_entry(HID_ANY_ID) \
+
+enum wacom_features_table_index {
+#define table_entry(product) \
+ WACOM_FEATURE_TABLE_##product,
+ HID_WACOM_PRODUCT_TABLE()
+ NUM_OF_WACOM_FEATURES_IN_TABLE
+#undef table_entry
+};
+
void wacom_wac_irq(struct wacom_wac *wacom_wac, size_t len);
void wacom_setup_device_quirks(struct wacom *wacom);
int wacom_setup_pen_input_capabilities(struct input_dev *input_dev,
@@ -249,4 +425,5 @@ struct wacom_led *wacom_led_next(struct wacom *wacom, struct wacom_led *cur);
int wacom_equivalent_usage(int usage);
int wacom_initialize_leds(struct wacom *wacom);
void wacom_idleprox_timeout(struct timer_list *list);
+const struct wacom_features *get_wacom_features(enum wacom_features_table_index index);
#endif
diff --git a/drivers/hid/wacom_sys.c b/drivers/hid/wacom_sys.c
index 2220168bf116..dd3ed1f8134b 100644
--- a/drivers/hid/wacom_sys.c
+++ b/drivers/hid/wacom_sys.c
@@ -2561,8 +2561,13 @@ static void wacom_wireless_work(struct work_struct *work)
}

/* Stylus interface */
- wacom_wac1->features =
- *((struct wacom_features *)id->driver_data);
+ const struct wacom_features *features = get_wacom_features(id->driver_data);
+
+ if (!features) {
+ hid_err(wacom->hdev, "could not get wacom features.\n");
+ return;
+ }
+ wacom_wac1->features = *features;

wacom_wac1->pid = wacom_wac->pid;
hid_hw_stop(hdev1);
@@ -2574,8 +2579,7 @@ static void wacom_wireless_work(struct work_struct *work)
if (wacom_wac1->features.touch_max ||
(wacom_wac1->features.type >= INTUOSHT &&
wacom_wac1->features.type <= BAMBOO_PT)) {
- wacom_wac2->features =
- *((struct wacom_features *)id->driver_data);
+ wacom_wac2->features = *features;
wacom_wac2->pid = wacom_wac->pid;
hid_hw_stop(hdev2);
error = wacom_parse_and_register(wacom2, true);
@@ -2845,7 +2849,15 @@ static int wacom_probe(struct hid_device *hdev,
wacom->hdev = hdev;

wacom_wac = &wacom->wacom_wac;
- wacom_wac->features = *((struct wacom_features *)id->driver_data);
+
+ const struct wacom_features *id_features = get_wacom_features(id->driver_data);
+
+ if (!id_features) {
+ hid_err(wacom->hdev, "could not get wacom id_features.\n");
+ return -EINVAL;
+ }
+ wacom_wac->features = *id_features;
+
features = &wacom_wac->features;

if (features->check_for_hid_type && features->hid_type != hdev->type)
diff --git a/drivers/hid/wacom_wac.c b/drivers/hid/wacom_wac.c
index da1f0ea85625..d0a864d52509 100644
--- a/drivers/hid/wacom_wac.c
+++ b/drivers/hid/wacom_wac.c
@@ -4945,25 +4945,40 @@ static const struct wacom_features wacom_features_HID_ANY_ID =
static const struct wacom_features wacom_features_0x94 =
{ "Wacom Bootloader", .type = BOOTLOADER };

+static const struct wacom_features *wacom_features_table[] = {
+#define table_entry(product) \
+ [WACOM_FEATURE_TABLE_##product] = &wacom_features_##product,
+ HID_WACOM_PRODUCT_TABLE()
+#undef table_entry
+};
+
+const struct wacom_features *get_wacom_features(enum wacom_features_table_index index)
+{
+ if (index >= NUM_OF_WACOM_FEATURES_IN_TABLE)
+ return NULL;
+
+ return wacom_features_table[index];
+}
+
#define USB_DEVICE_WACOM(prod) \
HID_DEVICE(BUS_USB, HID_GROUP_WACOM, USB_VENDOR_ID_WACOM, prod),\
- .driver_data = (kernel_ulong_t)&wacom_features_##prod
+ .driver_data = WACOM_FEATURE_TABLE_##prod

#define BT_DEVICE_WACOM(prod) \
HID_DEVICE(BUS_BLUETOOTH, HID_GROUP_WACOM, USB_VENDOR_ID_WACOM, prod),\
- .driver_data = (kernel_ulong_t)&wacom_features_##prod
+ .driver_data = WACOM_FEATURE_TABLE_##prod

#define I2C_DEVICE_WACOM(prod) \
HID_DEVICE(BUS_I2C, HID_GROUP_WACOM, USB_VENDOR_ID_WACOM, prod),\
- .driver_data = (kernel_ulong_t)&wacom_features_##prod
+ .driver_data = WACOM_FEATURE_TABLE_##prod

#define PCI_DEVICE_WACOM(prod) \
HID_DEVICE(BUS_PCI, HID_GROUP_WACOM, USB_VENDOR_ID_WACOM, prod),\
- .driver_data = (kernel_ulong_t)&wacom_features_##prod
+ .driver_data = WACOM_FEATURE_TABLE_##prod

#define USB_DEVICE_LENOVO(prod) \
HID_USB_DEVICE(USB_VENDOR_ID_LENOVO, prod), \
- .driver_data = (kernel_ulong_t)&wacom_features_##prod
+ .driver_data = WACOM_FEATURE_TABLE_##prod

const struct hid_device_id wacom_ids[] = {
{ USB_DEVICE_WACOM(0x00) },

--
2.43.0