Skip to contentSkip to conversation

usb: gadget: Fix double free of device descriptor pointers

PATCH In review Wesley Cheng 2 participants 7 messages Apr 21, 2021 – Apr 24, 2021

Subsystems (2)
lore
Share & tools
lore
#0 lore raw

PATCH v2 1/1 usb: gadget: Fix double free of device descriptor pointers

From: Hemant Kumar <[email protected]>

Upon driver unbind usb_free_all_descriptors() function frees all speed descriptor pointers without setting them to NULL. In case gadget speed changes (i.e from super speed plus to super speed) after driver unbind only upto super speed descriptor pointers get populated. Super speed plus desc still holds the stale (already freed) pointer. Fix this issue by setting all descriptor pointers to NULL after freeing them in usb_free_all_descriptors().

Fixes f5c61225cf29 ("usb: gadget: Update function for SuperSpeedPlus")

---
Changes in v2:
- Add Reviewed-by and Fixes tags
- CC'ed stable tree

 drivers/usb/gadget/config.c | 4 ++++
 1 file changed, 4 insertions(+)
drivers/usb/gadget/config.c +4 -0
index 2d11535..8bb2577 100644
@@ -194,9 +194,13 @@ EXPORT_SYMBOL_GPL(usb_assign_descriptors);
 void usb_free_all_descriptors(struct usb_function *f)
 {
 	usb_free_descriptors(f->fs_descriptors);
+	f->fs_descriptors = NULL;
 	usb_free_descriptors(f->hs_descriptors);
+	f->hs_descriptors = NULL;
 	usb_free_descriptors(f->ss_descriptors);
+	f->ss_descriptors = NULL;
 	usb_free_descriptors(f->ssp_descriptors);
+	f->ssp_descriptors = NULL;
 }
 EXPORT_SYMBOL_GPL(usb_free_all_descriptors);
 
Signature · 2 lines
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project
#1 lore raw

Hi,

Quoted 9 lines · Wesley Cheng

From: Hemant Kumar <[email protected]>

Upon driver unbind usb_free_all_descriptors() function frees all speed descriptor pointers without setting them to NULL. In case gadget speed changes (i.e from super speed plus to super speed) after driver unbind only upto super speed descriptor pointers get populated. Super speed plus desc still holds the stale (already freed) pointer. Fix this issue by setting all descriptor pointers to NULL after freeing them in usb_free_all_descriptors().

could you describe this a little better? How can one trigger this case? Is the speed demotion happening after unbinding? It's not clear how to cause this bug.

Signature · 1 line
balbi
#2 lore raw
Quoted 15 lines · Felipe Balbi

Hi,

Quoted 9 lines · Wesley Cheng

From: Hemant Kumar <[email protected]>

Upon driver unbind usb_free_all_descriptors() function frees all speed descriptor pointers without setting them to NULL. In case gadget speed changes (i.e from super speed plus to super speed) after driver unbind only upto super speed descriptor pointers get populated. Super speed plus desc still holds the stale (already freed) pointer. Fix this issue by setting all descriptor pointers to NULL after freeing them in usb_free_all_descriptors().

Quoted 4 lines

could you describe this a little better? How can one trigger this case? Is the speed demotion happening after unbinding? It's not clear how to cause this bug.

Hi Felipe,

Internally, we have a mechanism to switch the DWC3 core maximum speed parameter dynamically for displayport use cases. This issue happens whenever we have a maximum speed change occur on the USB gadget, which for DWC3 happens whenever we call gadget init. When we switch in and out of host mode, gadget init is being executed, leading to the change in the USB gadget max speed parameter:

dwc->gadget->max_speed = dwc->maximum_speed;

I know that configFS gadget has the max_speed sysfs file, which is a similar mechanism, but I haven't tried to see if we can reproduce the same issue with it. Let me see if we can reproduce this with that configfs speed setting.

Thanks
Wesley Cheng

Signature · 2 lines
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project
#3 lore raw
Quoted 33 lines · Wesley Cheng
Quoted 15 lines · Felipe Balbi

Hi,

Quoted 9 lines · Wesley Cheng

From: Hemant Kumar <[email protected]>

Upon driver unbind usb_free_all_descriptors() function frees all speed descriptor pointers without setting them to NULL. In case gadget speed changes (i.e from super speed plus to super speed) after driver unbind only upto super speed descriptor pointers get populated. Super speed plus desc still holds the stale (already freed) pointer. Fix this issue by setting all descriptor pointers to NULL after freeing them in usb_free_all_descriptors().

Quoted 4 lines

could you describe this a little better? How can one trigger this case? Is the speed demotion happening after unbinding? It's not clear how to cause this bug.

Hi Felipe,

Internally, we have a mechanism to switch the DWC3 core maximum speed parameter dynamically for displayport use cases. This issue happens whenever we have a maximum speed change occur on the USB gadget, which for DWC3 happens whenever we call gadget init. When we switch in and out of host mode, gadget init is being executed, leading to the change in the USB gadget max speed parameter:

dwc->gadget->max_speed = dwc->maximum_speed;

Quoted 8 lines

I know that configFS gadget has the max_speed sysfs file, which is a similar mechanism, but I haven't tried to see if we can reproduce the same issue with it. Let me see if we can reproduce this with that configfs speed setting.

Thanks
Wesley Cheng

Hi Felipe,

So I tried with doing it through the configFS max_speed, but it doesn't have the same effect, as the setting done in dwc3_gadget_init() will still be assigning the composite/UDC device's maximum speed to SSP/SS. This is what the usb_assign_descriptor() uses to determine whether or not to copy the SSP and SS descriptors.

So in summary, at least for a DWC3 based subsystem, the only way to reproduce it is if there is a way to dynamically switch the DWC3 core max speed parameter.

Thanks
Wesley Cheng

Signature · 2 lines
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project
#4 lore raw

Hi,

Quoted 42 lines · Wesley Cheng
Quoted 25 lines
Quoted 10 lines
Quoted 9 lines

From: Hemant Kumar <[email protected]>

Upon driver unbind usb_free_all_descriptors() function frees all speed descriptor pointers without setting them to NULL. In case gadget speed changes (i.e from super speed plus to super speed) after driver unbind only upto super speed descriptor pointers get populated. Super speed plus desc still holds the stale (already freed) pointer. Fix this issue by setting all descriptor pointers to NULL after freeing them in usb_free_all_descriptors().

Quoted 4 lines

could you describe this a little better? How can one trigger this case? Is the speed demotion happening after unbinding? It's not clear how to cause this bug.

Hi Felipe,

Internally, we have a mechanism to switch the DWC3 core maximum speed parameter dynamically for displayport use cases. This issue happens whenever we have a maximum speed change occur on the USB gadget, which for DWC3 happens whenever we call gadget init. When we switch in and out of host mode, gadget init is being executed, leading to the change in the USB gadget max speed parameter:

dwc->gadget->max_speed = dwc->maximum_speed;

Quoted 8 lines

I know that configFS gadget has the max_speed sysfs file, which is a similar mechanism, but I haven't tried to see if we can reproduce the same issue with it. Let me see if we can reproduce this with that configfs speed setting.

Thanks
Wesley Cheng

Hi Felipe,

So I tried with doing it through the configFS max_speed, but it doesn't have the same effect, as the setting done in dwc3_gadget_init() will still be assigning the composite/UDC device's maximum speed to SSP/SS. This is what the usb_assign_descriptor() uses to determine whether or not to copy the SSP and SS descriptors.

Quoted 3 lines

So in summary, at least for a DWC3 based subsystem, the only way to reproduce it is if there is a way to dynamically switch the DWC3 core max speed parameter.

Could it be that you have a bug in your out-of-tree changes? Perhaps there's some assumption which your changes aren't guaranteeing.

Signature · 1 line
balbi
#5 lore raw
Quoted 46 lines · Felipe Balbi

Hi,

Quoted 42 lines · Wesley Cheng
Quoted 25 lines
Quoted 10 lines
Quoted 9 lines

From: Hemant Kumar <[email protected]>

Upon driver unbind usb_free_all_descriptors() function frees all speed descriptor pointers without setting them to NULL. In case gadget speed changes (i.e from super speed plus to super speed) after driver unbind only upto super speed descriptor pointers get populated. Super speed plus desc still holds the stale (already freed) pointer. Fix this issue by setting all descriptor pointers to NULL after freeing them in usb_free_all_descriptors().

Quoted 4 lines

could you describe this a little better? How can one trigger this case? Is the speed demotion happening after unbinding? It's not clear how to cause this bug.

Hi Felipe,

Internally, we have a mechanism to switch the DWC3 core maximum speed parameter dynamically for displayport use cases. This issue happens whenever we have a maximum speed change occur on the USB gadget, which for DWC3 happens whenever we call gadget init. When we switch in and out of host mode, gadget init is being executed, leading to the change in the USB gadget max speed parameter:

dwc->gadget->max_speed = dwc->maximum_speed;

Quoted 8 lines

I know that configFS gadget has the max_speed sysfs file, which is a similar mechanism, but I haven't tried to see if we can reproduce the same issue with it. Let me see if we can reproduce this with that configfs speed setting.

Thanks
Wesley Cheng

Hi Felipe,

So I tried with doing it through the configFS max_speed, but it doesn't have the same effect, as the setting done in dwc3_gadget_init() will still be assigning the composite/UDC device's maximum speed to SSP/SS. This is what the usb_assign_descriptor() uses to determine whether or not to copy the SSP and SS descriptors.

Quoted 7 lines
Quoted 3 lines

So in summary, at least for a DWC3 based subsystem, the only way to reproduce it is if there is a way to dynamically switch the DWC3 core max speed parameter.

Could it be that you have a bug in your out-of-tree changes? Perhaps there's some assumption which your changes aren't guaranteeing.

Hi Felipe,

Unless there is a limitation on how the USB gadget max speed can be used, i.e. the USB gadget max speed MUST stay the same throughout a device's boot period, then our out of tree changes may have the wrong assumptions. However, I don't see that stated anywhere, and I still feel the current usb_assign_descriptors() and usb_free_all_descriptors() aren't aligned with one another. One API decides which descriptors to copy based on a parameter, whereas the other just frees all of them irrespective.

Thanks
Wesley Cheng

Signature · 2 lines
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project
#6 lore raw

Hi,

Quoted 51 lines · Wesley Cheng
Quoted 50 lines · Felipe Balbi

Hi,

Quoted 45 lines · Wesley Cheng
Quoted 25 lines
Quoted 10 lines
Quoted 9 lines

From: Hemant Kumar <[email protected]>

Upon driver unbind usb_free_all_descriptors() function frees all speed descriptor pointers without setting them to NULL. In case gadget speed changes (i.e from super speed plus to super speed) after driver unbind only upto super speed descriptor pointers get populated. Super speed plus desc still holds the stale (already freed) pointer. Fix this issue by setting all descriptor pointers to NULL after freeing them in usb_free_all_descriptors().

Quoted 4 lines

could you describe this a little better? How can one trigger this case? Is the speed demotion happening after unbinding? It's not clear how to cause this bug.

Hi Felipe,

Internally, we have a mechanism to switch the DWC3 core maximum speed parameter dynamically for displayport use cases. This issue happens whenever we have a maximum speed change occur on the USB gadget, which for DWC3 happens whenever we call gadget init. When we switch in and out of host mode, gadget init is being executed, leading to the change in the USB gadget max speed parameter:

dwc->gadget->max_speed = dwc->maximum_speed;

Quoted 8 lines

I know that configFS gadget has the max_speed sysfs file, which is a similar mechanism, but I haven't tried to see if we can reproduce the same issue with it. Let me see if we can reproduce this with that configfs speed setting.

Thanks
Wesley Cheng

Hi Felipe,

So I tried with doing it through the configFS max_speed, but it doesn't have the same effect, as the setting done in dwc3_gadget_init() will still be assigning the composite/UDC device's maximum speed to SSP/SS. This is what the usb_assign_descriptor() uses to determine whether or not to copy the SSP and SS descriptors.

So in summary, at least for a DWC3 based subsystem, the only way to reproduce it is if there is a way to dynamically switch the DWC3 core max speed parameter.

Quoted 8 lines
Quoted 3 lines

Could it be that you have a bug in your out-of-tree changes? Perhaps there's some assumption which your changes aren't guaranteeing.

Hi Felipe,

Unless there is a limitation on how the USB gadget max speed can be used, i.e. the USB gadget max speed MUST stay the same throughout a device's boot period, then our out of tree changes may have the wrong

no, not throughout boot, but it can't change while the device is enumerated.

Quoted 5 lines

assumptions. However, I don't see that stated anywhere, and I still feel the current usb_assign_descriptors() and usb_free_all_descriptors() aren't aligned with one another. One API decides which descriptors to copy based on a parameter, whereas the other just frees all of them irrespective.

that's a valid point.

Signature · 1 line
balbi