wifi: ath9k: fix races between ath9k_wmi_cmd and ath9k_wmi_ctrl_rx
[ Upstream commit b674fb513e2e7a514fcde287c0f73915d393fdb6 ]
Currently, the synchronization between ath9k_wmi_cmd() and
ath9k_wmi_ctrl_rx() is exposed to a race condition which, although being
rather unlikely, can lead to invalid behaviour of ath9k_wmi_cmd().
Consider the following scenario:
CPU0 CPU1
ath9k_wmi_cmd(...)
mutex_lock(&wmi->op_mutex)
ath9k_wmi_cmd_issue(...)
wait_for_completion_timeout(...)
---
timeout
---
/* the callback is being processed
* before last_seq_id became zero
*/
ath9k_wmi_ctrl_rx(...)
spin_lock_irqsave(...)
/* wmi->last_seq_id check here
* doesn't detect timeout yet
*/
spin_unlock_irqrestore(...)
/* last_seq_id is zeroed to
* indicate there was a timeout
*/
wmi->last_seq_id = 0
mutex_unlock(&wmi->op_mutex)
return -ETIMEDOUT
ath9k_wmi_cmd(...)
mutex_lock(&wmi->op_mutex)
/* the buffer is replaced with
* another one
*/
wmi->cmd_rsp_buf = rsp_buf
wmi->cmd_rsp_len = rsp_len
ath9k_wmi_cmd_issue(...)
spin_lock_irqsave(...)
spin_unlock_irqrestore(...)
wait_for_completion_timeout(...)
/* the continuation of the
* callback left after the first
* ath9k_wmi_cmd call
*/
ath9k_wmi_rsp_callback(...)
/* copying data designated
* to already timeouted
* WMI command into an
* inappropriate wmi_cmd_buf
*/
memcpy(...)
complete(&wmi->cmd_wait)
/* awakened by the bogus callback
* => invalid return result
*/
mutex_unlock(&wmi->op_mutex)
return 0
To fix this, update last_seq_id on timeout path inside ath9k_wmi_cmd()
under the wmi_lock. Move ath9k_wmi_rsp_callback() under wmi_lock inside
ath9k_wmi_ctrl_rx() so that the wmi->cmd_wait can be completed only for
initially designated wmi_cmd call, otherwise the path would be rejected
with last_seq_id check.
Found by Linux Verification Center (linuxtesting.org) with Syzkaller.
Fixes: fb9987d0f7
("ath9k_htc: Support for AR9271 chipset.")
Signed-off-by: Fedor Pchelkin <pchelkin@ispras.ru>
Acked-by: Toke Høiland-Jørgensen <toke@toke.dk>
Signed-off-by: Kalle Valo <quic_kvalo@quicinc.com>
Link: https://lore.kernel.org/r/20230425192607.18015-1-pchelkin@ispras.ru
Signed-off-by: Sasha Levin <sashal@kernel.org>
This commit is contained in:

committed by
Greg Kroah-Hartman

parent
91d92d8975
commit
ee72b2b1f5
@@ -242,10 +242,10 @@ static void ath9k_wmi_ctrl_rx(void *priv, struct sk_buff *skb,
|
|||||||
spin_unlock_irqrestore(&wmi->wmi_lock, flags);
|
spin_unlock_irqrestore(&wmi->wmi_lock, flags);
|
||||||
goto free_skb;
|
goto free_skb;
|
||||||
}
|
}
|
||||||
spin_unlock_irqrestore(&wmi->wmi_lock, flags);
|
|
||||||
|
|
||||||
/* WMI command response */
|
/* WMI command response */
|
||||||
ath9k_wmi_rsp_callback(wmi, skb);
|
ath9k_wmi_rsp_callback(wmi, skb);
|
||||||
|
spin_unlock_irqrestore(&wmi->wmi_lock, flags);
|
||||||
|
|
||||||
free_skb:
|
free_skb:
|
||||||
kfree_skb(skb);
|
kfree_skb(skb);
|
||||||
@@ -308,8 +308,8 @@ int ath9k_wmi_cmd(struct wmi *wmi, enum wmi_cmd_id cmd_id,
|
|||||||
struct ath_common *common = ath9k_hw_common(ah);
|
struct ath_common *common = ath9k_hw_common(ah);
|
||||||
u16 headroom = sizeof(struct htc_frame_hdr) +
|
u16 headroom = sizeof(struct htc_frame_hdr) +
|
||||||
sizeof(struct wmi_cmd_hdr);
|
sizeof(struct wmi_cmd_hdr);
|
||||||
|
unsigned long time_left, flags;
|
||||||
struct sk_buff *skb;
|
struct sk_buff *skb;
|
||||||
unsigned long time_left;
|
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
|
||||||
if (ah->ah_flags & AH_UNPLUGGED)
|
if (ah->ah_flags & AH_UNPLUGGED)
|
||||||
@@ -345,7 +345,9 @@ int ath9k_wmi_cmd(struct wmi *wmi, enum wmi_cmd_id cmd_id,
|
|||||||
if (!time_left) {
|
if (!time_left) {
|
||||||
ath_dbg(common, WMI, "Timeout waiting for WMI command: %s\n",
|
ath_dbg(common, WMI, "Timeout waiting for WMI command: %s\n",
|
||||||
wmi_cmd_to_name(cmd_id));
|
wmi_cmd_to_name(cmd_id));
|
||||||
|
spin_lock_irqsave(&wmi->wmi_lock, flags);
|
||||||
wmi->last_seq_id = 0;
|
wmi->last_seq_id = 0;
|
||||||
|
spin_unlock_irqrestore(&wmi->wmi_lock, flags);
|
||||||
mutex_unlock(&wmi->op_mutex);
|
mutex_unlock(&wmi->op_mutex);
|
||||||
return -ETIMEDOUT;
|
return -ETIMEDOUT;
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user