├── .dockerignore ├── .github └── workflows │ └── update-docs.yml ├── .gitignore ├── .gitmodules ├── Makefile ├── README.md ├── build-symcc-qsym.sh ├── build-symqemu.sh ├── docker ├── .gitignore ├── Dockerfile ├── Makefile └── kill.sh ├── docs ├── Makefile ├── conf.py ├── index.rst ├── install.md ├── microbenchmarks.md ├── requirements.txt └── usage.md ├── runner ├── executor.py ├── hybrid.py ├── limiter.py ├── path_tracer.py ├── queue_manager.py └── symfusion.py ├── sym_helpers ├── .gitignore ├── build.sh ├── cc_helper.c ├── cc_helper_template.h ├── fpu_helper.c ├── gen_sym_helpers.h ├── int_helper.c ├── ops_sse.h ├── qemu-isolate-build.h ├── sym_check_helpers.h ├── sym_helpers.h └── wrappers.c ├── tests ├── .gitkeepme ├── example │ ├── Makefile │ ├── example.c │ ├── inputs │ │ └── seed.dat │ └── start-rust-aflpp.sh └── microbenchmarks │ ├── .gitignore │ ├── 01-cpu-intensive-loop │ ├── Makefile │ ├── input.dat │ ├── lib.c │ └── main.c │ ├── 02-external-function │ ├── Makefile │ ├── input.dat │ ├── lib.c │ └── main.c │ ├── 03-x86-division-b │ ├── Makefile │ ├── input.dat │ ├── lib.c │ └── main.c │ ├── 03-x86-division │ ├── Makefile │ ├── input.dat │ ├── lib.c │ └── main.c │ ├── 04-ntohs │ ├── Makefile │ ├── input.dat │ ├── lib.c │ └── main.c │ ├── 05-strlen │ ├── Makefile │ ├── input.dat │ ├── input.dat.original │ ├── lib.c │ └── main.c │ ├── 06-cost-context-switch │ ├── Makefile │ ├── input.dat │ ├── lib.c │ └── main.c │ └── Makefile-stdin.inc └── utils ├── Makefile ├── afl-showmap.c ├── compare_bitmaps.c ├── comparer ├── compiler_runtime.cpp ├── compiler_symbolizer.cpp ├── qsym_solver.cpp └── time.sh /.dockerignore: -------------------------------------------------------------------------------- 1 | docs/ 2 | symcc-hybrid/build 3 | -------------------------------------------------------------------------------- /.github/workflows/update-docs.yml: -------------------------------------------------------------------------------- 1 | name: "Update documentation" 2 | on: 3 | push: 4 | paths: 5 | - 'docs/**' 6 | 7 | jobs: 8 | docs: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/checkout@v3 12 | with: 13 | token: ${{ github.token }} 14 | submodules: false 15 | - uses: ammaraskar/sphinx-action@master 16 | with: 17 | docs-folder: "docs/" 18 | - uses: actions/checkout@v2 19 | with: 20 | ref: gh-pages 21 | path: gh-pages 22 | submodules: false 23 | - name: Commit documentation changes 24 | run: | 25 | cp -r docs/_build/html/* gh-pages/ 26 | cd gh-pages 27 | touch .nojekyll 28 | git config --local user.email "action@github.com" 29 | git config --local user.name "GitHub Action" 30 | git add . 31 | git commit -m "Update documentation [ci skip]" -a || true 32 | # The above command will fail if no changes were present, so we ignore 33 | # that. 34 | - name: Push changes 35 | uses: ad-m/github-push-action@master 36 | with: 37 | branch: gh-pages 38 | directory: gh-pages 39 | github_token: ${{ secrets.GITHUB_TOKEN }} 40 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | symcc-klee 2 | runner/__pycache__/ 3 | 4 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "symqemu-hybrid"] 2 | path = symqemu-hybrid 3 | url = https://github.com/season-lab/symqemu-hybrid.git 4 | branch = hybrid_alt 5 | [submodule "symcc-hybrid"] 6 | path = symcc-hybrid 7 | url = https://github.com/season-lab/symcc-hybrid.git 8 | branch = hybrid 9 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | all: 2 | @echo "Targets: rebuild" 3 | 4 | rebuild: 5 | cd symcc-hybrid/build && ninja 6 | cd symqemu-hybrid && make -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SymFusion: Hybrid Instrumentation for Concolic Execution 2 | 3 | Please refer to the [documentation](https://season-lab.github.io/SymFusion/). 4 | -------------------------------------------------------------------------------- /build-symcc-qsym.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | cd symcc-hybrid 4 | rm -rf build 5 | mkdir build 2>/dev/null 6 | # RelWithDebInfo 7 | cd build && cmake -G Ninja \ 8 | -DCMAKE_BUILD_TYPE=Release \ 9 | -DQSYM_BACKEND=ON \ 10 | -DZ3_DIR=/mnt/ssd/symbolic/symfusion/z3/build/dist/lib/cmake/z3 \ 11 | .. && ninja 12 | 13 | exit 0 14 | -------------------------------------------------------------------------------- /build-symqemu.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | cd symqemu-hybrid 4 | CFLAGS="-march=ivybridge" ./configure \ 5 | --audio-drv-list= \ 6 | --disable-bluez \ 7 | --disable-sdl \ 8 | --disable-gtk \ 9 | --disable-vte \ 10 | --disable-opengl \ 11 | --disable-virglrenderer \ 12 | --disable-werror \ 13 | --target-list=x86_64-linux-user \ 14 | --enable-capstone=git \ 15 | --symcc-source=`pwd`/../symcc-hybrid/ \ 16 | --symcc-build=`pwd`/../symcc-hybrid/build && \ 17 | make clean && \ 18 | CFLAGS="-march=ivybridge" make -j `nproc` 19 | 20 | cd ../sym_helpers 21 | ./build.sh 22 | 23 | cd ../symqemu-hybrid 24 | CFLAGS="-march=ivybridge" ./configure \ 25 | --audio-drv-list= \ 26 | --disable-bluez \ 27 | --disable-sdl \ 28 | --disable-gtk \ 29 | --disable-vte \ 30 | --disable-opengl \ 31 | --disable-virglrenderer \ 32 | --disable-werror \ 33 | --target-list=x86_64-linux-user \ 34 | --enable-capstone=git \ 35 | --symcc-source=`pwd`/../symcc-hybrid/ \ 36 | --symcc-build=`pwd`/../symcc-hybrid/build && \ 37 | make clean && \ 38 | CFLAGS="-march=ivybridge" make -j `nproc` -------------------------------------------------------------------------------- /docker/.gitignore: -------------------------------------------------------------------------------- 1 | affinity.* 2 | exp-rust-no-symbolic-inputs 3 | -------------------------------------------------------------------------------- /docker/Dockerfile: -------------------------------------------------------------------------------- 1 | # This file is part of SymCC. 2 | # 3 | # SymCC is free software: you can redistribute it and/or modify it under the 4 | # terms of the GNU General Public License as published by the Free Software 5 | # Foundation, either version 3 of the License, or (at your option) any later 6 | # version. 7 | # 8 | # SymCC is distributed in the hope that it will be useful, but WITHOUT ANY 9 | # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 10 | # A PARTICULAR PURPOSE. See the GNU General Public License for more details. 11 | # 12 | # You should have received a copy of the GNU General Public License along with 13 | # SymCC. If not, see . 14 | 15 | # 16 | # The build stage 17 | # 18 | FROM ubuntu:20.04 AS builder 19 | 20 | RUN cp /etc/apt/sources.list /etc/apt/sources.list.bak 21 | RUN sed -Ei 's/^# deb-src /deb-src /' /etc/apt/sources.list 22 | 23 | # Install dependencies 24 | RUN apt-get update \ 25 | && DEBIAN_FRONTEND=noninteractive apt-get install -y \ 26 | cargo \ 27 | clang-10 \ 28 | clang \ 29 | cmake \ 30 | g++ \ 31 | git wget \ 32 | libz3-dev \ 33 | llvm-10-dev \ 34 | llvm-10-tools \ 35 | ninja-build \ 36 | python2 \ 37 | python3-pip \ 38 | zlib1g-dev \ 39 | ca-certificates \ 40 | && DEBIAN_FRONTEND=noninteractive apt build-dep -y qemu && update-ca-certificates \ 41 | && rm -rf /var/lib/apt/lists/* 42 | RUN pip3 install lit 43 | 44 | RUN ln -s /usr/bin/llvm-config-10 /usr/bin/llvm-config 45 | 46 | # Download the LLVM sources already so that we don't need to get them again when 47 | # SymCC changes 48 | RUN git clone -b llvmorg-10.0.1 --depth 1 https://github.com/llvm/llvm-project.git /llvm_source 49 | 50 | # Build new Z3 (old one has a regression) 51 | WORKDIR /z3 52 | RUN wget https://github.com/Z3Prover/z3/archive/refs/tags/z3-4.8.14.tar.gz 53 | RUN tar xvf z3-4.8.14.tar.gz 54 | RUN mkdir /symfusion 55 | RUN cp -a z3-z3-4.8.14 /symfusion/z3 56 | WORKDIR /symfusion/z3/build 57 | RUN cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=`pwd`/dist && make -j `nproc` && make install 58 | 59 | # Build original SymCC with the QSYM backend 60 | WORKDIR /symfusion/original/ 61 | RUN git clone https://github.com/eurecom-s3/symcc.git && cd symcc && git checkout 9b20609ada && git submodule init && git submodule update 62 | 63 | # DISABLE FUNCTIONS MODELS IN SYMCC 64 | # COPY ./utils/compiler_runtime.cpp /symfusion/original/symcc/compiler/Runtime.cpp 65 | # COPY ./utils/compiler_symbolizer.cpp /symfusion/original/symcc/compiler/Symbolizer.cpp 66 | 67 | # ADD FLAG TO SKIP QUERY 68 | COPY ./utils/qsym_solver.cpp /symfusion/original/symcc/runtime/qsym_backend/qsym/qsym/pintool/solver.cpp 69 | 70 | WORKDIR /symfusion/original/symcc/build 71 | RUN git diff 72 | RUN cmake -G Ninja \ 73 | -DQSYM_BACKEND=ON \ 74 | -DCMAKE_BUILD_TYPE=RelWithDebInfo \ 75 | -DZ3_DIR=/symfusion/z3/build/dist/lib/cmake/z3 \ 76 | -DLLVM_DIR=`llvm-config-10 --cmakedir` \ 77 | /symfusion/original/symcc/ \ 78 | && ninja 79 | 80 | # This does not work with AFL++ 81 | # RUN cargo install --path /symfusion/original/symcc/util/symcc_fuzzing_helper 82 | 83 | # Build original SymQEMU 84 | WORKDIR /symfusion/original 85 | RUN git clone https://github.com/eurecom-s3/symqemu.git 86 | WORKDIR /symfusion/original/symqemu 87 | RUN sed -Ei 's#https://git.qemu.org/git#https://gitlab.com/qemu-project#' .git/config 88 | RUN sed -Ei 's#https://git.qemu.org/git#https://gitlab.com/qemu-project#' .gitmodules 89 | RUN git checkout d183844 && ./configure \ 90 | --audio-drv-list= \ 91 | --disable-bluez \ 92 | --disable-sdl \ 93 | --disable-gtk \ 94 | --disable-vte \ 95 | --disable-opengl \ 96 | --disable-virglrenderer \ 97 | --disable-werror \ 98 | --target-list=x86_64-linux-user \ 99 | --enable-capstone=git \ 100 | --symcc-source=/symfusion/original/symcc/ \ 101 | --symcc-build=/symfusion/original/symcc/build && \ 102 | make -j `nproc` 103 | 104 | # Build libc++ with SymCC 105 | WORKDIR /symfusion/original/libcxx 106 | RUN export SYMCC_REGULAR_LIBCXX=yes SYMCC_NO_SYMBOLIC_INPUT=yes \ 107 | && mkdir /symfusion/original/libcxx_build \ 108 | && cd /symfusion/original/libcxx_build \ 109 | && cmake -G Ninja /llvm_source/llvm \ 110 | -DLLVM_ENABLE_PROJECTS="libcxx;libcxxabi" \ 111 | -DLLVM_TARGETS_TO_BUILD="X86" \ 112 | -DLLVM_DISTRIBUTION_COMPONENTS="cxx;cxxabi;cxx-headers" \ 113 | -DCMAKE_BUILD_TYPE=Release \ 114 | -DCMAKE_INSTALL_PREFIX=/symfusion/original/libcxx_install \ 115 | -DCMAKE_C_COMPILER=/symfusion/original/symcc/build/symcc \ 116 | -DCMAKE_CXX_COMPILER=/symfusion/original/symcc/build/sym++ \ 117 | && ninja distribution \ 118 | && ninja install-distribution 119 | 120 | # Init submodules if they are not initialiazed yet 121 | COPY ./symcc-hybrid /symfusion/symcc-hybrid 122 | COPY ./symqemu-hybrid /symfusion/symqemu-hybrid 123 | COPY ./.git /symfusion/.git 124 | 125 | WORKDIR /symfusion/symcc-hybrid 126 | RUN $(rm -r /symfusion/symcc-hybrid/build/ || echo "") 127 | 128 | # Build SymCC-hybrid with the QSYM backend 129 | WORKDIR /symfusion/symcc-hybrid/build 130 | RUN cmake -G Ninja \ 131 | -DQSYM_BACKEND=ON \ 132 | -DCMAKE_BUILD_TYPE=Release \ 133 | -DZ3_DIR=/symfusion/z3/build/dist/lib/cmake/z3 \ 134 | -DLLVM_DIR=`llvm-config-10 --cmakedir` \ 135 | /symfusion/symcc-hybrid/ \ 136 | && ninja 137 | RUN cargo install --path /symfusion/symcc-hybrid/util/symcc_fuzzing_helper 138 | 139 | # Build libc++ with symfusion 140 | WORKDIR /symfusion/libcxx 141 | RUN export SYMCC_REGULAR_LIBCXX=yes SYMCC_NO_SYMBOLIC_INPUT=yes \ 142 | && mkdir /symfusion/libcxx_build \ 143 | && cd /symfusion/libcxx_build \ 144 | && cmake -G Ninja /llvm_source/llvm \ 145 | -DLLVM_ENABLE_PROJECTS="libcxx;libcxxabi" \ 146 | -DLLVM_TARGETS_TO_BUILD="X86" \ 147 | -DLLVM_DISTRIBUTION_COMPONENTS="cxx;cxxabi;cxx-headers" \ 148 | -DCMAKE_BUILD_TYPE=Release \ 149 | -DCMAKE_INSTALL_PREFIX=/symfusion/libcxx_install \ 150 | -DCMAKE_C_COMPILER=/symfusion/symcc-hybrid/build/symcc \ 151 | -DCMAKE_CXX_COMPILER=/symfusion/symcc-hybrid/build/sym++ \ 152 | && ninja distribution \ 153 | && ninja install-distribution 154 | 155 | WORKDIR /symfusion/symqemu-hybrid 156 | RUN sed -Ei 's#https://git.qemu.org/git#https://gitlab.com/qemu-project#' .gitmodules 157 | RUN CFLAGS="-march=ivybridge" ./configure \ 158 | --audio-drv-list= \ 159 | --disable-bluez \ 160 | --disable-sdl \ 161 | --disable-gtk \ 162 | --disable-vte \ 163 | --disable-opengl \ 164 | --disable-virglrenderer \ 165 | --disable-werror \ 166 | --target-list=x86_64-linux-user \ 167 | --enable-capstone=git \ 168 | --symcc-source=/symfusion/symcc-hybrid/ \ 169 | --symcc-build=/symfusion/symcc-hybrid/build && \ 170 | make clean && \ 171 | CFLAGS="-march=ivybridge" make -j 172 | 173 | COPY ./sym_helpers /symfusion/sym_helpers 174 | WORKDIR /symfusion/sym_helpers 175 | RUN ./build.sh 176 | 177 | WORKDIR /symfusion/symqemu-hybrid 178 | RUN CFLAGS="-march=ivybridge" ./configure \ 179 | --audio-drv-list= \ 180 | --disable-bluez \ 181 | --disable-sdl \ 182 | --disable-gtk \ 183 | --disable-vte \ 184 | --disable-opengl \ 185 | --disable-virglrenderer \ 186 | --disable-werror \ 187 | --target-list=x86_64-linux-user \ 188 | --enable-capstone=git \ 189 | --symcc-source=/symfusion/symcc-hybrid/ \ 190 | --symcc-build=/symfusion/symcc-hybrid/build && \ 191 | make clean && \ 192 | CFLAGS="-march=ivybridge" make -j 193 | 194 | # bitmap comparer 195 | COPY ./utils/ /symfusion/utils 196 | WORKDIR /symfusion/utils 197 | RUN make comparer 198 | 199 | # 200 | # The final image 201 | # 202 | FROM ubuntu:20.04 203 | 204 | 205 | # NOTE: most of these dependencies are needed when building the 206 | # the real-world program used in our experiments... 207 | # we should remove them. 208 | # libtool-bin bison 209 | RUN apt-get update \ 210 | && DEBIAN_FRONTEND=noninteractive apt-get install -y \ 211 | build-essential \ 212 | clang-10 clang-10++ llvm-dev \ 213 | g++ \ 214 | libllvm10 \ 215 | zlib1g \ 216 | sudo \ 217 | nano \ 218 | libglib2.0-dev \ 219 | gdb \ 220 | wget \ 221 | curl \ 222 | time \ 223 | python3-pip ninja-build \ 224 | build-essential automake autoconf texinfo flex bison gcovr \ 225 | libtool libjpeg-dev libz-dev liblzma-dev \ 226 | libsqlite3-dev sqlite3 \ 227 | git make autoconf automake libtool bison re2c pkg-config libicu-dev \ 228 | git make autoconf automake libtool pkg-config cmake zlib1g-dev libjpeg-dev libopenjp2-7-dev libpng-dev libcairo2-dev libtiff-dev liblcms2-dev libboost-dev \ 229 | git make autoconf autogen automake build-essential libasound2-dev libflac-dev libogg-dev libtool libvorbis-dev libopus-dev libmp3lame-dev libmpg123-dev pkg-config python \ 230 | && rm -rf /var/lib/apt/lists/* 231 | 232 | RUN groupadd --gid 1008 ubuntu && \ 233 | useradd --uid 1008 --gid ubuntu --shell /bin/bash --create-home ubuntu && \ 234 | echo 'ubuntu ALL=(ALL) NOPASSWD:ALL' > /etc/sudoers.d/ubuntu 235 | 236 | # Build AFL++ 237 | RUN git clone https://github.com/AFLplusplus/AFLplusplus.git /afl && cd /afl && git checkout 3.14c 238 | # COPY ./AFLplusplus /afl 239 | WORKDIR /afl 240 | COPY --from=builder /symfusion/utils/afl-showmap.c src 241 | RUN make clean && make -j `nproc` 242 | RUN cd qemu_mode && ./build_qemu_support.sh 243 | # RUN cd custom_mutators/concolic && make clean && make 244 | 245 | RUN mkdir -p /symfusion/tests 246 | RUN chown -R ubuntu:ubuntu /symfusion/ 247 | USER ubuntu 248 | 249 | WORKDIR /home/ubuntu 250 | RUN wget -q https://raw.githubusercontent.com/hugsy/gef/main/scripts/gef.sh 251 | RUN chmod +x ./gef.sh 252 | RUN bash -c ./gef.sh 253 | 254 | RUN pip install psutil 255 | 256 | COPY --chown=ubuntu:ubuntu --from=builder /symfusion/symcc-hybrid /symfusion/symcc-hybrid/ 257 | COPY --chown=ubuntu:ubuntu --from=builder /symfusion/original/symcc/build /symfusion/original/symcc/build 258 | COPY --chown=ubuntu:ubuntu --from=builder /symfusion/symqemu-hybrid /symfusion/symqemu-hybrid 259 | COPY --chown=ubuntu:ubuntu --from=builder /symfusion/original/symqemu /symfusion/original/symqemu 260 | COPY --chown=ubuntu:ubuntu --from=builder /symfusion/sym_helpers /symfusion/sym_helpers 261 | COPY --chown=ubuntu:ubuntu --from=builder /root/.cargo/bin/symcc_fuzzing_helper /symfusion/symcc-hybrid/build/ 262 | COPY --chown=ubuntu:ubuntu --from=builder /symfusion/symcc-hybrid/util/pure_concolic_execution.sh /symfusion/symcc-hybrid/build/ 263 | COPY --chown=ubuntu:ubuntu --from=builder /symfusion/original/symcc/util/pure_concolic_execution.sh /symfusion/original/symcc/build/ 264 | COPY --chown=ubuntu:ubuntu ./runner /symfusion/runner 265 | COPY --chown=ubuntu:ubuntu --from=builder /symfusion/z3 /symfusion/z3 266 | COPY --chown=ubuntu:ubuntu --from=builder /symfusion/libcxx_install /symfusion/libcxx_install 267 | COPY --chown=ubuntu:ubuntu --from=builder /symfusion/original/libcxx_install /symfusion/original/libcxx_install 268 | COPY --chown=ubuntu:ubuntu --from=builder /symfusion/utils /symfusion/utils 269 | COPY --chown=ubuntu:ubuntu ./tests/microbenchmarks /symfusion/tests/microbenchmarks 270 | COPY --chown=ubuntu:ubuntu ./tests/example /symfusion/tests/example 271 | 272 | ENV PATH /symfusion/symcc-hybrid/build:$PATH 273 | ENV AFL_PATH /afl 274 | 275 | WORKDIR /symfusion/ 276 | -------------------------------------------------------------------------------- /docker/Makefile: -------------------------------------------------------------------------------- 1 | all: 2 | docker run -ti --cap-add SYS_ADMIN --rm \ 3 | -v `pwd`/../tests/:/symfusion/tests \ 4 | -w /symfusion \ 5 | --name symfusion-run-`date "+%y%m%d-%H%M"` \ 6 | ercoppa/symfusion bash 7 | 8 | build: 9 | docker build -t ercoppa/symfusion -f ./Dockerfile ../ 10 | 11 | clean: 12 | docker rmi $(docker images -f "dangling=true" -q) 13 | 14 | kill: 15 | docker kill `docker ps | grep "symfusion-" | awk '{ print $$1 }'` 16 | -------------------------------------------------------------------------------- /docker/kill.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | docker kill `docker ps | grep "symfusion-runner-s" | awk '{ print $1 }'` 4 | 5 | exit 0 6 | -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Minimal makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line, and also 5 | # from the environment for the first two. 6 | SPHINXOPTS ?= 7 | SPHINXBUILD ?= sphinx-build 8 | SOURCEDIR = . 9 | BUILDDIR = _build 10 | 11 | # Put it first so that "make" without argument is like "make help". 12 | help: 13 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 14 | 15 | .PHONY: help Makefile 16 | 17 | # Catch-all target: route all unknown targets to Sphinx using the new 18 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). 19 | %: Makefile 20 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 21 | -------------------------------------------------------------------------------- /docs/conf.py: -------------------------------------------------------------------------------- 1 | # Configuration file for the Sphinx documentation builder. 2 | # 3 | # This file only contains a selection of the most common options. For a full 4 | # list see the documentation: 5 | # https://www.sphinx-doc.org/en/master/usage/configuration.html 6 | 7 | # -- Path setup -------------------------------------------------------------- 8 | 9 | # If extensions (or modules to document with autodoc) are in another directory, 10 | # add these directories to sys.path here. If the directory is relative to the 11 | # documentation root, use os.path.abspath to make it absolute, like shown here. 12 | # 13 | # import os 14 | # import sys 15 | # sys.path.insert(0, os.path.abspath('.')) 16 | 17 | import sphinx_rtd_theme 18 | 19 | # -- Project information ----------------------------------------------------- 20 | 21 | project = 'SymFusion' 22 | copyright = '2022, Emilio Coppa' 23 | author = 'Emilio Coppa' 24 | 25 | 26 | # -- General configuration --------------------------------------------------- 27 | 28 | # Add any Sphinx extension module names here, as strings. They can be 29 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom 30 | # ones. 31 | extensions = [ 32 | "sphinx_rtd_theme", 33 | 'recommonmark', 34 | 'sphinx_markdown_tables', 35 | ] 36 | 37 | # Add any paths that contain templates here, relative to this directory. 38 | templates_path = ['_templates'] 39 | 40 | # List of patterns, relative to source directory, that match files and 41 | # directories to ignore when looking for source files. 42 | # This pattern also affects html_static_path and html_extra_path. 43 | exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] 44 | 45 | 46 | # -- Options for HTML output ------------------------------------------------- 47 | 48 | # The theme to use for HTML and HTML Help pages. See the documentation for 49 | # a list of builtin themes. 50 | # 51 | html_theme = "sphinx_rtd_theme" #'alabaster' 52 | 53 | # Add any paths that contain custom static files (such as style sheets) here, 54 | # relative to this directory. They are copied after the builtin static files, 55 | # so a file named "default.css" will overwrite the builtin "default.css". 56 | html_static_path = ['_static'] 57 | 58 | master_doc = 'index' 59 | 60 | source_suffix = { 61 | '.rst': 'restructuredtext', 62 | '.txt': 'markdown', 63 | '.md': 'markdown', 64 | } 65 | -------------------------------------------------------------------------------- /docs/index.rst: -------------------------------------------------------------------------------- 1 | .. Documentation of SymFusion 2 | 3 | SymFusion: Hybrid Instrumentation for Concolic Execution 4 | ================================================== 5 | 6 | .. raw:: html 7 | 8 | 9 | 10 | News 11 | ------------ 12 | 13 | * The preprint of the paper accepted at ASE 2022 is available: [`PDF `_]. 14 | * **The code of SymFusion has been released.** 15 | 16 | Publications 17 | ------------ 18 | 19 | * Emilio Coppa, Heng Yin, and Camil Demetrescu. SymFusion: Hybrid Instrumentation for Concolic Execution. Proceedings of the 37th IEEE/ACM International Conference on Automated Software Engineering (ASE 2022), 2022 `[PDF] `_ 20 | 21 | .. code-block:: latex 22 | 23 | @inproceedings{SYMFUSION-ASE22, 24 | author={Coppa, Emilio and Yin, Heng and Demetrescu, Camil}, 25 | title={{SymFusion: Hybrid Instrumentation for Concolic Execution}}, 26 | booktitle={Proceedings of the 37th IEEE/ACM International Conference on Automated Software Engineering}, 27 | series={ASE '22}, 28 | doi={10.1145/3551349.3556928}, 29 | year={2022}, 30 | } 31 | 32 | .. toctree:: 33 | :maxdepth: 2 34 | :caption: Getting Started 35 | 36 | install 37 | usage 38 | microbenchmarks 39 | 40 | .. toctree:: 41 | :maxdepth: 2 42 | :caption: Development 43 | 44 | 45 | Source code SymFusion 46 | 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /docs/install.md: -------------------------------------------------------------------------------- 1 | # Installation 2 | 3 | ## Docker container 4 | A prebuilt container image is available on Docker Hub. You can pull and launch it with: 5 | ``` 6 | $ docker run -ti --rm ercoppa/symfusion 7 | ``` 8 | 9 | ## Manual build 10 | SymFusion has been tested on Ubuntu 20.04 x86_64. A manual installation requires to build: 11 | * our fork of SymCC 12 | * our fork of SymQEMU (two times) 13 | * TCG Symbolic Helpers 14 | * SymCC Rust helper 15 | 16 | Download the source code: 17 | ``` 18 | $ git clone https://github.com/season-lab/SymFusion.git 19 | $ cd SymFusion 20 | $ git submodule init 21 | $ git submodule update --recursive 22 | $ cd symcc-hybrid 23 | $ git submodule init 24 | $ git submodule update --recursive 25 | $ cd .. 26 | ``` 27 | 28 | To build our forks of SymCC and SymQEMU, you have to satisfy their dependencies: 29 | - SymCC: e.g., on Ubuntu `sudo apt install git cargo clang-10 cmake g++ git libz3-dev llvm-10-dev llvm-10-tools ninja-build python2 python3-pip zlib1g-dev` 30 | - SymQEMU: e.g., on Ubuntu `sudo apt build-dep qemu` 31 | 32 | To build them you can use two scripts: 33 | ``` 34 | $ ./build-symcc-qsym.sh 35 | $ ./build-symqemu.sh 36 | ``` 37 | 38 | More detailed step-by-step instructions for building these components can be found inside the [`Dockerfile`](https://github.com/season-lab/SymFusion/blob/master/docker/Dockerfile). 39 | 40 | ## Tests 41 | 42 | To check whether SymFusion is running as expected, you can run the [microbenchmarks](/microbenchmarks). 43 | -------------------------------------------------------------------------------- /docs/microbenchmarks.md: -------------------------------------------------------------------------------- 1 | # Microbenchmarks 2 | 3 | We assume you are using our docker container image to run the microbenchmarks. E.g., started with: 4 | ``` 5 | docker run -ti --rm ercoppa/symfusion 6 | ``` 7 | If you are not running the docker container, you may have to fix a few paths in the `Makefile`s that we will use. 8 | 9 | Each microbenchmark is a different C program composed by `main.c` (internal code) and `lib.c` (external code). Look at `main.c` to get an idea of what the microbenchmark is doing. 10 | 11 | ## 01 - CPU Intensive Loop 12 | 13 | The goal of this program is to assess the analysis overhead of SymFusion compared to SymCC and SymQEMU when using a CPU intensive loop. 14 | 15 | ``` 16 | $ cd tests/microbenchmarks/01-cpu-intensive-loop 17 | $ make # to build the program for SymFusion/SymCC/SymQEMU 18 | $ make symfusion # run the program under SymFusion 19 | $ make symcc # run the program under SymFusion 20 | $ make symqemu # run the program under SymFusion 21 | ``` 22 | 23 | From the output of the three tools, you can see their running time. On our machine: 24 | - SymFusion: 940 ms 25 | - SymCC: 910 ms 26 | - SymQEMU: 6008 ms 27 | 28 | Hence, you can see that SymFusion is almost as fast as SymCC and way faster than SymQEMU. 29 | 30 | ## 02 - Data Flow via External Function 31 | 32 | The goal of this program is to validate that SymFusion can correctly track a data flow even when this flow is happening inside a library function that was not instrumented at compilation time. 33 | 34 | ``` 35 | $ cd tests/microbenchmarks/02-external-function 36 | $ make # to build the program for SymFusion/SymCC/SymQEMU 37 | $ make symfusion # run the program under SymFusion 38 | $ make symcc # run the program under SymFusion 39 | $ make symqemu # run the program under SymFusion 40 | ``` 41 | 42 | Expected results: 43 | - SymFusion is able to generate an input to satify the branch inside the program. Check it with: 44 | ``` 45 | $ LD_LIBRARY_PATH=`pwd` ./main.symqemu < out/symfusion-00000000/000000 46 | OK 47 | ``` 48 | where `OK` means that the branch was satisfied. `out` is the output directory used by SymFusion. We use the `main.symqemu` binary because it is binary without instrumentation (hence, no noise from SymCC or SymFusion). 49 | - SymCC does not generate any input able to satisfy the branch. It is missing the data flow via the external code. Check `out_symcc` to see that there no inputs. 50 | - SymQEMU is able to generate an input to satify the branch inside the program. Check it with: 51 | ``` 52 | $ LD_LIBRARY_PATH=`pwd` ./main.symqemu < out_symqemu/000000 53 | OK 54 | ``` 55 | where `out_symqemu` is the output directory used by SymQEMU. 56 | 57 | ## 03 - x86_64 Division in Internal Code 58 | 59 | The goal of this program is to validate whether SymFusion can handle an x84_64 division in the internal code. 60 | 61 | ``` 62 | $ cd tests/microbenchmarks/03-x86-division 63 | $ make # to build the program for SymFusion/SymCC/SymQEMU 64 | $ make symfusion # run the program under SymFusion 65 | $ make symcc # run the program under SymFusion 66 | $ make symqemu # run the program under SymFusion 67 | ``` 68 | 69 | Expected results: 70 | - SymFusion is able to generate an input to satify the branch inside the program. Check it with: 71 | ``` 72 | $ LD_LIBRARY_PATH=`pwd` ./main.symqemu < out/symfusion-00000000/000000 73 | OK 74 | ``` 75 | where `OK` means that the branch was satisfied. `out` is the output directory used by SymFusion. 76 | - SymCC is able to generate an input to satify the branch inside the program. Check it with: 77 | ``` 78 | $ LD_LIBRARY_PATH=`pwd` ./main.symqemu < out_symcc/000000 79 | OK 80 | ``` 81 | where `out_symcc` is the output directory used by SymCC. 82 | - SymQEMU does not generate any input able to satisfy the branch. Indeed, SymQEMU does not symbolically reason on the QEMU helpers used to handle the x86_64 division. Notice that SymQEMU generates one input in `out_symqemu` but this is not the correct one. 83 | 84 | ## 03b - x86_64 Division in External Code 85 | 86 | The goal of this program is to validate whether SymFusion can handle an x84_64 division in the external code. 87 | 88 | ``` 89 | $ cd tests/microbenchmarks/03-x86-division-b 90 | $ make # to build the program for SymFusion/SymCC/SymQEMU 91 | $ make symfusion # run the program under SymFusion 92 | $ make symcc # run the program under SymFusion 93 | $ make symqemu # run the program under SymFusion 94 | ``` 95 | 96 | Expected results: 97 | - SymFusion is able to generate an input to satify the branch inside the program. Check it with: 98 | ``` 99 | $ LD_LIBRARY_PATH=`pwd` ./main.symqemu < out/symfusion-00000000/000000 100 | OK 101 | ``` 102 | where `OK` means that the branch was satisfied. `out` is the output directory used by SymFusion. 103 | - SymCC does not generate any input able to satisfy the branch. Indeed, it is ignoring the symbolic operation in the external code. 104 | - SymQEMU does not generate any input able to satisfy the branch. Indeed, SymQEMU does not symbolically reason on the QEMU helpers used to handle the x86_64 division. Notice that SymQEMU generates one input in `out_symqemu` but this is not the correct one. 105 | 106 | ## 05 - ntohs 107 | 108 | The goal of this program is to validate whether SymFusion can handle `ntohs` from the standard C library. 109 | 110 | ``` 111 | $ cd tests/microbenchmarks/04-ntohs 112 | $ make # to build the program for SymFusion/SymCC/SymQEMU 113 | $ make symfusion # run the program under SymFusion 114 | $ make symcc # run the program under SymFusion 115 | $ make symqemu # run the program under SymFusion 116 | ``` 117 | 118 | Expected results: 119 | - SymFusion is able to generate an input to satify the branch inside the program. Check it with: 120 | ``` 121 | $ LD_LIBRARY_PATH=`pwd` ./main.symqemu < out/symfusion-00000000/000000 122 | OK 123 | ``` 124 | where `OK` means that the branch was satisfied. `out` is the output directory used by SymFusion. 125 | - SymCC does not generate any input able to satisfy the branch. `ntohs` is not modelled by SymCC and thus its effects are ignored. 126 | - SymQEMU is able to generate an input to satify the branch inside the program. Check it with: 127 | ``` 128 | $ LD_LIBRARY_PATH=`pwd` ./main.symqemu < out_symqemu/000000 129 | OK 130 | ``` 131 | where `out_symqemu` is the output directory used by SymQEMU. 132 | 133 | If you try to change `ntohs` with `ntohl` in `main.c` and then recompile the program and run again the tools, you can see that SymCC can generate a valid input since it has a model. The main is that SymFusion can track the code of functions even without models for them. 134 | 135 | ## 05 - strlen 136 | 137 | The goal of this program is to validate whether SymFusion can handle `strlen` from the standard C library. 138 | 139 | ``` 140 | $ cd tests/microbenchmarks/05-strlen 141 | $ make # to build the program for SymFusion/SymCC/SymQEMU 142 | $ make symfusion # run the program under SymFusion 143 | $ make symcc # run the program under SymFusion 144 | $ make symqemu # run the program under SymFusion 145 | ``` 146 | 147 | Expected results: 148 | - SymFusion is able to generate an input to satify the branch inside the program. Check it with: 149 | ``` 150 | $ LD_LIBRARY_PATH=`pwd` ./main.symqemu < out/symfusion-00000000/000000 151 | OK 152 | ``` 153 | where `OK` means that the branch was satisfied. `out` is the output directory used by SymFusion. However, this is possible thanks the use of a function model. If you disable the function model for `strlen` in SymFusion, it will accurately reason on `strlen`, however, the resulting expressions are too complex: the binary code is likely using vectorized instructions which require a concolic executor to handle symbolic addresses to solve them in some cases. SymFusion, similarly to SymCC and SymQEMU, concretizes symbolic addresses. 154 | - SymCC does not generate any input able to satisfy the branch. `strlen` is not modelled by SymCC and thus its effects are ignored. 155 | - SymQEMU does not generate any input able to satisfy the branch. Indeed, `strlen` is likely implemented with vectorized instructions that in QEMU are handled by helpers but SymQEMU ignores the effects of the QEMU helpers. 156 | 157 | ## 06 - Overhead of the Context Switch 158 | 159 | The goal of this program is to assess the cost of the context switch between the two execution modes (native mode and virtual mode) used by SymFusion. 160 | 161 | ``` 162 | $ cd tests/microbenchmarks/06-cost-context-switch 163 | $ make # to build the program for SymFusion/SymCC/SymQEMU 164 | $ make symfusion # run the program under SymFusion 165 | $ make symcc # run the program under SymFusion 166 | $ make symqemu # run the program under SymFusion 167 | ``` 168 | 169 | From the output of the three tools, you can see their running time. On our machine: 170 | - SymFusion: 114 ms 171 | - SymCC: 11 ms 172 | - SymQEMU: 84 ms 173 | 174 | We can that SymFusion is way slower than SymCC and even a bit slower than SymQEMU. This is due to large number of context switches (15000) performed by the program. -------------------------------------------------------------------------------- /docs/requirements.txt: -------------------------------------------------------------------------------- 1 | sphinx-rtd-theme 2 | recommonmark 3 | sphinx-markdown-tables 4 | -------------------------------------------------------------------------------- /docs/usage.md: -------------------------------------------------------------------------------- 1 | # Usage 2 | 3 | You can run SymFusion using: 4 | - a Python wrapper: quick way of running the tool without setting variables, creating directories, etc. 5 | - a Rust wrapper: this is an improved version of the wrapper proposed by SymCC. Faster than the Python wrapper but requires a few preliminary operations. We show how to use SymFusion in a hybrid fuzzing setup with AFL++. 6 | 7 | ## Concolic execution (Python wrapper) 8 | 9 | To run SymFusion in standalone mode, you need to execute the script `./runner/symfusion.py`. For instance: 10 | ``` 11 | $ ./runner/symfusion.py -o ./workdir -i ./seeds -- ./program [args] @@ 12 | ``` 13 | will run concolic execution on `./program`, passing (optional) arguments `args`, using initial inputs available in the directory `seeds`, generating the results in the directory `./workdir`. Similarly to AFL, since `@@` is specified for the program, then SymFusion will assume that the program is getting the input from a file stored on the filesystem (SymFusion will replace `@@` with the correct path at runtime). When `@@` is not used, SymFusion will assume that the input is obtained by reading from the standard input. The exploration will follow multiple paths, halting when no new interesting inputs can be generated anymore by SymFusion. 14 | 15 | Several other options can be set to enable additional features: 16 | * `-a, --afl AFL_WORKDIR`: this enables the AFL++ mode; 17 | * `-t, --timeout TIMEOUT`: maximum running time for each input (secs); 18 | * `-f, --fork-server`: run SymFusion with the fork server; 19 | * `-q, --queue-mode {symfusion, qsym, hash}`: how to assess whether a generated input is interesting and how to pick inputs from the queue. `symfusion` is using a custom edge tracer. `qsym` is using AFL++ `afl-showmap` (as done by QSYM) and you need to have an uninstrumented binary `.symqemu`. `hash` is keeping in the queue inputs that have a different hash. 20 | * `--keep-run-dirs`: intermediate run directories (`workdir/fuzzolic-XXXXX`), containing tracer/solver logs and generated testcases (before discarding uninteresting ones), will not be deleted when this option is set; 21 | * `-d, --debug {output, gdb}`: run SymFusion only on the first seed from the input directory. `output` will show you the full output of the run. `gdb` will execute SymFusion under GDB. 22 | 23 | The full list of fuzzolic options can be seen using `./runner/symfusion.py --help`. 24 | 25 | After (and during) an exploration, the workdir will typically contain the following files: 26 | * `symfusion-XXXXX/` (kept only when `--keep-run-dirs` is used): e.g., `{symfusion-00000, symfusion-00001, ...}` 27 | * `output.log`: standard output and standard error of the run 28 | * `id:XXXXXX`: e.g., `id:000000,src:seed`, this is the seed used for the run 29 | * `YYYYYY`: e.g., `000000`, a generated input 30 | * `queue/`: interesting test cases generated by SymFusion 31 | 32 | Hence, when looking for interesting test cases generated by SymFuson, check the directory `queue`. 33 | 34 | ### Example 35 | 36 | Let us consider the program [`tests/example/example.c`](https://github.com/season-lab/SymFusion/blob/master/tests/example/example.c#L21): 37 | ``` 38 | #include 39 | #include 40 | 41 | int magic_check(int p){ 42 | if (p == 0xDEADBEEF) 43 | return 1; 44 | else 45 | return 0; 46 | } 47 | 48 | int get_input(char* fname) { 49 | FILE* fp = fopen(fname, "r"); 50 | if (fp == NULL) exit(EXIT_FAILURE); 51 | int data; 52 | int r = fread(&data, 1, sizeof(data), fp); 53 | if (r != sizeof(data)) exit(EXIT_FAILURE); 54 | fclose(fp); 55 | return data; 56 | } 57 | 58 | int main(int argc, char* argv[]) { 59 | 60 | if (argc != 2) exit(EXIT_FAILURE); 61 | int input = get_input(argv[1]); // read four bytes from the input file 62 | if (magic_check(input)) { 63 | printf("Correct value [%x] :)\n", input); 64 | } else { 65 | printf("Wrong value [%x] :(\n", input); 66 | } 67 | 68 | return 0; 69 | } 70 | ``` 71 | Our goal is to automatically find the magic value `0xDEADBEEF` that is expected by the function `magic_check`. Since we do not know the magic value beforehand, we consider as an initial seed a file ([`tests/example/inputs/seed.dat`](https://github.com/season-lab/SymFusion/blob/master/tests/example/inputs/seed.dat)) containing just the `AAAA\n` characters. 72 | 73 | We build the programs in two versions (non instrumented and instrumented): 74 | ``` 75 | $ cd tests/example 76 | $ clang-10 -o example example.c # non instrument version 77 | $ ../../symcc-hybrid/build/symcc -o example.symfusion example.c # instrumented version 78 | ``` 79 | If we run the (non-instrumented) binary over the initial seed: 80 | ``` 81 | $ ./tests/example/example ./inputs/seed.dat 82 | ``` 83 | we should get the following output: 84 | ``` 85 | Wrong value [41414141] :( 86 | ``` 87 | 88 | Now, if we instead start the concolic exploration with SymFusion using the instrumented binary: 89 | ``` 90 | $ ./runner/symfusion.py -o ./out/ -i ./inputs/ -- ./example.symfusion @@ 91 | ``` 92 | The output should be similar to: 93 | ``` 94 | Done generating config. 95 | 96 | Evaluating seed inputs: 97 | Testcase seed.dat [1f1964918bb5b9a1, 27ad6cb4038e12a]: new_edges=[app=10, all=4181], unique_path=[app=True, all=True] 98 | Summary of testcases: duplicated=0 unique=1 tracer_time=0.06 99 | 100 | Picking input from queue Edges (application code): id:000000,src:seed [score=10, count=1, waiting=0] 101 | Working directory: workdir => symfusion-00000000 102 | Running... 103 | Completed (time=0.052) 104 | Testcase 000000 [3efe4a0512d16b03, 8eb61eab981997fd]: new_edges=[app=4, all=265], unique_path=[app=True, all=True] 105 | Summary of testcases: duplicated=0 unique=1 tracer_time=0.00 106 | Run took 0.06 secs [runner=0.05, tracer=0.00, tracer_run=0.00, tracer_comp=0.00, total_tracer=0.00, total_runner=0.05, avg_total_runner=0.052, all_time=0.06, pick_time=0.00] 107 | 108 | Picking input from queue Edges (application code): id:000001,src:id:000000 [score=4, count=2, waiting=0] 109 | total_run=0.06, total_pick=0.00, all_time=0.06 110 | Working directory: workdir => symfusion-00000001 111 | Running... 112 | Completed (time=0.055) 113 | Summary of testcases: duplicated=1 unique=0 tracer_time=0.00 114 | Run took 0.07 secs [runner=0.06, tracer=0.00, tracer_run=0.00, tracer_comp=0.00, total_tracer=0.01, total_runner=0.11, avg_total_runner=0.054, all_time=0.13, pick_time=0.00] 115 | total_run=0.13, total_pick=0.00, all_time=0.13 116 | 117 | [SymFusion] no more testcases. Finishing. 118 | ``` 119 | and we can see that the generated input is indeed accepted by the non-instrumented program: 120 | ``` 121 | $ ./example out/queue/id\:000001\,src\:id\:000000 122 | Correct value [deadbeef] :) 123 | ``` 124 | 125 | ## Hybrid fuzzing setup with AFL++ (Rust wrapper) 126 | 127 | We consider again the simple example from`./tests/example/`. We will have to perform several steps, we review each of them but they are all detailed in the script `./tests/example/start-rust-aflpp.sh`. 128 | 129 | To start, we build two versions of the program: 130 | ``` 131 | $ cd ./tests/example/ 132 | $ ../../symcc-hybrid/build/symcc -o example example.c # build for SymFusion 133 | $ /afl/afl-clang-fast example.c -o example.afl # build for AFL++ 134 | ``` 135 | 136 | Let us create an output directory: 137 | ``` 138 | $ export OUTPUT=`pwd`/out 139 | $ mkdir ${OUTPUT} 140 | ``` 141 | 142 | And set some environment variables 143 | ``` 144 | $ export HYBRID_CONF_FILE=$OUTPUT/hybrid.conf 145 | $ export LD_BIND_NOW=1 146 | $ export SYMFUSION_HYBRID=1 147 | $ export WRAPPER=`pwd`/../../symqemu-hybrid/x86_64-linux-user/symqemu-x86_64 148 | $ export SYMCC_ENABLE_LINEARIZATION=1 149 | $ export SEEDS=`pwd`/inputs # initial set of inputs for AFL++ 150 | ``` 151 | 152 | We now build the hybrid configuration: 153 | ``` 154 | $ ../../runner/symfusion.py -g ${HYBRID_CONF_FILE} -i ${SEEDS} -o ${OUTPUT}/concolic -- ./example.symfusion @@ 155 | $ rm -rf ${OUTPUT}/concolic 156 | ``` 157 | 158 | You can start AFL++ in background: 159 | ``` 160 | $ /afl/afl-fuzz -M afl-master -t 5000 -m 100M -i ${SEEDS} -o ${OUTPUT} -- ./example.afl @@ >/dev/null 2>&1 & 161 | ``` 162 | And then wait until AFL++ creates: 163 | - `${OUTPUT}/afl-master/fuzzer_stats` 164 | - `${OUTPUT}/afl-master/fuzz_bitmap` 165 | 166 | You can now start SymFusion with: 167 | ``` 168 | $ ../../symcc-hybrid/build/symcc_fuzzing_helper -a afl-master -o ${OUTPUT} -n concolic -- ${WRAPPER} ./example.symfusion @@ 169 | ``` 170 | Use CTRL+c to stop the concolic executor and AFL++. -------------------------------------------------------------------------------- /runner/executor.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | import os 4 | from posixpath import basename 5 | import sys 6 | import json 7 | import glob 8 | import filecmp 9 | import subprocess 10 | import time 11 | import signal 12 | import configparser 13 | import re 14 | import shutil 15 | import functools 16 | import tempfile 17 | import random 18 | import ctypes 19 | import resource 20 | import struct 21 | 22 | from queue_manager import QueueManager 23 | from limiter import setlimits, RUNNING_PROCESSES, SHUTDOWN 24 | import hybrid 25 | 26 | 27 | class Executor(object): 28 | 29 | def __init__(self, binary, input, output_dir, binary_args, 30 | debug=None, afl=None, timeout=None, input_fixed_name=None, 31 | keep_run_dirs=False, cache_dir=None, hybrid_mode=False, 32 | generate_hybrid_conf=None, forkserver=False, queue_mode=None): 33 | 34 | if not os.path.exists(binary): 35 | sys.exit('ERROR: invalid binary') 36 | self.binary = os.path.abspath(binary) 37 | 38 | self.binary_args = binary_args 39 | self.testcase_from_stdin = '@@' not in self.binary_args 40 | 41 | self.hybrid_mode = hybrid_mode 42 | self.hybrid_conf = os.path.abspath(output_dir) + "/hybrid.conf" if generate_hybrid_conf is None else generate_hybrid_conf 43 | if self.hybrid_mode: 44 | hybrid.build_conf(self.hybrid_conf, self.binary, 45 | os.path.abspath(output_dir) + "/hybrid.log" if generate_hybrid_conf is None else None) 46 | print("Done generating config.") 47 | 48 | if generate_hybrid_conf: 49 | sys.exit(0) 50 | 51 | if not os.path.exists(output_dir): 52 | sys.exit('ERROR: invalid working directory') 53 | self.output_dir = os.path.abspath(output_dir) 54 | 55 | if not os.path.exists(input): 56 | sys.exit('ERROR: invalid input') 57 | self.input = os.path.abspath(input) 58 | 59 | if afl: 60 | if not os.path.exists(afl): 61 | sys.exit('ERROR: invalid AFL workdir') 62 | self.afl = os.path.abspath(afl) 63 | else: 64 | self.afl = None 65 | 66 | self.afl_processed_testcases = set() 67 | self.afl_alt_processed_testcases = set() 68 | self.timeout_testcases = set() 69 | self.debug = debug 70 | self.tick_count = 0 71 | self.timeout = timeout 72 | self.input_fixed_name = input_fixed_name 73 | self.keep_run_dirs = keep_run_dirs 74 | self.work_dir = self.__get_root_dir() + "/workdir/" 75 | self.cex_cache_dir = cache_dir 76 | if self.cex_cache_dir is None: 77 | self.cex_cache_dir = self.__get_root_dir() + "/cache/" 78 | self.avoid_cache_dir = self.__get_root_dir() + "/avoid/" 79 | self.queue_manager = QueueManager( 80 | self.__get_root_dir(), self.binary, self.binary_args, 81 | self.hybrid_mode, self.hybrid_conf, self.afl, 82 | mode=queue_mode 83 | ) 84 | 85 | self.input_file = self.__get_work_dir() + "/input_file" 86 | 87 | self.forkserver = None 88 | if forkserver: 89 | print("Starting forkserver of the runner...") 90 | 91 | self.pipe_read_path = self.__get_root_dir() + "/pipe_read" 92 | self.pipe_write_path = self.__get_root_dir() + "/pipe_write" 93 | os.mkfifo(self.pipe_read_path) 94 | os.mkfifo(self.pipe_write_path) 95 | 96 | self.forkserver_file_done = self.__get_root_dir() + "/forkserver_child_status" 97 | self.forkserver_file_pid = self.__get_root_dir() + "/forkserver_child_pid" 98 | env = os.environ.copy() 99 | env['SYMFUSION_PATH_FORKSERVER_DONE'] = self.forkserver_file_done 100 | env['SYMFUSION_FORKSERVER_PIPE_WRITE'] = self.pipe_read_path 101 | env['SYMFUSION_FORKSERVER_PIPE_READ'] = self.pipe_write_path 102 | env['SYMFUSION_PATH_FORKSERVER_PID'] = self.forkserver_file_pid 103 | self.forkserver = True 104 | self.forkserver, _, _ = self.prepare_run(env, run_dir=self.__get_work_dir(), testcase=self.input_file) 105 | RUNNING_PROCESSES.append(self.forkserver) 106 | 107 | def __get_root_dir(self): 108 | if not os.path.exists(self.output_dir): 109 | os.system('mkdir ' + self.output_dir) 110 | return self.output_dir 111 | 112 | def __get_cex_cache_dir(self): 113 | if not os.path.exists(self.cex_cache_dir): 114 | os.system('mkdir ' + self.cex_cache_dir) 115 | return self.cex_cache_dir 116 | 117 | def __get_avoid_cache_dir(self): 118 | if not os.path.exists(self.avoid_cache_dir): 119 | os.system('mkdir ' + self.avoid_cache_dir) 120 | return self.avoid_cache_dir 121 | 122 | def __get_work_dir(self): 123 | if not os.path.exists(self.work_dir): 124 | os.system('mkdir ' + self.work_dir) 125 | return self.work_dir 126 | 127 | def __get_run_dir(self): 128 | root_dir = self.__get_root_dir() 129 | 130 | # status file 131 | status_file = root_dir + '/status' 132 | if not os.path.exists(status_file): 133 | os.system('echo 0 > ' + status_file) 134 | 135 | # get id for next run 136 | with open(status_file, 'r') as sf: 137 | run_id = sf.read() 138 | run_id = int(run_id) 139 | assert(run_id >= 0) 140 | 141 | # run dir 142 | run_dir = root_dir + '/symfusion-%08d' % run_id 143 | os.system('mkdir ' + run_dir) 144 | 145 | # increment id for the next run 146 | with open(status_file, 'w') as sf: 147 | sf.write(str(run_id + 1)) 148 | 149 | return run_dir, run_id 150 | 151 | def prepare_run(self, env, run_dir, testcase, output_log=None): 152 | 153 | # env['SYMFUSION_CEX_CACHE_DIR'] = self.__get_cex_cache_dir() 154 | # env['SYMFUSION_AVOID_CACHE_DIR'] = self.__get_avoid_cache_dir() 155 | env['SYMCC_AFL_COVERAGE_MAP'] = self.__get_root_dir() + "/map" 156 | env['SYMCC_OUTPUT_DIR'] = run_dir 157 | if not self.testcase_from_stdin: 158 | env['SYMCC_INPUT_FILE'] = testcase 159 | else: 160 | env['SYMCC_INPUT_STDIN_FILE'] = testcase 161 | 162 | p_args = [] 163 | if self.debug == "gdb": 164 | p_args.append("gdb") 165 | 166 | # p_args += ['perf', 'record', '--call-graph', 'dwarf'] 167 | 168 | args = self.binary_args[:] 169 | if self.hybrid_mode: 170 | p_args, args, env = hybrid.prepare( 171 | self.binary, self.binary_args, p_args, args, env, self.hybrid_conf) 172 | else: 173 | p_args += [self.binary] 174 | 175 | if not self.testcase_from_stdin: 176 | for k in range(len(args)): 177 | if args[k] == '@@': 178 | args[k] = testcase 179 | 180 | if self.debug != "gdb": 181 | p_args += args 182 | 183 | # print(p_args) 184 | 185 | start = time.time() 186 | p = subprocess.Popen( 187 | p_args, 188 | stdout=None if self.debug else (output_log if output_log is not None else subprocess.DEVNULL), 189 | stderr=None if self.debug else (output_log if output_log is not None else subprocess.DEVNULL), 190 | stdin=None, # subprocess.DEVNULL, 191 | cwd=run_dir, 192 | env=env, 193 | preexec_fn=setlimits, 194 | start_new_session=True, 195 | # bufsize=1 #if self.debug == 'gdb' else -1, 196 | # universal_newlines=True if self.debug == 'gdb_tracer' else False 197 | ) 198 | 199 | if self.forkserver: 200 | self.pipe_write = open(self.pipe_write_path, 'wb') 201 | self.pipe_read = open(self.pipe_read_path, 'rb') 202 | 203 | return p, start, args 204 | 205 | def fuzz_one(self, testcase): 206 | 207 | global RUNNING_PROCESSES 208 | self.__check_shutdown_flag() 209 | 210 | run_dir, run_id = self.__get_run_dir() 211 | print('Working directory: %s => %s' % (os.path.basename(self.__get_work_dir()[:-1]), os.path.basename(run_dir))) 212 | 213 | os.system("cp " + testcase + " " + self.input_file) 214 | 215 | if self.forkserver is None: 216 | env = os.environ.copy() 217 | output_log_name = run_dir + '/output.log' 218 | output_log = open(output_log_name, "w") 219 | p, start, args = self.prepare_run(env, run_dir=self.__get_work_dir(), testcase=self.input_file, output_log=output_log) 220 | RUNNING_PROCESSES.append(p) 221 | else: 222 | start = time.time() 223 | if os.path.exists(self.forkserver_file_done): 224 | os.unlink(self.forkserver_file_done) 225 | if os.path.exists(self.forkserver_file_pid): 226 | os.unlink(self.forkserver_file_pid) 227 | self.pipe_write.write(bytes([23])) 228 | self.pipe_write.flush() 229 | 230 | print("Running...") 231 | 232 | if self.debug == 'gdb': 233 | print("\nGDB COMMAND:\n\trun %s%s\n" % (' '.join(args), 234 | (" < " + testcase if self.testcase_from_stdin else ""))) 235 | 236 | if self.forkserver is None: 237 | # print("Waiting...") 238 | try: 239 | p.wait(self.timeout) 240 | except subprocess.TimeoutExpired: 241 | print('[SymFusion] Sending SIGINT to target binary.') 242 | p.send_signal(signal.SIGINT) 243 | try: 244 | p.wait(1) 245 | except subprocess.TimeoutExpired: 246 | print('[SymFusion] Sending SIGKILL to target binary.') 247 | p.send_signal(signal.SIGKILL) 248 | p.wait() 249 | if p in RUNNING_PROCESSES: 250 | RUNNING_PROCESSES.remove(p) 251 | if output_log: 252 | output_log.close() 253 | p_return_code = p.returncode 254 | else: 255 | wait_time = 0 256 | while not os.path.exists(self.forkserver_file_pid): 257 | time.sleep(0.0005) 258 | wait_time += 0.0005 259 | p_pid = None 260 | while p_pid is None: 261 | try: 262 | data = open(self.forkserver_file_pid, 'rb').read() 263 | p_pid = struct.unpack("I", data)[0] 264 | except: 265 | pass 266 | print("Child PID: %d" % p_pid) 267 | while not os.path.exists(self.forkserver_file_done): 268 | time.sleep(0.0005) 269 | wait_time += 0.0005 270 | if self.timeout and wait_time > self.timeout: 271 | print("Killing child: %d" % p_pid) 272 | os.system("kill -9 %s" % p_pid) 273 | break 274 | data = self.pipe_read.read(1) 275 | p_return_code = None 276 | while p_return_code is None: 277 | try: 278 | data = open(self.forkserver_file_done, 'rb').read() 279 | p_return_code = struct.unpack("I", data)[0] 280 | except: 281 | # print("Failed to read status code of the child. Retrying...") 282 | time.sleep(0.0005) 283 | 284 | end = time.time() 285 | print("Completed (time=%.3f)" % (end-start)) 286 | 287 | if p_return_code != 0: 288 | returncode_str = "(SIGSEGV)" if p_return_code == -11 else "" 289 | print("ERROR: target binary has returned code %d %s" % 290 | (p_return_code, returncode_str)) 291 | 292 | # check new test cases 293 | if not self.debug: 294 | # tracer_runner_time, tracer_comparer_time, _ = (0, 0, 0) # 295 | tracer_runner_time, tracer_comparer_time, _ = self.queue_manager.add_from_dir(self.__get_work_dir(), testcase) 296 | tracer_time = time.time() 297 | else: 298 | tracer_runner_time = 0 299 | tracer_comparer_time = 0 300 | tracer_time = end 301 | 302 | if self.keep_run_dirs: 303 | os.system("mv %s/* %s" % (self.__get_work_dir(), run_dir)) 304 | os.system("rm %s/%s" % (run_dir, os.path.basename(self.input_file))) 305 | os.system("cp " + testcase + " " + run_dir) 306 | else: 307 | print("Removing %s..." % run_dir) 308 | shutil.rmtree(run_dir) 309 | 310 | shutil.rmtree(self.__get_work_dir()) 311 | 312 | return (end-start), (tracer_time-end), tracer_runner_time, tracer_comparer_time 313 | 314 | def tick(self): 315 | self.tick_count += 1 316 | return self.tick_count - 1 317 | 318 | @property 319 | def cur_input(self): 320 | if self.input_fixed_name: 321 | return self.__get_root_dir() + '/' + self.input_fixed_name 322 | else: 323 | return self.__get_root_dir() + '/.cur_input' 324 | 325 | def __check_shutdown_flag(self): 326 | if SHUTDOWN: 327 | sys.exit("Forcefully terminating...") 328 | 329 | def run(self): 330 | 331 | if not self.debug or self.debug == "tracer": 332 | print("\nEvaluating seed inputs:") 333 | tracer_start = time.time() 334 | input_dir = self.input 335 | if os.path.isdir(self.input): 336 | tmp_dir = tempfile.TemporaryDirectory() 337 | input_dir = tmp_dir.name 338 | tracer_count = 0 339 | for seed in sorted(glob.glob(self.input + "/*")): 340 | os.system("cp %s %s" % (seed, tmp_dir.name)) 341 | tracer_count += 1 342 | if self.debug == "tracer": 343 | if tracer_count == 500: 344 | break 345 | else: 346 | tmp_dir = tempfile.TemporaryDirectory() 347 | os.system("cp %s %s" % (self.input, tmp_dir.name)) 348 | input_dir = tmp_dir.name 349 | tracer_count = 1 350 | self.queue_manager.add_from_dir(input_dir, "seed") 351 | 352 | if self.debug == "tracer": 353 | total = time.time() - tracer_start 354 | average = total / tracer_count 355 | print("Tracer: cumulative=%f secs, average=%f secs, count=%d" % (total, average, tracer_count)) 356 | sys.exit(0) 357 | 358 | total_runner_time = 0 359 | total_tracer_time = 0 360 | total_pick_time = 0 361 | total_run_time = 0 362 | 363 | self.__check_shutdown_flag() 364 | if not self.debug: 365 | testcase, tracer_run_time, tracer_comp_time = self.queue_manager.pick_testcase() 366 | total_tracer_time += tracer_run_time + tracer_comp_time 367 | else: 368 | testcase = self.input 369 | 370 | runs = 0 371 | start_loop = time.time() 372 | while testcase: 373 | runs += 1 374 | start = time.time() 375 | runner_time, tracer_time, tracer_run_time, tracer_comp_time = self.fuzz_one(testcase) 376 | end = time.time() 377 | total_runner_time += runner_time 378 | total_tracer_time += tracer_time 379 | print("Run took %.2f secs [runner=%.2f, tracer=%.2f, tracer_run=%.2f, tracer_comp=%.2f, total_tracer=%.2f, total_runner=%.2f, avg_total_runner=%.3f, all_time=%.2f, pick_time=%.2f]" 380 | % (end-start, runner_time, tracer_time, tracer_run_time, tracer_comp_time, total_tracer_time, total_runner_time, total_runner_time / runs, end-start_loop, total_pick_time)) 381 | total_run_time += end-start 382 | 383 | start = time.time() 384 | if self.debug: 385 | return 386 | self.__check_shutdown_flag() 387 | testcase = None 388 | while testcase is None: 389 | testcase, tracer_run_time, tracer_comp_time = self.queue_manager.pick_testcase() 390 | total_tracer_time += tracer_run_time + tracer_comp_time 391 | if testcase is None and self.afl: 392 | time.sleep(0.5) 393 | else: 394 | break 395 | self.__check_shutdown_flag() 396 | end = time.time() 397 | total_pick_time += end - start 398 | 399 | print("total_run=%.2f, total_pick=%.2f, all_time=%.2f" % (total_run_time, total_pick_time, end-start_loop)) 400 | 401 | print("\n[SymFusion] no more testcases. Finishing.\n") 402 | -------------------------------------------------------------------------------- /runner/hybrid.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | import os 4 | import sys 5 | import subprocess 6 | import argparse 7 | import re 8 | import tempfile 9 | from functools import reduce 10 | import time 11 | 12 | SCRIPT_DIR = os.path.dirname(os.path.realpath(__file__)) 13 | LIBRUNTIME_SO_NAME = "libSymRuntime.so" 14 | QEMU_BIN = SCRIPT_DIR + '/../symqemu-hybrid/x86_64-linux-user/symqemu-x86_64' 15 | LOG_FILE = None 16 | 17 | dump_cache = {} 18 | 19 | def run(args): 20 | try: 21 | p = subprocess.Popen(args, stdout=subprocess.PIPE) 22 | plt_patches = p.communicate() 23 | stdout = str(plt_patches[0].decode('ascii')) 24 | return stdout 25 | except Exception as e: 26 | print("Error when running the program: %s" % e) 27 | return None 28 | 29 | 30 | def get_linked_libs(binary): 31 | res = [] 32 | libs_binary = subprocess.check_output( 33 | "objdump -p %s | grep NEEDED | awk '{ print $2}'" % binary, shell=True) 34 | libs_binary = libs_binary.decode('ascii').strip("\t").split("\n") 35 | for l in libs_binary: 36 | # if "libdl.so" in l: 37 | # continue 38 | res.append(l) 39 | return res 40 | 41 | 42 | def get_lib_path(lib, all_libs): 43 | for libpath in all_libs: 44 | if libpath.endswith(lib): 45 | return libpath 46 | print("Cannot find path of lib: %s" % lib) 47 | assert(0) 48 | 49 | 50 | def is_instrumented(libpath): 51 | libs = get_linked_libs(libpath) 52 | return LIBRUNTIME_SO_NAME in libs 53 | 54 | 55 | def split_libs(libs, all_libs, emulated_libs, non_emulated_libs): 56 | # print(libs) 57 | for lib in libs: 58 | if len(lib) == 0: 59 | continue 60 | 61 | libpath = get_lib_path(lib, all_libs) 62 | if lib.endswith(LIBRUNTIME_SO_NAME): 63 | non_emulated_libs.add(lib) 64 | elif is_instrumented(libpath): 65 | non_emulated_libs.add(lib) 66 | lib_libs = get_linked_libs(libpath) 67 | emulated_libs, non_emulated_libs = split_libs( 68 | lib_libs, all_libs, emulated_libs, non_emulated_libs) 69 | else: 70 | emulated_libs.add(lib) 71 | return emulated_libs, non_emulated_libs 72 | 73 | 74 | def get_symbols_libs(libs): 75 | symbols = {} 76 | for lib in libs: 77 | try: 78 | res = subprocess.check_output( 79 | "nm -D %s" % lib, shell=True) 80 | res = res.decode('ascii') 81 | # print(res) 82 | except: 83 | res = "" 84 | for el in res.split("\n"): 85 | if el == "": 86 | continue 87 | split = el.split(" ") 88 | if len(split) != 3: 89 | continue 90 | addr = int(split[0], 16) 91 | name = split[2] 92 | type = split[1] 93 | if type not in ["T", "W", "i"]: 94 | continue 95 | if name in symbols and lib not in symbols[name]: 96 | # print("Function %s in both %s and %s" % (name, lib, symbols[name])) 97 | symbols[name].append(lib) 98 | else: 99 | symbols[name] = [lib] 100 | return symbols 101 | 102 | 103 | def define_plt_entries_to_patch(blob, all_libs, emulated_libs, symbols, plt_patches, runtime_plt_patches, functions_patched): 104 | 105 | blob_path = blob 106 | if ".so" in blob: 107 | blob_path = get_lib_path(blob, all_libs) 108 | 109 | plt_patches[blob_path] = [] 110 | runtime_plt_patches[blob_path] = [] 111 | 112 | plt_start = subprocess.check_output( 113 | "readelf -a %s | grep R_X86_64_JUMP_SLO | head -n 1 | awk '{ print $1 }'" % blob_path, shell=True) 114 | plt_start = int(plt_start.decode('ascii'), 16) 115 | # print("PLT JUMP TABLE ADDRESS of %s: %s" % (blob, hex(plt_start))) 116 | 117 | plt_entries = subprocess.check_output( 118 | "readelf -W -a %s | grep R_X86_64_JUMP_SLO" % blob_path, shell=True) 119 | plt_entries = list(filter(lambda x: len( 120 | x) > 0, plt_entries.decode('ascii').split("\n"))) 121 | nb_plt_entries = len(plt_entries) 122 | # print("NUMBER OF PLT ENTRIES of %s: %s" % (blob, nb_plt_entries)) 123 | 124 | got_entries = subprocess.check_output( 125 | "readelf -W -a %s | grep R_X86_64_GLOB_DAT | grep '@'" % blob_path, shell=True) 126 | got_entries = list(filter(lambda x: len( 127 | x) > 0, got_entries.decode('ascii').split("\n"))) 128 | nb_got_entries = len(got_entries) 129 | 130 | # print(got_entries) 131 | 132 | if blob_path not in dump_cache: 133 | tmp = tempfile.NamedTemporaryFile(delete=False) 134 | print("objdump -d %s > %s" % (blob_path, tmp.name)) 135 | os.system("objdump -d %s > %s" % (blob_path, tmp.name)) 136 | dump_cache[blob_path] = tmp.name 137 | 138 | section_offset = subprocess.check_output( 139 | "objdump -d %s | head | grep \"<\" | head -n 1" % blob_path, shell=True) 140 | section_offset = section_offset.decode('ascii').split("\n")[0] 141 | matches = re.findall("(\w+) .+", section_offset) 142 | if len(matches) == 1: 143 | section_offset = int(matches[0], 16) 144 | # print("section offset: %s" % hex(section_offset)) 145 | else: 146 | assert(0) 147 | 148 | for got_entry in got_entries: 149 | 150 | matches = re.findall( 151 | "(\w+)\s+\w+\s+\w+\s+\w+\s+([\w,@,\.]+)\s*\+*\d*", got_entry) 152 | assert(len(matches) == 1) 153 | m = matches[0] 154 | offset = int(m[0], 16) - (section_offset if ".so" in blob_path else 0) 155 | name = m[1] 156 | name = name.split("@")[0] 157 | 158 | try: 159 | plt_entry = subprocess.check_output( 160 | "cat %s | grep \"<%s@plt>:\"" % (dump_cache[blob_path], name), shell=True) 161 | except subprocess.CalledProcessError: 162 | continue 163 | plt_entry = list(filter(lambda x: len( 164 | x) > 0, plt_entry.decode('ascii').split("\n"))) 165 | if len(plt_entry) == 0: 166 | continue # it is not a function 167 | plt_entry = plt_entry[0] 168 | plt_offset = int(plt_entry.split(" ")[0], 16) 169 | 170 | is_plt_entry = False 171 | for plt_entry in plt_entries: 172 | matches = re.findall( 173 | "(\w+)\s+\w+\s+\w+\s+\w+\s+([\w,@,\.]+)\s*\+*\d*", plt_entry) 174 | assert(len(matches) == 1) 175 | m = matches[0] 176 | offset = int(m[0], 16) - (section_offset if ".so" in blob_path else 0) 177 | name_plt = m[1] 178 | if name == name_plt: 179 | is_plt_entry = True 180 | break 181 | 182 | # print("%s: %x is_plt_entry=%s" % (name, plt_offset, is_plt_entry)) 183 | if not is_plt_entry: # and name not in ["free"]: 184 | print("\n%s is R_X86_64_GLOB_DAT and not R_X86_64_JUMP_SLO but is PLT. Adding it to the set of PLT enries.\n" % name) 185 | plt_entries.append(got_entry) 186 | 187 | for plt_entry in plt_entries: 188 | matches = re.findall( 189 | "(\w+)\s+\w+\s+\w+\s+\w+\s+([\w,@,\.]+)\s*\+*\d*", plt_entry) 190 | assert(len(matches) == 1) 191 | m = matches[0] 192 | offset = int(m[0], 16) - (section_offset if ".so" in blob_path else 0) 193 | name = m[1] 194 | if "@" in name: 195 | name = name.split("@")[0] 196 | if name not in symbols: 197 | print("Cannot find: name=%s offset=%s full=%s" % 198 | (name, hex(offset), m[1])) 199 | 200 | all_emulated = None 201 | is_runtime = False 202 | for lib in symbols[name]: 203 | 204 | if lib.endswith(LIBRUNTIME_SO_NAME): 205 | is_runtime = True 206 | elif is_runtime: 207 | assert(0) 208 | 209 | if os.path.basename(lib) in emulated_libs: 210 | if all_emulated is None: 211 | all_emulated = True 212 | elif all_emulated: 213 | pass 214 | else: 215 | print("Function %s is exported by libs that are all emulated: %s" % ( 216 | name, symbols[name])) 217 | else: 218 | if all_emulated is None: 219 | all_emulated = False 220 | elif not all_emulated: 221 | pass 222 | else: 223 | print("Function %s is exported by libs that are all non emulated: %s" % ( 224 | name, symbols[name])) 225 | 226 | if ".so" not in blob: 227 | # all_emulated = True 228 | pass 229 | 230 | assert(all_emulated is not None) 231 | if all_emulated: 232 | plt_patches[blob_path].append(offset) 233 | print("PLT ENTRY: %s in %s will be patched" % (name, blob)) 234 | if blob_path not in functions_patched: 235 | functions_patched[blob_path] = [] 236 | was_only_got_entry = plt_entry in got_entries 237 | functions_patched[blob_path].append([name, offset, was_only_got_entry]) 238 | else: 239 | # print("PLT ENTRY: %s in %s will NOT be patched" % (name, blob)) 240 | pass 241 | 242 | if is_runtime: 243 | print("Function %s is part of the runtime" % name) 244 | runtime_plt_patches[blob_path].append([name, offset]) 245 | 246 | return plt_patches, runtime_plt_patches, functions_patched 247 | 248 | 249 | def get_section_offset(blob_path): 250 | section_offset = subprocess.check_output( 251 | "objdump -d %s | head | grep \"<\" | head -n 1" % blob_path, shell=True) 252 | section_offset = section_offset.decode('ascii').split("\n")[0] 253 | matches = re.findall("(\w+) .+", section_offset) 254 | if len(matches) == 1: 255 | section_offset = int(matches[0], 16) 256 | # print("section offset: %s" % hex(section_offset)) 257 | else: 258 | assert(0) 259 | return section_offset 260 | 261 | 262 | def find_possible_plt_aliases(lib, libs, functions_patched, possible_plt_redirects): 263 | blob_path = get_lib_path(lib, libs) 264 | 265 | got_entries = subprocess.check_output( 266 | "readelf -W -a %s | grep R_X86_64_GLOB_DAT" % blob_path, shell=True) 267 | got_entries = list(filter(lambda x: len( 268 | x) > 0, got_entries.decode('ascii').split("\n"))) 269 | nb_got_entries = len(got_entries) 270 | 271 | section_offset = subprocess.check_output( 272 | "objdump -d %s | head | grep \"<\" | head -n 1" % blob_path, shell=True) 273 | section_offset = section_offset.decode('ascii').split("\n")[0] 274 | matches = re.findall("(\w+) .+", section_offset) 275 | if len(matches) == 1: 276 | section_offset = int(matches[0], 16) 277 | # print("section offset: %s" % hex(section_offset)) 278 | else: 279 | assert(0) 280 | 281 | for got_entry in got_entries: 282 | matches = re.findall( 283 | "(\w+)\s+\w+\s+\w+\s+\w+\s+([\w,@,\.]+)\s*\+*\d*", got_entry) 284 | assert(len(matches) == 1) 285 | m = matches[0] 286 | offset = int(m[0], 16) - (section_offset if ".so" in blob_path else 0) 287 | name = m[1] 288 | 289 | if "@" in name: 290 | name = name.split("@")[0] 291 | 292 | alias_handled = False 293 | for l in functions_patched: 294 | 295 | l_path = l 296 | if ".so" in l: 297 | l_path = get_lib_path(l, libs) 298 | 299 | for p in functions_patched[l]: 300 | if name == p[0]: 301 | 302 | if alias_handled: 303 | print("Multiple alias of %s on %s" % 304 | (name, os.path.basename(l))) 305 | 306 | alias_handled = True 307 | if blob_path not in possible_plt_redirects: 308 | possible_plt_redirects[blob_path] = [] 309 | 310 | if l_path not in dump_cache: 311 | tmp = tempfile.NamedTemporaryFile(delete=False) 312 | print("objdump -d %s > %s" % (l_path, tmp.name)) 313 | os.system("objdump -d %s > %s" % (l_path, tmp.name)) 314 | dump_cache[l_path] = tmp.name 315 | 316 | plt_entry = subprocess.check_output( 317 | "cat %s | grep \"<%s@plt>:\"" % (dump_cache[l_path], name), shell=True) 318 | plt_entry = list(filter(lambda x: len( 319 | x) > 0, plt_entry.decode('ascii').split("\n"))) 320 | assert(len(plt_entry) == 1 and len(plt_entry) > 0) 321 | plt_entry = plt_entry[0] 322 | plt_offset = int(plt_entry.split(" ")[0], 16) 323 | assert(plt_offset > 0) 324 | if ".so" in l: 325 | plt_offset += get_section_offset(l_path) 326 | # print(hex(plt_offset)) 327 | 328 | print("ALIAS ON %s from %s [%x] to %s %s [%x]" % (name, os.path.basename( 329 | blob_path), offset, os.path.basename(l), p[1], plt_offset)) 330 | possible_plt_redirects[blob_path].append( 331 | [l, offset, p[1], plt_offset]) 332 | 333 | return possible_plt_redirects 334 | 335 | 336 | def define_syscall_to_patch(blob_path, syscall_patches): 337 | 338 | syscall_patches[blob_path] = [] 339 | try: 340 | section_offset = subprocess.check_output( 341 | "objdump -d %s | head | grep \"<\" | head -n 1" % blob_path, shell=True) 342 | section_offset = section_offset.decode('ascii').split("\n")[0] 343 | matches = re.findall("(\w+) .+", section_offset) 344 | if len(matches) == 1: 345 | section_offset = int(matches[0], 16) 346 | # print("section offset: %s" % hex(section_offset)) 347 | else: 348 | assert(0) 349 | 350 | cache_file = SCRIPT_DIR + "/cache/" + \ 351 | os.path.basename(blob_path) + ".dat" 352 | if os.path.exists(cache_file): 353 | with open(cache_file, 'r') as f: 354 | syscall_insns = f.readlines() 355 | else: 356 | 357 | # FIXME: this very slow... 358 | syscall_insns = subprocess.check_output( 359 | "objdump -d %s | grep syscall" % blob_path, shell=True) 360 | syscall_insns = syscall_insns.decode('ascii').split("\n") 361 | 362 | if ".so" in blob_path: 363 | print("Writing cache for %s..." % blob_path) 364 | with open(cache_file, 'w') as f: 365 | for syscall_insn in syscall_insns: 366 | f.write(syscall_insn + "\n") 367 | 368 | except: 369 | syscall_insns = [] 370 | 371 | for syscall_insn in syscall_insns: 372 | syscall_insn = syscall_insn.strip("\n") 373 | matches = re.findall("\s+(\w+):\s+0f 05\s+syscall\s*", syscall_insn) 374 | if len(matches) == 1: 375 | offset = int(matches[0], 16) - \ 376 | (section_offset if ".so" in blob_path else 0) 377 | # print("[%s] syscall at offset: %s" % (blob, hex(offset))) 378 | syscall_patches[blob_path].append(offset) 379 | 380 | return syscall_patches 381 | 382 | 383 | def find_runtime_offsets(libs): 384 | 385 | runtime = None 386 | for lib in libs: 387 | if lib.endswith(LIBRUNTIME_SO_NAME): 388 | runtime = lib 389 | break 390 | 391 | assert(runtime) 392 | 393 | names = { 394 | "open_symbolized", 395 | "read_symbolized", 396 | "_sym_read_memory" 397 | } 398 | offsets = {} 399 | for name in names: 400 | cmd = "objdump -d %s | grep \"<%s>\" | awk '{ print $1 }'" % ( 401 | runtime, name) 402 | offset = subprocess.check_output(cmd, shell=True) 403 | offset = offset.decode('ascii').split("\n") 404 | assert(len(offset) == 2) 405 | offset = int(offset[0], 16) 406 | print("RUNTIME: %s -> %s" % (name, hex(offset))) 407 | offsets[name] = offset 408 | 409 | return offsets 410 | 411 | 412 | def find_heap_functions(lib, libs, libs_symbols): 413 | 414 | heap_functions = {} 415 | FN = [ 416 | "__libc_malloc@@GLIBC_2.2.5", 417 | "__libc_calloc@@GLIBC_2.2.5", 418 | "__libc_realloc@@GLIBC_2.2.5", 419 | "__libc_free@@GLIBC_2.2.5", 420 | "__printf_chk@@GLIBC_2.3.4", 421 | "_IO_printf@@GLIBC_2.2.5", 422 | "__sigsetjmp@@GLIBC_2.2.5", 423 | "setjmp@@GLIBC_2.2.5", 424 | "_setjmp@@GLIBC_2.2.5", 425 | "__libc_siglongjmp@@GLIBC_PRIVATE", 426 | "__libc_longjmp@@GLIBC_PRIVATE", 427 | "__snprintf_chk@@GLIBC_2.3.4", 428 | "__vsnprintf_chk@@GLIBC_2.3.4", 429 | "__snprintf@@GLIBC_PRIVATE", 430 | "__vsnprintf@@GLIBC_2.2.5", 431 | "_IO_vfprintf@@GLIBC_2.2.5", 432 | "__vfprintf_chk@@GLIBC_2.3.4" 433 | ] 434 | 435 | libc = None 436 | for lib in libs: 437 | if "libc.so" in lib: 438 | libc = lib 439 | break 440 | 441 | assert(libc) 442 | 443 | with tempfile.NamedTemporaryFile() as tmp: 444 | 445 | os.system("objdump -d %s > %s" % (libc, tmp.name)) 446 | 447 | section_offset = subprocess.check_output( 448 | "cat %s | head | grep \"<\" | head -n 1" % tmp.name, shell=True) 449 | section_offset = section_offset.decode('ascii').split("\n")[0] 450 | matches = re.findall("(\w+) .+", section_offset) 451 | if len(matches) == 1: 452 | section_offset = int(matches[0], 16) 453 | # print("section offset: %s" % hex(section_offset)) 454 | else: 455 | assert(0) 456 | 457 | for fn in FN: 458 | out = subprocess.check_output( 459 | "cat %s | grep \"<%s>:\" " % (tmp.name, fn), shell=True) 460 | out = out.decode('ascii').split("\n")[0] 461 | fn_offset = int(out.split(" ")[0], 16) - section_offset 462 | print("%s at %s" % (fn, hex(fn_offset))) 463 | heap_functions[fn] = fn_offset 464 | 465 | FN = { 466 | "_IO_fopen@@GLIBC_2.2.5", 467 | "_IO_file_fopen@@GLIBC_2.2.5", 468 | "_IO_file_open@@GLIBC_2.2.5", 469 | "__open@@GLIBC_2.2.5", 470 | "openat@@GLIBC_2.4", 471 | "__open64_2@@GLIBC_2.7", 472 | "__openat_2@@GLIBC_2.7", 473 | "__openat64_2@@GLIBC_2.7", 474 | "_IO_fread@@GLIBC_2.2.5", 475 | "__fread_unlocked_chk@@GLIBC_2.7", 476 | "fread_unlocked@@GLIBC_2.2.5", 477 | "_IO_file_read@@GLIBC_2.2.5", 478 | "__read@@GLIBC_2.2.5", 479 | "__read_chk@@GLIBC_2.4", 480 | "__fread_chk@@GLIBC_2.7", 481 | "_IO_seekoff@@GLIBC_2.2.5", 482 | "fseek@@GLIBC_2.2.5", 483 | "__fseeko64@@GLIBC_PRIVATE", 484 | "_IO_file_seek@@GLIBC_2.2.5", 485 | "__lseek@@GLIBC_2.2.5", 486 | "_IO_fgets@@GLIBC_2.2.5", 487 | "fgets_unlocked@@GLIBC_2.2.5", 488 | "__fgets_chk@@GLIBC_2.4", 489 | "__fgets_unlocked_chk@@GLIBC_2.4", 490 | "rewind@@GLIBC_2.2.5", 491 | "_IO_getc@@GLIBC_2.2.5", 492 | "fgetc_unlocked@@GLIBC_2.2.5", 493 | "_IO_ungetc@@GLIBC_2.2.5", 494 | } 495 | model_functions = {} 496 | for fn in FN: 497 | out = subprocess.check_output( 498 | "cat %s | grep \"<%s>:\" " % (tmp.name, fn), shell=True) 499 | out = out.decode('ascii').split("\n")[0] 500 | fn_offset = int(out.split(" ")[0], 16) - section_offset 501 | print("%s at %s" % (fn, hex(fn_offset))) 502 | model_functions[fn] = fn_offset 503 | 504 | return heap_functions, libc, model_functions 505 | 506 | def find_libpthread_functions(lib, libs, libs_symbols): 507 | 508 | heap_functions = {} 509 | FN = [ 510 | "longjmp@GLIBC_2.2.5", 511 | "_setjmp@plt", 512 | ] 513 | 514 | libc = None 515 | for lib in libs: 516 | if "libpthread.so.0" in lib: 517 | libc = lib 518 | break 519 | 520 | assert(libc) 521 | 522 | with tempfile.NamedTemporaryFile() as tmp: 523 | 524 | os.system("objdump -d %s > %s" % (libc, tmp.name)) 525 | 526 | section_offset = subprocess.check_output( 527 | "cat %s | head | grep \"<\" | head -n 1" % tmp.name, shell=True) 528 | section_offset = section_offset.decode('ascii').split("\n")[0] 529 | matches = re.findall("(\w+) .+", section_offset) 530 | if len(matches) == 1: 531 | section_offset = int(matches[0], 16) 532 | # print("section offset: %s" % hex(section_offset)) 533 | else: 534 | assert(0) 535 | 536 | for fn in FN: 537 | out = subprocess.check_output( 538 | "cat %s | grep \"<%s>:\" " % (tmp.name, fn), shell=True) 539 | out = out.decode('ascii').split("\n")[0] 540 | fn_offset = int(out.split(" ")[0], 16) - section_offset 541 | print("%s at %s" % (fn, hex(fn_offset))) 542 | heap_functions[fn] = fn_offset 543 | 544 | return heap_functions, libc 545 | 546 | 547 | def remove_got_entry_as_plt_without_alias(plt_patches, functions_patched): 548 | 549 | return plt_patches, functions_patched 550 | 551 | for blob in functions_patched: 552 | to_be_removed = [] 553 | for entry in functions_patched[blob]: 554 | can_be_removed = False 555 | if entry[2]: 556 | print("\nAnalyzing GOT entry %s for %s" % (os.path.basename(blob), entry)) 557 | name = entry[0] 558 | for b2 in functions_patched: 559 | for e2 in functions_patched[b2]: 560 | if e2[0] == name and not e2[2]: 561 | print("\nGOT entry %s for %s will have PLT from %s with entry %s" % (os.path.basename(blob), entry, os.path.basename(b2), e2)) 562 | can_be_removed = True 563 | break 564 | if can_be_removed: 565 | break 566 | if can_be_removed: 567 | to_be_removed.append(entry) 568 | plt_patches[blob].remove(entry[1]) 569 | 570 | for entry in to_be_removed: 571 | functions_patched[blob].remove(entry) 572 | print("\nRemoving from PLT patches for %s entry: %s\n" % (os.path.basename(blob), entry)) 573 | 574 | return plt_patches, functions_patched 575 | 576 | def print(s): 577 | # sys.stdout.write(str(s) + "\n") 578 | if LOG_FILE: 579 | with open(LOG_FILE, 'a') as out: 580 | out.write(str(s) + "\n") 581 | 582 | 583 | def build_conf(conf, binary, log): 584 | global LOG_FILE 585 | LOG_FILE = log 586 | print("BINARY: %s" % binary) 587 | 588 | # to help ldd find libs 589 | if 'LD_LIBRARY_PATH' in os.environ: 590 | os.environ['LD_LIBRARY_PATH'] = os.path.dirname( 591 | os.path.realpath(binary)) + ":" + os.environ['LD_LIBRARY_PATH'] 592 | else: 593 | os.environ['LD_LIBRARY_PATH'] = os.path.dirname( 594 | os.path.realpath(binary)) 595 | 596 | # ALL LIBS 597 | ldd_out = run(["ldd", binary]) 598 | ldd_out = ldd_out.strip("\t").split("\n") 599 | libs = [] 600 | for lib in ldd_out: 601 | matches = re.findall(".* => (.+) \(.+\)", lib) 602 | for m in matches: 603 | libs.append(m) 604 | 605 | libs_binary = get_linked_libs(binary) 606 | emulated_libs, non_emulated_libs = split_libs( 607 | libs_binary, libs, set(), set()) 608 | 609 | print("ALL LIBS: %s" % libs) 610 | print("EMULATED LIBS: %s" % emulated_libs) 611 | print("NON EMULATED LIBS: %s" % non_emulated_libs) 612 | 613 | runtime_offsets = {} # find_runtime_offsets(libs) 614 | 615 | elf_to_patch = set([binary]) | non_emulated_libs 616 | libs_symbols = get_symbols_libs(libs) 617 | plt_patches = {} 618 | runtime_plt_patches = {} 619 | functions_patched = {} 620 | for lib in elf_to_patch: 621 | if lib == LIBRUNTIME_SO_NAME: 622 | continue 623 | plt_patches, runtime_plt_patches, functions_patched = define_plt_entries_to_patch( 624 | lib, libs, emulated_libs, libs_symbols, plt_patches, runtime_plt_patches, functions_patched) 625 | 626 | plt_patches, functions_patched = remove_got_entry_as_plt_without_alias(plt_patches, functions_patched) 627 | 628 | possible_plt_redirects = {} 629 | for lib in emulated_libs: 630 | possible_plt_redirects = find_possible_plt_aliases( 631 | lib, libs, functions_patched, possible_plt_redirects) 632 | 633 | syscall_patches = {} 634 | for blob in ([binary] + libs): 635 | # syscall_patches = define_syscall_to_patch(blob, syscall_patches) 636 | pass 637 | 638 | start_address = subprocess.check_output( 639 | # "readelf -a %s | grep ' _start' | awk '{ print $2}'" % binary, shell=True ) 640 | "readelf -a %s | grep ' main' | grep \"GLOBAL\|WEAK\" | awk '{ print $2}'" % binary, shell=True) 641 | start_address = int(start_address.decode('ascii').split("\n")[0], 16) 642 | print("START ADDRESS: %s" % hex(start_address)) 643 | 644 | heap_functions, libc, model_functions = find_heap_functions( 645 | lib, libs, libs_symbols) 646 | 647 | libpthread_functions, libpthread = find_libpthread_functions(lib, libs, libs_symbols) 648 | 649 | with open(conf, 'w') as tmp: 650 | 651 | tmp.write("[start_addr]\n") 652 | tmp.write("addr=%s\n" % hex(start_address)) 653 | 654 | for lib in set(plt_patches.keys()) | set(syscall_patches.keys()) | set(possible_plt_redirects.keys()): 655 | if lib == binary: 656 | name = "main_image" 657 | else: 658 | name = get_lib_path(lib, libs) 659 | tmp.write("\n[%s]\n" % name) 660 | 661 | if lib in plt_patches: 662 | tmp.write("patch_plt=") 663 | s = "" 664 | for offset in plt_patches[lib]: 665 | s += "%s;" % hex(offset) 666 | tmp.write("%s\n" % s[:-1]) 667 | 668 | if lib in syscall_patches: 669 | tmp.write("patch_syscall=") 670 | s = "" 671 | for offset in syscall_patches[lib]: 672 | s += "%s;" % hex(offset) 673 | tmp.write("%s\n" % s[:-1]) 674 | 675 | if lib in runtime_plt_patches: 676 | for p in runtime_plt_patches[lib]: 677 | tmp.write("RUNTIME_%s=%s\n" % (p[0], hex(p[1]))) 678 | tmp.write("\n") 679 | 680 | if lib in possible_plt_redirects: 681 | unique_keys = set() 682 | for p in possible_plt_redirects[lib]: 683 | unique_keys.add(p[1]) 684 | 685 | for key in unique_keys: 686 | tmp.write("%s=" % hex(key)) 687 | first = True 688 | for p in possible_plt_redirects[lib]: 689 | if p[1] != key: 690 | continue 691 | if p[0] == binary: 692 | b = "main_image" 693 | else: 694 | b = os.path.basename(p[0]) 695 | tmp.write("%s%s;%s;%s" % ( 696 | ";" if not first else "", b, hex(p[2]), hex(p[3]))) 697 | first = False 698 | tmp.write("\n") 699 | tmp.write("\n") 700 | 701 | tmp.write("\n[libc]\n") 702 | tmp.write("path=%s\n" % libc) 703 | for name in heap_functions: 704 | tmp.write("%s=%x\n" % (name.split("@@")[0], heap_functions[name])) 705 | 706 | tmp.write("\n[libc_models]\n") 707 | tmp.write("path=%s\n" % libc) 708 | for name in model_functions: 709 | tmp.write("%s=%x\n" % (name.split("@@")[0], model_functions[name])) 710 | 711 | tmp.write("\n[libpthread]\n") 712 | tmp.write("path=%s\n" % libpthread) 713 | for name in libpthread_functions: 714 | tmp.write("%s=%x\n" % (name.split("@@")[0], libpthread_functions[name])) 715 | 716 | """ 717 | tmp.write("\n[runtime]\n") 718 | for name in runtime_offsets: 719 | tmp.write("%s=%s\n" % (name, runtime_offsets[name])) 720 | """ 721 | 722 | tmp.flush() 723 | 724 | for l in dump_cache: 725 | os.unlink(dump_cache[l]) 726 | 727 | # sys.exit(0) 728 | LOG_FILE = None 729 | 730 | 731 | def prepare(binary, binary_args, p_args, args, env, hybrid_conf): 732 | 733 | env['HYBRID_CONF_FILE'] = hybrid_conf 734 | env['LD_BIND_NOW'] = "1" 735 | env['SYMFUSION_HYBRID'] = "1" 736 | 737 | p_args += [QEMU_BIN] 738 | # p_args += [ "-d", "in_asm,op_opt" ] # ,out_asm 739 | 740 | args = [binary] + args 741 | 742 | return p_args, args, env 743 | -------------------------------------------------------------------------------- /runner/limiter.py: -------------------------------------------------------------------------------- 1 | import resource 2 | 3 | MAX_VIRTUAL_MEMORY = 3 * 1024 * 1024 * 1024 4 | SHUTDOWN = False 5 | RUNNING_PROCESSES = [] 6 | 7 | def setlimits(): 8 | resource.setrlimit(resource.RLIMIT_CORE, (0, 0)) 9 | resource.setrlimit(resource.RLIMIT_AS, 10 | (MAX_VIRTUAL_MEMORY, MAX_VIRTUAL_MEMORY)) -------------------------------------------------------------------------------- /runner/path_tracer.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | import os 4 | import sys 5 | import json 6 | import glob 7 | import filecmp 8 | import subprocess 9 | import time 10 | import signal 11 | import configparser 12 | import re 13 | import shutil 14 | import functools 15 | import tempfile 16 | import struct 17 | import posix 18 | 19 | from limiter import setlimits, RUNNING_PROCESSES 20 | import hybrid 21 | 22 | class PathTracer(object): 23 | 24 | def __init__(self, binary, binary_args, root_dir, 25 | main_edge_bitmap=None, all_edge_bitmap=None, 26 | main_path_bitmap=None, all_path_bitmap=None, 27 | bitmap_size=65536, hybrid_mode=False, 28 | hybrid_conf=None): 29 | self.binary = binary 30 | self.binary_args = binary_args 31 | self.testcase_from_stdin = '@@' not in self.binary_args 32 | self.root_dir = root_dir 33 | self.tracer_dir = self.root_dir + "/tracer" 34 | os.system("mkdir %s" % self.tracer_dir) 35 | self.bitmap_size = bitmap_size 36 | self.hybrid_mode = hybrid_mode 37 | self.hybrid_conf = hybrid_conf 38 | # unique edges 39 | self.main_edge_bitmap = main_edge_bitmap if main_edge_bitmap else root_dir + \ 40 | "/bitmap_edge_main" 41 | self.all_edge_bitmap = all_edge_bitmap if all_edge_bitmap else root_dir + "/bitmap_edge_all" 42 | # unique path 43 | self.main_path_bitmap = main_path_bitmap if main_path_bitmap else root_dir + \ 44 | "/bitmap_path_main" 45 | self.all_path_bitmap = all_path_bitmap if all_path_bitmap else root_dir + "/bitmap_path_all" 46 | 47 | self.initialize_bitmap(self.main_edge_bitmap) 48 | self.initialize_bitmap(self.all_edge_bitmap) 49 | 50 | self.pipe_read_path = self.tracer_dir + "/pipe_read" 51 | self.pipe_write_path = self.tracer_dir + "/pipe_write" 52 | os.mkfifo(self.pipe_read_path) 53 | os.mkfifo(self.pipe_write_path) 54 | 55 | # print("Running path tracer on %s" % os.path.basename(testcase)) 56 | 57 | # bitmaps for the current testcase 58 | self.virgin_main_edge_bitmap = self.tracer_dir + "/bitmap_virgin_edge_main" 59 | self.virgin_all_edge_bitmap = self.tracer_dir + "/bitmap_virgin_edge_all" 60 | self.virgin_main_path_bitmap = self.tracer_dir + "/bitmap_virgin_path_main" 61 | self.virgin_all_path_bitmap = self.tracer_dir + "/bitmap_virgin_path_all" 62 | self.path_tracer_file_done = self.tracer_dir + "/done" 63 | self.path_tracer_file_pid = self.tracer_dir + "/pid" 64 | 65 | testcase = self.tracer_dir + "/input" 66 | self.tracer_testcase = testcase 67 | 68 | # os.system("cp %s %s" % ("/mnt/ssd/symbolic/symfusion/tests/objdump/small_exec.elf", self.tracer_testcase)) 69 | 70 | env = os.environ.copy() 71 | env['SYMFUSION_BITMAP_SIZE'] = str(self.bitmap_size) 72 | env['SYMFUSION_BITMAP_TRACE_EDGE_MAIN'] = self.main_edge_bitmap 73 | env['SYMFUSION_BITMAP_TRACE_EDGE_ALL'] = self.all_edge_bitmap 74 | env['SYMFUSION_BITMAP_VIRGIN_EDGE_MAIN'] = self.virgin_main_edge_bitmap 75 | env['SYMFUSION_BITMAP_VIRGIN_EDGE_ALL'] = self.virgin_all_edge_bitmap 76 | env['SYMFUSION_BITMAP_VIRGIN_PATH_MAIN'] = self.virgin_main_path_bitmap 77 | env['SYMFUSION_BITMAP_VIRGIN_PATH_ALL'] = self.virgin_all_path_bitmap 78 | env['SYMFUSION_FORKSERVER_PIPE_WRITE'] = self.pipe_read_path 79 | env['SYMFUSION_FORKSERVER_PIPE_READ'] = self.pipe_write_path 80 | env['SYMFUSION_PATH_TRACER'] = "1" 81 | env['SYMFUSION_PATH_FORKSERVER_DONE'] = self.path_tracer_file_done 82 | env['SYMFUSION_PATH_FORKSERVER_PID'] = self.path_tracer_file_pid 83 | env['SYMFUSION_HYBRID'] = "1" 84 | # env['SYMCC_NO_SYMBOLIC_INPUT'] = "1" 85 | env['SYMCC_OUTPUT_DIR'] = self.tracer_dir 86 | if not self.testcase_from_stdin: 87 | env['SYMCC_INPUT_FILE'] = testcase 88 | else: 89 | env['SYMCC_INPUT_STDIN_FILE'] = testcase 90 | 91 | # env["SYMFUSION_TRACER_SKIP_DUMP"] = "1" 92 | 93 | use_gdb = False 94 | p_args = [] 95 | 96 | if use_gdb: 97 | p_args += ['gdb'] 98 | 99 | args = self.binary_args[:] 100 | if self.hybrid_mode: 101 | p_args, args, env = hybrid.prepare( 102 | self.binary, self.binary_args, p_args, args, env, self.hybrid_conf) 103 | else: 104 | p_args += [self.binary] 105 | 106 | if not self.testcase_from_stdin: 107 | for k in range(len(args)): 108 | if args[k] == '@@': 109 | args[k] = testcase 110 | 111 | if not use_gdb: 112 | p_args += args 113 | pass 114 | else: 115 | print("\nGDB COMMAND:\n\trun %s%s\n" % (' '.join(args), 116 | (" < " + testcase if self.testcase_from_stdin else ""))) 117 | 118 | if False: 119 | env['SYMCC_NO_SYMBOLIC_INPUT'] = "1" 120 | p_args[0] = "/mnt/ssd/symbolic/symqemu-forkserver/x86_64-linux-user/symqemu-x86_64" 121 | f = glob.glob("/home/symbolic/qemu-hybrid-test/tests/*/%s.symqemu" % os.path.basename(binary).replace(".symfusion", "")) 122 | p_args[1] = f[0] 123 | print(p_args) 124 | main_address = subprocess.check_output("objdump -d %s | grep \"
\" | awk '{ print $1 }'" % f[0], shell=True) 125 | env['FORKSERVER_MAIN'] = main_address.decode('ascii') 126 | print("Main address: %s" % main_address.decode('ascii')) 127 | 128 | start = time.time() 129 | p = subprocess.Popen( 130 | p_args, 131 | stdout=subprocess.DEVNULL, 132 | stderr=subprocess.DEVNULL, 133 | stdin=subprocess.PIPE if self.testcase_from_stdin and use_gdb is False else None, 134 | cwd=self.tracer_dir, 135 | env=env, 136 | preexec_fn=setlimits 137 | ) 138 | 139 | self.tracer_process = p 140 | self.tracer_pid = p.pid 141 | RUNNING_PROCESSES.append(p) 142 | 143 | self.pipe_write = open(self.pipe_write_path, 'wb') 144 | self.pipe_read = open(self.pipe_read_path, 'rb') 145 | 146 | """ 147 | p.wait() 148 | sys.exit(0) 149 | end = time.time() 150 | total = end-start 151 | """ 152 | 153 | def run(self, testcase_run): 154 | 155 | # print("Executing tracer over %s" % os.path.basename(testcase_run)) 156 | 157 | start = time.time() 158 | if os.path.exists(self.virgin_main_edge_bitmap): 159 | os.unlink(self.virgin_main_edge_bitmap) 160 | if os.path.exists(self.virgin_all_edge_bitmap): 161 | os.unlink(self.virgin_all_edge_bitmap) 162 | if os.path.exists(self.virgin_main_path_bitmap): 163 | os.unlink(self.virgin_main_path_bitmap) 164 | if os.path.exists(self.virgin_all_path_bitmap): 165 | os.unlink(self.virgin_all_path_bitmap) 166 | if os.path.exists(self.path_tracer_file_done): 167 | os.unlink(self.path_tracer_file_done) 168 | 169 | os.system("cp %s %s" % (testcase_run, self.tracer_testcase)) 170 | # os.system("cp %s %s" % ("/mnt/ssd/symbolic/symfusion/tests/objdump/small_exec.elf", self.tracer_testcase)) 171 | # os.system("cp %s %s" % ("/bin/true", self.tracer_testcase)) 172 | 173 | # print("Opening pipe") 174 | self.pipe_write.write(bytes([23])) 175 | self.pipe_write.flush() 176 | # print("Waiting for termination...") 177 | 178 | data = self.pipe_read.read(1) 179 | 180 | # while not os.path.exists(self.path_tracer_file_done): 181 | # time.sleep(0.0005) 182 | 183 | end = time.time() 184 | 185 | # return None, None, None, None, end-start, end-end, 0 186 | 187 | status_code = self.read_values_report(self.path_tracer_file_done, 4) 188 | status_code = status_code[0] if len(status_code) > 0 else None 189 | 190 | # print("Path tracer: run=%.3f status_code=%s" % (end-start, status_code)) 191 | 192 | # print("Total time: run=%f total=%f" % (total / repeat, total)) 193 | # self.tracer_process.send_signal(signal.SIGTERM) 194 | # sys.exit(0) 195 | 196 | unique_main_edges = self.read_values_report(self.virgin_main_edge_bitmap, 2) 197 | unique_all_edges = self.read_values_report(self.virgin_all_edge_bitmap, 2) 198 | hash_main = self.read_values_report(self.virgin_main_path_bitmap, 8) 199 | hash_main = hash_main[0] if len(hash_main) > 0 else None 200 | hash_all = self.read_values_report(self.virgin_all_path_bitmap, 8) 201 | hash_all = hash_all[0] if len(hash_all) > 0 else None 202 | 203 | compare = time.time() 204 | # print("Path tracer: run=%.2f compare=%.2f" % (end-start, compare-end)) 205 | 206 | return unique_main_edges, unique_all_edges, hash_main, hash_all, end-start, compare-end, status_code 207 | 208 | def read_values_report(self, report, element_size): 209 | 210 | if element_size == 2: 211 | format = "H" 212 | elif element_size == 4: 213 | format = "I" 214 | elif element_size == 8: 215 | format = "Q" 216 | else: 217 | assert False and "Invalid element size" 218 | 219 | values = [] 220 | if os.path.exists(report): 221 | with open(report, "rb") as tmp: 222 | data = tmp.read(element_size) 223 | while data: 224 | id = struct.unpack(format, data)[0] 225 | values.append(id) 226 | data = tmp.read(element_size) 227 | 228 | return values 229 | 230 | def initialize_bitmap(self, trace_bitmap): 231 | with open(trace_bitmap, "wb") as trace: 232 | for id in range(self.bitmap_size): 233 | trace.write(struct.pack('B', 0)) 234 | -------------------------------------------------------------------------------- /runner/queue_manager.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | import os 4 | import sys 5 | import json 6 | import glob 7 | import filecmp 8 | import subprocess 9 | import time 10 | import signal 11 | import configparser 12 | import re 13 | import shutil 14 | import functools 15 | import tempfile 16 | import random 17 | import ctypes 18 | import resource 19 | import collections 20 | import struct 21 | 22 | from path_tracer import PathTracer 23 | 24 | from limiter import setlimits, RUNNING_PROCESSES 25 | 26 | SCRIPT_DIR = os.path.dirname(os.path.realpath(__file__)) 27 | 28 | class Queue(object): 29 | def __init__(self, name, only_unique_ids=True): 30 | self.name = name 31 | self.ids = {} # id -> (testcase, was_processed) 32 | self.testcase_ids = {} # testcase -> list of ids 33 | self.count_ids = {} # testcase -> number of ids to process for the testcase 34 | self.only_unique_ids = only_unique_ids 35 | 36 | def add(self, testcase, ids, imported=False): 37 | assert testcase not in self.count_ids 38 | inserted = False 39 | for id in ids: 40 | if id not in self.ids: 41 | self.ids[id] = {} 42 | self.ids[id][testcase] = False 43 | inserted = True 44 | elif not self.only_unique_ids: 45 | self.ids[id][testcase] = False 46 | if inserted: 47 | self.count_ids[testcase] = len(ids) + (1 if imported else 0) 48 | self.testcase_ids[testcase] = ids 49 | 50 | def testcase_with_highest_count(self): 51 | if len(self.count_ids) == 0: 52 | return None, 0 53 | max_key = max(self.count_ids, key=self.count_ids.get) 54 | if self.count_ids[max_key] > 0: 55 | return max_key, self.count_ids[max_key] 56 | else: 57 | return None, 0 58 | 59 | def mark_testcase_as_processed(self, testcase): 60 | if testcase in self.count_ids: 61 | for id in self.testcase_ids[testcase]: 62 | self.ids[id][testcase] = True 63 | self.count_ids[testcase] = 0 64 | 65 | def get_testcases_for_id(self, id): 66 | if id in self.ids: 67 | return self.ids[id].keys() 68 | else: 69 | return None 70 | 71 | def size(self): 72 | return len(self.count_ids) 73 | 74 | def count_unprocessed(self): 75 | count = 0 76 | for testcase in self.count_ids: 77 | if self.count_ids[testcase] > 0: 78 | count += 1 79 | return count 80 | 81 | 82 | class QueueManager(object): 83 | HASH_MODE = 1 84 | QSYM_MODE = 2 85 | SYMFUSION_MODE = 3 86 | 87 | def __init__(self, root_dir, binary, binary_args, hybrid_mode=False, hybrid_conf=None, afl_dir=None, mode=HASH_MODE): 88 | self.mode = mode 89 | self.root_dir = root_dir 90 | self.queue_dir = "%s/queue" % root_dir 91 | os.system("mkdir %s" % self.queue_dir) 92 | 93 | self.afl_dir = afl_dir 94 | self.afl_min_id = -1 95 | 96 | if mode == QueueManager.SYMFUSION_MODE: 97 | self.tracer = PathTracer(binary, binary_args, root_dir, hybrid_mode=hybrid_mode, hybrid_conf=hybrid_conf) 98 | self.queue_main_edges = Queue("Edges (application code)", only_unique_ids=False) 99 | self.queue_all_edges = Queue("Edges (all code)", only_unique_ids=False) 100 | self.queue_main_path = Queue("Paths (application code)") 101 | self.queue_all_path = Queue("Paths (all code)") 102 | 103 | elif mode == QueueManager.HASH_MODE: 104 | self.curr_gen_dir = "%s/curr" % root_dir 105 | os.system("mkdir %s" % self.curr_gen_dir) 106 | self.next_gen_dir = "%s/next" % root_dir 107 | os.system("mkdir %s" % self.next_gen_dir) 108 | self.hash_generated_inputs = set() 109 | self.hash_processed_inputs = set() 110 | 111 | elif mode == QueueManager.QSYM_MODE: 112 | afl_bin_dir = os.environ['AFL_PATH'] 113 | assert afl_bin_dir is not None 114 | self.afl_tracer = afl_bin_dir + "/afl-showmap" 115 | self.afl_binary_args = binary_args 116 | 117 | if False: 118 | binary = os.path.basename(binary).replace(".symfusion", "") 119 | binary = glob.glob("/home/symbolic/qemu-hybrid-test/tests/*/%s" % binary + ".symqemu")[0] 120 | binary = binary.replace(".symqemu", ".symfusion") 121 | 122 | if os.path.exists(binary.replace(".symfusion", ".afl")) and False: 123 | self.afl_tracer_qemu_mode = False 124 | self.afl_binary = binary.replace(".symfusion", ".afl") # FIXME 125 | else: 126 | assert os.path.exists(binary.replace(".symfusion", ".symqemu")) 127 | self.afl_tracer_qemu_mode = True 128 | self.afl_binary = binary.replace(".symfusion", ".symqemu") # FIXME 129 | assert os.path.exists(self.afl_tracer) 130 | self.afl_tracer_bitmap = root_dir + "/afl_bitmap" 131 | self.processed_inputs = set() 132 | self.inputs = {} 133 | 134 | self.afl_tracer_persistent = True 135 | 136 | if self.afl_tracer_persistent: 137 | assert self.afl_tracer_qemu_mode 138 | self.afl_pipe_read = self.root_dir + "/afl_pipe_read" 139 | self.afl_pipe_write = self.root_dir + "/afl_pipe_write" 140 | os.mkfifo(self.afl_pipe_read) 141 | os.mkfifo(self.afl_pipe_write) 142 | os.system("mkdir %s" % self.root_dir + "/tracer") 143 | self.afl_tracer_output_dir = self.root_dir + "/tracer/out" 144 | self.afl_tracer_input_dir = self.root_dir + "/tracer/in" 145 | 146 | proc, start = self.start_afl_tracer(input_dir=self.afl_tracer_input_dir, output_dir=self.afl_tracer_output_dir) 147 | 148 | self.tick_count = 0 149 | 150 | def add_from_dir(self, run_dir, source_testcase, min_id=None, sync=False): 151 | 152 | runner_time = 0 153 | comparer_time = 0 154 | duplicate = 0 155 | unique = 0 156 | 157 | if self.mode in [QueueManager.SYMFUSION_MODE, QueueManager.HASH_MODE]: 158 | for testcase in sorted(glob.glob(run_dir + "/*")): 159 | if testcase.endswith(".log"): 160 | continue 161 | if sync: 162 | source_testcase = "%s" % os.path.basename(testcase).split(",")[0].split("id:")[1] 163 | if os.path.basename(testcase) == os.path.basename(source_testcase): 164 | continue 165 | if os.path.basename(testcase) == "input_file": 166 | continue 167 | if min_id is not None: 168 | testcase_id = int(os.path.basename(testcase)[3:9]) 169 | if testcase_id <= min_id: 170 | continue 171 | min_id = testcase_id 172 | # print("ID: %s" % testcase_id) 173 | 174 | rt, ct, dup = self.add(testcase, source_testcase, sync) 175 | runner_time += rt 176 | comparer_time += ct 177 | if dup: 178 | duplicate += 1 179 | else: 180 | unique += 1 181 | 182 | elif self.mode == QueueManager.QSYM_MODE: 183 | 184 | input_dir = run_dir 185 | if sync or self.afl_tracer_persistent: 186 | if not self.afl_tracer_persistent: 187 | input_dir_tmp = tempfile.TemporaryDirectory() 188 | input_dir = input_dir_tmp.name 189 | else: 190 | os.system("rm -rf %s" % self.afl_tracer_input_dir) 191 | os.system("mkdir %s" % self.afl_tracer_input_dir) 192 | input_dir = self.afl_tracer_input_dir 193 | for testcase in sorted(glob.glob(run_dir + "/*")): 194 | if min_id is not None: 195 | testcase_id = int(os.path.basename(testcase)[3:9]) 196 | if testcase_id < min_id: 197 | continue 198 | min_id = testcase_id 199 | os.system("cp %s %s/" % (testcase, input_dir)) 200 | 201 | if not self.afl_tracer_persistent: 202 | tmpdir = tempfile.TemporaryDirectory() 203 | output_dir = tmpdir.name 204 | else: 205 | os.system("rm -rf %s" % self.afl_tracer_output_dir) 206 | os.system("mkdir %s" % self.afl_tracer_output_dir) 207 | output_dir = self.afl_tracer_output_dir 208 | 209 | if not self.afl_tracer_persistent: 210 | proc, start = self.start_afl_tracer(input_dir=input_dir, output_dir=output_dir) 211 | 212 | else: 213 | start = time.time() 214 | 215 | self.pipe_write.write(bytes([23, 23, 23, 23])) 216 | self.pipe_write.flush() 217 | 218 | data = self.pipe_read.read(4) 219 | 220 | if not self.afl_tracer_persistent: 221 | proc.wait() 222 | RUNNING_PROCESSES.remove(proc) 223 | 224 | runner_time = time.time() - start 225 | 226 | for f in sorted(glob.glob(output_dir + "/*")): 227 | start = time.time() 228 | if not self.afl_tracer_persistent: 229 | interesting, new_coverage = self.is_interesting_bitmap(f) 230 | else: 231 | with open(f, "rb") as fp: 232 | count_interesting = struct.unpack("I", fp.read(4))[0] 233 | count_new_coverage = struct.unpack("I", fp.read(4))[0] 234 | interesting = count_interesting > 0 235 | new_coverage = count_new_coverage > 0 236 | 237 | comparer_time += time.time() - start 238 | if interesting: 239 | unique += 1 240 | else: 241 | duplicate += 1 242 | 243 | if interesting: 244 | print("Testcase %s: interesting=%s new_cov=%s" % (os.path.basename(f), interesting, new_coverage)) 245 | if sync: 246 | source_testcase = os.path.basename(f)[:9] 247 | else: 248 | source_testcase = os.path.basename(source_testcase) 249 | name = "id:%06d,src:%s" % (self.tick(), source_testcase if "id:" not in source_testcase else source_testcase[:len("id:......")]) 250 | if sync: 251 | name += ",sync:afl" 252 | if new_coverage: 253 | name += ",+cov" 254 | new_testcase = "%s/%s" % (self.queue_dir, name) 255 | # print(new_testcase) 256 | os.system("cp %s/%s %s" % (input_dir, os.path.basename(f), new_testcase)) 257 | self.inputs[name] = get_score(f) 258 | 259 | print("Summary of testcases: duplicated=%d unique=%d tracer_time=%.2f" % (duplicate, unique, runner_time)) 260 | return runner_time, comparer_time, min_id 261 | 262 | def start_afl_tracer(self, input_dir, output_dir): 263 | cmd = [self.afl_tracer, 264 | "-t", 265 | str(3000), 266 | "-m", "1G", 267 | "-b" # binary mode 268 | ] 269 | 270 | if self.afl_tracer_qemu_mode: 271 | cmd += ['-Q'] 272 | 273 | cmd += ["-o", 274 | output_dir, 275 | '-i', 276 | input_dir, 277 | "--", 278 | ] + [ self.afl_binary ] + self.afl_binary_args 279 | 280 | # print(cmd) 281 | env = os.environ.copy() 282 | del env['AFL_PATH'] 283 | 284 | if self.afl_tracer_qemu_mode and 'AFL_MAP_SIZE' in env: 285 | del env['AFL_MAP_SIZE'] 286 | 287 | if self.afl_tracer_persistent: 288 | env['SYMFUSION_TRACER_PIPE_READ'] = self.afl_pipe_write 289 | env['SYMFUSION_TRACER_PIPE_WRITE'] = self.afl_pipe_read 290 | # env['AFL_INST_LIBS'] = "1" 291 | 292 | start = time.time() 293 | proc = subprocess.Popen(cmd, stdin=None, 294 | stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, 295 | env=env) 296 | RUNNING_PROCESSES.append(proc) 297 | 298 | if self.afl_tracer_persistent: 299 | self.pipe_write = open(self.afl_pipe_write, 'wb') 300 | self.pipe_read = open(self.afl_pipe_read, 'rb') 301 | 302 | data = self.pipe_read.read(4) 303 | 304 | return proc, start 305 | 306 | def is_interesting_bitmap(self, bitmap): 307 | if not os.path.exists(self.afl_tracer_bitmap): 308 | os.system("cp %s %s" % (bitmap, self.afl_tracer_bitmap)) 309 | return True, True 310 | 311 | cmd = [ 312 | SCRIPT_DIR + '/../utils/comparer', 313 | bitmap, 314 | self.afl_tracer_bitmap, 315 | str(os.path.getsize(bitmap)) 316 | ] 317 | # print(cmd) 318 | 319 | with open(os.devnull, "wb") as devnull: 320 | proc = subprocess.Popen(cmd, stdin=None, 321 | stdout=devnull, stderr=devnull 322 | ) 323 | proc.wait() 324 | if proc.returncode == 1: 325 | return True, False 326 | if proc.returncode == 2: 327 | return True, True 328 | elif proc.returncode == 0: 329 | return False, False 330 | else: 331 | print("Error while merging bitmap %s [error code %d]" % (bitmap, proc.returncode)) 332 | return False, False 333 | 334 | print("Error while merging bitmaps") 335 | 336 | def add(self, testcase, source_testcase, sync=False): 337 | 338 | if self.mode == QueueManager.QSYM_MODE: 339 | print("Use add_from_dir()") 340 | sys.exit(1) 341 | 342 | duplicate = False 343 | if self.mode == QueueManager.SYMFUSION_MODE: 344 | new_main_edge_ids, new_all_edge_ids, hash_main, hash_all, runner_time, comparer_time, status_code = self.tracer.run( 345 | testcase) 346 | 347 | # return runner_time, comparer_time, True 348 | 349 | if hash_main is None or hash_all is None: # likely a crash 350 | print("Crashing input in the path tracer: status_code=%s input=%s" % (status_code, os.path.basename(testcase))) 351 | return runner_time, comparer_time, True 352 | 353 | is_unique_path_main = self.queue_main_path.get_testcases_for_id(hash_main) is None 354 | is_unique_path_all = self.queue_all_path.get_testcases_for_id(hash_all) is None 355 | if len(new_main_edge_ids) == 0 and len(new_all_edge_ids) == 0 and not is_unique_path_main and not is_unique_path_all: 356 | duplicate = True 357 | 358 | if not duplicate: 359 | print("Testcase %s%s: new_edges=[app=%d, all=%d], unique_path=[app=%s, all=%s]" % ( 360 | os.path.basename(testcase), 361 | " [%x, %x]" % (hash_main, hash_all), 362 | len(new_main_edge_ids), len(new_all_edge_ids), is_unique_path_main,is_unique_path_all)) 363 | 364 | if duplicate: 365 | return runner_time, comparer_time, duplicate 366 | 367 | debug_queue = False 368 | if debug_queue: 369 | if len(new_main_edge_ids) > 0: 370 | assert len(new_all_edge_ids) > 0 371 | assert is_unique_path_main 372 | assert is_unique_path_all 373 | if len(new_all_edge_ids) > 0: 374 | assert is_unique_path_all 375 | if is_unique_path_main: 376 | if not is_unique_path_all: 377 | print("Testcase generate a unique path for the application code but not for the entire execution") 378 | if hash is not None: 379 | print("Testcase with same hash as testcase: %s" % self.queue_all_path.get_testcases_for_id(hash_all)) 380 | assert is_unique_path_all 381 | 382 | elif self.mode == QueueManager.HASH_MODE: 383 | start = time.time() 384 | hash = subprocess.check_output("sha256sum %s | cut -f1 -d' '" % testcase, shell=True) 385 | hash = hash.decode('ascii').rstrip("\n") 386 | if hash in self.hash_generated_inputs: 387 | duplicate = True 388 | runner_time = time.time() - start 389 | comparer_time = 0 390 | 391 | if duplicate: 392 | return runner_time, comparer_time, duplicate 393 | 394 | self.hash_generated_inputs.add(hash) 395 | os.system("cp %s %s" % (testcase, self.next_gen_dir + "/" + hash)) 396 | print("Testcase %s: hash=%s" % (os.path.basename(testcase), hash)) 397 | 398 | source_testcase = os.path.basename(source_testcase) 399 | name = "id:%06d,src:%s" % (self.tick( 400 | ), source_testcase if "id:" not in source_testcase else source_testcase[:len("id:......")]) 401 | if sync: 402 | name += ",sync:afl" 403 | new_testcase = "%s/%s" % (self.queue_dir, name) 404 | os.system("cp %s %s" % (testcase, new_testcase)) 405 | 406 | if self.mode == QueueManager.SYMFUSION_MODE: 407 | if len(new_main_edge_ids) > 0: 408 | self.queue_main_edges.add(new_testcase, new_main_edge_ids, imported=sync) 409 | if len(new_all_edge_ids) > 0: 410 | self.queue_all_edges.add(new_testcase, new_all_edge_ids, imported=sync) 411 | self.queue_main_path.add(new_testcase, [ hash_main ], imported=sync) 412 | self.queue_all_path.add(new_testcase, [ hash_all ], imported=sync) 413 | 414 | return runner_time, comparer_time, duplicate 415 | 416 | def import_from_afl(self): 417 | if self.afl_dir is None: 418 | return 0, 0 419 | 420 | print("\nImporting from AFL...") 421 | runner_time, comparer_time, self.afl_min_id = self.add_from_dir(self.afl_dir + "/queue", None, self.afl_min_id, True) 422 | 423 | return runner_time, comparer_time 424 | 425 | def pick_testcase(self): 426 | tracer_time, comparer_time = self.import_from_afl() 427 | 428 | if self.mode == QueueManager.SYMFUSION_MODE: 429 | queues = [self.queue_main_edges, 430 | self.queue_all_edges, 431 | self.queue_main_path, 432 | self.queue_all_path] 433 | for queue in queues: 434 | testcase, score = queue.testcase_with_highest_count() 435 | if testcase: 436 | for q in queues: 437 | q.mark_testcase_as_processed(testcase) 438 | print("\nPicking input from queue %s: %s [score=%d, count=%d, waiting=%d]" % 439 | (queue.name, os.path.basename(testcase), score, queue.size(), queue.count_unprocessed())) 440 | return testcase, tracer_time, comparer_time 441 | 442 | elif self.mode == QueueManager.HASH_MODE: 443 | 444 | testcases = sorted(glob.glob(self.curr_gen_dir + "/*")) 445 | if len(testcases) == 0: 446 | print("\nNew generation...") 447 | os.system("rmdir %s" % self.curr_gen_dir) 448 | os.system("mv %s %s" % (self.next_gen_dir, self.curr_gen_dir)) 449 | os.system("mkdir %s" % self.next_gen_dir) 450 | testcases = sorted(glob.glob(self.curr_gen_dir + "/*")) 451 | 452 | for testcase in testcases: 453 | new_testcase = self.root_dir + "/current_gen_input.dat" 454 | os.system("cp %s %s" % (testcase, new_testcase)) 455 | os.system("rm %s" % testcase) 456 | hash = os.path.basename(testcase) 457 | self.hash_processed_inputs.add(hash) 458 | print("\nPicking input from queue: %s (waiting=%d)" % (hash, len(testcases)-1)) 459 | return new_testcase, tracer_time, comparer_time 460 | 461 | elif self.mode == QueueManager.QSYM_MODE: 462 | start = time.time() 463 | if len(self.inputs) > 0: 464 | testcase = keywithmaxval(self.inputs) 465 | del self.inputs[testcase] 466 | self.processed_inputs.add(testcase) 467 | end = time.time() 468 | print("\nPicking input from queue: %s (waiting=%d, time=%.2f)" % (testcase, len(self.inputs), end-start)) 469 | return self.queue_dir + "/" + testcase, tracer_time, comparer_time 470 | 471 | return None, 0, 0 472 | 473 | def tick(self): 474 | self.tick_count += 1 475 | return self.tick_count - 1 476 | 477 | 478 | def get_score(testcase): 479 | # New coverage is the best 480 | score1 = testcase.endswith("+cov") 481 | # NOTE: seed files are not marked with "+cov" 482 | # even though it contains new coverage 483 | score2 = "seed" in testcase 484 | # Smaller size is better 485 | try: 486 | score3 = -os.path.getsize(testcase) 487 | except: 488 | score3 = -10000 # file has been renamed by AFL 489 | # Since name contains id, so later generated one will be chosen earlier 490 | score4 = testcase 491 | return (score1, score2, score3, score4) 492 | 493 | def testcase_compare(a, b): 494 | a_score = get_score(a) 495 | b_score = get_score(b) 496 | return 1 if a_score > b_score else -1 497 | 498 | def keywithmaxval(d): 499 | """ a) create a list of the dict's keys and values; 500 | b) return the key with the max value""" 501 | v=list(d.values()) 502 | k=list(d.keys()) 503 | return k[v.index(max(v))] -------------------------------------------------------------------------------- /runner/symfusion.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 -u 2 | 3 | import os 4 | import sys 5 | import executor 6 | import signal 7 | import argparse 8 | import shutil 9 | import limiter 10 | import queue_manager 11 | 12 | 13 | def kill_running_processes(verbose=False): 14 | limiter.SHUTDOWN = True 15 | P = limiter.RUNNING_PROCESSES[:] 16 | for p in P: 17 | # print("Terminating %s" % p.pid) 18 | if verbose: 19 | print("[SymFusion] Sending SIGINT") 20 | os.system("pkill -9 -P %s" % p.pid) 21 | p.send_signal(signal.SIGINT) 22 | p.send_signal(signal.SIGUSR2) 23 | try: 24 | p.wait(2) 25 | try: 26 | limiter.RUNNING_PROCESSES.remove(p) 27 | except: 28 | pass 29 | except: 30 | if verbose: 31 | print("[SymFusion] Sending SIGKILL") 32 | p.send_signal(signal.SIGKILL) 33 | try: 34 | limiter.RUNNING_PROCESSES.remove(p) 35 | except: 36 | pass 37 | # print("[SymFusion] Waiting for termination") 38 | # p.wait() 39 | if verbose: 40 | print("[SymFusion] Exiting") 41 | 42 | def handler(signo, stackframe): 43 | print("[SymFusion] Aborting....") 44 | kill_running_processes(True) 45 | sys.exit(1) 46 | 47 | 48 | def main(): 49 | 50 | parser = argparse.ArgumentParser( 51 | description='SymFusion: hybrid concolic executor') 52 | 53 | # version 54 | parser.add_argument('--version', action='version', 55 | version='%(prog)s pre-{\\alpha}^{\infty}') 56 | 57 | # optional args 58 | parser.add_argument( 59 | '-d', '--debug', choices=['output', 'gdb', 'tracer'], help='enable debug mode; a single input will be analyzed') 60 | parser.add_argument( 61 | '-a', '--afl', help='path to afl workdir (it enables AFL mode); AFL_PATH must be set.') 62 | parser.add_argument( 63 | '-t', '--timeout', type=int, help='maximum running time for each input (secs)') 64 | parser.add_argument( 65 | '-k', '--keep-run-dirs', action='store_true', help='keep run directories') 66 | parser.add_argument( 67 | '-c', '--cache', help='path to cache directory') 68 | parser.add_argument( 69 | '-m', '--hybrid-mode', action='store_false', help='Run in hybrid mode') 70 | parser.add_argument( 71 | '-g', '--generate-hybrid-conf', help='Only generate hybrid conf at the given path') 72 | parser.add_argument( 73 | '-f', '--fork-server', action='store_true', help='Run using a fork server') 74 | parser.add_argument( 75 | '-q', '--queue-mode', choices=['symfusion', 'qsym', 'hash'], help='The type of queue handling used when picking inputs') 76 | 77 | # required args 78 | parser.add_argument( 79 | '-i', '--input', help='path to the initial seed (or seed directory)', required=True) 80 | parser.add_argument( 81 | '-o', '--output', help='output directory', required=True) 82 | 83 | # positional args 84 | parser.add_argument('binary', metavar='', 85 | type=str, help='path to the binary to run') 86 | parser.add_argument('args', metavar='', type=str, help='args for the binary to run', 87 | nargs='*') # argparse.REMAINDER 88 | 89 | args = parser.parse_args() 90 | 91 | binary = args.binary 92 | if not os.path.exists(binary): 93 | sys.exit('ERROR: binary does not exist.') 94 | 95 | input = args.input 96 | if not os.path.exists(input): 97 | sys.exit('ERROR: input does not exist.') 98 | 99 | PLACEHOLDER = ".symfusion" 100 | output_dir = args.output 101 | if args.generate_hybrid_conf is None: 102 | if os.path.exists(output_dir): 103 | if os.path.exists(output_dir + '/' + PLACEHOLDER): 104 | shutil.rmtree(output_dir) 105 | else: 106 | sys.exit("Unsafe to remove %s. Do it manually." % output_dir) 107 | if not os.path.exists(output_dir): 108 | os.system("mkdir -p " + output_dir) 109 | if not os.path.exists(output_dir): 110 | sys.exit('ERROR: cannot create output directory.') 111 | os.system("touch " + output_dir + '/' + PLACEHOLDER) 112 | 113 | if args.afl and not os.path.exists(args.afl): 114 | sys.exit('ERROR: AFL wordir does not exist.') 115 | afl = args.afl 116 | 117 | binary_args = args.args 118 | debug = args.debug 119 | cache_dir = args.cache 120 | if cache_dir: 121 | cache_dir = os.path.abspath(cache_dir) 122 | 123 | timeout = args.timeout 124 | keep_run_dirs = args.keep_run_dirs 125 | if keep_run_dirs is None: 126 | keep_run_dirs = False 127 | 128 | if args.debug == "gdb" and args.fork_server: 129 | print("Cannot debug with GDB when using forkserver") 130 | sys.exit(1) 131 | 132 | queue_mode = queue_manager.QueueManager.SYMFUSION_MODE 133 | if args.queue_mode == "qsym": 134 | queue_mode = queue_manager.QueueManager.QSYM_MODE 135 | elif args.queue_mode == "hash": 136 | queue_mode = queue_manager.QueueManager.HASH_MODE 137 | elif args.queue_mode == "symfusion": 138 | queue_mode = queue_manager.QueueManager.SYMFUSION_MODE 139 | 140 | signal.signal(signal.SIGINT, handler) 141 | 142 | symfusion = executor.Executor( 143 | binary, input, output_dir, binary_args, debug, afl, 144 | timeout=timeout, keep_run_dirs=keep_run_dirs, cache_dir=cache_dir, 145 | hybrid_mode=args.hybrid_mode, generate_hybrid_conf=args.generate_hybrid_conf, 146 | forkserver=args.fork_server, queue_mode=queue_mode) 147 | symfusion.run() 148 | kill_running_processes() 149 | 150 | 151 | if __name__ == "__main__": 152 | main() 153 | -------------------------------------------------------------------------------- /sym_helpers/.gitignore: -------------------------------------------------------------------------------- 1 | helper.txt 2 | libsymhelpers.so 3 | wrappers.o 4 | old 5 | -------------------------------------------------------------------------------- /sym_helpers/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | SCRIPT=`realpath $0` 4 | SCRIPTPATH=`dirname $SCRIPT` 5 | 6 | export PATH="$SCRIPTPATH/../symcc-klee/build/:${PATH}:$SCRIPTPATH/../symcc-hybrid/build/:${PATH}" 7 | 8 | clang -c wrappers.c -O1 -fPIC \ 9 | -I../symqemu-hybrid/include/ \ 10 | -I../symqemu-hybrid/ \ 11 | `pkg-config --cflags glib-2.0` \ 12 | -I../symqemu-hybrid/tcg/ \ 13 | -I../symqemu-hybrid/tcg/i386/ \ 14 | -I../symqemu-hybrid/target/i386/ \ 15 | -I../symqemu-hybrid/x86_64-linux-user/ \ 16 | -I../symqemu-hybrid/accel/tcg/ && \ 17 | \ 18 | symcc -shared fpu_helper.c cc_helper.c int_helper.c \ 19 | wrappers.o -o libsymhelpers.so -O1 -fPIC \ 20 | -I../symqemu-hybrid/include/ \ 21 | -I../symqemu-hybrid/ \ 22 | `pkg-config --cflags --libs glib-2.0` \ 23 | -I../symqemu-hybrid/tcg/ \ 24 | -I../symqemu-hybrid/tcg/i386/ \ 25 | -I../symqemu-hybrid/target/i386/ \ 26 | -I../symqemu-hybrid/x86_64-linux-user/ \ 27 | -I../symqemu-hybrid/accel/tcg/ 28 | -------------------------------------------------------------------------------- /sym_helpers/cc_helper.c: -------------------------------------------------------------------------------- 1 | /* 2 | * x86 condition code helpers 3 | * 4 | * Copyright (c) 2003 Fabrice Bellard 5 | * 6 | * This library is free software; you can redistribute it and/or 7 | * modify it under the terms of the GNU Lesser General Public 8 | * License as published by the Free Software Foundation; either 9 | * version 2 of the License, or (at your option) any later version. 10 | * 11 | * This library is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | * Lesser General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public 17 | * License along with this library; if not, see . 18 | */ 19 | 20 | /* HYBRID */ 21 | #include "qemu-isolate-build.h" 22 | /* HYBRID */ 23 | 24 | #include "qemu/osdep.h" 25 | #include "cpu.h" 26 | #include "exec/helper-proto.h" 27 | 28 | /* HYBRID */ 29 | #include "sym_helpers.h" 30 | 31 | #define cpu_cc_compute_all SYM(cpu_cc_compute_all) 32 | #define helper_cc_compute_all SYM(helper_cc_compute_all) 33 | #define helper_cc_compute_c SYM(helper_cc_compute_c) 34 | #define helper_write_eflags SYM(helper_write_eflags) 35 | #define helper_read_eflags SYM(helper_read_eflags) 36 | #define helper_clts SYM(helper_clts) 37 | #define helper_reset_rf SYM(helper_reset_rf) 38 | #define helper_cli SYM(helper_cli) 39 | #define helper_sti SYM(helper_sti) 40 | #define helper_clac SYM(helper_clac) 41 | #define helper_stac SYM(helper_stac) 42 | 43 | /* HYBRID */ 44 | 45 | const uint8_t parity_table[256] = { 46 | CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0, 47 | 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P, 48 | 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P, 49 | CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0, 50 | 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P, 51 | CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0, 52 | CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0, 53 | 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P, 54 | 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P, 55 | CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0, 56 | CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0, 57 | 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P, 58 | CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0, 59 | 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P, 60 | 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P, 61 | CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0, 62 | 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P, 63 | CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0, 64 | CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0, 65 | 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P, 66 | CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0, 67 | 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P, 68 | 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P, 69 | CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0, 70 | CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0, 71 | 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P, 72 | 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P, 73 | CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0, 74 | 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P, 75 | CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0, 76 | CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0, 77 | 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P, 78 | }; 79 | 80 | #define SHIFT 0 81 | #include "cc_helper_template.h" 82 | #undef SHIFT 83 | 84 | #define SHIFT 1 85 | #include "cc_helper_template.h" 86 | #undef SHIFT 87 | 88 | #define SHIFT 2 89 | #include "cc_helper_template.h" 90 | #undef SHIFT 91 | 92 | #ifdef TARGET_X86_64 93 | 94 | #define SHIFT 3 95 | #include "cc_helper_template.h" 96 | #undef SHIFT 97 | 98 | #endif 99 | 100 | static target_ulong compute_all_adcx(target_ulong dst, target_ulong src1, 101 | target_ulong src2) 102 | { 103 | return (src1 & ~CC_C) | (dst * CC_C); 104 | } 105 | 106 | static target_ulong compute_all_adox(target_ulong dst, target_ulong src1, 107 | target_ulong src2) 108 | { 109 | return (src1 & ~CC_O) | (src2 * CC_O); 110 | } 111 | 112 | static target_ulong compute_all_adcox(target_ulong dst, target_ulong src1, 113 | target_ulong src2) 114 | { 115 | return (src1 & ~(CC_C | CC_O)) | (dst * CC_C) | (src2 * CC_O); 116 | } 117 | 118 | target_ulong helper_cc_compute_all(target_ulong dst, target_ulong src1, 119 | target_ulong src2, int op) 120 | { 121 | switch (op) { 122 | default: /* should never happen */ 123 | return 0; 124 | 125 | case CC_OP_EFLAGS: 126 | return src1; 127 | case CC_OP_CLR: 128 | return CC_Z | CC_P; 129 | case CC_OP_POPCNT: 130 | return src1 ? 0 : CC_Z; 131 | 132 | case CC_OP_MULB: 133 | return compute_all_mulb(dst, src1); 134 | case CC_OP_MULW: 135 | return compute_all_mulw(dst, src1); 136 | case CC_OP_MULL: 137 | return compute_all_mull(dst, src1); 138 | 139 | case CC_OP_ADDB: 140 | return compute_all_addb(dst, src1); 141 | case CC_OP_ADDW: 142 | return compute_all_addw(dst, src1); 143 | case CC_OP_ADDL: 144 | return compute_all_addl(dst, src1); 145 | 146 | case CC_OP_ADCB: 147 | return compute_all_adcb(dst, src1, src2); 148 | case CC_OP_ADCW: 149 | return compute_all_adcw(dst, src1, src2); 150 | case CC_OP_ADCL: 151 | return compute_all_adcl(dst, src1, src2); 152 | 153 | case CC_OP_SUBB: 154 | return compute_all_subb(dst, src1); 155 | case CC_OP_SUBW: 156 | return compute_all_subw(dst, src1); 157 | case CC_OP_SUBL: 158 | return compute_all_subl(dst, src1); 159 | 160 | case CC_OP_SBBB: 161 | return compute_all_sbbb(dst, src1, src2); 162 | case CC_OP_SBBW: 163 | return compute_all_sbbw(dst, src1, src2); 164 | case CC_OP_SBBL: 165 | return compute_all_sbbl(dst, src1, src2); 166 | 167 | case CC_OP_LOGICB: 168 | return compute_all_logicb(dst, src1); 169 | case CC_OP_LOGICW: 170 | return compute_all_logicw(dst, src1); 171 | case CC_OP_LOGICL: 172 | return compute_all_logicl(dst, src1); 173 | 174 | case CC_OP_INCB: 175 | return compute_all_incb(dst, src1); 176 | case CC_OP_INCW: 177 | return compute_all_incw(dst, src1); 178 | case CC_OP_INCL: 179 | return compute_all_incl(dst, src1); 180 | 181 | case CC_OP_DECB: 182 | return compute_all_decb(dst, src1); 183 | case CC_OP_DECW: 184 | return compute_all_decw(dst, src1); 185 | case CC_OP_DECL: 186 | return compute_all_decl(dst, src1); 187 | 188 | case CC_OP_SHLB: 189 | return compute_all_shlb(dst, src1); 190 | case CC_OP_SHLW: 191 | return compute_all_shlw(dst, src1); 192 | case CC_OP_SHLL: 193 | return compute_all_shll(dst, src1); 194 | 195 | case CC_OP_SARB: 196 | return compute_all_sarb(dst, src1); 197 | case CC_OP_SARW: 198 | return compute_all_sarw(dst, src1); 199 | case CC_OP_SARL: 200 | return compute_all_sarl(dst, src1); 201 | 202 | case CC_OP_BMILGB: 203 | return compute_all_bmilgb(dst, src1); 204 | case CC_OP_BMILGW: 205 | return compute_all_bmilgw(dst, src1); 206 | case CC_OP_BMILGL: 207 | return compute_all_bmilgl(dst, src1); 208 | 209 | case CC_OP_ADCX: 210 | return compute_all_adcx(dst, src1, src2); 211 | case CC_OP_ADOX: 212 | return compute_all_adox(dst, src1, src2); 213 | case CC_OP_ADCOX: 214 | return compute_all_adcox(dst, src1, src2); 215 | 216 | #ifdef TARGET_X86_64 217 | case CC_OP_MULQ: 218 | return compute_all_mulq(dst, src1); 219 | case CC_OP_ADDQ: 220 | return compute_all_addq(dst, src1); 221 | case CC_OP_ADCQ: 222 | return compute_all_adcq(dst, src1, src2); 223 | case CC_OP_SUBQ: 224 | return compute_all_subq(dst, src1); 225 | case CC_OP_SBBQ: 226 | return compute_all_sbbq(dst, src1, src2); 227 | case CC_OP_LOGICQ: 228 | return compute_all_logicq(dst, src1); 229 | case CC_OP_INCQ: 230 | return compute_all_incq(dst, src1); 231 | case CC_OP_DECQ: 232 | return compute_all_decq(dst, src1); 233 | case CC_OP_SHLQ: 234 | return compute_all_shlq(dst, src1); 235 | case CC_OP_SARQ: 236 | return compute_all_sarq(dst, src1); 237 | case CC_OP_BMILGQ: 238 | return compute_all_bmilgq(dst, src1); 239 | #endif 240 | } 241 | } 242 | 243 | uint32_t cpu_cc_compute_all(CPUX86State *env, int op) 244 | { 245 | return helper_cc_compute_all(CC_DST, CC_SRC, CC_SRC2, op); 246 | } 247 | 248 | target_ulong helper_cc_compute_c(target_ulong dst, target_ulong src1, 249 | target_ulong src2, int op) 250 | { 251 | switch (op) { 252 | default: /* should never happen */ 253 | case CC_OP_LOGICB: 254 | case CC_OP_LOGICW: 255 | case CC_OP_LOGICL: 256 | case CC_OP_LOGICQ: 257 | case CC_OP_CLR: 258 | case CC_OP_POPCNT: 259 | return 0; 260 | 261 | case CC_OP_EFLAGS: 262 | case CC_OP_SARB: 263 | case CC_OP_SARW: 264 | case CC_OP_SARL: 265 | case CC_OP_SARQ: 266 | case CC_OP_ADOX: 267 | return src1 & 1; 268 | 269 | case CC_OP_INCB: 270 | case CC_OP_INCW: 271 | case CC_OP_INCL: 272 | case CC_OP_INCQ: 273 | case CC_OP_DECB: 274 | case CC_OP_DECW: 275 | case CC_OP_DECL: 276 | case CC_OP_DECQ: 277 | return src1; 278 | 279 | case CC_OP_MULB: 280 | case CC_OP_MULW: 281 | case CC_OP_MULL: 282 | case CC_OP_MULQ: 283 | return src1 != 0; 284 | 285 | case CC_OP_ADCX: 286 | case CC_OP_ADCOX: 287 | return dst; 288 | 289 | case CC_OP_ADDB: 290 | return compute_c_addb(dst, src1); 291 | case CC_OP_ADDW: 292 | return compute_c_addw(dst, src1); 293 | case CC_OP_ADDL: 294 | return compute_c_addl(dst, src1); 295 | 296 | case CC_OP_ADCB: 297 | return compute_c_adcb(dst, src1, src2); 298 | case CC_OP_ADCW: 299 | return compute_c_adcw(dst, src1, src2); 300 | case CC_OP_ADCL: 301 | return compute_c_adcl(dst, src1, src2); 302 | 303 | case CC_OP_SUBB: 304 | return compute_c_subb(dst, src1); 305 | case CC_OP_SUBW: 306 | return compute_c_subw(dst, src1); 307 | case CC_OP_SUBL: 308 | return compute_c_subl(dst, src1); 309 | 310 | case CC_OP_SBBB: 311 | return compute_c_sbbb(dst, src1, src2); 312 | case CC_OP_SBBW: 313 | return compute_c_sbbw(dst, src1, src2); 314 | case CC_OP_SBBL: 315 | return compute_c_sbbl(dst, src1, src2); 316 | 317 | case CC_OP_SHLB: 318 | return compute_c_shlb(dst, src1); 319 | case CC_OP_SHLW: 320 | return compute_c_shlw(dst, src1); 321 | case CC_OP_SHLL: 322 | return compute_c_shll(dst, src1); 323 | 324 | case CC_OP_BMILGB: 325 | return compute_c_bmilgb(dst, src1); 326 | case CC_OP_BMILGW: 327 | return compute_c_bmilgw(dst, src1); 328 | case CC_OP_BMILGL: 329 | return compute_c_bmilgl(dst, src1); 330 | 331 | #ifdef TARGET_X86_64 332 | case CC_OP_ADDQ: 333 | return compute_c_addq(dst, src1); 334 | case CC_OP_ADCQ: 335 | return compute_c_adcq(dst, src1, src2); 336 | case CC_OP_SUBQ: 337 | return compute_c_subq(dst, src1); 338 | case CC_OP_SBBQ: 339 | return compute_c_sbbq(dst, src1, src2); 340 | case CC_OP_SHLQ: 341 | return compute_c_shlq(dst, src1); 342 | case CC_OP_BMILGQ: 343 | return compute_c_bmilgq(dst, src1); 344 | #endif 345 | } 346 | } 347 | 348 | void helper_write_eflags(CPUX86State *env, target_ulong t0, 349 | uint32_t update_mask) 350 | { 351 | cpu_load_eflags(env, t0, update_mask); 352 | } 353 | 354 | target_ulong helper_read_eflags(CPUX86State *env) 355 | { 356 | uint32_t eflags; 357 | 358 | eflags = cpu_cc_compute_all(env, CC_OP); 359 | eflags |= (env->df & DF_MASK); 360 | eflags |= env->eflags & ~(VM_MASK | RF_MASK); 361 | return eflags; 362 | } 363 | 364 | void helper_clts(CPUX86State *env) 365 | { 366 | env->cr[0] &= ~CR0_TS_MASK; 367 | env->hflags &= ~HF_TS_MASK; 368 | } 369 | 370 | void helper_reset_rf(CPUX86State *env) 371 | { 372 | env->eflags &= ~RF_MASK; 373 | } 374 | 375 | void helper_cli(CPUX86State *env) 376 | { 377 | env->eflags &= ~IF_MASK; 378 | } 379 | 380 | void helper_sti(CPUX86State *env) 381 | { 382 | env->eflags |= IF_MASK; 383 | } 384 | 385 | void helper_clac(CPUX86State *env) 386 | { 387 | env->eflags &= ~AC_MASK; 388 | } 389 | 390 | void helper_stac(CPUX86State *env) 391 | { 392 | env->eflags |= AC_MASK; 393 | } 394 | 395 | #if 0 396 | /* vm86plus instructions */ 397 | void helper_cli_vm(CPUX86State *env) 398 | { 399 | env->eflags &= ~VIF_MASK; 400 | } 401 | 402 | void helper_sti_vm(CPUX86State *env) 403 | { 404 | env->eflags |= VIF_MASK; 405 | if (env->eflags & VIP_MASK) { 406 | raise_exception_ra(env, EXCP0D_GPF, GETPC()); 407 | } 408 | } 409 | #endif 410 | -------------------------------------------------------------------------------- /sym_helpers/cc_helper_template.h: -------------------------------------------------------------------------------- 1 | /* 2 | * x86 condition code helpers 3 | * 4 | * Copyright (c) 2008 Fabrice Bellard 5 | * 6 | * This library is free software; you can redistribute it and/or 7 | * modify it under the terms of the GNU Lesser General Public 8 | * License as published by the Free Software Foundation; either 9 | * version 2 of the License, or (at your option) any later version. 10 | * 11 | * This library is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | * Lesser General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public 17 | * License along with this library; if not, see . 18 | */ 19 | 20 | #define DATA_BITS (1 << (3 + SHIFT)) 21 | 22 | #if DATA_BITS == 8 23 | #define SUFFIX b 24 | #define DATA_TYPE uint8_t 25 | #elif DATA_BITS == 16 26 | #define SUFFIX w 27 | #define DATA_TYPE uint16_t 28 | #elif DATA_BITS == 32 29 | #define SUFFIX l 30 | #define DATA_TYPE uint32_t 31 | #elif DATA_BITS == 64 32 | #define SUFFIX q 33 | #define DATA_TYPE uint64_t 34 | #else 35 | #error unhandled operand size 36 | #endif 37 | 38 | #define SIGN_MASK (((DATA_TYPE)1) << (DATA_BITS - 1)) 39 | 40 | /* dynamic flags computation */ 41 | 42 | static int glue(compute_all_add, SUFFIX)(DATA_TYPE dst, DATA_TYPE src1) 43 | { 44 | int cf, pf, af, zf, sf, of; 45 | DATA_TYPE src2 = dst - src1; 46 | 47 | cf = dst < src1; 48 | pf = parity_table[(uint8_t)dst]; 49 | af = (dst ^ src1 ^ src2) & CC_A; 50 | zf = (dst == 0) * CC_Z; 51 | sf = lshift(dst, 8 - DATA_BITS) & CC_S; 52 | of = lshift((src1 ^ src2 ^ -1) & (src1 ^ dst), 12 - DATA_BITS) & CC_O; 53 | return cf | pf | af | zf | sf | of; 54 | } 55 | 56 | static int glue(compute_c_add, SUFFIX)(DATA_TYPE dst, DATA_TYPE src1) 57 | { 58 | return dst < src1; 59 | } 60 | 61 | static int glue(compute_all_adc, SUFFIX)(DATA_TYPE dst, DATA_TYPE src1, 62 | DATA_TYPE src3) 63 | { 64 | int cf, pf, af, zf, sf, of; 65 | DATA_TYPE src2 = dst - src1 - src3; 66 | 67 | cf = (src3 ? dst <= src1 : dst < src1); 68 | pf = parity_table[(uint8_t)dst]; 69 | af = (dst ^ src1 ^ src2) & 0x10; 70 | zf = (dst == 0) << 6; 71 | sf = lshift(dst, 8 - DATA_BITS) & 0x80; 72 | of = lshift((src1 ^ src2 ^ -1) & (src1 ^ dst), 12 - DATA_BITS) & CC_O; 73 | return cf | pf | af | zf | sf | of; 74 | } 75 | 76 | static int glue(compute_c_adc, SUFFIX)(DATA_TYPE dst, DATA_TYPE src1, 77 | DATA_TYPE src3) 78 | { 79 | return src3 ? dst <= src1 : dst < src1; 80 | } 81 | 82 | static int glue(compute_all_sub, SUFFIX)(DATA_TYPE dst, DATA_TYPE src2) 83 | { 84 | int cf, pf, af, zf, sf, of; 85 | DATA_TYPE src1 = dst + src2; 86 | 87 | cf = src1 < src2; 88 | pf = parity_table[(uint8_t)dst]; 89 | af = (dst ^ src1 ^ src2) & CC_A; 90 | zf = (dst == 0) * CC_Z; 91 | sf = lshift(dst, 8 - DATA_BITS) & CC_S; 92 | of = lshift((src1 ^ src2) & (src1 ^ dst), 12 - DATA_BITS) & CC_O; 93 | return cf | pf | af | zf | sf | of; 94 | } 95 | 96 | static int glue(compute_c_sub, SUFFIX)(DATA_TYPE dst, DATA_TYPE src2) 97 | { 98 | DATA_TYPE src1 = dst + src2; 99 | 100 | return src1 < src2; 101 | } 102 | 103 | static int glue(compute_all_sbb, SUFFIX)(DATA_TYPE dst, DATA_TYPE src2, 104 | DATA_TYPE src3) 105 | { 106 | int cf, pf, af, zf, sf, of; 107 | DATA_TYPE src1 = dst + src2 + src3; 108 | 109 | cf = (src3 ? src1 <= src2 : src1 < src2); 110 | pf = parity_table[(uint8_t)dst]; 111 | af = (dst ^ src1 ^ src2) & 0x10; 112 | zf = (dst == 0) << 6; 113 | sf = lshift(dst, 8 - DATA_BITS) & 0x80; 114 | of = lshift((src1 ^ src2) & (src1 ^ dst), 12 - DATA_BITS) & CC_O; 115 | return cf | pf | af | zf | sf | of; 116 | } 117 | 118 | static int glue(compute_c_sbb, SUFFIX)(DATA_TYPE dst, DATA_TYPE src2, 119 | DATA_TYPE src3) 120 | { 121 | DATA_TYPE src1 = dst + src2 + src3; 122 | 123 | return (src3 ? src1 <= src2 : src1 < src2); 124 | } 125 | 126 | static int glue(compute_all_logic, SUFFIX)(DATA_TYPE dst, DATA_TYPE src1) 127 | { 128 | int cf, pf, af, zf, sf, of; 129 | 130 | cf = 0; 131 | pf = parity_table[(uint8_t)dst]; 132 | af = 0; 133 | zf = (dst == 0) * CC_Z; 134 | sf = lshift(dst, 8 - DATA_BITS) & CC_S; 135 | of = 0; 136 | return cf | pf | af | zf | sf | of; 137 | } 138 | 139 | static int glue(compute_all_inc, SUFFIX)(DATA_TYPE dst, DATA_TYPE src1) 140 | { 141 | int cf, pf, af, zf, sf, of; 142 | DATA_TYPE src2; 143 | 144 | cf = src1; 145 | src1 = dst - 1; 146 | src2 = 1; 147 | pf = parity_table[(uint8_t)dst]; 148 | af = (dst ^ src1 ^ src2) & CC_A; 149 | zf = (dst == 0) * CC_Z; 150 | sf = lshift(dst, 8 - DATA_BITS) & CC_S; 151 | of = (dst == SIGN_MASK) * CC_O; 152 | return cf | pf | af | zf | sf | of; 153 | } 154 | 155 | static int glue(compute_all_dec, SUFFIX)(DATA_TYPE dst, DATA_TYPE src1) 156 | { 157 | int cf, pf, af, zf, sf, of; 158 | DATA_TYPE src2; 159 | 160 | cf = src1; 161 | src1 = dst + 1; 162 | src2 = 1; 163 | pf = parity_table[(uint8_t)dst]; 164 | af = (dst ^ src1 ^ src2) & CC_A; 165 | zf = (dst == 0) * CC_Z; 166 | sf = lshift(dst, 8 - DATA_BITS) & CC_S; 167 | of = (dst == SIGN_MASK - 1) * CC_O; 168 | return cf | pf | af | zf | sf | of; 169 | } 170 | 171 | static int glue(compute_all_shl, SUFFIX)(DATA_TYPE dst, DATA_TYPE src1) 172 | { 173 | int cf, pf, af, zf, sf, of; 174 | 175 | cf = (src1 >> (DATA_BITS - 1)) & CC_C; 176 | pf = parity_table[(uint8_t)dst]; 177 | af = 0; /* undefined */ 178 | zf = (dst == 0) * CC_Z; 179 | sf = lshift(dst, 8 - DATA_BITS) & CC_S; 180 | /* of is defined iff shift count == 1 */ 181 | of = lshift(src1 ^ dst, 12 - DATA_BITS) & CC_O; 182 | return cf | pf | af | zf | sf | of; 183 | } 184 | 185 | static int glue(compute_c_shl, SUFFIX)(DATA_TYPE dst, DATA_TYPE src1) 186 | { 187 | return (src1 >> (DATA_BITS - 1)) & CC_C; 188 | } 189 | 190 | static int glue(compute_all_sar, SUFFIX)(DATA_TYPE dst, DATA_TYPE src1) 191 | { 192 | int cf, pf, af, zf, sf, of; 193 | 194 | cf = src1 & 1; 195 | pf = parity_table[(uint8_t)dst]; 196 | af = 0; /* undefined */ 197 | zf = (dst == 0) * CC_Z; 198 | sf = lshift(dst, 8 - DATA_BITS) & CC_S; 199 | /* of is defined iff shift count == 1 */ 200 | of = lshift(src1 ^ dst, 12 - DATA_BITS) & CC_O; 201 | return cf | pf | af | zf | sf | of; 202 | } 203 | 204 | /* NOTE: we compute the flags like the P4. On olders CPUs, only OF and 205 | CF are modified and it is slower to do that. Note as well that we 206 | don't truncate SRC1 for computing carry to DATA_TYPE. */ 207 | static int glue(compute_all_mul, SUFFIX)(DATA_TYPE dst, target_long src1) 208 | { 209 | int cf, pf, af, zf, sf, of; 210 | 211 | cf = (src1 != 0); 212 | pf = parity_table[(uint8_t)dst]; 213 | af = 0; /* undefined */ 214 | zf = (dst == 0) * CC_Z; 215 | sf = lshift(dst, 8 - DATA_BITS) & CC_S; 216 | of = cf * CC_O; 217 | return cf | pf | af | zf | sf | of; 218 | } 219 | 220 | static int glue(compute_all_bmilg, SUFFIX)(DATA_TYPE dst, DATA_TYPE src1) 221 | { 222 | int cf, pf, af, zf, sf, of; 223 | 224 | cf = (src1 == 0); 225 | pf = 0; /* undefined */ 226 | af = 0; /* undefined */ 227 | zf = (dst == 0) * CC_Z; 228 | sf = lshift(dst, 8 - DATA_BITS) & CC_S; 229 | of = 0; 230 | return cf | pf | af | zf | sf | of; 231 | } 232 | 233 | static int glue(compute_c_bmilg, SUFFIX)(DATA_TYPE dst, DATA_TYPE src1) 234 | { 235 | return src1 == 0; 236 | } 237 | 238 | #undef DATA_BITS 239 | #undef SIGN_MASK 240 | #undef DATA_TYPE 241 | #undef DATA_MASK 242 | #undef SUFFIX 243 | -------------------------------------------------------------------------------- /sym_helpers/gen_sym_helpers.h: -------------------------------------------------------------------------------- 1 | DEF_HELPER_FLAGS_1(sym_dbg, 0, void, env) 2 | 3 | DEF_HELPER_FLAGS_2(sym_init_args_2_void, 0, void, ptr, ptr) 4 | DEF_HELPER_FLAGS_3(sym_init_args_3_void, 0, void, ptr, ptr, ptr) 5 | 6 | DEF_HELPER_FLAGS_2(sym_init_args_2, TCG_CALL_NO_RWG_SE, ptr, ptr, ptr) 7 | DEF_HELPER_FLAGS_4(sym_init_args_4, TCG_CALL_NO_RWG_SE, ptr, ptr, ptr, ptr, ptr) 8 | DEF_HELPER_FLAGS_1(sym_set_return_value, TCG_CALL_NO_RWG_SE, ptr, ptr) 9 | 10 | DEF_HELPER_FLAGS_2(sym_load_mem_reg, 0, ptr, env, i64) 11 | DEF_HELPER_FLAGS_3(sym_store_mem_reg, TCG_CALL_NO_RWG, void, env, ptr, i64) 12 | 13 | // cc_helper.c 14 | DEF_HELPER_FLAGS_4(cc_compute_c_symbolized, TCG_CALL_NO_RWG_SE, tl, tl, tl, tl, int) 15 | DEF_HELPER_FLAGS_4(cc_compute_all_symbolized, TCG_CALL_NO_RWG_SE, tl, tl, tl, tl, int) 16 | 17 | // int_helper.c 18 | DEF_HELPER_2(divb_AL_symbolized, void, env, tl) 19 | DEF_HELPER_2(divw_AX_symbolized, void, env, tl) 20 | DEF_HELPER_2(divl_EAX_symbolized, void, env, tl) 21 | DEF_HELPER_2(divq_EAX_symbolized, void, env, tl) 22 | DEF_HELPER_2(idivb_AL_symbolized, void, env, tl) 23 | DEF_HELPER_2(idivw_AX_symbolized, void, env, tl) 24 | DEF_HELPER_2(idivl_EAX_symbolized, void, env, tl) 25 | DEF_HELPER_2(idivq_EAX_symbolized, void, env, tl) 26 | 27 | // fpu_helper.c and op_sse.h 28 | #define SUFFIX _xmm_symbolized 29 | #define Reg ZMMReg 30 | 31 | DEF_HELPER_3(glue(psrlw, SUFFIX), void, env, Reg, Reg) 32 | DEF_HELPER_3(glue(psraw, SUFFIX), void, env, Reg, Reg) 33 | DEF_HELPER_3(glue(psllw, SUFFIX), void, env, Reg, Reg) 34 | DEF_HELPER_3(glue(psrld, SUFFIX), void, env, Reg, Reg) 35 | DEF_HELPER_3(glue(psrad, SUFFIX), void, env, Reg, Reg) 36 | DEF_HELPER_3(glue(pslld, SUFFIX), void, env, Reg, Reg) 37 | DEF_HELPER_3(glue(psrlq, SUFFIX), void, env, Reg, Reg) 38 | DEF_HELPER_3(glue(psllq, SUFFIX), void, env, Reg, Reg) 39 | 40 | DEF_HELPER_3(glue(psrldq, SUFFIX), void, env, Reg, Reg) 41 | DEF_HELPER_3(glue(pslldq, SUFFIX), void, env, Reg, Reg) 42 | 43 | #define SSE_HELPER_B(name, F)\ 44 | DEF_HELPER_3(glue(name, SUFFIX), void, env, Reg, Reg) 45 | 46 | #define SSE_HELPER_W(name, F)\ 47 | DEF_HELPER_3(glue(name, SUFFIX), void, env, Reg, Reg) 48 | 49 | #define SSE_HELPER_L(name, F)\ 50 | DEF_HELPER_3(glue(name, SUFFIX), void, env, Reg, Reg) 51 | 52 | #define SSE_HELPER_Q(name, F)\ 53 | DEF_HELPER_3(glue(name, SUFFIX), void, env, Reg, Reg) 54 | 55 | // DEF_HELPER_3(glue(paddq, SUFFIX), void, env, Reg, Reg) 56 | 57 | SSE_HELPER_B(paddb, FADD) 58 | SSE_HELPER_W(paddw, FADD) 59 | SSE_HELPER_L(paddl, FADD) 60 | SSE_HELPER_Q(paddq, FADD) 61 | 62 | SSE_HELPER_B(psubb, FSUB) 63 | SSE_HELPER_W(psubw, FSUB) 64 | SSE_HELPER_L(psubl, FSUB) 65 | SSE_HELPER_Q(psubq, FSUB) 66 | 67 | SSE_HELPER_B(paddusb, FADDUB) 68 | SSE_HELPER_B(paddsb, FADDSB) 69 | SSE_HELPER_B(psubusb, FSUBUB) 70 | SSE_HELPER_B(psubsb, FSUBSB) 71 | 72 | SSE_HELPER_W(paddusw, FADDUW) 73 | SSE_HELPER_W(paddsw, FADDSW) 74 | SSE_HELPER_W(psubusw, FSUBUW) 75 | SSE_HELPER_W(psubsw, FSUBSW) 76 | 77 | SSE_HELPER_B(pminub, FMINUB) 78 | SSE_HELPER_B(pmaxub, FMAXUB) 79 | 80 | SSE_HELPER_W(pminsw, FMINSW) 81 | SSE_HELPER_W(pmaxsw, FMAXSW) 82 | 83 | SSE_HELPER_Q(pand, FAND) 84 | SSE_HELPER_Q(pandn, FANDN) 85 | SSE_HELPER_Q(por, FOR) 86 | SSE_HELPER_Q(pxor, FXOR) 87 | 88 | SSE_HELPER_B(pcmpgtb, FCMPGTB) 89 | SSE_HELPER_W(pcmpgtw, FCMPGTW) 90 | SSE_HELPER_L(pcmpgtl, FCMPGTL) 91 | 92 | SSE_HELPER_B(pcmpeqb, FCMPEQ) 93 | SSE_HELPER_W(pcmpeqw, FCMPEQ) 94 | SSE_HELPER_L(pcmpeql, FCMPEQ) 95 | 96 | SSE_HELPER_W(pmullw, FMULLW) 97 | // SSE_HELPER_W(pmulhrw, FMULHRW) 98 | SSE_HELPER_W(pmulhuw, FMULHUW) 99 | SSE_HELPER_W(pmulhw, FMULHW) 100 | 101 | SSE_HELPER_B(pavgb, FAVG) 102 | SSE_HELPER_W(pavgw, FAVG) 103 | 104 | DEF_HELPER_3(glue(pmuludq, SUFFIX), void, env, Reg, Reg) 105 | DEF_HELPER_3(glue(pmaddwd, SUFFIX), void, env, Reg, Reg) 106 | 107 | DEF_HELPER_3(glue(psadbw, SUFFIX), void, env, Reg, Reg) 108 | // DEF_HELPER_4(glue(maskmov, SUFFIX), void, env, Reg, Reg, tl) 109 | DEF_HELPER_2(glue(movl_mm_T0, SUFFIX), void, Reg, i32) 110 | DEF_HELPER_2(glue(movq_mm_T0, SUFFIX), void, Reg, i64) 111 | 112 | // DEF_HELPER_3(glue(pshufw, SUFFIX), void, Reg, Reg, int) 113 | DEF_HELPER_3(glue(pshufd, SUFFIX), void, Reg, Reg, int) 114 | DEF_HELPER_3(glue(pshuflw, SUFFIX), void, Reg, Reg, int) 115 | DEF_HELPER_3(glue(pshufhw, SUFFIX), void, Reg, Reg, int) 116 | 117 | DEF_HELPER_2(glue(pmovmskb, SUFFIX), i32, env, Reg) 118 | DEF_HELPER_3(glue(packsswb, SUFFIX), void, env, Reg, Reg) 119 | DEF_HELPER_3(glue(packuswb, SUFFIX), void, env, Reg, Reg) 120 | DEF_HELPER_3(glue(packssdw, SUFFIX), void, env, Reg, Reg) 121 | #define UNPCK_OP(base_name, base) \ 122 | DEF_HELPER_3(glue(punpck ## base_name ## bw, SUFFIX), void, env, Reg, Reg) \ 123 | DEF_HELPER_3(glue(punpck ## base_name ## wd, SUFFIX), void, env, Reg, Reg) \ 124 | DEF_HELPER_3(glue(punpck ## base_name ## dq, SUFFIX), void, env, Reg, Reg) 125 | 126 | UNPCK_OP(l, 0) 127 | UNPCK_OP(h, 1) 128 | 129 | DEF_HELPER_3(glue(punpcklqdq, SUFFIX), void, env, Reg, Reg) 130 | DEF_HELPER_3(glue(punpckhqdq, SUFFIX), void, env, Reg, Reg) 131 | 132 | DEF_HELPER_3(glue(phaddw, SUFFIX), void, env, Reg, Reg) 133 | DEF_HELPER_3(glue(phaddd, SUFFIX), void, env, Reg, Reg) 134 | DEF_HELPER_3(glue(phaddsw, SUFFIX), void, env, Reg, Reg) 135 | DEF_HELPER_3(glue(phsubw, SUFFIX), void, env, Reg, Reg) 136 | DEF_HELPER_3(glue(phsubd, SUFFIX), void, env, Reg, Reg) 137 | DEF_HELPER_3(glue(phsubsw, SUFFIX), void, env, Reg, Reg) 138 | DEF_HELPER_3(glue(pabsb, SUFFIX), void, env, Reg, Reg) 139 | DEF_HELPER_3(glue(pabsw, SUFFIX), void, env, Reg, Reg) 140 | DEF_HELPER_3(glue(pabsd, SUFFIX), void, env, Reg, Reg) 141 | DEF_HELPER_3(glue(pmaddubsw, SUFFIX), void, env, Reg, Reg) 142 | DEF_HELPER_3(glue(pmulhrsw, SUFFIX), void, env, Reg, Reg) 143 | DEF_HELPER_3(glue(pshufb, SUFFIX), void, env, Reg, Reg) 144 | DEF_HELPER_3(glue(psignb, SUFFIX), void, env, Reg, Reg) 145 | DEF_HELPER_3(glue(psignw, SUFFIX), void, env, Reg, Reg) 146 | DEF_HELPER_3(glue(psignd, SUFFIX), void, env, Reg, Reg) 147 | // DEF_HELPER_4(glue(palignr, SUFFIX), void, env, Reg, Reg, s32) 148 | 149 | DEF_HELPER_3(glue(pblendvb, SUFFIX), void, env, Reg, Reg) 150 | DEF_HELPER_3(glue(blendvps, SUFFIX), void, env, Reg, Reg) 151 | DEF_HELPER_3(glue(blendvpd, SUFFIX), void, env, Reg, Reg) 152 | DEF_HELPER_3(glue(ptest, SUFFIX), void, env, Reg, Reg) 153 | DEF_HELPER_3(glue(pmovsxbw, SUFFIX), void, env, Reg, Reg) 154 | DEF_HELPER_3(glue(pmovsxbd, SUFFIX), void, env, Reg, Reg) 155 | DEF_HELPER_3(glue(pmovsxbq, SUFFIX), void, env, Reg, Reg) 156 | DEF_HELPER_3(glue(pmovsxwd, SUFFIX), void, env, Reg, Reg) 157 | DEF_HELPER_3(glue(pmovsxwq, SUFFIX), void, env, Reg, Reg) 158 | DEF_HELPER_3(glue(pmovsxdq, SUFFIX), void, env, Reg, Reg) 159 | DEF_HELPER_3(glue(pmovzxbw, SUFFIX), void, env, Reg, Reg) 160 | DEF_HELPER_3(glue(pmovzxbd, SUFFIX), void, env, Reg, Reg) 161 | DEF_HELPER_3(glue(pmovzxbq, SUFFIX), void, env, Reg, Reg) 162 | DEF_HELPER_3(glue(pmovzxwd, SUFFIX), void, env, Reg, Reg) 163 | DEF_HELPER_3(glue(pmovzxwq, SUFFIX), void, env, Reg, Reg) 164 | DEF_HELPER_3(glue(pmovzxdq, SUFFIX), void, env, Reg, Reg) 165 | DEF_HELPER_3(glue(pmuldq, SUFFIX), void, env, Reg, Reg) 166 | DEF_HELPER_3(glue(pcmpeqq, SUFFIX), void, env, Reg, Reg) 167 | DEF_HELPER_3(glue(packusdw, SUFFIX), void, env, Reg, Reg) 168 | DEF_HELPER_3(glue(pminsb, SUFFIX), void, env, Reg, Reg) 169 | DEF_HELPER_3(glue(pminsd, SUFFIX), void, env, Reg, Reg) 170 | DEF_HELPER_3(glue(pminuw, SUFFIX), void, env, Reg, Reg) 171 | DEF_HELPER_3(glue(pminud, SUFFIX), void, env, Reg, Reg) 172 | DEF_HELPER_3(glue(pmaxsb, SUFFIX), void, env, Reg, Reg) 173 | DEF_HELPER_3(glue(pmaxsd, SUFFIX), void, env, Reg, Reg) 174 | DEF_HELPER_3(glue(pmaxuw, SUFFIX), void, env, Reg, Reg) 175 | DEF_HELPER_3(glue(pmaxud, SUFFIX), void, env, Reg, Reg) 176 | DEF_HELPER_3(glue(pmulld, SUFFIX), void, env, Reg, Reg) 177 | DEF_HELPER_3(glue(phminposuw, SUFFIX), void, env, Reg, Reg) 178 | 179 | // DEF_HELPER_4(glue(mpsadbw, SUFFIX), void, env, Reg, Reg, i32) 180 | 181 | #undef UNPCK_OP 182 | #undef SSE_HELPER_B 183 | #undef SSE_HELPER_W 184 | #undef SSE_HELPER_L 185 | #undef SSE_HELPER_Q 186 | #undef SUFFIX 187 | #undef Reg -------------------------------------------------------------------------------- /sym_helpers/int_helper.c: -------------------------------------------------------------------------------- 1 | /* 2 | * x86 integer helpers 3 | * 4 | * Copyright (c) 2003 Fabrice Bellard 5 | * 6 | * This library is free software; you can redistribute it and/or 7 | * modify it under the terms of the GNU Lesser General Public 8 | * License as published by the Free Software Foundation; either 9 | * version 2 of the License, or (at your option) any later version. 10 | * 11 | * This library is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | * Lesser General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public 17 | * License along with this library; if not, see . 18 | */ 19 | 20 | /* HYBRID */ 21 | #include "qemu-isolate-build.h" 22 | /* HYBRID */ 23 | 24 | #include "qemu/osdep.h" 25 | #include "cpu.h" 26 | #include "exec/exec-all.h" 27 | #include "qemu/host-utils.h" 28 | #include "exec/helper-proto.h" 29 | #include "qapi/error.h" 30 | #include "qemu/guest-random.h" 31 | 32 | /* HYBRID */ 33 | #include "sym_helpers.h" 34 | 35 | #include "qemu/log.h" 36 | #include "qemu/log-for-trace.h" 37 | 38 | // rename helpers to avoid conflicts with QEMU 39 | // NOTE: other functions must be static! 40 | 41 | #define cpu_cc_compute_all SYM(cpu_cc_compute_all) 42 | uint32_t SYM(cpu_cc_compute_all)(CPUX86State *env, int op); 43 | 44 | #define helper_divb_AL SYM(helper_divb_AL) 45 | #define helper_idivb_AL SYM(helper_idivb_AL) 46 | #define helper_divw_AX SYM(helper_divw_AX) 47 | #define helper_idivw_AX SYM(helper_idivw_AX) 48 | #define helper_divl_EAX SYM(helper_divl_EAX) 49 | #define helper_idivl_EAX SYM(helper_idivl_EAX) 50 | #define helper_divq_EAX SYM(helper_divq_EAX) 51 | #define helper_idivq_EAX SYM(helper_idivq_EAX) 52 | 53 | #define helper_aam SYM(helper_aam) 54 | #define helper_aad SYM(helper_aad) 55 | #define helper_aaa SYM(helper_aaa) 56 | #define helper_aas SYM(helper_aas) 57 | #define helper_daa SYM(helper_daa) 58 | #define helper_das SYM(helper_das) 59 | 60 | #define helper_pdep SYM(helper_pdep) 61 | #define helper_pext SYM(helper_pext) 62 | 63 | #define helper_rclb SYM(helper_rclb) 64 | #define helper_rcrb SYM(helper_rcrb) 65 | #define helper_rclw SYM(helper_rclw) 66 | #define helper_rcrw SYM(helper_rcrw) 67 | #define helper_rcll SYM(helper_rcll) 68 | #define helper_rcrl SYM(helper_rcrl) 69 | #define helper_rclq SYM(helper_rclq) 70 | #define helper_rcrq SYM(helper_rcrq) 71 | 72 | #define helper_rdrand SYM(helper_rdrand) 73 | 74 | // FIXME 75 | // #define qemu_log_mask(a, b, c) 76 | // #define error_free(e) 77 | 78 | extern void QEMU_NORETURN raise_exception_ra(CPUX86State *env, int exception_index, 79 | uintptr_t retaddr); 80 | 81 | /* HYBRID */ 82 | 83 | //#define DEBUG_MULDIV 84 | 85 | /* modulo 9 table */ 86 | static const uint8_t rclb_table[32] = { 87 | 0, 1, 2, 3, 4, 5, 6, 7, 88 | 8, 0, 1, 2, 3, 4, 5, 6, 89 | 7, 8, 0, 1, 2, 3, 4, 5, 90 | 6, 7, 8, 0, 1, 2, 3, 4, 91 | }; 92 | 93 | /* modulo 17 table */ 94 | static const uint8_t rclw_table[32] = { 95 | 0, 1, 2, 3, 4, 5, 6, 7, 96 | 8, 9, 10, 11, 12, 13, 14, 15, 97 | 16, 0, 1, 2, 3, 4, 5, 6, 98 | 7, 8, 9, 10, 11, 12, 13, 14, 99 | }; 100 | 101 | /* division, flags are undefined */ 102 | 103 | void helper_divb_AL(CPUX86State *env, target_ulong t0) 104 | { 105 | unsigned int num, den, q, r; 106 | 107 | num = (env->regs[R_EAX] & 0xffff); 108 | den = (t0 & 0xff); 109 | if (den == 0) { 110 | raise_exception_ra(env, EXCP00_DIVZ, GETPC()); 111 | } 112 | q = (num / den); 113 | if (q > 0xff) { 114 | raise_exception_ra(env, EXCP00_DIVZ, GETPC()); 115 | } 116 | q &= 0xff; 117 | r = (num % den) & 0xff; 118 | env->regs[R_EAX] = (env->regs[R_EAX] & ~0xffff) | (r << 8) | q; 119 | } 120 | 121 | void helper_idivb_AL(CPUX86State *env, target_ulong t0) 122 | { 123 | int num, den, q, r; 124 | 125 | num = (int16_t)env->regs[R_EAX]; 126 | den = (int8_t)t0; 127 | if (den == 0) { 128 | raise_exception_ra(env, EXCP00_DIVZ, GETPC()); 129 | } 130 | q = (num / den); 131 | if (q != (int8_t)q) { 132 | raise_exception_ra(env, EXCP00_DIVZ, GETPC()); 133 | } 134 | q &= 0xff; 135 | r = (num % den) & 0xff; 136 | env->regs[R_EAX] = (env->regs[R_EAX] & ~0xffff) | (r << 8) | q; 137 | } 138 | 139 | void helper_divw_AX(CPUX86State *env, target_ulong t0) 140 | { 141 | unsigned int num, den, q, r; 142 | 143 | num = (env->regs[R_EAX] & 0xffff) | ((env->regs[R_EDX] & 0xffff) << 16); 144 | den = (t0 & 0xffff); 145 | if (den == 0) { 146 | raise_exception_ra(env, EXCP00_DIVZ, GETPC()); 147 | } 148 | q = (num / den); 149 | if (q > 0xffff) { 150 | raise_exception_ra(env, EXCP00_DIVZ, GETPC()); 151 | } 152 | q &= 0xffff; 153 | r = (num % den) & 0xffff; 154 | env->regs[R_EAX] = (env->regs[R_EAX] & ~0xffff) | q; 155 | env->regs[R_EDX] = (env->regs[R_EDX] & ~0xffff) | r; 156 | } 157 | 158 | void helper_idivw_AX(CPUX86State *env, target_ulong t0) 159 | { 160 | int num, den, q, r; 161 | 162 | num = (env->regs[R_EAX] & 0xffff) | ((env->regs[R_EDX] & 0xffff) << 16); 163 | den = (int16_t)t0; 164 | if (den == 0) { 165 | raise_exception_ra(env, EXCP00_DIVZ, GETPC()); 166 | } 167 | q = (num / den); 168 | if (q != (int16_t)q) { 169 | raise_exception_ra(env, EXCP00_DIVZ, GETPC()); 170 | } 171 | q &= 0xffff; 172 | r = (num % den) & 0xffff; 173 | env->regs[R_EAX] = (env->regs[R_EAX] & ~0xffff) | q; 174 | env->regs[R_EDX] = (env->regs[R_EDX] & ~0xffff) | r; 175 | } 176 | 177 | void helper_divl_EAX(CPUX86State *env, target_ulong t0) 178 | { 179 | unsigned int den, r; 180 | uint64_t num, q; 181 | 182 | num = ((uint32_t)env->regs[R_EAX]) | ((uint64_t)((uint32_t)env->regs[R_EDX]) << 32); 183 | den = t0; 184 | if (den == 0) { 185 | raise_exception_ra(env, EXCP00_DIVZ, GETPC()); 186 | } 187 | q = (num / den); 188 | r = (num % den); 189 | if (q > 0xffffffff) { 190 | raise_exception_ra(env, EXCP00_DIVZ, GETPC()); 191 | } 192 | env->regs[R_EAX] = (uint32_t)q; 193 | env->regs[R_EDX] = (uint32_t)r; 194 | } 195 | 196 | void helper_idivl_EAX(CPUX86State *env, target_ulong t0) 197 | { 198 | int den, r; 199 | int64_t num, q; 200 | 201 | num = ((uint32_t)env->regs[R_EAX]) | ((uint64_t)((uint32_t)env->regs[R_EDX]) << 32); 202 | den = t0; 203 | if (den == 0) { 204 | raise_exception_ra(env, EXCP00_DIVZ, GETPC()); 205 | } 206 | q = (num / den); 207 | r = (num % den); 208 | if (q != (int32_t)q) { 209 | raise_exception_ra(env, EXCP00_DIVZ, GETPC()); 210 | } 211 | env->regs[R_EAX] = (uint32_t)q; 212 | env->regs[R_EDX] = (uint32_t)r; 213 | } 214 | 215 | /* bcd */ 216 | 217 | /* XXX: exception */ 218 | void helper_aam(CPUX86State *env, int base) 219 | { 220 | int al, ah; 221 | 222 | al = env->regs[R_EAX] & 0xff; 223 | ah = al / base; 224 | al = al % base; 225 | env->regs[R_EAX] = (env->regs[R_EAX] & ~0xffff) | al | (ah << 8); 226 | CC_DST = al; 227 | } 228 | 229 | void helper_aad(CPUX86State *env, int base) 230 | { 231 | int al, ah; 232 | 233 | al = env->regs[R_EAX] & 0xff; 234 | ah = (env->regs[R_EAX] >> 8) & 0xff; 235 | al = ((ah * base) + al) & 0xff; 236 | env->regs[R_EAX] = (env->regs[R_EAX] & ~0xffff) | al; 237 | CC_DST = al; 238 | } 239 | 240 | void helper_aaa(CPUX86State *env) 241 | { 242 | int icarry; 243 | int al, ah, af; 244 | int eflags; 245 | 246 | eflags = cpu_cc_compute_all(env, CC_OP); 247 | af = eflags & CC_A; 248 | al = env->regs[R_EAX] & 0xff; 249 | ah = (env->regs[R_EAX] >> 8) & 0xff; 250 | 251 | icarry = (al > 0xf9); 252 | if (((al & 0x0f) > 9) || af) { 253 | al = (al + 6) & 0x0f; 254 | ah = (ah + 1 + icarry) & 0xff; 255 | eflags |= CC_C | CC_A; 256 | } else { 257 | eflags &= ~(CC_C | CC_A); 258 | al &= 0x0f; 259 | } 260 | env->regs[R_EAX] = (env->regs[R_EAX] & ~0xffff) | al | (ah << 8); 261 | CC_SRC = eflags; 262 | } 263 | 264 | void helper_aas(CPUX86State *env) 265 | { 266 | int icarry; 267 | int al, ah, af; 268 | int eflags; 269 | 270 | eflags = cpu_cc_compute_all(env, CC_OP); 271 | af = eflags & CC_A; 272 | al = env->regs[R_EAX] & 0xff; 273 | ah = (env->regs[R_EAX] >> 8) & 0xff; 274 | 275 | icarry = (al < 6); 276 | if (((al & 0x0f) > 9) || af) { 277 | al = (al - 6) & 0x0f; 278 | ah = (ah - 1 - icarry) & 0xff; 279 | eflags |= CC_C | CC_A; 280 | } else { 281 | eflags &= ~(CC_C | CC_A); 282 | al &= 0x0f; 283 | } 284 | env->regs[R_EAX] = (env->regs[R_EAX] & ~0xffff) | al | (ah << 8); 285 | CC_SRC = eflags; 286 | } 287 | 288 | void helper_daa(CPUX86State *env) 289 | { 290 | int old_al, al, af, cf; 291 | int eflags; 292 | 293 | eflags = cpu_cc_compute_all(env, CC_OP); 294 | cf = eflags & CC_C; 295 | af = eflags & CC_A; 296 | old_al = al = env->regs[R_EAX] & 0xff; 297 | 298 | eflags = 0; 299 | if (((al & 0x0f) > 9) || af) { 300 | al = (al + 6) & 0xff; 301 | eflags |= CC_A; 302 | } 303 | if ((old_al > 0x99) || cf) { 304 | al = (al + 0x60) & 0xff; 305 | eflags |= CC_C; 306 | } 307 | env->regs[R_EAX] = (env->regs[R_EAX] & ~0xff) | al; 308 | /* well, speed is not an issue here, so we compute the flags by hand */ 309 | eflags |= (al == 0) << 6; /* zf */ 310 | eflags |= parity_table[al]; /* pf */ 311 | eflags |= (al & 0x80); /* sf */ 312 | CC_SRC = eflags; 313 | } 314 | 315 | void helper_das(CPUX86State *env) 316 | { 317 | int al, al1, af, cf; 318 | int eflags; 319 | 320 | eflags = cpu_cc_compute_all(env, CC_OP); 321 | cf = eflags & CC_C; 322 | af = eflags & CC_A; 323 | al = env->regs[R_EAX] & 0xff; 324 | 325 | eflags = 0; 326 | al1 = al; 327 | if (((al & 0x0f) > 9) || af) { 328 | eflags |= CC_A; 329 | if (al < 6 || cf) { 330 | eflags |= CC_C; 331 | } 332 | al = (al - 6) & 0xff; 333 | } 334 | if ((al1 > 0x99) || cf) { 335 | al = (al - 0x60) & 0xff; 336 | eflags |= CC_C; 337 | } 338 | env->regs[R_EAX] = (env->regs[R_EAX] & ~0xff) | al; 339 | /* well, speed is not an issue here, so we compute the flags by hand */ 340 | eflags |= (al == 0) << 6; /* zf */ 341 | eflags |= parity_table[al]; /* pf */ 342 | eflags |= (al & 0x80); /* sf */ 343 | CC_SRC = eflags; 344 | } 345 | 346 | #ifdef TARGET_X86_64 347 | static void add128(uint64_t *plow, uint64_t *phigh, uint64_t a, uint64_t b) 348 | { 349 | *plow += a; 350 | /* carry test */ 351 | if (*plow < a) { 352 | (*phigh)++; 353 | } 354 | *phigh += b; 355 | } 356 | 357 | static void neg128(uint64_t *plow, uint64_t *phigh) 358 | { 359 | *plow = ~*plow; 360 | *phigh = ~*phigh; 361 | add128(plow, phigh, 1, 0); 362 | } 363 | 364 | /* return TRUE if overflow */ 365 | static int div64(uint64_t *plow, uint64_t *phigh, uint64_t b) 366 | { 367 | uint64_t q, r, a1, a0; 368 | int i, qb, ab; 369 | 370 | a0 = *plow; 371 | a1 = *phigh; 372 | if (a1 == 0) { 373 | q = a0 / b; 374 | r = a0 % b; 375 | *plow = q; 376 | *phigh = r; 377 | } else { 378 | if (a1 >= b) { 379 | return 1; 380 | } 381 | /* XXX: use a better algorithm */ 382 | for (i = 0; i < 64; i++) { 383 | ab = a1 >> 63; 384 | a1 = (a1 << 1) | (a0 >> 63); 385 | if (ab || a1 >= b) { 386 | a1 -= b; 387 | qb = 1; 388 | } else { 389 | qb = 0; 390 | } 391 | a0 = (a0 << 1) | qb; 392 | } 393 | #if defined(DEBUG_MULDIV) 394 | printf("div: 0x%016" PRIx64 "%016" PRIx64 " / 0x%016" PRIx64 395 | ": q=0x%016" PRIx64 " r=0x%016" PRIx64 "\n", 396 | *phigh, *plow, b, a0, a1); 397 | #endif 398 | *plow = a0; 399 | *phigh = a1; 400 | } 401 | return 0; 402 | } 403 | 404 | /* return TRUE if overflow */ 405 | static int idiv64(uint64_t *plow, uint64_t *phigh, int64_t b) 406 | { 407 | int sa, sb; 408 | 409 | sa = ((int64_t)*phigh < 0); 410 | if (sa) { 411 | neg128(plow, phigh); 412 | } 413 | sb = (b < 0); 414 | if (sb) { 415 | b = -b; 416 | } 417 | if (div64(plow, phigh, b) != 0) { 418 | return 1; 419 | } 420 | if (sa ^ sb) { 421 | if (*plow > (1ULL << 63)) { 422 | return 1; 423 | } 424 | *plow = -*plow; 425 | } else { 426 | if (*plow >= (1ULL << 63)) { 427 | return 1; 428 | } 429 | } 430 | if (sa) { 431 | *phigh = -*phigh; 432 | } 433 | return 0; 434 | } 435 | 436 | void helper_divq_EAX(CPUX86State *env, target_ulong t0) 437 | { 438 | uint64_t r0, r1; 439 | 440 | if (t0 == 0) { 441 | raise_exception_ra(env, EXCP00_DIVZ, GETPC()); 442 | } 443 | r0 = env->regs[R_EAX]; 444 | r1 = env->regs[R_EDX]; 445 | if (div64(&r0, &r1, t0)) { 446 | raise_exception_ra(env, EXCP00_DIVZ, GETPC()); 447 | } 448 | env->regs[R_EAX] = r0; 449 | env->regs[R_EDX] = r1; 450 | } 451 | 452 | void helper_idivq_EAX(CPUX86State *env, target_ulong t0) 453 | { 454 | uint64_t r0, r1; 455 | 456 | if (t0 == 0) { 457 | raise_exception_ra(env, EXCP00_DIVZ, GETPC()); 458 | } 459 | r0 = env->regs[R_EAX]; 460 | r1 = env->regs[R_EDX]; 461 | if (idiv64(&r0, &r1, t0)) { 462 | raise_exception_ra(env, EXCP00_DIVZ, GETPC()); 463 | } 464 | env->regs[R_EAX] = r0; 465 | env->regs[R_EDX] = r1; 466 | } 467 | #endif 468 | 469 | #if TARGET_LONG_BITS == 32 470 | # define ctztl ctz32 471 | # define clztl clz32 472 | #else 473 | # define ctztl ctz64 474 | # define clztl clz64 475 | #endif 476 | 477 | target_ulong helper_pdep(target_ulong src, target_ulong mask) 478 | { 479 | target_ulong dest = 0; 480 | int i, o; 481 | 482 | for (i = 0; mask != 0; i++) { 483 | o = ctztl(mask); 484 | mask &= mask - 1; 485 | dest |= ((src >> i) & 1) << o; 486 | } 487 | return dest; 488 | } 489 | 490 | target_ulong helper_pext(target_ulong src, target_ulong mask) 491 | { 492 | target_ulong dest = 0; 493 | int i, o; 494 | 495 | for (o = 0; mask != 0; o++) { 496 | i = ctztl(mask); 497 | mask &= mask - 1; 498 | dest |= ((src >> i) & 1) << o; 499 | } 500 | return dest; 501 | } 502 | 503 | #define SHIFT 0 504 | #include "shift_helper_template.h" 505 | #undef SHIFT 506 | 507 | #define SHIFT 1 508 | #include "shift_helper_template.h" 509 | #undef SHIFT 510 | 511 | #define SHIFT 2 512 | #include "shift_helper_template.h" 513 | #undef SHIFT 514 | 515 | #ifdef TARGET_X86_64 516 | #define SHIFT 3 517 | #include "shift_helper_template.h" 518 | #undef SHIFT 519 | #endif 520 | 521 | /* Test that BIT is enabled in CR4. If not, raise an illegal opcode 522 | exception. This reduces the requirements for rare CR4 bits being 523 | mapped into HFLAGS. */ 524 | void helper_cr4_testbit(CPUX86State *env, uint32_t bit) 525 | { 526 | if (unlikely((env->cr[4] & bit) == 0)) { 527 | raise_exception_ra(env, EXCP06_ILLOP, GETPC()); 528 | } 529 | } 530 | 531 | target_ulong HELPER(rdrand)(CPUX86State *env) 532 | { 533 | Error *err = NULL; 534 | target_ulong ret; 535 | 536 | if (qemu_guest_getrandom(&ret, sizeof(ret), &err) < 0) { 537 | #if 0 538 | qemu_log_mask(LOG_UNIMP, "rdrand: Crypto failure: %s", 539 | error_get_pretty(err)); 540 | error_free(err); 541 | #endif 542 | /* Failure clears CF and all other flags, and returns 0. */ 543 | env->cc_src = 0; 544 | return 0; 545 | } 546 | 547 | /* Success sets CF and clears all others. */ 548 | env->cc_src = CC_C; 549 | return ret; 550 | } 551 | -------------------------------------------------------------------------------- /sym_helpers/qemu-isolate-build.h: -------------------------------------------------------------------------------- 1 | #define NEED_CPU_H 2 | #define HW_POISON_H -------------------------------------------------------------------------------- /sym_helpers/sym_check_helpers.h: -------------------------------------------------------------------------------- 1 | #ifdef SYM_HELPERS 2 | 3 | extern const char* tcg_find_helper(TCGContext* s, uintptr_t val); 4 | 5 | static inline void sym_check_helpers(TranslationBlock *tb, TCGContext *tcg_ctx) 6 | { 7 | TCGOp *op; 8 | QTAILQ_FOREACH(op, &tcg_ctx->ops, link) 9 | { 10 | switch (op->opc) 11 | { 12 | 13 | case INDEX_op_call: 14 | { 15 | const char* helper_name = tcg_find_helper( 16 | tcg_ctx, op->args[TCGOP_CALLI(op) + TCGOP_CALLO(op)]); 17 | int len = strlen(helper_name); 18 | if ( 19 | strncmp(helper_name, "sym_", 4) != 0 20 | && strcmp(helper_name, "lookup_tb_ptr") != 0 21 | && strcmp(helper_name, "syscall") != 0 22 | && strcmp(helper_name, "rechecking_single_step") != 0 23 | && strcmp(helper_name, "cpuid") != 0 24 | && strcmp(helper_name, "rdtsc") != 0 25 | && strcmp(helper_name, "muluh_i64") != 0 26 | && !( 27 | len > 11 // "_symbolized" 28 | && strcmp((helper_name + len - 11), "_symbolized") == 0 29 | ) 30 | ) 31 | printf("[0x%lx] Helper: %s\n", tb->pc, helper_name); 32 | break; 33 | } 34 | 35 | default: 36 | break; 37 | } 38 | } 39 | } 40 | 41 | #endif -------------------------------------------------------------------------------- /sym_helpers/sym_helpers.h: -------------------------------------------------------------------------------- 1 | #ifndef SYM_HELPERS_H 2 | #define SYM_HELPERS_H 3 | 4 | #define SYM(x) x ## _symbolized 5 | #define SYM_HELPERS 6 | 7 | #endif // SYM_HELPERS_H -------------------------------------------------------------------------------- /sym_helpers/wrappers.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "qemu-isolate-build.h" 5 | 6 | #include "qemu/osdep.h" 7 | #include "cpu.h" 8 | 9 | extern void _sym_set_parameter_expression(uint8_t index, void *expr); 10 | extern void *_sym_get_parameter_expression(uint8_t index); 11 | extern void *_sym_get_return_expression(void); 12 | extern char *_sym_expr_to_string(void *); 13 | extern void *_sym_read_memory(void* addr_expr, uint8_t *addr, size_t length, bool little_endian); 14 | extern void _sym_write_memory(void* addr_expr, uint8_t *addr, size_t length, void* expr, 15 | bool little_endian); 16 | 17 | void helper_sym_init_args_2_void(void *arg1, void *arg2) 18 | { 19 | _sym_set_parameter_expression(0, arg1); 20 | _sym_set_parameter_expression(1, arg2); 21 | } 22 | 23 | void* helper_sym_init_args_2(void *arg1, void *arg2) 24 | { 25 | #if 0 26 | if (arg2) 27 | { 28 | const char *s_expr = _sym_expr_to_string(arg2); 29 | printf("EXPR2: %s\n", s_expr); 30 | } 31 | #endif 32 | 33 | _sym_set_parameter_expression(0, arg1); 34 | _sym_set_parameter_expression(1, arg2); 35 | 36 | return NULL; // fake arg for _sym_get_return_expression 37 | } 38 | 39 | void helper_sym_init_args_3_void(void *arg1, void *arg2, void *arg3) 40 | { 41 | _sym_set_parameter_expression(0, arg1); 42 | _sym_set_parameter_expression(1, arg2); 43 | _sym_set_parameter_expression(2, arg3); 44 | } 45 | 46 | void* helper_sym_init_args_4(void *arg1, void *arg2, 47 | void *arg3, void *arg4) 48 | { 49 | _sym_set_parameter_expression(0, arg1); 50 | _sym_set_parameter_expression(1, arg2); 51 | _sym_set_parameter_expression(2, arg3); 52 | _sym_set_parameter_expression(3, arg4); 53 | 54 | return NULL; // fake arg for _sym_get_return_expression 55 | } 56 | 57 | void *helper_sym_set_return_value(void* fake_arg) 58 | { 59 | void *expr = _sym_get_return_expression(); 60 | #if 0 61 | if (expr) 62 | { 63 | const char *s_expr = _sym_expr_to_string(expr); 64 | printf("RETURN EXPR: %s\n", s_expr); 65 | } 66 | #endif 67 | return expr; 68 | } 69 | 70 | void helper_sym_store_mem_reg(CPUX86State *env, void *expr, uint64_t reg) 71 | { 72 | #if 0 73 | if (expr) 74 | { 75 | const char *s_expr = _sym_expr_to_string(expr); 76 | printf("EXPR for reg %lu: %s\n", reg, s_expr); 77 | } 78 | #endif 79 | 80 | _sym_write_memory(NULL, (uint8_t*) &env->regs[reg], 8, expr, true); 81 | } 82 | 83 | void *helper_sym_load_mem_reg(CPUX86State *env, uint64_t reg) 84 | { 85 | // printf("CHECKING EXPR for reg %lu at %p\n", reg, &env->regs[reg]); 86 | void *expr = _sym_read_memory(NULL, (uint8_t*) &env->regs[reg], 8, true); 87 | #if 0 88 | if (expr) 89 | { 90 | const char *s_expr = _sym_expr_to_string(expr); 91 | printf("RETURN EXPR for reg %lu: %s\n", reg, s_expr); 92 | } 93 | #endif 94 | _sym_write_memory(NULL, (uint8_t*) &env->regs[reg], 8, NULL, true); 95 | return expr; 96 | } 97 | 98 | void helper_sym_dbg(CPUX86State *env) 99 | { 100 | printf("Executing at %lx\n", env->eip); 101 | } -------------------------------------------------------------------------------- /tests/.gitkeepme: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/season-lab/SymFusion/a3ceaa28f0fcfe951de26f14d122ccfa9941f967/tests/.gitkeepme -------------------------------------------------------------------------------- /tests/example/Makefile: -------------------------------------------------------------------------------- 1 | all: 2 | clang-10 -o example example.c 3 | ../../symcc-hybrid/build/symcc -o example example.c 4 | -------------------------------------------------------------------------------- /tests/example/example.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | int magic_check(int p){ 5 | if (p == 0xDEADBEEF) 6 | return 1; 7 | else 8 | return 0; 9 | } 10 | 11 | int get_input(char* fname) { 12 | FILE* fp = fopen(fname, "r"); 13 | if (fp == NULL) exit(EXIT_FAILURE); 14 | int data; 15 | int r = fread(&data, 1, sizeof(data), fp); 16 | if (r != sizeof(data)) exit(EXIT_FAILURE); 17 | fclose(fp); 18 | return data; 19 | } 20 | 21 | int main(int argc, char* argv[]) { 22 | 23 | if (argc != 2) exit(EXIT_FAILURE); 24 | int input = get_input(argv[1]); // read four bytes from the input file 25 | if (magic_check(input)) { 26 | printf("Correct value [%x] :)\n", input); 27 | } else { 28 | printf("Wrong value [%x] :(\n", input); 29 | } 30 | 31 | return 0; 32 | } -------------------------------------------------------------------------------- /tests/example/inputs/seed.dat: -------------------------------------------------------------------------------- 1 | AAAA 2 | -------------------------------------------------------------------------------- /tests/example/start-rust-aflpp.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | /afl/afl-clang-fast example.c -o example.afl 4 | ../../symcc-hybrid/build/symcc -o example.symfusion example.c 5 | 6 | export OUTPUT=`pwd`/out 7 | rm -rf ${OUTPUT} 8 | mkdir ${OUTPUT} 9 | 10 | export HYBRID_CONF_FILE=$OUTPUT/hybrid.conf 11 | export LD_BIND_NOW=1 12 | export SYMFUSION_HYBRID=1 13 | export WRAPPER=`pwd`/../../symqemu-hybrid/x86_64-linux-user/symqemu-x86_64 14 | export SYMCC_ENABLE_LINEARIZATION=1 15 | 16 | export SEEDS=`pwd`/inputs 17 | 18 | ../../runner/symfusion.py -g ${HYBRID_CONF_FILE} -i ${SEEDS} -o ${OUTPUT}/concolic -- ./example.symfusion @@ 19 | rm -rf ${OUTPUT}/concolic 20 | 21 | /afl/afl-fuzz -M afl-master -t 5000 -m 100M -i ${SEEDS} -o ${OUTPUT} -- ./example.afl @@ >/dev/null 2>&1 & 22 | 23 | FUZZER_PID=$! 24 | while ps -p $FUZZER_PID > /dev/null 2>&1 && \ 25 | [[ ! -f "${OUTPUT}/afl-master/fuzzer_stats" ]]; do 26 | echo "Waiting fuzzer to start..." && sleep 1 27 | done 28 | 29 | while ps -p $FUZZER_PID > /dev/null 2>&1 && \ 30 | [[ ! -f "${OUTPUT}/afl-master/fuzz_bitmap" ]]; do 31 | echo "Waiting fuzzer to create bitmap..." && sleep 1 32 | done 33 | 34 | ../../symcc-hybrid/build/symcc_fuzzing_helper -a afl-master -o ${OUTPUT} -n concolic -- ${WRAPPER} ./example.symfusion @@ -------------------------------------------------------------------------------- /tests/microbenchmarks/.gitignore: -------------------------------------------------------------------------------- 1 | */out 2 | */out_symcc 3 | */out_symqemu 4 | */main.* 5 | */*.so -------------------------------------------------------------------------------- /tests/microbenchmarks/01-cpu-intensive-loop/Makefile: -------------------------------------------------------------------------------- 1 | BINARY=./main 2 | BINARY_ARGS= 3 | BINARY_SEED=./input.dat 4 | BINARY_ARGS_SEED=${BINARY_SEED} 5 | 6 | build: 7 | ../../../symcc-hybrid/build/symcc main.c -o main.symfusion 8 | ../../../original/symcc/build/symcc main.c -o main.symcc 9 | clang-10 main.c -o main.symqemu 10 | 11 | include ../Makefile-stdin.inc 12 | -------------------------------------------------------------------------------- /tests/microbenchmarks/01-cpu-intensive-loop/input.dat: -------------------------------------------------------------------------------- 1 | A 2 | -------------------------------------------------------------------------------- /tests/microbenchmarks/01-cpu-intensive-loop/lib.c: -------------------------------------------------------------------------------- 1 | int identity(int x) { return x; } -------------------------------------------------------------------------------- /tests/microbenchmarks/01-cpu-intensive-loop/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #define N 15000 5 | 6 | int identity(int x); 7 | 8 | int main() { 9 | 10 | unsigned char x; 11 | if (read(0, &x, sizeof(x)) != sizeof(x)) { 12 | printf("Failed to read x\n"); 13 | return -1; 14 | } 15 | 16 | int count = 0; 17 | for (int i = 0; i < N; i++) 18 | count += x; 19 | 20 | if (count == 2*N) printf("OK\n"); 21 | else printf("KO\n"); 22 | 23 | return 0; 24 | } 25 | -------------------------------------------------------------------------------- /tests/microbenchmarks/02-external-function/Makefile: -------------------------------------------------------------------------------- 1 | BINARY=./main 2 | BINARY_ARGS= 3 | BINARY_SEED=./input.dat 4 | BINARY_ARGS_SEED=${BINARY_SEED} 5 | 6 | build: 7 | clang-10 lib.c -shared -o libA.so -fPIC 8 | ../../../symcc-hybrid/build/symcc main.c -o main.symfusion -L. -lA 9 | ../../../original/symcc/build/symcc main.c -o main.symcc -L. -lA 10 | clang-10 main.c -o main.symqemu -L. -lA 11 | 12 | include ../Makefile-stdin.inc 13 | -------------------------------------------------------------------------------- /tests/microbenchmarks/02-external-function/input.dat: -------------------------------------------------------------------------------- 1 | A 2 | -------------------------------------------------------------------------------- /tests/microbenchmarks/02-external-function/lib.c: -------------------------------------------------------------------------------- 1 | int identity(int x) { return x; } -------------------------------------------------------------------------------- /tests/microbenchmarks/02-external-function/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #define N 15000 5 | 6 | int identity(int x); 7 | 8 | int main() { 9 | 10 | unsigned char x; 11 | if (read(0, &x, sizeof(x)) != sizeof(x)) { 12 | printf("Failed to read x\n"); 13 | return -1; 14 | } 15 | 16 | int r = identity(x); 17 | if (r == 23) printf("OK\n"); 18 | else printf("KO\n"); 19 | 20 | return 0; 21 | } 22 | -------------------------------------------------------------------------------- /tests/microbenchmarks/03-x86-division-b/Makefile: -------------------------------------------------------------------------------- 1 | BINARY=./main 2 | BINARY_ARGS= 3 | BINARY_SEED=./input.dat 4 | BINARY_ARGS_SEED=${BINARY_SEED} 5 | 6 | build: 7 | clang-10 lib.c -shared -o libA.so -fPIC 8 | ../../../symcc-hybrid/build/symcc main.c -o main.symfusion -L. -lA 9 | ../../../original/symcc/build/symcc main.c -o main.symcc -L. -lA 10 | clang-10 main.c -o main.symqemu -L. -lA 11 | 12 | include ../Makefile-stdin.inc 13 | -------------------------------------------------------------------------------- /tests/microbenchmarks/03-x86-division-b/input.dat: -------------------------------------------------------------------------------- 1 | A 2 | -------------------------------------------------------------------------------- /tests/microbenchmarks/03-x86-division-b/lib.c: -------------------------------------------------------------------------------- 1 | int identity(int x) { return x / 23; } 2 | -------------------------------------------------------------------------------- /tests/microbenchmarks/03-x86-division-b/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #define N 15000 5 | 6 | int identity(int x); 7 | 8 | int main() { 9 | 10 | unsigned char x; 11 | if (read(0, &x, sizeof(x)) != sizeof(x)) { 12 | printf("Failed to read x\n"); 13 | return -1; 14 | } 15 | 16 | int y = identity(x); // x / 23; 17 | 18 | if (y == 3) printf("OK\n"); 19 | else printf("KO\n"); 20 | 21 | return 0; 22 | } 23 | -------------------------------------------------------------------------------- /tests/microbenchmarks/03-x86-division/Makefile: -------------------------------------------------------------------------------- 1 | BINARY=./main 2 | BINARY_ARGS= 3 | BINARY_SEED=./input.dat 4 | BINARY_ARGS_SEED=${BINARY_SEED} 5 | 6 | build: 7 | ../../../symcc-hybrid/build/symcc main.c -o main.symfusion 8 | ../../../original/symcc/build/symcc main.c -o main.symcc 9 | clang-10 main.c -o main.symqemu 10 | 11 | include ../Makefile-stdin.inc 12 | -------------------------------------------------------------------------------- /tests/microbenchmarks/03-x86-division/input.dat: -------------------------------------------------------------------------------- 1 | A 2 | -------------------------------------------------------------------------------- /tests/microbenchmarks/03-x86-division/lib.c: -------------------------------------------------------------------------------- 1 | int identity(int x) { return x; } -------------------------------------------------------------------------------- /tests/microbenchmarks/03-x86-division/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #define N 15000 5 | 6 | int identity(int x); 7 | 8 | int main() { 9 | 10 | unsigned char x; 11 | if (read(0, &x, sizeof(x)) != sizeof(x)) { 12 | printf("Failed to read x\n"); 13 | return -1; 14 | } 15 | 16 | int y = x / 23; 17 | 18 | if (y == 3) printf("OK\n"); 19 | else printf("KO\n"); 20 | 21 | return 0; 22 | } 23 | -------------------------------------------------------------------------------- /tests/microbenchmarks/04-ntohs/Makefile: -------------------------------------------------------------------------------- 1 | BINARY=./main 2 | BINARY_ARGS= 3 | BINARY_SEED=./input.dat 4 | BINARY_ARGS_SEED=${BINARY_SEED} 5 | 6 | build: 7 | ../../../symcc-hybrid/build/symcc main.c -o main.symfusion 8 | ../../../original/symcc/build/symcc main.c -o main.symcc 9 | clang-10 main.c -o main.symqemu 10 | 11 | include ../Makefile-stdin.inc 12 | -------------------------------------------------------------------------------- /tests/microbenchmarks/04-ntohs/input.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/season-lab/SymFusion/a3ceaa28f0fcfe951de26f14d122ccfa9941f967/tests/microbenchmarks/04-ntohs/input.dat -------------------------------------------------------------------------------- /tests/microbenchmarks/04-ntohs/lib.c: -------------------------------------------------------------------------------- 1 | int identity(int x) { return x; } -------------------------------------------------------------------------------- /tests/microbenchmarks/04-ntohs/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | int main() { 6 | 7 | short data; 8 | read(0, &data, 2); 9 | data = ntohs(data); 10 | 11 | if (data == 0x23) printf("OK\n"); 12 | else printf("KO\n"); 13 | 14 | return 0; 15 | } 16 | -------------------------------------------------------------------------------- /tests/microbenchmarks/05-strlen/Makefile: -------------------------------------------------------------------------------- 1 | BINARY=./main 2 | BINARY_ARGS= 3 | BINARY_SEED=./input.dat 4 | BINARY_ARGS_SEED=${BINARY_SEED} 5 | 6 | build: 7 | ../../../symcc-hybrid/build/symcc main.c -o main.symfusion 8 | ../../../original/symcc/build/symcc main.c -o main.symcc 9 | clang-10 main.c -o main.symqemu 10 | 11 | include ../Makefile-stdin.inc 12 | -------------------------------------------------------------------------------- /tests/microbenchmarks/05-strlen/input.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/season-lab/SymFusion/a3ceaa28f0fcfe951de26f14d122ccfa9941f967/tests/microbenchmarks/05-strlen/input.dat -------------------------------------------------------------------------------- /tests/microbenchmarks/05-strlen/input.dat.original: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/season-lab/SymFusion/a3ceaa28f0fcfe951de26f14d122ccfa9941f967/tests/microbenchmarks/05-strlen/input.dat.original -------------------------------------------------------------------------------- /tests/microbenchmarks/05-strlen/lib.c: -------------------------------------------------------------------------------- 1 | int identity(int x) { return x; } -------------------------------------------------------------------------------- /tests/microbenchmarks/05-strlen/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | int main() { 6 | 7 | char x[64]; 8 | if (read(0, &x, sizeof(x)) <= 0) { 9 | printf("Failed to read x\n"); 10 | return -1; 11 | } 12 | 13 | // char* s = "AAAAAAAAAAAAAAAAAAAAAAAA"; 14 | //if (strcmp(s, x) == 0) printf("OK\n"); 15 | if (strlen(x) == 3) printf("OK\n"); 16 | else printf("KO\n"); 17 | 18 | return 0; 19 | } 20 | -------------------------------------------------------------------------------- /tests/microbenchmarks/06-cost-context-switch/Makefile: -------------------------------------------------------------------------------- 1 | BINARY=./main 2 | BINARY_ARGS= 3 | BINARY_SEED=./input.dat 4 | BINARY_ARGS_SEED=${BINARY_SEED} 5 | 6 | build: 7 | ../../../symcc-hybrid/build/symcc main.c -o main.symfusion 8 | ../../../original/symcc/build/symcc main.c -o main.symcc 9 | clang-10 main.c -o main.symqemu 10 | 11 | include ../Makefile-stdin.inc 12 | -------------------------------------------------------------------------------- /tests/microbenchmarks/06-cost-context-switch/input.dat: -------------------------------------------------------------------------------- 1 | A 2 | -------------------------------------------------------------------------------- /tests/microbenchmarks/06-cost-context-switch/lib.c: -------------------------------------------------------------------------------- 1 | int identity(int x) { return x; } -------------------------------------------------------------------------------- /tests/microbenchmarks/06-cost-context-switch/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #define N 15000 6 | 7 | int main() { 8 | 9 | unsigned char x; 10 | if (read(0, &x, sizeof(x)) != sizeof(x)) { 11 | printf("Failed to read x\n"); 12 | return -1; 13 | } 14 | 15 | void* blocks[N]; 16 | for (int i = 0; i < N; i++) 17 | blocks[i] = malloc(2); 18 | 19 | if (x == 23) printf("OK\n"); 20 | else printf("KO\n"); 21 | 22 | return 0; 23 | } -------------------------------------------------------------------------------- /tests/microbenchmarks/Makefile-stdin.inc: -------------------------------------------------------------------------------- 1 | ifndef DIRECTORY 2 | DIRECTORY=${BINARY} 3 | endif 4 | 5 | clean: 6 | mkdir out${SYMFUSION_FORKSERVER} || echo 7 | rm -rf out${SYMFUSION_FORKSERVER}/* || echo 8 | touch out${SYMFUSION_FORKSERVER}/.symfusion 9 | 10 | symfusion: clean 11 | LD_LIBRARY_PATH=`pwd` ../../../runner/symfusion.py -i ${BINARY_SEED} -o out -d output -- ./${BINARY}.symfusion ${BINARY_ARGS} 12 | 13 | symcc: 14 | rm -rf out_symcc || echo 15 | mkdir out_symcc 16 | LD_LIBRARY_PATH=`pwd` SYMCC_OUTPUT_DIR=./out_symcc ../../../utils/time.sh ./${BINARY}.symcc ${BINARY_ARGS} < ${BINARY_SEED} 17 | 18 | symqemu: 19 | rm -rf out_symqemu || echo 20 | mkdir out_symqemu 21 | LD_LIBRARY_PATH=`pwd` SYMCC_OUTPUT_DIR=./out_symqemu ../../../utils/time.sh ../../../original/symqemu/x86_64-linux-user/symqemu-x86_64 ${BINARY}.symqemu ${BINARY_ARGS} < ${BINARY_SEED} -------------------------------------------------------------------------------- /utils/Makefile: -------------------------------------------------------------------------------- 1 | all: 2 | echo "Targets: comparer" 3 | 4 | comparer: compare_bitmaps.c 5 | gcc -o comparer -O2 compare_bitmaps.c 6 | -------------------------------------------------------------------------------- /utils/compare_bitmaps.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #define BITMAP_SIZE (30 * 65536) 6 | 7 | char bitmap_src[BITMAP_SIZE] = { 0 }; 8 | char bitmap_dst[BITMAP_SIZE] = { 0 }; 9 | 10 | int unique_counter = 0; 11 | short uniques[BITMAP_SIZE] = { 0 }; 12 | 13 | static void load_bitmap(char* path, char* bitmap, int bitmap_size) 14 | { 15 | // printf("Loading bitmap: %s\n", path); 16 | FILE* fp = fopen(path, "r"); 17 | if (!fp) { 18 | printf("Cannot open: %s\n", path); 19 | exit(1); 20 | } 21 | int count = 0; 22 | while (count < bitmap_size) { 23 | int res = fread(bitmap + count, 1, bitmap_size - count, fp); 24 | if (res <= 0) break; 25 | count += res; 26 | } 27 | if (count < bitmap_size) { 28 | printf("Cannot read full bitmap: %s [%d]\n", path, count); 29 | exit(1); 30 | } 31 | fclose(fp); 32 | } 33 | 34 | static void save_bitmap(char* path, char* bitmap, int bitmap_size) 35 | { 36 | // printf("Saving bitmap: %s\n", path); 37 | FILE* fp = fopen(path, "w"); 38 | if (!fp) { 39 | printf("Cannot open: %s\n", path); 40 | exit(1); 41 | } 42 | int count = 0; 43 | while (count < bitmap_size) { 44 | int res = fwrite(bitmap + count, 1, bitmap_size - count, fp); 45 | if (res <= 0) break; 46 | count += res; 47 | } 48 | if (count < bitmap_size) { 49 | printf("Cannot write full bitmap: %s [%d]\n", path, count); 50 | exit(1); 51 | } 52 | fclose(fp); 53 | } 54 | 55 | static int compare_bitmaps(char* bitmap_src, char* bitmap_dst, char* report_file, int bitmap_size) 56 | { 57 | int new_coverage = 0; 58 | int is_interesting = 0; 59 | for (int i = 0; i < bitmap_size; i++) { 60 | if ((bitmap_src[i] | bitmap_dst[i]) != bitmap_dst[i]) { 61 | if (report_file) uniques[unique_counter++] = (short) i; 62 | is_interesting = 1; 63 | if (bitmap_dst[i] == 0) 64 | new_coverage = 1; 65 | bitmap_dst[i] |= bitmap_src[i]; 66 | // printf("Unique ID: %d\n", i); 67 | } 68 | } 69 | if (is_interesting && report_file != NULL) { 70 | FILE* fp = fopen(report_file, "w"); 71 | if (!fp) { 72 | printf("Cannot open: %s\n", report_file); 73 | exit(1); 74 | } 75 | int count = 0; 76 | while (count < (unique_counter * sizeof(short))) { 77 | int res = fwrite(uniques + count, 1, (unique_counter * sizeof(short)) - count, fp); 78 | if (res <= 0) break; 79 | count += res; 80 | } 81 | if (count < (unique_counter * sizeof(short))) { 82 | printf("Cannot write full bitmap: %s [%d]\n", report_file, count); 83 | exit(1); 84 | } 85 | fclose(fp); 86 | } 87 | return is_interesting + new_coverage; 88 | } 89 | 90 | int main(int argc, char* argv[]) 91 | { 92 | if (argc != 4 && argc != 5) { 93 | printf("Usage: %s []\n", argv[0]); 94 | exit(-1); 95 | } 96 | 97 | // printf("%s %s %s %s %s\n", argv[0], argv[1], argv[2], argv[3], argv[4]); 98 | 99 | int bitmap_size = strtol(argv[3], NULL, 10); 100 | if (bitmap_size > BITMAP_SIZE) { 101 | printf("Unexpected bitmap size. Fix me!"); 102 | exit(-1); 103 | } 104 | 105 | load_bitmap(argv[1], bitmap_src, bitmap_size); 106 | load_bitmap(argv[2], bitmap_dst, bitmap_size); 107 | int r = compare_bitmaps(bitmap_src, bitmap_dst, argv[4], bitmap_size); 108 | if (r) save_bitmap(argv[2], bitmap_dst, bitmap_size); 109 | return r; 110 | } -------------------------------------------------------------------------------- /utils/comparer: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/season-lab/SymFusion/a3ceaa28f0fcfe951de26f14d122ccfa9941f967/utils/comparer -------------------------------------------------------------------------------- /utils/compiler_runtime.cpp: -------------------------------------------------------------------------------- 1 | // This file is part of SymCC. 2 | // 3 | // SymCC is free software: you can redistribute it and/or modify it under the 4 | // terms of the GNU General Public License as published by the Free Software 5 | // Foundation, either version 3 of the License, or (at your option) any later 6 | // version. 7 | // 8 | // SymCC is distributed in the hope that it will be useful, but WITHOUT ANY 9 | // WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 10 | // A PARTICULAR PURPOSE. See the GNU General Public License for more details. 11 | // 12 | // You should have received a copy of the GNU General Public License along with 13 | // SymCC. If not, see . 14 | 15 | #include "Runtime.h" 16 | 17 | #include 18 | #include 19 | #include 20 | 21 | using namespace llvm; 22 | 23 | namespace { 24 | 25 | template 26 | SymFnT import(llvm::Module &M, llvm::StringRef name, llvm::Type *ret, 27 | ArgsTy... args) { 28 | #if LLVM_VERSION_MAJOR >= 9 && LLVM_VERSION_MAJOR < 11 29 | return M.getOrInsertFunction(name, ret, args...).getCallee(); 30 | #else 31 | return M.getOrInsertFunction(name, ret, args...); 32 | #endif 33 | } 34 | 35 | } // namespace 36 | 37 | Runtime::Runtime(Module &M) { 38 | IRBuilder<> IRB(M.getContext()); 39 | auto *intPtrType = M.getDataLayout().getIntPtrType(M.getContext()); 40 | auto *ptrT = IRB.getInt8PtrTy(); 41 | auto *int8T = IRB.getInt8Ty(); 42 | auto *voidT = IRB.getVoidTy(); 43 | 44 | buildInteger = import(M, "_sym_build_integer", ptrT, IRB.getInt64Ty(), int8T); 45 | buildInteger128 = import(M, "_sym_build_integer128", ptrT, IRB.getInt64Ty(), 46 | IRB.getInt64Ty()); 47 | buildFloat = 48 | import(M, "_sym_build_float", ptrT, IRB.getDoubleTy(), IRB.getInt1Ty()); 49 | buildNullPointer = import(M, "_sym_build_null_pointer", ptrT); 50 | buildTrue = import(M, "_sym_build_true", ptrT); 51 | buildFalse = import(M, "_sym_build_false", ptrT); 52 | buildBool = import(M, "_sym_build_bool", ptrT, IRB.getInt1Ty()); 53 | buildSExt = import(M, "_sym_build_sext", ptrT, ptrT, int8T); 54 | buildZExt = import(M, "_sym_build_zext", ptrT, ptrT, int8T); 55 | buildTrunc = import(M, "_sym_build_trunc", ptrT, ptrT, int8T); 56 | buildBswap = import(M, "_sym_build_bswap", ptrT, ptrT); 57 | buildIntToFloat = import(M, "_sym_build_int_to_float", ptrT, ptrT, 58 | IRB.getInt1Ty(), IRB.getInt1Ty()); 59 | buildFloatToFloat = 60 | import(M, "_sym_build_float_to_float", ptrT, ptrT, IRB.getInt1Ty()); 61 | buildBitsToFloat = 62 | import(M, "_sym_build_bits_to_float", ptrT, ptrT, IRB.getInt1Ty()); 63 | buildFloatToBits = import(M, "_sym_build_float_to_bits", ptrT, ptrT); 64 | buildFloatToSignedInt = 65 | import(M, "_sym_build_float_to_signed_integer", ptrT, ptrT, int8T); 66 | buildFloatToUnsignedInt = 67 | import(M, "_sym_build_float_to_unsigned_integer", ptrT, ptrT, int8T); 68 | buildFloatAbs = import(M, "_sym_build_fp_abs", ptrT, ptrT); 69 | buildBoolAnd = import(M, "_sym_build_bool_and", ptrT, ptrT, ptrT); 70 | buildBoolOr = import(M, "_sym_build_bool_or", ptrT, ptrT, ptrT); 71 | buildBoolXor = import(M, "_sym_build_bool_xor", ptrT, ptrT, ptrT); 72 | buildBoolToBits = import(M, "_sym_build_bool_to_bits", ptrT, ptrT, int8T); 73 | pushPathConstraint = import(M, "_sym_push_path_constraint", voidT, ptrT, 74 | IRB.getInt1Ty(), intPtrType); 75 | 76 | setParameterExpression = 77 | import(M, "_sym_set_parameter_expression", voidT, int8T, ptrT); 78 | getParameterExpression = 79 | import(M, "_sym_get_parameter_expression", ptrT, int8T); 80 | setReturnExpression = import(M, "_sym_set_return_expression", voidT, ptrT); 81 | getReturnExpression = import(M, "_sym_get_return_expression", ptrT); 82 | 83 | #define LOAD_BINARY_OPERATOR_HANDLER(constant, name) \ 84 | binaryOperatorHandlers[Instruction::constant] = \ 85 | import(M, "_sym_build_" #name, ptrT, ptrT, ptrT); 86 | 87 | LOAD_BINARY_OPERATOR_HANDLER(Add, add) 88 | LOAD_BINARY_OPERATOR_HANDLER(Sub, sub) 89 | LOAD_BINARY_OPERATOR_HANDLER(Mul, mul) 90 | LOAD_BINARY_OPERATOR_HANDLER(UDiv, unsigned_div) 91 | LOAD_BINARY_OPERATOR_HANDLER(SDiv, signed_div) 92 | LOAD_BINARY_OPERATOR_HANDLER(URem, unsigned_rem) 93 | LOAD_BINARY_OPERATOR_HANDLER(SRem, signed_rem) 94 | LOAD_BINARY_OPERATOR_HANDLER(Shl, shift_left) 95 | LOAD_BINARY_OPERATOR_HANDLER(LShr, logical_shift_right) 96 | LOAD_BINARY_OPERATOR_HANDLER(AShr, arithmetic_shift_right) 97 | LOAD_BINARY_OPERATOR_HANDLER(And, and) 98 | LOAD_BINARY_OPERATOR_HANDLER(Or, or) 99 | LOAD_BINARY_OPERATOR_HANDLER(Xor, xor) 100 | 101 | // Floating-point arithmetic 102 | LOAD_BINARY_OPERATOR_HANDLER(FAdd, fp_add) 103 | LOAD_BINARY_OPERATOR_HANDLER(FSub, fp_sub) 104 | LOAD_BINARY_OPERATOR_HANDLER(FMul, fp_mul) 105 | LOAD_BINARY_OPERATOR_HANDLER(FDiv, fp_div) 106 | LOAD_BINARY_OPERATOR_HANDLER(FRem, fp_rem) 107 | 108 | #undef LOAD_BINARY_OPERATOR_HANDLER 109 | 110 | #define LOAD_COMPARISON_HANDLER(constant, name) \ 111 | comparisonHandlers[CmpInst::constant] = \ 112 | import(M, "_sym_build_" #name, ptrT, ptrT, ptrT); 113 | 114 | LOAD_COMPARISON_HANDLER(ICMP_EQ, equal) 115 | LOAD_COMPARISON_HANDLER(ICMP_NE, not_equal) 116 | LOAD_COMPARISON_HANDLER(ICMP_UGT, unsigned_greater_than) 117 | LOAD_COMPARISON_HANDLER(ICMP_UGE, unsigned_greater_equal) 118 | LOAD_COMPARISON_HANDLER(ICMP_ULT, unsigned_less_than) 119 | LOAD_COMPARISON_HANDLER(ICMP_ULE, unsigned_less_equal) 120 | LOAD_COMPARISON_HANDLER(ICMP_SGT, signed_greater_than) 121 | LOAD_COMPARISON_HANDLER(ICMP_SGE, signed_greater_equal) 122 | LOAD_COMPARISON_HANDLER(ICMP_SLT, signed_less_than) 123 | LOAD_COMPARISON_HANDLER(ICMP_SLE, signed_less_equal) 124 | 125 | // Floating-point comparisons 126 | LOAD_COMPARISON_HANDLER(FCMP_OGT, float_ordered_greater_than) 127 | LOAD_COMPARISON_HANDLER(FCMP_OGE, float_ordered_greater_equal) 128 | LOAD_COMPARISON_HANDLER(FCMP_OLT, float_ordered_less_than) 129 | LOAD_COMPARISON_HANDLER(FCMP_OLE, float_ordered_less_equal) 130 | LOAD_COMPARISON_HANDLER(FCMP_OEQ, float_ordered_equal) 131 | LOAD_COMPARISON_HANDLER(FCMP_ONE, float_ordered_not_equal) 132 | LOAD_COMPARISON_HANDLER(FCMP_ORD, float_ordered) 133 | LOAD_COMPARISON_HANDLER(FCMP_UNO, float_unordered) 134 | LOAD_COMPARISON_HANDLER(FCMP_UGT, float_unordered_greater_than) 135 | LOAD_COMPARISON_HANDLER(FCMP_UGE, float_unordered_greater_equal) 136 | LOAD_COMPARISON_HANDLER(FCMP_ULT, float_unordered_less_than) 137 | LOAD_COMPARISON_HANDLER(FCMP_ULE, float_unordered_less_equal) 138 | LOAD_COMPARISON_HANDLER(FCMP_UEQ, float_unordered_equal) 139 | LOAD_COMPARISON_HANDLER(FCMP_UNE, float_unordered_not_equal) 140 | 141 | #undef LOAD_COMPARISON_HANDLER 142 | 143 | memcpy = import(M, "_sym_memcpy", voidT, ptrT, ptrT, intPtrType); 144 | memset = import(M, "_sym_memset", voidT, ptrT, ptrT, intPtrType); 145 | memmove = import(M, "_sym_memmove", voidT, ptrT, ptrT, intPtrType); 146 | readMemory = 147 | import(M, "_sym_read_memory", ptrT, intPtrType, intPtrType, int8T); 148 | writeMemory = import(M, "_sym_write_memory", voidT, intPtrType, intPtrType, 149 | ptrT, int8T); 150 | buildExtract = import(M, "_sym_build_extract", ptrT, ptrT, IRB.getInt64Ty(), 151 | IRB.getInt64Ty(), int8T); 152 | 153 | notifyCall = import(M, "_sym_notify_call", voidT, intPtrType); 154 | notifyRet = import(M, "_sym_notify_ret", voidT, intPtrType); 155 | notifyBasicBlock = import(M, "_sym_notify_basic_block", voidT, intPtrType); 156 | } 157 | 158 | /// Decide whether a function is called symbolically. 159 | bool isInterceptedFunction(const Function &f) { 160 | static const StringSet<> kInterceptedFunctions = { 161 | // "malloc", "calloc", "mmap", "mmap64", 162 | "open", "read", "lseek", 163 | "lseek64", "fopen", "fopen64", "fread", "fseek", "fseeko", "rewind", 164 | "fseeko64", "getc", "ungetc", 165 | // "memcpy", "memset", "strncpy", 166 | // "strchr", "memcmp","memmove", "ntohl", 167 | "fgets", "fgetc"}; 168 | 169 | return (kInterceptedFunctions.count(f.getName()) > 0); 170 | } 171 | -------------------------------------------------------------------------------- /utils/qsym_solver.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "solver.h" 4 | 5 | static bool skip_query = false; 6 | 7 | namespace qsym { 8 | 9 | namespace { 10 | 11 | const uint64_t kUsToS = 1000000; 12 | const int kSessionIdLength = 32; 13 | const unsigned kSolverTimeout = 10000; // 10 seconds 14 | 15 | std::string toString6digit(INT32 val) { 16 | char buf[6 + 1]; // ndigit + 1 17 | snprintf(buf, 7, "%06d", val); 18 | buf[6] = '\0'; 19 | return std::string(buf); 20 | } 21 | 22 | uint64_t getTimeStamp() { 23 | struct timeval tv; 24 | gettimeofday(&tv, NULL); 25 | return tv.tv_sec * kUsToS + tv.tv_usec; 26 | } 27 | 28 | void parseConstSym(ExprRef e, Kind &op, ExprRef& expr_sym, ExprRef& expr_const) { 29 | for (INT32 i = 0; i < 2; i++) { 30 | expr_sym = e->getChild(i); 31 | expr_const = e->getChild(1 - i); 32 | if (!isConstant(expr_sym) 33 | && isConstant(expr_const)) { 34 | op = i == 0 ? e->kind() : swapKind(e->kind()); 35 | return; 36 | } 37 | } 38 | UNREACHABLE(); 39 | } 40 | 41 | void getCanonicalExpr(ExprRef e, 42 | ExprRef* canonical, 43 | llvm::APInt* adjustment=NULL) { 44 | ExprRef first = NULL; 45 | ExprRef second = NULL; 46 | // e == Const + Sym --> canonical == Sym 47 | switch (e->kind()) { 48 | // TODO: handle Sub 49 | case Add: 50 | first = e->getFirstChild(); 51 | second = e->getSecondChild(); 52 | if (isConstant(first)) { 53 | *canonical = second; 54 | if (adjustment != NULL) 55 | *adjustment = 56 | static_pointer_cast(first)->value(); 57 | return; 58 | case Sub: 59 | // C_0 - Sym 60 | first = e->getFirstChild(); 61 | second = e->getSecondChild(); 62 | // XXX: need to handle reference count 63 | if (isConstant(first)) { 64 | *canonical = g_expr_builder->createNeg(second); 65 | if (adjustment != NULL) 66 | *adjustment = static_pointer_cast(first)->value(); 67 | return; 68 | } 69 | } 70 | default: 71 | break; 72 | } 73 | if (adjustment != NULL) 74 | *adjustment = llvm::APInt(e->bits(), 0); 75 | *canonical = e; 76 | } 77 | 78 | inline bool isEqual(ExprRef e, bool taken) { 79 | return (e->kind() == Equal && taken) || 80 | (e->kind() == Distinct && !taken); 81 | } 82 | 83 | } // namespace 84 | 85 | Solver::Solver( 86 | const std::string input_file, 87 | const std::string out_dir, 88 | const std::string bitmap) 89 | : input_file_(input_file) 90 | , inputs_() 91 | , out_dir_(out_dir) 92 | , context_(*g_z3_context) 93 | , solver_(z3::solver(context_, "QF_BV")) 94 | , num_generated_(0) 95 | , trace_(bitmap) 96 | , last_interested_(false) 97 | , syncing_(false) 98 | , start_time_(getTimeStamp()) 99 | , solving_time_(0) 100 | , last_pc_(0) 101 | , dep_forest_() 102 | { 103 | // Set timeout for solver 104 | z3::params p(context_); 105 | p.set(":timeout", kSolverTimeout); 106 | solver_.set(p); 107 | 108 | checkOutDir(); 109 | readInput(); 110 | 111 | if (getenv("SYMCC_SKIP_QUERIES")) { 112 | skip_query = true; 113 | } 114 | } 115 | 116 | void Solver::push() { 117 | solver_.push(); 118 | } 119 | 120 | void Solver::reset() { 121 | solver_.reset(); 122 | } 123 | 124 | void Solver::pop() { 125 | solver_.pop(); 126 | } 127 | 128 | void Solver::add(z3::expr expr) { 129 | if (!expr.is_const()) 130 | solver_.add(expr.simplify()); 131 | } 132 | 133 | z3::check_result Solver::check() { 134 | uint64_t before = getTimeStamp(); 135 | z3::check_result res; 136 | LOG_STAT( 137 | "SMT: { \"solving_time\": " + decstr(solving_time_) + ", " 138 | + "\"total_time\": " + decstr(before - start_time_) + " }\n"); 139 | // LOG_DEBUG("Constraints: " + solver_.to_smt2() + "\n"); 140 | try { 141 | res = solver_.check(); 142 | } 143 | catch(z3::exception e) { 144 | // https://github.com/Z3Prover/z3/issues/419 145 | // timeout can cause exception 146 | res = z3::unknown; 147 | } 148 | uint64_t cur = getTimeStamp(); 149 | uint64_t elapsed = cur - before; 150 | solving_time_ += elapsed; 151 | LOG_STAT("SMT: { \"solving_time\": " + decstr(solving_time_) + " }\n"); 152 | return res; 153 | } 154 | 155 | bool Solver::checkAndSave(const std::string& postfix) { 156 | if (check() == z3::sat) { 157 | saveValues(postfix); 158 | return true; 159 | } 160 | else { 161 | LOG_DEBUG("unsat\n"); 162 | return false; 163 | } 164 | } 165 | 166 | void Solver::addJcc(ExprRef e, bool taken, ADDRINT pc) { 167 | // Save the last instruction pointer for debugging 168 | last_pc_ = pc; 169 | 170 | if (e->isConcrete()) 171 | return; 172 | 173 | // if e == Bool(true), then ignore 174 | if (e->kind() == Bool) { 175 | assert(!(castAs(e)->value() ^ taken)); 176 | return; 177 | } 178 | 179 | assert(isRelational(e.get())); 180 | 181 | // check duplication before really solving something, 182 | // some can be handled by range based constraint solving 183 | bool is_interesting; 184 | if (pc == 0) { 185 | // If addJcc() is called by special case, then rely on last_interested_ 186 | is_interesting = last_interested_; 187 | } 188 | else 189 | is_interesting = isInterestingJcc(e, taken, pc); 190 | 191 | if (skip_query) 192 | is_interesting = false; 193 | 194 | if (is_interesting) 195 | negatePath(e, taken); 196 | addConstraint(e, taken, is_interesting); 197 | } 198 | 199 | void Solver::addAddr(ExprRef e, ADDRINT addr) { 200 | llvm::APInt v(e->bits(), addr); 201 | addAddr(e, v); 202 | } 203 | 204 | void Solver::addAddr(ExprRef e, llvm::APInt addr) { 205 | if (e->isConcrete()) 206 | return; 207 | 208 | if (last_interested_) { 209 | reset(); 210 | // TODO: add optimize in z3 211 | syncConstraints(e); 212 | if (check() != z3::sat) 213 | return; 214 | z3::expr &z3_expr = e->toZ3Expr(); 215 | 216 | // TODO: add unbound case 217 | z3::expr min_expr = getMinValue(z3_expr); 218 | z3::expr max_expr = getMaxValue(z3_expr); 219 | solveOne(z3_expr == min_expr); 220 | solveOne(z3_expr == max_expr); 221 | } 222 | 223 | addValue(e, addr); 224 | } 225 | 226 | void Solver::addValue(ExprRef e, ADDRINT val) { 227 | llvm::APInt v(e->bits(), val); 228 | addValue(e, v); 229 | } 230 | 231 | void Solver::addValue(ExprRef e, llvm::APInt val) { 232 | if (e->isConcrete()) 233 | return; 234 | 235 | #ifdef CONFIG_TRACE 236 | trace_addValue(e, val); 237 | #endif 238 | 239 | ExprRef expr_val = g_expr_builder->createConstant(val, e->bits()); 240 | ExprRef expr_concrete = g_expr_builder->createBinaryExpr(Equal, e, expr_val); 241 | 242 | addConstraint(expr_concrete, true, false); 243 | } 244 | 245 | void Solver::solveAll(ExprRef e, llvm::APInt val) { 246 | if (last_interested_) { 247 | std::string postfix = ""; 248 | ExprRef expr_val = g_expr_builder->createConstant(val, e->bits()); 249 | ExprRef expr_concrete = g_expr_builder->createBinaryExpr(Equal, e, expr_val); 250 | 251 | reset(); 252 | syncConstraints(e); 253 | addToSolver(expr_concrete, false); 254 | 255 | if (check() != z3::sat) { 256 | // Optimistic solving 257 | reset(); 258 | addToSolver(expr_concrete, false); 259 | postfix = "optimistic"; 260 | } 261 | 262 | z3::expr z3_expr = e->toZ3Expr(); 263 | while(true) { 264 | if (!checkAndSave(postfix)) 265 | break; 266 | z3::expr value = getPossibleValue(z3_expr); 267 | add(value != z3_expr); 268 | } 269 | } 270 | addValue(e, val); 271 | } 272 | 273 | UINT8 Solver::getInput(ADDRINT index) { 274 | assert(index < inputs_.size()); 275 | return inputs_[index]; 276 | } 277 | 278 | void Solver::checkOutDir() { 279 | // skip if there is no out_dir 280 | if (out_dir_.empty()) { 281 | LOG_INFO("Since output directory is not set, use stdout\n"); 282 | return; 283 | } 284 | 285 | struct stat info; 286 | if (stat(out_dir_.c_str(), &info) != 0 287 | || !(info.st_mode & S_IFDIR)) { 288 | LOG_FATAL("No such directory\n"); 289 | exit(-1); 290 | } 291 | } 292 | 293 | void Solver::readInput() { 294 | std::ifstream ifs (input_file_, std::ifstream::in | std::ifstream::binary); 295 | if (ifs.fail()) { 296 | LOG_FATAL("Cannot open an input file\n"); 297 | exit(-1); 298 | } 299 | 300 | char ch; 301 | while (ifs.get(ch)) 302 | inputs_.push_back((UINT8)ch); 303 | } 304 | 305 | std::vector Solver::getConcreteValues() { 306 | // TODO: change from real input 307 | z3::model m = solver_.get_model(); 308 | unsigned num_constants = m.num_consts(); 309 | std::vector values = inputs_; 310 | for (unsigned i = 0; i < num_constants; i++) { 311 | z3::func_decl decl = m.get_const_decl(i); 312 | z3::expr e = m.get_const_interp(decl); 313 | z3::symbol name = decl.name(); 314 | 315 | if (name.kind() == Z3_INT_SYMBOL) { 316 | int value = e.get_numeral_int(); 317 | values[name.to_int()] = (UINT8)value; 318 | } 319 | } 320 | return values; 321 | } 322 | 323 | void Solver::saveValues(const std::string& postfix) { 324 | std::vector values = getConcreteValues(); 325 | 326 | // If no output directory is specified, then just print it out 327 | if (out_dir_.empty()) { 328 | printValues(values); 329 | return; 330 | } 331 | 332 | std::string fname = out_dir_+ "/" + toString6digit(num_generated_); 333 | // Add postfix to record where it is genereated 334 | if (!postfix.empty()) 335 | fname = fname + "-" + postfix; 336 | ofstream of(fname, std::ofstream::out | std::ofstream::binary); 337 | LOG_INFO("New testcase: " + fname + "\n"); 338 | if (of.fail()) 339 | LOG_FATAL("Unable to open a file to write results\n"); 340 | 341 | // TODO: batch write 342 | for (unsigned i = 0; i < values.size(); i++) { 343 | char val = values[i]; 344 | of.write(&val, sizeof(val)); 345 | } 346 | 347 | of.close(); 348 | num_generated_++; 349 | } 350 | 351 | void Solver::printValues(const std::vector& values) { 352 | fprintf(stderr, "[INFO] Values: "); 353 | for (unsigned i = 0; i < values.size(); i++) { 354 | fprintf(stderr, "\\x%02X", values[i]); 355 | } 356 | fprintf(stderr, "\n"); 357 | } 358 | 359 | z3::expr Solver::getPossibleValue(z3::expr& z3_expr) { 360 | z3::model m = solver_.get_model(); 361 | return m.eval(z3_expr); 362 | } 363 | 364 | z3::expr Solver::getMinValue(z3::expr& z3_expr) { 365 | push(); 366 | z3::expr value(context_); 367 | while (true) { 368 | if (checkAndSave()) { 369 | value = getPossibleValue(z3_expr); 370 | solver_.add(z3::ult(z3_expr, value)); 371 | } 372 | else 373 | break; 374 | } 375 | pop(); 376 | return value; 377 | } 378 | 379 | z3::expr Solver::getMaxValue(z3::expr& z3_expr) { 380 | push(); 381 | z3::expr value(context_); 382 | while (true) { 383 | if (checkAndSave()) { 384 | value = getPossibleValue(z3_expr); 385 | solver_.add(z3::ugt(z3_expr, value)); 386 | } 387 | else 388 | break; 389 | } 390 | pop(); 391 | return value; 392 | } 393 | 394 | void Solver::addToSolver(ExprRef e, bool taken) { 395 | e->simplify(); 396 | if (!taken) 397 | e = g_expr_builder->createLNot(e); 398 | add(e->toZ3Expr()); 399 | } 400 | 401 | void Solver::syncConstraints(ExprRef e) { 402 | std::set>> forest; 403 | DependencySet* deps = e->getDependencies(); 404 | 405 | for (const size_t& index : *deps) 406 | forest.insert(dep_forest_.find(index)); 407 | 408 | for (std::shared_ptr> tree : forest) { 409 | std::vector> nodes = tree->getNodes(); 410 | for (std::shared_ptr node : nodes) { 411 | if (isRelational(node.get())) 412 | addToSolver(node, true); 413 | else { 414 | // Process range-based constraints 415 | bool valid = false; 416 | for (INT32 i = 0; i < 2; i++) { 417 | ExprRef expr_range = getRangeConstraint(node, i); 418 | if (expr_range != NULL) { 419 | addToSolver(expr_range, true); 420 | valid = true; 421 | } 422 | } 423 | 424 | // One of range expressions should be non-NULL 425 | if (!valid) 426 | LOG_INFO(std::string(__func__) + ": Incorrect constraints are inserted\n"); 427 | } 428 | } 429 | } 430 | 431 | checkFeasible(); 432 | } 433 | 434 | void Solver::addConstraint(ExprRef e, bool taken, bool is_interesting) { 435 | if (auto NE = castAs(e)) { 436 | addConstraint(NE->expr(), !taken, is_interesting); 437 | return; 438 | } 439 | if (!addRangeConstraint(e, taken)) 440 | addNormalConstraint(e, taken); 441 | } 442 | 443 | void Solver::addConstraint(ExprRef e) { 444 | // If e is true, then just skip 445 | if (e->kind() == Bool) { 446 | QSYM_ASSERT(castAs(e)->value()); 447 | return; 448 | } 449 | if (e->isConcrete()) 450 | return; 451 | dep_forest_.addNode(e); 452 | } 453 | 454 | bool Solver::addRangeConstraint(ExprRef e, bool taken) { 455 | if (!isConstSym(e)) 456 | return false; 457 | 458 | Kind kind = Invalid; 459 | ExprRef expr_sym, expr_const; 460 | parseConstSym(e, kind, expr_sym, expr_const); 461 | ExprRef canonical = NULL; 462 | llvm::APInt adjustment; 463 | getCanonicalExpr(expr_sym, &canonical, &adjustment); 464 | llvm::APInt value = static_pointer_cast(expr_const)->value(); 465 | 466 | if (!taken) 467 | kind = negateKind(kind); 468 | 469 | canonical->addConstraint(kind, value, 470 | adjustment); 471 | addConstraint(canonical); 472 | //updated_exprs_.insert(canonical); 473 | return true; 474 | } 475 | 476 | void Solver::addNormalConstraint(ExprRef e, bool taken) { 477 | if (!taken) 478 | e = g_expr_builder->createLNot(e); 479 | addConstraint(e); 480 | } 481 | 482 | ExprRef Solver::getRangeConstraint(ExprRef e, bool is_unsigned) { 483 | Kind lower_kind = is_unsigned ? Uge : Sge; 484 | Kind upper_kind = is_unsigned ? Ule : Sle; 485 | RangeSet *rs = e->getRangeSet(is_unsigned); 486 | if (rs == NULL) 487 | return NULL; 488 | 489 | ExprRef expr = NULL; 490 | for (auto i = rs->begin(), end = rs->end(); 491 | i != end; i++) { 492 | const llvm::APSInt& from = i->From(); 493 | const llvm::APSInt& to = i->To(); 494 | ExprRef bound = NULL; 495 | 496 | if (from == to) { 497 | // can simplify this case 498 | ExprRef imm = g_expr_builder->createConstant(from, e->bits()); 499 | bound = g_expr_builder->createEqual(e, imm); 500 | } 501 | else 502 | { 503 | ExprRef lb_imm = g_expr_builder->createConstant(i->From(), e->bits()); 504 | ExprRef ub_imm = g_expr_builder->createConstant(i->To(), e->bits()); 505 | ExprRef lb = g_expr_builder->createBinaryExpr(lower_kind, e, lb_imm); 506 | ExprRef ub = g_expr_builder->createBinaryExpr(upper_kind, e, ub_imm); 507 | bound = g_expr_builder->createLAnd(lb, ub); 508 | } 509 | 510 | if (expr == NULL) 511 | expr = bound; 512 | else 513 | expr = g_expr_builder->createLOr(expr, bound); 514 | } 515 | 516 | return expr; 517 | } 518 | 519 | 520 | bool Solver::isInterestingJcc(ExprRef rel_expr, bool taken, ADDRINT pc) { 521 | bool interesting = trace_.isInterestingBranch(pc, taken); 522 | // record for other decision 523 | last_interested_ = interesting; 524 | return interesting; 525 | } 526 | 527 | void Solver::negatePath(ExprRef e, bool taken) { 528 | reset(); 529 | syncConstraints(e); 530 | addToSolver(e, !taken); 531 | bool sat = checkAndSave(); 532 | if (!sat) { 533 | reset(); 534 | // optimistic solving 535 | addToSolver(e, !taken); 536 | checkAndSave("optimistic"); 537 | } 538 | } 539 | 540 | void Solver::solveOne(z3::expr z3_expr) { 541 | push(); 542 | add(z3_expr); 543 | checkAndSave(); 544 | pop(); 545 | } 546 | 547 | void Solver::checkFeasible() { 548 | #ifdef CONFIG_TRACE 549 | if (check() == z3::unsat) 550 | LOG_FATAL("Infeasible constraints: " + solver_.to_smt2() + "\n"); 551 | #endif 552 | } 553 | 554 | } // namespace qsym 555 | -------------------------------------------------------------------------------- /utils/time.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | ts=$(date +%s%N) ; $@ ; tt=$((($(date +%s%N) - $ts)/1000000)) ; echo "Time taken: $tt milliseconds" 3 | --------------------------------------------------------------------------------