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
8use crate::errno::Errno;
9use crate::sys::signal::Signal;
10use crate::Result;
11
12use libc::{c_int, c_ulong};
13use std::convert::TryFrom;
14use std::ffi::{CStr, CString};
15
16libc_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
33fn 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
38fn 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
44pub 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
49pub 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.
59pub 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.
64pub 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.
70pub 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
75pub 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
80pub 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
87pub 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
102pub 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.
113pub 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
125pub 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.
141pub 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
148pub 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.
162pub 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.
169pub 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.
176pub 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.
183pub 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.
190pub 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.
195pub 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.
201pub 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.
206pub fn get_thp_disable() -> Result<bool> {
207 prctl_get_bool(option:libc::PR_GET_THP_DISABLE)
208}
209