#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
#[repr(u64)]
pub enum Mitigation {
#[default]
Defer = 0,
AlwaysOn = 1,
AlwaysOff = 2,
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
#[repr(u64)]
pub enum RelocateImages {
#[default]
Defer = 0,
AlwaysOn = 1,
AlwaysOff = 2,
RequireRelocations = 3,
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
#[repr(u64)]
pub enum DynamicCode {
#[default]
Defer = 0,
Prohibit = 1,
Allow = 2,
ProhibitWithOptOut = 3,
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
#[repr(u64)]
pub enum ControlFlowGuard {
#[default]
Defer = 0,
AlwaysOn = 1,
AlwaysOff = 2,
ExportSuppression = 3,
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
#[repr(u64)]
pub enum SignedBinaries {
#[default]
Defer = 0,
MicrosoftOnly = 1,
AlwaysOff = 2,
MicrosoftAndStore = 3,
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
#[repr(u64)]
pub enum FontDisable {
#[default]
Defer = 0,
Block = 1,
Allow = 2,
Audit = 3,
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
#[repr(u64)]
pub enum LoaderIntegrity {
#[default]
Defer = 0,
AlwaysOn = 1,
AlwaysOff = 2,
Audit = 3,
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
#[repr(u64)]
pub enum ModuleTampering {
#[default]
Defer = 0,
AlwaysOn = 1,
AlwaysOff = 2,
NoInherit = 3,
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
#[repr(u64)]
pub enum CetShadowStacks {
#[default]
Defer = 0,
AlwaysOn = 1,
AlwaysOff = 2,
Strict = 3,
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
#[repr(u64)]
pub enum UserCetContextIpValidation {
#[default]
Defer = 0,
AlwaysOn = 1,
AlwaysOff = 2,
Relaxed = 3,
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
#[repr(u64)]
pub enum BlockNonCetBinaries {
#[default]
Defer = 0,
AlwaysOn = 1,
AlwaysOff = 2,
NonEhContinuation = 3,
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
pub struct MitigationPolicy {
words: [u64; 2],
}
impl MitigationPolicy {
#[must_use]
pub const fn new() -> Self {
Self { words: [0, 0] }
}
#[must_use]
pub const fn words(self) -> [u64; 2] {
self.words
}
#[must_use]
pub const fn dep(mut self, enable: bool) -> Self {
self.words[0] = set_bit(self.words[0], 0, enable);
self
}
#[must_use]
pub const fn dep_atl_thunk(mut self, enable: bool) -> Self {
self.words[0] = set_bit(self.words[0], 1, enable);
self
}
#[must_use]
pub const fn sehop(mut self, enable: bool) -> Self {
self.words[0] = set_bit(self.words[0], 2, enable);
self
}
#[must_use]
pub const fn relocate_images(mut self, value: RelocateImages) -> Self {
self.words[0] = replace(self.words[0], 8, value as u64);
self
}
#[must_use]
pub const fn heap_terminate(mut self, value: Mitigation) -> Self {
self.words[0] = replace(self.words[0], 12, value as u64);
self
}
#[must_use]
pub const fn bottom_up_aslr(mut self, value: Mitigation) -> Self {
self.words[0] = replace(self.words[0], 16, value as u64);
self
}
#[must_use]
pub const fn high_entropy_aslr(mut self, value: Mitigation) -> Self {
self.words[0] = replace(self.words[0], 20, value as u64);
self
}
#[must_use]
pub const fn strict_handle_checks(mut self, value: Mitigation) -> Self {
self.words[0] = replace(self.words[0], 24, value as u64);
self
}
#[must_use]
pub const fn disable_win32k_system_calls(mut self, value: Mitigation) -> Self {
self.words[0] = replace(self.words[0], 28, value as u64);
self
}
#[must_use]
pub const fn disable_extension_points(mut self, value: Mitigation) -> Self {
self.words[0] = replace(self.words[0], 32, value as u64);
self
}
#[must_use]
pub const fn dynamic_code(mut self, value: DynamicCode) -> Self {
self.words[0] = replace(self.words[0], 36, value as u64);
self
}
#[must_use]
pub const fn control_flow_guard(mut self, value: ControlFlowGuard) -> Self {
self.words[0] = replace(self.words[0], 40, value as u64);
self
}
#[must_use]
pub const fn signed_binaries(mut self, value: SignedBinaries) -> Self {
self.words[0] = replace(self.words[0], 44, value as u64);
self
}
#[must_use]
pub const fn font_disable(mut self, value: FontDisable) -> Self {
self.words[0] = replace(self.words[0], 48, value as u64);
self
}
#[must_use]
pub const fn block_remote_images(mut self, value: Mitigation) -> Self {
self.words[0] = replace(self.words[0], 52, value as u64);
self
}
#[must_use]
pub const fn block_low_label_images(mut self, value: Mitigation) -> Self {
self.words[0] = replace(self.words[0], 56, value as u64);
self
}
#[must_use]
pub const fn prefer_system32_images(mut self, value: Mitigation) -> Self {
self.words[0] = replace(self.words[0], 60, value as u64);
self
}
#[must_use]
pub const fn loader_integrity(mut self, value: LoaderIntegrity) -> Self {
self.words[1] = replace(self.words[1], 4, value as u64);
self
}
#[must_use]
pub const fn strict_control_flow_guard(mut self, value: Mitigation) -> Self {
self.words[1] = replace(self.words[1], 8, value as u64);
self
}
#[must_use]
pub const fn module_tampering(mut self, value: ModuleTampering) -> Self {
self.words[1] = replace(self.words[1], 12, value as u64);
self
}
#[must_use]
pub const fn restrict_indirect_branch_prediction(mut self, value: Mitigation) -> Self {
self.words[1] = replace(self.words[1], 16, value as u64);
self
}
#[must_use]
pub const fn allow_downgrade_dynamic_code(mut self, value: Mitigation) -> Self {
self.words[1] = replace(self.words[1], 20, value as u64);
self
}
#[must_use]
pub const fn disable_speculative_store_bypass(mut self, value: Mitigation) -> Self {
self.words[1] = replace(self.words[1], 24, value as u64);
self
}
#[must_use]
pub const fn cet_user_shadow_stacks(mut self, value: CetShadowStacks) -> Self {
self.words[1] = replace(self.words[1], 28, value as u64);
self
}
#[must_use]
pub const fn user_cet_context_ip_validation(
mut self,
value: UserCetContextIpValidation,
) -> Self {
self.words[1] = replace(self.words[1], 32, value as u64);
self
}
#[must_use]
pub const fn block_non_cet_binaries(mut self, value: BlockNonCetBinaries) -> Self {
self.words[1] = replace(self.words[1], 36, value as u64);
self
}
#[must_use]
pub const fn extended_control_flow_guard(mut self, value: Mitigation) -> Self {
self.words[1] = replace(self.words[1], 40, value as u64);
self
}
#[must_use]
pub const fn pointer_authentication(mut self, value: Mitigation) -> Self {
self.words[1] = replace(self.words[1], 44, value as u64);
self
}
#[must_use]
pub const fn cet_dynamic_apis_out_of_process(mut self, value: Mitigation) -> Self {
self.words[1] = replace(self.words[1], 48, value as u64);
self
}
#[must_use]
pub const fn restrict_core_sharing(mut self, value: Mitigation) -> Self {
self.words[1] = replace(self.words[1], 52, value as u64);
self
}
#[must_use]
pub const fn disable_fsctl_system_calls(mut self, value: Mitigation) -> Self {
self.words[1] = replace(self.words[1], 56, value as u64);
self
}
}
const fn replace(word: u64, shift: u32, value: u64) -> u64 {
(word & !(3_u64 << shift)) | (value << shift)
}
const fn set_bit(word: u64, shift: u32, value: bool) -> u64 {
if value {
word | (1_u64 << shift)
} else {
word & !(1_u64 << shift)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn setters_replace_only_their_field() {
let first = MitigationPolicy::new()
.dynamic_code(DynamicCode::ProhibitWithOptOut)
.font_disable(FontDisable::Audit)
.dynamic_code(DynamicCode::Allow);
assert_eq!(first.words(), [(2_u64 << 36) | (3_u64 << 48), 0]);
let second = MitigationPolicy::new()
.cet_user_shadow_stacks(CetShadowStacks::Strict)
.block_non_cet_binaries(BlockNonCetBinaries::NonEhContinuation);
assert_eq!(second.words(), [0, (3_u64 << 28) | (3_u64 << 36)]);
}
#[test]
fn legacy_bits_do_not_touch_two_bit_fields() {
let policy = MitigationPolicy::new()
.relocate_images(RelocateImages::RequireRelocations)
.dep(true)
.dep_atl_thunk(true)
.sehop(true);
assert_eq!(policy.words()[0], 7 | (3_u64 << 8));
let cleared = policy.dep(false).dep_atl_thunk(false).sehop(false);
assert_eq!(cleared.words()[0], 3_u64 << 8);
assert_eq!(MitigationPolicy::new().dep(true).dep(true).words()[0], 1);
}
#[test]
#[allow(clippy::too_many_lines)]
fn every_sdk_22621_field_has_the_expected_encoding() {
macro_rules! field {
($policy:expr, $word:expr, $shift:expr, $value:expr) => {{
let mut expected = [0_u64; 2];
expected[$word] = ($value as u64) << $shift;
assert_eq!($policy.words(), expected);
}};
}
field!(MitigationPolicy::new().dep(true), 0, 0, 1);
field!(MitigationPolicy::new().dep_atl_thunk(true), 0, 1, 1);
field!(MitigationPolicy::new().sehop(true), 0, 2, 1);
field!(
MitigationPolicy::new().relocate_images(RelocateImages::RequireRelocations),
0,
8,
3
);
field!(
MitigationPolicy::new().heap_terminate(Mitigation::AlwaysOn),
0,
12,
1
);
field!(
MitigationPolicy::new().bottom_up_aslr(Mitigation::AlwaysOn),
0,
16,
1
);
field!(
MitigationPolicy::new().high_entropy_aslr(Mitigation::AlwaysOn),
0,
20,
1
);
field!(
MitigationPolicy::new().strict_handle_checks(Mitigation::AlwaysOn),
0,
24,
1
);
field!(
MitigationPolicy::new().disable_win32k_system_calls(Mitigation::AlwaysOn),
0,
28,
1
);
field!(
MitigationPolicy::new().disable_extension_points(Mitigation::AlwaysOn),
0,
32,
1
);
field!(
MitigationPolicy::new().dynamic_code(DynamicCode::ProhibitWithOptOut),
0,
36,
3
);
field!(
MitigationPolicy::new().control_flow_guard(ControlFlowGuard::ExportSuppression),
0,
40,
3
);
field!(
MitigationPolicy::new().signed_binaries(SignedBinaries::MicrosoftAndStore),
0,
44,
3
);
field!(
MitigationPolicy::new().font_disable(FontDisable::Audit),
0,
48,
3
);
field!(
MitigationPolicy::new().block_remote_images(Mitigation::AlwaysOn),
0,
52,
1
);
field!(
MitigationPolicy::new().block_low_label_images(Mitigation::AlwaysOn),
0,
56,
1
);
field!(
MitigationPolicy::new().prefer_system32_images(Mitigation::AlwaysOn),
0,
60,
1
);
field!(
MitigationPolicy::new().loader_integrity(LoaderIntegrity::Audit),
1,
4,
3
);
field!(
MitigationPolicy::new().strict_control_flow_guard(Mitigation::AlwaysOn),
1,
8,
1
);
field!(
MitigationPolicy::new().module_tampering(ModuleTampering::NoInherit),
1,
12,
3
);
field!(
MitigationPolicy::new().restrict_indirect_branch_prediction(Mitigation::AlwaysOn),
1,
16,
1
);
field!(
MitigationPolicy::new().allow_downgrade_dynamic_code(Mitigation::AlwaysOn),
1,
20,
1
);
field!(
MitigationPolicy::new().disable_speculative_store_bypass(Mitigation::AlwaysOn),
1,
24,
1
);
field!(
MitigationPolicy::new().cet_user_shadow_stacks(CetShadowStacks::Strict),
1,
28,
3
);
field!(
MitigationPolicy::new()
.user_cet_context_ip_validation(UserCetContextIpValidation::Relaxed),
1,
32,
3
);
field!(
MitigationPolicy::new().block_non_cet_binaries(BlockNonCetBinaries::NonEhContinuation),
1,
36,
3
);
field!(
MitigationPolicy::new().extended_control_flow_guard(Mitigation::AlwaysOn),
1,
40,
1
);
field!(
MitigationPolicy::new().pointer_authentication(Mitigation::AlwaysOn),
1,
44,
1
);
field!(
MitigationPolicy::new().cet_dynamic_apis_out_of_process(Mitigation::AlwaysOn),
1,
48,
1
);
field!(
MitigationPolicy::new().restrict_core_sharing(Mitigation::AlwaysOn),
1,
52,
1
);
field!(
MitigationPolicy::new().disable_fsctl_system_calls(Mitigation::AlwaysOn),
1,
56,
1
);
}
}