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