diff --git a/Cargo.toml b/Cargo.toml index 4ba0e36e..b6a3a0d4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,4 +11,4 @@ elf = [] bzimage = [] [dependencies] -vm-memory = {version = "0.1.0", features = ["backend-mmap"]} +vm-memory = {version = ">=0.2.0", features = ["backend-mmap"]} diff --git a/coverage_config_aarch64.json b/coverage_config_aarch64.json index 70ee9da9..b1b035d8 100644 --- a/coverage_config_aarch64.json +++ b/coverage_config_aarch64.json @@ -1,5 +1,5 @@ { - "coverage_score": 70.9, + "coverage_score": 71.4, "exclude_path": "", "crate_features": "" } diff --git a/coverage_config_x86_64.json b/coverage_config_x86_64.json index 648083ff..27587e41 100644 --- a/coverage_config_x86_64.json +++ b/coverage_config_x86_64.json @@ -1,5 +1,5 @@ { - "coverage_score": 76.3, + "coverage_score": 77.1, "exclude_path": "", "crate_features": "" } diff --git a/src/loader/mod.rs b/src/loader/mod.rs index a0637635..8c299e7a 100644 --- a/src/loader/mod.rs +++ b/src/loader/mod.rs @@ -29,7 +29,7 @@ use std::io::{Read, Seek}; #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] use std::mem; -use vm_memory::{Address, Bytes, GuestAddress, GuestMemory, GuestUsize}; +use vm_memory::{Address, ByteValued, Bytes, GuestAddress, GuestMemory, GuestUsize}; #[allow(dead_code)] #[allow(non_camel_case_types)] @@ -51,9 +51,6 @@ pub mod start_info; #[allow(non_upper_case_globals)] #[cfg_attr(feature = "cargo-clippy", allow(clippy::all))] mod elf; -#[cfg(any(feature = "elf", feature = "bzimage"))] -#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] -mod struct_util; #[derive(Debug, PartialEq)] /// Kernel loader errors. @@ -193,6 +190,18 @@ pub trait KernelLoader { /// Raw ELF (a.k.a. vmlinux) kernel image support. pub struct Elf; +#[cfg(feature = "elf")] +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +unsafe impl ByteValued for elf::Elf64_Ehdr {} +#[cfg(feature = "elf")] +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +unsafe impl ByteValued for elf::Elf64_Nhdr {} +#[cfg(feature = "elf")] +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +unsafe impl ByteValued for elf::Elf64_Phdr {} + +unsafe impl ByteValued for bootparam::setup_header {} + #[cfg(feature = "elf")] #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] impl KernelLoader for Elf { @@ -218,14 +227,14 @@ impl KernelLoader for Elf { where F: Read + Seek, { - let mut ehdr: elf::Elf64_Ehdr = Default::default(); kernel_image .seek(SeekFrom::Start(0)) .map_err(|_| Error::SeekElfStart)?; - unsafe { - // read_struct is safe when reading a POD struct. It can be used and dropped without issue. - struct_util::read_struct(kernel_image, &mut ehdr).map_err(|_| Error::ReadElfHeader)?; - } + + let mut ehdr = elf::Elf64_Ehdr::default(); + ehdr.as_bytes() + .read_from(0, kernel_image, mem::size_of::()) + .map_err(|_| Error::ReadElfHeader)?; // Sanity checks if ehdr.e_ident[elf::EI_MAG0 as usize] != elf::ELFMAG0 as u8 @@ -260,18 +269,23 @@ impl KernelLoader for Elf { kernel_image .seek(SeekFrom::Start(ehdr.e_phoff)) .map_err(|_| Error::SeekProgramHeader)?; - let phdrs: Vec = unsafe { - // Reading the structs is safe for a slice of POD structs. - struct_util::read_struct_slice(kernel_image, ehdr.e_phnum as usize) - .map_err(|_| Error::ReadProgramHeader)? - }; + + let phdr_sz = mem::size_of::(); + let mut phdrs: Vec = vec![]; + for _ in 0usize..ehdr.e_phnum as usize { + let mut phdr = elf::Elf64_Phdr::default(); + phdr.as_bytes() + .read_from(0, kernel_image, phdr_sz) + .map_err(|_| Error::ReadProgramHeader)?; + phdrs.push(phdr); + } // Read in each section pointed to by the program headers. - for phdr in &phdrs { + for phdr in phdrs { if phdr.p_type != elf::PT_LOAD || phdr.p_filesz == 0 { if phdr.p_type == elf::PT_NOTE { // This segment describes a Note, check if PVH entry point is encoded. - loader_result.pvh_entry_addr = parse_elf_note(phdr, kernel_image)?; + loader_result.pvh_entry_addr = parse_elf_note(&phdr, kernel_image)?; } continue; } @@ -327,20 +341,20 @@ where // correct type that encodes the PVH entry point if there is one. let mut nhdr: elf::Elf64_Nhdr = Default::default(); let mut read_size: usize = 0; + let nhdr_sz = mem::size_of::(); while read_size < phdr.p_filesz as usize { - unsafe { - // read_struct is safe when reading a POD struct. - // It can be used and dropped without issue. - struct_util::read_struct(kernel_image, &mut nhdr).map_err(|_| Error::ReadNoteHeader)?; - } + nhdr.as_bytes() + .read_from(0, kernel_image, nhdr_sz) + .map_err(|_| Error::ReadNoteHeader)?; + // If the note header found is not the desired one, keep reading until // the end of the segment if nhdr.n_type == XEN_ELFNOTE_PHYS32_ENTRY { break; } // Skip the note header plus the size of its fields (with alignment) - read_size += mem::size_of::() + read_size += nhdr_sz + align_up(u64::from(nhdr.n_namesz), n_align) + align_up(u64::from(nhdr.n_descsz), n_align); @@ -413,15 +427,15 @@ impl KernelLoader for BzImage { let mut kernel_size = kernel_image .seek(SeekFrom::End(0)) .map_err(|_| Error::SeekBzImageEnd)? as usize; - let mut boot_header: bootparam::setup_header = Default::default(); kernel_image .seek(SeekFrom::Start(0x1F1)) .map_err(|_| Error::SeekBzImageHeader)?; - unsafe { - // read_struct is safe when reading a POD struct. It can be used and dropped without issue. - struct_util::read_struct(kernel_image, &mut boot_header) - .map_err(|_| Error::ReadBzImageHeader)?; - } + + let mut boot_header = bootparam::setup_header::default(); + boot_header + .as_bytes() + .read_from(0, kernel_image, mem::size_of::()) + .map_err(|_| Error::ReadBzImageHeader)?; // if the HdrS magic number is not found at offset 0x202, the boot protocol version is "old", // the image type is assumed as zImage, not bzImage. diff --git a/src/loader/struct_util.rs b/src/loader/struct_util.rs deleted file mode 100644 index dc0714b9..00000000 --- a/src/loader/struct_util.rs +++ /dev/null @@ -1,160 +0,0 @@ -// Copyright (c) 2019 Intel Corporation. All rights reserved. -// Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. -// -// Portions 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-BSD-3-Clause file. -// -// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause - -use std; -use std::io::Read; -use std::mem; - -#[derive(Debug)] -pub enum Error { - ReadStruct, -} -pub type Result = std::result::Result; - -/// Reads a struct from an input buffer. -/// -/// # Arguments -/// -/// * `f` - The input to read from. Often this is a file. -/// * `out` - The struct to fill with data read from `f`. -/// -/// # Safety -/// -/// This is unsafe because the struct is initialized to unverified data read from the input. -/// `read_struct` should only be called to fill plain data structs. It is not endian safe. -pub unsafe fn read_struct(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::()); - 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. -/// -/// # Arguments -/// -/// * `f` - The input to read from. Often this is a file. -/// * `len` - The number of structs to fill with data read from `f`. -/// -/// # Safety -/// -/// This is unsafe because the struct is initialized to unverified data read from the input. -/// `read_struct_slice` should only be called to fill plain data structs. It is not endian safe. -#[cfg(feature = "elf")] -pub unsafe fn read_struct_slice(f: &mut F, len: usize) -> Result> { - let mut out: Vec = 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::() * len, - ); - f.read_exact(out_slice).map_err(|_| Error::ReadStruct)?; - Ok(out) -} - -#[cfg(test)] -mod tests { - use super::*; - use std::io::Cursor; - use std::mem; - - #[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::(), - ) - }; - assert_eq!(mem::size_of::(), 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::() - 1, - ) - }; - let mut tr: TestRead = Default::default(); - unsafe { - assert!(read_struct(&mut Cursor::new(source), &mut tr).is_err()); - format!("{:?}", read_struct(&mut Cursor::new(source), &mut tr)); - } - } - - #[test] - #[cfg(feature = "elf")] - 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::() * 3, - ) - }; - - let tr: Vec = unsafe { read_struct_slice(&mut Cursor::new(source), 3).unwrap() }; - assert_eq!(orig, tr); - } -}