Commit 8f2b29fa authored by Jann Horn's avatar Jann Horn Committed by Yuntao Liu
Browse files

usb: cdc-acm: Check control transfer buffer size before access

stable inclusion
from stable-v6.6.79
commit 6abb510251e75f875797d8983a830e6731fa281c
category: bugfix
bugzilla: https://gitee.com/src-openeuler/kernel/issues/IBO4LT
CVE: CVE-2025-21704

Reference: https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?id=6abb510251e75f875797d8983a830e6731fa281c



--------------------------------

commit e563b01208f4d1f609bcab13333b6c0e24ce6a01 upstream.

If the first fragment is shorter than struct usb_cdc_notification, we can't
calculate an expected_size. Log an error and discard the notification
instead of reading lengths from memory outside the received data, which can
lead to memory corruption when the expected_size decreases between
fragments, causing `expected_size - acm->nb_index` to wrap.

This issue has been present since the beginning of git history; however,
it only leads to memory corruption since commit ea258352
("cdc-acm: reassemble fragmented notifications").

A mitigating factor is that acm_ctrl_irq() can only execute after userspace
has opened /dev/ttyACM*; but if ModemManager is running, ModemManager will
do that automatically depending on the USB device's vendor/product IDs and
its other interfaces.

Cc: stable <stable@kernel.org>
Fixes: 1da177e4 ("Linux-2.6.12-rc2")
Signed-off-by: default avatarJann Horn <jannh@google.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Signed-off-by: default avatarYuntao Liu <liuyuntao12@huawei.com>
parent c3c68200
Loading
Loading
Loading
Loading
+14 −3
Original line number Diff line number Diff line
@@ -371,7 +371,7 @@ static void acm_process_notification(struct acm *acm, unsigned char *buf)
static void acm_ctrl_irq(struct urb *urb)
{
	struct acm *acm = urb->context;
	struct usb_cdc_notification *dr = urb->transfer_buffer;
	struct usb_cdc_notification *dr;
	unsigned int current_size = urb->actual_length;
	unsigned int expected_size, copy_size, alloc_size;
	int retval;
@@ -398,9 +398,20 @@ static void acm_ctrl_irq(struct urb *urb)

	usb_mark_last_busy(acm->dev);

	if (acm->nb_index)
	if (acm->nb_index == 0) {
		/*
		 * The first chunk of a message must contain at least the
		 * notification header with the length field, otherwise we
		 * can't get an expected_size.
		 */
		if (current_size < sizeof(struct usb_cdc_notification)) {
			dev_dbg(&acm->control->dev, "urb too short\n");
			goto exit;
		}
		dr = urb->transfer_buffer;
	} else {
		dr = (struct usb_cdc_notification *)acm->notification_buffer;

	}
	/* size = notification-header + (optional) data */
	expected_size = sizeof(struct usb_cdc_notification) +
					le16_to_cpu(dr->wLength);