Commit 705d2fde authored by Lior David's avatar Lior David Committed by Kalle Valo

wil6210: add block size checks during FW load

When loading FW from file add block size checks to ensure a
corrupted FW file will not cause the driver to write outside
the device memory.
Signed-off-by: default avatarLior David <qca_liord@qca.qualcomm.com>
Signed-off-by: default avatarMaya Erez <qca_merez@qca.qualcomm.com>
Signed-off-by: default avatarKalle Valo <kvalo@qca.qualcomm.com>
parent 26a6d527
...@@ -26,14 +26,17 @@ ...@@ -26,14 +26,17 @@
prefix_type, rowsize, \ prefix_type, rowsize, \
groupsize, buf, len, ascii) groupsize, buf, len, ascii)
#define FW_ADDR_CHECK(ioaddr, val, msg) do { \ static bool wil_fw_addr_check(struct wil6210_priv *wil,
ioaddr = wmi_buffer(wil, val); \ void __iomem **ioaddr, __le32 val,
if (!ioaddr) { \ u32 size, const char *msg)
wil_err_fw(wil, "bad " msg ": 0x%08x\n", \ {
le32_to_cpu(val)); \ *ioaddr = wmi_buffer_block(wil, val, size);
return -EINVAL; \ if (!(*ioaddr)) {
} \ wil_err_fw(wil, "bad %s: 0x%08x\n", msg, le32_to_cpu(val));
} while (0) return false;
}
return true;
}
/** /**
* wil_fw_verify - verify firmware file validity * wil_fw_verify - verify firmware file validity
...@@ -160,7 +163,8 @@ static int fw_handle_data(struct wil6210_priv *wil, const void *data, ...@@ -160,7 +163,8 @@ static int fw_handle_data(struct wil6210_priv *wil, const void *data,
return -EINVAL; return -EINVAL;
} }
FW_ADDR_CHECK(dst, d->addr, "address"); if (!wil_fw_addr_check(wil, &dst, d->addr, s, "address"))
return -EINVAL;
wil_dbg_fw(wil, "write [0x%08x] <== %zu bytes\n", le32_to_cpu(d->addr), wil_dbg_fw(wil, "write [0x%08x] <== %zu bytes\n", le32_to_cpu(d->addr),
s); s);
wil_memcpy_toio_32(dst, d->data, s); wil_memcpy_toio_32(dst, d->data, s);
...@@ -192,7 +196,8 @@ static int fw_handle_fill(struct wil6210_priv *wil, const void *data, ...@@ -192,7 +196,8 @@ static int fw_handle_fill(struct wil6210_priv *wil, const void *data,
return -EINVAL; return -EINVAL;
} }
FW_ADDR_CHECK(dst, d->addr, "address"); if (!wil_fw_addr_check(wil, &dst, d->addr, s, "address"))
return -EINVAL;
v = le32_to_cpu(d->value); v = le32_to_cpu(d->value);
wil_dbg_fw(wil, "fill [0x%08x] <== 0x%08x, %zu bytes\n", wil_dbg_fw(wil, "fill [0x%08x] <== 0x%08x, %zu bytes\n",
...@@ -248,7 +253,8 @@ static int fw_handle_direct_write(struct wil6210_priv *wil, const void *data, ...@@ -248,7 +253,8 @@ static int fw_handle_direct_write(struct wil6210_priv *wil, const void *data,
u32 v = le32_to_cpu(block[i].value); u32 v = le32_to_cpu(block[i].value);
u32 x, y; u32 x, y;
FW_ADDR_CHECK(dst, block[i].addr, "address"); if (!wil_fw_addr_check(wil, &dst, block[i].addr, 0, "address"))
return -EINVAL;
x = readl(dst); x = readl(dst);
y = (x & m) | (v & ~m); y = (x & m) | (v & ~m);
...@@ -314,10 +320,15 @@ static int fw_handle_gateway_data(struct wil6210_priv *wil, const void *data, ...@@ -314,10 +320,15 @@ static int fw_handle_gateway_data(struct wil6210_priv *wil, const void *data,
wil_dbg_fw(wil, "gw write record [%3d] blocks, cmd 0x%08x\n", wil_dbg_fw(wil, "gw write record [%3d] blocks, cmd 0x%08x\n",
n, gw_cmd); n, gw_cmd);
FW_ADDR_CHECK(gwa_addr, d->gateway_addr_addr, "gateway_addr_addr"); if (!wil_fw_addr_check(wil, &gwa_addr, d->gateway_addr_addr, 0,
FW_ADDR_CHECK(gwa_val, d->gateway_value_addr, "gateway_value_addr"); "gateway_addr_addr") ||
FW_ADDR_CHECK(gwa_cmd, d->gateway_cmd_addr, "gateway_cmd_addr"); !wil_fw_addr_check(wil, &gwa_val, d->gateway_value_addr, 0,
FW_ADDR_CHECK(gwa_ctl, d->gateway_ctrl_address, "gateway_ctrl_address"); "gateway_value_addr") ||
!wil_fw_addr_check(wil, &gwa_cmd, d->gateway_cmd_addr, 0,
"gateway_cmd_addr") ||
!wil_fw_addr_check(wil, &gwa_ctl, d->gateway_ctrl_address, 0,
"gateway_ctrl_address"))
return -EINVAL;
wil_dbg_fw(wil, "gw addresses: addr 0x%08x val 0x%08x" wil_dbg_fw(wil, "gw addresses: addr 0x%08x val 0x%08x"
" cmd 0x%08x ctl 0x%08x\n", " cmd 0x%08x ctl 0x%08x\n",
...@@ -373,12 +384,19 @@ static int fw_handle_gateway_data4(struct wil6210_priv *wil, const void *data, ...@@ -373,12 +384,19 @@ static int fw_handle_gateway_data4(struct wil6210_priv *wil, const void *data,
wil_dbg_fw(wil, "gw4 write record [%3d] blocks, cmd 0x%08x\n", wil_dbg_fw(wil, "gw4 write record [%3d] blocks, cmd 0x%08x\n",
n, gw_cmd); n, gw_cmd);
FW_ADDR_CHECK(gwa_addr, d->gateway_addr_addr, "gateway_addr_addr"); if (!wil_fw_addr_check(wil, &gwa_addr, d->gateway_addr_addr, 0,
"gateway_addr_addr"))
return -EINVAL;
for (k = 0; k < ARRAY_SIZE(block->value); k++) for (k = 0; k < ARRAY_SIZE(block->value); k++)
FW_ADDR_CHECK(gwa_val[k], d->gateway_value_addr[k], if (!wil_fw_addr_check(wil, &gwa_val[k],
"gateway_value_addr"); d->gateway_value_addr[k],
FW_ADDR_CHECK(gwa_cmd, d->gateway_cmd_addr, "gateway_cmd_addr"); 0, "gateway_value_addr"))
FW_ADDR_CHECK(gwa_ctl, d->gateway_ctrl_address, "gateway_ctrl_address"); return -EINVAL;
if (!wil_fw_addr_check(wil, &gwa_cmd, d->gateway_cmd_addr, 0,
"gateway_cmd_addr") ||
!wil_fw_addr_check(wil, &gwa_ctl, d->gateway_ctrl_address, 0,
"gateway_ctrl_address"))
return -EINVAL;
wil_dbg_fw(wil, "gw4 addresses: addr 0x%08x cmd 0x%08x ctl 0x%08x\n", wil_dbg_fw(wil, "gw4 addresses: addr 0x%08x cmd 0x%08x ctl 0x%08x\n",
le32_to_cpu(d->gateway_addr_addr), le32_to_cpu(d->gateway_addr_addr),
......
...@@ -875,6 +875,7 @@ void wil_mbox_ring_le2cpus(struct wil6210_mbox_ring *r); ...@@ -875,6 +875,7 @@ void wil_mbox_ring_le2cpus(struct wil6210_mbox_ring *r);
int wil_find_cid(struct wil6210_priv *wil, const u8 *mac); int wil_find_cid(struct wil6210_priv *wil, const u8 *mac);
void wil_set_ethtoolops(struct net_device *ndev); void wil_set_ethtoolops(struct net_device *ndev);
void __iomem *wmi_buffer_block(struct wil6210_priv *wil, __le32 ptr, u32 size);
void __iomem *wmi_buffer(struct wil6210_priv *wil, __le32 ptr); void __iomem *wmi_buffer(struct wil6210_priv *wil, __le32 ptr);
void __iomem *wmi_addr(struct wil6210_priv *wil, u32 ptr); void __iomem *wmi_addr(struct wil6210_priv *wil, u32 ptr);
int wmi_read_hdr(struct wil6210_priv *wil, __le32 ptr, int wmi_read_hdr(struct wil6210_priv *wil, __le32 ptr,
......
...@@ -140,13 +140,15 @@ static u32 wmi_addr_remap(u32 x) ...@@ -140,13 +140,15 @@ static u32 wmi_addr_remap(u32 x)
/** /**
* Check address validity for WMI buffer; remap if needed * Check address validity for WMI buffer; remap if needed
* @ptr - internal (linker) fw/ucode address * @ptr - internal (linker) fw/ucode address
* @size - if non zero, validate the block does not
* exceed the device memory (bar)
* *
* Valid buffer should be DWORD aligned * Valid buffer should be DWORD aligned
* *
* return address for accessing buffer from the host; * return address for accessing buffer from the host;
* if buffer is not valid, return NULL. * if buffer is not valid, return NULL.
*/ */
void __iomem *wmi_buffer(struct wil6210_priv *wil, __le32 ptr_) void __iomem *wmi_buffer_block(struct wil6210_priv *wil, __le32 ptr_, u32 size)
{ {
u32 off; u32 off;
u32 ptr = le32_to_cpu(ptr_); u32 ptr = le32_to_cpu(ptr_);
...@@ -161,10 +163,17 @@ void __iomem *wmi_buffer(struct wil6210_priv *wil, __le32 ptr_) ...@@ -161,10 +163,17 @@ void __iomem *wmi_buffer(struct wil6210_priv *wil, __le32 ptr_)
off = HOSTADDR(ptr); off = HOSTADDR(ptr);
if (off > wil->bar_size - 4) if (off > wil->bar_size - 4)
return NULL; return NULL;
if (size && ((off + size > wil->bar_size) || (off + size < off)))
return NULL;
return wil->csr + off; return wil->csr + off;
} }
void __iomem *wmi_buffer(struct wil6210_priv *wil, __le32 ptr_)
{
return wmi_buffer_block(wil, ptr_, 0);
}
/** /**
* Check address validity * Check address validity
*/ */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment