Mentions légales du service

Skip to content
Snippets Groups Projects
README.md 7.68 KiB
Newer Older
# 1. Overview

This is the gem5 [1, 2] simulator with an extended CPU to nop instructions. We added a new CPU model called `NopsSimpleCPU`, which inherits from
`TimingSimpleCPU`. This new CPU :
- overrides the `fetch()` function of the `TimingSimpleCPU`
- requires a path to a configuration file as input

To specify the configuration file, also called *nop file*, a new option is added: `--nops-file`. This file contains a list of addresses to which specific actions are associated. During the initialization process, the configuration file is read and a hashtable is filled. Then, during the execution phase, each value of PC that has to be fetched is first searched into the hashtable. If found, the CPU execute the associated action to the instruction, *e.g,* skip or execute.

Here's the list of commands to compile and start gem5 with our CPU model (tested on Fedora only) :
```
# Compile (on systems like Ubuntu/Debian, the scons-2 command is probably scons)
scons-2 CPU_MODELS=AtomicSimpleCPU,TimingSimpleCPU,NopsSimpleCPU -j 1 ./build/ARM/gem5.opt
# Execute
$ ./build/ARM/gem5.opt configs/example/nops.py
                       -c tests/test-progs/nops/bin/arm/linux/add
                       --cpu-type NopsSimpleCPU
                       --nops-file nops.txt
```

* `-c`          : path to your binary
* `--cpu-type`  : which CPU model do you want to use in gem5. Use `TimingSimpleCPU` for default behavior
* `--nops-file` : described ahead

We added two specific flags to the `NopsSimpleCPU` for debugging purpose:
* `NopsSimpleCPUInit` : print the initialisation of data structures before starting the simulation
* `NopsSimpleCPUExec` : shows the behavior of the CPU during execution

A generic flag `NopsSimpleCPU` that enables both flags at the same time is also available. To use any of these flags, start gem5 with the `--debug-flags` option, *e.g.* `./build/ARM/gem5.opt --debug-flags=NopsSimpleCPUExec`.


# 2. Fault model

We consider a closed fault model to the one experienced by Rivière *et al.* [3]. When an instruction at address `@PC` is replaced by a `nop`, the previous instruction at address `@PC-1` is replayed. Below an example with simple addition. Expected result is `45`.
```
00010570 <asm_add>:
  10570:  mov r4, #0
  10574:  mov r4, r0
  10578:  add r4, r4, #1
  1057c:  add r4, r4, #2
  10580:  add r4, r4, #3
  10584:  add r4, r4, #4
  10588:  add r4, r4, #5
  1058c:  add r4, r4, #6
  10590:  add r4, r4, #7
  10594:  add r4, r4, #8
  10598:  add r4, r4, #9
  1059c:  nop           ; (mov r0, r0)
  105a0:  mov r0, r4
  105a4:  bx  lr
  ```

If we nop instructions `0x1057c` (r4+=2), here's what gem5 executes (start with `--debug-flags=Exec` to display asm execution). Final result is `44`:
```
426467000: @asm_add     :   mov   r4, #0
426516000: @asm_add+4   :   mov   r4, r0
426565000: @asm_add+8   :   add   r4, r4, #1
426565000: @asm_add+12  :   add   r4, r4, #1 <-- THIS SHOULD BE "add r4, r4, #2"
426628000: @asm_add+16  :   add   r4, r4, #3
426677000: @asm_add+20  :   add   r4, r4, #4
426726000: @asm_add+24  :   add   r4, r4, #5
426775000: @asm_add+28  :   add   r4, r4, #6
426824000: @asm_add+32  :   add   r4, r4, #7
426873000: @asm_add+36  :   add   r4, r4, #8
426922000: @asm_add+40  :   add   r4, r4, #9
426971000: @asm_add+44  :   mov   r0, r0
427020000: @asm_add+48  :   mov   r0, r4
427069000: @asm_add+52  :   bx    lr
```


# 3. Configuration file

This file is given as parameter to the `NopsSimpleCPU` through the `--nops-file` option. It contains a list of addresses with their specific actions. You have to specify **one physical address per line** and it has to be the **first value** on the line. Addresses are in hexadecimal, **without** the *0x* prefix. There is three possible actions for an instruction:
- `s` : the number of times you want to **s**kip this instruction
- `e` : the number of times you want to **e**xecute this instruction normally
- a combination of skip/execute/skip/execute ...

Actions are specified in a chronological order. If no action is specified, the instruction at this address is **always** skipped. If the last action of an instruction is **skip** and the number of required skip is done, the instruction will be always executed if the address is seen again. Conversly, if the last action is **execute** and the number of required execution is done, the instruction will be always skipped if the address is seen again. Below, some examples:
```
81c0                    -> always skipped
81c4 s:10               -> skipped 10 times then always executed
81c8 s:10 e:5           -> skipped 10 times, executed 5 times then always executed
81cc e:8                -> executed 8 times then always skipped
8200 s:10 e:5 s:3 e:4   -> skipped 10 times, executed 5 times, skipped 3 times, executed 4 times then always skipped
```
You can use comments in the file with `#` as the first character of the line. Comments cannot be added in the end of a line and have to be on a dedicated line.

```
# This is a comment
8204 s:2 # This a a non-legit comment
```

### 3.1 Synchonisation point
The first non-commented line of the configuration line is particular and defines the **synchronisation point**, *i.e.,* the point where the attack begins. It has to be an address with optionnaly the number of time you want to execute this address before starting the attack. By default this value is set to 1 and you don't have to specify it in the configuration. A value of 1 means that the first time you see the address defined as the synhronisation point, the attack begins. Typically, the synchronisation point is the entry in the `main()` function. Here's an example:
```
# Start the attack when reaching 0x10520
10520
# Start the attack after reaching 0x10524 4 times
10524 4
```


# 4. Reproduce our results

All the source code used in the paper is provided to reproduce the attacks.
Sources are located in `tests/test-progs/nops/src`.
In each folder you can find the file `nops.txt` given as an input for the `--nops-file` option.
We also release ELF executables in `tests/test-progs/nops/bin` which can be used to execute the attacks, in case
your compiler does not reproduce exactly the same physical layout used in our experiments,
Moreover, you can analyse the
attack by generating the objdump of the executable and read the related `nops.txt`.

To compile all executables execute the following command: `make -C tests/test-progs/nops binaries`. **CAUTION**: this operation erases our executables.

To generate the objdump from all executables, use this command: `make -C tests/test-progs/nops dump`


# 5. Miscellaneous

This version of gem5-nops is based on gem5 v20.0.0.0 (e2abf6c157ebde17e7bc7490bcbea7a9f823c8f1). Below we list the modifications to the original code, which are minimal:
- declare a new CPU model called `NopsSimpleCPU` in `src/cpu/simple/NopsSimpleCPU.py` **(new file, no modification)**
- add source code for this CPU: `src/cpu/simple/nops.{cc,hh}` **(new files, no modification)**
- set the `fetch()` method to `virtual` in `src/cpu/simple/timing.hh` to override implementation **(1 line modified)**
- declare this new CPU as a CPU to compile by default in `src/cpu/simple/SConsopts` **(1 line added)**
- declare object file, source file and debug flags for this new CPU in `src/cpu/simple/Sconscript` **(7 lines added)**
- add a new high-level script (`configs/example/nops.py`) to use this CPU (based on `configs/example/se.py`) **(new file, no modification)**
- add a `nops` folder in `tests/test-progs` with our source code and binaries to reproduce results

Total: 8 lines added, 2 lines modified, 4 new files, 1 new folder for testing only


# References

[1] Binkert *et al.* "The gem5 simulator" (2011)

[2] http://www.gem5.org

[3] Rivière *et al.* "High precision fault injections on the instruction cache of ARMv7-M architectures" (2015)