Overview

High
Severity
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactIncorrect authorization in USB
DescriptionIncorrect authorization in USB
ComponentUSB
Bug ClassLogic Error
Tracker517742721
Fix commit4a5d45bdafd6 (chromium/src) +8/-2
CISA KEVNot listed
CreditedGoogle
Disclosed2026-08-25

Files Changed

  • services/device/usb/mojo/device_impl.cc
  • services/device/usb/usb_device_handle_win.cc
From 4a5d45bdafd68a08eee548d9da4e2c8f2a7a7146 Mon Sep 17 00:00:00 2001
From: Alvin Ji <[email protected]>
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 <[email protected]>
Commit-Queue: Alvin Ji <[email protected]>
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;
   }
 
Loading diff…

Original Bug Report

reported by [email protected]

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.cc
  • services/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:

  1. Standard Request Filter Bypass:

    if (type == UsbControlTransferType::STANDARD) { ... }
    

    If the transfer type is UsbControlTransferType::RESERVED, the STANDARD filter checks are skipped entirely.

  2. 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 recipient is UsbControlTransferRecipient::DEVICE or UsbControlTransferRecipient::OTHER, and type is RESERVED, the interface pointer remains nullptr. 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
      ...
    }
    
  3. Permission Approved: Since the recipient is not INTERFACE or ENDPOINT, the function falls through and returns true (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

  1. 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).
  2. An attacker compromises the renderer process via an unrelated vulnerability to execute arbitrary code.
  3. The compromised renderer bypasses WebIDL bindings and invokes the Mojo interface device::mojom::UsbDevice::ControlTransferOut directly.
  4. The attacker specifies type = UsbControlTransferType::RESERVED, recipient = UsbControlTransferRecipient::DEVICE, request corresponding to a class action (e.g., HID SET_REPORT), and index mapping to the target interface.
  5. 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.

View on issue tracker