selftests/hid: add a test case for the recent syzbot underflow

Syzbot found a buffer underflow in __hid_request(). Add a related test
case for it.

It's not perfect, but it allows to catch a corner case when a report
descriptor is crafted so that it has a size of 0.

Link: https://patch.msgid.link/20250710-report-size-null-v2-4-ccf922b7c4e5@kernel.org
Signed-off-by: Benjamin Tissoires <bentiss@kernel.org>
This commit is contained in:
Benjamin Tissoires 2025-07-10 16:01:36 +02:00
parent c2ca42f190
commit 3a1d22bd85

View File

@ -439,6 +439,68 @@ class BadResolutionMultiplierMouse(ResolutionMultiplierMouse):
return 32 # EPIPE
class BadReportDescriptorMouse(BaseMouse):
"""
This "device" was one autogenerated by syzbot. There are a lot of issues in
it, and the most problematic is that it declares features that have no
size.
This leads to report->size being set to 0 and can mess up with usbhid
internals. Fortunately, uhid merely passes the incoming buffer, without
touching it so a buffer of size 0 will be translated to [] without
triggering a kernel oops.
Because the report descriptor is wrong, no input are created, and we need
to tweak a little bit the parameters to make it look correct.
"""
# fmt: off
report_descriptor = [
0x96, 0x01, 0x00, # Report Count (1) 0
0x06, 0x01, 0x00, # Usage Page (Generic Desktop) 3
# 0x03, 0x00, 0x00, 0x00, 0x00, # Ignored by the kernel somehow
0x2a, 0x90, 0xa0, # Usage Maximum (41104) 6
0x27, 0x00, 0x00, 0x00, 0x00, # Logical Maximum (0) 9
0xb3, 0x81, 0x3e, 0x25, 0x03, # Feature (Cnst,Arr,Abs,Vol) 14
0x1b, 0xdd, 0xe8, 0x40, 0x50, # Usage Minimum (1346431197) 19
0x3b, 0x5d, 0x8c, 0x3d, 0xda, # Designator Index 24
]
# fmt: on
def __init__(
self, rdesc=report_descriptor, name=None, input_info=(3, 0x045E, 0x07DA)
):
super().__init__(rdesc, name, input_info)
self.high_resolution_report_called = False
def get_evdev(self, application=None):
assert self._input_nodes is None
return (
"Ok" # should be a list or None, but both would fail, so abusing the system
)
def next_sync_events(self, application=None):
# there are no evdev nodes, so no events
return []
def is_ready(self):
# we wait for the SET_REPORT command to come
return self.high_resolution_report_called
def set_report(self, req, rnum, rtype, data):
if rtype != self.UHID_FEATURE_REPORT:
raise InvalidHIDCommunication(f"Unexpected report type: {rtype}")
if rnum != 0x0:
raise InvalidHIDCommunication(f"Unexpected report number: {rnum}")
if len(data) != 1:
raise InvalidHIDCommunication(f"Unexpected data: {data}, expected '[0]'")
self.high_resolution_report_called = True
return 0
class ResolutionMultiplierHWheelMouse(TwoWheelMouse):
# fmt: off
report_descriptor = [
@ -975,3 +1037,11 @@ class TestMiMouse(TestWheelMouse):
# assert below print out the real error
pass
assert remaining == []
class TestBadReportDescriptorMouse(base.BaseTestCase.TestUhid):
def create_device(self):
return BadReportDescriptorMouse()
def assertName(self, uhdev):
pass