Linux USB
 help / color / mirror / Atom feed
* revisiting the issue of hardening the USB enumeration parser
@ 2024-05-16 13:48 Oliver Neukum
  2024-05-16 16:58 ` Alan Stern
  0 siblings, 1 reply; 2+ messages in thread
From: Oliver Neukum @ 2024-05-16 13:48 UTC (permalink / raw)
  To: Alan Stern, Greg Kroah-Hartman; +Cc: USB list

[-- Attachment #1: Type: text/plain, Size: 1459 bytes --]

Hi,

you convinced me that my last attempt to look at the parser
was fundamentally flawed. Instead I went top down from parsing
the configuration down to endpoints. I found one major issue.

static int find_next_descriptor(unsigned char *buffer, int size,
     int dt1, int dt2, int *num_skipped)
{
         struct usb_descriptor_header *h;
         int n = 0;
         unsigned char *buffer0 = buffer;
         
         /* Find the next descriptor of type dt1 or dt2 */
         while (size > 0) {
                 h = (struct usb_descriptor_header *) buffer;

                 if (h->bDescriptorType == dt1 || h->bDescriptorType == dt2)
                         break;
                 buffer += h->bLength;
                 size -= h->bLength;
                 ++n;
         }
         
         /* Store the number of descriptors skipped and return the
          * number of bytes skipped */
         if (num_skipped)
                 *num_skipped = n;
         return buffer - buffer0;
}

This is called from multiple sites on chains of descriptors.
We do have a check for overflowing the buffer in the while statement.
However, there is no guarantee for make progress. If at some point
in the chain we arrive at a descriptor of neither type we are looking
for and a bLength of 0, size will remain constant and the loop
will go on forever.

AFAICT this is guarded nowhere outside the function against.
So how about the attached patch?

	Regards
		Oliver

[-- Attachment #2: 0001-USB-find_next_descriptor-prevent-eternal-loop-by-mis.patch --]
[-- Type: text/x-patch, Size: 1294 bytes --]

From 7c18f5673ae416027b813a51ece4e689b4383a6c Mon Sep 17 00:00:00 2001
From: Oliver Neukum <oneukum@suse.com>
Date: Thu, 16 May 2024 15:06:34 +0200
Subject: [PATCH] USB: find_next_descriptor: prevent eternal loop by misformed
 descriptors

In find_next_descriptor() is called to operate on chains of descriptors.
The callers make sure that the chain as a whole is of sufficient length
and it is ensured that the buffer is not overflown.
However, the central does not guard against an inner link in the chain
claiming zero length. In that case the loop will make no progress and
go on forever. This case has to be tested for.

Signed-off-by: Oliver Neukum <oneukum@suse.com>
---
 drivers/usb/core/config.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/drivers/usb/core/config.c b/drivers/usb/core/config.c
index 3362af165ef5..65d23fa19b3c 100644
--- a/drivers/usb/core/config.c
+++ b/drivers/usb/core/config.c
@@ -34,6 +34,8 @@ static int find_next_descriptor(unsigned char *buffer, int size,
 	/* Find the next descriptor of type dt1 or dt2 */
 	while (size > 0) {
 		h = (struct usb_descriptor_header *) buffer;
+		if (!h->bLength) /* we would loop forever */
+			return 0;
 		if (h->bDescriptorType == dt1 || h->bDescriptorType == dt2)
 			break;
 		buffer += h->bLength;
-- 
2.45.0


^ permalink raw reply related	[flat|nested] 2+ messages in thread

* Re: revisiting the issue of hardening the USB enumeration parser
  2024-05-16 13:48 revisiting the issue of hardening the USB enumeration parser Oliver Neukum
@ 2024-05-16 16:58 ` Alan Stern
  0 siblings, 0 replies; 2+ messages in thread
From: Alan Stern @ 2024-05-16 16:58 UTC (permalink / raw)
  To: Oliver Neukum; +Cc: Greg Kroah-Hartman, USB list

On Thu, May 16, 2024 at 03:48:41PM +0200, Oliver Neukum wrote:
> Hi,
> 
> you convinced me that my last attempt to look at the parser
> was fundamentally flawed. Instead I went top down from parsing
> the configuration down to endpoints. I found one major issue.
> 
> static int find_next_descriptor(unsigned char *buffer, int size,
>     int dt1, int dt2, int *num_skipped)
> {
>         struct usb_descriptor_header *h;
>         int n = 0;
>         unsigned char *buffer0 = buffer;
>         /* Find the next descriptor of type dt1 or dt2 */
>         while (size > 0) {
>                 h = (struct usb_descriptor_header *) buffer;
> 
>                 if (h->bDescriptorType == dt1 || h->bDescriptorType == dt2)
>                         break;
>                 buffer += h->bLength;
>                 size -= h->bLength;
>                 ++n;
>         }
>         /* Store the number of descriptors skipped and return the
>          * number of bytes skipped */
>         if (num_skipped)
>                 *num_skipped = n;
>         return buffer - buffer0;
> }
> 
> This is called from multiple sites on chains of descriptors.
> We do have a check for overflowing the buffer in the while statement.
> However, there is no guarantee for make progress. If at some point
> in the chain we arrive at a descriptor of neither type we are looking
> for and a bLength of 0, size will remain constant and the loop
> will go on forever.
> 
> AFAICT this is guarded nowhere outside the function against.

You didn't notice this code in usb_parse_configuration() (starting 
around line 659):

		header = (struct usb_descriptor_header *) buffer2;
		if ((header->bLength > size2) || (header->bLength < 2)) {
			dev_notice(ddev, "config %d has an invalid descriptor "
			    "of length %d, skipping remainder of the config\n",
			    cfgno, header->bLength);
			break;
		}

The inner parentheses in the "if" condition aren't necessary, but the 
second half of the condition protects against zero-length descriptors.

> So how about the attached patch?

It's not necessary.

Alan Stern

^ permalink raw reply	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2024-05-16 16:58 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2024-05-16 13:48 revisiting the issue of hardening the USB enumeration parser Oliver Neukum
2024-05-16 16:58 ` Alan Stern

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox