From b61e467059e55ad71f1b7ae5e1412beb3d4803e0 Mon Sep 17 00:00:00 2001 From: reillyg Date: Fri, 12 Feb 2016 17:54:22 -0800 Subject: Parse USB interface association descriptors. USB interface association descriptors are used to combine multiple interfaces into a single functional group. This patch adds support for parsing them out of the |extra_data| field left by libusb's parsing of configuration, interface and endpoint descriptors. The resulting association is then represented by setting the |first_interface| field of each interface in a function to the |interface_number| of the first interface in the function. WebUSB will use these associations to set permissions for an entire function with a single descriptor. BUG=492204 Review URL: https://codereview.chromium.org/1568673002 Cr-Commit-Position: refs/heads/master@{#375324} --- device/usb/usb_descriptors.cc | 81 +++++++++++++++++++++++++++++++++- device/usb/usb_descriptors.h | 6 +++ device/usb/usb_descriptors_unittest.cc | 75 +++++++++++++++++++++++++++++++ device/usb/usb_device_impl.cc | 2 + 4 files changed, 163 insertions(+), 1 deletion(-) (limited to 'device') diff --git a/device/usb/usb_descriptors.cc b/device/usb/usb_descriptors.cc index 6defda8..22414d7 100644 --- a/device/usb/usb_descriptors.cc +++ b/device/usb/usb_descriptors.cc @@ -7,6 +7,7 @@ #include #include +#include #include "base/barrier_closure.h" #include "base/bind.h" @@ -26,6 +27,42 @@ const uint8_t kStringDescriptorType = 0x03; const int kControlTransferTimeout = 60000; // 1 minute +struct UsbInterfaceAssociationDescriptor { + UsbInterfaceAssociationDescriptor(uint8_t first_interface, + uint8_t interface_count) + : first_interface(first_interface), interface_count(interface_count) {} + + bool operator<(const UsbInterfaceAssociationDescriptor& other) const { + return first_interface < other.first_interface; + } + + uint8_t first_interface; + uint8_t interface_count; +}; + +void ParseInterfaceAssociationDescriptors( + const std::vector& buffer, + std::vector* functions) { + const uint8_t kInterfaceAssociationDescriptorType = 11; + const uint8_t kInterfaceAssociationDescriptorLength = 8; + std::vector::const_iterator it = buffer.begin(); + + while (it != buffer.end()) { + // All descriptors must be at least 2 byte which means the length and type + // are safe to read. + if (std::distance(it, buffer.end()) < 2) + return; + uint8_t length = it[0]; + if (length > std::distance(it, buffer.end())) + return; + if (it[1] == kInterfaceAssociationDescriptorType && + length == kInterfaceAssociationDescriptorLength) { + functions->push_back(UsbInterfaceAssociationDescriptor(it[2], it[3])); + } + std::advance(it, length); + } +} + void StoreStringDescriptor(IndexMap::iterator it, const base::Closure& callback, const base::string16& string) { @@ -113,7 +150,8 @@ UsbInterfaceDescriptor::UsbInterfaceDescriptor(uint8_t interface_number, alternate_setting(alternate_setting), interface_class(interface_class), interface_subclass(interface_subclass), - interface_protocol(interface_protocol) {} + interface_protocol(interface_protocol), + first_interface(interface_number) {} UsbInterfaceDescriptor::~UsbInterfaceDescriptor() = default; @@ -128,6 +166,47 @@ UsbConfigDescriptor::UsbConfigDescriptor(uint8_t configuration_value, UsbConfigDescriptor::~UsbConfigDescriptor() = default; +void UsbConfigDescriptor::AssignFirstInterfaceNumbers() { + std::vector functions; + ParseInterfaceAssociationDescriptors(extra_data, &functions); + for (const auto& interface : interfaces) { + ParseInterfaceAssociationDescriptors(interface.extra_data, &functions); + for (const auto& endpoint : interface.endpoints) + ParseInterfaceAssociationDescriptors(endpoint.extra_data, &functions); + } + + // libusb has collected interface association descriptors in the |extra_data| + // fields of other descriptor types. This may have disturbed their order + // but sorting by the bFirstInterface should fix it. + std::sort(functions.begin(), functions.end()); + + uint8_t remaining_interfaces = 0; + auto function_it = functions.cbegin(); + for (auto interface_it = interfaces.begin(); interface_it != interfaces.end(); + ++interface_it) { + if (remaining_interfaces > 0) { + // Continuation of a previous function. Tag all alternate interfaces + // (which are guaranteed to be contiguous). + for (uint8_t interface_number = interface_it->interface_number; + interface_it != interfaces.end() && + interface_it->interface_number == interface_number; + ++interface_it) { + interface_it->first_interface = function_it->first_interface; + } + if (--remaining_interfaces == 0) + ++function_it; + } else if (function_it != functions.end() && + interface_it->interface_number == function_it->first_interface) { + // Start of a new function. + interface_it->first_interface = function_it->first_interface; + remaining_interfaces = function_it->interface_count - 1; + } else { + // Unassociated interfaces already have |first_interface| set to + // |interface_number|. + } + } +} + bool ParseUsbStringDescriptor(const std::vector& descriptor, base::string16* output) { if (descriptor.size() < 2 || descriptor[1] != kStringDescriptorType) diff --git a/device/usb/usb_descriptors.h b/device/usb/usb_descriptors.h index c99a6de..a1414154 100644 --- a/device/usb/usb_descriptors.h +++ b/device/usb/usb_descriptors.h @@ -85,6 +85,8 @@ struct UsbInterfaceDescriptor { uint8_t interface_protocol; std::vector endpoints; std::vector extra_data; + // First interface of the function to which this interface belongs. + uint8_t first_interface; }; struct UsbConfigDescriptor { @@ -95,6 +97,10 @@ struct UsbConfigDescriptor { UsbConfigDescriptor() = delete; ~UsbConfigDescriptor(); + // Scans through |extra_data| for interface association descriptors and + // populates |first_interface| for each interface in this configuration. + void AssignFirstInterfaceNumbers(); + uint8_t configuration_value; bool self_powered; bool remote_wakeup; diff --git a/device/usb/usb_descriptors_unittest.cc b/device/usb/usb_descriptors_unittest.cc index 402304f..5bc2943 100644 --- a/device/usb/usb_descriptors_unittest.cc +++ b/device/usb/usb_descriptors_unittest.cc @@ -32,6 +32,81 @@ void ExpectStringDescriptors( class UsbDescriptorsTest : public ::testing::Test {}; +TEST_F(UsbDescriptorsTest, NoInterfaceAssociations) { + UsbConfigDescriptor config(1, false, false, 0); + config.interfaces.emplace_back(0, 0, 255, 255, 255); + config.interfaces.emplace_back(0, 1, 255, 255, 255); + config.interfaces.emplace_back(1, 0, 255, 255, 255); + config.AssignFirstInterfaceNumbers(); + + EXPECT_EQ(0, config.interfaces[0].first_interface); + EXPECT_EQ(0, config.interfaces[1].first_interface); + EXPECT_EQ(1, config.interfaces[2].first_interface); +} + +TEST_F(UsbDescriptorsTest, InterfaceAssociations) { + // Links interfaces 0 and 1 into a single function. + static const uint8_t kIAD1[] = {0x08, 0x0b, 0x00, 0x02, + 0xff, 0xff, 0xff, 0x00}; + // Only references a single interface, 2. + static const uint8_t kIAD2[] = {0x08, 0x0b, 0x02, 0x01, + 0xff, 0xff, 0xff, 0x00}; + // Malformed. References interface 3 but bInterfaceCount is 0. + static const uint8_t kIAD3[] = {0x08, 0x0b, 0x03, 0x00, + 0xff, 0xff, 0xff, 0x00}; + // Malformed. References an indefined interface. + static const uint8_t kIAD4[] = {0x08, 0x0b, 0x07, 0x00, + 0xff, 0xff, 0xff, 0x00}; + // Links interfaces 4 and 5 into a single function. + static const uint8_t kIAD5[] = {0x08, 0x0b, 0x04, 0x01, + 0xff, 0xff, 0xff, 0x00}; + + UsbConfigDescriptor config(1, false, false, 0); + config.extra_data.assign(kIAD1, kIAD1 + sizeof(kIAD1)); + config.extra_data.insert(config.extra_data.end(), kIAD2, + kIAD2 + sizeof(kIAD2)); + config.interfaces.emplace_back(0, 0, 255, 255, 255); + config.interfaces.emplace_back(1, 0, 255, 255, 255); + UsbInterfaceDescriptor iface1a(1, 1, 255, 255, 255); + iface1a.extra_data.assign(kIAD3, kIAD3 + sizeof(kIAD3)); + config.interfaces.push_back(std::move(iface1a)); + config.interfaces.emplace_back(2, 0, 255, 255, 255); + config.interfaces.emplace_back(3, 0, 255, 255, 255); + UsbInterfaceDescriptor iface4(4, 0, 255, 255, 255); + iface4.extra_data.assign(kIAD4, kIAD4 + sizeof(kIAD4)); + iface4.extra_data.insert(iface4.extra_data.end(), kIAD5, + kIAD5 + sizeof(kIAD5)); + config.interfaces.push_back(std::move(iface4)); + config.interfaces.emplace_back(5, 0, 255, 255, 255); + config.AssignFirstInterfaceNumbers(); + + EXPECT_EQ(0, config.interfaces[0].first_interface); + EXPECT_EQ(0, config.interfaces[1].first_interface); + EXPECT_EQ(0, config.interfaces[2].first_interface); + EXPECT_EQ(2, config.interfaces[3].first_interface); + EXPECT_EQ(3, config.interfaces[4].first_interface); + EXPECT_EQ(4, config.interfaces[5].first_interface); + EXPECT_EQ(4, config.interfaces[5].first_interface); +} + +TEST_F(UsbDescriptorsTest, CorruptInterfaceAssociations) { + { + // Descriptor is too short. + static const uint8_t kIAD[] = {0x01}; + UsbConfigDescriptor config(1, false, false, 0); + config.extra_data.assign(kIAD, kIAD + sizeof(kIAD)); + config.AssignFirstInterfaceNumbers(); + } + { + // Descriptor is too long. + static const uint8_t kIAD[] = {0x09, 0x0b, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00}; + UsbConfigDescriptor config(1, false, false, 0); + config.extra_data.assign(kIAD, kIAD + sizeof(kIAD)); + config.AssignFirstInterfaceNumbers(); + } +} + TEST_F(UsbDescriptorsTest, StringDescriptor) { static const uint8_t kBuffer[] = {0x1a, 0x03, 'H', 0, 'e', 0, 'l', 0, 'l', 0, 'o', 0, ' ', 0, 'w', 0, 'o', 0, 'r', 0, diff --git a/device/usb/usb_device_impl.cc b/device/usb/usb_device_impl.cc index 1a8bedf..118416d 100644 --- a/device/usb/usb_device_impl.cc +++ b/device/usb/usb_device_impl.cc @@ -135,6 +135,8 @@ void ConvertConfigDescriptor(const libusb_config_descriptor* platform_config, configuration->extra_data.assign( platform_config->extra, platform_config->extra + platform_config->extra_length); + + configuration->AssignFirstInterfaceNumbers(); } } // namespace -- cgit v1.1