1#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
5#[repr(u64)]
6pub enum Mitigation {
7 #[default]
9 Defer = 0,
10 AlwaysOn = 1,
12 AlwaysOff = 2,
14}
15
16#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
18#[repr(u64)]
19pub enum RelocateImages {
20 #[default]
22 Defer = 0,
23 AlwaysOn = 1,
25 AlwaysOff = 2,
27 RequireRelocations = 3,
29}
30
31#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
33#[repr(u64)]
34pub enum DynamicCode {
35 #[default]
37 Defer = 0,
38 Prohibit = 1,
40 Allow = 2,
42 ProhibitWithOptOut = 3,
44}
45
46#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
48#[repr(u64)]
49pub enum ControlFlowGuard {
50 #[default]
52 Defer = 0,
53 AlwaysOn = 1,
55 AlwaysOff = 2,
57 ExportSuppression = 3,
59}
60
61#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
63#[repr(u64)]
64pub enum SignedBinaries {
65 #[default]
67 Defer = 0,
68 MicrosoftOnly = 1,
70 AlwaysOff = 2,
72 MicrosoftAndStore = 3,
74}
75
76#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
78#[repr(u64)]
79pub enum FontDisable {
80 #[default]
82 Defer = 0,
83 Block = 1,
85 Allow = 2,
87 Audit = 3,
89}
90
91#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
93#[repr(u64)]
94pub enum LoaderIntegrity {
95 #[default]
97 Defer = 0,
98 AlwaysOn = 1,
100 AlwaysOff = 2,
102 Audit = 3,
104}
105
106#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
108#[repr(u64)]
109pub enum ModuleTampering {
110 #[default]
112 Defer = 0,
113 AlwaysOn = 1,
115 AlwaysOff = 2,
117 NoInherit = 3,
119}
120
121#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
127#[repr(u64)]
128pub enum CetShadowStacks {
129 #[default]
131 Defer = 0,
132 AlwaysOn = 1,
134 AlwaysOff = 2,
136 Strict = 3,
138}
139
140#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
145#[repr(u64)]
146pub enum UserCetContextIpValidation {
147 #[default]
149 Defer = 0,
150 AlwaysOn = 1,
152 AlwaysOff = 2,
154 Relaxed = 3,
156}
157
158#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
163#[repr(u64)]
164pub enum BlockNonCetBinaries {
165 #[default]
167 Defer = 0,
168 AlwaysOn = 1,
170 AlwaysOff = 2,
172 NonEhContinuation = 3,
174}
175
176#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
189pub struct MitigationPolicy {
190 words: [u64; 2],
191}
192
193impl MitigationPolicy {
194 #[must_use]
196 pub const fn new() -> Self {
197 Self { words: [0, 0] }
198 }
199
200 #[must_use]
202 pub const fn words(self) -> [u64; 2] {
203 self.words
204 }
205
206 #[must_use]
208 pub const fn dep(mut self, enable: bool) -> Self {
209 self.words[0] = set_bit(self.words[0], 0, enable);
210 self
211 }
212
213 #[must_use]
215 pub const fn dep_atl_thunk(mut self, enable: bool) -> Self {
216 self.words[0] = set_bit(self.words[0], 1, enable);
217 self
218 }
219
220 #[must_use]
222 pub const fn sehop(mut self, enable: bool) -> Self {
223 self.words[0] = set_bit(self.words[0], 2, enable);
224 self
225 }
226
227 #[must_use]
229 pub const fn relocate_images(mut self, value: RelocateImages) -> Self {
230 self.words[0] = replace(self.words[0], 8, value as u64);
231 self
232 }
233
234 #[must_use]
236 pub const fn heap_terminate(mut self, value: Mitigation) -> Self {
237 self.words[0] = replace(self.words[0], 12, value as u64);
238 self
239 }
240
241 #[must_use]
243 pub const fn bottom_up_aslr(mut self, value: Mitigation) -> Self {
244 self.words[0] = replace(self.words[0], 16, value as u64);
245 self
246 }
247
248 #[must_use]
250 pub const fn high_entropy_aslr(mut self, value: Mitigation) -> Self {
251 self.words[0] = replace(self.words[0], 20, value as u64);
252 self
253 }
254
255 #[must_use]
257 pub const fn strict_handle_checks(mut self, value: Mitigation) -> Self {
258 self.words[0] = replace(self.words[0], 24, value as u64);
259 self
260 }
261
262 #[must_use]
264 pub const fn disable_win32k_system_calls(mut self, value: Mitigation) -> Self {
265 self.words[0] = replace(self.words[0], 28, value as u64);
266 self
267 }
268
269 #[must_use]
271 pub const fn disable_extension_points(mut self, value: Mitigation) -> Self {
272 self.words[0] = replace(self.words[0], 32, value as u64);
273 self
274 }
275
276 #[must_use]
278 pub const fn dynamic_code(mut self, value: DynamicCode) -> Self {
279 self.words[0] = replace(self.words[0], 36, value as u64);
280 self
281 }
282
283 #[must_use]
285 pub const fn control_flow_guard(mut self, value: ControlFlowGuard) -> Self {
286 self.words[0] = replace(self.words[0], 40, value as u64);
287 self
288 }
289
290 #[must_use]
292 pub const fn signed_binaries(mut self, value: SignedBinaries) -> Self {
293 self.words[0] = replace(self.words[0], 44, value as u64);
294 self
295 }
296
297 #[must_use]
299 pub const fn font_disable(mut self, value: FontDisable) -> Self {
300 self.words[0] = replace(self.words[0], 48, value as u64);
301 self
302 }
303
304 #[must_use]
306 pub const fn block_remote_images(mut self, value: Mitigation) -> Self {
307 self.words[0] = replace(self.words[0], 52, value as u64);
308 self
309 }
310
311 #[must_use]
313 pub const fn block_low_label_images(mut self, value: Mitigation) -> Self {
314 self.words[0] = replace(self.words[0], 56, value as u64);
315 self
316 }
317
318 #[must_use]
320 pub const fn prefer_system32_images(mut self, value: Mitigation) -> Self {
321 self.words[0] = replace(self.words[0], 60, value as u64);
322 self
323 }
324
325 #[must_use]
327 pub const fn loader_integrity(mut self, value: LoaderIntegrity) -> Self {
328 self.words[1] = replace(self.words[1], 4, value as u64);
329 self
330 }
331
332 #[must_use]
334 pub const fn strict_control_flow_guard(mut self, value: Mitigation) -> Self {
335 self.words[1] = replace(self.words[1], 8, value as u64);
336 self
337 }
338
339 #[must_use]
341 pub const fn module_tampering(mut self, value: ModuleTampering) -> Self {
342 self.words[1] = replace(self.words[1], 12, value as u64);
343 self
344 }
345
346 #[must_use]
348 pub const fn restrict_indirect_branch_prediction(mut self, value: Mitigation) -> Self {
349 self.words[1] = replace(self.words[1], 16, value as u64);
350 self
351 }
352
353 #[must_use]
355 pub const fn allow_downgrade_dynamic_code(mut self, value: Mitigation) -> Self {
356 self.words[1] = replace(self.words[1], 20, value as u64);
357 self
358 }
359
360 #[must_use]
362 pub const fn disable_speculative_store_bypass(mut self, value: Mitigation) -> Self {
363 self.words[1] = replace(self.words[1], 24, value as u64);
364 self
365 }
366
367 #[must_use]
369 pub const fn cet_user_shadow_stacks(mut self, value: CetShadowStacks) -> Self {
370 self.words[1] = replace(self.words[1], 28, value as u64);
371 self
372 }
373
374 #[must_use]
376 pub const fn user_cet_context_ip_validation(
377 mut self,
378 value: UserCetContextIpValidation,
379 ) -> Self {
380 self.words[1] = replace(self.words[1], 32, value as u64);
381 self
382 }
383
384 #[must_use]
386 pub const fn block_non_cet_binaries(mut self, value: BlockNonCetBinaries) -> Self {
387 self.words[1] = replace(self.words[1], 36, value as u64);
388 self
389 }
390
391 #[must_use]
393 pub const fn extended_control_flow_guard(mut self, value: Mitigation) -> Self {
394 self.words[1] = replace(self.words[1], 40, value as u64);
395 self
396 }
397
398 #[must_use]
400 pub const fn pointer_authentication(mut self, value: Mitigation) -> Self {
401 self.words[1] = replace(self.words[1], 44, value as u64);
402 self
403 }
404
405 #[must_use]
407 pub const fn cet_dynamic_apis_out_of_process(mut self, value: Mitigation) -> Self {
408 self.words[1] = replace(self.words[1], 48, value as u64);
409 self
410 }
411
412 #[must_use]
414 pub const fn restrict_core_sharing(mut self, value: Mitigation) -> Self {
415 self.words[1] = replace(self.words[1], 52, value as u64);
416 self
417 }
418
419 #[must_use]
421 pub const fn disable_fsctl_system_calls(mut self, value: Mitigation) -> Self {
422 self.words[1] = replace(self.words[1], 56, value as u64);
423 self
424 }
425}
426
427const fn replace(word: u64, shift: u32, value: u64) -> u64 {
428 (word & !(3_u64 << shift)) | (value << shift)
429}
430
431const fn set_bit(word: u64, shift: u32, value: bool) -> u64 {
432 if value {
433 word | (1_u64 << shift)
434 } else {
435 word & !(1_u64 << shift)
436 }
437}
438
439#[cfg(test)]
440mod tests {
441 use super::*;
442
443 #[test]
444 fn setters_replace_only_their_field() {
445 let first = MitigationPolicy::new()
446 .dynamic_code(DynamicCode::ProhibitWithOptOut)
447 .font_disable(FontDisable::Audit)
448 .dynamic_code(DynamicCode::Allow);
449 assert_eq!(first.words(), [(2_u64 << 36) | (3_u64 << 48), 0]);
450
451 let second = MitigationPolicy::new()
452 .cet_user_shadow_stacks(CetShadowStacks::Strict)
453 .block_non_cet_binaries(BlockNonCetBinaries::NonEhContinuation);
454 assert_eq!(second.words(), [0, (3_u64 << 28) | (3_u64 << 36)]);
455 }
456
457 #[test]
458 fn legacy_bits_do_not_touch_two_bit_fields() {
459 let policy = MitigationPolicy::new()
460 .relocate_images(RelocateImages::RequireRelocations)
461 .dep(true)
462 .dep_atl_thunk(true)
463 .sehop(true);
464 assert_eq!(policy.words()[0], 7 | (3_u64 << 8));
465
466 let cleared = policy.dep(false).dep_atl_thunk(false).sehop(false);
467 assert_eq!(cleared.words()[0], 3_u64 << 8);
468 assert_eq!(MitigationPolicy::new().dep(true).dep(true).words()[0], 1);
469 }
470
471 #[test]
472 #[allow(clippy::too_many_lines)]
473 fn every_sdk_22621_field_has_the_expected_encoding() {
474 macro_rules! field {
475 ($policy:expr, $word:expr, $shift:expr, $value:expr) => {{
476 let mut expected = [0_u64; 2];
477 expected[$word] = ($value as u64) << $shift;
478 assert_eq!($policy.words(), expected);
479 }};
480 }
481
482 field!(MitigationPolicy::new().dep(true), 0, 0, 1);
483 field!(MitigationPolicy::new().dep_atl_thunk(true), 0, 1, 1);
484 field!(MitigationPolicy::new().sehop(true), 0, 2, 1);
485 field!(
486 MitigationPolicy::new().relocate_images(RelocateImages::RequireRelocations),
487 0,
488 8,
489 3
490 );
491 field!(
492 MitigationPolicy::new().heap_terminate(Mitigation::AlwaysOn),
493 0,
494 12,
495 1
496 );
497 field!(
498 MitigationPolicy::new().bottom_up_aslr(Mitigation::AlwaysOn),
499 0,
500 16,
501 1
502 );
503 field!(
504 MitigationPolicy::new().high_entropy_aslr(Mitigation::AlwaysOn),
505 0,
506 20,
507 1
508 );
509 field!(
510 MitigationPolicy::new().strict_handle_checks(Mitigation::AlwaysOn),
511 0,
512 24,
513 1
514 );
515 field!(
516 MitigationPolicy::new().disable_win32k_system_calls(Mitigation::AlwaysOn),
517 0,
518 28,
519 1
520 );
521 field!(
522 MitigationPolicy::new().disable_extension_points(Mitigation::AlwaysOn),
523 0,
524 32,
525 1
526 );
527 field!(
528 MitigationPolicy::new().dynamic_code(DynamicCode::ProhibitWithOptOut),
529 0,
530 36,
531 3
532 );
533 field!(
534 MitigationPolicy::new().control_flow_guard(ControlFlowGuard::ExportSuppression),
535 0,
536 40,
537 3
538 );
539 field!(
540 MitigationPolicy::new().signed_binaries(SignedBinaries::MicrosoftAndStore),
541 0,
542 44,
543 3
544 );
545 field!(
546 MitigationPolicy::new().font_disable(FontDisable::Audit),
547 0,
548 48,
549 3
550 );
551 field!(
552 MitigationPolicy::new().block_remote_images(Mitigation::AlwaysOn),
553 0,
554 52,
555 1
556 );
557 field!(
558 MitigationPolicy::new().block_low_label_images(Mitigation::AlwaysOn),
559 0,
560 56,
561 1
562 );
563 field!(
564 MitigationPolicy::new().prefer_system32_images(Mitigation::AlwaysOn),
565 0,
566 60,
567 1
568 );
569 field!(
570 MitigationPolicy::new().loader_integrity(LoaderIntegrity::Audit),
571 1,
572 4,
573 3
574 );
575 field!(
576 MitigationPolicy::new().strict_control_flow_guard(Mitigation::AlwaysOn),
577 1,
578 8,
579 1
580 );
581 field!(
582 MitigationPolicy::new().module_tampering(ModuleTampering::NoInherit),
583 1,
584 12,
585 3
586 );
587 field!(
588 MitigationPolicy::new().restrict_indirect_branch_prediction(Mitigation::AlwaysOn),
589 1,
590 16,
591 1
592 );
593 field!(
594 MitigationPolicy::new().allow_downgrade_dynamic_code(Mitigation::AlwaysOn),
595 1,
596 20,
597 1
598 );
599 field!(
600 MitigationPolicy::new().disable_speculative_store_bypass(Mitigation::AlwaysOn),
601 1,
602 24,
603 1
604 );
605 field!(
606 MitigationPolicy::new().cet_user_shadow_stacks(CetShadowStacks::Strict),
607 1,
608 28,
609 3
610 );
611 field!(
612 MitigationPolicy::new()
613 .user_cet_context_ip_validation(UserCetContextIpValidation::Relaxed),
614 1,
615 32,
616 3
617 );
618 field!(
619 MitigationPolicy::new().block_non_cet_binaries(BlockNonCetBinaries::NonEhContinuation),
620 1,
621 36,
622 3
623 );
624 field!(
625 MitigationPolicy::new().extended_control_flow_guard(Mitigation::AlwaysOn),
626 1,
627 40,
628 1
629 );
630 field!(
631 MitigationPolicy::new().pointer_authentication(Mitigation::AlwaysOn),
632 1,
633 44,
634 1
635 );
636 field!(
637 MitigationPolicy::new().cet_dynamic_apis_out_of_process(Mitigation::AlwaysOn),
638 1,
639 48,
640 1
641 );
642 field!(
643 MitigationPolicy::new().restrict_core_sharing(Mitigation::AlwaysOn),
644 1,
645 52,
646 1
647 );
648 field!(
649 MitigationPolicy::new().disable_fsctl_system_calls(Mitigation::AlwaysOn),
650 1,
651 56,
652 1
653 );
654 }
655}