brcmfmac: Fix race condition between USB probe/load and disconnect.
When a USB device gets disconnected due to for example removal then it is possible that it is still in the loading phase due to the asynchronous load routines. These routines can then possible access memory which has been freed. Fix this by mutex locking the device init phase. Reviewed-by: Arend Van Spriel <arend@broadcom.com> Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com> Signed-off-by: Hante Meuleman <meuleman@broadcom.com> Signed-off-by: Arend van Spriel <arend@broadcom.com> Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
This commit is contained in:
parent
ff4445a850
commit
185f0eb0b5
1 changed files with 26 additions and 3 deletions
|
@ -144,6 +144,7 @@ struct brcmf_usbdev_info {
|
||||||
|
|
||||||
struct usb_device *usbdev;
|
struct usb_device *usbdev;
|
||||||
struct device *dev;
|
struct device *dev;
|
||||||
|
struct mutex dev_init_lock;
|
||||||
|
|
||||||
int ctl_in_pipe, ctl_out_pipe;
|
int ctl_in_pipe, ctl_out_pipe;
|
||||||
struct urb *ctl_urb; /* URB for control endpoint */
|
struct urb *ctl_urb; /* URB for control endpoint */
|
||||||
|
@ -1204,6 +1205,8 @@ static void brcmf_usb_probe_phase2(struct device *dev,
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
brcmf_dbg(USB, "Start fw downloading\n");
|
brcmf_dbg(USB, "Start fw downloading\n");
|
||||||
|
|
||||||
|
devinfo = bus->bus_priv.usb->devinfo;
|
||||||
ret = check_file(fw->data);
|
ret = check_file(fw->data);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
brcmf_err("invalid firmware\n");
|
brcmf_err("invalid firmware\n");
|
||||||
|
@ -1211,7 +1214,6 @@ static void brcmf_usb_probe_phase2(struct device *dev,
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
devinfo = bus->bus_priv.usb->devinfo;
|
|
||||||
devinfo->image = fw->data;
|
devinfo->image = fw->data;
|
||||||
devinfo->image_len = fw->size;
|
devinfo->image_len = fw->size;
|
||||||
|
|
||||||
|
@ -1224,9 +1226,11 @@ static void brcmf_usb_probe_phase2(struct device *dev,
|
||||||
if (ret)
|
if (ret)
|
||||||
goto error;
|
goto error;
|
||||||
|
|
||||||
|
mutex_unlock(&devinfo->dev_init_lock);
|
||||||
return;
|
return;
|
||||||
error:
|
error:
|
||||||
brcmf_dbg(TRACE, "failed: dev=%s, err=%d\n", dev_name(dev), ret);
|
brcmf_dbg(TRACE, "failed: dev=%s, err=%d\n", dev_name(dev), ret);
|
||||||
|
mutex_unlock(&devinfo->dev_init_lock);
|
||||||
device_release_driver(dev);
|
device_release_driver(dev);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1264,6 +1268,7 @@ static int brcmf_usb_probe_cb(struct brcmf_usbdev_info *devinfo)
|
||||||
if (ret)
|
if (ret)
|
||||||
goto fail;
|
goto fail;
|
||||||
/* we are done */
|
/* we are done */
|
||||||
|
mutex_unlock(&devinfo->dev_init_lock);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
bus->chip = bus_pub->devid;
|
bus->chip = bus_pub->devid;
|
||||||
|
@ -1317,6 +1322,12 @@ brcmf_usb_probe(struct usb_interface *intf, const struct usb_device_id *id)
|
||||||
|
|
||||||
devinfo->usbdev = usb;
|
devinfo->usbdev = usb;
|
||||||
devinfo->dev = &usb->dev;
|
devinfo->dev = &usb->dev;
|
||||||
|
/* Take an init lock, to protect for disconnect while still loading.
|
||||||
|
* Necessary because of the asynchronous firmware load construction
|
||||||
|
*/
|
||||||
|
mutex_init(&devinfo->dev_init_lock);
|
||||||
|
mutex_lock(&devinfo->dev_init_lock);
|
||||||
|
|
||||||
usb_set_intfdata(intf, devinfo);
|
usb_set_intfdata(intf, devinfo);
|
||||||
|
|
||||||
/* Check that the device supports only one configuration */
|
/* Check that the device supports only one configuration */
|
||||||
|
@ -1391,6 +1402,7 @@ brcmf_usb_probe(struct usb_interface *intf, const struct usb_device_id *id)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
fail:
|
fail:
|
||||||
|
mutex_unlock(&devinfo->dev_init_lock);
|
||||||
kfree(devinfo);
|
kfree(devinfo);
|
||||||
usb_set_intfdata(intf, NULL);
|
usb_set_intfdata(intf, NULL);
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -1403,8 +1415,19 @@ brcmf_usb_disconnect(struct usb_interface *intf)
|
||||||
|
|
||||||
brcmf_dbg(USB, "Enter\n");
|
brcmf_dbg(USB, "Enter\n");
|
||||||
devinfo = (struct brcmf_usbdev_info *)usb_get_intfdata(intf);
|
devinfo = (struct brcmf_usbdev_info *)usb_get_intfdata(intf);
|
||||||
|
|
||||||
|
if (devinfo) {
|
||||||
|
mutex_lock(&devinfo->dev_init_lock);
|
||||||
|
/* Make sure that devinfo still exists. Firmware probe routines
|
||||||
|
* may have released the device and cleared the intfdata.
|
||||||
|
*/
|
||||||
|
if (!usb_get_intfdata(intf))
|
||||||
|
goto done;
|
||||||
|
|
||||||
brcmf_usb_disconnect_cb(devinfo);
|
brcmf_usb_disconnect_cb(devinfo);
|
||||||
kfree(devinfo);
|
kfree(devinfo);
|
||||||
|
}
|
||||||
|
done:
|
||||||
brcmf_dbg(USB, "Exit\n");
|
brcmf_dbg(USB, "Exit\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue