[PATCH v2] media: coda: validate firmware payload before copying
From: Pengpeng Hou
Date: Sun Aug 16 2026 - 01:19:29 EST
coda_copy_firmware() accepts an optional 16-byte MX header, but moves the
source pointer without reducing the remaining size. The pre-reordered path
then copies the original size from the advanced pointer, reading 16 bytes
past the firmware allocation. The native-order path always subtracts 16
from the size, including for images without the header, and can truncate or
underflow the payload length. The function also probes and reads words
without first proving their alignment and extent.
Track the payload pointer and length together. Validate the optional
header, opcode, word alignment, and paired-word requirement before copying.
Use unaligned little-endian accessors and stop hardware initialization when
the firmware layout is invalid.
Fixes: a1a87fa3a0cf ("[media] coda: add support for native order firmware files with Freescale header")
Assisted-by: Codex:gpt-5
Signed-off-by: Pengpeng Hou <pengpeng@xxxxxxxxxxx>
---
Changes since v1:
- use unaligned little-endian accessors instead of typed firmware casts
- validate the optional header before probing the first payload opcode
- validate both native and pre-reordered payload extents
- report an invalid image instead of continuing hardware initialization
v1: https://lore.kernel.org/all/20260706092922.79898-1-pengpeng@xxxxxxxxxxx/
Validation:
- scripts/checkpatch.pl --no-tree --strict: clean
- git diff --check: clean
- manual source-level audit of every read and copy in coda_copy_firmware()
.../media/platform/chips-media/coda/coda-common.c | 61 ++++++++++++++++------
1 file changed, 45 insertions(+), 16 deletions(-)
diff --git a/drivers/media/platform/chips-media/coda/coda-common.c b/drivers/media/platform/chips-media/coda/coda-common.c
index be37ea568bfe5..18311b60cc285 100644
--- a/drivers/media/platform/chips-media/coda/coda-common.c
+++ b/drivers/media/platform/chips-media/coda/coda-common.c
@@ -24,6 +24,7 @@
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/slab.h>
+#include <linux/unaligned.h>
#include <linux/videodev2.h>
#include <linux/ratelimit.h>
#include <linux/reset.h>
@@ -2927,36 +2928,60 @@ static int coda_register_device(struct coda_dev *dev, int i)
return ret;
}
-static void coda_copy_firmware(struct coda_dev *dev, const u8 * const buf,
- size_t size)
+static int coda_copy_firmware(struct coda_dev *dev, const u8 *buf,
+ size_t size)
{
- u32 *src = (u32 *)buf;
+ u32 *dst = dev->codebuf.vaddr;
+ size_t words, i;
+ u32 first, second;
/* Check if the firmware has a 16-byte Freescale header, skip it */
- if (buf[0] == 'M' && buf[1] == 'X')
- src += 4;
+ if (size < 2)
+ return -EINVAL;
+
+ if (buf[0] == 'M' && buf[1] == 'X') {
+ if (size < 16)
+ return -EINVAL;
+
+ buf += 16;
+ size -= 16;
+ }
+
+ if (size < sizeof(__le16))
+ return -EINVAL;
+
/*
* Check whether the firmware is in native order or pre-reordered for
* memory access. The first instruction opcode always is 0xe40e.
*/
- if (__le16_to_cpup((__le16 *)src) == 0xe40e) {
- u32 *dst = dev->codebuf.vaddr;
- int i;
-
+ if (get_unaligned_le16(buf) == 0xe40e) {
/* Firmware in native order, reorder while copying */
+ if (size % sizeof(u32))
+ return -EINVAL;
+
+ words = size / sizeof(u32);
if (dev->devtype->product == CODA_DX6) {
- for (i = 0; i < (size - 16) / 4; i++)
- dst[i] = (src[i] << 16) | (src[i] >> 16);
+ for (i = 0; i < words; i++) {
+ first = get_unaligned_le32(buf + i * sizeof(u32));
+ dst[i] = (first << 16) | (first >> 16);
+ }
} else {
- for (i = 0; i < (size - 16) / 4; i += 2) {
- dst[i] = (src[i + 1] << 16) | (src[i + 1] >> 16);
- dst[i + 1] = (src[i] << 16) | (src[i] >> 16);
+ if (words % 2)
+ return -EINVAL;
+
+ for (i = 0; i < words; i += 2) {
+ first = get_unaligned_le32(buf + i * sizeof(u32));
+ second = get_unaligned_le32(buf + (i + 1) * sizeof(u32));
+ dst[i] = (second << 16) | (second >> 16);
+ dst[i + 1] = (first << 16) | (first >> 16);
}
}
} else {
/* Copy the already reordered firmware image */
- memcpy(dev->codebuf.vaddr, src, size);
+ memcpy(dev->codebuf.vaddr, buf, size);
}
+
+ return 0;
}
static void coda_fw_callback(const struct firmware *fw, void *context);
@@ -3007,8 +3032,12 @@ static void coda_fw_callback(const struct firmware *fw, void *context)
if (ret < 0)
goto put_pm;
- coda_copy_firmware(dev, fw->data, fw->size);
+ ret = coda_copy_firmware(dev, fw->data, fw->size);
release_firmware(fw);
+ if (ret) {
+ v4l2_err(&dev->v4l2_dev, "invalid firmware image\n");
+ goto put_pm;
+ }
ret = coda_hw_init(dev);
if (ret < 0) {
base-commit: 4900cad020c0580dfb1be27776ff10a4ef110cfa
--
2.50.1