linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 0/3] ihex: add some bounds checking to firmware parsing
@ 2025-05-28 20:22 Dan Carpenter
  2025-05-28 20:22 ` [PATCH 1/3] media: gspca: Add bounds checking to firmware parser Dan Carpenter
                   ` (2 more replies)
  0 siblings, 3 replies; 8+ messages in thread
From: Dan Carpenter @ 2025-05-28 20:22 UTC (permalink / raw)
  To: Dmitry Torokhov
  Cc: Al Viro, David Lechner, Enric Balletbo i Serra, Erick Archer,
	Guenter Roeck, Gustavo A. R. Silva, Hans de Goede, Hans Verkuil,
	Javier Carrasco, Kees Cook, linux-input, linux-kernel,
	linux-media, linux-watchdog, Mauro Carvalho Chehab,
	Wim Van Sebroeck

These three patches go to different subsystems so hopefully the
individual maintainers can apply them.  I can resend them as
individual patches if that's easier.

The ihex firmware code is a list of records of various lengths.  The
ihex code ensures that total length of the records doesn't read
beyond the end of the fw->data[], however the parsers need to check
that individual records are not too large.

Dan Carpenter (3):
  media: gspca: Add bounds checking to firmware parser
  watchdog: ziirave_wdt: check record length in ziirave_firm_verify()
  Input: ims-pcu - Check record size in ims_pcu_flash_firmware()

 drivers/input/misc/ims-pcu.c    |  6 ++++++
 drivers/media/usb/gspca/vicam.c | 10 ++++++++--
 drivers/watchdog/ziirave_wdt.c  |  3 +++
 3 files changed, 17 insertions(+), 2 deletions(-)

-- 
2.47.2


^ permalink raw reply	[flat|nested] 8+ messages in thread

* [PATCH 1/3] media: gspca: Add bounds checking to firmware parser
  2025-05-28 20:22 [PATCH 0/3] ihex: add some bounds checking to firmware parsing Dan Carpenter
@ 2025-05-28 20:22 ` Dan Carpenter
  2025-05-28 20:22 ` [PATCH 2/3] watchdog: ziirave_wdt: check record length in ziirave_firm_verify() Dan Carpenter
  2025-05-28 20:22 ` [PATCH 3/3] Input: ims-pcu - Check record size in ims_pcu_flash_firmware() Dan Carpenter
  2 siblings, 0 replies; 8+ messages in thread
From: Dan Carpenter @ 2025-05-28 20:22 UTC (permalink / raw)
  To: Hans de Goede
  Cc: Hans Verkuil, Mauro Carvalho Chehab, linux-media, linux-kernel

This sd_init() function reads the firmware.  The firmware data holds a
series of records and the function reads each record and sends the data
to the device.  The request_ihex_firmware() function
calls ihex_validate_fw() which ensures that the total length of all the
records won't read out of bounds of the fw->data[].

However, a potential issue is if there is a single very large
record (larger than PAGE_SIZE) and that would result in memory
corruption.  Generally we trust the firmware, but it's always better to
double check.

Fixes: 49b61ec9b5af ("[media] gspca: Add new vicam subdriver")
Cc: stable@vger.kernel.org
Signed-off-by: Dan Carpenter <dan.carpenter@linaro.org>
---
 drivers/media/usb/gspca/vicam.c | 10 ++++++++--
 1 file changed, 8 insertions(+), 2 deletions(-)

diff --git a/drivers/media/usb/gspca/vicam.c b/drivers/media/usb/gspca/vicam.c
index d98343fd33fe..91e177aa8136 100644
--- a/drivers/media/usb/gspca/vicam.c
+++ b/drivers/media/usb/gspca/vicam.c
@@ -227,6 +227,7 @@ static int sd_init(struct gspca_dev *gspca_dev)
 	const struct ihex_binrec *rec;
 	const struct firmware *fw;
 	u8 *firmware_buf;
+	int len;
 
 	ret = request_ihex_firmware(&fw, VICAM_FIRMWARE,
 				    &gspca_dev->dev->dev);
@@ -241,9 +242,14 @@ static int sd_init(struct gspca_dev *gspca_dev)
 		goto exit;
 	}
 	for (rec = (void *)fw->data; rec; rec = ihex_next_binrec(rec)) {
-		memcpy(firmware_buf, rec->data, be16_to_cpu(rec->len));
+		len = be16_to_cpu(rec->len);
+		if (len > PAGE_SIZE) {
+			ret = -EINVAL;
+			break;
+		}
+		memcpy(firmware_buf, rec->data, len);
 		ret = vicam_control_msg(gspca_dev, 0xff, 0, 0, firmware_buf,
-					be16_to_cpu(rec->len));
+					len);
 		if (ret < 0)
 			break;
 	}
-- 
2.47.2


^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [PATCH 2/3] watchdog: ziirave_wdt: check record length in ziirave_firm_verify()
  2025-05-28 20:22 [PATCH 0/3] ihex: add some bounds checking to firmware parsing Dan Carpenter
  2025-05-28 20:22 ` [PATCH 1/3] media: gspca: Add bounds checking to firmware parser Dan Carpenter
@ 2025-05-28 20:22 ` Dan Carpenter
  2025-06-02 14:11   ` Guenter Roeck
  2025-05-28 20:22 ` [PATCH 3/3] Input: ims-pcu - Check record size in ims_pcu_flash_firmware() Dan Carpenter
  2 siblings, 1 reply; 8+ messages in thread
From: Dan Carpenter @ 2025-05-28 20:22 UTC (permalink / raw)
  To: Enric Balletbo i Serra
  Cc: Wim Van Sebroeck, Guenter Roeck, linux-watchdog, linux-kernel

The "rec->len" value comes from the firmware.  We generally do
trust firmware, but it's always better to double check.  If
the length value is too large it would lead to memory corruption
when we set "data[i] = ret;"

Fixes: 217209db0204 ("watchdog: ziirave_wdt: Add support to upload the firmware.")
Signed-off-by: Dan Carpenter <dan.carpenter@linaro.org>
---
 drivers/watchdog/ziirave_wdt.c | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/drivers/watchdog/ziirave_wdt.c b/drivers/watchdog/ziirave_wdt.c
index fcc1ba02e75b..5c6e3fa001d8 100644
--- a/drivers/watchdog/ziirave_wdt.c
+++ b/drivers/watchdog/ziirave_wdt.c
@@ -302,6 +302,9 @@ static int ziirave_firm_verify(struct watchdog_device *wdd,
 		const u16 len = be16_to_cpu(rec->len);
 		const u32 addr = be32_to_cpu(rec->addr);
 
+		if (len > sizeof(data))
+			return -EINVAL;
+
 		if (ziirave_firm_addr_readonly(addr))
 			continue;
 
-- 
2.47.2


^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [PATCH 3/3] Input: ims-pcu - Check record size in ims_pcu_flash_firmware()
  2025-05-28 20:22 [PATCH 0/3] ihex: add some bounds checking to firmware parsing Dan Carpenter
  2025-05-28 20:22 ` [PATCH 1/3] media: gspca: Add bounds checking to firmware parser Dan Carpenter
  2025-05-28 20:22 ` [PATCH 2/3] watchdog: ziirave_wdt: check record length in ziirave_firm_verify() Dan Carpenter
@ 2025-05-28 20:22 ` Dan Carpenter
  2025-05-28 23:26   ` Kees Cook
  2025-05-30 23:14   ` Dmitry Torokhov
  2 siblings, 2 replies; 8+ messages in thread
From: Dan Carpenter @ 2025-05-28 20:22 UTC (permalink / raw)
  To: Dmitry Torokhov, Kees Cook
  Cc: Javier Carrasco, David Lechner, Gustavo A. R. Silva, Al Viro,
	Erick Archer, linux-input, linux-kernel

The "len" variable comes from the firmware and we generally do
trust firmware, but it's always better to double check.  If the "len"
is too large it could result in memory corruption when we do
"memcpy(fragment->data, rec->data, len);"

Fixes: 628329d52474 ("Input: add IMS Passenger Control Unit driver")
Signed-off-by: Dan Carpenter <dan.carpenter@linaro.org>
---
Kees, this is a __counted_by() thing.  Would the checkers catch this?
We know the maximum valid length for "fragment" is and so it's maybe
possible to know that "fragment->len = len;" is too long?

 drivers/input/misc/ims-pcu.c | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/drivers/input/misc/ims-pcu.c b/drivers/input/misc/ims-pcu.c
index d9ee14b1f451..4581f1c53644 100644
--- a/drivers/input/misc/ims-pcu.c
+++ b/drivers/input/misc/ims-pcu.c
@@ -844,6 +844,12 @@ static int ims_pcu_flash_firmware(struct ims_pcu *pcu,
 		addr = be32_to_cpu(rec->addr) / 2;
 		len = be16_to_cpu(rec->len);
 
+		if (len > sizeof(pcu->cmd_buf) - 1 - sizeof(*fragment)) {
+			dev_err(pcu->dev,
+				"Invalid record length in firmware: %d\n", len);
+			return -EINVAL;
+		}
+
 		fragment = (void *)&pcu->cmd_buf[1];
 		put_unaligned_le32(addr, &fragment->addr);
 		fragment->len = len;
-- 
2.47.2


^ permalink raw reply related	[flat|nested] 8+ messages in thread

* Re: [PATCH 3/3] Input: ims-pcu - Check record size in ims_pcu_flash_firmware()
  2025-05-28 20:22 ` [PATCH 3/3] Input: ims-pcu - Check record size in ims_pcu_flash_firmware() Dan Carpenter
@ 2025-05-28 23:26   ` Kees Cook
  2025-06-02 11:00     ` Dan Carpenter
  2025-05-30 23:14   ` Dmitry Torokhov
  1 sibling, 1 reply; 8+ messages in thread
From: Kees Cook @ 2025-05-28 23:26 UTC (permalink / raw)
  To: Dan Carpenter
  Cc: Dmitry Torokhov, Javier Carrasco, David Lechner,
	Gustavo A. R. Silva, Al Viro, Erick Archer, linux-input,
	linux-kernel

On Wed, May 28, 2025 at 11:22:24PM +0300, Dan Carpenter wrote:
> The "len" variable comes from the firmware and we generally do
> trust firmware, but it's always better to double check.  If the "len"
> is too large it could result in memory corruption when we do
> "memcpy(fragment->data, rec->data, len);"
> 
> Fixes: 628329d52474 ("Input: add IMS Passenger Control Unit driver")
> Signed-off-by: Dan Carpenter <dan.carpenter@linaro.org>
> ---
> Kees, this is a __counted_by() thing.  Would the checkers catch this?
> We know the maximum valid length for "fragment" is and so it's maybe
> possible to know that "fragment->len = len;" is too long?

I see:

pcu->cmd_buf as:

        u8 cmd_buf[IMS_PCU_BUF_SIZE];

and fragment is:

struct ims_pcu_flash_fmt {
        __le32 addr;
        u8 len;
        u8 data[] __counted_by(len);
};

I assume you're asking about this line:

		fragment->len = len;

I'm not aware of any compiler instrumentation that would bounds check
this -- it was designed to trust these sort of explicit assignments.

This is hardly the only place in the kernel doing this kind of
deserialization into a flexible array structure, so maybe there should
be some kind of helper to do the bounds checking and set the
"counted_by" counter?

#define gimme(from, into, counter, len)				\
	({							\
		int __gimme_rc = -EINVAL			\
		size_t __gimme_size = __member_size(from);	\
		if (__gimme_size >= sizeof(*into) &&		\
		    __gimme_size - sizeof(*into) >= len) {	\
			into = (void *)from;			\
			into->counter = len;			\
			__gimme_rc = 0;				\
		}						\
		__gimme_rc;					\
	})

	rc = gimme(&pcu->cmd_buf[1], fragment, len, len);
	if (rc) {
		dev_err(pcu->dev,
			"Invalid record length in firmware: %d\n", len);
		return rc;
	}

-Kees

> 
>  drivers/input/misc/ims-pcu.c | 6 ++++++
>  1 file changed, 6 insertions(+)
> 
> diff --git a/drivers/input/misc/ims-pcu.c b/drivers/input/misc/ims-pcu.c
> index d9ee14b1f451..4581f1c53644 100644
> --- a/drivers/input/misc/ims-pcu.c
> +++ b/drivers/input/misc/ims-pcu.c
> @@ -844,6 +844,12 @@ static int ims_pcu_flash_firmware(struct ims_pcu *pcu,
>  		addr = be32_to_cpu(rec->addr) / 2;
>  		len = be16_to_cpu(rec->len);
>  
> +		if (len > sizeof(pcu->cmd_buf) - 1 - sizeof(*fragment)) {
> +			dev_err(pcu->dev,
> +				"Invalid record length in firmware: %d\n", len);
> +			return -EINVAL;
> +		}
> +
>  		fragment = (void *)&pcu->cmd_buf[1];
>  		put_unaligned_le32(addr, &fragment->addr);
>  		fragment->len = len;
> -- 
> 2.47.2
> 

-- 
Kees Cook

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH 3/3] Input: ims-pcu - Check record size in ims_pcu_flash_firmware()
  2025-05-28 20:22 ` [PATCH 3/3] Input: ims-pcu - Check record size in ims_pcu_flash_firmware() Dan Carpenter
  2025-05-28 23:26   ` Kees Cook
@ 2025-05-30 23:14   ` Dmitry Torokhov
  1 sibling, 0 replies; 8+ messages in thread
From: Dmitry Torokhov @ 2025-05-30 23:14 UTC (permalink / raw)
  To: Dan Carpenter
  Cc: Kees Cook, Javier Carrasco, David Lechner, Gustavo A. R. Silva,
	Al Viro, Erick Archer, linux-input, linux-kernel

On Wed, May 28, 2025 at 11:22:24PM +0300, Dan Carpenter wrote:
> The "len" variable comes from the firmware and we generally do
> trust firmware, but it's always better to double check.  If the "len"
> is too large it could result in memory corruption when we do
> "memcpy(fragment->data, rec->data, len);"
> 
> Fixes: 628329d52474 ("Input: add IMS Passenger Control Unit driver")
> Signed-off-by: Dan Carpenter <dan.carpenter@linaro.org>

Applied, thank you.

-- 
Dmitry

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH 3/3] Input: ims-pcu - Check record size in ims_pcu_flash_firmware()
  2025-05-28 23:26   ` Kees Cook
@ 2025-06-02 11:00     ` Dan Carpenter
  0 siblings, 0 replies; 8+ messages in thread
From: Dan Carpenter @ 2025-06-02 11:00 UTC (permalink / raw)
  To: Kees Cook
  Cc: Dmitry Torokhov, Javier Carrasco, David Lechner,
	Gustavo A. R. Silva, Al Viro, Erick Archer, linux-input,
	linux-kernel

On Wed, May 28, 2025 at 04:26:18PM -0700, Kees Cook wrote:
> On Wed, May 28, 2025 at 11:22:24PM +0300, Dan Carpenter wrote:
> > The "len" variable comes from the firmware and we generally do
> > trust firmware, but it's always better to double check.  If the "len"
> > is too large it could result in memory corruption when we do
> > "memcpy(fragment->data, rec->data, len);"
> > 
> > Fixes: 628329d52474 ("Input: add IMS Passenger Control Unit driver")
> > Signed-off-by: Dan Carpenter <dan.carpenter@linaro.org>
> > ---
> > Kees, this is a __counted_by() thing.  Would the checkers catch this?
> > We know the maximum valid length for "fragment" is and so it's maybe
> > possible to know that "fragment->len = len;" is too long?
> 
> I see:
> 
> pcu->cmd_buf as:
> 
>         u8 cmd_buf[IMS_PCU_BUF_SIZE];
> 
> and fragment is:
> 
> struct ims_pcu_flash_fmt {
>         __le32 addr;
>         u8 len;
>         u8 data[] __counted_by(len);
> };
> 
> I assume you're asking about this line:
> 
> 		fragment->len = len;
> 
> I'm not aware of any compiler instrumentation that would bounds check
> this -- it was designed to trust these sort of explicit assignments.
> 
> This is hardly the only place in the kernel doing this kind of
> deserialization into a flexible array structure, so maybe there should
> be some kind of helper to do the bounds checking and set the
> "counted_by" counter?
> 
> #define gimme(from, into, counter, len)				\
> 	({							\
> 		int __gimme_rc = -EINVAL			\
> 		size_t __gimme_size = __member_size(from);	\
> 		if (__gimme_size >= sizeof(*into) &&		\
> 		    __gimme_size - sizeof(*into) >= len) {	\
> 			into = (void *)from;			\
> 			into->counter = len;			\
> 			__gimme_rc = 0;				\
> 		}						\
> 		__gimme_rc;					\
> 	})
> 
> 	rc = gimme(&pcu->cmd_buf[1], fragment, len, len);
> 	if (rc) {
> 		dev_err(pcu->dev,
> 			"Invalid record length in firmware: %d\n", len);
> 		return rc;
> 	}

I don't think that really scales...  I don't know how KASAN works
internally.  I was thinking it might track the buffer size when we
assign "fragment = (void *)&pcu->cmd_buf[1];" so it could calculate
the valid values of ->len.  But that's actually quite complicated.

Smatch does track this:

drivers/input/misc/ims-pcu.c:856 ims_pcu_flash_firmware() buf size: 'fragment->data' 119 elements, 119 bytes

But:

1) Smatch doesn't know about __counted_by().  This is just a matter of
   writing the code in Sparse.
2) It's not treating fw->data[] as user controlled data because this
   driver loads the firmware asynchronously and Smatch gets confused by
   threads.

regards,
dan carpenter


^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH 2/3] watchdog: ziirave_wdt: check record length in ziirave_firm_verify()
  2025-05-28 20:22 ` [PATCH 2/3] watchdog: ziirave_wdt: check record length in ziirave_firm_verify() Dan Carpenter
@ 2025-06-02 14:11   ` Guenter Roeck
  0 siblings, 0 replies; 8+ messages in thread
From: Guenter Roeck @ 2025-06-02 14:11 UTC (permalink / raw)
  To: Dan Carpenter, Enric Balletbo i Serra
  Cc: Wim Van Sebroeck, linux-watchdog, linux-kernel

On 5/28/25 13:22, Dan Carpenter wrote:
> The "rec->len" value comes from the firmware.  We generally do
> trust firmware, but it's always better to double check.  If
> the length value is too large it would lead to memory corruption
> when we set "data[i] = ret;"
> 
> Fixes: 217209db0204 ("watchdog: ziirave_wdt: Add support to upload the firmware.")
> Signed-off-by: Dan Carpenter <dan.carpenter@linaro.org>

Reviewed-by: Guenetr Roeck <linux@roeck-us.net>

> ---
>   drivers/watchdog/ziirave_wdt.c | 3 +++
>   1 file changed, 3 insertions(+)
> 
> diff --git a/drivers/watchdog/ziirave_wdt.c b/drivers/watchdog/ziirave_wdt.c
> index fcc1ba02e75b..5c6e3fa001d8 100644
> --- a/drivers/watchdog/ziirave_wdt.c
> +++ b/drivers/watchdog/ziirave_wdt.c
> @@ -302,6 +302,9 @@ static int ziirave_firm_verify(struct watchdog_device *wdd,
>   		const u16 len = be16_to_cpu(rec->len);
>   		const u32 addr = be32_to_cpu(rec->addr);
>   
> +		if (len > sizeof(data))
> +			return -EINVAL;
> +
>   		if (ziirave_firm_addr_readonly(addr))
>   			continue;
>   


^ permalink raw reply	[flat|nested] 8+ messages in thread

end of thread, other threads:[~2025-06-02 14:11 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-05-28 20:22 [PATCH 0/3] ihex: add some bounds checking to firmware parsing Dan Carpenter
2025-05-28 20:22 ` [PATCH 1/3] media: gspca: Add bounds checking to firmware parser Dan Carpenter
2025-05-28 20:22 ` [PATCH 2/3] watchdog: ziirave_wdt: check record length in ziirave_firm_verify() Dan Carpenter
2025-06-02 14:11   ` Guenter Roeck
2025-05-28 20:22 ` [PATCH 3/3] Input: ims-pcu - Check record size in ims_pcu_flash_firmware() Dan Carpenter
2025-05-28 23:26   ` Kees Cook
2025-06-02 11:00     ` Dan Carpenter
2025-05-30 23:14   ` Dmitry Torokhov

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).