Skip to content

Commit

Permalink
shadow_stack: determine support at runtime
Browse files Browse the repository at this point in the history
Trusted CPUID values are hard to come by, so let's just try to enable
CET in CR4 and handle failure gracefully.

Signed-off-by: Tom Dohrmann <[email protected]>
  • Loading branch information
Freax13 committed Oct 1, 2024
1 parent 62cd749 commit 933f877
Show file tree
Hide file tree
Showing 9 changed files with 76 additions and 23 deletions.
3 changes: 2 additions & 1 deletion kernel/src/cpu/idt/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ use crate::cpu::control_regs::{read_cr0, read_cr4};
use crate::cpu::efer::read_efer;
use crate::cpu::gdt::gdt;
use crate::cpu::registers::{X86GeneralRegs, X86InterruptFrame};
use crate::cpu::shadow_stack::is_cet_ss_supported;
use crate::insn_decode::{InsnError, InsnMachineCtx, InsnMachineMem, Register, SegRegister};
use crate::locking::{RWLock, ReadLockGuard, WriteLockGuard};
use crate::mm::GuestPtr;
Expand Down Expand Up @@ -75,7 +76,7 @@ impl X86ExceptionContext {
pub fn set_rip(&mut self, new_rip: usize) {
self.frame.rip = new_rip;

if cfg!(feature = "shadow-stacks") {
if is_cet_ss_supported() {
// Update the instruction pointer on the shadow stack.
let return_on_stack = (self.ssp + 8) as *const usize;
let return_on_stack_val = new_rip;
Expand Down
8 changes: 7 additions & 1 deletion kernel/src/cpu/idt/entry.S
Original file line number Diff line number Diff line change
Expand Up @@ -108,8 +108,11 @@ asm_entry_hv:
movq %rbx, 0x20(%rsp)
.if CFG_SHADOW_STACKS
// Update RIP on the shadow stack to the cancel point.
cmpb $0, {IS_CET_SUPPORTED}(%rip)
je 2f
rdsspq %rax
wrssq %rbx, 8(%rax)
2:
.endif
// Defer any further processing until interrupts can be processed.
jmp postpone_hv
Expand Down Expand Up @@ -205,8 +208,11 @@ restart_hv:
.if CFG_SHADOW_STACKS
// Pop the current stack frame, so that the previous stack frame sits
// on top of the shadow stack.
cmpb $0, {IS_CET_SUPPORTED}(%rip)
je 2f
movl $3, %eax
incsspq %rax
incsspq %rax
2:
.endif

continue_hv:
Expand Down
2 changes: 2 additions & 0 deletions kernel/src/cpu/idt/svsm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ use super::common::{
VC_VECTOR, XF_VECTOR,
};
use crate::address::VirtAddr;
use crate::cpu::shadow_stack::IS_CET_SUPPORTED;
use crate::cpu::X86ExceptionContext;
use crate::debug::gdbstub::svsm_gdbstub::handle_debug_exception;
use crate::mm::GuestPtr;
Expand Down Expand Up @@ -318,5 +319,6 @@ global_asm!(
),
// Include the assembly.
include_str!("entry.S"),
IS_CET_SUPPORTED = sym IS_CET_SUPPORTED,
options(att_syntax)
);
12 changes: 6 additions & 6 deletions kernel/src/cpu/percpu.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ extern crate alloc;
use super::gdt_mut;
use super::isst::Isst;
use super::msr::write_msr;
use super::shadow_stack::ISST_ADDR;
use super::shadow_stack::{is_cet_ss_supported, ISST_ADDR};
use super::tss::{X86Tss, IST_DF};
use crate::address::{Address, PhysAddr, VirtAddr};
use crate::cpu::idt::common::INT_INJ_VECTOR;
Expand Down Expand Up @@ -642,14 +642,14 @@ impl PerCpu {
// Allocate per-cpu init stack
self.allocate_init_stack()?;

if cfg!(feature = "shadow-stacks") {
if is_cet_ss_supported() {
self.allocate_init_shadow_stack()?;
}

// Allocate per-cpu context switch stack
self.allocate_context_switch_stack()?;

if cfg!(feature = "shadow-stacks") {
if is_cet_ss_supported() {
self.allocate_context_switch_shadow_stack()?;
}

Expand All @@ -659,7 +659,7 @@ impl PerCpu {
// Setup TSS
self.setup_tss();

if cfg!(feature = "shadow-stacks") {
if is_cet_ss_supported() {
// Allocate ISST shadow stacks
self.allocate_isst_shadow_stacks()?;

Expand Down Expand Up @@ -712,7 +712,7 @@ impl PerCpu {
pub fn load(&self) {
self.load_pgtable();
self.load_tss();
if cfg!(feature = "shadow-stacks") {
if is_cet_ss_supported() {
self.load_isst();
}
}
Expand Down Expand Up @@ -745,7 +745,7 @@ impl PerCpu {
vmsa.tr = self.vmsa_tr_segment();
vmsa.rip = start_rip;
vmsa.rsp = self.get_top_of_stack().into();
if cfg!(feature = "shadow-stacks") {
if is_cet_ss_supported() {
vmsa.ssp = self.get_top_of_shadow_stack().into();
}
vmsa.cr3 = self.get_pgtable().cr3_value().into();
Expand Down
51 changes: 43 additions & 8 deletions kernel/src/cpu/shadow_stack.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
// SPDX-License-Identifier: MIT OR Apache-2.0

use core::{
arch::asm,
sync::atomic::{AtomicBool, Ordering},
};

use bitflags::bitflags;

use super::msr::read_msr;
Expand All @@ -10,6 +15,44 @@ pub const ISST_ADDR: u32 = 0x6a8;

pub const MODE_64BIT: usize = 1;

pub static IS_CET_SUPPORTED: AtomicBool = AtomicBool::new(false);

// Try to enable the CET feature in CR4 and set `IS_CET_SUPPORTED` if successful.
pub fn determine_cet_support() {
// Don't try to determine support if the shadow-stacks feature is not enabled.
if !cfg!(feature = "shadow-stacks") {
return;
}

let rcx: u64;
unsafe {
asm!(// Try to enable CET in CR4.
" mov %cr4, %rax",
" or $1<<23, %rax",
"1: mov %rax, %cr4",
" xorq %rcx, %rcx",
"2:",
".pushsection \"__exception_table\",\"a\"",
".balign 16",
".quad (1b)",
".quad (2b)",
".popsection",
out("rax") _,
out("rcx") rcx,
options(att_syntax, nostack, nomem, pure, preserves_flags));
}

IS_CET_SUPPORTED.store(rcx == 0, Ordering::Relaxed);
}

/// Returns whether shadow stacks are supported by the CPU and the kernel.
#[inline(always)]
pub fn is_cet_ss_supported() -> bool {
// In theory CPUs can have support for CET, but not CET_SS, but in practice
// no such CPUs exist. Treat CET being supported as CET_SS being supported.
cfg!(feature = "shadow-stacks") && IS_CET_SUPPORTED.load(Ordering::Relaxed)
}

/// Enable shadow stacks.
///
/// This code is placed in a macro instead of a function so that we don't have
Expand All @@ -18,19 +61,11 @@ pub const MODE_64BIT: usize = 1;
macro_rules! enable_shadow_stacks {
($bsp_percpu:ident) => {{
use core::arch::asm;
use core::assert;
use svsm::address::Address;
use svsm::cpu::control_regs::{read_cr4, write_cr4, CR4Flags};
use svsm::cpu::shadow_stack::{SCetFlags, MODE_64BIT, S_CET};

let token_addr = $bsp_percpu.get_top_of_shadow_stack();

// Enable CET in CR4.
let mut cr4 = read_cr4();
assert!(!cr4.contains(CR4Flags::CET), "CET is already enabled");
cr4 |= CR4Flags::CET;
write_cr4(cr4);

unsafe {
asm!(
// Enable shadow stacks.
Expand Down
4 changes: 2 additions & 2 deletions kernel/src/cpu/vmsa.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ use super::control_regs::{read_cr0, read_cr3, read_cr4};
use super::efer::read_efer;
use super::gdt;
use super::idt::common::idt;
use super::shadow_stack::read_s_cet;
use super::shadow_stack::{is_cet_ss_supported, read_s_cet};

fn svsm_code_segment() -> VMSASegment {
VMSASegment {
Expand Down Expand Up @@ -67,7 +67,7 @@ pub fn init_svsm_vmsa(vmsa: &mut VMSA, vtom: u64) {
vmsa.cr3 = read_cr3().bits() as u64;
vmsa.cr4 = read_cr4().bits();
vmsa.efer = read_efer().bits();
if cfg!(feature = "shadow-stacks") {
if is_cet_ss_supported() {
vmsa.s_cet = read_s_cet().bits();
}

Expand Down
4 changes: 3 additions & 1 deletion kernel/src/svsm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#![cfg_attr(not(test), no_std)]
#![cfg_attr(not(test), no_main)]

use svsm::cpu::shadow_stack::{determine_cet_support, is_cet_ss_supported};
use svsm::enable_shadow_stacks;
use svsm::fw_meta::{print_fw_meta, validate_fw_memory, SevFWMetaData};

Expand Down Expand Up @@ -312,6 +313,7 @@ pub extern "C" fn svsm_start(li: &KernelLaunchInfo, vb_addr: usize) {

cr0_init();
cr4_init(platform);
determine_cet_support();
efer_init(platform);
install_console_logger("SVSM").expect("Console logger already initialized");
platform
Expand Down Expand Up @@ -349,7 +351,7 @@ pub extern "C" fn svsm_start(li: &KernelLaunchInfo, vb_addr: usize) {
.expect("Failed to run percpu.setup_on_cpu()");
bsp_percpu.load();

if cfg!(feature = "shadow-stacks") {
if is_cet_ss_supported() {
enable_shadow_stacks!(bsp_percpu);
}

Expand Down
10 changes: 8 additions & 2 deletions kernel/src/task/schedule.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ use super::{Task, TaskListAdapter, TaskPointer, TaskRunListAdapter};
use crate::address::{Address, VirtAddr};
use crate::cpu::msr::write_msr;
use crate::cpu::percpu::{irq_nesting_count, this_cpu};
use crate::cpu::shadow_stack::PL0_SSP;
use crate::cpu::shadow_stack::{is_cet_ss_supported, IS_CET_SUPPORTED, PL0_SSP};
use crate::cpu::IrqGuard;
use crate::error::SvsmError;
use crate::locking::SpinLock;
Expand Down Expand Up @@ -351,7 +351,7 @@ pub fn schedule() {
}

this_cpu().set_tss_rsp0(next.stack_bounds.end());
if cfg!(feature = "shadow-stacks") {
if is_cet_ss_supported() {
write_msr(PL0_SSP, next.exception_shadow_stack.bits() as u64);
}

Expand Down Expand Up @@ -420,6 +420,8 @@ global_asm!(
mov {CONTEXT_SWITCH_STACK}(%rip), %rsp
.if CFG_SHADOW_STACKS
cmpb $0, {IS_CET_SUPPORTED}(%rip)
je 1f
// Save the current shadow stack pointer
rdssp %rax
sub $8, %rax
Expand All @@ -438,11 +440,14 @@ global_asm!(
mov %rdx, %cr3
.if CFG_SHADOW_STACKS
cmpb $0, {IS_CET_SUPPORTED}(%rip)
je 2f
// Switch to the new task shadow stack and move the "shadow stack
// restore token" back.
mov 8(%rdi), %rdx
rstorssp (%rdx)
saveprevssp
2:
.endif
// Switch to the new task stack
Expand Down Expand Up @@ -471,6 +476,7 @@ global_asm!(
ret
"#,
IS_CET_SUPPORTED = sym IS_CET_SUPPORTED,
// TODO: Replace these with `const` operands once we upgrade the MSRV to 1.82.
CONTEXT_SWITCH_STACK = sym CONTEXT_SWITCH_STACK,
CONTEXT_SWITCH_RESTORE_TOKEN = sym CONTEXT_SWITCH_RESTORE_TOKEN,
Expand Down
5 changes: 3 additions & 2 deletions kernel/src/task/tasks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ use crate::address::{Address, VirtAddr};
use crate::cpu::idt::svsm::return_new_task;
use crate::cpu::msr::read_flags;
use crate::cpu::percpu::PerCpu;
use crate::cpu::shadow_stack::is_cet_ss_supported;
use crate::cpu::X86ExceptionContext;
use crate::cpu::{irqs_enable, X86GeneralRegs};
use crate::error::SvsmError;
Expand Down Expand Up @@ -190,7 +191,7 @@ impl Task {

let mut shadow_stack_offset = VirtAddr::null();
let mut exception_shadow_stack = VirtAddr::null();
if cfg!(feature = "shadow-stacks") {
if is_cet_ss_supported() {
let shadow_stack;
(shadow_stack, shadow_stack_offset) = VMKernelShadowStack::new(
SVSM_PERTASK_SHADOW_STACK_BASE,
Expand Down Expand Up @@ -259,7 +260,7 @@ impl Task {

let mut shadow_stack_offset = VirtAddr::null();
let mut exception_shadow_stack = VirtAddr::null();
if cfg!(feature = "shadow-stacks") {
if is_cet_ss_supported() {
let shadow_stack;
(shadow_stack, shadow_stack_offset) = VMKernelShadowStack::new(
SVSM_PERTASK_SHADOW_STACK_BASE,
Expand Down

0 comments on commit 933f877

Please sign in to comment.