mirror of https://github.com/m-labs/artiq.git
runtime: Validate ksupport ELF against hard-coded address ranges
This would have caught the reduction in header padding with LLD 14. In theory, we could just get rid of the hard-coded kernel CPU address ranges altogether and use ksupport.elf as the one source of truth; the code already exists in dyld. The actual base address of the file would still need to be forwarded to the kernel-side libunwind glue, though, as there doesn't seem to be a clean way to get the equivalent of KSUPPORT_HEADER_SIZE through the linker script. I have left this as-is with the hard-coded KERNELCPU_… constants for now.
This commit is contained in:
parent
8acfa82586
commit
efbae51f9d
|
@ -320,6 +320,7 @@ dependencies = [
|
||||||
"build_misoc",
|
"build_misoc",
|
||||||
"byteorder",
|
"byteorder",
|
||||||
"cslice",
|
"cslice",
|
||||||
|
"dyld",
|
||||||
"eh",
|
"eh",
|
||||||
"failure",
|
"failure",
|
||||||
"failure_derive",
|
"failure_derive",
|
||||||
|
|
|
@ -5,7 +5,7 @@ use elf::*;
|
||||||
|
|
||||||
pub mod elf;
|
pub mod elf;
|
||||||
|
|
||||||
fn read_unaligned<T: Copy>(data: &[u8], offset: usize) -> Result<T, ()> {
|
pub fn read_unaligned<T: Copy>(data: &[u8], offset: usize) -> Result<T, ()> {
|
||||||
if data.len() < offset + mem::size_of::<T>() {
|
if data.len() < offset + mem::size_of::<T>() {
|
||||||
Err(())
|
Err(())
|
||||||
} else {
|
} else {
|
||||||
|
@ -75,6 +75,25 @@ impl<'a> fmt::Display for Error<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn is_elf_for_current_arch(ehdr: &Elf32_Ehdr, e_type: u16) -> bool {
|
||||||
|
const IDENT: [u8; EI_NIDENT] = [
|
||||||
|
ELFMAG0, ELFMAG1, ELFMAG2, ELFMAG3,
|
||||||
|
ELFCLASS32, ELFDATA2LSB, EV_CURRENT, ELFOSABI_NONE,
|
||||||
|
/* ABI version */ 0, /* padding */ 0, 0, 0, 0, 0, 0, 0
|
||||||
|
];
|
||||||
|
if ehdr.e_ident != IDENT { return false; }
|
||||||
|
|
||||||
|
if ehdr.e_type != e_type { return false; }
|
||||||
|
|
||||||
|
#[cfg(target_arch = "riscv32")]
|
||||||
|
const ARCH: u16 = EM_RISCV;
|
||||||
|
#[cfg(not(target_arch = "riscv32"))]
|
||||||
|
const ARCH: u16 = EM_NONE;
|
||||||
|
if ehdr.e_machine != ARCH { return false; }
|
||||||
|
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
pub enum Arch {
|
pub enum Arch {
|
||||||
RiscV,
|
RiscV,
|
||||||
|
@ -268,25 +287,16 @@ impl<'a> Library<'a> {
|
||||||
let ehdr = read_unaligned::<Elf32_Ehdr>(data, 0)
|
let ehdr = read_unaligned::<Elf32_Ehdr>(data, 0)
|
||||||
.map_err(|()| "cannot read ELF header")?;
|
.map_err(|()| "cannot read ELF header")?;
|
||||||
|
|
||||||
const IDENT: [u8; EI_NIDENT] = [
|
if !is_elf_for_current_arch(&ehdr, ET_DYN) {
|
||||||
ELFMAG0, ELFMAG1, ELFMAG2, ELFMAG3,
|
return Err("not a shared library for current architecture")?
|
||||||
ELFCLASS32, ELFDATA2LSB, EV_CURRENT, ELFOSABI_NONE,
|
}
|
||||||
/* ABI version */ 0, /* padding */ 0, 0, 0, 0, 0, 0, 0
|
|
||||||
];
|
|
||||||
|
|
||||||
#[cfg(target_arch = "riscv32")]
|
|
||||||
const ARCH: u16 = EM_RISCV;
|
|
||||||
#[cfg(not(target_arch = "riscv32"))]
|
|
||||||
const ARCH: u16 = EM_NONE;
|
|
||||||
|
|
||||||
#[cfg(all(target_feature = "f", target_feature = "d"))]
|
#[cfg(all(target_feature = "f", target_feature = "d"))]
|
||||||
const FLAGS: u32 = EF_RISCV_FLOAT_ABI_DOUBLE;
|
const FLAGS: u32 = EF_RISCV_FLOAT_ABI_DOUBLE;
|
||||||
|
|
||||||
#[cfg(not(all(target_feature = "f", target_feature = "d")))]
|
#[cfg(not(all(target_feature = "f", target_feature = "d")))]
|
||||||
const FLAGS: u32 = EF_RISCV_FLOAT_ABI_SOFT;
|
const FLAGS: u32 = EF_RISCV_FLOAT_ABI_SOFT;
|
||||||
|
if ehdr.e_flags != FLAGS {
|
||||||
if ehdr.e_ident != IDENT || ehdr.e_type != ET_DYN || ehdr.e_machine != ARCH || ehdr.e_flags != FLAGS {
|
return Err("unexpected flags for shared library (wrong floating point ABI?)")?
|
||||||
return Err("not a shared library for current architecture")?
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut dyn_off = None;
|
let mut dyn_off = None;
|
||||||
|
|
|
@ -19,6 +19,7 @@ byteorder = { version = "1.0", default-features = false }
|
||||||
cslice = { version = "0.3" }
|
cslice = { version = "0.3" }
|
||||||
log = { version = "0.4", default-features = false }
|
log = { version = "0.4", default-features = false }
|
||||||
managed = { version = "^0.7.1", default-features = false, features = ["alloc", "map"] }
|
managed = { version = "^0.7.1", default-features = false, features = ["alloc", "map"] }
|
||||||
|
dyld = { path = "../libdyld" }
|
||||||
eh = { path = "../libeh" }
|
eh = { path = "../libeh" }
|
||||||
unwind_backtrace = { path = "../libunwind_backtrace" }
|
unwind_backtrace = { path = "../libunwind_backtrace" }
|
||||||
io = { path = "../libio", features = ["byteorder"] }
|
io = { path = "../libio", features = ["byteorder"] }
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use core::ptr;
|
|
||||||
use board_misoc::csr;
|
use board_misoc::csr;
|
||||||
|
use core::{ptr, slice};
|
||||||
use mailbox;
|
use mailbox;
|
||||||
use rpc_queue;
|
use rpc_queue;
|
||||||
|
|
||||||
|
@ -13,15 +13,20 @@ pub unsafe fn start() {
|
||||||
|
|
||||||
stop();
|
stop();
|
||||||
|
|
||||||
extern {
|
extern "C" {
|
||||||
static _binary____ksupport_ksupport_elf_start: u8;
|
static _binary____ksupport_ksupport_elf_start: u8;
|
||||||
static _binary____ksupport_ksupport_elf_end: u8;
|
static _binary____ksupport_ksupport_elf_end: u8;
|
||||||
}
|
}
|
||||||
let ksupport_start = &_binary____ksupport_ksupport_elf_start as *const _;
|
let ksupport_elf_start = &_binary____ksupport_ksupport_elf_start as *const u8;
|
||||||
let ksupport_end = &_binary____ksupport_ksupport_elf_end as *const _;
|
let ksupport_elf_end = &_binary____ksupport_ksupport_elf_end as *const u8;
|
||||||
ptr::copy_nonoverlapping(ksupport_start,
|
let ksupport_elf = slice::from_raw_parts(
|
||||||
(KERNELCPU_EXEC_ADDRESS - KSUPPORT_HEADER_SIZE) as *mut u8,
|
ksupport_elf_start,
|
||||||
ksupport_end as usize - ksupport_start as usize);
|
ksupport_elf_end as usize - ksupport_elf_start as usize,
|
||||||
|
);
|
||||||
|
|
||||||
|
if let Err(msg) = load_image(&ksupport_elf) {
|
||||||
|
panic!("failed to load kernel CPU image (ksupport.elf): {}", msg);
|
||||||
|
}
|
||||||
|
|
||||||
csr::kernel_cpu::reset_write(0);
|
csr::kernel_cpu::reset_write(0);
|
||||||
|
|
||||||
|
@ -41,6 +46,44 @@ pub unsafe fn stop() {
|
||||||
rpc_queue::init();
|
rpc_queue::init();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Loads the given image for execution on the kernel CPU.
|
||||||
|
///
|
||||||
|
/// The entire image including the headers is copied into memory for later use by libunwind, but
|
||||||
|
/// placed such that the text section ends up at the right location in memory. Currently, we just
|
||||||
|
/// hard-code the address range, but at least verify that this matches the ELF program header given
|
||||||
|
/// in the image (avoids loading the – non-relocatable – code at the wrong address on toolchain/…
|
||||||
|
/// changes).
|
||||||
|
unsafe fn load_image(image: &[u8]) -> Result<(), &'static str> {
|
||||||
|
use dyld::elf::*;
|
||||||
|
use dyld::{is_elf_for_current_arch, read_unaligned};
|
||||||
|
|
||||||
|
let ehdr = read_unaligned::<Elf32_Ehdr>(image, 0).map_err(|()| "could not read ELF header")?;
|
||||||
|
|
||||||
|
// The check assumes the two CPUs share the same architecture. This is just to avoid inscrutable
|
||||||
|
// errors; we do not functionally rely on this.
|
||||||
|
if !is_elf_for_current_arch(&ehdr, ET_EXEC) {
|
||||||
|
return Err("not an executable for kernel CPU architecture");
|
||||||
|
}
|
||||||
|
|
||||||
|
// First program header should be the main text/… LOAD (see ksupport.ld).
|
||||||
|
let phdr = read_unaligned::<Elf32_Phdr>(image, ehdr.e_phoff as usize)
|
||||||
|
.map_err(|()| "could not read program header")?;
|
||||||
|
if phdr.p_type != PT_LOAD {
|
||||||
|
return Err("unexpected program header type");
|
||||||
|
}
|
||||||
|
if phdr.p_vaddr + phdr.p_memsz > KERNELCPU_LAST_ADDRESS as u32 {
|
||||||
|
// This is a weak sanity check only; we also need to fit in the stack, etc.
|
||||||
|
return Err("too large for kernel CPU address range");
|
||||||
|
}
|
||||||
|
const TARGET_ADDRESS: u32 = (KERNELCPU_EXEC_ADDRESS - KSUPPORT_HEADER_SIZE) as _;
|
||||||
|
if phdr.p_vaddr - phdr.p_offset != TARGET_ADDRESS {
|
||||||
|
return Err("unexpected load address/offset");
|
||||||
|
}
|
||||||
|
|
||||||
|
ptr::copy_nonoverlapping(image.as_ptr(), TARGET_ADDRESS as *mut u8, image.len());
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
pub fn validate(ptr: usize) -> bool {
|
pub fn validate(ptr: usize) -> bool {
|
||||||
ptr >= KERNELCPU_EXEC_ADDRESS && ptr <= KERNELCPU_LAST_ADDRESS
|
ptr >= KERNELCPU_EXEC_ADDRESS && ptr <= KERNELCPU_LAST_ADDRESS
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
#![feature(lang_items, panic_info_message, const_btree_new, iter_advance_by)]
|
#![feature(lang_items, panic_info_message, const_btree_new, iter_advance_by)]
|
||||||
#![no_std]
|
#![no_std]
|
||||||
|
|
||||||
|
extern crate dyld;
|
||||||
extern crate eh;
|
extern crate eh;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate alloc;
|
extern crate alloc;
|
||||||
|
|
Loading…
Reference in New Issue