Linux Input/HID development
 help / color / mirror / Atom feed
* [PATCH] HID: roccat: free buffered reports when destroying device
@ 2026-07-31  8:49 raoxu
  2026-07-31  9:03 ` sashiko-bot
  0 siblings, 1 reply; 2+ messages in thread
From: raoxu @ 2026-07-31  8:49 UTC (permalink / raw)
  To: erazor_de; +Cc: jikos, bentiss, linux-input, linux-kernel, raoxu, stable

From: Xu Rao <raoxu@uniontech.com>

roccat_report_event() duplicates each report with kmemdup() and stores
the allocation in a circular-buffer slot. The allocation is released only
when that slot is reused.

The device destruction paths free struct roccat_device without releasing
reports still stored in cbuf[]. This makes those allocations unreachable
and leaks up to ROCCAT_CBUF_SIZE report buffers per device.

Add a small destructor that frees every buffered report before freeing the
device, and use it in both paths that can destroy a registered device.

Fixes: 206f5f2fcb5f ("HID: roccat: propagate special events of roccat hardware to userspace")
Cc: stable@vger.kernel.org
Signed-off-by: Xu Rao <raoxu@uniontech.com>
---
 drivers/hid/hid-roccat.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/drivers/hid/hid-roccat.c b/drivers/hid/hid-roccat.c
index d6fff53d4ee7..4f15eb951039 100644
--- a/drivers/hid/hid-roccat.c
+++ b/drivers/hid/hid-roccat.c
@@ -70,6 +70,15 @@ static struct roccat_device *devices[ROCCAT_MAX_DEVICES];
 /* protects modifications of devices array */
 static DEFINE_MUTEX(devices_lock);

+static void roccat_free_device(struct roccat_device *device)
+{
+	int i;
+
+	for (i = 0; i < ROCCAT_CBUF_SIZE; i++)
+		kfree(device->cbuf[i].value);
+	kfree(device);
+}
+
 static ssize_t roccat_read(struct file *file, char __user *buffer,
 		size_t count, loff_t *ppos)
 {
@@ -226,7 +235,7 @@ static int roccat_release(struct inode *inode, struct file *file)
 			hid_hw_power(device->hid, PM_HINT_NORMAL);
 			hid_hw_close(device->hid);
 		} else {
-			kfree(device);
+			roccat_free_device(device);
 		}
 	}

@@ -374,7 +383,7 @@ void roccat_disconnect(int minor)
 		hid_hw_close(device->hid);
 		wake_up_interruptible(&device->wait);
 	} else {
-		kfree(device);
+		roccat_free_device(device);
 	}
 }
 EXPORT_SYMBOL_GPL(roccat_disconnect);
--
2.50.1


^ permalink raw reply related	[flat|nested] 2+ messages in thread

* Re: [PATCH] HID: roccat: free buffered reports when destroying device
  2026-07-31  8:49 [PATCH] HID: roccat: free buffered reports when destroying device raoxu
@ 2026-07-31  9:03 ` sashiko-bot
  0 siblings, 0 replies; 2+ messages in thread
From: sashiko-bot @ 2026-07-31  9:03 UTC (permalink / raw)
  To: raoxu; +Cc: dmitry.torokhov, linux-input

Thank you for your contribution! Sashiko AI review found 5 potential issue(s) to consider:

Pre-existing issues:
- [High] Concurrent execution of `roccat_disconnect()` and `roccat_release()` leads to Use-After-Free and Double-Free vulnerabilities.
- [High] `roccat_release()` fails to free disconnected open devices due to a faulty array lookup, leaking memory and corrupting state if the minor number is reused.
- [High] Incoming HID events during or immediately after device removal trigger Use-After-Free, Double-Free, and panics due to missing teardown synchronization.
- [High] `roccat_report_event()` acquires sleeping locks in an atomic hardware interrupt context.
- [High] `roccat_connect()` publishes the device globally before initializing its mutexes and lists, leading to uninitialized use crashes.
--

--- Patch [1]: [PATCH] HID: roccat: free buffered reports when destroying device ---
commit dad9381303bffdf0e85a009b8168cbabdcb74643
Author: Xu Rao <raoxu@uniontech.com>

HID: roccat: free buffered reports when destroying device

This commit adds a destructor that frees every buffered report before freeing
the device. It uses this new destructor in both paths that destroy a
registered device to prevent leaking up to ROCCAT_CBUF_SIZE report buffers
per device.

Cc: stable@vger.kernel.org

> diff --git a/drivers/hid/hid-roccat.c b/drivers/hid/hid-roccat.c
> index d6fff53d4ee7a..4f15eb9510396 100644
> --- a/drivers/hid/hid-roccat.c
> +++ b/drivers/hid/hid-roccat.c
> @@ -70,6 +70,15 @@ static struct roccat_device *devices[ROCCAT_MAX_DEVICES];
>  /* protects modifications of devices array */
>  static DEFINE_MUTEX(devices_lock);
>  
> +static void roccat_free_device(struct roccat_device *device)
> +{
> +	int i;
> +
> +	for (i = 0; i < ROCCAT_CBUF_SIZE; i++)
> +		kfree(device->cbuf[i].value);

[Severity: High]
This is a pre-existing issue, but does this lockless iteration race with the
interrupt handler?

If a sub-driver like hid-roccat-kone triggers roccat_disconnect() and frees
its state before shutting down hardware events, HID interrupts can continue
firing during teardown.

Could this lockless kfree() race with an active interrupt handler modifying
the buffer, resulting in an immediate double-free of the buffer memory?

> +	kfree(device);
> +}
> +

[ ... ]

> @@ -226,7 +235,7 @@ static int roccat_release(struct inode *inode, struct file *file)
>  			hid_hw_power(device->hid, PM_HINT_NORMAL);
>  			hid_hw_close(device->hid);
>  		} else {
> -			kfree(device);
> +			roccat_free_device(device);
>  		}
>  	}

[Severity: High]
This is a pre-existing issue, but does roccat_release() fail to free
disconnected open devices due to the device lookup earlier in this function?

When an open device is unplugged, roccat_disconnect() sets
devices[minor] = NULL.

Later, when the file descriptor is closed, roccat_release() looks up the
device:

drivers/hid/hid-roccat.c:roccat_release() {
    ...
    device = devices[minor];
    if (!device) {
        mutex_unlock(&devices_lock);
        pr_emerg("roccat device with minor %d doesn't exist\n", minor);
        return -ENODEV;
    }
    ...
}

Since it is NULL, won't the function return -ENODEV early and skip this
cleanup logic entirely?

Furthermore, if a new Roccat device is plugged in before the descriptor is
closed, it reuses the same minor number. Does this mean roccat_release() will
fetch the new device, incorrectly decrement its open count, and remove the
old reader from the new device's list?

[ ... ]

> @@ -374,7 +383,7 @@ void roccat_disconnect(int minor)
>  		hid_hw_close(device->hid);
>  		wake_up_interruptible(&device->wait);
>  	} else {
> -		kfree(device);
> +		roccat_free_device(device);
>  	}
>  }
>  EXPORT_SYMBOL_GPL(roccat_disconnect);

[Severity: High]
This is a pre-existing issue, but can roccat_disconnect() and
roccat_release() race to cause a use-after-free and double-free?

In roccat_disconnect(), the global devices_lock is dropped before checking
device->open:

drivers/hid/hid-roccat.c:roccat_disconnect() {
    ...
    device->exist = 0;
    device_destroy(device->dev->class, MKDEV(roccat_major, minor));
    
    mutex_lock(&devices_lock);
    devices[minor] = NULL;
    mutex_unlock(&devices_lock);
    
    if (device->open) {
    ...
}

If a user closes an open file descriptor exactly when the device is being
hot-unplugged, could roccat_release() acquire the lock during this window,
see exist == 0, and free the device?

If so, when roccat_disconnect() continues, it would access the freed device
to check device->open, and potentially call roccat_free_device() a second
time.

[Severity: High]
This is a pre-existing issue, but does roccat_report_event() acquire
sleeping locks in an atomic context?

This function runs from the HID raw_event callback in atomic/softirq context.
However, it attempts to acquire mutexes:

drivers/hid/hid-roccat.c:roccat_report_event() {
    ...
    mutex_lock(&device->readers_lock);
    mutex_lock(&device->cbuf_lock);
    ...
}

Since mutexes can sleep, does this cause scheduling while atomic panics when
hardware events arrive?

[Severity: High]
This is a pre-existing issue, but does roccat_connect() publish the device
globally before its fundamental structures are initialized?

The device pointer is made accessible to interrupt handlers early:

drivers/hid/hid-roccat.c:roccat_connect() {
    ...
    if (minor < ROCCAT_MAX_DEVICES) {
        devices[minor] = device;
    ...
    INIT_LIST_HEAD(&device->readers);
    mutex_init(&device->readers_lock);
    ...
}

Because hid_hw_start() is called by sub-drivers prior to roccat_connect(),
hardware events are already active. If an interrupt fires during this narrow
window, could roccat_report_event() fetch the device and attempt to lock
uninitialized mutexes or iterate an uninitialized readers list?

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/B93A126D2A126AAD+20260731084926.1771993-1-raoxu@uniontech.com?part=1

^ permalink raw reply	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2026-07-31  9:03 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-31  8:49 [PATCH] HID: roccat: free buffered reports when destroying device raoxu
2026-07-31  9:03 ` sashiko-bot

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox