Run qemu_systemmode tests with self-hosted runners (#2018)

* run qemu fuzzers (qemu_systemmode only for now) in self-hosted runners

* Remove qemu-related fuzzers to general fuzzers

* fix

* Install dependencies before anything else

* Do not use sudo

* Install sudo

* Revert "Install dependencies before anything else"

This reverts commit 107addad5d9f68dec5a9af50831112cd72c28f4d.

* added qemu specific prerequisites

* add -y flag

* Format with nightly

* Do not use nightly only.
Install fmt and clippy for stable as well.

* Install qemu-img for qemu

* fix qemu-img install

* apt update

* Changed timeout.

* Fix qemu_systemmode test

* fmt

* clippy + decorrelate build and run for qemu_systemmode.

* fix fuzzer

* clippy

* add sqlite3-dev to package prerequisites.

* add arm-none-eabi-gcc

* fix profile dir

* fix condition.

* Run less QEMU stuff faster

---------

Co-authored-by: Toka <tokazerkje@outlook.com>
This commit is contained in:
Romain Malmain 2024-04-22 18:17:22 +02:00 committed by GitHub
parent fe64d10a5c
commit fef6c8d1b1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
23 changed files with 198 additions and 59 deletions

View File

@ -309,7 +309,7 @@ jobs:
- ./fuzzers/backtrace_baby_fuzzers - ./fuzzers/backtrace_baby_fuzzers
- ./fuzzers/fuzzbench_qemu - ./fuzzers/fuzzbench_qemu
- ./fuzzers/nyx_libxml2_parallel - ./fuzzers/nyx_libxml2_parallel
- ./fuzzers/qemu_launcher # - ./fuzzers/qemu_launcher
- ./fuzzers/frida_gdiplus - ./fuzzers/frida_gdiplus
- ./fuzzers/libfuzzer_stb_image_concolic - ./fuzzers/libfuzzer_stb_image_concolic
- ./fuzzers/nautilus_sync - ./fuzzers/nautilus_sync
@ -359,7 +359,32 @@ jobs:
- name: Build and run example fuzzers (Linux) - name: Build and run example fuzzers (Linux)
if: runner.os == 'Linux' if: runner.os == 'Linux'
shell: bash shell: bash
run: RUN_ON_CI=1 LLVM_CONFIG=llvm-config ./scripts/test_all_fuzzers.sh ${{ matrix.fuzzer }} run: RUN_ON_CI=1 LLVM_CONFIG=llvm-config ./scripts/test_fuzzer.sh ${{ matrix.fuzzer }}
fuzzers-qemu:
strategy:
matrix:
os: [ubuntu-latest]
fuzzer:
- ./fuzzers/qemu_cmin
- ./fuzzers/qemu_systemmode
- ./fuzzers/qemu_coverage
- ./fuzzers/qemu_launcher
runs-on: [ self-hosted, qemu ]
container: registry.gitlab.com/qemu-project/qemu/qemu/ubuntu2204:latest
steps:
- uses: actions/checkout@v3
- uses: ./.github/workflows/qemu-fuzzer-tester-prepare
- uses: ./.github/workflows/fuzzer-tester-prepare
- name: Symlink Headers
if: runner.os == 'Linux'
shell: bash
run: sudo ln -s /usr/include/asm-generic /usr/include/asm
- name: Build and run example QEMU fuzzers (Linux)
if: runner.os == 'Linux'
shell: bash
run: RUN_ON_CI=1 LLVM_CONFIG=llvm-config ./scripts/test_fuzzer.sh ${{ matrix.fuzzer }}
nostd-build: nostd-build:
runs-on: ubuntu-latest runs-on: ubuntu-latest
@ -409,8 +434,8 @@ jobs:
windows-frida-libpng: windows-frida-libpng:
runs-on: windows-latest runs-on: windows-latest
needs: needs:
- common - common
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
- uses: ./.github/workflows/windows-tester-prepare - uses: ./.github/workflows/windows-tester-prepare
- name: Build fuzzers/frida_libpng - name: Build fuzzers/frida_libpng
@ -439,7 +464,7 @@ jobs:
windows-tinyinst-simple: windows-tinyinst-simple:
runs-on: windows-latest runs-on: windows-latest
needs: needs:
- common - common
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
- uses: ./.github/workflows/windows-tester-prepare - uses: ./.github/workflows/windows-tester-prepare
@ -451,7 +476,7 @@ jobs:
windows-clippy: windows-clippy:
runs-on: windows-latest runs-on: windows-latest
needs: needs:
- common - common
steps: steps:
- uses: actions-rs/toolchain@v1 - uses: actions-rs/toolchain@v1
with: with:

View File

@ -13,6 +13,9 @@ runs:
with: with:
profile: minimal profile: minimal
toolchain: stable toolchain: stable
- name: Add stable rustfmt and clippy
shell: bash
run: rustup toolchain install stable --component rustfmt --component clippy --allow-downgrade
- name: Add nightly rustfmt and clippy - name: Add nightly rustfmt and clippy
shell: bash shell: bash
run: rustup toolchain install nightly --component rustfmt --component clippy --allow-downgrade run: rustup toolchain install nightly --component rustfmt --component clippy --allow-downgrade
@ -25,7 +28,7 @@ runs:
- name: Remove obsolete llvm (Linux) - name: Remove obsolete llvm (Linux)
if: runner.os == 'Linux' if: runner.os == 'Linux'
shell: bash shell: bash
run: sudo apt purge llvm* clang* run: sudo apt purge -y llvm* clang*
- name: Install LLVM and Clang - name: Install LLVM and Clang
uses: KyleMayes/install-llvm-action@v1 uses: KyleMayes/install-llvm-action@v1
with: with:
@ -33,7 +36,7 @@ runs:
version: 17 version: 17
- name: Install deps - name: Install deps
shell: bash shell: bash
run: sudo apt update && sudo apt install nasm ninja-build gcc-arm-linux-gnueabi g++-arm-linux-gnueabi gcc-aarch64-linux-gnu g++-aarch64-linux-gnu gcc-mipsel-linux-gnu g++-mipsel-linux-gnu gcc-powerpc-linux-gnu g++-powerpc-linux-gnu libc6-dev-i386-cross libc6-dev libc6-dev-i386 lib32gcc-11-dev lib32stdc++-11-dev libgtk-3-dev pax-utils libz3-dev run: sudo apt update && sudo apt install -y nasm ninja-build gcc-arm-linux-gnueabi g++-arm-linux-gnueabi gcc-aarch64-linux-gnu g++-aarch64-linux-gnu gcc-mipsel-linux-gnu g++-mipsel-linux-gnu gcc-powerpc-linux-gnu g++-powerpc-linux-gnu libc6-dev-i386-cross libc6-dev libc6-dev-i386 lib32gcc-11-dev lib32stdc++-11-dev libgtk-3-dev pax-utils libz3-dev
- name: pip install - name: pip install
shell: bash shell: bash
run: python3 -m pip install msgpack jinja2 find_libpython run: python3 -m pip install msgpack jinja2 find_libpython

View File

@ -0,0 +1,8 @@
name: Setup QEMU Fuzzers environment
description: Sets up the QEMU fuzzers environment
runs:
using: composite
steps:
- name: Install sudo
shell: bash
run: apt update && apt install -y sudo wget qemu-utils libsqlite3-dev gcc-arm-none-eabi

View File

@ -140,7 +140,7 @@ RUN cargo build && cargo build --release
# Copy fuzzers over # Copy fuzzers over
COPY fuzzers fuzzers COPY fuzzers fuzzers
# RUN ./scripts/test_all_fuzzers.sh --no-fmt # RUN ./scripts/test_fuzzer.sh --no-fmt
ENTRYPOINT [ "/bin/bash", "-c" ] ENTRYPOINT [ "/bin/bash", "-c" ]
CMD ["/bin/bash"] CMD ["/bin/bash"]

View File

@ -4,7 +4,7 @@
//! is platform independent. Hence, this file contains code for other platforms //! is platform independent. Hence, this file contains code for other platforms
//! but it's only meaningful for Windows because of the `gdiplus` target. If you //! but it's only meaningful for Windows because of the `gdiplus` target. If you
//! going to make it compilable only for Windows, don't forget to modify the //! going to make it compilable only for Windows, don't forget to modify the
//! `scripts/test_all_fuzzers.sh` to opt-out this fuzzer from that test. //! `scripts/test_fuzzer.sh` to opt-out this fuzzer from that test.
use mimalloc::MiMalloc; use mimalloc::MiMalloc;
#[global_allocator] #[global_allocator]

View File

@ -8,6 +8,7 @@ TARGET_DIR = "${CARGO_MAKE_CRATE_TARGET_DIRECTORY}/x86_64"
LIBPNG_ARCH = "x86_64" LIBPNG_ARCH = "x86_64"
LIBPNG_OPTIMIZATIONS = "yes" LIBPNG_OPTIMIZATIONS = "yes"
FEATURE = "x86_64" FEATURE = "x86_64"
LIBAFL_QEMU_CLONE_DIR = "${CARGO_MAKE_CRATE_TARGET_DIRECTORY}/qemu-libafl-bridge"
#LIBAFL_DEBUG_OUTPUT = "1" #LIBAFL_DEBUG_OUTPUT = "1"
#CUSTOM_QEMU_DIR= "~/qemu-libafl-bridge" #CUSTOM_QEMU_DIR= "~/qemu-libafl-bridge"
@ -19,6 +20,7 @@ TARGET_DIR = "${CARGO_MAKE_CRATE_TARGET_DIRECTORY}/arm"
LIBPNG_ARCH = "arm" LIBPNG_ARCH = "arm"
LIBPNG_OPTIMIZATIONS = "yes" LIBPNG_OPTIMIZATIONS = "yes"
FEATURE = "arm" FEATURE = "arm"
LIBAFL_QEMU_CLONE_DIR = "${CARGO_MAKE_CRATE_TARGET_DIRECTORY}/qemu-libafl-bridge"
[env.aarch64] [env.aarch64]
CROSS_CC = "aarch64-linux-gnu-gcc" CROSS_CC = "aarch64-linux-gnu-gcc"
@ -28,6 +30,7 @@ TARGET_DIR = "${CARGO_MAKE_CRATE_TARGET_DIRECTORY}/aarch64"
LIBPNG_ARCH = "aarch64" LIBPNG_ARCH = "aarch64"
LIBPNG_OPTIMIZATIONS = "yes" LIBPNG_OPTIMIZATIONS = "yes"
FEATURE = "aarch64" FEATURE = "aarch64"
LIBAFL_QEMU_CLONE_DIR = "${CARGO_MAKE_CRATE_TARGET_DIRECTORY}/qemu-libafl-bridge"
[env.x86_64] [env.x86_64]
CROSS_CC = "x86_64-linux-gnu-gcc" CROSS_CC = "x86_64-linux-gnu-gcc"
@ -37,6 +40,7 @@ TARGET_DIR = "${CARGO_MAKE_CRATE_TARGET_DIRECTORY}/x86_64"
LIBPNG_ARCH = "x86_64" LIBPNG_ARCH = "x86_64"
LIBPNG_OPTIMIZATIONS = "yes" LIBPNG_OPTIMIZATIONS = "yes"
FEATURE = "x86_64" FEATURE = "x86_64"
LIBAFL_QEMU_CLONE_DIR = "${CARGO_MAKE_CRATE_TARGET_DIRECTORY}/qemu-libafl-bridge"
[env.i386] [env.i386]
CROSS_CC = "x86_64-linux-gnu-gcc" CROSS_CC = "x86_64-linux-gnu-gcc"
@ -46,6 +50,7 @@ TARGET_DIR = "${CARGO_MAKE_CRATE_TARGET_DIRECTORY}/i386"
LIBPNG_ARCH = "i386" LIBPNG_ARCH = "i386"
LIBPNG_OPTIMIZATIONS = "yes" LIBPNG_OPTIMIZATIONS = "yes"
FEATURE = "i386" FEATURE = "i386"
LIBAFL_QEMU_CLONE_DIR = "${CARGO_MAKE_CRATE_TARGET_DIRECTORY}/qemu-libafl-bridge"
[env.mips] [env.mips]
CROSS_CC = "mipsel-linux-gnu-gcc" CROSS_CC = "mipsel-linux-gnu-gcc"
@ -55,6 +60,7 @@ TARGET_DIR = "${CARGO_MAKE_CRATE_TARGET_DIRECTORY}/mips"
LIBPNG_ARCH = "mips" LIBPNG_ARCH = "mips"
LIBPNG_OPTIMIZATIONS = "yes" LIBPNG_OPTIMIZATIONS = "yes"
FEATURE = "mips" FEATURE = "mips"
LIBAFL_QEMU_CLONE_DIR = "${CARGO_MAKE_CRATE_TARGET_DIRECTORY}/qemu-libafl-bridge"
[env.ppc] [env.ppc]
CROSS_CC = "powerpc-linux-gnu-gcc" CROSS_CC = "powerpc-linux-gnu-gcc"
@ -64,6 +70,7 @@ TARGET_DIR = "${CARGO_MAKE_CRATE_TARGET_DIRECTORY}/ppc"
LIBPNG_ARCH = "ppc" LIBPNG_ARCH = "ppc"
LIBPNG_OPTIMIZATIONS = "no" LIBPNG_OPTIMIZATIONS = "no"
FEATURE = "ppc" FEATURE = "ppc"
LIBAFL_QEMU_CLONE_DIR = "${CARGO_MAKE_CRATE_TARGET_DIRECTORY}/qemu-libafl-bridge"
[tasks.unsupported] [tasks.unsupported]
script_runner="@shell" script_runner="@shell"
@ -242,6 +249,20 @@ mac_alias = "unsupported"
windows_alias = "unsupported" windows_alias = "unsupported"
[tasks.test_unix] [tasks.test_unix]
dependencies = [ "lightweight" ]
# Tidy up after we've run our tests so we don't hog all the disk space
command = "cargo"
args = [
"make",
"clean",
]
[tasks.test_full]
linux_alias = "test_unix_full"
mac_alias = "unsupported"
windows_alias = "unsupported"
[tasks.test_unix_full]
dependencies = [ "all" ] dependencies = [ "all" ]
# Tidy up after we've run our tests so we don't hog all the disk space # Tidy up after we've run our tests so we don't hog all the disk space
command = "cargo" command = "cargo"
@ -321,3 +342,9 @@ dependencies = [
"mips", "mips",
"ppc" "ppc"
] ]
[tasks.lightweight]
dependencies = [
"arm",
"x86_64",
]

View File

@ -8,6 +8,7 @@ TARGET_DIR = "${CARGO_MAKE_CRATE_TARGET_DIRECTORY}/x86_64"
LIBPNG_ARCH = "x86_64" LIBPNG_ARCH = "x86_64"
LIBPNG_OPTIMIZATIONS = "yes" LIBPNG_OPTIMIZATIONS = "yes"
FEATURE = "x86_64" FEATURE = "x86_64"
LIBAFL_QEMU_CLONE_DIR = "${CARGO_MAKE_CRATE_TARGET_DIRECTORY}/qemu-libafl-bridge"
#LIBAFL_DEBUG_OUTPUT = "1" #LIBAFL_DEBUG_OUTPUT = "1"
#CUSTOM_QEMU_DIR= "~/qemu-libafl-bridge" #CUSTOM_QEMU_DIR= "~/qemu-libafl-bridge"
@ -19,6 +20,7 @@ TARGET_DIR = "${CARGO_MAKE_CRATE_TARGET_DIRECTORY}/arm"
LIBPNG_ARCH = "arm" LIBPNG_ARCH = "arm"
LIBPNG_OPTIMIZATIONS = "yes" LIBPNG_OPTIMIZATIONS = "yes"
FEATURE = "arm" FEATURE = "arm"
LIBAFL_QEMU_CLONE_DIR = "${CARGO_MAKE_CRATE_TARGET_DIRECTORY}/qemu-libafl-bridge"
[env.aarch64] [env.aarch64]
CROSS_CC = "aarch64-linux-gnu-gcc" CROSS_CC = "aarch64-linux-gnu-gcc"
@ -28,6 +30,7 @@ TARGET_DIR = "${CARGO_MAKE_CRATE_TARGET_DIRECTORY}/aarch64"
LIBPNG_ARCH = "aarch64" LIBPNG_ARCH = "aarch64"
LIBPNG_OPTIMIZATIONS = "yes" LIBPNG_OPTIMIZATIONS = "yes"
FEATURE = "aarch64" FEATURE = "aarch64"
LIBAFL_QEMU_CLONE_DIR = "${CARGO_MAKE_CRATE_TARGET_DIRECTORY}/qemu-libafl-bridge"
[env.x86_64] [env.x86_64]
CROSS_CC = "x86_64-linux-gnu-gcc" CROSS_CC = "x86_64-linux-gnu-gcc"
@ -37,6 +40,7 @@ TARGET_DIR = "${CARGO_MAKE_CRATE_TARGET_DIRECTORY}/x86_64"
LIBPNG_ARCH = "x86_64" LIBPNG_ARCH = "x86_64"
LIBPNG_OPTIMIZATIONS = "yes" LIBPNG_OPTIMIZATIONS = "yes"
FEATURE = "x86_64" FEATURE = "x86_64"
LIBAFL_QEMU_CLONE_DIR = "${CARGO_MAKE_CRATE_TARGET_DIRECTORY}/qemu-libafl-bridge"
[env.i386] [env.i386]
CROSS_CC = "x86_64-linux-gnu-gcc" CROSS_CC = "x86_64-linux-gnu-gcc"
@ -46,6 +50,7 @@ TARGET_DIR = "${CARGO_MAKE_CRATE_TARGET_DIRECTORY}/i386"
LIBPNG_ARCH = "i386" LIBPNG_ARCH = "i386"
LIBPNG_OPTIMIZATIONS = "yes" LIBPNG_OPTIMIZATIONS = "yes"
FEATURE = "i386" FEATURE = "i386"
LIBAFL_QEMU_CLONE_DIR = "${CARGO_MAKE_CRATE_TARGET_DIRECTORY}/qemu-libafl-bridge"
[env.mips] [env.mips]
CROSS_CC = "mipsel-linux-gnu-gcc" CROSS_CC = "mipsel-linux-gnu-gcc"
@ -55,6 +60,7 @@ TARGET_DIR = "${CARGO_MAKE_CRATE_TARGET_DIRECTORY}/mips"
LIBPNG_ARCH = "mips" LIBPNG_ARCH = "mips"
LIBPNG_OPTIMIZATIONS = "yes" LIBPNG_OPTIMIZATIONS = "yes"
FEATURE = "mips" FEATURE = "mips"
LIBAFL_QEMU_CLONE_DIR = "${CARGO_MAKE_CRATE_TARGET_DIRECTORY}/qemu-libafl-bridge"
[env.ppc] [env.ppc]
CROSS_CC = "powerpc-linux-gnu-gcc" CROSS_CC = "powerpc-linux-gnu-gcc"
@ -64,6 +70,7 @@ TARGET_DIR = "${CARGO_MAKE_CRATE_TARGET_DIRECTORY}/ppc"
LIBPNG_ARCH = "ppc" LIBPNG_ARCH = "ppc"
LIBPNG_OPTIMIZATIONS = "no" LIBPNG_OPTIMIZATIONS = "no"
FEATURE = "ppc" FEATURE = "ppc"
LIBAFL_QEMU_CLONE_DIR = "${CARGO_MAKE_CRATE_TARGET_DIRECTORY}/qemu-libafl-bridge"
[tasks.unsupported] [tasks.unsupported]
script_runner="@shell" script_runner="@shell"
@ -241,6 +248,20 @@ mac_alias = "unsupported"
windows_alias = "unsupported" windows_alias = "unsupported"
[tasks.test_unix] [tasks.test_unix]
dependencies = [ "lightweight" ]
# Tidy up after we've run our tests so we don't hog all the disk space
command = "cargo"
args = [
"make",
"clean",
]
[tasks.test_full]
linux_alias = "test_unix_full"
mac_alias = "unsupported"
windows_alias = "unsupported"
[tasks.test_unix_full]
dependencies = [ "all" ] dependencies = [ "all" ]
# Tidy up after we've run our tests so we don't hog all the disk space # Tidy up after we've run our tests so we don't hog all the disk space
command = "cargo" command = "cargo"
@ -320,3 +341,9 @@ dependencies = [
"mips", "mips",
"ppc" "ppc"
] ]
[tasks.lightweight]
dependencies = [
"arm",
"x86_64",
]

View File

@ -137,7 +137,7 @@ pub fn fuzz() {
"Mapping: 0x{:016x}-0x{:016x}, {}", "Mapping: 0x{:016x}-0x{:016x}, {}",
m.start(), m.start(),
m.end(), m.end(),
m.path().unwrap_or("<EMPTY>") m.path().unwrap_or(&"<EMPTY>".to_string())
); );
} }
@ -255,7 +255,7 @@ pub fn fuzz() {
tuple_list!(QemuDrCovHelper::new( tuple_list!(QemuDrCovHelper::new(
QemuInstrumentationAddressRangeFilter::None, QemuInstrumentationAddressRangeFilter::None,
rangemap, rangemap,
PathBuf::from(coverage), coverage,
false, false,
)), )),
); );

View File

@ -26,7 +26,8 @@ sudo apt install \
gcc-mipsel-linux-gnu \ gcc-mipsel-linux-gnu \
g++-mipsel-linux-gnu \ g++-mipsel-linux-gnu \
gcc-powerpc-linux-gnu \ gcc-powerpc-linux-gnu \
g++-powerpc-linux-gnu g++-powerpc-linux-gnu \
libsqlite3-dev
``` ```
## Run ## Run

View File

@ -1,6 +1,31 @@
env_scripts = [
'''
#!@duckscript
profile = get_env PROFILE
if eq ${profile} "dev"
set_env PROFILE_DIR debug
else
set_env PROFILE_DIR ${profile}
end
''',
'''
#!@duckscript
runs_on_ci = get_env RUN_ON_CI
if ${runs_on_ci}
cargo_target_dir = get_env CARGO_MAKE_CRATE_TARGET_DIRECTORY
set_env TARGET_DIR ${cargo_target_dir}
set_env KERNEL ${cargo_target_dir}/example.elf
end
'''
]
[env] [env]
PROFILE = { value = "release", condition = {env_not_set = ["PROFILE"]} } PROFILE = { value = "release", condition = { env_not_set = ["PROFILE"] } }
TARGET_DIR = "${CARGO_MAKE_CRATE_TARGET_DIRECTORY}/${FEATURE}" TARGET_DIR = "${CARGO_MAKE_CRATE_TARGET_DIRECTORY}/${FEATURE}"
LIBAFL_QEMU_CLONE_DIR = "${CARGO_MAKE_CRATE_TARGET_DIRECTORY}/qemu-libafl-bridge"
KERNEL = "${TARGET_DIR}/example.elf"
[tasks.target_dir] [tasks.target_dir]
condition = { files_not_exist = [ "${TARGET_DIR}" ] } condition = { files_not_exist = [ "${TARGET_DIR}" ] }
@ -18,7 +43,7 @@ qemu-img create -f qcow2 ${TARGET_DIR}/dummy.qcow2 32M
''' '''
[tasks.target] [tasks.target]
dependencies = ["target_dir", "build_fuzzer"] dependencies = ["target_dir"]
condition = { env_set = [ "TARGET_DEFINE" ] } condition = { env_set = [ "TARGET_DEFINE" ] }
command = "arm-none-eabi-gcc" command = "arm-none-eabi-gcc"
args = [ args = [
@ -31,7 +56,7 @@ args = [
"${CARGO_MAKE_WORKING_DIRECTORY}/example/main.c", "${CARGO_MAKE_WORKING_DIRECTORY}/example/main.c",
"${CARGO_MAKE_WORKING_DIRECTORY}/example/startup.c", "${CARGO_MAKE_WORKING_DIRECTORY}/example/startup.c",
"-D", "${TARGET_DEFINE}", "-D", "${TARGET_DEFINE}",
"-I", "${TARGET_DIR}/${PROFILE}/include", "-I", "${TARGET_DIR}/${PROFILE_DIR}/include",
"-o", "${TARGET_DIR}/example.elf", "-o", "${TARGET_DIR}/example.elf",
] ]
@ -49,8 +74,7 @@ args = [
dependencies = ["image"] dependencies = ["image"]
[tasks.run_fuzzer] [tasks.run_fuzzer]
env = { "KERNEL" = "${TARGET_DIR}/example.elf" } command = "${TARGET_DIR}/${PROFILE_DIR}/qemu_systemmode"
command = "${TARGET_DIR}/${PROFILE}/qemu_systemmode"
args = [ args = [
"-icount", "shift=auto,align=off,sleep=off", "-icount", "shift=auto,align=off,sleep=off",
"-machine", "mps2-an385", "-machine", "mps2-an385",
@ -62,7 +86,7 @@ args = [
"-drive", "if=none,format=qcow2,file=${TARGET_DIR}/dummy.qcow2", "-drive", "if=none,format=qcow2,file=${TARGET_DIR}/dummy.qcow2",
"-S", "-S",
] ]
dependencies = ["build_fuzzer", "target"] dependencies = ["target"]
[tasks.test_fuzzer] [tasks.test_fuzzer]
condition = { env_set = [ "FEATURE" ] } condition = { env_set = [ "FEATURE" ] }
@ -74,10 +98,10 @@ cargo make build_$FEATURE
timeout 15s cargo make ${FEATURE} | tee $TMP_DIR/fuzz.log 2>&1 || true timeout 15s cargo make ${FEATURE} | tee $TMP_DIR/fuzz.log 2>&1 || true
if [ -z "$(grep 'Objective' $TMP_DIR/fuzz.log)" ]; then if [ -z "$(grep 'Objective' $TMP_DIR/fuzz.log)" ]; then
echo "Fuzzer did not find the objective in $TMP_DIR/fuzz.log" echo "qemu_systemmode ${FEATURE}: Fuzzer did not find the objective in $TMP_DIR/fuzz.log"
exit 1 exit 1
else else
echo "Objective found." echo "qemu_systemmode ${FEATURE}: Objective found."
fi fi
''' '''
@ -161,8 +185,7 @@ args = [
[tasks.test] [tasks.test]
clear = true clear = true
run_task = { name = ["build"] } run_task = { name = ["test_classic", "test_breakpoint", "test_sync_exit"] }
# run_task = { name = ["test_classic", "test_breakpoint", "test_sync_exit"] }
[tasks.build] [tasks.build]
clear = true clear = true

View File

@ -8,6 +8,22 @@ It comes in three flavours (can be set through features):
-`breakpoint`: Interaction with QEMU using the command system, leveraging breakpoints. -`breakpoint`: Interaction with QEMU using the command system, leveraging breakpoints.
-`sync_exit`: Interaction with QEMU using the command system, leveraging sync exits. -`sync_exit`: Interaction with QEMU using the command system, leveraging sync exits.
## Prerequisite
You will need to have `qemu-img` and `arm-none-eabi-gcc` installed.
On Ubuntu and Debian, you will need to run
```bash
sudo apt update
sudo apt -y install qemu-utils gcc-arm-none-eabi
```
## Build
```bash
cargo make build
```
## Run ## Run
```bash ```bash
@ -25,5 +41,5 @@ With feature being `classic`, `breakpoint` or `sync_exit`.
This will build the desired fuzzer (src/fuzzer_<feature>.rs) and a small example binary based on FreeRTOS, which can run under a qemu emulation target. This will build the desired fuzzer (src/fuzzer_<feature>.rs) and a small example binary based on FreeRTOS, which can run under a qemu emulation target.
Since the instrumentation is based on snapshots, QEMU needs a virtual drive (even if it is unused...). Since the instrumentation is based on snapshots, QEMU needs a virtual drive (even if it is unused...).
Thus, the makefile creates a dummy QCOW2 image `dummy.qcow2` (can be found in the `target directory`). Thus, the makefile creates a dummy QCOW2 image `dummy.qcow2` (can be found in the `target directory`).
Currently the ``KERNEL`` variable is needed because the fuzzer does not parse QEMUs arguments to find the binary. Currently, the ``KERNEL`` variable is needed because the fuzzer does not parse QEMUs arguments to find the binary.
It is automatically set in the build script. It is automatically set in the build script.

View File

@ -8,7 +8,7 @@ int __attribute__((noinline)) BREAKPOINT() {
int LLVMFuzzerTestOneInput(unsigned int *Data, unsigned int Size) { int LLVMFuzzerTestOneInput(unsigned int *Data, unsigned int Size) {
#ifdef TARGET_SYNC_EXIT #ifdef TARGET_SYNC_EXIT
LIBAFL_EXIT_START_PHYS((unsigned int)Data, Size); LIBAFL_QEMU_START_PHYS((unsigned int)Data, Size);
#endif #endif
if (Data[3] == 0) { if (Data[3] == 0) {
while (1) {} while (1) {}
@ -27,7 +27,7 @@ int LLVMFuzzerTestOneInput(unsigned int *Data, unsigned int Size) {
} }
} }
#ifdef TARGET_SYNC_EXIT #ifdef TARGET_SYNC_EXIT
LIBAFL_EXIT_END(LIBAFL_EXIT_END_OK); LIBAFL_QEMU_END(LIBAFL_QEMU_END_OK);
#endif #endif
return BREAKPOINT(); return BREAKPOINT();
} }

View File

@ -41,8 +41,6 @@
//! //!
//! ... making for a total of 5 bytes. //! ... making for a total of 5 bytes.
#![cfg(feature = "std")]
use std::{ use std::{
fmt::{self, Debug, Formatter}, fmt::{self, Debug, Formatter},
io::{self, Cursor, Read, Seek, SeekFrom, Write}, io::{self, Cursor, Read, Seek, SeekFrom, Write},

View File

@ -263,7 +263,7 @@ use crate::Error;
extern "C" { extern "C" {
/// The `libc` `getcontext` /// The `libc` `getcontext`
/// For some reason, it's not available on MacOS. /// For some reason, it's not available on `MacOS`.
/// ///
fn getcontext(ucp: *mut ucontext_t) -> c_int; fn getcontext(ucp: *mut ucontext_t) -> c_int;
} }

View File

@ -34,7 +34,6 @@
unused_import_braces, unused_import_braces,
unused_qualifications, unused_qualifications,
unused_must_use, unused_must_use,
missing_docs,
//unused_results //unused_results
))] ))]
#![cfg_attr( #![cfg_attr(

View File

@ -35,7 +35,6 @@
unused_import_braces, unused_import_braces,
unused_qualifications, unused_qualifications,
unused_must_use, unused_must_use,
missing_docs,
//unused_results //unused_results
))] ))]
#![cfg_attr( #![cfg_attr(

View File

@ -41,7 +41,6 @@ Additional documentation is available in [the `LibAFL` book](https://aflplus.plu
unused_import_braces, unused_import_braces,
unused_qualifications, unused_qualifications,
unused_must_use, unused_must_use,
missing_docs,
//unused_results //unused_results
))] ))]
#![cfg_attr( #![cfg_attr(

View File

@ -266,10 +266,13 @@ pub fn build(
} }
let libafl_qemu_dir = env::var_os("LIBAFL_QEMU_DIR").map(|x| x.to_string_lossy().to_string()); let libafl_qemu_dir = env::var_os("LIBAFL_QEMU_DIR").map(|x| x.to_string_lossy().to_string());
let libafl_qemu_clone_dir =
env::var_os("LIBAFL_QEMU_CLONE_DIR").map(|x| x.to_string_lossy().to_string());
let libafl_qemu_force_configure = env::var("LIBAFL_QEMU_FORCE_CONFIGURE").is_ok(); let libafl_qemu_force_configure = env::var("LIBAFL_QEMU_FORCE_CONFIGURE").is_ok();
let libafl_qemu_no_build = env::var("LIBAFL_QEMU_NO_BUILD").is_ok(); let libafl_qemu_no_build = env::var("LIBAFL_QEMU_NO_BUILD").is_ok();
println!("cargo:rerun-if-env-changed=LIBAFL_QEMU_DIR"); println!("cargo:rerun-if-env-changed=LIBAFL_QEMU_DIR");
println!("cargo:rerun-if-env-changed=LIBAFL_QEMU_CLONE_DIR");
println!("cargo:rerun-if-env-changed=LIBAFL_QEMU_FORCE_CONFIGURE"); println!("cargo:rerun-if-env-changed=LIBAFL_QEMU_FORCE_CONFIGURE");
println!("cargo:rerun-if-env-changed=LIBAFL_QEMU_NO_BUILD"); println!("cargo:rerun-if-env-changed=LIBAFL_QEMU_NO_BUILD");
@ -286,10 +289,18 @@ pub fn build(
let cc_compiler = cc::Build::new().cpp(false).get_compiler(); let cc_compiler = cc::Build::new().cpp(false).get_compiler();
let cpp_compiler = cc::Build::new().cpp(true).get_compiler(); let cpp_compiler = cc::Build::new().cpp(true).get_compiler();
let qemu_path = if let Some(qemu_dir) = libafl_qemu_dir.as_ref() { let libafl_qemu_dir = if let Some(qemu_dir) = libafl_qemu_dir.as_ref() {
if libafl_qemu_clone_dir.is_some() {
println!("cargo:warning=LIBAFL_QEMU_DIR and LIBAFL_QEMU_CLONE_DIR are both set. LIBAFL_QEMU_DIR will be considered in priority");
}
Path::new(&qemu_dir).to_path_buf() Path::new(&qemu_dir).to_path_buf()
} else { } else {
let qemu_path = target_dir.join(QEMU_DIRNAME); let qemu_path = if let Some(clone_dir) = &libafl_qemu_clone_dir {
PathBuf::from(clone_dir)
} else {
target_dir.join(QEMU_DIRNAME)
};
let qemu_rev = target_dir.join("QEMU_REVISION"); let qemu_rev = target_dir.join("QEMU_REVISION");
if qemu_rev.exists() if qemu_rev.exists()
@ -339,8 +350,8 @@ pub fn build(
qemu_path qemu_path
}; };
let qemu_build_dir = qemu_path.join("build"); let libafl_qemu_build_dir = libafl_qemu_dir.join("build");
let config_signature_path = qemu_build_dir.join("libafl_config"); let config_signature_path = libafl_qemu_build_dir.join("libafl_config");
let target_suffix = if is_usermode { let target_suffix = if is_usermode {
"linux-user".to_string() "linux-user".to_string()
@ -350,12 +361,12 @@ pub fn build(
let (output_lib, output_lib_link) = if is_usermode { let (output_lib, output_lib_link) = if is_usermode {
( (
qemu_build_dir.join(format!("libqemu-{cpu_target}.so")), libafl_qemu_build_dir.join(format!("libqemu-{cpu_target}.so")),
format!("qemu-{cpu_target}"), format!("qemu-{cpu_target}"),
) )
} else { } else {
( (
qemu_build_dir.join(format!("libqemu-system-{cpu_target}.so")), libafl_qemu_build_dir.join(format!("libqemu-system-{cpu_target}.so")),
format!("qemu-system-{cpu_target}"), format!("qemu-system-{cpu_target}"),
) )
}; };
@ -365,8 +376,8 @@ pub fn build(
let mut config_cmd = configure_qemu( let mut config_cmd = configure_qemu(
&cc_compiler, &cc_compiler,
&cpp_compiler, &cpp_compiler,
&qemu_path, &libafl_qemu_dir,
&qemu_build_dir, &libafl_qemu_build_dir,
is_usermode, is_usermode,
&cpu_target, &cpu_target,
&target_suffix, &target_suffix,
@ -405,7 +416,7 @@ pub fn build(
// Always build by default, make will detect if it is necessary to rebuild qemu // Always build by default, make will detect if it is necessary to rebuild qemu
if !libafl_qemu_no_build { if !libafl_qemu_no_build {
let mut build_cmd = build_qemu(&cc_compiler, &cpp_compiler, &qemu_build_dir, jobs); let mut build_cmd = build_qemu(&cc_compiler, &cpp_compiler, &libafl_qemu_build_dir, jobs);
assert!( assert!(
build_cmd.status().expect("Invoking Make Failed").success(), build_cmd.status().expect("Invoking Make Failed").success(),
@ -439,13 +450,16 @@ pub fn build(
*/ */
if cfg!(feature = "shared") { if cfg!(feature = "shared") {
let qemu_build_dir_str = qemu_build_dir.to_str().expect("Could not convert to str"); let qemu_build_dir_str = libafl_qemu_build_dir
.to_str()
.expect("Could not convert to str");
println!("cargo:rustc-link-search=native={qemu_build_dir_str}"); println!("cargo:rustc-link-search=native={qemu_build_dir_str}");
println!("cargo:rustc-link-lib=dylib={output_lib_link}"); println!("cargo:rustc-link-lib=dylib={output_lib_link}");
cargo_add_rpath(qemu_build_dir_str); cargo_add_rpath(qemu_build_dir_str);
} else { } else {
let compile_commands_string = &fs::read_to_string(qemu_build_dir.join("linkinfo.json")) let compile_commands_string =
.expect("Failed to read linkinfo.json"); &fs::read_to_string(libafl_qemu_build_dir.join("linkinfo.json"))
.expect("Failed to read linkinfo.json");
let linkinfo = json::parse(compile_commands_string).expect("Failed to parse linkinfo.json"); let linkinfo = json::parse(compile_commands_string).expect("Failed to parse linkinfo.json");
@ -460,7 +474,7 @@ pub fn build(
let mut link_command = cpp_compiler.to_command(); let mut link_command = cpp_compiler.to_command();
link_command link_command
.current_dir(&qemu_build_dir) .current_dir(&libafl_qemu_build_dir)
.arg("-o") .arg("-o")
.arg("libqemu-partially-linked.o") .arg("libqemu-partially-linked.o")
.arg("-r") .arg("-r")
@ -471,10 +485,11 @@ pub fn build(
let output = link_command.output().expect("Partial linked failure"); let output = link_command.output().expect("Partial linked failure");
if !output.status.success() { if !output.status.success() {
fs::write(qemu_build_dir.join("link.command"), link_str).expect("Link command failed."); fs::write(libafl_qemu_build_dir.join("link.command"), link_str)
fs::write(qemu_build_dir.join("link.stdout"), &output.stdout) .expect("Link command failed.");
fs::write(libafl_qemu_build_dir.join("link.stdout"), &output.stdout)
.expect("Link stdout failed."); .expect("Link stdout failed.");
fs::write(qemu_build_dir.join("link.stderr"), &output.stderr) fs::write(libafl_qemu_build_dir.join("link.stderr"), &output.stderr)
.expect("Link stderr failed."); .expect("Link stderr failed.");
panic!("Linking failed."); panic!("Linking failed.");
} }
@ -567,7 +582,7 @@ pub fn build(
.current_dir(out_dir_path) .current_dir(out_dir_path)
.arg("crs") .arg("crs")
.arg("libqemu-partially-linked.a") .arg("libqemu-partially-linked.a")
.arg(qemu_build_dir.join("libqemu-partially-linked.o")) .arg(libafl_qemu_build_dir.join("libqemu-partially-linked.o"))
.status() .status()
.expect("Ar creation"); .expect("Ar creation");
@ -595,7 +610,7 @@ pub fn build(
.to_string() .to_string()
.replace( .replace(
"$ORIGIN", "$ORIGIN",
qemu_build_dir libafl_qemu_build_dir
.as_os_str() .as_os_str()
.to_str() .to_str()
.expect("Could not convert OsStr to str"), .expect("Could not convert OsStr to str"),
@ -623,7 +638,7 @@ pub fn build(
//} //}
fs::create_dir_all(target_dir.join("pc-bios")).unwrap(); fs::create_dir_all(target_dir.join("pc-bios")).unwrap();
for path in fs::read_dir(qemu_build_dir.join("pc-bios")).unwrap() { for path in fs::read_dir(libafl_qemu_build_dir.join("pc-bios")).unwrap() {
let path = path.unwrap().path(); let path = path.unwrap().path();
if path.is_file() { if path.is_file() {
if let Some(name) = path.file_name() { if let Some(name) = path.file_name() {
@ -635,7 +650,7 @@ pub fn build(
} }
BuildResult { BuildResult {
qemu_path, qemu_path: libafl_qemu_dir,
build_dir: qemu_build_dir, build_dir: libafl_qemu_build_dir,
} }
} }

View File

@ -117,7 +117,7 @@ macro_rules! create_wrapper {
{ {
unsafe { unsafe {
let hooks = get_qemu_hooks::<QT, S>(); let hooks = get_qemu_hooks::<QT, S>();
let func: fn(&mut QemuHooks<QT, S>, Option<&mut S>, $($param_type),*) = transmute(hook as *mut c_void); let func: fn(&mut QemuHooks<QT, S>, Option<&mut S>, $($param_type),*) = transmute(ptr::from_mut::<c_void>(hook));
func(hooks, inprocess_get_state::<S>(), $($param),*); func(hooks, inprocess_get_state::<S>(), $($param),*);
} }
} }
@ -144,7 +144,7 @@ macro_rules! create_wrapper {
{ {
unsafe { unsafe {
let hooks = get_qemu_hooks::<QT, S>(); let hooks = get_qemu_hooks::<QT, S>();
let func: fn(&mut QemuHooks<QT, S>, Option<&mut S>, $($param_type),*) -> $ret_type= transmute(hook as *mut c_void); let func: fn(&mut QemuHooks<QT, S>, Option<&mut S>, $($param_type),*) -> $ret_type= transmute(ptr::from_mut::<c_void>(hook));
func(hooks, inprocess_get_state::<S>(), $($param),*) func(hooks, inprocess_get_state::<S>(), $($param),*)
} }
} }

View File

@ -34,7 +34,6 @@
unused_import_braces, unused_import_braces,
unused_qualifications, unused_qualifications,
unused_must_use, unused_must_use,
missing_docs,
//unused_results //unused_results
))] ))]
#![cfg_attr( #![cfg_attr(

View File

@ -35,7 +35,6 @@
unused_import_braces, unused_import_braces,
unused_qualifications, unused_qualifications,
unused_must_use, unused_must_use,
missing_docs,
//unused_results //unused_results
))] ))]
#![cfg_attr( #![cfg_attr(

View File

@ -39,7 +39,6 @@ The tinyinst module for `LibAFL`.
unused_import_braces, unused_import_braces,
unused_qualifications, unused_qualifications,
unused_must_use, unused_must_use,
missing_docs,
//unused_results //unused_results
))] ))]
#![cfg_attr( #![cfg_attr(

View File

@ -6,7 +6,9 @@ cd "$SCRIPT_DIR/.." || exit 1
if [[ -z "${RUN_ON_CI}" ]]; then if [[ -z "${RUN_ON_CI}" ]]; then
fuzzer_to_test="$1" fuzzers=$(find ./fuzzers -mindepth 1 -maxdepth 1 -type d)
backtrace_fuzzers=$(find ./fuzzers/backtrace_baby_fuzzers -mindepth 1 -maxdepth 1 -type d)
fuzzer_to_test="$fuzzers $backtrace_fuzzers"
else else
fuzzer_to_test="$1" fuzzer_to_test="$1"
export PROFILE=dev export PROFILE=dev