diff options
209 files changed, 6263 insertions, 2439 deletions
diff --git a/.custom-format.py b/.custom-format.py index 428d7b0d..1295ce55 100755 --- a/.custom-format.py +++ b/.custom-format.py @@ -3,10 +3,10 @@ # american fuzzy lop++ - custom code formatter # -------------------------------------------- # -# Written and maintaned by Andrea Fioraldi <andreafioraldi@gmail.com> +# Written and maintained by Andrea Fioraldi <andreafioraldi@gmail.com> # # Copyright 2015, 2016, 2017 Google Inc. All rights reserved. -# Copyright 2019-2022 AFLplusplus Project. All rights reserved. +# Copyright 2019-2023 AFLplusplus Project. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -18,24 +18,57 @@ import subprocess import sys import os -import re +# import re # TODO: for future use import shutil +import importlib.metadata + +# string_re = re.compile('(\\"(\\\\.|[^"\\\\])*\\")') # TODO: for future use + +CURRENT_LLVM = os.getenv('LLVM_VERSION', 14) +CLANG_FORMAT_BIN = os.getenv("CLANG_FORMAT_BIN", "") + + +def check_clang_format_pip_version(): + """ + Check if the correct version of clang-format is installed via pip. + + Returns: + bool: True if the correct version of clang-format is installed, + False otherwise. + """ + # Check if clang-format is installed + if importlib.util.find_spec('clang_format'): + # Check if the installed version is the expected LLVM version + if importlib.metadata.version('clang-format')\ + .startswith(str(CURRENT_LLVM)+'.'): + return True + else: + # Return False, because the clang-format version does not match + return False + else: + # If the 'clang_format' package isn't installed, return False + return False -# string_re = re.compile('(\\"(\\\\.|[^"\\\\])*\\")') # future use with open(".clang-format") as f: fmt = f.read() -CURRENT_LLVM = os.getenv('LLVM_VERSION', 14) -CLANG_FORMAT_BIN = os.getenv("CLANG_FORMAT_BIN", "") + +CLANG_FORMAT_PIP = check_clang_format_pip_version() if shutil.which(CLANG_FORMAT_BIN) is None: CLANG_FORMAT_BIN = f"clang-format-{CURRENT_LLVM}" -if shutil.which(CLANG_FORMAT_BIN) is None: +if shutil.which(CLANG_FORMAT_BIN) is None \ + and CLANG_FORMAT_PIP is False: print(f"[!] clang-format-{CURRENT_LLVM} is needed. Aborted.") + print(f"Run `pip3 install \"clang-format=={CURRENT_LLVM}.*\"` \ +to install via pip.") exit(1) +if CLANG_FORMAT_PIP: + CLANG_FORMAT_BIN = shutil.which("clang-format") + COLUMN_LIMIT = 80 for line in fmt.split("\n"): line = line.split(":") @@ -54,43 +87,43 @@ def custom_format(filename): for line in src.split("\n"): if line.lstrip().startswith("#"): - if line[line.find("#") + 1 :].lstrip().startswith("define"): + if line[line.find("#") + 1:].lstrip().startswith("define"): in_define = True if ( - "/*" in line - and not line.strip().startswith("/*") - and line.endswith("*/") - and len(line) < (COLUMN_LIMIT - 2) + "/*" in line + and not line.strip().startswith("/*") + and line.endswith("*/") + and len(line) < (COLUMN_LIMIT - 2) ): cmt_start = line.rfind("/*") line = ( - line[:cmt_start] - + " " * (COLUMN_LIMIT - 2 - len(line)) - + line[cmt_start:] + line[:cmt_start] + + " " * (COLUMN_LIMIT - 2 - len(line)) + + line[cmt_start:] ) define_padding = 0 if last_line is not None and in_define and last_line.endswith("\\"): last_line = last_line[:-1] - define_padding = max(0, len(last_line[last_line.rfind("\n") + 1 :])) + define_padding = max(0, len(last_line[last_line.rfind("\n") + 1:])) if ( - last_line is not None - and last_line.strip().endswith("{") - and line.strip() != "" + last_line is not None + and last_line.strip().endswith("{") + and line.strip() != "" ): line = (" " * define_padding + "\\" if in_define else "") + "\n" + line elif ( - last_line is not None - and last_line.strip().startswith("}") - and line.strip() != "" + last_line is not None + and last_line.strip().startswith("}") + and line.strip() != "" ): line = (" " * define_padding + "\\" if in_define else "") + "\n" + line elif ( - line.strip().startswith("}") - and last_line is not None - and last_line.strip() != "" + line.strip().startswith("}") + and last_line is not None + and last_line.strip() != "" ): line = (" " * define_padding + "\\" if in_define else "") + "\n" + line diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 04cbaca8..ed1f3228 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -23,15 +23,17 @@ jobs: - name: debug run: apt-cache search plugin-dev | grep gcc-; echo; apt-cache search clang-format- | grep clang-format- - name: update - run: sudo apt-get update && sudo apt-get upgrade -y + run: sudo apt-get update + # && sudo apt-get upgrade -y - name: install packages - run: sudo apt-get install -y -m -f --install-suggests build-essential git libtool libtool-bin automake bison libglib2.0-0 clang llvm-dev libc++-dev findutils libcmocka-dev python3-dev python3-setuptools ninja-build + #run: sudo apt-get install -y -m -f --install-suggests build-essential git libtool libtool-bin automake bison libglib2.0-0 clang llvm-dev libc++-dev findutils libcmocka-dev python3-dev python3-setuptools ninja-build python3-pip + run: sudo apt-get install -y -m -f build-essential git libtool libtool-bin automake flex bison libglib2.0-0 clang llvm-dev libc++-dev findutils libcmocka-dev python3-dev python3-setuptools ninja-build python3-pip - name: compiler installed run: gcc -v; echo; clang -v - name: install gcc plugin run: sudo apt-get install -y -m -f --install-suggests $(readlink /usr/bin/gcc)-plugin-dev - name: build afl++ - run: make distrib ASAN_BUILD=1 + run: make distrib ASAN_BUILD=1 NO_NYX=1 - name: run tests run: sudo -E ./afl-system-config; make tests macos: diff --git a/.gitignore b/.gitignore index 8b0f0a7f..c01750e1 100644 --- a/.gitignore +++ b/.gitignore @@ -1,100 +1,107 @@ -.test -.test2 -.sync_tmp -.vscode +!coresight_mode +!coresight_mode/coresight-trace +*.dSYM *.o +*.o.tmp +*.pyc *.so *.swp -*.pyc -*.dSYM -as -a.out -ld -in -out -core* -compile_commands.json +.sync_tmp +.test +.test2 +.vscode afl-analyze +afl-analyze.8 afl-as +afl-as.8 +afl-c++ +afl-c++.8 +afl-cc +afl-cc.8 afl-clang afl-clang++ afl-clang-fast afl-clang-fast++ +afl-clang-fast++.8 +afl-clang-fast.8 afl-clang-lto afl-clang-lto++ +afl-clang-lto++.8 +afl-clang-lto.8 +afl-cmin.8 +afl-cmin.bash.8 +afl-cs-proxy +afl-frida-trace.so afl-fuzz +afl-fuzz.8 afl-g++ +afl-g++.8 afl-gcc +afl-gcc.8 afl-gcc-fast +afl-gcc-fast.8 afl-g++-fast +afl-g++-fast.8 afl-gotcpu +afl-gotcpu.8 afl-ld afl-ld-lto -afl-cs-proxy +afl-lto +afl-lto++ +afl-lto++.8 +afl-lto.8 +afl-persistent-config.8 +afl-plot.8 afl-qemu-trace afl-showmap -afl-tmin -afl-analyze.8 -afl-as.8 -afl-clang-fast++.8 -afl-clang-fast.8 -afl-clang-lto.8 -afl-clang-lto++.8 -afl-cmin.8 -afl-cmin.bash.8 -afl-fuzz.8 -afl-c++.8 -afl-cc.8 -afl-gcc.8 -afl-g++.8 -afl-gcc-fast.8 -afl-g++-fast.8 -afl-gotcpu.8 -afl-plot.8 afl-showmap.8 afl-system-config.8 +afl-tmin afl-tmin.8 afl-whatsup.8 -afl-persistent-config.8 -afl-c++ -afl-cc -afl-lto -afl-lto++ -afl-lto++.8 -afl-lto.8 -qemu_mode/libcompcov/compcovtest -qemu_mode/qemu-* -qemu_mode/qemuafl -unicorn_mode/samples/*/\.test-* -unicorn_mode/samples/*/output/ -test/unittests/unit_maybe_alloc -test/unittests/unit_preallocable -test/unittests/unit_list -test/unittests/unit_rand -test/unittests/unit_hash -examples/afl_network_proxy/afl-network-server -examples/afl_network_proxy/afl-network-client +a.out +as +compile_commands.json +core* examples/afl_frida/afl-frida -examples/afl_frida/libtestinstr.so examples/afl_frida/frida-gum-example.c examples/afl_frida/frida-gum.h +examples/afl_frida/libtestinstr.so +examples/afl_network_proxy/afl-network-client +examples/afl_network_proxy/afl-network-server examples/aflpp_driver/libAFLDriver.a examples/aflpp_driver/libAFLQemuDriver.a +gmon.out +in +ld libAFLDriver.a libAFLQemuDriver.a +out +qemu_mode/libcompcov/compcovtest +qemu_mode/qemu-* +qemu_mode/qemuafl test/.afl_performance -gmon.out -afl-frida-trace.so +test-instr +test/output +test/test-c +test/test-cmplog +test/test-compcov +test/test-instr.ts +test/test-persistent +test/unittests/unit_hash +test/unittests/unit_list +test/unittests/unit_maybe_alloc +test/unittests/unit_preallocable +test/unittests/unit_rand +unicorn_mode/samples/*/output/ +unicorn_mode/samples/*/\.test-* utils/afl_network_proxy/afl-network-client utils/afl_network_proxy/afl-network-server -utils/plot_ui/afl-plot-ui -*.o.tmp utils/afl_proxy/afl-proxy utils/optimin/build utils/optimin/optimin utils/persistent_mode/persistent_demo utils/persistent_mode/persistent_demo_new utils/persistent_mode/test-instr -!coresight_mode -!coresight_mode/coresight-trace -vuln_prog \ No newline at end of file +utils/plot_ui/afl-plot-ui +vuln_prog diff --git a/Dockerfile b/Dockerfile index f1b2fc01..4e53de40 100644 --- a/Dockerfile +++ b/Dockerfile @@ -9,18 +9,29 @@ FROM ubuntu:22.04 AS aflplusplus LABEL "maintainer"="afl++ team <afl@aflplus.plus>" LABEL "about"="AFLplusplus container image" +### Comment out to enable these features +# Only available on specific ARM64 boards +ENV NO_CORESIGHT=1 +# Possible but unlikely in a docker container +ENV NO_NYX=1 + +### Only change these if you know what you are doing: +# LLVM 15 does not look good so we stay at 14 to still have LTO +ENV LLVM_VERSION=14 +# GCC 12 is producing compile errors for some targets so we stay at GCC 11 +ENV GCC_VERSION=11 + +### No changes beyond the point unless you know what you are doing :) + ARG DEBIAN_FRONTEND=noninteractive ENV NO_ARCH_OPT=1 ENV IS_DOCKER=1 RUN apt-get update && apt-get full-upgrade -y && \ - apt-get install -y --no-install-recommends wget ca-certificates && \ + apt-get install -y --no-install-recommends wget ca-certificates apt-utils && \ rm -rf /var/lib/apt/lists/* -ENV LLVM_VERSION=14 -ENV GCC_VERSION=11 - RUN echo "deb [signed-by=/etc/apt/keyrings/llvm-snapshot.gpg.key] http://apt.llvm.org/jammy/ llvm-toolchain-jammy-${LLVM_VERSION} main" > /etc/apt/sources.list.d/llvm.list && \ wget -qO /etc/apt/keyrings/llvm-snapshot.gpg.key https://apt.llvm.org/llvm-snapshot.gpg.key @@ -28,15 +39,15 @@ RUN apt-get update && \ apt-get -y install --no-install-recommends \ make cmake automake meson ninja-build bison flex \ git xz-utils bzip2 wget jupp nano bash-completion less vim joe ssh psmisc \ - python3 python3-dev python3-setuptools python-is-python3 \ + python3 python3-dev python3-pip python-is-python3 \ libtool libtool-bin libglib2.0-dev \ - apt-utils apt-transport-https gnupg dialog \ + apt-transport-https gnupg dialog \ gnuplot-nox libpixman-1-dev \ gcc-${GCC_VERSION} g++-${GCC_VERSION} gcc-${GCC_VERSION}-plugin-dev gdb lcov \ clang-${LLVM_VERSION} clang-tools-${LLVM_VERSION} libc++1-${LLVM_VERSION} \ libc++-${LLVM_VERSION}-dev libc++abi1-${LLVM_VERSION} libc++abi-${LLVM_VERSION}-dev \ libclang1-${LLVM_VERSION} libclang-${LLVM_VERSION}-dev \ - libclang-common-${LLVM_VERSION}-dev libclang-cpp${LLVM_VERSION} \ + libclang-common-${LLVM_VERSION}-dev libclang-rt-${LLVM_VERSION}-dev libclang-cpp${LLVM_VERSION} \ libclang-cpp${LLVM_VERSION}-dev liblld-${LLVM_VERSION} \ liblld-${LLVM_VERSION}-dev liblldb-${LLVM_VERSION} liblldb-${LLVM_VERSION}-dev \ libllvm${LLVM_VERSION} libomp-${LLVM_VERSION}-dev libomp5-${LLVM_VERSION} \ @@ -56,6 +67,8 @@ RUN update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-${GCC_VERSION} 0 RUN wget -qO- https://sh.rustup.rs | CARGO_HOME=/etc/cargo sh -s -- -y -q --no-modify-path ENV PATH=$PATH:/etc/cargo/bin +RUN apt clean -y + ENV LLVM_CONFIG=llvm-config-${LLVM_VERSION} ENV AFL_SKIP_CPUFREQ=1 ENV AFL_TRY_AFFINITY=1 @@ -64,10 +77,6 @@ ENV AFL_I_DONT_CARE_ABOUT_MISSING_CRASHES=1 RUN git clone --depth=1 https://github.com/vanhauser-thc/afl-cov && \ (cd afl-cov && make install) && rm -rf afl-cov -# Build currently broken -ENV NO_CORESIGHT=1 -ENV NO_UNICORN_ARM64=1 - WORKDIR /AFLplusplus COPY . . @@ -85,4 +94,4 @@ RUN sed -i.bak 's/^ -/ /g' GNUmakefile && \ RUN echo "set encoding=utf-8" > /root/.vimrc && \ echo ". /etc/bash_completion" >> ~/.bashrc && \ echo 'alias joe="joe --wordwrap --joe_state -nobackup"' >> ~/.bashrc && \ - echo "export PS1='"'[afl++ \h] \w$(__git_ps1) \$ '"'" >> ~/.bashrc + echo "export PS1='"'[afl++ \h] \w \$ '"'" >> ~/.bashrc diff --git a/GNUmakefile b/GNUmakefile index 70299fc3..0f890308 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -91,9 +91,8 @@ ifneq "$(SYS)" "Darwin" #ifeq "$(HAVE_MARCHNATIVE)" "1" # SPECIAL_PERFORMANCE += -march=native #endif - # OS X does not like _FORTIFY_SOURCE=2 ifndef DEBUG - CFLAGS_OPT += -D_FORTIFY_SOURCE=2 + CFLAGS_OPT += -D_FORTIFY_SOURCE=1 endif else # On some odd MacOS system configurations, the Xcode sdk path is not set correctly @@ -103,7 +102,7 @@ endif ifeq "$(SYS)" "SunOS" CFLAGS_OPT += -Wno-format-truncation - LDFLAGS = -lkstat -lrt + LDFLAGS = -lkstat -lrt -lsocket -lnsl endif ifdef STATIC @@ -197,7 +196,7 @@ ifeq "$(PYTHON_INCLUDE)" "" ifneq "$(shell command -v python3-config 2>/dev/null)" "" PYTHON_INCLUDE ?= $(shell python3-config --includes) PYTHON_VERSION ?= $(strip $(shell python3 --version 2>&1)) - # Starting with python3.8, we need to pass the `embed` flag. Earier versions didn't know this flag. + # Starting with python3.8, we need to pass the `embed` flag. Earlier versions didn't know this flag. ifeq "$(shell python3-config --embed --libs 2>/dev/null | grep -q lpython && echo 1 )" "1" PYTHON_LIB ?= $(shell python3-config --libs --embed --ldflags) else @@ -313,9 +312,9 @@ all: test_x86 test_shm test_python ready $(PROGS) afl-as llvm gcc_plugin test_bu @echo @echo Build Summary: @test -e afl-fuzz && echo "[+] afl-fuzz and supporting tools successfully built" || echo "[-] afl-fuzz could not be built, please set CC to a working compiler" - @test -e afl-llvm-pass.so && echo "[+] LLVM basic mode successfully built" || echo "[-] LLVM mode could not be build, please install at least llvm-11 and clang-11 or newer, see docs/INSTALL.md" - @test -e SanitizerCoveragePCGUARD.so && echo "[+] LLVM mode successfully built" || echo "[-] LLVM mode could not be build, please install at least llvm-11 and clang-11 or newer, see docs/INSTALL.md" - @test -e SanitizerCoverageLTO.so && echo "[+] LLVM LTO mode successfully built" || echo "[-] LLVM LTO mode could not be build, it is optional, if you want it, please install LLVM 11-14. More information at instrumentation/README.lto.md on how to build it" + @test -e afl-llvm-pass.so && echo "[+] LLVM basic mode successfully built" || echo "[-] LLVM mode could not be built, please install at least llvm-11 and clang-11 or newer, see docs/INSTALL.md" + @test -e SanitizerCoveragePCGUARD.so && echo "[+] LLVM mode successfully built" || echo "[-] LLVM mode could not be built, please install at least llvm-11 and clang-11 or newer, see docs/INSTALL.md" + @test -e SanitizerCoverageLTO.so && echo "[+] LLVM LTO mode successfully built" || echo "[-] LLVM LTO mode could not be built, it is optional, if you want it, please install LLVM 11-14. More information at instrumentation/README.lto.md on how to build it" ifneq "$(SYS)" "Darwin" @test -e afl-gcc-pass.so && echo "[+] gcc_mode successfully built" || echo "[-] gcc_mode could not be built, it is optional, install gcc-VERSION-plugin-dev to enable this" endif @@ -381,6 +380,7 @@ help: @echo ASAN_BUILD - compiles AFL++ with memory sanitizer for debug purposes @echo UBSAN_BUILD - compiles AFL++ tools with undefined behaviour sanitizer for debug purposes @echo DEBUG - no optimization, -ggdb3, all warnings and -Werror + @echo LLVM_DEBUG - shows llvm deprecation warnings @echo PROFILING - compile afl-fuzz with profiling information @echo INTROSPECTION - compile afl-fuzz with mutation introspection @echo NO_PYTHON - disable python support @@ -388,6 +388,7 @@ help: @echo NO_NYX - disable building nyx mode dependencies @echo "NO_CORESIGHT - disable building coresight (arm64 only)" @echo NO_UNICORN_ARM64 - disable building unicorn on arm64 + @echo "WAFL_MODE - enable for WASM fuzzing with https://github.com/fgsect/WAFL" @echo AFL_NO_X86 - if compiling on non-intel/amd platforms @echo "LLVM_CONFIG - if your distro doesn't use the standard name for llvm-config (e.g., Debian)" @echo "==========================================" @@ -425,7 +426,7 @@ test_python: @echo "[+] $(PYTHON_VERSION) support seems to be working." else test_python: - @echo "[-] You seem to need to install the package python3-dev, python2-dev or python-dev (and perhaps python[23]-apt), but it is optional so we continue" + @echo "[-] You seem to need to install the package python3-dev or python-dev (and perhaps python[3]-apt), but it is optional so we continue" endif .PHONY: ready @@ -452,7 +453,7 @@ afl-fuzz: $(COMM_HDR) include/afl-fuzz.h $(AFL_FUZZ_FILES) src/afl-common.o src/ $(CC) $(CFLAGS) $(COMPILE_STATIC) $(CFLAGS_FLTO) $(AFL_FUZZ_FILES) src/afl-common.o src/afl-sharedmem.o src/afl-forkserver.o src/afl-performance.o -o $@ $(PYFLAGS) $(LDFLAGS) -lm afl-showmap: src/afl-showmap.c src/afl-common.o src/afl-sharedmem.o src/afl-forkserver.o src/afl-performance.o $(COMM_HDR) | test_x86 - $(CC) $(CFLAGS) $(COMPILE_STATIC) $(CFLAGS_FLTO) src/$@.c src/afl-common.o src/afl-sharedmem.o src/afl-forkserver.o src/afl-performance.o -o $@ $(LDFLAGS) + $(CC) $(CFLAGS) $(COMPILE_STATIC) $(CFLAGS_FLTO) src/$@.c src/afl-fuzz-mutators.c src/afl-fuzz-python.c src/afl-common.o src/afl-sharedmem.o src/afl-forkserver.o src/afl-performance.o -o $@ $(PYFLAGS) $(LDFLAGS) afl-tmin: src/afl-tmin.c src/afl-common.o src/afl-sharedmem.o src/afl-forkserver.o src/afl-performance.o $(COMM_HDR) | test_x86 $(CC) $(CFLAGS) $(COMPILE_STATIC) $(CFLAGS_FLTO) src/$@.c src/afl-common.o src/afl-sharedmem.o src/afl-forkserver.o src/afl-performance.o -o $@ $(LDFLAGS) @@ -546,8 +547,8 @@ ifndef AFL_NO_X86 test_build: afl-cc afl-gcc afl-as afl-showmap @echo "[*] Testing the CC wrapper afl-cc and its instrumentation output..." @unset AFL_MAP_SIZE AFL_USE_UBSAN AFL_USE_CFISAN AFL_USE_LSAN AFL_USE_ASAN AFL_USE_MSAN; ASAN_OPTIONS=detect_leaks=0 AFL_INST_RATIO=100 AFL_PATH=. ./afl-cc test-instr.c $(LDFLAGS) -o test-instr 2>&1 || (echo "Oops, afl-cc failed"; exit 1 ) - ASAN_OPTIONS=detect_leaks=0 ./afl-showmap -m none -q -o .test-instr0 ./test-instr < /dev/null - echo 1 | ASAN_OPTIONS=detect_leaks=0 ./afl-showmap -m none -q -o .test-instr1 ./test-instr + -ASAN_OPTIONS=detect_leaks=0 ./afl-showmap -q -m none -o .test-instr0 ./test-instr < /dev/null + -echo 1 | ASAN_OPTIONS=detect_leaks=0 ./afl-showmap -m none -q -o .test-instr1 ./test-instr @rm -f test-instr @cmp -s .test-instr0 .test-instr1; DR="$$?"; rm -f .test-instr0 .test-instr1; if [ "$$DR" = "0" ]; then echo; echo "Oops, the instrumentation of afl-cc does not seem to be behaving correctly!"; echo; echo "Please post to https://github.com/AFLplusplus/AFLplusplus/issues to troubleshoot the issue."; echo; exit 1; fi @echo @@ -592,6 +593,7 @@ clean: -$(MAKE) -C utils/argv_fuzzing clean -$(MAKE) -C utils/plot_ui clean -$(MAKE) -C qemu_mode/unsigaction clean + -$(MAKE) -C qemu_mode/fastexit clean -$(MAKE) -C qemu_mode/libcompcov clean -$(MAKE) -C qemu_mode/libqasan clean -$(MAKE) -C frida_mode clean @@ -627,25 +629,25 @@ distrib: all -$(MAKE) -j$(nproc) -f GNUmakefile.llvm ifneq "$(SYS)" "Darwin" -$(MAKE) -f GNUmakefile.gcc_plugin -endif -$(MAKE) -C utils/libdislocator -$(MAKE) -C utils/libtokencap +endif -$(MAKE) -C utils/afl_network_proxy -$(MAKE) -C utils/socket_fuzzing -$(MAKE) -C utils/argv_fuzzing # -$(MAKE) -C utils/plot_ui -$(MAKE) -C frida_mode ifneq "$(SYS)" "Darwin" -ifeq "$(ARCH)" "aarch64" - ifndef NO_CORESIGHT + ifeq "$(ARCH)" "aarch64" + ifndef NO_CORESIGHT -$(MAKE) -C coresight_mode + endif endif -endif -ifeq "$(SYS)" "Linux" - ifndef NO_NYX + ifeq "$(SYS)" "Linux" + ifndef NO_NYX -cd nyx_mode && ./build_nyx_support.sh + endif endif -endif -cd qemu_mode && sh ./build_qemu_support.sh ifeq "$(ARCH)" "aarch64" ifndef NO_UNICORN_ARM64 @@ -658,8 +660,10 @@ endif .PHONY: binary-only binary-only: test_shm test_python ready $(PROGS) +ifneq "$(SYS)" "Darwin" -$(MAKE) -C utils/libdislocator -$(MAKE) -C utils/libtokencap +endif -$(MAKE) -C utils/afl_network_proxy -$(MAKE) -C utils/socket_fuzzing -$(MAKE) -C utils/argv_fuzzing @@ -716,9 +720,9 @@ source-only: all -$(MAKE) -j$(nproc) -f GNUmakefile.llvm ifneq "$(SYS)" "Darwin" -$(MAKE) -f GNUmakefile.gcc_plugin -endif -$(MAKE) -C utils/libdislocator -$(MAKE) -C utils/libtokencap +endif # -$(MAKE) -C utils/plot_ui ifeq "$(SYS)" "Linux" ifndef NO_NYX @@ -729,9 +733,9 @@ endif @echo @echo Build Summary: @test -e afl-fuzz && echo "[+] afl-fuzz and supporting tools successfully built" || echo "[-] afl-fuzz could not be built, please set CC to a working compiler" - @test -e afl-llvm-pass.so && echo "[+] LLVM basic mode successfully built" || echo "[-] LLVM mode could not be build, please install at least llvm-11 and clang-11 or newer, see docs/INSTALL.md" - @test -e SanitizerCoveragePCGUARD.so && echo "[+] LLVM mode successfully built" || echo "[-] LLVM mode could not be build, please install at least llvm-11 and clang-11 or newer, see docs/INSTALL.md" - @test -e SanitizerCoverageLTO.so && echo "[+] LLVM LTO mode successfully built" || echo "[-] LLVM LTO mode could not be build, it is optional, if you want it, please install LLVM 11-14. More information at instrumentation/README.lto.md on how to build it" + @test -e afl-llvm-pass.so && echo "[+] LLVM basic mode successfully built" || echo "[-] LLVM mode could not be built, please install at least llvm-11 and clang-11 or newer, see docs/INSTALL.md" + @test -e SanitizerCoveragePCGUARD.so && echo "[+] LLVM mode successfully built" || echo "[-] LLVM mode could not be built, please install at least llvm-11 and clang-11 or newer, see docs/INSTALL.md" + @test -e SanitizerCoverageLTO.so && echo "[+] LLVM LTO mode successfully built" || echo "[-] LLVM LTO mode could not be built, it is optional, if you want it, please install LLVM 11-14. More information at instrumentation/README.lto.md on how to build it" ifneq "$(SYS)" "Darwin" test -e afl-gcc-pass.so && echo "[+] gcc_mode successfully built" || echo "[-] gcc_mode could not be built, it is optional, install gcc-VERSION-plugin-dev to enable this" endif diff --git a/GNUmakefile.gcc_plugin b/GNUmakefile.gcc_plugin index 17bd825d..4c4e10c4 100644 --- a/GNUmakefile.gcc_plugin +++ b/GNUmakefile.gcc_plugin @@ -11,7 +11,7 @@ # from Laszlo Szekeres. # # Copyright 2015 Google Inc. All rights reserved. -# Copyright 2019-2022 AFLplusplus Project. All rights reserved. +# Copyright 2019-2023 AFLplusplus Project. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -28,14 +28,14 @@ MAN_PATH ?= $(PREFIX)/share/man/man8 VERSION = $(shell grep '^$(HASH)define VERSION ' ./config.h | cut -d '"' -f2) -CFLAGS ?= -O3 -g -funroll-loops -D_FORTIFY_SOURCE=2 +CFLAGS ?= -O3 -g -funroll-loops -D_FORTIFY_SOURCE=1 CFLAGS_SAFE := -Wall -Iinclude -Wno-pointer-sign \ -DAFL_PATH=\"$(HELPER_PATH)\" -DBIN_PATH=\"$(BIN_PATH)\" \ -DGCC_VERSION=\"$(GCCVER)\" -DGCC_BINDIR=\"$(GCCBINDIR)\" \ -Wno-unused-function override CFLAGS += $(CFLAGS_SAFE) -CXXFLAGS ?= -O3 -g -funroll-loops -D_FORTIFY_SOURCE=2 +CXXFLAGS ?= -O3 -g -funroll-loops -D_FORTIFY_SOURCE=1 CXXEFLAGS := $(CXXFLAGS) -Wall -std=c++11 CC ?= gcc diff --git a/GNUmakefile.llvm b/GNUmakefile.llvm index e775ca98..a053403b 100644 --- a/GNUmakefile.llvm +++ b/GNUmakefile.llvm @@ -45,11 +45,12 @@ endif LLVMVER = $(shell $(LLVM_CONFIG) --version 2>/dev/null | sed 's/git//' | sed 's/svn//' ) LLVM_MAJOR = $(shell $(LLVM_CONFIG) --version 2>/dev/null | sed 's/\..*//' ) LLVM_MINOR = $(shell $(LLVM_CONFIG) --version 2>/dev/null | sed 's/.*\.//' | sed 's/git//' | sed 's/svn//' | sed 's/ .*//' ) -LLVM_UNSUPPORTED = $(shell $(LLVM_CONFIG) --version 2>/dev/null | egrep -q '^[0-2]\.|^3.[0-7]\.' && echo 1 || echo 0 ) -LLVM_TOO_NEW = $(shell $(LLVM_CONFIG) --version 2>/dev/null | egrep -q '^1[5-9]' && echo 1 || echo 0 ) -LLVM_NEW_API = $(shell $(LLVM_CONFIG) --version 2>/dev/null | egrep -q '^1[0-9]' && echo 1 || echo 0 ) -LLVM_10_OK = $(shell $(LLVM_CONFIG) --version 2>/dev/null | egrep -q '^1[1-9]|^10\.[1-9]|^10\.0.[1-9]' && echo 1 || echo 0 ) -LLVM_HAVE_LTO = $(shell $(LLVM_CONFIG) --version 2>/dev/null | egrep -q '^1[1-9]' && echo 1 || echo 0 ) +LLVM_UNSUPPORTED = $(shell $(LLVM_CONFIG) --version 2>/dev/null | grep -E -q '^[0-2]\.|^3.[0-7]\.' && echo 1 || echo 0 ) +LLVM_TOO_NEW = $(shell $(LLVM_CONFIG) --version 2>/dev/null | grep -E -q '^1[5-9]' && echo 1 || echo 0 ) +LLVM_NEW_API = $(shell $(LLVM_CONFIG) --version 2>/dev/null | grep -E -q '^1[0-9]' && echo 1 || echo 0 ) +LLVM_NEWER_API = $(shell $(LLVM_CONFIG) --version 2>/dev/null | grep -E -q '^1[6-9]' && echo 1 || echo 0 ) +LLVM_10_OK = $(shell $(LLVM_CONFIG) --version 2>/dev/null | grep -E -q '^1[1-9]|^10\.[1-9]|^10\.0.[1-9]' && echo 1 || echo 0 ) +LLVM_HAVE_LTO = $(shell $(LLVM_CONFIG) --version 2>/dev/null | grep -E -q '^1[1-9]' && echo 1 || echo 0 ) LLVM_BINDIR = $(shell $(LLVM_CONFIG) --bindir 2>/dev/null) LLVM_LIBDIR = $(shell $(LLVM_CONFIG) --libdir 2>/dev/null) LLVM_STDCXX = gnu++11 @@ -81,25 +82,23 @@ ifeq "$(LLVM_NEW_API)" "1" LLVM_TOO_OLD=0 endif +ifeq "$(LLVM_NEWER_API)" "1" + $(info [+] llvm_mode detected llvm 16+, enabling c++17) + LLVM_STDCXX = c++17 +endif + ifeq "$(LLVM_TOO_OLD)" "1" $(info [!] llvm_mode detected an old version of llvm, upgrade to at least 9 or preferable 11!) $(shell sleep 1) endif -ifeq "$(LLVM_MAJOR)" "15" - $(info [!] llvm_mode detected llvm 15, which is currently broken for LTO plugins.) - LLVM_LTO = 0 - LLVM_HAVE_LTO = 0 -endif - ifeq "$(LLVM_HAVE_LTO)" "1" $(info [+] llvm_mode detected llvm 11+, enabling afl-lto LTO implementation) LLVM_LTO = 1 - #TEST_MMAP = 1 endif ifeq "$(LLVM_LTO)" "0" - $(info [+] llvm_mode detected llvm < 11 or llvm 15, afl-lto LTO will not be build.) + $(info [+] llvm_mode detected llvm < 11, afl-lto LTO will not be build.) endif ifeq "$(LLVM_APPLE_XCODE)" "1" @@ -220,6 +219,17 @@ ifeq "$(LLVM_LTO)" "1" ifeq "$(AFL_REAL_LD)" "" ifneq "$(shell readlink $(LLVM_BINDIR)/ld.lld 2>&1)" "" AFL_REAL_LD = $(LLVM_BINDIR)/ld.lld + else ifneq "$(shell command -v ld.lld 2>/dev/null)" "" + AFL_REAL_LD = $(shell command -v ld.lld) + TMP_LDLDD_VERSION = $(shell $(AFL_REAL_LD) --version | awk '{ print $$2 }') + ifeq "$(LLVMVER)" "$(TMP_LDLDD_VERSION)" + $(warning ld.lld found in a weird location ($(AFL_REAL_LD)), but its the same version as LLVM so we will allow it) + else + $(warning ld.lld found in a weird location ($(AFL_REAL_LD)) and its of a different version than LLMV ($(TMP_LDLDD_VERSION) vs. $(LLVMVER)) - cannot enable LTO mode) + AFL_REAL_LD= + LLVM_LTO = 0 + endif + undefine TMP_LDLDD_VERSION else $(warning ld.lld not found, cannot enable LTO mode) LLVM_LTO = 0 @@ -235,7 +245,7 @@ AFL_CLANG_FUSELD= ifeq "$(LLVM_LTO)" "1" ifeq "$(shell echo 'int main() {return 0; }' | $(CLANG_BIN) -x c - -fuse-ld=`command -v ld` -o .test 2>/dev/null && echo 1 || echo 0 ; rm -f .test )" "1" AFL_CLANG_FUSELD=1 - ifeq "$(shell echo 'int main() {return 0; }' | $(CLANG_BIN) -x c - -fuse-ld=ld.lld --ld-path=$(LLVM_BINDIR)/ld.lld -o .test 2>/dev/null && echo 1 || echo 0 ; rm -f .test )" "1" + ifeq "$(shell echo 'int main() {return 0; }' | $(CLANG_BIN) -x c - -fuse-ld=ld.lld --ld-path=$(AFL_REAL_LD) -o .test 2>/dev/null && echo 1 || echo 0 ; rm -f .test )" "1" AFL_CLANG_LDPATH=1 endif else @@ -250,26 +260,29 @@ else AFL_CLANG_DEBUG_PREFIX = endif -CFLAGS ?= -O3 -funroll-loops -fPIC -D_FORTIFY_SOURCE=2 -CFLAGS_SAFE := -Wall -g -Wno-cast-qual -Wno-variadic-macros -Wno-pointer-sign -I ./include/ -I ./instrumentation/ \ +CFLAGS ?= -O3 -funroll-loops -fPIC -D_FORTIFY_SOURCE=1 +CFLAGS_SAFE := -Wall -g -Wno-cast-qual -Wno-variadic-macros -Wno-pointer-sign \ + -I ./include/ -I ./instrumentation/ \ -DAFL_PATH=\"$(HELPER_PATH)\" -DBIN_PATH=\"$(BIN_PATH)\" \ -DLLVM_BINDIR=\"$(LLVM_BINDIR)\" -DVERSION=\"$(VERSION)\" \ -DLLVM_LIBDIR=\"$(LLVM_LIBDIR)\" -DLLVM_VERSION=\"$(LLVMVER)\" \ - -Wno-deprecated -DAFL_CLANG_FLTO=\"$(AFL_CLANG_FLTO)\" \ - -DAFL_REAL_LD=\"$(AFL_REAL_LD)\" \ - -DAFL_CLANG_LDPATH=\"$(AFL_CLANG_LDPATH)\" \ - -DAFL_CLANG_FUSELD=\"$(AFL_CLANG_FUSELD)\" \ - -DCLANG_BIN=\"$(CLANG_BIN)\" -DCLANGPP_BIN=\"$(CLANGPP_BIN)\" -DUSE_BINDIR=$(USE_BINDIR) -Wno-unused-function \ - $(AFL_CLANG_DEBUG_PREFIX) + -DAFL_CLANG_FLTO=\"$(AFL_CLANG_FLTO)\" -DAFL_REAL_LD=\"$(AFL_REAL_LD)\" \ + -DAFL_CLANG_LDPATH=\"$(AFL_CLANG_LDPATH)\" -DAFL_CLANG_FUSELD=\"$(AFL_CLANG_FUSELD)\" \ + -DCLANG_BIN=\"$(CLANG_BIN)\" -DCLANGPP_BIN=\"$(CLANGPP_BIN)\" -DUSE_BINDIR=$(USE_BINDIR) \ + -Wno-unused-function $(AFL_CLANG_DEBUG_PREFIX) +ifndef LLVM_DEBUG + CFLAGS_SAFE += -Wno-deprecated +endif + override CFLAGS += $(CFLAGS_SAFE) ifdef AFL_TRACE_PC $(info Compile option AFL_TRACE_PC is deprecated, just set AFL_LLVM_INSTRUMENT=PCGUARD to activate when compiling targets ) endif -CXXFLAGS ?= -O3 -funroll-loops -fPIC -D_FORTIFY_SOURCE=2 +CXXFLAGS ?= -O3 -funroll-loops -fPIC -D_FORTIFY_SOURCE=1 override CXXFLAGS += -Wall -g -I ./include/ \ - -DVERSION=\"$(VERSION)\" -Wno-variadic-macros \ + -DVERSION=\"$(VERSION)\" -Wno-variadic-macros -Wno-deprecated-copy-with-dtor \ -DLLVM_MINOR=$(LLVM_MINOR) -DLLVM_MAJOR=$(LLVM_MAJOR) ifneq "$(shell $(LLVM_CONFIG) --includedir) 2> /dev/null" "" @@ -281,6 +294,11 @@ endif CLANG_CPPFL = `$(LLVM_CONFIG) --cxxflags` -fno-rtti -fPIC $(CXXFLAGS) -Wno-deprecated-declarations CLANG_LFL = `$(LLVM_CONFIG) --ldflags` $(LDFLAGS) +# wasm fuzzing: disable thread-local storage and unset LLVM debug flag +ifdef WAFL_MODE + $(info Compiling libraries for use with WAVM) + CLANG_CPPFL += -DNDEBUG -DNO_TLS +endif # User teor2345 reports that this is required to make things work on MacOS X. ifeq "$(SYS)" "Darwin" diff --git a/README.md b/README.md index a8b579d8..c012c400 100644 --- a/README.md +++ b/README.md @@ -2,9 +2,9 @@ <img align="right" src="https://raw.githubusercontent.com/AFLplusplus/Website/master/static/aflpp_bg.svg" alt="AFL++ logo" width="250" heigh="250"> -Release version: [4.02c](https://github.com/AFLplusplus/AFLplusplus/releases) +Release version: [4.06c](https://github.com/AFLplusplus/AFLplusplus/releases) -GitHub version: 4.03a +GitHub version: 4.07a Repository: [https://github.com/AFLplusplus/AFLplusplus](https://github.com/AFLplusplus/AFLplusplus) @@ -228,6 +228,7 @@ Thank you! (For people sending pull requests - please add yourself to this list Thomas Rooijakkers David Carlier Ruben ten Hove Joey Jiao fuzzah @intrigus-lgtm + Yaakov Saxon ``` </details> diff --git a/TODO.md b/TODO.md index 99d2c419..dba75070 100644 --- a/TODO.md +++ b/TODO.md @@ -2,18 +2,20 @@ ## Should - - better documentation for custom mutators + - splicing selection weighted? + - support persistent and deferred fork server in afl-showmap? - better autodetection of shifting runtime timeout values - Update afl->pending_not_fuzzed for MOpt - afl-plot to support multiple plot_data - parallel builds for source-only targets - get rid of check_binary, replace with more forkserver communication + - first fuzzer should be a main automatically? not sure. + - reload fuzz binary on signal ## Maybe - forkserver tells afl-fuzz if cmplog is supported and if so enable it by default, with AFL_CMPLOG_NO=1 (?) set to skip? - - afl_custom_fuzz_splice_optin() - afl_custom_splice() - cmdline option from-to range for mutations diff --git a/afl-cmin b/afl-cmin index b170667a..e2c26d91 100755 --- a/afl-cmin +++ b/afl-cmin @@ -105,12 +105,14 @@ function usage() { "Execution control settings:\n" \ " -f file - location read by the fuzzed program (stdin)\n" \ " -m megs - memory limit for child process ("mem_limit" MB)\n" \ -" -t msec - run time limit for child process (none)\n" \ +" -t msec - run time limit for child process (default: none)\n" \ " -O - use binary-only instrumentation (FRIDA mode)\n" \ " -Q - use binary-only instrumentation (QEMU mode)\n" \ " -U - use unicorn-based instrumentation (unicorn mode)\n" \ +" -X - use Nyx mode\n" \ "\n" \ "Minimization settings:\n" \ +" -A - allow crashes and timeouts (not recommended)\n" \ " -C - keep crashing inputs, reject everything else\n" \ " -e - solve for edge coverage only, ignore hit counts\n" \ "\n" \ @@ -122,11 +124,17 @@ function usage() { "AFL_FORKSRV_INIT_TMOUT: time the fuzzer waits for the forkserver to come up\n" \ "AFL_KEEP_TRACES: leave the temporary <out_dir>/.traces directory\n" \ "AFL_KILL_SIGNAL: Signal delivered to child processes on timeout (default: SIGKILL)\n" \ +"AFL_FORK_SERVER_KILL_SIGNAL: Signal delivered to fork server processes on\n" \ +" termination (default: SIGTERM). If this is not set and AFL_KILL_SIGNAL is\n" \ +" set, this will be set to the same value as AFL_KILL_SIGNAL.\n" \ "AFL_NO_FORKSRV: run target via execve instead of using the forkserver\n" \ +"AFL_CMIN_ALLOW_ANY: write tuples for crashing inputs also\n" \ "AFL_PATH: path for the afl-showmap binary if not found anywhere in PATH\n" \ "AFL_PRINT_FILENAMES: If set, the filename currently processed will be " \ "printed to stdout\n" \ "AFL_SKIP_BIN_CHECK: skip afl instrumentation checks for target binary\n" +"AFL_CUSTOM_MUTATOR_LIBRARY: custom mutator library (post_process and send)\n" +"AFL_PYTHON_MODULE: custom mutator library (post_process and send)\n" exit 1 } @@ -146,11 +154,12 @@ BEGIN { # defaults extra_par = "" AFL_CMIN_CRASHES_ONLY = "" + AFL_CMIN_ALLOW_ANY = "" # process options Opterr = 1 # default is to diagnose Optind = 1 # skip ARGV[0] - while ((_go_c = getopt(ARGC, ARGV, "hi:o:f:m:t:eCOQU?")) != -1) { + while ((_go_c = getopt(ARGC, ARGV, "hi:o:f:m:t:eACOQUXY?")) != -1) { if (_go_c == "i") { if (!Optarg) usage() if (in_dir) { print "Option "_go_c" is only allowed once" > "/dev/stderr"} @@ -186,6 +195,10 @@ BEGIN { AFL_CMIN_CRASHES_ONLY = "AFL_CMIN_CRASHES_ONLY=1 " continue } else + if (_go_c == "A") { + AFL_CMIN_ALLOW_ANY = "AFL_CMIN_ALLOW_ANY=1 " + continue + } else if (_go_c == "e") { extra_par = extra_par " -e" continue @@ -207,6 +220,12 @@ BEGIN { extra_par = extra_par " -U" unicorn_mode = 1 continue + } else + if (_go_c == "X" || _go_c == "Y") { + if (nyx_mode) { print "Option "_go_c" is only allowed once" > "/dev/stderr"} + extra_par = extra_par " -X" + nyx_mode = 1 + continue } else if (_go_c == "?") { exit 1 @@ -281,7 +300,8 @@ BEGIN { exit 1 } - if (target_bin && !exists_and_is_executable(target_bin)) { + + if (!nyx_mode && target_bin && !exists_and_is_executable(target_bin)) { "command -v "target_bin" 2>/dev/null" | getline tnew if (!tnew || !exists_and_is_executable(tnew)) { @@ -301,7 +321,7 @@ BEGIN { } } - if (!ENVIRON["AFL_SKIP_BIN_CHECK"] && !qemu_mode && !frida_mode && !unicorn_mode) { + if (!ENVIRON["AFL_SKIP_BIN_CHECK"] && !qemu_mode && !frida_mode && !unicorn_mode && !nyx_mode) { if (0 != system( "grep -q __AFL_SHM_ID "target_bin )) { print "[-] Error: binary '"target_bin"' doesn't appear to be instrumented." > "/dev/stderr" exit 1 @@ -445,15 +465,15 @@ BEGIN { if (!stdin_file) { print " Processing "in_count" files (forkserver mode)..." # print AFL_CMIN_CRASHES_ONLY"\""showmap"\" -m "mem_limit" -t "timeout" -o \""trace_dir"\" -Z "extra_par" -i \""in_dir"\" -- \""target_bin"\" "prog_args_string - retval = system(AFL_MAP_SIZE AFL_CMIN_CRASHES_ONLY"\""showmap"\" -m "mem_limit" -t "timeout" -o \""trace_dir"\" -Z "extra_par" -i \""in_dir"\" -- \""target_bin"\" "prog_args_string) + retval = system(AFL_MAP_SIZE AFL_CMIN_ALLOW_ANY AFL_CMIN_CRASHES_ONLY"\""showmap"\" -m "mem_limit" -t "timeout" -o \""trace_dir"\" -Z "extra_par" -i \""in_dir"\" -- \""target_bin"\" "prog_args_string) } else { print " Processing "in_count" files (forkserver mode)..." # print AFL_CMIN_CRASHES_ONLY"\""showmap"\" -m "mem_limit" -t "timeout" -o \""trace_dir"\" -Z "extra_par" -i \""in_dir"\" -H \""stdin_file"\" -- \""target_bin"\" "prog_args_string" </dev/null" - retval = system(AFL_MAP_SIZE AFL_CMIN_CRASHES_ONLY"\""showmap"\" -m "mem_limit" -t "timeout" -o \""trace_dir"\" -Z "extra_par" -i \""in_dir"\" -H \""stdin_file"\" -- \""target_bin"\" "prog_args_string" </dev/null") + retval = system(AFL_MAP_SIZE AFL_CMIN_ALLOW_ANY AFL_CMIN_CRASHES_ONLY"\""showmap"\" -m "mem_limit" -t "timeout" -o \""trace_dir"\" -Z "extra_par" -i \""in_dir"\" -H \""stdin_file"\" -- \""target_bin"\" "prog_args_string" </dev/null") } - if (retval && !AFL_CMIN_CRASHES_ONLY) { - print "[!] Exit code "retval" != 0 received from afl-showmap, terminating..." + if (retval && (!AFL_CMIN_CRASHES_ONLY && !AFL_CMIN_ALLOW_ANY)) { + print "[!] Exit code "retval" != 0 received from afl-showmap (this means a crashing or timeout input is likely present), terminating..." if (!ENVIRON["AFL_KEEP_TRACES"]) { system("rm -rf "trace_dir" 2>/dev/null") diff --git a/afl-cmin.bash b/afl-cmin.bash index d2218cd0..5258758e 100755 --- a/afl-cmin.bash +++ b/afl-cmin.bash @@ -53,7 +53,7 @@ unset IN_DIR OUT_DIR STDIN_FILE EXTRA_PAR MEM_LIMIT_GIVEN \ export AFL_QUIET=1 -while getopts "+i:o:f:m:t:eOQUCh" opt; do +while getopts "+i:o:f:m:t:eOQUAChXY" opt; do case "$opt" in @@ -80,6 +80,9 @@ while getopts "+i:o:f:m:t:eOQUCh" opt; do "e") EXTRA_PAR="$EXTRA_PAR -e" ;; + "A") + export AFL_CMIN_ALLOW_ANY=1 + ;; "C") export AFL_CMIN_CRASHES_ONLY=1 ;; @@ -91,6 +94,14 @@ while getopts "+i:o:f:m:t:eOQUCh" opt; do EXTRA_PAR="$EXTRA_PAR -Q" QEMU_MODE=1 ;; + "Y") + EXTRA_PAR="$EXTRA_PAR -X" + NYX_MODE=1 + ;; + "X") + EXTRA_PAR="$EXTRA_PAR -X" + NYX_MODE=1 + ;; "U") EXTRA_PAR="$EXTRA_PAR -U" UNICORN_MODE=1 @@ -125,9 +136,11 @@ Execution control settings: -O - use binary-only instrumentation (FRIDA mode) -Q - use binary-only instrumentation (QEMU mode) -U - use unicorn-based instrumentation (Unicorn mode) + -X - use Nyx mode Minimization settings: + -A - allow crashing and timeout inputs -C - keep crashing inputs, reject everything else -e - solve for edge coverage only, ignore hit counts @@ -138,6 +151,8 @@ AFL_KEEP_TRACES: leave the temporary <out_dir>\.traces directory AFL_NO_FORKSRV: run target via execve instead of using the forkserver AFL_PATH: last resort location to find the afl-showmap binary AFL_SKIP_BIN_CHECK: skip check for target binary +AFL_CUSTOM_MUTATOR_LIBRARY: custom mutator library (post_process and send) +AFL_PYTHON_MODULE: custom mutator library (post_process and send) _EOF_ exit 1 fi @@ -202,16 +217,19 @@ if [ ! "$TIMEOUT" = "none" ]; then fi -if [ ! -f "$TARGET_BIN" -o ! -x "$TARGET_BIN" ]; then +if [ "$NYX_MODE" = "" ]; then + if [ ! -f "$TARGET_BIN" -o ! -x "$TARGET_BIN" ]; then - TNEW="`which "$TARGET_BIN" 2>/dev/null`" + TNEW="`which "$TARGET_BIN" 2>/dev/null`" - if [ ! -f "$TNEW" -o ! -x "$TNEW" ]; then - echo "[-] Error: binary '$TARGET_BIN' not found or not executable." 1>&2 - exit 1 - fi + if [ ! -f "$TNEW" -o ! -x "$TNEW" ]; then + echo "[-] Error: binary '$TARGET_BIN' not found or not executable." 1>&2 + exit 1 + fi + + TARGET_BIN="$TNEW" - TARGET_BIN="$TNEW" + fi fi @@ -224,7 +242,7 @@ grep -aq AFL_DUMP_MAP_SIZE "./$TARGET_BIN" && { } } -if [ "$AFL_SKIP_BIN_CHECK" = "" -a "$QEMU_MODE" = "" -a "$FRIDA_MODE" = "" -a "$UNICORN_MODE" = "" ]; then +if [ "$AFL_SKIP_BIN_CHECK" = "" -a "$QEMU_MODE" = "" -a "$FRIDA_MODE" = "" -a "$UNICORN_MODE" = "" -a "$NYX_MODE" = "" ]; then if ! grep -qF "__AFL_SHM_ID" "$TARGET_BIN"; then echo "[-] Error: binary '$TARGET_BIN' doesn't appear to be instrumented." 1>&2 diff --git a/afl-persistent-config b/afl-persistent-config index fd453cbc..6d96c196 100755 --- a/afl-persistent-config +++ b/afl-persistent-config @@ -111,12 +111,12 @@ kernel.sched_latency_ns=250000000 EOF } - egrep -q '^GRUB_CMDLINE_LINUX_DEFAULT=' /etc/default/grub 2>/dev/null || echo Error: /etc/default/grub with GRUB_CMDLINE_LINUX_DEFAULT is not present, cannot set boot options - egrep -q '^GRUB_CMDLINE_LINUX_DEFAULT=' /etc/default/grub 2>/dev/null && { - egrep '^GRUB_CMDLINE_LINUX_DEFAULT=' /etc/default/grub | egrep -q hardened_usercopy=off || { + grep -E -q '^GRUB_CMDLINE_LINUX_DEFAULT=' /etc/default/grub 2>/dev/null || echo Error: /etc/default/grub with GRUB_CMDLINE_LINUX_DEFAULT is not present, cannot set boot options + grep -E -q '^GRUB_CMDLINE_LINUX_DEFAULT=' /etc/default/grub 2>/dev/null && { + grep -E '^GRUB_CMDLINE_LINUX_DEFAULT=' /etc/default/grub | grep -E -q 'noibrs pcid nopti' || { echo "Configuring performance boot options" - LINE=`egrep '^GRUB_CMDLINE_LINUX_DEFAULT=' /etc/default/grub | sed 's/^GRUB_CMDLINE_LINUX_DEFAULT=//' | tr -d '"'` - OPTIONS="$LINE ibpb=off ibrs=off kpti=off l1tf=off mds=off mitigations=off no_stf_barrier noibpb noibrs nopcid nopti nospec_store_bypass_disable nospectre_v1 nospectre_v2 pcid=off pti=off spec_store_bypass_disable=off spectre_v2=off stf_barrier=off srbds=off noexec=off noexec32=off tsx=on tsx=on tsx_async_abort=off mitigations=off audit=0 hardened_usercopy=off ssbd=force-off" + LINE=`grep -E '^GRUB_CMDLINE_LINUX_DEFAULT=' /etc/default/grub | sed 's/^GRUB_CMDLINE_LINUX_DEFAULT=//' | tr -d '"'` + OPTIONS="$LINE ibpb=off ibrs=off kpti=off l1tf=off mds=off mitigations=off no_stf_barrier noibpb noibrs pcid nopti nospec_store_bypass_disable nospectre_v1 nospectre_v2 pcid=on pti=off spec_store_bypass_disable=off spectre_v2=off stf_barrier=off srbds=off noexec=off noexec32=off tsx=on tsx=on tsx_async_abort=off mitigations=off audit=0 hardened_usercopy=off ssbd=force-off" echo Setting boot options in /etc/default/grub to GRUB_CMDLINE_LINUX_DEFAULT=\"$OPTIONS\" sed -i "s|^GRUB_CMDLINE_LINUX_DEFAULT=.*|GRUB_CMDLINE_LINUX_DEFAULT=\"$OPTIONS\"|" /etc/default/grub } diff --git a/afl-plot b/afl-plot index 90a46d24..230d3bfe 100755 --- a/afl-plot +++ b/afl-plot @@ -287,9 +287,9 @@ $PLOT_EG _EOF_ -) | gnuplot +) | gnuplot || echo "Note: if you see errors concerning 'unknown or ambiguous terminal type' then you need to use a gnuplot that has png support compiled in." -echo "[?] You can also use -g flag to view the plots in an GUI window, and interact with the plots (if you have built afl-plot-ui). Run \"afl-plot-h\" to know more." +echo "[?] You can also use -g flag to view the plots in an GUI window, and interact with the plots (if you have built afl-plot-ui). Run \"afl-plot -h\" to know more." fi diff --git a/afl-system-config b/afl-system-config index f482e4fb..bf6397fa 100755 --- a/afl-system-config +++ b/afl-system-config @@ -47,9 +47,9 @@ if [ "$PLATFORM" = "Linux" ] ; then } > /dev/null echo Settings applied. echo - dmesg | egrep -q 'nospectre_v2|spectre_v2=off' || { + dmesg | grep -E -q 'noibrs pcid nopti' || { echo It is recommended to boot the kernel with lots of security off - if you are running a machine that is in a secured network - so set this: - echo ' /etc/default/grub:GRUB_CMDLINE_LINUX_DEFAULT="ibpb=off ibrs=off kpti=0 l1tf=off mds=off mitigations=off no_stf_barrier noibpb noibrs nopcid nopti nospec_store_bypass_disable nospectre_v1 nospectre_v2 pcid=off pti=off spec_store_bypass_disable=off spectre_v2=off stf_barrier=off srbds=off noexec=off noexec32=off tsx=on tsx_async_abort=off arm64.nopauth audit=0 hardened_usercopy=off ssbd=force-off"' + echo ' /etc/default/grub:GRUB_CMDLINE_LINUX_DEFAULT="ibpb=off ibrs=off kpti=0 l1tf=off mds=off mitigations=off no_stf_barrier noibpb noibrs pcid nopti nospec_store_bypass_disable nospectre_v1 nospectre_v2 pcid=on pti=off spec_store_bypass_disable=off spectre_v2=off stf_barrier=off srbds=off noexec=off noexec32=off tsx=on tsx_async_abort=off arm64.nopauth audit=0 hardened_usercopy=off ssbd=force-off"' echo } echo If you run fuzzing instances in docker, run them with \"--security-opt seccomp=unconfined\" for more speed. diff --git a/afl-whatsup b/afl-whatsup index 160a8c74..cec1ae28 100755 --- a/afl-whatsup +++ b/afl-whatsup @@ -6,7 +6,7 @@ # Originally written by Michal Zalewski # # Copyright 2015 Google Inc. All rights reserved. -# Copyright 2019-2022 AFLplusplus Project. All rights reserved. +# Copyright 2019-2023 AFLplusplus Project. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -70,10 +70,10 @@ if [ -d queue ]; then fi -RED=`tput setaf 9 1 1` -GREEN=`tput setaf 2 1 1` -BLUE=`tput setaf 4 1 1` -YELLOW=`tput setaf 11 1 1` +RED=`tput setaf 9 1 1 2>/dev/null` +GREEN=`tput setaf 2 1 1 2>/dev/null` +BLUE=`tput setaf 4 1 1 2>/dev/null` +YELLOW=`tput setaf 11 1 1 2>/dev/null` NC=`tput sgr0` RESET="$NC" @@ -141,7 +141,8 @@ for i in `find . -maxdepth 2 -iname fuzzer_stats | sort`; do sed 's/^command_line.*$/_skip:1/;s/[ ]*:[ ]*/="/;s/$/"/' "$i" >"$TMP" . "$TMP" - + DIR=$(dirname "$i") + DIR=${DIR##*/} RUN_UNIX=$run_time RUN_DAYS=$((RUN_UNIX / 60 / 60 / 24)) RUN_HRS=$(((RUN_UNIX / 60 / 60) % 24)) @@ -154,7 +155,7 @@ for i in `find . -maxdepth 2 -iname fuzzer_stats | sort`; do if [ "$SUMMARY_ONLY" = "" ]; then - echo ">>> $afl_banner ($RUN_DAYS days, $RUN_HRS hrs) fuzzer PID: $fuzzer_pid <<<" + echo ">>> $afl_banner instance: $DIR ($RUN_DAYS days, $RUN_HRS hrs) fuzzer PID: $fuzzer_pid <<<" echo fi diff --git a/custom_mutators/README.md b/custom_mutators/README.md index 0289e150..8d01856f 100644 --- a/custom_mutators/README.md +++ b/custom_mutators/README.md @@ -11,6 +11,16 @@ The `./examples` folder contains examples for custom mutators in python and C. In `./rust`, you will find rust bindings, including a simple example in `./rust/example` and an example for structured fuzzing, based on lain, in`./rust/example_lain`. +## The AFL++ grammar agnostic grammar mutator + +In `./autotokens` you find a token-level fuzzer that does not need to know +anything about the grammar of an input as long as it is in ascii and allows +whitespace. +It is very fast and effective. + +If you are looking for an example of how to effectively create a custom +mutator take a look at this one. + ## The AFL++ Grammar Mutator If you use git to clone AFL++, then the following will incorporate our diff --git a/custom_mutators/autotokens/Makefile b/custom_mutators/autotokens/Makefile new file mode 100644 index 00000000..0daba17d --- /dev/null +++ b/custom_mutators/autotokens/Makefile @@ -0,0 +1,26 @@ +ifdef debug + CPPLAGS += -fsanitize=address + CXXFLAGS += -Wall + CC := clang + CXX := clang++ +endif +ifdef DEBUG + CPPFLAGS += -fsanitize=address + CXXFLAGS += -Wall + CC := clang + CXX := clang++ +endif + +all: autotokens.so + +afl-fuzz-queue.o: ../../src/afl-fuzz-queue.c + $(CC) -D_STANDALONE_MODULE=1 -I../../include -g -O3 $(CPPFLAGS) -fPIC -c -o ./afl-fuzz-queue.o ../../src/afl-fuzz-queue.c + +afl-common.o: ../../src/afl-common.c + $(CC) -I../../include -g -O3 $(CPPFLAGS) -DBIN_PATH=\"dummy\" -Wno-pointer-sign -fPIC -c -o ./afl-common.o ../../src/afl-common.c + +autotokens.so: afl-fuzz-queue.o afl-common.o autotokens.cpp + $(CXX) -Wno-deprecated -g -O3 $(CXXFLAGS) $(CPPFLAGS) -shared -fPIC -o autotokens.so -I../../include autotokens.cpp ./afl-fuzz-queue.o ../../src/afl-performance.o ./afl-common.o + +clean: + rm -f autotokens.so *.o *~ core diff --git a/custom_mutators/autotokens/README b/custom_mutators/autotokens/README new file mode 100644 index 00000000..cca168fd --- /dev/null +++ b/custom_mutators/autotokens/README @@ -0,0 +1,34 @@ +# Autotokens + +This implements an improved autotoken grammar fuzzing idea presented in +[Token-Level Fuzzing][https://www.usenix.org/system/files/sec21-salls.pdf]. +It is a grammar fuzzer without actually knowing the grammar, but only works +with text based inputs. + +It is recommended to run with together in an instance with `CMPLOG`. + +If you have a dictionary (`-x`) this improves this custom grammar mutator. + +If **not** running with `CMPLOG`, it is possible to set +`AFL_CUSTOM_MUTATOR_ONLY` to concentrate on grammar bug classes. + +Do **not** set `AFL_DISABLE_TRIM` with this custom mutator! + +## Configuration via environment variables + +`AUTOTOKENS_ONLY_FAV` - only use this mutator on favorite queue items +`AUTOTOKENS_COMMENT` - what character or string starts a comment which will be + removed. Default: `/* ... */` +`AUTOTOKENS_FUZZ_COUNT_SHIFT` - reduce the number of fuzzing performed, shifting + the value by this number, e.g. 1. +`AUTOTOKENS_AUTO_DISABLE` - disable this module if the seeds are not ascii + (or no input and no (ascii) dictionary) +`AUTOTOKENS_LEARN_DICT` - learn from dictionaries? + 0 = none + 1 = only -x or autodict + 2 = -x, autodict and `CMPLOG` +`AUTOTOKENS_CHANGE_MIN` - minimum number of mutations (1-256, default 8) +`AUTOTOKENS_CHANGE_MAX` - maximum number of mutations (1-4096, default 64) +`AUTOTOKENS_CREATE_FROM_THIN_AIR` - if only one small start file is present and + a dictionary loaded then create one initial + structure based on the dictionary. diff --git a/custom_mutators/autotokens/autotokens.cpp b/custom_mutators/autotokens/autotokens.cpp new file mode 100644 index 00000000..8135aba1 --- /dev/null +++ b/custom_mutators/autotokens/autotokens.cpp @@ -0,0 +1,1101 @@ +/* + token level fuzzing custom mutator for afl++ + (c) by Marc Heuse <mh@mh-sec.de> + License: Apache 2.0 +*/ + +extern "C" { + +#include "afl-fuzz.h" + +} + +#include <stdio.h> +#include <stdint.h> +#include <stdlib.h> + +#include <iostream> +#include <fstream> +#include <unordered_map> +#include <vector> +#include <regex> + +#define AUTOTOKENS_DEBUG 0 +#define AUTOTOKENS_ONLY_FAV 0 +#define AUTOTOKENS_CHANGE_MIN 8 +#define AUTOTOKENS_CHANGE_MAX 64 +#define AUTOTOKENS_SIZE_MIN 8 +#define AUTOTOKENS_SIZE_MAX 65535 +#define AUTOTOKENS_SPLICE_MIN 4 +#define AUTOTOKENS_SPLICE_MAX 64 +#define AUTOTOKENS_CREATE_FROM_THIN_AIR 0 +#define AUTOTOKENS_FUZZ_COUNT_SHIFT 0 +#define AUTOTOKENS_AUTO_DISABLE 0 +// 0 = no learning, 1 only from -x dict/autodict, 2 also from cmplog +#define AUTOTOKENS_LEARN_DICT 1 +#ifndef AUTOTOKENS_SPLICE_DISABLE + #define AUTOTOKENS_SPLICE_DISABLE 0 +#endif +#ifndef AFL_TXT_MAX_LEN + #define AFL_TXT_MAX_LEN 65535 +#endif + +#if AUTOTOKENS_SPLICE_MIN >= AUTOTOKENS_SIZE_MIN + #error SPLICE_MIN must be lower than SIZE_MIN +#endif + +using namespace std; + +typedef struct my_mutator { + + afl_state *afl; + +} my_mutator_t; + +#undef DEBUGF +#define DEBUGF \ + if (unlikely(debug)) fprintf +#define IFDEBUG if (unlikely(debug)) + +static afl_state *afl_ptr; +static int module_disabled = 0; +static int auto_disable = AUTOTOKENS_AUTO_DISABLE; +static int debug = AUTOTOKENS_DEBUG; +static int only_fav = AUTOTOKENS_ONLY_FAV; +static int learn_dictionary_tokens = AUTOTOKENS_LEARN_DICT; +static int fuzz_count_shift = AUTOTOKENS_FUZZ_COUNT_SHIFT; +static int create_from_thin_air = AUTOTOKENS_CREATE_FROM_THIN_AIR; +static int change_min = AUTOTOKENS_CHANGE_MIN; +static int change_max = AUTOTOKENS_CHANGE_MAX; +static u32 current_id; +static u32 valid_structures; +static u32 whitespace_ids; +static u32 extras_cnt, a_extras_cnt; +static u64 all_spaces, all_tabs, all_lf, all_ws; +static u64 all_structure_items; +static u64 fuzz_count; +static unordered_map<string, vector<u32> *> file_mapping; +static unordered_map<u32, vector<u32> *> id_mapping; +static unordered_map<string, u32> token_to_id; +static unordered_map<u32, string> id_to_token; +static string output; +static regex *regex_comment_custom; +// multiline requires g++-11 libs :( +static regex regex_comment_star( + "/\\*([:print:]|\n)*?\\*/", + regex_constants::optimize /* | regex_constants::multiline */); +static regex regex_word("[A-Za-z0-9_$.-]+", regex::optimize); +static regex regex_whitespace(R"([ \t]+)", regex::optimize); +static vector<u32> *s; // the structure of the currently selected input + +// FUNCTIONS + +/* This function is called once after everything is set up but before + any fuzzing attempt has been performed. + This is called in afl_custom_queue_get() */ +static void first_run(void *data) { + + (void)(data); + + /* For auto-loading this module we check here if we can analyze from the + input if the inputs look like text inputs and disable the module if + not. */ + + if (afl_ptr->custom_only || !auto_disable) { return; } + + if (unlikely(afl_ptr->active_items == 1 && + afl_ptr->queue_cur->len < AFL_TXT_MIN_LEN)) { + + if (afl_ptr->extras_cnt > 8) { + + u32 valid = 0; + + while (extras_cnt < afl_ptr->extras_cnt) { + + u32 ok = 1, l = afl_ptr->extras[extras_cnt].len; + u8 *buf, *ptr = afl_ptr->extras[extras_cnt].data; + + for (u32 i = 0; i < l; ++i) { + + if (!isascii((int)ptr[i]) && !isprint((int)ptr[i])) { + + ok = 0; + break; + + } + + } + + if (ok) { + + buf = (u8 *)malloc(afl_ptr->extras[extras_cnt].len + 1); + memcpy(buf, afl_ptr->extras[extras_cnt].data, + afl_ptr->extras[extras_cnt].len); + buf[afl_ptr->extras[extras_cnt].len] = 0; + token_to_id[(char *)buf] = current_id; + id_to_token[current_id] = (char *)buf; + ++current_id; + ++valid; + + } + + ++extras_cnt; + + } + + if ((valid * 100) / afl_ptr->extras_cnt <= 70) { module_disabled = 1; } + + DEBUGF(stderr, "DICT: total %u, valid %u, %u <= 70 == disable\n", + afl_ptr->extras_cnt, valid, + (u32)((valid * 100) / afl_ptr->extras_cnt)); + + } else { + + module_disabled = 1; + + } + + return; + + } + + u32 is_ascii = 0, valid = 0; + + for (u32 i = 0; i < afl_ptr->queued_items; ++i) { + + struct queue_entry *q; + + q = afl_ptr->queue_buf[i]; + + if (!q->disabled && q->len >= AUTOTOKENS_SIZE_MIN && + q->len <= AFL_TXT_MAX_LEN) { + + ++valid; + u8 *input = queue_testcase_get(afl_ptr, q); + + u32 valid_chars = 0; + for (u32 i = 0; i < q->len; ++i) { + + if (isascii((int)input[i]) || isprint((int)input[i])) { ++valid_chars; } + + } + + // we want at least 99% of text characters ... + if (((q->len * AFL_TXT_MIN_PERCENT) / 100) <= valid_chars) { + + ++is_ascii; + q->is_ascii = 1; + + } + + } + + } + + if ((is_ascii * 100) / valid <= 70) { module_disabled = 1; } + + DEBUGF(stderr, "seeds: total %u, valid %u, ascii %u, %u <= 70 == disabled\n", + afl_ptr->active_items, valid, is_ascii, + (u32)((is_ascii * 100) / valid)); + +} + +static u32 good_whitespace_or_singleval() { + + u32 i = rand_below(afl_ptr, current_id); + if (id_to_token[i].size() == 1) { return i; } + i = rand_below(afl_ptr, all_ws); + if (i < all_spaces) { + + return 0; + + } else if (i < all_tabs) { + + return 1; + + } else + + return 2; // linefeed + +} + +extern "C" u32 afl_custom_fuzz_count(void *data, const u8 *buf, + size_t buf_size) { + + (void)(data); + + if (s == NULL) return 0; + + u32 shift = unlikely(afl_ptr->custom_only) ? 7 : 8; + u32 stage_max = (u32)((HAVOC_CYCLES * afl_ptr->queue_cur->perf_score) / + afl_ptr->havoc_div) >> + shift; + if (fuzz_count_shift) { stage_max >>= (u32)fuzz_count_shift; }; + DEBUGF(stderr, "fuzz count: %u\n", stage_max); + + return stage_max; + +} + +extern "C" size_t afl_custom_fuzz(my_mutator_t *data, u8 *buf, size_t buf_size, + u8 **out_buf, u8 *add_buf, + size_t add_buf_size, size_t max_size) { + + (void)(data); + + if (unlikely(s == NULL)) { + + *out_buf = NULL; + return 0; + + } + + vector<u32> m = *s; // copy of the structure we will modify + u32 i, m_size = (u32)m.size(); + + u32 rounds = + MIN(change_max, + MAX(change_min, + MIN(m_size >> 3, HAVOC_CYCLES * afl_ptr->queue_cur->perf_score * + afl_ptr->havoc_div / 256))); + // DEBUGF(stderr, "structure size: %lu, rounds: %u \n", m.size(), rounds); + +#if AUTOTOKENS_SPLICE_DISABLE == 1 + #define AUTOTOKENS_MUT_MAX 18 +#else + #define AUTOTOKENS_MUT_MAX 27 +#endif + + u32 max_rand = AUTOTOKENS_MUT_MAX, new_item, pos; + + for (i = 0; i < rounds; ++i) { + + switch (rand_below(afl_ptr, max_rand)) { + + /* CHANGE/MUTATE single item */ + case 0 ... 9: { + + pos = rand_below(afl_ptr, m_size); + u32 cur_item = m[pos]; + do { + + new_item = rand_below(afl_ptr, current_id); + + } while (unlikely( + + new_item == cur_item || + ((whitespace_ids < new_item && whitespace_ids >= cur_item) || + (whitespace_ids >= new_item && whitespace_ids < cur_item)))); + + // DEBUGF(stderr, "MUT: %u -> %u\n", cur_item, new_item); + m[pos] = new_item; + break; + + } + + /* INSERT (m_size +1 so we insert also after last place) */ + case 10 ... 13: { + + do { + + new_item = rand_below(afl_ptr, current_id); + + } while (unlikely(new_item >= whitespace_ids)); + + u32 pos = rand_below(afl_ptr, m_size + 1); + m.insert(m.begin() + pos, new_item); + ++m_size; + // DEBUGF(stderr, "INS: %u at %u\n", new_item, pos); + + break; + + } + +#if AUTOTOKENS_SPLICE_DISABLE != 1 + /* SPLICING */ + case 14 ... 22: { + + u32 strategy = rand_below(afl_ptr, 4), dst_off, n; + auto src = id_mapping[rand_below(afl_ptr, valid_structures)]; + u32 src_size = src->size(); + u32 src_off = rand_below(afl_ptr, src_size - AUTOTOKENS_SPLICE_MIN); + u32 rand_r = 1 + MAX(AUTOTOKENS_SPLICE_MIN, + MIN(AUTOTOKENS_SPLICE_MAX, src_size - src_off)); + + switch (strategy) { + + // insert + case 0: { + + dst_off = rand_below(afl_ptr, m_size); + n = AUTOTOKENS_SPLICE_MIN + + rand_below(afl_ptr, MIN(AUTOTOKENS_SPLICE_MAX, + rand_r - AUTOTOKENS_SPLICE_MIN)); + m.insert(m.begin() + dst_off, src->begin() + src_off, + src->begin() + src_off + n); + m_size += n; + // DEBUGF(stderr, "SPLICE-INS: %u at %u\n", n, dst_off); + + break; + + } + + // overwrite + default: { + + dst_off = rand_below(afl_ptr, m_size - AUTOTOKENS_SPLICE_MIN); + n = AUTOTOKENS_SPLICE_MIN + + rand_below( + afl_ptr, + MIN(AUTOTOKENS_SPLICE_MAX - AUTOTOKENS_SPLICE_MIN, + MIN(m_size - dst_off - AUTOTOKENS_SPLICE_MIN, + src_size - src_off - AUTOTOKENS_SPLICE_MIN))); + + copy(src->begin() + src_off, src->begin() + src_off + n, + m.begin() + dst_off); + + // DEBUGF(stderr, "SPLICE-MUT: %u at %u\n", n, dst_off); + break; + + } + + } + + break; + + } + +#endif + + /* ERASE - only if large enough */ + default: { + + if (m_size > 8) { + + do { + + pos = rand_below(afl_ptr, m_size); + + } while (unlikely(m[pos] < whitespace_ids)); + + m.erase(m.begin() + pos); + --m_size; + + } else { + + // if the data is already too small do not try to make it smaller + // again this run. + + max_rand -= 4; + + } + + break; + + } + + } + + } + + /* Now we create the output */ + + output = ""; + u32 prev_size = 1, was_whitespace = 1; + + for (i = 0; i < m_size; ++i) { + + if (likely(i + 1 < m_size)) { + + u32 this_size = id_to_token[m[i]].size(); + u32 is_whitespace = m[i] < whitespace_ids; + + /* The output we are generating might need repairing. + General rule: two items that have a size larger than 2 are strings + or identifizers and need a whitespace or an item of length 1 in + between. */ + if (unlikely(!(prev_size == 1 || was_whitespace || this_size == 1 || + is_whitespace))) { + + output += id_to_token[good_whitespace_or_singleval()]; + + } + + prev_size = this_size; + was_whitespace = is_whitespace; + + } + + output += id_to_token[m[i]]; + + } + + u32 mutated_size = (u32)output.size(); + u8 *mutated_out = (u8 *)output.data(); + + if (unlikely(mutated_size > max_size)) { mutated_size = max_size; } + + /* + IFDEBUG { + + DEBUGF(stderr, "MUTATED to %u bytes:\n", mutated_size); + fwrite(output.data(), 1, mutated_size, stderr); + DEBUGF(stderr, "\n---\n"); + + } + + */ + + *out_buf = mutated_out; + ++fuzz_count; + return mutated_size; + +} + +/* I get f*cking stack overflow using C++ regex with a regex of + "\"[[:print:]]*?\"" if this matches a long string even with regex::optimize + enabled :-( */ +static u8 my_search_string(string::const_iterator cur, + string::const_iterator ende, + string::const_iterator *match_begin, + string::const_iterator *match_end) { + + string::const_iterator start = cur, found_begin; + u8 quote_type = 0; + + while (cur < ende) { + + switch (*cur) { + + case '"': { + + if (cur == start || *(cur - 1) != '\\') { + + if (!quote_type) { + + found_begin = cur; + quote_type = 1; + + } else if (quote_type == 1) { + + *match_begin = found_begin; + *match_end = cur + 1; + return 1; + + } + + } + + break; + + } + + case '\'': { + + if (cur == start || *(cur - 1) != '\\') { + + if (!quote_type) { + + found_begin = cur; + quote_type = 2; + + } else if (quote_type == 2) { + + *match_begin = found_begin; + *match_end = cur + 1; + return 1; + + } + + } + + break; + + } + + case '\n': + case '\r': + case 0: { + + quote_type = 0; + break; + + } + + default: + if (unlikely(quote_type && !isprint(*cur))) { quote_type = 0; } + break; + + } + + ++cur; + + } + + return 0; + +} + +/* We are not using afl_custom_queue_new_entry() because not every corpus entry + will be necessarily fuzzed with this custom mutator. + So we use afl_custom_queue_get() instead. */ + +extern "C" unsigned char afl_custom_queue_get(void *data, + const unsigned char *filename) { + + static int learn_state = 0; + static int is_first_run = 1; + (void)(data); + + if (unlikely(is_first_run)) { + + is_first_run = 0; + first_run(data); + + if (module_disabled) { + + WARNF("Autotokens custom module is disabled."); + + } else if (auto_disable) { + + OKF("Autotokens custom module is enabled."); + + } + + } + + if (likely(module_disabled) || + (unlikely(!afl_ptr->custom_only) && !create_from_thin_air && + ((afl_ptr->shm.cmplog_mode && !afl_ptr->queue_cur->is_ascii) || + (only_fav && !afl_ptr->queue_cur->favored)))) { + + s = NULL; + DEBUGF(stderr, + "cmplog not ascii or only_fav and not favorite or disabled\n"); + return 1; + + } + + // check if there are new dictionary entries and add them to the tokens + if (unlikely(learn_state < learn_dictionary_tokens) && + likely(valid_structures || create_from_thin_air)) { + + if (unlikely(!learn_state)) { learn_state = 1; } + + while (extras_cnt < afl_ptr->extras_cnt) { + + u32 ok = 1, l = afl_ptr->extras[extras_cnt].len; + u8 *buf, *ptr = afl_ptr->extras[extras_cnt].data; + + for (u32 i = 0; i < l; ++i) { + + if (!isascii((int)ptr[i]) && !isprint((int)ptr[i])) { + + ok = 0; + break; + + } + + } + + if (ok) { + + buf = (u8 *)malloc(afl_ptr->extras[extras_cnt].len + 1); + memcpy(buf, afl_ptr->extras[extras_cnt].data, + afl_ptr->extras[extras_cnt].len); + buf[afl_ptr->extras[extras_cnt].len] = 0; + token_to_id[(char *)buf] = current_id; + id_to_token[current_id] = (char *)buf; + ++current_id; + + } + + ++extras_cnt; + + } + + while (a_extras_cnt < afl_ptr->a_extras_cnt) { + + u32 ok = 1, l = afl_ptr->a_extras[a_extras_cnt].len; + u8 *ptr = afl_ptr->a_extras[a_extras_cnt].data; + + for (u32 i = 0; i < l; ++i) { + + if (!isascii((int)ptr[i]) && !isprint((int)ptr[i])) { + + ok = 0; + break; + + } + + } + + if (ok) { + + token_to_id[(char *)ptr] = current_id; + id_to_token[current_id] = (char *)ptr; + ++current_id; + + } + + ++a_extras_cnt; + + } + + } + + vector<u32> *structure = NULL; + string fn = (char *)filename; + auto entry = file_mapping.find(fn); + + // if there is only one active queue item at start and it is very small + // the we create once a structure randomly. + if (unlikely(create_from_thin_air)) { + + if (current_id > whitespace_ids + 6 && afl_ptr->active_items == 1 && + afl_ptr->queue_cur->len < AFL_TXT_MIN_LEN) { + + DEBUGF(stderr, "Creating an entry from thin air...\n"); + structure = new vector<u32>(); + u32 item, prev, cnt = current_id >> 1; + structure->reserve(cnt + 4); + for (u32 i = 0; i < cnt; i++) { + + item = rand_below(afl_ptr, current_id); + if (i && id_to_token[item].length() > 1 && + id_to_token[prev].length() > 1) { + + structure->push_back(good_whitespace_or_singleval()); + + } + + structure->push_back(item); + prev = item; + + } + + s = structure; + file_mapping[fn] = structure; + id_mapping[valid_structures] = structure; + ++valid_structures; + all_structure_items += structure->size(); + + return 1; + + } + + create_from_thin_air = 0; + + } + + if (entry == file_mapping.end()) { + + // this input file was not analyzed for tokens yet, so let's do it! + size_t len = afl_ptr->queue_cur->len; + + if (len < AFL_TXT_MIN_LEN) { + + file_mapping[fn] = structure; // NULL ptr so we don't read the file again + s = NULL; + DEBUGF(stderr, "Too short (%lu) %s\n", len, filename); + return 1; + + } else if (len > AFL_TXT_MAX_LEN) { + + file_mapping[fn] = structure; // NULL ptr so we don't read the file again + s = NULL; + DEBUGF(stderr, "Too long (%lu) %s\n", len, filename); + return 1; + + } + + u8 *input_buf = queue_testcase_get(afl_ptr, afl_ptr->queue_cur); + string input((char *)input_buf, afl_ptr->queue_cur->len); + + if (!afl_ptr->shm.cmplog_mode) { + + // not running with CMPLOG? bad choice, but whatever ... + // we only want text inputs, so we have to check it ourselves. + + u32 valid_chars = 0; + for (u32 i = 0; i < len; ++i) { + + if (isascii((int)input[i]) || isprint((int)input[i])) { ++valid_chars; } + + } + + // we want at least 95% of text characters ... + if (((len * AFL_TXT_MIN_PERCENT) / 100) > valid_chars) { + + file_mapping[fn] = NULL; + s = NULL; + DEBUGF(stderr, "Not text (%lu) %s\n", len, filename); + return 1; + + } + + } + + // DEBUGF(stderr, "Read %lu bytes for %s\nBefore comment trim:\n%s\n", + // input.size(), filename, input.c_str()); + + if (regex_comment_custom) { + + input = regex_replace(input, *regex_comment_custom, "$2"); + + } else { + + input = regex_replace(input, regex_comment_star, ""); + + } + + DEBUGF(stderr, "After replace %lu bytes for %s\n%s\n", input.size(), + filename, input.c_str()); + + u32 spaces = count(input.begin(), input.end(), ' '); + u32 tabs = count(input.begin(), input.end(), '\t'); + u32 linefeeds = count(input.begin(), input.end(), '\n'); + bool ends_with_linefeed = input[input.length() - 1] == '\n'; + + DEBUGF(stderr, "spaces=%u tabs=%u linefeeds=%u ends=%u\n", spaces, tabs, + linefeeds, ends_with_linefeed); + + all_spaces += spaces; + all_tabs += tabs; + all_lf += linefeeds; + all_ws = all_spaces + all_tabs + all_lf; + + // now extract all tokens + vector<string> tokens; + string::const_iterator cur = input.begin(), ende = input.end(), found, prev, + match_begin, match_end; + + DEBUGF(stderr, "START!\n"); + + while (my_search_string(cur, ende, &match_begin, &match_end)) { + + prev = cur; + found = match_begin; + cur = match_end; + + IFDEBUG { + + string foo(match_begin, match_end); + DEBUGF(stderr, + "string %s found at start %lu offset %lu continue at %lu\n", + foo.c_str(), prev - input.begin(), found - prev, + cur - input.begin()); + + } + + if (prev < found) { // there are items between search start and find + while (prev < found) { + + if (isspace(*prev)) { + + auto start = prev; + while (isspace(*prev)) { + + ++prev; + + } + + tokens.push_back(std::string(start, prev)); + DEBUGF(stderr, "WHITESPACE %ld \"%s\"\n", prev - start, + tokens[tokens.size() - 1].c_str()); + + } else if (isalnum(*prev) || *prev == '$' || *prev == '_') { + + auto start = prev; + while (isalnum(*prev) || *prev == '$' || *prev == '_' || + *prev == '.' || *prev == '/') { + + ++prev; + + } + + tokens.push_back(string(start, prev)); + DEBUGF(stderr, "IDENTIFIER %ld \"%s\"\n", prev - start, + tokens[tokens.size() - 1].c_str()); + + } else { + + tokens.push_back(string(prev, prev + 1)); + DEBUGF(stderr, "OTHER \"%c\"\n", *prev); + ++prev; + + } + + } + + } + + tokens.push_back(string(match_begin, match_end)); + DEBUGF(stderr, "TOK: %s\n", tokens[tokens.size() - 1].c_str()); + + } + + DEBUGF(stderr, "AFTER all strings\n"); + + if (cur < ende) { + + while (cur < ende) { + + if (isspace(*cur)) { + + auto start = cur; + while (isspace(*cur)) { + + ++cur; + + } + + tokens.push_back(std::string(start, cur)); + DEBUGF(stderr, "WHITESPACE %ld \"%s\"\n", cur - start, + tokens[tokens.size() - 1].c_str()); + + } else if (isalnum(*cur) || *cur == '$' || *cur == '_') { + + auto start = cur; + while (isalnum(*cur) || *cur == '$' || *cur == '_' || *cur == '.' || + *cur == '/') { + + ++cur; + + } + + tokens.push_back(std::string(start, cur)); + DEBUGF(stderr, "IDENTIFIER %ld \"%s\"\n", cur - start, + tokens[tokens.size() - 1].c_str()); + + } else { + + tokens.push_back(std::string(cur, cur + 1)); + DEBUGF(stderr, "OTHER \"%c\"\n", *cur); + ++cur; + + } + + } + + } + + IFDEBUG { + + DEBUGF(stderr, "DUMPING TOKENS:\n"); + for (u32 i = 0; i < tokens.size(); ++i) { + + DEBUGF(stderr, "%s", tokens[i].c_str()); + + } + + DEBUGF(stderr, "---------------------------\n"); + + } + + if (tokens.size() < AUTOTOKENS_SIZE_MIN) { + + file_mapping[fn] = NULL; + s = NULL; + DEBUGF(stderr, "too few tokens\n"); + return 1; + + } + + /* Now we transform the tokens into an ID list and saved that */ + + structure = new vector<u32>(); + u32 id; + + for (u32 i = 0; i < tokens.size(); ++i) { + + if ((id = token_to_id[tokens[i]]) == 0) { + + // First time we see this token, add it to the list + token_to_id[tokens[i]] = current_id; + id_to_token[current_id] = tokens[i]; + structure->push_back(current_id); + ++current_id; + + } else { + + structure->push_back(id); + + } + + } + + // save the token structure to the file mapping + file_mapping[fn] = structure; + id_mapping[valid_structures] = structure; + ++valid_structures; + s = structure; + all_structure_items += structure->size(); + + // we are done! + DEBUGF(stderr, "DONE! We have %lu tokens in the structure\n", + structure->size()); + + } else { + + if (entry->second == NULL) { + + DEBUGF(stderr, "Skipping %s\n", filename); + s = NULL; + return 1; + + } + + s = entry->second; + DEBUGF(stderr, "OK %s\n", filename); + + } + + return 1; // we always fuzz unless non-ascii or too small + +} + +extern "C" my_mutator_t *afl_custom_init(afl_state *afl, unsigned int seed) { + + (void)(seed); + my_mutator_t *data = (my_mutator_t *)calloc(1, sizeof(my_mutator_t)); + if (!data) { + + perror("afl_custom_init alloc"); + return NULL; + + } + + if (getenv("AUTOTOKENS_DEBUG")) { debug = 1; } + if (getenv("AUTOTOKENS_AUTO_DISABLE")) { auto_disable = 1; } + if (getenv("AUTOTOKENS_ONLY_FAV")) { only_fav = 1; } + if (getenv("AUTOTOKENS_CREATE_FROM_THIN_AIR")) { create_from_thin_air = 1; } + + if (getenv("AUTOTOKENS_LEARN_DICT")) { + + learn_dictionary_tokens = atoi(getenv("AUTOTOKENS_LEARN_DICT")); + if (learn_dictionary_tokens < 0 || learn_dictionary_tokens > 2) { + + learn_dictionary_tokens = AUTOTOKENS_LEARN_DICT; + + } + + } + + if (getenv("AUTOTOKENS_FUZZ_COUNT_SHIFT")) { + + fuzz_count_shift = atoi(getenv("AUTOTOKENS_FUZZ_COUNT_SHIFT")); + if (fuzz_count_shift < 0 || fuzz_count_shift > 16) { fuzz_count_shift = 0; } + + } + + if (getenv("AUTOTOKENS_CHANGE_MIN")) { + + change_min = atoi(getenv("AUTOTOKENS_CHANGE_MIN")); + if (change_min < 1 || change_min > 256) { + + change_min = AUTOTOKENS_CHANGE_MIN; + + } + + } + + if (getenv("AUTOTOKENS_CHANGE_MAX")) { + + change_max = atoi(getenv("AUTOTOKENS_CHANGE_MAX")); + if (change_max < 1 || change_max > 4096) { + + change_max = AUTOTOKENS_CHANGE_MAX; + + } + + } + + if (change_max < change_min) { change_max = change_min + 1; } + + if (getenv("AUTOTOKENS_COMMENT")) { + + char buf[256]; + snprintf(buf, sizeof(buf), "(%s.*)([\r\n]?)", getenv("AUTOTOKENS_COMMENT")); + regex_comment_custom = new regex(buf, regex::optimize); + + } + + data->afl = afl_ptr = afl; + + // set common whitespace tokens + // we deliberately do not put uncommon ones here to these will count as + // identifier tokens. + token_to_id[" "] = current_id; + id_to_token[current_id] = " "; + ++current_id; + token_to_id["\t"] = current_id; + id_to_token[current_id] = "\t"; + ++current_id; + token_to_id["\n"] = current_id; + id_to_token[current_id] = "\n"; + ++current_id; + token_to_id["\r\n"] = current_id; + id_to_token[current_id] = "\r\n"; + ++current_id; + token_to_id[" \n"] = current_id; + id_to_token[current_id] = " \n"; + ++current_id; + token_to_id[" "] = current_id; + id_to_token[current_id] = " "; + ++current_id; + token_to_id["\t\t"] = current_id; + id_to_token[current_id] = "\t\t"; + ++current_id; + token_to_id["\n\n"] = current_id; + id_to_token[current_id] = "\n\n"; + ++current_id; + token_to_id["\r\n\r\n"] = current_id; + id_to_token[current_id] = "\r\n\r\n"; + ++current_id; + token_to_id[" "] = current_id; + id_to_token[current_id] = " "; + ++current_id; + token_to_id["\t\t\t\t"] = current_id; + id_to_token[current_id] = "\t\t\t\t"; + ++current_id; + token_to_id["\n\n\n\n"] = current_id; + id_to_token[current_id] = "\n\n\n\n"; + ++current_id; + whitespace_ids = current_id; + token_to_id["\""] = current_id; + id_to_token[current_id] = "\""; + ++current_id; + token_to_id["'"] = current_id; + id_to_token[current_id] = "'"; + ++current_id; + + return data; + +} + +extern "C" void afl_custom_splice_optout(my_mutator_t *data) { + + (void)(data); + +} + +extern "C" void afl_custom_deinit(my_mutator_t *data) { + + /* we use this to print statistics at exit :-) + needs to be stderr as stdout is filtered */ + + if (module_disabled) { return; } + + fprintf(stderr, + "\n\nAutotoken mutator statistics:\n" + " Number of all seen tokens: %u\n" + " Number of input structures: %u\n" + " Number of all items in structures: %llu\n" + " Number of total fuzzes: %llu\n\n", + current_id - 1, valid_structures, all_structure_items, fuzz_count); + + free(data); + +} + diff --git a/custom_mutators/examples/custom_mutator_helpers.h b/custom_mutators/examples/custom_mutator_helpers.h deleted file mode 100644 index 62e6efba..00000000 --- a/custom_mutators/examples/custom_mutator_helpers.h +++ /dev/null @@ -1,342 +0,0 @@ -#ifndef CUSTOM_MUTATOR_HELPERS -#define CUSTOM_MUTATOR_HELPERS - -#include "config.h" -#include "types.h" -#include <stdlib.h> - -#define INITIAL_GROWTH_SIZE (64) - -#define RAND_BELOW(limit) (rand() % (limit)) - -/* Use in a struct: creates a name_buf and a name_size variable. */ -#define BUF_VAR(type, name) \ - type * name##_buf; \ - size_t name##_size; -/* this fills in `&structptr->something_buf, &structptr->something_size`. */ -#define BUF_PARAMS(struct, name) \ - (void **)&struct->name##_buf, &struct->name##_size - -typedef struct { - -} afl_t; - -static void surgical_havoc_mutate(u8 *out_buf, s32 begin, s32 end) { - - static s8 interesting_8[] = {INTERESTING_8}; - static s16 interesting_16[] = {INTERESTING_8, INTERESTING_16}; - static s32 interesting_32[] = {INTERESTING_8, INTERESTING_16, INTERESTING_32}; - - switch (RAND_BELOW(12)) { - - case 0: { - - /* Flip a single bit somewhere. Spooky! */ - - s32 bit_idx = ((RAND_BELOW(end - begin) + begin) << 3) + RAND_BELOW(8); - - out_buf[bit_idx >> 3] ^= 128 >> (bit_idx & 7); - - break; - - } - - case 1: { - - /* Set byte to interesting value. */ - - u8 val = interesting_8[RAND_BELOW(sizeof(interesting_8))]; - out_buf[(RAND_BELOW(end - begin) + begin)] = val; - - break; - - } - - case 2: { - - /* Set word to interesting value, randomly choosing endian. */ - - if (end - begin < 2) break; - - s32 byte_idx = (RAND_BELOW(end - begin) + begin); - - if (byte_idx >= end - 1) break; - - switch (RAND_BELOW(2)) { - - case 0: - *(u16 *)(out_buf + byte_idx) = - interesting_16[RAND_BELOW(sizeof(interesting_16) >> 1)]; - break; - case 1: - *(u16 *)(out_buf + byte_idx) = - SWAP16(interesting_16[RAND_BELOW(sizeof(interesting_16) >> 1)]); - break; - - } - - break; - - } - - case 3: { - - /* Set dword to interesting value, randomly choosing endian. */ - - if (end - begin < 4) break; - - s32 byte_idx = (RAND_BELOW(end - begin) + begin); - - if (byte_idx >= end - 3) break; - - switch (RAND_BELOW(2)) { - - case 0: - *(u32 *)(out_buf + byte_idx) = - interesting_32[RAND_BELOW(sizeof(interesting_32) >> 2)]; - break; - case 1: - *(u32 *)(out_buf + byte_idx) = - SWAP32(interesting_32[RAND_BELOW(sizeof(interesting_32) >> 2)]); - break; - - } - - break; - - } - - case 4: { - - /* Set qword to interesting value, randomly choosing endian. */ - - if (end - begin < 8) break; - - s32 byte_idx = (RAND_BELOW(end - begin) + begin); - - if (byte_idx >= end - 7) break; - - switch (RAND_BELOW(2)) { - - case 0: - *(u64 *)(out_buf + byte_idx) = - (s64)interesting_32[RAND_BELOW(sizeof(interesting_32) >> 2)]; - break; - case 1: - *(u64 *)(out_buf + byte_idx) = SWAP64( - (s64)interesting_32[RAND_BELOW(sizeof(interesting_32) >> 2)]); - break; - - } - - break; - - } - - case 5: { - - /* Randomly subtract from byte. */ - - out_buf[(RAND_BELOW(end - begin) + begin)] -= 1 + RAND_BELOW(ARITH_MAX); - - break; - - } - - case 6: { - - /* Randomly add to byte. */ - - out_buf[(RAND_BELOW(end - begin) + begin)] += 1 + RAND_BELOW(ARITH_MAX); - - break; - - } - - case 7: { - - /* Randomly subtract from word, random endian. */ - - if (end - begin < 2) break; - - s32 byte_idx = (RAND_BELOW(end - begin) + begin); - - if (byte_idx >= end - 1) break; - - if (RAND_BELOW(2)) { - - *(u16 *)(out_buf + byte_idx) -= 1 + RAND_BELOW(ARITH_MAX); - - } else { - - u16 num = 1 + RAND_BELOW(ARITH_MAX); - - *(u16 *)(out_buf + byte_idx) = - SWAP16(SWAP16(*(u16 *)(out_buf + byte_idx)) - num); - - } - - break; - - } - - case 8: { - - /* Randomly add to word, random endian. */ - - if (end - begin < 2) break; - - s32 byte_idx = (RAND_BELOW(end - begin) + begin); - - if (byte_idx >= end - 1) break; - - if (RAND_BELOW(2)) { - - *(u16 *)(out_buf + byte_idx) += 1 + RAND_BELOW(ARITH_MAX); - - } else { - - u16 num = 1 + RAND_BELOW(ARITH_MAX); - - *(u16 *)(out_buf + byte_idx) = - SWAP16(SWAP16(*(u16 *)(out_buf + byte_idx)) + num); - - } - - break; - - } - - case 9: { - - /* Randomly subtract from dword, random endian. */ - - if (end - begin < 4) break; - - s32 byte_idx = (RAND_BELOW(end - begin) + begin); - - if (byte_idx >= end - 3) break; - - if (RAND_BELOW(2)) { - - *(u32 *)(out_buf + byte_idx) -= 1 + RAND_BELOW(ARITH_MAX); - - } else { - - u32 num = 1 + RAND_BELOW(ARITH_MAX); - - *(u32 *)(out_buf + byte_idx) = - SWAP32(SWAP32(*(u32 *)(out_buf + byte_idx)) - num); - - } - - break; - - } - - case 10: { - - /* Randomly add to dword, random endian. */ - - if (end - begin < 4) break; - - s32 byte_idx = (RAND_BELOW(end - begin) + begin); - - if (byte_idx >= end - 3) break; - - if (RAND_BELOW(2)) { - - *(u32 *)(out_buf + byte_idx) += 1 + RAND_BELOW(ARITH_MAX); - - } else { - - u32 num = 1 + RAND_BELOW(ARITH_MAX); - - *(u32 *)(out_buf + byte_idx) = - SWAP32(SWAP32(*(u32 *)(out_buf + byte_idx)) + num); - - } - - break; - - } - - case 11: { - - /* Just set a random byte to a random value. Because, - why not. We use XOR with 1-255 to eliminate the - possibility of a no-op. */ - - out_buf[(RAND_BELOW(end - begin) + begin)] ^= 1 + RAND_BELOW(255); - - break; - - } - - } - -} - -/* This function calculates the next power of 2 greater or equal its argument. - @return The rounded up power of 2 (if no overflow) or 0 on overflow. -*/ -static inline size_t next_pow2(size_t in) { - - if (in == 0 || in > (size_t)-1) - return 0; /* avoid undefined behaviour under-/overflow */ - size_t out = in - 1; - out |= out >> 1; - out |= out >> 2; - out |= out >> 4; - out |= out >> 8; - out |= out >> 16; - return out + 1; - -} - -/* This function makes sure *size is > size_needed after call. - It will realloc *buf otherwise. - *size will grow exponentially as per: - https://blog.mozilla.org/nnethercote/2014/11/04/please-grow-your-buffers-exponentially/ - Will return NULL and free *buf if size_needed is <1 or realloc failed. - @return For convenience, this function returns *buf. - */ -static inline void *maybe_grow(void **buf, size_t *size, size_t size_needed) { - - /* No need to realloc */ - if (likely(size_needed && *size >= size_needed)) return *buf; - - /* No initial size was set */ - if (size_needed < INITIAL_GROWTH_SIZE) size_needed = INITIAL_GROWTH_SIZE; - - /* grow exponentially */ - size_t next_size = next_pow2(size_needed); - - /* handle overflow */ - if (!next_size) { next_size = size_needed; } - - /* alloc */ - *buf = realloc(*buf, next_size); - *size = *buf ? next_size : 0; - - return *buf; - -} - -/* Swaps buf1 ptr and buf2 ptr, as well as their sizes */ -static inline void afl_swap_bufs(void **buf1, size_t *size1, void **buf2, - size_t *size2) { - - void * scratch_buf = *buf1; - size_t scratch_size = *size1; - *buf1 = *buf2; - *size1 = *size2; - *buf2 = scratch_buf; - *size2 = scratch_size; - -} - -#undef INITIAL_GROWTH_SIZE - -#endif - diff --git a/custom_mutators/examples/custom_send.c b/custom_mutators/examples/custom_send.c new file mode 100644 index 00000000..9cc4b160 --- /dev/null +++ b/custom_mutators/examples/custom_send.c @@ -0,0 +1,63 @@ +// +// This is an example on how to use afl_custom_send +// It writes each mutated data set to /tmp/foo +// You can modify this to send to IPC, shared memory, etc. +// +// cc -O3 -fPIC -shared -g -o custom_send.so -I../../include custom_send.c +// cd ../.. +// afl-cc -o test-instr test-instr.c +// AFL_CUSTOM_MUTATOR_LIBRARY=custom_mutators/examples/custom_send.so \ +// afl-fuzz -i in -o out -- ./test-instr -f /tmp/foo +// + +#include <stdio.h> +#include <stdint.h> +#include <stdlib.h> +#include <unistd.h> +#include <fcntl.h> + +#include "afl-fuzz.h" + +typedef struct my_mutator { + + afl_state_t *afl; + +} my_mutator_t; + +my_mutator_t *afl_custom_init(afl_state_t *afl, unsigned int seed) { + + my_mutator_t *data = calloc(1, sizeof(my_mutator_t)); + if (!data) { + + perror("afl_custom_init alloc"); + return NULL; + + } + + data->afl = afl; + + return data; + +} + +void afl_custom_fuzz_send(my_mutator_t *data, uint8_t *buf, size_t buf_size) { + + int fd = open("/tmp/foo", O_CREAT | O_NOFOLLOW | O_TRUNC | O_RDWR, 0644); + + if (fd >= 0) { + + (void)write(fd, buf, buf_size); + close(fd); + + } + + return; + +} + +void afl_custom_deinit(my_mutator_t *data) { + + free(data); + +} + diff --git a/custom_mutators/examples/example.c b/custom_mutators/examples/example.c index 3f299508..42c7469c 100644 --- a/custom_mutators/examples/example.c +++ b/custom_mutators/examples/example.c @@ -6,8 +6,8 @@ Dominik Maier <mail@dmnk.co> */ -// You need to use -I /path/to/AFLplusplus/include -#include "custom_mutator_helpers.h" +// You need to use -I/path/to/AFLplusplus/include -I. +#include "afl-fuzz.h" #include <stdint.h> #include <stdlib.h> @@ -26,19 +26,14 @@ static const char *commands[] = { typedef struct my_mutator { - afl_t *afl; + afl_state_t *afl; // any additional data here! size_t trim_size_current; int trimmming_steps; int cur_step; - // Reused buffers: - BUF_VAR(u8, fuzz); - BUF_VAR(u8, data); - BUF_VAR(u8, havoc); - BUF_VAR(u8, trim); - BUF_VAR(u8, post_process); + u8 *mutated_out, *post_process_buf, *trim_buf; } my_mutator_t; @@ -53,7 +48,7 @@ typedef struct my_mutator { * There may be multiple instances of this mutator in one afl-fuzz run! * Return NULL on error. */ -my_mutator_t *afl_custom_init(afl_t *afl, unsigned int seed) { +my_mutator_t *afl_custom_init(afl_state_t *afl, unsigned int seed) { srand(seed); // needed also by surgical_havoc_mutate() @@ -65,6 +60,27 @@ my_mutator_t *afl_custom_init(afl_t *afl, unsigned int seed) { } + if ((data->mutated_out = (u8 *)malloc(MAX_FILE)) == NULL) { + + perror("afl_custom_init malloc"); + return NULL; + + } + + if ((data->post_process_buf = (u8 *)malloc(MAX_FILE)) == NULL) { + + perror("afl_custom_init malloc"); + return NULL; + + } + + if ((data->trim_buf = (u8 *)malloc(MAX_FILE)) == NULL) { + + perror("afl_custom_init malloc"); + return NULL; + + } + data->afl = afl; return data; @@ -96,29 +112,14 @@ size_t afl_custom_fuzz(my_mutator_t *data, uint8_t *buf, size_t buf_size, // the fuzzer size_t mutated_size = DATA_SIZE <= max_size ? DATA_SIZE : max_size; - // maybe_grow is optimized to be quick for reused buffers. - u8 *mutated_out = maybe_grow(BUF_PARAMS(data, fuzz), mutated_size); - if (!mutated_out) { - - *out_buf = NULL; - perror("custom mutator allocation (maybe_grow)"); - return 0; /* afl-fuzz will very likely error out after this. */ - - } + memcpy(data->mutated_out, buf, buf_size); // Randomly select a command string to add as a header to the packet - memcpy(mutated_out, commands[rand() % 3], 3); + memcpy(data->mutated_out, commands[rand() % 3], 3); - // Mutate the payload of the packet - int i; - for (i = 0; i < 8; ++i) { + if (mutated_size > max_size) { mutated_size = max_size; } - // Randomly perform one of the (no len modification) havoc mutations - surgical_havoc_mutate(mutated_out, 3, mutated_size); - - } - - *out_buf = mutated_out; + *out_buf = data->mutated_out; return mutated_size; } @@ -142,24 +143,16 @@ size_t afl_custom_fuzz(my_mutator_t *data, uint8_t *buf, size_t buf_size, size_t afl_custom_post_process(my_mutator_t *data, uint8_t *buf, size_t buf_size, uint8_t **out_buf) { - uint8_t *post_process_buf = - maybe_grow(BUF_PARAMS(data, post_process), buf_size + 5); - if (!post_process_buf) { + if (buf_size + 5 > MAX_FILE) { buf_size = MAX_FILE - 5; } - perror("custom mutator realloc failed."); - *out_buf = NULL; - return 0; - - } + memcpy(data->post_process_buf + 5, buf, buf_size); + data->post_process_buf[0] = 'A'; + data->post_process_buf[1] = 'F'; + data->post_process_buf[2] = 'L'; + data->post_process_buf[3] = '+'; + data->post_process_buf[4] = '+'; - memcpy(post_process_buf + 5, buf, buf_size); - post_process_buf[0] = 'A'; - post_process_buf[1] = 'F'; - post_process_buf[2] = 'L'; - post_process_buf[3] = '+'; - post_process_buf[4] = '+'; - - *out_buf = post_process_buf; + *out_buf = data->post_process_buf; return buf_size + 5; @@ -195,13 +188,6 @@ int32_t afl_custom_init_trim(my_mutator_t *data, uint8_t *buf, data->cur_step = 0; - if (!maybe_grow(BUF_PARAMS(data, trim), buf_size)) { - - perror("init_trim grow"); - return -1; - - } - memcpy(data->trim_buf, buf, buf_size); data->trim_size_current = buf_size; @@ -282,27 +268,11 @@ int32_t afl_custom_post_trim(my_mutator_t *data, int success) { size_t afl_custom_havoc_mutation(my_mutator_t *data, u8 *buf, size_t buf_size, u8 **out_buf, size_t max_size) { - if (buf_size == 0) { - - *out_buf = maybe_grow(BUF_PARAMS(data, havoc), 1); - if (!*out_buf) { - - perror("custom havoc: maybe_grow"); - return 0; - - } + *out_buf = buf; // in-place mutation - **out_buf = rand() % 256; - buf_size = 1; - - } else { - - // We reuse buf here. It's legal and faster. - *out_buf = buf; - - } + if (buf_size <= sizeof(size_t)) { return buf_size; } - size_t victim = rand() % buf_size; + size_t victim = rand() % (buf_size - sizeof(size_t)); (*out_buf)[victim] += rand() % 10; return buf_size; @@ -369,9 +339,7 @@ uint8_t afl_custom_queue_new_entry(my_mutator_t *data, void afl_custom_deinit(my_mutator_t *data) { free(data->post_process_buf); - free(data->havoc_buf); - free(data->data_buf); - free(data->fuzz_buf); + free(data->mutated_out); free(data->trim_buf); free(data); diff --git a/custom_mutators/examples/post_library_gif.so.c b/custom_mutators/examples/post_library_gif.so.c index 9cd224f4..6737c627 100644 --- a/custom_mutators/examples/post_library_gif.so.c +++ b/custom_mutators/examples/post_library_gif.so.c @@ -45,9 +45,8 @@ 1) If you don't want to modify the test case, simply set `*out_buf = in_buf` and return the original `len`. - NOTE: the following is currently NOT true, we abort in this case! 2) If you want to skip this test case altogether and have AFL generate a - new one, return 0 or set `*out_buf = NULL`. + new one, return 0. Use this sparingly - it's faster than running the target program with patently useless inputs, but still wastes CPU time. @@ -59,8 +58,6 @@ Note that the buffer will *not* be freed for you. To avoid memory leaks, you need to free it or reuse it on subsequent calls (as shown below). - *** Feel free to reuse the original 'in_buf' BUFFER and return it. *** - Alright. The example below shows a simple postprocessor that tries to make sure that all input files start with "GIF89a". @@ -72,7 +69,7 @@ #include <stdio.h> #include <stdlib.h> #include <string.h> -#include "alloc-inl.h" +#include "afl-fuzz.h" /* Header that must be present at the beginning of every test case: */ @@ -80,8 +77,7 @@ typedef struct post_state { - unsigned char *buf; - size_t size; + size_t size; } post_state_t; @@ -95,15 +91,6 @@ void *afl_custom_init(void *afl) { } - state->buf = calloc(sizeof(unsigned char), 4096); - if (!state->buf) { - - free(state); - perror("calloc"); - return NULL; - - } - return state; } @@ -113,6 +100,10 @@ void *afl_custom_init(void *afl) { size_t afl_custom_post_process(post_state_t *data, unsigned char *in_buf, unsigned int len, unsigned char **out_buf) { + /* we do in-place modification as we do not increase the size */ + + *out_buf = in_buf; + /* Skip execution altogether for buffers shorter than 6 bytes (just to show how it's done). We can trust len to be sane. */ @@ -120,34 +111,7 @@ size_t afl_custom_post_process(post_state_t *data, unsigned char *in_buf, /* Do nothing for buffers that already start with the expected header. */ - if (!memcmp(in_buf, HEADER, strlen(HEADER))) { - - *out_buf = in_buf; - return len; - - } - - /* Allocate memory for new buffer, reusing previous allocation if - possible. Note we have to use afl-fuzz's own realloc! - Note that you should only do this if you need to grow the buffer, - otherwise work with in_buf, and assign it to *out_buf instead. */ - - *out_buf = afl_realloc(out_buf, len); - - /* If we're out of memory, the most graceful thing to do is to return the - original buffer and give up on modifying it. Let AFL handle OOM on its - own later on. */ - - if (!*out_buf) { - - *out_buf = in_buf; - return len; - - } - - if (len > strlen(HEADER)) - memcpy(*out_buf + strlen(HEADER), in_buf + strlen(HEADER), - len - strlen(HEADER)); + if (!memcmp(in_buf, HEADER, strlen(HEADER))) { return len; } /* Insert the new header. */ @@ -162,7 +126,6 @@ size_t afl_custom_post_process(post_state_t *data, unsigned char *in_buf, /* Gets called afterwards */ void afl_custom_deinit(post_state_t *data) { - free(data->buf); free(data); } diff --git a/custom_mutators/examples/post_library_png.so.c b/custom_mutators/examples/post_library_png.so.c index cd65b1bc..652da497 100644 --- a/custom_mutators/examples/post_library_png.so.c +++ b/custom_mutators/examples/post_library_png.so.c @@ -30,7 +30,7 @@ #include <string.h> #include <zlib.h> #include <arpa/inet.h> -#include "alloc-inl.h" +#include "afl-fuzz.h" /* A macro to round an integer up to 4 kB. */ @@ -53,7 +53,7 @@ void *afl_custom_init(void *afl) { } - state->buf = calloc(sizeof(unsigned char), 4096); + state->buf = calloc(sizeof(unsigned char), MAX_FILE); if (!state->buf) { free(state); @@ -80,21 +80,7 @@ size_t afl_custom_post_process(post_state_t *data, const unsigned char *in_buf, } - /* This is not a good way to do it, if you do not need to grow the buffer - then just work with in_buf instead for speed reasons. - But we want to show how to grow a buffer, so this is how it's done: */ - - unsigned int pos = 8; - unsigned char *new_buf = afl_realloc(out_buf, UP4K(len)); - - if (!new_buf) { - - *out_buf = in_buf; - return len; - - } - - memcpy(new_buf, in_buf, len); + unsigned int pos = 8; /* Minimum size of a zero-length PNG chunk is 12 bytes; if we don't have that, we can bail out. */ @@ -124,7 +110,7 @@ size_t afl_custom_post_process(post_state_t *data, const unsigned char *in_buf, if (real_cksum != file_cksum) { - *(uint32_t *)(new_buf + pos + 8 + chunk_len) = real_cksum; + *(uint32_t *)(data->buf + pos + 8 + chunk_len) = real_cksum; } @@ -134,7 +120,7 @@ size_t afl_custom_post_process(post_state_t *data, const unsigned char *in_buf, } - *out_buf = new_buf; + *out_buf = data->buf; return len; } diff --git a/custom_mutators/examples/simple_example.c b/custom_mutators/examples/simple_example.c index d888ec1f..2c0abe29 100644 --- a/custom_mutators/examples/simple_example.c +++ b/custom_mutators/examples/simple_example.c @@ -1,6 +1,6 @@ // This simple example just creates random buffer <= 100 filled with 'A' // needs -I /path/to/AFLplusplus/include -#include "custom_mutator_helpers.h" +#include "afl-fuzz.h" #include <stdint.h> #include <stdlib.h> @@ -13,14 +13,14 @@ typedef struct my_mutator { - afl_t *afl; + afl_state_t *afl; // Reused buffers: - BUF_VAR(u8, fuzz); + u8 *fuzz_buf; } my_mutator_t; -my_mutator_t *afl_custom_init(afl_t *afl, unsigned int seed) { +my_mutator_t *afl_custom_init(afl_state_t *afl, unsigned int seed) { srand(seed); my_mutator_t *data = calloc(1, sizeof(my_mutator_t)); @@ -31,6 +31,14 @@ my_mutator_t *afl_custom_init(afl_t *afl, unsigned int seed) { } + data->fuzz_buf = (u8 *)malloc(MAX_FILE); + if (!data->fuzz_buf) { + + perror("afl_custom_init malloc"); + return NULL; + + } + data->afl = afl; return data; @@ -44,18 +52,10 @@ size_t afl_custom_fuzz(my_mutator_t *data, uint8_t *buf, size_t buf_size, int size = (rand() % 100) + 1; if (size > max_size) size = max_size; - u8 *mutated_out = maybe_grow(BUF_PARAMS(data, fuzz), size); - if (!mutated_out) { - - *out_buf = NULL; - perror("custom mutator allocation (maybe_grow)"); - return 0; /* afl-fuzz will very likely error out after this. */ - - } - memset(mutated_out, _FIXED_CHAR, size); + memset(data->fuzz_buf, _FIXED_CHAR, size); - *out_buf = mutated_out; + *out_buf = data->fuzz_buf; return size; } diff --git a/custom_mutators/gramatron/build_gramatron_mutator.sh b/custom_mutators/gramatron/build_gramatron_mutator.sh index 9952e7f5..c830329e 100755 --- a/custom_mutators/gramatron/build_gramatron_mutator.sh +++ b/custom_mutators/gramatron/build_gramatron_mutator.sh @@ -11,7 +11,7 @@ # Adapted for AFLplusplus by Dominik Maier <mail@dmnk.co> # # Copyright 2017 Battelle Memorial Institute. All rights reserved. -# Copyright 2019-2022 AFLplusplus Project. All rights reserved. +# Copyright 2019-2023 AFLplusplus Project. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -125,7 +125,7 @@ else } fi -test -d json-c/.git || { echo "[-] not checked out, please install git or check your internet connection." ; exit 1 ; } +test -e json-c/.git || { echo "[-] not checked out, please install git or check your internet connection." ; exit 1 ; } echo "[+] Got json-c." test -e json-c/.libs/libjson-c.a || { diff --git a/custom_mutators/grammar_mutator/build_grammar_mutator.sh b/custom_mutators/grammar_mutator/build_grammar_mutator.sh index 5121b07f..593cd2dc 100755 --- a/custom_mutators/grammar_mutator/build_grammar_mutator.sh +++ b/custom_mutators/grammar_mutator/build_grammar_mutator.sh @@ -14,7 +14,7 @@ # <andreafioraldi@gmail.com> # # Copyright 2017 Battelle Memorial Institute. All rights reserved. -# Copyright 2019-2022 AFLplusplus Project. All rights reserved. +# Copyright 2019-2023 AFLplusplus Project. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -119,7 +119,7 @@ else } fi -test -f grammar_mutator/.git || { echo "[-] not checked out, please install git or check your internet connection." ; exit 1 ; } +test -e grammar_mutator/.git || { echo "[-] not checked out, please install git or check your internet connection." ; exit 1 ; } echo "[+] Got grammar mutator." cd "grammar_mutator" || exit 1 diff --git a/custom_mutators/libafl_base/Cargo.toml b/custom_mutators/libafl_base/Cargo.toml index 6e40fc39..ac6b0c8f 100644 --- a/custom_mutators/libafl_base/Cargo.toml +++ b/custom_mutators/libafl_base/Cargo.toml @@ -6,7 +6,7 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -libafl = { git = "https://github.com/AFLplusplus/LibAFL.git", rev = "62614ce1016c86e3f00f35b56399292ceabd486b" } +libafl = { git = "https://github.com/AFLplusplus/LibAFL.git", rev = "266677bb88abe75165430f34e7de897c35560504" } custom_mutator = { path = "../rust/custom_mutator", features = ["afl_internals"] } serde = { version = "1.0", default-features = false, features = ["alloc"] } # serialization lib diff --git a/custom_mutators/libafl_base/src/lib.rs b/custom_mutators/libafl_base/src/lib.rs index 6f2db8ca..bae11e1f 100644 --- a/custom_mutators/libafl_base/src/lib.rs +++ b/custom_mutators/libafl_base/src/lib.rs @@ -1,5 +1,4 @@ #![cfg(unix)] -#![allow(unused_variables)] use serde::{Deserialize, Deserializer, Serialize, Serializer}; use std::{ @@ -18,10 +17,12 @@ use libafl::{ scheduled::{havoc_mutations, tokens_mutations, StdScheduledMutator, Tokens}, Mutator, }, - state::{HasCorpus, HasMaxSize, HasMetadata, HasRand, State}, + prelude::UsesInput, + state::{HasCorpus, HasMaxSize, HasMetadata, HasRand, State, UsesState}, Error, }; +#[allow(clippy::identity_op)] const MAX_FILE: usize = 1 * 1024 * 1024; static mut AFL: Option<&'static afl_state> = None; @@ -64,24 +65,32 @@ impl<'de> Deserialize<'de> for AFLCorpus { } } -impl Corpus<BytesInput> for AFLCorpus { +impl UsesState for AFLCorpus { + type State = AFLState; +} + +impl Corpus for AFLCorpus { #[inline] fn count(&self) -> usize { afl().queued_items as usize } #[inline] - fn add(&mut self, testcase: Testcase<BytesInput>) -> Result<usize, Error> { + fn add(&mut self, _testcase: Testcase<BytesInput>) -> Result<usize, Error> { unimplemented!(); } #[inline] - fn replace(&mut self, idx: usize, testcase: Testcase<BytesInput>) -> Result<(), Error> { + fn replace( + &mut self, + _idx: usize, + _testcase: Testcase<BytesInput>, + ) -> Result<Testcase<Self::Input>, Error> { unimplemented!(); } #[inline] - fn remove(&mut self, idx: usize) -> Result<Option<Testcase<BytesInput>>, Error> { + fn remove(&mut self, _idx: usize) -> Result<Option<Testcase<BytesInput>>, Error> { unimplemented!(); } @@ -92,7 +101,7 @@ impl Corpus<BytesInput> for AFLCorpus { entries.entry(idx).or_insert_with(|| { let queue_buf = std::slice::from_raw_parts_mut(afl().queue_buf, self.count()); let entry = queue_buf[idx].as_mut().unwrap(); - let fname = CStr::from_ptr((entry.fname as *mut i8).as_ref().unwrap()) + let fname = CStr::from_ptr((entry.fname.cast::<i8>()).as_ref().unwrap()) .to_str() .unwrap() .to_owned(); @@ -127,9 +136,10 @@ pub struct AFLState { } impl AFLState { + #[must_use] pub fn new(seed: u32) -> Self { Self { - rand: StdRand::with_seed(seed as u64), + rand: StdRand::with_seed(u64::from(seed)), corpus: AFLCorpus::default(), metadata: SerdeAnyMap::new(), max_size: MAX_FILE, @@ -153,7 +163,11 @@ impl HasRand for AFLState { } } -impl HasCorpus<BytesInput> for AFLState { +impl UsesInput for AFLState { + type Input = BytesInput; +} + +impl HasCorpus for AFLState { type Corpus = AFLCorpus; #[inline] @@ -208,7 +222,7 @@ impl CustomMutator for LibAFLBaseCustomMutator { tokens.push(data.to_vec()); } if !tokens.is_empty() { - state.add_metadata(Tokens::new(tokens)); + state.add_metadata(Tokens::from(tokens)); } Ok(Self { state, @@ -220,7 +234,7 @@ impl CustomMutator for LibAFLBaseCustomMutator { fn fuzz<'b, 's: 'b>( &'s mut self, buffer: &'b mut [u8], - add_buff: Option<&[u8]>, + _add_buff: Option<&[u8]>, max_size: usize, ) -> Result<Option<&'b [u8]>, Self::Error> { self.state.set_max_size(max_size); diff --git a/custom_mutators/rust/custom_mutator-sys/Cargo.toml b/custom_mutators/rust/custom_mutator-sys/Cargo.toml index 104f7df0..e38c972e 100644 --- a/custom_mutators/rust/custom_mutator-sys/Cargo.toml +++ b/custom_mutators/rust/custom_mutator-sys/Cargo.toml @@ -1,12 +1,12 @@ [package] name = "custom_mutator-sys" -version = "0.1.0" +version = "0.1.1" authors = ["Julius Hohnerlein <julihoh@users.noreply.github.com>"] -edition = "2018" +edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] [build-dependencies] -bindgen = "0.56" +bindgen = "0.63" diff --git a/custom_mutators/rust/custom_mutator-sys/build.rs b/custom_mutators/rust/custom_mutator-sys/build.rs index 3c88a90d..ba4390ff 100644 --- a/custom_mutators/rust/custom_mutator-sys/build.rs +++ b/custom_mutators/rust/custom_mutator-sys/build.rs @@ -15,8 +15,8 @@ fn main() { // The input header we would like to generate // bindings for. .header("wrapper.h") - .whitelist_type("afl_state_t") - .blacklist_type(r"u\d+") + .allowlist_type("afl_state_t") + .blocklist_type(r"u\d+") .opaque_type(r"_.*") .opaque_type("FILE") .opaque_type("in_addr(_t)?") diff --git a/custom_mutators/rust/custom_mutator-sys/src/lib.rs b/custom_mutators/rust/custom_mutator-sys/src/lib.rs index a38a13a8..719ac994 100644 --- a/custom_mutators/rust/custom_mutator-sys/src/lib.rs +++ b/custom_mutators/rust/custom_mutator-sys/src/lib.rs @@ -1,5 +1,7 @@ #![allow(non_upper_case_globals)] #![allow(non_camel_case_types)] #![allow(non_snake_case)] +#![allow(clippy::too_many_lines)] +#![allow(clippy::used_underscore_binding)] include!(concat!(env!("OUT_DIR"), "/bindings.rs")); diff --git a/custom_mutators/rust/custom_mutator/Cargo.toml b/custom_mutators/rust/custom_mutator/Cargo.toml index 2d3cdbfa..30f764dc 100644 --- a/custom_mutators/rust/custom_mutator/Cargo.toml +++ b/custom_mutators/rust/custom_mutator/Cargo.toml @@ -2,7 +2,7 @@ name = "custom_mutator" version = "0.1.0" authors = ["Julius Hohnerlein <julihoh@users.noreply.github.com>"] -edition = "2018" +edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/custom_mutators/rust/custom_mutator/src/lib.rs b/custom_mutators/rust/custom_mutator/src/lib.rs index f872241e..3b635eb5 100644 --- a/custom_mutators/rust/custom_mutator/src/lib.rs +++ b/custom_mutators/rust/custom_mutator/src/lib.rs @@ -20,7 +20,7 @@ //! This binding is panic-safe in that it will prevent panics from unwinding into AFL++. Any panic will `abort` at the boundary between the custom mutator and AFL++. //! //! # Access to AFL++ internals -//! This crate has an optional feature "afl_internals", which gives access to AFL++'s internal state. +//! This crate has an optional feature "`afl_internals`", which gives access to AFL++'s internal state. //! The state is passed to [`CustomMutator::init`], when the feature is activated. //! //! _This is completely unsafe and uses automatically generated types extracted from the AFL++ source._ @@ -115,7 +115,7 @@ pub mod wrappers { impl<M: RawCustomMutator> FFIContext<M> { fn from(ptr: *mut c_void) -> ManuallyDrop<Box<Self>> { assert!(!ptr.is_null()); - ManuallyDrop::new(unsafe { Box::from_raw(ptr as *mut Self) }) + ManuallyDrop::new(unsafe { Box::from_raw(ptr.cast::<Self>()) }) } fn into_ptr(self: Box<Self>) -> *const c_void { @@ -141,27 +141,28 @@ pub mod wrappers { } /// panic handler called for every panic - fn panic_handler(method: &str, panic_info: Box<dyn Any + Send + 'static>) -> ! { + fn panic_handler(method: &str, panic_info: &Box<dyn Any + Send + 'static>) -> ! { use std::ops::Deref; - let cause = panic_info - .downcast_ref::<String>() - .map(String::deref) - .unwrap_or_else(|| { + let cause = panic_info.downcast_ref::<String>().map_or_else( + || { panic_info .downcast_ref::<&str>() .copied() .unwrap_or("<cause unknown>") - }); - eprintln!("A panic occurred at {}: {}", method, cause); + }, + String::deref, + ); + eprintln!("A panic occurred at {method}: {cause}"); abort() } /// Internal function used in the macro #[cfg(not(feature = "afl_internals"))] + #[must_use] pub fn afl_custom_init_<M: RawCustomMutator>(seed: u32) -> *const c_void { match catch_unwind(|| FFIContext::<M>::new(seed).into_ptr()) { Ok(ret) => ret, - Err(err) => panic_handler("afl_custom_init", err), + Err(err) => panic_handler("afl_custom_init", &err), } } @@ -176,7 +177,7 @@ pub mod wrappers { FFIContext::<M>::new(afl, seed).into_ptr() }) { Ok(ret) => ret, - Err(err) => panic_handler("afl_custom_init", err), + Err(err) => panic_handler("afl_custom_init", &err), } } @@ -196,32 +197,27 @@ pub mod wrappers { ) -> usize { match catch_unwind(|| { let mut context = FFIContext::<M>::from(data); - if buf.is_null() { - panic!("null buf passed to afl_custom_fuzz") - } - if out_buf.is_null() { - panic!("null out_buf passed to afl_custom_fuzz") - } + + assert!(!buf.is_null(), "null buf passed to afl_custom_fuzz"); + assert!(!out_buf.is_null(), "null out_buf passed to afl_custom_fuzz"); + let buff_slice = slice::from_raw_parts_mut(buf, buf_size); let add_buff_slice = if add_buf.is_null() { None } else { Some(slice::from_raw_parts(add_buf, add_buf_size)) }; - match context.mutator.fuzz(buff_slice, add_buff_slice, max_size) { - Some(buffer) => { - *out_buf = buffer.as_ptr(); - buffer.len() - } - None => { - // return the input buffer with 0-length to let AFL skip this mutation attempt - *out_buf = buf; - 0 - } + if let Some(buffer) = context.mutator.fuzz(buff_slice, add_buff_slice, max_size) { + *out_buf = buffer.as_ptr(); + buffer.len() + } else { + // return the input buffer with 0-length to let AFL skip this mutation attempt + *out_buf = buf; + 0 } }) { Ok(ret) => ret, - Err(err) => panic_handler("afl_custom_fuzz", err), + Err(err) => panic_handler("afl_custom_fuzz", &err), } } @@ -237,9 +233,8 @@ pub mod wrappers { ) -> u32 { match catch_unwind(|| { let mut context = FFIContext::<M>::from(data); - if buf.is_null() { - panic!("null buf passed to afl_custom_fuzz") - } + assert!(!buf.is_null(), "null buf passed to afl_custom_fuzz"); + let buf_slice = slice::from_raw_parts(buf, buf_size); // see https://doc.rust-lang.org/nomicon/borrow-splitting.html let ctx = &mut **context; @@ -247,37 +242,39 @@ pub mod wrappers { mutator.fuzz_count(buf_slice) }) { Ok(ret) => ret, - Err(err) => panic_handler("afl_custom_fuzz_count", err), + Err(err) => panic_handler("afl_custom_fuzz_count", &err), } } /// Internal function used in the macro - pub fn afl_custom_queue_new_entry_<M: RawCustomMutator>( + pub unsafe fn afl_custom_queue_new_entry_<M: RawCustomMutator>( data: *mut c_void, filename_new_queue: *const c_char, filename_orig_queue: *const c_char, ) -> bool { match catch_unwind(|| { let mut context = FFIContext::<M>::from(data); - if filename_new_queue.is_null() { - panic!("received null filename_new_queue in afl_custom_queue_new_entry"); - } + assert!( + !filename_new_queue.is_null(), + "received null filename_new_queue in afl_custom_queue_new_entry" + ); + let filename_new_queue = Path::new(OsStr::from_bytes( unsafe { CStr::from_ptr(filename_new_queue) }.to_bytes(), )); - let filename_orig_queue = if !filename_orig_queue.is_null() { + let filename_orig_queue = if filename_orig_queue.is_null() { + None + } else { Some(Path::new(OsStr::from_bytes( unsafe { CStr::from_ptr(filename_orig_queue) }.to_bytes(), ))) - } else { - None }; context .mutator .queue_new_entry(filename_new_queue, filename_orig_queue) }) { Ok(ret) => ret, - Err(err) => panic_handler("afl_custom_queue_new_entry", err), + Err(err) => panic_handler("afl_custom_queue_new_entry", &err), } } @@ -292,7 +289,7 @@ pub mod wrappers { ManuallyDrop::into_inner(FFIContext::<M>::from(data)); }) { Ok(ret) => ret, - Err(err) => panic_handler("afl_custom_deinit", err), + Err(err) => panic_handler("afl_custom_deinit", &err), } } @@ -306,13 +303,13 @@ pub mod wrappers { buf.extend_from_slice(res.as_bytes()); buf.push(0); // unwrapping here, as the error case should be extremely rare - CStr::from_bytes_with_nul(&buf).unwrap().as_ptr() + CStr::from_bytes_with_nul(buf).unwrap().as_ptr() } else { null() } }) { Ok(ret) => ret, - Err(err) => panic_handler("afl_custom_introspection", err), + Err(err) => panic_handler("afl_custom_introspection", &err), } } @@ -329,18 +326,18 @@ pub mod wrappers { buf.extend_from_slice(res.as_bytes()); buf.push(0); // unwrapping here, as the error case should be extremely rare - CStr::from_bytes_with_nul(&buf).unwrap().as_ptr() + CStr::from_bytes_with_nul(buf).unwrap().as_ptr() } else { null() } }) { Ok(ret) => ret, - Err(err) => panic_handler("afl_custom_describe", err), + Err(err) => panic_handler("afl_custom_describe", &err), } } /// Internal function used in the macro - pub fn afl_custom_queue_get_<M: RawCustomMutator>( + pub unsafe fn afl_custom_queue_get_<M: RawCustomMutator>( data: *mut c_void, filename: *const c_char, ) -> u8 { @@ -348,12 +345,12 @@ pub mod wrappers { let mut context = FFIContext::<M>::from(data); assert!(!filename.is_null()); - context.mutator.queue_get(Path::new(OsStr::from_bytes( + u8::from(context.mutator.queue_get(Path::new(OsStr::from_bytes( unsafe { CStr::from_ptr(filename) }.to_bytes(), - ))) as u8 + )))) }) { Ok(ret) => ret, - Err(err) => panic_handler("afl_custom_queue_get", err), + Err(err) => panic_handler("afl_custom_queue_get", &err), } } } @@ -373,7 +370,7 @@ macro_rules! _define_afl_custom_init { }; } -/// An exported macro to defined afl_custom_init meant for insternal usage +/// An exported macro to defined `afl_custom_init` meant for internal usage #[cfg(not(feature = "afl_internals"))] #[macro_export] macro_rules! _define_afl_custom_init { @@ -444,7 +441,7 @@ macro_rules! export_mutator { } #[no_mangle] - pub extern "C" fn afl_custom_queue_new_entry( + pub unsafe extern "C" fn afl_custom_queue_new_entry( data: *mut ::std::os::raw::c_void, filename_new_queue: *const ::std::os::raw::c_char, filename_orig_queue: *const ::std::os::raw::c_char, @@ -457,7 +454,7 @@ macro_rules! export_mutator { } #[no_mangle] - pub extern "C" fn afl_custom_queue_get( + pub unsafe extern "C" fn afl_custom_queue_get( data: *mut ::std::os::raw::c_void, filename: *const ::std::os::raw::c_char, ) -> u8 { @@ -520,9 +517,10 @@ mod sanity_test { export_mutator!(ExampleMutator); } -#[allow(unused_variables)] /// A custom mutator. /// [`CustomMutator::handle_error`] will be called in case any method returns an [`Result::Err`]. +#[allow(unused_variables)] +#[allow(clippy::missing_errors_doc)] pub trait CustomMutator { /// The error type. All methods must return the same error type. type Error: Debug; @@ -537,7 +535,7 @@ pub trait CustomMutator { .map(|v| !v.is_empty()) .unwrap_or(false) { - eprintln!("Error in custom mutator: {:?}", err) + eprintln!("Error in custom mutator: {err:?}"); } } @@ -759,8 +757,7 @@ mod truncate_test { let actual_output = truncate_str_unicode_safe(input, *max_len); assert_eq!( &actual_output, expected_output, - "{:#?} truncated to {} bytes should be {:#?}, but is {:#?}", - input, max_len, expected_output, actual_output + "{input:#?} truncated to {max_len} bytes should be {expected_output:#?}, but is {actual_output:#?}" ); } } diff --git a/custom_mutators/rust/example/Cargo.toml b/custom_mutators/rust/example/Cargo.toml index 070d23b1..9d53ebe5 100644 --- a/custom_mutators/rust/example/Cargo.toml +++ b/custom_mutators/rust/example/Cargo.toml @@ -2,7 +2,7 @@ name = "example_mutator" version = "0.1.0" authors = ["Julius Hohnerlein <julihoh@users.noreply.github.com>"] -edition = "2018" +edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/custom_mutators/rust/example_lain/Cargo.toml b/custom_mutators/rust/example_lain/Cargo.toml index 29d606a4..c52bf86f 100644 --- a/custom_mutators/rust/example_lain/Cargo.toml +++ b/custom_mutators/rust/example_lain/Cargo.toml @@ -2,7 +2,7 @@ name = "example_lain" version = "0.1.0" authors = ["Julius Hohnerlein <julihoh@users.noreply.github.com>"] -edition = "2018" +edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/docs/Changelog.md b/docs/Changelog.md index 842b727b..5ed5ef2b 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -3,12 +3,86 @@ This is the list of all noteworthy changes made in every public release of the tool. See README.md for the general instruction manual. -## Staying informed +### Version ++4.07a (dev) + - afl-showmap: + - added custom mutator post_process and send support -Want to stay in the loop on major new features? Join our mailing list by -sending a mail to <afl-users+subscribe@googlegroups.com>. -### Version ++4.03a (dev) +### Version ++4.06c (release) + - afl-fuzz: + - ensure temporary file descriptor is closed when not used + - added `AFL_NO_WARN_INSTABILITY` + - added time_wo_finds to fuzzer_stats + - fixed a crash in pizza (1st april easter egg) mode. Sorry for + everyone who was affected! + - allow pizza mode to be disabled when AFL_PIZZA_MODE is set to -1 + - option `-p mmopt` now also selects new queue items more often + - fix bug in post_process custom mutator implementation + - print name of custom mutator in UI + - slight changes that improve fuzzer performance + - afl-cc: + - add CFI sanitizer variant to gcc targets + - llvm 16 + 17 support (thanks to @devnexen!) + - support llvm 15 native pcguard changes + - support for LLVMFuzzerTestOneInput -1 return + - LTO autoken and llvm_mode: added AFL_LLVM_DICT2FILE_NO_MAIN support + - qemu_mode: + - fix _RANGES envs to allow hyphens in the filenames + - basic riscv support + - frida_mode: + - added `AFL_FRIDA_STATS_INTERVAL` + - fix issue on MacOS + - unicorn_mode: + - updated and minor issues fixed + - nyx_mode support for all tools + - better sanitizer default options support for all tools + - new custom module: autotoken, a grammar free fuzzer for text inputs + - fixed custom mutator C examples + - more minor fixes and cross-platform support + +### Version ++4.05c (release) + - MacOS: libdislocator, libtokencap etc. do not work with modern + MacOS anymore, but could be patched to work, see this issue if you + want to make the effort and send a PR: + https://github.com/AFLplusplus/AFLplusplus/issues/1594 + - afl-fuzz: + - added afl_custom_fuzz_send custom mutator feature. Now your can + send fuzz data to the target as you need, e.g. via IPC. + - cmplog mode now has a -l R option for random colorization, thanks + to guyf2010 for the PR! + - queue statistics are written every 30 minutes to + out/NAME/queue_data if compiled with INTROSPECTION + - new env: AFL_FORK_SERVER_KILL_SIGNAL + - afl-showmap/afl-cmin + - `-t none` now translates to `-t 120000` (120 seconds) + - unicorn_mode updated + - updated rust custom mutator dependencies and LibAFL custom mutator + - overall better sanitizer default setting handling + - several minor bugfixes + +### Version ++4.04c (release) + - fix gramatron and grammar_mutator build scripts + - enhancements to the afl-persistent-config and afl-system-config + scripts + - afl-fuzz: + - force writing all stats on exit + - ensure targets are killed on exit + - `AFL_FORK_SERVER_KILL_SIGNAL` added + - afl-cc: + - make gcc_mode (afl-gcc-fast) work with gcc down to version 3.6 + - qemu_mode: + - fixed 10x speed degredation in v4.03c, thanks to @ele7enxxh for + reporting! + - added qemu_mode/fastexit helper library + - unicorn_mode: + - Enabled tricore arch (by @jma-qb) + - Updated Capstone version in Rust bindings + - llvm-mode: + - AFL runtime will always pass inputs via shared memory, when possible, + ignoring the command line. + + +### Version ++4.03c (release) - Building now gives a build summary what succeeded and what not - afl-fuzz: - added AFL_NO_STARTUP_CALIBRATION to start fuzzing at once instead @@ -17,7 +91,11 @@ sending a mail to <afl-users+subscribe@googlegroups.com>. - default calibration cycles set to 7 from 8, and only add 5 cycles to variables queue items instead of 12. - afl-cc: + - fixed off-by-one bug in our pcguard implemenation, thanks for + @tokatoka for reporting + - fix for llvm 15 and reenabling LTO, thanks to nikic for the PR! - better handling of -fsanitize=..,...,.. lists + - support added for LLVMFuzzerRunDriver() - fix gcc_mode cmplog - obtain the map size of a target with setting AFL_DUMP_MAP_SIZE=1 note that this will exit the target before main() @@ -25,6 +103,13 @@ sending a mail to <afl-users+subscribe@googlegroups.com>. - added AFL_QEMU_TRACK_UNSTABLE to log the addresses of unstable edges (together with AFL_DEBUG=1 afl-fuzz). thanks to worksbutnottested! + - afl-analyze broke at some point, fix by CodeLogicError, thank you! + - afl-cmin/afl-cmin.bash now have an -A option to allow also crashing + and timeout inputs + - unicorn_mode: + - updated upstream unicorn version + - fixed builds for aarch64 + - build now uses all available cores ### Version ++4.02c (release) diff --git a/docs/INSTALL.md b/docs/INSTALL.md index 86ba916f..591b7ded 100644 --- a/docs/INSTALL.md +++ b/docs/INSTALL.md @@ -83,6 +83,7 @@ These build options exist: * UBSAN_BUILD - compiles AFL++ tools with undefined behaviour sanitizer for debug purposes * DEBUG - no optimization, -ggdb3, all warnings and -Werror +* LLVM_DEBUG - shows llvm deprecation warnings * PROFILING - compile afl-fuzz with profiling information * INTROSPECTION - compile afl-fuzz with mutation introspection * NO_PYTHON - disable python support diff --git a/docs/afl-fuzz_approach.md b/docs/afl-fuzz_approach.md index 6af39769..cb173f10 100644 --- a/docs/afl-fuzz_approach.md +++ b/docs/afl-fuzz_approach.md @@ -483,6 +483,7 @@ directory. This includes: - `fuzzer_pid` - PID of the fuzzer process - `cycles_done` - queue cycles completed so far - `cycles_wo_finds` - number of cycles without any new paths found +- `time_wo_finds` - longest time in seconds no new path was found - `execs_done` - number of execve() calls attempted - `execs_per_sec` - overall number of execs per second - `corpus_count` - total number of entries in the queue diff --git a/docs/custom_mutators.md b/docs/custom_mutators.md index 6f3353ec..a1de479e 100644 --- a/docs/custom_mutators.md +++ b/docs/custom_mutators.md @@ -48,6 +48,7 @@ C/C++: ```c void *afl_custom_init(afl_state_t *afl, unsigned int seed); unsigned int afl_custom_fuzz_count(void *data, const unsigned char *buf, size_t buf_size); +void afl_custom_splice_optout(void *data); size_t afl_custom_fuzz(void *data, unsigned char *buf, size_t buf_size, unsigned char **out_buf, unsigned char *add_buf, size_t add_buf_size, size_t max_size); const char *afl_custom_describe(void *data, size_t max_description_len); size_t afl_custom_post_process(void *data, unsigned char *buf, size_t buf_size, unsigned char **out_buf); @@ -57,6 +58,7 @@ int afl_custom_post_trim(void *data, unsigned char success); size_t afl_custom_havoc_mutation(void *data, unsigned char *buf, size_t buf_size, unsigned char **out_buf, size_t max_size); unsigned char afl_custom_havoc_mutation_probability(void *data); unsigned char afl_custom_queue_get(void *data, const unsigned char *filename); +void (*afl_custom_fuzz_send)(void *data, const u8 *buf, size_t buf_size); u8 afl_custom_queue_new_entry(void *data, const unsigned char *filename_new_queue, const unsigned int *filename_orig_queue); const char* afl_custom_introspection(my_mutator_t *data); void afl_custom_deinit(void *data); @@ -68,9 +70,12 @@ Python: def init(seed): pass -def fuzz_count(buf, add_buf, max_size): +def fuzz_count(buf): return cnt +def splice_optout() + pass + def fuzz(buf, add_buf, max_size): return mutated_out @@ -98,6 +103,9 @@ def havoc_mutation_probability(): def queue_get(filename): return True +def fuzz_send(buf): + pass + def queue_new_entry(filename_new_queue, filename_orig_queue): return False @@ -110,7 +118,7 @@ def deinit(): # optional for Python ### Custom Mutation -- `init`: +- `init` (optional in Python): This method is called when AFL++ starts up and is used to seed RNG and set up buffers and state. @@ -128,6 +136,13 @@ def deinit(): # optional for Python for a specific queue entry, use this function. This function is most useful if `AFL_CUSTOM_MUTATOR_ONLY` is **not** used. +- `splice_optout` (optional): + + If this function is present, no splicing target is passed to the `fuzz` + function. This saves time if splicing data is not needed by the custom + fuzzing function. + This function is never called, just needs to be present to activate. + - `fuzz` (optional): This method performs custom mutations on a given input. It also accepts an @@ -135,6 +150,7 @@ def deinit(): # optional for Python sense to use it. You would only skip this if `post_process` is used to fix checksums etc. so if you are using it, e.g., as a post processing library. Note that a length > 0 *must* be returned! + The returned output buffer is under **your** memory management! - `describe` (optional): @@ -168,6 +184,18 @@ def deinit(): # optional for Python to the target, e.g. if it is too short, too corrupted, etc. If so, return a NULL buffer and zero length (or a 0 length string in Python). + NOTE: Do not make any random changes to the data in this function! + + PERFORMANCE for C/C++: If possible make the changes in-place (so modify + the `*data` directly, and return it as `*outbuf = data`. + +- `fuzz_send` (optional): + + This method can be used if you want to send data to the target yourself, + e.g. via IPC. This replaces some usage of utils/afl_proxy but requires + that you start the target with afl-fuzz. + Example: [custom_mutators/examples/custom_send.c](custom_mutators/examples/custom_send.c) + - `queue_new_entry` (optional): This methods is called after adding a new test case to the queue. If the @@ -179,7 +207,7 @@ def deinit(): # optional for Python discovered if compiled with INTROSPECTION. The custom mutator can then return a string (const char *) that reports the exact mutations used. -- `deinit`: +- `deinit` (optional in Python): The last method to be called, deinitializing the state. @@ -269,10 +297,10 @@ sudo apt install python-dev ``` Then, AFL++ can be compiled with Python support. The AFL++ Makefile detects -Python 2 and 3 through `python-config` if it is in the PATH and compiles -`afl-fuzz` with the feature if available. +Python3 through `python-config`/`python3-config` if it is in the PATH and +compiles `afl-fuzz` with the feature if available. -Note: for some distributions, you might also need the package `python[23]-apt`. +Note: for some distributions, you might also need the package `python[3]-apt`. In case your setup is different, set the necessary variables like this: `PYTHON_INCLUDE=/path/to/python/include LDFLAGS=-L/path/to/python/lib make`. diff --git a/docs/env_variables.md b/docs/env_variables.md index bb54357b..a6a0ae44 100644 --- a/docs/env_variables.md +++ b/docs/env_variables.md @@ -129,6 +129,9 @@ subset of the settings discussed in section 1, with the exception of: write all constant string comparisons to this file to be used later with afl-fuzz' `-x` option. + - An option to `AFL_LLVM_DICT2FILE` is `AFL_LLVM_DICT2FILE_NO_MAIN=1` which + skill not parse `main()`. + - `TMPDIR` and `AFL_KEEP_ASSEMBLY`, since no temporary assembly files are created. @@ -354,6 +357,9 @@ checks or alter some of the more exotic semantics of the tool: - Setting `AFL_KEEP_TIMEOUTS` will keep longer running inputs if they reach new coverage + - On the contrary, if you are not interested in any timeouts, you can set + `AFL_IGNORE_TIMEOUTS` to get a bit of speed instead. + - `AFL_EXIT_ON_SEED_ISSUES` will restore the vanilla afl-fuzz behavior which does not allow crashes or timeout seeds in the initial -i corpus. @@ -378,10 +384,10 @@ checks or alter some of the more exotic semantics of the tool: valid terminal was detected (for virtual consoles). - Setting `AFL_FORKSRV_INIT_TMOUT` allows you to specify a different timeout - to wait for the forkserver to spin up. The default is the `-t` value times - `FORK_WAIT_MULT` from `config.h` (usually 10), so for a `-t 100`, the - default would wait for `1000` milliseconds. Setting a different time here is - useful if the target has a very slow startup time, for example, when doing + to wait for the forkserver to spin up. The specified value is the new timeout, in milliseconds. + The default is the `-t` value times `FORK_WAIT_MULT` from `config.h` (usually 10), so for a `-t 100`, the default would wait for `1000` milliseconds. + The `AFL_FORKSRV_INIT_TMOUT` value does not get multiplied. It overwrites the initial timeout afl-fuzz waits for the target to come up with a constant time. + Setting a different time here is useful if the target has a very slow startup time, for example, when doing full-system fuzzing or emulation, but you don't want the actual runs to wait too long for timeouts. @@ -409,11 +415,22 @@ checks or alter some of the more exotic semantics of the tool: the afl-fuzz -g/-G command line option to control the minimum/maximum of fuzzing input generated. - - `AFL_KILL_SIGNAL`: Set the signal ID to be delivered to child processes on - timeout. Unless you implement your own targets or instrumentation, you + - `AFL_KILL_SIGNAL`: Set the signal ID to be delivered to child processes + on timeout. Unless you implement your own targets or instrumentation, you likely don't have to set it. By default, on timeout and on exit, `SIGKILL` (`AFL_KILL_SIGNAL=9`) will be delivered to the child. + - `AFL_FORK_SERVER_KILL_SIGNAL`: Set the signal ID to be delivered to the + fork server when AFL++ is terminated. Unless you implement your + fork server, you likely do not have to set it. By default, `SIGTERM` + (`AFL_FORK_SERVER_KILL_SIGNAL=15`) will be delivered to the fork server. + If only `AFL_KILL_SIGNAL` is provided, `AFL_FORK_SERVER_KILL_SIGNAL` will + be set to same value as `AFL_KILL_SIGNAL` to provide backward compatibility. + If `AFL_FORK_SERVER_KILL_SIGNAL` is also set, it takes precedence. + + NOTE: Uncatchable signals, such as `SIGKILL`, cause child processes of + the fork server to be orphaned and leaves them in a zombie state. + - `AFL_MAP_SIZE` sets the size of the shared map that afl-analyze, afl-fuzz, afl-showmap, and afl-tmin create to gather instrumentation data from the target. This must be equal or larger than the size the target was compiled @@ -455,7 +472,7 @@ checks or alter some of the more exotic semantics of the tool: normally done when starting up the forkserver and causes a pretty significant performance drop. - - `AFL_NO_SNAPSHOT` will advice afl-fuzz not to use the snapshot feature if + - `AFL_NO_SNAPSHOT` will advise afl-fuzz not to use the snapshot feature if the snapshot lkm is loaded. - Setting `AFL_NO_UI` inhibits the UI altogether and just periodically prints @@ -463,7 +480,10 @@ checks or alter some of the more exotic semantics of the tool: output from afl-fuzz is redirected to a file or to a pipe. - Setting `AFL_NO_STARTUP_CALIBRATION` will skip the initial calibration - of all starting seeds, and start fuzzing at once. + of all starting seeds, and start fuzzing at once. Use with care, this + degrades the fuzzing performance! + + - Setting `AFL_NO_WARN_INSTABILITY` will suppress instability warnings. - In QEMU mode (-Q) and FRIDA mode (-O), `AFL_PATH` will be searched for afl-qemu-trace and afl-frida-trace.so. @@ -473,7 +493,7 @@ checks or alter some of the more exotic semantics of the tool: some targets keep inherent state due which a detected crash test case does not crash the target again when the test case is given. To be able to still re-trigger these crashes, you can use the `AFL_PERSISTENT_RECORD` variable - with a value of how many previous fuzz cases to keep prio a crash. If set to + with a value of how many previous fuzz cases to keep prior a crash. If set to e.g., 10, then the 9 previous inputs are written to out/default/crashes as RECORD:000000,cnt:000000 to RECORD:000000,cnt:000008 and RECORD:000000,cnt:000009 being the crash case. NOTE: This option needs to be @@ -561,9 +581,15 @@ checks or alter some of the more exotic semantics of the tool: constructors in your target, you can set `AFL_EARLY_FORKSERVER`. Note that this is not a compile time option but a runtime option :-) - - Set `AFL_PIZZA_MODE` to 1 to enable the April 1st stats menu, set to 0 + - Set `AFL_PIZZA_MODE` to 1 to enable the April 1st stats menu, set to -1 to disable although it is 1st of April. + - If you need a specific interval to update fuzzer_stats file, you can + set `AFL_FUZZER_STATS_UPDATE_INTERVAL` to the interval in seconds you'd + the file to be updated. + Note that will not be exact and with slow targets it can take seconds + until there is a slice for the time test. + ## 5) Settings for afl-qemu-trace The QEMU wrapper used to instrument binary-only code supports several settings: @@ -694,8 +720,8 @@ support. * `AFL_FRIDA_STALKER_ADJACENT_BLOCKS` - Configure the number of adjacent blocks to fetch when generating instrumented code. By fetching blocks in the same order they appear in the original program, rather than the order of execution - should help reduce locallity and adjacency. This includes allowing us to - vector between adjancent blocks using a NOP slide rather than an immediate + should help reduce locality and adjacency. This includes allowing us to + vector between adjacent blocks using a NOP slide rather than an immediate branch. * `AFL_FRIDA_STALKER_IC_ENTRIES` - Configure the number of inline cache entries stored along-side branch instructions which provide a cache to avoid having to diff --git a/docs/fuzzing_binary-only_targets.md b/docs/fuzzing_binary-only_targets.md index c97af1b9..9d9d6bb6 100644 --- a/docs/fuzzing_binary-only_targets.md +++ b/docs/fuzzing_binary-only_targets.md @@ -201,10 +201,10 @@ afl-clang-fast's. ### RetroWrite RetroWrite is a static binary rewriter that can be combined with AFL++. If you -have an x86_64 binary that still has its symbols (i.e., not stripped binary), is -compiled with position independent code (PIC/PIE), and does not contain C++ -exceptions, then the RetroWrite solution might be for you. It decompiles to ASM -files which can then be instrumented with afl-gcc. +have an x86_64 or arm64 binary that does not contain C++ exceptions and - if +x86_64 - still has it's symbols and compiled with position independent code +(PIC/PIE), then the RetroWrite solution might be for you. +It decompiles to ASM files which can then be instrumented with afl-gcc. Binaries that are statically instrumented for fuzzing using RetroWrite are close in performance to compiler-instrumented binaries and outperform the QEMU-based @@ -291,7 +291,7 @@ its IPT performance is just 6%! There are many binary-only fuzzing frameworks. Some are great for CTFs but don't work with large binaries, others are very slow but have good path discovery, -some are very hard to set-up... +some are very hard to set up... * Jackalope: [https://github.com/googleprojectzero/Jackalope](https://github.com/googleprojectzero/Jackalope) diff --git a/docs/fuzzing_in_depth.md b/docs/fuzzing_in_depth.md index 92c9910b..f75ca5dc 100644 --- a/docs/fuzzing_in_depth.md +++ b/docs/fuzzing_in_depth.md @@ -523,7 +523,7 @@ mode!) and switch the input directory with a dash (`-`): afl-fuzz -i - -o output -- bin/target -someopt @@ ``` -Adding a dictionary is helpful. You have to following options: +Adding a dictionary is helpful. You have the following options: * See the directory [dictionaries/](../dictionaries/), if something is already included for your @@ -534,6 +534,8 @@ dictionaries/FORMAT.dict`. * With `afl-clang-fast`, you can set `AFL_LLVM_DICT2FILE=/full/path/to/new/file.dic` to automatically generate a dictionary during target compilation. + Adding `AFL_LLVM_DICT2FILE_NO_MAIN=1` to not parse main (usually command line + parameter parsing) is often a good idea too. * You also have the option to generate a dictionary yourself during an independent run of the target, see [utils/libtokencap/README.md](../utils/libtokencap/README.md). @@ -628,7 +630,8 @@ If you have a large corpus, a corpus from a previous run or are fuzzing in a CI, then also set `export AFL_CMPLOG_ONLY_NEW=1` and `export AFL_FAST_CAL=1`. If the queue in the CI is huge and/or the execution time is slow then you can also add `AFL_NO_STARTUP_CALIBRATION=1` to skip the initial queue calibration -phase and start fuzzing at once. +phase and start fuzzing at once - but only do this if the calibration phase +would be too long for your fuzz run time. You can also use different fuzzers. If you are using AFL spinoffs or AFL conforming fuzzers, then just use the same -o directory and give it a unique @@ -672,7 +675,7 @@ The syncing process itself is very simple. As the `-M main-$HOSTNAME` instance syncs to all `-S` secondaries as well as to other fuzzers, you have to copy only this directory to the other machines. -Lets say all servers have the `-o out` directory in /target/foo/out, and you +Let's say all servers have the `-o out` directory in /target/foo/out, and you created a file `servers.txt` which contains the hostnames of all participating servers, plus you have an ssh key deployed to all of them, then run: @@ -900,6 +903,13 @@ then color-codes the input based on which sections appear to be critical and which are not; while not bulletproof, it can often offer quick insights into complex file formats. +`casr-afl` from [CASR](https://github.com/ispras/casr) tools provides +comfortable triaging for crashes found by AFL++. Reports are clustered and +contain severity and other information. +```shell +casr-afl -i /path/to/afl/out/dir -o /path/to/casr/out/dir +``` + ## 5. CI fuzzing Some notes on continuous integration (CI) fuzzing - this fuzzing is different to @@ -907,7 +917,8 @@ normal fuzzing campaigns as these are much shorter runnings. If the queue in the CI is huge and/or the execution time is slow then you can also add `AFL_NO_STARTUP_CALIBRATION=1` to skip the initial queue calibration -phase and start fuzzing at once. +phase and start fuzzing at once. But only do that if the calibration time is +too long for your overall available fuzz run time. 1. Always: * LTO has a much longer compile time which is diametrical to short fuzzing - @@ -928,7 +939,7 @@ phase and start fuzzing at once. 3. Also randomize the afl-fuzz runtime options, e.g.: * 65% for `AFL_DISABLE_TRIM` * 50% for `AFL_KEEP_TIMEOUTS` - * 50% use a dictionary generated by `AFL_LLVM_DICT2FILE` + * 50% use a dictionary generated by `AFL_LLVM_DICT2FILE` + `AFL_LLVM_DICT2FILE_NO_MAIN=1` * 40% use MOpt (`-L 0`) * 40% for `AFL_EXPAND_HAVOC_NOW` * 20% for old queue processing (`-Z`) diff --git a/docs/ideas.md b/docs/ideas.md index b5de637f..4e419b80 100644 --- a/docs/ideas.md +++ b/docs/ideas.md @@ -3,6 +3,8 @@ In the following, we describe a variety of ideas that could be implemented for future AFL++ versions. +**NOTE:** Our GSoC participation is concerning [libafl](https://github.com/AFLplusplus/libafl), not AFL++. + ## Analysis software Currently analysis is done by using afl-plot, which is rather outdated. A GTK or @@ -16,17 +18,6 @@ and Y axis, zoom factor, log scaling on-off, etc. Mentor: vanhauser-thc -## WASM Instrumentation - -Currently, AFL++ can be used for source code fuzzing and traditional binaries. -With the rise of WASM as a compile target, however, a novel way of -instrumentation needs to be implemented for binaries compiled to Webassembly. -This can either be done by inserting instrumentation directly into the WASM AST, -or by patching feedback into a WASM VM of choice, similar to the current Unicorn -instrumentation. - -Mentor: any - ## Support other programming languages Other programming languages also use llvm hence they could be (easily?) diff --git a/docs/third_party_tools.md b/docs/third_party_tools.md index 1175d9e5..02a40ce5 100644 --- a/docs/third_party_tools.md +++ b/docs/third_party_tools.md @@ -1,5 +1,10 @@ # Tools that help fuzzing with AFL++ +## AFL++ and other development languages + +* [afl-rs](https://github.com/rust-fuzz/afl.rs) - AFL++ for RUST +* [WASM](https://github.com/fgsect/WAFL) - AFL++ for WASM + ## Speeding up fuzzing * [libfiowrapper](https://github.com/marekzmyslowski/libfiowrapper) - if the @@ -62,3 +67,5 @@ generates builds of debian packages suitable for AFL. * [afl-fid](https://github.com/FoRTE-Research/afl-fid) - a set of tools for working with input data. +* [CASR](https://github.com/ispras/casr) - a set of tools for crash triage and + analysis. diff --git a/dynamic_list.txt b/dynamic_list.txt index 7293ae77..50c0c6b8 100644 --- a/dynamic_list.txt +++ b/dynamic_list.txt @@ -8,6 +8,7 @@ "__afl_auto_first"; "__afl_auto_init"; "__afl_auto_second"; + "__afl_connected"; "__afl_coverage_discard"; "__afl_coverage_interesting"; "__afl_coverage_off"; @@ -53,4 +54,5 @@ "__sanitizer_cov_trace_pc_guard"; "__sanitizer_cov_trace_pc_guard_init"; "__sanitizer_cov_trace_switch"; + "LLVMFuzzerTestOneInput"; }; diff --git a/frida_mode/GNUmakefile b/frida_mode/GNUmakefile index 39c96d5b..c055fcbb 100644 --- a/frida_mode/GNUmakefile +++ b/frida_mode/GNUmakefile @@ -1,3 +1,4 @@ + PWD:=$(shell pwd)/ ROOT:=$(PWD)../ INC_DIR:=$(PWD)include/ @@ -57,7 +58,10 @@ ifdef DEBUG CFLAGS+=-Werror \ -Wall \ -Wextra \ - -Wpointer-arith + -Wpointer-arith \ + -Wno-unknown-pragmas \ + -Wno-pointer-to-int-cast \ + -Wno-int-to-pointer-cast else CFLAGS+=-Wno-pointer-arith endif @@ -95,6 +99,25 @@ ifeq "$(shell uname)" "Darwin" OS:=macos AFL_CFLAGS:=$(AFL_CFLAGS) -Wno-deprecated-declarations GUM_ARCH:="" + ifeq "$(ARCH)" "arm64" + TARGET_CC= \ + "clang" \ + "-target" \ + "arm64-apple-macos10.9" + TARGET_CXX= \ + "clang++" \ + "-target" \ + "arm64-apple-macos10.9" + else + TARGET_CC= \ + "clang" \ + "-target" \ + "x86_64-apple-macos10.9" + TARGET_CXX= \ + "clang++" \ + "-target" \ + "x86_64-apple-macos10.9" + endif else ifdef DEBUG AFL_CFLAGS:=$(AFL_CFLAGS) -Wno-prio-ctor-dtor @@ -142,7 +165,7 @@ ifndef OS $(error "Operating system unsupported") endif -GUM_DEVKIT_VERSION=15.2.1 +GUM_DEVKIT_VERSION=16.0.11 GUM_DEVKIT_FILENAME=frida-gumjs-devkit-$(GUM_DEVKIT_VERSION)-$(OS)-$(ARCH).tar.xz GUM_DEVKIT_URL="https://github.com/frida/frida/releases/download/$(GUM_DEVKIT_VERSION)/$(GUM_DEVKIT_FILENAME)" @@ -188,6 +211,9 @@ all: $(FRIDA_TRACE) $(FRIDA_TRACE_LIB) $(AFLPP_FRIDA_DRIVER_HOOK_OBJ) $(AFLPP_QE 32: CFLAGS="-m32" LDFLAGS="-m32" ARCH="x86" make all +arm: + CFLAGS="-marm" LDFLAGS="-marm" ARCH="armhf" TARGET_CC=arm-linux-gnueabihf-gcc TARGET_CXX=arm-linux-gnueabihf-g++ make all + $(BUILD_DIR): mkdir -p $(BUILD_DIR) @@ -206,7 +232,7 @@ $(FRIDA_MAKEFILE): | $(BUILD_DIR) .PHONY: $(GUM_DEVIT_LIBRARY) $(GUM_DEVIT_LIBRARY): $(FRIDA_MAKEFILE) - cd $(FRIDA_DIR) && make gum-$(OS)$(GUM_ARCH) + cd $(FRIDA_DIR) && make gum-$(OS)$(GUM_ARCH) FRIDA_V8=disabled $(GUM_DEVIT_HEADER): $(FRIDA_MAKEFILE) | $(FRIDA_BUILD_DIR) echo "#include <stdio.h>" > $@ @@ -245,40 +271,40 @@ TRACE_LDFLAGS+=$(FRIDA_DIR)build/frida-$(OS)-$(ARCH)/lib/libfrida-gum-1.0.a \ else ifeq "$(ARCH)" "arm64" -CFLAGS+=-I $(FRIDA_DIR)build/frida_thin-$(OS)-$(ARCH)/include/frida-1.0 \ - -I $(FRIDA_DIR)build/frida_thin-sdk-$(OS)-$(ARCH)/include/glib-2.0/ \ - -I $(FRIDA_DIR)build/frida_thin-sdk-$(OS)-$(ARCH)/lib/glib-2.0/include/ \ - -I $(FRIDA_DIR)build/frida_thin-sdk-$(OS)-$(ARCH)/include/capstone/ \ - -I $(FRIDA_DIR)build/frida_thin-sdk-$(OS)-$(ARCH)/include/json-glib-1.0/ \ +CFLAGS+=-I $(FRIDA_DIR)build/$(OS)-$(ARCH)/include/frida-1.0 \ + -I $(FRIDA_DIR)build/sdk-$(OS)-$(ARCH)/include/glib-2.0/ \ + -I $(FRIDA_DIR)build/sdk-$(OS)-$(ARCH)/lib/glib-2.0/include/ \ + -I $(FRIDA_DIR)build/sdk-$(OS)-$(ARCH)/include/capstone/ \ + -I $(FRIDA_DIR)build/sdk-$(OS)-$(ARCH)/include/json-glib-1.0/ \ ifeq "$(OS)" "android" CFLAGS += -static-libstdc++ endif else -CFLAGS+=-I $(FRIDA_DIR)build/frida_thin-$(OS)-$(ARCH)/include/frida-1.0 \ - -I $(FRIDA_DIR)build/frida_thin-sdk-$(OS)-$(ARCH)/include/glib-2.0/ \ - -I $(FRIDA_DIR)build/frida_thin-sdk-$(OS)-$(ARCH)/lib/glib-2.0/include/ \ - -I $(FRIDA_DIR)build/frida_thin-sdk-$(OS)-$(ARCH)/include/capstone/ \ - -I $(FRIDA_DIR)build/frida_thin-sdk-$(OS)-$(ARCH)/include/json-glib-1.0/ \ +CFLAGS+=-I $(FRIDA_DIR)build/$(OS)-$(ARCH)/include/frida-1.0 \ + -I $(FRIDA_DIR)build/sdk-$(OS)-$(ARCH)/include/glib-2.0/ \ + -I $(FRIDA_DIR)build/sdk-$(OS)-$(ARCH)/lib/glib-2.0/include/ \ + -I $(FRIDA_DIR)build/sdk-$(OS)-$(ARCH)/include/capstone/ \ + -I $(FRIDA_DIR)build/sdk-$(OS)-$(ARCH)/include/json-glib-1.0/ \ endif TRACE_LDFLAGS+=$(FRIDA_DIR)build/frida-$(OS)-$(ARCH)/lib/libfrida-gum-1.0.a \ - $(FRIDA_DIR)build/frida_thin-sdk-$(OS)-$(ARCH)/lib/libsoup-2.4.a \ - $(FRIDA_DIR)build/frida_thin-sdk-$(OS)-$(ARCH)/lib/libsqlite3.a \ - $(FRIDA_DIR)build/frida_thin-sdk-$(OS)-$(ARCH)/lib/libtcc.a \ - $(FRIDA_DIR)build/frida_thin-sdk-$(OS)-$(ARCH)/lib/libjson-glib-1.0.a \ - $(FRIDA_DIR)build/frida_thin-sdk-$(OS)-$(ARCH)/lib/libquickjs.a \ - $(FRIDA_DIR)build/frida_thin-sdk-$(OS)-$(ARCH)/lib/libcapstone.a \ - $(FRIDA_DIR)build/frida_thin-sdk-$(OS)-$(ARCH)/lib/libunwind.a \ - $(FRIDA_DIR)build/frida_thin-sdk-$(OS)-$(ARCH)/lib/libffi.a \ - $(FRIDA_DIR)build/frida_thin-sdk-$(OS)-$(ARCH)/lib/libdwarf.a \ - $(FRIDA_DIR)build/frida_thin-sdk-$(OS)-$(ARCH)/lib/libelf.a \ - $(FRIDA_DIR)build/frida_thin-sdk-$(OS)-$(ARCH)/lib/libgio-2.0.a \ - $(FRIDA_DIR)build/frida_thin-sdk-$(OS)-$(ARCH)/lib/libgobject-2.0.a \ - $(FRIDA_DIR)build/frida_thin-sdk-$(OS)-$(ARCH)/lib/libglib-2.0.a \ - $(FRIDA_DIR)build/frida_thin-sdk-$(OS)-$(ARCH)/lib/liblzma.a \ - $(FRIDA_DIR)build/frida_thin-sdk-$(OS)-$(ARCH)/lib/libz.a \ + $(FRIDA_DIR)build/sdk-$(OS)-$(ARCH)/lib/libsoup-2.4.a \ + $(FRIDA_DIR)build/sdk-$(OS)-$(ARCH)/lib/libsqlite3.a \ + $(FRIDA_DIR)build/sdk-$(OS)-$(ARCH)/lib/libtcc.a \ + $(FRIDA_DIR)build/sdk-$(OS)-$(ARCH)/lib/libjson-glib-1.0.a \ + $(FRIDA_DIR)build/sdk-$(OS)-$(ARCH)/lib/libquickjs.a \ + $(FRIDA_DIR)build/sdk-$(OS)-$(ARCH)/lib/libcapstone.a \ + $(FRIDA_DIR)build/sdk-$(OS)-$(ARCH)/lib/libunwind.a \ + $(FRIDA_DIR)build/sdk-$(OS)-$(ARCH)/lib/libffi.a \ + $(FRIDA_DIR)build/sdk-$(OS)-$(ARCH)/lib/libdwarf.a \ + $(FRIDA_DIR)build/sdk-$(OS)-$(ARCH)/lib/libelf.a \ + $(FRIDA_DIR)build/sdk-$(OS)-$(ARCH)/lib/libgio-2.0.a \ + $(FRIDA_DIR)build/sdk-$(OS)-$(ARCH)/lib/libgobject-2.0.a \ + $(FRIDA_DIR)build/sdk-$(OS)-$(ARCH)/lib/libglib-2.0.a \ + $(FRIDA_DIR)build/sdk-$(OS)-$(ARCH)/lib/liblzma.a \ + $(FRIDA_DIR)build/sdk-$(OS)-$(ARCH)/lib/libz.a \ CFLAGS+=-I $(FRIDA_DIR)build/frida-$(OS)-$(ARCH)/include/frida-1.0 \ -I $(FRIDA_DIR)build/sdk-$(OS)-$(ARCH)/include/glib-2.0/ \ diff --git a/frida_mode/README.md b/frida_mode/README.md index bfe0948b..aac13153 100644 --- a/frida_mode/README.md +++ b/frida_mode/README.md @@ -86,7 +86,7 @@ To enable the powerful CMPLOG mechanism, set `-c 0` for `afl-fuzz`. ## Scripting -One of the more powerful features of FRIDA mode is it's support for +One of the more powerful features of FRIDA mode is its support for configuration by JavaScript, rather than using environment variables. For details of how this works, see [Scripting.md](Scripting.md). @@ -193,6 +193,13 @@ instrumented address block translations. backpatching information. By default, the child will report applied backpatches to the parent so that they can be applied and then be inherited by the next child on fork. +* `AFL_FRIDA_INST_NO_SUPPRESS` - Disable deterministic branch suppression. + Deterministic branch suppression skips the preamble which generates coverage + information at the start of each block, if the block is reached by a + deterministic branch. This reduces map polution, and may improve performance + when all the executing blocks have been prefetched and backpatching applied. + However, in the event that backpatching is incomplete, this may incur a + performance penatly as branch instructions are disassembled on each branch. * `AFL_FRIDA_INST_SEED` - Sets the initial seed for the hash function used to generate block (and hence edge) IDs. Setting this to a constant value may be useful for debugging purposes, e.g., investigating unstable edges. diff --git a/frida_mode/Scripting.md b/frida_mode/Scripting.md index 2b18e200..023e4a19 100644 --- a/frida_mode/Scripting.md +++ b/frida_mode/Scripting.md @@ -2,7 +2,7 @@ FRIDA now supports the ability to configure itself using JavaScript. This allows the user to make use of the convenience of FRIDA's scripting engine (along with -it's support for debug symbols and exports) to configure all of the things which +its support for debug symbols and exports) to configure all of the things which were traditionally configured using environment variables. By default, FRIDA mode will look for the file `afl.js` in the current working @@ -95,7 +95,7 @@ Afl.print("done"); ## Stripped binaries -Lastly, if the binary you attempting to fuzz has no symbol information and no +Lastly, if the binary you're attempting to fuzz has no symbol information and no exports, then the following approach can be used. ```js @@ -390,7 +390,7 @@ Consider the [following](test/js/test2.c) test code... -------------------------------------------------------- Originally written by Michal Zalewski Copyright 2014 Google Inc. All rights reserved. - Copyright 2019-2022 AFLplusplus Project. All rights reserved. + Copyright 2019-2023 AFLplusplus Project. All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at: diff --git a/frida_mode/frida.map b/frida_mode/frida.map index 73fff686..baf067ab 100644 --- a/frida_mode/frida.map +++ b/frida_mode/frida.map @@ -22,6 +22,7 @@ js_api_set_instrument_no_optimize; js_api_set_instrument_regs_file; js_api_set_instrument_seed; + js_api_set_instrument_suppress_disable; js_api_set_instrument_trace; js_api_set_instrument_trace_unique; js_api_set_instrument_unstable_coverage_file; diff --git a/frida_mode/hook/frida_hook.c b/frida_mode/hook/frida_hook.c index 79e2348d..da1a59b2 100644 --- a/frida_mode/hook/frida_hook.c +++ b/frida_mode/hook/frida_hook.c @@ -54,10 +54,12 @@ __attribute__((visibility("default"))) void afl_persistent_hook( __attribute__((visibility("default"))) void afl_persistent_hook( GumCpuContext *regs, uint8_t *input_buf, uint32_t input_buf_len) { + // do a length check matching the target! memcpy((void *)regs->r[0], input_buf, input_buf_len); regs->r[1] = input_buf_len; + } #else diff --git a/frida_mode/include/instrument.h b/frida_mode/include/instrument.h index 8c93d881..1825e331 100644 --- a/frida_mode/include/instrument.h +++ b/frida_mode/include/instrument.h @@ -15,6 +15,7 @@ extern guint64 instrument_hash_zero; extern char *instrument_coverage_unstable_filename; extern gboolean instrument_coverage_insn; extern char *instrument_regs_filename; +extern gboolean instrument_suppress; extern gboolean instrument_use_fixed_seed; extern guint64 instrument_fixed_seed; diff --git a/frida_mode/src/cmplog/cmplog_arm64.c b/frida_mode/src/cmplog/cmplog_arm64.c index 5792cbfa..095dc242 100644 --- a/frida_mode/src/cmplog/cmplog_arm64.c +++ b/frida_mode/src/cmplog/cmplog_arm64.c @@ -204,10 +204,7 @@ static void cmplog_handle_cmp_sub(GumCpuContext *context, gsize operand1, gsize address = context->pc; - register uintptr_t k = (uintptr_t)address; - - k = (k >> 4) ^ (k << 8); - k &= CMP_MAP_W - 1; + register uintptr_t k = instrument_get_offset_hash(GUM_ADDRESS(address)); if (__afl_cmp_map->headers[k].type != CMP_TYPE_INS) __afl_cmp_map->headers[k].hits = 0; diff --git a/frida_mode/src/cmplog/cmplog_x64.c b/frida_mode/src/cmplog/cmplog_x64.c index 17912648..ce6b8681 100644 --- a/frida_mode/src/cmplog/cmplog_x64.c +++ b/frida_mode/src/cmplog/cmplog_x64.c @@ -188,10 +188,7 @@ static void cmplog_handle_cmp_sub(GumCpuContext *context, gsize operand1, gsize address = ctx_read_reg(context, X86_REG_RIP); - register uintptr_t k = (uintptr_t)address; - - k = (k >> 4) ^ (k << 8); - k &= CMP_MAP_W - 7; + register uintptr_t k = instrument_get_offset_hash(GUM_ADDRESS(address)); if (__afl_cmp_map->headers[k].type != CMP_TYPE_INS) __afl_cmp_map->headers[k].hits = 0; diff --git a/frida_mode/src/cmplog/cmplog_x86.c b/frida_mode/src/cmplog/cmplog_x86.c index a3a02457..fa06d611 100644 --- a/frida_mode/src/cmplog/cmplog_x86.c +++ b/frida_mode/src/cmplog/cmplog_x86.c @@ -193,10 +193,7 @@ static void cmplog_handle_cmp_sub(GumCpuContext *context, gsize operand1, gsize address = ctx_read_reg(context, X86_REG_EIP); - register uintptr_t k = (uintptr_t)address; - - k = (k >> 4) ^ (k << 8); - k &= CMP_MAP_W - 1; + register uintptr_t k = instrument_get_offset_hash(GUM_ADDRESS(address)); if (__afl_cmp_map->headers[k].type != CMP_TYPE_INS) __afl_cmp_map->headers[k].hits = 0; diff --git a/frida_mode/src/ctx/ctx_arm32.c b/frida_mode/src/ctx/ctx_arm32.c index 28fc706b..0e5b25a4 100644 --- a/frida_mode/src/ctx/ctx_arm32.c +++ b/frida_mode/src/ctx/ctx_arm32.c @@ -7,6 +7,8 @@ gsize ctx_read_reg(GumArmCpuContext *ctx, arm_reg reg) { + UNUSED_PARAMETER(ctx); + UNUSED_PARAMETER(reg); FFATAL("ctx_read_reg unimplemented for this architecture"); } diff --git a/frida_mode/src/instrument/instrument.c b/frida_mode/src/instrument/instrument.c index e1e4ac22..a6aac666 100644 --- a/frida_mode/src/instrument/instrument.c +++ b/frida_mode/src/instrument/instrument.c @@ -27,6 +27,7 @@ gboolean instrument_optimize = false; gboolean instrument_unique = false; guint64 instrument_hash_zero = 0; guint64 instrument_hash_seed = 0; +gboolean instrument_suppress = false; gboolean instrument_use_fixed_seed = FALSE; guint64 instrument_fixed_seed = 0; @@ -290,6 +291,7 @@ void instrument_config(void) { (getenv("AFL_FRIDA_INST_UNSTABLE_COVERAGE_FILE")); instrument_coverage_insn = (getenv("AFL_FRIDA_INST_INSN") != NULL); instrument_regs_filename = getenv("AFL_FRIDA_INST_REGS_FILE"); + instrument_suppress = (getenv("AFL_FRIDA_INST_NO_SUPPRESS") == NULL); instrument_debug_config(); instrument_coverage_config(); @@ -321,6 +323,9 @@ void instrument_init(void) { FOKF(cBLU "Instrumentation" cRST " - " cGRN "instructions:" cYEL " [%c]", instrument_coverage_insn ? 'X' : ' '); + FOKF(cBLU "Instrumentation" cRST " - " cGRN "suppression:" cYEL " [%c]", + instrument_suppress ? 'X' : ' '); + if (instrument_tracing && instrument_optimize) { WARNF("AFL_FRIDA_INST_TRACE implies AFL_FRIDA_INST_NO_OPTIMIZE"); diff --git a/frida_mode/src/instrument/instrument_arm32.c b/frida_mode/src/instrument/instrument_arm32.c index f2e825ee..51f78a35 100644 --- a/frida_mode/src/instrument/instrument_arm32.c +++ b/frida_mode/src/instrument/instrument_arm32.c @@ -1,6 +1,7 @@ #include "frida-gumjs.h" #include "instrument.h" +#include "stalker.h" #include "util.h" #if defined(__arm__) @@ -8,8 +9,9 @@ #define PAGE_MASK (~(GUM_ADDRESS(0xfff))) #define PAGE_ALIGNED(x) ((GUM_ADDRESS(x) & PAGE_MASK) == GUM_ADDRESS(x)) -gboolean instrument_cache_enabled = FALSE; -gsize instrument_cache_size = 0; +gboolean instrument_cache_enabled = FALSE; +gsize instrument_cache_size = 0; +static GHashTable *coverage_blocks = NULL; extern __thread guint64 instrument_previous_pc; @@ -22,7 +24,24 @@ typedef struct { // shared_mem[cur_location ^ prev_location]++; // prev_location = cur_location >> 1; - /* We can remove this branch when we add support for branch suppression */ + // str r0, [sp, #-128] ; 0xffffff80 + // str r1, [sp, #-132] ; 0xffffff7c + // ldr r0, [pc, #-20] ; 0xf691b29c + // ldrh r1, [r0] + // movw r0, #33222 ; 0x81c6 + // eor r0, r0, r1 + // ldr r1, [pc, #-40] ; 0xf691b298 + // add r1, r1, r0 + // ldrb r0, [r1] + // add r0, r0, #1 + // add r0, r0, r0, lsr #8 + // strb r0, [r1] + // movw r0, #49379 ; 0xc0e3 + // ldr r1, [pc, #-64] ; 0xf691b29c + // strh r0, [r1] + // ldr r1, [sp, #-132] ; 0xffffff7c + // ldr r0, [sp, #-128] ; 0xffffff80 + uint32_t b_code; /* b imm */ uint8_t *shared_mem; uint64_t *prev_location; @@ -115,6 +134,46 @@ gboolean instrument_is_coverage_optimize_supported(void) { } +static void instrument_coverage_switch(GumStalkerObserver *self, + gpointer from_address, + gpointer start_address, void *from_insn, + gpointer *target) { + + UNUSED_PARAMETER(self); + UNUSED_PARAMETER(from_address); + UNUSED_PARAMETER(start_address); + UNUSED_PARAMETER(from_insn); + + if (!g_hash_table_contains(coverage_blocks, GSIZE_TO_POINTER(*target))) { + + return; + + } + + *target = + (guint8 *)*target + G_STRUCT_OFFSET(afl_log_code_asm_t, str_r0_sp_rz); + +} + +static void instrument_coverage_suppress_init(void) { + + static gboolean initialized = false; + if (initialized) { return; } + initialized = true; + + GumStalkerObserver *observer = stalker_get_observer(); + GumStalkerObserverInterface *iface = GUM_STALKER_OBSERVER_GET_IFACE(observer); + iface->switch_callback = instrument_coverage_switch; + + coverage_blocks = g_hash_table_new(g_direct_hash, g_direct_equal); + if (coverage_blocks == NULL) { + + FATAL("Failed to g_hash_table_new, errno: %d", errno); + + } + +} + static void patch_t3_insn(uint32_t *insn, uint16_t val) { uint32_t orig = GUINT32_FROM_LE(*insn); @@ -135,14 +194,17 @@ void instrument_coverage_optimize(const cs_insn *instr, guint64 area_offset = instrument_get_offset_hash(GUM_ADDRESS(instr->address)); gsize map_size_pow2; gsize area_offset_ror; - GumAddress code_addr = 0; - // gum_arm64_writer_put_brk_imm(cw, 0x0); - - code_addr = cw->pc; + instrument_coverage_suppress_init(); block_start = GSIZE_TO_POINTER(GUM_ADDRESS(cw->code)); + if (!g_hash_table_add(coverage_blocks, block_start)) { + + FATAL("Failed - g_hash_table_add"); + + } + code.code = template; g_assert(PAGE_ALIGNED(__afl_area_ptr)); @@ -211,7 +273,19 @@ void instrument_flush(GumStalkerOutput *output) { gpointer instrument_cur(GumStalkerOutput *output) { - return gum_arm_writer_cur(output->writer.arm); + gpointer curr = NULL; + + if (output->encoding == GUM_INSTRUCTION_SPECIAL) { + + curr = gum_thumb_writer_cur(output->writer.thumb); + + } else { + + curr = gum_arm_writer_cur(output->writer.arm); + + } + + return curr; } diff --git a/frida_mode/src/instrument/instrument_arm64.c b/frida_mode/src/instrument/instrument_arm64.c index 87811b38..4372861d 100644 --- a/frida_mode/src/instrument/instrument_arm64.c +++ b/frida_mode/src/instrument/instrument_arm64.c @@ -156,26 +156,55 @@ static gboolean instrument_is_deterministic(const cs_insn *from_insn) { } +cs_insn *instrument_disassemble(gconstpointer address) { + + csh capstone; + cs_insn *insn = NULL; + + cs_open(CS_ARCH_ARM64, GUM_DEFAULT_CS_ENDIAN, &capstone); + cs_option(capstone, CS_OPT_DETAIL, CS_OPT_ON); + + cs_disasm(capstone, address, 16, GPOINTER_TO_SIZE(address), 1, &insn); + + cs_close(&capstone); + + return insn; + +} + static void instrument_coverage_switch(GumStalkerObserver *self, gpointer from_address, - gpointer start_address, - const cs_insn *from_insn, - gpointer *target) { + gpointer start_address, void *from_insn, + gpointer *target) { UNUSED_PARAMETER(self); UNUSED_PARAMETER(from_address); UNUSED_PARAMETER(start_address); - gsize fixup_offset; + cs_insn *insn = NULL; + gboolean deterministic = FALSE; + gsize fixup_offset; if (!g_hash_table_contains(coverage_blocks, GSIZE_TO_POINTER(*target)) && - !g_hash_table_contains(coverage_blocks, GSIZE_TO_POINTER(*target + 4))) { + !g_hash_table_contains(coverage_blocks, + GSIZE_TO_POINTER((guint8 *)*target + 4))) { return; } - if (instrument_is_deterministic(from_insn)) { return; } + insn = instrument_disassemble(from_insn); + deterministic = instrument_is_deterministic(insn); + cs_free(insn, 1); + + /* + * If the branch is deterministic, then we should start execution at the + * begining of the block. From here, we will branch and skip the coverage + * code and jump right to the target code of the instrumented block. + * Otherwise, if the branch is non-deterministic, then we need to branch + * part way into the block to where the coverage instrumentation starts. + */ + if (deterministic) { return; } /* * Since each block is prefixed with a restoration prologue, we need to be @@ -208,7 +237,7 @@ static void instrument_coverage_switch(GumStalkerObserver *self, */ fixup_offset = GUM_RESTORATION_PROLOG_SIZE + G_STRUCT_OFFSET(afl_log_code_asm_t, restoration_prolog); - *target += fixup_offset; + *target = (guint8 *)*target + fixup_offset; } @@ -284,7 +313,7 @@ void instrument_coverage_optimize(const cs_insn *instr, // gum_arm64_writer_put_brk_imm(cw, 0x0); - instrument_coverage_suppress_init(); + if (instrument_suppress) { instrument_coverage_suppress_init(); } code_addr = cw->pc; @@ -304,9 +333,13 @@ void instrument_coverage_optimize(const cs_insn *instr, block_start = GSIZE_TO_POINTER(GUM_ADDRESS(cw->code) - GUM_RESTORATION_PROLOG_SIZE); - if (!g_hash_table_add(coverage_blocks, block_start)) { + if (instrument_suppress) { + + if (!g_hash_table_add(coverage_blocks, block_start)) { - FATAL("Failed - g_hash_table_add"); + FATAL("Failed - g_hash_table_add"); + + } } @@ -342,7 +375,17 @@ void instrument_coverage_optimize(const cs_insn *instr, code.code.mov_x1_curr_loc_shr_1 |= (area_offset_ror << 5); - gum_arm64_writer_put_bytes(cw, code.bytes, sizeof(afl_log_code)); + if (instrument_suppress) { + + gum_arm64_writer_put_bytes(cw, code.bytes, sizeof(afl_log_code)); + + } else { + + size_t offset = offsetof(afl_log_code, code.stp_x0_x1); + gum_arm64_writer_put_bytes(cw, &code.bytes[offset], + sizeof(afl_log_code) - offset); + + } } diff --git a/frida_mode/src/instrument/instrument_x64.c b/frida_mode/src/instrument/instrument_x64.c index 13ced4a3..8338f8e7 100644 --- a/frida_mode/src/instrument/instrument_x64.c +++ b/frida_mode/src/instrument/instrument_x64.c @@ -171,11 +171,11 @@ void instrument_coverage_optimize_init(void) { } -static void instrument_coverage_switch(GumStalkerObserver *self, - gpointer from_address, - gpointer start_address, - const cs_insn *from_insn, - gpointer *target) { +static void instrument_coverage_switch_insn(GumStalkerObserver *self, + gpointer from_address, + gpointer start_address, + const cs_insn *from_insn, + gpointer *target) { UNUSED_PARAMETER(self); UNUSED_PARAMETER(from_address); @@ -224,6 +224,35 @@ static void instrument_coverage_switch(GumStalkerObserver *self, } +cs_insn *instrument_disassemble(gconstpointer address) { + + csh capstone; + cs_insn *insn = NULL; + + cs_open(CS_ARCH_X86, GUM_CPU_MODE, &capstone); + cs_option(capstone, CS_OPT_DETAIL, CS_OPT_ON); + + cs_disasm(capstone, address, 16, GPOINTER_TO_SIZE(address), 1, &insn); + + cs_close(&capstone); + + return insn; + +} + +static void instrument_coverage_switch(GumStalkerObserver *self, + gpointer from_address, + gpointer start_address, void *from_insn, + gpointer *target) { + + if (from_insn == NULL) { return; } + cs_insn *insn = instrument_disassemble(from_insn); + instrument_coverage_switch_insn(self, from_address, start_address, insn, + target); + cs_free(insn, 1); + +} + static void instrument_coverage_suppress_init(void) { static gboolean initialized = false; @@ -351,11 +380,15 @@ void instrument_coverage_optimize(const cs_insn *instr, } - instrument_coverage_suppress_init(); + if (instrument_suppress) { + + instrument_coverage_suppress_init(); + + if (!g_hash_table_add(coverage_blocks, GSIZE_TO_POINTER(cw->code))) { - if (!g_hash_table_add(coverage_blocks, GSIZE_TO_POINTER(cw->code))) { + FATAL("Failed - g_hash_table_add"); - FATAL("Failed - g_hash_table_add"); + } } diff --git a/frida_mode/src/instrument/instrument_x86.c b/frida_mode/src/instrument/instrument_x86.c index eabd5be4..4667ea29 100644 --- a/frida_mode/src/instrument/instrument_x86.c +++ b/frida_mode/src/instrument/instrument_x86.c @@ -83,11 +83,11 @@ gboolean instrument_is_coverage_optimize_supported(void) { } -static void instrument_coverage_switch(GumStalkerObserver *self, - gpointer from_address, - gpointer start_address, - const cs_insn *from_insn, - gpointer *target) { +static void instrument_coverage_switch_insn(GumStalkerObserver *self, + gpointer from_address, + gpointer start_address, + const cs_insn *from_insn, + gpointer *target) { UNUSED_PARAMETER(self); UNUSED_PARAMETER(from_address); @@ -130,6 +130,35 @@ static void instrument_coverage_switch(GumStalkerObserver *self, } +cs_insn *instrument_disassemble(gconstpointer address) { + + csh capstone; + cs_insn *insn = NULL; + + cs_open(CS_ARCH_X86, GUM_CPU_MODE, &capstone); + cs_option(capstone, CS_OPT_DETAIL, CS_OPT_ON); + + cs_disasm(capstone, address, 16, GPOINTER_TO_SIZE(address), 1, &insn); + + cs_close(&capstone); + + return insn; + +} + +static void instrument_coverage_switch(GumStalkerObserver *self, + gpointer from_address, + gpointer start_address, void *from_insn, + gpointer *target) { + + if (from_insn == NULL) { return; } + cs_insn *insn = instrument_disassemble(from_insn); + instrument_coverage_switch_insn(self, from_address, start_address, insn, + target); + cs_free(insn, 1); + +} + static void instrument_coverage_suppress_init(void) { static gboolean initialized = false; @@ -174,13 +203,17 @@ void instrument_coverage_optimize(const cs_insn *instr, code.code = template; - instrument_coverage_suppress_init(); + if (instrument_suppress) { + + instrument_coverage_suppress_init(); + + // gum_x86_writer_put_breakpoint(cw); - // gum_x86_writer_put_breakpoint(cw); + if (!g_hash_table_add(coverage_blocks, GSIZE_TO_POINTER(cw->code))) { - if (!g_hash_table_add(coverage_blocks, GSIZE_TO_POINTER(cw->code))) { + FATAL("Failed - g_hash_table_add"); - FATAL("Failed - g_hash_table_add"); + } } diff --git a/frida_mode/src/js/api.js b/frida_mode/src/js/api.js index fce7a5d7..f9ea1ffb 100644 --- a/frida_mode/src/js/api.js +++ b/frida_mode/src/js/api.js @@ -170,6 +170,12 @@ class Afl { static setInstrumentSeed(seed) { Afl.jsApiSetInstrumentSeed(seed); } + /* + * See `AFL_FRIDA_INST_NO_SUPPRESS` + */ + static setInstrumentSuppressDisable() { + Afl.jsApiSetInstrumentSuppressDisable(); + } /** * See `AFL_FRIDA_INST_TRACE_UNIQUE`. */ @@ -339,6 +345,7 @@ Afl.jsApiSetInstrumentLibraries = Afl.jsApiGetFunction("js_api_set_instrument_li Afl.jsApiSetInstrumentNoOptimize = Afl.jsApiGetFunction("js_api_set_instrument_no_optimize", "void", []); Afl.jsApiSetInstrumentRegsFile = Afl.jsApiGetFunction("js_api_set_instrument_regs_file", "void", ["pointer"]); Afl.jsApiSetInstrumentSeed = Afl.jsApiGetFunction("js_api_set_instrument_seed", "void", ["uint64"]); +Afl.jsApiSetInstrumentSuppressDisable = Afl.jsApiGetFunction("js_api_set_instrument_suppress_disable", "void", []); Afl.jsApiSetInstrumentTrace = Afl.jsApiGetFunction("js_api_set_instrument_trace", "void", []); Afl.jsApiSetInstrumentTraceUnique = Afl.jsApiGetFunction("js_api_set_instrument_trace_unique", "void", []); Afl.jsApiSetInstrumentUnstableCoverageFile = Afl.jsApiGetFunction("js_api_set_instrument_unstable_coverage_file", "void", ["pointer"]); diff --git a/frida_mode/src/js/js.c b/frida_mode/src/js/js.c index 6bc31864..25187694 100644 --- a/frida_mode/src/js/js.c +++ b/frida_mode/src/js/js.c @@ -18,10 +18,8 @@ static GumScriptScheduler *scheduler; static GMainContext *context; static GMainLoop *main_loop; -static void js_msg(GumScript *script, const gchar *message, GBytes *data, - gpointer user_data) { +static void js_msg(const gchar *message, GBytes *data, gpointer user_data) { - UNUSED_PARAMETER(script); UNUSED_PARAMETER(data); UNUSED_PARAMETER(user_data); FOKF("%s", message); @@ -124,8 +122,8 @@ void js_start(void) { main_loop = g_main_loop_new(context, true); g_main_context_push_thread_default(context); - gum_script_backend_create(backend, "example", source, cancellable, create_cb, - &error); + gum_script_backend_create(backend, "example", source, NULL, cancellable, + create_cb, &error); while (g_main_context_pending(context)) g_main_context_iteration(context, FALSE); diff --git a/frida_mode/src/js/js_api.c b/frida_mode/src/js/js_api.c index 01bba4ff..2e996c1c 100644 --- a/frida_mode/src/js/js_api.c +++ b/frida_mode/src/js/js_api.c @@ -289,6 +289,13 @@ __attribute__((visibility("default"))) void js_api_set_instrument_cache_size( } +__attribute__((visibility("default"))) void +js_api_set_instrument_suppress_disable(void) { + + instrument_suppress = false; + +} + __attribute__((visibility("default"))) void js_api_set_js_main_hook( const js_main_hook_t hook) { diff --git a/frida_mode/src/prefetch.c b/frida_mode/src/prefetch.c index 905e0ae9..f093cd53 100644 --- a/frida_mode/src/prefetch.c +++ b/frida_mode/src/prefetch.c @@ -29,7 +29,6 @@ gboolean prefetch_enable = TRUE; gboolean prefetch_backpatch = TRUE; static prefetch_data_t *prefetch_data = NULL; -static int prefetch_shm_id = -1; static GHashTable *cant_prefetch = NULL; diff --git a/frida_mode/src/stats/stats_arm32.c b/frida_mode/src/stats/stats_arm32.c index bd652aa3..6c72a476 100644 --- a/frida_mode/src/stats/stats_arm32.c +++ b/frida_mode/src/stats/stats_arm32.c @@ -13,6 +13,7 @@ void starts_arch_init(void) { void stats_write_arch(stats_data_t *data) { + UNUSED_PARAMETER(data); FFATAL("Stats not supported on this architecture"); } diff --git a/frida_mode/test/cmplog/GNUmakefile b/frida_mode/test/cmplog/GNUmakefile index bcaff42d..fca52f82 100644 --- a/frida_mode/test/cmplog/GNUmakefile +++ b/frida_mode/test/cmplog/GNUmakefile @@ -2,8 +2,9 @@ PWD:=$(shell pwd)/ ROOT:=$(PWD)../../../ BUILD_DIR:=$(PWD)build/ +TEST_CMPLOG_BASENAME=compcovtest TEST_CMPLOG_SRC=$(PWD)cmplog.c -TEST_CMPLOG_OBJ=$(BUILD_DIR)compcovtest +TEST_CMPLOG_OBJ=$(BUILD_DIR)$(TEST_CMPLOG_BASENAME) TEST_BIN:=$(PWD)../../build/test @@ -13,7 +14,7 @@ CMP_LOG_INPUT:=$(TEST_DATA_DIR)in QEMU_OUT:=$(BUILD_DIR)qemu-out FRIDA_OUT:=$(BUILD_DIR)frida-out -.PHONY: all 32 clean qemu frida frida-nocmplog format +.PHONY: all 32 clean qemu frida frida-nocmplog frida-unprefixedpath format all: $(TEST_CMPLOG_OBJ) make -C $(ROOT)frida_mode/ @@ -64,6 +65,18 @@ frida-nocmplog: $(TEST_CMPLOG_OBJ) $(CMP_LOG_INPUT) -- \ $(TEST_CMPLOG_OBJ) @@ + +frida-unprefixedpath: $(TEST_CMPLOG_OBJ) $(CMP_LOG_INPUT) + PATH=$(BUILD_DIR) $(ROOT)afl-fuzz \ + -O \ + -i $(TEST_DATA_DIR) \ + -o $(FRIDA_OUT) \ + -c 0 \ + -l 3AT \ + -Z \ + -- \ + $(TEST_CMPLOG_BASENAME) @@ + debug: $(TEST_CMPLOG_OBJ) $(CMP_LOG_INPUT) gdb \ --ex 'set environment LD_PRELOAD=$(ROOT)afl-frida-trace.so' \ diff --git a/frida_mode/test/cmplog/Makefile b/frida_mode/test/cmplog/Makefile index 7ca9a9a5..b84e9218 100644 --- a/frida_mode/test/cmplog/Makefile +++ b/frida_mode/test/cmplog/Makefile @@ -19,6 +19,9 @@ frida: frida-nocmplog: @gmake frida-nocmplog +frida-unprefixedpath: + @gmake frida-unprefixedpath + format: @gmake format diff --git a/frida_mode/test/cmplog/cmplog.c b/frida_mode/test/cmplog/cmplog.c index 7c047ed6..2565b35c 100644 --- a/frida_mode/test/cmplog/cmplog.c +++ b/frida_mode/test/cmplog/cmplog.c @@ -2,7 +2,7 @@ // // Author: Mateusz Jurczyk (mjurczyk@google.com) // -// Copyright 2019-2022 Google LLC +// Copyright 2019-2023 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/frida_mode/test/deferred/testinstr.c b/frida_mode/test/deferred/testinstr.c index 7e564a61..0ab44582 100644 --- a/frida_mode/test/deferred/testinstr.c +++ b/frida_mode/test/deferred/testinstr.c @@ -3,7 +3,7 @@ -------------------------------------------------------- Originally written by Michal Zalewski Copyright 2014 Google Inc. All rights reserved. - Copyright 2019-2022 AFLplusplus Project. All rights reserved. + Copyright 2019-2023 AFLplusplus Project. All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at: diff --git a/frida_mode/test/dynamic/testinstr.c b/frida_mode/test/dynamic/testinstr.c index ad26d060..8b285f6d 100644 --- a/frida_mode/test/dynamic/testinstr.c +++ b/frida_mode/test/dynamic/testinstr.c @@ -3,7 +3,7 @@ -------------------------------------------------------- Originally written by Michal Zalewski Copyright 2014 Google Inc. All rights reserved. - Copyright 2019-2022 AFLplusplus Project. All rights reserved. + Copyright 2019-2023 AFLplusplus Project. All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at: diff --git a/frida_mode/test/entry_point/testinstr.c b/frida_mode/test/entry_point/testinstr.c index 196b1d84..24d9a615 100644 --- a/frida_mode/test/entry_point/testinstr.c +++ b/frida_mode/test/entry_point/testinstr.c @@ -3,7 +3,7 @@ -------------------------------------------------------- Originally written by Michal Zalewski Copyright 2014 Google Inc. All rights reserved. - Copyright 2019-2022 AFLplusplus Project. All rights reserved. + Copyright 2019-2023 AFLplusplus Project. All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at: diff --git a/frida_mode/test/exe/testinstr.c b/frida_mode/test/exe/testinstr.c index 334f6518..d965502e 100644 --- a/frida_mode/test/exe/testinstr.c +++ b/frida_mode/test/exe/testinstr.c @@ -3,7 +3,7 @@ -------------------------------------------------------- Originally written by Michal Zalewski Copyright 2014 Google Inc. All rights reserved. - Copyright 2019-2022 AFLplusplus Project. All rights reserved. + Copyright 2019-2023 AFLplusplus Project. All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at: diff --git a/frida_mode/test/js/test.c b/frida_mode/test/js/test.c index f6778b6f..87c9cdf6 100644 --- a/frida_mode/test/js/test.c +++ b/frida_mode/test/js/test.c @@ -3,7 +3,7 @@ -------------------------------------------------------- Originally written by Michal Zalewski Copyright 2014 Google Inc. All rights reserved. - Copyright 2019-2022 AFLplusplus Project. All rights reserved. + Copyright 2019-2023 AFLplusplus Project. All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at: diff --git a/frida_mode/test/js/test2.c b/frida_mode/test/js/test2.c index 9e9cdbb4..6b680a24 100644 --- a/frida_mode/test/js/test2.c +++ b/frida_mode/test/js/test2.c @@ -3,7 +3,7 @@ -------------------------------------------------------- Originally written by Michal Zalewski Copyright 2014 Google Inc. All rights reserved. - Copyright 2019-2022 AFLplusplus Project. All rights reserved. + Copyright 2019-2023 AFLplusplus Project. All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at: diff --git a/frida_mode/test/output/testinstr.c b/frida_mode/test/output/testinstr.c index 334f6518..d965502e 100644 --- a/frida_mode/test/output/testinstr.c +++ b/frida_mode/test/output/testinstr.c @@ -3,7 +3,7 @@ -------------------------------------------------------- Originally written by Michal Zalewski Copyright 2014 Google Inc. All rights reserved. - Copyright 2019-2022 AFLplusplus Project. All rights reserved. + Copyright 2019-2023 AFLplusplus Project. All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at: diff --git a/frida_mode/test/perf/perf.c b/frida_mode/test/perf/perf.c index f6659b55..d9626974 100644 --- a/frida_mode/test/perf/perf.c +++ b/frida_mode/test/perf/perf.c @@ -3,7 +3,7 @@ -------------------------------------------------------- Originally written by Michal Zalewski Copyright 2014 Google Inc. All rights reserved. - Copyright 2019-2022 AFLplusplus Project. All rights reserved. + Copyright 2019-2023 AFLplusplus Project. All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at: diff --git a/frida_mode/test/persistent_ret/testinstr.c b/frida_mode/test/persistent_ret/testinstr.c index b2bc19ef..12365ceb 100644 --- a/frida_mode/test/persistent_ret/testinstr.c +++ b/frida_mode/test/persistent_ret/testinstr.c @@ -3,7 +3,7 @@ -------------------------------------------------------- Originally written by Michal Zalewski Copyright 2014 Google Inc. All rights reserved. - Copyright 2019-2022 AFLplusplus Project. All rights reserved. + Copyright 2019-2023 AFLplusplus Project. All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at: diff --git a/frida_mode/test/png/GNUmakefile b/frida_mode/test/png/GNUmakefile index 864265e0..408b7dcb 100644 --- a/frida_mode/test/png/GNUmakefile +++ b/frida_mode/test/png/GNUmakefile @@ -7,10 +7,10 @@ LIBPNG_BUILD_DIR:=$(BUILD_DIR)libpng/ HARNESS_BUILD_DIR:=$(BUILD_DIR)harness/ PNGTEST_BUILD_DIR:=$(BUILD_DIR)pngtest/ -LIBZ_FILE:=$(LIBZ_BUILD_DIR)zlib-1.2.12.tar.gz -LIBZ_URL:=http://www.zlib.net/zlib-1.2.12.tar.gz -LIBZ_DIR:=$(LIBZ_BUILD_DIR)zlib-1.2.12/ -LIBZ_PC:=$(ZLIB_DIR)zlib.pc +LIBZ_FILE:=$(LIBZ_BUILD_DIR)zlib-1.2.13.tar.gz +LIBZ_URL:=http://www.zlib.net/zlib-1.2.13.tar.gz +LIBZ_DIR:=$(LIBZ_BUILD_DIR)zlib-1.2.13/ +LIBZ_PC:=$(LIBZ_DIR)zlib.pc LIBZ_LIB:=$(LIBZ_DIR)libz.a LIBPNG_FILE:=$(LIBPNG_BUILD_DIR)libpng-1.2.56.tar.gz @@ -25,7 +25,7 @@ HARNESS_URL:="https://raw.githubusercontent.com/llvm/llvm-project/main/compiler- PNGTEST_FILE:=$(PNGTEST_BUILD_DIR)target.cc PNGTEST_OBJ:=$(PNGTEST_BUILD_DIR)target.o -PNGTEST_URL:="https://raw.githubusercontent.com/google/fuzzbench/master/benchmarks/libpng-1.2.56/target.cc" +PNGTEST_URL:="https://raw.githubusercontent.com/google/fuzzbench/e0c4a994b6999bae46e8dec5bcea9a73251b8dba/benchmarks/libpng-1.2.56/target.cc" TEST_BIN:=$(BUILD_DIR)test ifeq "$(shell uname)" "Darwin" @@ -48,7 +48,7 @@ all: $(TEST_BIN) CFLAGS="-m32" LDFLAGS="-m32" make $(TEST_BIN) arm: - ARCH="arm" CC="arm-linux-gnueabihf-gcc" CXX="arm-linux-gnueabihf-g++" make $(TEST_BIN) + CFLAGS="-marm" LDFLAGS="-marm" CC="arm-linux-gnueabihf-gcc" CXX="arm-linux-gnueabihf-g++" make $(TEST_BIN) $(BUILD_DIR): mkdir -p $@ @@ -96,7 +96,7 @@ $(LIBZ_PC): | $(LIBZ_DIR) --static \ --archs="$(ARCH)" -$(LIBZ_LIB): $(LIBZ_PC) +$(LIBZ_LIB): | $(LIBZ_PC) CFLAGS="$(CFLAGS) -fPIC" \ make \ -C $(LIBZ_DIR) \ @@ -133,7 +133,7 @@ png: $(LIBPNG_LIB) ######### TEST ######## -$(TEST_BIN): $(HARNESS_OBJ) $(PNGTEST_OBJ) $(LIBPNG_LIB) +$(TEST_BIN): $(HARNESS_OBJ) $(PNGTEST_OBJ) $(LIBPNG_LIB) $(LIBZ_LIB) $(CXX) \ $(CFLAGS) \ $(LDFLAGS) \ diff --git a/frida_mode/test/testinstr/GNUmakefile b/frida_mode/test/testinstr/GNUmakefile index 79eee213..ebc0b2dc 100644 --- a/frida_mode/test/testinstr/GNUmakefile +++ b/frida_mode/test/testinstr/GNUmakefile @@ -18,6 +18,9 @@ all: $(TESTINSTBIN) 32: CFLAGS="-m32" LDFLAGS="-m32" ARCH="x86" make all +arm: + CFLAGS="-marm" LDFLAGS="-marm" CC="arm-linux-gnueabihf-gcc" CXX="arm-linux-gnueabihf-g++" make $(TESTINSTBIN) + $(BUILD_DIR): mkdir -p $@ diff --git a/frida_mode/test/testinstr/testinstr.c b/frida_mode/test/testinstr/testinstr.c index 334f6518..d965502e 100644 --- a/frida_mode/test/testinstr/testinstr.c +++ b/frida_mode/test/testinstr/testinstr.c @@ -3,7 +3,7 @@ -------------------------------------------------------- Originally written by Michal Zalewski Copyright 2014 Google Inc. All rights reserved. - Copyright 2019-2022 AFLplusplus Project. All rights reserved. + Copyright 2019-2023 AFLplusplus Project. All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at: diff --git a/frida_mode/test/unstable/unstable.c b/frida_mode/test/unstable/unstable.c index 7d16c26c..a87b6c74 100644 --- a/frida_mode/test/unstable/unstable.c +++ b/frida_mode/test/unstable/unstable.c @@ -3,7 +3,7 @@ -------------------------------------------------------- Originally written by Michal Zalewski Copyright 2014 Google Inc. All rights reserved. - Copyright 2019-2022 AFLplusplus Project. All rights reserved. + Copyright 2019-2023 AFLplusplus Project. All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at: diff --git a/frida_mode/ts/lib/afl.ts b/frida_mode/ts/lib/afl.ts index 7a83c0fb..6a2350e7 100644 --- a/frida_mode/ts/lib/afl.ts +++ b/frida_mode/ts/lib/afl.ts @@ -201,6 +201,13 @@ class Afl { Afl.jsApiSetInstrumentSeed(seed); } + /* + * See `AFL_FRIDA_INST_NO_SUPPRESS` + */ + public static setInstrumentSuppressDisable(): void{ + Afl.jsApiSetInstrumentSuppressDisable(); + } + /** * See `AFL_FRIDA_INST_TRACE_UNIQUE`. */ @@ -451,6 +458,11 @@ class Afl { "void", ["uint64"]); + private static readonly jsApiSetInstrumentSuppressDisable = Afl.jsApiGetFunction( + "js_api_set_instrument_suppress_disable", + "void", + []); + private static readonly jsApiSetInstrumentTrace = Afl.jsApiGetFunction( "js_api_set_instrument_trace", "void", diff --git a/frida_mode/ts/package-lock.json b/frida_mode/ts/package-lock.json index e766c2c2..670d7a83 100644 --- a/frida_mode/ts/package-lock.json +++ b/frida_mode/ts/package-lock.json @@ -1,11 +1,433 @@ { - "requires": true, + "name": "@worksbutnottested/aflplusplus-frida", + "version": "1.0.1", "lockfileVersion": 1, + "requires": true, "dependencies": { - "tsc": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/tsc/-/tsc-2.0.3.tgz", - "integrity": "sha512-SN+9zBUtrpUcOpaUO7GjkEHgWtf22c7FKbKCA4e858eEM7Qz86rRDpgOU2lBIDf0fLCsEg65ms899UMUIB2+Ow==", + "@babel/code-frame": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz", + "integrity": "sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==", + "dev": true, + "requires": { + "@babel/highlight": "^7.18.6" + } + }, + "@babel/helper-validator-identifier": { + "version": "7.19.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", + "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==", + "dev": true + }, + "@babel/highlight": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz", + "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.18.6", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + } + }, + "@types/frida-gum": { + "version": "16.5.1", + "resolved": "https://registry.npmjs.org/@types/frida-gum/-/frida-gum-16.5.1.tgz", + "integrity": "sha512-t+2HZG6iBO2cEKtb2KvtP33m/7TGmzSd42YqznToA34+TkS97NttsFZ9OY2s0hPyDQOg+hZTjR1QggRkEL/Ovg==" + }, + "@types/node": { + "version": "14.18.36", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.36.tgz", + "integrity": "sha512-FXKWbsJ6a1hIrRxv+FoukuHnGTgEzKYGi7kilfMae96AL9UNkPFNWJEEYWzdRI9ooIkbr4AKldyuSTLql06vLQ==", + "dev": true + }, + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "requires": { + "sprintf-js": "~1.0.2" + } + }, + "balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "builtin-modules": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", + "integrity": "sha512-wxXCdllwGhI2kCC0MnvTGYTMvnVZTvqgypkiTI8Pa5tcz2i6VqsqwYGgqwXji+4RgCzms6EajE4IxiUH6HH8nQ==", + "dev": true + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true + }, + "diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true + }, + "esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true + }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true + }, + "get-caller-file": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", + "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==", + "dev": true + }, + "glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, + "requires": { + "function-bind": "^1.1.1" + } + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "dev": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "is-core-module": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz", + "integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==", + "dev": true, + "requires": { + "has": "^1.0.3" + } + }, + "js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + }, + "js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } + }, + "minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.7.tgz", + "integrity": "sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g==", + "dev": true + }, + "mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "dev": true, + "requires": { + "minimist": "^1.2.6" + } + }, + "mock-require": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/mock-require/-/mock-require-3.0.3.tgz", + "integrity": "sha512-lLzfLHcyc10MKQnNUCv7dMcoY/2Qxd6wJfbqCcVk3LDb8An4hF6ohk5AztrvgKhJCqj36uyzi/p5se+tvyD+Wg==", + "dev": true, + "requires": { + "get-caller-file": "^1.0.2", + "normalize-path": "^2.1.1" + } + }, + "normalize-path": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", + "integrity": "sha512-3pKJwH184Xo/lnH6oyP1q2pMd7HcypqqmRs91/6/i2CGtWwIKGCkOOMTm/zXbgTEWHw1uNpNi/igc3ePOYHb6w==", + "dev": true, + "requires": { + "remove-trailing-separator": "^1.0.1" + } + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "requires": { + "wrappy": "1" + } + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true + }, + "path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true + }, + "remove-trailing-separator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", + "integrity": "sha512-/hS+Y0u3aOfIETiaiirUFwDBDzmXPvO+jAfKTitUngIPzdKc6Z0LoFjM/CK5PL4C+eKwHohlHAb6H0VFfmmUsw==", + "dev": true + }, + "resolve": { + "version": "1.22.1", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", + "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", + "dev": true, + "requires": { + "is-core-module": "^2.9.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + } + }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + }, + "sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "dev": true + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + }, + "supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true + }, + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + }, + "tslint": { + "version": "6.1.3", + "resolved": "https://registry.npmjs.org/tslint/-/tslint-6.1.3.tgz", + "integrity": "sha512-IbR4nkT96EQOvKE2PW/djGz8iGNeJ4rF2mBfiYaR/nvUWYKJhLwimoJKgjIFEIDibBtOevj7BqCRL4oHeWWUCg==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "builtin-modules": "^1.1.1", + "chalk": "^2.3.0", + "commander": "^2.12.1", + "diff": "^4.0.1", + "glob": "^7.1.1", + "js-yaml": "^3.13.1", + "minimatch": "^3.0.4", + "mkdirp": "^0.5.3", + "resolve": "^1.3.2", + "semver": "^5.3.0", + "tslib": "^1.13.0", + "tsutils": "^2.29.0" + } + }, + "tsutils": { + "version": "2.29.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-2.29.0.tgz", + "integrity": "sha512-g5JVHCIJwzfISaXpXE1qvNalca5Jwob6FjI4AoPlqMusJ6ftFE7IkkFoMhVLRgK+4Kx3gkzb8UZK5t5yTTvEmA==", + "dev": true, + "requires": { + "tslib": "^1.8.1" + } + }, + "typescript": { + "version": "4.9.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", + "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", + "dev": true + }, + "typescript-tslint-plugin": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/typescript-tslint-plugin/-/typescript-tslint-plugin-0.5.5.tgz", + "integrity": "sha512-tR5igNQP+6FhxaPJYRlUBVsEl0n5cSuXRbg7L1y80mL4B1jUHb8uiIcbQBJ9zWyypJEdFYFUccpXxvMwZR8+AA==", + "dev": true, + "requires": { + "minimatch": "^3.0.4", + "mock-require": "^3.0.3", + "vscode-languageserver": "^5.2.1" + } + }, + "vscode-jsonrpc": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-4.0.0.tgz", + "integrity": "sha512-perEnXQdQOJMTDFNv+UF3h1Y0z4iSiaN9jIlb0OqIYgosPCZGYh/MCUlkFtV2668PL69lRDO32hmvL2yiidUYg==", + "dev": true + }, + "vscode-languageserver": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/vscode-languageserver/-/vscode-languageserver-5.2.1.tgz", + "integrity": "sha512-GuayqdKZqAwwaCUjDvMTAVRPJOp/SLON3mJ07eGsx/Iq9HjRymhKWztX41rISqDKhHVVyFM+IywICyZDla6U3A==", + "dev": true, + "requires": { + "vscode-languageserver-protocol": "3.14.1", + "vscode-uri": "^1.0.6" + } + }, + "vscode-languageserver-protocol": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.14.1.tgz", + "integrity": "sha512-IL66BLb2g20uIKog5Y2dQ0IiigW0XKrvmWiOvc0yXw80z3tMEzEnHjaGAb3ENuU7MnQqgnYJ1Cl2l9RvNgDi4g==", + "dev": true, + "requires": { + "vscode-jsonrpc": "^4.0.0", + "vscode-languageserver-types": "3.14.0" + } + }, + "vscode-languageserver-types": { + "version": "3.14.0", + "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.14.0.tgz", + "integrity": "sha512-lTmS6AlAlMHOvPQemVwo3CezxBp0sNB95KNPkqp3Nxd5VFEnuG1ByM0zlRWos0zjO3ZWtkvhal0COgiV1xIA4A==", + "dev": true + }, + "vscode-uri": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-1.0.8.tgz", + "integrity": "sha512-obtSWTlbJ+a+TFRYGaUumtVwb+InIUVI0Lu0VBUAPmj2cU5JutEXg3xUE0c2J5Tcy7h2DEKVJBFi+Y9ZSFzzPQ==", + "dev": true + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", "dev": true } } diff --git a/frida_mode/update_frida_version.sh b/frida_mode/update_frida_version.sh index 7d938712..18243fbb 100755 --- a/frida_mode/update_frida_version.sh +++ b/frida_mode/update_frida_version.sh @@ -1,8 +1,8 @@ #!/bin/sh test -n "$1" && { echo This script has no options. It updates the referenced Frida version in GNUmakefile to the most current one. ; exit 1 ; } -OLD=$(egrep '^GUM_DEVKIT_VERSION=' GNUmakefile 2>/dev/null|awk -F= '{print$2}') -NEW=$(curl https://github.com/frida/frida/releases/ 2>/dev/null|egrep 'frida-gum-devkit-[0-9.]*-linux-x86_64'|head -n 1|sed 's/.*frida-gum-devkit-//'|sed 's/-linux.*//') +OLD=$(grep -E '^GUM_DEVKIT_VERSION=' GNUmakefile 2>/dev/null|awk -F= '{print$2}') +NEW=$(curl https://github.com/frida/frida/releases/ 2>/dev/null|grep -E 'frida-gum-devkit-[0-9.]*-linux-x86_64'|head -n 1|sed 's/.*frida-gum-devkit-//'|sed 's/-linux.*//') echo Current set version: $OLD echo Newest available version: $NEW diff --git a/include/afl-as.h b/include/afl-as.h index bbbd5582..486314e2 100644 --- a/include/afl-as.h +++ b/include/afl-as.h @@ -10,7 +10,7 @@ Dominik Maier <mail@dmnk.co> Copyright 2016, 2017 Google Inc. All rights reserved. - Copyright 2019-2022 AFLplusplus Project. All rights reserved. + Copyright 2019-2023 AFLplusplus Project. All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/include/afl-fuzz.h b/include/afl-fuzz.h index 5d9b17e7..831a0dbc 100644 --- a/include/afl-fuzz.h +++ b/include/afl-fuzz.h @@ -10,7 +10,7 @@ Dominik Maier <mail@dmnk.co> Copyright 2016, 2017 Google Inc. All rights reserved. - Copyright 2019-2022 AFLplusplus Project. All rights reserved. + Copyright 2019-2023 AFLplusplus Project. All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -169,12 +169,22 @@ struct queue_entry { u32 bitmap_size, /* Number of bits set in bitmap */ fuzz_level, /* Number of fuzzing iterations */ - n_fuzz_entry; /* offset in n_fuzz */ + n_fuzz_entry /* offset in n_fuzz */ +#ifdef INTROSPECTION + , + stats_selected, /* stats: how often selected */ + stats_skipped, /* stats: how often skipped */ + stats_finds, /* stats: # of saved finds */ + stats_crashes, /* stats: # of saved crashes */ + stats_tmouts /* stats: # of saved timeouts */ +#endif + ; u64 exec_us, /* Execution time (us) */ handicap, /* Number of queue cycles behind */ depth, /* Path depth */ - exec_cksum; /* Checksum of the execution trace */ + exec_cksum, /* Checksum of the execution trace */ + stats_mutated; /* stats: # of mutations performed */ u8 *trace_mini; /* Trace bytes, if kept */ u32 tc_ref; /* Trace bytes ref count */ @@ -333,6 +343,8 @@ enum { /* 11 */ PY_FUNC_QUEUE_NEW_ENTRY, /* 12 */ PY_FUNC_INTROSPECTION, /* 13 */ PY_FUNC_DESCRIBE, + /* 14 */ PY_FUNC_FUZZ_SEND, + /* 15 */ PY_FUNC_SPLICE_OPTOUT, PY_FUNC_COUNT }; @@ -386,15 +398,18 @@ typedef struct afl_env_vars { afl_bench_until_crash, afl_debug_child, afl_autoresume, afl_cal_fast, afl_cycle_schedules, afl_expand_havoc, afl_statsd, afl_cmplog_only_new, afl_exit_on_seed_issues, afl_try_affinity, afl_ignore_problems, - afl_keep_timeouts, afl_pizza_mode, afl_post_process_keep_original, - afl_no_crash_readme, afl_no_startup_calibration; + afl_keep_timeouts, afl_no_crash_readme, afl_ignore_timeouts, + afl_no_startup_calibration, afl_no_warn_instability, + afl_post_process_keep_original; u8 *afl_tmpdir, *afl_custom_mutator_library, *afl_python_module, *afl_path, *afl_hang_tmout, *afl_forksrv_init_tmout, *afl_preload, *afl_max_det_extras, *afl_statsd_host, *afl_statsd_port, *afl_crash_exitcode, *afl_statsd_tags_flavor, *afl_testcache_size, - *afl_testcache_entries, *afl_kill_signal, *afl_target_env, - *afl_persistent_record, *afl_exit_on_time; + *afl_testcache_entries, *afl_child_kill_signal, *afl_fsrv_kill_signal, + *afl_target_env, *afl_persistent_record, *afl_exit_on_time; + + s32 afl_pizza_mode; } afl_env_vars_t; @@ -484,6 +499,7 @@ typedef struct afl_state { no_unlink, /* do not unlink cur_input */ debug, /* Debug mode */ custom_only, /* Custom mutator only mode */ + custom_splice_optout, /* Custom mutator no splice buffer */ is_main_node, /* if this is the main node */ is_secondary_node, /* if this is a secondary instance */ pizza_is_served; /* pizza mode */ @@ -578,6 +594,7 @@ typedef struct afl_state { last_find_time, /* Time for most recent path (ms) */ last_crash_time, /* Time for most recent crash (ms) */ last_hang_time, /* Time for most recent hang (ms) */ + longest_find_time, /* Longest time taken for a find */ exit_on_time, /* Delay to exit if no new paths */ sync_time; /* Sync time (ms) */ @@ -656,7 +673,7 @@ typedef struct afl_state { u32 cmplog_max_filesize; u32 cmplog_lvl; u32 colorize_success; - u8 cmplog_enable_arith, cmplog_enable_transform; + u8 cmplog_enable_arith, cmplog_enable_transform, cmplog_random_colorization; struct afl_pass_stat *pass_stats; struct cmp_map *orig_cmp_map; @@ -680,12 +697,14 @@ typedef struct afl_state { /* statistics file */ double last_bitmap_cvg, last_stability, last_eps; + u64 stats_file_update_freq_msecs; /* Stats update frequency (msecs) */ /* plot file saves from last run */ u32 plot_prev_qp, plot_prev_pf, plot_prev_pnf, plot_prev_ce, plot_prev_md; u64 plot_prev_qc, plot_prev_uc, plot_prev_uh, plot_prev_ed; - u64 stats_last_stats_ms, stats_last_plot_ms, stats_last_ms, stats_last_execs; + u64 stats_last_stats_ms, stats_last_plot_ms, stats_last_queue_ms, + stats_last_ms, stats_last_execs; /* StatsD */ u64 statsd_last_send_ms; @@ -817,17 +836,29 @@ struct custom_mutator { u32 (*afl_custom_fuzz_count)(void *data, const u8 *buf, size_t buf_size); /** - * Perform custom mutations on a given input + * Opt-out of a splicing input for the fuzz mutator * - * (Optional for now. Required in the future) + * Empty dummy function. It's presence tells afl-fuzz not to pass a + * splice data pointer and len. * * @param data pointer returned in afl_custom_init by this custom mutator + * @noreturn + */ + void (*afl_custom_splice_optout)(void *data); + + /** + * Perform custom mutations on a given input + * + * (Optional) + * + * Getting an add_buf can be skipped by using afl_custom_splice_optout(). + * + * @param[in] data Pointer returned in afl_custom_init by this custom mutator * @param[in] buf Pointer to the input data to be mutated and the mutated * output * @param[in] buf_size Size of the input/output data - * @param[out] out_buf the new buffer. We may reuse *buf if large enough. - * *out_buf = NULL is treated as FATAL. - * @param[in] add_buf Buffer containing the additional test case + * @param[out] out_buf The new buffer, under your memory mgmt. + * @param[in] add_buf Buffer containing an additional test case (splicing) * @param[in] add_buf_size Size of the additional test case * @param[in] max_size Maximum size of the mutated output. The mutation must * not produce data larger than max_size. @@ -855,14 +886,19 @@ struct custom_mutator { * A post-processing function to use right before AFL writes the test case to * disk in order to execute the target. * - * (Optional) If this functionality is not needed, simply don't define this + * NOTE: Do not do any random changes to the data in this function! + * + * PERFORMANCE: If you can modify the data in-place you will have a better + * performance. Modify *data and set `*out_buf = data`. + * + * (Optional) If this functionality is not needed, simply do not define this * function. * * @param[in] data pointer returned in afl_custom_init by this custom mutator * @param[in] buf Buffer containing the test case to be executed * @param[in] buf_size Size of the test case * @param[out] out_buf Pointer to the buffer storing the test case after - * processing. External library should allocate memory for out_buf. + * processing. The external library should allocate memory for out_buf. * It can chose to alter buf in-place, if the space is large enough. * @return Size of the output buffer. */ @@ -969,6 +1005,19 @@ struct custom_mutator { u8 (*afl_custom_queue_get)(void *data, const u8 *filename); /** + * This method can be used if you want to send data to the target yourself, + * e.g. via IPC. This replaces some usage of utils/afl_proxy but requires + * that you start the target with afl-fuzz. + * + * (Optional) + * + * @param data pointer returned in afl_custom_init by this custom mutator + * @param buf Buffer containing the test case + * @param buf_size Size of the test case + */ + void (*afl_custom_fuzz_send)(void *data, const u8 *buf, size_t buf_size); + + /** * Allow for additional analysis (e.g. calling a different tool that does a * different kind of coverage and saves this for the custom mutator). * @@ -1022,6 +1071,7 @@ struct custom_mutator *load_custom_mutator_py(afl_state_t *, char *); void finalize_py_module(void *); u32 fuzz_count_py(void *, const u8 *, size_t); +void fuzz_send_py(void *, const u8 *, size_t); size_t post_process_py(void *, u8 *, size_t, u8 **); s32 init_trim_py(void *, u8 *, size_t); s32 post_trim_py(void *, u8); @@ -1031,6 +1081,7 @@ u8 havoc_mutation_probability_py(void *); u8 queue_get_py(void *, const u8 *); const char *introspection_py(void *); u8 queue_new_entry_py(void *, const u8 *, const u8 *); +void splice_optout(void *); void deinit_py(void *); #endif @@ -1053,7 +1104,6 @@ u32 count_bits(afl_state_t *, u8 *); u32 count_bytes(afl_state_t *, u8 *); u32 count_non_255_bytes(afl_state_t *, u8 *); void simplify_trace(afl_state_t *, u8 *); -void classify_counts(afl_forkserver_t *); #ifdef WORD_SIZE_64 void discover_word(u8 *ret, u64 *current, u64 *virgin); #else @@ -1067,6 +1117,9 @@ u8 *describe_op(afl_state_t *, u8, size_t); u8 save_if_interesting(afl_state_t *, void *, u32, u8); u8 has_new_bits(afl_state_t *, u8 *); u8 has_new_bits_unclassified(afl_state_t *, u8 *); +#ifndef AFL_SHOWMAP +void classify_counts(afl_forkserver_t *); +#endif /* Extras */ @@ -1086,6 +1139,7 @@ void load_stats_file(afl_state_t *); void write_setup_file(afl_state_t *, u32, char **); void write_stats_file(afl_state_t *, u32, double, double, double); void maybe_update_plot_file(afl_state_t *, u32, double, double); +void write_queue_stats(afl_state_t *); void show_stats(afl_state_t *); void show_stats_normal(afl_state_t *); void show_stats_pizza(afl_state_t *); @@ -1141,11 +1195,13 @@ void fix_up_sync(afl_state_t *); void check_asan_opts(afl_state_t *); void check_binary(afl_state_t *, u8 *); void check_if_tty(afl_state_t *); -void setup_signal_handlers(void); void save_cmdline(afl_state_t *, u32, char **); void read_foreign_testcases(afl_state_t *, int); void write_crash_readme(afl_state_t *afl); u8 check_if_text_buf(u8 *buf, u32 len); +#ifndef AFL_SHOWMAP +void setup_signal_handlers(void); +#endif /* CmpLog */ diff --git a/include/afl-prealloc.h b/include/afl-prealloc.h index bdf0d87f..d19a7b52 100644 --- a/include/afl-prealloc.h +++ b/include/afl-prealloc.h @@ -10,7 +10,7 @@ Dominik Maier <mail@dmnk.co> Copyright 2016, 2017 Google Inc. All rights reserved. - Copyright 2019-2022 AFLplusplus Project. All rights reserved. + Copyright 2019-2023 AFLplusplus Project. All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/include/alloc-inl.h b/include/alloc-inl.h index 6c2bafff..ae37028e 100644 --- a/include/alloc-inl.h +++ b/include/alloc-inl.h @@ -10,7 +10,7 @@ Dominik Maier <mail@dmnk.co> Copyright 2016, 2017 Google Inc. All rights reserved. - Copyright 2019-2022 AFLplusplus Project. All rights reserved. + Copyright 2019-2023 AFLplusplus Project. All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/include/cmplog.h b/include/cmplog.h index c6d2957e..6e16e6b0 100644 --- a/include/cmplog.h +++ b/include/cmplog.h @@ -12,7 +12,7 @@ Dominik Maier <mail@dmnk.co> Copyright 2016, 2017 Google Inc. All rights reserved. - Copyright 2019-2022 AFLplusplus Project. All rights reserved. + Copyright 2019-2023 AFLplusplus Project. All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/include/common.h b/include/common.h index a983bb0e..8d85d201 100644 --- a/include/common.h +++ b/include/common.h @@ -10,7 +10,7 @@ Dominik Maier <mail@dmnk.co> Copyright 2016, 2017 Google Inc. All rights reserved. - Copyright 2019-2022 AFLplusplus Project. All rights reserved. + Copyright 2019-2023 AFLplusplus Project. All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -32,6 +32,7 @@ #include <unistd.h> #include <sys/time.h> #include <stdbool.h> +#include "forkserver.h" #include "types.h" /* STRINGIFY_VAL_SIZE_MAX will fit all stringify_ strings. */ @@ -42,6 +43,7 @@ u32 check_binary_signatures(u8 *fn); void detect_file_args(char **argv, u8 *prog_in, bool *use_stdin); void print_suggested_envs(char *mispelled_env); void check_environment_vars(char **env); +void set_sanitizer_defaults(); char **argv_cpy_dup(int argc, char **argv); void argv_cpy_free(char **argv); @@ -67,10 +69,19 @@ u8 *find_binary(u8 *fname); u8 *find_afl_binary(u8 *own_loc, u8 *fname); -/* Parses the kill signal environment variable, FATALs on error. - If the env is not set, sets the env to default_signal for the signal handlers - and returns the default_signal. */ -int parse_afl_kill_signal_env(u8 *afl_kill_signal_env, int default_signal); +/* Parses the (numeric) kill signal environment variable passed + via `numeric_signal_as_str`. + If NULL is passed, the `default_signal` value is returned. + FATALs if `numeric_signal_as_str` is not a valid integer .*/ +int parse_afl_kill_signal(u8 *numeric_signal_as_str, int default_signal); + +/* Configure the signals that are used to kill the forkserver + and the forked childs. If `afl_kill_signal_env` or `afl_fsrv_kill_signal_env` + is NULL, the appropiate values are read from the environment. */ +void configure_afl_kill_signals(afl_forkserver_t *fsrv, + char *afl_kill_signal_env, + char *afl_fsrv_kill_signal_env, + int default_server_kill_signal); /* Read a bitmap from file fname to memory This is for the -B option again. */ @@ -132,5 +143,15 @@ FILE *create_ffile(u8 *fn); /* create a file */ s32 create_file(u8 *fn); +/* memmem implementation as not all platforms support this */ +void *afl_memmem(const void *haystack, size_t haystacklen, const void *needle, + size_t needlelen); + +#ifdef __linux__ +/* Nyx helper functions to create and remove tmp workdirs */ +char *create_nyx_tmp_workdir(void); +void remove_nyx_tmp_workdir(afl_forkserver_t *fsrv, char *nyx_out_dir_path); +#endif + #endif diff --git a/include/config.h b/include/config.h index 1262668a..764c29dc 100644 --- a/include/config.h +++ b/include/config.h @@ -10,7 +10,7 @@ Dominik Maier <mail@dmnk.co> Copyright 2016, 2017 Google Inc. All rights reserved. - Copyright 2019-2022 AFLplusplus Project. All rights reserved. + Copyright 2019-2023 AFLplusplus Project. All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -26,7 +26,7 @@ /* Version string: */ // c = release, a = volatile github dev, e = experimental branch -#define VERSION "++4.03a" +#define VERSION "++4.07a" /****************************************************** * * @@ -290,10 +290,11 @@ #define UI_TARGET_HZ 5 -/* Fuzzer stats file and plot update intervals (sec): */ +/* Fuzzer stats file, queue stats and plot update intervals (sec): */ #define STATS_UPDATE_SEC 60 #define PLOT_UPDATE_SEC 5 +#define QUEUE_UPDATE_SEC 1800 /* Smoothing divisor for CPU load and exec speed stats (1 - no smoothing). */ @@ -363,9 +364,9 @@ * * ***********************************************************/ -/* Call count interval between reseeding the libc PRNG from /dev/urandom: */ +/* Call count interval between reseeding the PRNG from /dev/urandom: */ -#define RESEED_RNG 100000 +#define RESEED_RNG 2500000 /* The default maximum testcase cache size in MB, 0 = disable. A value between 50 and 250 is a good default value. Note that the @@ -490,10 +491,14 @@ #define AFL_TXT_MIN_LEN 12 +/* Maximum length of a queue input to be evaluated for "is_ascii"? */ + +#define AFL_TXT_MAX_LEN 65535 + /* What is the minimum percentage of ascii characters present to be classifed as "is_ascii"? */ -#define AFL_TXT_MIN_PERCENT 94 +#define AFL_TXT_MIN_PERCENT 99 /* How often to perform ASCII mutations 0 = disable, 1-8 are good values */ diff --git a/include/debug.h b/include/debug.h index 566b1d00..cd621a72 100644 --- a/include/debug.h +++ b/include/debug.h @@ -10,7 +10,7 @@ Dominik Maier <mail@dmnk.co> Copyright 2016, 2017 Google Inc. All rights reserved. - Copyright 2019-2022 AFLplusplus Project. All rights reserved. + Copyright 2019-2023 AFLplusplus Project. All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/include/envs.h b/include/envs.h index 1527dfcb..a3f4bc3f 100644 --- a/include/envs.h +++ b/include/envs.h @@ -42,6 +42,7 @@ static char *afl_environment_variables[] = { "AFL_DEBUG", "AFL_DEBUG_CHILD", "AFL_DEBUG_GDB", + "AFL_DEBUG_UNICORN", "AFL_DISABLE_TRIM", "AFL_DISABLE_LLVM_INSTRUMENTATION", "AFL_DONT_OPTIMIZE", @@ -67,6 +68,7 @@ static char *afl_environment_variables[] = { "AFL_FRIDA_INST_NO_OPTIMIZE", "AFL_FRIDA_INST_NO_PREFETCH", "AFL_FRIDA_INST_NO_PREFETCH_BACKPATCH", + "AFL_FRIDA_INST_NO_SUPPRESS" "AFL_FRIDA_INST_RANGES", "AFL_FRIDA_INST_REGS_FILE", "AFL_FRIDA_INST_SEED", @@ -89,6 +91,7 @@ static char *afl_environment_variables[] = { "AFL_FRIDA_TRACEABLE", "AFL_FRIDA_VERBOSE", "AFL_FUZZER_ARGS", // oss-fuzz + "AFL_FUZZER_STATS_UPDATE_INTERVAL", "AFL_GDB", "AFL_GCC_ALLOWLIST", "AFL_GCC_DENYLIST", @@ -102,6 +105,7 @@ static char *afl_environment_variables[] = { "AFL_HARDEN", "AFL_I_DONT_CARE_ABOUT_MISSING_CRASHES", "AFL_IGNORE_PROBLEMS", + "AFL_IGNORE_TIMEOUTS", "AFL_IGNORE_UNKNOWN_ENVS", "AFL_IMPORT_FIRST", "AFL_INPUT_LEN_MIN", @@ -110,6 +114,7 @@ static char *afl_environment_variables[] = { "AFL_INST_RATIO", "AFL_KEEP_TIMEOUTS", "AFL_KILL_SIGNAL", + "AFL_FORK_SERVER_KILL_SIGNAL", "AFL_KEEP_TRACES", "AFL_KEEP_ASSEMBLY", "AFL_LD_HARD_FAIL", @@ -122,12 +127,15 @@ static char *afl_environment_variables[] = { "AFL_LLVM_ALLOWLIST", "AFL_LLVM_DENYLIST", "AFL_LLVM_BLOCKLIST", + "AFL_CMPLOG", "AFL_LLVM_CMPLOG", + "AFL_GCC_CMPLOG", "AFL_LLVM_INSTRIM", "AFL_LLVM_CALLER", "AFL_LLVM_CTX", "AFL_LLVM_CTX_K", "AFL_LLVM_DICT2FILE", + "AFL_LLVM_DICT2FILE_NO_MAIN", "AFL_LLVM_DOCUMENT_IDS", "AFL_LLVM_INSTRIM_LOOPHEAD", "AFL_LLVM_INSTRUMENT", @@ -166,6 +174,7 @@ static char *afl_environment_variables[] = { "AFL_NO_UI", "AFL_NO_PYTHON", "AFL_NO_STARTUP_CALIBRATION", + "AFL_NO_WARN_INSTABILITY", "AFL_UNTRACER_FILE", "AFL_LLVM_USE_TRACE_PC", "AFL_MAP_SIZE", diff --git a/include/forkserver.h b/include/forkserver.h index 59ce0ee7..f5069ce2 100644 --- a/include/forkserver.h +++ b/include/forkserver.h @@ -12,7 +12,7 @@ Dominik Maier <mail@dmnk.co>> Copyright 2016, 2017 Google Inc. All rights reserved. - Copyright 2019-2022 AFLplusplus Project. All rights reserved. + Copyright 2019-2023 AFLplusplus Project. All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -43,7 +43,7 @@ typedef enum NyxReturnValue { Normal, Crash, Asan, - Timout, + Timeout, InvalidWriteToPayload, Error, IoError, @@ -51,16 +51,28 @@ typedef enum NyxReturnValue { } NyxReturnValue; +typedef enum NyxProcessRole { + + StandAlone, + Parent, + Child, + +} NyxProcessRole; + typedef struct { - void *(*nyx_new)(const char *sharedir, const char *workdir, uint32_t cpu_id, - uint32_t input_buffer_size, - bool input_buffer_write_protection); - void *(*nyx_new_parent)(const char *sharedir, const char *workdir, - uint32_t cpu_id, uint32_t input_buffer_size, - bool input_buffer_write_protection); - void *(*nyx_new_child)(const char *sharedir, const char *workdir, - uint32_t cpu_id, uint32_t worker_id); + void *(*nyx_config_load)(const char *sharedir); + void (*nyx_config_set_workdir_path)(void *config, const char *workdir); + void (*nyx_config_set_input_buffer_size)(void *config, + uint32_t input_buffer_size); + void (*nyx_config_set_input_buffer_write_protection)( + void *config, bool input_buffer_write_protection); + void (*nyx_config_set_hprintf_fd)(void *config, int32_t hprintf_fd); + void (*nyx_config_set_process_role)(void *config, enum NyxProcessRole role); + void (*nyx_config_set_reuse_snapshot_path)(void *config, + const char *reuse_snapshot_path); + + void *(*nyx_new)(void *config, uint32_t worker_id); void (*nyx_shutdown)(void *qemu_process); void (*nyx_option_set_reload_mode)(void *qemu_process, bool enable); void (*nyx_option_set_timeout)(void *qemu_process, uint8_t timeout_sec, @@ -73,8 +85,13 @@ typedef struct { uint32_t (*nyx_get_aux_string)(void *nyx_process, uint8_t *buffer, uint32_t size); + bool (*nyx_remove_work_dir)(const char *workdir); + } nyx_plugin_handler_t; +/* Imports helper functions to enable Nyx mode (Linux only )*/ +nyx_plugin_handler_t *afl_load_libnyx_plugin(u8 *libnyx_binary); + #endif typedef struct afl_forkserver { @@ -163,7 +180,9 @@ typedef struct afl_forkserver { void (*add_extra_func)(void *afl_ptr, u8 *mem, u32 len); - u8 kill_signal; + u8 child_kill_signal; + u8 fsrv_kill_signal; + u8 persistent_mode; #ifdef __linux__ @@ -176,6 +195,8 @@ typedef struct afl_forkserver { u32 nyx_id; /* nyx runner id (0 -> master) */ u32 nyx_bind_cpu_id; /* nyx runner cpu id */ char *nyx_aux_string; + bool nyx_use_tmp_workdir; + char *nyx_tmp_workdir_path; #endif } afl_forkserver_t; diff --git a/include/hash.h b/include/hash.h index d8fef70c..0243c5b7 100644 --- a/include/hash.h +++ b/include/hash.h @@ -15,7 +15,7 @@ Other code written by Michal Zalewski Copyright 2016 Google Inc. All rights reserved. - Copyright 2019-2022 AFLplusplus Project. All rights reserved. + Copyright 2019-2023 AFLplusplus Project. All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/include/list.h b/include/list.h index 72bef749..283bf035 100644 --- a/include/list.h +++ b/include/list.h @@ -10,7 +10,7 @@ Dominik Maier <mail@dmnk.co> Copyright 2016, 2017 Google Inc. All rights reserved. - Copyright 2019-2022 AFLplusplus Project. All rights reserved. + Copyright 2019-2023 AFLplusplus Project. All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/include/sharedmem.h b/include/sharedmem.h index fbe68abe..d32bd845 100644 --- a/include/sharedmem.h +++ b/include/sharedmem.h @@ -12,7 +12,7 @@ Dominik Maier <mail@dmnk.co> Copyright 2016, 2017 Google Inc. All rights reserved. - Copyright 2019-2022 AFLplusplus Project. All rights reserved. + Copyright 2019-2023 AFLplusplus Project. All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/include/snapshot-inl.h b/include/snapshot-inl.h index 8d2f41ff..3864e473 100644 --- a/include/snapshot-inl.h +++ b/include/snapshot-inl.h @@ -12,7 +12,7 @@ Dominik Maier <mail@dmnk.co> Copyright 2016, 2017 Google Inc. All rights reserved. - Copyright 2019-2022 AFLplusplus Project. All rights reserved. + Copyright 2019-2023 AFLplusplus Project. All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/include/types.h b/include/types.h index 96ce78f8..d6476d82 100644 --- a/include/types.h +++ b/include/types.h @@ -10,7 +10,7 @@ Dominik Maier <mail@dmnk.co> Copyright 2016, 2017 Google Inc. All rights reserved. - Copyright 2019-2022 AFLplusplus Project. All rights reserved. + Copyright 2019-2023 AFLplusplus Project. All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/include/xxhash.h b/include/xxhash.h index 4cabc884..7bc0a14e 100644 --- a/include/xxhash.h +++ b/include/xxhash.h @@ -1,7 +1,7 @@ /* * xxHash - Extremely Fast Hash algorithm * Header File - * Copyright (C) 2012-2022 Yann Collet + * Copyright (C) 2012-2023 Yann Collet * * BSD 2-Clause License (https://www.opensource.org/licenses/bsd-license.php) * diff --git a/instrumentation/README.llvm.md b/instrumentation/README.llvm.md index 7855a987..c0677474 100644 --- a/instrumentation/README.llvm.md +++ b/instrumentation/README.llvm.md @@ -116,7 +116,7 @@ PCGUARD analysis. Several options are present to make llvm_mode faster or help it rearrange the code to make afl-fuzz path discovery easier. -If you need just to instrument specific parts of the code, you can the +If you need just to instrument specific parts of the code, you can create the instrument file list which C/C++ files to actually instrument. See [README.instrument_list.md](README.instrument_list.md) @@ -167,6 +167,10 @@ Just specify `AFL_LLVM_DICT2FILE=/absolute/path/file.txt` and during compilation all constant string compare parameters will be written to this file to be used with afl-fuzz' `-x` option. +Adding `AFL_LLVM_DICT2FILE_NO_MAIN=1` will skip parsing `main()` which often +does command line parsing which has string comparisons that are not helpful +for fuzzing. + ## 6) AFL++ Context Sensitive Branch Coverage ### What is this? @@ -275,4 +279,4 @@ then this can give a small performance boost. Please note that the default counter implementations are not thread safe! Support for thread safe counters in mode LLVM CLASSIC can be activated with -setting `AFL_LLVM_THREADSAFE_INST=1`. \ No newline at end of file +setting `AFL_LLVM_THREADSAFE_INST=1`. diff --git a/instrumentation/README.lto.md b/instrumentation/README.lto.md index a20175b1..df59cc2a 100644 --- a/instrumentation/README.lto.md +++ b/instrumentation/README.lto.md @@ -2,52 +2,53 @@ ## TL;DR: -This version requires a current llvm 11+ compiled from the GitHub master. +This version requires a LLVM 11 or newer. -1. Use afl-clang-lto/afl-clang-lto++ because it is faster and gives better - coverage than anything else that is out there in the AFL world. +1. Use afl-clang-lto/afl-clang-lto++ because the resulting binaries run + slightly faster and give better coverage. -2. You can use it together with llvm_mode: laf-intel and the instrument file - listing features and can be combined with cmplog/Redqueen. +2. You can use it together with COMPCOV, COMPLOG and the instrument file + listing features. -3. It only works with llvm 11+. +3. It only works with LLVM 11 or newer. -4. AUTODICTIONARY feature (see below)! +4. AUTODICTIONARY feature (see below) -5. If any problems arise, be sure to set `AR=llvm-ar RANLIB=llvm-ranlib`. Some - targets might need `LD=afl-clang-lto` and others `LD=afl-ld-lto`. +5. If any problems arise, be sure to set `AR=llvm-ar RANLIB=llvm-ranlib AS=llvm-as`. + Some targets might need `LD=afl-clang-lto` and others `LD=afl-ld-lto`. ## Introduction and problem description -A big issue with how AFL++ works is that the basic block IDs that are set during -compilation are random - and hence naturally the larger the number of -instrumented locations, the higher the number of edge collisions are in the map. -This can result in not discovering new paths and therefore degrade the +A big issue with how vanilla AFL worked was that the basic block IDs that are +set during compilation are random - and hence naturally the larger the number +of instrumented locations, the higher the number of edge collisions are in the +map. This can result in not discovering new paths and therefore degrade the efficiency of the fuzzing process. -*This issue is underestimated in the fuzzing community!* With a 2^16 = 64kb +*This issue is underestimated in the fuzzing community* With a 2^16 = 64kb standard map at already 256 instrumented blocks, there is on average one collision. On average, a target has 10.000 to 50.000 instrumented blocks, hence the real collisions are between 750-18.000! -To reach a solution that prevents any collisions took several approaches and -many dead ends until we got to this: +Note that PCGUARD (our own modified implementation and the SANCOV PCGUARD +implementation from libfuzzer) also provides collision free coverage. +It is a bit slower though and can a few targets with very early constructors. * We instrument at link time when we have all files pre-compiled. * To instrument at link time, we compile in LTO (link time optimization) mode. * Our compiler (afl-clang-lto/afl-clang-lto++) takes care of setting the correct LTO options and runs our own afl-ld linker instead of the system linker. * The LLVM linker collects all LTO files to link and instruments them so that we - have non-colliding edge overage. + have non-colliding edge coverage. * We use a new (for afl) edge coverage - which is the same as in llvm -fsanitize=coverage edge coverage mode. :) The result: * 10-25% speed gain compared to llvm_mode -* guaranteed non-colliding edge coverage :-) +* guaranteed non-colliding edge coverage * The compile time, especially for binaries to an instrumented library, can be - much longer. + much (and sometimes much much) longer. Example build output from a libtiff build: @@ -59,70 +60,30 @@ AUTODICTIONARY: 11 strings found [+] Instrumented 12071 locations with no collisions (on average 1046 collisions would be in afl-gcc/afl-clang-fast) (non-hardened mode). ``` -## Getting llvm 11+ +## Getting LLVM 11+ -### Installing llvm version 11 or 12 +### Installing llvm -llvm 11 or even 12 should be available in all current Linux repositories. If you -use an outdated Linux distribution, read the next section. - -### Installing llvm from the llvm repository (version 12+) - -Installing the llvm snapshot builds is easy and mostly painless: - -In the following line, change `NAME` for your Debian or Ubuntu release name -(e.g., buster, focal, eon, etc.): +The best way to install LLVM is to follow [https://apt.llvm.org/](https://apt.llvm.org/) +e.g. for LLVM 15: ``` -echo deb http://apt.llvm.org/NAME/ llvm-toolchain-NAME NAME >> /etc/apt/sources.list +wget https://apt.llvm.org/llvm.sh +chmod +x llvm.sh +sudo ./llvm.sh 15 all ``` -Then add the pgp key of llvm and install the packages: +LLVM 11 to 16 should be available in all current Linux repositories. -``` -wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | apt-key add - -apt-get update && apt-get upgrade -y -apt-get install -y clang-12 clang-tools-12 libc++1-12 libc++-12-dev \ - libc++abi1-12 libc++abi-12-dev libclang1-12 libclang-12-dev \ - libclang-common-12-dev libclang-cpp12 libclang-cpp12-dev liblld-12 \ - liblld-12-dev liblldb-12 liblldb-12-dev libllvm12 libomp-12-dev \ - libomp5-12 lld-12 lldb-12 llvm-12 llvm-12-dev llvm-12-runtime llvm-12-tools -``` +## How to build afl-clang-lto + +That part is easy. +Just set `LLVM_CONFIG` to the llvm-config-VERSION and build AFL++, e.g. for +LLVM 15: -### Building llvm yourself (version 12+) - -Building llvm from GitHub takes quite some time and is not painless: - -```sh -sudo apt install binutils-dev # this is *essential*! -git clone --depth=1 https://github.com/llvm/llvm-project -cd llvm-project -mkdir build -cd build - -# Add -G Ninja if ninja-build installed -# "Building with ninja significantly improves your build time, especially with -# incremental builds, and improves your memory usage." -cmake \ - -DCLANG_INCLUDE_DOCS="OFF" \ - -DCMAKE_BUILD_TYPE=Release \ - -DLLVM_BINUTILS_INCDIR=/usr/include/ \ - -DLLVM_BUILD_LLVM_DYLIB="ON" \ - -DLLVM_ENABLE_BINDINGS="OFF" \ - -DLLVM_ENABLE_PROJECTS='clang;compiler-rt;libcxx;libcxxabi;libunwind;lld' \ - -DLLVM_ENABLE_WARNINGS="OFF" \ - -DLLVM_INCLUDE_BENCHMARKS="OFF" \ - -DLLVM_INCLUDE_DOCS="OFF" \ - -DLLVM_INCLUDE_EXAMPLES="OFF" \ - -DLLVM_INCLUDE_TESTS="OFF" \ - -DLLVM_LINK_LLVM_DYLIB="ON" \ - -DLLVM_TARGETS_TO_BUILD="host" \ - ../llvm/ -cmake --build . -j4 -export PATH="$(pwd)/bin:$PATH" -export LLVM_CONFIG="$(pwd)/bin/llvm-config" -export LD_LIBRARY_PATH="$(llvm-config --libdir)${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}" -cd /path/to/AFLplusplus/ +``` +cd ~/AFLplusplus +export LLVM_CONFIG=llvm-config-15 make sudo make install ``` @@ -135,10 +96,10 @@ Also, the instrument file listing (AFL_LLVM_ALLOWLIST/AFL_LLVM_DENYLIST -> [README.instrument_list.md](README.instrument_list.md)) and laf-intel/compcov (AFL_LLVM_LAF_* -> [README.laf-intel.md](README.laf-intel.md)) work. -Example: +Example (note that you might need to add the version, e.g. `llvm-ar-15`: ``` -CC=afl-clang-lto CXX=afl-clang-lto++ RANLIB=llvm-ranlib AR=llvm-ar ./configure +CC=afl-clang-lto CXX=afl-clang-lto++ RANLIB=llvm-ranlib AR=llvm-ar AS=llvm-as ./configure make ``` @@ -316,13 +277,13 @@ AS=llvm-as ... afl-clang-lto is still work in progress. Known issues: -* Anything that llvm 11+ cannot compile, afl-clang-lto cannot compile either - +* Anything that LLVM 11+ cannot compile, afl-clang-lto cannot compile either - obviously. * Anything that does not compile with LTO, afl-clang-lto cannot compile either - obviously. Hence, if building a target with afl-clang-lto fails, try to build it with -llvm12 and LTO enabled (`CC=clang-12`, `CXX=clang++-12`, `CFLAGS=-flto=full`, +LLVM 12 and LTO enabled (`CC=clang-12`, `CXX=clang++-12`, `CFLAGS=-flto=full`, and `CXXFLAGS=-flto=full`). If this succeeds, then there is an issue with afl-clang-lto. Please report at @@ -340,7 +301,7 @@ knows what this is doing. And the developer who implemented this didn't respond to emails.) In December then came the idea to implement this as a pass that is run via the -llvm "opt" program, which is performed via an own linker that afterwards calls +LLVM "opt" program, which is performed via an own linker that afterwards calls the real linker. This was first implemented in January and work ... kinda. The LTO time instrumentation worked, however, "how" the basic blocks were instrumented was a problem, as reducing duplicates turned out to be very, very @@ -352,13 +313,13 @@ dead-end too. The final idea to solve this came from domenukk who proposed to insert a block into an edge and then just use incremental counters ... and this worked! After some trials and errors to implement this vanhauser-thc found out that there is -actually an llvm function for this: SplitEdge() :-) +actually an LLVM function for this: SplitEdge() :-) -Still more problems came up though as this only works without bugs from llvm 9 +Still more problems came up though as this only works without bugs from LLVM 9 onwards, and with high optimization the link optimization ruins the instrumented control flow graph. -This is all now fixed with llvm 11+. The llvm's own linker is now able to load +This is all now fixed with LLVM 11+. The llvm's own linker is now able to load passes and this bypasses all problems we had. -Happy end :) \ No newline at end of file +Happy end :) diff --git a/instrumentation/SanitizerCoverageLTO.so.cc b/instrumentation/SanitizerCoverageLTO.so.cc index 721bc487..e41f19b6 100644 --- a/instrumentation/SanitizerCoverageLTO.so.cc +++ b/instrumentation/SanitizerCoverageLTO.so.cc @@ -17,7 +17,9 @@ #include "llvm/Transforms/Instrumentation/SanitizerCoverage.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/SmallVector.h" -#include "llvm/ADT/Triple.h" +#if LLVM_VERSION_MAJOR < 17 + #include "llvm/ADT/Triple.h" +#endif #include "llvm/Analysis/EHPersonalities.h" #include "llvm/Analysis/PostDominators.h" #include "llvm/Analysis/ValueTracking.h" @@ -111,6 +113,12 @@ static cl::opt<bool> ClPruneBlocks( cl::desc("Reduce the number of instrumented blocks"), cl::Hidden, cl::init(true)); +namespace llvm { + +void initializeModuleSanitizerCoverageLTOLegacyPassPass(PassRegistry &PB); + +} + namespace { SanitizerCoverageOptions getOptions(int LegacyCoverageLevel) { @@ -230,6 +238,7 @@ class ModuleSanitizerCoverageLTO // const SpecialCaseList * Allowlist; // const SpecialCaseList * Blocklist; uint32_t autodictionary = 1; + uint32_t autodictionary_no_main = 0; uint32_t inst = 0; uint32_t afl_global_id = 0; uint32_t unhandled = 0; @@ -255,13 +264,13 @@ class ModuleSanitizerCoverageLTO }; -class ModuleSanitizerCoverageLegacyPass : public ModulePass { +class ModuleSanitizerCoverageLTOLegacyPass : public ModulePass { public: static char ID; StringRef getPassName() const override { - return "sancov"; + return "sancov-lto"; } @@ -272,11 +281,11 @@ class ModuleSanitizerCoverageLegacyPass : public ModulePass { } - ModuleSanitizerCoverageLegacyPass( + ModuleSanitizerCoverageLTOLegacyPass( const SanitizerCoverageOptions &Options = SanitizerCoverageOptions()) : ModulePass(ID), Options(Options) { - initializeModuleSanitizerCoverageLegacyPassPass( + initializeModuleSanitizerCoverageLTOLegacyPassPass( *PassRegistry::getPassRegistry()); } @@ -318,8 +327,11 @@ llvmGetPassPluginInfo() { #if LLVM_VERSION_MAJOR <= 13 using OptimizationLevel = typename PassBuilder::OptimizationLevel; #endif - // PB.registerFullLinkTimeOptimizationLastEPCallback( +#if LLVM_VERSION_MAJOR >= 15 + PB.registerFullLinkTimeOptimizationLastEPCallback( +#else PB.registerOptimizerLastEPCallback( +#endif [](ModulePassManager &MPM, OptimizationLevel OL) { MPM.addPass(ModuleSanitizerCoverageLTO()); @@ -402,7 +414,8 @@ bool ModuleSanitizerCoverageLTO::instrumentModule( /* Show a banner */ setvbuf(stdout, NULL, _IONBF, 0); - if (getenv("AFL_DEBUG")) debug = 1; + if (getenv("AFL_DEBUG")) { debug = 1; } + if (getenv("AFL_LLVM_DICT2FILE_NO_MAIN")) { autodictionary_no_main = 1; } if ((isatty(2) && !getenv("AFL_QUIET")) || debug) { @@ -420,6 +433,8 @@ bool ModuleSanitizerCoverageLTO::instrumentModule( if ((afl_global_id = atoi(ptr)) < 0) FATAL("AFL_LLVM_LTO_STARTID value of \"%s\" is negative\n", ptr); + if (afl_global_id < 4) { afl_global_id = 4; } + if ((ptr = getenv("AFL_LLVM_DOCUMENT_IDS")) != NULL) { dFile.open(ptr, std::ofstream::out | std::ofstream::app); @@ -494,6 +509,13 @@ bool ModuleSanitizerCoverageLTO::instrumentModule( if (!isInInstrumentList(&F, MNAME) || !F.size()) { continue; } + if (autodictionary_no_main && + (!F.getName().compare("main") || !F.getName().compare("_main"))) { + + continue; + + } + for (auto &BB : F) { for (auto &IN : BB) { @@ -1750,30 +1772,22 @@ std::string ModuleSanitizerCoverageLTO::getSectionName( } -char ModuleSanitizerCoverageLegacyPass::ID = 0; +char ModuleSanitizerCoverageLTOLegacyPass::ID = 0; -INITIALIZE_PASS_BEGIN(ModuleSanitizerCoverageLegacyPass, "sancov", +INITIALIZE_PASS_BEGIN(ModuleSanitizerCoverageLTOLegacyPass, "sancov-lto", "Pass for instrumenting coverage on functions", false, false) INITIALIZE_PASS_DEPENDENCY(DominatorTreeWrapperPass) INITIALIZE_PASS_DEPENDENCY(PostDominatorTreeWrapperPass) -INITIALIZE_PASS_END(ModuleSanitizerCoverageLegacyPass, "sancov", +INITIALIZE_PASS_END(ModuleSanitizerCoverageLTOLegacyPass, "sancov-lto", "Pass for instrumenting coverage on functions", false, false) -ModulePass *llvm::createModuleSanitizerCoverageLegacyPassPass( - const SanitizerCoverageOptions &Options, - const std::vector<std::string> &AllowlistFiles, - const std::vector<std::string> &BlocklistFiles) { - - return new ModuleSanitizerCoverageLegacyPass(Options); - -} - +#if LLVM_VERSION_MAJOR < 16 static void registerLTOPass(const PassManagerBuilder &, legacy::PassManagerBase &PM) { - auto p = new ModuleSanitizerCoverageLegacyPass(); + auto p = new ModuleSanitizerCoverageLTOLegacyPass(); PM.add(p); } @@ -1784,8 +1798,9 @@ static RegisterStandardPasses RegisterCompTransPass( static RegisterStandardPasses RegisterCompTransPass0( PassManagerBuilder::EP_EnabledOnOptLevel0, registerLTOPass); -#if LLVM_VERSION_MAJOR >= 11 + #if LLVM_VERSION_MAJOR >= 11 static RegisterStandardPasses RegisterCompTransPassLTO( PassManagerBuilder::EP_FullLinkTimeOptimizationLast, registerLTOPass); + #endif #endif diff --git a/instrumentation/SanitizerCoveragePCGUARD.so.cc b/instrumentation/SanitizerCoveragePCGUARD.so.cc index e22c9ead..85b1ddd5 100644 --- a/instrumentation/SanitizerCoveragePCGUARD.so.cc +++ b/instrumentation/SanitizerCoveragePCGUARD.so.cc @@ -13,7 +13,9 @@ #include "llvm/Transforms/Instrumentation/SanitizerCoverage.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/SmallVector.h" -#include "llvm/ADT/Triple.h" +#if LLVM_VERSION_MAJOR < 17 + #include "llvm/ADT/Triple.h" +#endif #include "llvm/Analysis/EHPersonalities.h" #include "llvm/Analysis/PostDominators.h" #include "llvm/IR/CFG.h" @@ -209,57 +211,6 @@ class ModuleSanitizerCoverageAFL }; -class ModuleSanitizerCoverageLegacyPass : public ModulePass { - - public: - ModuleSanitizerCoverageLegacyPass( - const SanitizerCoverageOptions &Options = SanitizerCoverageOptions()) - : ModulePass(ID), Options(Options) { - - initializeModuleSanitizerCoverageLegacyPassPass( - *PassRegistry::getPassRegistry()); - - } - - bool runOnModule(Module &M) override { - - ModuleSanitizerCoverageAFL ModuleSancov(Options); - auto DTCallback = [this](Function &F) -> const DominatorTree * { - - return &this->getAnalysis<DominatorTreeWrapperPass>(F).getDomTree(); - - }; - - auto PDTCallback = [this](Function &F) -> const PostDominatorTree * { - - return &this->getAnalysis<PostDominatorTreeWrapperPass>(F) - .getPostDomTree(); - - }; - - return ModuleSancov.instrumentModule(M, DTCallback, PDTCallback); - - } - - /*static*/ char ID; // Pass identification, replacement for typeid - StringRef getPassName() const override { - - return "ModuleSanitizerCoverage"; - - } - - void getAnalysisUsage(AnalysisUsage &AU) const override { - - AU.addRequired<DominatorTreeWrapperPass>(); - AU.addRequired<PostDominatorTreeWrapperPass>(); - - } - - private: - SanitizerCoverageOptions Options; - -}; - } // namespace #if LLVM_VERSION_MAJOR >= 11 /* use new pass manager */ @@ -779,7 +730,11 @@ GlobalVariable *ModuleSanitizerCoverageAFL::CreateFunctionLocalArrayInSection( Array->setSection(getSectionName(Section)); #if (LLVM_VERSION_MAJOR >= 11) || \ (LLVM_VERSION_MAJOR == 10 && LLVM_VERSION_MINOR >= 1) + #if LLVM_VERSION_MAJOR >= 16 + Array->setAlignment(Align(DL->getTypeStoreSize(Ty).getFixedValue())); + #else Array->setAlignment(Align(DL->getTypeStoreSize(Ty).getFixedSize())); + #endif #else Array->setAlignment(Align(4)); // cheating #endif @@ -850,7 +805,8 @@ void ModuleSanitizerCoverageAFL::CreateFunctionLocalArrays( bool ModuleSanitizerCoverageAFL::InjectCoverage( Function &F, ArrayRef<BasicBlock *> AllBlocks, bool IsLeafFunc) { - uint32_t cnt_cov = 0, cnt_sel = 0, cnt_sel_inc = 0; + uint32_t cnt_cov = 0, cnt_sel = 0, cnt_sel_inc = 0; + static uint32_t first = 1; for (auto &BB : F) { @@ -876,9 +832,11 @@ bool ModuleSanitizerCoverageAFL::InjectCoverage( } - if (FuncName.compare(StringRef("__afl_coverage_interesting"))) continue; + if (!FuncName.compare(StringRef("__afl_coverage_interesting"))) { - cnt_cov++; + cnt_cov++; + + } } @@ -917,7 +875,8 @@ bool ModuleSanitizerCoverageAFL::InjectCoverage( } /* Create PCGUARD array */ - CreateFunctionLocalArrays(F, AllBlocks, cnt_cov + cnt_sel_inc); + CreateFunctionLocalArrays(F, AllBlocks, first + cnt_cov + cnt_sel_inc); + if (first) { first = 0; } selects += cnt_sel; uint32_t special = 0, local_selects = 0, skip_next = 0; @@ -1103,10 +1062,10 @@ bool ModuleSanitizerCoverageAFL::InjectCoverage( ModuleSanitizerCoverageAFL::SetNoSanitizeMetadata(MapPtr); /* - std::string errMsg; - raw_string_ostream os(errMsg); - result->print(os); - fprintf(stderr, "X: %s\n", os.str().c_str()); + std::string errMsg; + raw_string_ostream os(errMsg); + result->print(os); + fprintf(stderr, "X: %s\n", os.str().c_str()); */ while (1) { @@ -1526,26 +1485,3 @@ std::string ModuleSanitizerCoverageAFL::getSectionEnd( } -#if 0 - -char ModuleSanitizerCoverageLegacyPass::ID = 0; -INITIALIZE_PASS_BEGIN(ModuleSanitizerCoverageLegacyPass, "sancov", - "Pass for instrumenting coverage on functions", false, - false) -INITIALIZE_PASS_DEPENDENCY(DominatorTreeWrapperPass) -INITIALIZE_PASS_DEPENDENCY(PostDominatorTreeWrapperPass) -INITIALIZE_PASS_END(ModuleSanitizerCoverageLegacyPass, "sancov", - "Pass for instrumenting coverage on functions", false, - false) -ModulePass *llvm::createModuleSanitizerCoverageLegacyPassPass( - const SanitizerCoverageOptions &Options, - const std::vector<std::string> &AllowlistFiles, - const std::vector<std::string> &BlocklistFiles) { - - return new ModuleSanitizerCoverageLegacyPass(Options, AllowlistFiles, - BlocklistFiles); - -} - -#endif - diff --git a/instrumentation/afl-compiler-rt.o.c b/instrumentation/afl-compiler-rt.o.c index 1759898e..e0e40983 100644 --- a/instrumentation/afl-compiler-rt.o.c +++ b/instrumentation/afl-compiler-rt.o.c @@ -3,7 +3,7 @@ ------------------------------------------------ Copyright 2015, 2016 Google Inc. All rights reserved. - Copyright 2019-2022 AFLplusplus Project. All rights reserved. + Copyright 2019-2023 AFLplusplus Project. All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -97,18 +97,23 @@ u8 *__afl_dictionary; u8 *__afl_fuzz_ptr; static u32 __afl_fuzz_len_dummy; u32 *__afl_fuzz_len = &__afl_fuzz_len_dummy; +int __afl_sharedmem_fuzzing __attribute__((weak)); u32 __afl_final_loc; u32 __afl_map_size = MAP_SIZE; u32 __afl_dictionary_len; u64 __afl_map_addr; +u32 __afl_first_final_loc; + +/* 1 if we are running in afl, and the forkserver was started, else 0 */ +u32 __afl_connected = 0; // for the __AFL_COVERAGE_ON/__AFL_COVERAGE_OFF features to work: int __afl_selective_coverage __attribute__((weak)); int __afl_selective_coverage_start_off __attribute__((weak)); static int __afl_selective_coverage_temp = 1; -#if defined(__ANDROID__) || defined(__HAIKU__) +#if defined(__ANDROID__) || defined(__HAIKU__) || defined(NO_TLS) PREV_LOC_T __afl_prev_loc[NGRAM_SIZE_MAX]; PREV_LOC_T __afl_prev_caller[CTX_MAX_K]; u32 __afl_prev_ctx; @@ -118,8 +123,6 @@ __thread PREV_LOC_T __afl_prev_caller[CTX_MAX_K]; __thread u32 __afl_prev_ctx; #endif -int __afl_sharedmem_fuzzing __attribute__((weak)); - struct cmp_map *__afl_cmp_map; struct cmp_map *__afl_cmp_map_backup; @@ -146,6 +149,7 @@ u32 __afl_already_initialized_shm; u32 __afl_already_initialized_forkserver; u32 __afl_already_initialized_first; u32 __afl_already_initialized_second; +u32 __afl_already_initialized_early; u32 __afl_already_initialized_init; /* Dummy pipe for area_is_valid() */ @@ -159,6 +163,7 @@ static void at_exit(int signal) { if (unlikely(child_pid > 0)) { kill(child_pid, SIGKILL); + waitpid(child_pid, NULL, 0); child_pid = -1; } @@ -319,13 +324,16 @@ static void __afl_map_shm(void) { } else { - if (!getenv("AFL_QUIET")) + if (__afl_final_loc > MAP_INITIAL_SIZE && !getenv("AFL_QUIET")) { + fprintf(stderr, "Warning: AFL++ tools might need to set AFL_MAP_SIZE to %u " "to be able to run this instrumented program if this " "crashes!\n", __afl_final_loc); + } + } } @@ -343,29 +351,51 @@ static void __afl_map_shm(void) { } - if (!id_str && __afl_area_ptr_dummy == __afl_area_initial) { + if (__afl_sharedmem_fuzzing && (!id_str || !getenv(SHM_FUZZ_ENV_VAR) || + fcntl(FORKSRV_FD, F_GETFD) == -1 || + fcntl(FORKSRV_FD + 1, F_GETFD) == -1)) { + + if (__afl_debug) { + + fprintf(stderr, + "DEBUG: running not inside afl-fuzz, disabling shared memory " + "testcases\n"); + + } + + __afl_sharedmem_fuzzing = 0; + + } + + if (!id_str) { u32 val = 0; u8 *ptr; - if ((ptr = getenv("AFL_MAP_SIZE")) != NULL) val = atoi(ptr); + if ((ptr = getenv("AFL_MAP_SIZE")) != NULL) { val = atoi(ptr); } if (val > MAP_INITIAL_SIZE) { __afl_map_size = val; - __afl_area_ptr_dummy = malloc(__afl_map_size); - if (!__afl_area_ptr_dummy) { - fprintf(stderr, - "Error: AFL++ could not aquire %u bytes of memory, exiting!\n", - __afl_map_size); - exit(-1); + } else { + + if (__afl_first_final_loc > MAP_INITIAL_SIZE) { + + // done in second stage constructor + __afl_map_size = __afl_first_final_loc; + + } else { + + __afl_map_size = MAP_INITIAL_SIZE; } - } else { + } - __afl_map_size = MAP_INITIAL_SIZE; + if (__afl_map_size > MAP_INITIAL_SIZE && __afl_final_loc < __afl_map_size) { + + __afl_final_loc = __afl_map_size; } @@ -516,7 +546,9 @@ static void __afl_map_shm(void) { } - } else if (__afl_final_loc > __afl_map_size) { + } else if (__afl_final_loc > MAP_INITIAL_SIZE && + + __afl_final_loc > __afl_first_final_loc) { if (__afl_area_initial != __afl_area_ptr_dummy) { @@ -531,13 +563,13 @@ static void __afl_map_shm(void) { if (!__afl_area_ptr_dummy) { fprintf(stderr, - "Error: AFL++ could not aquire %u bytes of memory, exiting!\n", + "Error: AFL++ could not acquire %u bytes of memory, exiting!\n", __afl_final_loc); exit(-1); } - } + } // else: nothing to be done __afl_area_ptr_backup = __afl_area_ptr; @@ -745,10 +777,10 @@ static void __afl_start_snapshots(void) { assume we're not running in forkserver mode and just execute program. */ status |= (FS_OPT_ENABLED | FS_OPT_SNAPSHOT | FS_OPT_NEWCMPLOG); - if (__afl_sharedmem_fuzzing != 0) status |= FS_OPT_SHDMEM_FUZZ; + if (__afl_sharedmem_fuzzing) { status |= FS_OPT_SHDMEM_FUZZ; } if (__afl_map_size <= FS_OPT_MAX_MAPSIZE) status |= (FS_OPT_SET_MAPSIZE(__afl_map_size) | FS_OPT_MAPSIZE); - if (__afl_dictionary_len && __afl_dictionary) status |= FS_OPT_AUTODICT; + if (__afl_dictionary_len && __afl_dictionary) { status |= FS_OPT_AUTODICT; } memcpy(tmp, &status, 4); if (write(FORKSRV_FD + 1, tmp, 4) != 4) { return; } @@ -1009,7 +1041,7 @@ static void __afl_start_forkserver(void) { } - if (__afl_sharedmem_fuzzing != 0) { status_for_fsrv |= FS_OPT_SHDMEM_FUZZ; } + if (__afl_sharedmem_fuzzing) { status_for_fsrv |= FS_OPT_SHDMEM_FUZZ; } if (status_for_fsrv) { status_for_fsrv |= (FS_OPT_ENABLED | FS_OPT_NEWCMPLOG); @@ -1023,6 +1055,8 @@ static void __afl_start_forkserver(void) { if (write(FORKSRV_FD + 1, tmp, 4) != 4) { return; } + __afl_connected = 1; + if (__afl_sharedmem_fuzzing || (__afl_dictionary_len && __afl_dictionary)) { if (read(FORKSRV_FD, &was_killed, 4) != 4) _exit(1); @@ -1233,13 +1267,9 @@ int __afl_persistent_loop(unsigned int max_cnt) { iteration, it's our job to erase any trace of whatever happened before the loop. */ - if (is_persistent) { - - memset(__afl_area_ptr, 0, __afl_map_size); - __afl_area_ptr[0] = 1; - memset(__afl_prev_loc, 0, NGRAM_SIZE_MAX * sizeof(PREV_LOC_T)); - - } + memset(__afl_area_ptr, 0, __afl_map_size); + __afl_area_ptr[0] = 1; + memset(__afl_prev_loc, 0, NGRAM_SIZE_MAX * sizeof(PREV_LOC_T)); cycle_cnt = max_cnt; first_pass = 0; @@ -1247,34 +1277,28 @@ int __afl_persistent_loop(unsigned int max_cnt) { return 1; - } - - if (is_persistent) { + } else if (--cycle_cnt) { - if (--cycle_cnt) { + raise(SIGSTOP); - raise(SIGSTOP); - - __afl_area_ptr[0] = 1; - memset(__afl_prev_loc, 0, NGRAM_SIZE_MAX * sizeof(PREV_LOC_T)); - __afl_selective_coverage_temp = 1; + __afl_area_ptr[0] = 1; + memset(__afl_prev_loc, 0, NGRAM_SIZE_MAX * sizeof(PREV_LOC_T)); + __afl_selective_coverage_temp = 1; - return 1; + return 1; - } else { + } else { - /* When exiting __AFL_LOOP(), make sure that the subsequent code that - follows the loop is not traced. We do that by pivoting back to the - dummy output region. */ + /* When exiting __AFL_LOOP(), make sure that the subsequent code that + follows the loop is not traced. We do that by pivoting back to the + dummy output region. */ - __afl_area_ptr = __afl_area_ptr_dummy; + __afl_area_ptr = __afl_area_ptr_dummy; - } + return 0; } - return 0; - } /* This one can be called from user code when deferred forkserver mode @@ -1350,6 +1374,9 @@ __attribute__((constructor(EARLY_FS_PRIO))) void __early_forkserver(void) { __attribute__((constructor(CTOR_PRIO))) void __afl_auto_early(void) { + if (__afl_already_initialized_early) return; + __afl_already_initialized_early = 1; + is_persistent = !!getenv(PERSIST_ENV_VAR); if (getenv("AFL_DISABLE_LLVM_INSTRUMENTATION")) return; @@ -1375,21 +1402,24 @@ __attribute__((constructor(1))) void __afl_auto_second(void) { if (getenv("AFL_DISABLE_LLVM_INSTRUMENTATION")) return; u8 *ptr; - if (__afl_final_loc) { + if (__afl_final_loc > MAP_INITIAL_SIZE) { + + __afl_first_final_loc = __afl_final_loc + 1; if (__afl_area_ptr && __afl_area_ptr != __afl_area_initial) free(__afl_area_ptr); if (__afl_map_addr) - ptr = (u8 *)mmap((void *)__afl_map_addr, __afl_final_loc, + ptr = (u8 *)mmap((void *)__afl_map_addr, __afl_first_final_loc, PROT_READ | PROT_WRITE, MAP_FIXED_NOREPLACE | MAP_SHARED | MAP_ANONYMOUS, -1, 0); else - ptr = (u8 *)malloc(__afl_final_loc); + ptr = (u8 *)malloc(__afl_first_final_loc); if (ptr && (ssize_t)ptr != -1) { __afl_area_ptr = ptr; + __afl_area_ptr_dummy = __afl_area_ptr; __afl_area_ptr_backup = __afl_area_ptr; } @@ -1407,14 +1437,18 @@ __attribute__((constructor(0))) void __afl_auto_first(void) { __afl_already_initialized_first = 1; if (getenv("AFL_DISABLE_LLVM_INSTRUMENTATION")) return; - u8 *ptr = (u8 *)malloc(MAP_INITIAL_SIZE); - if (ptr && (ssize_t)ptr != -1) { + /* + u8 *ptr = (u8 *)malloc(MAP_INITIAL_SIZE); + + if (ptr && (ssize_t)ptr != -1) { - __afl_area_ptr = ptr; - __afl_area_ptr_backup = __afl_area_ptr; + __afl_area_ptr = ptr; + __afl_area_ptr_backup = __afl_area_ptr; - } + } + + */ } // ptr memleak report is a false positive @@ -1484,6 +1518,14 @@ void __sanitizer_cov_trace_pc_guard_init(uint32_t *start, uint32_t *stop) { _is_sancov = 1; + if (!getenv("AFL_DUMP_MAP_SIZE")) { + + __afl_auto_first(); + __afl_auto_second(); + __afl_auto_early(); + + } + if (__afl_debug) { fprintf(stderr, @@ -1494,7 +1536,21 @@ void __sanitizer_cov_trace_pc_guard_init(uint32_t *start, uint32_t *stop) { } - if (start == stop || *start) return; + if (start == stop || *start) { return; } + + x = getenv("AFL_INST_RATIO"); + if (x) { + + inst_ratio = (u32)atoi(x); + + if (!inst_ratio || inst_ratio > 100) { + + fprintf(stderr, "[-] ERROR: Invalid AFL_INST_RATIO (must be 1-100).\n"); + abort(); + + } + + } // If a dlopen of an instrumented library happens after the forkserver then // we have a problem as we cannot increase the coverage map anymore. @@ -1507,85 +1563,55 @@ void __sanitizer_cov_trace_pc_guard_init(uint32_t *start, uint32_t *stop) { "[-] FATAL: forkserver is already up, but an instrumented dlopen() " "library loaded afterwards. You must AFL_PRELOAD such libraries to " "be able to fuzz them or LD_PRELOAD to run outside of afl-fuzz.\n" - "To ignore this set AFL_IGNORE_PROBLEMS=1.\n"); + "To ignore this set AFL_IGNORE_PROBLEMS=1 but this will be bad for " + "coverage.\n"); abort(); } else { - static u32 offset = 4; + static u32 offset = 5; while (start < stop) { - *(start++) = offset; - if (unlikely(++offset >= __afl_final_loc)) { offset = 4; } - - } - - } - - } - - x = getenv("AFL_INST_RATIO"); - if (x) { inst_ratio = (u32)atoi(x); } - - if (!inst_ratio || inst_ratio > 100) { - - fprintf(stderr, "[-] ERROR: Invalid AFL_INST_RATIO (must be 1-100).\n"); - abort(); - - } - - /* instrumented code is loaded *after* our forkserver is up. this is a - problem. We cannot prevent collisions then :( */ - /* - if (__afl_already_initialized_forkserver && - __afl_final_loc + 1 + stop - start > __afl_map_size) { - - if (__afl_debug) { - - fprintf(stderr, "Warning: new instrumented code after the forkserver!\n"); - - } - - __afl_final_loc = 2; + if (likely(inst_ratio == 100) || R(100) < inst_ratio) { - if (1 + stop - start > __afl_map_size) { + *(start++) = offset; - *(start++) = ++__afl_final_loc; + } else { - while (start < stop) { + *(start++) = 0; // write to map[0] - if (R(100) < inst_ratio) - *start = ++__afl_final_loc % __afl_map_size; - else - *start = 4; + } - start++; + if (unlikely(++offset >= __afl_final_loc)) { offset = 5; } } - return; - } - } + return; // we are done for this special case - */ + } /* Make sure that the first element in the range is always set - we use that to avoid duplicate calls (which can happen as an artifact of the underlying implementation in LLVM). */ + if (__afl_final_loc < 5) __afl_final_loc = 5; // we skip the first 5 entries + *(start++) = ++__afl_final_loc; while (start < stop) { - if (R(100) < inst_ratio) - *start = ++__afl_final_loc; - else - *start = 4; + if (likely(inst_ratio == 100) || R(100) < inst_ratio) { + + *(start++) = ++__afl_final_loc; + + } else { - start++; + *(start++) = 0; // write to map[0] + + } } @@ -1597,17 +1623,23 @@ void __sanitizer_cov_trace_pc_guard_init(uint32_t *start, uint32_t *stop) { } - if (__afl_already_initialized_shm && __afl_final_loc > __afl_map_size) { + if (__afl_already_initialized_shm) { - if (__afl_debug) { + if (__afl_final_loc > __afl_map_size) { + + if (__afl_debug) { + + fprintf(stderr, "Reinit shm necessary (+%u)\n", + __afl_final_loc - __afl_map_size); + + } - fprintf(stderr, "Reinit shm necessary (+%u)\n", - __afl_final_loc - __afl_map_size); + __afl_unmap_shm(); + __afl_map_shm(); } - __afl_unmap_shm(); - __afl_map_shm(); + __afl_map_size = __afl_final_loc + 1; } diff --git a/instrumentation/afl-gcc-cmplog-pass.so.cc b/instrumentation/afl-gcc-cmplog-pass.so.cc index 5e5792c3..b4e6fda9 100644 --- a/instrumentation/afl-gcc-cmplog-pass.so.cc +++ b/instrumentation/afl-gcc-cmplog-pass.so.cc @@ -3,7 +3,7 @@ Copyright 2014-2019 Free Software Foundation, Inc Copyright 2015, 2016 Google Inc. All rights reserved. Copyright 2019-2020 AFLplusplus Project. All rights reserved. - Copyright 2019-2022 AdaCore + Copyright 2019-2023 AdaCore Written by Alexandre Oliva <oliva@adacore.com>, based on the AFL++ LLVM CmpLog pass by Andrea Fioraldi <andreafioraldi@gmail.com>, and @@ -243,9 +243,9 @@ struct afl_cmplog_pass : afl_base_pass { tree t = build_nonstandard_integer_type(sz, 1); - tree s = make_ssa_name(t); - gimple *g = gimple_build_assign(s, VIEW_CONVERT_EXPR, - build1(VIEW_CONVERT_EXPR, t, lhs)); + tree s = make_ssa_name(t); + gimple g = gimple_build_assign(s, VIEW_CONVERT_EXPR, + build1(VIEW_CONVERT_EXPR, t, lhs)); lhs = s; gsi_insert_before(&gsi, g, GSI_SAME_STMT); @@ -263,8 +263,8 @@ struct afl_cmplog_pass : afl_base_pass { lhs = fold_convert_loc(UNKNOWN_LOCATION, t, lhs); if (!is_gimple_val(lhs)) { - tree s = make_ssa_name(t); - gimple *g = gimple_build_assign(s, lhs); + tree s = make_ssa_name(t); + gimple g = gimple_build_assign(s, lhs); lhs = s; gsi_insert_before(&gsi, g, GSI_SAME_STMT); @@ -273,16 +273,16 @@ struct afl_cmplog_pass : afl_base_pass { rhs = fold_convert_loc(UNKNOWN_LOCATION, t, rhs); if (!is_gimple_val(rhs)) { - tree s = make_ssa_name(t); - gimple *g = gimple_build_assign(s, rhs); + tree s = make_ssa_name(t); + gimple g = gimple_build_assign(s, rhs); rhs = s; gsi_insert_before(&gsi, g, GSI_SAME_STMT); } /* Insert the call. */ - tree att = build_int_cst(t8u, attr); - gimple *call; + tree att = build_int_cst(t8u, attr); + gimple call; if (pass_n) call = gimple_build_call(fn, 4, lhs, rhs, att, build_int_cst(t8u, sz / 8 - 1)); @@ -305,7 +305,7 @@ struct afl_cmplog_pass : afl_base_pass { gimple_stmt_iterator gsi = gsi_last_bb(bb); if (gsi_end_p(gsi)) continue; - gimple *stmt = gsi_stmt(gsi); + gimple stmt = gsi_stmt(gsi); if (gimple_code(stmt) == GIMPLE_COND) { diff --git a/instrumentation/afl-gcc-cmptrs-pass.so.cc b/instrumentation/afl-gcc-cmptrs-pass.so.cc index e9e2fe0d..dbb408b0 100644 --- a/instrumentation/afl-gcc-cmptrs-pass.so.cc +++ b/instrumentation/afl-gcc-cmptrs-pass.so.cc @@ -3,7 +3,7 @@ Copyright 2014-2019 Free Software Foundation, Inc Copyright 2015, 2016 Google Inc. All rights reserved. Copyright 2019-2020 AFLplusplus Project. All rights reserved. - Copyright 2019-2022 AdaCore + Copyright 2019-2023 AdaCore Written by Alexandre Oliva <oliva@adacore.com>, based on the AFL++ LLVM CmpLog Routines pass by Andrea Fioraldi @@ -241,7 +241,7 @@ struct afl_cmptrs_pass : afl_base_pass { for (gimple_stmt_iterator gsi = gsi_after_labels(bb); !gsi_end_p(gsi); gsi_next(&gsi)) { - gimple *stmt = gsi_stmt(gsi); + gimple stmt = gsi_stmt(gsi); /* We're only interested in GIMPLE_CALLs. */ if (gimple_code(stmt) != GIMPLE_CALL) continue; @@ -291,8 +291,8 @@ struct afl_cmptrs_pass : afl_base_pass { tree c = fold_convert_loc(UNKNOWN_LOCATION, tp8u, arg[i]); if (!is_gimple_val(c)) { - tree s = make_ssa_name(tp8u); - gimple *g = gimple_build_assign(s, c); + tree s = make_ssa_name(tp8u); + gimple g = gimple_build_assign(s, c); c = s; gsi_insert_before(&gsi, g, GSI_SAME_STMT); @@ -302,7 +302,7 @@ struct afl_cmptrs_pass : afl_base_pass { } - gimple *call = gimple_build_call(fn, 2, arg[0], arg[1]); + gimple call = gimple_build_call(fn, 2, arg[0], arg[1]); gsi_insert_before(&gsi, call, GSI_SAME_STMT); } diff --git a/instrumentation/afl-gcc-common.h b/instrumentation/afl-gcc-common.h index 2b71bd22..1d5eb466 100644 --- a/instrumentation/afl-gcc-common.h +++ b/instrumentation/afl-gcc-common.h @@ -2,7 +2,7 @@ Copyright 2014-2019 Free Software Foundation, Inc Copyright 2015, 2016 Google Inc. All rights reserved. - Copyright 2019-2022 AdaCore + Copyright 2019-2023 AdaCore Written by Alexandre Oliva <oliva@adacore.com>, based on the AFL++ GCC plugin. @@ -498,3 +498,11 @@ struct afl_base_pass : gimple_opt_pass { } // namespace +// compatibility for older gcc versions +#if (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) >= \ + 60200 /* >= version 6.2.0 */ + #define gimple gimple * +#else + #define gimple gimple +#endif + diff --git a/instrumentation/afl-gcc-pass.so.cc b/instrumentation/afl-gcc-pass.so.cc index 052b3159..4d7fd0ef 100644 --- a/instrumentation/afl-gcc-pass.so.cc +++ b/instrumentation/afl-gcc-pass.so.cc @@ -2,7 +2,7 @@ Copyright 2014-2019 Free Software Foundation, Inc Copyright 2015, 2016 Google Inc. All rights reserved. - Copyright 2019-2022 AdaCore + Copyright 2019-2023 AdaCore Written by Alexandre Oliva <oliva@adacore.com>, based on the AFL LLVM pass by Laszlo Szekeres <lszekeres@google.com> and Michal @@ -125,7 +125,10 @@ */ #include "afl-gcc-common.h" -#include "memmodel.h" +#if (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) >= \ + 60200 /* >= version 6.2.0 */ + #include "memmodel.h" +#endif /* This plugin, being under the same license as GCC, satisfies the "GPL-compatible Software" definition in the GCC RUNTIME LIBRARY diff --git a/instrumentation/afl-llvm-common.cc b/instrumentation/afl-llvm-common.cc index 5fcf27fb..5d82aa25 100644 --- a/instrumentation/afl-llvm-common.cc +++ b/instrumentation/afl-llvm-common.cc @@ -12,6 +12,7 @@ #include <list> #include <string> #include <fstream> +#include <cmath> #include <llvm/Support/raw_ostream.h> @@ -288,6 +289,7 @@ void scanForDangerousFunctions(llvm::Module *M) { StringRef ifunc_name = IF.getName(); Constant *r = IF.getResolver(); + if (r->getNumOperands() == 0) { continue; } StringRef r_name = cast<Function>(r->getOperand(0))->getName(); if (!be_quiet) fprintf(stderr, diff --git a/instrumentation/afl-llvm-common.h b/instrumentation/afl-llvm-common.h index dee5f9fc..16a13da5 100644 --- a/instrumentation/afl-llvm-common.h +++ b/instrumentation/afl-llvm-common.h @@ -8,6 +8,7 @@ #include <list> #include <string> #include <fstream> +#include <optional> #include <sys/time.h> #include "llvm/Config/llvm-config.h" @@ -35,6 +36,12 @@ typedef long double max_align_t; #if LLVM_VERSION_MAJOR >= 11 #define MNAME M.getSourceFileName() #define FMNAME F.getParent()->getSourceFileName() + #if LLVM_VERSION_MAJOR >= 16 +// None becomes deprecated +// the standard std::nullopt_t is recommended instead +// from C++17 and onwards. +constexpr std::nullopt_t None = std::nullopt; + #endif #else #define MNAME std::string("") #define FMNAME std::string("") diff --git a/instrumentation/afl-llvm-dict2file.so.cc b/instrumentation/afl-llvm-dict2file.so.cc index fd8baea2..97f1d47f 100644 --- a/instrumentation/afl-llvm-dict2file.so.cc +++ b/instrumentation/afl-llvm-dict2file.so.cc @@ -4,7 +4,7 @@ Written by Marc Heuse <mh@mh-sec.de> - Copyright 2019-2022 AFLplusplus Project. All rights reserved. + Copyright 2019-2023 AFLplusplus Project. All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -182,7 +182,7 @@ bool AFLdict2filePass::runOnModule(Module &M) { DenseMap<Value *, std::string *> valueMap; char *ptr; - int found = 0; + int found = 0, handle_main = 1; /* Show a banner */ setvbuf(stdout, NULL, _IONBF, 0); @@ -192,10 +192,14 @@ bool AFLdict2filePass::runOnModule(Module &M) { SAYF(cCYA "afl-llvm-dict2file" VERSION cRST " by Marc \"vanHauser\" Heuse <mh@mh-sec.de>\n"); - } else + } else { be_quiet = 1; + } + + if (getenv("AFL_LLVM_DICT2FILE_NO_MAIN")) { handle_main = 0; } + scanForDangerousFunctions(&M); ptr = getenv("AFL_LLVM_DICT2FILE"); @@ -210,7 +214,14 @@ bool AFLdict2filePass::runOnModule(Module &M) { for (auto &F : M) { - if (isIgnoreFunction(&F)) continue; + if (!handle_main && + (!F.getName().compare("main") || !F.getName().compare("_main"))) { + + continue; + + } + + if (isIgnoreFunction(&F)) { continue; } if (!isInInstrumentList(&F, MNAME) || !F.size()) { continue; } /* Some implementation notes. diff --git a/instrumentation/afl-llvm-lto-instrumentlist.so.cc b/instrumentation/afl-llvm-lto-instrumentlist.so.cc index 32b1798a..db5bd55e 100644 --- a/instrumentation/afl-llvm-lto-instrumentlist.so.cc +++ b/instrumentation/afl-llvm-lto-instrumentlist.so.cc @@ -9,7 +9,7 @@ from afl-as.c are Michal's fault. Copyright 2015, 2016 Google Inc. All rights reserved. - Copyright 2019-2022 AFLplusplus Project. All rights reserved. + Copyright 2019-2023 AFLplusplus Project. All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/instrumentation/afl-llvm-pass.so.cc b/instrumentation/afl-llvm-pass.so.cc index df1ccc4f..c59324fd 100644 --- a/instrumentation/afl-llvm-pass.so.cc +++ b/instrumentation/afl-llvm-pass.so.cc @@ -12,7 +12,7 @@ NGRAM previous location coverage comes from Adrian Herrera. Copyright 2015, 2016 Google Inc. All rights reserved. - Copyright 2019-2022 AFLplusplus Project. All rights reserved. + Copyright 2019-2023 AFLplusplus Project. All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -413,7 +413,7 @@ bool AFLCoverage::runOnModule(Module &M) { GlobalVariable *AFLContext = NULL; if (ctx_str || caller_str) -#if defined(__ANDROID__) || defined(__HAIKU__) +#if defined(__ANDROID__) || defined(__HAIKU__) || defined(NO_TLS) AFLContext = new GlobalVariable( M, Int32Ty, false, GlobalValue::ExternalLinkage, 0, "__afl_prev_ctx"); #else @@ -424,7 +424,7 @@ bool AFLCoverage::runOnModule(Module &M) { #ifdef AFL_HAVE_VECTOR_INTRINSICS if (ngram_size) - #if defined(__ANDROID__) || defined(__HAIKU__) + #if defined(__ANDROID__) || defined(__HAIKU__) || defined(NO_TLS) AFLPrevLoc = new GlobalVariable( M, PrevLocTy, /* isConstant */ false, GlobalValue::ExternalLinkage, /* Initializer */ nullptr, "__afl_prev_loc"); @@ -437,7 +437,7 @@ bool AFLCoverage::runOnModule(Module &M) { #endif else #endif -#if defined(__ANDROID__) || defined(__HAIKU__) +#if defined(__ANDROID__) || defined(__HAIKU__) || defined(NO_TLS) AFLPrevLoc = new GlobalVariable( M, Int32Ty, false, GlobalValue::ExternalLinkage, 0, "__afl_prev_loc"); #else @@ -448,7 +448,7 @@ bool AFLCoverage::runOnModule(Module &M) { #ifdef AFL_HAVE_VECTOR_INTRINSICS if (ctx_k) - #if defined(__ANDROID__) || defined(__HAIKU__) + #if defined(__ANDROID__) || defined(__HAIKU__) || defined(NO_TLS) AFLPrevCaller = new GlobalVariable( M, PrevCallerTy, /* isConstant */ false, GlobalValue::ExternalLinkage, /* Initializer */ nullptr, "__afl_prev_caller"); @@ -461,7 +461,7 @@ bool AFLCoverage::runOnModule(Module &M) { #endif else #endif -#if defined(__ANDROID__) || defined(__HAIKU__) +#if defined(__ANDROID__) || defined(__HAIKU__) || defined(NO_TLS) AFLPrevCaller = new GlobalVariable(M, Int32Ty, false, GlobalValue::ExternalLinkage, 0, "__afl_prev_caller"); diff --git a/instrumentation/cmplog-instructions-pass.cc b/instrumentation/cmplog-instructions-pass.cc index 084ad8c9..bca1f927 100644 --- a/instrumentation/cmplog-instructions-pass.cc +++ b/instrumentation/cmplog-instructions-pass.cc @@ -5,7 +5,7 @@ Written by Andrea Fioraldi <andreafioraldi@gmail.com> Copyright 2015, 2016 Google Inc. All rights reserved. - Copyright 2019-2022 AFLplusplus Project. All rights reserved. + Copyright 2019-2023 AFLplusplus Project. All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/instrumentation/cmplog-routines-pass.cc b/instrumentation/cmplog-routines-pass.cc index 9733f86e..0498156d 100644 --- a/instrumentation/cmplog-routines-pass.cc +++ b/instrumentation/cmplog-routines-pass.cc @@ -5,7 +5,7 @@ Written by Andrea Fioraldi <andreafioraldi@gmail.com> Copyright 2015, 2016 Google Inc. All rights reserved. - Copyright 2019-2022 AFLplusplus Project. All rights reserved. + Copyright 2019-2023 AFLplusplus Project. All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/instrumentation/cmplog-switches-pass.cc b/instrumentation/cmplog-switches-pass.cc index 563a4481..cd0ae76d 100644 --- a/instrumentation/cmplog-switches-pass.cc +++ b/instrumentation/cmplog-switches-pass.cc @@ -5,7 +5,7 @@ Written by Andrea Fioraldi <andreafioraldi@gmail.com> Copyright 2015, 2016 Google Inc. All rights reserved. - Copyright 2019-2022 AFLplusplus Project. All rights reserved. + Copyright 2019-2023 AFLplusplus Project. All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/instrumentation/compare-transform-pass.so.cc b/instrumentation/compare-transform-pass.so.cc index 39bff510..efc99d20 100644 --- a/instrumentation/compare-transform-pass.so.cc +++ b/instrumentation/compare-transform-pass.so.cc @@ -708,7 +708,11 @@ bool CompareTransform::transformCmps(Module &M, const bool processStrcmp, /* since the call is the first instruction of the bb it is safe to * replace it with a phi instruction */ BasicBlock::iterator ii(callInst); +#if LLVM_MAJOR >= 16 + ReplaceInstWithInst(callInst->getParent(), ii, PN); +#else ReplaceInstWithInst(callInst->getParent()->getInstList(), ii, PN); +#endif } diff --git a/instrumentation/split-compares-pass.so.cc b/instrumentation/split-compares-pass.so.cc index 95eca0cb..8a07610c 100644 --- a/instrumentation/split-compares-pass.so.cc +++ b/instrumentation/split-compares-pass.so.cc @@ -322,8 +322,12 @@ bool SplitComparesTransform::simplifyFPCompares(Module &M) { * block bb it is now at the position where the old FcmpInst was */ Instruction *fcmp_np; fcmp_np = CmpInst::Create(Instruction::FCmp, new_pred, op0, op1); +#if LLVM_MAJOR >= 16 + fcmp_np->insertInto(bb, BasicBlock::iterator(bb->getTerminator())); +#else bb->getInstList().insert(BasicBlock::iterator(bb->getTerminator()), fcmp_np); +#endif /* create a new basic block which holds the new EQ fcmp */ Instruction *fcmp_eq; @@ -331,7 +335,11 @@ bool SplitComparesTransform::simplifyFPCompares(Module &M) { BasicBlock *middle_bb = BasicBlock::Create(C, "injected", end_bb->getParent(), end_bb); fcmp_eq = CmpInst::Create(Instruction::FCmp, CmpInst::FCMP_OEQ, op0, op1); +#if LLVM_MAJOR >= 16 + fcmp_eq->insertInto(middle_bb, middle_bb->end()); +#else middle_bb->getInstList().push_back(fcmp_eq); +#endif /* add an unconditional branch to the end of middle_bb with destination * end_bb */ BranchInst::Create(end_bb, middle_bb); @@ -352,7 +360,11 @@ bool SplitComparesTransform::simplifyFPCompares(Module &M) { PN->addIncoming(ConstantInt::get(Int1Ty, 1), bb); /* replace the old FcmpInst with our new and shiny PHI inst */ BasicBlock::iterator ii(FcmpInst); +#if LLVM_MAJOR >= 16 + ReplaceInstWithInst(FcmpInst->getParent(), ii, PN); +#else ReplaceInstWithInst(FcmpInst->getParent()->getInstList(), ii, PN); +#endif } @@ -409,7 +421,11 @@ bool SplitComparesTransform::simplifyOrEqualsCompare(CmpInst *IcmpInst, /* create the ICMP instruction with new_pred and add it to the old basic * block bb it is now at the position where the old IcmpInst was */ CmpInst *icmp_np = CmpInst::Create(Instruction::ICmp, new_pred, op0, op1); +#if LLVM_MAJOR >= 16 + icmp_np->insertInto(bb, BasicBlock::iterator(bb->getTerminator())); +#else bb->getInstList().insert(BasicBlock::iterator(bb->getTerminator()), icmp_np); +#endif /* create a new basic block which holds the new EQ icmp */ CmpInst *icmp_eq; @@ -417,7 +433,11 @@ bool SplitComparesTransform::simplifyOrEqualsCompare(CmpInst *IcmpInst, BasicBlock *middle_bb = BasicBlock::Create(C, "injected", end_bb->getParent(), end_bb); icmp_eq = CmpInst::Create(Instruction::ICmp, CmpInst::ICMP_EQ, op0, op1); +#if LLVM_MAJOR >= 16 + icmp_eq->insertInto(middle_bb, middle_bb->end()); +#else middle_bb->getInstList().push_back(icmp_eq); +#endif /* add an unconditional branch to the end of middle_bb with destination * end_bb */ BranchInst::Create(end_bb, middle_bb); @@ -438,7 +458,11 @@ bool SplitComparesTransform::simplifyOrEqualsCompare(CmpInst *IcmpInst, PN->addIncoming(ConstantInt::get(Int1Ty, 1), bb); /* replace the old IcmpInst with our new and shiny PHI inst */ BasicBlock::iterator ii(IcmpInst); +#if LLVM_MAJOR >= 16 + ReplaceInstWithInst(IcmpInst->getParent(), ii, PN); +#else ReplaceInstWithInst(IcmpInst->getParent()->getInstList(), ii, PN); +#endif worklist.push_back(icmp_np); worklist.push_back(icmp_eq); @@ -518,7 +542,11 @@ bool SplitComparesTransform::simplifySignedCompare(CmpInst *IcmpInst, Module &M, } +#if LLVM_MAJOR >= 16 + icmp_inv_sig_cmp->insertInto(sign_bb, sign_bb->end()); +#else sign_bb->getInstList().push_back(icmp_inv_sig_cmp); +#endif BranchInst::Create(end_bb, sign_bb); /* create a new bb which is executed if signedness is equal */ @@ -528,7 +556,11 @@ bool SplitComparesTransform::simplifySignedCompare(CmpInst *IcmpInst, Module &M, /* we can do a normal unsigned compare now */ icmp_usign_cmp = CmpInst::Create(Instruction::ICmp, new_pred, op0, op1); +#if LLVM_MAJOR >= 16 + icmp_usign_cmp->insertInto(middle_bb, middle_bb->end()); +#else middle_bb->getInstList().push_back(icmp_usign_cmp); +#endif BranchInst::Create(end_bb, middle_bb); auto term = bb->getTerminator(); @@ -543,7 +575,11 @@ bool SplitComparesTransform::simplifySignedCompare(CmpInst *IcmpInst, Module &M, PN->addIncoming(icmp_inv_sig_cmp, sign_bb); BasicBlock::iterator ii(IcmpInst); +#if LLVM_MAJOR >= 16 + ReplaceInstWithInst(IcmpInst->getParent(), ii, PN); +#else ReplaceInstWithInst(IcmpInst->getParent()->getInstList(), ii, PN); +#endif // save for later worklist.push_back(icmp_usign_cmp); @@ -717,7 +753,11 @@ bool SplitComparesTransform::splitCompare(CmpInst *cmp_inst, Module &M, } +#if LLVM_MAJOR >= 16 + icmp_inv_cmp->insertInto(inv_cmp_bb, inv_cmp_bb->end()); +#else inv_cmp_bb->getInstList().push_back(icmp_inv_cmp); +#endif worklist.push_back(icmp_inv_cmp); auto term = bb->getTerminator(); @@ -728,12 +768,18 @@ bool SplitComparesTransform::splitCompare(CmpInst *cmp_inst, Module &M, BasicBlock *cmp_low_bb = BasicBlock::Create(C, "" /*"injected"*/, end_bb->getParent(), end_bb); op0_low = new TruncInst(op0, NewIntType); - cmp_low_bb->getInstList().push_back(op0_low); op1_low = new TruncInst(op1, NewIntType); - cmp_low_bb->getInstList().push_back(op1_low); - icmp_low = CmpInst::Create(Instruction::ICmp, pred, op0_low, op1_low); + +#if LLVM_MAJOR >= 16 + op0_low->insertInto(cmp_low_bb, cmp_low_bb->end()); + op1_low->insertInto(cmp_low_bb, cmp_low_bb->end()); + icmp_low->insertInto(cmp_low_bb, cmp_low_bb->end()); +#else + cmp_low_bb->getInstList().push_back(op0_low); + cmp_low_bb->getInstList().push_back(op1_low); cmp_low_bb->getInstList().push_back(icmp_low); +#endif BranchInst::Create(end_bb, cmp_low_bb); BranchInst::Create(end_bb, cmp_low_bb, icmp_inv_cmp, inv_cmp_bb); @@ -754,7 +800,11 @@ bool SplitComparesTransform::splitCompare(CmpInst *cmp_inst, Module &M, } BasicBlock::iterator ii(cmp_inst); +#if LLVM_MAJOR >= 16 + ReplaceInstWithInst(cmp_inst->getParent(), ii, PN); +#else ReplaceInstWithInst(cmp_inst->getParent()->getInstList(), ii, PN); +#endif // We split the comparison into low and high. If this isn't our target // bitwidth we recursively split the low and high parts again until we have @@ -999,13 +1049,21 @@ size_t SplitComparesTransform::splitFPCompares(Module &M) { Instruction *bpre_op0, *bpre_op1; bpre_op0 = CastInst::Create(Instruction::BitCast, op0, IntegerType::get(C, op_size)); +#if LLVM_MAJOR >= 16 + bpre_op0->insertInto(bb, BasicBlock::iterator(bb->getTerminator())); +#else bb->getInstList().insert(BasicBlock::iterator(bb->getTerminator()), bpre_op0); +#endif bpre_op1 = CastInst::Create(Instruction::BitCast, op1, IntegerType::get(C, op_size)); +#if LLVM_MAJOR >= 16 + bpre_op1->insertInto(bb, BasicBlock::iterator(bb->getTerminator())); +#else bb->getInstList().insert(BasicBlock::iterator(bb->getTerminator()), bpre_op1); +#endif /* Check if any operand is NaN. * If so, all comparisons except unequal (which yields true) yield false */ @@ -1025,34 +1083,42 @@ size_t SplitComparesTransform::splitFPCompares(Module &M) { Instruction *nan_op0, *nan_op1; nan_op0 = BinaryOperator::Create(Instruction::Shl, bpre_op0, ConstantInt::get(bpre_op0->getType(), 1)); - bb->getInstList().insert(BasicBlock::iterator(bb->getTerminator()), - nan_op0); - + /* Check op1 for NaN */ + /* Shift right 1 Bit, ignore sign bit */ + nan_op1 = BinaryOperator::Create(Instruction::Shl, bpre_op1, + ConstantInt::get(bpre_op1->getType(), 1)); /* compare to NaN interval */ Instruction *is_op0_nan = CmpInst::Create(Instruction::ICmp, CmpInst::ICMP_UGT, nan_op0, ConstantInt::get(intType, NaN_lowend)); + /* compare to NaN interval */ + Instruction *is_op1_nan = + CmpInst::Create(Instruction::ICmp, CmpInst::ICMP_UGT, nan_op1, + ConstantInt::get(intType, NaN_lowend)); + /* combine checks */ + Instruction *is_nan = + BinaryOperator::Create(Instruction::Or, is_op0_nan, is_op1_nan); +#if LLVM_MAJOR >= 16 + nan_op0->insertInto(bb, BasicBlock::iterator(bb->getTerminator())); + is_op0_nan->insertInto(bb, BasicBlock::iterator(bb->getTerminator())); + nan_op1->insertInto(bb, BasicBlock::iterator(bb->getTerminator())); + is_op1_nan->insertInto(bb, BasicBlock::iterator(bb->getTerminator())); + is_nan->insertInto(bb, BasicBlock::iterator(bb->getTerminator())); +#else + bb->getInstList().insert(BasicBlock::iterator(bb->getTerminator()), + nan_op0); + bb->getInstList().insert(BasicBlock::iterator(bb->getTerminator()), is_op0_nan); - /* Check op1 for NaN */ - /* Shift right 1 Bit, ignore sign bit */ - nan_op1 = BinaryOperator::Create(Instruction::Shl, bpre_op1, - ConstantInt::get(bpre_op1->getType(), 1)); bb->getInstList().insert(BasicBlock::iterator(bb->getTerminator()), nan_op1); - /* compare to NaN interval */ - Instruction *is_op1_nan = - CmpInst::Create(Instruction::ICmp, CmpInst::ICMP_UGT, nan_op1, - ConstantInt::get(intType, NaN_lowend)); bb->getInstList().insert(BasicBlock::iterator(bb->getTerminator()), is_op1_nan); - /* combine checks */ - Instruction *is_nan = - BinaryOperator::Create(Instruction::Or, is_op0_nan, is_op1_nan); bb->getInstList().insert(BasicBlock::iterator(bb->getTerminator()), is_nan); +#endif /* the result of the comparison, when at least one op is NaN is true only for the "NOT EQUAL" predicates. */ @@ -1079,23 +1145,34 @@ size_t SplitComparesTransform::splitFPCompares(Module &M) { isMzero_op0 = CmpInst::Create(Instruction::ICmp, CmpInst::ICMP_EQ, bpre_op0, ConstantInt::get(intType, MinusZero)); + isMzero_op1 = CmpInst::Create(Instruction::ICmp, CmpInst::ICMP_EQ, bpre_op1, + ConstantInt::get(intType, MinusZero)); + b_op0 = SelectInst::Create(isMzero_op0, ConstantInt::get(intType, PlusZero), + bpre_op0); + b_op1 = SelectInst::Create(isMzero_op1, ConstantInt::get(intType, PlusZero), + bpre_op1); +#if LLVM_MAJOR >= 16 + isMzero_op0->insertInto(nonan_bb, + BasicBlock::iterator(nonan_bb->getTerminator())); + isMzero_op1->insertInto(nonan_bb, + BasicBlock::iterator(nonan_bb->getTerminator())); + b_op0->insertInto(nonan_bb, + BasicBlock::iterator(nonan_bb->getTerminator())); + b_op1->insertInto(nonan_bb, + BasicBlock::iterator(nonan_bb->getTerminator())); +#else nonan_bb->getInstList().insert( BasicBlock::iterator(nonan_bb->getTerminator()), isMzero_op0); - isMzero_op1 = CmpInst::Create(Instruction::ICmp, CmpInst::ICMP_EQ, bpre_op1, - ConstantInt::get(intType, MinusZero)); nonan_bb->getInstList().insert( BasicBlock::iterator(nonan_bb->getTerminator()), isMzero_op1); - b_op0 = SelectInst::Create(isMzero_op0, ConstantInt::get(intType, PlusZero), - bpre_op0); nonan_bb->getInstList().insert( BasicBlock::iterator(nonan_bb->getTerminator()), b_op0); - b_op1 = SelectInst::Create(isMzero_op1, ConstantInt::get(intType, PlusZero), - bpre_op1); nonan_bb->getInstList().insert( BasicBlock::iterator(nonan_bb->getTerminator()), b_op1); +#endif /* isolate signs of value of floating point type */ @@ -1106,26 +1183,35 @@ size_t SplitComparesTransform::splitFPCompares(Module &M) { s_s0 = BinaryOperator::Create(Instruction::LShr, b_op0, ConstantInt::get(b_op0->getType(), op_size - 1)); + s_s1 = + BinaryOperator::Create(Instruction::LShr, b_op1, + ConstantInt::get(b_op1->getType(), op_size - 1)); + t_s0 = new TruncInst(s_s0, Int1Ty); + t_s1 = new TruncInst(s_s1, Int1Ty); + /* compare of the sign bits */ + icmp_sign_bit = + CmpInst::Create(Instruction::ICmp, CmpInst::ICMP_EQ, t_s0, t_s1); +#if LLVM_MAJOR >= 16 + s_s0->insertInto(nonan_bb, BasicBlock::iterator(nonan_bb->getTerminator())); + t_s0->insertInto(nonan_bb, BasicBlock::iterator(nonan_bb->getTerminator())); + s_s1->insertInto(nonan_bb, BasicBlock::iterator(nonan_bb->getTerminator())); + t_s1->insertInto(nonan_bb, BasicBlock::iterator(nonan_bb->getTerminator())); + icmp_sign_bit->insertInto(nonan_bb, + BasicBlock::iterator(nonan_bb->getTerminator())); +#else nonan_bb->getInstList().insert( BasicBlock::iterator(nonan_bb->getTerminator()), s_s0); - t_s0 = new TruncInst(s_s0, Int1Ty); nonan_bb->getInstList().insert( BasicBlock::iterator(nonan_bb->getTerminator()), t_s0); - s_s1 = - BinaryOperator::Create(Instruction::LShr, b_op1, - ConstantInt::get(b_op1->getType(), op_size - 1)); nonan_bb->getInstList().insert( BasicBlock::iterator(nonan_bb->getTerminator()), s_s1); - t_s1 = new TruncInst(s_s1, Int1Ty); nonan_bb->getInstList().insert( BasicBlock::iterator(nonan_bb->getTerminator()), t_s1); - /* compare of the sign bits */ - icmp_sign_bit = - CmpInst::Create(Instruction::ICmp, CmpInst::ICMP_EQ, t_s0, t_s1); nonan_bb->getInstList().insert( BasicBlock::iterator(nonan_bb->getTerminator()), icmp_sign_bit); +#endif /* create a new basic block which is executed if the signedness bits are * equal */ @@ -1157,17 +1243,31 @@ size_t SplitComparesTransform::splitFPCompares(Module &M) { s_e1 = BinaryOperator::Create( Instruction::LShr, b_op1, ConstantInt::get(b_op1->getType(), shiftR_exponent)); +#if LLVM_MAJOR >= 16 + s_e0->insertInto(signequal_bb, + BasicBlock::iterator(signequal_bb->getTerminator())); + s_e1->insertInto(signequal_bb, + BasicBlock::iterator(signequal_bb->getTerminator())); +#else signequal_bb->getInstList().insert( BasicBlock::iterator(signequal_bb->getTerminator()), s_e0); signequal_bb->getInstList().insert( BasicBlock::iterator(signequal_bb->getTerminator()), s_e1); +#endif t_e0 = new TruncInst(s_e0, IntExponentTy); t_e1 = new TruncInst(s_e1, IntExponentTy); +#if LLVM_MAJOR >= 16 + t_e0->insertInto(signequal_bb, + BasicBlock::iterator(signequal_bb->getTerminator())); + t_e1->insertInto(signequal_bb, + BasicBlock::iterator(signequal_bb->getTerminator())); +#else signequal_bb->getInstList().insert( BasicBlock::iterator(signequal_bb->getTerminator()), t_e0); signequal_bb->getInstList().insert( BasicBlock::iterator(signequal_bb->getTerminator()), t_e1); +#endif if (sizeInBits - precision < exTySizeBytes * 8) { @@ -1177,10 +1277,17 @@ size_t SplitComparesTransform::splitFPCompares(Module &M) { m_e1 = BinaryOperator::Create( Instruction::And, t_e1, ConstantInt::get(t_e1->getType(), mask_exponent)); +#if LLVM_MAJOR >= 16 + m_e0->insertInto(signequal_bb, + BasicBlock::iterator(signequal_bb->getTerminator())); + m_e1->insertInto(signequal_bb, + BasicBlock::iterator(signequal_bb->getTerminator())); +#else signequal_bb->getInstList().insert( BasicBlock::iterator(signequal_bb->getTerminator()), m_e0); signequal_bb->getInstList().insert( BasicBlock::iterator(signequal_bb->getTerminator()), m_e1); +#endif } else { @@ -1214,9 +1321,14 @@ size_t SplitComparesTransform::splitFPCompares(Module &M) { Instruction *icmp_exponent; icmp_exponents_equal = CmpInst::Create(Instruction::ICmp, CmpInst::ICMP_EQ, m_e0, m_e1); +#if LLVM_MAJOR >= 16 + icmp_exponents_equal->insertInto( + signequal_bb, BasicBlock::iterator(signequal_bb->getTerminator())); +#else signequal_bb->getInstList().insert( BasicBlock::iterator(signequal_bb->getTerminator()), icmp_exponents_equal); +#endif // shortcut for unequal exponents signequal2_bb = signequal_bb->splitBasicBlock( @@ -1230,9 +1342,15 @@ size_t SplitComparesTransform::splitFPCompares(Module &M) { icmp_exponent = CmpInst::Create(Instruction::ICmp, CmpInst::ICMP_UGT, m_e0, m_e1); +#if LLVM_MAJOR >= 16 + icmp_exponent->insertInto( + signequal2_bb, + BasicBlock::iterator(signequal2_bb->getTerminator())); +#else signequal2_bb->getInstList().insert( BasicBlock::iterator(signequal2_bb->getTerminator()), icmp_exponent); +#endif icmp_exponent_result = BinaryOperator::Create(Instruction::Xor, icmp_exponent, t_s0); break; @@ -1240,9 +1358,14 @@ size_t SplitComparesTransform::splitFPCompares(Module &M) { case CmpInst::FCMP_ULT: icmp_exponents_equal = CmpInst::Create(Instruction::ICmp, CmpInst::ICMP_EQ, m_e0, m_e1); +#if LLVM_MAJOR >= 16 + icmp_exponents_equal->insertInto( + signequal_bb, BasicBlock::iterator(signequal_bb->getTerminator())); +#else signequal_bb->getInstList().insert( BasicBlock::iterator(signequal_bb->getTerminator()), icmp_exponents_equal); +#endif // shortcut for unequal exponents signequal2_bb = signequal_bb->splitBasicBlock( @@ -1256,9 +1379,15 @@ size_t SplitComparesTransform::splitFPCompares(Module &M) { icmp_exponent = CmpInst::Create(Instruction::ICmp, CmpInst::ICMP_ULT, m_e0, m_e1); +#if LLVM_MAJOR >= 16 + icmp_exponent->insertInto( + signequal2_bb, + BasicBlock::iterator(signequal2_bb->getTerminator())); +#else signequal2_bb->getInstList().insert( BasicBlock::iterator(signequal2_bb->getTerminator()), icmp_exponent); +#endif icmp_exponent_result = BinaryOperator::Create(Instruction::Xor, icmp_exponent, t_s0); break; @@ -1267,9 +1396,14 @@ size_t SplitComparesTransform::splitFPCompares(Module &M) { } +#if LLVM_MAJOR >= 16 + icmp_exponent_result->insertInto( + signequal2_bb, BasicBlock::iterator(signequal2_bb->getTerminator())); +#else signequal2_bb->getInstList().insert( BasicBlock::iterator(signequal2_bb->getTerminator()), icmp_exponent_result); +#endif { @@ -1319,19 +1453,33 @@ size_t SplitComparesTransform::splitFPCompares(Module &M) { m_f1 = BinaryOperator::Create( Instruction::And, b_op1, ConstantInt::get(b_op1->getType(), mask_fraction)); +#if LLVM_MAJOR >= 16 + m_f0->insertInto(middle_bb, + BasicBlock::iterator(middle_bb->getTerminator())); + m_f1->insertInto(middle_bb, + BasicBlock::iterator(middle_bb->getTerminator())); +#else middle_bb->getInstList().insert( BasicBlock::iterator(middle_bb->getTerminator()), m_f0); middle_bb->getInstList().insert( BasicBlock::iterator(middle_bb->getTerminator()), m_f1); +#endif if (needTrunc) { t_f0 = new TruncInst(m_f0, IntFractionTy); t_f1 = new TruncInst(m_f1, IntFractionTy); +#if LLVM_MAJOR >= 16 + t_f0->insertInto(middle_bb, + BasicBlock::iterator(middle_bb->getTerminator())); + t_f1->insertInto(middle_bb, + BasicBlock::iterator(middle_bb->getTerminator())); +#else middle_bb->getInstList().insert( BasicBlock::iterator(middle_bb->getTerminator()), t_f0); middle_bb->getInstList().insert( BasicBlock::iterator(middle_bb->getTerminator()), t_f1); +#endif } else { @@ -1346,10 +1494,17 @@ size_t SplitComparesTransform::splitFPCompares(Module &M) { t_f0 = new TruncInst(b_op0, IntFractionTy); t_f1 = new TruncInst(b_op1, IntFractionTy); +#if LLVM_MAJOR >= 16 + t_f0->insertInto(middle_bb, + BasicBlock::iterator(middle_bb->getTerminator())); + t_f1->insertInto(middle_bb, + BasicBlock::iterator(middle_bb->getTerminator())); +#else middle_bb->getInstList().insert( BasicBlock::iterator(middle_bb->getTerminator()), t_f0); middle_bb->getInstList().insert( BasicBlock::iterator(middle_bb->getTerminator()), t_f1); +#endif } else { @@ -1370,18 +1525,28 @@ size_t SplitComparesTransform::splitFPCompares(Module &M) { case CmpInst::FCMP_OEQ: icmp_fraction_result = CmpInst::Create(Instruction::ICmp, CmpInst::ICMP_EQ, t_f0, t_f1); +#if LLVM_MAJOR >= 16 + icmp_fraction_result->insertInto( + middle2_bb, BasicBlock::iterator(middle2_bb->getTerminator())); +#else middle2_bb->getInstList().insert( BasicBlock::iterator(middle2_bb->getTerminator()), icmp_fraction_result); +#endif break; case CmpInst::FCMP_UNE: case CmpInst::FCMP_ONE: icmp_fraction_result = CmpInst::Create(Instruction::ICmp, CmpInst::ICMP_NE, t_f0, t_f1); +#if LLVM_MAJOR >= 16 + icmp_fraction_result->insertInto( + middle2_bb, BasicBlock::iterator(middle2_bb->getTerminator())); +#else middle2_bb->getInstList().insert( BasicBlock::iterator(middle2_bb->getTerminator()), icmp_fraction_result); +#endif break; case CmpInst::FCMP_OGT: @@ -1402,21 +1567,31 @@ size_t SplitComparesTransform::splitFPCompares(Module &M) { if (FcmpInst->getPredicate() == CmpInst::FCMP_OGT || FcmpInst->getPredicate() == CmpInst::FCMP_UGT) { - negative_bb->getInstList().push_back( - icmp_fraction_result = CmpInst::Create( - Instruction::ICmp, CmpInst::ICMP_ULT, t_f0, t_f1)); - positive_bb->getInstList().push_back( - icmp_fraction_result2 = CmpInst::Create( - Instruction::ICmp, CmpInst::ICMP_UGT, t_f0, t_f1)); + icmp_fraction_result = + CmpInst::Create(Instruction::ICmp, CmpInst::ICMP_ULT, t_f0, t_f1); + icmp_fraction_result2 = + CmpInst::Create(Instruction::ICmp, CmpInst::ICMP_UGT, t_f0, t_f1); +#if LLVM_MAJOR >= 16 + icmp_fraction_result->insertInto(negative_bb, negative_bb->end()); + icmp_fraction_result2->insertInto(positive_bb, negative_bb->end()); +#else + negative_bb->getInstList().push_back(icmp_fraction_result); + positive_bb->getInstList().push_back(icmp_fraction_result2); +#endif } else { - negative_bb->getInstList().push_back( - icmp_fraction_result = CmpInst::Create( - Instruction::ICmp, CmpInst::ICMP_UGT, t_f0, t_f1)); - positive_bb->getInstList().push_back( - icmp_fraction_result2 = CmpInst::Create( - Instruction::ICmp, CmpInst::ICMP_ULT, t_f0, t_f1)); + icmp_fraction_result = + CmpInst::Create(Instruction::ICmp, CmpInst::ICMP_UGT, t_f0, t_f1); + icmp_fraction_result2 = + CmpInst::Create(Instruction::ICmp, CmpInst::ICMP_ULT, t_f0, t_f1); +#if LLVM_MAJOR >= 16 + icmp_fraction_result->insertInto(negative_bb, negative_bb->end()); + icmp_fraction_result2->insertInto(positive_bb, negative_bb->end()); +#else + negative_bb->getInstList().push_back(icmp_fraction_result); + positive_bb->getInstList().push_back(icmp_fraction_result2); +#endif } @@ -1430,8 +1605,13 @@ size_t SplitComparesTransform::splitFPCompares(Module &M) { PN2 = PHINode::Create(Int1Ty, 2, ""); PN2->addIncoming(icmp_fraction_result, negative_bb); PN2->addIncoming(icmp_fraction_result2, positive_bb); +#if LLVM_MAJOR >= 16 + PN2->insertInto(middle2_bb, + BasicBlock::iterator(middle2_bb->getTerminator())); +#else middle2_bb->getInstList().insert( BasicBlock::iterator(middle2_bb->getTerminator()), PN2); +#endif } break; @@ -1494,7 +1674,11 @@ size_t SplitComparesTransform::splitFPCompares(Module &M) { } BasicBlock::iterator ii(FcmpInst); +#if LLVM_MAJOR >= 16 + ReplaceInstWithInst(FcmpInst->getParent(), ii, PN); +#else ReplaceInstWithInst(FcmpInst->getParent()->getInstList(), ii, PN); +#endif ++count; } diff --git a/instrumentation/split-switches-pass.so.cc b/instrumentation/split-switches-pass.so.cc index 79ba12d2..dcd89652 100644 --- a/instrumentation/split-switches-pass.so.cc +++ b/instrumentation/split-switches-pass.so.cc @@ -225,12 +225,20 @@ BasicBlock *SplitSwitchesTransform::switchConvert( BasicBlock *NewNode = BasicBlock::Create(Val->getContext(), "NodeBlock", F); Shift = BinaryOperator::Create(Instruction::LShr, Val, ConstantInt::get(ValType, smallestIndex * 8)); +#if LLVM_VERSION_MAJOR >= 16 + Shift->insertInto(NewNode, NewNode->end()); +#else NewNode->getInstList().push_back(Shift); +#endif if (ValTypeBitWidth > 8) { Trunc = new TruncInst(Shift, ByteType); +#if LLVM_VERSION_MAJOR >= 16 + Trunc->insertInto(NewNode, NewNode->end()); +#else NewNode->getInstList().push_back(Trunc); +#endif } else { @@ -253,7 +261,11 @@ BasicBlock *SplitSwitchesTransform::switchConvert( ICmpInst *Comp = new ICmpInst(ICmpInst::ICMP_EQ, Trunc, ConstantInt::get(ByteType, byte), "byteMatch"); +#if LLVM_VERSION_MAJOR >= 16 + Comp->insertInto(NewNode, NewNode->end()); +#else NewNode->getInstList().push_back(Comp); +#endif bytesChecked[smallestIndex] = true; bool allBytesAreChecked = true; @@ -355,7 +367,11 @@ BasicBlock *SplitSwitchesTransform::switchConvert( ICmpInst *Comp = new ICmpInst(ICmpInst::ICMP_ULT, Trunc, ConstantInt::get(ByteType, pivot), "byteMatch"); +#if LLVM_VERSION_MAJOR >= 16 + Comp->insertInto(NewNode, NewNode->end()); +#else NewNode->getInstList().push_back(Comp); +#endif BranchInst::Create(LBB, RBB, Comp, NewNode); } @@ -452,7 +468,11 @@ bool SplitSwitchesTransform::splitSwitches(Module &M) { BranchInst::Create(SwitchBlock, OrigBlock); /* We are now done with the switch instruction, delete it. */ +#if LLVM_VERSION_MAJOR >= 16 + SI->eraseFromParent(); +#else CurBlock->getInstList().erase(SI); +#endif /* we have to update the phi nodes! */ for (BasicBlock::iterator I = Default->begin(); I != Default->end(); ++I) { diff --git a/nyx_mode/LIBNYX_VERSION b/nyx_mode/LIBNYX_VERSION index 00165a63..86b32eec 100644 --- a/nyx_mode/LIBNYX_VERSION +++ b/nyx_mode/LIBNYX_VERSION @@ -1 +1 @@ -acaf7f6 +2da7f08 diff --git a/nyx_mode/PACKER_VERSION b/nyx_mode/PACKER_VERSION index a8ebe13a..7db88233 100644 --- a/nyx_mode/PACKER_VERSION +++ b/nyx_mode/PACKER_VERSION @@ -1 +1 @@ -86b159b +202bace diff --git a/nyx_mode/QEMU-Nyx b/nyx_mode/QEMU-Nyx -Subproject 5c8cf793ec615b0df5fa722878c8f6906ad7936 +Subproject 60c216bc9e4c79834716d4099993d8397a3a8fd diff --git a/nyx_mode/QEMU_NYX_VERSION b/nyx_mode/QEMU_NYX_VERSION index f5888136..98cb134f 100644 --- a/nyx_mode/QEMU_NYX_VERSION +++ b/nyx_mode/QEMU_NYX_VERSION @@ -1 +1 @@ -5c8cf793ec +60c216bc9e diff --git a/nyx_mode/README.md b/nyx_mode/README.md index 1afedd9b..eee7d363 100644 --- a/nyx_mode/README.md +++ b/nyx_mode/README.md @@ -15,6 +15,7 @@ Underneath it is built upon KVM and QEMU and requires a modern Linux kernel requires an Intel processor (6th generation onwards) and a special 5.10 kernel (see [KVM-Nyx](https://github.com/nyx-fuzz/KVM-Nyx)). + ## Building Nyx mode 1. Install all the packages from [docs/INSTALL.md](../docs/INSTALL.md). @@ -41,6 +42,7 @@ requires an Intel processor (6th generation onwards) and a special 5.10 kernel 5. Optionally, for binary-only fuzzing: set up the required 5.10 kernel, see [KVM-Nyx](https://github.com/nyx-fuzz/KVM-Nyx). + ## Preparing to fuzz a target with Nyx mode For source instrumented fuzzing you can use any afl-cc mode, with LTO even @@ -68,12 +70,21 @@ This will create a directory with all necessary files and the Nyx configuration. The name of the directory will be whatever you choose for `PACKAGE-DIRECTORY` above. -In the final step for the packaging we generate the Nyx configuration: +Note that if the target reads from a file then use the `-file /path/to/file` +parameter to the above command. + +Note that Nyx does **not** support the afl `@@` argument. Instead pass +something like `-file /foo.file -args "--file /foo.file --other-args"` to +the above command. + + +Then the final step: we generate the Nyx package configuration: ```shell python3 nyx_mode/packer/packer/nyx_config_gen.py PACKAGE-DIRECTORY Kernel ``` + ## Fuzzing with Nyx mode All the hard parts are done, fuzzing with Nyx mode is easy - just supply the @@ -97,7 +108,7 @@ sudo modprobe kvm-intel # or kvm-amd for AMD processors If you want to fuzz in parallel (and you should!), then this has to be done in a special way: -* Instead of `-X` (standalone mode), you specify `-Y` (multi processor mode). +* Instead of `-X` (standalone mode), you specify `-Y` (multiprocessor mode). * First, a Main afl-fuzz instance has to be started with `-M 0`. * Only afterwards you can start Secondary afl-fuzz instances, which must have an increasing number value, starting at 1, e.g., `-S 1`. @@ -114,13 +125,39 @@ afl-fuzz -i in -o out -Y -S 1 -- ./PACKAGE-DIRECTORY afl-fuzz -i in -o out -Y -S 2 -- ./PACKAGE-DIRECTORY ``` + ## AFL++ companion tools (afl-showmap etc.) -Please note that AFL++ companion tools like afl-cmin, afl-showmap, etc. are -not supported with Nyx mode, only afl-fuzz. +AFL++ companion tools support Nyx mode and can be used to analyze or minimize one specific input or an entire output corpus. These tools work similarly to `afl-fuzz`. + +To run a target with one of these tools, add the `-X` parameter to the command line to enable Nyx mode, and pass the path to a Nyx package directory: + +```shell +afl-tmin -i in_file -o out_file -X -- ./PACKAGE-DIRECTORY +``` + +```shell +afl-analyze -i in_file -X -- ./PACKAGE-DIRECTORY +``` + +```shell +afl-showmap -i in_dir -o out_file -X -- ./PACKAGE-DIRECTORY +``` + +```shell +afl-cmin -i in_dir -o out_dir -X -- ./PACKAGE-DIRECTORY +``` + +On each program startup of one the AFL++ tools in Nyx mode, a Nyx VM is spawned, and a bootstrapping procedure is performed inside the VM to prepare the target environment. As a consequence, due to the bootstrapping procedure, the launch performance is much slower compared to other modes. However, this can be optimized by reusing an existing fuzzing snapshot to avoid the slow re-execution of the bootstrap procedure. + +A fuzzing snapshot is automatically created and stored in the output directory at `out_dir/workdir/snapshot/` by the first parent process of `afl-fuzz` if parallel mode is used. To enable this feature, set the path to an existing snapshot directory in the `NYX_REUSE_SNAPSHOT` environment variable and use the tools as usual: + +```shell +afl-fuzz -i ./in_dir -o ./out_dir -Y -M 0 ./PACKAGE-DIRECTORY + +NYX_REUSE_SNAPSHOT=./out_dir/workdir/snapshot/ afl-analyze -i in_file -X -- ./PACKAGE-DIRECTORY +``` -For source based instrumentation just use these tools normally, for -binary-only targets use with -Q for qemu_mode. ## Real-world examples diff --git a/nyx_mode/build_nyx_support.sh b/nyx_mode/build_nyx_support.sh index e7fca64f..581a8292 100755 --- a/nyx_mode/build_nyx_support.sh +++ b/nyx_mode/build_nyx_support.sh @@ -60,11 +60,6 @@ fi echo "[*] Checking QEMU-Nyx ..." if [ ! -f "QEMU-Nyx/x86_64-softmmu/qemu-system-x86_64" ]; then - - if ! dpkg -s gtk3-devel > /dev/null 2>&1; then - echo "[-] Disabling GTK because gtk3-devel is not installed." - sed -i 's/--enable-gtk//g' QEMU-Nyx/compile_qemu_nyx.sh - fi (cd QEMU-Nyx && ./compile_qemu_nyx.sh static) fi diff --git a/nyx_mode/libnyx b/nyx_mode/libnyx -Subproject acaf7f6346eeb5f1e2cf043543316909fca4365 +Subproject 2da7f08b6e0267ccfe64e1320b24cdb29223459 diff --git a/nyx_mode/packer b/nyx_mode/packer -Subproject 86b159bafc0b2ba8feeaa8761a45b6201d34084 +Subproject 202bace888d237e4e8f4507d0eba6791a811554 diff --git a/qemu_mode/QEMUAFL_VERSION b/qemu_mode/QEMUAFL_VERSION index d59a04e7..fa44d173 100644 --- a/qemu_mode/QEMUAFL_VERSION +++ b/qemu_mode/QEMUAFL_VERSION @@ -1 +1 @@ -12682ea816 +0569eff8a1 diff --git a/qemu_mode/README.deferred_initialization_example.md b/qemu_mode/README.deferred_initialization_example.md new file mode 100644 index 00000000..d940d6b5 --- /dev/null +++ b/qemu_mode/README.deferred_initialization_example.md @@ -0,0 +1,201 @@ +# Fuzz ARM32 Python Native Extensions in Binary-only Mode (LLVM fork-based) + +This is an example on how to fuzz Python native extensions in LLVM mode with deferred initialization on ARM32. + +We use Ubuntu x86_64 to run AFL++ and an Alpine ARMv7 Chroot to build the fuzzing target. + +Check [Resources](#resources) for the code used in this example. + +## Setup Alpine ARM Chroot on your x86_64 Linux Host + +### Use systemd-nspawn + +1. Install `qemu-user-binfmt`, `qemu-user-static` and `systemd-container` dependencies. +2. Restart the systemd-binfmt service: `systemctl restart systemd-binfmt.service` +3. Download an Alpine ARM RootFS from https://alpinelinux.org/downloads/ +4. Create a new `alpine_sysroot` folder and extract: `tar xfz alpine-minirootfs-3.17.1-armv7.tar.gz -C alpine_sysroot/` +5. Copy `qemu-arm-static` to Alpine's RootFS: `cp $(which qemu-arm-static) ./alpine/usr/bin/` +6. Chroot into the container: `sudo systemd-nspawn -D alpine/ --bind-ro=/etc/resolv.conf` +7. Install dependencies: `apk update && apk add build-base musl-dev clang15 python3 python3-dev py3-pip` +8. Exit the container with `exit` + +### Alternatively use Docker + +1. Install `qemu-user-binfmt` and `qemu-user-static` +2. Run Qemu container: ```$ docker run --rm --privileged multiarch/qemu-user-static --reset -p yes``` +3. Run Alpine container: ```$ docker run -it --rm arm32v7/alpine sh``` + +## Build AFL++ Qemu Mode with ARM Support + +First, build AFL++ as described [here](https://github.com/AFLplusplus/AFLplusplus/blob/dev/docs/INSTALL.md). Then, run the Qemu build script: + +```bash +cd qemu_mode && CPU_TARGET=arm ./build_qemu_support.sh +``` + +## Compile and Build the Fuzzing Project +Build the native extension and the fuzzing harness for ARM using the Alpine container (check [Resources](#resources) for the code): +```bash +ALPINE_ROOT=<your-alpine-sysroot-directory> +FUZZ=<your-path-to-the-code> +sudo systemd-nspawn -D $ALPINE_ROOT --bind=$FUZZ:/fuzz +CC=$(which clang) CFLAGS="-g" LDSHARED="clang -shared" python3 -m pip install /fuzz +clang $(python3-config --embed --cflags) $(python3-config --embed --ldflags) -o /fuzz/fuzz_harness /fuzz/fuzz_harness.c +exit +``` + +Manually trigger bug: +```bash +echo -n "FUZZ" | qemu-arm-static -L $ALPINE_ROOT $FUZZ/fuzz_harness +``` + +## Run AFL++ +Make sure to start the forkserver *after* loading all the shared objects by setting the `AFL_ENTRYPOINT` environment variable (see [here](https://aflplus.plus/docs/env_variables/#5-settings-for-afl-qemu-trace) for details): + +Choose an address just before the `while()` loop, for example: +```bash +qemu-arm-static -L $ALPINE_ROOT $ALPINE_ROOT/usr/bin/objdump -d $FUZZ/fuzz_harness | grep -A 1 "PyObject_GetAttrString" + +00000584 <PyObject_GetAttrString@plt>: + 584: e28fc600 add ip, pc, #0, 12 +-- + 7c8: ebffff6d bl 584 <PyObject_GetAttrString@plt> + 7cc: e58d0008 str r0, [sp, #8] +... +``` + +Check Qemu memory maps using the instructions from [here](https://aflplus.plus/docs/tutorials/libxml2_tutorial/): +>The binary is position independent and QEMU persistent needs the real addresses, not the offsets. Fortunately, QEMU loads PIE executables at a fixed address, 0x4000000000 for x86_64. +> +> We can check it using `AFL_QEMU_DEBUG_MAPS`. You don’t need this step if your binary is not PIE. + +Setup Python environment variables and run `afl-qemu-trace`: +```bash +PYTHONPATH=$ALPINE_ROOT/usr/lib/python3.10/ PYTHONHOME=$ALPINE_ROOT/usr/bin/ QEMU_LD_PREFIX=$ALPINE_ROOT AFL_QEMU_DEBUG_MAPS=1 afl-qemu-trace $FUZZ/fuzz_harness + +... +40000000-40001000 r-xp 00000000 103:03 8002276 fuzz_harness +40001000-4001f000 ---p 00000000 00:00 0 +4001f000-40020000 r--p 0000f000 103:03 8002276 fuzz_harness +40020000-40021000 rw-p 00010000 103:03 8002276 fuzz_harness +40021000-40022000 ---p 00000000 00:00 0 +40022000-40023000 rw-p 00000000 00:00 0 +``` + +Finally, setup Qemu environment variables... +```bash +export QEMU_SET_ENV=PYTHONPATH=$ALPINE_ROOT/usr/lib/python310.zip:$ALPINE_ROOT/usr/lib/python3.10:$ALPINE_ROOT/usr/lib/python3.10/lib-dynload:$ALPINE_ROOT/usr/lib/python3.10/site-packages,PYTHONHOME=$ALPINE_ROOT/usr/bin/ +export QEMU_LD_PREFIX=$ALPINE_ROOT +``` + +... and run AFL++: +```bash +mkdir -p $FUZZ/in && echo -n "FU" > $FUZZ/in/seed +AFL_ENTRYPOINT=0x400007cc afl-fuzz -i $FUZZ/in -o $FUZZ/out -Q -- $FUZZ/fuzz_harness +``` + +## Resources + +### setup.py + +```python +from distutils.core import setup, Extension + +module = Extension("memory", sources=["fuzz_target.c"]) + +setup( + name="memory", + version="1.0", + description='A simple "BOOM!" extension', + ext_modules=[module], +) +``` + +### fuzz_target.c + +```c +#define PY_SSIZE_T_CLEAN +#include <Python.h> + +#pragma clang optimize off + +static PyObject *corruption(PyObject* self, PyObject* args) { + char arr[3]; + Py_buffer name; + + if (!PyArg_ParseTuple(args, "y*", &name)) + return NULL; + + if (name.buf != NULL) { + if (strcmp(name.buf, "FUZZ") == 0) { + arr[0] = 'B'; + arr[1] = 'O'; + arr[2] = 'O'; + arr[3] = 'M'; + } + } + + PyBuffer_Release(&name); + Py_RETURN_NONE; +} + +static PyMethodDef MemoryMethods[] = { + {"corruption", corruption, METH_VARARGS, "BOOM!"}, + {NULL, NULL, 0, NULL} +}; + +static struct PyModuleDef memory_module = { + PyModuleDef_HEAD_INIT, + "memory", + "BOOM!", + -1, + MemoryMethods +}; + +PyMODINIT_FUNC PyInit_memory(void) { + return PyModule_Create(&memory_module); +} +``` + +### fuzz_harness.c + +```c +#include <Python.h> + +#pragma clang optimize off + +int main(int argc, char **argv) { + unsigned char buf[1024000]; + ssize_t size; + + Py_Initialize(); + PyObject* name = PyUnicode_DecodeFSDefault("memory"); + PyObject* module = PyImport_Import(name); + Py_DECREF(name); + + if (module != NULL) { + PyObject* corruption_func = PyObject_GetAttrString(module, "corruption"); + + while ((size = read(0, buf, sizeof(buf))) > 0 ? 1 : 0) { + PyObject* arg = PyBytes_FromStringAndSize((char *)buf, size); + + if (arg != NULL) { + PyObject* res = PyObject_CallFunctionObjArgs(corruption_func, arg, NULL); + + if (res != NULL) { + Py_XDECREF(res); + } + + Py_DECREF(arg); + } + } + + Py_DECREF(corruption_func); + Py_DECREF(module); + } + + // Py_Finalize() leaks memory on certain Python versions (see https://bugs.python.org/issue1635741) + // Py_Finalize(); + return 0; +} +``` diff --git a/qemu_mode/README.md b/qemu_mode/README.md index 3ebfc54c..92038737 100644 --- a/qemu_mode/README.md +++ b/qemu_mode/README.md @@ -13,8 +13,8 @@ afl-cc. The usual performance cost is 2-5x, which is considerably better than seen so far in experiments with tools such as DynamoRIO and PIN. -The idea and much of the initial implementation comes from Andrew Griffiths. The -actual implementation on current QEMU (shipped as qemuafl) is from Andrea +The idea and much of the initial implementation comes from Andrew Griffiths. +The actual implementation on current QEMU (shipped as qemuafl) is from Andrea Fioraldi. Special thanks to abiondo that re-enabled TCG chaining. ## 2) How to use QEMU mode @@ -30,17 +30,13 @@ glib2-devel). Once the binaries are compiled, you can leverage the QEMU tool by calling afl-fuzz and all the related utilities with `-Q` in the command line. -Note that QEMU requires a generous memory limit to run; somewhere around 200 MB -is a good starting point, but considerably more may be needed for more complex -programs. The default `-m` limit will be automatically bumped up to 200 MB when -specifying `-Q` to afl-fuzz; be careful when overriding this. - In principle, if you set `CPU_TARGET` before calling ./build_qemu_support.sh, you should get a build capable of running non-native binaries (say, you can try `CPU_TARGET=arm`). This is also necessary for running 32-bit binaries on a 64-bit system (`CPU_TARGET=i386`). If you're trying to run QEMU on a different architecture, you can also set `HOST` to the cross-compiler prefix to use (for example `HOST=arm-linux-gnueabi` to use arm-linux-gnueabi-gcc). +Another common target is `CPU_TARGET=aarch64`. You can also compile statically-linked binaries by setting `STATIC=1`. This can be useful when compiling QEMU on a different system than the one you're planning @@ -70,6 +66,8 @@ allows to move the forkserver to a different part, e.g., just before the file is opened (e.g., way after command line parsing and config file loading, etc.) which can be a huge speed improvement. +For an example, see [README.deferred_initialization_example.md](README.deferred_initialization_example.md). + ## 4) Persistent mode AFL++'s QEMU mode now supports also persistent mode for x86, x86_64, arm, and @@ -219,9 +217,6 @@ program may be utilizing. In particular, it does not appear to have full support for AVX2/FMA3. Using binaries for older CPUs or recompiling them with `-march=core2`, can help. -Beyond that, this is an early-stage mechanism, so fields reports are welcome. -You can send them to <afl-users@googlegroups.com>. - ## 14) Alternatives: static rewriting Statically rewriting binaries just once, instead of attempting to translate them @@ -230,4 +225,4 @@ with peril, because it depends on being able to properly and fully model program control flow without actually executing each and every code path. For more information and hints, check out -[docs/fuzzing_binary-only_targets.md](../docs/fuzzing_binary-only_targets.md). \ No newline at end of file +[docs/fuzzing_binary-only_targets.md](../docs/fuzzing_binary-only_targets.md). diff --git a/qemu_mode/README.persistent.md b/qemu_mode/README.persistent.md index ab45860d..ef8fb71b 100644 --- a/qemu_mode/README.persistent.md +++ b/qemu_mode/README.persistent.md @@ -27,11 +27,12 @@ function and will patch the return address (on stack or in the link register) to return to START (like WinAFL). *Note:* If the target is compiled with position independent code (PIE/PIC) qemu -loads these to a specific base address. For 64 bit you have to add 0x4000000000 -(9 zeroes) and for 32 bit 0x40000000 (7 zeroes) to the address. On strange -setups the base address set by QEMU for PIE executable may change. You can check -it printing the process map using `AFL_QEMU_DEBUG_MAPS=1 afl-qemu-trace -TARGET-BINARY`. +loads these to a specific base address. For amd64 bit you have to add +0x4000000000 (9 zeroes) and for 32 bit 0x40000000 (7 zeroes) to the address. +For aarch64 it is usually 0x5500000000. +On strange setups the base address set by QEMU for PIE executable may change. +You can check it printing the process map using +`AFL_QEMU_DEBUG_MAPS=1 afl-qemu-trace TARGET-BINARY`. If this address is not valid, afl-fuzz will error during startup with the message that the forkserver was not found. diff --git a/qemu_mode/build_qemu_support.sh b/qemu_mode/build_qemu_support.sh index 277a6323..a064fe58 100755 --- a/qemu_mode/build_qemu_support.sh +++ b/qemu_mode/build_qemu_support.sh @@ -13,7 +13,7 @@ # counters by Andrea Fioraldi <andreafioraldi@gmail.com> # # Copyright 2015, 2016, 2017 Google Inc. All rights reserved. -# Copyright 2019-2022 AFLplusplus Project. All rights reserved. +# Copyright 2019-2023 AFLplusplus Project. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -251,6 +251,7 @@ else --disable-qom-cast-debug \ --disable-stack-protector \ --disable-werror \ + --disable-docs \ " fi @@ -360,8 +361,10 @@ if ! command -v "$CROSS" > /dev/null ; then make -C libcompcov && echo "[+] libcompcov ready" echo "[+] Building unsigaction ..." make -C unsigaction && echo "[+] unsigaction ready" + echo "[+] Building fastexit ..." + make -C fastexit && echo "[+] fastexit ready" echo "[+] Building libqasan ..." - make -C libqasan && echo "[+] unsigaction ready" + make -C libqasan && echo "[+] libqasan ready" echo "[+] Building qemu libfuzzer helpers ..." make -C ../utils/aflpp_driver else @@ -373,8 +376,10 @@ else make -C libcompcov CC="$CROSS $CROSS_FLAGS" && echo "[+] libcompcov ready" echo "[+] Building unsigaction ..." make -C unsigaction CC="$CROSS $CROSS_FLAGS" && echo "[+] unsigaction ready" + echo "[+] Building fastexit ..." + make -C fastexit CC="$CROSS $CROSS_FLAGS" && echo "[+] fastexit ready" echo "[+] Building libqasan ..." - make -C libqasan CC="$CROSS $CROSS_FLAGS" && echo "[+] unsigaction ready" + make -C libqasan CC="$CROSS $CROSS_FLAGS" && echo "[+] libqasan ready" fi echo "[+] All done for qemu_mode, enjoy!" diff --git a/qemu_mode/fastexit/Makefile b/qemu_mode/fastexit/Makefile new file mode 100644 index 00000000..c7b79277 --- /dev/null +++ b/qemu_mode/fastexit/Makefile @@ -0,0 +1,30 @@ +# +# american fuzzy lop++ - fastexit +# -------------------------------- +# +# Written by Andrea Fioraldi <andreafioraldi@gmail.com> +# +# Copyright 2019-2023 Andrea Fioraldi. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at: +# +# http://www.apache.org/licenses/LICENSE-2.0 +# + +TARGETS=fastexit.so fastexit32.so fastexit64.so + +all: $(TARGETS) + +fastexit.so: fastexit.c + @if $(CC) -fPIC -shared fastexit.c -o fastexit.so 2>/dev/null ; then echo "fastexit build success"; else echo "fastexit build failure (that's fine)"; fi + +fastexit32.so: fastexit.c + @if $(CC) -fPIC -m32 -shared fastexit.c -o fastexit32.so 2>/dev/null ; then echo "fastexit32 build success"; else echo "fastexit32 build failure (that's fine)"; fi + +fastexit64.so: fastexit.c + @if $(CC) -fPIC -m64 -shared fastexit.c -o fastexit64.so 2>/dev/null ; then echo "fastexit64 build success"; else echo "fastexit64 build failure (that's fine)"; fi + +clean: + rm -f fastexit.so diff --git a/qemu_mode/fastexit/README.md b/qemu_mode/fastexit/README.md new file mode 100644 index 00000000..66763e94 --- /dev/null +++ b/qemu_mode/fastexit/README.md @@ -0,0 +1,5 @@ +# fastexit + +This library forces _exit on exit when preloaded to gain speed. + +Gives speed on complex targets like Android or Wine. diff --git a/qemu_mode/fastexit/fastexit.c b/qemu_mode/fastexit/fastexit.c new file mode 100644 index 00000000..44141af1 --- /dev/null +++ b/qemu_mode/fastexit/fastexit.c @@ -0,0 +1,6 @@ +#include <unistd.h> +#include <stdlib.h> + +void exit(int status) { + _exit(status); +} diff --git a/qemu_mode/libcompcov/Makefile b/qemu_mode/libcompcov/Makefile index cc591393..7260df87 100644 --- a/qemu_mode/libcompcov/Makefile +++ b/qemu_mode/libcompcov/Makefile @@ -4,7 +4,7 @@ # # Written by Andrea Fioraldi <andreafioraldi@gmail.com> # -# Copyright 2019-2022 Andrea Fioraldi. All rights reserved. +# Copyright 2019-2023 Andrea Fioraldi. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/qemu_mode/libcompcov/compcovtest.cc b/qemu_mode/libcompcov/compcovtest.cc index b2d64f8d..23215013 100644 --- a/qemu_mode/libcompcov/compcovtest.cc +++ b/qemu_mode/libcompcov/compcovtest.cc @@ -2,7 +2,7 @@ // // Author: Mateusz Jurczyk (mjurczyk@google.com) // -// Copyright 2019-2022 Google LLC +// Copyright 2019-2023 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/qemu_mode/libcompcov/libcompcov.so.c b/qemu_mode/libcompcov/libcompcov.so.c index c4107b8c..b6ee0019 100644 --- a/qemu_mode/libcompcov/libcompcov.so.c +++ b/qemu_mode/libcompcov/libcompcov.so.c @@ -5,7 +5,7 @@ Written and maintained by Andrea Fioraldi <andreafioraldi@gmail.com> - Copyright 2019-2022 AFLplusplus Project. All rights reserved. + Copyright 2019-2023 AFLplusplus Project. All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/qemu_mode/libqasan/Makefile b/qemu_mode/libqasan/Makefile index 79c3ab70..61782894 100644 --- a/qemu_mode/libqasan/Makefile +++ b/qemu_mode/libqasan/Makefile @@ -4,7 +4,7 @@ # # Written by Andrea Fioraldi <andreafioraldi@gmail.com> # -# Copyright 2019-2022 Andrea Fioraldi. All rights reserved. +# Copyright 2019-2023 Andrea Fioraldi. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/qemu_mode/libqasan/hooks.c b/qemu_mode/libqasan/hooks.c index 7f20e848..a9fd0ce9 100644 --- a/qemu_mode/libqasan/hooks.c +++ b/qemu_mode/libqasan/hooks.c @@ -1,5 +1,5 @@ /******************************************************************************* -Copyright (c) 2019-2022, Andrea Fioraldi +Copyright (c) 2019-2023, Andrea Fioraldi Redistribution and use in source and binary forms, with or without diff --git a/qemu_mode/libqasan/libqasan.c b/qemu_mode/libqasan/libqasan.c index f4d590bd..12be7778 100644 --- a/qemu_mode/libqasan/libqasan.c +++ b/qemu_mode/libqasan/libqasan.c @@ -1,5 +1,5 @@ /******************************************************************************* -Copyright (c) 2019-2022, Andrea Fioraldi +Copyright (c) 2019-2023, Andrea Fioraldi Redistribution and use in source and binary forms, with or without diff --git a/qemu_mode/libqasan/libqasan.h b/qemu_mode/libqasan/libqasan.h index 676f34b0..a430c868 100644 --- a/qemu_mode/libqasan/libqasan.h +++ b/qemu_mode/libqasan/libqasan.h @@ -1,5 +1,5 @@ /******************************************************************************* -Copyright (c) 2019-2022, Andrea Fioraldi +Copyright (c) 2019-2023, Andrea Fioraldi Redistribution and use in source and binary forms, with or without diff --git a/qemu_mode/libqasan/malloc.c b/qemu_mode/libqasan/malloc.c index c83b5eb2..d2db3856 100644 --- a/qemu_mode/libqasan/malloc.c +++ b/qemu_mode/libqasan/malloc.c @@ -1,5 +1,5 @@ /******************************************************************************* -Copyright (c) 2019-2022, Andrea Fioraldi +Copyright (c) 2019-2023, Andrea Fioraldi Redistribution and use in source and binary forms, with or without @@ -306,9 +306,7 @@ int __libqasan_posix_memalign(void **ptr, size_t align, size_t len) { } - size_t rem = len % align; - size_t size = len; - if (rem) size += rem; + size_t size = len + align; int state = QASAN_SWAP(QASAN_DISABLED); // disable qasan for this thread diff --git a/qemu_mode/libqasan/patch.c b/qemu_mode/libqasan/patch.c index 15c4df15..38e0903b 100644 --- a/qemu_mode/libqasan/patch.c +++ b/qemu_mode/libqasan/patch.c @@ -1,5 +1,5 @@ /******************************************************************************* -Copyright (c) 2019-2022, Andrea Fioraldi +Copyright (c) 2019-2023, Andrea Fioraldi Redistribution and use in source and binary forms, with or without diff --git a/qemu_mode/libqasan/string.c b/qemu_mode/libqasan/string.c index fc2de1f2..e17cff4b 100644 --- a/qemu_mode/libqasan/string.c +++ b/qemu_mode/libqasan/string.c @@ -1,5 +1,5 @@ /******************************************************************************* -Copyright (c) 2019-2022, Andrea Fioraldi +Copyright (c) 2019-2023, Andrea Fioraldi Redistribution and use in source and binary forms, with or without diff --git a/qemu_mode/libqasan/uninstrument.c b/qemu_mode/libqasan/uninstrument.c index 1686a015..e37a9b46 100644 --- a/qemu_mode/libqasan/uninstrument.c +++ b/qemu_mode/libqasan/uninstrument.c @@ -7,7 +7,7 @@ for some strange reason. */ /******************************************************************************* -Copyright (c) 2019-2022, Andrea Fioraldi +Copyright (c) 2019-2023, Andrea Fioraldi Redistribution and use in source and binary forms, with or without diff --git a/qemu_mode/qemuafl b/qemu_mode/qemuafl -Subproject 12682ea8169604a6c0f9b2b36eaa53ff7dcc7fd +Subproject 0569eff8a12dec73642b96757f6b5b51a618a03 diff --git a/qemu_mode/unsigaction/Makefile b/qemu_mode/unsigaction/Makefile index f026a2b7..c1a7397f 100644 --- a/qemu_mode/unsigaction/Makefile +++ b/qemu_mode/unsigaction/Makefile @@ -4,7 +4,7 @@ # # Written by Andrea Fioraldi <andreafioraldi@gmail.com> # -# Copyright 2019-2022 Andrea Fioraldi. All rights reserved. +# Copyright 2019-2023 Andrea Fioraldi. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/src/afl-analyze.c b/src/afl-analyze.c index d4822341..5b122741 100644 --- a/src/afl-analyze.c +++ b/src/afl-analyze.c @@ -9,7 +9,7 @@ Andrea Fioraldi <andreafioraldi@gmail.com> Copyright 2016, 2017 Google Inc. All rights reserved. - Copyright 2019-2022 AFLplusplus Project. All rights reserved. + Copyright 2019-2023 AFLplusplus Project. All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -114,16 +114,16 @@ static void kill_child() { if (fsrv.child_pid > 0) { - kill(fsrv.child_pid, fsrv.kill_signal); + kill(fsrv.child_pid, fsrv.child_kill_signal); fsrv.child_pid = -1; } } -static void classify_counts(u8 *mem) { +static void classify_counts(u8 *mem, u32 mem_size) { - u32 i = map_size; + u32 i = mem_size; if (edges_only) { @@ -203,7 +203,7 @@ static void read_initial_file(void) { /* Execute target application. Returns exec checksum, or 0 if program times out. */ -static u32 analyze_run_target(u8 *mem, u32 len, u8 first_run) { +static u64 analyze_run_target(u8 *mem, u32 len, u8 first_run) { afl_fsrv_write_to_testcase(&fsrv, mem, len); fsrv_run_result_t ret = afl_fsrv_run_target(&fsrv, exec_tmout, &stop_soon); @@ -222,7 +222,7 @@ static u32 analyze_run_target(u8 *mem, u32 len, u8 first_run) { } - classify_counts(fsrv.trace_bits); + classify_counts(fsrv.trace_bits, fsrv.map_size); total_execs++; if (stop_soon) { @@ -528,7 +528,7 @@ static void analyze() { for (i = 0; i < in_len; i++) { - u32 xor_ff, xor_01, sub_10, add_10; + u64 xor_ff, xor_01, sub_10, add_10; u8 xff_orig, x01_orig, s10_orig, a10_orig; /* Perform walking byte adjustments across the file. We perform four @@ -656,28 +656,6 @@ static void set_up_environment(char **argv) { if (fsrv.out_fd < 0) { PFATAL("Unable to create '%s'", fsrv.out_file); } /* Set sane defaults... */ - - x = get_afl_env("ASAN_OPTIONS"); - - if (x) { - - if (!strstr(x, "abort_on_error=1")) { - - FATAL("Custom ASAN_OPTIONS set without abort_on_error=1 - please fix!"); - - } - -#ifndef ASAN_BUILD - if (!getenv("AFL_DEBUG") && !strstr(x, "symbolize=0")) { - - FATAL("Custom ASAN_OPTIONS set without symbolize=0 - please fix!"); - - } - -#endif - - } - x = get_afl_env("MSAN_OPTIONS"); if (x) { @@ -689,69 +667,9 @@ static void set_up_environment(char **argv) { } - if (!strstr(x, "symbolize=0")) { - - FATAL("Custom MSAN_OPTIONS set without symbolize=0 - please fix!"); - - } - - } - - x = get_afl_env("LSAN_OPTIONS"); - - if (x) { - - if (!strstr(x, "symbolize=0")) { - - FATAL("Custom LSAN_OPTIONS set without symbolize=0 - please fix!"); - - } - } - setenv("ASAN_OPTIONS", - "abort_on_error=1:" - "detect_leaks=0:" - "allocator_may_return_null=1:" - "detect_odr_violation=0:" - "symbolize=0:" - "handle_segv=0:" - "handle_sigbus=0:" - "handle_abort=0:" - "handle_sigfpe=0:" - "handle_sigill=0", - 0); - - setenv("UBSAN_OPTIONS", - "halt_on_error=1:" - "abort_on_error=1:" - "malloc_context_size=0:" - "allocator_may_return_null=1:" - "symbolize=0:" - "handle_segv=0:" - "handle_sigbus=0:" - "handle_abort=0:" - "handle_sigfpe=0:" - "handle_sigill=0", - 0); - - setenv("MSAN_OPTIONS", "exit_code=" STRINGIFY(MSAN_ERROR) ":" - "abort_on_error=1:" - "msan_track_origins=0" - "allocator_may_return_null=1:" - "symbolize=0:" - "handle_segv=0:" - "handle_sigbus=0:" - "handle_abort=0:" - "handle_sigfpe=0:" - "handle_sigill=0", 0); - - setenv("LSAN_OPTIONS", - "exitcode=" STRINGIFY(LSAN_ERROR) ":" - "fast_unwind_on_malloc=0:" - "symbolize=0:" - "print_suppressions=0", - 0); + set_sanitizer_defaults(); if (get_afl_env("AFL_PRELOAD")) { @@ -807,7 +725,11 @@ static void setup_signal_handlers(void) { struct sigaction sa; sa.sa_handler = NULL; +#ifdef SA_RESTART sa.sa_flags = SA_RESTART; +#else + sa.sa_flags = 0; +#endif sa.sa_sigaction = NULL; sigemptyset(&sa.sa_mask); @@ -846,6 +768,7 @@ static void usage(u8 *argv0) { " -U - use unicorn-based instrumentation (Unicorn mode)\n" " -W - use qemu-based instrumentation with Wine (Wine " "mode)\n" + " -X - use Nyx mode\n" #endif "\n" @@ -862,11 +785,15 @@ static void usage(u8 *argv0) { "MSAN_OPTIONS: custom settings for MSAN\n" " (must contain exitcode="STRINGIFY(MSAN_ERROR)" and symbolize=0)\n" "AFL_ANALYZE_HEX: print file offsets in hexadecimal instead of decimal\n" + "AFL_KILL_SIGNAL: Signal ID delivered to child processes on timeout, etc.\n" + " (default: SIGKILL)\n" + "AFL_FORK_SERVER_KILL_SIGNAL: Kill signal for the fork server on termination\n" + " (default: SIGTERM). If unset and AFL_KILL_SIGNAL is\n" + " set, that value will be used.\n" "AFL_MAP_SIZE: the shared memory size for that target. must be >= the size\n" " the target was compiled for\n" "AFL_PRELOAD: LD_PRELOAD / DYLD_INSERT_LIBRARIES settings for target\n" "AFL_SKIP_BIN_CHECK: skip checking the location of and the target\n" - , argv0, EXEC_TIMEOUT, MEM_LIMIT, doc_path); exit(1); @@ -888,7 +815,7 @@ int main(int argc, char **argv_orig, char **envp) { afl_fsrv_init(&fsrv); - while ((opt = getopt(argc, argv, "+i:f:m:t:eAOQUWh")) > 0) { + while ((opt = getopt(argc, argv, "+i:f:m:t:eAOQUWXYh")) > 0) { switch (opt) { @@ -1040,6 +967,23 @@ int main(int argc, char **argv_orig, char **envp) { break; + case 'Y': // fallthough +#ifdef __linux__ + case 'X': /* NYX mode */ + + if (fsrv.nyx_mode) { FATAL("Multiple -X options not supported"); } + + fsrv.nyx_mode = 1; + fsrv.nyx_parent = true; + fsrv.nyx_standalone = true; + + break; +#else + case 'X': + FATAL("Nyx mode is only availabe on linux..."); + break; +#endif + case 'h': usage(argv[0]); return -1; @@ -1071,7 +1015,21 @@ int main(int argc, char **argv_orig, char **envp) { set_up_environment(argv); +#ifdef __linux__ + if (!fsrv.nyx_mode) { + + fsrv.target_path = find_binary(argv[optind]); + + } else { + + fsrv.target_path = ck_strdup(argv[optind]); + + } + +#else fsrv.target_path = find_binary(argv[optind]); +#endif + fsrv.trace_bits = afl_shm_init(&shm, map_size, 0); detect_file_args(argv + optind, fsrv.out_file, &use_stdin); signal(SIGALRM, kill_child); @@ -1094,6 +1052,26 @@ int main(int argc, char **argv_orig, char **envp) { use_argv = get_cs_argv(argv[0], &target_path, argc - optind, argv + optind); +#ifdef __linux__ + + } else if (fsrv.nyx_mode) { + + fsrv.nyx_id = 0; + + u8 *libnyx_binary = find_afl_binary(argv[0], "libnyx.so"); + fsrv.nyx_handlers = afl_load_libnyx_plugin(libnyx_binary); + if (fsrv.nyx_handlers == NULL) { + + FATAL("failed to initialize libnyx.so..."); + + } + + fsrv.nyx_use_tmp_workdir = true; + fsrv.nyx_bind_cpu_id = 0; + + use_argv = argv + optind; +#endif + } else { use_argv = argv + optind; @@ -1115,11 +1093,15 @@ int main(int argc, char **argv_orig, char **envp) { } - fsrv.kill_signal = - parse_afl_kill_signal_env(getenv("AFL_KILL_SIGNAL"), SIGKILL); + configure_afl_kill_signals( + &fsrv, NULL, NULL, (fsrv.qemu_mode || unicorn_mode) ? SIGKILL : SIGTERM); read_initial_file(); +#ifdef __linux__ + if (!fsrv.nyx_mode) { (void)check_binary_signatures(fsrv.target_path); } +#else (void)check_binary_signatures(fsrv.target_path); +#endif ACTF("Performing dry run (mem limit = %llu MB, timeout = %u ms%s)...", mem_limit, exec_tmout, edges_only ? ", edges only" : ""); diff --git a/src/afl-as.c b/src/afl-as.c index 1edc8cca..772e31b3 100644 --- a/src/afl-as.c +++ b/src/afl-as.c @@ -9,7 +9,7 @@ Andrea Fioraldi <andreafioraldi@gmail.com> Copyright 2016, 2017 Google Inc. All rights reserved. - Copyright 2019-2022 AFLplusplus Project. All rights reserved. + Copyright 2019-2023 AFLplusplus Project. All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -93,7 +93,7 @@ static u8 use_64bit = 0; static void edit_params(int argc, char **argv) { u8 *tmp_dir = getenv("TMPDIR"), *afl_as = getenv("AFL_AS"); - u32 i; + u32 i, input_index; #ifdef __APPLE__ @@ -142,7 +142,23 @@ static void edit_params(int argc, char **argv) { as_params[argc] = 0; - for (i = 1; (s32)i < argc - 1; i++) { + /* Find the input file. It's usually located near the end. + Assume there won't be any arguments referring to files after the input + file, e.g. as input.s -o output.o */ + for (input_index = argc - 1; input_index > 0; input_index--) { + + input_file = argv[input_index]; + /* Clang may add debug arguments after the input file. */ + if (strncmp(input_file, "-g", 2)) break; + + } + + if (input_index == 0) + FATAL("Could not find input file (not called through afl-gcc?)"); + + for (i = 1; (s32)i < argc; i++) { + + if (i == input_index) continue; if (!strcmp(argv[i], "--64")) { @@ -194,8 +210,6 @@ static void edit_params(int argc, char **argv) { #endif /* __APPLE__ */ - input_file = argv[argc - 1]; - if (input_file[0] == '-') { if (!strcmp(input_file + 1, "-version")) { diff --git a/src/afl-cc.c b/src/afl-cc.c index c0449e64..7f15ad76 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -5,7 +5,7 @@ Written by Michal Zalewski, Laszlo Szekeres and Marc Heuse Copyright 2015, 2016 Google Inc. All rights reserved. - Copyright 2019-2022 AFLplusplus Project. All rights reserved. + Copyright 2019-2023 AFLplusplus Project. All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -317,7 +317,7 @@ void parse_fsanitize(char *string) { char *tmp = malloc(strlen(ptr)); u32 count = 0, len, ende = 0; - if (!new || !tmp) { FATAL("could not aquire memory"); } + if (!new || !tmp) { FATAL("could not acquire memory"); } strcpy(new, "-fsanitize="); do { @@ -514,7 +514,9 @@ static void edit_params(u32 argc, char **argv, char **envp) { if (lto_mode && have_instr_env) { #if LLVM_MAJOR >= 11 /* use new pass manager */ + #if LLVM_MAJOR < 16 cc_params[cc_par_cnt++] = "-fexperimental-new-pass-manager"; + #endif cc_params[cc_par_cnt++] = alloc_printf( "-fpass-plugin=%s/afl-llvm-lto-instrumentlist.so", obj_path); #else @@ -530,7 +532,9 @@ static void edit_params(u32 argc, char **argv, char **envp) { if (getenv("AFL_LLVM_DICT2FILE")) { #if LLVM_MAJOR >= 11 /* use new pass manager */ + #if LLVM_MAJOR < 16 cc_params[cc_par_cnt++] = "-fexperimental-new-pass-manager"; + #endif cc_params[cc_par_cnt++] = alloc_printf("-fpass-plugin=%s/afl-llvm-dict2file.so", obj_path); #else @@ -547,7 +551,9 @@ static void edit_params(u32 argc, char **argv, char **envp) { if (getenv("LAF_SPLIT_SWITCHES") || getenv("AFL_LLVM_LAF_SPLIT_SWITCHES")) { #if LLVM_MAJOR >= 11 /* use new pass manager */ + #if LLVM_MAJOR < 16 cc_params[cc_par_cnt++] = "-fexperimental-new-pass-manager"; + #endif cc_params[cc_par_cnt++] = alloc_printf("-fpass-plugin=%s/split-switches-pass.so", obj_path); #else @@ -564,7 +570,9 @@ static void edit_params(u32 argc, char **argv, char **envp) { getenv("AFL_LLVM_LAF_TRANSFORM_COMPARES")) { #if LLVM_MAJOR >= 11 /* use new pass manager */ + #if LLVM_MAJOR < 16 cc_params[cc_par_cnt++] = "-fexperimental-new-pass-manager"; + #endif cc_params[cc_par_cnt++] = alloc_printf("-fpass-plugin=%s/compare-transform-pass.so", obj_path); #else @@ -581,7 +589,9 @@ static void edit_params(u32 argc, char **argv, char **envp) { getenv("AFL_LLVM_LAF_SPLIT_FLOATS")) { #if LLVM_MAJOR >= 11 /* use new pass manager */ + #if LLVM_MAJOR < 16 cc_params[cc_par_cnt++] = "-fexperimental-new-pass-manager"; + #endif cc_params[cc_par_cnt++] = alloc_printf("-fpass-plugin=%s/split-compares-pass.so", obj_path); #else @@ -604,10 +614,14 @@ static void edit_params(u32 argc, char **argv, char **envp) { cc_params[cc_par_cnt++] = "-fno-inline"; #if LLVM_MAJOR >= 11 /* use new pass manager */ + #if LLVM_MAJOR < 16 cc_params[cc_par_cnt++] = "-fexperimental-new-pass-manager"; + #endif cc_params[cc_par_cnt++] = alloc_printf("-fpass-plugin=%s/cmplog-switches-pass.so", obj_path); + #if LLVM_MAJOR < 16 cc_params[cc_par_cnt++] = "-fexperimental-new-pass-manager"; + #endif cc_params[cc_par_cnt++] = alloc_printf("-fpass-plugin=%s/split-switches-pass.so", obj_path); #else @@ -666,15 +680,21 @@ static void edit_params(u32 argc, char **argv, char **envp) { #endif free(ld_path); -#if defined(AFL_CLANG_LDPATH) && LLVM_MAJOR >= 13 +#if defined(AFL_CLANG_LDPATH) && LLVM_MAJOR >= 15 + // The NewPM implementation only works fully since LLVM 15. + cc_params[cc_par_cnt++] = alloc_printf( + "-Wl,--load-pass-plugin=%s/SanitizerCoverageLTO.so", obj_path); +#elif defined(AFL_CLANG_LDPATH) && LLVM_MAJOR >= 13 cc_params[cc_par_cnt++] = "-Wl,--lto-legacy-pass-manager"; + cc_params[cc_par_cnt++] = + alloc_printf("-Wl,-mllvm=-load=%s/SanitizerCoverageLTO.so", obj_path); #else cc_params[cc_par_cnt++] = "-fno-experimental-new-pass-manager"; + cc_params[cc_par_cnt++] = + alloc_printf("-Wl,-mllvm=-load=%s/SanitizerCoverageLTO.so", obj_path); #endif cc_params[cc_par_cnt++] = "-Wl,--allow-multiple-definition"; - cc_params[cc_par_cnt++] = - alloc_printf("-Wl,-mllvm=-load=%s/SanitizerCoverageLTO.so", obj_path); cc_params[cc_par_cnt++] = lto_flag; } else { @@ -699,7 +719,9 @@ static void edit_params(u32 argc, char **argv, char **envp) { } else { #if LLVM_MAJOR >= 11 /* use new pass manager */ + #if LLVM_MAJOR < 16 cc_params[cc_par_cnt++] = "-fexperimental-new-pass-manager"; + #endif cc_params[cc_par_cnt++] = alloc_printf( "-fpass-plugin=%s/SanitizerCoveragePCGUARD.so", obj_path); #else @@ -737,7 +759,9 @@ static void edit_params(u32 argc, char **argv, char **envp) { } else { #if LLVM_MAJOR >= 11 /* use new pass manager */ + #if LLVM_MAJOR < 16 cc_params[cc_par_cnt++] = "-fexperimental-new-pass-manager"; + #endif cc_params[cc_par_cnt++] = alloc_printf("-fpass-plugin=%s/afl-llvm-pass.so", obj_path); #else @@ -755,10 +779,14 @@ static void edit_params(u32 argc, char **argv, char **envp) { if (cmplog_mode) { #if LLVM_MAJOR >= 11 + #if LLVM_MAJOR < 16 cc_params[cc_par_cnt++] = "-fexperimental-new-pass-manager"; + #endif cc_params[cc_par_cnt++] = alloc_printf( "-fpass-plugin=%s/cmplog-instructions-pass.so", obj_path); + #if LLVM_MAJOR < 16 cc_params[cc_par_cnt++] = "-fexperimental-new-pass-manager"; + #endif cc_params[cc_par_cnt++] = alloc_printf("-fpass-plugin=%s/cmplog-routines-pass.so", obj_path); #else @@ -949,7 +977,7 @@ static void edit_params(u32 argc, char **argv, char **envp) { if (plusplus_mode && strlen(libdir) && strncmp(libdir, "/usr", 4) && strncmp(libdir, "/lib", 4)) { - cc_params[cc_par_cnt++] = "-rpath"; + cc_params[cc_par_cnt++] = "-Wl,-rpath"; cc_params[cc_par_cnt++] = libdir; } else { @@ -1022,17 +1050,25 @@ static void edit_params(u32 argc, char **argv, char **envp) { if (getenv("AFL_USE_CFISAN")) { - if (!lto_mode) { + if (compiler_mode == GCC_PLUGIN || compiler_mode == GCC) { - uint32_t i = 0, found = 0; - while (envp[i] != NULL && !found) - if (strncmp("-flto", envp[i++], 5) == 0) found = 1; - if (!found) cc_params[cc_par_cnt++] = "-flto"; + cc_params[cc_par_cnt++] = "-fcf-protection=full"; - } + } else { + + if (!lto_mode) { - cc_params[cc_par_cnt++] = "-fsanitize=cfi"; - cc_params[cc_par_cnt++] = "-fvisibility=hidden"; + uint32_t i = 0, found = 0; + while (envp[i] != NULL && !found) + if (strncmp("-flto", envp[i++], 5) == 0) found = 1; + if (!found) cc_params[cc_par_cnt++] = "-flto"; + + } + + cc_params[cc_par_cnt++] = "-fsanitize=cfi"; + cc_params[cc_par_cnt++] = "-fvisibility=hidden"; + + } } @@ -1065,37 +1101,45 @@ static void edit_params(u32 argc, char **argv, char **envp) { if (!have_c) cc_params[cc_par_cnt++] = "-lrt"; #endif - cc_params[cc_par_cnt++] = "-D__AFL_HAVE_MANUAL_CONTROL=1"; cc_params[cc_par_cnt++] = "-D__AFL_COMPILER=1"; cc_params[cc_par_cnt++] = "-DFUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION=1"; - /* When the user tries to use persistent or deferred forkserver modes by - appending a single line to the program, we want to reliably inject a - signature into the binary (to be picked up by afl-fuzz) and we want - to call a function from the runtime .o file. This is unnecessarily - painful for three reasons: + /* As documented in instrumentation/README.persistent_mode.md, deferred + forkserver initialization and persistent mode are not available in afl-gcc + and afl-clang. */ + if (compiler_mode != GCC && compiler_mode != CLANG) { - 1) We need to convince the compiler not to optimize out the signature. - This is done with __attribute__((used)). + cc_params[cc_par_cnt++] = "-D__AFL_HAVE_MANUAL_CONTROL=1"; - 2) We need to convince the linker, when called with -Wl,--gc-sections, - not to do the same. This is done by forcing an assignment to a - 'volatile' pointer. + /* When the user tries to use persistent or deferred forkserver modes by + appending a single line to the program, we want to reliably inject a + signature into the binary (to be picked up by afl-fuzz) and we want + to call a function from the runtime .o file. This is unnecessarily + painful for three reasons: - 3) We need to declare __afl_persistent_loop() in the global namespace, - but doing this within a method in a class is hard - :: and extern "C" - are forbidden and __attribute__((alias(...))) doesn't work. Hence the - __asm__ aliasing trick. + 1) We need to convince the compiler not to optimize out the signature. + This is done with __attribute__((used)). - */ + 2) We need to convince the linker, when called with -Wl,--gc-sections, + not to do the same. This is done by forcing an assignment to a + 'volatile' pointer. - cc_params[cc_par_cnt++] = - "-D__AFL_FUZZ_INIT()=" - "int __afl_sharedmem_fuzzing = 1;" - "extern unsigned int *__afl_fuzz_len;" - "extern unsigned char *__afl_fuzz_ptr;" - "unsigned char __afl_fuzz_alt[1048576];" - "unsigned char *__afl_fuzz_alt_ptr = __afl_fuzz_alt;"; + 3) We need to declare __afl_persistent_loop() in the global namespace, + but doing this within a method in a class is hard - :: and extern "C" + are forbidden and __attribute__((alias(...))) doesn't work. Hence the + __asm__ aliasing trick. + + */ + + cc_params[cc_par_cnt++] = + "-D__AFL_FUZZ_INIT()=" + "int __afl_sharedmem_fuzzing = 1;" + "extern unsigned int *__afl_fuzz_len;" + "extern unsigned char *__afl_fuzz_ptr;" + "unsigned char __afl_fuzz_alt[1048576];" + "unsigned char *__afl_fuzz_alt_ptr = __afl_fuzz_alt;"; + + } if (plusplus_mode) { @@ -1133,33 +1177,39 @@ static void edit_params(u32 argc, char **argv, char **envp) { "(*__afl_fuzz_len = read(0, __afl_fuzz_alt_ptr, 1048576)) == 0xffffffff " "? 0 : *__afl_fuzz_len)"; - cc_params[cc_par_cnt++] = - "-D__AFL_LOOP(_A)=" - "({ static volatile char *_B __attribute__((used,unused)); " - " _B = (char*)\"" PERSIST_SIG - "\"; " + if (compiler_mode != GCC && compiler_mode != CLANG) { + + cc_params[cc_par_cnt++] = + "-D__AFL_LOOP(_A)=" + "({ static volatile const char *_B __attribute__((used,unused)); " + " _B = (const char*)\"" PERSIST_SIG + "\"; " + "extern int __afl_connected;" #ifdef __APPLE__ - "__attribute__((visibility(\"default\"))) " - "int _L(unsigned int) __asm__(\"___afl_persistent_loop\"); " + "__attribute__((visibility(\"default\"))) " + "int _L(unsigned int) __asm__(\"___afl_persistent_loop\"); " #else - "__attribute__((visibility(\"default\"))) " - "int _L(unsigned int) __asm__(\"__afl_persistent_loop\"); " + "__attribute__((visibility(\"default\"))) " + "int _L(unsigned int) __asm__(\"__afl_persistent_loop\"); " #endif /* ^__APPLE__ */ - "_L(_A); })"; + // if afl is connected, we run _A times, else once. + "_L(__afl_connected ? _A : 1); })"; - cc_params[cc_par_cnt++] = - "-D__AFL_INIT()=" - "do { static volatile char *_A __attribute__((used,unused)); " - " _A = (char*)\"" DEFER_SIG - "\"; " + cc_params[cc_par_cnt++] = + "-D__AFL_INIT()=" + "do { static volatile const char *_A __attribute__((used,unused)); " + " _A = (const char*)\"" DEFER_SIG + "\"; " #ifdef __APPLE__ - "__attribute__((visibility(\"default\"))) " - "void _I(void) __asm__(\"___afl_manual_init\"); " + "__attribute__((visibility(\"default\"))) " + "void _I(void) __asm__(\"___afl_manual_init\"); " #else - "__attribute__((visibility(\"default\"))) " - "void _I(void) __asm__(\"__afl_manual_init\"); " + "__attribute__((visibility(\"default\"))) " + "void _I(void) __asm__(\"__afl_manual_init\"); " #endif /* ^__APPLE__ */ - "_I(); } while (0)"; + "_I(); } while (0)"; + + } if (x_set) { @@ -2003,6 +2053,8 @@ int main(int argc, char **argv, char **envp) { " AFL_LLVM_DICT2FILE: generate an afl dictionary based on found " "comparisons\n" + " AFL_LLVM_DICT2FILE_NO_MAIN: skip parsing main() for the " + "dictionary\n" " AFL_LLVM_LAF_ALL: enables all LAF splits/transforms\n" " AFL_LLVM_LAF_SPLIT_COMPARES: enable cascaded comparisons\n" " AFL_LLVM_LAF_SPLIT_COMPARES_BITW: size limit (default 8)\n" @@ -2090,7 +2142,8 @@ int main(int argc, char **argv, char **envp) { "defaults.\n" "Recommended is afl-clang-lto with AFL_LLVM_CMPLOG or afl-clang-fast " "with\n" - "AFL_LLVM_CMPLOG and AFL_LLVM_DICT2FILE.\n\n"); + "AFL_LLVM_CMPLOG and " + "AFL_LLVM_DICT2FILE+AFL_LLVM_DICT2FILE_NO_MAIN.\n\n"); exit(1); diff --git a/src/afl-common.c b/src/afl-common.c index f3e78ac5..a5c48e80 100644 --- a/src/afl-common.c +++ b/src/afl-common.c @@ -9,7 +9,7 @@ Andrea Fioraldi <andreafioraldi@gmail.com> Copyright 2016, 2017 Google Inc. All rights reserved. - Copyright 2019-2022 AFLplusplus Project. All rights reserved. + Copyright 2019-2023 AFLplusplus Project. All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -25,6 +25,7 @@ #include <stdlib.h> #include <stdio.h> +#include "forkserver.h" #ifndef _GNU_SOURCE #define _GNU_SOURCE #endif @@ -47,6 +48,7 @@ #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> +#include <signal.h> u8 be_quiet = 0; u8 *doc_path = ""; @@ -56,6 +58,90 @@ u8 last_intr = 0; #define AFL_PATH "/usr/local/lib/afl/" #endif +void *afl_memmem(const void *haystack, size_t haystacklen, const void *needle, + size_t needlelen) { + + if (unlikely(needlelen > haystacklen)) { return NULL; } + + for (u32 i = 0; i <= haystacklen - needlelen; ++i) { + + if (unlikely(memcmp(haystack + i, needle, needlelen) == 0)) { + + return (void *)(haystack + i); + + } + + } + + return (void *)NULL; + +} + +void set_sanitizer_defaults() { + + /* Set sane defaults for ASAN if nothing else is specified. */ + u8 *have_asan_options = getenv("ASAN_OPTIONS"); + u8 *have_ubsan_options = getenv("UBSAN_OPTIONS"); + u8 *have_msan_options = getenv("MSAN_OPTIONS"); + u8 *have_lsan_options = getenv("LSAN_OPTIONS"); + u8 have_san_options = 0; + u8 default_options[1024] = + "detect_odr_violation=0:abort_on_error=1:symbolize=0:allocator_may_" + "return_null=1:handle_segv=0:handle_sigbus=0:handle_abort=0:handle_" + "sigfpe=0:handle_sigill=0:"; + + if (have_asan_options || have_ubsan_options || have_msan_options || + have_lsan_options) { + + have_san_options = 1; + + } + + /* LSAN does not support abort_on_error=1. (is this still true??) */ + + if (!have_lsan_options) { + + u8 buf[2048] = ""; + if (!have_san_options) { strcpy(buf, default_options); } + strcat(buf, "exitcode=" STRINGIFY(LSAN_ERROR) ":fast_unwind_on_malloc=0:print_suppressions=0:detect_leaks=1:malloc_context_size=30:"); + setenv("LSAN_OPTIONS", buf, 1); + + } + + /* for everything not LSAN we disable detect_leaks */ + + if (!have_lsan_options) { + + strcat(default_options, "detect_leaks=0:malloc_context_size=0:"); + + } + + /* Set sane defaults for ASAN if nothing else is specified. */ + + if (!have_san_options) { setenv("ASAN_OPTIONS", default_options, 1); } + + /* Set sane defaults for UBSAN if nothing else is specified. */ + + if (!have_san_options) { setenv("UBSAN_OPTIONS", default_options, 1); } + + /* MSAN is tricky, because it doesn't support abort_on_error=1 at this + point. So, we do this in a very hacky way. */ + + if (!have_msan_options) { + + u8 buf[2048] = ""; + if (!have_san_options) { strcpy(buf, default_options); } + strcat(buf, "exit_code=" STRINGIFY(MSAN_ERROR) ":msan_track_origins=0:"); + setenv("MSAN_OPTIONS", buf, 1); + + } + + /* Envs for QASan */ + setenv("QASAN_MAX_CALL_STACK", "0", 0); + setenv("QASAN_SYMBOLIZE", "0", 0); + +} + u32 check_binary_signatures(u8 *fn) { int ret = 0, fd = open(fn, O_RDONLY); @@ -67,7 +153,7 @@ u32 check_binary_signatures(u8 *fn) { if (f_data == MAP_FAILED) { PFATAL("Unable to mmap file '%s'", fn); } close(fd); - if (memmem(f_data, f_len, PERSIST_SIG, strlen(PERSIST_SIG) + 1)) { + if (afl_memmem(f_data, f_len, PERSIST_SIG, strlen(PERSIST_SIG) + 1)) { if (!be_quiet) { OKF(cPIN "Persistent mode binary detected."); } setenv(PERSIST_ENV_VAR, "1", 1); @@ -92,7 +178,7 @@ u32 check_binary_signatures(u8 *fn) { } - if (memmem(f_data, f_len, DEFER_SIG, strlen(DEFER_SIG) + 1)) { + if (afl_memmem(f_data, f_len, DEFER_SIG, strlen(DEFER_SIG) + 1)) { if (!be_quiet) { OKF(cPIN "Deferred forkserver binary detected."); } setenv(DEFER_ENV_VAR, "1", 1); @@ -456,38 +542,57 @@ u8 *find_afl_binary(u8 *own_loc, u8 *fname) { } -/* Parses the kill signal environment variable, FATALs on error. - If the env is not set, sets the env to default_signal for the signal handlers - and returns the default_signal. */ -int parse_afl_kill_signal_env(u8 *afl_kill_signal_env, int default_signal) { +int parse_afl_kill_signal(u8 *numeric_signal_as_str, int default_signal) { - if (afl_kill_signal_env && afl_kill_signal_env[0]) { + if (numeric_signal_as_str && numeric_signal_as_str[0]) { char *endptr; u8 signal_code; - signal_code = (u8)strtoul(afl_kill_signal_env, &endptr, 10); + signal_code = (u8)strtoul(numeric_signal_as_str, &endptr, 10); /* Did we manage to parse the full string? */ - if (*endptr != '\0' || endptr == (char *)afl_kill_signal_env) { + if (*endptr != '\0' || endptr == (char *)numeric_signal_as_str) { + + FATAL("Invalid signal name: %s", numeric_signal_as_str); - FATAL("Invalid AFL_KILL_SIGNAL: %s (expected unsigned int)", - afl_kill_signal_env); + } else { + + return signal_code; } - return signal_code; + } + + return default_signal; - } else { +} + +void configure_afl_kill_signals(afl_forkserver_t *fsrv, + char *afl_kill_signal_env, + char *afl_fsrv_kill_signal_env, + int default_server_kill_signal) { + + afl_kill_signal_env = + afl_kill_signal_env ? afl_kill_signal_env : getenv("AFL_KILL_SIGNAL"); + afl_fsrv_kill_signal_env = afl_fsrv_kill_signal_env + ? afl_fsrv_kill_signal_env + : getenv("AFL_FORK_SERVER_KILL_SIGNAL"); - char *sigstr = alloc_printf("%d", default_signal); - if (!sigstr) { FATAL("Failed to alloc mem for signal buf"); } + fsrv->child_kill_signal = parse_afl_kill_signal(afl_kill_signal_env, SIGKILL); - /* Set the env for signal handler */ - setenv("AFL_KILL_SIGNAL", sigstr, 1); - free(sigstr); - return default_signal; + if (afl_kill_signal_env && !afl_fsrv_kill_signal_env) { + + /* + Set AFL_FORK_SERVER_KILL_SIGNAL to the value of AFL_KILL_SIGNAL for + backwards compatibility. However, if AFL_FORK_SERVER_KILL_SIGNAL is set, is + takes precedence. + */ + afl_fsrv_kill_signal_env = afl_kill_signal_env; } + fsrv->fsrv_kill_signal = parse_afl_kill_signal(afl_fsrv_kill_signal_env, + default_server_kill_signal); + } static inline unsigned int helper_min3(unsigned int a, unsigned int b, @@ -1254,3 +1359,52 @@ s32 create_file(u8 *fn) { } +#ifdef __linux__ + +/* Nyx requires a tmp workdir to access specific files (such as mmapped files, + * etc.). This helper function basically creates both a path to a tmp workdir + * and the workdir itself. If the environment variable TMPDIR is set, we use + * that as the base directory, otherwise we use /tmp. */ +char *create_nyx_tmp_workdir(void) { + + char *tmpdir = getenv("TMPDIR"); + + if (!tmpdir) { tmpdir = "/tmp"; } + + char *nyx_out_dir_path = + alloc_printf("%s/.nyx_tmp_%d/", tmpdir, (u32)getpid()); + + if (mkdir(nyx_out_dir_path, 0700)) { PFATAL("Unable to create nyx workdir"); } + + return nyx_out_dir_path; + +} + +/* Vice versa, we remove the tmp workdir for nyx with this helper function. */ +void remove_nyx_tmp_workdir(afl_forkserver_t *fsrv, char *nyx_out_dir_path) { + + char *workdir_path = alloc_printf("%s/workdir", nyx_out_dir_path); + + if (access(workdir_path, R_OK) == 0) { + + if (fsrv->nyx_handlers->nyx_remove_work_dir(workdir_path) != true) { + + WARNF("Unable to remove nyx workdir (%s)", workdir_path); + + } + + } + + if (rmdir(nyx_out_dir_path)) { + + WARNF("Unable to remove nyx workdir (%s)", nyx_out_dir_path); + + } + + ck_free(workdir_path); + ck_free(nyx_out_dir_path); + +} + +#endif + diff --git a/src/afl-forkserver.c b/src/afl-forkserver.c index 628ff590..aa8c8622 100644 --- a/src/afl-forkserver.c +++ b/src/afl-forkserver.c @@ -13,7 +13,7 @@ Copyright 2016, 2017 Google Inc. All rights reserved. - Copyright 2019-2022 AFLplusplus Project. All rights reserved. + Copyright 2019-2023 AFLplusplus Project. All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -49,6 +49,134 @@ #include <sys/select.h> #include <sys/stat.h> +#ifdef __linux__ + #include <dlfcn.h> + +/* function to load nyx_helper function from libnyx.so */ + +nyx_plugin_handler_t *afl_load_libnyx_plugin(u8 *libnyx_binary) { + + void *handle; + nyx_plugin_handler_t *plugin = calloc(1, sizeof(nyx_plugin_handler_t)); + + ACTF("Trying to load libnyx.so plugin..."); + handle = dlopen((char *)libnyx_binary, RTLD_NOW); + if (!handle) { goto fail; } + + plugin->nyx_config_load = dlsym(handle, "nyx_config_load"); + if (plugin->nyx_config_load == NULL) { goto fail; } + + plugin->nyx_config_set_workdir_path = + dlsym(handle, "nyx_config_set_workdir_path"); + if (plugin->nyx_config_set_workdir_path == NULL) { goto fail; } + + plugin->nyx_config_set_input_buffer_size = + dlsym(handle, "nyx_config_set_input_buffer_size"); + if (plugin->nyx_config_set_input_buffer_size == NULL) { goto fail; } + + plugin->nyx_config_set_input_buffer_write_protection = + dlsym(handle, "nyx_config_set_input_buffer_write_protection"); + if (plugin->nyx_config_set_input_buffer_write_protection == NULL) { + + goto fail; + + } + + plugin->nyx_config_set_hprintf_fd = + dlsym(handle, "nyx_config_set_hprintf_fd"); + if (plugin->nyx_config_set_hprintf_fd == NULL) { goto fail; } + + plugin->nyx_config_set_process_role = + dlsym(handle, "nyx_config_set_process_role"); + if (plugin->nyx_config_set_process_role == NULL) { goto fail; } + + plugin->nyx_config_set_reuse_snapshot_path = + dlsym(handle, "nyx_config_set_reuse_snapshot_path"); + if (plugin->nyx_config_set_reuse_snapshot_path == NULL) { goto fail; } + + plugin->nyx_new = dlsym(handle, "nyx_new"); + if (plugin->nyx_new == NULL) { goto fail; } + + plugin->nyx_shutdown = dlsym(handle, "nyx_shutdown"); + if (plugin->nyx_shutdown == NULL) { goto fail; } + + plugin->nyx_option_set_reload_mode = + dlsym(handle, "nyx_option_set_reload_mode"); + if (plugin->nyx_option_set_reload_mode == NULL) { goto fail; } + + plugin->nyx_option_set_timeout = dlsym(handle, "nyx_option_set_timeout"); + if (plugin->nyx_option_set_timeout == NULL) { goto fail; } + + plugin->nyx_option_apply = dlsym(handle, "nyx_option_apply"); + if (plugin->nyx_option_apply == NULL) { goto fail; } + + plugin->nyx_set_afl_input = dlsym(handle, "nyx_set_afl_input"); + if (plugin->nyx_set_afl_input == NULL) { goto fail; } + + plugin->nyx_exec = dlsym(handle, "nyx_exec"); + if (plugin->nyx_exec == NULL) { goto fail; } + + plugin->nyx_get_bitmap_buffer = dlsym(handle, "nyx_get_bitmap_buffer"); + if (plugin->nyx_get_bitmap_buffer == NULL) { goto fail; } + + plugin->nyx_get_bitmap_buffer_size = + dlsym(handle, "nyx_get_bitmap_buffer_size"); + if (plugin->nyx_get_bitmap_buffer_size == NULL) { goto fail; } + + plugin->nyx_get_aux_string = dlsym(handle, "nyx_get_aux_string"); + if (plugin->nyx_get_aux_string == NULL) { goto fail; } + + plugin->nyx_remove_work_dir = dlsym(handle, "nyx_remove_work_dir"); + if (plugin->nyx_remove_work_dir == NULL) { goto fail; } + + OKF("libnyx plugin is ready!"); + return plugin; + +fail: + + FATAL("failed to load libnyx: %s\n", dlerror()); + ck_free(plugin); + return NULL; + +} + +void afl_nyx_runner_kill(afl_forkserver_t *fsrv) { + + if (fsrv->nyx_mode) { + + if (fsrv->nyx_aux_string) { ck_free(fsrv->nyx_aux_string); } + + /* check if we actually got a valid nyx runner */ + if (fsrv->nyx_runner) { + + fsrv->nyx_handlers->nyx_shutdown(fsrv->nyx_runner); + + } + + /* if we have use a tmp work dir we need to remove it */ + if (fsrv->nyx_use_tmp_workdir && fsrv->nyx_tmp_workdir_path) { + + remove_nyx_tmp_workdir(fsrv, fsrv->nyx_tmp_workdir_path); + + } + + } + +} + + /* Wrapper for FATAL() that kills the nyx runner (and removes all created tmp + * files) before exiting. Used before "afl_fsrv_killall()" is registered as + * an atexit() handler. */ + #define NYX_PRE_FATAL(fsrv, x...) \ + do { \ + \ + afl_nyx_runner_kill(fsrv); \ + FATAL(x); \ + \ + } while (0) + +#endif + /** * The correct fds for reading and writing pipes */ @@ -84,6 +212,8 @@ void afl_fsrv_init(afl_forkserver_t *fsrv) { fsrv->nyx_runner = NULL; fsrv->nyx_id = 0xFFFFFFFF; fsrv->nyx_bind_cpu_id = 0xFFFFFFFF; + fsrv->nyx_use_tmp_workdir = false; + fsrv->nyx_tmp_workdir_path = NULL; #endif // this structure needs default so we initialize it if this was not done @@ -100,7 +230,7 @@ void afl_fsrv_init(afl_forkserver_t *fsrv) { fsrv->init_tmout = EXEC_TIMEOUT * FORK_WAIT_MULT; fsrv->mem_limit = MEM_LIMIT; fsrv->out_file = NULL; - fsrv->kill_signal = SIGKILL; + fsrv->child_kill_signal = SIGKILL; /* exec related stuff */ fsrv->child_pid = -1; @@ -134,7 +264,7 @@ void afl_fsrv_init_dup(afl_forkserver_t *fsrv_to, afl_forkserver_t *from) { fsrv_to->no_unlink = from->no_unlink; fsrv_to->uses_crash_exitcode = from->uses_crash_exitcode; fsrv_to->crash_exitcode = from->crash_exitcode; - fsrv_to->kill_signal = from->kill_signal; + fsrv_to->child_kill_signal = from->child_kill_signal; fsrv_to->debug = from->debug; // These are forkserver specific. @@ -397,40 +527,119 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv, if (!be_quiet) { ACTF("Spinning up the NYX backend..."); } - if (fsrv->out_dir_path == NULL) { FATAL("Nyx workdir path not found..."); } + if (fsrv->nyx_use_tmp_workdir) { + + fsrv->nyx_tmp_workdir_path = create_nyx_tmp_workdir(); + fsrv->out_dir_path = fsrv->nyx_tmp_workdir_path; + + } else { + + if (fsrv->out_dir_path == NULL) { + + NYX_PRE_FATAL(fsrv, "Nyx workdir path not found..."); + + } + + } + + /* libnyx expects an absolute path */ + char *outdir_path_absolute = realpath(fsrv->out_dir_path, NULL); + if (outdir_path_absolute == NULL) { - char *x = alloc_printf("%s/workdir", fsrv->out_dir_path); + NYX_PRE_FATAL(fsrv, "Nyx workdir path cannot be resolved ..."); - if (fsrv->nyx_id == 0xFFFFFFFF) { FATAL("Nyx ID is not set..."); } + } + + char *workdir_path = alloc_printf("%s/workdir", outdir_path_absolute); + + if (fsrv->nyx_id == 0xFFFFFFFF) { + + NYX_PRE_FATAL(fsrv, "Nyx ID is not set..."); + + } if (fsrv->nyx_bind_cpu_id == 0xFFFFFFFF) { - FATAL("Nyx CPU ID is not set..."); + NYX_PRE_FATAL(fsrv, "Nyx CPU ID is not set..."); } + void *nyx_config = fsrv->nyx_handlers->nyx_config_load(fsrv->target_path); + + fsrv->nyx_handlers->nyx_config_set_workdir_path(nyx_config, workdir_path); + fsrv->nyx_handlers->nyx_config_set_input_buffer_size(nyx_config, MAX_FILE); + fsrv->nyx_handlers->nyx_config_set_input_buffer_write_protection(nyx_config, + true); + if (fsrv->nyx_standalone) { - fsrv->nyx_runner = fsrv->nyx_handlers->nyx_new( - fsrv->target_path, x, fsrv->nyx_bind_cpu_id, MAX_FILE, true); + fsrv->nyx_handlers->nyx_config_set_process_role(nyx_config, StandAlone); } else { if (fsrv->nyx_parent) { - fsrv->nyx_runner = fsrv->nyx_handlers->nyx_new_parent( - fsrv->target_path, x, fsrv->nyx_bind_cpu_id, MAX_FILE, true); + fsrv->nyx_handlers->nyx_config_set_process_role(nyx_config, Parent); } else { - fsrv->nyx_runner = fsrv->nyx_handlers->nyx_new_child( - fsrv->target_path, x, fsrv->nyx_bind_cpu_id, fsrv->nyx_id); + fsrv->nyx_handlers->nyx_config_set_process_role(nyx_config, Child); + + } + + } + + if (getenv("NYX_REUSE_SNAPSHOT") != NULL) { + + if (access(getenv("NYX_REUSE_SNAPSHOT"), F_OK) == -1) { + + NYX_PRE_FATAL(fsrv, "NYX_REUSE_SNAPSHOT path does not exist"); + + } + + /* stupid sanity check to avoid passing an empty or invalid snapshot + * directory */ + char *snapshot_file_path = + alloc_printf("%s/global.state", getenv("NYX_REUSE_SNAPSHOT")); + if (access(snapshot_file_path, R_OK) == -1) { + + NYX_PRE_FATAL( + fsrv, + "NYX_REUSE_SNAPSHOT path does not contain a valid Nyx snapshot"); + + } + + ck_free(snapshot_file_path); + + /* another sanity check to avoid passing a snapshot directory that is + * located in the current workdir (the workdir will be wiped by libnyx on + * startup) */ + char *workdir_snapshot_path = + alloc_printf("%s/workdir/snapshot", outdir_path_absolute); + char *reuse_snapshot_path_real = + realpath(getenv("NYX_REUSE_SNAPSHOT"), NULL); + + if (strcmp(workdir_snapshot_path, reuse_snapshot_path_real) == 0) { + + NYX_PRE_FATAL(fsrv, + "NYX_REUSE_SNAPSHOT path is located in current workdir " + "(use another output directory)"); } + ck_free(reuse_snapshot_path_real); + ck_free(workdir_snapshot_path); + + fsrv->nyx_handlers->nyx_config_set_reuse_snapshot_path( + nyx_config, getenv("NYX_REUSE_SNAPSHOT")); + } - ck_free(x); + fsrv->nyx_runner = + fsrv->nyx_handlers->nyx_new(nyx_config, fsrv->nyx_bind_cpu_id); + + ck_free(workdir_path); + ck_free(outdir_path_absolute); if (fsrv->nyx_runner == NULL) { FATAL("Something went wrong ..."); } @@ -458,15 +667,13 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv, switch (fsrv->nyx_handlers->nyx_exec(fsrv->nyx_runner)) { case Abort: - fsrv->nyx_handlers->nyx_shutdown(fsrv->nyx_runner); - FATAL("Error: Nyx abort occured..."); + NYX_PRE_FATAL(fsrv, "Error: Nyx abort occured..."); break; case IoError: - FATAL("Error: QEMU-Nyx has died..."); + NYX_PRE_FATAL(fsrv, "Error: QEMU-Nyx has died..."); break; case Error: - fsrv->nyx_handlers->nyx_shutdown(fsrv->nyx_runner); - FATAL("Error: Nyx runtime error has occured..."); + NYX_PRE_FATAL(fsrv, "Error: Nyx runtime error has occured..."); break; default: break; @@ -476,7 +683,8 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv, /* autodict in Nyx mode */ if (!ignore_autodict) { - x = alloc_printf("%s/workdir/dump/afl_autodict.txt", fsrv->out_dir_path); + char *x = + alloc_printf("%s/workdir/dump/afl_autodict.txt", fsrv->out_dir_path); int nyx_autodict_fd = open(x, O_RDONLY); ck_free(x); @@ -489,8 +697,9 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv, u8 *dict = ck_alloc(f_len); if (dict == NULL) { - FATAL("Could not allocate %u bytes of autodictionary memory", - f_len); + NYX_PRE_FATAL( + fsrv, "Could not allocate %u bytes of autodictionary memory", + f_len); } @@ -507,7 +716,8 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv, } else { - FATAL( + NYX_PRE_FATAL( + fsrv, "Reading autodictionary fail at position %u with %u bytes " "left.", offset, len); @@ -688,70 +898,8 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv, if (!getenv("LD_BIND_LAZY")) { setenv("LD_BIND_NOW", "1", 1); } - /* Set sane defaults for ASAN if nothing else is specified. */ - - if (!getenv("ASAN_OPTIONS")) - setenv("ASAN_OPTIONS", - "abort_on_error=1:" - "detect_leaks=0:" - "malloc_context_size=0:" - "symbolize=0:" - "allocator_may_return_null=1:" - "detect_odr_violation=0:" - "handle_segv=0:" - "handle_sigbus=0:" - "handle_abort=0:" - "handle_sigfpe=0:" - "handle_sigill=0", - 1); - - /* Set sane defaults for UBSAN if nothing else is specified. */ - - if (!getenv("UBSAN_OPTIONS")) - setenv("UBSAN_OPTIONS", - "halt_on_error=1:" - "abort_on_error=1:" - "malloc_context_size=0:" - "allocator_may_return_null=1:" - "symbolize=0:" - "handle_segv=0:" - "handle_sigbus=0:" - "handle_abort=0:" - "handle_sigfpe=0:" - "handle_sigill=0", - 1); - - /* Envs for QASan */ - setenv("QASAN_MAX_CALL_STACK", "0", 0); - setenv("QASAN_SYMBOLIZE", "0", 0); - - /* MSAN is tricky, because it doesn't support abort_on_error=1 at this - point. So, we do this in a very hacky way. */ - - if (!getenv("MSAN_OPTIONS")) - setenv("MSAN_OPTIONS", - "exit_code=" STRINGIFY(MSAN_ERROR) ":" - "symbolize=0:" - "abort_on_error=1:" - "malloc_context_size=0:" - "allocator_may_return_null=1:" - "msan_track_origins=0:" - "handle_segv=0:" - "handle_sigbus=0:" - "handle_abort=0:" - "handle_sigfpe=0:" - "handle_sigill=0", - 1); - - /* LSAN, too, does not support abort_on_error=1. */ - - if (!getenv("LSAN_OPTIONS")) - setenv("LSAN_OPTIONS", - "exitcode=" STRINGIFY(LSAN_ERROR) ":" - "fast_unwind_on_malloc=0:" - "symbolize=0:" - "print_suppressions=0", - 1); + /* Set sane defaults for sanitizers */ + set_sanitizer_defaults(); fsrv->init_child_func(fsrv, argv); @@ -793,7 +941,7 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv, s32 tmp_pid = fsrv->fsrv_pid; if (tmp_pid > 0) { - kill(tmp_pid, fsrv->kill_signal); + kill(tmp_pid, fsrv->child_kill_signal); fsrv->fsrv_pid = -1; } @@ -804,7 +952,7 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv, s32 tmp_pid = fsrv->fsrv_pid; if (tmp_pid > 0) { - kill(tmp_pid, fsrv->kill_signal); + kill(tmp_pid, fsrv->child_kill_signal); fsrv->fsrv_pid = -1; } @@ -1242,11 +1390,11 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv, void afl_fsrv_kill(afl_forkserver_t *fsrv) { - if (fsrv->child_pid > 0) { kill(fsrv->child_pid, fsrv->kill_signal); } + if (fsrv->child_pid > 0) { kill(fsrv->child_pid, fsrv->child_kill_signal); } if (fsrv->fsrv_pid > 0) { - kill(fsrv->fsrv_pid, fsrv->kill_signal); - if (waitpid(fsrv->fsrv_pid, NULL, 0) <= 0) { WARNF("error waitpid\n"); } + kill(fsrv->fsrv_pid, fsrv->fsrv_kill_signal); + waitpid(fsrv->fsrv_pid, NULL, 0); } @@ -1256,13 +1404,7 @@ void afl_fsrv_kill(afl_forkserver_t *fsrv) { fsrv->child_pid = -1; #ifdef __linux__ - if (fsrv->nyx_mode) { - - free(fsrv->nyx_aux_string); - fsrv->nyx_handlers->nyx_shutdown(fsrv->nyx_runner); - - } - + afl_nyx_runner_kill(fsrv); #endif } @@ -1432,14 +1574,13 @@ afl_fsrv_run_target(afl_forkserver_t *fsrv, u32 timeout, case Crash: case Asan: return FSRV_RUN_CRASH; - case Timout: + case Timeout: return FSRV_RUN_TMOUT; case InvalidWriteToPayload: /* ??? */ FATAL("FixMe: Nyx InvalidWriteToPayload handler is missing"); break; case Abort: - fsrv->nyx_handlers->nyx_shutdown(fsrv->nyx_runner); FATAL("Error: Nyx abort occured..."); case IoError: if (*stop_soon_p) { @@ -1545,7 +1686,7 @@ afl_fsrv_run_target(afl_forkserver_t *fsrv, u32 timeout, s32 tmp_pid = fsrv->child_pid; if (tmp_pid > 0) { - kill(tmp_pid, fsrv->kill_signal); + kill(tmp_pid, fsrv->child_kill_signal); fsrv->child_pid = -1; } @@ -1605,7 +1746,7 @@ afl_fsrv_run_target(afl_forkserver_t *fsrv, u32 timeout, /* Did we timeout? */ if (unlikely(fsrv->last_run_timed_out)) { - fsrv->last_kill_signal = fsrv->kill_signal; + fsrv->last_kill_signal = fsrv->child_kill_signal; return FSRV_RUN_TMOUT; } diff --git a/src/afl-fuzz-bitmap.c b/src/afl-fuzz-bitmap.c index b3a10bb7..556bb5d1 100644 --- a/src/afl-fuzz-bitmap.c +++ b/src/afl-fuzz-bitmap.c @@ -9,7 +9,7 @@ Andrea Fioraldi <andreafioraldi@gmail.com> Copyright 2016, 2017 Google Inc. All rights reserved. - Copyright 2019-2022 AFLplusplus Project. All rights reserved. + Copyright 2019-2023 AFLplusplus Project. All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -457,9 +457,16 @@ save_if_interesting(afl_state_t *afl, void *mem, u32 len, u8 fault) { if (unlikely(len == 0)) { return 0; } + if (unlikely(fault == FSRV_RUN_TMOUT && afl->afl_env.afl_ignore_timeouts)) { + + return 0; + + } + u8 fn[PATH_MAX]; u8 *queue_fn = ""; - u8 new_bits = 0, keeping = 0, res, classified = 0, is_timeout = 0; + u8 new_bits = 0, keeping = 0, res, classified = 0, is_timeout = 0, + need_hash = 1; s32 fd; u64 cksum = 0; @@ -469,10 +476,14 @@ save_if_interesting(afl_state_t *afl, void *mem, u32 len, u8 fault) { only be used for special schedules */ if (unlikely(afl->schedule >= FAST && afl->schedule <= RARE)) { + classify_counts(&afl->fsrv); + classified = 1; + need_hash = 0; + cksum = hash64(afl->fsrv.trace_bits, afl->fsrv.map_size, HASH_CONST); /* Saturated increment */ - if (afl->n_fuzz[cksum % N_FUZZ_SIZE] < 0xFFFFFFFF) + if (likely(afl->n_fuzz[cksum % N_FUZZ_SIZE] < 0xFFFFFFFF)) afl->n_fuzz[cksum % N_FUZZ_SIZE]++; } @@ -482,7 +493,17 @@ save_if_interesting(afl_state_t *afl, void *mem, u32 len, u8 fault) { /* Keep only if there are new bits in the map, add to queue for future fuzzing, etc. */ - new_bits = has_new_bits_unclassified(afl, afl->virgin_bits); + if (likely(classified)) { + + new_bits = has_new_bits(afl, afl->virgin_bits); + + } else { + + new_bits = has_new_bits_unclassified(afl, afl->virgin_bits); + + if (unlikely(new_bits)) { classified = 1; } + + } if (likely(!new_bits)) { @@ -491,8 +512,6 @@ save_if_interesting(afl_state_t *afl, void *mem, u32 len, u8 fault) { } - classified = new_bits; - save_to_queue: #ifndef SIMPLE_FILES @@ -550,21 +569,25 @@ save_if_interesting(afl_state_t *afl, void *mem, u32 len, u8 fault) { } - /* AFLFast schedule? update the new queue entry */ - if (cksum) { + if (unlikely(need_hash && new_bits)) { + + /* due to classify counts we have to recalculate the checksum */ + afl->queue_top->exec_cksum = + hash64(afl->fsrv.trace_bits, afl->fsrv.map_size, HASH_CONST); + need_hash = 0; + + } + + /* For AFLFast schedules we update the new queue entry */ + if (likely(cksum)) { afl->queue_top->n_fuzz_entry = cksum % N_FUZZ_SIZE; afl->n_fuzz[afl->queue_top->n_fuzz_entry] = 1; } - /* due to classify counts we have to recalculate the checksum */ - afl->queue_top->exec_cksum = - hash64(afl->fsrv.trace_bits, afl->fsrv.map_size, HASH_CONST); - /* Try to calibrate inline; this also calls update_bitmap_score() when successful. */ - res = calibrate_case(afl, afl->queue_top, mem, afl->queue_cycle - 1, 0); if (unlikely(res == FSRV_RUN_ERROR)) { @@ -598,7 +621,7 @@ save_if_interesting(afl_state_t *afl, void *mem, u32 len, u8 fault) { if (likely(!afl->non_instrumented_mode)) { - if (!classified) { + if (unlikely(!classified)) { classify_counts(&afl->fsrv); classified = 1; @@ -723,7 +746,12 @@ save_if_interesting(afl_state_t *afl, void *mem, u32 len, u8 fault) { if (likely(!afl->non_instrumented_mode)) { - if (!classified) { classify_counts(&afl->fsrv); } + if (unlikely(!classified)) { + + classify_counts(&afl->fsrv); + classified = 1; + + } simplify_trace(afl, afl->fsrv.trace_bits); diff --git a/src/afl-fuzz-cmplog.c b/src/afl-fuzz-cmplog.c index d0c829e2..3e6432ca 100644 --- a/src/afl-fuzz-cmplog.c +++ b/src/afl-fuzz-cmplog.c @@ -11,7 +11,7 @@ Andrea Fioraldi <andreafioraldi@gmail.com> Copyright 2016, 2017 Google Inc. All rights reserved. - Copyright 2019-2022 AFLplusplus Project. All rights reserved. + Copyright 2019-2023 AFLplusplus Project. All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -33,15 +33,19 @@ void cmplog_exec_child(afl_forkserver_t *fsrv, char **argv) { setenv("___AFL_EINS_ZWEI_POLIZEI___", "1", 1); - if (fsrv->qemu_mode) { setenv("AFL_DISABLE_LLVM_INSTRUMENTATION", "1", 0); } + if (fsrv->qemu_mode || fsrv->cs_mode) { + + setenv("AFL_DISABLE_LLVM_INSTRUMENTATION", "1", 0); + + } if (!fsrv->qemu_mode && !fsrv->frida_mode && argv[0] != fsrv->cmplog_binary) { - argv[0] = fsrv->cmplog_binary; + fsrv->target_path = argv[0] = fsrv->cmplog_binary; } - execv(argv[0], argv); + execv(fsrv->target_path, argv); } diff --git a/src/afl-fuzz-extras.c b/src/afl-fuzz-extras.c index 884bb569..f6de11ae 100644 --- a/src/afl-fuzz-extras.c +++ b/src/afl-fuzz-extras.c @@ -9,7 +9,7 @@ Andrea Fioraldi <andreafioraldi@gmail.com> Copyright 2016, 2017 Google Inc. All rights reserved. - Copyright 2019-2022 AFLplusplus Project. All rights reserved. + Copyright 2019-2023 AFLplusplus Project. All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/src/afl-fuzz-init.c b/src/afl-fuzz-init.c index 4ffcfd2b..01d1e82e 100644 --- a/src/afl-fuzz-init.c +++ b/src/afl-fuzz-init.c @@ -9,7 +9,7 @@ Andrea Fioraldi <andreafioraldi@gmail.com> Copyright 2016, 2017 Google Inc. All rights reserved. - Copyright 2019-2022 AFLplusplus Project. All rights reserved. + Copyright 2019-2023 AFLplusplus Project. All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -24,7 +24,9 @@ */ #include "afl-fuzz.h" +#include "common.h" #include <limits.h> +#include <string.h> #include "cmplog.h" #ifdef HAVE_AFFINITY @@ -1120,7 +1122,7 @@ void perform_dry_run(afl_state_t *afl) { } - if (q->var_behavior) { + if (unlikely(q->var_behavior && !afl->afl_env.afl_no_warn_instability)) { WARNF("Instrumentation output varies across runs."); @@ -1817,17 +1819,35 @@ static void handle_existing_out_dir(afl_state_t *afl) { if (afl->file_extension) { - fn = alloc_printf("%s/.cur_input.%s", afl->tmp_dir, afl->file_extension); + fn = alloc_printf("%s/.cur_input.%s", afl->out_dir, afl->file_extension); } else { - fn = alloc_printf("%s/.cur_input", afl->tmp_dir); + fn = alloc_printf("%s/.cur_input", afl->out_dir); } if (unlink(fn) && errno != ENOENT) { goto dir_cleanup_failed; } ck_free(fn); + if (afl->afl_env.afl_tmpdir) { + + if (afl->file_extension) { + + fn = alloc_printf("%s/.cur_input.%s", afl->afl_env.afl_tmpdir, + afl->file_extension); + + } else { + + fn = alloc_printf("%s/.cur_input", afl->afl_env.afl_tmpdir); + + } + + if (unlink(fn) && errno != ENOENT) { goto dir_cleanup_failed; } + ck_free(fn); + + } + fn = alloc_printf("%s/fuzz_bitmap", afl->out_dir); if (unlink(fn) && errno != ENOENT) { goto dir_cleanup_failed; } ck_free(fn); @@ -1848,6 +1868,10 @@ static void handle_existing_out_dir(afl_state_t *afl) { } + fn = alloc_printf("%s/queue_data", afl->out_dir); + if (unlink(fn) && errno != ENOENT) { goto dir_cleanup_failed; } + ck_free(fn); + fn = alloc_printf("%s/cmdline", afl->out_dir); if (unlink(fn) && errno != ENOENT) { goto dir_cleanup_failed; } ck_free(fn); @@ -2420,7 +2444,9 @@ void get_core_count(afl_state_t *afl) { } else if ((s64)cur_runnable + 1 <= (s64)afl->cpu_core_count) { - OKF("Try parallel jobs - see %s/parallel_fuzzing.md.", doc_path); + OKF("Try parallel jobs - see " + "%s/fuzzing_in_depth.md#c-using-multiple-cores", + doc_path); } @@ -2762,7 +2788,7 @@ void check_binary(afl_state_t *afl, u8 *fname) { !afl->fsrv.nyx_mode && #endif !afl->fsrv.cs_mode && !afl->non_instrumented_mode && - !memmem(f_data, f_len, SHM_ENV_VAR, strlen(SHM_ENV_VAR) + 1)) { + !afl_memmem(f_data, f_len, SHM_ENV_VAR, strlen(SHM_ENV_VAR) + 1)) { SAYF("\n" cLRD "[-] " cRST "Looks like the target binary is not instrumented! The fuzzer depends " @@ -2793,7 +2819,7 @@ void check_binary(afl_state_t *afl, u8 *fname) { } if ((afl->fsrv.cs_mode || afl->fsrv.qemu_mode || afl->fsrv.frida_mode) && - memmem(f_data, f_len, SHM_ENV_VAR, strlen(SHM_ENV_VAR) + 1)) { + afl_memmem(f_data, f_len, SHM_ENV_VAR, strlen(SHM_ENV_VAR) + 1)) { SAYF("\n" cLRD "[-] " cRST "This program appears to be instrumented with afl-gcc, but is being " @@ -2806,9 +2832,9 @@ void check_binary(afl_state_t *afl, u8 *fname) { } - if (memmem(f_data, f_len, "__asan_init", 11) || - memmem(f_data, f_len, "__msan_init", 11) || - memmem(f_data, f_len, "__lsan_init", 11)) { + if (afl_memmem(f_data, f_len, "__asan_init", 11) || + afl_memmem(f_data, f_len, "__msan_init", 11) || + afl_memmem(f_data, f_len, "__lsan_init", 11)) { afl->fsrv.uses_asan = 1; @@ -2816,7 +2842,7 @@ void check_binary(afl_state_t *afl, u8 *fname) { /* Detect persistent & deferred init signatures in the binary. */ - if (memmem(f_data, f_len, PERSIST_SIG, strlen(PERSIST_SIG) + 1)) { + if (afl_memmem(f_data, f_len, PERSIST_SIG, strlen(PERSIST_SIG) + 1)) { OKF(cPIN "Persistent mode binary detected."); setenv(PERSIST_ENV_VAR, "1", 1); @@ -2843,7 +2869,7 @@ void check_binary(afl_state_t *afl, u8 *fname) { } if (afl->fsrv.frida_mode || - memmem(f_data, f_len, DEFER_SIG, strlen(DEFER_SIG) + 1)) { + afl_memmem(f_data, f_len, DEFER_SIG, strlen(DEFER_SIG) + 1)) { OKF(cPIN "Deferred forkserver binary detected."); setenv(DEFER_ENV_VAR, "1", 1); @@ -2899,8 +2925,11 @@ void setup_signal_handlers(void) { struct sigaction sa; + memset((void *)&sa, 0, sizeof(sa)); sa.sa_handler = NULL; +#ifdef SA_RESTART sa.sa_flags = SA_RESTART; +#endif sa.sa_sigaction = NULL; sigemptyset(&sa.sa_mask); diff --git a/src/afl-fuzz-mutators.c b/src/afl-fuzz-mutators.c index b9daebfa..64dbe7c6 100644 --- a/src/afl-fuzz-mutators.c +++ b/src/afl-fuzz-mutators.c @@ -10,7 +10,7 @@ Dominik Maier <mail@dmnk.co> Copyright 2016, 2017 Google Inc. All rights reserved. - Copyright 2019-2022 AFLplusplus Project. All rights reserved. + Copyright 2019-2023 AFLplusplus Project. All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -179,11 +179,19 @@ struct custom_mutator *load_custom_mutator(afl_state_t *afl, const char *fn) { void *dh; struct custom_mutator *mutator = ck_alloc(sizeof(struct custom_mutator)); - mutator->name = fn; - if (memchr(fn, '/', strlen(fn))) - mutator->name_short = strrchr(fn, '/') + 1; - else + if (memchr(fn, '/', strlen(fn))) { + + mutator->name_short = strdup(strrchr(fn, '/') + 1); + + } else { + mutator->name_short = strdup(fn); + + } + + if (strlen(mutator->name_short) > 22) { mutator->name_short[21] = 0; } + + mutator->name = fn; ACTF("Loading custom mutator library from '%s'...", fn); dh = dlopen(fn, RTLD_NOW); @@ -211,8 +219,16 @@ struct custom_mutator *load_custom_mutator(afl_state_t *afl, const char *fn) { WARNF("Symbol 'afl_custom_mutator' not found."); + } else { + + OKF("Found 'afl_custom_mutator'."); + } + } else { + + OKF("Found 'afl_custom_mutator'."); + } /* "afl_custom_introspection", optional */ @@ -222,6 +238,10 @@ struct custom_mutator *load_custom_mutator(afl_state_t *afl, const char *fn) { ACTF("optional symbol 'afl_custom_introspection' not found."); + } else { + + OKF("Found 'afl_custom_introspection'."); + } #endif @@ -232,6 +252,10 @@ struct custom_mutator *load_custom_mutator(afl_state_t *afl, const char *fn) { ACTF("optional symbol 'afl_custom_fuzz_count' not found."); + } else { + + OKF("Found 'afl_custom_fuzz_count'."); + } /* "afl_custom_deinit", optional for backward compatibility */ @@ -248,6 +272,10 @@ struct custom_mutator *load_custom_mutator(afl_state_t *afl, const char *fn) { ACTF("optional symbol 'afl_custom_post_process' not found."); + } else { + + OKF("Found 'afl_custom_post_process'."); + } u8 notrim = 0; @@ -258,6 +286,10 @@ struct custom_mutator *load_custom_mutator(afl_state_t *afl, const char *fn) { notrim = 1; ACTF("optional symbol 'afl_custom_init_trim' not found."); + } else { + + OKF("Found 'afl_custom_init_trim'."); + } /* "afl_custom_trim", optional */ @@ -267,6 +299,10 @@ struct custom_mutator *load_custom_mutator(afl_state_t *afl, const char *fn) { notrim = 1; ACTF("optional symbol 'afl_custom_trim' not found."); + } else { + + OKF("Found 'afl_custom_trim'."); + } /* "afl_custom_post_trim", optional */ @@ -276,16 +312,26 @@ struct custom_mutator *load_custom_mutator(afl_state_t *afl, const char *fn) { notrim = 1; ACTF("optional symbol 'afl_custom_post_trim' not found."); + } else { + + OKF("Found 'afl_custom_post_trim'."); + } if (notrim) { + if (mutator->afl_custom_init_trim || mutator->afl_custom_trim || + mutator->afl_custom_post_trim) { + + WARNF( + "Custom mutator does not implement all three trim APIs, standard " + "trimming will be used."); + + } + mutator->afl_custom_init_trim = NULL; mutator->afl_custom_trim = NULL; mutator->afl_custom_post_trim = NULL; - ACTF( - "Custom mutator does not implement all three trim APIs, standard " - "trimming will be used."); } @@ -295,6 +341,10 @@ struct custom_mutator *load_custom_mutator(afl_state_t *afl, const char *fn) { ACTF("optional symbol 'afl_custom_havoc_mutation' not found."); + } else { + + OKF("Found 'afl_custom_havoc_mutation'."); + } /* "afl_custom_havoc_mutation", optional */ @@ -304,6 +354,10 @@ struct custom_mutator *load_custom_mutator(afl_state_t *afl, const char *fn) { ACTF("optional symbol 'afl_custom_havoc_mutation_probability' not found."); + } else { + + OKF("Found 'afl_custom_havoc_mutation_probability'."); + } /* "afl_custom_queue_get", optional */ @@ -312,6 +366,35 @@ struct custom_mutator *load_custom_mutator(afl_state_t *afl, const char *fn) { ACTF("optional symbol 'afl_custom_queue_get' not found."); + } else { + + OKF("Found 'afl_custom_queue_get'."); + + } + + /* "afl_custom_splice_optout", optional, never called */ + mutator->afl_custom_splice_optout = dlsym(dh, "afl_custom_splice_optout"); + if (!mutator->afl_custom_splice_optout) { + + ACTF("optional symbol 'afl_custom_splice_optout' not found."); + + } else { + + OKF("Found 'afl_custom_splice_optout'."); + afl->custom_splice_optout = 1; + + } + + /* "afl_custom_fuzz_send", optional */ + mutator->afl_custom_fuzz_send = dlsym(dh, "afl_custom_fuzz_send"); + if (!mutator->afl_custom_fuzz_send) { + + ACTF("optional symbol 'afl_custom_fuzz_send' not found."); + + } else { + + OKF("Found 'afl_custom_fuzz_send'."); + } /* "afl_custom_queue_new_entry", optional */ @@ -320,13 +403,21 @@ struct custom_mutator *load_custom_mutator(afl_state_t *afl, const char *fn) { ACTF("optional symbol 'afl_custom_queue_new_entry' not found"); + } else { + + OKF("Found 'afl_custom_queue_new_entry'."); + } /* "afl_custom_describe", optional */ mutator->afl_custom_describe = dlsym(dh, "afl_custom_describe"); if (!mutator->afl_custom_describe) { - ACTF("Symbol 'afl_custom_describe' not found."); + ACTF("optional symbol 'afl_custom_describe' not found."); + + } else { + + OKF("Found 'afl_custom_describe'."); } diff --git a/src/afl-fuzz-one.c b/src/afl-fuzz-one.c index ed9e7a81..ee562f96 100644 --- a/src/afl-fuzz-one.c +++ b/src/afl-fuzz-one.c @@ -9,7 +9,7 @@ Andrea Fioraldi <andreafioraldi@gmail.com> Copyright 2016, 2017 Google Inc. All rights reserved. - Copyright 2019-2022 AFLplusplus Project. All rights reserved. + Copyright 2019-2023 AFLplusplus Project. All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -446,9 +446,12 @@ u8 fuzz_one_original(afl_state_t *afl) { ACTF( "Fuzzing test case #%u (%u total, %llu crashes saved, " - "perf_score=%0.0f, exec_us=%llu, hits=%u, map=%u, ascii=%u)...", + "perf_score=%0.0f, weight=%0.0f, favorite=%u, was_fuzzed=%u, " + "exec_us=%llu, hits=%u, map=%u, ascii=%u)...", afl->current_entry, afl->queued_items, afl->saved_crashes, - afl->queue_cur->perf_score, afl->queue_cur->exec_us, + afl->queue_cur->perf_score, afl->queue_cur->weight, + afl->queue_cur->favored, afl->queue_cur->was_fuzzed, + afl->queue_cur->exec_us, likely(afl->n_fuzz) ? afl->n_fuzz[afl->queue_cur->n_fuzz_entry] : 0, afl->queue_cur->bitmap_size, afl->queue_cur->is_ascii); fflush(stdout); @@ -561,11 +564,11 @@ u8 fuzz_one_original(afl_state_t *afl) { } else { - if (afl->cmplog_lvl == 3 || - (afl->cmplog_lvl == 2 && afl->queue_cur->tc_ref) || - afl->queue_cur->favored || - !(afl->fsrv.total_execs % afl->queued_items) || - get_cur_time() - afl->last_find_time > 300000) { // 300 seconds + if (afl->queue_cur->favored || afl->cmplog_lvl == 3 || + (afl->cmplog_lvl == 2 && + (afl->queue_cur->tc_ref || + afl->fsrv.total_execs % afl->queued_items <= 10)) || + get_cur_time() - afl->last_find_time > 250000) { // 250 seconds if (input_to_state_stage(afl, in_buf, out_buf, len)) { @@ -584,7 +587,7 @@ u8 fuzz_one_original(afl_state_t *afl) { if it has gone through deterministic testing in earlier, resumed runs (passed_det). */ - if (likely(afl->queue_cur->passed_det) || likely(afl->skip_deterministic) || + if (likely(afl->skip_deterministic) || likely(afl->queue_cur->passed_det) || likely(perf_score < (afl->queue_cur->depth * 30 <= afl->havoc_max_mult * 100 ? afl->queue_cur->depth * 30 @@ -743,6 +746,9 @@ u8 fuzz_one_original(afl_state_t *afl) { afl->stage_finds[STAGE_FLIP1] += new_hit_cnt - orig_hit_cnt; afl->stage_cycles[STAGE_FLIP1] += afl->stage_max; +#ifdef INTROSPECTION + afl->queue_cur->stats_mutated += afl->stage_max; +#endif /* Two walking bits. */ @@ -775,6 +781,9 @@ u8 fuzz_one_original(afl_state_t *afl) { afl->stage_finds[STAGE_FLIP2] += new_hit_cnt - orig_hit_cnt; afl->stage_cycles[STAGE_FLIP2] += afl->stage_max; +#ifdef INTROSPECTION + afl->queue_cur->stats_mutated += afl->stage_max; +#endif /* Four walking bits. */ @@ -811,6 +820,9 @@ u8 fuzz_one_original(afl_state_t *afl) { afl->stage_finds[STAGE_FLIP4] += new_hit_cnt - orig_hit_cnt; afl->stage_cycles[STAGE_FLIP4] += afl->stage_max; +#ifdef INTROSPECTION + afl->queue_cur->stats_mutated += afl->stage_max; +#endif /* Effector map setup. These macros calculate: @@ -919,6 +931,9 @@ u8 fuzz_one_original(afl_state_t *afl) { afl->stage_finds[STAGE_FLIP8] += new_hit_cnt - orig_hit_cnt; afl->stage_cycles[STAGE_FLIP8] += afl->stage_max; +#ifdef INTROSPECTION + afl->queue_cur->stats_mutated += afl->stage_max; +#endif /* Two walking bytes. */ @@ -962,6 +977,9 @@ u8 fuzz_one_original(afl_state_t *afl) { afl->stage_finds[STAGE_FLIP16] += new_hit_cnt - orig_hit_cnt; afl->stage_cycles[STAGE_FLIP16] += afl->stage_max; +#ifdef INTROSPECTION + afl->queue_cur->stats_mutated += afl->stage_max; +#endif if (len < 4) { goto skip_bitflip; } @@ -1005,6 +1023,9 @@ u8 fuzz_one_original(afl_state_t *afl) { afl->stage_finds[STAGE_FLIP32] += new_hit_cnt - orig_hit_cnt; afl->stage_cycles[STAGE_FLIP32] += afl->stage_max; +#ifdef INTROSPECTION + afl->queue_cur->stats_mutated += afl->stage_max; +#endif skip_bitflip: @@ -1097,6 +1118,9 @@ skip_bitflip: afl->stage_finds[STAGE_ARITH8] += new_hit_cnt - orig_hit_cnt; afl->stage_cycles[STAGE_ARITH8] += afl->stage_max; +#ifdef INTROSPECTION + afl->queue_cur->stats_mutated += afl->stage_max; +#endif /* 16-bit arithmetics, both endians. */ @@ -1227,6 +1251,9 @@ skip_bitflip: afl->stage_finds[STAGE_ARITH16] += new_hit_cnt - orig_hit_cnt; afl->stage_cycles[STAGE_ARITH16] += afl->stage_max; +#ifdef INTROSPECTION + afl->queue_cur->stats_mutated += afl->stage_max; +#endif /* 32-bit arithmetics, both endians. */ @@ -1356,6 +1383,9 @@ skip_bitflip: afl->stage_finds[STAGE_ARITH32] += new_hit_cnt - orig_hit_cnt; afl->stage_cycles[STAGE_ARITH32] += afl->stage_max; +#ifdef INTROSPECTION + afl->queue_cur->stats_mutated += afl->stage_max; +#endif skip_arith: @@ -1422,6 +1452,9 @@ skip_arith: afl->stage_finds[STAGE_INTEREST8] += new_hit_cnt - orig_hit_cnt; afl->stage_cycles[STAGE_INTEREST8] += afl->stage_max; +#ifdef INTROSPECTION + afl->queue_cur->stats_mutated += afl->stage_max; +#endif /* Setting 16-bit integers, both endians. */ @@ -1510,6 +1543,9 @@ skip_arith: afl->stage_finds[STAGE_INTEREST16] += new_hit_cnt - orig_hit_cnt; afl->stage_cycles[STAGE_INTEREST16] += afl->stage_max; +#ifdef INTROSPECTION + afl->queue_cur->stats_mutated += afl->stage_max; +#endif if (len < 4) { goto skip_interest; } @@ -1599,6 +1635,9 @@ skip_arith: afl->stage_finds[STAGE_INTEREST32] += new_hit_cnt - orig_hit_cnt; afl->stage_cycles[STAGE_INTEREST32] += afl->stage_max; +#ifdef INTROSPECTION + afl->queue_cur->stats_mutated += afl->stage_max; +#endif skip_interest: @@ -1672,6 +1711,9 @@ skip_interest: afl->stage_finds[STAGE_EXTRAS_UO] += new_hit_cnt - orig_hit_cnt; afl->stage_cycles[STAGE_EXTRAS_UO] += afl->stage_max; +#ifdef INTROSPECTION + afl->queue_cur->stats_mutated += afl->stage_max; +#endif /* Insertion of user-supplied extras. */ @@ -1728,6 +1770,9 @@ skip_interest: afl->stage_finds[STAGE_EXTRAS_UI] += new_hit_cnt - orig_hit_cnt; afl->stage_cycles[STAGE_EXTRAS_UI] += afl->stage_max; +#ifdef INTROSPECTION + afl->queue_cur->stats_mutated += afl->stage_max; +#endif skip_user_extras: @@ -1786,6 +1831,9 @@ skip_user_extras: afl->stage_finds[STAGE_EXTRAS_AO] += new_hit_cnt - orig_hit_cnt; afl->stage_cycles[STAGE_EXTRAS_AO] += afl->stage_max; +#ifdef INTROSPECTION + afl->queue_cur->stats_mutated += afl->stage_max; +#endif /* Insertion of auto extras. */ @@ -1842,6 +1890,9 @@ skip_user_extras: afl->stage_finds[STAGE_EXTRAS_AI] += new_hit_cnt - orig_hit_cnt; afl->stage_cycles[STAGE_EXTRAS_AI] += afl->stage_max; +#ifdef INTROSPECTION + afl->queue_cur->stats_mutated += afl->stage_max; +#endif skip_extras: @@ -1860,9 +1911,10 @@ custom_mutator_stage: afl->stage_name = "custom mutator"; afl->stage_short = "custom"; - afl->stage_max = HAVOC_CYCLES * perf_score / afl->havoc_div / 100; afl->stage_val_type = STAGE_VAL_NONE; bool has_custom_fuzz = false; + u32 shift = unlikely(afl->custom_only) ? 7 : 8; + afl->stage_max = (HAVOC_CYCLES * perf_score / afl->havoc_div) >> shift; if (afl->stage_max < HAVOC_MIN) { afl->stage_max = HAVOC_MIN; } @@ -1879,6 +1931,7 @@ custom_mutator_stage: if (el->afl_custom_fuzz) { afl->current_custom_fuzz = el; + afl->stage_name = el->name_short; if (el->afl_custom_fuzz_count) { @@ -1905,7 +1958,8 @@ custom_mutator_stage: u32 target_len = 0; /* check if splicing makes sense yet (enough entries) */ - if (likely(afl->ready_for_splicing_count > 1)) { + if (likely(!afl->custom_splice_optout && + afl->ready_for_splicing_count > 1)) { /* Pick a random other queue entry for passing to external API that has the necessary length */ @@ -1935,7 +1989,8 @@ custom_mutator_stage: if (unlikely(!mutated_buf)) { - FATAL("Error in custom_fuzz. Size returned: %zu", mutated_size); + // FATAL("Error in custom_fuzz. Size returned: %zu", mutated_size); + break; } @@ -1987,7 +2042,10 @@ custom_mutator_stage: new_hit_cnt = afl->queued_items + afl->saved_crashes; afl->stage_finds[STAGE_CUSTOM_MUTATOR] += new_hit_cnt - orig_hit_cnt; - afl->stage_cycles[STAGE_CUSTOM_MUTATOR] += afl->stage_max; + afl->stage_cycles[STAGE_CUSTOM_MUTATOR] += afl->stage_cur; +#ifdef INTROSPECTION + afl->queue_cur->stats_mutated += afl->stage_max; +#endif if (likely(afl->custom_only)) { @@ -2012,8 +2070,9 @@ havoc_stage: afl->stage_name = "havoc"; afl->stage_short = "havoc"; - afl->stage_max = (doing_det ? HAVOC_CYCLES_INIT : HAVOC_CYCLES) * - perf_score / afl->havoc_div / 100; + afl->stage_max = ((doing_det ? HAVOC_CYCLES_INIT : HAVOC_CYCLES) * + perf_score / afl->havoc_div) >> + 8; } else { @@ -2022,11 +2081,11 @@ havoc_stage: snprintf(afl->stage_name_buf, STAGE_BUF_SIZE, "splice %u", splice_cycle); afl->stage_name = afl->stage_name_buf; afl->stage_short = "splice"; - afl->stage_max = SPLICE_HAVOC * perf_score / afl->havoc_div / 100; + afl->stage_max = (SPLICE_HAVOC * perf_score / afl->havoc_div) >> 8; } - if (afl->stage_max < HAVOC_MIN) { afl->stage_max = HAVOC_MIN; } + if (unlikely(afl->stage_max < HAVOC_MIN)) { afl->stage_max = HAVOC_MIN; } temp_len = len; @@ -2925,11 +2984,17 @@ havoc_stage: afl->stage_finds[STAGE_HAVOC] += new_hit_cnt - orig_hit_cnt; afl->stage_cycles[STAGE_HAVOC] += afl->stage_max; +#ifdef INTROSPECTION + afl->queue_cur->stats_mutated += afl->stage_max; +#endif } else { afl->stage_finds[STAGE_SPLICE] += new_hit_cnt - orig_hit_cnt; afl->stage_cycles[STAGE_SPLICE] += afl->stage_max; +#ifdef INTROSPECTION + afl->queue_cur->stats_mutated += afl->stage_max; +#endif } @@ -3411,6 +3476,9 @@ static u8 mopt_common_fuzzing(afl_state_t *afl, MOpt_globals_t MOpt_globals) { afl->stage_finds[STAGE_FLIP1] += new_hit_cnt - orig_hit_cnt; afl->stage_cycles[STAGE_FLIP1] += afl->stage_max; +#ifdef INTROSPECTION + afl->queue_cur->stats_mutated += afl->stage_max; +#endif /* Two walking bits. */ @@ -3442,6 +3510,9 @@ static u8 mopt_common_fuzzing(afl_state_t *afl, MOpt_globals_t MOpt_globals) { afl->stage_finds[STAGE_FLIP2] += new_hit_cnt - orig_hit_cnt; afl->stage_cycles[STAGE_FLIP2] += afl->stage_max; +#ifdef INTROSPECTION + afl->queue_cur->stats_mutated += afl->stage_max; +#endif /* Four walking bits. */ @@ -3477,6 +3548,9 @@ static u8 mopt_common_fuzzing(afl_state_t *afl, MOpt_globals_t MOpt_globals) { afl->stage_finds[STAGE_FLIP4] += new_hit_cnt - orig_hit_cnt; afl->stage_cycles[STAGE_FLIP4] += afl->stage_max; +#ifdef INTROSPECTION + afl->queue_cur->stats_mutated += afl->stage_max; +#endif /* Effector map setup. These macros calculate: @@ -3584,6 +3658,9 @@ static u8 mopt_common_fuzzing(afl_state_t *afl, MOpt_globals_t MOpt_globals) { afl->stage_finds[STAGE_FLIP8] += new_hit_cnt - orig_hit_cnt; afl->stage_cycles[STAGE_FLIP8] += afl->stage_max; +#ifdef INTROSPECTION + afl->queue_cur->stats_mutated += afl->stage_max; +#endif /* Two walking bytes. */ @@ -3626,6 +3703,9 @@ static u8 mopt_common_fuzzing(afl_state_t *afl, MOpt_globals_t MOpt_globals) { afl->stage_finds[STAGE_FLIP16] += new_hit_cnt - orig_hit_cnt; afl->stage_cycles[STAGE_FLIP16] += afl->stage_max; +#ifdef INTROSPECTION + afl->queue_cur->stats_mutated += afl->stage_max; +#endif if (len < 4) { goto skip_bitflip; } @@ -3668,6 +3748,9 @@ static u8 mopt_common_fuzzing(afl_state_t *afl, MOpt_globals_t MOpt_globals) { afl->stage_finds[STAGE_FLIP32] += new_hit_cnt - orig_hit_cnt; afl->stage_cycles[STAGE_FLIP32] += afl->stage_max; +#ifdef INTROSPECTION + afl->queue_cur->stats_mutated += afl->stage_max; +#endif skip_bitflip: @@ -3758,6 +3841,9 @@ skip_bitflip: afl->stage_finds[STAGE_ARITH8] += new_hit_cnt - orig_hit_cnt; afl->stage_cycles[STAGE_ARITH8] += afl->stage_max; +#ifdef INTROSPECTION + afl->queue_cur->stats_mutated += afl->stage_max; +#endif /* 16-bit arithmetics, both endians. */ @@ -3884,6 +3970,9 @@ skip_bitflip: afl->stage_finds[STAGE_ARITH16] += new_hit_cnt - orig_hit_cnt; afl->stage_cycles[STAGE_ARITH16] += afl->stage_max; +#ifdef INTROSPECTION + afl->queue_cur->stats_mutated += afl->stage_max; +#endif /* 32-bit arithmetics, both endians. */ @@ -4009,6 +4098,9 @@ skip_bitflip: afl->stage_finds[STAGE_ARITH32] += new_hit_cnt - orig_hit_cnt; afl->stage_cycles[STAGE_ARITH32] += afl->stage_max; +#ifdef INTROSPECTION + afl->queue_cur->stats_mutated += afl->stage_max; +#endif skip_arith: @@ -4074,6 +4166,9 @@ skip_arith: afl->stage_finds[STAGE_INTEREST8] += new_hit_cnt - orig_hit_cnt; afl->stage_cycles[STAGE_INTEREST8] += afl->stage_max; +#ifdef INTROSPECTION + afl->queue_cur->stats_mutated += afl->stage_max; +#endif /* Setting 16-bit integers, both endians. */ @@ -4160,6 +4255,9 @@ skip_arith: afl->stage_finds[STAGE_INTEREST16] += new_hit_cnt - orig_hit_cnt; afl->stage_cycles[STAGE_INTEREST16] += afl->stage_max; +#ifdef INTROSPECTION + afl->queue_cur->stats_mutated += afl->stage_max; +#endif if (len < 4) { goto skip_interest; } @@ -4247,6 +4345,9 @@ skip_arith: afl->stage_finds[STAGE_INTEREST32] += new_hit_cnt - orig_hit_cnt; afl->stage_cycles[STAGE_INTEREST32] += afl->stage_max; +#ifdef INTROSPECTION + afl->queue_cur->stats_mutated += afl->stage_max; +#endif skip_interest: @@ -4320,6 +4421,9 @@ skip_interest: afl->stage_finds[STAGE_EXTRAS_UO] += new_hit_cnt - orig_hit_cnt; afl->stage_cycles[STAGE_EXTRAS_UO] += afl->stage_max; +#ifdef INTROSPECTION + afl->queue_cur->stats_mutated += afl->stage_max; +#endif /* Insertion of user-supplied extras. */ @@ -4376,6 +4480,9 @@ skip_interest: afl->stage_finds[STAGE_EXTRAS_UI] += new_hit_cnt - orig_hit_cnt; afl->stage_cycles[STAGE_EXTRAS_UI] += afl->stage_max; +#ifdef INTROSPECTION + afl->queue_cur->stats_mutated += afl->stage_max; +#endif skip_user_extras: @@ -4435,6 +4542,9 @@ skip_user_extras: afl->stage_finds[STAGE_EXTRAS_AO] += new_hit_cnt - orig_hit_cnt; afl->stage_cycles[STAGE_EXTRAS_AO] += afl->stage_max; +#ifdef INTROSPECTION + afl->queue_cur->stats_mutated += afl->stage_max; +#endif /* Insertion of auto extras. */ @@ -4491,6 +4601,9 @@ skip_user_extras: afl->stage_finds[STAGE_EXTRAS_AI] += new_hit_cnt - orig_hit_cnt; afl->stage_cycles[STAGE_EXTRAS_AI] += afl->stage_max; +#ifdef INTROSPECTION + afl->queue_cur->stats_mutated += afl->stage_max; +#endif skip_extras: @@ -4516,8 +4629,9 @@ pacemaker_fuzzing: afl->stage_name = MOpt_globals.havoc_stagename; afl->stage_short = MOpt_globals.havoc_stagenameshort; - afl->stage_max = (doing_det ? HAVOC_CYCLES_INIT : HAVOC_CYCLES) * - perf_score / afl->havoc_div / 100; + afl->stage_max = ((doing_det ? HAVOC_CYCLES_INIT : HAVOC_CYCLES) * + perf_score / afl->havoc_div) >> + 7; } else { @@ -4527,7 +4641,7 @@ pacemaker_fuzzing: MOpt_globals.splice_stageformat, splice_cycle); afl->stage_name = afl->stage_name_buf; afl->stage_short = MOpt_globals.splice_stagenameshort; - afl->stage_max = SPLICE_HAVOC * perf_score / afl->havoc_div / 100; + afl->stage_max = (SPLICE_HAVOC * perf_score / afl->havoc_div) >> 8; } @@ -5316,11 +5430,17 @@ pacemaker_fuzzing: afl->stage_finds[STAGE_HAVOC] += new_hit_cnt - orig_hit_cnt; afl->stage_cycles[STAGE_HAVOC] += afl->stage_max; +#ifdef INTROSPECTION + afl->queue_cur->stats_mutated += afl->stage_max; +#endif } else { afl->stage_finds[STAGE_SPLICE] += new_hit_cnt - orig_hit_cnt; afl->stage_cycles[STAGE_SPLICE] += afl->stage_max; +#ifdef INTROSPECTION + afl->queue_cur->stats_mutated += afl->stage_max; +#endif } @@ -5572,6 +5692,7 @@ pacemaker_fuzzing: } /* block */ + ++afl->queue_cur->fuzz_level; return ret_val; } @@ -5681,13 +5802,11 @@ void pso_updating(afl_state_t *afl) { } -/* larger change for MOpt implementation: the original fuzz_one was renamed - to fuzz_one_original. All documentation references to fuzz_one therefore - mean fuzz_one_original */ - +/* The entry point for the mutator, choosing the default mutator, and/or MOpt + depending on the configuration. */ u8 fuzz_one(afl_state_t *afl) { - int key_val_lv_1 = 0, key_val_lv_2 = 0; + int key_val_lv_1 = -1, key_val_lv_2 = -1; #ifdef _AFL_DOCUMENT_MUTATIONS @@ -5707,7 +5826,12 @@ u8 fuzz_one(afl_state_t *afl) { #endif - // if limit_time_sig == -1 then both are run after each other + /* + -L command line paramter => limit_time_sig value + limit_time_sig == 0 then run the default mutator + limit_time_sig > 0 then run MOpt + limit_time_sig < 0 both are run + */ if (afl->limit_time_sig <= 0) { key_val_lv_1 = fuzz_one_original(afl); } @@ -5729,6 +5853,9 @@ u8 fuzz_one(afl_state_t *afl) { } + if (unlikely(key_val_lv_1 == -1)) { key_val_lv_1 = 0; } + if (likely(key_val_lv_2 == -1)) { key_val_lv_2 = 0; } + return (key_val_lv_1 | key_val_lv_2); } diff --git a/src/afl-fuzz-python.c b/src/afl-fuzz-python.c index a43d80bb..7dad0770 100644 --- a/src/afl-fuzz-python.c +++ b/src/afl-fuzz-python.c @@ -9,7 +9,7 @@ Andrea Fioraldi <andreafioraldi@gmail.com> Copyright 2016, 2017 Google Inc. All rights reserved. - Copyright 2019-2022 AFLplusplus Project. All rights reserved. + Copyright 2019-2023 AFLplusplus Project. All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -219,11 +219,14 @@ static py_mutator_t *init_py_module(afl_state_t *afl, u8 *module_name) { if (py_module != NULL) { - u8 py_notrim = 0, py_idx; - /* init, required */ + u8 py_notrim = 0; py_functions[PY_FUNC_INIT] = PyObject_GetAttrString(py_module, "init"); - if (!py_functions[PY_FUNC_INIT]) - FATAL("init function not found in python module"); + if (!py_functions[PY_FUNC_INIT]) { + + WARNF("init function not found in python module"); + + } + py_functions[PY_FUNC_FUZZ] = PyObject_GetAttrString(py_module, "fuzz"); if (!py_functions[PY_FUNC_FUZZ]) py_functions[PY_FUNC_FUZZ] = PyObject_GetAttrString(py_module, "mutate"); @@ -231,8 +234,6 @@ static py_mutator_t *init_py_module(afl_state_t *afl, u8 *module_name) { PyObject_GetAttrString(py_module, "describe"); py_functions[PY_FUNC_FUZZ_COUNT] = PyObject_GetAttrString(py_module, "fuzz_count"); - if (!py_functions[PY_FUNC_FUZZ]) - WARNF("fuzz function not found in python module"); py_functions[PY_FUNC_POST_PROCESS] = PyObject_GetAttrString(py_module, "post_process"); py_functions[PY_FUNC_INIT_TRIM] = @@ -246,6 +247,11 @@ static py_mutator_t *init_py_module(afl_state_t *afl, u8 *module_name) { PyObject_GetAttrString(py_module, "havoc_mutation_probability"); py_functions[PY_FUNC_QUEUE_GET] = PyObject_GetAttrString(py_module, "queue_get"); + py_functions[PY_FUNC_FUZZ_SEND] = + PyObject_GetAttrString(py_module, "fuzz_send"); + py_functions[PY_FUNC_SPLICE_OPTOUT] = + PyObject_GetAttrString(py_module, "splice_optout"); + if (py_functions[PY_FUNC_SPLICE_OPTOUT]) { afl->custom_splice_optout = 1; } py_functions[PY_FUNC_QUEUE_NEW_ENTRY] = PyObject_GetAttrString(py_module, "queue_new_entry"); py_functions[PY_FUNC_INTROSPECTION] = @@ -254,36 +260,6 @@ static py_mutator_t *init_py_module(afl_state_t *afl, u8 *module_name) { if (!py_functions[PY_FUNC_DEINIT]) WARNF("deinit function not found in python module"); - for (py_idx = 0; py_idx < PY_FUNC_COUNT; ++py_idx) { - - if (!py_functions[py_idx] || !PyCallable_Check(py_functions[py_idx])) { - - if (py_idx >= PY_FUNC_INIT_TRIM && py_idx <= PY_FUNC_TRIM) { - - // Implementing the trim API is optional for now - if (PyErr_Occurred()) { PyErr_Print(); } - py_notrim = 1; - - } else if (py_idx >= PY_OPTIONAL) { - - // Only _init and _deinit are not optional currently - - if (PyErr_Occurred()) { PyErr_Print(); } - - } else { - - fprintf(stderr, - "Cannot find/call function with index %d in external " - "Python module.\n", - py_idx); - return NULL; - - } - - } - - } - if (py_notrim) { py_functions[PY_FUNC_INIT_TRIM] = NULL; @@ -336,6 +312,8 @@ static void init_py(afl_state_t *afl, py_mutator_t *py_mutator, (void)afl; + if (py_mutator->py_functions[PY_FUNC_INIT] == NULL) { return; } + PyObject *py_args, *py_value; /* Provide the init function a seed for the Python RNG */ @@ -392,16 +370,34 @@ void deinit_py(void *py_mutator) { } +void splice_optout_py(void *py_mutator) { + + // this is never called + (void)(py_mutator); + +} + struct custom_mutator *load_custom_mutator_py(afl_state_t *afl, char *module_name) { struct custom_mutator *mutator; mutator = ck_alloc(sizeof(struct custom_mutator)); - mutator->name = module_name; ACTF("Loading Python mutator library from '%s'...", module_name); + if (memchr(module_name, '/', strlen(module_name))) { + + mutator->name_short = strdup(strrchr(module_name, '/') + 1); + + } else { + + mutator->name_short = strdup(module_name); + + } + + if (strlen(mutator->name_short) > 22) { mutator->name_short[21] = 0; } + py_mutator_t *py_mutator; py_mutator = init_py_module(afl, module_name); mutator->data = py_mutator; @@ -466,6 +462,19 @@ struct custom_mutator *load_custom_mutator_py(afl_state_t *afl, } + if (py_functions[PY_FUNC_FUZZ_SEND]) { + + mutator->afl_custom_fuzz_send = fuzz_send_py; + + } + + if (py_functions[PY_FUNC_SPLICE_OPTOUT]) { + + mutator->afl_custom_splice_optout = splice_optout_py; + afl->custom_splice_optout = 1; + + } + if (py_functions[PY_FUNC_QUEUE_NEW_ENTRY]) { mutator->afl_custom_queue_new_entry = queue_new_entry_py; @@ -893,6 +902,29 @@ u8 queue_get_py(void *py_mutator, const u8 *filename) { } +void fuzz_send_py(void *py_mutator, const u8 *buf, size_t buf_size) { + + PyObject *py_args, *py_value; + + py_args = PyTuple_New(1); + py_value = PyByteArray_FromStringAndSize(buf, buf_size); + if (!py_value) { + + Py_DECREF(py_args); + FATAL("Failed to convert arguments"); + + } + + PyTuple_SetItem(py_args, 0, py_value); + + py_value = PyObject_CallObject( + ((py_mutator_t *)py_mutator)->py_functions[PY_FUNC_FUZZ_SEND], py_args); + Py_DECREF(py_args); + + if (py_value != NULL) { Py_DECREF(py_value); } + +} + u8 queue_new_entry_py(void *py_mutator, const u8 *filename_new_queue, const u8 *filename_orig_queue) { diff --git a/src/afl-fuzz-queue.c b/src/afl-fuzz-queue.c index d8dbdfbe..8ad7cd97 100644 --- a/src/afl-fuzz-queue.c +++ b/src/afl-fuzz-queue.c @@ -9,7 +9,7 @@ Andrea Fioraldi <andreafioraldi@gmail.com> Copyright 2016, 2017 Google Inc. All rights reserved. - Copyright 2019-2022 AFLplusplus Project. All rights reserved. + Copyright 2019-2023 AFLplusplus Project. All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at: @@ -27,6 +27,22 @@ #include <ctype.h> #include <math.h> +#ifdef _STANDALONE_MODULE +void minimize_bits(afl_state_t *afl, u8 *dst, u8 *src) { + + return; + +} + +void run_afl_custom_queue_new_entry(afl_state_t *afl, struct queue_entry *q, + u8 *a, u8 *b) { + + return; + +} + +#endif + /* select next queue entry based on alias algo - fast! */ inline u32 select_next_queue_entry(afl_state_t *afl) { @@ -51,13 +67,15 @@ double compute_weight(afl_state_t *afl, struct queue_entry *q, if (likely(afl->schedule >= FAST && afl->schedule <= RARE)) { u32 hits = afl->n_fuzz[q->n_fuzz_entry]; - if (likely(hits)) { weight *= log10(hits) + 1; } + if (likely(hits)) { weight /= (log10(hits) + 1); } } if (likely(afl->schedule < RARE)) { weight *= (avg_exec_us / q->exec_us); } weight *= (log(q->bitmap_size) / avg_bitmap_size); weight *= (1 + (q->tc_ref / avg_top_size)); + + if (unlikely(weight < 0.1)) { weight = 0.1; } if (unlikely(q->favored)) { weight *= 5; } if (unlikely(!q->was_fuzzed)) { weight *= 2; } @@ -77,8 +95,8 @@ void create_alias_table(afl_state_t *afl) { afl->alias_probability = (double *)afl_realloc( (void **)&afl->alias_probability, n * sizeof(double)); double *P = (double *)afl_realloc(AFL_BUF_PARAM(out), n * sizeof(double)); - int *S = (u32 *)afl_realloc(AFL_BUF_PARAM(out_scratch), n * sizeof(u32)); - int *L = (u32 *)afl_realloc(AFL_BUF_PARAM(in_scratch), n * sizeof(u32)); + int *S = (int *)afl_realloc(AFL_BUF_PARAM(out_scratch), n * sizeof(u32)); + int *L = (int *)afl_realloc(AFL_BUF_PARAM(in_scratch), n * sizeof(u32)); if (!P || !S || !L || !afl->alias_table || !afl->alias_probability) { @@ -131,6 +149,20 @@ void create_alias_table(afl_state_t *afl) { } + if (unlikely(afl->schedule == MMOPT) && afl->queued_discovered) { + + u32 cnt = afl->queued_discovered >= 5 ? 5 : afl->queued_discovered; + + for (i = n - cnt; i < n; i++) { + + struct queue_entry *q = afl->queue_buf[i]; + + if (likely(!q->disabled)) { q->weight *= 2.0; } + + } + + } + for (i = 0; i < n; i++) { // weight is always 0 for disabled entries @@ -246,11 +278,11 @@ void create_alias_table(afl_state_t *afl) { void mark_as_det_done(afl_state_t *afl, struct queue_entry *q) { - u8 fn[PATH_MAX]; - s32 fd; + char fn[PATH_MAX]; + s32 fd; snprintf(fn, PATH_MAX, "%s/queue/.state/deterministic_done/%s", afl->out_dir, - strrchr(q->fname, '/') + 1); + strrchr((char *)q->fname, '/') + 1); fd = open(fn, O_WRONLY | O_CREAT | O_EXCL, DEFAULT_PERMISSION); if (fd < 0) { PFATAL("Unable to create '%s'", fn); } @@ -265,10 +297,10 @@ void mark_as_det_done(afl_state_t *afl, struct queue_entry *q) { void mark_as_variable(afl_state_t *afl, struct queue_entry *q) { - u8 fn[PATH_MAX]; - u8 ldest[PATH_MAX]; + char fn[PATH_MAX]; + char ldest[PATH_MAX]; - u8 *fn_name = strrchr(q->fname, '/') + 1; + char *fn_name = strrchr((char *)q->fname, '/') + 1; sprintf(ldest, "../../%s", fn_name); sprintf(fn, "%s/queue/.state/variable_behavior/%s", afl->out_dir, fn_name); @@ -292,12 +324,12 @@ void mark_as_redundant(afl_state_t *afl, struct queue_entry *q, u8 state) { if (likely(state == q->fs_redundant)) { return; } - u8 fn[PATH_MAX]; + char fn[PATH_MAX]; q->fs_redundant = state; sprintf(fn, "%s/queue/.state/redundant_edges/%s", afl->out_dir, - strrchr(q->fname, '/') + 1); + strrchr((char *)q->fname, '/') + 1); if (state) { @@ -408,7 +440,7 @@ u8 check_if_text_buf(u8 *buf, u32 len) { static u8 check_if_text(afl_state_t *afl, struct queue_entry *q) { - if (q->len < AFL_TXT_MIN_LEN) return 0; + if (q->len < AFL_TXT_MIN_LEN || q->len < AFL_TXT_MAX_LEN) return 0; u8 *buf; int fd; @@ -416,8 +448,8 @@ static u8 check_if_text(afl_state_t *afl, struct queue_entry *q) { ssize_t comp; if (len >= MAX_FILE) len = MAX_FILE - 1; - if ((fd = open(q->fname, O_RDONLY)) < 0) return 0; - buf = afl_realloc(AFL_BUF_PARAM(in_scratch), len + 1); + if ((fd = open((char *)q->fname, O_RDONLY)) < 0) return 0; + buf = (u8 *)afl_realloc(AFL_BUF_PARAM(in_scratch), len + 1); comp = read(fd, buf, len); close(fd); if (comp != (ssize_t)len) return 0; @@ -519,7 +551,8 @@ static u8 check_if_text(afl_state_t *afl, struct queue_entry *q) { void add_to_queue(afl_state_t *afl, u8 *fname, u32 len, u8 passed_det) { - struct queue_entry *q = ck_alloc(sizeof(struct queue_entry)); + struct queue_entry *q = + (struct queue_entry *)ck_alloc(sizeof(struct queue_entry)); q->fname = fname; q->len = len; @@ -553,13 +586,30 @@ void add_to_queue(afl_state_t *afl, u8 *fname, u32 len, u8 passed_det) { afl->cycles_wo_finds = 0; - struct queue_entry **queue_buf = afl_realloc( + struct queue_entry **queue_buf = (struct queue_entry **)afl_realloc( AFL_BUF_PARAM(queue), afl->queued_items * sizeof(struct queue_entry *)); if (unlikely(!queue_buf)) { PFATAL("alloc"); } queue_buf[afl->queued_items - 1] = q; q->id = afl->queued_items - 1; - afl->last_find_time = get_cur_time(); + u64 cur_time = get_cur_time(); + + if (likely(afl->start_time) && + unlikely(afl->longest_find_time < cur_time - afl->last_find_time)) { + + if (unlikely(!afl->last_find_time)) { + + afl->longest_find_time = cur_time - afl->start_time; + + } else { + + afl->longest_find_time = cur_time - afl->last_find_time; + + } + + } + + afl->last_find_time = cur_time; if (afl->custom_mutators_count) { @@ -573,7 +623,11 @@ void add_to_queue(afl_state_t *afl, u8 *fname, u32 len, u8 passed_det) { } /* only redqueen currently uses is_ascii */ - if (afl->shm.cmplog_mode) q->is_ascii = check_if_text(afl, q); + if (unlikely(afl->shm.cmplog_mode && !q->is_ascii)) { + + q->is_ascii = check_if_text(afl, q); + + } } @@ -703,7 +757,7 @@ void update_bitmap_score(afl_state_t *afl, struct queue_entry *q) { if (!q->trace_mini) { u32 len = (afl->fsrv.map_size >> 3); - q->trace_mini = ck_alloc(len); + q->trace_mini = (u8 *)ck_alloc(len); minimize_bits(afl, q->trace_mini, afl->fsrv.trace_bits); } @@ -1006,10 +1060,16 @@ u32 calculate_score(afl_state_t *afl, struct queue_entry *q) { break; case LIN: + // Don't modify perf_score for unfuzzed seeds + if (!q->fuzz_level) break; + factor = q->fuzz_level / (afl->n_fuzz[q->n_fuzz_entry] + 1); break; case QUAD: + // Don't modify perf_score for unfuzzed seeds + if (!q->fuzz_level) break; + factor = q->fuzz_level * q->fuzz_level / (afl->n_fuzz[q->n_fuzz_entry] + 1); break; @@ -1089,19 +1149,19 @@ inline void queue_testcase_retake(afl_state_t *afl, struct queue_entry *q, if (len != old_len) { afl->q_testcase_cache_size = afl->q_testcase_cache_size + len - old_len; - q->testcase_buf = realloc(q->testcase_buf, len); + q->testcase_buf = (u8 *)realloc(q->testcase_buf, len); if (unlikely(!q->testcase_buf)) { - PFATAL("Unable to malloc '%s' with len %u", q->fname, len); + PFATAL("Unable to malloc '%s' with len %u", (char *)q->fname, len); } } - int fd = open(q->fname, O_RDONLY); + int fd = open((char *)q->fname, O_RDONLY); - if (unlikely(fd < 0)) { PFATAL("Unable to open '%s'", q->fname); } + if (unlikely(fd < 0)) { PFATAL("Unable to open '%s'", (char *)q->fname); } ck_read(fd, q->testcase_buf, len, q->fname); close(fd); @@ -1121,7 +1181,7 @@ inline void queue_testcase_retake_mem(afl_state_t *afl, struct queue_entry *q, if (likely(len != old_len)) { - u8 *ptr = realloc(q->testcase_buf, len); + u8 *ptr = (u8 *)realloc(q->testcase_buf, len); if (likely(ptr)) { @@ -1153,23 +1213,23 @@ inline u8 *queue_testcase_get(afl_state_t *afl, struct queue_entry *q) { if (unlikely(q == afl->queue_cur)) { - buf = afl_realloc((void **)&afl->testcase_buf, len); + buf = (u8 *)afl_realloc((void **)&afl->testcase_buf, len); } else { - buf = afl_realloc((void **)&afl->splicecase_buf, len); + buf = (u8 *)afl_realloc((void **)&afl->splicecase_buf, len); } if (unlikely(!buf)) { - PFATAL("Unable to malloc '%s' with len %u", q->fname, len); + PFATAL("Unable to malloc '%s' with len %u", (char *)q->fname, len); } - int fd = open(q->fname, O_RDONLY); + int fd = open((char *)q->fname, O_RDONLY); - if (unlikely(fd < 0)) { PFATAL("Unable to open '%s'", q->fname); } + if (unlikely(fd < 0)) { PFATAL("Unable to open '%s'", (char *)q->fname); } ck_read(fd, buf, len, q->fname); close(fd); @@ -1213,7 +1273,7 @@ inline u8 *queue_testcase_get(afl_state_t *afl, struct queue_entry *q) { do_once = 1; // release unneeded memory - afl->q_testcase_cache = ck_realloc( + afl->q_testcase_cache = (struct queue_entry **)ck_realloc( afl->q_testcase_cache, (afl->q_testcase_max_cache_entries + 1) * sizeof(size_t)); @@ -1260,15 +1320,15 @@ inline u8 *queue_testcase_get(afl_state_t *afl, struct queue_entry *q) { /* Map the test case into memory. */ - int fd = open(q->fname, O_RDONLY); + int fd = open((char *)q->fname, O_RDONLY); - if (unlikely(fd < 0)) { PFATAL("Unable to open '%s'", q->fname); } + if (unlikely(fd < 0)) { PFATAL("Unable to open '%s'", (char *)q->fname); } - q->testcase_buf = malloc(len); + q->testcase_buf = (u8 *)malloc(len); if (unlikely(!q->testcase_buf)) { - PFATAL("Unable to malloc '%s' with len %u", q->fname, len); + PFATAL("Unable to malloc '%s' with len %u", (char *)q->fname, len); } @@ -1331,11 +1391,11 @@ inline void queue_testcase_store_mem(afl_state_t *afl, struct queue_entry *q, /* Map the test case into memory. */ - q->testcase_buf = malloc(len); + q->testcase_buf = (u8 *)malloc(len); if (unlikely(!q->testcase_buf)) { - PFATAL("Unable to malloc '%s' with len %u", q->fname, len); + PFATAL("Unable to malloc '%s' with len %u", (char *)q->fname, len); } diff --git a/src/afl-fuzz-redqueen.c b/src/afl-fuzz-redqueen.c index 337f124d..6e4a655b 100644 --- a/src/afl-fuzz-redqueen.c +++ b/src/afl-fuzz-redqueen.c @@ -11,7 +11,7 @@ Andrea Fioraldi <andreafioraldi@gmail.com> Copyright 2016, 2017 Google Inc. All rights reserved. - Copyright 2019-2022 AFLplusplus Project. All rights reserved. + Copyright 2019-2023 AFLplusplus Project. All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -167,6 +167,25 @@ static u8 get_exec_checksum(afl_state_t *afl, u8 *buf, u32 len, u64 *cksum) { } +/* replace everything with different values */ +static void random_replace(afl_state_t *afl, u8 *buf, u32 len) { + + for (u32 i = 0; i < len; i++) { + + u8 c; + + do { + + c = rand_below(afl, 256); + + } while (c == buf[i]); + + buf[i] = c; + + } + +} + /* replace everything with different values but stay in the same type */ static void type_replace(afl_state_t *afl, u8 *buf, u32 len) { @@ -293,7 +312,15 @@ static u8 colorization(afl_state_t *afl, u8 *buf, u32 len, memcpy(backup, buf, len); memcpy(changed, buf, len); - type_replace(afl, changed, len); + if (afl->cmplog_random_colorization) { + + random_replace(afl, changed, len); + + } else { + + type_replace(afl, changed, len); + + } while ((rng = pop_biggest_range(&ranges)) != NULL && afl->stage_cur < afl->stage_max) { @@ -1008,7 +1035,7 @@ static u8 cmp_extend_encoding(afl_state_t *afl, struct cmp_header *h, } else { - diff = 0; + o_diff = 0; } @@ -1597,6 +1624,8 @@ static void try_to_add_to_dictN(afl_state_t *afl, u128 v, u8 size) { } + if (cons_0 > 1 || cons_ff > 1) { return; } + } maybe_add_auto(afl, (u8 *)&v + off, size); diff --git a/src/afl-fuzz-run.c b/src/afl-fuzz-run.c index 3c3d4817..2d53de93 100644 --- a/src/afl-fuzz-run.c +++ b/src/afl-fuzz-run.c @@ -10,7 +10,7 @@ Dominik Maier <mail@dmnk.co> Copyright 2016, 2017 Google Inc. All rights reserved. - Copyright 2019-2022 AFLplusplus Project. All rights reserved. + Copyright 2019-2023 AFLplusplus Project. All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -76,6 +76,8 @@ fuzz_run_target(afl_state_t *afl, afl_forkserver_t *fsrv, u32 timeout) { u32 __attribute__((hot)) write_to_testcase(afl_state_t *afl, void **mem, u32 len, u32 fix) { + u8 sent = 0; + if (unlikely(afl->custom_mutators_count)) { ssize_t new_size = len; @@ -131,13 +133,46 @@ write_to_testcase(afl_state_t *afl, void **mem, u32 len, u32 fix) { } - /* everything as planned. use the potentially new data. */ - afl_fsrv_write_to_testcase(&afl->fsrv, new_mem, new_size); + if (new_mem != *mem && new_mem != NULL && new_size > 0 + && !afl->afl_env.afl_post_process_keep_original) { + + u8 *new_buf = afl_realloc(AFL_BUF_PARAM(out_scratch), new_size); + if (unlikely(!new_buf)) { PFATAL("alloc"); } + *mem = new_buf; + memcpy(*mem, new_mem, new_size); + afl_swap_bufs(AFL_BUF_PARAM(out), AFL_BUF_PARAM(out_scratch)); + + } + + if (unlikely(afl->custom_mutators_count)) { + + LIST_FOREACH(&afl->custom_mutator_list, struct custom_mutator, { + + if (el->afl_custom_fuzz_send) { + + el->afl_custom_fuzz_send(el->data, *mem, new_size); + sent = 1; + + } + + }); + + } + + if (likely(!sent)) { + + /* everything as planned. use the potentially new data. */ + + if (likely(!afl->afl_env.afl_post_process_keep_original)) { - if (likely(!afl->afl_env.afl_post_process_keep_original)) { + afl_fsrv_write_to_testcase(&afl->fsrv, *mem, new_size); - if (new_mem != *mem) { *mem = new_mem; } - len = new_size; + } else { + + afl_fsrv_write_to_testcase(&afl->fsrv, new_mem, new_size); + + } + len = new_size; } @@ -153,8 +188,27 @@ write_to_testcase(afl_state_t *afl, void **mem, u32 len, u32 fix) { } - /* boring uncustom. */ - afl_fsrv_write_to_testcase(&afl->fsrv, *mem, len); + if (unlikely(afl->custom_mutators_count)) { + + LIST_FOREACH(&afl->custom_mutator_list, struct custom_mutator, { + + if (el->afl_custom_fuzz_send) { + + el->afl_custom_fuzz_send(el->data, *mem, len); + sent = 1; + + } + + }); + + } + + if (likely(!sent)) { + + /* boring uncustom. */ + afl_fsrv_write_to_testcase(&afl->fsrv, *mem, len); + + } } @@ -487,7 +541,7 @@ u8 calibrate_case(afl_state_t *afl, struct queue_entry *q, u8 *use_mem, } - if (unlikely(!var_detected)) { + if (unlikely(!var_detected && !afl->afl_env.afl_no_warn_instability)) { // note: from_queue seems to only be set during initialization if (afl->afl_env.afl_no_ui || from_queue) { diff --git a/src/afl-fuzz-state.c b/src/afl-fuzz-state.c index 10bc2768..cccebeb9 100644 --- a/src/afl-fuzz-state.c +++ b/src/afl-fuzz-state.c @@ -9,7 +9,7 @@ Andrea Fioraldi <andreafioraldi@gmail.com> Copyright 2016, 2017 Google Inc. All rights reserved. - Copyright 2019-2022 AFLplusplus Project. All rights reserved. + Copyright 2019-2023 AFLplusplus Project. All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -23,6 +23,8 @@ */ +#include <signal.h> +#include <limits.h> #include "afl-fuzz.h" #include "envs.h" @@ -99,6 +101,7 @@ void afl_state_init(afl_state_t *afl, uint32_t map_size) { afl->hang_tmout = EXEC_TIMEOUT; afl->exit_on_time = 0; afl->stats_update_freq = 1; + afl->stats_file_update_freq_msecs = STATS_UPDATE_SEC * 1000; afl->stats_avg_exec = 0; afl->skip_deterministic = 1; afl->sync_time = SYNC_TIME; @@ -203,6 +206,13 @@ void read_afl_environment(afl_state_t *afl, char **envp) { afl->afl_env.afl_no_affinity = get_afl_env(afl_environment_variables[i]) ? 1 : 0; + } else if (!strncmp(env, "AFL_NO_WARN_INSTABILITY", + + afl_environment_variable_len)) { + + afl->afl_env.afl_no_warn_instability = + get_afl_env(afl_environment_variables[i]) ? 1 : 0; + } else if (!strncmp(env, "AFL_TRY_AFFINITY", afl_environment_variable_len)) { @@ -291,6 +301,13 @@ void read_afl_environment(afl_state_t *afl, char **envp) { afl->afl_env.afl_ignore_problems = get_afl_env(afl_environment_variables[i]) ? 1 : 0; + } else if (!strncmp(env, "AFL_IGNORE_TIMEOUTS", + + afl_environment_variable_len)) { + + afl->afl_env.afl_ignore_timeouts = + get_afl_env(afl_environment_variables[i]) ? 1 : 0; + } else if (!strncmp(env, "AFL_I_DONT_CARE_ABOUT_MISSING_CRASHES", afl_environment_variable_len)) { @@ -494,7 +511,14 @@ void read_afl_environment(afl_state_t *afl, char **envp) { afl_environment_variable_len)) { - afl->afl_env.afl_kill_signal = + afl->afl_env.afl_child_kill_signal = + (u8 *)get_afl_env(afl_environment_variables[i]); + + } else if (!strncmp(env, "AFL_FORK_SERVER_KILL_SIGNAL", + + afl_environment_variable_len)) { + + afl->afl_env.afl_fsrv_kill_signal = (u8 *)get_afl_env(afl_environment_variables[i]); } else if (!strncmp(env, "AFL_TARGET_ENV", @@ -550,6 +574,26 @@ void read_afl_environment(afl_state_t *afl, char **envp) { } + } else if (!strncmp(env, "AFL_FUZZER_STATS_UPDATE_INTERVAL", + + afl_environment_variable_len)) { + + u64 stats_update_freq_sec = + strtoull(get_afl_env(afl_environment_variables[i]), NULL, 0); + if (stats_update_freq_sec >= UINT_MAX || + 0 == stats_update_freq_sec) { + + WARNF( + "Incorrect value given to AFL_FUZZER_STATS_UPDATE_INTERVAL, " + "using default of %d seconds\n", + STATS_UPDATE_SEC); + + } else { + + afl->stats_file_update_freq_msecs = stats_update_freq_sec * 1000; + + } + } } else { @@ -611,10 +655,14 @@ void read_afl_environment(afl_state_t *afl, char **envp) { } - if (afl->afl_env.afl_pizza_mode) { + if (afl->afl_env.afl_pizza_mode > 0) { afl->pizza_is_served = 1; + } else if (afl->afl_env.afl_pizza_mode < 0) { + + OKF("Pizza easter egg mode is now disabled."); + } if (issue_detected) { sleep(2); } @@ -665,8 +713,17 @@ void afl_states_stop(void) { LIST_FOREACH(&afl_states, afl_state_t, { - if (el->fsrv.child_pid > 0) kill(el->fsrv.child_pid, el->fsrv.kill_signal); - if (el->fsrv.fsrv_pid > 0) kill(el->fsrv.fsrv_pid, el->fsrv.kill_signal); + /* NOTE: We need to make sure that the parent (the forkserver) reap the + * child (see below). */ + if (el->fsrv.child_pid > 0) + kill(el->fsrv.child_pid, el->fsrv.child_kill_signal); + if (el->fsrv.fsrv_pid > 0) { + + kill(el->fsrv.fsrv_pid, el->fsrv.fsrv_kill_signal); + /* Make sure the forkserver does not end up as zombie. */ + waitpid(el->fsrv.fsrv_pid, NULL, 0); + + } }); diff --git a/src/afl-fuzz-stats.c b/src/afl-fuzz-stats.c index 51e292d8..07157bf7 100644 --- a/src/afl-fuzz-stats.c +++ b/src/afl-fuzz-stats.c @@ -9,7 +9,7 @@ Andrea Fioraldi <andreafioraldi@gmail.com> Copyright 2016, 2017 Google Inc. All rights reserved. - Copyright 2019-2022 AFLplusplus Project. All rights reserved. + Copyright 2019-2023 AFLplusplus Project. All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -62,7 +62,7 @@ void write_setup_file(afl_state_t *afl, u32 argc, char **argv) { if (memchr(argv[i], '\'', strlen(argv[i]))) { #else - if (index(argv[i], '\'')) { + if (strchr(argv[i], '\'')) { #endif @@ -251,6 +251,7 @@ void write_stats_file(afl_state_t *afl, u32 t_bytes, double bitmap_cvg, "fuzzer_pid : %u\n" "cycles_done : %llu\n" "cycles_wo_finds : %llu\n" + "time_wo_finds : %llu\n" "execs_done : %llu\n" "execs_per_sec : %0.02f\n" "execs_ps_last_min : %0.02f\n" @@ -291,6 +292,11 @@ void write_stats_file(afl_state_t *afl, u32 t_bytes, double bitmap_cvg, (afl->start_time - afl->prev_run_time) / 1000, cur_time / 1000, (afl->prev_run_time + cur_time - afl->start_time) / 1000, (u32)getpid(), afl->queue_cycle ? (afl->queue_cycle - 1) : 0, afl->cycles_wo_finds, + afl->longest_find_time > cur_time - afl->last_find_time + ? afl->longest_find_time / 1000 + : ((afl->start_time == 0 || afl->last_find_time == 0) + ? 0 + : (cur_time - afl->last_find_time) / 1000), afl->fsrv.total_execs, afl->fsrv.total_execs / ((double)(afl->prev_run_time + get_cur_time() - afl->start_time) / @@ -365,6 +371,39 @@ void write_stats_file(afl_state_t *afl, u32 t_bytes, double bitmap_cvg, } +#ifdef INTROSPECTION +void write_queue_stats(afl_state_t *afl) { + + FILE *f; + u8 *fn = alloc_printf("%s/queue_data", afl->out_dir); + if ((f = fopen(fn, "w")) != NULL) { + + u32 id; + fprintf(f, + "# filename, length, exec_us, selected, skipped, mutations, finds, " + "crashes, timeouts, bitmap_size, perf_score, weight, colorized, " + "favored, disabled\n"); + for (id = 0; id < afl->queued_items; ++id) { + + struct queue_entry *q = afl->queue_buf[id]; + fprintf(f, "\"%s\",%u,%llu,%u,%u,%llu,%u,%u,%u,%u,%.3f,%.3f,%u,%u,%u\n", + q->fname, q->len, q->exec_us, q->stats_selected, q->stats_skipped, + q->stats_mutated, q->stats_finds, q->stats_crashes, + q->stats_tmouts, q->bitmap_size, q->perf_score, q->weight, + q->colorized, q->favored, q->disabled); + + } + + fclose(f); + + } + + ck_free(fn); + +} + +#endif + /* Update the plot file if there is a reason to. */ void maybe_update_plot_file(afl_state_t *afl, u32 t_bytes, double bitmap_cvg, @@ -578,9 +617,10 @@ void show_stats_normal(afl_state_t *afl) { /* Roughly every minute, update fuzzer stats and save auto tokens. */ - if (unlikely(!afl->non_instrumented_mode && - (afl->force_ui_update || - cur_ms - afl->stats_last_stats_ms > STATS_UPDATE_SEC * 1000))) { + if (unlikely( + !afl->non_instrumented_mode && + (afl->force_ui_update || cur_ms - afl->stats_last_stats_ms > + afl->stats_file_update_freq_msecs))) { afl->stats_last_stats_ms = cur_ms; write_stats_file(afl, t_bytes, t_byte_ratio, stab_ratio, @@ -613,6 +653,18 @@ void show_stats_normal(afl_state_t *afl) { } + /* Every now and then, write queue data. */ + + if (unlikely(afl->force_ui_update || + cur_ms - afl->stats_last_queue_ms > QUEUE_UPDATE_SEC * 1000)) { + + afl->stats_last_queue_ms = cur_ms; +#ifdef INTROSPECTION + write_queue_stats(afl); +#endif + + } + /* Honor AFL_EXIT_WHEN_DONE and AFL_BENCH_UNTIL_CRASH. */ if (unlikely(!afl->non_instrumented_mode && afl->cycles_wo_finds > 100 && @@ -624,9 +676,14 @@ void show_stats_normal(afl_state_t *afl) { /* AFL_EXIT_ON_TIME. */ - if (unlikely(afl->last_find_time && !afl->non_instrumented_mode && - afl->afl_env.afl_exit_on_time && - (cur_ms - afl->last_find_time) > afl->exit_on_time)) { + /* If no coverage was found yet, check whether run time is greater than + * exit_on_time. */ + + if (unlikely(!afl->non_instrumented_mode && afl->afl_env.afl_exit_on_time && + ((afl->last_find_time && + (cur_ms - afl->last_find_time) > afl->exit_on_time) || + (!afl->last_find_time && + (cur_ms - afl->start_time) > afl->exit_on_time)))) { afl->stop_soon = 2; @@ -696,20 +753,20 @@ void show_stats_normal(afl_state_t *afl) { #ifdef __linux__ if (afl->fsrv.nyx_mode) { - sprintf(banner + banner_pad, - "%s " cLCY VERSION cLBL " {%s} " cLGN "(%s) " cPIN "[%s] - Nyx", - afl->crash_mode ? cPIN "peruvian were-rabbit" - : cYEL "american fuzzy lop", - si, afl->use_banner, afl->power_name); + snprintf(banner + banner_pad, sizeof(banner) - banner_pad, + "%s " cLCY VERSION cLBL " {%s} " cLGN "(%s) " cPIN "[%s] - Nyx", + afl->crash_mode ? cPIN "peruvian were-rabbit" + : cYEL "american fuzzy lop", + si, afl->use_banner, afl->power_name); } else { #endif - sprintf(banner + banner_pad, - "%s " cLCY VERSION cLBL " {%s} " cLGN "(%s) " cPIN "[%s]", - afl->crash_mode ? cPIN "peruvian were-rabbit" - : cYEL "american fuzzy lop", - si, afl->use_banner, afl->power_name); + snprintf(banner + banner_pad, sizeof(banner) - banner_pad, + "%s " cLCY VERSION cLBL " {%s} " cLGN "(%s) " cPIN "[%s]", + afl->crash_mode ? cPIN "peruvian were-rabbit" + : cYEL "american fuzzy lop", + si, afl->use_banner, afl->power_name); #ifdef __linux__ @@ -1399,6 +1456,18 @@ void show_stats_pizza(afl_state_t *afl) { } + /* Every now and then, write queue data. */ + + if (unlikely(afl->force_ui_update || + cur_ms - afl->stats_last_queue_ms > QUEUE_UPDATE_SEC * 1000)) { + + afl->stats_last_queue_ms = cur_ms; +#ifdef INTROSPECTION + write_queue_stats(afl); +#endif + + } + /* Honor AFL_EXIT_WHEN_DONE and AFL_BENCH_UNTIL_CRASH. */ if (unlikely(!afl->non_instrumented_mode && afl->cycles_wo_finds > 100 && @@ -1410,9 +1479,14 @@ void show_stats_pizza(afl_state_t *afl) { /* AFL_EXIT_ON_TIME. */ - if (unlikely(afl->last_find_time && !afl->non_instrumented_mode && - afl->afl_env.afl_exit_on_time && - (cur_ms - afl->last_find_time) > afl->exit_on_time)) { + /* If no coverage was found yet, check whether run time is greater than + * exit_on_time. */ + + if (unlikely(!afl->non_instrumented_mode && afl->afl_env.afl_exit_on_time && + ((afl->last_find_time && + (cur_ms - afl->last_find_time) > afl->exit_on_time) || + (!afl->last_find_time && + (cur_ms - afl->start_time) > afl->exit_on_time)))) { afl->stop_soon = 2; @@ -1483,20 +1557,22 @@ void show_stats_pizza(afl_state_t *afl) { #ifdef __linux__ if (afl->fsrv.nyx_mode) { - sprintf(banner + banner_pad, - "%s " cLCY VERSION cLBL " {%s} " cLGN "(%s) " cPIN "[%s] - Nyx", - afl->crash_mode ? cPIN "Mozzarbella Pizzeria table booking system" - : cYEL "Mozzarbella Pizzeria management system", - si, afl->use_banner, afl->power_name); + snprintf(banner + banner_pad, sizeof(banner) - banner_pad, + "%s " cLCY VERSION cLBL " {%s} " cLGN "(%s) " cPIN "[%s] - Nyx", + afl->crash_mode ? cPIN + "Mozzarbella Pizzeria table booking system" + : cYEL "Mozzarbella Pizzeria management system", + si, afl->use_banner, afl->power_name); } else { #endif - sprintf(banner + banner_pad, - "%s " cLCY VERSION cLBL " {%s} " cLGN "(%s) " cPIN "[%s]", - afl->crash_mode ? cPIN "Mozzarbella Pizzeria table booking system" - : cYEL "Mozzarbella Pizzeria management system", - si, afl->use_banner, afl->power_name); + snprintf(banner + banner_pad, sizeof(banner) - banner_pad, + "%s " cLCY VERSION cLBL " {%s} " cLGN "(%s) " cPIN "[%s]", + afl->crash_mode ? cPIN + "Mozzarbella Pizzeria table booking system" + : cYEL "Mozzarbella Pizzeria management system", + si, afl->use_banner, afl->power_name); #ifdef __linux__ @@ -1727,10 +1803,10 @@ void show_stats_pizza(afl_state_t *afl) { /* Show a warning about slow execution. */ - if (afl->stats_avg_exec < 100) { + if (afl->stats_avg_exec < 20) { sprintf(tmp, "%s/sec (%s)", u_stringify_float(IB(0), afl->stats_avg_exec), - afl->stats_avg_exec < 20 ? "zzzz..." : "Gennarino is at it again!"); + "zzzz..."); SAYF(bV bSTOP " pizza making speed : " cLRD "%-22s ", @@ -2105,7 +2181,9 @@ void show_init_stats(afl_state_t *afl) { ? 50000 : 10000)) { - WARNF(cLRD "The target binary is pretty slow! See %s/perf_tips.md.", + WARNF(cLRD + "The target binary is pretty slow! See " + "%s/fuzzing_in_depth.md#i-improve-the-speed", doc_path); } @@ -2134,13 +2212,17 @@ void show_init_stats(afl_state_t *afl) { if (max_len > 50 * 1024) { - WARNF(cLRD "Some test cases are huge (%s) - see %s/perf_tips.md!", + WARNF(cLRD + "Some test cases are huge (%s) - see " + "%s/fuzzing_in_depth.md#i-improve-the-speed", stringify_mem_size(IB(0), max_len), doc_path); } else if (max_len > 10 * 1024) { - WARNF("Some test cases are big (%s) - see %s/perf_tips.md.", - stringify_mem_size(IB(0), max_len), doc_path); + WARNF( + "Some test cases are big (%s) - see " + "%s/fuzzing_in_depth.md#i-improve-the-speed", + stringify_mem_size(IB(0), max_len), doc_path); } diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index b83af257..6b65c810 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -9,7 +9,7 @@ Andrea Fioraldi <andreafioraldi@gmail.com> Copyright 2016, 2017 Google Inc. All rights reserved. - Copyright 2019-2022 AFLplusplus Project. All rights reserved. + Copyright 2019-2023 AFLplusplus Project. All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -25,6 +25,7 @@ #include "afl-fuzz.h" #include "cmplog.h" +#include "common.h" #include <limits.h> #include <stdlib.h> #ifndef USEMMAP @@ -164,16 +165,16 @@ static void usage(u8 *argv0, int more_help) { " pacemaker mode (minutes of no new finds). 0 = " "immediately,\n" " -1 = immediately and together with normal mutation.\n" - " See docs/README.MOpt.md\n" " -c program - enable CmpLog by specifying a binary compiled for " "it.\n" " if using QEMU/FRIDA or the fuzzing target is " "compiled\n" " for CmpLog then just use -c 0.\n" - " -l cmplog_opts - CmpLog configuration values (e.g. \"2AT\"):\n" + " -l cmplog_opts - CmpLog configuration values (e.g. \"2ATR\"):\n" " 1=small files, 2=larger files (default), 3=all " "files,\n" - " A=arithmetic solving, T=transformational solving.\n\n" + " A=arithmetic solving, T=transformational solving,\n" + " R=random colorization bytes.\n\n" "Fuzzing behavior settings:\n" " -Z - sequential queue selection instead of weighted " "random\n" @@ -192,9 +193,9 @@ static void usage(u8 *argv0, int more_help) { "executions.\n\n" "Other stuff:\n" - " -M/-S id - distributed mode (see docs/parallel_fuzzing.md)\n" - " -M auto-sets -D, -Z (use -d to disable -D) and no " - "trimming\n" + " -M/-S id - distributed mode (-M sets -Z and disables trimming)\n" + " see docs/fuzzing_in_depth.md#c-using-multiple-cores\n" + " for effective recommendations for parallel fuzzing.\n" " -F path - sync to a foreign fuzzer queue directory (requires " "-M, can\n" " be specified up to %u times)\n" @@ -208,7 +209,8 @@ static void usage(u8 *argv0, int more_help) { " -b cpu_id - bind the fuzzing process to the specified CPU core " "(0-...)\n" " -e ext - file extension for the fuzz test input file (if " - "needed)\n\n", + "needed)\n" + "\n", argv0, EXEC_TIMEOUT, MEM_LIMIT, MAX_FILE, FOREIGN_SYNCS_MAX); if (more_help > 1) { @@ -248,19 +250,25 @@ static void usage(u8 *argv0, int more_help) { "AFL_DISABLE_TRIM: disable the trimming of test cases\n" "AFL_DUMB_FORKSRV: use fork server without feedback from target\n" "AFL_EXIT_WHEN_DONE: exit when all inputs are run and no new finds are found\n" - "AFL_EXIT_ON_TIME: exit when no new coverage finds are made within the specified time period\n" - "AFL_EXPAND_HAVOC_NOW: immediately enable expand havoc mode (default: after 60 minutes and a cycle without finds)\n" + "AFL_EXIT_ON_TIME: exit when no new coverage is found within the specified time\n" + "AFL_EXPAND_HAVOC_NOW: immediately enable expand havoc mode (default: after 60\n" + " minutes and a cycle without finds)\n" "AFL_FAST_CAL: limit the calibration stage to three cycles for speedup\n" "AFL_FORCE_UI: force showing the status screen (for virtual consoles)\n" - "AFL_FORKSRV_INIT_TMOUT: time spent waiting for forkserver during startup (in milliseconds)\n" + "AFL_FORKSRV_INIT_TMOUT: time spent waiting for forkserver during startup (in ms)\n" "AFL_HANG_TMOUT: override timeout value (in milliseconds)\n" "AFL_I_DONT_CARE_ABOUT_MISSING_CRASHES: don't warn about core dump handlers\n" + "AFL_IGNORE_PROBLEMS: do not abort fuzzing if an incorrect setup is detected\n" + "AFL_IGNORE_TIMEOUTS: do not process or save any timeouts\n" "AFL_IGNORE_UNKNOWN_ENVS: don't warn on unknown env vars\n" - "AFL_IGNORE_PROBLEMS: do not abort fuzzing if an incorrect setup is detected during a run\n" "AFL_IMPORT_FIRST: sync and import test cases from other fuzzer instances first\n" "AFL_INPUT_LEN_MIN/AFL_INPUT_LEN_MAX: like -g/-G set min/max fuzz length produced\n" "AFL_PIZZA_MODE: 1 - enforce pizza mode, 0 - disable for April 1st\n" - "AFL_KILL_SIGNAL: Signal ID delivered to child processes on timeout, etc. (default: SIGKILL)\n" + "AFL_KILL_SIGNAL: Signal ID delivered to child processes on timeout, etc.\n" + " (default: SIGKILL)\n" + "AFL_FORK_SERVER_KILL_SIGNAL: Kill signal for the fork server on termination\n" + " (default: SIGTERM). If unset and AFL_KILL_SIGNAL is\n" + " set, that value will be used.\n" "AFL_MAP_SIZE: the shared memory size for that target. must be >= the size\n" " the target was compiled for\n" "AFL_MAX_DET_EXTRAS: if more entries are in the dictionary list than this value\n" @@ -305,7 +313,9 @@ static void usage(u8 *argv0, int more_help) { "AFL_EARLY_FORKSERVER: force an early forkserver in an afl-clang-fast/\n" " afl-clang-lto/afl-gcc-fast target\n" "AFL_PERSISTENT: enforce persistent mode (if __AFL_LOOP is in a shared lib\n" - "AFL_DEFER_FORKSRV: enforced deferred forkserver (__AFL_INIT is in a .so\n" + "AFL_DEFER_FORKSRV: enforced deferred forkserver (__AFL_INIT is in a .so)\n" + "AFL_FUZZER_STATS_UPDATE_INTERVAL: interval to update fuzzer_stats file in seconds, " + "(default: 60, minimum: 1)\n" "\n" ); @@ -427,76 +437,13 @@ static void fasan_check_afl_preload(char *afl_preload) { } - #ifdef __linux__ - #include <dlfcn.h> - -nyx_plugin_handler_t *afl_load_libnyx_plugin(u8 *libnyx_binary) { - - void *handle; - nyx_plugin_handler_t *plugin = calloc(1, sizeof(nyx_plugin_handler_t)); - - ACTF("Trying to load libnyx.so plugin..."); - handle = dlopen((char *)libnyx_binary, RTLD_NOW); - if (!handle) { goto fail; } - - plugin->nyx_new = dlsym(handle, "nyx_new"); - if (plugin->nyx_new == NULL) { goto fail; } - - plugin->nyx_new_parent = dlsym(handle, "nyx_new_parent"); - if (plugin->nyx_new_parent == NULL) { goto fail; } - - plugin->nyx_new_child = dlsym(handle, "nyx_new_child"); - if (plugin->nyx_new_child == NULL) { goto fail; } - - plugin->nyx_shutdown = dlsym(handle, "nyx_shutdown"); - if (plugin->nyx_shutdown == NULL) { goto fail; } - - plugin->nyx_option_set_reload_mode = - dlsym(handle, "nyx_option_set_reload_mode"); - if (plugin->nyx_option_set_reload_mode == NULL) { goto fail; } - - plugin->nyx_option_set_timeout = dlsym(handle, "nyx_option_set_timeout"); - if (plugin->nyx_option_set_timeout == NULL) { goto fail; } - - plugin->nyx_option_apply = dlsym(handle, "nyx_option_apply"); - if (plugin->nyx_option_apply == NULL) { goto fail; } - - plugin->nyx_set_afl_input = dlsym(handle, "nyx_set_afl_input"); - if (plugin->nyx_set_afl_input == NULL) { goto fail; } - - plugin->nyx_exec = dlsym(handle, "nyx_exec"); - if (plugin->nyx_exec == NULL) { goto fail; } - - plugin->nyx_get_bitmap_buffer = dlsym(handle, "nyx_get_bitmap_buffer"); - if (plugin->nyx_get_bitmap_buffer == NULL) { goto fail; } - - plugin->nyx_get_bitmap_buffer_size = - dlsym(handle, "nyx_get_bitmap_buffer_size"); - if (plugin->nyx_get_bitmap_buffer_size == NULL) { goto fail; } - - plugin->nyx_get_aux_string = dlsym(handle, "nyx_get_aux_string"); - if (plugin->nyx_get_aux_string == NULL) { goto fail; } - - OKF("libnyx plugin is ready!"); - return plugin; - -fail: - - FATAL("failed to load libnyx: %s\n", dlerror()); - free(plugin); - return NULL; - -} - - #endif - /* Main entry point */ int main(int argc, char **argv_orig, char **envp) { s32 opt, auto_sync = 0 /*, user_set_cache = 0*/; u64 prev_queued = 0; - u32 sync_interval_cnt = 0, seek_to = 0, show_help = 0, + u32 sync_interval_cnt = 0, seek_to = 0, show_help = 0, default_output = 1, map_size = get_map_size(); u8 *extras_dir[4]; u8 mem_limit_given = 0, exit_1 = 0, debug = 0, @@ -797,6 +744,7 @@ int main(int argc, char **argv_orig, char **envp) { afl->fsrv.out_file = ck_strdup(optarg); afl->fsrv.use_stdin = 0; + default_output = 0; break; case 'x': /* dictionary */ @@ -1109,6 +1057,10 @@ int main(int argc, char **argv_orig, char **envp) { case 'T': afl->cmplog_enable_transform = 1; break; + case 'r': + case 'R': + afl->cmplog_random_colorization = 1; + break; default: FATAL("Unknown option value '%c' in -l %s", *c, optarg); @@ -1287,6 +1239,13 @@ int main(int argc, char **argv_orig, char **envp) { } + if (afl->is_main_node == 1 && afl->schedule != FAST && + afl->schedule != EXPLORE) { + + FATAL("-M is compatible only with fast and explore -p power schedules"); + + } + if (optind == argc || !afl->in_dir || !afl->out_dir || show_help) { usage(argv[0], show_help); @@ -1323,8 +1282,7 @@ int main(int argc, char **argv_orig, char **envp) { "Eißfeldt, Andrea Fioraldi and Dominik Maier"); OKF("afl++ is open source, get it at " "https://github.com/AFLplusplus/AFLplusplus"); - OKF("NOTE: This is v3.x which changes defaults and behaviours - see " - "README.md"); + OKF("NOTE: afl++ >= v3 has changed defaults and behaviours - see README.md"); #ifdef __linux__ if (afl->fsrv.nyx_mode) { @@ -1335,12 +1293,11 @@ int main(int argc, char **argv_orig, char **envp) { } #endif - if (afl->sync_id && afl->is_main_node && - afl->afl_env.afl_custom_mutator_only) { + if (!afl->skip_deterministic && afl->afl_env.afl_custom_mutator_only) { - WARNF( - "Using -M main node with the AFL_CUSTOM_MUTATOR_ONLY mutator options " - "will result in no deterministic mutations being done!"); + FATAL( + "Using -D determinstic fuzzing is incompatible with " + "AFL_CUSTOM_MUTATOR_ONLY!"); } @@ -1360,8 +1317,15 @@ int main(int argc, char **argv_orig, char **envp) { #endif - afl->fsrv.kill_signal = - parse_afl_kill_signal_env(afl->afl_env.afl_kill_signal, SIGKILL); + configure_afl_kill_signals(&afl->fsrv, afl->afl_env.afl_child_kill_signal, + afl->afl_env.afl_fsrv_kill_signal, + (afl->fsrv.qemu_mode || afl->unicorn_mode + #ifdef __linux__ + || afl->fsrv.nyx_mode + #endif + ) + ? SIGKILL + : SIGTERM); setup_signal_handlers(); check_asan_opts(afl); @@ -1563,6 +1527,29 @@ int main(int argc, char **argv_orig, char **envp) { } + if (afl->limit_time_sig > 0 && afl->custom_mutators_count) { + + if (afl->custom_only) { + + FATAL("Custom mutators are incompatible with MOpt (-L)"); + + } + + u32 custom_fuzz = 0; + LIST_FOREACH(&afl->custom_mutator_list, struct custom_mutator, { + + if (el->afl_custom_fuzz) { custom_fuzz = 1; } + + }); + + if (custom_fuzz) { + + WARNF("afl_custom_fuzz is incompatible with MOpt (-L)"); + + } + + } + if (afl->afl_env.afl_max_det_extras) { s32 max_det_extras = atoi(afl->afl_env.afl_max_det_extras); @@ -1895,6 +1882,7 @@ int main(int argc, char **argv_orig, char **envp) { if (aa_loc && !afl->fsrv.out_file) { afl->fsrv.use_stdin = 0; + default_output = 0; if (afl->file_extension) { @@ -2064,6 +2052,7 @@ int main(int argc, char **argv_orig, char **envp) { afl->cmplog_fsrv.qemu_mode = afl->fsrv.qemu_mode; afl->cmplog_fsrv.frida_mode = afl->fsrv.frida_mode; afl->cmplog_fsrv.cmplog_binary = afl->cmplog_binary; + afl->cmplog_fsrv.target_path = afl->fsrv.target_path; afl->cmplog_fsrv.init_child_func = cmplog_exec_child; if ((map_size <= DEFAULT_SHMEM_SIZE || @@ -2134,6 +2123,24 @@ int main(int argc, char **argv_orig, char **envp) { } + if (afl->fsrv.out_file && afl->fsrv.use_shmem_fuzz) { + + unlink(afl->fsrv.out_file); + afl->fsrv.out_file = NULL; + afl->fsrv.use_stdin = 0; + close(afl->fsrv.out_fd); + afl->fsrv.out_fd = -1; + + if (!afl->unicorn_mode && !afl->fsrv.use_stdin && !default_output) { + + WARNF( + "You specified -f or @@ on the command line but the target harness " + "specified fuzz cases via shmem, switching to shmem!"); + + } + + } + deunicode_extras(afl); dedup_extras(afl); if (afl->extras_cnt) { OKF("Loaded a total of %u extras.", afl->extras_cnt); } @@ -2181,14 +2188,6 @@ int main(int argc, char **argv_orig, char **envp) { if (!afl->pending_not_fuzzed || !valid_seeds) { - #ifdef __linux__ - if (afl->fsrv.nyx_mode) { - - afl->fsrv.nyx_handlers->nyx_shutdown(afl->fsrv.nyx_runner); - - } - - #endif FATAL("We need at least one valid input seed that does not crash!"); } @@ -2247,8 +2246,10 @@ int main(int argc, char **argv_orig, char **envp) { // real start time, we reset, so this works correctly with -V afl->start_time = get_cur_time(); - u32 runs_in_current_cycle = (u32)-1; - u32 prev_queued_items = 0; + #ifdef INTROSPECTION + u32 prev_saved_crashes = 0, prev_saved_tmouts = 0; + #endif + u32 prev_queued_items = 0, runs_in_current_cycle = (u32)-1; u8 skipped_fuzz; #ifdef INTROSPECTION @@ -2276,6 +2277,12 @@ int main(int argc, char **argv_orig, char **envp) { (!afl->queue_cycle && afl->afl_env.afl_import_first)) && afl->sync_id)) { + if (!afl->queue_cycle && afl->afl_env.afl_import_first) { + + OKF("Syncing queues from other fuzzer instances first ..."); + + } + sync_fuzzers(afl); } @@ -2419,10 +2426,22 @@ int main(int argc, char **argv_orig, char **envp) { } #ifdef INTROSPECTION - fprintf(afl->introspection_file, - "CYCLE cycle=%llu cycle_wo_finds=%llu expand_havoc=%u queue=%u\n", - afl->queue_cycle, afl->cycles_wo_finds, afl->expand_havoc, - afl->queued_items); + { + + u64 cur_time = get_cur_time(); + fprintf(afl->introspection_file, + "CYCLE cycle=%llu cycle_wo_finds=%llu time_wo_finds=%llu " + "expand_havoc=%u queue=%u\n", + afl->queue_cycle, afl->cycles_wo_finds, + afl->longest_find_time > cur_time - afl->last_find_time + ? afl->longest_find_time / 1000 + : ((afl->start_time == 0 || afl->last_find_time == 0) + ? 0 + : (cur_time - afl->last_find_time) / 1000), + afl->expand_havoc, afl->queued_items); + + } + #endif if (afl->cycle_schedules) { @@ -2493,27 +2512,70 @@ int main(int argc, char **argv_orig, char **envp) { } - afl->current_entry = select_next_queue_entry(afl); + do { + + afl->current_entry = select_next_queue_entry(afl); + + } while (unlikely(afl->current_entry >= afl->queued_items)); + afl->queue_cur = afl->queue_buf[afl->current_entry]; } skipped_fuzz = fuzz_one(afl); + #ifdef INTROSPECTION + ++afl->queue_cur->stats_selected; + + if (unlikely(skipped_fuzz)) { + + ++afl->queue_cur->stats_skipped; + + } else { + + if (unlikely(afl->queued_items > prev_queued_items)) { + + afl->queue_cur->stats_finds += afl->queued_items - prev_queued_items; + prev_queued_items = afl->queued_items; + + } + + if (unlikely(afl->saved_crashes > prev_saved_crashes)) { + + afl->queue_cur->stats_crashes += + afl->saved_crashes - prev_saved_crashes; + prev_saved_crashes = afl->saved_crashes; + + } + + if (unlikely(afl->saved_tmouts > prev_saved_tmouts)) { + + afl->queue_cur->stats_tmouts += afl->saved_tmouts - prev_saved_tmouts; + prev_saved_tmouts = afl->saved_tmouts; + + } + + } + + #endif if (unlikely(!afl->stop_soon && exit_1)) { afl->stop_soon = 2; } if (unlikely(afl->old_seed_selection)) { while (++afl->current_entry < afl->queued_items && - afl->queue_buf[afl->current_entry]->disabled) - ; + afl->queue_buf[afl->current_entry]->disabled) {}; if (unlikely(afl->current_entry >= afl->queued_items || afl->queue_buf[afl->current_entry] == NULL || - afl->queue_buf[afl->current_entry]->disabled)) + afl->queue_buf[afl->current_entry]->disabled)) { + afl->queue_cur = NULL; - else + + } else { + afl->queue_cur = afl->queue_buf[afl->current_entry]; + } + } } while (skipped_fuzz && afl->queue_cur && !afl->stop_soon); @@ -2558,6 +2620,7 @@ int main(int argc, char **argv_orig, char **envp) { stop_fuzzing: afl->force_ui_update = 1; // ensure the screen is reprinted + afl->stop_soon = 1; // ensure everything is written show_stats(afl); // print the screen one last time write_bitmap(afl); save_auto(afl); diff --git a/src/afl-gotcpu.c b/src/afl-gotcpu.c index 539206ce..4f851099 100644 --- a/src/afl-gotcpu.c +++ b/src/afl-gotcpu.c @@ -9,7 +9,7 @@ Andrea Fioraldi <andreafioraldi@gmail.com> Copyright 2016, 2017 Google Inc. All rights reserved. - Copyright 2019-2022 AFLplusplus Project. All rights reserved. + Copyright 2019-2023 AFLplusplus Project. All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -19,7 +19,8 @@ This tool provides a fairly accurate measurement of CPU preemption rate. It is meant to complement the quick-and-dirty load average widget shown - in the afl-fuzz UI. See docs/parallel_fuzzing.md for more info. + in the afl-fuzz UI. See docs/fuzzing_in_depth.md#c-using-multiple-cores + for more info. For some work loads, the tool may actually suggest running more instances than you have CPU cores. This can happen if the tested program is spending @@ -91,7 +92,7 @@ static u32 measure_preemption(u32 target_ms) { volatile u32 v1, v2 = 0; u64 st_t, en_t, st_c, en_c, real_delta, slice_delta; - s32 loop_repeats = 0; + // s32 loop_repeats = 0; st_t = get_cur_time_us(); st_c = get_cpu_usage_us(); @@ -112,7 +113,7 @@ repeat_loop: if (en_t - st_t < target_ms * 1000) { - loop_repeats++; + // loop_repeats++; goto repeat_loop; } @@ -173,7 +174,12 @@ int main(int argc, char **argv) { if (c == NULL) PFATAL("cpuset_create failed"); cpuset_set(i, c); - #elif defined(__APPLE__) + #elif defined(__APPLE__) && defined(__x86_64__) + // the api is not workable on arm64, core's principle + // differs significantly hive of core per type vs individual ones. + // Possible TODO: For arm64 is to slightly change the meaning + // of gotcpu since it makes no sense on this platform + // but rather just displaying current policy ? thread_affinity_policy_data_t c = {i}; thread_port_t native_thread = pthread_mach_thread_np(pthread_self()); if (thread_policy_set(native_thread, THREAD_AFFINITY_POLICY, @@ -208,7 +214,13 @@ int main(int argc, char **argv) { #if defined(__linux__) if (sched_setaffinity(0, sizeof(c), &c)) { - PFATAL("sched_setaffinity failed for cpu %d", i); + const char *error_code = "Unkown error code"; + if (errno == EFAULT) error_code = "EFAULT"; + if (errno == EINVAL) error_code = "EINVAL"; + if (errno == EPERM) error_code = "EPERM"; + if (errno == ESRCH) error_code = "ESRCH"; + + PFATAL("sched_setaffinity failed for cpu %d, error: %s", i, error_code); } diff --git a/src/afl-ld-lto.c b/src/afl-ld-lto.c index 5797def8..5438bd9f 100644 --- a/src/afl-ld-lto.c +++ b/src/afl-ld-lto.c @@ -9,7 +9,7 @@ Andrea Fioraldi <andreafioraldi@gmail.com> Dominik Maier <domenukk@gmail.com> - Copyright 2019-2022 AFLplusplus Project. All rights reserved. + Copyright 2019-2023 AFLplusplus Project. All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/src/afl-sharedmem.c b/src/afl-sharedmem.c index b48c6fb3..a2c81586 100644 --- a/src/afl-sharedmem.c +++ b/src/afl-sharedmem.c @@ -11,7 +11,7 @@ Andrea Fioraldi <andreafioraldi@gmail.com> Copyright 2016, 2017 Google Inc. All rights reserved. - Copyright 2019-2022 AFLplusplus Project. All rights reserved. + Copyright 2019-2023 AFLplusplus Project. All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/src/afl-showmap.c b/src/afl-showmap.c index 07f30326..b5a61de5 100644 --- a/src/afl-showmap.c +++ b/src/afl-showmap.c @@ -12,7 +12,7 @@ Dominik Maier <mail@dmnk.co> Copyright 2016, 2017 Google Inc. All rights reserved. - Copyright 2019-2022 AFLplusplus Project. All rights reserved. + Copyright 2019-2023 AFLplusplus Project. All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -30,8 +30,10 @@ */ #define AFL_MAIN +#define AFL_SHOWMAP #include "config.h" +#include "afl-fuzz.h" #include "types.h" #include "debug.h" #include "alloc-inl.h" @@ -62,6 +64,8 @@ #include <sys/types.h> #include <sys/resource.h> +static afl_state_t *afl; + static char *stdin_file; /* stdin file */ static u8 *in_dir = NULL, /* input folder */ @@ -129,7 +133,7 @@ static void kill_child() { timed_out = 1; if (fsrv->child_pid > 0) { - kill(fsrv->child_pid, fsrv->kill_signal); + kill(fsrv->child_pid, fsrv->child_kill_signal); fsrv->child_pid = -1; } @@ -308,12 +312,73 @@ static u32 write_results_to_file(afl_forkserver_t *fsrv, u8 *outfile) { } +void pre_afl_fsrv_write_to_testcase(afl_forkserver_t *fsrv, u8 *mem, u32 len) { + + static u8 buf[MAX_FILE]; + u32 sent = 0; + + if (unlikely(afl->custom_mutators_count)) { + + ssize_t new_size = len; + u8 *new_mem = mem; + u8 *new_buf = NULL; + + LIST_FOREACH(&afl->custom_mutator_list, struct custom_mutator, { + + if (el->afl_custom_post_process) { + + new_size = + el->afl_custom_post_process(el->data, new_mem, new_size, &new_buf); + + if (unlikely(!new_buf || new_size <= 0)) { + + return; + + } else { + + new_mem = new_buf; + len = new_size; + + } + + } + + }); + + if (new_mem != mem && new_mem != NULL) { + + mem = buf; + memcpy(mem, new_mem, new_size); + + } + + if (unlikely(afl->custom_mutators_count)) { + + LIST_FOREACH(&afl->custom_mutator_list, struct custom_mutator, { + + if (el->afl_custom_fuzz_send) { + + el->afl_custom_fuzz_send(el->data, mem, len); + sent = 1; + + } + + }); + + } + + } + + if (likely(!sent)) { afl_fsrv_write_to_testcase(fsrv, mem, len); } + +} + /* Execute target application. */ static void showmap_run_target_forkserver(afl_forkserver_t *fsrv, u8 *mem, u32 len) { - afl_fsrv_write_to_testcase(fsrv, mem, len); + pre_afl_fsrv_write_to_testcase(fsrv, mem, len); if (!quiet_mode) { SAYF("-- Program output begins --\n" cRST); } @@ -434,6 +499,23 @@ static u32 read_file(u8 *in_file) { } +#ifdef __linux__ +/* Execute the target application with an empty input (in Nyx mode). */ +static void showmap_run_target_nyx_mode(afl_forkserver_t *fsrv) { + + afl_fsrv_write_to_testcase(fsrv, NULL, 0); + + if (afl_fsrv_run_target(fsrv, fsrv->exec_tmout, &stop_soon) == + FSRV_RUN_ERROR) { + + FATAL("Error running target in Nyx mode"); + + } + +} + +#endif + /* Execute target application. */ static void showmap_run_target(afl_forkserver_t *fsrv, char **argv) { @@ -515,11 +597,11 @@ static void showmap_run_target(afl_forkserver_t *fsrv, char **argv) { it.it_value.tv_sec = (fsrv->exec_tmout / 1000); it.it_value.tv_usec = (fsrv->exec_tmout % 1000) * 1000; - } + signal(SIGALRM, kill_child); - signal(SIGALRM, kill_child); + setitimer(ITIMER_REAL, &it, NULL); - setitimer(ITIMER_REAL, &it, NULL); + } if (waitpid(fsrv->child_pid, &status, 0) <= 0) { FATAL("waitpid() failed"); } @@ -597,49 +679,8 @@ static void set_up_environment(afl_forkserver_t *fsrv, char **argv) { char *afl_preload; char *frida_afl_preload = NULL; - setenv("ASAN_OPTIONS", - "abort_on_error=1:" - "detect_leaks=0:" - "allocator_may_return_null=1:" - "symbolize=0:" - "detect_odr_violation=0:" - "handle_segv=0:" - "handle_sigbus=0:" - "handle_abort=0:" - "handle_sigfpe=0:" - "handle_sigill=0", - 0); - - setenv("LSAN_OPTIONS", - "exitcode=" STRINGIFY(LSAN_ERROR) ":" - "fast_unwind_on_malloc=0:" - "symbolize=0:" - "print_suppressions=0", - 0); - - setenv("UBSAN_OPTIONS", - "halt_on_error=1:" - "abort_on_error=1:" - "malloc_context_size=0:" - "allocator_may_return_null=1:" - "symbolize=0:" - "handle_segv=0:" - "handle_sigbus=0:" - "handle_abort=0:" - "handle_sigfpe=0:" - "handle_sigill=0", - 0); - - setenv("MSAN_OPTIONS", "exit_code=" STRINGIFY(MSAN_ERROR) ":" - "abort_on_error=1:" - "msan_track_origins=0" - "allocator_may_return_null=1:" - "symbolize=0:" - "handle_segv=0:" - "handle_sigbus=0:" - "handle_abort=0:" - "handle_sigfpe=0:" - "handle_sigill=0", 0); + + set_sanitizer_defaults(); if (get_afl_env("AFL_PRELOAD")) { @@ -695,7 +736,11 @@ static void setup_signal_handlers(void) { struct sigaction sa; sa.sa_handler = NULL; +#ifdef SA_RESTART sa.sa_flags = SA_RESTART; +#else + sa.sa_flags = 0; +#endif sa.sa_sigaction = NULL; sigemptyset(&sa.sa_mask); @@ -822,8 +867,8 @@ static void usage(u8 *argv0) { " -o file - file to write the trace data to\n\n" "Execution control settings:\n" - " -t msec - timeout for each run (none)\n" - " -m megs - memory limit for child process (%u MB)\n" + " -t msec - timeout for each run (default: 1000ms)\n" + " -m megs - memory limit for child process (default: none)\n" #if defined(__linux__) && defined(__aarch64__) " -A - use binary-only instrumentation (ARM CoreSight mode)\n" #endif @@ -834,6 +879,7 @@ static void usage(u8 *argv0) { " -W - use qemu-based instrumentation with Wine (Wine mode)\n" " (Not necessary, here for consistency with other afl-* " "tools)\n" + " -X - use Nyx mode\n" #endif "\n" "Other settings:\n" @@ -854,6 +900,10 @@ static void usage(u8 *argv0) { "This tool displays raw tuple data captured by AFL instrumentation.\n" "For additional help, consult %s/README.md.\n\n" + "If you use -i mode, then custom mutator post_process send send " + "functionality\n" + "is supported.\n\n" + "Environment variables used:\n" "LD_BIND_LAZY: do not set LD_BIND_NOW env var for target\n" "AFL_CMIN_CRASHES_ONLY: (cmin_mode) only write tuples for crashing " @@ -865,15 +915,22 @@ static void usage(u8 *argv0) { "AFL_FORKSRV_INIT_TMOUT: time spent waiting for forkserver during " "startup (in milliseconds)\n" "AFL_KILL_SIGNAL: Signal ID delivered to child processes on timeout, " - "etc. (default: SIGKILL)\n" + "etc.\n" + " (default: SIGKILL)\n" + "AFL_FORK_SERVER_KILL_SIGNAL: Kill signal for the fork server on " + "termination\n" + " (default: SIGTERM). If unset and " + "AFL_KILL_SIGNAL is\n" + " set, that value will be used.\n" "AFL_MAP_SIZE: the shared memory size for that target. must be >= the " - "size the target was compiled for\n" + "size the\n" + " target was compiled for\n" "AFL_PRELOAD: LD_PRELOAD / DYLD_INSERT_LIBRARIES settings for target\n" - "AFL_PRINT_FILENAMES: If set, the filename currently processed will be " - "printed to stdout\n" + "AFL_PRINT_FILENAMES: Print the queue entry currently processed will to " + "stdout\n" "AFL_QUIET: do not print extra informational output\n" "AFL_NO_FORKSRV: run target via execve instead of using the forkserver\n", - argv0, MEM_LIMIT, doc_path); + argv0, doc_path); exit(1); @@ -905,7 +962,7 @@ int main(int argc, char **argv_orig, char **envp) { if (getenv("AFL_QUIET") != NULL) { be_quiet = true; } - while ((opt = getopt(argc, argv, "+i:o:f:m:t:AeqCZOH:QUWbcrsh")) > 0) { + while ((opt = getopt(argc, argv, "+i:o:f:m:t:AeqCZOH:QUWbcrshXY")) > 0) { switch (opt) { @@ -1009,6 +1066,16 @@ int main(int argc, char **argv_orig, char **envp) { } + } else { + + // The forkserver code does not have a way to completely + // disable the timeout, so we'll use a very, very long + // timeout instead. + WARNF( + "Setting an execution timeout of 120 seconds ('none' is not " + "allowed)."); + fsrv->exec_tmout = 120 * 1000; + } break; @@ -1083,6 +1150,23 @@ int main(int argc, char **argv_orig, char **envp) { break; + case 'Y': // fallthough +#ifdef __linux__ + case 'X': /* NYX mode */ + + if (fsrv->nyx_mode) { FATAL("Multiple -X options not supported"); } + + fsrv->nyx_mode = 1; + fsrv->nyx_parent = true; + fsrv->nyx_standalone = true; + + break; +#else + case 'X': + FATAL("Nyx mode is only availabe on linux..."); + break; +#endif + case 'b': /* Secret undocumented mode. Writes output in raw binary format @@ -1154,7 +1238,21 @@ int main(int argc, char **argv_orig, char **envp) { set_up_environment(fsrv, argv); +#ifdef __linux__ + if (!fsrv->nyx_mode) { + + fsrv->target_path = find_binary(argv[optind]); + + } else { + + fsrv->target_path = ck_strdup(argv[optind]); + + } + +#else fsrv->target_path = find_binary(argv[optind]); +#endif + fsrv->trace_bits = afl_shm_init(&shm, map_size, 0); if (!quiet_mode) { @@ -1210,13 +1308,75 @@ int main(int argc, char **argv_orig, char **envp) { use_argv = get_cs_argv(argv[0], &fsrv->target_path, argc - optind, argv + optind); +#ifdef __linux__ + + } else if (fsrv->nyx_mode) { + + use_argv = ck_alloc(sizeof(char *) * (1)); + use_argv[0] = argv[0]; + + fsrv->nyx_id = 0; + + u8 *libnyx_binary = find_afl_binary(use_argv[0], "libnyx.so"); + fsrv->nyx_handlers = afl_load_libnyx_plugin(libnyx_binary); + if (fsrv->nyx_handlers == NULL) { + + FATAL("failed to initialize libnyx.so..."); + + } + + fsrv->nyx_use_tmp_workdir = true; + fsrv->nyx_bind_cpu_id = 0; +#endif + } else { use_argv = argv + optind; } + afl = calloc(1, sizeof(afl_state_t)); + + if (getenv("AFL_FORKSRV_INIT_TMOUT")) { + + s32 forksrv_init_tmout = atoi(getenv("AFL_FORKSRV_INIT_TMOUT")); + if (forksrv_init_tmout < 1) { + + FATAL("Bad value specified for AFL_FORKSRV_INIT_TMOUT"); + + } + + fsrv->init_tmout = (u32)forksrv_init_tmout; + + } + + if (getenv("AFL_CRASH_EXITCODE")) { + + long exitcode = strtol(getenv("AFL_CRASH_EXITCODE"), NULL, 10); + if ((!exitcode && (errno == EINVAL || errno == ERANGE)) || + exitcode < -127 || exitcode > 128) { + + FATAL("Invalid crash exitcode, expected -127 to 128, but got %s", + getenv("AFL_CRASH_EXITCODE")); + + } + + fsrv->uses_crash_exitcode = true; + // WEXITSTATUS is 8 bit unsigned + fsrv->crash_exitcode = (u8)exitcode; + + } + +#ifdef __linux__ + if (!fsrv->nyx_mode && in_dir) { + + (void)check_binary_signatures(fsrv->target_path); + + } + +#else if (in_dir) { (void)check_binary_signatures(fsrv->target_path); } +#endif shm_fuzz = ck_alloc(sizeof(sharedmem_t)); @@ -1236,16 +1396,29 @@ int main(int argc, char **argv_orig, char **envp) { fsrv->shmem_fuzz_len = (u32 *)map; fsrv->shmem_fuzz = map + sizeof(u32); + configure_afl_kill_signals(fsrv, NULL, NULL, + (fsrv->qemu_mode || unicorn_mode +#ifdef __linux__ + || fsrv->nyx_mode +#endif + ) + ? SIGKILL + : SIGTERM); + if (!fsrv->cs_mode && !fsrv->qemu_mode && !unicorn_mode) { u32 save_be_quiet = be_quiet; be_quiet = !debug; if (map_size > 4194304) { - fsrv->map_size = map_size; - } - else { - fsrv->map_size = 4194304; // dummy temporary value + + fsrv->map_size = map_size; + + } else { + + fsrv->map_size = 4194304; // dummy temporary value + } + u32 new_map_size = afl_fsrv_get_mapsize(fsrv, use_argv, &stop_soon, (get_afl_env("AFL_DEBUG_CHILD") || @@ -1254,9 +1427,6 @@ int main(int argc, char **argv_orig, char **envp) { : 0); be_quiet = save_be_quiet; - fsrv->kill_signal = - parse_afl_kill_signal_env(getenv("AFL_KILL_SIGNAL"), SIGKILL); - if (new_map_size) { // only reinitialize when it makes sense @@ -1264,7 +1434,7 @@ int main(int argc, char **argv_orig, char **envp) { (new_map_size > map_size && new_map_size - map_size > MAP_SIZE)) { if (!be_quiet) - ACTF("Aquired new map size for target: %u bytes\n", new_map_size); + ACTF("Acquired new map size for target: %u bytes\n", new_map_size); afl_shm_deinit(&shm); afl_fsrv_kill(fsrv); @@ -1283,6 +1453,26 @@ int main(int argc, char **argv_orig, char **envp) { if (in_dir) { + afl->fsrv.dev_urandom_fd = open("/dev/urandom", O_RDONLY); + afl->afl_env.afl_custom_mutator_library = + getenv("AFL_CUSTOM_MUTATOR_LIBRARY"); + afl->afl_env.afl_python_module = getenv("AFL_PYTHON_MODULE"); + setup_custom_mutators(afl); + + } else { + + if (getenv("AFL_CUSTOM_MUTATOR_LIBRARY") || getenv("AFL_PYTHON_MODULE")) { + + WARNF( + "Custom mutator environment detected, this is only supported in -i " + "mode!\n"); + + } + + } + + if (in_dir) { + DIR *dir_in, *dir_out = NULL; if (getenv("AFL_DEBUG_GDB")) wait_for_gdb = true; @@ -1343,36 +1533,6 @@ int main(int argc, char **argv_orig, char **envp) { } - if (getenv("AFL_FORKSRV_INIT_TMOUT")) { - - s32 forksrv_init_tmout = atoi(getenv("AFL_FORKSRV_INIT_TMOUT")); - if (forksrv_init_tmout < 1) { - - FATAL("Bad value specified for AFL_FORKSRV_INIT_TMOUT"); - - } - - fsrv->init_tmout = (u32)forksrv_init_tmout; - - } - - if (getenv("AFL_CRASH_EXITCODE")) { - - long exitcode = strtol(getenv("AFL_CRASH_EXITCODE"), NULL, 10); - if ((!exitcode && (errno == EINVAL || errno == ERANGE)) || - exitcode < -127 || exitcode > 128) { - - FATAL("Invalid crash exitcode, expected -127 to 128, but got %s", - getenv("AFL_CRASH_EXITCODE")); - - } - - fsrv->uses_crash_exitcode = true; - // WEXITSTATUS is 8 bit unsigned - fsrv->crash_exitcode = (u8)exitcode; - - } - afl_fsrv_start(fsrv, use_argv, &stop_soon, (get_afl_env("AFL_DEBUG_CHILD") || get_afl_env("AFL_DEBUG_CHILD_OUTPUT")) @@ -1406,7 +1566,20 @@ int main(int argc, char **argv_orig, char **envp) { if (fsrv->support_shmem_fuzz && !fsrv->use_shmem_fuzz) shm_fuzz = deinit_shmem(fsrv, shm_fuzz); - showmap_run_target(fsrv, use_argv); +#ifdef __linux__ + if (!fsrv->nyx_mode) { + +#endif + showmap_run_target(fsrv, use_argv); +#ifdef __linux__ + + } else { + + showmap_run_target_nyx_mode(fsrv); + + } + +#endif tcnt = write_results_to_file(fsrv, out_file); if (!quiet_mode) { diff --git a/src/afl-tmin.c b/src/afl-tmin.c index 78537f9f..e7442d1d 100644 --- a/src/afl-tmin.c +++ b/src/afl-tmin.c @@ -12,7 +12,7 @@ Dominik Maier <mail@dmnk.co> Copyright 2016, 2017 Google Inc. All rights reserved. - Copyright 2019-2022 AFLplusplus Project. All rights reserved. + Copyright 2019-2023 AFLplusplus Project. All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -113,7 +113,7 @@ static void kill_child() { if (fsrv->child_pid > 0) { - kill(fsrv->child_pid, fsrv->kill_signal); + kill(fsrv->child_pid, fsrv->child_kill_signal); fsrv->child_pid = -1; } @@ -674,27 +674,6 @@ static void set_up_environment(afl_forkserver_t *fsrv, char **argv) { /* Set sane defaults... */ - x = get_afl_env("ASAN_OPTIONS"); - - if (x) { - - if (!strstr(x, "abort_on_error=1")) { - - FATAL("Custom ASAN_OPTIONS set without abort_on_error=1 - please fix!"); - - } - -#ifndef ASAN_BUILD - if (!getenv("AFL_DEBUG") && !strstr(x, "symbolize=0")) { - - FATAL("Custom ASAN_OPTIONS set without symbolize=0 - please fix!"); - - } - -#endif - - } - x = get_afl_env("MSAN_OPTIONS"); if (x) { @@ -706,69 +685,9 @@ static void set_up_environment(afl_forkserver_t *fsrv, char **argv) { } - if (!strstr(x, "symbolize=0")) { - - FATAL("Custom MSAN_OPTIONS set without symbolize=0 - please fix!"); - - } - } - x = get_afl_env("LSAN_OPTIONS"); - - if (x) { - - if (!strstr(x, "symbolize=0")) { - - FATAL("Custom LSAN_OPTIONS set without symbolize=0 - please fix!"); - - } - - } - - setenv("ASAN_OPTIONS", - "abort_on_error=1:" - "detect_leaks=0:" - "allocator_may_return_null=1:" - "symbolize=0:" - "detect_odr_violation=0:" - "handle_segv=0:" - "handle_sigbus=0:" - "handle_abort=0:" - "handle_sigfpe=0:" - "handle_sigill=0", - 0); - - setenv("UBSAN_OPTIONS", - "halt_on_error=1:" - "abort_on_error=1:" - "malloc_context_size=0:" - "allocator_may_return_null=1:" - "symbolize=0:" - "handle_segv=0:" - "handle_sigbus=0:" - "handle_abort=0:" - "handle_sigfpe=0:" - "handle_sigill=0", - 0); - - setenv("MSAN_OPTIONS", "exit_code=" STRINGIFY(MSAN_ERROR) ":" - "abort_on_error=1:" - "msan_track_origins=0" - "allocator_may_return_null=1:" - "symbolize=0:" - "handle_segv=0:" - "handle_sigbus=0:" - "handle_abort=0:" - "handle_sigfpe=0:" - "handle_sigill=0", 0); - - setenv("LSAN_OPTIONS", - "exitcode=" STRINGIFY(LSAN_ERROR) ":" - "fast_unwind_on_malloc=0:" - "symbolize=0:" - "print_suppressions=0", - 0); + set_sanitizer_defaults(); if (get_afl_env("AFL_PRELOAD")) { @@ -824,7 +743,11 @@ static void setup_signal_handlers(void) { struct sigaction sa; sa.sa_handler = NULL; +#ifdef SA_RESTART sa.sa_flags = SA_RESTART; +#else + sa.sa_flags = 0; +#endif sa.sa_sigaction = NULL; sigemptyset(&sa.sa_mask); @@ -866,6 +789,7 @@ static void usage(u8 *argv0) { "mode)\n" " (Not necessary, here for consistency with other afl-* " "tools)\n" + " -X - use Nyx mode\n" #endif "\n" @@ -879,8 +803,12 @@ static void usage(u8 *argv0) { "Environment variables used:\n" "AFL_CRASH_EXITCODE: optional child exit code to be interpreted as crash\n" - "AFL_FORKSRV_INIT_TMOUT: time spent waiting for forkserver during startup (in milliseconds)\n" - "AFL_KILL_SIGNAL: Signal ID delivered to child processes on timeout, etc. (default: SIGKILL)\n" + "AFL_FORKSRV_INIT_TMOUT: time spent waiting for forkserver during startup (in ms)\n" + "AFL_KILL_SIGNAL: Signal ID delivered to child processes on timeout, etc.\n" + " (default: SIGKILL)\n" + "AFL_FORK_SERVER_KILL_SIGNAL: Kill signal for the fork server on termination\n" + " (default: SIGTERM). If unset and AFL_KILL_SIGNAL is\n" + " set, that value will be used.\n" "AFL_MAP_SIZE: the shared memory size for that target. must be >= the size\n" " the target was compiled for\n" "AFL_PRELOAD: LD_PRELOAD / DYLD_INSERT_LIBRARIES settings for target\n" @@ -918,7 +846,7 @@ int main(int argc, char **argv_orig, char **envp) { SAYF(cCYA "afl-tmin" VERSION cRST " by Michal Zalewski\n"); - while ((opt = getopt(argc, argv, "+i:o:f:m:t:B:xeAOQUWHh")) > 0) { + while ((opt = getopt(argc, argv, "+i:o:f:m:t:B:xeAOQUWXYHh")) > 0) { switch (opt) { @@ -1076,6 +1004,23 @@ int main(int argc, char **argv_orig, char **envp) { break; + case 'Y': // fallthough +#ifdef __linux__ + case 'X': /* NYX mode */ + + if (fsrv->nyx_mode) { FATAL("Multiple -X options not supported"); } + + fsrv->nyx_mode = 1; + fsrv->nyx_parent = true; + fsrv->nyx_standalone = true; + + break; +#else + case 'X': + FATAL("Nyx mode is only availabe on linux..."); + break; +#endif + case 'H': /* Hang Mode */ /* Minimizes a testcase to the minimum that still times out */ @@ -1141,7 +1086,21 @@ int main(int argc, char **argv_orig, char **envp) { set_up_environment(fsrv, argv); +#ifdef __linux__ + if (!fsrv->nyx_mode) { + + fsrv->target_path = find_binary(argv[optind]); + + } else { + + fsrv->target_path = ck_strdup(argv[optind]); + + } + +#else fsrv->target_path = find_binary(argv[optind]); +#endif + fsrv->trace_bits = afl_shm_init(&shm, map_size, 0); detect_file_args(argv + optind, out_file, &fsrv->use_stdin); signal(SIGALRM, kill_child); @@ -1165,6 +1124,26 @@ int main(int argc, char **argv_orig, char **envp) { use_argv = get_cs_argv(argv[0], &fsrv->target_path, argc - optind, argv + optind); +#ifdef __linux__ + + } else if (fsrv->nyx_mode) { + + fsrv->nyx_id = 0; + + u8 *libnyx_binary = find_afl_binary(argv[0], "libnyx.so"); + fsrv->nyx_handlers = afl_load_libnyx_plugin(libnyx_binary); + if (fsrv->nyx_handlers == NULL) { + + FATAL("failed to initialize libnyx.so..."); + + } + + fsrv->nyx_use_tmp_workdir = true; + fsrv->nyx_bind_cpu_id = 0; + + use_argv = argv + optind; +#endif + } else { use_argv = argv + optind; @@ -1195,8 +1174,8 @@ int main(int argc, char **argv_orig, char **envp) { } - fsrv->kill_signal = - parse_afl_kill_signal_env(getenv("AFL_KILL_SIGNAL"), SIGKILL); + configure_afl_kill_signals( + fsrv, NULL, NULL, (fsrv->qemu_mode || unicorn_mode) ? SIGKILL : SIGTERM); if (getenv("AFL_CRASH_EXITCODE")) { @@ -1234,7 +1213,12 @@ int main(int argc, char **argv_orig, char **envp) { fsrv->shmem_fuzz = map + sizeof(u32); read_initial_file(); + +#ifdef __linux__ + if (!fsrv->nyx_mode) { (void)check_binary_signatures(fsrv->target_path); } +#else (void)check_binary_signatures(fsrv->target_path); +#endif if (!fsrv->qemu_mode && !unicorn_mode) { @@ -1252,7 +1236,7 @@ int main(int argc, char **argv_orig, char **envp) { (new_map_size > map_size && new_map_size - map_size > MAP_SIZE)) { if (!be_quiet) - ACTF("Aquired new map size for target: %u bytes\n", new_map_size); + ACTF("Acquired new map size for target: %u bytes\n", new_map_size); afl_shm_deinit(&shm); afl_fsrv_kill(fsrv); diff --git a/test-instr.c b/test-instr.c index f304e208..1d9f2e6e 100644 --- a/test-instr.c +++ b/test-instr.c @@ -3,7 +3,7 @@ -------------------------------------------------------- Originally written by Michal Zalewski Copyright 2014 Google Inc. All rights reserved. - Copyright 2019-2022 AFLplusplus Project. All rights reserved. + Copyright 2019-2023 AFLplusplus Project. All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at: diff --git a/test/test-all.sh b/test/test-all.sh index 0c189727..3cb692ca 100755 --- a/test/test-all.sh +++ b/test/test-all.sh @@ -23,3 +23,5 @@ . ./test-unittests.sh . ./test-post.sh + +exit 0 \ No newline at end of file diff --git a/test/test-basic.sh b/test/test-basic.sh index bec42b4d..5bb2ca28 100755 --- a/test/test-basic.sh +++ b/test/test-basic.sh @@ -7,9 +7,10 @@ AFL_GCC=afl-gcc $ECHO "$BLUE[*] Testing: ${AFL_GCC}, afl-showmap, afl-fuzz, afl-cmin and afl-tmin" test "$SYS" = "i686" -o "$SYS" = "x86_64" -o "$SYS" = "amd64" -o "$SYS" = "i86pc" -o "$SYS" = "i386" && { test -e ../${AFL_GCC} -a -e ../afl-showmap -a -e ../afl-fuzz && { - ../${AFL_GCC} -o test-instr.plain -O0 ../test-instr.c > /dev/null 2>&1 - AFL_HARDEN=1 ../${AFL_GCC} -o test-compcov.harden test-compcov.c > /dev/null 2>&1 - test -e test-instr.plain && { + ../${AFL_GCC} -v 2>&1 | grep -qi "gcc version" && { + ../${AFL_GCC} -o test-instr.plain -O0 ../test-instr.c > /dev/null 2>&1 + AFL_HARDEN=1 ../${AFL_GCC} -o test-compcov.harden test-compcov.c > /dev/null 2>&1 + test -e test-instr.plain && { $ECHO "$GREEN[+] ${AFL_GCC} compilation succeeded" echo 0 | AFL_QUIET=1 ../afl-showmap -m ${MEM_LIMIT} -o test-instr.plain.0 -r -- ./test-instr.plain > /dev/null 2>&1 AFL_QUIET=1 ../afl-showmap -m ${MEM_LIMIT} -o test-instr.plain.1 -r -- ./test-instr.plain < /dev/null > /dev/null 2>&1 @@ -35,41 +36,41 @@ test "$SYS" = "i686" -o "$SYS" = "x86_64" -o "$SYS" = "amd64" -o "$SYS" = "i86pc } test "$TUPLES" -lt 3 && SKIP=1 true # this is needed because of the test above - } || { + } || { $ECHO "$RED[!] ${AFL_GCC} failed" echo CUT------------------------------------------------------------------CUT uname -a ../${AFL_GCC} -o test-instr.plain -O0 ../test-instr.c echo CUT------------------------------------------------------------------CUT CODE=1 - } - test -e test-compcov.harden && { + } + test -e test-compcov.harden && { nm test-compcov.harden | grep -Eq 'stack_chk_fail|fstack-protector-all|fortified' > /dev/null 2>&1 && { $ECHO "$GREEN[+] ${AFL_GCC} hardened mode succeeded and is working" } || { $ECHO "$RED[!] ${AFL_GCC} hardened mode is not hardened" - env | egrep 'AFL|PATH|LLVM' + env | grep -E 'AFL|PATH|LLVM' AFL_DEBUG=1 AFL_HARDEN=1 ../${AFL_GCC} -o test-compcov.harden test-compcov.c nm test-compcov.harden CODE=1 } rm -f test-compcov.harden - } || { + } || { $ECHO "$RED[!] ${AFL_GCC} hardened mode compilation failed" CODE=1 - } - # now we want to be sure that afl-fuzz is working - # make sure crash reporter is disabled on Mac OS X - (test "$(uname -s)" = "Darwin" && test $(launchctl list 2>/dev/null | grep -q '\.ReportCrash$') && { + } + # now we want to be sure that afl-fuzz is working + # make sure crash reporter is disabled on Mac OS X + (test "$(uname -s)" = "Darwin" && test $(launchctl list 2>/dev/null | grep -q '\.ReportCrash$') && { $ECHO "$RED[!] we cannot run afl-fuzz with enabled crash reporter. Run 'sudo sh afl-system-config'.$RESET" true - }) || { + }) || { mkdir -p in echo 0 > in/in test -z "$SKIP" && { $ECHO "$GREY[*] running afl-fuzz for ${AFL_GCC}, this will take approx 10 seconds" { - ../afl-fuzz -V10 -m ${MEM_LIMIT} -i in -o out -D -- ./test-instr.plain >>errors 2>&1 + ../afl-fuzz -V07 -m ${MEM_LIMIT} -i in -o out -- ./test-instr.plain >>errors 2>&1 } >>errors 2>&1 test -n "$( ls out/default/queue/id:000002* 2>/dev/null )" && { $ECHO "$GREEN[+] afl-fuzz is working correctly with ${AFL_GCC}" @@ -116,83 +117,89 @@ test "$SYS" = "i686" -o "$SYS" = "x86_64" -o "$SYS" = "amd64" -o "$SYS" = "i86pc } rm -rf in out errors in2 unset AFL_QUIET + } + rm -f test-instr.plain + } || { + $ECHO "$YELLOW[-] afl-gcc executes clang, cannot test!" + INCOMPLETE=1 } - rm -f test-instr.plain } || { - $ECHO "$YELLOW[-] afl is not compiled, cannot test" - INCOMPLETE=1 + $ECHO "$YELLOW[-] afl is not compiled, cannot test" + INCOMPLETE=1 } - if [ ${AFL_GCC} = "afl-gcc" ] ; then AFL_GCC=afl-clang ; else AFL_GCC=afl-gcc ; fi - $ECHO "$BLUE[*] Testing: ${AFL_GCC}, afl-showmap, afl-fuzz, afl-cmin and afl-tmin" + + AFL_CLANG=afl-clang + $ECHO "$BLUE[*] Testing: ${AFL_CLANG}, afl-showmap, afl-fuzz, afl-cmin and afl-tmin" SKIP= - test -e ../${AFL_GCC} -a -e ../afl-showmap -a -e ../afl-fuzz && { - ../${AFL_GCC} -o test-instr.plain -O0 ../test-instr.c > /dev/null 2>&1 - AFL_HARDEN=1 ../${AFL_GCC} -o test-compcov.harden test-compcov.c > /dev/null 2>&1 - test -e test-instr.plain && { - $ECHO "$GREEN[+] ${AFL_GCC} compilation succeeded" + test -e ../${AFL_CLANG} -a -e ../afl-showmap -a -e ../afl-fuzz && { + ../${AFL_CLANG} -v 2>&1 | grep -qi "clang version" && { + ../${AFL_CLANG} -O0 -o test-instr.plain ../test-instr.c > /dev/null 2>&1 + AFL_HARDEN=1 ../${AFL_CLANG} -o test-compcov.harden test-compcov.c > /dev/null 2>&1 + test -e test-instr.plain && { + $ECHO "$GREEN[+] ${AFL_CLANG} compilation succeeded" echo 0 | AFL_QUIET=1 ../afl-showmap -m ${MEM_LIMIT} -o test-instr.plain.0 -r -- ./test-instr.plain > /dev/null 2>&1 AFL_QUIET=1 ../afl-showmap -m ${MEM_LIMIT} -o test-instr.plain.1 -r -- ./test-instr.plain < /dev/null > /dev/null 2>&1 test -e test-instr.plain.0 -a -e test-instr.plain.1 && { diff test-instr.plain.0 test-instr.plain.1 > /dev/null 2>&1 && { - $ECHO "$RED[!] ${AFL_GCC} instrumentation should be different on different input but is not" + $ECHO "$RED[!] ${AFL_CLANG} instrumentation should be different on different input but is not" CODE=1 } || { - $ECHO "$GREEN[+] ${AFL_GCC} instrumentation present and working correctly" + $ECHO "$GREEN[+] ${AFL_CLANG} instrumentation present and working correctly" } } || { - $ECHO "$RED[!] ${AFL_GCC} instrumentation failed" + $ECHO "$RED[!] ${AFL_CLANG} instrumentation failed" CODE=1 } rm -f test-instr.plain.0 test-instr.plain.1 TUPLES=`echo 1|AFL_QUIET=1 ../afl-showmap -m ${MEM_LIMIT} -o /dev/null -- ./test-instr.plain 2>&1 | grep Captur | awk '{print$3}'` test "$TUPLES" -gt 1 -a "$TUPLES" -lt 12 && { - $ECHO "$GREEN[+] ${AFL_GCC} run reported $TUPLES instrumented locations which is fine" + $ECHO "$GREEN[+] ${AFL_CLANG} run reported $TUPLES instrumented locations which is fine" } || { - $ECHO "$RED[!] ${AFL_GCC} instrumentation produces weird numbers: $TUPLES" + $ECHO "$RED[!] ${AFL_CLANG} instrumentation produces weird numbers: $TUPLES" CODE=1 } test "$TUPLES" -lt 3 && SKIP=1 true # this is needed because of the test above - } || { - $ECHO "$RED[!] ${AFL_GCC} failed" + } || { + $ECHO "$RED[!] ${AFL_CLANG} failed" echo CUT------------------------------------------------------------------CUT uname -a - ../${AFL_GCC} -o test-instr.plain ../test-instr.c + ../${AFL_CLANG} -o test-instr.plain ../test-instr.c echo CUT------------------------------------------------------------------CUT CODE=1 - } - test -e test-compcov.harden && { + } + test -e test-compcov.harden && { nm test-compcov.harden | grep -Eq 'stack_chk_fail|fstack-protector-all|fortified' > /dev/null 2>&1 && { - $ECHO "$GREEN[+] ${AFL_GCC} hardened mode succeeded and is working" + $ECHO "$GREEN[+] ${AFL_CLANG} hardened mode succeeded and is working" } || { - $ECHO "$RED[!] ${AFL_GCC} hardened mode is not hardened" + $ECHO "$RED[!] ${AFL_CLANG} hardened mode is not hardened" CODE=1 } rm -f test-compcov.harden - } || { - $ECHO "$RED[!] ${AFL_GCC} hardened mode compilation failed" + } || { + $ECHO "$RED[!] ${AFL_CLANG} hardened mode compilation failed" CODE=1 - } - # now we want to be sure that afl-fuzz is working - # make sure crash reporter is disabled on Mac OS X - (test "$(uname -s)" = "Darwin" && test $(launchctl list 2>/dev/null | grep -q '\.ReportCrash$') && { + } + # now we want to be sure that afl-fuzz is working + # make sure crash reporter is disabled on Mac OS X + (test "$(uname -s)" = "Darwin" && test $(launchctl list 2>/dev/null | grep -q '\.ReportCrash$') && { $ECHO "$RED[!] we cannot run afl-fuzz with enabled crash reporter. Run 'sudo sh afl-system-config'.$RESET" true - }) || { + }) || { mkdir -p in echo 0 > in/in test -z "$SKIP" && { - $ECHO "$GREY[*] running afl-fuzz for ${AFL_GCC}, this will take approx 10 seconds" + $ECHO "$GREY[*] running afl-fuzz for ${AFL_CLANG}, this will take approx 10 seconds" { - ../afl-fuzz -V10 -m ${MEM_LIMIT} -i in -o out -D -- ./test-instr.plain >>errors 2>&1 + ../afl-fuzz -V07 -m ${MEM_LIMIT} -i in -o out -- ./test-instr.plain >>errors 2>&1 } >>errors 2>&1 test -n "$( ls out/default/queue/id:000002* 2>/dev/null )" && { - $ECHO "$GREEN[+] afl-fuzz is working correctly with ${AFL_GCC}" + $ECHO "$GREEN[+] afl-fuzz is working correctly with ${AFL_CLANG}" } || { echo CUT------------------------------------------------------------------CUT cat errors echo CUT------------------------------------------------------------------CUT - $ECHO "$RED[!] afl-fuzz is not working correctly with ${AFL_GCC}" + $ECHO "$RED[!] afl-fuzz is not working correctly with ${AFL_CLANG}" CODE=1 } } @@ -247,8 +254,12 @@ test "$SYS" = "i686" -o "$SYS" = "x86_64" -o "$SYS" = "amd64" -o "$SYS" = "i86pc } rm -rf in out errors in2 unset AFL_QUIET + } + rm -f test-instr.plain + } || { + $ECHO "$YELLOW[-] afl-clang executes gcc, cannot test" + INCOMPLETE=1 } - rm -f test-instr.plain } || { $ECHO "$YELLOW[-] afl is not compiled, cannot test" INCOMPLETE=1 diff --git a/test/test-cmplog.c b/test/test-cmplog.c index d724ecaf..bd1b73e3 100644 --- a/test/test-cmplog.c +++ b/test/test-cmplog.c @@ -8,7 +8,7 @@ int LLVMFuzzerTestOneInput(const uint8_t *buf, size_t i) { - if (i < 30) return 0; + if (i < 30) return -1; if (buf[0] != 'A') return 0; if (buf[1] != 'B') return 0; if (buf[2] != 'C') return 0; diff --git a/test/test-custom-mutators.sh b/test/test-custom-mutators.sh index 5d679a82..49feedc0 100755 --- a/test/test-custom-mutators.sh +++ b/test/test-custom-mutators.sh @@ -3,84 +3,92 @@ . ./test-pre.sh $ECHO "$BLUE[*] Testing: custom mutator" -test "1" = "`../afl-fuzz | grep -i 'without python' >/dev/null; echo $?`" && { - # normalize path - CUSTOM_MUTATOR_PATH=$(cd $(pwd)/../custom_mutators/examples;pwd) - test -e test-custom-mutator.c -a -e ${CUSTOM_MUTATOR_PATH}/example.c -a -e ${CUSTOM_MUTATOR_PATH}/example.py && { - unset AFL_CC - # Compile the vulnerable program for single mutator - test -e ../afl-clang-fast && { - ../afl-clang-fast -o test-custom-mutator test-custom-mutator.c > /dev/null 2>&1 +# normalize path +CUSTOM_MUTATOR_PATH=$(cd $(pwd)/../custom_mutators/examples;pwd) +test -e test-custom-mutator.c -a -e ${CUSTOM_MUTATOR_PATH}/example.c -a -e ${CUSTOM_MUTATOR_PATH}/example.py && { + unset AFL_CC + # Compile the vulnerable program for single mutator + test -e ../afl-clang-fast && { + ../afl-clang-fast -o test-custom-mutator test-custom-mutator.c > /dev/null 2>&1 + } || { + test -e ../afl-gcc-fast && { + ../afl-gcc-fast -o test-custom-mutator test-custom-mutator.c > /dev/null 2>&1 } || { - test -e ../afl-gcc-fast && { - ../afl-gcc-fast -o test-custom-mutator test-custom-mutator.c > /dev/null 2>&1 - } || { - ../afl-gcc -o test-custom-mutator test-custom-mutator.c > /dev/null 2>&1 - } + ../afl-gcc -o test-custom-mutator test-custom-mutator.c > /dev/null 2>&1 } - # Compile the vulnerable program for multiple mutators - test -e ../afl-clang-fast && { - ../afl-clang-fast -o test-multiple-mutators test-multiple-mutators.c > /dev/null 2>&1 + } + # Compile the vulnerable program for multiple mutators + test -e ../afl-clang-fast && { + ../afl-clang-fast -o test-multiple-mutators test-multiple-mutators.c > /dev/null 2>&1 + } || { + test -e ../afl-gcc-fast && { + ../afl-gcc-fast -o test-multiple-mutators test-multiple-mutators.c > /dev/null 2>&1 } || { - test -e ../afl-gcc-fast && { - ../afl-gcc-fast -o test-multiple-mutators test-multiple-mutators.c > /dev/null 2>&1 - } || { - ../afl-gcc -o test-multiple-mutators test-multiple-mutators.c > /dev/null 2>&1 - } + ../afl-gcc -o test-multiple-mutators test-multiple-mutators.c > /dev/null 2>&1 } - # Compile the custom mutator - cc -D_FIXED_CHAR=0x41 -g -fPIC -shared -I../include ../custom_mutators/examples/simple_example.c -o libexamplemutator.so > /dev/null 2>&1 - cc -D_FIXED_CHAR=0x42 -g -fPIC -shared -I../include ../custom_mutators/examples/simple_example.c -o libexamplemutator2.so > /dev/null 2>&1 - test -e test-custom-mutator -a -e ./libexamplemutator.so && { - # Create input directory - mkdir -p in - echo "00000" > in/in + } + # Compile the custom mutator + cc -D_FIXED_CHAR=0x41 -g -fPIC -shared -I../include ../custom_mutators/examples/simple_example.c -o libexamplemutator.so > /dev/null 2>&1 + cc -D_FIXED_CHAR=0x42 -g -fPIC -shared -I../include ../custom_mutators/examples/simple_example.c -o libexamplemutator2.so > /dev/null 2>&1 + test -e test-custom-mutator -a -e ./libexamplemutator.so && { + # Create input directory + mkdir -p in + echo "00000" > in/in - # Run afl-fuzz w/ the C mutator - $ECHO "$GREY[*] running afl-fuzz for the C mutator, this will take approx 10 seconds" - { - AFL_CUSTOM_MUTATOR_LIBRARY=./libexamplemutator.so AFL_CUSTOM_MUTATOR_ONLY=1 ../afl-fuzz -V10 -m ${MEM_LIMIT} -i in -o out -- ./test-custom-mutator >>errors 2>&1 - } >>errors 2>&1 + # Run afl-fuzz w/ the C mutator + $ECHO "$GREY[*] running afl-fuzz for the C mutator, this will take approx 10 seconds" + { + AFL_CUSTOM_MUTATOR_LIBRARY=./libexamplemutator.so AFL_CUSTOM_MUTATOR_ONLY=1 ../afl-fuzz -V07 -m ${MEM_LIMIT} -i in -o out -- ./test-custom-mutator >>errors 2>&1 + } >>errors 2>&1 - # Check results - test -n "$( ls out/default/crashes/id:000000* 2>/dev/null )" && { # TODO: update here - $ECHO "$GREEN[+] afl-fuzz is working correctly with the C mutator" - } || { - echo CUT------------------------------------------------------------------CUT - cat errors - echo CUT------------------------------------------------------------------CUT - $ECHO "$RED[!] afl-fuzz is not working correctly with the C mutator" - CODE=1 - } + # Check results + test -n "$( ls out/default/crashes/id:000000* 2>/dev/null )" && { # TODO: update here + $ECHO "$GREEN[+] afl-fuzz is working correctly with the C mutator" + } || { + echo CUT------------------------------------------------------------------CUT + cat errors + echo CUT------------------------------------------------------------------CUT + $ECHO "$RED[!] afl-fuzz is not working correctly with the C mutator" + CODE=1 + } - # Clean - rm -rf out errors core.* + # Clean + rm -rf out errors core.* - # Run afl-fuzz w/ multiple C mutators - $ECHO "$GREY[*] running afl-fuzz with multiple custom C mutators, this will take approx 10 seconds" - { - AFL_CUSTOM_MUTATOR_LIBRARY="./libexamplemutator.so;./libexamplemutator2.so" AFL_CUSTOM_MUTATOR_ONLY=1 ../afl-fuzz -V10 -m ${MEM_LIMIT} -i in -o out -- ./test-multiple-mutators >>errors 2>&1 - } >>errors 2>&1 + # Run afl-fuzz w/ multiple C mutators + $ECHO "$GREY[*] running afl-fuzz with multiple custom C mutators, this will take approx 10 seconds" + { + AFL_CUSTOM_MUTATOR_LIBRARY="./libexamplemutator.so;./libexamplemutator2.so" AFL_CUSTOM_MUTATOR_ONLY=1 ../afl-fuzz -V07 -m ${MEM_LIMIT} -i in -o out -- ./test-multiple-mutators >>errors 2>&1 + } >>errors 2>&1 - test -n "$( ls out/default/crashes/id:000000* 2>/dev/null )" && { # TODO: update here - $ECHO "$GREEN[+] afl-fuzz is working correctly with multiple C mutators" - } || { - echo CUT------------------------------------------------------------------CUT - cat errors - echo CUT------------------------------------------------------------------CUT - $ECHO "$RED[!] afl-fuzz is not working correctly with multiple C mutators" - CODE=1 - } + test -n "$( ls out/default/crashes/id:000000* 2>/dev/null )" && { # TODO: update here + $ECHO "$GREEN[+] afl-fuzz is working correctly with multiple C mutators" + } || { + echo CUT------------------------------------------------------------------CUT + cat errors + echo CUT------------------------------------------------------------------CUT + $ECHO "$RED[!] afl-fuzz is not working correctly with multiple C mutators" + CODE=1 + } - # Clean - rm -rf out errors core.* + # Clean + rm -rf out errors core.* + } || { + ls . + ls ${CUSTOM_MUTATOR_PATH} + $ECHO "$RED[!] cannot compile the test program or the custom mutator" + CODE=1 + } +} +test "1" = "`../afl-fuzz | grep -i 'without python' >/dev/null; echo $?`" && { + test -e test-custom-mutator && { # Run afl-fuzz w/ the Python mutator $ECHO "$GREY[*] running afl-fuzz for the Python mutator, this will take approx 10 seconds" { export PYTHONPATH=${CUSTOM_MUTATOR_PATH} export AFL_PYTHON_MODULE=example - AFL_CUSTOM_MUTATOR_ONLY=1 ../afl-fuzz -V10 -m ${MEM_LIMIT} -i in -o out -- ./test-custom-mutator >>errors 2>&1 + AFL_CUSTOM_MUTATOR_ONLY=1 ../afl-fuzz -V07 -m ${MEM_LIMIT} -i in -o out -- ./test-custom-mutator >>errors 2>&1 unset PYTHONPATH unset AFL_PYTHON_MODULE } >>errors 2>&1 @@ -106,20 +114,12 @@ test "1" = "`../afl-fuzz | grep -i 'without python' >/dev/null; echo $?`" && { $ECHO "$RED[!] cannot compile the test program or the custom mutator" CODE=1 } - - #test "$CODE" = 1 && { $ECHO "$YELLOW[!] custom mutator tests currently will not fail travis" ; CODE=0 ; } - - make -C ../utils/custom_mutators clean > /dev/null 2>&1 - rm -f test-custom-mutator - rm -f test-custom-mutators - } || { - $ECHO "$YELLOW[-] no custom mutators in $CUSTOM_MUTATOR_PATH, cannot test" - INCOMPLETE=1 - } - unset CUSTOM_MUTATOR_PATH } || { $ECHO "$YELLOW[-] no python support in afl-fuzz, cannot test" INCOMPLETE=1 } +make -C ../utils/custom_mutators clean > /dev/null 2>&1 +rm -f test-custom-mutator test-custom-mutators + . ./test-post.sh diff --git a/test/test-frida-mode.sh b/test/test-frida-mode.sh index 9e1f756d..3ae84656 100755 --- a/test/test-frida-mode.sh +++ b/test/test-frida-mode.sh @@ -22,7 +22,7 @@ test -e ../afl-frida-trace.so && { echo 00000 > in/in $ECHO "$GREY[*] running afl-fuzz for frida_mode, this will take approx 10 seconds" { - AFL_DEBUG=1 AFL_FRIDA_VERBOSE=1 ../afl-fuzz -m ${MEM_LIMIT} -V10 -O -i in -o out -- ./test-instr >>errors 2>&1 + AFL_DEBUG=1 AFL_FRIDA_VERBOSE=1 ../afl-fuzz -m ${MEM_LIMIT} -V07 -O -i in -o out -- ./test-instr >>errors 2>&1 } >>errors 2>&1 test -n "$( ls out/default/queue/id:000002* 2>/dev/null )" && { $ECHO "$GREEN[+] afl-fuzz is working correctly with frida_mode" @@ -39,7 +39,7 @@ test -e ../afl-frida-trace.so && { test "$SYS" = "i686" -o "$SYS" = "x86_64" -o "$SYS" = "amd64" -o "$SYS" = "i86pc" -o "$SYS" = "aarch64" -o ! "${SYS%%arm*}" && { $ECHO "$GREY[*] running afl-fuzz for frida_mode cmplog, this will take approx 10 seconds" { - ../afl-fuzz -m none -V10 -O -c 0 -i in -o out -- ./test-compcov >>errors 2>&1 + ../afl-fuzz -m none -V07 -O -c 0 -i in -o out -- ./test-compcov >>errors 2>&1 } >>errors 2>&1 test -n "$( ls out/default/queue/id:000003* 2>/dev/null )" && { $ECHO "$GREEN[+] afl-fuzz is working correctly with frida_mode cmplog" @@ -67,7 +67,7 @@ test -e ../afl-frida-trace.so && { file test-instr export AFL_DEBUG_CHILD=1 export AFL_FRIDA_VERBOSE=1 - ../afl-fuzz -m ${MEM_LIMIT} -V10 -O -i in -o out -- ./test-instr + ../afl-fuzz -m ${MEM_LIMIT} -V07 -O -i in -o out -- ./test-instr nm test-instr | grep -i "main" unset AFL_FRIDA_PERSISTENT_ADDR } >>errors 2>&1 diff --git a/test/test-gcc-plugin.sh b/test/test-gcc-plugin.sh index 95ae9c47..54e6987f 100755 --- a/test/test-gcc-plugin.sh +++ b/test/test-gcc-plugin.sh @@ -63,7 +63,7 @@ test -e ../afl-gcc-fast -a -e ../afl-compiler-rt.o && { echo 0 > in/in $ECHO "$GREY[*] running afl-fuzz for gcc_plugin, this will take approx 10 seconds" { - ../afl-fuzz -V10 -m ${MEM_LIMIT} -i in -o out -D -- ./test-instr.plain.gccpi >>errors 2>&1 + ../afl-fuzz -V07 -m ${MEM_LIMIT} -i in -o out -- ./test-instr.plain.gccpi >>errors 2>&1 } >>errors 2>&1 test -n "$( ls out/default/queue/id:000002* 2>/dev/null )" && { $ECHO "$GREEN[+] afl-fuzz is working correctly with gcc_plugin" diff --git a/test/test-llvm.sh b/test/test-llvm.sh index ce64d76c..0e66cc97 100755 --- a/test/test-llvm.sh +++ b/test/test-llvm.sh @@ -133,7 +133,7 @@ test -e ../afl-clang-fast -a -e ../split-switches-pass.so && { test -z "$SKIP" && { $ECHO "$GREY[*] running afl-fuzz for llvm_mode, this will take approx 10 seconds" { - ../afl-fuzz -V10 -m ${MEM_LIMIT} -i in -o out -D -- ./test-instr.plain >>errors 2>&1 + ../afl-fuzz -V07 -m ${MEM_LIMIT} -i in -o out -- ./test-instr.plain >>errors 2>&1 } >>errors 2>&1 test -n "$( ls out/default/queue/id:000002* 2>/dev/null )" && { $ECHO "$GREEN[+] afl-fuzz is working correctly with llvm_mode" @@ -228,7 +228,7 @@ test -e ../afl-clang-fast -a -e ../split-switches-pass.so && { echo ZZZZ > in/in $ECHO "$GREY[*] running afl-fuzz with floating point splitting, this will take max. 45 seconds" { - AFL_BENCH_UNTIL_CRASH=1 AFL_NO_UI=1 ../afl-fuzz -Z -s 123 -V50 -m ${MEM_LIMIT} -i in -o out -D -- ./test-floatingpoint >>errors 2>&1 + AFL_BENCH_UNTIL_CRASH=1 AFL_NO_UI=1 ../afl-fuzz -Z -s 123 -V15 -m ${MEM_LIMIT} -i in -o out -- ./test-floatingpoint >>errors 2>&1 } >>errors 2>&1 test -n "$( ls out/default/crashes/id:* 2>/dev/null )" && { $ECHO "$GREEN[+] llvm_mode laf-intel floatingpoint splitting feature works correctly" @@ -257,14 +257,15 @@ test -e ../afl-clang-fast -a -e ../split-switches-pass.so && { } rm -f test-compcov test.out instrumentlist.txt AFL_LLVM_CMPLOG=1 ../afl-clang-fast -o test-cmplog test-cmplog.c > /dev/null 2>&1 + ../afl-clang-fast -o test-c test-cmplog.c > /dev/null 2>&1 test -e test-cmplog && { $ECHO "$GREY[*] running afl-fuzz for llvm_mode cmplog, this will take approx 10 seconds" { mkdir -p in echo 00000000000000000000000000000000 > in/in - AFL_BENCH_UNTIL_CRASH=1 ../afl-fuzz -m none -V60 -i in -o out -c./test-cmplog -- ./test-cmplog >>errors 2>&1 + AFL_BENCH_UNTIL_CRASH=1 ../afl-fuzz -m none -V15 -i in -o out -c./test-cmplog -- ./test-c >>errors 2>&1 } >>errors 2>&1 - test -n "$( ls out/default/crashes/id:000000* out/default/hangs/id:000000* 2>/dev/null )" & { + test -n "$( ls out/default/crashes/id:000000* out/default/hangs/id:000000* 2>/dev/null )" && { $ECHO "$GREEN[+] afl-fuzz is working correctly with llvm_mode cmplog" } || { echo CUT------------------------------------------------------------------CUT @@ -277,7 +278,7 @@ test -e ../afl-clang-fast -a -e ../split-switches-pass.so && { $ECHO "$YELLOW[-] we cannot test llvm_mode cmplog because it is not present" INCOMPLETE=1 } - rm -rf errors test-cmplog in core.* + rm -rf errors test-cmplog test-c in core.* ../afl-clang-fast -o test-persistent ../utils/persistent_mode/persistent_demo.c > /dev/null 2>&1 test -e test-persistent && { echo foo | AFL_QUIET=1 ../afl-showmap -m ${MEM_LIMIT} -o /dev/null -q -r ./test-persistent && { diff --git a/test/test-qemu-mode.sh b/test/test-qemu-mode.sh index 46b138ff..9e268963 100755 --- a/test/test-qemu-mode.sh +++ b/test/test-qemu-mode.sh @@ -22,7 +22,7 @@ test -e ../afl-qemu-trace && { echo 00000 > in/in $ECHO "$GREY[*] running afl-fuzz for qemu_mode, this will take approx 10 seconds" { - ../afl-fuzz -m ${MEM_LIMIT} -V10 -Q -i in -o out -- ./test-instr >>errors 2>&1 + ../afl-fuzz -m ${MEM_LIMIT} -V07 -Q -i in -o out -- ./test-instr >>errors 2>&1 } >>errors 2>&1 test -n "$( ls out/default/queue/id:000002* 2>/dev/null )" && { $ECHO "$GREEN[+] afl-fuzz is working correctly with qemu_mode" @@ -63,7 +63,7 @@ test -e ../afl-qemu-trace && { { export AFL_PRELOAD=../libcompcov.so export AFL_COMPCOV_LEVEL=2 - ../afl-fuzz -m ${MEM_LIMIT} -V10 -Q -i in -o out -- ./test-compcov >>errors 2>&1 + ../afl-fuzz -m ${MEM_LIMIT} -V07 -Q -i in -o out -- ./test-compcov >>errors 2>&1 unset AFL_PRELOAD unset AFL_COMPCOV_LEVEL } >>errors 2>&1 @@ -88,7 +88,7 @@ test -e ../afl-qemu-trace && { test "$SYS" = "i686" -o "$SYS" = "x86_64" -o "$SYS" = "amd64" -o "$SYS" = "i86pc" -o "$SYS" = "aarch64" -o ! "${SYS%%arm*}" && { $ECHO "$GREY[*] running afl-fuzz for qemu_mode cmplog, this will take approx 10 seconds" { - ../afl-fuzz -m none -V10 -Q -c 0 -i in -o out -- ./test-compcov >>errors 2>&1 + ../afl-fuzz -m none -V07 -Q -c 0 -i in -o out -- ./test-compcov >>errors 2>&1 } >>errors 2>&1 test -n "$( ls out/default/queue/id:000001* 2>/dev/null )" && { $ECHO "$GREEN[+] afl-fuzz is working correctly with qemu_mode cmplog" @@ -107,19 +107,26 @@ test -e ../afl-qemu-trace && { test "$SYS" = "i686" -o "$SYS" = "x86_64" -o "$SYS" = "amd64" -o "$SYS" = "i86pc" -o "$SYS" = "aarch64" -o ! "${SYS%%arm*}" && { $ECHO "$GREY[*] running afl-fuzz for persistent qemu_mode, this will take approx 10 seconds" { - if file test-instr | grep -q "32-bit"; then - # for 32-bit reduce 8 nibbles to the lower 7 nibbles - ADDR_LOWER_PART=`nm test-instr | grep "T main" | awk '{print $1}' | sed 's/^.//'` - else - # for 64-bit reduce 16 nibbles to the lower 9 nibbles - ADDR_LOWER_PART=`nm test-instr | grep "T main" | awk '{print $1}' | sed 's/^.......//'` - fi - export AFL_QEMU_PERSISTENT_ADDR=`expr 0x4${ADDR_LOWER_PART}` + IS_STATIC="" + file test-instr | grep -q 'statically linked' && IS_STATIC=1 + test -z "$IS_STATIC" && { + if file test-instr | grep -q "32-bit"; then + # for 32-bit reduce 8 nibbles to the lower 7 nibbles + ADDR_LOWER_PART=`nm test-instr | grep "T main" | awk '{print $1}' | sed 's/^.//'` + else + # for 64-bit reduce 16 nibbles to the lower 9 nibbles + ADDR_LOWER_PART=`nm test-instr | grep "T main" | awk '{print $1}' | sed 's/^.......//'` + fi + export AFL_QEMU_PERSISTENT_ADDR=`expr 0x4${ADDR_LOWER_PART}` + } + test -n "$IS_STATIC" && { + export AFL_QEMU_PERSISTENT_ADDR=0x`nm test-instr | grep "T main" | awk '{print $1}'` + } export AFL_QEMU_PERSISTENT_GPR=1 $ECHO "Info: AFL_QEMU_PERSISTENT_ADDR=$AFL_QEMU_PERSISTENT_ADDR <= $(nm test-instr | grep "T main" | awk '{print $1}')" env|grep AFL_|sort file test-instr - ../afl-fuzz -m ${MEM_LIMIT} -V10 -Q -i in -o out -- ./test-instr + ../afl-fuzz -m ${MEM_LIMIT} -V07 -Q -i in -o out -- ./test-instr unset AFL_QEMU_PERSISTENT_ADDR } >>errors 2>&1 test -n "$( ls out/default/queue/id:000002* 2>/dev/null )" && { diff --git a/test/test-unicorn-mode.sh b/test/test-unicorn-mode.sh index f8ff4190..338c5982 100755 --- a/test/test-unicorn-mode.sh +++ b/test/test-unicorn-mode.sh @@ -34,7 +34,7 @@ test -d ../unicorn_mode/unicornafl -a -e ../unicorn_mode/unicornafl/Makefile && cd ../unicorn_mode/samples/persistent make >>errors 2>&1 $ECHO "$GREY[*] running afl-fuzz for unicorn_mode (persistent), this will take approx 25 seconds" - AFL_DEBUG_CHILD=1 ../../../afl-fuzz -m none -V25 -U -i sample_inputs -o out -d -- ./harness @@ >>errors 2>&1 + AFL_DEBUG_CHILD=1 ../../../afl-fuzz -m none -V15 -U -i sample_inputs -o out -d -- ./harness @@ >>errors 2>&1 test -n "$( ls out/default/queue/id:000002* 2>/dev/null )" && { $ECHO "$GREEN[+] afl-fuzz is working correctly with unicorn_mode (persistent)" } || { @@ -61,7 +61,7 @@ test -d ../unicorn_mode/unicornafl -a -e ../unicorn_mode/unicornafl/Makefile && { $ECHO "$GREY[*] running afl-fuzz for unicorn_mode in python, this will take approx 25 seconds" { - ../afl-fuzz -m ${MEM_LIMIT} -V25 -U -i in -o out -d -- "$PY" ../unicorn_mode/samples/python_simple/simple_test_harness.py @@ >>errors 2>&1 + ../afl-fuzz -m ${MEM_LIMIT} -V15 -U -i in -o out -d -- "$PY" ../unicorn_mode/samples/python_simple/simple_test_harness.py @@ >>errors 2>&1 } >>errors 2>&1 test -n "$( ls out/default/queue/id:000002* 2>/dev/null )" && { $ECHO "$GREEN[+] afl-fuzz is working correctly with unicorn_mode" @@ -80,7 +80,7 @@ test -d ../unicorn_mode/unicornafl -a -e ../unicorn_mode/unicornafl/Makefile && $ECHO "$GREY[*] running afl-fuzz for unicorn_mode compcov, this will take approx 35 seconds" { export AFL_COMPCOV_LEVEL=2 - ../afl-fuzz -m ${MEM_LIMIT} -V35 -U -i in -o out -d -- "$PY" ../unicorn_mode/samples/compcov_x64/compcov_test_harness.py @@ >>errors 2>&1 + ../afl-fuzz -m ${MEM_LIMIT} -V15 -U -i in -o out -d -- "$PY" ../unicorn_mode/samples/compcov_x64/compcov_test_harness.py @@ >>errors 2>&1 unset AFL_COMPCOV_LEVEL } >>errors 2>&1 test -n "$( ls out/default/queue/id:000001* 2>/dev/null )" && { diff --git a/unicorn_mode/UNICORNAFL_VERSION b/unicorn_mode/UNICORNAFL_VERSION index 5e7234c6..1c8e571f 100644 --- a/unicorn_mode/UNICORNAFL_VERSION +++ b/unicorn_mode/UNICORNAFL_VERSION @@ -1 +1 @@ -06796154996fef2d92ccd172181ee0cdf3631959 +f2cede37 diff --git a/unicorn_mode/build_unicorn_support.sh b/unicorn_mode/build_unicorn_support.sh index f24c8ce3..53ec2481 100755 --- a/unicorn_mode/build_unicorn_support.sh +++ b/unicorn_mode/build_unicorn_support.sh @@ -14,7 +14,7 @@ # <andreafioraldi@gmail.com> # # Copyright 2017 Battelle Memorial Institute. All rights reserved. -# Copyright 2019-2022 AFLplusplus Project. All rights reserved. +# Copyright 2019-2023 AFLplusplus Project. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -117,21 +117,23 @@ done # some python version should be available now PYTHONS="`command -v python3` `command -v python` `command -v python2`" -SETUPTOOLS_FOUND=0 +PIP_FOUND=0 for PYTHON in $PYTHONS ; do - if $PYTHON -c "import setuptools" ; then + if $PYTHON -c "import pip" ; then + if $PYTHON -c "import wheel" ; then - SETUPTOOLS_FOUND=1 - PYTHONBIN=$PYTHON - break + PIP_FOUND=1 + PYTHONBIN=$PYTHON + break + fi fi done -if [ "0" = $SETUPTOOLS_FOUND ]; then +if [ "0" = $PIP_FOUND ]; then - echo "[-] Error: Python setup-tools not found. Run 'sudo apt-get install python-setuptools', or install python3-setuptools, or run '$PYTHONBIN -m ensurepip', or create a virtualenv, or ..." + echo "[-] Error: Python pip or python wheel not found. Run 'sudo apt-get install python3-pip', or run '$PYTHONBIN -m ensurepip', or create a virtualenv, or ... - and 'pip3 install wheel'" PREREQ_NOTFOUND=1 fi @@ -196,15 +198,25 @@ $MAKECMD -j1 || exit 1 echo "[+] Build process successful!" echo "[*] Installing Unicorn python bindings..." +cd unicorn/bindings/python || exit 1 +if [ -z "$VIRTUAL_ENV" ]; then + echo "[*] Info: Installing python unicornafl using --user" + THREADS=$CORES $PYTHONBIN -m pip install --user --force .|| exit 1 +else + echo "[*] Info: Installing python unicornafl to virtualenv: $VIRTUAL_ENV" + THREADS=$CORES $PYTHONBIN -m pip install --force .|| exit 1 +fi +cd ../../../ +echo "[*] Installing Unicornafl python bindings..." cd bindings/python || exit 1 if [ -z "$VIRTUAL_ENV" ]; then echo "[*] Info: Installing python unicornafl using --user" - $PYTHONBIN setup.py install --user --force --prefix=|| exit 1 + THREADS=$CORES $PYTHONBIN -m pip install --user --force .|| exit 1 else echo "[*] Info: Installing python unicornafl to virtualenv: $VIRTUAL_ENV" - $PYTHONBIN setup.py install --force || exit 1 + THREADS=$CORES $PYTHONBIN -m pip install --force .|| exit 1 fi -echo '[*] If needed, you can (re)install the bindings from `./unicornafl/bindings/python` using `python setup.py install`' +echo '[*] If needed, you can (re)install the bindings in `./unicornafl/bindings/python` using `pip install --force .`' cd ../../ || exit 1 diff --git a/unicorn_mode/samples/speedtest/rust/Cargo.toml b/unicorn_mode/samples/speedtest/rust/Cargo.toml index 766b2f27..73e6ba4c 100644 --- a/unicorn_mode/samples/speedtest/rust/Cargo.toml +++ b/unicorn_mode/samples/speedtest/rust/Cargo.toml @@ -11,5 +11,5 @@ panic = "abort" [dependencies] unicornafl = { path = "../../../unicornafl/bindings/rust/", version="1.0.0" } -capstone="0.10.0" +capstone="0.11.0" libc="0.2.66" diff --git a/unicorn_mode/unicornafl b/unicorn_mode/unicornafl -Subproject 06796154996fef2d92ccd172181ee0cdf363195 +Subproject f2cede37a75bbd4a9b9438f0277727b5d462057 diff --git a/utils/afl_network_proxy/afl-network-client.c b/utils/afl_network_proxy/afl-network-client.c index 89ca6c4e..0416f0f9 100644 --- a/utils/afl_network_proxy/afl-network-client.c +++ b/utils/afl_network_proxy/afl-network-client.c @@ -4,7 +4,7 @@ Written by Marc Heuse <mh@mh-sec.de> - Copyright 2019-2022 AFLplusplus Project. All rights reserved. + Copyright 2019-2023 AFLplusplus Project. All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/utils/afl_network_proxy/afl-network-server.c b/utils/afl_network_proxy/afl-network-server.c index 8f0e9df9..04309ada 100644 --- a/utils/afl_network_proxy/afl-network-server.c +++ b/utils/afl_network_proxy/afl-network-server.c @@ -12,7 +12,7 @@ Dominik Maier <mail@dmnk.co> Copyright 2016, 2017 Google Inc. All rights reserved. - Copyright 2019-2022 AFLplusplus Project. All rights reserved. + Copyright 2019-2023 AFLplusplus Project. All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -194,7 +194,7 @@ static void set_up_environment(afl_forkserver_t *fsrv) { } - if (!strstr(x, "symbolize=0")) { + if (!getenv("AFL_DEBUG") && !strstr(x, "symbolize=0")) { FATAL("Custom ASAN_OPTIONS set without symbolize=0 - please fix!"); @@ -213,7 +213,7 @@ static void set_up_environment(afl_forkserver_t *fsrv) { } - if (!strstr(x, "symbolize=0")) { + if (!getenv("AFL_DEBUG") && !strstr(x, "symbolize=0")) { FATAL("Custom MSAN_OPTIONS set without symbolize=0 - please fix!"); @@ -221,18 +221,7 @@ static void set_up_environment(afl_forkserver_t *fsrv) { } - setenv("ASAN_OPTIONS", - "abort_on_error=1:" - "detect_leaks=0:" - "symbolize=0:" - "allocator_may_return_null=1", - 0); - - setenv("MSAN_OPTIONS", "exit_code=" STRINGIFY(MSAN_ERROR) ":" - "symbolize=0:" - "abort_on_error=1:" - "allocator_may_return_null=1:" - "msan_track_origins=0", 0); + set_sanitizer_defaults(); if (get_afl_env("AFL_PRELOAD")) { diff --git a/utils/afl_proxy/README.md b/utils/afl_proxy/README.md index 3c768a19..7965659d 100644 --- a/utils/afl_proxy/README.md +++ b/utils/afl_proxy/README.md @@ -7,3 +7,8 @@ You only need to change the while() loop of the main() to send the data of buf[] with length len to the target and write the coverage information to __afl_area_ptr[__afl_map_size] +UPDATE: you can also use [custom mutators](../../docs/custom_mutators.md) with +afl_custom_fuzz_send to send data to a target, which is much more efficient! +But you can only use this feature if you start the target via afl-fuzz and +a forkserver is active (e.g. via -Q qemu_mode or source compiled). + diff --git a/utils/afl_proxy/afl-proxy.c b/utils/afl_proxy/afl-proxy.c index afd0e5d2..531a97a2 100644 --- a/utils/afl_proxy/afl-proxy.c +++ b/utils/afl_proxy/afl-proxy.c @@ -4,7 +4,7 @@ Written by Marc Heuse <mh@mh-sec.de> - Copyright 2019-2022 AFLplusplus Project. All rights reserved. + Copyright 2019-2023 AFLplusplus Project. All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/utils/afl_untracer/afl-untracer.c b/utils/afl_untracer/afl-untracer.c index ed7047a4..a18e314e 100644 --- a/utils/afl_untracer/afl-untracer.c +++ b/utils/afl_untracer/afl-untracer.c @@ -4,7 +4,7 @@ Written by Marc Heuse <mh@mh-sec.de> - Copyright 2019-2022 AFLplusplus Project. All rights reserved. + Copyright 2019-2023 AFLplusplus Project. All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -156,7 +156,7 @@ void read_library_information(void) { *e = 0; if (n[strlen(n) - 1] == '\n') n[strlen(n) - 1] = 0; - liblist[liblist_cnt].name = strdup(n); + liblist[liblist_cnt].name = (u8 *)strdup((char *)n); liblist[liblist_cnt].addr_start = strtoull(b, NULL, 16); liblist[liblist_cnt].addr_end = strtoull(m, NULL, 16); if (debug) @@ -210,16 +210,17 @@ void read_library_information(void) { !(region->kve_protection & KVME_PROT_EXEC)) { liblist[liblist_cnt].name = - region->kve_path[0] != '\0' ? strdup(region->kve_path) : 0; + region->kve_path[0] != '\0' ? (u8 *)strdup(region->kve_path) : 0; liblist[liblist_cnt].addr_start = region->kve_start; liblist[liblist_cnt].addr_end = region->kve_end; if (debug) { - fprintf(stderr, "%s:%x (%lx-%lx)\n", liblist[liblist_cnt].name, - liblist[liblist_cnt].addr_end - liblist[liblist_cnt].addr_start, - liblist[liblist_cnt].addr_start, - liblist[liblist_cnt].addr_end - 1); + fprintf(stderr, "%s:%lx (%lx-%lx)\n", liblist[liblist_cnt].name, + (unsigned long)(liblist[liblist_cnt].addr_end - + liblist[liblist_cnt].addr_start), + (unsigned long)liblist[liblist_cnt].addr_start, + (unsigned long)(liblist[liblist_cnt].addr_end - 1)); } @@ -488,6 +489,12 @@ void setup_trap_instrumentation(void) { uint32_t bitmap_index = 0; #endif +#if defined(__FreeBSD__) && __FreeBSD_version >= 1301000 + // We try to allow W/X pages despite kern.elf32/64.allow_wx system settings + int allow_wx = PROC_WX_MAPPINGS_PERMIT; + (void)procctl(P_PID, 0, PROC_WXMAP_CTL, &allow_wx); +#endif + while ((nread = getline(&line, &len, patches)) != -1) { char *end = line + len; @@ -699,7 +706,7 @@ int main(int argc, char *argv[]) { if (argc > 1) { use_stdin = 0; - inputfile = argv[1]; + inputfile = (u8 *)argv[1]; } @@ -732,7 +739,7 @@ int main(int argc, char *argv[]) { if (pid) { u32 status; - if (waitpid(pid, &status, 0) < 0) exit(1); + if (waitpid(pid, (int *)&status, 0) < 0) exit(1); /* report the test case is done and wait for the next */ __afl_end_testcase(status); diff --git a/utils/afl_untracer/libtestinstr.c b/utils/afl_untracer/libtestinstr.c index a3f5acc8..b7afc325 100644 --- a/utils/afl_untracer/libtestinstr.c +++ b/utils/afl_untracer/libtestinstr.c @@ -3,7 +3,7 @@ -------------------------------------------------------- Originally written by Michal Zalewski Copyright 2014 Google Inc. All rights reserved. - Copyright 2019-2022 AFLplusplus Project. All rights reserved. + Copyright 2019-2023 AFLplusplus Project. All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at: diff --git a/utils/aflpp_driver/GNUmakefile b/utils/aflpp_driver/GNUmakefile index 234a1c31..b973f96a 100644 --- a/utils/aflpp_driver/GNUmakefile +++ b/utils/aflpp_driver/GNUmakefile @@ -8,9 +8,14 @@ ifeq "$(shell uname -s)" "Darwin" LDFLAGS += $(SDK_LD) endif +ifeq "" "$(LLVM_CONFIG)" + LLVM_CONFIG := llvm-config +endif LLVM_BINDIR = $(shell $(LLVM_CONFIG) --bindir 2>/dev/null) ifneq "" "$(LLVM_BINDIR)" - LLVM_BINDIR := $(LLVM_BINDIR)/ + ifeq "$(shell test -x $(LLVM_BINDIR)/clang && echo 1)" "1" + CC := $(LLVM_BINDIR)/clang + endif endif CFLAGS := -O3 -funroll-loops -g -fPIC @@ -18,31 +23,31 @@ CFLAGS := -O3 -funroll-loops -g -fPIC all: libAFLDriver.a libAFLQemuDriver.a aflpp_qemu_driver_hook.so aflpp_driver.o: aflpp_driver.c - -$(LLVM_BINDIR)clang -I. -I../../include $(CFLAGS) -c aflpp_driver.c + -$(CC) -I. -I../../include $(CFLAGS) -c aflpp_driver.c libAFLDriver.a: aflpp_driver.o @ar rc libAFLDriver.a aflpp_driver.o @cp -vf libAFLDriver.a ../../ debug: - $(LLVM_BINDIR)clang -Wno-deprecated -I../../include $(CFLAGS) -D_DEBUG=\"1\" -c -o afl-performance.o ../../src/afl-performance.c - $(LLVM_BINDIR)clang -I../../include -D_DEBUG=\"1\" -g -funroll-loops -c aflpp_driver.c - #$(LLVM_BINDIR)clang -S -emit-llvm -Wno-deprecated -I../../include $(CFLAGS) -D_DEBUG=\"1\" -c -o afl-performance.ll ../../src/afl-performance.c - #$(LLVM_BINDIR)clang -S -emit-llvm -I../../include -D_DEBUG=\"1\" -g -funroll-loops -c aflpp_driver.c + $(CC) -Wno-deprecated -I../../include $(CFLAGS) -D_DEBUG=\"1\" -c -o afl-performance.o ../../src/afl-performance.c + $(CC) -I../../include -D_DEBUG=\"1\" -g -funroll-loops -c aflpp_driver.c + #$(CC) -S -emit-llvm -Wno-deprecated -I../../include $(CFLAGS) -D_DEBUG=\"1\" -c -o afl-performance.ll ../../src/afl-performance.c + #$(CC) -S -emit-llvm -I../../include -D_DEBUG=\"1\" -g -funroll-loops -c aflpp_driver.c ar rc libAFLDriver.a afl-performance.o aflpp_driver.o aflpp_qemu_driver.o: aflpp_qemu_driver.c - -$(LLVM_BINDIR)clang $(CFLAGS) -O0 -funroll-loops -c aflpp_qemu_driver.c + -$(CC) $(CFLAGS) -O0 -funroll-loops -c aflpp_qemu_driver.c libAFLQemuDriver.a: aflpp_qemu_driver.o @-ar rc libAFLQemuDriver.a aflpp_qemu_driver.o @-cp -vf libAFLQemuDriver.a ../../ aflpp_qemu_driver_hook.so: aflpp_qemu_driver_hook.o - @-test -e aflpp_qemu_driver_hook.o && $(LLVM_BINDIR)clang $(LDFLAGS) -shared aflpp_qemu_driver_hook.o -o aflpp_qemu_driver_hook.so || echo "Note: Optional aflpp_qemu_driver_hook.so not built." + @-test -e aflpp_qemu_driver_hook.o && $(CC) $(LDFLAGS) -shared aflpp_qemu_driver_hook.o -o aflpp_qemu_driver_hook.so || echo "Note: Optional aflpp_qemu_driver_hook.so not built." aflpp_qemu_driver_hook.o: aflpp_qemu_driver_hook.c - @-test -e ../../qemu_mode/qemuafl/qemuafl/api.h && $(LLVM_BINDIR)clang $(CFLAGS) -funroll-loops -c aflpp_qemu_driver_hook.c || echo "Note: Optional aflpp_qemu_driver_hook.o not built." + @-test -e ../../qemu_mode/qemuafl/qemuafl/api.h && $(CC) $(CFLAGS) -funroll-loops -c aflpp_qemu_driver_hook.c || echo "Note: Optional aflpp_qemu_driver_hook.o not built." test: debug #clang -S -emit-llvm -D_DEBUG=\"1\" -I../../include -Wl,--allow-multiple-definition -funroll-loops -o aflpp_driver_test.ll aflpp_driver_test.c diff --git a/utils/aflpp_driver/aflpp_driver.c b/utils/aflpp_driver/aflpp_driver.c index 4e4ea129..4e8f466d 100644 --- a/utils/aflpp_driver/aflpp_driver.c +++ b/utils/aflpp_driver/aflpp_driver.c @@ -1,12 +1,16 @@ -//===- afl_driver.cpp - a glue between AFL++ and libFuzzer ------*- C++ -* ===// -//===----------------------------------------------------------------------===// +// +// afl_driver.cpp - a glue between AFL++ and LLVMFuzzerTestOneInput harnesses +// -/* This file allows to fuzz libFuzzer-style target functions +/* + + This file allows to fuzz libFuzzer-style target functions (LLVMFuzzerTestOneInput) with AFL++ using persistent in-memory fuzzing. Usage: -################################################################################ -cat << EOF > test_fuzzer.cc + +# Example target: +$ cat << EOF > test_fuzzer.cc #include <stddef.h> #include <stdint.h> extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { @@ -20,21 +24,24 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { } EOF -# Build your target with -fsanitize-coverage=trace-pc-guard using fresh clang. -clang -c aflpp_driver.c -# Build afl-compiler-rt.o.c from the AFL distribution. -clang -c $AFL_HOME/instrumentation/afl-compiler-rt.o.c -# Build this file, link it with afl-compiler-rt.o.o and the target code. -afl-clang-fast -o test_fuzzer test_fuzzer.cc afl-compiler-rt.o aflpp_driver.o + +# Build your target with afl-cc -fsanitize=fuzzer +$ afl-c++ -fsanitize=fuzzer -o test_fuzzer test_fuzzer.cc # Run AFL: -rm -rf IN OUT; mkdir IN OUT; echo z > IN/z; -$AFL_HOME/afl-fuzz -i IN -o OUT ./a.out -################################################################################ +$ mkdir -p in ; echo z > in/foo; +$ afl-fuzz -i in -o out -- ./test_fuzzer + */ +#ifdef __cplusplus +extern "C" { + +#endif + #include <assert.h> #include <errno.h> #include <stdarg.h> +#include <stdbool.h> #include <stdint.h> #include <stdio.h> #include <stdlib.h> @@ -57,15 +64,26 @@ $AFL_HOME/afl-fuzz -i IN -o OUT ./a.out #include "hash.h" #endif +// AFL++ shared memory fuzz cases int __afl_sharedmem_fuzzing = 1; extern unsigned int *__afl_fuzz_len; extern unsigned char *__afl_fuzz_ptr; -// libFuzzer interface is thin, so we don't include any libFuzzer headers. -int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size); -__attribute__((weak)) int LLVMFuzzerInitialize(int *argc, char ***argv); +// AFL++ coverage map +extern unsigned char *__afl_area_ptr; +extern unsigned int __afl_map_size; -// Default nop ASan hooks for manual posisoning when not linking the ASan +// libFuzzer interface is thin, so we don't include any libFuzzer headers. +/* Using the weak attributed on LLVMFuzzerTestOneInput() breaks oss-fuzz but + on the other hand this is what Google needs to make LLVMFuzzerRunDriver() + work. Choose your poison Google! */ +/*__attribute__((weak))*/ int LLVMFuzzerTestOneInput(const uint8_t *Data, + size_t Size); +__attribute__((weak)) int LLVMFuzzerInitialize(int *argc, char ***argv); +__attribute__((weak)) int LLVMFuzzerRunDriver( + int *argc, char ***argv, int (*callback)(const uint8_t *data, size_t size)); + +// Default nop ASan hooks for manual poisoning when not linking the ASan // runtime // https://github.com/google/sanitizers/wiki/AddressSanitizerManualPoisoning __attribute__((weak)) void __asan_poison_memory_region( @@ -187,7 +205,8 @@ static void maybe_close_fd_mask() { // Define LLVMFuzzerMutate to avoid link failures for targets that use it // with libFuzzer's LLVMFuzzerCustomMutator. -size_t LLVMFuzzerMutate(uint8_t *Data, size_t Size, size_t MaxSize) { +__attribute__((weak)) size_t LLVMFuzzerMutate(uint8_t *Data, size_t Size, + size_t MaxSize) { // assert(false && "LLVMFuzzerMutate should not be called from afl_driver"); return 0; @@ -195,7 +214,9 @@ size_t LLVMFuzzerMutate(uint8_t *Data, size_t Size, size_t MaxSize) { } // Execute any files provided as parameters. -static int ExecuteFilesOnyByOne(int argc, char **argv) { +static int ExecuteFilesOnyByOne(int argc, char **argv, + int (*callback)(const uint8_t *data, + size_t size)) { unsigned char *buf = (unsigned char *)malloc(MAX_FILE); @@ -231,7 +252,7 @@ static int ExecuteFilesOnyByOne(int argc, char **argv) { prev_length = length; printf("Reading %zu bytes from %s\n", length, argv[i]); - LLVMFuzzerTestOneInput(buf, length); + callback(buf, length); printf("Execution successful.\n"); } @@ -245,7 +266,18 @@ static int ExecuteFilesOnyByOne(int argc, char **argv) { } -int main(int argc, char **argv) { +__attribute__((weak)) int main(int argc, char **argv) { + + // Enable if LLVMFuzzerTestOneInput() has the weak attribute + /* + if (!LLVMFuzzerTestOneInput) { + + fprintf(stderr, "Error: function LLVMFuzzerTestOneInput() not found!\n"); + abort(); + + } + + */ if (argc < 2 || strncmp(argv[1], "-h", 2) == 0) printf( @@ -265,6 +297,17 @@ int main(int argc, char **argv) { "===================================================================\n", argv[0], argv[0]); + return LLVMFuzzerRunDriver(&argc, &argv, LLVMFuzzerTestOneInput); + +} + +__attribute__((weak)) int LLVMFuzzerRunDriver( + int *argcp, char ***argvp, + int (*callback)(const uint8_t *data, size_t size)) { + + int argc = *argcp; + char **argv = *argvp; + if (getenv("AFL_GDB")) { char cmd[64]; @@ -275,6 +318,12 @@ int main(int argc, char **argv) { } + bool in_afl = !(!getenv(SHM_FUZZ_ENV_VAR) || !getenv(SHM_ENV_VAR) || + fcntl(FORKSRV_FD, F_GETFD) == -1 || + fcntl(FORKSRV_FD + 1, F_GETFD) == -1); + + if (!in_afl) { __afl_sharedmem_fuzzing = 0; } + output_file = stderr; maybe_duplicate_stderr(); maybe_close_fd_mask(); @@ -295,27 +344,28 @@ int main(int argc, char **argv) { int N = INT_MAX; - if (argc == 2 && !strcmp(argv[1], "-")) { + if (!in_afl && argc == 2 && !strcmp(argv[1], "-")) { - __afl_sharedmem_fuzzing = 0; __afl_manual_init(); - return ExecuteFilesOnyByOne(argc, argv); + return ExecuteFilesOnyByOne(argc, argv, callback); - } else if (argc == 2 && argv[1][0] == '-') { + } else if (argc == 2 && argv[1][0] == '-' && argv[1][1]) { N = atoi(argv[1] + 1); - } else if (argc == 2 && (N = atoi(argv[1])) > 0) { + } else if (argc == 2 && argv[1][0] != '-' && (N = atoi(argv[1])) > 0) { printf("WARNING: using the deprecated call style `%s %d`\n", argv[0], N); - } else if (argc > 1) { - - __afl_sharedmem_fuzzing = 0; + } else if (!in_afl && argc > 1 && argv[1][0] != '-') { if (argc == 2) { __afl_manual_init(); } - return ExecuteFilesOnyByOne(argc, argv); + return ExecuteFilesOnyByOne(argc, argv, callback); + + } else { + + N = INT_MAX; } @@ -325,7 +375,7 @@ int main(int argc, char **argv) { // Call LLVMFuzzerTestOneInput here so that coverage caused by initialization // on the first execution of LLVMFuzzerTestOneInput is ignored. - LLVMFuzzerTestOneInput(dummy_input, 4); + callback(dummy_input, 4); __asan_poison_memory_region(__afl_fuzz_ptr, MAX_FILE); size_t prev_length = 0; @@ -352,7 +402,13 @@ int main(int argc, char **argv) { } prev_length = length; - LLVMFuzzerTestOneInput(__afl_fuzz_ptr, length); + + if (unlikely(callback(__afl_fuzz_ptr, length) == -1)) { + + memset(__afl_area_ptr, 0, __afl_map_size); + __afl_area_ptr[0] = 1; + + } } @@ -362,7 +418,7 @@ int main(int argc, char **argv) { while (__afl_persistent_loop(N)) { - LLVMFuzzerTestOneInput(__afl_fuzz_ptr, *__afl_fuzz_len); + callback(__afl_fuzz_ptr, *__afl_fuzz_len); } @@ -372,3 +428,9 @@ int main(int argc, char **argv) { } +#ifdef __cplusplus + +} + +#endif + diff --git a/utils/aflpp_driver/aflpp_driver_test.c b/utils/aflpp_driver/aflpp_driver_test.c index 527ba57b..32119485 100644 --- a/utils/aflpp_driver/aflpp_driver_test.c +++ b/utils/aflpp_driver/aflpp_driver_test.c @@ -2,23 +2,28 @@ #include <stdlib.h> #include <stdint.h> -void __attribute__((noinline)) crashme(const uint8_t *Data, size_t Size) { +char *foo = NULL; - if (Size < 5) return; +int __attribute__((noinline)) crashme(const uint8_t *Data, size_t Size) { + + if (Size < 5) return -1; if (Data[0] == 'F') if (Data[1] == 'A') if (Data[2] == '$') if (Data[3] == '$') - if (Data[4] == '$') abort(); + if (Data[4] == '$') *foo = 1; + + return 0; } int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { - if (Size) crashme(Data, Size); - - return 0; + if (Size) + return crashme(Data, Size); + else + return -1; } diff --git a/utils/analysis_scripts/queue2csv.sh b/utils/analysis_scripts/queue2csv.sh index 2528b438..47141efe 100755 --- a/utils/analysis_scripts/queue2csv.sh +++ b/utils/analysis_scripts/queue2csv.sh @@ -92,14 +92,14 @@ mkdir "$DIR" || exit 1 if [ -n "$3" -a -s "$DIR/../edges.txt" ]; then - cat "$DIR/"* | sed 's/:.*//' | sort -n | uniq -c | egrep '^[ \t]*1 ' | awk '{print$2}' > $DIR/../unique.txt + cat "$DIR/"* | sed 's/:.*//' | sort -n | uniq -c | grep -E '^[ \t]*1 ' | awk '{print$2}' > $DIR/../unique.txt if [ -s "$DIR/../unique.txt" ]; then ls "$DIR/id:"* | grep -v ",sync:" |sed 's/.*\/id:/id:/g' | while read file; do CNT=$(sed 's/:.*//' "$DIR/$file" | tee "$DIR/../tmp.txt" | wc -l) - DIFF=$(diff -u "$DIR/../tmp.txt" "$DIR/../unique.txt" | egrep '^-[0-9]' | wc -l) + DIFF=$(diff -u "$DIR/../tmp.txt" "$DIR/../unique.txt" | grep -E '^-[0-9]' | wc -l) UNIQUE=$(($CNT - $DIFF)) sed -i "s/;UNIQUE$file/;$UNIQUE/" "$DIR/../queue.csv" "$2" diff --git a/utils/argv_fuzzing/Makefile b/utils/argv_fuzzing/Makefile index 183f6bf8..6786467a 100644 --- a/utils/argv_fuzzing/Makefile +++ b/utils/argv_fuzzing/Makefile @@ -2,7 +2,7 @@ # american fuzzy lop++ - argvfuzz # -------------------------------- # -# Copyright 2019-2022 Kjell Braden <afflux@pentabarf.de> +# Copyright 2019-2023 Kjell Braden <afflux@pentabarf.de> # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -11,7 +11,7 @@ # http://www.apache.org/licenses/LICENSE-2.0 # -.PHONY: all install clean +.PHONY: all install clean argv_fuzz_persistent_demo argv_fuzz_demo demo PREFIX ?= /usr/local BIN_PATH = $(PREFIX)/bin @@ -41,7 +41,7 @@ __M32FLAG=$(_M32FLAG:00=-mbe32) ___M32FLAG=$(__M32FLAG:$(CC_IS_GCC)$(CC_IS_ARMCOMPILER)=-m32) M32FLAG=$(___M32FLAG) -all: argvfuzz32.so argvfuzz64.so +all: argvfuzz32.so argvfuzz64.so demo argvfuzz32.so: argvfuzz.c -@$(CC) $(M32FLAG) $(CFLAGS) $^ $(LDFLAGS) -o $@ 2>/dev/null || echo "argvfuzz32 build failure (that's fine)" @@ -54,5 +54,14 @@ install: argvfuzz32.so argvfuzz64.so if [ -f argvfuzz32.so ]; then set -e; install -m 755 argvfuzz32.so $(DESTDIR)$(HELPER_PATH)/; fi if [ -f argvfuzz64.so ]; then set -e; install -m 755 argvfuzz64.so $(DESTDIR)$(HELPER_PATH)/; fi +argv_fuzz_persistent_demo: argv_fuzz_persistent_demo.c + ../../afl-cc -g -o $@ $^ + +argv_fuzz_demo: argv_fuzz_demo.c + ../../afl-cc -g -o $@ $^ + +demo: argv_fuzz_persistent_demo argv_fuzz_demo + clean: - rm -f argvfuzz32.so argvfuzz64.so + rm -f argvfuzz32.so argvfuzz64.so argv_fuzz_demo argv_fuzz_persistent_demo + diff --git a/utils/argv_fuzzing/README.md b/utils/argv_fuzzing/README.md index e9224995..a085c098 100644 --- a/utils/argv_fuzzing/README.md +++ b/utils/argv_fuzzing/README.md @@ -1,16 +1,45 @@ -# argvfuzz +# argv_fuzzing feature +AFL++ supports fuzzing file inputs or standard input. The argv_fuzzing feature +allows for the fuzzing of arguments passed to a program from the command line +interface rather than from STDIN. -AFL++ supports fuzzing file inputs or stdin. When source is available, -`argv-fuzz-inl.h` can be used to change `main()` to build argv from stdin. +## With source code +When the source code is available, a specific macro from the `argv-fuzz-inl.h` +header file can be used to change the program's behavior to build argv from STDIN. +### Without persistent mode +Conditions needed to use the argv_fuzzing feature: +1. Include `argv-fuzz-inl.h` header file (`#include "argv-fuzz-inl.h"`) +2. Identify your main function that parses arguments +(for example, `int main(int argc, char **argv)`) +3. Use one of the following macros (near the beginning of the main function) +to initialize argv with the fuzzer's input: + - `AFL_INIT_ARGV();` or + - `AFL_INIT_SET0("prog_name");` to preserve `argv[0]` + (the name of the program being executed) + +see: [argv_fuzz_demo.c](argv_fuzz_demo.c) + +### With persistent mode +Conditions needed to use the argv_fuzzing feature with persistent mode: +1. Ensure your target can handle persistent mode fuzzing +2. Follow instructions in the [llvm_mode persistent mode](https://github.com/AFLplusplus/AFLplusplus/blob/stable/instrumentation/README.persistent_mode.md) +3. Use one of the following macros near the beginning of the main function and after +the buffer initialization (`unsigned char *buf = __AFL_FUZZ_TESTCASE_BUF`): + - `AFL_INIT_ARGV_PERSISTENT(buf)`, if you want to + - `AFL_INIT_SET0_PERSISTENT("name_of_binary", buf)` + +see: [argv_fuzz_persistent_demo.c](argv_fuzz_persistent_demo.c) + +## Binary only `argvfuzz` tries to provide the same functionality for binaries. When loaded using `LD_PRELOAD`, it will hook the call to `__libc_start_main` and replace argv using the same logic of `argv-fuzz-inl.h`. A few conditions need to be fulfilled for this mechanism to work correctly: -1. As it relies on hooking the loader, it cannot work on static binaries. +1. As it relies on hooking the loader, it cannot work on static binaries 2. If the target binary does not use the default libc's `_start` implementation (crt1.o), the hook may not run. -3. The hook will replace argv with pointers to `.data` of `argvfuzz.so`. If the - target binary expects argv to be living on the stack, things may go wrong. \ No newline at end of file +3. The hook will replace argv with pointers to `.data` of `argvfuzz.so`. +Things may go wrong if the target binary expects argv to live on the stack. diff --git a/utils/argv_fuzzing/argv-fuzz-inl.h b/utils/argv_fuzzing/argv-fuzz-inl.h index c15c0271..cb0af2bc 100644 --- a/utils/argv_fuzzing/argv-fuzz-inl.h +++ b/utils/argv_fuzzing/argv-fuzz-inl.h @@ -29,11 +29,17 @@ If you would like to always preserve argv[0], use this instead: AFL_INIT_SET0("prog_name"); + To enable persistent fuzzing, use the AFL_INIT_ARGV_PERSISTENT macro with + buf as argument, or use AFL_INIT_SET0_PERSISTENT("prog_name", buf) + to preserver argv[0]. buf is a pointer to a buffer containing + the input data for the current test case being processed defined as: + unsigned char *buf = __AFL_FUZZ_TESTCASE_BUF; */ #ifndef _HAVE_ARGV_FUZZ_INL #define _HAVE_ARGV_FUZZ_INL +#include <stdlib.h> #include <unistd.h> #define AFL_INIT_ARGV() \ @@ -52,6 +58,22 @@ \ } while (0) +#define AFL_INIT_ARGV_PERSISTENT(persistent_buff) \ + do { \ + \ + argv = afl_init_argv_persistent(&argc, persistent_buff); \ + \ + } while (0) + +#define AFL_INIT_SET0_PERSISTENT(_p, persistent_buff) \ + do { \ + \ + argv = afl_init_argv_persistent(&argc, persistent_buff); \ + argv[0] = (_p); \ + if (!argc) argc = 1; \ + \ + } while (0) + #define MAX_CMDLINE_LEN 100000 #define MAX_CMDLINE_PAR 50000 @@ -63,7 +85,10 @@ static char **afl_init_argv(int *argc) { char *ptr = in_buf; int rc = 0; - if (read(0, in_buf, MAX_CMDLINE_LEN - 2) < 0) {} + ssize_t num = read(0, in_buf, MAX_CMDLINE_LEN - 2); + if (num < 1) { _exit(1); } + in_buf[num] = '\0'; + in_buf[num + 1] = '\0'; while (*ptr && rc < MAX_CMDLINE_PAR) { @@ -83,6 +108,32 @@ static char **afl_init_argv(int *argc) { } +static char **afl_init_argv_persistent(int *argc, + unsigned char *persistent_buff) { + + static char *ret[MAX_CMDLINE_PAR]; + + unsigned char *ptr = persistent_buff; + int rc = 0; + + while (*ptr && rc < MAX_CMDLINE_PAR) { + + ret[rc] = (char *)ptr; + if (ret[rc][0] == 0x02 && !ret[rc][1]) ret[rc]++; + rc++; + + while (*ptr) + ptr++; + ptr++; + + } + + *argc = rc; + + return ret; + +} + #undef MAX_CMDLINE_LEN #undef MAX_CMDLINE_PAR diff --git a/utils/argv_fuzzing/argv_fuzz_demo.c b/utils/argv_fuzzing/argv_fuzz_demo.c new file mode 100644 index 00000000..6ab1e2e5 --- /dev/null +++ b/utils/argv_fuzzing/argv_fuzz_demo.c @@ -0,0 +1,28 @@ +#include <stdio.h> +#include <string.h> +#include "argv-fuzz-inl.h" + +int main(int argc, char **argv) { + + // Initialize the argv array for use with the AFL (American Fuzzy Lop) tool + AFL_INIT_ARGV(); + + /* Check the number of command line arguments and + compare the values of the first two arguments to specific strings. + If the number of arguments is not correct or the values do not match, + an error message is printed. If the values do match, the program + calls the abort() function. */ + if (argc > 1 && strcmp(argv[1], "XYZ") == 0) { + + if (strcmp(argv[2], "TEST2") == 0) { abort(); } + + } else { + + printf("Bad number of arguments!\n"); + + } + + return 0; + +} + diff --git a/utils/argv_fuzzing/argv_fuzz_persistent_demo.c b/utils/argv_fuzzing/argv_fuzz_persistent_demo.c new file mode 100644 index 00000000..016c3d35 --- /dev/null +++ b/utils/argv_fuzzing/argv_fuzz_persistent_demo.c @@ -0,0 +1,59 @@ +/* +This file contains a simple fuzzer for testing command line argument parsing +using persistent mode. +*/ + +#include <stdio.h> +#include <string.h> +#include "argv-fuzz-inl.h" + +__AFL_FUZZ_INIT(); + +/* The main function is an entry point for a program. + The argc parameter is an integer that indicates the number of arguments + passed to the program. The argv parameter is an array of character pointers, + with each element pointing to a null-terminated string that represents + one of the arguments. + */ +int main(int argc, char **argv) { + +#ifdef __AFL_HAVE_MANUAL_CONTROL + __AFL_INIT(); +#endif + unsigned char *buf = __AFL_FUZZ_TESTCASE_BUF; + + /* __AFL_LOOP() limits the maximum number of iterations before exiting + the loop and allowing the program to terminate. It protects against + accidental memory leaks and similar issues. */ + while (__AFL_LOOP(100000)) { + + int len = __AFL_FUZZ_TESTCASE_LEN; + + // Check that the length of the test case is at least 8 bytes + if (len < 8) continue; + + // Initialize the command line arguments using the testcase buffer + AFL_INIT_ARGV_PERSISTENT(buf); + + /* Check if the first argument is "XYZ" and the second argument is "TEST2" + If so, call the "abort" function to terminate the program. + Otherwise, print an error message. */ + if (argc > 1 && strcmp(argv[1], "XYZ") == 0) { + + if (strcmp(argv[2], "TEST2") == 0) { abort(); } + + } else { + + printf("Bad number of arguments!\n"); + + } + + } + + /* Exiting the loop allows the program to terminate normally. AFL will restart + the process with a clean slate for allocated memory, file descriptors, etc. + */ + return 0; + +} + diff --git a/utils/argv_fuzzing/argvfuzz.c b/utils/argv_fuzzing/argvfuzz.c index e7cc6b72..41eead0c 100644 --- a/utils/argv_fuzzing/argvfuzz.c +++ b/utils/argv_fuzzing/argvfuzz.c @@ -2,7 +2,7 @@ american fuzzy lop++ - LD_PRELOAD for fuzzing argv in binaries ------------------------------------------------------------ - Copyright 2019-2022 Kjell Braden <afflux@pentabarf.de> + Copyright 2019-2023 Kjell Braden <afflux@pentabarf.de> Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/utils/distributed_fuzzing/sync_script.sh b/utils/distributed_fuzzing/sync_script.sh index 251ae4e6..b22816f1 100755 --- a/utils/distributed_fuzzing/sync_script.sh +++ b/utils/distributed_fuzzing/sync_script.sh @@ -6,7 +6,7 @@ # Originally written by Michal Zalewski # # Copyright 2014 Google Inc. All rights reserved. -# Copyright 2019-2022 AFLplusplus Project. All rights reserved. +# Copyright 2019-2023 AFLplusplus Project. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/utils/libdislocator/README.md b/utils/libdislocator/README.md index e4934b5d..d0e45fff 100644 --- a/utils/libdislocator/README.md +++ b/utils/libdislocator/README.md @@ -34,8 +34,8 @@ heap-related security bugs in several ways: - Size alignment to `max_align_t` can be enforced with `AFL_ALIGNED_ALLOC=1`. In this case, a tail canary is inserted in the padding bytes at the end of the - allocated zone. This reduce the ability of libdislocator to detect - off-by-one bugs but also it make slibdislocator compliant to the C standard. + allocated zone. This reduces the ability of libdislocator to detect + off-by-one bugs but also it makes libdislocator compliant to the C standard. Basically, it is inspired by some of the non-default options available for the OpenBSD allocator - see malloc.conf(5) on that platform for reference. It is diff --git a/utils/libdislocator/libdislocator.so.c b/utils/libdislocator/libdislocator.so.c index 149b910e..1cd7abc6 100644 --- a/utils/libdislocator/libdislocator.so.c +++ b/utils/libdislocator/libdislocator.so.c @@ -6,7 +6,7 @@ Originally written by Michal Zalewski Copyright 2016 Google Inc. All rights reserved. - Copyright 2019-2022 AFLplusplus Project. All rights reserved. + Copyright 2019-2023 AFLplusplus Project. All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -304,7 +304,8 @@ static void *__dislocator_alloc(size_t len) { /* The "user-facing" wrapper for calloc(). This just checks for overflows and displays debug messages if requested. */ -void *calloc(size_t elem_len, size_t elem_cnt) { +__attribute__((malloc)) __attribute__((alloc_size(1, 2))) void *calloc( + size_t elem_len, size_t elem_cnt) { void *ret; @@ -339,7 +340,8 @@ void *calloc(size_t elem_len, size_t elem_cnt) { memory (unlike calloc(), malloc() is not guaranteed to return zeroed memory). */ -void *malloc(size_t len) { +__attribute__((malloc)) __attribute__((alloc_size(1))) void *malloc( + size_t len) { void *ret; @@ -398,7 +400,7 @@ void free(void *ptr) { /* Realloc is pretty straightforward, too. We forcibly reallocate the buffer, move data, and then free (aka mprotect()) the original one. */ -void *realloc(void *ptr, size_t len) { +__attribute__((alloc_size(2))) void *realloc(void *ptr, size_t len) { void *ret; @@ -450,7 +452,8 @@ int posix_memalign(void **ptr, size_t align, size_t len) { /* just the non-posix fashion */ -void *memalign(size_t align, size_t len) { +__attribute__((malloc)) __attribute__((alloc_size(2))) void *memalign( + size_t align, size_t len) { void *ret = NULL; @@ -466,7 +469,8 @@ void *memalign(size_t align, size_t len) { /* sort of C11 alias of memalign only more severe, alignment-wise */ -void *aligned_alloc(size_t align, size_t len) { +__attribute__((malloc)) __attribute__((alloc_size(2))) void *aligned_alloc( + size_t align, size_t len) { void *ret = NULL; @@ -484,7 +488,8 @@ void *aligned_alloc(size_t align, size_t len) { /* specific BSD api mainly checking possible overflow for the size */ -void *reallocarray(void *ptr, size_t elem_len, size_t elem_cnt) { +__attribute__((alloc_size(2, 3))) void *reallocarray(void *ptr, size_t elem_len, + size_t elem_cnt) { const size_t elem_lim = 1UL << (sizeof(size_t) * 4); const size_t elem_tot = elem_len * elem_cnt; @@ -505,6 +510,24 @@ void *reallocarray(void *ptr, size_t elem_len, size_t elem_cnt) { } +int reallocarr(void *ptr, size_t elem_len, size_t elem_cnt) { + + void *ret = NULL; + const size_t elem_tot = elem_len * elem_cnt; + + if (elem_tot == 0) { + + void **h = &ptr; + *h = ret; + return 0; + + } + + ret = reallocarray(ptr, elem_len, elem_cnt); + return ret ? 0 : -1; + +} + #if defined(__APPLE__) size_t malloc_size(const void *ptr) { diff --git a/utils/libtokencap/README.md b/utils/libtokencap/README.md index 50104291..8705452c 100644 --- a/utils/libtokencap/README.md +++ b/utils/libtokencap/README.md @@ -47,9 +47,11 @@ by AFL++ in that earlier run. This demonstrates the basic principle: ``` export AFL_TOKEN_FILE=$PWD/temp_output.txt + timeout_sec="5" for i in <out_dir>/queue/id*; do LD_PRELOAD=/path/to/libtokencap.so \ + timeout -s SIGKILL ${timeout_sec} \ /path/to/target/program [...params, including $i...] done diff --git a/utils/libtokencap/libtokencap.so.c b/utils/libtokencap/libtokencap.so.c index 5dcb8f4c..299056ab 100644 --- a/utils/libtokencap/libtokencap.so.c +++ b/utils/libtokencap/libtokencap.so.c @@ -6,7 +6,7 @@ Originally written by Michal Zalewski Copyright 2016 Google Inc. All rights reserved. - Copyright 2019-2022 AFLplusplus Project. All rights reserved. + Copyright 2019-2023 AFLplusplus Project. All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -378,7 +378,8 @@ __attribute__((hot)) int strcmp(const char *str1, const char *str2) { #undef strncmp -__attribute__((hot)) int strncmp(const char *str1, const char *str2, size_t len) { +__attribute__((hot)) int strncmp(const char *str1, const char *str2, + size_t len) { if (__tokencap_is_ro(str1)) __tokencap_dump(str1, len, 1); if (__tokencap_is_ro(str2)) __tokencap_dump(str2, len, 1); @@ -428,7 +429,8 @@ __attribute__((hot)) int strcasecmp(const char *str1, const char *str2) { #undef strncasecmp -__attribute__((hot)) int strncasecmp(const char *str1, const char *str2, size_t len) { +__attribute__((hot)) int strncasecmp(const char *str1, const char *str2, + size_t len) { if (__tokencap_is_ro(str1)) __tokencap_dump(str1, len, 1); if (__tokencap_is_ro(str2)) __tokencap_dump(str2, len, 1); @@ -454,7 +456,8 @@ __attribute__((hot)) int strncasecmp(const char *str1, const char *str2, size_t #undef memcmp -__attribute__((hot)) int memcmp(const void *mem1, const void *mem2, size_t len) { +__attribute__((hot)) int memcmp(const void *mem1, const void *mem2, + size_t len) { if (__tokencap_is_ro(mem1)) __tokencap_dump(mem1, len, 0); if (__tokencap_is_ro(mem2)) __tokencap_dump(mem2, len, 0); @@ -537,7 +540,8 @@ __attribute__((hot)) char *strstr(const char *haystack, const char *needle) { #undef strcasestr -__attribute__((hot)) char *strcasestr(const char *haystack, const char *needle) { +__attribute__((hot)) char *strcasestr(const char *haystack, + const char *needle) { if (__tokencap_is_ro(haystack)) __tokencap_dump(haystack, strlen(haystack), 1); @@ -566,8 +570,8 @@ __attribute__((hot)) char *strcasestr(const char *haystack, const char *needle) #undef memmem -__attribute__((hot)) void *memmem(const void *haystack, size_t haystack_len, const void *needle, - size_t needle_len) { +__attribute__((hot)) void *memmem(const void *haystack, size_t haystack_len, + const void *needle, size_t needle_len) { if (__tokencap_is_ro(haystack)) __tokencap_dump(haystack, haystack_len, 1); diff --git a/utils/persistent_mode/test-instr.c b/utils/persistent_mode/test-instr.c index 168aa429..4ead6577 100644 --- a/utils/persistent_mode/test-instr.c +++ b/utils/persistent_mode/test-instr.c @@ -3,7 +3,7 @@ -------------------------------------------------------- Originally written by Michal Zalewski Copyright 2014 Google Inc. All rights reserved. - Copyright 2019-2022 AFLplusplus Project. All rights reserved. + Copyright 2019-2023 AFLplusplus Project. All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at: |