Browse Source

Merge branch 'relibc_cwd' into 'master'

Move processes' cwd state into relibc

See merge request redox-os/relibc!346
Jeremy Soller 2 years ago
parent
commit
1315171a70

+ 2 - 1
src/ld_so/access.rs

@@ -29,7 +29,7 @@ unsafe fn access(path: *const c_char, mode: c_int) -> c_int {
         Ok(ok) => ok,
         Err(_) => return -1,
     };
-    let fd = match syscall::open(path, syscall::O_CLOEXEC) {
+    let fd = match crate::platform::sys::path::open(path, syscall::O_CLOEXEC) {
         Ok(fd) => fd,
         _ => return -1,
     };
@@ -40,6 +40,7 @@ unsafe fn access(path: *const c_char, mode: c_int) -> c_int {
     if syscall::fstat(fd, &mut stat).is_err() {
         return -1;
     }
+    let _ = syscall::close(fd);
     let uid = match syscall::getuid() {
         Ok(uid) => uid,
         Err(_) => return -1,

+ 8 - 18
src/ld_so/start.rs

@@ -11,7 +11,7 @@ use alloc::{
 use crate::{
     c_str::CStr,
     header::{sys_auxv::AT_NULL, unistd},
-    platform::{new_mspace, types::c_char},
+    platform::{get_auxvs, get_auxv, new_mspace, types::c_char},
     start::Stack,
     sync::mutex::Mutex,
     ALLOCATOR,
@@ -57,19 +57,6 @@ unsafe fn get_env(mut ptr: *const usize) -> (BTreeMap<String, String>, *const us
     return (envs, ptr);
 }
 
-unsafe fn get_auxv(mut ptr: *const usize) -> BTreeMap<usize, usize> {
-    //traverse the stack and collect argument environment variables
-    let mut auxv = BTreeMap::new();
-    while *ptr != AT_NULL {
-        let kind = *ptr;
-        ptr = ptr.add(1);
-        let value = *ptr;
-        ptr = ptr.add(1);
-        auxv.insert(kind, value);
-    }
-    return auxv;
-}
-
 unsafe fn adjust_stack(sp: &'static mut Stack) {
     let mut argv = sp.argv() as *mut usize;
 
@@ -163,7 +150,7 @@ pub extern "C" fn relibc_ld_so_start(sp: &'static mut Stack, ld_entry: usize) ->
         let argv_start = sp.argv() as *mut usize;
         let (argv, argv_end) = get_argv(argv_start);
         let (envs, envs_end) = get_env(argv_end.add(1));
-        let auxv = get_auxv(envs_end.add(1));
+        let auxv = get_auxvs(envs_end.add(1));
         (argv, envs, auxv)
     };
 
@@ -183,8 +170,8 @@ pub extern "C" fn relibc_ld_so_start(sp: &'static mut Stack, ld_entry: usize) ->
         crate::platform::environ = crate::platform::OUR_ENVIRON.as_mut_ptr();
     }
 
-    let is_manual = if let Some(img_entry) = auxv.get(&AT_ENTRY) {
-        *img_entry == ld_entry
+    let is_manual = if let Some(img_entry) = get_auxv(&auxv, AT_ENTRY) {
+        img_entry == ld_entry
     } else {
         true
     };
@@ -194,6 +181,9 @@ pub extern "C" fn relibc_ld_so_start(sp: &'static mut Stack, ld_entry: usize) ->
         _r_debug.r_ldbase = ld_entry;
     }
 
+    // TODO: Fix memory leak, although minimal.
+    crate::platform::init(auxv.clone());
+
     // Some variables that will be overridden by environment and auxiliary vectors
     let ld_library_path = envs.get("LD_LIBRARY_PATH").map(|s| s.to_owned());
 
@@ -229,7 +219,7 @@ pub extern "C" fn relibc_ld_so_start(sp: &'static mut Stack, ld_entry: usize) ->
     let base_addr = {
         let mut base = None;
         if !is_manual && cfg!(not(target_os = "redox")) {
-            let phdr = *auxv.get(&AT_PHDR).unwrap();
+            let phdr = get_auxv(&auxv, AT_PHDR).unwrap();
             if phdr != 0 {
                 base = Some(phdr - SIZEOF_EHDR);
             }

+ 6 - 0
src/platform/auxv_defs.rs

@@ -25,3 +25,9 @@ pub const AT_BASE_PLATFORM: usize = 24; /* String identifying real platforms.*/
 pub const AT_RANDOM: usize = 25; /* Address of 16 random bytes.  */
 pub const AT_HWCAP2: usize = 26; /* More machine-dependent hints about*/
 pub const AT_EXECFN: usize = 31; /* Filename of executable.  */
+
+#[cfg(target_os = "redox")]
+// XXX: The name AT_CWD is already used in openat... for a completely different purpose.
+pub const AT_REDOX_INITIALCWD_PTR: usize = 32;
+#[cfg(target_os = "redox")]
+pub const AT_REDOX_INITIALCWD_LEN: usize = 33;

+ 41 - 2
src/platform/mod.rs

@@ -1,4 +1,5 @@
 use crate::io::{self, Read, Write};
+use alloc::boxed::Box;
 use alloc::vec::Vec;
 use core::{fmt, ptr};
 
@@ -20,11 +21,11 @@ pub use self::sys::{e, Sys};
 
 #[cfg(all(not(feature = "no_std"), target_os = "linux"))]
 #[path = "linux/mod.rs"]
-mod sys;
+pub(crate) mod sys;
 
 #[cfg(all(not(feature = "no_std"), target_os = "redox"))]
 #[path = "redox/mod.rs"]
-mod sys;
+pub(crate) mod sys;
 
 #[cfg(test)]
 mod test;
@@ -261,3 +262,41 @@ impl<T: Write> Write for CountingWriter<T> {
         self.inner.flush()
     }
 }
+
+// TODO: Set a global variable once get_auxvs is called, and then implement getauxval based on
+// get_auxv.
+
+#[cold]
+pub unsafe fn get_auxvs(mut ptr: *const usize) -> Box<[[usize; 2]]> {
+    //traverse the stack and collect argument environment variables
+    let mut auxvs = Vec::new();
+
+    while *ptr != self::auxv_defs::AT_NULL {
+        let kind = ptr.read();
+        ptr = ptr.add(1);
+        let value = ptr.read();
+        ptr = ptr.add(1);
+        auxvs.push([kind, value]);
+    }
+
+    auxvs.sort_unstable_by_key(|[kind, _]| *kind);
+    auxvs.into_boxed_slice()
+}
+pub fn get_auxv(auxvs: &[[usize; 2]], key: usize) -> Option<usize> {
+    auxvs.binary_search_by_key(&key, |[entry_key, _]| *entry_key).ok().map(|idx| auxvs[idx][1])
+}
+
+#[cold]
+#[cfg(target_os = "redox")]
+pub fn init(auxvs: Box<[[usize; 2]]>) {
+    use self::auxv_defs::*;
+
+    if let (Some(cwd_ptr), Some(cwd_len)) = (get_auxv(&auxvs, AT_REDOX_INITIALCWD_PTR), get_auxv(&auxvs, AT_REDOX_INITIALCWD_LEN)) {
+        let cwd_bytes: &'static [u8] = unsafe { core::slice::from_raw_parts(cwd_ptr as *const u8, cwd_len) };
+        if let Ok(cwd) = core::str::from_utf8(cwd_bytes) {
+            self::sys::path::setcwd_manual(cwd.into());
+        }
+    }
+}
+#[cfg(not(target_os = "redox"))]
+pub fn init(auxvs: Box<[[usize; 2]]>) {}

+ 0 - 1
src/platform/redox/clone.rs

@@ -31,7 +31,6 @@ pub unsafe fn pte_clone_impl(stack: *mut usize) -> Result<usize> {
     }
 
     copy_str(*cur_pid_fd, *new_pid_fd, "name")?;
-    copy_str(*cur_pid_fd, *new_pid_fd, "cwd")?;
 
     // Reuse existing address space
     {

+ 13 - 10
src/platform/redox/exec.rs

@@ -7,9 +7,9 @@ use crate::platform::{sys::{S_ISUID, S_ISGID}, types::*};
 use syscall::data::Stat;
 use syscall::flag::*;
 use syscall::error::*;
-use redox_exec::{FdGuard, FexecResult};
+use redox_exec::{FdGuard, ExtraInfo, FexecResult};
 
-fn fexec_impl(file: File, path: &[u8], args: &[&[u8]], envs: &[&[u8]], total_args_envs_size: usize, interp_override: Option<redox_exec::InterpOverride>) -> Result<usize> {
+fn fexec_impl(file: File, path: &[u8], args: &[&[u8]], envs: &[&[u8]], total_args_envs_size: usize, extrainfo: &ExtraInfo, interp_override: Option<redox_exec::InterpOverride>) -> Result<usize> {
     let fd = *file;
     core::mem::forget(file);
     let image_file = FdGuard::new(fd as usize);
@@ -17,7 +17,7 @@ fn fexec_impl(file: File, path: &[u8], args: &[&[u8]], envs: &[&[u8]], total_arg
     let open_via_dup = FdGuard::new(syscall::open("thisproc:current/open_via_dup", 0)?);
     let memory = FdGuard::new(syscall::open("memory:", 0)?);
 
-    let addrspace_selection_fd = match redox_exec::fexec_impl(image_file, open_via_dup, &memory, path, args.iter().rev(), envs.iter().rev(), total_args_envs_size, interp_override)? {
+    let addrspace_selection_fd = match redox_exec::fexec_impl(image_file, open_via_dup, &memory, path, args.iter().rev(), envs.iter().rev(), total_args_envs_size, extrainfo, interp_override)? {
         FexecResult::Normal { addrspace_handle } => addrspace_handle,
         FexecResult::Interp { image_file, open_via_dup, path, interp_override: new_interp_override } => {
             drop(image_file);
@@ -75,6 +75,8 @@ pub fn execve(path: &CStr, arg_env: ArgEnv, interp_override: Option<redox_exec::
     }
     let wants_setugid = stat.st_mode & ((S_ISUID | S_ISGID) as u16) != 0;
 
+    let cwd: Box<[u8]> = super::path::clone_cwd().unwrap_or_default().into();
+
     // Count arguments
     let mut len = 0;
 
@@ -103,12 +105,11 @@ pub fn execve(path: &CStr, arg_env: ArgEnv, interp_override: Option<redox_exec::
         }
         shebang == *b"#!"
     };
-    // Since the fexec implementation is almost fully done in userspace, the kernel can no
-    // longer set UID/GID accordingly, and this code checking for them before using
-    // hypothetical interfaces to upgrade UID/GID, can not be trusted. So we ask the
-    // `escalate:` scheme for help. Note that `escalate:` can be deliberately excluded from the
-    // scheme namespace to deny privilege escalation (such as su/sudo/doas) for untrusted
-    // processes.
+    // Since the fexec implementation is almost fully done in userspace, the kernel can no longer
+    // set UID/GID accordingly, and this code checking for them before using interfaces to upgrade
+    // UID/GID, can not be trusted. So we ask the `escalate:` scheme for help. Note that
+    // `escalate:` can be deliberately excluded from the scheme namespace to deny privilege
+    // escalation (such as su/sudo/doas) for untrusted processes.
     //
     // According to execve(2), Linux and most other UNIXes ignore setuid/setgid for interpreted
     // executables and thereby simply keep the privileges as is. For compatibility we do that
@@ -224,6 +225,7 @@ pub fn execve(path: &CStr, arg_env: ArgEnv, interp_override: Option<redox_exec::
         // individual items. This can be copied directly into the new executable's memory.
         let _ = syscall::write(*escalate_fd, &flatten_with_nul(args))?;
         let _ = syscall::write(*escalate_fd, &flatten_with_nul(envs))?;
+        let _ = syscall::write(*escalate_fd, &cwd)?;
 
         // Closing will notify the scheme, and from that point we will no longer have control
         // over this process (unless it fails). We do this manually since drop cannot handle
@@ -235,7 +237,8 @@ pub fn execve(path: &CStr, arg_env: ArgEnv, interp_override: Option<redox_exec::
 
         unreachable!()
     } else {
-        fexec_impl(image_file, path.to_bytes(), &args, &envs, total_args_envs_size, interp_override)
+        let extrainfo = ExtraInfo { cwd: Some(&cwd) };
+        fexec_impl(image_file, path.to_bytes(), &args, &envs, total_args_envs_size, &extrainfo, interp_override)
     }
 }
 fn flatten_with_nul<T>(iter: impl IntoIterator<Item = T>) -> Box<[u8]> where T: AsRef<[u8]> {

+ 35 - 29
src/platform/redox/mod.rs

@@ -1,10 +1,11 @@
 use core::{mem, ptr, result::Result as CoreResult, slice, str};
+use core::convert::TryFrom;
 use core::arch::asm;
 
 use syscall::{
     self,
     data::{Map, Stat as redox_stat, StatVfs as redox_statvfs, TimeSpec as redox_timespec},
-    PtraceEvent, Result,
+    PtraceEvent, Result, Error, EMFILE,
 };
 
 use crate::{
@@ -29,6 +30,8 @@ use crate::{
     io::{self, prelude::*, BufReader, SeekFrom},
 };
 
+pub use redox_exec::FdGuard;
+
 use super::{errno, types::*, Pal, Read};
 
 static mut BRK_CUR: *mut c_void = ptr::null_mut();
@@ -43,6 +46,7 @@ mod clone;
 mod epoll;
 mod exec;
 mod extra;
+pub(crate) mod path;
 mod ptrace;
 mod signal;
 mod socket;
@@ -61,6 +65,8 @@ macro_rules! path_from_c_str {
     }};
 }
 
+use self::path::canonicalize;
+
 pub fn e(sys: Result<usize>) -> usize {
     match sys {
         Ok(ok) => ok,
@@ -163,7 +169,7 @@ impl Pal for Sys {
 
     fn chdir(path: &CStr) -> c_int {
         let path = path_from_c_str!(path);
-        e(syscall::chdir(path)) as c_int
+        e(path::chdir(path).map(|()| 0)) as c_int
     }
 
     fn chmod(path: &CStr, mode: mode_t) -> c_int {
@@ -354,29 +360,20 @@ impl Pal for Sys {
     }
 
     fn getcwd(buf: *mut c_char, size: size_t) -> *mut c_char {
+        // TODO: Not using MaybeUninit seems a little unsafe
+
         let buf_slice = unsafe { slice::from_raw_parts_mut(buf as *mut u8, size as usize) };
-        if !buf_slice.is_empty() {
-            let nonnull_size = buf_slice.len() - 1;
-            let read = e(syscall::getcwd(&mut buf_slice[..nonnull_size]));
-            if read == !0 {
-                ptr::null_mut()
-            } else if read == nonnull_size {
-                unsafe {
-                    errno = ERANGE;
-                }
-                ptr::null_mut()
-            } else {
-                for b in &mut buf_slice[read..] {
-                    *b = 0;
-                }
-                buf
-            }
-        } else {
-            unsafe {
-                errno = EINVAL;
-            }
-            ptr::null_mut()
+        if buf_slice.is_empty() {
+            unsafe { errno = EINVAL; }
+            return ptr::null_mut();
+        }
+
+        if path::getcwd(buf_slice).is_none() {
+            unsafe { errno = ERANGE; }
+            return ptr::null_mut()
         }
+
+        buf
     }
 
     fn getdents(fd: c_int, mut dirents: *mut dirent, max_bytes: usize) -> c_int {
@@ -708,10 +705,19 @@ impl Pal for Sys {
 
     fn open(path: &CStr, oflag: c_int, mode: mode_t) -> c_int {
         let path = path_from_c_str!(path);
-        e(syscall::open(
-            path,
-            ((oflag as usize) & 0xFFFF_0000) | ((mode as usize) & 0xFFFF),
-        )) as c_int
+
+        match path::open(path, ((oflag as usize) & 0xFFFF_0000) | ((mode as usize) & 0xFFFF)) {
+            Ok(fd) => {
+                match c_int::try_from(fd) {
+                    Ok(c_fd) => c_fd,
+                    Err(_) => {
+                        let _ = syscall::close(fd);
+                        e(Err(Error::new(EMFILE))) as c_int
+                    }
+                }
+            }
+            Err(error) => e(Err(error)) as c_int,
+        }
     }
 
     fn pipe2(fds: &mut [c_int], flags: c_int) -> c_int {
@@ -764,7 +770,7 @@ impl Pal for Sys {
 
     fn rmdir(path: &CStr) -> c_int {
         let path = path_from_c_str!(path);
-        e(syscall::rmdir(path)) as c_int
+        e(canonicalize(path).and_then(|path| syscall::rmdir(&path))) as c_int
     }
 
     fn sched_yield() -> c_int {
@@ -891,7 +897,7 @@ impl Pal for Sys {
 
     fn unlink(path: &CStr) -> c_int {
         let path = path_from_c_str!(path);
-        e(syscall::unlink(path)) as c_int
+        e(canonicalize(path).and_then(|path| syscall::unlink(&path))) as c_int
     }
 
     fn waitpid(mut pid: pid_t, stat_loc: *mut c_int, options: c_int) -> pid_t {

+ 185 - 0
src/platform/redox/path.rs

@@ -0,0 +1,185 @@
+use syscall::error::*;
+use syscall::flag::*;
+
+use alloc::borrow::{Cow, ToOwned};
+use alloc::boxed::Box;
+use alloc::string::String;
+use alloc::vec::Vec;
+
+use super::FdGuard;
+use crate::sync::Mutex;
+
+// TODO: Define in syscall
+const PATH_MAX: usize = 4096;
+
+/// Make a relative path absolute.
+///
+/// Given a cwd of "scheme:/path", this his function will turn "foo" into "scheme:/path/foo".
+/// "/foo" will turn into "scheme:/foo". "bar:/foo" will be used directly, as it is already
+/// absolute
+pub fn canonicalize_using_cwd<'a>(cwd_opt: Option<&str>, path: &'a str) -> Option<String> {
+    let mut canon = if path.find(':').is_none() {
+        let cwd = cwd_opt?;
+        let path_start = cwd.find(':')? + 1;
+
+        let mut canon = if !path.starts_with('/') {
+            let mut c = cwd.to_owned();
+            if ! c.ends_with('/') {
+                c.push('/');
+            }
+            c
+        } else {
+            cwd[..path_start].to_owned()
+        };
+
+        canon.push_str(&path);
+        canon
+    } else {
+        path.to_owned()
+    };
+
+    // NOTE: assumes the scheme does not include anything like "../" or "./"
+    let mut result = {
+        let parts = canon.split('/')
+            .rev()
+            .scan(0, |nskip, part| {
+                if part == "." {
+                    Some(None)
+                } else if part == ".." {
+                    *nskip += 1;
+                    Some(None)
+                } else if *nskip > 0 {
+                        *nskip -= 1;
+                        Some(None)
+                } else {
+                    Some(Some(part))
+                }
+            })
+            .filter_map(|x| x)
+            .filter(|x| !x.is_empty())
+            .collect::<Vec<_>>();
+        parts
+            .iter()
+            .rev()
+            .fold(String::new(), |mut string, &part| {
+                string.push_str(part);
+                string.push('/');
+                string
+            })
+    };
+    result.pop(); // remove extra '/'
+
+    // replace with the root of the scheme if it's empty
+    Some(if result.is_empty() {
+        let pos = canon.find(':')
+                        .map_or(canon.len(), |p| p + 1);
+        canon.truncate(pos);
+        canon
+    } else {
+        result
+    })
+}
+
+// XXX: chdir is not marked thread-safe (MT-safe) by POSIX. But on Linux it is simply run as a
+// syscall and is therefore atomic, which is presumably why Rust's libstd doesn't synchronize
+// access to this.
+//
+// https://internals.rust-lang.org/t/synchronized-ffi-access-to-posix-environment-variable-functions/15475
+//
+// chdir is however signal-safe, forbidding the use of locks. We therefore call sigprocmask before
+// and after acquiring the locks. (TODO: ArcSwap? That will need to be ported to no_std first,
+// though).
+pub fn chdir(path: &str) -> Result<()> {
+    let _siglock = SignalMask::lock();
+    let mut cwd_guard = CWD.lock();
+
+    let canonicalized = canonicalize_using_cwd(cwd_guard.as_deref(), path).ok_or(Error::new(ENOENT))?;
+
+    *cwd_guard = Some(canonicalized.into_boxed_str());
+
+    // TODO: Check that the dir exists and is a directory.
+
+    Ok(())
+}
+
+pub fn clone_cwd() -> Option<Box<str>> {
+    let _siglock = SignalMask::lock();
+    CWD.lock().clone()
+}
+
+// TODO: MaybeUninit
+pub fn getcwd(buf: &mut [u8]) -> Option<usize> {
+    let _siglock = SignalMask::lock();
+    let cwd_guard = CWD.lock();
+    let cwd = cwd_guard.as_deref().unwrap_or("").as_bytes();
+
+    // But is already checked not to be empty.
+    if buf.len() - 1 < cwd.len() { return None; }
+
+    buf[..cwd.len()].copy_from_slice(&cwd);
+    buf[cwd.len()..].fill(0_u8);
+
+    Some(cwd.len())
+}
+
+// TODO: Move cwd from kernel to libc. It is passed via auxiliary vectors.
+pub fn canonicalize(path: &str) -> Result<String> {
+    let _siglock = SignalMask::lock();
+    let cwd = CWD.lock();
+    canonicalize_using_cwd(cwd.as_deref(), path).ok_or(Error::new(ENOENT))
+}
+
+// TODO: arraystring?
+static CWD: Mutex<Option<Box<str>>> = Mutex::new(None);
+
+pub fn setcwd_manual(cwd: Box<str>) {
+    let _siglock = SignalMask::lock();
+    *CWD.lock() = Some(cwd);
+}
+
+/// RAII guard able to magically fix signal unsafety, by disabling signals during a critical
+/// section.
+pub struct SignalMask {
+    oldset: [u64; 2],
+}
+impl SignalMask {
+    pub fn lock() -> Self {
+        let mut oldset = [0; 2];
+        syscall::sigprocmask(syscall::SIG_SETMASK, Some(&[!0, !0]), Some(&mut oldset)).expect("failed to run sigprocmask");
+        Self { oldset }
+    }
+}
+impl Drop for SignalMask {
+    fn drop(&mut self) {
+        let _ = syscall::sigprocmask(syscall::SIG_SETMASK, Some(&self.oldset), None);
+    }
+}
+
+pub fn open(path: &str, flags: usize) -> Result<usize> {
+    // TODO: SYMLOOP_MAX
+    const MAX_LEVEL: usize = 64;
+
+    let mut resolve_buf = [0_u8; 4096];
+    let mut path = path;
+
+    for _ in 0..MAX_LEVEL {
+        let canon = canonicalize(path)?;
+        match syscall::open(&*canon, flags) {
+            Ok(fd) => return Ok(fd),
+            Err(error) if error == Error::new(EXDEV) => {
+                let resolve_flags = O_CLOEXEC | O_SYMLINK | O_RDONLY;
+                let resolve_fd = FdGuard::new(syscall::open(&*canon, resolve_flags)?);
+
+                let bytes_read = syscall::read(*resolve_fd, &mut resolve_buf)?;
+                // TODO: make resolve_buf PATH_MAX + 1 bytes?
+                if bytes_read == resolve_buf.len() { return Err(Error::new(ENAMETOOLONG)); }
+
+                // If the symbolic link path is non-UTF8, it cannot be opened, and is thus
+                // considered a "dangling symbolic link".
+                path = core::str::from_utf8(&resolve_buf[..bytes_read]).map_err(|_| Error::new(ENOENT))?;
+            }
+            Err(other_error) => return Err(other_error),
+        }
+    }
+    Err(Error::new(ELOOP))
+}

+ 14 - 3
src/platform/redox/redox-exec/src/lib.rs

@@ -34,7 +34,11 @@ pub struct InterpOverride {
     tree: BTreeMap<usize, usize>,
 }
 
-pub fn fexec_impl<A, E>(image_file: FdGuard, open_via_dup: FdGuard, memory_scheme_fd: &FdGuard, path: &[u8], args: A, envs: E, total_args_envs_size: usize, mut interp_override: Option<InterpOverride>) -> Result<FexecResult>
+pub struct ExtraInfo<'a> {
+    pub cwd: Option<&'a [u8]>,
+}
+
+pub fn fexec_impl<A, E>(image_file: FdGuard, open_via_dup: FdGuard, memory_scheme_fd: &FdGuard, path: &[u8], args: A, envs: E, total_args_envs_size: usize, extrainfo: &ExtraInfo, mut interp_override: Option<InterpOverride>) -> Result<FexecResult>
 where
     A: IntoIterator,
     E: IntoIterator,
@@ -178,7 +182,8 @@ where
     push(interp_override.as_ref().map_or(header.e_phentsize as usize, |o| o.at_phent))?;
     push(AT_PHENT)?;
 
-    let args_envs_size_aligned = (total_args_envs_size+PAGE_SIZE-1)/PAGE_SIZE*PAGE_SIZE;
+    let total_args_envs_auxvpointee_size = total_args_envs_size + extrainfo.cwd.map_or(0, |s| s.len() + 1);
+    let args_envs_size_aligned = (total_args_envs_auxvpointee_size+PAGE_SIZE-1)/PAGE_SIZE*PAGE_SIZE;
     let target_args_env_address = find_free_target_addr(&tree, args_envs_size_aligned).ok_or(Error::new(ENOMEM))?;
     allocate_remote(&grants_fd, memory_scheme_fd, target_args_env_address, args_envs_size_aligned, MapFlags::PROT_READ | MapFlags::PROT_WRITE)?;
     tree.insert(target_args_env_address, args_envs_size_aligned);
@@ -195,6 +200,13 @@ where
             Ok(address)
         };
 
+        if let Some(cwd) = extrainfo.cwd {
+            push(append(cwd)?)?;
+            push(AT_REDOX_INITIALCWD_PTR)?;
+            push(cwd.len())?;
+            push(AT_REDOX_INITIALCWD_LEN)?;
+        }
+
         push(0)?;
 
         for env in envs {
@@ -424,7 +436,6 @@ fn fork_inner(initial_rsp: *mut usize) -> Result<usize> {
         }
 
         copy_str(*cur_pid_fd, *new_pid_fd, "name")?;
-        copy_str(*cur_pid_fd, *new_pid_fd, "cwd")?;
 
         {
             let cur_sigaction_fd = FdGuard::new(syscall::dup(*cur_pid_fd, b"sigactions")?);

+ 3 - 1
src/start.rs

@@ -4,7 +4,7 @@ use core::{intrinsics, ptr};
 use crate::{
     header::{libgen, stdio, stdlib},
     ld_so,
-    platform::{self, new_mspace, types::*, Pal, Sys},
+    platform::{self, get_auxvs, new_mspace, types::*, Pal, Sys},
     ALLOCATOR,
 };
 
@@ -185,6 +185,8 @@ pub unsafe extern "C" fn relibc_start(sp: &'static Stack) -> ! {
         platform::OUR_ENVIRON = copy_string_array(envp, len);
         platform::environ = platform::OUR_ENVIRON.as_mut_ptr();
     }
+    let auxvs = get_auxvs(sp.auxv().cast());
+    crate::platform::init(auxvs);
 
     // Setup signal stack, otherwise we cannot handle any signals besides SIG_IGN/SIG_DFL behavior.
     #[cfg(target_os = "redox")]

+ 27 - 0
src/sync/reentrant_mutex.rs

@@ -0,0 +1,27 @@
+use super::{AtomicLock, AttemptStatus};
+
+const WAITING_BIT: u32 = 1 << 31;
+const UNLOCKED: u32 = 0;
+// We now have 2^32 - 1 possible thread ID values
+
+pub struct ReentrantMutex<T> {
+    lock: AtomicLock,
+    content: UnsafeCell<T>,
+}
+unsafe impl<T: Send> Send for ReentrantMutex {}
+unsafe impl<T: Send> Sync for ReentrantMutex {}
+
+impl<T> ReentrantMutex<T> {
+    pub const fn new(context: T) -> Self {
+        Self {
+            lock: AtomicLock::new(UNLOCKED),
+            content: UnsafeCell::new(content),
+        }
+    }
+}
+pub struct ReentrantMutexGuard<'a, T: 'a> {
+    mutex: &'a ReentrantMutex<T>,
+    content: &'a T,
+}
+impl<'a, T> Deref for MutexGuard {
+}