* [PATCH v5 0/2] HID: multitouch: fix use-after-free of haptic data on delayed input release [not found] <20261009103333.2476105-1-qwe.aldo@gmail.com> @ 2026-10-16 13:55 ` Aldo Ariel Panzardo 2026-10-16 13:55 ` [PATCH v5 1/2] HID: haptic: reorder try_module_get/get_device before input_ff_create Aldo Ariel Panzardo ` (2 more replies) 0 siblings, 3 replies; 16+ messages in thread From: Aldo Ariel Panzardo @ 2026-10-16 13:55 UTC (permalink / raw) To: jikos, bentiss Cc: dmitry.torokhov, linux-input, linux-kernel, stable, Aldo Ariel Panzardo Hi Benjamin, Thanks for the feedback on v4. Reworked as you asked -- devm_kzalloc stays, a devm_add_action() handles teardown, and the try_module_get reorder is now a separate patch. Patch 1 moves try_module_get()/get_device() before input_ff_create() so the old input_free error path (which returned ret == 0) goes away. Patch 2 adds a devm_add_action() callback in hid_haptic_init() that does all the resource teardown at unbind. hid_haptic_destroy() just sets ff->private = NULL so the subsequent kfree in input_ff_destroy is harmless. input_get_device() pins the input device for the duration of the devres action. Changes since v4: - keep devm_kzalloc, use devm_add_action (Benjamin) - split reorder into its own patch (Benjamin) - single-owner teardown -- destroy only NULLs ff->private, devm action owns workqueue drain + buffer frees + ref puts - input_get_device pins input_dev against concurrent release - workqueue drained before any pointer NULLs or frees - input_ff_destroy on devm_add_action failure - no hid-multitouch.c or hid-haptic.h changes v4: https://lore.kernel.org/linux-input/20261009103333.2476105-1-qwe.aldo@gmail.com/ v3: https://lore.kernel.org/linux-input/20261009110509.2432308-1-qwe.aldo@gmail.com/ v1: https://lore.kernel.org/linux-input/20261009031207.3233206-1-qwe.aldo@gmail.com/ Aldo Ariel Panzardo (2): HID: haptic: reorder try_module_get/get_device before input_ff_create HID: multitouch: fix use-after-free of haptic data on delayed input release drivers/hid/hid-haptic.c | 87 ++++++++++++++++++++++++++++------------ 1 file changed, 62 insertions(+), 25 deletions(-) -- 2.53.0 ^ permalink raw reply [flat|nested] 16+ messages in thread
* [PATCH v5 1/2] HID: haptic: reorder try_module_get/get_device before input_ff_create 2026-10-16 13:55 ` [PATCH v5 0/2] HID: multitouch: fix use-after-free of haptic data on delayed input release Aldo Ariel Panzardo @ 2026-10-16 13:55 ` Aldo Ariel Panzardo 2026-10-16 14:06 ` sashiko-bot 2026-10-16 13:55 ` [PATCH v5 2/2] HID: multitouch: fix use-after-free of haptic data on delayed input release Aldo Ariel Panzardo 2026-10-16 15:13 ` [PATCH v6 0/2] " Aldo Ariel Panzardo 2 siblings, 1 reply; 16+ messages in thread From: Aldo Ariel Panzardo @ 2026-10-16 13:55 UTC (permalink / raw) To: jikos, bentiss Cc: dmitry.torokhov, linux-input, linux-kernel, stable, Aldo Ariel Panzardo Move try_module_get() and get_device() before input_ff_create() in hid_haptic_init(). The original order placed them after input_ff_create(), and failures went to input_free which called input_ff_destroy() without ever setting ret. The caller saw ret == 0 on failure and kept a dangling pointer to the haptic struct. With the references acquired first, all failure paths reach stop_buffer_free directly. The flush/event save/restore is no longer needed. Fixes: 8d0bf7908b5a ("HID: multitouch: add haptic multitouch support") Cc: stable@vger.kernel.org Signed-off-by: Aldo Ariel Panzardo <qwe.aldo@gmail.com> --- drivers/hid/hid-haptic.c | 33 ++++++++++++++------------------- 1 file changed, 14 insertions(+), 19 deletions(-) diff --git a/drivers/hid/hid-haptic.c b/drivers/hid/hid-haptic.c index 8760eeb..592b44e 100644 --- a/drivers/hid/hid-haptic.c +++ b/drivers/hid/hid-haptic.c @@ -419,8 +419,6 @@ int hid_haptic_init(struct hid_device *hdev, }; const char *prefix = "hid-haptic"; char *name; - int (*flush)(struct input_dev *dev, struct file *file); - int (*event)(struct input_dev *dev, unsigned int type, unsigned int code, int value); haptic->hdev = hdev; haptic->max_waveform_id = max(2u, haptic->max_waveform_id); @@ -501,11 +499,23 @@ int hid_haptic_init(struct hid_device *hdev, input_set_capability(dev, EV_FF, FF_HAPTIC); - flush = dev->flush; - event = dev->event; + if (!try_module_get(THIS_MODULE)) { + dev_err(&hdev->dev, "Failed to increase module count.\n"); + ret = -ENODEV; + goto stop_buffer_free; + } + if (!get_device(&hdev->dev)) { + dev_err(&hdev->dev, "Failed to get hdev device.\n"); + ret = -ENODEV; + module_put(THIS_MODULE); + goto stop_buffer_free; + } + ret = input_ff_create(dev, FF_MAX_EFFECTS); if (ret) { dev_err(&hdev->dev, "Failed to create ff device.\n"); + put_device(&hdev->dev); + module_put(THIS_MODULE); goto stop_buffer_free; } @@ -515,23 +525,8 @@ int hid_haptic_init(struct hid_device *hdev, ff->playback = hid_haptic_playback; ff->erase = hid_haptic_erase; ff->destroy = hid_haptic_destroy; - if (!try_module_get(THIS_MODULE)) { - dev_err(&hdev->dev, "Failed to increase module count.\n"); - goto input_free; - } - if (!get_device(&hdev->dev)) { - dev_err(&hdev->dev, "Failed to get hdev device.\n"); - module_put(THIS_MODULE); - goto input_free; - } return 0; -input_free: - input_ff_destroy(dev); - /* Restore dev flush and event */ - dev->flush = flush; - dev->event = event; - return ret; stop_buffer_free: kfree(haptic->stop_effect.report_buf); haptic->stop_effect.report_buf = NULL; -- 2.53.0 ^ permalink raw reply related [flat|nested] 16+ messages in thread
* Re: [PATCH v5 1/2] HID: haptic: reorder try_module_get/get_device before input_ff_create 2026-10-16 13:55 ` [PATCH v5 1/2] HID: haptic: reorder try_module_get/get_device before input_ff_create Aldo Ariel Panzardo @ 2026-10-16 14:06 ` sashiko-bot 0 siblings, 0 replies; 16+ messages in thread From: sashiko-bot @ 2026-10-16 14:06 UTC (permalink / raw) To: Aldo Ariel Panzardo; +Cc: linux-input, dmitry.torokhov Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider: - [High] input: missing EV_FF capability cleanup on failure leads to NULL deref -- commit 58557bc733a3a68f242cf09872b1a593ec599fc2 Author: Aldo Ariel Panzardo <qwe.aldo@gmail.com> HID: haptic: reorder try_module_get/get_device before input_ff_create This commit moves try_module_get() and get_device() before input_ff_create() in hid_haptic_init() to acquire references early. It adjusts the error handling to jump to stop_buffer_free directly on failure, removing the need to restore handlers or call input_ff_destroy(). > diff --git a/drivers/hid/hid-haptic.c b/drivers/hid/hid-haptic.c > index 8760eeb08b2cd..592b44e020d6b 100644 > --- a/drivers/hid/hid-haptic.c > +++ b/drivers/hid/hid-haptic.c [ ... ] > @@ -499,11 +499,23 @@ int hid_haptic_init(struct hid_device *hdev, > > input_set_capability(dev, EV_FF, FF_HAPTIC); > > - flush = dev->flush; > - event = dev->event; > + if (!try_module_get(THIS_MODULE)) { > + dev_err(&hdev->dev, "Failed to increase module count.\n"); > + ret = -ENODEV; > + goto stop_buffer_free; > + } [Severity: High] Does this error path leave the EV_FF capability set on the input device? Before this change, the failure paths for try_module_get() and get_device() jumped to input_free, which called input_ff_destroy(dev). By jumping to stop_buffer_free instead, the EV_FF capability remains set on the device, but dev->ff is never initialized. If hid_haptic_init() returns an error that is ignored by its caller, the partially initialized input device could be registered. When a userspace application opens the input event node and issues a force feedback ioctl, could the input core's input_ff_upload() dereference a null pointer? For example, in drivers/input/ff-core.c:input_ff_upload(): if (!test_bit(effect->type, ff->ffbit)) { Because test_bit(EV_FF, dev->evbit) is true, the input core might assume dev->ff is valid (where ff aliases dev->ff) and cause a kernel panic. > + if (!get_device(&hdev->dev)) { > + dev_err(&hdev->dev, "Failed to get hdev device.\n"); > + ret = -ENODEV; > + module_put(THIS_MODULE); > + goto stop_buffer_free; > + } > + > ret = input_ff_create(dev, FF_MAX_EFFECTS); -- Sashiko AI review · https://sashiko.dev/#/patchset/20261016135516.2957381-1-qwe.aldo@gmail.com?part=1 ^ permalink raw reply [flat|nested] 16+ messages in thread
* [PATCH v5 2/2] HID: multitouch: fix use-after-free of haptic data on delayed input release 2026-10-16 13:55 ` [PATCH v5 0/2] HID: multitouch: fix use-after-free of haptic data on delayed input release Aldo Ariel Panzardo 2026-10-16 13:55 ` [PATCH v5 1/2] HID: haptic: reorder try_module_get/get_device before input_ff_create Aldo Ariel Panzardo @ 2026-10-16 13:55 ` Aldo Ariel Panzardo 2026-10-16 14:12 ` sashiko-bot 2026-10-16 15:13 ` [PATCH v6 0/2] " Aldo Ariel Panzardo 2 siblings, 1 reply; 16+ messages in thread From: Aldo Ariel Panzardo @ 2026-10-16 13:55 UTC (permalink / raw) To: jikos, bentiss Cc: dmitry.torokhov, linux-input, linux-kernel, stable, Aldo Ariel Panzardo mt_probe() allocates td->haptic with devm_kzalloc() and hid_haptic_init() stores it in ff->private. When the HID device is removed while an evdev fd is still held, devres frees td->haptic at unbind but hid_haptic_destroy() runs later from input_dev_release() and dereferences the freed pointer. input_ff_destroy() then double-frees it via kfree(ff->private). Move all resource cleanup into a devm_add_action() callback so it runs at unbind before devres frees the struct. hid_haptic_destroy() becomes just ff->private = NULL, making the later kfree a no-op. The input device is pinned with input_get_device() to prevent concurrent release during the devres action; the workqueue is drained first while all buffers are still live. Fixes: 8d0bf7908b5a ("HID: multitouch: add haptic multitouch support") Cc: stable@vger.kernel.org Signed-off-by: Aldo Ariel Panzardo <qwe.aldo@gmail.com> --- drivers/hid/hid-haptic.c | 65 +++++++++++++++++++++++++++++++++++----- 1 file changed, 57 insertions(+), 8 deletions(-) diff --git a/drivers/hid/hid-haptic.c b/drivers/hid/hid-haptic.c index 592b44e..109b4df 100644 --- a/drivers/hid/hid-haptic.c +++ b/drivers/hid/hid-haptic.c @@ -379,32 +379,57 @@ static int hid_haptic_erase(struct input_dev *dev, int effect_id) static void hid_haptic_destroy(struct ff_device *ff) { - struct hid_haptic_device *haptic = ff->private; - struct hid_device *hdev = haptic->hdev; + /* + * Resource cleanup is handled by hid_haptic_devm_release() which + * runs at driver unbind. This callback only disconnects the FF + * subsystem's pointer so input_ff_destroy()'s kfree(ff->private) + * becomes kfree(NULL), preventing a double-free of the + * devres-managed haptic struct. + */ + ff->private = NULL; +} + +static void hid_haptic_devm_release(void *data) +{ + struct hid_haptic_device *haptic = data; + struct input_dev *dev = haptic->input_dev; int r; - if (hdev) - put_device(&hdev->dev); + /* + * Drain pending work first -- handlers dereference ff->private + * through the input_dev, so the workqueue must be idle before + * we NULL that pointer or free any buffers. + */ + if (haptic->wq) { + destroy_workqueue(haptic->wq); + haptic->wq = NULL; + } + + if (dev && dev->ff && dev->ff->private == haptic) + dev->ff->private = NULL; + + if (haptic->hdev) + put_device(&haptic->hdev->dev); kfree(haptic->stop_effect.report_buf); haptic->stop_effect.report_buf = NULL; if (haptic->effect) { - for (r = 0; r < ff->max_effects; r++) + for (r = 0; r < FF_MAX_EFFECTS; r++) kfree(haptic->effect[r].report_buf); kfree(haptic->effect); } haptic->effect = NULL; - destroy_workqueue(haptic->wq); - haptic->wq = NULL; - kfree(haptic->duration_map); haptic->duration_map = NULL; kfree(haptic->hid_usage_map); haptic->hid_usage_map = NULL; + if (dev) + input_put_device(dev); + module_put(THIS_MODULE); } @@ -419,6 +444,9 @@ int hid_haptic_init(struct hid_device *hdev, }; const char *prefix = "hid-haptic"; char *name; + int (*flush)(struct input_dev *dev, struct file *file); + int (*event)(struct input_dev *dev, unsigned int type, + unsigned int code, int value); haptic->hdev = hdev; haptic->max_waveform_id = max(2u, haptic->max_waveform_id); @@ -511,6 +539,8 @@ int hid_haptic_init(struct hid_device *hdev, goto stop_buffer_free; } + flush = dev->flush; + event = dev->event; ret = input_ff_create(dev, FF_MAX_EFFECTS); if (ret) { dev_err(&hdev->dev, "Failed to create ff device.\n"); @@ -525,6 +555,25 @@ int hid_haptic_init(struct hid_device *hdev, ff->playback = hid_haptic_playback; ff->erase = hid_haptic_erase; ff->destroy = hid_haptic_destroy; + + /* + * Pin the input device so it cannot be released concurrently + * with our devres cleanup. hid_haptic_devm_release() drops + * this reference after cleanup is complete. + */ + input_get_device(dev); + + ret = devm_add_action(&hdev->dev, hid_haptic_devm_release, haptic); + if (ret) { + input_ff_destroy(dev); + dev->flush = flush; + dev->event = event; + input_put_device(dev); + put_device(&hdev->dev); + module_put(THIS_MODULE); + goto stop_buffer_free; + } + return 0; stop_buffer_free: -- 2.53.0 ^ permalink raw reply related [flat|nested] 16+ messages in thread
* Re: [PATCH v5 2/2] HID: multitouch: fix use-after-free of haptic data on delayed input release 2026-10-16 13:55 ` [PATCH v5 2/2] HID: multitouch: fix use-after-free of haptic data on delayed input release Aldo Ariel Panzardo @ 2026-10-16 14:12 ` sashiko-bot 0 siblings, 0 replies; 16+ messages in thread From: sashiko-bot @ 2026-10-16 14:12 UTC (permalink / raw) To: Aldo Ariel Panzardo; +Cc: linux-input, dmitry.torokhov Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider: - [High] input: module code UAF due to premature module_put() on unbind - [High] hid: double-put of hdev and input_dev on multiple haptic touchpad collections -- commit 058030eab60c1764e5ac8d06b32cafc8846c4171 Author: Aldo Ariel Panzardo <qwe.aldo@gmail.com> HID: multitouch: fix use-after-free of haptic data on delayed input release This commit moves haptic resource cleanup into a devm action so it runs at driver unbind before the haptic struct is freed. It also pins the input device and delays clearing ff->private to prevent double-frees if the input device release is delayed. > diff --git a/drivers/hid/hid-haptic.c b/drivers/hid/hid-haptic.c > --- a/drivers/hid/hid-haptic.c > +++ b/drivers/hid/hid-haptic.c > @@ -379,32 +379,57 @@ static int hid_haptic_erase(struct input_dev *dev, int effect_id) [ ... ] > +static void hid_haptic_devm_release(void *data) > +{ > + struct hid_haptic_device *haptic = data; > + struct input_dev *dev = haptic->input_dev; > int r; > [ ... ] > + if (haptic->hdev) > + put_device(&haptic->hdev->dev); [Severity: High] If a composite HID device presents multiple HID_DG_TOUCHPAD application collections with haptic usages, mt_input_configured() in hid-multitouch.c will invoke hid_haptic_input_configured() and hid_haptic_init() multiple times for the same device. This causes hid_haptic_init() to queue multiple identical devm release actions using the same haptic data pointer. During unbind, this release function will execute multiple times. Because haptic->hdev is not set to NULL after the first put_device(), will this result in a double-put of the hdev reference count? [ ... ] > + if (dev) > + input_put_device(dev); [Severity: High] Similarly, the local dev variable here is populated at the start of the function from haptic->input_dev. If the release action runs multiple times for the same haptic struct, will this result in a double-put of the input_dev reference count as well? > + > + module_put(THIS_MODULE); > } [Severity: High] The devres action runs synchronously during driver unbind. If an application holds an open file descriptor to the input device, the input_dev structure and its ff_device are kept alive after unbind. Since module_put() executes here, the module's reference count drops, allowing the system to unload the HID driver module. When the application eventually closes the file descriptor, the last reference to the input_dev is dropped. This triggers input_dev_release() which calls input_ff_destroy(). input_ff_destroy() unconditionally invokes: if (ff->destroy) ff->destroy(ff); Since dev->ff->destroy was not cleared and still points to hid_haptic_destroy() inside the unloaded module, does this result in the execution of unmapped memory? -- Sashiko AI review · https://sashiko.dev/#/patchset/20261016135516.2957381-1-qwe.aldo@gmail.com?part=2 ^ permalink raw reply [flat|nested] 16+ messages in thread
* [PATCH v6 0/2] HID: multitouch: fix use-after-free of haptic data on delayed input release 2026-10-16 13:55 ` [PATCH v5 0/2] HID: multitouch: fix use-after-free of haptic data on delayed input release Aldo Ariel Panzardo 2026-10-16 13:55 ` [PATCH v5 1/2] HID: haptic: reorder try_module_get/get_device before input_ff_create Aldo Ariel Panzardo 2026-10-16 13:55 ` [PATCH v5 2/2] HID: multitouch: fix use-after-free of haptic data on delayed input release Aldo Ariel Panzardo @ 2026-10-16 15:13 ` Aldo Ariel Panzardo 2026-10-16 15:13 ` [PATCH v6 1/2] HID: haptic: reorder try_module_get/get_device before input_ff_create Aldo Ariel Panzardo ` (2 more replies) 2 siblings, 3 replies; 16+ messages in thread From: Aldo Ariel Panzardo @ 2026-10-16 15:13 UTC (permalink / raw) To: jikos, bentiss Cc: dmitry.torokhov, linux-input, linux-kernel, stable, Aldo Ariel Panzardo v6 fixes issues found by Sashiko on v5. Patch 1 is unchanged -- reorders try_module_get()/get_device() before input_ff_create(). Patch 2 keeps the devm_add_action() design. Changes since v5: - module_put moved to hid_haptic_destroy so the module stays loaded until the FF subsystem is done with the callback; a second __module_get covers hid_haptic_devm_release itself - guard against double-init (return 0 when haptic->wq is set) - NULL haptic->hdev and haptic->input_dev after put in devm_release - restore dev->flush/event after input_ff_destroy on devm_add_action failure v5: https://lore.kernel.org/linux-input/20261016135516.2957381-1-qwe.aldo@gmail.com/ v4: https://lore.kernel.org/linux-input/20261009103333.2476105-1-qwe.aldo@gmail.com/ v1: https://lore.kernel.org/linux-input/20261009031207.3233206-1-qwe.aldo@gmail.com/ Aldo Ariel Panzardo (2): HID: haptic: reorder try_module_get/get_device before input_ff_create HID: multitouch: fix use-after-free of haptic data on delayed input release drivers/hid/hid-haptic.c | 93 ++++++++++++++++++++++++++++++++++------ 1 file changed, 80 insertions(+), 13 deletions(-) -- 2.53.0 ^ permalink raw reply [flat|nested] 16+ messages in thread
* [PATCH v6 1/2] HID: haptic: reorder try_module_get/get_device before input_ff_create 2026-10-16 15:13 ` [PATCH v6 0/2] " Aldo Ariel Panzardo @ 2026-10-16 15:13 ` Aldo Ariel Panzardo 2026-10-16 15:35 ` sashiko-bot 2026-10-16 15:13 ` [PATCH v6 2/2] HID: multitouch: fix use-after-free of haptic data on delayed input release Aldo Ariel Panzardo 2026-10-16 16:33 ` [PATCH v7 0/2] " Aldo Ariel Panzardo 2 siblings, 1 reply; 16+ messages in thread From: Aldo Ariel Panzardo @ 2026-10-16 15:13 UTC (permalink / raw) To: jikos, bentiss Cc: dmitry.torokhov, linux-input, linux-kernel, stable, Aldo Ariel Panzardo Move try_module_get() and get_device() before input_ff_create() in hid_haptic_init(). The original order placed them after input_ff_create(), and failures went to input_free which called input_ff_destroy() without ever setting ret. The caller saw ret == 0 on failure and kept a dangling pointer to the haptic struct. With the references acquired first, all failure paths reach stop_buffer_free directly. The flush/event save/restore is no longer needed. Fixes: 8d0bf7908b5a ("HID: multitouch: add haptic multitouch support") Cc: stable@vger.kernel.org Signed-off-by: Aldo Ariel Panzardo <qwe.aldo@gmail.com> --- drivers/hid/hid-haptic.c | 33 ++++++++++++++------------------- 1 file changed, 14 insertions(+), 19 deletions(-) diff --git a/drivers/hid/hid-haptic.c b/drivers/hid/hid-haptic.c index 8760eeb..592b44e 100644 --- a/drivers/hid/hid-haptic.c +++ b/drivers/hid/hid-haptic.c @@ -419,8 +419,6 @@ int hid_haptic_init(struct hid_device *hdev, }; const char *prefix = "hid-haptic"; char *name; - int (*flush)(struct input_dev *dev, struct file *file); - int (*event)(struct input_dev *dev, unsigned int type, unsigned int code, int value); haptic->hdev = hdev; haptic->max_waveform_id = max(2u, haptic->max_waveform_id); @@ -501,11 +499,23 @@ int hid_haptic_init(struct hid_device *hdev, input_set_capability(dev, EV_FF, FF_HAPTIC); - flush = dev->flush; - event = dev->event; + if (!try_module_get(THIS_MODULE)) { + dev_err(&hdev->dev, "Failed to increase module count.\n"); + ret = -ENODEV; + goto stop_buffer_free; + } + if (!get_device(&hdev->dev)) { + dev_err(&hdev->dev, "Failed to get hdev device.\n"); + ret = -ENODEV; + module_put(THIS_MODULE); + goto stop_buffer_free; + } + ret = input_ff_create(dev, FF_MAX_EFFECTS); if (ret) { dev_err(&hdev->dev, "Failed to create ff device.\n"); + put_device(&hdev->dev); + module_put(THIS_MODULE); goto stop_buffer_free; } @@ -515,23 +525,8 @@ int hid_haptic_init(struct hid_device *hdev, ff->playback = hid_haptic_playback; ff->erase = hid_haptic_erase; ff->destroy = hid_haptic_destroy; - if (!try_module_get(THIS_MODULE)) { - dev_err(&hdev->dev, "Failed to increase module count.\n"); - goto input_free; - } - if (!get_device(&hdev->dev)) { - dev_err(&hdev->dev, "Failed to get hdev device.\n"); - module_put(THIS_MODULE); - goto input_free; - } return 0; -input_free: - input_ff_destroy(dev); - /* Restore dev flush and event */ - dev->flush = flush; - dev->event = event; - return ret; stop_buffer_free: kfree(haptic->stop_effect.report_buf); haptic->stop_effect.report_buf = NULL; -- 2.53.0 ^ permalink raw reply related [flat|nested] 16+ messages in thread
* Re: [PATCH v6 1/2] HID: haptic: reorder try_module_get/get_device before input_ff_create 2026-10-16 15:13 ` [PATCH v6 1/2] HID: haptic: reorder try_module_get/get_device before input_ff_create Aldo Ariel Panzardo @ 2026-10-16 15:35 ` sashiko-bot 0 siblings, 0 replies; 16+ messages in thread From: sashiko-bot @ 2026-10-16 15:35 UTC (permalink / raw) To: Aldo Ariel Panzardo; +Cc: linux-input, dmitry.torokhov Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider: - [High] input: NULL pointer dereference in evdev on EVIOCGEFFECTS when hid_haptic_init fails -- commit 2755afe0061688f07b774dbe02464fb532ab3852 Author: Aldo Ariel Panzardo <qwe.aldo@gmail.com> HID: haptic: reorder try_module_get/get_device before input_ff_create This commit moves try_module_get() and get_device() calls before input_ff_create() to fix a dangling pointer and uninitialized return value in the error path of hid_haptic_init(). > diff --git a/drivers/hid/hid-haptic.c b/drivers/hid/hid-haptic.c > index 8760eeb08b2cd..592b44e020d6b 100644 > --- a/drivers/hid/hid-haptic.c > +++ b/drivers/hid/hid-haptic.c > @@ -501,11 +499,23 @@ int hid_haptic_init(struct hid_device *hdev, > > input_set_capability(dev, EV_FF, FF_HAPTIC); > > - flush = dev->flush; > - event = dev->event; > + if (!try_module_get(THIS_MODULE)) { > + dev_err(&hdev->dev, "Failed to increase module count.\n"); > + ret = -ENODEV; > + goto stop_buffer_free; > + } > + if (!get_device(&hdev->dev)) { > + dev_err(&hdev->dev, "Failed to get hdev device.\n"); > + ret = -ENODEV; > + module_put(THIS_MODULE); > + goto stop_buffer_free; > + } > + > ret = input_ff_create(dev, FF_MAX_EFFECTS); > if (ret) { > dev_err(&hdev->dev, "Failed to create ff device.\n"); > + put_device(&hdev->dev); > + module_put(THIS_MODULE); > goto stop_buffer_free; > } [Severity: High] Can this cause a NULL pointer dereference in evdev if try_module_get() or get_device() fails? By setting EV_FF on the device unconditionally via input_set_capability() before these checks, if we hit the stop_buffer_free error path, EV_FF remains set but dev->ff is never initialized. Because the caller hid_haptic_input_configured() masks the error: drivers/hid/hid-haptic.c:hid_haptic_input_configured() { ... error = hid_haptic_init(hdev, haptic, hi->input); if (error) { dev_warn(&hdev->dev, "Cannot allocate haptic for %s\n", hdev->name); return 0; } ... } The input device will still be successfully registered with the EV_FF capability. When userspace subsequently issues an EVIOCGEFFECTS ioctl, evdev_do_ioctl() checks EV_FF and blindly dereferences dev->ff: drivers/input/evdev.c:evdev_do_ioctl() { ... case EVIOCGEFFECTS: i = test_bit(EV_FF, dev->evbit) ? dev->ff->max_effects : 0; ... } Since dev->ff is NULL, this will cause a kernel panic. Should the EV_FF capability be cleared in the stop_buffer_free error path, or only set after these checks succeed? -- Sashiko AI review · https://sashiko.dev/#/patchset/20261016151343.3120300-1-qwe.aldo@gmail.com?part=1 ^ permalink raw reply [flat|nested] 16+ messages in thread
* [PATCH v6 2/2] HID: multitouch: fix use-after-free of haptic data on delayed input release 2026-10-16 15:13 ` [PATCH v6 0/2] " Aldo Ariel Panzardo 2026-10-16 15:13 ` [PATCH v6 1/2] HID: haptic: reorder try_module_get/get_device before input_ff_create Aldo Ariel Panzardo @ 2026-10-16 15:13 ` Aldo Ariel Panzardo 2026-10-16 16:33 ` [PATCH v7 0/2] " Aldo Ariel Panzardo 2 siblings, 0 replies; 16+ messages in thread From: Aldo Ariel Panzardo @ 2026-10-16 15:13 UTC (permalink / raw) To: jikos, bentiss Cc: dmitry.torokhov, linux-input, linux-kernel, stable, Aldo Ariel Panzardo mt_probe() allocates td->haptic with devm_kzalloc() and hid_haptic_init() stores it in ff->private. When the HID device is removed while an evdev fd is still held, devres frees td->haptic at unbind but hid_haptic_destroy() runs later from input_dev_release() and dereferences the freed pointer. input_ff_destroy() then double-frees it via kfree(ff->private). Move all resource cleanup into a devm_add_action() callback so it runs at unbind before devres frees the struct. hid_haptic_destroy() becomes ff->private = NULL plus module_put() -- keeping the module loaded until the FF subsystem is done with the callback. The input device is pinned with input_get_device() to prevent concurrent release during the devres action; the workqueue is drained first while all buffers are still live. Guard against double-init when multiple touchpad collections trigger hid_haptic_init() for the same haptic struct. Fixes: 8d0bf7908b5a ("HID: multitouch: add haptic multitouch support") Cc: stable@vger.kernel.org Signed-off-by: Aldo Ariel Panzardo <qwe.aldo@gmail.com> --- drivers/hid/hid-haptic.c | 74 +++++++++++++++++++++++++++++++++++----- 1 file changed, 66 insertions(+), 8 deletions(-) diff --git a/drivers/hid/hid-haptic.c b/drivers/hid/hid-haptic.c index 592b44e..6d72d9c 100644 --- a/drivers/hid/hid-haptic.c +++ b/drivers/hid/hid-haptic.c @@ -379,32 +379,61 @@ static int hid_haptic_erase(struct input_dev *dev, int effect_id) static void hid_haptic_destroy(struct ff_device *ff) { - struct hid_haptic_device *haptic = ff->private; - struct hid_device *hdev = haptic->hdev; + /* + * Resource cleanup is handled by hid_haptic_devm_release() at + * driver unbind. Disconnect the FF pointer so input_ff_destroy()'s + * kfree(ff->private) becomes kfree(NULL). Drop the module + * reference last -- it must outlive this callback. + */ + ff->private = NULL; + module_put(THIS_MODULE); +} + +static void hid_haptic_devm_release(void *data) +{ + struct hid_haptic_device *haptic = data; + struct input_dev *dev = haptic->input_dev; int r; - if (hdev) - put_device(&hdev->dev); + /* + * Drain pending work first -- handlers dereference ff->private + * through the input_dev, so the workqueue must be idle before + * we NULL that pointer or free any buffers. + */ + if (haptic->wq) { + destroy_workqueue(haptic->wq); + haptic->wq = NULL; + } + + if (dev && dev->ff && dev->ff->private == haptic) + dev->ff->private = NULL; + + if (haptic->hdev) { + put_device(&haptic->hdev->dev); + haptic->hdev = NULL; + } kfree(haptic->stop_effect.report_buf); haptic->stop_effect.report_buf = NULL; if (haptic->effect) { - for (r = 0; r < ff->max_effects; r++) + for (r = 0; r < FF_MAX_EFFECTS; r++) kfree(haptic->effect[r].report_buf); kfree(haptic->effect); } haptic->effect = NULL; - destroy_workqueue(haptic->wq); - haptic->wq = NULL; - kfree(haptic->duration_map); haptic->duration_map = NULL; kfree(haptic->hid_usage_map); haptic->hid_usage_map = NULL; + if (dev) { + input_put_device(dev); + haptic->input_dev = NULL; + } + module_put(THIS_MODULE); } @@ -414,12 +443,17 @@ int hid_haptic_init(struct hid_device *hdev, { struct ff_device *ff; int ret = 0, r; + int (*saved_flush)(struct input_dev *, struct file *); + int (*saved_event)(struct input_dev *, unsigned int, unsigned int, int); struct ff_haptic_effect stop_effect = { .hid_usage = HID_HP_WAVEFORMSTOP & HID_USAGE, }; const char *prefix = "hid-haptic"; char *name; + if (haptic->wq) + return 0; + haptic->hdev = hdev; haptic->max_waveform_id = max(2u, haptic->max_waveform_id); haptic->max_duration_id = max(2u, haptic->max_duration_id); @@ -511,6 +545,8 @@ int hid_haptic_init(struct hid_device *hdev, goto stop_buffer_free; } + saved_flush = dev->flush; + saved_event = dev->event; ret = input_ff_create(dev, FF_MAX_EFFECTS); if (ret) { dev_err(&hdev->dev, "Failed to create ff device.\n"); @@ -525,6 +561,28 @@ int hid_haptic_init(struct hid_device *hdev, ff->playback = hid_haptic_playback; ff->erase = hid_haptic_erase; ff->destroy = hid_haptic_destroy; + + /* + * Pin the input device so it cannot be released concurrently + * with our devres cleanup, and take a second module reference + * so the module stays loaded while hid_haptic_devm_release() + * executes (the first ref is dropped by hid_haptic_destroy() + * which may run inside input_put_device() below). + */ + input_get_device(dev); + __module_get(THIS_MODULE); + + ret = devm_add_action(&hdev->dev, hid_haptic_devm_release, haptic); + if (ret) { + module_put(THIS_MODULE); + input_ff_destroy(dev); + dev->flush = saved_flush; + dev->event = saved_event; + input_put_device(dev); + put_device(&hdev->dev); + goto stop_buffer_free; + } + return 0; stop_buffer_free: -- 2.53.0 ^ permalink raw reply related [flat|nested] 16+ messages in thread
* [PATCH v7 0/2] HID: multitouch: fix use-after-free of haptic data on delayed input release 2026-10-16 15:13 ` [PATCH v6 0/2] " Aldo Ariel Panzardo 2026-10-16 15:13 ` [PATCH v6 1/2] HID: haptic: reorder try_module_get/get_device before input_ff_create Aldo Ariel Panzardo 2026-10-16 15:13 ` [PATCH v6 2/2] HID: multitouch: fix use-after-free of haptic data on delayed input release Aldo Ariel Panzardo @ 2026-10-16 16:33 ` Aldo Ariel Panzardo 2026-10-16 16:33 ` [PATCH v7 1/2] HID: haptic: reorder try_module_get/get_device before input_ff_create Aldo Ariel Panzardo ` (2 more replies) 2 siblings, 3 replies; 16+ messages in thread From: Aldo Ariel Panzardo @ 2026-10-16 16:33 UTC (permalink / raw) To: jikos, bentiss Cc: dmitry.torokhov, linux-input, linux-kernel, stable, Aldo Ariel Panzardo v7 fixes issues found by Sashiko on v5 and v6. Patch 1 now moves input_set_capability(EV_FF) after input_ff_create() so the capability is not advertised when FF setup fails -- the caller masks the error and EVIOCGEFFECTS would deref a NULL dev->ff. Patch 2 keeps the devm_add_action() design. Changes since v5: - module_put in hid_haptic_destroy (not devm_release) so the module stays loaded until FF is done; __module_get for devm_release itself - guard against double-init (return 0 when haptic->wq is set) - NULL haptic->hdev and haptic->input_dev after put in devm_release - restore dev->flush/event on devm_add_action failure v6: https://lore.kernel.org/linux-input/20261016151343.3120300-1-qwe.aldo@gmail.com/ v5: https://lore.kernel.org/linux-input/20261016135516.2957381-1-qwe.aldo@gmail.com/ v4: https://lore.kernel.org/linux-input/20261009103333.2476105-1-qwe.aldo@gmail.com/ v1: https://lore.kernel.org/linux-input/20261009031207.3233206-1-qwe.aldo@gmail.com/ Aldo Ariel Panzardo (2): HID: haptic: reorder try_module_get/get_device before input_ff_create HID: multitouch: fix use-after-free of haptic data on delayed input release drivers/hid/hid-haptic.c | 105 +++++++++++++++++++++++++++++++++------ 1 file changed, 79 insertions(+), 26 deletions(-) -- 2.53.0 ^ permalink raw reply [flat|nested] 16+ messages in thread
* [PATCH v7 1/2] HID: haptic: reorder try_module_get/get_device before input_ff_create 2026-10-16 16:33 ` [PATCH v7 0/2] " Aldo Ariel Panzardo @ 2026-10-16 16:33 ` Aldo Ariel Panzardo 2026-10-16 16:42 ` sashiko-bot 2026-10-16 16:33 ` [PATCH v7 2/2] HID: multitouch: fix use-after-free of haptic data on delayed input release Aldo Ariel Panzardo 2026-10-16 17:30 ` [PATCH v8 0/2] " Aldo Ariel Panzardo 2 siblings, 1 reply; 16+ messages in thread From: Aldo Ariel Panzardo @ 2026-10-16 16:33 UTC (permalink / raw) To: jikos, bentiss Cc: dmitry.torokhov, linux-input, linux-kernel, stable, Aldo Ariel Panzardo Move try_module_get() and get_device() before input_ff_create() in hid_haptic_init(). The original order placed them after input_ff_create(), and failures went to input_free which called input_ff_destroy() without ever setting ret. The caller saw ret == 0 on failure and kept a dangling pointer to the haptic struct. With the references acquired first, all failure paths reach stop_buffer_free directly. The flush/event save/restore is no longer needed. Also move input_set_capability(dev, EV_FF, FF_HAPTIC) after input_ff_create() so the capability is not advertised when FF setup fails -- the caller masks the error and the input device may still be registered, where EVIOCGEFFECTS would dereference a NULL dev->ff. Fixes: 8d0bf7908b5a ("HID: multitouch: add haptic multitouch support") Cc: stable@vger.kernel.org Signed-off-by: Aldo Ariel Panzardo <qwe.aldo@gmail.com> --- drivers/hid/hid-haptic.c | 35 +++++++++++++++-------------------- 1 file changed, 15 insertions(+), 20 deletions(-) diff --git a/drivers/hid/hid-haptic.c b/drivers/hid/hid-haptic.c index 8760eeb..9753bbf 100644 --- a/drivers/hid/hid-haptic.c +++ b/drivers/hid/hid-haptic.c @@ -419,8 +419,6 @@ int hid_haptic_init(struct hid_device *hdev, }; const char *prefix = "hid-haptic"; char *name; - int (*flush)(struct input_dev *dev, struct file *file); - int (*event)(struct input_dev *dev, unsigned int type, unsigned int code, int value); haptic->hdev = hdev; haptic->max_waveform_id = max(2u, haptic->max_waveform_id); @@ -499,39 +497,36 @@ int hid_haptic_init(struct hid_device *hdev, fill_effect_buf(haptic, &stop_effect, &haptic->stop_effect, HID_HAPTIC_ORDINAL_WAVEFORMSTOP); - input_set_capability(dev, EV_FF, FF_HAPTIC); + if (!try_module_get(THIS_MODULE)) { + dev_err(&hdev->dev, "Failed to increase module count.\n"); + ret = -ENODEV; + goto stop_buffer_free; + } + if (!get_device(&hdev->dev)) { + dev_err(&hdev->dev, "Failed to get hdev device.\n"); + ret = -ENODEV; + module_put(THIS_MODULE); + goto stop_buffer_free; + } - flush = dev->flush; - event = dev->event; ret = input_ff_create(dev, FF_MAX_EFFECTS); if (ret) { dev_err(&hdev->dev, "Failed to create ff device.\n"); + put_device(&hdev->dev); + module_put(THIS_MODULE); goto stop_buffer_free; } + input_set_capability(dev, EV_FF, FF_HAPTIC); + ff = dev->ff; ff->private = haptic; ff->upload = hid_haptic_upload_effect; ff->playback = hid_haptic_playback; ff->erase = hid_haptic_erase; ff->destroy = hid_haptic_destroy; - if (!try_module_get(THIS_MODULE)) { - dev_err(&hdev->dev, "Failed to increase module count.\n"); - goto input_free; - } - if (!get_device(&hdev->dev)) { - dev_err(&hdev->dev, "Failed to get hdev device.\n"); - module_put(THIS_MODULE); - goto input_free; - } return 0; -input_free: - input_ff_destroy(dev); - /* Restore dev flush and event */ - dev->flush = flush; - dev->event = event; - return ret; stop_buffer_free: kfree(haptic->stop_effect.report_buf); haptic->stop_effect.report_buf = NULL; -- 2.53.0 ^ permalink raw reply related [flat|nested] 16+ messages in thread
* Re: [PATCH v7 1/2] HID: haptic: reorder try_module_get/get_device before input_ff_create 2026-10-16 16:33 ` [PATCH v7 1/2] HID: haptic: reorder try_module_get/get_device before input_ff_create Aldo Ariel Panzardo @ 2026-10-16 16:42 ` sashiko-bot 0 siblings, 0 replies; 16+ messages in thread From: sashiko-bot @ 2026-10-16 16:42 UTC (permalink / raw) To: Aldo Ariel Panzardo; +Cc: linux-input, dmitry.torokhov Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider: - [High] input: FF_HAPTIC capability lost due to input_set_capability() reordering -- commit 212e1315f3df5903b889706323b41c9477dd030e Author: Aldo Ariel Panzardo <qwe.aldo@gmail.com> HID: haptic: reorder try_module_get/get_device before input_ff_create Move try_module_get() and get_device() before input_ff_create() in hid_haptic_init(). The original order placed them after input_ff_create(), and failures went to input_free which called input_ff_destroy() without ever setting ret. > diff --git a/drivers/hid/hid-haptic.c b/drivers/hid/hid-haptic.c > index 8760eeb08b2cd..9753bbff9d0d0 100644 > --- a/drivers/hid/hid-haptic.c > +++ b/drivers/hid/hid-haptic.c [ ... ] > @@ -499,39 +497,36 @@ int hid_haptic_init(struct hid_device *hdev, > fill_effect_buf(haptic, &stop_effect, &haptic->stop_effect, > HID_HAPTIC_ORDINAL_WAVEFORMSTOP); > > - input_set_capability(dev, EV_FF, FF_HAPTIC); > + if (!try_module_get(THIS_MODULE)) { > + dev_err(&hdev->dev, "Failed to increase module count.\n"); > + ret = -ENODEV; > + goto stop_buffer_free; > + } > + if (!get_device(&hdev->dev)) { > + dev_err(&hdev->dev, "Failed to get hdev device.\n"); > + ret = -ENODEV; > + module_put(THIS_MODULE); > + goto stop_buffer_free; > + } > > - flush = dev->flush; > - event = dev->event; > ret = input_ff_create(dev, FF_MAX_EFFECTS); > if (ret) { > dev_err(&hdev->dev, "Failed to create ff device.\n"); > + put_device(&hdev->dev); > + module_put(THIS_MODULE); > goto stop_buffer_free; > } > > + input_set_capability(dev, EV_FF, FF_HAPTIC); [Severity: High] Does moving this call after input_ff_create() break the haptic functionality? Looking at input_ff_create() in drivers/input/ff-core.c, it iterates over dev->ffbit and copies the supported effects into the newly created ff device: /* Copy "true" bits into ff device bitmap */ for_each_set_bit(i, dev->ffbit, FF_CNT) __set_bit(i, ff->ffbit); If input_set_capability() is called after input_ff_create(), FF_HAPTIC won't be present in dev->ffbit during the copy. This leaves the internal state incomplete because ff->ffbit will lack the capability, causing the input core to reject haptic effects when userspace attempts to upload them. -- Sashiko AI review · https://sashiko.dev/#/patchset/20261016163349.3293666-1-qwe.aldo@gmail.com?part=1 ^ permalink raw reply [flat|nested] 16+ messages in thread
* [PATCH v7 2/2] HID: multitouch: fix use-after-free of haptic data on delayed input release 2026-10-16 16:33 ` [PATCH v7 0/2] " Aldo Ariel Panzardo 2026-10-16 16:33 ` [PATCH v7 1/2] HID: haptic: reorder try_module_get/get_device before input_ff_create Aldo Ariel Panzardo @ 2026-10-16 16:33 ` Aldo Ariel Panzardo 2026-10-16 17:30 ` [PATCH v8 0/2] " Aldo Ariel Panzardo 2 siblings, 0 replies; 16+ messages in thread From: Aldo Ariel Panzardo @ 2026-10-16 16:33 UTC (permalink / raw) To: jikos, bentiss Cc: dmitry.torokhov, linux-input, linux-kernel, stable, Aldo Ariel Panzardo mt_probe() allocates td->haptic with devm_kzalloc() and hid_haptic_init() stores it in ff->private. When the HID device is removed while an evdev fd is still held, devres frees td->haptic at unbind but hid_haptic_destroy() runs later from input_dev_release() and dereferences the freed pointer. input_ff_destroy() then double-frees it via kfree(ff->private). Move all resource cleanup into a devm_add_action() callback so it runs at unbind before devres frees the struct. hid_haptic_destroy() becomes ff->private = NULL plus module_put() -- keeping the module loaded until the FF subsystem is done with the callback. A second __module_get() covers hid_haptic_devm_release() itself, dropped after input_put_device(). The input device is pinned with input_get_device() to prevent concurrent release during the devres action; the workqueue is drained first while all buffers are still live. Guard against double-init when multiple touchpad collections trigger hid_haptic_init() for the same haptic struct. Fixes: 8d0bf7908b5a ("HID: multitouch: add haptic multitouch support") Cc: stable@vger.kernel.org Signed-off-by: Aldo Ariel Panzardo <qwe.aldo@gmail.com> --- drivers/hid/hid-haptic.c | 74 +++++++++++++++++++++++++++++++++++----- 1 file changed, 66 insertions(+), 8 deletions(-) diff --git a/drivers/hid/hid-haptic.c b/drivers/hid/hid-haptic.c index 9753bbf..c7fafb3 100644 --- a/drivers/hid/hid-haptic.c +++ b/drivers/hid/hid-haptic.c @@ -379,32 +379,61 @@ static int hid_haptic_erase(struct input_dev *dev, int effect_id) static void hid_haptic_destroy(struct ff_device *ff) { - struct hid_haptic_device *haptic = ff->private; - struct hid_device *hdev = haptic->hdev; + /* + * Resource cleanup is handled by hid_haptic_devm_release() at + * driver unbind. Disconnect the FF pointer so input_ff_destroy()'s + * kfree(ff->private) becomes kfree(NULL). Drop the module + * reference last -- it must outlive this callback. + */ + ff->private = NULL; + module_put(THIS_MODULE); +} + +static void hid_haptic_devm_release(void *data) +{ + struct hid_haptic_device *haptic = data; + struct input_dev *dev = haptic->input_dev; int r; - if (hdev) - put_device(&hdev->dev); + /* + * Drain pending work first -- handlers dereference ff->private + * through the input_dev, so the workqueue must be idle before + * we NULL that pointer or free any buffers. + */ + if (haptic->wq) { + destroy_workqueue(haptic->wq); + haptic->wq = NULL; + } + + if (dev && dev->ff && dev->ff->private == haptic) + dev->ff->private = NULL; + + if (haptic->hdev) { + put_device(&haptic->hdev->dev); + haptic->hdev = NULL; + } kfree(haptic->stop_effect.report_buf); haptic->stop_effect.report_buf = NULL; if (haptic->effect) { - for (r = 0; r < ff->max_effects; r++) + for (r = 0; r < FF_MAX_EFFECTS; r++) kfree(haptic->effect[r].report_buf); kfree(haptic->effect); } haptic->effect = NULL; - destroy_workqueue(haptic->wq); - haptic->wq = NULL; - kfree(haptic->duration_map); haptic->duration_map = NULL; kfree(haptic->hid_usage_map); haptic->hid_usage_map = NULL; + if (dev) { + input_put_device(dev); + haptic->input_dev = NULL; + } + module_put(THIS_MODULE); } @@ -419,6 +448,11 @@ int hid_haptic_init(struct hid_device *hdev, }; const char *prefix = "hid-haptic"; char *name; + int (*saved_flush)(struct input_dev *, struct file *); + int (*saved_event)(struct input_dev *, unsigned int, unsigned int, int); + + if (haptic->wq) + return 0; haptic->hdev = hdev; haptic->max_waveform_id = max(2u, haptic->max_waveform_id); @@ -509,6 +543,8 @@ int hid_haptic_init(struct hid_device *hdev, goto stop_buffer_free; } + saved_flush = dev->flush; + saved_event = dev->event; ret = input_ff_create(dev, FF_MAX_EFFECTS); if (ret) { dev_err(&hdev->dev, "Failed to create ff device.\n"); @@ -525,6 +561,28 @@ int hid_haptic_init(struct hid_device *hdev, ff->playback = hid_haptic_playback; ff->erase = hid_haptic_erase; ff->destroy = hid_haptic_destroy; + + /* + * Pin the input device so it cannot be released concurrently + * with our devres cleanup, and take a second module reference + * so the module stays loaded while hid_haptic_devm_release() + * executes (the first ref is dropped by hid_haptic_destroy() + * which may run inside input_put_device() below). + */ + input_get_device(dev); + __module_get(THIS_MODULE); + + ret = devm_add_action(&hdev->dev, hid_haptic_devm_release, haptic); + if (ret) { + module_put(THIS_MODULE); + input_ff_destroy(dev); + dev->flush = saved_flush; + dev->event = saved_event; + input_put_device(dev); + put_device(&hdev->dev); + goto stop_buffer_free; + } + return 0; stop_buffer_free: -- 2.53.0 ^ permalink raw reply related [flat|nested] 16+ messages in thread
* [PATCH v8 0/2] HID: multitouch: fix use-after-free of haptic data on delayed input release 2026-10-16 16:33 ` [PATCH v7 0/2] " Aldo Ariel Panzardo 2026-10-16 16:33 ` [PATCH v7 1/2] HID: haptic: reorder try_module_get/get_device before input_ff_create Aldo Ariel Panzardo 2026-10-16 16:33 ` [PATCH v7 2/2] HID: multitouch: fix use-after-free of haptic data on delayed input release Aldo Ariel Panzardo @ 2026-10-16 17:30 ` Aldo Ariel Panzardo 2026-10-16 17:30 ` [PATCH v8 1/2] HID: haptic: reorder try_module_get/get_device before input_ff_create Aldo Ariel Panzardo 2026-10-16 17:30 ` [PATCH v8 2/2] HID: multitouch: fix use-after-free of haptic data on delayed input release Aldo Ariel Panzardo 2 siblings, 2 replies; 16+ messages in thread From: Aldo Ariel Panzardo @ 2026-10-16 17:30 UTC (permalink / raw) To: jikos, bentiss Cc: dmitry.torokhov, linux-input, linux-kernel, stable, Aldo Ariel Panzardo v8 fixes issues found by Sashiko on v5-v7. Patch 1 keeps input_set_capability(EV_FF) before input_ff_create() (ff->ffbit needs the bit), but clears both bits on failure via a new ff_cap_clear label so a masked error from the caller does not leave EV_FF set with a NULL dev->ff. Patch 2 keeps the devm_add_action() design. Changes since v5: - module_put in hid_haptic_destroy (not devm_release) so the module stays loaded until FF is done; __module_get for devm_release itself - guard against double-init (return 0 when haptic->wq is set) - NULL haptic->hdev and haptic->input_dev after put in devm_release - restore dev->flush/event on devm_add_action failure v7: https://lore.kernel.org/linux-input/20261016163349.3293666-1-qwe.aldo@gmail.com/ v5: https://lore.kernel.org/linux-input/20261016135516.2957381-1-qwe.aldo@gmail.com/ v4: https://lore.kernel.org/linux-input/20261009103333.2476105-1-qwe.aldo@gmail.com/ v1: https://lore.kernel.org/linux-input/20261009031207.3233206-1-qwe.aldo@gmail.com/ Aldo Ariel Panzardo (2): HID: haptic: reorder try_module_get/get_device before input_ff_create HID: multitouch: fix use-after-free of haptic data on delayed input release drivers/hid/hid-haptic.c | 108 +++++++++++++++++++++++++++++++++------ 1 file changed, 82 insertions(+), 26 deletions(-) -- 2.53.0 ^ permalink raw reply [flat|nested] 16+ messages in thread
* [PATCH v8 1/2] HID: haptic: reorder try_module_get/get_device before input_ff_create 2026-10-16 17:30 ` [PATCH v8 0/2] " Aldo Ariel Panzardo @ 2026-10-16 17:30 ` Aldo Ariel Panzardo 2026-10-16 17:30 ` [PATCH v8 2/2] HID: multitouch: fix use-after-free of haptic data on delayed input release Aldo Ariel Panzardo 1 sibling, 0 replies; 16+ messages in thread From: Aldo Ariel Panzardo @ 2026-10-16 17:30 UTC (permalink / raw) To: jikos, bentiss Cc: dmitry.torokhov, linux-input, linux-kernel, stable, Aldo Ariel Panzardo Move try_module_get() and get_device() before input_ff_create() in hid_haptic_init(). The original order placed them after input_ff_create(), and failures went to input_free which called input_ff_destroy() without ever setting ret. The caller saw ret == 0 on failure and kept a dangling pointer to the haptic struct. With the references acquired first, all failure paths reach stop_buffer_free directly. The flush/event save/restore is no longer needed. Clear the EV_FF and FF_HAPTIC capability bits on failure so a masked error from the caller does not leave the input device advertising FF support without a dev->ff -- EVIOCGEFFECTS would NULL-deref otherwise. Fixes: 8d0bf7908b5a ("HID: multitouch: add haptic multitouch support") Cc: stable@vger.kernel.org Signed-off-by: Aldo Ariel Panzardo <qwe.aldo@gmail.com> --- drivers/hid/hid-haptic.c | 38 ++++++++++++++++++-------------------- 1 file changed, 18 insertions(+), 20 deletions(-) diff --git a/drivers/hid/hid-haptic.c b/drivers/hid/hid-haptic.c index 8760eeb..b8aa4bf 100644 --- a/drivers/hid/hid-haptic.c +++ b/drivers/hid/hid-haptic.c @@ -419,8 +419,6 @@ int hid_haptic_init(struct hid_device *hdev, }; const char *prefix = "hid-haptic"; char *name; - int (*flush)(struct input_dev *dev, struct file *file); - int (*event)(struct input_dev *dev, unsigned int type, unsigned int code, int value); haptic->hdev = hdev; haptic->max_waveform_id = max(2u, haptic->max_waveform_id); @@ -501,12 +499,24 @@ int hid_haptic_init(struct hid_device *hdev, input_set_capability(dev, EV_FF, FF_HAPTIC); - flush = dev->flush; - event = dev->event; + if (!try_module_get(THIS_MODULE)) { + dev_err(&hdev->dev, "Failed to increase module count.\n"); + ret = -ENODEV; + goto ff_cap_clear; + } + if (!get_device(&hdev->dev)) { + dev_err(&hdev->dev, "Failed to get hdev device.\n"); + ret = -ENODEV; + module_put(THIS_MODULE); + goto ff_cap_clear; + } + ret = input_ff_create(dev, FF_MAX_EFFECTS); if (ret) { dev_err(&hdev->dev, "Failed to create ff device.\n"); - goto stop_buffer_free; + put_device(&hdev->dev); + module_put(THIS_MODULE); + goto ff_cap_clear; } ff = dev->ff; @@ -515,23 +525,11 @@ int hid_haptic_init(struct hid_device *hdev, ff->playback = hid_haptic_playback; ff->erase = hid_haptic_erase; ff->destroy = hid_haptic_destroy; - if (!try_module_get(THIS_MODULE)) { - dev_err(&hdev->dev, "Failed to increase module count.\n"); - goto input_free; - } - if (!get_device(&hdev->dev)) { - dev_err(&hdev->dev, "Failed to get hdev device.\n"); - module_put(THIS_MODULE); - goto input_free; - } return 0; -input_free: - input_ff_destroy(dev); - /* Restore dev flush and event */ - dev->flush = flush; - dev->event = event; - return ret; +ff_cap_clear: + __clear_bit(FF_HAPTIC, dev->ffbit); + __clear_bit(EV_FF, dev->evbit); stop_buffer_free: kfree(haptic->stop_effect.report_buf); haptic->stop_effect.report_buf = NULL; -- 2.53.0 ^ permalink raw reply related [flat|nested] 16+ messages in thread
* [PATCH v8 2/2] HID: multitouch: fix use-after-free of haptic data on delayed input release 2026-10-16 17:30 ` [PATCH v8 0/2] " Aldo Ariel Panzardo 2026-10-16 17:30 ` [PATCH v8 1/2] HID: haptic: reorder try_module_get/get_device before input_ff_create Aldo Ariel Panzardo @ 2026-10-16 17:30 ` Aldo Ariel Panzardo 1 sibling, 0 replies; 16+ messages in thread From: Aldo Ariel Panzardo @ 2026-10-16 17:30 UTC (permalink / raw) To: jikos, bentiss Cc: dmitry.torokhov, linux-input, linux-kernel, stable, Aldo Ariel Panzardo mt_probe() allocates td->haptic with devm_kzalloc() and hid_haptic_init() stores it in ff->private. When the HID device is removed while an evdev fd is still held, devres frees td->haptic at unbind but hid_haptic_destroy() runs later from input_dev_release() and dereferences the freed pointer. input_ff_destroy() then double-frees it via kfree(ff->private). Move all resource cleanup into a devm_add_action() callback so it runs at unbind before devres frees the struct. hid_haptic_destroy() becomes ff->private = NULL plus module_put() -- keeping the module loaded until the FF subsystem is done with the callback. A second __module_get() covers hid_haptic_devm_release() itself, dropped after input_put_device(). The input device is pinned with input_get_device() to prevent concurrent release during the devres action; the workqueue is drained first while all buffers are still live. Guard against double-init when multiple touchpad collections trigger hid_haptic_init() for the same haptic struct. Fixes: 8d0bf7908b5a ("HID: multitouch: add haptic multitouch support") Cc: stable@vger.kernel.org Signed-off-by: Aldo Ariel Panzardo <qwe.aldo@gmail.com> --- drivers/hid/hid-haptic.c | 74 +++++++++++++++++++++++++++++++++++----- 1 file changed, 66 insertions(+), 8 deletions(-) diff --git a/drivers/hid/hid-haptic.c b/drivers/hid/hid-haptic.c index b8aa4bf..e4e9bb9 100644 --- a/drivers/hid/hid-haptic.c +++ b/drivers/hid/hid-haptic.c @@ -379,32 +379,61 @@ static int hid_haptic_erase(struct input_dev *dev, int effect_id) static void hid_haptic_destroy(struct ff_device *ff) { - struct hid_haptic_device *haptic = ff->private; - struct hid_device *hdev = haptic->hdev; + /* + * Resource cleanup is handled by hid_haptic_devm_release() at + * driver unbind. Disconnect the FF pointer so input_ff_destroy()'s + * kfree(ff->private) becomes kfree(NULL). Drop the module + * reference last -- it must outlive this callback. + */ + ff->private = NULL; + module_put(THIS_MODULE); +} + +static void hid_haptic_devm_release(void *data) +{ + struct hid_haptic_device *haptic = data; + struct input_dev *dev = haptic->input_dev; int r; - if (hdev) - put_device(&hdev->dev); + /* + * Drain pending work first -- handlers dereference ff->private + * through the input_dev, so the workqueue must be idle before + * we NULL that pointer or free any buffers. + */ + if (haptic->wq) { + destroy_workqueue(haptic->wq); + haptic->wq = NULL; + } + + if (dev && dev->ff && dev->ff->private == haptic) + dev->ff->private = NULL; + + if (haptic->hdev) { + put_device(&haptic->hdev->dev); + haptic->hdev = NULL; + } kfree(haptic->stop_effect.report_buf); haptic->stop_effect.report_buf = NULL; if (haptic->effect) { - for (r = 0; r < ff->max_effects; r++) + for (r = 0; r < FF_MAX_EFFECTS; r++) kfree(haptic->effect[r].report_buf); kfree(haptic->effect); } haptic->effect = NULL; - destroy_workqueue(haptic->wq); - haptic->wq = NULL; - kfree(haptic->duration_map); haptic->duration_map = NULL; kfree(haptic->hid_usage_map); haptic->hid_usage_map = NULL; + if (dev) { + input_put_device(dev); + haptic->input_dev = NULL; + } + module_put(THIS_MODULE); } @@ -419,6 +448,11 @@ int hid_haptic_init(struct hid_device *hdev, }; const char *prefix = "hid-haptic"; char *name; + int (*saved_flush)(struct input_dev *, struct file *); + int (*saved_event)(struct input_dev *, unsigned int, unsigned int, int); + + if (haptic->wq) + return 0; haptic->hdev = hdev; haptic->max_waveform_id = max(2u, haptic->max_waveform_id); @@ -511,6 +545,8 @@ int hid_haptic_init(struct hid_device *hdev, goto ff_cap_clear; } + saved_flush = dev->flush; + saved_event = dev->event; ret = input_ff_create(dev, FF_MAX_EFFECTS); if (ret) { dev_err(&hdev->dev, "Failed to create ff device.\n"); @@ -525,6 +561,28 @@ int hid_haptic_init(struct hid_device *hdev, ff->playback = hid_haptic_playback; ff->erase = hid_haptic_erase; ff->destroy = hid_haptic_destroy; + + /* + * Pin the input device so it cannot be released concurrently + * with our devres cleanup, and take a second module reference + * so the module stays loaded while hid_haptic_devm_release() + * executes (the first ref is dropped by hid_haptic_destroy() + * which may run inside input_put_device() below). + */ + input_get_device(dev); + __module_get(THIS_MODULE); + + ret = devm_add_action(&hdev->dev, hid_haptic_devm_release, haptic); + if (ret) { + module_put(THIS_MODULE); + input_ff_destroy(dev); + dev->flush = saved_flush; + dev->event = saved_event; + input_put_device(dev); + put_device(&hdev->dev); + goto ff_cap_clear; + } + return 0; ff_cap_clear: -- 2.53.0 ^ permalink raw reply related [flat|nested] 16+ messages in thread
end of thread, other threads:[~2026-10-16 17:31 UTC | newest]
Thread overview: 16+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
[not found] <20261009103333.2476105-1-qwe.aldo@gmail.com>
2026-10-16 13:55 ` [PATCH v5 0/2] HID: multitouch: fix use-after-free of haptic data on delayed input release Aldo Ariel Panzardo
2026-10-16 13:55 ` [PATCH v5 1/2] HID: haptic: reorder try_module_get/get_device before input_ff_create Aldo Ariel Panzardo
2026-10-16 14:06 ` sashiko-bot
2026-10-16 13:55 ` [PATCH v5 2/2] HID: multitouch: fix use-after-free of haptic data on delayed input release Aldo Ariel Panzardo
2026-10-16 14:12 ` sashiko-bot
2026-10-16 15:13 ` [PATCH v6 0/2] " Aldo Ariel Panzardo
2026-10-16 15:13 ` [PATCH v6 1/2] HID: haptic: reorder try_module_get/get_device before input_ff_create Aldo Ariel Panzardo
2026-10-16 15:35 ` sashiko-bot
2026-10-16 15:13 ` [PATCH v6 2/2] HID: multitouch: fix use-after-free of haptic data on delayed input release Aldo Ariel Panzardo
2026-10-16 16:33 ` [PATCH v7 0/2] " Aldo Ariel Panzardo
2026-10-16 16:33 ` [PATCH v7 1/2] HID: haptic: reorder try_module_get/get_device before input_ff_create Aldo Ariel Panzardo
2026-10-16 16:42 ` sashiko-bot
2026-10-16 16:33 ` [PATCH v7 2/2] HID: multitouch: fix use-after-free of haptic data on delayed input release Aldo Ariel Panzardo
2026-10-16 17:30 ` [PATCH v8 0/2] " Aldo Ariel Panzardo
2026-10-16 17:30 ` [PATCH v8 1/2] HID: haptic: reorder try_module_get/get_device before input_ff_create Aldo Ariel Panzardo
2026-10-16 17:30 ` [PATCH v8 2/2] HID: multitouch: fix use-after-free of haptic data on delayed input release Aldo Ariel Panzardo
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox