From 5a96173455b13a8f128070bb6ffc41f1b634854a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Marjam=C3=A4ki?= Date: Fri, 29 Mar 2019 22:58:56 +0100 Subject: [PATCH] oss-fuzz: refactoring --- oss-fuzz/Makefile | 28 +--- oss-fuzz/main.cpp | 313 +---------------------------------------- oss-fuzz/translate.cpp | 28 ++++ oss-fuzz/type2.cpp | 181 ++++++++++++++++++++++++ oss-fuzz/type2.h | 8 ++ 5 files changed, 226 insertions(+), 332 deletions(-) create mode 100644 oss-fuzz/translate.cpp create mode 100644 oss-fuzz/type2.cpp create mode 100644 oss-fuzz/type2.h diff --git a/oss-fuzz/Makefile b/oss-fuzz/Makefile index 15c353270..a7b6c0111 100644 --- a/oss-fuzz/Makefile +++ b/oss-fuzz/Makefile @@ -1,32 +1,18 @@ -# Cppcheck fuzzer +# oss-fuzz client # -# Example compile command when using AFL: -# make CXX=../afl/afl-clang++ CXXFLAGS="-static -O1" -# -# Example compile command when using LibFuzzer: -# make CXX=clang++-6.0 CXXFLAGS="-O1 -fsanitize=address,fuzzer -DNO_MAIN" -# -# Standalone LibFuzzer tool -# make CXX=clang++-6.0 CXXFLAGS="-O1 -fsanitize=address,fuzzer-no-link" -TARGET_NAME=cppcheck-fuzzing-client - -CPPCHECK_DIR=../cppcheck +CPPCHECK_DIR=.. INCLUDE_DIR=-I ${CPPCHECK_DIR}/lib -I ${CPPCHECK_DIR}/externals/simplecpp -I ${CPPCHECK_DIR}/externals/tinyxml -SRC_FILES=main.cpp ${CPPCHECK_DIR}/externals/simplecpp/simplecpp.cpp ${CPPCHECK_DIR}/externals/tinyxml/tinyxml2.cpp ${CPPCHECK_DIR}/lib/*.cpp -OBJ_FILES=main.cpp ${CPPCHECK_DIR}/externals/simplecpp/simplecpp.o ${CPPCHECK_DIR}/externals/tinyxml/tinyxml2.o ${CPPCHECK_DIR}/lib/*.o +SRC_FILES=main.cpp type2.cpp ${CPPCHECK_DIR}/externals/simplecpp/simplecpp.cpp ${CPPCHECK_DIR}/externals/tinyxml/tinyxml2.cpp ${CPPCHECK_DIR}/lib/*.cpp -all: ${TARGET_NAME} - -${TARGET_NAME}: main.cpp - ${CXX} -std=c++11 -g ${CXXFLAGS} -o ${TARGET_NAME} ${INCLUDE_DIR} ${SRC_FILES} +all: oss-fuzz-client translate oss-fuzz-client: ${CXX} -std=c++11 -g -DNO_MAIN ${CXXFLAGS} -o oss-fuzz-client ${INCLUDE_DIR} ${SRC_FILES} -lFuzzingEngine -fast-build: main.cpp - ${CXX} -std=c++11 -g ${CXXFLAGS} -o ${TARGET_NAME} ${INCLUDE_DIR} ${OBJ_FILES} +translate: + g++ -o translate type2.cpp translate.cpp clean: - rm -f ${TARGET_NAME} + rm -f oss-fuzz-client translate diff --git a/oss-fuzz/main.cpp b/oss-fuzz/main.cpp index ab7a56566..ec40bd60e 100644 --- a/oss-fuzz/main.cpp +++ b/oss-fuzz/main.cpp @@ -1,20 +1,7 @@ -#include -#include -#include -#include #include "cppcheck.h" +#include "type2.h" -#ifdef NOMAIN -constexpr int type = 2; -constexpr bool execCppcheck = true; -#else -int type = 0; -bool execCppcheck = true; -#endif - -// Type 2 constants: -constexpr int TYPE2_BITS_VARNR = 2; // 4 local variables class CppcheckExecutor : public ErrorLogger { private: @@ -39,308 +26,12 @@ public: const unsigned int value) {} }; -static const char * const data0[] = { - "(", ")", "{", "}", "[", "]", - "<", "<=", "==", "!=", ">=", ">", - "+", "-", "*", "/", "%", "~", "&", "|", "^", "&&", "||", "++", "--", "=", "?", ":" - "name1", "name2", "name3", "name4", "name5", "name6", - "const", "void", "char", "int", "enum", "if", "else", "while", "for", "switch", "case", "default", "return", "continue", "break", "struct", "typedef", - ";", ",", - "1", "0.1", "0xff", "-5", "\"abc\"", "'x'", -}; - -static const char * const data1[] = { - "(", ")", "[", "]", "{", "}", - "<", "<=", "==", "!=", ">=", ">", - "+", "-", "*", "/", "%", "~", "&", "|", "^", "&&", "||", "++", "--", "=", "?", ":", - "name1", "name2", "name3", "name4", "name5", "name6", - "const", "void", "char", "int", "enum", "if", "else", "while", "for", "switch", "case", "default", "return", "continue", "break", "struct", "typedef", - ",", ";", "#", "##", ".", "->", "...", - "1", "0.1", "0xff", "-5", "\"abc\"", "'x'" -}; - -#define NUM(data) (sizeof(data)/sizeof(*data)) - -static void writeCode(std::ostream &ostr, int type, unsigned int *value, unsigned int *ones, const unsigned int min) { - static char par[20] = {' ',0}; - static char parindex = 0; - const unsigned int num = (type == 0) ? NUM(data0) : NUM(data1); - const char * const * const data = (type == 0) ? data0 : data1; - while (*ones > min) { - unsigned char i = *value % num; - *value = *value / num; - *ones = *ones / num; - - if (parindex < (sizeof(par)-1) && std::strchr("([{", *data[i])) - par[++parindex] = *data[i+1]; - else if (std::strchr(")]}", *data[i])) { - while (parindex > 0 && par[parindex] != *data[i]) - ostr << par[parindex--]; - if (parindex > 0) - parindex--; - else - ostr << *data[i-1]; - } - ostr << ' ' << data[i]; - } - - if (min == 0) { - while (parindex > 0) - ostr << par[parindex--]; - } -} - -int getValue(const uint8_t *data, size_t dataSize, uint8_t maxValue, bool *done = nullptr) { - static size_t pos; // current "data" position - static int dataValue; // value extracted from data - static int ones; // ones. This variable tracks if we need to add more stuff in "dataValue". - - // Shift more bits from "data" into "dataValue" if needed - while (pos < dataSize && ones < 0xFFFF) { - ones = (ones << 8) | 0xff; - dataValue = (dataValue << 8) | data[pos]; - pos++; - } - - if (done) - *done = (ones == 0); - - if (maxValue == 0) - return 0; - - // Shift out info from "dataValue" using % . Using & and >> would work but then we are limited to "power of 2" max value. - const int ret = dataValue % maxValue; - ones /= maxValue; - dataValue /= maxValue; - return ret; -} - -static std::string generateExpression2_lvalue(const uint8_t *data, size_t dataSize) { - return "var" + std::to_string(1 + getValue(data, dataSize, 5)); -} - -static std::string generateExpression2_Op(const uint8_t *data, size_t dataSize, int numberOfGlobalConstants) { - std::ostringstream code; - switch (getValue(data, dataSize, 3)) - { - case 0: - code << generateExpression2_lvalue(data, dataSize); - break; - case 1: - code << "globalconstant" << (1 + getValue(data, dataSize, numberOfGlobalConstants)); - break; - case 2: - code << (getValue(data, dataSize, 0x80) * 0x80 + getValue(data, dataSize, 0x80)); - break; - }; - return code.str(); -} - -static std::string generateExpression2_Expr(const uint8_t *data, size_t dataSize, int numberOfGlobalConstants, int depth=0) { - ++depth; - const unsigned int type = (depth > 3) ? 0 : getValue(data, dataSize, 3); - const char binop[] = "=<>+-*/%&|^"; - const char *unop[] = {"++","--","()","~"}; - - switch (type) { - case 0: - return generateExpression2_Op(data, dataSize, numberOfGlobalConstants); - case 1: { - const char op = binop[getValue(data,dataSize,sizeof(binop)-1)]; - const std::string lhs = (op == '=') ? - generateExpression2_lvalue(data, dataSize) : - generateExpression2_Expr(data, dataSize, numberOfGlobalConstants, depth); - const std::string rhs = generateExpression2_Expr(data, dataSize, numberOfGlobalConstants, depth); - - return lhs + op + rhs; - } - case 2: { - const char *u = unop[getValue(data,dataSize,sizeof(unop)/sizeof(*unop))]; - if (u == std::string("()")) - return "(" + generateExpression2_Expr(data, dataSize, numberOfGlobalConstants, depth) + ")"; - else if (u == std::string("++") || u == std::string("--")) - return u + generateExpression2_lvalue(data, dataSize); - return u + generateExpression2_Expr(data, dataSize, numberOfGlobalConstants, depth); - } - default: - break; - }; - - return "0"; -} - - -static std::string generateExpression2_Cond(const uint8_t *data, size_t dataSize, int numberOfGlobalConstants) -{ - const char *comp[] = {"==", "!=", "<", "<=", ">", ">="}; - const int i = getValue(data, dataSize, 6); - const std::string lhs = generateExpression2_Expr(data, dataSize, numberOfGlobalConstants); - const std::string rhs = generateExpression2_Expr(data, dataSize, numberOfGlobalConstants); - return lhs + comp[i] + rhs; -} - - -static std::string functionStart() { - static int functionNumber; - return "int f" + std::to_string(++functionNumber) + "()\n" - "{\n"; -} - -static std::string generateExpression2_conditionalCode(const std::string &indent, - const uint8_t *data, - size_t dataSize, - int numberOfGlobalConstants) -{ - std::ostringstream code; - - if (indent.empty()) - code << functionStart(); - else - code << indent << "{\n"; - - for (int line = 0; line < 4 || indent.empty(); ++line) - { - bool done = false; - const int type1 = getValue(data, dataSize, 8, &done); - if (done) - break; - - const int mostLikelyType = (line >= 2) ? 4 : 0; // should var assignment or return be more likely? - - const int type2 = (indent.size() >= 12) ? - mostLikelyType : // max indentation, no inner conditions - ((type1 >= 5) ? mostLikelyType : type1); - - if (type2 == 0) { - code << indent << " var" << getValue(data, dataSize, 5) << "=" << generateExpression2_Expr(data, dataSize, numberOfGlobalConstants) << ";\n"; - } else if (type2 == 1) { - code << indent << " if (" << generateExpression2_Cond(data, dataSize, numberOfGlobalConstants) << ")\n"; - code << generateExpression2_conditionalCode(indent + " ", data, dataSize, numberOfGlobalConstants); - } else if (type2 == 2) { - code << indent << " if (" << generateExpression2_Cond(data, dataSize, numberOfGlobalConstants) << ")\n"; - code << generateExpression2_conditionalCode(indent + " ", data, dataSize, numberOfGlobalConstants); - code << indent << " else\n"; - code << generateExpression2_conditionalCode(indent + " ", data, dataSize, numberOfGlobalConstants); - } else if (type2 == 3) { - code << indent << " while (" << generateExpression2_Cond(data, dataSize, numberOfGlobalConstants) << ")\n"; - code << generateExpression2_conditionalCode(indent + " ", data, dataSize, numberOfGlobalConstants); - } else if (type2 == 4) { - code << indent << " return " << generateExpression2_Expr(data, dataSize, numberOfGlobalConstants) << ";\n"; - if (indent.empty()) - code << "}\n\n" << functionStart(); - else - break; - } - } - - if (!indent.empty()) - code << indent << "}\n"; - else - code << " return 0;\n}\n"; - return code.str(); -} - -static std::string generateCode2(const uint8_t *data, size_t dataSize) { - std::ostringstream code; - - // create global constants - constexpr int numberOfGlobalConstants = 0; - /* - const int numberOfGlobalConstants = getValue(data, dataSize, 5); - for (int nr = 1; nr <= numberOfGlobalConstants; nr++) { - const char *types[4] = {"char", "int", "long long", "float"}; - code << "const " << types[getValue(data, dataSize, 4)] << " globalconstant" << nr << " = " << generateExpression2_Expr(data, dataSize, nr - 1) << ";\n"; - } - */ - - code << "int var1 = 1;\n" - "int var2 = 0;\n" - "int var3 = 1;\n" - "int var4 = 0;\n" - "int var5 = -1;\n\n"; - - code << generateExpression2_conditionalCode("", data, dataSize, numberOfGlobalConstants); - - return code.str(); -} - -static std::string generateCode(const uint8_t *data, size_t dataSize) { - if (type == 2) - return generateCode2(data, dataSize); - - std::ostringstream ostr; - unsigned int value = 0; - unsigned int ones = 0; - for (size_t i = 0; i < dataSize; ++i) { - uint8_t c = data[i]; - value = (value << 8) | c; - ones = (ones << 8) | 0xff; - writeCode(ostr, type, &value, &ones, 0x100); - } - writeCode(ostr, type, &value, &ones, 0); - - std::string code; - switch (type) { - case 0: - code = ostr.str(); - break; - case 1: - code = "void f() {\n x=" + ostr.str() + ";\n}"; - break; - } - return code; -} extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t dataSize) { CppcheckExecutor cppcheckExecutor; - cppcheckExecutor.run(generateCode(data, dataSize)); + cppcheckExecutor.run(generateCode2(data, dataSize)); return 0; } -#ifndef NO_MAIN -int main(int argc, char **argv) { - const char *filename = nullptr; - bool execCppcheck = true; - - std::cout << "Command line:"; - for (int i = 0; i < argc; ++i) - std::cout << " " << argv[i]; - std::cout << "\n"; - - for (int i = 1; i < argc; ++i) { - if (strcmp(argv[i],"--type1")==0) - type = 1; - else if (strcmp(argv[i],"--type2")==0) - type = 2; - else if (strcmp(argv[i],"--translate-input")==0) - execCppcheck = false; - else if (*argv[i] == '-') { - std::cout << "Invalid option: " << argv[i] << std::endl; - } else - filename = argv[i]; - } - - if (!filename) { - std::cout << "Invalid args, no filename\n"; - return 1; - } - - std::ifstream f(filename); - if (!f.is_open()) { - std::cout << "failed to open file:" << filename << "\n"; - return 1; - } - - std::string str((std::istreambuf_iterator(f)), - std::istreambuf_iterator()); - - if (execCppcheck) - LLVMFuzzerTestOneInput((const uint8_t *)str.data(), str.size()); - else - std::cout << generateCode((const uint8_t *)str.data(), str.size()) << std::endl; - - return 0; -} -#endif diff --git a/oss-fuzz/translate.cpp b/oss-fuzz/translate.cpp new file mode 100644 index 000000000..4edd2b568 --- /dev/null +++ b/oss-fuzz/translate.cpp @@ -0,0 +1,28 @@ + +#include +#include + +#include "type2.h" + +int main(int argc, char **argv) { + const char *filename = argc==2 ? argv[1] : nullptr; + + if (!filename) { + std::cout << "Invalid args, no filename\n"; + return 1; + } + + std::ifstream f(filename); + if (!f.is_open()) { + std::cout << "failed to open file:" << filename << "\n"; + return 1; + } + + std::string str((std::istreambuf_iterator(f)), + std::istreambuf_iterator()); + + std::cout << generateCode2((const uint8_t *)str.data(), str.size()) << std::endl; + + return 0; +} + diff --git a/oss-fuzz/type2.cpp b/oss-fuzz/type2.cpp new file mode 100644 index 000000000..c127ad2f8 --- /dev/null +++ b/oss-fuzz/type2.cpp @@ -0,0 +1,181 @@ + +#include +#include "type2.h" + + + +static int getValue(const uint8_t *data, size_t dataSize, uint8_t maxValue, bool *done = nullptr) { + static size_t pos; // current "data" position + static int dataValue; // value extracted from data + static int ones; // ones. This variable tracks if we need to add more stuff in "dataValue". + + // Shift more bits from "data" into "dataValue" if needed + while (pos < dataSize && ones < 0xFFFF) { + ones = (ones << 8) | 0xff; + dataValue = (dataValue << 8) | data[pos]; + pos++; + } + + if (done) + *done = (ones == 0); + + if (maxValue == 0) + return 0; + + // Shift out info from "dataValue" using % . Using & and >> would work but then we are limited to "power of 2" max value. + const int ret = dataValue % maxValue; + ones /= maxValue; + dataValue /= maxValue; + return ret; +} + +static std::string generateExpression2_lvalue(const uint8_t *data, size_t dataSize) { + return "var" + std::to_string(1 + getValue(data, dataSize, 5)); +} + +static std::string generateExpression2_Op(const uint8_t *data, size_t dataSize, int numberOfGlobalConstants) { + std::ostringstream code; + switch (getValue(data, dataSize, 3)) + { + case 0: + code << generateExpression2_lvalue(data, dataSize); + break; + case 1: + code << "globalconstant" << (1 + getValue(data, dataSize, numberOfGlobalConstants)); + break; + case 2: + code << (getValue(data, dataSize, 0x80) * 0x80 + getValue(data, dataSize, 0x80)); + break; + }; + return code.str(); +} + +static std::string generateExpression2_Expr(const uint8_t *data, size_t dataSize, int numberOfGlobalConstants, int depth=0) { + ++depth; + const unsigned int type = (depth > 3) ? 0 : getValue(data, dataSize, 3); + const char binop[] = "=<>+-*/%&|^"; + const char *unop[] = {"++","--","()","~"}; + + switch (type) { + case 0: + return generateExpression2_Op(data, dataSize, numberOfGlobalConstants); + case 1: { + const char op = binop[getValue(data,dataSize,sizeof(binop)-1)]; + const std::string lhs = (op == '=') ? + generateExpression2_lvalue(data, dataSize) : + generateExpression2_Expr(data, dataSize, numberOfGlobalConstants, depth); + const std::string rhs = generateExpression2_Expr(data, dataSize, numberOfGlobalConstants, depth); + + return lhs + op + rhs; + } + case 2: { + const char *u = unop[getValue(data,dataSize,sizeof(unop)/sizeof(*unop))]; + if (u == std::string("()")) + return "(" + generateExpression2_Expr(data, dataSize, numberOfGlobalConstants, depth) + ")"; + else if (u == std::string("++") || u == std::string("--")) + return u + generateExpression2_lvalue(data, dataSize); + return u + generateExpression2_Expr(data, dataSize, numberOfGlobalConstants, depth); + } + default: + break; + }; + + return "0"; +} + + +static std::string generateExpression2_Cond(const uint8_t *data, size_t dataSize, int numberOfGlobalConstants) +{ + const char *comp[] = {"==", "!=", "<", "<=", ">", ">="}; + const int i = getValue(data, dataSize, 6); + const std::string lhs = generateExpression2_Expr(data, dataSize, numberOfGlobalConstants); + const std::string rhs = generateExpression2_Expr(data, dataSize, numberOfGlobalConstants); + return lhs + comp[i] + rhs; +} + + +static std::string functionStart() { + static int functionNumber; + return "int f" + std::to_string(++functionNumber) + "()\n" + "{\n"; +} + +static std::string generateExpression2_conditionalCode(const std::string &indent, + const uint8_t *data, + size_t dataSize, + int numberOfGlobalConstants) +{ + std::ostringstream code; + + if (indent.empty()) + code << functionStart(); + else + code << indent << "{\n"; + + for (int line = 0; line < 4 || indent.empty(); ++line) + { + bool done = false; + const int type1 = getValue(data, dataSize, 8, &done); + if (done) + break; + + const int mostLikelyType = (line >= 2) ? 4 : 0; // should var assignment or return be more likely? + + const int type2 = (indent.size() >= 12) ? + mostLikelyType : // max indentation, no inner conditions + ((type1 >= 5) ? mostLikelyType : type1); + + if (type2 == 0) { + code << indent << " var" << getValue(data, dataSize, 5) << "=" << generateExpression2_Expr(data, dataSize, numberOfGlobalConstants) << ";\n"; + } else if (type2 == 1) { + code << indent << " if (" << generateExpression2_Cond(data, dataSize, numberOfGlobalConstants) << ")\n"; + code << generateExpression2_conditionalCode(indent + " ", data, dataSize, numberOfGlobalConstants); + } else if (type2 == 2) { + code << indent << " if (" << generateExpression2_Cond(data, dataSize, numberOfGlobalConstants) << ")\n"; + code << generateExpression2_conditionalCode(indent + " ", data, dataSize, numberOfGlobalConstants); + code << indent << " else\n"; + code << generateExpression2_conditionalCode(indent + " ", data, dataSize, numberOfGlobalConstants); + } else if (type2 == 3) { + code << indent << " while (" << generateExpression2_Cond(data, dataSize, numberOfGlobalConstants) << ")\n"; + code << generateExpression2_conditionalCode(indent + " ", data, dataSize, numberOfGlobalConstants); + } else if (type2 == 4) { + code << indent << " return " << generateExpression2_Expr(data, dataSize, numberOfGlobalConstants) << ";\n"; + if (indent.empty()) + code << "}\n\n" << functionStart(); + else + break; + } + } + + if (!indent.empty()) + code << indent << "}\n"; + else + code << " return 0;\n}\n"; + return code.str(); +} + +std::string generateCode2(const uint8_t *data, size_t dataSize) { + std::ostringstream code; + + // create global constants + constexpr int numberOfGlobalConstants = 0; + /* + const int numberOfGlobalConstants = getValue(data, dataSize, 5); + for (int nr = 1; nr <= numberOfGlobalConstants; nr++) { + const char *types[4] = {"char", "int", "long long", "float"}; + code << "const " << types[getValue(data, dataSize, 4)] << " globalconstant" << nr << " = " << generateExpression2_Expr(data, dataSize, nr - 1) << ";\n"; + } + */ + + code << "int var1 = 1;\n" + "int var2 = 0;\n" + "int var3 = 1;\n" + "int var4 = 0;\n" + "int var5 = -1;\n\n"; + + code << generateExpression2_conditionalCode("", data, dataSize, numberOfGlobalConstants); + + return code.str(); +} + + diff --git a/oss-fuzz/type2.h b/oss-fuzz/type2.h new file mode 100644 index 000000000..36bd9a682 --- /dev/null +++ b/oss-fuzz/type2.h @@ -0,0 +1,8 @@ + +#pragma once + +#include +#include + +std::string generateCode2(const uint8_t *data, size_t dataSize); +