Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/instructions/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ pub mod interrupts;
pub mod port;
pub mod random;
pub mod segmentation;
pub mod smap;
pub mod tables;
pub mod tlb;

Expand Down
115 changes: 115 additions & 0 deletions src/instructions/smap.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
//! This module provides helpers for Supervisor Mode Access Prevention (SMAP).
//!
//! SMAP is a security feature that helps prevent accidental accesses to user
//! memory by a kernel. This feature can be enabled by setting
//! [`Cr4Flags::SUPERVISOR_MODE_ACCESS_PREVENTION`]. Once enabled, accesses to
//! user memory by the kernel will generate a page fault (#PF) unless the
//! [`RFlags::ALIGNMENT_CHECK`] bit is set.
//!
//! The `stac` and `clac` instructions can be used to efficiently update the
//! `ALIGNMENT_CHECK` flag.
//!
//! Not all processors support SMAP.

use core::arch::asm;

use bit_field::BitField;

#[cfg(doc)]
use crate::registers::control::Cr4Flags;
use crate::registers::rflags::{self, RFlags};

/// A helper type that provides SMAP related methods.
///
/// This type can only be instatiated if SMAP is supported by the CPU.
#[derive(Debug, Clone, Copy)]
pub struct Smap(());

impl Smap {
/// Checks if the CPU supports SMAP and returns a [`Smap`] instance if
/// supported or `None` if not.
///
/// This function uses CPUID to determine if SMAP is supported by the CPU.
///
/// Note that this function does not check whether SMAP has be enabled in
/// CR4.
pub fn new() -> Option<Self> {
// Check if the CPU supports `stac` and `clac`.
let cpuid = unsafe { core::arch::x86_64::__cpuid(7) };
if cpuid.ebx.get_bit(20) {
Some(Self(()))
} else {
None
}
}

/// Returns a [`Smap`] instance.
///
/// # Safety
///
/// The caller must ensure that the CPU supports SMAP.
#[inline]
pub const unsafe fn new_unchecked() -> Self {
Self(())
}

/// Returns whether the [`RFlags::ALIGNMENT_CHECK`] flag is unset.
///
/// Note that SMAP also requires
/// [`Cr4Flags::SUPERVISOR_MODE_ACCESS_PREVENTION`] to be set. This
/// function does not check CR4 because doing so is much slower than just
/// checking the AC flag.
#[inline]
pub fn is_enabled(self) -> bool {
!rflags::read().contains(RFlags::ALIGNMENT_CHECK)
}

/// Disable SMAP access checks by setting [`RFlags::ALIGNMENT_CHECK`] using
/// the `stac` instruction.
Comment on lines +67 to +68
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here (and in disable), we should explain what happens if SMAP is already disabled (or enabled respectively) when this function is called.

I think it's a no-op, but we should say so.

///
/// This will do nothing if `SMAP` access checks are already disabled.
#[doc(alias = "stac")]
#[inline]
pub fn disable(self) {
// Technically this modifies the AC flag, but the Rust compiler doesn't
// care about that, so it's fine to use preserves_flags.
unsafe {
asm!("stac", options(nomem, nostack, preserves_flags));
}
}

/// Enable SMAP access checks by clearing [`RFlags::ALIGNMENT_CHECK`] using
/// the `clac` instruction.
///
/// This will do nothing if `SMAP` access checks are already enabled.
#[doc(alias = "clac")]
#[inline]
pub fn enable(self) {
// Technically this modifies the AC flag, but the Rust compiler doesn't
// care about that, so it's fine to use preserves_flags.
unsafe {
asm!("clac", options(nomem, nostack, preserves_flags));
}
}

/// Call a closure with SMAP disabled.
///
/// This function disables SMAP before calling the closure and restores the
/// SMAP state afterwards.
pub fn without_smap<F, R>(self, f: F) -> R
where
F: FnOnce() -> R,
{
let was_enabled = self.is_enabled();

self.disable();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we want to disable SMAP again if it was already disabled?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is intentional. I assume that running stac unnecessarily will be faster than branching, but I did not actually test this.


let result = f();

if was_enabled {
self.enable();
}

result
}
}