redox: implement getrlimit/setrlimit with a per-process limits table (finite RLIMIT_NOFILE)
This commit is contained in:
@@ -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,
|
||||
|
||||
@@ -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<()> {
|
||||
|
||||
Reference in New Issue
Block a user