Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 2d4590be authored by Lior David's avatar Lior David Committed by Hamad Kadmany
Browse files

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.

Change-Id: I6d7342cd33b2c47b701bcca4ee3cd84febbc56a2
Signed-off-by: default avatarLior David <liord@codeaurora.org>
Signed-off-by: default avatarHamad Kadmany <hkadmany@codeaurora.org>
parent a116b23f
Loading
Loading
Loading
Loading
+38 −20
Original line number Diff line number Diff line
@@ -26,14 +26,17 @@
					     prefix_type, rowsize,	\
					     groupsize, buf, len, ascii)

#define FW_ADDR_CHECK(ioaddr, val, msg) do { \
		ioaddr = wmi_buffer(wil, val); \
		if (!ioaddr) { \
			wil_err_fw(wil, "bad " msg ": 0x%08x\n", \
				   le32_to_cpu(val)); \
			return -EINVAL; \
		} \
	} while (0)
static bool wil_fw_addr_check(struct wil6210_priv *wil,
			      void __iomem **ioaddr, __le32 val,
			      u32 size, const char *msg)
{
	*ioaddr = wmi_buffer_block(wil, val, size);
	if (!(*ioaddr)) {
		wil_err_fw(wil, "bad %s: 0x%08x\n", msg, le32_to_cpu(val));
		return false;
	}
	return true;
}

/**
 * wil_fw_verify - verify firmware file validity
@@ -160,7 +163,8 @@ static int fw_handle_data(struct wil6210_priv *wil, const void *data,
		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),
		   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,
		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);
	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,
		u32 v = le32_to_cpu(block[i].value);
		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);
		y = (x & m) | (v & ~m);
@@ -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",
		   n, gw_cmd);

	FW_ADDR_CHECK(gwa_addr, d->gateway_addr_addr, "gateway_addr_addr");
	FW_ADDR_CHECK(gwa_val, d->gateway_value_addr, "gateway_value_addr");
	FW_ADDR_CHECK(gwa_cmd, d->gateway_cmd_addr, "gateway_cmd_addr");
	FW_ADDR_CHECK(gwa_ctl, d->gateway_ctrl_address, "gateway_ctrl_address");
	if (!wil_fw_addr_check(wil, &gwa_addr, d->gateway_addr_addr, 0,
			       "gateway_addr_addr") ||
	    !wil_fw_addr_check(wil, &gwa_val, d->gateway_value_addr, 0,
			       "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"
		   " cmd 0x%08x ctl 0x%08x\n",
@@ -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",
		   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++)
		FW_ADDR_CHECK(gwa_val[k], d->gateway_value_addr[k],
			      "gateway_value_addr");
	FW_ADDR_CHECK(gwa_cmd, d->gateway_cmd_addr, "gateway_cmd_addr");
	FW_ADDR_CHECK(gwa_ctl, d->gateway_ctrl_address, "gateway_ctrl_address");
		if (!wil_fw_addr_check(wil, &gwa_val[k],
				       d->gateway_value_addr[k],
				       0, "gateway_value_addr"))
			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",
		   le32_to_cpu(d->gateway_addr_addr),
+1 −0
Original line number Diff line number Diff line
@@ -891,6 +891,7 @@ void wil_mbox_ring_le2cpus(struct wil6210_mbox_ring *r);
int wil_find_cid(struct wil6210_priv *wil, const u8 *mac);
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_addr(struct wil6210_priv *wil, u32 ptr);
int wmi_read_hdr(struct wil6210_priv *wil, __le32 ptr,
+10 −1
Original line number Diff line number Diff line
@@ -141,13 +141,15 @@ static u32 wmi_addr_remap(u32 x)
/**
 * Check address validity for WMI buffer; remap if needed
 * @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
 *
 * return address for accessing buffer from the host;
 * 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 ptr = le32_to_cpu(ptr_);
@@ -162,10 +164,17 @@ void __iomem *wmi_buffer(struct wil6210_priv *wil, __le32 ptr_)
	off = HOSTADDR(ptr);
	if (off > wil->bar_size - 4)
		return NULL;
	if (size && ((off + size > wil->bar_size) || (off + size < off)))
		return NULL;

	return wil->csr + off;
}

void __iomem *wmi_buffer(struct wil6210_priv *wil, __le32 ptr_)
{
	return wmi_buffer_block(wil, ptr_, 0);
}

/**
 * Check address validity
 */