Skip to content

Commit

Permalink
Fixes incorrect kernel mapping (#926)
Browse files Browse the repository at this point in the history
  • Loading branch information
ultimaweapon authored Aug 12, 2024
1 parent 979dd51 commit 7f04eee
Show file tree
Hide file tree
Showing 3 changed files with 44 additions and 74 deletions.
52 changes: 7 additions & 45 deletions src/core/src/vmm/hw/ram/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,16 +74,15 @@ impl RamBuilder {

/// # Panics
/// If called a second time.
pub fn alloc_kernel(&mut self) -> KernelBuilder {
let start = self.next;

pub fn alloc_kernel(&mut self, len: usize) -> Result<&mut [u8], RamError> {
assert!(self.kern.is_none());

KernelBuilder {
rb: self,
start,
len: 0,
}
let mem = unsafe { self.ram.alloc(self.next, len)? };

self.kern = Some(self.next..(self.next + len));
self.next += len;

Ok(mem)
}

/// # Panics
Expand Down Expand Up @@ -383,43 +382,6 @@ impl RamBuilder {
}
}

/// Struct to allocate RAM to map the kernel.
pub struct KernelBuilder<'a> {
rb: &'a mut RamBuilder,
start: usize,
len: usize,
}

impl<'a> KernelBuilder<'a> {
pub fn alloc_segment(&mut self, addr: usize, mut len: usize) -> Result<&mut [u8], RamError> {
// Check if addr valid.
if (addr % Ram::VM_PAGE_SIZE) != 0 {
return Err(RamError::UnalignedAddr);
} else if addr < self.len {
return Err(RamError::OverlappedAddr);
}

len = len
.checked_next_multiple_of(Ram::VM_PAGE_SIZE)
.ok_or(RamError::InvalidLen)?;

// Allocate.
let off = self.start.checked_add(addr).ok_or(RamError::InvalidAddr)?;
let seg = unsafe { self.rb.ram.alloc(off, len)? };

self.len = addr + len;

Ok(seg)
}
}

impl<'a> Drop for KernelBuilder<'a> {
fn drop(&mut self) {
self.rb.kern = Some(self.start..(self.start + self.len));
self.rb.next += self.len;
}
}

/// Contains information how kernel arguments was allocated.
pub struct KernelArgs {
ram: Range<usize>,
Expand Down
9 changes: 0 additions & 9 deletions src/core/src/vmm/hw/ram/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -160,18 +160,9 @@ unsafe impl Sync for Ram {}
/// Represents an error when an operation on [`Ram`] fails.
#[derive(Debug, Error)]
pub enum RamError {
#[error("unaligned address")]
UnalignedAddr,

#[error("overlapped address")]
OverlappedAddr,

#[error("invalid address")]
InvalidAddr,

#[error("invalid length")]
InvalidLen,

#[error("host failed")]
HostFailed(#[source] std::io::Error),
}
57 changes: 37 additions & 20 deletions src/core/src/vmm/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ pub unsafe extern "C" fn vmm_run(
}

// Parse program headers.
let mut loads = Vec::with_capacity(e_phnum);
let mut segments = Vec::with_capacity(e_phnum);
let mut dynamic = None;

for (index, data) in data.chunks_exact(e_phentsize).enumerate() {
Expand All @@ -147,7 +147,7 @@ pub unsafe extern "C" fn vmm_run(
return null_mut();
}

loads.push((p_offset, p_filesz, p_vaddr, p_memsz));
segments.push((p_offset, p_filesz, p_vaddr, p_memsz));
}
2 => {
if dynamic.is_some() {
Expand All @@ -165,10 +165,10 @@ pub unsafe extern "C" fn vmm_run(
}
}

loads.sort_unstable_by_key(|i| i.2);
segments.sort_unstable_by_key(|i| i.2);

// Make sure the first PT_LOAD includes the ELF header.
match loads.first() {
match segments.first() {
Some(&(p_offset, _, _, _)) => {
if p_offset != 0 {
*err = RustError::new("the first PT_LOAD does not includes ELF header");
Expand All @@ -181,6 +181,29 @@ pub unsafe extern "C" fn vmm_run(
}
}

// Get kernel memory size.
let mut len = 0;

for &(_, _, p_vaddr, p_memsz) in &segments {
if p_vaddr < len {
*err = RustError::new(format!(
"PT_LOAD at {p_vaddr:#x} is overlapped with the previous PT_LOAD"
));
return null_mut();
}

len = match p_vaddr
.checked_add(p_memsz)
.and_then(|end| end.checked_next_multiple_of(Ram::VM_PAGE_SIZE))
{
Some(v) => v,
None => {
*err = RustError::new(format!("invalid p_memsz on PT_LOAD at {p_vaddr:#x}"));
return null_mut();
}
};
}

// Setup RAM builder.
let mut ram = match RamBuilder::new() {
Ok(v) => v,
Expand All @@ -191,21 +214,15 @@ pub unsafe extern "C" fn vmm_run(
};

// Map the kernel.
let mut alloc = ram.alloc_kernel();

for &(p_offset, p_filesz, p_vaddr, p_memsz) in &loads {
// Allocate RAM for segment.
let seg = match alloc.alloc_segment(p_vaddr, p_memsz) {
Ok(v) => v,
Err(e) => {
*err = RustError::with_source(
format!("couldn't allocate RAM for a segment at {p_vaddr:#x}"),
e,
);
return null_mut();
}
};
let kern = match ram.alloc_kernel(len) {
Ok(v) => v,
Err(e) => {
*err = RustError::with_source("couldn't allocate RAM for the kernel", e);
return null_mut();
}
};

for &(p_offset, p_filesz, p_vaddr, p_memsz) in &segments {
// Seek to segment data.
match file.seek(SeekFrom::Start(p_offset)) {
Ok(v) => {
Expand All @@ -221,14 +238,14 @@ pub unsafe extern "C" fn vmm_run(
}

// Read segment data.
let seg = &mut kern[p_vaddr..(p_vaddr + p_memsz)];

if let Err(e) = file.read_exact(&mut seg[..p_filesz]) {
*err = RustError::with_source(format!("couldn't read kernet at offset {p_offset}"), e);
return null_mut();
}
}

drop(alloc);

// Allocate stack.
if let Err(e) = ram.alloc_stack(1024 * 1024 * 2) {
*err = RustError::with_source("couldn't allocate RAM for stack", e);
Expand Down

0 comments on commit 7f04eee

Please sign in to comment.