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 | |