#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
#[cfg(feature = "std")]
#[inline]
pub fn set_name<N: AsRef<std::ffi::OsStr>>(name: N) -> crate::Result<()> {
use std::os::unix::ffi::OsStrExt;
raw_set_name(name.as_ref().as_bytes())
}
#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
#[cfg(feature = "std")]
fn raw_set_name(name: &[u8]) -> crate::Result<()> {
if name.contains(&0) {
return Err(crate::Error::from_code(libc::EINVAL));
}
let mut buf = [0; 16];
let ptr = if name.len() < buf.len() {
buf[..name.len()].copy_from_slice(name);
buf.as_ptr()
} else {
name.as_ptr()
};
unsafe {
crate::raw_prctl(libc::PR_SET_NAME, ptr as libc::c_ulong, 0, 0, 0)?;
}
Ok(())
}
#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
#[cfg(feature = "std")]
pub fn get_name() -> crate::Result<std::ffi::OsString> {
use std::os::unix::ffi::OsStringExt;
let mut name_vec = vec![0; 16];
unsafe {
crate::raw_prctl(
libc::PR_GET_NAME,
name_vec.as_ptr() as libc::c_ulong,
0,
0,
0,
)?;
}
name_vec.truncate(name_vec.iter().position(|x| *x == 0).unwrap());
Ok(std::ffi::OsString::from_vec(name_vec))
}
#[inline]
pub fn get_no_new_privs() -> crate::Result<bool> {
let res = unsafe { crate::raw_prctl(libc::PR_GET_NO_NEW_PRIVS, 0, 0, 0, 0) }?;
Ok(res != 0)
}
#[inline]
pub fn set_no_new_privs() -> crate::Result<()> {
unsafe {
crate::raw_prctl(libc::PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)?;
}
Ok(())
}
#[inline]
pub fn get_keepcaps() -> crate::Result<bool> {
let res = unsafe { crate::raw_prctl(libc::PR_GET_KEEPCAPS, 0, 0, 0, 0) }?;
Ok(res != 0)
}
#[inline]
pub fn set_keepcaps(keep: bool) -> crate::Result<()> {
unsafe {
crate::raw_prctl(libc::PR_SET_KEEPCAPS, keep as libc::c_ulong, 0, 0, 0)?;
}
Ok(())
}
#[inline]
pub fn get_dumpable() -> crate::Result<bool> {
let res = unsafe { crate::raw_prctl(libc::PR_GET_DUMPABLE, 0, 0, 0, 0) }?;
Ok(res != 0)
}
#[inline]
pub fn set_dumpable(dumpable: bool) -> crate::Result<()> {
unsafe {
crate::raw_prctl(libc::PR_SET_DUMPABLE, dumpable as libc::c_ulong, 0, 0, 0)?;
}
Ok(())
}
#[inline]
pub fn set_subreaper(flag: bool) -> crate::Result<()> {
unsafe {
crate::raw_prctl(libc::PR_SET_CHILD_SUBREAPER, flag as libc::c_ulong, 0, 0, 0)?;
}
Ok(())
}
#[inline]
pub fn get_subreaper() -> crate::Result<bool> {
let mut res = 0;
unsafe {
crate::raw_prctl(
libc::PR_GET_CHILD_SUBREAPER,
(&mut res) as *mut libc::c_int as libc::c_ulong,
0,
0,
0,
)?;
}
Ok(res != 0)
}
#[inline]
pub fn set_pdeathsig(sig: Option<libc::c_int>) -> crate::Result<()> {
unsafe {
crate::raw_prctl(
libc::PR_SET_PDEATHSIG,
sig.unwrap_or(0) as libc::c_ulong,
0,
0,
0,
)?;
}
Ok(())
}
#[inline]
pub fn get_pdeathsig() -> crate::Result<Option<libc::c_int>> {
let mut sig = 0;
unsafe {
crate::raw_prctl(
libc::PR_GET_PDEATHSIG,
(&mut sig) as *mut libc::c_int as libc::c_ulong,
0,
0,
0,
)?;
}
Ok(if sig == 0 { None } else { Some(sig) })
}
bitflags::bitflags! {
pub struct Secbits: libc::c_ulong {
const NOROOT = 0x1;
const NOROOT_LOCKED = 0x2;
const NO_SETUID_FIXUP = 0x4;
const NO_SETUID_FIXUP_LOCKED = 0x8;
const KEEP_CAPS = 0x10;
const KEEP_CAPS_LOCKED = 0x20;
const NO_CAP_AMBIENT_RAISE = 0x40;
const NO_CAP_AMBIENT_RAISE_LOCKED = 0x80;
}
}
#[inline]
pub fn get_securebits() -> crate::Result<Secbits> {
let f = unsafe { crate::raw_prctl(libc::PR_GET_SECUREBITS, 0, 0, 0, 0) }?;
Ok(Secbits::from_bits_truncate(f as libc::c_ulong))
}
#[inline]
pub fn set_securebits(flags: Secbits) -> crate::Result<()> {
unsafe {
crate::raw_prctl(libc::PR_SET_SECUREBITS, flags.bits(), 0, 0, 0)?;
}
Ok(())
}
#[inline]
pub fn get_seccomp() -> crate::Result<bool> {
let res = unsafe { crate::raw_prctl(libc::PR_GET_SECCOMP, 0, 0, 0, 0) }?;
Ok(res != 0)
}
#[inline]
pub fn set_seccomp_strict() -> crate::Result<()> {
unsafe {
crate::raw_prctl(
libc::PR_SET_SECCOMP,
libc::SECCOMP_MODE_STRICT as libc::c_ulong,
0,
0,
0,
)?;
}
Ok(())
}
#[allow(clippy::needless_return)]
#[inline]
pub fn get_timerslack() -> crate::Result<libc::c_ulong> {
cfg_if::cfg_if! {
if #[cfg(feature = "sc")] {
return crate::sc_res_decode(unsafe {
sc::syscall!(PRCTL, libc::PR_GET_TIMERSLACK, 0, 0, 0)
}).map(|res| res as libc::c_ulong);
} else {
let res = unsafe { libc::syscall(libc::SYS_prctl, libc::PR_GET_TIMERSLACK, 0, 0, 0) };
return if res == -1 {
Err(crate::Error::last())
} else {
Ok(res as libc::c_ulong)
};
}
}
}
#[inline]
pub fn set_timerslack(new_slack: libc::c_ulong) -> crate::Result<()> {
unsafe {
crate::raw_prctl(libc::PR_SET_TIMERSLACK, new_slack, 0, 0, 0)?;
}
Ok(())
}
#[inline]
pub fn set_thp_disable(disable: bool) -> crate::Result<()> {
unsafe {
crate::raw_prctl(libc::PR_SET_THP_DISABLE, disable as _, 0, 0, 0)?;
}
Ok(())
}
#[inline]
pub fn get_thp_disable() -> crate::Result<bool> {
let res = unsafe { crate::raw_prctl(libc::PR_GET_THP_DISABLE, 0, 0, 0, 0) }?;
Ok(res != 0)
}
#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)]
pub enum Ptracer {
None,
Any,
Pid(libc::pid_t),
}
#[inline]
pub fn set_ptracer(ptracer: Ptracer) -> crate::Result<()> {
let pid = match ptracer {
Ptracer::None => 0,
Ptracer::Any => crate::sys::PR_SET_PTRACER_ANY,
Ptracer::Pid(pid) if pid <= 0 => return Err(crate::Error::from_code(libc::EINVAL)),
Ptracer::Pid(pid) => pid as libc::c_ulong,
};
unsafe {
crate::raw_prctl(libc::PR_SET_PTRACER, pid, 0, 0, 0)?;
}
Ok(())
}
#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)]
#[repr(i32)]
pub enum MceKill {
Early = libc::PR_MCE_KILL_EARLY,
Late = libc::PR_MCE_KILL_LATE,
Default = libc::PR_MCE_KILL_DEFAULT,
}
#[inline]
pub fn set_mce_kill(policy: MceKill) -> crate::Result<()> {
unsafe {
crate::raw_prctl(
libc::PR_MCE_KILL,
libc::PR_MCE_KILL_SET as _,
policy as _,
0,
0,
)?;
}
Ok(())
}
#[inline]
pub fn get_mce_kill() -> crate::Result<MceKill> {
let res = unsafe { crate::raw_prctl(libc::PR_MCE_KILL_GET, 0, 0, 0, 0) }?;
Ok(match res {
libc::PR_MCE_KILL_EARLY => MceKill::Early,
libc::PR_MCE_KILL_LATE => MceKill::Late,
libc::PR_MCE_KILL_DEFAULT => MceKill::Default,
_ => unreachable!(),
})
}
#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)]
#[repr(i32)]
pub enum SpecVariant {
StoreBypass = crate::sys::PR_SPEC_STORE_BYPASS,
IndirectBranch = crate::sys::PR_SPEC_INDIRECT_BRANCH,
L1DFlush = crate::sys::PR_SPEC_L1D_FLUSH,
}
bitflags::bitflags! {
pub struct SpecFlags: libc::c_int {
const PRCTL = crate::sys::PR_SPEC_PRCTL;
const ENABLE = crate::sys::PR_SPEC_ENABLE;
const DISABLE = crate::sys::PR_SPEC_DISABLE;
const FORCE_DISABLE = crate::sys::PR_SPEC_FORCE_DISABLE;
const DISABLE_NOEXEC = crate::sys::PR_SPEC_DISABLE_NOEXEC;
}
}
#[inline]
pub fn get_speculation_ctrl(variant: SpecVariant) -> crate::Result<SpecFlags> {
let res =
unsafe { crate::raw_prctl(crate::sys::PR_GET_SPECULATION_CTRL, variant as _, 0, 0, 0) }?;
Ok(SpecFlags::from_bits_truncate(res))
}
#[inline]
pub fn set_speculation_ctrl(variant: SpecVariant, control: SpecFlags) -> crate::Result<()> {
unsafe {
crate::raw_prctl(
crate::sys::PR_SET_SPECULATION_CTRL,
variant as _,
control.bits() as _,
0,
0,
)?;
}
Ok(())
}
#[inline]
pub fn get_tid_address() -> crate::Result<*mut libc::c_int> {
cfg_if::cfg_if! {
if #[cfg(any(target_arch = "x86", target_arch = "mips"))] {
let mut buf = 0u64;
unsafe {
crate::raw_prctl(libc::PR_GET_TID_ADDRESS, &mut buf as *mut _ as _, 0, 0, 0)?;
}
let addr = if cfg!(target_endian = "big") {
if buf & 0xFFFF_FFFF == 0 {
(buf >> 32) as *mut libc::c_int
} else {
debug_assert_eq!(buf >> 32, 0, "address too large");
buf as *mut libc::c_int
}
} else {
debug_assert_eq!(buf >> 32, 0, "address too large");
buf as *mut libc::c_int
};
} else {
let mut addr = core::ptr::null_mut();
unsafe {
crate::raw_prctl(libc::PR_GET_TID_ADDRESS, &mut addr as *mut _ as _, 0, 0, 0)?;
}
}
}
Ok(addr)
}
#[inline]
pub fn enable_perf_events() -> crate::Result<()> {
unsafe {
crate::raw_prctl(
libc::PR_TASK_PERF_EVENTS_ENABLE,
0,
0,
0,
0,
)?;
}
Ok(())
}
#[inline]
pub fn disable_perf_events() -> crate::Result<()> {
unsafe {
crate::raw_prctl(
libc::PR_TASK_PERF_EVENTS_DISABLE,
0,
0,
0,
0,
)?;
}
Ok(())
}
#[inline]
pub fn get_io_flusher() -> crate::Result<bool> {
let res = unsafe { crate::raw_prctl(crate::sys::PR_GET_IO_FLUSHER, 0, 0, 0, 0) }?;
Ok(res != 0)
}
#[inline]
pub fn set_io_flusher(flusher: bool) -> crate::Result<()> {
unsafe {
crate::raw_prctl(
crate::sys::PR_SET_IO_FLUSHER,
flusher as libc::c_ulong,
0,
0,
0,
)?;
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_keepcaps() {
let old_keepcaps = get_keepcaps().unwrap();
set_keepcaps(true).unwrap();
assert!(get_keepcaps().unwrap());
assert!(get_securebits().unwrap().contains(Secbits::KEEP_CAPS));
set_keepcaps(false).unwrap();
assert!(!get_keepcaps().unwrap());
assert!(!get_securebits().unwrap().contains(Secbits::KEEP_CAPS));
set_keepcaps(old_keepcaps).unwrap();
}
#[test]
fn test_nnp() {
set_no_new_privs().unwrap();
assert!(get_no_new_privs().unwrap());
set_no_new_privs().unwrap();
assert!(get_no_new_privs().unwrap());
}
#[test]
fn test_subreaper() {
let was_subreaper = get_subreaper().unwrap();
set_subreaper(false).unwrap();
assert!(!get_subreaper().unwrap());
set_subreaper(true).unwrap();
assert!(get_subreaper().unwrap());
set_subreaper(was_subreaper).unwrap();
}
#[test]
fn test_pdeathsig() {
let orig_pdeathsig = get_pdeathsig().unwrap();
set_pdeathsig(None).unwrap();
assert_eq!(get_pdeathsig().unwrap(), None);
set_pdeathsig(Some(0)).unwrap();
assert_eq!(get_pdeathsig().unwrap(), None);
set_pdeathsig(Some(libc::SIGCHLD)).unwrap();
assert_eq!(get_pdeathsig().unwrap(), Some(libc::SIGCHLD));
assert_eq!(set_pdeathsig(Some(-1)).unwrap_err().code(), libc::EINVAL);
set_pdeathsig(orig_pdeathsig).unwrap();
}
#[test]
fn test_dumpable() {
assert!(get_dumpable().unwrap());
set_dumpable(true).unwrap();
assert!(get_dumpable().unwrap());
}
#[cfg(feature = "std")]
#[test]
fn test_name() {
let orig_name = get_name().unwrap();
set_name("capctl-short").unwrap();
assert_eq!(get_name().unwrap(), "capctl-short");
set_name("capctl-very-very-long").unwrap();
assert_eq!(get_name().unwrap(), "capctl-very-ver");
assert_eq!(set_name("a\0").unwrap_err().code(), libc::EINVAL);
set_name(&orig_name).unwrap();
assert_eq!(get_name().unwrap(), orig_name);
}
#[test]
fn test_securebits() {
if crate::caps::CapState::get_current()
.unwrap()
.effective
.has(crate::caps::Cap::SETPCAP)
{
let orig_secbits = get_securebits().unwrap();
let mut secbits = orig_secbits;
secbits.insert(Secbits::KEEP_CAPS);
set_securebits(secbits).unwrap();
assert!(get_keepcaps().unwrap());
secbits.remove(Secbits::KEEP_CAPS);
set_securebits(secbits).unwrap();
assert!(!get_keepcaps().unwrap());
set_securebits(orig_secbits).unwrap();
} else {
assert_eq!(
set_securebits(get_securebits().unwrap())
.unwrap_err()
.code(),
libc::EPERM
);
}
}
#[test]
fn test_get_seccomp() {
get_seccomp().unwrap();
}
#[test]
fn test_set_seccomp_strict() {
match unsafe { libc::fork() } {
-1 => panic!("{}", crate::Error::last()),
0 => {
set_seccomp_strict().unwrap();
unsafe {
libc::syscall(libc::SYS_exit, 0);
libc::_exit(1);
}
}
pid => {
let mut wstatus = 0;
if unsafe { libc::waitpid(pid, &mut wstatus, 0) } != pid {
panic!("{}", crate::Error::last());
}
assert!(libc::WIFEXITED(wstatus));
assert_eq!(libc::WEXITSTATUS(wstatus), 0);
}
}
}
#[cfg(feature = "std")]
#[test]
fn test_timerslack() {
let orig_timerslack = get_timerslack().unwrap();
set_timerslack(orig_timerslack + 1).unwrap();
std::thread::spawn(move || {
assert_eq!(get_timerslack().unwrap(), orig_timerslack + 1);
set_timerslack(orig_timerslack).unwrap();
assert_eq!(get_timerslack().unwrap(), orig_timerslack);
set_timerslack(0).unwrap();
assert_eq!(get_timerslack().unwrap(), orig_timerslack + 1);
})
.join()
.unwrap();
}
#[test]
fn test_thp_disable() {
let orig_thp_disable = get_thp_disable().unwrap();
set_thp_disable(true).unwrap();
assert!(get_thp_disable().unwrap());
set_thp_disable(false).unwrap();
assert!(!get_thp_disable().unwrap());
set_thp_disable(orig_thp_disable).unwrap();
assert_eq!(get_thp_disable().unwrap(), orig_thp_disable);
}
#[cfg(feature = "std")]
#[test]
fn test_ptracer() {
assert_eq!(
set_ptracer(Ptracer::Pid(0)).unwrap_err().code(),
libc::EINVAL
);
assert_eq!(
set_ptracer(Ptracer::Pid(-1)).unwrap_err().code(),
libc::EINVAL
);
if std::path::Path::new("/proc/sys/kernel/yama/ptrace_scope").exists() {
assert_eq!(
set_ptracer(Ptracer::Pid(libc::pid_t::MAX))
.unwrap_err()
.code(),
libc::EINVAL
);
set_ptracer(Ptracer::Pid(1)).unwrap();
set_ptracer(Ptracer::Pid(unsafe { libc::getppid() })).unwrap();
set_ptracer(Ptracer::Pid(unsafe { libc::getpid() })).unwrap();
set_ptracer(Ptracer::None).unwrap();
} else {
for &pid in unsafe { [libc::pid_t::MAX, 1, libc::getppid(), libc::getpid()] }.iter() {
assert_eq!(
set_ptracer(Ptracer::Pid(pid)).unwrap_err().code(),
libc::EINVAL
);
}
assert_eq!(set_ptracer(Ptracer::None).unwrap_err().code(), libc::EINVAL);
}
}
#[test]
fn test_mce_kill() {
let orig_mce_kill = get_mce_kill().unwrap();
set_mce_kill(MceKill::Early).unwrap();
assert_eq!(get_mce_kill().unwrap(), MceKill::Early);
set_mce_kill(MceKill::Late).unwrap();
assert_eq!(get_mce_kill().unwrap(), MceKill::Late);
set_mce_kill(MceKill::Default).unwrap();
assert_eq!(get_mce_kill().unwrap(), MceKill::Default);
set_mce_kill(orig_mce_kill).unwrap();
assert_eq!(get_mce_kill().unwrap(), orig_mce_kill);
}
#[test]
fn test_get_tid_address() {
get_tid_address().unwrap();
}
}