| From 05255779d9ce4e10730759e04c76f3a2189c8c35 Mon Sep 17 00:00:00 2001 |
| From: Yan-Hsuan Chuang <yhchuang@realtek.com> |
| Date: Wed, 2 Oct 2019 14:35:25 +0800 |
| Subject: [PATCH] rtw88: fix beaconing mode rsvd_page memory violation issue |
| |
| commit c3594559f49c601d410dee4b767c3536a5535bfd upstream. |
| |
| When downloading the reserved page, the first page always contains |
| a beacon for the firmware to reference. For non-beaconing modes such |
| as station mode, also put a blank skb with length=1. |
| |
| And for the beaconing modes, driver will get a real beacon with a |
| length approximate to the page size. But as the beacon is always put |
| at the first page, it does not need a tx_desc, because the TX path |
| will generate one when TXing the reserved page to the hardware. So we |
| could allocate a buffer with a size smaller than the reserved page, |
| when using memcpy() to copy the content of reserved page to the buffer, |
| the over-sized reserved page will violate the kernel memory. |
| |
| To fix it, add the tx_desc before memcpy() the reserved packets to |
| the buffer, then we can get SKBs with correct length when counting |
| the pages in total. And for page 0, count the extra tx_desc_sz that |
| the TX path will generate. This way, the first beacon that allocated |
| without tx_desc can be counted with the extra tx_desc_sz to get |
| actual pages it requires. |
| |
| Fixes: e3037485c68e ("rtw88: new Realtek 802.11ac driver") |
| Signed-off-by: Yan-Hsuan Chuang <yhchuang@realtek.com> |
| Signed-off-by: Kalle Valo <kvalo@codeaurora.org> |
| Signed-off-by: Paul Gortmaker <paul.gortmaker@windriver.com> |
| |
| diff --git a/drivers/net/wireless/realtek/rtw88/fw.c b/drivers/net/wireless/realtek/rtw88/fw.c |
| index 628477971213..2dd5c091b02c 100644 |
| --- a/drivers/net/wireless/realtek/rtw88/fw.c |
| +++ b/drivers/net/wireless/realtek/rtw88/fw.c |
| @@ -367,9 +367,6 @@ static void rtw_rsvd_page_list_to_buf(struct rtw_dev *rtwdev, u8 page_size, |
| { |
| struct sk_buff *skb = rsvd_pkt->skb; |
| |
| - if (rsvd_pkt->add_txdesc) |
| - rtw_fill_rsvd_page_desc(rtwdev, skb); |
| - |
| if (page >= 1) |
| memcpy(buf + page_margin + page_size * (page - 1), |
| skb->data, skb->len); |
| @@ -494,16 +491,37 @@ static u8 *rtw_build_rsvd_page(struct rtw_dev *rtwdev, |
| list_for_each_entry(rsvd_pkt, &rtwdev->rsvd_page_list, list) { |
| iter = rtw_get_rsvd_page_skb(hw, vif, rsvd_pkt->type); |
| if (!iter) { |
| - rtw_err(rtwdev, "fail to build rsvd packet\n"); |
| + rtw_err(rtwdev, "failed to build rsvd packet\n"); |
| goto release_skb; |
| } |
| + |
| + /* Fill the tx_desc for the rsvd pkt that requires one. |
| + * And iter->len will be added with size of tx_desc_sz. |
| + */ |
| + if (rsvd_pkt->add_txdesc) |
| + rtw_fill_rsvd_page_desc(rtwdev, iter); |
| + |
| rsvd_pkt->skb = iter; |
| rsvd_pkt->page = total_page; |
| - if (rsvd_pkt->add_txdesc) |
| + |
| + /* Reserved page is downloaded via TX path, and TX path will |
| + * generate a tx_desc at the header to describe length of |
| + * the buffer. If we are not counting page numbers with the |
| + * size of tx_desc added at the first rsvd_pkt (usually a |
| + * beacon, firmware default refer to the first page as the |
| + * content of beacon), we could generate a buffer which size |
| + * is smaller than the actual size of the whole rsvd_page |
| + */ |
| + if (total_page == 0) { |
| + if (rsvd_pkt->type != RSVD_BEACON) { |
| + rtw_err(rtwdev, "first page should be a beacon\n"); |
| + goto release_skb; |
| + } |
| total_page += rtw_len_to_page(iter->len + tx_desc_sz, |
| page_size); |
| - else |
| + } else { |
| total_page += rtw_len_to_page(iter->len, page_size); |
| + } |
| } |
| |
| if (total_page > rtwdev->fifo.rsvd_drv_pg_num) { |
| @@ -516,13 +534,24 @@ static u8 *rtw_build_rsvd_page(struct rtw_dev *rtwdev, |
| if (!buf) |
| goto release_skb; |
| |
| + /* Copy the content of each rsvd_pkt to the buf, and they should |
| + * be aligned to the pages. |
| + * |
| + * Note that the first rsvd_pkt is a beacon no matter what vif->type. |
| + * And that rsvd_pkt does not require tx_desc because when it goes |
| + * through TX path, the TX path will generate one for it. |
| + */ |
| list_for_each_entry(rsvd_pkt, &rtwdev->rsvd_page_list, list) { |
| rtw_rsvd_page_list_to_buf(rtwdev, page_size, page_margin, |
| page, buf, rsvd_pkt); |
| - page += rtw_len_to_page(rsvd_pkt->skb->len, page_size); |
| - } |
| - list_for_each_entry(rsvd_pkt, &rtwdev->rsvd_page_list, list) |
| + if (page == 0) |
| + page += rtw_len_to_page(rsvd_pkt->skb->len + |
| + tx_desc_sz, page_size); |
| + else |
| + page += rtw_len_to_page(rsvd_pkt->skb->len, page_size); |
| + |
| kfree_skb(rsvd_pkt->skb); |
| + } |
| |
| return buf; |
| |
| @@ -575,6 +604,11 @@ int rtw_fw_download_rsvd_page(struct rtw_dev *rtwdev, struct ieee80211_vif *vif) |
| goto free; |
| } |
| |
| + /* The last thing is to download the *ONLY* beacon again, because |
| + * the previous tx_desc is to describe the total rsvd page. Download |
| + * the beacon again to replace the TX desc header, and we will get |
| + * a correct tx_desc for the beacon in the rsvd page. |
| + */ |
| ret = rtw_download_beacon(rtwdev, vif); |
| if (ret) { |
| rtw_err(rtwdev, "failed to download beacon\n"); |
| -- |
| 2.7.4 |
| |