[PATCH 4.14 45/52] iwlwifi: fw: harden page loading code

From: Greg Kroah-Hartman
Date: Sun Jun 24 2018 - 11:29:45 EST


4.14-stable review patch. If anyone has any objections, please let me know.

------------------

From: Luca Coelho <luciano.coelho@xxxxxxxxx>

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@xxxxxxxxxxxxxxx
Signed-off-by: Luca Coelho <luciano.coelho@xxxxxxxxx>
Signed-off-by: Greg Kroah-Hartman <gregkh@xxxxxxxxxxxxxxxxxxx>

---
drivers/net/wireless/intel/iwlwifi/fw/paging.c | 49 ++++++++++++++++++++-----
1 file changed, 41 insertions(+), 8 deletions(-)

--- 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(struc
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 iw
*/
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 iw
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 iw

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 iw
}

return 0;
+
+err:
+ iwl_free_fw_paging(fwrt);
+ return ret;
}

static int iwl_save_fw_paging(struct iwl_fw_runtime *fwrt,