* [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; 14+ 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] 14+ 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; 14+ 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] 14+ 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; 14+ 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] 14+ 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; 14+ 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] 14+ 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; 14+ 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] 14+ 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; 14+ 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] 14+ 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; 14+ 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] 14+ 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; 14+ 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] 14+ 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; 14+ 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] 14+ 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; 14+ 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] 14+ 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; 14+ 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] 14+ 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; 14+ 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] 14+ 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; 14+ 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] 14+ 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; 14+ 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] 14+ messages in thread
end of thread, other threads:[~2026-10-16 18:27 UTC | newest]
Thread overview: 14+ 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-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®