From 1db2a12767d727b361f3edaaa6a62fc64a9a5761 Mon Sep 17 00:00:00 2001 From: Vaxry <43317083+vaxerski@users.noreply.github.com> Date: Sun, 31 Dec 2023 16:51:50 +0100 Subject: [PATCH] CI: add fuzzing and sanitizing (#5) --- .github/workflows/arch.yml | 115 +++++++++++++++++++++++++++++++------ CMakeLists.txt | 11 +++- src/common.cpp | 5 +- src/config.cpp | 3 +- tests/fuzz/main.cpp | 42 ++++++++++++++ tests/{ => parse}/main.cpp | 0 6 files changed, 153 insertions(+), 23 deletions(-) create mode 100644 tests/fuzz/main.cpp rename tests/{ => parse}/main.cpp (100%) diff --git a/.github/workflows/arch.yml b/.github/workflows/arch.yml index 8e0e230..58ddc04 100644 --- a/.github/workflows/arch.yml +++ b/.github/workflows/arch.yml @@ -19,22 +19,102 @@ jobs: pacman --noconfirm --noprogressbar -Syyu pacman --noconfirm --noprogressbar -Sy gcc base-devel cmake clang - - name: Build with gcc + - name: Build hyprlang with gcc run: | - CC="/usr/bin/gcc" CXX="/usr/bin/g++" cmake --no-warn-unused-cli -DCMAKE_BUILD_TYPE:STRING=Release -S . -B ./build - CC="/usr/bin/gcc" CXX="/usr/bin/g++" cmake --build ./build --config Release --target all -j`nproc 2>/dev/null || getconf NPROCESSORS_CONF` - cp ./build/libhyprlang.so /usr/lib + CC="/usr/bin/gcc" CXX="/usr/bin/g++" cmake --no-warn-unused-cli -DCMAKE_BUILD_TYPE:STRING=Release -DCMAKE_INSTALL_PREFIX:PATH=/usr -S . -B ./build + CC="/usr/bin/gcc" CXX="/usr/bin/g++" cmake --build ./build --config Release --target hyprlang -j`nproc 2>/dev/null || getconf NPROCESSORS_CONF` + cmake --install ./build - - name: Build with clang + - name: Build tests with clang run: | rm -rf ./build - CC="/usr/bin/clang" CXX="/usr/bin/clang++" cmake --no-warn-unused-cli -DCMAKE_BUILD_TYPE:STRING=Release -S . -B ./build - CC="/usr/bin/clang" CXX="/usr/bin/clang++" cmake --build ./build --config Release --target all -j`nproc 2>/dev/null || getconf NPROCESSORS_CONF` - rm ./build/libhyprlang.so + CC="/usr/bin/clang" CXX="/usr/bin/clang++" cmake --no-warn-unused-cli -DCMAKE_BUILD_TYPE:STRING=Release -DCMAKE_INSTALL_PREFIX:PATH=/usr -S . -B ./build + CC="/usr/bin/clang" CXX="/usr/bin/clang++" cmake --build ./build --config Release --target tests -j`nproc 2>/dev/null || getconf NPROCESSORS_CONF` - name: Run tests run: | - cd ./tests && ../build/hyprlang_test + cd ./build && ctest --output-on-failure + + asan: + name: "gcc build / ASan tests" + runs-on: ubuntu-latest + container: + image: archlinux + steps: + - name: Checkout repository actions + uses: actions/checkout@v4 + with: + sparse-checkout: .github/actions + + - name: Get required pkgs + run: | + sed -i 's/SigLevel = Required DatabaseOptional/SigLevel = Optional TrustAll/' /etc/pacman.conf + pacman --noconfirm --noprogressbar -Syyu + pacman --noconfirm --noprogressbar -Sy gcc base-devel cmake clang + + - name: Build with gcc + run: | + CXXFLAGS="-fsanitize=address" CC="/usr/bin/gcc" CXX="/usr/bin/g++" cmake --no-warn-unused-cli -DCMAKE_BUILD_TYPE:STRING=Release -DCMAKE_INSTALL_PREFIX:PATH=/usr -S . -B ./build + CXXFLAGS="-fsanitize=address" CC="/usr/bin/gcc" CXX="/usr/bin/g++" cmake --build ./build --config Release --target all -j`nproc 2>/dev/null || getconf NPROCESSORS_CONF` + cmake --install ./build + + - name: Run tests + run: | + cd ./build && ctest --output-on-failure + + ubsan: + name: "gcc build / UBSan tests" + runs-on: ubuntu-latest + container: + image: archlinux + steps: + - name: Checkout repository actions + uses: actions/checkout@v4 + with: + sparse-checkout: .github/actions + + - name: Get required pkgs + run: | + sed -i 's/SigLevel = Required DatabaseOptional/SigLevel = Optional TrustAll/' /etc/pacman.conf + pacman --noconfirm --noprogressbar -Syyu + pacman --noconfirm --noprogressbar -Sy gcc base-devel cmake clang + + - name: Build with gcc + run: | + CXXFLAGS="-fsanitize=undefined" CC="/usr/bin/gcc" CXX="/usr/bin/g++" cmake --no-warn-unused-cli -DCMAKE_BUILD_TYPE:STRING=Release -DCMAKE_INSTALL_PREFIX:PATH=/usr -S . -B ./build + CXXFLAGS="-fsanitize=undefined" CC="/usr/bin/gcc" CXX="/usr/bin/g++" cmake --build ./build --config Release --target all -j`nproc 2>/dev/null || getconf NPROCESSORS_CONF` + cmake --install ./build + + - name: Run tests + run: | + cd ./build && ctest --output-on-failure + + msan: + name: "gcc build / MSan tests" + runs-on: ubuntu-latest + container: + image: archlinux + steps: + - name: Checkout repository actions + uses: actions/checkout@v4 + with: + sparse-checkout: .github/actions + + - name: Get required pkgs + run: | + sed -i 's/SigLevel = Required DatabaseOptional/SigLevel = Optional TrustAll/' /etc/pacman.conf + pacman --noconfirm --noprogressbar -Syyu + pacman --noconfirm --noprogressbar -Sy gcc base-devel cmake clang + + - name: Build with gcc + run: | + CXXFLAGS="-fsanitize=leak" CC="/usr/bin/gcc" CXX="/usr/bin/g++" cmake --no-warn-unused-cli -DCMAKE_BUILD_TYPE:STRING=Release -DCMAKE_INSTALL_PREFIX:PATH=/usr -S . -B ./build + CXXFLAGS="-fsanitize=leak" CC="/usr/bin/gcc" CXX="/usr/bin/g++" cmake --build ./build --config Release --target all -j`nproc 2>/dev/null || getconf NPROCESSORS_CONF` + cmake --install ./build + + - name: Run tests + run: | + cd ./build && ctest --output-on-failure clang: name: "clang build / gcc test" @@ -53,22 +133,21 @@ jobs: pacman --noconfirm --noprogressbar -Syyu pacman --noconfirm --noprogressbar -Sy gcc base-devel cmake clang - - name: Build with clang + - name: Build hyprlang with clang run: | - CC="/usr/bin/clang" CXX="/usr/bin/clang++" cmake --no-warn-unused-cli -DCMAKE_BUILD_TYPE:STRING=Release -S . -B ./build - CC="/usr/bin/clang" CXX="/usr/bin/clang++" cmake --build ./build --config Release --target all -j`nproc 2>/dev/null || getconf NPROCESSORS_CONF` - cp ./build/libhyprlang.so /usr/lib + CC="/usr/bin/clang" CXX="/usr/bin/clang++" cmake --no-warn-unused-cli -DCMAKE_BUILD_TYPE:STRING=Release -DCMAKE_INSTALL_PREFIX:PATH=/usr -S . -B ./build + CC="/usr/bin/clang" CXX="/usr/bin/clang++" cmake --build ./build --config Release --target hyprlang -j`nproc 2>/dev/null || getconf NPROCESSORS_CONF` + cmake --install ./build - - name: Build with gcc + - name: Build tests with gcc run: | rm -rf ./build - CC="/usr/bin/gcc" CXX="/usr/bin/g++" cmake --no-warn-unused-cli -DCMAKE_BUILD_TYPE:STRING=Release -S . -B ./build - CC="/usr/bin/gcc" CXX="/usr/bin/g++" cmake --build ./build --config Release --target all -j`nproc 2>/dev/null || getconf NPROCESSORS_CONF` - rm ./build/libhyprlang.so + CC="/usr/bin/gcc" CXX="/usr/bin/g++" cmake --no-warn-unused-cli -DCMAKE_BUILD_TYPE:STRING=Release -DCMAKE_INSTALL_PREFIX:PATH=/usr -S . -B ./build + CC="/usr/bin/gcc" CXX="/usr/bin/g++" cmake --build ./build --config Release --target tests -j`nproc 2>/dev/null || getconf NPROCESSORS_CONF` - name: Run tests run: | - cd ./tests && ../build/hyprlang_test + cd ./build && ctest --output-on-failure doxygen: name: "Deploy docs" diff --git a/CMakeLists.txt b/CMakeLists.txt index 1cb4bac..8a9149c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -25,9 +25,18 @@ set_target_properties(hyprlang PROPERTIES PUBLIC_HEADER include/hyprlang.hpp) install(TARGETS hyprlang) -add_executable(hyprlang_test "tests/main.cpp") +# tests +add_custom_target(tests) + +add_executable(hyprlang_test "tests/parse/main.cpp") target_link_libraries(hyprlang_test PRIVATE hyprlang) add_test(NAME "Parsing" WORKING_DIRECTORY "../tests/" COMMAND hyprlang_test "parse") +add_dependencies(tests hyprlang_test) + +add_executable(hyprlang_fuzz "tests/fuzz/main.cpp") +target_link_libraries(hyprlang_fuzz PRIVATE hyprlang) +add_test(NAME "Fuzz" WORKING_DIRECTORY "../tests/" COMMAND hyprlang_fuzz "fuzz") +add_dependencies(tests hyprlang_fuzz) # Installation install(TARGETS hyprlang diff --git a/src/common.cpp b/src/common.cpp index 9846ea5..eb4050a 100644 --- a/src/common.cpp +++ b/src/common.cpp @@ -92,7 +92,7 @@ void CConfigValue::defaultFrom(SConfigDefaultValue& ref) { break; } case CONFIGDATATYPE_STR: { - if (!m_pData) + if (m_pData) free(m_pData); std::string str = std::any_cast(ref.data); m_pData = calloc(1, str.length() + 1); @@ -131,7 +131,8 @@ void CConfigValue::setFrom(std::any value) { break; } case CONFIGDATATYPE_STR: { - free(m_pData); + if (m_pData) + free(m_pData); std::string str = std::any_cast(value); m_pData = calloc(1, str.length() + 1); strncpy((char*)m_pData, str.c_str(), str.length()); diff --git a/src/config.cpp b/src/config.cpp index bada693..be1f85c 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -351,11 +351,10 @@ CParseResult CConfig::parseVariable(const std::string& lhs, const std::string& r else { impl->variables.push_back({lhs.substr(1), rhs}); std::sort(impl->variables.begin(), impl->variables.end(), [](const auto& lhs, const auto& rhs) { return lhs.name.length() > rhs.name.length(); }); + IT = std::find_if(impl->variables.begin(), impl->variables.end(), [&](const auto& v) { return v.name == lhs.substr(1); }); } if (dynamic) { - if (IT == impl->variables.end()) - IT = std::find_if(impl->variables.begin(), impl->variables.end(), [&](const auto& v) { return v.name == lhs.substr(1); }); for (auto& l : IT->linesContainingVar) { parseLine(l, true); } diff --git a/tests/fuzz/main.cpp b/tests/fuzz/main.cpp new file mode 100644 index 0000000..deb13c4 --- /dev/null +++ b/tests/fuzz/main.cpp @@ -0,0 +1,42 @@ +#include + +#include + +#define FUZZ_ITERS 1337 + +std::string garbage() { + srand(time(nullptr)); + + int len = rand() % 10000; + + std::string chars; + for (int i = 0; i < len; ++i) { + chars += rand() % 254 + 1; + } + + return chars; +} + +int main(int argc, char** argv, char** envp) { + + Hyprlang::CConfig config("./eeeeeeeUnused", {.allowMissingConfig = true}); + config.addConfigValue("test", {0L}); + + config.parseDynamic(""); + config.parseDynamic("", ""); + config.parseDynamic("}"); + for (size_t i = 0; i < FUZZ_ITERS; ++i) { + config.parseDynamic(garbage().c_str(), garbage().c_str()); + config.parseDynamic((garbage() + "=" + garbage()).c_str()); + config.parseDynamic(garbage().c_str()); + config.parseDynamic((garbage() + " {").c_str()); + config.parseDynamic((std::string{"test = "} + garbage()).c_str()); + config.parseDynamic((std::string{"$"} + garbage()).c_str()); + config.parseDynamic((std::string{"$VAR = "} + garbage()).c_str()); + } + config.parseDynamic("}"); + + std::cout << "Success, no fuzzing errors\n"; + + return 0; +} \ No newline at end of file diff --git a/tests/main.cpp b/tests/parse/main.cpp similarity index 100% rename from tests/main.cpp rename to tests/parse/main.cpp