* [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; 15+ 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] 15+ 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 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, 0 replies; 15+ 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 [flat|nested] 15+ 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 15:13 ` [PATCH v6 0/2] " Aldo Ariel Panzardo 2 siblings, 0 replies; 15+ 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 [flat|nested] 15+ 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; 15+ 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] 15+ 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: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, 0 replies; 15+ 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 [flat|nested] 15+ 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; 15+ 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 [flat|nested] 15+ 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; 15+ 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] 15+ 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 18:16 ` Dmitry Torokhov 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; 15+ 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 [flat|nested] 15+ 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 18:16 ` Dmitry Torokhov 0 siblings, 0 replies; 15+ messages in thread From: Dmitry Torokhov @ 2026-10-16 18:16 UTC (permalink / raw) To: Aldo Ariel Panzardo; +Cc: jikos, bentiss, linux-input, linux-kernel, stable Hi Aldo, On Fri, Oct 16, 2026 at 01:33:48PM -0300, Aldo Ariel Panzardo wrote: > 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. How about we simply stop pretending that these can fail? I.e. replace try_module_get() with __module_get() As far as get_device() it cannot fail and is redundant: - get_device(&hdev->dev) only returns NULL if its argument is NULL, which is impossible here since hdev was already dereferenced throughout hid_haptic_init() - In hidinput_allocate(), input_dev->dev.parent is set to &hdev->dev. The driver core will already hold a reference to &hdev->dev (dev->parent) that will only released in device_release() after input_dev_release() -> input_ff_destroy() -> hid_haptic_destroy() finishes. Additionally I think we should simply fail probe if hid_haptic_init() fails instead of trying to limp along without it. Thanks. -- Dmitry ^ permalink raw reply [flat|nested] 15+ 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; 15+ 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 [flat|nested] 15+ 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; 15+ 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] 15+ 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-17 11:49 ` kernel test robot 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, 1 reply; 15+ 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 [flat|nested] 15+ messages in thread
* Re: [PATCH v8 1/2] HID: haptic: reorder try_module_get/get_device before input_ff_create 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-17 11:49 ` kernel test robot 0 siblings, 0 replies; 15+ messages in thread From: kernel test robot @ 2026-10-17 11:49 UTC (permalink / raw) To: Aldo Ariel Panzardo, jikos, bentiss Cc: oe-kbuild-all, dmitry.torokhov, linux-input, linux-kernel, stable, Aldo Ariel Panzardo Hi Aldo, kernel test robot noticed the following build warnings: [auto build test WARNING on hid/for-next] [also build test WARNING on linus/master v7.3-rc7 next-20261016] [If your patch is applied to the wrong git tree, kindly drop us a note. And when submitting patch, we suggest to use '--base' as documented in https://git-scm.com/docs/git-format-patch#_base_tree_information] url: https://github.com/intel-lab-lkp/linux/commits/Aldo-Ariel-Panzardo/HID-multitouch-fix-use-after-free-of-haptic-data-on-delayed-input-release/20261016-143056 base: https://git.kernel.org/pub/scm/linux/kernel/git/hid/hid.git for-next patch link: https://lore.kernel.org/r/20261016173056.3397211-2-qwe.aldo%40gmail.com patch subject: [PATCH v8 1/2] HID: haptic: reorder try_module_get/get_device before input_ff_create config: arc-randconfig-1001-20261017 (https://download.01.org/0day-ci/archive/20261017/202610171937.9UEsJzAu-lkp@intel.com/config) compiler: arc-linux-gcc (GCC) 10.5.0 reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20261017/202610171937.9UEsJzAu-lkp@intel.com/reproduce) If you fix the issue in a separate patch/commit (i.e. not just a new version of the same patch/commit), kindly add following tags | Reported-by: kernel test robot <lkp@intel.com> | Closes: https://lore.kernel.org/oe-kbuild-all/202610171937.9UEsJzAu-lkp@intel.com/ All warnings (new ones prefixed by >>): drivers/hid/hid-haptic.c: In function 'hid_haptic_init': >> drivers/hid/hid-haptic.c:533:1: warning: label 'stop_buffer_free' defined but not used [-Wunused-label] 533 | stop_buffer_free: | ^~~~~~~~~~~~~~~~ vim +/stop_buffer_free +533 drivers/hid/hid-haptic.c 344ff358495706a Angela Czubak 2025-08-18 410 344ff358495706a Angela Czubak 2025-08-18 411 int hid_haptic_init(struct hid_device *hdev, 7939f787f450d93 Dmitry Torokhov 2026-08-03 412 struct hid_haptic_device *haptic, 7939f787f450d93 Dmitry Torokhov 2026-08-03 413 struct input_dev *dev) 344ff358495706a Angela Czubak 2025-08-18 414 { 344ff358495706a Angela Czubak 2025-08-18 415 struct ff_device *ff; 344ff358495706a Angela Czubak 2025-08-18 416 int ret = 0, r; 344ff358495706a Angela Czubak 2025-08-18 417 struct ff_haptic_effect stop_effect = { 344ff358495706a Angela Czubak 2025-08-18 418 .hid_usage = HID_HP_WAVEFORMSTOP & HID_USAGE, 344ff358495706a Angela Czubak 2025-08-18 419 }; 344ff358495706a Angela Czubak 2025-08-18 420 const char *prefix = "hid-haptic"; 344ff358495706a Angela Czubak 2025-08-18 421 char *name; 344ff358495706a Angela Czubak 2025-08-18 422 344ff358495706a Angela Czubak 2025-08-18 423 haptic->hdev = hdev; 344ff358495706a Angela Czubak 2025-08-18 424 haptic->max_waveform_id = max(2u, haptic->max_waveform_id); 344ff358495706a Angela Czubak 2025-08-18 425 haptic->max_duration_id = max(2u, haptic->max_duration_id); 344ff358495706a Angela Czubak 2025-08-18 426 344ff358495706a Angela Czubak 2025-08-18 427 haptic->hid_usage_map = kcalloc(haptic->max_waveform_id + 1, 344ff358495706a Angela Czubak 2025-08-18 428 sizeof(u16), GFP_KERNEL); 344ff358495706a Angela Czubak 2025-08-18 429 if (!haptic->hid_usage_map) { 344ff358495706a Angela Czubak 2025-08-18 430 ret = -ENOMEM; 344ff358495706a Angela Czubak 2025-08-18 431 goto exit; 344ff358495706a Angela Czubak 2025-08-18 432 } 344ff358495706a Angela Czubak 2025-08-18 433 haptic->duration_map = kcalloc(haptic->max_duration_id + 1, 344ff358495706a Angela Czubak 2025-08-18 434 sizeof(u32), GFP_KERNEL); 344ff358495706a Angela Czubak 2025-08-18 435 if (!haptic->duration_map) { 344ff358495706a Angela Czubak 2025-08-18 436 ret = -ENOMEM; 344ff358495706a Angela Czubak 2025-08-18 437 goto usage_map; 344ff358495706a Angela Czubak 2025-08-18 438 } 344ff358495706a Angela Czubak 2025-08-18 439 344ff358495706a Angela Czubak 2025-08-18 440 if (haptic->max_waveform_id != haptic->max_duration_id) 344ff358495706a Angela Czubak 2025-08-18 441 dev_warn(&hdev->dev, 344ff358495706a Angela Czubak 2025-08-18 442 "Haptic duration and waveform lists have different max id (%u and %u).\n", 344ff358495706a Angela Czubak 2025-08-18 443 haptic->max_duration_id, haptic->max_waveform_id); 344ff358495706a Angela Czubak 2025-08-18 444 344ff358495706a Angela Czubak 2025-08-18 445 haptic->hid_usage_map[HID_HAPTIC_ORDINAL_WAVEFORMNONE] = 344ff358495706a Angela Czubak 2025-08-18 446 HID_HP_WAVEFORMNONE & HID_USAGE; 344ff358495706a Angela Czubak 2025-08-18 447 haptic->hid_usage_map[HID_HAPTIC_ORDINAL_WAVEFORMSTOP] = 344ff358495706a Angela Czubak 2025-08-18 448 HID_HP_WAVEFORMSTOP & HID_USAGE; 344ff358495706a Angela Czubak 2025-08-18 449 ff66b8eebbab371 Angela Czubak 2025-08-18 450 mutex_init(&haptic->auto_trigger_mutex); 344ff358495706a Angela Czubak 2025-08-18 451 for (r = 0; r < haptic->auto_trigger_report->maxfield; r++) 344ff358495706a Angela Czubak 2025-08-18 452 parse_auto_trigger_field(haptic, haptic->auto_trigger_report->field[r]); 344ff358495706a Angela Czubak 2025-08-18 453 344ff358495706a Angela Czubak 2025-08-18 454 haptic->input_dev = dev; 344ff358495706a Angela Czubak 2025-08-18 455 haptic->manual_trigger_report_len = 344ff358495706a Angela Czubak 2025-08-18 456 hid_report_len(haptic->manual_trigger_report); 344ff358495706a Angela Czubak 2025-08-18 457 mutex_init(&haptic->manual_trigger_mutex); 344ff358495706a Angela Czubak 2025-08-18 458 name = kmalloc(strlen(prefix) + strlen(hdev->name) + 2, GFP_KERNEL); 344ff358495706a Angela Czubak 2025-08-18 459 if (name) { 344ff358495706a Angela Czubak 2025-08-18 460 sprintf(name, "%s %s", prefix, hdev->name); 344ff358495706a Angela Czubak 2025-08-18 461 haptic->wq = create_singlethread_workqueue(name); 344ff358495706a Angela Czubak 2025-08-18 462 kfree(name); 344ff358495706a Angela Czubak 2025-08-18 463 } 344ff358495706a Angela Czubak 2025-08-18 464 if (!haptic->wq) { 344ff358495706a Angela Czubak 2025-08-18 465 ret = -ENOMEM; 344ff358495706a Angela Czubak 2025-08-18 466 goto duration_map; 344ff358495706a Angela Czubak 2025-08-18 467 } 32a92f8c8932698 Linus Torvalds 2026-02-21 468 haptic->effect = kzalloc_objs(struct hid_haptic_effect, FF_MAX_EFFECTS); 344ff358495706a Angela Czubak 2025-08-18 469 if (!haptic->effect) { 344ff358495706a Angela Czubak 2025-08-18 470 ret = -ENOMEM; 344ff358495706a Angela Czubak 2025-08-18 471 goto output_queue; 344ff358495706a Angela Czubak 2025-08-18 472 } 344ff358495706a Angela Czubak 2025-08-18 473 for (r = 0; r < FF_MAX_EFFECTS; r++) { 344ff358495706a Angela Czubak 2025-08-18 474 haptic->effect[r].report_buf = 344ff358495706a Angela Czubak 2025-08-18 475 hid_alloc_report_buf(haptic->manual_trigger_report, 344ff358495706a Angela Czubak 2025-08-18 476 GFP_KERNEL); 344ff358495706a Angela Czubak 2025-08-18 477 if (!haptic->effect[r].report_buf) { 344ff358495706a Angela Czubak 2025-08-18 478 dev_err(&hdev->dev, 344ff358495706a Angela Czubak 2025-08-18 479 "Failed to allocate a buffer for an effect.\n"); 344ff358495706a Angela Czubak 2025-08-18 480 ret = -ENOMEM; 344ff358495706a Angela Czubak 2025-08-18 481 goto buffer_free; 344ff358495706a Angela Czubak 2025-08-18 482 } 344ff358495706a Angela Czubak 2025-08-18 483 haptic->effect[r].input_dev = dev; 344ff358495706a Angela Czubak 2025-08-18 484 INIT_WORK(&haptic->effect[r].work, haptic_work_handler); 344ff358495706a Angela Czubak 2025-08-18 485 } 344ff358495706a Angela Czubak 2025-08-18 486 haptic->stop_effect.report_buf = 344ff358495706a Angela Czubak 2025-08-18 487 hid_alloc_report_buf(haptic->manual_trigger_report, 344ff358495706a Angela Czubak 2025-08-18 488 GFP_KERNEL); 344ff358495706a Angela Czubak 2025-08-18 489 if (!haptic->stop_effect.report_buf) { 344ff358495706a Angela Czubak 2025-08-18 490 dev_err(&hdev->dev, 344ff358495706a Angela Czubak 2025-08-18 491 "Failed to allocate a buffer for stop effect.\n"); 344ff358495706a Angela Czubak 2025-08-18 492 ret = -ENOMEM; 344ff358495706a Angela Czubak 2025-08-18 493 goto buffer_free; 344ff358495706a Angela Czubak 2025-08-18 494 } 344ff358495706a Angela Czubak 2025-08-18 495 haptic->stop_effect.input_dev = dev; 344ff358495706a Angela Czubak 2025-08-18 496 INIT_WORK(&haptic->stop_effect.work, haptic_work_handler); 344ff358495706a Angela Czubak 2025-08-18 497 fill_effect_buf(haptic, &stop_effect, &haptic->stop_effect, 344ff358495706a Angela Czubak 2025-08-18 498 HID_HAPTIC_ORDINAL_WAVEFORMSTOP); 344ff358495706a Angela Czubak 2025-08-18 499 344ff358495706a Angela Czubak 2025-08-18 500 input_set_capability(dev, EV_FF, FF_HAPTIC); 344ff358495706a Angela Czubak 2025-08-18 501 7668dc668064c7a Aldo Ariel Panzardo 2026-10-16 502 if (!try_module_get(THIS_MODULE)) { 7668dc668064c7a Aldo Ariel Panzardo 2026-10-16 503 dev_err(&hdev->dev, "Failed to increase module count.\n"); 7668dc668064c7a Aldo Ariel Panzardo 2026-10-16 504 ret = -ENODEV; 7668dc668064c7a Aldo Ariel Panzardo 2026-10-16 505 goto ff_cap_clear; 7668dc668064c7a Aldo Ariel Panzardo 2026-10-16 506 } 7668dc668064c7a Aldo Ariel Panzardo 2026-10-16 507 if (!get_device(&hdev->dev)) { 7668dc668064c7a Aldo Ariel Panzardo 2026-10-16 508 dev_err(&hdev->dev, "Failed to get hdev device.\n"); 7668dc668064c7a Aldo Ariel Panzardo 2026-10-16 509 ret = -ENODEV; 7668dc668064c7a Aldo Ariel Panzardo 2026-10-16 510 module_put(THIS_MODULE); 7668dc668064c7a Aldo Ariel Panzardo 2026-10-16 511 goto ff_cap_clear; 7668dc668064c7a Aldo Ariel Panzardo 2026-10-16 512 } 7668dc668064c7a Aldo Ariel Panzardo 2026-10-16 513 344ff358495706a Angela Czubak 2025-08-18 514 ret = input_ff_create(dev, FF_MAX_EFFECTS); 344ff358495706a Angela Czubak 2025-08-18 515 if (ret) { 344ff358495706a Angela Czubak 2025-08-18 516 dev_err(&hdev->dev, "Failed to create ff device.\n"); 7668dc668064c7a Aldo Ariel Panzardo 2026-10-16 517 put_device(&hdev->dev); 7668dc668064c7a Aldo Ariel Panzardo 2026-10-16 518 module_put(THIS_MODULE); 7668dc668064c7a Aldo Ariel Panzardo 2026-10-16 519 goto ff_cap_clear; 344ff358495706a Angela Czubak 2025-08-18 520 } 344ff358495706a Angela Czubak 2025-08-18 521 344ff358495706a Angela Czubak 2025-08-18 522 ff = dev->ff; 344ff358495706a Angela Czubak 2025-08-18 523 ff->private = haptic; 344ff358495706a Angela Czubak 2025-08-18 524 ff->upload = hid_haptic_upload_effect; 344ff358495706a Angela Czubak 2025-08-18 525 ff->playback = hid_haptic_playback; 344ff358495706a Angela Czubak 2025-08-18 526 ff->erase = hid_haptic_erase; 344ff358495706a Angela Czubak 2025-08-18 527 ff->destroy = hid_haptic_destroy; 344ff358495706a Angela Czubak 2025-08-18 528 return 0; 344ff358495706a Angela Czubak 2025-08-18 529 7668dc668064c7a Aldo Ariel Panzardo 2026-10-16 530 ff_cap_clear: 7668dc668064c7a Aldo Ariel Panzardo 2026-10-16 531 __clear_bit(FF_HAPTIC, dev->ffbit); 7668dc668064c7a Aldo Ariel Panzardo 2026-10-16 532 __clear_bit(EV_FF, dev->evbit); 344ff358495706a Angela Czubak 2025-08-18 @533 stop_buffer_free: 344ff358495706a Angela Czubak 2025-08-18 534 kfree(haptic->stop_effect.report_buf); 344ff358495706a Angela Czubak 2025-08-18 535 haptic->stop_effect.report_buf = NULL; 344ff358495706a Angela Czubak 2025-08-18 536 buffer_free: 344ff358495706a Angela Czubak 2025-08-18 537 while (--r >= 0) 344ff358495706a Angela Czubak 2025-08-18 538 kfree(haptic->effect[r].report_buf); 344ff358495706a Angela Czubak 2025-08-18 539 kfree(haptic->effect); 344ff358495706a Angela Czubak 2025-08-18 540 haptic->effect = NULL; 344ff358495706a Angela Czubak 2025-08-18 541 output_queue: 344ff358495706a Angela Czubak 2025-08-18 542 destroy_workqueue(haptic->wq); 344ff358495706a Angela Czubak 2025-08-18 543 haptic->wq = NULL; 344ff358495706a Angela Czubak 2025-08-18 544 duration_map: 344ff358495706a Angela Czubak 2025-08-18 545 kfree(haptic->duration_map); 344ff358495706a Angela Czubak 2025-08-18 546 haptic->duration_map = NULL; 344ff358495706a Angela Czubak 2025-08-18 547 usage_map: 344ff358495706a Angela Czubak 2025-08-18 548 kfree(haptic->hid_usage_map); 344ff358495706a Angela Czubak 2025-08-18 549 haptic->hid_usage_map = NULL; 344ff358495706a Angela Czubak 2025-08-18 550 exit: 344ff358495706a Angela Czubak 2025-08-18 551 return ret; 344ff358495706a Angela Czubak 2025-08-18 552 } 344ff358495706a Angela Czubak 2025-08-18 553 EXPORT_SYMBOL_GPL(hid_haptic_init); a77efca3aa85e38 Angela Czubak 2025-08-18 554 -- 0-DAY CI Kernel Test Service https://github.com/intel/lkp-tests/wiki ^ permalink raw reply [flat|nested] 15+ 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 2026-10-16 18:27 ` Dmitry Torokhov 1 sibling, 1 reply; 15+ 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 [flat|nested] 15+ messages in thread
* Re: [PATCH v8 2/2] HID: multitouch: fix use-after-free of haptic data on delayed input release 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 @ 2026-10-16 18:27 ` Dmitry Torokhov 0 siblings, 0 replies; 15+ messages in thread From: Dmitry Torokhov @ 2026-10-16 18:27 UTC (permalink / raw) To: Aldo Ariel Panzardo; +Cc: jikos, bentiss, linux-input, linux-kernel, stable On Fri, Oct 16, 2026 at 02:30:56PM -0300, Aldo Ariel Panzardo wrote: > 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). Right, so that is the root of the problem. We should not be allocating memory assigned to ff->private with devm as its lifetime is not directly tied to the time when device is bound to a driver. Your initial approach was correct one. Thanks. -- Dmitry ^ permalink raw reply [flat|nested] 15+ messages in thread
end of thread, other threads:[~2026-10-17 11:49 UTC | newest]
Thread overview: 15+ 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 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
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 ` [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 18:16 ` Dmitry Torokhov
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-17 11:49 ` kernel test robot
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
2026-10-16 18:27 ` Dmitry Torokhov
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®