diff --git a/src/lib.rs b/src/lib.rs index 8627940..2966c1b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -186,7 +186,7 @@ pub extern fn userspace_init() { } } - syscall::fexec_kernel(fd, args.into_boxed_slice(), vars.into_boxed_slice(), None).expect("failed to execute init"); + syscall::fexec_kernel(fd, args.into_boxed_slice(), vars.into_boxed_slice(), None, None).expect("failed to execute init"); panic!("init returned"); } diff --git a/src/syscall/process.rs b/src/syscall/process.rs index 2da33bd..9c794de 100644 --- a/src/syscall/process.rs +++ b/src/syscall/process.rs @@ -23,11 +23,11 @@ use crate::scheme::FileHandle; use crate::start::usermode; use crate::syscall::data::{SigAction, Stat}; use crate::syscall::error::*; -use crate::syscall::flag::{wifcontinued, wifstopped, CloneFlags, CLONE_FILES, - CLONE_FS, CLONE_SIGHAND, CLONE_STACK, CLONE_VFORK, CLONE_VM, MapFlags, - PROT_EXEC, PROT_READ, PROT_WRITE, PTRACE_EVENT_CLONE, PTRACE_STOP_EXIT, - SigActionFlags, SIG_BLOCK, SIG_DFL, SIG_SETMASK, SIG_UNBLOCK, SIGCONT, SIGTERM, - WaitFlags, WCONTINUED, WNOHANG,WUNTRACED}; +use crate::syscall::flag::{wifcontinued, wifstopped, AT_ENTRY, AT_NULL, CloneFlags, + CLONE_FILES, CLONE_FS, CLONE_SIGHAND, CLONE_STACK, CLONE_VFORK, CLONE_VM, + MapFlags, PROT_EXEC, PROT_READ, PROT_WRITE, PTRACE_EVENT_CLONE, + PTRACE_STOP_EXIT, SigActionFlags, SIG_BLOCK, SIG_DFL, SIG_SETMASK, SIG_UNBLOCK, + SIGCONT, SIGTERM, WaitFlags, WCONTINUED, WNOHANG, WUNTRACED}; use crate::syscall::ptrace_event; use crate::syscall::validate::{validate_slice, validate_slice_mut}; @@ -657,7 +657,8 @@ fn fexec_noreturn( name: Box<[u8]>, data: Box<[u8]>, args: Box<[Box<[u8]>]>, - vars: Box<[Box<[u8]>]> + vars: Box<[Box<[u8]>]>, + auxv: Box<[usize]>, ) -> ! { let entry; let singlestep; @@ -809,27 +810,40 @@ fn fexec_noreturn( context.tls = Some(tls); } + let mut push = |arg| { + sp -= mem::size_of::(); + unsafe { *(sp as *mut usize) = arg; } + }; + + // Push auxiliery vector + push(AT_NULL); + for &arg in auxv.iter().rev() { + push(arg); + } + + // drop(auxv); // no longer required + let mut arg_size = 0; - // Push arguments and variables + // Push environment variables and arguments for iter in &[&vars, &args] { // Push null-terminator - sp -= mem::size_of::(); - unsafe { *(sp as *mut usize) = 0; } + push(0); - // Push content + // Push pointer to content for arg in iter.iter().rev() { - sp -= mem::size_of::(); - unsafe { *(sp as *mut usize) = crate::USER_ARG_OFFSET + arg_size; } - + push(crate::USER_ARG_OFFSET + arg_size); arg_size += arg.len() + 1; } } - // Push arguments length - sp -= mem::size_of::(); - unsafe { *(sp as *mut usize) = args.len(); } + // For some reason, Linux pushes the argument count here (in + // addition to being null-terminated), but not the environment + // variable count. + // TODO: Push more counts? Less? Stop having null-termination? + push(args.len()); + // Write environment and argument pointers to USER_ARG_OFFSET if arg_size > 0 { let mut memory = context::memory::Memory::new( VirtualAddress::new(crate::USER_ARG_OFFSET), @@ -858,8 +872,9 @@ fn fexec_noreturn( context.image.push(memory.to_shared()); } - // Args no longer required, can deallocate + // Args and vars no longer required, can deallocate drop(args); + drop(vars); context.actions = Arc::new(Mutex::new(vec![( SigAction { @@ -908,7 +923,7 @@ fn fexec_noreturn( unsafe { usermode(entry, sp, 0, singlestep) } } -pub fn fexec_kernel(fd: FileHandle, args: Box<[Box<[u8]>]>, vars: Box<[Box<[u8]>]>, name_override_opt: Option>) -> Result { +pub fn fexec_kernel(fd: FileHandle, args: Box<[Box<[u8]>]>, vars: Box<[Box<[u8]>]>, name_override_opt: Option>, auxv: Option>) -> Result { let (uid, gid) = { let contexts = context::contexts(); let context_lock = contexts.current().ok_or(Error::new(ESRCH))?; @@ -980,72 +995,90 @@ pub fn fexec_kernel(fd: FileHandle, args: Box<[Box<[u8]>]>, vars: Box<[Box<[u8]> return Err(Error::new(E2BIG)); } - match elf::Elf::from(&data) { - Ok(elf) => { - // We check the validity of all loadable sections here - for segment in elf.segments() { - match segment.p_type { - program_header::PT_INTERP => { - //TODO: length restraint, parse interp earlier - let mut interp = vec![0; segment.p_memsz as usize]; - unsafe { - intrinsics::copy((elf.data.as_ptr() as usize + segment.p_offset as usize) as *const u8, - interp.as_mut_ptr(), - segment.p_filesz as usize); - } - - let mut i = 0; - while i < interp.len() { - if interp[i] == 0 { - break; - } - i += 1; - } - interp.truncate(i); - - println!(" interpreter: {:?}", ::core::str::from_utf8(&interp)); - - let interp_fd = super::fs::open(&interp, super::flag::O_RDONLY | super::flag::O_CLOEXEC)?; - - let mut args_vec = Vec::from(args); - args_vec.insert(0, interp.into_boxed_slice()); - //TODO: pass file handle in auxv - let name_override = name.into_boxed_slice(); - args_vec[1] = name_override.clone(); - - return fexec_kernel( - interp_fd, - args_vec.into_boxed_slice(), - vars, - Some(name_override), - ); - }, - program_header::PT_LOAD => { - let voff = segment.p_vaddr as usize % PAGE_SIZE; - let vaddr = segment.p_vaddr as usize - voff; - - // Due to the Userspace and kernel TLS bases being located right above 2GB, - // limit any loadable sections to lower than that. Eventually we will need - // to replace this with a more intelligent TLS address - if vaddr >= 0x8000_0000 { - println!("exec: invalid section address {:X}", segment.p_vaddr); - return Err(Error::new(ENOEXEC)); - } - }, - _ => (), - } - } - }, + let elf = match elf::Elf::from(&data) { + Ok(elf) => elf, Err(err) => { println!("fexec: failed to execute {}: {}", fd.into(), err); return Err(Error::new(ENOEXEC)); } + }; + + // `fexec_kernel` can recurse if an interpreter is found. We get the + // auxiliery vector from the first invocation, which is passed via an + // argument, or if this is the first one we create it. + let auxv = if let Some(auxv) = auxv { + auxv + } else { + let mut auxv = Vec::with_capacity(3); + + auxv.push(AT_ENTRY); + auxv.push(elf.entry()); + + auxv.into_boxed_slice() + }; + + // We check the validity of all loadable sections here + for segment in elf.segments() { + match segment.p_type { + program_header::PT_INTERP => { + //TODO: length restraint, parse interp earlier + let mut interp = vec![0; segment.p_memsz as usize]; + unsafe { + intrinsics::copy((elf.data.as_ptr() as usize + segment.p_offset as usize) as *const u8, + interp.as_mut_ptr(), + segment.p_filesz as usize); + } + + let mut i = 0; + while i < interp.len() { + if interp[i] == 0 { + break; + } + i += 1; + } + interp.truncate(i); + + println!(" interpreter: {:?}", ::core::str::from_utf8(&interp)); + + let interp_fd = super::fs::open(&interp, super::flag::O_RDONLY | super::flag::O_CLOEXEC)?; + + let mut args_vec = Vec::from(args); + args_vec.insert(0, interp.into_boxed_slice()); + //TODO: pass file handle in auxv + let name_override = name.into_boxed_slice(); + args_vec[1] = name_override.clone(); + + // Drop variables, since fexec_kernel probably won't return + drop(elf); + + return fexec_kernel( + interp_fd, + args_vec.into_boxed_slice(), + vars, + Some(name_override), + Some(auxv), + ); + }, + program_header::PT_LOAD => { + let voff = segment.p_vaddr as usize % PAGE_SIZE; + let vaddr = segment.p_vaddr as usize - voff; + + // Due to the Userspace and kernel TLS bases being located right above 2GB, + // limit any loadable sections to lower than that. Eventually we will need + // to replace this with a more intelligent TLS address + if vaddr >= 0x8000_0000 { + println!("exec: invalid section address {:X}", segment.p_vaddr); + return Err(Error::new(ENOEXEC)); + } + }, + _ => (), + } } // This is the point of no return, quite literaly. Any checks for validity need // to be done before, and appropriate errors returned. Otherwise, we have nothing // to return to. - fexec_noreturn(setuid, setgid, name.into_boxed_slice(), data.into_boxed_slice(), args, vars); + fexec_noreturn(setuid, setgid, name.into_boxed_slice(), data.into_boxed_slice(), args, vars, auxv); } pub fn fexec(fd: FileHandle, arg_ptrs: &[[usize; 2]], var_ptrs: &[[usize; 2]]) -> Result { @@ -1066,7 +1099,7 @@ pub fn fexec(fd: FileHandle, arg_ptrs: &[[usize; 2]], var_ptrs: &[[usize; 2]]) - // Neither arg_ptrs nor var_ptrs should be used after this point, the kernel // now has owned copies in args and vars - fexec_kernel(fd, args.into_boxed_slice(), vars.into_boxed_slice(), None) + fexec_kernel(fd, args.into_boxed_slice(), vars.into_boxed_slice(), None, None) } pub fn exit(status: usize) -> ! { diff --git a/syscall b/syscall index 27fcecb..10994ea 160000 --- a/syscall +++ b/syscall @@ -1 +1 @@ -Subproject commit 27fcecb30fb3154a9b45f66756d64f8f48281a7b +Subproject commit 10994eaa96e92890d945bec77023378fe374a114