From cfbaccf4d2c1e1fe55cce036ce84941940a8a11a Mon Sep 17 00:00:00 2001 From: Jeremy Soller Date: Sat, 17 Sep 2016 21:44:50 -0600 Subject: [PATCH] Complete execve - add argument support using safe ABI --- arch/x86_64/src/externs.rs | 16 ++-- arch/x86_64/src/lib.rs | 6 +- init/src/main.rs | 7 +- kernel/elf.rs | 81 +---------------- kernel/syscall/process.rs | 173 +++++++++++++++++++++++++++++++------ libstd | 2 +- syscall/src/lib.rs | 4 +- 7 files changed, 167 insertions(+), 122 deletions(-) diff --git a/arch/x86_64/src/externs.rs b/arch/x86_64/src/externs.rs index 3b87427..d92f8c4 100644 --- a/arch/x86_64/src/externs.rs +++ b/arch/x86_64/src/externs.rs @@ -6,7 +6,7 @@ pub unsafe extern fn memcpy(dest: *mut u8, src: *const u8, n: usize) -> *mut u8 { let mut i = 0; while i < n { - *dest.offset(i as isize) = *src.offset(i as isize); + *((dest as usize + i) as *mut u8) = *((src as usize + i) as *const u8); i += 1; } @@ -23,12 +23,12 @@ pub unsafe extern fn memmove(dest: *mut u8, src: *const u8, let mut i = n; while i != 0 { i -= 1; - *dest.offset(i as isize) = *src.offset(i as isize); + *((dest as usize + i) as *mut u8) = *((src as usize + i) as *const u8); } } else { let mut i = 0; while i < n { - *dest.offset(i as isize) = *src.offset(i as isize); + *((dest as usize + i) as *mut u8) = *((src as usize + i) as *const u8); i += 1; } } @@ -40,14 +40,14 @@ pub unsafe extern fn memmove(dest: *mut u8, src: *const u8, /// /// Fill a block of memory with a specified value. #[no_mangle] -pub unsafe extern fn memset(s: *mut u8, c: i32, n: usize) -> *mut u8 { +pub unsafe extern fn memset(dest: *mut u8, c: i32, n: usize) -> *mut u8 { let mut i = 0; while i < n { - *s.offset(i as isize) = c as u8; + *((dest as usize + i) as *mut u8) = c as u8; i += 1; } - s + dest } /// Memcmp @@ -58,8 +58,8 @@ pub unsafe extern fn memcmp(s1: *const u8, s2: *const u8, n: usize) -> i32 { let mut i = 0; while i < n { - let a = *s1.offset(i as isize); - let b = *s2.offset(i as isize); + let a = *((s1 as usize + i) as *const u8); + let b = *((s2 as usize + i) as *const u8); if a != b { return a as i32 - b as i32 } diff --git a/arch/x86_64/src/lib.rs b/arch/x86_64/src/lib.rs index 808d238..870bf73 100644 --- a/arch/x86_64/src/lib.rs +++ b/arch/x86_64/src/lib.rs @@ -47,6 +47,9 @@ pub extern crate x86; /// Offset to user image pub const USER_OFFSET: usize = 0; + /// Offset to user arguments + pub const USER_ARG_OFFSET: usize = USER_OFFSET + PML4_SIZE/2; + /// Offset to user heap pub const USER_HEAP_OFFSET: usize = USER_OFFSET + PML4_SIZE; @@ -139,7 +142,8 @@ macro_rules! interrupt_error { } // Push scratch registers - asm!("push rax + asm!("xchg bx, bx + push rax push rcx push rdx push rdi diff --git a/init/src/main.rs b/init/src/main.rs index 8f5c64b..7d649ba 100644 --- a/init/src/main.rs +++ b/init/src/main.rs @@ -38,10 +38,9 @@ pub fn main() { println!("init: spawning {}", line); match command.spawn() { - Ok(mut child) => if let Err(err) = child.wait() { - println!("init: failed to wait for '{}': {}", line, err); - } else { - println!("init: waited for {}", line); + Ok(mut child) => match child.wait() { + Ok(status) => println!("init: waited for {}: {:?}", line, status.code()), + Err(err) => println!("init: failed to wait for '{}': {}", line, err) }, Err(err) => println!("init: failed to execute '{}': {}", line, err) } diff --git a/kernel/elf.rs b/kernel/elf.rs index 180ff23..bf484fe 100644 --- a/kernel/elf.rs +++ b/kernel/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/kernel/syscall/process.rs b/kernel/syscall/process.rs index 182c5f9..383141c 100644 --- a/kernel/syscall/process.rs +++ b/kernel/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 { let contexts = context::contexts(); @@ -332,33 +334,147 @@ pub fn exit(status: usize) -> ! { unreachable!(); } -pub fn exec(path: &[u8], _args: &[[usize; 2]]) -> Result { - //TODO: Use args - //TODO: Unmap previous mappings - //TODO: Drop data vec +pub fn exec(path: &[u8], arg_ptrs: &[[usize; 2]]) -> Result { + 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 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::(); + unsafe { *(sp as *mut usize) = arch::USER_ARG_OFFSET + arg_size; } + sp -= mem::size_of::(); + unsafe { *(sp as *mut usize) = arg.len(); } + + arg_size += arg.len(); + } + + sp -= mem::size_of::(); + 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); + } } } - 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) - } - } + // Go to usermode + unsafe { usermode(entry, sp); } } pub fn getpid() -> Result { @@ -378,7 +494,7 @@ pub fn sched_yield() -> Result { Ok(0) } -pub fn waitpid(pid: usize, _status_ptr: usize, _options: usize) -> Result { +pub fn waitpid(pid: usize, status_ptr: usize, _options: usize) -> Result { //TODO: Implement status_ptr and options loop { { @@ -389,7 +505,10 @@ pub fn waitpid(pid: usize, _status_ptr: usize, _options: usize) -> Result 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; } } diff --git a/libstd b/libstd index 3483097..0d434cc 160000 --- a/libstd +++ b/libstd @@ -1 +1 @@ -Subproject commit 3483097e4f5172fe8b4cbe0dee868d18201c12f8 +Subproject commit 0d434cc168b1d88211f1a3b72c8290c911432826 diff --git a/syscall/src/lib.rs b/syscall/src/lib.rs index ebfbc0a..800a1db 100644 --- a/syscall/src/lib.rs +++ b/syscall/src/lib.rs @@ -126,8 +126,8 @@ pub fn dup(fd: usize) -> Result { unsafe { syscall1(SYS_DUP, fd) } } -pub fn execve(path: &str) -> Result { - unsafe { syscall2(SYS_EXECVE, path.as_ptr() as usize, path.len()) } +pub fn execve(path: &str, args: &[[usize; 2]]) -> Result { + unsafe { syscall4(SYS_EXECVE, path.as_ptr() as usize, path.len(), args.as_ptr() as usize, args.len()) } } pub fn exit(status: usize) -> Result {