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 instatiated 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        let cpuid = unsafe { core::arch::x86_64::__cpuid(7) };
39        if cpuid.ebx.get_bit(20) {
40            Some(Self(()))
41        } else {
42            None
43        }
44    }
45
46    /// Returns a [`Smap`] instance.
47    ///
48    /// # Safety
49    ///
50    /// The caller must ensure that the CPU supports SMAP.
51    #[inline]
52    pub const unsafe fn new_unchecked() -> Self {
53        Self(())
54    }
55
56    /// Returns whether the [`RFlags::ALIGNMENT_CHECK`] flag is unset.
57    ///
58    /// Note that SMAP also requires
59    /// [`Cr4Flags::SUPERVISOR_MODE_ACCESS_PREVENTION`] to be set. This
60    /// function does not check CR4 because doing so is much slower than just
61    /// checking the AC flag.
62    #[inline]
63    pub fn is_enabled(self) -> bool {
64        !rflags::read().contains(RFlags::ALIGNMENT_CHECK)
65    }
66
67    /// Disable SMAP access checks by setting [`RFlags::ALIGNMENT_CHECK`] using
68    /// the `stac` instruction.
69    ///
70    /// This will do nothing if `SMAP` access checks are already disabled.
71    #[doc(alias = "stac")]
72    #[inline]
73    pub fn disable(self) {
74        // Technically this modifies the AC flag, but the Rust compiler doesn't
75        // care about that, so it's fine to use preserves_flags.
76        unsafe {
77            asm!("stac", options(nomem, nostack, preserves_flags));
78        }
79    }
80
81    /// Enable SMAP access checks by clearing [`RFlags::ALIGNMENT_CHECK`] using
82    /// the `clac` instruction.
83    ///
84    /// This will do nothing if `SMAP` access checks are already enabled.
85    #[doc(alias = "clac")]
86    #[inline]
87    pub fn enable(self) {
88        // Technically this modifies the AC flag, but the Rust compiler doesn't
89        // care about that, so it's fine to use preserves_flags.
90        unsafe {
91            asm!("clac", options(nomem, nostack, preserves_flags));
92        }
93    }
94
95    /// Call a closure with SMAP disabled.
96    ///
97    /// This function disables SMAP before calling the closure and restores the
98    /// SMAP state afterwards.
99    pub fn without_smap<F, R>(self, f: F) -> R
100    where
101        F: FnOnce() -> R,
102    {
103        let was_enabled = self.is_enabled();
104
105        self.disable();
106
107        let result = f();
108
109        if was_enabled {
110            self.enable();
111        }
112
113        result
114    }
115}