* [PATCH 0/2] HID: roccat: bound device-supplied profile index
@ 2026-06-18 3:00 Michael Bommarito
2026-06-18 3:00 ` [PATCH 1/2] " Michael Bommarito
2026-06-18 3:00 ` [PATCH 2/2] HID: roccat: add KUnit test for kone profile-index bounds Michael Bommarito
0 siblings, 2 replies; 5+ messages in thread
From: Michael Bommarito @ 2026-06-18 3:00 UTC (permalink / raw)
To: Stefan Achatz, Jiri Kosina, Benjamin Tissoires; +Cc: linux-input, linux-kernel
The Roccat Kone driver uses an 8-bit value taken straight from a USB HID
interrupt report as an index into a fixed 5-element profiles[] array,
without any range check. A malicious or counterfeit device that claims
the Roccat Kone VID/PID can send a "switch profile" report with an
out-of-range value and make the driver read out of bounds; the same
unbounded index is also reachable at probe time from a device-supplied
startup_profile field. The read result is stored in actual_dpi and
exposed to user space through the actual_dpi sysfs attribute.
Michael Bommarito (2):
HID: roccat: bound device-supplied profile index
HID: roccat: add KUnit test for kone profile-index bounds
drivers/hid/Kconfig | 9 +++++
drivers/hid/hid-roccat-kone.c | 65 +++++++++++++++++++++++++++++++++--
2 files changed, 72 insertions(+), 2 deletions(-)
--
2.53.0
^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH 1/2] HID: roccat: bound device-supplied profile index
2026-06-18 3:00 [PATCH 0/2] HID: roccat: bound device-supplied profile index Michael Bommarito
@ 2026-06-18 3:00 ` Michael Bommarito
2026-06-18 3:18 ` sashiko-bot
2026-06-18 3:00 ` [PATCH 2/2] HID: roccat: add KUnit test for kone profile-index bounds Michael Bommarito
1 sibling, 1 reply; 5+ messages in thread
From: Michael Bommarito @ 2026-06-18 3:00 UTC (permalink / raw)
To: Stefan Achatz, Jiri Kosina, Benjamin Tissoires; +Cc: linux-input, linux-kernel
kone_keep_values_up_to_date() and kone_profile_activated() use an
8-bit, device-supplied profile value as an index into the 5-element
kone->profiles[] array without a range check. A malicious USB device
claiming the Roccat Kone id can send a switch-profile event (or a
startup_profile read at probe) with an out-of-range value and make the
driver read out of bounds; the result is exposed via the actual_dpi
sysfs attribute.
Reject out-of-range indices in both paths.
This was found with static analysis and confirmed with the KUnit test
added in the following patch (KASAN: slab-out-of-bounds).
Fixes: 14bf62cde7942 ("HID: add driver for Roccat Kone gaming mouse")
Assisted-by: Claude:claude-opus-4-8
Signed-off-by: Michael Bommarito <michael.bommarito@gmail.com>
---
drivers/hid/hid-roccat-kone.c | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
diff --git a/drivers/hid/hid-roccat-kone.c b/drivers/hid/hid-roccat-kone.c
index 58654cf78f0df..17495fcc8b7da 100644
--- a/drivers/hid/hid-roccat-kone.c
+++ b/drivers/hid/hid-roccat-kone.c
@@ -36,6 +36,8 @@ static uint profile_numbers[5] = {0, 1, 2, 3, 4};
static void kone_profile_activated(struct kone_device *kone, uint new_profile)
{
+ if (new_profile < 1 || new_profile > ARRAY_SIZE(kone->profiles))
+ new_profile = 1;
kone->actual_profile = new_profile;
kone->actual_dpi = kone->profiles[new_profile - 1].startup_dpi;
}
@@ -793,8 +795,10 @@ static void kone_keep_values_up_to_date(struct kone_device *kone,
{
switch (event->event) {
case kone_mouse_event_switch_profile:
- kone->actual_dpi = kone->profiles[event->value - 1].
- startup_dpi;
+ if (event->value >= 1 &&
+ event->value <= ARRAY_SIZE(kone->profiles))
+ kone->actual_dpi =
+ kone->profiles[event->value - 1].startup_dpi;
fallthrough;
case kone_mouse_event_osd_profile:
kone->actual_profile = event->value;
--
2.53.0
^ permalink raw reply related [flat|nested] 5+ messages in thread
* [PATCH 2/2] HID: roccat: add KUnit test for kone profile-index bounds
2026-06-18 3:00 [PATCH 0/2] HID: roccat: bound device-supplied profile index Michael Bommarito
2026-06-18 3:00 ` [PATCH 1/2] " Michael Bommarito
@ 2026-06-18 3:00 ` Michael Bommarito
2026-06-18 3:14 ` sashiko-bot
1 sibling, 1 reply; 5+ messages in thread
From: Michael Bommarito @ 2026-06-18 3:00 UTC (permalink / raw)
To: Stefan Achatz, Jiri Kosina, Benjamin Tissoires; +Cc: linux-input, linux-kernel
Drive kone_keep_values_up_to_date() with a crafted switch-profile event;
an out-of-range value reads past profiles[] (KASAN slab-out-of-bounds on
an unpatched tree). A benign control with an in-range value exercises the
same path. The test object is sized to end at profiles[] so the over-read
lands in the KASAN redzone.
Assisted-by: Claude:claude-opus-4-8
Signed-off-by: Michael Bommarito <michael.bommarito@gmail.com>
---
drivers/hid/Kconfig | 9 ++++++
drivers/hid/hid-roccat-kone.c | 57 +++++++++++++++++++++++++++++++++++
2 files changed, 66 insertions(+)
diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig
index ff2f580b660ba..3c6bc918aeb54 100644
--- a/drivers/hid/Kconfig
+++ b/drivers/hid/Kconfig
@@ -1057,6 +1057,15 @@ config HID_ROCCAT
Say Y here if you have a Roccat mouse or keyboard and want
support for its special functionalities.
+config HID_ROCCAT_KONE_KUNIT_TEST
+ bool "KUnit tests for the Roccat Kone driver" if !KUNIT_ALL_TESTS
+ depends on HID_ROCCAT=y && KUNIT=y
+ default KUNIT_ALL_TESTS
+ help
+ Enable the KUnit regression tests for the Roccat Kone driver,
+ covering bounds checking of device-supplied profile indices.
+ If unsure, say N.
+
config HID_SAITEK
tristate "Saitek (Mad Catz) non-fully HID-compliant devices"
help
diff --git a/drivers/hid/hid-roccat-kone.c b/drivers/hid/hid-roccat-kone.c
index 17495fcc8b7da..3dae9eaa0b6f4 100644
--- a/drivers/hid/hid-roccat-kone.c
+++ b/drivers/hid/hid-roccat-kone.c
@@ -919,3 +919,60 @@ module_exit(kone_exit);
MODULE_AUTHOR("Stefan Achatz");
MODULE_DESCRIPTION("USB Roccat Kone driver");
MODULE_LICENSE("GPL v2");
+
+#if IS_ENABLED(CONFIG_HID_ROCCAT_KONE_KUNIT_TEST)
+#include <kunit/test.h>
+
+/*
+ * Regression test for the out-of-bounds read in
+ * kone_keep_values_up_to_date(): a malicious USB device sends a
+ * "switch profile" HID event (event == kone_mouse_event_switch_profile)
+ * with an attacker-chosen value in 0..255, which is used unbounded as
+ * profiles[value - 1]. On an unpatched kernel the attack case triggers a
+ * KASAN slab-out-of-bounds read; the fix must leave actual_dpi unchanged.
+ */
+static void kone_profile_index_oob_test(struct kunit *test)
+{
+ struct kone_device *kone;
+ struct kone_mouse_event ev = {};
+ /*
+ * Allocate only up to the end of profiles[] so that any index past
+ * the 5-element array is IMMEDIATELY out of bounds and lands in the
+ * KASAN redzone (a far over-read would hit unrelated valid memory and
+ * escape KASAN).
+ */
+ size_t sz = offsetof(struct kone_device, profiles) +
+ sizeof(kone->profiles);
+
+ kone = kunit_kzalloc(test, sz, GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, kone);
+ kone->profiles[0].startup_dpi = 0x42;
+
+ /* benign control: a valid in-range value drives the SAME path and
+ * must succeed (proves the trigger reaches the real code).
+ */
+ ev.event = kone_mouse_event_switch_profile;
+ ev.value = 1;
+ kone_keep_values_up_to_date(kone, &ev);
+ KUNIT_EXPECT_EQ(test, kone->actual_dpi, 0x42);
+
+ /* attack: value == ARRAY_SIZE(profiles) + 1 reads profiles[5], one
+ * element past the array end -> KASAN slab-out-of-bounds read on an
+ * unpatched kernel. The fix must reject it (actual_dpi unchanged).
+ */
+ ev.value = ARRAY_SIZE(kone->profiles) + 1;
+ kone_keep_values_up_to_date(kone, &ev);
+ KUNIT_EXPECT_EQ(test, kone->actual_dpi, 0x42);
+}
+
+static struct kunit_case kone_test_cases[] = {
+ KUNIT_CASE(kone_profile_index_oob_test),
+ {}
+};
+
+static struct kunit_suite kone_test_suite = {
+ .name = "hid-roccat-kone",
+ .test_cases = kone_test_cases,
+};
+kunit_test_suite(kone_test_suite);
+#endif /* CONFIG_HID_ROCCAT_KONE_KUNIT_TEST */
--
2.53.0
^ permalink raw reply related [flat|nested] 5+ messages in thread
* Re: [PATCH 2/2] HID: roccat: add KUnit test for kone profile-index bounds
2026-06-18 3:00 ` [PATCH 2/2] HID: roccat: add KUnit test for kone profile-index bounds Michael Bommarito
@ 2026-06-18 3:14 ` sashiko-bot
0 siblings, 0 replies; 5+ messages in thread
From: sashiko-bot @ 2026-06-18 3:14 UTC (permalink / raw)
To: Michael Bommarito; +Cc: linux-input, dmitry.torokhov
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
Pre-existing issues:
- [High] A Use-After-Free vulnerability occurs because the `kone_device` structure is freed while hardware interrupts are still active, leaving a dangling pointer in `hid_drvdata`.
- [High] Uninitialized kernel memory is copied to the persistent `kone->settings` structure on a failed USB control message, leading to a heap information leak via sysfs.
--
commit e8aee21a8be54aaf776b4d76c32eeaefd255b5aa
Author: Michael Bommarito <michael.bommarito@gmail.com>
HID: roccat: add KUnit test for kone profile-index bounds
This commit adds a KUnit test for the Roccat Kone driver to verify bounds
checking on profile indices. It simulates a crafted switch-profile event to
ensure the driver does not read out-of-bounds.
> diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig
> index ff2f580b660ba..3c6bc918aeb54 100644
> --- a/drivers/hid/Kconfig
> +++ b/drivers/hid/Kconfig
> @@ -1057,6 +1057,15 @@ config HID_ROCCAT
> Say Y here if you have a Roccat mouse or keyboard and want
> support for its special functionalities.
>
> +config HID_ROCCAT_KONE_KUNIT_TEST
> + bool "KUnit tests for the Roccat Kone driver" if !KUNIT_ALL_TESTS
> + depends on HID_ROCCAT=y && KUNIT=y
> + default KUNIT_ALL_TESTS
> + help
> + Enable the KUnit regression tests for the Roccat Kone driver,
> + covering bounds checking of device-supplied profile indices.
> + If unsure, say N.
> +
> config HID_SAITEK
> tristate "Saitek (Mad Catz) non-fully HID-compliant devices"
> help
> diff --git a/drivers/hid/hid-roccat-kone.c b/drivers/hid/hid-roccat-kone.c
> index 17495fcc8b7da..3dae9eaa0b6f4 100644
> --- a/drivers/hid/hid-roccat-kone.c
> +++ b/drivers/hid/hid-roccat-kone.c
[ ... ]
> @@ -919,3 +919,60 @@ module_exit(kone_exit);
> MODULE_AUTHOR("Stefan Achatz");
> MODULE_DESCRIPTION("USB Roccat Kone driver");
> MODULE_LICENSE("GPL v2");
> +
> +#if IS_ENABLED(CONFIG_HID_ROCCAT_KONE_KUNIT_TEST)
> +#include <kunit/test.h>
> +
> +/*
> + * Regression test for the out-of-bounds read in
> + * kone_keep_values_up_to_date(): a malicious USB device sends a
> + * "switch profile" HID event (event == kone_mouse_event_switch_profile)
> + * with an attacker-chosen value in 0..255, which is used unbounded as
> + * profiles[value - 1]. On an unpatched kernel the attack case triggers a
> + * KASAN slab-out-of-bounds read; the fix must leave actual_dpi unchanged.
> + */
> +static void kone_profile_index_oob_test(struct kunit *test)
> +{
> + struct kone_device *kone;
> + struct kone_mouse_event ev = {};
[ ... ]
> + /* attack: value == ARRAY_SIZE(profiles) + 1 reads profiles[5], one
> + * element past the array end -> KASAN slab-out-of-bounds read on an
> + * unpatched kernel. The fix must reject it (actual_dpi unchanged).
> + */
> + ev.value = ARRAY_SIZE(kone->profiles) + 1;
> + kone_keep_values_up_to_date(kone, &ev);
> + KUNIT_EXPECT_EQ(test, kone->actual_dpi, 0x42);
> +}
> +
> +static struct kunit_case kone_test_cases[] = {
> + KUNIT_CASE(kone_profile_index_oob_test),
> + {}
> +};
> +
> +static struct kunit_suite kone_test_suite = {
> + .name = "hid-roccat-kone",
> + .test_cases = kone_test_cases,
> +};
> +kunit_test_suite(kone_test_suite);
> +#endif /* CONFIG_HID_ROCCAT_KONE_KUNIT_TEST */
[Severity: High]
This isn't a bug introduced by this patch, but while reviewing I noticed a
potential use-after-free vulnerability in the driver initialization and
teardown paths.
In kone_probe(), hid_hw_start() is called before kone_init_specials(),
starting the hardware and enabling USB interrupts. If initialization fails,
the error path frees the driver data but does not clear the pointer:
drivers/hid/hid-roccat-kone.c:kone_init_specials() {
...
exit_free:
kfree(kone);
return retval;
}
Between kfree() and hid_hw_stop(), an incoming USB interrupt will trigger
kone_raw_event(), which dereferences the dangling pointer:
drivers/hid/hid-roccat-kone.c:kone_raw_event() {
...
struct kone_device *kone = hid_get_drvdata(hdev);
...
}
The same window exists in the teardown path, where the driver state is freed
before stopping the hardware:
drivers/hid/hid-roccat-kone.c:kone_remove() {
...
kone_remove_specials(hdev);
hid_hw_stop(hdev);
}
Is it possible for an attacker to trigger this use-after-free by
disconnecting the device or simulating a device error while hardware
interrupts are active?
[Severity: High]
This is a pre-existing issue, but there appears to be a potential heap
information leak when copying uninitialized memory after a failed USB
transfer.
In kone_receive(), a bounce buffer is allocated with kmalloc() and
unconditionally copied to the target even if the USB transfer fails:
drivers/hid/hid-roccat-kone.c:kone_receive() {
buf = kmalloc(size, GFP_KERNEL);
...
retval = usb_control_msg(usb_dev, usb_rcvctrlpipe(usb_dev, 0), ...
...
memcpy(data, buf, size);
kfree(buf);
return retval;
}
When called via kone_get_settings(), the uninitialized kernel heap memory
is copied to the persistent settings structure. If this occurs during a
sysfs operation where the error is handled, the corrupted structure remains
intact:
drivers/hid/hid-roccat-kone.c:kone_sysfs_set_tcu() {
...
retval = kone_get_settings(usb_dev, &kone->settings);
if (retval)
goto exit_no_settings;
...
}
A subsequent read of the settings sysfs attribute exposes this memory
directly to userspace:
drivers/hid/hid-roccat-kone.c:kone_sysfs_read_settings() {
...
memcpy(buf, ((char const *)&kone->settings) + off, count);
...
}
Could a malicious USB device intentionally fail the transfer to exploit
this leak? Should we check the return value of usb_control_msg() before
calling memcpy() in kone_receive()?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260618030036.1880139-1-michael.bommarito@gmail.com?part=2
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [PATCH 1/2] HID: roccat: bound device-supplied profile index
2026-06-18 3:00 ` [PATCH 1/2] " Michael Bommarito
@ 2026-06-18 3:18 ` sashiko-bot
0 siblings, 0 replies; 5+ messages in thread
From: sashiko-bot @ 2026-06-18 3:18 UTC (permalink / raw)
To: Michael Bommarito; +Cc: dmitry.torokhov, linux-input
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
Pre-existing issues:
- [High] Closing a stale roccat character device file descriptor after device removal causes a memory leak, state corruption and Use-After-Free.
- [High] `kone_get_profile()` uses an embedded, unaligned struct field (`&kone->profiles[i]`) as a DMA buffer for inbound USB control messages, leading to cache-line sharing corruption.
--
commit cda04d8cb7b50c97cbcee76f405a2121b1b4c56d
Author: Michael Bommarito <michael.bommarito@gmail.com>
HID: roccat: bound device-supplied profile index
kone_keep_values_up_to_date() and kone_profile_activated() use an
8-bit, device-supplied profile value as an index into the 5-element
kone->profiles[] array without a range check. A malicious USB device
claiming the Roccat Kone id can send a switch-profile event (or a
startup_profile read at probe) with an out-of-range value and make the
driver read out of bounds; the result is exposed via the actual_dpi
sysfs attribute.
> diff --git a/drivers/hid/hid-roccat-kone.c b/drivers/hid/hid-roccat-kone.c
> index 58654cf78f0df..17495fcc8b7da 100644
> --- a/drivers/hid/hid-roccat-kone.c
> +++ b/drivers/hid/hid-roccat-kone.c
[ ... ]
> @@ -793,8 +795,10 @@ static void kone_keep_values_up_to_date(struct kone_device *kone,
> {
> switch (event->event) {
> case kone_mouse_event_switch_profile:
> - kone->actual_dpi = kone->profiles[event->value - 1].
> - startup_dpi;
> + if (event->value >= 1 &&
> + event->value <= ARRAY_SIZE(kone->profiles))
> + kone->actual_dpi =
> + kone->profiles[event->value - 1].startup_dpi;
> fallthrough;
> case kone_mouse_event_osd_profile:
> kone->actual_profile = event->value;
[Severity: High]
This is a pre-existing issue, but does kone_get_profile() use an embedded,
unaligned array element as a DMA buffer?
In kone_init_kone_device_struct(), we call kone_get_profile() passing a
pointer to the embedded array element:
drivers/hid/hid-roccat-kone.c:kone_init_kone_device_struct() {
...
retval = kone_get_profile(usb_dev, &kone->profiles[i], i + 1);
...
}
Which then passes the unaligned buffer directly to usb_control_msg() for a
USB_DIR_IN transfer:
drivers/hid/hid-roccat-kone.c:kone_get_profile() {
...
len = usb_control_msg(usb_dev, usb_rcvctrlpipe(usb_dev, 0),
USB_REQ_CLEAR_FEATURE,
USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN,
kone_command_profile, number, buf,
sizeof(struct kone_profile), USB_CTRL_SET_TIMEOUT);
...
}
Since sizeof(struct kone_profile) is 975 bytes, the array elements are not
cache-line aligned and share cache lines with other struct kone_device
members. Because hid_hw_start() is called earlier, the device can generate
interrupts and concurrent writes to these shared fields may occur.
On architectures with non-coherent DMA caches, won't the cache invalidation
required for the inbound USB DMA wipe out these concurrent CPU writes,
leading to state corruption?
[Severity: High]
This is a pre-existing issue, but can roccat_release() cause a memory leak,
list corruption, and Use-After-Free if a stale file descriptor is closed
after device removal?
When closing the file descriptor, roccat_release() retrieves the device from
the global array instead of using the local reader->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;
}
...
}
If the original device was disconnected, devices[minor] is cleared, causing
roccat_release() to return -ENODEV and permanently leak the roccat_reader
allocation and the original roccat_device.
If a new device is plugged in before the stale file descriptor is closed,
devices[minor] resolves to the new device. roccat_release() then operates on
the new device, locking its readers_lock while modifying the old device's
list:
drivers/hid/hid-roccat.c:roccat_release() {
...
list_del(&reader->node);
...
if (!--device->open) {
...
}
Could this lead to list corruption and incorrectly decrement the new device's
open count, causing premature shutdown and a Use-After-Free of the new device
when legitimate users close their descriptors?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260618030036.1880139-1-michael.bommarito@gmail.com?part=1
^ permalink raw reply [flat|nested] 5+ messages in thread
end of thread, other threads:[~2026-06-18 3:18 UTC | newest]
Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-06-18 3:00 [PATCH 0/2] HID: roccat: bound device-supplied profile index Michael Bommarito
2026-06-18 3:00 ` [PATCH 1/2] " Michael Bommarito
2026-06-18 3:18 ` sashiko-bot
2026-06-18 3:00 ` [PATCH 2/2] HID: roccat: add KUnit test for kone profile-index bounds Michael Bommarito
2026-06-18 3:14 ` sashiko-bot
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.