├── .gitmodules ├── src ├── py.typed ├── x86 │ ├── __init__.py │ └── executor │ │ ├── dbg.c │ │ ├── .gitignore │ │ ├── .clang-format │ │ ├── include │ │ ├── perf_counters.h │ │ ├── data_loader.h │ │ ├── code_loader.h │ │ ├── dbg.h │ │ ├── actor.h │ │ ├── special_registers.h │ │ ├── host_page_tables.h │ │ ├── measurement.h │ │ ├── hardware_desc.h │ │ ├── fault_handler.h │ │ ├── macro_loader.h │ │ ├── main.h │ │ ├── input_parser.h │ │ ├── test_case_parser.h │ │ ├── vmx.h │ │ ├── memory_guest.h │ │ ├── svm.h │ │ ├── sandbox_manager.h │ │ ├── svm_constants.h │ │ ├── vmx_config.h │ │ └── page_tables_common.h │ │ ├── readme.md │ │ ├── Makefile │ │ ├── data_loader.c │ │ └── sandbox_manager.c ├── executor.py ├── __init__.py ├── custom_conf.yaml ├── input_generator.py ├── isa_loader.py └── factory.py ├── tests ├── .gitignore ├── configs │ ├── zdo.yaml │ ├── v4.yaml │ ├── sco.yaml │ ├── v1-ver.yaml │ ├── common-nsco-nv1.yaml │ ├── v1.yaml │ ├── v1-var.yaml │ ├── v1-store.yaml │ ├── common-nsco.yaml │ └── common.yaml ├── x86_tests │ ├── configs │ │ ├── copy.yaml │ │ ├── arch-actors.yaml │ │ ├── ct-deh.yaml │ │ ├── ssbp-detect.yaml │ │ ├── ct-seq.yaml │ │ ├── ct-cond.yaml │ │ ├── mpx.yaml │ │ ├── mpx-verif.yaml │ │ ├── vm-switch.yaml │ │ ├── div-detect.yaml │ │ ├── l1tf-p.yaml │ │ ├── l1tf-w.yaml │ │ ├── meltdown.yaml │ │ ├── ssbp-verif.yaml │ │ ├── div-verif.yaml │ │ ├── exceptions.yaml │ │ ├── meltdown-verif.yaml │ │ ├── l1tf-p-verif.yaml │ │ ├── l1tf-w-verif.yaml │ │ ├── fault-handler.yaml │ │ ├── common.yaml │ │ ├── arch.yaml │ │ ├── archdiff.yaml │ │ ├── base-categories.yaml │ │ └── base-and-simd-categories.yaml │ ├── asm │ │ ├── fault_INT3.asm │ │ ├── fault_INT1.asm │ │ ├── model_match.asm │ │ ├── fault_UD.asm │ │ ├── fault_BR.asm │ │ ├── model_match_xmm.asm │ │ ├── fault_load.asm │ │ ├── fault_rmw.asm │ │ ├── fault-div-zero-speculation.asm │ │ ├── fault-div-overflow-speculation.asm │ │ ├── asm_multiactor.asm │ │ ├── asm_symbol.asm │ │ ├── fault_ooo_mem_access.asm │ │ ├── macro_fault_handler.asm │ │ ├── large_arithmetic.asm │ │ ├── minimization-after.asm │ │ ├── spectre_v1_independent.asm │ │ ├── minimization-before.asm │ │ ├── spectre_ret.asm │ │ ├── vm_switch.asm │ │ ├── spectre_v1.asm │ │ ├── model_match_memory.asm │ │ ├── model_flags_match.asm │ │ ├── spectre_v1_arch.asm │ │ ├── asm_basic.asm │ │ ├── spectre_v1_n2.asm │ │ ├── direct_jumps.asm │ │ ├── actor_switch.asm │ │ ├── spectre_v1.1.asm │ │ ├── spectre_v4.asm │ │ ├── spectre_v2.asm │ │ └── calls.asm │ ├── unit_isa_loader.py │ └── asm_to_bin_testcase.py ├── test-detection.yaml ├── test-nondetection.yaml ├── quick-test.sh ├── runtests.sh ├── unit_analyser.py ├── unit_isa_loader.py ├── unit_docs.py └── pre-release.sh ├── docs ├── sandbox.md ├── assets │ ├── arch.png │ ├── ms_icon.png │ ├── detection.png │ └── pretty-arch.png ├── index.md ├── trophies.md ├── registers.md ├── user │ └── modes.md ├── development.md └── architecture.md ├── .github ├── CODEOWNERS └── workflows │ └── python-lint-and-test.yaml ├── AUTHORS ├── .gitignore ├── CODE_OF_CONDUCT.md ├── demo ├── detecting-v1-var.yaml ├── conf-v4.yaml ├── detecting-v4.yaml ├── detecting-mds.yaml ├── detecting-v1.yaml ├── detecting-foreshadow.yaml ├── detecting-zdo.yaml ├── tsa-sq │ ├── config.yaml │ └── template.asm ├── README.md └── big-fuzz.yaml ├── revizor.py ├── .editorconfig ├── LICENSE ├── pyproject.toml ├── mkdocs.yml ├── CONTRIBUTING.md ├── SECURITY.md └── README.md /.gitmodules: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/py.typed: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/x86/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/x86/executor/dbg.c: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/.gitignore: -------------------------------------------------------------------------------- 1 | *.o -------------------------------------------------------------------------------- /docs/sandbox.md: -------------------------------------------------------------------------------- 1 | UNDER CONSTRUCTION 2 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @OleksiiOleksenko 2 | -------------------------------------------------------------------------------- /tests/configs/zdo.yaml: -------------------------------------------------------------------------------- 1 | file: !include common-nsco.yaml 2 | 3 | x86_disable_div64: false 4 | -------------------------------------------------------------------------------- /docs/assets/arch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/sca-fuzzer/HEAD/docs/assets/arch.png -------------------------------------------------------------------------------- /docs/assets/ms_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/sca-fuzzer/HEAD/docs/assets/ms_icon.png -------------------------------------------------------------------------------- /docs/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | template: home.html 3 | title: Revizor 4 | --- 5 | 6 | Welcome to Revizor. 7 | -------------------------------------------------------------------------------- /tests/x86_tests/configs/copy.yaml: -------------------------------------------------------------------------------- 1 | file: !include common.yaml 2 | file: !include ct-deh.yaml 3 | 4 | -------------------------------------------------------------------------------- /docs/assets/detection.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/sca-fuzzer/HEAD/docs/assets/detection.png -------------------------------------------------------------------------------- /tests/configs/v4.yaml: -------------------------------------------------------------------------------- 1 | file: !include common-nsco.yaml 2 | 3 | x86_executor_enable_ssbp_patch: false 4 | -------------------------------------------------------------------------------- /docs/assets/pretty-arch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/sca-fuzzer/HEAD/docs/assets/pretty-arch.png -------------------------------------------------------------------------------- /tests/configs/sco.yaml: -------------------------------------------------------------------------------- 1 | file: !include common.yaml 2 | 3 | min_bb_per_function: 1 4 | max_bb_per_function: 1 5 | -------------------------------------------------------------------------------- /tests/configs/v1-ver.yaml: -------------------------------------------------------------------------------- 1 | file: !include v1.yaml 2 | 3 | contract_execution_clause: 4 | - conditional_br_misprediction 5 | -------------------------------------------------------------------------------- /tests/x86_tests/configs/arch-actors.yaml: -------------------------------------------------------------------------------- 1 | file: !include arch.yaml 2 | 3 | actors: 4 | - actor2: 5 | - mode: "host" 6 | -------------------------------------------------------------------------------- /tests/configs/common-nsco-nv1.yaml: -------------------------------------------------------------------------------- 1 | file: !include common-nsco.yaml 2 | 3 | min_bb_per_function: 1 4 | max_bb_per_function: 1 5 | -------------------------------------------------------------------------------- /tests/x86_tests/configs/ct-deh.yaml: -------------------------------------------------------------------------------- 1 | contract_observation_clause: ct 2 | contract_execution_clause: 3 | - delayed-exception-handling 4 | -------------------------------------------------------------------------------- /tests/x86_tests/configs/ssbp-detect.yaml: -------------------------------------------------------------------------------- 1 | file: !include common.yaml 2 | 3 | input_gen_seed: 400 4 | x86_executor_enable_ssbp_patch: false 5 | -------------------------------------------------------------------------------- /tests/x86_tests/configs/ct-seq.yaml: -------------------------------------------------------------------------------- 1 | file: !include common.yaml 2 | 3 | contract_observation_clause: ct 4 | contract_execution_clause: 5 | - seq -------------------------------------------------------------------------------- /tests/x86_tests/configs/ct-cond.yaml: -------------------------------------------------------------------------------- 1 | file: !include common.yaml 2 | 3 | contract_observation_clause: ct 4 | contract_execution_clause: 5 | - cond 6 | -------------------------------------------------------------------------------- /src/x86/executor/.gitignore: -------------------------------------------------------------------------------- 1 | .tmp* 2 | *.o 3 | *.cmd 4 | *.symvers 5 | *.order 6 | *.ko 7 | *.mod 8 | *.mod.c 9 | start_qemu.sh 10 | update_module.sh -------------------------------------------------------------------------------- /tests/x86_tests/configs/mpx.yaml: -------------------------------------------------------------------------------- 1 | file: !include common.yaml 2 | file: !include ct-seq.yaml 3 | 4 | generator_faults_allowlist: 5 | - bounds-range-exceeded 6 | -------------------------------------------------------------------------------- /tests/x86_tests/configs/mpx-verif.yaml: -------------------------------------------------------------------------------- 1 | file: !include common.yaml 2 | file: !include ct-deh.yaml 3 | 4 | generator_faults_allowlist: 5 | - bounds-range-exceeded 6 | -------------------------------------------------------------------------------- /tests/x86_tests/configs/vm-switch.yaml: -------------------------------------------------------------------------------- 1 | file: !include common.yaml 2 | file: !include ct-seq.yaml 3 | 4 | 5 | actors: 6 | - actor2: 7 | - mode: "guest" 8 | -------------------------------------------------------------------------------- /tests/x86_tests/asm/fault_INT3.asm: -------------------------------------------------------------------------------- 1 | .intel_syntax noprefix 2 | .test_case_enter: 3 | .section .data.main 4 | int3 5 | mov rax, qword ptr [r14 + 256] 6 | .test_case_exit: 7 | -------------------------------------------------------------------------------- /tests/configs/v1.yaml: -------------------------------------------------------------------------------- 1 | file: !include common-nsco.yaml 2 | 3 | max_successors_per_bb: 2 4 | min_successors_per_bb: 2 5 | min_bb_per_function: 3 6 | max_bb_per_function: 3 7 | -------------------------------------------------------------------------------- /tests/x86_tests/configs/div-detect.yaml: -------------------------------------------------------------------------------- 1 | file: !include common.yaml 2 | file: !include ct-deh.yaml 3 | 4 | generator_faults_allowlist: 5 | - div-by-zero 6 | - div-overflow 7 | -------------------------------------------------------------------------------- /tests/x86_tests/configs/l1tf-p.yaml: -------------------------------------------------------------------------------- 1 | file: !include common.yaml 2 | file: !include ct-deh.yaml 3 | 4 | actors: 5 | - main: 6 | - data_properties: 7 | - present: false 8 | -------------------------------------------------------------------------------- /tests/x86_tests/configs/l1tf-w.yaml: -------------------------------------------------------------------------------- 1 | file: !include common.yaml 2 | file: !include ct-deh.yaml 3 | 4 | actors: 5 | - main: 6 | - data_properties: 7 | - writable: false 8 | -------------------------------------------------------------------------------- /tests/x86_tests/configs/meltdown.yaml: -------------------------------------------------------------------------------- 1 | file: !include common.yaml 2 | file: !include ct-deh.yaml 3 | 4 | actors: 5 | - main: 6 | - data_properties: 7 | - user: true 8 | -------------------------------------------------------------------------------- /tests/x86_tests/asm/fault_INT1.asm: -------------------------------------------------------------------------------- 1 | .intel_syntax noprefix 2 | .test_case_enter: 3 | .section .data.main 4 | .byte 0xf1 # int1 5 | mov rax, qword ptr [r14 + 256] 6 | .test_case_exit: 7 | -------------------------------------------------------------------------------- /tests/x86_tests/asm/model_match.asm: -------------------------------------------------------------------------------- 1 | .intel_syntax noprefix 2 | .test_case_enter: 3 | .section .data.main 4 | 5 | # empty - leaving initial reg values unchanged 6 | 7 | .test_case_exit: 8 | -------------------------------------------------------------------------------- /tests/x86_tests/configs/ssbp-verif.yaml: -------------------------------------------------------------------------------- 1 | file: !include common.yaml 2 | 3 | input_gen_seed: 400 4 | x86_executor_enable_ssbp_patch: false 5 | 6 | contract_execution_clause: 7 | - bpas 8 | -------------------------------------------------------------------------------- /tests/x86_tests/configs/div-verif.yaml: -------------------------------------------------------------------------------- 1 | file: !include common.yaml 2 | 3 | contract_execution_clause: 4 | - vspec-ops-div 5 | 6 | generator_faults_allowlist: 7 | - div-by-zero 8 | - div-overflow 9 | -------------------------------------------------------------------------------- /tests/x86_tests/configs/exceptions.yaml: -------------------------------------------------------------------------------- 1 | file: !include common.yaml 2 | file: !include ct-seq.yaml 3 | 4 | generator_faults_allowlist: 5 | - debug-register 6 | - breakpoint 7 | - opcode-undefined 8 | -------------------------------------------------------------------------------- /tests/x86_tests/asm/fault_UD.asm: -------------------------------------------------------------------------------- 1 | .intel_syntax noprefix 2 | .test_case_enter: 3 | .section .data.main 4 | ud2 5 | and rax, 0b111111111111 # instrumentation 6 | mov rax, qword ptr [r14 + rax + 128] 7 | .test_case_exit: 8 | -------------------------------------------------------------------------------- /tests/x86_tests/configs/meltdown-verif.yaml: -------------------------------------------------------------------------------- 1 | file: !include common.yaml 2 | 3 | contract_execution_clause: 4 | - nullinj-fault 5 | 6 | actors: 7 | - main: 8 | - data_properties: 9 | - user: true 10 | -------------------------------------------------------------------------------- /tests/x86_tests/configs/l1tf-p-verif.yaml: -------------------------------------------------------------------------------- 1 | file: !include common.yaml 2 | 3 | contract_execution_clause: 4 | - nullinj-fault 5 | 6 | actors: 7 | - main: 8 | - data_properties: 9 | - present: false 10 | 11 | -------------------------------------------------------------------------------- /tests/x86_tests/configs/l1tf-w-verif.yaml: -------------------------------------------------------------------------------- 1 | file: !include common.yaml 2 | 3 | contract_execution_clause: 4 | - nullinj-fault 5 | 6 | actors: 7 | - main: 8 | - data_properties: 9 | - writable: false 10 | 11 | -------------------------------------------------------------------------------- /tests/configs/v1-var.yaml: -------------------------------------------------------------------------------- 1 | file: !include common-nsco.yaml 2 | 3 | min_successors_per_bb: 2 4 | max_successors_per_bb: 2 5 | min_bb_per_function: 3 6 | max_bb_per_function: 3 7 | contract_execution_clause: 8 | - conditional_br_misprediction 9 | -------------------------------------------------------------------------------- /tests/x86_tests/asm/fault_BR.asm: -------------------------------------------------------------------------------- 1 | .intel_syntax noprefix 2 | .test_case_enter: 3 | .section .data.main 4 | and rax, 0b011111111111 5 | add rax, 0x1000 6 | bndcu bnd1, qword ptr [r14 + rax] 7 | mov rax, qword ptr [r14 + rax] 8 | .test_case_exit: 9 | -------------------------------------------------------------------------------- /src/executor.py: -------------------------------------------------------------------------------- 1 | """ 2 | File: Architecture-independent Executors 3 | 4 | Copyright (C) Microsoft Corporation 5 | SPDX-License-Identifier: MIT 6 | """ 7 | 8 | # This is a placeholder - so far, we have no architecture-independent code in executors 9 | -------------------------------------------------------------------------------- /src/x86/executor/.clang-format: -------------------------------------------------------------------------------- 1 | ColumnLimit: 100 2 | IndentWidth: 4 3 | BreakBeforeBraces: Linux 4 | AllowShortIfStatementsOnASingleLine: false 5 | AlignConsecutiveMacros: 6 | Enabled: true 7 | AcrossEmptyLines: false 8 | AcrossComments: false 9 | -------------------------------------------------------------------------------- /tests/x86_tests/asm/model_match_xmm.asm: -------------------------------------------------------------------------------- 1 | .intel_syntax noprefix 2 | .test_case_enter: 3 | .section .data.main 4 | movq rax, xmm0 5 | movq rbx, xmm1 6 | movq rdx, xmm2 7 | movq rcx, xmm3 8 | movq rsi, xmm4 9 | movq rdi, xmm5 10 | .test_case_exit: 11 | -------------------------------------------------------------------------------- /tests/x86_tests/asm/fault_load.asm: -------------------------------------------------------------------------------- 1 | .intel_syntax noprefix 2 | .test_case_enter: 3 | .section .data.main 4 | mov rax, qword ptr [r14 + 4096] 5 | xor rax, rcx 6 | and rax, 0b111111111111 # instrumentation 7 | mov rax, qword ptr [r14 + rax] 8 | .test_case_exit: 9 | -------------------------------------------------------------------------------- /tests/x86_tests/asm/fault_rmw.asm: -------------------------------------------------------------------------------- 1 | .intel_syntax noprefix 2 | .test_case_enter: 3 | .section .data.main 4 | xadd qword ptr [r14 + 4096], rax 5 | add rax, rbx 6 | and rax, 0b111111111111 # instrumentation 7 | mov rax, qword ptr [r14 + rax + 128] 8 | .test_case_exit: 9 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | Here is an inevitably incomplete list of MUCH-APPRECIATED CONTRIBUTORS: 2 | 3 | Oleksii Oleksenko 4 | Boris Koepf 5 | Emanuele Vannacci 6 | Jana Hofmann 7 | Connor Shugg 8 | Marco Guarnieri 9 | Flavien Solt 10 | Brian Fu 11 | Alvise de Faveri Tron 12 | -------------------------------------------------------------------------------- /tests/x86_tests/configs/fault-handler.yaml: -------------------------------------------------------------------------------- 1 | file: !include common.yaml 2 | file: !include ct-seq.yaml 3 | 4 | actors: 5 | - main: 6 | - data_properties: 7 | - present: false 8 | 9 | executor_mode: F+R 10 | logging_modes: 11 | - dbg_dump_htraces 12 | -------------------------------------------------------------------------------- /tests/x86_tests/asm/fault-div-zero-speculation.asm: -------------------------------------------------------------------------------- 1 | .intel_syntax noprefix 2 | .test_case_enter: 3 | .section .data.main 4 | mov edx, 0 5 | mov ebx, 0 6 | div ebx 7 | xor rax, rcx 8 | and rax, 0b111111111111 # instrumentation 9 | mov rax, qword ptr [r14 + rax + 128] 10 | .test_case_exit: 11 | -------------------------------------------------------------------------------- /tests/configs/v1-store.yaml: -------------------------------------------------------------------------------- 1 | file: !include common-nsco.yaml 2 | 3 | min_successors_per_bb: 2 4 | max_successors_per_bb: 2 5 | min_bb_per_function: 3 6 | max_bb_per_function: 3 7 | contract_observation_clause: ct-nonspecstore 8 | contract_execution_clause: 9 | - conditional_br_misprediction 10 | -------------------------------------------------------------------------------- /tests/x86_tests/configs/common.yaml: -------------------------------------------------------------------------------- 1 | input_gen_seed: 1234567 2 | program_generator_seed: 1234567 3 | 4 | # Acceptance tests do not require a large sample size 5 | executor_sample_sizes: 6 | - 10 7 | 8 | # No logging 9 | logging_modes: 10 | - 11 | # - dbg_dump_htraces 12 | # - dbg_dump_ctraces 13 | -------------------------------------------------------------------------------- /tests/x86_tests/asm/fault-div-overflow-speculation.asm: -------------------------------------------------------------------------------- 1 | .intel_syntax noprefix 2 | .test_case_enter: 3 | .section .data.main 4 | 5 | MOV ebx, 1 6 | lfence 7 | DIV bx 8 | 9 | XOR rax, rcx 10 | AND rax, 0b111111111111 # instrumentation 11 | MOV rax, qword ptr [r14 + rax + 128] 12 | 13 | 14 | .test_case_exit: 15 | -------------------------------------------------------------------------------- /tests/x86_tests/asm/asm_multiactor.asm: -------------------------------------------------------------------------------- 1 | .intel_syntax noprefix 2 | .test_case_enter: 3 | .section .data.main 4 | 5 | .function_0: 6 | nop 7 | nop 8 | 9 | .section .data.guest_1 10 | .function_1: 11 | nop 12 | 13 | .section .data.main 14 | .function_2: 15 | .bb0: 16 | nop 17 | 18 | # .section exit 19 | .test_case_exit: 20 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | cmake-build-*/ 2 | build/ 3 | .vscode/ 4 | .mypy_cache/ 5 | .lsync* 6 | venv/ 7 | **/__pycache__/ 8 | base.json 9 | src/x86/*.json 10 | *.code-workspace 11 | *.o 12 | src/generated.asm 13 | generated.asm 14 | generated 15 | src/x86/executor/.cache.mk 16 | src/x86/executor/measurement.o.ur-safe 17 | dbg/ 18 | site 19 | dist/ 20 | -------------------------------------------------------------------------------- /tests/x86_tests/configs/arch.yaml: -------------------------------------------------------------------------------- 1 | file: !include common.yaml 2 | file: !include base-and-simd-categories.yaml 3 | 4 | fuzzer: architectural 5 | enable_priming: false 6 | inputs_per_class: 1 7 | logging_modes: 8 | - info 9 | - dbg_violation 10 | 11 | program_size: 300 12 | avg_mem_accesses: 150 13 | max_bb_per_function: 3 14 | min_bb_per_function: 3 15 | -------------------------------------------------------------------------------- /tests/x86_tests/configs/archdiff.yaml: -------------------------------------------------------------------------------- 1 | file: !include common.yaml 2 | file: !include base-and-simd-categories.yaml 3 | 4 | fuzzer: archdiff 5 | enable_priming: false 6 | inputs_per_class: 1 7 | logging_modes: 8 | - info 9 | # - dbg_violation 10 | 11 | program_size: 150 12 | avg_mem_accesses: 100 13 | max_bb_per_function: 3 14 | min_bb_per_function: 3 15 | -------------------------------------------------------------------------------- /tests/x86_tests/configs/base-categories.yaml: -------------------------------------------------------------------------------- 1 | instruction_categories: 2 | - BASE-BINARY 3 | - BASE-BITBYTE 4 | - BASE-CMOV 5 | - BASE-COND_BR 6 | - BASE-CONVERT 7 | - BASE-DATAXFER 8 | - BASE-FLAGOP 9 | - BASE-LOGICAL 10 | - BASE-MISC 11 | - BASE-NOP 12 | - BASE-POP 13 | - BASE-PUSH 14 | - BASE-SEMAPHORE 15 | - BASE-SETCC 16 | - BASE-STRINGOP 17 | - BASE-WIDENOP 18 | -------------------------------------------------------------------------------- /src/x86/executor/include/perf_counters.h: -------------------------------------------------------------------------------- 1 | /// File: Header for perf_counters.c 2 | /// 3 | // Copyright (C) Microsoft Corporation 4 | // SPDX-License-Identifier: MIT 5 | 6 | #ifndef _PERF_COUNTERS_H_ 7 | #define _PERF_COUNTERS_H_ 8 | 9 | int pfc_configure(void); 10 | 11 | int init_perf_counters(void); 12 | void free_perf_counters(void); 13 | 14 | #endif // _PERF_COUNTERS_H_ 15 | -------------------------------------------------------------------------------- /tests/x86_tests/asm/asm_symbol.asm: -------------------------------------------------------------------------------- 1 | .intel_syntax noprefix 2 | .test_case_enter: 3 | .section .data.main 4 | 5 | .macro.measurement_start: nop qword ptr [rax + 0xff] 6 | 7 | nop 8 | 9 | .macro.measurement_end: nop qword ptr [rax + 0xff] 10 | 11 | and rax, rax 12 | 13 | .function_1: 14 | 15 | nop 16 | 17 | .section .data.guest_1 18 | .function_2: 19 | nop 20 | 21 | 22 | .test_case_exit: 23 | -------------------------------------------------------------------------------- /src/x86/executor/include/data_loader.h: -------------------------------------------------------------------------------- 1 | /// File: Header for data_loader.c 2 | /// 3 | // Copyright (C) Microsoft Corporation 4 | // SPDX-License-Identifier: MIT 5 | 6 | #ifndef _DATA_LOADER_H_ 7 | #define _DATA_LOADER_H_ 8 | 9 | #include 10 | 11 | int load_sandbox_data(int input_id); 12 | 13 | int init_data_loader(void); 14 | void free_data_loader(void); 15 | 16 | #endif // _DATA_LOADER_H_ 17 | -------------------------------------------------------------------------------- /tests/x86_tests/asm/fault_ooo_mem_access.asm: -------------------------------------------------------------------------------- 1 | .intel_syntax noprefix 2 | .test_case_enter: 3 | .section .data.main 4 | mov rax, qword ptr [r14 + 4096 + 64] 5 | 6 | # dependent memory access 7 | and rbx, 0b111111111000 # instrumentation 8 | mov rax, qword ptr [r14 + rbx] 9 | 10 | # independent memory access 11 | and rbx, 0b111111111000 # instrumentation 12 | mov rax, qword ptr [r14 + rbx] 13 | .test_case_exit: 14 | -------------------------------------------------------------------------------- /tests/x86_tests/asm/macro_fault_handler.asm: -------------------------------------------------------------------------------- 1 | .intel_syntax noprefix 2 | .test_case_enter: 3 | 4 | .section .data.main 5 | .function_main_0: 6 | 7 | mov rbx, qword ptr [r14 + 0x300] 8 | mov rax, qword ptr [r14 + 0x1000] 9 | lfence 10 | 11 | .macro.fault_handler: 12 | mov rax, qword ptr [r14 + 0x200] 13 | 14 | 15 | # ----------------------------- Exit ------------------------------------------------------------ 16 | .test_case_exit: 17 | -------------------------------------------------------------------------------- /src/x86/executor/include/code_loader.h: -------------------------------------------------------------------------------- 1 | /// File: Header for code_loader.c 2 | /// 3 | // Copyright (C) Microsoft Corporation 4 | // SPDX-License-Identifier: MIT 5 | 6 | #ifndef _CODE_LOADER_H_ 7 | #define _CODE_LOADER_H_ 8 | 9 | #include 10 | 11 | extern uint8_t *loaded_test_case_entry; 12 | 13 | int load_sandbox_code(void); 14 | 15 | int init_code_loader(void); 16 | void free_code_loader(void); 17 | 18 | #endif // _CODE_LOADER_H_ 19 | -------------------------------------------------------------------------------- /src/__init__.py: -------------------------------------------------------------------------------- 1 | # flake8: noqa 2 | 3 | from .isa_loader import * 4 | from .executor import * 5 | from .analyser import * 6 | from .input_generator import * 7 | from .generator import * 8 | from .cli import * 9 | from .util import * 10 | from .postprocessor import * 11 | from .model import * 12 | from .interfaces import * 13 | from .fuzzer import * 14 | from .factory import * 15 | from .config import * 16 | 17 | from .x86 import * 18 | 19 | __version__ = "1.3.2" 20 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Microsoft Open Source Code of Conduct 2 | 3 | This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). 4 | 5 | Resources: 6 | 7 | - [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/) 8 | - [Microsoft Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) 9 | - Contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with questions or concerns -------------------------------------------------------------------------------- /tests/configs/common-nsco.yaml: -------------------------------------------------------------------------------- 1 | file: !include common.yaml 2 | 3 | instruction_blocklist_append: 4 | - cmpsb 5 | - cmpsd 6 | - cmpsw 7 | - cmpsq 8 | - scasb 9 | - scasd 10 | - scasw 11 | - scasq 12 | - repe cmpsb 13 | - repe cmpsd 14 | - repe cmpsw 15 | - repe cmpsq 16 | - repe scasb 17 | - repe scasd 18 | - repe scasw 19 | - repe scasq 20 | - repne cmpsb 21 | - repne cmpsd 22 | - repne cmpsw 23 | - repne cmpsq 24 | - repne scasb 25 | - repne scasd 26 | - repne scasw 27 | - repne scasq 28 | -------------------------------------------------------------------------------- /src/custom_conf.yaml: -------------------------------------------------------------------------------- 1 | contract_observation_clause: ct 2 | contract_execution_clause: 3 | - cond 4 | # - seq 5 | 6 | enable_ssbp_patch: true 7 | enable_assist_page: false 8 | 9 | feedback_driven_generator: false 10 | input_gen_entropy_bits: 4 11 | 12 | inputs_per_class: 2 13 | 14 | executor_warmups: 40 15 | 16 | # coverage_type: none 17 | # no_priming: true 18 | 19 | # input_gen_seed: 0 20 | 21 | logging_modes: 22 | - info 23 | - fuzzer_debug 24 | # - fuzzer_trace 25 | # - coverage_debug 26 | -------------------------------------------------------------------------------- /tests/x86_tests/configs/base-and-simd-categories.yaml: -------------------------------------------------------------------------------- 1 | instruction_categories: 2 | - BASE-BINARY 3 | - BASE-BITBYTE 4 | - BASE-CMOV 5 | - BASE-COND_BR 6 | - BASE-CONVERT 7 | - BASE-DATAXFER 8 | - BASE-FLAGOP 9 | - BASE-LOGICAL 10 | - BASE-MISC 11 | - BASE-NOP 12 | - BASE-POP 13 | - BASE-PUSH 14 | - BASE-SEMAPHORE 15 | - BASE-SETCC 16 | - BASE-STRINGOP 17 | - BASE-WIDENOP 18 | - SSE-SSE 19 | - SSE-DATAXFER 20 | - SSE-MISC 21 | - SSE2-DATAXFER 22 | - SSE2-MISC 23 | - CLFLUSHOPT-CLFLUSHOPT 24 | - CLFSH-MISC 25 | -------------------------------------------------------------------------------- /src/x86/executor/readme.md: -------------------------------------------------------------------------------- 1 | # Install 2 | 3 | See https://microsoft.github.io/sca-fuzzer/quick-start/ or `README.md` in the project root. 4 | 5 | # Using the executor 6 | 7 | Use the Revizor CLI (`revizor.py`). 8 | This executor is not meant to be used standalone. 9 | 10 | On your own peril, you could try using it directly, through the `/sys/x86_executor/` pseudo file system. 11 | You can find an example of how to use it in `src/x86/tests/run.bats`. 12 | But I promise you, there will come a point when your machine will crash or hang. 13 | Better not. 14 | -------------------------------------------------------------------------------- /tests/test-detection.yaml: -------------------------------------------------------------------------------- 1 | instruction_categories: 2 | - BASE-NOP 3 | - BASE-BINARY 4 | - BASE-BITBYTE 5 | - BASE-COND_BR 6 | - BASE-CMOV 7 | - BASE-CONVERT 8 | - BASE-DATAXFER 9 | - BASE-FLAGOP 10 | - BASE-SETCC 11 | - BASE-LOGICAL 12 | - BASE-POP 13 | - BASE-PUSH 14 | contract_observation_clause: ct 15 | contract_execution_clause: 16 | - seq 17 | 18 | program_size: 16 19 | min_bb_per_function: 5 20 | max_bb_per_function: 5 21 | avg_mem_accesses: 4 22 | input_gen_entropy_bits: 9 23 | 24 | x86_executor_enable_ssbp_patch: true 25 | -------------------------------------------------------------------------------- /tests/test-nondetection.yaml: -------------------------------------------------------------------------------- 1 | instruction_categories: 2 | - BASE-NOP 3 | - BASE-BINARY 4 | - BASE-BITBYTE 5 | - BASE-COND_BR 6 | - BASE-CMOV 7 | - BASE-CONVERT 8 | - BASE-DATAXFER 9 | - BASE-FLAGOP 10 | - BASE-SETCC 11 | - BASE-LOGICAL 12 | - BASE-POP 13 | - BASE-PUSH 14 | contract_observation_clause: ct 15 | contract_execution_clause: 16 | - cond 17 | 18 | program_size: 16 19 | min_bb_per_function: 5 20 | max_bb_per_function: 5 21 | avg_mem_accesses: 4 22 | input_gen_entropy_bits: 3 23 | 24 | x86_executor_enable_ssbp_patch: true 25 | -------------------------------------------------------------------------------- /demo/detecting-v1-var.yaml: -------------------------------------------------------------------------------- 1 | # contract 2 | contract_observation_clause: ct 3 | contract_execution_clause: 4 | - cond 5 | analyser_subsets_is_violation: false 6 | 7 | # tested instructions 8 | instruction_categories: 9 | - BASE-BITBYTE 10 | - BASE-COND_BR 11 | - BASE-CMOV 12 | - BASE-LOGICAL 13 | 14 | # fuzzing configuration 15 | enable_speculation_filter: true 16 | enable_observation_filter: true 17 | input_gen_entropy_bits: 16 18 | min_bb_per_function: 2 19 | max_bb_per_function: 2 20 | program_size: 20 21 | avg_mem_accesses: 10 22 | inputs_per_class: 2 23 | -------------------------------------------------------------------------------- /demo/conf-v4.yaml: -------------------------------------------------------------------------------- 1 | # contract 2 | contract_observation_clause: ct 3 | contract_execution_clause: 4 | - seq 5 | 6 | # tested instructions 7 | instruction_categories: 8 | - BASE-BITBYTE 9 | - BASE-COND_BR 10 | - BASE-CMOV 11 | - BASE-LOGICAL 12 | 13 | # environment 14 | x86_executor_enable_ssbp_patch: false 15 | 16 | # fuzzing configuration 17 | enable_speculation_filter: true 18 | enable_observation_filter: true 19 | input_gen_entropy_bits: 16 20 | min_bb_per_function: 1 21 | max_bb_per_function: 1 22 | program_size: 20 23 | avg_mem_accesses: 10 24 | inputs_per_class: 2 25 | -------------------------------------------------------------------------------- /tests/x86_tests/asm/large_arithmetic.asm: -------------------------------------------------------------------------------- 1 | .intel_syntax noprefix 2 | .test_case_enter: 3 | .section .data.main 4 | mov rbx, 100 5 | .l1: 6 | lfence 7 | lea rax, qword ptr [rax + rax + 8] 8 | lea rax, qword ptr [rax + rax + 8] 9 | lea rax, qword ptr [rax + rax + 8] 10 | lea rax, qword ptr [rax + rax + 8] 11 | lea rax, qword ptr [rax + rax + 8] 12 | lea rax, qword ptr [rax + rax + 8] 13 | lea rax, qword ptr [rax + rax + 8] 14 | lea rax, qword ptr [rax + rax + 8] 15 | lea rax, qword ptr [rax + rax + 8] 16 | dec rbx 17 | jnz .l1 18 | .l2: 19 | 20 | lfence 21 | 22 | .test_case_exit: 23 | -------------------------------------------------------------------------------- /demo/detecting-v4.yaml: -------------------------------------------------------------------------------- 1 | # contract 2 | contract_observation_clause: ct 3 | contract_execution_clause: 4 | - seq 5 | 6 | # tested instructions 7 | instruction_categories: 8 | - BASE-BITBYTE 9 | - BASE-COND_BR 10 | - BASE-CMOV 11 | - BASE-LOGICAL 12 | 13 | # environment 14 | x86_executor_enable_ssbp_patch: false 15 | 16 | # fuzzing configuration 17 | enable_speculation_filter: true 18 | enable_observation_filter: true 19 | input_gen_entropy_bits: 16 20 | min_bb_per_function: 1 21 | max_bb_per_function: 1 22 | program_size: 20 23 | avg_mem_accesses: 10 24 | inputs_per_class: 2 25 | -------------------------------------------------------------------------------- /revizor.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | File: Command Line Interface to Revizor 4 | 5 | Copyright (C) Microsoft Corporation 6 | SPDX-License-Identifier: MIT 7 | """ 8 | 9 | try: 10 | from src.cli import main 11 | except ImportError as orig_import_error: 12 | try: 13 | from revizor.cli import main 14 | except ImportError: 15 | print("Unable to import main from src.cli.") 16 | print(f"Issue during import of src.cli: `{orig_import_error}`") 17 | exit(1) 18 | 19 | 20 | if __name__ == '__main__': 21 | exit_code = main() 22 | exit(exit_code) 23 | -------------------------------------------------------------------------------- /tests/x86_tests/asm/minimization-after.asm: -------------------------------------------------------------------------------- 1 | .intel_syntax noprefix 2 | .test_case_enter: 3 | .section .data.main 4 | .function_main: 5 | .macro.measurement_start: nop qword ptr [rax + 0xff] 6 | and rax, 0b1111111111111 # instrumentation 7 | or ebx, dword ptr [r14 + rax] # speculation source ? 8 | mov al, bl 9 | xor ax, -2067 10 | test al, -117 # instrumentation 11 | and rax, 0b1111111111111 # instrumentation 12 | mov qword ptr [r14 + rax], rcx # speculation sink ? 13 | .section .data.main 14 | .function_end: 15 | .macro.measurement_end: nop qword ptr [rax + 0xff] 16 | .section .data.main 17 | .test_case_exit:nop 18 | -------------------------------------------------------------------------------- /demo/detecting-mds.yaml: -------------------------------------------------------------------------------- 1 | # contract 2 | contract_observation_clause: ct 3 | contract_execution_clause: 4 | - seq-assist 5 | 6 | # tested instructions 7 | instruction_categories: 8 | - BASE-BITBYTE 9 | - BASE-COND_BR 10 | - BASE-CMOV 11 | - BASE-LOGICAL 12 | 13 | # environment 14 | actors: 15 | - main: 16 | - data_properties: 17 | - accessed: False 18 | 19 | # fuzzing configuration 20 | enable_speculation_filter: true 21 | enable_observation_filter: true 22 | input_gen_entropy_bits: 16 23 | min_bb_per_function: 1 24 | max_bb_per_function: 1 25 | program_size: 20 26 | avg_mem_accesses: 10 27 | inputs_per_class: 2 28 | -------------------------------------------------------------------------------- /tests/x86_tests/asm/spectre_v1_independent.asm: -------------------------------------------------------------------------------- 1 | # This test case is identical to spectre_v1 except the offset of the speculative mem. access 2 | # is input-independent. Therefore, this test case must not be flagged. 3 | 4 | .intel_syntax noprefix 5 | .test_case_enter: 6 | .section .data.main 7 | MOV rcx, r14 8 | 9 | # input: ebx - a random value, eax - fixed value 10 | MOV rax, 128 11 | lfence 12 | 13 | # no delay to increase the likelihood of a false positive 14 | SHL rbx, 63 15 | SHR rbx, 63 16 | 17 | # speculation 18 | CMP rbx, 0 19 | JE .l1 20 | .l0: 21 | # rbx != 0 22 | MOV rcx, qword ptr [rcx + rax] 23 | JMP .l2 24 | .l1: 25 | # rbx == 0 26 | MOV rcx, qword ptr [rcx] 27 | .l2: 28 | MFENCE 29 | 30 | .test_case_exit: 31 | -------------------------------------------------------------------------------- /tests/x86_tests/unit_isa_loader.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright (C) Microsoft Corporation 3 | SPDX-License-Identifier: MIT 4 | """ 5 | import unittest 6 | from pathlib import Path 7 | 8 | from src.isa_loader import InstructionSet 9 | from src.config import CONF 10 | 11 | test_path = Path(__file__).resolve() 12 | test_dir = test_path.parent 13 | CONF.instruction_set = "x86-64" 14 | 15 | 16 | class x86ISALoaderTest(unittest.TestCase): 17 | 18 | def test_instruction_filtering(self): 19 | instruction_set = InstructionSet((test_dir / "min_x86.json").absolute().as_posix(), 20 | ["BASE-BINARY"]) 21 | inst_names = [i.name for i in instruction_set.instructions] 22 | self.assertNotIn("HLT", inst_names) 23 | -------------------------------------------------------------------------------- /tests/x86_tests/asm/minimization-before.asm: -------------------------------------------------------------------------------- 1 | .intel_syntax noprefix 2 | .test_case_enter: 3 | .section .data.main 4 | .function_main: 5 | .bb_main.entry: 6 | jmp .bb_main.0 7 | .bb_main.0: 8 | test dil, 51 9 | adc ax, -49 10 | xor eax, ecx 11 | and rax, 0b1111111111111 # instrumentation 12 | or ebx, dword ptr [r14 + rax] 13 | or al, bl 14 | and rsi, 0b1111111111111 # instrumentation 15 | xor byte ptr [r14 + rsi], al 16 | setl bl 17 | xor ax, -2067 18 | lea si, qword ptr [rsi + rbx] 19 | sbb cl, cl 20 | and rdx, 0b1111111111111 # instrumentation 21 | lock and dword ptr [r14 + rdx], -37 22 | dec al 23 | test al, -117 # instrumentation 24 | and rax, 0b1111111111111 # instrumentation 25 | xchg qword ptr [r14 + rax], rcx 26 | movsx esi, cl 27 | xadd rdi, rdi 28 | .bb_main.exit: 29 | .test_case_exit: 30 | -------------------------------------------------------------------------------- /tests/configs/common.yaml: -------------------------------------------------------------------------------- 1 | input_gen_entropy_bits: 24 2 | inputs_per_class: 2 3 | 4 | enable_speculation_filter: true 5 | enable_observation_filter: true 6 | enable_priming: true 7 | 8 | program_size: 32 9 | avg_mem_accesses: 16 10 | 11 | logging_modes: 12 | - info 13 | - stat 14 | 15 | instruction_categories: 16 | - BASE-BINARY 17 | - BASE-BITBYTE 18 | - BASE-CMOV 19 | - BASE-COND_BR 20 | - BASE-CONVERT 21 | - BASE-DATAXFER 22 | - BASE-FLAGOP 23 | - BASE-LOGICAL 24 | - BASE-MISC 25 | - BASE-NOP 26 | - BASE-POP 27 | - BASE-PUSH 28 | - BASE-SEMAPHORE 29 | - BASE-SETCC 30 | - BASE-STRINGOP 31 | 32 | # these clauses may be re-assigned later 33 | contract_observation_clause: loads+stores+pc 34 | contract_execution_clause: 35 | - no_speculation 36 | 37 | program_generator_seed: 100 38 | input_gen_seed: 100 39 | -------------------------------------------------------------------------------- /src/x86/executor/include/dbg.h: -------------------------------------------------------------------------------- 1 | /// File: 2 | /// - Functions and macros used for debugging. 3 | /// Should be included temporary where needed, and removed before committing. 4 | /// 5 | // Copyright (C) Microsoft Corporation 6 | // SPDX-License-Identifier: MIT 7 | 8 | #ifndef _DEBUG_H_ 9 | #define _DEBUG_H_ 10 | 11 | #include 12 | 13 | #define GDB_LOOP \ 14 | __asm__ __volatile__(".globl gdb_loop\n" \ 15 | "gdb_loop:\n" \ 16 | "xchg %bx, %bx\n" \ 17 | "jmp gdb_loop\n") 18 | 19 | #endif // _DEBUG_H_ 20 | -------------------------------------------------------------------------------- /src/x86/executor/include/actor.h: -------------------------------------------------------------------------------- 1 | /// File: Header describing actor metadata 2 | /// 3 | // Copyright (C) Microsoft Corporation 4 | // SPDX-License-Identifier: MIT 5 | 6 | #ifndef _ACTOR_H_ 7 | #define _ACTOR_H_ 8 | 9 | #include 10 | 11 | #define MAX_ACTORS 16 12 | 13 | typedef uint64_t actor_id_t; 14 | typedef uint64_t actor_mode_t; 15 | typedef uint64_t actor_pl_t; 16 | 17 | enum { 18 | MODE_HOST = 0, 19 | MODE_GUEST = 1, 20 | }; 21 | 22 | enum { 23 | PL_KERNEL = 0, 24 | PL_USER = 1, 25 | }; 26 | 27 | typedef struct { 28 | actor_id_t id; 29 | actor_mode_t mode; 30 | actor_pl_t pl; 31 | uint64_t data_permissions; 32 | uint64_t data_ept_properties; 33 | uint64_t code_permissions; 34 | } actor_metadata_t; 35 | 36 | extern size_t n_actors; 37 | extern actor_metadata_t *actors; 38 | 39 | #endif // _ACTOR_H_ 40 | -------------------------------------------------------------------------------- /src/x86/executor/include/special_registers.h: -------------------------------------------------------------------------------- 1 | /// File: Header for msr.c 2 | /// 3 | // Copyright (C) Microsoft Corporation 4 | // SPDX-License-Identifier: MIT 5 | 6 | #ifndef _MSR_H_ 7 | #define _MSR_H_ 8 | 9 | #include 10 | #include "hardware_desc.h" 11 | 12 | typedef struct { 13 | uint64_t cr0; 14 | uint64_t cr4; 15 | uint64_t efer; 16 | uint64_t lstar; 17 | uint64_t spec_ctrl; 18 | uint64_t prefetcher_ctrl; 19 | uint64_t mpx_ctrl; 20 | uint64_t syscfg; 21 | uint64_t gs_base; 22 | } __attribute__((packed)) special_registers_t; 23 | 24 | extern special_registers_t *orig_special_registers_state; 25 | 26 | int set_special_registers(void); 27 | void restore_special_registers(void); 28 | 29 | int init_special_register_manager(void); 30 | void free_special_register_manager(void); 31 | 32 | 33 | #endif // _MSR_H_ 34 | -------------------------------------------------------------------------------- /tests/x86_tests/asm/spectre_ret.asm: -------------------------------------------------------------------------------- 1 | .intel_syntax noprefix 2 | .test_case_enter: 3 | .section .data.main 4 | 5 | # speculative offset: 6 | # these shifts generate a random page offset, 64-bit aligned 7 | and rax, 0b111111000000 8 | lfence 9 | 10 | mov rcx, r14 11 | add rsp, 8 # ensure that the call and ret use the first cache set 12 | 13 | call .function_1 14 | 15 | .unreachable: 16 | // lfence # if you uncomment this line, the speculation will stop 17 | and rax, 0b110000000 # reduce the number of possibilities 18 | mov rax, qword ptr [rcx + rax] # speculative access 19 | lfence 20 | 21 | .function_1: 22 | lea rdx, qword ptr [rip + .function_2] 23 | mov qword ptr [rsp], rdx 24 | ret 25 | 26 | .function_2: 27 | mov rdx, qword ptr [rcx + 64] 28 | mfence 29 | 30 | # clear to avoid failing the arch check 31 | mov rcx, 0 32 | mov rdx, 0 33 | 34 | .test_case_exit: 35 | -------------------------------------------------------------------------------- /tests/x86_tests/asm/vm_switch.asm: -------------------------------------------------------------------------------- 1 | .intel_syntax noprefix 2 | .test_case_enter: 3 | 4 | # ----------------------------- actor 1 ------------------------------------------------------------ 5 | .section .data.main 6 | .function_main1: 7 | 8 | .function_main2: 9 | .macro.set_h2g_target.actor2.function_a2: 10 | .macro.set_g2h_target.main.function_fin: 11 | .macro.switch_h2g.actor2: 12 | 13 | .function_fin: 14 | .macro.landing_g2h: 15 | nop 16 | 17 | # ----------------------------- actor 2 ------------------------------------------------------------ 18 | .section .data.actor2 19 | .function_a2: 20 | .macro.landing_h2g: 21 | .macro.measurement_start: 22 | .macro.measurement_end: 23 | 24 | .macro.switch_g2h.main: 25 | 26 | # ----------------------------- exit ------------------------------------------------------------ 27 | .test_case_exit: 28 | -------------------------------------------------------------------------------- /tests/x86_tests/asm/spectre_v1.asm: -------------------------------------------------------------------------------- 1 | .intel_syntax noprefix 2 | .test_case_enter: 3 | .section .data.main 4 | lfence 5 | 6 | # reduce the entropy of rax 7 | and rax, 0b111111000000 8 | 9 | # delay the cond. jump 10 | lea rbx, qword ptr [rbx + rax + 1] 11 | lea rbx, qword ptr [rbx + rax + 1] 12 | lea rbx, qword ptr [rbx + rax + 1] 13 | lea rbx, qword ptr [rbx + rax + 1] 14 | lea rbx, qword ptr [rbx + rax + 1] 15 | lea rbx, qword ptr [rbx + rax + 1] 16 | lea rbx, qword ptr [rbx + rax + 1] 17 | lea rbx, qword ptr [rbx + rax + 1] 18 | lea rbx, qword ptr [rbx + rax + 1] 19 | lea rbx, qword ptr [rbx + rax + 1] 20 | 21 | # reduce the entropy in rbx 22 | and rbx, 0b1000000 23 | 24 | cmp rbx, 0 25 | je .l1 # misprediction 26 | .l0: 27 | # rbx != 0 28 | mov rax, qword ptr [r14 + rax] 29 | jmp .l2 30 | .l1: 31 | # rbx == 0 32 | #mov rax, qword ptr [r14 + 64] 33 | .l2: 34 | mfence 35 | 36 | .test_case_exit: 37 | -------------------------------------------------------------------------------- /demo/detecting-v1.yaml: -------------------------------------------------------------------------------- 1 | # This demo illustrates detection of a Spectre V1 2 | # 3 | # 4 | # Run it with: ./cli.py fuzz -s x86/isa_spec/base.json -n 1000 -i 100 -c ../demo/detecting-v1.yaml 5 | 6 | # contract 7 | contract_observation_clause: loads+stores+pc 8 | contract_execution_clause: 9 | - no_speculation 10 | 11 | # tested instructions 12 | instruction_categories: 13 | - BASE-BINARY 14 | - BASE-BITBYTE 15 | - BASE-CMOV 16 | - BASE-COND_BR 17 | - BASE-CONVERT 18 | - BASE-DATAXFER 19 | - BASE-LOGICAL 20 | - BASE-MISC 21 | - BASE-NOP 22 | - BASE-POP 23 | - BASE-PUSH 24 | - BASE-SETCC 25 | 26 | # fuzzing configuration 27 | enable_speculation_filter: true 28 | enable_observation_filter: true 29 | enable_priming: false 30 | input_gen_entropy_bits: 16 31 | min_bb_per_function: 2 32 | max_bb_per_function: 2 33 | program_size: 16 34 | avg_mem_accesses: 8 35 | inputs_per_class: 2 36 | executor_warmups: 2 37 | x86_disable_div64: false 38 | -------------------------------------------------------------------------------- /demo/detecting-foreshadow.yaml: -------------------------------------------------------------------------------- 1 | # This demo illustrates detection of Foreshadow 2 | # 3 | # 4 | # 5 | 6 | # contract 7 | contract_observation_clause: loads+stores+pc 8 | contract_execution_clause: 9 | - delayed-exception-handling 10 | 11 | # tested instructions 12 | instruction_categories: 13 | - BASE-BINARY 14 | - BASE-BITBYTE 15 | - BASE-CMOV 16 | - BASE-CONVERT 17 | - BASE-DATAXFER 18 | - BASE-LOGICAL 19 | - BASE-MISC 20 | - BASE-NOP 21 | - BASE-POP 22 | - BASE-PUSH 23 | - BASE-SETCC 24 | 25 | instruction_blocklist: 26 | - DIV 27 | - IDIV 28 | 29 | actors: 30 | - main: 31 | - data_properties: 32 | - present: false 33 | - writable: false 34 | 35 | # misc. fuzzing configuration 36 | enable_speculation_filter: true 37 | enable_observation_filter: true 38 | enable_priming: false 39 | input_gen_entropy_bits: 16 40 | min_bb_per_function: 1 41 | max_bb_per_function: 1 42 | program_size: 16 43 | avg_mem_accesses: 8 44 | inputs_per_class: 2 45 | executor_warmups: 2 46 | x86_disable_div64: false 47 | -------------------------------------------------------------------------------- /tests/x86_tests/asm/model_match_memory.asm: -------------------------------------------------------------------------------- 1 | .intel_syntax noprefix 2 | .test_case_enter: 3 | .section .data.main 4 | 5 | mov rax, qword ptr [r14] # main page 6 | add rax, qword ptr [r14 + 4096] # faulty page 7 | 8 | mov rbx, qword ptr [r14 - 8] # underflow pad 9 | add rbx, qword ptr [r14 + 4096 + 4096 + 320] # overflow pad 10 | 11 | mov rcx, qword ptr [r14 + 4096 + 4096] # reg init 12 | mov rdx, qword ptr [r14 + 4096 + 4096 + 48] # patched flags 13 | mov rsi, qword ptr [r14 + 4096 + 4096 + 64] # simd init 14 | mov rdi, r14 15 | 16 | 17 | # uncomment the following to test the complete sandbox contents 18 | # xor rax, rax 19 | # xor rbx, rbx 20 | # xor rcx, rcx 21 | # xor rdx, rcx 22 | # mov rdi, 0xff8 23 | # .l1: 24 | # add rax, qword ptr [r14 + rdi] 25 | # add rbx, qword ptr [r14 + rdi + 0x1000] 26 | # add rcx, qword ptr [r14 + rdi + 0x2000] 27 | # sub rdi, 8 28 | # jnz .l1 29 | # .l1_exit: 30 | 31 | # mov rdi, 0xef8 32 | # .l2: 33 | # add rdx, qword ptr [r14 + rdi - 0xf00] 34 | # sub rdi, 8 35 | # jnz .l2 36 | 37 | .test_case_exit: 38 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # https://editorconfig.org/ 2 | 3 | root = true 4 | 5 | [*] 6 | indent_style = space 7 | indent_size = 4 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | end_of_line = lf 11 | charset = utf-8 12 | max_line_length = 100 13 | 14 | [*.json] 15 | indent_size = 2 16 | keep_blank_lines_in_code = 0 17 | keep_indents_on_empty_lines = false 18 | keep_line_breaks = true 19 | space_after_colon = true 20 | space_after_comma = true 21 | space_before_colon = true 22 | space_before_comma = false 23 | spaces_within_braces = false 24 | spaces_within_brackets = false 25 | wrap_long_lines = false 26 | insert_final_newline = ignore 27 | 28 | [Makefile] 29 | indent_style = tab 30 | 31 | [{*.bash,*.zsh,*.sh,*.bats}] 32 | tab_width = 4 33 | binary_ops_start_line = false 34 | keep_column_alignment_padding = false 35 | minify_program = false 36 | redirect_followed_by_space = false 37 | switch_cases_indented = false 38 | 39 | [{*.yml,*.yaml}] 40 | indent_size = 2 41 | keep_indents_on_empty_lines = false 42 | keep_line_breaks = true 43 | -------------------------------------------------------------------------------- /src/x86/executor/include/host_page_tables.h: -------------------------------------------------------------------------------- 1 | /// File: Header for page table functions 2 | /// 3 | // Copyright (C) Microsoft Corporation 4 | // SPDX-License-Identifier: MIT 5 | 6 | #ifndef _PAGE_TABLE_H_ 7 | #define _PAGE_TABLE_H_ 8 | 9 | #include "page_tables_common.h" 10 | #include 11 | 12 | typedef struct { 13 | pte_t_ *data_ptes; 14 | pte_t_ *code_ptes; 15 | pte_t_ *util_ptes; 16 | } sandbox_ptes_t; 17 | 18 | typedef struct { 19 | pte_t_ **data_pteps; 20 | pte_t_ **code_pteps; 21 | pte_t_ **util_pteps; 22 | } sandbox_pteps_t; 23 | 24 | extern sandbox_pteps_t *sandbox_pteps; 25 | 26 | pte_t *get_pte(uint64_t address); 27 | 28 | int cache_host_pteps(void); 29 | int store_orig_host_permissions(void); 30 | int restore_orig_host_permissions(void); 31 | 32 | int set_user_pages(void); 33 | void set_faulty_page_host_permissions(void); 34 | void restore_faulty_page_host_permissions(void); 35 | 36 | int init_page_table_manager(void); 37 | void free_page_table_manager(void); 38 | 39 | #endif // _PAGE_TABLE_H_ 40 | -------------------------------------------------------------------------------- /tests/x86_tests/asm/model_flags_match.asm: -------------------------------------------------------------------------------- 1 | .intel_syntax noprefix 2 | .test_case_enter: 3 | .section .data.main 4 | lfence 5 | mov rax, r14 6 | 7 | mov rbx, 0 8 | mov rcx, 64 9 | cmovb rbx, rcx 10 | mov rcx, qword ptr [rax + rbx] 11 | 12 | mov rbx, 0 13 | mov rcx, 128 14 | cmovbe rbx, rcx 15 | mov rcx, qword ptr [rax + rbx] 16 | 17 | mov rbx, 0 18 | mov rcx, 192 19 | cmovl rbx, rcx 20 | mov rcx, qword ptr [rax + rbx] 21 | 22 | mov rbx, 0 23 | mov rcx, 256 24 | cmovle rbx, rcx 25 | mov rcx, qword ptr [rax + rbx] 26 | 27 | mov rbx, 0 28 | mov rcx, 320 29 | cmovo rbx, rcx 30 | mov rcx, qword ptr [rax + rbx] 31 | 32 | mov rbx, 0 33 | mov rcx, 384 34 | cmovp rbx, rcx 35 | mov rcx, qword ptr [rax + rbx] 36 | 37 | mov rbx, 0 38 | mov rcx, 448 39 | cmovs rbx, rcx 40 | mov rcx, qword ptr [rax + rbx] 41 | 42 | mov rbx, 0 43 | mov rcx, 512 44 | cmovz rbx, rcx 45 | mov rcx, qword ptr [rax + rbx] 46 | 47 | // cmovnb 48 | // cmovnbe 49 | // cmovnl 50 | // cmovnle 51 | // cmovno 52 | // cmovnp 53 | // cmovns 54 | // cmovnz 55 | mfence 56 | .test_case_exit: 57 | -------------------------------------------------------------------------------- /tests/x86_tests/asm/spectre_v1_arch.asm: -------------------------------------------------------------------------------- 1 | .intel_syntax noprefix 2 | .test_case_enter: 3 | .section .data.main 4 | lfence 5 | 6 | # delay the cond. jump 7 | mov rax, 0 8 | lea rbx, qword ptr [rbx + rax + 1] 9 | lea rbx, qword ptr [rbx + rax - 1] 10 | lea rbx, qword ptr [rbx + rax + 1] 11 | lea rbx, qword ptr [rbx + rax - 1] 12 | lea rbx, qword ptr [rbx + rax + 1] 13 | lea rbx, qword ptr [rbx + rax - 1] 14 | lea rbx, qword ptr [rbx + rax + 1] 15 | lea rbx, qword ptr [rbx + rax - 1] 16 | lea rbx, qword ptr [rbx + rax + 1] 17 | lea rbx, qword ptr [rbx + rax - 1] 18 | lea rbx, qword ptr [rbx + rax + 1] 19 | lea rbx, qword ptr [rbx + rax - 1] 20 | lea rbx, qword ptr [rbx + rax + 1] 21 | lea rbx, qword ptr [rbx + rax - 1] 22 | 23 | # reduce the entropy in rbx 24 | and rbx, 0b1000000 25 | 26 | cmp rbx, 0 27 | je .l1 # misprediction 28 | .l0: 29 | # rbx != 0 30 | mov rax, qword ptr [r14 + 1024] 31 | shl rax, 2 32 | and rax, 0b111111000000 33 | mov rax, qword ptr [r14 + rax] # leakage happens here 34 | .l1: 35 | 36 | mfence 37 | 38 | .test_case_exit: 39 | -------------------------------------------------------------------------------- /docs/trophies.md: -------------------------------------------------------------------------------- 1 | # Trophies 2 | 3 | ## Newly discovered vulnerabilities 4 | 5 | ### String Comparison Overrun (SCO) 6 | 7 | The page is under construction 8 | 9 | ### Zero Dividend Injection (ZDI) 10 | 11 | The page is under construction 12 | 13 | ### Spectre V1-Var and V4-Var 14 | 15 | The page is under construction 16 | 17 | ### Store-based Spectre V1 18 | 19 | The page is under construction 20 | 21 | ### Speculative Store with Forwarding 22 | 23 | The page is under construction 24 | 25 | 26 | ## Known vulnerabilities automatically reproduced by Revizor 27 | 28 | ### Spectre V1 (Bounds Check Bypass, BCB) 29 | 30 | The page is under construction 31 | 32 | ### Spectre V4 (Speculative Store Bypass, SSBP) 33 | 34 | The page is under construction 35 | 36 | ### Meltdown (SMAP variant) 37 | 38 | The page is under construction 39 | 40 | ### Foreshadow (L1TF) 41 | 42 | The page is under construction 43 | 44 | ### Microarchitectural Data Sampling (MDS) 45 | 46 | The page is under construction 47 | 48 | ### Load Value Injection (LVI), including LVI-Null 49 | 50 | The page is under construction 51 | 52 | -------------------------------------------------------------------------------- /demo/detecting-zdo.yaml: -------------------------------------------------------------------------------- 1 | # This demo illustrates detection of a previously-unknown vulnerability 2 | # Zero Division Injection, described 3 | # in [Hide&Seek with Spectres](https://arxiv.org/abs/2301.07642) 4 | # Run it with: ./cli.py fuzz -s x86/isa_spec/base.json -n 1000 -i 100 -c ../demo/detecting-zdo.yaml 5 | 6 | # contract 7 | contract_observation_clause: loads+stores+pc 8 | contract_execution_clause: 9 | - conditional_br_misprediction 10 | 11 | # tested instructions 12 | instruction_categories: 13 | - BASE-BINARY 14 | - BASE-BITBYTE 15 | - BASE-CMOV 16 | - BASE-COND_BR 17 | - BASE-CONVERT 18 | - BASE-DATAXFER 19 | - BASE-LOGICAL 20 | - BASE-MISC 21 | - BASE-NOP 22 | - BASE-POP 23 | - BASE-PUSH 24 | - BASE-SEMAPHORE 25 | - BASE-SETCC 26 | 27 | # fuzzing configuration 28 | enable_speculation_filter: true 29 | enable_observation_filter: true 30 | input_gen_entropy_bits: 16 31 | min_bb_per_function: 1 32 | max_bb_per_function: 1 33 | program_size: 64 34 | avg_mem_accesses: 24 35 | inputs_per_class: 3 36 | executor_warmups: 2 37 | executor_threshold_outliers: 0 38 | x86_disable_div64: false 39 | -------------------------------------------------------------------------------- /tests/x86_tests/asm/asm_basic.asm: -------------------------------------------------------------------------------- 1 | .intel_syntax noprefix 2 | .test_case_enter: 3 | .section .data.main 4 | 5 | .function_0: 6 | .bb_0: 7 | 8 | # line with a comment 9 | nop # no operands 10 | div rbx # one operand 11 | and rax, rax # two operands 12 | and rax, 0b0111111000000 # immediate value - binary 13 | and rax, 42 # immediate value - decimal 14 | and rax, 0xfa # immediate value - hex 15 | and rax, -1 # immediate value - negative 16 | and rdi, r14 # reserved register 17 | neg rax # lowercase 18 | mov rax, qword ptr [r14] # load - simple addressing 19 | mov rax, qword ptr [r14 + rbx] # load - two parts 20 | mov rax, qword ptr [r14 + rbx + 8] # load - three parts 21 | mov rax, qword ptr [r14 + rbx] # store 22 | lock adc dword ptr [r14 + rbx], eax # lock prefix 23 | and rax, rax # instrumentation 24 | 25 | mov rdi, rdi # multiple matches 26 | 27 | 28 | jmp .bb_1 29 | .bb_1: 30 | and rdi, 0b0111111000000 # indentation 31 | cmp qword ptr [ r14 + rdi ] , 59 # extra spaces 32 | and rdi, 0b0111111000000 # instrumentation 33 | cmpxchg byte ptr [r14 + rsi], sil 34 | 35 | .test_case_exit: 36 | -------------------------------------------------------------------------------- /src/x86/executor/include/measurement.h: -------------------------------------------------------------------------------- 1 | /// File: Header for the measurement manager 2 | /// 3 | // Copyright (C) Microsoft Corporation 4 | // SPDX-License-Identifier: MIT 5 | 6 | #ifndef _MEASUREMENT_H_ 7 | #define _MEASUREMENT_H_ 8 | 9 | #include 10 | #include 11 | 12 | #define HTRACE_WIDTH 1 13 | #define NUM_PFC 5 14 | 15 | #define STATUS_UNINITIALIZED 0 16 | #define STATUS_STARTED 1 17 | #define STATUS_ENDED 2 18 | 19 | typedef struct measurement_status { 20 | uint8_t measurement_state; 21 | uint8_t reserved[3]; 22 | uint32_t smi_count; 23 | } __attribute__((packed)) measurement_status_t; 24 | 25 | typedef struct Measurement { 26 | uint64_t htrace[HTRACE_WIDTH]; 27 | uint64_t pfc_reading[NUM_PFC]; 28 | measurement_status_t status; 29 | } __attribute__((packed)) measurement_t; 30 | 31 | extern measurement_t *measurements; 32 | 33 | int trace_test_case(void); 34 | int run_experiment(void); 35 | 36 | void recover_orig_state(void); 37 | 38 | int alloc_measurements(void); 39 | int init_measurements(void); 40 | void free_measurements(void); 41 | 42 | #endif // _MEASUREMENT_H_ 43 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) Microsoft Corporation. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE 22 | -------------------------------------------------------------------------------- /src/x86/executor/include/hardware_desc.h: -------------------------------------------------------------------------------- 1 | /// File: Header for hardware configuration 2 | /// 3 | // Copyright (C) Microsoft Corporation 4 | // SPDX-License-Identifier: MIT 5 | 6 | #ifndef _HARDWARE_DESC_H_ 7 | #define _HARDWARE_DESC_H_ 8 | 9 | #ifndef VENDOR_ID 10 | #error "Undefined VENDOR_ID" 11 | #define VENDOR_ID 0 12 | #endif 13 | 14 | #define VENDOR_INTEL_ 1 15 | #define VENDOR_AMD_ 2 16 | #undef VENDOR_INTEL 17 | #undef VENDOR_AMD 18 | 19 | // Memory 20 | #ifndef PHYSICAL_WIDTH 21 | #define PHYSICAL_WIDTH 51 // unused in the build; used only for syntax highlighting 22 | #error "PHYSICAL_WIDTH must be defined by the makefile" 23 | #endif 24 | 25 | #define MAX_PHYSICAL_ADDRESS ((1ULL << PHYSICAL_WIDTH) - 1) 26 | 27 | // Cache configuration 28 | #ifndef L1D_ASSOCIATIVITY 29 | #error "Undefined L1D_ASSOCIATIVITY" 30 | #define L1D_ASSOCIATIVITY 0 31 | #elif L1D_ASSOCIATIVITY != 12 && L1D_ASSOCIATIVITY != 8 && L1D_ASSOCIATIVITY != 4 && \ 32 | L1D_ASSOCIATIVITY != 2 33 | #warning "Unsupported/corrupted L1D associativity. Falling back to 8-way" 34 | #define L1D_ASSOCIATIVITY 8 35 | #endif 36 | 37 | // Definitions of MSRs missing in the kernel 38 | #define MSR_SYSCFG 0xc0010010 39 | 40 | #endif // _HARDWARE_DESC_H_ 41 | -------------------------------------------------------------------------------- /src/x86/executor/include/fault_handler.h: -------------------------------------------------------------------------------- 1 | /// File: Header for fault handling 2 | /// 3 | // Copyright (C) Microsoft Corporation 4 | // SPDX-License-Identifier: MIT 5 | 6 | #ifndef _FAULT_HANDLER_H_ 7 | #define _FAULT_HANDLER_H_ 8 | 9 | #include <../arch/x86/include/asm/traps.h> 10 | #include 11 | #include 12 | 13 | #if LINUX_VERSION_CODE < KERNEL_VERSION(5, 10, 0) 14 | struct idt_data { 15 | unsigned int vector; 16 | unsigned int segment; 17 | struct idt_bits bits; 18 | const void *addr; 19 | }; 20 | #endif 21 | 22 | #define HANDLED_FAULTS_DEFAULT \ 23 | ((1 << X86_TRAP_DE) + (1 << X86_TRAP_DB) + (1 << X86_TRAP_BP) + (1 << X86_TRAP_BR) + \ 24 | (1 << X86_TRAP_UD) + (1 << X86_TRAP_GP) + (1 << X86_TRAP_PF) + (1 << X86_TRAP_AC)) 25 | 26 | extern char *fault_handler; 27 | extern uint32_t handled_faults; 28 | extern uint64_t pre_bubble_rsp; 29 | extern struct desc_ptr test_case_idtr; 30 | 31 | int set_bubble_idt(void); 32 | int unset_bubble_idt(void); 33 | int set_test_case_idt(void); 34 | int unset_test_case_idt(void); 35 | 36 | int init_fault_handler(void); 37 | void free_fault_handler(void); 38 | 39 | #endif // _FAULT_HANDLER_H_ 40 | -------------------------------------------------------------------------------- /src/x86/executor/include/macro_loader.h: -------------------------------------------------------------------------------- 1 | /// File: Header for test case macros 2 | /// 3 | // Copyright (C) Microsoft Corporation 4 | // SPDX-License-Identifier: MIT 5 | 6 | #ifndef _MACRO_H_ 7 | #define _MACRO_H_ 8 | 9 | #include "test_case_parser.h" 10 | #include "asm_snippets.h" 11 | #include 12 | 13 | typedef enum { 14 | NONMACRO_FUNCTION = 0, 15 | MACRO_MEASUREMENT_START = 1, 16 | MACRO_MEASUREMENT_END = 2, 17 | MACRO_FAULT_HANDLER = 3, 18 | MACRO_SWITCH = 4, 19 | MACRO_SET_K2U_TARGET = 5, 20 | MACRO_SWITCH_K2U = 6, 21 | MACRO_SET_U2K_TARGET = 7, 22 | MACRO_SWITCH_U2K = 8, 23 | MACRO_SET_H2G_TARGET = 9, 24 | MACRO_SWITCH_H2G = 10, 25 | MACRO_SET_G2H_TARGET = 11, 26 | MACRO_SWITCH_G2H = 12, 27 | MACRO_LANDING_K2U = 13, 28 | MACRO_LANDING_U2K = 14, 29 | MACRO_LANDING_H2G = 15, 30 | MACRO_LANDING_G2H = 16, 31 | MACRO_FAULT_HANDLER_WITH_MEASUREMENT = 17, 32 | MACRO_SET_DATA_PERMISSIONS = 18, 33 | } macro_name_e; 34 | 35 | #define MACRO_PLACEHOLDER_SIZE 8 36 | 37 | int expand_macro(tc_symbol_entry_t *macro, uint8_t *dest, uint8_t *macro_dest, size_t *macro_size); 38 | void set_main_prologue_size(size_t size); 39 | 40 | int init_macros_loader(void); 41 | void free_macros_loader(void); 42 | 43 | #endif // _MACRO_H_ 44 | -------------------------------------------------------------------------------- /tests/x86_tests/asm/spectre_v1_n2.asm: -------------------------------------------------------------------------------- 1 | .intel_syntax noprefix 2 | .test_case_enter: 3 | .section .data.main 4 | lfence 5 | 6 | # reduce the entropy of rax 7 | and rax, 0b111111000000 8 | 9 | # delay the cond. jump 10 | lea rbx, qword ptr [rbx + rax + 1] 11 | lea rbx, qword ptr [rbx + rax - 1] 12 | lea rbx, qword ptr [rbx + rax + 1] 13 | lea rbx, qword ptr [rbx + rax - 1] 14 | lea rbx, qword ptr [rbx + rax + 1] 15 | lea rbx, qword ptr [rbx + rax - 1] 16 | lea rbx, qword ptr [rbx + rax + 1] 17 | lea rbx, qword ptr [rbx + rax - 1] 18 | lea rbx, qword ptr [rbx + rax + 1] 19 | lea rbx, qword ptr [rbx + rax - 1] 20 | lea rbx, qword ptr [rbx + rax + 1] 21 | lea rbx, qword ptr [rbx + rax - 1] 22 | lea rbx, qword ptr [rbx + rax + 1] 23 | lea rbx, qword ptr [rbx + rax - 1] 24 | lea rbx, qword ptr [rbx + rax + 1] 25 | lea rbx, qword ptr [rbx + rax - 1] 26 | lea rbx, qword ptr [rbx + rax + 1] 27 | lea rbx, qword ptr [rbx + rax - 1] 28 | lea rbx, qword ptr [rbx + rax + 1] 29 | lea rbx, qword ptr [rbx + rax - 1] 30 | 31 | # reduce the entropy in rbx 32 | and rbx, 0b1000000 33 | 34 | cmp rbx, 0 35 | je .l1 # misprediction 36 | je .l1 37 | .l0: 38 | # rbx != 0 39 | mov rax, qword ptr [r14 + rax] 40 | jmp .l2 41 | .l1: 42 | # rbx == 0 43 | #mov rax, qword ptr [r14 + 64] 44 | .l2: 45 | mfence 46 | 47 | .test_case_exit: 48 | -------------------------------------------------------------------------------- /tests/x86_tests/asm/direct_jumps.asm: -------------------------------------------------------------------------------- 1 | .intel_syntax noprefix 2 | .test_case_enter: 3 | .section .data.main 4 | jmp .1 5 | .1: 6 | jmp .2 7 | .2: 8 | jmp .3 9 | .3: 10 | jmp .4 11 | .4: 12 | jmp .5 13 | .5: 14 | jmp .6 15 | .6: 16 | jmp .7 17 | .7: 18 | jmp .8 19 | .8: 20 | jmp .9 21 | .9: 22 | jmp .10 23 | .10: 24 | jmp .11 25 | .11: 26 | jmp .12 27 | .12: 28 | jmp .13 29 | .13: 30 | jmp .14 31 | .14: 32 | jmp .15 33 | .15: 34 | jmp .16 35 | .16: 36 | jmp .17 37 | .17: 38 | jmp .18 39 | .18: 40 | jmp .19 41 | .19: 42 | jmp .20 43 | .20: 44 | jmp .21 45 | .21: 46 | jmp .22 47 | .22: 48 | jmp .23 49 | .23: 50 | jmp .24 51 | .24: 52 | jmp .25 53 | .25: 54 | jmp .26 55 | .26: 56 | jmp .27 57 | .27: 58 | jmp .28 59 | .28: 60 | jmp .29 61 | .29: 62 | jmp .30 63 | .30: 64 | jmp .31 65 | .31: 66 | jmp .32 67 | .32: 68 | jmp .33 69 | .33: 70 | jmp .34 71 | .34: 72 | jmp .35 73 | .35: 74 | jmp .36 75 | .36: 76 | jmp .37 77 | .37: 78 | jmp .38 79 | .38: 80 | jmp .39 81 | .39: 82 | jmp .40 83 | .40: 84 | jmp .41 85 | .41: 86 | jmp .42 87 | .42: 88 | jmp .43 89 | .43: 90 | jmp .44 91 | .44: 92 | jmp .45 93 | .45: 94 | jmp .46 95 | .46: 96 | jmp .47 97 | .47: 98 | jmp .48 99 | .48: 100 | jmp .49 101 | .49: 102 | jmp .50 103 | .50: 104 | .test_case_exit: 105 | -------------------------------------------------------------------------------- /demo/tsa-sq/config.yaml: -------------------------------------------------------------------------------- 1 | instruction_set: x86-64 2 | instruction_categories: 3 | - BASE-BINARY 4 | - BASE-BITBYTE 5 | - BASE-CMOV 6 | - BASE-COND_BR 7 | - BASE-CONVERT 8 | - BASE-DATAXFER 9 | - BASE-FLAGOP 10 | - BASE-LOGICAL 11 | - BASE-MISC 12 | - BASE-NOP 13 | - BASE-POP 14 | - BASE-PUSH 15 | - BASE-SEMAPHORE 16 | - BASE-SETCC 17 | - BASE-WIDENOP 18 | 19 | 20 | generator_faults_allowlist: 21 | - user-to-kernel-access 22 | 23 | actors: 24 | - main: 25 | - mode: "host" 26 | - privilege_level: "kernel" 27 | - fault_blocklist: 28 | - user-to-kernel-access 29 | - user: 30 | - mode: "host" 31 | - observer: true 32 | - privilege_level: "user" 33 | - data_properties: 34 | - present: true 35 | 36 | contract_observation_clause: ct 37 | contract_execution_clause: 38 | - noninterference 39 | 40 | max_bb_per_function: 1 41 | 42 | executor_mode: F+R 43 | executor_sample_sizes: 44 | - 15 45 | - 40 46 | - 160 47 | - 320 48 | 49 | executor_filtering_repetitions: 5 50 | x86_enable_hpa_gpa_collisions: true 51 | 52 | program_generator_seed: 20000000 53 | input_gen_seed: 1000000 54 | inputs_per_class: 2 55 | 56 | analyser_stat_threshold: 0.05 57 | 58 | # enable_speculation_filter: true 59 | enable_observation_filter: true 60 | enable_fast_path_model: true 61 | 62 | # color: true 63 | logging_modes: 64 | - info 65 | # - stat 66 | -------------------------------------------------------------------------------- /tests/quick-test.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | function assert_violation() { 4 | local cmd="$@" 5 | log=$(mktemp) 6 | 7 | bash -c "$cmd" > $log 8 | status=$? 9 | output=$(cat $log) 10 | if [[ "$status" -eq 1 && "$output" = *"=== Violations detected ==="* ]]; then 11 | echo "Detection: OK" 12 | else 13 | echo "Detection: FAIL" 14 | echo "Command: $cmd" 15 | echo "Exit code: $status" 16 | echo "Output: '$output'" 17 | exit 1 18 | fi 19 | } 20 | 21 | function assert_no_violation() { 22 | local cmd="$@" 23 | 24 | log=$(mktemp) 25 | 26 | bash -c "$cmd" > $log 27 | status=$? 28 | output=$(cat $log) 29 | if [[ "$status" -eq 0 && "$output" != *"=== Violations detected ==="* ]]; then 30 | echo "Filtering: OK" 31 | else 32 | echo "Filtering: FAIL" 33 | echo "Command: $cmd" 34 | echo "Exit code: $status" 35 | echo "Output: '$output'" 36 | exit 1 37 | fi 38 | } 39 | 40 | SCRIPT_DIR=$(dirname $(realpath $0)) 41 | 42 | cmd="rvzr $cli fuzz -s $SCRIPT_DIR/../base.json --save-violations f -I $SCRIPT_DIR/x86_tests/configs -t $SCRIPT_DIR/x86_tests/asm/spectre_v1.asm -c $SCRIPT_DIR/x86_tests/configs/ct-seq.yaml -i 20" 43 | assert_violation "$cmd" 44 | 45 | cmd="rvzr $cli fuzz -s $SCRIPT_DIR/../base.json --save-violations f -I $SCRIPT_DIR/x86_tests/configs -t $SCRIPT_DIR/x86_tests/asm/spectre_v1.asm -c $SCRIPT_DIR/x86_tests/configs/ct-cond.yaml -i 20" 46 | assert_no_violation "$cmd" 47 | -------------------------------------------------------------------------------- /tests/x86_tests/asm/actor_switch.asm: -------------------------------------------------------------------------------- 1 | .intel_syntax noprefix 2 | .test_case_enter: 3 | .section .data.main 4 | 5 | .function_start: 6 | # delay on rbx 7 | lea rbx, qword ptr [rbx + rcx + 1] 8 | lea rbx, qword ptr [rbx + rcx + 1] 9 | lea rbx, qword ptr [rbx + rcx + 1] 10 | lea rbx, qword ptr [rbx + rcx + 1] 11 | lea rbx, qword ptr [rbx + rcx + 1] 12 | lea rbx, qword ptr [rbx + rcx + 1] 13 | lea rbx, qword ptr [rbx + rcx + 1] 14 | lea rbx, qword ptr [rbx + rcx + 1] 15 | lea rbx, qword ptr [rbx + rcx + 1] 16 | lea rbx, qword ptr [rbx + rcx + 1] 17 | and rbx, 0b1 18 | 19 | .macro.switch.actor2.function_1: 20 | # end of function_start 21 | # -------------------------------------------------------------------------------------------------- 22 | 23 | .function_fin: 24 | .bb0: 25 | nop 26 | # end of function_fin 27 | # -------------------------------------------------------------------------------------------------- 28 | 29 | .section .data.actor2 30 | .function_1: 31 | # a typical spectre v1 gadget 32 | jz .l3 33 | .l1: 34 | # mask the memory access 35 | and rax, 0b111111000000 36 | mov rax, qword ptr [r14 + rax] 37 | jmp .l3 38 | .l2: 39 | # mov rax, qword ptr [r14 + 0x100] 40 | .l3: 41 | 42 | and rdx, 0b111111000000 43 | mov rax, qword ptr [r14 + rdx] 44 | mov rsi, 0x42 45 | 46 | .macro.switch.main.function_fin: 47 | # end of function_1 48 | # -------------------------------------------------------------------------------------------------- 49 | 50 | .test_case_exit: 51 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["hatchling"] 3 | build-backend = "hatchling.build" 4 | 5 | [project] 6 | name = "revizor-fuzzer" 7 | version = "1.3.2" 8 | description = "A fuzzer to search for microarchitectural leaks in CPUs" 9 | readme = "README.md" 10 | requires-python = ">=3.9" 11 | classifiers = [ 12 | "Programming Language :: Python :: 3", 13 | "Programming Language :: Python :: 3 :: Only", 14 | "License :: OSI Approved :: MIT License", 15 | "Operating System :: POSIX :: Linux", 16 | "Typing :: Typed", 17 | ] 18 | dependencies = [ 19 | "unicorn==1.0.3", 20 | "pyyaml", 21 | "types-pyyaml", 22 | "numpy", 23 | "pyelftools", 24 | "xxhash", 25 | "scipy", 26 | "mypy", 27 | "flake8", 28 | "setuptools" 29 | ] 30 | maintainers = [{name = "Oleksii Oleksenko", email = ""}] 31 | 32 | [project.urls] 33 | "Homepage" = "https://microsoft.github.io/sca-fuzzer/" 34 | "Source code" = "https://github.com/microsoft/sca-fuzzer" 35 | "Bug Tracker" = "https://github.com/microsoft/sca-fuzzer/issues" 36 | "Changelog" = "https://github.com/microsoft/sca-fuzzer/releases" 37 | 38 | [tool.hatch.build.sources] 39 | "src" = "revizor" 40 | 41 | [tool.hatch.build] 42 | exclude = [ 43 | "demo/", 44 | "docs/", 45 | "mkdocs-overrides/", 46 | "src/x86/executor/", 47 | "tests/", 48 | "tests/x86_tests/", 49 | "tests/x86_tests/", 50 | "base.json", 51 | "site/", 52 | "dbg/", 53 | "revizor.code-workspace", 54 | ] 55 | 56 | [tool.hatch.build.targets.wheel] 57 | packages = ["revizor"] 58 | 59 | [project.scripts] 60 | rvzr = "revizor.cli:main" 61 | -------------------------------------------------------------------------------- /demo/README.md: -------------------------------------------------------------------------------- 1 | This demo shows how Revizor can find real-world vulnerabilities in CPUs. 2 | Each config here is intentionally made to detect only one type of vulnerabilities. 3 | 4 | For example, if you fuzz an Intel CPU with `conf-v1.yaml`, you will likely detect an instance of Spectre V1. 5 | (of course, there is always a chance that you will find a new previously-unknown vulnerability with this config, but the likelihood is rather low). 6 | 7 | This demo targets Intel CPUs. Other microarchitectures are not yet supported (but coming soon!). 8 | 9 | The commands below assume that the ISA spec (downloaded via `rvzr download_spec`) is stored in `base.json`. 10 | 11 | * Spectre V1 ([description](https://meltdownattack.com/)): 12 | 13 | ``` 14 | rvzr fuzz -s base.json -c demo/conf-v1.yaml -i 50 -n 10000 15 | ``` 16 | Expected duration - several seconds. 17 | 18 | * MDS or LVI-Null, depending on the CPU model ([description of MDS](https://mdsattacks.com/) and [LVI](https://lviattack.eu/)): 19 | 20 | ``` 21 | rvzr fuzz -s base.json -c demo/conf-v1.yaml -i 50 -n 10000 22 | ``` 23 | Expected duration - several minutes. 24 | 25 | * Spectre V4 ([description](https://www.cyberus-technology.de/posts/2018-05-22-intel-store-load-spectre-vulnerability.html)): 26 | 27 | ``` 28 | rvzr fuzz -s base.json -c demo/conf-v4.yaml -i 50 -n 10000 29 | ``` 30 | Expected duration - 5-20 minutes. 31 | 32 | 33 | * Spectre V1-Var ([description](https://dl.acm.org/doi/10.1145/3503222.3507729) and [here](https://eprint.iacr.org/2022/715.pdf)) 34 | 35 | ``` 36 | rvzr fuzz -s base.json -c demo/conf-v1.yaml -i 50 -n 10000 37 | ``` 38 | Expected duration - several minutes. 39 | -------------------------------------------------------------------------------- /src/x86/executor/include/main.h: -------------------------------------------------------------------------------- 1 | /// File: Main Header 2 | /// 3 | // Copyright (C) Microsoft Corporation 4 | // SPDX-License-Identifier: MIT 5 | 6 | #ifndef X86_EXECUTOR 7 | #define X86_EXECUTOR 8 | 9 | #include 10 | #include 11 | #include 12 | 13 | typedef enum { 14 | PRIME_PROBE, 15 | PARTIAL_PRIME_PROBE, 16 | FAST_PRIME_PROBE, 17 | FAST_PARTIAL_PRIME_PROBE, 18 | FLUSH_RELOAD, 19 | EVICT_RELOAD, 20 | TSC, 21 | } measurement_mode_e; 22 | 23 | #define EXECUTOR_DEBUG 0 24 | 25 | // Executor Configuration Interface 26 | extern bool quick_and_dirty_mode; 27 | extern measurement_mode_e measurement_mode; 28 | #define MEASUREMENT_MODE_DEFAULT PRIME_PROBE 29 | extern long uarch_reset_rounds; 30 | #define UARCH_RESET_ROUNDS_DEFAULT 1 31 | extern bool enable_ssbp_patch; 32 | #define SSBP_PATCH_DEFAULT true 33 | extern bool enable_prefetchers; 34 | #define PREFETCHER_DEFAULT false 35 | extern char pre_run_flush; 36 | #define PRE_RUN_FLUSH_DEFAULT 1 37 | extern bool enable_hpa_gpa_collisions; 38 | #define HPA_GPA_COLLISIONS_DEFAULT false 39 | extern bool enable_mpx; // MPX - unused on AMD 40 | #define MPX_DEFAULT false 41 | extern bool dbg_gpr_mode; 42 | #define DBG_GPR_MODE_DEFAULT false 43 | 44 | // Linux Kernel compatibility 45 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 0) 46 | #include 47 | extern int (*set_memory_x)(unsigned long, int); 48 | extern int (*set_memory_nx)(unsigned long, int); 49 | #else 50 | #include 51 | #endif 52 | 53 | // CPU features 54 | extern struct cpuinfo_x86 *cpuinfo; // cached result of cpu_data for CPU 0 55 | 56 | #endif // X86_EXECUTOR 57 | -------------------------------------------------------------------------------- /tests/x86_tests/asm/spectre_v1.1.asm: -------------------------------------------------------------------------------- 1 | .intel_syntax noprefix 2 | .test_case_enter: 3 | .section .data.main 4 | lfence 5 | 6 | # reduce the entropy of rax 7 | and rax, 0b111111000000 8 | 9 | # delay the cond. jump 10 | mov rcx, 0 11 | lea rbx, qword ptr [rbx + rcx + 1] 12 | lea rbx, qword ptr [rbx + rcx - 1] 13 | lea rbx, qword ptr [rbx + rcx + 1] 14 | lea rbx, qword ptr [rbx + rcx - 1] 15 | lea rbx, qword ptr [rbx + rcx + 1] 16 | lea rbx, qword ptr [rbx + rcx - 1] 17 | lea rbx, qword ptr [rbx + rcx + 1] 18 | lea rbx, qword ptr [rbx + rcx - 1] 19 | lea rbx, qword ptr [rbx + rcx + 1] 20 | lea rbx, qword ptr [rbx + rcx - 1] 21 | lea rbx, qword ptr [rbx + rcx + 1] 22 | lea rbx, qword ptr [rbx + rcx - 1] 23 | lea rbx, qword ptr [rbx + rcx + 1] 24 | lea rbx, qword ptr [rbx + rcx - 1] 25 | lea rbx, qword ptr [rbx + rcx + 1] 26 | lea rbx, qword ptr [rbx + rcx - 1] 27 | lea rbx, qword ptr [rbx + rcx + 1] 28 | lea rbx, qword ptr [rbx + rcx - 1] 29 | lea rbx, qword ptr [rbx + rcx + 1] 30 | lea rbx, qword ptr [rbx + rcx - 1] 31 | lea rbx, qword ptr [rbx + rcx + 1] 32 | lea rbx, qword ptr [rbx + rcx - 1] 33 | lea rbx, qword ptr [rbx + rcx + 1] 34 | lea rbx, qword ptr [rbx + rcx - 1] 35 | lea rbx, qword ptr [rbx + rcx + 1] 36 | lea rbx, qword ptr [rbx + rcx - 1] 37 | lea rbx, qword ptr [rbx + rcx + 1] 38 | lea rbx, qword ptr [rbx + rcx - 1] 39 | lea rbx, qword ptr [rbx + rcx + 1] 40 | lea rbx, qword ptr [rbx + rcx - 1] 41 | 42 | # reduce the entropy in rbx 43 | and rbx, 0b1 44 | 45 | cmp rbx, 0 46 | je .l1 # misprediction 47 | .l0: 48 | # rbx != 0 49 | mov qword ptr [r14], rax 50 | mov rdx, qword ptr [r14] 51 | mov rbx, qword ptr [r14 + rdx] 52 | .l1: 53 | mfence 54 | 55 | .test_case_exit: 56 | -------------------------------------------------------------------------------- /src/x86/executor/include/input_parser.h: -------------------------------------------------------------------------------- 1 | /// File: Header for the input parser 2 | /// 3 | // Copyright (C) Microsoft Corporation 4 | // SPDX-License-Identifier: MIT 5 | 6 | #ifndef _INPUT_PARSER_H_ 7 | #define _INPUT_PARSER_H_ 8 | 9 | #include "sandbox_manager.h" 10 | 11 | #define REG_INIT_AREA_SIZE_ALIGNED 4096 12 | 13 | typedef uint64_t input_fragment_size_t; 14 | typedef uint64_t input_fragment_reserved_field_t; 15 | 16 | typedef struct { 17 | input_fragment_size_t size; 18 | input_fragment_reserved_field_t reserved; 19 | } input_fragment_metadata_entry_t; 20 | 21 | typedef struct { 22 | char main_area[MAIN_AREA_SIZE]; 23 | char faulty_area[FAULTY_AREA_SIZE]; 24 | char reg_init_region[REG_INIT_AREA_SIZE_ALIGNED]; 25 | } input_fragment_t; 26 | 27 | typedef struct { 28 | size_t metadata_size; 29 | size_t data_size; 30 | input_fragment_metadata_entry_t *metadata; 31 | input_fragment_t *data; 32 | } input_batch_t; 33 | 34 | #define MAX_INPUTS (1024 * 1024) 35 | #define BATCH_HEADER_SIZE 16 // sizeof(n_actors) + sizeof(n_inputs) 36 | #define FRAGMENT_SIZE_ALIGNED \ 37 | (MAIN_AREA_SIZE + FAULTY_AREA_SIZE + REG_INIT_AREA_SIZE_ALIGNED) 38 | 39 | extern input_batch_t *inputs; 40 | extern size_t n_inputs; 41 | 42 | input_fragment_t *get_input_fragment(uint64_t input_id, uint64_t actor_id); 43 | input_fragment_t *get_input_fragment_unsafe(uint64_t input_id, uint64_t actor_id); 44 | ssize_t parse_input_buffer(const char *buf, size_t count, bool *finished); 45 | bool input_parsing_completed(void); 46 | 47 | int init_input_parser(void); 48 | void free_input_parser(void); 49 | 50 | #endif // _INPUT_PARSER_H_ 51 | -------------------------------------------------------------------------------- /mkdocs.yml: -------------------------------------------------------------------------------- 1 | --- 2 | site_name: Revizor 3 | site_url: https://github.com/microsoft/sca-fuzzer 4 | site_author: Microsoft 5 | site_description: A security-oriented tool for detecting microarchitectural leaks in CPUs, such as Spectre and Meltdown. 6 | copyright: 'Copyright © 2024 Microsoft' 7 | 8 | repo_name: microsoft/sca-fuzzer 9 | repo_url: https://github.com/microsoft/sca-fuzzer 10 | 11 | theme: 12 | name: material 13 | custom_dir: mkdocs-overrides 14 | logo: assets/ms_icon.png 15 | favicon: assets/ms_icon.png 16 | palette: 17 | primary: indigo 18 | features: 19 | - navigation.instant 20 | - navigation.tabs 21 | - search.share 22 | 23 | plugins: 24 | - search 25 | 26 | markdown_extensions: 27 | - pymdownx.superfences: 28 | custom_fences: 29 | - name: mermaid 30 | class: mermaid 31 | format: !!python/name:pymdownx.superfences.fence_code_format 32 | - meta 33 | - admonition 34 | - pymdownx.highlight 35 | - pymdownx.pathconverter 36 | - pymdownx.tabbed 37 | - pymdownx.tasklist 38 | 39 | nav: 40 | - Home: index.md 41 | - Quick Start: quick-start.md 42 | - User Documentation: 43 | - Command Line Interface: user/cli.md 44 | - Modes of Operation: user/modes.md 45 | - Configuration Options: user/config.md 46 | - Minimization Passes: user/minimization.md 47 | - Violation Root-Causing: user/fuzzing-guide.md 48 | - Developer Documentation: 49 | - Developer Guide: development.md 50 | - How Revizor Works: how-revizor-works.md 51 | - Architecture Overview: architecture.md 52 | - Executor-related Topics: 53 | - Register Allocation: registers.md 54 | - Memory Layout: sandbox.md 55 | # - Trophies: trophies.md 56 | -------------------------------------------------------------------------------- /tests/x86_tests/asm/spectre_v4.asm: -------------------------------------------------------------------------------- 1 | .intel_syntax noprefix 2 | .test_case_enter: 3 | .section .data.main 4 | 5 | # the leaked value - rcx 6 | # construct a page offset in the range [0x200; 0x900] 7 | and rcx, 0b11100000000 8 | add rcx, 0x200 9 | 10 | # save the offset into the offset 0 11 | mov qword ptr [r14], rcx 12 | mfence 13 | 14 | # create a delay on rbx 15 | mov rax, 0 16 | lea rbx, qword ptr [rbx + rax + 1] 17 | lea rbx, qword ptr [rbx + rax - 1] 18 | lea rbx, qword ptr [rbx + rax + 1] 19 | lea rbx, qword ptr [rbx + rax - 1] 20 | lea rbx, qword ptr [rbx + rax + 1] 21 | lea rbx, qword ptr [rbx + rax - 1] 22 | lea rbx, qword ptr [rbx + rax + 1] 23 | lea rbx, qword ptr [rbx + rax - 1] 24 | lea rbx, qword ptr [rbx + rax + 1] 25 | lea rbx, qword ptr [rbx + rax - 1] 26 | lea rbx, qword ptr [rbx + rax + 1] 27 | lea rbx, qword ptr [rbx + rax - 1] 28 | lea rbx, qword ptr [rbx + rax + 1] 29 | lea rbx, qword ptr [rbx + rax - 1] 30 | lea rbx, qword ptr [rbx + rax + 1] 31 | lea rbx, qword ptr [rbx + rax - 1] 32 | lea rbx, qword ptr [rbx + rax + 1] 33 | lea rbx, qword ptr [rbx + rax - 1] 34 | lea rbx, qword ptr [rbx + rax + 1] 35 | lea rbx, qword ptr [rbx + rax - 1] 36 | lea rbx, qword ptr [rbx + rax + 1] 37 | lea rbx, qword ptr [rbx + rax - 1] 38 | lea rbx, qword ptr [rbx + rax + 1] 39 | lea rbx, qword ptr [rbx + rax - 1] 40 | lea rbx, qword ptr [rbx + rax + 1] 41 | lea rbx, qword ptr [rbx + rax - 1] 42 | 43 | 44 | # store and load, potentially matching 45 | and rbx, 0b111000000 46 | mov qword ptr [r14 + rbx], 0x100 # store offset 0x100 47 | mov rdx, qword ptr [r14] # load the offset; misprediction happens here 48 | 49 | # dependent load with the offset 50 | and rdx, 0b111111000000 51 | mov rdx, qword ptr [r14 + rdx] 52 | mfence 53 | 54 | .test_case_exit: 55 | -------------------------------------------------------------------------------- /tests/runtests.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | if [ "$1" == "" ] || [ $1 != "--ignore-errors" ]; then 4 | set -e 5 | fi 6 | 7 | SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" &>/dev/null && pwd)" 8 | 9 | echo "" 10 | echo "===== Type Checking with mypy =====" 11 | cd $SCRIPT_DIR/.. || exit 12 | MYPYPATH=src/ python3 -m mypy src/*.py src/x86/*.py --exclude src/tests/unit_isa_loader.py \ 13 | --exclude src/x86/tests/unit_model.py --exclude src/x86/tests/unit_executor.py \ 14 | --exclude src/x86/tests/unit_generators.py 15 | cd - >/dev/null || exit 16 | 17 | echo "" 18 | echo "===== Code Style Checking with flake8 =====" 19 | cd $SCRIPT_DIR/.. || exit 20 | python3 -m flake8 --max-line-length 100 --ignore E402,W503 . --count --show-source --statistics 21 | cd - >/dev/null || exit 22 | 23 | echo "" 24 | echo "===== Core Unit Tests =====" 25 | cd $SCRIPT_DIR/.. || exit 26 | python3 -m unittest discover tests -p "unit_*.py" -v 27 | cd - >/dev/null || exit 28 | 29 | echo "" 30 | echo "===== x86 kernel module =====" 31 | cd $SCRIPT_DIR || exit 32 | ./x86_tests/kernel_module.bats 33 | cd - >/dev/null || exit 34 | 35 | echo "" 36 | echo "===== x86 unit tests =====" 37 | cd $SCRIPT_DIR/.. || exit 38 | # Note: we intentionally do not use the 'discover' option because it causes cross-contamination 39 | # of config options between unit tests 40 | python3 -m unittest tests.x86_tests.unit_generators -v 41 | echo "-------------" 42 | python3 -m unittest tests.x86_tests.unit_isa_loader -v 43 | echo "-------------" 44 | python3 -m unittest tests.x86_tests.unit_model -v 45 | echo "-------------" 46 | cd - >/dev/null || exit 47 | 48 | echo "" 49 | echo "===== x86 acceptance tests =====" 50 | cd $SCRIPT_DIR || exit 51 | ./x86_tests//acceptance.bats 52 | cd - >/dev/null || exit 53 | -------------------------------------------------------------------------------- /demo/big-fuzz.yaml: -------------------------------------------------------------------------------- 1 | instruction_set: x86-64 2 | 3 | # Model 4 | contract_observation_clause: ct 5 | contract_execution_clause: 6 | - seq 7 | 8 | # Actors 9 | actors: 10 | - main: 11 | - data_properties: 12 | - present: true 13 | 14 | # Executor 15 | executor_mode: P+P 16 | x86_executor_enable_ssbp_patch: true 17 | 18 | # Program generator 19 | program_size: 64 20 | avg_mem_accesses: 16 21 | max_bb_per_function: 1 # straight-line code only 22 | min_bb_per_function: 1 23 | min_successors_per_bb: 1 24 | max_successors_per_bb: 1 25 | 26 | instruction_categories: 27 | - BASE-BINARY 28 | - BASE-BITBYTE 29 | - BASE-CMOV 30 | - BASE-COND_BR 31 | - BASE-CONVERT 32 | - BASE-DATAXFER 33 | - BASE-FLAGOP 34 | - BASE-LOGICAL 35 | - BASE-MISC 36 | - BASE-NOP 37 | - BASE-WIDENOP 38 | - BASE-POP 39 | - BASE-PUSH 40 | - BASE-SEMAPHORE 41 | - BASE-SETCC 42 | # - BASE-STRINGOP # commented out as it triggers a known information leak 43 | - LONGMODE-CONVERT 44 | - LONGMODE-DATAXFER 45 | - LONGMODE-SEMAPHORE 46 | # - LONGMODE-STRINGOP # commented out as it triggers a known information leak 47 | - SSE-DATAXFER 48 | - SSE-LOGICAL_FP 49 | - SSE-MISC 50 | - SSE-SSE 51 | 52 | # Input generator 53 | input_gen_entropy_bits: 24 54 | inputs_per_class: 2 55 | 56 | # Fuzzer 57 | enable_speculation_filter: true 58 | enable_observation_filter: true 59 | enable_fast_path_model: true 60 | coverage_type: model_instructions 61 | 62 | # Output 63 | color: true 64 | logging_modes: 65 | - info 66 | - stat 67 | - dbg_generator 68 | # - dbg_timestamp 69 | # - dbg_violation 70 | # - dbg_dump_htraces 71 | # - dbg_dump_ctraces 72 | # - dbg_dump_traces_unlimited 73 | # - dbg_model 74 | - dbg_coverage 75 | # - dbg_priming 76 | # - dbg_executor_raw 77 | 78 | -------------------------------------------------------------------------------- /src/x86/executor/include/test_case_parser.h: -------------------------------------------------------------------------------- 1 | /// File: Header for the test case parser and manager 2 | /// 3 | // Copyright (C) Microsoft Corporation 4 | // SPDX-License-Identifier: MIT 5 | 6 | #ifndef _TEST_CASE_PARSER_H_ 7 | #define _TEST_CASE_PARSER_H_ 8 | 9 | #include "actor.h" 10 | 11 | #define MAX_SECTIONS MAX_ACTORS 12 | #define MAX_SYMBOLS 128 13 | #define MAX_SECTION_SIZE 4096 // NOTE: must be exactly 1 page to detect sysfs buffering 14 | #define MAX_LOADED_SECTION_SIZE (4096 * 2) 15 | #define TC_HEADER_SIZE (2 * sizeof(uint64_t)) 16 | 17 | typedef uint64_t section_size_t; 18 | typedef uint64_t section_metadata_reserved_t; 19 | typedef uint64_t section_id_t; 20 | typedef uint64_t symbol_offset_t; 21 | typedef uint64_t symbol_id_t; 22 | typedef uint64_t symbol_args_t; 23 | 24 | typedef struct { 25 | actor_id_t owner; 26 | section_size_t size; 27 | section_metadata_reserved_t reserved; 28 | } tc_section_metadata_entry_t; 29 | 30 | typedef struct { 31 | char code[MAX_SECTION_SIZE]; 32 | } tc_section_t; 33 | 34 | typedef struct { 35 | actor_id_t owner; 36 | symbol_offset_t offset; 37 | symbol_id_t id; 38 | symbol_args_t args; 39 | } tc_symbol_entry_t; 40 | 41 | typedef struct { 42 | bool includes_vm_actors; 43 | bool includes_user_actors; 44 | bool has_explicit_fault_handler; 45 | } tc_features_t; 46 | 47 | typedef struct { 48 | tc_features_t features; 49 | size_t actor_table_size; 50 | size_t symbol_table_size; 51 | size_t metadata_size; 52 | size_t sections_size; 53 | actor_metadata_t *actor_table; 54 | tc_symbol_entry_t *symbol_table; 55 | tc_section_metadata_entry_t *metadata; 56 | tc_section_t *sections; 57 | } test_case_t; 58 | 59 | extern test_case_t *test_case; 60 | 61 | ssize_t parse_test_case_buffer(const char *buf, size_t count, bool *finished); 62 | bool tc_parsing_completed(void); 63 | int init_test_case_parser(void); 64 | void free_test_case_parser(void); 65 | 66 | #endif // _TEST_CASE_PARSER_H_ 67 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | As an open source project, Revizor welcomes contributions and suggestions. 4 | 5 | ## Contributor License Agreement and Code of Conduct 6 | 7 | Most contributions require you to agree to a 8 | Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us 9 | the rights to use your contribution. For details, visit https://cla.opensource.microsoft.com. 10 | 11 | When you submit a pull request, a CLA bot will automatically determine whether you need to provide 12 | a CLA and decorate the PR appropriately (e.g., status check, comment). Simply follow the instructions 13 | provided by the bot. You will only need to do this once across all repos using our CLA. 14 | 15 | This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). 16 | For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or 17 | contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. 18 | 19 | ## Reporting Issues 20 | 21 | To submit a bug or a feature request, use Github [issues](https://github.com/microsoft/sca-fuzzer/issues). 22 | 23 | If you have a question or need help, please use [Github Discussions](https://github.com/microsoft/sca-fuzzer/discussions) and tag the maintainer (@OleksiiOleksenko) for visibility. 24 | 25 | ## Submitting Patches 26 | 27 | To submit a patch, use the following procedure: 28 | * Fork Revizor on github (https://docs.github.com/en/github/getting-started-with-github/fork-a-repo) 29 | * Create a topic branch (`git checkout -b my_branch`) 30 | * Make sure all tests pass (`./tests/runtests.sh `) and that the code is formatted accordingly to the [Code Style](docs/development.md). 31 | * Push to your branch (`git push origin my_branch`) 32 | * Initiate a pull request on github (https://docs.github.com/en/github/collaborating-with-issues-and-pull-requests/creating-a-pull-request) 33 | * Wait for the PR to get reviewed and merged 34 | -------------------------------------------------------------------------------- /tests/unit_analyser.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright (C) Microsoft Corporation 3 | SPDX-License-Identifier: MIT 4 | """ 5 | import unittest 6 | from src.analyser import MergedBitmapAnalyser, SetAnalyser, ChiSquaredAnalyser 7 | from src.interfaces import Input, HTrace 8 | from src.config import CONF 9 | 10 | 11 | class AnalyserTest(unittest.TestCase): 12 | 13 | def test_merged_bitmap_analyser(self): 14 | analyser = MergedBitmapAnalyser() 15 | dummy_input = Input() 16 | inputs = [dummy_input] * 4 17 | 18 | h1 = HTrace([0b1101, 0b1101]) 19 | h2 = HTrace([0b1011, 0b1011]) 20 | h3 = HTrace([0b1000, 0b1000]) 21 | h4 = HTrace([0b1000, 0b1000]) 22 | htraces = [h1, h2, h3, h4] 23 | ctraces = [1, 1, 2, 2] 24 | 25 | violations = analyser.filter_violations(inputs, ctraces, htraces) 26 | self.assertEqual(len(violations), 1) 27 | self.assertEqual(violations[0].ctrace, 1) 28 | 29 | def test_set_analyser(self): 30 | analyser = SetAnalyser() 31 | dummy_input = Input() 32 | inputs = [dummy_input] * 4 33 | 34 | h1 = HTrace([1, 2, 2, 1]) 35 | h2 = HTrace([1, 3, 3, 1]) 36 | h3 = HTrace([1, 1, 1, 1]) 37 | h4 = HTrace([1, 1, 1, 1]) 38 | htraces = [h1, h2, h3, h4] 39 | ctraces = [1, 1, 2, 2] 40 | 41 | violations = analyser.filter_violations(inputs, ctraces, htraces) 42 | self.assertEqual(len(violations), 1) 43 | self.assertEqual(violations[0].ctrace, 1) 44 | 45 | def test_chi2_analyser(self): 46 | analyser = ChiSquaredAnalyser() 47 | dummy_input = Input() 48 | inputs = [dummy_input] * 4 49 | 50 | h1 = [1] * CONF.executor_sample_sizes[0] 51 | h2 = [2] * CONF.executor_sample_sizes[0] 52 | h2[0] = 1 53 | h2[1] = 1 54 | htraces = [HTrace(h1), HTrace(h2), HTrace(h2), HTrace(h2)] 55 | ctraces = [1, 1, 2, 2] 56 | 57 | violations = analyser.filter_violations(inputs, ctraces, htraces) 58 | self.assertEqual(len(violations), 1) 59 | self.assertEqual(violations[0].ctrace, 1) 60 | -------------------------------------------------------------------------------- /demo/tsa-sq/template.asm: -------------------------------------------------------------------------------- 1 | .intel_syntax noprefix 2 | .test_case_enter: 3 | 4 | # ----------------------------- Kernel-mode Actor (Victim) ------------------- 5 | .section .data.main 6 | .function_main_0: 7 | # observer start 8 | .macro.set_k2u_target.user.function_user_0: 9 | .macro.set_u2k_target.main.function_main_1: 10 | .macro.switch_k2u.user.0: 11 | 12 | 13 | .function_main_1: 14 | .macro.landing_u2k.main_1: 15 | 16 | # secret injection 17 | .macro.random_instructions.64.32.main_1: 18 | 19 | .macro.set_k2u_target.user.function_user_1: 20 | .macro.set_u2k_target.main.function_main_2: 21 | .macro.switch_k2u.user.1: 22 | 23 | .function_main_2: 24 | .macro.landing_u2k.main_2: 25 | 26 | .macro.fault_handler: 27 | .macro.set_k2u_target.user.function_user_2: 28 | .macro.set_u2k_target.main.function_main_3: 29 | .macro.switch_k2u.user.2: 30 | 31 | .function_main_3: 32 | .macro.landing_u2k.main_3: 33 | nop 34 | 35 | # ----------------------------- User-mode Actor ------------------------------ 36 | .section .data.user 37 | .function_user_0: 38 | .macro.landing_k2u.user_0: 39 | .macro.measurement_start: 40 | .macro.switch_u2k.main.user_0: 41 | lfence 42 | 43 | 44 | .function_user_1: 45 | .macro.landing_k2u.user_1: 46 | xor rax, rax # noremove 47 | mov rax, qword ptr [r14 + 0x2000] # noremove 48 | mov rbx, qword ptr [r14 + 0x2008] # noremove 49 | mov rcx, qword ptr [r14 + 0x2010] # noremove 50 | mov rdx, qword ptr [r14 + 0x2018] # noremove 51 | mov rsi, qword ptr [r14 + 0x2020] # noremove 52 | mov rdi, qword ptr [r14 + 0x2028] # noremove 53 | lfence 54 | 55 | # secret retrieval 56 | .macro.random_instructions.64.32.user_1: 57 | 58 | # make sure the model doesn't attempt to go further than this point 59 | lfence # noremove 60 | 61 | .macro.measurement_end.user_1: 62 | .macro.switch_u2k.main.1: 63 | lfence 64 | 65 | 66 | .function_user_2: 67 | .macro.landing_k2u.user_2: 68 | .macro.measurement_end.user_2: 69 | .macro.switch_u2k.main.2: 70 | lfence 71 | 72 | 73 | # ----------------------------- Exit ----------------------------------------- 74 | .test_case_exit: 75 | -------------------------------------------------------------------------------- /.github/workflows/python-lint-and-test.yaml: -------------------------------------------------------------------------------- 1 | # This workflow will install Python dependencies, run tests and lint with a variety of Python versions 2 | # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python 3 | 4 | name: Python Lint and Test 5 | permissions: 6 | contents: read 7 | 8 | on: 9 | push: 10 | branches: 11 | - main 12 | - main-fixes 13 | - pre-release 14 | - dev 15 | pull_request: 16 | branches: 17 | - main 18 | - main-fixes 19 | - pre-release 20 | - dev 21 | 22 | jobs: 23 | build: 24 | 25 | runs-on: ubuntu-latest 26 | strategy: 27 | fail-fast: false 28 | matrix: 29 | python-version: ["3.9", "3.10", "3.11"] 30 | 31 | steps: 32 | - uses: actions/checkout@v4 33 | - name: Set up Python ${{ matrix.python-version }} 34 | uses: actions/setup-python@v5 35 | with: 36 | python-version: ${{ matrix.python-version }} 37 | - name: Install dependencies 38 | run: | 39 | python -m pip install --upgrade pip 40 | python -m pip install flake8 mypy 41 | python -m pip install . 42 | - name: Lint with flake8 43 | run: | 44 | # stop the build if there are Python syntax errors or undefined names 45 | flake8 . --max-line-length 100 --ignore E402,W503 --count --show-source --statistics 46 | - name: Type check with mypy 47 | run: | 48 | MYPYPATH=src/ mypy src/*.py src/x86/*.py --exclude src/tests/unit_isa_loader.py \ 49 | --exclude src/x86/tests/unit_model.py --exclude src/x86/tests/unit_executor.py \ 50 | --exclude src/x86/tests/unit_generators.py 51 | - name: Common unit tests 52 | run: | 53 | python -m unittest discover tests -p "unit_*.py" -v 54 | - name: x86 unit tests 55 | run: | 56 | # Note: we intentionally do not use the 'discover' option because 57 | # it causes cross-contamination of config options between unit tests 58 | python3 -m unittest tests.x86_tests.unit_generators -v 59 | python3 -m unittest tests.x86_tests.unit_isa_loader -v 60 | python3 -m unittest tests.x86_tests.unit_model -v 61 | -------------------------------------------------------------------------------- /tests/x86_tests/asm/spectre_v2.asm: -------------------------------------------------------------------------------- 1 | .intel_syntax noprefix 2 | .test_case_enter: 3 | .section .data.main 4 | 5 | # reduce the entropy of rax 6 | and rax, 0b111111000000 7 | 8 | # prepare jump targets 9 | lea rdx, qword ptr [rip + .l1] 10 | lea rsi, qword ptr [rip + .l2] 11 | 12 | # delay the jump 13 | lea rbx, qword ptr [rbx + rax + 1] 14 | lea rbx, qword ptr [rbx + rax - 1] 15 | lea rbx, qword ptr [rbx + rax + 1] 16 | lea rbx, qword ptr [rbx + rax - 1] 17 | lea rbx, qword ptr [rbx + rax + 1] 18 | lea rbx, qword ptr [rbx + rax - 1] 19 | lea rbx, qword ptr [rbx + rax + 1] 20 | lea rbx, qword ptr [rbx + rax - 1] 21 | lea rbx, qword ptr [rbx + rax + 1] 22 | lea rbx, qword ptr [rbx + rax - 1] 23 | lea rbx, qword ptr [rbx + rax + 1] 24 | lea rbx, qword ptr [rbx + rax - 1] 25 | lea rbx, qword ptr [rbx + rax + 1] 26 | lea rbx, qword ptr [rbx + rax - 1] 27 | lea rbx, qword ptr [rbx + rax + 1] 28 | lea rbx, qword ptr [rbx + rax - 1] 29 | lea rbx, qword ptr [rbx + rax + 1] 30 | lea rbx, qword ptr [rbx + rax - 1] 31 | lea rbx, qword ptr [rbx + rax + 1] 32 | lea rbx, qword ptr [rbx + rax - 1] 33 | lea rbx, qword ptr [rbx + rax + 1] 34 | lea rbx, qword ptr [rbx + rax - 1] 35 | lea rbx, qword ptr [rbx + rax + 1] 36 | lea rbx, qword ptr [rbx + rax - 1] 37 | lea rbx, qword ptr [rbx + rax + 1] 38 | lea rbx, qword ptr [rbx + rax - 1] 39 | lea rbx, qword ptr [rbx + rax + 1] 40 | lea rbx, qword ptr [rbx + rax - 1] 41 | lea rbx, qword ptr [rbx + rax + 1] 42 | lea rbx, qword ptr [rbx + rax - 1] 43 | lea rbx, qword ptr [rbx + rax + 1] 44 | lea rbx, qword ptr [rbx + rax - 1] 45 | lea rbx, qword ptr [rbx + rax + 1] 46 | lea rbx, qword ptr [rbx + rax - 1] 47 | lea rbx, qword ptr [rbx + rax + 1] 48 | lea rbx, qword ptr [rbx + rax - 1] 49 | lea rbx, qword ptr [rbx + rax + 1] 50 | lea rbx, qword ptr [rbx + rax - 1] 51 | lea rbx, qword ptr [rbx + rax + 1] 52 | lea rbx, qword ptr [rbx + rax - 1] 53 | 54 | # reduce the entropy in rbx 55 | and rbx, 0b1000000 56 | 57 | # select a target based on the random value in rbx 58 | cmp rbx, 0 59 | cmove rsi, rdx 60 | 61 | jmp rsi # misprediction 62 | .l1: 63 | # rbx = 0 64 | mov rdx, qword ptr [r14 + rax] 65 | .l2: 66 | mfence 67 | 68 | # override the targets to avoid failing the arch. check 69 | mov rdx, 0 70 | mov rsi, 0 71 | 72 | .test_case_exit: 73 | -------------------------------------------------------------------------------- /src/x86/executor/include/vmx.h: -------------------------------------------------------------------------------- 1 | /// File: Header for vmx.c 2 | /// 3 | // Copyright (C) Microsoft Corporation 4 | // SPDX-License-Identifier: MIT 5 | 6 | #ifndef _X86_EXECUTOR_VMX_H_ 7 | #define _X86_EXECUTOR_VMX_H_ 8 | 9 | #include 10 | #include 11 | 12 | // ================================================================================================= 13 | // Kernel compatibility 14 | #ifdef FEAT_CTL_VMX_ENABLED_OUTSIDE_SMX 15 | #define FEATURE_VMX_ENABLED_OUTSIDE_SMX FEAT_CTL_VMX_ENABLED_OUTSIDE_SMX 16 | #elif defined(FEATURE_CONTROL_VMXON_ENABLED_OUTSIDE_SMX) 17 | #define FEATURE_VMX_ENABLED_OUTSIDE_SMX FEATURE_CONTROL_VMXON_ENABLED_OUTSIDE_SMX 18 | #else 19 | #error "FEATURE_VMX_ENABLED_OUTSIDE_SMX not defined" 20 | #endif 21 | 22 | #ifdef FEAT_CTL_LOCKED 23 | #define FEATURE_CTL_LOCKED FEAT_CTL_LOCKED 24 | #elif defined(FEATURE_CONTROL_LOCKED) 25 | #define FEATURE_CTL_LOCKED FEATURE_CONTROL_LOCKED 26 | #else 27 | #error "FEATURE_CTL_LOCKED not defined" 28 | #endif 29 | 30 | #ifdef MSR_IA32_FEAT_CTL 31 | #define MSR_FEATURE_CONTROL MSR_IA32_FEAT_CTL 32 | #elif defined(MSR_IA32_FEATURE_CONTROL) 33 | #define MSR_FEATURE_CONTROL MSR_IA32_FEATURE_CONTROL 34 | #else 35 | #error "MSR_FEATURE_CONTROL not defined" 36 | #endif 37 | 38 | #ifndef SECONDARY_EXEC_XSAVES 39 | #define SECONDARY_EXEC_XSAVES SECONDARY_EXEC_ENABLE_XSAVES 40 | #endif 41 | 42 | // ================================================================================================= 43 | // Host VMX data structures 44 | #define VMXON_SIZE 4096 // 4KB, as defined in SDM "Enabling and Entering VMX Operation" 45 | #define VMCS_SIZE 4096 // 4KB, as defined in SDM "Format of the VMCS Region" 46 | 47 | typedef struct { 48 | uint32_t revision_id : 30; 49 | uint32_t reserved_31 : 1; 50 | uint8_t data[VMXON_SIZE - 4]; 51 | } __attribute__((packed)) vmxon_region_t; 52 | 53 | typedef struct { 54 | uint32_t revision_id : 30; 55 | uint32_t reserved_31 : 1; 56 | uint32_t abort_indicator; 57 | uint8_t data[VMCS_SIZE - 8]; 58 | } __attribute__((packed)) vmcs_t; 59 | 60 | // ================================================================================================= 61 | // Module interface 62 | extern bool vmx_is_on; 63 | extern uint64_t *vmcs_hpas; 64 | 65 | int vmx_check_cpu_compatibility(void); 66 | int start_vmx_operation(void); 67 | void stop_vmx_operation(void); 68 | int store_orig_vmcs_state(void); 69 | void restore_orig_vmcs_state(void); 70 | int set_vmcs_state(void); 71 | int print_vmx_exit_info(void); 72 | 73 | int init_vmx(void); 74 | void free_vmx(void); 75 | 76 | #endif // _X86_EXECUTOR_VMX_H_ 77 | -------------------------------------------------------------------------------- /src/x86/executor/include/memory_guest.h: -------------------------------------------------------------------------------- 1 | /// File: Header for guest page table functions 2 | /// 3 | // Copyright (C) Microsoft Corporation 4 | // SPDX-License-Identifier: MIT 5 | 6 | #ifndef _GUEST_PAGE_TABLES_H_ 7 | #define _GUEST_PAGE_TABLES_H_ 8 | 9 | #include "page_tables_common.h" 10 | #include "sandbox_manager.h" 11 | 12 | // start of guest's physical memory; this is an arbitrary large aligned number 13 | #define GUEST_P_MEMORY_START 0 14 | #define GUEST_V_MEMORY_START 0x0ULL 15 | #define GUEST_MEMORY_SIZE (512 * 4096) // max size that could be mapped by a single last-level PT 16 | 17 | // Memory layout within the guest memory 18 | typedef struct { 19 | pte_t_ l1[ENTRIES_PER_PAGE]; // PT 20 | pdte_t l2[ENTRIES_PER_PAGE]; // PDT 21 | pdpte_t l3[ENTRIES_PER_PAGE]; // PDPT 22 | pml4e_t l4[ENTRIES_PER_PAGE]; // PML4 23 | } actor_page_table_t; 24 | 25 | typedef struct { 26 | epte_t_ l1[ENTRIES_PER_PAGE]; // EPT PT 27 | epdte_t l2[ENTRIES_PER_PAGE]; // EPT PDT 28 | epdpte_t l3[ENTRIES_PER_PAGE]; // EPT PDPT 29 | epml4e_t l4[ENTRIES_PER_PAGE]; // EPT PML4 30 | } actor_ept_t; 31 | 32 | typedef struct { 33 | uint8_t entries[PAGE_SIZE]; 34 | } __attribute__((packed)) actor_gdt_t; 35 | 36 | // Guest memory layout; it is identical for both physical and virtual memory 37 | typedef struct { 38 | util_t util; 39 | actor_data_t data; 40 | actor_code_t code; 41 | uint8_t vmlaunch_page[PAGE_SIZE]; 42 | actor_gdt_t gdt; 43 | actor_page_table_t guest_page_tables; 44 | } __attribute__((packed)) guest_memory_t; 45 | 46 | // Translation from virtual to guest and host physical addresses 47 | typedef struct { 48 | uint64_t hpa; 49 | uint64_t gpa; 50 | void *gva; 51 | void *hva; 52 | } __attribute__((packed)) hgpa_t; 53 | 54 | // Specialized translation data structure to speed up virtual-to-physical translations 55 | typedef struct { 56 | hgpa_t util[sizeof(util_t) / PAGE_SIZE]; 57 | hgpa_t data[sizeof(actor_data_t) / PAGE_SIZE]; 58 | hgpa_t code[sizeof(actor_code_t) / PAGE_SIZE]; 59 | hgpa_t vmlaunch_page[1]; 60 | hgpa_t gdt[1]; 61 | hgpa_t guest_page_tables[4]; 62 | } __attribute__((packed)) guest_memory_translations_t; 63 | 64 | extern eptp_t *ept_ptr; 65 | 66 | int dbg_dump_guest_page_tables(int actor_id); 67 | int dbg_dump_ept(int actor_id); 68 | 69 | int map_sandbox_to_guest_memory(void); 70 | 71 | void set_faulty_page_guest_permissions(void); 72 | void restore_faulty_page_guest_permissions(void); 73 | 74 | void set_faulty_page_ept_permissions(void); 75 | void restore_faulty_page_ept_permissions(void); 76 | 77 | int allocate_guest_page_tables(void); 78 | void free_guest_page_tables(void); 79 | 80 | #endif // _GUEST_PAGE_TABLES_H_ 81 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ## Security 4 | 5 | Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/Microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet), [Xamarin](https://github.com/xamarin), and [our GitHub organizations](https://opensource.microsoft.com/). 6 | 7 | If you believe you have found a security vulnerability in any Microsoft-owned repository that meets [Microsoft's definition of a security vulnerability](https://docs.microsoft.com/en-us/previous-versions/tn-archive/cc751383(v=technet.10)), please report it to us as described below. 8 | 9 | ## Reporting Security Issues 10 | 11 | **Please do not report security vulnerabilities through public GitHub issues.** 12 | 13 | Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://msrc.microsoft.com/create-report). 14 | 15 | If you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com). If possible, encrypt your message with our PGP key; please download it from the [Microsoft Security Response Center PGP Key page](https://www.microsoft.com/en-us/msrc/pgp-key-msrc). 16 | 17 | You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://www.microsoft.com/msrc). 18 | 19 | Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue: 20 | 21 | * Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.) 22 | * Full paths of source file(s) related to the manifestation of the issue 23 | * The location of the affected source code (tag/branch/commit or direct URL) 24 | * Any special configuration required to reproduce the issue 25 | * Step-by-step instructions to reproduce the issue 26 | * Proof-of-concept or exploit code (if possible) 27 | * Impact of the issue, including how an attacker might exploit the issue 28 | 29 | This information will help us triage your report more quickly. 30 | 31 | If you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://microsoft.com/msrc/bounty) page for more details about our active programs. 32 | 33 | ## Preferred Languages 34 | 35 | We prefer all communications to be in English. 36 | 37 | ## Policy 38 | 39 | Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://www.microsoft.com/en-us/msrc/cvd). 40 | 41 | -------------------------------------------------------------------------------- /tests/x86_tests/asm_to_bin_testcase.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | import sys 3 | import os 4 | import subprocess 5 | 6 | 7 | def write_actor_metadata(f, entry): 8 | f.write((entry[0]).to_bytes(8, byteorder='little')) # id 9 | f.write((entry[1]).to_bytes(8, byteorder='little')) # mode 10 | f.write((entry[2]).to_bytes(8, byteorder='little')) # pl 11 | f.write((entry[3]).to_bytes(8, byteorder='little')) # data permissions 12 | f.write((entry[4]).to_bytes(8, byteorder='little')) # data ept properties 13 | f.write((entry[5]).to_bytes(8, byteorder='little')) # code permissions 14 | 15 | 16 | def write_st_entry(f, entry): 17 | f.write((entry[0]).to_bytes(8, byteorder='little')) # owner 18 | f.write((entry[1]).to_bytes(8, byteorder='little')) # offset 19 | f.write((entry[2]).to_bytes(8, byteorder='little')) # id 20 | f.write((entry[3]).to_bytes(8, byteorder='little')) # args 21 | 22 | 23 | def write_metadata_entry(f, entry): 24 | f.write((entry[0]).to_bytes(8, byteorder='little')) # owner 25 | f.write((entry[1]).to_bytes(8, byteorder='little')) # size 26 | f.write((entry[2]).to_bytes(8, byteorder='little')) # reserved 27 | 28 | 29 | def main(asm_file: str, obj_file: str): 30 | n_actors = 1 31 | n_symbols = 3 32 | 33 | # compile the assembly file 34 | tmpbin = asm_file + '.o' 35 | subprocess.run(['as', asm_file, '-o', tmpbin]) 36 | subprocess.run(['strip', '--remove-section=.note.gnu.property', tmpbin]) 37 | subprocess.run(['objcopy', tmpbin, '-O', 'binary', tmpbin]) 38 | main_size = os.path.getsize(tmpbin) 39 | 40 | # create the test case file 41 | with open(obj_file, 'wb') as f: 42 | # write the test case header 43 | f.write((n_actors).to_bytes(8, byteorder='little')) 44 | f.write((n_symbols).to_bytes(8, byteorder='little')) 45 | 46 | # write actor metadata 47 | write_actor_metadata(f, (0, 0, 0, 0x8000000000000063, 0, 0)) 48 | 49 | # write the symbol table 50 | # - symbol 1: main 51 | write_st_entry(f, (0, 0, 0, 0)) 52 | # - symbol 2: MACRO_MEASUREMENT_START 53 | write_st_entry(f, (0, 0, 1, 0)) 54 | main_size += 8 55 | # - symbol 3: MACRO_MEASUREMENT_END 56 | write_st_entry(f, (0, main_size, 2, 0)) 57 | main_size += 8 58 | 59 | # write the section metadata 60 | write_metadata_entry(f, (0, main_size, 0)) 61 | 62 | # write the code 63 | f.write(b'\x0f\x1f\x84\x00\xff\x00\x00\x00') # nop - MACRO_MEASUREMENT_START 64 | with open(tmpbin, 'rb') as bin_file: 65 | code = bin_file.read() 66 | f.write(code) 67 | f.write(b'\x0f\x1f\x84\x00\xff\x00\x00\x00') # nop - MACRO_MEASUREMENT_END 68 | 69 | 70 | if __name__ == '__main__': 71 | if len(sys.argv) != 3: 72 | print("Usage: %s " % sys.argv[0]) 73 | sys.exit(1) 74 | 75 | sys.exit(main(sys.argv[1], sys.argv[2])) 76 | -------------------------------------------------------------------------------- /src/x86/executor/Makefile: -------------------------------------------------------------------------------- 1 | NAME = x86_executor 2 | KDIR=/lib/modules/$(shell uname -r)/build 3 | 4 | obj-m += $(NAME).o 5 | $(NAME)-objs += fault_handler.o perf_counters.o vmx.o svm.o \ 6 | special_registers.o host_page_tables.o memory_guest.o \ 7 | input_parser.o test_case_parser.o code_loader.o data_loader.o macro_loader.o \ 8 | sandbox_manager.o measurement.o main.o 9 | VPATH += $(src)/hw_features 10 | 11 | # build flags 12 | ifneq ($(shell grep "Intel" /proc/cpuinfo),) 13 | VENDOR_ID=1 14 | else ifneq ($(shell grep "AMD" /proc/cpuinfo),) 15 | VENDOR_ID=2 16 | 17 | # deprecated? 18 | ifneq ($(shell grep -m1 "cpu family" /proc/cpuinfo | grep "23" ),) 19 | CPU_FAMILY=23 20 | else 21 | CPU_FAMILY=25 # Default to 19h 22 | endif 23 | endif 24 | 25 | EXTRA_CFLAGS += -I$(src)/include 26 | EXTRA_CFLAGS += -std=gnu11 -Wno-declaration-after-statement -Wno-comment -msse2 27 | EXTRA_CFLAGS += -g -DDEBUG 28 | EXTRA_CFLAGS += \ 29 | -DL1D_ASSOCIATIVITY=$(shell cat /sys/devices/system/cpu/cpu0/cache/index0/ways_of_associativity) 30 | EXTRA_CFLAGS += -DVENDOR_ID=$(VENDOR_ID) 31 | EXTRA_CFLAGS += -DCPU_FAMILY=$(CPU_FAMILY) 32 | EXTRA_CFLAGS += -DFORCE_SMAP_OFF 33 | EXTRA_CFLAGS += -DPHYSICAL_WIDTH=$(shell grep -m1 "bits physical," /proc/cpuinfo | awk '{print $$4}') 34 | 35 | ifdef VMBUILD 36 | EXTRA_CFLAGS += -DVMBUILD 37 | endif 38 | 39 | # hide objtool warnings - a lot of code in the module violates the checks intentionally, 40 | # so it's impossible to fix 41 | GREP_FILTER = "return found in RETHUNK|indirect call found in RETPOLINE|call without frame pointer|undefined stack stae|return with modified stack frame|unsupported instruction in callable|undefined stack state|ENDBR: " 42 | OBJECT_FILES_NON_STANDARD := y 43 | 44 | # file-specific flags 45 | CFLAGS_code_loader.o := -Wno-attribute-warning # workaround for __write_overflow_field warning 46 | 47 | # build targets 48 | all: 49 | ifeq ($(shell cat /sys/devices/virtual/dmi/id/sys_vendor), QEMU) 50 | ifndef VMBUILD 51 | $(error ERROR: VM environment detected; use `make VMBUILD=1`) 52 | else 53 | $(eval EXTRA_CFLAGS += -DVMBUILD) 54 | endif 55 | endif 56 | make -C $(KDIR) M=$(PWD) modules 2>&1 | grep -vE $(GREP_FILTER) 57 | 58 | clean: 59 | make -C $(KDIR) M=$(PWD) clean 60 | 61 | install: 62 | sudo insmod $(NAME).ko 63 | 64 | uninstall: 65 | sudo rmmod $(NAME) || true 66 | 67 | # --------------- debugging crushes --------------- 68 | dbg_symbols: 69 | objcopy --only-keep-debug $(NAME).o $(NAME).dbg 70 | sudo cat /sys/module/$(NAME)/sections/.text 71 | # continue manually with gdb: 72 | # (if the executor is in a VM): 73 | # scp vm:revizor_dir/$x86_executor.dbg . 74 | # gdb 75 | # target remote localhost:1234 76 | # add-symbol-file x86_executor.dbg # addr is the address printed by the cat command above 77 | # b run_experiment # or any other function that you want to debug 78 | # (if guest VM path does not match the host path): 79 | # set substitute-path /home/revizor_dir /home/revizor_dir 80 | # c 81 | -------------------------------------------------------------------------------- /tests/x86_tests/asm/calls.asm: -------------------------------------------------------------------------------- 1 | .intel_syntax noprefix 2 | .test_case_enter: 3 | .section .data.main 4 | .function_0: 5 | call .function_1 6 | .function_1: 7 | add rsp, 8 8 | call .function_2 9 | .function_2: 10 | add rsp, 8 11 | call .function_3 12 | .function_3: 13 | add rsp, 8 14 | call .function_4 15 | .function_4: 16 | add rsp, 8 17 | call .function_5 18 | .function_5: 19 | add rsp, 8 20 | call .function_6 21 | .function_6: 22 | add rsp, 8 23 | call .function_7 24 | .function_7: 25 | add rsp, 8 26 | call .function_8 27 | .function_8: 28 | add rsp, 8 29 | call .function_9 30 | .function_9: 31 | add rsp, 8 32 | call .function_10 33 | .function_10: 34 | add rsp, 8 35 | call .function_11 36 | .function_11: 37 | add rsp, 8 38 | call .function_12 39 | .function_12: 40 | add rsp, 8 41 | call .function_13 42 | .function_13: 43 | add rsp, 8 44 | call .function_14 45 | .function_14: 46 | add rsp, 8 47 | call .function_15 48 | .function_15: 49 | add rsp, 8 50 | call .function_16 51 | .function_16: 52 | add rsp, 8 53 | call .function_17 54 | .function_17: 55 | add rsp, 8 56 | call .function_18 57 | .function_18: 58 | add rsp, 8 59 | call .function_19 60 | .function_19: 61 | add rsp, 8 62 | call .function_20 63 | .function_20: 64 | add rsp, 8 65 | call .function_21 66 | .function_21: 67 | add rsp, 8 68 | call .function_22 69 | .function_22: 70 | add rsp, 8 71 | call .function_23 72 | .function_23: 73 | add rsp, 8 74 | call .function_24 75 | .function_24: 76 | add rsp, 8 77 | call .function_25 78 | .function_25: 79 | add rsp, 8 80 | call .function_26 81 | .function_26: 82 | add rsp, 8 83 | call .function_27 84 | .function_27: 85 | add rsp, 8 86 | call .function_28 87 | .function_28: 88 | add rsp, 8 89 | call .function_29 90 | .function_29: 91 | add rsp, 8 92 | call .function_30 93 | .function_30: 94 | add rsp, 8 95 | call .function_31 96 | .function_31: 97 | add rsp, 8 98 | call .function_32 99 | .function_32: 100 | add rsp, 8 101 | call .function_33 102 | .function_33: 103 | add rsp, 8 104 | call .function_34 105 | .function_34: 106 | add rsp, 8 107 | call .function_35 108 | .function_35: 109 | add rsp, 8 110 | call .function_36 111 | .function_36: 112 | add rsp, 8 113 | call .function_37 114 | .function_37: 115 | add rsp, 8 116 | call .function_38 117 | .function_38: 118 | add rsp, 8 119 | call .function_39 120 | .function_39: 121 | add rsp, 8 122 | call .function_40 123 | .function_40: 124 | add rsp, 8 125 | call .function_41 126 | .function_41: 127 | add rsp, 8 128 | call .function_42 129 | .function_42: 130 | add rsp, 8 131 | call .function_43 132 | .function_43: 133 | add rsp, 8 134 | call .function_44 135 | .function_44: 136 | add rsp, 8 137 | call .function_45 138 | .function_45: 139 | add rsp, 8 140 | call .function_46 141 | .function_46: 142 | add rsp, 8 143 | call .function_47 144 | .function_47: 145 | add rsp, 8 146 | call .function_48 147 | .function_48: 148 | add rsp, 8 149 | call .function_49 150 | .function_49: 151 | add rsp, 8 152 | call .function_50 153 | .function_50: 154 | add rsp, 8 155 | .test_case_exit: 156 | -------------------------------------------------------------------------------- /tests/unit_isa_loader.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright (C) Microsoft Corporation 3 | SPDX-License-Identifier: MIT 4 | """ 5 | import unittest 6 | 7 | import os 8 | import tempfile 9 | 10 | from src.isa_loader import InstructionSet 11 | from src.interfaces import OT, InstructionSpec 12 | 13 | basic = """ 14 | [ 15 | {"name": "test", "category": "CATEGORY", "control_flow": true, 16 | "operands": [ 17 | {"type_": "MEM", "values": [], "src": true, "dest": true, "width": 16}, 18 | {"type_": "REG", "values": ["ax"], "src": true, "dest": false, "width": 16} 19 | ], 20 | "implicit_operands": [ 21 | {"type_": "FLAGS", "values": ["w", "r", "undef", "w", "w", "", "", "", "w"], 22 | "src": false, "dest": false, "width": 0} 23 | ] 24 | } 25 | ] 26 | """ 27 | 28 | duplicate = """ 29 | [ 30 | {"name": "test", "category": "CATEGORY", "control_flow": false, 31 | "operands": [ 32 | {"type_": "MEM", "values": [], "src": true, "dest": true, "width": 16} 33 | ], 34 | "implicit_operands": [] 35 | }, 36 | {"name": "test", "category": "CATEGORY", "control_flow": false, 37 | "operands": [ 38 | {"type_": "MEM", "values": [], "src": true, "dest": true, "width": 16} 39 | ], 40 | "implicit_operands": [] 41 | } 42 | ] 43 | """ 44 | 45 | 46 | class InstructionSetParserTest(unittest.TestCase): 47 | 48 | def test_parsing(self): 49 | spec_file = tempfile.NamedTemporaryFile("w", delete=False) 50 | with open(spec_file.name, "w") as f: 51 | f.write(basic) 52 | 53 | instruction_set = InstructionSet(spec_file.name) 54 | spec_file.close() 55 | os.unlink(spec_file.name) 56 | 57 | spec: InstructionSpec = instruction_set.instructions[0] 58 | self.assertEqual(spec.name, "test") 59 | self.assertEqual(spec.category, "CATEGORY") 60 | self.assertEqual(spec.has_mem_operand, True) 61 | self.assertEqual(spec.has_write, True) 62 | self.assertEqual(spec.control_flow, True) 63 | 64 | self.assertEqual(len(spec.operands), 2) 65 | op1 = spec.operands[0] 66 | self.assertEqual(op1.type, OT.MEM) 67 | self.assertEqual(op1.width, 16) 68 | self.assertEqual(op1.src, True) 69 | self.assertEqual(op1.dest, True) 70 | 71 | op2 = spec.operands[1] 72 | self.assertEqual(op2.type, OT.REG) 73 | self.assertEqual(op2.values, ["ax"]) 74 | self.assertEqual(op2.src, True) 75 | self.assertEqual(op2.dest, False) 76 | 77 | self.assertEqual(len(spec.implicit_operands), 1) 78 | flags = spec.implicit_operands[0] 79 | self.assertEqual(flags.type, OT.FLAGS) 80 | self.assertEqual(flags.values, ['w', 'r', 'undef', 'w', 'w', '', '', '', 'w']) 81 | 82 | def test_dedup_identical(self): 83 | spec_file = tempfile.NamedTemporaryFile("w", delete=False) 84 | with open(spec_file.name, "w") as f: 85 | f.write(duplicate) 86 | 87 | instruction_set = InstructionSet(spec_file.name) 88 | spec_file.close() 89 | os.unlink(spec_file.name) 90 | 91 | self.assertEqual(len(instruction_set.instructions), 1, "No deduplication") 92 | -------------------------------------------------------------------------------- /tests/unit_docs.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright (C) Microsoft Corporation 3 | SPDX-License-Identifier: MIT 4 | """ 5 | import unittest 6 | import inspect 7 | import pathlib 8 | from src.config import CONF 9 | 10 | FILE_DIR = pathlib.Path(__file__).parent.resolve() 11 | DOC_DIR = FILE_DIR.parent / "docs" 12 | 13 | 14 | class DocumentationTest(unittest.TestCase): 15 | """ 16 | A class for testing if the documentation is up to date. 17 | """ 18 | longMessage = False 19 | 20 | def test_conf_docs(self): 21 | """ 22 | Test if the documentation contains all the config options. 23 | """ 24 | # get the text of the config documentation 25 | with open(DOC_DIR / "user/config.md", "r") as f: 26 | doc_text = f.read() 27 | 28 | # get a list of config options 29 | options = [ 30 | k[0] 31 | for k in inspect.getmembers(CONF, lambda x: not inspect.isroutine(x)) 32 | if not k[0].startswith("_") 33 | ] 34 | 35 | # check if each option is in the documentation 36 | for option in options: 37 | self.assertTrue(option in doc_text, msg=f"{option} not found in documentation") 38 | 39 | def test_conf_options_docs(self): 40 | """ 41 | Test if the documentation contains all possible values for the config options. 42 | """ 43 | # get the text of the config documentation 44 | with open(DOC_DIR / "user/config.md", "r") as f: 45 | doc_text = f.readlines() 46 | 47 | # build a map of config options to their possible values in the doc 48 | doc_options = {} 49 | curr_name = "" 50 | for line in doc_text: 51 | if line.startswith("Name"): 52 | curr_name = line.split(":")[1].strip() 53 | doc_options[curr_name] = ["", []] 54 | elif line.startswith("Actor Option:"): 55 | curr_name = f"actor_{line.split(':')[1].strip()}" 56 | doc_options[curr_name] = ["", []] 57 | elif line.startswith("Default:"): 58 | doc_options[curr_name][0] = line.split(":")[1].strip().strip("'") 59 | elif line.startswith("Options:"): 60 | if "(" in line: 61 | continue 62 | doc_options[curr_name][1] = line.split(":")[1].strip().split("|") 63 | for i in range(len(doc_options[curr_name][1])): 64 | doc_options[curr_name][1][i] = doc_options[curr_name][1][i].strip().strip("'") 65 | 66 | # get a list of config options 67 | options = [ 68 | k for k in inspect.getmembers(CONF, lambda x: not inspect.isroutine(x)) 69 | if not k[0].startswith("_") 70 | ] 71 | alternatives = CONF._option_values 72 | 73 | # check if all alternatives and defaults are documented 74 | for name, default_ in options: 75 | if name not in doc_options: 76 | continue 77 | if not doc_options[name][0].startswith("("): 78 | self.assertEqual( 79 | str(default_), 80 | doc_options[name][0], 81 | msg=f"Default for `{name}` is incorrect: {default_} != {doc_options[name][0]}" 82 | ) 83 | 84 | if doc_options[name][1]: 85 | doc_values = doc_options[name][1] 86 | self.assertSetEqual( 87 | set(alternatives[name]), 88 | set(doc_values), 89 | msg=f"Options for `{name}` are incorrect: {alternatives[name]} != {doc_values}" 90 | ) 91 | -------------------------------------------------------------------------------- /src/x86/executor/data_loader.c: -------------------------------------------------------------------------------- 1 | /// File: 2 | /// - Parsing inputs and test cases 3 | /// 4 | // Copyright (C) Microsoft Corporation 5 | // SPDX-License-Identifier: MIT 6 | 7 | #include "data_loader.h" 8 | #include "actor.h" 9 | #include "input_parser.h" 10 | #include "main.h" 11 | #include "sandbox_manager.h" 12 | #include "shortcuts.h" 13 | 14 | /// @brief This function serves a dual purpose: 15 | /// - it initializes the data area of the sandbox with the values from the current input 16 | /// - it (indirectly) sets the microarchitectural state of some of the memory buffers (e.g., the 17 | /// store buffer) to a the state that depends on the current input; hence, we reduce the 18 | /// non-determinism of the measurements 19 | /// @param input_id 20 | /// @return 21 | int load_sandbox_data(int input_id) 22 | { 23 | // NOTE: this function intentionally does not use memset (with a few exceptions), because 24 | // we found that direct initialization is more effective at priming the uarch state 25 | 26 | for (int actor_id = 0; actor_id < n_actors; actor_id++) { 27 | actor_data_t *dest = &sandbox->data[actor_id]; 28 | input_fragment_t *source = get_input_fragment_unsafe(input_id, actor_id); 29 | 30 | // Zero-initialize the areas surrounding the sandbox 31 | if (!quick_and_dirty_mode) { 32 | memset(&dest->underflow_pad[0], 0, UNDERFLOW_PAD_SIZE * sizeof(char)); 33 | for (int j = 0; j < OVERFLOW_PAD_SIZE / 8; j += 1) { 34 | // ((uint64_t *) sandbox->underflow_pad)[j] = 0; 35 | ((uint64_t *)dest->overflow_pad)[j] = 0; 36 | } 37 | } 38 | 39 | // Initialize the main and faulty areas of the sandbox data 40 | uint64_t *main_src = (uint64_t *)source->main_area; 41 | uint64_t *main_dest = (uint64_t *)dest->main_area; 42 | for (int j = 0; j < MAIN_AREA_SIZE / 8; j += 1) { 43 | main_dest[j] = main_src[j]; 44 | } 45 | 46 | uint64_t *faulty_src = (uint64_t *)source->faulty_area; 47 | uint64_t *faulty_dest = (uint64_t *)dest->faulty_area; 48 | for (int j = 0; j < FAULTY_AREA_SIZE / 8; j += 1) { 49 | faulty_dest[j] = faulty_src[j]; 50 | } 51 | 52 | // Initial register values 53 | // (the registers will be set to these values in code_loader template) 54 | uint64_t *reg_src = (uint64_t *)source->reg_init_region; 55 | uint64_t *reg_dest = (uint64_t *)dest->reg_init_area; 56 | for (int j = 0; j < REG_INIT_AREA_SIZE / 8; j += 1) { 57 | reg_dest[j] = reg_src[j]; 58 | } 59 | 60 | // - Ensure that the flags are valid 61 | reg_dest[6] = (reg_src[6] & 2263) | 2; 62 | 63 | // - RSP and RBP are do not take a value from the input, 64 | // and are rather set to the stack base 65 | 66 | // stack pointer for actor 0 67 | reg_dest[7] = (uint64_t)sandbox->data[0].main_area + LOCAL_RSP_OFFSET; 68 | } 69 | 70 | // - Initialize SIMD registers 71 | // Note: GPRs will be initialized directly by the test case template; see code_loader.c 72 | uint64_t *simd_src = (uint64_t *)&get_input_fragment_unsafe(input_id, 0)->reg_init_region[64]; 73 | asm volatile("" 74 | "movdqa 0x00(%0), %%xmm0\n" 75 | "movdqa 0x10(%0), %%xmm1\n" 76 | "movdqa 0x20(%0), %%xmm2\n" 77 | "movdqa 0x30(%0), %%xmm3\n" 78 | "movdqa 0x40(%0), %%xmm4\n" 79 | "movdqa 0x50(%0), %%xmm5\n" 80 | "movdqa 0x60(%0), %%xmm6\n" 81 | "movdqa 0x70(%0), %%xmm7\n" 82 | "movdqa 0x80(%0), %%xmm8\n" 83 | "movdqa 0x90(%0), %%xmm9\n" 84 | "movdqa 0xa0(%0), %%xmm10\n" 85 | "movdqa 0xb0(%0), %%xmm11\n" 86 | "movdqa 0xc0(%0), %%xmm12\n" 87 | "movdqa 0xd0(%0), %%xmm13\n" 88 | "movdqa 0xe0(%0), %%xmm14\n" 89 | "movdqa 0xf0(%0), %%xmm15\n" ::"r"(&simd_src[0])); 90 | 91 | return 0; 92 | } 93 | 94 | // ================================================================================================= 95 | int init_data_loader(void) { return 0; } 96 | 97 | void free_data_loader(void) {} 98 | -------------------------------------------------------------------------------- /src/x86/executor/include/svm.h: -------------------------------------------------------------------------------- 1 | /// File: Header for svm.c 2 | /// 3 | // Copyright (C) Microsoft Corporation 4 | // SPDX-License-Identifier: MIT 5 | 6 | #ifndef _X86_EXECUTOR_SVM_H_ 7 | #define _X86_EXECUTOR_SVM_H_ 8 | 9 | #include 10 | #include 11 | 12 | #include "svm_constants.h" 13 | 14 | // ================================================================================================= 15 | // Virtual Machine Control Block (VMCB) definitions 16 | #define VMCB_SIZE PAGE_SIZE 17 | 18 | typedef struct { 19 | uint32_t intercept_cr; 20 | uint32_t intercept_dr; 21 | uint32_t intercept_exceptions; 22 | uint64_t intercept; 23 | uint32_t intercept_ext; 24 | uint8_t reserved_1[36]; 25 | uint16_t pause_filter_thresh; 26 | uint16_t pause_filter_count; 27 | uint64_t iopm_base_pa; 28 | uint64_t msrpm_base_pa; 29 | uint64_t tsc_offset; 30 | uint32_t asid; 31 | uint8_t tlb_ctl; 32 | uint8_t reserved_2[3]; 33 | uint32_t int_ctl; 34 | uint8_t int_vector; 35 | uint8_t reserved_3[3]; 36 | uint8_t int_state; 37 | uint8_t reserved_4[7]; 38 | uint64_t exit_code; 39 | uint64_t exit_info_1; 40 | uint64_t exit_info_2; 41 | uint64_t exit_int_info; 42 | uint64_t nested_ctl; 43 | uint64_t avic_vapic_bar; 44 | uint8_t reserved_5[8]; 45 | uint32_t event_inj; 46 | uint32_t event_inj_err; 47 | uint64_t nested_cr3; 48 | uint64_t virt_ext; 49 | uint32_t clean; 50 | uint32_t reserved_6; 51 | uint64_t next_rip; 52 | uint8_t insn_len; 53 | uint8_t insn_bytes[15]; 54 | uint64_t avic_backing_page; 55 | uint8_t reserved_7[8]; 56 | uint64_t avic_logical_id; 57 | uint64_t avic_physical_id; 58 | uint8_t reserved_8[768]; 59 | } __attribute__((__packed__)) vmcb_control_t; 60 | 61 | typedef struct { 62 | uint16_t selector; 63 | uint16_t attrib; 64 | uint32_t limit; 65 | uint64_t base; 66 | } __attribute__((__packed__)) seg_t; 67 | 68 | typedef struct { 69 | seg_t es; 70 | seg_t cs; 71 | seg_t ss; 72 | seg_t ds; 73 | seg_t fs; 74 | seg_t gs; 75 | seg_t gdtr; 76 | seg_t ldtr; 77 | seg_t idtr; 78 | seg_t tr; 79 | uint8_t reserved_1[43]; 80 | uint8_t cpl; 81 | uint8_t reserved_2[4]; 82 | uint64_t efer; 83 | uint64_t reserved_2a; 84 | uint64_t perf_ctl0; 85 | uint64_t perf_ctr0; 86 | uint64_t perf_ctl1; 87 | uint64_t perf_ctr1; 88 | uint64_t perf_ctl2; 89 | uint64_t perf_ctr2; 90 | uint64_t perf_ctl3; 91 | uint64_t perf_ctr3; 92 | uint64_t perf_ctl4; 93 | uint64_t perf_ctr4; 94 | uint64_t perf_ctl5; 95 | uint64_t perf_ctr5; 96 | uint64_t reserved_3; 97 | // uint8_t reserved_3[112]; 98 | uint64_t cr4; 99 | uint64_t cr3; 100 | uint64_t cr0; 101 | uint64_t dr7; 102 | uint64_t dr6; 103 | uint64_t rflags; 104 | uint64_t rip; 105 | uint8_t reserved_4[88]; 106 | uint64_t rsp; 107 | uint8_t reserved_5[24]; 108 | uint64_t rax; 109 | uint64_t star; 110 | uint64_t lstar; 111 | uint64_t cstar; 112 | uint64_t sfmask; 113 | uint64_t kernel_gs_base; 114 | uint64_t sysenter_cs; 115 | uint64_t sysenter_esp; 116 | uint64_t sysenter_eip; 117 | uint64_t cr2; 118 | uint8_t reserved_6[32]; 119 | uint64_t g_pat; 120 | uint64_t dbgctl; 121 | uint64_t br_from; 122 | uint64_t br_to; 123 | uint64_t last_excp_from; 124 | uint64_t last_excp_to; 125 | } __attribute__((__packed__)) vmcb_save_t; 126 | 127 | typedef struct { 128 | vmcb_control_t control; 129 | vmcb_save_t save; 130 | } __attribute__((packed)) vmcb_t; 131 | 132 | 133 | // ================================================================================================= 134 | // Module interface 135 | #define VMCB_RIP_OFFSET offsetof(vmcb_t, save.rip) 136 | 137 | extern bool svm_is_on; 138 | extern uint64_t *vmcb_hpas; 139 | extern uint64_t *vmcb_hvas; 140 | 141 | int svm_check_cpu_compatibility(void); 142 | int start_svm_operation(void); 143 | void stop_svm_operation(void); 144 | int store_orig_vmcb_state(void); 145 | void restore_orig_vmcb_state(void); 146 | int set_vmcb_state(void); 147 | int print_svm_exit_info(void); 148 | 149 | int init_svm(void); 150 | void free_svm(void); 151 | 152 | #endif // _X86_EXECUTOR_SVM_H_ 153 | -------------------------------------------------------------------------------- /docs/registers.md: -------------------------------------------------------------------------------- 1 | # Register Allocation 2 | 3 | The test cases are executed in a sandboxed environment, some of the registers are reserved for internal use, and some are available for use in the test cases. 4 | Below is a list of registers and their purpose. 5 | 6 | ## `R15` 7 | 8 | Contains the base address of the UTILITY area in the [sandbox](sandbox.md). 9 | 10 | If the test case does not enter a VM, the register value remains constant during the execution of the test cases. 11 | Otherwise, the register value is updated to point to the UTILITY area of the currently active VM when the `switch_h2g` macro is called, and it is restored to the original value when the `switch_g2h` macro is called. 12 | 13 | The register is used by internal functions, such as the implementation of Prime+Probe. 14 | 15 | ## `R14` 16 | 17 | Contains the base address of the current actor's [sandbox](sandbox.md) (namely, it points to the base of the actor's MAIN area). 18 | 19 | At the beginning of the test case execution, the register is set to the base address of the MAIN area of the first actor (actor `main`). The register value is updated to point to the MAIN area of the currently active actor when a macro from the `landing_*` group of macros is called. It is also updated by the `fault_handler` macro. 20 | 21 | The register is used in test cases as a part of the sandboxing mechanism. 22 | For example, all generated memory accesses are relative to the value stored in `R14`, and have the form of `[R14 + offset]`. 23 | 24 | 25 | ## `R13` (`HTRACE_REGISTER` constant in the kernel module) 26 | 27 | Contains either intermediate or final result of the hardware trace measurements. 28 | 29 | Before entering the test case, the register is set to 0. 30 | When a `measurement_start` macro is executed, the register is (optionally) set to the starting value, 31 | such a initial reading of time stamp counter when the `TSC` mode is used. 32 | When a `measurement_end` macro is executed, the register is updated with the final value of the measurement and contains the resulting hardware trace. 33 | 34 | ## `R12` (`STATUS_REGISTER` constant in the kernel module) 35 | 36 | Contains a compressed status of the test case execution: 37 | 38 | Bits[0:7] contain a measurement status. 39 | At the beginning of the test case execution, the bits are set to 0. 40 | When `measurement_start` macro is executed, the bits are set to 1. 41 | When `measurement_end` macro is executed, the bits are set to 2. 42 | If the measurement status is not 2 at the end of the test case execution, the kernel module will report an error. 43 | 44 | Bits[8:31] are unused. 45 | 46 | Bits[32:63] contain a counter of SMI (System Management Interrupt) events. 47 | The counter is set automatically before entering the test case (`READ_SMI_START`), and updated when the test case finishes (`READ_SMI_END`). 48 | If the difference between the readings is not 0, the kernel module will report an error. 49 | 50 | ## `R11` 51 | 52 | The register is used as a temporary buffer by some of the macros. 53 | 54 | Before entering the test case, the register is set to 0. 55 | When certain macros are executed (e.g., `set_k2u_target`), the register will contain temporary values. 56 | The register should not be used in the test case, as the temporary value may be consumed by latter macros. 57 | 58 | ## `R10, R9, R8` 59 | 60 | Stores the values of performance counters. 61 | `R10` stores the value of performance counter #1, `R9` stores the value of performance counter #2, and `R8` stores the value of performance counter #3. 62 | 63 | Before entering the test case, the registers are set to 0. 64 | When a `measurement_start` macro is executed, the registers are (optionally) set to the starting values. 65 | When a `measurement_end` macro is executed, the registers are updated with the final values of the measurements. 66 | 67 | 68 | ## Other General Purpose Registers 69 | 70 | The remaining registers (`rax`, `rcx`, `rdx`, `rsi`, `rdi`, `rflags`) are available for use in the test cases and can be modified freely. 71 | A special case are `rsp` and `rbp`, which can be used in the test cases, but their values must always remain within the sandbox (see [Sandbox](sandbox.md)). 72 | 73 | ## Vector Registers 74 | 75 | Vector registers (`xmm0`-`xmm15`) are also available for use in the test cases. 76 | However, only `xmm0-xmm7` are initialized with input-based values, and the remaining registers are always zero-initialized. 77 | 78 | Large-size vector registers (`ymm` and `zmm`) are not supported. 79 | -------------------------------------------------------------------------------- /docs/user/modes.md: -------------------------------------------------------------------------------- 1 | # Modes of Operation 2 | 3 | Revizor supports several modes of operation, each targeting a different use cases. 4 | The selection of the mode is described in the [CLI documentation](cli.md). 5 | Below is a brief description of each mode. 6 | 7 | 8 | | Mode | CLI Key | Use Case | Description | 9 | | ---------------- | ------------- | ------------------------ | ---------------------------------------------------------------------------------------------------------- | 10 | | Fuzzing | fuzz | General Testing | Test a CPU against a contract model. Test cases generated randomly | 11 | | Template Fuzzing | tfuzz | Targeted Testing | Test a CPU against a contract model. Test cases generated based on a template | 12 | | Reproduce | reproduce | Reproducing a Violation | Reproduce a violation found by fuzzing OR run a manually-written test case | 13 | | Minimization | minimize | Violation Simplification | Simplify a test case by applying a series of simplification passes to the test case program and its inputs | 14 | | Trace Analysis | analyse | Stand-alone Analysis | Analyze pre-recorded traces for violations | 15 | | Generation | generate | Stand-alone Generation | Only generate test cases, without testing them | 16 | | ISA Spec Install | download_spec | Tool Installation | Call a script that downloads the instruction set specification | 17 | 18 | 19 | ## Fuzzing and Template Fuzzing Modes 20 | 21 | Two main modes of operation in revizor are fuzzing and template fuzzing. 22 | These modes are used to test a CPU against a contract model. 23 | In both modes, revizor generates test cases and executes them on the target CPU and the model, records the corresponding traces, and checks if the hardware traces contain the same (or less) information as the contract traces. 24 | 25 | In the fuzzing mode, test cases are generated randomly, with the instruction set and size of test cases defined by the config file. 26 | This mode is used for broad testing of the CPU. 27 | 28 | In the template fuzzing mode, test cases are generated based on a template: 29 | The generator takes an assembly template as an input, and produces a test case by expanding the `random_instructions` macro in the template. 30 | This mode is used to narrow down the fuzzing space and focus on specific scenarios, such as testing microarchitectural patches or certain interactions between actors. 31 | 32 | ## Reproduce 33 | 34 | In this mode, Revizor loads a test case from a set of files and runs a single round of the fuzzer with this test case. 35 | The test case is usually a violation previously found in the (template) fuzzing mode, but it can also be written manually. 36 | 37 | There are three main use cases for this mode: 38 | 39 | 1. **Analysis of the violation**: to understand the root cause of the violation, the user may manually modify the test case and re-run it in the reproduce mode to see if the violation is still present. 40 | 2. **Reproducibility check**: to check if a violation is reproducible on different CPUs, or on different configurations of the same CPU (e.g., after a microcode patch has been applied). 41 | 3. **Manual testing**: to test a manually-written test case. 42 | 43 | ## Minimization 44 | 45 | In this mode, Revizor takes a test case that causes a violation and applies a series of simplification passes to the test case program and its inputs. 46 | The goal is to reduce the test case to its minimal form to simplify the root cause analysis of the violation. 47 | Revizor supports an extensive list of passes, described in the [minimization documentation](minimization.md). 48 | 49 | ## Stand-alone Interfaces 50 | 51 | The `analyse` and `generate` modes are used to perform stand-alone access to modules of Revizor. 52 | In the `analyse` mode, the user can analyze pre-recorded traces for violations. 53 | In the `generate` mode, the user can generate test cases without testing them. 54 | 55 | ## ISA Spec Install 56 | 57 | The `download_spec` mode isn't used for testing, but rather for tool installation. 58 | It provides an interface to download, parse, and store the instruction specifications for the tested ISA in the JSON format. 59 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Revizor 2 | 3 | ![GitHub](https://img.shields.io/github/license/microsoft/sca-fuzzer) 4 | ![PyPI](https://img.shields.io/pypi/v/revizor-fuzzer) 5 | ![GitHub all releases](https://img.shields.io/github/downloads/microsoft/sca-fuzzer/total) 6 | ![GitHub contributors](https://img.shields.io/github/contributors/microsoft/sca-fuzzer) 7 | 8 | 9 | Revizor is a security-oriented fuzzer for detecting information leaks in CPUs, such as [Spectre and Meltdown](https://meltdownattack.com/). 10 | It tests CPUs against [Leakage Contracts](https://arxiv.org/abs/2006.03841) and searches for unexpected leaks. 11 | 12 | 13 | 14 | ## Getting Started and Documentation 15 | 16 | You can find a quick start guide at [Quick Start](https://microsoft.github.io/sca-fuzzer/quick-start/). 17 | 18 | For information on how to use Revizor, see [User Documentation](https://microsoft.github.io/sca-fuzzer/user/). 19 | 20 | For information on how to contribute to Revizor, see [CONTRIBUTING.md](CONTRIBUTING.md). 21 | 22 | ## Need Help with Revizor? 23 | 24 | If you find a bug in Revizor, don't hesitate to [open an issue](https://github.com/microsoft/sca-fuzzer/issues). 25 | 26 | If something is confusing or you need help in using Revizor, we have a [discussion page](https://github.com/microsoft/sca-fuzzer/discussions). 27 | 28 | ## Citing Revizor 29 | 30 | To cite this project, you can use any of the following references: 31 | 32 | 1. Original paper that introduced the concept of Model-based Relation Testing as well as the Revizor tool: 33 | 34 | Oleksii Oleksenko, Christof Fetzer, Boris Köpf, Mark Silberstein. "[Revizor: Testing Black-box CPUs against Speculation Contracts](https://www.microsoft.com/en-us/research/publication/revizor-testing-black-box-cpus-against-speculation-contracts/)" in Proceedings of the 27th ACM International Conference on Architectural Support for Programming Languages and Operating Systems (ASPLOS), 2022. 35 | 36 | 2. Theoretical foundations of leakage contract: 37 | 38 | Marco Guarnieri, Boris Köpf, Jan Reineke, and Pepe Vila. "[Hardware-software contracts for secure speculation](https://www.microsoft.com/en-us/research/publication/hardware-software-contracts-for-secure-speculation/)" in Proceedings of the 2021 IEEE Symposium on Security and Privacy (SP), 2021. 39 | 40 | 3. Accessible summary of the two papers above, in a journal format: 41 | 42 | Oleksii Oleksenko, Christof Fetzer, Boris Köpf, Mark Silberstein. "Revizor: Testing Black-box CPUs against Speculation Contracts". In IEEE Micro, 2023. 43 | 44 | 4. Paper that introduced speculation filtering, observation filtering, and contract-based input generation: 45 | 46 | Oleksii Oleksenko, Marco Guarnieri, Boris Köpf, and Mark Silberstein. "[Hide and Seek with Spectres: Efficient discovery of speculative information leaks with random testing](https://www.microsoft.com/en-us/research/publication/hide-and-seek-with-spectres-efficient-discovery-of-speculative-information-leaks-with-random-testing/)" in Proceedings of the 2023 IEEE Symposium on Security and Privacy (SP), 2022. 47 | 48 | 5. Paper that introduced exception-based testing (i.e., focus on Meltdown, Foreshadow) into Revizor: 49 | 50 | Jana Hofmann, Emanuele Vannacci, Cédric Fournet, Boris Köpf, and Oleksii Oleksenko. "[Speculation at Fault: Modeling and Testing Microarchitectural Leakage of CPU Exceptions.](https://www.usenix.org/conference/usenixsecurity23/presentation/hofmann)" in Proceedings of 32nd USENIX Security Symposium (USENIX Security), 2023. 51 | 52 | 6. Paper that introduced testing of cross-VM and user-kernel leaks in Revizor, as well as presented TSA attacks on AMD CPUs: 53 | 54 | Oleksii Oleksenko, Flavien Solt, Cédric Fournet, Jana Hofmann, Boris Köpf, and Stavros Volos. "[Enter, Exit, Page Fault, Leak: Testing Isolation Boundaries for Microarchitectural Leaks](https://www.microsoft.com/en-us/research/wp-content/uploads/2025/07/Enter-Exit-SP26.pdf)" (to be published) in Proceedings of the 2026 IEEE Symposium on Security and Privacy (SP), 2026. 55 | 56 | ## Trademarks 57 | 58 | This project may contain trademarks or logos for projects, products, or services. Authorized use of Microsoft 59 | trademarks or logos is subject to and must follow 60 | [Microsoft's Trademark & Brand Guidelines](https://www.microsoft.com/en-us/legal/intellectualproperty/trademarks/usage/general). 61 | Use of Microsoft trademarks or logos in modified versions of this project must not cause confusion or imply Microsoft sponsorship. 62 | Any use of third-party trademarks or logos are subject to those third-party's policies. 63 | -------------------------------------------------------------------------------- /src/x86/executor/include/sandbox_manager.h: -------------------------------------------------------------------------------- 1 | /// File: Header for sandbox management 2 | /// 3 | // Copyright (C) Microsoft Corporation 4 | // SPDX-License-Identifier: MIT 5 | 6 | #ifndef _SANDBOX_MANAGER_H_ 7 | #define _SANDBOX_MANAGER_H_ 8 | 9 | #include 10 | 11 | #include "hardware_desc.h" // L1D_ASSOCIATIVITY 12 | #include "measurement.h" // measurement_t 13 | 14 | // ================================================================================================= 15 | // Sandbox data layout 16 | // ================================================================================================= 17 | #define L1D_PRIMING_AREA_SIZE (L1D_ASSOCIATIVITY * 4096) 18 | 19 | // layout of actor_data_t 20 | #define MACRO_STACK_SIZE 64 21 | #define UNDERFLOW_PAD_SIZE (4096 - MACRO_STACK_SIZE) 22 | #define MAIN_AREA_SIZE 4096 23 | #define FAULTY_AREA_SIZE 4096 24 | #define REG_INIT_AREA_SIZE 320 // 8 64-bit GPRs + 8 256-bit YMMs 25 | #define OVERFLOW_PAD_SIZE (4096 - REG_INIT_AREA_SIZE) 26 | 27 | // offsets w.r.t. the base of util_t (r15 will be initialized to point there) 28 | #define L1D_PRIMING_OFFSET 0 29 | #define STORED_RSP_OFFSET (L1D_PRIMING_AREA_SIZE) 30 | #define MEASUREMENT_OFFSET (STORED_RSP_OFFSET + 8) 31 | #define NESTED_FAULT_OFFSET (MEASUREMENT_OFFSET + sizeof(measurement_t)) 32 | 33 | // offset of util_t w.r.t. the base of main_area of the main actor 34 | #define UTIL_REL_TO_MAIN (L1D_PRIMING_AREA_SIZE + 4096 + UNDERFLOW_PAD_SIZE + MACRO_STACK_SIZE) 35 | 36 | // offsets w.r.t. the base of main_area of the current actor (r14 will contain the base) 37 | #define MACRO_STACK_TOP_OFFSET (UNDERFLOW_PAD_SIZE) 38 | #define MAIN_AREA_OFFSET 0 39 | #define FAULTY_AREA_OFFSET (MAIN_AREA_SIZE) 40 | #define REG_INIT_OFFSET (FAULTY_AREA_OFFSET + FAULTY_AREA_SIZE) 41 | #define OVERFLOW_PAD_OFFSET (REG_INIT_OFFSET + REG_INIT_AREA_SIZE) 42 | #define LOCAL_RSP_OFFSET (FAULTY_AREA_OFFSET - 8) 43 | 44 | // area page IDs 45 | #define MAIN_PAGE_ID (MACRO_STACK_SIZE + UNDERFLOW_PAD_SIZE) / PAGE_SIZE 46 | #define FAULTY_PAGE_ID (MACRO_STACK_SIZE + UNDERFLOW_PAD_SIZE + MAIN_AREA_SIZE) / PAGE_SIZE 47 | 48 | /// @brief Utility data structure used by various primitives in the test case. 49 | /// Must be allocated strictly before the main actor data as its code accesses 50 | /// fields of util_t by using constant offsets from the base of its main_area. 51 | typedef struct { 52 | uint8_t l1d_priming_area[L1D_PRIMING_AREA_SIZE]; 53 | uint64_t stored_rsp; // stores the stack pointer before calling the test case 54 | measurement_t latest_measurement; // measurement results 55 | uint64_t nested_fault; // non-zero if a fault occurs during a fault handler 56 | uint8_t unused[4096 - 16 - sizeof(measurement_t)]; 57 | } __attribute__((packed)) util_t; 58 | 59 | /// @brief Data structure representing the memory accessible by the actor's code 60 | typedef struct { 61 | uint8_t macro_stack[MACRO_STACK_SIZE]; // stack for storing registers when calling macros 62 | uint8_t underflow_pad[UNDERFLOW_PAD_SIZE]; // zero-initialized region for accidental underflows 63 | uint8_t main_area[MAIN_AREA_SIZE]; // first input page; does not cause faults 64 | uint8_t faulty_area[FAULTY_AREA_SIZE]; // second input page; causes a (configurable) fault 65 | uint8_t reg_init_area[REG_INIT_AREA_SIZE]; // region for initializing registers 66 | uint8_t overflow_pad[OVERFLOW_PAD_SIZE]; // zero-initialized region for accidental overflows 67 | } __attribute__((packed)) actor_data_t; 68 | 69 | // ================================================================================================= 70 | // Sandbox code layout 71 | // ================================================================================================= 72 | #define MAX_EXPANDED_SECTION_SIZE (0x1000 * 2) 73 | #define MAX_EXPANDED_MACROS_SIZE (0x1000) 74 | 75 | typedef struct { 76 | uint8_t section[MAX_EXPANDED_SECTION_SIZE]; 77 | uint8_t macros[MAX_EXPANDED_MACROS_SIZE]; 78 | } __attribute__((packed)) actor_code_t; 79 | 80 | // ================================================================================================= 81 | // sandbox_t 82 | // ================================================================================================= 83 | typedef struct { 84 | actor_data_t *data; 85 | actor_code_t *code; 86 | util_t *util; 87 | } sandbox_t; 88 | 89 | #define N_UTIL_PAGES (sizeof(util_t) / PAGE_SIZE) 90 | #define N_DATA_PAGES_PER_ACTOR (sizeof(actor_data_t) / PAGE_SIZE) 91 | #define N_CODE_PAGES_PER_ACTOR (sizeof(actor_code_t) / PAGE_SIZE) 92 | 93 | extern sandbox_t *sandbox; 94 | 95 | int get_sandbox_size_pages(void); 96 | 97 | int set_sandbox_page_tables(void); 98 | void restore_orig_sandbox_page_tables(void); 99 | 100 | void set_faulty_page_permissions(void); 101 | void restore_faulty_page_permissions(void); 102 | 103 | int allocate_sandbox(void); 104 | int init_sandbox_manager(void); 105 | void free_sandbox_manager(void); 106 | 107 | #endif // _SANDBOX_MANAGER_H_ 108 | -------------------------------------------------------------------------------- /src/x86/executor/include/svm_constants.h: -------------------------------------------------------------------------------- 1 | /// File: Definitions of constants used by AMD SVM (Secure Virtual Machine) technology 2 | /// 3 | // Copyright (C) Microsoft Corporation 4 | // SPDX-License-Identifier: MIT 5 | 6 | #include 7 | 8 | #ifndef _X86_EXECUTOR_SVM_CONSTANTS_H_ 9 | #define _X86_EXECUTOR_SVM_CONSTANTS_H_ 10 | 11 | // ================================================================================================= 12 | // Default values for configuration registers and VMCB fields 13 | 14 | // Could be read from cpuid 15 | #define SVM_MAX_NUM_GUESTS 64 // DO NOT INCREASE without knowing exactly what you are doing 16 | 17 | // ------------------------------------------------------------------------------------------------- 18 | // Guest control registers 19 | #define MUST_SET_BITS_CR0_SVM_GUEST \ 20 | (X86_CR0_PE | X86_CR0_PG | X86_CR0_NE | X86_CR0_WP | X86_CR0_AM | X86_CR0_ET) 21 | #define MUST_CLEAR_BITS_CR0_SVM_GUEST (X86_CR0_NW | X86_CR0_CD) 22 | 23 | #define MUST_SET_BITS_CR4_SVM_GUEST \ 24 | (X86_CR4_PSE | X86_CR4_PAE | X86_CR4_MCE | X86_CR4_PGE | X86_CR4_PCE | X86_CR4_OSFXSR | \ 25 | X86_CR4_OSXMMEXCPT) 26 | #define MUST_CLEAR_BITS_CR4_SVM_GUEST (X86_CR4_VME) 27 | 28 | #define MUST_SET_BITS_EFER_SVM_GUEST (EFER_SCE | EFER_LME | EFER_LMA | EFER_NX | EFER_SVME) 29 | #define MUST_CLEAR_BITS_EFER_SVM_GUEST (EFER_LMSLE) 30 | 31 | // ------------------------------------------------------------------------------------------------- 32 | // Segment attributes 33 | #define MUST_SET_BITS_CS_SVM_GUEST \ 34 | (SVM_SELECTOR_P_MASK | SVM_SELECTOR_L_MASK | SVM_SELECTOR_WRITE_MASK) 35 | #define MUST_SET_BITS_DS_SVM_GUEST \ 36 | (SVM_SELECTOR_P_MASK | SVM_SELECTOR_L_MASK | SVM_SELECTOR_WRITE_MASK) 37 | #define MUST_SET_BITS_SS_SVM_GUEST \ 38 | (SVM_SELECTOR_P_MASK | SVM_SELECTOR_L_MASK | SVM_SELECTOR_WRITE_MASK) 39 | 40 | // ------------------------------------------------------------------------------------------------- 41 | // VMCB control fields 42 | 43 | // ================================================================================================= 44 | // Kernel compatibility: Constant definitions that are either missing in the kernel, or are 45 | // inconsistent between versions 46 | 47 | // We define VMCB bits here, as the definitions within the kernel are not stable between versions 48 | #define VMCB_INTERCEPT_CR0_READ 0 49 | #define VMCB_INTERCEPT_CR3_READ 3 50 | #define VMCB_INTERCEPT_CR4_READ 4 51 | #define VMCB_INTERCEPT_CR8_READ 8 52 | #define VMCB_INTERCEPT_CR0_WRITE (16 + 0) 53 | #define VMCB_INTERCEPT_CR3_WRITE (16 + 3) 54 | #define VMCB_INTERCEPT_CR4_WRITE (16 + 4) 55 | #define VMCB_INTERCEPT_CR8_WRITE (16 + 8) 56 | 57 | #define VMCB_INTERCEPT_DR0_READ 0 58 | #define VMCB_INTERCEPT_DR1_READ 1 59 | #define VMCB_INTERCEPT_DR2_READ 2 60 | #define VMCB_INTERCEPT_DR3_READ 3 61 | #define VMCB_INTERCEPT_DR4_READ 4 62 | #define VMCB_INTERCEPT_DR5_READ 5 63 | #define VMCB_INTERCEPT_DR6_READ 6 64 | #define VMCB_INTERCEPT_DR7_READ 7 65 | #define VMCB_INTERCEPT_DR0_WRITE (16 + 0) 66 | #define VMCB_INTERCEPT_DR1_WRITE (16 + 1) 67 | #define VMCB_INTERCEPT_DR2_WRITE (16 + 2) 68 | #define VMCB_INTERCEPT_DR3_WRITE (16 + 3) 69 | #define VMCB_INTERCEPT_DR4_WRITE (16 + 4) 70 | #define VMCB_INTERCEPT_DR5_WRITE (16 + 5) 71 | #define VMCB_INTERCEPT_DR6_WRITE (16 + 6) 72 | #define VMCB_INTERCEPT_DR7_WRITE (16 + 7) 73 | 74 | enum { 75 | VMCB_INTERCEPT_INTR, 76 | VMCB_INTERCEPT_NMI, 77 | VMCB_INTERCEPT_SMI, 78 | VMCB_INTERCEPT_INIT, 79 | VMCB_INTERCEPT_VINTR, 80 | VMCB_INTERCEPT_SELECTIVE_CR0, 81 | VMCB_INTERCEPT_STORE_IDTR, 82 | VMCB_INTERCEPT_STORE_GDTR, 83 | VMCB_INTERCEPT_STORE_LDTR, 84 | VMCB_INTERCEPT_STORE_TR, 85 | VMCB_INTERCEPT_LOAD_IDTR, 86 | VMCB_INTERCEPT_LOAD_GDTR, 87 | VMCB_INTERCEPT_LOAD_LDTR, 88 | VMCB_INTERCEPT_LOAD_TR, 89 | VMCB_INTERCEPT_RDTSC, 90 | VMCB_INTERCEPT_RDPMC, 91 | VMCB_INTERCEPT_PUSHF, 92 | VMCB_INTERCEPT_POPF, 93 | VMCB_INTERCEPT_CPUID, 94 | VMCB_INTERCEPT_RSM, 95 | VMCB_INTERCEPT_IRET, 96 | VMCB_INTERCEPT_INTn, 97 | VMCB_INTERCEPT_INVD, 98 | VMCB_INTERCEPT_PAUSE, 99 | VMCB_INTERCEPT_HLT, 100 | VMCB_INTERCEPT_INVLPG, 101 | VMCB_INTERCEPT_INVLPGA, 102 | VMCB_INTERCEPT_IOIO_PROT, 103 | VMCB_INTERCEPT_MSR_PROT, 104 | VMCB_INTERCEPT_TASK_SWITCH, 105 | VMCB_INTERCEPT_FERR_FREEZE, 106 | VMCB_INTERCEPT_SHUTDOWN, 107 | VMCB_INTERCEPT_VMRUN, 108 | VMCB_INTERCEPT_VMMCALL, 109 | VMCB_INTERCEPT_VMLOAD, 110 | VMCB_INTERCEPT_VMSAVE, 111 | VMCB_INTERCEPT_STGI, 112 | VMCB_INTERCEPT_CLGI, 113 | VMCB_INTERCEPT_SKINIT, 114 | VMCB_INTERCEPT_RDTSCP, 115 | VMCB_INTERCEPT_ICEBP, 116 | VMCB_INTERCEPT_WBINVD, 117 | VMCB_INTERCEPT_MONITOR, 118 | VMCB_INTERCEPT_MWAIT, 119 | VMCB_INTERCEPT_MWAIT_COND, 120 | VMCB_INTERCEPT_XSETBV, 121 | VMCB_INTERCEPT_RDPRU, 122 | VMCB_INTERCEPT_EFER_WRITE, 123 | }; 124 | 125 | enum { 126 | VMCB_INTERCEPT_ALL_INVLPGB, 127 | VMCB_INTERCEPT_INVALID_INVLPGB, 128 | VMCB_INTERCEPT_INVPCID, 129 | VMCB_INTERCEPT_MCOMMIT, 130 | VMCB_INTERCEPT_TLBSYNC, 131 | VMCB_INTERCEPT_BUS_LOCK, 132 | VMCB_INTERCEPT_HLT_IF_NOT_VINTR, 133 | }; 134 | 135 | #endif // _X86_EXECUTOR_SVM_CONSTANTS_H_ 136 | -------------------------------------------------------------------------------- /src/input_generator.py: -------------------------------------------------------------------------------- 1 | """ 2 | File: Input Generation 3 | 4 | Copyright (C) Microsoft Corporation 5 | SPDX-License-Identifier: MIT 6 | """ 7 | import os 8 | import random 9 | import numpy as np 10 | from typing import List, Tuple, Final 11 | from .interfaces import Input, InputTaint, InputGenerator, InputFragment, GPR_SUBREGION_SIZE 12 | from .config import CONF 13 | from .util import Logger 14 | 15 | POW32 = pow(2, 32) 16 | 17 | 18 | class NumpyRandomInputGenerator(InputGenerator): 19 | """ Numpy-based implementation of the input gen """ 20 | 21 | _state: int = 0 22 | _boosting_state: int = 0 23 | n_actors = 1 24 | max_gpr_value: Final[int] = pow(2, 64) - 1 25 | 26 | def __init__(self, seed: int): 27 | super().__init__(seed) 28 | self.LOG = Logger() 29 | 30 | self.max_input_value = pow(2, CONF.input_gen_entropy_bits) - 1 31 | self._probability_of_zero = CONF.input_gen_probability_of_special_value 32 | self._probability_of_max = CONF.input_gen_probability_of_special_value * 2 33 | assert self._probability_of_max < 1, \ 34 | "The sum of probabilities of special values must be less than 1." 35 | 36 | def _generate_one(self, state: int) -> Tuple[Input, int]: 37 | input_ = Input(self.n_actors) 38 | input_.seed = state 39 | 40 | size = input_.itemsize // 8 41 | 42 | rng = np.random.default_rng(seed=state) 43 | for i in range(len(input_)): 44 | # generate random data 45 | data = rng.integers(self.max_input_value, size=size, dtype=np.uint64) # type: ignore 46 | 47 | # copy lower 32-bits to upper 32-bits, for every 8-byte word 48 | data = (data << np.uint64(32)) + data 49 | 50 | # cast to InputFragment 51 | input_[i] = data.view(InputFragment) 52 | 53 | # zero-fill the unused parts of the input 54 | input_[i]['padding'] = 0 55 | 56 | # for each of the registers and with a probability of 0.01 57 | # set the register to zero or to max value 58 | if self._probability_of_zero <= 0: 59 | continue 60 | for reg_id in range(GPR_SUBREGION_SIZE // 8): 61 | roll = rng.random() 62 | if roll < self._probability_of_zero: 63 | input_[i]['gpr'][reg_id] = 0 64 | elif roll < self._probability_of_max: 65 | input_[i]['gpr'][reg_id] = self.max_gpr_value 66 | 67 | return input_, state + 1 68 | 69 | def generate(self, count: int) -> List[Input]: 70 | # if it's the first invocation and the seed is zero - use random seed 71 | if self._state == 0: 72 | self._state = random.randint(0, pow(2, 32) - 1) 73 | self.LOG.inform("input_gen", f"Setting input seed to: {self._state}") 74 | 75 | generated_inputs = [] 76 | for _ in range(count): 77 | input_, self._state = self._generate_one(self._state) 78 | generated_inputs.append(input_) 79 | 80 | # make sure that boosted inputs will continue from the updated state 81 | self._boosting_state = self._state 82 | return generated_inputs 83 | 84 | def reset_boosting_state(self) -> None: 85 | self._boosting_state = self._state 86 | 87 | def extend_equivalence_classes(self, inputs: List[Input], 88 | taints: List[InputTaint]) -> List[Input]: 89 | """ 90 | Produce a new sequence of random inputs, but copy the tainted values from 91 | the base sequence 92 | """ 93 | if not inputs: 94 | return [] 95 | 96 | if len(inputs) != len(taints): 97 | raise Exception("Error: Cannot extend inputs. " 98 | "The number of taints does not match the number of inputs.") 99 | n_actors = len(inputs[0]) 100 | 101 | # create inputs 102 | new_inputs = [] 103 | for i, input_ in enumerate(inputs): 104 | new_input, self._boosting_state = self._generate_one(self._boosting_state) 105 | for actor_id in range(n_actors): 106 | taint = taints[i].linear_view(actor_id) 107 | input_old = input_.linear_view(actor_id) 108 | input_new = new_input.linear_view(actor_id) 109 | for j in range(input_.data_size): 110 | if taint[j]: 111 | input_new[j] = input_old[j] 112 | new_inputs.append(new_input) 113 | 114 | return new_inputs 115 | 116 | def load(self, input_paths: List[str]) -> List[Input]: 117 | # mirror the state update in generate() as 'load' function is used for reproducing 118 | # violations, which requires the generator state to be identical to the one during 119 | # fuzzing 120 | if self._state == 0: 121 | self._state = random.randint(0, pow(2, 32) - 1) 122 | self.LOG.inform("input_gen", f"Setting input seed to: {self._state}") 123 | 124 | inputs = [] 125 | for input_path in input_paths: 126 | input_ = Input(self.n_actors) 127 | 128 | # check that the file is not corrupted 129 | size = os.path.getsize(input_path) 130 | expected = input_.itemsize * self.n_actors 131 | if size != expected: 132 | self.LOG.error(f"Incorrect size of input `{input_path}` " 133 | f"({size} B, expected {expected} B)") 134 | 135 | input_.load(input_path) 136 | inputs.append(input_) 137 | self._state += 1 138 | 139 | self._boosting_state = self._state 140 | return inputs 141 | -------------------------------------------------------------------------------- /src/x86/executor/include/vmx_config.h: -------------------------------------------------------------------------------- 1 | /// File: Configuration constants for VMX 2 | /// 3 | // Copyright (C) Microsoft Corporation 4 | // SPDX-License-Identifier: MIT 5 | 6 | #ifndef _VMX_CONFIG_H_ 7 | #define _VMX_CONFIG_H_ 8 | 9 | #include 10 | 11 | // Could be read from cpuid 12 | #define VMX_MAX_NUM_GUESTS 64 // DO NOT INCREASE without knowing exactly what you are doing 13 | 14 | // Constants missing in (some versions of) Linux 15 | #ifndef CPU_BASED_ACTIVATE_TERTIARY_CONTROLS 16 | #define CPU_BASED_ACTIVATE_TERTIARY_CONTROLS (1ULL << 17) 17 | #endif 18 | #ifndef SECONDARY_EXEC_RDTSCP 19 | #define SECONDARY_EXEC_RDTSCP (1ULL << 3) 20 | #endif 21 | #define SECONDARY_EXEC_EPT_VIOLATION_CAUSES_VE (1ULL << 18) 22 | #define SECONDARY_EXEC_PASID_TRANSLATION (1ULL << 21) 23 | #define SECONDARY_EXEC_SUBPAGE_WRITE_PERM (1ULL << 23) 24 | #define SECONDARY_EXEC_ENABLE_PCONFIG (1ULL << 27) 25 | #define SECONDARY_EXEC_ENABLE_ENCLV_EXITING (1ULL << 28) 26 | 27 | #define VM_EXIT_UINV (1ULL << 19) 28 | #define VM_ENTRY_CET (1ULL << 20) 29 | #define VM_ENTRY_LOAD_IA32_LBR_CTL (1ULL << 21) 30 | #define VM_ENTRY_LOAD_IA32_PKRS (1ULL << 22) 31 | 32 | // ---------------------------------------------------------------------------------------------- 33 | // Guest control registers 34 | #define MUST_SET_BITS_CR0_VMX_GUEST \ 35 | (X86_CR0_PE | X86_CR0_PG | X86_CR0_NE | X86_CR0_WP | X86_CR0_AM | X86_CR0_ET) 36 | #define MUST_CLEAR_BITS_CR0_VMX_GUEST (X86_CR0_NW | X86_CR0_CD) 37 | 38 | #define MUST_SET_BITS_CR4_VMX_GUEST \ 39 | (X86_CR4_PSE | X86_CR4_PAE | X86_CR4_MCE | X86_CR4_PGE | X86_CR4_PCE | X86_CR4_OSFXSR | \ 40 | X86_CR4_OSXMMEXCPT | X86_CR4_VMXE | X86_CR4_PCIDE) 41 | #define MUST_CLEAR_BITS_CR4_VMX_GUEST \ 42 | (X86_CR4_VME | X86_CR4_PVI | X86_CR4_TSD | X86_CR4_UMIP | X86_CR4_SMXE | X86_CR4_FSGSBASE | \ 43 | X86_CR4_OSXSAVE) 44 | 45 | // ---------------------------------------------------------------------------------------------- 46 | // VMCS control fields 47 | 48 | // Table 25-5. Definitions of Pin-Based VM-Execution Controls 49 | // IMPORTANT: never combine setting of PIN_BASED_EXT_INTR_MASK and VM_EXIT_ACK_INTR_ON_EXIT 50 | // (i.e., at least one must be disabled); otherwise, interrupts lead to system crash 51 | #define MUST_SET_PIN_BASED_VM_EXEC_CONTROL \ 52 | (PIN_BASED_NMI_EXITING | PIN_BASED_VIRTUAL_NMIS | PIN_BASED_VMX_PREEMPTION_TIMER) 53 | #define MUST_CLEAR_PIN_BASED_VM_EXEC_CONTROL (PIN_BASED_EXT_INTR_MASK | PIN_BASED_POSTED_INTR) 54 | 55 | // Table 25-6. Definitions of Primary Processor-Based VM-Execution Controls 56 | // DO NOT add CPU_BASED_RDPMC_EXITING because we may need it if guest primes or probes 57 | #define MUST_SET_PRIMARY_VM_EXEC_CONTROL \ 58 | (CPU_BASED_INTR_WINDOW_EXITING | CPU_BASED_HLT_EXITING | CPU_BASED_INVLPG_EXITING | \ 59 | CPU_BASED_MWAIT_EXITING | CPU_BASED_CR3_LOAD_EXITING | CPU_BASED_CR3_STORE_EXITING | \ 60 | CPU_BASED_CR8_LOAD_EXITING | CPU_BASED_CR8_STORE_EXITING | CPU_BASED_MOV_DR_EXITING | \ 61 | CPU_BASED_UNCOND_IO_EXITING | CPU_BASED_MONITOR_EXITING | CPU_BASED_PAUSE_EXITING | \ 62 | CPU_BASED_ACTIVATE_SECONDARY_CONTROLS | CPU_BASED_NMI_WINDOW_EXITING) 63 | #define MUST_CLEAR_PRIMARY_VM_EXEC_CONTROL \ 64 | (CPU_BASED_USE_TSC_OFFSETTING | CPU_BASED_RDPMC_EXITING | CPU_BASED_RDTSC_EXITING | \ 65 | CPU_BASED_ACTIVATE_TERTIARY_CONTROLS | CPU_BASED_TPR_SHADOW | CPU_BASED_USE_IO_BITMAPS | \ 66 | CPU_BASED_MONITOR_TRAP_FLAG | CPU_BASED_USE_MSR_BITMAPS) 67 | 68 | // Table 25-7. Definitions of Secondary Processor-Based VM-Execution Controls 69 | #define MUST_SET_SECONDARY_VM_EXEC_CONTROL \ 70 | (SECONDARY_EXEC_ENABLE_EPT | SECONDARY_EXEC_DESC | SECONDARY_EXEC_WBINVD_EXITING | \ 71 | SECONDARY_EXEC_ENABLE_INVPCID | SECONDARY_EXEC_RDRAND_EXITING | \ 72 | SECONDARY_EXEC_RDSEED_EXITING) 73 | #define MUST_CLEAR_SECONDARY_VM_EXEC_CONTROL \ 74 | (SECONDARY_EXEC_VIRTUALIZE_APIC_ACCESSES | SECONDARY_EXEC_RDTSCP | \ 75 | SECONDARY_EXEC_VIRTUALIZE_X2APIC_MODE | SECONDARY_EXEC_ENABLE_VPID | \ 76 | SECONDARY_EXEC_UNRESTRICTED_GUEST | SECONDARY_EXEC_APIC_REGISTER_VIRT | \ 77 | SECONDARY_EXEC_VIRTUAL_INTR_DELIVERY | SECONDARY_EXEC_ENABLE_VMFUNC | \ 78 | SECONDARY_EXEC_ENCLS_EXITING | SECONDARY_EXEC_ENABLE_PML | \ 79 | SECONDARY_EXEC_EPT_VIOLATION_CAUSES_VE | SECONDARY_EXEC_PT_CONCEAL_VMX | \ 80 | SECONDARY_EXEC_XSAVES | SECONDARY_EXEC_PASID_TRANSLATION | \ 81 | SECONDARY_EXEC_MODE_BASED_EPT_EXEC | SECONDARY_EXEC_SUBPAGE_WRITE_PERM | \ 82 | SECONDARY_EXEC_PT_USE_GPA | SECONDARY_EXEC_TSC_SCALING | \ 83 | SECONDARY_EXEC_ENABLE_USR_WAIT_PAUSE | SECONDARY_EXEC_ENABLE_PCONFIG | \ 84 | SECONDARY_EXEC_ENABLE_ENCLV_EXITING | SECONDARY_EXEC_SHADOW_VMCS) 85 | 86 | #ifdef VMBUILD 87 | #undef MUST_SET_SECONDARY_VM_EXEC_CONTROL 88 | #define MUST_SET_SECONDARY_VM_EXEC_CONTROL \ 89 | (SECONDARY_EXEC_ENABLE_EPT | SECONDARY_EXEC_DESC | SECONDARY_EXEC_WBINVD_EXITING | \ 90 | SECONDARY_EXEC_RDRAND_EXITING | SECONDARY_EXEC_RDSEED_EXITING) 91 | #endif 92 | 93 | // Misc. 94 | #define DEFAULT_EXCEPTION_BITMAP 0xFFFFFFFF // all exceptions are redirected to host 95 | 96 | // Exit/entry controls 97 | #define MUST_SET_EXIT_CTRL (VM_EXIT_SAVE_DEBUG_CONTROLS | VM_EXIT_HOST_ADDR_SPACE_SIZE) 98 | #define MUST_CLEAR_EXIT_CTRL \ 99 | (VM_EXIT_LOAD_IA32_PERF_GLOBAL_CTRL | VM_EXIT_SAVE_IA32_PAT | VM_EXIT_LOAD_IA32_PAT | \ 100 | VM_EXIT_SAVE_IA32_EFER | VM_EXIT_LOAD_IA32_EFER | VM_EXIT_SAVE_VMX_PREEMPTION_TIMER | \ 101 | VM_EXIT_CLEAR_BNDCFGS | VM_EXIT_PT_CONCEAL_PIP | VM_EXIT_CLEAR_IA32_RTIT_CTL | \ 102 | VM_EXIT_ACK_INTR_ON_EXIT) 103 | 104 | #define MUST_SET_ENTRY_CTRL (VM_ENTRY_LOAD_DEBUG_CONTROLS | VM_ENTRY_IA32E_MODE) 105 | #define MUST_CLEAR_ENTRY_CTRL \ 106 | (VM_ENTRY_SMM | VM_ENTRY_DEACT_DUAL_MONITOR | VM_ENTRY_LOAD_IA32_PERF_GLOBAL_CTRL | \ 107 | VM_ENTRY_LOAD_IA32_PAT | VM_ENTRY_LOAD_IA32_EFER | VM_ENTRY_LOAD_BNDCFGS | \ 108 | VM_ENTRY_PT_CONCEAL_PIP | VM_ENTRY_LOAD_IA32_RTIT_CTL | VM_EXIT_UINV | VM_ENTRY_CET | \ 109 | VM_ENTRY_LOAD_IA32_LBR_CTL | VM_ENTRY_LOAD_IA32_PKRS) 110 | 111 | #endif // _VMX_CONFIG_H_ 112 | -------------------------------------------------------------------------------- /docs/development.md: -------------------------------------------------------------------------------- 1 | # Development 2 | 3 | This page contains various bits of information helpful when developing and expanding Revizor. 4 | 5 | ## Tests 6 | 7 | To run automated tests you will need to install a few more dependencies: 8 | 9 | * [Bash Automated Testing System](https://bats-core.readthedocs.io/en/latest/index.html) 10 | * [mypy](https://mypy.readthedocs.io/en/latest/getting_started.html#installing-and-running-mypy) 11 | * [flake8](https://flake8.pycqa.org/en/latest/index.html) 12 | 13 | With the dependencies installed, you can run the tests with: 14 | 15 | ```bash 16 | ./tests/runtests.sh 17 | ``` 18 | 19 | Note that some of the acceptance tests are microarchitecture-dependent. 20 | These tests are labeled "Detection" (e.g., "Detection [spectre-type] Spectre V1; load variant"), and they may fail if the CPU under test does not have a given vulnerability. 21 | Generally, if a few of these tests fail, it is not a problem, but if all of them (or a significant portion) fail, it indicates an issue with the fuzzer. 22 | 23 | ## Code Style 24 | 25 | Please follow these coding standards when writing code for inclusion in Revizor. 26 | 27 | Python: 28 | 29 | * Unless otherwise specified, follow PEP 8. But remember that PEP 8 is only a guide, so respect the style of the surrounding code as a primary goal. 30 | * An exception to PEP 8 is our rules on line lengths. Don’t limit lines of code to 79 characters if it means the code looks significantly uglier or is harder to read. We allow up to 100 characters. 31 | * All files should be formatted using the `flake8` auto-formatter. Use all default settings except for the line width (`--max-line-length 100`) 32 | * The Python and C files use 4 spaces for indentation, and YAML uses 2 spaces. 33 | * The project repository includes an .editorconfig file. We recommend using a text editor with EditorConfig support to avoid indentation and whitespace issues. 34 | * Use underscores, not camelCase, for variable, function and method names (i.e. poll.get_unique_voters(), not poll.getUniqueVoters()). 35 | * Use InitialCaps for class names (or for factory functions that return classes). 36 | * In docstrings, follow PEP 257. 37 | 38 | C: 39 | 40 | * All files should be formatted using the `clang-format`. The settings are included into the `.clang-format` files in the directories with C files. Just run the formatter with: `clang-format -i *.c` 41 | 42 | Misc: 43 | 44 | * Remove import statements that are no longer used when you change code. flake8 will identify these imports for you. If an unused import needs to remain for backwards-compatibility, mark the end of with # NOQA to silence the flake8 warning. 45 | * Systematically remove all trailing whitespaces from your code as those add unnecessary bytes, add visual clutter to the patches and can also occasionally cause unnecessary merge conflicts. Some IDE’s can be configured to automatically remove them and most VCS tools can be set to highlight them in diff outputs. 46 | 47 | ## Git Messages 48 | 49 | We practice the following conventions for commit messages: 50 | 51 | ``` 52 | : [] 53 | ``` 54 | 55 | Where: 56 | 57 | * ``: The scope of the change. 58 | * ``: The type of the change. 59 | * ``: A short description of the change. 60 | 61 | 62 | ### Scopes 63 | 64 | The following scopes are typical: 65 | 66 | 67 | | Scope | Description | 68 | | ----------- | ---------------------------------------------------------------- | 69 | | `all` | Changes that affect the entire project (e.g., major refactoring) | 70 | | `root` | Root directory changes (e.g., readme, git, author list) | 71 | | `fuzz` | Changes to the core fuzzer algorithm. | 72 | | `cli` | Changes to the command-line interface. | 73 | | `exec` | Changes to the executor. | 74 | | `model` | Changes to the model. | 75 | | `analyser` | Changes to the analyser. | 76 | | `mini` | Changes to the postprocessor (i.e., minimizer). | 77 | | `gen` | Changes to the program generator | 78 | | `input_gen` | Changes to the input generator | 79 | | `tests` | Changes to the tests | 80 | | `isa` | Changes to the ISA loader or to `get_spec` files | 81 | 82 | If a commit covers several scopes, use the most relevant one. 83 | 84 | If a commit targets a specific architecture (e.g., x86), add the architecture to the scope (e.g., `fuzz/x86`). 85 | 86 | ### Types 87 | 88 | Use one of the following types: 89 | 90 | | Type | Description | 91 | | -------- | ----------------------------------------------------------------------------- | 92 | | `feat` | A new feature. | 93 | | `fix` | A bug fix. | 94 | | `docs` | Documentation changes. | 95 | | `chore` | Changes to the build process or auxiliary tools. | 96 | | `ft` | Fault tolerance changes (e.g., adding error handling or recovery mechanisms). | 97 | | `refact` | Refactoring of the codebase. This includes code style change. | 98 | | `perf` | Performance improvements. | 99 | | `revert` | Reverts a previous commit. | 100 | 101 | If possible, try to use only these types. 102 | If you need to use a different type, please discuss it with a maintainer. 103 | 104 | ## Git Branches 105 | 106 | We practice the [git workflow](https://git-scm.com/docs/gitworkflows), with a few modifications. 107 | 108 | We use the following branches for graduation: 109 | 110 | * `main`: The latest release. This branch should always be stable, and it is the last branch to receive changes. 111 | * `main-fixes`: Commits that go in the next maintenance release. This branch is created from the last release branch. 112 | * `pre-release`: Stable commits that go in the next release. 113 | * `dev`: The development branch. This branch is the first to receive changes. 114 | 115 | Commits should be merged upwards: 116 | 117 | * `dev` -> `pre-release` -> `main` 118 | * In case of hot fixes, `main-fixes` -> `main` AND `main-fixes` -> `pre-release` 119 | 120 | For working on unstable code (e.g., progress on features or bug fixes), use either forks or feature branches. 121 | Use forks if you are the only one working on the feature, and use a pull request to merge the changes back into the main repository. 122 | Use a feature branch if multiple people are working on the feature, in which case name the branch `feature-` or `bugfix-`, and make sure to branch from the `dev` branch. 123 | 124 | The only exception is the `gh-pages` branch, which is used for the project's website. 125 | This branch is used by automated tools and should never be used for development. 126 | -------------------------------------------------------------------------------- /src/isa_loader.py: -------------------------------------------------------------------------------- 1 | """ 2 | File: 3 | 4 | Copyright (C) Microsoft Corporation 5 | SPDX-License-Identifier: MIT 6 | """ 7 | import json 8 | from typing import Dict, List 9 | from copy import deepcopy 10 | from .interfaces import OT, InstructionSetAbstract, OperandSpec, InstructionSpec 11 | from .config import CONF 12 | 13 | 14 | class InstructionSet(InstructionSetAbstract): 15 | ot_str_to_enum = { 16 | "REG": OT.REG, 17 | "MEM": OT.MEM, 18 | "IMM": OT.IMM, 19 | "LABEL": OT.LABEL, 20 | "AGEN": OT.AGEN, 21 | "FLAGS": OT.FLAGS, 22 | "COND": OT.COND, 23 | } 24 | instructions: List[InstructionSpec] 25 | instruction_unfiltered: List[InstructionSpec] 26 | 27 | def __init__(self, filename: str, include_categories=None): 28 | self.instructions: List[InstructionSpec] = [] 29 | self.init_from_file(filename) 30 | self.instruction_unfiltered = deepcopy(self.instructions) 31 | self.reduce(include_categories) 32 | self.dedup() 33 | 34 | def init_from_file(self, filename: str): 35 | with open(filename, "r") as f: 36 | root = json.load(f) 37 | for instruction_node in root: 38 | instruction = InstructionSpec() 39 | instruction.name = instruction_node["name"] 40 | instruction.category = instruction_node["category"] 41 | instruction.control_flow = instruction_node["control_flow"] 42 | 43 | for op_node in instruction_node["operands"]: 44 | op = self.parse_operand(op_node, instruction) 45 | instruction.operands.append(op) 46 | if op.magic_value: 47 | instruction.has_magic_value = True 48 | 49 | for op_node in instruction_node["implicit_operands"]: 50 | op = self.parse_operand(op_node, instruction) 51 | instruction.implicit_operands.append(op) 52 | 53 | self.instructions.append(instruction) 54 | 55 | def parse_operand(self, op: Dict, parent: InstructionSpec) -> OperandSpec: 56 | op_type = self.ot_str_to_enum[op["type_"]] 57 | op_values = op.get("values", []) 58 | if op_type == "REG": 59 | op_values = sorted(op_values) 60 | spec = OperandSpec(op_values, op_type, op["src"], op["dest"]) 61 | spec.width = op["width"] 62 | spec.signed = op.get("signed", True) 63 | 64 | if op_type == OT.MEM: 65 | parent.has_mem_operand = True 66 | if spec.dest: 67 | parent.has_write = True 68 | 69 | return spec 70 | 71 | def reduce(self, include_categories): 72 | """ Remove unsupported instructions and operand values """ 73 | 74 | def is_supported(spec: InstructionSpec): 75 | if CONF._no_generation: 76 | # if we use an existing test case, then instruction filtering is irrelevant 77 | return True 78 | 79 | # allowlist has priority over blocklists 80 | if spec.name in CONF.instruction_allowlist: 81 | return True 82 | 83 | if include_categories and spec.category not in include_categories: 84 | return False 85 | 86 | if spec.name in CONF.instruction_blocklist: 87 | return False 88 | 89 | for operand in spec.operands: 90 | if operand.type == OT.MEM and operand.values \ 91 | and operand.values[0] in register_blocklist: 92 | return False 93 | 94 | for implicit_operand in spec.implicit_operands: 95 | assert implicit_operand.type != OT.LABEL # I know no such instructions 96 | if implicit_operand.type == OT.MEM \ 97 | and implicit_operand.values[0] in register_blocklist: 98 | return False 99 | 100 | if implicit_operand.type == OT.REG \ 101 | and implicit_operand.values[0] in register_blocklist: 102 | assert len(implicit_operand.values) == 1 103 | return False 104 | return True 105 | 106 | skip_list = [] 107 | register_blocklist = set(CONF.register_blocklist) - set(CONF.register_allowlist) 108 | 109 | for s in self.instructions: 110 | # Unsupported instructions 111 | if not is_supported(s): 112 | skip_list.append(s) 113 | continue 114 | 115 | skip_pending = False 116 | for op in s.operands: 117 | if op.type == OT.REG: 118 | choices = sorted(list(set(op.values) - register_blocklist)) 119 | if not choices: 120 | skip_pending = True 121 | break 122 | op.values = choices 123 | 124 | # FIXME: temporary disabled generation of higher reg. bytes for x86 125 | for i, reg in enumerate(op.values): 126 | if reg[-1] == 'h': 127 | op.values[i] = reg.replace( 128 | 'h', 129 | 'l', 130 | ) 131 | 132 | if skip_pending: 133 | skip_list.append(s) 134 | 135 | # remove the unsupported 136 | for s in skip_list: 137 | self.instructions.remove(s) 138 | 139 | # set parameters 140 | for inst in self.instructions: 141 | if inst.control_flow: 142 | if inst.category == "BASE-UNCOND_BR": 143 | self.has_unconditional_branch = True 144 | else: 145 | self.has_conditional_branch = True 146 | elif inst.has_mem_operand: 147 | if inst.has_write: 148 | self.has_writes = True 149 | else: 150 | self.has_reads = True 151 | 152 | def dedup(self): 153 | """ 154 | Instruction set spec may contain several copies of the same instruction. 155 | Remove them. 156 | """ 157 | skip_list = set() 158 | for i in range(len(self.instructions)): 159 | for j in range(i + 1, len(self.instructions)): 160 | inst1 = self.instructions[i] 161 | inst2 = self.instructions[j] 162 | if inst1.name == inst2.name and len(inst1.operands) == len(inst2.operands): 163 | match = True 164 | for k, op1 in enumerate(inst1.operands): 165 | op2 = inst2.operands[k] 166 | 167 | if op1.type != op2.type: 168 | match = False 169 | continue 170 | 171 | if op1.values != op2.values: 172 | match = False 173 | continue 174 | 175 | if op1.width != op2.width and op1.type != OT.IMM: 176 | match = False 177 | continue 178 | 179 | # assert op1.src == op2.src 180 | # assert op1.dest == op2.dest 181 | 182 | if match: 183 | skip_list.add(inst1) 184 | 185 | for s in skip_list: 186 | self.instructions.remove(s) 187 | -------------------------------------------------------------------------------- /src/factory.py: -------------------------------------------------------------------------------- 1 | """ 2 | File: Configuration factory 3 | 4 | Copyright (C) Microsoft Corporation 5 | SPDX-License-Identifier: MIT 6 | """ 7 | 8 | from typing import Tuple, Dict, Type, List, Callable 9 | 10 | from . import input_generator, analyser, postprocessor, interfaces, model 11 | from .x86 import x86_model, x86_executor, x86_fuzzer, x86_generator, x86_asm_parser, get_spec 12 | from .config import CONF, ConfigException 13 | 14 | GENERATORS: Dict[str, Type[interfaces.Generator]] = { 15 | "x86-64-random": x86_generator.X86RandomGenerator 16 | } 17 | 18 | INPUT_GENERATORS: Dict[str, Type[interfaces.InputGenerator]] = { 19 | 'random': input_generator.NumpyRandomInputGenerator, 20 | } 21 | 22 | TRACERS: Dict[str, Type[model.UnicornTracer]] = { 23 | "none": model.NoneTracer, 24 | "l1d": model.L1DTracer, 25 | "pc": model.PCTracer, 26 | "memory": model.MemoryTracer, 27 | "ct": model.CTTracer, 28 | "loads+stores+pc": model.CTTracer, 29 | "ct-nonspecstore": model.CTNonSpecStoreTracer, 30 | "ctr": model.CTRTracer, 31 | "arch": model.ArchTracer, 32 | "tct": model.TruncatedCTTracer, 33 | "tcto": model.TruncatedCTWithOverflowsTracer, 34 | } 35 | 36 | X86_EXECUTION_CLAUSES: Dict[str, Type[x86_model.UnicornModel]] = { 37 | "seq": x86_model.X86UnicornSeq, 38 | "no_speculation": x86_model.X86UnicornSeq, 39 | "seq-assist": x86_model.X86SequentialAssist, 40 | "cond": x86_model.X86UnicornCond, 41 | "conditional_br_misprediction": x86_model.X86UnicornCond, 42 | "bpas": x86_model.X86UnicornBpas, 43 | "nullinj-fault": x86_model.X86UnicornNull, 44 | "nullinj-assist": x86_model.X86UnicornNullAssist, 45 | "delayed-exception-handling": x86_model.X86UnicornDEH, 46 | "div-zero": x86_model.X86UnicornDivZero, 47 | "div-overflow": x86_model.X86UnicornDivOverflow, 48 | "meltdown": x86_model.X86Meltdown, 49 | "fault-skip": x86_model.X86FaultSkip, 50 | "noncanonical": x86_model.X86NonCanonicalAddress, 51 | "vspec-ops-div": x86_model.x86UnicornVspecOpsDIV, 52 | "vspec-ops-memory-faults": x86_model.x86UnicornVspecOpsMemoryFaults, 53 | "vspec-ops-memory-assists": x86_model.x86UnicornVspecOpsMemoryAssists, 54 | "vspec-ops-gp": x86_model.x86UnicornVspecOpsGP, 55 | "vspec-all-div": x86_model.x86UnicornVspecAllDIV, 56 | "vspec-all-memory-faults": x86_model.X86UnicornVspecAllMemoryFaults, 57 | "vspec-all-memory-assists": x86_model.X86UnicornVspecAllMemoryAssists, 58 | "noninterference": x86_model.ActorNonInterferenceModel, 59 | "cond-bpas": x86_model.X86UnicornCondBpas, 60 | "cond-nullinj-fault": x86_model.X86NullInjCond, 61 | } 62 | 63 | EXECUTORS = { 64 | 'x86-64-intel': x86_executor.X86IntelExecutor, 65 | 'x86-64-amd': x86_executor.X86AMDExecutor, 66 | } 67 | 68 | ANALYSERS: Dict[str, Type[interfaces.Analyser]] = { 69 | 'bitmaps': analyser.MergedBitmapAnalyser, 70 | 'sets': analyser.SetAnalyser, 71 | 'mwu': analyser.MWUAnalyser, 72 | 'chi2': analyser.ChiSquaredAnalyser, 73 | } 74 | 75 | MINIMIZERS: Dict[str, Type[interfaces.Minimizer]] = { 76 | 'violation': postprocessor.MainMinimizer, 77 | } 78 | 79 | SPEC_DOWNLOADERS: Dict[str, Type] = { 80 | 'x86-64': get_spec.Downloader, 81 | } 82 | 83 | ASM_PARSERS: Dict[str, Type] = { 84 | 'x86-64': x86_asm_parser.X86AsmParser, 85 | } 86 | 87 | 88 | def _get_from_config(options: Dict, key: str, conf_option_name: str, *args): 89 | GenCls = options.get(key, None) 90 | if GenCls: 91 | return GenCls(*args) 92 | 93 | raise ConfigException( 94 | f"ERROR: unknown value `{key}` of `{conf_option_name}` configuration option.\n" 95 | " Available options are:\n - " + "\n - ".join(options.keys())) 96 | 97 | 98 | def get_fuzzer(instruction_set, working_directory, testcase, inputs): 99 | if CONF.fuzzer == "architectural": 100 | if CONF.instruction_set == "x86-64": 101 | return x86_fuzzer.X86ArchitecturalFuzzer(instruction_set, working_directory, testcase, 102 | inputs) 103 | raise ConfigException("ERROR: unknown value of `instruction_set` configuration option") 104 | elif CONF.fuzzer == "archdiff": 105 | if CONF.instruction_set == "x86-64": 106 | return x86_fuzzer.X86ArchDiffFuzzer(instruction_set, working_directory, testcase, 107 | inputs) 108 | raise ConfigException("ERROR: unknown value of `instruction_set` configuration option") 109 | elif CONF.fuzzer == "basic": 110 | if CONF.instruction_set == "x86-64": 111 | return x86_fuzzer.X86Fuzzer(instruction_set, working_directory, testcase, inputs) 112 | raise ConfigException("ERROR: unknown value of `instruction_set` configuration option") 113 | raise ConfigException("ERROR: unknown value of `fuzzer` configuration option") 114 | 115 | 116 | def get_program_generator(instruction_set: interfaces.InstructionSetAbstract, 117 | seed: int) -> interfaces.Generator: 118 | return _get_from_config(GENERATORS, CONF.instruction_set + "-" + CONF.generator, 119 | "instruction_set", instruction_set, seed) 120 | 121 | 122 | def get_asm_parser(generator: interfaces.Generator) -> interfaces.AsmParser: 123 | return _get_from_config(ASM_PARSERS, CONF.instruction_set, "instruction_set", generator) 124 | 125 | 126 | def get_input_generator(seed: int) -> interfaces.InputGenerator: 127 | return _get_from_config(INPUT_GENERATORS, CONF.input_generator, "input_generator", seed) 128 | 129 | 130 | def get_model(bases: Tuple[int, int], enable_mismatch_check_mode: bool = False) -> interfaces.Model: 131 | # observational clause of the contract 132 | tracer = _get_from_config(TRACERS, CONF.contract_observation_clause, 133 | "contract_observation_clause") 134 | 135 | # execution clause of the contract 136 | if "cond" in CONF.contract_execution_clause and "bpas" in CONF.contract_execution_clause: 137 | clause_name = "cond-bpas" 138 | elif "conditional_br_misprediction" in CONF.contract_execution_clause and \ 139 | "nullinj-fault" in CONF.contract_execution_clause: 140 | clause_name = "cond-nullinj-fault" 141 | elif len(CONF.contract_execution_clause) == 1: 142 | clause_name = CONF.contract_execution_clause[0] 143 | else: 144 | raise ConfigException( 145 | "ERROR: unknown value of `contract_execution_clause` configuration option") 146 | 147 | return _get_from_config(X86_EXECUTION_CLAUSES, clause_name, "contract_execution_clause", 148 | bases[0], bases[1], tracer, enable_mismatch_check_mode) 149 | 150 | 151 | def get_executor(enable_mismatch_check_mode: bool = False) -> interfaces.Executor: 152 | return _get_from_config(EXECUTORS, CONF.executor, "executor", enable_mismatch_check_mode) 153 | 154 | 155 | def get_analyser() -> interfaces.Analyser: 156 | return _get_from_config(ANALYSERS, CONF.analyser, "analyser") 157 | 158 | 159 | def get_minimizer(fuzzer: interfaces.Fuzzer, 160 | instruction_set: interfaces.InstructionSetAbstract) -> interfaces.Minimizer: 161 | return _get_from_config(MINIMIZERS, "violation", "minimizer", fuzzer, instruction_set) 162 | 163 | 164 | def get_downloader(arch: str, extensions: List[str], out_file: str) -> Callable: 165 | return _get_from_config(SPEC_DOWNLOADERS, arch, "architecture", extensions, out_file) 166 | -------------------------------------------------------------------------------- /src/x86/executor/sandbox_manager.c: -------------------------------------------------------------------------------- 1 | /// File: Sandbox memory management 2 | /// 3 | // Copyright (C) Microsoft Corporation 4 | // SPDX-License-Identifier: MIT 5 | 6 | #include "sandbox_manager.h" 7 | #include "actor.h" 8 | #include "code_loader.h" // loaded_test_case_entry 9 | #include "main.h" // set_memory_x, set_memory_nx 10 | #include "shortcuts.h" 11 | #include "test_case_parser.h" 12 | 13 | #include "memory_guest.h" 14 | #include "host_page_tables.h" 15 | 16 | sandbox_t *sandbox = NULL; // global 17 | 18 | static void *_util_n_data_unaligned = NULL; 19 | static void *util_n_data = NULL; 20 | static void *code = NULL; 21 | 22 | static size_t old_x_size = 0; 23 | 24 | static int allocate_util_and_data(size_t n_actors) 25 | { 26 | SAFE_VFREE(_util_n_data_unaligned); 27 | 28 | // allocate working memory 29 | size_t mem_size = sizeof(util_t) + n_actors * sizeof(actor_data_t); 30 | _util_n_data_unaligned = CHECKED_VMALLOC(mem_size + 0x1000); 31 | memset(_util_n_data_unaligned, 0, mem_size); 32 | 33 | // align memory to 2 pages (vmalloc guarantees 1 page alignment) 34 | if ((unsigned long)_util_n_data_unaligned % 0x2000 == 0) 35 | util_n_data = (sandbox_t *)_util_n_data_unaligned; 36 | else 37 | util_n_data = (sandbox_t *)((unsigned long)_util_n_data_unaligned + 0x1000); 38 | 39 | return 0; 40 | } 41 | 42 | static int allocate_code(size_t n_actors) 43 | { 44 | // release old space for sections 45 | if (code) { 46 | set_memory_nx((unsigned long)code, old_x_size); 47 | SAFE_VFREE(code); 48 | loaded_test_case_entry = NULL; 49 | } 50 | 51 | // create new space for sections 52 | code = CHECKED_VMALLOC(n_actors * sizeof(actor_code_t)); 53 | memset(code, 0x90, n_actors * sizeof(actor_code_t)); // pad with nops 54 | 55 | // make it executable 56 | size_t size_pages = n_actors * sizeof(actor_code_t) / PAGE_SIZE; 57 | if (n_actors * sizeof(actor_code_t) % PAGE_SIZE != 0) 58 | size_pages++; 59 | set_memory_x((unsigned long)code, size_pages); 60 | old_x_size = size_pages; 61 | 62 | // initialize the main section with a single ret instruction 63 | ((uint8_t *)code)[0] = '\xC3'; 64 | return 0; 65 | } 66 | 67 | int allocate_sandbox(void) 68 | { 69 | int err = 0; 70 | static int old_n_actors = 0; 71 | 72 | // Allocate sandbox in host memory 73 | if (old_n_actors < n_actors) { 74 | err = allocate_util_and_data(n_actors); 75 | CHECK_ERR("allocate_util_and_data"); 76 | 77 | err = allocate_code(n_actors); 78 | CHECK_ERR("allocate_code"); 79 | 80 | // initialize pointers 81 | sandbox = CHECKED_MALLOC(sizeof(sandbox_t)); 82 | sandbox->data = (actor_data_t *)((unsigned long)util_n_data + sizeof(util_t)); 83 | sandbox->code = (actor_code_t *)code; 84 | sandbox->util = (util_t *)util_n_data; 85 | 86 | // point to the main section of the first actor 87 | loaded_test_case_entry = code; 88 | } 89 | 90 | // Make sure that everything is property initialized 91 | memset(util_n_data, 0, sizeof(util_t) + n_actors * sizeof(actor_data_t)); 92 | memset(code, 0x90, n_actors * sizeof(actor_code_t)); 93 | 94 | err = cache_host_pteps(); 95 | CHECK_ERR("cache_host_pteps"); 96 | 97 | // when necessary, map the sandbox into guest memory and allocate VM management data structures 98 | if (test_case->features.includes_vm_actors) { 99 | err = allocate_guest_page_tables(); 100 | CHECK_ERR("allocate_guest_page_tables"); 101 | 102 | err = map_sandbox_to_guest_memory(); 103 | CHECK_ERR("map_sandbox_to_guest_memory"); 104 | } 105 | old_n_actors = n_actors; 106 | 107 | return err; 108 | } 109 | 110 | /// @brief Returns the number of pages allocated for the sandbox, including util area, code and data 111 | /// @param void 112 | /// @return number of pages; -1 on error 113 | int get_sandbox_size_pages(void) 114 | { 115 | if (sandbox == NULL) { 116 | return -1; 117 | } 118 | 119 | int n_pages = 0; 120 | n_pages += sizeof(util_t) / PAGE_SIZE; 121 | n_pages += sizeof(actor_data_t) / PAGE_SIZE * n_actors; 122 | n_pages += sizeof(actor_code_t) / PAGE_SIZE * n_actors; 123 | 124 | return n_pages; 125 | } 126 | 127 | /// @brief Sets PTE values for the sandbox based on the current test case configuration 128 | /// @param void 129 | /// @return 0 on success; -1 on error 130 | int set_sandbox_page_tables(void) 131 | { 132 | int err = 0; 133 | 134 | err = store_orig_host_permissions(); 135 | CHECK_ERR("store_orig_host_permissions"); 136 | 137 | if (test_case->features.includes_user_actors) { 138 | err = set_user_pages(); 139 | CHECK_ERR("set_user_pages"); 140 | } 141 | return 0; 142 | } 143 | 144 | void restore_orig_sandbox_page_tables(void) { restore_orig_host_permissions(); } 145 | 146 | /// @brief Fast modification of the faulty page PTE; sets the permissions according to 147 | /// actor_t->data_permissions 148 | void set_faulty_page_permissions(void) 149 | { 150 | set_faulty_page_host_permissions(); 151 | set_faulty_page_guest_permissions(); 152 | set_faulty_page_ept_permissions(); 153 | } 154 | 155 | /// @brief Fast recovery of original permissions of the faulty page PTE 156 | void restore_faulty_page_permissions(void) 157 | { 158 | restore_faulty_page_host_permissions(); 159 | restore_faulty_page_guest_permissions(); 160 | restore_faulty_page_ept_permissions(); 161 | } 162 | 163 | // ================================================================================================= 164 | int init_sandbox_manager(void) 165 | { 166 | int err = 0; 167 | err = allocate_util_and_data(1); 168 | CHECK_ERR("allocate_util_and_data"); 169 | 170 | err = allocate_code(1); 171 | CHECK_ERR("allocate_code"); 172 | 173 | // initialize pointers 174 | sandbox = CHECKED_MALLOC(sizeof(sandbox_t)); 175 | sandbox->data = (actor_data_t *)((unsigned long)util_n_data + sizeof(util_t)); 176 | sandbox->code = (actor_code_t *)code; 177 | sandbox->util = (util_t *)util_n_data; 178 | loaded_test_case_entry = code; 179 | 180 | // self-test: make sure the sandbox is aligned as we expect 181 | int offset = (unsigned long)sandbox->data[0].main_area % 0x2000; 182 | ASSERT(offset == 0, "init_sandbox_manager"); 183 | 184 | actor_data_t *data = &sandbox->data[0]; 185 | util_t *util = sandbox->util; 186 | ASSERT(&util->l1d_priming_area[0] - (uint8_t *)util == L1D_PRIMING_OFFSET, "init_sandbox"); 187 | ASSERT((uint8_t *)&util->stored_rsp - (uint8_t *)util == STORED_RSP_OFFSET, "init_sandbox"); 188 | ASSERT((uint8_t *)&util->latest_measurement - (uint8_t *)util == MEASUREMENT_OFFSET, 189 | "init_sandbox"); 190 | 191 | ASSERT(&data->main_area[0] - (uint8_t *)util == UTIL_REL_TO_MAIN, "init_sandbox"); 192 | ASSERT(&data->main_area[0] - &data->macro_stack[64] == MACRO_STACK_TOP_OFFSET, "init_sandbox"); 193 | ASSERT(&data->faulty_area[0] - &data->main_area[0] == FAULTY_AREA_OFFSET, "init_sandbox"); 194 | ASSERT(&data->reg_init_area[0] - &data->main_area[0] == REG_INIT_OFFSET, "init_sandbox"); 195 | ASSERT(&data->overflow_pad[0] - &data->main_area[0] == OVERFLOW_PAD_OFFSET, "init_sandbox"); 196 | 197 | return 0; 198 | } 199 | 200 | void free_sandbox_manager(void) 201 | { 202 | SAFE_VFREE(_util_n_data_unaligned); 203 | SAFE_VFREE(code); 204 | util_n_data = NULL; 205 | 206 | if (code) { 207 | set_memory_nx((unsigned long)code, old_x_size); 208 | SAFE_VFREE(code); 209 | loaded_test_case_entry = NULL; 210 | } 211 | 212 | // since sandbox manager called allocators, it is responsible for also freeing the memory 213 | // note that the below calls are safe even if the corresponding allocations were not made 214 | free_guest_page_tables(); 215 | } 216 | -------------------------------------------------------------------------------- /tests/pre-release.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -o errexit -o pipefail -o noclobber -o nounset 3 | trap exit INT 4 | 5 | SCRIPT=$(realpath $0) 6 | SCRIPT_DIR=$(dirname $SCRIPT) 7 | 8 | # ================================================================================================== 9 | # Read arguments 10 | 11 | # check for availability of getopt 12 | getopt --test >/dev/null && true 13 | if [[ $? -ne 4 ]]; then 14 | echo 'ERROR: getopt is not available' 15 | exit 1 16 | fi 17 | 18 | # List arguments 19 | LONGOPTS=rvzr:,workdir:,verbose 20 | OPTIONS=r:w:v 21 | 22 | # Parse output 23 | PARSED=$(getopt --options=$OPTIONS --longoptions=$LONGOPTS --name "$0" -- "$@") || exit 2 24 | eval set -- "$PARSED" 25 | 26 | verbose=0 27 | revizor_dir="" 28 | work_dir="" 29 | 30 | usage="Usage: $0 [-v] -r -w " 31 | while true; do 32 | case "$1" in 33 | -v | --verbose) 34 | verbose=0 35 | ;; 36 | -r | --rvzr) 37 | revizor_dir=$2 38 | shift 39 | ;; 40 | -w | --workdir) 41 | work_dir=$2 42 | shift 43 | ;; 44 | --) 45 | shift 46 | break 47 | ;; 48 | esac 49 | shift 50 | done 51 | 52 | # check usage 53 | if [ -z "$revizor_dir" ]; then 54 | echo "ERROR: revizor_dir is not set" 55 | echo $usage 56 | exit 1 57 | fi 58 | if [ -z "$work_dir" ]; then 59 | echo "ERROR: work_dir is not set" 60 | echo $usage 61 | exit 1 62 | fi 63 | 64 | # make sure that the directories and required files exist 65 | if [ ! -d "$revizor_dir" ]; then 66 | echo "ERROR: Could not find '$revizor_dir'" 67 | fi 68 | if [ ! -d "$work_dir" ]; then 69 | echo "ERROR: Could not find '$work_dir'" 70 | fi 71 | if [ ! -f "$revizor_dir/revizor.py" ]; then 72 | echo "ERROR: Could not find '$revizor_dir/revizor.py'" 73 | fi 74 | if [ ! -f "$revizor_dir/src/x86/base.json" ]; then 75 | echo "ERROR: Could not find '$revizor_dir/src/x86/base.json'" 76 | fi 77 | 78 | work_dir=$(realpath $work_dir) 79 | work_dir="$work_dir/$(date '+%Y-%m-%d-%H-%M-%S')" 80 | 81 | # ================================================================================================== 82 | # Test configuration 83 | NUM_INPUTS=25 84 | NUM_PROGS=1000000000 # some large number that is never reached before the timeout 85 | TIMEOUT=7200 # seconds 86 | 87 | # Globals 88 | revizor="$revizor_dir/revizor.py" 89 | instructions="$revizor_dir/src/x86/base.json" 90 | conf_dir="$SCRIPT_DIR/configs/" 91 | 92 | # ================================================================================================== 93 | # Functions 94 | function _check_results() { 95 | # Check the output of the experiment for errors and parse the results 96 | 97 | # arguments 98 | local log=$1 99 | local exit_code=$2 100 | local expected=$3 101 | 102 | # output messages 103 | fail="\033[33;31mfail\033[0m" 104 | error="\033[33;31merror\033[0m" 105 | ok="\033[33;32mok\033[0m" 106 | 107 | # check for errors 108 | if grep "ERROR" $log &>/dev/null; then 109 | printf "$error\n" 110 | return 1 111 | fi 112 | if grep "Errno" $log &>/dev/null; then 113 | printf "$error\n" 114 | return 1 115 | fi 116 | 117 | # if no violations were found, the test failed 118 | if [ $exit_code -ne $expected ]; then 119 | printf "$fail [exit code %s != %s]\n" "$exit_code" "$expected" 120 | return 1 121 | fi 122 | 123 | # parse the output 124 | duration=$(awk '/Duration/{print $2}' $log) 125 | length=$(awk '/^Test Cases:/{print $3}' $log) 126 | printf "$ok [%s sec, %s tc]\n" "$duration" "$length" 127 | return 0 128 | } 129 | 130 | function run() { 131 | local name=$1 132 | 133 | # remove leftovers from previous runs 134 | rm -rf $work_dir &>/dev/null || true 135 | mkdir -p $work_dir 136 | 137 | # check that the configuration file exists 138 | config="$conf_dir/${name}.yaml" 139 | if [ ! -f "$config" ]; then 140 | echo "ERROR: Could not find '$config'" 141 | exit 1 142 | fi 143 | 144 | # create a log file 145 | log="$conf_dir/${name}-log.txt" 146 | rm $log &>/dev/null || true 147 | 148 | # Print the header 149 | echo "================================================================================" 150 | echo "Running test: $name" 151 | 152 | # run the test 153 | printf "+ Detect ... " 154 | set +e 155 | if [ $verbose -eq 1 ]; then set -x; fi 156 | python ${revizor} fuzz -s $instructions -c $config -i $NUM_INPUTS -n $NUM_PROGS --timeout $TIMEOUT -w "$work_dir" | tee "$log" 157 | exit_code=$? 158 | if [ $verbose -eq 1 ]; then set +x; fi 159 | set -e 160 | 161 | _check_results $log $exit_code 1 162 | if [ $? -ne 0 ]; then 163 | return 164 | fi 165 | 166 | # move the violation into a dedicated dir 167 | vdir="$work_dir/violation*" 168 | if [ -d "$vdir" ]; then 169 | echo "ERROR: Could not find a violation directory: '$vdir'" 170 | exit 1 171 | fi 172 | 173 | # reproduce the violations 174 | printf "+ Reproduce ... " 175 | set +e 176 | if [ $verbose -eq 1 ]; then set -x; fi 177 | python ${revizor} reproduce -s $instructions -c $vdir/reproduce.yaml -I $conf_dir -t $vdir/program.asm -i $(ls $vdir/input*.bin) | tee "$log" 178 | exit_code=$? 179 | if [ $verbose -eq 1 ]; then set +x; fi 180 | set -e 181 | 182 | _check_results $log $exit_code 1 183 | if [ $? -ne 0 ]; then 184 | return 185 | fi 186 | } 187 | 188 | function reproduce() { 189 | local name=$1 190 | local expected=$2 191 | 192 | printf " reproducing ... " 193 | set +e 194 | violation_dir="$conf_dir/$name" 195 | config="$conf_dir/${name}-repro.yaml" 196 | cp "$conf_dir/${name}.yaml" $config 197 | awk "/Input seed:/{print \"input_gen_seed:\", \$4}" $violation_dir/violation-*/report.txt >>$config 198 | python ${revizor} reproduce -s $instructions -c $config -n $NUM_INPUTS -t $violation_dir/violation-*/program.asm -i $(ls $violation_dir/violation-*/input*.bin | sort -t _ -k2 -n) &>>"$conf_dir/$name-log.txt" 199 | exit_code=$? 200 | set -e 201 | 202 | if [ $exit_code -eq $expected ]; then 203 | if grep "ERROR" $conf_dir/$name-log.txt &>/dev/null; then 204 | printf "\033[33;31merror\033[0m\n" 205 | elif grep "Errno" $conf_dir/$name-log.txt &>/dev/null; then 206 | printf "\033[33;31merror\033[0m\n" 207 | else 208 | printf "\033[33;32mok\033[0m\n" 209 | fi 210 | else 211 | printf "\033[33;31mfail\033[0m\n" 212 | fi 213 | } 214 | 215 | function verify() { 216 | local name=$1 217 | local expected=$2 218 | 219 | printf " validating ... " 220 | set +e 221 | violation_dir="$conf_dir/$name" 222 | config="$conf_dir/${name}-verify.yaml" 223 | awk "/Input seed:/{print \"input_gen_seed:\", \$4}" $violation_dir/violation-*/report.txt >>$config 224 | python ${revizor} reproduce -s $instructions -c $config -n $NUM_INPUTS -t $violation_dir/violation-*/program.asm -i $(ls $violation_dir/violation-*/input*.bin | sort -t _ -k2 -n) &>>"$conf_dir/$name-log.txt" 225 | exit_code=$? 226 | set -e 227 | 228 | if [ $exit_code -ne $expected ]; then 229 | if grep "ERROR" $conf_dir/$name-log.txt &>/dev/null; then 230 | printf "\033[33;31merror\033[0m\n" 231 | else 232 | printf "\033[33;32mok\033[0m\n" 233 | fi 234 | else 235 | printf "\033[33;31mfail\033[0m\n" 236 | fi 237 | } 238 | 239 | function fuzz_and_verify() { 240 | local name=$1 241 | 242 | fuzz $name 243 | reproduce $name $expected 244 | # verify $name $expected 245 | } 246 | 247 | function fuzz_no_verify() { 248 | local name=$1 249 | local expected=$2 250 | 251 | fuzz $name $expected 252 | reproduce $name $expected 253 | 254 | printf " no validation\n" 255 | } 256 | 257 | # ================================================================================================== 258 | # Measurements 259 | printf "Starting at $(date '+%H:%M:%S on %d.%m.%Y')\n\n" 260 | 261 | run "v1" 262 | run "v1-store" 263 | run "v1-var" 264 | run "v4" 265 | run "zdi" 266 | run "sco" 267 | run "ooo" 268 | -------------------------------------------------------------------------------- /docs/architecture.md: -------------------------------------------------------------------------------- 1 | # Revizor Architecture 2 | 3 | Below is a high-level overview of Revizor's architecture and its key modules. 4 | 5 | [THE FOLLOWING IS A WORK IN PROGRESS] 6 | 7 | ![architecture](assets/arch.png) 8 | 9 | Revizor has **five** chief components: 10 | 11 | 1. Test Case Generator 12 | 2. Input Generator 13 | 3. Model 14 | 4. Executor 15 | 5. Analyser 16 | 17 | The **Test Case Generator** and **Input Generator** are responsible for 18 | generating random test cases to be run through the **Model** and **Executor**. 19 | The results are examined by the **Analyser** for contract violations. 20 | 21 | ### Test Case (Program) Generator 22 | 23 | The TCG is responsible for generating random assembly test cases. It takes an 24 | Instruction Set Specification as input in order for it to understand the 25 | instructions and syntax it can use for generation. 26 | 27 | ### Input Generator 28 | 29 | The IG is responsible for generating the *inputs* that are passed into a test 30 | case created by the TCG. Largely, this means **register** and **memory** values 31 | that the microarchitecture will be primed with before executing the test case. 32 | In this way, a single test case program can be run across several different 33 | inputs, allowing for multiple contract traces (and later, hardware traces) to be 34 | collected for analysis. 35 | 36 | ### Model 37 | 38 | The Model's job is to accept test cases and inputs from the TCG & IG and 39 | *emulate* the test case to collect **contract traces**. A single test case seeded 40 | with several inputs (`N` inputs) will create several contract traces (`N` 41 | contract traces) as the model's output. These are passed to the Analyser to 42 | determine **input classes**. 43 | 44 | ### Executor 45 | 46 | The Executor, on the other side from the Model, is responsible for running the 47 | *same* test cases (with the *same* inputs) on physical hardware to collect 48 | **hardware traces**. Hardware traces from the same input class are collected and 49 | studied by the Analyser to detect **contract violations**. 50 | 51 | ### Analyser 52 | 53 | The Analyser receives contract traces from the Model and hardware traces from 54 | the Executor to accomplish two primary goals: 55 | 56 | 1. Compare contract traces to set up **input classes**. 57 | 2. Compare hardware traces to detect **contract violations**. 58 | 59 | 60 | ## Revizor Modules and Interfaces 61 | 62 | Revizor's implementation and [architecture](architecture.md) is separated into 63 | multiple Python files: 64 | 65 | * `cli.py` - implements the command-line interface of revizor. 66 | * `config.py` - implements parsing and managing of revizor's YAML configuration 67 | file. 68 | * `generator.py` - implements the **Test Case Generator** portion of 69 | [revizor's architecture](architecture.md). 70 | * `input_generator.py` - implements the **Input Generator** portion of 71 | [revizor's architecture](architecture.md). 72 | * `model.py` - implements the Unicorn-based **Model** portion of 73 | [revizor's architecture](architecture.md). 74 | * `executor.py` - implements the **Executor** portion of 75 | [revizor's architecture](architecture.md). 76 | * `analyser.py` - implements the **Analyser** portion of 77 | [revizor's architecture](architecture.md). 78 | * `postprocessor.py` - defines the `MinimizerViolation` class, used during 79 | `minimize` mode to reduce a violation-inducing test case down to a smaller 80 | size while still maintaining the violation-inducing behavior. 81 | * `fuzzer.py` - implements `fuzz` mode that utilizes all main components to 82 | perform end-to-end hardware fuzzing. 83 | * `factory.py` - used to configure revizor accordingly to the user provided 84 | YAML configuration. Implements a simplified version of the Factory pattern: 85 | Defines a series of dictionaries that allows revizor to choose 86 | between various contract, generation techniques, executors, analysers, etc. 87 | In future, it be also used to implement multiple-ISA support. 88 | * `interfaces.py` - defines abstract classes (i.e., interfaces) of all main 89 | components of revizor (e.g., abstract `Executor`, `Model`, `TestCase`, 90 | `Input`, etc) 91 | * `isa\_loader.py` - defines the `InstructionSet` class, used to load an 92 | ISA's specifications from a JSON file provided via the 93 | [command-line interface](user/cli.md). 94 | * `service.py` - defines logging, statistical, and other services to all other 95 | modules within revizor. 96 | 97 | ### Architecture-specific Implementation 98 | 99 | The modules above are ISA-independent. The architecture-specific implementations 100 | are located in the subdirectories. For example, the implementation of the modules 101 | for the x86-64 architecture is located in `src/x86/`. It's structure largely 102 | mirrors the main modules of revizor (e.g., `x86_model.py` contains x86-specific 103 | parts of the **Model** module). The only unique parts are: 104 | 105 | * `*_target_desc.py` - defines constants describing the ISA (e.g., a list of 106 | available registers) and some helper functions. 107 | * `get_spec.py` - a script for transforming the ISA description provided 108 | by the CPU vendor (different for every vendor) into a unified JSON format 109 | * `executor/` - contains a low-level implementation of the executor. The 110 | implementation will be different for each architecture. For black-box x86 CPUs, 111 | it is a Linux kernel module. 112 | 113 | ### Abstract Test Case 114 | 115 | This describes a number of Python classes within revizor that define parts of an 116 | assembly test case. Revizor's TCG uses them to generate syntactically-valid 117 | assembly. The classes are defined in `interfaces.py`. 118 | 119 | #### `OperandSpec` 120 | 121 | The `OperandSpec` class defines a set of valid operands for any given assembly 122 | instruction. Each `InstructionSpec` object (described below) contains a list of 123 | these operand specifications. It contains properties such as: 124 | 125 | * The `type` of operand 126 | * The `width` of the operand 127 | * Whether or not the operand is a `src` or `dest` operand 128 | 129 | #### `InstructionSpec` 130 | 131 | This class represents a single instruction specification. It contains a name 132 | (i.e. the actual instruction mnemonic, such as `ADD`) and a list of 133 | `OperandSpec`s, defining valid operands for the instruction. It also has a 134 | number of boolean flags that indicate unique attributes about the instruction, 135 | such as: 136 | 137 | * If the instruction contains a memory write 138 | * If the instruction is a control-flow instruction 139 | 140 | #### `Operand` 141 | 142 | The `Operand` class defines an actual operand to be used in an instruction 143 | placed into the TCG's generated test case (not to be confused with 144 | `OperandSpec`, which is a set of rules used to define possible operand choices 145 | for an instruction). This is an **abstract base class** that provides a number 146 | of sub-classes: 147 | 148 | * `RegisterOperand` 149 | * `MemoryOperand` 150 | * `ImmediateOperand` 151 | * `LabelOperand` 152 | * `AgenOperand` 153 | * `FlagsOperand` 154 | 155 | #### `Instruction` 156 | 157 | Similar to the relationship between `OperandSpec` and `Operand`, the 158 | `Instruction` class defines an actual instruction, constrained by an 159 | `InstructionSpec`, that is used during test case generation. It contains a list 160 | of `Operand`s and is linked to its neighboring instructions via object 161 | references. 162 | 163 | #### `BasicBlock` 164 | 165 | Thisi class represents a single basic block within the generated test case (a 166 | **basic block** is a straight-line sequence of assembly instructions that has a 167 | single entry and exit point). It contains a list of all instructions contained 168 | within, references to its successor basic block(s), and a list of "terminator" 169 | instructions (instructions that exit the basic block, such as a branch). 170 | 171 | #### `Function` 172 | 173 | This object represents a collection of basic blocks that form a function. It has 174 | an "entry" basic block and an "exit" basic block, along with a list of all basic 175 | blocks that comprise the function. 176 | 177 | #### `TestCaseDAG` 178 | 179 | **DAG** is short for **Directed Acyclic Graph**. This object represents the 180 | *entire* test case's control flow. It contains a list of functions that, within, 181 | define all instructions to be written out to the test case's assembly file. 182 | -------------------------------------------------------------------------------- /src/x86/executor/include/page_tables_common.h: -------------------------------------------------------------------------------- 1 | /// File: Common definitions for page tables 2 | /// 3 | // Copyright (C) Microsoft Corporation 4 | // SPDX-License-Identifier: MIT 5 | 6 | #ifndef _PAGE_TABLES_COMMON_H_ 7 | #define _PAGE_TABLES_COMMON_H_ 8 | 9 | #include "hardware_desc.h" 10 | #include // PAGE_SIZE 11 | #include 12 | 13 | #define ENTRIES_PER_PAGE (PAGE_SIZE / sizeof(uint64_t)) 14 | 15 | #define MODIFIABLE_PTE_BITS \ 16 | (_PAGE_PRESENT | _PAGE_RW | _PAGE_PWT | _PAGE_PCD | _PAGE_ACCESSED | _PAGE_DIRTY | \ 17 | _PAGE_PKEY_BIT0 | _PAGE_PKEY_BIT1 | _PAGE_PKEY_BIT2 | _PAGE_PKEY_BIT3 | _PAGE_NX | \ 18 | (1ULL << 51)) 19 | 20 | #define _E_PAGE_PRESENT (1 << 0) 21 | #define _E_PAGE_RW (1 << 1) 22 | #define _E_PAGE_X (1 << 2) 23 | #define _E_PAGE_ACCESSED (1 << 8) 24 | #define _E_PAGE_DIRTY (1 << 9) 25 | #define _E_PAGE_USER (1 << 10) 26 | 27 | #if VENDOR_ID == VENDOR_INTEL_ // Intel 28 | #define MODIFIABLE_EPTE_BITS \ 29 | (_E_PAGE_PRESENT | _E_PAGE_RW | _E_PAGE_X | _E_PAGE_ACCESSED | _E_PAGE_DIRTY | _E_PAGE_USER | \ 30 | (1ULL << 51)) 31 | #else 32 | #define MODIFIABLE_EPTE_BITS MODIFIABLE_PTE_BITS 33 | #endif 34 | 35 | // ================================================================================================= 36 | // Normal page tables 37 | // ================================================================================================= 38 | #define PML4_SHIFT 39 39 | #define PDPT_SHIFT 30 40 | #define PDT_SHIFT 21 41 | #define PT_SHIFT 12 42 | #define MAX_VADDR_BITS 48 43 | 44 | #define PML4_INDEX(vaddr) (((uint64_t)(vaddr) >> PML4_SHIFT) & 0x1FF) 45 | #define PDPT_INDEX(vaddr) (((uint64_t)(vaddr) >> PDPT_SHIFT) & 0x1FF) 46 | #define PDT_INDEX(vaddr) (((uint64_t)(vaddr) >> PDT_SHIFT) & 0x1FF) 47 | #define PT_INDEX(vaddr) (((uint64_t)(vaddr) >> PT_SHIFT) & 0x1FF) 48 | 49 | // Table 4-15. Format of a PML4 Entry (PML4E) that References a Page-Directory-Pointer Table 50 | typedef struct { 51 | uint64_t present : 1; 52 | uint64_t write_access : 1; 53 | uint64_t user_supervisor : 1; 54 | uint64_t page_write_through : 1; 55 | uint64_t page_cache_disable : 1; 56 | uint64_t accessed : 1; 57 | uint64_t ignored : 1; 58 | uint64_t reserved_zero : 1; 59 | uint64_t ignored_11_8 : 4; 60 | uint64_t paddr : (PHYSICAL_WIDTH - 12); 61 | #if PHYSICAL_WIDTH < 52 62 | uint64_t reserved_51_M : (52 - PHYSICAL_WIDTH); 63 | #endif 64 | uint64_t ignored_62_52 : 11; 65 | uint64_t execute_disable : 1; 66 | } __attribute__((packed)) pml4e_t; 67 | 68 | // Table 4-17. Format of a Page-Directory-Pointer-Table Entry (PDPTE) that 69 | // References a Page Directory 70 | typedef struct { 71 | uint64_t present : 1; 72 | uint64_t write_access : 1; 73 | uint64_t user_supervisor : 1; 74 | uint64_t page_write_through : 1; 75 | uint64_t page_cache_disable : 1; 76 | uint64_t accessed : 1; 77 | uint64_t ignored : 1; 78 | uint64_t reserved_zero : 1; 79 | uint64_t ignored_11_8 : 4; 80 | uint64_t paddr : (PHYSICAL_WIDTH - 12); 81 | #if PHYSICAL_WIDTH < 52 82 | uint64_t reserved_51_M : (52 - PHYSICAL_WIDTH); 83 | #endif 84 | uint64_t ignored_62_52 : 11; 85 | uint64_t execute_disable : 1; 86 | } __attribute__((packed)) pdpte_t; 87 | 88 | // Table 4-19. Format of a Page-Directory Entry that References a Page Table 89 | typedef struct { 90 | uint64_t present : 1; 91 | uint64_t write_access : 1; 92 | uint64_t user_supervisor : 1; 93 | uint64_t page_write_through : 1; 94 | uint64_t page_cache_disable : 1; 95 | uint64_t accessed : 1; 96 | uint64_t ignored : 1; 97 | uint64_t reserved_zero : 1; 98 | uint64_t ignored_11_8 : 4; 99 | uint64_t paddr : (PHYSICAL_WIDTH - 12); 100 | #if PHYSICAL_WIDTH < 52 101 | uint64_t reserved_51_M : (52 - PHYSICAL_WIDTH); 102 | #endif 103 | uint64_t ignored_62_52 : 11; 104 | uint64_t execute_disable : 1; 105 | } __attribute__((packed)) pdte_t; 106 | 107 | // Table 4-20. Format of a Page-Table Entry that Maps a 4-KByte Page 108 | typedef struct { 109 | uint64_t present : 1; 110 | uint64_t write_access : 1; 111 | uint64_t user_supervisor : 1; 112 | uint64_t page_write_through : 1; 113 | uint64_t page_cache_disable : 1; 114 | uint64_t accessed : 1; 115 | uint64_t dirty : 1; 116 | uint64_t page_attribute_table : 1; 117 | uint64_t global_page : 1; 118 | uint64_t ignored_11_9 : 3; 119 | uint64_t paddr : (PHYSICAL_WIDTH - 12); 120 | #if PHYSICAL_WIDTH < 52 121 | uint64_t reserved_51_M : (52 - PHYSICAL_WIDTH); 122 | #endif 123 | uint64_t ignored_58_52 : 7; 124 | uint64_t protection_key : 4; 125 | uint64_t execute_disable : 1; 126 | } __attribute__((packed)) pte_t_; // using pte_t_ as pte_t is already defined in linux/types.h 127 | 128 | // ================================================================================================= 129 | // Extended page tables 130 | // ================================================================================================= 131 | 132 | // Figure 29-1. Formats of EPTP and EPT Paging-Structure Entries 133 | typedef struct { 134 | uint64_t memory_type : 3; 135 | uint64_t page_walk_length : 3; 136 | uint64_t ad_enabled : 1; 137 | uint64_t superv_sdw_stack : 1; 138 | uint64_t reserved_11_08 : 4; 139 | uint64_t paddr : (PHYSICAL_WIDTH - 12); 140 | #if PHYSICAL_WIDTH < 52 141 | uint64_t reserved_51_M : (52 - PHYSICAL_WIDTH); 142 | #endif 143 | uint64_t reserved_63_52 : 12; 144 | } __attribute__((packed)) eptp_t; 145 | 146 | #if VENDOR_ID == 1 // Intel 147 | // Table 28-1. Format of an EPT PML4E 148 | typedef struct { 149 | uint64_t read_access : 1; 150 | uint64_t write_access : 1; 151 | uint64_t execute_access : 1; 152 | uint64_t reserved_7_3 : 5; 153 | uint64_t accessed : 1; 154 | uint64_t ignored_9 : 1; 155 | uint64_t user_ex_access : 1; 156 | uint64_t ignored_11 : 1; 157 | uint64_t paddr : (PHYSICAL_WIDTH - 12); 158 | #if PHYSICAL_WIDTH < 52 159 | uint64_t reserved_51_M : (52 - PHYSICAL_WIDTH); 160 | #endif 161 | uint64_t ignored_63_52 : 12; 162 | } __attribute__((packed)) epml4e_t; 163 | #else 164 | typedef pml4e_t epml4e_t; 165 | #endif 166 | 167 | #if VENDOR_ID == 1 // Intel 168 | // Table 28-3. Format of an EPT Page-Directory-Pointer-Table Entry (EPT PDPTE) 169 | typedef struct { 170 | uint64_t read_access : 1; 171 | uint64_t write_access : 1; 172 | uint64_t execute_access : 1; 173 | uint64_t reserved_6_3 : 4; 174 | uint64_t reserved_7 : 1; 175 | uint64_t accessed : 1; 176 | uint64_t ignored_9 : 1; 177 | uint64_t user_ex_access : 1; 178 | uint64_t ignored_11 : 1; 179 | uint64_t paddr : (PHYSICAL_WIDTH - 12); 180 | #if PHYSICAL_WIDTH < 52 181 | uint64_t reserved_51_M : (52 - PHYSICAL_WIDTH); 182 | #endif 183 | uint64_t ignored_63_52 : 12; 184 | } __attribute__((packed)) epdpte_t; 185 | #else 186 | typedef pdpte_t epdpte_t; 187 | #endif 188 | 189 | #if VENDOR_ID == 1 // Intel 190 | typedef struct { 191 | uint64_t read_access : 1; 192 | uint64_t write_access : 1; 193 | uint64_t execute_access : 1; 194 | uint64_t reserved_6_3 : 4; 195 | uint64_t reserved_7 : 1; 196 | uint64_t accessed : 1; 197 | uint64_t ignored_9 : 1; 198 | uint64_t user_ex_access : 1; 199 | uint64_t ignored_11 : 1; 200 | uint64_t paddr : (PHYSICAL_WIDTH - 12); 201 | #if PHYSICAL_WIDTH < 52 202 | uint64_t reserved_51_M : (52 - PHYSICAL_WIDTH); 203 | #endif 204 | uint64_t ignored_63_52 : 12; 205 | } __attribute__((packed)) epdte_t; 206 | #else 207 | typedef pdte_t epdte_t; 208 | #endif 209 | 210 | #if VENDOR_ID == 1 // Intel 211 | typedef struct { 212 | uint64_t read_access : 1; 213 | uint64_t write_access : 1; 214 | uint64_t execute_access : 1; 215 | uint64_t ept_mem_type : 3; 216 | uint64_t ignore_pat : 1; 217 | uint64_t ignored_7 : 1; 218 | uint64_t accessed : 1; 219 | uint64_t dirty : 1; 220 | uint64_t user_ex_access : 1; 221 | uint64_t ignored_11 : 1; 222 | uint64_t paddr : (PHYSICAL_WIDTH - 12); 223 | #if PHYSICAL_WIDTH < 52 224 | uint64_t reserved_51_M : (52 - PHYSICAL_WIDTH); 225 | #endif 226 | uint64_t ignored_56_52 : 5; 227 | uint64_t verif_guest_pag : 1; 228 | uint64_t pag_write_access : 1; 229 | uint64_t ignored_59 : 1; 230 | uint64_t superv_sdw_stack : 1; 231 | uint64_t subpg_write_perm : 1; 232 | uint64_t ignored_62 : 1; 233 | uint64_t suppress_ve : 1; 234 | } __attribute__((packed)) epte_t_; 235 | #else 236 | typedef pte_t_ epte_t_; 237 | #endif 238 | 239 | #endif // _PAGE_TABLES_COMMON_H_ 240 | --------------------------------------------------------------------------------