├── tools ├── CMakeLists.txt └── Retrace │ ├── CMakeLists.txt │ ├── fzl │ └── Retracer │ │ ├── TraceReader.h │ │ ├── MemoryModel.h │ │ ├── MemoryModel.cpp │ │ ├── TraceReader.cpp │ │ ├── Retracer.h │ │ └── Retracer.cpp │ └── Main.cpp ├── runtime ├── CMakeLists.txt └── fzl.cpp ├── tidy.sh ├── passes ├── CMakeLists.txt ├── BasicBlockInstr │ ├── CMakeLists.txt │ └── BasicBlockInstr.cpp └── BasicBlockTagger │ ├── CMakeLists.txt │ └── BasicBlockTagger.cpp ├── common ├── fzl.cpp └── fzl.h ├── .gitignore ├── retrace.sh ├── test ├── complex.c └── test.c ├── llvm-tests ├── tag.c ├── instr.c ├── lit.cfg.in ├── main_args.c ├── retrace.c └── vararg.c.nope ├── test.sh ├── cmake └── FindPythonModule.cmake ├── LICENSE ├── README.md └── CMakeLists.txt /tools/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_subdirectory(Retrace) -------------------------------------------------------------------------------- /runtime/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | ADD_LIBRARY(fuzzilly STATIC fzl.cpp ${FZL_COMMON_SOURCES}) 2 | -------------------------------------------------------------------------------- /tidy.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | run-clang-tidy-9 -style llvm -checks "*" -p ./build/ 4 | 5 | -------------------------------------------------------------------------------- /passes/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_subdirectory(BasicBlockInstr) 2 | add_subdirectory(BasicBlockTagger) 3 | -------------------------------------------------------------------------------- /common/fzl.cpp: -------------------------------------------------------------------------------- 1 | #include "fzl.h" 2 | 3 | namespace fzl { 4 | const char *TagBasicBlockIDKind = "fzl.BB_ID"; 5 | const char *TagLoadStoreInstructionKind = "fzl.I_LOAD_STORE"; 6 | } 7 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | *xz 3 | llvm_src/ 4 | llvm_build/ 5 | .idea/ 6 | cmake-build-debug/ 7 | generated/ 8 | test/retraced 9 | ls_copied 10 | .gdb_history 11 | *.ll 12 | *.bc 13 | *.dot 14 | *.bin 15 | -------------------------------------------------------------------------------- /retrace.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | ./build/tools/Retrace/fuzzilly-retrace -i ./test/target.meta.ll -t ./test/trace.bin -o ./test/retraced.bc 4 | llvm-dis-9 ./test/retraced.bc 5 | clang-9 -o ./test/retraced ./test/retraced.bc 6 | 7 | -------------------------------------------------------------------------------- /test/complex.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | void useless(int i) { 4 | printf("IMMA SLIGHTLY LESSA USELESSA %d\n", i + 1); 5 | } 6 | 7 | 8 | int main() { 9 | int a; 10 | 11 | if (a == 0) { 12 | puts("ITSAME!"); 13 | } else { 14 | puts("ITSANOTTAME!"); 15 | } 16 | 17 | for (int i=0; i<3; i++) { 18 | useless(i); 19 | } 20 | 21 | return 0; 22 | } 23 | 24 | -------------------------------------------------------------------------------- /llvm-tests/tag.c: -------------------------------------------------------------------------------- 1 | // RUN: clang %s -O0 -S -emit-llvm -o %t.ll 2 | // RUN: opt -load=%bindir/passes/BasicBlockTagger/BasicBlockTaggerPass.so --basic_block_tagger %t.ll -S -o %t.meta.ll 3 | // RUN: cat %t.meta.ll | %FileCheck %s 4 | 5 | #include 6 | 7 | int main() { 8 | // CHECK: fzl.BB_ID 9 | if (getc(stdin) > 0x20) { 10 | return 0; 11 | } else { 12 | return 1; 13 | } 14 | } 15 | 16 | -------------------------------------------------------------------------------- /test/test.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int child(int arg) { 4 | return arg + 1; 5 | } 6 | 7 | int main() { 8 | int a = getc(stdin); 9 | int b = 0; 10 | 11 | if (a == 0) { 12 | puts("ITSAME!"); 13 | } else { 14 | puts("ITSANOTTAME!"); 15 | } 16 | 17 | for (int i=0; i<3; i++) { 18 | a *= 2; 19 | } 20 | 21 | if (a > 1) { 22 | if (a > 2) { 23 | a = 2; 24 | } else { 25 | b = 3; 26 | } 27 | } else { 28 | a = 1; 29 | } 30 | 31 | a = child(a); 32 | 33 | return a + b; 34 | } 35 | 36 | -------------------------------------------------------------------------------- /llvm-tests/instr.c: -------------------------------------------------------------------------------- 1 | // RUN: clang %s -O0 -S -emit-llvm -o %t.ll 2 | // RUN: opt -load=%bindir/passes/BasicBlockTagger/BasicBlockTaggerPass.so --basic_block_tagger %t.ll -S -o %t.meta.ll 3 | // RUN: opt -load=%bindir/passes/BasicBlockInstr/BasicBlockInstrPass.so --basic_block_instr %t.meta.ll -S -o %t.trace.ll 4 | // RUN: cat %t.trace.ll | %FileCheck %s 5 | 6 | #include 7 | 8 | int main() { 9 | // CHECK: fzl_trace_basic_block 10 | if (getc(stdin) > 0x20) { 11 | return 0; 12 | } else { 13 | return 1; 14 | } 15 | } 16 | 17 | -------------------------------------------------------------------------------- /tools/Retrace/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | project(fuzzilly-retrace) 2 | 3 | add_definitions(${LLVM_DEFINITIONS}) 4 | include_directories(${LLVM_INCLUDE_DIRS}) 5 | 6 | add_executable( 7 | fuzzilly-retrace 8 | Main.cpp 9 | fzl/Retracer/TraceReader.cpp 10 | fzl/Retracer/Retracer.cpp 11 | ${FZL_COMMON_SOURCES} 12 | fzl/Retracer/MemoryModel.cpp fzl/Retracer/MemoryModel.h) 13 | 14 | llvm_map_components_to_libnames(llvm_libs support core irreader TransformUtils BitWriter) 15 | 16 | target_include_directories(fuzzilly-retrace PRIVATE 17 | . 18 | ) 19 | target_link_libraries(fuzzilly-retrace ${llvm_libs}) 20 | -------------------------------------------------------------------------------- /tools/Retrace/fzl/Retracer/TraceReader.h: -------------------------------------------------------------------------------- 1 | #ifndef FUZILLY_TRACEREADER_H 2 | #define FUZILLY_TRACEREADER_H 3 | 4 | #include 5 | #include 6 | 7 | #include "../../../../common/fzl.h" 8 | 9 | namespace fzl { 10 | 11 | namespace Retracer { 12 | 13 | class TraceReader { 14 | private: 15 | std::ifstream TraceStream; 16 | 17 | public: 18 | explicit TraceReader(const char *FileName); 19 | ~TraceReader() = default; 20 | 21 | std::optional getNext(); 22 | bool isOk() { return bool(TraceStream); } 23 | }; 24 | 25 | } // namespace Retracer 26 | 27 | } // namespace fzl 28 | 29 | 30 | #endif //FUZILLY_TRACEREADER_H 31 | -------------------------------------------------------------------------------- /tools/Retrace/fzl/Retracer/MemoryModel.h: -------------------------------------------------------------------------------- 1 | #ifndef FUZILLY_MEMORY_H 2 | #define FUZILLY_MEMORY_H 3 | 4 | #include 5 | 6 | #include 7 | 8 | #include "fzl.h" 9 | 10 | namespace fzl { 11 | namespace Retracer { 12 | 13 | using MemoryContent = std::pair; 14 | using MemoryMap = std::map; 15 | 16 | class MemoryModel { 17 | private: 18 | MemoryMap Memory; 19 | 20 | public: 21 | std::optional load(MemoryAddressTy Address); 22 | void store(MemoryAddressTy Address, llvm::Value *Value); 23 | 24 | void dump(llvm::raw_ostream& out); 25 | }; 26 | 27 | } // namespace Retracer 28 | } // namespace fzl 29 | 30 | #endif //FUZILLY_MEMORY_H 31 | -------------------------------------------------------------------------------- /llvm-tests/lit.cfg.in: -------------------------------------------------------------------------------- 1 | import os 2 | import re 3 | import platform 4 | import lit 5 | 6 | config.name = 'fuzzilly' 7 | config.test_source_root = os.path.join('@CMAKE_CURRENT_SOURCE_DIR@', 'llvm-tests') 8 | config.test_format = lit.formats.ShTest(True) 9 | 10 | config.suffixes = ['.c', '.cpp', '.ll'] 11 | 12 | config.excludes = ['CMakeCache.txt', 'CMakeFiles', 'CMakeLists.txt'] 13 | 14 | config.substitutions.append(('%bindir', '@CMAKE_BINARY_DIR@')) 15 | config.substitutions.append(('%FileCheck', '@LLVM_TOOLS_BINARY_DIR@/FileCheck')) 16 | 17 | config.environment['PATH'] = os.pathsep.join(["@LLVM_TOOLS_BINARY_DIR@", 18 | config.environment['PATH']]) 19 | config.environment['LLVM_ROOT'] = "@LLVM_ROOT@" 20 | config.environment['CMAKE_SOURCE_DIR'] = "@CMAKE_SOURCE_DIR@" 21 | config.environment['MOD_EXT'] = "@CMAKE_SHARED_LIBRARY_SUFFIX@" 22 | -------------------------------------------------------------------------------- /passes/BasicBlockInstr/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_llvm_library(BasicBlockInstrPass MODULE 2 | # List your source files here. 3 | BasicBlockInstr.cpp 4 | ${FZL_COMMON_SOURCES} 5 | 6 | PLUGIN_TOOL 7 | opt 8 | ) 9 | 10 | # Use C++11 to compile your pass (i.e., supply -std=c++11). 11 | target_compile_features(BasicBlockInstrPass PRIVATE cxx_range_for cxx_auto_type) 12 | 13 | # LLVM is (typically) built with no C++ RTTI. We need to match that; 14 | # otherwise, we'll get linker errors about missing RTTI data. 15 | set_target_properties(BasicBlockInstrPass PROPERTIES 16 | COMPILE_FLAGS "-fno-rtti" 17 | ) 18 | 19 | # Get proper shared-library behavior (where symbols are not necessarily 20 | # resolved when the shared library is linked) on OS X. 21 | if(APPLE) 22 | set_target_properties(BasicBlockInstrPass PROPERTIES 23 | LINK_FLAGS "-undefined dynamic_lookup" 24 | ) 25 | endif(APPLE) 26 | -------------------------------------------------------------------------------- /passes/BasicBlockTagger/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_llvm_library(BasicBlockTaggerPass MODULE 2 | # List your source files here. 3 | BasicBlockTagger.cpp 4 | ${FZL_COMMON_SOURCES} 5 | 6 | PLUGIN_TOOL 7 | opt 8 | ) 9 | 10 | # Use C++11 to compile your pass (i.e., supply -std=c++11). 11 | target_compile_features(BasicBlockTaggerPass PRIVATE cxx_range_for cxx_auto_type) 12 | 13 | # LLVM is (typically) built with no C++ RTTI. We need to match that; 14 | # otherwise, we'll get linker errors about missing RTTI data. 15 | set_target_properties(BasicBlockTaggerPass PROPERTIES 16 | COMPILE_FLAGS "-fno-rtti" 17 | ) 18 | 19 | # Get proper shared-library behavior (where symbols are not necessarily 20 | # resolved when the shared library is linked) on OS X. 21 | if(APPLE) 22 | set_target_properties(BasicBlockTaggerPass PROPERTIES 23 | LINK_FLAGS "-undefined dynamic_lookup" 24 | ) 25 | endif(APPLE) 26 | -------------------------------------------------------------------------------- /common/fzl.h: -------------------------------------------------------------------------------- 1 | #ifndef FZL_FZL_H 2 | #define FZL_FZL_H 3 | 4 | #include 5 | 6 | #define FZL_DEV 7 | 8 | #ifdef FZL_DEV 9 | #include 10 | #define FZL_DBG(...) fprintf(stderr, __VA_ARGS__) 11 | #else 12 | #define FZL_DBG(...) {} 13 | #endif 14 | 15 | namespace fzl { 16 | 17 | using BasicBlockIdTy = uint32_t; 18 | using MemoryAddressTy = uint64_t; 19 | 20 | // Trace tags 21 | extern const char *TagBasicBlockIDKind; 22 | extern const char *TagLoadStoreInstructionKind; 23 | 24 | enum { 25 | TEK_BasicBlock = 0, 26 | TEK_MemoryLoad = 1, 27 | TEK_MemoryStore = 2, 28 | }; 29 | 30 | struct BasicBlockEvent { 31 | BasicBlockIdTy Id; 32 | }; 33 | 34 | struct MemoryAccessEvent { 35 | MemoryAddressTy Address; 36 | }; 37 | 38 | struct TraceEvent { 39 | uint8_t Kind; 40 | union { 41 | BasicBlockEvent BasicBlock; 42 | MemoryAccessEvent MemoryAccess; 43 | }; 44 | }; 45 | 46 | } // namespace 47 | 48 | #endif 49 | -------------------------------------------------------------------------------- /test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | LLVM_BINDIR=$(llvm-config-9 --bindir) 4 | 5 | cd test 6 | 7 | echo "====== COMPILING ======" 8 | #$LLVM_BINDIR/clang -O1 -S -emit-llvm test.c 9 | $LLVM_BINDIR/clang -O0 -S -emit-llvm complex.c -o test.ll 10 | #sed -i s/optnone//g test.ll 11 | #opt-9 -mem2reg test.ll -S > test_fix.ll 12 | #mv test_fix.ll test.ll 13 | 14 | echo "====== LLVM-LINK ======" 15 | $LLVM_BINDIR/llvm-link ./test.ll -S -o target.ll 16 | 17 | echo "====== TAG ======" 18 | $LLVM_BINDIR/opt -load=../build/passes/BasicBlockTagger/BasicBlockTaggerPass.so --basic_block_tagger ./target.ll -S -o target.meta.ll 19 | 20 | echo "====== INSTRUMENT ======" 21 | $LLVM_BINDIR/opt -load=../build/passes/BasicBlockInstr/BasicBlockInstrPass.so --basic_block_instr ./target.meta.ll -S -o target.trace.ll 22 | 23 | echo "====== LINK ======" 24 | $LLVM_BINDIR/clang++ -O0 ./target.trace.ll -o target.trace.bin ../build/runtime/libfuzzilly.a 25 | 26 | echo "====== MOTHAFUCKING RUNNING ======" 27 | echo -e \\x00 | ./target.trace.bin 28 | 29 | -------------------------------------------------------------------------------- /llvm-tests/main_args.c: -------------------------------------------------------------------------------- 1 | // RUN: clang %s -O0 -S -emit-llvm -o %t.ll 2 | // RUN: sed -i s/optnone//g %t.ll 3 | // RUN: llvm-link %t.ll -S -o %t.ll 4 | // RUN: opt -load=%bindir/passes/BasicBlockTagger/BasicBlockTaggerPass.so --basic_block_tagger %t.ll -S -o %t.meta.ll 5 | // RUN: opt -load=%bindir/passes/BasicBlockInstr/BasicBlockInstrPass.so --basic_block_instr %t.meta.ll -S -o %t.tracer.ll 6 | // RUN: clang++ -O0 %t.tracer.ll -o %t.tracer.bin %bindir/runtime/libfuzzilly.a 7 | // RUN: export FZL_TRACE_FILE=%t.trace; %t.tracer.bin arg > %t.tracer.stdout 8 | // RUN: %bindir/tools/Retrace/fuzzilly-retrace -S -i %t.meta.ll -t %t.trace -o %t.retraced.ll 9 | // RUN: clang -o %t.retraced.bin %t.retraced.ll 10 | // RUN: %t.retraced.bin > %t.retraced.stdout; diff %t.retraced.stdout %t.tracer.stdout 11 | 12 | #include 13 | 14 | int child(int arg) { 15 | return arg - 1; 16 | } 17 | 18 | int main(int argc, char *argv[]) { 19 | argc = child(argc); 20 | 21 | if (argc == 1) { 22 | puts("ONE"); 23 | } else { 24 | puts("NOT ONE"); 25 | } 26 | 27 | return 0; 28 | } 29 | 30 | -------------------------------------------------------------------------------- /cmake/FindPythonModule.cmake: -------------------------------------------------------------------------------- 1 | # Find if a Python module is installed 2 | # Found at http://www.cmake.org/pipermail/cmake/2011-January/041666.html 3 | # To use do: find_python_module(PyQt4 REQUIRED) 4 | function(find_python_module module) 5 | string(TOUPPER ${module} module_upper) 6 | if(NOT PY_${module_upper}) 7 | if(ARGC GREATER 1 AND ARGV1 STREQUAL "REQUIRED") 8 | set(${module}_FIND_REQUIRED TRUE) 9 | endif() 10 | # A module's location is usually a directory, but for binary modules 11 | # it's a .so file. 12 | execute_process(COMMAND "${PYTHON_EXECUTABLE}" "-c" 13 | "import re, ${module}; print(re.compile('/__init__.py.*').sub('',${module}.__file__))" 14 | RESULT_VARIABLE _${module}_status 15 | OUTPUT_VARIABLE _${module}_location 16 | ERROR_QUIET 17 | OUTPUT_STRIP_TRAILING_WHITESPACE) 18 | if(NOT _${module}_status) 19 | set(PY_${module_upper} ${_${module}_location} CACHE STRING 20 | "Location of Python module ${module}") 21 | endif(NOT _${module}_status) 22 | endif(NOT PY_${module_upper}) 23 | find_package_handle_standard_args(PY_${module} DEFAULT_MSG PY_${module_upper}) 24 | endfunction(find_python_module) 25 | 26 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2020 Paolo Montesel 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /tools/Retrace/fzl/Retracer/MemoryModel.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "MemoryModel.h" 6 | 7 | namespace fzl { 8 | namespace Retracer { 9 | 10 | std::optional MemoryModel::load(MemoryAddressTy Address) { 11 | // TODO: Handle overlapping 12 | auto Ret = Memory.find(Address); 13 | return Ret == Memory.end() ? std::optional{} : std::make_optional(Ret->second.second); 14 | } 15 | 16 | void MemoryModel::store(MemoryAddressTy Address, llvm::Value *Value) { 17 | // TODO: Handle overlapping 18 | llvm::Type* ValueType = Value->getType(); 19 | if (auto Integer = llvm::dyn_cast(ValueType)) { 20 | unsigned int Bits = Integer->getScalarSizeInBits(); 21 | llvm::errs() << "STORE BITS: " << Bits << "\n"; 22 | // if (Bits != 32) return; 23 | Memory[Address] = std::make_pair(Bits, Value); 24 | } 25 | } 26 | 27 | void MemoryModel::dump(llvm::raw_ostream &out) { 28 | out << "### MEMORY DUMP BEG ###\n"; 29 | for (auto&[K, V]: Memory) { 30 | out << llvm::format("%018p: ", K) << V.first << "\n"; 31 | } 32 | out << "### MEMORY DUMP END ###\n"; 33 | } 34 | 35 | } // namespace Retracer 36 | } // namespace fzl 37 | -------------------------------------------------------------------------------- /tools/Retrace/fzl/Retracer/TraceReader.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "TraceReader.h" 4 | 5 | namespace fzl { 6 | 7 | namespace Retracer { 8 | 9 | TraceReader::TraceReader(const char *FileName) { 10 | TraceStream.open(FileName, std::ios::in | std::ios::binary); 11 | } 12 | 13 | std::optional TraceReader::getNext() { 14 | TraceEvent Event; 15 | 16 | // Read the structure tag 17 | if (!TraceStream.read(reinterpret_cast(&Event.Kind), sizeof(Event.Kind))) { 18 | return {}; 19 | } 20 | 21 | // Read the fields manually 22 | switch (Event.Kind) { 23 | case TEK_BasicBlock: { 24 | TraceStream.read( 25 | reinterpret_cast(&Event.BasicBlock.Id), 26 | sizeof(Event.BasicBlock.Id) 27 | ); 28 | break; 29 | } 30 | case TEK_MemoryLoad: 31 | case TEK_MemoryStore: { 32 | TraceStream.read( 33 | reinterpret_cast(&Event.MemoryAccess.Address), 34 | sizeof(Event.MemoryAccess.Address) 35 | ); 36 | break; 37 | } 38 | default:assert(false && "Unknown event tag!"); 39 | } 40 | 41 | // Check that we read everything OK 42 | if (!TraceStream) { 43 | return {}; 44 | } else { 45 | return Event; 46 | } 47 | } 48 | 49 | } // namespace Retracer 50 | 51 | } // namespace fzl 52 | 53 | -------------------------------------------------------------------------------- /llvm-tests/retrace.c: -------------------------------------------------------------------------------- 1 | // RUN: clang %s -O0 -S -emit-llvm -o %t.ll 2 | // RUN: sed -i s/optnone//g %t.ll 3 | // RUN: llvm-link %t.ll -S -o %t.ll 4 | // RUN: opt -load=%bindir/passes/BasicBlockTagger/BasicBlockTaggerPass.so --basic_block_tagger %t.ll -S -o %t.meta.ll 5 | // RUN: opt -load=%bindir/passes/BasicBlockInstr/BasicBlockInstrPass.so --basic_block_instr %t.meta.ll -S -o %t.tracer.ll 6 | // RUN: clang++ -O0 %t.tracer.ll -o %t.tracer.bin %bindir/runtime/libfuzzilly.a 7 | // RUN: export FZL_TRACE_FILE=%t.trace; echo -e "\x00" | %t.tracer.bin > %t.tracer.stdout; echo -n "" 8 | // RUN: %bindir/tools/Retrace/fuzzilly-retrace -i %t.meta.ll -t %t.trace -o %t.retraced.bc 9 | // RUN: opt -S %t.retraced.bc | %FileCheck %s 10 | // RUN: clang -o %t.retraced.bin %t.retraced.bc 11 | // RUN: echo A | %t.retraced.bin > %t.retraced.stdout; diff %t.retraced.stdout %t.tracer.stdout 12 | 13 | #include 14 | 15 | // CHECK: main 16 | int main() { 17 | // CHECK: fuzzilly_start_block 18 | // CHECK-NOT: store 19 | // CHECK: ret 20 | 21 | int a = getc(stdin); 22 | int b = 0; 23 | 24 | if (a == 0) { 25 | puts("ITSAME!"); 26 | } else { 27 | puts("ITSANOTTAME!"); 28 | } 29 | 30 | for (int i=0; i<3; i++) { 31 | a *= 2; 32 | } 33 | 34 | if (a > 1) { 35 | if (a > 2) { 36 | a = 2; 37 | } else { 38 | b = 3; 39 | } 40 | } else { 41 | a = 1; 42 | } 43 | 44 | return a + b; 45 | } 46 | 47 | -------------------------------------------------------------------------------- /llvm-tests/vararg.c.nope: -------------------------------------------------------------------------------- 1 | // RUN: clang %s -O0 -S -emit-llvm -o %t.ll 2 | // RUN: llvm-link %t.ll -S -o %t.ll 3 | // RUN: opt -load=%bindir/passes/BasicBlockTagger/BasicBlockTaggerPass.so --basic_block_tagger %t.ll -S -o %t.meta.ll 4 | // RUN: opt -load=%bindir/passes/BasicBlockInstr/BasicBlockInstrPass.so --basic_block_instr %t.meta.ll -S -o %t.tracer.ll 5 | // RUN: clang++ -O0 %t.tracer.ll -o %t.tracer.bin %bindir/runtime/libfuzzilly.a 6 | // RUN: export FZL_TRACE_FILE=%t.trace; %t.tracer.bin > %t.tracer.stdout 7 | // RUN: %bindir/tools/Retrace/fuzzilly-retrace -S -i %t.meta.ll -t %t.trace -o %t.retraced.ll 8 | // RUN: clang -o %t.retraced.bin %t.retraced.ll 9 | // RUN: %t.retraced.bin arg arg > %t.retraced.stdout; diff %t.retraced.stdout %t.tracer.stdout 10 | 11 | #include 12 | #include 13 | 14 | 15 | void printer(const char* fmt, ...) { 16 | va_list args; 17 | va_start(args, fmt); 18 | 19 | while (*fmt != '\0') { 20 | switch (*fmt) { 21 | case 'd': { 22 | int d = va_arg(args, int); 23 | printf("%d\n", d); 24 | break; 25 | } 26 | case 'f': { 27 | double f = va_arg(args, double); 28 | printf("%f\n", f); 29 | } 30 | } 31 | 32 | ++fmt; 33 | } 34 | 35 | va_end(args); 36 | } 37 | 38 | int main(int argc, char *argv[]) { 39 | if (argc == 1) { 40 | printer("df", 666, 666.0); 41 | } else { 42 | puts("NOT ONE"); 43 | } 44 | 45 | return 0; 46 | } 47 | 48 | -------------------------------------------------------------------------------- /passes/BasicBlockTagger/BasicBlockTagger.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "llvm/IR/Constants.h" 4 | #include "llvm/IR/InstrTypes.h" 5 | #include "llvm/IR/LegacyPassManager.h" 6 | #include "llvm/IR/Module.h" 7 | #include "llvm/Pass.h" 8 | #include "llvm/Support/raw_ostream.h" 9 | #include "llvm/Transforms/IPO/PassManagerBuilder.h" 10 | 11 | #include "../../common/fzl.h" 12 | 13 | using namespace llvm; 14 | 15 | namespace { 16 | struct BasicBlockTaggerPass : public ModulePass { 17 | static char ID; 18 | BasicBlockTaggerPass() : ModulePass(ID) {} 19 | 20 | void tagBasicBlock(BasicBlock& BB, fzl::BasicBlockIdTy id) { 21 | auto &ctx = BB.getContext(); 22 | auto I = BB.getTerminator(); 23 | MDNode* node = MDNode::get(ctx, ConstantAsMetadata::get(ConstantInt::get( 24 | ctx, APInt(8 * sizeof(fzl::BasicBlockIdTy), id, false) 25 | ))); 26 | I->setMetadata(fzl::TagBasicBlockIDKind, node); 27 | } 28 | 29 | bool runOnModule(Module &M) { 30 | fzl::BasicBlockIdTy counter = 0; 31 | 32 | for (auto& F : M) { 33 | for (auto& BB : F) { 34 | errs() << "I saw a BB: " << counter << "\n"; 35 | tagBasicBlock(BB, counter); 36 | ++counter; 37 | } 38 | } 39 | 40 | // We modified the module 41 | return true; 42 | } 43 | }; 44 | } // namespace 45 | 46 | char BasicBlockTaggerPass::ID = 0; 47 | 48 | static RegisterPass X( 49 | "basic_block_tagger", // Command line argument 50 | "Tag every basic block in the target", // Command line desciption 51 | false, // Only looks at CFG 52 | false // Analysis Pass 53 | ); 54 | 55 | -------------------------------------------------------------------------------- /runtime/fzl.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "../common/fzl.h" 8 | 9 | extern "C" { 10 | void fzl_init(); 11 | void fzl_fini(); 12 | void fzl_trace_basic_block(fzl::BasicBlockIdTy Id); 13 | void fzl_trace_memory_load(fzl::MemoryAddressTy Addr); 14 | void fzl_trace_memory_store(fzl::MemoryAddressTy Addr); 15 | } 16 | 17 | namespace { 18 | int TraceFD = -1; 19 | } // namespace 20 | 21 | void fzl_init() { 22 | const char* traceName = getenv("FZL_TRACE_FILE"); 23 | if (traceName == nullptr) { 24 | traceName = "trace.bin"; 25 | } 26 | TraceFD = open(traceName, O_CREAT | O_WRONLY | O_TRUNC, 0664); 27 | if (TraceFD == -1) { 28 | fputs("FZL: Couldn't open trace file!\n", stderr); 29 | } 30 | } 31 | 32 | void fzl_fini() { 33 | close(TraceFD); 34 | } 35 | 36 | void fzl_trace_basic_block(fzl::BasicBlockIdTy Id) { 37 | FZL_DBG("FZL_TRACE_BASIC_BLOCK: %18d\n", Id); 38 | uint8_t Tag = fzl::TEK_BasicBlock; 39 | write(TraceFD, (void *) &Tag, sizeof(Tag)); 40 | write(TraceFD, (void *) &Id, sizeof(Id)); 41 | } 42 | 43 | void fzl_trace_memory_access(uint8_t Tag, fzl::MemoryAddressTy Addr) { 44 | write(TraceFD, (void *) &Tag, sizeof(Tag)); 45 | write(TraceFD, (void *) &Addr, sizeof(Addr)); 46 | } 47 | 48 | void fzl_trace_memory_load(fzl::MemoryAddressTy Addr) { 49 | FZL_DBG("FZL_TRACE_MEMORY_LOAD: %018p\n", Addr); 50 | uint8_t Tag = fzl::TEK_MemoryLoad; 51 | fzl_trace_memory_access(Tag, Addr); 52 | } 53 | 54 | void fzl_trace_memory_store(fzl::MemoryAddressTy Addr) { 55 | FZL_DBG("FZL_TRACE_MEMORY_STORE: %018p\n", Addr); 56 | uint8_t Tag = fzl::TEK_MemoryStore; 57 | fzl_trace_memory_access(Tag, Addr); 58 | } 59 | 60 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # fuzzilly 2 | 3 | ![fuzzilly](https://user-images.githubusercontent.com/1985669/49517033-6d2bb380-f89b-11e8-8928-2f6668e3a133.png) 4 | 5 | Fuzzilly is a PoC LLVM-based tool to *unroll* a program's execution. 6 | It does so by instrumenting the LLVM IR of the target in such a way that during 7 | execution it will dump its own memory accesses. 8 | After that, it reconstructs the execution without any conditional statement. 9 | 10 | It is source-based. 11 | 12 | I know the name is 13 | [unfortunate](https://github.com/googleprojectzero/fuzzilli), but I made this 14 | before that project was released. 15 | 16 | Also, I made a beautiful logo. 17 | 18 | Also, yes. 19 | 20 | ## Screenshots 21 | 22 | Fuzzilly can turn this 23 | 24 | ![source](https://user-images.githubusercontent.com/1985669/75091570-0ad9d400-556f-11ea-8335-7fd0ec266255.png) 25 | 26 | into this 27 | 28 | ![source-retraced](https://user-images.githubusercontent.com/1985669/75091569-09a8a700-556f-11ea-9fd5-40579fcb76dc.png) 29 | 30 | or, if you prefer x86 assembly, 31 | 32 | ![asm](https://user-images.githubusercontent.com/1985669/75091564-06adb680-556f-11ea-96b6-0f51432dcd58.png) 33 | 34 | into this 35 | 36 | ![asm-retraced](https://user-images.githubusercontent.com/1985669/75091566-07dee380-556f-11ea-9217-dd692cb44456.png) 37 | 38 | ## Usage 39 | 40 | Have llvm-9 installed and then 41 | 42 | ```sh 43 | ./build.sh 44 | cd build && make check && cd .. 45 | ./test.sh && ./retrace.sh && ./test/retraced 46 | ``` 47 | 48 | ## Why? 49 | 50 | Why not? 51 | 52 | ## Limitations 53 | 54 | This is a PoC so it only works on simple programs. 55 | Its main limitation is that it doesn't support varargs because LLVM treats them 56 | in a weird way: 57 | it has builtin IR operations made specifically for varargs, but it generates IR 58 | that mixes them with normal memory accesses to the arguments. 59 | Long story short: in order to support varargs on X86-64, one would need to 60 | reimplement the SystemV ABI inside fuzzilly. 61 | 62 | ## Info 63 | 64 | I did this during my time at [Politecnico di Milano's NECSTLab](https://necst.it/). 65 | 66 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.4) 2 | set(CMAKE_CXX_STANDARD 17) 3 | 4 | project(fuzilly) 5 | 6 | set(CMAKE_EXPORT_COMPILE_COMMANDS ON) 7 | 8 | execute_process( 9 | COMMAND llvm-config-9 --src-root 10 | RESULT_VARIABLE HAD_ERROR 11 | OUTPUT_VARIABLE LLVM_ROOT 12 | ) 13 | 14 | if (NOT HAD_ERROR) 15 | string(REGEX REPLACE "[ \t]*[\r\n]+[ \t]*" "" LLVM_ROOT ${LLVM_ROOT}) 16 | else() 17 | message(FATAL_ERROR "llvm-config-9 failed -- ${HAD_ERROR}") 18 | endif() 19 | 20 | # A bit of a sanity check: 21 | if (NOT EXISTS ${LLVM_ROOT}/include/llvm) 22 | message(FATAL_ERROR "LLVM_ROOT (${LLVM_ROOT}) is invalid") 23 | endif() 24 | 25 | list(APPEND CMAKE_MODULE_PATH "${LLVM_ROOT}/lib/cmake/llvm") 26 | list(APPEND CMAKE_PREFIX_PATH "${LLVM_ROOT}/lib/cmake/llvm") 27 | list(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake") 28 | 29 | find_package(LLVM REQUIRED CONFIG) 30 | 31 | include(HandleLLVMOptions) # load additional config 32 | include(AddLLVM) # used to add our own modules 33 | 34 | add_definitions(${LLVM_DEFINITIONS}) 35 | include_directories(${LLVM_INCLUDE_DIRS}) 36 | link_directories(${LLVM_LIBRARY_DIRS}) 37 | 38 | set(FZL_COMMON_SOURCES "${PROJECT_SOURCE_DIR}/common/fzl.cpp") 39 | 40 | include_directories("${PROJECT_SOURCE_DIR}/include/") 41 | include_directories("${PROJECT_SOURCE_DIR}/common/") 42 | 43 | add_subdirectory(runtime) 44 | add_subdirectory(passes) 45 | add_subdirectory(tools) 46 | 47 | ######################################### TESTS 48 | # https://github.com/quarkslab/llvm-dev-meeting-tutorial-2015/blob/master/CMakeLists.txt 49 | # Find python 50 | include(FindPythonModule) 51 | set(Python_ADDITIONAL_VERSIONS "3") 52 | find_package(PythonInterp) 53 | find_python_module(lit REQUIRED) 54 | 55 | # Find FileCheck 56 | if(NOT EXISTS ${LLVM_TOOLS_BINARY_DIR}/FileCheck) 57 | message(FATAL_ERROR "LLVM wasn't configured with -DLLVM_INSTALL_UTILS, cannot use FileCheck") 58 | endif() 59 | 60 | # lit configuration and target 61 | configure_file("${CMAKE_CURRENT_SOURCE_DIR}/llvm-tests/lit.cfg.in" "${CMAKE_CURRENT_BINARY_DIR}/llvm-tests/lit.cfg") 62 | add_custom_target(check 63 | COMMAND ${PYTHON_EXECUTABLE} -m lit.main 64 | "${CMAKE_CURRENT_BINARY_DIR}/llvm-tests" -v 65 | DEPENDS BasicBlockTaggerPass BasicBlockInstrPass fuzzilly fuzzilly-retrace 66 | ) 67 | -------------------------------------------------------------------------------- /tools/Retrace/Main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #include "../../common/fzl.h" 19 | #include "fzl/Retracer/Retracer.h" 20 | 21 | 22 | int main(int argc, char *argv[]) { 23 | llvm::cl::opt OutputAssembly( 24 | "S", 25 | llvm::cl::desc("Write output as LLVM assembly") 26 | ); 27 | 28 | llvm::cl::opt IRFilename( 29 | "i", 30 | llvm::cl::desc("Specify IR filename"), 31 | llvm::cl::value_desc("filename"), 32 | llvm::cl::Required 33 | ); 34 | 35 | llvm::cl::opt OutFilename( 36 | "o", 37 | llvm::cl::desc("Specify output filename"), 38 | llvm::cl::value_desc("filename"), 39 | llvm::cl::Required 40 | ); 41 | 42 | llvm::cl::opt TraceFilename( 43 | "t", 44 | llvm::cl::desc("Specify trace.bin filename"), 45 | llvm::cl::value_desc("filename"), 46 | llvm::cl::Required 47 | ); 48 | 49 | llvm::cl::ParseCommandLineOptions(argc, argv); 50 | 51 | static llvm::LLVMContext Context; 52 | llvm::SMDiagnostic Diag; 53 | std::unique_ptr Mod = llvm::parseIRFile(IRFilename, Diag, Context); 54 | 55 | if (Mod == nullptr) { 56 | llvm::errs() << "Invalid file " << IRFilename << "\n"; 57 | return 1; 58 | } 59 | 60 | fzl::Retracer::Retracer R(&Context, Mod.get()); 61 | llvm::Function *TracedFunction = R.buildTraceFromFile(TraceFilename.c_str()); 62 | 63 | // Debug print 64 | llvm::errs() << *TracedFunction; 65 | 66 | // Write bitcode to file 67 | std::error_code EC; 68 | llvm::raw_fd_ostream OS(OutFilename, EC); 69 | 70 | if (OutputAssembly) { 71 | llvm::createPrintModulePass(OS, "", true)->runOnModule(*Mod); 72 | } else { 73 | llvm::WriteBitcodeToFile(*Mod, OS); 74 | } 75 | 76 | return 0; 77 | } 78 | 79 | -------------------------------------------------------------------------------- /tools/Retrace/fzl/Retracer/Retracer.h: -------------------------------------------------------------------------------- 1 | #ifndef FUZILLY_RETRACE_H 2 | #define FUZILLY_RETRACE_H 3 | 4 | #include 5 | 6 | #include 7 | 8 | #include "fzl.h" 9 | #include "TraceReader.h" 10 | #include "MemoryModel.h" 11 | 12 | namespace fzl::Retracer { 13 | 14 | using BasicBlockIdMapTy = std::unordered_map; 15 | using ArgVector = llvm::SmallVector; 16 | 17 | class Retracer { 18 | private: 19 | llvm::LLVMContext *Context; 20 | llvm::Module *Module; 21 | 22 | const bool OptionCreateFakeBranches = false; 23 | const bool OptionKeepMemoryAccesses = true; 24 | const bool OptionSwapEntryPoints = true; 25 | const char *OptionSwappedEntryPointName = "totally_not_main"; 26 | 27 | BasicBlockIdMapTy BasicBlockIdMap; 28 | MemoryModel Memory; 29 | 30 | public: 31 | Retracer(llvm::LLVMContext *C, llvm::Module *M); 32 | 33 | llvm::Function *buildTraceFromFile(const char *FN); 34 | 35 | private: 36 | void buildBasicBlockMap(BasicBlockIdMapTy &Map, llvm::Module *Mod); 37 | llvm::Value *buildFunctionTrace(ArgVector &Args, 38 | llvm::Function &F, 39 | TraceReader &Reader, 40 | llvm::BasicBlock *OutBlock, 41 | llvm::BasicBlock **PtrNewOutBlock); 42 | void buildTrace(llvm::Function &F, TraceReader &Reader, llvm::BasicBlock *OutBlock); 43 | 44 | llvm::BasicBlock *handleCallInstruction(TraceReader &Reader, 45 | llvm::BasicBlock *OutBlock, 46 | llvm::CallInst &OriginalCall, 47 | llvm::ValueToValueMapTy &Remap); 48 | 49 | static void handleGenericInstruction(llvm::BasicBlock &OutBlock, 50 | const llvm::Instruction &OriginalInstr, 51 | llvm::ValueToValueMapTy &Map); 52 | 53 | void handleLoadInstruction(TraceReader &Reader, 54 | llvm::BasicBlock &OutBlock, 55 | llvm::LoadInst &LoadInst, 56 | llvm::ValueToValueMapTy &Map); 57 | 58 | static void handlePhiNode(const llvm::BasicBlock *PrevBlock, 59 | const llvm::PHINode &OriginalPhi, 60 | llvm::ValueToValueMapTy &Map); 61 | 62 | static llvm::Value *handleRetInstruction(llvm::BasicBlock **PtrNewOutBlock, 63 | llvm::BasicBlock *OutBlock, 64 | const llvm::ReturnInst &RetInst, 65 | llvm::ValueToValueMapTy &Remap); 66 | 67 | void handleStoreInstruction(TraceReader &Reader, 68 | llvm::BasicBlock &OutBlock, 69 | llvm::StoreInst &Store, 70 | llvm::ValueToValueMapTy &Map); 71 | 72 | static void handleSwitchInstruction(const llvm::BasicBlock *PrevBlock, 73 | const llvm::SwitchInst &SwitchInst, 74 | llvm::ValueToValueMapTy &Map); 75 | 76 | static void remapFunctionArguments(const llvm::Function &F, const ArgVector &Args, llvm::ValueToValueMapTy &Map); 77 | 78 | }; 79 | 80 | } // namespace fzl::Retracer 81 | 82 | #endif //FUZILLY_RETRACE_H 83 | -------------------------------------------------------------------------------- /passes/BasicBlockInstr/BasicBlockInstr.cpp: -------------------------------------------------------------------------------- 1 | #include "llvm/IR/Constants.h" 2 | #include "llvm/IR/Function.h" 3 | #include "llvm/IR/Instructions.h" 4 | #include "llvm/IR/LegacyPassManager.h" 5 | #include "llvm/IR/Module.h" 6 | #include "llvm/Transforms/Utils/ModuleUtils.h" 7 | #include "llvm/Support/raw_ostream.h" 8 | #include "llvm/Transforms/IPO/PassManagerBuilder.h" 9 | 10 | #include "../../common/fzl.h" 11 | 12 | using namespace llvm; 13 | 14 | namespace { 15 | struct BasicBlockInstrPass : public BasicBlockPass { 16 | static char ID; 17 | BasicBlockInstrPass() : BasicBlockPass(ID) {} 18 | 19 | Function* FzlFini = nullptr; 20 | Function* FzlInit = nullptr; 21 | Function* FzlTraceBB = nullptr; 22 | Function* FzlTraceLoad = nullptr; 23 | Function* FzlTraceStore = nullptr; 24 | 25 | void createCtor(Module& M) { 26 | FzlInit->setDoesNotThrow(); 27 | appendToGlobalCtors(M, FzlInit, 10000, nullptr); 28 | } 29 | 30 | void createDtor(Module& M) { 31 | FzlFini->setDoesNotThrow(); 32 | appendToGlobalDtors(M, FzlFini, 10000, nullptr); 33 | } 34 | 35 | virtual bool doInitialization(Module& M) { 36 | errs() << "Init module: " << M.getName() << "\n"; 37 | 38 | Type* Int32Ty = IntegerType::getInt32Ty(M.getContext()); 39 | Type* Int64Ty = IntegerType::getInt64Ty(M.getContext()); 40 | Type* VoidTy = Type::getVoidTy(M.getContext()); 41 | 42 | FzlFini = cast(M.getOrInsertFunction( 43 | "fzl_fini", 44 | VoidTy 45 | ).getCallee()); 46 | 47 | FzlInit = cast(M.getOrInsertFunction( 48 | "fzl_init", 49 | VoidTy 50 | ).getCallee()); 51 | 52 | FzlTraceBB = cast(M.getOrInsertFunction( 53 | "fzl_trace_basic_block", 54 | VoidTy, 55 | Int32Ty 56 | ).getCallee()); 57 | 58 | FzlTraceLoad = cast(M.getOrInsertFunction( 59 | "fzl_trace_memory_load", 60 | VoidTy, 61 | Int64Ty 62 | ).getCallee()); 63 | 64 | FzlTraceStore = cast(M.getOrInsertFunction( 65 | "fzl_trace_memory_store", 66 | VoidTy, 67 | Int64Ty 68 | ).getCallee()); 69 | 70 | createCtor(M); 71 | createDtor(M); 72 | 73 | return false; 74 | } 75 | 76 | void tagInstructionWithCounter(Instruction &I, unsigned Counter) { 77 | // TODO 78 | } 79 | 80 | void instrumentInstructions(BasicBlock &B) { 81 | // TODO: Move out 82 | Type* Int64Ty = IntegerType::getInt64Ty(B.getContext()); 83 | 84 | unsigned Counter = 0; 85 | 86 | for (auto BI = B.begin(), BE = B.end(); BI != BE; ++BI, ++Counter) { 87 | Instruction& I = *BI; 88 | 89 | switch (I.getOpcode()) { 90 | case Instruction::Load: { 91 | Instruction* CastInst = PtrToIntInst::CreatePointerCast(((LoadInst*) &I)->getPointerOperand(), Int64Ty, "", &I); 92 | std::vector args = {CastInst}; 93 | CallInst::Create(FzlTraceLoad, args, "", &I); 94 | errs() << "LOAD: " << I << "\n"; 95 | break; 96 | } 97 | case Instruction::Store: { 98 | Instruction* CastInst = PtrToIntInst::CreatePointerCast(((LoadInst*) &I)->getPointerOperand(), Int64Ty, "", &I); 99 | std::vector args = {CastInst}; 100 | CallInst::Create(FzlTraceStore, args, "", &I); 101 | errs() << "STORE: " << I << "\n"; 102 | break; 103 | } 104 | default: 105 | break; 106 | } 107 | } 108 | } 109 | 110 | bool runOnBasicBlock(BasicBlock &B) override { 111 | instrumentInstructions(B); 112 | 113 | auto I = B.getTerminator(); 114 | auto md = I->getMetadata(fzl::TagBasicBlockIDKind); 115 | 116 | assert(md && "Found a basic block without ID :/"); 117 | 118 | auto kBbID = cast(md->getOperand(0))->getValue(); 119 | auto bbID = cast(kBbID); 120 | unsigned id = bbID->getValue().getLimitedValue(); 121 | errs() << "Found basic block with ID: " << id << "\n"; 122 | 123 | std::vector args = {bbID}; 124 | CallInst::Create(FzlTraceBB, args, "", B.getFirstNonPHI()); 125 | 126 | return true; 127 | } 128 | }; 129 | } // namespace 130 | 131 | char BasicBlockInstrPass::ID = 0; 132 | 133 | static RegisterPass X( 134 | "basic_block_instr", // Command line argument 135 | "Add BB tracing instrumentation", // Command line desciption 136 | false, // Only looks at CFG 137 | false // Analysis Pass 138 | ); 139 | -------------------------------------------------------------------------------- /tools/Retrace/fzl/Retracer/Retracer.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "Retracer.h" 7 | 8 | #include "fzl/Retracer/TraceReader.h" 9 | 10 | namespace fzl::Retracer { 11 | 12 | Retracer::Retracer(llvm::LLVMContext *C, llvm::Module *M) { 13 | Context = C; 14 | Module = M; 15 | 16 | buildBasicBlockMap(BasicBlockIdMap, M); 17 | } 18 | 19 | llvm::Function *Retracer::buildTraceFromFile(const char *FN) { 20 | const char *SymFuzzillyMain = "fuzzilly_main"; 21 | const char *SymOriginalMain = "main"; 22 | 23 | TraceReader Reader(FN); 24 | 25 | if (!Reader.isOk()) { 26 | return nullptr; 27 | } 28 | 29 | llvm::Function *OrigFunk = Module->getFunction(SymOriginalMain); 30 | 31 | llvm::FunctionType *FT = Module->getFunction(SymOriginalMain)->getFunctionType(); 32 | llvm::Function *F = llvm::Function::Create(FT, llvm::Function::ExternalLinkage, SymFuzzillyMain, Module); 33 | 34 | llvm::BasicBlock *FatBlock = llvm::BasicBlock::Create(*Context, "fuzzilly_start_block", F); 35 | 36 | buildTrace(*OrigFunk, Reader, FatBlock); 37 | 38 | if (OptionSwapEntryPoints) { 39 | OrigFunk->setName(OptionSwappedEntryPointName); 40 | F->setName(SymOriginalMain); 41 | } 42 | 43 | return F; 44 | } 45 | 46 | void Retracer::buildBasicBlockMap(BasicBlockIdMapTy &Map, llvm::Module *Mod) { 47 | 48 | for (llvm::Function &F : *Mod) { 49 | for (auto BI = F.begin(), E = F.end(); BI != E; BI++) { 50 | llvm::BasicBlock *B = &(*BI); 51 | const llvm::Instruction *I = B->getTerminator(); 52 | 53 | auto MD = I->getMetadata(fzl::TagBasicBlockIDKind); 54 | 55 | assert(MD && "Basic block without ID"); 56 | 57 | auto MetadataConst = llvm::cast(MD->getOperand(0))->getValue(); 58 | auto BBIntID = llvm::cast(MetadataConst); 59 | fzl::BasicBlockIdTy ID = BBIntID->getValue().getLimitedValue(); 60 | 61 | Map[ID] = B; 62 | } 63 | } 64 | 65 | } 66 | 67 | void Retracer::buildTrace(llvm::Function &F, TraceReader &Reader, llvm::BasicBlock *OutBlock) { 68 | ArgVector Dummy; 69 | llvm::BasicBlock *NewOutBlock = nullptr; 70 | 71 | llvm::Value *RetVal = buildFunctionTrace(Dummy, F, Reader, OutBlock, &NewOutBlock); 72 | assert(NewOutBlock != nullptr && "RETRACER: buildFunctionTrace returned a null new basic block"); 73 | 74 | // Append the return instruction 75 | OutBlock = NewOutBlock; 76 | llvm::ReturnInst::Create(F.getContext(), RetVal, OutBlock); 77 | } 78 | 79 | llvm::Value *Retracer::buildFunctionTrace(ArgVector &Args, 80 | llvm::Function &F, 81 | TraceReader &Reader, 82 | llvm::BasicBlock *OutBlock, 83 | llvm::BasicBlock **PtrNewOutBlock) { 84 | // Original instruction -> Unrolled instruction 85 | llvm::ValueToValueMapTy Remap; 86 | 87 | // Build the initial map using parent's Values 88 | remapFunctionArguments(F, Args, Remap); 89 | 90 | llvm::BasicBlock *PrevBlock = nullptr; 91 | while (std::optional MaybeEvent = Reader.getNext()) { 92 | TraceEvent Event = *MaybeEvent; 93 | 94 | assert(Event.Kind == TEK_BasicBlock && "RETRACER: Unexpected memory event"); 95 | 96 | llvm::BasicBlock *OriginalBlock = BasicBlockIdMap[Event.BasicBlock.Id]; 97 | 98 | llvm::errs() << "RETRACER: Processing block " << Event.BasicBlock.Id << "\n"; 99 | llvm::errs() << *OriginalBlock; 100 | 101 | // Fix basic block 102 | for (llvm::Instruction &OriginalInstr : *OriginalBlock) { 103 | switch (OriginalInstr.getOpcode()) { 104 | case llvm::Instruction::Br: { 105 | // TODO(babush): Implement optional fake branching 106 | break; 107 | } 108 | case llvm::Instruction::Call: { 109 | OutBlock = handleCallInstruction(Reader, OutBlock, llvm::cast(OriginalInstr), Remap); 110 | break; 111 | } 112 | case llvm::Instruction::Load: { 113 | handleLoadInstruction(Reader, *OutBlock, llvm::cast(OriginalInstr), Remap); 114 | break; 115 | } 116 | case llvm::Instruction::PHI: { 117 | handlePhiNode(PrevBlock, llvm::cast(OriginalInstr), Remap); 118 | break; 119 | } 120 | case llvm::Instruction::Ret: { 121 | return handleRetInstruction(PtrNewOutBlock, OutBlock, llvm::cast(OriginalInstr), Remap); 122 | } 123 | case llvm::Instruction::Switch: { 124 | handleSwitchInstruction(PrevBlock, llvm::cast(OriginalInstr), Remap); 125 | break; 126 | } 127 | case llvm::Instruction::Store: { 128 | handleStoreInstruction(Reader, *OutBlock, llvm::cast(OriginalInstr), Remap); 129 | break; 130 | } 131 | default:handleGenericInstruction(*OutBlock, OriginalInstr, Remap); 132 | } 133 | } 134 | 135 | PrevBlock = OriginalBlock; 136 | } 137 | 138 | llvm_unreachable("RETRACER: End of function unrolling reached without RETs"); 139 | } 140 | 141 | llvm::BasicBlock *Retracer::handleCallInstruction(TraceReader &Reader, 142 | llvm::BasicBlock *OutBlock, 143 | llvm::CallInst &OriginalCall, 144 | llvm::ValueToValueMapTy &Remap) { 145 | // TODO(babush): Handle varargs 146 | if (OriginalCall.getCalledFunction()->isDeclaration()) { 147 | // We don't have the definition of this function 148 | // Just keep it and return the current basic block 149 | handleGenericInstruction(*OutBlock, OriginalCall, Remap); 150 | return OutBlock; 151 | } 152 | 153 | ArgVector ChildArgs; 154 | for (llvm::Value *A : OriginalCall.arg_operands()) { 155 | llvm::Value *MappedArg = Remap[A]; 156 | 157 | if (MappedArg == nullptr) { 158 | assert(llvm::isa(A) && "Calling a subfunction with an unmapped, not constant Value"); 159 | ChildArgs.push_back(A); 160 | } else { 161 | ChildArgs.push_back(MappedArg); 162 | } 163 | } 164 | 165 | // In case the child created a new basic block 166 | llvm::BasicBlock *NewOutBlock; 167 | 168 | // Unroll the child 169 | llvm::Value 170 | *Ret = buildFunctionTrace(ChildArgs, *OriginalCall.getCalledFunction(), Reader, OutBlock, &NewOutBlock); 171 | Remap[&OriginalCall] = Ret; 172 | 173 | // Return the current basic block 174 | return NewOutBlock; 175 | } 176 | 177 | void Retracer::handleGenericInstruction(llvm::BasicBlock &OutBlock, 178 | const llvm::Instruction &OriginalInstr, 179 | llvm::ValueToValueMapTy &Map) { 180 | llvm::Instruction *NewInst = OriginalInstr.clone(); 181 | llvm::RemapInstruction(NewInst, Map); 182 | OutBlock.getInstList().push_back(NewInst); 183 | Map[&OriginalInstr] = NewInst; 184 | } 185 | 186 | void Retracer::handleLoadInstruction(TraceReader &Reader, 187 | llvm::BasicBlock &OutBlock, 188 | llvm::LoadInst &LoadInst, 189 | llvm::ValueToValueMapTy &Map) { 190 | TraceEvent MemoryEvent = *Reader.getNext(); 191 | assert(MemoryEvent.Kind == TEK_MemoryLoad && "Mismatch in event type!"); 192 | 193 | MemoryAddressTy Address = MemoryEvent.MemoryAccess.Address; 194 | 195 | // We have the value in memory 196 | if (std::optional Value = Memory.load(Address)) { 197 | Map[&LoadInst] = *Value; 198 | return; 199 | } 200 | 201 | // We don't have the value, keep the load instruction 202 | llvm::errs() << "RETRACER: Missing load from address " << llvm::format("%018p", Address) << "\n"; 203 | handleGenericInstruction(OutBlock, LoadInst, Map); 204 | } 205 | 206 | void Retracer::handlePhiNode(const llvm::BasicBlock *PrevBlock, 207 | const llvm::PHINode &OriginalPhi, 208 | llvm::ValueToValueMapTy &Map) { 209 | assert(PrevBlock != nullptr && "PrevBlock is null"); 210 | 211 | // We skip phi nodes and just keep track of their last value 212 | llvm::Value *OriginalValue = OriginalPhi.getIncomingValueForBlock(PrevBlock); 213 | Map[&OriginalPhi] = Map[OriginalValue]; 214 | } 215 | 216 | llvm::Value *Retracer::handleRetInstruction(llvm::BasicBlock **PtrNewOutBlock, 217 | llvm::BasicBlock *OutBlock, 218 | const llvm::ReturnInst &RetInst, 219 | llvm::ValueToValueMapTy &Remap) { 220 | *PtrNewOutBlock = OutBlock; 221 | 222 | llvm::Value *RetVal = RetInst.getReturnValue(); 223 | if (RetVal == nullptr) { 224 | return nullptr; 225 | } 226 | 227 | auto RI = Remap.find(RetVal); 228 | if (RI != Remap.end()) { 229 | return RI->second; 230 | } 231 | 232 | assert(llvm::isa(RetVal) && "Returning a non-constant value with no mapping"); 233 | return RetVal; 234 | } 235 | 236 | void Retracer::handleStoreInstruction(TraceReader &Reader, 237 | llvm::BasicBlock &OutBlock, 238 | llvm::StoreInst &Store, 239 | llvm::ValueToValueMapTy &Map) { 240 | TraceEvent MemoryEvent = *Reader.getNext(); 241 | llvm::errs() << "EVENT TYPE: " << (int) MemoryEvent.Kind << "\n"; 242 | llvm::errs() << Store << "\n"; 243 | assert(MemoryEvent.Kind == TEK_MemoryStore && "Mismatch in event type!"); 244 | 245 | Memory.dump(llvm::errs()); 246 | 247 | llvm::Value *StoreValue = Store.getValueOperand(); 248 | auto RI = Map.find(StoreValue); 249 | 250 | // We have the value in the Remap 251 | if (RI != Map.end()) { 252 | Memory.store(MemoryEvent.MemoryAccess.Address, RI->second); 253 | return; 254 | } 255 | 256 | // We are dealing with a constant 257 | if (auto *C = llvm::dyn_cast(StoreValue)) { 258 | Memory.store(MemoryEvent.MemoryAccess.Address, C); 259 | return; 260 | } 261 | 262 | // We don't have the store value, so we keep the store instruction 263 | llvm::errs() << "RETRACER: Missing remap item for value " << *StoreValue << "\n"; 264 | handleGenericInstruction(OutBlock, Store, Map); 265 | } 266 | 267 | void Retracer::handleSwitchInstruction(const llvm::BasicBlock *PrevBlock, 268 | const llvm::SwitchInst &SwitchInst, 269 | llvm::ValueToValueMapTy &Map) { 270 | // For now, do nothing 271 | } 272 | 273 | void Retracer::remapFunctionArguments(const llvm::Function &F, const ArgVector &Args, llvm::ValueToValueMapTy &Map) { 274 | unsigned ArgIdx = 0; 275 | size_t AS = Args.size(); 276 | 277 | // Remap with early stop is parent's values are not enough 278 | for (auto AI = F.arg_begin(), AE = F.arg_end(); AI != AE && ArgIdx < AS; ++AI, ++ArgIdx) { 279 | Map[AI] = Args[ArgIdx]; 280 | } 281 | } 282 | 283 | } // namespace fzl::Retracer 284 | --------------------------------------------------------------------------------