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 pop 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)
MFC after:	3 days
Sponsored by:	Mellanox Technologies
This commit is contained in:
hselasky 2020-06-05 07:57:16 +00:00
parent 16f48c78bb
commit 83f4a222f0
2 changed files with 44 additions and 42 deletions

View File

@ -403,26 +403,28 @@ hid_get_item_raw(hid_data_t s, hid_item_t *h)
s->loc_count = dval & mask; s->loc_count = dval & mask;
break; break;
case 10: /* Push */ case 10: /* Push */
/* stop parsing, if invalid push level */
if ((s->pushlevel + 1) >= MAXPUSH)
return (0);
s->pushlevel ++; s->pushlevel ++;
if (s->pushlevel < MAXPUSH) { s->cur[s->pushlevel] = *c;
s->cur[s->pushlevel] = *c; /* store size and count */
/* store size and count */ c->report_size = s->loc_size;
c->report_size = s->loc_size; c->report_count = s->loc_count;
c->report_count = s->loc_count; /* update current item pointer */
/* update current item pointer */ c = &s->cur[s->pushlevel];
c = &s->cur[s->pushlevel];
}
break; break;
case 11: /* Pop */ case 11: /* Pop */
/* stop parsing, if invalid push level */
if (s->pushlevel == 0)
return (0);
s->pushlevel --; s->pushlevel --;
if (s->pushlevel < MAXPUSH) { c = &s->cur[s->pushlevel];
c = &s->cur[s->pushlevel]; /* restore size and count */
/* restore size and count */ s->loc_size = c->report_size;
s->loc_size = c->report_size; s->loc_count = c->report_count;
s->loc_count = c->report_count; c->report_size = 0;
c->report_size = 0; c->report_count = 0;
c->report_count = 0;
}
break; break;
default: default:
break; break;

View File

@ -436,36 +436,36 @@ hid_get_item(struct hid_data *s, struct hid_item *h)
s->loc_count = dval & mask; s->loc_count = dval & mask;
break; break;
case 10: /* Push */ case 10: /* Push */
s->pushlevel ++; /* stop parsing, if invalid push level */
if (s->pushlevel < MAXPUSH) { if ((s->pushlevel + 1) >= MAXPUSH) {
s->cur[s->pushlevel] = *c; DPRINTFN(0, "Cannot push item @ %d\n", s->pushlevel);
/* store size and count */ return (0);
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);
} }
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; break;
case 11: /* Pop */ case 11: /* Pop */
s->pushlevel --; /* stop parsing, if invalid push level */
if (s->pushlevel < MAXPUSH) { if (s->pushlevel == 0) {
/* preserve position */ DPRINTFN(0, "Cannot pop item @ 0\n");
oldpos = c->loc.pos; return (0);
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);
} }
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; break;
default: default:
DPRINTFN(0, "Global bTag=%d\n", bTag); DPRINTFN(0, "Global bTag=%d\n", bTag);