#include "Runtime.h"
#include "GarbageCollection.h"
#if __has_include(<filesystem>)
#define HAVE_FILESYSTEM 1
#elif __has_include(<experimental/filesystem>)
#define HAVE_FILESYSTEM 0
#else
#error "We need either <filesystem> or the older <experimental/filesystem>."
#endif
#include <atomic>
#include <fstream>
#include <iostream>
#include <iterator>
#include <map>
#include <unordered_set>
#if HAVE_FILESYSTEM
#include <filesystem>
#else
#include <experimental/filesystem>
#endif
#ifdef DEBUG_RUNTIME
#include <chrono>
#endif
#include <cstdio>
#include <afl_trace_map.h>
#include <call_stack_manager.h>
#include <expr_builder.h>
#include <solver.h>
#include <llvm/ADT/APInt.h>
#include <llvm/ADT/ArrayRef.h>
#include <Config.h>
#include <LibcWrappers.h>
#include <Shadow.h>
namespace qsym {
ExprBuilder *g_expr_builder;
Solver *g_solver;
CallStackManager g_call_stack_manager;
z3::context *g_z3_context;
}
namespace {
std::atomic_flag g_initialized = ATOMIC_FLAG_INIT;
std::string inputFileName;
void deleteInputFile() { std::remove(inputFileName.c_str()); }
std::map<SymExpr, qsym::ExprRef> allocatedExpressions;
SymExpr registerExpression(const qsym::ExprRef &expr) {
SymExpr rawExpr = expr.get();
if (allocatedExpressions.count(rawExpr) == 0) {
allocatedExpressions[rawExpr] = expr;
}
return rawExpr;
}
}
using namespace qsym;
#if HAVE_FILESYSTEM
namespace fs = std::filesystem;
#else
namespace fs = std::experimental::filesystem;
#endif
void _sym_initialize(void) {
if (g_initialized.test_and_set())
return;
loadConfig();
initLibcWrappers();
std::cerr << "This is SymCC running with the QSYM backend" << std::endl;
if (g_config.fullyConcrete) {
std::cerr
<< "Performing fully concrete execution (i.e., without symbolic input)"
<< std::endl;
return;
}
if (!fs::exists(g_config.outputDir) ||
!fs::is_directory(g_config.outputDir)) {
std::cerr << "Error: the output directory " << g_config.outputDir
<< " (configurable via SYMCC_OUTPUT_DIR) does not exist."
<< std::endl;
exit(-1);
}
if (g_config.inputFile.empty()) {
std::cerr << "Reading program input until EOF (use Ctrl+D in a terminal)..."
<< std::endl;
std::istreambuf_iterator<char> in_begin(std::cin), in_end;
std::vector<char> inputData(in_begin, in_end);
inputFileName = std::tmpnam(nullptr);
std::ofstream inputFile(inputFileName, std::ios::trunc);
std::copy(inputData.begin(), inputData.end(),
std::ostreambuf_iterator<char>(inputFile));
inputFile.close();
#ifdef DEBUG_RUNTIME
std::cerr << "Loaded input:" << std::endl;
std::copy(inputData.begin(), inputData.end(),
std::ostreambuf_iterator<char>(std::cerr));
std::cerr << std::endl;
#endif
atexit(deleteInputFile);
auto *newStdin = freopen(inputFileName.c_str(), "r", stdin);
if (newStdin == nullptr) {
perror("Failed to reopen stdin");
exit(-1);
}
} else {
inputFileName = g_config.inputFile;
std::cerr << "Making data read from " << inputFileName << " as symbolic"
<< std::endl;
}
g_z3_context = new z3::context{};
g_solver =
new Solver(inputFileName, g_config.outputDir, g_config.aflCoverageMap);
g_expr_builder = g_config.pruning ? PruneExprBuilder::create()
: SymbolicExprBuilder::create();
}
SymExpr _sym_build_integer(uint64_t value, uint8_t bits) {
if constexpr (sizeof(uint64_t) == sizeof(uintptr_t)) {
return registerExpression(g_expr_builder->createConstant(value, bits));
} else {
if (uintptr_t value32 = value; value32 == value)
return registerExpression(g_expr_builder->createConstant(value32, bits));
return registerExpression(
g_expr_builder->createConstant({64, value}, bits));
}
}
SymExpr _sym_build_integer128(uint64_t high, uint64_t low) {
std::array<uint64_t, 2> words = {low, high};
return registerExpression(g_expr_builder->createConstant({128, words}, 128));
}
SymExpr _sym_build_null_pointer() {
return registerExpression(
g_expr_builder->createConstant(0, sizeof(uintptr_t) * 8));
}
SymExpr _sym_build_true() {
return registerExpression(g_expr_builder->createTrue());
}
SymExpr _sym_build_false() {
return registerExpression(g_expr_builder->createFalse());
}
SymExpr _sym_build_bool(bool value) {
return registerExpression(g_expr_builder->createBool(value));
}
#define DEF_BINARY_EXPR_BUILDER(name, qsymName) \
SymExpr _sym_build_##name(SymExpr a, SymExpr b) { \
return registerExpression(g_expr_builder->create##qsymName( \
allocatedExpressions.at(a), allocatedExpressions.at(b))); \
}
DEF_BINARY_EXPR_BUILDER(add, Add)
DEF_BINARY_EXPR_BUILDER(sub, Sub)
DEF_BINARY_EXPR_BUILDER(mul, Mul)
DEF_BINARY_EXPR_BUILDER(unsigned_div, UDiv)
DEF_BINARY_EXPR_BUILDER(signed_div, SDiv)
DEF_BINARY_EXPR_BUILDER(unsigned_rem, URem)
DEF_BINARY_EXPR_BUILDER(signed_rem, SRem)
DEF_BINARY_EXPR_BUILDER(shift_left, Shl)
DEF_BINARY_EXPR_BUILDER(logical_shift_right, LShr)
DEF_BINARY_EXPR_BUILDER(arithmetic_shift_right, AShr)
DEF_BINARY_EXPR_BUILDER(signed_less_than, Slt)
DEF_BINARY_EXPR_BUILDER(signed_less_equal, Sle)
DEF_BINARY_EXPR_BUILDER(signed_greater_than, Sgt)
DEF_BINARY_EXPR_BUILDER(signed_greater_equal, Sge)
DEF_BINARY_EXPR_BUILDER(unsigned_less_than, Ult)
DEF_BINARY_EXPR_BUILDER(unsigned_less_equal, Ule)
DEF_BINARY_EXPR_BUILDER(unsigned_greater_than, Ugt)
DEF_BINARY_EXPR_BUILDER(unsigned_greater_equal, Uge)
DEF_BINARY_EXPR_BUILDER(equal, Equal)
DEF_BINARY_EXPR_BUILDER(not_equal, Distinct)
DEF_BINARY_EXPR_BUILDER(bool_and, LAnd)
DEF_BINARY_EXPR_BUILDER(and, And)
DEF_BINARY_EXPR_BUILDER(bool_or, LOr)
DEF_BINARY_EXPR_BUILDER(or, Or)
DEF_BINARY_EXPR_BUILDER(bool_xor, Distinct)
DEF_BINARY_EXPR_BUILDER(xor, Xor)
#undef DEF_BINARY_EXPR_BUILDER
SymExpr _sym_build_neg(SymExpr expr) {
return registerExpression(
g_expr_builder->createNeg(allocatedExpressions.at(expr)));
}
SymExpr _sym_build_not(SymExpr expr) {
return registerExpression(
g_expr_builder->createNot(allocatedExpressions.at(expr)));
}
SymExpr _sym_build_sext(SymExpr expr, uint8_t bits) {
return registerExpression(g_expr_builder->createSExt(
allocatedExpressions.at(expr), bits + expr->bits()));
}
SymExpr _sym_build_zext(SymExpr expr, uint8_t bits) {
return registerExpression(g_expr_builder->createZExt(
allocatedExpressions.at(expr), bits + expr->bits()));
}
SymExpr _sym_build_trunc(SymExpr expr, uint8_t bits) {
return registerExpression(
g_expr_builder->createTrunc(allocatedExpressions.at(expr), bits));
}
void _sym_push_path_constraint(SymExpr constraint, int taken,
uintptr_t site_id) {
if (constraint == nullptr)
return;
g_solver->addJcc(allocatedExpressions.at(constraint), taken != 0, site_id);
}
SymExpr _sym_get_input_byte(size_t offset) {
return registerExpression(g_expr_builder->createRead(offset));
}
SymExpr _sym_concat_helper(SymExpr a, SymExpr b) {
return registerExpression(g_expr_builder->createConcat(
allocatedExpressions.at(a), allocatedExpressions.at(b)));
}
SymExpr _sym_extract_helper(SymExpr expr, size_t first_bit, size_t last_bit) {
return registerExpression(g_expr_builder->createExtract(
allocatedExpressions.at(expr), last_bit, first_bit - last_bit + 1));
}
size_t _sym_bits_helper(SymExpr expr) { return expr->bits(); }
SymExpr _sym_build_bool_to_bits(SymExpr expr, uint8_t bits) {
return registerExpression(
g_expr_builder->boolToBit(allocatedExpressions.at(expr), bits));
}
#define UNSUPPORTED(prototype) \
prototype { return nullptr; }
UNSUPPORTED(SymExpr _sym_build_float(double, int))
UNSUPPORTED(SymExpr _sym_build_fp_add(SymExpr, SymExpr))
UNSUPPORTED(SymExpr _sym_build_fp_sub(SymExpr, SymExpr))
UNSUPPORTED(SymExpr _sym_build_fp_mul(SymExpr, SymExpr))
UNSUPPORTED(SymExpr _sym_build_fp_div(SymExpr, SymExpr))
UNSUPPORTED(SymExpr _sym_build_fp_rem(SymExpr, SymExpr))
UNSUPPORTED(SymExpr _sym_build_fp_abs(SymExpr))
UNSUPPORTED(SymExpr _sym_build_float_ordered_greater_than(SymExpr, SymExpr))
UNSUPPORTED(SymExpr _sym_build_float_ordered_greater_equal(SymExpr, SymExpr))
UNSUPPORTED(SymExpr _sym_build_float_ordered_less_than(SymExpr, SymExpr))
UNSUPPORTED(SymExpr _sym_build_float_ordered_less_equal(SymExpr, SymExpr))
UNSUPPORTED(SymExpr _sym_build_float_ordered_equal(SymExpr, SymExpr))
UNSUPPORTED(SymExpr _sym_build_float_ordered_not_equal(SymExpr, SymExpr))
UNSUPPORTED(SymExpr _sym_build_float_ordered(SymExpr, SymExpr))
UNSUPPORTED(SymExpr _sym_build_float_unordered(SymExpr, SymExpr))
UNSUPPORTED(SymExpr _sym_build_float_unordered_greater_than(SymExpr, SymExpr))
UNSUPPORTED(SymExpr _sym_build_float_unordered_greater_equal(SymExpr, SymExpr))
UNSUPPORTED(SymExpr _sym_build_float_unordered_less_than(SymExpr, SymExpr))
UNSUPPORTED(SymExpr _sym_build_float_unordered_less_equal(SymExpr, SymExpr))
UNSUPPORTED(SymExpr _sym_build_float_unordered_equal(SymExpr, SymExpr))
UNSUPPORTED(SymExpr _sym_build_float_unordered_not_equal(SymExpr, SymExpr))
UNSUPPORTED(SymExpr _sym_build_int_to_float(SymExpr, int, int))
UNSUPPORTED(SymExpr _sym_build_float_to_float(SymExpr, int))
UNSUPPORTED(SymExpr _sym_build_bits_to_float(SymExpr, int))
UNSUPPORTED(SymExpr _sym_build_float_to_bits(SymExpr))
UNSUPPORTED(SymExpr _sym_build_float_to_signed_integer(SymExpr, uint8_t))
UNSUPPORTED(SymExpr _sym_build_float_to_unsigned_integer(SymExpr, uint8_t))
#undef UNSUPPORTED
#undef H
void _sym_notify_call(uintptr_t site_id) {
g_call_stack_manager.visitCall(site_id);
}
void _sym_notify_ret(uintptr_t site_id) {
g_call_stack_manager.visitRet(site_id);
}
void _sym_notify_basic_block(uintptr_t site_id) {
g_call_stack_manager.visitBasicBlock(site_id);
}
const char *_sym_expr_to_string(SymExpr expr) {
static char buffer[4096];
auto expr_string = expr->toString();
auto copied = expr_string.copy(
buffer, std::min(expr_string.length(), sizeof(buffer) - 1));
buffer[copied] = '\0';
return buffer;
}
bool _sym_feasible(SymExpr expr) {
expr->simplify();
g_solver->push();
g_solver->add(expr->toZ3Expr());
bool feasible = (g_solver->check() == z3::sat);
g_solver->pop();
return feasible;
}
void _sym_collect_garbage() {
if (allocatedExpressions.size() < g_config.garbageCollectionThreshold)
return;
#ifdef DEBUG_RUNTIME
auto start = std::chrono::high_resolution_clock::now();
#endif
auto reachableExpressions = collectReachableExpressions();
for (auto expr_it = allocatedExpressions.begin();
expr_it != allocatedExpressions.end();) {
if (reachableExpressions.count(expr_it->first) == 0) {
expr_it = allocatedExpressions.erase(expr_it);
} else {
++expr_it;
}
}
#ifdef DEBUG_RUNTIME
auto end = std::chrono::high_resolution_clock::now();
std::cerr << "After garbage collection: " << allocatedExpressions.size()
<< " expressions remain" << std::endl
<< "\t(collection took "
<< std::chrono::duration_cast<std::chrono::milliseconds>(end -
start)
.count()
<< " milliseconds)" << std::endl;
#endif
}