// SPDX-License-Identifier: MPL-2.0 use super::SyscallReturn; use crate::{ prelude::*, process::{ credentials::SecureBits, posix_thread::MAX_THREAD_NAME_LEN, signal::sig_num::SigNum, }, }; pub fn sys_prctl( option: i32, arg2: u64, arg3: u64, arg4: u64, arg5: u64, ctx: &Context, ) -> Result { let prctl_cmd = PrctlCmd::from_args(option, arg2, arg3, arg4, arg5)?; debug!("prctl cmd = {:x?}", prctl_cmd); match prctl_cmd { PrctlCmd::PR_SET_PDEATHSIG(signum) => { ctx.process.set_parent_death_signal(signum); } PrctlCmd::PR_GET_PDEATHSIG(write_to_addr) => { let write_val = { match ctx.process.parent_death_signal() { None => 0i32, Some(signum) => signum.as_u8() as i32, } }; ctx.user_space().write_val(write_to_addr, &write_val)?; } PrctlCmd::PR_GET_DUMPABLE => { // TODO: when coredump is supported, return the actual value return Ok(SyscallReturn::Return(Dumpable::Disable as _)); } PrctlCmd::PR_SET_DUMPABLE(dumpable) => { if dumpable != Dumpable::Disable && dumpable != Dumpable::User { return_errno!(Errno::EINVAL) } // TODO: implement coredump } PrctlCmd::PR_GET_KEEPCAPS => { let keep_cap = { let credentials = ctx.posix_thread.credentials(); if credentials.keep_capabilities() { 1 } else { 0 } }; return Ok(SyscallReturn::Return(keep_cap as _)); } PrctlCmd::PR_SET_KEEPCAPS(keep_cap) => { if keep_cap > 1 { return_errno!(Errno::EINVAL) } let credentials = ctx.posix_thread.credentials_mut(); credentials.set_keep_capabilities(keep_cap != 0)?; } PrctlCmd::PR_GET_NAME(write_to_addr) => { let thread_name = ctx.posix_thread.thread_name().lock(); ctx.user_space().write_bytes( write_to_addr, &mut VmReader::from(thread_name.name().to_bytes_with_nul()), )?; } PrctlCmd::PR_SET_NAME(read_addr) => { let new_thread_name = ctx .user_space() .read_cstring(read_addr, MAX_THREAD_NAME_LEN)?; let mut thread_name = ctx.posix_thread.thread_name().lock(); thread_name.set_name(&new_thread_name); } PrctlCmd::PR_SET_CHILD_SUBREAPER(is_set) => { let process = ctx.process.as_ref(); if is_set { process.set_child_subreaper(); } else { process.unset_child_subreaper(); } } PrctlCmd::PR_GET_CHILD_SUBREAPER(write_addr) => { let process = ctx.process.as_ref(); ctx.user_space() .write_val(write_addr, &(process.is_child_subreaper() as u32))?; } PrctlCmd::PR_GET_SECUREBITS => { let credentials = ctx.posix_thread.credentials(); let securebits = credentials.securebits(); return Ok(SyscallReturn::Return(securebits.bits() as _)); } PrctlCmd::PR_SET_SECUREBITS(securebits) => { let credentials = ctx.posix_thread.credentials_mut(); credentials.set_securebits(securebits)?; } _ => todo!(), } Ok(SyscallReturn::Return(0)) } const PR_SET_PDEATHSIG: i32 = 1; const PR_GET_PDEATHSIG: i32 = 2; const PR_GET_DUMPABLE: i32 = 3; const PR_SET_DUMPABLE: i32 = 4; const PR_GET_KEEPCAPS: i32 = 7; const PR_SET_KEEPCAPS: i32 = 8; const PR_SET_NAME: i32 = 15; const PR_GET_NAME: i32 = 16; const PR_GET_SECUREBITS: i32 = 27; const PR_SET_SECUREBITS: i32 = 28; const PR_SET_TIMERSLACK: i32 = 29; const PR_GET_TIMERSLACK: i32 = 30; const PR_SET_CHILD_SUBREAPER: i32 = 36; const PR_GET_CHILD_SUBREAPER: i32 = 37; #[expect(non_camel_case_types)] #[derive(Debug, Clone, Copy)] pub enum PrctlCmd { PR_SET_PDEATHSIG(SigNum), PR_GET_PDEATHSIG(Vaddr), PR_SET_NAME(Vaddr), PR_GET_NAME(Vaddr), PR_GET_KEEPCAPS, PR_SET_KEEPCAPS(u32), #[expect(dead_code)] PR_SET_TIMERSLACK(u64), #[expect(dead_code)] PR_GET_TIMERSLACK, PR_SET_DUMPABLE(Dumpable), PR_GET_DUMPABLE, PR_SET_CHILD_SUBREAPER(bool), PR_GET_CHILD_SUBREAPER(Vaddr), PR_GET_SECUREBITS, PR_SET_SECUREBITS(SecureBits), } #[repr(u64)] #[derive(Debug, Clone, Copy, PartialEq, Eq, TryFromInt)] pub enum Dumpable { Disable = 0, /* No setuid dumping */ User = 1, /* Dump as user of process */ Root = 2, /* Dump as root */ } impl PrctlCmd { fn from_args(option: i32, arg2: u64, _arg3: u64, _arg4: u64, _arg5: u64) -> Result { match option { PR_SET_PDEATHSIG => { let signum = SigNum::try_from(arg2 as u8)?; Ok(PrctlCmd::PR_SET_PDEATHSIG(signum)) } PR_GET_PDEATHSIG => Ok(PrctlCmd::PR_GET_PDEATHSIG(arg2 as _)), PR_GET_DUMPABLE => Ok(PrctlCmd::PR_GET_DUMPABLE), PR_SET_DUMPABLE => Ok(PrctlCmd::PR_SET_DUMPABLE(Dumpable::try_from(arg2)?)), PR_SET_NAME => Ok(PrctlCmd::PR_SET_NAME(arg2 as _)), PR_GET_NAME => Ok(PrctlCmd::PR_GET_NAME(arg2 as _)), PR_GET_TIMERSLACK => todo!(), PR_SET_TIMERSLACK => todo!(), PR_GET_KEEPCAPS => Ok(PrctlCmd::PR_GET_KEEPCAPS), PR_SET_KEEPCAPS => Ok(PrctlCmd::PR_SET_KEEPCAPS(arg2 as _)), PR_SET_CHILD_SUBREAPER => Ok(PrctlCmd::PR_SET_CHILD_SUBREAPER(arg2 > 0)), PR_GET_CHILD_SUBREAPER => Ok(PrctlCmd::PR_GET_CHILD_SUBREAPER(arg2 as _)), PR_GET_SECUREBITS => Ok(PrctlCmd::PR_GET_SECUREBITS), PR_SET_SECUREBITS => Ok(PrctlCmd::PR_SET_SECUREBITS(SecureBits::try_from( arg2 as u16, )?)), _ => { debug!("prctl cmd number: {}", option); return_errno_with_message!(Errno::EINVAL, "unsupported prctl command"); } } } }