Files
RedBear-OS/local/patches/relibc/P11-getrlimit-getrusage.patch
T
vasilito 11993af01f fix: rebase base patches, commit recipe drift, add relibc rlimit/sysconf
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.
2026-05-04 11:49:15 +01:00

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, &current) == -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, &current) == 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;
+}