From fd44f07e69fce0cd9ecc6530c18a0d14bfe41167 Mon Sep 17 00:00:00 2001 From: Oleksii Oleksenko Date: Tue, 4 Apr 2023 15:13:06 +0100 Subject: [PATCH] docs: update all examples with pip-based installation as a default --- README.md | 230 ++++++++++++++-------------- demo/README.md | 14 +- docs/architecture.md | 87 ----------- docs/cli.md | 2 +- docs/{modules.md => development.md} | 80 +++++++++- docs/fuzzing-guide.md | 13 +- docs/install.md | 125 +++++---------- docs/quick-start.md | 70 ++++++--- mkdocs.yml | 3 +- src/x86/executor/readme.md | 13 +- 10 files changed, 292 insertions(+), 345 deletions(-) delete mode 100644 docs/architecture.md rename docs/{modules.md => development.md} (66%) diff --git a/README.md b/README.md index 78882869..c25d0839 100644 --- a/README.md +++ b/README.md @@ -1,160 +1,159 @@ # Revizor -This is Revizor, a microarchitectural fuzzer. -It is a rather unconventional fuzzer as, instead of finding bugs in programs, Revizor searches for microarchitectural vulnerabilities in CPUs. +![GitHub](https://img.shields.io/github/license/microsoft/sca-fuzzer) +![PyPI](https://img.shields.io/pypi/v/revizor-fuzzer) +![GitHub all releases](https://img.shields.io/github/downloads/microsoft/sca-fuzzer/total) +![GitHub contributors](https://img.shields.io/github/contributors/microsoft/sca-fuzzer) + -What is a microarchitectural vulnerability? -In the context of Revizor, it is a violation of out expectations about the CPU behavior, expressed as contract violations (see [Contracts](https://arxiv.org/abs/2006.03841)). -The most prominent examples would be [Spectre](https://spectreattack.com/), [Meltdown](https://meltdownattack.com/), and other speculative execution vulnerabilities. -Alternatively, a "bug" could also be in a form of a microarchitectural backdoor, although we are yet to encounter one of those. +Revizor is a security-oriented fuzzer for detecting information leaks in CPUs, such as [Spectre and Meltdown](https://meltdownattack.com/). +It tests CPUs against [Leakage Contracts](https://arxiv.org/abs/2006.03841) and searches for unexpected leaks. For more details, see our [Paper](https://dl.acm.org/doi/10.1145/3503222.3507729) (open access [here](https://arxiv.org/abs/2105.06872)), and the [follow-up paper](https://arxiv.org/pdf/2301.07642.pdf). -# Getting Started - -If you find a bug in Revizor, don't hesitate to [open an issue](https://github.com/microsoft/sca-fuzzer/issues). -If something is confusing or you need help in using Revizor, we have a [discussion page](https://github.com/microsoft/sca-fuzzer/discussions). - -## Before you use Revizor +## Installation -Keep in mind that Revizor executes randomly-generated code in kernel space. +**Warning**: +Keep in mind that the Revizor runs randomly-generated code in kernel space. As you can imagine, things could go wrong. -We are doing our best to avoid it by thoroughly testing the tool and making sure it is stable, but still, no software is perfect. -Make sure you're not running these experiments on an important machine. - -## Requirements & Dependencies +Make sure you're not running Revizor on an important machine. -### 1. Hardware Requirements +### 1. Check Requirements -Revizor supports Intel and AMD x86-64 CPUs. +* Architecture: Revizor supports Intel and AMD x86-64 CPUs. +We also have experimental support for ARM CPUs (see `arm-port` branch) but it is at very early stages, use it on your own peril. -We also have experimental support for ARM CPUs (see `arm-port` branch) but use it on your own peril. +* No virtualization: You will need a bare-metal OS installation. +Testing from inside a VM is not (yet) supported. -### 2. Software Requirements +* OS: The target machine has to be running Linux v4.15 or later. -* OS and virtualization: -You will need a Linux bare-metal installation. -Other operating systems and testing from inside a VM is not (yet) supported. +### 2. Install Revizor Python Package -* Linux kernel: v4.15 or later. -Older kernels may or may not work - we have not tested on them. +If you use `pip`, you can install Revizor with: -To check your current Linux kernel version: -```shell -cat /proc/version +```bash +pip install revizor-fuzzer ``` -* Linux Kernel Headers - -```shell -# On Ubuntu -sudo apt-get install linux-headers-$(uname -r) +Alternatively, install Revizor from sources: +```bash +# run from the project root directory +make install ``` -* [Python 3.9+](https://www.python.org/downloads/) +### 3. Install Revizor Executor (kernel module) -If your distribution has an older python by default, the best practice is to use a virtual environment. -```shell -# On Ubuntu 18 -sudo apt install python3.9 python3.9-distutils +Then build and install the kernel module: + +```bash +# building a kernel module require kernel headers +sudo apt-get install linux-headers-$(uname -r) -python3.9 -m venv venv +# get the source code +git clone https://github.com/microsoft/sca-fuzzer.git -# re-run this command every time you open a new terminal session -source ./venv/bin/activate +# build the executor +cd sca-fuzzer/src/x86/executor +make uninstall # the command will give an error message, but it's ok! +make clean +make +make install ``` -* [Unicorn 1.0.2+](https://www.unicorn-engine.org/docs/). -Preferably, use version 1.0.2 or 1.0.3 as they seem to be currently the most stable. -We've encountered several bugs when using the most recent version 2.0. - +### 4. Download ISA spec -```shell -sudo apt install unicorn +```bash +rvzr download_spec -a x86-64 --extensions BASE SSE SSE2 CLFLUSHOPT CLFSH --outfile base.json ``` -* Python bindings to Unicorn +### 5. (Optional) System Configuration -```shell -pip3 install --user unicorn +For more stable results, disable hyperthreading (there's usually a BIOS option for it). +If you do not disable hyperthreading, you will see a warning every time you invoke Revizor; you can ignore it. -# OR, if installed from sources -cd bindings/python -sudo make install +Optionally (and it *really* is optional), you can boot the kernel on a single core by adding `-maxcpus=1` to the boot parameters ([how to add a boot parameter](https://wiki.ubuntu.com/Kernel/KernelBootParameters)). -# if you're using venv, copy the installation (the paths in your installation may differ) -cp -r /usr/local/lib/python3.6/site-packages/unicorn-1.0.3-py3.8.egg/unicorn/ venv/lib64/python3.9/site-packages/ -``` -* Python packages `pyyaml`, `types-pyyaml`, `numpy`: +## Command Line Interface -```shell -pip3 install --user pyyaml types-pyyaml numpy # skip --user if you're installing in venv -``` +The fuzzer is controlled via a single command line interface `rvzr` (or `revizor.py` if you're running directly from the source directory). -### 3. Software Requirements for Revizor Development +It accepts the following arguments: +* `-s, --instruction-set PATH` - path to the ISA description file +* `-c, --config PATH` - path to the fuzzing configuration file +* `-n , --num-test-cases N` - number of test cases to be tested +* `-i , --num-inputs N` - number of input classes per test case. The number of actual inputs = input classes * inputs_per_class, which is a configuration option +* `-t , --testcase PATH` - use an existing test case instead of generating random test cases +* `--timeout TIMEOUT` - run fuzzing with a time limit [seconds] +* `-w` - working directory where the detected violations will be stored -Tests: -* [Bash Automated Testing System](https://bats-core.readthedocs.io/en/latest/index.html) -* [mypy](https://mypy.readthedocs.io/en/latest/getting_started.html#installing-and-running-mypy) -* [flake8](https://flake8.pycqa.org/en/latest/index.html) +For example, this command +```bash +rvzr fuzz -s base.json -n 100 -i 10 -c config.yaml -w ./violations +``` +will run the fuzzer for 100 iterations (i.e., 100 test cases), with 10 inputs per test case. +The fuzzer will use the ISA spec stored in the `base.json` file, and will read the configuration from `config.yaml`. If the fuzzer finds a violation, it will be stored in the `./violations` directory. +See [docs](https://microsoft.github.io/sca-fuzzer/cli/) for more details. -Documentation: -* [pdoc3](https://pypi.org/project/pdoc3/) +## How To Fuzz With Revizor -### 4. (Optional) System Configuration +The fuzzing process is controlled by a configuration file in the YAML format, passed via `--config` option. At the very minimum, this file should contain the following fields: +* `contract_observation_clause` and `contract_execution_clause` describe the contract that the CPU-under-test is tested against. See [this page](https://microsoft.github.io/sca-fuzzer/config/) for a list of available contracts. If you don't know what a contract is, Sec. 3 of [this paper](https://arxiv.org/pdf/2105.06872.pdf) will give you a high-level introduction to contracts, and [this paper](https://www.microsoft.com/en-us/research/publication/hardware-software-contracts-for-secure-speculation/) will provide a deep dive into contracts. +* `instruction_categories` is a list of instruction types that will be tested. Effectively, Revizor uses this list to filter out instructions from `base.json` (the file you downloaded via `rvzr download_spec`). -For more stable results, disable hyperthreading (there's usually a BIOS option for it). -If you do not disable hyperthreading, you will see a warning every time you invoke Revizor; you can ignore it. +For a full list of configuration options, see [docs](https://microsoft.github.io/sca-fuzzer/config/). -Optionally (and it *really* is optional), you can boot the kernel on a single core by adding `-maxcpus=1` to the boot parameters ([how to add a boot parameter](https://wiki.ubuntu.com/Kernel/KernelBootParameters)). +### Baseline Experiment -In addition, you might want to stop any other actively-running software on the tested machine. We never encountered issues with it, but it might be useful. +After a fresh installation, it is normally a good idea to do a quick test run to check that everything works ok. -## Installation +For example, we can create a configuration file `config.yaml` with only simple arithmetic instructions. As this instruction set does not include any instructions that would trigger speculation on Intel or AMD CPUs (at least that we know of), the expected contract would be `CT-SEQ`: -### 1. Get the x86-64 ISA description: +```yaml +# config.yaml +instruction_categories: + - BASE-BINARY # arithmetic instructions +max_bb_per_function: 1 # no branches! +min_bb_per_function: 1 -```bash -cd src/x86/isa_spec -./get_spec.py --extensions BASE SSE SSE2 CLFLUSHOPT CLFSH +contract_observation_clause: loads+stores+pc # aka CT +contract_execution_clause: + - no_speculation # aka SEQ ``` -### 2. Install the executor kernel module: - +Start the fuzzer: ```bash -cd src/x86/executor -make uninstall # the command will give an error message, but it's ok! -make clean -make -make install +rvzr fuzz -s base.json -i 50 -n 100 -c config.yaml -w . ``` -## Running Tests +This command should terminate with no violations. -```bash -cd src/tests -./runtests.sh -``` -If a few (up to 3) "Detection" tests fail, it's fine, you might just have a slightly different microarchitecture. But if other tests fail - something is broken. +### Detection of a Simple Contract Violation -## Basic Usability Test +Next, we could intentionally make a mistake in a contract to check that Revizor can detect it. +To this end, we can modify the config file from the previous example to include instructions that trigger speculation (e.g., conditional branches) but keep the contract the same: +```yaml +# config.yaml +instruction_categories: + - BASE-BINARY # arithmetic instructions + - BASE-COND_BR +max_bb_per_function: 5 # up to 5 branches per test case +min_bb_per_function: 1 -1. Fuzz in a violation-free configuration: -```bash -./cli.py fuzz -s x86/isa_spec/base.json -i 50 -n 100 -c tests/test-nondetection.yaml +contract_observation_clause: loads+stores+pc # aka CT +contract_execution_clause: + - no_speculation # aka SEQ ``` -No violations should be detected. - -2. Fuzz in a configuration with a known contract violation (Spectre V1): +Start the fuzzer: ```bash -./cli.py fuzz -s x86/isa_spec/base.json -i 20 -n 1000 -c tests/test-detection.yaml +rvzr fuzz -s base.json -i 50 -n 1000 -c config.yaml -w . ``` -A violation should be detected within a few minutes, with a message similar to this: +As your CPU-under-test almost definitely implements branch prediction, Revizor should detect a violation within a few minutes, with a message similar to this: ``` ================================ Violations detected ========================== @@ -169,36 +168,29 @@ A violation should be detected within a few minutes, with a message similar to t ``` -Congratulations, you just found your first Spectre! You can find the violating test case in `generated.asm`. +You can find the violating test case as well as the violation report in the directory named `./violation-*/`. +It will contain an assembly file `program.asm` that surfaced a violation, a sequence of inputs `input-*.bin` to this program, and some details about the violation in `report.txt`. -# Fuzzing Example +### Full-Scale Fuzzing Campaign -To start a real fuzzing campaign, write your own configuration file (see description [here](docs/config.md) and an example config [here](src/tests/big-fuzz.yaml)), and launch the fuzzer. - -Below is a example launch command, which will start a 24-hour fuzzing session, with 100 input classes per test case: +To start a full-scale test, write your own configuration file (see description [here](config.md) and an example config [here](https://github.com/microsoft/sca-fuzzer/tree/main/src/tests/big-fuzz.yaml)), and launch the fuzzer. +Below is a example launch command, which will start a 24-hour fuzzing session, with 100 input classes per test case, and which uses [big-fuzz.yaml](https://github.com/microsoft/sca-fuzzer/tree/main/src/tests/big-fuzz.yaml) configuration: ```shell -./cli.py fuzz -s x86/isa_spec/base.json -c tests/big-fuzz.yaml -i 100 -n 100000000 --timeout 86400 -w `pwd` --nonstop +rvzr fuzz -s base.json -c src/tests/big-fuzz.yaml -i 100 -n 100000000 --timeout 86400 -w `pwd` --nonstop ``` -For more examples, see the `demo/` directory. +When you find a violation, you will have to do some manual investigation to understand the source of it; [this guide](fuzzing-guide.md) is an example of how to do such an investigation. -# Command line interface +## Need Help with Revizor? -The fuzzer is controlled via a single command line interface `cli.py` (located in `src/cli.py`). It accepts the following arguments: +If you find a bug in Revizor, don't hesitate to [open an issue](https://github.com/microsoft/sca-fuzzer/issues). -* `-s, --instruction-set PATH` - path to the ISA description file -* `-c, --config PATH` - path to the fuzzing configuration file -* `-n , --num-test-cases N` - number of test cases to be tested -* `-i , --num-inputs N` - number of input classes per test case. The number of actual inputs = input classes * inputs_per_class, which is a configuration option -* `-t , --testcase PATH` - use an existing test case instead of generating random test cases -* `--timeout TIMEOUT` - run fuzzing with a time limit [seconds] -* `--nonstop` - don't stop after detecting a contract violation -* `-w` - working directory where the detected violations will be stored +If something is confusing or you need help in using Revizor, we have a [discussion page](https://github.com/microsoft/sca-fuzzer/discussions). -# Documentation +## Documentation -For more details, see [docs/main.md](docs/main.md). +For more details, see [the website](https://microsoft.github.io/sca-fuzzer/). ## Contributing diff --git a/demo/README.md b/demo/README.md index e535896d..4ab37368 100644 --- a/demo/README.md +++ b/demo/README.md @@ -6,27 +6,26 @@ For example, if you fuzz an Intel CPU with `conf-v1.yaml`, you will likely detec This demo targets Intel CPUs. Other microarchitectures are not yet supported (but coming soon!). +The commands below assume that the ISA spec (downloaded via `rvzr download_spec`) is stored in `base.json`. + * Spectre V1 ([description](https://meltdownattack.com/)): ``` -cd src -./cli.py fuzz -s x86/isa_spec/base.json -c ../demo/conf-v1.yaml -i 50 -n 10000 +rvzr fuzz -s base.json -c demo/conf-v1.yaml -i 50 -n 10000 ``` Expected duration - several seconds. * MDS or LVI-Null, depending on the CPU model ([description of MDS](https://mdsattacks.com/) and [LVI](https://lviattack.eu/)): ``` -cd src -./cli.py fuzz -s x86/isa_spec/base.json -c ../demo/conf-v1.yaml -i 50 -n 10000 +rvzr fuzz -s base.json -c demo/conf-v1.yaml -i 50 -n 10000 ``` Expected duration - several minutes. * Spectre V4 ([description](https://www.cyberus-technology.de/posts/2018-05-22-intel-store-load-spectre-vulnerability.html)): ``` -cd src -./cli.py fuzz -s x86/isa_spec/base.json -c ../demo/conf-v4.yaml -i 50 -n 10000 +rvzr fuzz -s base.json -c demo/conf-v4.yaml -i 50 -n 10000 ``` Expected duration - 5-20 minutes. @@ -34,7 +33,6 @@ Expected duration - 5-20 minutes. * Spectre V1-Var ([description](https://dl.acm.org/doi/10.1145/3503222.3507729) and [here](https://eprint.iacr.org/2022/715.pdf)) ``` -cd src -./cli.py fuzz -s x86/isa_spec/base.json -c ../demo/conf-v1.yaml -i 50 -n 10000 +rvzr fuzz -s base.json -c demo/conf-v1.yaml -i 50 -n 10000 ``` Expected duration - several minutes. diff --git a/docs/architecture.md b/docs/architecture.md deleted file mode 100644 index d147139f..00000000 --- a/docs/architecture.md +++ /dev/null @@ -1,87 +0,0 @@ -# Revizor's Architecture - -![architecture](assets/arch.png) - -Revizor has **five** chief components: - -1. Test Case Generator -2. Input Generator -3. Model -4. Executor -5. Analyser - -The **Test Case Generator** and **Input Generator** are responsible for -generating random test cases to be run through the **Model** and **Executor**. -The results are examined by the **Analyser** for contract violations. - -## Test Case Generator - -The TCG is responsible for generating random assembly test cases. It takes an -Instruction Set Specification as input in order for it to understand the -instructions and syntax it can use for generation. - -## Input Generator - -The IG is responsible for generating the *inputs* that are passed into a test -case created by the TCG. Largely, this means **register** and **memory** values -that the microarchitecture will be primed with before executing the test case. -In this way, a single test case program can be run across several different -inputs, allowing for multiple contract traces (and later, hardware traces) to be -collected for analysis. - -## Model - -The Model's job is to accept test cases and inputs from the TCG & IG and -*emulate* the test case to collect **contract traces**. A single test case seeded -with several inputs (`N` inputs) will create several contract traces (`N` -contract traces) as the model's output. These are passed to the Analyser to -determine **input classes**. - -## Executor - -The Executor, on the other side from the Model, is responsible for running the -*same* test cases (with the *same* inputs) on physical hardware to collect -**hardware traces**. Hardware traces from the same input class are collected and -studied by the Analyser to detect **contract violations**. - -## Analyser - -The Analyser receives contract traces from the Model and hardware traces from -the Executor to accomplish two primary goals: - -1. Compare contract traces to set up **input classes**. -2. Compare hardware traces to detect **contract violations**. - -[comment]: <> (## Instruction Set Spec) - -[comment]: <> (This XML file: https://www.uops.info/xml.html originating from Intel XED (https://intelxed.github.io/)) - -[comment]: <> (Received from: `--instruction-set` (or `-s`) CLI argument.) - -[comment]: <> (Passed down to: `Generator.__init__`) - - -[comment]: <> (## Generator Initializer) - -[comment]: <> (None so far.) - -[comment]: <> (In future, may include test case templates, grammar, etc.) - -[comment]: <> (## Test Case) - -[comment]: <> (An assembly file. Currently, in Intel syntax.) - -[comment]: <> (Received from: `self.generator.create_test_case()` + `self.generator.materialize(filename)`) - -[comment]: <> (Passed down to: `model.load_test_case` and `executor.load_test_case`) - - -[comment]: <> (## Inputs) - -[comment]: <> (Currently, each input is a single 32-bit integer, used later as a PRNG seed inside the test case to initialize memory and registers.) - -[comment]: <> (Inputs are generated in batches; that is, Input Generator returns `List[int]`.) - -[comment]: <> (Received from: `input_gen.generate(...)`) - -[comment]: <> (Passed down to: `model.trace_test_case(inputs)` and `executor.trace_test_case(inputs)`.) diff --git a/docs/cli.md b/docs/cli.md index 7b74876f..d80ff74e 100644 --- a/docs/cli.md +++ b/docs/cli.md @@ -14,7 +14,7 @@ Revizor can run in one of multiple "modes": To select a mode on the command-line, begin your command with: ```shell -cli.py MODE # ... arguments go here +rvzr MODE # ... arguments go here # Where MODE can be: # fuzz for fuzzing mode diff --git a/docs/modules.md b/docs/development.md similarity index 66% rename from docs/modules.md rename to docs/development.md index e1d88a0b..5ef3f405 100644 --- a/docs/modules.md +++ b/docs/development.md @@ -1,3 +1,78 @@ +# Development + +This page contains various bits of information helpful when developing and expanding Revizor. + +# Running Tests + +To run automated tests you will need to install a few more dependencies: +* [Bash Automated Testing System](https://bats-core.readthedocs.io/en/latest/index.html) +* [mypy](https://mypy.readthedocs.io/en/latest/getting_started.html#installing-and-running-mypy) +* [flake8](https://flake8.pycqa.org/en/latest/index.html) + +With the dependencies installed, you can run the tests with: + +```bash +make test +``` + +If a few (up to 3) "Detection" tests fail, it's fine, you might just have a slightly different microarchitecture. +But if other tests fail - something is broken. + +# Revizor's Architecture + +![architecture](assets/arch.png) + +Revizor has **five** chief components: + +1. Test Case Generator +2. Input Generator +3. Model +4. Executor +5. Analyser + +The **Test Case Generator** and **Input Generator** are responsible for +generating random test cases to be run through the **Model** and **Executor**. +The results are examined by the **Analyser** for contract violations. + +## Test Case Generator + +The TCG is responsible for generating random assembly test cases. It takes an +Instruction Set Specification as input in order for it to understand the +instructions and syntax it can use for generation. + +## Input Generator + +The IG is responsible for generating the *inputs* that are passed into a test +case created by the TCG. Largely, this means **register** and **memory** values +that the microarchitecture will be primed with before executing the test case. +In this way, a single test case program can be run across several different +inputs, allowing for multiple contract traces (and later, hardware traces) to be +collected for analysis. + +## Model + +The Model's job is to accept test cases and inputs from the TCG & IG and +*emulate* the test case to collect **contract traces**. A single test case seeded +with several inputs (`N` inputs) will create several contract traces (`N` +contract traces) as the model's output. These are passed to the Analyser to +determine **input classes**. + +## Executor + +The Executor, on the other side from the Model, is responsible for running the +*same* test cases (with the *same* inputs) on physical hardware to collect +**hardware traces**. Hardware traces from the same input class are collected and +studied by the Analyser to detect **contract violations**. + +## Analyser + +The Analyser receives contract traces from the Model and hardware traces from +the Executor to accomplish two primary goals: + +1. Compare contract traces to set up **input classes**. +2. Compare hardware traces to detect **contract violations**. + + # Revizor Modules and Interfaces Revizor's implementation and [architecture](architecture.md) is separated into @@ -46,9 +121,9 @@ parts of the **Model** module). The only unique parts are: * `*_target_desc.py` - defines constants describing the ISA (e.g., a list of available registers) and some helper functions. -* `isa_spec/get_spec.py` - a script for transforming the ISA description provided +* `get_spec.py` - a script for transforming the ISA description provided by the CPU vendor (different for every vendor) into a unified JSON format -* `executor/` - contains a low-level implementation of the executor. The +* `executor/` - contains a low-level implementation of the executor. The implementation will be different for each architecture. For black-box x86 CPUs, it is a Linux kernel module. @@ -121,4 +196,3 @@ blocks that comprise the function. **DAG** is short for **Directed Acyclic Graph**. This object represents the *entire* test case's control flow. It contains a list of functions that, within, define all instructions to be written out to the test case's assembly file. - diff --git a/docs/fuzzing-guide.md b/docs/fuzzing-guide.md index 0d8ead74..ce55a42f 100644 --- a/docs/fuzzing-guide.md +++ b/docs/fuzzing-guide.md @@ -56,11 +56,12 @@ We save the configuration into a file (`config.yaml`) and start a fuzzing campai We start Revizor with the following command: ```shell -./cli.py fuzz -s x86/isa_spec/base.json -c config.yaml -n 100000 -i 100 -w ./results +rvzr fuzz -s base.json -c config.yaml -n 100000 -i 100 -w ./results ``` +(use `./revizor.py` instead of `rvzr` if you want to run Revizor directly from the source directory) Here -* `-s x86/isa_spec/base.json` - tells Revizor where to find a description of the tested instructions +* `-s base.json` - tells Revizor where to find a description of the tested instructions * `-c config.yaml` - points Revizor to the configuration file described above * `-n 100000` - number of randomly-generated programs to be tested. Note that 100k programs will be tested only if none of them surfaces a contract violation; otherwise, Revizor will stop as soon as it detects a violation * `-i 100` - number of inputs per test case @@ -138,7 +139,7 @@ Fortunately, we don't have to do it, as there are several techniques that can si ### 1. Remove irrelevant instructions from the program ```shell -./cli.py minimize -s x86/isa_spec/base.json -c config.yaml -i /results/violation.asm -o min.asm -n 100 +rvzr minimize -s base.json -c config.yaml -i /results/violation.asm -o min.asm -n 100 ``` It simplifies the program and stores the result into `min.asm`. The result is: @@ -201,13 +202,13 @@ MFENCE # instrumentation To make sure that we didn't make a mistake while modifying the program, we can verify the result by reproducing the violation: ```shell -./cli.py fuzz -s x86/isa_spec/base.json -c config.yaml -t min.asm -i 100 +rvzr fuzz -s base.json -c config.yaml -t min.asm -i 100 ``` ### 2. Add speculation fences to narrow down the part of the program that causes leakage ```shell -./cli.py minimize -s x86/isa_spec/base.json -c config.yaml -i min.asm -o min.asm -n 100 --add-fences +rvzr minimize -s base.json -c config.yaml -i min.asm -o min.asm -n 100 --add-fences ``` This command iteratively attempts to add an `LFENCE` before every instruction in the program while checking if the violation persists. The result is: @@ -248,7 +249,7 @@ We go through the program, try removing instructions one at a time, execute the For example, let's say we start from the bottom. We first try to remove the last line (`LOCK SBB byte ptr [R14 + RAX], CL`), and execute the program on Revizor: ```shell -./cli.py fuzz -s x86/isa_spec/base.json -c config.yaml -t min.asm -i 100 +rvzr fuzz -s base.json -c config.yaml -t min.asm -i 100 INFO: [fuzzer] Starting at 17:16:52 0 ( 0%)| Stats: diff --git a/docs/install.md b/docs/install.md index a14cf48c..bf8978e6 100644 --- a/docs/install.md +++ b/docs/install.md @@ -1,117 +1,62 @@ -## Requirements & Dependencies +# Installation -### 1. Hardware Requirements +**Warning**: +Keep in mind that the Revizor runs randomly-generated code in kernel space. +As you can imagine, things could go wrong. +Make sure you're not running Revizor on an important machine. -Revizor supports Intel and AMD x86-64 CPUs. +## 1. Check Requirements -We also have experimental support for ARM CPUs (see `arm-port` branch) but use it on your own peril. +* Architecture: Revizor supports Intel and AMD x86-64 CPUs. +We also have experimental support for ARM CPUs (see `arm-port` branch) but it is at very early stages, use it on your own peril. -### 2. Software Requirements +* No virtualization: You will need a bare-metal OS installation. +Testing from inside a VM is not (yet) supported. -* OS and virtualization: -You will need a Linux bare-metal installation. -Other operating systems and testing from inside a VM is not (yet) supported. +* OS: The target machine has to be running Linux v4.15 or later. -* Linux kernel: v4.15 or later. -Older kernels may or may not work - we have not tested on them. +## 2. Install Revizor Python Package -To check your current Linux kernel version: -```shell -cat /proc/version -``` - -* Linux Kernel Headers - -```shell -# On Ubuntu -sudo apt-get install linux-headers-$(uname -r) -``` - -* [Python 3.9+](https://www.python.org/downloads/) - -If your distribution has an older python by default, the best practice is to use a virtual environment. -```shell -# On Ubuntu 18 -sudo apt install python3.9 python3.9-distutils - -python3.9 -m venv venv - -# re-run this command every time you open a new terminal session -source ./venv/bin/activate -``` +If you use `pip`, you can install Revizor with: -* [Unicorn 1.0.2+](https://www.unicorn-engine.org/docs/). -Preferably, use version 1.0.2 or 1.0.3 as they seem to be currently the most stable. -We've encountered several bugs when using the most recent version 2.0. - - -```shell -sudo apt install unicorn -``` - -* Python bindings to Unicorn - -```shell -pip3 install --user unicorn - -# OR, if installed from sources -cd bindings/python -sudo make install - -# if you're using venv, copy the installation (the paths in your installation may differ) -cp -r /usr/local/lib/python3.6/site-packages/unicorn-1.0.3-py3.8.egg/unicorn/ venv/lib64/python3.9/site-packages/ +```bash +pip install revizor-fuzzer ``` -* Python packages `pyyaml`, `types-pyyaml`, `numpy`: - -```shell -pip3 install --user pyyaml types-pyyaml numpy # skip --user if you're installing in venv +Alternatively, install Revizor from sources: +```bash +# run from the project root directory +make install ``` -### 3. Software Requirements for Revizor Development - -Tests: -* [Bash Automated Testing System](https://bats-core.readthedocs.io/en/latest/index.html) -* [mypy](https://mypy.readthedocs.io/en/latest/getting_started.html#installing-and-running-mypy) -* [flake8](https://flake8.pycqa.org/en/latest/index.html) - - -Documentation: -* [pdoc3](https://pypi.org/project/pdoc3/) - -### 4. (Optional) System Configuration - -For more stable results, disable hyperthreading (there's usually a BIOS option for it). -If you do not disable hyperthreading, you will see a warning every time you invoke Revizor; you can ignore it. - -Optionally (and it *really* is optional), you can boot the kernel on a single core by adding `-maxcpus=1` to the boot parameters ([how to add a boot parameter](https://wiki.ubuntu.com/Kernel/KernelBootParameters)). - -In addition, you might want to stop any other actively-running software on the tested machine. We never encountered issues with it, but it might be useful. - -## Installation +## 3. Install Revizor Executor (kernel module) -### 1. Get the x86-64 ISA description: +Then build and install the kernel module: ```bash -cd src/x86/isa_spec -./get_spec.py --extensions BASE SSE SSE2 CLFLUSHOPT CLFSH -``` +# building a kernel module require kernel headers +sudo apt-get install linux-headers-$(uname -r) -### 2. Install the executor kernel module: +# get the source code +git clone https://github.com/microsoft/sca-fuzzer.git -```bash -cd src/x86/executor +# build the executor +cd sca-fuzzer/src/x86/executor make uninstall # the command will give an error message, but it's ok! make clean make make install ``` -### 3. (Optional) Run Tests +## 4. Download ISA spec ```bash -cd src/tests -./runtests.sh +rvzr download_spec -a x86-64 --extensions BASE SSE SSE2 CLFLUSHOPT CLFSH --outfile base.json ``` -If a few (up to 3) "Detection" tests fail, it's fine, you might just have a slightly different microarchitecture. But if other tests fail - something is broken. +## 5. (Optional) System Configuration + +For more stable results, disable hyperthreading (there's usually a BIOS option for it). +If you do not disable hyperthreading, you will see a warning every time you invoke Revizor; you can ignore it. + +Optionally (and it *really* is optional), you can boot the kernel on a single core by adding `-maxcpus=1` to the boot parameters ([how to add a boot parameter](https://wiki.ubuntu.com/Kernel/KernelBootParameters)). diff --git a/docs/quick-start.md b/docs/quick-start.md index cc300c25..91c35161 100644 --- a/docs/quick-start.md +++ b/docs/quick-start.md @@ -1,35 +1,71 @@ # Quick Start Guide -## 1. Testbed +## Testbed Get yourself a machine for testing: Revizor can test x86-64 CPUs, Intel or AMD. You will need a Linux installation running directly on the hardware (testing from within a VM is not supported). -## 2. Installation +## Installation Follow the installation instructions on [this page](install.md). -## 3. Baseline Testing +## Config File -Try running Revizor in a simple, violation-free configuration: +The fuzzing process is controlled by a configuration file in the YAML format, passed via `--config` option. At the very minimum, this file should contain the following fields: +* `contract_observation_clause` and `contract_execution_clause` describe the contract that the CPU-under-test is tested against. See [this page](https://microsoft.github.io/sca-fuzzer/config/) for a list of available contracts. If you don't know what a contract is, Sec. 3 of [this paper](https://arxiv.org/pdf/2105.06872.pdf) will give you a high-level introduction to contracts, and [this paper](https://www.microsoft.com/en-us/research/publication/hardware-software-contracts-for-secure-speculation/) will provide a deep dive into contracts. +* `instruction_categories` is a list of instruction types that will be tested. Effectively, Revizor uses this list to filter out instructions from `base.json` (the file you downloaded via `rvzr download_spec`). +For a full list of configuration options, see [docs](https://microsoft.github.io/sca-fuzzer/config/). + +## Baseline Experiment + +After a fresh installation, it is normally a good idea to do a quick test run to check that everything works ok. + +For example, we can create a configuration file `config.yaml` with only simple arithmetic instructions. As this instruction set does not include any instructions that would trigger speculation on Intel or AMD CPUs (at least that we know of), the expected contract would be `CT-SEQ`: + +```yaml +# config.yaml +instruction_categories: + - BASE-BINARY # arithmetic instructions +max_bb_per_function: 1 # no branches! +min_bb_per_function: 1 + +contract_observation_clause: loads+stores+pc # aka CT +contract_execution_clause: + - no_speculation # aka SEQ +``` + +Start the fuzzer: ```bash -./cli.py fuzz -s x86/isa_spec/base.json -i 50 -n 100 -c tests/test-nondetection.yaml +rvzr fuzz -s base.json -i 50 -n 100 -c config.yaml -w . ``` -This command should terminate with no output. +This command should terminate with no violations. -## 4. Simile Detection -Try starting detecting a contract violation with Revizor: +## Detection of a Simple Contract Violation +Next, we could intentionally make a mistake in a contract to check that Revizor can detect it. +To this end, we can modify the config file from the previous example to include instructions that trigger speculation (e.g., conditional branches) but keep the contract the same: +```yaml +# config.yaml +instruction_categories: + - BASE-BINARY # arithmetic instructions + - BASE-COND_BR +max_bb_per_function: 5 # up to 5 branches per test case +min_bb_per_function: 1 +contract_observation_clause: loads+stores+pc # aka CT +contract_execution_clause: + - no_speculation # aka SEQ +``` + +Start the fuzzer: ```bash -./cli.py fuzz -s x86/isa_spec/base.json -i 20 -n 1000 -c tests/test-detection.yaml -w . +rvzr fuzz -s base.json -i 50 -n 1000 -c config.yaml -w . ``` -This command will test the CPU against a contract that completely forbids speculation. -As your CPU under test almost definitely implements at least some form of speculation, Revizor should detect a violation within a few minutes, with a message similar to this: +As your CPU-under-test almost definitely implements branch prediction, Revizor should detect a violation within a few minutes, with a message similar to this: ``` ================================ Violations detected ========================== @@ -47,15 +83,13 @@ As your CPU under test almost definitely implements at least some form of specul You can find the violating test case as well as the violation report in the directory named `./violation-*/`. It will contain an assembly file `program.asm` that surfaced a violation, a sequence of inputs `input-*.bin` to this program, and some details about the violation in `report.txt`. -## 5. Start Real Fuzzing - -Write your own configuration file (see description [here](config.md) and an example config [here](https://github.com/microsoft/sca-fuzzer/tree/main/src/tests/big-fuzz.yaml)), and launch the fuzzer. +## Full-Scale Fuzzing Campaign -Below is a example launch command, which will start a 24-hour fuzzing session, with 100 input classes per test case: +To start a full-scale test, write your own configuration file (see description [here](config.md) and an example config [here](https://github.com/microsoft/sca-fuzzer/tree/main/src/tests/big-fuzz.yaml)), and launch the fuzzer. +Below is a example launch command, which will start a 24-hour fuzzing session, with 100 input classes per test case, and which uses [big-fuzz.yaml](https://github.com/microsoft/sca-fuzzer/tree/main/src/tests/big-fuzz.yaml) configuration: ```shell -./cli.py fuzz -s x86/isa_spec/base.json -c tests/big-fuzz.yaml -i 100 -n 100000000 --timeout 86400 -w `pwd` --nonstop +rvzr fuzz -s base.json -c src/tests/big-fuzz.yaml -i 100 -n 100000000 --timeout 86400 -w `pwd` --nonstop ``` -When you find a violation, you will have to do some manual investigation to understand the source of it; -[this guide](fuzzing-guide.md) is an example of how to do such an investigation. +When you find a violation, you will have to do some manual investigation to understand the source of it; [this guide](fuzzing-guide.md) is an example of how to do such an investigation. diff --git a/mkdocs.yml b/mkdocs.yml index e62464c2..a619ca4f 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -47,7 +47,6 @@ nav: - How Revizor Works: how-revizor-works.md - Command Line Interface: cli.md - Configuration Files: config.md - - Modules and Interfaces: modules.md - - Architecture: architecture.md + - Development: development.md - Fuzzing Guide: fuzzing-guide.md # - Trophies: trophies.md diff --git a/src/x86/executor/readme.md b/src/x86/executor/readme.md index 3c983ab1..f2b11ca4 100644 --- a/src/x86/executor/readme.md +++ b/src/x86/executor/readme.md @@ -1,19 +1,10 @@ # Install -Tested on Linux v5.6.6-300 and v5.6.13-100. -No guarantees about other versions. -To build the executor, run: - -``` -make uninstall -make clean -make -make install -``` +See https://microsoft.github.io/sca-fuzzer/quick-start/ or `README.md` in the project root. # Using the executor -Use the Revizor CLI (`src/cli.py`). +Use the Revizor CLI (`revizor.py`). This executor is not meant to be used standalone. On your own peril, you could try using it directly, through the `/sys/x86_executor/` pseudo file system.