diff --git a/CMakeLists.txt b/CMakeLists.txt index 3db2ba8e..812e0e27 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -18,6 +18,7 @@ project(SymbolicCompiler) list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake") option(QSYM_BACKEND "Use the Qsym backend instead of our own" OFF) +option(RUST_BACKEND "Build the support code required for a Rust backend as a static archive." OFF) option(TARGET_32BIT "Make the compiler work correctly with -m32" OFF) # We need to build the runtime as an external project because CMake otherwise @@ -39,6 +40,7 @@ set(SYM_RUNTIME_BUILD_ARGS -DCMAKE_MODULE_LINKER_FLAGS=${CMAKE_MODULE_LINKER_FLAGS} -DCMAKE_MODULE_LINKER_FLAGS_INIT=${CMAKE_MODULE_LINKER_FLAGS_INIT} -DCMAKE_SHARED_LINKER_FLAGS=${CMAKE_SHARED_LINKER_FLAGS} + -DRUST_BACKEND=${RUST_BACKEND} -DCMAKE_SHARED_LINKER_FLAGS_INIT=${CMAKE_SHARED_LINKER_FLAGS_INIT} -DCMAKE_MODULE_PATH=${CMAKE_MODULE_PATH} -DCMAKE_SYSROOT=${CMAKE_SYSROOT} diff --git a/compiler/Main.cpp b/compiler/Main.cpp index e4c7d4e7..ce43af74 100644 --- a/compiler/Main.cpp +++ b/compiler/Main.cpp @@ -36,6 +36,17 @@ using OptimizationLevel = llvm::PassBuilder::OptimizationLevel; #include #endif +#if LLVM_VERSION_MAJOR >= 13 +#include +#include + +#if LLVM_VERSION_MAJOR >= 14 +#include +#else +using OptimizationLevel = llvm::PassBuilder::OptimizationLevel; +#endif +#endif + #include "Pass.h" using namespace llvm; diff --git a/docs/Experiments.txt b/docs/Experiments.txt index 001a06fc..6a284ed3 100644 --- a/docs/Experiments.txt +++ b/docs/Experiments.txt @@ -42,7 +42,7 @@ you have exported CC=symcc, CXX=sym++ and SYMCC_NO_SYMBOLIC_INPUT=1, first download the code, then build it using its own build system, and finally unset SYMCC_NO_SYMBOLIC_INPUT and analyze the program in concert with AFL (which requires building a second time for AFL, see docs/Fuzzing.txt). We used AFL -2.56b and built the targets with AFL_USE_ASAN=1. Note that the fuzzing helper is +2.56b and built the targets with AFL_USE_ASan=1. Note that the fuzzing helper is already installed in the Docker container. OpenJPEG [4]: we used revision 1f1e9682, built with CMake as described in the @@ -63,7 +63,7 @@ tcpdump: we built both tcpdump [7] and libpcap [8]; in order to make the former and analyzed "tcpdump/tcpdump -e -r @@"; the corpus consisted of just a single dummy file containing the character "A". -All experiments used one AFL master process, one secondary AFL process, and one +All experiments used one AFL main process, one secondary AFL process, and one SymCC process. We let them run for 24 hours and repeated each of them 30 times to create the graphs in the paper; AFL map density was extracted from the secondary AFL process' "plot_data" file, column "map_size". diff --git a/docs/Fuzzing.txt b/docs/Fuzzing.txt index ecbda392..074ff289 100644 --- a/docs/Fuzzing.txt +++ b/docs/Fuzzing.txt @@ -50,7 +50,7 @@ $ tcpdump -e -r Compile tcpdump and libpcap, the library it uses for pcap reading, once with SymCC and once with one of AFL's compiler wrappers (e.g., afl-clang). In order to detect memory corruptions, enable address sanitizer in the AFL-instrumented -version by exporting AFL_USE_ASAN=1 before compiling: +version by exporting AFL_USE_ASan=1 before compiling: $ git clone https://github.com/the-tcpdump-group/libpcap.git $ git clone https://github.com/the-tcpdump-group/tcpdump.git @@ -66,7 +66,7 @@ $ make $ cd .. $ mkdir afl_build; cd afl_build -$ export AFL_USE_ASAN=1 +$ export AFL_USE_ASan=1 $ cp -r ../{libpcap,tcpdump} . $ cd libpcap $ CC=/path/to/afl-clang ./configure @@ -88,10 +88,10 @@ AFL: $ mkdir corpus $ echo A > corpus/dummy -Then launch one AFL master and one AFL secondary instance, both writing their +Then launch one AFL main and one AFL secondary instance, both writing their outputs to the arbitrarily named directory "afl_out": -$ afl-fuzz -M afl-master -i corpus -o afl_out -m none -- afl_build/tcpdump/tcpdump -e -r @@ +$ afl-fuzz -M afl-main -i corpus -o afl_out -m none -- afl_build/tcpdump/tcpdump -e -r @@ $ afl-fuzz -S afl-secondary -i corpus -o afl_out -m none -- afl_build/tcpdump/tcpdump -e -r @@ For simplicity, we disable memory limits (with "-m none"); be sure to read AFL's @@ -109,7 +109,7 @@ you should see the counter "imported" in the "path geometry" section increase after a short time - this means that the fuzzer instances and SymCC are exchanging inputs. Crashes will be stored in afl_out/*/crashes as usual. -It is possible to run SymCC with only an AFL master or only a secondary AFL +It is possible to run SymCC with only an AFL main or only a secondary AFL instance; see the AFL docs for the implications. Moreover, the number of fuzzer and SymCC instances can be increased - just make sure that each has a unique name. diff --git a/runtime/CMakeLists.txt b/runtime/CMakeLists.txt index 07277b8f..af28bb1d 100644 --- a/runtime/CMakeLists.txt +++ b/runtime/CMakeLists.txt @@ -23,6 +23,7 @@ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++17 \ -Wmissing-format-attribute -Wformat-nonliteral") option(QSYM_BACKEND "Use the Qsym backend instead of our own" OFF) +option(RUST_BACKEND "Build the support code required for a Rust backend as a static archive." OFF) option(Z3_TRUST_SYSTEM_VERSION "Use the system-provided Z3 without a version check" OFF) # Place the final product in the top-level output directory @@ -36,7 +37,9 @@ set(SHARED_RUNTIME_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/Shadow.cpp ${CMAKE_CURRENT_SOURCE_DIR}/GarbageCollection.cpp) -if (${QSYM_BACKEND}) +if (${RUST_BACKEND}) + add_subdirectory(rust_backend) +elseif (${QSYM_BACKEND}) add_subdirectory(qsym_backend) else() add_subdirectory(simple_backend) diff --git a/runtime/RuntimeCommon.h b/runtime/RuntimeCommon.h index 4c05ceb0..fcb9f95c 100644 --- a/runtime/RuntimeCommon.h +++ b/runtime/RuntimeCommon.h @@ -33,10 +33,12 @@ #define nullable #ifdef __cplusplus +#include // for size_t #include #include extern "C" { #else +#include // for size_t #include #include #endif diff --git a/runtime/Shadow.h b/runtime/Shadow.h index fe630bb4..d32444c6 100644 --- a/runtime/Shadow.h +++ b/runtime/Shadow.h @@ -23,8 +23,6 @@ #include -#include - // // This file is dedicated to the management of shadow memory. // diff --git a/runtime/rust_backend/CMakeLists.txt b/runtime/rust_backend/CMakeLists.txt new file mode 100644 index 00000000..b237c093 --- /dev/null +++ b/runtime/rust_backend/CMakeLists.txt @@ -0,0 +1,25 @@ +# This file is part of SymCC. +# +# SymCC is free software: you can redistribute it and/or modify it under the +# terms of the GNU General Public License as published by the Free Software +# Foundation, either version 3 of the License, or (at your option) any later +# version. +# +# SymCC is distributed in the hope that it will be useful, but WITHOUT ANY +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR +# A PARTICULAR PURPOSE. See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with +# SymCC. If not, see . + +add_library(SymRuntime STATIC + ${SHARED_RUNTIME_SOURCES} + Runtime.cpp) + +target_include_directories(SymRuntime PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR} + ${CMAKE_CURRENT_SOURCE_DIR}/..) + +set_target_properties(SymRuntime PROPERTIES COMPILE_FLAGS "-Werror") + +INSTALL(TARGETS SymRuntime) \ No newline at end of file diff --git a/runtime/rust_backend/README.md b/runtime/rust_backend/README.md new file mode 100644 index 00000000..a21f9874 --- /dev/null +++ b/runtime/rust_backend/README.md @@ -0,0 +1,24 @@ +# SymCC Rust Runtime + +This runtime is a wrapper around a stripped down runtime which can be implemented in Rust (or any other language). +This wrapper implements Garbage Collection like the the Simple and QSym runtimes and implements the `_sym_bits_helper`. + +The functions that are left to be implemented by the wrapped runtime are defined in `RustRuntime.h` and mirror those which are defined in `RuntimeCommon.h` except for having new name prefixes and missing those which are related to memory management and utilites. + +## GC implementation +The GC implementation works by keeping track of all expressions that the wrapped runtime generates and calling a new method (`_rsym_expression_unreachable(RSymExpr)`) for each expression that became unreachable in terms of the GC. +The details of this implementation are the same as those of the Simple backend (it's a straight copy). + +## Bits Helper +The bits helper is implemented by embedding the number of bits inside the expression pointer. +Specifically, the least significant byte contains the bit width of the expression. +Boolean expressions have a bit width of 0. +The actual expression pointer is shifted towards the MSB to make space for the bit width. +This reduces the amount of available bits in the expression pointer by 8. +The runtime panics if an expression pointer is returned that would not fit, but this is not expected on 64-bit systems. +(On 32-bit systems this may be a problem, but at this point, we don't care about 32-bit.) + +On a high level, this means that there are two `SymExpr` types now: `SymExpr`, which is used by the wrapper, and `RSymExpr`, which is used by the wrapped runtime. +The wrapper takes care of translating between the two representations as necessary. + +The wrapper also takes care of maintaining the correct bit widths by calculating the resulting width when a width-changing instruction is encountered. \ No newline at end of file diff --git a/runtime/rust_backend/Runtime.cpp b/runtime/rust_backend/Runtime.cpp new file mode 100644 index 00000000..2f11cbe3 --- /dev/null +++ b/runtime/rust_backend/Runtime.cpp @@ -0,0 +1,368 @@ +// This file is part of SymCC. +// +// SymCC is free software: you can redistribute it and/or modify it under the +// terms of the GNU General Public License as published by the Free Software +// Foundation, either version 3 of the License, or (at your option) any later +// version. +// +// SymCC is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR +// A PARTICULAR PURPOSE. See the GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along with +// SymCC. If not, see . + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#ifndef NDEBUG +#include +#endif + +#include "Config.h" +#include "GarbageCollection.h" +#include "LibcWrappers.h" +#include "Shadow.h" + +#ifndef NDEBUG +// Helper to print pointers properly. +#define P(ptr) reinterpret_cast(ptr) +#endif + +/* TODO Eventually we'll want to inline as much of this as possible. I'm keeping + it in C for now because that makes it easier to experiment with new features, + but I expect that a lot of the functions will stay so simple that we can + generate the corresponding bitcode directly in the compiler pass. */ + +namespace { + +/// Indicate whether the runtime has been initialized. +std::atomic_flag g_initialized = ATOMIC_FLAG_INIT; + +FILE *g_log = stderr; + +#ifndef NDEBUG +[[maybe_unused]] void dump_known_regions() { + std::cerr << "Known regions:" << std::endl; + for (const auto &[page, shadow] : g_shadow_pages) { + std::cerr << " " << P(page) << " shadowed by " << P(shadow) << std::endl; + } +} + +#endif + +/// The set of all expressions we have ever passed to client code. +std::set allocatedExpressions; + +SymExpr registerExpression(SymExpr expr) { + allocatedExpressions.insert(expr); + return expr; +} + +// To understand why the following functions exist, read the Bits Helper section +// in the README. + +// Get the bit width out of a SymExpr. +uint8_t symexpr_width(SymExpr expr) { + return (uint8_t)((uintptr_t)expr & UINT8_MAX); +} + +// Get the id out of a SymExpr (which is an RSymExpr). +RSymExpr symexpr_id(SymExpr expr) { return (uintptr_t)expr >> 8; } + +// Construct a SymExpr from a RSymExpr and a bit width. +SymExpr symexpr(RSymExpr expr, uint8_t width) { + if (expr == 0) { + // ensure that 0 RSymExpr still maps to 0 in SymExpr, as this is a special + // value for the rest of the backend. + return 0; + } + // ensure that the RSymExpr fits inside the SymExpr. + assert((((expr << 8) >> 8) == expr) && "expr is too large to be stored"); + return (SymExpr)((expr << 8) | width); +} +} // namespace + +void _sym_initialize(void) { + if (g_initialized.test_and_set()) + return; + +#ifndef NDEBUG + std::cerr << "Initializing symbolic runtime" << std::endl; +#endif + + loadConfig(); + initLibcWrappers(); + std::cerr << "This is SymCC running with the Rust backend" << std::endl; + + if (g_config.logFile.empty()) { + g_log = stderr; + } else { + g_log = fopen(g_config.logFile.c_str(), "w"); + } +} + +SymExpr _sym_build_integer(uint64_t value, uint8_t bits) { + return registerExpression(symexpr(_rsym_build_integer(value, bits), bits)); +} + +SymExpr _sym_build_integer128(uint64_t high, uint64_t low) { + return registerExpression(symexpr(_rsym_build_integer128(high, low), 128)); +} + +SymExpr _sym_build_integer_from_buffer(void *buffer, unsigned num_bits) { + return registerExpression( + symexpr(_rsym_build_integer_from_buffer(buffer, num_bits), num_bits)); +} + +SymExpr _sym_build_float(double value, int is_double) { + return registerExpression( + symexpr(_rsym_build_float(value, is_double), is_double ? 64 : 32)); +} + +SymExpr _sym_get_input_byte(size_t offset, uint8_t value) { + return registerExpression(symexpr(_rsym_get_input_byte(offset, value), 8)); +} + +SymExpr _sym_build_null_pointer(void) { + return registerExpression( + symexpr(_rsym_build_null_pointer(), sizeof(uintptr_t) * 8)); +} + +SymExpr _sym_build_true(void) { + return registerExpression(symexpr(_rsym_build_true(), 0)); +} + +SymExpr _sym_build_false(void) { + return registerExpression(symexpr(_rsym_build_false(), 0)); +} + +SymExpr _sym_build_bool(bool value) { + return registerExpression(symexpr(_rsym_build_bool(value), 0)); +} + +#define DEF_UNARY_EXPR_BUILDER(name) \ + SymExpr _sym_build_##name(SymExpr expr) { \ + return registerExpression( \ + symexpr(_rsym_build_##name(symexpr_id(expr)), symexpr_width(expr))); \ + } + +DEF_UNARY_EXPR_BUILDER(neg) + +#define DEF_BINARY_BV_EXPR_BUILDER(name) \ + SymExpr _sym_build_##name(SymExpr a, SymExpr b) { \ + return registerExpression(symexpr( \ + _rsym_build_##name(symexpr_id(a), symexpr_id(b)), symexpr_width(a))); \ + } + +DEF_BINARY_BV_EXPR_BUILDER(add) +DEF_BINARY_BV_EXPR_BUILDER(sub) +DEF_BINARY_BV_EXPR_BUILDER(mul) +DEF_BINARY_BV_EXPR_BUILDER(unsigned_div) +DEF_BINARY_BV_EXPR_BUILDER(signed_div) +DEF_BINARY_BV_EXPR_BUILDER(unsigned_rem) +DEF_BINARY_BV_EXPR_BUILDER(signed_rem) +DEF_BINARY_BV_EXPR_BUILDER(shift_left) +DEF_BINARY_BV_EXPR_BUILDER(logical_shift_right) +DEF_BINARY_BV_EXPR_BUILDER(arithmetic_shift_right) + +#define DEF_BINARY_BOOL_EXPR_BUILDER(name) \ + SymExpr _sym_build_##name(SymExpr a, SymExpr b) { \ + return registerExpression( \ + symexpr(_rsym_build_##name(symexpr_id(a), symexpr_id(b)), 0)); \ + } + +DEF_BINARY_BOOL_EXPR_BUILDER(signed_less_than) +DEF_BINARY_BOOL_EXPR_BUILDER(signed_less_equal) +DEF_BINARY_BOOL_EXPR_BUILDER(signed_greater_than) +DEF_BINARY_BOOL_EXPR_BUILDER(signed_greater_equal) +DEF_BINARY_BOOL_EXPR_BUILDER(unsigned_less_than) +DEF_BINARY_BOOL_EXPR_BUILDER(unsigned_less_equal) +DEF_BINARY_BOOL_EXPR_BUILDER(unsigned_greater_than) +DEF_BINARY_BOOL_EXPR_BUILDER(unsigned_greater_equal) +DEF_BINARY_BOOL_EXPR_BUILDER(equal) + +DEF_BINARY_BV_EXPR_BUILDER(and) +DEF_BINARY_BV_EXPR_BUILDER(or) +DEF_BINARY_BV_EXPR_BUILDER(bool_xor) +DEF_BINARY_BV_EXPR_BUILDER(xor) + +DEF_BINARY_BOOL_EXPR_BUILDER(float_ordered_greater_than) +DEF_BINARY_BOOL_EXPR_BUILDER(float_ordered_greater_equal) +DEF_BINARY_BOOL_EXPR_BUILDER(float_ordered_less_than) +DEF_BINARY_BOOL_EXPR_BUILDER(float_ordered_less_equal) +DEF_BINARY_BOOL_EXPR_BUILDER(float_ordered_equal) + +DEF_BINARY_BV_EXPR_BUILDER(fp_add) +DEF_BINARY_BV_EXPR_BUILDER(fp_sub) +DEF_BINARY_BV_EXPR_BUILDER(fp_mul) +DEF_BINARY_BV_EXPR_BUILDER(fp_div) +DEF_BINARY_BV_EXPR_BUILDER(fp_rem) + +#undef DEF_BINARY_BV_EXPR_BUILDER + +DEF_UNARY_EXPR_BUILDER(fp_abs) +DEF_UNARY_EXPR_BUILDER(fp_neg) + +DEF_UNARY_EXPR_BUILDER(not ) +DEF_BINARY_BOOL_EXPR_BUILDER(not_equal) + +#undef DEF_UNARY_EXPR_BUILDER + +DEF_BINARY_BOOL_EXPR_BUILDER(bool_and) +DEF_BINARY_BOOL_EXPR_BUILDER(bool_or) + +DEF_BINARY_BOOL_EXPR_BUILDER(float_ordered_not_equal) +DEF_BINARY_BOOL_EXPR_BUILDER(float_ordered) +DEF_BINARY_BOOL_EXPR_BUILDER(float_unordered) + +DEF_BINARY_BOOL_EXPR_BUILDER(float_unordered_greater_than) +DEF_BINARY_BOOL_EXPR_BUILDER(float_unordered_greater_equal) +DEF_BINARY_BOOL_EXPR_BUILDER(float_unordered_less_than) +DEF_BINARY_BOOL_EXPR_BUILDER(float_unordered_less_equal) +DEF_BINARY_BOOL_EXPR_BUILDER(float_unordered_equal) +DEF_BINARY_BOOL_EXPR_BUILDER(float_unordered_not_equal) + +#undef DEF_BINARY_BOOL_EXPR_BUILDER + +SymExpr _sym_build_ite(SymExpr cond, SymExpr a, SymExpr b) { + return registerExpression(symexpr( + _rsym_build_ite(symexpr_id(cond), symexpr_id(a), symexpr_id(b)), 0)); +} + +SymExpr _sym_build_sext(SymExpr expr, uint8_t bits) { + return registerExpression(symexpr(_rsym_build_sext(symexpr_id(expr), bits), + symexpr_width(expr) + bits)); +} + +SymExpr _sym_build_zext(SymExpr expr, uint8_t bits) { + return registerExpression(symexpr(_rsym_build_zext(symexpr_id(expr), bits), + symexpr_width(expr) + bits)); +} + +SymExpr _sym_build_trunc(SymExpr expr, uint8_t bits) { + return registerExpression( + symexpr(_rsym_build_trunc(symexpr_id(expr), bits), bits)); +} + +SymExpr _sym_build_int_to_float(SymExpr expr, int is_double, int is_signed) { + return registerExpression( + symexpr(_rsym_build_int_to_float(symexpr_id(expr), is_double, is_signed), + is_double ? 64 : 32)); +} + +SymExpr _sym_build_float_to_float(SymExpr expr, int to_double) { + return registerExpression( + symexpr(_rsym_build_float_to_float(symexpr_id(expr), to_double), + to_double ? 64 : 32)); +} + +SymExpr _sym_build_bits_to_float(SymExpr expr, int to_double) { + if (expr == 0) + return 0; + + return registerExpression( + symexpr(_rsym_build_bits_to_float(symexpr_id(expr), to_double), + to_double ? 64 : 32)); +} + +SymExpr _sym_build_float_to_bits(SymExpr expr) { + if (expr == nullptr) + return nullptr; + return registerExpression(symexpr(_rsym_build_float_to_bits(symexpr_id(expr)), + symexpr_width(expr))); +} + +SymExpr _sym_build_float_to_signed_integer(SymExpr expr, uint8_t bits) { + return registerExpression(symexpr( + _rsym_build_float_to_signed_integer(symexpr_id(expr), bits), bits)); +} + +SymExpr _sym_build_float_to_unsigned_integer(SymExpr expr, uint8_t bits) { + return registerExpression(symexpr( + _rsym_build_float_to_unsigned_integer(symexpr_id(expr), bits), bits)); +} + +SymExpr _sym_build_bool_to_bit(SymExpr expr) { + return registerExpression( + symexpr(_rsym_build_bool_to_bit(symexpr_id(expr)), 1)); +} + +void _sym_push_path_constraint(SymExpr constraint, int taken, + uintptr_t site_id) { + if (constraint == 0) + return; + _rsym_push_path_constraint(symexpr_id(constraint), taken, site_id); +} + +SymExpr _sym_concat_helper(SymExpr a, SymExpr b) { + return registerExpression( + symexpr(_rsym_concat_helper(symexpr_id(a), symexpr_id(b)), + symexpr_width(a) + symexpr_width(b))); +} + +SymExpr _sym_extract_helper(SymExpr expr, size_t first_bit, size_t last_bit) { + return registerExpression( + symexpr(_rsym_extract_helper(symexpr_id(expr), first_bit, last_bit), + first_bit - last_bit + 1)); +} + +size_t _sym_bits_helper(SymExpr expr) { return symexpr_width(expr); } + +void _sym_notify_call(uintptr_t loc) { _rsym_notify_call(loc); } +void _sym_notify_ret(uintptr_t loc) { _rsym_notify_ret(loc); } +void _sym_notify_basic_block(uintptr_t loc) { _rsym_notify_basic_block(loc); } + +/* Debugging */ +const char *_sym_expr_to_string(SymExpr) { return nullptr; } + +bool _sym_feasible(SymExpr) { return false; } + +/* Garbage collection */ +void _sym_collect_garbage() { + if (allocatedExpressions.size() < g_config.garbageCollectionThreshold) + return; + +#ifndef NDEBUG + auto start = std::chrono::high_resolution_clock::now(); + auto startSize = allocatedExpressions.size(); +#endif + + std::vector unreachable_expressions; + + auto reachableExpressions = collectReachableExpressions(); + for (auto expr_it = allocatedExpressions.begin(); + expr_it != allocatedExpressions.end();) { + if (reachableExpressions.count(*expr_it) == 0) { + unreachable_expressions.push_back(symexpr_id(*expr_it)); + expr_it = allocatedExpressions.erase(expr_it); + } else { + ++expr_it; + } + } + if (unreachable_expressions.size() > 0) { + _rsym_expression_unreachable(unreachable_expressions.data(), + unreachable_expressions.size()); + } + +#ifndef NDEBUG + auto end = std::chrono::high_resolution_clock::now(); + auto endSize = allocatedExpressions.size(); + + std::cerr << "After garbage collection: " << endSize + << " expressions remain (before: " << startSize << ")" << std::endl + << "\t(collection took " + << std::chrono::duration_cast(end - + start) + .count() + << " milliseconds)" << std::endl; +#endif +} diff --git a/runtime/rust_backend/Runtime.h b/runtime/rust_backend/Runtime.h new file mode 100644 index 00000000..988bac79 --- /dev/null +++ b/runtime/rust_backend/Runtime.h @@ -0,0 +1,22 @@ +// This file is part of SymCC. +// +// SymCC is free software: you can redistribute it and/or modify it under the +// terms of the GNU General Public License as published by the Free Software +// Foundation, either version 3 of the License, or (at your option) any later +// version. +// +// SymCC is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR +// A PARTICULAR PURPOSE. See the GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along with +// SymCC. If not, see . + +#ifndef RUNTIME_H +#define RUNTIME_H +#include + +typedef void* SymExpr; +#include + +#endif diff --git a/runtime/rust_backend/RustRuntime.h b/runtime/rust_backend/RustRuntime.h new file mode 100644 index 00000000..ebc28eb3 --- /dev/null +++ b/runtime/rust_backend/RustRuntime.h @@ -0,0 +1,153 @@ +// Rust Run-time library interface -*- C++ -*- +// +// This header is mostly a straight copy of RuntimeCommon.h with different +// function name prefixes, a separate SymExpr type and all functions that are +// implemented by this wrapper removed. This file defines the interface that the +// wrapped runtime should implement. Consult the README for a high-level +// overview. +// +// This file is part of SymCC. +// +// SymCC is free software: you can redistribute it and/or modify it under the +// terms of the GNU General Public License as published by the Free Software +// Foundation, either version 3 of the License, or (at your option) any later +// version. +// +// SymCC is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR +// A PARTICULAR PURPOSE. See the GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along with +// SymCC. If not, see . + +#ifndef RUSTRUNTIME_H +#define RUSTRUNTIME_H + +#include + +#ifdef __cplusplus +#include +extern "C" { +#else +#include +#endif + +typedef uintptr_t RSymExpr; + +/* + * Construction of simple values + */ +RSymExpr _rsym_build_integer(uint64_t value, uint8_t bits); +RSymExpr _rsym_build_integer128(uint64_t high, uint64_t low); +RSymExpr _rsym_build_integer_from_buffer(void *buffer, unsigned num_bits); +RSymExpr _rsym_build_float(double value, bool is_double); +RSymExpr _rsym_build_null_pointer(void); +RSymExpr _rsym_build_true(void); +RSymExpr _rsym_build_false(void); +RSymExpr _rsym_build_bool(bool value); + +/* + * Arithmetic and shifts + */ +RSymExpr _rsym_build_neg(RSymExpr expr); +RSymExpr _rsym_build_add(RSymExpr a, RSymExpr b); +RSymExpr _rsym_build_sub(RSymExpr a, RSymExpr b); +RSymExpr _rsym_build_mul(RSymExpr a, RSymExpr b); +RSymExpr _rsym_build_unsigned_div(RSymExpr a, RSymExpr b); +RSymExpr _rsym_build_signed_div(RSymExpr a, RSymExpr b); +RSymExpr _rsym_build_unsigned_rem(RSymExpr a, RSymExpr b); +RSymExpr _rsym_build_signed_rem(RSymExpr a, RSymExpr b); +RSymExpr _rsym_build_shift_left(RSymExpr a, RSymExpr b); +RSymExpr _rsym_build_logical_shift_right(RSymExpr a, RSymExpr b); +RSymExpr _rsym_build_arithmetic_shift_right(RSymExpr a, RSymExpr b); + +RSymExpr _rsym_build_fp_add(RSymExpr a, RSymExpr b); +RSymExpr _rsym_build_fp_sub(RSymExpr a, RSymExpr b); +RSymExpr _rsym_build_fp_mul(RSymExpr a, RSymExpr b); +RSymExpr _rsym_build_fp_div(RSymExpr a, RSymExpr b); +RSymExpr _rsym_build_fp_rem(RSymExpr a, RSymExpr b); +RSymExpr _rsym_build_fp_abs(RSymExpr a); +RSymExpr _rsym_build_fp_neg(RSymExpr a); + +/* + * Boolean operations + */ +RSymExpr _rsym_build_not(RSymExpr expr); +RSymExpr _rsym_build_signed_less_than(RSymExpr a, RSymExpr b); +RSymExpr _rsym_build_signed_less_equal(RSymExpr a, RSymExpr b); +RSymExpr _rsym_build_signed_greater_than(RSymExpr a, RSymExpr b); +RSymExpr _rsym_build_signed_greater_equal(RSymExpr a, RSymExpr b); +RSymExpr _rsym_build_unsigned_less_than(RSymExpr a, RSymExpr b); +RSymExpr _rsym_build_unsigned_less_equal(RSymExpr a, RSymExpr b); +RSymExpr _rsym_build_unsigned_greater_than(RSymExpr a, RSymExpr b); +RSymExpr _rsym_build_unsigned_greater_equal(RSymExpr a, RSymExpr b); +RSymExpr _rsym_build_equal(RSymExpr a, RSymExpr b); +RSymExpr _rsym_build_not_equal(RSymExpr a, RSymExpr b); +RSymExpr _rsym_build_bool_and(RSymExpr a, RSymExpr b); +RSymExpr _rsym_build_and(RSymExpr a, RSymExpr b); +RSymExpr _rsym_build_bool_or(RSymExpr a, RSymExpr b); +RSymExpr _rsym_build_or(RSymExpr a, RSymExpr b); +RSymExpr _rsym_build_bool_xor(RSymExpr a, RSymExpr b); +RSymExpr _rsym_build_xor(RSymExpr a, RSymExpr b); +RSymExpr _rsym_build_ite(RSymExpr cond, RSymExpr a, RSymExpr b); + +RSymExpr _rsym_build_float_ordered_greater_than(RSymExpr a, RSymExpr b); +RSymExpr _rsym_build_float_ordered_greater_equal(RSymExpr a, RSymExpr b); +RSymExpr _rsym_build_float_ordered_less_than(RSymExpr a, RSymExpr b); +RSymExpr _rsym_build_float_ordered_less_equal(RSymExpr a, RSymExpr b); +RSymExpr _rsym_build_float_ordered_equal(RSymExpr a, RSymExpr b); +RSymExpr _rsym_build_float_ordered_not_equal(RSymExpr a, RSymExpr b); +RSymExpr _rsym_build_float_ordered(RSymExpr a, RSymExpr b); +RSymExpr _rsym_build_float_unordered(RSymExpr a, RSymExpr b); +RSymExpr _rsym_build_float_unordered_greater_than(RSymExpr a, RSymExpr b); +RSymExpr _rsym_build_float_unordered_greater_equal(RSymExpr a, RSymExpr b); +RSymExpr _rsym_build_float_unordered_less_than(RSymExpr a, RSymExpr b); +RSymExpr _rsym_build_float_unordered_less_equal(RSymExpr a, RSymExpr b); +RSymExpr _rsym_build_float_unordered_equal(RSymExpr a, RSymExpr b); +RSymExpr _rsym_build_float_unordered_not_equal(RSymExpr a, RSymExpr b); + +/* + * Casts + */ +RSymExpr _rsym_build_sext(RSymExpr expr, uint8_t bits); +RSymExpr _rsym_build_zext(RSymExpr expr, uint8_t bits); +RSymExpr _rsym_build_trunc(RSymExpr expr, uint8_t bits); +RSymExpr _rsym_build_int_to_float(RSymExpr value, bool is_double, + bool is_signed); +RSymExpr _rsym_build_float_to_float(RSymExpr expr, bool to_double); +RSymExpr _rsym_build_bits_to_float(RSymExpr expr, bool to_double); +RSymExpr _rsym_build_float_to_bits(RSymExpr expr); +RSymExpr _rsym_build_float_to_signed_integer(RSymExpr expr, uint8_t bits); +RSymExpr _rsym_build_float_to_unsigned_integer(RSymExpr expr, uint8_t bits); +RSymExpr _rsym_build_bool_to_bit(RSymExpr expr); + +/* + * Bit-array helpers + */ +RSymExpr _rsym_concat_helper(RSymExpr a, RSymExpr b); +RSymExpr _rsym_extract_helper(RSymExpr expr, size_t first_bit, size_t last_bit); + +/* + * Constraint handling + */ +void _rsym_push_path_constraint(RSymExpr constraint, bool taken, + uintptr_t site_id); +RSymExpr _rsym_get_input_byte(size_t offset, uint8_t value); + +/* + * Call-stack tracing + */ +void _rsym_notify_call(uintptr_t site_id); +void _rsym_notify_ret(uintptr_t site_id); +void _rsym_notify_basic_block(uintptr_t site_id); + +/* + * Garbage collection + */ +void _rsym_expression_unreachable(RSymExpr *expressions, size_t num_elements); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/util/symcc_fuzzing_helper/src/main.rs b/util/symcc_fuzzing_helper/src/main.rs index 378e79d1..f9b0a870 100644 --- a/util/symcc_fuzzing_helper/src/main.rs +++ b/util/symcc_fuzzing_helper/src/main.rs @@ -227,24 +227,35 @@ impl State { let mut num_interesting = 0u64; let mut num_total = 0u64; + let mut num_failed = 0u64; let symcc_result = symcc .run(&input, tmp_dir.path().join("output")) .context("Failed to run SymCC")?; for new_test in symcc_result.test_cases.iter() { - let res = process_new_testcase(&new_test, &input, &tmp_dir, &afl_config, self)?; + let res = process_new_testcase(&new_test, &input, &tmp_dir, &afl_config, self); num_total += 1; - if res == TestcaseResult::New { - log::debug!("Test case is interesting"); - num_interesting += 1; - } + + match res { + Err(e) => { + log::error!("Showmap failed with {}", e); + num_failed += 1; + } + Ok(o) => { + if o == TestcaseResult::New { + log::debug!("Test case is interesting"); + num_interesting += 1; + } + } + }; } log::info!( - "Generated {} test cases ({} new)", + "Generated {} test cases ({} new, {} failed)", num_total, - num_interesting + num_interesting, + num_failed ); if symcc_result.killed {