Skip to content

Commit c9834cf

Browse files
committed
ruleset: Use OwnedFd instead of RawFd
Improve consistency by delegating lifetime management to OwnedFd. The only visible change would be that with latest Rust versions, the duplicated file descriptor will be greater than 2 (not with Rust 1.63). See https://doc.rust-lang.org/1.63.0/std/os/unix/io/struct.OwnedFd.html Remove the explicit RulesetCreated's Drop implementation which is now handled by its inner OwnedFd. Replate IntoRawFd with the safer Into<Option<OwnedFd>> for RulesetCreated. The former is implemented for OwnedFd. This is not a breaking change because the IntoRawFd implementation was never released. Add tests leveraging RulesetCreated::try_clone(). Signed-off-by: Mickaël Salaün <mic@digikod.net>
1 parent fb7850f commit c9834cf

File tree

3 files changed

+71
-36
lines changed

3 files changed

+71
-36
lines changed

src/fs.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -318,7 +318,7 @@ fn path_beneath_try_compat_children() {
318318
// Do not actually perform any syscall.
319319
ruleset.compat.state = CompatState::Dummy;
320320
assert!(matches!(
321-
RulesetCreated::new(ruleset, -1)
321+
RulesetCreated::new(ruleset, None)
322322
.set_compatibility(CompatLevel::HardRequirement)
323323
.add_rule(PathBeneath::new(PathFd::new("/dev/null").unwrap(), access_file))
324324
.unwrap_err(),
@@ -332,7 +332,7 @@ fn path_beneath_try_compat_children() {
332332
// Do not actually perform any syscall.
333333
ruleset.compat.state = CompatState::Dummy;
334334
assert!(matches!(
335-
RulesetCreated::new(ruleset, -1)
335+
RulesetCreated::new(ruleset, None)
336336
.set_compatibility(CompatLevel::HardRequirement)
337337
.add_rule(PathBeneath::new(PathFd::new("/dev/null").unwrap(), access_file))
338338
.unwrap_err(),

src/lib.rs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -452,4 +452,33 @@ mod tests {
452452
false,
453453
);
454454
}
455+
456+
#[test]
457+
fn ruleset_created_try_clone_ownedfd() {
458+
use std::os::unix::io::{AsRawFd, OwnedFd};
459+
460+
let abi = ABI::V1;
461+
check_ruleset_support(
462+
abi,
463+
Some(abi),
464+
move |ruleset: Ruleset| -> _ {
465+
let ruleset1 = ruleset.handle_access(AccessFs::from_all(abi))?.create()?;
466+
let ruleset2 = ruleset1.try_clone().unwrap();
467+
let ruleset3 = ruleset2.try_clone().unwrap();
468+
469+
let some1: Option<OwnedFd> = ruleset1.into();
470+
if let Some(fd1) = some1 {
471+
assert!(fd1.as_raw_fd() >= 0);
472+
473+
let some2: Option<OwnedFd> = ruleset2.into();
474+
let fd2 = some2.unwrap();
475+
assert!(fd2.as_raw_fd() >= 0);
476+
477+
assert_ne!(fd1.as_raw_fd(), fd2.as_raw_fd());
478+
}
479+
Ok(ruleset3.restrict_self()?)
480+
},
481+
false,
482+
);
483+
}
455484
}

src/ruleset.rs

Lines changed: 40 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,9 @@ use crate::{
66
Compatibility, Compatible, CreateRulesetError, HandledAccess, PrivateHandledAccess,
77
RestrictSelfError, RulesetError, Scope, ScopeError, TryCompat,
88
};
9-
use libc::close;
109
use std::io::Error;
1110
use std::mem::size_of_val;
12-
use std::os::unix::io::{IntoRawFd, RawFd};
11+
use std::os::unix::io::{AsRawFd, FromRawFd, OwnedFd};
1312

1413
#[cfg(test)]
1514
use crate::*;
@@ -272,7 +271,7 @@ impl Ruleset {
272271
CompatLevel::HardRequirement => {
273272
Err(CreateRulesetError::MissingHandledAccess)
274273
}
275-
_ => Ok(RulesetCreated::new(self, -1)),
274+
_ => Ok(RulesetCreated::new(self, None)),
276275
}
277276
}
278277
CompatState::Full | CompatState::Partial => {
@@ -290,7 +289,10 @@ impl Ruleset {
290289
scoped: self.actual_scoped.bits(),
291290
};
292291
match unsafe { uapi::landlock_create_ruleset(&attr, size_of_val(&attr), 0) } {
293-
fd if fd >= 0 => Ok(RulesetCreated::new(self, fd)),
292+
fd if fd >= 0 => Ok(RulesetCreated::new(
293+
self,
294+
Some(unsafe { OwnedFd::from_raw_fd(fd) }),
295+
)),
294296
_ => Err(CreateRulesetError::CreateRulesetCall {
295297
source: Error::last_os_error(),
296298
}),
@@ -600,15 +602,20 @@ pub trait RulesetCreatedAttr: Sized + AsMut<RulesetCreated> + Compatible {
600602
};
601603
match self_ref.compat.state {
602604
CompatState::Init | CompatState::No | CompatState::Dummy => Ok(self),
603-
CompatState::Full | CompatState::Partial => match unsafe {
604-
uapi::landlock_add_rule(self_ref.fd, T::TYPE_ID, compat_rule.as_ptr(), 0)
605-
} {
606-
0 => Ok(self),
607-
_ => Err(AddRuleError::<U>::AddRuleCall {
608-
source: Error::last_os_error(),
605+
CompatState::Full | CompatState::Partial => {
606+
#[cfg(test)]
607+
assert!(self_ref.fd.is_some());
608+
let fd = self_ref.fd.as_ref().map(|f| f.as_raw_fd()).unwrap_or(-1);
609+
match unsafe {
610+
uapi::landlock_add_rule(fd, T::TYPE_ID, compat_rule.as_ptr(), 0)
611+
} {
612+
0 => Ok(self),
613+
_ => Err(AddRuleError::<U>::AddRuleCall {
614+
source: Error::last_os_error(),
615+
}
616+
.into()),
609617
}
610-
.into()),
611-
},
618+
}
612619
}
613620
};
614621
Ok(body()?)
@@ -715,15 +722,15 @@ pub trait RulesetCreatedAttr: Sized + AsMut<RulesetCreated> + Compatible {
715722
/// Ruleset created with [`Ruleset::create()`].
716723
#[cfg_attr(test, derive(Debug))]
717724
pub struct RulesetCreated {
718-
fd: RawFd,
725+
fd: Option<OwnedFd>,
719726
no_new_privs: bool,
720727
pub(crate) requested_handled_fs: BitFlags<AccessFs>,
721728
pub(crate) requested_handled_net: BitFlags<AccessNet>,
722729
compat: Compatibility,
723730
}
724731

725732
impl RulesetCreated {
726-
pub(crate) fn new(ruleset: Ruleset, fd: RawFd) -> Self {
733+
pub(crate) fn new(ruleset: Ruleset, fd: Option<OwnedFd>) -> Self {
727734
// The compatibility state is initialized by Ruleset::create().
728735
#[cfg(test)]
729736
assert!(!matches!(ruleset.compat.state, CompatState::Init));
@@ -793,7 +800,11 @@ impl RulesetCreated {
793800
no_new_privs: enforced_nnp,
794801
}),
795802
CompatState::Full | CompatState::Partial => {
796-
match unsafe { uapi::landlock_restrict_self(self.fd, 0) } {
803+
#[cfg(test)]
804+
assert!(self.fd.is_some());
805+
// Does not consume ruleset FD, which will be automatically closed after this block.
806+
let fd = self.fd.as_ref().map(|f| f.as_raw_fd()).unwrap_or(-1);
807+
match unsafe { uapi::landlock_restrict_self(fd, 0) } {
797808
0 => {
798809
self.compat.update(CompatState::Full);
799810
Ok(RestrictionStatus {
@@ -818,13 +829,7 @@ impl RulesetCreated {
818829
/// On error, returns [`std::io::Error`].
819830
pub fn try_clone(&self) -> std::io::Result<Self> {
820831
Ok(RulesetCreated {
821-
fd: match self.fd {
822-
-1 => -1,
823-
self_fd => match unsafe { libc::fcntl(self_fd, libc::F_DUPFD_CLOEXEC, 0) } {
824-
dup_fd if dup_fd >= 0 => dup_fd,
825-
_ => return Err(Error::last_os_error()),
826-
},
827-
},
832+
fd: self.fd.as_ref().map(|f| f.try_clone()).transpose()?,
828833
no_new_privs: self.no_new_privs,
829834
requested_handled_fs: self.requested_handled_fs,
830835
requested_handled_net: self.requested_handled_net,
@@ -833,20 +838,21 @@ impl RulesetCreated {
833838
}
834839
}
835840

836-
impl Drop for RulesetCreated {
837-
fn drop(&mut self) {
838-
if self.fd >= 0 {
839-
unsafe { close(self.fd) };
840-
}
841+
impl From<RulesetCreated> for Option<OwnedFd> {
842+
fn from(ruleset: RulesetCreated) -> Self {
843+
ruleset.fd
841844
}
842845
}
843846

844-
impl IntoRawFd for RulesetCreated {
845-
fn into_raw_fd(self) -> RawFd {
846-
let fd = self.fd;
847-
std::mem::forget(self);
848-
fd
849-
}
847+
#[test]
848+
fn ruleset_created_ownedfd_none() {
849+
let ruleset = Ruleset::from(ABI::Unsupported)
850+
.handle_access(AccessFs::Execute)
851+
.unwrap()
852+
.create()
853+
.unwrap();
854+
let fd: Option<OwnedFd> = ruleset.into();
855+
assert!(fd.is_none());
850856
}
851857

852858
impl AsMut<RulesetCreated> for RulesetCreated {
@@ -1126,7 +1132,7 @@ fn ruleset_unsupported() {
11261132
.unwrap();
11271133
// Fakes a call to create() to test without involving the kernel (i.e. no
11281134
// landlock_ruleset_create() call).
1129-
let ruleset_created = RulesetCreated::new(ruleset, -1);
1135+
let ruleset_created = RulesetCreated::new(ruleset, None);
11301136
assert!(matches!(
11311137
ruleset_created
11321138
.add_rule(PathBeneath::new(

0 commit comments

Comments
 (0)