Coverage Report

Created: 2026-06-07 06:19

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/landlock-0.4.5/src/flags.rs
Line
Count
Source
1
// SPDX-License-Identifier: Apache-2.0 OR MIT
2
3
//! Syscall flag types for `landlock_restrict_self()` and future syscall flags.
4
//!
5
//! This module provides a compatibility mechanism for individual syscall flags,
6
//! parallel to but simpler than the [`Access`](crate::Access) /
7
//! [`TryCompat`](crate::TryCompat) machinery.
8
//!
9
//! Key differences from the [`Access`](crate::Access) pattern:
10
//!
11
//! - Access types ([`AccessFs`](crate::AccessFs), [`AccessNet`](crate::AccessNet),
12
//!   [`Scope`](crate::Scope)) operate on **sets** of rights passed as
13
//!   [`BitFlags<T>`](enumflags2::BitFlags), where the compat result can be Full,
14
//!   Partial, or No depending on which bits are supported.  They use the
15
//!   [`TryCompat`](crate::TryCompat) trait and the full
16
//!   [`AccessError`](crate::AccessError) hierarchy.
17
//!
18
//! - Syscall flags are set **individually** through boolean setter methods
19
//!   (e.g., [`log_same_exec()`](crate::RulesetCreatedAttr::log_same_exec)), so
20
//!   the compat result is binary: supported or not.  There is no Partial case,
21
//!   no set-level validation, and no need for an
22
//!   [`AccessError`](crate::AccessError)-like hierarchy.  The
23
//!   [`try_compat()`](SyscallFlagExt::try_compat) default method on
24
//!   [`SyscallFlagExt`] handles the compat dispatch directly, using
25
//!   [`Compatibility::try_compat_binary()`](crate::compat::Compatibility::try_compat_binary)
26
//!   factored out from the No branch of
27
//!   [`TryCompat::try_compat()`](crate::TryCompat::try_compat).
28
//!
29
//! - Access types implement the [`Access`](crate::Access) trait (which requires
30
//!   [`BitFlag`](enumflags2::BitFlag)) and are used with
31
//!   [`BitFlags<T>`](enumflags2::BitFlags) throughout the builder.  Syscall
32
//!   flags are plain enums (no `#[bitflags]`); the raw UAPI bitmask is built
33
//!   internally by OR-ing the constants returned by
34
//!   [`raw_bit()`](SyscallFlagExt::raw_bit) into a `u32`.
35
36
use crate::compat::{Compatibility, ABI};
37
use crate::errors::SyscallFlagError;
38
use crate::private;
39
use crate::uapi;
40
41
/// Marker trait for syscall flag types used in compatibility checks.
42
///
43
/// This is the syscall-flag equivalent of the [`Access`](crate::Access) trait,
44
/// but without the [`BitFlag`](enumflags2::BitFlag) requirement since syscall
45
/// flags are set individually rather than as sets.
46
///
47
/// This trait is sealed and cannot be implemented outside of this crate.
48
pub trait SyscallFlag: Copy + core::fmt::Debug + private::Sealed {}
49
50
/// Internal extension providing compatibility logic for syscall flags.
51
///
52
/// This is the syscall-flag equivalent of [`TryCompat`](crate::TryCompat),
53
/// simplified for single-flag boolean setters where the compat result is
54
/// binary (supported or not) rather than Full/Partial/No.
55
///
56
/// Implementors provide [`default_value()`](Self::default_value),
57
/// [`raw_bit()`](Self::raw_bit), and [`since()`](Self::since); the
58
/// [`try_compat()`](Self::try_compat) default method handles the compat
59
/// state update and level dispatch.
60
pub(crate) trait SyscallFlagExt: SyscallFlag {
61
    /// Returns the kernel's default state for this flag.
62
    fn default_value(self) -> bool;
63
64
    /// Returns the raw UAPI constant for this flag.
65
    fn raw_bit(self) -> u32;
66
67
    /// Returns the minimum ABI version that supports this flag.
68
    fn since(self) -> ABI;
69
70
    /// Checks compatibility and returns whether the non-default bit should
71
    /// be applied.
72
    ///
73
    /// Returns `Ok(true)` if [`raw_bit()`](Self::raw_bit) should be set in
74
    /// the actual flags.  Returns `Ok(false)` if the flag should be cleared
75
    /// to its default state, either because `set == default_value()` or
76
    /// because the kernel does not support the flag and the compat level
77
    /// permits dropping it.  Returns `Err` if the flag is unsupported with
78
    /// [`CompatLevel::HardRequirement`](crate::CompatLevel::HardRequirement).
79
    ///
80
    /// Setting to the default value never requires a compat check.
81
    ///
82
    /// This mirrors the compat dispatch in
83
    /// [`TryCompat::try_compat()`](crate::TryCompat::try_compat) but without
84
    /// the Partial case (a single flag is either fully supported or not).
85
0
    fn try_compat(
86
0
        self,
87
0
        set: bool,
88
0
        compat: &mut Compatibility,
89
0
    ) -> Result<bool, SyscallFlagError<Self>> {
90
0
        if set == self.default_value() {
91
            // Setting to the default value is always safe; no compat check
92
            // needed and the caller should clear the bit.
93
0
            return Ok(false);
94
0
        }
95
0
        compat.try_compat_binary(compat.abi() >= self.since(), || {
96
0
            SyscallFlagError::NotSupported { flag: self, set }
97
0
        })
98
0
    }
99
}
100
101
/// Identifies a configuration flag for the `landlock_restrict_self()` syscall.
102
///
103
/// Unlike access rights ([`AccessFs`](crate::AccessFs), [`AccessNet`](crate::AccessNet))
104
/// and scopes ([`Scope`](crate::Scope)), these flags are not passed to
105
/// `landlock_create_ruleset()` but to `landlock_restrict_self()`.  They control
106
/// audit logging behavior rather than access restrictions.
107
///
108
/// Each flag is set through a dedicated boolean method on
109
/// [`RulesetCreatedAttr`](crate::RulesetCreatedAttr) or
110
/// [`RestrictSelfAttr`](crate::RestrictSelfAttr).  The polarity mapping
111
/// (e.g., `log_same_exec(false)` maps to `LANDLOCK_RESTRICT_SELF_LOG_SAME_EXEC_OFF`)
112
/// is handled internally.
113
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
114
#[non_exhaustive]
115
// All variants intentionally share the "Log" prefix to match the kernel's
116
// LANDLOCK_RESTRICT_SELF_LOG_* naming convention.  Future non-logging flags
117
// (e.g., TSYNC) will break the shared prefix, removing the need for this allow.
118
#[allow(clippy::enum_variant_names)]
119
pub enum RestrictSelfFlag {
120
    /// Same-exec logging (see [`RulesetCreatedAttr::log_same_exec()`](crate::RulesetCreatedAttr::log_same_exec)).
121
    LogSameExec,
122
    /// New-exec logging (see [`RulesetCreatedAttr::log_new_exec()`](crate::RulesetCreatedAttr::log_new_exec)).
123
    LogNewExec,
124
    /// Subdomain logging (see [`RestrictSelfAttr::log_subdomains()`](crate::RestrictSelfAttr::log_subdomains)).
125
    LogSubdomains,
126
}
127
128
impl SyscallFlag for RestrictSelfFlag {}
129
130
impl RestrictSelfFlag {
131
    /// Returns the effective state of this flag given a raw bitmask of
132
    /// applied flags.
133
0
    pub(crate) fn is_set(self, raw_flags: u32) -> bool {
134
        // Each flag's raw_bit() returns either an OFF or ON UAPI constant
135
        // depending on the kernel's default for that flag.  When the bit
136
        // is set in raw_flags, the kernel applies the opposite of the
137
        // default; when unset, the default applies.
138
0
        if raw_flags & self.raw_bit() != 0 {
139
0
            !self.default_value()
140
        } else {
141
0
            self.default_value()
142
        }
143
0
    }
144
}
145
146
impl SyscallFlagExt for RestrictSelfFlag {
147
0
    fn default_value(self) -> bool {
148
0
        match self {
149
            // Same-exec logging is enabled by default.
150
0
            Self::LogSameExec => true,
151
            // New-exec logging is disabled by default.
152
0
            Self::LogNewExec => false,
153
            // Subdomain logging is enabled by default.
154
0
            Self::LogSubdomains => true,
155
        }
156
0
    }
157
158
0
    fn raw_bit(self) -> u32 {
159
0
        match self {
160
0
            Self::LogSameExec => uapi::LANDLOCK_RESTRICT_SELF_LOG_SAME_EXEC_OFF,
161
0
            Self::LogNewExec => uapi::LANDLOCK_RESTRICT_SELF_LOG_NEW_EXEC_ON,
162
0
            Self::LogSubdomains => uapi::LANDLOCK_RESTRICT_SELF_LOG_SUBDOMAINS_OFF,
163
        }
164
0
    }
165
166
0
    fn since(self) -> ABI {
167
0
        match self {
168
0
            Self::LogSameExec => ABI::V7,
169
0
            Self::LogNewExec => ABI::V7,
170
0
            Self::LogSubdomains => ABI::V7,
171
        }
172
0
    }
173
}
174
175
#[cfg(test)]
176
mod tests {
177
    use super::*;
178
    use crate::uapi;
179
180
    #[test]
181
    fn restrict_self_flag_raw_bit() {
182
        assert_eq!(
183
            RestrictSelfFlag::LogSameExec.raw_bit(),
184
            uapi::LANDLOCK_RESTRICT_SELF_LOG_SAME_EXEC_OFF,
185
        );
186
        assert_eq!(
187
            RestrictSelfFlag::LogNewExec.raw_bit(),
188
            uapi::LANDLOCK_RESTRICT_SELF_LOG_NEW_EXEC_ON,
189
        );
190
        assert_eq!(
191
            RestrictSelfFlag::LogSubdomains.raw_bit(),
192
            uapi::LANDLOCK_RESTRICT_SELF_LOG_SUBDOMAINS_OFF,
193
        );
194
    }
195
196
    #[test]
197
    fn restrict_self_flag_default_value() {
198
        assert!(RestrictSelfFlag::LogSameExec.default_value());
199
        assert!(!RestrictSelfFlag::LogNewExec.default_value());
200
        assert!(RestrictSelfFlag::LogSubdomains.default_value());
201
    }
202
203
    #[test]
204
    fn restrict_self_flag_since() {
205
        assert_eq!(RestrictSelfFlag::LogSameExec.since(), ABI::V7);
206
        assert_eq!(RestrictSelfFlag::LogNewExec.since(), ABI::V7);
207
        assert_eq!(RestrictSelfFlag::LogSubdomains.since(), ABI::V7);
208
    }
209
}