diff --git a/src/loader/mod.rs b/src/loader/mod.rs index a0637635..2ef99515 100644 --- a/src/loader/mod.rs +++ b/src/loader/mod.rs @@ -29,7 +29,9 @@ 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}; +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +use vm_memory::VolatileMemory; +use vm_memory::{Address, ByteValued, Bytes, GuestAddress, GuestMemory, GuestUsize}; #[allow(dead_code)] #[allow(non_camel_case_types)] @@ -51,9 +53,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 +192,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 +229,17 @@ 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)?; - } + + // TODO: use `Bytes` trait methods when rust-vmm/vm-memory#83 is merged. + let ehdr_bytes = &mut [0u8; mem::size_of::()][..]; + ehdr_bytes + .as_volatile_slice() + .read_from(0, kernel_image, ehdr_bytes.len()) + .map_err(|_| Error::ReadElfHeader)?; + let ehdr = elf::Elf64_Ehdr::from_mut_slice(ehdr_bytes).ok_or(Error::ReadElfHeader)?; // Sanity checks if ehdr.e_ident[elf::EI_MAG0 as usize] != elf::ELFMAG0 as u8 @@ -260,14 +274,24 @@ 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)? - }; + + // TODO: use `Bytes` trait methods when rust-vmm/vm-memory#83 is merged. + let phdr_sz = mem::size_of::(); + let phdrs_bytes = &mut vec![0u8; ehdr.e_phnum as usize * phdr_sz][..]; + phdrs_bytes + .as_volatile_slice() + .read_from(0, kernel_image, phdrs_bytes.len()) + .map_err(|_| Error::ReadProgramHeader)?; + let mut phdrs: Vec<&elf::Elf64_Phdr> = vec![]; + for i in 0usize..ehdr.e_phnum as usize { + phdrs.push( + elf::Elf64_Phdr::from_slice(&phdrs_bytes[i * phdr_sz..(i + 1) * phdr_sz]) + .ok_or(Error::ReadElfHeader)?, + ); + } // 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. @@ -329,11 +353,13 @@ where let mut read_size: usize = 0; 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)?; - } + let nhdr_bytes = &mut [0u8; mem::size_of::()][..]; + nhdr_bytes + .as_volatile_slice() + .read_from(0, kernel_image, nhdr_bytes.len()) + .map_err(|_| Error::ReadBzImageHeader)?; + nhdr = *elf::Elf64_Nhdr::from_mut_slice(nhdr_bytes).ok_or(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 { @@ -413,15 +439,18 @@ 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)?; - } + + // TODO: use `Bytes` trait methods when rust-vmm/vm-memory#83 is merged. + let boothdr_bytes = &mut [0u8; mem::size_of::()][..]; + boothdr_bytes + .as_volatile_slice() + .read_from(0, kernel_image, boothdr_bytes.len()) + .map_err(|_| Error::ReadBzImageHeader)?; + let boot_header = bootparam::setup_header::from_mut_slice(boothdr_bytes) + .ok_or(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. @@ -457,7 +486,7 @@ impl KernelLoader for BzImage { boot_header.code32_start = mem_offset.raw_value() as u32; let mut loader_result: KernelLoaderResult = Default::default(); - loader_result.setup_header = Some(boot_header); + loader_result.setup_header = Some(*boot_header); loader_result.kernel_load = mem_offset; //seek the compressed vmlinux.bin and read to memory 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); - } -}