From ccb95543ee6cf6de75421a8232e0734f4e3e7545 Mon Sep 17 00:00:00 2001 From: Daniel Verkamp Date: Tue, 6 Apr 2021 16:13:53 -0700 Subject: [PATCH] usb_util: add raw access to descriptors Also allow querying the offset of each descriptor within the raw slice of bytes. BUG=b:180238956 BUG=chromium:1030778 TEST=cargo test -p usb_util Change-Id: I1e7de89e075b78b3675c8f95c992d247e8f83ce3 Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/crosvm/+/2811861 Reviewed-by: Zach Reizner Tested-by: kokoro Commit-Queue: Daniel Verkamp --- usb_util/src/descriptor.rs | 96 +++++++++++++++++++++++++------------- usb_util/src/device.rs | 7 ++- usb_util/src/lib.rs | 6 +-- 3 files changed, 71 insertions(+), 38 deletions(-) diff --git a/usb_util/src/descriptor.rs b/usb_util/src/descriptor.rs index db7300d975..88bdbc0ffe 100644 --- a/usb_util/src/descriptor.rs +++ b/usb_util/src/descriptor.rs @@ -7,12 +7,13 @@ use crate::{Error, Result}; use base::warn; use data_model::DataInit; use std::collections::BTreeMap; -use std::io::{self, Read}; use std::mem::size_of; use std::ops::Deref; #[derive(Clone)] pub struct DeviceDescriptorTree { + // Full descriptor tree in the original format returned by the device. + raw: Vec, inner: types::DeviceDescriptor, // Map of bConfigurationValue to ConfigDescriptor config_descriptors: BTreeMap, @@ -22,6 +23,7 @@ pub struct DeviceDescriptorTree { #[derive(Clone)] pub struct ConfigDescriptorTree { + offset: usize, inner: types::ConfigDescriptor, // Map of (bInterfaceNumber, bAlternateSetting) to InterfaceDescriptor interface_descriptors: BTreeMap<(u8, u8), InterfaceDescriptorTree>, @@ -29,6 +31,7 @@ pub struct ConfigDescriptorTree { #[derive(Clone)] pub struct InterfaceDescriptorTree { + offset: usize, inner: types::InterfaceDescriptor, // Map of bEndpointAddress to EndpointDescriptor endpoint_descriptors: BTreeMap, @@ -48,6 +51,11 @@ impl DeviceDescriptorTree { self.config_descriptors .get(self.config_values.get(&config_index)?) } + + /// Access the raw descriptor tree as a slice of bytes. + pub fn raw(&self) -> &[u8] { + &self.raw + } } impl Deref for DeviceDescriptorTree { @@ -68,6 +76,11 @@ impl ConfigDescriptorTree { self.interface_descriptors .get(&(interface_num, alt_setting)) } + + /// Get the offset of this configuration descriptor within the raw descriptor tree. + pub fn offset(&self) -> usize { + self.offset + } } impl Deref for ConfigDescriptorTree { @@ -82,6 +95,11 @@ impl InterfaceDescriptorTree { pub fn get_endpoint_descriptor(&self, ep_idx: u8) -> Option<&EndpointDescriptor> { self.endpoint_descriptors.get(&ep_idx) } + + /// Get the offset of this interface descriptor within the raw descriptor tree. + pub fn offset(&self) -> usize { + self.offset + } } impl Deref for InterfaceDescriptorTree { @@ -92,75 +110,87 @@ impl Deref for InterfaceDescriptorTree { } } -/// Given a `reader` for a full set of descriptors as provided by the Linux kernel +/// Given `data` containing a full set of descriptors as provided by the Linux kernel /// usbdevfs `descriptors` file, parse the descriptors into a tree data structure. -pub fn parse_usbfs_descriptors(mut reader: R) -> Result { - // Given a structure of length `struct_length`, of which `bytes_consumed` have - // already been read, skip the remainder of the struct. If `bytes_consumed` is - // more than `struct_length`, no additional bytes are skipped. - fn skip(reader: R, bytes_consumed: usize, struct_length: u8) -> io::Result { - let bytes_to_skip = u64::from(struct_length).saturating_sub(bytes_consumed as u64); - io::copy(&mut reader.take(bytes_to_skip), &mut io::sink()) - } +pub fn parse_usbfs_descriptors(data: &[u8]) -> Result { + let mut offset = 0; - // Find the next descriptor of type T and return it. + // Find the next descriptor of type T and return it and its offset. // Any other descriptors encountered while searching for the expected type are skipped. - fn next_descriptor(mut reader: R) -> Result { + // The `offset` parameter will be advanced to point to the next byte after the returned + // descriptor. + fn next_descriptor( + data: &[u8], + offset: &mut usize, + ) -> Result<(T, usize)> { let desc_type = T::descriptor_type() as u8; loop { - let hdr = DescriptorHeader::from_reader(&mut reader).map_err(Error::DescriptorRead)?; + let hdr = DescriptorHeader::from_slice( + &data + .get(*offset..*offset + size_of::()) + .ok_or(Error::DescriptorParse)?, + ) + .ok_or(Error::DescriptorParse)?; if hdr.bDescriptorType == desc_type { if usize::from(hdr.bLength) < size_of::() + size_of::() { return Err(Error::DescriptorParse); } - let desc = T::from_reader(&mut reader).map_err(Error::DescriptorRead)?; + let desc_offset = *offset; - // Skip any extra data beyond the standard descriptor length. - skip( - &mut reader, - size_of::() + size_of::(), - hdr.bLength, + *offset += size_of::(); + let desc = T::from_slice( + &data + .get(*offset..*offset + size_of::()) + .ok_or(Error::DescriptorParse)?, ) - .map_err(Error::DescriptorRead)?; - return Ok(desc); + .ok_or(Error::DescriptorParse)?; + *offset += hdr.bLength as usize - size_of::(); + return Ok((*desc, desc_offset)); + } else { + // Skip this entire descriptor, since it's not the right type. + *offset += hdr.bLength as usize; } - - // Skip this entire descriptor, since it's not the right type. - skip(&mut reader, size_of::(), hdr.bLength) - .map_err(Error::DescriptorRead)?; } } - let raw_device_descriptor: types::DeviceDescriptor = next_descriptor(&mut reader)?; + let (raw_device_descriptor, _) = + next_descriptor::(&data, &mut offset)?; let mut device_descriptor = DeviceDescriptorTree { + raw: data.into(), inner: raw_device_descriptor, config_descriptors: BTreeMap::new(), config_values: BTreeMap::new(), }; for cfg_idx in 0..device_descriptor.bNumConfigurations { - if let Ok(raw_config_descriptor) = - next_descriptor::<_, types::ConfigDescriptor>(&mut reader) + if let Ok((raw_config_descriptor, config_offset)) = + next_descriptor::(&device_descriptor.raw, &mut offset) { let mut config_descriptor = ConfigDescriptorTree { + offset: config_offset, inner: raw_config_descriptor, interface_descriptors: BTreeMap::new(), }; for intf_idx in 0..config_descriptor.bNumInterfaces { - if let Ok(raw_interface_descriptor) = - next_descriptor::<_, types::InterfaceDescriptor>(&mut reader) + if let Ok((raw_interface_descriptor, interface_offset)) = + next_descriptor::( + &device_descriptor.raw, + &mut offset, + ) { let mut interface_descriptor = InterfaceDescriptorTree { + offset: interface_offset, inner: raw_interface_descriptor, endpoint_descriptors: BTreeMap::new(), }; for ep_idx in 0..interface_descriptor.bNumEndpoints { - if let Ok(endpoint_descriptor) = - next_descriptor::<_, EndpointDescriptor>(&mut reader) - { + if let Ok((endpoint_descriptor, _)) = next_descriptor::( + &device_descriptor.raw, + &mut offset, + ) { interface_descriptor .endpoint_descriptors .insert(ep_idx, endpoint_descriptor); diff --git a/usb_util/src/device.rs b/usb_util/src/device.rs index 16cf993298..6a6f5a43da 100644 --- a/usb_util/src/device.rs +++ b/usb_util/src/device.rs @@ -12,7 +12,7 @@ use data_model::vec_with_array_field; use libc::{EAGAIN, ENODEV, ENOENT}; use std::convert::TryInto; use std::fs::File; -use std::io::{Seek, SeekFrom}; +use std::io::{Read, Seek, SeekFrom}; use std::mem::size_of_val; use std::os::raw::{c_int, c_uint, c_void}; use std::sync::Arc; @@ -55,7 +55,10 @@ impl Device { /// `fd` should be a file in usbdevfs (e.g. `/dev/bus/usb/001/002`). pub fn new(mut fd: File) -> Result { fd.seek(SeekFrom::Start(0)).map_err(Error::DescriptorRead)?; - let device_descriptor_tree = descriptor::parse_usbfs_descriptors(&mut fd)?; + let mut descriptor_data = Vec::new(); + fd.read_to_end(&mut descriptor_data) + .map_err(Error::DescriptorRead)?; + let device_descriptor_tree = descriptor::parse_usbfs_descriptors(&descriptor_data)?; let device = Device { fd: Arc::new(fd), diff --git a/usb_util/src/lib.rs b/usb_util/src/lib.rs index d793f62901..c9c620ff2e 100644 --- a/usb_util/src/lib.rs +++ b/usb_util/src/lib.rs @@ -14,7 +14,7 @@ pub use self::device::{Device, Transfer, TransferStatus}; pub use self::error::{Error, Result}; pub use self::types::{ control_request_type, ConfigDescriptor, ControlRequestDataPhaseTransferDirection, - ControlRequestRecipient, ControlRequestType, DeviceDescriptor, EndpointDescriptor, - EndpointDirection, EndpointType, InterfaceDescriptor, StandardControlRequest, UsbRequestSetup, - ENDPOINT_DIRECTION_OFFSET, + ControlRequestRecipient, ControlRequestType, DescriptorHeader, DescriptorType, + DeviceDescriptor, EndpointDescriptor, EndpointDirection, EndpointType, InterfaceDescriptor, + StandardControlRequest, UsbRequestSetup, ENDPOINT_DIRECTION_OFFSET, };