//! Bindings for the Linux `prctl` system call. //! //! There are similarities (but also differences) with FreeBSD's `procctl` system call, whose //! interface is located in the `procctl.rs` file. #![allow(unsafe_code)] use core::convert::{TryFrom, TryInto}; use core::mem::MaybeUninit; use core::ptr::NonNull; use core::{mem, ptr}; use bitflags::bitflags; use crate::backend::c::{c_int, c_uint, c_void}; use crate::backend::process::syscalls; use crate::backend::process::types::Signal; use crate::fd::{AsRawFd, BorrowedFd}; use crate::ffi::CStr; use crate::io; use crate::process::{Pid, RawPid}; // // Helper functions. // #[inline] pub(crate) unsafe fn prctl_1arg(option: c_int) -> io::Result { const NULL: *mut c_void = ptr::null_mut(); syscalls::prctl(option, NULL, NULL, NULL, NULL) } #[inline] pub(crate) unsafe fn prctl_2args(option: c_int, arg2: *mut c_void) -> io::Result { const NULL: *mut c_void = ptr::null_mut(); syscalls::prctl(option, arg2, NULL, NULL, NULL) } #[inline] pub(crate) unsafe fn prctl_3args( option: c_int, arg2: *mut c_void, arg3: *mut c_void, ) -> io::Result { syscalls::prctl(option, arg2, arg3, ptr::null_mut(), ptr::null_mut()) } #[inline] pub(crate) unsafe fn prctl_get_at_arg2_optional

(option: i32) -> io::Result

{ let mut value: MaybeUninit

= MaybeUninit::uninit(); prctl_2args(option, value.as_mut_ptr().cast())?; Ok(value.assume_init()) } #[inline] pub(crate) unsafe fn prctl_get_at_arg2(option: i32) -> io::Result where P: Default, T: TryFrom, { let mut value: P = Default::default(); prctl_2args(option, ((&mut value) as *mut P).cast())?; TryFrom::try_from(value) } // // PR_GET_PDEATHSIG/PR_SET_PDEATHSIG // const PR_GET_PDEATHSIG: c_int = 2; /// Get the current value of the parent process death signal. /// /// # References /// - [Linux: `prctl(PR_GET_PDEATHSIG,...)`] /// - [FreeBSD: `procctl(PROC_PDEATHSIG_STATUS,...)`] /// /// [Linux: `prctl(PR_GET_PDEATHSIG,...)`]: https://man7.org/linux/man-pages/man2/prctl.2.html /// [FreeBSD: `procctl(PROC_PDEATHSIG_STATUS,...)`]: https://www.freebsd.org/cgi/man.cgi?query=procctl&sektion=2 #[inline] pub fn parent_process_death_signal() -> io::Result> { unsafe { prctl_get_at_arg2_optional::(PR_GET_PDEATHSIG) }.map(Signal::from_raw) } const PR_SET_PDEATHSIG: c_int = 1; /// Set the parent-death signal of the calling process. /// /// # References /// - [Linux: `prctl(PR_SET_PDEATHSIG,...)`] /// - [FreeBSD: `procctl(PROC_PDEATHSIG_CTL,...)`] /// /// [Linux: `prctl(PR_SET_PDEATHSIG,...)`]: https://man7.org/linux/man-pages/man2/prctl.2.html /// [FreeBSD: `procctl(PROC_PDEATHSIG_CTL,...)`]: https://www.freebsd.org/cgi/man.cgi?query=procctl&sektion=2 #[inline] pub fn set_parent_process_death_signal(signal: Option) -> io::Result<()> { let signal = signal.map_or(0_usize, |signal| signal as usize); unsafe { prctl_2args(PR_SET_PDEATHSIG, signal as *mut _) }.map(|_r| ()) } // // PR_GET_DUMPABLE/PR_SET_DUMPABLE // const PR_GET_DUMPABLE: c_int = 3; const SUID_DUMP_DISABLE: i32 = 0; const SUID_DUMP_USER: i32 = 1; const SUID_DUMP_ROOT: i32 = 2; /// `SUID_DUMP_*`. #[derive(Copy, Clone, Debug, Eq, PartialEq)] #[repr(i32)] pub enum DumpableBehavior { /// Not dumpable. NotDumpable = SUID_DUMP_DISABLE, /// Dumpable. Dumpable = SUID_DUMP_USER, /// Dumpable but only readable by root. DumpableReadableOnlyByRoot = SUID_DUMP_ROOT, } impl TryFrom for DumpableBehavior { type Error = io::Errno; fn try_from(value: i32) -> Result { match value { SUID_DUMP_DISABLE => Ok(Self::NotDumpable), SUID_DUMP_USER => Ok(Self::Dumpable), SUID_DUMP_ROOT => Ok(Self::DumpableReadableOnlyByRoot), _ => Err(io::Errno::RANGE), } } } /// Get the current state of the calling process's `dumpable` attribute. /// /// # References /// - [`prctl(PR_GET_DUMPABLE,...)`] /// /// [`prctl(PR_GET_DUMPABLE,...)`]: https://man7.org/linux/man-pages/man2/prctl.2.html #[inline] pub fn dumpable_behavior() -> io::Result { unsafe { prctl_1arg(PR_GET_DUMPABLE) }.and_then(TryInto::try_into) } const PR_SET_DUMPABLE: c_int = 4; /// Set the state of the `dumpable` attribute, which determines whether the process can be traced /// and whether core dumps are produced for the calling process upon delivery of a signal whose /// default behavior is to produce a core dump. /// /// A similar function with the same name is available on FreeBSD (as part of the `procctl` /// interface), but it has an extra argument which allows to select a process other then the /// current process. /// /// # References /// - [`prctl(PR_SET_DUMPABLE,...)`] /// /// [`prctl(PR_SET_DUMPABLE,...)`]: https://man7.org/linux/man-pages/man2/prctl.2.html #[inline] pub fn set_dumpable_behavior(config: DumpableBehavior) -> io::Result<()> { unsafe { prctl_2args(PR_SET_DUMPABLE, config as usize as *mut _) }.map(|_r| ()) } // // PR_GET_UNALIGN/PR_SET_UNALIGN // const PR_GET_UNALIGN: c_int = 5; bitflags! { /// `PR_UNALIGN_*`. pub struct UnalignedAccessControl: u32 { /// Silently fix up unaligned user accesses. const NO_PRINT = 1; /// Generate `SIGBUS` on unaligned user access. const SIGBUS = 2; } } /// Get unaligned access control bits. /// /// # References /// - [`prctl(PR_GET_UNALIGN,...)`] /// /// [`prctl(PR_GET_UNALIGN,...)`]: https://man7.org/linux/man-pages/man2/prctl.2.html #[inline] pub fn unaligned_access_control() -> io::Result { let r = unsafe { prctl_get_at_arg2_optional::(PR_GET_UNALIGN)? }; UnalignedAccessControl::from_bits(r).ok_or(io::Errno::RANGE) } const PR_SET_UNALIGN: c_int = 6; /// Set unaligned access control bits. /// /// # References /// - [`prctl(PR_SET_UNALIGN,...)`] /// /// [`prctl(PR_SET_UNALIGN,...)`]: https://man7.org/linux/man-pages/man2/prctl.2.html #[inline] pub fn set_unaligned_access_control(config: UnalignedAccessControl) -> io::Result<()> { unsafe { prctl_2args(PR_SET_UNALIGN, config.bits() as usize as *mut _) }.map(|_r| ()) } // // PR_GET_FPEMU/PR_SET_FPEMU // const PR_GET_FPEMU: c_int = 9; bitflags! { /// `PR_FPEMU_*`. pub struct FloatingPointEmulationControl: u32 { /// Silently emulate floating point operations accesses. const NO_PRINT = 1; /// Don't emulate floating point operations, send `SIGFPE` instead. const SIGFPE = 2; } } /// Get floating point emulation control bits. /// /// # References /// - [`prctl(PR_GET_FPEMU,...)`] /// /// [`prctl(PR_GET_FPEMU,...)`]: https://man7.org/linux/man-pages/man2/prctl.2.html #[inline] pub fn floating_point_emulation_control() -> io::Result { let r = unsafe { prctl_get_at_arg2_optional::(PR_GET_FPEMU)? }; FloatingPointEmulationControl::from_bits(r).ok_or(io::Errno::RANGE) } const PR_SET_FPEMU: c_int = 10; /// Set floating point emulation control bits. /// /// # References /// - [`prctl(PR_SET_FPEMU,...)`] /// /// [`prctl(PR_SET_FPEMU,...)`]: https://man7.org/linux/man-pages/man2/prctl.2.html #[inline] pub fn set_floating_point_emulation_control( config: FloatingPointEmulationControl, ) -> io::Result<()> { unsafe { prctl_2args(PR_SET_FPEMU, config.bits() as usize as *mut _) }.map(|_r| ()) } // // PR_GET_FPEXC/PR_SET_FPEXC // const PR_GET_FPEXC: c_int = 11; bitflags! { /// Zero means floating point exceptions are disabled. pub struct FloatingPointExceptionMode: u32 { /// Async non-recoverable exception mode. const NONRECOV = 1; /// Async recoverable exception mode. const ASYNC = 2; /// Precise exception mode. const PRECISE = 3; /// Use FPEXC for floating point exception enables. const SW_ENABLE = 0x80; /// Floating point divide by zero. const DIV = 0x01_0000; /// Floating point overflow. const OVF = 0x02_0000; /// Floating point underflow. const UND = 0x04_0000; /// Floating point inexact result. const RES = 0x08_0000; /// Floating point invalid operation. const INV = 0x10_0000; } } /// Get floating point exception mode. /// /// # References /// - [`prctl(PR_GET_FPEXC,...)`] /// /// [`prctl(PR_GET_FPEXC,...)`]: https://man7.org/linux/man-pages/man2/prctl.2.html #[inline] pub fn floating_point_exception_mode() -> io::Result> { unsafe { prctl_get_at_arg2_optional::(PR_GET_FPEXC) } .map(FloatingPointExceptionMode::from_bits) } const PR_SET_FPEXC: c_int = 12; /// Set floating point exception mode. /// /// # References /// - [`prctl(PR_SET_FPEXC,...)`] /// /// [`prctl(PR_SET_FPEXC,...)`]: https://man7.org/linux/man-pages/man2/prctl.2.html #[inline] pub fn set_floating_point_exception_mode( config: Option, ) -> io::Result<()> { let config = config.as_ref().map_or(0, FloatingPointExceptionMode::bits); unsafe { prctl_2args(PR_SET_FPEXC, config as usize as *mut _) }.map(|_r| ()) } // // PR_GET_TIMING/PR_SET_TIMING // const PR_GET_TIMING: c_int = 13; const PR_TIMING_STATISTICAL: i32 = 0; const PR_TIMING_TIMESTAMP: i32 = 1; /// `PR_TIMING_*`. #[derive(Copy, Clone, Debug, Eq, PartialEq)] #[repr(i32)] pub enum TimingMethod { /// Normal, traditional, statistical process timing. Statistical = PR_TIMING_STATISTICAL, /// Accurate timestamp based process timing. TimeStamp = PR_TIMING_TIMESTAMP, } impl TryFrom for TimingMethod { type Error = io::Errno; fn try_from(value: i32) -> Result { match value { PR_TIMING_STATISTICAL => Ok(Self::Statistical), PR_TIMING_TIMESTAMP => Ok(Self::TimeStamp), _ => Err(io::Errno::RANGE), } } } /// Get which process timing method is currently in use. /// /// # References /// - [`prctl(PR_GET_TIMING,...)`] /// /// [`prctl(PR_GET_TIMING,...)`]: https://man7.org/linux/man-pages/man2/prctl.2.html #[inline] pub fn timing_method() -> io::Result { unsafe { prctl_1arg(PR_GET_TIMING) }.and_then(TryInto::try_into) } const PR_SET_TIMING: c_int = 14; /// Set whether to use (normal, traditional) statistical process timing or accurate /// timestamp-based process timing. /// /// # References /// - [`prctl(PR_SET_TIMING,...)`] /// /// [`prctl(PR_SET_TIMING,...)`]: https://man7.org/linux/man-pages/man2/prctl.2.html #[inline] pub fn set_timing_method(method: TimingMethod) -> io::Result<()> { unsafe { prctl_2args(PR_SET_TIMING, method as usize as *mut _) }.map(|_r| ()) } // // PR_GET_ENDIAN/PR_SET_ENDIAN // const PR_GET_ENDIAN: c_int = 19; const PR_ENDIAN_BIG: u32 = 0; const PR_ENDIAN_LITTLE: u32 = 1; const PR_ENDIAN_PPC_LITTLE: u32 = 2; /// `PR_ENDIAN_*`. #[derive(Copy, Clone, Debug, Eq, PartialEq)] #[repr(u32)] pub enum EndianMode { /// Big endian mode. Big = PR_ENDIAN_BIG, /// True little endian mode. Little = PR_ENDIAN_LITTLE, /// `PowerPC` pseudo little endian. PowerPCLittle = PR_ENDIAN_PPC_LITTLE, } impl TryFrom for EndianMode { type Error = io::Errno; fn try_from(value: u32) -> Result { match value { PR_ENDIAN_BIG => Ok(Self::Big), PR_ENDIAN_LITTLE => Ok(Self::Little), PR_ENDIAN_PPC_LITTLE => Ok(Self::PowerPCLittle), _ => Err(io::Errno::RANGE), } } } /// Get the endianness of the calling process. /// /// # References /// - [`prctl(PR_GET_ENDIAN,...)`] /// /// [`prctl(PR_GET_ENDIAN,...)`]: https://man7.org/linux/man-pages/man2/prctl.2.html #[inline] pub fn endian_mode() -> io::Result { unsafe { prctl_get_at_arg2::(PR_GET_ENDIAN) } } const PR_SET_ENDIAN: c_int = 20; /// Set the endianness of the calling process. /// /// # References /// - [`prctl(PR_SET_ENDIAN,...)`] /// /// # Safety /// /// Please ensure the conditions necessary to safely call this function, /// as detailed in the references above. /// /// [`prctl(PR_SET_ENDIAN,...)`]: https://man7.org/linux/man-pages/man2/prctl.2.html #[inline] pub unsafe fn set_endian_mode(mode: EndianMode) -> io::Result<()> { prctl_2args(PR_SET_ENDIAN, mode as usize as *mut _).map(|_r| ()) } // // PR_GET_TSC/PR_SET_TSC // const PR_GET_TSC: c_int = 25; const PR_TSC_ENABLE: u32 = 1; const PR_TSC_SIGSEGV: u32 = 2; /// `PR_TSC_*`. #[derive(Copy, Clone, Debug, Eq, PartialEq)] #[repr(u32)] pub enum TimeStampCounterReadability { /// Allow the use of the timestamp counter. Readable = PR_TSC_ENABLE, /// Throw a `SIGSEGV` instead of reading the TSC. RaiseSIGSEGV = PR_TSC_SIGSEGV, } impl TryFrom for TimeStampCounterReadability { type Error = io::Errno; fn try_from(value: u32) -> Result { match value { PR_TSC_ENABLE => Ok(Self::Readable), PR_TSC_SIGSEGV => Ok(Self::RaiseSIGSEGV), _ => Err(io::Errno::RANGE), } } } /// Get the state of the flag determining if the timestamp counter can be read. /// /// # References /// - [`prctl(PR_GET_TSC,...)`] /// /// [`prctl(PR_GET_TSC,...)`]: https://man7.org/linux/man-pages/man2/prctl.2.html #[inline] pub fn time_stamp_counter_readability() -> io::Result { unsafe { prctl_get_at_arg2::(PR_GET_TSC) } } const PR_SET_TSC: c_int = 26; /// Set the state of the flag determining if the timestamp counter can be read by the process. /// /// # References /// - [`prctl(PR_SET_TSC,...)`] /// /// [`prctl(PR_SET_TSC,...)`]: https://man7.org/linux/man-pages/man2/prctl.2.html #[inline] pub fn set_time_stamp_counter_readability( readability: TimeStampCounterReadability, ) -> io::Result<()> { unsafe { prctl_2args(PR_SET_TSC, readability as usize as *mut _) }.map(|_r| ()) } // // PR_TASK_PERF_EVENTS_DISABLE/PR_TASK_PERF_EVENTS_ENABLE // const PR_TASK_PERF_EVENTS_DISABLE: c_int = 31; const PR_TASK_PERF_EVENTS_ENABLE: c_int = 32; /// Enable or disable all performance counters attached to the calling process. /// /// # References /// - [`prctl(PR_TASK_PERF_EVENTS_ENABLE,...)`] /// - [`prctl(PR_TASK_PERF_EVENTS_DISABLE,...)`] /// /// [`prctl(PR_TASK_PERF_EVENTS_ENABLE,...)`]: https://man7.org/linux/man-pages/man2/prctl.2.html /// [`prctl(PR_TASK_PERF_EVENTS_DISABLE,...)`]: https://man7.org/linux/man-pages/man2/prctl.2.html #[inline] pub fn configure_performance_counters(enable: bool) -> io::Result<()> { let option = if enable { PR_TASK_PERF_EVENTS_ENABLE } else { PR_TASK_PERF_EVENTS_DISABLE }; unsafe { prctl_1arg(option) }.map(|_r| ()) } // // PR_MCE_KILL_GET/PR_MCE_KILL // const PR_MCE_KILL_GET: c_int = 34; const PR_MCE_KILL_LATE: u32 = 0; const PR_MCE_KILL_EARLY: u32 = 1; const PR_MCE_KILL_DEFAULT: u32 = 2; /// `PR_MCE_KILL_*`. #[derive(Copy, Clone, Debug, Eq, PartialEq)] #[repr(u32)] pub enum MachineCheckMemoryCorruptionKillPolicy { /// Late kill policy. Late = PR_MCE_KILL_LATE, /// Early kill policy. Early = PR_MCE_KILL_EARLY, /// System-wide default policy. Default = PR_MCE_KILL_DEFAULT, } impl TryFrom for MachineCheckMemoryCorruptionKillPolicy { type Error = io::Errno; fn try_from(value: u32) -> Result { match value { PR_MCE_KILL_LATE => Ok(Self::Late), PR_MCE_KILL_EARLY => Ok(Self::Early), PR_MCE_KILL_DEFAULT => Ok(Self::Default), _ => Err(io::Errno::RANGE), } } } /// Get the current per-process machine check kill policy. /// /// # References /// - [`prctl(PR_MCE_KILL_GET,...)`] /// /// [`prctl(PR_MCE_KILL_GET,...)`]: https://man7.org/linux/man-pages/man2/prctl.2.html #[inline] pub fn machine_check_memory_corruption_kill_policy( ) -> io::Result { let r = unsafe { prctl_1arg(PR_MCE_KILL_GET)? } as c_uint; MachineCheckMemoryCorruptionKillPolicy::try_from(r) } const PR_MCE_KILL: c_int = 33; const PR_MCE_KILL_CLEAR: usize = 0; const PR_MCE_KILL_SET: usize = 1; /// Set the machine check memory corruption kill policy for the calling thread. /// /// # References /// - [`prctl(PR_MCE_KILL,...)`] /// /// [`prctl(PR_MCE_KILL,...)`]: https://man7.org/linux/man-pages/man2/prctl.2.html #[inline] pub fn set_machine_check_memory_corruption_kill_policy( policy: Option, ) -> io::Result<()> { let (sub_operation, policy) = if let Some(policy) = policy { (PR_MCE_KILL_SET, policy as usize as *mut _) } else { (PR_MCE_KILL_CLEAR, ptr::null_mut()) }; unsafe { prctl_3args(PR_MCE_KILL, sub_operation as *mut _, policy) }.map(|_r| ()) } // // PR_SET_MM // const PR_SET_MM: c_int = 35; const PR_SET_MM_START_CODE: u32 = 1; const PR_SET_MM_END_CODE: u32 = 2; const PR_SET_MM_START_DATA: u32 = 3; const PR_SET_MM_END_DATA: u32 = 4; const PR_SET_MM_START_STACK: u32 = 5; const PR_SET_MM_START_BRK: u32 = 6; const PR_SET_MM_BRK: u32 = 7; const PR_SET_MM_ARG_START: u32 = 8; const PR_SET_MM_ARG_END: u32 = 9; const PR_SET_MM_ENV_START: u32 = 10; const PR_SET_MM_ENV_END: u32 = 11; const PR_SET_MM_AUXV: usize = 12; const PR_SET_MM_EXE_FILE: usize = 13; const PR_SET_MM_MAP: usize = 14; const PR_SET_MM_MAP_SIZE: usize = 15; /// `PR_SET_MM_*`. #[derive(Copy, Clone, Debug, Eq, PartialEq)] #[repr(u32)] pub enum VirtualMemoryMapAddress { /// Set the address above which the program text can run. CodeStart = PR_SET_MM_START_CODE, /// Set the address below which the program text can run. CodeEnd = PR_SET_MM_END_CODE, /// Set the address above which initialized and uninitialized (bss) data are placed. DataStart = PR_SET_MM_START_DATA, /// Set the address below which initialized and uninitialized (bss) data are placed. DataEnd = PR_SET_MM_END_DATA, /// Set the start address of the stack. StackStart = PR_SET_MM_START_STACK, /// Set the address above which the program heap can be expanded with `brk` call. BrkStart = PR_SET_MM_START_BRK, /// Set the current `brk` value. BrkCurrent = PR_SET_MM_BRK, /// Set the address above which the program command line is placed. ArgStart = PR_SET_MM_ARG_START, /// Set the address below which the program command line is placed. ArgEnd = PR_SET_MM_ARG_END, /// Set the address above which the program environment is placed. EnvironmentStart = PR_SET_MM_ENV_START, /// Set the address below which the program environment is placed. EnvironmentEnd = PR_SET_MM_ENV_END, } /// Modify certain kernel memory map descriptor addresses of the calling process. /// /// # References /// - [`prctl(PR_SET_MM,...)`] /// /// # Safety /// /// Please ensure the conditions necessary to safely call this function, /// as detailed in the references above. /// /// [`prctl(PR_SET_MM,...)`]: https://man7.org/linux/man-pages/man2/prctl.2.html #[inline] pub unsafe fn set_virtual_memory_map_address( option: VirtualMemoryMapAddress, address: Option>, ) -> io::Result<()> { let address = address.map_or_else(ptr::null_mut, NonNull::as_ptr); prctl_3args(PR_SET_MM, option as usize as *mut _, address).map(|_r| ()) } /// Supersede the `/proc/pid/exe` symbolic link with a new one pointing to a new executable file. /// /// # References /// - [`prctl(PR_SET_MM,PR_SET_MM_EXE_FILE,...)`] /// /// [`prctl(PR_SET_MM,PR_SET_MM_EXE_FILE,...)`]: https://man7.org/linux/man-pages/man2/prctl.2.html #[inline] pub fn set_executable_file(fd: BorrowedFd) -> io::Result<()> { let fd = usize::try_from(fd.as_raw_fd()).map_err(|_r| io::Errno::RANGE)?; unsafe { prctl_3args(PR_SET_MM, PR_SET_MM_EXE_FILE as *mut _, fd as *mut _) }.map(|_r| ()) } /// Set a new auxiliary vector. /// /// # References /// - [`prctl(PR_SET_MM,PR_SET_MM_AUXV,...)`] /// /// # Safety /// /// Please ensure the conditions necessary to safely call this function, /// as detailed in the references above. /// /// [`prctl(PR_SET_MM,PR_SET_MM_AUXV,...)`]: https://man7.org/linux/man-pages/man2/prctl.2.html #[inline] pub unsafe fn set_auxiliary_vector(auxv: &[*const c_void]) -> io::Result<()> { syscalls::prctl( PR_SET_MM, PR_SET_MM_AUXV as *mut _, auxv.as_ptr() as *mut _, auxv.len() as *mut _, ptr::null_mut(), ) .map(|_r| ()) } /// Get the size of the [`PrctlMmMap`] the kernel expects. /// /// # References /// - [`prctl(PR_SET_MM,PR_SET_MM_MAP_SIZE,...)`] /// /// [`prctl(PR_SET_MM,PR_SET_MM_MAP_SIZE,...)`]: https://man7.org/linux/man-pages/man2/prctl.2.html #[inline] pub fn virtual_memory_map_config_struct_size() -> io::Result { let mut value: c_uint = 0; let value_ptr = (&mut value) as *mut c_uint; unsafe { prctl_3args(PR_SET_MM, PR_SET_MM_MAP_SIZE as *mut _, value_ptr.cast())? }; Ok(value as usize) } /// This structure provides new memory descriptor map which mostly modifies `/proc/pid/stat[m]` /// output for a task. /// This mostly done in a sake of checkpoint/restore functionality. #[repr(C)] #[derive(Debug, Clone)] pub struct PrctlMmMap { /// Code section start address. pub start_code: u64, /// Code section end address. pub end_code: u64, /// Data section start address. pub start_data: u64, /// Data section end address. pub end_data: u64, /// brk() start address. pub start_brk: u64, /// brk() current address. pub brk: u64, /// Stack start address. pub start_stack: u64, /// Program command line start address. pub arg_start: u64, /// Program command line end address. pub arg_end: u64, /// Program environment start address. pub env_start: u64, /// Program environment end address. pub env_end: u64, /// Auxiliary vector start address. pub auxv: *mut u64, /// Auxiliary vector size. pub auxv_size: u32, /// File descriptor of executable file that was used to create this process. pub exe_fd: u32, } /// Provides one-shot access to all the addresses by passing in a [`PrctlMmMap`]. /// /// # References /// - [`prctl(PR_SET_MM,PR_SET_MM_MAP,...)`] /// /// # Safety /// /// Please ensure the conditions necessary to safely call this function, /// as detailed in the references above. /// /// [`prctl(PR_SET_MM,PR_SET_MM_MAP,...)`]: https://man7.org/linux/man-pages/man2/prctl.2.html #[inline] pub unsafe fn configure_virtual_memory_map(config: &PrctlMmMap) -> io::Result<()> { syscalls::prctl( PR_SET_MM, PR_SET_MM_MAP as *mut _, config as *const PrctlMmMap as *mut _, mem::size_of::() as *mut _, ptr::null_mut(), ) .map(|_r| ()) } // // PR_SET_PTRACER // const PR_SET_PTRACER: c_int = 0x59_61_6d_61; const PR_SET_PTRACER_ANY: usize = usize::MAX; /// Process ptracer. #[derive(Copy, Clone, Debug, Eq, PartialEq)] pub enum PTracer { /// None. None, /// Disable `ptrace` restrictions for the calling process. Any, /// Specific process. ProcessID(Pid), } /// Declare that the ptracer process can `ptrace` the calling process as if it were a direct /// process ancestor. /// /// # References /// - [`prctl(PR_SET_PTRACER,...)`] /// /// [`prctl(PR_SET_PTRACER,...)`]: https://man7.org/linux/man-pages/man2/prctl.2.html #[inline] pub fn set_ptracer(tracer: PTracer) -> io::Result<()> { let pid = match tracer { PTracer::None => ptr::null_mut(), PTracer::Any => PR_SET_PTRACER_ANY as *mut _, PTracer::ProcessID(pid) => pid.as_raw_nonzero().get() as usize as *mut _, }; unsafe { prctl_2args(PR_SET_PTRACER, pid) }.map(|_r| ()) } // // PR_GET_CHILD_SUBREAPER/PR_SET_CHILD_SUBREAPER // const PR_GET_CHILD_SUBREAPER: c_int = 37; /// Get the `child subreaper` setting of the calling process. /// /// # References /// - [`prctl(PR_GET_CHILD_SUBREAPER,...)`] /// /// [`prctl(PR_GET_CHILD_SUBREAPER,...)`]: https://man7.org/linux/man-pages/man2/prctl.2.html #[inline] pub fn child_subreaper() -> io::Result> { unsafe { let r = prctl_get_at_arg2_optional::(PR_GET_CHILD_SUBREAPER)?; Ok(Pid::from_raw(r as RawPid)) } } const PR_SET_CHILD_SUBREAPER: c_int = 36; /// Set the `child subreaper` attribute of the calling process. /// /// # References /// - [`prctl(PR_SET_CHILD_SUBREAPER,...)`] /// /// [`prctl(PR_SET_CHILD_SUBREAPER,...)`]: https://man7.org/linux/man-pages/man2/prctl.2.html #[inline] pub fn set_child_subreaper(pid: Option) -> io::Result<()> { let pid = pid.map_or(0_usize, |pid| pid.as_raw_nonzero().get() as usize); unsafe { prctl_2args(PR_SET_CHILD_SUBREAPER, pid as *mut _) }.map(|_r| ()) } // // PR_GET_FP_MODE/PR_SET_FP_MODE // const PR_GET_FP_MODE: c_int = 46; const PR_FP_MODE_FR: u32 = 1_u32 << 0; const PR_FP_MODE_FRE: u32 = 1_u32 << 1; /// `PR_FP_MODE_*`. #[derive(Copy, Clone, Debug, Eq, PartialEq)] #[repr(u32)] pub enum FloatingPointMode { /// 64-bit floating point registers. FloatingPointRegisters = PR_FP_MODE_FR, /// Enable emulation of 32-bit floating-point mode. FloatingPointEmulation = PR_FP_MODE_FRE, } impl TryFrom for FloatingPointMode { type Error = io::Errno; fn try_from(value: u32) -> Result { match value { PR_FP_MODE_FR => Ok(Self::FloatingPointRegisters), PR_FP_MODE_FRE => Ok(Self::FloatingPointEmulation), _ => Err(io::Errno::RANGE), } } } /// Get the current floating point mode. /// /// # References /// - [`prctl(PR_GET_FP_MODE,...)`] /// /// [`prctl(PR_GET_FP_MODE,...)`]: https://man7.org/linux/man-pages/man2/prctl.2.html #[inline] pub fn floating_point_mode() -> io::Result { let r = unsafe { prctl_1arg(PR_GET_FP_MODE)? } as c_uint; FloatingPointMode::try_from(r) } const PR_SET_FP_MODE: c_int = 45; /// Allow control of the floating point mode from user space. /// /// # References /// - [`prctl(PR_SET_FP_MODE,...)`] /// /// [`prctl(PR_SET_FP_MODE,...)`]: https://man7.org/linux/man-pages/man2/prctl.2.html #[inline] pub fn set_floating_point_mode(mode: FloatingPointMode) -> io::Result<()> { unsafe { prctl_2args(PR_SET_FP_MODE, mode as usize as *mut _) }.map(|_r| ()) } // // PR_GET_SPECULATION_CTRL/PR_SET_SPECULATION_CTRL // const PR_GET_SPECULATION_CTRL: c_int = 52; const PR_SPEC_STORE_BYPASS: u32 = 0; const PR_SPEC_INDIRECT_BRANCH: u32 = 1; const PR_SPEC_L1D_FLUSH: u32 = 2; /// `PR_SPEC_*`. #[derive(Copy, Clone, Debug, Eq, PartialEq)] #[repr(u32)] pub enum SpeculationFeature { /// Set the state of the speculative store bypass misfeature. SpeculativeStoreBypass = PR_SPEC_STORE_BYPASS, /// Set the state of the indirect branch speculation misfeature. IndirectBranchSpeculation = PR_SPEC_INDIRECT_BRANCH, /// Flush L1D Cache on context switch out of the task. FlushL1DCacheOnContextSwitchOutOfTask = PR_SPEC_L1D_FLUSH, } impl TryFrom for SpeculationFeature { type Error = io::Errno; fn try_from(value: u32) -> Result { match value { PR_SPEC_STORE_BYPASS => Ok(Self::SpeculativeStoreBypass), PR_SPEC_INDIRECT_BRANCH => Ok(Self::IndirectBranchSpeculation), PR_SPEC_L1D_FLUSH => Ok(Self::FlushL1DCacheOnContextSwitchOutOfTask), _ => Err(io::Errno::RANGE), } } } bitflags! { /// `PR_SPEC_*`. pub struct SpeculationFeatureControl: u32 { /// The speculation feature is enabled, mitigation is disabled. const ENABLE = 1_u32 << 1; /// The speculation feature is disabled, mitigation is enabled. const DISABLE = 1_u32 << 2; /// The speculation feature is disabled, mitigation is enabled, and it cannot be undone. const FORCE_DISABLE = 1_u32 << 3; /// The speculation feature is disabled, mitigation is enabled, and the state will be cleared on `execve`. const DISABLE_NOEXEC = 1_u32 << 4; } } bitflags! { /// Zero means the processors are not vulnerable. pub struct SpeculationFeatureState: u32 { /// Mitigation can be controlled per thread by `PR_SET_SPECULATION_CTRL`. const PRCTL = 1_u32 << 0; /// The speculation feature is enabled, mitigation is disabled. const ENABLE = 1_u32 << 1; /// The speculation feature is disabled, mitigation is enabled. const DISABLE = 1_u32 << 2; /// The speculation feature is disabled, mitigation is enabled, and it cannot be undone. const FORCE_DISABLE = 1_u32 << 3; /// The speculation feature is disabled, mitigation is enabled, and the state will be cleared on `execve`. const DISABLE_NOEXEC = 1_u32 << 4; } } /// Get the state of the speculation misfeature. /// /// # References /// - [`prctl(PR_GET_SPECULATION_CTRL,...)`] /// /// [`prctl(PR_GET_SPECULATION_CTRL,...)`]: https://www.kernel.org/doc/html/v5.18/userspace-api/spec_ctrl.html #[inline] pub fn speculative_feature_state( feature: SpeculationFeature, ) -> io::Result> { let r = unsafe { prctl_2args(PR_GET_SPECULATION_CTRL, feature as usize as *mut _)? } as c_uint; Ok(SpeculationFeatureState::from_bits(r)) } const PR_SET_SPECULATION_CTRL: c_int = 53; /// Sets the state of the speculation misfeature. /// /// # References /// - [`prctl(PR_SET_SPECULATION_CTRL,...)`] /// /// [`prctl(PR_SET_SPECULATION_CTRL,...)`]: https://www.kernel.org/doc/html/v5.18/userspace-api/spec_ctrl.html #[inline] pub fn control_speculative_feature( feature: SpeculationFeature, config: SpeculationFeatureControl, ) -> io::Result<()> { let feature = feature as usize as *mut _; let config = config.bits() as usize as *mut _; unsafe { prctl_3args(PR_SET_SPECULATION_CTRL, feature, config) }.map(|_r| ()) } // // PR_GET_IO_FLUSHER/PR_SET_IO_FLUSHER // const PR_GET_IO_FLUSHER: c_int = 58; /// Get the `IO_FLUSHER` state of the caller. /// /// # References /// - [`prctl(PR_GET_IO_FLUSHER,...)`] /// /// [`prctl(PR_GET_IO_FLUSHER,...)`]: https://man7.org/linux/man-pages/man2/prctl.2.html #[inline] pub fn is_io_flusher() -> io::Result { unsafe { prctl_1arg(PR_GET_IO_FLUSHER) }.map(|r| r != 0) } const PR_SET_IO_FLUSHER: c_int = 57; /// Put the process in the `IO_FLUSHER` state, allowing it to make progress when /// allocating memory. /// /// # References /// - [`prctl(PR_SET_IO_FLUSHER,...)`] /// /// [`prctl(PR_SET_IO_FLUSHER,...)`]: https://man7.org/linux/man-pages/man2/prctl.2.html #[inline] pub fn configure_io_flusher_behavior(enable: bool) -> io::Result<()> { unsafe { prctl_2args(PR_SET_IO_FLUSHER, enable as usize as *mut _) }.map(|_r| ()) } // // PR_PAC_GET_ENABLED_KEYS/PR_PAC_SET_ENABLED_KEYS // const PR_PAC_GET_ENABLED_KEYS: c_int = 61; bitflags! { /// `PR_PAC_AP*`. pub struct PointerAuthenticationKeys: u32 { /// Instruction authentication key `A`. const INSTRUCTION_AUTHENTICATION_KEY_A = 1_u32 << 0; /// Instruction authentication key `B`. const INSTRUCTION_AUTHENTICATION_KEY_B = 1_u32 << 1; /// Data authentication key `A`. const DATA_AUTHENTICATION_KEY_A = 1_u32 << 2; /// Data authentication key `B`. const DATA_AUTHENTICATION_KEY_B = 1_u32 << 3; /// Generic authentication `A` key. const GENERIC_AUTHENTICATION_KEY_A = 1_u32 << 4; } } /// Get enabled pointer authentication keys. /// /// # References /// - [`prctl(PR_PAC_GET_ENABLED_KEYS,...)`] /// /// [`prctl(PR_PAC_GET_ENABLED_KEYS,...)`]: https://www.kernel.org/doc/html/v5.18/arm64/pointer-authentication.html #[inline] pub fn enabled_pointer_authentication_keys() -> io::Result { let r = unsafe { prctl_1arg(PR_PAC_GET_ENABLED_KEYS)? } as c_uint; PointerAuthenticationKeys::from_bits(r).ok_or(io::Errno::RANGE) } const PR_PAC_SET_ENABLED_KEYS: c_int = 60; /// Set enabled pointer authentication keys. /// /// # References /// - [`prctl(PR_PAC_SET_ENABLED_KEYS,...)`] /// /// # Safety /// /// Please ensure the conditions necessary to safely call this function, /// as detailed in the references above. /// /// [`prctl(PR_PAC_SET_ENABLED_KEYS,...)`]: https://www.kernel.org/doc/html/v5.18/arm64/pointer-authentication.html #[inline] pub unsafe fn configure_pointer_authentication_keys( config: impl Iterator, ) -> io::Result<()> { let mut affected_keys: u32 = 0; let mut enabled_keys: u32 = 0; for (key, enable) in config { let key = key.bits(); affected_keys |= key; if enable { enabled_keys |= key; } else { enabled_keys &= !key; } } if affected_keys == 0 { return Ok(()); // Nothing to do. } prctl_3args( PR_PAC_SET_ENABLED_KEYS, affected_keys as usize as *mut _, enabled_keys as usize as *mut _, ) .map(|_r| ()) } // // PR_SET_VMA // const PR_SET_VMA: c_int = 0x53_56_4d_41; const PR_SET_VMA_ANON_NAME: usize = 0; /// Set the name for a virtual memory region. /// /// # References /// - [`prctl(PR_SET_VMA,PR_SET_VMA_ANON_NAME,...)`] /// /// [`prctl(PR_SET_VMA,PR_SET_VMA_ANON_NAME,...)`]: https://lwn.net/Articles/867818/ #[inline] pub fn set_virtual_memory_region_name(region: &[u8], name: Option<&CStr>) -> io::Result<()> { unsafe { syscalls::prctl( PR_SET_VMA, PR_SET_VMA_ANON_NAME as *mut _, region.as_ptr() as *mut _, region.len() as *mut _, name.map_or_else(ptr::null, CStr::as_ptr) as *mut _, ) .map(|_r| ()) } }