Browse Source

Made use of libfuzzer, and fixed a few edge cases identified by libfuzzer

pipelines/235045657
eidheim 5 years ago
parent
commit
78c3b59528
  1. 7
      CMakeLists.txt
  2. 3
      src/cmake.cpp
  3. 111
      src/ctags.cpp
  4. 2
      src/grep.cpp
  5. 168
      tests/CMakeLists.txt
  6. 6
      tests/fuzzers/README.md
  7. 12
      tests/fuzzers/cmake.cpp
  8. 13
      tests/fuzzers/ctags.cpp
  9. 28
      tests/fuzzers/docstring.cpp
  10. 28
      tests/fuzzers/doxygen.cpp
  11. 12
      tests/fuzzers/grep.cpp
  12. 28
      tests/fuzzers/markdown.cpp

7
CMakeLists.txt

@ -44,6 +44,7 @@ if(${CMAKE_SYSTEM_NAME} MATCHES FreeBSD)
endif() endif()
option(BUILD_TESTING "Build tests") option(BUILD_TESTING "Build tests")
option(BUILD_FUZZING "Build tests")
option(LIBCLANG_PATH "Use custom path for libclang") option(LIBCLANG_PATH "Use custom path for libclang")
option(LIBLLDB_PATH "Use custom path for liblldb") option(LIBLLDB_PATH "Use custom path for liblldb")
@ -115,7 +116,9 @@ include_directories(
add_subdirectory("src") add_subdirectory("src")
if(BUILD_TESTING) if(BUILD_TESTING OR BUILD_FUZZING)
enable_testing() if(BUILD_TESTING)
enable_testing()
endif()
add_subdirectory(tests) add_subdirectory(tests)
endif() endif()

3
src/cmake.cpp

@ -311,7 +311,8 @@ void CMake::parse_file(const std::string &src, std::map<std::string, std::list<s
variables.emplace("PROJECT_NAME", function->parameters); variables.emplace("PROJECT_NAME", function->parameters);
} }
} }
on_function(std::move(*function)); if(on_function)
on_function(std::move(*function));
} }
} }
} }

111
src/ctags.cpp

@ -8,6 +8,8 @@
#include <vector> #include <vector>
Ctags::Ctags(const boost::filesystem::path &path, bool enable_scope, bool enable_kind, const std::string &languages) : enable_scope(enable_scope), enable_kind(enable_kind) { Ctags::Ctags(const boost::filesystem::path &path, bool enable_scope, bool enable_kind, const std::string &languages) : enable_scope(enable_scope), enable_kind(enable_kind) {
if(path.empty())
return;
// TODO: When universal ctags is available on all platforms (Ubuntu 20.04 LTS does), add: --pattern-length-limit=0 // TODO: When universal ctags is available on all platforms (Ubuntu 20.04 LTS does), add: --pattern-length-limit=0
auto options = " --sort=foldcase -I \"override noexcept\" -f -" + (!languages.empty() ? " --languages=" + languages : std::string()); auto options = " --sort=foldcase -I \"override noexcept\" -f -" + (!languages.empty() ? " --languages=" + languages : std::string());
std::string fields(" --fields=n"); std::string fields(" --fields=n");
@ -57,10 +59,8 @@ Ctags::Location Ctags::get_location(const std::string &line_, bool add_markup, b
auto &line = line_; auto &line = line_;
#endif #endif
auto symbol_end = line.find('\t'); auto symbol_end = line.find('\t');
if(symbol_end == std::string::npos) { if(symbol_end == std::string::npos)
std::cerr << "Warning (ctags): could not parse line: " << line << std::endl;
return location; return location;
}
location.symbol = line.substr(0, symbol_end); location.symbol = line.substr(0, symbol_end);
if(9 < location.symbol.size() && location.symbol[8] == ' ' && starts_with(location.symbol, "operator")) { if(9 < location.symbol.size() && location.symbol[8] == ' ' && starts_with(location.symbol, "operator")) {
auto &chr = location.symbol[9]; auto &chr = location.symbol[9];
@ -69,68 +69,56 @@ Ctags::Location Ctags::get_location(const std::string &line_, bool add_markup, b
} }
auto file_start = symbol_end + 1; auto file_start = symbol_end + 1;
if(file_start >= line.size()) { if(file_start >= line.size())
std::cerr << "Warning (ctags): could not parse line: " << line << std::endl;
return location; return location;
}
auto file_end = line.find('\t', file_start); auto file_end = line.find('\t', file_start);
if(file_end == std::string::npos) { if(file_end == std::string::npos)
std::cerr << "Warning (ctags): could not parse line: " << line << std::endl;
return location; return location;
}
location.file_path = line.substr(file_start, file_end - file_start); location.file_path = line.substr(file_start, file_end - file_start);
auto source_start = file_end + 3; auto source_start = file_end + 3;
if(source_start >= line.size()) { if(source_start >= line.size())
std::cerr << "Warning (ctags): could not parse line: " << line << std::endl;
return location; return location;
}
location.index = 0; location.index = 0;
while(source_start < line.size() && (line[source_start] == ' ' || line[source_start] == '\t')) { while(source_start < line.size() && (line[source_start] == ' ' || line[source_start] == '\t')) {
++source_start; ++source_start;
++location.index; ++location.index;
} }
auto source_end = line.find("/;\"\t", source_start); auto source_end = line.find("/;\"\t", source_start);
if(source_end == std::string::npos) { if(source_end == std::string::npos)
std::cerr << "Warning (ctags): could not parse line: " << line << std::endl;
return location; return location;
}
// Unescape source // Unescape source
auto end = source_end - (line[source_end - 1] == '$' ? 1 : 0); if(source_end > source_start) {
location.source.reserve(end - source_start); auto end = source_end - (line[source_end - 1] == '$' ? 1 : 0);
bool escaped = false; location.source.reserve(end - source_start);
for(auto i = source_start; i < end; ++i) { bool escaped = false;
if(!escaped && line[i] == '\\') { for(auto i = source_start; i < end; ++i) {
escaped = true; if(!escaped && line[i] == '\\') {
continue; escaped = true;
continue;
}
escaped = false;
location.source += line[i];
} }
escaped = false;
location.source += line[i];
} }
size_t line_start; size_t line_start;
if(enable_kind) { if(enable_kind) {
auto kind_start = source_end + 4; auto kind_start = source_end + 4;
if(kind_start >= line.size()) { if(kind_start >= line.size())
std::cerr << "Warning (ctags): could not parse line: " << line << std::endl;
return location; return location;
}
auto kind_end = line.find('\t', kind_start); auto kind_end = line.find('\t', kind_start);
if(kind_end == std::string::npos) { if(kind_end == std::string::npos)
std::cerr << "Warning (ctags): could not parse line: " << line << std::endl;
return location; return location;
}
location.kind = line.substr(kind_start, kind_end - kind_start); location.kind = line.substr(kind_start, kind_end - kind_start);
line_start = kind_start + location.kind.size() + 6; line_start = kind_start + location.kind.size() + 6;
} }
else else
line_start = source_end + 9; line_start = source_end + 9;
if(line_start >= line.size()) { if(line_start >= line.size())
std::cerr << "Warning (ctags): could not parse line: " << line << std::endl;
return location; return location;
}
auto line_end = line.find('\t', line_start); auto line_end = line.find('\t', line_start);
size_t line_size = line_end == std::string::npos ? std::string::npos : line_end - line_start; size_t line_size = line_end == std::string::npos ? std::string::npos : line_end - line_start;
try { try {
@ -146,39 +134,40 @@ Ctags::Location Ctags::get_location(const std::string &line_, bool add_markup, b
location.scope = line.substr(scope_start + 1); location.scope = line.substr(scope_start + 1);
} }
if(add_markup) { if(!location.symbol.empty()) {
location.source = Glib::Markup::escape_text(location.source); if(add_markup) {
std::string symbol = Glib::Markup::escape_text(location.symbol); location.source = Glib::Markup::escape_text(location.source);
if(symbol_ends_with_open_parenthesis) std::string symbol = Glib::Markup::escape_text(location.symbol);
symbol += '('; if(symbol_ends_with_open_parenthesis)
bool first = true; symbol += '(';
bool escaped = false; bool first = true;
for(size_t i = 0; i < location.source.size(); i++) { bool escaped = false;
if(!escaped) { for(size_t i = 0; i < location.source.size(); i++) {
if(starts_with(location.source, i, symbol)) { if(!escaped) {
location.source.insert(i + symbol.size(), "</b>"); if(starts_with(location.source, i, symbol)) {
location.source.insert(i, "<b>"); location.source.insert(i + symbol.size(), "</b>");
i += 7 + symbol.size() - 1; location.source.insert(i, "<b>");
if(first) i += 7 + symbol.size() - 1;
first = false; first = false;
}
else {
if(location.source[i] == '&') {
escaped = true;
i += 2; // Minimum character entities: &lt; and &gt;
} }
if(first) else {
location.index++; if(location.source[i] == '&') {
escaped = true;
i += 2; // Minimum character entities: &lt; and &gt;
}
if(first)
location.index++;
}
} }
else if(location.source[i] == ';')
escaped = false;
} }
else if(location.source[i] == ';')
escaped = false;
} }
} else {
else { auto pos = location.source.find(location.symbol);
auto pos = location.source.find(location.symbol); if(pos != std::string::npos)
if(pos != std::string::npos) location.index += pos;
location.index += pos; }
} }
return location; return location;

2
src/grep.cpp

@ -6,6 +6,8 @@
#include "utility.hpp" #include "utility.hpp"
Grep::Grep(const boost::filesystem::path &path, const std::string &pattern, bool case_sensitive, bool extended_regex) { Grep::Grep(const boost::filesystem::path &path, const std::string &pattern, bool case_sensitive, bool extended_regex) {
if(path.empty())
return;
auto build = Project::Build::create(path); auto build = Project::Build::create(path);
std::string exclude; std::string exclude;
if(!build->project_path.empty()) { if(!build->project_path.empty()) {

168
tests/CMakeLists.txt

@ -5,11 +5,7 @@ endif()
add_definitions(-DJUCI_BUILD_PATH="${CMAKE_BINARY_DIR}" -DJUCI_TESTS_PATH="${CMAKE_CURRENT_SOURCE_DIR}") add_definitions(-DJUCI_BUILD_PATH="${CMAKE_BINARY_DIR}" -DJUCI_TESTS_PATH="${CMAKE_CURRENT_SOURCE_DIR}")
include_directories( include_directories(${CMAKE_SOURCE_DIR}/src)
${CMAKE_SOURCE_DIR}/src
${CMAKE_SOURCE_DIR}/libclangmm/src
${CMAKE_SOURCE_DIR}/tiny-process-library
)
add_library(test_stubs OBJECT add_library(test_stubs OBJECT
stubs/config.cpp stubs/config.cpp
@ -21,69 +17,103 @@ add_library(test_stubs OBJECT
stubs/selection_dialog.cpp stubs/selection_dialog.cpp
) )
add_executable(process_test process_test.cpp $<TARGET_OBJECTS:test_stubs>) if(BUILD_TESTING)
target_link_libraries(process_test juci_shared) add_executable(process_test process_test.cpp $<TARGET_OBJECTS:test_stubs>)
add_test(process_test process_test) target_link_libraries(process_test juci_shared)
add_test(process_test process_test)
add_executable(compile_commands_test compile_commands_test.cpp $<TARGET_OBJECTS:test_stubs>)
target_link_libraries(compile_commands_test juci_shared) add_executable(compile_commands_test compile_commands_test.cpp $<TARGET_OBJECTS:test_stubs>)
add_test(compile_commands_test compile_commands_test) target_link_libraries(compile_commands_test juci_shared)
add_test(compile_commands_test compile_commands_test)
add_executable(filesystem_test filesystem_test.cpp $<TARGET_OBJECTS:test_stubs>)
target_link_libraries(filesystem_test juci_shared) add_executable(filesystem_test filesystem_test.cpp $<TARGET_OBJECTS:test_stubs>)
add_test(filesystem_test filesystem_test) target_link_libraries(filesystem_test juci_shared)
add_test(filesystem_test filesystem_test)
add_executable(cmake_build_test cmake_build_test.cpp $<TARGET_OBJECTS:test_stubs>)
target_link_libraries(cmake_build_test juci_shared) add_executable(cmake_build_test cmake_build_test.cpp $<TARGET_OBJECTS:test_stubs>)
add_test(cmake_build_test cmake_build_test) target_link_libraries(cmake_build_test juci_shared)
add_test(cmake_build_test cmake_build_test)
add_executable(meson_build_test meson_build_test.cpp $<TARGET_OBJECTS:test_stubs>)
target_link_libraries(meson_build_test juci_shared) add_executable(meson_build_test meson_build_test.cpp $<TARGET_OBJECTS:test_stubs>)
add_test(meson_build_test meson_build_test) target_link_libraries(meson_build_test juci_shared)
add_test(meson_build_test meson_build_test)
add_executable(source_test source_test.cpp $<TARGET_OBJECTS:test_stubs>)
target_link_libraries(source_test juci_shared) add_executable(source_test source_test.cpp $<TARGET_OBJECTS:test_stubs>)
add_test(source_test source_test) target_link_libraries(source_test juci_shared)
add_test(source_test source_test)
add_executable(source_clang_test source_clang_test.cpp $<TARGET_OBJECTS:test_stubs>)
target_link_libraries(source_clang_test juci_shared) add_executable(source_clang_test source_clang_test.cpp $<TARGET_OBJECTS:test_stubs>)
add_test(source_clang_test source_clang_test) target_link_libraries(source_clang_test juci_shared)
add_test(source_clang_test source_clang_test)
add_executable(source_generic_test source_generic_test.cpp $<TARGET_OBJECTS:test_stubs>)
target_link_libraries(source_generic_test juci_shared) add_executable(source_generic_test source_generic_test.cpp $<TARGET_OBJECTS:test_stubs>)
add_test(source_generic_test source_generic_test) target_link_libraries(source_generic_test juci_shared)
add_test(source_generic_test source_generic_test)
add_executable(source_key_test source_key_test.cpp $<TARGET_OBJECTS:test_stubs>)
target_link_libraries(source_key_test juci_shared) add_executable(source_key_test source_key_test.cpp $<TARGET_OBJECTS:test_stubs>)
add_test(source_key_test source_key_test) target_link_libraries(source_key_test juci_shared)
add_test(source_key_test source_key_test)
add_executable(terminal_test terminal_test.cpp $<TARGET_OBJECTS:test_stubs>)
target_link_libraries(terminal_test juci_shared) add_executable(terminal_test terminal_test.cpp $<TARGET_OBJECTS:test_stubs>)
add_test(terminal_test terminal_test) target_link_libraries(terminal_test juci_shared)
add_test(terminal_test terminal_test)
add_executable(usages_clang_test usages_clang_test.cpp $<TARGET_OBJECTS:test_stubs>)
target_link_libraries(usages_clang_test juci_shared) add_executable(usages_clang_test usages_clang_test.cpp $<TARGET_OBJECTS:test_stubs>)
add_test(usages_clang_test usages_clang_test) target_link_libraries(usages_clang_test juci_shared)
add_test(usages_clang_test usages_clang_test)
if(LIBLLDB_FOUND)
add_executable(lldb_test lldb_test.cpp $<TARGET_OBJECTS:test_stubs>) if(LIBLLDB_FOUND)
target_link_libraries(lldb_test juci_shared) add_executable(lldb_test lldb_test.cpp $<TARGET_OBJECTS:test_stubs>)
add_test(lldb_test lldb_test) target_link_libraries(lldb_test juci_shared)
add_subdirectory("lldb_test_files") add_test(lldb_test lldb_test)
add_subdirectory("lldb_test_files")
endif()
add_executable(git_test git_test.cpp $<TARGET_OBJECTS:test_stubs>)
target_link_libraries(git_test juci_shared)
add_test(git_test git_test)
add_executable(ctags_grep_test ctags_grep_test.cpp $<TARGET_OBJECTS:test_stubs>)
target_link_libraries(ctags_grep_test juci_shared)
add_test(ctags_grep_test ctags_grep_test)
add_executable(tooltips_test tooltips_test.cpp $<TARGET_OBJECTS:test_stubs>)
target_link_libraries(tooltips_test juci_shared)
add_test(tooltips_test tooltips_test)
add_executable(utility_test utility_test.cpp $<TARGET_OBJECTS:test_stubs>)
target_link_libraries(utility_test juci_shared)
add_test(utility_test utility_test)
endif() endif()
add_executable(git_test git_test.cpp $<TARGET_OBJECTS:test_stubs>) if(BUILD_FUZZING)
target_link_libraries(git_test juci_shared) add_executable(cmake_fuzzer fuzzers/cmake.cpp $<TARGET_OBJECTS:test_stubs>)
add_test(git_test git_test) target_compile_options(cmake_fuzzer PRIVATE -fsanitize=address,fuzzer)
target_link_options(cmake_fuzzer PRIVATE -fsanitize=address,fuzzer)
add_executable(ctags_grep_test ctags_grep_test.cpp $<TARGET_OBJECTS:test_stubs>) target_link_libraries(cmake_fuzzer juci_shared)
target_link_libraries(ctags_grep_test juci_shared)
add_test(ctags_grep_test ctags_grep_test) add_executable(ctags_fuzzer fuzzers/ctags.cpp $<TARGET_OBJECTS:test_stubs>)
target_compile_options(ctags_fuzzer PRIVATE -fsanitize=address,fuzzer)
add_executable(tooltips_test tooltips_test.cpp $<TARGET_OBJECTS:test_stubs>) target_link_options(ctags_fuzzer PRIVATE -fsanitize=address,fuzzer)
target_link_libraries(tooltips_test juci_shared) target_link_libraries(ctags_fuzzer juci_shared)
add_test(tooltips_test tooltips_test)
add_executable(docstring_fuzzer fuzzers/docstring.cpp $<TARGET_OBJECTS:test_stubs>)
add_executable(utility_test utility_test.cpp $<TARGET_OBJECTS:test_stubs>) target_compile_options(docstring_fuzzer PRIVATE -fsanitize=address,fuzzer)
target_link_libraries(utility_test juci_shared) target_link_options(docstring_fuzzer PRIVATE -fsanitize=address,fuzzer)
add_test(utility_test utility_test) target_link_libraries(docstring_fuzzer juci_shared)
add_executable(doxygen_fuzzer fuzzers/doxygen.cpp $<TARGET_OBJECTS:test_stubs>)
target_compile_options(doxygen_fuzzer PRIVATE -fsanitize=address,fuzzer)
target_link_options(doxygen_fuzzer PRIVATE -fsanitize=address,fuzzer)
target_link_libraries(doxygen_fuzzer juci_shared)
add_executable(grep_fuzzer fuzzers/grep.cpp $<TARGET_OBJECTS:test_stubs>)
target_compile_options(grep_fuzzer PRIVATE -fsanitize=address,fuzzer)
target_link_options(grep_fuzzer PRIVATE -fsanitize=address,fuzzer)
target_link_libraries(grep_fuzzer juci_shared)
add_executable(markdown_fuzzer fuzzers/markdown.cpp $<TARGET_OBJECTS:test_stubs>)
target_compile_options(markdown_fuzzer PRIVATE -fsanitize=address,fuzzer)
target_link_options(markdown_fuzzer PRIVATE -fsanitize=address,fuzzer)
target_link_libraries(markdown_fuzzer juci_shared)
endif()

6
tests/fuzzers/README.md

@ -0,0 +1,6 @@
Prior to running the fuzzers, build and prepare for instance as follows:
```sh
CXX=clang++ cmake -DBUILD_FUZZING=1 ..
make
export LSAN_OPTIONS=detect_leaks=0
```

12
tests/fuzzers/cmake.cpp

@ -0,0 +1,12 @@
#include "cmake.hpp"
#include <glib.h>
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
const gchar *end;
if(!g_utf8_validate(reinterpret_cast<const char *>(data), size, &end))
return 0;
std::map<std::string, std::list<std::string>> variables;
CMake::parse_file(std::string(reinterpret_cast<const char *>(data), size), variables, [](CMake::Function) {});
return 0;
}

13
tests/fuzzers/ctags.cpp

@ -0,0 +1,13 @@
#include "ctags.hpp"
#include <glib.h>
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
const gchar *end;
if(!g_utf8_validate(reinterpret_cast<const char *>(data), size, &end))
return 0;
std::map<std::string, std::list<std::string>> variables;
Ctags ctags({}, true, true);
ctags.get_location(std::string(reinterpret_cast<const char *>(data), size), true, false);
return 0;
}

28
tests/fuzzers/docstring.cpp

@ -0,0 +1,28 @@
#include "tooltips.hpp"
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
const gchar *end;
if(!g_utf8_validate(reinterpret_cast<const char *>(data), size, &end))
return 0;
class Init {
Glib::RefPtr<Gtk::Application> app;
public:
Init() {
app = Gtk::Application::create();
Gsv::init();
}
};
static Init init;
static auto get_docstring_tooltip = [](const std::string &input) {
auto tooltip = std::make_unique<Tooltip>([&](Tooltip &tooltip) {
tooltip.insert_docstring(input);
});
tooltip->show();
return tooltip;
};
get_docstring_tooltip(std::string(reinterpret_cast<const char *>(data), size));
return 0;
}

28
tests/fuzzers/doxygen.cpp

@ -0,0 +1,28 @@
#include "tooltips.hpp"
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
const gchar *end;
if(!g_utf8_validate(reinterpret_cast<const char *>(data), size, &end))
return 0;
class Init {
Glib::RefPtr<Gtk::Application> app;
public:
Init() {
app = Gtk::Application::create();
Gsv::init();
}
};
static Init init;
static auto get_doxygen_tooltip = [](const std::string &input) {
auto tooltip = std::make_unique<Tooltip>([&](Tooltip &tooltip) {
tooltip.insert_doxygen(input, true);
});
tooltip->show();
return tooltip;
};
get_doxygen_tooltip(std::string(reinterpret_cast<const char *>(data), size));
return 0;
}

12
tests/fuzzers/grep.cpp

@ -0,0 +1,12 @@
#include "grep.hpp"
#include <glib.h>
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
const gchar *end;
if(!g_utf8_validate(reinterpret_cast<const char *>(data), size, &end))
return 0;
Grep grep({}, {}, false, false);
grep.get_location(std::string(reinterpret_cast<const char *>(data), size), true, true);
return 0;
}

28
tests/fuzzers/markdown.cpp

@ -0,0 +1,28 @@
#include "tooltips.hpp"
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
const gchar *end;
if(!g_utf8_validate(reinterpret_cast<const char *>(data), size, &end))
return 0;
class Init {
Glib::RefPtr<Gtk::Application> app;
public:
Init() {
app = Gtk::Application::create();
Gsv::init();
}
};
static Init init;
static auto get_markdown_tooltip = [](const std::string &input) {
auto tooltip = std::make_unique<Tooltip>([&](Tooltip &tooltip) {
tooltip.insert_markdown(input);
});
tooltip->show();
return tooltip;
};
get_markdown_tooltip(std::string(reinterpret_cast<const char *>(data), size));
return 0;
}
Loading…
Cancel
Save