Skip to contentSkip to conversation

usb: typec: ucsi: ccg: reject firmware images without a ':' record header

PATCH In review Greg Kroah-Hartman 2 participants 2 messages May 14 – May 18

Subsystems (2)
lore
Share & tools
lore

Greg Kroah-Hartman

author maintainer
#0 lore raw

PATCH 1/1 usb: typec: ucsi: ccg: reject firmware images without a ':' record header

do_flash() locates the first .cyacd record with

	p = strnchr(fw->data, fw->size, ':');
	while (p < eof) {
		s = strnchr(p + 1, eof - p - 1, ':');
		...
	}

If the firmware image contains no ':' byte, strnchr() returns NULL. NULL compares less than the valid kernel pointer eof, so the loop body runs and strnchr() is called with p + 1 == (void *)1 and a length of roughly (unsigned long)eof, causing a wonderful crash.

The not_signed_fw fallthrough earlier in do_flash() and the chip-state branches in ccg_fw_update_needed() allow an unsigned blob to reach this loop, so a root user who can place a crafted file under /lib/firmware and write the do_flash sysfs attribute can trigger the oops.

Bail out with -EINVAL when the initial strnchr() returns NULL.

Assisted-by gkh_clanker_t1000
Cc stable <[email protected]>
Cc Heikki Krogerus <[email protected]>
---
 drivers/usb/typec/ucsi/ucsi_ccg.c | 5 +++++
 1 file changed, 5 insertions(+)
drivers/usb/typec/ucsi/ucsi_ccg.c +5 -0
index 199799b319c2..4463c1ae96bd 100644
@@ -1243,6 +1243,11 @@ static int do_flash(struct ucsi_ccg *uc, enum enum_flash_mode mode)
 	 *****************************************************************/
 
 	p = strnchr(fw->data, fw->size, ':');
+	if (!p) {
+		dev_err(dev, "Bad FW format: no ':' record header found\n");
+		err = -EINVAL;
+		goto release_mem;
+	}
 	while (p < eof) {
 		s = strnchr(p + 1, eof - p - 1, ':');
 
Signature · 1 line
2.54.0
maintainer Reviewed-by on v1 1/1
#1 lore raw
Quoted 18 lines · Greg Kroah-Hartman

do_flash() locates the first .cyacd record with

	p = strnchr(fw->data, fw->size, ':');
	while (p < eof) {
		s = strnchr(p + 1, eof - p - 1, ':');
		...
	}

If the firmware image contains no ':' byte, strnchr() returns NULL. NULL compares less than the valid kernel pointer eof, so the loop body runs and strnchr() is called with p + 1 == (void *)1 and a length of roughly (unsigned long)eof, causing a wonderful crash.

The not_signed_fw fallthrough earlier in do_flash() and the chip-state branches in ccg_fw_update_needed() allow an unsigned blob to reach this loop, so a root user who can place a crafted file under /lib/firmware and write the do_flash sysfs attribute can trigger the oops.

Quoted 6 lines

Bail out with -EINVAL when the initial strnchr() returns NULL.

Assisted-by gkh_clanker_t1000
Cc stable <[email protected]>
Cc Heikki Krogerus <[email protected]>
Quoted 22 lines
---
 drivers/usb/typec/ucsi/ucsi_ccg.c | 5 +++++
 1 file changed, 5 insertions(+)
drivers/usb/typec/ucsi/ucsi_ccg.c +5 -0
index 199799b319c2..4463c1ae96bd 100644
@@ -1243,6 +1243,11 @@ static int do_flash(struct ucsi_ccg *uc, enum enum_flash_mode mode)
 	 *****************************************************************/
 
 	p = strnchr(fw->data, fw->size, ':');
+	if (!p) {
+		dev_err(dev, "Bad FW format: no ':' record header found\n");
+		err = -EINVAL;
+		goto release_mem;
+	}
 	while (p < eof) {
 		s = strnchr(p + 1, eof - p - 1, ':');
 
Signature · 1 line
2.54.0
Signature · 1 line
heikki