From 4821357934e50f789ac2e22af5f45d95bca8f932 Mon Sep 17 00:00:00 2001 From: Jeremy Soller <jackpot51@gmail.com> Date: Sat, 17 Sep 2016 21:44:50 -0600 Subject: [PATCH] Complete execve - add argument support using safe ABI --- elf.rs | 81 +-------------------- syscall/process.rs | 173 ++++++++++++++++++++++++++++++++++++++------- 2 files changed, 148 insertions(+), 106 deletions(-) diff --git a/elf.rs b/elf.rs index 180ff23d..bf484fe9 100644 --- a/elf.rs +++ b/elf.rs @@ -5,17 +5,10 @@ use collections::String; use core::str; #[cfg(target_arch = "x86")] -use goblin::elf32::{header, program_header}; +pub use goblin::elf32::{header, program_header}; #[cfg(target_arch = "x86_64")] -use goblin::elf64::{header, program_header}; - -use arch; -use arch::externs::memcpy; -use arch::paging::{entry, VirtualAddress}; -use arch::start::usermode; -use context; -use syscall::{Error, Result as SysResult}; +pub use goblin::elf64::{header, program_header}; /// An ELF executable pub struct Elf<'a> { @@ -52,76 +45,6 @@ impl<'a> Elf<'a> { pub fn entry(&self) -> usize { self.header.e_entry as usize } - - /// Test function to run. Remove and replace with proper syscall - pub fn run(self) -> SysResult<!> { - { - let contexts = context::contexts(); - let context_lock = contexts.current().ok_or(Error::NoProcess)?; - let mut context = context_lock.write(); - - // Unmap previous image and stack - context.image.clear(); - drop(context.heap.take()); - drop(context.stack.take()); - - for segment in self.segments() { - if segment.p_type == program_header::PT_LOAD { - let mut memory = context::memory::Memory::new( - VirtualAddress::new(segment.p_vaddr as usize), - segment.p_memsz as usize, - entry::NO_EXECUTE | entry::WRITABLE, - true, - true - ); - - unsafe { - // Copy file data - memcpy(segment.p_vaddr as *mut u8, - (self.data.as_ptr() as usize + segment.p_offset as usize) as *const u8, - segment.p_filesz as usize); - } - - let mut flags = entry::NO_EXECUTE | entry::USER_ACCESSIBLE; - - if segment.p_flags & program_header::PF_R == program_header::PF_R { - flags.insert(entry::PRESENT); - } - - // W ^ X. If it is executable, do not allow it to be writable, even if requested - if segment.p_flags & program_header::PF_X == program_header::PF_X { - flags.remove(entry::NO_EXECUTE); - } else if segment.p_flags & program_header::PF_W == program_header::PF_W { - flags.insert(entry::WRITABLE); - } - - memory.remap(flags, true); - - context.image.push(memory.to_shared()); - } - } - - context.heap = Some(context::memory::Memory::new( - VirtualAddress::new(arch::USER_HEAP_OFFSET), - 0, - entry::NO_EXECUTE | entry::WRITABLE | entry::USER_ACCESSIBLE, - true, - true - ).to_shared()); - - // Map stack - context.stack = Some(context::memory::Memory::new( - VirtualAddress::new(arch::USER_STACK_OFFSET), - arch::USER_STACK_SIZE, - entry::NO_EXECUTE | entry::WRITABLE | entry::USER_ACCESSIBLE, - true, - true - )); - } - - // Go to usermode - unsafe { usermode(self.entry(), arch::USER_STACK_OFFSET + arch::USER_STACK_SIZE - 256); } - } } pub struct ElfSegments<'a> { diff --git a/syscall/process.rs b/syscall/process.rs index 182c5f97..383141cb 100644 --- a/syscall/process.rs +++ b/syscall/process.rs @@ -7,13 +7,15 @@ use core::str; use spin::Mutex; use arch; +use arch::externs::memcpy; use arch::memory::allocate_frame; use arch::paging::{ActivePageTable, InactivePageTable, Page, VirtualAddress, entry}; use arch::paging::temporary_page::TemporaryPage; +use arch::start::usermode; use context; -use elf; +use elf::{self, program_header}; use scheme; -use syscall::{self, Error, Result}; +use syscall::{self, Error, Result, validate_slice, validate_slice_mut}; pub fn brk(address: usize) -> Result<usize> { let contexts = context::contexts(); @@ -332,33 +334,147 @@ pub fn exit(status: usize) -> ! { unreachable!(); } -pub fn exec(path: &[u8], _args: &[[usize; 2]]) -> Result<usize> { - //TODO: Use args - //TODO: Unmap previous mappings - //TODO: Drop data vec +pub fn exec(path: &[u8], arg_ptrs: &[[usize; 2]]) -> Result<usize> { + let entry; + let mut sp = arch::USER_STACK_OFFSET + arch::USER_STACK_SIZE - 256; - let file = syscall::open(path, 0)?; - let mut data = vec![]; - loop { - let mut buf = [0; 4096]; - let count = syscall::read(file, &mut buf)?; - if count > 0 { - data.extend_from_slice(&buf[..count]); - } else { - break; + { + let mut args = Vec::new(); + for arg_ptr in arg_ptrs { + let arg = validate_slice(arg_ptr[0] as *const u8, arg_ptr[1])?; + args.push(arg.to_vec()); // Must be moved into kernel space before exec unmaps all memory } - } - let _ = syscall::close(file); - - match elf::Elf::from(&data) { - Ok(elf) => { - elf.run().and(Ok(0)) - }, - Err(err) => { - println!("failed to execute {}: {}", unsafe { str::from_utf8_unchecked(path) }, err); - Err(Error::NoExec) + + let file = syscall::open(path, 0)?; + //TODO: Only read elf header, not entire file. Then read required segments + let mut data = vec![]; + loop { + let mut buf = [0; 4096]; + let count = syscall::read(file, &mut buf)?; + if count > 0 { + data.extend_from_slice(&buf[..count]); + } else { + break; + } + } + let _ = syscall::close(file); + + match elf::Elf::from(&data) { + Ok(elf) => { + entry = elf.entry(); + + drop(path); // Drop so that usage is not allowed after unmapping context + drop(arg_ptrs); // Drop so that usage is not allowed after unmapping context + + let contexts = context::contexts(); + let context_lock = contexts.current().ok_or(Error::NoProcess)?; + let mut context = context_lock.write(); + + // Unmap previous image and stack + context.image.clear(); + drop(context.heap.take()); + drop(context.stack.take()); + + for segment in elf.segments() { + if segment.p_type == program_header::PT_LOAD { + let mut memory = context::memory::Memory::new( + VirtualAddress::new(segment.p_vaddr as usize), + segment.p_memsz as usize, + entry::NO_EXECUTE | entry::WRITABLE, + true, + true + ); + + unsafe { + // Copy file data + memcpy(segment.p_vaddr as *mut u8, + (elf.data.as_ptr() as usize + segment.p_offset as usize) as *const u8, + segment.p_filesz as usize); + } + + let mut flags = entry::NO_EXECUTE | entry::USER_ACCESSIBLE; + + if segment.p_flags & program_header::PF_R == program_header::PF_R { + flags.insert(entry::PRESENT); + } + + // W ^ X. If it is executable, do not allow it to be writable, even if requested + if segment.p_flags & program_header::PF_X == program_header::PF_X { + flags.remove(entry::NO_EXECUTE); + } else if segment.p_flags & program_header::PF_W == program_header::PF_W { + flags.insert(entry::WRITABLE); + } + + memory.remap(flags, true); + + context.image.push(memory.to_shared()); + } + } + + context.heap = Some(context::memory::Memory::new( + VirtualAddress::new(arch::USER_HEAP_OFFSET), + 0, + entry::NO_EXECUTE | entry::WRITABLE | entry::USER_ACCESSIBLE, + true, + true + ).to_shared()); + + // Map stack + context.stack = Some(context::memory::Memory::new( + VirtualAddress::new(arch::USER_STACK_OFFSET), + arch::USER_STACK_SIZE, + entry::NO_EXECUTE | entry::WRITABLE | entry::USER_ACCESSIBLE, + true, + true + )); + + let mut arg_size = 0; + for arg in args.iter() { + sp -= mem::size_of::<usize>(); + unsafe { *(sp as *mut usize) = arch::USER_ARG_OFFSET + arg_size; } + sp -= mem::size_of::<usize>(); + unsafe { *(sp as *mut usize) = arg.len(); } + + arg_size += arg.len(); + } + + sp -= mem::size_of::<usize>(); + unsafe { *(sp as *mut usize) = args.len(); } + + if arg_size > 0 { + let mut memory = context::memory::Memory::new( + VirtualAddress::new(arch::USER_ARG_OFFSET), + arg_size, + entry::NO_EXECUTE | entry::WRITABLE, + true, + true + ); + + let mut arg_offset = 0; + for arg in args.iter() { + unsafe { + memcpy((arch::USER_ARG_OFFSET + arg_offset) as *mut u8, + arg.as_ptr(), + arg.len()); + } + + arg_offset += arg.len(); + } + + memory.remap(entry::NO_EXECUTE | entry::USER_ACCESSIBLE, true); + + context.image.push(memory.to_shared()); + } + }, + Err(err) => { + println!("failed to execute {}: {}", unsafe { str::from_utf8_unchecked(path) }, err); + return Err(Error::NoExec); + } } } + + // Go to usermode + unsafe { usermode(entry, sp); } } pub fn getpid() -> Result<usize> { @@ -378,7 +494,7 @@ pub fn sched_yield() -> Result<usize> { Ok(0) } -pub fn waitpid(pid: usize, _status_ptr: usize, _options: usize) -> Result<usize> { +pub fn waitpid(pid: usize, status_ptr: usize, _options: usize) -> Result<usize> { //TODO: Implement status_ptr and options loop { { @@ -389,7 +505,10 @@ pub fn waitpid(pid: usize, _status_ptr: usize, _options: usize) -> Result<usize> let context_lock = contexts.get(pid).ok_or(Error::NoProcess)?; let context = context_lock.read(); if let context::Status::Exited(status) = context.status { - //TODO: set status_ptr + if status_ptr != 0 { + let status_slice = validate_slice_mut(status_ptr as *mut usize, 1)?; + status_slice[0] = status; + } exited = true; } } -- GitLab