Author: gordon
Date: Tue Jun  9 16:13:54 2020
New Revision: 361972
URL: https://svnweb.freebsd.org/changeset/base/361972

Log:
  Fix USB HID descriptor parsing error.
  
  Approved by:  so
  Approved by:  re (implicit)
  Security:     FreeBSD-SA-20:17.usb
  Security:     CVE-2020-7456

Modified:
  releng/11.3/lib/libusbhid/parse.c
  releng/11.3/sys/dev/usb/usb_hid.c
  releng/11.4/lib/libusbhid/parse.c
  releng/11.4/sys/dev/usb/usb_hid.c
  releng/12.1/lib/libusbhid/parse.c
  releng/12.1/sys/dev/usb/usb_hid.c

Modified: releng/11.3/lib/libusbhid/parse.c
==============================================================================
--- releng/11.3/lib/libusbhid/parse.c   Tue Jun  9 16:11:54 2020        
(r361971)
+++ releng/11.3/lib/libusbhid/parse.c   Tue Jun  9 16:13:54 2020        
(r361972)
@@ -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;

Modified: releng/11.3/sys/dev/usb/usb_hid.c
==============================================================================
--- releng/11.3/sys/dev/usb/usb_hid.c   Tue Jun  9 16:11:54 2020        
(r361971)
+++ releng/11.3/sys/dev/usb/usb_hid.c   Tue Jun  9 16:13:54 2020        
(r361972)
@@ -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);

Modified: releng/11.4/lib/libusbhid/parse.c
==============================================================================
--- releng/11.4/lib/libusbhid/parse.c   Tue Jun  9 16:11:54 2020        
(r361971)
+++ releng/11.4/lib/libusbhid/parse.c   Tue Jun  9 16:13:54 2020        
(r361972)
@@ -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;

Modified: releng/11.4/sys/dev/usb/usb_hid.c
==============================================================================
--- releng/11.4/sys/dev/usb/usb_hid.c   Tue Jun  9 16:11:54 2020        
(r361971)
+++ releng/11.4/sys/dev/usb/usb_hid.c   Tue Jun  9 16:13:54 2020        
(r361972)
@@ -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);

Modified: releng/12.1/lib/libusbhid/parse.c
==============================================================================
--- releng/12.1/lib/libusbhid/parse.c   Tue Jun  9 16:11:54 2020        
(r361971)
+++ releng/12.1/lib/libusbhid/parse.c   Tue Jun  9 16:13:54 2020        
(r361972)
@@ -403,26 +403,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;

Modified: releng/12.1/sys/dev/usb/usb_hid.c
==============================================================================
--- releng/12.1/sys/dev/usb/usb_hid.c   Tue Jun  9 16:11:54 2020        
(r361971)
+++ releng/12.1/sys/dev/usb/usb_hid.c   Tue Jun  9 16:13:54 2020        
(r361972)
@@ -436,36 +436,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);
_______________________________________________
svn-src-all@freebsd.org mailing list
https://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "svn-src-all-unsubscr...@freebsd.org"

Reply via email to