redox: implement getrlimit/setrlimit with a per-process limits table (finite RLIMIT_NOFILE)

This commit is contained in:
Red Bear OS
2026-07-16 06:39:43 +09:00
parent 9c106e3cea
commit 323b95416b
2 changed files with 63 additions and 9 deletions
+1
View File
@@ -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,
+62 -9
View File
@@ -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<rlimit>) -> 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<rusage>) -> Result<()> {