diff --git a/.github/builder/Dockerfile b/.github/builder/Dockerfile index fd6a3e9a..bbdef3cd 100644 --- a/.github/builder/Dockerfile +++ b/.github/builder/Dockerfile @@ -8,7 +8,7 @@ RUN yum -y update && \ coreutils \ gcc \ gcc-c++ \ - make \ + make && \ yum clean all COPY .github/builder/rsrc/rustup-init /install/rustup-init diff --git a/.github/workflows/scans.yml b/.github/workflows/scans.yml index b33c867f..4676dbaf 100644 --- a/.github/workflows/scans.yml +++ b/.github/workflows/scans.yml @@ -44,36 +44,46 @@ jobs: /action/lib/linter.sh || ( echo "❗ [CT222] Super linter found an issue (possibly Hadolint)" && exit 1 ) echo "✅ [CT222] Hadolint Dockerfile check passed" - scan_containers: + scan_x86_64_breakpoint_uefi_edk2_container: runs-on: ubuntu-latest - strategy: - matrix: - dockerfile: - # NOTE: These containers exceed the GitHub size limit and must be scanned manually - # - Dockerfile - # - examples/manual-example/Dockerfile - # - modules/tsffs/tests/targets/minimal-riscv-64/Dockerfile - # - modules/tsffs/tests/targets/minimal-riscv-64-edk2/Dockerfile - - tests/rsrc/x86_64-breakpoint-uefi-edk2/Dockerfile - - tests/rsrc/x86_64-timeout-uefi-edk2/Dockerfile - - tests/rsrc/x86_64-uefi-edk2/Dockerfile - include: - # NOTE: These containers exceed the GitHub size limit and must be scanned manually - # - dockerfile: Dockerfile - # context: . - # - dockerfile: examples/manual-example/Dockerfile - # context: . - # - dockerfile: modules/tsffs/tests/targets/minimal-riscv-64/Dockerfile - # context: modules/tsffs/tests/targets/minimal-riscv-64/ - # - dockerfile: modules/tsffs/tests/targets/minimal-riscv-64-edk2/Dockerfile - # context: modules/tsffs/tests/targets/minimal-riscv-64-edk2/ - - dockerfile: tests/rsrc/x86_64-breakpoint-uefi-edk2/Dockerfile - context: tests/rsrc/x86_64-breakpoint-uefi-edk2/ - - dockerfile: tests/rsrc/x86_64-timeout-uefi-edk2/Dockerfile - context: tests/rsrc/x86_64-timeout-uefi-edk2/ - - dockerfile: tests/rsrc/x86_64-uefi-edk2/Dockerfile - context: tests/rsrc/x86_64-uefi-edk2/ + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + lfs: false + + - name: Build Image + run: | + cd tests/rsrc/x86_64-breakpoint-uefi-edk2/ + cp "../../../harness/tsffs.h" "src/tsffs.h" + docker buildx build -t container -f Dockerfile . > build.log 2>&1 || { tail -n 1000 build.log; exit 1; } + - name: Run Trivy vulnerability scanner + uses: aquasecurity/trivy-action@master + with: + image-ref: container + + scan_x86_64_timeout_uefi_edk2_container: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + lfs: false + + - name: Build Image + run: | + cd tests/rsrc/x86_64-timeout-uefi-edk2/ + cp "../../../harness/tsffs.h" "src/tsffs.h" + docker buildx build -t container -f Dockerfile . > build.log 2>&1 || { tail -n 1000 build.log; exit 1; } + + - name: Run Trivy vulnerability scanner + uses: aquasecurity/trivy-action@master + with: + image-ref: container + + scan_x86_64_uefi_edk2_container: + runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 with: @@ -82,7 +92,9 @@ jobs: - name: Build Image run: | - docker build -t container -f ${{ matrix.dockerfile }} ${{ matrix.context }} + cd tests/rsrc/x86_64-uefi-edk2/ + cp "../../../harness/tsffs.h" "src/tsffs.h" + docker buildx build -t container -f Dockerfile . > build.log 2>&1 || { tail -n 1000 build.log; exit 1; } - name: Run Trivy vulnerability scanner uses: aquasecurity/trivy-action@master diff --git a/Cargo.toml b/Cargo.toml index 56964ecf..8d17d5f8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,8 +21,8 @@ version = "0.2.1" [package.metadata.simics] package-number = 31337 -# version = "6.0.pre4" -version = "6.0.5" + +version = "6.0.6" [lib] crate-type = ["cdylib", "rlib"] @@ -71,6 +71,8 @@ serde = { version = "1.0.197", features = ["derive"] } serde_json = "1.0.114" versions = { version = "6.1.0", features = ["serde"] } ffi = "0.1.1" +num-traits = "0.2.18" +num-derive = "0.4.2" tracing-subscriber = "0.3.18" tracing = { version = "0.1.40", features = ["log"] } diff --git a/Dockerfile b/Dockerfile index 972685de..496647e7 100644 --- a/Dockerfile +++ b/Dockerfile @@ -119,7 +119,7 @@ RUN ispm projects /workspace/projects/example/ --create \ cp /workspace/tsffs/examples/docker-example/fuzz.simics /workspace/projects/example/ && \ cp /workspace/tsffs/tests/rsrc/minimal_boot_disk.craff /workspace/projects/example/ && \ cp /workspace/tsffs/tests/rsrc/x86_64-uefi/* /workspace/projects/example/ && \ - cp /workspace/tsffs/harness/tsffs-gcc-x86_64.h /workspace/projects/example/ && \ + cp /workspace/tsffs/harness/tsffs.h /workspace/projects/example/ && \ ninja RUN echo 'echo "To run the demo, run ./simics -no-gui --no-win fuzz.simics"' >> /root/.bashrc diff --git a/docs/src/config/common-options.md b/docs/src/config/common-options.md index 9ae2fd7f..7e68074b 100644 --- a/docs/src/config/common-options.md +++ b/docs/src/config/common-options.md @@ -12,11 +12,16 @@ is desired. - [Using Snapshots](#using-snapshots) - [Using CMPLog](#using-cmplog) - [Set Corpus and Solutions Directory](#set-corpus-and-solutions-directory) + - [Enable and Set the Checkpoint Path](#enable-and-set-the-checkpoint-path) - [Enable Random Corpus Generation](#enable-random-corpus-generation) - [Set an Iteration Limit](#set-an-iteration-limit) - [Adding Tokens From Target Software](#adding-tokens-from-target-software) - [Setting an Architecture Hint](#setting-an-architecture-hint) - [Adding a Trace Processor](#adding-a-trace-processor) + - [Disabling Coverage Reporting](#disabling-coverage-reporting) + - [Enable Logging and Set Log path](#enable-logging-and-set-log-path) + - [Keep All Corpus Entries](#keep-all-corpus-entries) + - [Use Initial Buffer Contents As Corpus](#use-initial-buffer-contents-as-corpus) ## Solution Configuration @@ -36,6 +41,18 @@ Note that this timeout is in virtual time, not real time. This means that whethe simulation runs faster or slower than real time, the timeout will be accurate to the target software's execution speed. +The fuzzing executor also has a timeout, which runs in real time. This timeout +is intended to detect situations where the fuzzer reaches a broken state where +it is no longer able to iterate (e.g. the virtual time timeout is not working) +and stop. By default, this timeout is set to 60 seconds and resets each +iteration. Only iterations which take more than 60 seconds will trigger the +timeout, but some very large fuzzing cases could exceed this time. To increase +it, for example to set the timeout to 10 minutes: + +```python +@tsffs.executor_timeout = 600 +``` + ### Setting Exception Solutions The primary way TSFFS detects bugs is via CPU exceptions that are raised, but should not @@ -163,7 +180,25 @@ changed with: ```python -tsffs.solutions_directory = SIM_lookup_file("%simics%/other_solutions_directory") +@tsffs.solutions_directory = SIM_lookup_file("%simics%/other_solutions_directory") +``` + +### Enable and Set the Checkpoint Path + +The fuzzer captures an on-disk checkpoint before starting fuzzing by default. On Simics +7 and higher, this increases the snapshot restore speed very significantly, so it should +only be disabled if required. + +To disable this behavior, you can set: + +```python +@tsffs.pre_snapshot_checkpoint = False +``` + +To set the path for the checkpoint, you can set: + +```python +@tsffs.checkpoint_path = SIM_lookup_file("%simics%") + "/checkpoint.ckpt" ``` ### Enable Random Corpus Generation @@ -182,6 +217,14 @@ This can be enabled with: @tsffs.generate_random_corpus = True ``` +The size of the initial random corpus can be set via (note, larger random corpuses are +generally not useful and a real corpus matching the expected data format should be used +instead!): + +```python +@tsffs.initial_random_corpus_size = 64 +``` + ### Set an Iteration Limit The fuzzer can be set to execute only a specific number of iterations before exiting. @@ -249,7 +292,7 @@ running `i386` code in backward-compatibility mode. An architecture hint can be set with: ```python -@tsffs.iface.tsffs.add_architecture_hint(qsp.mb.cpu0.core[0][0], "i386") +@tsffs.iface.config.add_architecture_hint(qsp.mb.cpu0.core[0][0], "i386") ``` ### Adding a Trace Processor @@ -259,5 +302,53 @@ to the [manual start API](../harnessing/closed-box.md) is traced during executio code running on multiple cores, the additional cores can be added with: ```python -@tsffs.iface.tsffs.add_trace_processor(qsp.mb.cpu0.core[0][1]) -``` \ No newline at end of file +@tsffs.iface.config.add_trace_processor(qsp.mb.cpu0.core[0][1]) +``` + +### Disabling Coverage Reporting + +By default, the fuzzer will report new interesting control flow edges. This is +normally useful to check the fuzzer's progress and ensure it is finding new +paths. However in some cases, output may not be needed, so coverage reporting +can be disabled with: + +```python +@tsffs.coverage_reporting = False +``` + +### Enable Logging and Set Log path + +By default, the fuzzer will log useful informational messages in JSON format to +a log in the project directory (`log.json`). + +The path for this log can be set by setting: + +```python +@tsffs.log_path = SIM_lookup_file("%simics%) + "/log.json" +``` + +You can also disable the logging completely with: + +```python +@tsffs.log_to_file = False +``` + +### Keep All Corpus Entries + +For debugging purposes, TSFFS can be set to keep *all* corpus entries, not just +corpus entries which cause interesting results. This generates a large number +of corpus files. + +```python +@tsffs.keep_all_corpus = True +``` + +### Use Initial Buffer Contents As Corpus + +When using compiled-in or manual harnessing, the initial contents of the +testcase +buffer can be used as a seed corpus entry. This can be enabled with: + +```python +@tsffs.use_initial_as_corpus = True +``` diff --git a/docs/src/developer/debugging.md b/docs/src/developer/debugging.md index e04316ec..e7009490 100644 --- a/docs/src/developer/debugging.md +++ b/docs/src/developer/debugging.md @@ -6,9 +6,6 @@ The easiest way to do this is by loading and using it in a script that does what want. For example, early in development there was a bug when calling the interface API. -```txt -@tsffs.iface.tsffs.set_corpus_directory("%simics%/corpus") -``` So this script was used to help debug: @@ -19,7 +16,7 @@ tsffs.log-level 4 @import time @print("Sleeping") @time.sleep(30) -@tsffs.iface.tsffs.set_corpus_directory("%simics%/corpus") +# Call your API here ``` ALl this script does is sleep for 30 seconds, then call the API we care about. The 30 diff --git a/docs/src/harnessing/closed-box.md b/docs/src/harnessing/closed-box.md index 72bfabca..63f5c054 100644 --- a/docs/src/harnessing/closed-box.md +++ b/docs/src/harnessing/closed-box.md @@ -14,7 +14,7 @@ The same code as before, with no harness: ```c -#include "tsffs-gcc-x86_64.h" +#include "tsffs.h" int main() { char buf[20]; diff --git a/docs/src/harnessing/compiled-in.md b/docs/src/harnessing/compiled-in.md index 42d3faf4..8582c2ce 100644 --- a/docs/src/harnessing/compiled-in.md +++ b/docs/src/harnessing/compiled-in.md @@ -3,6 +3,7 @@ - [Compiled-In Harnessing](#compiled-in-harnessing) - [Using Provided Headers](#using-provided-headers) - [Multiple Harnesses in One Binary](#multiple-harnesses-in-one-binary) + - [Alternative Start Harnesses](#alternative-start-harnesses) - [Troubleshooting](#troubleshooting) - [Compile Errors About Temporaries](#compile-errors-about-temporaries) @@ -10,11 +11,12 @@ The TSFFS project provides harnessing headers for each supported combination of architecture and build toolchain. These headers can be found in the `harness` directory -in the repository. +in the repository. There is also a monolithic header `tsffs.h` which conditionally +compiles to whichever architecture is in use and can be used on any supported +architecture and platform. Each header provides the macros `HARNESS_START` and `HARNESS_STOP`. - `HARNESS_START(testcase_ptr, size_ptr)` takes two arguments, a buffer for the fuzzer to write testcases into each fuzzing iteration and a pointer to a pointer-sized variable, which the fuzzer will write the size of each testcase to each fuzzing iteration. The @@ -26,7 +28,7 @@ testcase, typically the size of the buffer passed as the first argument. For example, the following code will invoke the start and stop harnesses correctly: ```c -#include "tsffs-gcc-x86_64.h" +#include "tsffs.h" int main() { char buffer[20]; @@ -63,45 +65,41 @@ necessary. However, the defaults are equivalent to the configuration: ```python @tsffs.start_on_harness = True @tsffs.stop_on_harness = True -@tsffs.magic_start = 1 -@tsffs.magic_stop = 2 -@tsffs.magic_assert = 3 +@tsffs.magic_start_index = 0 +@tsffs.magic_stop_indices = [0] +@tsffs.magic_assert_indices = [0] ``` -This sets TSFFS to start the fuzzing loop on a *magic* harness with magic number `1` -(used by `HARNESS_START`) and stop execution and restore to the initial snapshot on -*magic* harnesses with magic number `2` (used by `HARNESS_STOP`). +This sets TSFFS to start the fuzzing loop on a *magic* +harness with magic number `1` (used by `HARNESS_START`) +and index `0` (the default) and stop execution and +restore to the initial snapshot on *magic* harnesses +with magic number `2` (used by `HARNESS_STOP`) and +index `0` (the default). ## Multiple Harnesses in One Binary -If multiple fuzzing campaigns will be run on the same target software, it is sometimes -advantageous to compile multiple harnesses into the same target software ahead of time, -and choose which to enable at runtime.Each provided header also provides two lower-level -macros for this purpose. - -* `__arch_harness_start(start, testcase_ptr, size_ptr)` -* `__arch_harness_stop(stop)` +If multiple fuzzing campaigns will be run on the same target software, it is +sometimes advantageous to compile multiple harnesses into the same target +software ahead of time, and choose which to enable at runtime.Each provided +header also provides two lower-level macros for this purpose. -These macros are used in the same way as `HARNESS_START` and `HARNESS_STOP`, with the -additional first argument. The default value of `start` is 1, and the default value of -`stop` is 2, but TSFFS can be configured to treat a different value as the trigger to -start or stop the fuzzing loop. Note that `start` and `stop` must be at least 1 and at -most 11, so it is possible to create a target software with up to 10 different harnesses -(by using magic values `1`, `3-11` as start values and `2` as the stop value). This is a -limitation of the instructions SIMICS understands as *magic*, some of which only support -an immediate `0<=n<=12` (with magic numbers 0 and 12 *being reserved by SIMICS). +* `HARNESS_START_INDEX(index, testcase_ptr, size_ptr)` +* `HARNESS_STOP(index)` -For convenience, definitions are provided for all the alternative magic numbers -available, through the definitions `MAGIC_ALT_0` through `MAGIC_ALT_7`. +These macros are used in the same way as `HARNESS_START` and `HARNESS_STOP`, +with the additional first argument. The default value of `index` is 0, but +TSFFS can be configured to treat a different index as the trigger to start or +stop the fuzzing loop. ```c -#include "tsffs-gcc-x86_64.h" +#include "tsffs.h" int main() { char buf[20]; size_t size = sizeof(buf); - __arch_harness_start(MAGIC_START, buf, &size); + HARNESS_START(buf, &size); if (size < 3) { // Stop early if there is not enough data @@ -113,7 +111,7 @@ int main() { // Stop normally on success HARNESS_STOP(); - __arch_harness_start(MAGIC_ALT_0, result, &size); + HARNESS_START_INDEX(1, result, &size); second_function_under_test(result); @@ -129,12 +127,26 @@ And configuration settings like: ```python @tsffs.start_on_harness = True @tsffs.stop_on_harness = True -@tsffs.start_magic_number = 4 +@tsffs.magic_start_index = 1 ``` With this runtime configuration, the first harness will be ignored, and only the second set of harness calls will be used. +## Alternative Start Harnesses + +Several additional variants of the start harness are provided to allow +different target software to be used with as little modification as possible. + +* `HARNESS_START_WITH_MAXIMUM_SIZE(void *buffer, size_t max_size)` takes a + pointer to a buffer like `HARNESS_START` but takes a size instead of a + pointer to a size as the second argument. Use this harness when the target + software does not need to read the actual buffer size. +* `HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR(void *buffer, void *size_ptr, size_t max_size)` + takes a pointer to both a buffer and size like `HARNESS_START`, and takes a + size as the third argument. Use this harness when the target software does + not initially have `*size_ptr` set to the maximum size, but still needs to + read the actual buffer size. ## Troubleshooting @@ -149,4 +161,4 @@ char buffer[100]; size_t size = sizeof(buffer); size_t size_ptr = &size; HARNESS_START(buffer, size_ptr); -``` \ No newline at end of file +``` diff --git a/docs/src/setup/windows.md b/docs/src/setup/windows.md index d2834eec..608ad073 100644 --- a/docs/src/setup/windows.md +++ b/docs/src/setup/windows.md @@ -265,7 +265,7 @@ ispm.exe projects $env:TEMP\TSFFS-Windows\ --create cp examples\docker-example\fuzz.simics $env:TEMP\TSFFS-Windows\ cp tests\rsrc\x86_64-uefi\* $env:TEMP\TSFFS-Windows\ cp tests\rsrc\minimal_boot_disk.craff $env:TEMP\TSFFS-Windows\ -cp harness\tsffs-gcc-x86_64.h $env:TEMP\TSFFS-Windows\ +cp harness\tsffs.h $env:TEMP\TSFFS-Windows\ cd $env:TEMP\TSFFS-Windows ./simics --no-win ./fuzz.simics ``` diff --git a/docs/src/tutorials/edk2-simics-platform-bios/harnessing.md b/docs/src/tutorials/edk2-simics-platform-bios/harnessing.md index 6eb4b40c..0b3c3a60 100644 --- a/docs/src/tutorials/edk2-simics-platform-bios/harnessing.md +++ b/docs/src/tutorials/edk2-simics-platform-bios/harnessing.md @@ -130,7 +130,7 @@ We'll add our harness in the form of a patch to `edk2-platforms`. Our `Dockerfile` from [previously](building-bios.md) just needs a couple modifications. First, we need to copy -[tsffs-gcc-x86_64.h](https://github.com/intel/tsffs/blob/main/harness/tsffs-gcc-x86_64.h) +[tsffs.h](https://github.com/intel/tsffs/blob/main/harness/tsffs.h) from the `harness` directory of the [repository](https://github.com/intel/tsffs/) and put it next to our `Dockerfile`. Then, just before the last `RUN` step (where we run `build_bios.py`), we'll add the following to create and apply our patch and copy the @@ -154,7 +154,7 @@ index 9cea5f4665..00815adba2 100644 #include #include -+#include "tsffs-gcc-x86_64.h" ++#include "tsffs.h" /** Convert a *.BMP graphics image to a GOP blt buffer. If a NULL Blt buffer is passed in a GopBlt buffer will be allocated by this routine. If a GopBlt @@ -232,7 +232,7 @@ index 9cea5f4665..00815adba2 100644 EOF -COPY tsffs-gcc-x86_64.h /workspace/edk2-platforms/Platform/Intel/SimicsOpenBoardPkg/Library/DxeLogoLib/tsffs-gcc-x86_64.h +COPY tsffs.h /workspace/edk2-platforms/Platform/Intel/SimicsOpenBoardPkg/Library/DxeLogoLib/tsffs.h RUN git -C /workspace/edk2-platforms apply /tmp/edk2-platforms.patch ``` diff --git a/docs/src/tutorials/edk2-uefi/reproducing-runs.md b/docs/src/tutorials/edk2-uefi/reproducing-runs.md index 5e83b38a..a1eb6a78 100644 --- a/docs/src/tutorials/edk2-uefi/reproducing-runs.md +++ b/docs/src/tutorials/edk2-uefi/reproducing-runs.md @@ -42,5 +42,5 @@ and more! Check out the SIMICS documentation and explore all the deep debugging capabilities that SIMICS offers. When you're done exploring, run `c` to continue. You can change the testcase you are examining by choosing a different one with -`tsffs.iface.tsffs.repro`, but you cannot resume fuzzing after entering repro mode due +`tsffs.iface.fuzz.repro`, but you cannot resume fuzzing after entering repro mode due to inconsistencies with the simulated system clock. diff --git a/docs/src/tutorials/edk2-uefi/writing-the-application.md b/docs/src/tutorials/edk2-uefi/writing-the-application.md index 150c3783..b7a35786 100644 --- a/docs/src/tutorials/edk2-uefi/writing-the-application.md +++ b/docs/src/tutorials/edk2-uefi/writing-the-application.md @@ -12,7 +12,7 @@ First, we will create a `src` directory with the following files: * `Tutorial.dsc` - The EDK2 description file for building our target software * `Tutorial.inf` - The EDK2 info file for building our target software * `Tutorial.c` - Our C source file -* `tsffs-gcc-x86_64.h` - The header file from the `harness` directory in the repository +* `tsffs.h` - The header file from the `harness` directory in the repository for our target architecture We'll cover the auxiliary and build files first, then we'll cover the source code. @@ -149,7 +149,7 @@ whole platform including our application and requisite additional libraries. Tutorial/Tutorial.inf ``` -## tsffs-gcc-x86_64.h +## tsffs.h Copy this file from the TSFFS repository's `harness` directory. It provides macros for compiling in the harness so the target software can communicate with and receive @@ -168,7 +168,7 @@ which tries to verify a certificate was issued by a given certificate authority. #include #include -#include "tsffs-gcc-x86_64.h" +#include "tsffs.h" void hexdump(UINT8 *buf, UINTN size) { for (UINTN i = 0; i < size; i++) { diff --git a/docs/src/tutorials/kernel-module/kernel-module-code.md b/docs/src/tutorials/kernel-module/kernel-module-code.md index 3664eb70..126dd49e 100644 --- a/docs/src/tutorials/kernel-module/kernel-module-code.md +++ b/docs/src/tutorials/kernel-module/kernel-module-code.md @@ -19,8 +19,8 @@ clean: This in turn invokes the standard KBuild process, specifying our current directory as an out of tree modules directory. -Then, copy `tsffs-gcc-riscv64.h` from the `harness` directory of the repository into -`src/tutorial-kernel-modules/package/kernel-modules/tutorial-mod/tsffs-gcc-riscv64.h`. +Then, copy `tsffs.h` from the `harness` directory of the repository into +`src/tutorial-kernel-modules/package/kernel-modules/tutorial-mod/tsffs.h`. Finally, we can write our Kernel module. Doing so is well beyond the scope of this tutorial, so copy the code below into @@ -41,7 +41,7 @@ tutorial, so copy the code below into #include #include -#include "tsffs-gcc-riscv64.h" +#include "tsffs.h" #define MAJOR_NUM 100 #define IOCTL_SET_MSG _IOW(MAJOR_NUM, 0, char *) diff --git a/docs/src/tutorials/kernel-module/kernel-module-harnessing.md b/docs/src/tutorials/kernel-module/kernel-module-harnessing.md index 73c185da..f6b99023 100644 --- a/docs/src/tutorials/kernel-module/kernel-module-harnessing.md +++ b/docs/src/tutorials/kernel-module/kernel-module-harnessing.md @@ -45,8 +45,8 @@ take over and start the fuzzing loop. ## Userspace Driver Code -First, copy `tsffs-gcc-riscv64.h` from the `harness` directory in the repository into -`src/tsffs-gcc-riscv64.h`. +First, copy `tsffs.h` from the `harness` directory in the repository into +`src/tsffs.h`. We'll also create `src/tutorial-mod-driver.c`, a user-space application which we will use to drive the kernel module code via IOCTL. @@ -59,7 +59,7 @@ use to drive the kernel module code via IOCTL. #include #include -#include "tsffs-gcc-riscv64.h" +#include "tsffs.h" #define MAJOR_NUM 100 #define IOCTL_SET_MSG _IOW(MAJOR_NUM, 0, char *) diff --git a/examples/docker-example/fuzz.simics b/examples/docker-example/fuzz.simics index f0614f57..fe0acb3f 100644 --- a/examples/docker-example/fuzz.simics +++ b/examples/docker-example/fuzz.simics @@ -6,35 +6,32 @@ load-module tsffs # Create the TSFFS fuzzer object -@tsffs = SIM_create_object(SIM_get_class("tsffs"), "tsffs", []) +init-tsffs # Set the log level for TSFFS to 3 (debug). Set to 4 (trace) or 1 (error) for additional # debug information or brevity, respectively tsffs.log-level 3 # Set to start on magic harness. This is the "default" behavior for harnessing. -@tsffs.iface.tsffs.set_start_on_harness(True) +@tsffs.start_on_harness = True # Set to stop on magic harness. This is the "default" behavior for harnessing. -@tsffs.iface.tsffs.set_stop_on_harness(True) +@tsffs.stop_on_harness = True # Set a virtual-time timeout of 3 seconds. If the target runs for this amount of time # without encountering a normal stop or another solution condition, it will be # considered a timeout. -@tsffs.iface.tsffs.set_timeout(3.0) +@tsffs.timeout = 3.0 # Set page faults as an exception-type solution. When the CPU raises exception #14, the # input that caused it will be saved as a solution. -@tsffs.iface.tsffs.add_exception_solution(14) +@tsffs.exceptions = [14] # Set to generate a random corpus. This is only used for demonstration purposes. -@tsffs.iface.tsffs.set_generate_random_corpus(True) - -# Public SIMICS uses SIMICS base version 6.0.169, so snapshots are not yet available -@tsffs.iface.tsffs.set_use_snapshots(False) +@tsffs.generate_random_corpus = True # Tokenize our UEFI executable to greatly enhance mutation efficacy -@tsffs.iface.tsffs.tokenize_executable("%simics%/test.efi") +@tsffs.token_executables += [SIM_lookup_file("%simics%/test.efi")] # Load the UEFI shell target for the QSP-x86 board, using an x86-64 Golden Cove (Sapphire Rapids) processor class # We set disk0 to the minimal_boot_disk.craff provided, which includes the SimicsAgent.efi app we can use to download diff --git a/examples/tutorials/edk2-simics-platform/.gitignore b/examples/tutorials/edk2-simics-platform/.gitignore index c0bbc4c1..1e5ef342 100644 --- a/examples/tutorials/edk2-simics-platform/.gitignore +++ b/examples/tutorials/edk2-simics-platform/.gitignore @@ -1,2 +1,2 @@ workspace -tsffs-gcc-x86_64.h \ No newline at end of file +tsffs*.h \ No newline at end of file diff --git a/examples/tutorials/edk2-simics-platform/Dockerfile b/examples/tutorials/edk2-simics-platform/Dockerfile index 65d98b6d..b6d08241 100644 --- a/examples/tutorials/edk2-simics-platform/Dockerfile +++ b/examples/tutorials/edk2-simics-platform/Dockerfile @@ -51,7 +51,7 @@ index 9cea5f4665..00815adba2 100644 #include #include -+#include "tsffs-gcc-x86_64.h" ++#include "tsffs.h" /** Convert a *.BMP graphics image to a GOP blt buffer. If a NULL Blt buffer is passed in a GopBlt buffer will be allocated by this routine. If a GopBlt @@ -129,7 +129,7 @@ index 9cea5f4665..00815adba2 100644 EOF -COPY tsffs-gcc-x86_64.h "${PROJECT}/edk2-platforms/Platform/Intel/SimicsOpenBoardPkg/Library/DxeLogoLib/tsffs-gcc-x86_64.h" +COPY tsffs.h "${PROJECT}/edk2-platforms/Platform/Intel/SimicsOpenBoardPkg/Library/DxeLogoLib/tsffs.h" RUN git -C "${PROJECT}/edk2-platforms" apply --whitespace fix /tmp/edk2-platforms.patch diff --git a/examples/tutorials/edk2-simics-platform/build-custom.sh b/examples/tutorials/edk2-simics-platform/build-custom.sh index 8740fc1a..71644220 100755 --- a/examples/tutorials/edk2-simics-platform/build-custom.sh +++ b/examples/tutorials/edk2-simics-platform/build-custom.sh @@ -21,7 +21,7 @@ if [ ! -d "${SCRIPT_DIR}/workspace" ]; then git clone https://github.com/tianocore/edk2-platforms.git "${SCRIPT_DIR}/workspace/edk2-platforms" git -C "${SCRIPT_DIR}/workspace/edk2-platforms" checkout "${EDK2_PLATFORMS_HASH}" git -C "${SCRIPT_DIR}/workspace/edk2-platforms" submodule update --init - cp "${SCRIPT_DIR}/../../../harness/tsffs-gcc-x86_64.h" "${SCRIPT_DIR}/workspace/edk2-platforms/Platform/Intel/SimicsOpenBoardPkg/Library/DxeLogoLib/tsffs-gcc-x86_64.h" + cp "${SCRIPT_DIR}/../../../harness/tsffs.h" "${SCRIPT_DIR}/workspace/edk2-platforms/Platform/Intel/SimicsOpenBoardPkg/Library/DxeLogoLib/tsffs.h" git clone https://github.com/tianocore/edk2-non-osi.git "${SCRIPT_DIR}/workspace/edk2-non-osi" git -C "${SCRIPT_DIR}/workspace/edk2-non-osi" checkout "${EDK2_NON_OSI_HASH}" git -C "${SCRIPT_DIR}/workspace/edk2-non-osi" submodule update --init diff --git a/examples/tutorials/edk2-simics-platform/build.sh b/examples/tutorials/edk2-simics-platform/build.sh index 998e3539..84e5f13d 100755 --- a/examples/tutorials/edk2-simics-platform/build.sh +++ b/examples/tutorials/edk2-simics-platform/build.sh @@ -9,7 +9,7 @@ DOCKERFILE="${SCRIPT_DIR}/Dockerfile" CONTAINER_UID=$(echo "${RANDOM}" | sha256sum | head -c 8) CONTAINER_NAME="${IMAGE_NAME}-tmp-${CONTAINER_UID}" -cp "${SCRIPT_DIR}/../../../harness/tsffs-gcc-x86_64.h" "${SCRIPT_DIR}/tsffs-gcc-x86_64.h" +cp "${SCRIPT_DIR}/../../../harness/tsffs.h" "${SCRIPT_DIR}/tsffs.h" mkdir -p "${SCRIPT_DIR}/project/" docker build -t "${IMAGE_NAME}" -f "${DOCKERFILE}" --build-arg "PROJECT=${SCRIPT_DIR}/project/workspace/" "${SCRIPT_DIR}" docker create --name "${CONTAINER_NAME}" "${IMAGE_NAME}" bash diff --git a/examples/tutorials/edk2-simics-platform/edk2-platforms.patch b/examples/tutorials/edk2-simics-platform/edk2-platforms.patch index d384ac2f..5f2ca173 100644 --- a/examples/tutorials/edk2-simics-platform/edk2-platforms.patch +++ b/examples/tutorials/edk2-simics-platform/edk2-platforms.patch @@ -1,8 +1,8 @@ -diff --git a/Platform/Intel/SimicsOpenBoardPkg/Library/DxeLogoLib/Logo.c b/Platform/Intel/SimicsOpenBoardPkg/Library/DxeLogoLib/Logo.c -index 9cea5f4665..00815adba2 100644 ---- a/Platform/Intel/SimicsOpenBoardPkg/Library/DxeLogoLib/Logo.c -+++ b/Platform/Intel/SimicsOpenBoardPkg/Library/DxeLogoLib/Logo.c -@@ -11,6 +11,7 @@ +diff --git a/Platform/Intel/SimicsOpenBoardPkg/Library/DxeLogoLib/Logo.c b/Platform/Intel/SimicsOpenBoardPkg/Library/DxeLogoLib/Logo.c +index 9cea5f4665..00815adba2 100644 +--- a/Platform/Intel/SimicsOpenBoardPkg/Library/DxeLogoLib/Logo.c ++++ b/Platform/Intel/SimicsOpenBoardPkg/Library/DxeLogoLib/Logo.c +@@ -11,6 +11,7 @@ #include #include #include @@ -10,15 +10,15 @@ index 9cea5f4665..00815adba2 100644 #include #include #include -@@ -22,6 +23,7 @@ +@@ -22,6 +23,7 @@ #include #include -+#include "tsffs-gcc-x86_64.h" ++#include "tsffs.h" /** Convert a *.BMP graphics image to a GOP blt buffer. If a NULL Blt buffer is passed in a GopBlt buffer will be allocated by this routine. If a GopBlt -@@ -164,9 +166,6 @@ ConvertBmpToGopBlt ( +@@ -164,9 +166,6 @@ ConvertBmpToGopBlt ( *GopBltSize = (UINTN) BltBufferSize; *GopBlt = AllocatePool (*GopBltSize); IsAllocated = TRUE; @@ -28,7 +28,7 @@ index 9cea5f4665..00815adba2 100644 } else { // // GopBlt has been allocated by caller. -@@ -184,6 +183,7 @@ ConvertBmpToGopBlt ( +@@ -184,6 +183,7 @@ ConvertBmpToGopBlt ( // Convert image from BMP to Blt buffer format // BltBuffer = *GopBlt; @@ -36,7 +36,7 @@ index 9cea5f4665..00815adba2 100644 for (Height = 0; Height < BmpHeader->PixelHeight; Height++) { Blt = &BltBuffer[(BmpHeader->PixelHeight - Height - 1) * BmpHeader->PixelWidth]; for (Width = 0; Width < BmpHeader->PixelWidth; Width++, Image++, Blt++) { -@@ -398,6 +398,7 @@ EnableBootLogo ( +@@ -398,6 +398,7 @@ EnableBootLogo ( // Try BMP decoder // Blt = NULL; @@ -44,7 +44,7 @@ index 9cea5f4665..00815adba2 100644 Status = ConvertBmpToGopBlt ( ImageData, ImageSize, -@@ -411,6 +412,7 @@ EnableBootLogo ( +@@ -411,6 +412,7 @@ EnableBootLogo ( FreePool (ImageData); if (Badging == NULL) { @@ -52,7 +52,7 @@ index 9cea5f4665..00815adba2 100644 return Status; } else { continue; -@@ -537,6 +539,7 @@ Done: +@@ -537,6 +539,7 @@ Done: FreePool (Blt); } @@ -60,7 +60,7 @@ index 9cea5f4665..00815adba2 100644 return Status; } -@@ -561,6 +564,7 @@ Done: +@@ -561,6 +564,7 @@ Done: // Ensure the LogoHeight * LogoWidth doesn't overflow // if (LogoHeight > DivU64x64Remainder ((UINTN) ~0, LogoWidth, NULL)) { @@ -68,7 +68,7 @@ index 9cea5f4665..00815adba2 100644 return EFI_UNSUPPORTED; } BufferSize = MultU64x64 (LogoWidth, LogoHeight); -@@ -569,11 +573,13 @@ Done: +@@ -569,11 +573,13 @@ Done: // Ensure the BufferSize * sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL) doesn't overflow // if (BufferSize > DivU64x32 ((UINTN) ~0, sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL))) { @@ -82,7 +82,7 @@ index 9cea5f4665..00815adba2 100644 return EFI_OUT_OF_RESOURCES; } -@@ -600,5 +606,6 @@ Done: +@@ -600,5 +606,6 @@ Done: } FreePool (LogoBlt); diff --git a/examples/tutorials/edk2-uefi/src/Tutorial.c b/examples/tutorials/edk2-uefi/src/Tutorial.c index 48f50675..a7fa529a 100644 --- a/examples/tutorials/edk2-uefi/src/Tutorial.c +++ b/examples/tutorials/edk2-uefi/src/Tutorial.c @@ -8,7 +8,7 @@ #include #include -#include "tsffs-gcc-x86_64.h" +#include "tsffs.h" void hexdump(UINT8 *buf, UINTN size) { for (UINTN i = 0; i < size; i++) { diff --git a/examples/tutorials/edk2-uefi/src/tsffs-gcc-x86_64.h b/examples/tutorials/edk2-uefi/src/tsffs-gcc-x86_64.h deleted file mode 100644 index 7925dae7..00000000 --- a/examples/tutorials/edk2-uefi/src/tsffs-gcc-x86_64.h +++ /dev/null @@ -1,122 +0,0 @@ -// Copyright (C) 2024 Intel Corporation -// SPDX-License-Identifier: Apache-2.0 - -/// Definitions and macros for compiled-in harnessing of C and C++ target -/// software for the RISC-V (64-bit) architecture -/// -/// Example: -/// ```c -/// #include "tsffs-gcc-x86_64.h" -/// -/// int main() { -/// char buf[0x10]; -/// size_t size = 0x10; -/// size_t *size_ptr = &size; -/// HARNESS_START((char *)buf, size_ptr); -/// int retval = YourSpecialDecoder(buf, *size_ptr); -/// if (!retval) { -/// HARNESS_ASSERT(); -/// } else { -/// HARNESS_STOP(); -/// } -/// } -/// ``` - -#ifndef TSFFS_H -#define TSFFS_H - -/// Define common with LibFuzzer and other fuzzers to allow code that is -/// fuzzing-specific to be left in the codebase. See -/// https://llvm.org/docs/LibFuzzer.html#id35 for more information -#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION -#define FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION (1) -#endif // FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION - -/// Magic value defined by SIMICS as the "leaf" value of a CPUID instruction -/// that is treated as a magic instruction. -#define MAGIC (0x4711U) -/// Default magic instruction `n` value to signal the TSFFS fuzzer to start the -/// fuzzing loop -#define MAGIC_START (0x0001U) -/// Default magic instruction `n` value to signal the TSFFS fuzzer to stop and -/// reset the fuzzing loop -#define MAGIC_STOP (0x0002U) -/// Default magic instruction `n` value to signal the TSFFS fuzzer that an error -/// has occurred and the testcase input should be saved as a solution -#define MAGIC_ASSERT (0x0003U) - -/// Invoke the magic instruction defined by SIMICS for the x86-64 architecture -/// (`cpuid`) with a specific value of `n`, after setting register `rdi` to the -/// value of the pointer to the testcase and register `rsi` to the value of the -/// pointer to the testcase size. These registers are accessed by the fuzzer and -/// are defined per-architecture. -#define __cpuid_extended2(value, inout_ptr_0, inout_ptr_1) \ - unsigned int _a __attribute__((unused)) = 0; \ - __asm__ __volatile__("cpuid" \ - : "=a"(_a) \ - : "a"(value), "D"(inout_ptr_0), "S"(inout_ptr_1) \ - : "rbx", "rcx", "rdx"); - -/// Invoke the magic instruction defined by SIMICS for the x86-64 architecture -/// (`cpuid`) with a specific value of `n` -#define __cpuid(value) \ - unsigned int _a __attribute__((unused)) = 0; \ - __asm__ __volatile__("cpuid\n\t" \ - : "=a"(_a) \ - : "a"(value) \ - : "rbx", "rcx", "rdx") - -/// Signal the fuzzer using a specific magic value `start` to start the fuzzing -/// loop at the point this macro is called. A snapshot will be taken when the -/// macro is called, and the maximum testcase size `*size_ptr` will be saved as -/// `max_testcase_size`. Each iteration of the fuzzing loop, the fuzzer input -/// (the "testcase") will be written to `*testcase_ptr` as if running -/// `memcpy(testcase_ptr, current_testcase, max_testcase_size)`, and the actual -/// size of the current testcase will be written to -/// `*size_ptr` as if running `*size_ptr = current_testcase_size`. -#define __arch_harness_start(start, testcase_ptr, size_ptr) \ - do { \ - unsigned int magic = (start << 0x10U) | MAGIC; \ - __cpuid_extended2(magic, testcase_ptr, size_ptr); \ - } while (0) - -/// Signal the fuzzer using the a specific magic value (`stop`) to stop and -/// reset to the beginning of the fuzzing loop with a "normal" stop status, -/// indicating no solution has occurred. -#define __arch_harness_stop(stop) \ - do { \ - unsigned int magic = (stop << 0x10U) | MAGIC; \ - __cpuid(magic); \ - } while (0) - -/// Signal the fuzzer using the default magic value to start the fuzzing loop at -/// the point this macro is called. A snapshot will be taken when the macro is -/// called, and the maximum testcase size `*size_ptr` will be saved as -/// `max_testcase_size`. Each iteration of the fuzzing loop, the fuzzer input -/// (the "testcase") will be written to -/// `*testcase_ptr` as if running `memcpy(testcase_ptr, current_testcase, -/// max_testcase_size)`, and the actual size of the current testcase will be -/// written to -/// `*size_ptr` as if running `*size_ptr = current_testcase_size`. -#define HARNESS_START(testcase_ptr, size_ptr) \ - do { \ - __arch_harness_start(MAGIC_START, testcase_ptr, size_ptr); \ - } while (0) - -/// Signal the fuzzer using the default magic value to stop and reset to the -/// beginning of the fuzzing loop with a "normal" stop status, indicating no -/// solution has occurred. -#define HARNESS_STOP() \ - do { \ - __arch_harness_stop(MAGIC_STOP); \ - } while (0) - -/// Signal the fuzzer using the default magic value to stop and reset to the -/// beginning of the fuzzing loop with a "solution" stop status, indicating some -/// custom error has occurred. -#define HARNESS_ASSERT() \ - do { \ - __arch_harness_stop(MAGIC_ASSERT); \ - } while (0) - -#endif // TSFFS_H \ No newline at end of file diff --git a/examples/tutorials/edk2-uefi/src/tsffs.h b/examples/tutorials/edk2-uefi/src/tsffs.h new file mode 100644 index 00000000..95b604fd --- /dev/null +++ b/examples/tutorials/edk2-uefi/src/tsffs.h @@ -0,0 +1,2876 @@ +// Copyright (C) 2024 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 + +#ifdef __GNUC__ +#ifdef __i386__ +// Copyright (C) 2024 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 + +/// Definitions and macros for compiled-in harnessing of C and C++ target +/// software for the X86 architecture. + +#ifndef TSFFS_H +#define TSFFS_H + +/// Define common with LibFuzzer and other fuzzers to allow code that is +/// fuzzing-specific to be left in the codebase. See +/// https://llvm.org/docs/LibFuzzer.html#id35 for more information +#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION +#define FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION (1) +#endif // FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION + +/// __cpuid +/// +/// Invoke the CPUID instruction with a specific value `value` in register +/// `rax`. +/// +/// # Arguments +/// +/// - `value`: The value to load into the `rax` register before invoking the +/// CPUID instruction +#define __cpuid(value) \ + unsigned int _a __attribute__((unused)) = 0; \ + unsigned int _b __attribute__((unused)) = 0; \ + unsigned int _c __attribute__((unused)) = 0; \ + unsigned int _d __attribute__((unused)) = 0; \ + __asm__ __volatile__("cpuid\n\t" \ + : "=a"(_a), "=b"(_b), "=c"(_c), "=d"(_d) \ + : "a"(value)); + +/// __cpuid_extended1 +/// +/// Invoke the CPUID instruction with a specific value `value` in register +/// `rax` and a pseudo-argument in register `rdi`. +/// +/// # Arguments +/// +/// - `value`: The value to load into the `rax` register before invoking the +/// CPUID instruction +/// - `arg0`: The value to load into the `rdi` register before invoking the +/// CPUID instruction +#define __cpuid_extended1(value, arg0) \ + unsigned int _a __attribute__((unused)) = 0; \ + unsigned int _b __attribute__((unused)) = 0; \ + unsigned int _c __attribute__((unused)) = 0; \ + unsigned int _d __attribute__((unused)) = 0; \ + __asm__ __volatile__("cpuid\n\t" \ + : "=a"(_a), "=b"(_b), "=c"(_c), "=d"(_d) \ + : "a"(value), "D"(arg0)); + +/// __cpuid_extended2 +/// +/// Invoke the CPUID instruction with a specific value `value` in register +/// `rax` and a pseudo-arguments in registers `rdi` and `rsi`. +/// +/// # Arguments +/// +/// - `value`: The value to load into the `rax` register before invoking the +/// CPUID instruction +/// - `arg0`: The value to load into the `rdi` register before invoking the +/// CPUID instruction +/// - `arg1`: The value to load into the `rsi` register before invoking the +/// CPUID instruction +#define __cpuid_extended2(value, arg0, arg1) \ + unsigned int _a __attribute__((unused)) = 0; \ + unsigned int _b __attribute__((unused)) = 0; \ + unsigned int _c __attribute__((unused)) = 0; \ + unsigned int _d __attribute__((unused)) = 0; \ + __asm__ __volatile__("cpuid\n\t" \ + : "=a"(_a), "=b"(_b), "=c"(_c), "=d"(_d) \ + : "a"(value), "D"(arg0), "S"(arg1)); + +/// __cpuid_extended3 +/// +/// Invoke the CPUID instruction with a specific value `value` in register +/// `rax` and a pseudo-arguments in registers `rdi`, `rsi`, and `rdx`. +/// +/// # Arguments +/// +/// - `value`: The value to load into the `rax` register before invoking the +/// CPUID instruction +/// - `arg0`: The value to load into the `rdi` register before invoking the +/// CPUID instruction +/// - `arg1`: The value to load into the `rsi` register before invoking the +/// CPUID instruction +/// - `arg2`: The value to load into the `rdx` register before invoking the +/// CPUID instruction +#define __cpuid_extended3(value, arg0, arg1, arg2) \ + unsigned int _a __attribute__((unused)) = 0; \ + unsigned int _b __attribute__((unused)) = 0; \ + unsigned int _c __attribute__((unused)) = 0; \ + unsigned int _d __attribute__((unused)) = 0; \ + __asm__ __volatile__("cpuid\n\t" \ + : "=a"(_a), "=b"(_b), "=c"(_c), "=d"(_d) \ + : "a"(value), "D"(arg0), "S"(arg1), "d"(arg2)); + +/// __cpuid_extended4 +/// +/// Invoke the CPUID instruction with a specific value `value` in register +/// `rax` and a pseudo-arguments in registers `rdi`, `rsi`, `rdx`, and `rcx`. +/// +/// # Arguments +/// +/// - `value`: The value to load into the `rax` register before invoking the +/// CPUID instruction +/// - `arg0`: The value to load into the `rdi` register before invoking the +/// CPUID instruction +/// - `arg1`: The value to load into the `rsi` register before invoking the +/// CPUID instruction +/// - `arg2`: The value to load into the `rdx` register before invoking the +/// CPUID instruction +/// - `arg3`: The value to load into the `rcx` register before invoking the +/// CPUID instruction +#define __cpuid_extended4(value, arg0, arg1, arg2, arg3) \ + unsigned int _a __attribute__((unused)) = 0; \ + unsigned int _b __attribute__((unused)) = 0; \ + unsigned int _c __attribute__((unused)) = 0; \ + unsigned int _d __attribute__((unused)) = 0; \ + __asm__ __volatile__("cpuid\n\t" \ + : "=a"(_a), "=b"(_b), "=c"(_c), "=d"(_d) \ + : "a"(value), "D"(arg0), "S"(arg1), "d"(arg2), \ + "c"(arg3)); + +/// Magic value defined by SIMICS as the "leaf" value of a CPUID instruction +/// that is treated as a magic instruction. +#define MAGIC (0x4711U) + +/// The default index number used for magic instructions. All magic instructions +/// support multiple start and stop indices, which defaults to 0 if not +/// specified. +#define DEFAULT_INDEX (0x0000U) + +/// Pseudo-hypercall number to signal the fuzzer to use the first argument to +/// the magic instruction as the pointer to the testcase buffer and the second +/// argument as a pointer to the size of the testcase buffer. +#define N_START_BUFFER_PTR_SIZE_PTR (0x0001U) + +/// HARNESS_START +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The default "index" of 0 will be used. If you need multiple start +/// harnesses compiled into the same binary, you can use the +/// `HARNESS_START_INDEX` macro to specify different indices, then enable them +/// at runtime by configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The size of the buffer pointed to by `size_ptr` will be saved as the +/// maximum testcase size. Each fuzzing iteration, the actual size of the +/// current testcase will be written to `*size_ptr`. +/// +/// # Arguments +/// +/// - `buffer`: The pointer to the testcase buffer +/// - `size_ptr`: The pointer to the size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// size_t size; +/// HARNESS_START(buffer, &size); +/// ``` +#define HARNESS_START(buffer, size_ptr) \ + do { \ + unsigned int value = (N_START_BUFFER_PTR_SIZE_PTR << 0x10U) | MAGIC; \ + __cpuid_extended3(value, DEFAULT_INDEX, buffer, size_ptr); \ + } while (0); + +/// HARNESS_START_INDEX +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The index specified by `start_index` will be used. If you need +/// multiple start harnesses compiled into the same binary, you can use this +/// macro to specify different indices, then enable them at runtime by +/// configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The size of the buffer pointed to by `size_ptr` will be saved as the +/// maximum testcase size. Each fuzzing iteration, the actual size of the +/// current testcase will be written to `*size_ptr`. +/// +/// # Arguments +/// +/// - `start_index`: The index to use for this start harness +/// - `buffer`: The pointer to the testcase buffer +/// - `size_ptr`: The pointer to the size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// size_t size; +/// HARNESS_START_INDEX(0x0001U, buffer, &size); +/// ``` +#define HARNESS_START_INDEX(start_index, buffer, size_ptr) \ + do { \ + unsigned int value = (N_START_BUFFER_PTR_SIZE_PTR << 0x10U) | MAGIC; \ + __cpuid_extended3(value, start_index, buffer, size_ptr); \ + } while (0); + +/// Pseudo-hypercall number to signal the fuzzer to use the first argument to +/// the magic instruction as the pointer to the testcase buffer and the second +/// argument as the maximum size of the testcase buffer. +#define N_START_BUFFER_PTR_SIZE_VAL (0x0002U) + +/// HARNESS_START_WITH_MAXIMUM_SIZE +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The default "index" of 0 will be used. If you need multiple start +/// harnesses compiled into the same binary, you can use the +/// `HARNESS_START_WITH_MAXIMUM_SIZE_INDEX` macro to specify different indices, +/// then enable them at runtime by configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The `max_size` value will be saved as the maximum testcase size. Fuzzing +/// test cases will be truncated to this size before being written to the +/// buffer. +/// +/// # Arguments +/// +/// - `buffer`: The pointer to the testcase buffer +/// - `max_size`: The maximum size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// HARNESS_START_WITH_MAXIMUM_SIZE(buffer, 1024); +/// ``` +#define HARNESS_START_WITH_MAXIMUM_SIZE(buffer, max_size) \ + do { \ + unsigned int value = (N_START_BUFFER_PTR_SIZE_VAL << 0x10U) | MAGIC; \ + __cpuid_extended3(value, DEFAULT_INDEX, buffer, max_size); \ + } while (0); + +/// HARNESS_START_WITH_MAXIMUM_SIZE_INDEX +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The index specified by `start_index` will be used. If you need +/// multiple start harnesses compiled into the same binary, you can use this +/// macro to specify different indices, then enable them at runtime by +/// configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The `max_size` value will be saved as the maximum testcase size. Fuzzing +/// test cases will be truncated to this size before being written to the +/// buffer. +/// +/// # Arguments +/// +/// - `start_index`: The index to use for this start harness +/// - `buffer`: The pointer to the testcase buffer +/// - `max_size`: The maximum size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// HARNESS_START_WITH_MAXIMUM_SIZE_INDEX(0x0001U, buffer, 1024); +/// ``` +#define HARNESS_START_WITH_MAXIMUM_SIZE_INDEX(start_index, buffer, max_size) \ + do { \ + unsigned int value = (N_START_BUFFER_PTR_SIZE_VAL << 0x10U) | MAGIC; \ + __cpuid_extended3(value, start_index, buffer, max_size); \ + } while (0); + +/// Pseudo-hypercall number to signal the fuzzer to use the first argument to +/// the magic instruction as the pointer to the testcase buffer, the second +/// argument as a pointer to the size of the testcase buffer, and the third +/// argument as the maximum size of the testcase buffer. +#define N_START_BUFFER_PTR_SIZE_PTR_VAL (0x0003U) + +/// HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The default "index" of 0 will be used. If you need multiple start +/// harnesses compiled into the same binary, you can use the +/// `HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR_INDEX` macro to specify different +/// indices, then enable them at runtime by configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The address `size_ptr` will be saved. Each fuzzing iteration, the actual +/// size of the current testcase will be written to `*size_ptr`. +/// - The `max_size` value will be saved as the maximum testcase size. Fuzzing +/// test cases will be truncated to this size before being written to the +/// buffer. +/// +/// # Arguments +/// +/// - `buffer`: The pointer to the testcase buffer +/// - `size_ptr`: The pointer to the size of the testcase buffer +/// - `max_size`: The maximum size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// size_t size; +/// HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR(buffer, &size, 1024); +/// ``` +#define HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR(buffer, size_ptr, max_size) \ + do { \ + unsigned int value = (N_START_BUFFER_PTR_SIZE_PTR_VAL << 0x10U) | MAGIC; \ + __cpuid_extended4(value, DEFAULT_INDEX, buffer, size_ptr, max_size); \ + } while (0); + +/// HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR_INDEX +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The index specified by `start_index` will be used. If you need +/// multiple start harnesses compiled into the same binary, you can use this +/// macro to specify different indices, then enable them at runtime by +/// configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The address `size_ptr` will be saved. Each fuzzing iteration, the actual +/// size of the current testcase will be written to `*size_ptr`. +/// - The `max_size` value will be saved as the maximum testcase size. Fuzzing +/// test cases will be truncated to this size before being written to the +/// buffer. +/// +/// # Arguments +/// +/// - `start_index`: The index to use for this start harness +/// - `buffer`: The pointer to the testcase buffer +/// - `size_ptr`: The pointer to the size of the testcase buffer +/// - `max_size`: The maximum size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// size_t size; +/// HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR_INDEX(0x0001U, buffer, &size, 1024); +/// ``` +#define HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR_INDEX(start_index, buffer, \ + size_ptr, max_size) \ + do { \ + unsigned int value = (N_START_BUFFER_PTR_SIZE_PTR_VAL << 0x10U) | MAGIC; \ + __cpuid_extended4(value, start_index, buffer, size_ptr, max_size); \ + } while (0); + +/// Pseudo-hypercall number to signal the fuzzer to stop the current fuzzing +/// iteration and reset to the beginning of the fuzzing loop with a "normal" +/// stop status, indicating no solution has occurred. +#define N_STOP_NORMAL (0x0004U) + +/// HARNESS_STOP +/// +/// Signal the fuzzer to stop and reset to the beginning of the fuzzing loop +/// with a "normal" stop status, indicating no solution has occurred. The +/// default index of 0 will be used. If you need to differentiate between +/// multiple stop harnesses compiled into the same binary, you can use the +/// `HARNESS_STOP_INDEX` macro to specify different indices, then enable them at +/// runtime by configuring the fuzzer. +/// +/// # Example +/// +/// ``` +/// HARNESS_STOP(); +/// ``` +#define HARNESS_STOP() \ + do { \ + unsigned int value = (N_STOP_NORMAL << 0x10U) | MAGIC; \ + __cpuid_extended1(value, DEFAULT_INDEX); \ + } while (0); + +/// HARNESS_STOP_INDEX +/// +/// Signal the fuzzer to stop and reset to the beginning of the fuzzing loop +/// with a "normal" stop status, indicating no solution has occurred. The index +/// specified by `stop_index` will be used. If you need to differentiate between +/// multiple stop harnesses compiled into the same binary, you can use this +/// macro to specify different indices, then enable them at runtime by +/// configuring the fuzzer. +/// +/// # Arguments +/// +/// - `stop_index`: The index to use for this stop harness +/// +/// # Example +/// +/// ``` +/// HARNESS_STOP_INDEX(0x0001U); +/// ``` +#define HARNESS_STOP_INDEX(stop_index) \ + do { \ + unsigned int value = (N_STOP_NORMAL << 0x10U) | MAGIC; \ + __cpuid_extended1(value, stop_index); \ + } while (0); + +/// Pseudo-hypercall number to signal the fuzzer that a custom assertion has +/// occurred, and the fuzzer should stop the current fuzzing iteration and reset +/// to the beginning of the fuzzing loop with a "solution" stop status. +#define N_STOP_ASSERT (0x0005U) + +/// HARNESS_ASSERT +/// +/// Signal the fuzzer that a custom assertion has occurred, and the fuzzer +/// should stop the current fuzzing iteration and reset to the beginning of the +/// fuzzing loop with a "solution" stop status. The default index of 0 will be +/// used. If you need to differentiate between multiple assertion harnesses +/// compiled into the same binary, you can use the `HARNESS_ASSERT_INDEX` macro +/// to specify different indices, then enable them at runtime by configuring the +/// fuzzer. +/// +/// # Example +/// +/// ``` +/// HARNESS_ASSERT(); +/// ``` +#define HARNESS_ASSERT() \ + do { \ + unsigned int value = (N_STOP_ASSERT << 0x10U) | MAGIC; \ + __cpuid_extended1(value, DEFAULT_INDEX); \ + } while (0); + +/// HARNESS_ASSERT_INDEX +/// +/// Signal the fuzzer that a custom assertion has occurred, and the fuzzer +/// should stop the current fuzzing iteration and reset to the beginning of the +/// fuzzing loop with a "solution" stop status. The index specified by +/// `assert_index` will be used. If you need to differentiate between multiple +/// assertion harnesses compiled into the same binary, you can use this macro to +/// specify different indices, then enable them at runtime by configuring the +/// fuzzer. +/// +/// # Arguments +/// +/// - `assert_index`: The index to use for this assertion harness +/// +/// # Example +/// +/// ``` +/// HARNESS_ASSERT_INDEX(0x0001U); +/// ``` +#define HARNESS_ASSERT_INDEX(assert_index) \ + do { \ + unsigned int value = (N_STOP_ASSERT << 0x10U) | MAGIC; \ + __cpuid_extended1(value, assert_index); \ + } while (0); + +#endif // TSFFS_H +#elif __x86_64__ +// Copyright (C) 2024 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 + +/// Definitions and macros for compiled-in harnessing of C and C++ target +/// software for the x86_64 architecture. + +#ifndef TSFFS_H +#define TSFFS_H + +/// Define common with LibFuzzer and other fuzzers to allow code that is +/// fuzzing-specific to be left in the codebase. See +/// https://llvm.org/docs/LibFuzzer.html#id35 for more information +#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION +#define FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION (1) +#endif // FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION + +/// __cpuid +/// +/// Invoke the CPUID instruction with a specific value `value` in register +/// `rax`. +/// +/// # Arguments +/// +/// - `value`: The value to load into the `rax` register before invoking the +/// CPUID instruction +#define __cpuid(value) \ + unsigned int _a __attribute__((unused)) = 0; \ + unsigned int _b __attribute__((unused)) = 0; \ + unsigned int _c __attribute__((unused)) = 0; \ + unsigned int _d __attribute__((unused)) = 0; \ + __asm__ __volatile__("cpuid\n\t" \ + : "=a"(_a), "=b"(_b), "=c"(_c), "=d"(_d) \ + : "a"(value)); + +/// __cpuid_extended1 +/// +/// Invoke the CPUID instruction with a specific value `value` in register +/// `rax` and a pseudo-argument in register `rdi`. +/// +/// # Arguments +/// +/// - `value`: The value to load into the `rax` register before invoking the +/// CPUID instruction +/// - `arg0`: The value to load into the `rdi` register before invoking the +/// CPUID instruction +#define __cpuid_extended1(value, arg0) \ + unsigned int _a __attribute__((unused)) = 0; \ + unsigned int _b __attribute__((unused)) = 0; \ + unsigned int _c __attribute__((unused)) = 0; \ + unsigned int _d __attribute__((unused)) = 0; \ + __asm__ __volatile__("cpuid\n\t" \ + : "=a"(_a), "=b"(_b), "=c"(_c), "=d"(_d) \ + : "a"(value), "D"(arg0)); + +/// __cpuid_extended2 +/// +/// Invoke the CPUID instruction with a specific value `value` in register +/// `rax` and a pseudo-arguments in registers `rdi` and `rsi`. +/// +/// # Arguments +/// +/// - `value`: The value to load into the `rax` register before invoking the +/// CPUID instruction +/// - `arg0`: The value to load into the `rdi` register before invoking the +/// CPUID instruction +/// - `arg1`: The value to load into the `rsi` register before invoking the +/// CPUID instruction +#define __cpuid_extended2(value, arg0, arg1) \ + unsigned int _a __attribute__((unused)) = 0; \ + unsigned int _b __attribute__((unused)) = 0; \ + unsigned int _c __attribute__((unused)) = 0; \ + unsigned int _d __attribute__((unused)) = 0; \ + __asm__ __volatile__("cpuid\n\t" \ + : "=a"(_a), "=b"(_b), "=c"(_c), "=d"(_d) \ + : "a"(value), "D"(arg0), "S"(arg1)); + +/// __cpuid_extended3 +/// +/// Invoke the CPUID instruction with a specific value `value` in register +/// `rax` and a pseudo-arguments in registers `rdi`, `rsi`, and `rdx`. +/// +/// # Arguments +/// +/// - `value`: The value to load into the `rax` register before invoking the +/// CPUID instruction +/// - `arg0`: The value to load into the `rdi` register before invoking the +/// CPUID instruction +/// - `arg1`: The value to load into the `rsi` register before invoking the +/// CPUID instruction +/// - `arg2`: The value to load into the `rdx` register before invoking the +/// CPUID instruction +#define __cpuid_extended3(value, arg0, arg1, arg2) \ + unsigned int _a __attribute__((unused)) = 0; \ + unsigned int _b __attribute__((unused)) = 0; \ + unsigned int _c __attribute__((unused)) = 0; \ + unsigned int _d __attribute__((unused)) = 0; \ + __asm__ __volatile__("cpuid\n\t" \ + : "=a"(_a), "=b"(_b), "=c"(_c), "=d"(_d) \ + : "a"(value), "D"(arg0), "S"(arg1), "d"(arg2)); + +/// __cpuid_extended4 +/// +/// Invoke the CPUID instruction with a specific value `value` in register +/// `rax` and a pseudo-arguments in registers `rdi`, `rsi`, `rdx`, and `rcx`. +/// +/// # Arguments +/// +/// - `value`: The value to load into the `rax` register before invoking the +/// CPUID instruction +/// - `arg0`: The value to load into the `rdi` register before invoking the +/// CPUID instruction +/// - `arg1`: The value to load into the `rsi` register before invoking the +/// CPUID instruction +/// - `arg2`: The value to load into the `rdx` register before invoking the +/// CPUID instruction +/// - `arg3`: The value to load into the `rcx` register before invoking the +/// CPUID instruction +#define __cpuid_extended4(value, arg0, arg1, arg2, arg3) \ + unsigned int _a __attribute__((unused)) = 0; \ + unsigned int _b __attribute__((unused)) = 0; \ + unsigned int _c __attribute__((unused)) = 0; \ + unsigned int _d __attribute__((unused)) = 0; \ + __asm__ __volatile__("cpuid\n\t" \ + : "=a"(_a), "=b"(_b), "=c"(_c), "=d"(_d) \ + : "a"(value), "D"(arg0), "S"(arg1), "d"(arg2), \ + "c"(arg3)); + +/// Magic value defined by SIMICS as the "leaf" value of a CPUID instruction +/// that is treated as a magic instruction. +#define MAGIC (0x4711U) + +/// The default index number used for magic instructions. All magic instructions +/// support multiple start and stop indices, which defaults to 0 if not +/// specified. +#define DEFAULT_INDEX (0x0000U) + +/// Pseudo-hypercall number to signal the fuzzer to use the first argument to +/// the magic instruction as the pointer to the testcase buffer and the second +/// argument as a pointer to the size of the testcase buffer. +#define N_START_BUFFER_PTR_SIZE_PTR (0x0001U) + +/// HARNESS_START +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The default "index" of 0 will be used. If you need multiple start +/// harnesses compiled into the same binary, you can use the +/// `HARNESS_START_INDEX` macro to specify different indices, then enable them +/// at runtime by configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The size of the buffer pointed to by `size_ptr` will be saved as the +/// maximum testcase size. Each fuzzing iteration, the actual size of the +/// current testcase will be written to `*size_ptr`. +/// +/// # Arguments +/// +/// - `buffer`: The pointer to the testcase buffer +/// - `size_ptr`: The pointer to the size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// size_t size; +/// HARNESS_START(buffer, &size); +/// ``` +#define HARNESS_START(buffer, size_ptr) \ + do { \ + unsigned int value = (N_START_BUFFER_PTR_SIZE_PTR << 0x10U) | MAGIC; \ + __cpuid_extended3(value, DEFAULT_INDEX, buffer, size_ptr); \ + } while (0); + +/// HARNESS_START_INDEX +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The index specified by `start_index` will be used. If you need +/// multiple start harnesses compiled into the same binary, you can use this +/// macro to specify different indices, then enable them at runtime by +/// configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The size of the buffer pointed to by `size_ptr` will be saved as the +/// maximum testcase size. Each fuzzing iteration, the actual size of the +/// current testcase will be written to `*size_ptr`. +/// +/// # Arguments +/// +/// - `start_index`: The index to use for this start harness +/// - `buffer`: The pointer to the testcase buffer +/// - `size_ptr`: The pointer to the size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// size_t size; +/// HARNESS_START_INDEX(0x0001U, buffer, &size); +/// ``` +#define HARNESS_START_INDEX(start_index, buffer, size_ptr) \ + do { \ + unsigned int value = (N_START_BUFFER_PTR_SIZE_PTR << 0x10U) | MAGIC; \ + __cpuid_extended3(value, start_index, buffer, size_ptr); \ + } while (0); + +/// Pseudo-hypercall number to signal the fuzzer to use the first argument to +/// the magic instruction as the pointer to the testcase buffer and the second +/// argument as the maximum size of the testcase buffer. +#define N_START_BUFFER_PTR_SIZE_VAL (0x0002U) + +/// HARNESS_START_WITH_MAXIMUM_SIZE +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The default "index" of 0 will be used. If you need multiple start +/// harnesses compiled into the same binary, you can use the +/// `HARNESS_START_WITH_MAXIMUM_SIZE_INDEX` macro to specify different indices, +/// then enable them at runtime by configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The `max_size` value will be saved as the maximum testcase size. Fuzzing +/// test cases will be truncated to this size before being written to the +/// buffer. +/// +/// # Arguments +/// +/// - `buffer`: The pointer to the testcase buffer +/// - `max_size`: The maximum size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// HARNESS_START_WITH_MAXIMUM_SIZE(buffer, 1024); +/// ``` +#define HARNESS_START_WITH_MAXIMUM_SIZE(buffer, max_size) \ + do { \ + unsigned int value = (N_START_BUFFER_PTR_SIZE_VAL << 0x10U) | MAGIC; \ + __cpuid_extended3(value, DEFAULT_INDEX, buffer, max_size); \ + } while (0); + +/// HARNESS_START_WITH_MAXIMUM_SIZE_INDEX +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The index specified by `start_index` will be used. If you need +/// multiple start harnesses compiled into the same binary, you can use this +/// macro to specify different indices, then enable them at runtime by +/// configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The `max_size` value will be saved as the maximum testcase size. Fuzzing +/// test cases will be truncated to this size before being written to the +/// buffer. +/// +/// # Arguments +/// +/// - `start_index`: The index to use for this start harness +/// - `buffer`: The pointer to the testcase buffer +/// - `max_size`: The maximum size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// HARNESS_START_WITH_MAXIMUM_SIZE_INDEX(0x0001U, buffer, 1024); +/// ``` +#define HARNESS_START_WITH_MAXIMUM_SIZE_INDEX(start_index, buffer, max_size) \ + do { \ + unsigned int value = (N_START_BUFFER_PTR_SIZE_VAL << 0x10U) | MAGIC; \ + __cpuid_extended3(value, start_index, buffer, max_size); \ + } while (0); + +/// Pseudo-hypercall number to signal the fuzzer to use the first argument to +/// the magic instruction as the pointer to the testcase buffer, the second +/// argument as a pointer to the size of the testcase buffer, and the third +/// argument as the maximum size of the testcase buffer. +#define N_START_BUFFER_PTR_SIZE_PTR_VAL (0x0003U) + +/// HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The default "index" of 0 will be used. If you need multiple start +/// harnesses compiled into the same binary, you can use the +/// `HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR_INDEX` macro to specify different +/// indices, then enable them at runtime by configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The address `size_ptr` will be saved. Each fuzzing iteration, the actual +/// size of the current testcase will be written to `*size_ptr`. +/// - The `max_size` value will be saved as the maximum testcase size. Fuzzing +/// test cases will be truncated to this size before being written to the +/// buffer. +/// +/// # Arguments +/// +/// - `buffer`: The pointer to the testcase buffer +/// - `size_ptr`: The pointer to the size of the testcase buffer +/// - `max_size`: The maximum size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// size_t size; +/// HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR(buffer, &size, 1024); +/// ``` +#define HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR(buffer, size_ptr, max_size) \ + do { \ + unsigned int value = (N_START_BUFFER_PTR_SIZE_PTR_VAL << 0x10U) | MAGIC; \ + __cpuid_extended4(value, DEFAULT_INDEX, buffer, size_ptr, max_size); \ + } while (0); + +/// HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR_INDEX +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The index specified by `start_index` will be used. If you need +/// multiple start harnesses compiled into the same binary, you can use this +/// macro to specify different indices, then enable them at runtime by +/// configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The address `size_ptr` will be saved. Each fuzzing iteration, the actual +/// size of the current testcase will be written to `*size_ptr`. +/// - The `max_size` value will be saved as the maximum testcase size. Fuzzing +/// test cases will be truncated to this size before being written to the +/// buffer. +/// +/// # Arguments +/// +/// - `start_index`: The index to use for this start harness +/// - `buffer`: The pointer to the testcase buffer +/// - `size_ptr`: The pointer to the size of the testcase buffer +/// - `max_size`: The maximum size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// size_t size; +/// HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR_INDEX(0x0001U, buffer, &size, 1024); +/// ``` +#define HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR_INDEX(start_index, buffer, \ + size_ptr, max_size) \ + do { \ + unsigned int value = (N_START_BUFFER_PTR_SIZE_PTR_VAL << 0x10U) | MAGIC; \ + __cpuid_extended4(value, start_index, buffer, size_ptr, max_size); \ + } while (0); + +/// Pseudo-hypercall number to signal the fuzzer to stop the current fuzzing +/// iteration and reset to the beginning of the fuzzing loop with a "normal" +/// stop status, indicating no solution has occurred. +#define N_STOP_NORMAL (0x0004U) + +/// HARNESS_STOP +/// +/// Signal the fuzzer to stop and reset to the beginning of the fuzzing loop +/// with a "normal" stop status, indicating no solution has occurred. The +/// default index of 0 will be used. If you need to differentiate between +/// multiple stop harnesses compiled into the same binary, you can use the +/// `HARNESS_STOP_INDEX` macro to specify different indices, then enable them at +/// runtime by configuring the fuzzer. +/// +/// # Example +/// +/// ``` +/// HARNESS_STOP(); +/// ``` +#define HARNESS_STOP() \ + do { \ + unsigned int value = (N_STOP_NORMAL << 0x10U) | MAGIC; \ + __cpuid_extended1(value, DEFAULT_INDEX); \ + } while (0); + +/// HARNESS_STOP_INDEX +/// +/// Signal the fuzzer to stop and reset to the beginning of the fuzzing loop +/// with a "normal" stop status, indicating no solution has occurred. The index +/// specified by `stop_index` will be used. If you need to differentiate between +/// multiple stop harnesses compiled into the same binary, you can use this +/// macro to specify different indices, then enable them at runtime by +/// configuring the fuzzer. +/// +/// # Arguments +/// +/// - `stop_index`: The index to use for this stop harness +/// +/// # Example +/// +/// ``` +/// HARNESS_STOP_INDEX(0x0001U); +/// ``` +#define HARNESS_STOP_INDEX(stop_index) \ + do { \ + unsigned int value = (N_STOP_NORMAL << 0x10U) | MAGIC; \ + __cpuid_extended1(value, stop_index); \ + } while (0); + +/// Pseudo-hypercall number to signal the fuzzer that a custom assertion has +/// occurred, and the fuzzer should stop the current fuzzing iteration and reset +/// to the beginning of the fuzzing loop with a "solution" stop status. +#define N_STOP_ASSERT (0x0005U) + +/// HARNESS_ASSERT +/// +/// Signal the fuzzer that a custom assertion has occurred, and the fuzzer +/// should stop the current fuzzing iteration and reset to the beginning of the +/// fuzzing loop with a "solution" stop status. The default index of 0 will be +/// used. If you need to differentiate between multiple assertion harnesses +/// compiled into the same binary, you can use the `HARNESS_ASSERT_INDEX` macro +/// to specify different indices, then enable them at runtime by configuring the +/// fuzzer. +/// +/// # Example +/// +/// ``` +/// HARNESS_ASSERT(); +/// ``` +#define HARNESS_ASSERT() \ + do { \ + unsigned int value = (N_STOP_ASSERT << 0x10U) | MAGIC; \ + __cpuid_extended1(value, DEFAULT_INDEX); \ + } while (0); + +/// HARNESS_ASSERT_INDEX +/// +/// Signal the fuzzer that a custom assertion has occurred, and the fuzzer +/// should stop the current fuzzing iteration and reset to the beginning of the +/// fuzzing loop with a "solution" stop status. The index specified by +/// `assert_index` will be used. If you need to differentiate between multiple +/// assertion harnesses compiled into the same binary, you can use this macro to +/// specify different indices, then enable them at runtime by configuring the +/// fuzzer. +/// +/// # Arguments +/// +/// - `assert_index`: The index to use for this assertion harness +/// +/// # Example +/// +/// ``` +/// HARNESS_ASSERT_INDEX(0x0001U); +/// ``` +#define HARNESS_ASSERT_INDEX(assert_index) \ + do { \ + unsigned int value = (N_STOP_ASSERT << 0x10U) | MAGIC; \ + __cpuid_extended1(value, assert_index); \ + } while (0); + +#endif // TSFFS_H +#elif __riscv && !__LP64__ +// Copyright (C) 2024 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 + +/// Definitions and macros for compiled-in harnessing of C and C++ target +/// software for the RISC-V (32-bit) architecture + +#ifndef TSFFS_H +#define TSFFS_H + +/// Define common with LibFuzzer and other fuzzers to allow code that is +/// fuzzing-specific to be left in the codebase. See +/// https://llvm.org/docs/LibFuzzer.html#id35 for more information +#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION +#define FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION (1) +#endif // FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION + +/// __srai +/// +/// Invoke the magic instruction defined by SIMICS for the RISC-V architecture +/// with a specific value of `n` +/// +/// # Arguments +/// +/// * `value` - The value of `n` to use in the magic instruction +#define __srai(value) \ + __asm__ __volatile__("srai zero, zero, %0" : : "I"(value) :) + +/// __srai_extended1 +/// +/// Invoke the magic instruction defined by SIMICS for the RISC-V architecture +/// with a specific value of `n` and a pseudo-argument in register `a0`. +/// +/// # Arguments +/// +/// * `value` - The value of `n` to use in the magic instruction +/// * `arg0` - The value to place in register `a0` +#define __srai_extended1(value, arg0) \ + __asm__ __volatile__("mv a0, %0; srai zero, zero, %1" \ + : \ + : "r"(arg0), "I"(value) \ + : "a0"); + +/// __srai_extended2 +/// +/// Invoke the magic instruction defined by SIMICS for the RISC-V architecture +/// with a specific value of `n` and pseudo-arguments in registers `a0` and +/// `a1`. +/// +/// # Arguments +/// +/// * `value` - The value of `n` to use in the magic instruction +/// * `arg0` - The value to place in register `a0` +/// * `arg1` - The value to place in register `a1` +#define __srai_extended2(value, arg0, arg1) \ + __asm__ __volatile__("mv a0, %0; mv a1, %1; srai zero, zero, %2" \ + : \ + : "r"(arg0), "r"(arg1), "I"(value) \ + : "a0", "a1"); + +/// __srai_extended3 +/// +/// Invoke the magic instruction defined by SIMICS for the RISC-V architecture +/// with a specific value of `n` and pseudo-arguments in registers `a0`, `a1`, +/// and `a2`. +/// +/// # Arguments +/// +/// * `value` - The value of `n` to use in the magic instruction +/// * `arg0` - The value to place in register `a0` +/// * `arg1` - The value to place in register `a1` +/// * `arg2` - The value to place in register `a2` +#define __srai_extended3(value, arg0, arg1, arg2) \ + __asm__ __volatile__("mv a0, %0; mv a1, %1; mv a2, %2; srai zero, zero, %3" \ + : \ + : "r"(arg0), "r"(arg1), "r"(arg2), "I"(value) \ + : "a0", "a1", "a2"); + +/// __srai_extended4 +/// +/// Invoke the magic instruction defined by SIMICS for the RISC-V architecture +/// with a specific value of `n` and pseudo-arguments in registers `a0`, `a1`, +/// `a2`, and `a3`. +/// +/// # Arguments +/// +/// * `value` - The value of `n` to use in the magic instruction +/// * `arg0` - The value to place in register `a0` +/// * `arg1` - The value to place in register `a1` +/// * `arg2` - The value to place in register `a2` +/// * `arg3` - The value to place in register `a3` +#define __srai_extended4(value, arg0, arg1, arg2, arg3) \ + __asm__ __volatile__( \ + "mv a0, %0; mv a1, %1; mv a2, %2; mv a3, %3; srai zero, zero, %4" \ + : \ + : "r"(arg0), "r"(arg1), "r"(arg2), "r"(arg3), "I"(value) \ + : "a0", "a1", "a2", "a3"); + +/// Magic value defined by SIMICS as the "leaf" value of a CPUID instruction +/// that is treated as a magic instruction. +#define MAGIC (0x4711U) + +/// The default index number used for magic instructions. All magic instructions +/// support multiple start and stop indices, which defaults to 0 if not +/// specified. +#define DEFAULT_INDEX (0x0000U) + +/// Pseudo-hypercall number to signal the fuzzer to use the first argument to +/// the magic instruction as the pointer to the testcase buffer and the second +/// argument as a pointer to the size of the testcase buffer. +#define N_START_BUFFER_PTR_SIZE_PTR (0x0001U) + +/// HARNESS_START +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The default "index" of 0 will be used. If you need multiple start +/// harnesses compiled into the same binary, you can use the +/// `HARNESS_START_INDEX` macro to specify different indices, then enable them +/// at runtime by configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The size of the buffer pointed to by `size_ptr` will be saved as the +/// maximum testcase size. Each fuzzing iteration, the actual size of the +/// current testcase will be written to `*size_ptr`. +/// +/// # Arguments +/// +/// - `buffer`: The pointer to the testcase buffer +/// - `size_ptr`: The pointer to the size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// size_t size; +/// HARNESS_START(buffer, &size); +/// ``` +#define HARNESS_START(buffer, size_ptr) \ + do { \ + __srai_extended3(N_START_BUFFER_PTR_SIZE_PTR, DEFAULT_INDEX, buffer, \ + size_ptr); \ + } while (0); + +/// HARNESS_START_INDEX +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The index specified by `start_index` will be used. If you need +/// multiple start harnesses compiled into the same binary, you can use this +/// macro to specify different indices, then enable them at runtime by +/// configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The size of the buffer pointed to by `size_ptr` will be saved as the +/// maximum testcase size. Each fuzzing iteration, the actual size of the +/// current testcase will be written to `*size_ptr`. +/// +/// # Arguments +/// +/// - `start_index`: The index to use for this start harness +/// - `buffer`: The pointer to the testcase buffer +/// - `size_ptr`: The pointer to the size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// size_t size; +/// HARNESS_START_INDEX(0x0001U, buffer, &size); +/// ``` +#define HARNESS_START_INDEX(start_index, buffer, size_ptr) \ + do { \ + __srai_extended3(N_START_BUFFER_PTR_SIZE_PTR, start_index, buffer, \ + size_ptr); \ + } while (0); + +/// Pseudo-hypercall number to signal the fuzzer to use the first argument to +/// the magic instruction as the pointer to the testcase buffer and the second +/// argument as the maximum size of the testcase buffer. +#define N_START_BUFFER_PTR_SIZE_VAL (0x0002U) + +/// HARNESS_START_WITH_MAXIMUM_SIZE +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The default "index" of 0 will be used. If you need multiple start +/// harnesses compiled into the same binary, you can use the +/// `HARNESS_START_WITH_MAXIMUM_SIZE_INDEX` macro to specify different indices, +/// then enable them at runtime by configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The `max_size` value will be saved as the maximum testcase size. Fuzzing +/// test cases will be truncated to this size before being written to the +/// buffer. +/// +/// # Arguments +/// +/// - `buffer`: The pointer to the testcase buffer +/// - `max_size`: The maximum size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// HARNESS_START_WITH_MAXIMUM_SIZE(buffer, 1024); +/// ``` +#define HARNESS_START_WITH_MAXIMUM_SIZE(buffer, max_size) \ + do { \ + __srai_extended3(N_START_BUFFER_PTR_SIZE_VAL, DEFAULT_INDEX, buffer, \ + max_size); \ + } while (0); + +/// HARNESS_START_WITH_MAXIMUM_SIZE_INDEX +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The index specified by `start_index` will be used. If you need +/// multiple start harnesses compiled into the same binary, you can use this +/// macro to specify different indices, then enable them at runtime by +/// configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The `max_size` value will be saved as the maximum testcase size. Fuzzing +/// test cases will be truncated to this size before being written to the +/// buffer. +/// +/// # Arguments +/// +/// - `start_index`: The index to use for this start harness +/// - `buffer`: The pointer to the testcase buffer +/// - `max_size`: The maximum size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// HARNESS_START_WITH_MAXIMUM_SIZE_INDEX(0x0001U, buffer, 1024); +/// ``` +#define HARNESS_START_WITH_MAXIMUM_SIZE_INDEX(start_index, buffer, max_size) \ + do { \ + __srai_extended3(N_START_BUFFER_PTR_SIZE_VAL, start_index, buffer, \ + max_size); \ + } while (0); + +/// Pseudo-hypercall number to signal the fuzzer to use the first argument to +/// the magic instruction as the pointer to the testcase buffer, the second +/// argument as a pointer to the size of the testcase buffer, and the third +/// argument as the maximum size of the testcase buffer. +#define N_START_BUFFER_PTR_SIZE_PTR_VAL (0x0003U) + +/// HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The default "index" of 0 will be used. If you need multiple start +/// harnesses compiled into the same binary, you can use the +/// `HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR_INDEX` macro to specify different +/// indices, then enable them at runtime by configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The address `size_ptr` will be saved. Each fuzzing iteration, the actual +/// size of the current testcase will be written to `*size_ptr`. +/// - The `max_size` value will be saved as the maximum testcase size. Fuzzing +/// test cases will be truncated to this size before being written to the +/// buffer. +/// +/// # Arguments +/// +/// - `buffer`: The pointer to the testcase buffer +/// - `size_ptr`: The pointer to the size of the testcase buffer +/// - `max_size`: The maximum size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// size_t size; +/// HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR(buffer, &size, 1024); +/// ``` +#define HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR(buffer, size_ptr, max_size) \ + do { \ + __srai_extended4(N_START_BUFFER_PTR_SIZE_PTR_VAL, DEFAULT_INDEX, buffer, \ + size_ptr, max_size); \ + } while (0); + +/// HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR_INDEX +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The index specified by `start_index` will be used. If you need +/// multiple start harnesses compiled into the same binary, you can use this +/// macro to specify different indices, then enable them at runtime by +/// configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The address `size_ptr` will be saved. Each fuzzing iteration, the actual +/// size of the current testcase will be written to `*size_ptr`. +/// - The `max_size` value will be saved as the maximum testcase size. Fuzzing +/// test cases will be truncated to this size before being written to the +/// buffer. +/// +/// # Arguments +/// +/// - `start_index`: The index to use for this start harness +/// - `buffer`: The pointer to the testcase buffer +/// - `size_ptr`: The pointer to the size of the testcase buffer +/// - `max_size`: The maximum size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// size_t size; +/// HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR_INDEX(0x0001U, buffer, &size, 1024); +/// ``` +#define HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR_INDEX(start_index, buffer, \ + size_ptr, max_size) \ + do { \ + __srai_extended4(N_START_BUFFER_PTR_SIZE_PTR_VAL, start_index, buffer, \ + size_ptr, max_size); \ + } while (0); + +/// Pseudo-hypercall number to signal the fuzzer to stop the current fuzzing +/// iteration and reset to the beginning of the fuzzing loop with a "normal" +/// stop status, indicating no solution has occurred. +#define N_STOP_NORMAL (0x0004U) + +/// HARNESS_STOP +/// +/// Signal the fuzzer to stop and reset to the beginning of the fuzzing loop +/// with a "normal" stop status, indicating no solution has occurred. The +/// default index of 0 will be used. If you need to differentiate between +/// multiple stop harnesses compiled into the same binary, you can use the +/// `HARNESS_STOP_INDEX` macro to specify different indices, then enable them at +/// runtime by configuring the fuzzer. +/// +/// # Example +/// +/// ``` +/// HARNESS_STOP(); +/// ``` +#define HARNESS_STOP() \ + do { \ + __srai_extended1(N_STOP_NORMAL, DEFAULT_INDEX); \ + } while (0); + +/// HARNESS_STOP_INDEX +/// +/// Signal the fuzzer to stop and reset to the beginning of the fuzzing loop +/// with a "normal" stop status, indicating no solution has occurred. The index +/// specified by `stop_index` will be used. If you need to differentiate between +/// multiple stop harnesses compiled into the same binary, you can use this +/// macro to specify different indices, then enable them at runtime by +/// configuring the fuzzer. +/// +/// # Arguments +/// +/// - `stop_index`: The index to use for this stop harness +/// +/// # Example +/// +/// ``` +/// HARNESS_STOP_INDEX(0x0001U); +/// ``` +#define HARNESS_STOP_INDEX(stop_index) \ + do { \ + __srai_extended1(N_STOP_NORMAL, stop_index); \ + } while (0); + +/// Pseudo-hypercall number to signal the fuzzer that a custom assertion has +/// occurred, and the fuzzer should stop the current fuzzing iteration and reset +/// to the beginning of the fuzzing loop with a "solution" stop status. +#define N_STOP_ASSERT (0x0005U) + +/// HARNESS_ASSERT +/// +/// Signal the fuzzer that a custom assertion has occurred, and the fuzzer +/// should stop the current fuzzing iteration and reset to the beginning of the +/// fuzzing loop with a "solution" stop status. The default index of 0 will be +/// used. If you need to differentiate between multiple assertion harnesses +/// compiled into the same binary, you can use the `HARNESS_ASSERT_INDEX` macro +/// to specify different indices, then enable them at runtime by configuring the +/// fuzzer. +/// +/// # Example +/// +/// ``` +/// HARNESS_ASSERT(); +/// ``` +#define HARNESS_ASSERT() \ + do { \ + __srai_extended1(N_STOP_ASSERT, DEFAULT_INDEX); \ + } while (0); + +/// HARNESS_ASSERT_INDEX +/// +/// Signal the fuzzer that a custom assertion has occurred, and the fuzzer +/// should stop the current fuzzing iteration and reset to the beginning of the +/// fuzzing loop with a "solution" stop status. The index specified by +/// `assert_index` will be used. If you need to differentiate between multiple +/// assertion harnesses compiled into the same binary, you can use this macro to +/// specify different indices, then enable them at runtime by configuring the +/// fuzzer. +/// +/// # Arguments +/// +/// - `assert_index`: The index to use for this assertion harness +/// +/// # Example +/// +/// ``` +/// HARNESS_ASSERT_INDEX(0x0001U); +/// ``` +#define HARNESS_ASSERT_INDEX(assert_index) \ + do { \ + __srai_extended1(N_STOP_ASSERT, assert_index); \ + } while (0); + +#endif // TSFFS_H +#elif __riscv && __LP64__ +// Copyright (C) 2024 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 + +/// Definitions and macros for compiled-in harnessing of C and C++ target +/// software for the RISC-V (32-bit) architecture + +#ifndef TSFFS_H +#define TSFFS_H + +/// Define common with LibFuzzer and other fuzzers to allow code that is +/// fuzzing-specific to be left in the codebase. See +/// https://llvm.org/docs/LibFuzzer.html#id35 for more information +#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION +#define FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION (1) +#endif // FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION + +/// __srai +/// +/// Invoke the magic instruction defined by SIMICS for the RISC-V architecture +/// with a specific value of `n` +/// +/// # Arguments +/// +/// * `value` - The value of `n` to use in the magic instruction +#define __srai(value) \ + __asm__ __volatile__("srai zero, zero, %0" : : "I"(value) :) + +/// __srai_extended1 +/// +/// Invoke the magic instruction defined by SIMICS for the RISC-V architecture +/// with a specific value of `n` and a pseudo-argument in register `a0`. +/// +/// # Arguments +/// +/// * `value` - The value of `n` to use in the magic instruction +/// * `arg0` - The value to place in register `a0` +#define __srai_extended1(value, arg0) \ + __asm__ __volatile__("mv a0, %0; srai zero, zero, %1" \ + : \ + : "r"(arg0), "I"(value) \ + : "a0"); + +/// __srai_extended2 +/// +/// Invoke the magic instruction defined by SIMICS for the RISC-V architecture +/// with a specific value of `n` and pseudo-arguments in registers `a0` and +/// `a1`. +/// +/// # Arguments +/// +/// * `value` - The value of `n` to use in the magic instruction +/// * `arg0` - The value to place in register `a0` +/// * `arg1` - The value to place in register `a1` +#define __srai_extended2(value, arg0, arg1) \ + __asm__ __volatile__("mv a0, %0; mv a1, %1; srai zero, zero, %2" \ + : \ + : "r"(arg0), "r"(arg1), "I"(value) \ + : "a0", "a1"); + +/// __srai_extended3 +/// +/// Invoke the magic instruction defined by SIMICS for the RISC-V architecture +/// with a specific value of `n` and pseudo-arguments in registers `a0`, `a1`, +/// and `a2`. +/// +/// # Arguments +/// +/// * `value` - The value of `n` to use in the magic instruction +/// * `arg0` - The value to place in register `a0` +/// * `arg1` - The value to place in register `a1` +/// * `arg2` - The value to place in register `a2` +#define __srai_extended3(value, arg0, arg1, arg2) \ + __asm__ __volatile__("mv a0, %0; mv a1, %1; mv a2, %2; srai zero, zero, %3" \ + : \ + : "r"(arg0), "r"(arg1), "r"(arg2), "I"(value) \ + : "a0", "a1", "a2"); + +/// __srai_extended4 +/// +/// Invoke the magic instruction defined by SIMICS for the RISC-V architecture +/// with a specific value of `n` and pseudo-arguments in registers `a0`, `a1`, +/// `a2`, and `a3`. +/// +/// # Arguments +/// +/// * `value` - The value of `n` to use in the magic instruction +/// * `arg0` - The value to place in register `a0` +/// * `arg1` - The value to place in register `a1` +/// * `arg2` - The value to place in register `a2` +/// * `arg3` - The value to place in register `a3` +#define __srai_extended4(value, arg0, arg1, arg2, arg3) \ + __asm__ __volatile__( \ + "mv a0, %0; mv a1, %1; mv a2, %2; mv a3, %3; srai zero, zero, %4" \ + : \ + : "r"(arg0), "r"(arg1), "r"(arg2), "r"(arg3), "I"(value) \ + : "a0", "a1", "a2", "a3"); + +/// Magic value defined by SIMICS as the "leaf" value of a CPUID instruction +/// that is treated as a magic instruction. +#define MAGIC (0x4711U) + +/// The default index number used for magic instructions. All magic instructions +/// support multiple start and stop indices, which defaults to 0 if not +/// specified. +#define DEFAULT_INDEX (0x0000U) + +/// Pseudo-hypercall number to signal the fuzzer to use the first argument to +/// the magic instruction as the pointer to the testcase buffer and the second +/// argument as a pointer to the size of the testcase buffer. +#define N_START_BUFFER_PTR_SIZE_PTR (0x0001U) + +/// HARNESS_START +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The default "index" of 0 will be used. If you need multiple start +/// harnesses compiled into the same binary, you can use the +/// `HARNESS_START_INDEX` macro to specify different indices, then enable them +/// at runtime by configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The size of the buffer pointed to by `size_ptr` will be saved as the +/// maximum testcase size. Each fuzzing iteration, the actual size of the +/// current testcase will be written to `*size_ptr`. +/// +/// # Arguments +/// +/// - `buffer`: The pointer to the testcase buffer +/// - `size_ptr`: The pointer to the size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// size_t size; +/// HARNESS_START(buffer, &size); +/// ``` +#define HARNESS_START(buffer, size_ptr) \ + do { \ + __srai_extended3(N_START_BUFFER_PTR_SIZE_PTR, DEFAULT_INDEX, buffer, size_ptr); \ + } while (0); + +/// HARNESS_START_INDEX +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The index specified by `start_index` will be used. If you need +/// multiple start harnesses compiled into the same binary, you can use this +/// macro to specify different indices, then enable them at runtime by +/// configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The size of the buffer pointed to by `size_ptr` will be saved as the +/// maximum testcase size. Each fuzzing iteration, the actual size of the +/// current testcase will be written to `*size_ptr`. +/// +/// # Arguments +/// +/// - `start_index`: The index to use for this start harness +/// - `buffer`: The pointer to the testcase buffer +/// - `size_ptr`: The pointer to the size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// size_t size; +/// HARNESS_START_INDEX(0x0001U, buffer, &size); +/// ``` +#define HARNESS_START_INDEX(start_index, buffer, size_ptr) \ + do { \ + __srai_extended3(N_START_BUFFER_PTR_SIZE_PTR, start_index, buffer, size_ptr); \ + } while (0); + +/// Pseudo-hypercall number to signal the fuzzer to use the first argument to +/// the magic instruction as the pointer to the testcase buffer and the second +/// argument as the maximum size of the testcase buffer. +#define N_START_BUFFER_PTR_SIZE_VAL (0x0002U) + +/// HARNESS_START_WITH_MAXIMUM_SIZE +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The default "index" of 0 will be used. If you need multiple start +/// harnesses compiled into the same binary, you can use the +/// `HARNESS_START_WITH_MAXIMUM_SIZE_INDEX` macro to specify different indices, +/// then enable them at runtime by configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The `max_size` value will be saved as the maximum testcase size. Fuzzing +/// test cases will be truncated to this size before being written to the +/// buffer. +/// +/// # Arguments +/// +/// - `buffer`: The pointer to the testcase buffer +/// - `max_size`: The maximum size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// HARNESS_START_WITH_MAXIMUM_SIZE(buffer, 1024); +/// ``` +#define HARNESS_START_WITH_MAXIMUM_SIZE(buffer, max_size) \ + do { \ + __srai_extended3(N_START_BUFFER_PTR_SIZE_VAL, DEFAULT_INDEX, buffer, max_size); \ + } while (0); + +/// HARNESS_START_WITH_MAXIMUM_SIZE_INDEX +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The index specified by `start_index` will be used. If you need +/// multiple start harnesses compiled into the same binary, you can use this +/// macro to specify different indices, then enable them at runtime by +/// configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The `max_size` value will be saved as the maximum testcase size. Fuzzing +/// test cases will be truncated to this size before being written to the +/// buffer. +/// +/// # Arguments +/// +/// - `start_index`: The index to use for this start harness +/// - `buffer`: The pointer to the testcase buffer +/// - `max_size`: The maximum size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// HARNESS_START_WITH_MAXIMUM_SIZE_INDEX(0x0001U, buffer, 1024); +/// ``` +#define HARNESS_START_WITH_MAXIMUM_SIZE_INDEX(start_index, buffer, max_size) \ + do { \ + __srai_extended3(N_START_BUFFER_PTR_SIZE_VAL, start_index, buffer, max_size); \ + } while (0); + +/// Pseudo-hypercall number to signal the fuzzer to use the first argument to +/// the magic instruction as the pointer to the testcase buffer, the second +/// argument as a pointer to the size of the testcase buffer, and the third +/// argument as the maximum size of the testcase buffer. +#define N_START_BUFFER_PTR_SIZE_PTR_VAL (0x0003U) + +/// HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The default "index" of 0 will be used. If you need multiple start +/// harnesses compiled into the same binary, you can use the +/// `HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR_INDEX` macro to specify different +/// indices, then enable them at runtime by configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The address `size_ptr` will be saved. Each fuzzing iteration, the actual +/// size of the current testcase will be written to `*size_ptr`. +/// - The `max_size` value will be saved as the maximum testcase size. Fuzzing +/// test cases will be truncated to this size before being written to the +/// buffer. +/// +/// # Arguments +/// +/// - `buffer`: The pointer to the testcase buffer +/// - `size_ptr`: The pointer to the size of the testcase buffer +/// - `max_size`: The maximum size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// size_t size; +/// HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR(buffer, &size, 1024); +/// ``` +#define HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR(buffer, size_ptr, max_size) \ + do { \ + __srai_extended4(N_START_BUFFER_PTR_SIZE_PTR_VAL, DEFAULT_INDEX, buffer, size_ptr, max_size); \ + } while (0); + +/// HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR_INDEX +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The index specified by `start_index` will be used. If you need +/// multiple start harnesses compiled into the same binary, you can use this +/// macro to specify different indices, then enable them at runtime by +/// configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The address `size_ptr` will be saved. Each fuzzing iteration, the actual +/// size of the current testcase will be written to `*size_ptr`. +/// - The `max_size` value will be saved as the maximum testcase size. Fuzzing +/// test cases will be truncated to this size before being written to the +/// buffer. +/// +/// # Arguments +/// +/// - `start_index`: The index to use for this start harness +/// - `buffer`: The pointer to the testcase buffer +/// - `size_ptr`: The pointer to the size of the testcase buffer +/// - `max_size`: The maximum size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// size_t size; +/// HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR_INDEX(0x0001U, buffer, &size, 1024); +/// ``` +#define HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR_INDEX(start_index, buffer, \ + size_ptr, max_size) \ + do { \ + __srai_extended4(N_START_BUFFER_PTR_SIZE_PTR_VAL, start_index, buffer, size_ptr, max_size); \ + } while (0); + +/// Pseudo-hypercall number to signal the fuzzer to stop the current fuzzing +/// iteration and reset to the beginning of the fuzzing loop with a "normal" +/// stop status, indicating no solution has occurred. +#define N_STOP_NORMAL (0x0004U) + +/// HARNESS_STOP +/// +/// Signal the fuzzer to stop and reset to the beginning of the fuzzing loop +/// with a "normal" stop status, indicating no solution has occurred. The +/// default index of 0 will be used. If you need to differentiate between +/// multiple stop harnesses compiled into the same binary, you can use the +/// `HARNESS_STOP_INDEX` macro to specify different indices, then enable them at +/// runtime by configuring the fuzzer. +/// +/// # Example +/// +/// ``` +/// HARNESS_STOP(); +/// ``` +#define HARNESS_STOP() \ + do { \ + __srai_extended1(N_STOP_NORMAL, DEFAULT_INDEX); \ + } while (0); + +/// HARNESS_STOP_INDEX +/// +/// Signal the fuzzer to stop and reset to the beginning of the fuzzing loop +/// with a "normal" stop status, indicating no solution has occurred. The index +/// specified by `stop_index` will be used. If you need to differentiate between +/// multiple stop harnesses compiled into the same binary, you can use this +/// macro to specify different indices, then enable them at runtime by +/// configuring the fuzzer. +/// +/// # Arguments +/// +/// - `stop_index`: The index to use for this stop harness +/// +/// # Example +/// +/// ``` +/// HARNESS_STOP_INDEX(0x0001U); +/// ``` +#define HARNESS_STOP_INDEX(stop_index) \ + do { \ + __srai_extended1(N_STOP_NORMAL, stop_index); \ + } while (0); + +/// Pseudo-hypercall number to signal the fuzzer that a custom assertion has +/// occurred, and the fuzzer should stop the current fuzzing iteration and reset +/// to the beginning of the fuzzing loop with a "solution" stop status. +#define N_STOP_ASSERT (0x0005U) + +/// HARNESS_ASSERT +/// +/// Signal the fuzzer that a custom assertion has occurred, and the fuzzer +/// should stop the current fuzzing iteration and reset to the beginning of the +/// fuzzing loop with a "solution" stop status. The default index of 0 will be +/// used. If you need to differentiate between multiple assertion harnesses +/// compiled into the same binary, you can use the `HARNESS_ASSERT_INDEX` macro +/// to specify different indices, then enable them at runtime by configuring the +/// fuzzer. +/// +/// # Example +/// +/// ``` +/// HARNESS_ASSERT(); +/// ``` +#define HARNESS_ASSERT() \ + do { \ + __srai_extended1(N_STOP_ASSERT, DEFAULT_INDEX); \ + } while (0); + +/// HARNESS_ASSERT_INDEX +/// +/// Signal the fuzzer that a custom assertion has occurred, and the fuzzer +/// should stop the current fuzzing iteration and reset to the beginning of the +/// fuzzing loop with a "solution" stop status. The index specified by +/// `assert_index` will be used. If you need to differentiate between multiple +/// assertion harnesses compiled into the same binary, you can use this macro to +/// specify different indices, then enable them at runtime by configuring the +/// fuzzer. +/// +/// # Arguments +/// +/// - `assert_index`: The index to use for this assertion harness +/// +/// # Example +/// +/// ``` +/// HARNESS_ASSERT_INDEX(0x0001U); +/// ``` +#define HARNESS_ASSERT_INDEX(assert_index) \ + do { \ + __srai_extended1(N_STOP_ASSERT, assert_index); \ + } while (0); + +#endif // TSFFS_H +#elif __aarch64__ +// Copyright (C) 2024 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 + +/// Definitions and macros for compiled-in harnessing of C and C++ target +/// software for the RISC-V (32-bit) architecture + +#ifndef TSFFS_H +#define TSFFS_H + +/// Define common with LibFuzzer and other fuzzers to allow code that is +/// fuzzing-specific to be left in the codebase. See +/// https://llvm.org/docs/LibFuzzer.html#id35 for more information +#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION +#define FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION (1) +#endif // FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION + +#define __stringify(x) #x +#define __tostring(x) __stringify(x) + +/// __orr +/// +/// Invoke the magic instruction defined by SIMICS for the RISC-V architecture +/// with a specific value of `n` +/// +/// # Arguments +/// +/// * `value` - The value of `n` to use in the magic instruction +#define __orr(value) \ + __asm__ __volatile__("orr x" __tostring(value) ", x" __tostring( \ + value) ", x" __tostring(value)); + +/// __orr_extended1 +/// +/// Invoke the magic instruction defined by SIMICS for the RISC-V architecture +/// with a specific value of `n` and a pseudo-argument in register `r10`. +/// +/// # Arguments +/// +/// * `value` - The value of `n` to use in the magic instruction +/// * `arg0` - The value to place in register `r10` +#define __orr_extended1(value, arg0) \ + __asm__ __volatile__( \ + "mov x28, %0; orr x" __tostring(value) ", x" __tostring( \ + value) ", x" __tostring(value) \ + : \ + : "g"(arg0)); + +/// __orr_extended2 +/// +/// Invoke the magic instruction defined by SIMICS for the RISC-V architecture +/// with a specific value of `n` and two pseudo-arguments in registers `r10` and +/// `r9`. +/// +/// # Arguments +/// +/// * `value` - The value of `n` to use in the magic instruction +/// * `arg0` - The value to place in register `r10` +/// * `arg1` - The value to place in register `r9` +#define __orr_extended2(value, arg0, arg1) \ + __asm__ __volatile__( \ + "mov x28, %0; mov x27, %1; orr x" __tostring(value) ", x" __tostring( \ + value) ", x" __tostring(value) \ + : \ + : "r"(arg0), "r"(arg1)); + +/// __orr_extended3 +/// +/// Invoke the magic instruction defined by SIMICS for the RISC-V architecture +/// with a specific value of `n` and three pseudo-arguments in registers `r10`, +/// `r9`, and `r8`. +/// +/// # Arguments +/// +/// * `value` - The value of `n` to use in the magic instruction +/// * `arg0` - The value to place in register `r10` +/// * `arg1` - The value to place in register `r9` +/// * `arg2` - The value to place in register `r8` +#define __orr_extended3(value, arg0, arg1, arg2) \ + __asm__ __volatile__( \ + "mov x28, %0; mov x27, %1; mov x26, %2; orr x" __tostring( \ + value) ", x" __tostring(value) ", x" __tostring(value) \ + : \ + : "r"(arg0), "r"(arg1), "r"(arg2)); + +/// __orr_extended4 +/// +/// Invoke the magic instruction defined by SIMICS for the RISC-V architecture +/// with a specific value of `n` and four pseudo-arguments in registers `r10`, +/// `r9`, `r8`, and `r7`. +/// +/// # Arguments +/// +/// * `value` - The value of `n` to use in the magic instruction +/// * `arg0` - The value to place in register `r10` +/// * `arg1` - The value to place in register `r9` +/// * `arg2` - The value to place in register `r8` +/// * `arg3` - The value to place in register `r7` +#define __orr_extended4(value, arg0, arg1, arg2, arg3) \ + __asm__ __volatile__( \ + "mov x28, %0; mov x27, %1; mov x26, %2; mov x25, %3; " \ + "orr x" __tostring(value) ", x" __tostring(value) ", x" __tostring( \ + value) \ + : \ + : "r"(arg0), "r"(arg1), "r"(arg2), "r"(arg3)); + +/// The default index number used for magic instructions. All magic instructions +/// support multiple start and stop indices, which defaults to 0 if not +/// specified. +#define DEFAULT_INDEX (0x0000U) + +/// Pseudo-hypercall number to signal the fuzzer to use the first argument to +/// the magic instruction as the pointer to the testcase buffer and the second +/// argument as a pointer to the size of the testcase buffer. +#define N_START_BUFFER_PTR_SIZE_PTR 1 + +/// HARNESS_START +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The default "index" of 0 will be used. If you need multiple start +/// harnesses compiled into the same binary, you can use the +/// `HARNESS_START_INDEX` macro to specify different indices, then enable them +/// at runtime by configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The size of the buffer pointed to by `size_ptr` will be saved as the +/// maximum testcase size. Each fuzzing iteration, the actual size of the +/// current testcase will be written to `*size_ptr`. +/// +/// # Arguments +/// +/// - `buffer`: The pointer to the testcase buffer +/// - `size_ptr`: The pointer to the size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// size_t size; +/// HARNESS_START(buffer, &size); +/// ``` +#define HARNESS_START(buffer, size_ptr) \ + do { \ + __orr_extended3(N_START_BUFFER_PTR_SIZE_PTR, DEFAULT_INDEX, buffer, \ + size_ptr); \ + } while (0); + +/// HARNESS_START_INDEX +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The index specified by `start_index` will be used. If you need +/// multiple start harnesses compiled into the same binary, you can use this +/// macro to specify different indices, then enable them at runtime by +/// configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The size of the buffer pointed to by `size_ptr` will be saved as the +/// maximum testcase size. Each fuzzing iteration, the actual size of the +/// current testcase will be written to `*size_ptr`. +/// +/// # Arguments +/// +/// - `start_index`: The index to use for this start harness +/// - `buffer`: The pointer to the testcase buffer +/// - `size_ptr`: The pointer to the size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// size_t size; +/// HARNESS_START_INDEX(0x0001U, buffer, &size); +/// ``` +#define HARNESS_START_INDEX(start_index, buffer, size_ptr) \ + do { \ + __orr_extended3(N_START_BUFFER_PTR_SIZE_PTR, start_index, buffer, \ + size_ptr); \ + } while (0); + +/// Pseudo-hypercall number to signal the fuzzer to use the first argument to +/// the magic instruction as the pointer to the testcase buffer and the second +/// argument as the maximum size of the testcase buffer. +#define N_START_BUFFER_PTR_SIZE_VAL 2 + +/// HARNESS_START_WITH_MAXIMUM_SIZE +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The default "index" of 0 will be used. If you need multiple start +/// harnesses compiled into the same binary, you can use the +/// `HARNESS_START_WITH_MAXIMUM_SIZE_INDEX` macro to specify different indices, +/// then enable them at runtime by configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The `max_size` value will be saved as the maximum testcase size. Fuzzing +/// test cases will be truncated to this size before being written to the +/// buffer. +/// +/// # Arguments +/// +/// - `buffer`: The pointer to the testcase buffer +/// - `max_size`: The maximum size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// HARNESS_START_WITH_MAXIMUM_SIZE(buffer, 1024); +/// ``` +#define HARNESS_START_WITH_MAXIMUM_SIZE(buffer, max_size) \ + do { \ + __orr_extended3(N_START_BUFFER_PTR_SIZE_VAL, DEFAULT_INDEX, buffer, \ + max_size); \ + } while (0); + +/// HARNESS_START_WITH_MAXIMUM_SIZE_INDEX +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The index specified by `start_index` will be used. If you need +/// multiple start harnesses compiled into the same binary, you can use this +/// macro to specify different indices, then enable them at runtime by +/// configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The `max_size` value will be saved as the maximum testcase size. Fuzzing +/// test cases will be truncated to this size before being written to the +/// buffer. +/// +/// # Arguments +/// +/// - `start_index`: The index to use for this start harness +/// - `buffer`: The pointer to the testcase buffer +/// - `max_size`: The maximum size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// HARNESS_START_WITH_MAXIMUM_SIZE_INDEX(0x0001U, buffer, 1024); +/// ``` +#define HARNESS_START_WITH_MAXIMUM_SIZE_INDEX(start_index, buffer, max_size) \ + do { \ + __orr_extended3(N_START_BUFFER_PTR_SIZE_VAL, start_index, buffer, \ + max_size); \ + } while (0); + +/// Pseudo-hypercall number to signal the fuzzer to use the first argument to +/// the magic instruction as the pointer to the testcase buffer, the second +/// argument as a pointer to the size of the testcase buffer, and the third +/// argument as the maximum size of the testcase buffer. +#define N_START_BUFFER_PTR_SIZE_PTR_VAL 3 + +/// HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The default "index" of 0 will be used. If you need multiple start +/// harnesses compiled into the same binary, you can use the +/// `HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR_INDEX` macro to specify different +/// indices, then enable them at runtime by configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The address `size_ptr` will be saved. Each fuzzing iteration, the actual +/// size of the current testcase will be written to `*size_ptr`. +/// - The `max_size` value will be saved as the maximum testcase size. Fuzzing +/// test cases will be truncated to this size before being written to the +/// buffer. +/// +/// # Arguments +/// +/// - `buffer`: The pointer to the testcase buffer +/// - `size_ptr`: The pointer to the size of the testcase buffer +/// - `max_size`: The maximum size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// size_t size; +/// HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR(buffer, &size, 1024); +/// ``` +#define HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR(buffer, size_ptr, max_size) \ + do { \ + __orr_extended4(N_START_BUFFER_PTR_SIZE_PTR_VAL, DEFAULT_INDEX, buffer, \ + size_ptr, max_size); \ + } while (0); + +/// HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR_INDEX +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The index specified by `start_index` will be used. If you need +/// multiple start harnesses compiled into the same binary, you can use this +/// macro to specify different indices, then enable them at runtime by +/// configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The address `size_ptr` will be saved. Each fuzzing iteration, the actual +/// size of the current testcase will be written to `*size_ptr`. +/// - The `max_size` value will be saved as the maximum testcase size. Fuzzing +/// test cases will be truncated to this size before being written to the +/// buffer. +/// +/// # Arguments +/// +/// - `start_index`: The index to use for this start harness +/// - `buffer`: The pointer to the testcase buffer +/// - `size_ptr`: The pointer to the size of the testcase buffer +/// - `max_size`: The maximum size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// size_t size; +/// HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR_INDEX(0x0001U, buffer, &size, 1024); +/// ``` +#define HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR_INDEX(start_index, buffer, \ + size_ptr, max_size) \ + do { \ + __orr_extended4(N_START_BUFFER_PTR_SIZE_PTR_VAL, start_index, buffer, \ + size_ptr, max_size); \ + } while (0); + +/// Pseudo-hypercall number to signal the fuzzer to stop the current fuzzing +/// iteration and reset to the beginning of the fuzzing loop with a "normal" +/// stop status, indicating no solution has occurred. +#define N_STOP_NORMAL 4 + +/// HARNESS_STOP +/// +/// Signal the fuzzer to stop and reset to the beginning of the fuzzing loop +/// with a "normal" stop status, indicating no solution has occurred. The +/// default index of 0 will be used. If you need to differentiate between +/// multiple stop harnesses compiled into the same binary, you can use the +/// `HARNESS_STOP_INDEX` macro to specify different indices, then enable them at +/// runtime by configuring the fuzzer. +/// +/// # Example +/// +/// ``` +/// HARNESS_STOP(); +/// ``` +#define HARNESS_STOP() \ + do { \ + __orr_extended1(N_STOP_NORMAL, DEFAULT_INDEX); \ + } while (0); + +/// HARNESS_STOP_INDEX +/// +/// Signal the fuzzer to stop and reset to the beginning of the fuzzing loop +/// with a "normal" stop status, indicating no solution has occurred. The index +/// specified by `stop_index` will be used. If you need to differentiate between +/// multiple stop harnesses compiled into the same binary, you can use this +/// macro to specify different indices, then enable them at runtime by +/// configuring the fuzzer. +/// +/// # Arguments +/// +/// - `stop_index`: The index to use for this stop harness +/// +/// # Example +/// +/// ``` +/// HARNESS_STOP_INDEX(0x0001U); +/// ``` +#define HARNESS_STOP_INDEX(stop_index) \ + do { \ + __orr_extended1(N_STOP_NORMAL, stop_index); \ + } while (0); + +/// Pseudo-hypercall number to signal the fuzzer that a custom assertion has +/// occurred, and the fuzzer should stop the current fuzzing iteration and reset +/// to the beginning of the fuzzing loop with a "solution" stop status. +#define N_STOP_ASSERT 5 + +/// HARNESS_ASSERT +/// +/// Signal the fuzzer that a custom assertion has occurred, and the fuzzer +/// should stop the current fuzzing iteration and reset to the beginning of the +/// fuzzing loop with a "solution" stop status. The default index of 0 will be +/// used. If you need to differentiate between multiple assertion harnesses +/// compiled into the same binary, you can use the `HARNESS_ASSERT_INDEX` macro +/// to specify different indices, then enable them at runtime by configuring the +/// fuzzer. +/// +/// # Example +/// +/// ``` +/// HARNESS_ASSERT(); +/// ``` +#define HARNESS_ASSERT() \ + do { \ + __orr_extended1(N_STOP_ASSERT, DEFAULT_INDEX); \ + } while (0); + +/// HARNESS_ASSERT_INDEX +/// +/// Signal the fuzzer that a custom assertion has occurred, and the fuzzer +/// should stop the current fuzzing iteration and reset to the beginning of the +/// fuzzing loop with a "solution" stop status. The index specified by +/// `assert_index` will be used. If you need to differentiate between multiple +/// assertion harnesses compiled into the same binary, you can use this macro to +/// specify different indices, then enable them at runtime by configuring the +/// fuzzer. +/// +/// # Arguments +/// +/// - `assert_index`: The index to use for this assertion harness +/// +/// # Example +/// +/// ``` +/// HARNESS_ASSERT_INDEX(0x0001U); +/// ``` +#define HARNESS_ASSERT_INDEX(assert_index) \ + do { \ + __orr_extended1(N_STOP_ASSERT, assert_index); \ + } while (0); + +#endif // TSFFS_H +#elif __arm__ +// Copyright (C) 2024 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 + +/// Definitions and macros for compiled-in harnessing of C and C++ target +/// software for the RISC-V (32-bit) architecture + +#ifndef TSFFS_H +#define TSFFS_H + +/// Define common with LibFuzzer and other fuzzers to allow code that is +/// fuzzing-specific to be left in the codebase. See +/// https://llvm.org/docs/LibFuzzer.html#id35 for more information +#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION +#define FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION (1) +#endif // FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION + +#define __stringify(x) #x +#define __tostring(x) __stringify(x) + +/// __orr +/// +/// Invoke the magic instruction defined by SIMICS for the RISC-V architecture +/// with a specific value of `n` +/// +/// # Arguments +/// +/// * `value` - The value of `n` to use in the magic instruction +#define __orr(value) \ + __asm__ __volatile__("orr r" __tostring(value) ", r" __tostring( \ + value) ", r" __tostring(value)); + +/// __orr_extended1 +/// +/// Invoke the magic instruction defined by SIMICS for the RISC-V architecture +/// with a specific value of `n` and a pseudo-argument in register `r10`. +/// +/// # Arguments +/// +/// * `value` - The value of `n` to use in the magic instruction +/// * `arg0` - The value to place in register `r10` +#define __orr_extended1(value, arg0) \ + __asm__ __volatile__( \ + "mov r10, %0; orr r" __tostring(value) ", r" __tostring( \ + value) ", r" __tostring(value) \ + : \ + : "r"(arg0)); + +/// __orr_extended2 +/// +/// Invoke the magic instruction defined by SIMICS for the RISC-V architecture +/// with a specific value of `n` and two pseudo-arguments in registers `r10` and +/// `r9`. +/// +/// # Arguments +/// +/// * `value` - The value of `n` to use in the magic instruction +/// * `arg0` - The value to place in register `r10` +/// * `arg1` - The value to place in register `r9` +#define __orr_extended2(value, arg0, arg1) \ + __asm__ __volatile__( \ + "mov r10, %0; mov r9, %1; orr r" __tostring(value) ", r" __tostring( \ + value) ", r" __tostring(value) \ + : \ + : "r"(arg0), "r"(arg1)); + +/// __orr_extended3 +/// +/// Invoke the magic instruction defined by SIMICS for the RISC-V architecture +/// with a specific value of `n` and three pseudo-arguments in registers `r10`, +/// `r9`, and `r8`. +/// +/// # Arguments +/// +/// * `value` - The value of `n` to use in the magic instruction +/// * `arg0` - The value to place in register `r10` +/// * `arg1` - The value to place in register `r9` +/// * `arg2` - The value to place in register `r8` +#define __orr_extended3(value, arg0, arg1, arg2) \ + __asm__ __volatile__( \ + "mov r10, %0; mov r9, %1; mov r8, %2; orr r" __tostring( \ + value) ", r" __tostring(value) ", r" __tostring(value) \ + : \ + : "r"(arg0), "r"(arg1), "r"(arg2)); + +/// __orr_extended4 +/// +/// Invoke the magic instruction defined by SIMICS for the RISC-V architecture +/// with a specific value of `n` and four pseudo-arguments in registers `r10`, +/// `r9`, `r8`, and `r7`. +/// +/// # Arguments +/// +/// * `value` - The value of `n` to use in the magic instruction +/// * `arg0` - The value to place in register `r10` +/// * `arg1` - The value to place in register `r9` +/// * `arg2` - The value to place in register `r8` +/// * `arg3` - The value to place in register `r7` +#define __orr_extended4(value, arg0, arg1, arg2, arg3) \ + __asm__ __volatile__( \ + "mov r10, %0; mov r9, %1; mov r8, %2; mov r7, %3; " \ + "orr r" __tostring(value) ", r" __tostring(value) ", r" __tostring( \ + value) \ + : \ + : "r"(arg0), "r"(arg1), "r"(arg2), "r"(arg3)); + +/// Magic value defined by SIMICS as the "leaf" value of a CPUID instruction +/// that is treated as a magic instruction. +#define MAGIC (0x4711U) + +/// The default index number used for magic instructions. All magic instructions +/// support multiple start and stop indices, which defaults to 0 if not +/// specified. +#define DEFAULT_INDEX (0x0000U) + +/// Pseudo-hypercall number to signal the fuzzer to use the first argument to +/// the magic instruction as the pointer to the testcase buffer and the second +/// argument as a pointer to the size of the testcase buffer. +#define N_START_BUFFER_PTR_SIZE_PTR 1 + +/// HARNESS_START +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The default "index" of 0 will be used. If you need multiple start +/// harnesses compiled into the same binary, you can use the +/// `HARNESS_START_INDEX` macro to specify different indices, then enable them +/// at runtime by configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The size of the buffer pointed to by `size_ptr` will be saved as the +/// maximum testcase size. Each fuzzing iteration, the actual size of the +/// current testcase will be written to `*size_ptr`. +/// +/// # Arguments +/// +/// - `buffer`: The pointer to the testcase buffer +/// - `size_ptr`: The pointer to the size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// size_t size; +/// HARNESS_START(buffer, &size); +/// ``` +#define HARNESS_START(buffer, size_ptr) \ + do { \ + __orr_extended2(N_START_BUFFER_PTR_SIZE_PTR, buffer, size_ptr); \ + } while (0); + +/// Pseudo-hypercall number to signal the fuzzer to use the first argument to +/// the magic instruction as the pointer to the testcase buffer and the second +/// argument as the maximum size of the testcase buffer. +#define N_START_BUFFER_PTR_SIZE_VAL 2 + +/// HARNESS_START_WITH_MAXIMUM_SIZE +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The default "index" of 0 will be used. If you need multiple start +/// harnesses compiled into the same binary, you can use the +/// `HARNESS_START_WITH_MAXIMUM_SIZE_INDEX` macro to specify different indices, +/// then enable them at runtime by configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The `max_size` value will be saved as the maximum testcase size. Fuzzing +/// test cases will be truncated to this size before being written to the +/// buffer. +/// +/// # Arguments +/// +/// - `buffer`: The pointer to the testcase buffer +/// - `max_size`: The maximum size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// HARNESS_START_WITH_MAXIMUM_SIZE(buffer, 1024); +/// ``` +#define HARNESS_START_WITH_MAXIMUM_SIZE(buffer, max_size) \ + do { \ + __orr_extended2(N_START_BUFFER_PTR_SIZE_VAL, buffer, max_size); \ + } while (0); + +/// Pseudo-hypercall number to signal the fuzzer to use the first argument to +/// the magic instruction as the pointer to the testcase buffer, the second +/// argument as a pointer to the size of the testcase buffer, and the third +/// argument as the maximum size of the testcase buffer. +#define N_START_BUFFER_PTR_SIZE_PTR_VAL 3 + +/// HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The default "index" of 0 will be used. If you need multiple start +/// harnesses compiled into the same binary, you can use the +/// `HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR_INDEX` macro to specify different +/// indices, then enable them at runtime by configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The address `size_ptr` will be saved. Each fuzzing iteration, the actual +/// size of the current testcase will be written to `*size_ptr`. +/// - The `max_size` value will be saved as the maximum testcase size. Fuzzing +/// test cases will be truncated to this size before being written to the +/// buffer. +/// +/// # Arguments +/// +/// - `buffer`: The pointer to the testcase buffer +/// - `size_ptr`: The pointer to the size of the testcase buffer +/// - `max_size`: The maximum size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// size_t size; +/// HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR(buffer, &size, 1024); +/// ``` +#define HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR(buffer, size_ptr, max_size) \ + do { \ + __orr_extended3(N_START_BUFFER_PTR_SIZE_PTR_VAL, buffer, size_ptr, \ + max_size); \ + } while (0); + +/// Pseudo-hypercall number to signal the fuzzer to stop the current fuzzing +/// iteration and reset to the beginning of the fuzzing loop with a "normal" +/// stop status, indicating no solution has occurred. +#define N_STOP_NORMAL 4 + +/// HARNESS_STOP +/// +/// Signal the fuzzer to stop and reset to the beginning of the fuzzing loop +/// with a "normal" stop status, indicating no solution has occurred. The +/// default index of 0 will be used. If you need to differentiate between +/// multiple stop harnesses compiled into the same binary, you can use the +/// `HARNESS_STOP_INDEX` macro to specify different indices, then enable them at +/// runtime by configuring the fuzzer. +/// +/// # Example +/// +/// ``` +/// HARNESS_STOP(); +/// ``` +#define HARNESS_STOP() \ + do { \ + __orr(N_STOP_NORMAL); \ + } while (0); + +/// Pseudo-hypercall number to signal the fuzzer that a custom assertion has +/// occurred, and the fuzzer should stop the current fuzzing iteration and reset +/// to the beginning of the fuzzing loop with a "solution" stop status. +#define N_STOP_ASSERT 5 + +/// HARNESS_ASSERT +/// +/// Signal the fuzzer that a custom assertion has occurred, and the fuzzer +/// should stop the current fuzzing iteration and reset to the beginning of the +/// fuzzing loop with a "solution" stop status. The default index of 0 will be +/// used. If you need to differentiate between multiple assertion harnesses +/// compiled into the same binary, you can use the `HARNESS_ASSERT_INDEX` macro +/// to specify different indices, then enable them at runtime by configuring the +/// fuzzer. +/// +/// # Example +/// +/// ``` +/// HARNESS_ASSERT(); +/// ``` +#define HARNESS_ASSERT() \ + do { \ + __orr_extended1(N_STOP_ASSERT, DEFAULT_INDEX); \ + } while (0); + +#endif // TSFFS_H +#else +#error "Unsupported platform!" +#endif +#elif _MSC_VER +#ifndef TSFFS_H +#define TSFFS_H + +/// Define common with LibFuzzer and other fuzzers to allow code that is +/// fuzzing-specific to be left in the codebase. See +/// https://llvm.org/docs/LibFuzzer.html#id35 for more information +#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION +#define FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION (1) +#endif // FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION + +#include + +/// HARNESS_START +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The default "index" of 0 will be used. If you need multiple start +/// harnesses compiled into the same binary, you can use the +/// `HARNESS_START_INDEX` macro to specify different indices, then enable them +/// at runtime by configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The size of the buffer pointed to by `size_ptr` will be saved as the +/// maximum testcase size. Each fuzzing iteration, the actual size of the +/// current testcase will be written to `*size_ptr`. +/// +/// # Arguments +/// +/// - `buffer`: The pointer to the testcase buffer +/// - `size_ptr`: The pointer to the size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// size_t size; +/// HARNESS_START(buffer, &size); +/// ``` +void HARNESS_START(void *buffer, void *size_ptr); + +/// HARNESS_START_INDEX +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The index specified by `start_index` will be used. If you need +/// multiple start harnesses compiled into the same binary, you can use this +/// macro to specify different indices, then enable them at runtime by +/// configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The size of the buffer pointed to by `size_ptr` will be saved as the +/// maximum testcase size. Each fuzzing iteration, the actual size of the +/// current testcase will be written to `*size_ptr`. +/// +/// # Arguments +/// +/// - `start_index`: The index to use for this start harness +/// - `buffer`: The pointer to the testcase buffer +/// - `size_ptr`: The pointer to the size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// size_t size; +/// HARNESS_START_INDEX(0x0001U, buffer, &size); +/// ``` +void HARNESS_START_INDEX(size_t start_index, void *buffer, void *size_ptr); + +/// HARNESS_START_WITH_MAXIMUM_SIZE +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The default "index" of 0 will be used. If you need multiple start +/// harnesses compiled into the same binary, you can use the +/// `HARNESS_START_WITH_MAXIMUM_SIZE_INDEX` macro to specify different indices, +/// then enable them at runtime by configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The `max_size` value will be saved as the maximum testcase size. Fuzzing +/// test cases will be truncated to this size before being written to the +/// buffer. +/// +/// # Arguments +/// +/// - `buffer`: The pointer to the testcase buffer +/// - `max_size`: The maximum size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// HARNESS_START_WITH_MAXIMUM_SIZE(buffer, 1024); +/// ``` +void HARNESS_START_WITH_MAXIMUM_SIZE(void *buffer, size_t max_size); + +/// HARNESS_START_WITH_MAXIMUM_SIZE_INDEX +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The index specified by `start_index` will be used. If you need +/// multiple start harnesses compiled into the same binary, you can use this +/// macro to specify different indices, then enable them at runtime by +/// configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The `max_size` value will be saved as the maximum testcase size. Fuzzing +/// test cases will be truncated to this size before being written to the +/// buffer. +/// +/// # Arguments +/// +/// - `start_index`: The index to use for this start harness +/// - `buffer`: The pointer to the testcase buffer +/// - `max_size`: The maximum size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// HARNESS_START_WITH_MAXIMUM_SIZE_INDEX(0x0001U, buffer, 1024); +/// ``` +void HARNESS_START_WITH_MAXIMUM_SIZE_INDEX(size_t start_index, void *buffer, + size_t max_size); + +/// HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The default "index" of 0 will be used. If you need multiple start +/// harnesses compiled into the same binary, you can use the +/// `HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR_INDEX` macro to specify different +/// indices, then enable them at runtime by configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The address `size_ptr` will be saved. Each fuzzing iteration, the actual +/// size of the current testcase will be written to `*size_ptr`. +/// - The `max_size` value will be saved as the maximum testcase size. Fuzzing +/// test cases will be truncated to this size before being written to the +/// buffer. +/// +/// # Arguments +/// +/// - `buffer`: The pointer to the testcase buffer +/// - `size_ptr`: The pointer to the size of the testcase buffer +/// - `max_size`: The maximum size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// size_t size; +/// HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR(buffer, &size, 1024); +/// ``` +void HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR(void *buffer, void *size_ptr, + size_t max_size); + +/// HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR_INDEX +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The index specified by `start_index` will be used. If you need +/// multiple start harnesses compiled into the same binary, you can use this +/// macro to specify different indices, then enable them at runtime by +/// configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The address `size_ptr` will be saved. Each fuzzing iteration, the actual +/// size of the current testcase will be written to `*size_ptr`. +/// - The `max_size` value will be saved as the maximum testcase size. Fuzzing +/// test cases will be truncated to this size before being written to the +/// buffer. +/// +/// # Arguments +/// +/// - `start_index`: The index to use for this start harness +/// - `buffer`: The pointer to the testcase buffer +/// - `size_ptr`: The pointer to the size of the testcase buffer +/// - `max_size`: The maximum size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// size_t size; +/// HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR_INDEX(0x0001U, buffer, &size, 1024); +/// ``` +void HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR_INDEX(size_t start_index, + void *buffer, void *size_ptr, + size_t max_size); + +/// HARNESS_STOP +/// +/// Signal the fuzzer to stop and reset to the beginning of the fuzzing loop +/// with a "normal" stop status, indicating no solution has occurred. The +/// default index of 0 will be used. If you need to differentiate between +/// multiple stop harnesses compiled into the same binary, you can use the +/// `HARNESS_STOP_INDEX` macro to specify different indices, then enable them at +/// runtime by configuring the fuzzer. +/// +/// # Example +/// +/// ``` +/// HARNESS_STOP(); +/// ``` +void HARNESS_STOP(void); + +/// HARNESS_STOP_INDEX +/// +/// Signal the fuzzer to stop and reset to the beginning of the fuzzing loop +/// with a "normal" stop status, indicating no solution has occurred. The index +/// specified by `stop_index` will be used. If you need to differentiate between +/// multiple stop harnesses compiled into the same binary, you can use this +/// macro to specify different indices, then enable them at runtime by +/// configuring the fuzzer. +/// +/// # Arguments +/// +/// - `stop_index`: The index to use for this stop harness +/// +/// # Example +/// +/// ``` +/// HARNESS_STOP_INDEX(0x0001U); +/// ``` +void HARNESS_STOP_INDEX(size_t stop_index); + +/// HARNESS_ASSERT +/// +/// Signal the fuzzer that a custom assertion has occurred, and the fuzzer +/// should stop the current fuzzing iteration and reset to the beginning of the +/// fuzzing loop with a "solution" stop status. The default index of 0 will be +/// used. If you need to differentiate between multiple assertion harnesses +/// compiled into the same binary, you can use the `HARNESS_ASSERT_INDEX` macro +/// to specify different indices, then enable them at runtime by configuring the +/// fuzzer. +/// +/// # Example +/// +/// ``` +/// HARNESS_ASSERT(); +/// ``` +void HARNESS_ASSERT(void); + +/// HARNESS_ASSERT_INDEX +/// +/// Signal the fuzzer that a custom assertion has occurred, and the fuzzer +/// should stop the current fuzzing iteration and reset to the beginning of the +/// fuzzing loop with a "solution" stop status. The index specified by +/// `assert_index` will be used. If you need to differentiate between multiple +/// assertion harnesses compiled into the same binary, you can use this macro to +/// specify different indices, then enable them at runtime by configuring the +/// fuzzer. +/// +/// # Arguments +/// +/// - `assert_index`: The index to use for this assertion harness +/// +/// # Example +/// +/// ``` +/// HARNESS_ASSERT_INDEX(0x0001U); +/// ``` +void HARNESS_ASSERT_INDEX(size_t assert_index); + +#endif // TSFFS_H +#else +#error "Unsupported compiler!" +#endif diff --git a/examples/tutorials/risc-v-kernel/src/tsffs-gcc-riscv64.h b/examples/tutorials/risc-v-kernel/src/tsffs-gcc-riscv64.h deleted file mode 100644 index f6acf1ea..00000000 --- a/examples/tutorials/risc-v-kernel/src/tsffs-gcc-riscv64.h +++ /dev/null @@ -1,124 +0,0 @@ -// Copyright (C) 2024 Intel Corporation -// SPDX-License-Identifier: Apache-2.0 - -/// Definitions and macros for compiled-in harnessing of C and C++ target -/// software for the RISC-V (64-bit) architecture -/// -/// Example: -/// ```c -/// #include "tsffs-gcc-riscv64.h" -/// -/// int main() { -/// char buf[0x10]; -/// size_t size = 0x10; -/// size_t *size_ptr = &size; -/// HARNESS_START((char *)buf, size_ptr); -/// int retval = YourSpecialDecoder(buf, *size_ptr); -/// if (!retval) { -/// HARNESS_ASSERT(); -/// } else { -/// HARNESS_STOP(); -/// } -/// } -/// ``` - -#ifndef TSFFS_H -#define TSFFS_H - -/// Define common with LibFuzzer and other fuzzers to allow code that is -/// fuzzing-specific to be left in the codebase. See -/// https://llvm.org/docs/LibFuzzer.html#id35 for more information -#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION -#define FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION (1) -#endif // FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION - -/// Default magic instruction `n` value to signal the TSFFS fuzzer to start the -/// fuzzing loop -#define MAGIC_START (0x0001U) -/// Default magic instruction `n` value to signal the TSFFS fuzzer to stop and -/// reset the fuzzing loop -#define MAGIC_STOP (0x0002U) -/// Default magic instruction `n` value to signal the TSFFS fuzzer that an error -/// has occurred and the testcase input should be saved as a solution -#define MAGIC_ASSERT (0x0003U) - -/// Alternative magic numbers that can be used for start and stop events in -/// conjunction with setting the magic number for each event via the SIMICS or -/// SIMICS Python script interface -#define MAGIC_ALT_0 (0x0004U) -#define MAGIC_ALT_1 (0x0005U) -#define MAGIC_ALT_2 (0x0006U) -#define MAGIC_ALT_3 (0x0007U) -#define MAGIC_ALT_4 (0x0008U) -#define MAGIC_ALT_5 (0x0009U) -#define MAGIC_ALT_6 (0x000aU) -#define MAGIC_ALT_7 (0x000bU) - -/// Invoke the magic instruction defined by SIMICS for the RISC-V architecture -/// (`srai zero, zero, n`) with a specific value of `n`, after setting register -/// `a0` to the value of the pointer to the testcase and register `a1` to the -/// value of the pointer to the testcase size. These registers are accessed by -/// the fuzzer and are defined per-architecture. -#define __srai_extended(value, testcase_ptr, size_ptr) \ - __asm__ __volatile__("mv a0, %0; mv a1, %1; srai zero, zero, %2" \ - : \ - : "r"(testcase_ptr), "r"(size_ptr), "I"(value) \ - : "a0", "a1"); - -/// Invoke the magic instruction defined by SIMICS for the RISC-V architecture -/// (`srai zero, zero, n`) with a specific value of `n` -#define __srai(value) \ - __asm__ __volatile__("srai zero, zero, %0" : : "I"(value) :) - -/// Signal the fuzzer using a specific magic value `start` to start the fuzzing -/// loop at the point this macro is called. A snapshot will be taken when the -/// macro is called, and the maximum testcase size `*size_ptr` will be saved as -/// `max_testcase_size`. Each iteration of the fuzzing loop, the fuzzer input -/// (the "testcase") will be written to `*testcase_ptr` as if running -/// `memcpy(testcase_ptr, current_testcase, max_testcase_size)`, and the actual -/// size of the current testcase will be written to -/// `*size_ptr` as if running `*size_ptr = current_testcase_size`. -#define __arch_harness_start(start, testcase_ptr, size_ptr) \ - do { \ - __srai_extended(start, testcase_ptr, size_ptr); \ - } while (0) - -/// Signal the fuzzer using the a specific magic value (`stop`) to stop and -/// reset to the beginning of the fuzzing loop with a "normal" stop status, -/// indicating no solution has occurred. -#define __arch_harness_stop(stop) \ - do { \ - __srai(stop); \ - } while (0) - -/// Signal the fuzzer using the default magic value to start the fuzzing loop at -/// the point this macro is called. A snapshot will be taken when the macro is -/// called, and the maximum testcase size `*size_ptr` will be saved as -/// `max_testcase_size`. Each iteration of the fuzzing loop, the fuzzer input -/// (the "testcase") will be written to -/// `*testcase_ptr` as if running `memcpy(testcase_ptr, current_testcase, -/// max_testcase_size)`, and the actual size of the current testcase will be -/// written to -/// `*size_ptr` as if running `*size_ptr = current_testcase_size`. -#define HARNESS_START(testcase_ptr, size_ptr) \ - do { \ - __arch_harness_start(MAGIC_START, testcase_ptr, size_ptr); \ - } while (0) - -/// Signal the fuzzer using the default magic value to stop and reset to the -/// beginning of the fuzzing loop with a "normal" stop status, indicating no -/// solution has occurred. -#define HARNESS_STOP() \ - do { \ - __arch_harness_stop(MAGIC_STOP); \ - } while (0) - -/// Signal the fuzzer using the default magic value to stop and reset to the -/// beginning of the fuzzing loop with a "solution" stop status, indicating some -/// custom error has occurred. -#define HARNESS_ASSERT() \ - do { \ - __arch_harness_stop(MAGIC_ASSERT); \ - } while (0) - -#endif // TSFFS_H \ No newline at end of file diff --git a/examples/tutorials/risc-v-kernel/src/tsffs.h b/examples/tutorials/risc-v-kernel/src/tsffs.h new file mode 100644 index 00000000..95b604fd --- /dev/null +++ b/examples/tutorials/risc-v-kernel/src/tsffs.h @@ -0,0 +1,2876 @@ +// Copyright (C) 2024 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 + +#ifdef __GNUC__ +#ifdef __i386__ +// Copyright (C) 2024 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 + +/// Definitions and macros for compiled-in harnessing of C and C++ target +/// software for the X86 architecture. + +#ifndef TSFFS_H +#define TSFFS_H + +/// Define common with LibFuzzer and other fuzzers to allow code that is +/// fuzzing-specific to be left in the codebase. See +/// https://llvm.org/docs/LibFuzzer.html#id35 for more information +#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION +#define FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION (1) +#endif // FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION + +/// __cpuid +/// +/// Invoke the CPUID instruction with a specific value `value` in register +/// `rax`. +/// +/// # Arguments +/// +/// - `value`: The value to load into the `rax` register before invoking the +/// CPUID instruction +#define __cpuid(value) \ + unsigned int _a __attribute__((unused)) = 0; \ + unsigned int _b __attribute__((unused)) = 0; \ + unsigned int _c __attribute__((unused)) = 0; \ + unsigned int _d __attribute__((unused)) = 0; \ + __asm__ __volatile__("cpuid\n\t" \ + : "=a"(_a), "=b"(_b), "=c"(_c), "=d"(_d) \ + : "a"(value)); + +/// __cpuid_extended1 +/// +/// Invoke the CPUID instruction with a specific value `value` in register +/// `rax` and a pseudo-argument in register `rdi`. +/// +/// # Arguments +/// +/// - `value`: The value to load into the `rax` register before invoking the +/// CPUID instruction +/// - `arg0`: The value to load into the `rdi` register before invoking the +/// CPUID instruction +#define __cpuid_extended1(value, arg0) \ + unsigned int _a __attribute__((unused)) = 0; \ + unsigned int _b __attribute__((unused)) = 0; \ + unsigned int _c __attribute__((unused)) = 0; \ + unsigned int _d __attribute__((unused)) = 0; \ + __asm__ __volatile__("cpuid\n\t" \ + : "=a"(_a), "=b"(_b), "=c"(_c), "=d"(_d) \ + : "a"(value), "D"(arg0)); + +/// __cpuid_extended2 +/// +/// Invoke the CPUID instruction with a specific value `value` in register +/// `rax` and a pseudo-arguments in registers `rdi` and `rsi`. +/// +/// # Arguments +/// +/// - `value`: The value to load into the `rax` register before invoking the +/// CPUID instruction +/// - `arg0`: The value to load into the `rdi` register before invoking the +/// CPUID instruction +/// - `arg1`: The value to load into the `rsi` register before invoking the +/// CPUID instruction +#define __cpuid_extended2(value, arg0, arg1) \ + unsigned int _a __attribute__((unused)) = 0; \ + unsigned int _b __attribute__((unused)) = 0; \ + unsigned int _c __attribute__((unused)) = 0; \ + unsigned int _d __attribute__((unused)) = 0; \ + __asm__ __volatile__("cpuid\n\t" \ + : "=a"(_a), "=b"(_b), "=c"(_c), "=d"(_d) \ + : "a"(value), "D"(arg0), "S"(arg1)); + +/// __cpuid_extended3 +/// +/// Invoke the CPUID instruction with a specific value `value` in register +/// `rax` and a pseudo-arguments in registers `rdi`, `rsi`, and `rdx`. +/// +/// # Arguments +/// +/// - `value`: The value to load into the `rax` register before invoking the +/// CPUID instruction +/// - `arg0`: The value to load into the `rdi` register before invoking the +/// CPUID instruction +/// - `arg1`: The value to load into the `rsi` register before invoking the +/// CPUID instruction +/// - `arg2`: The value to load into the `rdx` register before invoking the +/// CPUID instruction +#define __cpuid_extended3(value, arg0, arg1, arg2) \ + unsigned int _a __attribute__((unused)) = 0; \ + unsigned int _b __attribute__((unused)) = 0; \ + unsigned int _c __attribute__((unused)) = 0; \ + unsigned int _d __attribute__((unused)) = 0; \ + __asm__ __volatile__("cpuid\n\t" \ + : "=a"(_a), "=b"(_b), "=c"(_c), "=d"(_d) \ + : "a"(value), "D"(arg0), "S"(arg1), "d"(arg2)); + +/// __cpuid_extended4 +/// +/// Invoke the CPUID instruction with a specific value `value` in register +/// `rax` and a pseudo-arguments in registers `rdi`, `rsi`, `rdx`, and `rcx`. +/// +/// # Arguments +/// +/// - `value`: The value to load into the `rax` register before invoking the +/// CPUID instruction +/// - `arg0`: The value to load into the `rdi` register before invoking the +/// CPUID instruction +/// - `arg1`: The value to load into the `rsi` register before invoking the +/// CPUID instruction +/// - `arg2`: The value to load into the `rdx` register before invoking the +/// CPUID instruction +/// - `arg3`: The value to load into the `rcx` register before invoking the +/// CPUID instruction +#define __cpuid_extended4(value, arg0, arg1, arg2, arg3) \ + unsigned int _a __attribute__((unused)) = 0; \ + unsigned int _b __attribute__((unused)) = 0; \ + unsigned int _c __attribute__((unused)) = 0; \ + unsigned int _d __attribute__((unused)) = 0; \ + __asm__ __volatile__("cpuid\n\t" \ + : "=a"(_a), "=b"(_b), "=c"(_c), "=d"(_d) \ + : "a"(value), "D"(arg0), "S"(arg1), "d"(arg2), \ + "c"(arg3)); + +/// Magic value defined by SIMICS as the "leaf" value of a CPUID instruction +/// that is treated as a magic instruction. +#define MAGIC (0x4711U) + +/// The default index number used for magic instructions. All magic instructions +/// support multiple start and stop indices, which defaults to 0 if not +/// specified. +#define DEFAULT_INDEX (0x0000U) + +/// Pseudo-hypercall number to signal the fuzzer to use the first argument to +/// the magic instruction as the pointer to the testcase buffer and the second +/// argument as a pointer to the size of the testcase buffer. +#define N_START_BUFFER_PTR_SIZE_PTR (0x0001U) + +/// HARNESS_START +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The default "index" of 0 will be used. If you need multiple start +/// harnesses compiled into the same binary, you can use the +/// `HARNESS_START_INDEX` macro to specify different indices, then enable them +/// at runtime by configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The size of the buffer pointed to by `size_ptr` will be saved as the +/// maximum testcase size. Each fuzzing iteration, the actual size of the +/// current testcase will be written to `*size_ptr`. +/// +/// # Arguments +/// +/// - `buffer`: The pointer to the testcase buffer +/// - `size_ptr`: The pointer to the size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// size_t size; +/// HARNESS_START(buffer, &size); +/// ``` +#define HARNESS_START(buffer, size_ptr) \ + do { \ + unsigned int value = (N_START_BUFFER_PTR_SIZE_PTR << 0x10U) | MAGIC; \ + __cpuid_extended3(value, DEFAULT_INDEX, buffer, size_ptr); \ + } while (0); + +/// HARNESS_START_INDEX +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The index specified by `start_index` will be used. If you need +/// multiple start harnesses compiled into the same binary, you can use this +/// macro to specify different indices, then enable them at runtime by +/// configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The size of the buffer pointed to by `size_ptr` will be saved as the +/// maximum testcase size. Each fuzzing iteration, the actual size of the +/// current testcase will be written to `*size_ptr`. +/// +/// # Arguments +/// +/// - `start_index`: The index to use for this start harness +/// - `buffer`: The pointer to the testcase buffer +/// - `size_ptr`: The pointer to the size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// size_t size; +/// HARNESS_START_INDEX(0x0001U, buffer, &size); +/// ``` +#define HARNESS_START_INDEX(start_index, buffer, size_ptr) \ + do { \ + unsigned int value = (N_START_BUFFER_PTR_SIZE_PTR << 0x10U) | MAGIC; \ + __cpuid_extended3(value, start_index, buffer, size_ptr); \ + } while (0); + +/// Pseudo-hypercall number to signal the fuzzer to use the first argument to +/// the magic instruction as the pointer to the testcase buffer and the second +/// argument as the maximum size of the testcase buffer. +#define N_START_BUFFER_PTR_SIZE_VAL (0x0002U) + +/// HARNESS_START_WITH_MAXIMUM_SIZE +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The default "index" of 0 will be used. If you need multiple start +/// harnesses compiled into the same binary, you can use the +/// `HARNESS_START_WITH_MAXIMUM_SIZE_INDEX` macro to specify different indices, +/// then enable them at runtime by configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The `max_size` value will be saved as the maximum testcase size. Fuzzing +/// test cases will be truncated to this size before being written to the +/// buffer. +/// +/// # Arguments +/// +/// - `buffer`: The pointer to the testcase buffer +/// - `max_size`: The maximum size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// HARNESS_START_WITH_MAXIMUM_SIZE(buffer, 1024); +/// ``` +#define HARNESS_START_WITH_MAXIMUM_SIZE(buffer, max_size) \ + do { \ + unsigned int value = (N_START_BUFFER_PTR_SIZE_VAL << 0x10U) | MAGIC; \ + __cpuid_extended3(value, DEFAULT_INDEX, buffer, max_size); \ + } while (0); + +/// HARNESS_START_WITH_MAXIMUM_SIZE_INDEX +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The index specified by `start_index` will be used. If you need +/// multiple start harnesses compiled into the same binary, you can use this +/// macro to specify different indices, then enable them at runtime by +/// configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The `max_size` value will be saved as the maximum testcase size. Fuzzing +/// test cases will be truncated to this size before being written to the +/// buffer. +/// +/// # Arguments +/// +/// - `start_index`: The index to use for this start harness +/// - `buffer`: The pointer to the testcase buffer +/// - `max_size`: The maximum size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// HARNESS_START_WITH_MAXIMUM_SIZE_INDEX(0x0001U, buffer, 1024); +/// ``` +#define HARNESS_START_WITH_MAXIMUM_SIZE_INDEX(start_index, buffer, max_size) \ + do { \ + unsigned int value = (N_START_BUFFER_PTR_SIZE_VAL << 0x10U) | MAGIC; \ + __cpuid_extended3(value, start_index, buffer, max_size); \ + } while (0); + +/// Pseudo-hypercall number to signal the fuzzer to use the first argument to +/// the magic instruction as the pointer to the testcase buffer, the second +/// argument as a pointer to the size of the testcase buffer, and the third +/// argument as the maximum size of the testcase buffer. +#define N_START_BUFFER_PTR_SIZE_PTR_VAL (0x0003U) + +/// HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The default "index" of 0 will be used. If you need multiple start +/// harnesses compiled into the same binary, you can use the +/// `HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR_INDEX` macro to specify different +/// indices, then enable them at runtime by configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The address `size_ptr` will be saved. Each fuzzing iteration, the actual +/// size of the current testcase will be written to `*size_ptr`. +/// - The `max_size` value will be saved as the maximum testcase size. Fuzzing +/// test cases will be truncated to this size before being written to the +/// buffer. +/// +/// # Arguments +/// +/// - `buffer`: The pointer to the testcase buffer +/// - `size_ptr`: The pointer to the size of the testcase buffer +/// - `max_size`: The maximum size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// size_t size; +/// HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR(buffer, &size, 1024); +/// ``` +#define HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR(buffer, size_ptr, max_size) \ + do { \ + unsigned int value = (N_START_BUFFER_PTR_SIZE_PTR_VAL << 0x10U) | MAGIC; \ + __cpuid_extended4(value, DEFAULT_INDEX, buffer, size_ptr, max_size); \ + } while (0); + +/// HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR_INDEX +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The index specified by `start_index` will be used. If you need +/// multiple start harnesses compiled into the same binary, you can use this +/// macro to specify different indices, then enable them at runtime by +/// configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The address `size_ptr` will be saved. Each fuzzing iteration, the actual +/// size of the current testcase will be written to `*size_ptr`. +/// - The `max_size` value will be saved as the maximum testcase size. Fuzzing +/// test cases will be truncated to this size before being written to the +/// buffer. +/// +/// # Arguments +/// +/// - `start_index`: The index to use for this start harness +/// - `buffer`: The pointer to the testcase buffer +/// - `size_ptr`: The pointer to the size of the testcase buffer +/// - `max_size`: The maximum size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// size_t size; +/// HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR_INDEX(0x0001U, buffer, &size, 1024); +/// ``` +#define HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR_INDEX(start_index, buffer, \ + size_ptr, max_size) \ + do { \ + unsigned int value = (N_START_BUFFER_PTR_SIZE_PTR_VAL << 0x10U) | MAGIC; \ + __cpuid_extended4(value, start_index, buffer, size_ptr, max_size); \ + } while (0); + +/// Pseudo-hypercall number to signal the fuzzer to stop the current fuzzing +/// iteration and reset to the beginning of the fuzzing loop with a "normal" +/// stop status, indicating no solution has occurred. +#define N_STOP_NORMAL (0x0004U) + +/// HARNESS_STOP +/// +/// Signal the fuzzer to stop and reset to the beginning of the fuzzing loop +/// with a "normal" stop status, indicating no solution has occurred. The +/// default index of 0 will be used. If you need to differentiate between +/// multiple stop harnesses compiled into the same binary, you can use the +/// `HARNESS_STOP_INDEX` macro to specify different indices, then enable them at +/// runtime by configuring the fuzzer. +/// +/// # Example +/// +/// ``` +/// HARNESS_STOP(); +/// ``` +#define HARNESS_STOP() \ + do { \ + unsigned int value = (N_STOP_NORMAL << 0x10U) | MAGIC; \ + __cpuid_extended1(value, DEFAULT_INDEX); \ + } while (0); + +/// HARNESS_STOP_INDEX +/// +/// Signal the fuzzer to stop and reset to the beginning of the fuzzing loop +/// with a "normal" stop status, indicating no solution has occurred. The index +/// specified by `stop_index` will be used. If you need to differentiate between +/// multiple stop harnesses compiled into the same binary, you can use this +/// macro to specify different indices, then enable them at runtime by +/// configuring the fuzzer. +/// +/// # Arguments +/// +/// - `stop_index`: The index to use for this stop harness +/// +/// # Example +/// +/// ``` +/// HARNESS_STOP_INDEX(0x0001U); +/// ``` +#define HARNESS_STOP_INDEX(stop_index) \ + do { \ + unsigned int value = (N_STOP_NORMAL << 0x10U) | MAGIC; \ + __cpuid_extended1(value, stop_index); \ + } while (0); + +/// Pseudo-hypercall number to signal the fuzzer that a custom assertion has +/// occurred, and the fuzzer should stop the current fuzzing iteration and reset +/// to the beginning of the fuzzing loop with a "solution" stop status. +#define N_STOP_ASSERT (0x0005U) + +/// HARNESS_ASSERT +/// +/// Signal the fuzzer that a custom assertion has occurred, and the fuzzer +/// should stop the current fuzzing iteration and reset to the beginning of the +/// fuzzing loop with a "solution" stop status. The default index of 0 will be +/// used. If you need to differentiate between multiple assertion harnesses +/// compiled into the same binary, you can use the `HARNESS_ASSERT_INDEX` macro +/// to specify different indices, then enable them at runtime by configuring the +/// fuzzer. +/// +/// # Example +/// +/// ``` +/// HARNESS_ASSERT(); +/// ``` +#define HARNESS_ASSERT() \ + do { \ + unsigned int value = (N_STOP_ASSERT << 0x10U) | MAGIC; \ + __cpuid_extended1(value, DEFAULT_INDEX); \ + } while (0); + +/// HARNESS_ASSERT_INDEX +/// +/// Signal the fuzzer that a custom assertion has occurred, and the fuzzer +/// should stop the current fuzzing iteration and reset to the beginning of the +/// fuzzing loop with a "solution" stop status. The index specified by +/// `assert_index` will be used. If you need to differentiate between multiple +/// assertion harnesses compiled into the same binary, you can use this macro to +/// specify different indices, then enable them at runtime by configuring the +/// fuzzer. +/// +/// # Arguments +/// +/// - `assert_index`: The index to use for this assertion harness +/// +/// # Example +/// +/// ``` +/// HARNESS_ASSERT_INDEX(0x0001U); +/// ``` +#define HARNESS_ASSERT_INDEX(assert_index) \ + do { \ + unsigned int value = (N_STOP_ASSERT << 0x10U) | MAGIC; \ + __cpuid_extended1(value, assert_index); \ + } while (0); + +#endif // TSFFS_H +#elif __x86_64__ +// Copyright (C) 2024 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 + +/// Definitions and macros for compiled-in harnessing of C and C++ target +/// software for the x86_64 architecture. + +#ifndef TSFFS_H +#define TSFFS_H + +/// Define common with LibFuzzer and other fuzzers to allow code that is +/// fuzzing-specific to be left in the codebase. See +/// https://llvm.org/docs/LibFuzzer.html#id35 for more information +#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION +#define FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION (1) +#endif // FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION + +/// __cpuid +/// +/// Invoke the CPUID instruction with a specific value `value` in register +/// `rax`. +/// +/// # Arguments +/// +/// - `value`: The value to load into the `rax` register before invoking the +/// CPUID instruction +#define __cpuid(value) \ + unsigned int _a __attribute__((unused)) = 0; \ + unsigned int _b __attribute__((unused)) = 0; \ + unsigned int _c __attribute__((unused)) = 0; \ + unsigned int _d __attribute__((unused)) = 0; \ + __asm__ __volatile__("cpuid\n\t" \ + : "=a"(_a), "=b"(_b), "=c"(_c), "=d"(_d) \ + : "a"(value)); + +/// __cpuid_extended1 +/// +/// Invoke the CPUID instruction with a specific value `value` in register +/// `rax` and a pseudo-argument in register `rdi`. +/// +/// # Arguments +/// +/// - `value`: The value to load into the `rax` register before invoking the +/// CPUID instruction +/// - `arg0`: The value to load into the `rdi` register before invoking the +/// CPUID instruction +#define __cpuid_extended1(value, arg0) \ + unsigned int _a __attribute__((unused)) = 0; \ + unsigned int _b __attribute__((unused)) = 0; \ + unsigned int _c __attribute__((unused)) = 0; \ + unsigned int _d __attribute__((unused)) = 0; \ + __asm__ __volatile__("cpuid\n\t" \ + : "=a"(_a), "=b"(_b), "=c"(_c), "=d"(_d) \ + : "a"(value), "D"(arg0)); + +/// __cpuid_extended2 +/// +/// Invoke the CPUID instruction with a specific value `value` in register +/// `rax` and a pseudo-arguments in registers `rdi` and `rsi`. +/// +/// # Arguments +/// +/// - `value`: The value to load into the `rax` register before invoking the +/// CPUID instruction +/// - `arg0`: The value to load into the `rdi` register before invoking the +/// CPUID instruction +/// - `arg1`: The value to load into the `rsi` register before invoking the +/// CPUID instruction +#define __cpuid_extended2(value, arg0, arg1) \ + unsigned int _a __attribute__((unused)) = 0; \ + unsigned int _b __attribute__((unused)) = 0; \ + unsigned int _c __attribute__((unused)) = 0; \ + unsigned int _d __attribute__((unused)) = 0; \ + __asm__ __volatile__("cpuid\n\t" \ + : "=a"(_a), "=b"(_b), "=c"(_c), "=d"(_d) \ + : "a"(value), "D"(arg0), "S"(arg1)); + +/// __cpuid_extended3 +/// +/// Invoke the CPUID instruction with a specific value `value` in register +/// `rax` and a pseudo-arguments in registers `rdi`, `rsi`, and `rdx`. +/// +/// # Arguments +/// +/// - `value`: The value to load into the `rax` register before invoking the +/// CPUID instruction +/// - `arg0`: The value to load into the `rdi` register before invoking the +/// CPUID instruction +/// - `arg1`: The value to load into the `rsi` register before invoking the +/// CPUID instruction +/// - `arg2`: The value to load into the `rdx` register before invoking the +/// CPUID instruction +#define __cpuid_extended3(value, arg0, arg1, arg2) \ + unsigned int _a __attribute__((unused)) = 0; \ + unsigned int _b __attribute__((unused)) = 0; \ + unsigned int _c __attribute__((unused)) = 0; \ + unsigned int _d __attribute__((unused)) = 0; \ + __asm__ __volatile__("cpuid\n\t" \ + : "=a"(_a), "=b"(_b), "=c"(_c), "=d"(_d) \ + : "a"(value), "D"(arg0), "S"(arg1), "d"(arg2)); + +/// __cpuid_extended4 +/// +/// Invoke the CPUID instruction with a specific value `value` in register +/// `rax` and a pseudo-arguments in registers `rdi`, `rsi`, `rdx`, and `rcx`. +/// +/// # Arguments +/// +/// - `value`: The value to load into the `rax` register before invoking the +/// CPUID instruction +/// - `arg0`: The value to load into the `rdi` register before invoking the +/// CPUID instruction +/// - `arg1`: The value to load into the `rsi` register before invoking the +/// CPUID instruction +/// - `arg2`: The value to load into the `rdx` register before invoking the +/// CPUID instruction +/// - `arg3`: The value to load into the `rcx` register before invoking the +/// CPUID instruction +#define __cpuid_extended4(value, arg0, arg1, arg2, arg3) \ + unsigned int _a __attribute__((unused)) = 0; \ + unsigned int _b __attribute__((unused)) = 0; \ + unsigned int _c __attribute__((unused)) = 0; \ + unsigned int _d __attribute__((unused)) = 0; \ + __asm__ __volatile__("cpuid\n\t" \ + : "=a"(_a), "=b"(_b), "=c"(_c), "=d"(_d) \ + : "a"(value), "D"(arg0), "S"(arg1), "d"(arg2), \ + "c"(arg3)); + +/// Magic value defined by SIMICS as the "leaf" value of a CPUID instruction +/// that is treated as a magic instruction. +#define MAGIC (0x4711U) + +/// The default index number used for magic instructions. All magic instructions +/// support multiple start and stop indices, which defaults to 0 if not +/// specified. +#define DEFAULT_INDEX (0x0000U) + +/// Pseudo-hypercall number to signal the fuzzer to use the first argument to +/// the magic instruction as the pointer to the testcase buffer and the second +/// argument as a pointer to the size of the testcase buffer. +#define N_START_BUFFER_PTR_SIZE_PTR (0x0001U) + +/// HARNESS_START +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The default "index" of 0 will be used. If you need multiple start +/// harnesses compiled into the same binary, you can use the +/// `HARNESS_START_INDEX` macro to specify different indices, then enable them +/// at runtime by configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The size of the buffer pointed to by `size_ptr` will be saved as the +/// maximum testcase size. Each fuzzing iteration, the actual size of the +/// current testcase will be written to `*size_ptr`. +/// +/// # Arguments +/// +/// - `buffer`: The pointer to the testcase buffer +/// - `size_ptr`: The pointer to the size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// size_t size; +/// HARNESS_START(buffer, &size); +/// ``` +#define HARNESS_START(buffer, size_ptr) \ + do { \ + unsigned int value = (N_START_BUFFER_PTR_SIZE_PTR << 0x10U) | MAGIC; \ + __cpuid_extended3(value, DEFAULT_INDEX, buffer, size_ptr); \ + } while (0); + +/// HARNESS_START_INDEX +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The index specified by `start_index` will be used. If you need +/// multiple start harnesses compiled into the same binary, you can use this +/// macro to specify different indices, then enable them at runtime by +/// configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The size of the buffer pointed to by `size_ptr` will be saved as the +/// maximum testcase size. Each fuzzing iteration, the actual size of the +/// current testcase will be written to `*size_ptr`. +/// +/// # Arguments +/// +/// - `start_index`: The index to use for this start harness +/// - `buffer`: The pointer to the testcase buffer +/// - `size_ptr`: The pointer to the size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// size_t size; +/// HARNESS_START_INDEX(0x0001U, buffer, &size); +/// ``` +#define HARNESS_START_INDEX(start_index, buffer, size_ptr) \ + do { \ + unsigned int value = (N_START_BUFFER_PTR_SIZE_PTR << 0x10U) | MAGIC; \ + __cpuid_extended3(value, start_index, buffer, size_ptr); \ + } while (0); + +/// Pseudo-hypercall number to signal the fuzzer to use the first argument to +/// the magic instruction as the pointer to the testcase buffer and the second +/// argument as the maximum size of the testcase buffer. +#define N_START_BUFFER_PTR_SIZE_VAL (0x0002U) + +/// HARNESS_START_WITH_MAXIMUM_SIZE +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The default "index" of 0 will be used. If you need multiple start +/// harnesses compiled into the same binary, you can use the +/// `HARNESS_START_WITH_MAXIMUM_SIZE_INDEX` macro to specify different indices, +/// then enable them at runtime by configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The `max_size` value will be saved as the maximum testcase size. Fuzzing +/// test cases will be truncated to this size before being written to the +/// buffer. +/// +/// # Arguments +/// +/// - `buffer`: The pointer to the testcase buffer +/// - `max_size`: The maximum size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// HARNESS_START_WITH_MAXIMUM_SIZE(buffer, 1024); +/// ``` +#define HARNESS_START_WITH_MAXIMUM_SIZE(buffer, max_size) \ + do { \ + unsigned int value = (N_START_BUFFER_PTR_SIZE_VAL << 0x10U) | MAGIC; \ + __cpuid_extended3(value, DEFAULT_INDEX, buffer, max_size); \ + } while (0); + +/// HARNESS_START_WITH_MAXIMUM_SIZE_INDEX +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The index specified by `start_index` will be used. If you need +/// multiple start harnesses compiled into the same binary, you can use this +/// macro to specify different indices, then enable them at runtime by +/// configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The `max_size` value will be saved as the maximum testcase size. Fuzzing +/// test cases will be truncated to this size before being written to the +/// buffer. +/// +/// # Arguments +/// +/// - `start_index`: The index to use for this start harness +/// - `buffer`: The pointer to the testcase buffer +/// - `max_size`: The maximum size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// HARNESS_START_WITH_MAXIMUM_SIZE_INDEX(0x0001U, buffer, 1024); +/// ``` +#define HARNESS_START_WITH_MAXIMUM_SIZE_INDEX(start_index, buffer, max_size) \ + do { \ + unsigned int value = (N_START_BUFFER_PTR_SIZE_VAL << 0x10U) | MAGIC; \ + __cpuid_extended3(value, start_index, buffer, max_size); \ + } while (0); + +/// Pseudo-hypercall number to signal the fuzzer to use the first argument to +/// the magic instruction as the pointer to the testcase buffer, the second +/// argument as a pointer to the size of the testcase buffer, and the third +/// argument as the maximum size of the testcase buffer. +#define N_START_BUFFER_PTR_SIZE_PTR_VAL (0x0003U) + +/// HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The default "index" of 0 will be used. If you need multiple start +/// harnesses compiled into the same binary, you can use the +/// `HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR_INDEX` macro to specify different +/// indices, then enable them at runtime by configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The address `size_ptr` will be saved. Each fuzzing iteration, the actual +/// size of the current testcase will be written to `*size_ptr`. +/// - The `max_size` value will be saved as the maximum testcase size. Fuzzing +/// test cases will be truncated to this size before being written to the +/// buffer. +/// +/// # Arguments +/// +/// - `buffer`: The pointer to the testcase buffer +/// - `size_ptr`: The pointer to the size of the testcase buffer +/// - `max_size`: The maximum size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// size_t size; +/// HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR(buffer, &size, 1024); +/// ``` +#define HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR(buffer, size_ptr, max_size) \ + do { \ + unsigned int value = (N_START_BUFFER_PTR_SIZE_PTR_VAL << 0x10U) | MAGIC; \ + __cpuid_extended4(value, DEFAULT_INDEX, buffer, size_ptr, max_size); \ + } while (0); + +/// HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR_INDEX +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The index specified by `start_index` will be used. If you need +/// multiple start harnesses compiled into the same binary, you can use this +/// macro to specify different indices, then enable them at runtime by +/// configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The address `size_ptr` will be saved. Each fuzzing iteration, the actual +/// size of the current testcase will be written to `*size_ptr`. +/// - The `max_size` value will be saved as the maximum testcase size. Fuzzing +/// test cases will be truncated to this size before being written to the +/// buffer. +/// +/// # Arguments +/// +/// - `start_index`: The index to use for this start harness +/// - `buffer`: The pointer to the testcase buffer +/// - `size_ptr`: The pointer to the size of the testcase buffer +/// - `max_size`: The maximum size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// size_t size; +/// HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR_INDEX(0x0001U, buffer, &size, 1024); +/// ``` +#define HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR_INDEX(start_index, buffer, \ + size_ptr, max_size) \ + do { \ + unsigned int value = (N_START_BUFFER_PTR_SIZE_PTR_VAL << 0x10U) | MAGIC; \ + __cpuid_extended4(value, start_index, buffer, size_ptr, max_size); \ + } while (0); + +/// Pseudo-hypercall number to signal the fuzzer to stop the current fuzzing +/// iteration and reset to the beginning of the fuzzing loop with a "normal" +/// stop status, indicating no solution has occurred. +#define N_STOP_NORMAL (0x0004U) + +/// HARNESS_STOP +/// +/// Signal the fuzzer to stop and reset to the beginning of the fuzzing loop +/// with a "normal" stop status, indicating no solution has occurred. The +/// default index of 0 will be used. If you need to differentiate between +/// multiple stop harnesses compiled into the same binary, you can use the +/// `HARNESS_STOP_INDEX` macro to specify different indices, then enable them at +/// runtime by configuring the fuzzer. +/// +/// # Example +/// +/// ``` +/// HARNESS_STOP(); +/// ``` +#define HARNESS_STOP() \ + do { \ + unsigned int value = (N_STOP_NORMAL << 0x10U) | MAGIC; \ + __cpuid_extended1(value, DEFAULT_INDEX); \ + } while (0); + +/// HARNESS_STOP_INDEX +/// +/// Signal the fuzzer to stop and reset to the beginning of the fuzzing loop +/// with a "normal" stop status, indicating no solution has occurred. The index +/// specified by `stop_index` will be used. If you need to differentiate between +/// multiple stop harnesses compiled into the same binary, you can use this +/// macro to specify different indices, then enable them at runtime by +/// configuring the fuzzer. +/// +/// # Arguments +/// +/// - `stop_index`: The index to use for this stop harness +/// +/// # Example +/// +/// ``` +/// HARNESS_STOP_INDEX(0x0001U); +/// ``` +#define HARNESS_STOP_INDEX(stop_index) \ + do { \ + unsigned int value = (N_STOP_NORMAL << 0x10U) | MAGIC; \ + __cpuid_extended1(value, stop_index); \ + } while (0); + +/// Pseudo-hypercall number to signal the fuzzer that a custom assertion has +/// occurred, and the fuzzer should stop the current fuzzing iteration and reset +/// to the beginning of the fuzzing loop with a "solution" stop status. +#define N_STOP_ASSERT (0x0005U) + +/// HARNESS_ASSERT +/// +/// Signal the fuzzer that a custom assertion has occurred, and the fuzzer +/// should stop the current fuzzing iteration and reset to the beginning of the +/// fuzzing loop with a "solution" stop status. The default index of 0 will be +/// used. If you need to differentiate between multiple assertion harnesses +/// compiled into the same binary, you can use the `HARNESS_ASSERT_INDEX` macro +/// to specify different indices, then enable them at runtime by configuring the +/// fuzzer. +/// +/// # Example +/// +/// ``` +/// HARNESS_ASSERT(); +/// ``` +#define HARNESS_ASSERT() \ + do { \ + unsigned int value = (N_STOP_ASSERT << 0x10U) | MAGIC; \ + __cpuid_extended1(value, DEFAULT_INDEX); \ + } while (0); + +/// HARNESS_ASSERT_INDEX +/// +/// Signal the fuzzer that a custom assertion has occurred, and the fuzzer +/// should stop the current fuzzing iteration and reset to the beginning of the +/// fuzzing loop with a "solution" stop status. The index specified by +/// `assert_index` will be used. If you need to differentiate between multiple +/// assertion harnesses compiled into the same binary, you can use this macro to +/// specify different indices, then enable them at runtime by configuring the +/// fuzzer. +/// +/// # Arguments +/// +/// - `assert_index`: The index to use for this assertion harness +/// +/// # Example +/// +/// ``` +/// HARNESS_ASSERT_INDEX(0x0001U); +/// ``` +#define HARNESS_ASSERT_INDEX(assert_index) \ + do { \ + unsigned int value = (N_STOP_ASSERT << 0x10U) | MAGIC; \ + __cpuid_extended1(value, assert_index); \ + } while (0); + +#endif // TSFFS_H +#elif __riscv && !__LP64__ +// Copyright (C) 2024 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 + +/// Definitions and macros for compiled-in harnessing of C and C++ target +/// software for the RISC-V (32-bit) architecture + +#ifndef TSFFS_H +#define TSFFS_H + +/// Define common with LibFuzzer and other fuzzers to allow code that is +/// fuzzing-specific to be left in the codebase. See +/// https://llvm.org/docs/LibFuzzer.html#id35 for more information +#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION +#define FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION (1) +#endif // FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION + +/// __srai +/// +/// Invoke the magic instruction defined by SIMICS for the RISC-V architecture +/// with a specific value of `n` +/// +/// # Arguments +/// +/// * `value` - The value of `n` to use in the magic instruction +#define __srai(value) \ + __asm__ __volatile__("srai zero, zero, %0" : : "I"(value) :) + +/// __srai_extended1 +/// +/// Invoke the magic instruction defined by SIMICS for the RISC-V architecture +/// with a specific value of `n` and a pseudo-argument in register `a0`. +/// +/// # Arguments +/// +/// * `value` - The value of `n` to use in the magic instruction +/// * `arg0` - The value to place in register `a0` +#define __srai_extended1(value, arg0) \ + __asm__ __volatile__("mv a0, %0; srai zero, zero, %1" \ + : \ + : "r"(arg0), "I"(value) \ + : "a0"); + +/// __srai_extended2 +/// +/// Invoke the magic instruction defined by SIMICS for the RISC-V architecture +/// with a specific value of `n` and pseudo-arguments in registers `a0` and +/// `a1`. +/// +/// # Arguments +/// +/// * `value` - The value of `n` to use in the magic instruction +/// * `arg0` - The value to place in register `a0` +/// * `arg1` - The value to place in register `a1` +#define __srai_extended2(value, arg0, arg1) \ + __asm__ __volatile__("mv a0, %0; mv a1, %1; srai zero, zero, %2" \ + : \ + : "r"(arg0), "r"(arg1), "I"(value) \ + : "a0", "a1"); + +/// __srai_extended3 +/// +/// Invoke the magic instruction defined by SIMICS for the RISC-V architecture +/// with a specific value of `n` and pseudo-arguments in registers `a0`, `a1`, +/// and `a2`. +/// +/// # Arguments +/// +/// * `value` - The value of `n` to use in the magic instruction +/// * `arg0` - The value to place in register `a0` +/// * `arg1` - The value to place in register `a1` +/// * `arg2` - The value to place in register `a2` +#define __srai_extended3(value, arg0, arg1, arg2) \ + __asm__ __volatile__("mv a0, %0; mv a1, %1; mv a2, %2; srai zero, zero, %3" \ + : \ + : "r"(arg0), "r"(arg1), "r"(arg2), "I"(value) \ + : "a0", "a1", "a2"); + +/// __srai_extended4 +/// +/// Invoke the magic instruction defined by SIMICS for the RISC-V architecture +/// with a specific value of `n` and pseudo-arguments in registers `a0`, `a1`, +/// `a2`, and `a3`. +/// +/// # Arguments +/// +/// * `value` - The value of `n` to use in the magic instruction +/// * `arg0` - The value to place in register `a0` +/// * `arg1` - The value to place in register `a1` +/// * `arg2` - The value to place in register `a2` +/// * `arg3` - The value to place in register `a3` +#define __srai_extended4(value, arg0, arg1, arg2, arg3) \ + __asm__ __volatile__( \ + "mv a0, %0; mv a1, %1; mv a2, %2; mv a3, %3; srai zero, zero, %4" \ + : \ + : "r"(arg0), "r"(arg1), "r"(arg2), "r"(arg3), "I"(value) \ + : "a0", "a1", "a2", "a3"); + +/// Magic value defined by SIMICS as the "leaf" value of a CPUID instruction +/// that is treated as a magic instruction. +#define MAGIC (0x4711U) + +/// The default index number used for magic instructions. All magic instructions +/// support multiple start and stop indices, which defaults to 0 if not +/// specified. +#define DEFAULT_INDEX (0x0000U) + +/// Pseudo-hypercall number to signal the fuzzer to use the first argument to +/// the magic instruction as the pointer to the testcase buffer and the second +/// argument as a pointer to the size of the testcase buffer. +#define N_START_BUFFER_PTR_SIZE_PTR (0x0001U) + +/// HARNESS_START +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The default "index" of 0 will be used. If you need multiple start +/// harnesses compiled into the same binary, you can use the +/// `HARNESS_START_INDEX` macro to specify different indices, then enable them +/// at runtime by configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The size of the buffer pointed to by `size_ptr` will be saved as the +/// maximum testcase size. Each fuzzing iteration, the actual size of the +/// current testcase will be written to `*size_ptr`. +/// +/// # Arguments +/// +/// - `buffer`: The pointer to the testcase buffer +/// - `size_ptr`: The pointer to the size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// size_t size; +/// HARNESS_START(buffer, &size); +/// ``` +#define HARNESS_START(buffer, size_ptr) \ + do { \ + __srai_extended3(N_START_BUFFER_PTR_SIZE_PTR, DEFAULT_INDEX, buffer, \ + size_ptr); \ + } while (0); + +/// HARNESS_START_INDEX +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The index specified by `start_index` will be used. If you need +/// multiple start harnesses compiled into the same binary, you can use this +/// macro to specify different indices, then enable them at runtime by +/// configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The size of the buffer pointed to by `size_ptr` will be saved as the +/// maximum testcase size. Each fuzzing iteration, the actual size of the +/// current testcase will be written to `*size_ptr`. +/// +/// # Arguments +/// +/// - `start_index`: The index to use for this start harness +/// - `buffer`: The pointer to the testcase buffer +/// - `size_ptr`: The pointer to the size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// size_t size; +/// HARNESS_START_INDEX(0x0001U, buffer, &size); +/// ``` +#define HARNESS_START_INDEX(start_index, buffer, size_ptr) \ + do { \ + __srai_extended3(N_START_BUFFER_PTR_SIZE_PTR, start_index, buffer, \ + size_ptr); \ + } while (0); + +/// Pseudo-hypercall number to signal the fuzzer to use the first argument to +/// the magic instruction as the pointer to the testcase buffer and the second +/// argument as the maximum size of the testcase buffer. +#define N_START_BUFFER_PTR_SIZE_VAL (0x0002U) + +/// HARNESS_START_WITH_MAXIMUM_SIZE +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The default "index" of 0 will be used. If you need multiple start +/// harnesses compiled into the same binary, you can use the +/// `HARNESS_START_WITH_MAXIMUM_SIZE_INDEX` macro to specify different indices, +/// then enable them at runtime by configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The `max_size` value will be saved as the maximum testcase size. Fuzzing +/// test cases will be truncated to this size before being written to the +/// buffer. +/// +/// # Arguments +/// +/// - `buffer`: The pointer to the testcase buffer +/// - `max_size`: The maximum size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// HARNESS_START_WITH_MAXIMUM_SIZE(buffer, 1024); +/// ``` +#define HARNESS_START_WITH_MAXIMUM_SIZE(buffer, max_size) \ + do { \ + __srai_extended3(N_START_BUFFER_PTR_SIZE_VAL, DEFAULT_INDEX, buffer, \ + max_size); \ + } while (0); + +/// HARNESS_START_WITH_MAXIMUM_SIZE_INDEX +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The index specified by `start_index` will be used. If you need +/// multiple start harnesses compiled into the same binary, you can use this +/// macro to specify different indices, then enable them at runtime by +/// configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The `max_size` value will be saved as the maximum testcase size. Fuzzing +/// test cases will be truncated to this size before being written to the +/// buffer. +/// +/// # Arguments +/// +/// - `start_index`: The index to use for this start harness +/// - `buffer`: The pointer to the testcase buffer +/// - `max_size`: The maximum size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// HARNESS_START_WITH_MAXIMUM_SIZE_INDEX(0x0001U, buffer, 1024); +/// ``` +#define HARNESS_START_WITH_MAXIMUM_SIZE_INDEX(start_index, buffer, max_size) \ + do { \ + __srai_extended3(N_START_BUFFER_PTR_SIZE_VAL, start_index, buffer, \ + max_size); \ + } while (0); + +/// Pseudo-hypercall number to signal the fuzzer to use the first argument to +/// the magic instruction as the pointer to the testcase buffer, the second +/// argument as a pointer to the size of the testcase buffer, and the third +/// argument as the maximum size of the testcase buffer. +#define N_START_BUFFER_PTR_SIZE_PTR_VAL (0x0003U) + +/// HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The default "index" of 0 will be used. If you need multiple start +/// harnesses compiled into the same binary, you can use the +/// `HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR_INDEX` macro to specify different +/// indices, then enable them at runtime by configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The address `size_ptr` will be saved. Each fuzzing iteration, the actual +/// size of the current testcase will be written to `*size_ptr`. +/// - The `max_size` value will be saved as the maximum testcase size. Fuzzing +/// test cases will be truncated to this size before being written to the +/// buffer. +/// +/// # Arguments +/// +/// - `buffer`: The pointer to the testcase buffer +/// - `size_ptr`: The pointer to the size of the testcase buffer +/// - `max_size`: The maximum size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// size_t size; +/// HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR(buffer, &size, 1024); +/// ``` +#define HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR(buffer, size_ptr, max_size) \ + do { \ + __srai_extended4(N_START_BUFFER_PTR_SIZE_PTR_VAL, DEFAULT_INDEX, buffer, \ + size_ptr, max_size); \ + } while (0); + +/// HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR_INDEX +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The index specified by `start_index` will be used. If you need +/// multiple start harnesses compiled into the same binary, you can use this +/// macro to specify different indices, then enable them at runtime by +/// configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The address `size_ptr` will be saved. Each fuzzing iteration, the actual +/// size of the current testcase will be written to `*size_ptr`. +/// - The `max_size` value will be saved as the maximum testcase size. Fuzzing +/// test cases will be truncated to this size before being written to the +/// buffer. +/// +/// # Arguments +/// +/// - `start_index`: The index to use for this start harness +/// - `buffer`: The pointer to the testcase buffer +/// - `size_ptr`: The pointer to the size of the testcase buffer +/// - `max_size`: The maximum size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// size_t size; +/// HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR_INDEX(0x0001U, buffer, &size, 1024); +/// ``` +#define HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR_INDEX(start_index, buffer, \ + size_ptr, max_size) \ + do { \ + __srai_extended4(N_START_BUFFER_PTR_SIZE_PTR_VAL, start_index, buffer, \ + size_ptr, max_size); \ + } while (0); + +/// Pseudo-hypercall number to signal the fuzzer to stop the current fuzzing +/// iteration and reset to the beginning of the fuzzing loop with a "normal" +/// stop status, indicating no solution has occurred. +#define N_STOP_NORMAL (0x0004U) + +/// HARNESS_STOP +/// +/// Signal the fuzzer to stop and reset to the beginning of the fuzzing loop +/// with a "normal" stop status, indicating no solution has occurred. The +/// default index of 0 will be used. If you need to differentiate between +/// multiple stop harnesses compiled into the same binary, you can use the +/// `HARNESS_STOP_INDEX` macro to specify different indices, then enable them at +/// runtime by configuring the fuzzer. +/// +/// # Example +/// +/// ``` +/// HARNESS_STOP(); +/// ``` +#define HARNESS_STOP() \ + do { \ + __srai_extended1(N_STOP_NORMAL, DEFAULT_INDEX); \ + } while (0); + +/// HARNESS_STOP_INDEX +/// +/// Signal the fuzzer to stop and reset to the beginning of the fuzzing loop +/// with a "normal" stop status, indicating no solution has occurred. The index +/// specified by `stop_index` will be used. If you need to differentiate between +/// multiple stop harnesses compiled into the same binary, you can use this +/// macro to specify different indices, then enable them at runtime by +/// configuring the fuzzer. +/// +/// # Arguments +/// +/// - `stop_index`: The index to use for this stop harness +/// +/// # Example +/// +/// ``` +/// HARNESS_STOP_INDEX(0x0001U); +/// ``` +#define HARNESS_STOP_INDEX(stop_index) \ + do { \ + __srai_extended1(N_STOP_NORMAL, stop_index); \ + } while (0); + +/// Pseudo-hypercall number to signal the fuzzer that a custom assertion has +/// occurred, and the fuzzer should stop the current fuzzing iteration and reset +/// to the beginning of the fuzzing loop with a "solution" stop status. +#define N_STOP_ASSERT (0x0005U) + +/// HARNESS_ASSERT +/// +/// Signal the fuzzer that a custom assertion has occurred, and the fuzzer +/// should stop the current fuzzing iteration and reset to the beginning of the +/// fuzzing loop with a "solution" stop status. The default index of 0 will be +/// used. If you need to differentiate between multiple assertion harnesses +/// compiled into the same binary, you can use the `HARNESS_ASSERT_INDEX` macro +/// to specify different indices, then enable them at runtime by configuring the +/// fuzzer. +/// +/// # Example +/// +/// ``` +/// HARNESS_ASSERT(); +/// ``` +#define HARNESS_ASSERT() \ + do { \ + __srai_extended1(N_STOP_ASSERT, DEFAULT_INDEX); \ + } while (0); + +/// HARNESS_ASSERT_INDEX +/// +/// Signal the fuzzer that a custom assertion has occurred, and the fuzzer +/// should stop the current fuzzing iteration and reset to the beginning of the +/// fuzzing loop with a "solution" stop status. The index specified by +/// `assert_index` will be used. If you need to differentiate between multiple +/// assertion harnesses compiled into the same binary, you can use this macro to +/// specify different indices, then enable them at runtime by configuring the +/// fuzzer. +/// +/// # Arguments +/// +/// - `assert_index`: The index to use for this assertion harness +/// +/// # Example +/// +/// ``` +/// HARNESS_ASSERT_INDEX(0x0001U); +/// ``` +#define HARNESS_ASSERT_INDEX(assert_index) \ + do { \ + __srai_extended1(N_STOP_ASSERT, assert_index); \ + } while (0); + +#endif // TSFFS_H +#elif __riscv && __LP64__ +// Copyright (C) 2024 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 + +/// Definitions and macros for compiled-in harnessing of C and C++ target +/// software for the RISC-V (32-bit) architecture + +#ifndef TSFFS_H +#define TSFFS_H + +/// Define common with LibFuzzer and other fuzzers to allow code that is +/// fuzzing-specific to be left in the codebase. See +/// https://llvm.org/docs/LibFuzzer.html#id35 for more information +#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION +#define FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION (1) +#endif // FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION + +/// __srai +/// +/// Invoke the magic instruction defined by SIMICS for the RISC-V architecture +/// with a specific value of `n` +/// +/// # Arguments +/// +/// * `value` - The value of `n` to use in the magic instruction +#define __srai(value) \ + __asm__ __volatile__("srai zero, zero, %0" : : "I"(value) :) + +/// __srai_extended1 +/// +/// Invoke the magic instruction defined by SIMICS for the RISC-V architecture +/// with a specific value of `n` and a pseudo-argument in register `a0`. +/// +/// # Arguments +/// +/// * `value` - The value of `n` to use in the magic instruction +/// * `arg0` - The value to place in register `a0` +#define __srai_extended1(value, arg0) \ + __asm__ __volatile__("mv a0, %0; srai zero, zero, %1" \ + : \ + : "r"(arg0), "I"(value) \ + : "a0"); + +/// __srai_extended2 +/// +/// Invoke the magic instruction defined by SIMICS for the RISC-V architecture +/// with a specific value of `n` and pseudo-arguments in registers `a0` and +/// `a1`. +/// +/// # Arguments +/// +/// * `value` - The value of `n` to use in the magic instruction +/// * `arg0` - The value to place in register `a0` +/// * `arg1` - The value to place in register `a1` +#define __srai_extended2(value, arg0, arg1) \ + __asm__ __volatile__("mv a0, %0; mv a1, %1; srai zero, zero, %2" \ + : \ + : "r"(arg0), "r"(arg1), "I"(value) \ + : "a0", "a1"); + +/// __srai_extended3 +/// +/// Invoke the magic instruction defined by SIMICS for the RISC-V architecture +/// with a specific value of `n` and pseudo-arguments in registers `a0`, `a1`, +/// and `a2`. +/// +/// # Arguments +/// +/// * `value` - The value of `n` to use in the magic instruction +/// * `arg0` - The value to place in register `a0` +/// * `arg1` - The value to place in register `a1` +/// * `arg2` - The value to place in register `a2` +#define __srai_extended3(value, arg0, arg1, arg2) \ + __asm__ __volatile__("mv a0, %0; mv a1, %1; mv a2, %2; srai zero, zero, %3" \ + : \ + : "r"(arg0), "r"(arg1), "r"(arg2), "I"(value) \ + : "a0", "a1", "a2"); + +/// __srai_extended4 +/// +/// Invoke the magic instruction defined by SIMICS for the RISC-V architecture +/// with a specific value of `n` and pseudo-arguments in registers `a0`, `a1`, +/// `a2`, and `a3`. +/// +/// # Arguments +/// +/// * `value` - The value of `n` to use in the magic instruction +/// * `arg0` - The value to place in register `a0` +/// * `arg1` - The value to place in register `a1` +/// * `arg2` - The value to place in register `a2` +/// * `arg3` - The value to place in register `a3` +#define __srai_extended4(value, arg0, arg1, arg2, arg3) \ + __asm__ __volatile__( \ + "mv a0, %0; mv a1, %1; mv a2, %2; mv a3, %3; srai zero, zero, %4" \ + : \ + : "r"(arg0), "r"(arg1), "r"(arg2), "r"(arg3), "I"(value) \ + : "a0", "a1", "a2", "a3"); + +/// Magic value defined by SIMICS as the "leaf" value of a CPUID instruction +/// that is treated as a magic instruction. +#define MAGIC (0x4711U) + +/// The default index number used for magic instructions. All magic instructions +/// support multiple start and stop indices, which defaults to 0 if not +/// specified. +#define DEFAULT_INDEX (0x0000U) + +/// Pseudo-hypercall number to signal the fuzzer to use the first argument to +/// the magic instruction as the pointer to the testcase buffer and the second +/// argument as a pointer to the size of the testcase buffer. +#define N_START_BUFFER_PTR_SIZE_PTR (0x0001U) + +/// HARNESS_START +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The default "index" of 0 will be used. If you need multiple start +/// harnesses compiled into the same binary, you can use the +/// `HARNESS_START_INDEX` macro to specify different indices, then enable them +/// at runtime by configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The size of the buffer pointed to by `size_ptr` will be saved as the +/// maximum testcase size. Each fuzzing iteration, the actual size of the +/// current testcase will be written to `*size_ptr`. +/// +/// # Arguments +/// +/// - `buffer`: The pointer to the testcase buffer +/// - `size_ptr`: The pointer to the size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// size_t size; +/// HARNESS_START(buffer, &size); +/// ``` +#define HARNESS_START(buffer, size_ptr) \ + do { \ + __srai_extended3(N_START_BUFFER_PTR_SIZE_PTR, DEFAULT_INDEX, buffer, size_ptr); \ + } while (0); + +/// HARNESS_START_INDEX +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The index specified by `start_index` will be used. If you need +/// multiple start harnesses compiled into the same binary, you can use this +/// macro to specify different indices, then enable them at runtime by +/// configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The size of the buffer pointed to by `size_ptr` will be saved as the +/// maximum testcase size. Each fuzzing iteration, the actual size of the +/// current testcase will be written to `*size_ptr`. +/// +/// # Arguments +/// +/// - `start_index`: The index to use for this start harness +/// - `buffer`: The pointer to the testcase buffer +/// - `size_ptr`: The pointer to the size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// size_t size; +/// HARNESS_START_INDEX(0x0001U, buffer, &size); +/// ``` +#define HARNESS_START_INDEX(start_index, buffer, size_ptr) \ + do { \ + __srai_extended3(N_START_BUFFER_PTR_SIZE_PTR, start_index, buffer, size_ptr); \ + } while (0); + +/// Pseudo-hypercall number to signal the fuzzer to use the first argument to +/// the magic instruction as the pointer to the testcase buffer and the second +/// argument as the maximum size of the testcase buffer. +#define N_START_BUFFER_PTR_SIZE_VAL (0x0002U) + +/// HARNESS_START_WITH_MAXIMUM_SIZE +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The default "index" of 0 will be used. If you need multiple start +/// harnesses compiled into the same binary, you can use the +/// `HARNESS_START_WITH_MAXIMUM_SIZE_INDEX` macro to specify different indices, +/// then enable them at runtime by configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The `max_size` value will be saved as the maximum testcase size. Fuzzing +/// test cases will be truncated to this size before being written to the +/// buffer. +/// +/// # Arguments +/// +/// - `buffer`: The pointer to the testcase buffer +/// - `max_size`: The maximum size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// HARNESS_START_WITH_MAXIMUM_SIZE(buffer, 1024); +/// ``` +#define HARNESS_START_WITH_MAXIMUM_SIZE(buffer, max_size) \ + do { \ + __srai_extended3(N_START_BUFFER_PTR_SIZE_VAL, DEFAULT_INDEX, buffer, max_size); \ + } while (0); + +/// HARNESS_START_WITH_MAXIMUM_SIZE_INDEX +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The index specified by `start_index` will be used. If you need +/// multiple start harnesses compiled into the same binary, you can use this +/// macro to specify different indices, then enable them at runtime by +/// configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The `max_size` value will be saved as the maximum testcase size. Fuzzing +/// test cases will be truncated to this size before being written to the +/// buffer. +/// +/// # Arguments +/// +/// - `start_index`: The index to use for this start harness +/// - `buffer`: The pointer to the testcase buffer +/// - `max_size`: The maximum size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// HARNESS_START_WITH_MAXIMUM_SIZE_INDEX(0x0001U, buffer, 1024); +/// ``` +#define HARNESS_START_WITH_MAXIMUM_SIZE_INDEX(start_index, buffer, max_size) \ + do { \ + __srai_extended3(N_START_BUFFER_PTR_SIZE_VAL, start_index, buffer, max_size); \ + } while (0); + +/// Pseudo-hypercall number to signal the fuzzer to use the first argument to +/// the magic instruction as the pointer to the testcase buffer, the second +/// argument as a pointer to the size of the testcase buffer, and the third +/// argument as the maximum size of the testcase buffer. +#define N_START_BUFFER_PTR_SIZE_PTR_VAL (0x0003U) + +/// HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The default "index" of 0 will be used. If you need multiple start +/// harnesses compiled into the same binary, you can use the +/// `HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR_INDEX` macro to specify different +/// indices, then enable them at runtime by configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The address `size_ptr` will be saved. Each fuzzing iteration, the actual +/// size of the current testcase will be written to `*size_ptr`. +/// - The `max_size` value will be saved as the maximum testcase size. Fuzzing +/// test cases will be truncated to this size before being written to the +/// buffer. +/// +/// # Arguments +/// +/// - `buffer`: The pointer to the testcase buffer +/// - `size_ptr`: The pointer to the size of the testcase buffer +/// - `max_size`: The maximum size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// size_t size; +/// HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR(buffer, &size, 1024); +/// ``` +#define HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR(buffer, size_ptr, max_size) \ + do { \ + __srai_extended4(N_START_BUFFER_PTR_SIZE_PTR_VAL, DEFAULT_INDEX, buffer, size_ptr, max_size); \ + } while (0); + +/// HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR_INDEX +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The index specified by `start_index` will be used. If you need +/// multiple start harnesses compiled into the same binary, you can use this +/// macro to specify different indices, then enable them at runtime by +/// configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The address `size_ptr` will be saved. Each fuzzing iteration, the actual +/// size of the current testcase will be written to `*size_ptr`. +/// - The `max_size` value will be saved as the maximum testcase size. Fuzzing +/// test cases will be truncated to this size before being written to the +/// buffer. +/// +/// # Arguments +/// +/// - `start_index`: The index to use for this start harness +/// - `buffer`: The pointer to the testcase buffer +/// - `size_ptr`: The pointer to the size of the testcase buffer +/// - `max_size`: The maximum size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// size_t size; +/// HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR_INDEX(0x0001U, buffer, &size, 1024); +/// ``` +#define HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR_INDEX(start_index, buffer, \ + size_ptr, max_size) \ + do { \ + __srai_extended4(N_START_BUFFER_PTR_SIZE_PTR_VAL, start_index, buffer, size_ptr, max_size); \ + } while (0); + +/// Pseudo-hypercall number to signal the fuzzer to stop the current fuzzing +/// iteration and reset to the beginning of the fuzzing loop with a "normal" +/// stop status, indicating no solution has occurred. +#define N_STOP_NORMAL (0x0004U) + +/// HARNESS_STOP +/// +/// Signal the fuzzer to stop and reset to the beginning of the fuzzing loop +/// with a "normal" stop status, indicating no solution has occurred. The +/// default index of 0 will be used. If you need to differentiate between +/// multiple stop harnesses compiled into the same binary, you can use the +/// `HARNESS_STOP_INDEX` macro to specify different indices, then enable them at +/// runtime by configuring the fuzzer. +/// +/// # Example +/// +/// ``` +/// HARNESS_STOP(); +/// ``` +#define HARNESS_STOP() \ + do { \ + __srai_extended1(N_STOP_NORMAL, DEFAULT_INDEX); \ + } while (0); + +/// HARNESS_STOP_INDEX +/// +/// Signal the fuzzer to stop and reset to the beginning of the fuzzing loop +/// with a "normal" stop status, indicating no solution has occurred. The index +/// specified by `stop_index` will be used. If you need to differentiate between +/// multiple stop harnesses compiled into the same binary, you can use this +/// macro to specify different indices, then enable them at runtime by +/// configuring the fuzzer. +/// +/// # Arguments +/// +/// - `stop_index`: The index to use for this stop harness +/// +/// # Example +/// +/// ``` +/// HARNESS_STOP_INDEX(0x0001U); +/// ``` +#define HARNESS_STOP_INDEX(stop_index) \ + do { \ + __srai_extended1(N_STOP_NORMAL, stop_index); \ + } while (0); + +/// Pseudo-hypercall number to signal the fuzzer that a custom assertion has +/// occurred, and the fuzzer should stop the current fuzzing iteration and reset +/// to the beginning of the fuzzing loop with a "solution" stop status. +#define N_STOP_ASSERT (0x0005U) + +/// HARNESS_ASSERT +/// +/// Signal the fuzzer that a custom assertion has occurred, and the fuzzer +/// should stop the current fuzzing iteration and reset to the beginning of the +/// fuzzing loop with a "solution" stop status. The default index of 0 will be +/// used. If you need to differentiate between multiple assertion harnesses +/// compiled into the same binary, you can use the `HARNESS_ASSERT_INDEX` macro +/// to specify different indices, then enable them at runtime by configuring the +/// fuzzer. +/// +/// # Example +/// +/// ``` +/// HARNESS_ASSERT(); +/// ``` +#define HARNESS_ASSERT() \ + do { \ + __srai_extended1(N_STOP_ASSERT, DEFAULT_INDEX); \ + } while (0); + +/// HARNESS_ASSERT_INDEX +/// +/// Signal the fuzzer that a custom assertion has occurred, and the fuzzer +/// should stop the current fuzzing iteration and reset to the beginning of the +/// fuzzing loop with a "solution" stop status. The index specified by +/// `assert_index` will be used. If you need to differentiate between multiple +/// assertion harnesses compiled into the same binary, you can use this macro to +/// specify different indices, then enable them at runtime by configuring the +/// fuzzer. +/// +/// # Arguments +/// +/// - `assert_index`: The index to use for this assertion harness +/// +/// # Example +/// +/// ``` +/// HARNESS_ASSERT_INDEX(0x0001U); +/// ``` +#define HARNESS_ASSERT_INDEX(assert_index) \ + do { \ + __srai_extended1(N_STOP_ASSERT, assert_index); \ + } while (0); + +#endif // TSFFS_H +#elif __aarch64__ +// Copyright (C) 2024 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 + +/// Definitions and macros for compiled-in harnessing of C and C++ target +/// software for the RISC-V (32-bit) architecture + +#ifndef TSFFS_H +#define TSFFS_H + +/// Define common with LibFuzzer and other fuzzers to allow code that is +/// fuzzing-specific to be left in the codebase. See +/// https://llvm.org/docs/LibFuzzer.html#id35 for more information +#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION +#define FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION (1) +#endif // FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION + +#define __stringify(x) #x +#define __tostring(x) __stringify(x) + +/// __orr +/// +/// Invoke the magic instruction defined by SIMICS for the RISC-V architecture +/// with a specific value of `n` +/// +/// # Arguments +/// +/// * `value` - The value of `n` to use in the magic instruction +#define __orr(value) \ + __asm__ __volatile__("orr x" __tostring(value) ", x" __tostring( \ + value) ", x" __tostring(value)); + +/// __orr_extended1 +/// +/// Invoke the magic instruction defined by SIMICS for the RISC-V architecture +/// with a specific value of `n` and a pseudo-argument in register `r10`. +/// +/// # Arguments +/// +/// * `value` - The value of `n` to use in the magic instruction +/// * `arg0` - The value to place in register `r10` +#define __orr_extended1(value, arg0) \ + __asm__ __volatile__( \ + "mov x28, %0; orr x" __tostring(value) ", x" __tostring( \ + value) ", x" __tostring(value) \ + : \ + : "g"(arg0)); + +/// __orr_extended2 +/// +/// Invoke the magic instruction defined by SIMICS for the RISC-V architecture +/// with a specific value of `n` and two pseudo-arguments in registers `r10` and +/// `r9`. +/// +/// # Arguments +/// +/// * `value` - The value of `n` to use in the magic instruction +/// * `arg0` - The value to place in register `r10` +/// * `arg1` - The value to place in register `r9` +#define __orr_extended2(value, arg0, arg1) \ + __asm__ __volatile__( \ + "mov x28, %0; mov x27, %1; orr x" __tostring(value) ", x" __tostring( \ + value) ", x" __tostring(value) \ + : \ + : "r"(arg0), "r"(arg1)); + +/// __orr_extended3 +/// +/// Invoke the magic instruction defined by SIMICS for the RISC-V architecture +/// with a specific value of `n` and three pseudo-arguments in registers `r10`, +/// `r9`, and `r8`. +/// +/// # Arguments +/// +/// * `value` - The value of `n` to use in the magic instruction +/// * `arg0` - The value to place in register `r10` +/// * `arg1` - The value to place in register `r9` +/// * `arg2` - The value to place in register `r8` +#define __orr_extended3(value, arg0, arg1, arg2) \ + __asm__ __volatile__( \ + "mov x28, %0; mov x27, %1; mov x26, %2; orr x" __tostring( \ + value) ", x" __tostring(value) ", x" __tostring(value) \ + : \ + : "r"(arg0), "r"(arg1), "r"(arg2)); + +/// __orr_extended4 +/// +/// Invoke the magic instruction defined by SIMICS for the RISC-V architecture +/// with a specific value of `n` and four pseudo-arguments in registers `r10`, +/// `r9`, `r8`, and `r7`. +/// +/// # Arguments +/// +/// * `value` - The value of `n` to use in the magic instruction +/// * `arg0` - The value to place in register `r10` +/// * `arg1` - The value to place in register `r9` +/// * `arg2` - The value to place in register `r8` +/// * `arg3` - The value to place in register `r7` +#define __orr_extended4(value, arg0, arg1, arg2, arg3) \ + __asm__ __volatile__( \ + "mov x28, %0; mov x27, %1; mov x26, %2; mov x25, %3; " \ + "orr x" __tostring(value) ", x" __tostring(value) ", x" __tostring( \ + value) \ + : \ + : "r"(arg0), "r"(arg1), "r"(arg2), "r"(arg3)); + +/// The default index number used for magic instructions. All magic instructions +/// support multiple start and stop indices, which defaults to 0 if not +/// specified. +#define DEFAULT_INDEX (0x0000U) + +/// Pseudo-hypercall number to signal the fuzzer to use the first argument to +/// the magic instruction as the pointer to the testcase buffer and the second +/// argument as a pointer to the size of the testcase buffer. +#define N_START_BUFFER_PTR_SIZE_PTR 1 + +/// HARNESS_START +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The default "index" of 0 will be used. If you need multiple start +/// harnesses compiled into the same binary, you can use the +/// `HARNESS_START_INDEX` macro to specify different indices, then enable them +/// at runtime by configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The size of the buffer pointed to by `size_ptr` will be saved as the +/// maximum testcase size. Each fuzzing iteration, the actual size of the +/// current testcase will be written to `*size_ptr`. +/// +/// # Arguments +/// +/// - `buffer`: The pointer to the testcase buffer +/// - `size_ptr`: The pointer to the size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// size_t size; +/// HARNESS_START(buffer, &size); +/// ``` +#define HARNESS_START(buffer, size_ptr) \ + do { \ + __orr_extended3(N_START_BUFFER_PTR_SIZE_PTR, DEFAULT_INDEX, buffer, \ + size_ptr); \ + } while (0); + +/// HARNESS_START_INDEX +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The index specified by `start_index` will be used. If you need +/// multiple start harnesses compiled into the same binary, you can use this +/// macro to specify different indices, then enable them at runtime by +/// configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The size of the buffer pointed to by `size_ptr` will be saved as the +/// maximum testcase size. Each fuzzing iteration, the actual size of the +/// current testcase will be written to `*size_ptr`. +/// +/// # Arguments +/// +/// - `start_index`: The index to use for this start harness +/// - `buffer`: The pointer to the testcase buffer +/// - `size_ptr`: The pointer to the size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// size_t size; +/// HARNESS_START_INDEX(0x0001U, buffer, &size); +/// ``` +#define HARNESS_START_INDEX(start_index, buffer, size_ptr) \ + do { \ + __orr_extended3(N_START_BUFFER_PTR_SIZE_PTR, start_index, buffer, \ + size_ptr); \ + } while (0); + +/// Pseudo-hypercall number to signal the fuzzer to use the first argument to +/// the magic instruction as the pointer to the testcase buffer and the second +/// argument as the maximum size of the testcase buffer. +#define N_START_BUFFER_PTR_SIZE_VAL 2 + +/// HARNESS_START_WITH_MAXIMUM_SIZE +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The default "index" of 0 will be used. If you need multiple start +/// harnesses compiled into the same binary, you can use the +/// `HARNESS_START_WITH_MAXIMUM_SIZE_INDEX` macro to specify different indices, +/// then enable them at runtime by configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The `max_size` value will be saved as the maximum testcase size. Fuzzing +/// test cases will be truncated to this size before being written to the +/// buffer. +/// +/// # Arguments +/// +/// - `buffer`: The pointer to the testcase buffer +/// - `max_size`: The maximum size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// HARNESS_START_WITH_MAXIMUM_SIZE(buffer, 1024); +/// ``` +#define HARNESS_START_WITH_MAXIMUM_SIZE(buffer, max_size) \ + do { \ + __orr_extended3(N_START_BUFFER_PTR_SIZE_VAL, DEFAULT_INDEX, buffer, \ + max_size); \ + } while (0); + +/// HARNESS_START_WITH_MAXIMUM_SIZE_INDEX +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The index specified by `start_index` will be used. If you need +/// multiple start harnesses compiled into the same binary, you can use this +/// macro to specify different indices, then enable them at runtime by +/// configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The `max_size` value will be saved as the maximum testcase size. Fuzzing +/// test cases will be truncated to this size before being written to the +/// buffer. +/// +/// # Arguments +/// +/// - `start_index`: The index to use for this start harness +/// - `buffer`: The pointer to the testcase buffer +/// - `max_size`: The maximum size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// HARNESS_START_WITH_MAXIMUM_SIZE_INDEX(0x0001U, buffer, 1024); +/// ``` +#define HARNESS_START_WITH_MAXIMUM_SIZE_INDEX(start_index, buffer, max_size) \ + do { \ + __orr_extended3(N_START_BUFFER_PTR_SIZE_VAL, start_index, buffer, \ + max_size); \ + } while (0); + +/// Pseudo-hypercall number to signal the fuzzer to use the first argument to +/// the magic instruction as the pointer to the testcase buffer, the second +/// argument as a pointer to the size of the testcase buffer, and the third +/// argument as the maximum size of the testcase buffer. +#define N_START_BUFFER_PTR_SIZE_PTR_VAL 3 + +/// HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The default "index" of 0 will be used. If you need multiple start +/// harnesses compiled into the same binary, you can use the +/// `HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR_INDEX` macro to specify different +/// indices, then enable them at runtime by configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The address `size_ptr` will be saved. Each fuzzing iteration, the actual +/// size of the current testcase will be written to `*size_ptr`. +/// - The `max_size` value will be saved as the maximum testcase size. Fuzzing +/// test cases will be truncated to this size before being written to the +/// buffer. +/// +/// # Arguments +/// +/// - `buffer`: The pointer to the testcase buffer +/// - `size_ptr`: The pointer to the size of the testcase buffer +/// - `max_size`: The maximum size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// size_t size; +/// HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR(buffer, &size, 1024); +/// ``` +#define HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR(buffer, size_ptr, max_size) \ + do { \ + __orr_extended4(N_START_BUFFER_PTR_SIZE_PTR_VAL, DEFAULT_INDEX, buffer, \ + size_ptr, max_size); \ + } while (0); + +/// HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR_INDEX +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The index specified by `start_index` will be used. If you need +/// multiple start harnesses compiled into the same binary, you can use this +/// macro to specify different indices, then enable them at runtime by +/// configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The address `size_ptr` will be saved. Each fuzzing iteration, the actual +/// size of the current testcase will be written to `*size_ptr`. +/// - The `max_size` value will be saved as the maximum testcase size. Fuzzing +/// test cases will be truncated to this size before being written to the +/// buffer. +/// +/// # Arguments +/// +/// - `start_index`: The index to use for this start harness +/// - `buffer`: The pointer to the testcase buffer +/// - `size_ptr`: The pointer to the size of the testcase buffer +/// - `max_size`: The maximum size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// size_t size; +/// HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR_INDEX(0x0001U, buffer, &size, 1024); +/// ``` +#define HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR_INDEX(start_index, buffer, \ + size_ptr, max_size) \ + do { \ + __orr_extended4(N_START_BUFFER_PTR_SIZE_PTR_VAL, start_index, buffer, \ + size_ptr, max_size); \ + } while (0); + +/// Pseudo-hypercall number to signal the fuzzer to stop the current fuzzing +/// iteration and reset to the beginning of the fuzzing loop with a "normal" +/// stop status, indicating no solution has occurred. +#define N_STOP_NORMAL 4 + +/// HARNESS_STOP +/// +/// Signal the fuzzer to stop and reset to the beginning of the fuzzing loop +/// with a "normal" stop status, indicating no solution has occurred. The +/// default index of 0 will be used. If you need to differentiate between +/// multiple stop harnesses compiled into the same binary, you can use the +/// `HARNESS_STOP_INDEX` macro to specify different indices, then enable them at +/// runtime by configuring the fuzzer. +/// +/// # Example +/// +/// ``` +/// HARNESS_STOP(); +/// ``` +#define HARNESS_STOP() \ + do { \ + __orr_extended1(N_STOP_NORMAL, DEFAULT_INDEX); \ + } while (0); + +/// HARNESS_STOP_INDEX +/// +/// Signal the fuzzer to stop and reset to the beginning of the fuzzing loop +/// with a "normal" stop status, indicating no solution has occurred. The index +/// specified by `stop_index` will be used. If you need to differentiate between +/// multiple stop harnesses compiled into the same binary, you can use this +/// macro to specify different indices, then enable them at runtime by +/// configuring the fuzzer. +/// +/// # Arguments +/// +/// - `stop_index`: The index to use for this stop harness +/// +/// # Example +/// +/// ``` +/// HARNESS_STOP_INDEX(0x0001U); +/// ``` +#define HARNESS_STOP_INDEX(stop_index) \ + do { \ + __orr_extended1(N_STOP_NORMAL, stop_index); \ + } while (0); + +/// Pseudo-hypercall number to signal the fuzzer that a custom assertion has +/// occurred, and the fuzzer should stop the current fuzzing iteration and reset +/// to the beginning of the fuzzing loop with a "solution" stop status. +#define N_STOP_ASSERT 5 + +/// HARNESS_ASSERT +/// +/// Signal the fuzzer that a custom assertion has occurred, and the fuzzer +/// should stop the current fuzzing iteration and reset to the beginning of the +/// fuzzing loop with a "solution" stop status. The default index of 0 will be +/// used. If you need to differentiate between multiple assertion harnesses +/// compiled into the same binary, you can use the `HARNESS_ASSERT_INDEX` macro +/// to specify different indices, then enable them at runtime by configuring the +/// fuzzer. +/// +/// # Example +/// +/// ``` +/// HARNESS_ASSERT(); +/// ``` +#define HARNESS_ASSERT() \ + do { \ + __orr_extended1(N_STOP_ASSERT, DEFAULT_INDEX); \ + } while (0); + +/// HARNESS_ASSERT_INDEX +/// +/// Signal the fuzzer that a custom assertion has occurred, and the fuzzer +/// should stop the current fuzzing iteration and reset to the beginning of the +/// fuzzing loop with a "solution" stop status. The index specified by +/// `assert_index` will be used. If you need to differentiate between multiple +/// assertion harnesses compiled into the same binary, you can use this macro to +/// specify different indices, then enable them at runtime by configuring the +/// fuzzer. +/// +/// # Arguments +/// +/// - `assert_index`: The index to use for this assertion harness +/// +/// # Example +/// +/// ``` +/// HARNESS_ASSERT_INDEX(0x0001U); +/// ``` +#define HARNESS_ASSERT_INDEX(assert_index) \ + do { \ + __orr_extended1(N_STOP_ASSERT, assert_index); \ + } while (0); + +#endif // TSFFS_H +#elif __arm__ +// Copyright (C) 2024 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 + +/// Definitions and macros for compiled-in harnessing of C and C++ target +/// software for the RISC-V (32-bit) architecture + +#ifndef TSFFS_H +#define TSFFS_H + +/// Define common with LibFuzzer and other fuzzers to allow code that is +/// fuzzing-specific to be left in the codebase. See +/// https://llvm.org/docs/LibFuzzer.html#id35 for more information +#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION +#define FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION (1) +#endif // FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION + +#define __stringify(x) #x +#define __tostring(x) __stringify(x) + +/// __orr +/// +/// Invoke the magic instruction defined by SIMICS for the RISC-V architecture +/// with a specific value of `n` +/// +/// # Arguments +/// +/// * `value` - The value of `n` to use in the magic instruction +#define __orr(value) \ + __asm__ __volatile__("orr r" __tostring(value) ", r" __tostring( \ + value) ", r" __tostring(value)); + +/// __orr_extended1 +/// +/// Invoke the magic instruction defined by SIMICS for the RISC-V architecture +/// with a specific value of `n` and a pseudo-argument in register `r10`. +/// +/// # Arguments +/// +/// * `value` - The value of `n` to use in the magic instruction +/// * `arg0` - The value to place in register `r10` +#define __orr_extended1(value, arg0) \ + __asm__ __volatile__( \ + "mov r10, %0; orr r" __tostring(value) ", r" __tostring( \ + value) ", r" __tostring(value) \ + : \ + : "r"(arg0)); + +/// __orr_extended2 +/// +/// Invoke the magic instruction defined by SIMICS for the RISC-V architecture +/// with a specific value of `n` and two pseudo-arguments in registers `r10` and +/// `r9`. +/// +/// # Arguments +/// +/// * `value` - The value of `n` to use in the magic instruction +/// * `arg0` - The value to place in register `r10` +/// * `arg1` - The value to place in register `r9` +#define __orr_extended2(value, arg0, arg1) \ + __asm__ __volatile__( \ + "mov r10, %0; mov r9, %1; orr r" __tostring(value) ", r" __tostring( \ + value) ", r" __tostring(value) \ + : \ + : "r"(arg0), "r"(arg1)); + +/// __orr_extended3 +/// +/// Invoke the magic instruction defined by SIMICS for the RISC-V architecture +/// with a specific value of `n` and three pseudo-arguments in registers `r10`, +/// `r9`, and `r8`. +/// +/// # Arguments +/// +/// * `value` - The value of `n` to use in the magic instruction +/// * `arg0` - The value to place in register `r10` +/// * `arg1` - The value to place in register `r9` +/// * `arg2` - The value to place in register `r8` +#define __orr_extended3(value, arg0, arg1, arg2) \ + __asm__ __volatile__( \ + "mov r10, %0; mov r9, %1; mov r8, %2; orr r" __tostring( \ + value) ", r" __tostring(value) ", r" __tostring(value) \ + : \ + : "r"(arg0), "r"(arg1), "r"(arg2)); + +/// __orr_extended4 +/// +/// Invoke the magic instruction defined by SIMICS for the RISC-V architecture +/// with a specific value of `n` and four pseudo-arguments in registers `r10`, +/// `r9`, `r8`, and `r7`. +/// +/// # Arguments +/// +/// * `value` - The value of `n` to use in the magic instruction +/// * `arg0` - The value to place in register `r10` +/// * `arg1` - The value to place in register `r9` +/// * `arg2` - The value to place in register `r8` +/// * `arg3` - The value to place in register `r7` +#define __orr_extended4(value, arg0, arg1, arg2, arg3) \ + __asm__ __volatile__( \ + "mov r10, %0; mov r9, %1; mov r8, %2; mov r7, %3; " \ + "orr r" __tostring(value) ", r" __tostring(value) ", r" __tostring( \ + value) \ + : \ + : "r"(arg0), "r"(arg1), "r"(arg2), "r"(arg3)); + +/// Magic value defined by SIMICS as the "leaf" value of a CPUID instruction +/// that is treated as a magic instruction. +#define MAGIC (0x4711U) + +/// The default index number used for magic instructions. All magic instructions +/// support multiple start and stop indices, which defaults to 0 if not +/// specified. +#define DEFAULT_INDEX (0x0000U) + +/// Pseudo-hypercall number to signal the fuzzer to use the first argument to +/// the magic instruction as the pointer to the testcase buffer and the second +/// argument as a pointer to the size of the testcase buffer. +#define N_START_BUFFER_PTR_SIZE_PTR 1 + +/// HARNESS_START +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The default "index" of 0 will be used. If you need multiple start +/// harnesses compiled into the same binary, you can use the +/// `HARNESS_START_INDEX` macro to specify different indices, then enable them +/// at runtime by configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The size of the buffer pointed to by `size_ptr` will be saved as the +/// maximum testcase size. Each fuzzing iteration, the actual size of the +/// current testcase will be written to `*size_ptr`. +/// +/// # Arguments +/// +/// - `buffer`: The pointer to the testcase buffer +/// - `size_ptr`: The pointer to the size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// size_t size; +/// HARNESS_START(buffer, &size); +/// ``` +#define HARNESS_START(buffer, size_ptr) \ + do { \ + __orr_extended2(N_START_BUFFER_PTR_SIZE_PTR, buffer, size_ptr); \ + } while (0); + +/// Pseudo-hypercall number to signal the fuzzer to use the first argument to +/// the magic instruction as the pointer to the testcase buffer and the second +/// argument as the maximum size of the testcase buffer. +#define N_START_BUFFER_PTR_SIZE_VAL 2 + +/// HARNESS_START_WITH_MAXIMUM_SIZE +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The default "index" of 0 will be used. If you need multiple start +/// harnesses compiled into the same binary, you can use the +/// `HARNESS_START_WITH_MAXIMUM_SIZE_INDEX` macro to specify different indices, +/// then enable them at runtime by configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The `max_size` value will be saved as the maximum testcase size. Fuzzing +/// test cases will be truncated to this size before being written to the +/// buffer. +/// +/// # Arguments +/// +/// - `buffer`: The pointer to the testcase buffer +/// - `max_size`: The maximum size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// HARNESS_START_WITH_MAXIMUM_SIZE(buffer, 1024); +/// ``` +#define HARNESS_START_WITH_MAXIMUM_SIZE(buffer, max_size) \ + do { \ + __orr_extended2(N_START_BUFFER_PTR_SIZE_VAL, buffer, max_size); \ + } while (0); + +/// Pseudo-hypercall number to signal the fuzzer to use the first argument to +/// the magic instruction as the pointer to the testcase buffer, the second +/// argument as a pointer to the size of the testcase buffer, and the third +/// argument as the maximum size of the testcase buffer. +#define N_START_BUFFER_PTR_SIZE_PTR_VAL 3 + +/// HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The default "index" of 0 will be used. If you need multiple start +/// harnesses compiled into the same binary, you can use the +/// `HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR_INDEX` macro to specify different +/// indices, then enable them at runtime by configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The address `size_ptr` will be saved. Each fuzzing iteration, the actual +/// size of the current testcase will be written to `*size_ptr`. +/// - The `max_size` value will be saved as the maximum testcase size. Fuzzing +/// test cases will be truncated to this size before being written to the +/// buffer. +/// +/// # Arguments +/// +/// - `buffer`: The pointer to the testcase buffer +/// - `size_ptr`: The pointer to the size of the testcase buffer +/// - `max_size`: The maximum size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// size_t size; +/// HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR(buffer, &size, 1024); +/// ``` +#define HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR(buffer, size_ptr, max_size) \ + do { \ + __orr_extended3(N_START_BUFFER_PTR_SIZE_PTR_VAL, buffer, size_ptr, \ + max_size); \ + } while (0); + +/// Pseudo-hypercall number to signal the fuzzer to stop the current fuzzing +/// iteration and reset to the beginning of the fuzzing loop with a "normal" +/// stop status, indicating no solution has occurred. +#define N_STOP_NORMAL 4 + +/// HARNESS_STOP +/// +/// Signal the fuzzer to stop and reset to the beginning of the fuzzing loop +/// with a "normal" stop status, indicating no solution has occurred. The +/// default index of 0 will be used. If you need to differentiate between +/// multiple stop harnesses compiled into the same binary, you can use the +/// `HARNESS_STOP_INDEX` macro to specify different indices, then enable them at +/// runtime by configuring the fuzzer. +/// +/// # Example +/// +/// ``` +/// HARNESS_STOP(); +/// ``` +#define HARNESS_STOP() \ + do { \ + __orr(N_STOP_NORMAL); \ + } while (0); + +/// Pseudo-hypercall number to signal the fuzzer that a custom assertion has +/// occurred, and the fuzzer should stop the current fuzzing iteration and reset +/// to the beginning of the fuzzing loop with a "solution" stop status. +#define N_STOP_ASSERT 5 + +/// HARNESS_ASSERT +/// +/// Signal the fuzzer that a custom assertion has occurred, and the fuzzer +/// should stop the current fuzzing iteration and reset to the beginning of the +/// fuzzing loop with a "solution" stop status. The default index of 0 will be +/// used. If you need to differentiate between multiple assertion harnesses +/// compiled into the same binary, you can use the `HARNESS_ASSERT_INDEX` macro +/// to specify different indices, then enable them at runtime by configuring the +/// fuzzer. +/// +/// # Example +/// +/// ``` +/// HARNESS_ASSERT(); +/// ``` +#define HARNESS_ASSERT() \ + do { \ + __orr_extended1(N_STOP_ASSERT, DEFAULT_INDEX); \ + } while (0); + +#endif // TSFFS_H +#else +#error "Unsupported platform!" +#endif +#elif _MSC_VER +#ifndef TSFFS_H +#define TSFFS_H + +/// Define common with LibFuzzer and other fuzzers to allow code that is +/// fuzzing-specific to be left in the codebase. See +/// https://llvm.org/docs/LibFuzzer.html#id35 for more information +#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION +#define FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION (1) +#endif // FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION + +#include + +/// HARNESS_START +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The default "index" of 0 will be used. If you need multiple start +/// harnesses compiled into the same binary, you can use the +/// `HARNESS_START_INDEX` macro to specify different indices, then enable them +/// at runtime by configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The size of the buffer pointed to by `size_ptr` will be saved as the +/// maximum testcase size. Each fuzzing iteration, the actual size of the +/// current testcase will be written to `*size_ptr`. +/// +/// # Arguments +/// +/// - `buffer`: The pointer to the testcase buffer +/// - `size_ptr`: The pointer to the size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// size_t size; +/// HARNESS_START(buffer, &size); +/// ``` +void HARNESS_START(void *buffer, void *size_ptr); + +/// HARNESS_START_INDEX +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The index specified by `start_index` will be used. If you need +/// multiple start harnesses compiled into the same binary, you can use this +/// macro to specify different indices, then enable them at runtime by +/// configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The size of the buffer pointed to by `size_ptr` will be saved as the +/// maximum testcase size. Each fuzzing iteration, the actual size of the +/// current testcase will be written to `*size_ptr`. +/// +/// # Arguments +/// +/// - `start_index`: The index to use for this start harness +/// - `buffer`: The pointer to the testcase buffer +/// - `size_ptr`: The pointer to the size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// size_t size; +/// HARNESS_START_INDEX(0x0001U, buffer, &size); +/// ``` +void HARNESS_START_INDEX(size_t start_index, void *buffer, void *size_ptr); + +/// HARNESS_START_WITH_MAXIMUM_SIZE +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The default "index" of 0 will be used. If you need multiple start +/// harnesses compiled into the same binary, you can use the +/// `HARNESS_START_WITH_MAXIMUM_SIZE_INDEX` macro to specify different indices, +/// then enable them at runtime by configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The `max_size` value will be saved as the maximum testcase size. Fuzzing +/// test cases will be truncated to this size before being written to the +/// buffer. +/// +/// # Arguments +/// +/// - `buffer`: The pointer to the testcase buffer +/// - `max_size`: The maximum size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// HARNESS_START_WITH_MAXIMUM_SIZE(buffer, 1024); +/// ``` +void HARNESS_START_WITH_MAXIMUM_SIZE(void *buffer, size_t max_size); + +/// HARNESS_START_WITH_MAXIMUM_SIZE_INDEX +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The index specified by `start_index` will be used. If you need +/// multiple start harnesses compiled into the same binary, you can use this +/// macro to specify different indices, then enable them at runtime by +/// configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The `max_size` value will be saved as the maximum testcase size. Fuzzing +/// test cases will be truncated to this size before being written to the +/// buffer. +/// +/// # Arguments +/// +/// - `start_index`: The index to use for this start harness +/// - `buffer`: The pointer to the testcase buffer +/// - `max_size`: The maximum size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// HARNESS_START_WITH_MAXIMUM_SIZE_INDEX(0x0001U, buffer, 1024); +/// ``` +void HARNESS_START_WITH_MAXIMUM_SIZE_INDEX(size_t start_index, void *buffer, + size_t max_size); + +/// HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The default "index" of 0 will be used. If you need multiple start +/// harnesses compiled into the same binary, you can use the +/// `HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR_INDEX` macro to specify different +/// indices, then enable them at runtime by configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The address `size_ptr` will be saved. Each fuzzing iteration, the actual +/// size of the current testcase will be written to `*size_ptr`. +/// - The `max_size` value will be saved as the maximum testcase size. Fuzzing +/// test cases will be truncated to this size before being written to the +/// buffer. +/// +/// # Arguments +/// +/// - `buffer`: The pointer to the testcase buffer +/// - `size_ptr`: The pointer to the size of the testcase buffer +/// - `max_size`: The maximum size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// size_t size; +/// HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR(buffer, &size, 1024); +/// ``` +void HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR(void *buffer, void *size_ptr, + size_t max_size); + +/// HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR_INDEX +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The index specified by `start_index` will be used. If you need +/// multiple start harnesses compiled into the same binary, you can use this +/// macro to specify different indices, then enable them at runtime by +/// configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The address `size_ptr` will be saved. Each fuzzing iteration, the actual +/// size of the current testcase will be written to `*size_ptr`. +/// - The `max_size` value will be saved as the maximum testcase size. Fuzzing +/// test cases will be truncated to this size before being written to the +/// buffer. +/// +/// # Arguments +/// +/// - `start_index`: The index to use for this start harness +/// - `buffer`: The pointer to the testcase buffer +/// - `size_ptr`: The pointer to the size of the testcase buffer +/// - `max_size`: The maximum size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// size_t size; +/// HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR_INDEX(0x0001U, buffer, &size, 1024); +/// ``` +void HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR_INDEX(size_t start_index, + void *buffer, void *size_ptr, + size_t max_size); + +/// HARNESS_STOP +/// +/// Signal the fuzzer to stop and reset to the beginning of the fuzzing loop +/// with a "normal" stop status, indicating no solution has occurred. The +/// default index of 0 will be used. If you need to differentiate between +/// multiple stop harnesses compiled into the same binary, you can use the +/// `HARNESS_STOP_INDEX` macro to specify different indices, then enable them at +/// runtime by configuring the fuzzer. +/// +/// # Example +/// +/// ``` +/// HARNESS_STOP(); +/// ``` +void HARNESS_STOP(void); + +/// HARNESS_STOP_INDEX +/// +/// Signal the fuzzer to stop and reset to the beginning of the fuzzing loop +/// with a "normal" stop status, indicating no solution has occurred. The index +/// specified by `stop_index` will be used. If you need to differentiate between +/// multiple stop harnesses compiled into the same binary, you can use this +/// macro to specify different indices, then enable them at runtime by +/// configuring the fuzzer. +/// +/// # Arguments +/// +/// - `stop_index`: The index to use for this stop harness +/// +/// # Example +/// +/// ``` +/// HARNESS_STOP_INDEX(0x0001U); +/// ``` +void HARNESS_STOP_INDEX(size_t stop_index); + +/// HARNESS_ASSERT +/// +/// Signal the fuzzer that a custom assertion has occurred, and the fuzzer +/// should stop the current fuzzing iteration and reset to the beginning of the +/// fuzzing loop with a "solution" stop status. The default index of 0 will be +/// used. If you need to differentiate between multiple assertion harnesses +/// compiled into the same binary, you can use the `HARNESS_ASSERT_INDEX` macro +/// to specify different indices, then enable them at runtime by configuring the +/// fuzzer. +/// +/// # Example +/// +/// ``` +/// HARNESS_ASSERT(); +/// ``` +void HARNESS_ASSERT(void); + +/// HARNESS_ASSERT_INDEX +/// +/// Signal the fuzzer that a custom assertion has occurred, and the fuzzer +/// should stop the current fuzzing iteration and reset to the beginning of the +/// fuzzing loop with a "solution" stop status. The index specified by +/// `assert_index` will be used. If you need to differentiate between multiple +/// assertion harnesses compiled into the same binary, you can use this macro to +/// specify different indices, then enable them at runtime by configuring the +/// fuzzer. +/// +/// # Arguments +/// +/// - `assert_index`: The index to use for this assertion harness +/// +/// # Example +/// +/// ``` +/// HARNESS_ASSERT_INDEX(0x0001U); +/// ``` +void HARNESS_ASSERT_INDEX(size_t assert_index); + +#endif // TSFFS_H +#else +#error "Unsupported compiler!" +#endif diff --git a/examples/tutorials/risc-v-kernel/src/tutorial-kernel-modules/package/kernel-modules/tutorial-mod/tsffs-gcc-riscv64.h b/examples/tutorials/risc-v-kernel/src/tutorial-kernel-modules/package/kernel-modules/tutorial-mod/tsffs-gcc-riscv64.h deleted file mode 100644 index f6acf1ea..00000000 --- a/examples/tutorials/risc-v-kernel/src/tutorial-kernel-modules/package/kernel-modules/tutorial-mod/tsffs-gcc-riscv64.h +++ /dev/null @@ -1,124 +0,0 @@ -// Copyright (C) 2024 Intel Corporation -// SPDX-License-Identifier: Apache-2.0 - -/// Definitions and macros for compiled-in harnessing of C and C++ target -/// software for the RISC-V (64-bit) architecture -/// -/// Example: -/// ```c -/// #include "tsffs-gcc-riscv64.h" -/// -/// int main() { -/// char buf[0x10]; -/// size_t size = 0x10; -/// size_t *size_ptr = &size; -/// HARNESS_START((char *)buf, size_ptr); -/// int retval = YourSpecialDecoder(buf, *size_ptr); -/// if (!retval) { -/// HARNESS_ASSERT(); -/// } else { -/// HARNESS_STOP(); -/// } -/// } -/// ``` - -#ifndef TSFFS_H -#define TSFFS_H - -/// Define common with LibFuzzer and other fuzzers to allow code that is -/// fuzzing-specific to be left in the codebase. See -/// https://llvm.org/docs/LibFuzzer.html#id35 for more information -#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION -#define FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION (1) -#endif // FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION - -/// Default magic instruction `n` value to signal the TSFFS fuzzer to start the -/// fuzzing loop -#define MAGIC_START (0x0001U) -/// Default magic instruction `n` value to signal the TSFFS fuzzer to stop and -/// reset the fuzzing loop -#define MAGIC_STOP (0x0002U) -/// Default magic instruction `n` value to signal the TSFFS fuzzer that an error -/// has occurred and the testcase input should be saved as a solution -#define MAGIC_ASSERT (0x0003U) - -/// Alternative magic numbers that can be used for start and stop events in -/// conjunction with setting the magic number for each event via the SIMICS or -/// SIMICS Python script interface -#define MAGIC_ALT_0 (0x0004U) -#define MAGIC_ALT_1 (0x0005U) -#define MAGIC_ALT_2 (0x0006U) -#define MAGIC_ALT_3 (0x0007U) -#define MAGIC_ALT_4 (0x0008U) -#define MAGIC_ALT_5 (0x0009U) -#define MAGIC_ALT_6 (0x000aU) -#define MAGIC_ALT_7 (0x000bU) - -/// Invoke the magic instruction defined by SIMICS for the RISC-V architecture -/// (`srai zero, zero, n`) with a specific value of `n`, after setting register -/// `a0` to the value of the pointer to the testcase and register `a1` to the -/// value of the pointer to the testcase size. These registers are accessed by -/// the fuzzer and are defined per-architecture. -#define __srai_extended(value, testcase_ptr, size_ptr) \ - __asm__ __volatile__("mv a0, %0; mv a1, %1; srai zero, zero, %2" \ - : \ - : "r"(testcase_ptr), "r"(size_ptr), "I"(value) \ - : "a0", "a1"); - -/// Invoke the magic instruction defined by SIMICS for the RISC-V architecture -/// (`srai zero, zero, n`) with a specific value of `n` -#define __srai(value) \ - __asm__ __volatile__("srai zero, zero, %0" : : "I"(value) :) - -/// Signal the fuzzer using a specific magic value `start` to start the fuzzing -/// loop at the point this macro is called. A snapshot will be taken when the -/// macro is called, and the maximum testcase size `*size_ptr` will be saved as -/// `max_testcase_size`. Each iteration of the fuzzing loop, the fuzzer input -/// (the "testcase") will be written to `*testcase_ptr` as if running -/// `memcpy(testcase_ptr, current_testcase, max_testcase_size)`, and the actual -/// size of the current testcase will be written to -/// `*size_ptr` as if running `*size_ptr = current_testcase_size`. -#define __arch_harness_start(start, testcase_ptr, size_ptr) \ - do { \ - __srai_extended(start, testcase_ptr, size_ptr); \ - } while (0) - -/// Signal the fuzzer using the a specific magic value (`stop`) to stop and -/// reset to the beginning of the fuzzing loop with a "normal" stop status, -/// indicating no solution has occurred. -#define __arch_harness_stop(stop) \ - do { \ - __srai(stop); \ - } while (0) - -/// Signal the fuzzer using the default magic value to start the fuzzing loop at -/// the point this macro is called. A snapshot will be taken when the macro is -/// called, and the maximum testcase size `*size_ptr` will be saved as -/// `max_testcase_size`. Each iteration of the fuzzing loop, the fuzzer input -/// (the "testcase") will be written to -/// `*testcase_ptr` as if running `memcpy(testcase_ptr, current_testcase, -/// max_testcase_size)`, and the actual size of the current testcase will be -/// written to -/// `*size_ptr` as if running `*size_ptr = current_testcase_size`. -#define HARNESS_START(testcase_ptr, size_ptr) \ - do { \ - __arch_harness_start(MAGIC_START, testcase_ptr, size_ptr); \ - } while (0) - -/// Signal the fuzzer using the default magic value to stop and reset to the -/// beginning of the fuzzing loop with a "normal" stop status, indicating no -/// solution has occurred. -#define HARNESS_STOP() \ - do { \ - __arch_harness_stop(MAGIC_STOP); \ - } while (0) - -/// Signal the fuzzer using the default magic value to stop and reset to the -/// beginning of the fuzzing loop with a "solution" stop status, indicating some -/// custom error has occurred. -#define HARNESS_ASSERT() \ - do { \ - __arch_harness_stop(MAGIC_ASSERT); \ - } while (0) - -#endif // TSFFS_H \ No newline at end of file diff --git a/examples/tutorials/risc-v-kernel/src/tutorial-kernel-modules/package/kernel-modules/tutorial-mod/tsffs.h b/examples/tutorials/risc-v-kernel/src/tutorial-kernel-modules/package/kernel-modules/tutorial-mod/tsffs.h new file mode 100644 index 00000000..95b604fd --- /dev/null +++ b/examples/tutorials/risc-v-kernel/src/tutorial-kernel-modules/package/kernel-modules/tutorial-mod/tsffs.h @@ -0,0 +1,2876 @@ +// Copyright (C) 2024 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 + +#ifdef __GNUC__ +#ifdef __i386__ +// Copyright (C) 2024 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 + +/// Definitions and macros for compiled-in harnessing of C and C++ target +/// software for the X86 architecture. + +#ifndef TSFFS_H +#define TSFFS_H + +/// Define common with LibFuzzer and other fuzzers to allow code that is +/// fuzzing-specific to be left in the codebase. See +/// https://llvm.org/docs/LibFuzzer.html#id35 for more information +#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION +#define FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION (1) +#endif // FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION + +/// __cpuid +/// +/// Invoke the CPUID instruction with a specific value `value` in register +/// `rax`. +/// +/// # Arguments +/// +/// - `value`: The value to load into the `rax` register before invoking the +/// CPUID instruction +#define __cpuid(value) \ + unsigned int _a __attribute__((unused)) = 0; \ + unsigned int _b __attribute__((unused)) = 0; \ + unsigned int _c __attribute__((unused)) = 0; \ + unsigned int _d __attribute__((unused)) = 0; \ + __asm__ __volatile__("cpuid\n\t" \ + : "=a"(_a), "=b"(_b), "=c"(_c), "=d"(_d) \ + : "a"(value)); + +/// __cpuid_extended1 +/// +/// Invoke the CPUID instruction with a specific value `value` in register +/// `rax` and a pseudo-argument in register `rdi`. +/// +/// # Arguments +/// +/// - `value`: The value to load into the `rax` register before invoking the +/// CPUID instruction +/// - `arg0`: The value to load into the `rdi` register before invoking the +/// CPUID instruction +#define __cpuid_extended1(value, arg0) \ + unsigned int _a __attribute__((unused)) = 0; \ + unsigned int _b __attribute__((unused)) = 0; \ + unsigned int _c __attribute__((unused)) = 0; \ + unsigned int _d __attribute__((unused)) = 0; \ + __asm__ __volatile__("cpuid\n\t" \ + : "=a"(_a), "=b"(_b), "=c"(_c), "=d"(_d) \ + : "a"(value), "D"(arg0)); + +/// __cpuid_extended2 +/// +/// Invoke the CPUID instruction with a specific value `value` in register +/// `rax` and a pseudo-arguments in registers `rdi` and `rsi`. +/// +/// # Arguments +/// +/// - `value`: The value to load into the `rax` register before invoking the +/// CPUID instruction +/// - `arg0`: The value to load into the `rdi` register before invoking the +/// CPUID instruction +/// - `arg1`: The value to load into the `rsi` register before invoking the +/// CPUID instruction +#define __cpuid_extended2(value, arg0, arg1) \ + unsigned int _a __attribute__((unused)) = 0; \ + unsigned int _b __attribute__((unused)) = 0; \ + unsigned int _c __attribute__((unused)) = 0; \ + unsigned int _d __attribute__((unused)) = 0; \ + __asm__ __volatile__("cpuid\n\t" \ + : "=a"(_a), "=b"(_b), "=c"(_c), "=d"(_d) \ + : "a"(value), "D"(arg0), "S"(arg1)); + +/// __cpuid_extended3 +/// +/// Invoke the CPUID instruction with a specific value `value` in register +/// `rax` and a pseudo-arguments in registers `rdi`, `rsi`, and `rdx`. +/// +/// # Arguments +/// +/// - `value`: The value to load into the `rax` register before invoking the +/// CPUID instruction +/// - `arg0`: The value to load into the `rdi` register before invoking the +/// CPUID instruction +/// - `arg1`: The value to load into the `rsi` register before invoking the +/// CPUID instruction +/// - `arg2`: The value to load into the `rdx` register before invoking the +/// CPUID instruction +#define __cpuid_extended3(value, arg0, arg1, arg2) \ + unsigned int _a __attribute__((unused)) = 0; \ + unsigned int _b __attribute__((unused)) = 0; \ + unsigned int _c __attribute__((unused)) = 0; \ + unsigned int _d __attribute__((unused)) = 0; \ + __asm__ __volatile__("cpuid\n\t" \ + : "=a"(_a), "=b"(_b), "=c"(_c), "=d"(_d) \ + : "a"(value), "D"(arg0), "S"(arg1), "d"(arg2)); + +/// __cpuid_extended4 +/// +/// Invoke the CPUID instruction with a specific value `value` in register +/// `rax` and a pseudo-arguments in registers `rdi`, `rsi`, `rdx`, and `rcx`. +/// +/// # Arguments +/// +/// - `value`: The value to load into the `rax` register before invoking the +/// CPUID instruction +/// - `arg0`: The value to load into the `rdi` register before invoking the +/// CPUID instruction +/// - `arg1`: The value to load into the `rsi` register before invoking the +/// CPUID instruction +/// - `arg2`: The value to load into the `rdx` register before invoking the +/// CPUID instruction +/// - `arg3`: The value to load into the `rcx` register before invoking the +/// CPUID instruction +#define __cpuid_extended4(value, arg0, arg1, arg2, arg3) \ + unsigned int _a __attribute__((unused)) = 0; \ + unsigned int _b __attribute__((unused)) = 0; \ + unsigned int _c __attribute__((unused)) = 0; \ + unsigned int _d __attribute__((unused)) = 0; \ + __asm__ __volatile__("cpuid\n\t" \ + : "=a"(_a), "=b"(_b), "=c"(_c), "=d"(_d) \ + : "a"(value), "D"(arg0), "S"(arg1), "d"(arg2), \ + "c"(arg3)); + +/// Magic value defined by SIMICS as the "leaf" value of a CPUID instruction +/// that is treated as a magic instruction. +#define MAGIC (0x4711U) + +/// The default index number used for magic instructions. All magic instructions +/// support multiple start and stop indices, which defaults to 0 if not +/// specified. +#define DEFAULT_INDEX (0x0000U) + +/// Pseudo-hypercall number to signal the fuzzer to use the first argument to +/// the magic instruction as the pointer to the testcase buffer and the second +/// argument as a pointer to the size of the testcase buffer. +#define N_START_BUFFER_PTR_SIZE_PTR (0x0001U) + +/// HARNESS_START +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The default "index" of 0 will be used. If you need multiple start +/// harnesses compiled into the same binary, you can use the +/// `HARNESS_START_INDEX` macro to specify different indices, then enable them +/// at runtime by configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The size of the buffer pointed to by `size_ptr` will be saved as the +/// maximum testcase size. Each fuzzing iteration, the actual size of the +/// current testcase will be written to `*size_ptr`. +/// +/// # Arguments +/// +/// - `buffer`: The pointer to the testcase buffer +/// - `size_ptr`: The pointer to the size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// size_t size; +/// HARNESS_START(buffer, &size); +/// ``` +#define HARNESS_START(buffer, size_ptr) \ + do { \ + unsigned int value = (N_START_BUFFER_PTR_SIZE_PTR << 0x10U) | MAGIC; \ + __cpuid_extended3(value, DEFAULT_INDEX, buffer, size_ptr); \ + } while (0); + +/// HARNESS_START_INDEX +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The index specified by `start_index` will be used. If you need +/// multiple start harnesses compiled into the same binary, you can use this +/// macro to specify different indices, then enable them at runtime by +/// configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The size of the buffer pointed to by `size_ptr` will be saved as the +/// maximum testcase size. Each fuzzing iteration, the actual size of the +/// current testcase will be written to `*size_ptr`. +/// +/// # Arguments +/// +/// - `start_index`: The index to use for this start harness +/// - `buffer`: The pointer to the testcase buffer +/// - `size_ptr`: The pointer to the size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// size_t size; +/// HARNESS_START_INDEX(0x0001U, buffer, &size); +/// ``` +#define HARNESS_START_INDEX(start_index, buffer, size_ptr) \ + do { \ + unsigned int value = (N_START_BUFFER_PTR_SIZE_PTR << 0x10U) | MAGIC; \ + __cpuid_extended3(value, start_index, buffer, size_ptr); \ + } while (0); + +/// Pseudo-hypercall number to signal the fuzzer to use the first argument to +/// the magic instruction as the pointer to the testcase buffer and the second +/// argument as the maximum size of the testcase buffer. +#define N_START_BUFFER_PTR_SIZE_VAL (0x0002U) + +/// HARNESS_START_WITH_MAXIMUM_SIZE +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The default "index" of 0 will be used. If you need multiple start +/// harnesses compiled into the same binary, you can use the +/// `HARNESS_START_WITH_MAXIMUM_SIZE_INDEX` macro to specify different indices, +/// then enable them at runtime by configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The `max_size` value will be saved as the maximum testcase size. Fuzzing +/// test cases will be truncated to this size before being written to the +/// buffer. +/// +/// # Arguments +/// +/// - `buffer`: The pointer to the testcase buffer +/// - `max_size`: The maximum size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// HARNESS_START_WITH_MAXIMUM_SIZE(buffer, 1024); +/// ``` +#define HARNESS_START_WITH_MAXIMUM_SIZE(buffer, max_size) \ + do { \ + unsigned int value = (N_START_BUFFER_PTR_SIZE_VAL << 0x10U) | MAGIC; \ + __cpuid_extended3(value, DEFAULT_INDEX, buffer, max_size); \ + } while (0); + +/// HARNESS_START_WITH_MAXIMUM_SIZE_INDEX +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The index specified by `start_index` will be used. If you need +/// multiple start harnesses compiled into the same binary, you can use this +/// macro to specify different indices, then enable them at runtime by +/// configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The `max_size` value will be saved as the maximum testcase size. Fuzzing +/// test cases will be truncated to this size before being written to the +/// buffer. +/// +/// # Arguments +/// +/// - `start_index`: The index to use for this start harness +/// - `buffer`: The pointer to the testcase buffer +/// - `max_size`: The maximum size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// HARNESS_START_WITH_MAXIMUM_SIZE_INDEX(0x0001U, buffer, 1024); +/// ``` +#define HARNESS_START_WITH_MAXIMUM_SIZE_INDEX(start_index, buffer, max_size) \ + do { \ + unsigned int value = (N_START_BUFFER_PTR_SIZE_VAL << 0x10U) | MAGIC; \ + __cpuid_extended3(value, start_index, buffer, max_size); \ + } while (0); + +/// Pseudo-hypercall number to signal the fuzzer to use the first argument to +/// the magic instruction as the pointer to the testcase buffer, the second +/// argument as a pointer to the size of the testcase buffer, and the third +/// argument as the maximum size of the testcase buffer. +#define N_START_BUFFER_PTR_SIZE_PTR_VAL (0x0003U) + +/// HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The default "index" of 0 will be used. If you need multiple start +/// harnesses compiled into the same binary, you can use the +/// `HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR_INDEX` macro to specify different +/// indices, then enable them at runtime by configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The address `size_ptr` will be saved. Each fuzzing iteration, the actual +/// size of the current testcase will be written to `*size_ptr`. +/// - The `max_size` value will be saved as the maximum testcase size. Fuzzing +/// test cases will be truncated to this size before being written to the +/// buffer. +/// +/// # Arguments +/// +/// - `buffer`: The pointer to the testcase buffer +/// - `size_ptr`: The pointer to the size of the testcase buffer +/// - `max_size`: The maximum size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// size_t size; +/// HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR(buffer, &size, 1024); +/// ``` +#define HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR(buffer, size_ptr, max_size) \ + do { \ + unsigned int value = (N_START_BUFFER_PTR_SIZE_PTR_VAL << 0x10U) | MAGIC; \ + __cpuid_extended4(value, DEFAULT_INDEX, buffer, size_ptr, max_size); \ + } while (0); + +/// HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR_INDEX +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The index specified by `start_index` will be used. If you need +/// multiple start harnesses compiled into the same binary, you can use this +/// macro to specify different indices, then enable them at runtime by +/// configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The address `size_ptr` will be saved. Each fuzzing iteration, the actual +/// size of the current testcase will be written to `*size_ptr`. +/// - The `max_size` value will be saved as the maximum testcase size. Fuzzing +/// test cases will be truncated to this size before being written to the +/// buffer. +/// +/// # Arguments +/// +/// - `start_index`: The index to use for this start harness +/// - `buffer`: The pointer to the testcase buffer +/// - `size_ptr`: The pointer to the size of the testcase buffer +/// - `max_size`: The maximum size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// size_t size; +/// HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR_INDEX(0x0001U, buffer, &size, 1024); +/// ``` +#define HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR_INDEX(start_index, buffer, \ + size_ptr, max_size) \ + do { \ + unsigned int value = (N_START_BUFFER_PTR_SIZE_PTR_VAL << 0x10U) | MAGIC; \ + __cpuid_extended4(value, start_index, buffer, size_ptr, max_size); \ + } while (0); + +/// Pseudo-hypercall number to signal the fuzzer to stop the current fuzzing +/// iteration and reset to the beginning of the fuzzing loop with a "normal" +/// stop status, indicating no solution has occurred. +#define N_STOP_NORMAL (0x0004U) + +/// HARNESS_STOP +/// +/// Signal the fuzzer to stop and reset to the beginning of the fuzzing loop +/// with a "normal" stop status, indicating no solution has occurred. The +/// default index of 0 will be used. If you need to differentiate between +/// multiple stop harnesses compiled into the same binary, you can use the +/// `HARNESS_STOP_INDEX` macro to specify different indices, then enable them at +/// runtime by configuring the fuzzer. +/// +/// # Example +/// +/// ``` +/// HARNESS_STOP(); +/// ``` +#define HARNESS_STOP() \ + do { \ + unsigned int value = (N_STOP_NORMAL << 0x10U) | MAGIC; \ + __cpuid_extended1(value, DEFAULT_INDEX); \ + } while (0); + +/// HARNESS_STOP_INDEX +/// +/// Signal the fuzzer to stop and reset to the beginning of the fuzzing loop +/// with a "normal" stop status, indicating no solution has occurred. The index +/// specified by `stop_index` will be used. If you need to differentiate between +/// multiple stop harnesses compiled into the same binary, you can use this +/// macro to specify different indices, then enable them at runtime by +/// configuring the fuzzer. +/// +/// # Arguments +/// +/// - `stop_index`: The index to use for this stop harness +/// +/// # Example +/// +/// ``` +/// HARNESS_STOP_INDEX(0x0001U); +/// ``` +#define HARNESS_STOP_INDEX(stop_index) \ + do { \ + unsigned int value = (N_STOP_NORMAL << 0x10U) | MAGIC; \ + __cpuid_extended1(value, stop_index); \ + } while (0); + +/// Pseudo-hypercall number to signal the fuzzer that a custom assertion has +/// occurred, and the fuzzer should stop the current fuzzing iteration and reset +/// to the beginning of the fuzzing loop with a "solution" stop status. +#define N_STOP_ASSERT (0x0005U) + +/// HARNESS_ASSERT +/// +/// Signal the fuzzer that a custom assertion has occurred, and the fuzzer +/// should stop the current fuzzing iteration and reset to the beginning of the +/// fuzzing loop with a "solution" stop status. The default index of 0 will be +/// used. If you need to differentiate between multiple assertion harnesses +/// compiled into the same binary, you can use the `HARNESS_ASSERT_INDEX` macro +/// to specify different indices, then enable them at runtime by configuring the +/// fuzzer. +/// +/// # Example +/// +/// ``` +/// HARNESS_ASSERT(); +/// ``` +#define HARNESS_ASSERT() \ + do { \ + unsigned int value = (N_STOP_ASSERT << 0x10U) | MAGIC; \ + __cpuid_extended1(value, DEFAULT_INDEX); \ + } while (0); + +/// HARNESS_ASSERT_INDEX +/// +/// Signal the fuzzer that a custom assertion has occurred, and the fuzzer +/// should stop the current fuzzing iteration and reset to the beginning of the +/// fuzzing loop with a "solution" stop status. The index specified by +/// `assert_index` will be used. If you need to differentiate between multiple +/// assertion harnesses compiled into the same binary, you can use this macro to +/// specify different indices, then enable them at runtime by configuring the +/// fuzzer. +/// +/// # Arguments +/// +/// - `assert_index`: The index to use for this assertion harness +/// +/// # Example +/// +/// ``` +/// HARNESS_ASSERT_INDEX(0x0001U); +/// ``` +#define HARNESS_ASSERT_INDEX(assert_index) \ + do { \ + unsigned int value = (N_STOP_ASSERT << 0x10U) | MAGIC; \ + __cpuid_extended1(value, assert_index); \ + } while (0); + +#endif // TSFFS_H +#elif __x86_64__ +// Copyright (C) 2024 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 + +/// Definitions and macros for compiled-in harnessing of C and C++ target +/// software for the x86_64 architecture. + +#ifndef TSFFS_H +#define TSFFS_H + +/// Define common with LibFuzzer and other fuzzers to allow code that is +/// fuzzing-specific to be left in the codebase. See +/// https://llvm.org/docs/LibFuzzer.html#id35 for more information +#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION +#define FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION (1) +#endif // FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION + +/// __cpuid +/// +/// Invoke the CPUID instruction with a specific value `value` in register +/// `rax`. +/// +/// # Arguments +/// +/// - `value`: The value to load into the `rax` register before invoking the +/// CPUID instruction +#define __cpuid(value) \ + unsigned int _a __attribute__((unused)) = 0; \ + unsigned int _b __attribute__((unused)) = 0; \ + unsigned int _c __attribute__((unused)) = 0; \ + unsigned int _d __attribute__((unused)) = 0; \ + __asm__ __volatile__("cpuid\n\t" \ + : "=a"(_a), "=b"(_b), "=c"(_c), "=d"(_d) \ + : "a"(value)); + +/// __cpuid_extended1 +/// +/// Invoke the CPUID instruction with a specific value `value` in register +/// `rax` and a pseudo-argument in register `rdi`. +/// +/// # Arguments +/// +/// - `value`: The value to load into the `rax` register before invoking the +/// CPUID instruction +/// - `arg0`: The value to load into the `rdi` register before invoking the +/// CPUID instruction +#define __cpuid_extended1(value, arg0) \ + unsigned int _a __attribute__((unused)) = 0; \ + unsigned int _b __attribute__((unused)) = 0; \ + unsigned int _c __attribute__((unused)) = 0; \ + unsigned int _d __attribute__((unused)) = 0; \ + __asm__ __volatile__("cpuid\n\t" \ + : "=a"(_a), "=b"(_b), "=c"(_c), "=d"(_d) \ + : "a"(value), "D"(arg0)); + +/// __cpuid_extended2 +/// +/// Invoke the CPUID instruction with a specific value `value` in register +/// `rax` and a pseudo-arguments in registers `rdi` and `rsi`. +/// +/// # Arguments +/// +/// - `value`: The value to load into the `rax` register before invoking the +/// CPUID instruction +/// - `arg0`: The value to load into the `rdi` register before invoking the +/// CPUID instruction +/// - `arg1`: The value to load into the `rsi` register before invoking the +/// CPUID instruction +#define __cpuid_extended2(value, arg0, arg1) \ + unsigned int _a __attribute__((unused)) = 0; \ + unsigned int _b __attribute__((unused)) = 0; \ + unsigned int _c __attribute__((unused)) = 0; \ + unsigned int _d __attribute__((unused)) = 0; \ + __asm__ __volatile__("cpuid\n\t" \ + : "=a"(_a), "=b"(_b), "=c"(_c), "=d"(_d) \ + : "a"(value), "D"(arg0), "S"(arg1)); + +/// __cpuid_extended3 +/// +/// Invoke the CPUID instruction with a specific value `value` in register +/// `rax` and a pseudo-arguments in registers `rdi`, `rsi`, and `rdx`. +/// +/// # Arguments +/// +/// - `value`: The value to load into the `rax` register before invoking the +/// CPUID instruction +/// - `arg0`: The value to load into the `rdi` register before invoking the +/// CPUID instruction +/// - `arg1`: The value to load into the `rsi` register before invoking the +/// CPUID instruction +/// - `arg2`: The value to load into the `rdx` register before invoking the +/// CPUID instruction +#define __cpuid_extended3(value, arg0, arg1, arg2) \ + unsigned int _a __attribute__((unused)) = 0; \ + unsigned int _b __attribute__((unused)) = 0; \ + unsigned int _c __attribute__((unused)) = 0; \ + unsigned int _d __attribute__((unused)) = 0; \ + __asm__ __volatile__("cpuid\n\t" \ + : "=a"(_a), "=b"(_b), "=c"(_c), "=d"(_d) \ + : "a"(value), "D"(arg0), "S"(arg1), "d"(arg2)); + +/// __cpuid_extended4 +/// +/// Invoke the CPUID instruction with a specific value `value` in register +/// `rax` and a pseudo-arguments in registers `rdi`, `rsi`, `rdx`, and `rcx`. +/// +/// # Arguments +/// +/// - `value`: The value to load into the `rax` register before invoking the +/// CPUID instruction +/// - `arg0`: The value to load into the `rdi` register before invoking the +/// CPUID instruction +/// - `arg1`: The value to load into the `rsi` register before invoking the +/// CPUID instruction +/// - `arg2`: The value to load into the `rdx` register before invoking the +/// CPUID instruction +/// - `arg3`: The value to load into the `rcx` register before invoking the +/// CPUID instruction +#define __cpuid_extended4(value, arg0, arg1, arg2, arg3) \ + unsigned int _a __attribute__((unused)) = 0; \ + unsigned int _b __attribute__((unused)) = 0; \ + unsigned int _c __attribute__((unused)) = 0; \ + unsigned int _d __attribute__((unused)) = 0; \ + __asm__ __volatile__("cpuid\n\t" \ + : "=a"(_a), "=b"(_b), "=c"(_c), "=d"(_d) \ + : "a"(value), "D"(arg0), "S"(arg1), "d"(arg2), \ + "c"(arg3)); + +/// Magic value defined by SIMICS as the "leaf" value of a CPUID instruction +/// that is treated as a magic instruction. +#define MAGIC (0x4711U) + +/// The default index number used for magic instructions. All magic instructions +/// support multiple start and stop indices, which defaults to 0 if not +/// specified. +#define DEFAULT_INDEX (0x0000U) + +/// Pseudo-hypercall number to signal the fuzzer to use the first argument to +/// the magic instruction as the pointer to the testcase buffer and the second +/// argument as a pointer to the size of the testcase buffer. +#define N_START_BUFFER_PTR_SIZE_PTR (0x0001U) + +/// HARNESS_START +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The default "index" of 0 will be used. If you need multiple start +/// harnesses compiled into the same binary, you can use the +/// `HARNESS_START_INDEX` macro to specify different indices, then enable them +/// at runtime by configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The size of the buffer pointed to by `size_ptr` will be saved as the +/// maximum testcase size. Each fuzzing iteration, the actual size of the +/// current testcase will be written to `*size_ptr`. +/// +/// # Arguments +/// +/// - `buffer`: The pointer to the testcase buffer +/// - `size_ptr`: The pointer to the size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// size_t size; +/// HARNESS_START(buffer, &size); +/// ``` +#define HARNESS_START(buffer, size_ptr) \ + do { \ + unsigned int value = (N_START_BUFFER_PTR_SIZE_PTR << 0x10U) | MAGIC; \ + __cpuid_extended3(value, DEFAULT_INDEX, buffer, size_ptr); \ + } while (0); + +/// HARNESS_START_INDEX +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The index specified by `start_index` will be used. If you need +/// multiple start harnesses compiled into the same binary, you can use this +/// macro to specify different indices, then enable them at runtime by +/// configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The size of the buffer pointed to by `size_ptr` will be saved as the +/// maximum testcase size. Each fuzzing iteration, the actual size of the +/// current testcase will be written to `*size_ptr`. +/// +/// # Arguments +/// +/// - `start_index`: The index to use for this start harness +/// - `buffer`: The pointer to the testcase buffer +/// - `size_ptr`: The pointer to the size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// size_t size; +/// HARNESS_START_INDEX(0x0001U, buffer, &size); +/// ``` +#define HARNESS_START_INDEX(start_index, buffer, size_ptr) \ + do { \ + unsigned int value = (N_START_BUFFER_PTR_SIZE_PTR << 0x10U) | MAGIC; \ + __cpuid_extended3(value, start_index, buffer, size_ptr); \ + } while (0); + +/// Pseudo-hypercall number to signal the fuzzer to use the first argument to +/// the magic instruction as the pointer to the testcase buffer and the second +/// argument as the maximum size of the testcase buffer. +#define N_START_BUFFER_PTR_SIZE_VAL (0x0002U) + +/// HARNESS_START_WITH_MAXIMUM_SIZE +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The default "index" of 0 will be used. If you need multiple start +/// harnesses compiled into the same binary, you can use the +/// `HARNESS_START_WITH_MAXIMUM_SIZE_INDEX` macro to specify different indices, +/// then enable them at runtime by configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The `max_size` value will be saved as the maximum testcase size. Fuzzing +/// test cases will be truncated to this size before being written to the +/// buffer. +/// +/// # Arguments +/// +/// - `buffer`: The pointer to the testcase buffer +/// - `max_size`: The maximum size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// HARNESS_START_WITH_MAXIMUM_SIZE(buffer, 1024); +/// ``` +#define HARNESS_START_WITH_MAXIMUM_SIZE(buffer, max_size) \ + do { \ + unsigned int value = (N_START_BUFFER_PTR_SIZE_VAL << 0x10U) | MAGIC; \ + __cpuid_extended3(value, DEFAULT_INDEX, buffer, max_size); \ + } while (0); + +/// HARNESS_START_WITH_MAXIMUM_SIZE_INDEX +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The index specified by `start_index` will be used. If you need +/// multiple start harnesses compiled into the same binary, you can use this +/// macro to specify different indices, then enable them at runtime by +/// configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The `max_size` value will be saved as the maximum testcase size. Fuzzing +/// test cases will be truncated to this size before being written to the +/// buffer. +/// +/// # Arguments +/// +/// - `start_index`: The index to use for this start harness +/// - `buffer`: The pointer to the testcase buffer +/// - `max_size`: The maximum size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// HARNESS_START_WITH_MAXIMUM_SIZE_INDEX(0x0001U, buffer, 1024); +/// ``` +#define HARNESS_START_WITH_MAXIMUM_SIZE_INDEX(start_index, buffer, max_size) \ + do { \ + unsigned int value = (N_START_BUFFER_PTR_SIZE_VAL << 0x10U) | MAGIC; \ + __cpuid_extended3(value, start_index, buffer, max_size); \ + } while (0); + +/// Pseudo-hypercall number to signal the fuzzer to use the first argument to +/// the magic instruction as the pointer to the testcase buffer, the second +/// argument as a pointer to the size of the testcase buffer, and the third +/// argument as the maximum size of the testcase buffer. +#define N_START_BUFFER_PTR_SIZE_PTR_VAL (0x0003U) + +/// HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The default "index" of 0 will be used. If you need multiple start +/// harnesses compiled into the same binary, you can use the +/// `HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR_INDEX` macro to specify different +/// indices, then enable them at runtime by configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The address `size_ptr` will be saved. Each fuzzing iteration, the actual +/// size of the current testcase will be written to `*size_ptr`. +/// - The `max_size` value will be saved as the maximum testcase size. Fuzzing +/// test cases will be truncated to this size before being written to the +/// buffer. +/// +/// # Arguments +/// +/// - `buffer`: The pointer to the testcase buffer +/// - `size_ptr`: The pointer to the size of the testcase buffer +/// - `max_size`: The maximum size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// size_t size; +/// HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR(buffer, &size, 1024); +/// ``` +#define HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR(buffer, size_ptr, max_size) \ + do { \ + unsigned int value = (N_START_BUFFER_PTR_SIZE_PTR_VAL << 0x10U) | MAGIC; \ + __cpuid_extended4(value, DEFAULT_INDEX, buffer, size_ptr, max_size); \ + } while (0); + +/// HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR_INDEX +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The index specified by `start_index` will be used. If you need +/// multiple start harnesses compiled into the same binary, you can use this +/// macro to specify different indices, then enable them at runtime by +/// configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The address `size_ptr` will be saved. Each fuzzing iteration, the actual +/// size of the current testcase will be written to `*size_ptr`. +/// - The `max_size` value will be saved as the maximum testcase size. Fuzzing +/// test cases will be truncated to this size before being written to the +/// buffer. +/// +/// # Arguments +/// +/// - `start_index`: The index to use for this start harness +/// - `buffer`: The pointer to the testcase buffer +/// - `size_ptr`: The pointer to the size of the testcase buffer +/// - `max_size`: The maximum size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// size_t size; +/// HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR_INDEX(0x0001U, buffer, &size, 1024); +/// ``` +#define HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR_INDEX(start_index, buffer, \ + size_ptr, max_size) \ + do { \ + unsigned int value = (N_START_BUFFER_PTR_SIZE_PTR_VAL << 0x10U) | MAGIC; \ + __cpuid_extended4(value, start_index, buffer, size_ptr, max_size); \ + } while (0); + +/// Pseudo-hypercall number to signal the fuzzer to stop the current fuzzing +/// iteration and reset to the beginning of the fuzzing loop with a "normal" +/// stop status, indicating no solution has occurred. +#define N_STOP_NORMAL (0x0004U) + +/// HARNESS_STOP +/// +/// Signal the fuzzer to stop and reset to the beginning of the fuzzing loop +/// with a "normal" stop status, indicating no solution has occurred. The +/// default index of 0 will be used. If you need to differentiate between +/// multiple stop harnesses compiled into the same binary, you can use the +/// `HARNESS_STOP_INDEX` macro to specify different indices, then enable them at +/// runtime by configuring the fuzzer. +/// +/// # Example +/// +/// ``` +/// HARNESS_STOP(); +/// ``` +#define HARNESS_STOP() \ + do { \ + unsigned int value = (N_STOP_NORMAL << 0x10U) | MAGIC; \ + __cpuid_extended1(value, DEFAULT_INDEX); \ + } while (0); + +/// HARNESS_STOP_INDEX +/// +/// Signal the fuzzer to stop and reset to the beginning of the fuzzing loop +/// with a "normal" stop status, indicating no solution has occurred. The index +/// specified by `stop_index` will be used. If you need to differentiate between +/// multiple stop harnesses compiled into the same binary, you can use this +/// macro to specify different indices, then enable them at runtime by +/// configuring the fuzzer. +/// +/// # Arguments +/// +/// - `stop_index`: The index to use for this stop harness +/// +/// # Example +/// +/// ``` +/// HARNESS_STOP_INDEX(0x0001U); +/// ``` +#define HARNESS_STOP_INDEX(stop_index) \ + do { \ + unsigned int value = (N_STOP_NORMAL << 0x10U) | MAGIC; \ + __cpuid_extended1(value, stop_index); \ + } while (0); + +/// Pseudo-hypercall number to signal the fuzzer that a custom assertion has +/// occurred, and the fuzzer should stop the current fuzzing iteration and reset +/// to the beginning of the fuzzing loop with a "solution" stop status. +#define N_STOP_ASSERT (0x0005U) + +/// HARNESS_ASSERT +/// +/// Signal the fuzzer that a custom assertion has occurred, and the fuzzer +/// should stop the current fuzzing iteration and reset to the beginning of the +/// fuzzing loop with a "solution" stop status. The default index of 0 will be +/// used. If you need to differentiate between multiple assertion harnesses +/// compiled into the same binary, you can use the `HARNESS_ASSERT_INDEX` macro +/// to specify different indices, then enable them at runtime by configuring the +/// fuzzer. +/// +/// # Example +/// +/// ``` +/// HARNESS_ASSERT(); +/// ``` +#define HARNESS_ASSERT() \ + do { \ + unsigned int value = (N_STOP_ASSERT << 0x10U) | MAGIC; \ + __cpuid_extended1(value, DEFAULT_INDEX); \ + } while (0); + +/// HARNESS_ASSERT_INDEX +/// +/// Signal the fuzzer that a custom assertion has occurred, and the fuzzer +/// should stop the current fuzzing iteration and reset to the beginning of the +/// fuzzing loop with a "solution" stop status. The index specified by +/// `assert_index` will be used. If you need to differentiate between multiple +/// assertion harnesses compiled into the same binary, you can use this macro to +/// specify different indices, then enable them at runtime by configuring the +/// fuzzer. +/// +/// # Arguments +/// +/// - `assert_index`: The index to use for this assertion harness +/// +/// # Example +/// +/// ``` +/// HARNESS_ASSERT_INDEX(0x0001U); +/// ``` +#define HARNESS_ASSERT_INDEX(assert_index) \ + do { \ + unsigned int value = (N_STOP_ASSERT << 0x10U) | MAGIC; \ + __cpuid_extended1(value, assert_index); \ + } while (0); + +#endif // TSFFS_H +#elif __riscv && !__LP64__ +// Copyright (C) 2024 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 + +/// Definitions and macros for compiled-in harnessing of C and C++ target +/// software for the RISC-V (32-bit) architecture + +#ifndef TSFFS_H +#define TSFFS_H + +/// Define common with LibFuzzer and other fuzzers to allow code that is +/// fuzzing-specific to be left in the codebase. See +/// https://llvm.org/docs/LibFuzzer.html#id35 for more information +#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION +#define FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION (1) +#endif // FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION + +/// __srai +/// +/// Invoke the magic instruction defined by SIMICS for the RISC-V architecture +/// with a specific value of `n` +/// +/// # Arguments +/// +/// * `value` - The value of `n` to use in the magic instruction +#define __srai(value) \ + __asm__ __volatile__("srai zero, zero, %0" : : "I"(value) :) + +/// __srai_extended1 +/// +/// Invoke the magic instruction defined by SIMICS for the RISC-V architecture +/// with a specific value of `n` and a pseudo-argument in register `a0`. +/// +/// # Arguments +/// +/// * `value` - The value of `n` to use in the magic instruction +/// * `arg0` - The value to place in register `a0` +#define __srai_extended1(value, arg0) \ + __asm__ __volatile__("mv a0, %0; srai zero, zero, %1" \ + : \ + : "r"(arg0), "I"(value) \ + : "a0"); + +/// __srai_extended2 +/// +/// Invoke the magic instruction defined by SIMICS for the RISC-V architecture +/// with a specific value of `n` and pseudo-arguments in registers `a0` and +/// `a1`. +/// +/// # Arguments +/// +/// * `value` - The value of `n` to use in the magic instruction +/// * `arg0` - The value to place in register `a0` +/// * `arg1` - The value to place in register `a1` +#define __srai_extended2(value, arg0, arg1) \ + __asm__ __volatile__("mv a0, %0; mv a1, %1; srai zero, zero, %2" \ + : \ + : "r"(arg0), "r"(arg1), "I"(value) \ + : "a0", "a1"); + +/// __srai_extended3 +/// +/// Invoke the magic instruction defined by SIMICS for the RISC-V architecture +/// with a specific value of `n` and pseudo-arguments in registers `a0`, `a1`, +/// and `a2`. +/// +/// # Arguments +/// +/// * `value` - The value of `n` to use in the magic instruction +/// * `arg0` - The value to place in register `a0` +/// * `arg1` - The value to place in register `a1` +/// * `arg2` - The value to place in register `a2` +#define __srai_extended3(value, arg0, arg1, arg2) \ + __asm__ __volatile__("mv a0, %0; mv a1, %1; mv a2, %2; srai zero, zero, %3" \ + : \ + : "r"(arg0), "r"(arg1), "r"(arg2), "I"(value) \ + : "a0", "a1", "a2"); + +/// __srai_extended4 +/// +/// Invoke the magic instruction defined by SIMICS for the RISC-V architecture +/// with a specific value of `n` and pseudo-arguments in registers `a0`, `a1`, +/// `a2`, and `a3`. +/// +/// # Arguments +/// +/// * `value` - The value of `n` to use in the magic instruction +/// * `arg0` - The value to place in register `a0` +/// * `arg1` - The value to place in register `a1` +/// * `arg2` - The value to place in register `a2` +/// * `arg3` - The value to place in register `a3` +#define __srai_extended4(value, arg0, arg1, arg2, arg3) \ + __asm__ __volatile__( \ + "mv a0, %0; mv a1, %1; mv a2, %2; mv a3, %3; srai zero, zero, %4" \ + : \ + : "r"(arg0), "r"(arg1), "r"(arg2), "r"(arg3), "I"(value) \ + : "a0", "a1", "a2", "a3"); + +/// Magic value defined by SIMICS as the "leaf" value of a CPUID instruction +/// that is treated as a magic instruction. +#define MAGIC (0x4711U) + +/// The default index number used for magic instructions. All magic instructions +/// support multiple start and stop indices, which defaults to 0 if not +/// specified. +#define DEFAULT_INDEX (0x0000U) + +/// Pseudo-hypercall number to signal the fuzzer to use the first argument to +/// the magic instruction as the pointer to the testcase buffer and the second +/// argument as a pointer to the size of the testcase buffer. +#define N_START_BUFFER_PTR_SIZE_PTR (0x0001U) + +/// HARNESS_START +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The default "index" of 0 will be used. If you need multiple start +/// harnesses compiled into the same binary, you can use the +/// `HARNESS_START_INDEX` macro to specify different indices, then enable them +/// at runtime by configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The size of the buffer pointed to by `size_ptr` will be saved as the +/// maximum testcase size. Each fuzzing iteration, the actual size of the +/// current testcase will be written to `*size_ptr`. +/// +/// # Arguments +/// +/// - `buffer`: The pointer to the testcase buffer +/// - `size_ptr`: The pointer to the size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// size_t size; +/// HARNESS_START(buffer, &size); +/// ``` +#define HARNESS_START(buffer, size_ptr) \ + do { \ + __srai_extended3(N_START_BUFFER_PTR_SIZE_PTR, DEFAULT_INDEX, buffer, \ + size_ptr); \ + } while (0); + +/// HARNESS_START_INDEX +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The index specified by `start_index` will be used. If you need +/// multiple start harnesses compiled into the same binary, you can use this +/// macro to specify different indices, then enable them at runtime by +/// configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The size of the buffer pointed to by `size_ptr` will be saved as the +/// maximum testcase size. Each fuzzing iteration, the actual size of the +/// current testcase will be written to `*size_ptr`. +/// +/// # Arguments +/// +/// - `start_index`: The index to use for this start harness +/// - `buffer`: The pointer to the testcase buffer +/// - `size_ptr`: The pointer to the size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// size_t size; +/// HARNESS_START_INDEX(0x0001U, buffer, &size); +/// ``` +#define HARNESS_START_INDEX(start_index, buffer, size_ptr) \ + do { \ + __srai_extended3(N_START_BUFFER_PTR_SIZE_PTR, start_index, buffer, \ + size_ptr); \ + } while (0); + +/// Pseudo-hypercall number to signal the fuzzer to use the first argument to +/// the magic instruction as the pointer to the testcase buffer and the second +/// argument as the maximum size of the testcase buffer. +#define N_START_BUFFER_PTR_SIZE_VAL (0x0002U) + +/// HARNESS_START_WITH_MAXIMUM_SIZE +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The default "index" of 0 will be used. If you need multiple start +/// harnesses compiled into the same binary, you can use the +/// `HARNESS_START_WITH_MAXIMUM_SIZE_INDEX` macro to specify different indices, +/// then enable them at runtime by configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The `max_size` value will be saved as the maximum testcase size. Fuzzing +/// test cases will be truncated to this size before being written to the +/// buffer. +/// +/// # Arguments +/// +/// - `buffer`: The pointer to the testcase buffer +/// - `max_size`: The maximum size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// HARNESS_START_WITH_MAXIMUM_SIZE(buffer, 1024); +/// ``` +#define HARNESS_START_WITH_MAXIMUM_SIZE(buffer, max_size) \ + do { \ + __srai_extended3(N_START_BUFFER_PTR_SIZE_VAL, DEFAULT_INDEX, buffer, \ + max_size); \ + } while (0); + +/// HARNESS_START_WITH_MAXIMUM_SIZE_INDEX +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The index specified by `start_index` will be used. If you need +/// multiple start harnesses compiled into the same binary, you can use this +/// macro to specify different indices, then enable them at runtime by +/// configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The `max_size` value will be saved as the maximum testcase size. Fuzzing +/// test cases will be truncated to this size before being written to the +/// buffer. +/// +/// # Arguments +/// +/// - `start_index`: The index to use for this start harness +/// - `buffer`: The pointer to the testcase buffer +/// - `max_size`: The maximum size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// HARNESS_START_WITH_MAXIMUM_SIZE_INDEX(0x0001U, buffer, 1024); +/// ``` +#define HARNESS_START_WITH_MAXIMUM_SIZE_INDEX(start_index, buffer, max_size) \ + do { \ + __srai_extended3(N_START_BUFFER_PTR_SIZE_VAL, start_index, buffer, \ + max_size); \ + } while (0); + +/// Pseudo-hypercall number to signal the fuzzer to use the first argument to +/// the magic instruction as the pointer to the testcase buffer, the second +/// argument as a pointer to the size of the testcase buffer, and the third +/// argument as the maximum size of the testcase buffer. +#define N_START_BUFFER_PTR_SIZE_PTR_VAL (0x0003U) + +/// HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The default "index" of 0 will be used. If you need multiple start +/// harnesses compiled into the same binary, you can use the +/// `HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR_INDEX` macro to specify different +/// indices, then enable them at runtime by configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The address `size_ptr` will be saved. Each fuzzing iteration, the actual +/// size of the current testcase will be written to `*size_ptr`. +/// - The `max_size` value will be saved as the maximum testcase size. Fuzzing +/// test cases will be truncated to this size before being written to the +/// buffer. +/// +/// # Arguments +/// +/// - `buffer`: The pointer to the testcase buffer +/// - `size_ptr`: The pointer to the size of the testcase buffer +/// - `max_size`: The maximum size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// size_t size; +/// HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR(buffer, &size, 1024); +/// ``` +#define HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR(buffer, size_ptr, max_size) \ + do { \ + __srai_extended4(N_START_BUFFER_PTR_SIZE_PTR_VAL, DEFAULT_INDEX, buffer, \ + size_ptr, max_size); \ + } while (0); + +/// HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR_INDEX +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The index specified by `start_index` will be used. If you need +/// multiple start harnesses compiled into the same binary, you can use this +/// macro to specify different indices, then enable them at runtime by +/// configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The address `size_ptr` will be saved. Each fuzzing iteration, the actual +/// size of the current testcase will be written to `*size_ptr`. +/// - The `max_size` value will be saved as the maximum testcase size. Fuzzing +/// test cases will be truncated to this size before being written to the +/// buffer. +/// +/// # Arguments +/// +/// - `start_index`: The index to use for this start harness +/// - `buffer`: The pointer to the testcase buffer +/// - `size_ptr`: The pointer to the size of the testcase buffer +/// - `max_size`: The maximum size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// size_t size; +/// HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR_INDEX(0x0001U, buffer, &size, 1024); +/// ``` +#define HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR_INDEX(start_index, buffer, \ + size_ptr, max_size) \ + do { \ + __srai_extended4(N_START_BUFFER_PTR_SIZE_PTR_VAL, start_index, buffer, \ + size_ptr, max_size); \ + } while (0); + +/// Pseudo-hypercall number to signal the fuzzer to stop the current fuzzing +/// iteration and reset to the beginning of the fuzzing loop with a "normal" +/// stop status, indicating no solution has occurred. +#define N_STOP_NORMAL (0x0004U) + +/// HARNESS_STOP +/// +/// Signal the fuzzer to stop and reset to the beginning of the fuzzing loop +/// with a "normal" stop status, indicating no solution has occurred. The +/// default index of 0 will be used. If you need to differentiate between +/// multiple stop harnesses compiled into the same binary, you can use the +/// `HARNESS_STOP_INDEX` macro to specify different indices, then enable them at +/// runtime by configuring the fuzzer. +/// +/// # Example +/// +/// ``` +/// HARNESS_STOP(); +/// ``` +#define HARNESS_STOP() \ + do { \ + __srai_extended1(N_STOP_NORMAL, DEFAULT_INDEX); \ + } while (0); + +/// HARNESS_STOP_INDEX +/// +/// Signal the fuzzer to stop and reset to the beginning of the fuzzing loop +/// with a "normal" stop status, indicating no solution has occurred. The index +/// specified by `stop_index` will be used. If you need to differentiate between +/// multiple stop harnesses compiled into the same binary, you can use this +/// macro to specify different indices, then enable them at runtime by +/// configuring the fuzzer. +/// +/// # Arguments +/// +/// - `stop_index`: The index to use for this stop harness +/// +/// # Example +/// +/// ``` +/// HARNESS_STOP_INDEX(0x0001U); +/// ``` +#define HARNESS_STOP_INDEX(stop_index) \ + do { \ + __srai_extended1(N_STOP_NORMAL, stop_index); \ + } while (0); + +/// Pseudo-hypercall number to signal the fuzzer that a custom assertion has +/// occurred, and the fuzzer should stop the current fuzzing iteration and reset +/// to the beginning of the fuzzing loop with a "solution" stop status. +#define N_STOP_ASSERT (0x0005U) + +/// HARNESS_ASSERT +/// +/// Signal the fuzzer that a custom assertion has occurred, and the fuzzer +/// should stop the current fuzzing iteration and reset to the beginning of the +/// fuzzing loop with a "solution" stop status. The default index of 0 will be +/// used. If you need to differentiate between multiple assertion harnesses +/// compiled into the same binary, you can use the `HARNESS_ASSERT_INDEX` macro +/// to specify different indices, then enable them at runtime by configuring the +/// fuzzer. +/// +/// # Example +/// +/// ``` +/// HARNESS_ASSERT(); +/// ``` +#define HARNESS_ASSERT() \ + do { \ + __srai_extended1(N_STOP_ASSERT, DEFAULT_INDEX); \ + } while (0); + +/// HARNESS_ASSERT_INDEX +/// +/// Signal the fuzzer that a custom assertion has occurred, and the fuzzer +/// should stop the current fuzzing iteration and reset to the beginning of the +/// fuzzing loop with a "solution" stop status. The index specified by +/// `assert_index` will be used. If you need to differentiate between multiple +/// assertion harnesses compiled into the same binary, you can use this macro to +/// specify different indices, then enable them at runtime by configuring the +/// fuzzer. +/// +/// # Arguments +/// +/// - `assert_index`: The index to use for this assertion harness +/// +/// # Example +/// +/// ``` +/// HARNESS_ASSERT_INDEX(0x0001U); +/// ``` +#define HARNESS_ASSERT_INDEX(assert_index) \ + do { \ + __srai_extended1(N_STOP_ASSERT, assert_index); \ + } while (0); + +#endif // TSFFS_H +#elif __riscv && __LP64__ +// Copyright (C) 2024 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 + +/// Definitions and macros for compiled-in harnessing of C and C++ target +/// software for the RISC-V (32-bit) architecture + +#ifndef TSFFS_H +#define TSFFS_H + +/// Define common with LibFuzzer and other fuzzers to allow code that is +/// fuzzing-specific to be left in the codebase. See +/// https://llvm.org/docs/LibFuzzer.html#id35 for more information +#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION +#define FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION (1) +#endif // FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION + +/// __srai +/// +/// Invoke the magic instruction defined by SIMICS for the RISC-V architecture +/// with a specific value of `n` +/// +/// # Arguments +/// +/// * `value` - The value of `n` to use in the magic instruction +#define __srai(value) \ + __asm__ __volatile__("srai zero, zero, %0" : : "I"(value) :) + +/// __srai_extended1 +/// +/// Invoke the magic instruction defined by SIMICS for the RISC-V architecture +/// with a specific value of `n` and a pseudo-argument in register `a0`. +/// +/// # Arguments +/// +/// * `value` - The value of `n` to use in the magic instruction +/// * `arg0` - The value to place in register `a0` +#define __srai_extended1(value, arg0) \ + __asm__ __volatile__("mv a0, %0; srai zero, zero, %1" \ + : \ + : "r"(arg0), "I"(value) \ + : "a0"); + +/// __srai_extended2 +/// +/// Invoke the magic instruction defined by SIMICS for the RISC-V architecture +/// with a specific value of `n` and pseudo-arguments in registers `a0` and +/// `a1`. +/// +/// # Arguments +/// +/// * `value` - The value of `n` to use in the magic instruction +/// * `arg0` - The value to place in register `a0` +/// * `arg1` - The value to place in register `a1` +#define __srai_extended2(value, arg0, arg1) \ + __asm__ __volatile__("mv a0, %0; mv a1, %1; srai zero, zero, %2" \ + : \ + : "r"(arg0), "r"(arg1), "I"(value) \ + : "a0", "a1"); + +/// __srai_extended3 +/// +/// Invoke the magic instruction defined by SIMICS for the RISC-V architecture +/// with a specific value of `n` and pseudo-arguments in registers `a0`, `a1`, +/// and `a2`. +/// +/// # Arguments +/// +/// * `value` - The value of `n` to use in the magic instruction +/// * `arg0` - The value to place in register `a0` +/// * `arg1` - The value to place in register `a1` +/// * `arg2` - The value to place in register `a2` +#define __srai_extended3(value, arg0, arg1, arg2) \ + __asm__ __volatile__("mv a0, %0; mv a1, %1; mv a2, %2; srai zero, zero, %3" \ + : \ + : "r"(arg0), "r"(arg1), "r"(arg2), "I"(value) \ + : "a0", "a1", "a2"); + +/// __srai_extended4 +/// +/// Invoke the magic instruction defined by SIMICS for the RISC-V architecture +/// with a specific value of `n` and pseudo-arguments in registers `a0`, `a1`, +/// `a2`, and `a3`. +/// +/// # Arguments +/// +/// * `value` - The value of `n` to use in the magic instruction +/// * `arg0` - The value to place in register `a0` +/// * `arg1` - The value to place in register `a1` +/// * `arg2` - The value to place in register `a2` +/// * `arg3` - The value to place in register `a3` +#define __srai_extended4(value, arg0, arg1, arg2, arg3) \ + __asm__ __volatile__( \ + "mv a0, %0; mv a1, %1; mv a2, %2; mv a3, %3; srai zero, zero, %4" \ + : \ + : "r"(arg0), "r"(arg1), "r"(arg2), "r"(arg3), "I"(value) \ + : "a0", "a1", "a2", "a3"); + +/// Magic value defined by SIMICS as the "leaf" value of a CPUID instruction +/// that is treated as a magic instruction. +#define MAGIC (0x4711U) + +/// The default index number used for magic instructions. All magic instructions +/// support multiple start and stop indices, which defaults to 0 if not +/// specified. +#define DEFAULT_INDEX (0x0000U) + +/// Pseudo-hypercall number to signal the fuzzer to use the first argument to +/// the magic instruction as the pointer to the testcase buffer and the second +/// argument as a pointer to the size of the testcase buffer. +#define N_START_BUFFER_PTR_SIZE_PTR (0x0001U) + +/// HARNESS_START +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The default "index" of 0 will be used. If you need multiple start +/// harnesses compiled into the same binary, you can use the +/// `HARNESS_START_INDEX` macro to specify different indices, then enable them +/// at runtime by configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The size of the buffer pointed to by `size_ptr` will be saved as the +/// maximum testcase size. Each fuzzing iteration, the actual size of the +/// current testcase will be written to `*size_ptr`. +/// +/// # Arguments +/// +/// - `buffer`: The pointer to the testcase buffer +/// - `size_ptr`: The pointer to the size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// size_t size; +/// HARNESS_START(buffer, &size); +/// ``` +#define HARNESS_START(buffer, size_ptr) \ + do { \ + __srai_extended3(N_START_BUFFER_PTR_SIZE_PTR, DEFAULT_INDEX, buffer, size_ptr); \ + } while (0); + +/// HARNESS_START_INDEX +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The index specified by `start_index` will be used. If you need +/// multiple start harnesses compiled into the same binary, you can use this +/// macro to specify different indices, then enable them at runtime by +/// configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The size of the buffer pointed to by `size_ptr` will be saved as the +/// maximum testcase size. Each fuzzing iteration, the actual size of the +/// current testcase will be written to `*size_ptr`. +/// +/// # Arguments +/// +/// - `start_index`: The index to use for this start harness +/// - `buffer`: The pointer to the testcase buffer +/// - `size_ptr`: The pointer to the size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// size_t size; +/// HARNESS_START_INDEX(0x0001U, buffer, &size); +/// ``` +#define HARNESS_START_INDEX(start_index, buffer, size_ptr) \ + do { \ + __srai_extended3(N_START_BUFFER_PTR_SIZE_PTR, start_index, buffer, size_ptr); \ + } while (0); + +/// Pseudo-hypercall number to signal the fuzzer to use the first argument to +/// the magic instruction as the pointer to the testcase buffer and the second +/// argument as the maximum size of the testcase buffer. +#define N_START_BUFFER_PTR_SIZE_VAL (0x0002U) + +/// HARNESS_START_WITH_MAXIMUM_SIZE +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The default "index" of 0 will be used. If you need multiple start +/// harnesses compiled into the same binary, you can use the +/// `HARNESS_START_WITH_MAXIMUM_SIZE_INDEX` macro to specify different indices, +/// then enable them at runtime by configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The `max_size` value will be saved as the maximum testcase size. Fuzzing +/// test cases will be truncated to this size before being written to the +/// buffer. +/// +/// # Arguments +/// +/// - `buffer`: The pointer to the testcase buffer +/// - `max_size`: The maximum size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// HARNESS_START_WITH_MAXIMUM_SIZE(buffer, 1024); +/// ``` +#define HARNESS_START_WITH_MAXIMUM_SIZE(buffer, max_size) \ + do { \ + __srai_extended3(N_START_BUFFER_PTR_SIZE_VAL, DEFAULT_INDEX, buffer, max_size); \ + } while (0); + +/// HARNESS_START_WITH_MAXIMUM_SIZE_INDEX +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The index specified by `start_index` will be used. If you need +/// multiple start harnesses compiled into the same binary, you can use this +/// macro to specify different indices, then enable them at runtime by +/// configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The `max_size` value will be saved as the maximum testcase size. Fuzzing +/// test cases will be truncated to this size before being written to the +/// buffer. +/// +/// # Arguments +/// +/// - `start_index`: The index to use for this start harness +/// - `buffer`: The pointer to the testcase buffer +/// - `max_size`: The maximum size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// HARNESS_START_WITH_MAXIMUM_SIZE_INDEX(0x0001U, buffer, 1024); +/// ``` +#define HARNESS_START_WITH_MAXIMUM_SIZE_INDEX(start_index, buffer, max_size) \ + do { \ + __srai_extended3(N_START_BUFFER_PTR_SIZE_VAL, start_index, buffer, max_size); \ + } while (0); + +/// Pseudo-hypercall number to signal the fuzzer to use the first argument to +/// the magic instruction as the pointer to the testcase buffer, the second +/// argument as a pointer to the size of the testcase buffer, and the third +/// argument as the maximum size of the testcase buffer. +#define N_START_BUFFER_PTR_SIZE_PTR_VAL (0x0003U) + +/// HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The default "index" of 0 will be used. If you need multiple start +/// harnesses compiled into the same binary, you can use the +/// `HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR_INDEX` macro to specify different +/// indices, then enable them at runtime by configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The address `size_ptr` will be saved. Each fuzzing iteration, the actual +/// size of the current testcase will be written to `*size_ptr`. +/// - The `max_size` value will be saved as the maximum testcase size. Fuzzing +/// test cases will be truncated to this size before being written to the +/// buffer. +/// +/// # Arguments +/// +/// - `buffer`: The pointer to the testcase buffer +/// - `size_ptr`: The pointer to the size of the testcase buffer +/// - `max_size`: The maximum size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// size_t size; +/// HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR(buffer, &size, 1024); +/// ``` +#define HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR(buffer, size_ptr, max_size) \ + do { \ + __srai_extended4(N_START_BUFFER_PTR_SIZE_PTR_VAL, DEFAULT_INDEX, buffer, size_ptr, max_size); \ + } while (0); + +/// HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR_INDEX +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The index specified by `start_index` will be used. If you need +/// multiple start harnesses compiled into the same binary, you can use this +/// macro to specify different indices, then enable them at runtime by +/// configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The address `size_ptr` will be saved. Each fuzzing iteration, the actual +/// size of the current testcase will be written to `*size_ptr`. +/// - The `max_size` value will be saved as the maximum testcase size. Fuzzing +/// test cases will be truncated to this size before being written to the +/// buffer. +/// +/// # Arguments +/// +/// - `start_index`: The index to use for this start harness +/// - `buffer`: The pointer to the testcase buffer +/// - `size_ptr`: The pointer to the size of the testcase buffer +/// - `max_size`: The maximum size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// size_t size; +/// HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR_INDEX(0x0001U, buffer, &size, 1024); +/// ``` +#define HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR_INDEX(start_index, buffer, \ + size_ptr, max_size) \ + do { \ + __srai_extended4(N_START_BUFFER_PTR_SIZE_PTR_VAL, start_index, buffer, size_ptr, max_size); \ + } while (0); + +/// Pseudo-hypercall number to signal the fuzzer to stop the current fuzzing +/// iteration and reset to the beginning of the fuzzing loop with a "normal" +/// stop status, indicating no solution has occurred. +#define N_STOP_NORMAL (0x0004U) + +/// HARNESS_STOP +/// +/// Signal the fuzzer to stop and reset to the beginning of the fuzzing loop +/// with a "normal" stop status, indicating no solution has occurred. The +/// default index of 0 will be used. If you need to differentiate between +/// multiple stop harnesses compiled into the same binary, you can use the +/// `HARNESS_STOP_INDEX` macro to specify different indices, then enable them at +/// runtime by configuring the fuzzer. +/// +/// # Example +/// +/// ``` +/// HARNESS_STOP(); +/// ``` +#define HARNESS_STOP() \ + do { \ + __srai_extended1(N_STOP_NORMAL, DEFAULT_INDEX); \ + } while (0); + +/// HARNESS_STOP_INDEX +/// +/// Signal the fuzzer to stop and reset to the beginning of the fuzzing loop +/// with a "normal" stop status, indicating no solution has occurred. The index +/// specified by `stop_index` will be used. If you need to differentiate between +/// multiple stop harnesses compiled into the same binary, you can use this +/// macro to specify different indices, then enable them at runtime by +/// configuring the fuzzer. +/// +/// # Arguments +/// +/// - `stop_index`: The index to use for this stop harness +/// +/// # Example +/// +/// ``` +/// HARNESS_STOP_INDEX(0x0001U); +/// ``` +#define HARNESS_STOP_INDEX(stop_index) \ + do { \ + __srai_extended1(N_STOP_NORMAL, stop_index); \ + } while (0); + +/// Pseudo-hypercall number to signal the fuzzer that a custom assertion has +/// occurred, and the fuzzer should stop the current fuzzing iteration and reset +/// to the beginning of the fuzzing loop with a "solution" stop status. +#define N_STOP_ASSERT (0x0005U) + +/// HARNESS_ASSERT +/// +/// Signal the fuzzer that a custom assertion has occurred, and the fuzzer +/// should stop the current fuzzing iteration and reset to the beginning of the +/// fuzzing loop with a "solution" stop status. The default index of 0 will be +/// used. If you need to differentiate between multiple assertion harnesses +/// compiled into the same binary, you can use the `HARNESS_ASSERT_INDEX` macro +/// to specify different indices, then enable them at runtime by configuring the +/// fuzzer. +/// +/// # Example +/// +/// ``` +/// HARNESS_ASSERT(); +/// ``` +#define HARNESS_ASSERT() \ + do { \ + __srai_extended1(N_STOP_ASSERT, DEFAULT_INDEX); \ + } while (0); + +/// HARNESS_ASSERT_INDEX +/// +/// Signal the fuzzer that a custom assertion has occurred, and the fuzzer +/// should stop the current fuzzing iteration and reset to the beginning of the +/// fuzzing loop with a "solution" stop status. The index specified by +/// `assert_index` will be used. If you need to differentiate between multiple +/// assertion harnesses compiled into the same binary, you can use this macro to +/// specify different indices, then enable them at runtime by configuring the +/// fuzzer. +/// +/// # Arguments +/// +/// - `assert_index`: The index to use for this assertion harness +/// +/// # Example +/// +/// ``` +/// HARNESS_ASSERT_INDEX(0x0001U); +/// ``` +#define HARNESS_ASSERT_INDEX(assert_index) \ + do { \ + __srai_extended1(N_STOP_ASSERT, assert_index); \ + } while (0); + +#endif // TSFFS_H +#elif __aarch64__ +// Copyright (C) 2024 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 + +/// Definitions and macros for compiled-in harnessing of C and C++ target +/// software for the RISC-V (32-bit) architecture + +#ifndef TSFFS_H +#define TSFFS_H + +/// Define common with LibFuzzer and other fuzzers to allow code that is +/// fuzzing-specific to be left in the codebase. See +/// https://llvm.org/docs/LibFuzzer.html#id35 for more information +#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION +#define FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION (1) +#endif // FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION + +#define __stringify(x) #x +#define __tostring(x) __stringify(x) + +/// __orr +/// +/// Invoke the magic instruction defined by SIMICS for the RISC-V architecture +/// with a specific value of `n` +/// +/// # Arguments +/// +/// * `value` - The value of `n` to use in the magic instruction +#define __orr(value) \ + __asm__ __volatile__("orr x" __tostring(value) ", x" __tostring( \ + value) ", x" __tostring(value)); + +/// __orr_extended1 +/// +/// Invoke the magic instruction defined by SIMICS for the RISC-V architecture +/// with a specific value of `n` and a pseudo-argument in register `r10`. +/// +/// # Arguments +/// +/// * `value` - The value of `n` to use in the magic instruction +/// * `arg0` - The value to place in register `r10` +#define __orr_extended1(value, arg0) \ + __asm__ __volatile__( \ + "mov x28, %0; orr x" __tostring(value) ", x" __tostring( \ + value) ", x" __tostring(value) \ + : \ + : "g"(arg0)); + +/// __orr_extended2 +/// +/// Invoke the magic instruction defined by SIMICS for the RISC-V architecture +/// with a specific value of `n` and two pseudo-arguments in registers `r10` and +/// `r9`. +/// +/// # Arguments +/// +/// * `value` - The value of `n` to use in the magic instruction +/// * `arg0` - The value to place in register `r10` +/// * `arg1` - The value to place in register `r9` +#define __orr_extended2(value, arg0, arg1) \ + __asm__ __volatile__( \ + "mov x28, %0; mov x27, %1; orr x" __tostring(value) ", x" __tostring( \ + value) ", x" __tostring(value) \ + : \ + : "r"(arg0), "r"(arg1)); + +/// __orr_extended3 +/// +/// Invoke the magic instruction defined by SIMICS for the RISC-V architecture +/// with a specific value of `n` and three pseudo-arguments in registers `r10`, +/// `r9`, and `r8`. +/// +/// # Arguments +/// +/// * `value` - The value of `n` to use in the magic instruction +/// * `arg0` - The value to place in register `r10` +/// * `arg1` - The value to place in register `r9` +/// * `arg2` - The value to place in register `r8` +#define __orr_extended3(value, arg0, arg1, arg2) \ + __asm__ __volatile__( \ + "mov x28, %0; mov x27, %1; mov x26, %2; orr x" __tostring( \ + value) ", x" __tostring(value) ", x" __tostring(value) \ + : \ + : "r"(arg0), "r"(arg1), "r"(arg2)); + +/// __orr_extended4 +/// +/// Invoke the magic instruction defined by SIMICS for the RISC-V architecture +/// with a specific value of `n` and four pseudo-arguments in registers `r10`, +/// `r9`, `r8`, and `r7`. +/// +/// # Arguments +/// +/// * `value` - The value of `n` to use in the magic instruction +/// * `arg0` - The value to place in register `r10` +/// * `arg1` - The value to place in register `r9` +/// * `arg2` - The value to place in register `r8` +/// * `arg3` - The value to place in register `r7` +#define __orr_extended4(value, arg0, arg1, arg2, arg3) \ + __asm__ __volatile__( \ + "mov x28, %0; mov x27, %1; mov x26, %2; mov x25, %3; " \ + "orr x" __tostring(value) ", x" __tostring(value) ", x" __tostring( \ + value) \ + : \ + : "r"(arg0), "r"(arg1), "r"(arg2), "r"(arg3)); + +/// The default index number used for magic instructions. All magic instructions +/// support multiple start and stop indices, which defaults to 0 if not +/// specified. +#define DEFAULT_INDEX (0x0000U) + +/// Pseudo-hypercall number to signal the fuzzer to use the first argument to +/// the magic instruction as the pointer to the testcase buffer and the second +/// argument as a pointer to the size of the testcase buffer. +#define N_START_BUFFER_PTR_SIZE_PTR 1 + +/// HARNESS_START +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The default "index" of 0 will be used. If you need multiple start +/// harnesses compiled into the same binary, you can use the +/// `HARNESS_START_INDEX` macro to specify different indices, then enable them +/// at runtime by configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The size of the buffer pointed to by `size_ptr` will be saved as the +/// maximum testcase size. Each fuzzing iteration, the actual size of the +/// current testcase will be written to `*size_ptr`. +/// +/// # Arguments +/// +/// - `buffer`: The pointer to the testcase buffer +/// - `size_ptr`: The pointer to the size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// size_t size; +/// HARNESS_START(buffer, &size); +/// ``` +#define HARNESS_START(buffer, size_ptr) \ + do { \ + __orr_extended3(N_START_BUFFER_PTR_SIZE_PTR, DEFAULT_INDEX, buffer, \ + size_ptr); \ + } while (0); + +/// HARNESS_START_INDEX +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The index specified by `start_index` will be used. If you need +/// multiple start harnesses compiled into the same binary, you can use this +/// macro to specify different indices, then enable them at runtime by +/// configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The size of the buffer pointed to by `size_ptr` will be saved as the +/// maximum testcase size. Each fuzzing iteration, the actual size of the +/// current testcase will be written to `*size_ptr`. +/// +/// # Arguments +/// +/// - `start_index`: The index to use for this start harness +/// - `buffer`: The pointer to the testcase buffer +/// - `size_ptr`: The pointer to the size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// size_t size; +/// HARNESS_START_INDEX(0x0001U, buffer, &size); +/// ``` +#define HARNESS_START_INDEX(start_index, buffer, size_ptr) \ + do { \ + __orr_extended3(N_START_BUFFER_PTR_SIZE_PTR, start_index, buffer, \ + size_ptr); \ + } while (0); + +/// Pseudo-hypercall number to signal the fuzzer to use the first argument to +/// the magic instruction as the pointer to the testcase buffer and the second +/// argument as the maximum size of the testcase buffer. +#define N_START_BUFFER_PTR_SIZE_VAL 2 + +/// HARNESS_START_WITH_MAXIMUM_SIZE +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The default "index" of 0 will be used. If you need multiple start +/// harnesses compiled into the same binary, you can use the +/// `HARNESS_START_WITH_MAXIMUM_SIZE_INDEX` macro to specify different indices, +/// then enable them at runtime by configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The `max_size` value will be saved as the maximum testcase size. Fuzzing +/// test cases will be truncated to this size before being written to the +/// buffer. +/// +/// # Arguments +/// +/// - `buffer`: The pointer to the testcase buffer +/// - `max_size`: The maximum size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// HARNESS_START_WITH_MAXIMUM_SIZE(buffer, 1024); +/// ``` +#define HARNESS_START_WITH_MAXIMUM_SIZE(buffer, max_size) \ + do { \ + __orr_extended3(N_START_BUFFER_PTR_SIZE_VAL, DEFAULT_INDEX, buffer, \ + max_size); \ + } while (0); + +/// HARNESS_START_WITH_MAXIMUM_SIZE_INDEX +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The index specified by `start_index` will be used. If you need +/// multiple start harnesses compiled into the same binary, you can use this +/// macro to specify different indices, then enable them at runtime by +/// configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The `max_size` value will be saved as the maximum testcase size. Fuzzing +/// test cases will be truncated to this size before being written to the +/// buffer. +/// +/// # Arguments +/// +/// - `start_index`: The index to use for this start harness +/// - `buffer`: The pointer to the testcase buffer +/// - `max_size`: The maximum size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// HARNESS_START_WITH_MAXIMUM_SIZE_INDEX(0x0001U, buffer, 1024); +/// ``` +#define HARNESS_START_WITH_MAXIMUM_SIZE_INDEX(start_index, buffer, max_size) \ + do { \ + __orr_extended3(N_START_BUFFER_PTR_SIZE_VAL, start_index, buffer, \ + max_size); \ + } while (0); + +/// Pseudo-hypercall number to signal the fuzzer to use the first argument to +/// the magic instruction as the pointer to the testcase buffer, the second +/// argument as a pointer to the size of the testcase buffer, and the third +/// argument as the maximum size of the testcase buffer. +#define N_START_BUFFER_PTR_SIZE_PTR_VAL 3 + +/// HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The default "index" of 0 will be used. If you need multiple start +/// harnesses compiled into the same binary, you can use the +/// `HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR_INDEX` macro to specify different +/// indices, then enable them at runtime by configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The address `size_ptr` will be saved. Each fuzzing iteration, the actual +/// size of the current testcase will be written to `*size_ptr`. +/// - The `max_size` value will be saved as the maximum testcase size. Fuzzing +/// test cases will be truncated to this size before being written to the +/// buffer. +/// +/// # Arguments +/// +/// - `buffer`: The pointer to the testcase buffer +/// - `size_ptr`: The pointer to the size of the testcase buffer +/// - `max_size`: The maximum size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// size_t size; +/// HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR(buffer, &size, 1024); +/// ``` +#define HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR(buffer, size_ptr, max_size) \ + do { \ + __orr_extended4(N_START_BUFFER_PTR_SIZE_PTR_VAL, DEFAULT_INDEX, buffer, \ + size_ptr, max_size); \ + } while (0); + +/// HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR_INDEX +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The index specified by `start_index` will be used. If you need +/// multiple start harnesses compiled into the same binary, you can use this +/// macro to specify different indices, then enable them at runtime by +/// configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The address `size_ptr` will be saved. Each fuzzing iteration, the actual +/// size of the current testcase will be written to `*size_ptr`. +/// - The `max_size` value will be saved as the maximum testcase size. Fuzzing +/// test cases will be truncated to this size before being written to the +/// buffer. +/// +/// # Arguments +/// +/// - `start_index`: The index to use for this start harness +/// - `buffer`: The pointer to the testcase buffer +/// - `size_ptr`: The pointer to the size of the testcase buffer +/// - `max_size`: The maximum size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// size_t size; +/// HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR_INDEX(0x0001U, buffer, &size, 1024); +/// ``` +#define HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR_INDEX(start_index, buffer, \ + size_ptr, max_size) \ + do { \ + __orr_extended4(N_START_BUFFER_PTR_SIZE_PTR_VAL, start_index, buffer, \ + size_ptr, max_size); \ + } while (0); + +/// Pseudo-hypercall number to signal the fuzzer to stop the current fuzzing +/// iteration and reset to the beginning of the fuzzing loop with a "normal" +/// stop status, indicating no solution has occurred. +#define N_STOP_NORMAL 4 + +/// HARNESS_STOP +/// +/// Signal the fuzzer to stop and reset to the beginning of the fuzzing loop +/// with a "normal" stop status, indicating no solution has occurred. The +/// default index of 0 will be used. If you need to differentiate between +/// multiple stop harnesses compiled into the same binary, you can use the +/// `HARNESS_STOP_INDEX` macro to specify different indices, then enable them at +/// runtime by configuring the fuzzer. +/// +/// # Example +/// +/// ``` +/// HARNESS_STOP(); +/// ``` +#define HARNESS_STOP() \ + do { \ + __orr_extended1(N_STOP_NORMAL, DEFAULT_INDEX); \ + } while (0); + +/// HARNESS_STOP_INDEX +/// +/// Signal the fuzzer to stop and reset to the beginning of the fuzzing loop +/// with a "normal" stop status, indicating no solution has occurred. The index +/// specified by `stop_index` will be used. If you need to differentiate between +/// multiple stop harnesses compiled into the same binary, you can use this +/// macro to specify different indices, then enable them at runtime by +/// configuring the fuzzer. +/// +/// # Arguments +/// +/// - `stop_index`: The index to use for this stop harness +/// +/// # Example +/// +/// ``` +/// HARNESS_STOP_INDEX(0x0001U); +/// ``` +#define HARNESS_STOP_INDEX(stop_index) \ + do { \ + __orr_extended1(N_STOP_NORMAL, stop_index); \ + } while (0); + +/// Pseudo-hypercall number to signal the fuzzer that a custom assertion has +/// occurred, and the fuzzer should stop the current fuzzing iteration and reset +/// to the beginning of the fuzzing loop with a "solution" stop status. +#define N_STOP_ASSERT 5 + +/// HARNESS_ASSERT +/// +/// Signal the fuzzer that a custom assertion has occurred, and the fuzzer +/// should stop the current fuzzing iteration and reset to the beginning of the +/// fuzzing loop with a "solution" stop status. The default index of 0 will be +/// used. If you need to differentiate between multiple assertion harnesses +/// compiled into the same binary, you can use the `HARNESS_ASSERT_INDEX` macro +/// to specify different indices, then enable them at runtime by configuring the +/// fuzzer. +/// +/// # Example +/// +/// ``` +/// HARNESS_ASSERT(); +/// ``` +#define HARNESS_ASSERT() \ + do { \ + __orr_extended1(N_STOP_ASSERT, DEFAULT_INDEX); \ + } while (0); + +/// HARNESS_ASSERT_INDEX +/// +/// Signal the fuzzer that a custom assertion has occurred, and the fuzzer +/// should stop the current fuzzing iteration and reset to the beginning of the +/// fuzzing loop with a "solution" stop status. The index specified by +/// `assert_index` will be used. If you need to differentiate between multiple +/// assertion harnesses compiled into the same binary, you can use this macro to +/// specify different indices, then enable them at runtime by configuring the +/// fuzzer. +/// +/// # Arguments +/// +/// - `assert_index`: The index to use for this assertion harness +/// +/// # Example +/// +/// ``` +/// HARNESS_ASSERT_INDEX(0x0001U); +/// ``` +#define HARNESS_ASSERT_INDEX(assert_index) \ + do { \ + __orr_extended1(N_STOP_ASSERT, assert_index); \ + } while (0); + +#endif // TSFFS_H +#elif __arm__ +// Copyright (C) 2024 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 + +/// Definitions and macros for compiled-in harnessing of C and C++ target +/// software for the RISC-V (32-bit) architecture + +#ifndef TSFFS_H +#define TSFFS_H + +/// Define common with LibFuzzer and other fuzzers to allow code that is +/// fuzzing-specific to be left in the codebase. See +/// https://llvm.org/docs/LibFuzzer.html#id35 for more information +#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION +#define FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION (1) +#endif // FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION + +#define __stringify(x) #x +#define __tostring(x) __stringify(x) + +/// __orr +/// +/// Invoke the magic instruction defined by SIMICS for the RISC-V architecture +/// with a specific value of `n` +/// +/// # Arguments +/// +/// * `value` - The value of `n` to use in the magic instruction +#define __orr(value) \ + __asm__ __volatile__("orr r" __tostring(value) ", r" __tostring( \ + value) ", r" __tostring(value)); + +/// __orr_extended1 +/// +/// Invoke the magic instruction defined by SIMICS for the RISC-V architecture +/// with a specific value of `n` and a pseudo-argument in register `r10`. +/// +/// # Arguments +/// +/// * `value` - The value of `n` to use in the magic instruction +/// * `arg0` - The value to place in register `r10` +#define __orr_extended1(value, arg0) \ + __asm__ __volatile__( \ + "mov r10, %0; orr r" __tostring(value) ", r" __tostring( \ + value) ", r" __tostring(value) \ + : \ + : "r"(arg0)); + +/// __orr_extended2 +/// +/// Invoke the magic instruction defined by SIMICS for the RISC-V architecture +/// with a specific value of `n` and two pseudo-arguments in registers `r10` and +/// `r9`. +/// +/// # Arguments +/// +/// * `value` - The value of `n` to use in the magic instruction +/// * `arg0` - The value to place in register `r10` +/// * `arg1` - The value to place in register `r9` +#define __orr_extended2(value, arg0, arg1) \ + __asm__ __volatile__( \ + "mov r10, %0; mov r9, %1; orr r" __tostring(value) ", r" __tostring( \ + value) ", r" __tostring(value) \ + : \ + : "r"(arg0), "r"(arg1)); + +/// __orr_extended3 +/// +/// Invoke the magic instruction defined by SIMICS for the RISC-V architecture +/// with a specific value of `n` and three pseudo-arguments in registers `r10`, +/// `r9`, and `r8`. +/// +/// # Arguments +/// +/// * `value` - The value of `n` to use in the magic instruction +/// * `arg0` - The value to place in register `r10` +/// * `arg1` - The value to place in register `r9` +/// * `arg2` - The value to place in register `r8` +#define __orr_extended3(value, arg0, arg1, arg2) \ + __asm__ __volatile__( \ + "mov r10, %0; mov r9, %1; mov r8, %2; orr r" __tostring( \ + value) ", r" __tostring(value) ", r" __tostring(value) \ + : \ + : "r"(arg0), "r"(arg1), "r"(arg2)); + +/// __orr_extended4 +/// +/// Invoke the magic instruction defined by SIMICS for the RISC-V architecture +/// with a specific value of `n` and four pseudo-arguments in registers `r10`, +/// `r9`, `r8`, and `r7`. +/// +/// # Arguments +/// +/// * `value` - The value of `n` to use in the magic instruction +/// * `arg0` - The value to place in register `r10` +/// * `arg1` - The value to place in register `r9` +/// * `arg2` - The value to place in register `r8` +/// * `arg3` - The value to place in register `r7` +#define __orr_extended4(value, arg0, arg1, arg2, arg3) \ + __asm__ __volatile__( \ + "mov r10, %0; mov r9, %1; mov r8, %2; mov r7, %3; " \ + "orr r" __tostring(value) ", r" __tostring(value) ", r" __tostring( \ + value) \ + : \ + : "r"(arg0), "r"(arg1), "r"(arg2), "r"(arg3)); + +/// Magic value defined by SIMICS as the "leaf" value of a CPUID instruction +/// that is treated as a magic instruction. +#define MAGIC (0x4711U) + +/// The default index number used for magic instructions. All magic instructions +/// support multiple start and stop indices, which defaults to 0 if not +/// specified. +#define DEFAULT_INDEX (0x0000U) + +/// Pseudo-hypercall number to signal the fuzzer to use the first argument to +/// the magic instruction as the pointer to the testcase buffer and the second +/// argument as a pointer to the size of the testcase buffer. +#define N_START_BUFFER_PTR_SIZE_PTR 1 + +/// HARNESS_START +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The default "index" of 0 will be used. If you need multiple start +/// harnesses compiled into the same binary, you can use the +/// `HARNESS_START_INDEX` macro to specify different indices, then enable them +/// at runtime by configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The size of the buffer pointed to by `size_ptr` will be saved as the +/// maximum testcase size. Each fuzzing iteration, the actual size of the +/// current testcase will be written to `*size_ptr`. +/// +/// # Arguments +/// +/// - `buffer`: The pointer to the testcase buffer +/// - `size_ptr`: The pointer to the size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// size_t size; +/// HARNESS_START(buffer, &size); +/// ``` +#define HARNESS_START(buffer, size_ptr) \ + do { \ + __orr_extended2(N_START_BUFFER_PTR_SIZE_PTR, buffer, size_ptr); \ + } while (0); + +/// Pseudo-hypercall number to signal the fuzzer to use the first argument to +/// the magic instruction as the pointer to the testcase buffer and the second +/// argument as the maximum size of the testcase buffer. +#define N_START_BUFFER_PTR_SIZE_VAL 2 + +/// HARNESS_START_WITH_MAXIMUM_SIZE +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The default "index" of 0 will be used. If you need multiple start +/// harnesses compiled into the same binary, you can use the +/// `HARNESS_START_WITH_MAXIMUM_SIZE_INDEX` macro to specify different indices, +/// then enable them at runtime by configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The `max_size` value will be saved as the maximum testcase size. Fuzzing +/// test cases will be truncated to this size before being written to the +/// buffer. +/// +/// # Arguments +/// +/// - `buffer`: The pointer to the testcase buffer +/// - `max_size`: The maximum size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// HARNESS_START_WITH_MAXIMUM_SIZE(buffer, 1024); +/// ``` +#define HARNESS_START_WITH_MAXIMUM_SIZE(buffer, max_size) \ + do { \ + __orr_extended2(N_START_BUFFER_PTR_SIZE_VAL, buffer, max_size); \ + } while (0); + +/// Pseudo-hypercall number to signal the fuzzer to use the first argument to +/// the magic instruction as the pointer to the testcase buffer, the second +/// argument as a pointer to the size of the testcase buffer, and the third +/// argument as the maximum size of the testcase buffer. +#define N_START_BUFFER_PTR_SIZE_PTR_VAL 3 + +/// HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The default "index" of 0 will be used. If you need multiple start +/// harnesses compiled into the same binary, you can use the +/// `HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR_INDEX` macro to specify different +/// indices, then enable them at runtime by configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The address `size_ptr` will be saved. Each fuzzing iteration, the actual +/// size of the current testcase will be written to `*size_ptr`. +/// - The `max_size` value will be saved as the maximum testcase size. Fuzzing +/// test cases will be truncated to this size before being written to the +/// buffer. +/// +/// # Arguments +/// +/// - `buffer`: The pointer to the testcase buffer +/// - `size_ptr`: The pointer to the size of the testcase buffer +/// - `max_size`: The maximum size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// size_t size; +/// HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR(buffer, &size, 1024); +/// ``` +#define HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR(buffer, size_ptr, max_size) \ + do { \ + __orr_extended3(N_START_BUFFER_PTR_SIZE_PTR_VAL, buffer, size_ptr, \ + max_size); \ + } while (0); + +/// Pseudo-hypercall number to signal the fuzzer to stop the current fuzzing +/// iteration and reset to the beginning of the fuzzing loop with a "normal" +/// stop status, indicating no solution has occurred. +#define N_STOP_NORMAL 4 + +/// HARNESS_STOP +/// +/// Signal the fuzzer to stop and reset to the beginning of the fuzzing loop +/// with a "normal" stop status, indicating no solution has occurred. The +/// default index of 0 will be used. If you need to differentiate between +/// multiple stop harnesses compiled into the same binary, you can use the +/// `HARNESS_STOP_INDEX` macro to specify different indices, then enable them at +/// runtime by configuring the fuzzer. +/// +/// # Example +/// +/// ``` +/// HARNESS_STOP(); +/// ``` +#define HARNESS_STOP() \ + do { \ + __orr(N_STOP_NORMAL); \ + } while (0); + +/// Pseudo-hypercall number to signal the fuzzer that a custom assertion has +/// occurred, and the fuzzer should stop the current fuzzing iteration and reset +/// to the beginning of the fuzzing loop with a "solution" stop status. +#define N_STOP_ASSERT 5 + +/// HARNESS_ASSERT +/// +/// Signal the fuzzer that a custom assertion has occurred, and the fuzzer +/// should stop the current fuzzing iteration and reset to the beginning of the +/// fuzzing loop with a "solution" stop status. The default index of 0 will be +/// used. If you need to differentiate between multiple assertion harnesses +/// compiled into the same binary, you can use the `HARNESS_ASSERT_INDEX` macro +/// to specify different indices, then enable them at runtime by configuring the +/// fuzzer. +/// +/// # Example +/// +/// ``` +/// HARNESS_ASSERT(); +/// ``` +#define HARNESS_ASSERT() \ + do { \ + __orr_extended1(N_STOP_ASSERT, DEFAULT_INDEX); \ + } while (0); + +#endif // TSFFS_H +#else +#error "Unsupported platform!" +#endif +#elif _MSC_VER +#ifndef TSFFS_H +#define TSFFS_H + +/// Define common with LibFuzzer and other fuzzers to allow code that is +/// fuzzing-specific to be left in the codebase. See +/// https://llvm.org/docs/LibFuzzer.html#id35 for more information +#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION +#define FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION (1) +#endif // FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION + +#include + +/// HARNESS_START +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The default "index" of 0 will be used. If you need multiple start +/// harnesses compiled into the same binary, you can use the +/// `HARNESS_START_INDEX` macro to specify different indices, then enable them +/// at runtime by configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The size of the buffer pointed to by `size_ptr` will be saved as the +/// maximum testcase size. Each fuzzing iteration, the actual size of the +/// current testcase will be written to `*size_ptr`. +/// +/// # Arguments +/// +/// - `buffer`: The pointer to the testcase buffer +/// - `size_ptr`: The pointer to the size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// size_t size; +/// HARNESS_START(buffer, &size); +/// ``` +void HARNESS_START(void *buffer, void *size_ptr); + +/// HARNESS_START_INDEX +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The index specified by `start_index` will be used. If you need +/// multiple start harnesses compiled into the same binary, you can use this +/// macro to specify different indices, then enable them at runtime by +/// configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The size of the buffer pointed to by `size_ptr` will be saved as the +/// maximum testcase size. Each fuzzing iteration, the actual size of the +/// current testcase will be written to `*size_ptr`. +/// +/// # Arguments +/// +/// - `start_index`: The index to use for this start harness +/// - `buffer`: The pointer to the testcase buffer +/// - `size_ptr`: The pointer to the size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// size_t size; +/// HARNESS_START_INDEX(0x0001U, buffer, &size); +/// ``` +void HARNESS_START_INDEX(size_t start_index, void *buffer, void *size_ptr); + +/// HARNESS_START_WITH_MAXIMUM_SIZE +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The default "index" of 0 will be used. If you need multiple start +/// harnesses compiled into the same binary, you can use the +/// `HARNESS_START_WITH_MAXIMUM_SIZE_INDEX` macro to specify different indices, +/// then enable them at runtime by configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The `max_size` value will be saved as the maximum testcase size. Fuzzing +/// test cases will be truncated to this size before being written to the +/// buffer. +/// +/// # Arguments +/// +/// - `buffer`: The pointer to the testcase buffer +/// - `max_size`: The maximum size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// HARNESS_START_WITH_MAXIMUM_SIZE(buffer, 1024); +/// ``` +void HARNESS_START_WITH_MAXIMUM_SIZE(void *buffer, size_t max_size); + +/// HARNESS_START_WITH_MAXIMUM_SIZE_INDEX +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The index specified by `start_index` will be used. If you need +/// multiple start harnesses compiled into the same binary, you can use this +/// macro to specify different indices, then enable them at runtime by +/// configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The `max_size` value will be saved as the maximum testcase size. Fuzzing +/// test cases will be truncated to this size before being written to the +/// buffer. +/// +/// # Arguments +/// +/// - `start_index`: The index to use for this start harness +/// - `buffer`: The pointer to the testcase buffer +/// - `max_size`: The maximum size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// HARNESS_START_WITH_MAXIMUM_SIZE_INDEX(0x0001U, buffer, 1024); +/// ``` +void HARNESS_START_WITH_MAXIMUM_SIZE_INDEX(size_t start_index, void *buffer, + size_t max_size); + +/// HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The default "index" of 0 will be used. If you need multiple start +/// harnesses compiled into the same binary, you can use the +/// `HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR_INDEX` macro to specify different +/// indices, then enable them at runtime by configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The address `size_ptr` will be saved. Each fuzzing iteration, the actual +/// size of the current testcase will be written to `*size_ptr`. +/// - The `max_size` value will be saved as the maximum testcase size. Fuzzing +/// test cases will be truncated to this size before being written to the +/// buffer. +/// +/// # Arguments +/// +/// - `buffer`: The pointer to the testcase buffer +/// - `size_ptr`: The pointer to the size of the testcase buffer +/// - `max_size`: The maximum size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// size_t size; +/// HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR(buffer, &size, 1024); +/// ``` +void HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR(void *buffer, void *size_ptr, + size_t max_size); + +/// HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR_INDEX +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The index specified by `start_index` will be used. If you need +/// multiple start harnesses compiled into the same binary, you can use this +/// macro to specify different indices, then enable them at runtime by +/// configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The address `size_ptr` will be saved. Each fuzzing iteration, the actual +/// size of the current testcase will be written to `*size_ptr`. +/// - The `max_size` value will be saved as the maximum testcase size. Fuzzing +/// test cases will be truncated to this size before being written to the +/// buffer. +/// +/// # Arguments +/// +/// - `start_index`: The index to use for this start harness +/// - `buffer`: The pointer to the testcase buffer +/// - `size_ptr`: The pointer to the size of the testcase buffer +/// - `max_size`: The maximum size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// size_t size; +/// HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR_INDEX(0x0001U, buffer, &size, 1024); +/// ``` +void HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR_INDEX(size_t start_index, + void *buffer, void *size_ptr, + size_t max_size); + +/// HARNESS_STOP +/// +/// Signal the fuzzer to stop and reset to the beginning of the fuzzing loop +/// with a "normal" stop status, indicating no solution has occurred. The +/// default index of 0 will be used. If you need to differentiate between +/// multiple stop harnesses compiled into the same binary, you can use the +/// `HARNESS_STOP_INDEX` macro to specify different indices, then enable them at +/// runtime by configuring the fuzzer. +/// +/// # Example +/// +/// ``` +/// HARNESS_STOP(); +/// ``` +void HARNESS_STOP(void); + +/// HARNESS_STOP_INDEX +/// +/// Signal the fuzzer to stop and reset to the beginning of the fuzzing loop +/// with a "normal" stop status, indicating no solution has occurred. The index +/// specified by `stop_index` will be used. If you need to differentiate between +/// multiple stop harnesses compiled into the same binary, you can use this +/// macro to specify different indices, then enable them at runtime by +/// configuring the fuzzer. +/// +/// # Arguments +/// +/// - `stop_index`: The index to use for this stop harness +/// +/// # Example +/// +/// ``` +/// HARNESS_STOP_INDEX(0x0001U); +/// ``` +void HARNESS_STOP_INDEX(size_t stop_index); + +/// HARNESS_ASSERT +/// +/// Signal the fuzzer that a custom assertion has occurred, and the fuzzer +/// should stop the current fuzzing iteration and reset to the beginning of the +/// fuzzing loop with a "solution" stop status. The default index of 0 will be +/// used. If you need to differentiate between multiple assertion harnesses +/// compiled into the same binary, you can use the `HARNESS_ASSERT_INDEX` macro +/// to specify different indices, then enable them at runtime by configuring the +/// fuzzer. +/// +/// # Example +/// +/// ``` +/// HARNESS_ASSERT(); +/// ``` +void HARNESS_ASSERT(void); + +/// HARNESS_ASSERT_INDEX +/// +/// Signal the fuzzer that a custom assertion has occurred, and the fuzzer +/// should stop the current fuzzing iteration and reset to the beginning of the +/// fuzzing loop with a "solution" stop status. The index specified by +/// `assert_index` will be used. If you need to differentiate between multiple +/// assertion harnesses compiled into the same binary, you can use this macro to +/// specify different indices, then enable them at runtime by configuring the +/// fuzzer. +/// +/// # Arguments +/// +/// - `assert_index`: The index to use for this assertion harness +/// +/// # Example +/// +/// ``` +/// HARNESS_ASSERT_INDEX(0x0001U); +/// ``` +void HARNESS_ASSERT_INDEX(size_t assert_index); + +#endif // TSFFS_H +#else +#error "Unsupported compiler!" +#endif diff --git a/examples/tutorials/risc-v-kernel/src/tutorial-kernel-modules/package/kernel-modules/tutorial-mod/tutorial-mod.c b/examples/tutorials/risc-v-kernel/src/tutorial-kernel-modules/package/kernel-modules/tutorial-mod/tutorial-mod.c index 2ce22982..bf8fc817 100644 --- a/examples/tutorials/risc-v-kernel/src/tutorial-kernel-modules/package/kernel-modules/tutorial-mod/tutorial-mod.c +++ b/examples/tutorials/risc-v-kernel/src/tutorial-kernel-modules/package/kernel-modules/tutorial-mod/tutorial-mod.c @@ -15,7 +15,7 @@ #include #include -#include "tsffs-gcc-riscv64.h" +#include "tsffs.h" #define MAJOR_NUM 100 #define IOCTL_SET_MSG _IOW(MAJOR_NUM, 0, char *) diff --git a/examples/tutorials/risc-v-kernel/src/tutorial-mod-driver.c b/examples/tutorials/risc-v-kernel/src/tutorial-mod-driver.c index 8bbc9851..57e25aff 100644 --- a/examples/tutorials/risc-v-kernel/src/tutorial-mod-driver.c +++ b/examples/tutorials/risc-v-kernel/src/tutorial-mod-driver.c @@ -8,7 +8,7 @@ #include #include -#include "tsffs-gcc-riscv64.h" +#include "tsffs.h" #define MAJOR_NUM 100 #define IOCTL_SET_MSG _IOW(MAJOR_NUM, 0, char *) diff --git a/harness/.gitignore b/harness/.gitignore new file mode 100644 index 00000000..15309787 --- /dev/null +++ b/harness/.gitignore @@ -0,0 +1 @@ +*.o \ No newline at end of file diff --git a/harness/build.sh b/harness/build.sh new file mode 100755 index 00000000..4f271ab6 --- /dev/null +++ b/harness/build.sh @@ -0,0 +1,30 @@ +#!/bin/bash + +SCRIPT_DIR=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" &>/dev/null && pwd) + +cat < "${SCRIPT_DIR}/tsffs.h" +// Copyright (C) 2024 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 + +#if defined(__GNUC__) || defined(__clang__) +#ifdef __i386__ +$(cat "${SCRIPT_DIR}/tsffs-gcc-x86.h") +#elif __x86_64__ +$(cat "${SCRIPT_DIR}/tsffs-gcc-x86_64.h") +#elif __riscv && !__LP64__ +$(cat "${SCRIPT_DIR}/tsffs-gcc-riscv32.h") +#elif __riscv && __LP64__ +$(cat "${SCRIPT_DIR}/tsffs-gcc-riscv64.h") +#elif __aarch64__ +$(cat "${SCRIPT_DIR}/tsffs-gcc-aarch64.h") +#elif __arm__ +$(cat "${SCRIPT_DIR}/tsffs-gcc-arm32.h") +#else +#error "Unsupported platform!" +#endif +#elif _MSC_VER +$(cat "${SCRIPT_DIR}/tsffs-msvc-x86_64.h") +#else +#error "Unsupported compiler!" +#endif +EOF diff --git a/harness/test.c b/harness/test.c new file mode 100644 index 00000000..bd5b665e --- /dev/null +++ b/harness/test.c @@ -0,0 +1,92 @@ +#ifdef SINGLE_FILE +#include "tsffs.h" +#else +#ifdef __i386__ +#include "tsffs-gcc-x86.h" +#elif __x86_64__ +#include "tsffs-gcc-x86_64.h" +#elif __riscv && !__LP64__ +#include "tsffs-gcc-riscv32.h" +#elif __riscv && __LP64__ +#include "tsffs-gcc-riscv64.h" +#elif __aarch64__ +#include "tsffs-gcc-aarch64.h" +#elif __arm__ +#include "tsffs-gcc-arm32.h" +#endif +#endif + +#include + +int test_start() { + char buf[1024]; + size_t size = 1024; + HARNESS_START(buf, &size); + return 0; +} + +int test_start_with_maximum_size() { + char buf[1024]; + size_t size = 1024; + HARNESS_START_WITH_MAXIMUM_SIZE(buf, size); + return 0; +} + +int test_start_with_maximum_size_and_ptr() { + char buf[1024]; + size_t size = 1024; + HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR(buf, &size, 1024); + return 0; +} + +int test_stop() { + char buf[1024]; + size_t size = 1024; + HARNESS_STOP(); + return 0; +} + +int test_assert() { + char buf[1024]; + size_t size = 1024; + HARNESS_ASSERT(); + return 0; +} + +#ifndef __arm__ +int test_start_index() { + char buf[1024]; + size_t size = 1024; + HARNESS_START_INDEX(1, buf, &size); + return 0; +} + +int test_start_with_maximum_size_index() { + char buf[1024]; + size_t size = 1024; + HARNESS_START_WITH_MAXIMUM_SIZE_INDEX(2, buf, size); + return 0; +} + +int test_start_with_maximum_size_and_ptr_index() { + char buf[1024]; + size_t size = 1024; + HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR_INDEX(3, buf, &size, 1024); + return 0; +} + +int test_stop_index() { + char buf[1024]; + size_t size = 1024; + HARNESS_STOP_INDEX(4); + return 0; +} + +int test_assert_index() { + char buf[1024]; + size_t size = 1024; + HARNESS_ASSERT_INDEX(5); + return 0; +} + +#endif diff --git a/harness/test.sh b/harness/test.sh new file mode 100755 index 00000000..3d9be350 --- /dev/null +++ b/harness/test.sh @@ -0,0 +1,62 @@ +#!/bin/bash + +# Copyright (C) 2024 Intel Corporation +# SPDX-License-Identifier: Apache-2.0 + +# Compile test.c for each of x86, x86_64, riscv32, riscv64 architecture + +SCRIPT_DIR=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" &>/dev/null && pwd) + +set -e + +"${SCRIPT_DIR}/build.sh" + +rm -f "${SCRIPT_DIR}/test_x86_64-clang.o" || exit 0 +rm -f "${SCRIPT_DIR}/test_x86-clang.o" || exit 0 +rm -f "${SCRIPT_DIR}/test_riscv32-clang.o" || exit 0 +rm -f "${SCRIPT_DIR}/test_riscv64-clang.o" || exit 0 +rm -f "${SCRIPT_DIR}/test_x86_64-gcc.o" || exit 0 +rm -f "${SCRIPT_DIR}/test_x86-gcc.o" || exit 0 +rm -rf "${SCRIPT_DIR}/test_aarch64-clang.o" || exit 0 +rm -rf "${SCRIPT_DIR}/test_arm32-clang.o" || exit 0 +rm -f "${SCRIPT_DIR}/test_x86_64-clang-single-file.o" || exit 0 +rm -f "${SCRIPT_DIR}/test_x86-clang-single-file.o" || exit 0 +rm -f "${SCRIPT_DIR}/test_riscv32-clang-single-file.o" || exit 0 +rm -f "${SCRIPT_DIR}/test_riscv64-clang-single-file.o" || exit 0 +rm -f "${SCRIPT_DIR}/test_x86_64-gcc-single-file.o" || exit 0 +rm -f "${SCRIPT_DIR}/test_x86-gcc-single-file.o" || exit 0 +rm -rf "${SCRIPT_DIR}test_aarch64-clang-single-file.o" || exit 0 +rm -rf "${SCRIPT_DIR}test_arm32-clang-single-file.o" || exit 0 + +echo "Testing x86_64 (single file)..." +clang -target x86_64-unknown-linux-gnu -DSINGLE_FILE=1 -g -c "${SCRIPT_DIR}/test.c" -o "${SCRIPT_DIR}/test_x86_64-clang-single-file.o" +echo "Testing i386 (single file)..." +clang -target i386-unknown-linux-gnu -DSINGLE_FILE=1 -g -c "${SCRIPT_DIR}/test.c" -o "${SCRIPT_DIR}/test_x86-clang-single-file.o" +echo "Testing riscv32 (single file)..." +clang -target riscv32-unknown-linux-gnu -DSINGLE_FILE=1 -g -c "${SCRIPT_DIR}/test.c" -o "${SCRIPT_DIR}/test_riscv32-clang-single-file.o" +echo "Testing riscv64 (single file)..." +clang -target riscv64-unknown-linux-gnu -DSINGLE_FILE=1 -g -c "${SCRIPT_DIR}/test.c" -o "${SCRIPT_DIR}/test_riscv64-clang-single-file.o" +echo "Testing aarch64 (single file)..." +clang -target aarch64-unknown-linux-gnu -DSINGLE_FILE=1 -g -c "${SCRIPT_DIR}/test.c" -o "${SCRIPT_DIR}/test_aarch64-clang-single-file.o" +echo "Testing arm (single file)..." +clang -target arm-unknown-linux-gnu -mfloat-abi=soft -DSINGLE_FILE=1 -g -c "${SCRIPT_DIR}/test.c" -o "${SCRIPT_DIR}/test_arm32-clang-single-file.o" +echo "Testing x86_64 (single file, gcc)..." +gcc -DSINGLE_FILE=1 -g -c "${SCRIPT_DIR}/test.c" -o "${SCRIPT_DIR}/test_x86_64-gcc.o" +echo "Testing i386 (single file, gcc)..." +gcc -DSINGLE_FILE=1 -g -m32 -c "${SCRIPT_DIR}/test.c" -o "${SCRIPT_DIR}/test_x86-gcc.o" +echo "Testing x86_64 (multi file)..." +clang -target x86_64-unknown-linux-gnu -g -c "${SCRIPT_DIR}/test.c" -o "${SCRIPT_DIR}/test_x86_64-clang.o" +echo "Testing i386 (multi file)..." +clang -target i386-unknown-linux-gnu -g -c "${SCRIPT_DIR}/test.c" -o "${SCRIPT_DIR}/test_x86-clang.o" +echo "Testing riscv32(multi file)..." +clang -target riscv32-unknown-linux-gnu -g -c "${SCRIPT_DIR}/test.c" -o "${SCRIPT_DIR}/test_riscv32-clang.o" +echo "Testing riscv64(multi file)..." +clang -target riscv64-unknown-linux-gnu -g -c "${SCRIPT_DIR}/test.c" -o "${SCRIPT_DIR}/test_riscv64-clang.o" +echo "Testing aarch64 (multi file)..." +clang -target aarch64-unknown-linux-gnu -g -c "${SCRIPT_DIR}/test.c" -o "${SCRIPT_DIR}/test_aarch64-clang.o" +echo "Testing arm (multi file)..." +clang -target arm-unknown-linux-gnu -mfloat-abi=soft -g -c "${SCRIPT_DIR}/test.c" -o "${SCRIPT_DIR}/test_arm32-clang.o" +echo "Testing x86_64 (multi file, gcc)..." +gcc -g -c "${SCRIPT_DIR}/test.c" -o "${SCRIPT_DIR}/test_x86_64-gcc.o" +echo "Testing i386(multi file, gcc)..." +gcc -g -m32 -c "${SCRIPT_DIR}/test.c" -o "${SCRIPT_DIR}/test_x86-gcc.o" \ No newline at end of file diff --git a/harness/tsffs-gcc-aarch64.h b/harness/tsffs-gcc-aarch64.h new file mode 100644 index 00000000..f8cfa738 --- /dev/null +++ b/harness/tsffs-gcc-aarch64.h @@ -0,0 +1,447 @@ +// Copyright (C) 2024 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 + +/// Definitions and macros for compiled-in harnessing of C and C++ target +/// software for the RISC-V (32-bit) architecture + +#ifndef TSFFS_H +#define TSFFS_H + +/// Define common with LibFuzzer and other fuzzers to allow code that is +/// fuzzing-specific to be left in the codebase. See +/// https://llvm.org/docs/LibFuzzer.html#id35 for more information +#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION +#define FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION (1) +#endif // FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION + +#define __stringify(x) #x +#define __tostring(x) __stringify(x) + +/// __orr +/// +/// Invoke the magic instruction defined by SIMICS for the RISC-V architecture +/// with a specific value of `n` +/// +/// # Arguments +/// +/// * `value` - The value of `n` to use in the magic instruction +#define __orr(value) \ + __asm__ __volatile__("orr x" __tostring(value) ", x" __tostring( \ + value) ", x" __tostring(value)); + +/// __orr_extended1 +/// +/// Invoke the magic instruction defined by SIMICS for the RISC-V architecture +/// with a specific value of `n` and a pseudo-argument in register `r10`. +/// +/// # Arguments +/// +/// * `value` - The value of `n` to use in the magic instruction +/// * `arg0` - The value to place in register `r10` +#define __orr_extended1(value, arg0) \ + __asm__ __volatile__( \ + "mov x28, %0; orr x" __tostring(value) ", x" __tostring( \ + value) ", x" __tostring(value) \ + : \ + : "g"(arg0)); + +/// __orr_extended2 +/// +/// Invoke the magic instruction defined by SIMICS for the RISC-V architecture +/// with a specific value of `n` and two pseudo-arguments in registers `r10` and +/// `r9`. +/// +/// # Arguments +/// +/// * `value` - The value of `n` to use in the magic instruction +/// * `arg0` - The value to place in register `r10` +/// * `arg1` - The value to place in register `r9` +#define __orr_extended2(value, arg0, arg1) \ + __asm__ __volatile__( \ + "mov x28, %0; mov x27, %1; orr x" __tostring(value) ", x" __tostring( \ + value) ", x" __tostring(value) \ + : \ + : "r"(arg0), "r"(arg1)); + +/// __orr_extended3 +/// +/// Invoke the magic instruction defined by SIMICS for the RISC-V architecture +/// with a specific value of `n` and three pseudo-arguments in registers `r10`, +/// `r9`, and `r8`. +/// +/// # Arguments +/// +/// * `value` - The value of `n` to use in the magic instruction +/// * `arg0` - The value to place in register `r10` +/// * `arg1` - The value to place in register `r9` +/// * `arg2` - The value to place in register `r8` +#define __orr_extended3(value, arg0, arg1, arg2) \ + __asm__ __volatile__( \ + "mov x28, %0; mov x27, %1; mov x26, %2; orr x" __tostring( \ + value) ", x" __tostring(value) ", x" __tostring(value) \ + : \ + : "r"(arg0), "r"(arg1), "r"(arg2)); + +/// __orr_extended4 +/// +/// Invoke the magic instruction defined by SIMICS for the RISC-V architecture +/// with a specific value of `n` and four pseudo-arguments in registers `r10`, +/// `r9`, `r8`, and `r7`. +/// +/// # Arguments +/// +/// * `value` - The value of `n` to use in the magic instruction +/// * `arg0` - The value to place in register `r10` +/// * `arg1` - The value to place in register `r9` +/// * `arg2` - The value to place in register `r8` +/// * `arg3` - The value to place in register `r7` +#define __orr_extended4(value, arg0, arg1, arg2, arg3) \ + __asm__ __volatile__( \ + "mov x28, %0; mov x27, %1; mov x26, %2; mov x25, %3; " \ + "orr x" __tostring(value) ", x" __tostring(value) ", x" __tostring( \ + value) \ + : \ + : "r"(arg0), "r"(arg1), "r"(arg2), "r"(arg3)); + +/// The default index number used for magic instructions. All magic instructions +/// support multiple start and stop indices, which defaults to 0 if not +/// specified. +#define DEFAULT_INDEX (0x0000U) + +/// Pseudo-hypercall number to signal the fuzzer to use the first argument to +/// the magic instruction as the pointer to the testcase buffer and the second +/// argument as a pointer to the size of the testcase buffer. +#define N_START_BUFFER_PTR_SIZE_PTR 1 + +/// HARNESS_START +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The default "index" of 0 will be used. If you need multiple start +/// harnesses compiled into the same binary, you can use the +/// `HARNESS_START_INDEX` macro to specify different indices, then enable them +/// at runtime by configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The size of the buffer pointed to by `size_ptr` will be saved as the +/// maximum testcase size. Each fuzzing iteration, the actual size of the +/// current testcase will be written to `*size_ptr`. +/// +/// # Arguments +/// +/// - `buffer`: The pointer to the testcase buffer +/// - `size_ptr`: The pointer to the size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// size_t size; +/// HARNESS_START(buffer, &size); +/// ``` +#define HARNESS_START(buffer, size_ptr) \ + do { \ + __orr_extended3(N_START_BUFFER_PTR_SIZE_PTR, DEFAULT_INDEX, buffer, \ + size_ptr); \ + } while (0); + +/// HARNESS_START_INDEX +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The index specified by `start_index` will be used. If you need +/// multiple start harnesses compiled into the same binary, you can use this +/// macro to specify different indices, then enable them at runtime by +/// configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The size of the buffer pointed to by `size_ptr` will be saved as the +/// maximum testcase size. Each fuzzing iteration, the actual size of the +/// current testcase will be written to `*size_ptr`. +/// +/// # Arguments +/// +/// - `start_index`: The index to use for this start harness +/// - `buffer`: The pointer to the testcase buffer +/// - `size_ptr`: The pointer to the size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// size_t size; +/// HARNESS_START_INDEX(0x0001U, buffer, &size); +/// ``` +#define HARNESS_START_INDEX(start_index, buffer, size_ptr) \ + do { \ + __orr_extended3(N_START_BUFFER_PTR_SIZE_PTR, start_index, buffer, \ + size_ptr); \ + } while (0); + +/// Pseudo-hypercall number to signal the fuzzer to use the first argument to +/// the magic instruction as the pointer to the testcase buffer and the second +/// argument as the maximum size of the testcase buffer. +#define N_START_BUFFER_PTR_SIZE_VAL 2 + +/// HARNESS_START_WITH_MAXIMUM_SIZE +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The default "index" of 0 will be used. If you need multiple start +/// harnesses compiled into the same binary, you can use the +/// `HARNESS_START_WITH_MAXIMUM_SIZE_INDEX` macro to specify different indices, +/// then enable them at runtime by configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The `max_size` value will be saved as the maximum testcase size. Fuzzing +/// test cases will be truncated to this size before being written to the +/// buffer. +/// +/// # Arguments +/// +/// - `buffer`: The pointer to the testcase buffer +/// - `max_size`: The maximum size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// HARNESS_START_WITH_MAXIMUM_SIZE(buffer, 1024); +/// ``` +#define HARNESS_START_WITH_MAXIMUM_SIZE(buffer, max_size) \ + do { \ + __orr_extended3(N_START_BUFFER_PTR_SIZE_VAL, DEFAULT_INDEX, buffer, \ + max_size); \ + } while (0); + +/// HARNESS_START_WITH_MAXIMUM_SIZE_INDEX +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The index specified by `start_index` will be used. If you need +/// multiple start harnesses compiled into the same binary, you can use this +/// macro to specify different indices, then enable them at runtime by +/// configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The `max_size` value will be saved as the maximum testcase size. Fuzzing +/// test cases will be truncated to this size before being written to the +/// buffer. +/// +/// # Arguments +/// +/// - `start_index`: The index to use for this start harness +/// - `buffer`: The pointer to the testcase buffer +/// - `max_size`: The maximum size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// HARNESS_START_WITH_MAXIMUM_SIZE_INDEX(0x0001U, buffer, 1024); +/// ``` +#define HARNESS_START_WITH_MAXIMUM_SIZE_INDEX(start_index, buffer, max_size) \ + do { \ + __orr_extended3(N_START_BUFFER_PTR_SIZE_VAL, start_index, buffer, \ + max_size); \ + } while (0); + +/// Pseudo-hypercall number to signal the fuzzer to use the first argument to +/// the magic instruction as the pointer to the testcase buffer, the second +/// argument as a pointer to the size of the testcase buffer, and the third +/// argument as the maximum size of the testcase buffer. +#define N_START_BUFFER_PTR_SIZE_PTR_VAL 3 + +/// HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The default "index" of 0 will be used. If you need multiple start +/// harnesses compiled into the same binary, you can use the +/// `HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR_INDEX` macro to specify different +/// indices, then enable them at runtime by configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The address `size_ptr` will be saved. Each fuzzing iteration, the actual +/// size of the current testcase will be written to `*size_ptr`. +/// - The `max_size` value will be saved as the maximum testcase size. Fuzzing +/// test cases will be truncated to this size before being written to the +/// buffer. +/// +/// # Arguments +/// +/// - `buffer`: The pointer to the testcase buffer +/// - `size_ptr`: The pointer to the size of the testcase buffer +/// - `max_size`: The maximum size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// size_t size; +/// HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR(buffer, &size, 1024); +/// ``` +#define HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR(buffer, size_ptr, max_size) \ + do { \ + __orr_extended4(N_START_BUFFER_PTR_SIZE_PTR_VAL, DEFAULT_INDEX, buffer, \ + size_ptr, max_size); \ + } while (0); + +/// HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR_INDEX +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The index specified by `start_index` will be used. If you need +/// multiple start harnesses compiled into the same binary, you can use this +/// macro to specify different indices, then enable them at runtime by +/// configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The address `size_ptr` will be saved. Each fuzzing iteration, the actual +/// size of the current testcase will be written to `*size_ptr`. +/// - The `max_size` value will be saved as the maximum testcase size. Fuzzing +/// test cases will be truncated to this size before being written to the +/// buffer. +/// +/// # Arguments +/// +/// - `start_index`: The index to use for this start harness +/// - `buffer`: The pointer to the testcase buffer +/// - `size_ptr`: The pointer to the size of the testcase buffer +/// - `max_size`: The maximum size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// size_t size; +/// HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR_INDEX(0x0001U, buffer, &size, 1024); +/// ``` +#define HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR_INDEX(start_index, buffer, \ + size_ptr, max_size) \ + do { \ + __orr_extended4(N_START_BUFFER_PTR_SIZE_PTR_VAL, start_index, buffer, \ + size_ptr, max_size); \ + } while (0); + +/// Pseudo-hypercall number to signal the fuzzer to stop the current fuzzing +/// iteration and reset to the beginning of the fuzzing loop with a "normal" +/// stop status, indicating no solution has occurred. +#define N_STOP_NORMAL 4 + +/// HARNESS_STOP +/// +/// Signal the fuzzer to stop and reset to the beginning of the fuzzing loop +/// with a "normal" stop status, indicating no solution has occurred. The +/// default index of 0 will be used. If you need to differentiate between +/// multiple stop harnesses compiled into the same binary, you can use the +/// `HARNESS_STOP_INDEX` macro to specify different indices, then enable them at +/// runtime by configuring the fuzzer. +/// +/// # Example +/// +/// ``` +/// HARNESS_STOP(); +/// ``` +#define HARNESS_STOP() \ + do { \ + __orr_extended1(N_STOP_NORMAL, DEFAULT_INDEX); \ + } while (0); + +/// HARNESS_STOP_INDEX +/// +/// Signal the fuzzer to stop and reset to the beginning of the fuzzing loop +/// with a "normal" stop status, indicating no solution has occurred. The index +/// specified by `stop_index` will be used. If you need to differentiate between +/// multiple stop harnesses compiled into the same binary, you can use this +/// macro to specify different indices, then enable them at runtime by +/// configuring the fuzzer. +/// +/// # Arguments +/// +/// - `stop_index`: The index to use for this stop harness +/// +/// # Example +/// +/// ``` +/// HARNESS_STOP_INDEX(0x0001U); +/// ``` +#define HARNESS_STOP_INDEX(stop_index) \ + do { \ + __orr_extended1(N_STOP_NORMAL, stop_index); \ + } while (0); + +/// Pseudo-hypercall number to signal the fuzzer that a custom assertion has +/// occurred, and the fuzzer should stop the current fuzzing iteration and reset +/// to the beginning of the fuzzing loop with a "solution" stop status. +#define N_STOP_ASSERT 5 + +/// HARNESS_ASSERT +/// +/// Signal the fuzzer that a custom assertion has occurred, and the fuzzer +/// should stop the current fuzzing iteration and reset to the beginning of the +/// fuzzing loop with a "solution" stop status. The default index of 0 will be +/// used. If you need to differentiate between multiple assertion harnesses +/// compiled into the same binary, you can use the `HARNESS_ASSERT_INDEX` macro +/// to specify different indices, then enable them at runtime by configuring the +/// fuzzer. +/// +/// # Example +/// +/// ``` +/// HARNESS_ASSERT(); +/// ``` +#define HARNESS_ASSERT() \ + do { \ + __orr_extended1(N_STOP_ASSERT, DEFAULT_INDEX); \ + } while (0); + +/// HARNESS_ASSERT_INDEX +/// +/// Signal the fuzzer that a custom assertion has occurred, and the fuzzer +/// should stop the current fuzzing iteration and reset to the beginning of the +/// fuzzing loop with a "solution" stop status. The index specified by +/// `assert_index` will be used. If you need to differentiate between multiple +/// assertion harnesses compiled into the same binary, you can use this macro to +/// specify different indices, then enable them at runtime by configuring the +/// fuzzer. +/// +/// # Arguments +/// +/// - `assert_index`: The index to use for this assertion harness +/// +/// # Example +/// +/// ``` +/// HARNESS_ASSERT_INDEX(0x0001U); +/// ``` +#define HARNESS_ASSERT_INDEX(assert_index) \ + do { \ + __orr_extended1(N_STOP_ASSERT, assert_index); \ + } while (0); + +#endif // TSFFS_H diff --git a/harness/tsffs-gcc-arm32.h b/harness/tsffs-gcc-arm32.h new file mode 100644 index 00000000..62893809 --- /dev/null +++ b/harness/tsffs-gcc-arm32.h @@ -0,0 +1,288 @@ +// Copyright (C) 2024 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 + +/// Definitions and macros for compiled-in harnessing of C and C++ target +/// software for the RISC-V (32-bit) architecture + +#ifndef TSFFS_H +#define TSFFS_H + +/// Define common with LibFuzzer and other fuzzers to allow code that is +/// fuzzing-specific to be left in the codebase. See +/// https://llvm.org/docs/LibFuzzer.html#id35 for more information +#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION +#define FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION (1) +#endif // FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION + +#define __stringify(x) #x +#define __tostring(x) __stringify(x) + +/// __orr +/// +/// Invoke the magic instruction defined by SIMICS for the RISC-V architecture +/// with a specific value of `n` +/// +/// # Arguments +/// +/// * `value` - The value of `n` to use in the magic instruction +#define __orr(value) \ + __asm__ __volatile__("orr r" __tostring(value) ", r" __tostring( \ + value) ", r" __tostring(value)); + +/// __orr_extended1 +/// +/// Invoke the magic instruction defined by SIMICS for the RISC-V architecture +/// with a specific value of `n` and a pseudo-argument in register `r10`. +/// +/// # Arguments +/// +/// * `value` - The value of `n` to use in the magic instruction +/// * `arg0` - The value to place in register `r10` +#define __orr_extended1(value, arg0) \ + __asm__ __volatile__( \ + "mov r10, %0; orr r" __tostring(value) ", r" __tostring( \ + value) ", r" __tostring(value) \ + : \ + : "r"(arg0)); + +/// __orr_extended2 +/// +/// Invoke the magic instruction defined by SIMICS for the RISC-V architecture +/// with a specific value of `n` and two pseudo-arguments in registers `r10` and +/// `r9`. +/// +/// # Arguments +/// +/// * `value` - The value of `n` to use in the magic instruction +/// * `arg0` - The value to place in register `r10` +/// * `arg1` - The value to place in register `r9` +#define __orr_extended2(value, arg0, arg1) \ + __asm__ __volatile__( \ + "mov r10, %0; mov r9, %1; orr r" __tostring(value) ", r" __tostring( \ + value) ", r" __tostring(value) \ + : \ + : "r"(arg0), "r"(arg1)); + +/// __orr_extended3 +/// +/// Invoke the magic instruction defined by SIMICS for the RISC-V architecture +/// with a specific value of `n` and three pseudo-arguments in registers `r10`, +/// `r9`, and `r8`. +/// +/// # Arguments +/// +/// * `value` - The value of `n` to use in the magic instruction +/// * `arg0` - The value to place in register `r10` +/// * `arg1` - The value to place in register `r9` +/// * `arg2` - The value to place in register `r8` +#define __orr_extended3(value, arg0, arg1, arg2) \ + __asm__ __volatile__( \ + "mov r10, %0; mov r9, %1; mov r8, %2; orr r" __tostring( \ + value) ", r" __tostring(value) ", r" __tostring(value) \ + : \ + : "r"(arg0), "r"(arg1), "r"(arg2)); + +/// __orr_extended4 +/// +/// Invoke the magic instruction defined by SIMICS for the RISC-V architecture +/// with a specific value of `n` and four pseudo-arguments in registers `r10`, +/// `r9`, `r8`, and `r7`. +/// +/// # Arguments +/// +/// * `value` - The value of `n` to use in the magic instruction +/// * `arg0` - The value to place in register `r10` +/// * `arg1` - The value to place in register `r9` +/// * `arg2` - The value to place in register `r8` +/// * `arg3` - The value to place in register `r7` +#define __orr_extended4(value, arg0, arg1, arg2, arg3) \ + __asm__ __volatile__( \ + "mov r10, %0; mov r9, %1; mov r8, %2; mov r7, %3; " \ + "orr r" __tostring(value) ", r" __tostring(value) ", r" __tostring( \ + value) \ + : \ + : "r"(arg0), "r"(arg1), "r"(arg2), "r"(arg3)); + +/// Magic value defined by SIMICS as the "leaf" value of a CPUID instruction +/// that is treated as a magic instruction. +#define MAGIC (0x4711U) + +/// The default index number used for magic instructions. All magic instructions +/// support multiple start and stop indices, which defaults to 0 if not +/// specified. +#define DEFAULT_INDEX (0x0000U) + +/// Pseudo-hypercall number to signal the fuzzer to use the first argument to +/// the magic instruction as the pointer to the testcase buffer and the second +/// argument as a pointer to the size of the testcase buffer. +#define N_START_BUFFER_PTR_SIZE_PTR 1 + +/// HARNESS_START +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The default "index" of 0 will be used. If you need multiple start +/// harnesses compiled into the same binary, you can use the +/// `HARNESS_START_INDEX` macro to specify different indices, then enable them +/// at runtime by configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The size of the buffer pointed to by `size_ptr` will be saved as the +/// maximum testcase size. Each fuzzing iteration, the actual size of the +/// current testcase will be written to `*size_ptr`. +/// +/// # Arguments +/// +/// - `buffer`: The pointer to the testcase buffer +/// - `size_ptr`: The pointer to the size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// size_t size; +/// HARNESS_START(buffer, &size); +/// ``` +#define HARNESS_START(buffer, size_ptr) \ + do { \ + __orr_extended2(N_START_BUFFER_PTR_SIZE_PTR, buffer, size_ptr); \ + } while (0); + +/// Pseudo-hypercall number to signal the fuzzer to use the first argument to +/// the magic instruction as the pointer to the testcase buffer and the second +/// argument as the maximum size of the testcase buffer. +#define N_START_BUFFER_PTR_SIZE_VAL 2 + +/// HARNESS_START_WITH_MAXIMUM_SIZE +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The default "index" of 0 will be used. If you need multiple start +/// harnesses compiled into the same binary, you can use the +/// `HARNESS_START_WITH_MAXIMUM_SIZE_INDEX` macro to specify different indices, +/// then enable them at runtime by configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The `max_size` value will be saved as the maximum testcase size. Fuzzing +/// test cases will be truncated to this size before being written to the +/// buffer. +/// +/// # Arguments +/// +/// - `buffer`: The pointer to the testcase buffer +/// - `max_size`: The maximum size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// HARNESS_START_WITH_MAXIMUM_SIZE(buffer, 1024); +/// ``` +#define HARNESS_START_WITH_MAXIMUM_SIZE(buffer, max_size) \ + do { \ + __orr_extended2(N_START_BUFFER_PTR_SIZE_VAL, buffer, max_size); \ + } while (0); + +/// Pseudo-hypercall number to signal the fuzzer to use the first argument to +/// the magic instruction as the pointer to the testcase buffer, the second +/// argument as a pointer to the size of the testcase buffer, and the third +/// argument as the maximum size of the testcase buffer. +#define N_START_BUFFER_PTR_SIZE_PTR_VAL 3 + +/// HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The default "index" of 0 will be used. If you need multiple start +/// harnesses compiled into the same binary, you can use the +/// `HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR_INDEX` macro to specify different +/// indices, then enable them at runtime by configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The address `size_ptr` will be saved. Each fuzzing iteration, the actual +/// size of the current testcase will be written to `*size_ptr`. +/// - The `max_size` value will be saved as the maximum testcase size. Fuzzing +/// test cases will be truncated to this size before being written to the +/// buffer. +/// +/// # Arguments +/// +/// - `buffer`: The pointer to the testcase buffer +/// - `size_ptr`: The pointer to the size of the testcase buffer +/// - `max_size`: The maximum size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// size_t size; +/// HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR(buffer, &size, 1024); +/// ``` +#define HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR(buffer, size_ptr, max_size) \ + do { \ + __orr_extended3(N_START_BUFFER_PTR_SIZE_PTR_VAL, buffer, size_ptr, \ + max_size); \ + } while (0); + +/// Pseudo-hypercall number to signal the fuzzer to stop the current fuzzing +/// iteration and reset to the beginning of the fuzzing loop with a "normal" +/// stop status, indicating no solution has occurred. +#define N_STOP_NORMAL 4 + +/// HARNESS_STOP +/// +/// Signal the fuzzer to stop and reset to the beginning of the fuzzing loop +/// with a "normal" stop status, indicating no solution has occurred. The +/// default index of 0 will be used. If you need to differentiate between +/// multiple stop harnesses compiled into the same binary, you can use the +/// `HARNESS_STOP_INDEX` macro to specify different indices, then enable them at +/// runtime by configuring the fuzzer. +/// +/// # Example +/// +/// ``` +/// HARNESS_STOP(); +/// ``` +#define HARNESS_STOP() \ + do { \ + __orr(N_STOP_NORMAL); \ + } while (0); + +/// Pseudo-hypercall number to signal the fuzzer that a custom assertion has +/// occurred, and the fuzzer should stop the current fuzzing iteration and reset +/// to the beginning of the fuzzing loop with a "solution" stop status. +#define N_STOP_ASSERT 5 + +/// HARNESS_ASSERT +/// +/// Signal the fuzzer that a custom assertion has occurred, and the fuzzer +/// should stop the current fuzzing iteration and reset to the beginning of the +/// fuzzing loop with a "solution" stop status. The default index of 0 will be +/// used. If you need to differentiate between multiple assertion harnesses +/// compiled into the same binary, you can use the `HARNESS_ASSERT_INDEX` macro +/// to specify different indices, then enable them at runtime by configuring the +/// fuzzer. +/// +/// # Example +/// +/// ``` +/// HARNESS_ASSERT(); +/// ``` +#define HARNESS_ASSERT() \ + do { \ + __orr_extended1(N_STOP_ASSERT, DEFAULT_INDEX); \ + } while (0); + +#endif // TSFFS_H \ No newline at end of file diff --git a/harness/tsffs-gcc-riscv32.h b/harness/tsffs-gcc-riscv32.h index 1847b3c4..5e664c1c 100644 --- a/harness/tsffs-gcc-riscv32.h +++ b/harness/tsffs-gcc-riscv32.h @@ -2,25 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 /// Definitions and macros for compiled-in harnessing of C and C++ target -/// software for the RISC-V (64-bit) architecture -/// -/// Example: -/// ```c -/// #include "tsffs-gcc-riscv32.h" -/// -/// int main() { -/// char buf[0x10]; -/// size_t size = 0x10; -/// size_t *size_ptr = &size; -/// HARNESS_START((char *)buf, size_ptr); -/// int retval = YourSpecialDecoder(buf, *size_ptr); -/// if (!retval) { -/// HARNESS_ASSERT(); -/// } else { -/// HARNESS_STOP(); -/// } -/// } -/// ``` +/// software for the RISC-V (32-bit) architecture #ifndef TSFFS_H #define TSFFS_H @@ -32,93 +14,430 @@ #define FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION (1) #endif // FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION -/// Default magic instruction `n` value to signal the TSFFS fuzzer to start the -/// fuzzing loop -#define MAGIC_START (0x0001U) -/// Default magic instruction `n` value to signal the TSFFS fuzzer to stop and -/// reset the fuzzing loop -#define MAGIC_STOP (0x0002U) -/// Default magic instruction `n` value to signal the TSFFS fuzzer that an error -/// has occurred and the testcase input should be saved as a solution -#define MAGIC_ASSERT (0x0003U) - -/// Alternative magic numbers that can be used for start and stop events in -/// conjunction with setting the magic number for each event via the SIMICS or -/// SIMICS Python script interface -#define MAGIC_ALT_0 (0x0004U) -#define MAGIC_ALT_1 (0x0005U) -#define MAGIC_ALT_2 (0x0006U) -#define MAGIC_ALT_3 (0x0007U) -#define MAGIC_ALT_4 (0x0008U) -#define MAGIC_ALT_5 (0x0009U) -#define MAGIC_ALT_6 (0x000aU) -#define MAGIC_ALT_7 (0x000bU) +/// __srai +/// +/// Invoke the magic instruction defined by SIMICS for the RISC-V architecture +/// with a specific value of `n` +/// +/// # Arguments +/// +/// * `value` - The value of `n` to use in the magic instruction +#define __srai(value) \ + __asm__ __volatile__("srai zero, zero, %0" : : "I"(value) :) +/// __srai_extended1 +/// /// Invoke the magic instruction defined by SIMICS for the RISC-V architecture -/// (`srai zero, zero, n`) with a specific value of `n`, after setting register -/// `a0` to the value of the pointer to the testcase and register `a1` to the -/// value of the pointer to the testcase size. These registers are accessed by -/// the fuzzer and are defined per-architecture. -#define __srai_extended(value, testcase_ptr, size_ptr) \ - __asm__ __volatile__("mv a0, %0; mv a1, %1; srai zero, zero, %2" \ - : \ - : "r"(testcase_ptr), "r"(size_ptr), "I"(value) \ +/// with a specific value of `n` and a pseudo-argument in register `a0`. +/// +/// # Arguments +/// +/// * `value` - The value of `n` to use in the magic instruction +/// * `arg0` - The value to place in register `a0` +#define __srai_extended1(value, arg0) \ + __asm__ __volatile__("mv a0, %0; srai zero, zero, %1" \ + : \ + : "r"(arg0), "I"(value) \ + : "a0"); + +/// __srai_extended2 +/// +/// Invoke the magic instruction defined by SIMICS for the RISC-V architecture +/// with a specific value of `n` and pseudo-arguments in registers `a0` and +/// `a1`. +/// +/// # Arguments +/// +/// * `value` - The value of `n` to use in the magic instruction +/// * `arg0` - The value to place in register `a0` +/// * `arg1` - The value to place in register `a1` +#define __srai_extended2(value, arg0, arg1) \ + __asm__ __volatile__("mv a0, %0; mv a1, %1; srai zero, zero, %2" \ + : \ + : "r"(arg0), "r"(arg1), "I"(value) \ : "a0", "a1"); +/// __srai_extended3 +/// /// Invoke the magic instruction defined by SIMICS for the RISC-V architecture -/// (`srai zero, zero, n`) with a specific value of `n` -#define __srai(value) \ - __asm__ __volatile__("srai zero, zero, %0" : : "I"(value) :) +/// with a specific value of `n` and pseudo-arguments in registers `a0`, `a1`, +/// and `a2`. +/// +/// # Arguments +/// +/// * `value` - The value of `n` to use in the magic instruction +/// * `arg0` - The value to place in register `a0` +/// * `arg1` - The value to place in register `a1` +/// * `arg2` - The value to place in register `a2` +#define __srai_extended3(value, arg0, arg1, arg2) \ + __asm__ __volatile__("mv a0, %0; mv a1, %1; mv a2, %2; srai zero, zero, %3" \ + : \ + : "r"(arg0), "r"(arg1), "r"(arg2), "I"(value) \ + : "a0", "a1", "a2"); + +/// __srai_extended4 +/// +/// Invoke the magic instruction defined by SIMICS for the RISC-V architecture +/// with a specific value of `n` and pseudo-arguments in registers `a0`, `a1`, +/// `a2`, and `a3`. +/// +/// # Arguments +/// +/// * `value` - The value of `n` to use in the magic instruction +/// * `arg0` - The value to place in register `a0` +/// * `arg1` - The value to place in register `a1` +/// * `arg2` - The value to place in register `a2` +/// * `arg3` - The value to place in register `a3` +#define __srai_extended4(value, arg0, arg1, arg2, arg3) \ + __asm__ __volatile__( \ + "mv a0, %0; mv a1, %1; mv a2, %2; mv a3, %3; srai zero, zero, %4" \ + : \ + : "r"(arg0), "r"(arg1), "r"(arg2), "r"(arg3), "I"(value) \ + : "a0", "a1", "a2", "a3"); + +/// Magic value defined by SIMICS as the "leaf" value of a CPUID instruction +/// that is treated as a magic instruction. +#define MAGIC (0x4711U) + +/// The default index number used for magic instructions. All magic instructions +/// support multiple start and stop indices, which defaults to 0 if not +/// specified. +#define DEFAULT_INDEX (0x0000U) + +/// Pseudo-hypercall number to signal the fuzzer to use the first argument to +/// the magic instruction as the pointer to the testcase buffer and the second +/// argument as a pointer to the size of the testcase buffer. +#define N_START_BUFFER_PTR_SIZE_PTR (0x0001U) + +/// HARNESS_START +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The default "index" of 0 will be used. If you need multiple start +/// harnesses compiled into the same binary, you can use the +/// `HARNESS_START_INDEX` macro to specify different indices, then enable them +/// at runtime by configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The size of the buffer pointed to by `size_ptr` will be saved as the +/// maximum testcase size. Each fuzzing iteration, the actual size of the +/// current testcase will be written to `*size_ptr`. +/// +/// # Arguments +/// +/// - `buffer`: The pointer to the testcase buffer +/// - `size_ptr`: The pointer to the size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// size_t size; +/// HARNESS_START(buffer, &size); +/// ``` +#define HARNESS_START(buffer, size_ptr) \ + do { \ + __srai_extended3(N_START_BUFFER_PTR_SIZE_PTR, DEFAULT_INDEX, buffer, \ + size_ptr); \ + } while (0); + +/// HARNESS_START_INDEX +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The index specified by `start_index` will be used. If you need +/// multiple start harnesses compiled into the same binary, you can use this +/// macro to specify different indices, then enable them at runtime by +/// configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The size of the buffer pointed to by `size_ptr` will be saved as the +/// maximum testcase size. Each fuzzing iteration, the actual size of the +/// current testcase will be written to `*size_ptr`. +/// +/// # Arguments +/// +/// - `start_index`: The index to use for this start harness +/// - `buffer`: The pointer to the testcase buffer +/// - `size_ptr`: The pointer to the size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// size_t size; +/// HARNESS_START_INDEX(0x0001U, buffer, &size); +/// ``` +#define HARNESS_START_INDEX(start_index, buffer, size_ptr) \ + do { \ + __srai_extended3(N_START_BUFFER_PTR_SIZE_PTR, start_index, buffer, \ + size_ptr); \ + } while (0); + +/// Pseudo-hypercall number to signal the fuzzer to use the first argument to +/// the magic instruction as the pointer to the testcase buffer and the second +/// argument as the maximum size of the testcase buffer. +#define N_START_BUFFER_PTR_SIZE_VAL (0x0002U) -/// Signal the fuzzer using a specific magic value `start` to start the fuzzing -/// loop at the point this macro is called. A snapshot will be taken when the -/// macro is called, and the maximum testcase size `*size_ptr` will be saved as -/// `max_testcase_size`. Each iteration of the fuzzing loop, the fuzzer input -/// (the "testcase") will be written to `*testcase_ptr` as if running -/// `memcpy(testcase_ptr, current_testcase, max_testcase_size)`, and the actual -/// size of the current testcase will be written to -/// `*size_ptr` as if running `*size_ptr = current_testcase_size`. -#define __arch_harness_start(start, testcase_ptr, size_ptr) \ - do { \ - __srai_extended(start, testcase_ptr, size_ptr); \ - } while (0) - -/// Signal the fuzzer using the a specific magic value (`stop`) to stop and -/// reset to the beginning of the fuzzing loop with a "normal" stop status, -/// indicating no solution has occurred. -#define __arch_harness_stop(stop) \ - do { \ - __srai(stop); \ - } while (0) - -/// Signal the fuzzer using the default magic value to start the fuzzing loop at -/// the point this macro is called. A snapshot will be taken when the macro is -/// called, and the maximum testcase size `*size_ptr` will be saved as -/// `max_testcase_size`. Each iteration of the fuzzing loop, the fuzzer input -/// (the "testcase") will be written to -/// `*testcase_ptr` as if running `memcpy(testcase_ptr, current_testcase, -/// max_testcase_size)`, and the actual size of the current testcase will be -/// written to -/// `*size_ptr` as if running `*size_ptr = current_testcase_size`. -#define HARNESS_START(testcase_ptr, size_ptr) \ - do { \ - __arch_harness_start(MAGIC_START, testcase_ptr, size_ptr); \ - } while (0) - -/// Signal the fuzzer using the default magic value to stop and reset to the -/// beginning of the fuzzing loop with a "normal" stop status, indicating no -/// solution has occurred. -#define HARNESS_STOP() \ - do { \ - __arch_harness_stop(MAGIC_STOP); \ - } while (0) - -/// Signal the fuzzer using the default magic value to stop and reset to the -/// beginning of the fuzzing loop with a "solution" stop status, indicating some -/// custom error has occurred. -#define HARNESS_ASSERT() \ - do { \ - __arch_harness_stop(MAGIC_ASSERT); \ - } while (0) +/// HARNESS_START_WITH_MAXIMUM_SIZE +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The default "index" of 0 will be used. If you need multiple start +/// harnesses compiled into the same binary, you can use the +/// `HARNESS_START_WITH_MAXIMUM_SIZE_INDEX` macro to specify different indices, +/// then enable them at runtime by configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The `max_size` value will be saved as the maximum testcase size. Fuzzing +/// test cases will be truncated to this size before being written to the +/// buffer. +/// +/// # Arguments +/// +/// - `buffer`: The pointer to the testcase buffer +/// - `max_size`: The maximum size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// HARNESS_START_WITH_MAXIMUM_SIZE(buffer, 1024); +/// ``` +#define HARNESS_START_WITH_MAXIMUM_SIZE(buffer, max_size) \ + do { \ + __srai_extended3(N_START_BUFFER_PTR_SIZE_VAL, DEFAULT_INDEX, buffer, \ + max_size); \ + } while (0); + +/// HARNESS_START_WITH_MAXIMUM_SIZE_INDEX +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The index specified by `start_index` will be used. If you need +/// multiple start harnesses compiled into the same binary, you can use this +/// macro to specify different indices, then enable them at runtime by +/// configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The `max_size` value will be saved as the maximum testcase size. Fuzzing +/// test cases will be truncated to this size before being written to the +/// buffer. +/// +/// # Arguments +/// +/// - `start_index`: The index to use for this start harness +/// - `buffer`: The pointer to the testcase buffer +/// - `max_size`: The maximum size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// HARNESS_START_WITH_MAXIMUM_SIZE_INDEX(0x0001U, buffer, 1024); +/// ``` +#define HARNESS_START_WITH_MAXIMUM_SIZE_INDEX(start_index, buffer, max_size) \ + do { \ + __srai_extended3(N_START_BUFFER_PTR_SIZE_VAL, start_index, buffer, \ + max_size); \ + } while (0); + +/// Pseudo-hypercall number to signal the fuzzer to use the first argument to +/// the magic instruction as the pointer to the testcase buffer, the second +/// argument as a pointer to the size of the testcase buffer, and the third +/// argument as the maximum size of the testcase buffer. +#define N_START_BUFFER_PTR_SIZE_PTR_VAL (0x0003U) + +/// HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The default "index" of 0 will be used. If you need multiple start +/// harnesses compiled into the same binary, you can use the +/// `HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR_INDEX` macro to specify different +/// indices, then enable them at runtime by configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The address `size_ptr` will be saved. Each fuzzing iteration, the actual +/// size of the current testcase will be written to `*size_ptr`. +/// - The `max_size` value will be saved as the maximum testcase size. Fuzzing +/// test cases will be truncated to this size before being written to the +/// buffer. +/// +/// # Arguments +/// +/// - `buffer`: The pointer to the testcase buffer +/// - `size_ptr`: The pointer to the size of the testcase buffer +/// - `max_size`: The maximum size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// size_t size; +/// HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR(buffer, &size, 1024); +/// ``` +#define HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR(buffer, size_ptr, max_size) \ + do { \ + __srai_extended4(N_START_BUFFER_PTR_SIZE_PTR_VAL, DEFAULT_INDEX, buffer, \ + size_ptr, max_size); \ + } while (0); + +/// HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR_INDEX +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The index specified by `start_index` will be used. If you need +/// multiple start harnesses compiled into the same binary, you can use this +/// macro to specify different indices, then enable them at runtime by +/// configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The address `size_ptr` will be saved. Each fuzzing iteration, the actual +/// size of the current testcase will be written to `*size_ptr`. +/// - The `max_size` value will be saved as the maximum testcase size. Fuzzing +/// test cases will be truncated to this size before being written to the +/// buffer. +/// +/// # Arguments +/// +/// - `start_index`: The index to use for this start harness +/// - `buffer`: The pointer to the testcase buffer +/// - `size_ptr`: The pointer to the size of the testcase buffer +/// - `max_size`: The maximum size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// size_t size; +/// HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR_INDEX(0x0001U, buffer, &size, 1024); +/// ``` +#define HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR_INDEX(start_index, buffer, \ + size_ptr, max_size) \ + do { \ + __srai_extended4(N_START_BUFFER_PTR_SIZE_PTR_VAL, start_index, buffer, \ + size_ptr, max_size); \ + } while (0); + +/// Pseudo-hypercall number to signal the fuzzer to stop the current fuzzing +/// iteration and reset to the beginning of the fuzzing loop with a "normal" +/// stop status, indicating no solution has occurred. +#define N_STOP_NORMAL (0x0004U) + +/// HARNESS_STOP +/// +/// Signal the fuzzer to stop and reset to the beginning of the fuzzing loop +/// with a "normal" stop status, indicating no solution has occurred. The +/// default index of 0 will be used. If you need to differentiate between +/// multiple stop harnesses compiled into the same binary, you can use the +/// `HARNESS_STOP_INDEX` macro to specify different indices, then enable them at +/// runtime by configuring the fuzzer. +/// +/// # Example +/// +/// ``` +/// HARNESS_STOP(); +/// ``` +#define HARNESS_STOP() \ + do { \ + __srai_extended1(N_STOP_NORMAL, DEFAULT_INDEX); \ + } while (0); + +/// HARNESS_STOP_INDEX +/// +/// Signal the fuzzer to stop and reset to the beginning of the fuzzing loop +/// with a "normal" stop status, indicating no solution has occurred. The index +/// specified by `stop_index` will be used. If you need to differentiate between +/// multiple stop harnesses compiled into the same binary, you can use this +/// macro to specify different indices, then enable them at runtime by +/// configuring the fuzzer. +/// +/// # Arguments +/// +/// - `stop_index`: The index to use for this stop harness +/// +/// # Example +/// +/// ``` +/// HARNESS_STOP_INDEX(0x0001U); +/// ``` +#define HARNESS_STOP_INDEX(stop_index) \ + do { \ + __srai_extended1(N_STOP_NORMAL, stop_index); \ + } while (0); + +/// Pseudo-hypercall number to signal the fuzzer that a custom assertion has +/// occurred, and the fuzzer should stop the current fuzzing iteration and reset +/// to the beginning of the fuzzing loop with a "solution" stop status. +#define N_STOP_ASSERT (0x0005U) + +/// HARNESS_ASSERT +/// +/// Signal the fuzzer that a custom assertion has occurred, and the fuzzer +/// should stop the current fuzzing iteration and reset to the beginning of the +/// fuzzing loop with a "solution" stop status. The default index of 0 will be +/// used. If you need to differentiate between multiple assertion harnesses +/// compiled into the same binary, you can use the `HARNESS_ASSERT_INDEX` macro +/// to specify different indices, then enable them at runtime by configuring the +/// fuzzer. +/// +/// # Example +/// +/// ``` +/// HARNESS_ASSERT(); +/// ``` +#define HARNESS_ASSERT() \ + do { \ + __srai_extended1(N_STOP_ASSERT, DEFAULT_INDEX); \ + } while (0); + +/// HARNESS_ASSERT_INDEX +/// +/// Signal the fuzzer that a custom assertion has occurred, and the fuzzer +/// should stop the current fuzzing iteration and reset to the beginning of the +/// fuzzing loop with a "solution" stop status. The index specified by +/// `assert_index` will be used. If you need to differentiate between multiple +/// assertion harnesses compiled into the same binary, you can use this macro to +/// specify different indices, then enable them at runtime by configuring the +/// fuzzer. +/// +/// # Arguments +/// +/// - `assert_index`: The index to use for this assertion harness +/// +/// # Example +/// +/// ``` +/// HARNESS_ASSERT_INDEX(0x0001U); +/// ``` +#define HARNESS_ASSERT_INDEX(assert_index) \ + do { \ + __srai_extended1(N_STOP_ASSERT, assert_index); \ + } while (0); #endif // TSFFS_H \ No newline at end of file diff --git a/harness/tsffs-gcc-riscv64.h b/harness/tsffs-gcc-riscv64.h index f6acf1ea..68471915 100644 --- a/harness/tsffs-gcc-riscv64.h +++ b/harness/tsffs-gcc-riscv64.h @@ -2,25 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 /// Definitions and macros for compiled-in harnessing of C and C++ target -/// software for the RISC-V (64-bit) architecture -/// -/// Example: -/// ```c -/// #include "tsffs-gcc-riscv64.h" -/// -/// int main() { -/// char buf[0x10]; -/// size_t size = 0x10; -/// size_t *size_ptr = &size; -/// HARNESS_START((char *)buf, size_ptr); -/// int retval = YourSpecialDecoder(buf, *size_ptr); -/// if (!retval) { -/// HARNESS_ASSERT(); -/// } else { -/// HARNESS_STOP(); -/// } -/// } -/// ``` +/// software for the RISC-V (32-bit) architecture #ifndef TSFFS_H #define TSFFS_H @@ -32,93 +14,424 @@ #define FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION (1) #endif // FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION -/// Default magic instruction `n` value to signal the TSFFS fuzzer to start the -/// fuzzing loop -#define MAGIC_START (0x0001U) -/// Default magic instruction `n` value to signal the TSFFS fuzzer to stop and -/// reset the fuzzing loop -#define MAGIC_STOP (0x0002U) -/// Default magic instruction `n` value to signal the TSFFS fuzzer that an error -/// has occurred and the testcase input should be saved as a solution -#define MAGIC_ASSERT (0x0003U) - -/// Alternative magic numbers that can be used for start and stop events in -/// conjunction with setting the magic number for each event via the SIMICS or -/// SIMICS Python script interface -#define MAGIC_ALT_0 (0x0004U) -#define MAGIC_ALT_1 (0x0005U) -#define MAGIC_ALT_2 (0x0006U) -#define MAGIC_ALT_3 (0x0007U) -#define MAGIC_ALT_4 (0x0008U) -#define MAGIC_ALT_5 (0x0009U) -#define MAGIC_ALT_6 (0x000aU) -#define MAGIC_ALT_7 (0x000bU) +/// __srai +/// +/// Invoke the magic instruction defined by SIMICS for the RISC-V architecture +/// with a specific value of `n` +/// +/// # Arguments +/// +/// * `value` - The value of `n` to use in the magic instruction +#define __srai(value) \ + __asm__ __volatile__("srai zero, zero, %0" : : "I"(value) :) +/// __srai_extended1 +/// /// Invoke the magic instruction defined by SIMICS for the RISC-V architecture -/// (`srai zero, zero, n`) with a specific value of `n`, after setting register -/// `a0` to the value of the pointer to the testcase and register `a1` to the -/// value of the pointer to the testcase size. These registers are accessed by -/// the fuzzer and are defined per-architecture. -#define __srai_extended(value, testcase_ptr, size_ptr) \ - __asm__ __volatile__("mv a0, %0; mv a1, %1; srai zero, zero, %2" \ - : \ - : "r"(testcase_ptr), "r"(size_ptr), "I"(value) \ +/// with a specific value of `n` and a pseudo-argument in register `a0`. +/// +/// # Arguments +/// +/// * `value` - The value of `n` to use in the magic instruction +/// * `arg0` - The value to place in register `a0` +#define __srai_extended1(value, arg0) \ + __asm__ __volatile__("mv a0, %0; srai zero, zero, %1" \ + : \ + : "r"(arg0), "I"(value) \ + : "a0"); + +/// __srai_extended2 +/// +/// Invoke the magic instruction defined by SIMICS for the RISC-V architecture +/// with a specific value of `n` and pseudo-arguments in registers `a0` and +/// `a1`. +/// +/// # Arguments +/// +/// * `value` - The value of `n` to use in the magic instruction +/// * `arg0` - The value to place in register `a0` +/// * `arg1` - The value to place in register `a1` +#define __srai_extended2(value, arg0, arg1) \ + __asm__ __volatile__("mv a0, %0; mv a1, %1; srai zero, zero, %2" \ + : \ + : "r"(arg0), "r"(arg1), "I"(value) \ : "a0", "a1"); +/// __srai_extended3 +/// /// Invoke the magic instruction defined by SIMICS for the RISC-V architecture -/// (`srai zero, zero, n`) with a specific value of `n` -#define __srai(value) \ - __asm__ __volatile__("srai zero, zero, %0" : : "I"(value) :) +/// with a specific value of `n` and pseudo-arguments in registers `a0`, `a1`, +/// and `a2`. +/// +/// # Arguments +/// +/// * `value` - The value of `n` to use in the magic instruction +/// * `arg0` - The value to place in register `a0` +/// * `arg1` - The value to place in register `a1` +/// * `arg2` - The value to place in register `a2` +#define __srai_extended3(value, arg0, arg1, arg2) \ + __asm__ __volatile__("mv a0, %0; mv a1, %1; mv a2, %2; srai zero, zero, %3" \ + : \ + : "r"(arg0), "r"(arg1), "r"(arg2), "I"(value) \ + : "a0", "a1", "a2"); + +/// __srai_extended4 +/// +/// Invoke the magic instruction defined by SIMICS for the RISC-V architecture +/// with a specific value of `n` and pseudo-arguments in registers `a0`, `a1`, +/// `a2`, and `a3`. +/// +/// # Arguments +/// +/// * `value` - The value of `n` to use in the magic instruction +/// * `arg0` - The value to place in register `a0` +/// * `arg1` - The value to place in register `a1` +/// * `arg2` - The value to place in register `a2` +/// * `arg3` - The value to place in register `a3` +#define __srai_extended4(value, arg0, arg1, arg2, arg3) \ + __asm__ __volatile__( \ + "mv a0, %0; mv a1, %1; mv a2, %2; mv a3, %3; srai zero, zero, %4" \ + : \ + : "r"(arg0), "r"(arg1), "r"(arg2), "r"(arg3), "I"(value) \ + : "a0", "a1", "a2", "a3"); + +/// Magic value defined by SIMICS as the "leaf" value of a CPUID instruction +/// that is treated as a magic instruction. +#define MAGIC (0x4711U) + +/// The default index number used for magic instructions. All magic instructions +/// support multiple start and stop indices, which defaults to 0 if not +/// specified. +#define DEFAULT_INDEX (0x0000U) + +/// Pseudo-hypercall number to signal the fuzzer to use the first argument to +/// the magic instruction as the pointer to the testcase buffer and the second +/// argument as a pointer to the size of the testcase buffer. +#define N_START_BUFFER_PTR_SIZE_PTR (0x0001U) + +/// HARNESS_START +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The default "index" of 0 will be used. If you need multiple start +/// harnesses compiled into the same binary, you can use the +/// `HARNESS_START_INDEX` macro to specify different indices, then enable them +/// at runtime by configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The size of the buffer pointed to by `size_ptr` will be saved as the +/// maximum testcase size. Each fuzzing iteration, the actual size of the +/// current testcase will be written to `*size_ptr`. +/// +/// # Arguments +/// +/// - `buffer`: The pointer to the testcase buffer +/// - `size_ptr`: The pointer to the size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// size_t size; +/// HARNESS_START(buffer, &size); +/// ``` +#define HARNESS_START(buffer, size_ptr) \ + do { \ + __srai_extended3(N_START_BUFFER_PTR_SIZE_PTR, DEFAULT_INDEX, buffer, size_ptr); \ + } while (0); + +/// HARNESS_START_INDEX +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The index specified by `start_index` will be used. If you need +/// multiple start harnesses compiled into the same binary, you can use this +/// macro to specify different indices, then enable them at runtime by +/// configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The size of the buffer pointed to by `size_ptr` will be saved as the +/// maximum testcase size. Each fuzzing iteration, the actual size of the +/// current testcase will be written to `*size_ptr`. +/// +/// # Arguments +/// +/// - `start_index`: The index to use for this start harness +/// - `buffer`: The pointer to the testcase buffer +/// - `size_ptr`: The pointer to the size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// size_t size; +/// HARNESS_START_INDEX(0x0001U, buffer, &size); +/// ``` +#define HARNESS_START_INDEX(start_index, buffer, size_ptr) \ + do { \ + __srai_extended3(N_START_BUFFER_PTR_SIZE_PTR, start_index, buffer, size_ptr); \ + } while (0); + +/// Pseudo-hypercall number to signal the fuzzer to use the first argument to +/// the magic instruction as the pointer to the testcase buffer and the second +/// argument as the maximum size of the testcase buffer. +#define N_START_BUFFER_PTR_SIZE_VAL (0x0002U) -/// Signal the fuzzer using a specific magic value `start` to start the fuzzing -/// loop at the point this macro is called. A snapshot will be taken when the -/// macro is called, and the maximum testcase size `*size_ptr` will be saved as -/// `max_testcase_size`. Each iteration of the fuzzing loop, the fuzzer input -/// (the "testcase") will be written to `*testcase_ptr` as if running -/// `memcpy(testcase_ptr, current_testcase, max_testcase_size)`, and the actual -/// size of the current testcase will be written to -/// `*size_ptr` as if running `*size_ptr = current_testcase_size`. -#define __arch_harness_start(start, testcase_ptr, size_ptr) \ - do { \ - __srai_extended(start, testcase_ptr, size_ptr); \ - } while (0) - -/// Signal the fuzzer using the a specific magic value (`stop`) to stop and -/// reset to the beginning of the fuzzing loop with a "normal" stop status, -/// indicating no solution has occurred. -#define __arch_harness_stop(stop) \ - do { \ - __srai(stop); \ - } while (0) - -/// Signal the fuzzer using the default magic value to start the fuzzing loop at -/// the point this macro is called. A snapshot will be taken when the macro is -/// called, and the maximum testcase size `*size_ptr` will be saved as -/// `max_testcase_size`. Each iteration of the fuzzing loop, the fuzzer input -/// (the "testcase") will be written to -/// `*testcase_ptr` as if running `memcpy(testcase_ptr, current_testcase, -/// max_testcase_size)`, and the actual size of the current testcase will be -/// written to -/// `*size_ptr` as if running `*size_ptr = current_testcase_size`. -#define HARNESS_START(testcase_ptr, size_ptr) \ - do { \ - __arch_harness_start(MAGIC_START, testcase_ptr, size_ptr); \ - } while (0) - -/// Signal the fuzzer using the default magic value to stop and reset to the -/// beginning of the fuzzing loop with a "normal" stop status, indicating no -/// solution has occurred. -#define HARNESS_STOP() \ - do { \ - __arch_harness_stop(MAGIC_STOP); \ - } while (0) - -/// Signal the fuzzer using the default magic value to stop and reset to the -/// beginning of the fuzzing loop with a "solution" stop status, indicating some -/// custom error has occurred. -#define HARNESS_ASSERT() \ - do { \ - __arch_harness_stop(MAGIC_ASSERT); \ - } while (0) +/// HARNESS_START_WITH_MAXIMUM_SIZE +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The default "index" of 0 will be used. If you need multiple start +/// harnesses compiled into the same binary, you can use the +/// `HARNESS_START_WITH_MAXIMUM_SIZE_INDEX` macro to specify different indices, +/// then enable them at runtime by configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The `max_size` value will be saved as the maximum testcase size. Fuzzing +/// test cases will be truncated to this size before being written to the +/// buffer. +/// +/// # Arguments +/// +/// - `buffer`: The pointer to the testcase buffer +/// - `max_size`: The maximum size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// HARNESS_START_WITH_MAXIMUM_SIZE(buffer, 1024); +/// ``` +#define HARNESS_START_WITH_MAXIMUM_SIZE(buffer, max_size) \ + do { \ + __srai_extended3(N_START_BUFFER_PTR_SIZE_VAL, DEFAULT_INDEX, buffer, max_size); \ + } while (0); + +/// HARNESS_START_WITH_MAXIMUM_SIZE_INDEX +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The index specified by `start_index` will be used. If you need +/// multiple start harnesses compiled into the same binary, you can use this +/// macro to specify different indices, then enable them at runtime by +/// configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The `max_size` value will be saved as the maximum testcase size. Fuzzing +/// test cases will be truncated to this size before being written to the +/// buffer. +/// +/// # Arguments +/// +/// - `start_index`: The index to use for this start harness +/// - `buffer`: The pointer to the testcase buffer +/// - `max_size`: The maximum size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// HARNESS_START_WITH_MAXIMUM_SIZE_INDEX(0x0001U, buffer, 1024); +/// ``` +#define HARNESS_START_WITH_MAXIMUM_SIZE_INDEX(start_index, buffer, max_size) \ + do { \ + __srai_extended3(N_START_BUFFER_PTR_SIZE_VAL, start_index, buffer, max_size); \ + } while (0); + +/// Pseudo-hypercall number to signal the fuzzer to use the first argument to +/// the magic instruction as the pointer to the testcase buffer, the second +/// argument as a pointer to the size of the testcase buffer, and the third +/// argument as the maximum size of the testcase buffer. +#define N_START_BUFFER_PTR_SIZE_PTR_VAL (0x0003U) + +/// HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The default "index" of 0 will be used. If you need multiple start +/// harnesses compiled into the same binary, you can use the +/// `HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR_INDEX` macro to specify different +/// indices, then enable them at runtime by configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The address `size_ptr` will be saved. Each fuzzing iteration, the actual +/// size of the current testcase will be written to `*size_ptr`. +/// - The `max_size` value will be saved as the maximum testcase size. Fuzzing +/// test cases will be truncated to this size before being written to the +/// buffer. +/// +/// # Arguments +/// +/// - `buffer`: The pointer to the testcase buffer +/// - `size_ptr`: The pointer to the size of the testcase buffer +/// - `max_size`: The maximum size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// size_t size; +/// HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR(buffer, &size, 1024); +/// ``` +#define HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR(buffer, size_ptr, max_size) \ + do { \ + __srai_extended4(N_START_BUFFER_PTR_SIZE_PTR_VAL, DEFAULT_INDEX, buffer, size_ptr, max_size); \ + } while (0); + +/// HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR_INDEX +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The index specified by `start_index` will be used. If you need +/// multiple start harnesses compiled into the same binary, you can use this +/// macro to specify different indices, then enable them at runtime by +/// configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The address `size_ptr` will be saved. Each fuzzing iteration, the actual +/// size of the current testcase will be written to `*size_ptr`. +/// - The `max_size` value will be saved as the maximum testcase size. Fuzzing +/// test cases will be truncated to this size before being written to the +/// buffer. +/// +/// # Arguments +/// +/// - `start_index`: The index to use for this start harness +/// - `buffer`: The pointer to the testcase buffer +/// - `size_ptr`: The pointer to the size of the testcase buffer +/// - `max_size`: The maximum size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// size_t size; +/// HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR_INDEX(0x0001U, buffer, &size, 1024); +/// ``` +#define HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR_INDEX(start_index, buffer, \ + size_ptr, max_size) \ + do { \ + __srai_extended4(N_START_BUFFER_PTR_SIZE_PTR_VAL, start_index, buffer, size_ptr, max_size); \ + } while (0); + +/// Pseudo-hypercall number to signal the fuzzer to stop the current fuzzing +/// iteration and reset to the beginning of the fuzzing loop with a "normal" +/// stop status, indicating no solution has occurred. +#define N_STOP_NORMAL (0x0004U) + +/// HARNESS_STOP +/// +/// Signal the fuzzer to stop and reset to the beginning of the fuzzing loop +/// with a "normal" stop status, indicating no solution has occurred. The +/// default index of 0 will be used. If you need to differentiate between +/// multiple stop harnesses compiled into the same binary, you can use the +/// `HARNESS_STOP_INDEX` macro to specify different indices, then enable them at +/// runtime by configuring the fuzzer. +/// +/// # Example +/// +/// ``` +/// HARNESS_STOP(); +/// ``` +#define HARNESS_STOP() \ + do { \ + __srai_extended1(N_STOP_NORMAL, DEFAULT_INDEX); \ + } while (0); + +/// HARNESS_STOP_INDEX +/// +/// Signal the fuzzer to stop and reset to the beginning of the fuzzing loop +/// with a "normal" stop status, indicating no solution has occurred. The index +/// specified by `stop_index` will be used. If you need to differentiate between +/// multiple stop harnesses compiled into the same binary, you can use this +/// macro to specify different indices, then enable them at runtime by +/// configuring the fuzzer. +/// +/// # Arguments +/// +/// - `stop_index`: The index to use for this stop harness +/// +/// # Example +/// +/// ``` +/// HARNESS_STOP_INDEX(0x0001U); +/// ``` +#define HARNESS_STOP_INDEX(stop_index) \ + do { \ + __srai_extended1(N_STOP_NORMAL, stop_index); \ + } while (0); + +/// Pseudo-hypercall number to signal the fuzzer that a custom assertion has +/// occurred, and the fuzzer should stop the current fuzzing iteration and reset +/// to the beginning of the fuzzing loop with a "solution" stop status. +#define N_STOP_ASSERT (0x0005U) + +/// HARNESS_ASSERT +/// +/// Signal the fuzzer that a custom assertion has occurred, and the fuzzer +/// should stop the current fuzzing iteration and reset to the beginning of the +/// fuzzing loop with a "solution" stop status. The default index of 0 will be +/// used. If you need to differentiate between multiple assertion harnesses +/// compiled into the same binary, you can use the `HARNESS_ASSERT_INDEX` macro +/// to specify different indices, then enable them at runtime by configuring the +/// fuzzer. +/// +/// # Example +/// +/// ``` +/// HARNESS_ASSERT(); +/// ``` +#define HARNESS_ASSERT() \ + do { \ + __srai_extended1(N_STOP_ASSERT, DEFAULT_INDEX); \ + } while (0); + +/// HARNESS_ASSERT_INDEX +/// +/// Signal the fuzzer that a custom assertion has occurred, and the fuzzer +/// should stop the current fuzzing iteration and reset to the beginning of the +/// fuzzing loop with a "solution" stop status. The index specified by +/// `assert_index` will be used. If you need to differentiate between multiple +/// assertion harnesses compiled into the same binary, you can use this macro to +/// specify different indices, then enable them at runtime by configuring the +/// fuzzer. +/// +/// # Arguments +/// +/// - `assert_index`: The index to use for this assertion harness +/// +/// # Example +/// +/// ``` +/// HARNESS_ASSERT_INDEX(0x0001U); +/// ``` +#define HARNESS_ASSERT_INDEX(assert_index) \ + do { \ + __srai_extended1(N_STOP_ASSERT, assert_index); \ + } while (0); #endif // TSFFS_H \ No newline at end of file diff --git a/harness/tsffs-gcc-x86.h b/harness/tsffs-gcc-x86.h index 6f91792c..81e8620b 100644 --- a/harness/tsffs-gcc-x86.h +++ b/harness/tsffs-gcc-x86.h @@ -2,25 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 /// Definitions and macros for compiled-in harnessing of C and C++ target -/// software for the RISC-V (64-bit) architecture -/// -/// Example: -/// ```c -/// #include "tsffs-gcc-x86.h" -/// -/// int main() { -/// char buf[0x10]; -/// size_t size = 0x10; -/// size_t *size_ptr = &size; -/// HARNESS_START((char *)buf, size_ptr); -/// int retval = YourSpecialDecoder(buf, *size_ptr); -/// if (!retval) { -/// HARNESS_ASSERT(); -/// } else { -/// HARNESS_STOP(); -/// } -/// } -/// ``` +/// software for the X86 architecture. #ifndef TSFFS_H #define TSFFS_H @@ -32,103 +14,464 @@ #define FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION (1) #endif // FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION +/// __cpuid +/// +/// Invoke the CPUID instruction with a specific value `value` in register +/// `rax`. +/// +/// # Arguments +/// +/// - `value`: The value to load into the `rax` register before invoking the +/// CPUID instruction +#define __cpuid(value) \ + unsigned int _a __attribute__((unused)) = 0; \ + unsigned int _b __attribute__((unused)) = 0; \ + unsigned int _c __attribute__((unused)) = 0; \ + unsigned int _d __attribute__((unused)) = 0; \ + __asm__ __volatile__("cpuid\n\t" \ + : "=a"(_a), "=b"(_b), "=c"(_c), "=d"(_d) \ + : "a"(value)); + +/// __cpuid_extended1 +/// +/// Invoke the CPUID instruction with a specific value `value` in register +/// `rax` and a pseudo-argument in register `rdi`. +/// +/// # Arguments +/// +/// - `value`: The value to load into the `rax` register before invoking the +/// CPUID instruction +/// - `arg0`: The value to load into the `rdi` register before invoking the +/// CPUID instruction +#define __cpuid_extended1(value, arg0) \ + unsigned int _a __attribute__((unused)) = 0; \ + unsigned int _b __attribute__((unused)) = 0; \ + unsigned int _c __attribute__((unused)) = 0; \ + unsigned int _d __attribute__((unused)) = 0; \ + __asm__ __volatile__("cpuid\n\t" \ + : "=a"(_a), "=b"(_b), "=c"(_c), "=d"(_d) \ + : "a"(value), "D"(arg0)); + +/// __cpuid_extended2 +/// +/// Invoke the CPUID instruction with a specific value `value` in register +/// `rax` and a pseudo-arguments in registers `rdi` and `rsi`. +/// +/// # Arguments +/// +/// - `value`: The value to load into the `rax` register before invoking the +/// CPUID instruction +/// - `arg0`: The value to load into the `rdi` register before invoking the +/// CPUID instruction +/// - `arg1`: The value to load into the `rsi` register before invoking the +/// CPUID instruction +#define __cpuid_extended2(value, arg0, arg1) \ + unsigned int _a __attribute__((unused)) = 0; \ + unsigned int _b __attribute__((unused)) = 0; \ + unsigned int _c __attribute__((unused)) = 0; \ + unsigned int _d __attribute__((unused)) = 0; \ + __asm__ __volatile__("cpuid\n\t" \ + : "=a"(_a), "=b"(_b), "=c"(_c), "=d"(_d) \ + : "a"(value), "D"(arg0), "S"(arg1)); + +/// __cpuid_extended3 +/// +/// Invoke the CPUID instruction with a specific value `value` in register +/// `rax` and a pseudo-arguments in registers `rdi`, `rsi`, and `rdx`. +/// +/// # Arguments +/// +/// - `value`: The value to load into the `rax` register before invoking the +/// CPUID instruction +/// - `arg0`: The value to load into the `rdi` register before invoking the +/// CPUID instruction +/// - `arg1`: The value to load into the `rsi` register before invoking the +/// CPUID instruction +/// - `arg2`: The value to load into the `rdx` register before invoking the +/// CPUID instruction +#define __cpuid_extended3(value, arg0, arg1, arg2) \ + unsigned int _a __attribute__((unused)) = 0; \ + unsigned int _b __attribute__((unused)) = 0; \ + unsigned int _c __attribute__((unused)) = 0; \ + unsigned int _d __attribute__((unused)) = 0; \ + __asm__ __volatile__("cpuid\n\t" \ + : "=a"(_a), "=b"(_b), "=c"(_c), "=d"(_d) \ + : "a"(value), "D"(arg0), "S"(arg1), "d"(arg2)); + +/// __cpuid_extended4 +/// +/// Invoke the CPUID instruction with a specific value `value` in register +/// `rax` and a pseudo-arguments in registers `rdi`, `rsi`, `rdx`, and `rcx`. +/// +/// # Arguments +/// +/// - `value`: The value to load into the `rax` register before invoking the +/// CPUID instruction +/// - `arg0`: The value to load into the `rdi` register before invoking the +/// CPUID instruction +/// - `arg1`: The value to load into the `rsi` register before invoking the +/// CPUID instruction +/// - `arg2`: The value to load into the `rdx` register before invoking the +/// CPUID instruction +/// - `arg3`: The value to load into the `rcx` register before invoking the +/// CPUID instruction +#define __cpuid_extended4(value, arg0, arg1, arg2, arg3) \ + unsigned int _a __attribute__((unused)) = 0; \ + unsigned int _b __attribute__((unused)) = 0; \ + unsigned int _c __attribute__((unused)) = 0; \ + unsigned int _d __attribute__((unused)) = 0; \ + __asm__ __volatile__("cpuid\n\t" \ + : "=a"(_a), "=b"(_b), "=c"(_c), "=d"(_d) \ + : "a"(value), "D"(arg0), "S"(arg1), "d"(arg2), \ + "c"(arg3)); + /// Magic value defined by SIMICS as the "leaf" value of a CPUID instruction /// that is treated as a magic instruction. #define MAGIC (0x4711U) -/// Default magic instruction `n` value to signal the TSFFS fuzzer to start the -/// fuzzing loop -#define MAGIC_START (0x0001U) -/// Default magic instruction `n` value to signal the TSFFS fuzzer to stop and -/// reset the fuzzing loop -#define MAGIC_STOP (0x0002U) -/// Default magic instruction `n` value to signal the TSFFS fuzzer that an error -/// has occurred and the testcase input should be saved as a solution -#define MAGIC_ASSERT (0x0003U) - -/// Alternative magic numbers that can be used for start and stop events in -/// conjunction with setting the magic number for each event via the SIMICS or -/// SIMICS Python script interface -#define MAGIC_ALT_0 (0x0004U) -#define MAGIC_ALT_1 (0x0005U) -#define MAGIC_ALT_2 (0x0006U) -#define MAGIC_ALT_3 (0x0007U) -#define MAGIC_ALT_4 (0x0008U) -#define MAGIC_ALT_5 (0x0009U) -#define MAGIC_ALT_6 (0x000aU) -#define MAGIC_ALT_7 (0x000bU) - -/// Invoke the magic instruction defined by SIMICS for the x86 architecture -/// (`cpuid`) with a specific value of `n`, after setting register `edi` to the -/// value of the pointer to the testcase and register `esi` to the value of the -/// pointer to the testcase size. These registers are accessed by the fuzzer and -/// are defined per-architecture. -#define __cpuid_extended2(value, inout_ptr_0, inout_ptr_1) \ - unsigned int _a __attribute__((unused)) = 0; \ - __asm__ __volatile__("cpuid" \ - : "=a"(_a) \ - : "a"(value), "D"(inout_ptr_0), "S"(inout_ptr_1) \ - : "ebx", "ecx", "edx"); - -/// Invoke the magic instruction defined by SIMICS for the x86 architecture -/// (`cpuid`) with a specific value of `n` -#define __cpuid(value) \ - unsigned int _a __attribute__((unused)) = 0; \ - __asm__ __volatile__("cpuid\n\t" \ - : "=a"(_a) \ - : "a"(value) \ - : "ebx", "ecx", "edx") - -/// Signal the fuzzer using a specific magic value `start` to start the fuzzing -/// loop at the point this macro is called. A snapshot will be taken when the -/// macro is called, and the maximum testcase size `*size_ptr` will be saved as -/// `max_testcase_size`. Each iteration of the fuzzing loop, the fuzzer input -/// (the "testcase") will be written to `*testcase_ptr` as if running -/// `memcpy(testcase_ptr, current_testcase, max_testcase_size)`, and the actual -/// size of the current testcase will be written to -/// `*size_ptr` as if running `*size_ptr = current_testcase_size`. -#define __arch_harness_start(start, testcase_ptr, size_ptr) \ - do { \ - unsigned int magic = (start << 0x10U) | MAGIC; \ - __cpuid_extended2(magic, testcase_ptr, size_ptr); \ - } while (0) - -/// Signal the fuzzer using the a specific magic value (`stop`) to stop and -/// reset to the beginning of the fuzzing loop with a "normal" stop status, -/// indicating no solution has occurred. -#define __arch_harness_stop(stop) \ - do { \ - unsigned int magic = (stop << 0x10U) | MAGIC; \ - __cpuid(magic); \ - } while (0) - -/// Signal the fuzzer using the default magic value to start the fuzzing loop at -/// the point this macro is called. A snapshot will be taken when the macro is -/// called, and the maximum testcase size `*size_ptr` will be saved as -/// `max_testcase_size`. Each iteration of the fuzzing loop, the fuzzer input -/// (the "testcase") will be written to -/// `*testcase_ptr` as if running `memcpy(testcase_ptr, current_testcase, -/// max_testcase_size)`, and the actual size of the current testcase will be -/// written to -/// `*size_ptr` as if running `*size_ptr = current_testcase_size`. -#define HARNESS_START(testcase_ptr, size_ptr) \ - do { \ - __arch_harness_start(MAGIC_START, testcase_ptr, size_ptr); \ - } while (0) - -/// Signal the fuzzer using the default magic value to stop and reset to the -/// beginning of the fuzzing loop with a "normal" stop status, indicating no -/// solution has occurred. -#define HARNESS_STOP() \ - do { \ - __arch_harness_stop(MAGIC_STOP); \ - } while (0) - -/// Signal the fuzzer using the default magic value to stop and reset to the -/// beginning of the fuzzing loop with a "solution" stop status, indicating some -/// custom error has occurred. -#define HARNESS_ASSERT() \ - do { \ - __arch_harness_stop(MAGIC_ASSERT); \ - } while (0) + +/// The default index number used for magic instructions. All magic instructions +/// support multiple start and stop indices, which defaults to 0 if not +/// specified. +#define DEFAULT_INDEX (0x0000U) + +/// Pseudo-hypercall number to signal the fuzzer to use the first argument to +/// the magic instruction as the pointer to the testcase buffer and the second +/// argument as a pointer to the size of the testcase buffer. +#define N_START_BUFFER_PTR_SIZE_PTR (0x0001U) + +/// HARNESS_START +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The default "index" of 0 will be used. If you need multiple start +/// harnesses compiled into the same binary, you can use the +/// `HARNESS_START_INDEX` macro to specify different indices, then enable them +/// at runtime by configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The size of the buffer pointed to by `size_ptr` will be saved as the +/// maximum testcase size. Each fuzzing iteration, the actual size of the +/// current testcase will be written to `*size_ptr`. +/// +/// # Arguments +/// +/// - `buffer`: The pointer to the testcase buffer +/// - `size_ptr`: The pointer to the size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// size_t size; +/// HARNESS_START(buffer, &size); +/// ``` +#define HARNESS_START(buffer, size_ptr) \ + do { \ + unsigned int value = (N_START_BUFFER_PTR_SIZE_PTR << 0x10U) | MAGIC; \ + __cpuid_extended3(value, DEFAULT_INDEX, buffer, size_ptr); \ + } while (0); + +/// HARNESS_START_INDEX +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The index specified by `start_index` will be used. If you need +/// multiple start harnesses compiled into the same binary, you can use this +/// macro to specify different indices, then enable them at runtime by +/// configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The size of the buffer pointed to by `size_ptr` will be saved as the +/// maximum testcase size. Each fuzzing iteration, the actual size of the +/// current testcase will be written to `*size_ptr`. +/// +/// # Arguments +/// +/// - `start_index`: The index to use for this start harness +/// - `buffer`: The pointer to the testcase buffer +/// - `size_ptr`: The pointer to the size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// size_t size; +/// HARNESS_START_INDEX(0x0001U, buffer, &size); +/// ``` +#define HARNESS_START_INDEX(start_index, buffer, size_ptr) \ + do { \ + unsigned int value = (N_START_BUFFER_PTR_SIZE_PTR << 0x10U) | MAGIC; \ + __cpuid_extended3(value, start_index, buffer, size_ptr); \ + } while (0); + +/// Pseudo-hypercall number to signal the fuzzer to use the first argument to +/// the magic instruction as the pointer to the testcase buffer and the second +/// argument as the maximum size of the testcase buffer. +#define N_START_BUFFER_PTR_SIZE_VAL (0x0002U) + +/// HARNESS_START_WITH_MAXIMUM_SIZE +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The default "index" of 0 will be used. If you need multiple start +/// harnesses compiled into the same binary, you can use the +/// `HARNESS_START_WITH_MAXIMUM_SIZE_INDEX` macro to specify different indices, +/// then enable them at runtime by configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The `max_size` value will be saved as the maximum testcase size. Fuzzing +/// test cases will be truncated to this size before being written to the +/// buffer. +/// +/// # Arguments +/// +/// - `buffer`: The pointer to the testcase buffer +/// - `max_size`: The maximum size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// HARNESS_START_WITH_MAXIMUM_SIZE(buffer, 1024); +/// ``` +#define HARNESS_START_WITH_MAXIMUM_SIZE(buffer, max_size) \ + do { \ + unsigned int value = (N_START_BUFFER_PTR_SIZE_VAL << 0x10U) | MAGIC; \ + __cpuid_extended3(value, DEFAULT_INDEX, buffer, max_size); \ + } while (0); + +/// HARNESS_START_WITH_MAXIMUM_SIZE_INDEX +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The index specified by `start_index` will be used. If you need +/// multiple start harnesses compiled into the same binary, you can use this +/// macro to specify different indices, then enable them at runtime by +/// configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The `max_size` value will be saved as the maximum testcase size. Fuzzing +/// test cases will be truncated to this size before being written to the +/// buffer. +/// +/// # Arguments +/// +/// - `start_index`: The index to use for this start harness +/// - `buffer`: The pointer to the testcase buffer +/// - `max_size`: The maximum size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// HARNESS_START_WITH_MAXIMUM_SIZE_INDEX(0x0001U, buffer, 1024); +/// ``` +#define HARNESS_START_WITH_MAXIMUM_SIZE_INDEX(start_index, buffer, max_size) \ + do { \ + unsigned int value = (N_START_BUFFER_PTR_SIZE_VAL << 0x10U) | MAGIC; \ + __cpuid_extended3(value, start_index, buffer, max_size); \ + } while (0); + +/// Pseudo-hypercall number to signal the fuzzer to use the first argument to +/// the magic instruction as the pointer to the testcase buffer, the second +/// argument as a pointer to the size of the testcase buffer, and the third +/// argument as the maximum size of the testcase buffer. +#define N_START_BUFFER_PTR_SIZE_PTR_VAL (0x0003U) + +/// HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The default "index" of 0 will be used. If you need multiple start +/// harnesses compiled into the same binary, you can use the +/// `HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR_INDEX` macro to specify different +/// indices, then enable them at runtime by configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The address `size_ptr` will be saved. Each fuzzing iteration, the actual +/// size of the current testcase will be written to `*size_ptr`. +/// - The `max_size` value will be saved as the maximum testcase size. Fuzzing +/// test cases will be truncated to this size before being written to the +/// buffer. +/// +/// # Arguments +/// +/// - `buffer`: The pointer to the testcase buffer +/// - `size_ptr`: The pointer to the size of the testcase buffer +/// - `max_size`: The maximum size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// size_t size; +/// HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR(buffer, &size, 1024); +/// ``` +#define HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR(buffer, size_ptr, max_size) \ + do { \ + unsigned int value = (N_START_BUFFER_PTR_SIZE_PTR_VAL << 0x10U) | MAGIC; \ + __cpuid_extended4(value, DEFAULT_INDEX, buffer, size_ptr, max_size); \ + } while (0); + +/// HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR_INDEX +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The index specified by `start_index` will be used. If you need +/// multiple start harnesses compiled into the same binary, you can use this +/// macro to specify different indices, then enable them at runtime by +/// configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The address `size_ptr` will be saved. Each fuzzing iteration, the actual +/// size of the current testcase will be written to `*size_ptr`. +/// - The `max_size` value will be saved as the maximum testcase size. Fuzzing +/// test cases will be truncated to this size before being written to the +/// buffer. +/// +/// # Arguments +/// +/// - `start_index`: The index to use for this start harness +/// - `buffer`: The pointer to the testcase buffer +/// - `size_ptr`: The pointer to the size of the testcase buffer +/// - `max_size`: The maximum size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// size_t size; +/// HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR_INDEX(0x0001U, buffer, &size, 1024); +/// ``` +#define HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR_INDEX(start_index, buffer, \ + size_ptr, max_size) \ + do { \ + unsigned int value = (N_START_BUFFER_PTR_SIZE_PTR_VAL << 0x10U) | MAGIC; \ + __cpuid_extended4(value, start_index, buffer, size_ptr, max_size); \ + } while (0); + +/// Pseudo-hypercall number to signal the fuzzer to stop the current fuzzing +/// iteration and reset to the beginning of the fuzzing loop with a "normal" +/// stop status, indicating no solution has occurred. +#define N_STOP_NORMAL (0x0004U) + +/// HARNESS_STOP +/// +/// Signal the fuzzer to stop and reset to the beginning of the fuzzing loop +/// with a "normal" stop status, indicating no solution has occurred. The +/// default index of 0 will be used. If you need to differentiate between +/// multiple stop harnesses compiled into the same binary, you can use the +/// `HARNESS_STOP_INDEX` macro to specify different indices, then enable them at +/// runtime by configuring the fuzzer. +/// +/// # Example +/// +/// ``` +/// HARNESS_STOP(); +/// ``` +#define HARNESS_STOP() \ + do { \ + unsigned int value = (N_STOP_NORMAL << 0x10U) | MAGIC; \ + __cpuid_extended1(value, DEFAULT_INDEX); \ + } while (0); + +/// HARNESS_STOP_INDEX +/// +/// Signal the fuzzer to stop and reset to the beginning of the fuzzing loop +/// with a "normal" stop status, indicating no solution has occurred. The index +/// specified by `stop_index` will be used. If you need to differentiate between +/// multiple stop harnesses compiled into the same binary, you can use this +/// macro to specify different indices, then enable them at runtime by +/// configuring the fuzzer. +/// +/// # Arguments +/// +/// - `stop_index`: The index to use for this stop harness +/// +/// # Example +/// +/// ``` +/// HARNESS_STOP_INDEX(0x0001U); +/// ``` +#define HARNESS_STOP_INDEX(stop_index) \ + do { \ + unsigned int value = (N_STOP_NORMAL << 0x10U) | MAGIC; \ + __cpuid_extended1(value, stop_index); \ + } while (0); + +/// Pseudo-hypercall number to signal the fuzzer that a custom assertion has +/// occurred, and the fuzzer should stop the current fuzzing iteration and reset +/// to the beginning of the fuzzing loop with a "solution" stop status. +#define N_STOP_ASSERT (0x0005U) + +/// HARNESS_ASSERT +/// +/// Signal the fuzzer that a custom assertion has occurred, and the fuzzer +/// should stop the current fuzzing iteration and reset to the beginning of the +/// fuzzing loop with a "solution" stop status. The default index of 0 will be +/// used. If you need to differentiate between multiple assertion harnesses +/// compiled into the same binary, you can use the `HARNESS_ASSERT_INDEX` macro +/// to specify different indices, then enable them at runtime by configuring the +/// fuzzer. +/// +/// # Example +/// +/// ``` +/// HARNESS_ASSERT(); +/// ``` +#define HARNESS_ASSERT() \ + do { \ + unsigned int value = (N_STOP_ASSERT << 0x10U) | MAGIC; \ + __cpuid_extended1(value, DEFAULT_INDEX); \ + } while (0); + +/// HARNESS_ASSERT_INDEX +/// +/// Signal the fuzzer that a custom assertion has occurred, and the fuzzer +/// should stop the current fuzzing iteration and reset to the beginning of the +/// fuzzing loop with a "solution" stop status. The index specified by +/// `assert_index` will be used. If you need to differentiate between multiple +/// assertion harnesses compiled into the same binary, you can use this macro to +/// specify different indices, then enable them at runtime by configuring the +/// fuzzer. +/// +/// # Arguments +/// +/// - `assert_index`: The index to use for this assertion harness +/// +/// # Example +/// +/// ``` +/// HARNESS_ASSERT_INDEX(0x0001U); +/// ``` +#define HARNESS_ASSERT_INDEX(assert_index) \ + do { \ + unsigned int value = (N_STOP_ASSERT << 0x10U) | MAGIC; \ + __cpuid_extended1(value, assert_index); \ + } while (0); #endif // TSFFS_H \ No newline at end of file diff --git a/harness/tsffs-gcc-x86_64.h b/harness/tsffs-gcc-x86_64.h index 8de28fa1..8171f702 100644 --- a/harness/tsffs-gcc-x86_64.h +++ b/harness/tsffs-gcc-x86_64.h @@ -2,25 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 /// Definitions and macros for compiled-in harnessing of C and C++ target -/// software for the RISC-V (64-bit) architecture -/// -/// Example: -/// ```c -/// #include "tsffs-gcc-x86_64.h" -/// -/// int main() { -/// char buf[0x10]; -/// size_t size = 0x10; -/// size_t *size_ptr = &size; -/// HARNESS_START((char *)buf, size_ptr); -/// int retval = YourSpecialDecoder(buf, *size_ptr); -/// if (!retval) { -/// HARNESS_ASSERT(); -/// } else { -/// HARNESS_STOP(); -/// } -/// } -/// ``` +/// software for the x86_64 architecture. #ifndef TSFFS_H #define TSFFS_H @@ -32,103 +14,464 @@ #define FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION (1) #endif // FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION +/// __cpuid +/// +/// Invoke the CPUID instruction with a specific value `value` in register +/// `rax`. +/// +/// # Arguments +/// +/// - `value`: The value to load into the `rax` register before invoking the +/// CPUID instruction +#define __cpuid(value) \ + unsigned int _a __attribute__((unused)) = 0; \ + unsigned int _b __attribute__((unused)) = 0; \ + unsigned int _c __attribute__((unused)) = 0; \ + unsigned int _d __attribute__((unused)) = 0; \ + __asm__ __volatile__("cpuid\n\t" \ + : "=a"(_a), "=b"(_b), "=c"(_c), "=d"(_d) \ + : "a"(value)); + +/// __cpuid_extended1 +/// +/// Invoke the CPUID instruction with a specific value `value` in register +/// `rax` and a pseudo-argument in register `rdi`. +/// +/// # Arguments +/// +/// - `value`: The value to load into the `rax` register before invoking the +/// CPUID instruction +/// - `arg0`: The value to load into the `rdi` register before invoking the +/// CPUID instruction +#define __cpuid_extended1(value, arg0) \ + unsigned int _a __attribute__((unused)) = 0; \ + unsigned int _b __attribute__((unused)) = 0; \ + unsigned int _c __attribute__((unused)) = 0; \ + unsigned int _d __attribute__((unused)) = 0; \ + __asm__ __volatile__("cpuid\n\t" \ + : "=a"(_a), "=b"(_b), "=c"(_c), "=d"(_d) \ + : "a"(value), "D"(arg0)); + +/// __cpuid_extended2 +/// +/// Invoke the CPUID instruction with a specific value `value` in register +/// `rax` and a pseudo-arguments in registers `rdi` and `rsi`. +/// +/// # Arguments +/// +/// - `value`: The value to load into the `rax` register before invoking the +/// CPUID instruction +/// - `arg0`: The value to load into the `rdi` register before invoking the +/// CPUID instruction +/// - `arg1`: The value to load into the `rsi` register before invoking the +/// CPUID instruction +#define __cpuid_extended2(value, arg0, arg1) \ + unsigned int _a __attribute__((unused)) = 0; \ + unsigned int _b __attribute__((unused)) = 0; \ + unsigned int _c __attribute__((unused)) = 0; \ + unsigned int _d __attribute__((unused)) = 0; \ + __asm__ __volatile__("cpuid\n\t" \ + : "=a"(_a), "=b"(_b), "=c"(_c), "=d"(_d) \ + : "a"(value), "D"(arg0), "S"(arg1)); + +/// __cpuid_extended3 +/// +/// Invoke the CPUID instruction with a specific value `value` in register +/// `rax` and a pseudo-arguments in registers `rdi`, `rsi`, and `rdx`. +/// +/// # Arguments +/// +/// - `value`: The value to load into the `rax` register before invoking the +/// CPUID instruction +/// - `arg0`: The value to load into the `rdi` register before invoking the +/// CPUID instruction +/// - `arg1`: The value to load into the `rsi` register before invoking the +/// CPUID instruction +/// - `arg2`: The value to load into the `rdx` register before invoking the +/// CPUID instruction +#define __cpuid_extended3(value, arg0, arg1, arg2) \ + unsigned int _a __attribute__((unused)) = 0; \ + unsigned int _b __attribute__((unused)) = 0; \ + unsigned int _c __attribute__((unused)) = 0; \ + unsigned int _d __attribute__((unused)) = 0; \ + __asm__ __volatile__("cpuid\n\t" \ + : "=a"(_a), "=b"(_b), "=c"(_c), "=d"(_d) \ + : "a"(value), "D"(arg0), "S"(arg1), "d"(arg2)); + +/// __cpuid_extended4 +/// +/// Invoke the CPUID instruction with a specific value `value` in register +/// `rax` and a pseudo-arguments in registers `rdi`, `rsi`, `rdx`, and `rcx`. +/// +/// # Arguments +/// +/// - `value`: The value to load into the `rax` register before invoking the +/// CPUID instruction +/// - `arg0`: The value to load into the `rdi` register before invoking the +/// CPUID instruction +/// - `arg1`: The value to load into the `rsi` register before invoking the +/// CPUID instruction +/// - `arg2`: The value to load into the `rdx` register before invoking the +/// CPUID instruction +/// - `arg3`: The value to load into the `rcx` register before invoking the +/// CPUID instruction +#define __cpuid_extended4(value, arg0, arg1, arg2, arg3) \ + unsigned int _a __attribute__((unused)) = 0; \ + unsigned int _b __attribute__((unused)) = 0; \ + unsigned int _c __attribute__((unused)) = 0; \ + unsigned int _d __attribute__((unused)) = 0; \ + __asm__ __volatile__("cpuid\n\t" \ + : "=a"(_a), "=b"(_b), "=c"(_c), "=d"(_d) \ + : "a"(value), "D"(arg0), "S"(arg1), "d"(arg2), \ + "c"(arg3)); + /// Magic value defined by SIMICS as the "leaf" value of a CPUID instruction /// that is treated as a magic instruction. #define MAGIC (0x4711U) -/// Default magic instruction `n` value to signal the TSFFS fuzzer to start the -/// fuzzing loop -#define MAGIC_START (0x0001U) -/// Default magic instruction `n` value to signal the TSFFS fuzzer to stop and -/// reset the fuzzing loop -#define MAGIC_STOP (0x0002U) -/// Default magic instruction `n` value to signal the TSFFS fuzzer that an error -/// has occurred and the testcase input should be saved as a solution -#define MAGIC_ASSERT (0x0003U) - -/// Alternative magic numbers that can be used for start and stop events in -/// conjunction with setting the magic number for each event via the SIMICS or -/// SIMICS Python script interface -#define MAGIC_ALT_0 (0x0004U) -#define MAGIC_ALT_1 (0x0005U) -#define MAGIC_ALT_2 (0x0006U) -#define MAGIC_ALT_3 (0x0007U) -#define MAGIC_ALT_4 (0x0008U) -#define MAGIC_ALT_5 (0x0009U) -#define MAGIC_ALT_6 (0x000aU) -#define MAGIC_ALT_7 (0x000bU) - -/// Invoke the magic instruction defined by SIMICS for the x86-64 architecture -/// (`cpuid`) with a specific value of `n`, after setting register `rdi` to the -/// value of the pointer to the testcase and register `rsi` to the value of the -/// pointer to the testcase size. These registers are accessed by the fuzzer and -/// are defined per-architecture. -#define __cpuid_extended2(value, inout_ptr_0, inout_ptr_1) \ - unsigned int _a __attribute__((unused)) = 0; \ - __asm__ __volatile__("cpuid" \ - : "=a"(_a) \ - : "a"(value), "D"(inout_ptr_0), "S"(inout_ptr_1) \ - : "rbx", "rcx", "rdx"); - -/// Invoke the magic instruction defined by SIMICS for the x86-64 architecture -/// (`cpuid`) with a specific value of `n` -#define __cpuid(value) \ - unsigned int _a __attribute__((unused)) = 0; \ - __asm__ __volatile__("cpuid\n\t" \ - : "=a"(_a) \ - : "a"(value) \ - : "rbx", "rcx", "rdx") - -/// Signal the fuzzer using a specific magic value `start` to start the fuzzing -/// loop at the point this macro is called. A snapshot will be taken when the -/// macro is called, and the maximum testcase size `*size_ptr` will be saved as -/// `max_testcase_size`. Each iteration of the fuzzing loop, the fuzzer input -/// (the "testcase") will be written to `*testcase_ptr` as if running -/// `memcpy(testcase_ptr, current_testcase, max_testcase_size)`, and the actual -/// size of the current testcase will be written to -/// `*size_ptr` as if running `*size_ptr = current_testcase_size`. -#define __arch_harness_start(start, testcase_ptr, size_ptr) \ - do { \ - unsigned int magic = (start << 0x10U) | MAGIC; \ - __cpuid_extended2(magic, testcase_ptr, size_ptr); \ - } while (0) - -/// Signal the fuzzer using the a specific magic value (`stop`) to stop and -/// reset to the beginning of the fuzzing loop with a "normal" stop status, -/// indicating no solution has occurred. -#define __arch_harness_stop(stop) \ - do { \ - unsigned int magic = (stop << 0x10U) | MAGIC; \ - __cpuid(magic); \ - } while (0) - -/// Signal the fuzzer using the default magic value to start the fuzzing loop at -/// the point this macro is called. A snapshot will be taken when the macro is -/// called, and the maximum testcase size `*size_ptr` will be saved as -/// `max_testcase_size`. Each iteration of the fuzzing loop, the fuzzer input -/// (the "testcase") will be written to -/// `*testcase_ptr` as if running `memcpy(testcase_ptr, current_testcase, -/// max_testcase_size)`, and the actual size of the current testcase will be -/// written to -/// `*size_ptr` as if running `*size_ptr = current_testcase_size`. -#define HARNESS_START(testcase_ptr, size_ptr) \ - do { \ - __arch_harness_start(MAGIC_START, testcase_ptr, size_ptr); \ - } while (0) - -/// Signal the fuzzer using the default magic value to stop and reset to the -/// beginning of the fuzzing loop with a "normal" stop status, indicating no -/// solution has occurred. -#define HARNESS_STOP() \ - do { \ - __arch_harness_stop(MAGIC_STOP); \ - } while (0) - -/// Signal the fuzzer using the default magic value to stop and reset to the -/// beginning of the fuzzing loop with a "solution" stop status, indicating some -/// custom error has occurred. -#define HARNESS_ASSERT() \ - do { \ - __arch_harness_stop(MAGIC_ASSERT); \ - } while (0) + +/// The default index number used for magic instructions. All magic instructions +/// support multiple start and stop indices, which defaults to 0 if not +/// specified. +#define DEFAULT_INDEX (0x0000U) + +/// Pseudo-hypercall number to signal the fuzzer to use the first argument to +/// the magic instruction as the pointer to the testcase buffer and the second +/// argument as a pointer to the size of the testcase buffer. +#define N_START_BUFFER_PTR_SIZE_PTR (0x0001U) + +/// HARNESS_START +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The default "index" of 0 will be used. If you need multiple start +/// harnesses compiled into the same binary, you can use the +/// `HARNESS_START_INDEX` macro to specify different indices, then enable them +/// at runtime by configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The size of the buffer pointed to by `size_ptr` will be saved as the +/// maximum testcase size. Each fuzzing iteration, the actual size of the +/// current testcase will be written to `*size_ptr`. +/// +/// # Arguments +/// +/// - `buffer`: The pointer to the testcase buffer +/// - `size_ptr`: The pointer to the size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// size_t size; +/// HARNESS_START(buffer, &size); +/// ``` +#define HARNESS_START(buffer, size_ptr) \ + do { \ + unsigned int value = (N_START_BUFFER_PTR_SIZE_PTR << 0x10U) | MAGIC; \ + __cpuid_extended3(value, DEFAULT_INDEX, buffer, size_ptr); \ + } while (0); + +/// HARNESS_START_INDEX +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The index specified by `start_index` will be used. If you need +/// multiple start harnesses compiled into the same binary, you can use this +/// macro to specify different indices, then enable them at runtime by +/// configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The size of the buffer pointed to by `size_ptr` will be saved as the +/// maximum testcase size. Each fuzzing iteration, the actual size of the +/// current testcase will be written to `*size_ptr`. +/// +/// # Arguments +/// +/// - `start_index`: The index to use for this start harness +/// - `buffer`: The pointer to the testcase buffer +/// - `size_ptr`: The pointer to the size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// size_t size; +/// HARNESS_START_INDEX(0x0001U, buffer, &size); +/// ``` +#define HARNESS_START_INDEX(start_index, buffer, size_ptr) \ + do { \ + unsigned int value = (N_START_BUFFER_PTR_SIZE_PTR << 0x10U) | MAGIC; \ + __cpuid_extended3(value, start_index, buffer, size_ptr); \ + } while (0); + +/// Pseudo-hypercall number to signal the fuzzer to use the first argument to +/// the magic instruction as the pointer to the testcase buffer and the second +/// argument as the maximum size of the testcase buffer. +#define N_START_BUFFER_PTR_SIZE_VAL (0x0002U) + +/// HARNESS_START_WITH_MAXIMUM_SIZE +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The default "index" of 0 will be used. If you need multiple start +/// harnesses compiled into the same binary, you can use the +/// `HARNESS_START_WITH_MAXIMUM_SIZE_INDEX` macro to specify different indices, +/// then enable them at runtime by configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The `max_size` value will be saved as the maximum testcase size. Fuzzing +/// test cases will be truncated to this size before being written to the +/// buffer. +/// +/// # Arguments +/// +/// - `buffer`: The pointer to the testcase buffer +/// - `max_size`: The maximum size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// HARNESS_START_WITH_MAXIMUM_SIZE(buffer, 1024); +/// ``` +#define HARNESS_START_WITH_MAXIMUM_SIZE(buffer, max_size) \ + do { \ + unsigned int value = (N_START_BUFFER_PTR_SIZE_VAL << 0x10U) | MAGIC; \ + __cpuid_extended3(value, DEFAULT_INDEX, buffer, max_size); \ + } while (0); + +/// HARNESS_START_WITH_MAXIMUM_SIZE_INDEX +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The index specified by `start_index` will be used. If you need +/// multiple start harnesses compiled into the same binary, you can use this +/// macro to specify different indices, then enable them at runtime by +/// configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The `max_size` value will be saved as the maximum testcase size. Fuzzing +/// test cases will be truncated to this size before being written to the +/// buffer. +/// +/// # Arguments +/// +/// - `start_index`: The index to use for this start harness +/// - `buffer`: The pointer to the testcase buffer +/// - `max_size`: The maximum size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// HARNESS_START_WITH_MAXIMUM_SIZE_INDEX(0x0001U, buffer, 1024); +/// ``` +#define HARNESS_START_WITH_MAXIMUM_SIZE_INDEX(start_index, buffer, max_size) \ + do { \ + unsigned int value = (N_START_BUFFER_PTR_SIZE_VAL << 0x10U) | MAGIC; \ + __cpuid_extended3(value, start_index, buffer, max_size); \ + } while (0); + +/// Pseudo-hypercall number to signal the fuzzer to use the first argument to +/// the magic instruction as the pointer to the testcase buffer, the second +/// argument as a pointer to the size of the testcase buffer, and the third +/// argument as the maximum size of the testcase buffer. +#define N_START_BUFFER_PTR_SIZE_PTR_VAL (0x0003U) + +/// HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The default "index" of 0 will be used. If you need multiple start +/// harnesses compiled into the same binary, you can use the +/// `HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR_INDEX` macro to specify different +/// indices, then enable them at runtime by configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The address `size_ptr` will be saved. Each fuzzing iteration, the actual +/// size of the current testcase will be written to `*size_ptr`. +/// - The `max_size` value will be saved as the maximum testcase size. Fuzzing +/// test cases will be truncated to this size before being written to the +/// buffer. +/// +/// # Arguments +/// +/// - `buffer`: The pointer to the testcase buffer +/// - `size_ptr`: The pointer to the size of the testcase buffer +/// - `max_size`: The maximum size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// size_t size; +/// HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR(buffer, &size, 1024); +/// ``` +#define HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR(buffer, size_ptr, max_size) \ + do { \ + unsigned int value = (N_START_BUFFER_PTR_SIZE_PTR_VAL << 0x10U) | MAGIC; \ + __cpuid_extended4(value, DEFAULT_INDEX, buffer, size_ptr, max_size); \ + } while (0); + +/// HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR_INDEX +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The index specified by `start_index` will be used. If you need +/// multiple start harnesses compiled into the same binary, you can use this +/// macro to specify different indices, then enable them at runtime by +/// configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The address `size_ptr` will be saved. Each fuzzing iteration, the actual +/// size of the current testcase will be written to `*size_ptr`. +/// - The `max_size` value will be saved as the maximum testcase size. Fuzzing +/// test cases will be truncated to this size before being written to the +/// buffer. +/// +/// # Arguments +/// +/// - `start_index`: The index to use for this start harness +/// - `buffer`: The pointer to the testcase buffer +/// - `size_ptr`: The pointer to the size of the testcase buffer +/// - `max_size`: The maximum size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// size_t size; +/// HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR_INDEX(0x0001U, buffer, &size, 1024); +/// ``` +#define HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR_INDEX(start_index, buffer, \ + size_ptr, max_size) \ + do { \ + unsigned int value = (N_START_BUFFER_PTR_SIZE_PTR_VAL << 0x10U) | MAGIC; \ + __cpuid_extended4(value, start_index, buffer, size_ptr, max_size); \ + } while (0); + +/// Pseudo-hypercall number to signal the fuzzer to stop the current fuzzing +/// iteration and reset to the beginning of the fuzzing loop with a "normal" +/// stop status, indicating no solution has occurred. +#define N_STOP_NORMAL (0x0004U) + +/// HARNESS_STOP +/// +/// Signal the fuzzer to stop and reset to the beginning of the fuzzing loop +/// with a "normal" stop status, indicating no solution has occurred. The +/// default index of 0 will be used. If you need to differentiate between +/// multiple stop harnesses compiled into the same binary, you can use the +/// `HARNESS_STOP_INDEX` macro to specify different indices, then enable them at +/// runtime by configuring the fuzzer. +/// +/// # Example +/// +/// ``` +/// HARNESS_STOP(); +/// ``` +#define HARNESS_STOP() \ + do { \ + unsigned int value = (N_STOP_NORMAL << 0x10U) | MAGIC; \ + __cpuid_extended1(value, DEFAULT_INDEX); \ + } while (0); + +/// HARNESS_STOP_INDEX +/// +/// Signal the fuzzer to stop and reset to the beginning of the fuzzing loop +/// with a "normal" stop status, indicating no solution has occurred. The index +/// specified by `stop_index` will be used. If you need to differentiate between +/// multiple stop harnesses compiled into the same binary, you can use this +/// macro to specify different indices, then enable them at runtime by +/// configuring the fuzzer. +/// +/// # Arguments +/// +/// - `stop_index`: The index to use for this stop harness +/// +/// # Example +/// +/// ``` +/// HARNESS_STOP_INDEX(0x0001U); +/// ``` +#define HARNESS_STOP_INDEX(stop_index) \ + do { \ + unsigned int value = (N_STOP_NORMAL << 0x10U) | MAGIC; \ + __cpuid_extended1(value, stop_index); \ + } while (0); + +/// Pseudo-hypercall number to signal the fuzzer that a custom assertion has +/// occurred, and the fuzzer should stop the current fuzzing iteration and reset +/// to the beginning of the fuzzing loop with a "solution" stop status. +#define N_STOP_ASSERT (0x0005U) + +/// HARNESS_ASSERT +/// +/// Signal the fuzzer that a custom assertion has occurred, and the fuzzer +/// should stop the current fuzzing iteration and reset to the beginning of the +/// fuzzing loop with a "solution" stop status. The default index of 0 will be +/// used. If you need to differentiate between multiple assertion harnesses +/// compiled into the same binary, you can use the `HARNESS_ASSERT_INDEX` macro +/// to specify different indices, then enable them at runtime by configuring the +/// fuzzer. +/// +/// # Example +/// +/// ``` +/// HARNESS_ASSERT(); +/// ``` +#define HARNESS_ASSERT() \ + do { \ + unsigned int value = (N_STOP_ASSERT << 0x10U) | MAGIC; \ + __cpuid_extended1(value, DEFAULT_INDEX); \ + } while (0); + +/// HARNESS_ASSERT_INDEX +/// +/// Signal the fuzzer that a custom assertion has occurred, and the fuzzer +/// should stop the current fuzzing iteration and reset to the beginning of the +/// fuzzing loop with a "solution" stop status. The index specified by +/// `assert_index` will be used. If you need to differentiate between multiple +/// assertion harnesses compiled into the same binary, you can use this macro to +/// specify different indices, then enable them at runtime by configuring the +/// fuzzer. +/// +/// # Arguments +/// +/// - `assert_index`: The index to use for this assertion harness +/// +/// # Example +/// +/// ``` +/// HARNESS_ASSERT_INDEX(0x0001U); +/// ``` +#define HARNESS_ASSERT_INDEX(assert_index) \ + do { \ + unsigned int value = (N_STOP_ASSERT << 0x10U) | MAGIC; \ + __cpuid_extended1(value, assert_index); \ + } while (0); #endif // TSFFS_H \ No newline at end of file diff --git a/harness/tsffs-msvc-x86_64.asm b/harness/tsffs-msvc-x86_64.asm new file mode 100644 index 00000000..c66a039a --- /dev/null +++ b/harness/tsffs-msvc-x86_64.asm @@ -0,0 +1,187 @@ +; NOTES: +; Arguments: RCX, RDX, R8, R9 +; Callee-saved: RBX, RBP, RDI, RSI, RSP, R12, R13, R14, R15, XMM6-15 +; Caller-saved: RAX, RCX, RDX, R8, R9, R10, R11, XMM0-5 +; Return Value: RAX +; +; Test with: "W:\Program Files\Microsoft Visual Studio\2022\BuildTools\VC\Tools\MSVC\14.31.31103\bin\Hostx64\x64\ml64.exe" /c /Cp /Cx /Zf tsffs-msvc-x86_64.asm + +.CODE + +HARNESS_START PROC + push RDI + push RSI + push RBX + + mov RDI, 00h + mov RSI, RCX + ; mov RDX, RDX ; Unnecessary + mov RAX, 014711h + + cpuid + + pop RBX + pop RSI + pop RDI + + ret +HARNESS_START ENDP + +HARNESS_START_INDEX PROC + push RDI + push RSI + push RBX + + mov RDI, RCX + mov RSI, RDX + mov RDX, R8 + mov RAX, 014711h + + cpuid + + pop RBX + pop RSI + pop RDI + + ret +HARNESS_START_INDEX ENDP + +HARNESS_START_WITH_MAXIMUM_SIZE PROC + push RDI + push RSI + push RBX + + mov RDI, 00h + mov RSI, RCX + ; mov RDX, RDX ; Unnecessary + mov RAX, 024711h + + cpuid + + pop RBX + pop RSI + pop RDI + + ret +HARNESS_START_WITH_MAXIMUM_SIZE ENDP + +HARNESS_START_WITH_MAXIMUM_SIZE_INDEX PROC + push RDI + push RSI + push RBX + + mov RDI, RCX + mov RSI, RDX + mov RDX, R8 + mov RAX, 024711h + + cpuid + + pop RBX + pop RSI + pop RDI + + ret +HARNESS_START_WITH_MAXIMUM_SIZE_INDEX ENDP + +HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR PROC + push RDI + push RSI + push RBX + + mov RDI, 00h + mov RSI, RCX + ; mov RDX, RDX ; Unnecessary + mov RCX, R8 + mov RAX, 034711h + + cpuid + + pop RBX + pop RSI + pop RDI + + ret +HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR ENDP + +HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR_INDEX PROC + push RDI + push RSI + push RBX + + mov RDI, RCX + mov RSI, RDX + mov RDX, R8 + mov RCX, R9 + mov RAX, 034711h + + cpuid + + pop RBX + pop RSI + pop RDI + + ret +HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR_INDEX ENDP + +HARNESS_STOP PROC + push RDI + push RBX + + mov RDI, 00h + mov RAX, 044711h + + cpuid + + pop RBX + pop RDI + + ret +HARNESS_STOP ENDP + +HARNESS_STOP_INDEX PROC + push RDI + push RBX + + mov RDI, RCX + mov RAX, 044711h + + cpuid + + pop RBX + pop RDI + + ret +HARNESS_STOP_INDEX ENDP + +HARNESS_ASSERT PROC + push RDI + push RBX + + mov RDI, 00h + mov RAX, 054711h + + cpuid + + pop RBX + pop RDI + + ret +HARNESS_ASSERT ENDP + +HARNESS_ASSERT_INDEX PROC + push RDI + push RBX + + mov RDI, RCX + mov RAX, 054711h + + cpuid + + pop RBX + pop RDI + + ret +HARNESS_ASSERT_INDEX ENDP + +END \ No newline at end of file diff --git a/harness/tsffs-msvc-x86_64.h b/harness/tsffs-msvc-x86_64.h new file mode 100644 index 00000000..0122014f --- /dev/null +++ b/harness/tsffs-msvc-x86_64.h @@ -0,0 +1,290 @@ +#ifndef TSFFS_H +#define TSFFS_H + +/// Define common with LibFuzzer and other fuzzers to allow code that is +/// fuzzing-specific to be left in the codebase. See +/// https://llvm.org/docs/LibFuzzer.html#id35 for more information +#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION +#define FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION (1) +#endif // FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION + +#include + +/// HARNESS_START +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The default "index" of 0 will be used. If you need multiple start +/// harnesses compiled into the same binary, you can use the +/// `HARNESS_START_INDEX` macro to specify different indices, then enable them +/// at runtime by configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The size of the buffer pointed to by `size_ptr` will be saved as the +/// maximum testcase size. Each fuzzing iteration, the actual size of the +/// current testcase will be written to `*size_ptr`. +/// +/// # Arguments +/// +/// - `buffer`: The pointer to the testcase buffer +/// - `size_ptr`: The pointer to the size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// size_t size; +/// HARNESS_START(buffer, &size); +/// ``` +void HARNESS_START(void *buffer, void *size_ptr); + +/// HARNESS_START_INDEX +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The index specified by `start_index` will be used. If you need +/// multiple start harnesses compiled into the same binary, you can use this +/// macro to specify different indices, then enable them at runtime by +/// configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The size of the buffer pointed to by `size_ptr` will be saved as the +/// maximum testcase size. Each fuzzing iteration, the actual size of the +/// current testcase will be written to `*size_ptr`. +/// +/// # Arguments +/// +/// - `start_index`: The index to use for this start harness +/// - `buffer`: The pointer to the testcase buffer +/// - `size_ptr`: The pointer to the size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// size_t size; +/// HARNESS_START_INDEX(0x0001U, buffer, &size); +/// ``` +void HARNESS_START_INDEX(size_t start_index, void *buffer, void *size_ptr); + +/// HARNESS_START_WITH_MAXIMUM_SIZE +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The default "index" of 0 will be used. If you need multiple start +/// harnesses compiled into the same binary, you can use the +/// `HARNESS_START_WITH_MAXIMUM_SIZE_INDEX` macro to specify different indices, +/// then enable them at runtime by configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The `max_size` value will be saved as the maximum testcase size. Fuzzing +/// test cases will be truncated to this size before being written to the +/// buffer. +/// +/// # Arguments +/// +/// - `buffer`: The pointer to the testcase buffer +/// - `max_size`: The maximum size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// HARNESS_START_WITH_MAXIMUM_SIZE(buffer, 1024); +/// ``` +void HARNESS_START_WITH_MAXIMUM_SIZE(void *buffer, size_t max_size); + +/// HARNESS_START_WITH_MAXIMUM_SIZE_INDEX +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The index specified by `start_index` will be used. If you need +/// multiple start harnesses compiled into the same binary, you can use this +/// macro to specify different indices, then enable them at runtime by +/// configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The `max_size` value will be saved as the maximum testcase size. Fuzzing +/// test cases will be truncated to this size before being written to the +/// buffer. +/// +/// # Arguments +/// +/// - `start_index`: The index to use for this start harness +/// - `buffer`: The pointer to the testcase buffer +/// - `max_size`: The maximum size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// HARNESS_START_WITH_MAXIMUM_SIZE_INDEX(0x0001U, buffer, 1024); +/// ``` +void HARNESS_START_WITH_MAXIMUM_SIZE_INDEX(size_t start_index, void *buffer, + size_t max_size); + +/// HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The default "index" of 0 will be used. If you need multiple start +/// harnesses compiled into the same binary, you can use the +/// `HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR_INDEX` macro to specify different +/// indices, then enable them at runtime by configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The address `size_ptr` will be saved. Each fuzzing iteration, the actual +/// size of the current testcase will be written to `*size_ptr`. +/// - The `max_size` value will be saved as the maximum testcase size. Fuzzing +/// test cases will be truncated to this size before being written to the +/// buffer. +/// +/// # Arguments +/// +/// - `buffer`: The pointer to the testcase buffer +/// - `size_ptr`: The pointer to the size of the testcase buffer +/// - `max_size`: The maximum size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// size_t size; +/// HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR(buffer, &size, 1024); +/// ``` +void HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR(void *buffer, void *size_ptr, + size_t max_size); + +/// HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR_INDEX +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The index specified by `start_index` will be used. If you need +/// multiple start harnesses compiled into the same binary, you can use this +/// macro to specify different indices, then enable them at runtime by +/// configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The address `size_ptr` will be saved. Each fuzzing iteration, the actual +/// size of the current testcase will be written to `*size_ptr`. +/// - The `max_size` value will be saved as the maximum testcase size. Fuzzing +/// test cases will be truncated to this size before being written to the +/// buffer. +/// +/// # Arguments +/// +/// - `start_index`: The index to use for this start harness +/// - `buffer`: The pointer to the testcase buffer +/// - `size_ptr`: The pointer to the size of the testcase buffer +/// - `max_size`: The maximum size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// size_t size; +/// HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR_INDEX(0x0001U, buffer, &size, 1024); +/// ``` +void HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR_INDEX(size_t start_index, + void *buffer, void *size_ptr, + size_t max_size); + +/// HARNESS_STOP +/// +/// Signal the fuzzer to stop and reset to the beginning of the fuzzing loop +/// with a "normal" stop status, indicating no solution has occurred. The +/// default index of 0 will be used. If you need to differentiate between +/// multiple stop harnesses compiled into the same binary, you can use the +/// `HARNESS_STOP_INDEX` macro to specify different indices, then enable them at +/// runtime by configuring the fuzzer. +/// +/// # Example +/// +/// ``` +/// HARNESS_STOP(); +/// ``` +void HARNESS_STOP(void); + +/// HARNESS_STOP_INDEX +/// +/// Signal the fuzzer to stop and reset to the beginning of the fuzzing loop +/// with a "normal" stop status, indicating no solution has occurred. The index +/// specified by `stop_index` will be used. If you need to differentiate between +/// multiple stop harnesses compiled into the same binary, you can use this +/// macro to specify different indices, then enable them at runtime by +/// configuring the fuzzer. +/// +/// # Arguments +/// +/// - `stop_index`: The index to use for this stop harness +/// +/// # Example +/// +/// ``` +/// HARNESS_STOP_INDEX(0x0001U); +/// ``` +void HARNESS_STOP_INDEX(size_t stop_index); + +/// HARNESS_ASSERT +/// +/// Signal the fuzzer that a custom assertion has occurred, and the fuzzer +/// should stop the current fuzzing iteration and reset to the beginning of the +/// fuzzing loop with a "solution" stop status. The default index of 0 will be +/// used. If you need to differentiate between multiple assertion harnesses +/// compiled into the same binary, you can use the `HARNESS_ASSERT_INDEX` macro +/// to specify different indices, then enable them at runtime by configuring the +/// fuzzer. +/// +/// # Example +/// +/// ``` +/// HARNESS_ASSERT(); +/// ``` +void HARNESS_ASSERT(void); + +/// HARNESS_ASSERT_INDEX +/// +/// Signal the fuzzer that a custom assertion has occurred, and the fuzzer +/// should stop the current fuzzing iteration and reset to the beginning of the +/// fuzzing loop with a "solution" stop status. The index specified by +/// `assert_index` will be used. If you need to differentiate between multiple +/// assertion harnesses compiled into the same binary, you can use this macro to +/// specify different indices, then enable them at runtime by configuring the +/// fuzzer. +/// +/// # Arguments +/// +/// - `assert_index`: The index to use for this assertion harness +/// +/// # Example +/// +/// ``` +/// HARNESS_ASSERT_INDEX(0x0001U); +/// ``` +void HARNESS_ASSERT_INDEX(size_t assert_index); + +#endif // TSFFS_H \ No newline at end of file diff --git a/harness/tsffs.h b/harness/tsffs.h new file mode 100644 index 00000000..bf116af2 --- /dev/null +++ b/harness/tsffs.h @@ -0,0 +1,2876 @@ +// Copyright (C) 2024 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 + +#if defined(__GNUC__) || defined(__clang__) +#ifdef __i386__ +// Copyright (C) 2024 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 + +/// Definitions and macros for compiled-in harnessing of C and C++ target +/// software for the X86 architecture. + +#ifndef TSFFS_H +#define TSFFS_H + +/// Define common with LibFuzzer and other fuzzers to allow code that is +/// fuzzing-specific to be left in the codebase. See +/// https://llvm.org/docs/LibFuzzer.html#id35 for more information +#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION +#define FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION (1) +#endif // FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION + +/// __cpuid +/// +/// Invoke the CPUID instruction with a specific value `value` in register +/// `rax`. +/// +/// # Arguments +/// +/// - `value`: The value to load into the `rax` register before invoking the +/// CPUID instruction +#define __cpuid(value) \ + unsigned int _a __attribute__((unused)) = 0; \ + unsigned int _b __attribute__((unused)) = 0; \ + unsigned int _c __attribute__((unused)) = 0; \ + unsigned int _d __attribute__((unused)) = 0; \ + __asm__ __volatile__("cpuid\n\t" \ + : "=a"(_a), "=b"(_b), "=c"(_c), "=d"(_d) \ + : "a"(value)); + +/// __cpuid_extended1 +/// +/// Invoke the CPUID instruction with a specific value `value` in register +/// `rax` and a pseudo-argument in register `rdi`. +/// +/// # Arguments +/// +/// - `value`: The value to load into the `rax` register before invoking the +/// CPUID instruction +/// - `arg0`: The value to load into the `rdi` register before invoking the +/// CPUID instruction +#define __cpuid_extended1(value, arg0) \ + unsigned int _a __attribute__((unused)) = 0; \ + unsigned int _b __attribute__((unused)) = 0; \ + unsigned int _c __attribute__((unused)) = 0; \ + unsigned int _d __attribute__((unused)) = 0; \ + __asm__ __volatile__("cpuid\n\t" \ + : "=a"(_a), "=b"(_b), "=c"(_c), "=d"(_d) \ + : "a"(value), "D"(arg0)); + +/// __cpuid_extended2 +/// +/// Invoke the CPUID instruction with a specific value `value` in register +/// `rax` and a pseudo-arguments in registers `rdi` and `rsi`. +/// +/// # Arguments +/// +/// - `value`: The value to load into the `rax` register before invoking the +/// CPUID instruction +/// - `arg0`: The value to load into the `rdi` register before invoking the +/// CPUID instruction +/// - `arg1`: The value to load into the `rsi` register before invoking the +/// CPUID instruction +#define __cpuid_extended2(value, arg0, arg1) \ + unsigned int _a __attribute__((unused)) = 0; \ + unsigned int _b __attribute__((unused)) = 0; \ + unsigned int _c __attribute__((unused)) = 0; \ + unsigned int _d __attribute__((unused)) = 0; \ + __asm__ __volatile__("cpuid\n\t" \ + : "=a"(_a), "=b"(_b), "=c"(_c), "=d"(_d) \ + : "a"(value), "D"(arg0), "S"(arg1)); + +/// __cpuid_extended3 +/// +/// Invoke the CPUID instruction with a specific value `value` in register +/// `rax` and a pseudo-arguments in registers `rdi`, `rsi`, and `rdx`. +/// +/// # Arguments +/// +/// - `value`: The value to load into the `rax` register before invoking the +/// CPUID instruction +/// - `arg0`: The value to load into the `rdi` register before invoking the +/// CPUID instruction +/// - `arg1`: The value to load into the `rsi` register before invoking the +/// CPUID instruction +/// - `arg2`: The value to load into the `rdx` register before invoking the +/// CPUID instruction +#define __cpuid_extended3(value, arg0, arg1, arg2) \ + unsigned int _a __attribute__((unused)) = 0; \ + unsigned int _b __attribute__((unused)) = 0; \ + unsigned int _c __attribute__((unused)) = 0; \ + unsigned int _d __attribute__((unused)) = 0; \ + __asm__ __volatile__("cpuid\n\t" \ + : "=a"(_a), "=b"(_b), "=c"(_c), "=d"(_d) \ + : "a"(value), "D"(arg0), "S"(arg1), "d"(arg2)); + +/// __cpuid_extended4 +/// +/// Invoke the CPUID instruction with a specific value `value` in register +/// `rax` and a pseudo-arguments in registers `rdi`, `rsi`, `rdx`, and `rcx`. +/// +/// # Arguments +/// +/// - `value`: The value to load into the `rax` register before invoking the +/// CPUID instruction +/// - `arg0`: The value to load into the `rdi` register before invoking the +/// CPUID instruction +/// - `arg1`: The value to load into the `rsi` register before invoking the +/// CPUID instruction +/// - `arg2`: The value to load into the `rdx` register before invoking the +/// CPUID instruction +/// - `arg3`: The value to load into the `rcx` register before invoking the +/// CPUID instruction +#define __cpuid_extended4(value, arg0, arg1, arg2, arg3) \ + unsigned int _a __attribute__((unused)) = 0; \ + unsigned int _b __attribute__((unused)) = 0; \ + unsigned int _c __attribute__((unused)) = 0; \ + unsigned int _d __attribute__((unused)) = 0; \ + __asm__ __volatile__("cpuid\n\t" \ + : "=a"(_a), "=b"(_b), "=c"(_c), "=d"(_d) \ + : "a"(value), "D"(arg0), "S"(arg1), "d"(arg2), \ + "c"(arg3)); + +/// Magic value defined by SIMICS as the "leaf" value of a CPUID instruction +/// that is treated as a magic instruction. +#define MAGIC (0x4711U) + +/// The default index number used for magic instructions. All magic instructions +/// support multiple start and stop indices, which defaults to 0 if not +/// specified. +#define DEFAULT_INDEX (0x0000U) + +/// Pseudo-hypercall number to signal the fuzzer to use the first argument to +/// the magic instruction as the pointer to the testcase buffer and the second +/// argument as a pointer to the size of the testcase buffer. +#define N_START_BUFFER_PTR_SIZE_PTR (0x0001U) + +/// HARNESS_START +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The default "index" of 0 will be used. If you need multiple start +/// harnesses compiled into the same binary, you can use the +/// `HARNESS_START_INDEX` macro to specify different indices, then enable them +/// at runtime by configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The size of the buffer pointed to by `size_ptr` will be saved as the +/// maximum testcase size. Each fuzzing iteration, the actual size of the +/// current testcase will be written to `*size_ptr`. +/// +/// # Arguments +/// +/// - `buffer`: The pointer to the testcase buffer +/// - `size_ptr`: The pointer to the size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// size_t size; +/// HARNESS_START(buffer, &size); +/// ``` +#define HARNESS_START(buffer, size_ptr) \ + do { \ + unsigned int value = (N_START_BUFFER_PTR_SIZE_PTR << 0x10U) | MAGIC; \ + __cpuid_extended3(value, DEFAULT_INDEX, buffer, size_ptr); \ + } while (0); + +/// HARNESS_START_INDEX +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The index specified by `start_index` will be used. If you need +/// multiple start harnesses compiled into the same binary, you can use this +/// macro to specify different indices, then enable them at runtime by +/// configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The size of the buffer pointed to by `size_ptr` will be saved as the +/// maximum testcase size. Each fuzzing iteration, the actual size of the +/// current testcase will be written to `*size_ptr`. +/// +/// # Arguments +/// +/// - `start_index`: The index to use for this start harness +/// - `buffer`: The pointer to the testcase buffer +/// - `size_ptr`: The pointer to the size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// size_t size; +/// HARNESS_START_INDEX(0x0001U, buffer, &size); +/// ``` +#define HARNESS_START_INDEX(start_index, buffer, size_ptr) \ + do { \ + unsigned int value = (N_START_BUFFER_PTR_SIZE_PTR << 0x10U) | MAGIC; \ + __cpuid_extended3(value, start_index, buffer, size_ptr); \ + } while (0); + +/// Pseudo-hypercall number to signal the fuzzer to use the first argument to +/// the magic instruction as the pointer to the testcase buffer and the second +/// argument as the maximum size of the testcase buffer. +#define N_START_BUFFER_PTR_SIZE_VAL (0x0002U) + +/// HARNESS_START_WITH_MAXIMUM_SIZE +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The default "index" of 0 will be used. If you need multiple start +/// harnesses compiled into the same binary, you can use the +/// `HARNESS_START_WITH_MAXIMUM_SIZE_INDEX` macro to specify different indices, +/// then enable them at runtime by configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The `max_size` value will be saved as the maximum testcase size. Fuzzing +/// test cases will be truncated to this size before being written to the +/// buffer. +/// +/// # Arguments +/// +/// - `buffer`: The pointer to the testcase buffer +/// - `max_size`: The maximum size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// HARNESS_START_WITH_MAXIMUM_SIZE(buffer, 1024); +/// ``` +#define HARNESS_START_WITH_MAXIMUM_SIZE(buffer, max_size) \ + do { \ + unsigned int value = (N_START_BUFFER_PTR_SIZE_VAL << 0x10U) | MAGIC; \ + __cpuid_extended3(value, DEFAULT_INDEX, buffer, max_size); \ + } while (0); + +/// HARNESS_START_WITH_MAXIMUM_SIZE_INDEX +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The index specified by `start_index` will be used. If you need +/// multiple start harnesses compiled into the same binary, you can use this +/// macro to specify different indices, then enable them at runtime by +/// configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The `max_size` value will be saved as the maximum testcase size. Fuzzing +/// test cases will be truncated to this size before being written to the +/// buffer. +/// +/// # Arguments +/// +/// - `start_index`: The index to use for this start harness +/// - `buffer`: The pointer to the testcase buffer +/// - `max_size`: The maximum size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// HARNESS_START_WITH_MAXIMUM_SIZE_INDEX(0x0001U, buffer, 1024); +/// ``` +#define HARNESS_START_WITH_MAXIMUM_SIZE_INDEX(start_index, buffer, max_size) \ + do { \ + unsigned int value = (N_START_BUFFER_PTR_SIZE_VAL << 0x10U) | MAGIC; \ + __cpuid_extended3(value, start_index, buffer, max_size); \ + } while (0); + +/// Pseudo-hypercall number to signal the fuzzer to use the first argument to +/// the magic instruction as the pointer to the testcase buffer, the second +/// argument as a pointer to the size of the testcase buffer, and the third +/// argument as the maximum size of the testcase buffer. +#define N_START_BUFFER_PTR_SIZE_PTR_VAL (0x0003U) + +/// HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The default "index" of 0 will be used. If you need multiple start +/// harnesses compiled into the same binary, you can use the +/// `HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR_INDEX` macro to specify different +/// indices, then enable them at runtime by configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The address `size_ptr` will be saved. Each fuzzing iteration, the actual +/// size of the current testcase will be written to `*size_ptr`. +/// - The `max_size` value will be saved as the maximum testcase size. Fuzzing +/// test cases will be truncated to this size before being written to the +/// buffer. +/// +/// # Arguments +/// +/// - `buffer`: The pointer to the testcase buffer +/// - `size_ptr`: The pointer to the size of the testcase buffer +/// - `max_size`: The maximum size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// size_t size; +/// HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR(buffer, &size, 1024); +/// ``` +#define HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR(buffer, size_ptr, max_size) \ + do { \ + unsigned int value = (N_START_BUFFER_PTR_SIZE_PTR_VAL << 0x10U) | MAGIC; \ + __cpuid_extended4(value, DEFAULT_INDEX, buffer, size_ptr, max_size); \ + } while (0); + +/// HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR_INDEX +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The index specified by `start_index` will be used. If you need +/// multiple start harnesses compiled into the same binary, you can use this +/// macro to specify different indices, then enable them at runtime by +/// configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The address `size_ptr` will be saved. Each fuzzing iteration, the actual +/// size of the current testcase will be written to `*size_ptr`. +/// - The `max_size` value will be saved as the maximum testcase size. Fuzzing +/// test cases will be truncated to this size before being written to the +/// buffer. +/// +/// # Arguments +/// +/// - `start_index`: The index to use for this start harness +/// - `buffer`: The pointer to the testcase buffer +/// - `size_ptr`: The pointer to the size of the testcase buffer +/// - `max_size`: The maximum size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// size_t size; +/// HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR_INDEX(0x0001U, buffer, &size, 1024); +/// ``` +#define HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR_INDEX(start_index, buffer, \ + size_ptr, max_size) \ + do { \ + unsigned int value = (N_START_BUFFER_PTR_SIZE_PTR_VAL << 0x10U) | MAGIC; \ + __cpuid_extended4(value, start_index, buffer, size_ptr, max_size); \ + } while (0); + +/// Pseudo-hypercall number to signal the fuzzer to stop the current fuzzing +/// iteration and reset to the beginning of the fuzzing loop with a "normal" +/// stop status, indicating no solution has occurred. +#define N_STOP_NORMAL (0x0004U) + +/// HARNESS_STOP +/// +/// Signal the fuzzer to stop and reset to the beginning of the fuzzing loop +/// with a "normal" stop status, indicating no solution has occurred. The +/// default index of 0 will be used. If you need to differentiate between +/// multiple stop harnesses compiled into the same binary, you can use the +/// `HARNESS_STOP_INDEX` macro to specify different indices, then enable them at +/// runtime by configuring the fuzzer. +/// +/// # Example +/// +/// ``` +/// HARNESS_STOP(); +/// ``` +#define HARNESS_STOP() \ + do { \ + unsigned int value = (N_STOP_NORMAL << 0x10U) | MAGIC; \ + __cpuid_extended1(value, DEFAULT_INDEX); \ + } while (0); + +/// HARNESS_STOP_INDEX +/// +/// Signal the fuzzer to stop and reset to the beginning of the fuzzing loop +/// with a "normal" stop status, indicating no solution has occurred. The index +/// specified by `stop_index` will be used. If you need to differentiate between +/// multiple stop harnesses compiled into the same binary, you can use this +/// macro to specify different indices, then enable them at runtime by +/// configuring the fuzzer. +/// +/// # Arguments +/// +/// - `stop_index`: The index to use for this stop harness +/// +/// # Example +/// +/// ``` +/// HARNESS_STOP_INDEX(0x0001U); +/// ``` +#define HARNESS_STOP_INDEX(stop_index) \ + do { \ + unsigned int value = (N_STOP_NORMAL << 0x10U) | MAGIC; \ + __cpuid_extended1(value, stop_index); \ + } while (0); + +/// Pseudo-hypercall number to signal the fuzzer that a custom assertion has +/// occurred, and the fuzzer should stop the current fuzzing iteration and reset +/// to the beginning of the fuzzing loop with a "solution" stop status. +#define N_STOP_ASSERT (0x0005U) + +/// HARNESS_ASSERT +/// +/// Signal the fuzzer that a custom assertion has occurred, and the fuzzer +/// should stop the current fuzzing iteration and reset to the beginning of the +/// fuzzing loop with a "solution" stop status. The default index of 0 will be +/// used. If you need to differentiate between multiple assertion harnesses +/// compiled into the same binary, you can use the `HARNESS_ASSERT_INDEX` macro +/// to specify different indices, then enable them at runtime by configuring the +/// fuzzer. +/// +/// # Example +/// +/// ``` +/// HARNESS_ASSERT(); +/// ``` +#define HARNESS_ASSERT() \ + do { \ + unsigned int value = (N_STOP_ASSERT << 0x10U) | MAGIC; \ + __cpuid_extended1(value, DEFAULT_INDEX); \ + } while (0); + +/// HARNESS_ASSERT_INDEX +/// +/// Signal the fuzzer that a custom assertion has occurred, and the fuzzer +/// should stop the current fuzzing iteration and reset to the beginning of the +/// fuzzing loop with a "solution" stop status. The index specified by +/// `assert_index` will be used. If you need to differentiate between multiple +/// assertion harnesses compiled into the same binary, you can use this macro to +/// specify different indices, then enable them at runtime by configuring the +/// fuzzer. +/// +/// # Arguments +/// +/// - `assert_index`: The index to use for this assertion harness +/// +/// # Example +/// +/// ``` +/// HARNESS_ASSERT_INDEX(0x0001U); +/// ``` +#define HARNESS_ASSERT_INDEX(assert_index) \ + do { \ + unsigned int value = (N_STOP_ASSERT << 0x10U) | MAGIC; \ + __cpuid_extended1(value, assert_index); \ + } while (0); + +#endif // TSFFS_H +#elif __x86_64__ +// Copyright (C) 2024 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 + +/// Definitions and macros for compiled-in harnessing of C and C++ target +/// software for the x86_64 architecture. + +#ifndef TSFFS_H +#define TSFFS_H + +/// Define common with LibFuzzer and other fuzzers to allow code that is +/// fuzzing-specific to be left in the codebase. See +/// https://llvm.org/docs/LibFuzzer.html#id35 for more information +#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION +#define FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION (1) +#endif // FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION + +/// __cpuid +/// +/// Invoke the CPUID instruction with a specific value `value` in register +/// `rax`. +/// +/// # Arguments +/// +/// - `value`: The value to load into the `rax` register before invoking the +/// CPUID instruction +#define __cpuid(value) \ + unsigned int _a __attribute__((unused)) = 0; \ + unsigned int _b __attribute__((unused)) = 0; \ + unsigned int _c __attribute__((unused)) = 0; \ + unsigned int _d __attribute__((unused)) = 0; \ + __asm__ __volatile__("cpuid\n\t" \ + : "=a"(_a), "=b"(_b), "=c"(_c), "=d"(_d) \ + : "a"(value)); + +/// __cpuid_extended1 +/// +/// Invoke the CPUID instruction with a specific value `value` in register +/// `rax` and a pseudo-argument in register `rdi`. +/// +/// # Arguments +/// +/// - `value`: The value to load into the `rax` register before invoking the +/// CPUID instruction +/// - `arg0`: The value to load into the `rdi` register before invoking the +/// CPUID instruction +#define __cpuid_extended1(value, arg0) \ + unsigned int _a __attribute__((unused)) = 0; \ + unsigned int _b __attribute__((unused)) = 0; \ + unsigned int _c __attribute__((unused)) = 0; \ + unsigned int _d __attribute__((unused)) = 0; \ + __asm__ __volatile__("cpuid\n\t" \ + : "=a"(_a), "=b"(_b), "=c"(_c), "=d"(_d) \ + : "a"(value), "D"(arg0)); + +/// __cpuid_extended2 +/// +/// Invoke the CPUID instruction with a specific value `value` in register +/// `rax` and a pseudo-arguments in registers `rdi` and `rsi`. +/// +/// # Arguments +/// +/// - `value`: The value to load into the `rax` register before invoking the +/// CPUID instruction +/// - `arg0`: The value to load into the `rdi` register before invoking the +/// CPUID instruction +/// - `arg1`: The value to load into the `rsi` register before invoking the +/// CPUID instruction +#define __cpuid_extended2(value, arg0, arg1) \ + unsigned int _a __attribute__((unused)) = 0; \ + unsigned int _b __attribute__((unused)) = 0; \ + unsigned int _c __attribute__((unused)) = 0; \ + unsigned int _d __attribute__((unused)) = 0; \ + __asm__ __volatile__("cpuid\n\t" \ + : "=a"(_a), "=b"(_b), "=c"(_c), "=d"(_d) \ + : "a"(value), "D"(arg0), "S"(arg1)); + +/// __cpuid_extended3 +/// +/// Invoke the CPUID instruction with a specific value `value` in register +/// `rax` and a pseudo-arguments in registers `rdi`, `rsi`, and `rdx`. +/// +/// # Arguments +/// +/// - `value`: The value to load into the `rax` register before invoking the +/// CPUID instruction +/// - `arg0`: The value to load into the `rdi` register before invoking the +/// CPUID instruction +/// - `arg1`: The value to load into the `rsi` register before invoking the +/// CPUID instruction +/// - `arg2`: The value to load into the `rdx` register before invoking the +/// CPUID instruction +#define __cpuid_extended3(value, arg0, arg1, arg2) \ + unsigned int _a __attribute__((unused)) = 0; \ + unsigned int _b __attribute__((unused)) = 0; \ + unsigned int _c __attribute__((unused)) = 0; \ + unsigned int _d __attribute__((unused)) = 0; \ + __asm__ __volatile__("cpuid\n\t" \ + : "=a"(_a), "=b"(_b), "=c"(_c), "=d"(_d) \ + : "a"(value), "D"(arg0), "S"(arg1), "d"(arg2)); + +/// __cpuid_extended4 +/// +/// Invoke the CPUID instruction with a specific value `value` in register +/// `rax` and a pseudo-arguments in registers `rdi`, `rsi`, `rdx`, and `rcx`. +/// +/// # Arguments +/// +/// - `value`: The value to load into the `rax` register before invoking the +/// CPUID instruction +/// - `arg0`: The value to load into the `rdi` register before invoking the +/// CPUID instruction +/// - `arg1`: The value to load into the `rsi` register before invoking the +/// CPUID instruction +/// - `arg2`: The value to load into the `rdx` register before invoking the +/// CPUID instruction +/// - `arg3`: The value to load into the `rcx` register before invoking the +/// CPUID instruction +#define __cpuid_extended4(value, arg0, arg1, arg2, arg3) \ + unsigned int _a __attribute__((unused)) = 0; \ + unsigned int _b __attribute__((unused)) = 0; \ + unsigned int _c __attribute__((unused)) = 0; \ + unsigned int _d __attribute__((unused)) = 0; \ + __asm__ __volatile__("cpuid\n\t" \ + : "=a"(_a), "=b"(_b), "=c"(_c), "=d"(_d) \ + : "a"(value), "D"(arg0), "S"(arg1), "d"(arg2), \ + "c"(arg3)); + +/// Magic value defined by SIMICS as the "leaf" value of a CPUID instruction +/// that is treated as a magic instruction. +#define MAGIC (0x4711U) + +/// The default index number used for magic instructions. All magic instructions +/// support multiple start and stop indices, which defaults to 0 if not +/// specified. +#define DEFAULT_INDEX (0x0000U) + +/// Pseudo-hypercall number to signal the fuzzer to use the first argument to +/// the magic instruction as the pointer to the testcase buffer and the second +/// argument as a pointer to the size of the testcase buffer. +#define N_START_BUFFER_PTR_SIZE_PTR (0x0001U) + +/// HARNESS_START +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The default "index" of 0 will be used. If you need multiple start +/// harnesses compiled into the same binary, you can use the +/// `HARNESS_START_INDEX` macro to specify different indices, then enable them +/// at runtime by configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The size of the buffer pointed to by `size_ptr` will be saved as the +/// maximum testcase size. Each fuzzing iteration, the actual size of the +/// current testcase will be written to `*size_ptr`. +/// +/// # Arguments +/// +/// - `buffer`: The pointer to the testcase buffer +/// - `size_ptr`: The pointer to the size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// size_t size; +/// HARNESS_START(buffer, &size); +/// ``` +#define HARNESS_START(buffer, size_ptr) \ + do { \ + unsigned int value = (N_START_BUFFER_PTR_SIZE_PTR << 0x10U) | MAGIC; \ + __cpuid_extended3(value, DEFAULT_INDEX, buffer, size_ptr); \ + } while (0); + +/// HARNESS_START_INDEX +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The index specified by `start_index` will be used. If you need +/// multiple start harnesses compiled into the same binary, you can use this +/// macro to specify different indices, then enable them at runtime by +/// configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The size of the buffer pointed to by `size_ptr` will be saved as the +/// maximum testcase size. Each fuzzing iteration, the actual size of the +/// current testcase will be written to `*size_ptr`. +/// +/// # Arguments +/// +/// - `start_index`: The index to use for this start harness +/// - `buffer`: The pointer to the testcase buffer +/// - `size_ptr`: The pointer to the size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// size_t size; +/// HARNESS_START_INDEX(0x0001U, buffer, &size); +/// ``` +#define HARNESS_START_INDEX(start_index, buffer, size_ptr) \ + do { \ + unsigned int value = (N_START_BUFFER_PTR_SIZE_PTR << 0x10U) | MAGIC; \ + __cpuid_extended3(value, start_index, buffer, size_ptr); \ + } while (0); + +/// Pseudo-hypercall number to signal the fuzzer to use the first argument to +/// the magic instruction as the pointer to the testcase buffer and the second +/// argument as the maximum size of the testcase buffer. +#define N_START_BUFFER_PTR_SIZE_VAL (0x0002U) + +/// HARNESS_START_WITH_MAXIMUM_SIZE +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The default "index" of 0 will be used. If you need multiple start +/// harnesses compiled into the same binary, you can use the +/// `HARNESS_START_WITH_MAXIMUM_SIZE_INDEX` macro to specify different indices, +/// then enable them at runtime by configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The `max_size` value will be saved as the maximum testcase size. Fuzzing +/// test cases will be truncated to this size before being written to the +/// buffer. +/// +/// # Arguments +/// +/// - `buffer`: The pointer to the testcase buffer +/// - `max_size`: The maximum size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// HARNESS_START_WITH_MAXIMUM_SIZE(buffer, 1024); +/// ``` +#define HARNESS_START_WITH_MAXIMUM_SIZE(buffer, max_size) \ + do { \ + unsigned int value = (N_START_BUFFER_PTR_SIZE_VAL << 0x10U) | MAGIC; \ + __cpuid_extended3(value, DEFAULT_INDEX, buffer, max_size); \ + } while (0); + +/// HARNESS_START_WITH_MAXIMUM_SIZE_INDEX +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The index specified by `start_index` will be used. If you need +/// multiple start harnesses compiled into the same binary, you can use this +/// macro to specify different indices, then enable them at runtime by +/// configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The `max_size` value will be saved as the maximum testcase size. Fuzzing +/// test cases will be truncated to this size before being written to the +/// buffer. +/// +/// # Arguments +/// +/// - `start_index`: The index to use for this start harness +/// - `buffer`: The pointer to the testcase buffer +/// - `max_size`: The maximum size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// HARNESS_START_WITH_MAXIMUM_SIZE_INDEX(0x0001U, buffer, 1024); +/// ``` +#define HARNESS_START_WITH_MAXIMUM_SIZE_INDEX(start_index, buffer, max_size) \ + do { \ + unsigned int value = (N_START_BUFFER_PTR_SIZE_VAL << 0x10U) | MAGIC; \ + __cpuid_extended3(value, start_index, buffer, max_size); \ + } while (0); + +/// Pseudo-hypercall number to signal the fuzzer to use the first argument to +/// the magic instruction as the pointer to the testcase buffer, the second +/// argument as a pointer to the size of the testcase buffer, and the third +/// argument as the maximum size of the testcase buffer. +#define N_START_BUFFER_PTR_SIZE_PTR_VAL (0x0003U) + +/// HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The default "index" of 0 will be used. If you need multiple start +/// harnesses compiled into the same binary, you can use the +/// `HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR_INDEX` macro to specify different +/// indices, then enable them at runtime by configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The address `size_ptr` will be saved. Each fuzzing iteration, the actual +/// size of the current testcase will be written to `*size_ptr`. +/// - The `max_size` value will be saved as the maximum testcase size. Fuzzing +/// test cases will be truncated to this size before being written to the +/// buffer. +/// +/// # Arguments +/// +/// - `buffer`: The pointer to the testcase buffer +/// - `size_ptr`: The pointer to the size of the testcase buffer +/// - `max_size`: The maximum size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// size_t size; +/// HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR(buffer, &size, 1024); +/// ``` +#define HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR(buffer, size_ptr, max_size) \ + do { \ + unsigned int value = (N_START_BUFFER_PTR_SIZE_PTR_VAL << 0x10U) | MAGIC; \ + __cpuid_extended4(value, DEFAULT_INDEX, buffer, size_ptr, max_size); \ + } while (0); + +/// HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR_INDEX +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The index specified by `start_index` will be used. If you need +/// multiple start harnesses compiled into the same binary, you can use this +/// macro to specify different indices, then enable them at runtime by +/// configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The address `size_ptr` will be saved. Each fuzzing iteration, the actual +/// size of the current testcase will be written to `*size_ptr`. +/// - The `max_size` value will be saved as the maximum testcase size. Fuzzing +/// test cases will be truncated to this size before being written to the +/// buffer. +/// +/// # Arguments +/// +/// - `start_index`: The index to use for this start harness +/// - `buffer`: The pointer to the testcase buffer +/// - `size_ptr`: The pointer to the size of the testcase buffer +/// - `max_size`: The maximum size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// size_t size; +/// HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR_INDEX(0x0001U, buffer, &size, 1024); +/// ``` +#define HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR_INDEX(start_index, buffer, \ + size_ptr, max_size) \ + do { \ + unsigned int value = (N_START_BUFFER_PTR_SIZE_PTR_VAL << 0x10U) | MAGIC; \ + __cpuid_extended4(value, start_index, buffer, size_ptr, max_size); \ + } while (0); + +/// Pseudo-hypercall number to signal the fuzzer to stop the current fuzzing +/// iteration and reset to the beginning of the fuzzing loop with a "normal" +/// stop status, indicating no solution has occurred. +#define N_STOP_NORMAL (0x0004U) + +/// HARNESS_STOP +/// +/// Signal the fuzzer to stop and reset to the beginning of the fuzzing loop +/// with a "normal" stop status, indicating no solution has occurred. The +/// default index of 0 will be used. If you need to differentiate between +/// multiple stop harnesses compiled into the same binary, you can use the +/// `HARNESS_STOP_INDEX` macro to specify different indices, then enable them at +/// runtime by configuring the fuzzer. +/// +/// # Example +/// +/// ``` +/// HARNESS_STOP(); +/// ``` +#define HARNESS_STOP() \ + do { \ + unsigned int value = (N_STOP_NORMAL << 0x10U) | MAGIC; \ + __cpuid_extended1(value, DEFAULT_INDEX); \ + } while (0); + +/// HARNESS_STOP_INDEX +/// +/// Signal the fuzzer to stop and reset to the beginning of the fuzzing loop +/// with a "normal" stop status, indicating no solution has occurred. The index +/// specified by `stop_index` will be used. If you need to differentiate between +/// multiple stop harnesses compiled into the same binary, you can use this +/// macro to specify different indices, then enable them at runtime by +/// configuring the fuzzer. +/// +/// # Arguments +/// +/// - `stop_index`: The index to use for this stop harness +/// +/// # Example +/// +/// ``` +/// HARNESS_STOP_INDEX(0x0001U); +/// ``` +#define HARNESS_STOP_INDEX(stop_index) \ + do { \ + unsigned int value = (N_STOP_NORMAL << 0x10U) | MAGIC; \ + __cpuid_extended1(value, stop_index); \ + } while (0); + +/// Pseudo-hypercall number to signal the fuzzer that a custom assertion has +/// occurred, and the fuzzer should stop the current fuzzing iteration and reset +/// to the beginning of the fuzzing loop with a "solution" stop status. +#define N_STOP_ASSERT (0x0005U) + +/// HARNESS_ASSERT +/// +/// Signal the fuzzer that a custom assertion has occurred, and the fuzzer +/// should stop the current fuzzing iteration and reset to the beginning of the +/// fuzzing loop with a "solution" stop status. The default index of 0 will be +/// used. If you need to differentiate between multiple assertion harnesses +/// compiled into the same binary, you can use the `HARNESS_ASSERT_INDEX` macro +/// to specify different indices, then enable them at runtime by configuring the +/// fuzzer. +/// +/// # Example +/// +/// ``` +/// HARNESS_ASSERT(); +/// ``` +#define HARNESS_ASSERT() \ + do { \ + unsigned int value = (N_STOP_ASSERT << 0x10U) | MAGIC; \ + __cpuid_extended1(value, DEFAULT_INDEX); \ + } while (0); + +/// HARNESS_ASSERT_INDEX +/// +/// Signal the fuzzer that a custom assertion has occurred, and the fuzzer +/// should stop the current fuzzing iteration and reset to the beginning of the +/// fuzzing loop with a "solution" stop status. The index specified by +/// `assert_index` will be used. If you need to differentiate between multiple +/// assertion harnesses compiled into the same binary, you can use this macro to +/// specify different indices, then enable them at runtime by configuring the +/// fuzzer. +/// +/// # Arguments +/// +/// - `assert_index`: The index to use for this assertion harness +/// +/// # Example +/// +/// ``` +/// HARNESS_ASSERT_INDEX(0x0001U); +/// ``` +#define HARNESS_ASSERT_INDEX(assert_index) \ + do { \ + unsigned int value = (N_STOP_ASSERT << 0x10U) | MAGIC; \ + __cpuid_extended1(value, assert_index); \ + } while (0); + +#endif // TSFFS_H +#elif __riscv && !__LP64__ +// Copyright (C) 2024 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 + +/// Definitions and macros for compiled-in harnessing of C and C++ target +/// software for the RISC-V (32-bit) architecture + +#ifndef TSFFS_H +#define TSFFS_H + +/// Define common with LibFuzzer and other fuzzers to allow code that is +/// fuzzing-specific to be left in the codebase. See +/// https://llvm.org/docs/LibFuzzer.html#id35 for more information +#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION +#define FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION (1) +#endif // FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION + +/// __srai +/// +/// Invoke the magic instruction defined by SIMICS for the RISC-V architecture +/// with a specific value of `n` +/// +/// # Arguments +/// +/// * `value` - The value of `n` to use in the magic instruction +#define __srai(value) \ + __asm__ __volatile__("srai zero, zero, %0" : : "I"(value) :) + +/// __srai_extended1 +/// +/// Invoke the magic instruction defined by SIMICS for the RISC-V architecture +/// with a specific value of `n` and a pseudo-argument in register `a0`. +/// +/// # Arguments +/// +/// * `value` - The value of `n` to use in the magic instruction +/// * `arg0` - The value to place in register `a0` +#define __srai_extended1(value, arg0) \ + __asm__ __volatile__("mv a0, %0; srai zero, zero, %1" \ + : \ + : "r"(arg0), "I"(value) \ + : "a0"); + +/// __srai_extended2 +/// +/// Invoke the magic instruction defined by SIMICS for the RISC-V architecture +/// with a specific value of `n` and pseudo-arguments in registers `a0` and +/// `a1`. +/// +/// # Arguments +/// +/// * `value` - The value of `n` to use in the magic instruction +/// * `arg0` - The value to place in register `a0` +/// * `arg1` - The value to place in register `a1` +#define __srai_extended2(value, arg0, arg1) \ + __asm__ __volatile__("mv a0, %0; mv a1, %1; srai zero, zero, %2" \ + : \ + : "r"(arg0), "r"(arg1), "I"(value) \ + : "a0", "a1"); + +/// __srai_extended3 +/// +/// Invoke the magic instruction defined by SIMICS for the RISC-V architecture +/// with a specific value of `n` and pseudo-arguments in registers `a0`, `a1`, +/// and `a2`. +/// +/// # Arguments +/// +/// * `value` - The value of `n` to use in the magic instruction +/// * `arg0` - The value to place in register `a0` +/// * `arg1` - The value to place in register `a1` +/// * `arg2` - The value to place in register `a2` +#define __srai_extended3(value, arg0, arg1, arg2) \ + __asm__ __volatile__("mv a0, %0; mv a1, %1; mv a2, %2; srai zero, zero, %3" \ + : \ + : "r"(arg0), "r"(arg1), "r"(arg2), "I"(value) \ + : "a0", "a1", "a2"); + +/// __srai_extended4 +/// +/// Invoke the magic instruction defined by SIMICS for the RISC-V architecture +/// with a specific value of `n` and pseudo-arguments in registers `a0`, `a1`, +/// `a2`, and `a3`. +/// +/// # Arguments +/// +/// * `value` - The value of `n` to use in the magic instruction +/// * `arg0` - The value to place in register `a0` +/// * `arg1` - The value to place in register `a1` +/// * `arg2` - The value to place in register `a2` +/// * `arg3` - The value to place in register `a3` +#define __srai_extended4(value, arg0, arg1, arg2, arg3) \ + __asm__ __volatile__( \ + "mv a0, %0; mv a1, %1; mv a2, %2; mv a3, %3; srai zero, zero, %4" \ + : \ + : "r"(arg0), "r"(arg1), "r"(arg2), "r"(arg3), "I"(value) \ + : "a0", "a1", "a2", "a3"); + +/// Magic value defined by SIMICS as the "leaf" value of a CPUID instruction +/// that is treated as a magic instruction. +#define MAGIC (0x4711U) + +/// The default index number used for magic instructions. All magic instructions +/// support multiple start and stop indices, which defaults to 0 if not +/// specified. +#define DEFAULT_INDEX (0x0000U) + +/// Pseudo-hypercall number to signal the fuzzer to use the first argument to +/// the magic instruction as the pointer to the testcase buffer and the second +/// argument as a pointer to the size of the testcase buffer. +#define N_START_BUFFER_PTR_SIZE_PTR (0x0001U) + +/// HARNESS_START +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The default "index" of 0 will be used. If you need multiple start +/// harnesses compiled into the same binary, you can use the +/// `HARNESS_START_INDEX` macro to specify different indices, then enable them +/// at runtime by configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The size of the buffer pointed to by `size_ptr` will be saved as the +/// maximum testcase size. Each fuzzing iteration, the actual size of the +/// current testcase will be written to `*size_ptr`. +/// +/// # Arguments +/// +/// - `buffer`: The pointer to the testcase buffer +/// - `size_ptr`: The pointer to the size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// size_t size; +/// HARNESS_START(buffer, &size); +/// ``` +#define HARNESS_START(buffer, size_ptr) \ + do { \ + __srai_extended3(N_START_BUFFER_PTR_SIZE_PTR, DEFAULT_INDEX, buffer, \ + size_ptr); \ + } while (0); + +/// HARNESS_START_INDEX +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The index specified by `start_index` will be used. If you need +/// multiple start harnesses compiled into the same binary, you can use this +/// macro to specify different indices, then enable them at runtime by +/// configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The size of the buffer pointed to by `size_ptr` will be saved as the +/// maximum testcase size. Each fuzzing iteration, the actual size of the +/// current testcase will be written to `*size_ptr`. +/// +/// # Arguments +/// +/// - `start_index`: The index to use for this start harness +/// - `buffer`: The pointer to the testcase buffer +/// - `size_ptr`: The pointer to the size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// size_t size; +/// HARNESS_START_INDEX(0x0001U, buffer, &size); +/// ``` +#define HARNESS_START_INDEX(start_index, buffer, size_ptr) \ + do { \ + __srai_extended3(N_START_BUFFER_PTR_SIZE_PTR, start_index, buffer, \ + size_ptr); \ + } while (0); + +/// Pseudo-hypercall number to signal the fuzzer to use the first argument to +/// the magic instruction as the pointer to the testcase buffer and the second +/// argument as the maximum size of the testcase buffer. +#define N_START_BUFFER_PTR_SIZE_VAL (0x0002U) + +/// HARNESS_START_WITH_MAXIMUM_SIZE +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The default "index" of 0 will be used. If you need multiple start +/// harnesses compiled into the same binary, you can use the +/// `HARNESS_START_WITH_MAXIMUM_SIZE_INDEX` macro to specify different indices, +/// then enable them at runtime by configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The `max_size` value will be saved as the maximum testcase size. Fuzzing +/// test cases will be truncated to this size before being written to the +/// buffer. +/// +/// # Arguments +/// +/// - `buffer`: The pointer to the testcase buffer +/// - `max_size`: The maximum size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// HARNESS_START_WITH_MAXIMUM_SIZE(buffer, 1024); +/// ``` +#define HARNESS_START_WITH_MAXIMUM_SIZE(buffer, max_size) \ + do { \ + __srai_extended3(N_START_BUFFER_PTR_SIZE_VAL, DEFAULT_INDEX, buffer, \ + max_size); \ + } while (0); + +/// HARNESS_START_WITH_MAXIMUM_SIZE_INDEX +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The index specified by `start_index` will be used. If you need +/// multiple start harnesses compiled into the same binary, you can use this +/// macro to specify different indices, then enable them at runtime by +/// configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The `max_size` value will be saved as the maximum testcase size. Fuzzing +/// test cases will be truncated to this size before being written to the +/// buffer. +/// +/// # Arguments +/// +/// - `start_index`: The index to use for this start harness +/// - `buffer`: The pointer to the testcase buffer +/// - `max_size`: The maximum size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// HARNESS_START_WITH_MAXIMUM_SIZE_INDEX(0x0001U, buffer, 1024); +/// ``` +#define HARNESS_START_WITH_MAXIMUM_SIZE_INDEX(start_index, buffer, max_size) \ + do { \ + __srai_extended3(N_START_BUFFER_PTR_SIZE_VAL, start_index, buffer, \ + max_size); \ + } while (0); + +/// Pseudo-hypercall number to signal the fuzzer to use the first argument to +/// the magic instruction as the pointer to the testcase buffer, the second +/// argument as a pointer to the size of the testcase buffer, and the third +/// argument as the maximum size of the testcase buffer. +#define N_START_BUFFER_PTR_SIZE_PTR_VAL (0x0003U) + +/// HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The default "index" of 0 will be used. If you need multiple start +/// harnesses compiled into the same binary, you can use the +/// `HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR_INDEX` macro to specify different +/// indices, then enable them at runtime by configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The address `size_ptr` will be saved. Each fuzzing iteration, the actual +/// size of the current testcase will be written to `*size_ptr`. +/// - The `max_size` value will be saved as the maximum testcase size. Fuzzing +/// test cases will be truncated to this size before being written to the +/// buffer. +/// +/// # Arguments +/// +/// - `buffer`: The pointer to the testcase buffer +/// - `size_ptr`: The pointer to the size of the testcase buffer +/// - `max_size`: The maximum size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// size_t size; +/// HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR(buffer, &size, 1024); +/// ``` +#define HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR(buffer, size_ptr, max_size) \ + do { \ + __srai_extended4(N_START_BUFFER_PTR_SIZE_PTR_VAL, DEFAULT_INDEX, buffer, \ + size_ptr, max_size); \ + } while (0); + +/// HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR_INDEX +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The index specified by `start_index` will be used. If you need +/// multiple start harnesses compiled into the same binary, you can use this +/// macro to specify different indices, then enable them at runtime by +/// configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The address `size_ptr` will be saved. Each fuzzing iteration, the actual +/// size of the current testcase will be written to `*size_ptr`. +/// - The `max_size` value will be saved as the maximum testcase size. Fuzzing +/// test cases will be truncated to this size before being written to the +/// buffer. +/// +/// # Arguments +/// +/// - `start_index`: The index to use for this start harness +/// - `buffer`: The pointer to the testcase buffer +/// - `size_ptr`: The pointer to the size of the testcase buffer +/// - `max_size`: The maximum size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// size_t size; +/// HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR_INDEX(0x0001U, buffer, &size, 1024); +/// ``` +#define HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR_INDEX(start_index, buffer, \ + size_ptr, max_size) \ + do { \ + __srai_extended4(N_START_BUFFER_PTR_SIZE_PTR_VAL, start_index, buffer, \ + size_ptr, max_size); \ + } while (0); + +/// Pseudo-hypercall number to signal the fuzzer to stop the current fuzzing +/// iteration and reset to the beginning of the fuzzing loop with a "normal" +/// stop status, indicating no solution has occurred. +#define N_STOP_NORMAL (0x0004U) + +/// HARNESS_STOP +/// +/// Signal the fuzzer to stop and reset to the beginning of the fuzzing loop +/// with a "normal" stop status, indicating no solution has occurred. The +/// default index of 0 will be used. If you need to differentiate between +/// multiple stop harnesses compiled into the same binary, you can use the +/// `HARNESS_STOP_INDEX` macro to specify different indices, then enable them at +/// runtime by configuring the fuzzer. +/// +/// # Example +/// +/// ``` +/// HARNESS_STOP(); +/// ``` +#define HARNESS_STOP() \ + do { \ + __srai_extended1(N_STOP_NORMAL, DEFAULT_INDEX); \ + } while (0); + +/// HARNESS_STOP_INDEX +/// +/// Signal the fuzzer to stop and reset to the beginning of the fuzzing loop +/// with a "normal" stop status, indicating no solution has occurred. The index +/// specified by `stop_index` will be used. If you need to differentiate between +/// multiple stop harnesses compiled into the same binary, you can use this +/// macro to specify different indices, then enable them at runtime by +/// configuring the fuzzer. +/// +/// # Arguments +/// +/// - `stop_index`: The index to use for this stop harness +/// +/// # Example +/// +/// ``` +/// HARNESS_STOP_INDEX(0x0001U); +/// ``` +#define HARNESS_STOP_INDEX(stop_index) \ + do { \ + __srai_extended1(N_STOP_NORMAL, stop_index); \ + } while (0); + +/// Pseudo-hypercall number to signal the fuzzer that a custom assertion has +/// occurred, and the fuzzer should stop the current fuzzing iteration and reset +/// to the beginning of the fuzzing loop with a "solution" stop status. +#define N_STOP_ASSERT (0x0005U) + +/// HARNESS_ASSERT +/// +/// Signal the fuzzer that a custom assertion has occurred, and the fuzzer +/// should stop the current fuzzing iteration and reset to the beginning of the +/// fuzzing loop with a "solution" stop status. The default index of 0 will be +/// used. If you need to differentiate between multiple assertion harnesses +/// compiled into the same binary, you can use the `HARNESS_ASSERT_INDEX` macro +/// to specify different indices, then enable them at runtime by configuring the +/// fuzzer. +/// +/// # Example +/// +/// ``` +/// HARNESS_ASSERT(); +/// ``` +#define HARNESS_ASSERT() \ + do { \ + __srai_extended1(N_STOP_ASSERT, DEFAULT_INDEX); \ + } while (0); + +/// HARNESS_ASSERT_INDEX +/// +/// Signal the fuzzer that a custom assertion has occurred, and the fuzzer +/// should stop the current fuzzing iteration and reset to the beginning of the +/// fuzzing loop with a "solution" stop status. The index specified by +/// `assert_index` will be used. If you need to differentiate between multiple +/// assertion harnesses compiled into the same binary, you can use this macro to +/// specify different indices, then enable them at runtime by configuring the +/// fuzzer. +/// +/// # Arguments +/// +/// - `assert_index`: The index to use for this assertion harness +/// +/// # Example +/// +/// ``` +/// HARNESS_ASSERT_INDEX(0x0001U); +/// ``` +#define HARNESS_ASSERT_INDEX(assert_index) \ + do { \ + __srai_extended1(N_STOP_ASSERT, assert_index); \ + } while (0); + +#endif // TSFFS_H +#elif __riscv && __LP64__ +// Copyright (C) 2024 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 + +/// Definitions and macros for compiled-in harnessing of C and C++ target +/// software for the RISC-V (32-bit) architecture + +#ifndef TSFFS_H +#define TSFFS_H + +/// Define common with LibFuzzer and other fuzzers to allow code that is +/// fuzzing-specific to be left in the codebase. See +/// https://llvm.org/docs/LibFuzzer.html#id35 for more information +#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION +#define FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION (1) +#endif // FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION + +/// __srai +/// +/// Invoke the magic instruction defined by SIMICS for the RISC-V architecture +/// with a specific value of `n` +/// +/// # Arguments +/// +/// * `value` - The value of `n` to use in the magic instruction +#define __srai(value) \ + __asm__ __volatile__("srai zero, zero, %0" : : "I"(value) :) + +/// __srai_extended1 +/// +/// Invoke the magic instruction defined by SIMICS for the RISC-V architecture +/// with a specific value of `n` and a pseudo-argument in register `a0`. +/// +/// # Arguments +/// +/// * `value` - The value of `n` to use in the magic instruction +/// * `arg0` - The value to place in register `a0` +#define __srai_extended1(value, arg0) \ + __asm__ __volatile__("mv a0, %0; srai zero, zero, %1" \ + : \ + : "r"(arg0), "I"(value) \ + : "a0"); + +/// __srai_extended2 +/// +/// Invoke the magic instruction defined by SIMICS for the RISC-V architecture +/// with a specific value of `n` and pseudo-arguments in registers `a0` and +/// `a1`. +/// +/// # Arguments +/// +/// * `value` - The value of `n` to use in the magic instruction +/// * `arg0` - The value to place in register `a0` +/// * `arg1` - The value to place in register `a1` +#define __srai_extended2(value, arg0, arg1) \ + __asm__ __volatile__("mv a0, %0; mv a1, %1; srai zero, zero, %2" \ + : \ + : "r"(arg0), "r"(arg1), "I"(value) \ + : "a0", "a1"); + +/// __srai_extended3 +/// +/// Invoke the magic instruction defined by SIMICS for the RISC-V architecture +/// with a specific value of `n` and pseudo-arguments in registers `a0`, `a1`, +/// and `a2`. +/// +/// # Arguments +/// +/// * `value` - The value of `n` to use in the magic instruction +/// * `arg0` - The value to place in register `a0` +/// * `arg1` - The value to place in register `a1` +/// * `arg2` - The value to place in register `a2` +#define __srai_extended3(value, arg0, arg1, arg2) \ + __asm__ __volatile__("mv a0, %0; mv a1, %1; mv a2, %2; srai zero, zero, %3" \ + : \ + : "r"(arg0), "r"(arg1), "r"(arg2), "I"(value) \ + : "a0", "a1", "a2"); + +/// __srai_extended4 +/// +/// Invoke the magic instruction defined by SIMICS for the RISC-V architecture +/// with a specific value of `n` and pseudo-arguments in registers `a0`, `a1`, +/// `a2`, and `a3`. +/// +/// # Arguments +/// +/// * `value` - The value of `n` to use in the magic instruction +/// * `arg0` - The value to place in register `a0` +/// * `arg1` - The value to place in register `a1` +/// * `arg2` - The value to place in register `a2` +/// * `arg3` - The value to place in register `a3` +#define __srai_extended4(value, arg0, arg1, arg2, arg3) \ + __asm__ __volatile__( \ + "mv a0, %0; mv a1, %1; mv a2, %2; mv a3, %3; srai zero, zero, %4" \ + : \ + : "r"(arg0), "r"(arg1), "r"(arg2), "r"(arg3), "I"(value) \ + : "a0", "a1", "a2", "a3"); + +/// Magic value defined by SIMICS as the "leaf" value of a CPUID instruction +/// that is treated as a magic instruction. +#define MAGIC (0x4711U) + +/// The default index number used for magic instructions. All magic instructions +/// support multiple start and stop indices, which defaults to 0 if not +/// specified. +#define DEFAULT_INDEX (0x0000U) + +/// Pseudo-hypercall number to signal the fuzzer to use the first argument to +/// the magic instruction as the pointer to the testcase buffer and the second +/// argument as a pointer to the size of the testcase buffer. +#define N_START_BUFFER_PTR_SIZE_PTR (0x0001U) + +/// HARNESS_START +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The default "index" of 0 will be used. If you need multiple start +/// harnesses compiled into the same binary, you can use the +/// `HARNESS_START_INDEX` macro to specify different indices, then enable them +/// at runtime by configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The size of the buffer pointed to by `size_ptr` will be saved as the +/// maximum testcase size. Each fuzzing iteration, the actual size of the +/// current testcase will be written to `*size_ptr`. +/// +/// # Arguments +/// +/// - `buffer`: The pointer to the testcase buffer +/// - `size_ptr`: The pointer to the size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// size_t size; +/// HARNESS_START(buffer, &size); +/// ``` +#define HARNESS_START(buffer, size_ptr) \ + do { \ + __srai_extended3(N_START_BUFFER_PTR_SIZE_PTR, DEFAULT_INDEX, buffer, size_ptr); \ + } while (0); + +/// HARNESS_START_INDEX +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The index specified by `start_index` will be used. If you need +/// multiple start harnesses compiled into the same binary, you can use this +/// macro to specify different indices, then enable them at runtime by +/// configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The size of the buffer pointed to by `size_ptr` will be saved as the +/// maximum testcase size. Each fuzzing iteration, the actual size of the +/// current testcase will be written to `*size_ptr`. +/// +/// # Arguments +/// +/// - `start_index`: The index to use for this start harness +/// - `buffer`: The pointer to the testcase buffer +/// - `size_ptr`: The pointer to the size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// size_t size; +/// HARNESS_START_INDEX(0x0001U, buffer, &size); +/// ``` +#define HARNESS_START_INDEX(start_index, buffer, size_ptr) \ + do { \ + __srai_extended3(N_START_BUFFER_PTR_SIZE_PTR, start_index, buffer, size_ptr); \ + } while (0); + +/// Pseudo-hypercall number to signal the fuzzer to use the first argument to +/// the magic instruction as the pointer to the testcase buffer and the second +/// argument as the maximum size of the testcase buffer. +#define N_START_BUFFER_PTR_SIZE_VAL (0x0002U) + +/// HARNESS_START_WITH_MAXIMUM_SIZE +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The default "index" of 0 will be used. If you need multiple start +/// harnesses compiled into the same binary, you can use the +/// `HARNESS_START_WITH_MAXIMUM_SIZE_INDEX` macro to specify different indices, +/// then enable them at runtime by configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The `max_size` value will be saved as the maximum testcase size. Fuzzing +/// test cases will be truncated to this size before being written to the +/// buffer. +/// +/// # Arguments +/// +/// - `buffer`: The pointer to the testcase buffer +/// - `max_size`: The maximum size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// HARNESS_START_WITH_MAXIMUM_SIZE(buffer, 1024); +/// ``` +#define HARNESS_START_WITH_MAXIMUM_SIZE(buffer, max_size) \ + do { \ + __srai_extended3(N_START_BUFFER_PTR_SIZE_VAL, DEFAULT_INDEX, buffer, max_size); \ + } while (0); + +/// HARNESS_START_WITH_MAXIMUM_SIZE_INDEX +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The index specified by `start_index` will be used. If you need +/// multiple start harnesses compiled into the same binary, you can use this +/// macro to specify different indices, then enable them at runtime by +/// configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The `max_size` value will be saved as the maximum testcase size. Fuzzing +/// test cases will be truncated to this size before being written to the +/// buffer. +/// +/// # Arguments +/// +/// - `start_index`: The index to use for this start harness +/// - `buffer`: The pointer to the testcase buffer +/// - `max_size`: The maximum size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// HARNESS_START_WITH_MAXIMUM_SIZE_INDEX(0x0001U, buffer, 1024); +/// ``` +#define HARNESS_START_WITH_MAXIMUM_SIZE_INDEX(start_index, buffer, max_size) \ + do { \ + __srai_extended3(N_START_BUFFER_PTR_SIZE_VAL, start_index, buffer, max_size); \ + } while (0); + +/// Pseudo-hypercall number to signal the fuzzer to use the first argument to +/// the magic instruction as the pointer to the testcase buffer, the second +/// argument as a pointer to the size of the testcase buffer, and the third +/// argument as the maximum size of the testcase buffer. +#define N_START_BUFFER_PTR_SIZE_PTR_VAL (0x0003U) + +/// HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The default "index" of 0 will be used. If you need multiple start +/// harnesses compiled into the same binary, you can use the +/// `HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR_INDEX` macro to specify different +/// indices, then enable them at runtime by configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The address `size_ptr` will be saved. Each fuzzing iteration, the actual +/// size of the current testcase will be written to `*size_ptr`. +/// - The `max_size` value will be saved as the maximum testcase size. Fuzzing +/// test cases will be truncated to this size before being written to the +/// buffer. +/// +/// # Arguments +/// +/// - `buffer`: The pointer to the testcase buffer +/// - `size_ptr`: The pointer to the size of the testcase buffer +/// - `max_size`: The maximum size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// size_t size; +/// HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR(buffer, &size, 1024); +/// ``` +#define HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR(buffer, size_ptr, max_size) \ + do { \ + __srai_extended4(N_START_BUFFER_PTR_SIZE_PTR_VAL, DEFAULT_INDEX, buffer, size_ptr, max_size); \ + } while (0); + +/// HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR_INDEX +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The index specified by `start_index` will be used. If you need +/// multiple start harnesses compiled into the same binary, you can use this +/// macro to specify different indices, then enable them at runtime by +/// configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The address `size_ptr` will be saved. Each fuzzing iteration, the actual +/// size of the current testcase will be written to `*size_ptr`. +/// - The `max_size` value will be saved as the maximum testcase size. Fuzzing +/// test cases will be truncated to this size before being written to the +/// buffer. +/// +/// # Arguments +/// +/// - `start_index`: The index to use for this start harness +/// - `buffer`: The pointer to the testcase buffer +/// - `size_ptr`: The pointer to the size of the testcase buffer +/// - `max_size`: The maximum size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// size_t size; +/// HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR_INDEX(0x0001U, buffer, &size, 1024); +/// ``` +#define HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR_INDEX(start_index, buffer, \ + size_ptr, max_size) \ + do { \ + __srai_extended4(N_START_BUFFER_PTR_SIZE_PTR_VAL, start_index, buffer, size_ptr, max_size); \ + } while (0); + +/// Pseudo-hypercall number to signal the fuzzer to stop the current fuzzing +/// iteration and reset to the beginning of the fuzzing loop with a "normal" +/// stop status, indicating no solution has occurred. +#define N_STOP_NORMAL (0x0004U) + +/// HARNESS_STOP +/// +/// Signal the fuzzer to stop and reset to the beginning of the fuzzing loop +/// with a "normal" stop status, indicating no solution has occurred. The +/// default index of 0 will be used. If you need to differentiate between +/// multiple stop harnesses compiled into the same binary, you can use the +/// `HARNESS_STOP_INDEX` macro to specify different indices, then enable them at +/// runtime by configuring the fuzzer. +/// +/// # Example +/// +/// ``` +/// HARNESS_STOP(); +/// ``` +#define HARNESS_STOP() \ + do { \ + __srai_extended1(N_STOP_NORMAL, DEFAULT_INDEX); \ + } while (0); + +/// HARNESS_STOP_INDEX +/// +/// Signal the fuzzer to stop and reset to the beginning of the fuzzing loop +/// with a "normal" stop status, indicating no solution has occurred. The index +/// specified by `stop_index` will be used. If you need to differentiate between +/// multiple stop harnesses compiled into the same binary, you can use this +/// macro to specify different indices, then enable them at runtime by +/// configuring the fuzzer. +/// +/// # Arguments +/// +/// - `stop_index`: The index to use for this stop harness +/// +/// # Example +/// +/// ``` +/// HARNESS_STOP_INDEX(0x0001U); +/// ``` +#define HARNESS_STOP_INDEX(stop_index) \ + do { \ + __srai_extended1(N_STOP_NORMAL, stop_index); \ + } while (0); + +/// Pseudo-hypercall number to signal the fuzzer that a custom assertion has +/// occurred, and the fuzzer should stop the current fuzzing iteration and reset +/// to the beginning of the fuzzing loop with a "solution" stop status. +#define N_STOP_ASSERT (0x0005U) + +/// HARNESS_ASSERT +/// +/// Signal the fuzzer that a custom assertion has occurred, and the fuzzer +/// should stop the current fuzzing iteration and reset to the beginning of the +/// fuzzing loop with a "solution" stop status. The default index of 0 will be +/// used. If you need to differentiate between multiple assertion harnesses +/// compiled into the same binary, you can use the `HARNESS_ASSERT_INDEX` macro +/// to specify different indices, then enable them at runtime by configuring the +/// fuzzer. +/// +/// # Example +/// +/// ``` +/// HARNESS_ASSERT(); +/// ``` +#define HARNESS_ASSERT() \ + do { \ + __srai_extended1(N_STOP_ASSERT, DEFAULT_INDEX); \ + } while (0); + +/// HARNESS_ASSERT_INDEX +/// +/// Signal the fuzzer that a custom assertion has occurred, and the fuzzer +/// should stop the current fuzzing iteration and reset to the beginning of the +/// fuzzing loop with a "solution" stop status. The index specified by +/// `assert_index` will be used. If you need to differentiate between multiple +/// assertion harnesses compiled into the same binary, you can use this macro to +/// specify different indices, then enable them at runtime by configuring the +/// fuzzer. +/// +/// # Arguments +/// +/// - `assert_index`: The index to use for this assertion harness +/// +/// # Example +/// +/// ``` +/// HARNESS_ASSERT_INDEX(0x0001U); +/// ``` +#define HARNESS_ASSERT_INDEX(assert_index) \ + do { \ + __srai_extended1(N_STOP_ASSERT, assert_index); \ + } while (0); + +#endif // TSFFS_H +#elif __aarch64__ +// Copyright (C) 2024 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 + +/// Definitions and macros for compiled-in harnessing of C and C++ target +/// software for the RISC-V (32-bit) architecture + +#ifndef TSFFS_H +#define TSFFS_H + +/// Define common with LibFuzzer and other fuzzers to allow code that is +/// fuzzing-specific to be left in the codebase. See +/// https://llvm.org/docs/LibFuzzer.html#id35 for more information +#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION +#define FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION (1) +#endif // FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION + +#define __stringify(x) #x +#define __tostring(x) __stringify(x) + +/// __orr +/// +/// Invoke the magic instruction defined by SIMICS for the RISC-V architecture +/// with a specific value of `n` +/// +/// # Arguments +/// +/// * `value` - The value of `n` to use in the magic instruction +#define __orr(value) \ + __asm__ __volatile__("orr x" __tostring(value) ", x" __tostring( \ + value) ", x" __tostring(value)); + +/// __orr_extended1 +/// +/// Invoke the magic instruction defined by SIMICS for the RISC-V architecture +/// with a specific value of `n` and a pseudo-argument in register `r10`. +/// +/// # Arguments +/// +/// * `value` - The value of `n` to use in the magic instruction +/// * `arg0` - The value to place in register `r10` +#define __orr_extended1(value, arg0) \ + __asm__ __volatile__( \ + "mov x28, %0; orr x" __tostring(value) ", x" __tostring( \ + value) ", x" __tostring(value) \ + : \ + : "g"(arg0)); + +/// __orr_extended2 +/// +/// Invoke the magic instruction defined by SIMICS for the RISC-V architecture +/// with a specific value of `n` and two pseudo-arguments in registers `r10` and +/// `r9`. +/// +/// # Arguments +/// +/// * `value` - The value of `n` to use in the magic instruction +/// * `arg0` - The value to place in register `r10` +/// * `arg1` - The value to place in register `r9` +#define __orr_extended2(value, arg0, arg1) \ + __asm__ __volatile__( \ + "mov x28, %0; mov x27, %1; orr x" __tostring(value) ", x" __tostring( \ + value) ", x" __tostring(value) \ + : \ + : "r"(arg0), "r"(arg1)); + +/// __orr_extended3 +/// +/// Invoke the magic instruction defined by SIMICS for the RISC-V architecture +/// with a specific value of `n` and three pseudo-arguments in registers `r10`, +/// `r9`, and `r8`. +/// +/// # Arguments +/// +/// * `value` - The value of `n` to use in the magic instruction +/// * `arg0` - The value to place in register `r10` +/// * `arg1` - The value to place in register `r9` +/// * `arg2` - The value to place in register `r8` +#define __orr_extended3(value, arg0, arg1, arg2) \ + __asm__ __volatile__( \ + "mov x28, %0; mov x27, %1; mov x26, %2; orr x" __tostring( \ + value) ", x" __tostring(value) ", x" __tostring(value) \ + : \ + : "r"(arg0), "r"(arg1), "r"(arg2)); + +/// __orr_extended4 +/// +/// Invoke the magic instruction defined by SIMICS for the RISC-V architecture +/// with a specific value of `n` and four pseudo-arguments in registers `r10`, +/// `r9`, `r8`, and `r7`. +/// +/// # Arguments +/// +/// * `value` - The value of `n` to use in the magic instruction +/// * `arg0` - The value to place in register `r10` +/// * `arg1` - The value to place in register `r9` +/// * `arg2` - The value to place in register `r8` +/// * `arg3` - The value to place in register `r7` +#define __orr_extended4(value, arg0, arg1, arg2, arg3) \ + __asm__ __volatile__( \ + "mov x28, %0; mov x27, %1; mov x26, %2; mov x25, %3; " \ + "orr x" __tostring(value) ", x" __tostring(value) ", x" __tostring( \ + value) \ + : \ + : "r"(arg0), "r"(arg1), "r"(arg2), "r"(arg3)); + +/// The default index number used for magic instructions. All magic instructions +/// support multiple start and stop indices, which defaults to 0 if not +/// specified. +#define DEFAULT_INDEX (0x0000U) + +/// Pseudo-hypercall number to signal the fuzzer to use the first argument to +/// the magic instruction as the pointer to the testcase buffer and the second +/// argument as a pointer to the size of the testcase buffer. +#define N_START_BUFFER_PTR_SIZE_PTR 1 + +/// HARNESS_START +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The default "index" of 0 will be used. If you need multiple start +/// harnesses compiled into the same binary, you can use the +/// `HARNESS_START_INDEX` macro to specify different indices, then enable them +/// at runtime by configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The size of the buffer pointed to by `size_ptr` will be saved as the +/// maximum testcase size. Each fuzzing iteration, the actual size of the +/// current testcase will be written to `*size_ptr`. +/// +/// # Arguments +/// +/// - `buffer`: The pointer to the testcase buffer +/// - `size_ptr`: The pointer to the size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// size_t size; +/// HARNESS_START(buffer, &size); +/// ``` +#define HARNESS_START(buffer, size_ptr) \ + do { \ + __orr_extended3(N_START_BUFFER_PTR_SIZE_PTR, DEFAULT_INDEX, buffer, \ + size_ptr); \ + } while (0); + +/// HARNESS_START_INDEX +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The index specified by `start_index` will be used. If you need +/// multiple start harnesses compiled into the same binary, you can use this +/// macro to specify different indices, then enable them at runtime by +/// configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The size of the buffer pointed to by `size_ptr` will be saved as the +/// maximum testcase size. Each fuzzing iteration, the actual size of the +/// current testcase will be written to `*size_ptr`. +/// +/// # Arguments +/// +/// - `start_index`: The index to use for this start harness +/// - `buffer`: The pointer to the testcase buffer +/// - `size_ptr`: The pointer to the size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// size_t size; +/// HARNESS_START_INDEX(0x0001U, buffer, &size); +/// ``` +#define HARNESS_START_INDEX(start_index, buffer, size_ptr) \ + do { \ + __orr_extended3(N_START_BUFFER_PTR_SIZE_PTR, start_index, buffer, \ + size_ptr); \ + } while (0); + +/// Pseudo-hypercall number to signal the fuzzer to use the first argument to +/// the magic instruction as the pointer to the testcase buffer and the second +/// argument as the maximum size of the testcase buffer. +#define N_START_BUFFER_PTR_SIZE_VAL 2 + +/// HARNESS_START_WITH_MAXIMUM_SIZE +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The default "index" of 0 will be used. If you need multiple start +/// harnesses compiled into the same binary, you can use the +/// `HARNESS_START_WITH_MAXIMUM_SIZE_INDEX` macro to specify different indices, +/// then enable them at runtime by configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The `max_size` value will be saved as the maximum testcase size. Fuzzing +/// test cases will be truncated to this size before being written to the +/// buffer. +/// +/// # Arguments +/// +/// - `buffer`: The pointer to the testcase buffer +/// - `max_size`: The maximum size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// HARNESS_START_WITH_MAXIMUM_SIZE(buffer, 1024); +/// ``` +#define HARNESS_START_WITH_MAXIMUM_SIZE(buffer, max_size) \ + do { \ + __orr_extended3(N_START_BUFFER_PTR_SIZE_VAL, DEFAULT_INDEX, buffer, \ + max_size); \ + } while (0); + +/// HARNESS_START_WITH_MAXIMUM_SIZE_INDEX +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The index specified by `start_index` will be used. If you need +/// multiple start harnesses compiled into the same binary, you can use this +/// macro to specify different indices, then enable them at runtime by +/// configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The `max_size` value will be saved as the maximum testcase size. Fuzzing +/// test cases will be truncated to this size before being written to the +/// buffer. +/// +/// # Arguments +/// +/// - `start_index`: The index to use for this start harness +/// - `buffer`: The pointer to the testcase buffer +/// - `max_size`: The maximum size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// HARNESS_START_WITH_MAXIMUM_SIZE_INDEX(0x0001U, buffer, 1024); +/// ``` +#define HARNESS_START_WITH_MAXIMUM_SIZE_INDEX(start_index, buffer, max_size) \ + do { \ + __orr_extended3(N_START_BUFFER_PTR_SIZE_VAL, start_index, buffer, \ + max_size); \ + } while (0); + +/// Pseudo-hypercall number to signal the fuzzer to use the first argument to +/// the magic instruction as the pointer to the testcase buffer, the second +/// argument as a pointer to the size of the testcase buffer, and the third +/// argument as the maximum size of the testcase buffer. +#define N_START_BUFFER_PTR_SIZE_PTR_VAL 3 + +/// HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The default "index" of 0 will be used. If you need multiple start +/// harnesses compiled into the same binary, you can use the +/// `HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR_INDEX` macro to specify different +/// indices, then enable them at runtime by configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The address `size_ptr` will be saved. Each fuzzing iteration, the actual +/// size of the current testcase will be written to `*size_ptr`. +/// - The `max_size` value will be saved as the maximum testcase size. Fuzzing +/// test cases will be truncated to this size before being written to the +/// buffer. +/// +/// # Arguments +/// +/// - `buffer`: The pointer to the testcase buffer +/// - `size_ptr`: The pointer to the size of the testcase buffer +/// - `max_size`: The maximum size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// size_t size; +/// HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR(buffer, &size, 1024); +/// ``` +#define HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR(buffer, size_ptr, max_size) \ + do { \ + __orr_extended4(N_START_BUFFER_PTR_SIZE_PTR_VAL, DEFAULT_INDEX, buffer, \ + size_ptr, max_size); \ + } while (0); + +/// HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR_INDEX +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The index specified by `start_index` will be used. If you need +/// multiple start harnesses compiled into the same binary, you can use this +/// macro to specify different indices, then enable them at runtime by +/// configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The address `size_ptr` will be saved. Each fuzzing iteration, the actual +/// size of the current testcase will be written to `*size_ptr`. +/// - The `max_size` value will be saved as the maximum testcase size. Fuzzing +/// test cases will be truncated to this size before being written to the +/// buffer. +/// +/// # Arguments +/// +/// - `start_index`: The index to use for this start harness +/// - `buffer`: The pointer to the testcase buffer +/// - `size_ptr`: The pointer to the size of the testcase buffer +/// - `max_size`: The maximum size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// size_t size; +/// HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR_INDEX(0x0001U, buffer, &size, 1024); +/// ``` +#define HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR_INDEX(start_index, buffer, \ + size_ptr, max_size) \ + do { \ + __orr_extended4(N_START_BUFFER_PTR_SIZE_PTR_VAL, start_index, buffer, \ + size_ptr, max_size); \ + } while (0); + +/// Pseudo-hypercall number to signal the fuzzer to stop the current fuzzing +/// iteration and reset to the beginning of the fuzzing loop with a "normal" +/// stop status, indicating no solution has occurred. +#define N_STOP_NORMAL 4 + +/// HARNESS_STOP +/// +/// Signal the fuzzer to stop and reset to the beginning of the fuzzing loop +/// with a "normal" stop status, indicating no solution has occurred. The +/// default index of 0 will be used. If you need to differentiate between +/// multiple stop harnesses compiled into the same binary, you can use the +/// `HARNESS_STOP_INDEX` macro to specify different indices, then enable them at +/// runtime by configuring the fuzzer. +/// +/// # Example +/// +/// ``` +/// HARNESS_STOP(); +/// ``` +#define HARNESS_STOP() \ + do { \ + __orr_extended1(N_STOP_NORMAL, DEFAULT_INDEX); \ + } while (0); + +/// HARNESS_STOP_INDEX +/// +/// Signal the fuzzer to stop and reset to the beginning of the fuzzing loop +/// with a "normal" stop status, indicating no solution has occurred. The index +/// specified by `stop_index` will be used. If you need to differentiate between +/// multiple stop harnesses compiled into the same binary, you can use this +/// macro to specify different indices, then enable them at runtime by +/// configuring the fuzzer. +/// +/// # Arguments +/// +/// - `stop_index`: The index to use for this stop harness +/// +/// # Example +/// +/// ``` +/// HARNESS_STOP_INDEX(0x0001U); +/// ``` +#define HARNESS_STOP_INDEX(stop_index) \ + do { \ + __orr_extended1(N_STOP_NORMAL, stop_index); \ + } while (0); + +/// Pseudo-hypercall number to signal the fuzzer that a custom assertion has +/// occurred, and the fuzzer should stop the current fuzzing iteration and reset +/// to the beginning of the fuzzing loop with a "solution" stop status. +#define N_STOP_ASSERT 5 + +/// HARNESS_ASSERT +/// +/// Signal the fuzzer that a custom assertion has occurred, and the fuzzer +/// should stop the current fuzzing iteration and reset to the beginning of the +/// fuzzing loop with a "solution" stop status. The default index of 0 will be +/// used. If you need to differentiate between multiple assertion harnesses +/// compiled into the same binary, you can use the `HARNESS_ASSERT_INDEX` macro +/// to specify different indices, then enable them at runtime by configuring the +/// fuzzer. +/// +/// # Example +/// +/// ``` +/// HARNESS_ASSERT(); +/// ``` +#define HARNESS_ASSERT() \ + do { \ + __orr_extended1(N_STOP_ASSERT, DEFAULT_INDEX); \ + } while (0); + +/// HARNESS_ASSERT_INDEX +/// +/// Signal the fuzzer that a custom assertion has occurred, and the fuzzer +/// should stop the current fuzzing iteration and reset to the beginning of the +/// fuzzing loop with a "solution" stop status. The index specified by +/// `assert_index` will be used. If you need to differentiate between multiple +/// assertion harnesses compiled into the same binary, you can use this macro to +/// specify different indices, then enable them at runtime by configuring the +/// fuzzer. +/// +/// # Arguments +/// +/// - `assert_index`: The index to use for this assertion harness +/// +/// # Example +/// +/// ``` +/// HARNESS_ASSERT_INDEX(0x0001U); +/// ``` +#define HARNESS_ASSERT_INDEX(assert_index) \ + do { \ + __orr_extended1(N_STOP_ASSERT, assert_index); \ + } while (0); + +#endif // TSFFS_H +#elif __arm__ +// Copyright (C) 2024 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 + +/// Definitions and macros for compiled-in harnessing of C and C++ target +/// software for the RISC-V (32-bit) architecture + +#ifndef TSFFS_H +#define TSFFS_H + +/// Define common with LibFuzzer and other fuzzers to allow code that is +/// fuzzing-specific to be left in the codebase. See +/// https://llvm.org/docs/LibFuzzer.html#id35 for more information +#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION +#define FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION (1) +#endif // FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION + +#define __stringify(x) #x +#define __tostring(x) __stringify(x) + +/// __orr +/// +/// Invoke the magic instruction defined by SIMICS for the RISC-V architecture +/// with a specific value of `n` +/// +/// # Arguments +/// +/// * `value` - The value of `n` to use in the magic instruction +#define __orr(value) \ + __asm__ __volatile__("orr r" __tostring(value) ", r" __tostring( \ + value) ", r" __tostring(value)); + +/// __orr_extended1 +/// +/// Invoke the magic instruction defined by SIMICS for the RISC-V architecture +/// with a specific value of `n` and a pseudo-argument in register `r10`. +/// +/// # Arguments +/// +/// * `value` - The value of `n` to use in the magic instruction +/// * `arg0` - The value to place in register `r10` +#define __orr_extended1(value, arg0) \ + __asm__ __volatile__( \ + "mov r10, %0; orr r" __tostring(value) ", r" __tostring( \ + value) ", r" __tostring(value) \ + : \ + : "r"(arg0)); + +/// __orr_extended2 +/// +/// Invoke the magic instruction defined by SIMICS for the RISC-V architecture +/// with a specific value of `n` and two pseudo-arguments in registers `r10` and +/// `r9`. +/// +/// # Arguments +/// +/// * `value` - The value of `n` to use in the magic instruction +/// * `arg0` - The value to place in register `r10` +/// * `arg1` - The value to place in register `r9` +#define __orr_extended2(value, arg0, arg1) \ + __asm__ __volatile__( \ + "mov r10, %0; mov r9, %1; orr r" __tostring(value) ", r" __tostring( \ + value) ", r" __tostring(value) \ + : \ + : "r"(arg0), "r"(arg1)); + +/// __orr_extended3 +/// +/// Invoke the magic instruction defined by SIMICS for the RISC-V architecture +/// with a specific value of `n` and three pseudo-arguments in registers `r10`, +/// `r9`, and `r8`. +/// +/// # Arguments +/// +/// * `value` - The value of `n` to use in the magic instruction +/// * `arg0` - The value to place in register `r10` +/// * `arg1` - The value to place in register `r9` +/// * `arg2` - The value to place in register `r8` +#define __orr_extended3(value, arg0, arg1, arg2) \ + __asm__ __volatile__( \ + "mov r10, %0; mov r9, %1; mov r8, %2; orr r" __tostring( \ + value) ", r" __tostring(value) ", r" __tostring(value) \ + : \ + : "r"(arg0), "r"(arg1), "r"(arg2)); + +/// __orr_extended4 +/// +/// Invoke the magic instruction defined by SIMICS for the RISC-V architecture +/// with a specific value of `n` and four pseudo-arguments in registers `r10`, +/// `r9`, `r8`, and `r7`. +/// +/// # Arguments +/// +/// * `value` - The value of `n` to use in the magic instruction +/// * `arg0` - The value to place in register `r10` +/// * `arg1` - The value to place in register `r9` +/// * `arg2` - The value to place in register `r8` +/// * `arg3` - The value to place in register `r7` +#define __orr_extended4(value, arg0, arg1, arg2, arg3) \ + __asm__ __volatile__( \ + "mov r10, %0; mov r9, %1; mov r8, %2; mov r7, %3; " \ + "orr r" __tostring(value) ", r" __tostring(value) ", r" __tostring( \ + value) \ + : \ + : "r"(arg0), "r"(arg1), "r"(arg2), "r"(arg3)); + +/// Magic value defined by SIMICS as the "leaf" value of a CPUID instruction +/// that is treated as a magic instruction. +#define MAGIC (0x4711U) + +/// The default index number used for magic instructions. All magic instructions +/// support multiple start and stop indices, which defaults to 0 if not +/// specified. +#define DEFAULT_INDEX (0x0000U) + +/// Pseudo-hypercall number to signal the fuzzer to use the first argument to +/// the magic instruction as the pointer to the testcase buffer and the second +/// argument as a pointer to the size of the testcase buffer. +#define N_START_BUFFER_PTR_SIZE_PTR 1 + +/// HARNESS_START +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The default "index" of 0 will be used. If you need multiple start +/// harnesses compiled into the same binary, you can use the +/// `HARNESS_START_INDEX` macro to specify different indices, then enable them +/// at runtime by configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The size of the buffer pointed to by `size_ptr` will be saved as the +/// maximum testcase size. Each fuzzing iteration, the actual size of the +/// current testcase will be written to `*size_ptr`. +/// +/// # Arguments +/// +/// - `buffer`: The pointer to the testcase buffer +/// - `size_ptr`: The pointer to the size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// size_t size; +/// HARNESS_START(buffer, &size); +/// ``` +#define HARNESS_START(buffer, size_ptr) \ + do { \ + __orr_extended2(N_START_BUFFER_PTR_SIZE_PTR, buffer, size_ptr); \ + } while (0); + +/// Pseudo-hypercall number to signal the fuzzer to use the first argument to +/// the magic instruction as the pointer to the testcase buffer and the second +/// argument as the maximum size of the testcase buffer. +#define N_START_BUFFER_PTR_SIZE_VAL 2 + +/// HARNESS_START_WITH_MAXIMUM_SIZE +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The default "index" of 0 will be used. If you need multiple start +/// harnesses compiled into the same binary, you can use the +/// `HARNESS_START_WITH_MAXIMUM_SIZE_INDEX` macro to specify different indices, +/// then enable them at runtime by configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The `max_size` value will be saved as the maximum testcase size. Fuzzing +/// test cases will be truncated to this size before being written to the +/// buffer. +/// +/// # Arguments +/// +/// - `buffer`: The pointer to the testcase buffer +/// - `max_size`: The maximum size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// HARNESS_START_WITH_MAXIMUM_SIZE(buffer, 1024); +/// ``` +#define HARNESS_START_WITH_MAXIMUM_SIZE(buffer, max_size) \ + do { \ + __orr_extended2(N_START_BUFFER_PTR_SIZE_VAL, buffer, max_size); \ + } while (0); + +/// Pseudo-hypercall number to signal the fuzzer to use the first argument to +/// the magic instruction as the pointer to the testcase buffer, the second +/// argument as a pointer to the size of the testcase buffer, and the third +/// argument as the maximum size of the testcase buffer. +#define N_START_BUFFER_PTR_SIZE_PTR_VAL 3 + +/// HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The default "index" of 0 will be used. If you need multiple start +/// harnesses compiled into the same binary, you can use the +/// `HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR_INDEX` macro to specify different +/// indices, then enable them at runtime by configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The address `size_ptr` will be saved. Each fuzzing iteration, the actual +/// size of the current testcase will be written to `*size_ptr`. +/// - The `max_size` value will be saved as the maximum testcase size. Fuzzing +/// test cases will be truncated to this size before being written to the +/// buffer. +/// +/// # Arguments +/// +/// - `buffer`: The pointer to the testcase buffer +/// - `size_ptr`: The pointer to the size of the testcase buffer +/// - `max_size`: The maximum size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// size_t size; +/// HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR(buffer, &size, 1024); +/// ``` +#define HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR(buffer, size_ptr, max_size) \ + do { \ + __orr_extended3(N_START_BUFFER_PTR_SIZE_PTR_VAL, buffer, size_ptr, \ + max_size); \ + } while (0); + +/// Pseudo-hypercall number to signal the fuzzer to stop the current fuzzing +/// iteration and reset to the beginning of the fuzzing loop with a "normal" +/// stop status, indicating no solution has occurred. +#define N_STOP_NORMAL 4 + +/// HARNESS_STOP +/// +/// Signal the fuzzer to stop and reset to the beginning of the fuzzing loop +/// with a "normal" stop status, indicating no solution has occurred. The +/// default index of 0 will be used. If you need to differentiate between +/// multiple stop harnesses compiled into the same binary, you can use the +/// `HARNESS_STOP_INDEX` macro to specify different indices, then enable them at +/// runtime by configuring the fuzzer. +/// +/// # Example +/// +/// ``` +/// HARNESS_STOP(); +/// ``` +#define HARNESS_STOP() \ + do { \ + __orr(N_STOP_NORMAL); \ + } while (0); + +/// Pseudo-hypercall number to signal the fuzzer that a custom assertion has +/// occurred, and the fuzzer should stop the current fuzzing iteration and reset +/// to the beginning of the fuzzing loop with a "solution" stop status. +#define N_STOP_ASSERT 5 + +/// HARNESS_ASSERT +/// +/// Signal the fuzzer that a custom assertion has occurred, and the fuzzer +/// should stop the current fuzzing iteration and reset to the beginning of the +/// fuzzing loop with a "solution" stop status. The default index of 0 will be +/// used. If you need to differentiate between multiple assertion harnesses +/// compiled into the same binary, you can use the `HARNESS_ASSERT_INDEX` macro +/// to specify different indices, then enable them at runtime by configuring the +/// fuzzer. +/// +/// # Example +/// +/// ``` +/// HARNESS_ASSERT(); +/// ``` +#define HARNESS_ASSERT() \ + do { \ + __orr_extended1(N_STOP_ASSERT, DEFAULT_INDEX); \ + } while (0); + +#endif // TSFFS_H +#else +#error "Unsupported platform!" +#endif +#elif _MSC_VER +#ifndef TSFFS_H +#define TSFFS_H + +/// Define common with LibFuzzer and other fuzzers to allow code that is +/// fuzzing-specific to be left in the codebase. See +/// https://llvm.org/docs/LibFuzzer.html#id35 for more information +#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION +#define FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION (1) +#endif // FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION + +#include + +/// HARNESS_START +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The default "index" of 0 will be used. If you need multiple start +/// harnesses compiled into the same binary, you can use the +/// `HARNESS_START_INDEX` macro to specify different indices, then enable them +/// at runtime by configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The size of the buffer pointed to by `size_ptr` will be saved as the +/// maximum testcase size. Each fuzzing iteration, the actual size of the +/// current testcase will be written to `*size_ptr`. +/// +/// # Arguments +/// +/// - `buffer`: The pointer to the testcase buffer +/// - `size_ptr`: The pointer to the size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// size_t size; +/// HARNESS_START(buffer, &size); +/// ``` +void HARNESS_START(void *buffer, void *size_ptr); + +/// HARNESS_START_INDEX +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The index specified by `start_index` will be used. If you need +/// multiple start harnesses compiled into the same binary, you can use this +/// macro to specify different indices, then enable them at runtime by +/// configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The size of the buffer pointed to by `size_ptr` will be saved as the +/// maximum testcase size. Each fuzzing iteration, the actual size of the +/// current testcase will be written to `*size_ptr`. +/// +/// # Arguments +/// +/// - `start_index`: The index to use for this start harness +/// - `buffer`: The pointer to the testcase buffer +/// - `size_ptr`: The pointer to the size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// size_t size; +/// HARNESS_START_INDEX(0x0001U, buffer, &size); +/// ``` +void HARNESS_START_INDEX(size_t start_index, void *buffer, void *size_ptr); + +/// HARNESS_START_WITH_MAXIMUM_SIZE +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The default "index" of 0 will be used. If you need multiple start +/// harnesses compiled into the same binary, you can use the +/// `HARNESS_START_WITH_MAXIMUM_SIZE_INDEX` macro to specify different indices, +/// then enable them at runtime by configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The `max_size` value will be saved as the maximum testcase size. Fuzzing +/// test cases will be truncated to this size before being written to the +/// buffer. +/// +/// # Arguments +/// +/// - `buffer`: The pointer to the testcase buffer +/// - `max_size`: The maximum size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// HARNESS_START_WITH_MAXIMUM_SIZE(buffer, 1024); +/// ``` +void HARNESS_START_WITH_MAXIMUM_SIZE(void *buffer, size_t max_size); + +/// HARNESS_START_WITH_MAXIMUM_SIZE_INDEX +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The index specified by `start_index` will be used. If you need +/// multiple start harnesses compiled into the same binary, you can use this +/// macro to specify different indices, then enable them at runtime by +/// configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The `max_size` value will be saved as the maximum testcase size. Fuzzing +/// test cases will be truncated to this size before being written to the +/// buffer. +/// +/// # Arguments +/// +/// - `start_index`: The index to use for this start harness +/// - `buffer`: The pointer to the testcase buffer +/// - `max_size`: The maximum size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// HARNESS_START_WITH_MAXIMUM_SIZE_INDEX(0x0001U, buffer, 1024); +/// ``` +void HARNESS_START_WITH_MAXIMUM_SIZE_INDEX(size_t start_index, void *buffer, + size_t max_size); + +/// HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The default "index" of 0 will be used. If you need multiple start +/// harnesses compiled into the same binary, you can use the +/// `HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR_INDEX` macro to specify different +/// indices, then enable them at runtime by configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The address `size_ptr` will be saved. Each fuzzing iteration, the actual +/// size of the current testcase will be written to `*size_ptr`. +/// - The `max_size` value will be saved as the maximum testcase size. Fuzzing +/// test cases will be truncated to this size before being written to the +/// buffer. +/// +/// # Arguments +/// +/// - `buffer`: The pointer to the testcase buffer +/// - `size_ptr`: The pointer to the size of the testcase buffer +/// - `max_size`: The maximum size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// size_t size; +/// HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR(buffer, &size, 1024); +/// ``` +void HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR(void *buffer, void *size_ptr, + size_t max_size); + +/// HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR_INDEX +/// +/// Signal the fuzzer to start the fuzzing loop at the point this macro is +/// called. The index specified by `start_index` will be used. If you need +/// multiple start harnesses compiled into the same binary, you can use this +/// macro to specify different indices, then enable them at runtime by +/// configuring the fuzzer. +/// +/// When this macro is called: +/// +/// - A snapshot will be taken and saved +/// - The buffer pointed to by `buffer` will be saved and used as the testcase +/// buffer. Each +/// fuzzing iteration, a new test case will be written to this buffer. +/// - The address `size_ptr` will be saved. Each fuzzing iteration, the actual +/// size of the current testcase will be written to `*size_ptr`. +/// - The `max_size` value will be saved as the maximum testcase size. Fuzzing +/// test cases will be truncated to this size before being written to the +/// buffer. +/// +/// # Arguments +/// +/// - `start_index`: The index to use for this start harness +/// - `buffer`: The pointer to the testcase buffer +/// - `size_ptr`: The pointer to the size of the testcase buffer +/// - `max_size`: The maximum size of the testcase buffer +/// +/// # Example +/// +/// ``` +/// unsigned char buffer[1024]; +/// size_t size; +/// HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR_INDEX(0x0001U, buffer, &size, 1024); +/// ``` +void HARNESS_START_WITH_MAXIMUM_SIZE_AND_PTR_INDEX(size_t start_index, + void *buffer, void *size_ptr, + size_t max_size); + +/// HARNESS_STOP +/// +/// Signal the fuzzer to stop and reset to the beginning of the fuzzing loop +/// with a "normal" stop status, indicating no solution has occurred. The +/// default index of 0 will be used. If you need to differentiate between +/// multiple stop harnesses compiled into the same binary, you can use the +/// `HARNESS_STOP_INDEX` macro to specify different indices, then enable them at +/// runtime by configuring the fuzzer. +/// +/// # Example +/// +/// ``` +/// HARNESS_STOP(); +/// ``` +void HARNESS_STOP(void); + +/// HARNESS_STOP_INDEX +/// +/// Signal the fuzzer to stop and reset to the beginning of the fuzzing loop +/// with a "normal" stop status, indicating no solution has occurred. The index +/// specified by `stop_index` will be used. If you need to differentiate between +/// multiple stop harnesses compiled into the same binary, you can use this +/// macro to specify different indices, then enable them at runtime by +/// configuring the fuzzer. +/// +/// # Arguments +/// +/// - `stop_index`: The index to use for this stop harness +/// +/// # Example +/// +/// ``` +/// HARNESS_STOP_INDEX(0x0001U); +/// ``` +void HARNESS_STOP_INDEX(size_t stop_index); + +/// HARNESS_ASSERT +/// +/// Signal the fuzzer that a custom assertion has occurred, and the fuzzer +/// should stop the current fuzzing iteration and reset to the beginning of the +/// fuzzing loop with a "solution" stop status. The default index of 0 will be +/// used. If you need to differentiate between multiple assertion harnesses +/// compiled into the same binary, you can use the `HARNESS_ASSERT_INDEX` macro +/// to specify different indices, then enable them at runtime by configuring the +/// fuzzer. +/// +/// # Example +/// +/// ``` +/// HARNESS_ASSERT(); +/// ``` +void HARNESS_ASSERT(void); + +/// HARNESS_ASSERT_INDEX +/// +/// Signal the fuzzer that a custom assertion has occurred, and the fuzzer +/// should stop the current fuzzing iteration and reset to the beginning of the +/// fuzzing loop with a "solution" stop status. The index specified by +/// `assert_index` will be used. If you need to differentiate between multiple +/// assertion harnesses compiled into the same binary, you can use this macro to +/// specify different indices, then enable them at runtime by configuring the +/// fuzzer. +/// +/// # Arguments +/// +/// - `assert_index`: The index to use for this assertion harness +/// +/// # Example +/// +/// ``` +/// HARNESS_ASSERT_INDEX(0x0001U); +/// ``` +void HARNESS_ASSERT_INDEX(size_t assert_index); + +#endif // TSFFS_H +#else +#error "Unsupported compiler!" +#endif diff --git a/src/arch/mod.rs b/src/arch/mod.rs index 606abe2d..e1fd90f0 100644 --- a/src/arch/mod.rs +++ b/src/arch/mod.rs @@ -7,16 +7,20 @@ use self::{ risc_v::RISCVArchitectureOperations, x86::X86ArchitectureOperations, x86_64::X86_64ArchitectureOperations, }; -use crate::{tracer::TraceEntry, traits::TracerDisassembler, StartBuffer, StartSize, CLASS_NAME}; -use anyhow::{anyhow, bail, Error, Result}; +use crate::{ + tracer::TraceEntry, traits::TracerDisassembler, ManualStartAddress, ManualStartInfo, StartInfo, + StartPhysicalAddress, StartSize, +}; +use anyhow::anyhow; +use anyhow::{bail, ensure, Error, Result}; use raw_cstr::AsRawCstr; use simics::{ api::{ - get_object, read_phys_memory, sys::instruction_handle_t, write_byte, Access, AttrValueType, - ConfObject, CpuInstructionQueryInterface, CpuInstrumentationSubscribeInterface, - CycleInterface, GenericAddress, IntRegisterInterface, ProcessorInfoV2Interface, + read_phys_memory, sys::instruction_handle_t, write_byte, Access, AttrValueType, ConfObject, + CpuInstructionQueryInterface, CpuInstrumentationSubscribeInterface, CycleInterface, + IntRegisterInterface, ProcessorInfoV2Interface, }, - trace, + read_byte, }; use std::{fmt::Debug, str::FromStr}; @@ -100,12 +104,11 @@ impl Debug for Architecture { } /// Each architecture must provide a struct that performs architecture-specific operations pub trait ArchitectureOperations { - /// The register for this architecture which contains the address of the testcase buffer - /// when using the magic start functionality - const DEFAULT_TESTCASE_AREA_REGISTER_NAME: &'static str; - /// The register for this architecture which contains the pointer to the size of the - /// testcase buffer when using the magic start functionality - const DEFAULT_TESTCASE_SIZE_REGISTER_NAME: &'static str; + const INDEX_SELECTOR_REGISTER: &'static str; + const ARGUMENT_REGISTER_0: &'static str; + const ARGUMENT_REGISTER_1: &'static str; + const ARGUMENT_REGISTER_2: &'static str; + const POINTER_WIDTH_OVERRIDE: Option = None; /// Create a new instance of the architecture operations fn new(cpu: *mut ConfObject) -> Result @@ -141,169 +144,340 @@ pub trait ArchitectureOperations { /// Return a mutable reference to the interface for querying CPU cycles and timing fn cycle(&mut self) -> &mut CycleInterface; - /// Returns the address and whether the address is virtual for the testcase buffer used by - /// the magic start functionality - fn get_magic_start_buffer(&mut self) -> Result { - let number = self + /// Return the value of the magic index selector register, which is used to determine + /// whether a magic instruction should be used or skipped. + fn get_magic_index_selector(&mut self) -> Result { + Ok(self .int_register() - .get_number(Self::DEFAULT_TESTCASE_AREA_REGISTER_NAME.as_raw_cstr()?)?; + .get_number(Self::INDEX_SELECTOR_REGISTER.as_raw_cstr()?) + .and_then(|n| self.int_register().read(n))?) + } - trace!( - get_object(CLASS_NAME)?, - "Got number {} for register {}", - number, - Self::DEFAULT_TESTCASE_AREA_REGISTER_NAME - ); + /// Get the magic start information from the harness which takes the arguments: + /// + /// - buffer: The address of the buffer containing the testcase + /// - size_ptr: A pointer to a pointer-sized variable containing the size of the testcase + fn get_magic_start_buffer_ptr_size_ptr(&mut self) -> Result { + let buffer_register_number = self + .int_register() + .get_number(Self::ARGUMENT_REGISTER_0.as_raw_cstr()?)?; + let size_ptr_register_number = self + .int_register() + .get_number(Self::ARGUMENT_REGISTER_1.as_raw_cstr()?)?; + let buffer_logical_address = self.int_register().read(buffer_register_number)?; + let size_ptr_logical_address = self.int_register().read(size_ptr_register_number)?; + let buffer_physical_address_block = self + .processor_info_v2() + .logical_to_physical(buffer_logical_address, Access::Sim_Access_Read)?; + let size_ptr_physical_address_block = self + .processor_info_v2() + .logical_to_physical(size_ptr_logical_address, Access::Sim_Access_Read)?; - let logical_address = self.int_register().read(number)?; - trace!( - get_object(CLASS_NAME)?, - "Got logical address {:#x} from register", - logical_address + ensure!( + buffer_physical_address_block.valid != 0, + "Invalid linear address found in magic start buffer register {buffer_register_number}: {buffer_logical_address:#x}" + ); + ensure!( + size_ptr_physical_address_block.valid != 0, + "Invalid linear address found in magic start size register {size_ptr_register_number}: {size_ptr_logical_address:#x}" ); - let physical_address_block = self + let size_size = if let Some(width) = Self::POINTER_WIDTH_OVERRIDE { + width + } else { + self.processor_info_v2().get_logical_address_width()? / u8::BITS as i32 + }; + + let size = read_phys_memory( + self.cpu(), + size_ptr_physical_address_block.address, + size_size, + )?; + + let contents = (0..size) + .map(|i| { + read_byte( + self.processor_info_v2().get_physical_memory()?, + buffer_physical_address_block.address + i, + ) + .map_err(|e| { + anyhow!( + "Failed to read byte at {:#x}: {}", + buffer_physical_address_block.address + i, + e + ) + }) + }) + .collect::>>()?; + + Ok(StartInfo::builder() + .address( + if buffer_physical_address_block.address != buffer_logical_address { + StartPhysicalAddress::WasVirtual(buffer_physical_address_block.address) + } else { + StartPhysicalAddress::WasPhysical(buffer_physical_address_block.address) + }, + ) + .contents(contents) + .size(StartSize::SizePtr { + address: if size_ptr_physical_address_block.address != size_ptr_logical_address { + StartPhysicalAddress::WasVirtual(size_ptr_physical_address_block.address) + } else { + StartPhysicalAddress::WasPhysical(size_ptr_physical_address_block.address) + }, + maximum_size: size as usize, + }) + .build()) + } + + /// Get the magic start information from the harness which takes the arguments: + /// + /// - buffer: The address of the buffer containing the testcase + /// - size_val: The maximum size of the testcase + fn get_magic_start_buffer_ptr_size_val(&mut self) -> Result { + let buffer_register_number = self + .int_register() + .get_number(Self::ARGUMENT_REGISTER_0.as_raw_cstr()?)?; + let size_val_register_number = self + .int_register() + .get_number(Self::ARGUMENT_REGISTER_1.as_raw_cstr()?)?; + let buffer_logical_address = self.int_register().read(buffer_register_number)?; + let size_val = self.int_register().read(size_val_register_number)?; + let buffer_physical_address_block = self .processor_info_v2() - // NOTE: Do we need to support segmented memory via logical_to_physical? - .logical_to_physical(logical_address, Access::Sim_Access_Read)?; + .logical_to_physical(buffer_logical_address, Access::Sim_Access_Read)?; - // NOTE: -1 signals no valid mapping, but this is equivalent to u64::MAX - if physical_address_block.valid == 0 { - bail!("Invalid linear address found in magic start buffer register {number}: {logical_address:#x}"); - } else { - trace!( - get_object(CLASS_NAME)?, - "Got physical address {:#x} from logical address", - physical_address_block.address - ); - Ok(StartBuffer::builder() - .physical_address(physical_address_block.address) - .virt(physical_address_block.address != logical_address) - .build()) - } + ensure!( + buffer_physical_address_block.valid != 0, + "Invalid linear address found in magic start buffer register {buffer_register_number}: {buffer_logical_address:#x}" + ); + + let contents = (0..size_val) + .map(|i| { + read_byte( + self.processor_info_v2().get_physical_memory()?, + buffer_physical_address_block.address + i, + ) + .map_err(|e| { + anyhow!( + "Failed to read byte at {:#x}: {}", + buffer_physical_address_block.address + i, + e + ) + }) + }) + .collect::>>()?; + + Ok(StartInfo::builder() + .address( + if buffer_physical_address_block.address != buffer_logical_address { + StartPhysicalAddress::WasVirtual(buffer_physical_address_block.address) + } else { + StartPhysicalAddress::WasPhysical(buffer_physical_address_block.address) + }, + ) + .contents(contents) + .size(StartSize::MaxSize(size_val as usize)) + .build()) } - /// Returns the memory pointed to by the magic start functionality containing the maximum - /// size of an input testcase - fn get_magic_start_size(&mut self) -> Result { - let number = self + + /// Get the magic start information from the harness which takes the arguments: + /// + /// - buffer: The address of the buffer containing the testcase + /// - size_ptr: A pointer to a pointer-sized variable to which the size is written + /// - size_val: The maximum size of the testcase + fn get_magic_start_buffer_ptr_size_ptr_val(&mut self) -> Result { + let buffer_register_number = self .int_register() - .get_number(Self::DEFAULT_TESTCASE_SIZE_REGISTER_NAME.as_raw_cstr()?)?; - let logical_address = self.int_register().read(number)?; - let physical_address_block = self + .get_number(Self::ARGUMENT_REGISTER_0.as_raw_cstr()?)?; + let size_ptr_register_number = self + .int_register() + .get_number(Self::ARGUMENT_REGISTER_1.as_raw_cstr()?)?; + let size_val_register_number = self + .int_register() + .get_number(Self::ARGUMENT_REGISTER_2.as_raw_cstr()?)?; + + let buffer_logical_address = self.int_register().read(buffer_register_number)?; + let size_ptr_logical_address = self.int_register().read(size_ptr_register_number)?; + let size_val = self.int_register().read(size_val_register_number)?; + + let buffer_physical_address_block = self .processor_info_v2() - // NOTE: Do we need to support segmented memory via logical_to_physical? - .logical_to_physical(logical_address, Access::Sim_Access_Read)?; + .logical_to_physical(buffer_logical_address, Access::Sim_Access_Read)?; - // NOTE: -1 signals no valid mapping, but this is equivalent to u64::MAX - if physical_address_block.valid == 0 { - bail!("Invalid linear address found in magic start buffer register {number}: {logical_address:#x}"); - } + let size_ptr_physical_address_block = self + .processor_info_v2() + .logical_to_physical(size_ptr_logical_address, Access::Sim_Access_Read)?; - let size_size = self.processor_info_v2().get_logical_address_width()? / u8::BITS as i32; - let size = read_phys_memory(self.cpu(), physical_address_block.address, size_size)?; + ensure!( + buffer_physical_address_block.valid != 0, + "Invalid linear address found in magic start buffer register {buffer_register_number}: {buffer_logical_address:#x}" + ); + ensure!( + size_ptr_physical_address_block.valid != 0, + "Invalid linear address found in magic start size register {size_ptr_register_number}: {size_ptr_logical_address:#x}" + ); - Ok(StartSize::builder() - .physical_address(( - physical_address_block.address, - physical_address_block.address != logical_address, - )) - .initial_size(size) + let contents = (0..size_val) + .map(|i| { + read_byte( + self.processor_info_v2().get_physical_memory()?, + buffer_physical_address_block.address + i, + ) + .map_err(|e| { + anyhow!( + "Failed to read byte at {:#x}: {}", + buffer_physical_address_block.address + i, + e + ) + }) + }) + .collect::>>()?; + + Ok(StartInfo::builder() + .address( + if buffer_physical_address_block.address != buffer_logical_address { + StartPhysicalAddress::WasVirtual(buffer_physical_address_block.address) + } else { + StartPhysicalAddress::WasPhysical(buffer_physical_address_block.address) + }, + ) + .contents(contents) + .size(StartSize::SizePtrAndMaxSize { + address: if size_ptr_physical_address_block.address != size_ptr_logical_address { + StartPhysicalAddress::WasVirtual(size_ptr_physical_address_block.address) + } else { + StartPhysicalAddress::WasPhysical(size_ptr_physical_address_block.address) + }, + maximum_size: size_val as usize, + }) .build()) } /// Returns the address and whether the address is virtual for the testcase buffer used by /// the manual start functionality - fn get_manual_start_buffer( - &mut self, - buffer_address: GenericAddress, - virt: bool, - ) -> Result { - let physical_address = if virt { + fn get_manual_start_info(&mut self, info: &ManualStartInfo) -> Result { + let buffer_physical_address = if matches!(info.address, ManualStartAddress::Virtual(_)) { let physical_address_block = self .processor_info_v2() // NOTE: Do we need to support segmented memory via logical_to_physical? - .logical_to_physical(buffer_address, Access::Sim_Access_Read)?; + .logical_to_physical( + match info.address { + ManualStartAddress::Virtual(address) => address, + ManualStartAddress::Physical(address) => address, + }, + Access::Sim_Access_Read, + )?; if physical_address_block.valid == 0 { bail!( - "Invalid linear address for given buffer address {:#x}", - buffer_address + "Invalid linear address for given buffer address {:?}", + info.address ); } physical_address_block.address } else { - buffer_address + info.address.address() }; - Ok(StartBuffer::builder() - .physical_address(physical_address) - .virt(physical_address != buffer_address) - .build()) - } - - /// Returns the initial start size for non-magic instructions by reading it from a given - /// (possibly virtual) address - fn get_manual_start_size( - &mut self, - size_address: GenericAddress, - virt: bool, - ) -> Result { - let physical_address = if virt { - let physical_address_block = self - .processor_info_v2() - // NOTE: Do we need to support segmented memory via logical_to_physical? - .logical_to_physical(size_address, Access::Sim_Access_Read)?; - - if physical_address_block.valid == 0 { - bail!("Invalid linear address given for start buffer : {size_address:#x}"); + let address = StartPhysicalAddress::WasPhysical(buffer_physical_address); + + let size = match &info.size { + crate::ManualStartSize::SizePtr { address } => { + let address = match address { + ManualStartAddress::Virtual(v) => { + let physical_address = self + .processor_info_v2() + .logical_to_physical(*v, Access::Sim_Access_Read)?; + + if physical_address.valid == 0 { + bail!("Invalid linear address given for start buffer : {v:#x}"); + } + + StartPhysicalAddress::WasVirtual(physical_address.address) + } + ManualStartAddress::Physical(p) => StartPhysicalAddress::WasPhysical(*p), + }; + + let size_size = if let Some(width) = Self::POINTER_WIDTH_OVERRIDE { + width + } else { + self.processor_info_v2().get_logical_address_width()? / u8::BITS as i32 + }; + let maximum_size = + read_phys_memory(self.cpu(), address.physical_address(), size_size)?; + StartSize::SizePtr { + address, + maximum_size: maximum_size as usize, + } + } + crate::ManualStartSize::MaxSize(maximum_size) => StartSize::MaxSize(*maximum_size), + crate::ManualStartSize::SizePtrAndMaxSize { + address, + maximum_size, + } => { + let address = match address { + ManualStartAddress::Virtual(v) => { + let physical_address = self + .processor_info_v2() + .logical_to_physical(*v, Access::Sim_Access_Read)?; + + if physical_address.valid == 0 { + bail!("Invalid linear address given for start buffer : {v:#x}"); + } + + StartPhysicalAddress::WasVirtual(physical_address.address) + } + ManualStartAddress::Physical(p) => StartPhysicalAddress::WasPhysical(*p), + }; + + StartSize::SizePtrAndMaxSize { + address, + maximum_size: *maximum_size, + } } - - physical_address_block.address - } else { - size_address }; - let size_size = self.processor_info_v2().get_logical_address_width()? / u8::BITS as i32; - let size = read_phys_memory(self.cpu(), physical_address, size_size)?; - - Ok(StartSize::builder() - .physical_address((physical_address, physical_address != size_address)) - .initial_size(size) + let contents = (0..size.maximum_size()) + .map(|i| { + read_byte( + self.processor_info_v2().get_physical_memory()?, + buffer_physical_address + i as u64, + ) + .map_err(|e| { + anyhow!( + "Failed to read byte at {:#x}: {}", + buffer_physical_address + i as u64, + e + ) + }) + }) + .collect::>>()?; + + Ok(StartInfo::builder() + .address(address) + .contents(contents) + .size(size) .build()) } - /// Writes the buffer with a testcase of a certain size - fn write_start( - &mut self, - testcase: &[u8], - buffer: &StartBuffer, - size: &StartSize, - ) -> Result<()> { + fn write_start(&mut self, testcase: &[u8], info: &StartInfo) -> Result<()> { let mut testcase = testcase.to_vec(); // NOTE: We have to handle both riscv64 and riscv32 here let addr_size = self.processor_info_v2().get_logical_address_width()? as usize / u8::BITS as usize; - let initial_size = - size.initial_size - .ok_or_else(|| anyhow!("Expected initial size for start"))? as usize; let physical_memory = self.processor_info_v2().get_physical_memory()?; - trace!( - get_object(CLASS_NAME)?, - "Truncating testcase to {initial_size} bytes (from {} bytes)", - testcase.len() - ); - - testcase.truncate(initial_size); + testcase.truncate(info.size.maximum_size()); testcase.iter().enumerate().try_for_each(|(i, c)| { - let physical_address = buffer.physical_address + (i as u64); + let physical_address = info.address.physical_address() + (i as u64); write_byte(physical_memory, physical_address, *c) })?; - if let Some((address, _)) = size.physical_address { + if let Some(size_address) = info.size.physical_address().map(|s| s.physical_address()) { testcase .len() .to_le_bytes() @@ -311,14 +485,9 @@ pub trait ArchitectureOperations { .take(addr_size) .enumerate() .try_for_each(|(i, c)| { - let physical_address = address + (i as u64); + let physical_address = size_address + (i as u64); write_byte(physical_memory, physical_address, *c) })?; - } else { - trace!( - get_object(CLASS_NAME)?, - "Not writing testcase size, no physical address saved for size" - ); } Ok(()) @@ -329,8 +498,10 @@ pub trait ArchitectureOperations { } impl ArchitectureOperations for Architecture { - const DEFAULT_TESTCASE_AREA_REGISTER_NAME: &'static str = ""; - const DEFAULT_TESTCASE_SIZE_REGISTER_NAME: &'static str = ""; + const INDEX_SELECTOR_REGISTER: &'static str = ""; + const ARGUMENT_REGISTER_0: &'static str = ""; + const ARGUMENT_REGISTER_1: &'static str = ""; + const ARGUMENT_REGISTER_2: &'static str = ""; fn new(cpu: *mut ConfObject) -> Result where @@ -403,56 +574,51 @@ impl ArchitectureOperations for Architecture { } } - fn get_magic_start_buffer(&mut self) -> Result { + fn get_magic_index_selector(&mut self) -> Result { + match self { + Architecture::X86_64(x86_64) => x86_64.get_magic_index_selector(), + Architecture::I386(i386) => i386.get_magic_index_selector(), + Architecture::Riscv(riscv) => riscv.get_magic_index_selector(), + } + } + + fn get_magic_start_buffer_ptr_size_ptr(&mut self) -> Result { match self { - Architecture::X86_64(x86_64) => x86_64.get_magic_start_buffer(), - Architecture::I386(i386) => i386.get_magic_start_buffer(), - Architecture::Riscv(riscv) => riscv.get_magic_start_buffer(), + Architecture::X86_64(x86_64) => x86_64.get_magic_start_buffer_ptr_size_ptr(), + Architecture::I386(i386) => i386.get_magic_start_buffer_ptr_size_ptr(), + Architecture::Riscv(riscv) => riscv.get_magic_start_buffer_ptr_size_ptr(), } } - fn get_magic_start_size(&mut self) -> Result { + fn get_magic_start_buffer_ptr_size_val(&mut self) -> Result { match self { - Architecture::X86_64(x86_64) => x86_64.get_magic_start_size(), - Architecture::I386(i386) => i386.get_magic_start_size(), - Architecture::Riscv(riscv) => riscv.get_magic_start_size(), + Architecture::X86_64(x86_64) => x86_64.get_magic_start_buffer_ptr_size_val(), + Architecture::I386(i386) => i386.get_magic_start_buffer_ptr_size_val(), + Architecture::Riscv(riscv) => riscv.get_magic_start_buffer_ptr_size_val(), } } - fn get_manual_start_buffer( - &mut self, - buffer_address: GenericAddress, - virt: bool, - ) -> Result { + fn get_magic_start_buffer_ptr_size_ptr_val(&mut self) -> Result { match self { - Architecture::X86_64(x86_64) => x86_64.get_manual_start_buffer(buffer_address, virt), - Architecture::I386(i386) => i386.get_manual_start_buffer(buffer_address, virt), - Architecture::Riscv(riscv) => riscv.get_manual_start_buffer(buffer_address, virt), + Architecture::X86_64(x86_64) => x86_64.get_magic_start_buffer_ptr_size_ptr(), + Architecture::I386(i386) => i386.get_magic_start_buffer_ptr_size_ptr(), + Architecture::Riscv(riscv) => riscv.get_magic_start_buffer_ptr_size_ptr(), } } - fn get_manual_start_size( - &mut self, - size_address: GenericAddress, - virt: bool, - ) -> Result { + fn get_manual_start_info(&mut self, info: &ManualStartInfo) -> Result { match self { - Architecture::X86_64(x86_64) => x86_64.get_manual_start_size(size_address, virt), - Architecture::I386(i386) => i386.get_manual_start_size(size_address, virt), - Architecture::Riscv(riscv) => riscv.get_manual_start_size(size_address, virt), + Architecture::X86_64(x86_64) => x86_64.get_manual_start_info(info), + Architecture::I386(i386) => i386.get_manual_start_info(info), + Architecture::Riscv(riscv) => riscv.get_manual_start_info(info), } } - fn write_start( - &mut self, - testcase: &[u8], - buffer: &StartBuffer, - size: &StartSize, - ) -> Result<()> { + fn write_start(&mut self, testcase: &[u8], info: &StartInfo) -> Result<()> { match self { - Architecture::X86_64(x86_64) => x86_64.write_start(testcase, buffer, size), - Architecture::I386(i386) => i386.write_start(testcase, buffer, size), - Architecture::Riscv(riscv) => riscv.write_start(testcase, buffer, size), + Architecture::X86_64(x86_64) => x86_64.write_start(testcase, info), + Architecture::I386(i386) => i386.write_start(testcase, info), + Architecture::Riscv(riscv) => riscv.write_start(testcase, info), } } diff --git a/src/arch/risc_v.rs b/src/arch/risc_v.rs index 8a64ae35..aee0cb33 100644 --- a/src/arch/risc_v.rs +++ b/src/arch/risc_v.rs @@ -22,17 +22,6 @@ use crate::{ use super::ArchitectureOperations; -/// The default register the fuzzer expects to contain a pointer to an area to write -/// each testcase into when using an in-target harness. This is AKA a0 but we use x10 as the -/// canonical name -pub const DEFAULT_TESTCASE_AREA_REGISTER_NAME: &str = "x10"; -/// The default register the fuzzer expects to contain a pointer to a variable, -/// initially containing the maximum size of the area pointed to by -/// `DEFAULT_TESTCASE_AREA_REGISTER_NAME`, which will be written each fuzzer execution -/// to contain the actual size of the current testcase. This is AKA a1 but we use x11 as the -/// canonical name -pub const DEFAULT_TESTCASE_SIZE_REGISTER_NAME: &str = "x11"; - pub(crate) struct RISCVArchitectureOperations { cpu: *mut ConfObject, disassembler: Disassembler, @@ -44,8 +33,13 @@ pub(crate) struct RISCVArchitectureOperations { } impl ArchitectureOperations for RISCVArchitectureOperations { - const DEFAULT_TESTCASE_AREA_REGISTER_NAME: &'static str = DEFAULT_TESTCASE_AREA_REGISTER_NAME; - const DEFAULT_TESTCASE_SIZE_REGISTER_NAME: &'static str = DEFAULT_TESTCASE_SIZE_REGISTER_NAME; + const INDEX_SELECTOR_REGISTER: &'static str = "x10"; + + const ARGUMENT_REGISTER_0: &'static str = "x11"; + + const ARGUMENT_REGISTER_1: &'static str = "x12"; + + const ARGUMENT_REGISTER_2: &'static str = "x13"; fn new(cpu: *mut ConfObject) -> Result { let mut processor_info_v2: ProcessorInfoV2Interface = get_interface(cpu)?; diff --git a/src/arch/x86.rs b/src/arch/x86.rs index 5cba75c2..21a4add0 100644 --- a/src/arch/x86.rs +++ b/src/arch/x86.rs @@ -9,30 +9,21 @@ use super::ArchitectureOperations; use crate::{ tracer::{CmpExpr, CmpType, CmpValue, TraceEntry}, traits::TracerDisassembler, - StartBuffer, StartSize, CLASS_NAME, + CLASS_NAME, }; use anyhow::{anyhow, bail, Error, Result}; use libafl::prelude::CmpValues; use raw_cstr::AsRawCstr; use simics::{ api::{ - get_interface, get_object, read_phys_memory, sys::instruction_handle_t, write_byte, Access, - ConfObject, CpuInstructionQueryInterface, CpuInstrumentationSubscribeInterface, - CycleInterface, GenericAddress, IntRegisterInterface, ProcessorInfoV2Interface, + get_interface, get_object, read_phys_memory, sys::instruction_handle_t, Access, ConfObject, + CpuInstructionQueryInterface, CpuInstrumentationSubscribeInterface, CycleInterface, + IntRegisterInterface, ProcessorInfoV2Interface, }, trace, }; use yaxpeax_x86::protected_mode::{ConditionCode, InstDecoder, Instruction, Opcode, Operand}; -/// The default register the fuzzer expects to contain a pointer to an area to write -/// each testcase into when using an in-target harness -pub const DEFAULT_TESTCASE_AREA_REGISTER_NAME: &str = "edi"; -/// The default register the fuzzer expects to contain a pointer to a variable, -/// initially containing the maximum size of the area pointed to by -/// `DEFAULT_TESTCASE_AREA_REGISTER_NAME`, which will be written each fuzzer execution -/// to contain the actual size of the current testcase. -pub const DEFAULT_TESTCASE_SIZE_REGISTER_NAME: &str = "esi"; - pub(crate) struct X86ArchitectureOperations { cpu: *mut ConfObject, disassembler: Disassembler, @@ -44,8 +35,11 @@ pub(crate) struct X86ArchitectureOperations { } impl ArchitectureOperations for X86ArchitectureOperations { - const DEFAULT_TESTCASE_AREA_REGISTER_NAME: &'static str = DEFAULT_TESTCASE_AREA_REGISTER_NAME; - const DEFAULT_TESTCASE_SIZE_REGISTER_NAME: &'static str = DEFAULT_TESTCASE_SIZE_REGISTER_NAME; + const INDEX_SELECTOR_REGISTER: &'static str = "edi"; + const ARGUMENT_REGISTER_0: &'static str = "esi"; + const ARGUMENT_REGISTER_1: &'static str = "edx"; + const ARGUMENT_REGISTER_2: &'static str = "ecx"; + const POINTER_WIDTH_OVERRIDE: Option = Some(4); fn new(cpu: *mut ConfObject) -> Result { let mut processor_info_v2: ProcessorInfoV2Interface = get_interface(cpu)?; @@ -172,223 +166,6 @@ impl ArchitectureOperations for X86ArchitectureOperations { &mut self.cycle } - /// Returns the address and whether the address is virtual for the testcase buffer used by - /// the magic start functionality - fn get_magic_start_buffer(&mut self) -> Result { - let number = self - .int_register() - .get_number(Self::DEFAULT_TESTCASE_AREA_REGISTER_NAME.as_raw_cstr()?)?; - trace!( - get_object(CLASS_NAME)?, - "Got number {} for register {}", - number, - Self::DEFAULT_TESTCASE_AREA_REGISTER_NAME - ); - - let logical_address = self.int_register().read(number)?; - trace!( - get_object(CLASS_NAME)?, - "Got logical address {:#x} from register", - logical_address - ); - - let physical_address_block = self - .processor_info_v2() - // NOTE: Do we need to support segmented memory via logical_to_physical? - .logical_to_physical(logical_address, Access::Sim_Access_Read)?; - - // NOTE: -1 signals no valid mapping, but this is equivalent to u64::MAX - if physical_address_block.valid == 0 { - bail!("Invalid linear address found in magic start buffer register {number}: {logical_address:#x}"); - } else { - trace!( - get_object(CLASS_NAME)?, - "Got physical address {:#x} from logical address", - physical_address_block.address - ); - Ok(StartBuffer::builder() - .physical_address(physical_address_block.address) - .virt(physical_address_block.address != logical_address) - .build()) - } - } - - // NOTE: Manual implementation because we must ensure we set the width to 4 bytes - // instead of 6 for misreporting/hinted architectures - fn get_magic_start_size(&mut self) -> Result { - let number = self - .int_register() - .get_number(Self::DEFAULT_TESTCASE_SIZE_REGISTER_NAME.as_raw_cstr()?)?; - trace!( - get_object(CLASS_NAME)?, - "Got number {} for register {}", - number, - Self::DEFAULT_TESTCASE_SIZE_REGISTER_NAME - ); - let logical_address = self.int_register().read(number)?; - trace!( - get_object(CLASS_NAME)?, - "Got logical address {:#x} from register", - logical_address - ); - let physical_address_block = self - .processor_info_v2() - // NOTE: Do we need to support segmented memory via logical_to_physical? - .logical_to_physical(logical_address, Access::Sim_Access_Read)?; - trace!( - get_object(CLASS_NAME)?, - "Got physical address {:#x} from logical address", - physical_address_block.address - ); - - // NOTE: -1 signals no valid mapping, but this is equivalent to u64::MAX - if physical_address_block.valid == 0 { - bail!("Invalid linear address found in magic start buffer register {number}: {logical_address:#x}"); - } - - let size = read_phys_memory(self.cpu(), physical_address_block.address, 4)?; - - trace!( - get_object(CLASS_NAME)?, - "Read start size {size} from start size address" - ); - - Ok(StartSize::builder() - .physical_address(( - physical_address_block.address, - physical_address_block.address != logical_address, - )) - .initial_size(size) - .build()) - } - - fn get_manual_start_buffer( - &mut self, - buffer_address: GenericAddress, - virt: bool, - ) -> Result { - let physical_address = if virt { - let physical_address_block = self - .processor_info_v2() - // NOTE: Do we need to support segmented memory via logical_to_physical? - .logical_to_physical(buffer_address, Access::Sim_Access_Read)?; - - if physical_address_block.valid == 0 { - bail!( - "Invalid linear address for given buffer address {:#x}", - buffer_address - ); - } - physical_address_block.address - } else { - buffer_address - }; - - trace!( - get_object(CLASS_NAME)?, - "Got physical address {:#x} from logical address for manual start buffer", - physical_address - ); - Ok(StartBuffer::builder() - .physical_address(physical_address) - .virt(physical_address != buffer_address) - .build()) - } - - /// Returns the initial start size for non-magic instructions by reading it from a given - /// (possibly virtual) address - fn get_manual_start_size( - &mut self, - size_address: GenericAddress, - virt: bool, - ) -> Result { - let physical_address = if virt { - let physical_address_block = self - .processor_info_v2() - // NOTE: Do we need to support segmented memory via logical_to_physical? - .logical_to_physical(size_address, Access::Sim_Access_Read)?; - - if physical_address_block.valid == 0 { - bail!("Invalid linear address given for start buffer : {size_address:#x}"); - } - physical_address_block.address - } else { - size_address - }; - - trace!( - get_object(CLASS_NAME)?, - "Got physical address {:#x} from logical address for manual start buffer size", - physical_address - ); - - let size = read_phys_memory(self.cpu(), physical_address, 4)?; - - trace!( - get_object(CLASS_NAME)?, - "Read start size {size} from start size address" - ); - - Ok(StartSize::builder() - .physical_address((physical_address, physical_address != size_address)) - .initial_size(size) - .build()) - } - - /// Writes the buffer with a testcase of a certain size - fn write_start( - &mut self, - testcase: &[u8], - buffer: &StartBuffer, - size: &StartSize, - ) -> Result<()> { - let mut testcase = testcase.to_vec(); - let physical_memory = self.processor_info_v2().get_physical_memory()?; - - let initial_size = - size.initial_size - .ok_or_else(|| anyhow!("Expected initial size for start"))? as usize; - - trace!( - get_object(CLASS_NAME)?, - "Truncating testcase to {initial_size} bytes (from {} bytes)", - testcase.len() - ); - - testcase.truncate(initial_size); - - testcase.iter().enumerate().try_for_each(|(i, c)| { - let physical_address = buffer.physical_address + (i as u64); - trace!( - get_object(CLASS_NAME)?, - "Writing testcase byte {:?} to physical memory {:#x}", - c, - physical_address - ); - write_byte(physical_memory, physical_address, *c) - })?; - - if let Some((address, _)) = size.physical_address { - testcase - .len() - .to_le_bytes() - .iter() - .take(4) - .enumerate() - .try_for_each(|(i, c)| { - let physical_address = address + (i as u64); - write_byte(physical_memory, physical_address, *c) - })?; - } else { - trace!( - get_object(CLASS_NAME)?, - "Not writing testcase size, no physical address saved for size" - ); - } - - Ok(()) - } - fn trace_pc(&mut self, instruction_query: *mut instruction_handle_t) -> Result { let instruction_bytes = self .cpu_instruction_query diff --git a/src/arch/x86_64.rs b/src/arch/x86_64.rs index 74fa47cd..9d756450 100644 --- a/src/arch/x86_64.rs +++ b/src/arch/x86_64.rs @@ -21,15 +21,6 @@ use yaxpeax_x86::amd64::{ConditionCode, InstDecoder, Instruction, Opcode, Operan use super::ArchitectureOperations; -/// The default register the fuzzer expects to contain a pointer to an area to write -/// each testcase into when using an in-target harness -pub const DEFAULT_TESTCASE_AREA_REGISTER_NAME: &str = "rdi"; -/// The default register the fuzzer expects to contain a pointer to a variable, -/// initially containing the maximum size of the area pointed to by -/// `DEFAULT_TESTCASE_AREA_REGISTER_NAME`, which will be written each fuzzer execution -/// to contain the actual size of the current testcase. -pub const DEFAULT_TESTCASE_SIZE_REGISTER_NAME: &str = "rsi"; - pub(crate) struct X86_64ArchitectureOperations { cpu: *mut ConfObject, disassembler: Disassembler, @@ -41,8 +32,10 @@ pub(crate) struct X86_64ArchitectureOperations { } impl ArchitectureOperations for X86_64ArchitectureOperations { - const DEFAULT_TESTCASE_AREA_REGISTER_NAME: &'static str = DEFAULT_TESTCASE_AREA_REGISTER_NAME; - const DEFAULT_TESTCASE_SIZE_REGISTER_NAME: &'static str = DEFAULT_TESTCASE_SIZE_REGISTER_NAME; + const INDEX_SELECTOR_REGISTER: &'static str = "rdi"; + const ARGUMENT_REGISTER_0: &'static str = "rsi"; + const ARGUMENT_REGISTER_1: &'static str = "rdx"; + const ARGUMENT_REGISTER_2: &'static str = "rcx"; fn new(cpu: *mut ConfObject) -> Result { let mut processor_info_v2: ProcessorInfoV2Interface = get_interface(cpu)?; diff --git a/src/fuzzer/mod.rs b/src/fuzzer/mod.rs index 5374b825..3da5c2f9 100644 --- a/src/fuzzer/mod.rs +++ b/src/fuzzer/mod.rs @@ -10,7 +10,7 @@ use crate::{ use anyhow::{anyhow, Result}; use libafl::{ feedback_or, feedback_or_fast, - inputs::HasBytesVec, + inputs::{HasBytesVec, Input}, prelude::{ havoc_mutations, ondisk::OnDiskMetadataFormat, tokens_mutations, AFLppRedQueen, BytesInput, CachedOnDiskCorpus, Corpus, CrashFeedback, ExitKind, HasCurrentCorpusIdx, HasTargetBytes, @@ -37,10 +37,10 @@ use libafl_bolts::{ AsMutSlice, AsSlice, }; use libafl_targets::{AFLppCmpLogObserver, AFLppCmplogTracingStage}; -use simics::{api::AsConfObject, debug, warn}; +use simics::{api::AsConfObject, debug, trace, warn}; use std::{ - cell::RefCell, fmt::Debug, io::stderr, slice::from_raw_parts_mut, sync::mpsc::channel, - thread::spawn, time::Duration, + cell::RefCell, fmt::Debug, fs::write, io::stderr, slice::from_raw_parts_mut, + sync::mpsc::channel, thread::spawn, time::Duration, }; use tokenize::{tokenize_executable_file, tokenize_src_file}; use tracing::{level_filters::LevelFilter, Level}; @@ -181,6 +181,14 @@ impl Tsffs { let initial_random_corpus_size = self.initial_random_corpus_size; let executor_timeout = self.executor_timeout; let debug_log_libafl = self.debug_log_libafl; + let initial_contents = self + .use_initial_as_corpus + .then(|| { + self.start_info + .get() + .map(|si| BytesInput::new(si.contents.clone())) + }) + .flatten(); // NOTE: We do *not* use `run_in_thread` because it causes the fuzzer to block when HAPs arrive // which prevents forward progress. @@ -418,6 +426,13 @@ impl Tsffs { anyhow!("Couldn't initialize fuzzer dump to disk stage: {e}") })?; + if let Some(contents) = initial_contents { + write( + corpus_directory.join(contents.generate_name(0)), + contents.bytes(), + )?; + } + if state.must_load_initial_inputs() { state .load_initial_inputs( @@ -565,6 +580,24 @@ impl Tsffs { .map_err(|e| anyhow!("Error receiving from fuzzer: {e}"))? }; + if self.keep_all_corpus { + let testcase_name = testcase.testcase.generate_name(0); + trace!( + self.as_conf_object(), + "Writing testcase {}.testcase to corpus directory: {}", + &testcase_name, + self.corpus_directory.display() + ); + + write( + self.corpus_directory + .join(format!("{}.testcase", &testcase_name)), + testcase.testcase.bytes(), + )?; + } + + self.cmplog_enabled = testcase.cmplog; + debug!(self.as_conf_object(), "Testcase: {testcase:?}"); Ok(testcase) diff --git a/src/haps/mod.rs b/src/haps/mod.rs index 95e08225..c8b56397 100644 --- a/src/haps/mod.rs +++ b/src/haps/mod.rs @@ -7,292 +7,438 @@ use std::time::SystemTime; use crate::{ arch::ArchitectureOperations, - state::{MagicStart, ManualStartSize, Solution, SolutionKind, Stop, StopReason}, - StartSize, Tsffs, + magic::MagicNumber, + state::{SolutionKind, StopReason}, + ManualStartInfo, Tsffs, }; -use anyhow::{anyhow, Result}; +use anyhow::{anyhow, bail, Result}; use libafl::prelude::ExitKind; use simics::{ api::{ continue_simulation, log_level, object_is_processor, quit, run_alone, set_log_level, AsConfObject, ConfObject, GenericTransaction, LogLevel, }, - debug, info, trace, warn, + debug, get_processor_number, info, trace, warn, }; impl Tsffs { - /// Called on core simulation stopped HAP - pub fn on_simulation_stopped(&mut self) -> Result<()> { - if self.stopped_for_repro { - // If we are stopped for repro, we do nothing on this HAP! - return Ok(()); + fn on_simulation_stopped_magic_start(&mut self, magic_number: MagicNumber) -> Result<()> { + if !self.have_initial_snapshot() { + self.start_fuzzer_thread()?; + + let start_processor = self + .start_processor() + .ok_or_else(|| anyhow!("No start processor"))?; + + let start_info = match magic_number { + MagicNumber::StartBufferPtrSizePtr => { + start_processor.get_magic_start_buffer_ptr_size_ptr()? + } + MagicNumber::StartBufferPtrSizeVal => { + start_processor.get_magic_start_buffer_ptr_size_val()? + } + MagicNumber::StartBufferPtrSizePtrVal => { + start_processor.get_magic_start_buffer_ptr_size_ptr_val()? + } + MagicNumber::StopNormal => unreachable!("StopNormal is not handled here"), + MagicNumber::StopAssert => unreachable!("StopAssert is not handled here"), + }; + + debug!(self.as_conf_object(), "Start info: {start_info:?}"); + + self.start_info + .set(start_info) + .map_err(|_| anyhow!("Failed to set start size"))?; + self.start_time + .set(SystemTime::now()) + .map_err(|_| anyhow!("Failed to set start time"))?; + self.coverage_enabled = true; + self.save_initial_snapshot()?; + self.get_and_write_testcase()?; + self.post_timeout_event()?; } - self.log_messages()?; + self.save_repro_bookmark_if_needed()?; - if let Some(reason) = self.stop_reason.take() { - debug!(self.as_conf_object(), "on_simulation_stopped({reason:?})"); - - match reason { - StopReason::MagicStart(magic_start) => { - if !self.have_initial_snapshot() { - self.start_fuzzer_thread()?; - self.add_processor(magic_start.processor, true)?; - - let (start_buffer, start_size) = { - let start_processor = self - .start_processor() - .ok_or_else(|| anyhow!("No start processor"))?; - ( - start_processor.get_magic_start_buffer()?, - start_processor.get_magic_start_size()?, - ) - }; - - debug!( - self.as_conf_object(), - "Start buffer: {start_buffer:?} Start size: {start_size:?}" - ); - - self.start_buffer - .set(start_buffer) - .map_err(|_| anyhow!("Failed to set start buffer"))?; - self.start_size - .set(start_size) - .map_err(|_| anyhow!("Failed to set start size"))?; - self.start_time - .set(SystemTime::now()) - .map_err(|_| anyhow!("Failed to set start time"))?; - self.coverage_enabled = true; - self.save_initial_snapshot()?; - self.get_and_write_testcase()?; - self.post_timeout_event()?; - } - - self.save_repro_bookmark_if_needed()?; + debug!(self.as_conf_object(), "Resuming simulation"); + + run_alone(|| { + continue_simulation(0)?; + Ok(()) + })?; + + Ok(()) + } + + fn on_simulation_stopped_magic_assert(&mut self) -> Result<()> { + self.on_simulation_stopped_solution(SolutionKind::Manual) + } + + fn on_simulation_stopped_magic_stop(&mut self) -> Result<()> { + if !self.have_initial_snapshot() { + warn!( + self.as_conf_object(), + "Stopped normally before start was reached (no snapshot). Resuming without restoring non-existent snapshot." + ); + } else { + self.cancel_timeout_event()?; + + if self.repro_bookmark_set { + self.stopped_for_repro = true; + let current_log_level = log_level(self.as_conf_object_mut())?; + + if current_log_level < LogLevel::Info as u32 { + set_log_level(self.as_conf_object_mut(), LogLevel::Info)?; } - StopReason::ManualStart(start) => { - if !self.have_initial_snapshot() { - self.start_fuzzer_thread()?; - self.add_processor(start.processor, true)?; - - let (mut start_buffer, mut start_size) = { - let start_processor = self - .start_processor() - .ok_or_else(|| anyhow!("No start processor"))?; - ( - if let Some(buffer) = start.buffer.as_ref() { - Some( - start_processor - .get_manual_start_buffer(*buffer, start.virt)?, - ) - } else { - None - }, - match start.size { - ManualStartSize::MaximumSize(s) => { - Some(StartSize::builder().initial_size(s).build()) - } - ManualStartSize::SizeAddress(a) => { - Some(start_processor.get_manual_start_size(a, start.virt)?) - } - ManualStartSize::NoSize => None, - }, - ) - }; - - debug!( - self.as_conf_object(), - "Start buffer: {start_buffer:?} Start size: {start_size:?}" - ); - - if let Some(start_buffer) = start_buffer.take() { - self.start_buffer - .set(start_buffer) - .map_err(|_| anyhow!("Failed to set start buffer"))?; - } - if let Some(start_size) = start_size.take() { - self.start_size - .set(start_size) - .map_err(|_| anyhow!("Failed to set start size"))?; - } - self.start_time - .set(SystemTime::now()) - .map_err(|_| anyhow!("Failed to set start time"))?; - self.coverage_enabled = true; - self.save_initial_snapshot()?; - - if self.start_buffer.get().is_some() && self.start_size.get().is_some() { - self.get_and_write_testcase()?; - } - - self.post_timeout_event()?; - } - - self.save_repro_bookmark_if_needed()?; + + info!( + self.as_conf_object(), + "Stopped for repro. Restore to start bookmark with 'reverse-to start'" + ); + + // Skip the shutdown and continue, we are finished here + return Ok(()); + } + + self.iterations += 1; + + if self.iteration_limit != 0 && self.iterations >= self.iteration_limit { + let duration = SystemTime::now().duration_since( + *self + .start_time + .get() + .ok_or_else(|| anyhow!("Start time was not set"))?, + )?; + + // Set the log level so this message always prints + set_log_level(self.as_conf_object_mut(), LogLevel::Info)?; + + info!( + self.as_conf_object(), + "Configured iteration count {} reached. Stopping after {} seconds ({} exec/s).", + self.iterations, + duration.as_secs_f32(), + self.iterations as f32 / duration.as_secs_f32() + ); + + self.send_shutdown()?; + + quit(0)?; + } + + let fuzzer_tx = self + .fuzzer_tx + .get() + .ok_or_else(|| anyhow!("No fuzzer tx channel"))?; + + fuzzer_tx.send(ExitKind::Ok)?; + + self.restore_initial_snapshot()?; + self.coverage_prev_loc = 0; + + if self.start_info.get().is_some() { + self.get_and_write_testcase()?; + } else { + debug!( + self.as_conf_object(), + "Missing start buffer or size, not writing testcase." + ); + } + + self.post_timeout_event()?; + } + + debug!(self.as_conf_object(), "Resuming simulation"); + + run_alone(|| { + continue_simulation(0)?; + Ok(()) + })?; + + Ok(()) + } + + fn on_simulation_stopped_with_magic(&mut self, magic_number: MagicNumber) -> Result<()> { + match magic_number { + MagicNumber::StartBufferPtrSizePtr + | MagicNumber::StartBufferPtrSizeVal + | MagicNumber::StartBufferPtrSizePtrVal => { + self.on_simulation_stopped_magic_start(magic_number)? + } + MagicNumber::StopNormal => self.on_simulation_stopped_magic_stop()?, + MagicNumber::StopAssert => self.on_simulation_stopped_magic_assert()?, + } + + Ok(()) + } + + fn on_simulation_stopped_with_manual_start( + &mut self, + processor: *mut ConfObject, + info: ManualStartInfo, + ) -> Result<()> { + if !self.have_initial_snapshot() { + self.start_fuzzer_thread()?; + self.add_processor(processor, true)?; + + let start_info = self + .start_processor() + .ok_or_else(|| anyhow!("No start processor"))? + .get_manual_start_info(&info)?; + + self.start_info + .set(start_info) + .map_err(|_| anyhow!("Failed to set start info"))?; + self.start_time + .set(SystemTime::now()) + .map_err(|_| anyhow!("Failed to set start time"))?; + self.coverage_enabled = true; + self.save_initial_snapshot()?; + + self.get_and_write_testcase()?; + + self.post_timeout_event()?; + } + + self.save_repro_bookmark_if_needed()?; + + debug!(self.as_conf_object(), "Resuming simulation"); + + run_alone(|| { + continue_simulation(0)?; + Ok(()) + })?; + + Ok(()) + } + + fn on_simulation_stopped_manual_start_without_buffer( + &mut self, + processor: *mut ConfObject, + ) -> Result<()> { + if !self.have_initial_snapshot() { + self.start_fuzzer_thread()?; + self.add_processor(processor, true)?; + + self.start_time + .set(SystemTime::now()) + .map_err(|_| anyhow!("Failed to set start time"))?; + self.coverage_enabled = true; + self.save_initial_snapshot()?; + + self.post_timeout_event()?; + } + + self.save_repro_bookmark_if_needed()?; + + debug!(self.as_conf_object(), "Resuming simulation"); + + run_alone(|| { + continue_simulation(0)?; + Ok(()) + })?; + + Ok(()) + } + + fn on_simulation_stopped_manual_stop(&mut self) -> Result<()> { + if !self.have_initial_snapshot() { + warn!( + self.as_conf_object(), + "Stopped for manual stop before start was reached (no snapshot). Resuming without restoring non-existent snapshot." + ); + } else { + self.cancel_timeout_event()?; + + if self.repro_bookmark_set { + self.stopped_for_repro = true; + let current_log_level = log_level(self.as_conf_object_mut())?; + + if current_log_level < LogLevel::Info as u32 { + set_log_level(self.as_conf_object_mut(), LogLevel::Info)?; } - StopReason::MagicStop(_) | StopReason::ManualStop(_) => { - if !self.have_initial_snapshot() { - warn!( - self.as_conf_object(), - "Stopped with {reason:?} before start was reached (no snapshot). Resuming without restoring non-existent snapshot." - ); - } else { - self.cancel_timeout_event()?; - - if self.repro_bookmark_set { - self.stopped_for_repro = true; - let current_log_level = log_level(self.as_conf_object_mut())?; - - if current_log_level < LogLevel::Info as u32 { - set_log_level(self.as_conf_object_mut(), LogLevel::Info)?; - } - - info!( - self.as_conf_object(), - "Stopped for repro. Restore to start bookmark with 'reverse-to start'" - ); - - // Skip the shutdown and continue, we are finished here - return Ok(()); - } - - self.iterations += 1; - - if self.iteration_limit != 0 && self.iterations >= self.iteration_limit { - let duration = SystemTime::now().duration_since( - *self - .start_time - .get() - .ok_or_else(|| anyhow!("Start time was not set"))?, - )?; - - // Set the log level so this message always prints - set_log_level(self.as_conf_object_mut(), LogLevel::Info)?; - - info!( - self.as_conf_object(), - "Configured iteration count {} reached. Stopping after {} seconds ({} exec/s).", - self.iterations, - duration.as_secs_f32(), - self.iterations as f32 / duration.as_secs_f32() - ); - - self.send_shutdown()?; - - quit(0)?; - } - - let fuzzer_tx = self - .fuzzer_tx - .get() - .ok_or_else(|| anyhow!("No fuzzer tx channel"))?; - - fuzzer_tx.send(ExitKind::Ok)?; - - self.restore_initial_snapshot()?; - self.coverage_prev_loc = 0; - - if self.start_buffer.get().is_some() && self.start_size.get().is_some() { - self.get_and_write_testcase()?; - } else { - debug!( - self.as_conf_object(), - "Missing start buffer or size, not writing testcase." - ); - } - self.post_timeout_event()?; - } + info!( + self.as_conf_object(), + "Stopped for repro. Restore to start bookmark with 'reverse-to start'" + ); + + // Skip the shutdown and continue, we are finished here + return Ok(()); + } + + self.iterations += 1; + + if self.iteration_limit != 0 && self.iterations >= self.iteration_limit { + let duration = SystemTime::now().duration_since( + *self + .start_time + .get() + .ok_or_else(|| anyhow!("Start time was not set"))?, + )?; + + // Set the log level so this message always prints + set_log_level(self.as_conf_object_mut(), LogLevel::Info)?; + + info!( + self.as_conf_object(), + "Configured iteration count {} reached. Stopping after {} seconds ({} exec/s).", + self.iterations, + duration.as_secs_f32(), + self.iterations as f32 / duration.as_secs_f32() + ); + + self.send_shutdown()?; + + quit(0)?; + } + + let fuzzer_tx = self + .fuzzer_tx + .get() + .ok_or_else(|| anyhow!("No fuzzer tx channel"))?; + + fuzzer_tx.send(ExitKind::Ok)?; + + self.restore_initial_snapshot()?; + self.coverage_prev_loc = 0; + + if self.start_info.get().is_some() { + self.get_and_write_testcase()?; + } else { + debug!( + self.as_conf_object(), + "Missing start buffer or size, not writing testcase. This may be due to using manual no-buffer harnessing." + ); + } + + self.post_timeout_event()?; + } + + debug!(self.as_conf_object(), "Resuming simulation"); + + run_alone(|| { + continue_simulation(0)?; + Ok(()) + })?; + + Ok(()) + } + + fn on_simulation_stopped_solution(&mut self, kind: SolutionKind) -> Result<()> { + if !self.have_initial_snapshot() { + warn!( + self.as_conf_object(), + "Solution {kind:?} before start was reached (no snapshot). Resuming without restoring non-existent snapshot." + ); + } else { + self.cancel_timeout_event()?; + + if self.repro_bookmark_set { + self.stopped_for_repro = true; + let current_log_level = log_level(self.as_conf_object_mut())?; + + if current_log_level < LogLevel::Info as u32 { + set_log_level(self.as_conf_object_mut(), LogLevel::Info)?; } - StopReason::Solution(solution) => { - if !self.have_initial_snapshot() { - warn!( - self.as_conf_object(), - "Solution {solution:?} before start was reached (no snapshot). Resuming without restoring non-existent snapshot." - ); - } else { - self.cancel_timeout_event()?; - - if self.repro_bookmark_set { - self.stopped_for_repro = true; - let current_log_level = log_level(self.as_conf_object_mut())?; - - if current_log_level < LogLevel::Info as u32 { - set_log_level(self.as_conf_object_mut(), LogLevel::Info)?; - } - - info!( - self.as_conf_object(), - "Stopped for repro. Restore to start bookmark with 'reverse-to start'" - ); - - // Skip the shutdown and continue, we are finished here - return Ok(()); - } - - self.iterations += 1; - - if self.iteration_limit != 0 && self.iterations >= self.iteration_limit { - let duration = SystemTime::now().duration_since( - *self - .start_time - .get() - .ok_or_else(|| anyhow!("Start time was not set"))?, - )?; - - // Set the log level so this message always prints - set_log_level(self.as_conf_object_mut(), LogLevel::Info)?; - - info!( - self.as_conf_object(), - "Configured iteration count {} reached. Stopping after {} seconds ({} exec/s).", - self.iterations, - duration.as_secs_f32(), - self.iterations as f32 / duration.as_secs_f32() - ); - - self.send_shutdown()?; - - quit(0)?; - } - - let fuzzer_tx = self - .fuzzer_tx - .get() - .ok_or_else(|| anyhow!("No fuzzer tx channel"))?; - - match solution.kind { - SolutionKind::Timeout => fuzzer_tx.send(ExitKind::Timeout)?, - SolutionKind::Exception - | SolutionKind::Breakpoint - | SolutionKind::Manual => fuzzer_tx.send(ExitKind::Crash)?, - } - - self.restore_initial_snapshot()?; - self.coverage_prev_loc = 0; - - if self.start_buffer.get().is_some() && self.start_size.get().is_some() { - self.get_and_write_testcase()?; - } else { - debug!( - self.as_conf_object(), - "Missing start buffer or size, not writing testcase." - ); - } - self.post_timeout_event()?; - } + info!( + self.as_conf_object(), + "Stopped for repro. Restore to start bookmark with 'reverse-to start'" + ); + + // Skip the shutdown and continue, we are finished here + return Ok(()); + } + + self.iterations += 1; + + if self.iteration_limit != 0 && self.iterations >= self.iteration_limit { + let duration = SystemTime::now().duration_since( + *self + .start_time + .get() + .ok_or_else(|| anyhow!("Start time was not set"))?, + )?; + + // Set the log level so this message always prints + set_log_level(self.as_conf_object_mut(), LogLevel::Info)?; + + info!( + self.as_conf_object(), + "Configured iteration count {} reached. Stopping after {} seconds ({} exec/s).", + self.iterations, + duration.as_secs_f32(), + self.iterations as f32 / duration.as_secs_f32() + ); + + self.send_shutdown()?; + + quit(0)?; + } + + let fuzzer_tx = self + .fuzzer_tx + .get() + .ok_or_else(|| anyhow!("No fuzzer tx channel"))?; + + match kind { + SolutionKind::Timeout => fuzzer_tx.send(ExitKind::Timeout)?, + SolutionKind::Exception | SolutionKind::Breakpoint | SolutionKind::Manual => { + fuzzer_tx.send(ExitKind::Crash)? } } - debug!(self.as_conf_object(), "Resuming simulation"); + self.restore_initial_snapshot()?; + self.coverage_prev_loc = 0; - run_alone(|| { - continue_simulation(0)?; - Ok(()) - })?; - } else if self.have_initial_snapshot() { + if self.start_info.get().is_some() { + self.get_and_write_testcase()?; + } else { + debug!( + self.as_conf_object(), + "Missing start buffer or size, not writing testcase." + ); + } + + self.post_timeout_event()?; + } + + debug!(self.as_conf_object(), "Resuming simulation"); + + run_alone(|| { + continue_simulation(0)?; + Ok(()) + })?; + + Ok(()) + } + + fn on_simulation_stopped_with_reason(&mut self, reason: StopReason) -> Result<()> { + debug!( + self.as_conf_object(), + "Simulation stopped with reason {reason:?}" + ); + + match reason { + StopReason::Magic { magic_number } => { + self.on_simulation_stopped_with_magic(magic_number) + } + StopReason::ManualStart { processor, info } => { + self.on_simulation_stopped_with_manual_start(processor, info) + } + StopReason::ManualStartWithoutBuffer { processor } => { + self.on_simulation_stopped_manual_start_without_buffer(processor) + } + StopReason::ManualStop => self.on_simulation_stopped_manual_stop(), + StopReason::Solution { kind } => self.on_simulation_stopped_solution(kind), + } + } + + fn on_simulation_stopped_without_reason(&mut self) -> Result<()> { + if self.have_initial_snapshot() { + // We only do anything here if we have run, otherwise the simulation was just + // stopped for a reason unrelated to fuzzing (like the user using the CLI) self.cancel_timeout_event()?; let fuzzer_tx = self @@ -329,13 +475,30 @@ impl Tsffs { Ok(()) } + /// Called on core simulation stopped HAP + pub fn on_simulation_stopped(&mut self) -> Result<()> { + if self.stopped_for_repro { + // If we are stopped for repro, we do nothing on this HAP! + return Ok(()); + } + + // Log information from the fuzzer + self.log_messages()?; + + if let Some(reason) = self.stop_reason.take() { + self.on_simulation_stopped_with_reason(reason) + } else { + self.on_simulation_stopped_without_reason() + } + } + /// Called on core exception HAP. Check to see if this exception is configured as a solution /// or all exceptions are solutions and trigger a stop if so pub fn on_exception(&mut self, _obj: *mut ConfObject, exception: i64) -> Result<()> { if self.all_exceptions_are_solutions || self.exceptions.contains(&exception) { - self.stop_simulation(StopReason::Solution( - Solution::builder().kind(SolutionKind::Exception).build(), - ))?; + self.stop_simulation(StopReason::Solution { + kind: SolutionKind::Exception, + })?; } Ok(()) } @@ -357,9 +520,9 @@ impl Tsffs { transaction as usize ); - self.stop_simulation(StopReason::Solution( - Solution::builder().kind(SolutionKind::Breakpoint).build(), - ))?; + self.stop_simulation(StopReason::Solution { + kind: SolutionKind::Breakpoint, + })?; } Ok(()) } @@ -369,7 +532,7 @@ impl Tsffs { pub fn on_magic_instruction( &mut self, trigger_obj: *mut ConfObject, - magic_number: i64, + magic_number: MagicNumber, ) -> Result<()> { trace!( self.as_conf_object(), @@ -377,18 +540,58 @@ impl Tsffs { ); if object_is_processor(trigger_obj)? { - if self.start_on_harness && magic_number == self.magic_start { - self.stop_simulation(StopReason::MagicStart( - MagicStart::builder().processor(trigger_obj).build(), - ))?; - } else if self.stop_on_harness && magic_number == self.magic_stop { - self.stop_simulation(StopReason::MagicStop(Stop::default()))?; - } else if self.stop_on_harness && magic_number == self.magic_assert { - self.stop_simulation(StopReason::Solution( - Solution::builder().kind(SolutionKind::Manual).build(), - ))?; + let processor_number = get_processor_number(trigger_obj)?; + + if !self.processors.contains_key(&processor_number) { + self.add_processor(trigger_obj, false)?; } + + let processor = self + .processors + .get_mut(&processor_number) + .ok_or_else(|| anyhow!("Processor not found"))?; + + let index_selector = processor.get_magic_index_selector()?; + + if match magic_number { + MagicNumber::StartBufferPtrSizePtr + | MagicNumber::StartBufferPtrSizeVal + | MagicNumber::StartBufferPtrSizePtrVal => { + self.start_on_harness + && (if self.magic_start_index == index_selector { + // Set this processor as the start processor now that we know it is + // enabled, but only set if it is not already set + let _ = self.start_processor_number.get_or_init(|| processor_number); + true + } else { + debug!( + "Not setting processor {} as start processor", + processor_number + ); + false + }) + } + MagicNumber::StopNormal => { + self.stop_on_harness && self.magic_stop_indices.contains(&index_selector) + } + MagicNumber::StopAssert => { + self.stop_on_harness && self.magic_assert_indices.contains(&index_selector) + } + } { + self.stop_simulation(StopReason::Magic { magic_number })?; + } else { + debug!( + self.as_conf_object(), + "Magic instruction {magic_number} was triggered by processor {trigger_obj:?} with index {index_selector} but the index is not configured for this magic number or start/stop on harness was disabled. Configured indices are: start: {}, stop: {:?}, assert: {:?}", + self.magic_start_index, + self.magic_stop_indices, + self.magic_assert_indices + ); + } + } else { + bail!("Magic instruction was triggered by a non-processor object"); } + Ok(()) } } diff --git a/src/interfaces/fuzz.rs b/src/interfaces/fuzz.rs index 6c9046dc..3fae6173 100644 --- a/src/interfaces/fuzz.rs +++ b/src/interfaces/fuzz.rs @@ -2,8 +2,8 @@ // SPDX-License-Identifier: Apache-2.0 use crate::{ - state::{ManualStart, ManualStartSize, Solution, SolutionKind, Stop, StopReason}, - Tsffs, + state::{SolutionKind, StopReason}, + ManualStartAddress, ManualStartInfo, ManualStartSize, Tsffs, }; use anyhow::{anyhow, Result}; use libafl::inputs::HasBytesVec; @@ -70,26 +70,35 @@ impl Tsffs { /// /// If your target cannot take advantage of the written-back size pointer, use /// `start_with_max_size` instead. - pub fn start( + pub fn start_with_buffer_ptr_size_ptr( &mut self, cpu: *mut ConfObject, - testcase_address: GenericAddress, + buffer_address: GenericAddress, size_address: GenericAddress, virt: bool, ) -> Result<()> { debug!( self.as_conf_object(), - "start({testcase_address:#x}, {size_address:#x})" + "start({buffer_address:#x}, {size_address:#x})" ); - self.stop_simulation(StopReason::ManualStart( - ManualStart::builder() - .processor(cpu) - .buffer(testcase_address) - .size(ManualStartSize::SizeAddress(size_address)) - .virt(virt) - .build(), - ))?; + self.stop_simulation(StopReason::ManualStart { + processor: cpu, + info: ManualStartInfo { + address: if virt { + ManualStartAddress::Virtual(buffer_address) + } else { + ManualStartAddress::Physical(buffer_address) + }, + size: ManualStartSize::SizePtr { + address: if virt { + ManualStartAddress::Virtual(size_address) + } else { + ManualStartAddress::Physical(size_address) + }, + }, + }, + })?; Ok(()) } @@ -108,7 +117,7 @@ impl Tsffs { /// If your target does not have a buffer readily available to receive testcase data or /// you simply want to use it directly in some other way (e.g. by sending it to a network /// port), use `start_without_buffer` - pub fn start_with_maximum_size( + pub fn start_with_buffer_ptr_size_value( &mut self, cpu: *mut ConfObject, testcase_address: GenericAddress, @@ -120,14 +129,66 @@ impl Tsffs { "start_with_maximum_size({testcase_address:#x}, {maximum_size:#x})" ); - self.stop_simulation(StopReason::ManualStart( - ManualStart::builder() - .processor(cpu) - .buffer(testcase_address) - .size(ManualStartSize::MaximumSize(maximum_size as u64)) - .virt(virt) - .build(), - ))?; + self.stop_simulation(StopReason::ManualStart { + processor: cpu, + info: ManualStartInfo { + address: if virt { + ManualStartAddress::Virtual(testcase_address) + } else { + ManualStartAddress::Physical(testcase_address) + }, + size: ManualStartSize::MaxSize(maximum_size.try_into()?), + }, + })?; + + Ok(()) + } + + /// Interface method to manually start the fuzzing loop by taking a snapshot, saving + /// the testcase, size address, and maximum testcase size and resuming execution of the + /// simulation. This method does not need to be called if `set_start_on_harness` is enabled. + /// + /// # Arguments + /// + /// * `cpu` - The CPU whose memory space should be written + /// * `testcase_address` - The address to write test cases to + /// * `size_address` - The address to write the size of each test case to + /// * `maximum_size` - The maximum size of the test case. The actual size of each test case will + /// be written back to the target software at the provided size address. + /// + /// If your target cannot take advantage of the written-back size pointer, use + /// `start_with_max_size` instead. + pub fn start_with_buffer_ptr_size_ptr_value( + &mut self, + cpu: *mut ConfObject, + buffer_address: GenericAddress, + size_address: GenericAddress, + maximum_size: u32, + virt: bool, + ) -> Result<()> { + debug!( + self.as_conf_object(), + "start({buffer_address:#x}, {size_address:#x}, {maximum_size:#x})" + ); + + self.stop_simulation(StopReason::ManualStart { + processor: cpu, + info: ManualStartInfo { + address: if virt { + ManualStartAddress::Virtual(buffer_address) + } else { + ManualStartAddress::Physical(buffer_address) + }, + size: ManualStartSize::SizePtrAndMaxSize { + address: if virt { + ManualStartAddress::Virtual(size_address) + } else { + ManualStartAddress::Physical(size_address) + }, + maximum_size: maximum_size.try_into()?, + }, + }, + })?; Ok(()) } @@ -149,12 +210,10 @@ impl Tsffs { // Start the fuzzer thread early so we can get a testcase self.start_fuzzer_thread()?; } + let testcase = self.get_testcase()?; - self.cmplog_enabled = testcase.cmplog; - self.stop_simulation(StopReason::ManualStart( - ManualStart::builder().processor(cpu).build(), - ))?; + self.stop_simulation(StopReason::ManualStartWithoutBuffer { processor: cpu })?; Ok(testcase.testcase.bytes().to_vec().try_into()?) } @@ -168,7 +227,7 @@ impl Tsffs { pub fn stop(&mut self) -> Result<()> { debug!(self.as_conf_object(), "stop"); - self.stop_simulation(StopReason::ManualStop(Stop::default()))?; + self.stop_simulation(StopReason::ManualStop)?; Ok(()) } @@ -182,9 +241,9 @@ impl Tsffs { debug!(self.as_conf_object(), "solution({id:#x}, {message})"); - self.stop_simulation(StopReason::Solution( - Solution::builder().kind(SolutionKind::Manual).build(), - ))?; + self.stop_simulation(StopReason::Solution { + kind: SolutionKind::Manual, + })?; Ok(()) } diff --git a/src/interfaces/mod.rs b/src/interfaces/mod.rs index 3158fc35..ec8d20ab 100644 --- a/src/interfaces/mod.rs +++ b/src/interfaces/mod.rs @@ -5,4 +5,3 @@ pub(crate) mod config; pub(crate) mod fuzz; -pub(crate) mod tsffs; diff --git a/src/interfaces/tsffs.rs b/src/interfaces/tsffs.rs deleted file mode 100644 index 26a5d734..00000000 --- a/src/interfaces/tsffs.rs +++ /dev/null @@ -1,596 +0,0 @@ -// Copyright (C) 2024 Intel Corporation -// SPDX-License-Identifier: Apache-2.0 - -//! Backward-compatibility interface - -use crate::{ - arch::ArchitectureHint, - state::Stop, - state::{ManualStart, ManualStartSize, StopReason}, - Solution, SolutionKind, Tsffs, -}; -use anyhow::anyhow; -use libafl::inputs::HasBytesVec; -use simics::{ - continue_simulation, debug, error, get_processor_number, interface, lookup_file, run_alone, - version, AsConfObject, AttrValue, BreakpointId, ConfObject, GenericAddress, Result, -}; -use std::{ - ffi::{c_char, CStr}, - fs::read, - str::FromStr, -}; - -#[interface(name = "tsffs")] -impl Tsffs { - /// Interface method to enable or disable the fuzzer to start automatically when it - /// reaches the default start condition for the architecture of the processor that - /// is running when the default start condition occurs. Note that this method will - /// not resume or run the simulation, the SIMICS script containing this call should - /// resume execution afterward. - /// - /// These conditions are: - /// - /// # x86_64 - /// - /// - Magic instruction executed with `n=1` - /// * `rsi` - set to the address the fuzzer should write the testcase to each execution - /// * `rdi` - set to the address of a variable containing the maximum size of a testcase, - /// which will be overwritten each execution with the current actual size of the testcase - /// - /// # x86_32 - /// - /// - Magic instruction executed with `n=1` - /// * `esi` - set to the address the fuzzer should write the testcase to each execution - /// * `edi` - set to the address of a variable containing the maximum size of a testcase, - /// which will be overwritten each execution with the current actual size of the testcase - /// - /// # RISC-V - /// - /// - Magic instruction executed with `n=1` - /// * `x10` - set to the address the fuzzer should write the testcase to each execution - /// * `x11` - set to the address of a variable containing the maximum size of a testcase, - /// which will be overwritten each execution with the current actual size of the testcase - /// - /// # ARM - /// - /// - Magic instruction executed with `n=1` - /// * `r0` - set to the address the fuzzer should write the testcase to each execution - /// * `r1` - set to the address of a variable containing the maximum size of a testcase, - /// which will be overwritten each execution with the current actual size of the testcase - /// - /// # ARM Thumb-2 - /// - /// - Magic instruction executed with `n=1` - /// * `r0` - set to the address the fuzzer should write the testcase to each execution - /// * `r1` - set to the address of a variable containing the maximum size of a testcase, - /// which will be overwritten each execution with the current actual size of the testcase - /// - /// # ARMv8 - /// - /// - Magic instruction executed with `n=1` - /// * `x0` - set to the address the fuzzer should write the testcase to each execution - /// * `x1` - set to the address of a variable containing the maximum size of a testcase, - /// which will be overwritten each execution with the current actual size of the testcase - /// - /// # ARC - /// - /// - Magic instruction executed with `n=1` - /// * `r0` - set to the address the fuzzer should write the testcase to each execution - /// * `r1` - set to the address of a variable containing the maximum size of a testcase, - /// which will be overwritten each execution with the current actual size of the testcase - pub fn set_start_on_harness(&mut self, start_on_harness: bool) -> Result<()> { - debug!( - self.as_conf_object(), - "set_start_on_harness({start_on_harness})" - ); - - self.start_on_harness = start_on_harness; - - Ok(()) - } - - /// Interface method to set the magic value the fuzzer will wait for when - /// `set_start_on_harness` has ben configured. This allows you to place multiple harnesses in - /// a single binary and selectively enable one of them. - pub fn set_start_magic_number(&mut self, magic_number: i64) { - debug!( - self.as_conf_object(), - "set_start_magic_number({magic_number})" - ); - - self.magic_start = magic_number; - } - - /// Interface method to enable or disable the fuzzer to stop automatically when it - /// reaches the default stop condition for the architecture of the processor that is - /// running when the default stop condition occurs. Note that this method will not - /// resume or run the simulation, the SIMICS script containing this call should - /// resume execution afterward. - pub fn set_stop_on_harness(&mut self, stop_on_harness: bool) -> Result<()> { - debug!( - self.as_conf_object(), - "set_stop_on_harness({stop_on_harness})" - ); - - self.stop_on_harness = stop_on_harness; - - Ok(()) - } - - /// Interface method to set the magic value the fuzzer will wait for when - /// `set_start_on_harness` has ben configured. This allows you to place multiple harnesses in - /// a single binary and selectively enable one of them. - pub fn set_stop_magic_number(&mut self, magic_number: i64) { - debug!( - self.as_conf_object(), - "set_stop_magic_number({magic_number})" - ); - - self.magic_stop = magic_number; - } - - #[interface(name = "start")] - /// Interface method to manually start the fuzzing loop by taking a snapshot, saving the - /// testcase and size address and resuming execution of the simulation. This method does - /// not need to be called if `set_start_on_harness` is enabled. - /// - /// # Arguments - /// - /// * `cpu` - The CPU whose memory space should be written - /// * `testcase_address` - The address to write test cases to - /// * `size_address` - The address to write the size of each test case to (optional, - /// `max_size` must be given if not provided). - /// - /// If your target cannot take advantage of the written-back size pointer, use - /// `start_with_max_size` instead. - pub fn start_legacy( - &mut self, - cpu: *mut ConfObject, - testcase_address: GenericAddress, - size_address: GenericAddress, - virt: bool, - ) -> Result<()> { - debug!( - self.as_conf_object(), - "start({testcase_address:#x}, {size_address:#x})" - ); - - self.stop_simulation(StopReason::ManualStart( - ManualStart::builder() - .processor(cpu) - .buffer(testcase_address) - .size(ManualStartSize::SizeAddress(size_address)) - .virt(virt) - .build(), - ))?; - - Ok(()) - } - - #[interface(name = "start_with_maximum_size")] - /// Interface method to manually start the fuzzing loop by taking a snapshot, saving - /// the testcase and maximum testcase size and resuming execution of the simulation. - /// This method does not need to be called if `set_start_on_harness` is enabled. - /// - /// # Arguments - /// - /// * `cpu` - The CPU whose memory space should be written - /// * `testcase_address` - The address to write test cases to - /// * `maximum_size` - The maximum size of the test case. The actual size of each test case will - /// not be written back to the target software - /// - /// If your target does not have a buffer readily available to receive testcase data or - /// you simply want to use it directly in some other way (e.g. by sending it to a network - /// port), use `start_without_buffer` - pub fn start_with_maximum_size_legacy( - &mut self, - cpu: *mut ConfObject, - testcase_address: GenericAddress, - maximum_size: u32, - virt: bool, - ) -> Result<()> { - debug!( - self.as_conf_object(), - "start_with_maximum_size({testcase_address:#x}, {maximum_size:#x})" - ); - - self.stop_simulation(StopReason::ManualStart( - ManualStart::builder() - .processor(cpu) - .buffer(testcase_address) - .size(ManualStartSize::MaximumSize(maximum_size as u64)) - .virt(virt) - .build(), - ))?; - - Ok(()) - } - - #[interface(name = "start_without_buffer")] - /// Interface method to manually start the fuzzing loop by taking a snapshot, saving - /// the testcase and maximum testcase size and resuming execution of the simulation. - /// This method does not need to be called if `set_start_on_harness` is enabled. - /// - /// # Arguments - /// - /// * `cpu` - The CPU to initially trace and post timeout events on. This should typically be - /// the CPU that is running the code receiving the input this function returns. - /// - /// # Return Value - /// - /// Returns an [`AttrValue`] list of integers. Integers are `u8` sized, in the range 0-255. - pub fn start_without_buffer_legacy(&mut self, cpu: *mut ConfObject) -> Result { - if !self.have_initial_snapshot() { - // Start the fuzzer thread early so we can get a testcase - self.start_fuzzer_thread()?; - } - let testcase = self.get_testcase()?; - self.cmplog_enabled = testcase.cmplog; - - self.stop_simulation(StopReason::ManualStart( - ManualStart::builder().processor(cpu).build(), - ))?; - - testcase.testcase.bytes().to_vec().try_into() - } - - #[interface(name = "stop")] - /// Interface method to manually signal to stop a testcase execution. When this - /// method is called, the current testcase execution will be stopped as if it had - /// finished executing normally, and the state will be restored to the state at the - /// initial snapshot. This method is particularly useful in callbacks triggered on - /// breakpoints or other complex conditions. This method does - /// not need to be called if `set_stop_on_harness` is enabled. - pub fn stop_legacy(&mut self) -> Result<()> { - debug!(self.as_conf_object(), "stop"); - - self.stop_simulation(StopReason::ManualStop(Stop::default()))?; - - Ok(()) - } - - #[interface(name = "solution")] - /// Interface method to manually signal to stop execution with a solution condition. - /// When this method is called, the current testcase execution will be stopped as if - /// it had finished executing with an exception or timeout, and the state will be - /// restored to the state at the initial snapshot. - pub fn solution_legacy(&mut self, id: u64, message: *mut c_char) -> Result<()> { - let message = unsafe { CStr::from_ptr(message) }.to_str()?; - - debug!(self.as_conf_object(), "solution({id:#x}, {message})"); - - self.stop_simulation(StopReason::Solution( - Solution::builder().kind(SolutionKind::Manual).build(), - ))?; - - Ok(()) - } - - #[interface(name = "repro_legacy")] - /// Reproduce a test case execution. This will set the fuzzer's next input through - /// one execution using the provided file as input instead of taking input from the - /// fuzzer. It will stop execution at the first stop, timeout, or other solution - /// instead of continuing the fuzzing loop. - /// - /// This can be called during configuration *or* after stopping the fuzzer once a solution - /// has been found. - pub fn repro_legacy(&mut self, testcase_file: *mut c_char) -> Result<()> { - let simics_path = unsafe { CStr::from_ptr(testcase_file) }.to_str()?; - - let testcase_file = lookup_file(simics_path)?; - - debug!(self.as_conf_object(), "repro({})", testcase_file.display()); - - let contents = read(&testcase_file).map_err(|e| { - anyhow!( - "Failed to read repro testcase file {}: {}", - testcase_file.display(), - e - ) - })?; - - self.repro_testcase = Some(contents); - - if self.iterations > 0 { - // We've done an iteration already, so we need to reset and run - self.restore_initial_snapshot()?; - self.get_and_write_testcase()?; - self.post_timeout_event()?; - - run_alone(|| { - continue_simulation(0)?; - Ok(()) - })?; - } - - Ok(()) - } - - /// Interface method to set the fuzzer to use the experimental snapshots interface - /// instead of the micro checkpoints interface for snapshot save and restore operations - pub fn set_use_snapshots(&mut self, use_snapshots: bool) -> Result<()> { - debug!(self.as_conf_object(), "use_snapshots({use_snapshots})"); - - if cfg!(simics_experimental_api_snapshots) - || cfg!(simics_experimental_api_snapshots_v2) - || cfg!(simics_stable_api_snapshots) - { - self.use_snapshots = use_snapshots; - } else if use_snapshots { - let version = version()?; - - error!( - self.as_conf_object(), - "Not enabling snapshots, API is unsupported for target SIMICS version {version}", - ); - } else { - // NOTE: We don't report an error if snapshots are turned off when they are unsupported - } - - Ok(()) - } - - /// Interface method to set the execution timeout in seconds - pub fn set_timeout(&mut self, timeout: f64) { - debug!(self.as_conf_object(), "set_timeout({timeout})"); - - self.timeout = timeout; - } - - /// Interface method to add an exception-type solution number to the set of - /// exception-type solution numbers currently being monitored for. If any exception in - /// the set of exceptions currently monitored occurs, the testcase will be saved and - /// reported as a solution. - /// - /// For example on x86_64, `add_exception_solution(14)` would treat any page fault as - /// a solution. - pub fn add_exception_solution(&mut self, exception: i64) { - debug!(self.as_conf_object(), "add_exception_solution({exception})"); - - self.exceptions.insert(exception); - } - - /// Interface method to remove an exception-type solution number from the set of - /// exception-type solution numbers currently being monitored for. If any exception in - /// the set of solutions currently monitored occurs, the testcase will be saved and - /// reported as a solution. - pub fn remove_exception_solution(&mut self, exception: i64) { - debug!( - self.as_conf_object(), - "remove_exception_solution({exception})" - ); - - self.exceptions.remove(&exception); - } - - /// Set whether all CPU exceptions are considered solutions. If set to true, any - /// exception encountered during fuzzing will be saved as a solution. This is typically - /// not desired. - pub fn set_all_exceptions_are_solutions(&mut self, all_exceptions_are_solutions: bool) { - debug!( - self.as_conf_object(), - "set_all_exceptions_are_solutions({all_exceptions_are_solutions})" - ); - - self.all_exceptions_are_solutions = all_exceptions_are_solutions; - } - - /// Set a specific breakpoint number to be considered a solution. If a breakpoint with - /// this ID is encountered during fuzzing, the input will be saved as a solution. - pub fn add_breakpoint_solution(&mut self, breakpoint: BreakpointId) { - debug!( - self.as_conf_object(), - "add_breakpoint_solution({breakpoint})" - ); - - self.breakpoints.insert(breakpoint); - } - - /// Remove a specific breakpoint from consideration as a solution. If a breakpoint with - /// this ID is encountered during fuzzing, the input will be saved as a solution. - pub fn remove_breakpoint_solution(&mut self, breakpoint: BreakpointId) { - debug!( - self.as_conf_object(), - "remove_breakpoint_solution({breakpoint})" - ); - self.breakpoints.remove(&breakpoint); - } - - /// Set whether all SIMICS breakpoints are considered solutions. If set to true, any - /// breakpoint (read, write, or execute) encountered during fuzzing will be saved as - /// a solution. - pub fn set_all_breakpoints_are_solutions(&mut self, all_breakpoints_are_solutions: bool) { - debug!( - self.as_conf_object(), - "set_all_breakpoints_are_solutions({all_breakpoints_are_solutions})" - ); - - self.all_breakpoints_are_solutions = all_breakpoints_are_solutions; - } - - /// Set whether cmplog is enabled or disabled. Cmplog adds stages to trace and - /// analyze comparison operands during target software execution and mutate test - /// cases strategically using the logged operands. Execution speed is lower when - /// running with cmplog enabled, but the efficiency gain from improved mutations - /// typically makes up for the lost speed by many orders of magnitude. It is - /// particularly well suited for software which performs magic value checks, large - /// value and string comparisons, and sums. - pub fn set_cmplog_enabled(&mut self, enabled: bool) { - debug!(self.as_conf_object(), "set_cmplog_enabled({enabled})"); - - self.cmplog = enabled; - } - - /// Set the directory path where the input corpus should be taken from when the - /// fuzzer first starts, and where new corpus items will be saved. This path may be - /// a SIMICS relative path prefixed with "%simics%". It is an error to provide no - /// corpus directory when `set_generate_random_corpus(True)` has not been called - /// prior to fuzzer startup. It is also an error to provide an *empty* corpus - /// directory without calling `set_generate_random_corpus(True)`. If not provided, - /// "%simics%/corpus" will be used by default. - pub fn set_corpus_directory(&mut self, corpus_directory: *mut c_char) -> Result<()> { - let corpus_directory_path = unsafe { CStr::from_ptr(corpus_directory) }.to_str()?; - - if let Ok(corpus_directory) = lookup_file(corpus_directory_path) { - debug!( - self.as_conf_object(), - "set_corpus_directory({})", - corpus_directory.display(), - ); - - self.corpus_directory = corpus_directory; - } else { - error!(self.as_conf_object(), "Corpus directory cannot be set. The requested directory {corpus_directory_path} does not exist."); - } - - Ok(()) - } - - /// Set the directory path where solutions should be saved when the fuzzer finds them. This - /// directory will contain the fuzzer inputs which triggered any solution condition that had - /// been configured for the fuzzing campaign. These entries can be used to reproduce - /// and traige defects using the `reproduce` method. If no solutions directory is provided, - /// "%simics%/solutions" will be used by default. - pub fn set_solutions_directory(&mut self, solutions_directory: *mut c_char) -> Result<()> { - let solutions_directory_path = unsafe { CStr::from_ptr(solutions_directory) }.to_str()?; - - if let Ok(solutions_directory) = lookup_file(solutions_directory_path) { - debug!( - self.as_conf_object(), - "set_solutions_directory({})", - solutions_directory.display(), - ); - - self.solutions_directory = solutions_directory; - } else { - error!(self.as_conf_object(), "Solutions directory cannot be set. The requested directory {solutions_directory_path} does not exist."); - } - - Ok(()) - } - - /// Set whether a random corpus should be generated in the event that a corpus directory is - /// not provided, or an empty corpus directory is provided. This option defaults to false - /// because the penalty for using a random corpus is extremely high and corpus entries should - /// be customized for the target software wherever possible. By setting this option, you - /// should be aware your fuzz campaign's efficiency will be lowered. This is, however, very - /// useful for demonstration and test purposes. - pub fn set_generate_random_corpus(&mut self, generate_random_corpus: bool) -> Result<()> { - debug!( - self.as_conf_object(), - "set_generate_random_corpus({generate_random_corpus})" - ); - - self.generate_random_corpus = generate_random_corpus; - - Ok(()) - } - - /// Set the number of iterations to run the fuzzer for. This is the number of actual testcases - /// executed, and includes all stages (e.g. calibration). This should typically not be used - /// to limit the time of a fuzzing campaign, and is only useful for demonstration purposes. - pub fn set_iterations(&mut self, iterations: usize) -> Result<()> { - debug!(self.as_conf_object(), "set_iterations({iterations})"); - - self.iteration_limit = iterations; - - Ok(()) - } - - /// Tokenize an executable file and add extracted tokens to token mutations for the fuzzer - pub fn tokenize_executable(&mut self, executable_file: *mut c_char) -> Result<()> { - let simics_path = unsafe { CStr::from_ptr(executable_file) }.to_str()?; - - let executable_path = lookup_file(simics_path)?; - - debug!( - self.as_conf_object(), - "tokenize_executable({})", - executable_path.display() - ); - - self.token_executables.push(executable_path); - - Ok(()) - } - - /// Tokenize a source file and add extracted tokens to token mutations for the fuzzer - pub fn tokenize_src(&mut self, source_file: *mut c_char) -> Result<()> { - let simics_path = unsafe { CStr::from_ptr(source_file) }.to_str()?; - - let source_path = lookup_file(simics_path)?; - - debug!( - self.as_conf_object(), - "tokenize_src({})", - source_path.display() - ); - - self.token_src_files.push(source_path); - - Ok(()) - } - - /// Add tokens from a file of the format below, containing tokens extracted from the fuzz - /// target: - /// ```text,ignore - /// x = "hello" - /// y = "foo\x41bar" - /// ``` - pub fn add_token_file(&mut self, token_file: *mut c_char) -> Result<()> { - let simics_path = unsafe { CStr::from_ptr(token_file) }.to_str()?; - - let token_file = lookup_file(simics_path)?; - - debug!( - self.as_conf_object(), - "add_token_file({})", - token_file.display() - ); - - if token_file.is_file() { - self.token_files.push(token_file); - } - - Ok(()) - } - - #[interface(name = "add_trace_processor")] - /// Add a processor to be traced. By default, only the processor the start event occurs on - /// is used for tracing. - pub fn add_trace_processor_legacy(&mut self, cpu: *mut ConfObject) -> Result<()> { - debug!( - self.as_conf_object(), - "add_trace_processor({:#x})", cpu as usize - ); - - self.add_processor(cpu, false)?; - - Ok(()) - } - - #[interface(name = "add_architecture_hint")] - /// Set an architecture hint to be used for a particular processor. This allows overriding - /// the detected or reported architecture for the processor object. This is particularly - /// useful for x86 processors which report as x86-64 processors, or when fuzzing x86 code - /// running on an x86-64 processor in a backward compatibility mode. - pub fn add_architecture_hint_legacy( - &mut self, - cpu: *mut ConfObject, - hint: *mut c_char, - ) -> Result<()> { - let hint = unsafe { CStr::from_ptr(hint) }.to_str()?; - let processor_number = get_processor_number(cpu)?; - - debug!( - self.as_conf_object(), - "add_architecture_hint({processor_number}, {hint})" - ); - - self.architecture_hints - .insert(processor_number, ArchitectureHint::from_str(hint)?); - - Ok(()) - } -} diff --git a/src/lib.rs b/src/lib.rs index 9f5893c8..83973db8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -29,19 +29,18 @@ #![warn(missing_docs)] use crate::interfaces::{config::config, fuzz::fuzz}; -use crate::state::{Solution, SolutionKind}; +use crate::state::SolutionKind; #[cfg(not(simics_deprecated_api_rev_exec))] use crate::util::Utils; use anyhow::{anyhow, Result}; use arch::{Architecture, ArchitectureHint, ArchitectureOperations}; use fuzzer::{messages::FuzzerMessage, ShutdownMessage, Testcase}; use indoc::indoc; -use libafl::{ - inputs::{HasBytesVec, Input}, - prelude::ExitKind, -}; +use libafl::{inputs::HasBytesVec, prelude::ExitKind}; use libafl_bolts::prelude::OwnedMutSlice; use libafl_targets::AFLppCmpLogMap; +use magic::MagicNumber; +use num_traits::FromPrimitive as _; use serde::{Deserialize, Serialize}; use simics::{ break_simulation, class, error, free_attribute, get_class, get_interface, get_processor_number, @@ -77,7 +76,7 @@ use std::{ alloc::{alloc_zeroed, Layout}, cell::OnceCell, collections::{hash_map::Entry, BTreeSet, HashMap, HashSet}, - fs::{write, File}, + fs::File, path::PathBuf, ptr::null_mut, sync::mpsc::{Receiver, Sender}, @@ -92,6 +91,7 @@ pub(crate) mod fuzzer; pub(crate) mod haps; pub(crate) mod interfaces; pub(crate) mod log; +pub(crate) mod magic; pub(crate) mod state; pub(crate) mod tracer; pub(crate) mod traits; @@ -101,26 +101,106 @@ pub(crate) mod util; pub const CLASS_NAME: &str = env!("CARGO_PKG_NAME"); +#[derive(Serialize, Deserialize, Clone, Debug)] +/// An address that was formerly virtual or formerly physical. The actual +/// address *must* be physical. +pub(crate) enum StartPhysicalAddress { + /// The address was formerly virtual + WasVirtual(u64), + /// The address was formerly physical + WasPhysical(u64), +} + +impl StartPhysicalAddress { + /// Get the physical address + pub fn physical_address(&self) -> u64 { + match self { + StartPhysicalAddress::WasVirtual(addr) => *addr, + StartPhysicalAddress::WasPhysical(addr) => *addr, + } + } +} + +#[derive(Serialize, Deserialize, Clone, Debug)] +pub(crate) enum ManualStartAddress { + Virtual(u64), + Physical(u64), +} + +impl ManualStartAddress { + pub fn address(&self) -> u64 { + match self { + ManualStartAddress::Virtual(addr) => *addr, + ManualStartAddress::Physical(addr) => *addr, + } + } +} + #[derive(TypedBuilder, Serialize, Deserialize, Clone, Debug)] -pub(crate) struct StartBuffer { +pub(crate) struct StartInfo { /// The physical address of the buffer. Must be physical, if the input address was /// virtual, it should be pre-translated - pub physical_address: u64, - /// Whether the address that translated to this physical address was virtual - /// this should not be used or checked, it's simply informational - pub virt: bool, + pub address: StartPhysicalAddress, + /// The initial contents of the buffer + pub contents: Vec, + /// The initial size of the buffer. This will either be only an address, in which + /// case the initial size will be `*size_ptr` and the actual size of each testcase + /// will be written back to `*size_ptr`, a `max_size` in which case the size will + /// not be written, or a `size_ptr` and `max_size` in which case the size will be + /// written back to `*size_ptr` and the maximum size will be `max_size`. + pub size: StartSize, } -#[derive(TypedBuilder, Serialize, Deserialize, Clone, Debug)] -pub(crate) struct StartSize { - #[builder(default, setter(into, strip_option))] - /// The address of the magic start size value, and whether the address that translated - /// to this physical address was virtual. The address must be physical. - pub physical_address: Option<(u64, bool)>, - #[builder(default, setter(into, strip_option))] - // NOTE: There is no need to save the size fo the size, it must be pointer-sized. - /// The initial size of the magic start size - pub initial_size: Option, +#[derive(Serialize, Deserialize, Clone, Debug)] +/// Exactly the same as `StartInfo` except with the semantic difference that the address +/// may not always be stored as physical, the user may provide a virtual address for both +/// the address and the size pointer (if there is one). +pub(crate) struct ManualStartInfo { + pub address: ManualStartAddress, + pub size: ManualStartSize, +} + +#[derive(Serialize, Deserialize, Clone, Debug)] +pub(crate) enum StartSize { + SizePtr { + address: StartPhysicalAddress, + maximum_size: usize, + }, + MaxSize(usize), + SizePtrAndMaxSize { + address: StartPhysicalAddress, + maximum_size: usize, + }, +} + +impl StartSize { + pub fn maximum_size(&self) -> usize { + match self { + StartSize::SizePtr { maximum_size, .. } => *maximum_size, + StartSize::MaxSize(maximum_size) => *maximum_size, + StartSize::SizePtrAndMaxSize { maximum_size, .. } => *maximum_size, + } + } + + pub fn physical_address(&self) -> Option { + match self { + StartSize::SizePtr { address, .. } => Some(address.clone()), + StartSize::MaxSize(_) => None, + StartSize::SizePtrAndMaxSize { address, .. } => Some(address.clone()), + } + } +} + +#[derive(Serialize, Deserialize, Clone, Debug)] +pub(crate) enum ManualStartSize { + SizePtr { + address: ManualStartAddress, + }, + MaxSize(usize), + SizePtrAndMaxSize { + address: ManualStartAddress, + maximum_size: usize, + }, } #[class(name = "tsffs", skip_objects_finalize, attr_value)] @@ -195,26 +275,35 @@ pub(crate) struct Tsffs { /// the version of SIMICS being used, the fuzzer will quit with an error message when this /// option is set. pub use_snapshots: bool, - #[class(attribute(optional, default = 1))] - /// The magic number `n` which is passed to the platform-specific magic instruction HAP + #[class(attribute(optional, default = 0))] + /// The index number which is passed to the platform-specific magic instruction HAP /// by a compiled-in harness to signal that the fuzzer should start the fuzzing loop. /// /// This option is useful when fuzzing a target which has multiple start harnesses compiled /// into it, and the fuzzer should start on a specific harness. - pub magic_start: i64, - #[class(attribute(optional, default = 2))] - /// The magic number `n` which is passed to the platform-specific magic instruction HAP + /// + /// There can only be one magic start value, because only one fuzzing loop can be running + /// (and they cannot be nested). This only has an effect if `start_on_harness` is set. + pub magic_start_index: u64, + #[class(attribute(optional, default = vec![0]))] + #[attr_value(fallible)] + /// The magic numbers which is passed to the platform-specific magic instruction HAP /// by a compiled-in harness to signal that the fuzzer should stop execution of the current /// iteration. /// /// This option is useful when fuzzing a target which has multiple stop harnesses compiled - /// into it, and the fuzzer should stop on a specific harness. - pub magic_stop: i64, - #[class(attribute(optional, default = 3))] - /// The magic number `n` which is passed to the platform-specific magic instruction HAP - /// by a compiled-in harness to signal that the fuzzer should stop execution of the current - /// iteration and save the testcase as a solution. - pub magic_assert: i64, + /// into it, and the fuzzer should stop on a specific subset of stop harness macro calls. + /// + /// This only has an effect if `stop_on_harness` is set. + pub magic_stop_indices: Vec, + #[class(attribute(optional, default = vec![0]))] + #[attr_value(fallible)] + /// The numbers which are passed to the platform-specific magic instruction HAP by a + /// compiled-in harness to signal that the fuzzer should stop execution of the + /// current iteration and save the testcase as a solution. + /// + /// This only has an effect if `stop_on_harness` is set. + pub magic_assert_indices: Vec, #[class(attribute(optional))] /// The limit on the number of fuzzing iterations to execute. If set to 0, the fuzzer will /// run indefinitely. If set to a positive integer, the fuzzer will run until the limit is @@ -283,13 +372,6 @@ pub(crate) struct Tsffs { /// Sets of tokens to use to drive token mutations of testcases. Each token set is a /// bytes which will be randomically inserted into testcases. pub tokens: Vec>, - #[attr_value(skip)] - /// A mapping of architecture hints from CPU index to architecture hint. This architecture - /// hint overrides the detected architecture of the CPU core. This is useful when the - /// architecture of the CPU core is not detected correctly, or when the architecture of the - /// CPU core is not known at the time the fuzzer is started. Specifically, x86 cores which - /// report their architecture as x86_64 can be overridden to x86. - pub architecture_hints: HashMap, #[class(attribute(optional, default = lookup_file("%simics%")?.join("checkpoint.ckpt")))] #[attr_value(fallible)] /// The path to the checkpoint saved prior to fuzzing when using snapshots @@ -305,6 +387,10 @@ pub(crate) struct Tsffs { #[class(attribute(optional, default = false))] pub keep_all_corpus: bool, #[class(attribute(optional, default = false))] + /// Whether to use the initial contents of the testcase buffer as an entry in the corpus + pub use_initial_as_corpus: bool, + #[class(attribute(optional, default = false))] + /// Whether to enable extra debug logging for LibAFL pub debug_log_libafl: bool, #[attr_value(skip)] @@ -322,6 +408,13 @@ pub(crate) struct Tsffs { /// or `stop_on_harness` are set. magic_hap_handle: HapHandle, + #[attr_value(skip)] + /// A mapping of architecture hints from CPU index to architecture hint. This architecture + /// hint overrides the detected architecture of the CPU core. This is useful when the + /// architecture of the CPU core is not detected correctly, or when the architecture of the + /// CPU core is not known at the time the fuzzer is started. Specifically, x86 cores which + /// report their architecture as x86_64 can be overridden to x86. + pub architecture_hints: HashMap, // Threads and message channels #[attr_value(skip)] /// Fuzzer thread @@ -380,9 +473,7 @@ pub(crate) struct Tsffs { stop_reason: Option, #[attr_value(skip)] /// The buffer and size information, if saved - start_buffer: OnceCell, - #[attr_value(skip)] - start_size: OnceCell, + start_info: OnceCell, #[attr_value(skip)] // #[builder(default = SystemTime::now())] @@ -456,9 +547,15 @@ impl ClassObjectsFinalize for Tsffs { CoreMagicInstructionHap::add_callback(move |trigger_obj, magic_number| { let tsffs: &'static mut Tsffs = instance.into(); - tsffs - .on_magic_instruction(trigger_obj, magic_number) - .expect("Error calling magic instruction callback"); + // NOTE: Some things (notably, the x86_64 UEFI app loader) do a + // legitimate CPUID (in the UEFI loader, with number 0xc aka + // eax=0xc4711) that registers as a magic number. We therefore permit + // non-valid magic numbers to be executed, but we do nothing for them. + if let Some(magic_number) = MagicNumber::from_i64(magic_number) { + tsffs + .on_magic_instruction(trigger_obj, magic_number) + .expect("Failed to execute on_magic_instruction callback") + } })?; tsffs .coverage_map @@ -509,7 +606,9 @@ impl Tsffs { /// Stop the simulation with a reason pub fn stop_simulation(&mut self, reason: StopReason) -> Result<()> { let break_string = reason.to_string(); + self.stop_reason = Some(reason); + break_simulation(break_string)?; Ok(()) @@ -522,6 +621,12 @@ impl Tsffs { /// "start processor" which is the processor running when the fuzzing loop begins pub fn add_processor(&mut self, cpu: *mut ConfObject, is_start: bool) -> Result<()> { let cpu_number = get_processor_number(cpu)?; + debug!( + self.as_conf_object(), + "Adding {}processor {} to fuzzer", + if is_start { "start " } else { "" }, + cpu_number + ); if let Entry::Vacant(e) = self.processors.entry(cpu_number) { let architecture = if let Some(hint) = self.architecture_hints.get(&cpu_number) { @@ -687,42 +792,18 @@ impl Tsffs { pub fn get_and_write_testcase(&mut self) -> Result<()> { let testcase = self.get_testcase()?; - if self.keep_all_corpus { - let testcase_name = testcase.testcase.generate_name(0); - trace!( - self.as_conf_object(), - "Writing testcase {}.testcase to corpus directory: {}", - &testcase_name, - self.corpus_directory.display() - ); - - write( - self.corpus_directory - .join(format!("{}.testcase", &testcase_name)), - testcase.testcase.bytes(), - )?; - } - - self.cmplog_enabled = testcase.cmplog; - // TODO: Fix cloning - refcell? - let start_buffer = self - .start_buffer - .get() - .ok_or_else(|| anyhow!("No start buffer"))? - .clone(); - - let start_size = self - .start_size + let start_info = self + .start_info .get() - .ok_or_else(|| anyhow!("No start size"))? + .ok_or_else(|| anyhow!("No start info"))? .clone(); let start_processor = self .start_processor() .ok_or_else(|| anyhow!("No start processor"))?; - start_processor.write_start(testcase.testcase.bytes(), &start_buffer, &start_size)?; + start_processor.write_start(testcase.testcase.bytes(), &start_info)?; Ok(()) } @@ -756,9 +837,9 @@ impl Tsffs { let tsffs: &'static mut Tsffs = tsffs_ptr.into(); info!(tsffs.as_conf_object_mut(), "timeout({:#x})", obj as usize); tsffs - .stop_simulation(StopReason::Solution( - Solution::builder().kind(SolutionKind::Timeout).build(), - )) + .stop_simulation(StopReason::Solution { + kind: SolutionKind::Timeout, + }) .expect("Error calling timeout callback"); }, )?; diff --git a/src/magic/mod.rs b/src/magic/mod.rs new file mode 100644 index 00000000..69b9fc2b --- /dev/null +++ b/src/magic/mod.rs @@ -0,0 +1,24 @@ +//! Magic number definitions + +use std::fmt::Display; + +use num_derive::{FromPrimitive, ToPrimitive}; +#[allow(unused_imports)] +use num_traits::{FromPrimitive as _, ToPrimitive as _}; +use serde::{Deserialize, Serialize}; + +#[repr(i64)] +#[derive(Debug, Copy, Clone, PartialEq, Deserialize, Serialize, FromPrimitive, ToPrimitive)] +pub enum MagicNumber { + StartBufferPtrSizePtr = 1, + StartBufferPtrSizeVal = 2, + StartBufferPtrSizePtrVal = 3, + StopNormal = 4, + StopAssert = 5, +} + +impl Display for MagicNumber { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", *self as i64) + } +} diff --git a/src/state/mod.rs b/src/state/mod.rs index af51a794..23f0cf5c 100644 --- a/src/state/mod.rs +++ b/src/state/mod.rs @@ -12,37 +12,8 @@ use std::{ ptr::null_mut, str::FromStr, }; -use typed_builder::TypedBuilder; -#[derive(Serialize, Deserialize, Debug, Clone)] -pub(crate) enum ManualStartSize { - MaximumSize(u64), - SizeAddress(u64), - NoSize, -} - -#[derive(TypedBuilder, Serialize, Deserialize, Debug, Clone)] -pub(crate) struct ManualStart { - #[builder(default = null_mut())] - #[serde(skip, default = "null_mut")] - pub processor: *mut ConfObject, - #[builder(default, setter(into, strip_option))] - pub buffer: Option, - #[builder(default = ManualStartSize::NoSize)] - pub size: ManualStartSize, - #[builder(default)] - pub virt: bool, -} - -#[derive(TypedBuilder, Serialize, Deserialize, Debug, Clone)] -pub(crate) struct MagicStart { - #[builder(default = null_mut())] - #[serde(skip, default = "null_mut")] - pub processor: *mut ConfObject, -} - -#[derive(TypedBuilder, Serialize, Deserialize, Debug, Clone, Default)] -pub(crate) struct Stop {} +use crate::{magic::MagicNumber, ManualStartInfo}; #[derive(Serialize, Deserialize, Debug, Clone)] pub(crate) enum SolutionKind { @@ -52,21 +23,27 @@ pub(crate) enum SolutionKind { Manual, } -#[derive(TypedBuilder, Serialize, Deserialize, Debug, Clone)] -pub(crate) struct Solution { - pub kind: SolutionKind, -} - #[derive(Debug, Clone, Serialize, Deserialize)] /// Definition of all the reasons the simulator could be stopped by the fuzzer. In general, /// callbacks in the fuzzer, for example [`Driver::on_magic_instruction`] may be called /// asynchronously and stop the simulation. pub(crate) enum StopReason { - MagicStart(MagicStart), - MagicStop(Stop), - ManualStart(ManualStart), - ManualStop(Stop), - Solution(Solution), + Magic { + magic_number: MagicNumber, + }, + ManualStart { + #[serde(skip, default = "null_mut")] + processor: *mut ConfObject, + info: ManualStartInfo, + }, + ManualStartWithoutBuffer { + #[serde(skip, default = "null_mut")] + processor: *mut ConfObject, + }, + ManualStop, + Solution { + kind: SolutionKind, + }, } impl Display for StopReason { diff --git a/tests/riscv_64_kernel_from_userspace_magic.rs b/tests/riscv_64_kernel_from_userspace_magic.rs index 9e37434d..f115f9a3 100644 --- a/tests/riscv_64_kernel_from_userspace_magic.rs +++ b/tests/riscv_64_kernel_from_userspace_magic.rs @@ -41,8 +41,8 @@ fn test_riscv_64_kernel_from_userspace_magic() -> Result<()> { @tsffs.log_level = 2 @tsffs.start_on_harness = True @tsffs.stop_on_harness = True - @tsffs.magic_start = 4 - @tsffs.magic_stop = 5 + @tsffs.magic_start_index = 1 + @tsffs.magic_stop_indices = [1] @tsffs.timeout = 3.0 @tsffs.exceptions = [14] @tsffs.generate_random_corpus = True diff --git a/tests/riscv_64_kernel_magic.rs b/tests/riscv_64_kernel_magic.rs index 82f209ab..8e94bf1f 100644 --- a/tests/riscv_64_kernel_magic.rs +++ b/tests/riscv_64_kernel_magic.rs @@ -46,6 +46,7 @@ fn test_riscv_64_kernel_magic() -> Result<()> { @tsffs.generate_random_corpus = True @tsffs.iteration_limit = 1000 @tsffs.use_snapshots = True + @tsffs.debug_log_libafl = True load-target "risc-v-simple/linux" namespace = riscv machine:hardware:storage:disk1:image = "test.fs.craff" diff --git a/tests/rsrc/build.sh b/tests/rsrc/build.sh index 06751653..42e33d11 100755 --- a/tests/rsrc/build.sh +++ b/tests/rsrc/build.sh @@ -3,6 +3,8 @@ # Copyright (C) 2024 Intel Corporation # SPDX-License-Identifier: Apache-2.0 +set -e + if [ -z "${SIMICS_BASE}" ]; then echo "SIMICS_BASE is not set, defaulting to latest." SIMICS_BASE="$(ispm packages --list-installed --json | jq -r '[ .installedPackages[] | select(.pkgNumber == 1000) ] | ([ .[].version ] | max_by(split(".") | map(tonumber))) as $m | first(first(.[]|select(.version == $m)).paths[0])')" diff --git a/tests/rsrc/riscv-64/Dockerfile b/tests/rsrc/riscv-64/Dockerfile index 6fd70598..422ab4ff 100644 --- a/tests/rsrc/riscv-64/Dockerfile +++ b/tests/rsrc/riscv-64/Dockerfile @@ -1,6 +1,8 @@ # hadolint global ignore=DL3008 FROM ubuntu:22.04 AS buildroot +ARG BUILDROOT_REV="2023.11.x" + SHELL ["/bin/bash", "-o", "pipefail", "-c"] ENV DEBIAN_FRONTEND=noninteractive @@ -20,7 +22,8 @@ RUN apt-get -y update && \ wget RUN git clone \ - https://github.com/buildroot/buildroot.git + https://github.com/buildroot/buildroot.git && \ + git -C buildroot checkout "${BUILDROOT_REV}" WORKDIR /buildroot @@ -31,7 +34,7 @@ COPY simics_simple_riscv_defconfig configs/simics_simple_riscv_defconfig COPY test.c /test/usr/test.c COPY test-mod.c /test/usr/test-mod.c COPY test-mod-userspace.c /test/usr/test-mod-userspace.c -COPY tsffs-gcc-riscv64.h /test/usr/tsffs-gcc-riscv64.h +COPY tsffs.h /test/usr/tsffs.h # Build Linux, Linux Kernel Modules & RootFS # Build size: 7.9G diff --git a/tests/rsrc/riscv-64/build.sh b/tests/rsrc/riscv-64/build.sh index fbf59765..3b507955 100755 --- a/tests/rsrc/riscv-64/build.sh +++ b/tests/rsrc/riscv-64/build.sh @@ -7,6 +7,8 @@ # this only needs to be run if you want to modify the source code for the HelloWorld.efi module, # otherwise, the EFI is included in the source tree for ease of use +set -e + SCRIPT_DIR=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" &>/dev/null && pwd) IMAGE_NAME="buildroot-build-tsffs-gcc-riscv64-test" @@ -23,8 +25,8 @@ fi pushd "${SCRIPT_DIR}" || exit 1 -cp "${SCRIPT_DIR}/../../../harness/tsffs-gcc-riscv64.h" "${SCRIPT_DIR}/tsffs-gcc-riscv64.h" -cp "${SCRIPT_DIR}/../../../harness/tsffs-gcc-riscv64.h" "${SCRIPT_DIR}/test-kernel-modules/package/kernel-modules/test-mod/tsffs-gcc-riscv64.h" +cp "${SCRIPT_DIR}/../../../harness/tsffs.h" "${SCRIPT_DIR}/tsffs.h" +cp "${SCRIPT_DIR}/../../../harness/tsffs.h" "${SCRIPT_DIR}/test-kernel-modules/package/kernel-modules/test-mod/tsffs.h" mkdir -p "${SCRIPT_DIR}/targets/risc-v-simple/images/linux/" echo "Building container" diff --git a/tests/rsrc/riscv-64/test-kernel-modules/package/kernel-modules/test-mod/test-mod.c b/tests/rsrc/riscv-64/test-kernel-modules/package/kernel-modules/test-mod/test-mod.c index 81a3bb8d..2e548c03 100644 --- a/tests/rsrc/riscv-64/test-kernel-modules/package/kernel-modules/test-mod/test-mod.c +++ b/tests/rsrc/riscv-64/test-kernel-modules/package/kernel-modules/test-mod/test-mod.c @@ -16,7 +16,7 @@ #include #include -#include "tsffs-gcc-riscv64.h" +#include "tsffs.h" #define MAJOR_NUM 100 #define IOCTL_SET_MSG _IOW(MAJOR_NUM, 0, char *) diff --git a/tests/rsrc/riscv-64/test-kernel-modules/package/kernel-modules/test-mod/tsffs-gcc-riscv64.h b/tests/rsrc/riscv-64/test-kernel-modules/package/kernel-modules/test-mod/tsffs-gcc-riscv64.h deleted file mode 100644 index d680ba0d..00000000 --- a/tests/rsrc/riscv-64/test-kernel-modules/package/kernel-modules/test-mod/tsffs-gcc-riscv64.h +++ /dev/null @@ -1,124 +0,0 @@ -// Copyright (C) 2023 Intel Corporation -// SPDX-License-Identifier: Apache-2.0 - -/// Definitions and macros for compiled-in harnessing of C and C++ target -/// software for the RISC-V (64-bit) architecture -/// -/// Example: -/// ```c -/// #include "tsffs-gcc-riscv64.h" -/// -/// int main() { -/// char buf[0x10]; -/// size_t size = 0x10; -/// size_t *size_ptr = &size; -/// HARNESS_START((char *)buf, size_ptr); -/// int retval = YourSpecialDecoder(buf, *size_ptr); -/// if (!retval) { -/// HARNESS_ASSERT(); -/// } else { -/// HARNESS_STOP(); -/// } -/// } -/// ``` - -#ifndef TSFFS_H -#define TSFFS_H - -/// Define common with LibFuzzer and other fuzzers to allow code that is -/// fuzzing-specific to be left in the codebase. See -/// https://llvm.org/docs/LibFuzzer.html#id35 for more information -#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION -#define FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION (1) -#endif // FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION - -/// Default magic instruction `n` value to signal the TSFFS fuzzer to start the -/// fuzzing loop -#define MAGIC_START (0x0001U) -/// Default magic instruction `n` value to signal the TSFFS fuzzer to stop and -/// reset the fuzzing loop -#define MAGIC_STOP (0x0002U) -/// Default magic instruction `n` value to signal the TSFFS fuzzer that an error -/// has occurred and the testcase input should be saved as a solution -#define MAGIC_ASSERT (0x0003U) - -/// Alternative magic numbers that can be used for start and stop events in -/// conjunction with setting the magic number for each event via the SIMICS or -/// SIMICS Python script interface -#define MAGIC_ALT_0 (0x0004U) -#define MAGIC_ALT_1 (0x0005U) -#define MAGIC_ALT_2 (0x0006U) -#define MAGIC_ALT_3 (0x0007U) -#define MAGIC_ALT_4 (0x0008U) -#define MAGIC_ALT_5 (0x0009U) -#define MAGIC_ALT_6 (0x000aU) -#define MAGIC_ALT_7 (0x000bU) - -/// Invoke the magic instruction defined by SIMICS for the RISC-V architecture -/// (`srai zero, zero, n`) with a specific value of `n`, after setting register -/// `a0` to the value of the pointer to the testcase and register `a1` to the -/// value of the pointer to the testcase size. These registers are accessed by -/// the fuzzer and are defined per-architecture. -#define __srai_extended(value, testcase_ptr, size_ptr) \ - __asm__ __volatile__("mv a0, %0; mv a1, %1; srai zero, zero, %2" \ - : \ - : "r"(testcase_ptr), "r"(size_ptr), "I"(value) \ - : "a0", "a1"); - -/// Invoke the magic instruction defined by SIMICS for the RISC-V architecture -/// (`srai zero, zero, n`) with a specific value of `n` -#define __srai(value) \ - __asm__ __volatile__("srai zero, zero, %0" : : "I"(value) :) - -/// Signal the fuzzer using a specific magic value `start` to start the fuzzing -/// loop at the point this macro is called. A snapshot will be taken when the -/// macro is called, and the maximum testcase size `*size_ptr` will be saved as -/// `max_testcase_size`. Each iteration of the fuzzing loop, the fuzzer input -/// (the "testcase") will be written to `*testcase_ptr` as if running -/// `memcpy(testcase_ptr, current_testcase, max_testcase_size)`, and the actual -/// size of the current testcase will be written to -/// `*size_ptr` as if running `*size_ptr = current_testcase_size`. -#define __arch_harness_start(start, testcase_ptr, size_ptr) \ - do { \ - __srai_extended(start, testcase_ptr, size_ptr); \ - } while (0) - -/// Signal the fuzzer using the a specific magic value (`stop`) to stop and -/// reset to the beginning of the fuzzing loop with a "normal" stop status, -/// indicating no solution has occurred. -#define __arch_harness_stop(stop) \ - do { \ - __srai(stop); \ - } while (0) - -/// Signal the fuzzer using the default magic value to start the fuzzing loop at -/// the point this macro is called. A snapshot will be taken when the macro is -/// called, and the maximum testcase size `*size_ptr` will be saved as -/// `max_testcase_size`. Each iteration of the fuzzing loop, the fuzzer input -/// (the "testcase") will be written to -/// `*testcase_ptr` as if running `memcpy(testcase_ptr, current_testcase, -/// max_testcase_size)`, and the actual size of the current testcase will be -/// written to -/// `*size_ptr` as if running `*size_ptr = current_testcase_size`. -#define HARNESS_START(testcase_ptr, size_ptr) \ - do { \ - __arch_harness_start(MAGIC_START, testcase_ptr, size_ptr); \ - } while (0) - -/// Signal the fuzzer using the default magic value to stop and reset to the -/// beginning of the fuzzing loop with a "normal" stop status, indicating no -/// solution has occurred. -#define HARNESS_STOP() \ - do { \ - __arch_harness_stop(MAGIC_STOP); \ - } while (0) - -/// Signal the fuzzer using the default magic value to stop and reset to the -/// beginning of the fuzzing loop with a "solution" stop status, indicating some -/// custom error has occurred. -#define HARNESS_ASSERT() \ - do { \ - __arch_harness_stop(MAGIC_ASSERT); \ - } while (0) - -#endif // TSFFS_H \ No newline at end of file diff --git a/tests/rsrc/riscv-64/test-mod-userspace.c b/tests/rsrc/riscv-64/test-mod-userspace.c index c7db73a1..699b9144 100644 --- a/tests/rsrc/riscv-64/test-mod-userspace.c +++ b/tests/rsrc/riscv-64/test-mod-userspace.c @@ -8,7 +8,7 @@ #include #include -#include "tsffs-gcc-riscv64.h" +#include "tsffs.h" #define MAJOR_NUM 100 #define IOCTL_SET_MSG _IOW(MAJOR_NUM, 0, char *) @@ -76,11 +76,11 @@ int main(void) { size_t msg_size = 80; size_t *msg_size_ptr = &msg_size; - __arch_harness_start(MAGIC_ALT_0, msg, msg_size_ptr); + HARNESS_START_INDEX(1, msg, msg_size_ptr); ret_val = ioctl_set_msg(file_desc, msg); - __arch_harness_stop(MAGIC_ALT_1); + HARNESS_STOP_INDEX(1); if (ret_val) goto error; @@ -89,4 +89,4 @@ int main(void) { error: close(file_desc); exit(EXIT_FAILURE); -} \ No newline at end of file +} diff --git a/tests/rsrc/riscv-64/test.c b/tests/rsrc/riscv-64/test.c index 7bb2926c..648f824f 100644 --- a/tests/rsrc/riscv-64/test.c +++ b/tests/rsrc/riscv-64/test.c @@ -5,7 +5,7 @@ #include #include -#include "tsffs-gcc-riscv64.h" +#include "tsffs.h" const char *password = "fuzzing!"; diff --git a/tests/rsrc/riscv-64/tsffs-gcc-riscv64.h b/tests/rsrc/riscv-64/tsffs-gcc-riscv64.h deleted file mode 100644 index d680ba0d..00000000 --- a/tests/rsrc/riscv-64/tsffs-gcc-riscv64.h +++ /dev/null @@ -1,124 +0,0 @@ -// Copyright (C) 2023 Intel Corporation -// SPDX-License-Identifier: Apache-2.0 - -/// Definitions and macros for compiled-in harnessing of C and C++ target -/// software for the RISC-V (64-bit) architecture -/// -/// Example: -/// ```c -/// #include "tsffs-gcc-riscv64.h" -/// -/// int main() { -/// char buf[0x10]; -/// size_t size = 0x10; -/// size_t *size_ptr = &size; -/// HARNESS_START((char *)buf, size_ptr); -/// int retval = YourSpecialDecoder(buf, *size_ptr); -/// if (!retval) { -/// HARNESS_ASSERT(); -/// } else { -/// HARNESS_STOP(); -/// } -/// } -/// ``` - -#ifndef TSFFS_H -#define TSFFS_H - -/// Define common with LibFuzzer and other fuzzers to allow code that is -/// fuzzing-specific to be left in the codebase. See -/// https://llvm.org/docs/LibFuzzer.html#id35 for more information -#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION -#define FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION (1) -#endif // FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION - -/// Default magic instruction `n` value to signal the TSFFS fuzzer to start the -/// fuzzing loop -#define MAGIC_START (0x0001U) -/// Default magic instruction `n` value to signal the TSFFS fuzzer to stop and -/// reset the fuzzing loop -#define MAGIC_STOP (0x0002U) -/// Default magic instruction `n` value to signal the TSFFS fuzzer that an error -/// has occurred and the testcase input should be saved as a solution -#define MAGIC_ASSERT (0x0003U) - -/// Alternative magic numbers that can be used for start and stop events in -/// conjunction with setting the magic number for each event via the SIMICS or -/// SIMICS Python script interface -#define MAGIC_ALT_0 (0x0004U) -#define MAGIC_ALT_1 (0x0005U) -#define MAGIC_ALT_2 (0x0006U) -#define MAGIC_ALT_3 (0x0007U) -#define MAGIC_ALT_4 (0x0008U) -#define MAGIC_ALT_5 (0x0009U) -#define MAGIC_ALT_6 (0x000aU) -#define MAGIC_ALT_7 (0x000bU) - -/// Invoke the magic instruction defined by SIMICS for the RISC-V architecture -/// (`srai zero, zero, n`) with a specific value of `n`, after setting register -/// `a0` to the value of the pointer to the testcase and register `a1` to the -/// value of the pointer to the testcase size. These registers are accessed by -/// the fuzzer and are defined per-architecture. -#define __srai_extended(value, testcase_ptr, size_ptr) \ - __asm__ __volatile__("mv a0, %0; mv a1, %1; srai zero, zero, %2" \ - : \ - : "r"(testcase_ptr), "r"(size_ptr), "I"(value) \ - : "a0", "a1"); - -/// Invoke the magic instruction defined by SIMICS for the RISC-V architecture -/// (`srai zero, zero, n`) with a specific value of `n` -#define __srai(value) \ - __asm__ __volatile__("srai zero, zero, %0" : : "I"(value) :) - -/// Signal the fuzzer using a specific magic value `start` to start the fuzzing -/// loop at the point this macro is called. A snapshot will be taken when the -/// macro is called, and the maximum testcase size `*size_ptr` will be saved as -/// `max_testcase_size`. Each iteration of the fuzzing loop, the fuzzer input -/// (the "testcase") will be written to `*testcase_ptr` as if running -/// `memcpy(testcase_ptr, current_testcase, max_testcase_size)`, and the actual -/// size of the current testcase will be written to -/// `*size_ptr` as if running `*size_ptr = current_testcase_size`. -#define __arch_harness_start(start, testcase_ptr, size_ptr) \ - do { \ - __srai_extended(start, testcase_ptr, size_ptr); \ - } while (0) - -/// Signal the fuzzer using the a specific magic value (`stop`) to stop and -/// reset to the beginning of the fuzzing loop with a "normal" stop status, -/// indicating no solution has occurred. -#define __arch_harness_stop(stop) \ - do { \ - __srai(stop); \ - } while (0) - -/// Signal the fuzzer using the default magic value to start the fuzzing loop at -/// the point this macro is called. A snapshot will be taken when the macro is -/// called, and the maximum testcase size `*size_ptr` will be saved as -/// `max_testcase_size`. Each iteration of the fuzzing loop, the fuzzer input -/// (the "testcase") will be written to -/// `*testcase_ptr` as if running `memcpy(testcase_ptr, current_testcase, -/// max_testcase_size)`, and the actual size of the current testcase will be -/// written to -/// `*size_ptr` as if running `*size_ptr = current_testcase_size`. -#define HARNESS_START(testcase_ptr, size_ptr) \ - do { \ - __arch_harness_start(MAGIC_START, testcase_ptr, size_ptr); \ - } while (0) - -/// Signal the fuzzer using the default magic value to stop and reset to the -/// beginning of the fuzzing loop with a "normal" stop status, indicating no -/// solution has occurred. -#define HARNESS_STOP() \ - do { \ - __arch_harness_stop(MAGIC_STOP); \ - } while (0) - -/// Signal the fuzzer using the default magic value to stop and reset to the -/// beginning of the fuzzing loop with a "solution" stop status, indicating some -/// custom error has occurred. -#define HARNESS_ASSERT() \ - do { \ - __arch_harness_stop(MAGIC_ASSERT); \ - } while (0) - -#endif // TSFFS_H \ No newline at end of file diff --git a/tests/rsrc/x86-user/build.sh b/tests/rsrc/x86-user/build.sh index 58532e4d..a035fbc4 100755 --- a/tests/rsrc/x86-user/build.sh +++ b/tests/rsrc/x86-user/build.sh @@ -14,7 +14,7 @@ if [ -n "${SIMICS_BASE}" ]; then fi -cp "${SCRIPT_DIR}/../../../harness/tsffs-gcc-x86.h" "${SCRIPT_DIR}/tsffs-gcc-x86.h" +cp "${SCRIPT_DIR}/../../../harness/tsffs.h" "${SCRIPT_DIR}/tsffs.h" cp "${SCRIPT_DIR}/../../rsrc/minimal_boot_disk.craff" "${SCRIPT_DIR}/minimal_boot_disk.craff" ninja diff --git a/tests/rsrc/x86-user/minimal_boot_disk.craff b/tests/rsrc/x86-user/minimal_boot_disk.craff deleted file mode 100644 index 2488b728..00000000 --- a/tests/rsrc/x86-user/minimal_boot_disk.craff +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:8ffab484ad42b567214be5a6a7245e2385a30964dd5438035a963ebc19126e36 -size 11505650 diff --git a/tests/rsrc/x86-user/test.c b/tests/rsrc/x86-user/test.c index 6a0e37a9..32f8025d 100644 --- a/tests/rsrc/x86-user/test.c +++ b/tests/rsrc/x86-user/test.c @@ -6,7 +6,7 @@ #include #include -#include "tsffs-gcc-x86.h" +#include "tsffs.h" const char hex[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; diff --git a/tests/rsrc/x86-user/tsffs-gcc-riscv64.h b/tests/rsrc/x86-user/tsffs-gcc-riscv64.h deleted file mode 100644 index 24243dcc..00000000 --- a/tests/rsrc/x86-user/tsffs-gcc-riscv64.h +++ /dev/null @@ -1,42 +0,0 @@ -#ifndef TSFFS_H -#define TSFFS_H - -/// TSFFS Magic Include - -// GCC EASM notes: -// - The stack pointer is required to be the same on exit to an asm block as it -// was on entry - -#define MAGIC_START (0x0001U) -#define MAGIC_STOP (0x0002U) - -#define __srai_extended(value, testcase_ptr, size_ptr) \ - __asm__ __volatile__("mv a0, %0; mv a1, %1; srai zero, zero, %2" \ - : \ - : "r"(testcase_ptr), "r"(size_ptr), "I"(value) \ - : "a0", "a1"); - -#define __srai(value) \ - __asm__ __volatile__("srai zero, zero, %0" : : "I"(value) :) - -#define __arch_harness_start(start, testcase_ptr, size_ptr) \ - do { \ - __srai_extended(start, testcase_ptr, size_ptr); \ - } while (0) - -#define __arch_harness_stop(stop) \ - do { \ - __srai(stop); \ - } while (0) - -#define HARNESS_START(testcase_ptr, size_ptr) \ - do { \ - __arch_harness_start(MAGIC_START, testcase_ptr, size_ptr); \ - } while (0) - -#define HARNESS_STOP() \ - do { \ - __arch_harness_stop(MAGIC_STOP); \ - } while (0) - -#endif // TSFFS_H \ No newline at end of file diff --git a/tests/rsrc/x86-user/tsffs-gcc-x86.h b/tests/rsrc/x86-user/tsffs-gcc-x86.h deleted file mode 100644 index ba6a8053..00000000 --- a/tests/rsrc/x86-user/tsffs-gcc-x86.h +++ /dev/null @@ -1,134 +0,0 @@ -// Copyright (C) 2023 Intel Corporation -// SPDX-License-Identifier: Apache-2.0 - -/// Definitions and macros for compiled-in harnessing of C and C++ target -/// software for the RISC-V (64-bit) architecture -/// -/// Example: -/// ```c -/// #include "tsffs-gcc-x86.h" -/// -/// int main() { -/// char buf[0x10]; -/// size_t size = 0x10; -/// size_t *size_ptr = &size; -/// HARNESS_START((char *)buf, size_ptr); -/// int retval = YourSpecialDecoder(buf, *size_ptr); -/// if (!retval) { -/// HARNESS_ASSERT(); -/// } else { -/// HARNESS_STOP(); -/// } -/// } -/// ``` - -#ifndef TSFFS_H -#define TSFFS_H - -/// Define common with LibFuzzer and other fuzzers to allow code that is -/// fuzzing-specific to be left in the codebase. See -/// https://llvm.org/docs/LibFuzzer.html#id35 for more information -#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION -#define FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION (1) -#endif // FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION - -/// Magic value defined by SIMICS as the "leaf" value of a CPUID instruction -/// that is treated as a magic instruction. -#define MAGIC (0x4711U) -/// Default magic instruction `n` value to signal the TSFFS fuzzer to start the -/// fuzzing loop -#define MAGIC_START (0x0001U) -/// Default magic instruction `n` value to signal the TSFFS fuzzer to stop and -/// reset the fuzzing loop -#define MAGIC_STOP (0x0002U) -/// Default magic instruction `n` value to signal the TSFFS fuzzer that an error -/// has occurred and the testcase input should be saved as a solution -#define MAGIC_ASSERT (0x0003U) - -/// Alternative magic numbers that can be used for start and stop events in -/// conjunction with setting the magic number for each event via the SIMICS or -/// SIMICS Python script interface -#define MAGIC_ALT_0 (0x0004U) -#define MAGIC_ALT_1 (0x0005U) -#define MAGIC_ALT_2 (0x0006U) -#define MAGIC_ALT_3 (0x0007U) -#define MAGIC_ALT_4 (0x0008U) -#define MAGIC_ALT_5 (0x0009U) -#define MAGIC_ALT_6 (0x000aU) -#define MAGIC_ALT_7 (0x000bU) - -/// Invoke the magic instruction defined by SIMICS for the x86 architecture -/// (`cpuid`) with a specific value of `n`, after setting register `edi` to the -/// value of the pointer to the testcase and register `esi` to the value of the -/// pointer to the testcase size. These registers are accessed by the fuzzer and -/// are defined per-architecture. -#define __cpuid_extended2(value, inout_ptr_0, inout_ptr_1) \ - unsigned int _a __attribute__((unused)) = 0; \ - __asm__ __volatile__("cpuid" \ - : "=a"(_a) \ - : "a"(value), "D"(inout_ptr_0), "S"(inout_ptr_1) \ - : "ebx", "ecx", "edx"); - -/// Invoke the magic instruction defined by SIMICS for the x86 architecture -/// (`cpuid`) with a specific value of `n` -#define __cpuid(value) \ - unsigned int _a __attribute__((unused)) = 0; \ - __asm__ __volatile__("cpuid\n\t" \ - : "=a"(_a) \ - : "a"(value) \ - : "ebx", "ecx", "edx") - -/// Signal the fuzzer using a specific magic value `start` to start the fuzzing -/// loop at the point this macro is called. A snapshot will be taken when the -/// macro is called, and the maximum testcase size `*size_ptr` will be saved as -/// `max_testcase_size`. Each iteration of the fuzzing loop, the fuzzer input -/// (the "testcase") will be written to `*testcase_ptr` as if running -/// `memcpy(testcase_ptr, current_testcase, max_testcase_size)`, and the actual -/// size of the current testcase will be written to -/// `*size_ptr` as if running `*size_ptr = current_testcase_size`. -#define __arch_harness_start(start, testcase_ptr, size_ptr) \ - do { \ - unsigned int magic = (start << 0x10U) | MAGIC; \ - __cpuid_extended2(magic, testcase_ptr, size_ptr); \ - } while (0) - -/// Signal the fuzzer using the a specific magic value (`stop`) to stop and -/// reset to the beginning of the fuzzing loop with a "normal" stop status, -/// indicating no solution has occurred. -#define __arch_harness_stop(stop) \ - do { \ - unsigned int magic = (stop << 0x10U) | MAGIC; \ - __cpuid(magic); \ - } while (0) - -/// Signal the fuzzer using the default magic value to start the fuzzing loop at -/// the point this macro is called. A snapshot will be taken when the macro is -/// called, and the maximum testcase size `*size_ptr` will be saved as -/// `max_testcase_size`. Each iteration of the fuzzing loop, the fuzzer input -/// (the "testcase") will be written to -/// `*testcase_ptr` as if running `memcpy(testcase_ptr, current_testcase, -/// max_testcase_size)`, and the actual size of the current testcase will be -/// written to -/// `*size_ptr` as if running `*size_ptr = current_testcase_size`. -#define HARNESS_START(testcase_ptr, size_ptr) \ - do { \ - __arch_harness_start(MAGIC_START, testcase_ptr, size_ptr); \ - } while (0) - -/// Signal the fuzzer using the default magic value to stop and reset to the -/// beginning of the fuzzing loop with a "normal" stop status, indicating no -/// solution has occurred. -#define HARNESS_STOP() \ - do { \ - __arch_harness_stop(MAGIC_STOP); \ - } while (0) - -/// Signal the fuzzer using the default magic value to stop and reset to the -/// beginning of the fuzzing loop with a "solution" stop status, indicating some -/// custom error has occurred. -#define HARNESS_ASSERT() \ - do { \ - __arch_harness_stop(MAGIC_ASSERT); \ - } while (0) - -#endif // TSFFS_H \ No newline at end of file diff --git a/tests/rsrc/x86_64-breakpoint-uefi-edk2/.gitignore b/tests/rsrc/x86_64-breakpoint-uefi-edk2/.gitignore index 9564f30d..23b63b94 100644 --- a/tests/rsrc/x86_64-breakpoint-uefi-edk2/.gitignore +++ b/tests/rsrc/x86_64-breakpoint-uefi-edk2/.gitignore @@ -1,2 +1,3 @@ .ninja_log -test.efi \ No newline at end of file +test.efi +*.craff \ No newline at end of file diff --git a/tests/rsrc/x86_64-breakpoint-uefi-edk2/build.sh b/tests/rsrc/x86_64-breakpoint-uefi-edk2/build.sh index e49d87f9..095ac026 100755 --- a/tests/rsrc/x86_64-breakpoint-uefi-edk2/build.sh +++ b/tests/rsrc/x86_64-breakpoint-uefi-edk2/build.sh @@ -7,6 +7,8 @@ # this only needs to be run if you want to modify the source code for the HelloWorld.efi module, # otherwise, the EFI is included in the source tree for ease of use +set -e + SCRIPT_DIR=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" &>/dev/null && pwd) IMAGE_NAME="edk2-build-tsffs-gcc-x86_64-test-breakpoint" CONTAINER_UID=$(echo "${RANDOM}" | sha256sum | head -c 8) @@ -14,8 +16,8 @@ CONTAINER_NAME="${IMAGE_NAME}-tmp-${CONTAINER_UID}" pushd "${SCRIPT_DIR}" || exit 1 -cp "${SCRIPT_DIR}/../../../harness/tsffs-gcc-x86_64.h" "${SCRIPT_DIR}/src/tsffs-gcc-x86_64.h" -cp "${SCRIPT_DIR}/../rsrc/minimal_boot_disk.craff" "${SCRIPT_DIR}/minimal_boot_disk.craff" +cp "${SCRIPT_DIR}/../../../harness/tsffs.h" "${SCRIPT_DIR}/src/tsffs.h" +cp "${SCRIPT_DIR}/../minimal_boot_disk.craff" "${SCRIPT_DIR}/minimal_boot_disk.craff" docker buildx build -t "${IMAGE_NAME}" -f "Dockerfile" . docker create --name "${CONTAINER_NAME}" "${IMAGE_NAME}" diff --git a/tests/rsrc/x86_64-breakpoint-uefi-edk2/src/HelloWorld.c b/tests/rsrc/x86_64-breakpoint-uefi-edk2/src/HelloWorld.c index 8cb5b92b..d8d0c953 100644 --- a/tests/rsrc/x86_64-breakpoint-uefi-edk2/src/HelloWorld.c +++ b/tests/rsrc/x86_64-breakpoint-uefi-edk2/src/HelloWorld.c @@ -16,7 +16,7 @@ #include #include -#include "tsffs-gcc-x86_64.h" +#include "tsffs.h" /** The user Entry Point for Application. The user code starts with this function diff --git a/tests/rsrc/x86_64-breakpoint-uefi-edk2/src/tsffs-gcc-x86_64.h b/tests/rsrc/x86_64-breakpoint-uefi-edk2/src/tsffs-gcc-x86_64.h deleted file mode 100644 index 706f9946..00000000 --- a/tests/rsrc/x86_64-breakpoint-uefi-edk2/src/tsffs-gcc-x86_64.h +++ /dev/null @@ -1,134 +0,0 @@ -// Copyright (C) 2023 Intel Corporation -// SPDX-License-Identifier: Apache-2.0 - -/// Definitions and macros for compiled-in harnessing of C and C++ target -/// software for the RISC-V (64-bit) architecture -/// -/// Example: -/// ```c -/// #include "tsffs-gcc-x86_64.h" -/// -/// int main() { -/// char buf[0x10]; -/// size_t size = 0x10; -/// size_t *size_ptr = &size; -/// HARNESS_START((char *)buf, size_ptr); -/// int retval = YourSpecialDecoder(buf, *size_ptr); -/// if (!retval) { -/// HARNESS_ASSERT(); -/// } else { -/// HARNESS_STOP(); -/// } -/// } -/// ``` - -#ifndef TSFFS_H -#define TSFFS_H - -/// Define common with LibFuzzer and other fuzzers to allow code that is -/// fuzzing-specific to be left in the codebase. See -/// https://llvm.org/docs/LibFuzzer.html#id35 for more information -#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION -#define FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION (1) -#endif // FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION - -/// Magic value defined by SIMICS as the "leaf" value of a CPUID instruction -/// that is treated as a magic instruction. -#define MAGIC (0x4711U) -/// Default magic instruction `n` value to signal the TSFFS fuzzer to start the -/// fuzzing loop -#define MAGIC_START (0x0001U) -/// Default magic instruction `n` value to signal the TSFFS fuzzer to stop and -/// reset the fuzzing loop -#define MAGIC_STOP (0x0002U) -/// Default magic instruction `n` value to signal the TSFFS fuzzer that an error -/// has occurred and the testcase input should be saved as a solution -#define MAGIC_ASSERT (0x0003U) - -/// Alternative magic numbers that can be used for start and stop events in -/// conjunction with setting the magic number for each event via the SIMICS or -/// SIMICS Python script interface -#define MAGIC_ALT_0 (0x0004U) -#define MAGIC_ALT_1 (0x0005U) -#define MAGIC_ALT_2 (0x0006U) -#define MAGIC_ALT_3 (0x0007U) -#define MAGIC_ALT_4 (0x0008U) -#define MAGIC_ALT_5 (0x0009U) -#define MAGIC_ALT_6 (0x000aU) -#define MAGIC_ALT_7 (0x000bU) - -/// Invoke the magic instruction defined by SIMICS for the x86-64 architecture -/// (`cpuid`) with a specific value of `n`, after setting register `rdi` to the -/// value of the pointer to the testcase and register `rsi` to the value of the -/// pointer to the testcase size. These registers are accessed by the fuzzer and -/// are defined per-architecture. -#define __cpuid_extended2(value, inout_ptr_0, inout_ptr_1) \ - unsigned int _a __attribute__((unused)) = 0; \ - __asm__ __volatile__("cpuid" \ - : "=a"(_a) \ - : "a"(value), "D"(inout_ptr_0), "S"(inout_ptr_1) \ - : "rbx", "rcx", "rdx"); - -/// Invoke the magic instruction defined by SIMICS for the x86-64 architecture -/// (`cpuid`) with a specific value of `n` -#define __cpuid(value) \ - unsigned int _a __attribute__((unused)) = 0; \ - __asm__ __volatile__("cpuid\n\t" \ - : "=a"(_a) \ - : "a"(value) \ - : "rbx", "rcx", "rdx") - -/// Signal the fuzzer using a specific magic value `start` to start the fuzzing -/// loop at the point this macro is called. A snapshot will be taken when the -/// macro is called, and the maximum testcase size `*size_ptr` will be saved as -/// `max_testcase_size`. Each iteration of the fuzzing loop, the fuzzer input -/// (the "testcase") will be written to `*testcase_ptr` as if running -/// `memcpy(testcase_ptr, current_testcase, max_testcase_size)`, and the actual -/// size of the current testcase will be written to -/// `*size_ptr` as if running `*size_ptr = current_testcase_size`. -#define __arch_harness_start(start, testcase_ptr, size_ptr) \ - do { \ - unsigned int magic = (start << 0x10U) | MAGIC; \ - __cpuid_extended2(magic, testcase_ptr, size_ptr); \ - } while (0) - -/// Signal the fuzzer using the a specific magic value (`stop`) to stop and -/// reset to the beginning of the fuzzing loop with a "normal" stop status, -/// indicating no solution has occurred. -#define __arch_harness_stop(stop) \ - do { \ - unsigned int magic = (stop << 0x10U) | MAGIC; \ - __cpuid(magic); \ - } while (0) - -/// Signal the fuzzer using the default magic value to start the fuzzing loop at -/// the point this macro is called. A snapshot will be taken when the macro is -/// called, and the maximum testcase size `*size_ptr` will be saved as -/// `max_testcase_size`. Each iteration of the fuzzing loop, the fuzzer input -/// (the "testcase") will be written to -/// `*testcase_ptr` as if running `memcpy(testcase_ptr, current_testcase, -/// max_testcase_size)`, and the actual size of the current testcase will be -/// written to -/// `*size_ptr` as if running `*size_ptr = current_testcase_size`. -#define HARNESS_START(testcase_ptr, size_ptr) \ - do { \ - __arch_harness_start(MAGIC_START, testcase_ptr, size_ptr); \ - } while (0) - -/// Signal the fuzzer using the default magic value to stop and reset to the -/// beginning of the fuzzing loop with a "normal" stop status, indicating no -/// solution has occurred. -#define HARNESS_STOP() \ - do { \ - __arch_harness_stop(MAGIC_STOP); \ - } while (0) - -/// Signal the fuzzer using the default magic value to stop and reset to the -/// beginning of the fuzzing loop with a "solution" stop status, indicating some -/// custom error has occurred. -#define HARNESS_ASSERT() \ - do { \ - __arch_harness_stop(MAGIC_ASSERT); \ - } while (0) - -#endif // TSFFS_H \ No newline at end of file diff --git a/tests/rsrc/x86_64-crash-uefi/build.sh b/tests/rsrc/x86_64-crash-uefi/build.sh index bf1f8940..2eb84040 100755 --- a/tests/rsrc/x86_64-crash-uefi/build.sh +++ b/tests/rsrc/x86_64-crash-uefi/build.sh @@ -3,9 +3,11 @@ # Copyright (C) 2024 Intel Corporation # SPDX-License-Identifier: Apache-2.0 +set -e + SCRIPT_DIR=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" &>/dev/null && pwd) -cp "${SCRIPT_DIR}/../../../harness/tsffs-gcc-x86_64.h" "${SCRIPT_DIR}/tsffs-gcc-x86_64.h" +cp "${SCRIPT_DIR}/../../../harness/tsffs.h" "${SCRIPT_DIR}/tsffs.h" cp "${SCRIPT_DIR}/../../rsrc/minimal_boot_disk.craff" "${SCRIPT_DIR}/minimal_boot_disk.craff" ninja \ No newline at end of file diff --git a/tests/rsrc/x86_64-crash-uefi/minimal_boot_disk.craff b/tests/rsrc/x86_64-crash-uefi/minimal_boot_disk.craff deleted file mode 100644 index 2488b728..00000000 --- a/tests/rsrc/x86_64-crash-uefi/minimal_boot_disk.craff +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:8ffab484ad42b567214be5a6a7245e2385a30964dd5438035a963ebc19126e36 -size 11505650 diff --git a/tests/rsrc/x86_64-crash-uefi/test.c b/tests/rsrc/x86_64-crash-uefi/test.c index d33221a8..9c623455 100644 --- a/tests/rsrc/x86_64-crash-uefi/test.c +++ b/tests/rsrc/x86_64-crash-uefi/test.c @@ -4,7 +4,7 @@ #include #include -#include "tsffs-gcc-x86_64.h" +#include "tsffs.h" typedef struct EfiTableHeader { uint64_t signature; diff --git a/tests/rsrc/x86_64-crash-uefi/tsffs-gcc-x86_64.h b/tests/rsrc/x86_64-crash-uefi/tsffs-gcc-x86_64.h deleted file mode 100644 index 706f9946..00000000 --- a/tests/rsrc/x86_64-crash-uefi/tsffs-gcc-x86_64.h +++ /dev/null @@ -1,134 +0,0 @@ -// Copyright (C) 2023 Intel Corporation -// SPDX-License-Identifier: Apache-2.0 - -/// Definitions and macros for compiled-in harnessing of C and C++ target -/// software for the RISC-V (64-bit) architecture -/// -/// Example: -/// ```c -/// #include "tsffs-gcc-x86_64.h" -/// -/// int main() { -/// char buf[0x10]; -/// size_t size = 0x10; -/// size_t *size_ptr = &size; -/// HARNESS_START((char *)buf, size_ptr); -/// int retval = YourSpecialDecoder(buf, *size_ptr); -/// if (!retval) { -/// HARNESS_ASSERT(); -/// } else { -/// HARNESS_STOP(); -/// } -/// } -/// ``` - -#ifndef TSFFS_H -#define TSFFS_H - -/// Define common with LibFuzzer and other fuzzers to allow code that is -/// fuzzing-specific to be left in the codebase. See -/// https://llvm.org/docs/LibFuzzer.html#id35 for more information -#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION -#define FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION (1) -#endif // FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION - -/// Magic value defined by SIMICS as the "leaf" value of a CPUID instruction -/// that is treated as a magic instruction. -#define MAGIC (0x4711U) -/// Default magic instruction `n` value to signal the TSFFS fuzzer to start the -/// fuzzing loop -#define MAGIC_START (0x0001U) -/// Default magic instruction `n` value to signal the TSFFS fuzzer to stop and -/// reset the fuzzing loop -#define MAGIC_STOP (0x0002U) -/// Default magic instruction `n` value to signal the TSFFS fuzzer that an error -/// has occurred and the testcase input should be saved as a solution -#define MAGIC_ASSERT (0x0003U) - -/// Alternative magic numbers that can be used for start and stop events in -/// conjunction with setting the magic number for each event via the SIMICS or -/// SIMICS Python script interface -#define MAGIC_ALT_0 (0x0004U) -#define MAGIC_ALT_1 (0x0005U) -#define MAGIC_ALT_2 (0x0006U) -#define MAGIC_ALT_3 (0x0007U) -#define MAGIC_ALT_4 (0x0008U) -#define MAGIC_ALT_5 (0x0009U) -#define MAGIC_ALT_6 (0x000aU) -#define MAGIC_ALT_7 (0x000bU) - -/// Invoke the magic instruction defined by SIMICS for the x86-64 architecture -/// (`cpuid`) with a specific value of `n`, after setting register `rdi` to the -/// value of the pointer to the testcase and register `rsi` to the value of the -/// pointer to the testcase size. These registers are accessed by the fuzzer and -/// are defined per-architecture. -#define __cpuid_extended2(value, inout_ptr_0, inout_ptr_1) \ - unsigned int _a __attribute__((unused)) = 0; \ - __asm__ __volatile__("cpuid" \ - : "=a"(_a) \ - : "a"(value), "D"(inout_ptr_0), "S"(inout_ptr_1) \ - : "rbx", "rcx", "rdx"); - -/// Invoke the magic instruction defined by SIMICS for the x86-64 architecture -/// (`cpuid`) with a specific value of `n` -#define __cpuid(value) \ - unsigned int _a __attribute__((unused)) = 0; \ - __asm__ __volatile__("cpuid\n\t" \ - : "=a"(_a) \ - : "a"(value) \ - : "rbx", "rcx", "rdx") - -/// Signal the fuzzer using a specific magic value `start` to start the fuzzing -/// loop at the point this macro is called. A snapshot will be taken when the -/// macro is called, and the maximum testcase size `*size_ptr` will be saved as -/// `max_testcase_size`. Each iteration of the fuzzing loop, the fuzzer input -/// (the "testcase") will be written to `*testcase_ptr` as if running -/// `memcpy(testcase_ptr, current_testcase, max_testcase_size)`, and the actual -/// size of the current testcase will be written to -/// `*size_ptr` as if running `*size_ptr = current_testcase_size`. -#define __arch_harness_start(start, testcase_ptr, size_ptr) \ - do { \ - unsigned int magic = (start << 0x10U) | MAGIC; \ - __cpuid_extended2(magic, testcase_ptr, size_ptr); \ - } while (0) - -/// Signal the fuzzer using the a specific magic value (`stop`) to stop and -/// reset to the beginning of the fuzzing loop with a "normal" stop status, -/// indicating no solution has occurred. -#define __arch_harness_stop(stop) \ - do { \ - unsigned int magic = (stop << 0x10U) | MAGIC; \ - __cpuid(magic); \ - } while (0) - -/// Signal the fuzzer using the default magic value to start the fuzzing loop at -/// the point this macro is called. A snapshot will be taken when the macro is -/// called, and the maximum testcase size `*size_ptr` will be saved as -/// `max_testcase_size`. Each iteration of the fuzzing loop, the fuzzer input -/// (the "testcase") will be written to -/// `*testcase_ptr` as if running `memcpy(testcase_ptr, current_testcase, -/// max_testcase_size)`, and the actual size of the current testcase will be -/// written to -/// `*size_ptr` as if running `*size_ptr = current_testcase_size`. -#define HARNESS_START(testcase_ptr, size_ptr) \ - do { \ - __arch_harness_start(MAGIC_START, testcase_ptr, size_ptr); \ - } while (0) - -/// Signal the fuzzer using the default magic value to stop and reset to the -/// beginning of the fuzzing loop with a "normal" stop status, indicating no -/// solution has occurred. -#define HARNESS_STOP() \ - do { \ - __arch_harness_stop(MAGIC_STOP); \ - } while (0) - -/// Signal the fuzzer using the default magic value to stop and reset to the -/// beginning of the fuzzing loop with a "solution" stop status, indicating some -/// custom error has occurred. -#define HARNESS_ASSERT() \ - do { \ - __arch_harness_stop(MAGIC_ASSERT); \ - } while (0) - -#endif // TSFFS_H \ No newline at end of file diff --git a/tests/rsrc/x86_64-timeout-uefi-edk2/build.sh b/tests/rsrc/x86_64-timeout-uefi-edk2/build.sh index 64a2aed1..d99f0d10 100755 --- a/tests/rsrc/x86_64-timeout-uefi-edk2/build.sh +++ b/tests/rsrc/x86_64-timeout-uefi-edk2/build.sh @@ -7,6 +7,8 @@ # this only needs to be run if you want to modify the source code for the HelloWorld.efi module, # otherwise, the EFI is included in the source tree for ease of use +set -e + SCRIPT_DIR=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" &>/dev/null && pwd) IMAGE_NAME="edk2-build-tsffs-gcc-x86_64-test" CONTAINER_UID=$(echo "${RANDOM}" | sha256sum | head -c 8) @@ -14,7 +16,7 @@ CONTAINER_NAME="${IMAGE_NAME}-tmp-${CONTAINER_UID}" pushd "${SCRIPT_DIR}" || exit 1 -cp "${SCRIPT_DIR}/../../../harness/tsffs-gcc-x86_64.h" "${SCRIPT_DIR}/src/tsffs-gcc-x86_64.h" +cp "${SCRIPT_DIR}/../../../harness/tsffs.h" "${SCRIPT_DIR}/src/tsffs.h" cp "${SCRIPT_DIR}/../../rsrc/minimal_boot_disk.craff" "${SCRIPT_DIR}/minimal_boot_disk.craff" docker buildx build -t "${IMAGE_NAME}" -f "Dockerfile" . diff --git a/tests/rsrc/x86_64-timeout-uefi-edk2/minimal_boot_disk.craff b/tests/rsrc/x86_64-timeout-uefi-edk2/minimal_boot_disk.craff deleted file mode 100644 index 2488b728..00000000 --- a/tests/rsrc/x86_64-timeout-uefi-edk2/minimal_boot_disk.craff +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:8ffab484ad42b567214be5a6a7245e2385a30964dd5438035a963ebc19126e36 -size 11505650 diff --git a/tests/rsrc/x86_64-timeout-uefi-edk2/src/HelloWorld.c b/tests/rsrc/x86_64-timeout-uefi-edk2/src/HelloWorld.c index fe409b25..fafb65a6 100644 --- a/tests/rsrc/x86_64-timeout-uefi-edk2/src/HelloWorld.c +++ b/tests/rsrc/x86_64-timeout-uefi-edk2/src/HelloWorld.c @@ -16,7 +16,7 @@ #include #include -#include "tsffs-gcc-x86_64.h" +#include "tsffs.h" /** The user Entry Point for Application. The user code starts with this function diff --git a/tests/rsrc/x86_64-timeout-uefi-edk2/src/tsffs-gcc-x86_64.h b/tests/rsrc/x86_64-timeout-uefi-edk2/src/tsffs-gcc-x86_64.h deleted file mode 100644 index 706f9946..00000000 --- a/tests/rsrc/x86_64-timeout-uefi-edk2/src/tsffs-gcc-x86_64.h +++ /dev/null @@ -1,134 +0,0 @@ -// Copyright (C) 2023 Intel Corporation -// SPDX-License-Identifier: Apache-2.0 - -/// Definitions and macros for compiled-in harnessing of C and C++ target -/// software for the RISC-V (64-bit) architecture -/// -/// Example: -/// ```c -/// #include "tsffs-gcc-x86_64.h" -/// -/// int main() { -/// char buf[0x10]; -/// size_t size = 0x10; -/// size_t *size_ptr = &size; -/// HARNESS_START((char *)buf, size_ptr); -/// int retval = YourSpecialDecoder(buf, *size_ptr); -/// if (!retval) { -/// HARNESS_ASSERT(); -/// } else { -/// HARNESS_STOP(); -/// } -/// } -/// ``` - -#ifndef TSFFS_H -#define TSFFS_H - -/// Define common with LibFuzzer and other fuzzers to allow code that is -/// fuzzing-specific to be left in the codebase. See -/// https://llvm.org/docs/LibFuzzer.html#id35 for more information -#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION -#define FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION (1) -#endif // FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION - -/// Magic value defined by SIMICS as the "leaf" value of a CPUID instruction -/// that is treated as a magic instruction. -#define MAGIC (0x4711U) -/// Default magic instruction `n` value to signal the TSFFS fuzzer to start the -/// fuzzing loop -#define MAGIC_START (0x0001U) -/// Default magic instruction `n` value to signal the TSFFS fuzzer to stop and -/// reset the fuzzing loop -#define MAGIC_STOP (0x0002U) -/// Default magic instruction `n` value to signal the TSFFS fuzzer that an error -/// has occurred and the testcase input should be saved as a solution -#define MAGIC_ASSERT (0x0003U) - -/// Alternative magic numbers that can be used for start and stop events in -/// conjunction with setting the magic number for each event via the SIMICS or -/// SIMICS Python script interface -#define MAGIC_ALT_0 (0x0004U) -#define MAGIC_ALT_1 (0x0005U) -#define MAGIC_ALT_2 (0x0006U) -#define MAGIC_ALT_3 (0x0007U) -#define MAGIC_ALT_4 (0x0008U) -#define MAGIC_ALT_5 (0x0009U) -#define MAGIC_ALT_6 (0x000aU) -#define MAGIC_ALT_7 (0x000bU) - -/// Invoke the magic instruction defined by SIMICS for the x86-64 architecture -/// (`cpuid`) with a specific value of `n`, after setting register `rdi` to the -/// value of the pointer to the testcase and register `rsi` to the value of the -/// pointer to the testcase size. These registers are accessed by the fuzzer and -/// are defined per-architecture. -#define __cpuid_extended2(value, inout_ptr_0, inout_ptr_1) \ - unsigned int _a __attribute__((unused)) = 0; \ - __asm__ __volatile__("cpuid" \ - : "=a"(_a) \ - : "a"(value), "D"(inout_ptr_0), "S"(inout_ptr_1) \ - : "rbx", "rcx", "rdx"); - -/// Invoke the magic instruction defined by SIMICS for the x86-64 architecture -/// (`cpuid`) with a specific value of `n` -#define __cpuid(value) \ - unsigned int _a __attribute__((unused)) = 0; \ - __asm__ __volatile__("cpuid\n\t" \ - : "=a"(_a) \ - : "a"(value) \ - : "rbx", "rcx", "rdx") - -/// Signal the fuzzer using a specific magic value `start` to start the fuzzing -/// loop at the point this macro is called. A snapshot will be taken when the -/// macro is called, and the maximum testcase size `*size_ptr` will be saved as -/// `max_testcase_size`. Each iteration of the fuzzing loop, the fuzzer input -/// (the "testcase") will be written to `*testcase_ptr` as if running -/// `memcpy(testcase_ptr, current_testcase, max_testcase_size)`, and the actual -/// size of the current testcase will be written to -/// `*size_ptr` as if running `*size_ptr = current_testcase_size`. -#define __arch_harness_start(start, testcase_ptr, size_ptr) \ - do { \ - unsigned int magic = (start << 0x10U) | MAGIC; \ - __cpuid_extended2(magic, testcase_ptr, size_ptr); \ - } while (0) - -/// Signal the fuzzer using the a specific magic value (`stop`) to stop and -/// reset to the beginning of the fuzzing loop with a "normal" stop status, -/// indicating no solution has occurred. -#define __arch_harness_stop(stop) \ - do { \ - unsigned int magic = (stop << 0x10U) | MAGIC; \ - __cpuid(magic); \ - } while (0) - -/// Signal the fuzzer using the default magic value to start the fuzzing loop at -/// the point this macro is called. A snapshot will be taken when the macro is -/// called, and the maximum testcase size `*size_ptr` will be saved as -/// `max_testcase_size`. Each iteration of the fuzzing loop, the fuzzer input -/// (the "testcase") will be written to -/// `*testcase_ptr` as if running `memcpy(testcase_ptr, current_testcase, -/// max_testcase_size)`, and the actual size of the current testcase will be -/// written to -/// `*size_ptr` as if running `*size_ptr = current_testcase_size`. -#define HARNESS_START(testcase_ptr, size_ptr) \ - do { \ - __arch_harness_start(MAGIC_START, testcase_ptr, size_ptr); \ - } while (0) - -/// Signal the fuzzer using the default magic value to stop and reset to the -/// beginning of the fuzzing loop with a "normal" stop status, indicating no -/// solution has occurred. -#define HARNESS_STOP() \ - do { \ - __arch_harness_stop(MAGIC_STOP); \ - } while (0) - -/// Signal the fuzzer using the default magic value to stop and reset to the -/// beginning of the fuzzing loop with a "solution" stop status, indicating some -/// custom error has occurred. -#define HARNESS_ASSERT() \ - do { \ - __arch_harness_stop(MAGIC_ASSERT); \ - } while (0) - -#endif // TSFFS_H \ No newline at end of file diff --git a/tests/rsrc/x86_64-uefi-edk2/build.sh b/tests/rsrc/x86_64-uefi-edk2/build.sh index 64a2aed1..30a629d9 100755 --- a/tests/rsrc/x86_64-uefi-edk2/build.sh +++ b/tests/rsrc/x86_64-uefi-edk2/build.sh @@ -14,7 +14,7 @@ CONTAINER_NAME="${IMAGE_NAME}-tmp-${CONTAINER_UID}" pushd "${SCRIPT_DIR}" || exit 1 -cp "${SCRIPT_DIR}/../../../harness/tsffs-gcc-x86_64.h" "${SCRIPT_DIR}/src/tsffs-gcc-x86_64.h" +cp "${SCRIPT_DIR}/../../../harness/tsffs.h" "${SCRIPT_DIR}/src/tsffs.h" cp "${SCRIPT_DIR}/../../rsrc/minimal_boot_disk.craff" "${SCRIPT_DIR}/minimal_boot_disk.craff" docker buildx build -t "${IMAGE_NAME}" -f "Dockerfile" . diff --git a/tests/rsrc/x86_64-uefi-edk2/minimal_boot_disk.craff b/tests/rsrc/x86_64-uefi-edk2/minimal_boot_disk.craff deleted file mode 100644 index 2488b728..00000000 --- a/tests/rsrc/x86_64-uefi-edk2/minimal_boot_disk.craff +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:8ffab484ad42b567214be5a6a7245e2385a30964dd5438035a963ebc19126e36 -size 11505650 diff --git a/tests/rsrc/x86_64-uefi-edk2/src/HelloWorld.c b/tests/rsrc/x86_64-uefi-edk2/src/HelloWorld.c index a6cdda78..c5ebfc91 100644 --- a/tests/rsrc/x86_64-uefi-edk2/src/HelloWorld.c +++ b/tests/rsrc/x86_64-uefi-edk2/src/HelloWorld.c @@ -16,7 +16,7 @@ #include #include -#include "tsffs-gcc-x86_64.h" +#include "tsffs.h" /** The user Entry Point for Application. The user code starts with this function diff --git a/tests/rsrc/x86_64-uefi-edk2/src/tsffs-gcc-x86_64.h b/tests/rsrc/x86_64-uefi-edk2/src/tsffs-gcc-x86_64.h deleted file mode 100644 index 706f9946..00000000 --- a/tests/rsrc/x86_64-uefi-edk2/src/tsffs-gcc-x86_64.h +++ /dev/null @@ -1,134 +0,0 @@ -// Copyright (C) 2023 Intel Corporation -// SPDX-License-Identifier: Apache-2.0 - -/// Definitions and macros for compiled-in harnessing of C and C++ target -/// software for the RISC-V (64-bit) architecture -/// -/// Example: -/// ```c -/// #include "tsffs-gcc-x86_64.h" -/// -/// int main() { -/// char buf[0x10]; -/// size_t size = 0x10; -/// size_t *size_ptr = &size; -/// HARNESS_START((char *)buf, size_ptr); -/// int retval = YourSpecialDecoder(buf, *size_ptr); -/// if (!retval) { -/// HARNESS_ASSERT(); -/// } else { -/// HARNESS_STOP(); -/// } -/// } -/// ``` - -#ifndef TSFFS_H -#define TSFFS_H - -/// Define common with LibFuzzer and other fuzzers to allow code that is -/// fuzzing-specific to be left in the codebase. See -/// https://llvm.org/docs/LibFuzzer.html#id35 for more information -#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION -#define FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION (1) -#endif // FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION - -/// Magic value defined by SIMICS as the "leaf" value of a CPUID instruction -/// that is treated as a magic instruction. -#define MAGIC (0x4711U) -/// Default magic instruction `n` value to signal the TSFFS fuzzer to start the -/// fuzzing loop -#define MAGIC_START (0x0001U) -/// Default magic instruction `n` value to signal the TSFFS fuzzer to stop and -/// reset the fuzzing loop -#define MAGIC_STOP (0x0002U) -/// Default magic instruction `n` value to signal the TSFFS fuzzer that an error -/// has occurred and the testcase input should be saved as a solution -#define MAGIC_ASSERT (0x0003U) - -/// Alternative magic numbers that can be used for start and stop events in -/// conjunction with setting the magic number for each event via the SIMICS or -/// SIMICS Python script interface -#define MAGIC_ALT_0 (0x0004U) -#define MAGIC_ALT_1 (0x0005U) -#define MAGIC_ALT_2 (0x0006U) -#define MAGIC_ALT_3 (0x0007U) -#define MAGIC_ALT_4 (0x0008U) -#define MAGIC_ALT_5 (0x0009U) -#define MAGIC_ALT_6 (0x000aU) -#define MAGIC_ALT_7 (0x000bU) - -/// Invoke the magic instruction defined by SIMICS for the x86-64 architecture -/// (`cpuid`) with a specific value of `n`, after setting register `rdi` to the -/// value of the pointer to the testcase and register `rsi` to the value of the -/// pointer to the testcase size. These registers are accessed by the fuzzer and -/// are defined per-architecture. -#define __cpuid_extended2(value, inout_ptr_0, inout_ptr_1) \ - unsigned int _a __attribute__((unused)) = 0; \ - __asm__ __volatile__("cpuid" \ - : "=a"(_a) \ - : "a"(value), "D"(inout_ptr_0), "S"(inout_ptr_1) \ - : "rbx", "rcx", "rdx"); - -/// Invoke the magic instruction defined by SIMICS for the x86-64 architecture -/// (`cpuid`) with a specific value of `n` -#define __cpuid(value) \ - unsigned int _a __attribute__((unused)) = 0; \ - __asm__ __volatile__("cpuid\n\t" \ - : "=a"(_a) \ - : "a"(value) \ - : "rbx", "rcx", "rdx") - -/// Signal the fuzzer using a specific magic value `start` to start the fuzzing -/// loop at the point this macro is called. A snapshot will be taken when the -/// macro is called, and the maximum testcase size `*size_ptr` will be saved as -/// `max_testcase_size`. Each iteration of the fuzzing loop, the fuzzer input -/// (the "testcase") will be written to `*testcase_ptr` as if running -/// `memcpy(testcase_ptr, current_testcase, max_testcase_size)`, and the actual -/// size of the current testcase will be written to -/// `*size_ptr` as if running `*size_ptr = current_testcase_size`. -#define __arch_harness_start(start, testcase_ptr, size_ptr) \ - do { \ - unsigned int magic = (start << 0x10U) | MAGIC; \ - __cpuid_extended2(magic, testcase_ptr, size_ptr); \ - } while (0) - -/// Signal the fuzzer using the a specific magic value (`stop`) to stop and -/// reset to the beginning of the fuzzing loop with a "normal" stop status, -/// indicating no solution has occurred. -#define __arch_harness_stop(stop) \ - do { \ - unsigned int magic = (stop << 0x10U) | MAGIC; \ - __cpuid(magic); \ - } while (0) - -/// Signal the fuzzer using the default magic value to start the fuzzing loop at -/// the point this macro is called. A snapshot will be taken when the macro is -/// called, and the maximum testcase size `*size_ptr` will be saved as -/// `max_testcase_size`. Each iteration of the fuzzing loop, the fuzzer input -/// (the "testcase") will be written to -/// `*testcase_ptr` as if running `memcpy(testcase_ptr, current_testcase, -/// max_testcase_size)`, and the actual size of the current testcase will be -/// written to -/// `*size_ptr` as if running `*size_ptr = current_testcase_size`. -#define HARNESS_START(testcase_ptr, size_ptr) \ - do { \ - __arch_harness_start(MAGIC_START, testcase_ptr, size_ptr); \ - } while (0) - -/// Signal the fuzzer using the default magic value to stop and reset to the -/// beginning of the fuzzing loop with a "normal" stop status, indicating no -/// solution has occurred. -#define HARNESS_STOP() \ - do { \ - __arch_harness_stop(MAGIC_STOP); \ - } while (0) - -/// Signal the fuzzer using the default magic value to stop and reset to the -/// beginning of the fuzzing loop with a "solution" stop status, indicating some -/// custom error has occurred. -#define HARNESS_ASSERT() \ - do { \ - __arch_harness_stop(MAGIC_ASSERT); \ - } while (0) - -#endif // TSFFS_H \ No newline at end of file diff --git a/tests/rsrc/x86_64-uefi/build.sh b/tests/rsrc/x86_64-uefi/build.sh index bf1f8940..2eb84040 100755 --- a/tests/rsrc/x86_64-uefi/build.sh +++ b/tests/rsrc/x86_64-uefi/build.sh @@ -3,9 +3,11 @@ # Copyright (C) 2024 Intel Corporation # SPDX-License-Identifier: Apache-2.0 +set -e + SCRIPT_DIR=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" &>/dev/null && pwd) -cp "${SCRIPT_DIR}/../../../harness/tsffs-gcc-x86_64.h" "${SCRIPT_DIR}/tsffs-gcc-x86_64.h" +cp "${SCRIPT_DIR}/../../../harness/tsffs.h" "${SCRIPT_DIR}/tsffs.h" cp "${SCRIPT_DIR}/../../rsrc/minimal_boot_disk.craff" "${SCRIPT_DIR}/minimal_boot_disk.craff" ninja \ No newline at end of file diff --git a/tests/rsrc/x86_64-uefi/minimal_boot_disk.craff b/tests/rsrc/x86_64-uefi/minimal_boot_disk.craff deleted file mode 100644 index 2488b728..00000000 --- a/tests/rsrc/x86_64-uefi/minimal_boot_disk.craff +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:8ffab484ad42b567214be5a6a7245e2385a30964dd5438035a963ebc19126e36 -size 11505650 diff --git a/tests/rsrc/x86_64-uefi/test-cov.c b/tests/rsrc/x86_64-uefi/test-cov.c index b8a52e3f..616cfca3 100644 --- a/tests/rsrc/x86_64-uefi/test-cov.c +++ b/tests/rsrc/x86_64-uefi/test-cov.c @@ -4,7 +4,7 @@ #include #include -#include "tsffs-gcc-x86_64.h" +#include "tsffs.h" typedef struct EfiTableHeader { uint64_t signature; diff --git a/tests/rsrc/x86_64-uefi/test-fast.c b/tests/rsrc/x86_64-uefi/test-fast.c index 709a6b1c..2a240b51 100644 --- a/tests/rsrc/x86_64-uefi/test-fast.c +++ b/tests/rsrc/x86_64-uefi/test-fast.c @@ -4,7 +4,7 @@ #include #include -#include "tsffs-gcc-x86_64.h" +#include "tsffs.h" typedef struct EfiTableHeader { uint64_t signature; diff --git a/tests/rsrc/x86_64-uefi/test.c b/tests/rsrc/x86_64-uefi/test.c index b6f9d442..509b41fd 100644 --- a/tests/rsrc/x86_64-uefi/test.c +++ b/tests/rsrc/x86_64-uefi/test.c @@ -4,7 +4,7 @@ #include #include -#include "tsffs-gcc-x86_64.h" +#include "tsffs.h" typedef struct EfiTableHeader { uint64_t signature; diff --git a/tests/rsrc/x86_64-uefi/tsffs-gcc-x86_64.h b/tests/rsrc/x86_64-uefi/tsffs-gcc-x86_64.h deleted file mode 100644 index 706f9946..00000000 --- a/tests/rsrc/x86_64-uefi/tsffs-gcc-x86_64.h +++ /dev/null @@ -1,134 +0,0 @@ -// Copyright (C) 2023 Intel Corporation -// SPDX-License-Identifier: Apache-2.0 - -/// Definitions and macros for compiled-in harnessing of C and C++ target -/// software for the RISC-V (64-bit) architecture -/// -/// Example: -/// ```c -/// #include "tsffs-gcc-x86_64.h" -/// -/// int main() { -/// char buf[0x10]; -/// size_t size = 0x10; -/// size_t *size_ptr = &size; -/// HARNESS_START((char *)buf, size_ptr); -/// int retval = YourSpecialDecoder(buf, *size_ptr); -/// if (!retval) { -/// HARNESS_ASSERT(); -/// } else { -/// HARNESS_STOP(); -/// } -/// } -/// ``` - -#ifndef TSFFS_H -#define TSFFS_H - -/// Define common with LibFuzzer and other fuzzers to allow code that is -/// fuzzing-specific to be left in the codebase. See -/// https://llvm.org/docs/LibFuzzer.html#id35 for more information -#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION -#define FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION (1) -#endif // FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION - -/// Magic value defined by SIMICS as the "leaf" value of a CPUID instruction -/// that is treated as a magic instruction. -#define MAGIC (0x4711U) -/// Default magic instruction `n` value to signal the TSFFS fuzzer to start the -/// fuzzing loop -#define MAGIC_START (0x0001U) -/// Default magic instruction `n` value to signal the TSFFS fuzzer to stop and -/// reset the fuzzing loop -#define MAGIC_STOP (0x0002U) -/// Default magic instruction `n` value to signal the TSFFS fuzzer that an error -/// has occurred and the testcase input should be saved as a solution -#define MAGIC_ASSERT (0x0003U) - -/// Alternative magic numbers that can be used for start and stop events in -/// conjunction with setting the magic number for each event via the SIMICS or -/// SIMICS Python script interface -#define MAGIC_ALT_0 (0x0004U) -#define MAGIC_ALT_1 (0x0005U) -#define MAGIC_ALT_2 (0x0006U) -#define MAGIC_ALT_3 (0x0007U) -#define MAGIC_ALT_4 (0x0008U) -#define MAGIC_ALT_5 (0x0009U) -#define MAGIC_ALT_6 (0x000aU) -#define MAGIC_ALT_7 (0x000bU) - -/// Invoke the magic instruction defined by SIMICS for the x86-64 architecture -/// (`cpuid`) with a specific value of `n`, after setting register `rdi` to the -/// value of the pointer to the testcase and register `rsi` to the value of the -/// pointer to the testcase size. These registers are accessed by the fuzzer and -/// are defined per-architecture. -#define __cpuid_extended2(value, inout_ptr_0, inout_ptr_1) \ - unsigned int _a __attribute__((unused)) = 0; \ - __asm__ __volatile__("cpuid" \ - : "=a"(_a) \ - : "a"(value), "D"(inout_ptr_0), "S"(inout_ptr_1) \ - : "rbx", "rcx", "rdx"); - -/// Invoke the magic instruction defined by SIMICS for the x86-64 architecture -/// (`cpuid`) with a specific value of `n` -#define __cpuid(value) \ - unsigned int _a __attribute__((unused)) = 0; \ - __asm__ __volatile__("cpuid\n\t" \ - : "=a"(_a) \ - : "a"(value) \ - : "rbx", "rcx", "rdx") - -/// Signal the fuzzer using a specific magic value `start` to start the fuzzing -/// loop at the point this macro is called. A snapshot will be taken when the -/// macro is called, and the maximum testcase size `*size_ptr` will be saved as -/// `max_testcase_size`. Each iteration of the fuzzing loop, the fuzzer input -/// (the "testcase") will be written to `*testcase_ptr` as if running -/// `memcpy(testcase_ptr, current_testcase, max_testcase_size)`, and the actual -/// size of the current testcase will be written to -/// `*size_ptr` as if running `*size_ptr = current_testcase_size`. -#define __arch_harness_start(start, testcase_ptr, size_ptr) \ - do { \ - unsigned int magic = (start << 0x10U) | MAGIC; \ - __cpuid_extended2(magic, testcase_ptr, size_ptr); \ - } while (0) - -/// Signal the fuzzer using the a specific magic value (`stop`) to stop and -/// reset to the beginning of the fuzzing loop with a "normal" stop status, -/// indicating no solution has occurred. -#define __arch_harness_stop(stop) \ - do { \ - unsigned int magic = (stop << 0x10U) | MAGIC; \ - __cpuid(magic); \ - } while (0) - -/// Signal the fuzzer using the default magic value to start the fuzzing loop at -/// the point this macro is called. A snapshot will be taken when the macro is -/// called, and the maximum testcase size `*size_ptr` will be saved as -/// `max_testcase_size`. Each iteration of the fuzzing loop, the fuzzer input -/// (the "testcase") will be written to -/// `*testcase_ptr` as if running `memcpy(testcase_ptr, current_testcase, -/// max_testcase_size)`, and the actual size of the current testcase will be -/// written to -/// `*size_ptr` as if running `*size_ptr = current_testcase_size`. -#define HARNESS_START(testcase_ptr, size_ptr) \ - do { \ - __arch_harness_start(MAGIC_START, testcase_ptr, size_ptr); \ - } while (0) - -/// Signal the fuzzer using the default magic value to stop and reset to the -/// beginning of the fuzzing loop with a "normal" stop status, indicating no -/// solution has occurred. -#define HARNESS_STOP() \ - do { \ - __arch_harness_stop(MAGIC_STOP); \ - } while (0) - -/// Signal the fuzzer using the default magic value to stop and reset to the -/// beginning of the fuzzing loop with a "solution" stop status, indicating some -/// custom error has occurred. -#define HARNESS_ASSERT() \ - do { \ - __arch_harness_stop(MAGIC_ASSERT); \ - } while (0) - -#endif // TSFFS_H \ No newline at end of file diff --git a/tests/x86_64_magic_apitest.rs b/tests/x86_64_magic_apitest.rs index f1b1cfb3..75534d0a 100644 --- a/tests/x86_64_magic_apitest.rs +++ b/tests/x86_64_magic_apitest.rs @@ -53,9 +53,6 @@ fn test_x86_64_magic_apitest() -> Result<()> { @tsffs.start_on_harness = True @tsffs.stop_on_harness = True @tsffs.use_snapshots = True - @tsffs.magic_start = 1 - @tsffs.magic_stop = 2 - @tsffs.magic_assert = 3 @tsffs.iteration_limit = 100 @tsffs.initial_random_corpus_size = 32 @tsffs.corpus_directory = SIM_lookup_file("%simics%") + "/corpus" diff --git a/tests/x86_64_manual.rs b/tests/x86_64_manual.rs index 93f6010d..d6b7d687 100644 --- a/tests/x86_64_manual.rs +++ b/tests/x86_64_manual.rs @@ -45,7 +45,7 @@ fn test_x86_64_manual() -> Result<()> { simics.SIM_load_module("tsffs") tsffs = simics.SIM_create_object(simics.SIM_get_class("tsffs"), "tsffs", []) - simics.SIM_set_log_level(tsffs, 2) + simics.SIM_set_log_level(tsffs, 4) tsffs.start_on_harness = False tsffs.stop_on_harness = False tsffs.timeout = 3.0 @@ -82,14 +82,14 @@ fn test_x86_64_manual() -> Result<()> { # In reality, you probably have a known buffer in mind to fuzz testcase_address_regno = conf.qsp.mb.cpu0.core[0][0].iface.int_register.get_number( - "rdi" + "rsi" ) print("testcase address regno: ", testcase_address_regno) testcase_address = conf.qsp.mb.cpu0.core[0][0].iface.int_register.read( testcase_address_regno ) print("testcase address: ", testcase_address) - size_regno = conf.qsp.mb.cpu0.core[0][0].iface.int_register.get_number("rsi") + size_regno = conf.qsp.mb.cpu0.core[0][0].iface.int_register.get_number("rdx") print("size regno: ", size_regno) size_address = conf.qsp.mb.cpu0.core[0][0].iface.int_register.read(size_regno) print("size address: ", size_address) @@ -104,7 +104,7 @@ fn test_x86_64_manual() -> Result<()> { virt, ) - tsffs.iface.fuzz.start( + tsffs.iface.fuzz.start_with_buffer_ptr_size_ptr( conf.qsp.mb.cpu0.core[0][0], testcase_address, size_address, diff --git a/tests/x86_64_manual_max.rs b/tests/x86_64_manual_max.rs index 1eaf0520..bb3f307e 100644 --- a/tests/x86_64_manual_max.rs +++ b/tests/x86_64_manual_max.rs @@ -82,7 +82,7 @@ fn test_x86_64_manual_max() -> Result<()> { # In reality, you probably have a known buffer in mind to fuzz testcase_address_regno = conf.qsp.mb.cpu0.core[0][0].iface.int_register.get_number( - "rdi" + "rsi" ) print("testcase address regno: ", testcase_address_regno) testcase_address = conf.qsp.mb.cpu0.core[0][0].iface.int_register.read( @@ -101,7 +101,7 @@ fn test_x86_64_manual_max() -> Result<()> { virt, ) - tsffs.iface.fuzz.start_with_maximum_size( + tsffs.iface.fuzz.start_with_buffer_ptr_size_value( conf.qsp.mb.cpu0.core[0][0], testcase_address, maximum_size,