Skip to content

Commit 33186c4

Browse files
TomServo3000gregkh
authored andcommitted
usb: wusbcore: avoid stack overflow in URB enqueue error path
This patch modifies wa_urb_enqueue to return an error and not call the urb completion routine if it failed to enqueue the urb because the HWA device is gone. This prevents a stack overflow due to infinite submit/complete recursion when unplugging the HWA while connected to a HID device. Signed-off-by: Thomas Pugliese <[email protected]> Signed-off-by: Greg Kroah-Hartman <[email protected]>
1 parent 02c123e commit 33186c4

File tree

1 file changed

+38
-13
lines changed

1 file changed

+38
-13
lines changed

drivers/usb/wusbcore/wa-xfer.c

Lines changed: 38 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1052,7 +1052,7 @@ static int __wa_xfer_submit(struct wa_xfer *xfer)
10521052
* result never kicks in, the xfer will timeout from the USB code and
10531053
* dequeue() will be called.
10541054
*/
1055-
static void wa_urb_enqueue_b(struct wa_xfer *xfer)
1055+
static int wa_urb_enqueue_b(struct wa_xfer *xfer)
10561056
{
10571057
int result;
10581058
unsigned long flags;
@@ -1063,40 +1063,51 @@ static void wa_urb_enqueue_b(struct wa_xfer *xfer)
10631063
unsigned done;
10641064

10651065
result = rpipe_get_by_ep(wa, xfer->ep, urb, xfer->gfp);
1066-
if (result < 0)
1066+
if (result < 0) {
1067+
pr_err("%s: error_rpipe_get\n", __func__);
10671068
goto error_rpipe_get;
1069+
}
10681070
result = -ENODEV;
10691071
/* FIXME: segmentation broken -- kills DWA */
10701072
mutex_lock(&wusbhc->mutex); /* get a WUSB dev */
10711073
if (urb->dev == NULL) {
10721074
mutex_unlock(&wusbhc->mutex);
1075+
pr_err("%s: error usb dev gone\n", __func__);
10731076
goto error_dev_gone;
10741077
}
10751078
wusb_dev = __wusb_dev_get_by_usb_dev(wusbhc, urb->dev);
10761079
if (wusb_dev == NULL) {
10771080
mutex_unlock(&wusbhc->mutex);
1081+
pr_err("%s: error wusb dev gone\n", __func__);
10781082
goto error_dev_gone;
10791083
}
10801084
mutex_unlock(&wusbhc->mutex);
10811085

10821086
spin_lock_irqsave(&xfer->lock, flags);
10831087
xfer->wusb_dev = wusb_dev;
10841088
result = urb->status;
1085-
if (urb->status != -EINPROGRESS)
1089+
if (urb->status != -EINPROGRESS) {
1090+
pr_err("%s: error_dequeued\n", __func__);
10861091
goto error_dequeued;
1092+
}
10871093

10881094
result = __wa_xfer_setup(xfer, urb);
1089-
if (result < 0)
1095+
if (result < 0) {
1096+
pr_err("%s: error_xfer_setup\n", __func__);
10901097
goto error_xfer_setup;
1098+
}
10911099
result = __wa_xfer_submit(xfer);
1092-
if (result < 0)
1100+
if (result < 0) {
1101+
pr_err("%s: error_xfer_submit\n", __func__);
10931102
goto error_xfer_submit;
1103+
}
10941104
spin_unlock_irqrestore(&xfer->lock, flags);
1095-
return;
1105+
return 0;
10961106

1097-
/* this is basically wa_xfer_completion() broken up wa_xfer_giveback()
1098-
* does a wa_xfer_put() that will call wa_xfer_destroy() and clean
1099-
* upundo setup().
1107+
/*
1108+
* this is basically wa_xfer_completion() broken up wa_xfer_giveback()
1109+
* does a wa_xfer_put() that will call wa_xfer_destroy() and undo
1110+
* setup().
11001111
*/
11011112
error_xfer_setup:
11021113
error_dequeued:
@@ -1108,15 +1119,16 @@ static void wa_urb_enqueue_b(struct wa_xfer *xfer)
11081119
rpipe_put(xfer->ep->hcpriv);
11091120
error_rpipe_get:
11101121
xfer->result = result;
1111-
wa_xfer_giveback(xfer);
1112-
return;
1122+
return result;
11131123

11141124
error_xfer_submit:
11151125
done = __wa_xfer_is_done(xfer);
11161126
xfer->result = result;
11171127
spin_unlock_irqrestore(&xfer->lock, flags);
11181128
if (done)
11191129
wa_xfer_completion(xfer);
1130+
/* return success since the completion routine will run. */
1131+
return 0;
11201132
}
11211133

11221134
/*
@@ -1150,7 +1162,8 @@ void wa_urb_enqueue_run(struct work_struct *ws)
11501162
list_del_init(&xfer->list_node);
11511163

11521164
urb = xfer->urb;
1153-
wa_urb_enqueue_b(xfer);
1165+
if (wa_urb_enqueue_b(xfer) < 0)
1166+
wa_xfer_giveback(xfer);
11541167
usb_put_urb(urb); /* taken when queuing */
11551168
}
11561169
}
@@ -1256,7 +1269,19 @@ int wa_urb_enqueue(struct wahc *wa, struct usb_host_endpoint *ep,
12561269
spin_unlock_irqrestore(&wa->xfer_list_lock, my_flags);
12571270
queue_work(wusbd, &wa->xfer_enqueue_work);
12581271
} else {
1259-
wa_urb_enqueue_b(xfer);
1272+
result = wa_urb_enqueue_b(xfer);
1273+
if (result < 0) {
1274+
/*
1275+
* URB submit/enqueue failed. Clean up, return an
1276+
* error and do not run the callback. This avoids
1277+
* an infinite submit/complete loop.
1278+
*/
1279+
dev_err(dev, "%s: URB enqueue failed: %d\n",
1280+
__func__, result);
1281+
wa_put(xfer->wa);
1282+
wa_xfer_put(xfer);
1283+
return result;
1284+
}
12601285
}
12611286
return 0;
12621287

0 commit comments

Comments
 (0)