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. 3
      CMakeLists.txt
  2. 1
      src/cmake.cpp
  3. 39
      src/ctags.cpp
  4. 2
      src/grep.cpp
  5. 40
      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

3
CMakeLists.txt

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

1
src/cmake.cpp

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

39
src/ctags.cpp

@ -8,6 +8,8 @@
#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) {
if(path.empty())
return;
// 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());
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_;
#endif
auto symbol_end = line.find('\t');
if(symbol_end == std::string::npos) {
std::cerr << "Warning (ctags): could not parse line: " << line << std::endl;
if(symbol_end == std::string::npos)
return location;
}
location.symbol = line.substr(0, symbol_end);
if(9 < location.symbol.size() && location.symbol[8] == ' ' && starts_with(location.symbol, "operator")) {
auto &chr = location.symbol[9];
@ -69,34 +69,27 @@ Ctags::Location Ctags::get_location(const std::string &line_, bool add_markup, b
}
auto file_start = symbol_end + 1;
if(file_start >= line.size()) {
std::cerr << "Warning (ctags): could not parse line: " << line << std::endl;
if(file_start >= line.size())
return location;
}
auto file_end = line.find('\t', file_start);
if(file_end == std::string::npos) {
std::cerr << "Warning (ctags): could not parse line: " << line << std::endl;
if(file_end == std::string::npos)
return location;
}
location.file_path = line.substr(file_start, file_end - file_start);
auto source_start = file_end + 3;
if(source_start >= line.size()) {
std::cerr << "Warning (ctags): could not parse line: " << line << std::endl;
if(source_start >= line.size())
return location;
}
location.index = 0;
while(source_start < line.size() && (line[source_start] == ' ' || line[source_start] == '\t')) {
++source_start;
++location.index;
}
auto source_end = line.find("/;\"\t", source_start);
if(source_end == std::string::npos) {
std::cerr << "Warning (ctags): could not parse line: " << line << std::endl;
if(source_end == std::string::npos)
return location;
}
// Unescape source
if(source_end > source_start) {
auto end = source_end - (line[source_end - 1] == '$' ? 1 : 0);
location.source.reserve(end - source_start);
bool escaped = false;
@ -108,29 +101,24 @@ Ctags::Location Ctags::get_location(const std::string &line_, bool add_markup, b
escaped = false;
location.source += line[i];
}
}
size_t line_start;
if(enable_kind) {
auto kind_start = source_end + 4;
if(kind_start >= line.size()) {
std::cerr << "Warning (ctags): could not parse line: " << line << std::endl;
if(kind_start >= line.size())
return location;
}
auto kind_end = line.find('\t', kind_start);
if(kind_end == std::string::npos) {
std::cerr << "Warning (ctags): could not parse line: " << line << std::endl;
if(kind_end == std::string::npos)
return location;
}
location.kind = line.substr(kind_start, kind_end - kind_start);
line_start = kind_start + location.kind.size() + 6;
}
else
line_start = source_end + 9;
if(line_start >= line.size()) {
std::cerr << "Warning (ctags): could not parse line: " << line << std::endl;
if(line_start >= line.size())
return location;
}
auto line_end = line.find('\t', line_start);
size_t line_size = line_end == std::string::npos ? std::string::npos : line_end - line_start;
try {
@ -146,6 +134,7 @@ Ctags::Location Ctags::get_location(const std::string &line_, bool add_markup, b
location.scope = line.substr(scope_start + 1);
}
if(!location.symbol.empty()) {
if(add_markup) {
location.source = Glib::Markup::escape_text(location.source);
std::string symbol = Glib::Markup::escape_text(location.symbol);
@ -159,7 +148,6 @@ Ctags::Location Ctags::get_location(const std::string &line_, bool add_markup, b
location.source.insert(i + symbol.size(), "</b>");
location.source.insert(i, "<b>");
i += 7 + symbol.size() - 1;
if(first)
first = false;
}
else {
@ -180,6 +168,7 @@ Ctags::Location Ctags::get_location(const std::string &line_, bool add_markup, b
if(pos != std::string::npos)
location.index += pos;
}
}
return location;
}

2
src/grep.cpp

@ -6,6 +6,8 @@
#include "utility.hpp"
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);
std::string exclude;
if(!build->project_path.empty()) {

40
tests/CMakeLists.txt

@ -5,11 +5,7 @@ endif()
add_definitions(-DJUCI_BUILD_PATH="${CMAKE_BINARY_DIR}" -DJUCI_TESTS_PATH="${CMAKE_CURRENT_SOURCE_DIR}")
include_directories(
${CMAKE_SOURCE_DIR}/src
${CMAKE_SOURCE_DIR}/libclangmm/src
${CMAKE_SOURCE_DIR}/tiny-process-library
)
include_directories(${CMAKE_SOURCE_DIR}/src)
add_library(test_stubs OBJECT
stubs/config.cpp
@ -21,6 +17,7 @@ add_library(test_stubs OBJECT
stubs/selection_dialog.cpp
)
if(BUILD_TESTING)
add_executable(process_test process_test.cpp $<TARGET_OBJECTS:test_stubs>)
target_link_libraries(process_test juci_shared)
add_test(process_test process_test)
@ -87,3 +84,36 @@ 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()
if(BUILD_FUZZING)
add_executable(cmake_fuzzer fuzzers/cmake.cpp $<TARGET_OBJECTS:test_stubs>)
target_compile_options(cmake_fuzzer PRIVATE -fsanitize=address,fuzzer)
target_link_options(cmake_fuzzer PRIVATE -fsanitize=address,fuzzer)
target_link_libraries(cmake_fuzzer juci_shared)
add_executable(ctags_fuzzer fuzzers/ctags.cpp $<TARGET_OBJECTS:test_stubs>)
target_compile_options(ctags_fuzzer PRIVATE -fsanitize=address,fuzzer)
target_link_options(ctags_fuzzer PRIVATE -fsanitize=address,fuzzer)
target_link_libraries(ctags_fuzzer juci_shared)
add_executable(docstring_fuzzer fuzzers/docstring.cpp $<TARGET_OBJECTS:test_stubs>)
target_compile_options(docstring_fuzzer PRIVATE -fsanitize=address,fuzzer)
target_link_options(docstring_fuzzer PRIVATE -fsanitize=address,fuzzer)
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