Minsuk Kang <linuxlovemin@xxxxxxxxxxxx> writes: > Fix a stack-out-of-bounds write that occurs in a WMI response callback > function that is called after a timeout occurs in ath9k_wmi_cmd(). > The callback writes to wmi->cmd_rsp_buf, a stack-allocated buffer that > could no longer be valid when a timeout occurs. Checking seq_no is > insufficient as the bug can occur between the timeout and the next WMI > command. Add wmi->timedout to check whether a timeout occurred. > > Found by a modified version of syzkaller. > > BUG: KASAN: stack-out-of-bounds in ath9k_wmi_ctrl_rx > Write of size 4 > Call Trace: > memcpy > ath9k_wmi_ctrl_rx > ath9k_htc_rx_msg > ath9k_hif_usb_reg_in_cb > __usb_hcd_giveback_urb > usb_hcd_giveback_urb > dummy_timer > call_timer_fn > run_timer_softirq > __do_softirq > irq_exit_rcu > sysvec_apic_timer_interrupt > > Signed-off-by: Minsuk Kang <linuxlovemin@xxxxxxxxxxxx> > --- > drivers/net/wireless/ath/ath9k/wmi.c | 5 ++++- > drivers/net/wireless/ath/ath9k/wmi.h | 1 + > 2 files changed, 5 insertions(+), 1 deletion(-) > > diff --git a/drivers/net/wireless/ath/ath9k/wmi.c b/drivers/net/wireless/ath/ath9k/wmi.c > index f315c54bd3ac..f46cbecc12e3 100644 > --- a/drivers/net/wireless/ath/ath9k/wmi.c > +++ b/drivers/net/wireless/ath/ath9k/wmi.c > @@ -234,7 +234,8 @@ static void ath9k_wmi_ctrl_rx(void *priv, struct sk_buff *skb, > > /* Check if there has been a timeout. */ > spin_lock_irqsave(&wmi->wmi_lock, flags); > - if (be16_to_cpu(hdr->seq_no) != wmi->last_seq_id) { > + if (be16_to_cpu(hdr->seq_no) != wmi->last_seq_id || > + wmi->timedout) { > spin_unlock_irqrestore(&wmi->wmi_lock, flags); > goto free_skb; > } > @@ -290,6 +291,7 @@ static int ath9k_wmi_cmd_issue(struct wmi *wmi, > > spin_lock_irqsave(&wmi->wmi_lock, flags); > wmi->last_seq_id = wmi->tx_seq_id; > + wmi->timedout = false; > spin_unlock_irqrestore(&wmi->wmi_lock, flags); > > return htc_send_epid(wmi->htc, skb, wmi->ctrl_epid); > @@ -341,6 +343,7 @@ int ath9k_wmi_cmd(struct wmi *wmi, enum wmi_cmd_id cmd_id, > if (!time_left) { > ath_dbg(common, WMI, "Timeout waiting for WMI command: %s\n", > wmi_cmd_to_name(cmd_id)); > + wmi->timedout = true; Instead of introducing a new 'timedout' field, why not just reset last_seq_id to 0 here? That way the existing check should trigger the abort in ath9k_wmi_ctrl_rx()... -Toke