From 323b95416b0ba408785da76d4d3e81873fe37a77 Mon Sep 17 00:00:00 2001 From: Red Bear OS Date: Thu, 16 Jul 2026 06:39:43 +0900 Subject: [PATCH] redox: implement getrlimit/setrlimit with a per-process limits table (finite RLIMIT_NOFILE) --- src/header/sys_resource/mod.rs | 1 + src/platform/redox/mod.rs | 71 +++++++++++++++++++++++++++++----- 2 files changed, 63 insertions(+), 9 deletions(-) diff --git a/src/header/sys_resource/mod.rs b/src/header/sys_resource/mod.rs index 74a7e067a5..3eec55be70 100644 --- a/src/header/sys_resource/mod.rs +++ b/src/header/sys_resource/mod.rs @@ -98,6 +98,7 @@ pub const RLIMIT_NLIMITS: c_int = 15; pub type rlim_t = c_ulonglong; #[repr(C)] +#[derive(Clone, Copy)] pub struct rlimit { /// The current (soft) limit. pub rlim_cur: rlim_t, diff --git a/src/platform/redox/mod.rs b/src/platform/redox/mod.rs index db2895d701..628c7ffad8 100644 --- a/src/platform/redox/mod.rs +++ b/src/platform/redox/mod.rs @@ -44,7 +44,10 @@ use crate::{ sys_file, sys_mman::{MAP_ANONYMOUS, PROT_READ, PROT_WRITE}, sys_random, - sys_resource::{PRIO_PROCESS, RLIM_INFINITY, rlimit, rusage, setpriority}, + sys_resource::{ + PRIO_PROCESS, RLIM_INFINITY, RLIMIT_NLIMITS, RLIMIT_NOFILE, rlimit, rusage, + setpriority, + }, sys_select::timeval, sys_stat::{S_ISGID, S_ISUID, S_ISVTX, stat}, sys_statvfs::statvfs, @@ -66,11 +69,43 @@ use crate::{ timer::{TIMERS, timer_routine, timer_update_wake_time}, }, }, - sync::rwlock::RwLock, + sync::{Mutex, rwlock::RwLock}, }; pub use redox_rt::proc::FdGuard; +/// Per-process resource limits (`getrlimit`/`setrlimit`). +/// +/// Redox does not enforce most resource limits in the kernel, but POSIX +/// requires `setrlimit`/`getrlimit` to round-trip values, and programs read +/// them at startup. We therefore maintain the limits here in userspace. +/// +/// `RLIMIT_NOFILE` is finite by default because programs iterate up to it +/// (e.g. closing every possible fd) — returning `RLIM_INFINITY` there would +/// make such loops run essentially forever. Every other resource defaults to +/// unlimited. Note: these are not yet inherited across `execve` (each new +/// image starts from these defaults); that refinement would require passing +/// the table through the exec boundary. +static RLIMITS: Mutex<[rlimit; RLIM_COUNT]> = Mutex::new(default_rlimits()); + +/// Number of `RLIMIT_*` resources tracked (`RLIMIT_NLIMITS` is the highest +/// index, so the table has one more entry than that). +const RLIM_COUNT: usize = RLIMIT_NLIMITS as usize + 1; + +const fn default_rlimits() -> [rlimit; RLIM_COUNT] { + let mut arr = [rlimit { + rlim_cur: RLIM_INFINITY, + rlim_max: RLIM_INFINITY, + }; RLIM_COUNT]; + // Finite open-file limit (soft 1024, hard 65536), matching common + // Unix defaults, so fd-closing loops terminate. + arr[RLIMIT_NOFILE as usize] = rlimit { + rlim_cur: 1024, + rlim_max: 65536, + }; + arr +} + mod epoll; mod event; pub(crate) mod exec; @@ -713,17 +748,35 @@ impl Pal for Sys { } fn getrlimit(resource: c_int, mut rlim: Out) -> Result<()> { - todo_skip!(0, "getrlimit({}, {:p}): not implemented", resource, rlim); - rlim.write(rlimit { - rlim_cur: RLIM_INFINITY, - rlim_max: RLIM_INFINITY, - }); + let idx = usize::try_from(resource).map_err(|_| Errno(EINVAL))?; + if idx >= RLIM_COUNT { + return Err(Errno(EINVAL)); + } + let limits = RLIMITS.lock(); + rlim.write(limits[idx]); Ok(()) } unsafe fn setrlimit(resource: c_int, rlim: *const rlimit) -> Result<()> { - todo_skip!(0, "setrlimit({}, {:p}): not implemented", resource, rlim); - Err(Errno(EPERM)) + let idx = usize::try_from(resource).map_err(|_| Errno(EINVAL))?; + if idx >= RLIM_COUNT { + return Err(Errno(EINVAL)); + } + if rlim.is_null() { + return Err(Errno(EFAULT)); + } + let new = unsafe { rlim.read() }; + // The soft limit may not exceed the hard limit. + if new.rlim_cur > new.rlim_max { + return Err(Errno(EINVAL)); + } + // Redox does not enforce limits, so any well-formed request is + // accepted and recorded (a subsequent getrlimit returns it). A real + // privilege model would additionally forbid raising the hard limit; + // there is none here yet, so both bounds are stored as given. + let mut limits = RLIMITS.lock(); + limits[idx] = new; + Ok(()) } fn getrusage(_who: c_int, mut r_usage: Out) -> Result<()> {