Skip to main content

x86_64/instructions/
smap.rs

1//! This module provides helpers for Supervisor Mode Access Prevention (SMAP).
2//!
3//! SMAP is a security feature that helps prevent accidental accesses to user
4//! memory by a kernel. This feature can be enabled by setting
5//! [`Cr4Flags::SUPERVISOR_MODE_ACCESS_PREVENTION`]. Once enabled, accesses to
6//! user memory by the kernel will generate a page fault (#PF) unless the
7//! [`RFlags::ALIGNMENT_CHECK`] bit is set.
8//!
9//! The `stac` and `clac` instructions can be used to efficiently update the
10//! `ALIGNMENT_CHECK` flag.
11//!
12//! Not all processors support SMAP.
13
14use core::arch::asm;
15
16use bit_field::BitField;
17
18#[cfg(doc)]
19use crate::registers::control::Cr4Flags;
20use crate::registers::rflags::{self, RFlags};
21
22/// A helper type that provides SMAP related methods.
23///
24/// This type can only be instantiated if SMAP is supported by the CPU.
25#[derive(Debug, Clone, Copy)]
26pub struct Smap(());
27
28impl Smap {
29    /// Checks if the CPU supports SMAP and returns a [`Smap`] instance if
30    /// supported or `None` if not.
31    ///
32    /// This function uses CPUID to determine if SMAP is supported by the CPU.
33    ///
34    /// Note that this function does not check whether SMAP has be enabled in
35    /// CR4.
36    pub fn new() -> Option<Self> {
37        // Check if the CPU supports `stac` and `clac`.
38        #[allow(unused_unsafe)]
39        let cpuid = unsafe { core::arch::x86_64::__cpuid(7) };
40        if cpuid.ebx.get_bit(20) {
41            Some(Self(()))
42        } else {
43            None
44        }
45    }
46
47    /// Returns a [`Smap`] instance.
48    ///
49    /// # Safety
50    ///
51    /// The caller must ensure that the CPU supports SMAP.
52    #[inline]
53    pub const unsafe fn new_unchecked() -> Self {
54        Self(())
55    }
56
57    /// Returns whether the [`RFlags::ALIGNMENT_CHECK`] flag is unset.
58    ///
59    /// Note that SMAP also requires
60    /// [`Cr4Flags::SUPERVISOR_MODE_ACCESS_PREVENTION`] to be set. This
61    /// function does not check CR4 because doing so is much slower than just
62    /// checking the AC flag.
63    #[inline]
64    pub fn is_enabled(self) -> bool {
65        !rflags::read().contains(RFlags::ALIGNMENT_CHECK)
66    }
67
68    /// Disable SMAP access checks by setting [`RFlags::ALIGNMENT_CHECK`] using
69    /// the `stac` instruction.
70    ///
71    /// This will do nothing if `SMAP` access checks are already disabled.
72    #[doc(alias = "stac")]
73    #[inline]
74    pub fn disable(self) {
75        // Technically this modifies the AC flag, but the Rust compiler doesn't
76        // care about that, so it's fine to use preserves_flags.
77        unsafe {
78            asm!("stac", options(nomem, nostack, preserves_flags));
79        }
80    }
81
82    /// Enable SMAP access checks by clearing [`RFlags::ALIGNMENT_CHECK`] using
83    /// the `clac` instruction.
84    ///
85    /// This will do nothing if `SMAP` access checks are already enabled.
86    #[doc(alias = "clac")]
87    #[inline]
88    pub fn enable(self) {
89        // Technically this modifies the AC flag, but the Rust compiler doesn't
90        // care about that, so it's fine to use preserves_flags.
91        unsafe {
92            asm!("clac", options(nomem, nostack, preserves_flags));
93        }
94    }
95
96    /// Call a closure with SMAP disabled.
97    ///
98    /// This function disables SMAP before calling the closure and restores the
99    /// SMAP state afterwards.
100    pub fn without_smap<F, R>(self, f: F) -> R
101    where
102        F: FnOnce() -> R,
103    {
104        let was_enabled = self.is_enabled();
105
106        self.disable();
107
108        let result = f();
109
110        if was_enabled {
111            self.enable();
112        }
113
114        result
115    }
116}