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}