diff --git a/src/header/sys_resource/mod.rs b/src/header/sys_resource/mod.rs index 9166007a..c645e8eb 100644 --- a/src/header/sys_resource/mod.rs +++ b/src/header/sys_resource/mod.rs @@ -92,7 +92,10 @@ pub unsafe extern "C" fn setpriority(which: c_int, who: id_t, nice: c_int) -> c_ /// See . #[unsafe(no_mangle)] pub unsafe extern "C" fn getrlimit(resource: c_int, rlp: *mut rlimit) -> c_int { - let rlp = unsafe { Out::nonnull(rlp) }; + let Some(rlp) = (unsafe { Out::nullable(rlp) }) else { + crate::platform::ERRNO.set(crate::header::errno::EFAULT); + return -1; + }; Sys::getrlimit(resource, rlp) .map(|()| 0) @@ -110,7 +113,12 @@ pub unsafe extern "C" fn setrlimit(resource: c_int, rlp: *const rlimit) -> c_int /// See . #[unsafe(no_mangle)] pub unsafe extern "C" fn getrusage(who: c_int, r_usage: *mut rusage) -> c_int { - Sys::getrusage(who, unsafe { Out::nonnull(r_usage) }) + let Some(r_usage) = (unsafe { Out::nullable(r_usage) }) else { + crate::platform::ERRNO.set(crate::header::errno::EFAULT); + return -1; + }; + + Sys::getrusage(who, r_usage) .map(|()| 0) .or_minus_one_errno() } diff --git a/src/header/unistd/mod.rs b/src/header/unistd/mod.rs index fdd1ff0d..9e3a20e9 100644 --- a/src/header/unistd/mod.rs +++ b/src/header/unistd/mod.rs @@ -521,7 +521,7 @@ pub extern "C" fn getdtablesize() -> c_int { }; if r == 0 { let cur = unsafe { lim.assume_init() }.rlim_cur; - match cur { + return match cur { c if c < i32::MAX as u64 => c as i32, _ => i32::MAX, }; diff --git a/src/header/unistd/sysconf/linux.rs b/src/header/unistd/sysconf/linux.rs index 2ec17eaf..8ec01d2d 100644 --- a/src/header/unistd/sysconf/linux.rs +++ b/src/header/unistd/sysconf/linux.rs @@ -167,11 +167,33 @@ pub(super) fn sysconf_impl(name: c_int) -> c_long { // Values from musl which we can assume is correct. match name { _SC_CLK_TCK => 100, - // TODO: getrlimit - _SC_CHILD_MAX => -1, + _SC_CHILD_MAX => { + let mut lim = core::mem::MaybeUninit::::uninit(); + let r = unsafe { + crate::header::sys_resource::getrlimit( + crate::header::sys_resource::RLIMIT_NPROC as c_int, + lim.as_mut_ptr().cast::(), + ) + }; + if r == 0 { + let cur = unsafe { lim.assume_init() }.rlim_cur; + if cur == crate::header::sys_resource::RLIM_INFINITY { -1 } else if cur > c_long::MAX as u64 { c_long::MAX } else { cur as c_long } + } else { -1 } + } _SC_NGROUPS_MAX => NGROUPS_MAX as c_long, - // TODO: getrlimit - _SC_OPEN_MAX => -1, + _SC_OPEN_MAX => { + let mut lim = core::mem::MaybeUninit::::uninit(); + let r = unsafe { + crate::header::sys_resource::getrlimit( + crate::header::sys_resource::RLIMIT_NOFILE as c_int, + lim.as_mut_ptr().cast::(), + ) + }; + if r == 0 { + let cur = unsafe { lim.assume_init() }.rlim_cur; + if cur == crate::header::sys_resource::RLIM_INFINITY { -1 } else if cur > c_long::MAX as u64 { c_long::MAX } else { cur as c_long } + } else { -1 } + } _SC_STREAM_MAX => -1, // TODO: limits.h _SC_TZNAME_MAX => -1, diff --git a/src/header/unistd/sysconf/redox.rs b/src/header/unistd/sysconf/redox.rs index 97ee81aa..3d7f96dc 100644 --- a/src/header/unistd/sysconf/redox.rs +++ b/src/header/unistd/sysconf/redox.rs @@ -5,7 +5,7 @@ use alloc::string::String; use crate::{ error::Errno, fs::File, - header::{errno, fcntl, limits, sys_statvfs}, + header::{errno, fcntl, limits, sys_resource, sys_statvfs}, io::Read, out::Out, platform::{ @@ -65,14 +65,31 @@ pub const _SC_SIGQUEUE_MAX: c_int = 190; pub const _SC_REALTIME_SIGNALS: c_int = 191; // } POSIX.1 +fn resource_limit_sysconf(resource: c_int) -> c_long { + let mut lim = core::mem::MaybeUninit::::uninit(); + let r = unsafe { sys_resource::getrlimit(resource, lim.as_mut_ptr()) }; + if r != 0 { + return -1; + } + + let cur = unsafe { lim.assume_init() }.rlim_cur; + if cur == sys_resource::RLIM_INFINITY { + -1 + } else if cur > c_long::MAX as u64 { + c_long::MAX + } else { + cur as c_long + } +} + pub(super) fn sysconf_impl(name: c_int) -> c_long { //TODO: Real values match name { _SC_ARG_MAX => 4096, - _SC_CHILD_MAX => 65536, + _SC_CHILD_MAX => resource_limit_sysconf(sys_resource::RLIMIT_NPROC as c_int), _SC_CLK_TCK => 100, _SC_NGROUPS_MAX => limits::NGROUPS_MAX as c_long, - _SC_OPEN_MAX => 1024, + _SC_OPEN_MAX => resource_limit_sysconf(sys_resource::RLIMIT_NOFILE as c_int), _SC_STREAM_MAX => 16, _SC_TZNAME_MAX => -1, _SC_VERSION => 200809, diff --git a/src/platform/redox/mod.rs b/src/platform/redox/mod.rs index 8b5560e7..e6dcac55 100644 --- a/src/platform/redox/mod.rs +++ b/src/platform/redox/mod.rs @@ -43,7 +43,7 @@ use crate::{ sys_file, sys_mman::{MAP_ANONYMOUS, PROT_READ, PROT_WRITE}, sys_random, - sys_resource::{RLIM_INFINITY, rlimit, rusage}, + sys_resource::{RLIM_INFINITY, RLIMIT_NLIMITS, rlimit, rusage}, sys_select::timeval, sys_stat::{S_ISVTX, stat}, sys_statvfs::statvfs, @@ -103,6 +103,32 @@ macro_rules! path_from_c_str { static CLONE_LOCK: RwLock<()> = RwLock::new(()); +/// Per-process resource limits. Initialized with Linux-compatible defaults. +/// Inherited automatically across fork() (kernel copies address space). +const RLIMIT_DEFAULTS: [rlimit; RLIMIT_NLIMITS as usize] = [ + rlimit { rlim_cur: RLIM_INFINITY, rlim_max: RLIM_INFINITY }, // RLIMIT_CPU + rlimit { rlim_cur: RLIM_INFINITY, rlim_max: RLIM_INFINITY }, // RLIMIT_FSIZE + rlimit { rlim_cur: RLIM_INFINITY, rlim_max: RLIM_INFINITY }, // RLIMIT_DATA + rlimit { rlim_cur: 8 * 1024 * 1024, rlim_max: RLIM_INFINITY }, // RLIMIT_STACK (8 MB soft) + rlimit { rlim_cur: 0, rlim_max: RLIM_INFINITY }, // RLIMIT_CORE + rlimit { rlim_cur: RLIM_INFINITY, rlim_max: RLIM_INFINITY }, // RLIMIT_RSS + rlimit { rlim_cur: 4096, rlim_max: RLIM_INFINITY }, // RLIMIT_NPROC + rlimit { rlim_cur: 1024, rlim_max: 1024 * 64 }, // RLIMIT_NOFILE + rlimit { rlim_cur: RLIM_INFINITY, rlim_max: RLIM_INFINITY }, // RLIMIT_MEMLOCK + rlimit { rlim_cur: RLIM_INFINITY, rlim_max: RLIM_INFINITY }, // RLIMIT_AS + rlimit { rlim_cur: RLIM_INFINITY, rlim_max: RLIM_INFINITY }, // RLIMIT_LOCKS + rlimit { rlim_cur: 4096, rlim_max: RLIM_INFINITY }, // RLIMIT_SIGPENDING + rlimit { rlim_cur: 819200, rlim_max: RLIM_INFINITY }, // RLIMIT_MSGQUEUE + rlimit { rlim_cur: 0, rlim_max: 0 }, // RLIMIT_NICE + rlimit { rlim_cur: 0, rlim_max: 0 }, // RLIMIT_RTPRIO +]; + +/// Runtime resource limits, mutable via setrlimit(). +/// Inherited across fork() (kernel copies address space). +static RLIMIT_TABLE: RwLock<[rlimit; RLIMIT_NLIMITS as usize]> = RwLock::new( + RLIMIT_DEFAULTS +); + /// Redox syscall implementation of [`Pal`]. pub struct Sys; @@ -729,21 +755,77 @@ impl Pal for Sys { } fn getrlimit(resource: c_int, mut rlim: Out) -> Result<()> { - todo_skip!(0, "getrlimit({}, {:p}): not implemented", resource, rlim); + if resource < 0 || resource >= RLIMIT_NLIMITS as c_int { + return Err(Errno(EINVAL)); + } + let table = RLIMIT_TABLE.read(); + let current = &table[resource as usize]; rlim.write(rlimit { - rlim_cur: RLIM_INFINITY, - rlim_max: RLIM_INFINITY, + rlim_cur: current.rlim_cur, + rlim_max: current.rlim_max, }); Ok(()) } unsafe fn setrlimit(resource: c_int, rlim: *const rlimit) -> Result<()> { - todo_skip!(0, "setrlimit({}, {:p}): not implemented", resource, rlim); - Err(Errno(EPERM)) + if resource < 0 || resource >= RLIMIT_NLIMITS as c_int { + return Err(Errno(EINVAL)); + } + if rlim.is_null() { + return Err(Errno(EFAULT)); + } + let new = unsafe { &*rlim }; + if new.rlim_cur > new.rlim_max { + return Err(Errno(EINVAL)); + } + let mut table = RLIMIT_TABLE.write(); + let old = &table[resource as usize]; + if new.rlim_max > old.rlim_max { + return Err(Errno(EPERM)); + } + table[resource as usize] = rlimit { + rlim_cur: new.rlim_cur, + rlim_max: new.rlim_max, + }; + Ok(()) } - fn getrusage(who: c_int, r_usage: Out) -> Result<()> { - todo_skip!(0, "getrusage({}, {:p}): not implemented", who, r_usage); + fn getrusage(who: c_int, mut r_usage: Out) -> Result<()> { + let clock_id = match who { + 0 /* RUSAGE_SELF */ => 2 /* CLOCK_PROCESS_CPUTIME_ID */, + 1 /* RUSAGE_THREAD */ => 3 /* CLOCK_THREAD_CPUTIME_ID */, + -1 /* RUSAGE_CHILDREN */ => { + r_usage.write(rusage { + ru_utime: timeval { tv_sec: 0, tv_usec: 0 }, + ru_stime: timeval { tv_sec: 0, tv_usec: 0 }, + ru_maxrss: 0, ru_ixrss: 0, ru_idrss: 0, ru_isrss: 0, + ru_minflt: 0, ru_majflt: 0, ru_nswap: 0, + ru_inblock: 0, ru_oublock: 0, ru_msgsnd: 0, ru_msgrcv: 0, + ru_nsignals: 0, ru_nvcsw: 0, ru_nivcsw: 0, + }); + return Ok(()); + } + _ => return Err(Errno(EINVAL)), + }; + + let mut redox_tp = syscall::TimeSpec::default(); + let clock_result = syscall::clock_gettime(clock_id, &mut redox_tp); + + let (tv_sec, tv_usec) = if clock_result.is_ok() { + let ts: timespec = (&redox_tp).into(); + (ts.tv_sec, (ts.tv_nsec / 1000) as _) + } else { + (0, 0) + }; + + r_usage.write(rusage { + ru_utime: timeval { tv_sec, tv_usec }, + ru_stime: timeval { tv_sec: 0, tv_usec: 0 }, + ru_maxrss: 0, ru_ixrss: 0, ru_idrss: 0, ru_isrss: 0, + ru_minflt: 0, ru_majflt: 0, ru_nswap: 0, + ru_inblock: 0, ru_oublock: 0, ru_msgsnd: 0, ru_msgrcv: 0, + ru_nsignals: 0, ru_nvcsw: 0, ru_nivcsw: 0, + }); Ok(()) } diff --git a/tests/sys_resource/rlimit_roundtrip.c b/tests/sys_resource/rlimit_roundtrip.c new file mode 100644 index 00000000..c90a6b79 --- /dev/null +++ b/tests/sys_resource/rlimit_roundtrip.c @@ -0,0 +1,80 @@ +#include +#include +#include +#include +#include +#include + +int main(void) { + struct rlimit original; + struct rlimit current; + struct rlimit invalid; + + errno = 0; + assert(getrlimit(RLIMIT_NOFILE, &original) == 0); + + errno = 0; + assert(getrlimit(RLIMIT_NLIMITS, ¤t) == -1); + assert(errno == EINVAL); + + errno = 0; + assert(getrlimit(RLIMIT_NOFILE, NULL) == -1); + assert(errno == EFAULT); + + errno = 0; + assert(getrusage(RUSAGE_SELF, NULL) == -1); + assert(errno == EFAULT); + + errno = 0; + assert(setrlimit(RLIMIT_NOFILE, NULL) == -1); + assert(errno == EFAULT); + + invalid.rlim_cur = original.rlim_max; + invalid.rlim_max = original.rlim_cur; + errno = 0; + assert(setrlimit(RLIMIT_NOFILE, &invalid) == -1); + assert(errno == EINVAL); + + if (original.rlim_max != RLIM_INFINITY) { + invalid.rlim_cur = original.rlim_max + 1; + invalid.rlim_max = original.rlim_max + 1; + errno = 0; + assert(setrlimit(RLIMIT_NOFILE, &invalid) == -1); + assert(errno == EPERM); + } + + current.rlim_cur = original.rlim_cur > 16 ? original.rlim_cur - 16 : original.rlim_cur; + current.rlim_max = original.rlim_max; + errno = 0; + assert(setrlimit(RLIMIT_NOFILE, ¤t) == 0); + + struct rlimit roundtrip; + errno = 0; + assert(getrlimit(RLIMIT_NOFILE, &roundtrip) == 0); + assert(roundtrip.rlim_cur == current.rlim_cur); + assert(roundtrip.rlim_max == current.rlim_max); + + long open_max = sysconf(_SC_OPEN_MAX); + if (current.rlim_cur == RLIM_INFINITY) { + assert(open_max == -1); + } else if (current.rlim_cur > LONG_MAX) { + assert(open_max == LONG_MAX); + } else { + assert(open_max == (long)current.rlim_cur); + } + + if (current.rlim_cur > INT_MAX) { + assert(getdtablesize() == INT_MAX); + } else { + assert(getdtablesize() == (int)current.rlim_cur); + } + + errno = 0; + assert(setrlimit(RLIMIT_NOFILE, &original) == 0); + assert(getrlimit(RLIMIT_NOFILE, &roundtrip) == 0); + assert(roundtrip.rlim_cur == original.rlim_cur); + assert(roundtrip.rlim_max == original.rlim_max); + + puts("rlimit roundtrip ok"); + return 0; +}