mirror of
https://github.com/AFLplusplus/AFLplusplus.git
synced 2025-06-12 01:58:17 +00:00
@ -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.")
|
||||
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)
|
||||
# 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"
|
||||
|
||||
COLUMN_LIMIT = 80
|
||||
for line in fmt.split("\n"):
|
||||
|
@ -1,65 +1,75 @@
|
||||
!/coresight_mode
|
||||
*.dSYM
|
||||
*.o
|
||||
*.pyc
|
||||
*.so
|
||||
.sync_tmp
|
||||
.test
|
||||
.test2
|
||||
.sync_tmp
|
||||
*.o
|
||||
*.so
|
||||
*.pyc
|
||||
*.dSYM
|
||||
as
|
||||
ld
|
||||
in
|
||||
out
|
||||
core*
|
||||
.git
|
||||
.dockerignore
|
||||
.github
|
||||
CITATION.cff
|
||||
CONTRIBUTING.md
|
||||
Changelog.md
|
||||
Dockerfile
|
||||
LICENSE
|
||||
TODO.md
|
||||
afl-analyze
|
||||
afl-as
|
||||
afl-clang
|
||||
afl-clang\+\+
|
||||
afl-clang-fast
|
||||
afl-clang-fast\+\+
|
||||
afl-clang-lto
|
||||
afl-clang-lto\+\+
|
||||
afl-fuzz
|
||||
afl-g\+\+
|
||||
afl-gcc
|
||||
afl-gcc-fast
|
||||
afl-g\+\+-fast
|
||||
afl-gotcpu
|
||||
afl-ld
|
||||
afl-ld-lto
|
||||
afl-qemu-trace
|
||||
afl-showmap
|
||||
afl-tmin
|
||||
afl-analyze.8
|
||||
afl-as
|
||||
afl-as.8
|
||||
afl-clang-fast\+\+.8
|
||||
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-gcc.8
|
||||
afl-gcc-fast.8
|
||||
afl-g\+\+
|
||||
afl-g\+\+-fast
|
||||
afl-g\+\+-fast.8
|
||||
afl-gcc
|
||||
afl-gcc-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-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-*
|
||||
test/unittests/unit_hash
|
||||
test/unittests/unit_list
|
||||
test/unittests/unit_maybe_alloc
|
||||
test/unittests/unit_preallocable
|
||||
test/unittests/unit_rand
|
||||
unicorn_mode/samples/*/\.test-*
|
||||
unicorn_mode/samples/*/output
|
||||
unicorn_mode/unicornafl
|
||||
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
|
49
.github/workflows/build_aflplusplus_docker.yaml
vendored
49
.github/workflows/build_aflplusplus_docker.yaml
vendored
@ -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' }}
|
15
.github/workflows/ci.yml
vendored
15
.github/workflows/ci.yml
vendored
@ -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
|
||||
|
33
.github/workflows/code-format.yml
vendored
Normal file
33
.github/workflows/code-format.yml
vendored
Normal file
@ -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
|
37
.github/workflows/codeql-analysis.yml
vendored
37
.github/workflows/codeql-analysis.yml
vendored
@ -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: 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@v1
|
||||
uses: github/codeql-action/init@v2
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@v1
|
||||
|
||||
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@v1
|
||||
uses: github/codeql-action/analyze@v2
|
||||
|
75
.github/workflows/container.yml
vendored
Normal file
75
.github/workflows/container.yml
vendored
Normal file
@ -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
|
9
.github/workflows/rust_custom_mutator.yml
vendored
9
.github/workflows/rust_custom_mutator.yml
vendored
@ -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:
|
||||
|
121
Dockerfile
121
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
|
||||
|
||||
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/*
|
||||
|
||||
# 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 -
|
||||
|
||||
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
|
||||
ENV NO_ARCH_OPT=1
|
||||
ENV IS_DOCKER=1
|
||||
|
||||
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
|
||||
apt-get install -y --no-install-recommends wget ca-certificates && \
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# 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
|
||||
ENV LLVM_VERSION=14
|
||||
ENV GCC_VERSION=11
|
||||
|
||||
RUN rm -rf /var/lib/apt/lists/*
|
||||
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 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
|
||||
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
|
||||
|
||||
ENV LLVM_CONFIG=llvm-config-14
|
||||
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 . .
|
||||
|
||||
RUN export CC=gcc-12 && export CXX=g++-12 && make clean && \
|
||||
make distrib && make install && make clean
|
||||
ARG CC=gcc-$GCC_VERSION
|
||||
ARG CXX=g++-$GCC_VERSION
|
||||
|
||||
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"
|
||||
# Used in CI to prevent a 'make clean' which would remove the binaries to be tested
|
||||
ARG TEST_BUILD
|
||||
|
||||
# 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 sed -i.bak 's/^ -/ /g' GNUmakefile && \
|
||||
make clean && make distrib && \
|
||||
([ "${TEST_BUILD}" ] || (make install && make clean)) && \
|
||||
mv GNUmakefile.bak 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
|
||||
|
24
GNUmakefile
24
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,16 +623,24 @@ 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
|
||||
-cd nyx_mode && ./build_nyx_support.sh
|
||||
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
|
||||
binary-only: test_shm test_python ready $(PROGS)
|
||||
@ -645,20 +653,28 @@ 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
|
||||
-cd nyx_mode && ./build_nyx_support.sh
|
||||
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
|
||||
|
@ -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
|
||||
|
1
afl-cmin
1
afl-cmin
@ -534,7 +534,6 @@ BEGIN {
|
||||
}
|
||||
}
|
||||
close(sortedKeys)
|
||||
print ""
|
||||
print "[+] Found "tuple_count" unique tuples across "in_count" files."
|
||||
|
||||
if (out_count == 1) {
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -8,6 +8,13 @@
|
||||
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! :-)
|
||||
- llvm_mode:
|
||||
- laf cmp splitting fixed for more comparison types
|
||||
|
||||
|
||||
### Version ++4.01c (release)
|
||||
- fixed */build_...sh scripts to work outside of git
|
||||
- new custom_mutator: libafl with token fuzzing :)
|
||||
|
@ -21,12 +21,12 @@ development state of AFL++.
|
||||
If you want to build AFL++ yourself, you have many options. The easiest choice
|
||||
is to build and install everything:
|
||||
|
||||
NOTE: depending on your Debian/Ubuntu/Kali/... version replease `-12` with
|
||||
NOTE: depending on your Debian/Ubuntu/Kali/... version release `-12` with
|
||||
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
|
||||
@ -148,7 +148,7 @@ and definitely don't look POSIX-compliant. This means two things:
|
||||
environment before starting afl-fuzz.
|
||||
|
||||
User emulation mode of QEMU does not appear to be supported on MacOS X, so
|
||||
black-box instrumentation mode (`-Q`) will not work. However, Frida mode (`-O`)
|
||||
black-box instrumentation mode (`-Q`) will not work. However, FRIDA mode (`-O`)
|
||||
works on both x86 and arm64 MacOS boxes.
|
||||
|
||||
MacOS X supports SYSV shared memory used by AFL's instrumentation, but the
|
||||
|
@ -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).
|
||||
|
||||
@ -460,7 +462,7 @@ checks or alter some of the more exotic semantics of the tool:
|
||||
some basic stats. This behavior is also automatically triggered when the
|
||||
output from afl-fuzz is redirected to a file or to a pipe.
|
||||
|
||||
- In QEMU mode (-Q) and Frida mode (-O), `AFL_PATH` will be searched for
|
||||
- In QEMU mode (-Q) and FRIDA mode (-O), `AFL_PATH` will be searched for
|
||||
afl-qemu-trace and afl-frida-trace.so.
|
||||
|
||||
- If you are using persistent mode (you should, see
|
||||
@ -553,10 +555,10 @@ checks or alter some of the more exotic semantics of the tool:
|
||||
in the target binary
|
||||
|
||||
- If you need an early forkserver in your target because of early
|
||||
constructors in your target you can set `AFL_EARLY_FORKSERVER`.
|
||||
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 0
|
||||
to disable although it is 1st of April.
|
||||
|
||||
## 5) Settings for afl-qemu-trace
|
||||
|
@ -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) | | | | | | |
|
||||
|
@ -838,9 +838,10 @@ Here are some of the most important caveats for AFL++:
|
||||
|
||||
- There is no direct support for fuzzing network services, background daemons,
|
||||
or interactive apps that require UI interaction to work. You may need to make
|
||||
simple code changes to make them behave in a more traditional way. Preeny or libdesock may
|
||||
offer a relatively simple option, too - see:
|
||||
[https://github.com/zardus/preeny](https://github.com/zardus/preeny) or [https://github.com/fkie-cad/libdesock](https://github.com/fkie-cad/libdesock)
|
||||
simple code changes to make them behave in a more traditional way. Preeny or
|
||||
libdesock may offer a relatively simple option, too - see:
|
||||
[https://github.com/zardus/preeny](https://github.com/zardus/preeny) or
|
||||
[https://github.com/fkie-cad/libdesock](https://github.com/fkie-cad/libdesock)
|
||||
|
||||
Some useful tips for modifying network-based services can be also found at:
|
||||
[https://www.fastly.com/blog/how-to-fuzz-server-american-fuzzy-lop](https://www.fastly.com/blog/how-to-fuzz-server-american-fuzzy-lop)
|
||||
|
@ -19,18 +19,18 @@ 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
|
||||
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?) supported
|
||||
for fuzzing, e.g., mono, swift, go, kotlin native, fortran, ...
|
||||
Other programming languages also use llvm hence they could be (easily?)
|
||||
supported for fuzzing, e.g., mono, swift, go, kotlin native, fortran, ...
|
||||
|
||||
GCC also supports: Objective-C, Fortran, Ada, Go, and D (according to
|
||||
[Gcc homepage](https://gcc.gnu.org/))
|
||||
|
@ -1,11 +1,13 @@
|
||||
# Tools that help fuzzing with AFL++
|
||||
|
||||
Speeding up fuzzing:
|
||||
## Speeding up fuzzing
|
||||
|
||||
* [libfiowrapper](https://github.com/marekzmyslowski/libfiowrapper) - if the
|
||||
function you want to fuzz requires loading a file, this allows using the
|
||||
shared memory test case feature :-) - recommended.
|
||||
|
||||
Minimization of test cases:
|
||||
## Minimization of test cases
|
||||
|
||||
* [afl-pytmin](https://github.com/ilsani/afl-pytmin) - a wrapper for afl-tmin
|
||||
that tries to speed up the process of minimization of a single test case by
|
||||
using many CPU cores.
|
||||
@ -14,7 +16,8 @@ Minimization of test cases:
|
||||
* [halfempty](https://github.com/googleprojectzero/halfempty) - is a fast
|
||||
utility for minimizing test cases by Tavis Ormandy based on parallelization.
|
||||
|
||||
Distributed execution:
|
||||
## Distributed execution
|
||||
|
||||
* [disfuzz-afl](https://github.com/MartijnB/disfuzz-afl) - distributed fuzzing
|
||||
for AFL.
|
||||
* [AFLDFF](https://github.com/quantumvm/AFLDFF) - AFL distributed fuzzing
|
||||
@ -26,7 +29,8 @@ Distributed execution:
|
||||
* [afl-in-the-cloud](https://github.com/abhisek/afl-in-the-cloud) - another
|
||||
script for running AFL in AWS.
|
||||
|
||||
Deployment, management, monitoring, reporting
|
||||
## Deployment, management, monitoring, reporting
|
||||
|
||||
* [afl-utils](https://gitlab.com/rc0r/afl-utils) - a set of utilities for
|
||||
automatic processing/analysis of crashes and reducing the number of test
|
||||
cases.
|
||||
@ -44,7 +48,8 @@ Deployment, management, monitoring, reporting
|
||||
* [afl-extras](https://github.com/fekir/afl-extras) - shell scripts to
|
||||
parallelize afl-tmin, startup, and data collection.
|
||||
|
||||
Crash processing
|
||||
## Crash processing
|
||||
|
||||
* [AFLTriage](https://github.com/quic/AFLTriage) -
|
||||
triage crashing input files using gdb.
|
||||
* [afl-crash-analyzer](https://github.com/floyd-fuh/afl-crash-analyzer) -
|
||||
|
@ -33,6 +33,7 @@ structure is), these links have you covered (some are outdated though):
|
||||
[https://github.com/adrian-rt/superion-mutator](https://github.com/adrian-rt/superion-mutator)
|
||||
|
||||
## Video Tutorials
|
||||
|
||||
* [Install AFL++ Ubuntu](https://www.youtube.com/watch?v=5dCvhkbi3RA)
|
||||
* [[Fuzzing with AFLplusplus] Installing AFLPlusplus and fuzzing a simple C program](https://www.youtube.com/watch?v=9wRVo0kYSlc)
|
||||
* [[Fuzzing with AFLplusplus] How to fuzz a binary with no source code on Linux in persistent mode](https://www.youtube.com/watch?v=LGPJdEO02p4)
|
||||
|
@ -116,7 +116,7 @@ ifndef OS
|
||||
$(error "Operating system unsupported")
|
||||
endif
|
||||
|
||||
GUM_DEVKIT_VERSION=15.1.22
|
||||
GUM_DEVKIT_VERSION=15.1.27
|
||||
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)"
|
||||
|
||||
@ -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)
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -225,8 +225,8 @@ static void instrument_cache_rewrite_branch_insn(const cs_insn * instr,
|
||||
} else {
|
||||
|
||||
GumAddress target = instr->address + old_offset;
|
||||
gum_x86_writer_put_mov_reg_address(cw, GUM_REG_RAX, target);
|
||||
gum_x86_writer_put_mov_reg_reg_ptr(cw, GUM_REG_RAX, GUM_REG_RAX);
|
||||
gum_x86_writer_put_mov_reg_address(cw, GUM_X86_RAX, target);
|
||||
gum_x86_writer_put_mov_reg_reg_ptr(cw, GUM_X86_RAX, GUM_X86_RAX);
|
||||
return;
|
||||
|
||||
}
|
||||
@ -249,29 +249,29 @@ static void instrument_cache_rewrite_branch_insn(const cs_insn * instr,
|
||||
static void instrument_cache_write_push_frame(GumX86Writer *cw) {
|
||||
|
||||
gum_x86_writer_put_mov_reg_offset_ptr_reg(
|
||||
cw, GUM_REG_XSP, -(GUM_RED_ZONE_SIZE + (1 * sizeof(gpointer))),
|
||||
GUM_REG_XAX);
|
||||
cw, GUM_X86_XSP, -(GUM_RED_ZONE_SIZE + (1 * sizeof(gpointer))),
|
||||
GUM_X86_XAX);
|
||||
gum_x86_writer_put_lahf(cw);
|
||||
gum_x86_writer_put_mov_reg_offset_ptr_reg(
|
||||
cw, GUM_REG_XSP, -(GUM_RED_ZONE_SIZE + (2 * sizeof(gpointer))),
|
||||
GUM_REG_XAX);
|
||||
cw, GUM_X86_XSP, -(GUM_RED_ZONE_SIZE + (2 * sizeof(gpointer))),
|
||||
GUM_X86_XAX);
|
||||
gum_x86_writer_put_mov_reg_offset_ptr_reg(
|
||||
cw, GUM_REG_XSP, -(GUM_RED_ZONE_SIZE + (3 * sizeof(gpointer))),
|
||||
GUM_REG_XBX);
|
||||
cw, GUM_X86_XSP, -(GUM_RED_ZONE_SIZE + (3 * sizeof(gpointer))),
|
||||
GUM_X86_XBX);
|
||||
|
||||
}
|
||||
|
||||
static void instrument_cache_write_pop_frame(GumX86Writer *cw) {
|
||||
|
||||
gum_x86_writer_put_mov_reg_reg_offset_ptr(
|
||||
cw, GUM_REG_XBX, GUM_REG_XSP,
|
||||
cw, GUM_X86_XBX, GUM_X86_XSP,
|
||||
-(GUM_RED_ZONE_SIZE + (3 * sizeof(gpointer))));
|
||||
gum_x86_writer_put_mov_reg_reg_offset_ptr(
|
||||
cw, GUM_REG_XAX, GUM_REG_XSP,
|
||||
cw, GUM_X86_XAX, GUM_X86_XSP,
|
||||
-(GUM_RED_ZONE_SIZE + (2 * sizeof(gpointer))));
|
||||
gum_x86_writer_put_sahf(cw);
|
||||
gum_x86_writer_put_mov_reg_reg_offset_ptr(
|
||||
cw, GUM_REG_XAX, GUM_REG_XSP,
|
||||
cw, GUM_X86_XAX, GUM_X86_XSP,
|
||||
-(GUM_RED_ZONE_SIZE + (1 * sizeof(gpointer))));
|
||||
|
||||
}
|
||||
@ -281,14 +281,14 @@ static void instrument_cache_write_lookup(GumX86Writer *cw) {
|
||||
/* &map_base[GPOINTER_TO_SIZE(addr) & MAP_MASK]; */
|
||||
|
||||
gsize mask = (instrument_cache_size / sizeof(gpointer)) - 1;
|
||||
gum_x86_writer_put_mov_reg_u64(cw, GUM_REG_XBX, mask);
|
||||
gum_x86_writer_put_and_reg_reg(cw, GUM_REG_XAX, GUM_REG_XBX);
|
||||
gum_x86_writer_put_shl_reg_u8(cw, GUM_REG_XAX, util_log2(sizeof(gpointer)));
|
||||
gum_x86_writer_put_mov_reg_u64(cw, GUM_REG_XBX, GPOINTER_TO_SIZE(map_base));
|
||||
gum_x86_writer_put_add_reg_reg(cw, GUM_REG_XAX, GUM_REG_XBX);
|
||||
gum_x86_writer_put_mov_reg_u64(cw, GUM_X86_XBX, mask);
|
||||
gum_x86_writer_put_and_reg_reg(cw, GUM_X86_XAX, GUM_X86_XBX);
|
||||
gum_x86_writer_put_shl_reg_u8(cw, GUM_X86_XAX, util_log2(sizeof(gpointer)));
|
||||
gum_x86_writer_put_mov_reg_u64(cw, GUM_X86_XBX, GPOINTER_TO_SIZE(map_base));
|
||||
gum_x86_writer_put_add_reg_reg(cw, GUM_X86_XAX, GUM_X86_XBX);
|
||||
|
||||
/* Read the return address lookup */
|
||||
gum_x86_writer_put_mov_reg_reg_ptr(cw, GUM_REG_XAX, GUM_REG_XAX);
|
||||
gum_x86_writer_put_mov_reg_reg_ptr(cw, GUM_X86_XAX, GUM_X86_XAX);
|
||||
|
||||
}
|
||||
|
||||
@ -315,7 +315,7 @@ void instrument_cache_jmp_call(const cs_insn *instr, GumStalkerOutput *output) {
|
||||
* red-zone.
|
||||
*/
|
||||
gum_x86_writer_put_mov_reg_reg_offset_ptr(
|
||||
cw, GUM_REG_XAX, GUM_REG_XSP,
|
||||
cw, GUM_X86_XAX, GUM_X86_XSP,
|
||||
-(GUM_RED_ZONE_SIZE + (1 * sizeof(gpointer))));
|
||||
|
||||
instrument_cache_rewrite_branch_insn(instr, output);
|
||||
@ -323,33 +323,33 @@ void instrument_cache_jmp_call(const cs_insn *instr, GumStalkerOutput *output) {
|
||||
instrument_cache_write_lookup(cw);
|
||||
|
||||
/* Test if its set*/
|
||||
gum_x86_writer_put_cmp_reg_i32(cw, GUM_REG_XAX, INVALID);
|
||||
gum_x86_writer_put_cmp_reg_i32(cw, GUM_X86_XAX, INVALID);
|
||||
gum_x86_writer_put_jcc_short_label(cw, X86_INS_JLE, null, GUM_UNLIKELY);
|
||||
|
||||
/* If it's set, then stash the address beyond the red-zone */
|
||||
gum_x86_writer_put_mov_reg_offset_ptr_reg(
|
||||
cw, GUM_REG_XSP, -(GUM_RED_ZONE_SIZE + (4 * sizeof(gpointer))),
|
||||
GUM_REG_XAX);
|
||||
cw, GUM_X86_XSP, -(GUM_RED_ZONE_SIZE + (4 * sizeof(gpointer))),
|
||||
GUM_X86_XAX);
|
||||
|
||||
if (instr->id == X86_INS_JMP) {
|
||||
|
||||
instrument_cache_write_pop_frame(cw);
|
||||
gum_x86_writer_put_jmp_reg_offset_ptr(
|
||||
cw, GUM_REG_XSP, -(GUM_RED_ZONE_SIZE + (4 * sizeof(gpointer))));
|
||||
cw, GUM_X86_XSP, -(GUM_RED_ZONE_SIZE + (4 * sizeof(gpointer))));
|
||||
|
||||
} else {
|
||||
|
||||
gum_x86_writer_put_mov_reg_address(
|
||||
cw, GUM_REG_XAX, GUM_ADDRESS(instr->address + instr->size));
|
||||
gum_x86_writer_put_mov_reg_offset_ptr_reg(cw, GUM_REG_XSP,
|
||||
-sizeof(gpointer), GUM_REG_XAX);
|
||||
cw, GUM_X86_XAX, GUM_ADDRESS(instr->address + instr->size));
|
||||
gum_x86_writer_put_mov_reg_offset_ptr_reg(cw, GUM_X86_XSP,
|
||||
-sizeof(gpointer), GUM_X86_XAX);
|
||||
|
||||
instrument_cache_write_pop_frame(cw);
|
||||
|
||||
gum_x86_writer_put_lea_reg_reg_offset(cw, GUM_REG_XSP, GUM_REG_XSP,
|
||||
gum_x86_writer_put_lea_reg_reg_offset(cw, GUM_X86_XSP, GUM_X86_XSP,
|
||||
-sizeof(gpointer));
|
||||
gum_x86_writer_put_jmp_reg_offset_ptr(
|
||||
cw, GUM_REG_XSP, -(GUM_RED_ZONE_SIZE + ((4 - 1) * sizeof(gpointer))));
|
||||
cw, GUM_X86_XSP, -(GUM_RED_ZONE_SIZE + ((4 - 1) * sizeof(gpointer))));
|
||||
|
||||
}
|
||||
|
||||
@ -381,16 +381,16 @@ void instrument_cache_ret(const cs_insn *instr, GumStalkerOutput *output) {
|
||||
|
||||
instrument_cache_write_push_frame(cw);
|
||||
|
||||
gum_x86_writer_put_mov_reg_reg_ptr(cw, GUM_REG_XAX, GUM_REG_XSP);
|
||||
gum_x86_writer_put_mov_reg_reg_ptr(cw, GUM_X86_XAX, GUM_X86_XSP);
|
||||
|
||||
instrument_cache_write_lookup(cw);
|
||||
|
||||
/* Test if its set*/
|
||||
gum_x86_writer_put_cmp_reg_i32(cw, GUM_REG_XAX, INVALID);
|
||||
gum_x86_writer_put_cmp_reg_i32(cw, GUM_X86_XAX, INVALID);
|
||||
gum_x86_writer_put_jcc_short_label(cw, X86_INS_JLE, null, GUM_UNLIKELY);
|
||||
|
||||
/* If it's set, then overwrite our return address and return */
|
||||
gum_x86_writer_put_mov_reg_ptr_reg(cw, GUM_REG_XSP, GUM_REG_XAX);
|
||||
gum_x86_writer_put_mov_reg_ptr_reg(cw, GUM_X86_XSP, GUM_X86_XAX);
|
||||
instrument_cache_write_pop_frame(cw);
|
||||
|
||||
if (n == 0) {
|
||||
|
@ -105,18 +105,13 @@ static void instrument_persitent_save_regs(GumArm64Writer * cw,
|
||||
offsetof(persistent_ctx_t, rflags));
|
||||
|
||||
/* Q */
|
||||
for (int i = 0; i < 16; i++) {
|
||||
|
||||
gum_arm64_writer_put_stp_reg_reg_reg_offset(
|
||||
cw, ARM64_REG_Q0, ARM64_REG_Q1, ARM64_REG_X0,
|
||||
offsetof(GumCpuContext, q[0]), GUM_INDEX_SIGNED_OFFSET);
|
||||
gum_arm64_writer_put_stp_reg_reg_reg_offset(
|
||||
cw, ARM64_REG_Q2, ARM64_REG_Q3, ARM64_REG_X0,
|
||||
offsetof(GumCpuContext, q[16]), GUM_INDEX_SIGNED_OFFSET);
|
||||
gum_arm64_writer_put_stp_reg_reg_reg_offset(
|
||||
cw, ARM64_REG_Q4, ARM64_REG_Q5, ARM64_REG_X0,
|
||||
offsetof(GumCpuContext, q[32]), GUM_INDEX_SIGNED_OFFSET);
|
||||
gum_arm64_writer_put_stp_reg_reg_reg_offset(
|
||||
cw, ARM64_REG_Q6, ARM64_REG_Q7, ARM64_REG_X0,
|
||||
offsetof(GumCpuContext, q[48]), GUM_INDEX_SIGNED_OFFSET);
|
||||
cw, ARM64_REG_Q0 + (i * 2), ARM64_REG_Q0 + (i * 2) + 1, ARM64_REG_X0,
|
||||
offsetof(GumCpuContext, v[i]), GUM_INDEX_SIGNED_OFFSET);
|
||||
|
||||
}
|
||||
|
||||
/* x0 & x1 */
|
||||
gum_arm64_writer_put_ldp_reg_reg_reg_offset(cw, ARM64_REG_X2, ARM64_REG_X3,
|
||||
@ -201,18 +196,14 @@ static void instrument_persitent_restore_regs(GumArm64Writer * cw,
|
||||
offsetof(persistent_ctx_t, rflags));
|
||||
gum_arm64_writer_put_instruction(cw, msr_nzcv_x1);
|
||||
|
||||
/* Q */
|
||||
for (int i = 0; i < 16; i++) {
|
||||
|
||||
gum_arm64_writer_put_ldp_reg_reg_reg_offset(
|
||||
cw, ARM64_REG_Q0, ARM64_REG_Q1, ARM64_REG_X0,
|
||||
offsetof(GumCpuContext, q[0]), GUM_INDEX_SIGNED_OFFSET);
|
||||
gum_arm64_writer_put_ldp_reg_reg_reg_offset(
|
||||
cw, ARM64_REG_Q2, ARM64_REG_Q3, ARM64_REG_X0,
|
||||
offsetof(GumCpuContext, q[16]), GUM_INDEX_SIGNED_OFFSET);
|
||||
gum_arm64_writer_put_ldp_reg_reg_reg_offset(
|
||||
cw, ARM64_REG_Q4, ARM64_REG_Q5, ARM64_REG_X0,
|
||||
offsetof(GumCpuContext, q[32]), GUM_INDEX_SIGNED_OFFSET);
|
||||
gum_arm64_writer_put_ldp_reg_reg_reg_offset(
|
||||
cw, ARM64_REG_Q6, ARM64_REG_Q7, ARM64_REG_X0,
|
||||
offsetof(GumCpuContext, q[48]), GUM_INDEX_SIGNED_OFFSET);
|
||||
cw, ARM64_REG_Q0 + (i * 2), ARM64_REG_Q0 + (i * 2) + 1, ARM64_REG_X0,
|
||||
offsetof(GumCpuContext, v[i]), GUM_INDEX_SIGNED_OFFSET);
|
||||
|
||||
}
|
||||
|
||||
/* x2 & x3 */
|
||||
gum_arm64_writer_put_ldp_reg_reg_reg_offset(
|
||||
|
@ -29,74 +29,74 @@ static void instrument_persitent_save_regs(GumX86Writer * cw,
|
||||
persistent_ctx_t *regs) {
|
||||
|
||||
GumAddress regs_address = GUM_ADDRESS(regs);
|
||||
gum_x86_writer_put_lea_reg_reg_offset(cw, GUM_REG_RSP, GUM_REG_RSP,
|
||||
gum_x86_writer_put_lea_reg_reg_offset(cw, GUM_X86_RSP, GUM_X86_RSP,
|
||||
-(GUM_RED_ZONE_SIZE));
|
||||
|
||||
/* Should be pushing FPU here, but meh */
|
||||
gum_x86_writer_put_pushfx(cw);
|
||||
gum_x86_writer_put_push_reg(cw, GUM_REG_RAX);
|
||||
gum_x86_writer_put_push_reg(cw, GUM_X86_RAX);
|
||||
|
||||
gum_x86_writer_put_mov_reg_address(cw, GUM_REG_RAX, regs_address);
|
||||
gum_x86_writer_put_mov_reg_address(cw, GUM_X86_RAX, regs_address);
|
||||
|
||||
gum_x86_writer_put_mov_reg_offset_ptr_reg(
|
||||
cw, GUM_REG_RAX, offsetof(GumCpuContext, rbx), GUM_REG_RBX);
|
||||
cw, GUM_X86_RAX, offsetof(GumCpuContext, rbx), GUM_X86_RBX);
|
||||
gum_x86_writer_put_mov_reg_offset_ptr_reg(
|
||||
cw, GUM_REG_RAX, offsetof(GumCpuContext, rcx), GUM_REG_RCX);
|
||||
cw, GUM_X86_RAX, offsetof(GumCpuContext, rcx), GUM_X86_RCX);
|
||||
gum_x86_writer_put_mov_reg_offset_ptr_reg(
|
||||
cw, GUM_REG_RAX, offsetof(GumCpuContext, rdx), GUM_REG_RDX);
|
||||
cw, GUM_X86_RAX, offsetof(GumCpuContext, rdx), GUM_X86_RDX);
|
||||
gum_x86_writer_put_mov_reg_offset_ptr_reg(
|
||||
cw, GUM_REG_RAX, offsetof(GumCpuContext, rdi), GUM_REG_RDI);
|
||||
cw, GUM_X86_RAX, offsetof(GumCpuContext, rdi), GUM_X86_RDI);
|
||||
gum_x86_writer_put_mov_reg_offset_ptr_reg(
|
||||
cw, GUM_REG_RAX, offsetof(GumCpuContext, rsi), GUM_REG_RSI);
|
||||
cw, GUM_X86_RAX, offsetof(GumCpuContext, rsi), GUM_X86_RSI);
|
||||
gum_x86_writer_put_mov_reg_offset_ptr_reg(
|
||||
cw, GUM_REG_RAX, offsetof(GumCpuContext, rbp), GUM_REG_RBP);
|
||||
cw, GUM_X86_RAX, offsetof(GumCpuContext, rbp), GUM_X86_RBP);
|
||||
gum_x86_writer_put_mov_reg_offset_ptr_reg(
|
||||
cw, GUM_REG_RAX, offsetof(GumCpuContext, r8), GUM_REG_R8);
|
||||
cw, GUM_X86_RAX, offsetof(GumCpuContext, r8), GUM_X86_R8);
|
||||
gum_x86_writer_put_mov_reg_offset_ptr_reg(
|
||||
cw, GUM_REG_RAX, offsetof(GumCpuContext, r9), GUM_REG_R9);
|
||||
cw, GUM_X86_RAX, offsetof(GumCpuContext, r9), GUM_X86_R9);
|
||||
gum_x86_writer_put_mov_reg_offset_ptr_reg(
|
||||
cw, GUM_REG_RAX, offsetof(GumCpuContext, r10), GUM_REG_R10);
|
||||
cw, GUM_X86_RAX, offsetof(GumCpuContext, r10), GUM_X86_R10);
|
||||
gum_x86_writer_put_mov_reg_offset_ptr_reg(
|
||||
cw, GUM_REG_RAX, offsetof(GumCpuContext, r11), GUM_REG_R11);
|
||||
cw, GUM_X86_RAX, offsetof(GumCpuContext, r11), GUM_X86_R11);
|
||||
gum_x86_writer_put_mov_reg_offset_ptr_reg(
|
||||
cw, GUM_REG_RAX, offsetof(GumCpuContext, r12), GUM_REG_R12);
|
||||
cw, GUM_X86_RAX, offsetof(GumCpuContext, r12), GUM_X86_R12);
|
||||
gum_x86_writer_put_mov_reg_offset_ptr_reg(
|
||||
cw, GUM_REG_RAX, offsetof(GumCpuContext, r13), GUM_REG_R13);
|
||||
cw, GUM_X86_RAX, offsetof(GumCpuContext, r13), GUM_X86_R13);
|
||||
gum_x86_writer_put_mov_reg_offset_ptr_reg(
|
||||
cw, GUM_REG_RAX, offsetof(GumCpuContext, r14), GUM_REG_R14);
|
||||
cw, GUM_X86_RAX, offsetof(GumCpuContext, r14), GUM_X86_R14);
|
||||
gum_x86_writer_put_mov_reg_offset_ptr_reg(
|
||||
cw, GUM_REG_RAX, offsetof(GumCpuContext, r15), GUM_REG_R15);
|
||||
cw, GUM_X86_RAX, offsetof(GumCpuContext, r15), GUM_X86_R15);
|
||||
|
||||
/* Store RIP */
|
||||
gum_x86_writer_put_mov_reg_address(cw, GUM_REG_RBX,
|
||||
gum_x86_writer_put_mov_reg_address(cw, GUM_X86_RBX,
|
||||
GUM_ADDRESS(persistent_start));
|
||||
|
||||
gum_x86_writer_put_mov_reg_offset_ptr_reg(
|
||||
cw, GUM_REG_RAX, offsetof(GumCpuContext, rip), GUM_REG_RBX);
|
||||
cw, GUM_X86_RAX, offsetof(GumCpuContext, rip), GUM_X86_RBX);
|
||||
|
||||
/* Store adjusted RSP */
|
||||
gum_x86_writer_put_mov_reg_reg(cw, GUM_REG_RBX, GUM_REG_RSP);
|
||||
gum_x86_writer_put_mov_reg_reg(cw, GUM_X86_RBX, GUM_X86_RSP);
|
||||
|
||||
/* RED_ZONE + Saved flags, RAX, alignment */
|
||||
gum_x86_writer_put_add_reg_imm(cw, GUM_REG_RBX,
|
||||
gum_x86_writer_put_add_reg_imm(cw, GUM_X86_RBX,
|
||||
GUM_RED_ZONE_SIZE + (0x8 * 2));
|
||||
gum_x86_writer_put_mov_reg_offset_ptr_reg(
|
||||
cw, GUM_REG_RAX, offsetof(GumCpuContext, rsp), GUM_REG_RBX);
|
||||
cw, GUM_X86_RAX, offsetof(GumCpuContext, rsp), GUM_X86_RBX);
|
||||
|
||||
/* Save the flags */
|
||||
gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_REG_RBX, GUM_REG_RSP, 0x8);
|
||||
gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_X86_RBX, GUM_X86_RSP, 0x8);
|
||||
gum_x86_writer_put_mov_reg_offset_ptr_reg(
|
||||
cw, GUM_REG_RAX, offsetof(persistent_ctx_t, rflags), GUM_REG_RBX);
|
||||
cw, GUM_X86_RAX, offsetof(persistent_ctx_t, rflags), GUM_X86_RBX);
|
||||
|
||||
/* Save the RAX */
|
||||
gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_REG_RBX, GUM_REG_RSP, 0x0);
|
||||
gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_X86_RBX, GUM_X86_RSP, 0x0);
|
||||
gum_x86_writer_put_mov_reg_offset_ptr_reg(
|
||||
cw, GUM_REG_RAX, offsetof(GumCpuContext, rax), GUM_REG_RBX);
|
||||
cw, GUM_X86_RAX, offsetof(GumCpuContext, rax), GUM_X86_RBX);
|
||||
|
||||
/* Pop the saved values */
|
||||
gum_x86_writer_put_lea_reg_reg_offset(cw, GUM_REG_RSP, GUM_REG_RSP, 0x10);
|
||||
gum_x86_writer_put_lea_reg_reg_offset(cw, GUM_X86_RSP, GUM_X86_RSP, 0x10);
|
||||
|
||||
gum_x86_writer_put_lea_reg_reg_offset(cw, GUM_REG_RSP, GUM_REG_RSP,
|
||||
gum_x86_writer_put_lea_reg_reg_offset(cw, GUM_X86_RSP, GUM_X86_RSP,
|
||||
(GUM_RED_ZONE_SIZE));
|
||||
|
||||
}
|
||||
@ -105,68 +105,68 @@ static void instrument_persitent_restore_regs(GumX86Writer * cw,
|
||||
persistent_ctx_t *regs) {
|
||||
|
||||
GumAddress regs_address = GUM_ADDRESS(regs);
|
||||
gum_x86_writer_put_mov_reg_address(cw, GUM_REG_RAX, regs_address);
|
||||
gum_x86_writer_put_mov_reg_address(cw, GUM_X86_RAX, regs_address);
|
||||
|
||||
gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_REG_RCX, GUM_REG_RAX,
|
||||
gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_X86_RCX, GUM_X86_RAX,
|
||||
offsetof(GumCpuContext, rcx));
|
||||
gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_REG_RDX, GUM_REG_RAX,
|
||||
gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_X86_RDX, GUM_X86_RAX,
|
||||
offsetof(GumCpuContext, rdx));
|
||||
gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_REG_RDI, GUM_REG_RAX,
|
||||
gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_X86_RDI, GUM_X86_RAX,
|
||||
offsetof(GumCpuContext, rdi));
|
||||
gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_REG_RSI, GUM_REG_RAX,
|
||||
gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_X86_RSI, GUM_X86_RAX,
|
||||
offsetof(GumCpuContext, rsi));
|
||||
gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_REG_RBP, GUM_REG_RAX,
|
||||
gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_X86_RBP, GUM_X86_RAX,
|
||||
offsetof(GumCpuContext, rbp));
|
||||
gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_REG_R8, GUM_REG_RAX,
|
||||
gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_X86_R8, GUM_X86_RAX,
|
||||
offsetof(GumCpuContext, r8));
|
||||
gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_REG_R9, GUM_REG_RAX,
|
||||
gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_X86_R9, GUM_X86_RAX,
|
||||
offsetof(GumCpuContext, r9));
|
||||
gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_REG_R10, GUM_REG_RAX,
|
||||
gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_X86_R10, GUM_X86_RAX,
|
||||
offsetof(GumCpuContext, r10));
|
||||
gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_REG_R11, GUM_REG_RAX,
|
||||
gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_X86_R11, GUM_X86_RAX,
|
||||
offsetof(GumCpuContext, r11));
|
||||
gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_REG_R12, GUM_REG_RAX,
|
||||
gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_X86_R12, GUM_X86_RAX,
|
||||
offsetof(GumCpuContext, r12));
|
||||
gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_REG_R13, GUM_REG_RAX,
|
||||
gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_X86_R13, GUM_X86_RAX,
|
||||
offsetof(GumCpuContext, r13));
|
||||
gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_REG_R14, GUM_REG_RAX,
|
||||
gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_X86_R14, GUM_X86_RAX,
|
||||
offsetof(GumCpuContext, r14));
|
||||
gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_REG_R15, GUM_REG_RAX,
|
||||
gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_X86_R15, GUM_X86_RAX,
|
||||
offsetof(GumCpuContext, r15));
|
||||
|
||||
/* Don't restore RIP */
|
||||
gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_REG_RSP, GUM_REG_RAX,
|
||||
gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_X86_RSP, GUM_X86_RAX,
|
||||
offsetof(GumCpuContext, rsp));
|
||||
|
||||
/* Restore RBX, RAX & Flags */
|
||||
gum_x86_writer_put_lea_reg_reg_offset(cw, GUM_REG_RSP, GUM_REG_RSP,
|
||||
gum_x86_writer_put_lea_reg_reg_offset(cw, GUM_X86_RSP, GUM_X86_RSP,
|
||||
-(GUM_RED_ZONE_SIZE));
|
||||
|
||||
gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_REG_RBX, GUM_REG_RAX,
|
||||
gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_X86_RBX, GUM_X86_RAX,
|
||||
offsetof(GumCpuContext, rbx));
|
||||
gum_x86_writer_put_push_reg(cw, GUM_REG_RBX);
|
||||
gum_x86_writer_put_push_reg(cw, GUM_X86_RBX);
|
||||
|
||||
gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_REG_RBX, GUM_REG_RAX,
|
||||
gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_X86_RBX, GUM_X86_RAX,
|
||||
offsetof(GumCpuContext, rax));
|
||||
gum_x86_writer_put_push_reg(cw, GUM_REG_RBX);
|
||||
gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_REG_RBX, GUM_REG_RAX,
|
||||
gum_x86_writer_put_push_reg(cw, GUM_X86_RBX);
|
||||
gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_X86_RBX, GUM_X86_RAX,
|
||||
offsetof(persistent_ctx_t, rflags));
|
||||
gum_x86_writer_put_push_reg(cw, GUM_REG_RBX);
|
||||
gum_x86_writer_put_push_reg(cw, GUM_X86_RBX);
|
||||
|
||||
gum_x86_writer_put_popfx(cw);
|
||||
gum_x86_writer_put_pop_reg(cw, GUM_REG_RAX);
|
||||
gum_x86_writer_put_pop_reg(cw, GUM_REG_RBX);
|
||||
gum_x86_writer_put_pop_reg(cw, GUM_X86_RAX);
|
||||
gum_x86_writer_put_pop_reg(cw, GUM_X86_RBX);
|
||||
|
||||
gum_x86_writer_put_lea_reg_reg_offset(cw, GUM_REG_RSP, GUM_REG_RSP,
|
||||
gum_x86_writer_put_lea_reg_reg_offset(cw, GUM_X86_RSP, GUM_X86_RSP,
|
||||
(GUM_RED_ZONE_SIZE));
|
||||
|
||||
}
|
||||
|
||||
static void instrument_exit(GumX86Writer *cw) {
|
||||
|
||||
gum_x86_writer_put_mov_reg_address(cw, GUM_REG_RAX, GUM_ADDRESS(_exit));
|
||||
gum_x86_writer_put_mov_reg_u32(cw, GUM_REG_RDI, 0);
|
||||
gum_x86_writer_put_call_reg(cw, GUM_REG_RAX);
|
||||
gum_x86_writer_put_mov_reg_address(cw, GUM_X86_RAX, GUM_ADDRESS(_exit));
|
||||
gum_x86_writer_put_mov_reg_u32(cw, GUM_X86_RDI, 0);
|
||||
gum_x86_writer_put_call_reg(cw, GUM_X86_RAX);
|
||||
|
||||
}
|
||||
|
||||
@ -186,13 +186,13 @@ static int instrument_afl_persistent_loop_func(void) {
|
||||
|
||||
static void instrument_afl_persistent_loop(GumX86Writer *cw) {
|
||||
|
||||
gum_x86_writer_put_lea_reg_reg_offset(cw, GUM_REG_RSP, GUM_REG_RSP,
|
||||
gum_x86_writer_put_lea_reg_reg_offset(cw, GUM_X86_RSP, GUM_X86_RSP,
|
||||
-(GUM_RED_ZONE_SIZE));
|
||||
gum_x86_writer_put_call_address_with_arguments(
|
||||
cw, GUM_CALL_CAPI, GUM_ADDRESS(instrument_afl_persistent_loop_func), 0);
|
||||
gum_x86_writer_put_test_reg_reg(cw, GUM_REG_RAX, GUM_REG_RAX);
|
||||
gum_x86_writer_put_test_reg_reg(cw, GUM_X86_RAX, GUM_X86_RAX);
|
||||
|
||||
gum_x86_writer_put_lea_reg_reg_offset(cw, GUM_REG_RSP, GUM_REG_RSP,
|
||||
gum_x86_writer_put_lea_reg_reg_offset(cw, GUM_X86_RSP, GUM_X86_RSP,
|
||||
(GUM_RED_ZONE_SIZE));
|
||||
|
||||
}
|
||||
@ -200,26 +200,26 @@ static void instrument_afl_persistent_loop(GumX86Writer *cw) {
|
||||
static void persistent_prologue_hook(GumX86Writer *cw, persistent_ctx_t *regs) {
|
||||
|
||||
if (persistent_hook == NULL) return;
|
||||
gum_x86_writer_put_lea_reg_reg_offset(cw, GUM_REG_RSP, GUM_REG_RSP,
|
||||
gum_x86_writer_put_lea_reg_reg_offset(cw, GUM_X86_RSP, GUM_X86_RSP,
|
||||
-(GUM_RED_ZONE_SIZE));
|
||||
|
||||
gum_x86_writer_put_mov_reg_address(cw, GUM_REG_RDX,
|
||||
gum_x86_writer_put_mov_reg_address(cw, GUM_X86_RDX,
|
||||
GUM_ADDRESS(&__afl_fuzz_len));
|
||||
gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_REG_RDX, GUM_REG_RDX, 0);
|
||||
gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_REG_RDX, GUM_REG_RDX, 0);
|
||||
gum_x86_writer_put_mov_reg_u64(cw, GUM_REG_RDI, 0xffffffff);
|
||||
gum_x86_writer_put_and_reg_reg(cw, GUM_REG_RDX, GUM_REG_RDI);
|
||||
gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_X86_RDX, GUM_X86_RDX, 0);
|
||||
gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_X86_RDX, GUM_X86_RDX, 0);
|
||||
gum_x86_writer_put_mov_reg_u64(cw, GUM_X86_RDI, 0xffffffff);
|
||||
gum_x86_writer_put_and_reg_reg(cw, GUM_X86_RDX, GUM_X86_RDI);
|
||||
|
||||
gum_x86_writer_put_mov_reg_address(cw, GUM_REG_RSI,
|
||||
gum_x86_writer_put_mov_reg_address(cw, GUM_X86_RSI,
|
||||
GUM_ADDRESS(&__afl_fuzz_ptr));
|
||||
gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_REG_RSI, GUM_REG_RSI, 0);
|
||||
gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_X86_RSI, GUM_X86_RSI, 0);
|
||||
|
||||
gum_x86_writer_put_call_address_with_arguments(
|
||||
cw, GUM_CALL_CAPI, GUM_ADDRESS(persistent_hook), 3, GUM_ARG_ADDRESS,
|
||||
GUM_ADDRESS(®s->ctx), GUM_ARG_REGISTER, GUM_REG_RSI, GUM_ARG_REGISTER,
|
||||
GUM_REG_RDX);
|
||||
GUM_ADDRESS(®s->ctx), GUM_ARG_REGISTER, GUM_X86_RSI, GUM_ARG_REGISTER,
|
||||
GUM_X86_RDX);
|
||||
|
||||
gum_x86_writer_put_lea_reg_reg_offset(cw, GUM_REG_RSP, GUM_REG_RSP,
|
||||
gum_x86_writer_put_lea_reg_reg_offset(cw, GUM_X86_RSP, GUM_X86_RSP,
|
||||
(GUM_RED_ZONE_SIZE));
|
||||
|
||||
}
|
||||
@ -228,23 +228,23 @@ static void instrument_persitent_save_ret(GumX86Writer *cw) {
|
||||
|
||||
/* Stack usage by this function */
|
||||
gssize offset = GUM_RED_ZONE_SIZE + (3 * 8);
|
||||
gum_x86_writer_put_lea_reg_reg_offset(cw, GUM_REG_RSP, GUM_REG_RSP,
|
||||
gum_x86_writer_put_lea_reg_reg_offset(cw, GUM_X86_RSP, GUM_X86_RSP,
|
||||
-(GUM_RED_ZONE_SIZE));
|
||||
|
||||
gum_x86_writer_put_pushfx(cw);
|
||||
gum_x86_writer_put_push_reg(cw, GUM_REG_RAX);
|
||||
gum_x86_writer_put_push_reg(cw, GUM_REG_RBX);
|
||||
gum_x86_writer_put_push_reg(cw, GUM_X86_RAX);
|
||||
gum_x86_writer_put_push_reg(cw, GUM_X86_RBX);
|
||||
|
||||
gum_x86_writer_put_mov_reg_address(cw, GUM_REG_RAX, GUM_ADDRESS(&saved_ret));
|
||||
gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_REG_RBX, GUM_REG_RSP,
|
||||
gum_x86_writer_put_mov_reg_address(cw, GUM_X86_RAX, GUM_ADDRESS(&saved_ret));
|
||||
gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_X86_RBX, GUM_X86_RSP,
|
||||
offset);
|
||||
gum_x86_writer_put_mov_reg_ptr_reg(cw, GUM_REG_RAX, GUM_REG_RBX);
|
||||
gum_x86_writer_put_mov_reg_ptr_reg(cw, GUM_X86_RAX, GUM_X86_RBX);
|
||||
|
||||
gum_x86_writer_put_pop_reg(cw, GUM_REG_RBX);
|
||||
gum_x86_writer_put_pop_reg(cw, GUM_REG_RAX);
|
||||
gum_x86_writer_put_pop_reg(cw, GUM_X86_RBX);
|
||||
gum_x86_writer_put_pop_reg(cw, GUM_X86_RAX);
|
||||
gum_x86_writer_put_popfx(cw);
|
||||
|
||||
gum_x86_writer_put_lea_reg_reg_offset(cw, GUM_REG_RSP, GUM_REG_RSP,
|
||||
gum_x86_writer_put_lea_reg_reg_offset(cw, GUM_X86_RSP, GUM_X86_RSP,
|
||||
(GUM_RED_ZONE_SIZE));
|
||||
|
||||
}
|
||||
@ -278,7 +278,7 @@ void persistent_prologue_arch(GumStalkerOutput *output) {
|
||||
FVERBOSE("Persistent loop reached");
|
||||
|
||||
/* Pop the return value */
|
||||
gum_x86_writer_put_lea_reg_reg_offset(cw, GUM_REG_RSP, GUM_REG_RSP, 8);
|
||||
gum_x86_writer_put_lea_reg_reg_offset(cw, GUM_X86_RSP, GUM_X86_RSP, 8);
|
||||
|
||||
instrument_persitent_save_regs(cw, &saved_regs);
|
||||
|
||||
@ -326,13 +326,13 @@ void persistent_epilogue_arch(GumStalkerOutput *output) {
|
||||
|
||||
/* The stack should be aligned when we re-enter our loop */
|
||||
gconstpointer zero = cw->code + 1;
|
||||
gum_x86_writer_put_test_reg_u32(cw, GUM_REG_RSP, 0xF);
|
||||
gum_x86_writer_put_test_reg_u32(cw, GUM_X86_RSP, 0xF);
|
||||
gum_x86_writer_put_jcc_near_label(cw, X86_INS_JE, zero, GUM_NO_HINT);
|
||||
gum_x86_writer_put_lea_reg_reg_offset(cw, GUM_REG_RSP, GUM_REG_RSP, -8);
|
||||
gum_x86_writer_put_lea_reg_reg_offset(cw, GUM_X86_RSP, GUM_X86_RSP, -8);
|
||||
gum_x86_writer_put_label(cw, zero);
|
||||
|
||||
gum_x86_writer_put_mov_reg_address(cw, GUM_REG_RAX, GUM_ADDRESS(&saved_ret));
|
||||
gum_x86_writer_put_jmp_reg_ptr(cw, GUM_REG_RAX);
|
||||
gum_x86_writer_put_mov_reg_address(cw, GUM_X86_RAX, GUM_ADDRESS(&saved_ret));
|
||||
gum_x86_writer_put_jmp_reg_ptr(cw, GUM_X86_RAX);
|
||||
|
||||
}
|
||||
|
||||
|
@ -32,50 +32,50 @@ static void instrument_persitent_save_regs(GumX86Writer * cw,
|
||||
|
||||
/* Should be pushing FPU here, but meh */
|
||||
gum_x86_writer_put_pushfx(cw);
|
||||
gum_x86_writer_put_push_reg(cw, GUM_REG_EAX);
|
||||
gum_x86_writer_put_push_reg(cw, GUM_X86_EAX);
|
||||
|
||||
gum_x86_writer_put_mov_reg_address(cw, GUM_REG_EAX, regs_address);
|
||||
gum_x86_writer_put_mov_reg_address(cw, GUM_X86_EAX, regs_address);
|
||||
|
||||
gum_x86_writer_put_mov_reg_offset_ptr_reg(
|
||||
cw, GUM_REG_EAX, offsetof(GumCpuContext, ebx), GUM_REG_EBX);
|
||||
cw, GUM_X86_EAX, offsetof(GumCpuContext, ebx), GUM_X86_EBX);
|
||||
gum_x86_writer_put_mov_reg_offset_ptr_reg(
|
||||
cw, GUM_REG_EAX, offsetof(GumCpuContext, ecx), GUM_REG_ECX);
|
||||
cw, GUM_X86_EAX, offsetof(GumCpuContext, ecx), GUM_X86_ECX);
|
||||
gum_x86_writer_put_mov_reg_offset_ptr_reg(
|
||||
cw, GUM_REG_EAX, offsetof(GumCpuContext, edx), GUM_REG_EDX);
|
||||
cw, GUM_X86_EAX, offsetof(GumCpuContext, edx), GUM_X86_EDX);
|
||||
gum_x86_writer_put_mov_reg_offset_ptr_reg(
|
||||
cw, GUM_REG_EAX, offsetof(GumCpuContext, edi), GUM_REG_EDI);
|
||||
cw, GUM_X86_EAX, offsetof(GumCpuContext, edi), GUM_X86_EDI);
|
||||
gum_x86_writer_put_mov_reg_offset_ptr_reg(
|
||||
cw, GUM_REG_EAX, offsetof(GumCpuContext, esi), GUM_REG_ESI);
|
||||
cw, GUM_X86_EAX, offsetof(GumCpuContext, esi), GUM_X86_ESI);
|
||||
gum_x86_writer_put_mov_reg_offset_ptr_reg(
|
||||
cw, GUM_REG_EAX, offsetof(GumCpuContext, ebp), GUM_REG_EBP);
|
||||
cw, GUM_X86_EAX, offsetof(GumCpuContext, ebp), GUM_X86_EBP);
|
||||
|
||||
/* Store RIP */
|
||||
gum_x86_writer_put_mov_reg_address(cw, GUM_REG_EBX,
|
||||
gum_x86_writer_put_mov_reg_address(cw, GUM_X86_EBX,
|
||||
GUM_ADDRESS(persistent_start));
|
||||
|
||||
gum_x86_writer_put_mov_reg_offset_ptr_reg(
|
||||
cw, GUM_REG_EAX, offsetof(GumCpuContext, eip), GUM_REG_EBX);
|
||||
cw, GUM_X86_EAX, offsetof(GumCpuContext, eip), GUM_X86_EBX);
|
||||
|
||||
/* Store adjusted RSP */
|
||||
gum_x86_writer_put_mov_reg_reg(cw, GUM_REG_EBX, GUM_REG_ESP);
|
||||
gum_x86_writer_put_mov_reg_reg(cw, GUM_X86_EBX, GUM_X86_ESP);
|
||||
|
||||
/* RED_ZONE + Saved flags, RAX */
|
||||
gum_x86_writer_put_add_reg_imm(cw, GUM_REG_EBX, (0x4 * 2));
|
||||
gum_x86_writer_put_add_reg_imm(cw, GUM_X86_EBX, (0x4 * 2));
|
||||
gum_x86_writer_put_mov_reg_offset_ptr_reg(
|
||||
cw, GUM_REG_EAX, offsetof(GumCpuContext, esp), GUM_REG_EBX);
|
||||
cw, GUM_X86_EAX, offsetof(GumCpuContext, esp), GUM_X86_EBX);
|
||||
|
||||
/* Save the flags */
|
||||
gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_REG_EBX, GUM_REG_ESP, 0x4);
|
||||
gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_X86_EBX, GUM_X86_ESP, 0x4);
|
||||
gum_x86_writer_put_mov_reg_offset_ptr_reg(
|
||||
cw, GUM_REG_EAX, offsetof(persistent_ctx_t, eflags), GUM_REG_EBX);
|
||||
cw, GUM_X86_EAX, offsetof(persistent_ctx_t, eflags), GUM_X86_EBX);
|
||||
|
||||
/* Save the RAX */
|
||||
gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_REG_EBX, GUM_REG_ESP, 0x0);
|
||||
gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_X86_EBX, GUM_X86_ESP, 0x0);
|
||||
gum_x86_writer_put_mov_reg_offset_ptr_reg(
|
||||
cw, GUM_REG_EAX, offsetof(GumCpuContext, eax), GUM_REG_EBX);
|
||||
cw, GUM_X86_EAX, offsetof(GumCpuContext, eax), GUM_X86_EBX);
|
||||
|
||||
/* Pop the saved values */
|
||||
gum_x86_writer_put_lea_reg_reg_offset(cw, GUM_REG_ESP, GUM_REG_ESP, 0x8);
|
||||
gum_x86_writer_put_lea_reg_reg_offset(cw, GUM_X86_ESP, GUM_X86_ESP, 0x8);
|
||||
|
||||
}
|
||||
|
||||
@ -83,47 +83,47 @@ static void instrument_persitent_restore_regs(GumX86Writer * cw,
|
||||
persistent_ctx_t *regs) {
|
||||
|
||||
GumAddress regs_address = GUM_ADDRESS(regs);
|
||||
gum_x86_writer_put_mov_reg_address(cw, GUM_REG_EAX, regs_address);
|
||||
gum_x86_writer_put_mov_reg_address(cw, GUM_X86_EAX, regs_address);
|
||||
|
||||
gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_REG_ECX, GUM_REG_EAX,
|
||||
gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_X86_ECX, GUM_X86_EAX,
|
||||
offsetof(GumCpuContext, ecx));
|
||||
gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_REG_EDX, GUM_REG_EAX,
|
||||
gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_X86_EDX, GUM_X86_EAX,
|
||||
offsetof(GumCpuContext, edx));
|
||||
gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_REG_EDI, GUM_REG_EAX,
|
||||
gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_X86_EDI, GUM_X86_EAX,
|
||||
offsetof(GumCpuContext, edi));
|
||||
gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_REG_ESI, GUM_REG_EAX,
|
||||
gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_X86_ESI, GUM_X86_EAX,
|
||||
offsetof(GumCpuContext, esi));
|
||||
gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_REG_EBP, GUM_REG_EAX,
|
||||
gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_X86_EBP, GUM_X86_EAX,
|
||||
offsetof(GumCpuContext, ebp));
|
||||
|
||||
/* Don't restore RIP */
|
||||
gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_REG_ESP, GUM_REG_EAX,
|
||||
gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_X86_ESP, GUM_X86_EAX,
|
||||
offsetof(GumCpuContext, esp));
|
||||
|
||||
/* Restore RBX, RAX & Flags */
|
||||
gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_REG_EBX, GUM_REG_EAX,
|
||||
gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_X86_EBX, GUM_X86_EAX,
|
||||
offsetof(GumCpuContext, ebx));
|
||||
gum_x86_writer_put_push_reg(cw, GUM_REG_EBX);
|
||||
gum_x86_writer_put_push_reg(cw, GUM_X86_EBX);
|
||||
|
||||
gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_REG_EBX, GUM_REG_EAX,
|
||||
gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_X86_EBX, GUM_X86_EAX,
|
||||
offsetof(GumCpuContext, eax));
|
||||
gum_x86_writer_put_push_reg(cw, GUM_REG_EBX);
|
||||
gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_REG_EBX, GUM_REG_EAX,
|
||||
gum_x86_writer_put_push_reg(cw, GUM_X86_EBX);
|
||||
gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_X86_EBX, GUM_X86_EAX,
|
||||
offsetof(persistent_ctx_t, eflags));
|
||||
gum_x86_writer_put_push_reg(cw, GUM_REG_EBX);
|
||||
gum_x86_writer_put_push_reg(cw, GUM_X86_EBX);
|
||||
|
||||
gum_x86_writer_put_popfx(cw);
|
||||
gum_x86_writer_put_pop_reg(cw, GUM_REG_EAX);
|
||||
gum_x86_writer_put_pop_reg(cw, GUM_REG_EBX);
|
||||
gum_x86_writer_put_pop_reg(cw, GUM_X86_EAX);
|
||||
gum_x86_writer_put_pop_reg(cw, GUM_X86_EBX);
|
||||
|
||||
}
|
||||
|
||||
static void instrument_exit(GumX86Writer *cw) {
|
||||
|
||||
gum_x86_writer_put_mov_reg_address(cw, GUM_REG_EAX, GUM_ADDRESS(_exit));
|
||||
gum_x86_writer_put_mov_reg_u32(cw, GUM_REG_EDI, 0);
|
||||
gum_x86_writer_put_push_reg(cw, GUM_REG_EDI);
|
||||
gum_x86_writer_put_call_reg(cw, GUM_REG_EAX);
|
||||
gum_x86_writer_put_mov_reg_address(cw, GUM_X86_EAX, GUM_ADDRESS(_exit));
|
||||
gum_x86_writer_put_mov_reg_u32(cw, GUM_X86_EDI, 0);
|
||||
gum_x86_writer_put_push_reg(cw, GUM_X86_EDI);
|
||||
gum_x86_writer_put_call_reg(cw, GUM_X86_EAX);
|
||||
|
||||
}
|
||||
|
||||
@ -145,7 +145,7 @@ static void instrument_afl_persistent_loop(GumX86Writer *cw) {
|
||||
|
||||
gum_x86_writer_put_call_address_with_arguments(
|
||||
cw, GUM_CALL_CAPI, GUM_ADDRESS(instrument_afl_persistent_loop_func), 0);
|
||||
gum_x86_writer_put_test_reg_reg(cw, GUM_REG_EAX, GUM_REG_EAX);
|
||||
gum_x86_writer_put_test_reg_reg(cw, GUM_X86_EAX, GUM_X86_EAX);
|
||||
|
||||
}
|
||||
|
||||
@ -153,20 +153,20 @@ static void persistent_prologue_hook(GumX86Writer *cw, persistent_ctx_t *regs) {
|
||||
|
||||
if (persistent_hook == NULL) return;
|
||||
|
||||
gum_x86_writer_put_mov_reg_address(cw, GUM_REG_ECX,
|
||||
gum_x86_writer_put_mov_reg_address(cw, GUM_X86_ECX,
|
||||
GUM_ADDRESS(&__afl_fuzz_len));
|
||||
gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_REG_ECX, GUM_REG_ECX, 0);
|
||||
gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_REG_ECX, GUM_REG_ECX, 0);
|
||||
gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_X86_ECX, GUM_X86_ECX, 0);
|
||||
gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_X86_ECX, GUM_X86_ECX, 0);
|
||||
|
||||
gum_x86_writer_put_mov_reg_address(cw, GUM_REG_EDX,
|
||||
gum_x86_writer_put_mov_reg_address(cw, GUM_X86_EDX,
|
||||
GUM_ADDRESS(&__afl_fuzz_ptr));
|
||||
gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_REG_EDX, GUM_REG_EDX, 0);
|
||||
gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_X86_EDX, GUM_X86_EDX, 0);
|
||||
|
||||
/* Base address is 64-bits (hence two zero arguments) */
|
||||
gum_x86_writer_put_call_address_with_arguments(
|
||||
cw, GUM_CALL_CAPI, GUM_ADDRESS(persistent_hook), 3, GUM_ARG_ADDRESS,
|
||||
GUM_ADDRESS(®s->ctx), GUM_ARG_REGISTER, GUM_REG_EDX, GUM_ARG_REGISTER,
|
||||
GUM_REG_ECX);
|
||||
GUM_ADDRESS(®s->ctx), GUM_ARG_REGISTER, GUM_X86_EDX, GUM_ARG_REGISTER,
|
||||
GUM_X86_ECX);
|
||||
|
||||
}
|
||||
|
||||
@ -176,16 +176,16 @@ static void instrument_persitent_save_ret(GumX86Writer *cw) {
|
||||
gssize offset = (3 * 4);
|
||||
|
||||
gum_x86_writer_put_pushfx(cw);
|
||||
gum_x86_writer_put_push_reg(cw, GUM_REG_EAX);
|
||||
gum_x86_writer_put_push_reg(cw, GUM_REG_EBX);
|
||||
gum_x86_writer_put_push_reg(cw, GUM_X86_EAX);
|
||||
gum_x86_writer_put_push_reg(cw, GUM_X86_EBX);
|
||||
|
||||
gum_x86_writer_put_mov_reg_address(cw, GUM_REG_EAX, GUM_ADDRESS(&saved_ret));
|
||||
gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_REG_EBX, GUM_REG_ESP,
|
||||
gum_x86_writer_put_mov_reg_address(cw, GUM_X86_EAX, GUM_ADDRESS(&saved_ret));
|
||||
gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_X86_EBX, GUM_X86_ESP,
|
||||
offset);
|
||||
gum_x86_writer_put_mov_reg_ptr_reg(cw, GUM_REG_EAX, GUM_REG_EBX);
|
||||
gum_x86_writer_put_mov_reg_ptr_reg(cw, GUM_X86_EAX, GUM_X86_EBX);
|
||||
|
||||
gum_x86_writer_put_pop_reg(cw, GUM_REG_EBX);
|
||||
gum_x86_writer_put_pop_reg(cw, GUM_REG_EAX);
|
||||
gum_x86_writer_put_pop_reg(cw, GUM_X86_EBX);
|
||||
gum_x86_writer_put_pop_reg(cw, GUM_X86_EAX);
|
||||
gum_x86_writer_put_popfx(cw);
|
||||
|
||||
}
|
||||
@ -219,7 +219,7 @@ void persistent_prologue_arch(GumStalkerOutput *output) {
|
||||
FVERBOSE("Persistent loop reached");
|
||||
|
||||
/* Pop the return value */
|
||||
gum_x86_writer_put_lea_reg_reg_offset(cw, GUM_REG_ESP, GUM_REG_ESP, 4);
|
||||
gum_x86_writer_put_lea_reg_reg_offset(cw, GUM_X86_ESP, GUM_X86_ESP, 4);
|
||||
|
||||
instrument_persitent_save_regs(cw, &saved_regs);
|
||||
|
||||
@ -263,8 +263,8 @@ void persistent_epilogue_arch(GumStalkerOutput *output) {
|
||||
|
||||
if (persistent_debug) { gum_x86_writer_put_breakpoint(cw); }
|
||||
|
||||
gum_x86_writer_put_mov_reg_address(cw, GUM_REG_EAX, GUM_ADDRESS(&saved_ret));
|
||||
gum_x86_writer_put_jmp_reg_ptr(cw, GUM_REG_EAX);
|
||||
gum_x86_writer_put_mov_reg_address(cw, GUM_X86_EAX, GUM_ADDRESS(&saved_ret));
|
||||
gum_x86_writer_put_jmp_reg_ptr(cw, GUM_X86_EAX);
|
||||
|
||||
}
|
||||
|
||||
|
@ -26,7 +26,7 @@
|
||||
/* Version string: */
|
||||
|
||||
// c = release, a = volatile github dev, e = experimental branch
|
||||
#define VERSION "++4.01c"
|
||||
#define VERSION "++4.02a"
|
||||
|
||||
/******************************************************
|
||||
* *
|
||||
|
@ -100,3 +100,10 @@ See
|
||||
|
||||
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).
|
||||
|
||||
## 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.
|
||||
|
||||
|
@ -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)
|
||||
|
404
instrumentation/afl-gcc-cmplog-pass.so.cc
Normal file
404
instrumentation/afl-gcc-cmplog-pass.so.cc
Normal file
@ -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;
|
||||
|
||||
}
|
||||
|
366
instrumentation/afl-gcc-cmptrs-pass.so.cc
Normal file
366
instrumentation/afl-gcc-cmptrs-pass.so.cc
Normal file
@ -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;
|
||||
|
||||
}
|
||||
|
500
instrumentation/afl-gcc-common.h
Normal file
500
instrumentation/afl-gcc-common.h
Normal file
@ -0,0 +1,500 @@
|
||||
/* 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;
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
@ -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,9 +246,15 @@ struct afl_pass : gimple_opt_pass {
|
||||
gimple_build_assign(ntry, POINTER_PLUS_EXPR, map_ptr, indx);
|
||||
gimple_seq_add_stmt(&seq, idx_map);
|
||||
|
||||
/* Prepare to add constant 1 to it. */
|
||||
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");
|
||||
|
||||
@ -307,11 +262,6 @@ struct afl_pass : gimple_opt_pass {
|
||||
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));
|
||||
|
||||
if (neverZero) {
|
||||
|
||||
/* NeverZero: if count wrapped around to zero, advance to
|
||||
one. */
|
||||
if (blocks == 0) {
|
||||
@ -348,8 +298,6 @@ 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);
|
||||
@ -358,6 +306,17 @@ struct afl_pass : gimple_opt_pass {
|
||||
auto store_cntr = gimple_build_assign(unshare_expr(memref), cntr);
|
||||
gimple_seq_add_stmt(&seq, store_cntr);
|
||||
|
||||
} else {
|
||||
|
||||
/* 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 =
|
||||
gimple_build_assign(ploc, build_int_cst(TREE_TYPE(ploc), bid >> 1));
|
||||
@ -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\
|
||||
|
@ -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,8 @@ bool SplitComparesTransform::splitCompare(CmpInst *cmp_inst, Module &M,
|
||||
if (!intTyOp0) {
|
||||
|
||||
// not an integer type
|
||||
if (!be_quiet)
|
||||
fprintf(stderr, "Error: split-compare: not an integer type\n");
|
||||
return false;
|
||||
|
||||
}
|
||||
@ -675,6 +686,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 +704,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 +747,8 @@ bool SplitComparesTransform::splitCompare(CmpInst *cmp_inst, Module &M,
|
||||
}
|
||||
|
||||
default:
|
||||
if (!be_quiet)
|
||||
fprintf(stderr, "Error: split-compare: should not happen\n");
|
||||
return false;
|
||||
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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!"
|
||||
|
||||
|
@ -3886,11 +3886,7 @@ static void internal_malloc_stats(mstate m) {
|
||||
mark_smallmap(M, I); \
|
||||
else if (RTCHECK(ok_address(M, B->fd))) \
|
||||
F = B->fd; \
|
||||
else { \
|
||||
\
|
||||
CORRUPTION_ERROR_ACTION(M); \
|
||||
\
|
||||
} \
|
||||
else { CORRUPTION_ERROR_ACTION(M); } \
|
||||
B->fd = P; \
|
||||
F->bk = P; \
|
||||
P->fd = F; \
|
||||
@ -4104,11 +4100,7 @@ static void internal_malloc_stats(mstate m) {
|
||||
} \
|
||||
if (RTCHECK(ok_address(M, RP))) \
|
||||
*RP = 0; \
|
||||
else { \
|
||||
\
|
||||
CORRUPTION_ERROR_ACTION(M); \
|
||||
\
|
||||
} \
|
||||
else { CORRUPTION_ERROR_ACTION(M); } \
|
||||
\
|
||||
} \
|
||||
\
|
||||
@ -5598,8 +5590,9 @@ static void *internal_memalign(mstate m, size_t alignment, size_t bytes) {
|
||||
We've allocated enough total room so that this is always
|
||||
possible.
|
||||
*/
|
||||
char * br = (char *)mem2chunk((size_t)(
|
||||
((size_t)((char *)mem + alignment - SIZE_T_ONE)) & -alignment));
|
||||
char *br = (char *)mem2chunk(
|
||||
(size_t)(((size_t)((char *)mem + alignment - SIZE_T_ONE)) &
|
||||
-alignment));
|
||||
char *pos = ((size_t)(br - (char *)(p)) >= MIN_CHUNK_SIZE)
|
||||
? br
|
||||
: br + alignment;
|
||||
|
@ -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);
|
||||
|
||||
|
24
src/afl-cc.c
24
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);
|
||||
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]);
|
||||
|
@ -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;
|
||||
|
@ -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]);
|
||||
|
||||
}
|
||||
|
||||
|
@ -785,6 +785,8 @@ u32 execute_testcases(u8 *dir) {
|
||||
ck_free(in_data);
|
||||
++done;
|
||||
|
||||
if (child_crashed && debug) { WARNF("crashed: %s", fn2); }
|
||||
|
||||
if (collect_coverage)
|
||||
analyze_results(fsrv);
|
||||
else
|
||||
|
15
test-instr.c
15
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')
|
||||
switch (buf[0]) {
|
||||
|
||||
case '0':
|
||||
printf("Looks like a zero to me!\n");
|
||||
else if (buf[0] == '1')
|
||||
break;
|
||||
|
||||
case '1':
|
||||
printf("Pretty sure that is a one!\n");
|
||||
else
|
||||
break;
|
||||
|
||||
default:
|
||||
printf("Neither one or zero? How quaint!\n");
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
|
Reference in New Issue
Block a user