aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLuca Coelho <luciano.coelho@intel.com>2018-02-13 11:09:40 +0200
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2018-06-26 08:06:33 +0800
commitb8511dbff2639db94baad73ab912bd6f22afea14 (patch)
treeaaa65534760fcfd390b96ada983d567261eca3d7
parent2d58a9ac892a2c08480a113f1fb0f55dbf85e2bb (diff)
iwlwifi: fw: harden page loading code
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>
-rw-r--r--drivers/net/wireless/intel/iwlwifi/fw/paging.c49
1 files changed, 41 insertions, 8 deletions
diff --git a/drivers/net/wireless/intel/iwlwifi/fw/paging.c b/drivers/net/wireless/intel/iwlwifi/fw/paging.c
index 1610722b8099..747eef82cefd 100644
--- a/drivers/net/wireless/intel/iwlwifi/fw/paging.c
+++ b/drivers/net/wireless/intel/iwlwifi/fw/paging.c
@@ -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
@@ -174,7 +176,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;
/*
@@ -201,17 +203,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,
@@ -232,6 +240,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);
@@ -242,19 +258,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 += block->fw_paging_size;
- offset += fwrt->fw_paging_db[idx].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,
@@ -266,6 +295,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,