]> git.proxmox.com Git - mirror_ubuntu-bionic-kernel.git/commitdiff
iwlwifi: fw: harden page loading code
authorLuca Coelho <luciano.coelho@intel.com>
Tue, 13 Feb 2018 09:09:40 +0000 (11:09 +0200)
committerThadeu Lima de Souza Cascardo <cascardo@canonical.com>
Fri, 9 Nov 2018 19:00:27 +0000 (17:00 -0200)
BugLink: http://bugs.launchpad.net/bugs/1800537
commit 9039d985811d5b109b58b202b7594fd24e433fed upstream.

The page loading code trusts the data provided in the firmware images
a bit too much and may cause a buffer overflow or copy unknown data if
the block sizes don't match what we expect.

To prevent potential problems, harden the code by checking if the
sizes we are copying are what we expect.

Cc: stable@vger.kernel.org
Signed-off-by: Luca Coelho <luciano.coelho@intel.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Signed-off-by: Kamal Mostafa <kamal@canonical.com>
Signed-off-by: Khalid Elmously <khalid.elmously@canonical.com>
drivers/net/wireless/intel/iwlwifi/fw/paging.c

index 1fec8e3a6b35cea23eeccf7f1c1c9b9b1f4bcdba..6afcfd1f0eec4f7bf1169a7b3aa426334df1c191 100644 (file)
@@ -8,6 +8,7 @@
  * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
  * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
  * Copyright(c) 2016 - 2017 Intel Deutschland GmbH
+ * Copyright(c) 2018        Intel Corporation
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
@@ -30,6 +31,7 @@
  * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
  * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
  * Copyright(c) 2016 - 2017 Intel Deutschland GmbH
+ * Copyright(c) 2018        Intel Corporation
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -163,7 +165,7 @@ static int iwl_alloc_fw_paging_mem(struct iwl_fw_runtime *fwrt,
 static int iwl_fill_paging_mem(struct iwl_fw_runtime *fwrt,
                               const struct fw_img *image)
 {
-       int sec_idx, idx;
+       int sec_idx, idx, ret;
        u32 offset = 0;
 
        /*
@@ -190,17 +192,23 @@ static int iwl_fill_paging_mem(struct iwl_fw_runtime *fwrt,
         */
        if (sec_idx >= image->num_sec - 1) {
                IWL_ERR(fwrt, "Paging: Missing CSS and/or paging sections\n");
-               iwl_free_fw_paging(fwrt);
-               return -EINVAL;
+               ret = -EINVAL;
+               goto err;
        }
 
        /* copy the CSS block to the dram */
        IWL_DEBUG_FW(fwrt, "Paging: load paging CSS to FW, sec = %d\n",
                     sec_idx);
 
+       if (image->sec[sec_idx].len > fwrt->fw_paging_db[0].fw_paging_size) {
+               IWL_ERR(fwrt, "CSS block is larger than paging size\n");
+               ret = -EINVAL;
+               goto err;
+       }
+
        memcpy(page_address(fwrt->fw_paging_db[0].fw_paging_block),
               image->sec[sec_idx].data,
-              fwrt->fw_paging_db[0].fw_paging_size);
+              image->sec[sec_idx].len);
        dma_sync_single_for_device(fwrt->trans->dev,
                                   fwrt->fw_paging_db[0].fw_paging_phys,
                                   fwrt->fw_paging_db[0].fw_paging_size,
@@ -221,6 +229,14 @@ static int iwl_fill_paging_mem(struct iwl_fw_runtime *fwrt,
        for (idx = 1; idx < fwrt->num_of_paging_blk; idx++) {
                struct iwl_fw_paging *block = &fwrt->fw_paging_db[idx];
 
+               if (block->fw_paging_size > image->sec[sec_idx].len - offset) {
+                       IWL_ERR(fwrt,
+                               "Paging: paging size is larger than remaining data in block %d\n",
+                               idx);
+                       ret = -EINVAL;
+                       goto err;
+               }
+
                memcpy(page_address(block->fw_paging_block),
                       image->sec[sec_idx].data + offset,
                       block->fw_paging_size);
@@ -231,19 +247,32 @@ static int iwl_fill_paging_mem(struct iwl_fw_runtime *fwrt,
 
                IWL_DEBUG_FW(fwrt,
                             "Paging: copied %d paging bytes to block %d\n",
-                            fwrt->fw_paging_db[idx].fw_paging_size,
-                            idx);
+                            block->fw_paging_size, idx);
 
-               offset += fwrt->fw_paging_db[idx].fw_paging_size;
+               offset += block->fw_paging_size;
+
+               if (offset > image->sec[sec_idx].len) {
+                       IWL_ERR(fwrt,
+                               "Paging: offset goes over section size\n");
+                       ret = -EINVAL;
+                       goto err;
+               }
        }
 
        /* copy the last paging block */
        if (fwrt->num_of_pages_in_last_blk > 0) {
                struct iwl_fw_paging *block = &fwrt->fw_paging_db[idx];
 
+               if (image->sec[sec_idx].len - offset > block->fw_paging_size) {
+                       IWL_ERR(fwrt,
+                               "Paging: last block is larger than paging size\n");
+                       ret = -EINVAL;
+                       goto err;
+               }
+
                memcpy(page_address(block->fw_paging_block),
                       image->sec[sec_idx].data + offset,
-                      FW_PAGING_SIZE * fwrt->num_of_pages_in_last_blk);
+                      image->sec[sec_idx].len - offset);
                dma_sync_single_for_device(fwrt->trans->dev,
                                           block->fw_paging_phys,
                                           block->fw_paging_size,
@@ -255,6 +284,10 @@ static int iwl_fill_paging_mem(struct iwl_fw_runtime *fwrt,
        }
 
        return 0;
+
+err:
+       iwl_free_fw_paging(fwrt);
+       return ret;
 }
 
 static int iwl_save_fw_paging(struct iwl_fw_runtime *fwrt,