diff mbox series

Prevent buffer overflow on USB control endpoint

Message ID 20221117094847.60409-1-szymon.heidrich@gmail.com
State Changes Requested
Delegated to: Marek Vasut
Headers show
Series Prevent buffer overflow on USB control endpoint | expand

Commit Message

Szymon Heidrich Nov. 17, 2022, 9:48 a.m. UTC
Assure that the control endpoint buffer of size USB_BUFSIZ (4096)
can not be overflown during handling of USB control transfer
requests with wLength greater than USB_BUFSIZ.

Signed-off-by: Szymon Heidrich <szymon.heidrich@gmail.com>
---
 drivers/usb/gadget/composite.c | 11 +++++++++++
 1 file changed, 11 insertions(+)

Comments

Fabio Estevam Nov. 17, 2022, 11:50 a.m. UTC | #1
[Adding Lukasz and Marek]

On Thu, Nov 17, 2022 at 6:50 AM Szymon Heidrich
<szymon.heidrich@gmail.com> wrote:
>
> Assure that the control endpoint buffer of size USB_BUFSIZ (4096)
> can not be overflown during handling of USB control transfer
> requests with wLength greater than USB_BUFSIZ.
>
> Signed-off-by: Szymon Heidrich <szymon.heidrich@gmail.com>
> ---
>  drivers/usb/gadget/composite.c | 11 +++++++++++
>  1 file changed, 11 insertions(+)
>
> diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c
> index 2a309e624e..cb89f6dca9 100644
> --- a/drivers/usb/gadget/composite.c
> +++ b/drivers/usb/gadget/composite.c
> @@ -1019,6 +1019,17 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
>         u8                              endp;
>         struct usb_configuration        *c;
>
> +       if (w_length > USB_BUFSIZ) {
> +               if (ctrl->bRequestType & USB_DIR_IN) {
> +                       /* Cast away the const, we are going to overwrite on purpose. */
> +                       __le16 *temp = (__le16 *)&ctrl->wLength;
> +                       *temp = cpu_to_le16(USB_BUFSIZ);
> +                       w_length = USB_BUFSIZ;
> +               } else {
> +                       goto done;
> +               }
> +       }
> +
>         /*
>          * partial re-init of the response message; the function or the
>          * gadget might need to intercept e.g. a control-OUT completion
> --
> 2.38.1
>
Marek Vasut Nov. 20, 2022, 2:43 p.m. UTC | #2
On 11/17/22 12:50, Fabio Estevam wrote:
> [Adding Lukasz and Marek]
> 
> On Thu, Nov 17, 2022 at 6:50 AM Szymon Heidrich
> <szymon.heidrich@gmail.com> wrote:
>>
>> Assure that the control endpoint buffer of size USB_BUFSIZ (4096)
>> can not be overflown during handling of USB control transfer
>> requests with wLength greater than USB_BUFSIZ.
>>
>> Signed-off-by: Szymon Heidrich <szymon.heidrich@gmail.com>
>> ---
>>   drivers/usb/gadget/composite.c | 11 +++++++++++
>>   1 file changed, 11 insertions(+)
>>
>> diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c
>> index 2a309e624e..cb89f6dca9 100644
>> --- a/drivers/usb/gadget/composite.c
>> +++ b/drivers/usb/gadget/composite.c
>> @@ -1019,6 +1019,17 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
>>          u8                              endp;
>>          struct usb_configuration        *c;
>>
>> +       if (w_length > USB_BUFSIZ) {
>> +               if (ctrl->bRequestType & USB_DIR_IN) {
>> +                       /* Cast away the const, we are going to overwrite on purpose. */
>> +                       __le16 *temp = (__le16 *)&ctrl->wLength;
>> +                       *temp = cpu_to_le16(USB_BUFSIZ);
>> +                       w_length = USB_BUFSIZ;

Won't this end up sending corrupted packets in case they are longer than 
USB_BUFSIZ ?

Where do such long packets come from ?

What is the test-case ?
Szymon Heidrich Nov. 20, 2022, 3:29 p.m. UTC | #3
On 20/11/2022 15:43, Marek Vasut wrote:
> On 11/17/22 12:50, Fabio Estevam wrote:
>> [Adding Lukasz and Marek]
>>
>> On Thu, Nov 17, 2022 at 6:50 AM Szymon Heidrich
>> <szymon.heidrich@gmail.com> wrote:
>>>
>>> Assure that the control endpoint buffer of size USB_BUFSIZ (4096)
>>> can not be overflown during handling of USB control transfer
>>> requests with wLength greater than USB_BUFSIZ.
>>>
>>> Signed-off-by: Szymon Heidrich <szymon.heidrich@gmail.com>
>>> ---
>>>   drivers/usb/gadget/composite.c | 11 +++++++++++
>>>   1 file changed, 11 insertions(+)
>>>
>>> diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c
>>> index 2a309e624e..cb89f6dca9 100644
>>> --- a/drivers/usb/gadget/composite.c
>>> +++ b/drivers/usb/gadget/composite.c
>>> @@ -1019,6 +1019,17 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
>>>          u8                              endp;
>>>          struct usb_configuration        *c;
>>>
>>> +       if (w_length > USB_BUFSIZ) {
>>> +               if (ctrl->bRequestType & USB_DIR_IN) {
>>> +                       /* Cast away the const, we are going to overwrite on purpose. */
>>> +                       __le16 *temp = (__le16 *)&ctrl->wLength;
>>> +                       *temp = cpu_to_le16(USB_BUFSIZ);
>>> +                       w_length = USB_BUFSIZ;
> 
> Won't this end up sending corrupted packets in case they are longer than USB_BUFSIZ ?
> 
> Where do such long packets come from ?
> 
> What is the test-case ?

The USB host will not attempt to retrieve more than wLenght bytes during transfer phase.
If the device would erroneously attempt to provide more data it would result in an unexpected state.
In case of most implementations the buffer for endpoint 0 along with max control transfer is limited to 4096 bytes (USB_BUFSIZ for U-Boot and Linux kernel).
Still according to the USB specification wLength is two bytes an the device may receive requests with wLength larger than 4096 bytes e.g. in case of a custom/malicious USB host.
For example one may build libusb with MAX_CTRL_BUFFER_LENGTH altered to 0xffff and this will allow the host to send requests with wLength up to 0xffff.
In this case the original implementation may result in buffer overflows as in multiple locations a value directly derived from wLength is set as the transfer phase length.
With the change applied IN requests with wLength larger than USB_BUFSIZ will be trimmed to USB_BUFSIZ, otherwise the host would read wLength-USB_BUFSIZ past cdev->req->buf.
I am not aware of any cases where more than USB_BUFSIZ would be provided from a buffer other than cdev->req->buf. In case I missed such case please let me know.
Marek Vasut Nov. 20, 2022, 5:25 p.m. UTC | #4
On 11/20/22 16:29, Szymon Heidrich wrote:
> On 20/11/2022 15:43, Marek Vasut wrote:
>> On 11/17/22 12:50, Fabio Estevam wrote:
>>> [Adding Lukasz and Marek]
>>>
>>> On Thu, Nov 17, 2022 at 6:50 AM Szymon Heidrich
>>> <szymon.heidrich@gmail.com> wrote:
>>>>
>>>> Assure that the control endpoint buffer of size USB_BUFSIZ (4096)
>>>> can not be overflown during handling of USB control transfer
>>>> requests with wLength greater than USB_BUFSIZ.
>>>>
>>>> Signed-off-by: Szymon Heidrich <szymon.heidrich@gmail.com>
>>>> ---
>>>>    drivers/usb/gadget/composite.c | 11 +++++++++++
>>>>    1 file changed, 11 insertions(+)
>>>>
>>>> diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c
>>>> index 2a309e624e..cb89f6dca9 100644
>>>> --- a/drivers/usb/gadget/composite.c
>>>> +++ b/drivers/usb/gadget/composite.c
>>>> @@ -1019,6 +1019,17 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
>>>>           u8                              endp;
>>>>           struct usb_configuration        *c;
>>>>
>>>> +       if (w_length > USB_BUFSIZ) {
>>>> +               if (ctrl->bRequestType & USB_DIR_IN) {
>>>> +                       /* Cast away the const, we are going to overwrite on purpose. */
>>>> +                       __le16 *temp = (__le16 *)&ctrl->wLength;
>>>> +                       *temp = cpu_to_le16(USB_BUFSIZ);
>>>> +                       w_length = USB_BUFSIZ;
>>
>> Won't this end up sending corrupted packets in case they are longer than USB_BUFSIZ ?
>>
>> Where do such long packets come from ?
>>
>> What is the test-case ?
> 
> The USB host will not attempt to retrieve more than wLenght bytes during transfer phase.
> If the device would erroneously attempt to provide more data it would result in an unexpected state.
> In case of most implementations the buffer for endpoint 0 along with max control transfer is limited to 4096 bytes (USB_BUFSIZ for U-Boot and Linux kernel).
> Still according to the USB specification wLength is two bytes an the device may receive requests with wLength larger than 4096 bytes e.g. in case of a custom/malicious USB host.
> For example one may build libusb with MAX_CTRL_BUFFER_LENGTH altered to 0xffff and this will allow the host to send requests with wLength up to 0xffff.
> In this case the original implementation may result in buffer overflows as in multiple locations a value directly derived from wLength is set as the transfer phase length.
> With the change applied IN requests with wLength larger than USB_BUFSIZ will be trimmed to USB_BUFSIZ, otherwise the host would read wLength-USB_BUFSIZ past cdev->req->buf.
> I am not aware of any cases where more than USB_BUFSIZ would be provided from a buffer other than cdev->req->buf. In case I missed such case please let me know.

Why shouldn't the patch do simple

w_length = min(w_length, USB_BUFSIZ);

?
Szymon Heidrich Nov. 20, 2022, 5:42 p.m. UTC | #5
On 20/11/2022 18:25, Marek Vasut wrote:
> On 11/20/22 16:29, Szymon Heidrich wrote:
>> On 20/11/2022 15:43, Marek Vasut wrote:
>>> On 11/17/22 12:50, Fabio Estevam wrote:
>>>> [Adding Lukasz and Marek]
>>>>
>>>> On Thu, Nov 17, 2022 at 6:50 AM Szymon Heidrich
>>>> <szymon.heidrich@gmail.com> wrote:
>>>>>
>>>>> Assure that the control endpoint buffer of size USB_BUFSIZ (4096)
>>>>> can not be overflown during handling of USB control transfer
>>>>> requests with wLength greater than USB_BUFSIZ.
>>>>>
>>>>> Signed-off-by: Szymon Heidrich <szymon.heidrich@gmail.com>
>>>>> ---
>>>>>    drivers/usb/gadget/composite.c | 11 +++++++++++
>>>>>    1 file changed, 11 insertions(+)
>>>>>
>>>>> diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c
>>>>> index 2a309e624e..cb89f6dca9 100644
>>>>> --- a/drivers/usb/gadget/composite.c
>>>>> +++ b/drivers/usb/gadget/composite.c
>>>>> @@ -1019,6 +1019,17 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
>>>>>           u8                              endp;
>>>>>           struct usb_configuration        *c;
>>>>>
>>>>> +       if (w_length > USB_BUFSIZ) {
>>>>> +               if (ctrl->bRequestType & USB_DIR_IN) {
>>>>> +                       /* Cast away the const, we are going to overwrite on purpose. */
>>>>> +                       __le16 *temp = (__le16 *)&ctrl->wLength;
>>>>> +                       *temp = cpu_to_le16(USB_BUFSIZ);
>>>>> +                       w_length = USB_BUFSIZ;
>>>
>>> Won't this end up sending corrupted packets in case they are longer than USB_BUFSIZ ?
>>>
>>> Where do such long packets come from ?
>>>
>>> What is the test-case ?
>>
>> The USB host will not attempt to retrieve more than wLenght bytes during transfer phase.
>> If the device would erroneously attempt to provide more data it would result in an unexpected state.
>> In case of most implementations the buffer for endpoint 0 along with max control transfer is limited to 4096 bytes (USB_BUFSIZ for U-Boot and Linux kernel).
>> Still according to the USB specification wLength is two bytes an the device may receive requests with wLength larger than 4096 bytes e.g. in case of a custom/malicious USB host.
>> For example one may build libusb with MAX_CTRL_BUFFER_LENGTH altered to 0xffff and this will allow the host to send requests with wLength up to 0xffff.
>> In this case the original implementation may result in buffer overflows as in multiple locations a value directly derived from wLength is set as the transfer phase length.
>> With the change applied IN requests with wLength larger than USB_BUFSIZ will be trimmed to USB_BUFSIZ, otherwise the host would read wLength-USB_BUFSIZ past cdev->req->buf.
>> I am not aware of any cases where more than USB_BUFSIZ would be provided from a buffer other than cdev->req->buf. In case I missed such case please let me know.
> 
> Why shouldn't the patch do simple
> 
> w_length = min(w_length, USB_BUFSIZ);
> 
> ?

The composite_setup function in composite.c uses w_length but function specific setup functions are passed the struct usb_ctrlrequest *ctrl so both must be updated.
If only w_length is updated using min(w_length, USB_BUFSIZ) than function specific setup will still receive the original request including unaltered wLength so it 
may again return a number of bytes greater than buffer size resulting in an overflow.
Szymon Heidrich Nov. 28, 2022, 9:21 a.m. UTC | #6
On 20/11/2022 16:29, Szymon Heidrich wrote:
> On 20/11/2022 15:43, Marek Vasut wrote:
>> On 11/17/22 12:50, Fabio Estevam wrote:
>>> [Adding Lukasz and Marek]
>>>
>>> On Thu, Nov 17, 2022 at 6:50 AM Szymon Heidrich
>>> <szymon.heidrich@gmail.com> wrote:
>>>>
>>>> Assure that the control endpoint buffer of size USB_BUFSIZ (4096)
>>>> can not be overflown during handling of USB control transfer
>>>> requests with wLength greater than USB_BUFSIZ.
>>>>
>>>> Signed-off-by: Szymon Heidrich <szymon.heidrich@gmail.com>
>>>> ---
>>>>   drivers/usb/gadget/composite.c | 11 +++++++++++
>>>>   1 file changed, 11 insertions(+)
>>>>
>>>> diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c
>>>> index 2a309e624e..cb89f6dca9 100644
>>>> --- a/drivers/usb/gadget/composite.c
>>>> +++ b/drivers/usb/gadget/composite.c
>>>> @@ -1019,6 +1019,17 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
>>>>          u8                              endp;
>>>>          struct usb_configuration        *c;
>>>>
>>>> +       if (w_length > USB_BUFSIZ) {
>>>> +               if (ctrl->bRequestType & USB_DIR_IN) {
>>>> +                       /* Cast away the const, we are going to overwrite on purpose. */
>>>> +                       __le16 *temp = (__le16 *)&ctrl->wLength;
>>>> +                       *temp = cpu_to_le16(USB_BUFSIZ);
>>>> +                       w_length = USB_BUFSIZ;
>>
>> Won't this end up sending corrupted packets in case they are longer than USB_BUFSIZ ?
>>
>> Where do such long packets come from ?
>>
>> What is the test-case ?
> 
> The USB host will not attempt to retrieve more than wLenght bytes during transfer phase.
> If the device would erroneously attempt to provide more data it would result in an unexpected state.
> In case of most implementations the buffer for endpoint 0 along with max control transfer is limited to 4096 bytes (USB_BUFSIZ for U-Boot and Linux kernel).
> Still according to the USB specification wLength is two bytes an the device may receive requests with wLength larger than 4096 bytes e.g. in case of a custom/malicious USB host.
> For example one may build libusb with MAX_CTRL_BUFFER_LENGTH altered to 0xffff and this will allow the host to send requests with wLength up to 0xffff.
> In this case the original implementation may result in buffer overflows as in multiple locations a value directly derived from wLength is set as the transfer phase length.
> With the change applied IN requests with wLength larger than USB_BUFSIZ will be trimmed to USB_BUFSIZ, otherwise the host would read wLength-USB_BUFSIZ past cdev->req->buf.
> I am not aware of any cases where more than USB_BUFSIZ would be provided from a buffer other than cdev->req->buf. In case I missed such case please let me know.

Is there anything additional required from my side?
Marek Vasut Nov. 28, 2022, 9:27 a.m. UTC | #7
On 11/28/22 10:21, Szymon Heidrich wrote:
> On 20/11/2022 16:29, Szymon Heidrich wrote:
>> On 20/11/2022 15:43, Marek Vasut wrote:
>>> On 11/17/22 12:50, Fabio Estevam wrote:
>>>> [Adding Lukasz and Marek]
>>>>
>>>> On Thu, Nov 17, 2022 at 6:50 AM Szymon Heidrich
>>>> <szymon.heidrich@gmail.com> wrote:
>>>>>
>>>>> Assure that the control endpoint buffer of size USB_BUFSIZ (4096)
>>>>> can not be overflown during handling of USB control transfer
>>>>> requests with wLength greater than USB_BUFSIZ.
>>>>>
>>>>> Signed-off-by: Szymon Heidrich <szymon.heidrich@gmail.com>
>>>>> ---
>>>>>    drivers/usb/gadget/composite.c | 11 +++++++++++
>>>>>    1 file changed, 11 insertions(+)
>>>>>
>>>>> diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c
>>>>> index 2a309e624e..cb89f6dca9 100644
>>>>> --- a/drivers/usb/gadget/composite.c
>>>>> +++ b/drivers/usb/gadget/composite.c
>>>>> @@ -1019,6 +1019,17 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
>>>>>           u8                              endp;
>>>>>           struct usb_configuration        *c;
>>>>>
>>>>> +       if (w_length > USB_BUFSIZ) {
>>>>> +               if (ctrl->bRequestType & USB_DIR_IN) {
>>>>> +                       /* Cast away the const, we are going to overwrite on purpose. */
>>>>> +                       __le16 *temp = (__le16 *)&ctrl->wLength;
>>>>> +                       *temp = cpu_to_le16(USB_BUFSIZ);
>>>>> +                       w_length = USB_BUFSIZ;
>>>
>>> Won't this end up sending corrupted packets in case they are longer than USB_BUFSIZ ?
>>>
>>> Where do such long packets come from ?
>>>
>>> What is the test-case ?
>>
>> The USB host will not attempt to retrieve more than wLenght bytes during transfer phase.
>> If the device would erroneously attempt to provide more data it would result in an unexpected state.
>> In case of most implementations the buffer for endpoint 0 along with max control transfer is limited to 4096 bytes (USB_BUFSIZ for U-Boot and Linux kernel).
>> Still according to the USB specification wLength is two bytes an the device may receive requests with wLength larger than 4096 bytes e.g. in case of a custom/malicious USB host.
>> For example one may build libusb with MAX_CTRL_BUFFER_LENGTH altered to 0xffff and this will allow the host to send requests with wLength up to 0xffff.
>> In this case the original implementation may result in buffer overflows as in multiple locations a value directly derived from wLength is set as the transfer phase length.
>> With the change applied IN requests with wLength larger than USB_BUFSIZ will be trimmed to USB_BUFSIZ, otherwise the host would read wLength-USB_BUFSIZ past cdev->req->buf.
>> I am not aware of any cases where more than USB_BUFSIZ would be provided from a buffer other than cdev->req->buf. In case I missed such case please let me know.
> 
> Is there anything additional required from my side?

Sorry for the delay, I am still processing outstanding email.
Szymon Heidrich Dec. 12, 2022, 9:55 a.m. UTC | #8
On 28/11/2022 10:27, Marek Vasut wrote:
> On 11/28/22 10:21, Szymon Heidrich wrote:
>> On 20/11/2022 16:29, Szymon Heidrich wrote:
>>> On 20/11/2022 15:43, Marek Vasut wrote:
>>>> On 11/17/22 12:50, Fabio Estevam wrote:
>>>>> [Adding Lukasz and Marek]
>>>>>
>>>>> On Thu, Nov 17, 2022 at 6:50 AM Szymon Heidrich
>>>>> <szymon.heidrich@gmail.com> wrote:
>>>>>>
>>>>>> Assure that the control endpoint buffer of size USB_BUFSIZ (4096)
>>>>>> can not be overflown during handling of USB control transfer
>>>>>> requests with wLength greater than USB_BUFSIZ.
>>>>>>
>>>>>> Signed-off-by: Szymon Heidrich <szymon.heidrich@gmail.com>
>>>>>> ---
>>>>>>    drivers/usb/gadget/composite.c | 11 +++++++++++
>>>>>>    1 file changed, 11 insertions(+)
>>>>>>
>>>>>> diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c
>>>>>> index 2a309e624e..cb89f6dca9 100644
>>>>>> --- a/drivers/usb/gadget/composite.c
>>>>>> +++ b/drivers/usb/gadget/composite.c
>>>>>> @@ -1019,6 +1019,17 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
>>>>>>           u8                              endp;
>>>>>>           struct usb_configuration        *c;
>>>>>>
>>>>>> +       if (w_length > USB_BUFSIZ) {
>>>>>> +               if (ctrl->bRequestType & USB_DIR_IN) {
>>>>>> +                       /* Cast away the const, we are going to overwrite on purpose. */
>>>>>> +                       __le16 *temp = (__le16 *)&ctrl->wLength;
>>>>>> +                       *temp = cpu_to_le16(USB_BUFSIZ);
>>>>>> +                       w_length = USB_BUFSIZ;
>>>>
>>>> Won't this end up sending corrupted packets in case they are longer than USB_BUFSIZ ?
>>>>
>>>> Where do such long packets come from ?
>>>>
>>>> What is the test-case ?
>>>
>>> The USB host will not attempt to retrieve more than wLenght bytes during transfer phase.
>>> If the device would erroneously attempt to provide more data it would result in an unexpected state.
>>> In case of most implementations the buffer for endpoint 0 along with max control transfer is limited to 4096 bytes (USB_BUFSIZ for U-Boot and Linux kernel).
>>> Still according to the USB specification wLength is two bytes an the device may receive requests with wLength larger than 4096 bytes e.g. in case of a custom/malicious USB host.
>>> For example one may build libusb with MAX_CTRL_BUFFER_LENGTH altered to 0xffff and this will allow the host to send requests with wLength up to 0xffff.
>>> In this case the original implementation may result in buffer overflows as in multiple locations a value directly derived from wLength is set as the transfer phase length.
>>> With the change applied IN requests with wLength larger than USB_BUFSIZ will be trimmed to USB_BUFSIZ, otherwise the host would read wLength-USB_BUFSIZ past cdev->req->buf.
>>> I am not aware of any cases where more than USB_BUFSIZ would be provided from a buffer other than cdev->req->buf. In case I missed such case please let me know.
>>
>> Is there anything additional required from my side?
> 
> Sorry for the delay, I am still processing outstanding email.

Could you please review this patch?
Marek Vasut Dec. 12, 2022, 1:24 p.m. UTC | #9
On 12/12/22 10:55, Szymon Heidrich wrote:
> On 28/11/2022 10:27, Marek Vasut wrote:
>> On 11/28/22 10:21, Szymon Heidrich wrote:
>>> On 20/11/2022 16:29, Szymon Heidrich wrote:
>>>> On 20/11/2022 15:43, Marek Vasut wrote:
>>>>> On 11/17/22 12:50, Fabio Estevam wrote:
>>>>>> [Adding Lukasz and Marek]
>>>>>>
>>>>>> On Thu, Nov 17, 2022 at 6:50 AM Szymon Heidrich
>>>>>> <szymon.heidrich@gmail.com> wrote:
>>>>>>>
>>>>>>> Assure that the control endpoint buffer of size USB_BUFSIZ (4096)
>>>>>>> can not be overflown during handling of USB control transfer
>>>>>>> requests with wLength greater than USB_BUFSIZ.
>>>>>>>
>>>>>>> Signed-off-by: Szymon Heidrich <szymon.heidrich@gmail.com>
>>>>>>> ---
>>>>>>>     drivers/usb/gadget/composite.c | 11 +++++++++++
>>>>>>>     1 file changed, 11 insertions(+)
>>>>>>>
>>>>>>> diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c
>>>>>>> index 2a309e624e..cb89f6dca9 100644
>>>>>>> --- a/drivers/usb/gadget/composite.c
>>>>>>> +++ b/drivers/usb/gadget/composite.c
>>>>>>> @@ -1019,6 +1019,17 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
>>>>>>>            u8                              endp;
>>>>>>>            struct usb_configuration        *c;
>>>>>>>
>>>>>>> +       if (w_length > USB_BUFSIZ) {
>>>>>>> +               if (ctrl->bRequestType & USB_DIR_IN) {
>>>>>>> +                       /* Cast away the const, we are going to overwrite on purpose. */
>>>>>>> +                       __le16 *temp = (__le16 *)&ctrl->wLength;
>>>>>>> +                       *temp = cpu_to_le16(USB_BUFSIZ);
>>>>>>> +                       w_length = USB_BUFSIZ;
>>>>>
>>>>> Won't this end up sending corrupted packets in case they are longer than USB_BUFSIZ ?
>>>>>
>>>>> Where do such long packets come from ?
>>>>>
>>>>> What is the test-case ?
>>>>
>>>> The USB host will not attempt to retrieve more than wLenght bytes during transfer phase.
>>>> If the device would erroneously attempt to provide more data it would result in an unexpected state.
>>>> In case of most implementations the buffer for endpoint 0 along with max control transfer is limited to 4096 bytes (USB_BUFSIZ for U-Boot and Linux kernel).
>>>> Still according to the USB specification wLength is two bytes an the device may receive requests with wLength larger than 4096 bytes e.g. in case of a custom/malicious USB host.
>>>> For example one may build libusb with MAX_CTRL_BUFFER_LENGTH altered to 0xffff and this will allow the host to send requests with wLength up to 0xffff.
>>>> In this case the original implementation may result in buffer overflows as in multiple locations a value directly derived from wLength is set as the transfer phase length.
>>>> With the change applied IN requests with wLength larger than USB_BUFSIZ will be trimmed to USB_BUFSIZ, otherwise the host would read wLength-USB_BUFSIZ past cdev->req->buf.
>>>> I am not aware of any cases where more than USB_BUFSIZ would be provided from a buffer other than cdev->req->buf. In case I missed such case please let me know.
>>>
>>> Is there anything additional required from my side?
>>
>> Sorry for the delay, I am still processing outstanding email.
> 
> Could you please review this patch?

I have it (or rather, one outstanding reply) in the pipeline, sorry for 
the delay.
Marek Vasut Dec. 16, 2022, 3:22 a.m. UTC | #10
On 11/20/22 18:42, Szymon Heidrich wrote:
> On 20/11/2022 18:25, Marek Vasut wrote:
>> On 11/20/22 16:29, Szymon Heidrich wrote:
>>> On 20/11/2022 15:43, Marek Vasut wrote:
>>>> On 11/17/22 12:50, Fabio Estevam wrote:
>>>>> [Adding Lukasz and Marek]
>>>>>
>>>>> On Thu, Nov 17, 2022 at 6:50 AM Szymon Heidrich
>>>>> <szymon.heidrich@gmail.com> wrote:
>>>>>>
>>>>>> Assure that the control endpoint buffer of size USB_BUFSIZ (4096)
>>>>>> can not be overflown during handling of USB control transfer
>>>>>> requests with wLength greater than USB_BUFSIZ.
>>>>>>
>>>>>> Signed-off-by: Szymon Heidrich <szymon.heidrich@gmail.com>
>>>>>> ---
>>>>>>     drivers/usb/gadget/composite.c | 11 +++++++++++
>>>>>>     1 file changed, 11 insertions(+)
>>>>>>
>>>>>> diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c
>>>>>> index 2a309e624e..cb89f6dca9 100644
>>>>>> --- a/drivers/usb/gadget/composite.c
>>>>>> +++ b/drivers/usb/gadget/composite.c
>>>>>> @@ -1019,6 +1019,17 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
>>>>>>            u8                              endp;
>>>>>>            struct usb_configuration        *c;
>>>>>>
>>>>>> +       if (w_length > USB_BUFSIZ) {
>>>>>> +               if (ctrl->bRequestType & USB_DIR_IN) {
>>>>>> +                       /* Cast away the const, we are going to overwrite on purpose. */
>>>>>> +                       __le16 *temp = (__le16 *)&ctrl->wLength;
>>>>>> +                       *temp = cpu_to_le16(USB_BUFSIZ);
>>>>>> +                       w_length = USB_BUFSIZ;
>>>>
>>>> Won't this end up sending corrupted packets in case they are longer than USB_BUFSIZ ?
>>>>
>>>> Where do such long packets come from ?
>>>>
>>>> What is the test-case ?
>>>
>>> The USB host will not attempt to retrieve more than wLenght bytes during transfer phase.
>>> If the device would erroneously attempt to provide more data it would result in an unexpected state.
>>> In case of most implementations the buffer for endpoint 0 along with max control transfer is limited to 4096 bytes (USB_BUFSIZ for U-Boot and Linux kernel).
>>> Still according to the USB specification wLength is two bytes an the device may receive requests with wLength larger than 4096 bytes e.g. in case of a custom/malicious USB host.
>>> For example one may build libusb with MAX_CTRL_BUFFER_LENGTH altered to 0xffff and this will allow the host to send requests with wLength up to 0xffff.
>>> In this case the original implementation may result in buffer overflows as in multiple locations a value directly derived from wLength is set as the transfer phase length.
>>> With the change applied IN requests with wLength larger than USB_BUFSIZ will be trimmed to USB_BUFSIZ, otherwise the host would read wLength-USB_BUFSIZ past cdev->req->buf.
>>> I am not aware of any cases where more than USB_BUFSIZ would be provided from a buffer other than cdev->req->buf. In case I missed such case please let me know.
>>
>> Why shouldn't the patch do simple
>>
>> w_length = min(w_length, USB_BUFSIZ);
>>
>> ?
> 
> The composite_setup function in composite.c uses w_length but function specific setup functions are passed the struct usb_ctrlrequest *ctrl so both must be updated.
> If only w_length is updated using min(w_length, USB_BUFSIZ) than function specific setup will still receive the original request including unaltered wLength so it
> may again return a number of bytes greater than buffer size resulting in an overflow.

Can you fill this sanitization into the setup wrapper here ?

https://patchwork.ozlabs.org/project/uboot/patch/20221216032047.536441-1-marex@denx.de/

That way, it would apply for all the gadget drivers and you won't have 
to deal with the const overwrite , right ?
Szymon Heidrich Dec. 19, 2022, 6:52 p.m. UTC | #11
On 16/12/2022 04:22, Marek Vasut wrote:
> On 11/20/22 18:42, Szymon Heidrich wrote:
>> On 20/11/2022 18:25, Marek Vasut wrote:
>>> On 11/20/22 16:29, Szymon Heidrich wrote:
>>>> On 20/11/2022 15:43, Marek Vasut wrote:
>>>>> On 11/17/22 12:50, Fabio Estevam wrote:
>>>>>> [Adding Lukasz and Marek]
>>>>>>
>>>>>> On Thu, Nov 17, 2022 at 6:50 AM Szymon Heidrich
>>>>>> <szymon.heidrich@gmail.com> wrote:
>>>>>>>
>>>>>>> Assure that the control endpoint buffer of size USB_BUFSIZ (4096)
>>>>>>> can not be overflown during handling of USB control transfer
>>>>>>> requests with wLength greater than USB_BUFSIZ.
>>>>>>>
>>>>>>> Signed-off-by: Szymon Heidrich <szymon.heidrich@gmail.com>
>>>>>>> ---
>>>>>>>     drivers/usb/gadget/composite.c | 11 +++++++++++
>>>>>>>     1 file changed, 11 insertions(+)
>>>>>>>
>>>>>>> diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c
>>>>>>> index 2a309e624e..cb89f6dca9 100644
>>>>>>> --- a/drivers/usb/gadget/composite.c
>>>>>>> +++ b/drivers/usb/gadget/composite.c
>>>>>>> @@ -1019,6 +1019,17 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
>>>>>>>            u8                              endp;
>>>>>>>            struct usb_configuration        *c;
>>>>>>>
>>>>>>> +       if (w_length > USB_BUFSIZ) {
>>>>>>> +               if (ctrl->bRequestType & USB_DIR_IN) {
>>>>>>> +                       /* Cast away the const, we are going to overwrite on purpose. */
>>>>>>> +                       __le16 *temp = (__le16 *)&ctrl->wLength;
>>>>>>> +                       *temp = cpu_to_le16(USB_BUFSIZ);
>>>>>>> +                       w_length = USB_BUFSIZ;
>>>>>
>>>>> Won't this end up sending corrupted packets in case they are longer than USB_BUFSIZ ?
>>>>>
>>>>> Where do such long packets come from ?
>>>>>
>>>>> What is the test-case ?
>>>>
>>>> The USB host will not attempt to retrieve more than wLenght bytes during transfer phase.
>>>> If the device would erroneously attempt to provide more data it would result in an unexpected state.
>>>> In case of most implementations the buffer for endpoint 0 along with max control transfer is limited to 4096 bytes (USB_BUFSIZ for U-Boot and Linux kernel).
>>>> Still according to the USB specification wLength is two bytes an the device may receive requests with wLength larger than 4096 bytes e.g. in case of a custom/malicious USB host.
>>>> For example one may build libusb with MAX_CTRL_BUFFER_LENGTH altered to 0xffff and this will allow the host to send requests with wLength up to 0xffff.
>>>> In this case the original implementation may result in buffer overflows as in multiple locations a value directly derived from wLength is set as the transfer phase length.
>>>> With the change applied IN requests with wLength larger than USB_BUFSIZ will be trimmed to USB_BUFSIZ, otherwise the host would read wLength-USB_BUFSIZ past cdev->req->buf.
>>>> I am not aware of any cases where more than USB_BUFSIZ would be provided from a buffer other than cdev->req->buf. In case I missed such case please let me know.
>>>
>>> Why shouldn't the patch do simple
>>>
>>> w_length = min(w_length, USB_BUFSIZ);
>>>
>>> ?
>>
>> The composite_setup function in composite.c uses w_length but function specific setup functions are passed the struct usb_ctrlrequest *ctrl so both must be updated.
>> If only w_length is updated using min(w_length, USB_BUFSIZ) than function specific setup will still receive the original request including unaltered wLength so it
>> may again return a number of bytes greater than buffer size resulting in an overflow.
> 
> Can you fill this sanitization into the setup wrapper here ?
> 
> https://patchwork.ozlabs.org/project/uboot/patch/20221216032047.536441-1-marex@denx.de/
> 
> That way, it would apply for all the gadget drivers and you won't have to deal with the const overwrite , right ?

Sure, that should be fine. 
I'll provide a patch when when this is merged, OK?
Marek Vasut Dec. 19, 2022, 7:46 p.m. UTC | #12
On 12/19/22 19:52, Szymon Heidrich wrote:
> On 16/12/2022 04:22, Marek Vasut wrote:
>> On 11/20/22 18:42, Szymon Heidrich wrote:
>>> On 20/11/2022 18:25, Marek Vasut wrote:
>>>> On 11/20/22 16:29, Szymon Heidrich wrote:
>>>>> On 20/11/2022 15:43, Marek Vasut wrote:
>>>>>> On 11/17/22 12:50, Fabio Estevam wrote:
>>>>>>> [Adding Lukasz and Marek]
>>>>>>>
>>>>>>> On Thu, Nov 17, 2022 at 6:50 AM Szymon Heidrich
>>>>>>> <szymon.heidrich@gmail.com> wrote:
>>>>>>>>
>>>>>>>> Assure that the control endpoint buffer of size USB_BUFSIZ (4096)
>>>>>>>> can not be overflown during handling of USB control transfer
>>>>>>>> requests with wLength greater than USB_BUFSIZ.
>>>>>>>>
>>>>>>>> Signed-off-by: Szymon Heidrich <szymon.heidrich@gmail.com>
>>>>>>>> ---
>>>>>>>>      drivers/usb/gadget/composite.c | 11 +++++++++++
>>>>>>>>      1 file changed, 11 insertions(+)
>>>>>>>>
>>>>>>>> diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c
>>>>>>>> index 2a309e624e..cb89f6dca9 100644
>>>>>>>> --- a/drivers/usb/gadget/composite.c
>>>>>>>> +++ b/drivers/usb/gadget/composite.c
>>>>>>>> @@ -1019,6 +1019,17 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
>>>>>>>>             u8                              endp;
>>>>>>>>             struct usb_configuration        *c;
>>>>>>>>
>>>>>>>> +       if (w_length > USB_BUFSIZ) {
>>>>>>>> +               if (ctrl->bRequestType & USB_DIR_IN) {
>>>>>>>> +                       /* Cast away the const, we are going to overwrite on purpose. */
>>>>>>>> +                       __le16 *temp = (__le16 *)&ctrl->wLength;
>>>>>>>> +                       *temp = cpu_to_le16(USB_BUFSIZ);
>>>>>>>> +                       w_length = USB_BUFSIZ;
>>>>>>
>>>>>> Won't this end up sending corrupted packets in case they are longer than USB_BUFSIZ ?
>>>>>>
>>>>>> Where do such long packets come from ?
>>>>>>
>>>>>> What is the test-case ?
>>>>>
>>>>> The USB host will not attempt to retrieve more than wLenght bytes during transfer phase.
>>>>> If the device would erroneously attempt to provide more data it would result in an unexpected state.
>>>>> In case of most implementations the buffer for endpoint 0 along with max control transfer is limited to 4096 bytes (USB_BUFSIZ for U-Boot and Linux kernel).
>>>>> Still according to the USB specification wLength is two bytes an the device may receive requests with wLength larger than 4096 bytes e.g. in case of a custom/malicious USB host.
>>>>> For example one may build libusb with MAX_CTRL_BUFFER_LENGTH altered to 0xffff and this will allow the host to send requests with wLength up to 0xffff.
>>>>> In this case the original implementation may result in buffer overflows as in multiple locations a value directly derived from wLength is set as the transfer phase length.
>>>>> With the change applied IN requests with wLength larger than USB_BUFSIZ will be trimmed to USB_BUFSIZ, otherwise the host would read wLength-USB_BUFSIZ past cdev->req->buf.
>>>>> I am not aware of any cases where more than USB_BUFSIZ would be provided from a buffer other than cdev->req->buf. In case I missed such case please let me know.
>>>>
>>>> Why shouldn't the patch do simple
>>>>
>>>> w_length = min(w_length, USB_BUFSIZ);
>>>>
>>>> ?
>>>
>>> The composite_setup function in composite.c uses w_length but function specific setup functions are passed the struct usb_ctrlrequest *ctrl so both must be updated.
>>> If only w_length is updated using min(w_length, USB_BUFSIZ) than function specific setup will still receive the original request including unaltered wLength so it
>>> may again return a number of bytes greater than buffer size resulting in an overflow.
>>
>> Can you fill this sanitization into the setup wrapper here ?
>>
>> https://patchwork.ozlabs.org/project/uboot/patch/20221216032047.536441-1-marex@denx.de/
>>
>> That way, it would apply for all the gadget drivers and you won't have to deal with the const overwrite , right ?
> 
> Sure, that should be fine.
> I'll provide a patch when when this is merged, OK?

Please review/test the aforementioned patch and let me know if it works 
for you, the patch can still be tweaked one way or the other.
diff mbox series

Patch

diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c
index 2a309e624e..cb89f6dca9 100644
--- a/drivers/usb/gadget/composite.c
+++ b/drivers/usb/gadget/composite.c
@@ -1019,6 +1019,17 @@  composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
 	u8				endp;
 	struct usb_configuration	*c;
 
+	if (w_length > USB_BUFSIZ) {
+		if (ctrl->bRequestType & USB_DIR_IN) {
+			/* Cast away the const, we are going to overwrite on purpose. */
+			__le16 *temp = (__le16 *)&ctrl->wLength;
+			*temp = cpu_to_le16(USB_BUFSIZ);
+			w_length = USB_BUFSIZ;
+		} else {
+			goto done;
+		}
+	}
+
 	/*
 	 * partial re-init of the response message; the function or the
 	 * gadget might need to intercept e.g. a control-OUT completion