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