• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2017 The ChromiumOS Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 //! Linux kernel ELF file loader.
6 
7 use std::ffi::CStr;
8 use std::io::Read;
9 use std::io::Seek;
10 use std::io::SeekFrom;
11 use std::mem;
12 
13 use base::AsRawDescriptor;
14 use data_model::zerocopy_from_reader;
15 use remain::sorted;
16 use resources::AddressRange;
17 use thiserror::Error;
18 use vm_memory::GuestAddress;
19 use vm_memory::GuestMemory;
20 use zerocopy::FromBytes;
21 
22 #[allow(dead_code)]
23 #[allow(non_camel_case_types)]
24 #[allow(non_snake_case)]
25 #[allow(non_upper_case_globals)]
26 #[allow(clippy::all)]
27 mod elf;
28 
29 mod arm64;
30 
31 pub use arm64::load_arm64_kernel;
32 
33 #[sorted]
34 #[derive(Error, Debug, PartialEq, Eq)]
35 pub enum Error {
36     #[error("trying to load big-endian binary on little-endian machine")]
37     BigEndianOnLittle,
38     #[error("failed writing command line to guest memory")]
39     CommandLineCopy,
40     #[error("command line overflowed guest memory")]
41     CommandLineOverflow,
42     #[error("invalid elf class")]
43     InvalidElfClass,
44     #[error("invalid elf version")]
45     InvalidElfVersion,
46     #[error("invalid entry point")]
47     InvalidEntryPoint,
48     #[error("invalid kernel offset")]
49     InvalidKernelOffset,
50     #[error("invalid kernel size")]
51     InvalidKernelSize,
52     #[error("invalid magic number")]
53     InvalidMagicNumber,
54     #[error("invalid Program Header Address")]
55     InvalidProgramHeaderAddress,
56     #[error("invalid Program Header memory size")]
57     InvalidProgramHeaderMemSize,
58     #[error("invalid program header offset")]
59     InvalidProgramHeaderOffset,
60     #[error("invalid program header size")]
61     InvalidProgramHeaderSize,
62     #[error("no loadable program headers found")]
63     NoLoadableProgramHeaders,
64     #[error("program header address out of allowed address range")]
65     ProgramHeaderAddressOutOfRange,
66     #[error("unable to read header")]
67     ReadHeader,
68     #[error("unable to read kernel image")]
69     ReadKernelImage,
70     #[error("unable to read program header")]
71     ReadProgramHeader,
72     #[error("unable to seek to kernel end")]
73     SeekKernelEnd,
74     #[error("unable to seek to kernel start")]
75     SeekKernelStart,
76     #[error("unable to seek to program header")]
77     SeekProgramHeader,
78 }
79 pub type Result<T> = std::result::Result<T, Error>;
80 
81 #[derive(Debug, Copy, Clone, PartialEq, Eq)]
82 /// Information about a kernel loaded with the [`load_elf`] function.
83 pub struct LoadedKernel {
84     /// Address range containg the bounds of the loaded program headers.
85     /// `address_range.start` is the start of the lowest loaded program header.
86     /// `address_range.end` is the end of the highest loaded program header.
87     pub address_range: AddressRange,
88 
89     /// Size of the kernel image in bytes.
90     pub size: u64,
91 
92     /// Entry point address of the kernel.
93     pub entry: GuestAddress,
94 }
95 
96 /// Loads a kernel from a 32-bit ELF image into memory.
97 ///
98 /// The ELF file will be loaded at the physical address specified by the `p_paddr` fields of its
99 /// program headers.
100 ///
101 /// # Arguments
102 ///
103 /// * `guest_mem` - The guest memory region the kernel is written to.
104 /// * `kernel_start` - The minimum guest address to allow when loading program headers.
105 /// * `kernel_image` - Input vmlinux image.
106 /// * `phys_offset` - An offset in bytes to add to each physical address (`p_paddr`).
load_elf32<F>( guest_mem: &GuestMemory, kernel_start: GuestAddress, kernel_image: &mut F, phys_offset: u64, ) -> Result<LoadedKernel> where F: Read + Seek + AsRawDescriptor,107 pub fn load_elf32<F>(
108     guest_mem: &GuestMemory,
109     kernel_start: GuestAddress,
110     kernel_image: &mut F,
111     phys_offset: u64,
112 ) -> Result<LoadedKernel>
113 where
114     F: Read + Seek + AsRawDescriptor,
115 {
116     load_elf_for_class(
117         guest_mem,
118         kernel_start,
119         kernel_image,
120         phys_offset,
121         Some(elf::ELFCLASS32),
122     )
123 }
124 
125 /// Loads a kernel from a 64-bit ELF image into memory.
126 ///
127 /// The ELF file will be loaded at the physical address specified by the `p_paddr` fields of its
128 /// program headers.
129 ///
130 /// # Arguments
131 ///
132 /// * `guest_mem` - The guest memory region the kernel is written to.
133 /// * `kernel_start` - The minimum guest address to allow when loading program headers.
134 /// * `kernel_image` - Input vmlinux image.
135 /// * `phys_offset` - An offset in bytes to add to each physical address (`p_paddr`).
load_elf64<F>( guest_mem: &GuestMemory, kernel_start: GuestAddress, kernel_image: &mut F, phys_offset: u64, ) -> Result<LoadedKernel> where F: Read + Seek + AsRawDescriptor,136 pub fn load_elf64<F>(
137     guest_mem: &GuestMemory,
138     kernel_start: GuestAddress,
139     kernel_image: &mut F,
140     phys_offset: u64,
141 ) -> Result<LoadedKernel>
142 where
143     F: Read + Seek + AsRawDescriptor,
144 {
145     load_elf_for_class(
146         guest_mem,
147         kernel_start,
148         kernel_image,
149         phys_offset,
150         Some(elf::ELFCLASS64),
151     )
152 }
153 
154 /// Loads a kernel from a 32-bit or 64-bit ELF image into memory.
155 ///
156 /// The ELF file will be loaded at the physical address specified by the `p_paddr` fields of its
157 /// program headers.
158 ///
159 /// # Arguments
160 ///
161 /// * `guest_mem` - The guest memory region the kernel is written to.
162 /// * `kernel_start` - The minimum guest address to allow when loading program headers.
163 /// * `kernel_image` - Input vmlinux image.
164 /// * `phys_offset` - An offset in bytes to add to each physical address (`p_paddr`).
load_elf<F>( guest_mem: &GuestMemory, kernel_start: GuestAddress, kernel_image: &mut F, phys_offset: u64, ) -> Result<LoadedKernel> where F: Read + Seek + AsRawDescriptor,165 pub fn load_elf<F>(
166     guest_mem: &GuestMemory,
167     kernel_start: GuestAddress,
168     kernel_image: &mut F,
169     phys_offset: u64,
170 ) -> Result<LoadedKernel>
171 where
172     F: Read + Seek + AsRawDescriptor,
173 {
174     load_elf_for_class(guest_mem, kernel_start, kernel_image, phys_offset, None)
175 }
176 
load_elf_for_class<F>( guest_mem: &GuestMemory, kernel_start: GuestAddress, kernel_image: &mut F, phys_offset: u64, ei_class: Option<u32>, ) -> Result<LoadedKernel> where F: Read + Seek + AsRawDescriptor,177 fn load_elf_for_class<F>(
178     guest_mem: &GuestMemory,
179     kernel_start: GuestAddress,
180     kernel_image: &mut F,
181     phys_offset: u64,
182     ei_class: Option<u32>,
183 ) -> Result<LoadedKernel>
184 where
185     F: Read + Seek + AsRawDescriptor,
186 {
187     let elf = read_elf(kernel_image, ei_class)?;
188     let mut start = None;
189     let mut end = 0;
190 
191     // Read in each section pointed to by the program headers.
192     for phdr in &elf.program_headers {
193         if phdr.p_type != elf::PT_LOAD {
194             continue;
195         }
196 
197         let paddr = phdr
198             .p_paddr
199             .checked_add(phys_offset)
200             .ok_or(Error::ProgramHeaderAddressOutOfRange)?;
201 
202         if paddr < kernel_start.offset() {
203             return Err(Error::ProgramHeaderAddressOutOfRange);
204         }
205 
206         if start.is_none() {
207             start = Some(paddr);
208         }
209 
210         end = paddr
211             .checked_add(phdr.p_memsz)
212             .ok_or(Error::InvalidProgramHeaderMemSize)?;
213 
214         if phdr.p_filesz == 0 {
215             continue;
216         }
217 
218         kernel_image
219             .seek(SeekFrom::Start(phdr.p_offset))
220             .map_err(|_| Error::SeekKernelStart)?;
221 
222         guest_mem
223             .read_to_memory(GuestAddress(paddr), kernel_image, phdr.p_filesz as usize)
224             .map_err(|_| Error::ReadKernelImage)?;
225     }
226 
227     // We should have found at least one PT_LOAD program header. If not, `start` will not be set.
228     let start = start.ok_or(Error::NoLoadableProgramHeaders)?;
229 
230     let size = end
231         .checked_sub(start)
232         .ok_or(Error::InvalidProgramHeaderSize)?;
233 
234     let address_range = AddressRange { start, end };
235 
236     // The entry point address must fall within one of the loaded sections.
237     // We approximate this by checking whether it within the bounds of the first and last sections.
238     let entry = elf
239         .file_header
240         .e_entry
241         .checked_add(phys_offset)
242         .ok_or(Error::InvalidEntryPoint)?;
243     if !address_range.contains(entry) {
244         return Err(Error::InvalidEntryPoint);
245     }
246 
247     Ok(LoadedKernel {
248         address_range,
249         size,
250         entry: GuestAddress(entry),
251     })
252 }
253 
254 /// Writes the command line string to the given memory slice.
255 ///
256 /// # Arguments
257 ///
258 /// * `guest_mem` - A u8 slice that will be partially overwritten by the command line.
259 /// * `guest_addr` - The address in `guest_mem` at which to load the command line.
260 /// * `cmdline` - The kernel command line.
load_cmdline( guest_mem: &GuestMemory, guest_addr: GuestAddress, cmdline: &CStr, ) -> Result<()>261 pub fn load_cmdline(
262     guest_mem: &GuestMemory,
263     guest_addr: GuestAddress,
264     cmdline: &CStr,
265 ) -> Result<()> {
266     let len = cmdline.to_bytes().len();
267     if len == 0 {
268         return Ok(());
269     }
270 
271     let end = guest_addr
272         .checked_add(len as u64 + 1)
273         .ok_or(Error::CommandLineOverflow)?; // Extra for null termination.
274     if end > guest_mem.end_addr() {
275         return Err(Error::CommandLineOverflow);
276     }
277 
278     guest_mem
279         .write_at_addr(cmdline.to_bytes_with_nul(), guest_addr)
280         .map_err(|_| Error::CommandLineCopy)?;
281 
282     Ok(())
283 }
284 
285 struct Elf64 {
286     file_header: elf::Elf64_Ehdr,
287     program_headers: Vec<elf::Elf64_Phdr>,
288 }
289 
290 /// Reads the headers of an ELF32 or ELF64 object file.  Returns ELF file and program headers,
291 /// converted to ELF64 format.  If `required_ei_class` is Some and the file's ELF ei_class doesn't
292 /// match, an Err is returned.
read_elf<F>(file: &mut F, required_ei_class: Option<u32>) -> Result<Elf64> where F: Read + Seek + AsRawDescriptor,293 fn read_elf<F>(file: &mut F, required_ei_class: Option<u32>) -> Result<Elf64>
294 where
295     F: Read + Seek + AsRawDescriptor,
296 {
297     // Read the ELF identification (e_ident) block.
298     file.seek(SeekFrom::Start(0))
299         .map_err(|_| Error::SeekKernelStart)?;
300     let mut ident = [0u8; 16];
301     file.read_exact(&mut ident).map_err(|_| Error::ReadHeader)?;
302 
303     // e_ident checks
304     if ident[elf::EI_MAG0 as usize] != elf::ELFMAG0 as u8
305         || ident[elf::EI_MAG1 as usize] != elf::ELFMAG1
306         || ident[elf::EI_MAG2 as usize] != elf::ELFMAG2
307         || ident[elf::EI_MAG3 as usize] != elf::ELFMAG3
308     {
309         return Err(Error::InvalidMagicNumber);
310     }
311     if ident[elf::EI_DATA as usize] != elf::ELFDATA2LSB as u8 {
312         return Err(Error::BigEndianOnLittle);
313     }
314     if ident[elf::EI_VERSION as usize] != elf::EV_CURRENT as u8 {
315         return Err(Error::InvalidElfVersion);
316     }
317 
318     let ei_class = ident[elf::EI_CLASS as usize] as u32;
319     if let Some(required_ei_class) = required_ei_class {
320         if ei_class != required_ei_class {
321             return Err(Error::InvalidElfClass);
322         }
323     }
324     match ei_class {
325         elf::ELFCLASS32 => read_elf_by_type::<_, elf::Elf32_Ehdr, elf::Elf32_Phdr>(file),
326         elf::ELFCLASS64 => read_elf_by_type::<_, elf::Elf64_Ehdr, elf::Elf64_Phdr>(file),
327         _ => Err(Error::InvalidElfClass),
328     }
329 }
330 
331 /// Reads the headers of an ELF32 or ELF64 object file.  Returns ELF file and program headers,
332 /// converted to ELF64 format.  `FileHeader` and `ProgramHeader` are the ELF32 or ELF64 ehdr/phdr
333 /// types to read from the file.  Caller should check that `file` is a valid ELF file before calling
334 /// this function.
read_elf_by_type<F, FileHeader, ProgramHeader>(mut file: &mut F) -> Result<Elf64> where F: Read + Seek + AsRawDescriptor, FileHeader: FromBytes + Default + Into<elf::Elf64_Ehdr>, ProgramHeader: FromBytes + Default + Into<elf::Elf64_Phdr>,335 fn read_elf_by_type<F, FileHeader, ProgramHeader>(mut file: &mut F) -> Result<Elf64>
336 where
337     F: Read + Seek + AsRawDescriptor,
338     FileHeader: FromBytes + Default + Into<elf::Elf64_Ehdr>,
339     ProgramHeader: FromBytes + Default + Into<elf::Elf64_Phdr>,
340 {
341     file.seek(SeekFrom::Start(0))
342         .map_err(|_| Error::SeekKernelStart)?;
343     let ehdr: FileHeader = zerocopy_from_reader(&mut file).map_err(|_| Error::ReadHeader)?;
344     let ehdr: elf::Elf64_Ehdr = ehdr.into();
345 
346     if ehdr.e_phentsize as usize != mem::size_of::<ProgramHeader>() {
347         return Err(Error::InvalidProgramHeaderSize);
348     }
349     if (ehdr.e_phoff as usize) < mem::size_of::<FileHeader>() {
350         // If the program header is backwards, bail.
351         return Err(Error::InvalidProgramHeaderOffset);
352     }
353 
354     file.seek(SeekFrom::Start(ehdr.e_phoff as u64))
355         .map_err(|_| Error::SeekProgramHeader)?;
356     let phdrs: Vec<ProgramHeader> = (0..ehdr.e_phnum)
357         .enumerate()
358         .map(|_| zerocopy_from_reader(&mut file).map_err(|_| Error::ReadProgramHeader))
359         .collect::<Result<Vec<ProgramHeader>>>()?;
360 
361     Ok(Elf64 {
362         file_header: ehdr,
363         program_headers: phdrs.into_iter().map(|ph| ph.into()).collect(),
364     })
365 }
366 
367 impl From<elf::Elf32_Ehdr> for elf::Elf64_Ehdr {
from(ehdr32: elf::Elf32_Ehdr) -> Self368     fn from(ehdr32: elf::Elf32_Ehdr) -> Self {
369         elf::Elf64_Ehdr {
370             e_ident: ehdr32.e_ident,
371             e_type: ehdr32.e_type as elf::Elf64_Half,
372             e_machine: ehdr32.e_machine as elf::Elf64_Half,
373             e_version: ehdr32.e_version as elf::Elf64_Word,
374             e_entry: ehdr32.e_entry as elf::Elf64_Addr,
375             e_phoff: ehdr32.e_phoff as elf::Elf64_Off,
376             e_shoff: ehdr32.e_shoff as elf::Elf64_Off,
377             e_flags: ehdr32.e_flags as elf::Elf64_Word,
378             e_ehsize: ehdr32.e_ehsize as elf::Elf64_Half,
379             e_phentsize: ehdr32.e_phentsize as elf::Elf64_Half,
380             e_phnum: ehdr32.e_phnum as elf::Elf64_Half,
381             e_shentsize: ehdr32.e_shentsize as elf::Elf64_Half,
382             e_shnum: ehdr32.e_shnum as elf::Elf64_Half,
383             e_shstrndx: ehdr32.e_shstrndx as elf::Elf64_Half,
384         }
385     }
386 }
387 
388 impl From<elf::Elf32_Phdr> for elf::Elf64_Phdr {
from(phdr32: elf::Elf32_Phdr) -> Self389     fn from(phdr32: elf::Elf32_Phdr) -> Self {
390         elf::Elf64_Phdr {
391             p_type: phdr32.p_type as elf::Elf64_Word,
392             p_flags: phdr32.p_flags as elf::Elf64_Word,
393             p_offset: phdr32.p_offset as elf::Elf64_Off,
394             p_vaddr: phdr32.p_vaddr as elf::Elf64_Addr,
395             p_paddr: phdr32.p_paddr as elf::Elf64_Addr,
396             p_filesz: phdr32.p_filesz as elf::Elf64_Xword,
397             p_memsz: phdr32.p_memsz as elf::Elf64_Xword,
398             p_align: phdr32.p_align as elf::Elf64_Xword,
399         }
400     }
401 }
402 
403 #[cfg(test)]
404 mod test {
405     use std::fs::File;
406     use std::io::Write;
407 
408     use tempfile::tempfile;
409     use vm_memory::GuestAddress;
410     use vm_memory::GuestMemory;
411 
412     use super::*;
413 
414     const MEM_SIZE: u64 = 0x40_0000;
415 
create_guest_mem() -> GuestMemory416     fn create_guest_mem() -> GuestMemory {
417         GuestMemory::new(&[(GuestAddress(0x0), MEM_SIZE)]).unwrap()
418     }
419 
420     #[test]
cmdline_overflow()421     fn cmdline_overflow() {
422         let gm = create_guest_mem();
423         let cmdline_address = GuestAddress(MEM_SIZE - 5);
424         assert_eq!(
425             Err(Error::CommandLineOverflow),
426             load_cmdline(
427                 &gm,
428                 cmdline_address,
429                 CStr::from_bytes_with_nul(b"12345\0").unwrap()
430             )
431         );
432     }
433 
434     #[test]
cmdline_write_end()435     fn cmdline_write_end() {
436         let gm = create_guest_mem();
437         let mut cmdline_address = GuestAddress(45);
438         assert_eq!(
439             Ok(()),
440             load_cmdline(
441                 &gm,
442                 cmdline_address,
443                 CStr::from_bytes_with_nul(b"1234\0").unwrap()
444             )
445         );
446         let val: u8 = gm.read_obj_from_addr(cmdline_address).unwrap();
447         assert_eq!(val, b'1');
448         cmdline_address = cmdline_address.unchecked_add(1);
449         let val: u8 = gm.read_obj_from_addr(cmdline_address).unwrap();
450         assert_eq!(val, b'2');
451         cmdline_address = cmdline_address.unchecked_add(1);
452         let val: u8 = gm.read_obj_from_addr(cmdline_address).unwrap();
453         assert_eq!(val, b'3');
454         cmdline_address = cmdline_address.unchecked_add(1);
455         let val: u8 = gm.read_obj_from_addr(cmdline_address).unwrap();
456         assert_eq!(val, b'4');
457         cmdline_address = cmdline_address.unchecked_add(1);
458         let val: u8 = gm.read_obj_from_addr(cmdline_address).unwrap();
459         assert_eq!(val, b'\0');
460     }
461 
462     // Elf32 image that prints hello world on x86.
make_elf32_bin() -> File463     fn make_elf32_bin() -> File {
464         // test_elf32.bin built on Linux with gcc -m32 -static-pie
465         let bytes = include_bytes!("test_elf32.bin");
466         make_elf_bin(bytes)
467     }
468 
469     // Elf64 image that prints hello world on x86_64.
make_elf64_bin() -> File470     fn make_elf64_bin() -> File {
471         let bytes = include_bytes!("test_elf64.bin");
472         make_elf_bin(bytes)
473     }
474 
make_elf_bin(elf_bytes: &[u8]) -> File475     fn make_elf_bin(elf_bytes: &[u8]) -> File {
476         let mut file = tempfile().expect("failed to create tempfile");
477         file.write_all(elf_bytes)
478             .expect("failed to write elf to shared memory");
479         file
480     }
481 
mutate_elf_bin(mut f: &File, offset: u64, val: u8)482     fn mutate_elf_bin(mut f: &File, offset: u64, val: u8) {
483         f.seek(SeekFrom::Start(offset))
484             .expect("failed to seek file");
485         f.write_all(&[val])
486             .expect("failed to write mutated value to file");
487     }
488 
489     #[test]
load_elf32()490     fn load_elf32() {
491         let gm = create_guest_mem();
492         let kernel_addr = GuestAddress(0x0);
493         let mut image = make_elf32_bin();
494         let kernel = load_elf(&gm, kernel_addr, &mut image, 0).unwrap();
495         assert_eq!(kernel.address_range.start, 0);
496         assert_eq!(kernel.address_range.end, 0xa_2038);
497         assert_eq!(kernel.size, 0xa_2038);
498         assert_eq!(kernel.entry, GuestAddress(0x3dc0));
499     }
500 
501     #[test]
load_elf64()502     fn load_elf64() {
503         let gm = create_guest_mem();
504         let kernel_addr = GuestAddress(0x0);
505         let mut image = make_elf64_bin();
506         let kernel = load_elf(&gm, kernel_addr, &mut image, 0).expect("failed to load ELF");
507         assert_eq!(kernel.address_range.start, 0x20_0000);
508         assert_eq!(kernel.address_range.end, 0x20_0035);
509         assert_eq!(kernel.size, 0x35);
510         assert_eq!(kernel.entry, GuestAddress(0x20_000e));
511     }
512 
513     #[test]
bad_magic()514     fn bad_magic() {
515         let gm = create_guest_mem();
516         let kernel_addr = GuestAddress(0x0);
517         let mut bad_image = make_elf64_bin();
518         mutate_elf_bin(&bad_image, 0x1, 0x33);
519         assert_eq!(
520             Err(Error::InvalidMagicNumber),
521             load_elf(&gm, kernel_addr, &mut bad_image, 0)
522         );
523     }
524 
525     #[test]
bad_endian()526     fn bad_endian() {
527         // Only little endian is supported
528         let gm = create_guest_mem();
529         let kernel_addr = GuestAddress(0x20_0000);
530         let mut bad_image = make_elf64_bin();
531         mutate_elf_bin(&bad_image, 0x5, 2);
532         assert_eq!(
533             Err(Error::BigEndianOnLittle),
534             load_elf(&gm, kernel_addr, &mut bad_image, 0)
535         );
536     }
537 
538     #[test]
bad_phoff()539     fn bad_phoff() {
540         // program header has to be past the end of the elf header
541         let gm = create_guest_mem();
542         let kernel_addr = GuestAddress(0x0);
543         let mut bad_image = make_elf64_bin();
544         mutate_elf_bin(&bad_image, 0x20, 0x10);
545         assert_eq!(
546             Err(Error::InvalidProgramHeaderOffset),
547             load_elf(&gm, kernel_addr, &mut bad_image, 0)
548         );
549     }
550 
551     #[test]
paddr_below_start()552     fn paddr_below_start() {
553         let gm = create_guest_mem();
554         // test_elf.bin loads a phdr at 0x20_0000, so this will fail due to an out-of-bounds address
555         let kernel_addr = GuestAddress(0x30_0000);
556         let mut image = make_elf64_bin();
557         let res = load_elf(&gm, kernel_addr, &mut image, 0);
558         assert_eq!(res, Err(Error::ProgramHeaderAddressOutOfRange));
559     }
560 }
561