CVE-2026-78911
Overview
Files Changed
services/device/usb/mojo/device_impl.ccservices/device/usb/usb_device_handle_win.cc
Patch
From 4a5d45bdafd68a08eee548d9da4e2c8f2a7a7146 Mon Sep 17 00:00:00 2001
From: Alvin Ji <alvinji@chromium.org>
Date: Fri, 10 Jul 2026 18:51:31 -0700
Subject: [PATCH] usb: Secure RESERVED control transfer generation and document blocks
Aligns the Windows wire-protocol mapping for RESERVED requests with the
USB Specification and updates DeviceImpl comments clarifying the
fallback blocking unexposed request types.
BUG=517742721
Change-Id: Icfc544c8c9cb6b322239997c789c3c7529ca748a
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/8072277
Reviewed-by: Matt Reynolds <mattreynolds@chromium.org>
Commit-Queue: Alvin Ji <alvinji@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1660643}
---
diff --git a/services/device/usb/mojo/device_impl.cc b/services/device/usb/mojo/device_impl.cc
index a0d8d49..7724921 100644
--- a/services/device/usb/mojo/device_impl.cc
+++ b/services/device/usb/mojo/device_impl.cc
@@ -380,7 +380,8 @@
return AllowAndLog(WebUsbControlTransferPermissionOutcome::kAllowed);
}
- // Default fallback (should not be reached unless new types are added).
+ // Default fallback. Catches and blocks unhandled request types, including
+ // RESERVED (which is not exposed to JavaScript via the WebIDL layer).
return BlockAndLog(WebUsbControlTransferPermissionOutcome::kBlocked);
}
diff --git a/services/device/usb/usb_device_handle_win.cc b/services/device/usb/usb_device_handle_win.cc
index 25928bf..de3a78c03 100644
--- a/services/device/usb/usb_device_handle_win.cc
+++ b/services/device/usb/usb_device_handle_win.cc
@@ -49,6 +49,10 @@
const std::wstring_view kWinUsbDriverName = L"winusb";
+// Bits 6:5 of the bmRequestType byte in the USB specification define the
+// request type: 0=Standard, 1=Class, 2=Vendor, 3=Reserved.
+constexpr uint8_t kBmRequestReserved = 3;
+
uint8_t BuildRequestFlags(UsbTransferDirection direction,
UsbControlTransferType request_type,
UsbControlTransferRecipient recipient) {
@@ -74,7 +78,8 @@
flags |= BMREQUEST_VENDOR << 5;
break;
case UsbControlTransferType::RESERVED:
- flags |= 4 << 5; // Not defined by usbspec.h.
+ flags |= kBmRequestReserved
+ << 5; // USB Spec reserved type (0x60 on the wire).
break;
}
Original Bug Report
Potential WebUSB security gate bypass via UsbControlTransferType::RESERVED
Project Fortify, an experimental security project, has identified the following potential security issue. If you’re a feature owner CC-ed on this bug, please do your best to review these reports. Please see https://chromium.googlesource.com/chromium/src/+/main/docs/security/ai-generated-security-bugs-faq.md for more information.
Overview: A potential logic bypass in WebUSB control transfer permission checks could allow a compromised renderer to issue arbitrary control transfers to protected device interfaces. By utilizing the UsbControlTransferType::RESERVED type with a DEVICE or OTHER recipient, the request can bypass standard and class-specific blocklists. This may enable communication with protected interfaces like HID or Mass Storage on platforms that support the RESERVED type.
Affected files:
services/device/usb/mojo/device_impl.ccservices/device/usb/usb_device_handle_win.cc
Estimated timestamp from git blame: 2026-04-07
Potential Security Bypass in WebUSB Control Transfer Checks
There is a potential logic-level security bypass within WebUSB’s control transfer permission checks (DeviceImpl::HasControlTransferPermission in services/device/usb/mojo/device_impl.cc).
An attacker who has already compromised the renderer process could potentially make direct Mojo calls utilizing the UsbControlTransferType::RESERVED transfer type. This bypasses the browser’s filters designed to prevent untrusted web pages from sending arbitrary or class-specific control transfers to protected USB device interfaces (such as keyboards/HID or mass storage).
Technical Details
In services/device/usb/mojo/device_impl.cc, DeviceImpl::HasControlTransferPermission performs the following validation steps:
-
Standard Request Filter Bypass:
if (type == UsbControlTransferType::STANDARD) { ... }If the transfer type is
UsbControlTransferType::RESERVED, theSTANDARDfilter checks are skipped entirely. -
Class/Interface Filter Bypass: The logic to resolve the target interface pointer is as follows:
const mojom::UsbInterfaceInfo* interface = nullptr; if (recipient == UsbControlTransferRecipient::ENDPOINT) { interface = device_handle_->FindInterfaceByEndpoint(index & 0xff); } else if (recipient == UsbControlTransferRecipient::INTERFACE || type == UsbControlTransferType::CLASS) { // This block is skipped because recipient is DEVICE/OTHER and type is RESERVED ... }When
recipientisUsbControlTransferRecipient::DEVICEorUsbControlTransferRecipient::OTHER, andtypeisRESERVED, theinterfacepointer remainsnullptr. Thus, the critical block for checking protected classes is completely bypassed:if (interface && base::FeatureList::IsEnabled( features::kWebUsbProtectedClassControlTransferBlock)) { // Protected class check (e.g., HID, Mass Storage) is bypassed because interface is nullptr ... } -
Permission Approved: Since the recipient is not
INTERFACEorENDPOINT, the function falls through and returnstrue(allowing the transfer).
Downstream Wire Transfer
Once permitted, the transfer is submitted to the low-level backend. On Linux, macOS, Android, and ChromeOS, RESERVED maps directly to standard USB reserved request type bits (0x60) on the wire. If the connected USB device’s firmware implements relaxed request validation (e.g., executing class commands based on bRequest while ignoring the transfer type bits), the transfer will succeed on the targeted protected interface.
(Note: On Windows, WinUSB incorrectly maps RESERVED due to a shift overflow in usb_device_handle_win.cc, meaning this potential bypass is highly likely limited to non-Windows platforms).
Potential Attack Steps
- A user grants WebUSB access to a composite USB device that exposes both a benign vendor interface and a protected interface (e.g., HID/keyboard).
- An attacker compromises the renderer process via an unrelated vulnerability to execute arbitrary code.
- The compromised renderer bypasses WebIDL bindings and invokes the Mojo interface
device::mojom::UsbDevice::ControlTransferOutdirectly. - The attacker specifies
type = UsbControlTransferType::RESERVED,recipient = UsbControlTransferRecipient::DEVICE,requestcorresponding to a class action (e.g., HIDSET_REPORT), andindexmapping to the target interface. - The browser process approves the request and transmits it to the USB device, potentially allowing arbitrary operations on the protected interface (e.g., keystroke injection).
Please note: These are potential steps based on source code analysis. Our tooling does not currently have the capability to run code or verify this with a live proof of concept.
Suggested Remediation
To fix this potential bypass, DeviceImpl::HasControlTransferPermission should reject control transfers of type UsbControlTransferType::RESERVED unless they are explicitly required and verified. If RESERVED is not intended for untrusted client use, consider blocking it at the Mojo boundary or within HasControlTransferPermission:
if (type == UsbControlTransferType::RESERVED) {
return false;
}
Alternatively, ensure that the protected class control transfer blocklist is applied to all request types if an interface is targetable, or treat RESERVED requests as restricted/class-specific.
Evaluated with Chrome root at commit: 5133b93d189b383c37805b1cf3a9d2dbfe8d7379
Results so far have been promising, but there can be wrong deductions. Feel free to adjust as follows:
- If you are familiar with the severity guidelines, you may adjust the severity.
- If this is a false positive, and there’s no work to be done, please close as WAI.
- If there is work to do here but not a vulnerability, please change the issue type to Task/Bug/FR.
Data from false positives will be used to improve accuracy over time. And please feel free to reach out to me directly if you have concerns or feedback on the project.