| 1 | //! prctl is a Linux-only API for performing operations on a process or thread. |
| 2 | //! |
| 3 | //! Note that careless use of some prctl() operations can confuse the user-space run-time |
| 4 | //! environment, so these operations should be used with care. |
| 5 | //! |
| 6 | //! For more documentation, please read [prctl(2)](https://man7.org/linux/man-pages/man2/prctl.2.html). |
| 7 | |
| 8 | use crate::errno::Errno; |
| 9 | use crate::sys::signal::Signal; |
| 10 | use crate::Result; |
| 11 | |
| 12 | use libc::{c_int, c_ulong, c_void}; |
| 13 | use std::convert::TryFrom; |
| 14 | use std::ffi::{CStr, CString}; |
| 15 | use std::num::NonZeroUsize; |
| 16 | use std::ptr::NonNull; |
| 17 | |
| 18 | libc_enum! { |
| 19 | /// The type of hardware memory corruption kill policy for the thread. |
| 20 | |
| 21 | #[repr (i32)] |
| 22 | #[non_exhaustive ] |
| 23 | #[allow (non_camel_case_types)] |
| 24 | pub enum PrctlMCEKillPolicy { |
| 25 | /// The thread will receive SIGBUS as soon as a memory corruption is detected. |
| 26 | PR_MCE_KILL_EARLY, |
| 27 | /// The process is killed only when it accesses a corrupted page. |
| 28 | PR_MCE_KILL_LATE, |
| 29 | /// Uses the system-wide default. |
| 30 | PR_MCE_KILL_DEFAULT, |
| 31 | } |
| 32 | impl TryFrom<i32> |
| 33 | } |
| 34 | |
| 35 | fn prctl_set_bool(option: c_int, status: bool) -> Result<()> { |
| 36 | let res: i32 = unsafe { libc::prctl(option, status as c_ulong, 0, 0, 0) }; |
| 37 | Errno::result(res).map(op:drop) |
| 38 | } |
| 39 | |
| 40 | fn prctl_get_bool(option: c_int) -> Result<bool> { |
| 41 | let res: i32 = unsafe { libc::prctl(option, 0, 0, 0, 0) }; |
| 42 | Errno::result(res).map(|res: i32| res != 0) |
| 43 | } |
| 44 | |
| 45 | /// Set the "child subreaper" attribute for this process |
| 46 | pub fn set_child_subreaper(attribute: bool) -> Result<()> { |
| 47 | prctl_set_bool(option:libc::PR_SET_CHILD_SUBREAPER, status:attribute) |
| 48 | } |
| 49 | |
| 50 | /// Get the "child subreaper" attribute for this process |
| 51 | pub fn get_child_subreaper() -> Result<bool> { |
| 52 | // prctl writes into this var |
| 53 | let mut subreaper: c_int = 0; |
| 54 | |
| 55 | let res: i32 = unsafe { |
| 56 | libc::prctl(option:libc::PR_GET_CHILD_SUBREAPER, &mut subreaper, 0, 0, 0) |
| 57 | }; |
| 58 | |
| 59 | Errno::result(res).map(|_| subreaper != 0) |
| 60 | } |
| 61 | |
| 62 | /// Set the dumpable attribute which determines if core dumps are created for this process. |
| 63 | pub fn set_dumpable(attribute: bool) -> Result<()> { |
| 64 | prctl_set_bool(option:libc::PR_SET_DUMPABLE, status:attribute) |
| 65 | } |
| 66 | |
| 67 | /// Get the dumpable attribute for this process. |
| 68 | pub fn get_dumpable() -> Result<bool> { |
| 69 | prctl_get_bool(option:libc::PR_GET_DUMPABLE) |
| 70 | } |
| 71 | |
| 72 | /// Set the "keep capabilities" attribute for this process. This causes the thread to retain |
| 73 | /// capabilities even if it switches its UID to a nonzero value. |
| 74 | pub fn set_keepcaps(attribute: bool) -> Result<()> { |
| 75 | prctl_set_bool(option:libc::PR_SET_KEEPCAPS, status:attribute) |
| 76 | } |
| 77 | |
| 78 | /// Get the "keep capabilities" attribute for this process |
| 79 | pub fn get_keepcaps() -> Result<bool> { |
| 80 | prctl_get_bool(option:libc::PR_GET_KEEPCAPS) |
| 81 | } |
| 82 | |
| 83 | /// Clear the thread memory corruption kill policy and use the system-wide default |
| 84 | pub fn clear_mce_kill() -> Result<()> { |
| 85 | let res: i32 = unsafe { |
| 86 | libc::prctl(option:libc::PR_MCE_KILL, libc::PR_MCE_KILL_CLEAR, 0, 0, 0) |
| 87 | }; |
| 88 | |
| 89 | Errno::result(res).map(op:drop) |
| 90 | } |
| 91 | |
| 92 | /// Set the thread memory corruption kill policy |
| 93 | pub fn set_mce_kill(policy: PrctlMCEKillPolicy) -> Result<()> { |
| 94 | let res: i32 = unsafe { |
| 95 | libc::prctl( |
| 96 | option:libc::PR_MCE_KILL, |
| 97 | libc::PR_MCE_KILL_SET, |
| 98 | policy as c_ulong, |
| 99 | 0, |
| 100 | 0, |
| 101 | ) |
| 102 | }; |
| 103 | |
| 104 | Errno::result(res).map(op:drop) |
| 105 | } |
| 106 | |
| 107 | /// Get the thread memory corruption kill policy |
| 108 | pub fn get_mce_kill() -> Result<PrctlMCEKillPolicy> { |
| 109 | let res: i32 = unsafe { libc::prctl(option:libc::PR_MCE_KILL_GET, 0, 0, 0, 0) }; |
| 110 | |
| 111 | match Errno::result(res) { |
| 112 | Ok(val: i32) => Ok(PrctlMCEKillPolicy::try_from(val)?), |
| 113 | Err(e: Errno) => Err(e), |
| 114 | } |
| 115 | } |
| 116 | |
| 117 | /// Set the parent-death signal of the calling process. This is the signal that the calling process |
| 118 | /// will get when its parent dies. |
| 119 | pub fn set_pdeathsig<T: Into<Option<Signal>>>(signal: T) -> Result<()> { |
| 120 | let sig: i32 = match signal.into() { |
| 121 | Some(s: i32) => s as c_int, |
| 122 | None => 0, |
| 123 | }; |
| 124 | |
| 125 | let res: i32 = unsafe { libc::prctl(option:libc::PR_SET_PDEATHSIG, sig, 0, 0, 0) }; |
| 126 | |
| 127 | Errno::result(res).map(op:drop) |
| 128 | } |
| 129 | |
| 130 | /// Returns the current parent-death signal |
| 131 | pub fn get_pdeathsig() -> Result<Option<Signal>> { |
| 132 | // prctl writes into this var |
| 133 | let mut sig: c_int = 0; |
| 134 | |
| 135 | let res: i32 = unsafe { libc::prctl(option:libc::PR_GET_PDEATHSIG, &mut sig, 0, 0, 0) }; |
| 136 | |
| 137 | match Errno::result(res) { |
| 138 | Ok(_) => Ok(match sig { |
| 139 | 0 => None, |
| 140 | _ => Some(Signal::try_from(sig)?), |
| 141 | }), |
| 142 | Err(e: Errno) => Err(e), |
| 143 | } |
| 144 | } |
| 145 | |
| 146 | /// Set the name of the calling thread. Strings longer than 15 bytes will be truncated. |
| 147 | pub fn set_name(name: &CStr) -> Result<()> { |
| 148 | let res: i32 = unsafe { libc::prctl(option:libc::PR_SET_NAME, name.as_ptr(), 0, 0, 0) }; |
| 149 | |
| 150 | Errno::result(res).map(op:drop) |
| 151 | } |
| 152 | |
| 153 | /// Return the name of the calling thread |
| 154 | pub fn get_name() -> Result<CString> { |
| 155 | // Size of buffer determined by linux/sched.h TASK_COMM_LEN |
| 156 | let buf: [u8; 16] = [0u8; 16]; |
| 157 | |
| 158 | let res: i32 = unsafe { libc::prctl(option:libc::PR_GET_NAME, &buf, 0, 0, 0) }; |
| 159 | |
| 160 | Errno::result(res).and_then(|_| { |
| 161 | CStr::from_bytes_until_nul(&buf) |
| 162 | .map(CStr::to_owned) |
| 163 | .map_err(|_| Errno::EINVAL) |
| 164 | }) |
| 165 | } |
| 166 | |
| 167 | /// Sets the timer slack value for the calling thread. Timer slack is used by the kernel to group |
| 168 | /// timer expirations and make them the supplied amount of nanoseconds late. |
| 169 | pub fn set_timerslack(ns: u64) -> Result<()> { |
| 170 | let res: i32 = unsafe { libc::prctl(option:libc::PR_SET_TIMERSLACK, ns, 0, 0, 0) }; |
| 171 | |
| 172 | Errno::result(res).map(op:drop) |
| 173 | } |
| 174 | |
| 175 | /// Get the timerslack for the calling thread. |
| 176 | pub fn get_timerslack() -> Result<i32> { |
| 177 | let res: i32 = unsafe { libc::prctl(option:libc::PR_GET_TIMERSLACK, 0, 0, 0, 0) }; |
| 178 | |
| 179 | Errno::result(res) |
| 180 | } |
| 181 | |
| 182 | /// Disable all performance counters attached to the calling process. |
| 183 | pub fn task_perf_events_disable() -> Result<()> { |
| 184 | let res: i32 = |
| 185 | unsafe { libc::prctl(option:libc::PR_TASK_PERF_EVENTS_DISABLE, 0, 0, 0, 0) }; |
| 186 | |
| 187 | Errno::result(res).map(op:drop) |
| 188 | } |
| 189 | |
| 190 | /// Enable all performance counters attached to the calling process. |
| 191 | pub fn task_perf_events_enable() -> Result<()> { |
| 192 | let res: i32 = |
| 193 | unsafe { libc::prctl(option:libc::PR_TASK_PERF_EVENTS_ENABLE, 0, 0, 0, 0) }; |
| 194 | |
| 195 | Errno::result(res).map(op:drop) |
| 196 | } |
| 197 | |
| 198 | /// Set the calling threads "no new privs" attribute. Once set this option can not be unset. |
| 199 | pub fn set_no_new_privs() -> Result<()> { |
| 200 | prctl_set_bool(option:libc::PR_SET_NO_NEW_PRIVS, status:true) // Cannot be unset |
| 201 | } |
| 202 | |
| 203 | /// Get the "no new privs" attribute for the calling thread. |
| 204 | pub fn get_no_new_privs() -> Result<bool> { |
| 205 | prctl_get_bool(option:libc::PR_GET_NO_NEW_PRIVS) |
| 206 | } |
| 207 | |
| 208 | /// Set the state of the "THP disable" flag for the calling thread. Setting this disables |
| 209 | /// transparent huge pages. |
| 210 | pub fn set_thp_disable(flag: bool) -> Result<()> { |
| 211 | prctl_set_bool(option:libc::PR_SET_THP_DISABLE, status:flag) |
| 212 | } |
| 213 | |
| 214 | /// Get the "THP disable" flag for the calling thread. |
| 215 | pub fn get_thp_disable() -> Result<bool> { |
| 216 | prctl_get_bool(option:libc::PR_GET_THP_DISABLE) |
| 217 | } |
| 218 | |
| 219 | /// Set an identifier (or reset it) to the address memory range. |
| 220 | pub fn set_vma_anon_name(addr: NonNull<c_void>, length: NonZeroUsize, name: Option<&CStr>) -> Result<()> { |
| 221 | let nameref: *const i8 = match name { |
| 222 | Some(n: &CStr) => n.as_ptr(), |
| 223 | _ => std::ptr::null() |
| 224 | }; |
| 225 | let res: i32 = unsafe { libc::prctl(option:libc::PR_SET_VMA, libc::PR_SET_VMA_ANON_NAME, addr.as_ptr(), length, nameref) }; |
| 226 | |
| 227 | Errno::result(res).map(op:drop) |
| 228 | } |
| 229 | |