Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 3eb80292 authored by Qiujun Huang's avatar Qiujun Huang Committed by Greg Kroah-Hartman
Browse files

ath9k: Fix use-after-free Read in ath9k_wmi_ctrl_rx

commit abeaa85054ff8cfe8b99aafc5c70ea067e5d0908 upstream.

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: default avatar <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


Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 7f336346
Loading
Loading
Loading
Loading
+3 −2
Original line number Diff line number Diff line
@@ -973,7 +973,7 @@ static int ath9k_hif_usb_alloc_urbs(struct hif_device_usb *hif_dev)
	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);
	ath9k_hif_usb_dealloc_reg_in_urbs(hif_dev);
@@ -1341,8 +1341,9 @@ static void ath9k_hif_usb_disconnect(struct usb_interface *interface)

	if (hif_dev->flags & HIF_USB_READY) {
		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_destoy_wmi(hif_dev->htc_handle->drv_priv);
		ath9k_htc_hw_free(hif_dev->htc_handle);
	}

	usb_set_intfdata(interface, NULL);
+1 −0
Original line number Diff line number Diff line
@@ -133,5 +133,6 @@ struct hif_device_usb {

int ath9k_hif_usb_init(void);
void ath9k_hif_usb_exit(void);
void ath9k_hif_usb_dealloc_urbs(struct hif_device_usb *hif_dev);

#endif /* HTC_USB_H */
+7 −3
Original line number Diff line number Diff line
@@ -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,
			   u16 devid, char *product, u32 drv_info)
{
	struct ieee80211_hw *hw;
	struct hif_device_usb *hif_dev;
	struct ath9k_htc_priv *priv;
	struct ieee80211_hw *hw;
	int ret;

	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,
	return 0;

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:
	ieee80211_free_hw(hw);
	return ret;
@@ -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;

		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);
	}
}
+4 −1
Original line number Diff line number Diff line
@@ -112,14 +112,17 @@ struct wmi *ath9k_init_wmi(struct ath9k_htc_priv *priv)
	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;

	mutex_lock(&wmi->op_mutex);
	wmi->stopped = true;
	mutex_unlock(&wmi->op_mutex);
}

void ath9k_destoy_wmi(struct ath9k_htc_priv *priv)
{
	kfree(priv->wmi);
}

+2 −1
Original line number Diff line number Diff line
@@ -179,7 +179,6 @@ struct wmi {
};

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,
		      enum htc_endpoint_id *wmi_ctrl_epid);
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_fatal_work(struct work_struct *work);
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)						\
	do {								\