Refactor the implementation of prlimit64
This commit is contained in:
parent
c91d383c91
commit
82ccfcd4c6
|
|
@ -1,15 +1,16 @@
|
|||
// SPDX-License-Identifier: MPL-2.0
|
||||
// FIXME: The resource limits should be respected by the corresponding subsystems of the kernel.
|
||||
|
||||
#![expect(non_camel_case_types)]
|
||||
|
||||
use core::{
|
||||
array,
|
||||
sync::atomic::{AtomicU64, Ordering},
|
||||
};
|
||||
|
||||
use super::process_vm::{INIT_STACK_SIZE, USER_HEAP_SIZE_LIMIT};
|
||||
use crate::prelude::*;
|
||||
use crate::{
|
||||
prelude::*,
|
||||
process::{UserNamespace, credentials::capabilities::CapSet},
|
||||
};
|
||||
|
||||
// Constants for the boot-time rlimit defaults
|
||||
// See https://github.com/torvalds/linux/blob/fac04efc5c793dccbd07e2d59af9f90b7fc0dca4/include/asm-generic/resource.h#L11
|
||||
|
|
@ -34,7 +35,7 @@ pub struct ResourceLimits {
|
|||
}
|
||||
|
||||
impl ResourceLimits {
|
||||
// Get a reference to a specific resource limit
|
||||
/// Returns a reference to a specific resource limit.
|
||||
pub fn get_rlimit(&self, resource: ResourceType) -> &RLimit64 {
|
||||
&self.rlimits[resource as usize]
|
||||
}
|
||||
|
|
@ -44,38 +45,32 @@ impl Default for ResourceLimits {
|
|||
fn default() -> Self {
|
||||
let mut rlimits: [RLimit64; RLIMIT_COUNT] = array::from_fn(|_| RLimit64::default());
|
||||
|
||||
// Setting the resource limits with predefined values
|
||||
rlimits[ResourceType::RLIMIT_CPU as usize] =
|
||||
RLimit64::new(RLIM_INFINITY, RLIM_INFINITY).unwrap();
|
||||
rlimits[ResourceType::RLIMIT_FSIZE as usize] =
|
||||
RLimit64::new(RLIM_INFINITY, RLIM_INFINITY).unwrap();
|
||||
// Sets the resource limits with predefined values
|
||||
rlimits[ResourceType::RLIMIT_CPU as usize] = RLimit64::new(RLIM_INFINITY, RLIM_INFINITY);
|
||||
rlimits[ResourceType::RLIMIT_FSIZE as usize] = RLimit64::new(RLIM_INFINITY, RLIM_INFINITY);
|
||||
rlimits[ResourceType::RLIMIT_DATA as usize] =
|
||||
RLimit64::new(USER_HEAP_SIZE_LIMIT as u64, RLIM_INFINITY).unwrap();
|
||||
RLimit64::new(USER_HEAP_SIZE_LIMIT as u64, RLIM_INFINITY);
|
||||
rlimits[ResourceType::RLIMIT_STACK as usize] =
|
||||
RLimit64::new(INIT_STACK_SIZE as u64, RLIM_INFINITY).unwrap();
|
||||
rlimits[ResourceType::RLIMIT_CORE as usize] = RLimit64::new(0, RLIM_INFINITY).unwrap();
|
||||
rlimits[ResourceType::RLIMIT_RSS as usize] =
|
||||
RLimit64::new(RLIM_INFINITY, RLIM_INFINITY).unwrap();
|
||||
RLimit64::new(INIT_STACK_SIZE as u64, RLIM_INFINITY);
|
||||
rlimits[ResourceType::RLIMIT_CORE as usize] = RLimit64::new(0, RLIM_INFINITY);
|
||||
rlimits[ResourceType::RLIMIT_RSS as usize] = RLimit64::new(RLIM_INFINITY, RLIM_INFINITY);
|
||||
rlimits[ResourceType::RLIMIT_NPROC as usize] =
|
||||
RLimit64::new(INIT_RLIMIT_NPROC, INIT_RLIMIT_NPROC).unwrap();
|
||||
RLimit64::new(INIT_RLIMIT_NPROC, INIT_RLIMIT_NPROC);
|
||||
rlimits[ResourceType::RLIMIT_NOFILE as usize] =
|
||||
RLimit64::new(INIT_RLIMIT_NOFILE_CUR, INIT_RLIMIT_NOFILE_MAX).unwrap();
|
||||
RLimit64::new(INIT_RLIMIT_NOFILE_CUR, INIT_RLIMIT_NOFILE_MAX);
|
||||
rlimits[ResourceType::RLIMIT_MEMLOCK as usize] =
|
||||
RLimit64::new(INIT_RLIMIT_MEMLOCK, INIT_RLIMIT_MEMLOCK).unwrap();
|
||||
rlimits[ResourceType::RLIMIT_AS as usize] =
|
||||
RLimit64::new(RLIM_INFINITY, RLIM_INFINITY).unwrap();
|
||||
rlimits[ResourceType::RLIMIT_LOCKS as usize] =
|
||||
RLimit64::new(RLIM_INFINITY, RLIM_INFINITY).unwrap();
|
||||
RLimit64::new(INIT_RLIMIT_MEMLOCK, INIT_RLIMIT_MEMLOCK);
|
||||
rlimits[ResourceType::RLIMIT_AS as usize] = RLimit64::new(RLIM_INFINITY, RLIM_INFINITY);
|
||||
rlimits[ResourceType::RLIMIT_LOCKS as usize] = RLimit64::new(RLIM_INFINITY, RLIM_INFINITY);
|
||||
rlimits[ResourceType::RLIMIT_SIGPENDING as usize] =
|
||||
RLimit64::new(INIT_RLIMIT_SIGPENDING, INIT_RLIMIT_SIGPENDING).unwrap();
|
||||
RLimit64::new(INIT_RLIMIT_SIGPENDING, INIT_RLIMIT_SIGPENDING);
|
||||
rlimits[ResourceType::RLIMIT_MSGQUEUE as usize] =
|
||||
RLimit64::new(INIT_RLIMIT_MSGQUEUE, INIT_RLIMIT_MSGQUEUE).unwrap();
|
||||
RLimit64::new(INIT_RLIMIT_MSGQUEUE, INIT_RLIMIT_MSGQUEUE);
|
||||
rlimits[ResourceType::RLIMIT_NICE as usize] =
|
||||
RLimit64::new(INIT_RLIMIT_NICE, INIT_RLIMIT_NICE).unwrap();
|
||||
RLimit64::new(INIT_RLIMIT_NICE, INIT_RLIMIT_NICE);
|
||||
rlimits[ResourceType::RLIMIT_RTPRIO as usize] =
|
||||
RLimit64::new(INIT_RLIMIT_RTPRIO, INIT_RLIMIT_RTPRIO).unwrap();
|
||||
rlimits[ResourceType::RLIMIT_RTTIME as usize] =
|
||||
RLimit64::new(RLIM_INFINITY, RLIM_INFINITY).unwrap();
|
||||
RLimit64::new(INIT_RLIMIT_RTPRIO, INIT_RLIMIT_RTPRIO);
|
||||
rlimits[ResourceType::RLIMIT_RTTIME as usize] = RLimit64::new(RLIM_INFINITY, RLIM_INFINITY);
|
||||
|
||||
ResourceLimits { rlimits }
|
||||
}
|
||||
|
|
@ -83,6 +78,7 @@ impl Default for ResourceLimits {
|
|||
|
||||
#[repr(u32)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, TryFromInt)]
|
||||
#[expect(non_camel_case_types)]
|
||||
pub enum ResourceType {
|
||||
RLIMIT_CPU = 0,
|
||||
RLIMIT_FSIZE = 1,
|
||||
|
|
@ -102,7 +98,7 @@ pub enum ResourceType {
|
|||
RLIMIT_RTTIME = 15,
|
||||
}
|
||||
|
||||
pub const RLIMIT_COUNT: usize = 16;
|
||||
const RLIMIT_COUNT: usize = 16;
|
||||
|
||||
#[derive(Debug, Clone, Copy, Pod)]
|
||||
#[repr(C)]
|
||||
|
|
@ -112,7 +108,6 @@ pub struct RawRLimit64 {
|
|||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[repr(C)]
|
||||
pub struct RLimit64 {
|
||||
cur: AtomicU64,
|
||||
max: AtomicU64,
|
||||
|
|
@ -120,47 +115,68 @@ pub struct RLimit64 {
|
|||
}
|
||||
|
||||
impl RLimit64 {
|
||||
pub fn new(cur_: u64, max_: u64) -> Result<Self> {
|
||||
if cur_ > max_ {
|
||||
return_errno_with_message!(Errno::EINVAL, "invalid rlimit");
|
||||
}
|
||||
Ok(Self {
|
||||
cur: AtomicU64::new(cur_),
|
||||
max: AtomicU64::new(max_),
|
||||
#[track_caller]
|
||||
pub(self) const fn new(cur: u64, max: u64) -> Self {
|
||||
assert!(cur <= max, "the current rlimit exceeds the max rlimit");
|
||||
Self {
|
||||
cur: AtomicU64::new(cur),
|
||||
max: AtomicU64::new(max),
|
||||
lock: SpinLock::new(()),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets the current rlimit without synchronization.
|
||||
/// Returns the current rlimit without synchronization.
|
||||
pub fn get_cur(&self) -> u64 {
|
||||
self.cur.load(Ordering::Relaxed)
|
||||
}
|
||||
|
||||
/// Gets the max rlimit without synchronization.
|
||||
pub fn get_max(&self) -> u64 {
|
||||
/// Returns the max rlimit without synchronization.
|
||||
fn get_max(&self) -> u64 {
|
||||
self.max.load(Ordering::Relaxed)
|
||||
}
|
||||
|
||||
/// Gets the rlimit with synchronization.
|
||||
///
|
||||
/// Only called when handling the `getrlimit` or `prlimit` syscall.
|
||||
pub fn get_cur_and_max(&self) -> (u64, u64) {
|
||||
pub fn get_raw_rlimit(&self) -> RawRLimit64 {
|
||||
let _guard = self.lock.lock();
|
||||
(self.get_cur(), self.get_max())
|
||||
RawRLimit64 {
|
||||
cur: self.cur.load(Ordering::Relaxed),
|
||||
max: self.max.load(Ordering::Relaxed),
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the rlimit with synchronization.
|
||||
/// Sets the rlimit with synchronization and returns the old value.
|
||||
///
|
||||
/// Only called when handling the `setrlimit` or `prlimit` syscall
|
||||
/// or during init process creation.
|
||||
pub fn set_cur_and_max(&self, new_cur: u64, new_max: u64) -> Result<()> {
|
||||
if new_cur > new_max {
|
||||
return_errno_with_message!(Errno::EINVAL, "invalid rlimit");
|
||||
/// Only called when handling the `setrlimit` or `prlimit` syscall.
|
||||
pub fn set_raw_rlimit(&self, new: RawRLimit64, ctx: &Context) -> Result<RawRLimit64> {
|
||||
if new.cur > new.max {
|
||||
return_errno_with_message!(Errno::EINVAL, "the current rlimit exceeds the max rlimit");
|
||||
}
|
||||
let _guard = self.lock.lock();
|
||||
self.cur.store(new_cur, Ordering::Relaxed);
|
||||
self.max.store(new_max, Ordering::Relaxed);
|
||||
Ok(())
|
||||
if new.max > self.get_max() {
|
||||
let init_user_ns = UserNamespace::get_init_singleton();
|
||||
init_user_ns.check_cap(CapSet::SYS_RESOURCE, ctx.posix_thread)?;
|
||||
}
|
||||
let old = RawRLimit64 {
|
||||
cur: self.cur.load(Ordering::Relaxed),
|
||||
max: self.max.load(Ordering::Relaxed),
|
||||
};
|
||||
self.set_raw_rlimit_unchecked(new);
|
||||
Ok(old)
|
||||
}
|
||||
|
||||
/// Sets the rlimit _without_ synchronization and permission check.
|
||||
///
|
||||
/// Only called during init process creation.
|
||||
#[track_caller]
|
||||
pub(self) fn set_raw_rlimit_unchecked(&self, new: RawRLimit64) {
|
||||
assert!(
|
||||
new.cur <= new.max,
|
||||
"the current rlimit exceeds the max rlimit"
|
||||
);
|
||||
self.cur.store(new.cur, Ordering::Relaxed);
|
||||
self.max.store(new.max, Ordering::Relaxed);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -176,10 +192,10 @@ impl Default for RLimit64 {
|
|||
|
||||
impl Clone for RLimit64 {
|
||||
fn clone(&self) -> Self {
|
||||
let (cur, max) = self.get_cur_and_max();
|
||||
let raw_limit = self.get_raw_rlimit();
|
||||
Self {
|
||||
cur: AtomicU64::new(cur),
|
||||
max: AtomicU64::new(max),
|
||||
cur: AtomicU64::new(raw_limit.cur),
|
||||
max: AtomicU64::new(raw_limit.max),
|
||||
lock: SpinLock::new(()),
|
||||
}
|
||||
}
|
||||
|
|
@ -196,13 +212,15 @@ pub(super) fn new_resource_limits_for_init() -> ResourceLimits {
|
|||
// and other factors.
|
||||
// Reference: <https://elixir.bootlin.com/linux/v6.16.9/source/kernel/fork.c#L761>
|
||||
let max_threads: u64 = 100000;
|
||||
let raw_rlimit = RawRLimit64 {
|
||||
cur: max_threads / 2,
|
||||
max: max_threads / 2,
|
||||
};
|
||||
resource_limits
|
||||
.get_rlimit(ResourceType::RLIMIT_NPROC)
|
||||
.set_cur_and_max(max_threads / 2, max_threads / 2)
|
||||
.unwrap();
|
||||
.set_raw_rlimit_unchecked(raw_rlimit);
|
||||
resource_limits
|
||||
.get_rlimit(ResourceType::RLIMIT_SIGPENDING)
|
||||
.set_cur_and_max(max_threads / 2, max_threads / 2)
|
||||
.unwrap();
|
||||
.set_raw_rlimit_unchecked(raw_rlimit);
|
||||
resource_limits
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,39 +8,21 @@ use crate::{
|
|||
process::{
|
||||
Pid, Process, ResourceType,
|
||||
credentials::capabilities::CapSet,
|
||||
posix_thread::{AsPosixThread, PosixThread},
|
||||
posix_thread::AsPosixThread,
|
||||
process_table,
|
||||
rlimit::{RawRLimit64, SYSCTL_NR_OPEN},
|
||||
},
|
||||
};
|
||||
|
||||
pub fn sys_getrlimit(resource: u32, rlim_addr: Vaddr, ctx: &Context) -> Result<SyscallReturn> {
|
||||
let resource = ResourceType::try_from(resource)?;
|
||||
debug!("resource = {:?}, rlim_addr = 0x{:x}", resource, rlim_addr);
|
||||
let resource_limits = ctx.process.resource_limits();
|
||||
let rlimit = resource_limits.get_rlimit(resource);
|
||||
let (cur, max) = rlimit.get_cur_and_max();
|
||||
let rlimit_raw = RawRLimit64 { cur, max };
|
||||
ctx.user_space().write_val(rlim_addr, &rlimit_raw)?;
|
||||
let old_raw = do_prlimit64(&ctx.process, resource, None, ctx)?;
|
||||
ctx.user_space().write_val(rlim_addr, &old_raw)?;
|
||||
Ok(SyscallReturn::Return(0))
|
||||
}
|
||||
|
||||
pub fn sys_setrlimit(resource: u32, new_rlim_addr: Vaddr, ctx: &Context) -> Result<SyscallReturn> {
|
||||
let resource = ResourceType::try_from(resource)?;
|
||||
debug!(
|
||||
"resource = {:?}, new_rlim_addr = 0x{:x}",
|
||||
resource, new_rlim_addr
|
||||
);
|
||||
let new_raw: RawRLimit64 = ctx.user_space().read_val(new_rlim_addr)?;
|
||||
let resource_limits = ctx.process.resource_limits();
|
||||
|
||||
if resource == ResourceType::RLIMIT_NOFILE && new_raw.max > SYSCTL_NR_OPEN {
|
||||
return_errno_with_message!(Errno::EPERM, "the new limit exceeds the system limit");
|
||||
}
|
||||
|
||||
resource_limits
|
||||
.get_rlimit(resource)
|
||||
.set_cur_and_max(new_raw.cur, new_raw.max)?;
|
||||
let new_raw = ctx.user_space().read_val(new_rlim_addr)?;
|
||||
do_prlimit64(&ctx.process, resource, Some(new_raw), ctx)?;
|
||||
Ok(SyscallReturn::Return(0))
|
||||
}
|
||||
|
||||
|
|
@ -51,59 +33,67 @@ pub fn sys_prlimit64(
|
|||
old_rlim_addr: Vaddr,
|
||||
ctx: &Context,
|
||||
) -> Result<SyscallReturn> {
|
||||
let resource = ResourceType::try_from(resource)?;
|
||||
debug!(
|
||||
"pid = {}, resource = {:?}, new_rlim_addr = 0x{:x}, old_rlim_addr = 0x{:x}",
|
||||
pid, resource, new_rlim_addr, old_rlim_addr
|
||||
);
|
||||
let userspace = ctx.user_space();
|
||||
|
||||
let get_and_set_rlimit = |process: &Process| -> Result<()> {
|
||||
let resource_limits = process.resource_limits();
|
||||
|
||||
if old_rlim_addr != 0 {
|
||||
let rlimit = resource_limits.get_rlimit(resource);
|
||||
let (cur, max) = rlimit.get_cur_and_max();
|
||||
let rlimit_raw = RawRLimit64 { cur, max };
|
||||
ctx.user_space().write_val(old_rlim_addr, &rlimit_raw)?;
|
||||
}
|
||||
if new_rlim_addr != 0 {
|
||||
let new_raw: RawRLimit64 = ctx.user_space().read_val(new_rlim_addr)?;
|
||||
debug!("new_rlimit = {:?}", new_raw);
|
||||
if resource == ResourceType::RLIMIT_NOFILE && new_raw.max > SYSCTL_NR_OPEN {
|
||||
return_errno_with_message!(Errno::EPERM, "the new limit exceeds the system limit");
|
||||
}
|
||||
|
||||
resource_limits
|
||||
.get_rlimit(resource)
|
||||
.set_cur_and_max(new_raw.cur, new_raw.max)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
let new_raw = if new_rlim_addr == 0 {
|
||||
None
|
||||
} else {
|
||||
Some(userspace.read_val(new_rlim_addr)?)
|
||||
};
|
||||
|
||||
if pid == 0 || pid == ctx.process.pid() {
|
||||
get_and_set_rlimit(ctx.process.as_ref())?;
|
||||
let old_raw = if pid == 0 || pid == ctx.process.pid() {
|
||||
do_prlimit64(&ctx.process, resource, new_raw, ctx)?
|
||||
} else {
|
||||
let target_process = process_table::get_process(pid).ok_or_else(|| {
|
||||
Error::with_message(Errno::ESRCH, "the target process does not exist")
|
||||
})?;
|
||||
|
||||
// Check permissions
|
||||
check_rlimit_perm(target_process.main_thread().as_posix_thread().unwrap(), ctx)?;
|
||||
get_and_set_rlimit(target_process.as_ref())?;
|
||||
check_rlimit_perm(&target_process, ctx)?;
|
||||
do_prlimit64(&target_process, resource, new_raw, ctx)?
|
||||
};
|
||||
|
||||
if old_rlim_addr != 0 {
|
||||
userspace.write_val(old_rlim_addr, &old_raw)?;
|
||||
}
|
||||
|
||||
Ok(SyscallReturn::Return(0))
|
||||
}
|
||||
|
||||
fn do_prlimit64(
|
||||
target_process: &Process,
|
||||
resource: u32,
|
||||
new_raw: Option<RawRLimit64>,
|
||||
ctx: &Context,
|
||||
) -> Result<RawRLimit64> {
|
||||
let resource = ResourceType::try_from(resource)?;
|
||||
debug!(
|
||||
"pid = {}, resource = {:?}, new_raw = {:?}",
|
||||
target_process.pid(),
|
||||
resource,
|
||||
new_raw,
|
||||
);
|
||||
|
||||
let rlimit = {
|
||||
let resource_limits = target_process.resource_limits();
|
||||
resource_limits.get_rlimit(resource)
|
||||
};
|
||||
|
||||
let old_raw = if let Some(new_raw) = new_raw {
|
||||
if resource == ResourceType::RLIMIT_NOFILE && new_raw.max > SYSCTL_NR_OPEN {
|
||||
return_errno_with_message!(Errno::EPERM, "the new limit exceeds the system limit");
|
||||
}
|
||||
rlimit.set_raw_rlimit(new_raw, ctx)?
|
||||
} else {
|
||||
rlimit.get_raw_rlimit()
|
||||
};
|
||||
|
||||
Ok(old_raw)
|
||||
}
|
||||
|
||||
/// Checks whether the current process has permission to access
|
||||
/// the resource limits of the target process.
|
||||
// Reference: <https://man7.org/linux/man-pages/man2/prlimit.2.html>
|
||||
fn check_rlimit_perm(target: &PosixThread, ctx: &Context) -> Result<()> {
|
||||
let target_process = target.process();
|
||||
|
||||
let current_cred = ctx.posix_thread.credentials();
|
||||
let target_cred = target.credentials();
|
||||
|
||||
fn check_rlimit_perm(target_process: &Process, ctx: &Context) -> Result<()> {
|
||||
if target_process
|
||||
.user_ns()
|
||||
.lock()
|
||||
|
|
@ -113,8 +103,16 @@ fn check_rlimit_perm(target: &PosixThread, ctx: &Context) -> Result<()> {
|
|||
return Ok(());
|
||||
}
|
||||
|
||||
let current_ruid = current_cred.ruid();
|
||||
let current_rgid = current_cred.rgid();
|
||||
let (current_ruid, current_rgid) = {
|
||||
let current_cred = ctx.posix_thread.credentials();
|
||||
(current_cred.ruid(), current_cred.rgid())
|
||||
};
|
||||
|
||||
let target_cred = target_process
|
||||
.main_thread()
|
||||
.as_posix_thread()
|
||||
.unwrap()
|
||||
.credentials();
|
||||
|
||||
if current_ruid == target_cred.ruid()
|
||||
&& current_ruid == target_cred.euid()
|
||||
|
|
|
|||
|
|
@ -1446,8 +1446,8 @@ setreuid06
|
|||
# setreuid07_16
|
||||
|
||||
# setrlimit01
|
||||
# setrlimit02
|
||||
# setrlimit03
|
||||
setrlimit02
|
||||
setrlimit03
|
||||
setrlimit04
|
||||
setrlimit05
|
||||
# setrlimit06
|
||||
|
|
|
|||
Loading…
Reference in New Issue