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