Skip to content

Commit 7e90ae2

Browse files
authored
Rework Capability types (#1436)
- Rename CapabilityFlags to CapabilitySet - Deprecated Capability, use CapabilitySet instead. - Add sealed CompatCapability trait.
1 parent 0006343 commit 7e90ae2

File tree

6 files changed

+129
-26
lines changed

6 files changed

+129
-26
lines changed

src/thread/libcap.rs

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,18 +8,22 @@ use crate::{backend, io};
88
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
99
pub struct CapabilitySets {
1010
/// `__user_cap_data_struct.effective`
11-
pub effective: CapabilityFlags,
11+
pub effective: CapabilitySet,
1212
/// `__user_cap_data_struct.permitted`
13-
pub permitted: CapabilityFlags,
13+
pub permitted: CapabilitySet,
1414
/// `__user_cap_data_struct.inheritable`
15-
pub inheritable: CapabilityFlags,
15+
pub inheritable: CapabilitySet,
1616
}
1717

18+
/// Previous name of `CapabilitySet`.
19+
#[deprecated(since = "1.1.0", note = "Renamed to CapabilitySet")]
20+
pub type CapabilityFlags = CapabilitySet;
21+
1822
bitflags! {
1923
/// `CAP_*` constants.
2024
#[repr(transparent)]
2125
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
22-
pub struct CapabilityFlags: u64 {
26+
pub struct CapabilitySet: u64 {
2327
/// `CAP_CHOWN`
2428
const CHOWN = 1 << linux_raw_sys::general::CAP_CHOWN;
2529
/// `CAP_DAC_OVERRIDE`
@@ -156,9 +160,9 @@ fn capget(pid: Option<Pid>) -> io::Result<CapabilitySets> {
156160

157161
// The kernel returns a partitioned bitset that we just combined above.
158162
Ok(CapabilitySets {
159-
effective: CapabilityFlags::from_bits_retain(effective),
160-
permitted: CapabilityFlags::from_bits_retain(permitted),
161-
inheritable: CapabilityFlags::from_bits_retain(inheritable),
163+
effective: CapabilitySet::from_bits_retain(effective),
164+
permitted: CapabilitySet::from_bits_retain(permitted),
165+
inheritable: CapabilitySet::from_bits_retain(inheritable),
162166
})
163167
}
164168

src/thread/mod.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,9 @@ pub use clock::*;
2323
#[cfg(linux_kernel)]
2424
pub use id::*;
2525
#[cfg(linux_kernel)]
26-
pub use libcap::{capabilities, set_capabilities, CapabilityFlags, CapabilitySets};
26+
// #[expect(deprecated, reason = "CapabilityFlags is deprecated")]
27+
#[allow(deprecated)]
28+
pub use libcap::{capabilities, set_capabilities, CapabilityFlags, CapabilitySet, CapabilitySets};
2729
#[cfg(linux_kernel)]
2830
pub use membarrier::*;
2931
#[cfg(linux_kernel)]

src/thread/prctl.rs

Lines changed: 95 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ use crate::prctl::{
2727
};
2828
use crate::utils::as_ptr;
2929

30+
use super::CapabilitySet;
31+
3032
//
3133
// PR_GET_KEEPCAPS/PR_SET_KEEPCAPS
3234
//
@@ -178,6 +180,7 @@ pub fn set_secure_computing_mode(mode: SecureComputingMode) -> io::Result<()> {
178180
const PR_CAPBSET_READ: c_int = 23;
179181

180182
/// Linux per-thread capability.
183+
#[deprecated(since = "1.1.0", note = "Use CapabilitySet with a single bit instead")]
181184
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
182185
#[repr(u32)]
183186
#[non_exhaustive]
@@ -383,6 +386,75 @@ pub enum Capability {
383386
CheckpointRestore = linux_raw_sys::general::CAP_CHECKPOINT_RESTORE,
384387
}
385388

389+
mod private {
390+
pub trait Sealed {}
391+
pub struct Token;
392+
393+
#[allow(deprecated)]
394+
impl Sealed for crate::thread::Capability {}
395+
impl Sealed for crate::thread::CapabilitySet {}
396+
}
397+
/// Compatibility trait to keep existing code that uses the deprecated [`Capability`] type working.
398+
///
399+
/// This trait and its methods are sealed. It must not be used downstream.
400+
pub trait CompatCapability: private::Sealed + Copy {
401+
#[doc(hidden)]
402+
fn as_capability_set(self, _: private::Token) -> CapabilitySet;
403+
}
404+
#[allow(deprecated)]
405+
impl CompatCapability for Capability {
406+
fn as_capability_set(self, _: private::Token) -> CapabilitySet {
407+
match self {
408+
Self::ChangeOwnership => CapabilitySet::CHOWN,
409+
Self::DACOverride => CapabilitySet::DAC_OVERRIDE,
410+
Self::DACReadSearch => CapabilitySet::DAC_READ_SEARCH,
411+
Self::FileOwner => CapabilitySet::FOWNER,
412+
Self::FileSetID => CapabilitySet::FSETID,
413+
Self::Kill => CapabilitySet::KILL,
414+
Self::SetGroupID => CapabilitySet::SETGID,
415+
Self::SetUserID => CapabilitySet::SETUID,
416+
Self::SetPermittedCapabilities => CapabilitySet::SETPCAP,
417+
Self::LinuxImmutable => CapabilitySet::LINUX_IMMUTABLE,
418+
Self::NetBindService => CapabilitySet::NET_BIND_SERVICE,
419+
Self::NetBroadcast => CapabilitySet::NET_BROADCAST,
420+
Self::NetAdmin => CapabilitySet::NET_ADMIN,
421+
Self::NetRaw => CapabilitySet::NET_RAW,
422+
Self::IPCLock => CapabilitySet::IPC_LOCK,
423+
Self::IPCOwner => CapabilitySet::IPC_OWNER,
424+
Self::SystemModule => CapabilitySet::SYS_MODULE,
425+
Self::SystemRawIO => CapabilitySet::SYS_RAWIO,
426+
Self::SystemChangeRoot => CapabilitySet::SYS_CHROOT,
427+
Self::SystemProcessTrace => CapabilitySet::SYS_PTRACE,
428+
Self::SystemProcessAccounting => CapabilitySet::SYS_PACCT,
429+
Self::SystemAdmin => CapabilitySet::SYS_ADMIN,
430+
Self::SystemBoot => CapabilitySet::SYS_BOOT,
431+
Self::SystemNice => CapabilitySet::SYS_NICE,
432+
Self::SystemResource => CapabilitySet::SYS_RESOURCE,
433+
Self::SystemTime => CapabilitySet::SYS_TIME,
434+
Self::SystemTTYConfig => CapabilitySet::SYS_TTY_CONFIG,
435+
Self::MakeNode => CapabilitySet::MKNOD,
436+
Self::Lease => CapabilitySet::LEASE,
437+
Self::AuditWrite => CapabilitySet::AUDIT_WRITE,
438+
Self::AuditControl => CapabilitySet::AUDIT_CONTROL,
439+
Self::SetFileCapabilities => CapabilitySet::SETFCAP,
440+
Self::MACOverride => CapabilitySet::MAC_OVERRIDE,
441+
Self::MACAdmin => CapabilitySet::MAC_ADMIN,
442+
Self::SystemLog => CapabilitySet::SYSLOG,
443+
Self::WakeAlarm => CapabilitySet::WAKE_ALARM,
444+
Self::BlockSuspend => CapabilitySet::BLOCK_SUSPEND,
445+
Self::AuditRead => CapabilitySet::AUDIT_READ,
446+
Self::PerformanceMonitoring => CapabilitySet::PERFMON,
447+
Self::BerkeleyPacketFilters => CapabilitySet::BPF,
448+
Self::CheckpointRestore => CapabilitySet::CHECKPOINT_RESTORE,
449+
}
450+
}
451+
}
452+
impl CompatCapability for CapabilitySet {
453+
fn as_capability_set(self, _: private::Token) -> CapabilitySet {
454+
self
455+
}
456+
}
457+
386458
/// Check if the specified capability is in the calling thread's capability
387459
/// bounding set.
388460
///
@@ -391,8 +463,14 @@ pub enum Capability {
391463
///
392464
/// [`prctl(PR_CAPBSET_READ,…)`]: https://man7.org/linux/man-pages/man2/prctl.2.html
393465
#[inline]
394-
pub fn capability_is_in_bounding_set(capability: Capability) -> io::Result<bool> {
395-
unsafe { prctl_2args(PR_CAPBSET_READ, capability as usize as *mut _) }.map(|r| r != 0)
466+
pub fn capability_is_in_bounding_set(capability: impl CompatCapability) -> io::Result<bool> {
467+
unsafe {
468+
prctl_2args(
469+
PR_CAPBSET_READ,
470+
capability.as_capability_set(private::Token).bits() as usize as *mut _,
471+
)
472+
}
473+
.map(|r| r != 0)
396474
}
397475

398476
const PR_CAPBSET_DROP: c_int = 24;
@@ -406,8 +484,14 @@ const PR_CAPBSET_DROP: c_int = 24;
406484
///
407485
/// [`prctl(PR_CAPBSET_DROP,…)`]: https://man7.org/linux/man-pages/man2/prctl.2.html
408486
#[inline]
409-
pub fn remove_capability_from_bounding_set(capability: Capability) -> io::Result<()> {
410-
unsafe { prctl_2args(PR_CAPBSET_DROP, capability as usize as *mut _) }.map(|_r| ())
487+
pub fn remove_capability_from_bounding_set(capability: impl CompatCapability) -> io::Result<()> {
488+
unsafe {
489+
prctl_2args(
490+
PR_CAPBSET_DROP,
491+
capability.as_capability_set(private::Token).bits() as usize as *mut _,
492+
)
493+
}
494+
.map(|_r| ())
411495
}
412496

413497
//
@@ -608,8 +692,8 @@ const PR_CAP_AMBIENT_IS_SET: usize = 1;
608692
///
609693
/// [`prctl(PR_CAP_AMBIENT,PR_CAP_AMBIENT_IS_SET,…)`]: https://man7.org/linux/man-pages/man2/prctl.2.html
610694
#[inline]
611-
pub fn capability_is_in_ambient_set(capability: Capability) -> io::Result<bool> {
612-
let cap = capability as usize as *mut _;
695+
pub fn capability_is_in_ambient_set(capability: impl CompatCapability) -> io::Result<bool> {
696+
let cap = capability.as_capability_set(private::Token).bits() as usize as *mut _;
613697
unsafe { prctl_3args(PR_CAP_AMBIENT, PR_CAP_AMBIENT_IS_SET as *mut _, cap) }.map(|r| r != 0)
614698
}
615699

@@ -636,13 +720,16 @@ const PR_CAP_AMBIENT_LOWER: usize = 3;
636720
///
637721
/// [`prctl(PR_CAP_AMBIENT,…)`]: https://man7.org/linux/man-pages/man2/prctl.2.html
638722
#[inline]
639-
pub fn configure_capability_in_ambient_set(capability: Capability, enable: bool) -> io::Result<()> {
723+
pub fn configure_capability_in_ambient_set(
724+
capability: impl CompatCapability,
725+
enable: bool,
726+
) -> io::Result<()> {
640727
let sub_operation = if enable {
641728
PR_CAP_AMBIENT_RAISE
642729
} else {
643730
PR_CAP_AMBIENT_LOWER
644731
};
645-
let cap = capability as usize as *mut _;
732+
let cap = capability.as_capability_set(private::Token).bits() as usize as *mut _;
646733

647734
unsafe { prctl_3args(PR_CAP_AMBIENT, sub_operation as *mut _, cap) }.map(|_r| ())
648735
}

tests/process/prctl.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use {
77

88
use rustix::process::*;
99
#[cfg(feature = "thread")]
10-
use rustix::thread::Capability;
10+
use rustix::thread::CapabilitySet;
1111

1212
#[test]
1313
fn test_parent_process_death_signal() {
@@ -87,7 +87,7 @@ fn test_speculative_feature_state() {
8787
#[cfg(feature = "thread")]
8888
#[test]
8989
fn test_is_io_flusher() {
90-
if !thread_has_capability(Capability::SystemResource).unwrap() {
90+
if !thread_has_capability(CapabilitySet::SYS_RESOURCE).unwrap() {
9191
eprintln!("test_is_io_flusher: Test skipped due to missing capability: CAP_SYS_RESOURCE.");
9292
return;
9393
}
@@ -99,7 +99,7 @@ fn test_is_io_flusher() {
9999
#[cfg(feature = "system")]
100100
#[test]
101101
fn test_virtual_memory_map_config_struct_size() {
102-
if !thread_has_capability(Capability::SystemResource).unwrap() {
102+
if !thread_has_capability(CapabilitySet::SYS_RESOURCE).unwrap() {
103103
eprintln!(
104104
"test_virtual_memory_map_config_struct_size: Test skipped due to missing capability: \
105105
CAP_SYS_RESOURCE."
@@ -129,7 +129,7 @@ fn test_floating_point_emulation_control() {
129129
//
130130

131131
#[cfg(feature = "thread")]
132-
pub(crate) fn thread_has_capability(capability: Capability) -> io::Result<bool> {
132+
pub(crate) fn thread_has_capability(capability: CapabilitySet) -> io::Result<bool> {
133133
const _LINUX_CAPABILITY_VERSION_3: u32 = 0x2008_0522;
134134

135135
#[repr(C)]
@@ -175,7 +175,7 @@ pub(crate) fn thread_has_capability(capability: Capability) -> io::Result<bool>
175175
return Err(io::Error::last_os_error());
176176
}
177177

178-
let cap_index = capability as u32;
178+
let cap_index = capability.bits() as u32;
179179
let (data_index, cap_index) = if cap_index < 32 {
180180
(0, cap_index)
181181
} else {

tests/system/reboot.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,15 @@
33
fn test_reboot() {
44
use rustix::io::Errno;
55
use rustix::system::{self, RebootCommand};
6-
use rustix::thread::{self, CapabilityFlags};
6+
use rustix::thread::{self, CapabilitySet};
77

88
let mut capabilities = thread::capabilities(None).expect("Failed to get capabilities");
99

10-
capabilities.effective.set(CapabilityFlags::SYS_BOOT, false);
10+
capabilities.effective.set(CapabilitySet::SYS_BOOT, false);
1111

1212
thread::set_capabilities(None, capabilities).expect("Failed to set capabilities");
1313

14-
// The reboot syscall requires the `CapabilityFlags::SYS_BOOT` permission
14+
// The reboot syscall requires the `CapabilitySet::SYS_BOOT` permission
1515
// to be called, otherwise [`Errno::PERM`] is returned
1616
assert_eq!(system::reboot(RebootCommand::Restart), Err(Errno::PERM));
1717
}

tests/thread/prctl.rs

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,12 @@ fn test_name() {
1818

1919
#[test]
2020
fn test_capability_is_in_bounding_set() {
21-
dbg!(capability_is_in_bounding_set(Capability::ChangeOwnership).unwrap());
21+
dbg!(capability_is_in_bounding_set(CapabilitySet::CHOWN).unwrap());
22+
dbg!(capability_is_in_bounding_set(
23+
#[allow(deprecated)]
24+
Capability::ChangeOwnership
25+
)
26+
.unwrap());
2227
}
2328

2429
#[test]
@@ -38,7 +43,12 @@ fn test_no_new_privs() {
3843

3944
#[test]
4045
fn test_capability_is_in_ambient_set() {
41-
dbg!(capability_is_in_ambient_set(Capability::ChangeOwnership).unwrap());
46+
dbg!(capability_is_in_ambient_set(CapabilitySet::CHOWN).unwrap());
47+
dbg!(capability_is_in_ambient_set(
48+
#[allow(deprecated)]
49+
Capability::ChangeOwnership
50+
)
51+
.unwrap());
4252
}
4353

4454
#[cfg(target_arch = "aarch64")]

0 commit comments

Comments
 (0)