• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2017 The Chromium OS Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 use std::ffi::CStr;
6 use std::fmt::{self, Display};
7 use std::io::{Read, Seek, SeekFrom};
8 use std::mem;
9 
10 use base::AsRawDescriptor;
11 use vm_memory::{GuestAddress, GuestMemory};
12 
13 #[allow(dead_code)]
14 #[allow(non_camel_case_types)]
15 #[allow(non_snake_case)]
16 #[allow(non_upper_case_globals)]
17 #[allow(clippy::all)]
18 mod elf;
19 
20 #[derive(Debug, PartialEq)]
21 pub enum Error {
22     BigEndianElfOnLittle,
23     CommandLineCopy,
24     CommandLineOverflow,
25     InvalidElfMagicNumber,
26     InvalidProgramHeaderSize,
27     InvalidProgramHeaderOffset,
28     InvalidProgramHeaderAddress,
29     InvalidProgramHeaderMemSize,
30     ReadElfHeader,
31     ReadKernelImage,
32     ReadProgramHeader,
33     SeekKernelStart,
34     SeekElfStart,
35     SeekProgramHeader,
36 }
37 pub type Result<T> = std::result::Result<T, Error>;
38 
39 impl std::error::Error for Error {}
40 
41 impl Display for Error {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result42     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
43         use self::Error::*;
44 
45         let description = match self {
46             BigEndianElfOnLittle => "trying to load big-endian binary on little-endian machine",
47             CommandLineCopy => "failed writing command line to guest memory",
48             CommandLineOverflow => "command line overflowed guest memory",
49             InvalidElfMagicNumber => "invalid Elf magic number",
50             InvalidProgramHeaderSize => "invalid program header size",
51             InvalidProgramHeaderOffset => "invalid program header offset",
52             InvalidProgramHeaderAddress => "invalid Program Header Address",
53             InvalidProgramHeaderMemSize => "invalid Program Header memory size",
54             ReadElfHeader => "unable to read elf header",
55             ReadKernelImage => "unable to read kernel image",
56             ReadProgramHeader => "unable to read program header",
57             SeekKernelStart => "unable to seek to kernel start",
58             SeekElfStart => "unable to seek to elf start",
59             SeekProgramHeader => "unable to seek to program header",
60         };
61 
62         write!(f, "kernel loader: {}", description)
63     }
64 }
65 
66 /// Loads a kernel from a vmlinux elf image to a slice
67 ///
68 /// # Arguments
69 ///
70 /// * `guest_mem` - The guest memory region the kernel is written to.
71 /// * `kernel_start` - The offset into `guest_mem` at which to load the kernel.
72 /// * `kernel_image` - Input vmlinux image.
load_kernel<F>( guest_mem: &GuestMemory, kernel_start: GuestAddress, kernel_image: &mut F, ) -> Result<u64> where F: Read + Seek + AsRawDescriptor,73 pub fn load_kernel<F>(
74     guest_mem: &GuestMemory,
75     kernel_start: GuestAddress,
76     kernel_image: &mut F,
77 ) -> Result<u64>
78 where
79     F: Read + Seek + AsRawDescriptor,
80 {
81     let mut ehdr: elf::Elf64_Ehdr = Default::default();
82     kernel_image
83         .seek(SeekFrom::Start(0))
84         .map_err(|_| Error::SeekElfStart)?;
85     unsafe {
86         // read_struct is safe when reading a POD struct.  It can be used and dropped without issue.
87         base::read_struct(kernel_image, &mut ehdr).map_err(|_| Error::ReadElfHeader)?;
88     }
89 
90     // Sanity checks
91     if ehdr.e_ident[elf::EI_MAG0 as usize] != elf::ELFMAG0 as u8
92         || ehdr.e_ident[elf::EI_MAG1 as usize] != elf::ELFMAG1
93         || ehdr.e_ident[elf::EI_MAG2 as usize] != elf::ELFMAG2
94         || ehdr.e_ident[elf::EI_MAG3 as usize] != elf::ELFMAG3
95     {
96         return Err(Error::InvalidElfMagicNumber);
97     }
98     if ehdr.e_ident[elf::EI_DATA as usize] != elf::ELFDATA2LSB as u8 {
99         return Err(Error::BigEndianElfOnLittle);
100     }
101     if ehdr.e_phentsize as usize != mem::size_of::<elf::Elf64_Phdr>() {
102         return Err(Error::InvalidProgramHeaderSize);
103     }
104     if (ehdr.e_phoff as usize) < mem::size_of::<elf::Elf64_Ehdr>() {
105         // If the program header is backwards, bail.
106         return Err(Error::InvalidProgramHeaderOffset);
107     }
108 
109     kernel_image
110         .seek(SeekFrom::Start(ehdr.e_phoff))
111         .map_err(|_| Error::SeekProgramHeader)?;
112     let phdrs: Vec<elf::Elf64_Phdr> = unsafe {
113         // Reading the structs is safe for a slice of POD structs.
114         base::read_struct_slice(kernel_image, ehdr.e_phnum as usize)
115             .map_err(|_| Error::ReadProgramHeader)?
116     };
117 
118     let mut kernel_end = 0;
119 
120     // Read in each section pointed to by the program headers.
121     for phdr in &phdrs {
122         if phdr.p_type != elf::PT_LOAD || phdr.p_filesz == 0 {
123             continue;
124         }
125 
126         kernel_image
127             .seek(SeekFrom::Start(phdr.p_offset))
128             .map_err(|_| Error::SeekKernelStart)?;
129 
130         let mem_offset = kernel_start
131             .checked_add(phdr.p_paddr)
132             .ok_or(Error::InvalidProgramHeaderAddress)?;
133         guest_mem
134             .read_to_memory(mem_offset, kernel_image, phdr.p_filesz as usize)
135             .map_err(|_| Error::ReadKernelImage)?;
136 
137         kernel_end = mem_offset
138             .offset()
139             .checked_add(phdr.p_memsz)
140             .ok_or(Error::InvalidProgramHeaderMemSize)?;
141     }
142 
143     Ok(kernel_end)
144 }
145 
146 /// Writes the command line string to the given memory slice.
147 ///
148 /// # Arguments
149 ///
150 /// * `guest_mem` - A u8 slice that will be partially overwritten by the command line.
151 /// * `guest_addr` - The address in `guest_mem` at which to load the command line.
152 /// * `cmdline` - The kernel command line.
load_cmdline( guest_mem: &GuestMemory, guest_addr: GuestAddress, cmdline: &CStr, ) -> Result<()>153 pub fn load_cmdline(
154     guest_mem: &GuestMemory,
155     guest_addr: GuestAddress,
156     cmdline: &CStr,
157 ) -> Result<()> {
158     let len = cmdline.to_bytes().len();
159     if len == 0 {
160         return Ok(());
161     }
162 
163     let end = guest_addr
164         .checked_add(len as u64 + 1)
165         .ok_or(Error::CommandLineOverflow)?; // Extra for null termination.
166     if end > guest_mem.end_addr() {
167         return Err(Error::CommandLineOverflow);
168     }
169 
170     guest_mem
171         .write_at_addr(cmdline.to_bytes_with_nul(), guest_addr)
172         .map_err(|_| Error::CommandLineCopy)?;
173 
174     Ok(())
175 }
176 
177 #[cfg(test)]
178 mod test {
179     use super::*;
180     use std::fs::File;
181     use std::io::Write;
182     use tempfile::tempfile;
183     use vm_memory::{GuestAddress, GuestMemory};
184 
185     const MEM_SIZE: u64 = 0x8000;
186 
create_guest_mem() -> GuestMemory187     fn create_guest_mem() -> GuestMemory {
188         GuestMemory::new(&vec![(GuestAddress(0x0), MEM_SIZE)]).unwrap()
189     }
190 
191     #[test]
cmdline_overflow()192     fn cmdline_overflow() {
193         let gm = create_guest_mem();
194         let cmdline_address = GuestAddress(MEM_SIZE - 5);
195         assert_eq!(
196             Err(Error::CommandLineOverflow),
197             load_cmdline(
198                 &gm,
199                 cmdline_address,
200                 CStr::from_bytes_with_nul(b"12345\0").unwrap()
201             )
202         );
203     }
204 
205     #[test]
cmdline_write_end()206     fn cmdline_write_end() {
207         let gm = create_guest_mem();
208         let mut cmdline_address = GuestAddress(45);
209         assert_eq!(
210             Ok(()),
211             load_cmdline(
212                 &gm,
213                 cmdline_address,
214                 CStr::from_bytes_with_nul(b"1234\0").unwrap()
215             )
216         );
217         let val: u8 = gm.read_obj_from_addr(cmdline_address).unwrap();
218         assert_eq!(val, '1' as u8);
219         cmdline_address = cmdline_address.unchecked_add(1);
220         let val: u8 = gm.read_obj_from_addr(cmdline_address).unwrap();
221         assert_eq!(val, '2' as u8);
222         cmdline_address = cmdline_address.unchecked_add(1);
223         let val: u8 = gm.read_obj_from_addr(cmdline_address).unwrap();
224         assert_eq!(val, '3' as u8);
225         cmdline_address = cmdline_address.unchecked_add(1);
226         let val: u8 = gm.read_obj_from_addr(cmdline_address).unwrap();
227         assert_eq!(val, '4' as u8);
228         cmdline_address = cmdline_address.unchecked_add(1);
229         let val: u8 = gm.read_obj_from_addr(cmdline_address).unwrap();
230         assert_eq!(val, '\0' as u8);
231     }
232 
233     // Elf64 image that prints hello world on x86_64.
make_elf_bin() -> File234     fn make_elf_bin() -> File {
235         let elf_bytes = include_bytes!("test_elf.bin");
236         let mut file = tempfile().expect("failed to create tempfile");
237         file.write_all(elf_bytes)
238             .expect("failed to write elf to shared memoy");
239         file
240     }
241 
mutate_elf_bin(mut f: &File, offset: u64, val: u8)242     fn mutate_elf_bin(mut f: &File, offset: u64, val: u8) {
243         f.seek(SeekFrom::Start(offset))
244             .expect("failed to seek file");
245         f.write(&[val])
246             .expect("failed to write mutated value to file");
247     }
248 
249     #[test]
load_elf()250     fn load_elf() {
251         let gm = create_guest_mem();
252         let kernel_addr = GuestAddress(0x0);
253         let mut image = make_elf_bin();
254         assert_eq!(Ok(16613), load_kernel(&gm, kernel_addr, &mut image));
255     }
256 
257     #[test]
bad_magic()258     fn bad_magic() {
259         let gm = create_guest_mem();
260         let kernel_addr = GuestAddress(0x0);
261         let mut bad_image = make_elf_bin();
262         mutate_elf_bin(&bad_image, 0x1, 0x33);
263         assert_eq!(
264             Err(Error::InvalidElfMagicNumber),
265             load_kernel(&gm, kernel_addr, &mut bad_image)
266         );
267     }
268 
269     #[test]
bad_endian()270     fn bad_endian() {
271         // Only little endian is supported
272         let gm = create_guest_mem();
273         let kernel_addr = GuestAddress(0x0);
274         let mut bad_image = make_elf_bin();
275         mutate_elf_bin(&bad_image, 0x5, 2);
276         assert_eq!(
277             Err(Error::BigEndianElfOnLittle),
278             load_kernel(&gm, kernel_addr, &mut bad_image)
279         );
280     }
281 
282     #[test]
bad_phoff()283     fn bad_phoff() {
284         // program header has to be past the end of the elf header
285         let gm = create_guest_mem();
286         let kernel_addr = GuestAddress(0x0);
287         let mut bad_image = make_elf_bin();
288         mutate_elf_bin(&bad_image, 0x20, 0x10);
289         assert_eq!(
290             Err(Error::InvalidProgramHeaderOffset),
291             load_kernel(&gm, kernel_addr, &mut bad_image)
292         );
293     }
294 }
295