diff --git a/src/lib.rs b/src/lib.rs index c3caf85a44885f9d42a600da32cbc08ec37ee409..f0ce72923176d1751e1d66a9b925e14d3ba95013 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -143,7 +143,8 @@ pub extern fn userspace_init() { assert_eq!(syscall::open(b"debug:", syscall::flag::O_WRONLY).map(FileHandle::into), Ok(1)); assert_eq!(syscall::open(b"debug:", syscall::flag::O_WRONLY).map(FileHandle::into), Ok(2)); - syscall::exec(b"/bin/init", &[]).expect("failed to execute init"); + let fd = syscall::open(b"/bin/init", syscall::flag::O_RDONLY).expect("failed to open init"); + syscall::fexec(fd, &[], &[]).expect("failed to execute init"); panic!("init returned"); } diff --git a/src/syscall/debug.rs b/src/syscall/debug.rs index 06474a129d4367c771846b9094b0536d5800fc2a..d126f2e26b91b9adefd8f963707dad1e4f3a0045 100644 --- a/src/syscall/debug.rs +++ b/src/syscall/debug.rs @@ -191,13 +191,26 @@ pub fn format_call(a: usize, b: usize, c: usize, d: usize, e: usize, f: usize) - "clone({})", b ), + SYS_EXIT => format!( + "exit({})", + b + ), //TODO: Cleanup, do not allocate - SYS_EXECVE => format!( - "execve({:?}, {:?})", - validate_slice(b as *const u8, c).map(ByteStr), + SYS_FEXEC => format!( + "fexec({}, {:?}, {:?})", + b, + validate_slice( + c as *const [usize; 2], + d + ).map(|slice| { + slice.iter().map(|a| + validate_slice(a[0] as *const u8, a[1]).ok() + .and_then(|s| ::core::str::from_utf8(s).ok()) + ).collect::<Vec<Option<&str>>>() + }), validate_slice( - d as *const [usize; 2], - e + e as *const [usize; 2], + f ).map(|slice| { slice.iter().map(|a| validate_slice(a[0] as *const u8, a[1]).ok() @@ -205,10 +218,6 @@ pub fn format_call(a: usize, b: usize, c: usize, d: usize, e: usize, f: usize) - ).collect::<Vec<Option<&str>>>() }) ), - SYS_EXIT => format!( - "exit({})", - b - ), SYS_FUTEX => format!( "futex({:#X} [{:?}], {}, {}, {}, {})", b, diff --git a/src/syscall/mod.rs b/src/syscall/mod.rs index f06f1de64c44380d99048b48ccd6335839ef8de1..6ebcbacf99a7e9e5b9bd90ea7a86b071fbb3de8c 100644 --- a/src/syscall/mod.rs +++ b/src/syscall/mod.rs @@ -64,6 +64,7 @@ pub fn syscall(a: usize, b: usize, c: usize, d: usize, e: usize, f: usize, bp: u SYS_DUP2 => dup2(fd, FileHandle::from(c), validate_slice(d as *const u8, e)?).map(FileHandle::into), SYS_FCNTL => fcntl(fd, c, d), SYS_FEVENT => fevent(fd, c), + SYS_FEXEC => fexec(fd, validate_slice(c as *const [usize; 2], d)?, validate_slice(e as *const [usize; 2], f)?), SYS_FRENAME => frename(fd, validate_slice(c as *const u8, d)?), SYS_FUNMAP => funmap(b), _ => file_op(a, fd, c, d) @@ -98,7 +99,6 @@ pub fn syscall(a: usize, b: usize, c: usize, d: usize, e: usize, f: usize, bp: u SYS_KILL => kill(ContextId::from(b), c), SYS_WAITPID => waitpid(ContextId::from(b), c, d).map(ContextId::into), SYS_CHDIR => chdir(validate_slice(b as *const u8, c)?), - SYS_EXECVE => exec(validate_slice(b as *const u8, c)?, validate_slice(d as *const [usize; 2], e)?), SYS_IOPL => iopl(b, stack), SYS_GETCWD => getcwd(validate_slice_mut(b as *mut u8, c)?), SYS_GETEGID => getegid(), diff --git a/src/syscall/process.rs b/src/syscall/process.rs index 0126115fed108edd562b3a3d77267df2ed401712..8b012aaa08af3af1f4dd8841033ba6adafb42751 100644 --- a/src/syscall/process.rs +++ b/src/syscall/process.rs @@ -2,7 +2,7 @@ use alloc::arc::Arc; use alloc::boxed::Box; use alloc::{BTreeMap, Vec}; use core::alloc::{Alloc, GlobalAlloc, Layout}; -use core::{intrinsics, mem, str}; +use core::{intrinsics, mem}; use core::ops::DerefMut; use spin::Mutex; @@ -541,11 +541,11 @@ impl Drop for ExecFile { } fn exec_noreturn( - canonical: Box<[u8]>, setuid: Option<u32>, setgid: Option<u32>, data: Box<[u8]>, - args: Box<[Box<[u8]>]> + args: Box<[Box<[u8]>]>, + vars: Box<[Box<[u8]>]> ) -> ! { let entry; let mut sp = ::USER_STACK_OFFSET + ::USER_STACK_SIZE - 256; @@ -557,7 +557,9 @@ fn exec_noreturn( let mut context = context_lock.write(); // Set name - context.name = Arc::new(Mutex::new(canonical)); + if let Some(name) = args.get(0) { + context.name = Arc::new(Mutex::new(name.clone())); + } empty(&mut context, false); @@ -776,7 +778,7 @@ fn exec_noreturn( unsafe { usermode(entry, sp, 0); } } -pub fn exec(path: &[u8], arg_ptrs: &[[usize; 2]]) -> Result<usize> { +pub fn fexec(mut fd: FileHandle, arg_ptrs: &[[usize; 2]], var_ptrs: &[[usize; 2]]) -> Result<usize> { let mut args = Vec::new(); for arg_ptr in arg_ptrs { let arg = validate_slice(arg_ptr[0] as *const u8, arg_ptr[1])?; @@ -784,17 +786,25 @@ pub fn exec(path: &[u8], arg_ptrs: &[[usize; 2]]) -> Result<usize> { args.push(arg.to_vec().into_boxed_slice()); } - let (uid, gid, mut canonical) = { + let mut vars = Vec::new(); + for var_ptr in var_ptrs { + let var = validate_slice(var_ptr[0] as *const u8, var_ptr[1])?; + // Argument must be moved into kernel space before exec unmaps all memory + vars.push(var.to_vec().into_boxed_slice()); + } + + let (uid, gid) = { let contexts = context::contexts(); let context_lock = contexts.current().ok_or(Error::new(ESRCH))?; let context = context_lock.read(); - (context.euid, context.egid, context.canonicalize(path)) + (context.euid, context.egid) }; let mut stat: Stat; let mut data: Vec<u8>; - loop { - let file = ExecFile(syscall::open(&canonical, syscall::flag::O_RDONLY)?); + //loop + { + let file = ExecFile(fd); stat = Stat::default(); syscall::file_op_mut_slice(syscall::number::SYS_FSTAT, file.0, &mut stat)?; @@ -819,34 +829,37 @@ pub fn exec(path: &[u8], arg_ptrs: &[[usize; 2]]) -> Result<usize> { syscall::file_op_mut_slice(syscall::number::SYS_READ, file.0, &mut data)?; drop(file); - if data.starts_with(b"#!") { - if let Some(line) = data[2..].split(|&b| b == b'\n').next() { - // Strip whitespace - let line = &line[line.iter().position(|&b| b != b' ') - .unwrap_or(0)..]; - let executable = line.split(|x| *x == b' ').next().unwrap_or(b""); - let mut parts = line.split(|x| *x == b' ') - .map(|x| x.iter().cloned().collect::<Vec<_>>().into_boxed_slice()) - .collect::<Vec<_>>(); - if ! args.is_empty() { - args.remove(0); - } - parts.push(path.to_vec().into_boxed_slice()); - parts.extend(args.iter().cloned()); - args = parts; - canonical = { - let contexts = context::contexts(); - let context_lock = contexts.current().ok_or(Error::new(ESRCH))?; - let context = context_lock.read(); - context.canonicalize(executable) - }; - } else { - println!("invalid script {}", unsafe { str::from_utf8_unchecked(path) }); - return Err(Error::new(ENOEXEC)); - } - } else { - break; - } + // TODO: Move to userspace + // if data.starts_with(b"#!") { + // if let Some(line) = data[2..].split(|&b| b == b'\n').next() { + // // Strip whitespace + // let line = &line[line.iter().position(|&b| b != b' ') + // .unwrap_or(0)..]; + // let executable = line.split(|x| *x == b' ').next().unwrap_or(b""); + // let mut parts = line.split(|x| *x == b' ') + // .map(|x| x.iter().cloned().collect::<Vec<_>>().into_boxed_slice()) + // .collect::<Vec<_>>(); + // if ! args.is_empty() { + // args.remove(0); + // } + // parts.push(path.to_vec().into_boxed_slice()); + // parts.extend(args.iter().cloned()); + // args = parts; + // let canonical = { + // let contexts = context::contexts(); + // let context_lock = contexts.current().ok_or(Error::new(ESRCH))?; + // let context = context_lock.read(); + // context.canonicalize(executable) + // }; + // + // fd = syscall::open(&canonical, syscall::flag::O_RDONLY)?; + // } else { + // println!("invalid script {}", unsafe { str::from_utf8_unchecked(path) }); + // return Err(Error::new(ENOEXEC)); + // } + // } else { + // break; + // } } // Set UID and GID are determined after resolving any hashbangs @@ -871,7 +884,7 @@ pub fn exec(path: &[u8], arg_ptrs: &[[usize; 2]]) -> Result<usize> { // argument pointer array and potential padding // // A limit of 4095 would mean a stack of (4095 + 1) * 8 * 2 = 65536, or 64KB - if args.len() > 4095 { + if (args.len() + vars.len()) > 4095 { return Err(Error::new(E2BIG)); } @@ -894,19 +907,19 @@ pub fn exec(path: &[u8], arg_ptrs: &[[usize; 2]]) -> Result<usize> { } }, Err(err) => { - println!("exec: failed to execute {}: {}", unsafe { str::from_utf8_unchecked(path) }, err); + println!("fexec: failed to execute {}: {}", fd.into(), err); return Err(Error::new(ENOEXEC)); } } // Drop so that usage is not allowed after unmapping context - drop(path); drop(arg_ptrs); + drop(var_ptrs); // 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. - exec_noreturn(canonical.into_boxed_slice(), setuid, setgid, data.into_boxed_slice(), args.into_boxed_slice()); + exec_noreturn(setuid, setgid, data.into_boxed_slice(), args.into_boxed_slice(), vars.into_boxed_slice()); } pub fn exit(status: usize) -> ! { diff --git a/syscall b/syscall index 0ab552da9a9587b360b5d9991ed9921300e5667b..66e34aea2ee48e55a023861595064df26f8573b9 160000 --- a/syscall +++ b/syscall @@ -1 +1 @@ -Subproject commit 0ab552da9a9587b360b5d9991ed9921300e5667b +Subproject commit 66e34aea2ee48e55a023861595064df26f8573b9