Skip to content

Commit 899ee62

Browse files
authored
add SMAP helpers (#566)
* add SMAP helpers This PR adds helpers for SMAP. SMAP is a security feature that can be used to detect accidental access to user memory by the kernel.
1 parent 45d0797 commit 899ee62

File tree

2 files changed

+116
-0
lines changed

2 files changed

+116
-0
lines changed

src/instructions/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ pub mod interrupts;
66
pub mod port;
77
pub mod random;
88
pub mod segmentation;
9+
pub mod smap;
910
pub mod tables;
1011
pub mod tlb;
1112

src/instructions/smap.rs

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
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+
14+
use core::arch::asm;
15+
16+
use bit_field::BitField;
17+
18+
#[cfg(doc)]
19+
use crate::registers::control::Cr4Flags;
20+
use 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)]
26+
pub struct Smap(());
27+
28+
impl 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+
}

0 commit comments

Comments
 (0)