summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPengpeng Hou <pengpeng@iscas.ac.cn>2026-07-22 12:43:47 +0800
committerMarc Kleine-Budde <mkl@pengutronix.de>2026-07-29 12:00:00 +0200
commit856d6cb04e5407523566b075841dcd6423757d1c (patch)
tree8f58fda9b2e8691a40a4c491a81726f0ba9723ca
parentbef9004c5b91debfceaea2841855a4ebe81ff2b3 (diff)
downloadlinux-next-856d6cb04e5407523566b075841dcd6423757d1c.tar.gz
linux-next-856d6cb04e5407523566b075841dcd6423757d1c.zip
can: softing: fw_parse(): validate firmware record spans
fw_parse() reads a fixed record header, a firmware-provided payload, and a trailing checksum without knowing the end of the firmware blob. A truncated record can therefore make those reads exceed the blob. The same record also supplies addresses and lengths for writes into DPRAM. The generic loader uses wrap-prone mixed signed arithmetic for its bounds check, while the application loader does not bound the staging copy at all. Pass the firmware end to the parser and validate the full source record. Use a signed wide offset for generic DPRAM records and validate the application staging span against the mapped DPRAM before copying. Fixes: 03fd3cf5a179 ("can: add driver for Softing card") Signed-off-by: Pengpeng Hou <pengpeng@iscas.ac.cn> Link: https://patch.msgid.link/20260722044347.2708-1-pengpeng@iscas.ac.cn Cc: stable@kernel.org Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de>
-rw-r--r--drivers/net/can/softing/softing_fw.c46
1 files changed, 32 insertions, 14 deletions
diff --git a/drivers/net/can/softing/softing_fw.c b/drivers/net/can/softing/softing_fw.c
index 721df91cdbfb..282570daf3ef 100644
--- a/drivers/net/can/softing/softing_fw.c
+++ b/drivers/net/can/softing/softing_fw.c
@@ -91,12 +91,12 @@ int softing_bootloader_command(struct softing *card, int16_t cmd,
return ret;
}
-static int fw_parse(const uint8_t **pmem, uint16_t *ptype, uint32_t *paddr,
- uint16_t *plen, const uint8_t **pdat)
+static int fw_parse(const u8 **pmem, const u8 *limit, u16 *ptype,
+ u32 *paddr, u16 *plen, const u8 **pdat)
{
uint16_t checksum[2];
- const uint8_t *mem;
- const uint8_t *end;
+ const u8 *mem;
+ const u8 *record_end;
/*
* firmware records are a binary, unaligned stream composed of:
@@ -114,14 +114,21 @@ static int fw_parse(const uint8_t **pmem, uint16_t *ptype, uint32_t *paddr,
* endianness & alignment.
*/
mem = *pmem;
+ /* A record needs an 8-byte prefix and a 2-byte checksum. */
+ if (mem > limit || limit - mem < 10)
+ return -EINVAL;
+
*ptype = le16_to_cpup((void *)&mem[0]);
*paddr = le32_to_cpup((void *)&mem[2]);
*plen = le16_to_cpup((void *)&mem[6]);
+ if (*plen > limit - mem - 10)
+ return -EINVAL;
+
*pdat = &mem[8];
/* verify checksum */
- end = &mem[8 + *plen];
- checksum[0] = le16_to_cpup((void *)end);
- for (checksum[1] = 0; mem < end; ++mem)
+ record_end = &mem[8 + *plen];
+ checksum[0] = le16_to_cpup((void *)record_end);
+ for (checksum[1] = 0; mem < record_end; ++mem)
checksum[1] += *mem;
if (checksum[0] != checksum[1])
return -EINVAL;
@@ -139,6 +146,7 @@ int softing_load_fw(const char *file, struct softing *card,
uint16_t type, len;
uint32_t addr;
uint8_t *buf = NULL, *new_buf;
+ s64 dpram_offset;
int buflen = 0;
int8_t type_end = 0;
@@ -153,7 +161,7 @@ int softing_load_fw(const char *file, struct softing *card,
mem = fw->data;
end = &mem[fw->size];
/* look for header record */
- ret = fw_parse(&mem, &type, &addr, &len, &dat);
+ ret = fw_parse(&mem, end, &type, &addr, &len, &dat);
if (ret < 0)
goto failed;
if (type != 0xffff)
@@ -164,7 +172,7 @@ int softing_load_fw(const char *file, struct softing *card,
}
/* ok, we had a header */
while (mem < end) {
- ret = fw_parse(&mem, &type, &addr, &len, &dat);
+ ret = fw_parse(&mem, end, &type, &addr, &len, &dat);
if (ret < 0)
goto failed;
if (type == 3) {
@@ -179,9 +187,13 @@ int softing_load_fw(const char *file, struct softing *card,
goto failed;
}
- if ((addr + len + offset) > size)
+ dpram_offset = (s64)addr + offset;
+ if (dpram_offset < 0 || dpram_offset > size ||
+ len > size - dpram_offset) {
+ ret = -EINVAL;
goto failed;
- memcpy_toio(&dpram[addr + offset], dat, len);
+ }
+ memcpy_toio(&dpram[dpram_offset], dat, len);
/* be sure to flush caches from IO space */
mb();
if (len > buflen) {
@@ -195,7 +207,7 @@ int softing_load_fw(const char *file, struct softing *card,
buf = new_buf;
}
/* verify record data */
- memcpy_fromio(buf, &dpram[addr + offset], len);
+ memcpy_fromio(buf, &dpram[dpram_offset], len);
if (memcmp(buf, dat, len)) {
/* is not ok */
dev_alert(&card->pdev->dev, "DPRAM readback failed\n");
@@ -237,7 +249,7 @@ int softing_load_app_fw(const char *file, struct softing *card)
mem = fw->data;
end = &mem[fw->size];
/* look for header record */
- ret = fw_parse(&mem, &type, &addr, &len, &dat);
+ ret = fw_parse(&mem, end, &type, &addr, &len, &dat);
if (ret)
goto failed;
ret = -EINVAL;
@@ -253,7 +265,7 @@ int softing_load_app_fw(const char *file, struct softing *card)
}
/* ok, we had a header */
while (mem < end) {
- ret = fw_parse(&mem, &type, &addr, &len, &dat);
+ ret = fw_parse(&mem, end, &type, &addr, &len, &dat);
if (ret)
goto failed;
@@ -279,6 +291,12 @@ int softing_load_app_fw(const char *file, struct softing *card)
/* work in 16bit (target) */
sum &= 0xffff;
+ if (card->pdat->app.offs > card->dpram_size ||
+ len > card->dpram_size - card->pdat->app.offs) {
+ ret = -EINVAL;
+ goto failed;
+ }
+
memcpy_toio(&card->dpram[card->pdat->app.offs], dat, len);
iowrite32(card->pdat->app.offs + card->pdat->app.addr,
&card->dpram[DPRAM_COMMAND + 2]);