diff options
author | Dylan Reid <dgreid@chromium.org> | 2017-05-03 19:37:03 -0700 |
---|---|---|
committer | chrome-bot <chrome-bot@chromium.org> | 2017-05-10 20:58:06 -0700 |
commit | 5d084600e9b2abbefe02b761f968bfc385c70723 (patch) | |
tree | 5d3f4ddd1b8bcfdd6b23e4181f16f3376c60b6b0 /sys_util/src/struct_util.rs | |
parent | 66b86b50cad0ee726194bee54c60bfbb7abb4fbe (diff) | |
download | crosvm-5d084600e9b2abbefe02b761f968bfc385c70723.tar crosvm-5d084600e9b2abbefe02b761f968bfc385c70723.tar.gz crosvm-5d084600e9b2abbefe02b761f968bfc385c70723.tar.bz2 crosvm-5d084600e9b2abbefe02b761f968bfc385c70723.tar.lz crosvm-5d084600e9b2abbefe02b761f968bfc385c70723.tar.xz crosvm-5d084600e9b2abbefe02b761f968bfc385c70723.tar.zst crosvm-5d084600e9b2abbefe02b761f968bfc385c70723.zip |
sys_util: Add struct utils
These utilities are used for reading structs from u8 slices. Often the kernel returns pointers to void pointers that represent a struct. This helps convert back to the struct. Change-Id: I6e59fb772f3ba75f006c9370412267ff565dfe4c Signed-off-by: Dylan Reid <dgreid@chromium.org> Reviewed-on: https://chromium-review.googlesource.com/497768 Reviewed-by: Zach Reizner <zachr@chromium.org>
Diffstat (limited to 'sys_util/src/struct_util.rs')
-rw-r--r-- | sys_util/src/struct_util.rs | 136 |
1 files changed, 136 insertions, 0 deletions
diff --git a/sys_util/src/struct_util.rs b/sys_util/src/struct_util.rs new file mode 100644 index 0000000..05625b1 --- /dev/null +++ b/sys_util/src/struct_util.rs @@ -0,0 +1,136 @@ +// Copyright 2017 The Chromium OS Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +use std; +use std::io::Read; +use std::mem; + +#[derive(Debug)] +pub enum Error { + ReadStruct, +} +pub type Result<T> = std::result::Result<T, Error>; + +/// Reads a struct from an input buffer. +/// This is unsafe because the struct is initialized to unverified data read from the input. +/// `read_struct` should only be called to fill plain old data structs. It is not endian safe. +/// +/// # Arguments +/// +/// * `f` - The input to read from. Often this is a file. +/// * `out` - The struct to fill with data read from `f`. +pub unsafe fn read_struct<T: Copy, F: Read>(f: &mut F, out: &mut T) -> Result<()> { + let out_slice = std::slice::from_raw_parts_mut(out as *mut T as *mut u8, mem::size_of::<T>()); + f.read_exact(out_slice).map_err(|_| Error::ReadStruct)?; + Ok(()) +} + +/// Reads an array of structs from an input buffer. Returns a Vec of structs initialized with data +/// from the specified input. +/// This is unsafe because the structs are initialized to unverified data read from the input. +/// `read_struct_slice` should only be called for plain old data structs. It is not endian safe. +/// +/// # Arguments +/// +/// * `f` - The input to read from. Often this is a file. +/// * `len` - The number of structs to fill with data read from `f`. +pub unsafe fn read_struct_slice<T: Copy, F: Read>(f: &mut F, len: usize) -> Result<Vec<T>> { + let mut out: Vec<T> = Vec::with_capacity(len); + out.set_len(len); + let out_slice = std::slice::from_raw_parts_mut(out.as_ptr() as *mut T as *mut u8, + mem::size_of::<T>() * len); + f.read_exact(out_slice).map_err(|_| Error::ReadStruct)?; + Ok(out) +} + +#[cfg(test)] +mod test { + use std::io::Cursor; + use std::mem; + use super::*; + + #[derive(Clone, Copy, Debug, Default, PartialEq)] + struct TestRead { + a: u64, + b: u8, + c: u8, + d: u8, + e: u8, + } + + #[test] + fn struct_basic_read() { + let orig = TestRead { + a: 0x7766554433221100, + b: 0x88, + c: 0x99, + d: 0xaa, + e: 0xbb, + }; + let source = unsafe { + // Don't worry it's a test + std::slice::from_raw_parts(&orig as *const _ as *const u8, + std::mem::size_of::<TestRead>()) + }; + assert_eq!(mem::size_of::<TestRead>(), mem::size_of_val(&source)); + let mut tr: TestRead = Default::default(); + unsafe { + read_struct(&mut Cursor::new(source), &mut tr).unwrap(); + } + assert_eq!(orig, tr); + } + + #[test] + fn struct_read_past_end() { + let orig = TestRead { + a: 0x7766554433221100, + b: 0x88, + c: 0x99, + d: 0xaa, + e: 0xbb, + }; + let source = unsafe { + // Don't worry it's a test + std::slice::from_raw_parts(&orig as *const _ as *const u8, + std::mem::size_of::<TestRead>() - 1) + }; + let mut tr: TestRead = Default::default(); + unsafe { + assert!(read_struct(&mut Cursor::new(source), &mut tr).is_err()); + } + } + + #[test] + fn struct_slice_read() { + let orig = vec![TestRead { + a: 0x7766554433221100, + b: 0x88, + c: 0x99, + d: 0xaa, + e: 0xbb, + }, + TestRead { + a: 0x7867564534231201, + b: 0x02, + c: 0x13, + d: 0x24, + e: 0x35, + }, + TestRead { + a: 0x7a69584736251403, + b: 0x04, + c: 0x15, + d: 0x26, + e: 0x37, + }]; + let source = unsafe { + // Don't worry it's a test + std::slice::from_raw_parts(orig.as_ptr() as *const u8, + std::mem::size_of::<TestRead>() * 3) + }; + + let tr: Vec<TestRead> = unsafe { read_struct_slice(&mut Cursor::new(source), 3).unwrap() }; + assert_eq!(orig, tr); + } +} |