mirror of
https://github.com/microsoft/onefuzz.git
synced 2025-06-15 19:38:11 +00:00
add non-x86_64 architecture libfuzzer target support using qemu-user (#600)
This commit is contained in:
8
.github/workflows/ci.yml
vendored
8
.github/workflows/ci.yml
vendored
@ -327,7 +327,7 @@ jobs:
|
|||||||
name: release-artifacts
|
name: release-artifacts
|
||||||
path: release-artifacts
|
path: release-artifacts
|
||||||
build-integration-tests-linux:
|
build-integration-tests-linux:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-18.04
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
- run: |
|
- run: |
|
||||||
@ -363,6 +363,12 @@ jobs:
|
|||||||
export AFL_CC_PATH=$PWD/AFL/afl-clang
|
export AFL_CC_PATH=$PWD/AFL/afl-clang
|
||||||
(cd trivial-crash ; make clean; make CC=$AFL_CC_PATH)
|
(cd trivial-crash ; make clean; make CC=$AFL_CC_PATH)
|
||||||
cp -r trivial-crash/fuzz.exe trivial-crash/seeds artifacts/linux-trivial-crash-afl
|
cp -r trivial-crash/fuzz.exe trivial-crash/seeds artifacts/linux-trivial-crash-afl
|
||||||
|
|
||||||
|
# libfuzzer cross-compiled to aarch64
|
||||||
|
mkdir -p artifacts/linux-libfuzzer-aarch64-crosscompile
|
||||||
|
(cd libfuzzer-aarch64-crosscompile; make)
|
||||||
|
cp -r libfuzzer-aarch64-crosscompile/fuzz.exe libfuzzer-aarch64-crosscompile/inputs artifacts/linux-libfuzzer-aarch64-crosscompile
|
||||||
|
|
||||||
- uses: actions/upload-artifact@v2.2.2
|
- uses: actions/upload-artifact@v2.2.2
|
||||||
with:
|
with:
|
||||||
name: integration-test-artifacts
|
name: integration-test-artifacts
|
||||||
|
151
docs/how-to/fuzzing-other-architectures-on-azure.md
Normal file
151
docs/how-to/fuzzing-other-architectures-on-azure.md
Normal file
@ -0,0 +1,151 @@
|
|||||||
|
# Fuzzing non-X86_64 targets on Azure
|
||||||
|
|
||||||
|
Fuzzing non-x86_64 targets using libFuzzer on Azure makes use of two Open
|
||||||
|
Source capabilities:
|
||||||
|
|
||||||
|
2. [libFuzzer](https://www.llvm.org/docs/LibFuzzer.html), which performs the fuzzing
|
||||||
|
3. [qemu-user](https://qemu.readthedocs.io/en/latest/user/main.html), which provides user-space emulation.
|
||||||
|
|
||||||
|
TL/DR: check out our [libfuzzer-aarch64-crosscompile example](../../src/integration-tests/libfuzzer-aarch64-crosscompile/)
|
||||||
|
|
||||||
|
## Issues using qemu_user based LibFuzzer in OneFuzz
|
||||||
|
|
||||||
|
There are a few notable limitations when using `qemu-user` based libFuzzer targets in OneFuzz.
|
||||||
|
1. Live reproduction of crashes does not work.
|
||||||
|
2. The `libfuzzer-coverage` task does not work.
|
||||||
|
3. Only Linux is supported at this time.
|
||||||
|
4. Only `aarch64` CPU emulation has been tested. PRs are welcome to support other architectures.
|
||||||
|
5. Custom setup scripts are not supported, though you can provide your own sysroot using `--sysroot`.
|
||||||
|
|
||||||
|
As such, a `libfuzzer qemu_user` template is available, which only uses the `libfuzzer_fuzz` and `libfuzzer_crash_report`. As these issues are resolve, the template will be updated to include the additional tasks.
|
||||||
|
|
||||||
|
## Example
|
||||||
|
|
||||||
|
Let's build a simple `aarch64` target using GCC as a cross-compiler (See [our example](../../src/integration-tests/libfuzzer-aarch64-crosscompile/)).
|
||||||
|
|
||||||
|
1. Make sure you have QEMU and the appropriate cross compiler installed:
|
||||||
|
```bash
|
||||||
|
sudo apt update
|
||||||
|
sudo apt install -y qemu-user g++-aarch64-linux-gnu
|
||||||
|
```
|
||||||
|
2. Check out the libFuzzer libraries from `compiler-rt`. Note, GCC requires the use of `pc-guard` for instrumentation, which was removed by the `compiler-rt` project. As such, we need an older version of the library:
|
||||||
|
```
|
||||||
|
git clone https://github.com/llvm-mirror/compiler-rt
|
||||||
|
(cd compiler-rt; git checkout daa6759576548a2f3825faddaa6811cabbfb45eb)
|
||||||
|
```
|
||||||
|
3. Build the libFuzzer libraries *without* ASAN:
|
||||||
|
```
|
||||||
|
mkdir -p fuzz-libs
|
||||||
|
(cd fuzz-libs; aarch64-linux-gnu-g++ -c ../compiler-rt/lib/fuzzer/*.cpp)
|
||||||
|
```
|
||||||
|
4. Build our target:
|
||||||
|
```
|
||||||
|
aarch64-linux-gnu-g++ -pthread -lasan -o fuzz.exe fuzz-libs/*.o fuzz.c -fsanitize=address -fsanitize-coverage=trace-pc
|
||||||
|
```
|
||||||
|
5. Verify our target built correctly:
|
||||||
|
```
|
||||||
|
ASAN_OPTIONS=:detect_leaks=0 qemu-aarch64 -L /usr/aarch64-linux-gnu ./fuzz.exe -help=1
|
||||||
|
```
|
||||||
|
> NOTE: `LSAN` does not work in `qemu-user`, so we need to disable that.
|
||||||
|
|
||||||
|
Now we're ready to deploy this target to OneFuzz. Note, if we have custom
|
||||||
|
libraries or want to run on a different version of linux, we'll need to
|
||||||
|
provide our own sysroot.
|
||||||
|
|
||||||
|
7. Now we can fuzz!
|
||||||
|
Execute our `fuzz.exe` with `qemu-aarch64` our `inputs` directory:
|
||||||
|
```
|
||||||
|
ASAN_OPTIONS=:detect_leaks=0 qemu-aarch64 -L /usr/aarch64-linux-gnu ./fuzz.exe ./inputs
|
||||||
|
```
|
||||||
|
|
||||||
|
In a few seconds, you'll see output that looks something like this:
|
||||||
|
```
|
||||||
|
INFO: Seed: 113138795
|
||||||
|
INFO: 1 files found in ./inputs/ INFO: -max_len is not provided; libFuzzer will not generate inputs larger than 4096 bytes
|
||||||
|
INFO: seed corpus: files: 1 min: 3b max: 3b total: 3b rss: 314Mb
|
||||||
|
#2 INITED cov: 5 ft: 5 corp: 1/3b lim: 4 exec/s: 0 rss: 317Mb
|
||||||
|
#3 NEW cov: 5 ft: 9 corp: 2/6b lim: 4 exec/s: 0 rss: 317Mb L: 3/3 MS: 1 ChangeBit-
|
||||||
|
#4 NEW cov: 5 ft: 13 corp: 3/9b lim: 4 exec/s: 0 rss: 317Mb L: 3/3 MS: 1 ChangeBit-
|
||||||
|
#8 NEW cov: 10 ft: 21 corp: 4/13b lim: 4 exec/s: 0 rss: 317Mb L: 4/4 MS: 4 ShuffleBytes-ChangeBit-ChangeBinInt-CrossOver-
|
||||||
|
#9 NEW cov: 10 ft: 26 corp: 5/17b lim: 4 exec/s: 0 rss: 317Mb L: 4/4 MS: 1 CopyPart-
|
||||||
|
#10 NEW cov: 10 ft: 31 corp: 6/21b lim: 4 exec/s: 0 rss: 317Mb L: 4/4 MS: 1 ChangeBit-
|
||||||
|
#11 NEW cov: 10 ft: 36 corp: 7/25b lim: 4 exec/s: 0 rss: 317Mb L: 4/4 MS: 1 ShuffleBytes-
|
||||||
|
#12 NEW cov: 10 ft: 37 corp: 8/28b lim: 4 exec/s: 0 rss: 317Mb L: 3/4 MS: 1 EraseBytes-
|
||||||
|
#16 NEW cov: 10 ft: 45 corp: 9/32b lim: 4 exec/s: 0 rss: 317Mb L: 4/4 MS: 4 CopyPart-CopyPart-CrossOver-ShuffleBytes-
|
||||||
|
#18 NEW cov: 11 ft: 46 corp: 10/36b lim: 4 exec/s: 0 rss: 317Mb L: 4/4 MS: 2 CopyPart-ChangeBit-
|
||||||
|
#19 NEW cov: 11 ft: 47 corp: 11/40b lim: 4 exec/s: 0 rss: 317Mb L: 4/4 MS: 1 ShuffleBytes-
|
||||||
|
#20 REDUCE cov: 11 ft: 47 corp: 11/39b lim: 4 exec/s: 0 rss: 317Mb L: 2/4 MS: 1 EraseBytes-
|
||||||
|
#30 NEW cov: 11 ft: 52 corp: 12/43b lim: 4 exec/s: 0 rss: 317Mb L: 4/4 MS: 5 ChangeBit-CrossOver-CrossOver-CrossOver-CrossOver-
|
||||||
|
#32 NEW cov: 11 ft: 56 corp: 13/45b lim: 4 exec/s: 0 rss: 317Mb L: 2/4 MS: 2 CrossOver-ChangeBit-
|
||||||
|
#38 REDUCE cov: 11 ft: 56 corp: 13/44b lim: 4 exec/s: 0 rss: 317Mb L: 1/4 MS: 1 EraseBytes-
|
||||||
|
```
|
||||||
|
## Launching our example in OneFuzz
|
||||||
|
|
||||||
|
These commands launches the a qemu-user based libFuzzer job in OneFuzz. Note, we've added the arguments `--wait_for_running --wait_for_files inputs` such that we can monitor our job until we've seen at least one new input found via fuzzing.
|
||||||
|
```bash
|
||||||
|
TARGET_PROJECT=AARCH64
|
||||||
|
TARGET_NAME=Example
|
||||||
|
TARGET_BUILD=1
|
||||||
|
FUZZ_POOL=linux
|
||||||
|
onefuzz template libfuzzer qemu_user ${TARGET_PROJECT} ${TARGET_NAME} ${TARGET_BUILD} ${FUZZ_POOL} --wait_for_running --wait_for_files inputs
|
||||||
|
```
|
||||||
|
|
||||||
|
When we run this, we'll see output similar to:
|
||||||
|
```
|
||||||
|
WARNING:onefuzz:qemu_user jobs are a preview feature and may change in the future
|
||||||
|
INFO:onefuzz:creating job (runtime: 24 hours)
|
||||||
|
INFO:onefuzz:created job: fa5b7870-a51b-4f79-924f-2ef11a9830a0
|
||||||
|
INFO:onefuzz:using container: oft-setup-5346d5f33bc35c3d94cbc70f7815b85e
|
||||||
|
INFO:onefuzz:using container: oft-inputs-9c31136dc16a5aab8edf7666a614a285
|
||||||
|
INFO:onefuzz:using container: oft-crashes-9c31136dc16a5aab8edf7666a614a285
|
||||||
|
INFO:onefuzz:using container: oft-reports-9c31136dc16a5aab8edf7666a614a285
|
||||||
|
INFO:onefuzz:using container: oft-unique-reports-9c31136dc16a5aab8edf7666a614a285
|
||||||
|
INFO:onefuzz:using container: oft-no-repro-9c31136dc16a5aab8edf7666a614a285
|
||||||
|
INFO:onefuzz:uploading target exe `fuzz.exe`
|
||||||
|
INFO:onefuzz:uploading /tmp/tmp_9_f9kc3/setup.sh
|
||||||
|
INFO:onefuzz:uploading /tmp/tmp_9_f9kc3/fuzz.exe-wrapper.sh
|
||||||
|
INFO:onefuzz:creating libfuzzer_fuzz task
|
||||||
|
INFO:onefuzz:creating libfuzzer_crash_report task
|
||||||
|
INFO:onefuzz:done creating tasks
|
||||||
|
- waiting on: libfuzzer_crash_report:init, libfuzzer_fuzz:init
|
||||||
|
- waiting on: libfuzzer_crash_report:waiting, libfuzzer_fuzz:scheduled
|
||||||
|
| waiting on: libfuzzer_crash_report:waiting, libfuzzer_fuzz:setting_up
|
||||||
|
/ waiting on: libfuzzer_crash_report:waiting
|
||||||
|
| waiting on: libfuzzer_crash_report:scheduled
|
||||||
|
\ waiting on: libfuzzer_crash_report:setting_up
|
||||||
|
INFO:onefuzz:tasks started
|
||||||
|
\ waiting for new files: oft-inputs-9c31136dc16a5aab8edf7666a614a285
|
||||||
|
INFO:onefuzz:new files found
|
||||||
|
{
|
||||||
|
"config": {
|
||||||
|
"build": "1",
|
||||||
|
"duration": 24,
|
||||||
|
"name": "Example",
|
||||||
|
"project": "AARCH64"
|
||||||
|
},
|
||||||
|
"end_time": "2021-02-27T16:41:24+00:00",
|
||||||
|
"job_id": "fa5b7870-a51b-4f79-924f-2ef11a9830a0",
|
||||||
|
"state": "enabled",
|
||||||
|
"task_info": [
|
||||||
|
{
|
||||||
|
"state": "stopped",
|
||||||
|
"task_id": "15cc52b9-b15b-4cf7-9fa7-669db67c8e0b",
|
||||||
|
"type": "libfuzzer_fuzz"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"state": "running",
|
||||||
|
"task_id": "b3466396-7047-46f5-a58d-a21ada881e97",
|
||||||
|
"type": "libfuzzer_crash_report"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"user_info": {
|
||||||
|
"application_id": "e3b350d1-7863-4bd5-a4c0-83e6436c9c09",
|
||||||
|
"object_id": "232a2ac6-f8fc-4eb3-b427-0c91bbab7eea",
|
||||||
|
"upn": "example@contoso.com"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## See Also
|
||||||
|
* [afl-unicorn](https://github.com/Battelle/afl-unicorn) - AFL-based application fuzzing using [Unicorn](https://www.unicorn-engine.org/)
|
||||||
|
* [TriforceAFL] (https://github.com/nccgroup/TriforceAFL) - AFL-based full-system fuzzing using a fork of [QEMU](https://qemu.readthedocs.io/)
|
@ -4,6 +4,8 @@
|
|||||||
# Licensed under the MIT License.
|
# Licensed under the MIT License.
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
import tempfile
|
||||||
|
from enum import Enum
|
||||||
from typing import Dict, List, Optional
|
from typing import Dict, List, Optional
|
||||||
|
|
||||||
from onefuzztypes.enums import OS, ContainerType, TaskDebugFlag, TaskType
|
from onefuzztypes.enums import OS, ContainerType, TaskDebugFlag, TaskType
|
||||||
@ -17,6 +19,10 @@ from . import JobHelper
|
|||||||
LIBFUZZER_MAGIC_STRING = b"ERROR: libFuzzer"
|
LIBFUZZER_MAGIC_STRING = b"ERROR: libFuzzer"
|
||||||
|
|
||||||
|
|
||||||
|
class QemuArch(Enum):
|
||||||
|
aarch64 = "aarch64"
|
||||||
|
|
||||||
|
|
||||||
class Libfuzzer(Command):
|
class Libfuzzer(Command):
|
||||||
""" Pre-defined Libfuzzer job """
|
""" Pre-defined Libfuzzer job """
|
||||||
|
|
||||||
@ -488,3 +494,199 @@ class Libfuzzer(Command):
|
|||||||
self.logger.info("done creating tasks")
|
self.logger.info("done creating tasks")
|
||||||
helper.wait()
|
helper.wait()
|
||||||
return helper.job
|
return helper.job
|
||||||
|
|
||||||
|
def qemu_user(
|
||||||
|
self,
|
||||||
|
project: str,
|
||||||
|
name: str,
|
||||||
|
build: str,
|
||||||
|
pool_name: PoolName,
|
||||||
|
*,
|
||||||
|
arch: QemuArch = QemuArch.aarch64,
|
||||||
|
target_exe: File = File("fuzz.exe"),
|
||||||
|
sysroot: Optional[File] = None,
|
||||||
|
vm_count: int = 1,
|
||||||
|
inputs: Optional[Directory] = None,
|
||||||
|
reboot_after_setup: bool = False,
|
||||||
|
duration: int = 24,
|
||||||
|
target_workers: Optional[int] = 1,
|
||||||
|
target_options: Optional[List[str]] = None,
|
||||||
|
target_env: Optional[Dict[str, str]] = None,
|
||||||
|
tags: Optional[Dict[str, str]] = None,
|
||||||
|
wait_for_running: bool = False,
|
||||||
|
wait_for_files: Optional[List[ContainerType]] = None,
|
||||||
|
existing_inputs: Optional[Container] = None,
|
||||||
|
debug: Optional[List[TaskDebugFlag]] = None,
|
||||||
|
ensemble_sync_delay: Optional[int] = None,
|
||||||
|
colocate_all_tasks: bool = False,
|
||||||
|
crash_report_timeout: Optional[int] = 1,
|
||||||
|
check_retry_count: Optional[int] = 300,
|
||||||
|
check_fuzzer_help: bool = True,
|
||||||
|
) -> Optional[Job]:
|
||||||
|
|
||||||
|
"""
|
||||||
|
libfuzzer tasks, wrapped via qemu-user (PREVIEW FEATURE)
|
||||||
|
"""
|
||||||
|
|
||||||
|
self.logger.warning(
|
||||||
|
"qemu_user jobs are a preview feature and may change in the future"
|
||||||
|
)
|
||||||
|
|
||||||
|
pool = self.onefuzz.pools.get(pool_name)
|
||||||
|
if pool.os != OS.linux:
|
||||||
|
raise Exception("libfuzzer qemu-user jobs are only compatible with Linux")
|
||||||
|
|
||||||
|
self._check_is_libfuzzer(target_exe)
|
||||||
|
|
||||||
|
if target_options is None:
|
||||||
|
target_options = []
|
||||||
|
|
||||||
|
# disable detect_leaks, as this is non-functional on cross-compile targets
|
||||||
|
if target_env is None:
|
||||||
|
target_env = {}
|
||||||
|
target_env["ASAN_OPTIONS"] = (
|
||||||
|
target_env.get("ASAN_OPTIONS", "") + ":detect_leaks=0"
|
||||||
|
)
|
||||||
|
|
||||||
|
helper = JobHelper(
|
||||||
|
self.onefuzz,
|
||||||
|
self.logger,
|
||||||
|
project,
|
||||||
|
name,
|
||||||
|
build,
|
||||||
|
duration,
|
||||||
|
pool_name=pool_name,
|
||||||
|
target_exe=target_exe,
|
||||||
|
)
|
||||||
|
|
||||||
|
helper.add_tags(tags)
|
||||||
|
helper.define_containers(
|
||||||
|
ContainerType.setup,
|
||||||
|
ContainerType.inputs,
|
||||||
|
ContainerType.crashes,
|
||||||
|
ContainerType.reports,
|
||||||
|
ContainerType.unique_reports,
|
||||||
|
ContainerType.no_repro,
|
||||||
|
)
|
||||||
|
|
||||||
|
if existing_inputs:
|
||||||
|
self.onefuzz.containers.get(existing_inputs)
|
||||||
|
helper.containers[ContainerType.inputs] = existing_inputs
|
||||||
|
else:
|
||||||
|
helper.define_containers(ContainerType.inputs)
|
||||||
|
|
||||||
|
fuzzer_containers = [
|
||||||
|
(ContainerType.setup, helper.containers[ContainerType.setup]),
|
||||||
|
(ContainerType.crashes, helper.containers[ContainerType.crashes]),
|
||||||
|
(ContainerType.inputs, helper.containers[ContainerType.inputs]),
|
||||||
|
]
|
||||||
|
|
||||||
|
helper.create_containers()
|
||||||
|
|
||||||
|
target_exe_blob_name = helper.target_exe_blob_name(target_exe, None)
|
||||||
|
|
||||||
|
wrapper_name = File(target_exe_blob_name + "-wrapper.sh")
|
||||||
|
|
||||||
|
with tempfile.TemporaryDirectory() as tempdir:
|
||||||
|
if sysroot:
|
||||||
|
setup_path = File(os.path.join(tempdir, "setup.sh"))
|
||||||
|
with open(setup_path, "w") as handle:
|
||||||
|
sysroot_filename = helper.target_exe_blob_name(sysroot, None)
|
||||||
|
handle.write(
|
||||||
|
"#!/bin/bash\n"
|
||||||
|
"set -ex\n"
|
||||||
|
"sudo apt-get install -y qemu-user g++-aarch64-linux-gnu libasan5-arm64-cross\n"
|
||||||
|
'cd $(dirname "$(readlink -f "$0")")\n'
|
||||||
|
"mkdir -p sysroot\n"
|
||||||
|
"tar -C sysroot -zxvf %s\n" % sysroot_filename
|
||||||
|
)
|
||||||
|
|
||||||
|
wrapper_path = File(os.path.join(tempdir, wrapper_name))
|
||||||
|
with open(wrapper_path, "w") as handle:
|
||||||
|
handle.write(
|
||||||
|
"#!/bin/bash\n"
|
||||||
|
'SETUP_DIR=$(dirname "$(readlink -f "$0")")\n'
|
||||||
|
"qemu-%s -L $SETUP_DIR/sysroot $SETUP_DIR/%s $*"
|
||||||
|
% (arch.name, target_exe_blob_name)
|
||||||
|
)
|
||||||
|
upload_files = [setup_path, wrapper_path, sysroot]
|
||||||
|
else:
|
||||||
|
setup_path = File(os.path.join(tempdir, "setup.sh"))
|
||||||
|
with open(setup_path, "w") as handle:
|
||||||
|
handle.write(
|
||||||
|
"#!/bin/bash\n"
|
||||||
|
"set -ex\n"
|
||||||
|
"sudo apt-get install -y qemu-user g++-aarch64-linux-gnu libasan5-arm64-cross\n"
|
||||||
|
)
|
||||||
|
|
||||||
|
wrapper_path = File(os.path.join(tempdir, wrapper_name))
|
||||||
|
with open(wrapper_path, "w") as handle:
|
||||||
|
handle.write(
|
||||||
|
"#!/bin/bash\n"
|
||||||
|
'SETUP_DIR=$(dirname "$(readlink -f "$0")")\n'
|
||||||
|
"qemu-%s -L /usr/%s-linux-gnu $SETUP_DIR/%s $*"
|
||||||
|
% (arch.name, arch.name, target_exe_blob_name)
|
||||||
|
)
|
||||||
|
upload_files = [setup_path, wrapper_path]
|
||||||
|
helper.upload_setup(None, target_exe, upload_files)
|
||||||
|
|
||||||
|
if inputs:
|
||||||
|
helper.upload_inputs(inputs)
|
||||||
|
helper.wait_on(wait_for_files, wait_for_running)
|
||||||
|
|
||||||
|
self.logger.info("creating libfuzzer_fuzz task")
|
||||||
|
fuzzer_task = self.onefuzz.tasks.create(
|
||||||
|
helper.job.job_id,
|
||||||
|
TaskType.libfuzzer_fuzz,
|
||||||
|
wrapper_name,
|
||||||
|
fuzzer_containers,
|
||||||
|
pool_name=pool_name,
|
||||||
|
reboot_after_setup=reboot_after_setup,
|
||||||
|
duration=duration,
|
||||||
|
vm_count=vm_count,
|
||||||
|
target_options=target_options,
|
||||||
|
target_env=target_env,
|
||||||
|
target_workers=target_workers,
|
||||||
|
tags=tags,
|
||||||
|
debug=debug,
|
||||||
|
ensemble_sync_delay=ensemble_sync_delay,
|
||||||
|
expect_crash_on_failure=False,
|
||||||
|
check_fuzzer_help=check_fuzzer_help,
|
||||||
|
)
|
||||||
|
|
||||||
|
report_containers = [
|
||||||
|
(ContainerType.setup, helper.containers[ContainerType.setup]),
|
||||||
|
(ContainerType.crashes, helper.containers[ContainerType.crashes]),
|
||||||
|
(ContainerType.reports, helper.containers[ContainerType.reports]),
|
||||||
|
(
|
||||||
|
ContainerType.unique_reports,
|
||||||
|
helper.containers[ContainerType.unique_reports],
|
||||||
|
),
|
||||||
|
(ContainerType.no_repro, helper.containers[ContainerType.no_repro]),
|
||||||
|
]
|
||||||
|
|
||||||
|
self.logger.info("creating libfuzzer_crash_report task")
|
||||||
|
self.onefuzz.tasks.create(
|
||||||
|
helper.job.job_id,
|
||||||
|
TaskType.libfuzzer_crash_report,
|
||||||
|
wrapper_name,
|
||||||
|
report_containers,
|
||||||
|
pool_name=pool_name,
|
||||||
|
duration=duration,
|
||||||
|
vm_count=1,
|
||||||
|
reboot_after_setup=reboot_after_setup,
|
||||||
|
target_options=target_options,
|
||||||
|
target_env=target_env,
|
||||||
|
tags=tags,
|
||||||
|
prereq_tasks=[fuzzer_task.task_id],
|
||||||
|
target_timeout=crash_report_timeout,
|
||||||
|
check_retry_count=check_retry_count,
|
||||||
|
debug=debug,
|
||||||
|
colocate=colocate_all_tasks,
|
||||||
|
expect_crash_on_failure=False,
|
||||||
|
check_fuzzer_help=check_fuzzer_help,
|
||||||
|
)
|
||||||
|
|
||||||
|
self.logger.info("done creating tasks")
|
||||||
|
helper.wait()
|
||||||
|
return helper.job
|
||||||
|
@ -42,6 +42,7 @@ BUILD = "0"
|
|||||||
class TemplateType(Enum):
|
class TemplateType(Enum):
|
||||||
libfuzzer = "libfuzzer"
|
libfuzzer = "libfuzzer"
|
||||||
libfuzzer_dotnet = "libfuzzer_dotnet"
|
libfuzzer_dotnet = "libfuzzer_dotnet"
|
||||||
|
libfuzzer_qemu_user = "libfuzzer_qemu_user"
|
||||||
afl = "afl"
|
afl = "afl"
|
||||||
radamsa = "radamsa"
|
radamsa = "radamsa"
|
||||||
|
|
||||||
@ -57,6 +58,7 @@ class Integration(BaseModel):
|
|||||||
check_asan_log: Optional[bool] = Field(default=False)
|
check_asan_log: Optional[bool] = Field(default=False)
|
||||||
disable_check_debugger: Optional[bool] = Field(default=False)
|
disable_check_debugger: Optional[bool] = Field(default=False)
|
||||||
reboot_after_setup: Optional[bool] = Field(default=False)
|
reboot_after_setup: Optional[bool] = Field(default=False)
|
||||||
|
test_repro: Optional[bool] = Field(default=True)
|
||||||
|
|
||||||
|
|
||||||
TARGETS: Dict[str, Integration] = {
|
TARGETS: Dict[str, Integration] = {
|
||||||
@ -84,6 +86,15 @@ TARGETS: Dict[str, Integration] = {
|
|||||||
use_setup=True,
|
use_setup=True,
|
||||||
wait_for_files=[ContainerType.inputs, ContainerType.crashes],
|
wait_for_files=[ContainerType.inputs, ContainerType.crashes],
|
||||||
),
|
),
|
||||||
|
"linux-libfuzzer-aarch64-crosscompile": Integration(
|
||||||
|
template=TemplateType.libfuzzer_qemu_user,
|
||||||
|
os=OS.linux,
|
||||||
|
target_exe="fuzz.exe",
|
||||||
|
inputs="inputs",
|
||||||
|
use_setup=True,
|
||||||
|
wait_for_files=[ContainerType.inputs, ContainerType.crashes],
|
||||||
|
test_repro=False,
|
||||||
|
),
|
||||||
"linux-libfuzzer-rust": Integration(
|
"linux-libfuzzer-rust": Integration(
|
||||||
template=TemplateType.libfuzzer,
|
template=TemplateType.libfuzzer,
|
||||||
os=OS.linux,
|
os=OS.linux,
|
||||||
@ -237,6 +248,17 @@ class TestOnefuzz:
|
|||||||
duration=1,
|
duration=1,
|
||||||
vm_count=1,
|
vm_count=1,
|
||||||
)
|
)
|
||||||
|
elif config.template == TemplateType.libfuzzer_qemu_user:
|
||||||
|
job = self.of.template.libfuzzer.qemu_user(
|
||||||
|
self.project,
|
||||||
|
target,
|
||||||
|
BUILD,
|
||||||
|
self.pools[config.os].name,
|
||||||
|
inputs=inputs,
|
||||||
|
target_exe=target_exe,
|
||||||
|
duration=1,
|
||||||
|
vm_count=1,
|
||||||
|
)
|
||||||
elif config.template == TemplateType.radamsa:
|
elif config.template == TemplateType.radamsa:
|
||||||
job = self.of.template.radamsa.basic(
|
job = self.of.template.radamsa.basic(
|
||||||
self.project,
|
self.project,
|
||||||
@ -407,6 +429,10 @@ class TestOnefuzz:
|
|||||||
has_cdb = bool(which("cdb.exe"))
|
has_cdb = bool(which("cdb.exe"))
|
||||||
has_gdb = bool(which("gdb"))
|
has_gdb = bool(which("gdb"))
|
||||||
for job_id in self.successful_jobs:
|
for job_id in self.successful_jobs:
|
||||||
|
if not TARGETS[self.target_jobs[job_id]].test_repro:
|
||||||
|
self.logger.info("skipping repro for %s", self.target_jobs[job_id])
|
||||||
|
continue
|
||||||
|
|
||||||
if self.job_os[job_id] == OS.linux and not has_gdb:
|
if self.job_os[job_id] == OS.linux and not has_gdb:
|
||||||
self.logger.warning(
|
self.logger.warning(
|
||||||
"missing gdb in path, not launching repro: %s",
|
"missing gdb in path, not launching repro: %s",
|
||||||
|
3
src/integration-tests/libfuzzer-aarch64-crosscompile/.gitignore
vendored
Normal file
3
src/integration-tests/libfuzzer-aarch64-crosscompile/.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
compiler-rt
|
||||||
|
fuzz-libs
|
||||||
|
fuzz.exe
|
@ -0,0 +1,31 @@
|
|||||||
|
# Copyright (c) Microsoft Corporation.
|
||||||
|
# Licensed under the MIT License.
|
||||||
|
|
||||||
|
all: check
|
||||||
|
|
||||||
|
|
||||||
|
fuzz-libs:
|
||||||
|
sudo apt update
|
||||||
|
sudo apt install -y qemu-user g++-aarch64-linux-gnu
|
||||||
|
git clone https://github.com/llvm-mirror/compiler-rt
|
||||||
|
|
||||||
|
# last version that supports pc-guard instrumentation, required by GCC
|
||||||
|
(cd compiler-rt; git checkout daa6759576548a2f3825faddaa6811cabbfb45eb)
|
||||||
|
|
||||||
|
# These *must* be built without ASAN
|
||||||
|
mkdir -p fuzz-libs
|
||||||
|
(cd fuzz-libs; aarch64-linux-gnu-g++ -c ../compiler-rt/lib/fuzzer/*.cpp)
|
||||||
|
|
||||||
|
fuzz.exe: fuzz-libs fuzz.c
|
||||||
|
aarch64-linux-gnu-g++ -pthread -lasan -o fuzz.exe fuzz-libs/*.o fuzz.c -fsanitize=address -fsanitize-coverage=trace-pc
|
||||||
|
|
||||||
|
check: fuzz.exe
|
||||||
|
ASAN_OPTIONS=:detect_leaks=0 qemu-aarch64 -L /usr/aarch64-linux-gnu ./fuzz.exe -runs=1
|
||||||
|
|
||||||
|
fuzz: check
|
||||||
|
ASAN_OPTIONS=:detect_leaks=0 qemu-aarch64 -L /usr/aarch64-linux-gnu ./fuzz.exe
|
||||||
|
|
||||||
|
.PHONY: check clean fuzz
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -rf fuzz.exe fuzz-libs compiler-rt
|
64
src/integration-tests/libfuzzer-aarch64-crosscompile/fuzz.c
Normal file
64
src/integration-tests/libfuzzer-aarch64-crosscompile/fuzz.c
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||||
|
// Licensed under the MIT License.
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
|
||||||
|
extern "C" int LLVMFuzzerTestOneInput(const u_int8_t *data, size_t len) {
|
||||||
|
int cnt = 0;
|
||||||
|
|
||||||
|
if (len < 4) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data[0] == 'x') { cnt++; }
|
||||||
|
if (data[1] == 'y') { cnt++; }
|
||||||
|
if (data[2] == 'z') { cnt++; }
|
||||||
|
|
||||||
|
if (cnt >= 3) {
|
||||||
|
switch (data[3]) {
|
||||||
|
case '0': {
|
||||||
|
// segv
|
||||||
|
int *p = NULL; *p = 123;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case '1': {
|
||||||
|
// stack-buffer-underflow
|
||||||
|
int* p = &cnt - 32; for (int i = 0; i < 32; i++) { *(p + i) = 0; }
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case '2': {
|
||||||
|
// stack-buffer-overflow
|
||||||
|
int* p = &cnt + 32; for (int i = 0; i < 32; i++) { *(p - i) = 0; }
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case '3': {
|
||||||
|
// bad-free
|
||||||
|
int *p = &cnt; free(p);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case '4': {
|
||||||
|
// double-free
|
||||||
|
int* p = (int *) malloc(sizeof(int)); free(p); free(p);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case '5': {
|
||||||
|
// heap-use-after-free
|
||||||
|
int* p = (int *) malloc(sizeof(int)); free(p); *p = 123;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case '6': {
|
||||||
|
// heap-buffer-overflow
|
||||||
|
int* p = (int *) malloc(8 * sizeof(int)); for (int i = 0; i < 32; i++) { *(p + i) = 0; }
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case '7': {
|
||||||
|
// fpe
|
||||||
|
int x = 0; int y = 123 / x;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
@ -0,0 +1 @@
|
|||||||
|
hi
|
@ -9,7 +9,7 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t len) {
|
|||||||
int cnt = 0;
|
int cnt = 0;
|
||||||
|
|
||||||
if (len < 4) {
|
if (len < 4) {
|
||||||
return 1;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data[0] == 'x') { cnt++; }
|
if (data[0] == 'x') { cnt++; }
|
||||||
@ -40,17 +40,17 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t len) {
|
|||||||
}
|
}
|
||||||
case '4': {
|
case '4': {
|
||||||
// double-free
|
// double-free
|
||||||
int* p = malloc(sizeof(int)); free(p); free(p);
|
int* p = (int *) malloc(sizeof(int)); free(p); free(p);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case '5': {
|
case '5': {
|
||||||
// heap-use-after-free
|
// heap-use-after-free
|
||||||
int* p = malloc(sizeof(int)); free(p); *p = 123;
|
int* p = (int *) malloc(sizeof(int)); free(p); *p = 123;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case '6': {
|
case '6': {
|
||||||
// heap-buffer-overflow
|
// heap-buffer-overflow
|
||||||
int* p = malloc(8 * sizeof(int)); for (int i = 0; i < 32; i++) { *(p + i) = 0; }
|
int* p = (int *) malloc(8 * sizeof(int)); for (int i = 0; i < 32; i++) { *(p + i) = 0; }
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case '7': {
|
case '7': {
|
||||||
@ -58,6 +58,10 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t len) {
|
|||||||
int x = 0; int y = 123 / x;
|
int x = 0; int y = 123 / x;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case '8': {
|
||||||
|
abort();
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user