Hi,

Auditing the HID report-descriptor parser I noticed that hid_report_size()
and the position accumulation in hid_get_item() compute
loc.pos + loc.size * loc.count with uint32_t operands taken straight from
the report descriptor, with no bound.  A crafted descriptor can wrap the
32-bit multiply, so hid_report_size() returns a wrong (usually too small)
report size.

I don't think it is exploitable: hid_get_data() clamps reads to the
caller's buffer and the drivers reject reports larger than the endpoint.
But the length is still wrong, so it seems worth doing the arithmetic in
64-bit and saturating.

Caveat: I build-tested only the equivalent FreeBSD parser, which is the
same code; I have not built a NetBSD kernel with this, so please treat it
as a proposal.  For reference the same fix is under review in FreeBSD as
https://reviews.freebsd.org/D60846 .

Does this look reasonable?

--- a/sys/dev/hid/hid.c 2026-10-18 23:21:18.340040000 +0000
+++ b/sys/dev/hid/hid.c 2026-10-18 23:22:02.659433000 +0000
@@ -238,12 +238,16 @@
                                        }
                                        goto top;
                                } else {
+                                       uint64_t pos;
+
                                        if (s->minset)
                                                c->usage = c->usage_minimum;
                                        *h = *c;
                                        h->next = NULL;
-                                       c->loc.pos +=
-                                           c->loc.size * c->loc.count;
+                                       pos = (uint64_t)c->loc.pos +
+                                           (uint64_t)c->loc.size * 
c->loc.count;
+                                       c->loc.pos = pos > 0xffffffffU ?
+                                           0xffffffffU : (uint32_t)pos;
                                        s->minset = 0;
                                        s->nu = 0;
                                        hid_clear_local(c);
@@ -385,7 +389,7 @@
 {
        struct hid_data *d;
        struct hid_item h;
-       int lo, hi;
+       int64_t lo, hi;
 
        h.report_ID = 0;
        lo = hi = -1;
@@ -405,8 +409,11 @@
                                }
 #endif
                        }
-                       hi = h.loc.pos + h.loc.size * h.loc.count;
-                       DPRINTFN(2,("hid_report_size: lo=%d hi=%d\n", lo, hi));
+                       hi = (uint64_t)h.loc.pos +
+                           (uint64_t)h.loc.size * h.loc.count;
+                       if (hi > 0xffffffffU)
+                               hi = 0xffffffffU;
+                       DPRINTFN(2,("hid_report_size: lo=%d hi=%d\n", (int)lo, 
(int)hi));
                }
        }
        hid_end_parse(d);


Thanks,
Osvaldo Janeri Filho

Reply via email to