wifi: rtw89: fw: validate multi-firmware header before accessing
authorPing-Ke Shih <pkshih@realtek.com>
Mon, 3 Feb 2025 07:29:10 +0000 (15:29 +0800)
committerPing-Ke Shih <pkshih@realtek.com>
Mon, 10 Feb 2025 03:01:51 +0000 (11:01 +0800)
A firmeware file contains multi-firmware with a header to represent
contents. The mfw_hdr->fw_nr is to define number of firmware in file.

         +-----+-------+------+---------+--------------+
         | sig | fw_nr | rsvd | version | reserved     |
         +---------------------------------------------+ --
 fw 0    | cv | type | mp | rsvd | shift | size | rsvd |   \
         +---------------------------------------------+   |
 fw 1    | cv | type | mp | rsvd | shift | size | rsvd |   | mfw_hdr->fw_nr
         +---------------------------------------------+   |
 fw N-1  |                  ...                        |   /
         +=============================================+ --
         |               fw 0 content                  |
         |       (pointed by fw0 shift/size)           |
         +=============================================+

To avoid Coverity warning, validate header is in range of firmware size,
and also validate the range of actual firmware content is in range.

Addresses-Coverity-ID: 1494046 ("Untrusted loop bound")

Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
Link: https://patch.msgid.link/20250203072911.47313-4-pkshih@realtek.com
drivers/net/wireless/realtek/rtw89/fw.c

index c86a0d3284356b88e8aa8f45a1bf67a6576ce75a..68e80e54ab5fa91a153d9dee84587fb21c3743ae 100644 (file)
@@ -489,6 +489,30 @@ static int rtw89_fw_hdr_parser(struct rtw89_dev *rtwdev,
        }
 }
 
+static int rtw89_mfw_validate_hdr(struct rtw89_dev *rtwdev,
+                                 const struct firmware *firmware,
+                                 const struct rtw89_mfw_hdr *mfw_hdr)
+{
+       const void *mfw = firmware->data;
+       u32 mfw_len = firmware->size;
+       u8 fw_nr = mfw_hdr->fw_nr;
+       const void *ptr;
+
+       if (fw_nr == 0) {
+               rtw89_err(rtwdev, "mfw header has no fw entry\n");
+               return -ENOENT;
+       }
+
+       ptr = &mfw_hdr->info[fw_nr];
+
+       if (ptr > mfw + mfw_len) {
+               rtw89_err(rtwdev, "mfw header out of address\n");
+               return -EFAULT;
+       }
+
+       return 0;
+}
+
 static
 int rtw89_mfw_recognize(struct rtw89_dev *rtwdev, enum rtw89_fw_type type,
                        struct rtw89_fw_suit *fw_suit, bool nowarn)
@@ -499,6 +523,7 @@ int rtw89_mfw_recognize(struct rtw89_dev *rtwdev, enum rtw89_fw_type type,
        u32 mfw_len = firmware->size;
        const struct rtw89_mfw_hdr *mfw_hdr = (const struct rtw89_mfw_hdr *)mfw;
        const struct rtw89_mfw_info *mfw_info = NULL, *tmp;
+       int ret;
        int i;
 
        if (mfw_hdr->sig != RTW89_MFW_SIG) {
@@ -511,6 +536,10 @@ int rtw89_mfw_recognize(struct rtw89_dev *rtwdev, enum rtw89_fw_type type,
                return 0;
        }
 
+       ret = rtw89_mfw_validate_hdr(rtwdev, firmware, mfw_hdr);
+       if (ret)
+               return ret;
+
        for (i = 0; i < mfw_hdr->fw_nr; i++) {
                tmp = &mfw_hdr->info[i];
                if (tmp->type != type)
@@ -540,6 +569,12 @@ int rtw89_mfw_recognize(struct rtw89_dev *rtwdev, enum rtw89_fw_type type,
 found:
        fw_suit->data = mfw + le32_to_cpu(mfw_info->shift);
        fw_suit->size = le32_to_cpu(mfw_info->size);
+
+       if (fw_suit->data + fw_suit->size > mfw + mfw_len) {
+               rtw89_err(rtwdev, "fw_suit %d out of address\n", type);
+               return -EFAULT;
+       }
+
        return 0;
 }