Skip to content

Commit beecdf8

Browse files
committed
ruleset: Add the Errno helper
This helper is useful for FFI to easily translate a Landlock error into an errno value. Extend check_ruleset_support() with errno translation tests. Add the unsupported_handled_access_errno() test. Signed-off-by: Mickaël Salaün <mic@digikod.net>
1 parent 6d18345 commit beecdf8

File tree

3 files changed

+113
-9
lines changed

3 files changed

+113
-9
lines changed

src/errors.rs

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,3 +209,89 @@ pub(crate) enum TestRulesetError {
209209
#[error(transparent)]
210210
File(#[from] std::io::Error),
211211
}
212+
213+
/// Get the underlying errno value.
214+
///
215+
/// This helper is useful for FFI to easily translate a Landlock error into an
216+
/// errno value.
217+
#[derive(Debug, PartialEq, Eq)]
218+
pub struct Errno(libc::c_int);
219+
220+
impl Errno {
221+
pub fn new(value: libc::c_int) -> Self {
222+
Self(value)
223+
}
224+
}
225+
226+
impl<T> From<T> for Errno
227+
where
228+
T: std::error::Error,
229+
{
230+
fn from(error: T) -> Self {
231+
let default = libc::EINVAL;
232+
if let Some(e) = error.source() {
233+
if let Some(e) = e.downcast_ref::<std::io::Error>() {
234+
return Errno(e.raw_os_error().unwrap_or(default));
235+
}
236+
}
237+
Errno(default)
238+
}
239+
}
240+
241+
impl AsRef<libc::c_int> for Errno {
242+
fn as_ref(&self) -> &libc::c_int {
243+
&self.0
244+
}
245+
}
246+
247+
#[cfg(test)]
248+
fn _test_ruleset_errno(expected_errno: libc::c_int) {
249+
use std::io::Error;
250+
251+
let handle_access_err = RulesetError::HandleAccesses(HandleAccessesError::Fs(
252+
HandleAccessError::Compat(CompatError::Access(AccessError::Empty)),
253+
));
254+
assert_eq!(Errno::from(handle_access_err).0, libc::EINVAL);
255+
256+
let create_ruleset_err = RulesetError::CreateRuleset(CreateRulesetError::CreateRulesetCall {
257+
source: Error::from_raw_os_error(expected_errno),
258+
});
259+
assert_eq!(Errno::from(create_ruleset_err).0, expected_errno);
260+
261+
let add_rules_fs_err = RulesetError::AddRules(AddRulesError::Fs(AddRuleError::AddRuleCall {
262+
source: Error::from_raw_os_error(expected_errno),
263+
}));
264+
assert_eq!(Errno::from(add_rules_fs_err).0, expected_errno);
265+
266+
let add_rules_net_err = RulesetError::AddRules(AddRulesError::Net(AddRuleError::AddRuleCall {
267+
source: Error::from_raw_os_error(expected_errno),
268+
}));
269+
assert_eq!(Errno::from(add_rules_net_err).0, expected_errno);
270+
271+
let add_rules_other_err =
272+
RulesetError::AddRules(AddRulesError::Fs(AddRuleError::UnhandledAccess {
273+
access: AccessFs::Execute.into(),
274+
incompatible: BitFlags::<AccessFs>::EMPTY,
275+
}));
276+
assert_eq!(Errno::from(add_rules_other_err).0, libc::EINVAL);
277+
278+
let restrict_self_err = RulesetError::RestrictSelf(RestrictSelfError::RestrictSelfCall {
279+
source: Error::from_raw_os_error(expected_errno),
280+
});
281+
assert_eq!(Errno::from(restrict_self_err).0, expected_errno);
282+
283+
let set_no_new_privs_err = RulesetError::RestrictSelf(RestrictSelfError::SetNoNewPrivsCall {
284+
source: Error::from_raw_os_error(expected_errno),
285+
});
286+
assert_eq!(Errno::from(set_no_new_privs_err).0, expected_errno);
287+
288+
let create_ruleset_missing_err =
289+
RulesetError::CreateRuleset(CreateRulesetError::MissingHandledAccess);
290+
assert_eq!(Errno::from(create_ruleset_missing_err).0, libc::EINVAL);
291+
}
292+
293+
#[test]
294+
fn test_ruleset_errno() {
295+
_test_ruleset_errno(libc::EACCES);
296+
_test_ruleset_errno(libc::EIO);
297+
}

src/lib.rs

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -83,8 +83,9 @@ pub use access::Access;
8383
pub use compat::{CompatLevel, Compatible, ABI};
8484
pub use enumflags2::{make_bitflags, BitFlags};
8585
pub use errors::{
86-
AccessError, AddRuleError, AddRulesError, CompatError, CreateRulesetError, HandleAccessError,
87-
HandleAccessesError, PathBeneathError, PathFdError, RestrictSelfError, RulesetError,
86+
AccessError, AddRuleError, AddRulesError, CompatError, CreateRulesetError, Errno,
87+
HandleAccessError, HandleAccessesError, PathBeneathError, PathFdError, RestrictSelfError,
88+
RulesetError,
8889
};
8990
pub use fs::{path_beneath_rules, AccessFs, PathBeneath, PathFd};
9091
pub use net::{AccessNet, NetPort};
@@ -168,13 +169,18 @@ mod tests {
168169
let errno = get_errno_from_landlock_status();
169170
println!("Expecting error {errno:?}");
170171
match ret {
171-
Err(TestRulesetError::Ruleset(RulesetError::CreateRuleset(
172-
CreateRulesetError::CreateRulesetCall { source },
173-
))) => match (source.raw_os_error(), errno) {
174-
(Some(e1), Some(e2)) => assert_eq!(e1, e2),
175-
(Some(e1), None) => assert!(matches!(e1, libc::EINVAL | libc::E2BIG)),
176-
_ => unreachable!(),
177-
},
172+
Err(
173+
ref error @ TestRulesetError::Ruleset(RulesetError::CreateRuleset(
174+
CreateRulesetError::CreateRulesetCall { ref source },
175+
)),
176+
) => {
177+
assert_eq!(source.raw_os_error(), Some(*Errno::from(error).as_ref()));
178+
match (source.raw_os_error(), errno) {
179+
(Some(e1), Some(e2)) => assert_eq!(e1, e2),
180+
(Some(e1), None) => assert!(matches!(e1, libc::EINVAL | libc::E2BIG)),
181+
_ => unreachable!(),
182+
}
183+
}
178184
_ => unreachable!(),
179185
}
180186
}

src/ruleset.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1024,3 +1024,15 @@ fn unsupported_handled_access() {
10241024
)))
10251025
);
10261026
}
1027+
1028+
#[test]
1029+
fn unsupported_handled_access_errno() {
1030+
assert_eq!(
1031+
Errno::from(
1032+
Ruleset::from(ABI::V3)
1033+
.handle_access(AccessNet::from_all(ABI::V3))
1034+
.unwrap_err()
1035+
),
1036+
Errno::new(libc::EINVAL)
1037+
);
1038+
}

0 commit comments

Comments
 (0)