├── .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 |
--------------------------------------------------------------------------------