Commit abeaa850 authored by Qiujun Huang's avatar Qiujun Huang Committed by Kalle Valo

ath9k: Fix use-after-free Read in ath9k_wmi_ctrl_rx

Free wmi later after cmd urb has been killed, as urb cb will access wmi.

the case reported by syzbot:
https://lore.kernel.org/linux-usb/0000000000000002fc05a1d61a68@google.com
BUG: KASAN: use-after-free in ath9k_wmi_ctrl_rx+0x416/0x500
drivers/net/wireless/ath/ath9k/wmi.c:215
Read of size 1 at addr ffff8881cef1417c by task swapper/1/0

Call Trace:
<IRQ>
ath9k_wmi_ctrl_rx+0x416/0x500 drivers/net/wireless/ath/ath9k/wmi.c:215
ath9k_htc_rx_msg+0x2da/0xaf0
drivers/net/wireless/ath/ath9k/htc_hst.c:459
ath9k_hif_usb_reg_in_cb+0x1ba/0x630
drivers/net/wireless/ath/ath9k/hif_usb.c:718
__usb_hcd_giveback_urb+0x29a/0x550 drivers/usb/core/hcd.c:1650
usb_hcd_giveback_urb+0x368/0x420 drivers/usb/core/hcd.c:1716
dummy_timer+0x1258/0x32ae drivers/usb/gadget/udc/dummy_hcd.c:1966
call_timer_fn+0x195/0x6f0 kernel/time/timer.c:1404
expire_timers kernel/time/timer.c:1449 [inline]
__run_timers kernel/time/timer.c:1773 [inline]
__run_timers kernel/time/timer.c:1740 [inline]
run_timer_softirq+0x5f9/0x1500 kernel/time/timer.c:1786

Reported-and-tested-by: syzbot+5d338854440137ea0fef@syzkaller.appspotmail.com
Signed-off-by: default avatarQiujun Huang <hqjagain@gmail.com>
Signed-off-by: default avatarKalle Valo <kvalo@codeaurora.org>
Link: https://lore.kernel.org/r/20200404041838.10426-3-hqjagain@gmail.com
parent ced21a4c
...@@ -973,7 +973,7 @@ static int ath9k_hif_usb_alloc_urbs(struct hif_device_usb *hif_dev) ...@@ -973,7 +973,7 @@ static int ath9k_hif_usb_alloc_urbs(struct hif_device_usb *hif_dev)
return -ENOMEM; return -ENOMEM;
} }
static void ath9k_hif_usb_dealloc_urbs(struct hif_device_usb *hif_dev) void ath9k_hif_usb_dealloc_urbs(struct hif_device_usb *hif_dev)
{ {
usb_kill_anchored_urbs(&hif_dev->regout_submitted); usb_kill_anchored_urbs(&hif_dev->regout_submitted);
ath9k_hif_usb_dealloc_reg_in_urbs(hif_dev); ath9k_hif_usb_dealloc_reg_in_urbs(hif_dev);
...@@ -1341,8 +1341,9 @@ static void ath9k_hif_usb_disconnect(struct usb_interface *interface) ...@@ -1341,8 +1341,9 @@ static void ath9k_hif_usb_disconnect(struct usb_interface *interface)
if (hif_dev->flags & HIF_USB_READY) { if (hif_dev->flags & HIF_USB_READY) {
ath9k_htc_hw_deinit(hif_dev->htc_handle, unplugged); ath9k_htc_hw_deinit(hif_dev->htc_handle, unplugged);
ath9k_htc_hw_free(hif_dev->htc_handle);
ath9k_hif_usb_dev_deinit(hif_dev); ath9k_hif_usb_dev_deinit(hif_dev);
ath9k_destoy_wmi(hif_dev->htc_handle->drv_priv);
ath9k_htc_hw_free(hif_dev->htc_handle);
} }
usb_set_intfdata(interface, NULL); usb_set_intfdata(interface, NULL);
......
...@@ -133,5 +133,6 @@ struct hif_device_usb { ...@@ -133,5 +133,6 @@ struct hif_device_usb {
int ath9k_hif_usb_init(void); int ath9k_hif_usb_init(void);
void ath9k_hif_usb_exit(void); void ath9k_hif_usb_exit(void);
void ath9k_hif_usb_dealloc_urbs(struct hif_device_usb *hif_dev);
#endif /* HTC_USB_H */ #endif /* HTC_USB_H */
...@@ -931,8 +931,9 @@ static int ath9k_init_device(struct ath9k_htc_priv *priv, ...@@ -931,8 +931,9 @@ static int ath9k_init_device(struct ath9k_htc_priv *priv,
int ath9k_htc_probe_device(struct htc_target *htc_handle, struct device *dev, int ath9k_htc_probe_device(struct htc_target *htc_handle, struct device *dev,
u16 devid, char *product, u32 drv_info) u16 devid, char *product, u32 drv_info)
{ {
struct ieee80211_hw *hw; struct hif_device_usb *hif_dev;
struct ath9k_htc_priv *priv; struct ath9k_htc_priv *priv;
struct ieee80211_hw *hw;
int ret; int ret;
hw = ieee80211_alloc_hw(sizeof(struct ath9k_htc_priv), &ath9k_htc_ops); hw = ieee80211_alloc_hw(sizeof(struct ath9k_htc_priv), &ath9k_htc_ops);
...@@ -967,7 +968,10 @@ int ath9k_htc_probe_device(struct htc_target *htc_handle, struct device *dev, ...@@ -967,7 +968,10 @@ int ath9k_htc_probe_device(struct htc_target *htc_handle, struct device *dev,
return 0; return 0;
err_init: err_init:
ath9k_deinit_wmi(priv); ath9k_stop_wmi(priv);
hif_dev = (struct hif_device_usb *)htc_handle->hif_dev;
ath9k_hif_usb_dealloc_urbs(hif_dev);
ath9k_destoy_wmi(priv);
err_free: err_free:
ieee80211_free_hw(hw); ieee80211_free_hw(hw);
return ret; return ret;
...@@ -982,7 +986,7 @@ void ath9k_htc_disconnect_device(struct htc_target *htc_handle, bool hotunplug) ...@@ -982,7 +986,7 @@ void ath9k_htc_disconnect_device(struct htc_target *htc_handle, bool hotunplug)
htc_handle->drv_priv->ah->ah_flags |= AH_UNPLUGGED; htc_handle->drv_priv->ah->ah_flags |= AH_UNPLUGGED;
ath9k_deinit_device(htc_handle->drv_priv); ath9k_deinit_device(htc_handle->drv_priv);
ath9k_deinit_wmi(htc_handle->drv_priv); ath9k_stop_wmi(htc_handle->drv_priv);
ieee80211_free_hw(htc_handle->drv_priv->hw); ieee80211_free_hw(htc_handle->drv_priv->hw);
} }
} }
......
...@@ -112,14 +112,17 @@ struct wmi *ath9k_init_wmi(struct ath9k_htc_priv *priv) ...@@ -112,14 +112,17 @@ struct wmi *ath9k_init_wmi(struct ath9k_htc_priv *priv)
return wmi; return wmi;
} }
void ath9k_deinit_wmi(struct ath9k_htc_priv *priv) void ath9k_stop_wmi(struct ath9k_htc_priv *priv)
{ {
struct wmi *wmi = priv->wmi; struct wmi *wmi = priv->wmi;
mutex_lock(&wmi->op_mutex); mutex_lock(&wmi->op_mutex);
wmi->stopped = true; wmi->stopped = true;
mutex_unlock(&wmi->op_mutex); mutex_unlock(&wmi->op_mutex);
}
void ath9k_destoy_wmi(struct ath9k_htc_priv *priv)
{
kfree(priv->wmi); kfree(priv->wmi);
} }
......
...@@ -179,7 +179,6 @@ struct wmi { ...@@ -179,7 +179,6 @@ struct wmi {
}; };
struct wmi *ath9k_init_wmi(struct ath9k_htc_priv *priv); struct wmi *ath9k_init_wmi(struct ath9k_htc_priv *priv);
void ath9k_deinit_wmi(struct ath9k_htc_priv *priv);
int ath9k_wmi_connect(struct htc_target *htc, struct wmi *wmi, int ath9k_wmi_connect(struct htc_target *htc, struct wmi *wmi,
enum htc_endpoint_id *wmi_ctrl_epid); enum htc_endpoint_id *wmi_ctrl_epid);
int ath9k_wmi_cmd(struct wmi *wmi, enum wmi_cmd_id cmd_id, int ath9k_wmi_cmd(struct wmi *wmi, enum wmi_cmd_id cmd_id,
...@@ -189,6 +188,8 @@ int ath9k_wmi_cmd(struct wmi *wmi, enum wmi_cmd_id cmd_id, ...@@ -189,6 +188,8 @@ int ath9k_wmi_cmd(struct wmi *wmi, enum wmi_cmd_id cmd_id,
void ath9k_wmi_event_tasklet(unsigned long data); void ath9k_wmi_event_tasklet(unsigned long data);
void ath9k_fatal_work(struct work_struct *work); void ath9k_fatal_work(struct work_struct *work);
void ath9k_wmi_event_drain(struct ath9k_htc_priv *priv); void ath9k_wmi_event_drain(struct ath9k_htc_priv *priv);
void ath9k_stop_wmi(struct ath9k_htc_priv *priv);
void ath9k_destoy_wmi(struct ath9k_htc_priv *priv);
#define WMI_CMD(_wmi_cmd) \ #define WMI_CMD(_wmi_cmd) \
do { \ do { \
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment