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}