diff options
34 files changed, 1713 insertions, 740 deletions
diff --git a/.custom-format.py b/.custom-format.py index 7ac63396..428d7b0d 100755 --- a/.custom-format.py +++ b/.custom-format.py @@ -19,40 +19,22 @@ import subprocess import sys import os import re +import shutil # string_re = re.compile('(\\"(\\\\.|[^"\\\\])*\\")') # future use with open(".clang-format") as f: fmt = f.read() -CLANG_FORMAT_BIN = os.getenv("CLANG_FORMAT_BIN") -if CLANG_FORMAT_BIN is None: - o = 0 - try: - p = subprocess.Popen(["clang-format-11", "--version"], stdout=subprocess.PIPE) - o, _ = p.communicate() - o = str(o, "utf-8") - o = re.sub(r".*ersion ", "", o) - # o = o[len("clang-format version "):].strip() - o = o[: o.find(".")] - o = int(o) - except: - print("clang-format-11 is needed. Aborted.") - exit(1) - # if o < 7: - # if subprocess.call(['which', 'clang-format-7'], stdout=subprocess.PIPE) == 0: - # CLANG_FORMAT_BIN = 'clang-format-7' - # elif subprocess.call(['which', 'clang-format-8'], stdout=subprocess.PIPE) == 0: - # CLANG_FORMAT_BIN = 'clang-format-8' - # elif subprocess.call(['which', 'clang-format-9'], stdout=subprocess.PIPE) == 0: - # CLANG_FORMAT_BIN = 'clang-format-9' - # elif subprocess.call(['which', 'clang-format-11'], stdout=subprocess.PIPE) == 0: - # CLANG_FORMAT_BIN = 'clang-format-11' - # else: - # print ("clang-format 7 or above is needed. Aborted.") - # exit(1) - else: - CLANG_FORMAT_BIN = "clang-format-11" +CURRENT_LLVM = os.getenv('LLVM_VERSION', 14) +CLANG_FORMAT_BIN = os.getenv("CLANG_FORMAT_BIN", "") + +if shutil.which(CLANG_FORMAT_BIN) is None: + CLANG_FORMAT_BIN = f"clang-format-{CURRENT_LLVM}" + +if shutil.which(CLANG_FORMAT_BIN) is None: + print(f"[!] clang-format-{CURRENT_LLVM} is needed. Aborted.") + exit(1) COLUMN_LIMIT = 80 for line in fmt.split("\n"): diff --git a/.dockerignore b/.dockerignore index d05bf1c6..271d338c 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,65 +1,75 @@ -.test -.test2 -.sync_tmp +!/coresight_mode +*.dSYM *.o -*.so *.pyc -*.dSYM -as -ld -in -out -core* +*.so +.sync_tmp +.test +.test2 +.git +.dockerignore +.github +CITATION.cff +CONTRIBUTING.md +Changelog.md +Dockerfile +LICENSE +TODO.md afl-analyze +afl-analyze.8 afl-as +afl-as.8 afl-clang -afl-clang\+\+ afl-clang-fast +afl-clang-fast.8 afl-clang-fast\+\+ +afl-clang-fast\+\+.8 afl-clang-lto +afl-clang-lto.8 afl-clang-lto\+\+ +afl-clang-lto\+\+.8 +afl-clang\+\+ +afl-cmin.8 +afl-cmin.bash.8 afl-fuzz +afl-fuzz.8 afl-g\+\+ +afl-g\+\+-fast +afl-g\+\+-fast.8 afl-gcc afl-gcc-fast -afl-g\+\+-fast +afl-gcc-fast.8 +afl-gcc.8 afl-gotcpu +afl-gotcpu.8 afl-ld afl-ld-lto +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-gcc.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 +as +core* +examples/afl_frida/afl-frida +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 +in +ld +out qemu_mode/libcompcov/compcovtest qemu_mode/qemu-* -unicorn_mode/samples/*/\.test-* -unicorn_mode/samples/*/output -unicorn_mode/unicornafl +test/unittests/unit_hash +test/unittests/unit_list 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 -examples/afl_frida/afl-frida -examples/afl_frida/libtestinstr.so -examples/afl_frida/frida-gum-example.c -examples/afl_frida/frida-gum.h \ No newline at end of file +unicorn_mode/samples/*/\.test-* +unicorn_mode/samples/*/output +unicorn_mode/unicornafl diff --git a/.github/workflows/build_aflplusplus_docker.yaml b/.github/workflows/build_aflplusplus_docker.yaml deleted file mode 100644 index 7245a84e..00000000 --- a/.github/workflows/build_aflplusplus_docker.yaml +++ /dev/null @@ -1,49 +0,0 @@ -name: Publish Docker Images - -on: - push: - branches: - - stable - - dev - tags: - - '*' - -jobs: - push_to_registry: - name: Push Docker images to Dockerhub - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@master - - name: Set up QEMU - uses: docker/setup-qemu-action@v2 - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v2 - - name: Login to Dockerhub - uses: docker/login-action@v2 - with: - username: ${{ secrets.DOCKER_USERNAME }} - password: ${{ secrets.DOCKER_TOKEN }} - - name: Publish dev as dev to docker.io registry - uses: docker/build-push-action@v3 - with: - context: . - platforms: linux/amd64,linux/arm64 - push: true - tags: aflplusplus/aflplusplus:${{ github.ref_name }} - if: ${{ github.ref_name == 'dev' }} - - name: Publish stable as stable and latest to docker.io registry - uses: docker/build-push-action@v3 - with: - context: . - platforms: linux/amd64,linux/arm64 - push: true - tags: aflplusplus/aflplusplus:${{ github.ref_name }},aflplusplus/aflplusplus:latest - if: ${{ github.ref_name == 'stable' }} - - name: Publish tagged release to docker.io registry - uses: docker/build-push-action@v3 - with: - context: . - platforms: linux/amd64,linux/arm64 - push: true - tags: aflplusplus/aflplusplus:${{ github.ref_name }} - if: ${{ github.ref_type == 'tag' }} diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 799b72e7..04cbaca8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -2,13 +2,16 @@ name: CI on: push: - branches: [ stable, dev ] + branches: + - stable + - dev pull_request: - branches: [ stable, dev ] + branches: + - dev # No need for stable-pull-request, as that equals dev-push jobs: linux: - runs-on: '${{ matrix.os }}' + runs-on: "${{ matrix.os }}" strategy: matrix: os: [ubuntu-22.04, ubuntu-20.04, ubuntu-18.04] @@ -16,7 +19,7 @@ jobs: AFL_SKIP_CPUFREQ: 1 AFL_I_DONT_CARE_ABOUT_MISSING_CRASHES: 1 steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: debug run: apt-cache search plugin-dev | grep gcc-; echo; apt-cache search clang-format- | grep clang-format- - name: update @@ -38,9 +41,9 @@ jobs: AFL_SKIP_CPUFREQ: 1 AFL_I_DONT_CARE_ABOUT_MISSING_CRASHES: 1 steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: install - run: brew install make gcc + run: brew install make gcc llvm - name: fix install run: cd /usr/local/bin; ln -s gcc-11 gcc; ln -s g++-11 g++; which gcc; gcc -v - name: build diff --git a/.github/workflows/code-format.yml b/.github/workflows/code-format.yml new file mode 100644 index 00000000..314137ff --- /dev/null +++ b/.github/workflows/code-format.yml @@ -0,0 +1,33 @@ +name: Formatting + +on: + push: + branches: + - stable + - dev + pull_request: + branches: + - dev # No need for stable-pull-request, as that equals dev-push + +jobs: + code-format-check: + name: Check code format + if: ${{ 'false' == 'true' }} # Disable the job + runs-on: ubuntu-22.04 + container: docker.io/aflplusplus/aflplusplus:dev + steps: + - name: Checkout + uses: actions/checkout@v3 + - name: Format + run: | + git config --global --add safe.directory /__w/AFLplusplus/AFLplusplus + apt-get update + apt-get install -y clang-format-${LLVM_VERSION} + make code-format + - name: Check if code needed formatting + run: | + git --no-pager -c color.ui=always diff HEAD + if ! git diff HEAD --quiet; then + echo "[!] Please run 'make code-format' and push its changes." + exit 1 + fi diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index eda8dfd0..75935123 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -2,31 +2,32 @@ name: "CodeQL" on: push: - branches: [ stable, dev ] + branches: + - stable + - dev pull_request: - branches: [ stable, dev ] + branches: + - dev # No need for stable-pull-request, as that equals dev-push jobs: analyze: name: Analyze runs-on: ubuntu-latest - - strategy: - fail-fast: false - matrix: - language: [ 'cpp' ] - + container: # We use a previous image as it's expected to have all the dependencies + image: docker.io/aflplusplus/aflplusplus:dev steps: - - name: Checkout repository - uses: actions/checkout@v2 - - - name: Initialize CodeQL - uses: github/codeql-action/init@v1 - with: - languages: ${{ matrix.language }} - - - name: Autobuild - uses: github/codeql-action/autobuild@v1 - - - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v1 + - name: Fix for using external repo in container build # https://github.com/actions/checkout/issues/760 + run: git config --global --add safe.directory /__w/AFLplusplus/AFLplusplus + - name: Checkout + uses: actions/checkout@v3 + - name: Initialize CodeQL + uses: github/codeql-action/init@v2 + with: + languages: cpp, python + - name: Build AFLplusplus # Rebuild because CodeQL needs to monitor the build process + env: + CC: gcc # These are symlinked to the version used in the container build + CXX: g++ + run: make -i all # Best effort using -i + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v2 diff --git a/.github/workflows/container.yml b/.github/workflows/container.yml new file mode 100644 index 00000000..8836997d --- /dev/null +++ b/.github/workflows/container.yml @@ -0,0 +1,75 @@ +name: Container +on: + push: + branches: + - stable + - dev + tags: + - "*" + pull_request: + branches: + - dev # No need for stable-pull-request, as that equals dev-push + +jobs: + build-and-test-amd64: + name: Test amd64 image + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v3 + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v2 + - name: Build amd64 + uses: docker/build-push-action@v3 + with: + context: . + tags: aflplusplus:test-amd64 + load: true + cache-to: type=gha,mode=max + build-args: | + TEST_BUILD=1 + - name: Test amd64 + run: > + docker run --rm aflplusplus:test-amd64 bash -c " + apt-get update && + apt-get install -y libcmocka-dev && + make -i tests + " + + push: + name: Push amd64 and arm64 images + runs-on: ubuntu-latest + needs: + - build-and-test-amd64 + if: ${{ github.event_name == 'push' && github.repository == 'AFLplusplus/AFLplusplus' }} + steps: + - name: Checkout + uses: actions/checkout@v3 + - name: Set up QEMU + uses: docker/setup-qemu-action@v2 + with: + platforms: arm64 + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v2 + - name: Login to docker.io + uses: docker/login-action@v2 + with: + username: ${{ secrets.DOCKER_USERNAME }} + password: ${{ secrets.DOCKER_TOKEN }} + - name: Set tags to push + id: push-tags + run: | + PUSH_TAGS=docker.io/aflplusplus/aflplusplus:${GITHUB_REF_NAME} + if [ "${GITHUB_REF_NAME}" = "stable" ]; then + PUSH_TAGS=${PUSH_TAGS},docker.io/aflplusplus/aflplusplus:latest + fi + export PUSH_TAGS + echo "::set-output name=PUSH_TAGS::${PUSH_TAGS}" + - name: Push to docker.io registry + uses: docker/build-push-action@v3 + with: + context: . + platforms: linux/amd64,linux/arm64 + push: true + tags: ${{ steps.push-tags.outputs.PUSH_TAGS }} + cache-from: type=gha diff --git a/.github/workflows/rust_custom_mutator.yml b/.github/workflows/rust_custom_mutator.yml index c279439e..7c2f0c12 100644 --- a/.github/workflows/rust_custom_mutator.yml +++ b/.github/workflows/rust_custom_mutator.yml @@ -2,9 +2,12 @@ name: Rust Custom Mutators on: push: - branches: [ stable, dev ] + branches: + - stable + - dev pull_request: - branches: [ stable, dev ] + branches: + - dev # No need for stable-pull-request, as that equals dev-push jobs: test: @@ -17,7 +20,7 @@ jobs: matrix: os: [ubuntu-22.04, ubuntu-20.04] steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Install Rust Toolchain uses: actions-rs/toolchain@v1 with: @@ -27,4 +30,4 @@ jobs: - name: Run General Tests run: cargo test - name: Run Tests for afl_internals feature flag - run: cd custom_mutator && cargo test --features=afl_internals \ No newline at end of file + run: cd custom_mutator && cargo test --features=afl_internals diff --git a/Dockerfile b/Dockerfile index bdfa1c56..f1b2fc01 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,81 +1,88 @@ # # This Dockerfile for AFLplusplus uses Ubuntu 22.04 jammy and -# installs LLVM 14 for afl-clang-lto support :-) +# installs LLVM 14 for afl-clang-lto support. +# +# GCC 11 is used instead of 12 because genhtml for afl-cov doesn't like it. # FROM ubuntu:22.04 AS aflplusplus LABEL "maintainer"="afl++ team <afl@aflplus.plus>" -LABEL "about"="AFLplusplus docker image" +LABEL "about"="AFLplusplus container image" ARG DEBIAN_FRONTEND=noninteractive -env NO_ARCH_OPT 1 +ENV NO_ARCH_OPT=1 +ENV IS_DOCKER=1 -RUN apt-get update && \ - apt-get -y install --no-install-suggests --no-install-recommends \ - automake \ - cmake \ - meson \ - ninja-build \ - bison flex \ - build-essential \ - git \ - python3 python3-dev python3-setuptools python-is-python3 \ - libtool libtool-bin \ - libglib2.0-dev \ - wget vim jupp nano bash-completion less \ - apt-utils apt-transport-https ca-certificates gnupg dialog \ - libpixman-1-dev \ - gnuplot-nox \ - && rm -rf /var/lib/apt/lists/* +RUN apt-get update && apt-get full-upgrade -y && \ + apt-get install -y --no-install-recommends wget ca-certificates && \ + rm -rf /var/lib/apt/lists/* -# TODO: reactivate in timely manner -#RUN echo "deb http://apt.llvm.org/jammy/ llvm-toolchain-jammy-15 main" >> /etc/apt/sources.list && \ -# wget -qO - https://apt.llvm.org/llvm-snapshot.gpg.key | apt-key add - +ENV LLVM_VERSION=14 +ENV GCC_VERSION=11 -RUN echo "deb http://ppa.launchpad.net/ubuntu-toolchain-r/test/ubuntu jammy main" >> /etc/apt/sources.list && \ - apt-key adv --recv-keys --keyserver keyserver.ubuntu.com 1E9377A2BA9EF27F +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 -RUN apt-get update && apt-get full-upgrade -y && \ - apt-get -y install --no-install-suggests --no-install-recommends \ - gcc-12 g++-12 gcc-12-plugin-dev gdb lcov \ - clang-14 clang-tools-14 libc++1-14 libc++-14-dev \ - libc++abi1-14 libc++abi-14-dev libclang1-14 libclang-14-dev \ - libclang-common-14-dev libclang-cpp14 libclang-cpp14-dev liblld-14 \ - liblld-14-dev liblldb-14 liblldb-14-dev libllvm14 libomp-14-dev \ - libomp5-14 lld-14 lldb-14 llvm-14 llvm-14-dev llvm-14-runtime llvm-14-tools - -# arm64 doesn't have gcc-multilib, and it's only used for -m32 support on x86 -ARG TARGETPLATFORM -RUN [ "$TARGETPLATFORM" = "linux/amd64" ] && \ - apt-get -y install --no-install-suggests --no-install-recommends \ - gcc-10-multilib gcc-multilib || true - -RUN rm -rf /var/lib/apt/lists/* - -RUN update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-12 0 -RUN update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-12 0 - -ENV LLVM_CONFIG=llvm-config-14 +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 \ + libtool libtool-bin libglib2.0-dev \ + apt-utils 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-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} \ + lld-${LLVM_VERSION} lldb-${LLVM_VERSION} llvm-${LLVM_VERSION} \ + llvm-${LLVM_VERSION}-dev llvm-${LLVM_VERSION}-runtime llvm-${LLVM_VERSION}-tools \ + $([ "$(dpkg --print-architecture)" = "amd64" ] && echo gcc-${GCC_VERSION}-multilib gcc-multilib) \ + $([ "$(dpkg --print-architecture)" = "arm64" ] && echo libcapstone-dev) && \ + rm -rf /var/lib/apt/lists/* + # gcc-multilib is only used for -m32 support on x86 + # libcapstone-dev is used for coresight_mode on arm64 + +RUN update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-${GCC_VERSION} 0 && \ + update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-${GCC_VERSION} 0 && \ + update-alternatives --install /usr/bin/clang clang /usr/bin/clang-${LLVM_VERSION} 0 && \ + update-alternatives --install /usr/bin/clang++ clang++ /usr/bin/clang++-${LLVM_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 + +ENV LLVM_CONFIG=llvm-config-${LLVM_VERSION} ENV AFL_SKIP_CPUFREQ=1 ENV AFL_TRY_AFFINITY=1 ENV AFL_I_DONT_CARE_ABOUT_MISSING_CRASHES=1 -RUN git clone --depth=1 https://github.com/vanhauser-thc/afl-cov /afl-cov -RUN cd /afl-cov && make install && cd .. +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 -COPY . /AFLplusplus WORKDIR /AFLplusplus +COPY . . + +ARG CC=gcc-$GCC_VERSION +ARG CXX=g++-$GCC_VERSION -RUN export CC=gcc-12 && export CXX=g++-12 && make clean && \ - make distrib && make install && make clean +# Used in CI to prevent a 'make clean' which would remove the binaries to be tested +ARG TEST_BUILD -RUN sh -c 'echo set encoding=utf-8 > /root/.vimrc' -RUN echo '. /etc/bash_completion' >> ~/.bashrc -RUN echo 'alias joe="joe --wordwrap --joe_state -nobackup"' >> ~/.bashrc -RUN echo "export PS1='"'[afl++ \h] \w$(__git_ps1) \$ '"'" >> ~/.bashrc -ENV IS_DOCKER="1" +RUN sed -i.bak 's/^ -/ /g' GNUmakefile && \ + make clean && make distrib && \ + ([ "${TEST_BUILD}" ] || (make install && make clean)) && \ + mv GNUmakefile.bak GNUmakefile -# Disabled as there are now better alternatives -#COPY --from=aflplusplus/afl-dyninst /usr/local/lib/libdyninstAPI_RT.so /usr/local/lib/libdyninstAPI_RT.so -#COPY --from=aflplusplus/afl-dyninst /afl-dyninst/libAflDyninst.so /usr/local/lib/libAflDyninst.so +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 diff --git a/GNUmakefile b/GNUmakefile index 42d48b68..b6865f0c 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -312,7 +312,7 @@ all: test_x86 test_shm test_python ready $(PROGS) afl-as llvm gcc_plugin test_bu .PHONY: llvm llvm: - -$(MAKE) -j4 -f GNUmakefile.llvm + -$(MAKE) -j$(nproc) -f GNUmakefile.llvm @test -e afl-cc || { echo "[-] Compiling afl-cc failed. You seem not to have a working compiler." ; exit 1; } .PHONY: gcc_plugin @@ -572,7 +572,7 @@ clean: -$(MAKE) -f GNUmakefile.gcc_plugin clean -$(MAKE) -C utils/libdislocator clean -$(MAKE) -C utils/libtokencap clean - $(MAKE) -C utils/aflpp_driver clean + -$(MAKE) -C utils/aflpp_driver clean -$(MAKE) -C utils/afl_network_proxy clean -$(MAKE) -C utils/socket_fuzzing clean -$(MAKE) -C utils/argv_fuzzing clean @@ -610,7 +610,7 @@ endif .PHONY: distrib distrib: all - -$(MAKE) -j4 -f GNUmakefile.llvm + -$(MAKE) -j$(nproc) -f GNUmakefile.llvm ifneq "$(SYS)" "Darwin" -$(MAKE) -f GNUmakefile.gcc_plugin endif @@ -623,15 +623,23 @@ endif -$(MAKE) -C frida_mode ifneq "$(SYS)" "Darwin" ifeq "$(ARCH)" "aarch64" + ifndef NO_CORESIGHT -$(MAKE) -C coresight_mode + endif endif ifeq "$(SYS)" "Linux" -ifndef NO_NYX + 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 + -cd unicorn_mode && unset CFLAGS && sh ./build_unicorn_support.sh + endif + else -cd unicorn_mode && unset CFLAGS && sh ./build_unicorn_support.sh + endif endif .PHONY: binary-only @@ -645,7 +653,9 @@ binary-only: test_shm test_python ready $(PROGS) -$(MAKE) -C frida_mode ifneq "$(SYS)" "Darwin" ifeq "$(ARCH)" "aarch64" + ifndef NO_CORESIGHT -$(MAKE) -C coresight_mode + endif endif ifeq "$(SYS)" "Linux" ifndef NO_NYX @@ -653,12 +663,18 @@ ifndef NO_NYX endif endif -cd qemu_mode && sh ./build_qemu_support.sh + ifeq "$(ARCH)" "aarch64" + ifndef NO_UNICORN_ARM64 -cd unicorn_mode && unset CFLAGS && sh ./build_unicorn_support.sh + endif + else + -cd unicorn_mode && unset CFLAGS && sh ./build_unicorn_support.sh + endif endif .PHONY: source-only source-only: all - -$(MAKE) -j4 -f GNUmakefile.llvm + -$(MAKE) -j$(nproc) -f GNUmakefile.llvm ifneq "$(SYS)" "Darwin" -$(MAKE) -f GNUmakefile.gcc_plugin endif diff --git a/GNUmakefile.gcc_plugin b/GNUmakefile.gcc_plugin index e21203ae..17bd825d 100644 --- a/GNUmakefile.gcc_plugin +++ b/GNUmakefile.gcc_plugin @@ -100,7 +100,9 @@ ifeq "$(SYS)" "SunOS" endif -PROGS = ./afl-gcc-pass.so ./afl-compiler-rt.o ./afl-compiler-rt-32.o ./afl-compiler-rt-64.o +PASSES = ./afl-gcc-pass.so ./afl-gcc-cmplog-pass.so ./afl-gcc-cmptrs-pass.so + +PROGS = $(PASSES) ./afl-compiler-rt.o ./afl-compiler-rt-32.o ./afl-compiler-rt-64.o .PHONY: all all: test_shm test_deps $(PROGS) test_build all_done @@ -141,6 +143,8 @@ afl-common.o: ./src/afl-common.c @printf "[*] Building 64-bit variant of the runtime (-m64)... " @$(CC) $(CFLAGS_SAFE) $(CPPFLAGS) -O3 -Wno-unused-result -m64 -fPIC -c $< -o $@ 2>/dev/null; if [ "$$?" = "0" ]; then echo "success!"; else echo "failed (that's fine)"; fi +$(PASSES): instrumentation/afl-gcc-common.h + ./afl-gcc-pass.so: instrumentation/afl-gcc-pass.so.cc | test_deps $(CXX) $(CXXEFLAGS) $(PLUGIN_FLAGS) -shared $< -o $@ ln -sf afl-cc afl-gcc-fast @@ -148,6 +152,12 @@ afl-common.o: ./src/afl-common.c ln -sf afl-cc.8 afl-gcc-fast.8 ln -sf afl-cc.8 afl-g++-fast.8 +./afl-gcc-cmplog-pass.so: instrumentation/afl-gcc-cmplog-pass.so.cc | test_deps + $(CXX) $(CXXEFLAGS) $(PLUGIN_FLAGS) -shared $< -o $@ + +./afl-gcc-cmptrs-pass.so: instrumentation/afl-gcc-cmptrs-pass.so.cc | test_deps + $(CXX) $(CXXEFLAGS) $(PLUGIN_FLAGS) -shared $< -o $@ + .PHONY: test_build test_build: $(PROGS) @echo "[*] Testing the CC wrapper and instrumentation output..." @@ -190,6 +200,8 @@ install: all ln -sf afl-c++ $${DESTDIR}$(BIN_PATH)/afl-g++-fast ln -sf afl-compiler-rt.o $${DESTDIR}$(HELPER_PATH)/afl-gcc-rt.o install -m 755 ./afl-gcc-pass.so $${DESTDIR}$(HELPER_PATH) + install -m 755 ./afl-gcc-cmplog-pass.so $${DESTDIR}$(HELPER_PATH) + install -m 755 ./afl-gcc-cmptrs-pass.so $${DESTDIR}$(HELPER_PATH) install -m 644 -T instrumentation/README.gcc_plugin.md $${DESTDIR}$(DOC_PATH)/README.gcc_plugin.md .PHONY: clean diff --git a/coresight_mode/GNUmakefile b/coresight_mode/GNUmakefile index 9ab30ff7..167b83fa 100644 --- a/coresight_mode/GNUmakefile +++ b/coresight_mode/GNUmakefile @@ -54,7 +54,7 @@ $(GLIBC_LDSO): | $(GLIBC_NAME).tar.xz $(MAKE) install $(GLIBC_NAME).tar.xz: - wget -O $@ $(GLIBC_URL_BASE)/$@ + wget -qO $@ $(GLIBC_URL_BASE)/$@ clean: $(MAKE) -C $(CS_TRACE) clean diff --git a/custom_mutators/grammar_mutator/build_grammar_mutator.sh b/custom_mutators/grammar_mutator/build_grammar_mutator.sh index e8594ba3..5121b07f 100755 --- a/custom_mutators/grammar_mutator/build_grammar_mutator.sh +++ b/custom_mutators/grammar_mutator/build_grammar_mutator.sh @@ -128,7 +128,7 @@ git pull >/dev/null 2>&1 sh -c 'git stash && git stash drop' 1>/dev/null 2>/dev/null git checkout "$GRAMMAR_VERSION" || exit 1 echo "[*] Downloading antlr..." -wget -c https://www.antlr.org/download/antlr-4.8-complete.jar +wget -q https://www.antlr.org/download/antlr-4.8-complete.jar cd .. echo diff --git a/docs/Changelog.md b/docs/Changelog.md index 737df7fa..7284500e 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -8,6 +8,11 @@ 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.02a (dev) + - gcc_plugin: + - Adacore submitted CMPLOG support to the gcc_plugin! :-) + + ### Version ++4.01c (release) - fixed */build_...sh scripts to work outside of git - new custom_mutator: libafl with token fuzzing :) diff --git a/docs/INSTALL.md b/docs/INSTALL.md index 41ec8561..9036a1f1 100644 --- a/docs/INSTALL.md +++ b/docs/INSTALL.md @@ -26,7 +26,7 @@ whatever llvm version is available! ```shell sudo apt-get update -sudo apt-get install -y build-essential python3-dev automake cmake git flex bison libglib2.0-dev libpixman-1-dev python3-setuptools +sudo apt-get install -y build-essential python3-dev automake cmake git flex bison libglib2.0-dev libpixman-1-dev python3-setuptools cargo libgtk-3-dev # try to install llvm 12 and install the distro default if that fails sudo apt-get install -y lld-12 llvm-12 llvm-12-dev clang-12 || sudo apt-get install -y lld llvm llvm-dev clang sudo apt-get install -y gcc-$(gcc --version|head -n1|sed 's/\..*//'|sed 's/.* //')-plugin-dev libstdc++-$(gcc --version|head -n1|sed 's/\..*//'|sed 's/.* //')-dev diff --git a/docs/env_variables.md b/docs/env_variables.md index 0598a809..c836a929 100644 --- a/docs/env_variables.md +++ b/docs/env_variables.md @@ -160,6 +160,8 @@ Available options: Setting `AFL_LLVM_CMPLOG=1` during compilation will tell afl-clang-fast to produce a CmpLog binary. +For afl-gcc-fast set `AFL_GCC_CMPLOG=1` instead. + For more information, see [instrumentation/README.cmplog.md](../instrumentation/README.cmplog.md). diff --git a/docs/features.md b/docs/features.md index dd3d2bcb..212302f8 100644 --- a/docs/features.md +++ b/docs/features.md @@ -12,7 +12,7 @@ QEMU 5.1 with laf-intel and Redqueen, FRIDA mode, unicorn mode, gcc plugin, full | NeverZero [B] | x86[_64] | x(1) | x | x | x | x | | | | Persistent Mode [C] | | x | x | x86[_64]/arm64 | x86[_64]/arm[64] | x | | | | LAF-Intel / CompCov [D] | | x | | | x86[_64]/arm[64] | x86[_64]/arm[64] | x86[_64] | | -| CmpLog [E] | | x | | x86[_64]/arm64 | x86[_64]/arm[64] | | | | +| CmpLog [E] | | x | x | x86[_64]/arm64 | x86[_64]/arm[64] | | | | | Selective Instrumentation [F] | | x | x | x | x | | | | | Non-Colliding Coverage [G] | | x(4) | | | (x)(5) | | | | | Ngram prev_loc Coverage [H] | | x(6) | | | | | | | diff --git a/frida_mode/GNUmakefile b/frida_mode/GNUmakefile index 9c37cfa3..b9b47b62 100644 --- a/frida_mode/GNUmakefile +++ b/frida_mode/GNUmakefile @@ -275,7 +275,7 @@ endif else $(GUM_DEVKIT_TARBALL): | $(FRIDA_BUILD_DIR) - wget -O $@ $(GUM_DEVKIT_URL) || curl -L -o $@ $(GUM_DEVKIT_URL) + wget -qO $@ $(GUM_DEVKIT_URL) || curl -L -o $@ $(GUM_DEVKIT_URL) $(GUM_DEVIT_LIBRARY): $(GUM_DEVKIT_TARBALL) tar Jxvfm $(GUM_DEVKIT_TARBALL) -C $(FRIDA_BUILD_DIR) diff --git a/frida_mode/src/instrument/instrument_coverage.c b/frida_mode/src/instrument/instrument_coverage.c index 098e7269..ec421861 100644 --- a/frida_mode/src/instrument/instrument_coverage.c +++ b/frida_mode/src/instrument/instrument_coverage.c @@ -289,9 +289,9 @@ static void coverage_write_modules(int fd, GArray *coverage_modules) { coverage_format(fd, "%016" G_GINT64_MODIFIER "X, ", module->base_address); coverage_format(fd, "%016" G_GINT64_MODIFIER "X, ", module->limit); /* entry */ - coverage_format(fd, "%016" G_GINT64_MODIFIER "X, ", 0); + coverage_format(fd, "%016" G_GINT64_MODIFIER "X, ", 0UL); /* checksum */ - coverage_format(fd, "%016" G_GINT64_MODIFIER "X, ", 0); + coverage_format(fd, "%016" G_GINT64_MODIFIER "X, ", 0UL); /* timestamp */ coverage_format(fd, "%08" G_GINT32_MODIFIER "X, ", 0); coverage_format(fd, "%s\n", module->path); diff --git a/frida_mode/src/instrument/instrument_debug.c b/frida_mode/src/instrument/instrument_debug.c index 592ab673..d26f9cec 100644 --- a/frida_mode/src/instrument/instrument_debug.c +++ b/frida_mode/src/instrument/instrument_debug.c @@ -66,7 +66,7 @@ static void instrument_disasm(guint8 *start, guint8 *end, instrument_debug("\t0x%" G_GINT64_MODIFIER "x\t* 0x%016" G_GSIZE_MODIFIER "x\n", - curr, *(size_t *)curr); + (uint64_t)curr, *(size_t *)curr); len += sizeof(size_t); continue; diff --git a/instrumentation/README.gcc_plugin.md b/instrumentation/README.gcc_plugin.md index ed39af9d..011a574a 100644 --- a/instrumentation/README.gcc_plugin.md +++ b/instrumentation/README.gcc_plugin.md @@ -99,4 +99,11 @@ See ## 6) Bonus feature #3: selective instrumentation It can be more effective to fuzzing to only instrument parts of the code. For -details, see [README.instrument_list.md](README.instrument_list.md). \ No newline at end of file +details, see [README.instrument_list.md](README.instrument_list.md). + +## 7) Bonus feature #4: CMPLOG + +The gcc_plugin also support CMPLOG/Redqueen, just set `AFL_GCC_CMPLOG` before +instrumenting the target. +Read more about this in the llvm document. + diff --git a/instrumentation/SanitizerCoverageLTO.so.cc b/instrumentation/SanitizerCoverageLTO.so.cc index 9a48ae6d..f976f48a 100644 --- a/instrumentation/SanitizerCoverageLTO.so.cc +++ b/instrumentation/SanitizerCoverageLTO.so.cc @@ -921,7 +921,7 @@ bool ModuleSanitizerCoverageLTO::instrumentModule( std::string outstring; fprintf(stderr, "%s: length %zu/%zu \"", FuncName.c_str(), optLen, thestring.length()); - for (uint8_t i = 0; i < thestring.length(); i++) { + for (uint16_t i = 0; i < (uint16_t)thestring.length(); i++) { uint8_t c = thestring[i]; if (c <= 32 || c >= 127) diff --git a/instrumentation/afl-gcc-cmplog-pass.so.cc b/instrumentation/afl-gcc-cmplog-pass.so.cc new file mode 100644 index 00000000..c2910498 --- /dev/null +++ b/instrumentation/afl-gcc-cmplog-pass.so.cc @@ -0,0 +1,404 @@ +/* GCC plugin for cmplog instrumentation of code for AFL++. + + 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 + + Written by Alexandre Oliva <oliva@adacore.com>, based on the AFL++ + LLVM CmpLog pass by Andrea Fioraldi <andreafioraldi@gmail.com>, and + on the AFL GCC pass. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + + */ + +#include "afl-gcc-common.h" + +/* This plugin, being under the same license as GCC, satisfies the + "GPL-compatible Software" definition in the GCC RUNTIME LIBRARY + EXCEPTION, so it can be part of an "Eligible" "Compilation + Process". */ +int plugin_is_GPL_compatible = 1; + +namespace { + +static const struct pass_data afl_cmplog_pass_data = { + + .type = GIMPLE_PASS, + .name = "aflcmplog", + .optinfo_flags = OPTGROUP_NONE, + .tv_id = TV_NONE, + .properties_required = 0, + .properties_provided = 0, + .properties_destroyed = 0, + .todo_flags_start = 0, + .todo_flags_finish = (TODO_update_ssa | TODO_cleanup_cfg | TODO_verify_il | + TODO_rebuild_cgraph_edges), + +}; + +struct afl_cmplog_pass : afl_base_pass { + + afl_cmplog_pass(bool quiet) + : afl_base_pass(quiet, /*debug=*/false, afl_cmplog_pass_data), + t8u(), + cmplog_hooks() { + + } + + /* An unsigned 8-bit integral type. */ + tree t8u; + + /* Declarations for the various cmplog hook functions, allocated on demand.. + [0] is for __cmplog_ins_hookN, that accepts non-power-of-2 sizes. + [n in 1..5] are for unsigned ints of 2^{n-1} bytes. */ + tree cmplog_hooks[6]; + + tree cmplog_hook(unsigned i) { + + tree t, fnt; + + if (!t8u) { + + if (BITS_PER_UNIT == 8) + t8u = unsigned_char_type_node; + else + t8u = build_nonstandard_integer_type(8, 1); + + } + + if (i <= ARRAY_SIZE(cmplog_hooks) && cmplog_hooks[i]) + return cmplog_hooks[i]; + + switch (i) { + + case 0: +#ifdef uint128_type_node + t = uint128_type_node; +#else + t = build_nonstandard_integer_type(128, 1); +#endif + fnt = + build_function_type_list(void_type_node, t, t, t8u, t8u, NULL_TREE); + t = cmplog_hooks[0] = build_fn_decl("__cmplog_ins_hookN", fnt); + break; + + case 1: + t = t8u; + fnt = build_function_type_list(void_type_node, t, t, t8u, NULL_TREE); + t = cmplog_hooks[1] = build_fn_decl("__cmplog_ins_hook1", fnt); + break; + + case 2: + t = uint16_type_node; + fnt = build_function_type_list(void_type_node, t, t, t8u, NULL_TREE); + t = cmplog_hooks[2] = build_fn_decl("__cmplog_ins_hook2", fnt); + break; + + case 3: + t = uint32_type_node; + fnt = build_function_type_list(void_type_node, t, t, t8u, NULL_TREE); + t = cmplog_hooks[3] = build_fn_decl("__cmplog_ins_hook4", fnt); + break; + + case 4: + t = uint64_type_node; + fnt = build_function_type_list(void_type_node, t, t, t8u, NULL_TREE); + t = cmplog_hooks[4] = build_fn_decl("__cmplog_ins_hook8", fnt); + break; + + case 5: +#ifdef uint128_type_node + t = uint128_type_node; +#else + t = build_nonstandard_integer_type(128, 1); +#endif + fnt = build_function_type_list(void_type_node, t, t, t8u, NULL_TREE); + t = cmplog_hooks[5] = build_fn_decl("__cmplog_ins_hook16", fnt); + break; + + default: + gcc_unreachable(); + + } + + /* Mark the newly-created decl as non-throwing, so that we can + insert call within basic blocks. */ + TREE_NOTHROW(t) = 1; + + return t; + + } + + /* Insert a cmplog hook call before GSI for a CODE compare between + LHS and RHS. */ + void insert_cmplog_call(gimple_stmt_iterator gsi, tree_code code, tree lhs, + tree rhs) { + + gcc_checking_assert(TYPE_MAIN_VARIANT(TREE_TYPE(lhs)) == + TYPE_MAIN_VARIANT(TREE_TYPE(rhs))); + + tree fn; + bool pass_n = false; + + /* Obtain the compare operand size as a constant. */ + tree st = TREE_TYPE(lhs); + tree szt = TYPE_SIZE(st); + + if (!tree_fits_uhwi_p(szt)) return; + + unsigned HOST_WIDE_INT sz = tree_to_uhwi(szt); + + /* Round it up. */ + if (sz % 8) sz = (((sz - 1) / 8) + 1) * 8; + + /* Select the hook function to call, based on the size. */ + switch (sz) { + + default: + fn = cmplog_hook(0); + pass_n = true; + break; + + case 8: + fn = cmplog_hook(1); + break; + + case 16: + fn = cmplog_hook(2); + break; + + case 32: + fn = cmplog_hook(3); + break; + + case 64: + fn = cmplog_hook(4); + break; + + case 128: + fn = cmplog_hook(5); + break; + + } + + /* Set attr according to the compare operation. */ + unsigned char attr = 0; + + switch (code) { + + case UNORDERED_EXPR: + case ORDERED_EXPR: + /* ??? */ + /* Fallthrough. */ + case NE_EXPR: + case LTGT_EXPR: + break; + + case EQ_EXPR: + case UNEQ_EXPR: + attr += 1; + break; + + case GT_EXPR: + case UNGT_EXPR: + attr += 2; + break; + + case GE_EXPR: + case UNGE_EXPR: + attr += 3; + break; + + case LT_EXPR: + case UNLT_EXPR: + attr += 4; + break; + + case LE_EXPR: + case UNLE_EXPR: + attr += 5; + break; + + default: + gcc_unreachable(); + + } + + if (FLOAT_TYPE_P(TREE_TYPE(lhs))) { + + attr += 8; + + 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)); + lhs = s; + gsi_insert_before(&gsi, g, GSI_SAME_STMT); + + s = make_ssa_name(t); + g = gimple_build_assign(s, VIEW_CONVERT_EXPR, + build1(VIEW_CONVERT_EXPR, t, rhs)); + rhs = s; + gsi_insert_before(&gsi, g, GSI_SAME_STMT); + + } + + /* Convert the operands to the hook arg type, if needed. */ + tree t = TREE_VALUE(TYPE_ARG_TYPES(TREE_TYPE(fn))); + + 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); + lhs = s; + gsi_insert_before(&gsi, g, GSI_SAME_STMT); + + } + + 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); + rhs = s; + gsi_insert_before(&gsi, g, GSI_SAME_STMT); + + } + + /* Insert the 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)); + else + call = gimple_build_call(fn, 3, lhs, rhs, att); + + gsi_insert_before(&gsi, call, GSI_SAME_STMT); + + } + + virtual unsigned int execute(function *fn) { + + if (!isInInstrumentList(fn)) return 0; + + basic_block bb; + FOR_EACH_BB_FN(bb, fn) { + + /* A GIMPLE_COND or GIMPLE_SWITCH will always be the last stmt + in a BB. */ + gimple_stmt_iterator gsi = gsi_last_bb(bb); + if (gsi_end_p(gsi)) continue; + + gimple *stmt = gsi_stmt(gsi); + + if (gimple_code(stmt) == GIMPLE_COND) { + + tree_code code = gimple_cond_code(stmt); + tree lhs = gimple_cond_lhs(stmt); + tree rhs = gimple_cond_rhs(stmt); + + insert_cmplog_call(gsi, code, lhs, rhs); + + } else if (gimple_code(stmt) == GIMPLE_SWITCH) { + + gswitch *sw = as_a<gswitch *>(stmt); + tree lhs = gimple_switch_index(sw); + + for (int i = 0, e = gimple_switch_num_labels(sw); i < e; i++) { + + tree clx = gimple_switch_label(sw, i); + tree rhsl = CASE_LOW(clx); + /* Default case labels exprs don't have a CASE_LOW. */ + if (!rhsl) continue; + tree rhsh = CASE_HIGH(clx); + /* If there is a CASE_HIGH, issue range compares. */ + if (rhsh) { + + insert_cmplog_call(gsi, GE_EXPR, lhs, rhsl); + insert_cmplog_call(gsi, LE_EXPR, lhs, rhsh); + + } + + /* Otherwise, use a single equality compare. */ + else + insert_cmplog_call(gsi, EQ_EXPR, lhs, rhsl); + + } + + } else + + continue; + + } + + return 0; + + } + +}; + +static struct plugin_info afl_cmplog_plugin = { + + .version = "20220420", + .help = G_("AFL gcc cmplog plugin\n\ +\n\ +Set AFL_QUIET in the environment to silence it.\n\ +"), + +}; + +} // namespace + +/* This is the function GCC calls when loading a plugin. Initialize + and register further callbacks. */ +int plugin_init(struct plugin_name_args * info, + struct plugin_gcc_version *version) { + + if (!plugin_default_version_check(version, &gcc_version)) + FATAL(G_("GCC and plugin have incompatible versions, expected GCC %s, " + "is %s"), + gcc_version.basever, version->basever); + + /* Show a banner. */ + bool quiet = false; + if (isatty(2) && !getenv("AFL_QUIET")) + SAYF(cCYA "afl-gcc-cmplog-pass " cBRI VERSION cRST + " by <oliva@adacore.com>\n"); + else + quiet = true; + + const char *name = info->base_name; + register_callback(name, PLUGIN_INFO, NULL, &afl_cmplog_plugin); + + afl_cmplog_pass * aflp = new afl_cmplog_pass(quiet); + struct register_pass_info pass_info = { + + .pass = aflp, + .reference_pass_name = "ssa", + .ref_pass_instance_number = 1, + .pos_op = PASS_POS_INSERT_AFTER, + + }; + + register_callback(name, PLUGIN_PASS_MANAGER_SETUP, NULL, &pass_info); + + return 0; + +} + diff --git a/instrumentation/afl-gcc-cmptrs-pass.so.cc b/instrumentation/afl-gcc-cmptrs-pass.so.cc new file mode 100644 index 00000000..31679b9b --- /dev/null +++ b/instrumentation/afl-gcc-cmptrs-pass.so.cc @@ -0,0 +1,366 @@ +/* GCC plugin for cmplog routines instrumentation of code for AFL++. + + 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 + + Written by Alexandre Oliva <oliva@adacore.com>, based on the AFL++ + LLVM CmpLog Routines pass by Andrea Fioraldi + <andreafioraldi@gmail.com>, and on the AFL GCC CmpLog pass. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + + */ + +#include "afl-gcc-common.h" + +/* This plugin, being under the same license as GCC, satisfies the + "GPL-compatible Software" definition in the GCC RUNTIME LIBRARY + EXCEPTION, so it can be part of an "Eligible" "Compilation + Process". */ +int plugin_is_GPL_compatible = 1; + +namespace { + +static const struct pass_data afl_cmptrs_pass_data = { + + .type = GIMPLE_PASS, + .name = "aflcmptrs", + .optinfo_flags = OPTGROUP_NONE, + .tv_id = TV_NONE, + .properties_required = 0, + .properties_provided = 0, + .properties_destroyed = 0, + .todo_flags_start = 0, + .todo_flags_finish = (TODO_update_ssa | TODO_cleanup_cfg | TODO_verify_il | + TODO_rebuild_cgraph_edges), + +}; + +struct afl_cmptrs_pass : afl_base_pass { + + afl_cmptrs_pass(bool quiet) + : afl_base_pass(quiet, /*debug=*/false, afl_cmptrs_pass_data), + tp8u(), + cmptrs_hooks() { + + } + + /* A pointer type to a unsigned 8-bit integral type. */ + tree tp8u; + + /* Declarations for the various cmptrs hook functions, allocated on + demand.. [0] is for compares between any pointers, [1] is for + compares between G++ std::string, [2] is for compares between G++ + std::string and GCC C strings, [3] and [4] are analogous to [1] + and [2] but for LLVM C++ strings. */ + tree cmptrs_hooks[5]; + + tree cmptrs_hook(unsigned i) { + + if (!tp8u) { + + tree t8u; + if (BITS_PER_UNIT == 8) + t8u = unsigned_char_type_node; + else + t8u = build_nonstandard_integer_type(8, 1); + tp8u = build_pointer_type(t8u); + + } + + if (i <= ARRAY_SIZE(cmptrs_hooks) && cmptrs_hooks[i]) + return cmptrs_hooks[i]; + + const char *n = NULL; + + switch (i) { + + case 0: + n = "__cmplog_rtn_hook"; + break; + + case 1: + n = "__cmplog_rtn_gcc_stdstring_stdstring"; + break; + + case 2: + n = "__cmplog_rtn_gcc_stdstring_cstring"; + break; + + case 3: + n = "__cmplog_rtn_llvm_stdstring_stdstring"; + break; + + case 4: + n = "__cmplog_rtn_llvm_stdstring_cstring"; + break; + + default: + gcc_unreachable(); + + } + + tree fnt = build_function_type_list(void_type_node, tp8u, tp8u, NULL_TREE); + tree t = cmptrs_hooks[i] = build_fn_decl(n, fnt); + + /* Mark the newly-created decl as non-throwing, so that we can + insert call within basic blocks. */ + TREE_NOTHROW(t) = 1; + + return t; + + } + + /* Return true if T is the char* type. */ + bool is_c_string(tree t) { + + return (POINTER_TYPE_P(t) && + TYPE_MAIN_VARIANT(TREE_TYPE(t)) == char_type_node); + + } + + /* Return true if T is an indirect std::string type. The LLVM pass + tests portions of the mangled name of the callee. We could do + that in GCC too, but computing the mangled name may cause + template instantiations and get symbols defined that could + otherwise be considered unused. We check for compatible layout, + and class, namespace, and field names. These have been unchanged + since at least GCC 7, probably longer, up to GCC 11. Odds are + that, if it were to change in significant ways, mangling would + also change to flag the incompatibility, and we'd have to use a + different hook anyway. */ + bool is_gxx_std_string(tree t) { + + /* We need a pointer or reference type. */ + if (!POINTER_TYPE_P(t)) return false; + + /* Get to the pointed-to type. */ + t = TREE_TYPE(t); + if (!t) return false; + + /* Select the main variant, so that can compare types with pointers. */ + t = TYPE_MAIN_VARIANT(t); + + /* We expect it to be a record type. */ + if (TREE_CODE(t) != RECORD_TYPE) return false; + + /* The type of the template is basic_string. */ + if (strcmp(IDENTIFIER_POINTER(TYPE_IDENTIFIER(t)), "basic_string") != 0) + return false; + + /* It's declared in an internal namespace named __cxx11. */ + tree c = DECL_CONTEXT(TYPE_NAME(t)); + if (!c || TREE_CODE(c) != NAMESPACE_DECL || + strcmp(IDENTIFIER_POINTER(DECL_NAME(c)), "__cxx11") != 0) + return false; + + /* The __cxx11 namespace is a member of namespace std. */ + c = DECL_CONTEXT(c); + if (!c || TREE_CODE(c) != NAMESPACE_DECL || + strcmp(IDENTIFIER_POINTER(DECL_NAME(c)), "std") != 0) + return false; + + /* And the std namespace is in the global namespace. */ + c = DECL_CONTEXT(c); + if (c && TREE_CODE(c) != TRANSLATION_UNIT_DECL) return false; + + /* Check that the first nonstatic data member of the record type + is named _M_dataplus. */ + for (c = TYPE_FIELDS(t); c; c = DECL_CHAIN(c)) + if (TREE_CODE(c) == FIELD_DECL) break; + if (!c || !integer_zerop(DECL_FIELD_BIT_OFFSET(c)) || + strcmp(IDENTIFIER_POINTER(DECL_NAME(c)), "_M_dataplus") != 0) + return false; + + /* Check that the second nonstatic data member of the record type + is named _M_string_length. */ + tree f2; + for (f2 = DECL_CHAIN(c); f2; f2 = DECL_CHAIN(f2)) + if (TREE_CODE(f2) == FIELD_DECL) break; + if (!f2 /* No need to check this field's offset. */ + || strcmp(IDENTIFIER_POINTER(DECL_NAME(f2)), "_M_string_length") != 0) + return false; + + /* The type of the second data member is size_t. */ + if (!TREE_TYPE(f2) || TYPE_MAIN_VARIANT(TREE_TYPE(f2)) != size_type_node) + return false; + + /* Now go back to the first data member. Its type should be a + record type named _Alloc_hider. */ + c = TREE_TYPE(c); + if (!c || TREE_CODE(c) != RECORD_TYPE || + strcmp(IDENTIFIER_POINTER(TYPE_IDENTIFIER(c)), "_Alloc_hider") != 0) + return false; + + /* And its first data member is named _M_p. */ + for (c = TYPE_FIELDS(c); c; c = DECL_CHAIN(c)) + if (TREE_CODE(c) == FIELD_DECL) break; + if (!c || !integer_zerop(DECL_FIELD_BIT_OFFSET(c)) || + strcmp(IDENTIFIER_POINTER(DECL_NAME(c)), "_M_p") != 0) + return false; + + /* For the basic_string<char> type we're interested in, the type + of the data member is the C string type. */ + if (!is_c_string(TREE_TYPE(c))) return false; + + /* This might not be the real thing, but the bits that matter for + the hook are there. */ + + return true; + + } + + /* ??? This is not implemented. What would the point be of + recognizing LLVM's string type in GCC? */ + bool is_llvm_std_string(tree t) { + + return false; + + } + + virtual unsigned int execute(function *fn) { + + if (!isInInstrumentList(fn)) return 0; + + basic_block bb; + FOR_EACH_BB_FN(bb, fn) { + + for (gimple_stmt_iterator gsi = gsi_after_labels(bb); !gsi_end_p(gsi); + gsi_next(&gsi)) { + + gimple *stmt = gsi_stmt(gsi); + + /* We're only interested in GIMPLE_CALLs. */ + if (gimple_code(stmt) != GIMPLE_CALL) continue; + + if (gimple_call_num_args(stmt) < 2) continue; + + gcall *c = as_a<gcall *>(stmt); + + tree callee_type = gimple_call_fntype(c); + + if (!callee_type || !TYPE_ARG_TYPES(callee_type) || + !TREE_CHAIN(TYPE_ARG_TYPES(callee_type))) + continue; + + tree arg_type[2] = { + + TYPE_MAIN_VARIANT(TREE_VALUE(TYPE_ARG_TYPES(callee_type))), + TYPE_MAIN_VARIANT( + TREE_VALUE(TREE_CHAIN(TYPE_ARG_TYPES(callee_type))))}; + + tree fn = NULL; + /* Callee arglist starts with two GCC std::string arguments. */ + if (arg_type[0] == arg_type[1] && is_gxx_std_string(arg_type[0])) + fn = cmptrs_hook(1); + /* Callee arglist starts with GCC std::string and C string. */ + else if (is_gxx_std_string(arg_type[0]) && is_c_string(arg_type[1])) + fn = cmptrs_hook(2); + /* Callee arglist starts with two LLVM std::string arguments. */ + else if (arg_type[0] == arg_type[1] && is_llvm_std_string(arg_type[0])) + fn = cmptrs_hook(3); + /* Callee arglist starts with LLVM std::string and C string. */ + else if (is_llvm_std_string(arg_type[0]) && is_c_string(arg_type[1])) + fn = cmptrs_hook(4); + /* Callee arglist starts with two pointers to the same type, + and callee returns a value. */ + else if (arg_type[0] == arg_type[1] && POINTER_TYPE_P(arg_type[0]) && + (TYPE_MAIN_VARIANT(gimple_call_return_type(c)) != + void_type_node)) + fn = cmptrs_hook(0); + else + continue; + + tree arg[2] = {gimple_call_arg(c, 0), gimple_call_arg(c, 1)}; + + for (unsigned i = 0; i < ARRAY_SIZE(arg); i++) { + + 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); + c = s; + gsi_insert_before(&gsi, g, GSI_SAME_STMT); + + } + + arg[i] = c; + + } + + gimple *call = gimple_build_call(fn, 2, arg[0], arg[1]); + gsi_insert_before(&gsi, call, GSI_SAME_STMT); + + } + + } + + return 0; + + } + +}; + +static struct plugin_info afl_cmptrs_plugin = { + + .version = "20220420", + .help = G_("AFL gcc cmptrs plugin\n\ +\n\ +Set AFL_QUIET in the environment to silence it.\n\ +"), + +}; + +} // namespace + +/* This is the function GCC calls when loading a plugin. Initialize + and register further callbacks. */ +int plugin_init(struct plugin_name_args * info, + struct plugin_gcc_version *version) { + + if (!plugin_default_version_check(version, &gcc_version)) + FATAL(G_("GCC and plugin have incompatible versions, expected GCC %s, " + "is %s"), + gcc_version.basever, version->basever); + + /* Show a banner. */ + bool quiet = false; + if (isatty(2) && !getenv("AFL_QUIET")) + SAYF(cCYA "afl-gcc-cmptrs-pass " cBRI VERSION cRST + " by <oliva@adacore.com>\n"); + else + quiet = true; + + const char *name = info->base_name; + register_callback(name, PLUGIN_INFO, NULL, &afl_cmptrs_plugin); + + afl_cmptrs_pass * aflp = new afl_cmptrs_pass(quiet); + struct register_pass_info pass_info = { + + .pass = aflp, + .reference_pass_name = "ssa", + .ref_pass_instance_number = 1, + .pos_op = PASS_POS_INSERT_AFTER, + + }; + + register_callback(name, PLUGIN_PASS_MANAGER_SETUP, NULL, &pass_info); + + return 0; + +} + diff --git a/instrumentation/afl-gcc-common.h b/instrumentation/afl-gcc-common.h new file mode 100644 index 00000000..806e7ac3 --- /dev/null +++ b/instrumentation/afl-gcc-common.h @@ -0,0 +1,498 @@ +/* GCC plugin common infrastructure for AFL++ instrumentation passes. + + Copyright 2014-2019 Free Software Foundation, Inc + Copyright 2015, 2016 Google Inc. All rights reserved. + Copyright 2019-2022 AdaCore + + Written by Alexandre Oliva <oliva@adacore.com>, based on the AFL++ + GCC plugin. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + + */ + +#include "../include/config.h" +#include "../include/debug.h" + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> + +#ifdef likely + #undef likely +#endif +#ifdef unlikely + #undef unlikely +#endif + +#include <list> +#include <string> +#include <fstream> + +#include <algorithm> +#include <fnmatch.h> + +#include <gcc-plugin.h> +#include <plugin-version.h> +#include <toplev.h> +#include <tree-pass.h> +#include <context.h> +#include <tree.h> +#include <gimplify.h> +#include <basic-block.h> +#include <tree-ssa-alias.h> +#include <gimple-expr.h> +#include <gimple.h> +#include <gimple-iterator.h> +#include <stringpool.h> +#include <gimple-ssa.h> +#if (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) >= \ + 60200 /* >= version 6.2.0 */ + #include <tree-vrp.h> +#endif +#include <tree-ssanames.h> +#include <tree-phinodes.h> +#include <ssa-iterators.h> + +#include <intl.h> + +namespace { + +struct afl_base_pass : gimple_opt_pass { + + afl_base_pass(bool quiet, bool debug, struct pass_data const &pd) + : gimple_opt_pass(pd, g), be_quiet(quiet), debug(debug) { + + initInstrumentList(); + + } + + /* Are we outputting to a non-terminal, or running with AFL_QUIET + set? */ + const bool be_quiet; + + /* Are we running with AFL_DEBUG set? */ + const bool debug; + +#define report_fatal_error(msg) BADF(msg) + + std::list<std::string> allowListFiles; + std::list<std::string> allowListFunctions; + std::list<std::string> denyListFiles; + std::list<std::string> denyListFunctions; + + /* Note: this ignore check is also called in isInInstrumentList() */ + bool isIgnoreFunction(function *F) { + + // Starting from "LLVMFuzzer" these are functions used in libfuzzer based + // fuzzing campaign installations, e.g. oss-fuzz + + static constexpr const char *ignoreList[] = { + + "asan.", + "llvm.", + "sancov.", + "__ubsan_", + "ign.", + "__afl_", + "_fini", + "__libc_csu", + "__asan", + "__msan", + "__cmplog", + "__sancov", + "msan.", + "LLVMFuzzerM", + "LLVMFuzzerC", + "LLVMFuzzerI", + "__decide_deferred", + "maybe_duplicate_stderr", + "discard_output", + "close_stdout", + "dup_and_close_stderr", + "maybe_close_fd_mask", + "ExecuteFilesOnyByOne" + + }; + + const char *name = IDENTIFIER_POINTER(DECL_NAME(F->decl)); + int len = IDENTIFIER_LENGTH(DECL_NAME(F->decl)); + + for (auto const &ignoreListFunc : ignoreList) { + + if (strncmp(name, ignoreListFunc, len) == 0) { return true; } + + } + + return false; + + } + + void initInstrumentList() { + + char *allowlist = getenv("AFL_GCC_ALLOWLIST"); + if (!allowlist) allowlist = getenv("AFL_GCC_INSTRUMENT_FILE"); + if (!allowlist) allowlist = getenv("AFL_GCC_WHITELIST"); + if (!allowlist) allowlist = getenv("AFL_LLVM_ALLOWLIST"); + if (!allowlist) allowlist = getenv("AFL_LLVM_INSTRUMENT_FILE"); + if (!allowlist) allowlist = getenv("AFL_LLVM_WHITELIST"); + char *denylist = getenv("AFL_GCC_DENYLIST"); + if (!denylist) denylist = getenv("AFL_GCC_BLOCKLIST"); + if (!denylist) denylist = getenv("AFL_LLVM_DENYLIST"); + if (!denylist) denylist = getenv("AFL_LLVM_BLOCKLIST"); + + if (allowlist && denylist) + FATAL( + "You can only specify either AFL_GCC_ALLOWLIST or AFL_GCC_DENYLIST " + "but not both!"); + + if (allowlist) { + + std::string line; + std::ifstream fileStream; + fileStream.open(allowlist); + if (!fileStream) report_fatal_error("Unable to open AFL_GCC_ALLOWLIST"); + getline(fileStream, line); + + while (fileStream) { + + int is_file = -1; + std::size_t npos; + std::string original_line = line; + + line.erase(std::remove_if(line.begin(), line.end(), ::isspace), + line.end()); + + // remove # and following + if ((npos = line.find("#")) != std::string::npos) + line = line.substr(0, npos); + + if (line.compare(0, 4, "fun:") == 0) { + + is_file = 0; + line = line.substr(4); + + } else if (line.compare(0, 9, "function:") == 0) { + + is_file = 0; + line = line.substr(9); + + } else if (line.compare(0, 4, "src:") == 0) { + + is_file = 1; + line = line.substr(4); + + } else if (line.compare(0, 7, "source:") == 0) { + + is_file = 1; + line = line.substr(7); + + } + + if (line.find(":") != std::string::npos) { + + FATAL("invalid line in AFL_GCC_ALLOWLIST: %s", original_line.c_str()); + + } + + if (line.length() > 0) { + + // if the entry contains / or . it must be a file + if (is_file == -1) + if (line.find("/") != std::string::npos || + line.find(".") != std::string::npos) + is_file = 1; + // otherwise it is a function + + if (is_file == 1) + allowListFiles.push_back(line); + else + allowListFunctions.push_back(line); + + } + + getline(fileStream, line); + + } + + if (debug) + DEBUGF("loaded allowlist with %zu file and %zu function entries\n", + allowListFiles.size(), allowListFunctions.size()); + + } + + if (denylist) { + + std::string line; + std::ifstream fileStream; + fileStream.open(denylist); + if (!fileStream) report_fatal_error("Unable to open AFL_GCC_DENYLIST"); + getline(fileStream, line); + + while (fileStream) { + + int is_file = -1; + std::size_t npos; + std::string original_line = line; + + line.erase(std::remove_if(line.begin(), line.end(), ::isspace), + line.end()); + + // remove # and following + if ((npos = line.find("#")) != std::string::npos) + line = line.substr(0, npos); + + if (line.compare(0, 4, "fun:") == 0) { + + is_file = 0; + line = line.substr(4); + + } else if (line.compare(0, 9, "function:") == 0) { + + is_file = 0; + line = line.substr(9); + + } else if (line.compare(0, 4, "src:") == 0) { + + is_file = 1; + line = line.substr(4); + + } else if (line.compare(0, 7, "source:") == 0) { + + is_file = 1; + line = line.substr(7); + + } + + if (line.find(":") != std::string::npos) { + + FATAL("invalid line in AFL_GCC_DENYLIST: %s", original_line.c_str()); + + } + + if (line.length() > 0) { + + // if the entry contains / or . it must be a file + if (is_file == -1) + if (line.find("/") != std::string::npos || + line.find(".") != std::string::npos) + is_file = 1; + // otherwise it is a function + + if (is_file == 1) + denyListFiles.push_back(line); + else + denyListFunctions.push_back(line); + + } + + getline(fileStream, line); + + } + + if (debug) + DEBUGF("loaded denylist with %zu file and %zu function entries\n", + denyListFiles.size(), denyListFunctions.size()); + + } + + } + + /* Returns the source file name attached to the function declaration F. If + there is no source location information, returns an empty string. */ + std::string getSourceName(function *F) { + + return DECL_SOURCE_FILE(F->decl) ? DECL_SOURCE_FILE(F->decl) : ""; + + } + + bool isInInstrumentList(function *F) { + + bool return_default = true; + + // is this a function with code? If it is external we don't instrument it + // anyway and it can't be in the instrument file list. Or if it is it is + // ignored. + if (isIgnoreFunction(F)) return false; + + if (!denyListFiles.empty() || !denyListFunctions.empty()) { + + if (!denyListFunctions.empty()) { + + std::string instFunction = IDENTIFIER_POINTER(DECL_NAME(F->decl)); + + for (std::list<std::string>::iterator it = denyListFunctions.begin(); + it != denyListFunctions.end(); ++it) { + + /* We don't check for filename equality here because + * filenames might actually be full paths. Instead we + * check that the actual filename ends in the filename + * specified in the list. We also allow UNIX-style pattern + * matching */ + + if (instFunction.length() >= it->length()) { + + if (fnmatch(("*" + *it).c_str(), instFunction.c_str(), 0) == 0) { + + if (debug) + DEBUGF( + "Function %s is in the deny function list, not " + "instrumenting ... \n", + instFunction.c_str()); + return false; + + } + + } + + } + + } + + if (!denyListFiles.empty()) { + + std::string source_file = getSourceName(F); + + if (!source_file.empty()) { + + for (std::list<std::string>::iterator it = denyListFiles.begin(); + it != denyListFiles.end(); ++it) { + + /* We don't check for filename equality here because + * filenames might actually be full paths. Instead we + * check that the actual filename ends in the filename + * specified in the list. We also allow UNIX-style pattern + * matching */ + + if (source_file.length() >= it->length()) { + + if (fnmatch(("*" + *it).c_str(), source_file.c_str(), 0) == 0) { + + return false; + + } + + } + + } + + } else { + + // we could not find out the location. in this case we say it is not + // in the instrument file list + if (!be_quiet) + WARNF( + "No debug information found for function %s, will be " + "instrumented (recompile with -g -O[1-3]).", + IDENTIFIER_POINTER(DECL_NAME(F->decl))); + + } + + } + + } + + // if we do not have a instrument file list return true + if (!allowListFiles.empty() || !allowListFunctions.empty()) { + + return_default = false; + + if (!allowListFunctions.empty()) { + + std::string instFunction = IDENTIFIER_POINTER(DECL_NAME(F->decl)); + + for (std::list<std::string>::iterator it = allowListFunctions.begin(); + it != allowListFunctions.end(); ++it) { + + /* We don't check for filename equality here because + * filenames might actually be full paths. Instead we + * check that the actual filename ends in the filename + * specified in the list. We also allow UNIX-style pattern + * matching */ + + if (instFunction.length() >= it->length()) { + + if (fnmatch(("*" + *it).c_str(), instFunction.c_str(), 0) == 0) { + + if (debug) + DEBUGF( + "Function %s is in the allow function list, instrumenting " + "... \n", + instFunction.c_str()); + return true; + + } + + } + + } + + } + + if (!allowListFiles.empty()) { + + std::string source_file = getSourceName(F); + + if (!source_file.empty()) { + + for (std::list<std::string>::iterator it = allowListFiles.begin(); + it != allowListFiles.end(); ++it) { + + /* We don't check for filename equality here because + * filenames might actually be full paths. Instead we + * check that the actual filename ends in the filename + * specified in the list. We also allow UNIX-style pattern + * matching */ + + if (source_file.length() >= it->length()) { + + if (fnmatch(("*" + *it).c_str(), source_file.c_str(), 0) == 0) { + + if (debug) + DEBUGF( + "Function %s is in the allowlist (%s), instrumenting ... " + "\n", + IDENTIFIER_POINTER(DECL_NAME(F->decl)), + source_file.c_str()); + return true; + + } + + } + + } + + } else { + + // we could not find out the location. In this case we say it is not + // in the instrument file list + if (!be_quiet) + WARNF( + "No debug information found for function %s, will not be " + "instrumented (recompile with -g -O[1-3]).", + IDENTIFIER_POINTER(DECL_NAME(F->decl))); + return false; + + } + + } + + } + + return return_default; + + } +}; + +} diff --git a/instrumentation/afl-gcc-pass.so.cc b/instrumentation/afl-gcc-pass.so.cc index bb5483fc..795bbd8a 100644 --- a/instrumentation/afl-gcc-pass.so.cc +++ b/instrumentation/afl-gcc-pass.so.cc @@ -124,50 +124,8 @@ entry edge for the entry block. */ -#include "../include/config.h" -#include "../include/debug.h" - -#include <stdio.h> -#include <stdlib.h> -#include <unistd.h> - -#ifdef likely - #undef likely -#endif -#ifdef unlikely - #undef unlikely -#endif - -#include <list> -#include <string> -#include <fstream> - -#include <algorithm> -#include <fnmatch.h> - -#include <gcc-plugin.h> -#include <plugin-version.h> -#include <toplev.h> -#include <tree-pass.h> -#include <context.h> -#include <tree.h> -#include <gimplify.h> -#include <basic-block.h> -#include <tree-ssa-alias.h> -#include <gimple-expr.h> -#include <gimple.h> -#include <gimple-iterator.h> -#include <stringpool.h> -#include <gimple-ssa.h> -#if (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) >= \ - 60200 /* >= version 6.2.0 */ - #include <tree-vrp.h> -#endif -#include <tree-ssanames.h> -#include <tree-phinodes.h> -#include <ssa-iterators.h> - -#include <intl.h> +#include "afl-gcc-common.h" +#include "memmodel.h" /* This plugin, being under the same license as GCC, satisfies the "GPL-compatible Software" definition in the GCC RUNTIME LIBRARY @@ -191,12 +149,10 @@ static constexpr struct pass_data afl_pass_data = { }; -struct afl_pass : gimple_opt_pass { +struct afl_pass : afl_base_pass { afl_pass(bool quiet, unsigned int ratio) - : gimple_opt_pass(afl_pass_data, g), - be_quiet(quiet), - debug(!!getenv("AFL_DEBUG")), + : afl_base_pass(quiet, !!getenv("AFL_DEBUG"), afl_pass_data), inst_ratio(ratio), #ifdef AFL_GCC_OUT_OF_LINE out_of_line(!!(AFL_GCC_OUT_OF_LINE)), @@ -210,13 +166,6 @@ struct afl_pass : gimple_opt_pass { } - /* Are we outputting to a non-terminal, or running with AFL_QUIET - set? */ - const bool be_quiet; - - /* Are we running with AFL_DEBUG set? */ - const bool debug; - /* How likely (%) is a block to be instrumented? */ const unsigned int inst_ratio; @@ -297,21 +246,22 @@ struct afl_pass : gimple_opt_pass { gimple_build_assign(ntry, POINTER_PLUS_EXPR, map_ptr, indx); gimple_seq_add_stmt(&seq, idx_map); - /* Increment the counter in idx_map. */ - tree memref = build2(MEM_REF, TREE_TYPE(TREE_TYPE(ntry)), ntry, - build_zero_cst(TREE_TYPE(ntry))); - if (blocks == 0) - cntr = create_tmp_var(TREE_TYPE(memref), ".afl_edge_count"); - - /* Load the count from the entry. */ - auto load_cntr = gimple_build_assign(cntr, memref); - gimple_seq_add_stmt(&seq, load_cntr); - /* Prepare to add constant 1 to it. */ - tree incrv = build_one_cst(TREE_TYPE(cntr)); + tree incrv = build_one_cst(TREE_TYPE(TREE_TYPE(ntry))); if (neverZero) { + /* Increment the counter in idx_map. */ + tree memref = build2(MEM_REF, TREE_TYPE(TREE_TYPE(ntry)), ntry, + build_zero_cst(TREE_TYPE(ntry))); + + if (blocks == 0) + cntr = create_tmp_var(TREE_TYPE(memref), ".afl_edge_count"); + + /* Load the count from the entry. */ + auto load_cntr = gimple_build_assign(cntr, memref); + gimple_seq_add_stmt(&seq, load_cntr); + /* NeverZero: if count wrapped around to zero, advance to one. */ if (blocks == 0) { @@ -348,15 +298,24 @@ struct afl_pass : gimple_opt_pass { in xincr. */ incrv = xincr; - } + /* Add the increment (1 or the overflow bit) to count. */ + auto incr_cntr = gimple_build_assign(cntr, PLUS_EXPR, cntr, incrv); + gimple_seq_add_stmt(&seq, incr_cntr); + + /* Store count in the map entry. */ + auto store_cntr = gimple_build_assign(unshare_expr(memref), cntr); + gimple_seq_add_stmt(&seq, store_cntr); - /* Add the increment (1 or the overflow bit) to count. */ - auto incr_cntr = gimple_build_assign(cntr, PLUS_EXPR, cntr, incrv); - gimple_seq_add_stmt(&seq, incr_cntr); + } else { - /* Store count in the map entry. */ - auto store_cntr = gimple_build_assign(unshare_expr(memref), cntr); - gimple_seq_add_stmt(&seq, store_cntr); + /* Use a serialized memory model. */ + tree memmod = build_int_cst(integer_type_node, MEMMODEL_SEQ_CST); + + tree fadd = builtin_decl_explicit(BUILT_IN_ATOMIC_FETCH_ADD_1); + auto incr_cntr = gimple_build_call(fadd, 3, ntry, incrv, memmod); + gimple_seq_add_stmt(&seq, incr_cntr); + + } /* Store bid >> 1 in __afl_prev_loc. */ auto shift_loc = @@ -456,6 +415,8 @@ struct afl_pass : gimple_opt_pass { thread-local variable. */ static inline tree get_afl_area_ptr_decl() { + /* If type changes, the size N in FETCH_ADD_<N> must be adjusted + in builtin calls above. */ tree type = build_pointer_type(unsigned_char_type_node); tree decl = build_decl(BUILTINS_LOCATION, VAR_DECL, get_identifier("__afl_area_ptr"), type); @@ -490,420 +451,11 @@ struct afl_pass : gimple_opt_pass { } -#define report_fatal_error(msg) BADF(msg) - - std::list<std::string> allowListFiles; - std::list<std::string> allowListFunctions; - std::list<std::string> denyListFiles; - std::list<std::string> denyListFunctions; - - /* Note: this ignore check is also called in isInInstrumentList() */ - bool isIgnoreFunction(function *F) { - - // Starting from "LLVMFuzzer" these are functions used in libfuzzer based - // fuzzing campaign installations, e.g. oss-fuzz - - static constexpr const char *ignoreList[] = { - - "asan.", - "llvm.", - "sancov.", - "__ubsan_", - "ign.", - "__afl_", - "_fini", - "__libc_csu", - "__asan", - "__msan", - "__cmplog", - "__sancov", - "msan.", - "LLVMFuzzerM", - "LLVMFuzzerC", - "LLVMFuzzerI", - "__decide_deferred", - "maybe_duplicate_stderr", - "discard_output", - "close_stdout", - "dup_and_close_stderr", - "maybe_close_fd_mask", - "ExecuteFilesOnyByOne" - - }; - - const char *name = IDENTIFIER_POINTER(DECL_NAME(F->decl)); - int len = IDENTIFIER_LENGTH(DECL_NAME(F->decl)); - - for (auto const &ignoreListFunc : ignoreList) { - - if (strncmp(name, ignoreListFunc, len) == 0) { return true; } - - } - - return false; - - } - - void initInstrumentList() { - - char *allowlist = getenv("AFL_GCC_ALLOWLIST"); - if (!allowlist) allowlist = getenv("AFL_GCC_INSTRUMENT_FILE"); - if (!allowlist) allowlist = getenv("AFL_GCC_WHITELIST"); - if (!allowlist) allowlist = getenv("AFL_LLVM_ALLOWLIST"); - if (!allowlist) allowlist = getenv("AFL_LLVM_INSTRUMENT_FILE"); - if (!allowlist) allowlist = getenv("AFL_LLVM_WHITELIST"); - char *denylist = getenv("AFL_GCC_DENYLIST"); - if (!denylist) denylist = getenv("AFL_GCC_BLOCKLIST"); - if (!denylist) denylist = getenv("AFL_LLVM_DENYLIST"); - if (!denylist) denylist = getenv("AFL_LLVM_BLOCKLIST"); - - if (allowlist && denylist) - FATAL( - "You can only specify either AFL_GCC_ALLOWLIST or AFL_GCC_DENYLIST " - "but not both!"); - - if (allowlist) { - - std::string line; - std::ifstream fileStream; - fileStream.open(allowlist); - if (!fileStream) report_fatal_error("Unable to open AFL_GCC_ALLOWLIST"); - getline(fileStream, line); - - while (fileStream) { - - int is_file = -1; - std::size_t npos; - std::string original_line = line; - - line.erase(std::remove_if(line.begin(), line.end(), ::isspace), - line.end()); - - // remove # and following - if ((npos = line.find("#")) != std::string::npos) - line = line.substr(0, npos); - - if (line.compare(0, 4, "fun:") == 0) { - - is_file = 0; - line = line.substr(4); - - } else if (line.compare(0, 9, "function:") == 0) { - - is_file = 0; - line = line.substr(9); - - } else if (line.compare(0, 4, "src:") == 0) { - - is_file = 1; - line = line.substr(4); - - } else if (line.compare(0, 7, "source:") == 0) { - - is_file = 1; - line = line.substr(7); - - } - - if (line.find(":") != std::string::npos) { - - FATAL("invalid line in AFL_GCC_ALLOWLIST: %s", original_line.c_str()); - - } - - if (line.length() > 0) { - - // if the entry contains / or . it must be a file - if (is_file == -1) - if (line.find("/") != std::string::npos || - line.find(".") != std::string::npos) - is_file = 1; - // otherwise it is a function - - if (is_file == 1) - allowListFiles.push_back(line); - else - allowListFunctions.push_back(line); - - } - - getline(fileStream, line); - - } - - if (debug) - DEBUGF("loaded allowlist with %zu file and %zu function entries\n", - allowListFiles.size(), allowListFunctions.size()); - - } - - if (denylist) { - - std::string line; - std::ifstream fileStream; - fileStream.open(denylist); - if (!fileStream) report_fatal_error("Unable to open AFL_GCC_DENYLIST"); - getline(fileStream, line); - - while (fileStream) { - - int is_file = -1; - std::size_t npos; - std::string original_line = line; - - line.erase(std::remove_if(line.begin(), line.end(), ::isspace), - line.end()); - - // remove # and following - if ((npos = line.find("#")) != std::string::npos) - line = line.substr(0, npos); - - if (line.compare(0, 4, "fun:") == 0) { - - is_file = 0; - line = line.substr(4); - - } else if (line.compare(0, 9, "function:") == 0) { - - is_file = 0; - line = line.substr(9); - - } else if (line.compare(0, 4, "src:") == 0) { - - is_file = 1; - line = line.substr(4); - - } else if (line.compare(0, 7, "source:") == 0) { - - is_file = 1; - line = line.substr(7); - - } - - if (line.find(":") != std::string::npos) { - - FATAL("invalid line in AFL_GCC_DENYLIST: %s", original_line.c_str()); - - } - - if (line.length() > 0) { - - // if the entry contains / or . it must be a file - if (is_file == -1) - if (line.find("/") != std::string::npos || - line.find(".") != std::string::npos) - is_file = 1; - // otherwise it is a function - - if (is_file == 1) - denyListFiles.push_back(line); - else - denyListFunctions.push_back(line); - - } - - getline(fileStream, line); - - } - - if (debug) - DEBUGF("loaded denylist with %zu file and %zu function entries\n", - denyListFiles.size(), denyListFunctions.size()); - - } - - } - - /* Returns the source file name attached to the function declaration F. If - there is no source location information, returns an empty string. */ - std::string getSourceName(function *F) { - - return DECL_SOURCE_FILE(F->decl) ? DECL_SOURCE_FILE(F->decl) : ""; - - } - - bool isInInstrumentList(function *F) { - - bool return_default = true; - - // is this a function with code? If it is external we don't instrument it - // anyway and it can't be in the instrument file list. Or if it is it is - // ignored. - if (isIgnoreFunction(F)) return false; - - if (!denyListFiles.empty() || !denyListFunctions.empty()) { - - if (!denyListFunctions.empty()) { - - std::string instFunction = IDENTIFIER_POINTER(DECL_NAME(F->decl)); - - for (std::list<std::string>::iterator it = denyListFunctions.begin(); - it != denyListFunctions.end(); ++it) { - - /* We don't check for filename equality here because - * filenames might actually be full paths. Instead we - * check that the actual filename ends in the filename - * specified in the list. We also allow UNIX-style pattern - * matching */ - - if (instFunction.length() >= it->length()) { - - if (fnmatch(("*" + *it).c_str(), instFunction.c_str(), 0) == 0) { - - if (debug) - DEBUGF( - "Function %s is in the deny function list, not " - "instrumenting ... \n", - instFunction.c_str()); - return false; - - } - - } - - } - - } - - if (!denyListFiles.empty()) { - - std::string source_file = getSourceName(F); - - if (!source_file.empty()) { - - for (std::list<std::string>::iterator it = denyListFiles.begin(); - it != denyListFiles.end(); ++it) { - - /* We don't check for filename equality here because - * filenames might actually be full paths. Instead we - * check that the actual filename ends in the filename - * specified in the list. We also allow UNIX-style pattern - * matching */ - - if (source_file.length() >= it->length()) { - - if (fnmatch(("*" + *it).c_str(), source_file.c_str(), 0) == 0) { - - return false; - - } - - } - - } - - } else { - - // we could not find out the location. in this case we say it is not - // in the instrument file list - if (!be_quiet) - WARNF( - "No debug information found for function %s, will be " - "instrumented (recompile with -g -O[1-3]).", - IDENTIFIER_POINTER(DECL_NAME(F->decl))); - - } - - } - - } - - // if we do not have a instrument file list return true - if (!allowListFiles.empty() || !allowListFunctions.empty()) { - - return_default = false; - - if (!allowListFunctions.empty()) { - - std::string instFunction = IDENTIFIER_POINTER(DECL_NAME(F->decl)); - - for (std::list<std::string>::iterator it = allowListFunctions.begin(); - it != allowListFunctions.end(); ++it) { - - /* We don't check for filename equality here because - * filenames might actually be full paths. Instead we - * check that the actual filename ends in the filename - * specified in the list. We also allow UNIX-style pattern - * matching */ - - if (instFunction.length() >= it->length()) { - - if (fnmatch(("*" + *it).c_str(), instFunction.c_str(), 0) == 0) { - - if (debug) - DEBUGF( - "Function %s is in the allow function list, instrumenting " - "... \n", - instFunction.c_str()); - return true; - - } - - } - - } - - } - - if (!allowListFiles.empty()) { - - std::string source_file = getSourceName(F); - - if (!source_file.empty()) { - - for (std::list<std::string>::iterator it = allowListFiles.begin(); - it != allowListFiles.end(); ++it) { - - /* We don't check for filename equality here because - * filenames might actually be full paths. Instead we - * check that the actual filename ends in the filename - * specified in the list. We also allow UNIX-style pattern - * matching */ - - if (source_file.length() >= it->length()) { - - if (fnmatch(("*" + *it).c_str(), source_file.c_str(), 0) == 0) { - - if (debug) - DEBUGF( - "Function %s is in the allowlist (%s), instrumenting ... " - "\n", - IDENTIFIER_POINTER(DECL_NAME(F->decl)), - source_file.c_str()); - return true; - - } - - } - - } - - } else { - - // we could not find out the location. In this case we say it is not - // in the instrument file list - if (!be_quiet) - WARNF( - "No debug information found for function %s, will not be " - "instrumented (recompile with -g -O[1-3]).", - IDENTIFIER_POINTER(DECL_NAME(F->decl))); - return false; - - } - - } - - } - - return return_default; - - } - }; static struct plugin_info afl_plugin = { - .version = "20220907", + .version = "20220420", .help = G_("AFL gcc plugin\n\ \n\ Set AFL_QUIET in the environment to silence it.\n\ diff --git a/instrumentation/split-compares-pass.so.cc b/instrumentation/split-compares-pass.so.cc index d7bb7aba..375de065 100644 --- a/instrumentation/split-compares-pass.so.cc +++ b/instrumentation/split-compares-pass.so.cc @@ -566,8 +566,17 @@ bool SplitComparesTransform::splitCompare(CmpInst *cmp_inst, Module &M, case CmpInst::ICMP_NE: case CmpInst::ICMP_UGT: case CmpInst::ICMP_ULT: + case CmpInst::ICMP_UGE: + case CmpInst::ICMP_ULE: + case CmpInst::ICMP_SGT: + case CmpInst::ICMP_SLT: + case CmpInst::ICMP_SGE: + case CmpInst::ICMP_SLE: break; default: + if (!be_quiet) + fprintf(stderr, "Error: split-compare: Unsupported predicate (%u)\n", + pred); // unsupported predicate! return false; @@ -581,6 +590,7 @@ bool SplitComparesTransform::splitCompare(CmpInst *cmp_inst, Module &M, if (!intTyOp0) { // not an integer type + fprintf(stderr, "Error: split-compare: not an integer type\n"); return false; } @@ -675,6 +685,12 @@ bool SplitComparesTransform::splitCompare(CmpInst *cmp_inst, Module &M, } + case CmpInst::ICMP_SGE: + case CmpInst::ICMP_SLE: + case CmpInst::ICMP_SGT: + case CmpInst::ICMP_SLT: + case CmpInst::ICMP_UGE: + case CmpInst::ICMP_ULE: case CmpInst::ICMP_UGT: case CmpInst::ICMP_ULT: { @@ -687,7 +703,8 @@ bool SplitComparesTransform::splitCompare(CmpInst *cmp_inst, Module &M, CmpInst * icmp_inv_cmp = nullptr; BasicBlock * inv_cmp_bb = BasicBlock::Create(C, "inv_cmp", end_bb->getParent(), end_bb); - if (pred == CmpInst::ICMP_UGT) { + if (pred == CmpInst::ICMP_UGT || pred == CmpInst::ICMP_SGT || + pred == CmpInst::ICMP_UGE || pred == CmpInst::ICMP_SGE) { icmp_inv_cmp = CmpInst::Create(Instruction::ICmp, CmpInst::ICMP_ULT, op0_high, op1_high); @@ -729,6 +746,8 @@ bool SplitComparesTransform::splitCompare(CmpInst *cmp_inst, Module &M, } default: + if (!be_quiet) + fprintf(stderr, "Error: split-compare: should not happen\n"); return false; } diff --git a/nyx_mode/build_nyx_support.sh b/nyx_mode/build_nyx_support.sh index 83e0ae32..e7fca64f 100755 --- a/nyx_mode/build_nyx_support.sh +++ b/nyx_mode/build_nyx_support.sh @@ -1,4 +1,7 @@ #!/bin/bash + +set -e + echo "=================================================" echo " Nyx build script" echo "=================================================" @@ -6,14 +9,14 @@ echo echo "[*] Performing basic sanity checks..." -if [ ! "`uname -s`" = "Linux" ]; then +if [ ! "$(uname -s)" = "Linux" ]; then echo "[-] Error: Nyx mode is only available on Linux." exit 0 fi -if [ ! "`uname -m`" = "x86_64" ]; then +if [ ! "$(uname -m)" = "x86_64" ]; then echo "[-] Error: Nyx mode is only available on x86_64 (yet)." exit 0 @@ -22,10 +25,10 @@ fi echo "[*] Making sure all Nyx is checked out" -git status 1>/dev/null 2>/dev/null -if [ $? -eq 0 ]; then - git submodule init || exit 1 +if git status 1>/dev/null 2>&1; then + + git submodule init echo "[*] initializing QEMU-Nyx submodule" git submodule update ./QEMU-Nyx 2>/dev/null # ignore errors echo "[*] initializing packer submodule" @@ -47,32 +50,27 @@ test -e QEMU-Nyx/.git || { echo "[-] QEMU-Nyx not checked out, please install gi echo "[*] checking packer init.cpio.gz ..." if [ ! -f "packer/linux_initramfs/init.cpio.gz" ]; then - cd packer/linux_initramfs/ - sh pack.sh || exit 1 - cd ../../ + (cd packer/linux_initramfs/ && sh pack.sh) fi echo "[*] Checking libnyx ..." if [ ! -f "libnyx/libnyx/target/release/liblibnyx.a" ]; then - cd libnyx/libnyx - cargo build --release || exit 1 - cd ../../ + (cd libnyx/libnyx && cargo build --release) fi echo "[*] Checking QEMU-Nyx ..." if [ ! -f "QEMU-Nyx/x86_64-softmmu/qemu-system-x86_64" ]; then - cd QEMU-Nyx/ - ./compile_qemu_nyx.sh static || exit 1 - cd .. + + 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 echo "[*] Checking libnyx.so ..." -if [ -f "libnyx/libnyx/target/release/liblibnyx.so" ]; then - cp -v libnyx/libnyx/target/release/liblibnyx.so ../libnyx.so || exit 1 -else - echo "[ ] libnyx.so not found..." - exit 1 -fi +cp libnyx/libnyx/target/release/liblibnyx.so ../libnyx.so + echo "[+] All done for nyx_mode, enjoy!" exit 0 diff --git a/qemu_mode/build_qemu_support.sh b/qemu_mode/build_qemu_support.sh index 5dbd9d44..277a6323 100755 --- a/qemu_mode/build_qemu_support.sh +++ b/qemu_mode/build_qemu_support.sh @@ -273,7 +273,7 @@ echo "[+] Configuration complete." echo "[*] Attempting to build QEMU (fingers crossed!)..." -make -j `nproc` || exit 1 +make -j$(nproc) || exit 1 echo "[+] Build process successful!" diff --git a/qemu_mode/libqasan/patch.c b/qemu_mode/libqasan/patch.c index ee928ab3..8c5553c0 100644 --- a/qemu_mode/libqasan/patch.c +++ b/qemu_mode/libqasan/patch.c @@ -147,7 +147,7 @@ static void find_libc(void) { fields = sscanf(line, "%" PRIx64 "-%" PRIx64 " %c%c%c%c %" PRIx64 " %x:%x %d" - " %512s", + " %511s", &min, &max, &flag_r, &flag_w, &flag_x, &flag_p, &offset, &dev_maj, &dev_min, &inode, path); diff --git a/src/afl-cc.c b/src/afl-cc.c index 4a56169f..246e01cd 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -422,8 +422,24 @@ static void edit_params(u32 argc, char **argv, char **envp) { if (compiler_mode == GCC_PLUGIN) { - char *fplugin_arg = alloc_printf("-fplugin=%s/afl-gcc-pass.so", obj_path); - cc_params[cc_par_cnt++] = fplugin_arg; + char *fplugin_arg; + + if (cmplog_mode) { + + fplugin_arg = + alloc_printf("-fplugin=%s/afl-gcc-cmplog-pass.so", obj_path); + cc_params[cc_par_cnt++] = fplugin_arg; + fplugin_arg = + alloc_printf("-fplugin=%s/afl-gcc-cmptrs-pass.so", obj_path); + cc_params[cc_par_cnt++] = fplugin_arg; + + } else { + + fplugin_arg = alloc_printf("-fplugin=%s/afl-gcc-pass.so", obj_path); + cc_params[cc_par_cnt++] = fplugin_arg; + + } + cc_params[cc_par_cnt++] = "-fno-if-conversion"; cc_params[cc_par_cnt++] = "-fno-if-conversion2"; @@ -1879,6 +1895,7 @@ int main(int argc, char **argv, char **envp) { if (have_gcc_plugin) SAYF( "\nGCC Plugin-specific environment variables:\n" + " AFL_GCC_CMPLOG: log operands of comparisons (RedQueen mutator)\n" " AFL_GCC_OUT_OF_LINE: disable inlined instrumentation\n" " AFL_GCC_SKIP_NEVERZERO: do not skip zero on trace counters\n" " AFL_GCC_INSTRUMENT_FILE: enable selective instrumentation by " @@ -2149,9 +2166,8 @@ int main(int argc, char **argv, char **envp) { } - cmplog_mode = getenv("AFL_CMPLOG") || getenv("AFL_LLVM_CMPLOG"); - if (!be_quiet && cmplog_mode) - printf("CmpLog mode by <andreafioraldi@gmail.com>\n"); + cmplog_mode = getenv("AFL_CMPLOG") || getenv("AFL_LLVM_CMPLOG") || + getenv("AFL_GCC_CMPLOG"); #if !defined(__ANDROID__) && !defined(ANDROID) ptr = find_object("afl-compiler-rt.o", argv[0]); diff --git a/src/afl-fuzz-init.c b/src/afl-fuzz-init.c index 6a653a00..f4b2d908 100644 --- a/src/afl-fuzz-init.c +++ b/src/afl-fuzz-init.c @@ -146,6 +146,10 @@ void bind_to_free_cpu(afl_state_t *afl) { } + } else { + + OKF("CPU binding request using -b %d successful.", afl->cpu_to_bind); + } return; diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index b23cef37..18367cf2 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -1469,7 +1469,7 @@ int main(int argc, char **argv_orig, char **envp) { if (afl->shm.cmplog_mode && (!strcmp("-", afl->cmplog_binary) || !strcmp("0", afl->cmplog_binary))) { - afl->cmplog_binary = argv[optind]; + afl->cmplog_binary = strdup(argv[optind]); } diff --git a/test-instr.c b/test-instr.c index b2caa1fe..f304e208 100644 --- a/test-instr.c +++ b/test-instr.c @@ -58,12 +58,21 @@ int main(int argc, char **argv) { // we support three input cases (plus a 4th if stdin is used but there is no // input) - if (buf[0] == '0') - printf("Looks like a zero to me!\n"); - else if (buf[0] == '1') - printf("Pretty sure that is a one!\n"); - else - printf("Neither one or zero? How quaint!\n"); + switch (buf[0]) { + + case '0': + printf("Looks like a zero to me!\n"); + break; + + case '1': + printf("Pretty sure that is a one!\n"); + break; + + default: + printf("Neither one or zero? How quaint!\n"); + break; + + } return 0; |