Skip to content

Commit 286d997

Browse files
Badhri Jagan Sridharangregkh
authored andcommitted
usb: gadget: udc: core: Prevent soft_connect_store() race
usb_udc_connect_control(), soft_connect_store() and usb_gadget_deactivate() can potentially race against each other to invoke usb_gadget_connect()/usb_gadget_disconnect(). To prevent this, guard udc->started, gadget->allow_connect, gadget->deactivate and gadget->connect with connect_lock so that ->pullup() is only invoked when the gadget is bound, started and not deactivated. The routines usb_gadget_connect_locked(), usb_gadget_disconnect_locked(), usb_udc_connect_control_locked(), usb_gadget_udc_start_locked(), usb_gadget_udc_stop_locked() are called with this lock held. An earlier version of this commit was reverted due to the crash reported in https://lore.kernel.org/all/ZF4BvgsOyoKxdPFF@francesco-nb.int.toradex.com/. commit 16737e78d190 ("usb: gadget: udc: core: Offload usb_udc_vbus_handler processing") addresses the crash reported. Cc: stable@vger.kernel.org Fixes: 628ef0d ("usb: udc: add usb_udc_vbus_handler") Signed-off-by: Badhri Jagan Sridharan <badhri@google.com> Reviewed-by: Alan Stern <stern@rowland.harvard.edu> Message-ID: <20230609010227.978661-2-badhri@google.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
1 parent 50966da commit 286d997

File tree

1 file changed

+106
-49
lines changed

1 file changed

+106
-49
lines changed

drivers/usb/gadget/udc/core.c

Lines changed: 106 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,11 @@ static const struct bus_type gadget_bus_type;
4040
* @allow_connect: Indicates whether UDC is allowed to be pulled up.
4141
* Set/cleared by gadget_(un)bind_driver() after gadget driver is bound or
4242
* unbound.
43+
* @connect_lock: protects udc->started, gadget->connect,
44+
* gadget->allow_connect and gadget->deactivate. The routines
45+
* usb_gadget_connect_locked(), usb_gadget_disconnect_locked(),
46+
* usb_udc_connect_control_locked(), usb_gadget_udc_start_locked() and
47+
* usb_gadget_udc_stop_locked() are called with this lock held.
4348
*
4449
* This represents the internal data structure which is used by the UDC-class
4550
* to hold information about udc driver and gadget together.
@@ -53,6 +58,7 @@ struct usb_udc {
5358
bool started;
5459
bool allow_connect;
5560
struct work_struct vbus_work;
61+
struct mutex connect_lock;
5662
};
5763

5864
static struct class *udc_class;
@@ -692,17 +698,8 @@ int usb_gadget_vbus_disconnect(struct usb_gadget *gadget)
692698
}
693699
EXPORT_SYMBOL_GPL(usb_gadget_vbus_disconnect);
694700

695-
/**
696-
* usb_gadget_connect - software-controlled connect to USB host
697-
* @gadget:the peripheral being connected
698-
*
699-
* Enables the D+ (or potentially D-) pullup. The host will start
700-
* enumerating this gadget when the pullup is active and a VBUS session
701-
* is active (the link is powered).
702-
*
703-
* Returns zero on success, else negative errno.
704-
*/
705-
int usb_gadget_connect(struct usb_gadget *gadget)
701+
static int usb_gadget_connect_locked(struct usb_gadget *gadget)
702+
__must_hold(&gadget->udc->connect_lock)
706703
{
707704
int ret = 0;
708705

@@ -711,10 +708,12 @@ int usb_gadget_connect(struct usb_gadget *gadget)
711708
goto out;
712709
}
713710

714-
if (gadget->deactivated || !gadget->udc->allow_connect) {
711+
if (gadget->deactivated || !gadget->udc->allow_connect || !gadget->udc->started) {
715712
/*
716-
* If gadget is deactivated we only save new state.
717-
* Gadget will be connected automatically after activation.
713+
* If the gadget isn't usable (because it is deactivated,
714+
* unbound, or not yet started), we only save the new state.
715+
* The gadget will be connected automatically when it is
716+
* activated/bound/started.
718717
*/
719718
gadget->connected = true;
720719
goto out;
@@ -729,22 +728,31 @@ int usb_gadget_connect(struct usb_gadget *gadget)
729728

730729
return ret;
731730
}
732-
EXPORT_SYMBOL_GPL(usb_gadget_connect);
733731

734732
/**
735-
* usb_gadget_disconnect - software-controlled disconnect from USB host
736-
* @gadget:the peripheral being disconnected
737-
*
738-
* Disables the D+ (or potentially D-) pullup, which the host may see
739-
* as a disconnect (when a VBUS session is active). Not all systems
740-
* support software pullup controls.
733+
* usb_gadget_connect - software-controlled connect to USB host
734+
* @gadget:the peripheral being connected
741735
*
742-
* Following a successful disconnect, invoke the ->disconnect() callback
743-
* for the current gadget driver so that UDC drivers don't need to.
736+
* Enables the D+ (or potentially D-) pullup. The host will start
737+
* enumerating this gadget when the pullup is active and a VBUS session
738+
* is active (the link is powered).
744739
*
745740
* Returns zero on success, else negative errno.
746741
*/
747-
int usb_gadget_disconnect(struct usb_gadget *gadget)
742+
int usb_gadget_connect(struct usb_gadget *gadget)
743+
{
744+
int ret;
745+
746+
mutex_lock(&gadget->udc->connect_lock);
747+
ret = usb_gadget_connect_locked(gadget);
748+
mutex_unlock(&gadget->udc->connect_lock);
749+
750+
return ret;
751+
}
752+
EXPORT_SYMBOL_GPL(usb_gadget_connect);
753+
754+
static int usb_gadget_disconnect_locked(struct usb_gadget *gadget)
755+
__must_hold(&gadget->udc->connect_lock)
748756
{
749757
int ret = 0;
750758

@@ -756,7 +764,7 @@ int usb_gadget_disconnect(struct usb_gadget *gadget)
756764
if (!gadget->connected)
757765
goto out;
758766

759-
if (gadget->deactivated) {
767+
if (gadget->deactivated || !gadget->udc->started) {
760768
/*
761769
* If gadget is deactivated we only save new state.
762770
* Gadget will stay disconnected after activation.
@@ -779,6 +787,30 @@ int usb_gadget_disconnect(struct usb_gadget *gadget)
779787

780788
return ret;
781789
}
790+
791+
/**
792+
* usb_gadget_disconnect - software-controlled disconnect from USB host
793+
* @gadget:the peripheral being disconnected
794+
*
795+
* Disables the D+ (or potentially D-) pullup, which the host may see
796+
* as a disconnect (when a VBUS session is active). Not all systems
797+
* support software pullup controls.
798+
*
799+
* Following a successful disconnect, invoke the ->disconnect() callback
800+
* for the current gadget driver so that UDC drivers don't need to.
801+
*
802+
* Returns zero on success, else negative errno.
803+
*/
804+
int usb_gadget_disconnect(struct usb_gadget *gadget)
805+
{
806+
int ret;
807+
808+
mutex_lock(&gadget->udc->connect_lock);
809+
ret = usb_gadget_disconnect_locked(gadget);
810+
mutex_unlock(&gadget->udc->connect_lock);
811+
812+
return ret;
813+
}
782814
EXPORT_SYMBOL_GPL(usb_gadget_disconnect);
783815

784816
/**
@@ -796,13 +828,14 @@ int usb_gadget_deactivate(struct usb_gadget *gadget)
796828
{
797829
int ret = 0;
798830

831+
mutex_lock(&gadget->udc->connect_lock);
799832
if (gadget->deactivated)
800-
goto out;
833+
goto unlock;
801834

802835
if (gadget->connected) {
803-
ret = usb_gadget_disconnect(gadget);
836+
ret = usb_gadget_disconnect_locked(gadget);
804837
if (ret)
805-
goto out;
838+
goto unlock;
806839

807840
/*
808841
* If gadget was being connected before deactivation, we want
@@ -812,7 +845,8 @@ int usb_gadget_deactivate(struct usb_gadget *gadget)
812845
}
813846
gadget->deactivated = true;
814847

815-
out:
848+
unlock:
849+
mutex_unlock(&gadget->udc->connect_lock);
816850
trace_usb_gadget_deactivate(gadget, ret);
817851

818852
return ret;
@@ -832,8 +866,9 @@ int usb_gadget_activate(struct usb_gadget *gadget)
832866
{
833867
int ret = 0;
834868

869+
mutex_lock(&gadget->udc->connect_lock);
835870
if (!gadget->deactivated)
836-
goto out;
871+
goto unlock;
837872

838873
gadget->deactivated = false;
839874

@@ -842,9 +877,11 @@ int usb_gadget_activate(struct usb_gadget *gadget)
842877
* while it was being deactivated, we call usb_gadget_connect().
843878
*/
844879
if (gadget->connected)
845-
ret = usb_gadget_connect(gadget);
880+
ret = usb_gadget_connect_locked(gadget);
881+
mutex_unlock(&gadget->udc->connect_lock);
846882

847-
out:
883+
unlock:
884+
mutex_unlock(&gadget->udc->connect_lock);
848885
trace_usb_gadget_activate(gadget, ret);
849886

850887
return ret;
@@ -1083,19 +1120,22 @@ EXPORT_SYMBOL_GPL(usb_gadget_set_state);
10831120

10841121
/* ------------------------------------------------------------------------- */
10851122

1086-
static void usb_udc_connect_control(struct usb_udc *udc)
1123+
/* Acquire connect_lock before calling this function. */
1124+
static void usb_udc_connect_control_locked(struct usb_udc *udc) __must_hold(&udc->connect_lock)
10871125
{
10881126
if (udc->vbus)
1089-
usb_gadget_connect(udc->gadget);
1127+
usb_gadget_connect_locked(udc->gadget);
10901128
else
1091-
usb_gadget_disconnect(udc->gadget);
1129+
usb_gadget_disconnect_locked(udc->gadget);
10921130
}
10931131

10941132
static void vbus_event_work(struct work_struct *work)
10951133
{
10961134
struct usb_udc *udc = container_of(work, struct usb_udc, vbus_work);
10971135

1098-
usb_udc_connect_control(udc);
1136+
mutex_lock(&udc->connect_lock);
1137+
usb_udc_connect_control_locked(udc);
1138+
mutex_unlock(&udc->connect_lock);
10991139
}
11001140

11011141
/**
@@ -1144,7 +1184,7 @@ void usb_gadget_udc_reset(struct usb_gadget *gadget,
11441184
EXPORT_SYMBOL_GPL(usb_gadget_udc_reset);
11451185

11461186
/**
1147-
* usb_gadget_udc_start - tells usb device controller to start up
1187+
* usb_gadget_udc_start_locked - tells usb device controller to start up
11481188
* @udc: The UDC to be started
11491189
*
11501190
* This call is issued by the UDC Class driver when it's about
@@ -1155,8 +1195,11 @@ EXPORT_SYMBOL_GPL(usb_gadget_udc_reset);
11551195
* necessary to have it powered on.
11561196
*
11571197
* Returns zero on success, else negative errno.
1198+
*
1199+
* Caller should acquire connect_lock before invoking this function.
11581200
*/
1159-
static inline int usb_gadget_udc_start(struct usb_udc *udc)
1201+
static inline int usb_gadget_udc_start_locked(struct usb_udc *udc)
1202+
__must_hold(&udc->connect_lock)
11601203
{
11611204
int ret;
11621205

@@ -1173,7 +1216,7 @@ static inline int usb_gadget_udc_start(struct usb_udc *udc)
11731216
}
11741217

11751218
/**
1176-
* usb_gadget_udc_stop - tells usb device controller we don't need it anymore
1219+
* usb_gadget_udc_stop_locked - tells usb device controller we don't need it anymore
11771220
* @udc: The UDC to be stopped
11781221
*
11791222
* This call is issued by the UDC Class driver after calling
@@ -1182,8 +1225,11 @@ static inline int usb_gadget_udc_start(struct usb_udc *udc)
11821225
* The details are implementation specific, but it can go as
11831226
* far as powering off UDC completely and disable its data
11841227
* line pullups.
1228+
*
1229+
* Caller should acquire connect lock before invoking this function.
11851230
*/
1186-
static inline void usb_gadget_udc_stop(struct usb_udc *udc)
1231+
static inline void usb_gadget_udc_stop_locked(struct usb_udc *udc)
1232+
__must_hold(&udc->connect_lock)
11871233
{
11881234
if (!udc->started) {
11891235
dev_err(&udc->dev, "UDC had already stopped\n");
@@ -1342,6 +1388,7 @@ int usb_add_gadget(struct usb_gadget *gadget)
13421388

13431389
udc->gadget = gadget;
13441390
gadget->udc = udc;
1391+
mutex_init(&udc->connect_lock);
13451392

13461393
udc->started = false;
13471394

@@ -1545,12 +1592,16 @@ static int gadget_bind_driver(struct device *dev)
15451592
if (ret)
15461593
goto err_bind;
15471594

1548-
ret = usb_gadget_udc_start(udc);
1549-
if (ret)
1595+
mutex_lock(&udc->connect_lock);
1596+
ret = usb_gadget_udc_start_locked(udc);
1597+
if (ret) {
1598+
mutex_unlock(&udc->connect_lock);
15501599
goto err_start;
1600+
}
15511601
usb_gadget_enable_async_callbacks(udc);
15521602
udc->allow_connect = true;
1553-
usb_udc_connect_control(udc);
1603+
usb_udc_connect_control_locked(udc);
1604+
mutex_unlock(&udc->connect_lock);
15541605

15551606
kobject_uevent(&udc->dev.kobj, KOBJ_CHANGE);
15561607
return 0;
@@ -1583,12 +1634,14 @@ static void gadget_unbind_driver(struct device *dev)
15831634

15841635
udc->allow_connect = false;
15851636
cancel_work_sync(&udc->vbus_work);
1586-
usb_gadget_disconnect(gadget);
1637+
mutex_lock(&udc->connect_lock);
1638+
usb_gadget_disconnect_locked(gadget);
15871639
usb_gadget_disable_async_callbacks(udc);
15881640
if (gadget->irq)
15891641
synchronize_irq(gadget->irq);
15901642
udc->driver->unbind(gadget);
1591-
usb_gadget_udc_stop(udc);
1643+
usb_gadget_udc_stop_locked(udc);
1644+
mutex_unlock(&udc->connect_lock);
15921645

15931646
mutex_lock(&udc_lock);
15941647
driver->is_bound = false;
@@ -1674,11 +1727,15 @@ static ssize_t soft_connect_store(struct device *dev,
16741727
}
16751728

16761729
if (sysfs_streq(buf, "connect")) {
1677-
usb_gadget_udc_start(udc);
1678-
usb_gadget_connect(udc->gadget);
1730+
mutex_lock(&udc->connect_lock);
1731+
usb_gadget_udc_start_locked(udc);
1732+
usb_gadget_connect_locked(udc->gadget);
1733+
mutex_unlock(&udc->connect_lock);
16791734
} else if (sysfs_streq(buf, "disconnect")) {
1680-
usb_gadget_disconnect(udc->gadget);
1681-
usb_gadget_udc_stop(udc);
1735+
mutex_lock(&udc->connect_lock);
1736+
usb_gadget_disconnect_locked(udc->gadget);
1737+
usb_gadget_udc_stop_locked(udc);
1738+
mutex_unlock(&udc->connect_lock);
16821739
} else {
16831740
dev_err(dev, "unsupported command '%s'\n", buf);
16841741
ret = -EINVAL;

0 commit comments

Comments
 (0)