Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

rtw88: fix beaconing mode rsvd_page memory violation issue

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>

authored by

Yan-Hsuan Chuang and committed by
Kalle Valo
c3594559 1131ad7f

+43 -9
+43 -9
drivers/net/wireless/realtek/rtw88/fw.c
··· 672 672 { 673 673 struct sk_buff *skb = rsvd_pkt->skb; 674 674 675 - if (rsvd_pkt->add_txdesc) 676 - rtw_fill_rsvd_page_desc(rtwdev, skb); 677 - 678 675 if (page >= 1) 679 676 memcpy(buf + page_margin + page_size * (page - 1), 680 677 skb->data, skb->len); ··· 796 799 list_for_each_entry(rsvd_pkt, &rtwdev->rsvd_page_list, list) { 797 800 iter = rtw_get_rsvd_page_skb(hw, vif, rsvd_pkt->type); 798 801 if (!iter) { 799 - rtw_err(rtwdev, "fail to build rsvd packet\n"); 802 + rtw_err(rtwdev, "failed to build rsvd packet\n"); 800 803 goto release_skb; 801 804 } 805 + 806 + /* Fill the tx_desc for the rsvd pkt that requires one. 807 + * And iter->len will be added with size of tx_desc_sz. 808 + */ 809 + if (rsvd_pkt->add_txdesc) 810 + rtw_fill_rsvd_page_desc(rtwdev, iter); 811 + 802 812 rsvd_pkt->skb = iter; 803 813 rsvd_pkt->page = total_page; 804 - if (rsvd_pkt->add_txdesc) 814 + 815 + /* Reserved page is downloaded via TX path, and TX path will 816 + * generate a tx_desc at the header to describe length of 817 + * the buffer. If we are not counting page numbers with the 818 + * size of tx_desc added at the first rsvd_pkt (usually a 819 + * beacon, firmware default refer to the first page as the 820 + * content of beacon), we could generate a buffer which size 821 + * is smaller than the actual size of the whole rsvd_page 822 + */ 823 + if (total_page == 0) { 824 + if (rsvd_pkt->type != RSVD_BEACON) { 825 + rtw_err(rtwdev, "first page should be a beacon\n"); 826 + goto release_skb; 827 + } 805 828 total_page += rtw_len_to_page(iter->len + tx_desc_sz, 806 829 page_size); 807 - else 830 + } else { 808 831 total_page += rtw_len_to_page(iter->len, page_size); 832 + } 809 833 } 810 834 811 835 if (total_page > rtwdev->fifo.rsvd_drv_pg_num) { ··· 839 821 if (!buf) 840 822 goto release_skb; 841 823 824 + /* Copy the content of each rsvd_pkt to the buf, and they should 825 + * be aligned to the pages. 826 + * 827 + * Note that the first rsvd_pkt is a beacon no matter what vif->type. 828 + * And that rsvd_pkt does not require tx_desc because when it goes 829 + * through TX path, the TX path will generate one for it. 830 + */ 842 831 list_for_each_entry(rsvd_pkt, &rtwdev->rsvd_page_list, list) { 843 832 rtw_rsvd_page_list_to_buf(rtwdev, page_size, page_margin, 844 833 page, buf, rsvd_pkt); 845 - page += rtw_len_to_page(rsvd_pkt->skb->len, page_size); 846 - } 847 - list_for_each_entry(rsvd_pkt, &rtwdev->rsvd_page_list, list) 834 + if (page == 0) 835 + page += rtw_len_to_page(rsvd_pkt->skb->len + 836 + tx_desc_sz, page_size); 837 + else 838 + page += rtw_len_to_page(rsvd_pkt->skb->len, page_size); 839 + 848 840 kfree_skb(rsvd_pkt->skb); 841 + } 849 842 850 843 return buf; 851 844 ··· 909 880 goto free; 910 881 } 911 882 883 + /* The last thing is to download the *ONLY* beacon again, because 884 + * the previous tx_desc is to describe the total rsvd page. Download 885 + * the beacon again to replace the TX desc header, and we will get 886 + * a correct tx_desc for the beacon in the rsvd page. 887 + */ 912 888 ret = rtw_download_beacon(rtwdev, vif); 913 889 if (ret) { 914 890 rtw_err(rtwdev, "failed to download beacon\n");