mirror of
https://chromium.googlesource.com/crosvm/crosvm
synced 2025-02-09 03:57:24 +00:00
kernel_loader: accept physical address offset
This interprets the p_paddr field of ELF program headers as an offset into physical RAM on aarch64 systems, which is a change in behavior. We pass an offset of 0 on x86-64, so it makes no difference there. BUG=b:254601048 BUG=b:255697205 TEST=cargo test -p kernel_loader Change-Id: I9ebaa285c4cde1f70cb7752e91ff4520e06dc82f Reviewed-on: https://chromium-review.googlesource.com/c/crosvm/crosvm/+/4035738 Reviewed-by: Alexandre Courbot <acourbot@chromium.org> Commit-Queue: Daniel Verkamp <dverkamp@chromium.org>
This commit is contained in:
parent
94f130c23d
commit
f50029e071
4 changed files with 54 additions and 28 deletions
|
@ -343,9 +343,12 @@ impl arch::LinuxArch for AArch64 {
|
|||
}
|
||||
}
|
||||
VmImage::Kernel(ref mut kernel_image) => {
|
||||
let loaded_kernel = if let Ok(elf_kernel) =
|
||||
kernel_loader::load_elf64(&mem, get_kernel_addr(), kernel_image)
|
||||
{
|
||||
let loaded_kernel = if let Ok(elf_kernel) = kernel_loader::load_elf64(
|
||||
&mem,
|
||||
get_kernel_addr(),
|
||||
kernel_image,
|
||||
AARCH64_PHYS_MEM_START,
|
||||
) {
|
||||
elf_kernel
|
||||
} else {
|
||||
kernel_loader::load_arm64_kernel(&mem, get_kernel_addr(), kernel_image)
|
||||
|
|
|
@ -24,6 +24,6 @@ fn make_elf_bin(elf_bytes: &[u8]) -> File {
|
|||
fuzz_target!(|bytes| {
|
||||
let mut kimage = make_elf_bin(bytes);
|
||||
let mem = GuestMemory::new(&[(GuestAddress(0), MEM_SIZE)]).unwrap();
|
||||
let _ = kernel_loader::load_elf32(&mem, GuestAddress(0), &mut kimage);
|
||||
let _ = kernel_loader::load_elf64(&mem, GuestAddress(0), &mut kimage);
|
||||
let _ = kernel_loader::load_elf32(&mem, GuestAddress(0), &mut kimage, 0);
|
||||
let _ = kernel_loader::load_elf64(&mem, GuestAddress(0), &mut kimage, 0);
|
||||
});
|
||||
|
|
|
@ -114,15 +114,23 @@ pub struct LoadedKernel {
|
|||
/// * `guest_mem` - The guest memory region the kernel is written to.
|
||||
/// * `kernel_start` - The minimum guest address to allow when loading program headers.
|
||||
/// * `kernel_image` - Input vmlinux image.
|
||||
/// * `phys_offset` - An offset in bytes to add to each physical address (`p_paddr`).
|
||||
pub fn load_elf32<F>(
|
||||
guest_mem: &GuestMemory,
|
||||
kernel_start: GuestAddress,
|
||||
kernel_image: &mut F,
|
||||
phys_offset: u64,
|
||||
) -> Result<LoadedKernel>
|
||||
where
|
||||
F: Read + Seek + AsRawDescriptor,
|
||||
{
|
||||
load_elf_for_class(guest_mem, kernel_start, kernel_image, Some(elf::ELFCLASS32))
|
||||
load_elf_for_class(
|
||||
guest_mem,
|
||||
kernel_start,
|
||||
kernel_image,
|
||||
phys_offset,
|
||||
Some(elf::ELFCLASS32),
|
||||
)
|
||||
}
|
||||
|
||||
/// Loads a kernel from a 64-bit ELF image into memory.
|
||||
|
@ -135,15 +143,23 @@ where
|
|||
/// * `guest_mem` - The guest memory region the kernel is written to.
|
||||
/// * `kernel_start` - The minimum guest address to allow when loading program headers.
|
||||
/// * `kernel_image` - Input vmlinux image.
|
||||
/// * `phys_offset` - An offset in bytes to add to each physical address (`p_paddr`).
|
||||
pub fn load_elf64<F>(
|
||||
guest_mem: &GuestMemory,
|
||||
kernel_start: GuestAddress,
|
||||
kernel_image: &mut F,
|
||||
phys_offset: u64,
|
||||
) -> Result<LoadedKernel>
|
||||
where
|
||||
F: Read + Seek + AsRawDescriptor,
|
||||
{
|
||||
load_elf_for_class(guest_mem, kernel_start, kernel_image, Some(elf::ELFCLASS64))
|
||||
load_elf_for_class(
|
||||
guest_mem,
|
||||
kernel_start,
|
||||
kernel_image,
|
||||
phys_offset,
|
||||
Some(elf::ELFCLASS64),
|
||||
)
|
||||
}
|
||||
|
||||
/// Loads a kernel from a 32-bit or 64-bit ELF image into memory.
|
||||
|
@ -156,21 +172,24 @@ where
|
|||
/// * `guest_mem` - The guest memory region the kernel is written to.
|
||||
/// * `kernel_start` - The minimum guest address to allow when loading program headers.
|
||||
/// * `kernel_image` - Input vmlinux image.
|
||||
/// * `phys_offset` - An offset in bytes to add to each physical address (`p_paddr`).
|
||||
pub fn load_elf<F>(
|
||||
guest_mem: &GuestMemory,
|
||||
kernel_start: GuestAddress,
|
||||
kernel_image: &mut F,
|
||||
phys_offset: u64,
|
||||
) -> Result<LoadedKernel>
|
||||
where
|
||||
F: Read + Seek + AsRawDescriptor,
|
||||
{
|
||||
load_elf_for_class(guest_mem, kernel_start, kernel_image, None)
|
||||
load_elf_for_class(guest_mem, kernel_start, kernel_image, phys_offset, None)
|
||||
}
|
||||
|
||||
fn 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
|
||||
|
@ -186,16 +205,20 @@ where
|
|||
continue;
|
||||
}
|
||||
|
||||
if phdr.p_paddr < kernel_start.offset() {
|
||||
let paddr = phdr
|
||||
.p_paddr
|
||||
.checked_add(phys_offset)
|
||||
.ok_or(Error::ProgramHeaderAddressOutOfRange)?;
|
||||
|
||||
if paddr < kernel_start.offset() {
|
||||
return Err(Error::ProgramHeaderAddressOutOfRange);
|
||||
}
|
||||
|
||||
if start.is_none() {
|
||||
start = Some(phdr.p_paddr);
|
||||
start = Some(paddr);
|
||||
}
|
||||
|
||||
end = phdr
|
||||
.p_paddr
|
||||
end = paddr
|
||||
.checked_add(phdr.p_memsz)
|
||||
.ok_or(Error::InvalidProgramHeaderMemSize)?;
|
||||
|
||||
|
@ -208,11 +231,7 @@ where
|
|||
.map_err(|_| Error::SeekKernelStart)?;
|
||||
|
||||
guest_mem
|
||||
.read_to_memory(
|
||||
GuestAddress(phdr.p_paddr),
|
||||
kernel_image,
|
||||
phdr.p_filesz as usize,
|
||||
)
|
||||
.read_to_memory(GuestAddress(paddr), kernel_image, phdr.p_filesz as usize)
|
||||
.map_err(|_| Error::ReadKernelImage)?;
|
||||
}
|
||||
|
||||
|
@ -225,17 +244,21 @@ where
|
|||
|
||||
let address_range = AddressRange { start, end };
|
||||
|
||||
// `e_entry` of 0 means there is no entry point, which we do not want to allow.
|
||||
// The entry point address must also fall within one of the loaded sections.
|
||||
// The entry point address must fall within one of the loaded sections.
|
||||
// We approximate this by checking whether it within the bounds of the first and last sections.
|
||||
if elf.file_header.e_entry == 0 || !address_range.contains(elf.file_header.e_entry) {
|
||||
let entry = elf
|
||||
.file_header
|
||||
.e_entry
|
||||
.checked_add(phys_offset)
|
||||
.ok_or(Error::InvalidEntryPoint)?;
|
||||
if !address_range.contains(entry) {
|
||||
return Err(Error::InvalidEntryPoint);
|
||||
}
|
||||
|
||||
Ok(LoadedKernel {
|
||||
address_range,
|
||||
size,
|
||||
entry: GuestAddress(elf.file_header.e_entry),
|
||||
entry: GuestAddress(entry),
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -479,7 +502,7 @@ mod test {
|
|||
let gm = create_guest_mem();
|
||||
let kernel_addr = GuestAddress(0x0);
|
||||
let mut image = make_elf32_bin();
|
||||
let kernel = load_elf(&gm, kernel_addr, &mut image).unwrap();
|
||||
let kernel = load_elf(&gm, kernel_addr, &mut image, 0).unwrap();
|
||||
assert_eq!(kernel.address_range.start, 0);
|
||||
assert_eq!(kernel.address_range.end, 0xa_2038);
|
||||
assert_eq!(kernel.size, 0xa_2038);
|
||||
|
@ -491,7 +514,7 @@ mod test {
|
|||
let gm = create_guest_mem();
|
||||
let kernel_addr = GuestAddress(0x0);
|
||||
let mut image = make_elf64_bin();
|
||||
let kernel = load_elf(&gm, kernel_addr, &mut image).expect("failed to load ELF");
|
||||
let kernel = load_elf(&gm, kernel_addr, &mut image, 0).expect("failed to load ELF");
|
||||
assert_eq!(kernel.address_range.start, 0x20_0000);
|
||||
assert_eq!(kernel.address_range.end, 0x20_0035);
|
||||
assert_eq!(kernel.size, 0x35);
|
||||
|
@ -506,7 +529,7 @@ mod test {
|
|||
mutate_elf_bin(&bad_image, 0x1, 0x33);
|
||||
assert_eq!(
|
||||
Err(Error::InvalidMagicNumber),
|
||||
load_elf(&gm, kernel_addr, &mut bad_image)
|
||||
load_elf(&gm, kernel_addr, &mut bad_image, 0)
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -519,7 +542,7 @@ mod test {
|
|||
mutate_elf_bin(&bad_image, 0x5, 2);
|
||||
assert_eq!(
|
||||
Err(Error::BigEndianOnLittle),
|
||||
load_elf(&gm, kernel_addr, &mut bad_image)
|
||||
load_elf(&gm, kernel_addr, &mut bad_image, 0)
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -532,7 +555,7 @@ mod test {
|
|||
mutate_elf_bin(&bad_image, 0x20, 0x10);
|
||||
assert_eq!(
|
||||
Err(Error::InvalidProgramHeaderOffset),
|
||||
load_elf(&gm, kernel_addr, &mut bad_image)
|
||||
load_elf(&gm, kernel_addr, &mut bad_image, 0)
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -542,7 +565,7 @@ mod test {
|
|||
// test_elf.bin loads a phdr at 0x20_0000, so this will fail due to an out-of-bounds address
|
||||
let kernel_addr = GuestAddress(0x30_0000);
|
||||
let mut image = make_elf64_bin();
|
||||
let res = load_elf(&gm, kernel_addr, &mut image);
|
||||
let res = load_elf(&gm, kernel_addr, &mut image, 0);
|
||||
assert_eq!(res, Err(Error::ProgramHeaderAddressOutOfRange));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1413,7 +1413,7 @@ impl X8664arch {
|
|||
kernel_image: &mut File,
|
||||
) -> Result<(boot_params, u64, GuestAddress)> {
|
||||
let kernel_start = GuestAddress(KERNEL_START_OFFSET);
|
||||
match kernel_loader::load_elf64(mem, kernel_start, kernel_image) {
|
||||
match kernel_loader::load_elf64(mem, kernel_start, kernel_image, 0) {
|
||||
Ok(loaded_kernel) => {
|
||||
// ELF kernels don't contain a `boot_params` structure, so synthesize a default one.
|
||||
let boot_params = Default::default();
|
||||
|
|
Loading…
Reference in a new issue