11993af01f
Base: fix P6-driver-new-modules.patch (ed format -> unified diff) for new driver modules (ncq, itr, phy). P6-driver-main-fixes.patch now applies with offset on current upstream source. Relibc: remove stale P5-named-semaphores (upstream has stubs), add P10-stack-size-8mb and P11-getrlimit-getrusage (per-process rlimit table, sysconf integration, getdtablesize fix, null-pointer safety). Kernel: consolidate 29 individual patches into single redbear-consolidated.patch. Userutils: P5-redbear-branding replaces P4-login-rate-limit. Recipe.toml changes now committed so they survive source resets.
350 lines
13 KiB
Diff
350 lines
13 KiB
Diff
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 <https://pubs.opengroup.org/onlinepubs/9799919799/functions/getrlimit.html>.
|
|
#[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 <https://pubs.opengroup.org/onlinepubs/9799919799/functions/getrusage.html>.
|
|
#[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::<crate::header::sys_resource::rlimit>::uninit();
|
|
+ let r = unsafe {
|
|
+ crate::header::sys_resource::getrlimit(
|
|
+ crate::header::sys_resource::RLIMIT_NPROC as c_int,
|
|
+ lim.as_mut_ptr().cast::<crate::header::sys_resource::rlimit>(),
|
|
+ )
|
|
+ };
|
|
+ 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::<crate::header::sys_resource::rlimit>::uninit();
|
|
+ let r = unsafe {
|
|
+ crate::header::sys_resource::getrlimit(
|
|
+ crate::header::sys_resource::RLIMIT_NOFILE as c_int,
|
|
+ lim.as_mut_ptr().cast::<crate::header::sys_resource::rlimit>(),
|
|
+ )
|
|
+ };
|
|
+ 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::<sys_resource::rlimit>::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<rlimit>) -> 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<rusage>) -> Result<()> {
|
|
- todo_skip!(0, "getrusage({}, {:p}): not implemented", who, r_usage);
|
|
+ fn getrusage(who: c_int, mut r_usage: Out<rusage>) -> 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 <assert.h>
|
|
+#include <errno.h>
|
|
+#include <limits.h>
|
|
+#include <stdio.h>
|
|
+#include <sys/resource.h>
|
|
+#include <unistd.h>
|
|
+
|
|
+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;
|
|
+}
|