aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHans Petter Selasky <hselasky@FreeBSD.org>2020-06-08 09:34:16 +0000
committerHans Petter Selasky <hselasky@FreeBSD.org>2020-06-08 09:34:16 +0000
commit1036bfa7dd4ecac232c91779e9096d3744885f3e (patch)
treef50c944d83a5db1fe356d475523215882937b52b
parenta879f39e0f5fe80e3e332fecd2fcf5c9c1731cd0 (diff)
downloadsrc-1036bfa7dd4ecac232c91779e9096d3744885f3e.tar.gz
src-1036bfa7dd4ecac232c91779e9096d3744885f3e.zip
MFC r361827:
USB HID descriptors may push/pop the current state to allow description of items residing in a so-called union. FreeBSD currently only supports 4 such push levels. If the push level is not restored within the processing of the same HID item, an invalid memory location may be used for subsequent HID item processing. Verify that the push level is always valid when processing HID items. Reported by: Andy Nguyen (Google) Sponsored by: Mellanox Technologies
Notes
Notes: svn path=/stable/10/; revision=361921
-rw-r--r--lib/libusbhid/parse.c34
-rw-r--r--sys/dev/usb/usb_hid.c52
2 files changed, 44 insertions, 42 deletions
diff --git a/lib/libusbhid/parse.c b/lib/libusbhid/parse.c
index 7a2b3f41eea9..943af2696536 100644
--- a/lib/libusbhid/parse.c
+++ b/lib/libusbhid/parse.c
@@ -401,26 +401,28 @@ hid_get_item_raw(hid_data_t s, hid_item_t *h)
s->loc_count = dval & mask;
break;
case 10: /* Push */
+ /* stop parsing, if invalid push level */
+ if ((s->pushlevel + 1) >= MAXPUSH)
+ return (0);
s->pushlevel ++;
- if (s->pushlevel < MAXPUSH) {
- s->cur[s->pushlevel] = *c;
- /* store size and count */
- c->report_size = s->loc_size;
- c->report_count = s->loc_count;
- /* update current item pointer */
- c = &s->cur[s->pushlevel];
- }
+ s->cur[s->pushlevel] = *c;
+ /* store size and count */
+ c->report_size = s->loc_size;
+ c->report_count = s->loc_count;
+ /* update current item pointer */
+ c = &s->cur[s->pushlevel];
break;
case 11: /* Pop */
+ /* stop parsing, if invalid push level */
+ if (s->pushlevel == 0)
+ return (0);
s->pushlevel --;
- if (s->pushlevel < MAXPUSH) {
- c = &s->cur[s->pushlevel];
- /* restore size and count */
- s->loc_size = c->report_size;
- s->loc_count = c->report_count;
- c->report_size = 0;
- c->report_count = 0;
- }
+ c = &s->cur[s->pushlevel];
+ /* restore size and count */
+ s->loc_size = c->report_size;
+ s->loc_count = c->report_count;
+ c->report_size = 0;
+ c->report_count = 0;
break;
default:
break;
diff --git a/sys/dev/usb/usb_hid.c b/sys/dev/usb/usb_hid.c
index dc0ce99facc4..c2ec26f58dc5 100644
--- a/sys/dev/usb/usb_hid.c
+++ b/sys/dev/usb/usb_hid.c
@@ -434,36 +434,36 @@ hid_get_item(struct hid_data *s, struct hid_item *h)
s->loc_count = dval & mask;
break;
case 10: /* Push */
- s->pushlevel ++;
- if (s->pushlevel < MAXPUSH) {
- s->cur[s->pushlevel] = *c;
- /* store size and count */
- c->loc.size = s->loc_size;
- c->loc.count = s->loc_count;
- /* update current item pointer */
- c = &s->cur[s->pushlevel];
- } else {
- DPRINTFN(0, "Cannot push "
- "item @ %d\n", s->pushlevel);
+ /* stop parsing, if invalid push level */
+ if ((s->pushlevel + 1) >= MAXPUSH) {
+ DPRINTFN(0, "Cannot push item @ %d\n", s->pushlevel);
+ return (0);
}
+ s->pushlevel ++;
+ s->cur[s->pushlevel] = *c;
+ /* store size and count */
+ c->loc.size = s->loc_size;
+ c->loc.count = s->loc_count;
+ /* update current item pointer */
+ c = &s->cur[s->pushlevel];
break;
case 11: /* Pop */
- s->pushlevel --;
- if (s->pushlevel < MAXPUSH) {
- /* preserve position */
- oldpos = c->loc.pos;
- c = &s->cur[s->pushlevel];
- /* restore size and count */
- s->loc_size = c->loc.size;
- s->loc_count = c->loc.count;
- /* set default item location */
- c->loc.pos = oldpos;
- c->loc.size = 0;
- c->loc.count = 0;
- } else {
- DPRINTFN(0, "Cannot pop "
- "item @ %d\n", s->pushlevel);
+ /* stop parsing, if invalid push level */
+ if (s->pushlevel == 0) {
+ DPRINTFN(0, "Cannot pop item @ 0\n");
+ return (0);
}
+ s->pushlevel --;
+ /* preserve position */
+ oldpos = c->loc.pos;
+ c = &s->cur[s->pushlevel];
+ /* restore size and count */
+ s->loc_size = c->loc.size;
+ s->loc_count = c->loc.count;
+ /* set default item location */
+ c->loc.pos = oldpos;
+ c->loc.size = 0;
+ c->loc.count = 0;
break;
default:
DPRINTFN(0, "Global bTag=%d\n", bTag);