├── .github └── workflows │ ├── build.yml │ ├── codeql-analysis.yml │ └── lint.yml ├── .gitignore ├── .gitmodules ├── LICENSE ├── README.md ├── analysis ├── Readme.md ├── analysis.ipynb ├── analysisfunctions.py ├── fault-aes.json ├── sahadiagonalfault.py └── tenthRound.py ├── build.sh ├── calculate_trigger.py ├── controller.py ├── examples ├── riscv64 │ ├── fault.json │ ├── minimal.elf │ ├── qemuconf.json │ ├── run.sh │ └── src │ │ ├── Makefile │ │ ├── crt0.s │ │ ├── minimal.c │ │ └── riscv64-virt.ld ├── stm32-timeout-wfi │ ├── fault.json │ ├── minimal.elf │ ├── qemuconf.json │ ├── run.sh │ └── src │ │ ├── Makefile │ │ ├── minimal.c │ │ └── stm32f0-discovery.ld └── stm32 │ ├── fault.json │ ├── minimal.elf │ ├── qemuconf.json │ ├── run.sh │ └── src │ ├── Makefile │ ├── minimal.c │ └── stm32f0-discovery.ld ├── fault-readme.md ├── faultclass.py ├── faultplugin ├── LICENSE ├── Makefile ├── fault_injection.c ├── fault_injection.h ├── fault_list.c ├── fault_list.h ├── faultdata.c ├── faultdata.h ├── faultplugin.c ├── faultplugin.h ├── lib │ ├── README.md │ ├── avl.c │ └── avl.h ├── protobuf │ ├── control.pb-c.c │ ├── control.pb-c.h │ ├── data.pb-c.c │ ├── data.pb-c.h │ ├── fault.pb-c.c │ └── fault.pb-c.h ├── registerdump.c ├── registerdump.h ├── singlestep.c ├── singlestep.h ├── tb_exec_data_collection.c ├── tb_exec_data_collection.h ├── tb_faulted_collection.c ├── tb_faulted_collection.h ├── tb_info_data_collection.c └── tb_info_data_collection.h ├── goldenrun.py ├── hdf5-readme.md ├── hdf5logger.py ├── protobuf ├── __init__.py ├── control.proto ├── control_pb2.py ├── control_pb2.pyi ├── data.proto ├── data_pb2.py ├── data_pb2.pyi ├── fault.proto ├── fault_pb2.py ├── fault_pb2.pyi └── generate_protobuf.sh ├── requirements.txt └── util.py /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: build 2 | 3 | on: 4 | push: 5 | pull_request: 6 | branches: [ master ] 7 | schedule: 8 | - cron: 0 8 * * 0 9 | 10 | jobs: 11 | build: 12 | name: Test Build 13 | runs-on: ubuntu-latest 14 | 15 | steps: 16 | - name: Clone Repository 17 | uses: actions/checkout@v4 18 | 19 | - name: install packages 20 | run: | 21 | sudo apt update 22 | sudo apt upgrade -y 23 | sudo apt install -y build-essential ninja-build libglib2.0-dev libfdt-dev libpixman-1-dev zlib1g-dev libprotobuf-c-dev protobuf-compiler protobuf-c-compiler libcap-dev 24 | pip install -r requirements.txt 25 | 26 | - name: Checkout submodules 27 | run: git submodule update --init 28 | 29 | - name: Build QEMU 30 | run: mkdir -p qemu/build/debug; cd qemu/build/debug; ./../../configure --target-list=arm-softmmu,riscv64-softmmu --enable-debug --enable-plugins --disable-sdl --disable-gtk --disable-curses --disable-vnc; make -j $(nproc --all); echo "done" 31 | 32 | - name: Build Faultplugin 33 | run: cd faultplugin; make -j; echo "done" 34 | 35 | - name: Run ARCHIE 36 | run: cd examples/stm32; ./run.sh; cd ../riscv64; ./run.sh; cd ../stm32-timeout-wfi; ./run.sh 37 | 38 | - name: Upload test artifacts 39 | uses: actions/upload-artifact@v4 40 | with: 41 | name: test-artifacts 42 | path: | 43 | examples/riscv64/*.txt 44 | examples/riscv64/*.hdf5 45 | examples/stm32/*.txt 46 | examples/stm32/*.hdf5 47 | examples/stm32-timeout-wfi/*.txt 48 | examples/stm32-timeout-wfi/*.hdf5 49 | 50 | build_with_script: 51 | name: Test build.sh Script 52 | runs-on: ubuntu-latest 53 | 54 | steps: 55 | - name: Clone Repository 56 | uses: actions/checkout@v4 57 | 58 | - name: test bash build script 59 | run: | 60 | (cat < 15 | flake8 --exclude .git,__pycache__,protobuf 16 | --max-line-length=88 17 | --ignore=E203,E266,E501,W503 18 | 19 | black: 20 | runs-on: ubuntu-latest 21 | steps: 22 | - uses: actions/checkout@v2 23 | - run: pip3 install black 24 | - run: | 25 | black --version 26 | black --check --diff *.py analysis/*.py 27 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # ARCHIE specific debug files 2 | *.hdf5 3 | log_* 4 | 5 | # C 6 | ## Prerequisites 7 | *.d 8 | 9 | ## Object files 10 | *.o 11 | *.ko 12 | *.obj 13 | *.elf 14 | 15 | ## Linker output 16 | *.ilk 17 | *.map 18 | *.exp 19 | 20 | ## Precompiled Headers 21 | *.gch 22 | *.pch 23 | 24 | ## Libraries 25 | *.lib 26 | *.a 27 | *.la 28 | *.lo 29 | 30 | ## Shared objects (inc. Windows DLLs) 31 | *.dll 32 | *.so 33 | *.so.* 34 | *.dylib 35 | 36 | ## Executables 37 | *.exe 38 | *.out 39 | *.app 40 | *.i*86 41 | *.x86_64 42 | *.hex 43 | 44 | ## Debug files 45 | *.dSYM/ 46 | *.su 47 | *.idb 48 | *.pdb 49 | 50 | # Python3 51 | ## Byte-compiled / optimized / DLL files 52 | __pycache__/ 53 | *.py[cod] 54 | *$py.class 55 | 56 | ## C extensions 57 | *.so 58 | 59 | ## Distribution / packaging 60 | .Python 61 | build/ 62 | develop-eggs/ 63 | dist/ 64 | downloads/ 65 | eggs/ 66 | .eggs/ 67 | lib/ 68 | lib64/ 69 | parts/ 70 | sdist/ 71 | var/ 72 | wheels/ 73 | share/python-wheels/ 74 | *.egg-info/ 75 | .installed.cfg 76 | *.egg 77 | MANIFEST 78 | 79 | ## PyInstaller 80 | ## Usually these files are written by a python script from a template 81 | ## before PyInstaller builds the exe, so as to inject date/other infos into it. 82 | *.manifest 83 | *.spec 84 | 85 | ## Installer logs 86 | pip-log.txt 87 | pip-delete-this-directory.txt 88 | 89 | ## Unit test / coverage reports 90 | htmlcov/ 91 | .tox/ 92 | .nox/ 93 | .coverage 94 | .coverage.* 95 | .cache 96 | nosetests.xml 97 | coverage.xml 98 | *.cover 99 | *.py,cover 100 | .hypothesis/ 101 | .pytest_cache/ 102 | cover/ 103 | 104 | ## Translations 105 | *.mo 106 | *.pot 107 | 108 | ## Django stuff: 109 | *.log 110 | local_settings.py 111 | db.sqlite3 112 | db.sqlite3-journal 113 | 114 | ## Flask stuff: 115 | instance/ 116 | .webassets-cache 117 | 118 | ## Scrapy stuff: 119 | .scrapy 120 | 121 | ## Sphinx documentation 122 | docs/_build/ 123 | 124 | ## PyBuilder 125 | .pybuilder/ 126 | target/ 127 | 128 | ## Jupyter Notebook 129 | .ipynb_checkpoints 130 | 131 | ## IPython 132 | profile_default/ 133 | ipython_config.py 134 | 135 | ## pyenv 136 | ## For a library or package, you might want to ignore these files since the code is 137 | ## intended to run in multiple environments; otherwise, check them in: 138 | ## .python-version 139 | 140 | ## pipenv 141 | ## According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 142 | ## However, in case of collaboration, if having platform-specific dependencies or dependencies 143 | ## having no cross-platform support, pipenv may install dependencies that don't work, or not 144 | ## install all needed dependencies. 145 | ##Pipfile.lock 146 | 147 | ## PEP 582; used by e.g. github.com/David-OConnor/pyflow 148 | __pypackages__/ 149 | 150 | ## Celery stuff 151 | celerybeat-schedule 152 | celerybeat.pid 153 | 154 | ## SageMath parsed files 155 | *.sage.py 156 | 157 | ## Environments 158 | .env 159 | .venv 160 | env/ 161 | venv/ 162 | ENV/ 163 | env.bak/ 164 | venv.bak/ 165 | 166 | ## Spyder project settings 167 | .spyderproject 168 | .spyproject 169 | 170 | ## Rope project settings 171 | .ropeproject 172 | 173 | ## mkdocs documentation 174 | /site 175 | 176 | ## mypy 177 | .mypy_cache/ 178 | .dmypy.json 179 | dmypy.json 180 | 181 | ## Pyre type checker 182 | .pyre/ 183 | 184 | ## pytype static type analyzer 185 | .pytype/ 186 | 187 | ## Cython debug symbols 188 | cython_debug/ 189 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "qemu"] 2 | path = qemu 3 | url = https://github.com/Fraunhofer-AISEC/archie-qemu.git 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusve, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright 2021 Florian Andreas Hauschild 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ARCHIE 2 | 3 | [![Build](https://github.com/Fraunhofer-AISEC/archie/actions/workflows/build.yml/badge.svg)](https://github.com/Fraunhofer-AISEC/archie/actions/workflows/build.yml) 4 | 5 | [ARCHIE](https://fdtc.deib.polimi.it/FDTC21/slides/session%201%20-%20paper%203.pdf) is a QEMU-based framework for ARCHitecture-Independent Evaluation of faults. 6 | It allows the user to define fault campaigns using a JSON configuration file and automatically run the whole campaign without additional user input. 7 | ARCHIE is capable of simulating permanent and transient faults in instructions, memory, and registers. 8 | Behavioral data of the target is collected and stored inside an HDF5 log file for later analysis. 9 | 10 | To use this Python program, QEMU with the fault plugin is needed (QEMU can be found in [qemu](https://github.com/Fraunhofer-AISEC/archie-qemu), the fault plugin can be found in the [faultplugin](faultplugin) folder). 11 | 12 | An example project, which injects faults into an embedded TinyAES firmware, can be found unter [https://github.com/tibersam/archie-aes-example](https://github.com/tibersam/archie-aes-example) 13 | 14 | [[_TOC_]] 15 | 16 | ## Build 17 | 18 | For the toolchain QEMU and the fault plugin need to be compiled. This can be done automatically by running the *build.sh* script. 19 | Please make sure the required libraries for [qemu](https://wiki.qemu.org/Hosts/Linux) and the [python libraries](#installation) for ARCHIE are installed. 20 | For Ubuntu, the build script can install the missing dependencies for QEMU and Python. It will ask you if it should install the dependencies. 21 | ``` 22 | ./build.sh 23 | ``` 24 | Alternatively, the build instructions are provided in the following sections. 25 | 26 | ## In [archie-qemu](https://github.com/Fraunhofer-AISEC/archie-qemu) 27 | 28 | ARCHIE was tested with QEMU 6.0, which is available in [archie-qemu](https://github.com/Fraunhofer-AISEC/archie-qemu). 29 | First make sure the basic requirements for QEMU are installed. See the wiki for required libraries (https://wiki.qemu.org/Hosts/Linux). 30 | On Ubuntu systems, you can install the minimum required packages with: 31 | 32 | ```sh 33 | sudo apt install git build-essential ninja-build libglib2.0-dev libfdt-dev libpixman-1-dev zlib1g-dev 34 | ``` 35 | 36 | Additionally, the faultplugin requires the following package: 37 | 38 | ```sh 39 | sudo apt install libprotobuf-c-dev 40 | ``` 41 | 42 | Checkout git submodule qemu, which should checkout tcg_plugin_dev of the git. See code segment below. 43 | 44 | ```sh 45 | git submodule update --init 46 | mkdir -p qemu/build/debug 47 | cd qemu/build/debug 48 | ./../../configure --target-list=arm-softmmu --enable-debug --enable-plugins --disable-sdl --disable-gtk --disable-curses --disable-vnc 49 | make -j $(nproc) 50 | cd ../../../faultplugin/ 51 | make 52 | ``` 53 | 54 | With this, *archie-qemu* is build in qemu/build/debug/ and the plugin is build in *faultplugin/* 55 | If you change the build directory for *archie-qemu*, please change the path in the [Makefile](faultplugin/Makefile) in the *faultplugin/* folder for building the plugin. 56 | 57 | ## In [archie](https://github.com/Fraunhofer-AISEC/archie) 58 | 59 | ### Installation 60 | 61 | For the Python3 program, the following libraries are needed 62 | ``` 63 | pandas (tested 0.25.3) 64 | tables (tested 3.6.1) 65 | python-prctl (tested 1.6.1) 66 | numpy (tested 1.17.4) 67 | json (tested 2.0.9), or json5 (tested 0.9.6) 68 | ``` 69 | These python3 libraries can either be installed using your linux-distribution's installation method or by using pip3. 70 | JSON5 is strongly recommended as it allows integers to be represented as hexadecimal numbers. 71 | 72 | For pip3 the [requirements.txt](requirements.txt) can be used. 73 | If you are using pip3, please make sure to install **libcap-dev**. It is required for python-prctl. See also [https://pythonhosted.org/python-prctl/#downloading-and-installing](https://pythonhosted.org/python-prctl/#downloading-and-installing) 74 | 75 | ### Config files 76 | 77 | To use the python3 program (controller.py), two configuration files are needed. These files are in JSON format. See https://www.json.org/json-en.html for details. 78 | 79 | **qemuconf.json** contains an object with the path to the QEMU executable, the plugin library, the BIOS, and the kernel that should be run by QEMU. Additional arguments that should be passed to QEMU can be specified with `additional_qemu_args`. See "qemuconf.json" for a valid json file with paths. Please adjust the paths to your respective system. The folder **miniblink** contains a demo binary for initial experimentation. To test it, modify the kernel path to ``"kernel" : "miniblink/miniblink.bin``. If another architecture should be used, change the line ``"machine" : "stm32f0discovery"`` by replacing stm32f0discovery with the associated name in QEMU. To find the name, execute the QEMU binary with option ``-M ?``. 80 | 81 | **fault.json** contains the description of faults. It contains an object that entails the start point object, endpoint object, memdump object and an array of faults. 82 | Please see the descriptions in [**fault-readme.md**](fault-readme.md) to see how to build this JSON object. An example setup for several experiments can be found in fault.json 83 | 84 | The program output will be stored in an HDF5 file. For a description of how to interpret the file content see [**hdf5-readme.md**](hdf5-readme.md). 85 | 86 | ### Running the program 87 | 88 | To run the python3 program, type: 89 | ```sh 90 | python3 controller.py --debug --fault fault.json --qemu qemuconf.json output.hdf5 91 | ``` 92 | Replace *fault.json* and *qemuconf.json* with the corresponding files. 93 | 94 | The *--debug flag* creates a log file for each experiment. The name of the log file has the following format: ``log_experiment-id.txt``, e.g., ``log_4.txt`` for the experiment with ID 4. 95 | 96 | To obtain further information on the input parameters, type: 97 | ```sh 98 | python3 controller.py --help 99 | ``` 100 | 101 | #### GNU Debugger 102 | 103 | It is possible to connect to a running QEMU instance with GDB. To use this feature in the framework and observe introduced faults the *--gdb* flag can be set. 104 | ARCHIE will start the internal QEMU process with GDB enabled and halts at the startup of the simulated system. To connect to QEMU from GDB use port 1234. 105 | It also will force the framework to only spawn one worker and it will step through all faults configured in *fault.json*. If one specific fault is required, the JSON file needs to be edited to only contain this specific fault. 106 | ```sh 107 | python3 controller.py --gdb --fault fault.json --qemu qemuconf.json output.hdf5 108 | ``` 109 | To connect from GDB to the QEMU session use 110 | ``` 111 | targ rem:localhost:1234 112 | ``` 113 | QEMU will wait unil the GDB session is attached. The debugging mode is only suitable for the analysis of a low number of faults. Stepping through a large amount of faults is cumbersome. This should be considered when adjusting the JSON files. 114 | -------------------------------------------------------------------------------- /analysis/Readme.md: -------------------------------------------------------------------------------- 1 | # Analysis 2 | 3 | This is an example analysis of an AES attack. It is the same as described in "ARCHIE: A QEMU-Based Framework for Architecture-Independent Evaluation of Faults", which will be published with IEEE. The data temporarily at [https://syncandshare.lrz.de/getlink/fiH6PtwwY2yXpzGCexzntfaX/](https://syncandshare.lrz.de/getlink/fiH6PtwwY2yXpzGCexzntfaX/). We are currently looking for a more permanent solution of publishing the data. 4 | 5 | ## Attacks 6 | 7 | Both attacks are described in full details here: 8 | 9 | K. Garb and J. Obermaier, "Temporary Laser Fault Injection into Flash Memory: Calibration, Enhanced Attacks, and Countermeasures," 2020 IEEE 26th International Symposium on On-Line Testing and Robust System Design (IOLTS), 2020, pp. 1-7, doi: 10.1109/IOLTS50870.2020.9159712. 10 | 11 | ### Saha diagonal fault attack. 12 | 13 | The Saha diagonal fault attack tries to introduce a fault at round 8 (assuming AES-128) inside the AES encrypted data (AES state). By using multiple faulted cyphertexts and the golden cyphertext (non faulted), Possible round key candidates can be calculated. The attack was originally published at 14 | 15 | D. Saha, D. Mukhopadhyay, and D. Roy Chowdhury, “A Diagonal Fault 16 | Attack on the Advanced Encryption Standard,” IACR Cryptology ePrint 17 | Archive, vol. 2009, p. 581, 2009. 18 | 19 | ### Tenth round skip attack 20 | 21 | In this attack we try to skip the last round of AES. When successfull the attacker optains the 9th round state. With the help of the original cyphertext, he then can proceed to calculate the tenth round key, which is trivial. With this roundkey he then can calculate the original key used. Advantage over a Saha diagonal fault attack is less needed faulted cyphertext. Disadvantage for this in practise is a precise fault is needed. 22 | 23 | ## Files 24 | 25 | ### analysis.ipynb 26 | 27 | This file is the main analysis script. It tries to use the Tenth round attack skip and the saha diagonal fault attack to retrieve the tenth round key. The original key is not calculated, as it is only reversing the key scheduler, which is trivial with a round key at hand. 28 | Furthermore it shows how to filter for specific experiments matching ones criteria. It shows how the filter functions provided can be used and how to read data from the HDF5 file. 29 | 30 | ### analysisfunctions.py 31 | 32 | Contains functions needed to access and filter the hdf5 file for fault data. Currently supported: 33 | 34 | * Get complete fault configurations 35 | * Get tbinfo, tbexec, and meminfo compressed or deflated 36 | * Query fault configuration inside the file without holding it in RAM 37 | 38 | All functions either take a function handle or the fault group handle for their operation. 39 | For example usage see analysis.ipynb 40 | 41 | ### tenthRound.py 42 | 43 | Contains the logic to retrieve the tenth round key by using the original cyphertext and the 9th round state. Copyright K. Garb 44 | 45 | ### sahadiagonalfault.py 46 | 47 | Contains the logic of a Saha diagonal fault analysis using the M0 model. Copyright K. Garb 48 | -------------------------------------------------------------------------------- /analysis/analysis.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": null, 6 | "metadata": {}, 7 | "outputs": [], 8 | "source": [ 9 | "import pandas as pd\n", 10 | "import tables\n", 11 | "from analysisfunctions import *\n", 12 | "from tenthRound import getTenthRoundKey\n" 13 | ] 14 | }, 15 | { 16 | "cell_type": "code", 17 | "execution_count": null, 18 | "metadata": {}, 19 | "outputs": [], 20 | "source": [ 21 | "f = tables.open_file(\"aes_small.hdf5\", 'r')" 22 | ] 23 | }, 24 | { 25 | "cell_type": "code", 26 | "execution_count": null, 27 | "metadata": {}, 28 | "outputs": [], 29 | "source": [ 30 | "print(f.root)\n", 31 | "faultgroup = f.root\n", 32 | "for n in faultgroup._f_iter_nodes('Group'):\n", 33 | " print(n)\n", 34 | " \n", 35 | "# Findout if experiments reached end status and sort them\n", 36 | "[success, fail] = filter_endstatus_status(f.root.fault)\n", 37 | "print(len(success))\n", 38 | "print(len(fail))" 39 | ] 40 | }, 41 | { 42 | "cell_type": "code", 43 | "execution_count": null, 44 | "metadata": {}, 45 | "outputs": [], 46 | "source": [ 47 | "# Functions for analysing aes output\n", 48 | "def findout_aes_faulttype(faultgroup, goldengroup):\n", 49 | " noerror = []\n", 50 | " oneerror = []\n", 51 | " fourerror = []\n", 52 | " allerror = []\n", 53 | " notmatched = []\n", 54 | " garray = goldengroup.memdumps.location_20000040_16_1[:][0]\n", 55 | " for node in faultgroup._f_iter_nodes('Group'):\n", 56 | " array_node = node.memdumps.location_20000040_16_1\n", 57 | " array = array_node[:][0]\n", 58 | " diff = 0\n", 59 | " for i in range(0,16):\n", 60 | " if array[i] != garray[i]:\n", 61 | " diff = diff + 1\n", 62 | " if diff == 0:\n", 63 | " noerror.append(node._v_name)\n", 64 | " elif diff == 1:\n", 65 | " oneerror.append(node._v_name)\n", 66 | " elif diff == 4:\n", 67 | " fourerror.append(node._v_name)\n", 68 | " elif diff == 16:\n", 69 | " allerror.append(node._v_name)\n", 70 | " else:\n", 71 | " notmatched.append(node._v_name)\n", 72 | " #print(array)\n", 73 | " #print(garray)\n", 74 | " return [noerror, oneerror, fourerror, allerror, notmatched]\n", 75 | "\n", 76 | "def group_exp_cyphers(groupnamelist):\n", 77 | " retlist = []\n", 78 | " for expname in groupnamelist:\n", 79 | " found = 0\n", 80 | " node = f.root.fault._f_get_child(expname)\n", 81 | " aes_cypher = node.memdumps.location_20000040_16_1[:][0]\n", 82 | " for exp in retlist:\n", 83 | " diff = 0\n", 84 | " for i in range(0, 16):\n", 85 | " if exp['cypher'][i] != aes_cypher[i]:\n", 86 | " diff = diff + 1\n", 87 | " if (diff == 0):\n", 88 | " found = found + 1\n", 89 | " exp['expgroup'].append(expname)\n", 90 | " break\n", 91 | " if found == 0:\n", 92 | " nodedict = {}\n", 93 | " tmp = []\n", 94 | " tmp.append(expname)\n", 95 | " nodedict['expgroup'] = tmp\n", 96 | " nodedict['cypher'] = aes_cypher\n", 97 | " retlist.append(nodedict)\n", 98 | " return retlist" 99 | ] 100 | }, 101 | { 102 | "cell_type": "code", 103 | "execution_count": null, 104 | "metadata": {}, 105 | "outputs": [], 106 | "source": [ 107 | "# Try to find all round skip attacks, only use experiments that reached end point\n", 108 | "instruction_faults = filter_experiment_type(f.root.fault, \"instruction\", success)\n", 109 | "instruction_faults = filter_experiment_fault_address(f.root.fault, 134220088, 134220098, instruction_faults)\n", 110 | "print(len(instruction_faults))\n", 111 | "print(instruction_faults)" 112 | ] 113 | }, 114 | { 115 | "cell_type": "code", 116 | "execution_count": null, 117 | "metadata": {}, 118 | "outputs": [], 119 | "source": [ 120 | "# Combine the different outputs of the round skip attack into dictionaries\n", 121 | "\n", 122 | "grouped_roundskips = group_exp_cyphers(instruction_faults)\n", 123 | "print(len(grouped_roundskips))\n", 124 | "print(grouped_roundskips)" 125 | ] 126 | }, 127 | { 128 | "cell_type": "code", 129 | "execution_count": null, 130 | "metadata": {}, 131 | "outputs": [], 132 | "source": [ 133 | "# Reverse the tenth round to get roundkey\n", 134 | "golden_aes_cypher = f.root.Goldenrun.memdumps.location_20000040_16_1[:][0]\n", 135 | "\n", 136 | "for groupdic in grouped_roundskips:\n", 137 | " groupdic['guess'] = getTenthRoundKey(groupdic['cypher'], golden_aes_cypher)" 138 | ] 139 | }, 140 | { 141 | "cell_type": "code", 142 | "execution_count": null, 143 | "metadata": {}, 144 | "outputs": [], 145 | "source": [ 146 | "# Check if a correct guess is inside the dataset for round skip attack\n", 147 | "tenthroundkey = [208, 20, 249, 168, 201, 238, 37, 137, 225, 63, 12, 200, 182, 99, 12, 166]\n", 148 | "correct_guess = []\n", 149 | "for groupdic in grouped_roundskips:\n", 150 | " match = 0\n", 151 | " for i in range(0, 16):\n", 152 | " if groupdic['guess'][i] == tenthroundkey[i]:\n", 153 | " match = match + 1\n", 154 | " if match == 16:\n", 155 | " print(len(groupdic['expgroup']))\n", 156 | " correct_guess.append(groupdic)\n", 157 | "print(len(correct_guess))\n", 158 | "print(correct_guess)" 159 | ] 160 | }, 161 | { 162 | "cell_type": "code", 163 | "execution_count": null, 164 | "metadata": {}, 165 | "outputs": [], 166 | "source": [ 167 | "# Sort output in different amounts of errors in the status\n", 168 | "[noerror, oneerror, fourerror, allerror, notmatched] = findout_aes_faulttype(f.root.fault, f.root.Goldenrun)\n", 169 | "print(\"noerror: {}\".format(len(noerror)))\n", 170 | "print(\"oneerror: {}\".format(len(oneerror)))\n", 171 | "print(\"fourerror: {}\".format(len(fourerror)))\n", 172 | "print(\"allerror: {}\".format(len(allerror)))\n", 173 | "print(\"notmatched: {}\".format(len(notmatched)))" 174 | ] 175 | }, 176 | { 177 | "cell_type": "code", 178 | "execution_count": null, 179 | "metadata": {}, 180 | "outputs": [], 181 | "source": [ 182 | "# Find all experiments perforiming register faults\n", 183 | "reggroups = filter_experiment_type(f.root.fault, \"register\", success)\n", 184 | "print(len(reggroups))\n", 185 | "print(reggroups)" 186 | ] 187 | }, 188 | { 189 | "cell_type": "code", 190 | "execution_count": null, 191 | "metadata": {}, 192 | "outputs": [], 193 | "source": [ 194 | "match_allerror = intersectlists(allerror, reggroups)\n", 195 | "print(len(match_allerror))\n", 196 | "deduped_match_allerror = group_exp_cyphers(match_allerror)\n", 197 | "print(len(deduped_match_allerror))\n", 198 | "print(deduped_match_allerror)" 199 | ] 200 | }, 201 | { 202 | "cell_type": "code", 203 | "execution_count": null, 204 | "metadata": {}, 205 | "outputs": [], 206 | "source": [ 207 | "from sahadiagonalfault import differentialFaultAttack\n", 208 | "\n", 209 | "def allbyteerror(deduped_error, goldengroup):\n", 210 | " goldencypher = goldengroup.memdumps.location_20000040_16_1[:][0]\n", 211 | " for fault in deduped_error:\n", 212 | " keysetCipher = [[] for i in range(0,16)]\n", 213 | " #print(fault['cypher'])\n", 214 | " differentialFaultAttack(fault['cypher'], keysetCipher, goldencypher)\n", 215 | " fault['guess'] = keysetCipher" 216 | ] 217 | }, 218 | { 219 | "cell_type": "code", 220 | "execution_count": null, 221 | "metadata": {}, 222 | "outputs": [], 223 | "source": [ 224 | "allbyteerror(deduped_match_allerror, f.root.Goldenrun)\n", 225 | "print(len(deduped_match_allerror))\n", 226 | "print(deduped_match_allerror)" 227 | ] 228 | }, 229 | { 230 | "cell_type": "code", 231 | "execution_count": null, 232 | "metadata": {}, 233 | "outputs": [], 234 | "source": [ 235 | "keysetCipher = [[] for i in range(0,16)]\n", 236 | "for i in range(0, 16):\n", 237 | " firstencounter = 0\n", 238 | " for fault in deduped_match_allerror:\n", 239 | " if len(fault['guess'][i]) != 0:\n", 240 | " tmp = list(set(fault['guess'][i]))\n", 241 | " tmp.sort()\n", 242 | " if firstencounter == 0:\n", 243 | " keysetCipher[i] = list(set(fault['guess'][i]))\n", 244 | " keysetCipher[i].sort()\n", 245 | " firstencounter = 1\n", 246 | " keysetCipher[i] = list(set(keysetCipher[i]) & set(tmp))\n", 247 | " keysetCipher[i].sort()\n", 248 | "\n", 249 | "print(keysetCipher)\n", 250 | "print(tenthroundkey)\n", 251 | "match = 0\n", 252 | "for i in range(0, 16):\n", 253 | " if keysetCipher[i][0] == tenthroundkey[i]:\n", 254 | " match = match + 1\n", 255 | "if match == 16:\n", 256 | " print(\"Success!\")" 257 | ] 258 | } 259 | ], 260 | "metadata": { 261 | "kernelspec": { 262 | "display_name": "Python 3", 263 | "language": "python", 264 | "name": "python3" 265 | }, 266 | "language_info": { 267 | "codemirror_mode": { 268 | "name": "ipython", 269 | "version": 3 270 | }, 271 | "file_extension": ".py", 272 | "mimetype": "text/x-python", 273 | "name": "python", 274 | "nbconvert_exporter": "python", 275 | "pygments_lexer": "ipython3", 276 | "version": "3.8.10" 277 | } 278 | }, 279 | "nbformat": 4, 280 | "nbformat_minor": 4 281 | } 282 | -------------------------------------------------------------------------------- /analysis/analysisfunctions.py: -------------------------------------------------------------------------------- 1 | import pandas as pd 2 | 3 | 4 | def generate_groupname_list(faultgroup): 5 | """ 6 | Generator to get names of all childs in faultgroup 7 | """ 8 | for node in faultgroup._f_iter_nodes("Group"): 9 | yield node._v_name 10 | 11 | 12 | def intersectlists(list1, list2): 13 | """ 14 | Returns list1 ^ list2 (aka the intersection between the list) 15 | """ 16 | return list(set(list1).intersection(list2)) 17 | 18 | 19 | def differenclists(list1, list2): 20 | r""" 21 | Returns list1 \ list2 (aka all elements in list 1 that are not in list 2) 22 | """ 23 | return list(set(list1).difference(list2)) 24 | 25 | 26 | def generic_filter_faults( 27 | faultgroup, columname, lowvalue, highvalue=None, interestlist=None 28 | ): 29 | """ 30 | Access function to faults table and filter experiments 31 | """ 32 | if interestlist is None: 33 | interestlist = generate_groupname_list(faultgroup) 34 | if highvalue is None: 35 | highvalue = lowvalue 36 | retgroups = [] 37 | for nodename in interestlist: 38 | node = faultgroup._f_get_child(nodename) 39 | faulttable = node.faults.read() 40 | for faultrow in faulttable: 41 | if (faultrow[columname] >= lowvalue) and (faultrow[columname] <= highvalue): 42 | retgroups.append(node._v_name) 43 | break 44 | return retgroups 45 | 46 | 47 | def filter_endstatus_status(faultgroup, interestlist=None): 48 | """ 49 | Sort all Experiments into reached end point (success) or not (failed). 50 | """ 51 | success = [] 52 | failed = [] 53 | if interestlist is None: 54 | interestlist = generate_groupname_list(faultgroup) 55 | 56 | for nodename in interestlist: 57 | node = faultgroup._f_get_child(nodename) 58 | if node.faults.attrs.endpoint == 0: 59 | failed.append(node._v_name) 60 | else: 61 | success.append(node._v_name) 62 | return [success, failed] 63 | 64 | 65 | def filter_experiment_type(faultgroup, faulttype, interestlist=None): 66 | """ 67 | Filters for a specific fault target. If interestlist is given only 68 | experiments in this list will be analysed. 69 | 0 memory fault 70 | 1 instruction fault 71 | 2 register fault 72 | """ 73 | if not isinstance(faulttype, int): 74 | if "memory" in faulttype: 75 | faulttype = 0 76 | elif "instruction" in faulttype: 77 | faulttype = 1 78 | elif "register" in faulttype: 79 | faulttype = 2 80 | else: 81 | raise ValueError("Faulttype not known") 82 | 83 | return generic_filter_faults( 84 | faultgroup, "fault_type", faulttype, None, interestlist 85 | ) 86 | 87 | 88 | def filter_experiment_model(faultgroup, faultmodel, interestlist=None): 89 | """ 90 | Filter for a specific fault model. If interestlist is given only experiments 91 | in this list will be analysed. 92 | 0 set 0 93 | 1 set 1 94 | 2 Toggle 95 | 3 Overwrite 96 | """ 97 | if not isinstance(faultmodel, int): 98 | if "set0" in faultmodel: 99 | faultmodel = 0 100 | elif "set1" in faultmodel: 101 | faultmodel = 1 102 | elif "toggle" in faultmodel: 103 | faultmodel = 2 104 | elif "overwrite" in faultmodel: 105 | faultmodel = 3 106 | else: 107 | raise ValueError("Faultmodel not understood") 108 | 109 | return generic_filter_faults( 110 | faultgroup, "fault_model", faultmodel, None, interestlist 111 | ) 112 | 113 | 114 | def filter_experiment_faultmask(faultgroup, mask, interestlist=None): 115 | """ 116 | Filter for a certain fault maks. If interestlist is given only experiments 117 | in this list will be analysed. 118 | """ 119 | if interestlist is None: 120 | interestlist = generate_groupname_list(faultgroup) 121 | 122 | return generic_filter_faults(faultgroup, "fault_mask", mask, None, interestlist) 123 | 124 | 125 | def filter_experiment_fault_address( 126 | faultgroup, lowaddress, highaddress=None, interestlist=None 127 | ): 128 | """ 129 | Filter for a specific fault address range. If interestlist is given only 130 | experiments in this list will be analysed 131 | """ 132 | return generic_filter_faults( 133 | faultgroup, "fault_address", lowaddress, highaddress, interestlist 134 | ) 135 | 136 | 137 | def filter_experiment_trigger_counter( 138 | faultgroup, lowcounter, highcounter=None, interestlist=None 139 | ): 140 | """ 141 | Filter for a specific trigger hit counter values. If interestlist is given 142 | only experiments in this list will be analysed 143 | """ 144 | return generic_filter_faults( 145 | faultgroup, "trigger_hitcounter", lowcounter, highcounter, interestlist 146 | ) 147 | 148 | 149 | def filter_experiment_trigger_address( 150 | faultgroup, lowaddress, highaddress=None, interestlist=None 151 | ): 152 | """ 153 | Filter for a specific trigger address range. If interestlist is given 154 | only experiments in this list will be analysed. 155 | """ 156 | return generic_filter_faults( 157 | faultgroup, "trigger_address", lowaddress, highaddress, interestlist 158 | ) 159 | 160 | 161 | def filter_experiment_fault_lifespan( 162 | faultgroup, lowlifespan, highlifespan=None, interestlist=None 163 | ): 164 | """ 165 | Filter for a specific fault lifespan range. If interestlist is given 166 | only experiments in this list will be analysed. 167 | """ 168 | return generic_filter_faults( 169 | faultgroup, "fault_lifespan", lowlifespan, highlifespan, interestlist 170 | ) 171 | 172 | 173 | def filter_experiment_faults_num_bytes( 174 | faultgroup, lowbound, highbound=None, interestlist=None 175 | ): 176 | """ 177 | Filter for a specific num bytes range. If interestlist is given 178 | only experiments in this list will be analysed. 179 | """ 180 | return generic_filter_faults( 181 | faultgroup, "fault_num_bytes", lowbound, highbound, interestlist 182 | ) 183 | 184 | 185 | def get_faultgroup_configuration(faultgroup, name): 186 | """ 187 | Get the fault configuration of a specific fault group 188 | """ 189 | fault = {} 190 | node = faultgroup._f_get_child(name) 191 | fault["faults"] = pd.DataFrame(node.faults.read()).to_dict() 192 | fault["index"] = int(name[10:]) 193 | return fault 194 | 195 | 196 | def get_complete_faultconfiguration(filehandle, interestlist=None): 197 | """ 198 | Build a list of all fault configurations inside the hdf5 file 199 | """ 200 | faultfolder = filehandle.root.fault 201 | faultconfiguration = [] 202 | if interestlist is None: 203 | interestlist = generate_groupname_list(faultfolder) 204 | for faultname in interestlist: 205 | faultconfiguration.append(get_faultgroup_configuration(faultfolder, faultname)) 206 | return faultconfiguration 207 | 208 | 209 | def get_experiment_table(faultgroup, faultname, tablename): 210 | """ 211 | Get anny table from a faultgroup 212 | """ 213 | node = faultgroup._f_get_child(faultname) 214 | table = node._f_get_child(tablename) 215 | return pd.DataFrame(table.read()) 216 | 217 | 218 | def get_experiment_table_expanded(filehandle, faultname, tablename, keywords): 219 | """ 220 | Get the experiment table and recombine it with the goldenrun data 221 | TODO: This data must be checked if not to much is included 222 | """ 223 | golden_table = get_experiment_table(filehandle.root, "Goldenrun", tablename) 224 | exp_table = get_experiment_table(filehandle.root.fault, faultname, tablename) 225 | idxs = pd.Index([]) 226 | for keyword in keywords: 227 | tmp = exp_table[keyword] 228 | idx = pd.Index([]) 229 | for t in tmp: 230 | idt = golden_table.index[golden_table[keyword] == t] 231 | idx.append(idt) 232 | idxs.append(idx) 233 | golden_table.drop(idxs, inplace=True) 234 | data = [exp_table, golden_table] 235 | return pd.concat(data).to_dict("records") 236 | 237 | 238 | def get_experiment_tbinfo(faultgroup, faultname): 239 | return get_experiment_table(faultgroup, faultname, "tbinfo") 240 | 241 | 242 | def get_experiment_tbinfo_expanded(filehandle, faultname): 243 | return get_experiment_table_expanded(filehandle, faultname, "tbinfo", ["identity"]) 244 | 245 | 246 | def get_experiment_tbexec(faultgroup, faultname): 247 | return get_experiment_table(faultgroup, faultname, "tbexeclist") 248 | 249 | 250 | def get_experiment_tbexec_expanded(filehandle, faultname): 251 | return get_experiment_table_expanded(filehandle, faultname, "tbexeclist", ["pos"]) 252 | 253 | 254 | def get_experiment_meminfo(faultgroup, faultname): 255 | return get_experiment_table(faultgroup, faultname, "meminfo") 256 | 257 | 258 | def get_experiment_meminfo_expanded(filehandle, faultname): 259 | return get_experiment_table_expanded( 260 | filehandle, faultname, "meminfo", ["insaddr", "address"] 261 | ) 262 | -------------------------------------------------------------------------------- /analysis/fault-aes.json: -------------------------------------------------------------------------------- 1 | { 2 | "max_instruction_count": 100 , 3 | "start" : { 4 | "address" : 134220182, 5 | "counter" : 1 6 | }, 7 | "end" : { 8 | "address" : 134220188, 9 | "counter" : 3 10 | }, 11 | "faults" :[ 12 | [ 13 | { 14 | "comment" : "Change values fetched from S-Box, model flash load fault", 15 | "fault_address" : [3], 16 | "fault_type" : "register", 17 | "fault_model" : "set1", 18 | "fault_lifespan" : [0], 19 | "fault_mask" : [1, 256, 1], 20 | "trigger_address" : [134219846], 21 | "trigger_counter" : [128, 160, 1] 22 | } 23 | ], 24 | [ 25 | { 26 | "comment" : "Attack S-Box step instructions", 27 | "fault_address" : [134219842, 134219855, 1], 28 | "fault_type" : "instruction", 29 | "fault_model" : "set1", 30 | "fault_lifespan" : [2], 31 | "fault_mask" : [1, 256, 1], 32 | "trigger_address" : [-1], 33 | "trigger_counter" : [141] 34 | } 35 | ], 36 | [ 37 | { 38 | "comment" : "Tenth round skip, model multibit fault between bytes", 39 | "fault_address" : [134220088, 134220098, 1], 40 | "fault_type" : "instruction", 41 | "fault_model" : "set1", 42 | "fault_lifespan" : [2], 43 | "fault_mask" : { "type" : "shift" , "range" : [3, 7, 10]}, 44 | "trigger_address" : [-1], 45 | "trigger_counter" : [8, 10, 1] 46 | } 47 | ], 48 | [ 49 | { 50 | "comment" : "Tenth round skip, model multibit fault in tenth round instructions", 51 | "fault_address" : [134220088, 134220098, 1], 52 | "fault_type" : "instruction", 53 | "fault_model" : "set1", 54 | "fault_lifespan" : [2], 55 | "fault_mask" : [1, 256, 1], 56 | "trigger_address" : [-1], 57 | "trigger_counter" : [8, 10, 1] 58 | } 59 | ] 60 | ], 61 | "memorydump": [ 62 | { 63 | "comment": "Dump aes cypher state", 64 | "address" : 536870976 , 65 | "length" : 16 66 | }, 67 | { 68 | "Comment" : "Dump round keys", 69 | "address" : 536878864, 70 | "length" : 176 71 | } 72 | ] 73 | } 74 | -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -Eeuo pipefail 4 | 5 | build_dir="build/debug" 6 | 7 | install_qemu_packages() { 8 | echo "Install QEMU dependencies" 9 | echo "Try to find out distro" 10 | echo "Running on ${PRETTY_NAME:-Linux}" 11 | if [ "${ID:-linux}" = "debian" ] || [ "${ID_LIKE#*debian*}" != "${ID_LIKE}" ] 12 | then 13 | echo "Looks like Debian!" 14 | sudo apt-get install git build-essential ninja-build libglib2.0-dev libfdt-dev libpixman-1-dev zlib1g-dev libprotobuf-c-dev protobuf-compiler protobuf-c-compiler 15 | else 16 | echo "Distro Version not supported by script. Please install dependencies of QEMU by looking in the QEMU wiki" 17 | fi 18 | } 19 | 20 | install_python3_pip3() { 21 | echo "Running on ${PRETTY_NAME:-Linux}" 22 | if [ "${ID:-linux}" = "debian" ] || [ "${ID_LIKE#*debian*}" != "${ID_LIKE}" ] 23 | then 24 | echo "Looks like Debian!" 25 | echo "Need to install libcap-dev for python-prctl" 26 | sudo apt install libcap-dev 27 | else 28 | echo "You might need to install libcap-dev for python-prctl" 29 | fi 30 | 31 | echo "Install with pip3" 32 | pip3 install -r requirements.txt 33 | } 34 | 35 | install_python3_distro() { 36 | echo "Running on ${PRETTY_NAME:-Linux}" 37 | if [ "${ID:-linux}" = "debian" ] || [ "${ID_LIKE#*debian*}" != "${ID_LIKE}" ] 38 | then 39 | echo "Looks like Debian!" 40 | sudo apt-get install python3-tables python3-pandas python3-prctl python3-protobuf python3-tqdm python3-psutil python3-json5 python3-setuptools 41 | 42 | echo "Rebuild protobuf files to support the installed package versions" 43 | cd protobuf 44 | ./generate_protobuf.sh 45 | cd .. 46 | else 47 | echo "Distro package manager not yet supported" 48 | fi 49 | } 50 | 51 | install_python3_packages() { 52 | echo "Install python3 packages" 53 | echo "Should this script use pip3 or the distro package manager?" 54 | select answer in "pip3" "distro"; do 55 | case $answer in 56 | pip3 ) install_python3_pip3 ; break;; 57 | distro ) install_python3_distro ; break;; 58 | esac 59 | done 60 | } 61 | 62 | #Begin of installation script 63 | 64 | 65 | test -e /etc/os-release && os_release='/etc/os-release' || test -e /usr/lib/os-release && os_release='/usr/lib/os-release' 66 | . "${os_release}" 67 | 68 | echo "Should this script try to install the required QEMU libraries and tools?" 69 | select yn in "YES" "NO"; do 70 | case $yn in 71 | YES ) install_qemu_packages ; break;; 72 | NO ) echo "See Readme for required packages"; break;; 73 | esac 74 | echo "use 1 or 2 to answer yes or no" 75 | done 76 | 77 | echo Should this script try to install the required libraries for the Python3 part of ARCHIE? 78 | select yn in "YES" "NO"; do 79 | case $yn in 80 | YES ) install_python3_packages ; break;; 81 | NO ) echo "See Readme for required packages"; break;; 82 | esac 83 | echo "use 1 or 2 to answer yes or no" 84 | done 85 | 86 | if [[ ! -f "qemu/README.rst" ]]; then 87 | echo "Checkout submodules" 88 | git submodule update --init 89 | fi 90 | 91 | echo "Building QEMU for archie" 92 | cd qemu 93 | if [[ ! -e $build_dir ]]; then 94 | mkdir -p $build_dir 95 | fi 96 | cd $build_dir 97 | ./../../configure --target-list=arm-softmmu,riscv64-softmmu --enable-debug --enable-plugins --disable-sdl --disable-gtk --disable-curses --disable-vnc 98 | make -j $(nproc --all) 99 | 100 | echo "Building faultplugin" 101 | cd ../../../faultplugin/ 102 | make clean && make 103 | cd .. 104 | 105 | echo "Test ARCHIE" 106 | cd examples/stm32 107 | ./run.sh 108 | cd - && cd examples/stm32-timeout-wfi 109 | ./run.sh 110 | cd - && cd examples/riscv64 111 | ./run.sh 112 | cd - 113 | 114 | echo "Do you want to delete log files and HDF5 file inside the example directory?" 115 | select yn in "YES" "NO"; do 116 | case $yn in 117 | YES ) rm examples/stm32/log_* && rm examples/stm32/output.hdf5 && rm examples/riscv64/log_* && rm examples/riscv64/output.hdf5 && echo "Deleted log and HDF5 files"; break;; 118 | NO ) echo "cmd to delete: rm log_* && rm output.hdf5"; break;; 119 | esac 120 | echo "Please type the number corresponding to Yes or No" 121 | done 122 | echo "Archie was build and tested successfully" 123 | -------------------------------------------------------------------------------- /calculate_trigger.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2021 Florian Andreas Hauschild 2 | # Copyright (c) 2021 Fraunhofer AISEC 3 | # Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | import logging 18 | 19 | import pandas 20 | from tqdm import tqdm 21 | 22 | from faultclass import build_filters 23 | 24 | logger = logging.getLogger(__name__) 25 | 26 | 27 | def find_tb_info_row(tb_id, goldenrun_tb_info): 28 | idx = goldenrun_tb_info.index[goldenrun_tb_info["id"] == tb_id] 29 | return idx[0] 30 | 31 | 32 | def allign_fault_to_instruction(address, tbinfo_size, tbinfo_assembler, tbinfo_id): 33 | asm_addresses = [] 34 | # Start searching for instruction addresses 35 | split = tbinfo_assembler.split("[ ") 36 | for sp in split[1:]: 37 | # Find end of address 38 | s = sp.split("]") 39 | # Convert and append to list 40 | asm_addresses.append(int("0x" + s[0].strip(), 0)) 41 | asm_addresses.append(tbinfo_id + tbinfo_size) 42 | for i in range(0, len(asm_addresses) - 1, 1): 43 | if address >= asm_addresses[i] and address < asm_addresses[i + 1]: 44 | return asm_addresses[i] 45 | 46 | 47 | def calculate_lifespan_from_start( 48 | filter_lists, 49 | fault_address, 50 | goldenrun_tb_exec, 51 | goldenrun_tb_info, 52 | trigger_occurrences, 53 | ): 54 | [idx, instruction_address] = find_fault( 55 | fault_address, goldenrun_tb_exec, goldenrun_tb_info, trigger_occurrences 56 | ) 57 | tb_id = goldenrun_tb_exec.at[idx, "tb"] 58 | lifespan = 0 59 | for filt in filter_lists: 60 | if filt[0] != tb_id: 61 | continue 62 | for ins in filt: 63 | lifespan += 1 64 | if ins == instruction_address: 65 | break 66 | break 67 | for itter in range(0, idx): 68 | idtbinfo = find_tb_info_row( 69 | goldenrun_tb_exec.at[itter, "tb"], goldenrun_tb_info 70 | ) 71 | lifespan += goldenrun_tb_info.at[idtbinfo, "ins_count"] 72 | return lifespan 73 | 74 | 75 | def find_fault( 76 | fault_address, goldenrun_tb_exec, goldenrun_tb_info, trigger_occurrences 77 | ): 78 | matching_tbs = goldenrun_tb_info.query( 79 | f"id <= {fault_address} & id + size > {fault_address}" 80 | ) 81 | matching_tb_ids = matching_tbs["id"] # noqa: F841 82 | matching_tbs_with_positions = goldenrun_tb_exec.query("tb in @matching_tb_ids") 83 | idx = matching_tbs_with_positions.index 84 | 85 | # Identify desired occurrence 86 | if trigger_occurrences > len(idx): 87 | return [-1, 0] 88 | idx = idx[trigger_occurrences - 1] 89 | idtbinfo = find_tb_info_row(goldenrun_tb_exec.at[idx, "tb"], goldenrun_tb_info) 90 | ins = allign_fault_to_instruction( 91 | fault_address, 92 | goldenrun_tb_info.at[idtbinfo, "size"], 93 | goldenrun_tb_info.at[idtbinfo, "assembler"], 94 | goldenrun_tb_info.at[idtbinfo, "id"], 95 | ) 96 | return [idx, ins] 97 | 98 | 99 | def search_for_fault_location( 100 | filter_lists, 101 | trigger_position, 102 | fault_address, 103 | trigger_occurrences, 104 | fault_lifespan, 105 | goldenrun_tb_exec, 106 | goldenrun_tb_info, 107 | tb_start_end, 108 | ): 109 | logger.debug(f"Search trigger to fault INSN at 0x{fault_address:08x}") 110 | [idx, ins] = find_fault( 111 | fault_address, goldenrun_tb_exec, goldenrun_tb_info, trigger_occurrences 112 | ) 113 | if idx < 0: 114 | return [-1, trigger_occurrences, fault_lifespan] 115 | trigger_not_in_same_tb = 0 116 | lifespan_diff = trigger_position + fault_lifespan 117 | trigger_position = trigger_position * (-1) 118 | while trigger_position != 0: 119 | if idx < 0: 120 | if fault_lifespan > 0: 121 | fault_lifespan = calculate_lifespan_from_start( 122 | filter_lists, 123 | fault_address, 124 | goldenrun_tb_exec, 125 | goldenrun_tb_info, 126 | trigger_occurrences, 127 | ) 128 | fault_lifespan += lifespan_diff 129 | return [fault_address, 0, fault_lifespan] 130 | idtbinfo = find_tb_info_row(goldenrun_tb_exec.at[idx, "tb"], goldenrun_tb_info) 131 | if trigger_not_in_same_tb == 1: 132 | # Is current tb to short for trigger position 133 | if trigger_position > goldenrun_tb_info.at[idtbinfo, "ins_count"]: 134 | idx = idx - 1 135 | trigger_position = ( 136 | trigger_position - goldenrun_tb_info.at[idtbinfo, "ins_count"] 137 | ) 138 | else: 139 | tb_id = goldenrun_tb_info.at[idtbinfo, "id"] 140 | for filt in filter_lists: 141 | if filt[0] != tb_id: 142 | continue 143 | ins = filt[len(filt) - trigger_position] 144 | trigger_position = 0 145 | break 146 | else: 147 | tb_id = goldenrun_tb_exec.at[idx, "tb"] 148 | for filt in filter_lists: 149 | # found matching filter 150 | if filt[0] != tb_id: 151 | continue 152 | for i in range(0, len(filt), 1): 153 | if filt[i] != ins: 154 | continue 155 | # Case ins is in the current tb 156 | if i >= trigger_position: 157 | i -= trigger_position 158 | ins = filt[i] 159 | trigger_position = 0 160 | else: 161 | # Case ins is not in the current tb 162 | trigger_not_in_same_tb = 1 163 | trigger_position -= i 164 | idx -= 1 165 | break 166 | # Got trigger address, now calculate the trigger hitcounter 167 | trigger_tb = goldenrun_tb_exec.at[idx, "tb"] 168 | tb_hitcounters = goldenrun_tb_exec.iloc[0 : idx + 1].tb.value_counts() 169 | trigger_hitcounter = tb_hitcounters[trigger_tb] 170 | 171 | # Is the trigger TB a sub-TB? 172 | sub_tbs = tb_start_end[ 173 | (trigger_tb > tb_start_end["tb_start"]) & (trigger_tb <= tb_start_end["tb_end"]) 174 | ] 175 | 176 | for sub_tb, _ in sub_tbs.iterrows(): 177 | # Filter out TBs we did not execute yet 178 | if sub_tb not in tb_hitcounters: 179 | continue 180 | 181 | trigger_hitcounter += tb_hitcounters[sub_tb] 182 | 183 | # Does the trigger TB contain a sub-TB? 184 | last_instr = tb_start_end.loc[trigger_tb, "tb_end"] 185 | 186 | sub_tbs = tb_start_end[ 187 | (trigger_tb < tb_start_end["tb_start"]) 188 | & (last_instr > tb_start_end["tb_start"]) 189 | & (last_instr <= tb_start_end["tb_end"]) 190 | ] 191 | 192 | for sub_tb, sub_tb_data in sub_tbs.iterrows(): 193 | # Filter out TBs we did not execute yet 194 | if sub_tb not in tb_hitcounters: 195 | continue 196 | 197 | # Is the trigger instruction part of the sub-TB? 198 | if ins >= sub_tb_data["tb_start"]: 199 | trigger_hitcounter += tb_hitcounters[sub_tb] 200 | 201 | logger.debug( 202 | "Found trigger for faulting instruction address {} at {} with " 203 | "hitcounter {}".format(fault_address, ins, trigger_hitcounter) 204 | ) 205 | return [ins, trigger_hitcounter, fault_lifespan] 206 | 207 | 208 | def calculate_trigger_addresses(fault_list, goldenrun_tb_exec, goldenrun_tb_info): 209 | """""" 210 | logger.info("Calculating trigger addresses") 211 | 212 | # Create a dataframe with the start and end addresses of the tb's 213 | tb_start = goldenrun_tb_info["id"].copy() 214 | tb_start.index = goldenrun_tb_info["id"] 215 | 216 | tb_end = goldenrun_tb_info["id"] + goldenrun_tb_info["size"] - 1 217 | tb_end.index = goldenrun_tb_info["id"] 218 | 219 | tb_start_end = pandas.DataFrame( 220 | { 221 | "tb_start": tb_start, 222 | "tb_end": tb_end, 223 | } 224 | ) 225 | 226 | # check every fault list 227 | cache_dict = dict() 228 | lists = build_filters(goldenrun_tb_info) 229 | for list in lists: 230 | list = list.reverse() 231 | for faults in tqdm(fault_list, desc="Calculating trigger addresses"): 232 | for fault in faults["faultlist"]: 233 | if fault.trigger.address >= 0 or fault.trigger.hitcounter == 0: 234 | continue 235 | 236 | tdict = cache_dict.get(fault.address) 237 | if ( 238 | tdict is not None 239 | and tdict["faultlifespan"] == fault.lifespan 240 | and tdict["triggerhitcounter"] == fault.trigger.hitcounter 241 | and tdict["triggeraddress"] == fault.trigger.address 242 | ): 243 | fault.trigger.address = tdict["answer"][0] 244 | fault.trigger.hitcounter = tdict["answer"][1] 245 | fault.lifespan = tdict["answer"][2] 246 | continue 247 | 248 | if fault.lifespan != 0 and fault.trigger.address + fault.lifespan < 0: 249 | logger.warning( 250 | f"Lifespan is too short to take effect for 0x{fault.address:0x}" 251 | f" with hitcounter {fault.trigger.hitcounter}, trigger address" 252 | f" {fault.trigger.address} and lifespan {fault.lifespan}" 253 | ) 254 | tbs = [-1, fault.trigger.hitcounter, fault.lifespan] 255 | else: 256 | tbs = search_for_fault_location( 257 | lists, 258 | fault.trigger.address, 259 | fault.address, 260 | fault.trigger.hitcounter, 261 | fault.lifespan, 262 | goldenrun_tb_exec, 263 | goldenrun_tb_info, 264 | tb_start_end, 265 | ) 266 | d = dict() 267 | d["faultaddress"] = fault.address 268 | d["triggerhitcounter"] = fault.trigger.hitcounter 269 | d["triggeraddress"] = fault.trigger.address 270 | d["faultlifespan"] = fault.lifespan 271 | d["answer"] = tbs 272 | cache_dict[fault.address] = d 273 | fault.trigger.address = tbs[0] 274 | fault.trigger.hitcounter = tbs[1] 275 | fault.lifespan = tbs[2] 276 | -------------------------------------------------------------------------------- /examples/riscv64/fault.json: -------------------------------------------------------------------------------- 1 | { 2 | "max_instruction_count": 100, 3 | "start" : { 4 | "address" : 0x80000014, 5 | "counter" : 1 6 | }, 7 | "end" : [ 8 | { 9 | "address" : 0x8000002a, 10 | "counter" : 3 11 | }, 12 | { 13 | "address" : 0x8000003c, 14 | "counter" : 1 15 | } 16 | ], 17 | "faults" :[ 18 | [ 19 | { 20 | "fault_address" : [0x87ffffec], 21 | "fault_type" : "data", 22 | "fault_model" : "set0", 23 | "fault_lifespan" : [100], 24 | "fault_mask" : [1], 25 | "trigger_address" : [0x80000024], 26 | "trigger_counter" : [1] 27 | } 28 | ], 29 | [ 30 | { 31 | "fault_address" : [0x8000002a], 32 | "fault_type" : "instruction", 33 | "fault_model" : "overwrite", 34 | "num_bytes" : 2, 35 | "fault_lifespan" : [10], 36 | "fault_mask" : [0x0001], 37 | "trigger_address" : [0x80000024], 38 | "trigger_counter" : [1] 39 | } 40 | ], 41 | [ 42 | { 43 | "fault_address" : [15], 44 | "fault_type" : "register", 45 | "fault_model" : "set0", 46 | "fault_lifespan" : [0], 47 | "fault_mask" : [0xffffffff], 48 | "trigger_address" : [0x80000028], 49 | "trigger_counter" : [1] 50 | } 51 | ] 52 | ], 53 | "mem_info": true 54 | } 55 | -------------------------------------------------------------------------------- /examples/riscv64/minimal.elf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fraunhofer-AISEC/archie/41a12dc23a697abd75efec0b11815ef0caf2c00b/examples/riscv64/minimal.elf -------------------------------------------------------------------------------- /examples/riscv64/qemuconf.json: -------------------------------------------------------------------------------- 1 | { 2 | "qemu" : "../../qemu/build/debug/riscv64-softmmu/qemu-system-riscv64", 3 | "bios" : "none", 4 | "kernel" : "minimal.elf", 5 | "plugin" : "../../faultplugin/libfaultplugin.so", 6 | "machine" : "virt", 7 | "additional_qemu_args" : "" 8 | } 9 | -------------------------------------------------------------------------------- /examples/riscv64/run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | python3 ../../controller.py --debug --fault fault.json --qemu qemuconf.json output.hdf5 3 | -------------------------------------------------------------------------------- /examples/riscv64/src/Makefile: -------------------------------------------------------------------------------- 1 | BINARY = minimal 2 | 3 | PREFIX :=riscv64-unknown-elf- 4 | 5 | CC :=$(PREFIX)gcc 6 | 7 | make: 8 | $(CC) -g -ffreestanding -Wl,--gc-sections -nostartfiles -nostdlib -nodefaultlibs -Wl,-T,riscv64-virt.ld crt0.s minimal.c -o $(BINARY).elf 9 | 10 | clean: 11 | rm $(BINARY).elf 12 | -------------------------------------------------------------------------------- /examples/riscv64/src/crt0.s: -------------------------------------------------------------------------------- 1 | .section .init, "ax" 2 | .global _start 3 | _start: 4 | .option norelax 5 | la sp, __stack_top 6 | add s0, sp, zero 7 | jal zero, main 8 | .end 9 | -------------------------------------------------------------------------------- /examples/riscv64/src/minimal.c: -------------------------------------------------------------------------------- 1 | int main(void) { 2 | volatile int i = 1; 3 | volatile int x = 0; 4 | 5 | while (i) { 6 | __asm__("nop"); 7 | } 8 | 9 | x = 0x10; 10 | 11 | // Another loop to ensure that the program does not lock up after finishing execution. 12 | // Otherwise archie will wait infinitely 13 | while (x) { 14 | __asm__("nop"); 15 | } 16 | 17 | return x; 18 | } 19 | -------------------------------------------------------------------------------- /examples/riscv64/src/riscv64-virt.ld: -------------------------------------------------------------------------------- 1 | MEMORY 2 | { 3 | RAM (rwx) : ORIGIN = 0x80000000, LENGTH = 128M 4 | } 5 | ENTRY(_start) 6 | 7 | PROVIDE(__stack_top = ORIGIN(RAM) + LENGTH(RAM)); 8 | -------------------------------------------------------------------------------- /examples/stm32-timeout-wfi/fault.json: -------------------------------------------------------------------------------- 1 | { 2 | "max_instruction_count": 100, 3 | "start" : { 4 | "address" : 0x0800006a, 5 | "counter" : 1 6 | }, 7 | "end" :[ 8 | { 9 | "address" : 0x08000056, 10 | "counter" : 3 11 | }, 12 | { 13 | "address" : 0x08000070, 14 | "counter" : 1 15 | } 16 | ], 17 | "faults" :[ 18 | [ 19 | { 20 | "fault_address" : [0x20001fec], 21 | "fault_type" : "data", 22 | "fault_model" : "set0", 23 | "fault_lifespan" : [100], 24 | "fault_mask" : [1], 25 | "trigger_address" : [0x0800004a], 26 | "trigger_counter" : [1] 27 | } 28 | ], 29 | [ 30 | { 31 | "fault_address" : [0x08000056], 32 | "fault_type" : "instruction", 33 | "fault_model" : "overwrite", 34 | "num_bytes" : 2, 35 | "fault_lifespan" : [100], 36 | "fault_mask" : [0x46c0], 37 | "trigger_address" : [0x08000040], 38 | "trigger_counter" : [1] 39 | } 40 | ], 41 | [ 42 | { 43 | "fault_address" : [3], 44 | "fault_type" : "register", 45 | "fault_model" : "set0", 46 | "fault_lifespan" : [0], 47 | "fault_mask" : [0xffffffff], 48 | "trigger_address" : [0x08000054], 49 | "trigger_counter" : [1] 50 | } 51 | ] 52 | ], 53 | "memorydump": [ 54 | { 55 | "address" : 0x08000000, 56 | "length" : 1023 57 | } 58 | ], 59 | "mem_info": true, 60 | "timeout": 1 61 | } 62 | -------------------------------------------------------------------------------- /examples/stm32-timeout-wfi/minimal.elf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fraunhofer-AISEC/archie/41a12dc23a697abd75efec0b11815ef0caf2c00b/examples/stm32-timeout-wfi/minimal.elf -------------------------------------------------------------------------------- /examples/stm32-timeout-wfi/qemuconf.json: -------------------------------------------------------------------------------- 1 | { 2 | "qemu" : "../../qemu/build/debug/arm-softmmu/qemu-system-arm", 3 | "bios" : "", 4 | "kernel" : "minimal.elf", 5 | "plugin" : "../../faultplugin/libfaultplugin.so", 6 | "machine" : "stm32vldiscovery", 7 | "additional_qemu_args" : "" 8 | } 9 | -------------------------------------------------------------------------------- /examples/stm32-timeout-wfi/run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | python3 ../../controller.py --debug --fault fault.json --qemu qemuconf.json output.hdf5 3 | -------------------------------------------------------------------------------- /examples/stm32-timeout-wfi/src/Makefile: -------------------------------------------------------------------------------- 1 | BINARY = minimal 2 | 3 | PREFIX :=arm-none-eabi- 4 | 5 | CC :=$(PREFIX)gcc 6 | 7 | LDSCRIPT = ./stm32f0-discovery.ld 8 | 9 | make: 10 | $(CC) -g -mcpu=cortex-m0 -mthumb -Wl,-static -nostartfiles -Wl,--start-group -lc -lgcc -lnosys -Wl,--end-group -Wl,-T,$(LDSCRIPT) minimal.c -o $(BINARY).elf 11 | 12 | clean: 13 | rm $(BINARY).elf -f 14 | -------------------------------------------------------------------------------- /examples/stm32-timeout-wfi/src/minimal.c: -------------------------------------------------------------------------------- 1 | typedef void (*vector_table_entry_t)(void); 2 | 3 | typedef struct { 4 | unsigned int *initial_sp_value; /**< Initial stack pointer value. */ 5 | vector_table_entry_t reset; 6 | vector_table_entry_t nmi; 7 | vector_table_entry_t hard_fault; 8 | vector_table_entry_t memory_manage_fault; /* not in CM0 */ 9 | vector_table_entry_t bus_fault; /* not in CM0 */ 10 | vector_table_entry_t usage_fault; /* not in CM0 */ 11 | vector_table_entry_t reserved_x001c[4]; 12 | vector_table_entry_t sv_call; 13 | vector_table_entry_t debug_monitor; /* not in CM0 */ 14 | vector_table_entry_t reserved_x0034; 15 | vector_table_entry_t pend_sv; 16 | vector_table_entry_t systick; 17 | vector_table_entry_t irq[0]; 18 | } vector_table_t; 19 | 20 | extern vector_table_t vector_table; 21 | 22 | int main(void) { 23 | volatile int i = 1; 24 | volatile int x = 0; 25 | 26 | while (i) { 27 | __asm__("nop"); 28 | } 29 | 30 | x = 0x10; 31 | 32 | return x; 33 | } 34 | 35 | void reset_handler(void) { 36 | main(); 37 | 38 | while(1) { 39 | __asm__("wfi"); 40 | } 41 | } 42 | 43 | __attribute__ ((section(".vectors"))) 44 | vector_table_t vector_table = { 45 | .initial_sp_value = (unsigned *)0x20002000, 46 | .reset = reset_handler 47 | }; 48 | -------------------------------------------------------------------------------- /examples/stm32-timeout-wfi/src/stm32f0-discovery.ld: -------------------------------------------------------------------------------- 1 | MEMORY 2 | { 3 | rom (rx) : ORIGIN = 0x08000000, LENGTH = 64K 4 | ram (rwx) : ORIGIN = 0x20000000, LENGTH = 8K 5 | } 6 | 7 | EXTERN (vector_table) 8 | 9 | ENTRY(reset_handler) 10 | 11 | SECTIONS 12 | { 13 | .text : { 14 | *(.vectors) /* Vector table */ 15 | *(.text*) /* Program code */ 16 | . = ALIGN(4); 17 | } >rom 18 | 19 | end = .; 20 | } 21 | 22 | -------------------------------------------------------------------------------- /examples/stm32/fault.json: -------------------------------------------------------------------------------- 1 | { 2 | "max_instruction_count": 100, 3 | "start" : { 4 | "address" : 0x0800006a, 5 | "counter" : 1 6 | }, 7 | "end" :[ 8 | { 9 | "address" : 0x08000056, 10 | "counter" : 3 11 | }, 12 | { 13 | "address" : 0x08000070, 14 | "counter" : 1 15 | } 16 | ], 17 | "faults" :[ 18 | [ 19 | { 20 | "fault_address" : [0x20001fec], 21 | "fault_type" : "data", 22 | "fault_model" : "set0", 23 | "fault_lifespan" : [100], 24 | "fault_mask" : [1], 25 | "trigger_address" : [0x0800004a], 26 | "trigger_counter" : [1] 27 | } 28 | ], 29 | [ 30 | { 31 | "fault_address" : [0x08000056], 32 | "fault_type" : "instruction", 33 | "fault_model" : "overwrite", 34 | "num_bytes" : 2, 35 | "fault_lifespan" : [100], 36 | "fault_mask" : [0x46c0], 37 | "trigger_address" : [0x08000040], 38 | "trigger_counter" : [1] 39 | } 40 | ], 41 | [ 42 | { 43 | "fault_address" : [3], 44 | "fault_type" : "register", 45 | "fault_model" : "set0", 46 | "fault_lifespan" : [0], 47 | "fault_mask" : [0xffffffff], 48 | "trigger_address" : [0x08000054], 49 | "trigger_counter" : [1] 50 | } 51 | ] 52 | ], 53 | "memorydump": [ 54 | { 55 | "address" : 0x08000000, 56 | "length" : 1023 57 | } 58 | ], 59 | "mem_info": true 60 | } 61 | -------------------------------------------------------------------------------- /examples/stm32/minimal.elf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fraunhofer-AISEC/archie/41a12dc23a697abd75efec0b11815ef0caf2c00b/examples/stm32/minimal.elf -------------------------------------------------------------------------------- /examples/stm32/qemuconf.json: -------------------------------------------------------------------------------- 1 | { 2 | "qemu" : "../../qemu/build/debug/arm-softmmu/qemu-system-arm", 3 | "bios" : "", 4 | "kernel" : "minimal.elf", 5 | "plugin" : "../../faultplugin/libfaultplugin.so", 6 | "machine" : "stm32vldiscovery", 7 | "additional_qemu_args" : "" 8 | } 9 | -------------------------------------------------------------------------------- /examples/stm32/run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | python3 ../../controller.py --debug --fault fault.json --qemu qemuconf.json output.hdf5 3 | -------------------------------------------------------------------------------- /examples/stm32/src/Makefile: -------------------------------------------------------------------------------- 1 | BINARY = minimal 2 | 3 | PREFIX :=arm-none-eabi- 4 | 5 | CC :=$(PREFIX)gcc 6 | 7 | LDSCRIPT = ./stm32f0-discovery.ld 8 | 9 | make: 10 | $(CC) -g -mcpu=cortex-m0 -mthumb -Wl,-static -nostartfiles -Wl,--start-group -lc -lgcc -lnosys -Wl,--end-group -Wl,-T,$(LDSCRIPT) minimal.c -o $(BINARY).elf 11 | 12 | clean: 13 | rm $(BINARY).elf -f 14 | -------------------------------------------------------------------------------- /examples/stm32/src/minimal.c: -------------------------------------------------------------------------------- 1 | typedef void (*vector_table_entry_t)(void); 2 | 3 | typedef struct { 4 | unsigned int *initial_sp_value; /**< Initial stack pointer value. */ 5 | vector_table_entry_t reset; 6 | vector_table_entry_t nmi; 7 | vector_table_entry_t hard_fault; 8 | vector_table_entry_t memory_manage_fault; /* not in CM0 */ 9 | vector_table_entry_t bus_fault; /* not in CM0 */ 10 | vector_table_entry_t usage_fault; /* not in CM0 */ 11 | vector_table_entry_t reserved_x001c[4]; 12 | vector_table_entry_t sv_call; 13 | vector_table_entry_t debug_monitor; /* not in CM0 */ 14 | vector_table_entry_t reserved_x0034; 15 | vector_table_entry_t pend_sv; 16 | vector_table_entry_t systick; 17 | vector_table_entry_t irq[0]; 18 | } vector_table_t; 19 | 20 | extern vector_table_t vector_table; 21 | 22 | int main(void) { 23 | volatile int i = 1; 24 | volatile int x = 0; 25 | 26 | while (i) { 27 | __asm__("nop"); 28 | } 29 | 30 | x = 0x10; 31 | 32 | return x; 33 | } 34 | 35 | void reset_handler(void) { 36 | main(); 37 | 38 | while(1) { 39 | __asm__("nop"); 40 | } 41 | } 42 | 43 | __attribute__ ((section(".vectors"))) 44 | vector_table_t vector_table = { 45 | .initial_sp_value = (unsigned *)0x20002000, 46 | .reset = reset_handler 47 | }; 48 | -------------------------------------------------------------------------------- /examples/stm32/src/stm32f0-discovery.ld: -------------------------------------------------------------------------------- 1 | MEMORY 2 | { 3 | rom (rx) : ORIGIN = 0x08000000, LENGTH = 64K 4 | ram (rwx) : ORIGIN = 0x20000000, LENGTH = 8K 5 | } 6 | 7 | EXTERN (vector_table) 8 | 9 | ENTRY(reset_handler) 10 | 11 | SECTIONS 12 | { 13 | .text : { 14 | *(.vectors) /* Vector table */ 15 | *(.text*) /* Program code */ 16 | . = ALIGN(4); 17 | } >rom 18 | 19 | end = .; 20 | } 21 | 22 | -------------------------------------------------------------------------------- /faultplugin/LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright 2021 Florian Andreas Hauschild 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /faultplugin/Makefile: -------------------------------------------------------------------------------- 1 | # -*- Mode: makefile -*- 2 | # 3 | # This Makefile example is fairly independent from the main makefile 4 | # so users can take and adapt it for their build. We only really 5 | # include config-host.mak so we don't have to repeat probing for 6 | # cflags that the main configure has already done for us. 7 | # 8 | 9 | #BUILD_DIR := $(CURDIR)/../../../build/release 10 | BUILD_DIR := $(CURDIR)/../qemu/build/debug 11 | 12 | include $(BUILD_DIR)/config-host.mak 13 | 14 | VPATH += $(SRC_PATH)/tests/plugin/develop 15 | 16 | NAMES := 17 | #NAMES += bb 18 | #NAMES += empty 19 | NAMES += faultplugin 20 | #NAMES += mem 21 | #NAMES += hotblocks 22 | #NAMES += howvec 23 | #NAMES += hotpages 24 | #NAMES += lockstep 25 | 26 | SONAMES := $(addsuffix .so,$(addprefix lib,$(NAMES))) 27 | 28 | # The main QEMU uses Glib extensively so it's perfectly fine to use it 29 | # in plugins (which many example do). 30 | CFLAGS = $(GLIB_CFLAGS) 31 | CFLAGS += -fPIC 32 | CFLAGS += $(if $(findstring no-psabi,$(QEMU_CFLAGS)),-Wpsabi) 33 | CFLAGS += -I$(SRC_PATH)/include 34 | #CFLAGS += -I$(SRC_PATH)/include/hw/core 35 | CFLAGS += -I$(BUILD_DIR) 36 | # Used for protobuf 37 | LDLIBS = -lprotobuf-c 38 | all: $(SONAMES) 39 | 40 | lib/avl.o: lib/avl.c 41 | $(CC) $(CFLAGS) -c -o $@ $< 42 | 43 | faultdata.o: faultdata.c 44 | $(CC) $(CFLAGS) -c -o $@ $< 45 | 46 | registerdump.o: registerdump.c 47 | $(CC) $(CFLAGS) -c -o $@ $< 48 | 49 | singlestep.o: singlestep.c 50 | $(CC) $(CFLAGS) -c -o $@ $< 51 | 52 | fault_list.o: fault_list.c 53 | $(CC) $(CFLAGS) -c -o $@ $< 54 | 55 | fault_injection.o: fault_injection.c 56 | $(CC) $(CFLAGS) -c -o $@ $< 57 | 58 | tb_info_data_collection.o: tb_info_data_collection.c 59 | $(CC) $(CFLAGS) -c -o $@ $< 60 | 61 | tb_exec_data_collection.o: tb_exec_data_collection.c 62 | $(CC) $(CFLAGS) -c -o $@ $< 63 | 64 | tb_faulted_collection.o : tb_faulted_collection.c 65 | $(CC) $(CFLAGS) -c -o $@ $< 66 | 67 | protobuf/fault.pb-c.o: ./protobuf/fault.pb-c.c 68 | $(CC) $(CFLAGS) -c -o $@ $< 69 | 70 | protobuf/control.pb-c.o: ./protobuf/control.pb-c.c 71 | $(CC) $(CFLAGS) -c -o $@ $< 72 | 73 | protobuf/data.pb-c.o: ./protobuf/data.pb-c.c 74 | $(CC) $(CFLAGS) -c -o $@ $< 75 | 76 | %.o: %.c 77 | $(CC) $(CFLAGS) -c -o $@ $< 78 | 79 | lib%.so: %.o lib/avl.o faultdata.o registerdump.o singlestep.o fault_list.o fault_injection.o tb_info_data_collection.o tb_exec_data_collection.o tb_faulted_collection.o protobuf/fault.pb-c.o protobuf/control.pb-c.o protobuf/data.pb-c.o 80 | $(CC) -shared -Wl,-soname,$@ -o $@ $^ $(LDLIBS) 81 | 82 | clean: 83 | rm -f *.o *.so *.d 84 | rm -f lib/*.o 85 | rm -f protobuf/*.o 86 | rm -Rf .libs 87 | 88 | .PHONY: all clean 89 | -------------------------------------------------------------------------------- /faultplugin/fault_injection.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Florian Andreas Hauschild 3 | * Copyright (c) 2021 Fraunhofer AISEC 4 | * Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | * This file contains the header for injecting faults in qemu 19 | */ 20 | 21 | #ifndef FAULT_INJECTION 22 | #define FAULT_INJECTION 23 | 24 | #include "fault_list.h" 25 | #include "singlestep.h" 26 | 27 | /** 28 | * inject_fault 29 | * 30 | * At this point the fault needs to be injected. This is the function to select the right model and call the injection function 31 | * 32 | * current: Struct address containing the fault information needed 33 | */ 34 | void inject_fault(fault_list_t * current); 35 | 36 | /** 37 | * reverse_fault 38 | * 39 | * Reverse the fault injected 40 | * 41 | * current: fault description 42 | */ 43 | void reverse_fault(fault_list_t * current); 44 | 45 | /** 46 | * inject_register_fault 47 | * 48 | * Inject fault into registers. Reads the current string and determines the register that is attacked, loads it and performs the fault required 49 | */ 50 | void inject_register_fault(fault_list_t * current); 51 | 52 | /** 53 | * reverse_register_fault 54 | */ 55 | void reverse_register_fault(fault_list_t * current); 56 | 57 | /** 58 | * inject_memory_fault 59 | * 60 | * injects fault into memory regions 61 | * Reads current struct to determine the location, model, and mask of fault. 62 | * Then performs the fault injection 63 | * 64 | * current: Struct address containing the fault information 65 | */ 66 | void inject_memory_fault(fault_list_t * current); 67 | 68 | /** 69 | * process_overwrite_memory 70 | * 71 | * Read memory, then overwrite the bytes specified by mask value. Num_bytes specify the number of bytes. currently max 16 bytes. 72 | * 73 | * address: base address of lowest byte 74 | * maks: Values to overwrite the bytes at address location. 75 | * num_bytes: Number of bytes to be overwritten. 76 | * restoremask: Mask used to restore back values to original values for temoporary faults 77 | */ 78 | void process_overwrite_memory(uint64_t address, uint8_t num_bytes, uint8_t mask[], uint8_t restoremask[]); 79 | 80 | /** 81 | * process_set1_memory 82 | * 83 | * Read memory, then set bits according to mask, then write memory back 84 | * 85 | * address: base address of lowest byte 86 | * mask: mask containing which bits need to be flipped to 1 87 | */ 88 | void process_set1_memory(uint64_t address, uint8_t mask[], uint8_t restoremask[]); 89 | 90 | /** 91 | * process_reverse_fault 92 | * 93 | * Read memory, then apply restore mask according to fault mask, then write memory back 94 | * 95 | * address: base address of fault 96 | * mask: location mask of bits set to 0 for reverse 97 | */ 98 | void process_reverse_fault(uint64_t address, uint8_t mask[], uint8_t restoremask[]); 99 | 100 | /** 101 | * process_set0_memory 102 | * 103 | * Read memory, then clear bits according to mask, then write memory back 104 | * 105 | * address: base address of fault 106 | * mask: location mask of bits set to 0 107 | */ 108 | void process_set0_memory(uint64_t address, uint8_t mask[], uint8_t restoremask[]); 109 | 110 | /** 111 | * process_toggle_memory 112 | * 113 | * Read memory, then toggle bits according to mask, then write memory back 114 | * 115 | * address: base address of fault 116 | * mask: location mask of bits to be toggled 117 | */ 118 | void process_toggle_memory(uint64_t address, uint8_t mask[], uint8_t restoremask[]); 119 | #endif 120 | -------------------------------------------------------------------------------- /faultplugin/fault_list.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Florian Andreas Hauschild 3 | * Copyright (c) 2021 Fraunhofer AISEC 4 | * Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | * This function manages the fault description objects 19 | */ 20 | 21 | #include "fault_list.h" 22 | 23 | 24 | /* Global data structures */ 25 | 26 | fault_list_t *first_fault; 27 | 28 | void init_fault_list(void) 29 | { 30 | first_fault = NULL; 31 | } 32 | 33 | 34 | /** 35 | * add fault 36 | * 37 | * This function appends one fault to the linked list. 38 | * 39 | * fault_address: address of fault 40 | * fault_type: type of fault. See enum on implemented targets 41 | * fault_model: model of fault. See enum on implemented fault models 42 | * fault_lifetime: How long the fault should reside. 0 means indefinitely 43 | * fault_mask: bit mask on which bits should be targeted. 44 | * fault_trigger_address: Address of trigger location. Fault will be injected if this location is reached 45 | * fault_trigger_hitcounter: Set how many times the location needs to be reached before the fault is injected 46 | * num_bytes: Used by overwrite to determen the number of bytes to overwrite 47 | * 48 | * return -1 if fault 49 | */ 50 | int add_fault(uint64_t fault_address, uint64_t fault_type, uint64_t fault_model, uint64_t fault_lifetime, uint8_t fault_mask[16], uint64_t fault_trigger_address, uint64_t fault_trigger_hitcounter, uint8_t num_bytes) 51 | { 52 | fault_list_t *new_fault; 53 | new_fault = malloc(sizeof(fault_list_t)); 54 | if( new_fault == NULL) 55 | { 56 | return -1; 57 | } 58 | new_fault->next = NULL; 59 | new_fault->fault.address = fault_address; 60 | new_fault->fault.type = fault_type; 61 | new_fault->fault.model = fault_model; 62 | new_fault->fault.lifetime = fault_lifetime; 63 | //new_fault->fault.mask = fault_mask; 64 | new_fault->fault.trigger.address = fault_trigger_address; 65 | new_fault->fault.trigger.hitcounter = fault_trigger_hitcounter; 66 | new_fault->fault.num_bytes = num_bytes; 67 | for(int i = 0; i < 16; i++) 68 | { 69 | new_fault->fault.restoremask[i] = 0; 70 | new_fault->fault.mask[i] = fault_mask[i]; 71 | } 72 | if(first_fault != NULL) 73 | { 74 | new_fault->next = first_fault; 75 | } 76 | first_fault = new_fault; 77 | return 0; 78 | } 79 | 80 | 81 | /** 82 | * 83 | * delete_fault_queue 84 | * 85 | * This function removes faults from linked list 86 | * 87 | */ 88 | void delete_fault_queue(void) 89 | { 90 | fault_list_t *del_item = NULL; 91 | while(first_fault != NULL) 92 | { 93 | del_item = first_fault; 94 | first_fault = first_fault->next; 95 | free(del_item); 96 | } 97 | } 98 | 99 | 100 | /** 101 | * return first 102 | * 103 | * This function exists to separate fault list management from the rest of the code base 104 | */ 105 | fault_list_t* return_first_fault(void) 106 | { 107 | return first_fault; 108 | } 109 | 110 | /** 111 | * return_next 112 | * 113 | * function to return next pointer. 114 | * This is to be able to change the current link list if desired 115 | */ 116 | fault_list_t * return_next(fault_list_t * current) 117 | { 118 | return current->next; 119 | } 120 | 121 | /** 122 | * get_fault_trigger_address 123 | * 124 | * function to return the fault address. 125 | * This is to be able to change the current data structure if needed 126 | */ 127 | uint64_t get_fault_trigger_address(fault_list_t * current) 128 | { 129 | return current->fault.trigger.address; 130 | } 131 | 132 | /** 133 | * set_fault_trigger_num 134 | * 135 | * Function sets the trigger number field. This is done to separate between two triggers with the same address 136 | */ 137 | void set_fault_trigger_num(fault_list_t * current, uint64_t trignum) 138 | { 139 | current->fault.trigger.trignum = trignum; 140 | } 141 | 142 | fault_list_t * get_fault_struct_by_trigger(uint64_t fault_trigger_address, uint64_t fault_trigger_number) 143 | { 144 | fault_list_t * current = first_fault; 145 | while(current != NULL) 146 | { 147 | if(current->fault.trigger.address == fault_trigger_address) 148 | { 149 | if(current->fault.trigger.trignum == fault_trigger_number) 150 | { 151 | return current; 152 | } 153 | } 154 | current = current->next; 155 | } 156 | return NULL; 157 | } 158 | -------------------------------------------------------------------------------- /faultplugin/fault_list.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Florian Andreas Hauschild 3 | * Copyright (c) 2021 Fraunhofer AISEC 4 | * Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | * This file contains the headers for managing fault descriptions 19 | */ 20 | 21 | #ifndef FAULT_LIST 22 | #define FAULT_LIST 23 | #include 24 | #include 25 | 26 | 27 | typedef struct 28 | { 29 | uint64_t address; //uint64_t? 30 | uint64_t hitcounter; 31 | uint64_t trignum; 32 | } fault_trigger_t; 33 | 34 | typedef struct 35 | { 36 | uint64_t address; //uint64_t? 37 | uint64_t type; //Typedef enum? 38 | uint64_t model; 39 | uint64_t lifetime; 40 | uint8_t num_bytes; // Used by overwrite to find out how many bytes to overwrite 41 | uint8_t mask[16]; // uint8_t array? 42 | uint8_t restoremask[16]; 43 | fault_trigger_t trigger; 44 | } fault_t; 45 | 46 | typedef struct fault_list_t fault_list_t; 47 | typedef struct fault_list_t 48 | { 49 | fault_list_t *next; 50 | fault_t fault; 51 | } fault_list_t; 52 | 53 | 54 | 55 | void init_fault_list(void); 56 | 57 | /** 58 | * add fault 59 | * 60 | * This function appends one fault to the linked list. 61 | * 62 | * fault_address: address of fault 63 | * fault_type: type of fault. see enum on implemented targets 64 | * fault_model: model of fault. see enum on implemented fault models 65 | * fault_lifetime: How long should the fault reside. 0 means indefinitely 66 | * fault_mask: bit mask on which bits should be targeted. 67 | * fault_trigger_address: Address of trigger location. Fault will be injected if this location is reached 68 | * fault_trigger_hitcounter: set how many times the location needs to be reached before the fault is injected 69 | * num_bytes: Used by overwrite to determen the number of bytes to overwrite 70 | * 71 | * return -1 if fault 72 | */ 73 | int add_fault(uint64_t fault_address, uint64_t fault_type, uint64_t fault_model, uint64_t fault_lifetime, uint8_t fault_mask[16], uint64_t fault_trigger_address, uint64_t fault_trigger_hitcounter, uint8_t num_bytes); 74 | 75 | /** 76 | * 77 | * delete_fault_queue 78 | * 79 | * This function removes faults from linked list 80 | * 81 | */ 82 | void delete_fault_queue(void); 83 | 84 | /** 85 | * return_first_fault 86 | * 87 | * This function exists to separate fault list management from the rest of the code base 88 | */ 89 | fault_list_t* return_first_fault(void); 90 | 91 | /** 92 | * return_next 93 | * 94 | * function to return next pointer. 95 | * This is to be able to change the current link list if desired 96 | */ 97 | fault_list_t * return_next(fault_list_t * current); 98 | 99 | /** 100 | * get_fault_trigger_address 101 | * 102 | * function to return the fault address. 103 | * This is to be able to change the current data structure if needed 104 | */ 105 | uint64_t get_fault_trigger_address(fault_list_t * current); 106 | 107 | /** 108 | * set_fault_trigger_num 109 | * 110 | * Function sets the trigger number field. This is done to separate between two triggers with the same address 111 | */ 112 | void set_fault_trigger_num(fault_list_t * current, uint64_t trignum); 113 | 114 | /** 115 | * get_fault_struct_by_trigger 116 | * 117 | * Function returns the corresponding fault to a trigger address and trigger number 118 | */ 119 | fault_list_t * get_fault_struct_by_trigger(uint64_t fault_trigger_address, uint64_t fault_trigger_number); 120 | #endif 121 | -------------------------------------------------------------------------------- /faultplugin/faultdata.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Florian Andreas Hauschild 3 | * Copyright (c) 2021 Fraunhofer AISEC 4 | * Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | * This part of the plugin manages the memory data dumps collected and how to 19 | * send them to the data pipe 20 | */ 21 | 22 | #include "faultdata.h" 23 | 24 | typedef struct 25 | { 26 | uint64_t address; 27 | uint64_t len; 28 | uint64_t num_dumps; 29 | uint64_t used_dumps; 30 | uint8_t **buf; 31 | }memorydump_t; 32 | 33 | memorydump_t **memdump; 34 | uint64_t num_memdump; 35 | uint64_t used_memdump; 36 | 37 | 38 | void init_memory_module(void) 39 | { 40 | memdump = NULL; 41 | num_memdump = 0; 42 | used_memdump = 0; 43 | } 44 | 45 | int memory_module_configured(void) 46 | { 47 | if(memdump == NULL) 48 | { 49 | return 0; 50 | } 51 | return 1; 52 | } 53 | // Initialise vector with empty elements 54 | int init_memory(int number_of_regions) 55 | { 56 | num_memdump = number_of_regions; 57 | used_memdump = 0; 58 | // Initialise vector 59 | memdump = NULL; 60 | memdump = malloc(sizeof(memorydump_t *) * number_of_regions); 61 | if(memdump == NULL) 62 | { 63 | return -1; 64 | } 65 | // Clear pointers with NULL 66 | for(int i = 0; i < number_of_regions; i++) 67 | { 68 | *(memdump + i) = NULL; 69 | } 70 | // Fill vector with struct 71 | for(int i = 0; i < number_of_regions; i++) 72 | { 73 | memorydump_t *tmp = malloc(sizeof(memorydump_t)); 74 | if(tmp == NULL) 75 | { 76 | goto Abort; 77 | } 78 | tmp->address = 0; 79 | tmp->len = 0; 80 | tmp->num_dumps = 0; 81 | tmp->used_dumps = 0; 82 | tmp->buf = NULL; 83 | *(memdump + i) = tmp; 84 | } 85 | return 0; 86 | Abort: 87 | delete_memory_dump(); 88 | return -1; 89 | } 90 | 91 | void delete_memory_dump(void) 92 | { 93 | if(memdump != NULL) 94 | { 95 | for(int i = 0; i < num_memdump; i++) 96 | { 97 | if(*(memdump + i) != NULL) 98 | { 99 | memorydump_t *tmp = *(memdump + i); 100 | if(tmp->buf != NULL) 101 | { 102 | for(int j = 0; j < tmp->num_dumps; j++) 103 | { 104 | free(*((tmp->buf) + j)); 105 | } 106 | free(tmp->buf); 107 | } 108 | free(*(memdump + i)); 109 | } 110 | } 111 | free(memdump); 112 | } 113 | memdump = NULL; 114 | } 115 | // Fill in one vector element 116 | int insert_memorydump_config(uint64_t baseaddress, uint64_t len) 117 | { 118 | g_autoptr(GString) out = g_string_new(""); 119 | if(memdump == NULL) 120 | { 121 | qemu_plugin_outs("[ERROR]: Memorydump: Not initialised!\n"); 122 | return -1; 123 | } 124 | if(num_memdump == used_memdump) 125 | { 126 | qemu_plugin_outs("[DEBUG]: memorydump: Increase memory dump vector........."); 127 | memorydump_t **buf = malloc(sizeof(memorydump_t *) * (num_memdump + 1)); 128 | if(buf == NULL) 129 | { 130 | qemu_plugin_outs("failed\n[ERROR]: Could not increase memorydump vector! Malloc failed\n"); 131 | return -1; 132 | } 133 | for(int i = 0; i < num_memdump; i++) 134 | { 135 | *(buf + i) = *(memdump + i); 136 | } 137 | free(memdump); 138 | memdump = NULL; 139 | memdump = buf; 140 | *(memdump + num_memdump) = malloc(sizeof(memorydump_t)); 141 | memorydump_t *tmp = *(memdump + num_memdump); 142 | tmp->buf = NULL; 143 | tmp->address = 0; 144 | tmp->len = 0; 145 | tmp->num_dumps = 0; 146 | tmp->used_dumps = 0; 147 | num_memdump++; 148 | qemu_plugin_outs("done\n"); 149 | } 150 | memorydump_t *tmp = *(memdump + used_memdump); 151 | used_memdump++; 152 | tmp->address = baseaddress; 153 | tmp->len = len; 154 | tmp->num_dumps = 1; 155 | tmp->buf = malloc(sizeof(uint8_t*) * tmp->num_dumps); 156 | if(tmp->buf == NULL) 157 | { 158 | qemu_plugin_outs("[ERROR]: Could not allocate memory vor buffer!\n"); 159 | tmp->address = 0; 160 | return -1; 161 | } 162 | for(int j = 0; j < tmp->num_dumps; j++) 163 | { 164 | *(tmp->buf + j) = malloc(sizeof(uint8_t) * len); 165 | for( int i = 0; i < len; i++) 166 | { 167 | *(*(tmp->buf + j) + i) = 0; 168 | } 169 | } 170 | g_string_printf(out,"[DEBUG]: Memorydump: config was address %08lx len %li\n", baseaddress, len); 171 | qemu_plugin_outs(out->str); 172 | return 1; 173 | } 174 | 175 | int read_all_memory(void) 176 | { 177 | for(int i = 0; i < used_memdump; i++) 178 | { 179 | int status = read_memoryregion( i); 180 | if(status != 0) 181 | { 182 | qemu_plugin_outs("[ERROR]: read_memoryregion failed\n"); 183 | return -1; 184 | } 185 | } 186 | 187 | return 0; 188 | } 189 | 190 | void read_specific_memoryregion(uint64_t baseaddress) 191 | { 192 | for(int i = 0; i < used_memdump; i++) 193 | { 194 | memorydump_t *current = *(memdump + i); 195 | if(current->address == baseaddress) 196 | { 197 | read_memoryregion(i); 198 | } 199 | } 200 | } 201 | 202 | int read_memoryregion(uint64_t memorydump_position) 203 | { 204 | g_autoptr(GString) out = g_string_new(""); 205 | uint64_t ret; 206 | memorydump_t *current = *(memdump + memorydump_position); 207 | if(current->num_dumps == current->used_dumps) 208 | { 209 | qemu_plugin_outs("[DEBUG]: Memorydump: Allocate new buffer......"); 210 | //We need to add a new memory dump arrea 211 | uint8_t **buf = malloc(sizeof(uint8_t *) * (current->num_dumps + 1)); 212 | if(buf == NULL) 213 | { 214 | qemu_plugin_outs("failed\n[ERROR]: Could not create new buffer vector for memory region! Malloc error\n"); 215 | return -1; 216 | } 217 | for(int i = 0; i < current->num_dumps; i++) 218 | { 219 | *(buf + i) = *(current->buf + i); 220 | } 221 | *(buf + current->num_dumps) = malloc(sizeof(uint8_t) * current->len); 222 | if(*(buf + current->num_dumps) == NULL) 223 | { 224 | qemu_plugin_outs("failed\n[ERROR]: Could not create buffer! Malloc Error\n"); 225 | free(buf); 226 | return -1; 227 | } 228 | for( int i = 0; i < current->len; i++) 229 | { 230 | *(*(buf + current->num_dumps) + i) = 0; 231 | } 232 | free(current->buf); 233 | current->buf = buf; 234 | current->num_dumps++; 235 | qemu_plugin_outs("done\n"); 236 | } 237 | qemu_plugin_outs("[DEBUG]: start reading memory memdump"); 238 | ret = qemu_plugin_rw_memory_cpu(current->address, *(current->buf + current->used_dumps), current->len, 0); 239 | current->used_dumps++; 240 | qemu_plugin_outs("..... done\n"); 241 | return ret; 242 | } 243 | 244 | void readout_memorydump(uint64_t memorydump_position, Archie__MemDumpInfo* protobuf_mem_dump_info) 245 | { 246 | memorydump_t *current = *(memdump + memorydump_position); 247 | protobuf_mem_dump_info->address = current->address; 248 | protobuf_mem_dump_info->len = current-> len; 249 | 250 | // Allocate and init memory for memory dumps for a protobuf memdump object 251 | Archie__MemDump** mem_dump_list; 252 | mem_dump_list = malloc(sizeof(Archie__MemDump*) * current->used_dumps); 253 | if(mem_dump_list == NULL) 254 | { 255 | qemu_plugin_outs("[ERROR]: Malloc for Archie__MemDump array failed\n"); 256 | exit(EXIT_FAILURE); 257 | } 258 | protobuf_mem_dump_info->n_dumps = current->used_dumps; 259 | 260 | for(int i = 0; i < current->used_dumps; i++) 261 | { 262 | mem_dump_list[i] = malloc(sizeof(Archie__MemDump)); 263 | if(mem_dump_list[i] == NULL) 264 | { 265 | qemu_plugin_outs("[ERROR]: Malloc error Archie__MemDump failed\n"); 266 | exit(EXIT_FAILURE); 267 | } 268 | archie__mem_dump__init(mem_dump_list[i]); 269 | 270 | uint8_t *dump = *(current->buf + i); 271 | 272 | mem_dump_list[i]->mem.len = current->len; 273 | mem_dump_list[i]->mem.data = dump; 274 | } 275 | 276 | protobuf_mem_dump_info->dumps = mem_dump_list; 277 | } 278 | 279 | 280 | int readout_all_memorydump(Archie__Data* protobuf_msg) 281 | { 282 | // Allocate and init memory for list of memory dump infos on protobuf message 283 | if(used_memdump == 0) 284 | { 285 | return 0; 286 | } 287 | 288 | Archie__MemDumpInfo** mem_dump_info_list; 289 | mem_dump_info_list = malloc(sizeof(Archie__MemDumpInfo*) * used_memdump); 290 | if(mem_dump_info_list == NULL) 291 | { 292 | qemu_plugin_outs("[ERROR]: Malloc for Archie__MemDumpInfo array failed\n"); 293 | return -1; 294 | } 295 | protobuf_msg->n_mem_dump_infos = used_memdump; 296 | 297 | for(int i = 0; i < used_memdump; i++) 298 | { 299 | mem_dump_info_list[i] = malloc(sizeof(Archie__MemDumpInfo)); 300 | if(mem_dump_info_list[i] == NULL) 301 | { 302 | qemu_plugin_outs("[ERROR]: Malloc for Archie__MemDumpInfo failed\n"); 303 | return -1; 304 | } 305 | archie__mem_dump_info__init(mem_dump_info_list[i]); 306 | 307 | readout_memorydump(i, mem_dump_info_list[i]); 308 | } 309 | 310 | protobuf_msg->mem_dump_infos = mem_dump_info_list; 311 | return 0; 312 | } 313 | -------------------------------------------------------------------------------- /faultplugin/faultdata.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Florian Andreas Hauschild 3 | * Copyright (c) 2021 Fraunhofer AISEC 4 | * Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | * This File contains the headers for functions managing memory dumps 19 | */ 20 | 21 | #ifndef QEMU_FAULTPLUGIN_DATA 22 | #define QEMU_FAULTPLUGIN_DATA 23 | 24 | #include 25 | #include 26 | #include "faultplugin.h" 27 | #include "protobuf/data.pb-c.h" 28 | 29 | #include 30 | 31 | #include 32 | 33 | 34 | /** 35 | * init_memory_module() 36 | * 37 | * Initialise the global variables. 38 | * This only makes sure the plugin can deliver a valid response to memory_module_configured 39 | */ 40 | void init_memory_module(void); 41 | 42 | 43 | /** 44 | * memory_module_configured() 45 | * 46 | * returns 1 if configured otherwise 0 47 | */ 48 | int memory_module_configured(void); 49 | 50 | /** 51 | * init_memory 52 | * 53 | * Initialise the global pointer with the number_of_regions amount of structs. 54 | * 55 | * @param number_of_regions: Number of structs to initialise 56 | */ 57 | int init_memory(int number_of_regions); 58 | 59 | /** 60 | * delete_memory_dump 61 | * 62 | * Free the complete internal data structure. After this all data is no longer accessible 63 | */ 64 | void delete_memory_dump(void); 65 | 66 | /** 67 | * insert_memorydump_config 68 | * 69 | * Initialise one vector element with the memory region, that should be read. 70 | * 71 | * @param baseaddress: Baseaddress of memory region 72 | * @param len: length of memory region in bytes 73 | */ 74 | int insert_memorydump_config(uint64_t baseaddress, uint64_t len); 75 | 76 | /** 77 | * read_all_memory 78 | * 79 | * Read all client memory regions defined by user. 80 | */ 81 | int read_all_memory(void); 82 | 83 | 84 | /** 85 | * read_specific_memoryregion 86 | * 87 | * Read a specific memory region as defined by baseaddress 88 | * 89 | * @param baseaddress: the start location provided by insert_memory_dump_config 90 | */ 91 | void read_specific_memoryregion(uint64_t baseaddress); 92 | 93 | /** 94 | * read_memoryregion 95 | * 96 | * Read one client memory region defined by user 97 | * 98 | * @param memorydump_position: select which region should be read in vector element position 99 | */ 100 | int read_memoryregion(uint64_t memorydump_position); 101 | 102 | /** 103 | * readout_memorydump_dump 104 | * 105 | * parse the memory region to the protobuf message 106 | * 107 | * @param memorydump_position: select which region should be read in vector element 108 | * @param dump_pos: select which data dump should be written to pipe. Multiple can be taken during the execution of the config. 109 | * @param protobuf_msg_memdump: protobuf mem_dump belonging to mem_dump_info 110 | */ 111 | void readout_memorydump_dump(uint64_t memorydump_position, uint64_t dump_pos, Archie__MemDump* protobuf_msg_memdump); 112 | 113 | /** 114 | * readout_memorydump 115 | * 116 | * Call read_memorydump_dump for all available dumps inside the struct. All 117 | * dumps are parsed to protobuf message. Also parse config for this memorydump 118 | * 119 | */ 120 | void readout_memorydump(uint64_t memorydump_position, Archie__MemDumpInfo* protobuf_mem_dump_object); 121 | 122 | /** 123 | * readout_all_memorydump 124 | * 125 | * This function will write all memorydumps to protobuf message 126 | */ 127 | int readout_all_memorydump(Archie__Data* protobuf_msg); 128 | 129 | #endif 130 | -------------------------------------------------------------------------------- /faultplugin/faultplugin.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Florian Andreas Hauschild 3 | * Copyright (c) 2021 Fraunhofer AISEC 4 | * Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | * This file contains the header for the main part of the plugin. 19 | */ 20 | 21 | #ifndef QEMU_FAULTPLUGIN 22 | #define QEMU_FAULTPLUGIN 23 | 24 | 25 | #include 26 | #include 27 | #include "fault_list.h" 28 | 29 | enum{ DATA, INSTRUCTION, REGISTER}; 30 | enum{ SET0, SET1, TOGGLE, OVERWRITE}; 31 | 32 | int register_live_faults_callback(fault_list_t *fault); 33 | 34 | void invalidate_fault_trigger_address(int fault_trigger_number); 35 | 36 | int plugin_write_to_data_pipe(char *str, size_t len); 37 | 38 | size_t readout_pipe_size(int pipe_fd); 39 | 40 | int readout_pipe(uint8_t **out, int pipe_fd); 41 | 42 | #endif 43 | -------------------------------------------------------------------------------- /faultplugin/lib/README.md: -------------------------------------------------------------------------------- 1 | # GNU libavl 2 | 3 | This avl library was taken from the GNU libavl. The source can be clonded from [here](git clone https://git.savannah.gnu.org/git/avl.git) 4 | The Website of the project can be found under [https://savannah.gnu.org/projects/avl/](https://savannah.gnu.org/projects/avl/) 5 | -------------------------------------------------------------------------------- /faultplugin/lib/avl.h: -------------------------------------------------------------------------------- 1 | /* Produced by texiweb from libavl.w. */ 2 | 3 | /* libavl - library for manipulation of binary trees. 4 | Copyright (C) 1998, 1999, 2000, 2001, 2002, 2004 Free Software 5 | Foundation, Inc. 6 | 7 | This library is free software; you can redistribute it and/or 8 | modify it under the terms of the GNU Lesser General Public 9 | License as published by the Free Software Foundation; either 10 | version 3 of the License, or (at your option) any later version. 11 | 12 | This library is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | Lesser General Public License for more details. 16 | 17 | You should have received a copy of the GNU Lesser General Public 18 | License along with this library; if not, write to the Free Software 19 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 20 | 02110-1301 USA. 21 | */ 22 | 23 | #ifndef AVL_H 24 | #define AVL_H 1 25 | 26 | #include 27 | 28 | /* Function types. */ 29 | typedef int avl_comparison_func (const void *avl_a, const void *avl_b, 30 | void *avl_param); 31 | typedef void avl_item_func (void *avl_item, void *avl_param); 32 | typedef void *avl_copy_func (void *avl_item, void *avl_param); 33 | 34 | #ifndef LIBAVL_ALLOCATOR 35 | #define LIBAVL_ALLOCATOR 36 | /* Memory allocator. */ 37 | struct libavl_allocator 38 | { 39 | void *(*libavl_malloc) (struct libavl_allocator *, size_t libavl_size); 40 | void (*libavl_free) (struct libavl_allocator *, void *libavl_block); 41 | }; 42 | #endif 43 | 44 | /* Default memory allocator. */ 45 | extern struct libavl_allocator avl_allocator_default; 46 | void *avl_malloc (struct libavl_allocator *, size_t); 47 | void avl_free (struct libavl_allocator *, void *); 48 | 49 | /* Maximum AVL tree height. */ 50 | #ifndef AVL_MAX_HEIGHT 51 | #define AVL_MAX_HEIGHT 92 52 | #endif 53 | 54 | /* Tree data structure. */ 55 | struct avl_table 56 | { 57 | struct avl_node *avl_root; /* Tree's root. */ 58 | avl_comparison_func *avl_compare; /* Comparison function. */ 59 | void *avl_param; /* Extra argument to |avl_compare|. */ 60 | struct libavl_allocator *avl_alloc; /* Memory allocator. */ 61 | size_t avl_count; /* Number of items in tree. */ 62 | unsigned long avl_generation; /* Generation number. */ 63 | }; 64 | 65 | /* An AVL tree node. */ 66 | struct avl_node 67 | { 68 | struct avl_node *avl_link[2]; /* Subtrees. */ 69 | void *avl_data; /* Pointer to data. */ 70 | signed char avl_balance; /* Balance factor. */ 71 | }; 72 | 73 | /* AVL traverser structure. */ 74 | struct avl_traverser 75 | { 76 | struct avl_table *avl_table; /* Tree being traversed. */ 77 | struct avl_node *avl_node; /* Current node in tree. */ 78 | struct avl_node *avl_stack[AVL_MAX_HEIGHT]; 79 | /* All the nodes above |avl_node|. */ 80 | size_t avl_height; /* Number of nodes in |avl_parent|. */ 81 | unsigned long avl_generation; /* Generation number. */ 82 | }; 83 | 84 | /* Table functions. */ 85 | struct avl_table *avl_create (avl_comparison_func *, void *, 86 | struct libavl_allocator *); 87 | struct avl_table *avl_copy (const struct avl_table *, avl_copy_func *, 88 | avl_item_func *, struct libavl_allocator *); 89 | void avl_destroy (struct avl_table *, avl_item_func *); 90 | void **avl_probe (struct avl_table *, void *); 91 | void *avl_insert (struct avl_table *, void *); 92 | void *avl_replace (struct avl_table *, void *); 93 | void *avl_delete (struct avl_table *, const void *); 94 | void *avl_find (const struct avl_table *, const void *); 95 | void avl_assert_insert (struct avl_table *, void *); 96 | void *avl_assert_delete (struct avl_table *, void *); 97 | 98 | #define avl_count(table) ((size_t) (table)->avl_count) 99 | 100 | /* Table traverser functions. */ 101 | void avl_t_init (struct avl_traverser *, struct avl_table *); 102 | void *avl_t_first (struct avl_traverser *, struct avl_table *); 103 | void *avl_t_last (struct avl_traverser *, struct avl_table *); 104 | void *avl_t_find (struct avl_traverser *, struct avl_table *, void *); 105 | void *avl_t_insert (struct avl_traverser *, struct avl_table *, void *); 106 | void *avl_t_copy (struct avl_traverser *, const struct avl_traverser *); 107 | void *avl_t_next (struct avl_traverser *); 108 | void *avl_t_prev (struct avl_traverser *); 109 | void *avl_t_cur (struct avl_traverser *); 110 | void *avl_t_replace (struct avl_traverser *, void *); 111 | 112 | #endif /* avl.h */ 113 | -------------------------------------------------------------------------------- /faultplugin/protobuf/control.pb-c.h: -------------------------------------------------------------------------------- 1 | /* Generated by the protocol buffer compiler. DO NOT EDIT! */ 2 | /* Generated from: control.proto */ 3 | 4 | #ifndef PROTOBUF_C_control_2eproto__INCLUDED 5 | #define PROTOBUF_C_control_2eproto__INCLUDED 6 | 7 | #include 8 | 9 | PROTOBUF_C__BEGIN_DECLS 10 | 11 | #if PROTOBUF_C_VERSION_NUMBER < 1003000 12 | # error This file was generated by a newer version of protoc-c which is incompatible with your libprotobuf-c headers. Please update your headers. 13 | #elif 1004001 < PROTOBUF_C_MIN_COMPILER_VERSION 14 | # error This file was generated by an older version of protoc-c which is incompatible with your libprotobuf-c headers. Please regenerate this file with a newer version of protoc-c. 15 | #endif 16 | 17 | 18 | typedef struct Archie__Control Archie__Control; 19 | typedef struct Archie__EndPoint Archie__EndPoint; 20 | typedef struct Archie__MemoryDump Archie__MemoryDump; 21 | 22 | 23 | /* --- enums --- */ 24 | 25 | 26 | /* --- messages --- */ 27 | 28 | struct Archie__Control 29 | { 30 | ProtobufCMessage base; 31 | int64_t max_duration; 32 | int64_t num_faults; 33 | protobuf_c_boolean tb_exec_list; 34 | protobuf_c_boolean tb_info; 35 | protobuf_c_boolean mem_info; 36 | uint64_t start_address; 37 | uint64_t start_counter; 38 | size_t n_end_points; 39 | Archie__EndPoint **end_points; 40 | protobuf_c_boolean tb_exec_list_ring_buffer; 41 | size_t n_memorydumps; 42 | Archie__MemoryDump **memorydumps; 43 | protobuf_c_boolean has_start; 44 | }; 45 | #define ARCHIE__CONTROL__INIT \ 46 | { PROTOBUF_C_MESSAGE_INIT (&archie__control__descriptor) \ 47 | , 0, 0, 0, 0, 0, 0, 0, 0,NULL, 0, 0,NULL, 0 } 48 | 49 | 50 | struct Archie__EndPoint 51 | { 52 | ProtobufCMessage base; 53 | uint64_t address; 54 | uint64_t counter; 55 | }; 56 | #define ARCHIE__END_POINT__INIT \ 57 | { PROTOBUF_C_MESSAGE_INIT (&archie__end_point__descriptor) \ 58 | , 0, 0 } 59 | 60 | 61 | struct Archie__MemoryDump 62 | { 63 | ProtobufCMessage base; 64 | uint64_t address; 65 | uint64_t length; 66 | }; 67 | #define ARCHIE__MEMORY_DUMP__INIT \ 68 | { PROTOBUF_C_MESSAGE_INIT (&archie__memory_dump__descriptor) \ 69 | , 0, 0 } 70 | 71 | 72 | /* Archie__Control methods */ 73 | void archie__control__init 74 | (Archie__Control *message); 75 | size_t archie__control__get_packed_size 76 | (const Archie__Control *message); 77 | size_t archie__control__pack 78 | (const Archie__Control *message, 79 | uint8_t *out); 80 | size_t archie__control__pack_to_buffer 81 | (const Archie__Control *message, 82 | ProtobufCBuffer *buffer); 83 | Archie__Control * 84 | archie__control__unpack 85 | (ProtobufCAllocator *allocator, 86 | size_t len, 87 | const uint8_t *data); 88 | void archie__control__free_unpacked 89 | (Archie__Control *message, 90 | ProtobufCAllocator *allocator); 91 | /* Archie__EndPoint methods */ 92 | void archie__end_point__init 93 | (Archie__EndPoint *message); 94 | size_t archie__end_point__get_packed_size 95 | (const Archie__EndPoint *message); 96 | size_t archie__end_point__pack 97 | (const Archie__EndPoint *message, 98 | uint8_t *out); 99 | size_t archie__end_point__pack_to_buffer 100 | (const Archie__EndPoint *message, 101 | ProtobufCBuffer *buffer); 102 | Archie__EndPoint * 103 | archie__end_point__unpack 104 | (ProtobufCAllocator *allocator, 105 | size_t len, 106 | const uint8_t *data); 107 | void archie__end_point__free_unpacked 108 | (Archie__EndPoint *message, 109 | ProtobufCAllocator *allocator); 110 | /* Archie__MemoryDump methods */ 111 | void archie__memory_dump__init 112 | (Archie__MemoryDump *message); 113 | size_t archie__memory_dump__get_packed_size 114 | (const Archie__MemoryDump *message); 115 | size_t archie__memory_dump__pack 116 | (const Archie__MemoryDump *message, 117 | uint8_t *out); 118 | size_t archie__memory_dump__pack_to_buffer 119 | (const Archie__MemoryDump *message, 120 | ProtobufCBuffer *buffer); 121 | Archie__MemoryDump * 122 | archie__memory_dump__unpack 123 | (ProtobufCAllocator *allocator, 124 | size_t len, 125 | const uint8_t *data); 126 | void archie__memory_dump__free_unpacked 127 | (Archie__MemoryDump *message, 128 | ProtobufCAllocator *allocator); 129 | /* --- per-message closures --- */ 130 | 131 | typedef void (*Archie__Control_Closure) 132 | (const Archie__Control *message, 133 | void *closure_data); 134 | typedef void (*Archie__EndPoint_Closure) 135 | (const Archie__EndPoint *message, 136 | void *closure_data); 137 | typedef void (*Archie__MemoryDump_Closure) 138 | (const Archie__MemoryDump *message, 139 | void *closure_data); 140 | 141 | /* --- services --- */ 142 | 143 | 144 | /* --- descriptors --- */ 145 | 146 | extern const ProtobufCMessageDescriptor archie__control__descriptor; 147 | extern const ProtobufCMessageDescriptor archie__end_point__descriptor; 148 | extern const ProtobufCMessageDescriptor archie__memory_dump__descriptor; 149 | 150 | PROTOBUF_C__END_DECLS 151 | 152 | 153 | #endif /* PROTOBUF_C_control_2eproto__INCLUDED */ 154 | -------------------------------------------------------------------------------- /faultplugin/protobuf/fault.pb-c.c: -------------------------------------------------------------------------------- 1 | /* Generated by the protocol buffer compiler. DO NOT EDIT! */ 2 | /* Generated from: fault.proto */ 3 | 4 | /* Do not generate deprecated warnings for self */ 5 | #ifndef PROTOBUF_C__NO_DEPRECATED 6 | #define PROTOBUF_C__NO_DEPRECATED 7 | #endif 8 | 9 | #include "fault.pb-c.h" 10 | void archie__fault__init 11 | (Archie__Fault *message) 12 | { 13 | static const Archie__Fault init_value = ARCHIE__FAULT__INIT; 14 | *message = init_value; 15 | } 16 | size_t archie__fault__get_packed_size 17 | (const Archie__Fault *message) 18 | { 19 | assert(message->base.descriptor == &archie__fault__descriptor); 20 | return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message)); 21 | } 22 | size_t archie__fault__pack 23 | (const Archie__Fault *message, 24 | uint8_t *out) 25 | { 26 | assert(message->base.descriptor == &archie__fault__descriptor); 27 | return protobuf_c_message_pack ((const ProtobufCMessage*)message, out); 28 | } 29 | size_t archie__fault__pack_to_buffer 30 | (const Archie__Fault *message, 31 | ProtobufCBuffer *buffer) 32 | { 33 | assert(message->base.descriptor == &archie__fault__descriptor); 34 | return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer); 35 | } 36 | Archie__Fault * 37 | archie__fault__unpack 38 | (ProtobufCAllocator *allocator, 39 | size_t len, 40 | const uint8_t *data) 41 | { 42 | return (Archie__Fault *) 43 | protobuf_c_message_unpack (&archie__fault__descriptor, 44 | allocator, len, data); 45 | } 46 | void archie__fault__free_unpacked 47 | (Archie__Fault *message, 48 | ProtobufCAllocator *allocator) 49 | { 50 | if(!message) 51 | return; 52 | assert(message->base.descriptor == &archie__fault__descriptor); 53 | protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator); 54 | } 55 | void archie__fault_pack__init 56 | (Archie__FaultPack *message) 57 | { 58 | static const Archie__FaultPack init_value = ARCHIE__FAULT_PACK__INIT; 59 | *message = init_value; 60 | } 61 | size_t archie__fault_pack__get_packed_size 62 | (const Archie__FaultPack *message) 63 | { 64 | assert(message->base.descriptor == &archie__fault_pack__descriptor); 65 | return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message)); 66 | } 67 | size_t archie__fault_pack__pack 68 | (const Archie__FaultPack *message, 69 | uint8_t *out) 70 | { 71 | assert(message->base.descriptor == &archie__fault_pack__descriptor); 72 | return protobuf_c_message_pack ((const ProtobufCMessage*)message, out); 73 | } 74 | size_t archie__fault_pack__pack_to_buffer 75 | (const Archie__FaultPack *message, 76 | ProtobufCBuffer *buffer) 77 | { 78 | assert(message->base.descriptor == &archie__fault_pack__descriptor); 79 | return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer); 80 | } 81 | Archie__FaultPack * 82 | archie__fault_pack__unpack 83 | (ProtobufCAllocator *allocator, 84 | size_t len, 85 | const uint8_t *data) 86 | { 87 | return (Archie__FaultPack *) 88 | protobuf_c_message_unpack (&archie__fault_pack__descriptor, 89 | allocator, len, data); 90 | } 91 | void archie__fault_pack__free_unpacked 92 | (Archie__FaultPack *message, 93 | ProtobufCAllocator *allocator) 94 | { 95 | if(!message) 96 | return; 97 | assert(message->base.descriptor == &archie__fault_pack__descriptor); 98 | protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator); 99 | } 100 | static const ProtobufCFieldDescriptor archie__fault__field_descriptors[9] = 101 | { 102 | { 103 | "address", 104 | 1, 105 | PROTOBUF_C_LABEL_NONE, 106 | PROTOBUF_C_TYPE_UINT64, 107 | 0, /* quantifier_offset */ 108 | offsetof(Archie__Fault, address), 109 | NULL, 110 | NULL, 111 | 0, /* flags */ 112 | 0,NULL,NULL /* reserved1,reserved2, etc */ 113 | }, 114 | { 115 | "type", 116 | 2, 117 | PROTOBUF_C_LABEL_NONE, 118 | PROTOBUF_C_TYPE_UINT64, 119 | 0, /* quantifier_offset */ 120 | offsetof(Archie__Fault, type), 121 | NULL, 122 | NULL, 123 | 0, /* flags */ 124 | 0,NULL,NULL /* reserved1,reserved2, etc */ 125 | }, 126 | { 127 | "model", 128 | 3, 129 | PROTOBUF_C_LABEL_NONE, 130 | PROTOBUF_C_TYPE_UINT64, 131 | 0, /* quantifier_offset */ 132 | offsetof(Archie__Fault, model), 133 | NULL, 134 | NULL, 135 | 0, /* flags */ 136 | 0,NULL,NULL /* reserved1,reserved2, etc */ 137 | }, 138 | { 139 | "lifespan", 140 | 4, 141 | PROTOBUF_C_LABEL_NONE, 142 | PROTOBUF_C_TYPE_UINT64, 143 | 0, /* quantifier_offset */ 144 | offsetof(Archie__Fault, lifespan), 145 | NULL, 146 | NULL, 147 | 0, /* flags */ 148 | 0,NULL,NULL /* reserved1,reserved2, etc */ 149 | }, 150 | { 151 | "trigger_address", 152 | 5, 153 | PROTOBUF_C_LABEL_NONE, 154 | PROTOBUF_C_TYPE_UINT64, 155 | 0, /* quantifier_offset */ 156 | offsetof(Archie__Fault, trigger_address), 157 | NULL, 158 | NULL, 159 | 0, /* flags */ 160 | 0,NULL,NULL /* reserved1,reserved2, etc */ 161 | }, 162 | { 163 | "trigger_hitcounter", 164 | 6, 165 | PROTOBUF_C_LABEL_NONE, 166 | PROTOBUF_C_TYPE_UINT64, 167 | 0, /* quantifier_offset */ 168 | offsetof(Archie__Fault, trigger_hitcounter), 169 | NULL, 170 | NULL, 171 | 0, /* flags */ 172 | 0,NULL,NULL /* reserved1,reserved2, etc */ 173 | }, 174 | { 175 | "mask_upper", 176 | 7, 177 | PROTOBUF_C_LABEL_NONE, 178 | PROTOBUF_C_TYPE_UINT64, 179 | 0, /* quantifier_offset */ 180 | offsetof(Archie__Fault, mask_upper), 181 | NULL, 182 | NULL, 183 | 0, /* flags */ 184 | 0,NULL,NULL /* reserved1,reserved2, etc */ 185 | }, 186 | { 187 | "mask_lower", 188 | 8, 189 | PROTOBUF_C_LABEL_NONE, 190 | PROTOBUF_C_TYPE_UINT64, 191 | 0, /* quantifier_offset */ 192 | offsetof(Archie__Fault, mask_lower), 193 | NULL, 194 | NULL, 195 | 0, /* flags */ 196 | 0,NULL,NULL /* reserved1,reserved2, etc */ 197 | }, 198 | { 199 | "num_bytes", 200 | 9, 201 | PROTOBUF_C_LABEL_NONE, 202 | PROTOBUF_C_TYPE_UINT32, 203 | 0, /* quantifier_offset */ 204 | offsetof(Archie__Fault, num_bytes), 205 | NULL, 206 | NULL, 207 | 0, /* flags */ 208 | 0,NULL,NULL /* reserved1,reserved2, etc */ 209 | }, 210 | }; 211 | static const unsigned archie__fault__field_indices_by_name[] = { 212 | 0, /* field[0] = address */ 213 | 3, /* field[3] = lifespan */ 214 | 7, /* field[7] = mask_lower */ 215 | 6, /* field[6] = mask_upper */ 216 | 2, /* field[2] = model */ 217 | 8, /* field[8] = num_bytes */ 218 | 4, /* field[4] = trigger_address */ 219 | 5, /* field[5] = trigger_hitcounter */ 220 | 1, /* field[1] = type */ 221 | }; 222 | static const ProtobufCIntRange archie__fault__number_ranges[1 + 1] = 223 | { 224 | { 1, 0 }, 225 | { 0, 9 } 226 | }; 227 | const ProtobufCMessageDescriptor archie__fault__descriptor = 228 | { 229 | PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC, 230 | "archie.Fault", 231 | "Fault", 232 | "Archie__Fault", 233 | "archie", 234 | sizeof(Archie__Fault), 235 | 9, 236 | archie__fault__field_descriptors, 237 | archie__fault__field_indices_by_name, 238 | 1, archie__fault__number_ranges, 239 | (ProtobufCMessageInit) archie__fault__init, 240 | NULL,NULL,NULL /* reserved[123] */ 241 | }; 242 | static const ProtobufCFieldDescriptor archie__fault_pack__field_descriptors[1] = 243 | { 244 | { 245 | "faults", 246 | 1, 247 | PROTOBUF_C_LABEL_REPEATED, 248 | PROTOBUF_C_TYPE_MESSAGE, 249 | offsetof(Archie__FaultPack, n_faults), 250 | offsetof(Archie__FaultPack, faults), 251 | &archie__fault__descriptor, 252 | NULL, 253 | 0, /* flags */ 254 | 0,NULL,NULL /* reserved1,reserved2, etc */ 255 | }, 256 | }; 257 | static const unsigned archie__fault_pack__field_indices_by_name[] = { 258 | 0, /* field[0] = faults */ 259 | }; 260 | static const ProtobufCIntRange archie__fault_pack__number_ranges[1 + 1] = 261 | { 262 | { 1, 0 }, 263 | { 0, 1 } 264 | }; 265 | const ProtobufCMessageDescriptor archie__fault_pack__descriptor = 266 | { 267 | PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC, 268 | "archie.FaultPack", 269 | "FaultPack", 270 | "Archie__FaultPack", 271 | "archie", 272 | sizeof(Archie__FaultPack), 273 | 1, 274 | archie__fault_pack__field_descriptors, 275 | archie__fault_pack__field_indices_by_name, 276 | 1, archie__fault_pack__number_ranges, 277 | (ProtobufCMessageInit) archie__fault_pack__init, 278 | NULL,NULL,NULL /* reserved[123] */ 279 | }; 280 | -------------------------------------------------------------------------------- /faultplugin/protobuf/fault.pb-c.h: -------------------------------------------------------------------------------- 1 | /* Generated by the protocol buffer compiler. DO NOT EDIT! */ 2 | /* Generated from: fault.proto */ 3 | 4 | #ifndef PROTOBUF_C_fault_2eproto__INCLUDED 5 | #define PROTOBUF_C_fault_2eproto__INCLUDED 6 | 7 | #include 8 | 9 | PROTOBUF_C__BEGIN_DECLS 10 | 11 | #if PROTOBUF_C_VERSION_NUMBER < 1003000 12 | # error This file was generated by a newer version of protoc-c which is incompatible with your libprotobuf-c headers. Please update your headers. 13 | #elif 1004001 < PROTOBUF_C_MIN_COMPILER_VERSION 14 | # error This file was generated by an older version of protoc-c which is incompatible with your libprotobuf-c headers. Please regenerate this file with a newer version of protoc-c. 15 | #endif 16 | 17 | 18 | typedef struct Archie__Fault Archie__Fault; 19 | typedef struct Archie__FaultPack Archie__FaultPack; 20 | 21 | 22 | /* --- enums --- */ 23 | 24 | 25 | /* --- messages --- */ 26 | 27 | struct Archie__Fault 28 | { 29 | ProtobufCMessage base; 30 | uint64_t address; 31 | uint64_t type; 32 | uint64_t model; 33 | uint64_t lifespan; 34 | uint64_t trigger_address; 35 | uint64_t trigger_hitcounter; 36 | uint64_t mask_upper; 37 | uint64_t mask_lower; 38 | uint32_t num_bytes; 39 | }; 40 | #define ARCHIE__FAULT__INIT \ 41 | { PROTOBUF_C_MESSAGE_INIT (&archie__fault__descriptor) \ 42 | , 0, 0, 0, 0, 0, 0, 0, 0, 0 } 43 | 44 | 45 | struct Archie__FaultPack 46 | { 47 | ProtobufCMessage base; 48 | size_t n_faults; 49 | Archie__Fault **faults; 50 | }; 51 | #define ARCHIE__FAULT_PACK__INIT \ 52 | { PROTOBUF_C_MESSAGE_INIT (&archie__fault_pack__descriptor) \ 53 | , 0,NULL } 54 | 55 | 56 | /* Archie__Fault methods */ 57 | void archie__fault__init 58 | (Archie__Fault *message); 59 | size_t archie__fault__get_packed_size 60 | (const Archie__Fault *message); 61 | size_t archie__fault__pack 62 | (const Archie__Fault *message, 63 | uint8_t *out); 64 | size_t archie__fault__pack_to_buffer 65 | (const Archie__Fault *message, 66 | ProtobufCBuffer *buffer); 67 | Archie__Fault * 68 | archie__fault__unpack 69 | (ProtobufCAllocator *allocator, 70 | size_t len, 71 | const uint8_t *data); 72 | void archie__fault__free_unpacked 73 | (Archie__Fault *message, 74 | ProtobufCAllocator *allocator); 75 | /* Archie__FaultPack methods */ 76 | void archie__fault_pack__init 77 | (Archie__FaultPack *message); 78 | size_t archie__fault_pack__get_packed_size 79 | (const Archie__FaultPack *message); 80 | size_t archie__fault_pack__pack 81 | (const Archie__FaultPack *message, 82 | uint8_t *out); 83 | size_t archie__fault_pack__pack_to_buffer 84 | (const Archie__FaultPack *message, 85 | ProtobufCBuffer *buffer); 86 | Archie__FaultPack * 87 | archie__fault_pack__unpack 88 | (ProtobufCAllocator *allocator, 89 | size_t len, 90 | const uint8_t *data); 91 | void archie__fault_pack__free_unpacked 92 | (Archie__FaultPack *message, 93 | ProtobufCAllocator *allocator); 94 | /* --- per-message closures --- */ 95 | 96 | typedef void (*Archie__Fault_Closure) 97 | (const Archie__Fault *message, 98 | void *closure_data); 99 | typedef void (*Archie__FaultPack_Closure) 100 | (const Archie__FaultPack *message, 101 | void *closure_data); 102 | 103 | /* --- services --- */ 104 | 105 | 106 | /* --- descriptors --- */ 107 | 108 | extern const ProtobufCMessageDescriptor archie__fault__descriptor; 109 | extern const ProtobufCMessageDescriptor archie__fault_pack__descriptor; 110 | 111 | PROTOBUF_C__END_DECLS 112 | 113 | 114 | #endif /* PROTOBUF_C_fault_2eproto__INCLUDED */ 115 | -------------------------------------------------------------------------------- /faultplugin/registerdump.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Florian Andreas Hauschild 3 | * Copyright (c) 2021 Fraunhofer AISEC 4 | * Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | * This file contains all functions needed to collect register data and send 19 | * it over the data pipe 20 | */ 21 | 22 | #include "registerdump.h" 23 | 24 | 25 | typedef struct registerdump_t registerdump_t; 26 | typedef struct registerdump_t 27 | { 28 | uint64_t pc; 29 | uint64_t tbcount; 30 | registerdump_t *next; 31 | uint64_t regs[]; 32 | } registerdump_t; 33 | 34 | registerdump_t *first_registerdump; 35 | int arch; 36 | 37 | /** 38 | * readout_arm_registers 39 | * 40 | * readout arm registers from QEMU. 41 | * 42 | * @params current the current registerdump_t struct. It fills the regs part 43 | */ 44 | void readout_arm_registers(registerdump_t * current); 45 | 46 | 47 | /** 48 | * readout_riscv_registers 49 | * 50 | * readout riscv registers from QEMU. 51 | * 52 | * @params current the current registerdump_t struct. It fills the regs part 53 | */ 54 | void readout_riscv_registers(registerdump_t * current); 55 | 56 | /** 57 | * read_registers 58 | * 59 | * update protobuf message with register information 60 | * 61 | * returns 0 on success, -1 on fail 62 | */ 63 | int read_registers(Archie__RegisterInfo* protobuf_reg_info); 64 | 65 | void init_register_module(int architecture) 66 | { 67 | first_registerdump = NULL; 68 | arch = architecture; 69 | } 70 | 71 | void delete_register_module(void) 72 | { 73 | registerdump_t* current; 74 | while(first_registerdump != NULL) 75 | { 76 | current = first_registerdump; 77 | first_registerdump = first_registerdump->next; 78 | free(current); 79 | } 80 | } 81 | 82 | int add_new_registerdump(uint64_t tbcount) 83 | { 84 | registerdump_t* current = NULL; 85 | if(arch == ARM) 86 | { 87 | current = malloc(sizeof(registerdump_t) + sizeof(uint64_t[N_ARM_REGISTERS + 1]) ); 88 | if(current == NULL) 89 | { 90 | qemu_plugin_outs("failed\n[ERROR]: Malloc error for registerdump_t"); 91 | return -1; 92 | } 93 | readout_arm_registers(current); 94 | current->pc = current->regs[15]; 95 | } 96 | if(arch == RISCV) 97 | { 98 | current = malloc(sizeof(registerdump_t) + sizeof(uint64_t[N_RISCV_REGISTERS + 1])); 99 | if(current == NULL) 100 | { 101 | qemu_plugin_outs("failed\n[ERROR]: Malloc error for registerdump_t"); 102 | return -1; 103 | } 104 | readout_riscv_registers(current); 105 | current->pc = current->regs[32]; 106 | } 107 | current->next = first_registerdump; 108 | current->tbcount = tbcount; 109 | first_registerdump = current; 110 | return 0; 111 | } 112 | 113 | void readout_riscv_registers(registerdump_t * current) 114 | { 115 | //read all registers (32 is PC) 116 | for(int i = 0; i < N_RISCV_REGISTERS + 1; i++) 117 | { 118 | current->regs[i] = 0; 119 | current->regs[i] = qemu_plugin_read_reg(i); 120 | } 121 | } 122 | 123 | void readout_arm_registers(registerdump_t * current) 124 | { 125 | // read r0 - r15 126 | for(int i = 0; i < N_ARM_REGISTERS; i++) 127 | { 128 | current->regs[i] = 0; 129 | current->regs[i] = qemu_plugin_read_reg(i); 130 | } 131 | // read XPSR 132 | current->regs[16] = qemu_plugin_read_reg(25); 133 | } 134 | 135 | size_t get_register_dump_count(void) 136 | { 137 | size_t size = 0; 138 | registerdump_t* current = first_registerdump; 139 | while(current != NULL) 140 | { 141 | size++; 142 | current = current->next; 143 | } 144 | 145 | return size; 146 | } 147 | 148 | int read_registers(Archie__RegisterInfo* protobuf_reg_info) 149 | { 150 | // Allocate memory for register info on protobuf message 151 | size_t n_register_dumps = get_register_dump_count(); 152 | Archie__RegisterDump** protobuf_reg_dump_list; 153 | protobuf_reg_dump_list = malloc(sizeof(Archie__RegisterDump*) * n_register_dumps); 154 | if(protobuf_reg_dump_list == NULL) 155 | { 156 | qemu_plugin_outs("[ERROR]: Malloc for Archie__RegisterDump array failed\n"); 157 | return -1; 158 | } 159 | 160 | uint64_t n_registers = 0; 161 | if(arch == ARM) 162 | { 163 | qemu_plugin_outs("[DEBUG]: start reading arm registerdumps\n"); 164 | protobuf_reg_info->arch_type = ARM; 165 | 166 | // Extra register carries the XPSR register 167 | n_registers = N_ARM_REGISTERS + 1; 168 | } 169 | else if(arch == RISCV) 170 | { 171 | qemu_plugin_outs("[DEBUG]: start reading riscv registerdumps\n"); 172 | protobuf_reg_info->arch_type = RISCV; 173 | 174 | // Extra register carries the program counter 175 | n_registers = N_RISCV_REGISTERS + 1; 176 | } 177 | else 178 | { 179 | qemu_plugin_outs("[ERROR]: [CRITICAL]: Unknown Architecture for register module"); 180 | return -1; 181 | } 182 | 183 | registerdump_t* current = first_registerdump; 184 | int counter = 0; 185 | while(current != NULL) 186 | { 187 | protobuf_reg_dump_list[counter] = malloc(sizeof(Archie__RegisterDump)); 188 | if(protobuf_reg_dump_list[counter] == NULL) 189 | { 190 | qemu_plugin_outs("[ERROR]: Malloc for Archie__RegisterDump failed\n"); 191 | return -1; 192 | } 193 | 194 | archie__register_dump__init(protobuf_reg_dump_list[counter]); 195 | 196 | // Copy register values into current protobuf register info dump 197 | protobuf_reg_dump_list[counter]->n_register_values = n_registers; 198 | protobuf_reg_dump_list[counter]->register_values = malloc(sizeof(uint64_t) * n_registers); 199 | if(protobuf_reg_dump_list[counter]->register_values == NULL) 200 | { 201 | qemu_plugin_outs("[ERROR]: Malloc failed\n"); 202 | return -1; 203 | } 204 | for(int i = 0; i < n_registers; i++) 205 | { 206 | protobuf_reg_dump_list[counter]->register_values[i] = current->regs[i]; 207 | } 208 | 209 | protobuf_reg_dump_list[counter]->pc = current->pc; 210 | protobuf_reg_dump_list[counter]->tb_count = current->tbcount; 211 | 212 | counter++; 213 | current = current->next; 214 | } 215 | 216 | protobuf_reg_info->n_register_dumps = n_register_dumps; 217 | protobuf_reg_info->register_dumps = protobuf_reg_dump_list; 218 | 219 | return 0; 220 | } 221 | 222 | int read_register_module(Archie__Data* msg) 223 | { 224 | // Allocate and init register info of protobuf data messages 225 | Archie__RegisterInfo* reg = malloc(sizeof(Archie__RegisterInfo)); 226 | if(reg == NULL) 227 | { 228 | qemu_plugin_outs("[ERROR]: Malloc for Archie__RegisterInfo failed\n"); 229 | return -1; 230 | } 231 | archie__register_info__init(reg); 232 | 233 | if(read_registers(reg) != 0) 234 | { 235 | qemu_plugin_outs("[ERROR]: read_registers() failed\n"); 236 | return -1; 237 | } 238 | 239 | msg->register_info = reg; 240 | return 0; 241 | } 242 | -------------------------------------------------------------------------------- /faultplugin/registerdump.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Florian Andreas Hauschild 3 | * Copyright (c) 2021 Fraunhofer AISEC 4 | * Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | * This file contains the header functions for managing register data. 19 | */ 20 | 21 | #ifndef FAULTPLUGIN_REGISTERDUMP_H 22 | #define FAULTPLUGIN_REGISTERDUMP_H 23 | 24 | #include "faultplugin.h" 25 | #include 26 | 27 | #include "protobuf/data.pb-c.h" 28 | 29 | /** 30 | * This enum is the internal value for all available architectures supported 31 | */ 32 | enum architecture {ARM = 0, RISCV = 1}; 33 | 34 | #define N_ARM_REGISTERS 16 35 | #define N_RISCV_REGISTERS 32 36 | 37 | /** 38 | * init_register_module 39 | * 40 | * This function initialises the module. Must be called in setup. 41 | * 42 | * @param architecture is used to select the current simulated hardware. See architecture enum for which value represents what 43 | */ 44 | void init_register_module(int architecture); 45 | 46 | 47 | /** 48 | * delete_register_module 49 | * 50 | * Clear all internal datastructures to free memory. All data is lost, if this function is called to early 51 | */ 52 | void delete_register_module(void); 53 | 54 | /** 55 | * add_new_registerdump 56 | * 57 | * Readout all architecture registers and save value. Then save the value to internal linked list 58 | * 59 | * @param tbcount Value that is saved in the tbcount 60 | * 61 | * @return return negative value, if something went wrong 62 | */ 63 | int add_new_registerdump(uint64_t tbcount); 64 | 65 | 66 | /** 67 | * read_register_module 68 | * 69 | * Readout structs and write them to protobuf message 70 | */ 71 | int read_register_module(Archie__Data* protobuf_msg); 72 | 73 | 74 | #endif 75 | -------------------------------------------------------------------------------- /faultplugin/singlestep.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Florian Andreas Hauschild 3 | * Copyright (c) 2021 Fraunhofer AISEC 4 | * Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | * This file contains the functions for managing singlestep mode inside 19 | * the plugin 20 | */ 21 | 22 | #include "singlestep.h" 23 | 24 | #include 25 | #include 26 | 27 | 28 | volatile uint64_t req_singlestep = 0; 29 | 30 | void init_singlestep_req(void) 31 | { 32 | req_singlestep = 0; 33 | } 34 | 35 | void check_singlestep(void) 36 | { 37 | if(req_singlestep == 0) 38 | { 39 | qemu_plugin_single_step(0); 40 | } 41 | else 42 | { 43 | qemu_plugin_single_step(1); 44 | } 45 | qemu_plugin_flush_tb(); 46 | } 47 | 48 | void add_singlestep_req(void) 49 | { 50 | g_autoptr(GString) out = g_string_new(""); 51 | qemu_plugin_outs("[SINGLESTEP]: increase request\n"); 52 | req_singlestep++; 53 | g_string_printf(out, "[SINGLESTEP]: requests %li\n", req_singlestep); 54 | qemu_plugin_outs(out->str); 55 | check_singlestep(); 56 | } 57 | 58 | void rem_singlestep_req(void) 59 | { 60 | if(req_singlestep != 0) 61 | { 62 | g_autoptr(GString) out = g_string_new(""); 63 | qemu_plugin_outs("[SINGLESTEP]: decrease request\n"); 64 | req_singlestep--; 65 | g_string_printf(out, "[SINGLESTEP]: requests %li\n", req_singlestep); 66 | qemu_plugin_outs(out->str); 67 | check_singlestep(); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /faultplugin/singlestep.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Florian Andreas Hauschild 3 | * Copyright (c) 2021 Fraunhofer AISEC 4 | * Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | * This file contains the headers for managing singlestep mode inside 19 | */ 20 | 21 | #ifndef SINGLESTEP 22 | #define SINGLESTEP 23 | 24 | /** 25 | * init_singlestep_req 26 | * 27 | * Init singlestep module. This will initialise all needed variables 28 | */ 29 | void init_singlestep_req(void); 30 | 31 | /** 32 | * check_singlestep 33 | * 34 | * Check weather singlestepping should be enabled or not. It will disable singlestep if no requests are open. If requests are open it will force qemu into singlestep. 35 | */ 36 | void check_singlestep(void); 37 | 38 | /** 39 | * add_singlestep_req 40 | * 41 | * Increase counter for requested singlesteps. This function should be called, if singlestep should be enabled. It will internally call check_singlestep 42 | */ 43 | void add_singlestep_req(void); 44 | 45 | /** 46 | * rem_singlestep_req 47 | * 48 | * decrease counter for request singlestep. This function should be called, if singlestep should be disabled or is no longer needed. 49 | */ 50 | void rem_singlestep_req(void); 51 | #endif 52 | -------------------------------------------------------------------------------- /faultplugin/tb_exec_data_collection.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Florian Andreas Hauschild 3 | * Copyright (c) 2021 Fraunhofer AISEC 4 | * Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | * This file contains the functions needed to keep track of tb execution order. 19 | */ 20 | 21 | #include "tb_exec_data_collection.h" 22 | #include "faultplugin.h" 23 | #include 24 | #include 25 | 26 | extern int tb_exec_order_ring_buffer; 27 | 28 | tb_exec_order_t *tb_exec_order_list; 29 | 30 | uint64_t num_exec_order; 31 | 32 | struct tb_exec_rb_element { 33 | tb_info_t *tb_info; 34 | uint64_t pos; 35 | }; 36 | 37 | #define TB_EXEC_RB_SIZE 100 38 | struct tb_exec_rb_element *tb_exec_rb_list = NULL; 39 | int tb_exec_rb_list_index; 40 | 41 | int tb_exec_order_init(void) 42 | { 43 | // List of execution order of tbs. 44 | tb_exec_order_list = NULL; 45 | num_exec_order = 0; 46 | 47 | if (tb_exec_order_ring_buffer) 48 | { 49 | tb_exec_rb_list_index = 0; 50 | tb_exec_rb_list = (struct tb_exec_rb_element *)malloc( 51 | TB_EXEC_RB_SIZE * sizeof(struct tb_exec_rb_element)); 52 | if (tb_exec_rb_list == NULL) 53 | { 54 | return -1; 55 | } 56 | } 57 | 58 | return 0; 59 | } 60 | 61 | 62 | /** 63 | * tb_exec_order_free() 64 | * 65 | * Free linked list of tb_exec_order_t elements. It does not free the tb_info_t inside. 66 | * These must be freed separately with tb_info_free() 67 | */ 68 | void tb_exec_order_free(void) 69 | { 70 | tb_exec_order_t *item; 71 | while(tb_exec_order_list != NULL) 72 | { 73 | item = tb_exec_order_list; 74 | tb_exec_order_list = tb_exec_order_list->prev; 75 | free(item); 76 | } 77 | 78 | if (tb_exec_order_ring_buffer && tb_exec_rb_list != NULL) 79 | { 80 | free(tb_exec_rb_list); 81 | } 82 | } 83 | 84 | 85 | /** 86 | * plugin_dump_tb_exec_order 87 | * 88 | * Write the order of translation blocks executed. Also provide a counter number, such that it can be later resorted in python 89 | */ 90 | int plugin_dump_tb_exec_order(Archie__Data* protobuf_msg) 91 | { 92 | uint64_t i = 0; 93 | 94 | Archie__TbExecOrder** msg_tb_exec_order_list; 95 | if (tb_exec_order_ring_buffer) 96 | { 97 | /* 98 | * If we logged less than TB_EXEC_RB_SIZE, the start of the buffer is 99 | * at index 0. Otherwise, it is stored in tb_exec_rb_list_index. 100 | */ 101 | if (num_exec_order >= TB_EXEC_RB_SIZE) 102 | { 103 | i = tb_exec_rb_list_index; 104 | msg_tb_exec_order_list = malloc(sizeof(Archie__TbExecOrder *) * TB_EXEC_RB_SIZE); 105 | if(msg_tb_exec_order_list == NULL) 106 | { 107 | qemu_plugin_outs("[ERROR]: Malloc for Archie__TbExecOrder array failed\n"); 108 | return -1; 109 | } 110 | protobuf_msg->n_tb_exec_orders = TB_EXEC_RB_SIZE; 111 | } 112 | else 113 | { 114 | if(num_exec_order == 0) 115 | { 116 | qemu_plugin_outs("[DEBUG]: num_exec_order is 0\n"); 117 | protobuf_msg->n_tb_exec_orders = 0; 118 | return 0; 119 | } 120 | 121 | msg_tb_exec_order_list = malloc(sizeof(Archie__TbExecOrder*) * num_exec_order); 122 | if(msg_tb_exec_order_list == NULL) 123 | { 124 | qemu_plugin_outs("[ERROR]: Malloc for Archie__TbExecOrder array failed\n"); 125 | return -1; 126 | } 127 | protobuf_msg->n_tb_exec_orders = num_exec_order; 128 | } 129 | for (int j = 0; j < TB_EXEC_RB_SIZE && j < num_exec_order; j++) 130 | { 131 | msg_tb_exec_order_list[j] = malloc(sizeof(Archie__TbExecOrder)); 132 | if(msg_tb_exec_order_list[j] == NULL) 133 | { 134 | qemu_plugin_outs("[ERROR]: Malloc for Archie__TbExecOrder failed\n"); 135 | return -1; 136 | } 137 | archie__tb_exec_order__init(msg_tb_exec_order_list[j]); 138 | 139 | if (tb_exec_rb_list[i].tb_info == NULL) 140 | { 141 | msg_tb_exec_order_list[j]->tb_base_address = 0; 142 | msg_tb_exec_order_list[j]->pos = tb_exec_rb_list[i].pos; 143 | } 144 | else 145 | { 146 | msg_tb_exec_order_list[j]->tb_base_address = tb_exec_rb_list[i].tb_info->base_address; 147 | msg_tb_exec_order_list[j]->pos = tb_exec_rb_list[i].pos; 148 | } 149 | 150 | i++; 151 | if (i == TB_EXEC_RB_SIZE) 152 | { 153 | i = 0; 154 | } 155 | } 156 | } 157 | else 158 | { 159 | tb_exec_order_t *item = tb_exec_order_list; 160 | 161 | if(num_exec_order == 0) 162 | { 163 | qemu_plugin_outs("[DEBUG]: num_exec_order is 0\n"); 164 | protobuf_msg->n_tb_exec_orders = 0; 165 | return 0; 166 | } 167 | 168 | msg_tb_exec_order_list = malloc(sizeof(Archie__TbExecOrder*) * num_exec_order); 169 | if(msg_tb_exec_order_list == NULL) 170 | { 171 | qemu_plugin_outs("[ERROR]: Malloc for Archie__TbExecOrder array failed\n"); 172 | return -1; 173 | } 174 | protobuf_msg->n_tb_exec_orders = num_exec_order; 175 | 176 | while(item->prev != NULL) 177 | { 178 | i++; 179 | item = item->prev; 180 | } 181 | i++; 182 | if(i != num_exec_order) 183 | { 184 | qemu_plugin_outs("[WARNING]: i and numexec differ!\n"); 185 | } 186 | i = 0; 187 | while(item != NULL) 188 | { 189 | msg_tb_exec_order_list[i] = malloc(sizeof(Archie__TbExecOrder)); 190 | if(msg_tb_exec_order_list[i] == NULL) 191 | { 192 | qemu_plugin_outs("[ERROR]: Malloc for Archie__TbExecOrder failed\n"); 193 | return -1; 194 | } 195 | 196 | archie__tb_exec_order__init(msg_tb_exec_order_list[i]); 197 | 198 | if(item->tb_info == NULL) 199 | { 200 | msg_tb_exec_order_list[i]->tb_base_address = 0; 201 | msg_tb_exec_order_list[i]->pos = i; 202 | } 203 | else 204 | { 205 | msg_tb_exec_order_list[i]->tb_base_address = item->tb_info->base_address; 206 | msg_tb_exec_order_list[i]->pos = i; 207 | } 208 | 209 | item = item->next; 210 | i++; 211 | } 212 | } 213 | 214 | protobuf_msg->tb_exec_orders = msg_tb_exec_order_list; 215 | return 0; 216 | } 217 | 218 | /** 219 | * tb_exec_data_event 220 | * 221 | * Function to collect the exec data about translation blocks 222 | * 223 | * vcpu_index: current index of cpu the callback was triggered from 224 | * vcurrent: pointer to tb_info struct of the current tb 225 | */ 226 | void tb_exec_data_event(unsigned int vcpu_index, void *vcurrent) 227 | { 228 | tb_info_t *tb_info = vcurrent; 229 | if(tb_info != NULL) 230 | { 231 | tb_info->num_of_exec++; 232 | } 233 | 234 | if (tb_exec_order_ring_buffer) 235 | { 236 | tb_exec_rb_list[tb_exec_rb_list_index].tb_info = tb_info; 237 | tb_exec_rb_list[tb_exec_rb_list_index].pos = num_exec_order; 238 | 239 | tb_exec_rb_list_index++; 240 | if (tb_exec_rb_list_index == TB_EXEC_RB_SIZE) 241 | { 242 | tb_exec_rb_list_index = 0; 243 | } 244 | } 245 | else 246 | { 247 | tb_exec_order_t *last = malloc(sizeof(tb_exec_order_t)); 248 | last->tb_info = tb_info; 249 | last->next = NULL; 250 | last->prev = tb_exec_order_list; 251 | if(tb_exec_order_list != NULL) 252 | { 253 | tb_exec_order_list->next = last; 254 | } 255 | tb_exec_order_list = last; 256 | } 257 | 258 | num_exec_order++; 259 | } 260 | -------------------------------------------------------------------------------- /faultplugin/tb_exec_data_collection.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Florian Andreas Hauschild 3 | * Copyright (c) 2021 Fraunhofer AISEC 4 | * Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | * This file contains the headers for collection tb execution information 19 | */ 20 | 21 | #ifndef TB_EXEC_DATA_COLLECTION 22 | #define TB_EXEC_DATA_COLLECTION 23 | 24 | 25 | #include 26 | #include "tb_info_data_collection.h" 27 | 28 | typedef struct tb_exec_order_t tb_exec_order_t; 29 | typedef struct tb_exec_order_t 30 | { 31 | tb_info_t *tb_info; 32 | tb_exec_order_t *prev; 33 | tb_exec_order_t *next; 34 | }tb_exec_order_t; 35 | 36 | int tb_exec_order_init(void); 37 | 38 | /** 39 | * tb_exec_order_free() 40 | * 41 | * Free linked list of tb_exec_order_t elements. It does not free the tb_info_t inside. 42 | * These must be freed separately with tb_info_free() 43 | */ 44 | void tb_exec_order_free(void); 45 | 46 | /** 47 | * plugin_dump_tb_exec_order 48 | * 49 | * Print the order of translation blocks executed. Also provide a counter number, such that it can be later resorted in python 50 | */ 51 | int plugin_dump_tb_exec_order(Archie__Data* protobuf_msg); 52 | 53 | /** 54 | * tb_exec_data_event 55 | * 56 | * Function to collect the exec data about translation blocks 57 | * 58 | * vcpu_index: current index of cpu the callback was triggered from 59 | * vcurrent: pointer to tb_info struct of the current tb 60 | */ 61 | void tb_exec_data_event(unsigned int vcpu_index, void *vcurrent); 62 | #endif 63 | -------------------------------------------------------------------------------- /faultplugin/tb_faulted_collection.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Florian Andreas Hauschild 3 | * Copyright (c) 2021 Fraunhofer AISEC 4 | * Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | * This file contains the functions needed to collect faulted instruction assembler. 19 | */ 20 | 21 | #include "singlestep.h" 22 | #include "tb_info_data_collection.h" 23 | #include "tb_faulted_collection.h" 24 | 25 | typedef struct tb_faulted_t tb_faulted_t; 26 | typedef struct tb_faulted_t 27 | { 28 | uint64_t trigger_address; 29 | GString * assembler; 30 | tb_faulted_t *next; 31 | }tb_faulted_t; 32 | 33 | 34 | tb_faulted_t *tb_faulted_list; 35 | 36 | uint64_t *active_triggers; 37 | int max_triggers; 38 | int current_triggers; 39 | int done_triggers; 40 | 41 | 42 | /** 43 | * insert_faulted_assembly 44 | * 45 | * get needed list element and add assembly to it 46 | */ 47 | void insert_faulted_assembly(struct qemu_plugin_tb *tb, uint64_t trigger_address); 48 | 49 | void tb_faulted_init(int number_faults) 50 | { 51 | tb_faulted_list = NULL; 52 | active_triggers = malloc(sizeof(uint64_t)*number_faults); 53 | for(int i = 0; i < number_faults; i++) 54 | { 55 | *(active_triggers + i) = 0xdeadbeef; 56 | } 57 | max_triggers = number_faults; 58 | current_triggers = 0; 59 | done_triggers = 0; 60 | qemu_plugin_outs("[TBFaulted] Init done\n"); 61 | } 62 | 63 | void tb_faulted_free(void) 64 | { 65 | tb_faulted_t *item = NULL; 66 | while(tb_faulted_list != NULL) 67 | { 68 | item = tb_faulted_list; 69 | tb_faulted_list = tb_faulted_list->next; 70 | free(item); 71 | } 72 | if(active_triggers != NULL) 73 | { 74 | free(active_triggers); 75 | } 76 | max_triggers = 0; 77 | current_triggers = 0; 78 | done_triggers = 0; 79 | } 80 | 81 | void insert_faulted_assembly(struct qemu_plugin_tb *tb, uint64_t trigger_address) 82 | { 83 | tb_faulted_t *item = tb_faulted_list; 84 | while(item != NULL) 85 | { 86 | if(item->trigger_address == trigger_address) 87 | { 88 | break; 89 | } 90 | item = item->next; 91 | } 92 | if(item == NULL) 93 | { 94 | g_autoptr(GString) out = g_string_new(""); 95 | g_string_append_printf(out, "[TBFaulted]: %lx\n", trigger_address); 96 | qemu_plugin_outs("[TBFaulted]: Found no fault to be assembled!\n"); 97 | qemu_plugin_outs(out->str); 98 | return; 99 | } 100 | rem_singlestep_req(); 101 | item->assembler = decode_assembler(tb); 102 | } 103 | 104 | void tb_faulted_register(uint64_t fault_address) 105 | { 106 | if(max_triggers == current_triggers) 107 | { 108 | qemu_plugin_outs("[TBFaulted]: Registered tb faulted failed\n"); 109 | return; 110 | } 111 | g_autoptr(GString) out = g_string_new(""); 112 | g_string_printf(out, "[TBFaulted]: %lx\n", fault_address); 113 | qemu_plugin_outs("[TBFaulted]: Registered tb faulted to be saved\n"); 114 | qemu_plugin_outs(out->str); 115 | add_singlestep_req(); 116 | tb_faulted_t *item = malloc(sizeof(tb_faulted_t)); 117 | item->trigger_address = fault_address; 118 | item->assembler = NULL; 119 | item->next = tb_faulted_list; 120 | tb_faulted_list = item; 121 | *(active_triggers + current_triggers ) = fault_address; 122 | current_triggers++; 123 | } 124 | 125 | void check_tb_faulted(struct qemu_plugin_tb *tb) 126 | { 127 | if(done_triggers == current_triggers) 128 | { 129 | return; 130 | } 131 | size_t tb_size = calculate_bytesize_instructions(tb); 132 | for(int i = 0; i < current_triggers; i++) 133 | { 134 | if((*(active_triggers + i) >= tb->vaddr) && (*(active_triggers + i) <= tb->vaddr + tb_size) ) 135 | { 136 | g_autoptr(GString) out = g_string_new(""); 137 | g_string_printf(out, "[TBFaulted]: %lx\n", *(active_triggers + i)); 138 | qemu_plugin_outs("[TBFaulted]: Found tb faulted to be saved\n"); 139 | qemu_plugin_outs(out->str); 140 | insert_faulted_assembly(tb, *(active_triggers + i)); 141 | *(active_triggers + i) = 0xdeadbeaf; 142 | done_triggers++; 143 | } 144 | } 145 | } 146 | 147 | size_t get_tb_faulted_data_count(void) 148 | { 149 | size_t size = 0; 150 | tb_faulted_t *item = tb_faulted_list; 151 | while(item != NULL) 152 | { 153 | if(item->assembler != NULL) 154 | { 155 | size++; 156 | } 157 | item = item->next; 158 | } 159 | 160 | return size; 161 | } 162 | 163 | int dump_tb_faulted_data(Archie__Data* protobuf_msg) 164 | { 165 | if(tb_faulted_list == NULL) 166 | { 167 | qemu_plugin_outs("[TBFaulted]: Found no tb faulted list\n"); 168 | return 0; 169 | } 170 | 171 | // Allocate and init a list of tb_faulted_data on protobuf 172 | size_t size = get_tb_faulted_data_count(); 173 | Archie__FaultedData** faulted_data_list; 174 | faulted_data_list = malloc(sizeof(Archie__FaultedData*) * size); 175 | if(faulted_data_list == NULL) 176 | { 177 | qemu_plugin_outs("[ERROR]: Malloc for Archie__FaultedData failed\n"); 178 | return -1; 179 | } 180 | 181 | int counter = 0; 182 | tb_faulted_t *item = tb_faulted_list; 183 | while(item != NULL) 184 | { 185 | if(item->assembler != NULL) 186 | { 187 | faulted_data_list[counter] = malloc(sizeof(Archie__FaultedData)); 188 | if(faulted_data_list[counter] == NULL) 189 | { 190 | qemu_plugin_outs("[ERROR]: Malloc for Archie__FaultedData failed\n"); 191 | return -1; 192 | } 193 | archie__faulted_data__init(faulted_data_list[counter]); 194 | 195 | faulted_data_list[counter]->trigger_address = item->trigger_address; 196 | faulted_data_list[counter]->assembler = item->assembler->str; 197 | 198 | counter++; 199 | } 200 | item = item->next; 201 | } 202 | 203 | protobuf_msg->n_faulted_datas = size; 204 | protobuf_msg->faulted_datas = faulted_data_list; 205 | return 0; 206 | } 207 | -------------------------------------------------------------------------------- /faultplugin/tb_faulted_collection.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Florian Andreas Hauschild 3 | * Copyright (c) 2021 Fraunhofer AISEC 4 | * Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | * This file contains the functions needed to collect faulted instruction assembler. 19 | */ 20 | 21 | #ifndef TB_FAULTED_COLLECTION_H 22 | #define TB_FAULTED_COLLECTION_H 23 | 24 | #include "singlestep.h" 25 | #include "faultplugin.h" 26 | #include 27 | 28 | /** 29 | * tb_faulte_init 30 | * 31 | * This function initalises the plugin 32 | * 33 | * @param number_faults: Number of faults in the plugin 34 | */ 35 | void tb_faulted_init(int number_faults); 36 | 37 | /** 38 | * tb_faulted_free 39 | * 40 | * Free all allocated memory by this module 41 | */ 42 | void tb_faulted_free(void); 43 | 44 | /** 45 | * tb_faulted_register 46 | * 47 | * Register a callback for getting a faulted assembly 48 | */ 49 | void tb_faulted_register(uint64_t fault_address); 50 | 51 | /** 52 | * check_tb_faulted 53 | * 54 | * Check if a register faulted assembly is available 55 | * 56 | * @param tb: Pointer to tb struct given by qemu. 57 | */ 58 | void check_tb_faulted(struct qemu_plugin_tb *tb); 59 | 60 | /** 61 | * dump_tb_faulted_data 62 | * 63 | * Write collected data protobuf message 64 | */ 65 | int dump_tb_faulted_data(Archie__Data* protobuf_msg); 66 | 67 | #endif 68 | -------------------------------------------------------------------------------- /faultplugin/tb_info_data_collection.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Florian Andreas Hauschild 3 | * Copyright (c) 2021 Fraunhofer AISEC 4 | * Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | * This file contains the functions to manage the collection about tb information 19 | */ 20 | 21 | #include "tb_info_data_collection.h" 22 | #include "faultplugin.h" 23 | 24 | #include 25 | 26 | tb_info_t *tb_info_list; 27 | /* AVL global variables */ 28 | struct avl_table *tb_avl_root; 29 | 30 | void tb_info_init(void) 31 | { 32 | // Linked list of tb structs inside tb. Used to delete them. 33 | tb_info_list = NULL; 34 | tb_avl_root = NULL; 35 | } 36 | 37 | int tb_info_avl_init(void) 38 | { 39 | // AVL tree used in collecting data. This contains the tbs info of all generated tbs. 40 | // The id of a tb is its base address 41 | tb_avl_root = avl_create( &tb_comparison_func, NULL, NULL); 42 | if(tb_avl_root == NULL) 43 | { 44 | return -1; 45 | } 46 | return 0; 47 | } 48 | 49 | 50 | /** 51 | * tb_info_free() 52 | * 53 | * Function to delete the translation block information 54 | * structs from memory. Also deletes the avl tree 55 | */ 56 | void tb_info_free(void) 57 | { 58 | tb_info_t *item; 59 | while(tb_info_list != NULL) 60 | { 61 | item = tb_info_list; 62 | tb_info_list = tb_info_list->next; 63 | free(item); 64 | } 65 | if(tb_avl_root != NULL) 66 | { 67 | avl_destroy( tb_avl_root, NULL); 68 | tb_avl_root = NULL; 69 | } 70 | } 71 | 72 | /** 73 | * tb_comparison_func 74 | * 75 | * Needed for avl library. It will determine which element is larger, of type tb_info_t. 76 | * See documentation of gnuavl lib for more information 77 | * 78 | * tbl_a: Element a to be compared 79 | * tbl_b: Element b to be compared 80 | * tbl_param: Is not used by this avl tree. But can be used to give additional information 81 | * to the comparison function 82 | * 83 | * return if negative, a is larger. If positive, b is larger. If 0, it is the same element. 84 | */ 85 | int tb_comparison_func(const void *tbl_a, const void *tbl_b, void * tbl_param) 86 | { 87 | const tb_info_t * tb_a = tbl_a; 88 | const tb_info_t * tb_b = tbl_b; 89 | if(tb_a->base_address < tb_b->base_address) 90 | { 91 | 92 | return -1; 93 | } 94 | else if(tb_a->base_address > tb_b->base_address) return 1; 95 | else return 0; 96 | } 97 | 98 | size_t get_tb_info_list_size(void) 99 | { 100 | tb_info_t *item = tb_info_list; 101 | size_t size = 0; 102 | while(item != NULL) 103 | { 104 | size++; 105 | item = item->next; 106 | } 107 | 108 | return size; 109 | } 110 | 111 | /** 112 | * plugin_dump_tb_information() 113 | * 114 | * Function that reads the tb information structs and prints each one to the data pipe. Furthermore, writes the command to python, such that it knows tb information is provided 115 | * 116 | * 117 | */ 118 | int plugin_dump_tb_information(Archie__Data* protobuf_msg) 119 | { 120 | // Allocate and init list for protobuf tb information dumps 121 | size_t size = get_tb_info_list_size(); 122 | if(size == 0) 123 | { 124 | qemu_plugin_outs("[DEBUG]: Tb information list is empty\n"); 125 | return 0; 126 | } 127 | 128 | Archie__TbInformation** tb_information_list; 129 | tb_information_list = malloc(sizeof(Archie__TbInformation*) * size); 130 | if(tb_information_list == NULL) 131 | { 132 | qemu_plugin_outs("[ERROR]: Malloc for Archie__TbInformation array failed\n"); 133 | return -1; 134 | } 135 | protobuf_msg->n_tb_informations = size; 136 | 137 | tb_info_t *item = tb_info_list; 138 | int counter = 0; 139 | while(item != NULL) 140 | { 141 | tb_information_list[counter] = malloc(sizeof(Archie__TbInformation)); 142 | if(tb_information_list[counter] == NULL) 143 | { 144 | qemu_plugin_outs("[ERROR]: Malloc for Archie__TbInformation failed\n"); 145 | return -1; 146 | } 147 | 148 | archie__tb_information__init(tb_information_list[counter]); 149 | 150 | tb_information_list[counter]->base_address = item->base_address; 151 | tb_information_list[counter]->size = item->size; 152 | tb_information_list[counter]->instruction_count = item->instruction_count; 153 | tb_information_list[counter]->num_of_exec = item->num_of_exec; 154 | tb_information_list[counter]->assembler = item->assembler->str; 155 | 156 | counter++; 157 | 158 | item = item->next; 159 | } 160 | 161 | protobuf_msg->tb_informations = tb_information_list; 162 | return 0; 163 | } 164 | 165 | tb_info_t * add_tb_info(struct qemu_plugin_tb *tb) 166 | { 167 | g_autoptr(GString) out = g_string_new(""); 168 | g_string_printf(out, "\n"); 169 | tb_info_t tmp; 170 | tmp.base_address = tb->vaddr; 171 | g_string_append_printf(out, "[TB Info]: Search TB......"); 172 | tb_info_t * tb_information = (tb_info_t *) avl_find(tb_avl_root, &tmp); 173 | if(tb_information == NULL) 174 | { 175 | tb_information = malloc(sizeof(tb_info_t)); 176 | if(tb_information == NULL) 177 | { 178 | return NULL; 179 | } 180 | tb_information->base_address = tb->vaddr; 181 | tb_information->instruction_count = tb->n; 182 | tb_information->assembler = decode_assembler(tb); 183 | tb_information->num_of_exec = 0; 184 | tb_information->size = calculate_bytesize_instructions(tb); 185 | tb_information->next = tb_info_list; 186 | tb_info_list = tb_information; 187 | g_string_append(out, "Not Found\n"); 188 | if( avl_insert(tb_avl_root, tb_information) != NULL) 189 | { 190 | qemu_plugin_outs("[ERROR]: Something went wrong in avl insert"); 191 | return NULL; 192 | } 193 | else 194 | { 195 | if(avl_find(tb_avl_root, &tmp) != tb_information) 196 | { 197 | qemu_plugin_outs("[ERROR]: Content changed!"); 198 | return NULL; 199 | } 200 | } 201 | g_string_append(out, "[TB Info]: Done insertion into avl\n"); 202 | } 203 | else 204 | { 205 | g_string_append(out, "Found\n"); 206 | } 207 | qemu_plugin_outs(out->str); 208 | return tb_information; 209 | } 210 | 211 | /** 212 | * decode_assembler() 213 | * 214 | * build string that is later provided to python. !! is the replacement for \n, as this would directly affect decoding. 215 | * 216 | * tb: tb struct, that contains the information needed to get the assembler for the instructions inside the translation block. 217 | * 218 | * return: gstring object, that contains the assembly instructions. The object needs to be deleted by the function that called this function 219 | */ 220 | GString* decode_assembler(struct qemu_plugin_tb *tb) 221 | { 222 | GString* out = g_string_new(""); 223 | 224 | for(int i = 0; i < tb->n; i++) 225 | { 226 | struct qemu_plugin_insn * insn = qemu_plugin_tb_get_insn(tb, i); 227 | g_string_append_printf(out, "[ %8lx ]: %s !!", insn->vaddr, qemu_plugin_insn_disas(insn)); 228 | } 229 | return out; 230 | } 231 | 232 | /* 233 | * calculate_bytesize_instructions 234 | * 235 | * Function to calculate size of TB. It uses the information of the TB and the last insn to determine the byte size of the instructions inside the translation block 236 | */ 237 | size_t calculate_bytesize_instructions(struct qemu_plugin_tb *tb) 238 | { 239 | struct qemu_plugin_insn * insn_first = qemu_plugin_tb_get_insn(tb, 0); 240 | struct qemu_plugin_insn * insn_last = qemu_plugin_tb_get_insn(tb, tb->n -1); 241 | uint64_t size = (insn_last->vaddr - insn_first->vaddr) + insn_last->data->len; 242 | return (size_t) size; 243 | } 244 | -------------------------------------------------------------------------------- /faultplugin/tb_info_data_collection.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Florian Andreas Hauschild 3 | * Copyright (c) 2021 Fraunhofer AISEC 4 | * Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | #ifndef TB_INFO_DATA_COLLECTION 20 | #define TB_INFO_DATA_COLLECTION 21 | 22 | #include "lib/avl.h" 23 | //#include "glib.h" 24 | #include "stdint.h" 25 | #include "qemu/osdep.h" 26 | #include 27 | #include 28 | #include "protobuf/data.pb-c.h" 29 | 30 | /* Output TB data structures */ 31 | typedef struct tb_info_t tb_info_t; 32 | typedef struct tb_info_t 33 | { 34 | uint64_t base_address; 35 | uint64_t size; 36 | uint64_t instruction_count; 37 | GString * assembler; 38 | uint64_t num_of_exec; // Number of executions(aka a counter) 39 | tb_info_t *next; 40 | }tb_info_t; 41 | 42 | /** 43 | * tb_info_init() 44 | * 45 | * This function initialises all global variables used in module 46 | */ 47 | void tb_info_init(void); 48 | 49 | /** 50 | * tb_info_avl_init() 51 | * 52 | * function initialises avl tree for tb info 53 | */ 54 | int tb_info_avl_init(void); 55 | 56 | /** 57 | * tb_info_free() 58 | * 59 | * function to delete the translation block information 60 | * structs from memory. Also deletes the avl tree 61 | */ 62 | void tb_info_free(void); 63 | 64 | /** 65 | * tb_comparison_func 66 | * 67 | * Needed for avl library. it will determine which element is larger, of type tb_info_t. 68 | * See documentation of gnuavl lib for more information 69 | * 70 | * tbl_a: Element a to be compared 71 | * tbl_b: Element b to be compared 72 | * tbl_param: Is not used by this avl tree. But can be used to give additional information 73 | * to the comparison function 74 | * 75 | * return if negative, a is larger. If positive, b is larger. If 0, it is the same element. 76 | */ 77 | int tb_comparison_func(const void *tbl_a, const void *tbl_b, void * tbl_param); 78 | 79 | /** 80 | * plugin_dump_tb_information() 81 | * 82 | * Function that reads the tb information structs and writes them to protobuf message. 83 | * Furthermore writes the command to python, such that it knows tb information is provided 84 | * 85 | * 86 | */ 87 | int plugin_dump_tb_information(Archie__Data* protobuf_msg); 88 | 89 | tb_info_t * add_tb_info(struct qemu_plugin_tb *tb); 90 | 91 | GString* decode_assembler(struct qemu_plugin_tb *tb); 92 | 93 | size_t calculate_bytesize_instructions(struct qemu_plugin_tb *tb); 94 | 95 | #endif 96 | -------------------------------------------------------------------------------- /hdf5-readme.md: -------------------------------------------------------------------------------- 1 | # This Readme documents the structure of the HDF5 output files 2 | 3 | ## Overall structure 4 | 5 | ### Goldenrun 6 | 7 | The Goldenrun represents the program run (defined by "start" and "end", see fault-readme.md) without any injected faults. It therefore serves as a reference to compare the faulted program to. It consists of the following tables: 8 | 9 | * **armregisters:** register content after execution, currently only for ARM registers (future feature: multiple register dumps at multiple points in time) 10 | * **faults:** fault specification according to fault.json file (in this case, no faults) 11 | 12 | * **endpoint:** is 1 if endpoint was reached, 0 if not. Is an attribute of the fault table. Can be used to determine if the guest exited through endpoint or max instruction limit. (accessed through fault.attrs.endpoint) 13 | 14 | * a list of **memdumps**: containing the memory dumps starting at a certain location and of a certain length as defined in fault.json (future feature: multiple memdumps) 15 | * **meminfo**: contains information on the memory accesses. It is not collected by default. It is enabled using the `mem_info` fault configuration property. 16 | 17 | * **address:** address of the memory access (store or load) 18 | * **counter:** number of times the memory access occurred 19 | * **direction:** type of access, i.e., read (0) or write (1) 20 | * **insaddr:** address of the instruction that triggered the memory access 21 | * **size:** number of bytes of the memory access 22 | * **tbid:** identity of translations block, i.e., start address of the respective TB 23 | 24 | * **tbexeclist**: the list of translation blocks in between "start" and "end" (see fault-readme.md). The table contains the position of the translation block in the order of execution and the start address of the translation block. The same TB can be executed multiple times. 25 | * **tbinfo**: content of the translation blocks listed in *tbexeclist* 26 | 27 | * **assembler:** contains the assembler instructions contained in one TB 28 | * **identity:** start address of TB 29 | * **ins_count:** number of instructions in the respective TB 30 | * **num_exec:** number of executions of the respective TB 31 | * **size:** size of TB in bytes 32 | 33 | ### Pregoldenrun 34 | 35 | The Pregoldenrun includes everything before the translations block defined by "start". If "start" is not defined, the Pregoldenrun is empty. 36 | In the Pregoldenrun no faults are injected. For an explanation of the tables in the hdf5 output file see previous section on Goldenrun. 37 | 38 | ### fault 39 | 40 | For each "injected" fault an experiment is created that contains the data associated with the fault and the difference in translation blocks compared with the Goldenrun. 41 | The order of experiments in the json config file is reversed in the hdf5 output file. 42 | Each experiment has the same table structure as the Goldenrun. However, the table tbinfo contains only the differences compared to the Goldenrun. All identical executions are not listed. 43 | 44 | By default, the list of executed translation blocks (`tbexeclist`) is stored in a ring buffer able to store the last 100 entries. This behavior is controlled with the fault configuration property `ring_buffer` and the `--disable-ring-buffer` command line argument, which takes precedence. For the goldenrun, the ring buffer is always disabled. 45 | 46 | ## Analysis 47 | 48 | An exemplary analysis script of the hdf5 output for an AES round skip and differential fault analysis can be found in the folder *analysis*. 49 | 50 | 51 | -------------------------------------------------------------------------------- /protobuf/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fraunhofer-AISEC/archie/41a12dc23a697abd75efec0b11815ef0caf2c00b/protobuf/__init__.py -------------------------------------------------------------------------------- /protobuf/control.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package archie; 4 | 5 | message Control { 6 | int64 max_duration = 1; 7 | int64 num_faults = 2; 8 | bool tb_exec_list = 3; 9 | bool tb_info = 4; 10 | bool mem_info = 5; 11 | uint64 start_address = 6; 12 | uint64 start_counter = 7; 13 | repeated EndPoint end_points = 8; 14 | bool tb_exec_list_ring_buffer = 9; 15 | repeated MemoryDump memorydumps = 10; 16 | bool has_start = 13; 17 | } 18 | 19 | message EndPoint { 20 | uint64 address = 1; 21 | uint64 counter = 2; 22 | } 23 | 24 | message MemoryDump { 25 | uint64 address = 1; 26 | uint64 length = 2; 27 | } 28 | -------------------------------------------------------------------------------- /protobuf/control_pb2.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by the protocol buffer compiler. DO NOT EDIT! 3 | # source: control.proto 4 | """Generated protocol buffer code.""" 5 | from google.protobuf.internal import builder as _builder 6 | from google.protobuf import descriptor as _descriptor 7 | from google.protobuf import descriptor_pool as _descriptor_pool 8 | from google.protobuf import symbol_database as _symbol_database 9 | # @@protoc_insertion_point(imports) 10 | 11 | _sym_db = _symbol_database.Default() 12 | 13 | 14 | 15 | 16 | DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\rcontrol.proto\x12\x06\x61rchie\"\x9e\x02\n\x07\x43ontrol\x12\x14\n\x0cmax_duration\x18\x01 \x01(\x03\x12\x12\n\nnum_faults\x18\x02 \x01(\x03\x12\x14\n\x0ctb_exec_list\x18\x03 \x01(\x08\x12\x0f\n\x07tb_info\x18\x04 \x01(\x08\x12\x10\n\x08mem_info\x18\x05 \x01(\x08\x12\x15\n\rstart_address\x18\x06 \x01(\x04\x12\x15\n\rstart_counter\x18\x07 \x01(\x04\x12$\n\nend_points\x18\x08 \x03(\x0b\x32\x10.archie.EndPoint\x12 \n\x18tb_exec_list_ring_buffer\x18\t \x01(\x08\x12\'\n\x0bmemorydumps\x18\n \x03(\x0b\x32\x12.archie.MemoryDump\x12\x11\n\thas_start\x18\r \x01(\x08\",\n\x08\x45ndPoint\x12\x0f\n\x07\x61\x64\x64ress\x18\x01 \x01(\x04\x12\x0f\n\x07\x63ounter\x18\x02 \x01(\x04\"-\n\nMemoryDump\x12\x0f\n\x07\x61\x64\x64ress\x18\x01 \x01(\x04\x12\x0e\n\x06length\x18\x02 \x01(\x04\x62\x06proto3') 17 | 18 | _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) 19 | _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'control_pb2', globals()) 20 | if _descriptor._USE_C_DESCRIPTORS == False: 21 | 22 | DESCRIPTOR._options = None 23 | _CONTROL._serialized_start=26 24 | _CONTROL._serialized_end=312 25 | _ENDPOINT._serialized_start=314 26 | _ENDPOINT._serialized_end=358 27 | _MEMORYDUMP._serialized_start=360 28 | _MEMORYDUMP._serialized_end=405 29 | # @@protoc_insertion_point(module_scope) 30 | -------------------------------------------------------------------------------- /protobuf/control_pb2.pyi: -------------------------------------------------------------------------------- 1 | from google.protobuf.internal import containers as _containers 2 | from google.protobuf import descriptor as _descriptor 3 | from google.protobuf import message as _message 4 | from typing import ClassVar as _ClassVar, Iterable as _Iterable, Mapping as _Mapping, Optional as _Optional, Union as _Union 5 | 6 | DESCRIPTOR: _descriptor.FileDescriptor 7 | 8 | class Control(_message.Message): 9 | __slots__ = ["end_points", "has_start", "max_duration", "mem_info", "memorydumps", "num_faults", "start_address", "start_counter", "tb_exec_list", "tb_exec_list_ring_buffer", "tb_info"] 10 | END_POINTS_FIELD_NUMBER: _ClassVar[int] 11 | HAS_START_FIELD_NUMBER: _ClassVar[int] 12 | MAX_DURATION_FIELD_NUMBER: _ClassVar[int] 13 | MEMORYDUMPS_FIELD_NUMBER: _ClassVar[int] 14 | MEM_INFO_FIELD_NUMBER: _ClassVar[int] 15 | NUM_FAULTS_FIELD_NUMBER: _ClassVar[int] 16 | START_ADDRESS_FIELD_NUMBER: _ClassVar[int] 17 | START_COUNTER_FIELD_NUMBER: _ClassVar[int] 18 | TB_EXEC_LIST_FIELD_NUMBER: _ClassVar[int] 19 | TB_EXEC_LIST_RING_BUFFER_FIELD_NUMBER: _ClassVar[int] 20 | TB_INFO_FIELD_NUMBER: _ClassVar[int] 21 | end_points: _containers.RepeatedCompositeFieldContainer[EndPoint] 22 | has_start: bool 23 | max_duration: int 24 | mem_info: bool 25 | memorydumps: _containers.RepeatedCompositeFieldContainer[MemoryDump] 26 | num_faults: int 27 | start_address: int 28 | start_counter: int 29 | tb_exec_list: bool 30 | tb_exec_list_ring_buffer: bool 31 | tb_info: bool 32 | def __init__(self, max_duration: _Optional[int] = ..., num_faults: _Optional[int] = ..., tb_exec_list: bool = ..., tb_info: bool = ..., mem_info: bool = ..., start_address: _Optional[int] = ..., start_counter: _Optional[int] = ..., end_points: _Optional[_Iterable[_Union[EndPoint, _Mapping]]] = ..., tb_exec_list_ring_buffer: bool = ..., memorydumps: _Optional[_Iterable[_Union[MemoryDump, _Mapping]]] = ..., has_start: bool = ...) -> None: ... 33 | 34 | class EndPoint(_message.Message): 35 | __slots__ = ["address", "counter"] 36 | ADDRESS_FIELD_NUMBER: _ClassVar[int] 37 | COUNTER_FIELD_NUMBER: _ClassVar[int] 38 | address: int 39 | counter: int 40 | def __init__(self, address: _Optional[int] = ..., counter: _Optional[int] = ...) -> None: ... 41 | 42 | class MemoryDump(_message.Message): 43 | __slots__ = ["address", "length"] 44 | ADDRESS_FIELD_NUMBER: _ClassVar[int] 45 | LENGTH_FIELD_NUMBER: _ClassVar[int] 46 | address: int 47 | length: int 48 | def __init__(self, address: _Optional[int] = ..., length: _Optional[int] = ...) -> None: ... 49 | -------------------------------------------------------------------------------- /protobuf/data.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package archie; 4 | 5 | message Data { 6 | int32 end_point = 1; 7 | string end_reason = 2; 8 | repeated TbInformation tb_informations = 3; 9 | repeated TbExecOrder tb_exec_orders = 4; 10 | repeated MemInfo mem_infos = 5; 11 | RegisterInfo register_info = 6; 12 | repeated FaultedData faulted_datas = 7; 13 | repeated MemDumpInfo mem_dump_infos = 8; 14 | } 15 | 16 | message TbInformation { 17 | uint64 base_address = 1; 18 | uint64 size = 2; 19 | uint64 instruction_count = 3; 20 | uint64 num_of_exec = 4; 21 | string assembler = 5; 22 | } 23 | 24 | message TbExecOrder { 25 | uint64 tb_base_address = 1; 26 | uint64 pos = 2; 27 | } 28 | 29 | message MemInfo { 30 | uint64 ins_address = 1; 31 | uint64 size = 2; 32 | uint64 memmory_address = 3; 33 | uint32 direction = 4; 34 | uint64 counter = 5; 35 | } 36 | 37 | message MemDumpInfo { 38 | uint64 address = 1; 39 | uint64 len = 2; 40 | repeated MemDump dumps = 3; 41 | } 42 | 43 | message MemDump { 44 | bytes mem = 1; 45 | } 46 | 47 | // Type 0 = ARM 48 | // Type 1 = RISCV 49 | message RegisterInfo { 50 | repeated RegisterDump register_dumps = 1; 51 | uint32 arch_type = 2; 52 | } 53 | 54 | message RegisterDump { 55 | repeated uint64 register_values = 1; 56 | uint64 pc = 2; 57 | uint64 tb_count = 3; 58 | } 59 | 60 | message FaultedData { 61 | uint64 trigger_address = 1; 62 | string assembler = 2; 63 | } 64 | -------------------------------------------------------------------------------- /protobuf/data_pb2.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by the protocol buffer compiler. DO NOT EDIT! 3 | # source: data.proto 4 | """Generated protocol buffer code.""" 5 | from google.protobuf.internal import builder as _builder 6 | from google.protobuf import descriptor as _descriptor 7 | from google.protobuf import descriptor_pool as _descriptor_pool 8 | from google.protobuf import symbol_database as _symbol_database 9 | # @@protoc_insertion_point(imports) 10 | 11 | _sym_db = _symbol_database.Default() 12 | 13 | 14 | 15 | 16 | DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\ndata.proto\x12\x06\x61rchie\"\xb4\x02\n\x04\x44\x61ta\x12\x11\n\tend_point\x18\x01 \x01(\x05\x12\x12\n\nend_reason\x18\x02 \x01(\t\x12.\n\x0ftb_informations\x18\x03 \x03(\x0b\x32\x15.archie.TbInformation\x12+\n\x0etb_exec_orders\x18\x04 \x03(\x0b\x32\x13.archie.TbExecOrder\x12\"\n\tmem_infos\x18\x05 \x03(\x0b\x32\x0f.archie.MemInfo\x12+\n\rregister_info\x18\x06 \x01(\x0b\x32\x14.archie.RegisterInfo\x12*\n\rfaulted_datas\x18\x07 \x03(\x0b\x32\x13.archie.FaultedData\x12+\n\x0emem_dump_infos\x18\x08 \x03(\x0b\x32\x13.archie.MemDumpInfo\"v\n\rTbInformation\x12\x14\n\x0c\x62\x61se_address\x18\x01 \x01(\x04\x12\x0c\n\x04size\x18\x02 \x01(\x04\x12\x19\n\x11instruction_count\x18\x03 \x01(\x04\x12\x13\n\x0bnum_of_exec\x18\x04 \x01(\x04\x12\x11\n\tassembler\x18\x05 \x01(\t\"3\n\x0bTbExecOrder\x12\x17\n\x0ftb_base_address\x18\x01 \x01(\x04\x12\x0b\n\x03pos\x18\x02 \x01(\x04\"i\n\x07MemInfo\x12\x13\n\x0bins_address\x18\x01 \x01(\x04\x12\x0c\n\x04size\x18\x02 \x01(\x04\x12\x17\n\x0fmemmory_address\x18\x03 \x01(\x04\x12\x11\n\tdirection\x18\x04 \x01(\r\x12\x0f\n\x07\x63ounter\x18\x05 \x01(\x04\"K\n\x0bMemDumpInfo\x12\x0f\n\x07\x61\x64\x64ress\x18\x01 \x01(\x04\x12\x0b\n\x03len\x18\x02 \x01(\x04\x12\x1e\n\x05\x64umps\x18\x03 \x03(\x0b\x32\x0f.archie.MemDump\"\x16\n\x07MemDump\x12\x0b\n\x03mem\x18\x01 \x01(\x0c\"O\n\x0cRegisterInfo\x12,\n\x0eregister_dumps\x18\x01 \x03(\x0b\x32\x14.archie.RegisterDump\x12\x11\n\tarch_type\x18\x02 \x01(\r\"E\n\x0cRegisterDump\x12\x17\n\x0fregister_values\x18\x01 \x03(\x04\x12\n\n\x02pc\x18\x02 \x01(\x04\x12\x10\n\x08tb_count\x18\x03 \x01(\x04\"9\n\x0b\x46\x61ultedData\x12\x17\n\x0ftrigger_address\x18\x01 \x01(\x04\x12\x11\n\tassembler\x18\x02 \x01(\tb\x06proto3') 17 | 18 | _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) 19 | _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'data_pb2', globals()) 20 | if _descriptor._USE_C_DESCRIPTORS == False: 21 | 22 | DESCRIPTOR._options = None 23 | _DATA._serialized_start=23 24 | _DATA._serialized_end=331 25 | _TBINFORMATION._serialized_start=333 26 | _TBINFORMATION._serialized_end=451 27 | _TBEXECORDER._serialized_start=453 28 | _TBEXECORDER._serialized_end=504 29 | _MEMINFO._serialized_start=506 30 | _MEMINFO._serialized_end=611 31 | _MEMDUMPINFO._serialized_start=613 32 | _MEMDUMPINFO._serialized_end=688 33 | _MEMDUMP._serialized_start=690 34 | _MEMDUMP._serialized_end=712 35 | _REGISTERINFO._serialized_start=714 36 | _REGISTERINFO._serialized_end=793 37 | _REGISTERDUMP._serialized_start=795 38 | _REGISTERDUMP._serialized_end=864 39 | _FAULTEDDATA._serialized_start=866 40 | _FAULTEDDATA._serialized_end=923 41 | # @@protoc_insertion_point(module_scope) 42 | -------------------------------------------------------------------------------- /protobuf/data_pb2.pyi: -------------------------------------------------------------------------------- 1 | from google.protobuf.internal import containers as _containers 2 | from google.protobuf import descriptor as _descriptor 3 | from google.protobuf import message as _message 4 | from typing import ClassVar as _ClassVar, Iterable as _Iterable, Mapping as _Mapping, Optional as _Optional, Union as _Union 5 | 6 | DESCRIPTOR: _descriptor.FileDescriptor 7 | 8 | class Data(_message.Message): 9 | __slots__ = ["end_point", "end_reason", "faulted_datas", "mem_dump_infos", "mem_infos", "register_info", "tb_exec_orders", "tb_informations"] 10 | END_POINT_FIELD_NUMBER: _ClassVar[int] 11 | END_REASON_FIELD_NUMBER: _ClassVar[int] 12 | FAULTED_DATAS_FIELD_NUMBER: _ClassVar[int] 13 | MEM_DUMP_INFOS_FIELD_NUMBER: _ClassVar[int] 14 | MEM_INFOS_FIELD_NUMBER: _ClassVar[int] 15 | REGISTER_INFO_FIELD_NUMBER: _ClassVar[int] 16 | TB_EXEC_ORDERS_FIELD_NUMBER: _ClassVar[int] 17 | TB_INFORMATIONS_FIELD_NUMBER: _ClassVar[int] 18 | end_point: int 19 | end_reason: str 20 | faulted_datas: _containers.RepeatedCompositeFieldContainer[FaultedData] 21 | mem_dump_infos: _containers.RepeatedCompositeFieldContainer[MemDumpInfo] 22 | mem_infos: _containers.RepeatedCompositeFieldContainer[MemInfo] 23 | register_info: RegisterInfo 24 | tb_exec_orders: _containers.RepeatedCompositeFieldContainer[TbExecOrder] 25 | tb_informations: _containers.RepeatedCompositeFieldContainer[TbInformation] 26 | def __init__(self, end_point: _Optional[int] = ..., end_reason: _Optional[str] = ..., tb_informations: _Optional[_Iterable[_Union[TbInformation, _Mapping]]] = ..., tb_exec_orders: _Optional[_Iterable[_Union[TbExecOrder, _Mapping]]] = ..., mem_infos: _Optional[_Iterable[_Union[MemInfo, _Mapping]]] = ..., register_info: _Optional[_Union[RegisterInfo, _Mapping]] = ..., faulted_datas: _Optional[_Iterable[_Union[FaultedData, _Mapping]]] = ..., mem_dump_infos: _Optional[_Iterable[_Union[MemDumpInfo, _Mapping]]] = ...) -> None: ... 27 | 28 | class FaultedData(_message.Message): 29 | __slots__ = ["assembler", "trigger_address"] 30 | ASSEMBLER_FIELD_NUMBER: _ClassVar[int] 31 | TRIGGER_ADDRESS_FIELD_NUMBER: _ClassVar[int] 32 | assembler: str 33 | trigger_address: int 34 | def __init__(self, trigger_address: _Optional[int] = ..., assembler: _Optional[str] = ...) -> None: ... 35 | 36 | class MemDump(_message.Message): 37 | __slots__ = ["mem"] 38 | MEM_FIELD_NUMBER: _ClassVar[int] 39 | mem: bytes 40 | def __init__(self, mem: _Optional[bytes] = ...) -> None: ... 41 | 42 | class MemDumpInfo(_message.Message): 43 | __slots__ = ["address", "dumps", "len"] 44 | ADDRESS_FIELD_NUMBER: _ClassVar[int] 45 | DUMPS_FIELD_NUMBER: _ClassVar[int] 46 | LEN_FIELD_NUMBER: _ClassVar[int] 47 | address: int 48 | dumps: _containers.RepeatedCompositeFieldContainer[MemDump] 49 | len: int 50 | def __init__(self, address: _Optional[int] = ..., len: _Optional[int] = ..., dumps: _Optional[_Iterable[_Union[MemDump, _Mapping]]] = ...) -> None: ... 51 | 52 | class MemInfo(_message.Message): 53 | __slots__ = ["counter", "direction", "ins_address", "memmory_address", "size"] 54 | COUNTER_FIELD_NUMBER: _ClassVar[int] 55 | DIRECTION_FIELD_NUMBER: _ClassVar[int] 56 | INS_ADDRESS_FIELD_NUMBER: _ClassVar[int] 57 | MEMMORY_ADDRESS_FIELD_NUMBER: _ClassVar[int] 58 | SIZE_FIELD_NUMBER: _ClassVar[int] 59 | counter: int 60 | direction: int 61 | ins_address: int 62 | memmory_address: int 63 | size: int 64 | def __init__(self, ins_address: _Optional[int] = ..., size: _Optional[int] = ..., memmory_address: _Optional[int] = ..., direction: _Optional[int] = ..., counter: _Optional[int] = ...) -> None: ... 65 | 66 | class RegisterDump(_message.Message): 67 | __slots__ = ["pc", "register_values", "tb_count"] 68 | PC_FIELD_NUMBER: _ClassVar[int] 69 | REGISTER_VALUES_FIELD_NUMBER: _ClassVar[int] 70 | TB_COUNT_FIELD_NUMBER: _ClassVar[int] 71 | pc: int 72 | register_values: _containers.RepeatedScalarFieldContainer[int] 73 | tb_count: int 74 | def __init__(self, register_values: _Optional[_Iterable[int]] = ..., pc: _Optional[int] = ..., tb_count: _Optional[int] = ...) -> None: ... 75 | 76 | class RegisterInfo(_message.Message): 77 | __slots__ = ["arch_type", "register_dumps"] 78 | ARCH_TYPE_FIELD_NUMBER: _ClassVar[int] 79 | REGISTER_DUMPS_FIELD_NUMBER: _ClassVar[int] 80 | arch_type: int 81 | register_dumps: _containers.RepeatedCompositeFieldContainer[RegisterDump] 82 | def __init__(self, register_dumps: _Optional[_Iterable[_Union[RegisterDump, _Mapping]]] = ..., arch_type: _Optional[int] = ...) -> None: ... 83 | 84 | class TbExecOrder(_message.Message): 85 | __slots__ = ["pos", "tb_base_address"] 86 | POS_FIELD_NUMBER: _ClassVar[int] 87 | TB_BASE_ADDRESS_FIELD_NUMBER: _ClassVar[int] 88 | pos: int 89 | tb_base_address: int 90 | def __init__(self, tb_base_address: _Optional[int] = ..., pos: _Optional[int] = ...) -> None: ... 91 | 92 | class TbInformation(_message.Message): 93 | __slots__ = ["assembler", "base_address", "instruction_count", "num_of_exec", "size"] 94 | ASSEMBLER_FIELD_NUMBER: _ClassVar[int] 95 | BASE_ADDRESS_FIELD_NUMBER: _ClassVar[int] 96 | INSTRUCTION_COUNT_FIELD_NUMBER: _ClassVar[int] 97 | NUM_OF_EXEC_FIELD_NUMBER: _ClassVar[int] 98 | SIZE_FIELD_NUMBER: _ClassVar[int] 99 | assembler: str 100 | base_address: int 101 | instruction_count: int 102 | num_of_exec: int 103 | size: int 104 | def __init__(self, base_address: _Optional[int] = ..., size: _Optional[int] = ..., instruction_count: _Optional[int] = ..., num_of_exec: _Optional[int] = ..., assembler: _Optional[str] = ...) -> None: ... 105 | -------------------------------------------------------------------------------- /protobuf/fault.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package archie; 4 | 5 | message Fault { 6 | uint64 address = 1; 7 | uint64 type = 2; 8 | uint64 model = 3; 9 | uint64 lifespan = 4; 10 | uint64 trigger_address = 5; 11 | uint64 trigger_hitcounter = 6; 12 | uint64 mask_upper = 7; 13 | uint64 mask_lower = 8; 14 | uint32 num_bytes = 9; 15 | } 16 | 17 | message FaultPack { 18 | repeated Fault faults = 1; 19 | } 20 | 21 | -------------------------------------------------------------------------------- /protobuf/fault_pb2.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by the protocol buffer compiler. DO NOT EDIT! 3 | # source: fault.proto 4 | """Generated protocol buffer code.""" 5 | from google.protobuf.internal import builder as _builder 6 | from google.protobuf import descriptor as _descriptor 7 | from google.protobuf import descriptor_pool as _descriptor_pool 8 | from google.protobuf import symbol_database as _symbol_database 9 | # @@protoc_insertion_point(imports) 10 | 11 | _sym_db = _symbol_database.Default() 12 | 13 | 14 | 15 | 16 | DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x0b\x66\x61ult.proto\x12\x06\x61rchie\"\xb7\x01\n\x05\x46\x61ult\x12\x0f\n\x07\x61\x64\x64ress\x18\x01 \x01(\x04\x12\x0c\n\x04type\x18\x02 \x01(\x04\x12\r\n\x05model\x18\x03 \x01(\x04\x12\x10\n\x08lifespan\x18\x04 \x01(\x04\x12\x17\n\x0ftrigger_address\x18\x05 \x01(\x04\x12\x1a\n\x12trigger_hitcounter\x18\x06 \x01(\x04\x12\x12\n\nmask_upper\x18\x07 \x01(\x04\x12\x12\n\nmask_lower\x18\x08 \x01(\x04\x12\x11\n\tnum_bytes\x18\t \x01(\r\"*\n\tFaultPack\x12\x1d\n\x06\x66\x61ults\x18\x01 \x03(\x0b\x32\r.archie.Faultb\x06proto3') 17 | 18 | _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) 19 | _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'fault_pb2', globals()) 20 | if _descriptor._USE_C_DESCRIPTORS == False: 21 | 22 | DESCRIPTOR._options = None 23 | _FAULT._serialized_start=24 24 | _FAULT._serialized_end=207 25 | _FAULTPACK._serialized_start=209 26 | _FAULTPACK._serialized_end=251 27 | # @@protoc_insertion_point(module_scope) 28 | -------------------------------------------------------------------------------- /protobuf/fault_pb2.pyi: -------------------------------------------------------------------------------- 1 | from google.protobuf.internal import containers as _containers 2 | from google.protobuf import descriptor as _descriptor 3 | from google.protobuf import message as _message 4 | from typing import ClassVar as _ClassVar, Iterable as _Iterable, Mapping as _Mapping, Optional as _Optional, Union as _Union 5 | 6 | DESCRIPTOR: _descriptor.FileDescriptor 7 | 8 | class Fault(_message.Message): 9 | __slots__ = ["address", "lifespan", "mask_lower", "mask_upper", "model", "num_bytes", "trigger_address", "trigger_hitcounter", "type"] 10 | ADDRESS_FIELD_NUMBER: _ClassVar[int] 11 | LIFESPAN_FIELD_NUMBER: _ClassVar[int] 12 | MASK_LOWER_FIELD_NUMBER: _ClassVar[int] 13 | MASK_UPPER_FIELD_NUMBER: _ClassVar[int] 14 | MODEL_FIELD_NUMBER: _ClassVar[int] 15 | NUM_BYTES_FIELD_NUMBER: _ClassVar[int] 16 | TRIGGER_ADDRESS_FIELD_NUMBER: _ClassVar[int] 17 | TRIGGER_HITCOUNTER_FIELD_NUMBER: _ClassVar[int] 18 | TYPE_FIELD_NUMBER: _ClassVar[int] 19 | address: int 20 | lifespan: int 21 | mask_lower: int 22 | mask_upper: int 23 | model: int 24 | num_bytes: int 25 | trigger_address: int 26 | trigger_hitcounter: int 27 | type: int 28 | def __init__(self, address: _Optional[int] = ..., type: _Optional[int] = ..., model: _Optional[int] = ..., lifespan: _Optional[int] = ..., trigger_address: _Optional[int] = ..., trigger_hitcounter: _Optional[int] = ..., mask_upper: _Optional[int] = ..., mask_lower: _Optional[int] = ..., num_bytes: _Optional[int] = ...) -> None: ... 29 | 30 | class FaultPack(_message.Message): 31 | __slots__ = ["faults"] 32 | FAULTS_FIELD_NUMBER: _ClassVar[int] 33 | faults: _containers.RepeatedCompositeFieldContainer[Fault] 34 | def __init__(self, faults: _Optional[_Iterable[_Union[Fault, _Mapping]]] = ...) -> None: ... 35 | -------------------------------------------------------------------------------- /protobuf/generate_protobuf.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | echo "Generating protobuf files for faultplugin" 4 | 5 | protoc --c_out=../faultplugin/protobuf ./control.proto 6 | protoc --c_out=../faultplugin/protobuf ./fault.proto 7 | protoc --c_out=../faultplugin/protobuf ./data.proto 8 | 9 | echo "Generating protobuf files for python controller" 10 | 11 | # If installed protobuf compiler supports it 12 | # create pyi files 13 | if protoc --python_out=./ --pyi_out=./ ./fault.proto; then 14 | protoc --python_out=./ --pyi_out=./ ./control.proto 15 | protoc --python_out=./ --pyi_out=./ ./data.proto 16 | else 17 | echo "Protobuf compiler does not support pyi files" 18 | echo "Generating protobuf files without pyi files" 19 | protoc --python_out=./ ./fault.proto 20 | protoc --python_out=./ ./control.proto 21 | protoc --python_out=./ ./data.proto 22 | fi 23 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | pandas==2.1.4 2 | python-prctl==1.8.1 3 | tables==3.9.2 4 | json5==0.9.10 5 | protobuf==4.21.12 6 | tqdm==4.65.0 7 | psutil==5.9.8 8 | -------------------------------------------------------------------------------- /util.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2021 Florian Andreas Hauschild 2 | # Copyright (c) 2021 Fraunhofer AISEC 3 | # Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | import resource 18 | 19 | 20 | def gather_process_ram_usage(queue_ram_usage, max_ram_usage): 21 | process_ram_usage = resource.getrusage(resource.RUSAGE_SELF).ru_maxrss 22 | 23 | if queue_ram_usage is not None: 24 | queue_ram_usage.put(process_ram_usage) 25 | 26 | if process_ram_usage > max_ram_usage: 27 | max_ram_usage = process_ram_usage 28 | 29 | return max_ram_usage 30 | --------------------------------------------------------------------------------