diff options
Diffstat (limited to 'archived/projt-launcher/fuzz')
| -rw-r--r-- | archived/projt-launcher/fuzz/CMakeLists.txt | 39 | ||||
| -rw-r--r-- | archived/projt-launcher/fuzz/fuzz_gzip.cpp | 17 | ||||
| -rw-r--r-- | archived/projt-launcher/fuzz/fuzz_nbt_reader.cpp | 36 | ||||
| -rw-r--r-- | archived/projt-launcher/fuzz/fuzz_qjson_parse.cpp | 17 | ||||
| -rw-r--r-- | archived/projt-launcher/fuzz/fuzz_separator_prefix_tree.cpp | 123 |
5 files changed, 232 insertions, 0 deletions
diff --git a/archived/projt-launcher/fuzz/CMakeLists.txt b/archived/projt-launcher/fuzz/CMakeLists.txt new file mode 100644 index 0000000000..8828726a7b --- /dev/null +++ b/archived/projt-launcher/fuzz/CMakeLists.txt @@ -0,0 +1,39 @@ +set(FUZZ_ENGINE "") +if(DEFINED ENV{LIB_FUZZING_ENGINE}) + set(FUZZ_ENGINE $ENV{LIB_FUZZING_ENGINE}) +endif() + +function(add_fuzzer target) + add_executable(${target} ${ARGN}) + target_compile_features(${target} PRIVATE cxx_std_20) + if(FUZZ_ENGINE) + target_link_libraries(${target} PRIVATE ${FUZZ_ENGINE}) + else() + target_compile_options(${target} PRIVATE -fsanitize=fuzzer) + target_link_options(${target} PRIVATE -fsanitize=fuzzer) + endif() +endfunction() + +add_fuzzer(fuzz_nbt_reader fuzz_nbt_reader.cpp) +target_link_libraries(fuzz_nbt_reader PRIVATE nbt++) + +# Disabled: Qt6 adds unnecessary glib dependency in fuzzing environment +# add_fuzzer(fuzz_qjson_parse fuzz_qjson_parse.cpp) +# target_link_libraries(fuzz_qjson_parse PRIVATE Qt6::Core) + +# fuzz_gzip requires Qt6::Core for QByteArray/QFile usage in GZip.cpp +# Only build if Qt6::Core is available +if(TARGET Qt6::Core) + add_fuzzer(fuzz_gzip fuzz_gzip.cpp ${PROJECT_SOURCE_DIR}/launcher/GZip.cpp) + target_include_directories(fuzz_gzip PRIVATE ${PROJECT_SOURCE_DIR}/launcher) + # Use static ptlibzippy for fuzzer to avoid sanitizer symbol mismatch + if(TARGET ptlibzippystatic) + target_link_libraries(fuzz_gzip PRIVATE Qt6::Core ptlibzippystatic) + else() + target_link_libraries(fuzz_gzip PRIVATE Qt6::Core PTlibzippy::PTlibzippy) + endif() + + add_fuzzer(fuzz_separator_prefix_tree fuzz_separator_prefix_tree.cpp) + target_include_directories(fuzz_separator_prefix_tree PRIVATE ${PROJECT_SOURCE_DIR}/launcher) + target_link_libraries(fuzz_separator_prefix_tree PRIVATE Qt6::Core) +endif() diff --git a/archived/projt-launcher/fuzz/fuzz_gzip.cpp b/archived/projt-launcher/fuzz/fuzz_gzip.cpp new file mode 100644 index 0000000000..8db3c716f4 --- /dev/null +++ b/archived/projt-launcher/fuzz/fuzz_gzip.cpp @@ -0,0 +1,17 @@ +#include <cstdint> + +#include <QByteArray> + +#include "GZip.h" + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) +{ + QByteArray input(reinterpret_cast<const char*>(data), static_cast<int>(size)); + QByteArray output; + QByteArray roundtrip; + + GZip::unzip(input, output); + GZip::zip(output, roundtrip); + + return 0; +} diff --git a/archived/projt-launcher/fuzz/fuzz_nbt_reader.cpp b/archived/projt-launcher/fuzz/fuzz_nbt_reader.cpp new file mode 100644 index 0000000000..d6b1291c6f --- /dev/null +++ b/archived/projt-launcher/fuzz/fuzz_nbt_reader.cpp @@ -0,0 +1,36 @@ +#include <cstdint> +#include <exception> +#include <sstream> +#include <string> + +#include "io/stream_reader.h" + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) +{ + // Avoid excessive allocations on pathological inputs + // Very strict limit to prevent array allocation bombs + // E.g., NBT array with count 0x92929292 would allocate 3.2GB + constexpr size_t kMaxInputSize = 4 * 1024; // 4 KiB (was 64 KiB) + if (!data || size == 0 || size > kMaxInputSize) + { + return 0; + } + + try + { + // Use custom string stream without extra copies + std::istringstream stream(std::string(reinterpret_cast<const char*>(data), size), std::ios::binary); + nbt::io::read_compound(stream); + } + catch (const std::exception&) + { + // Expected for malformed inputs or resource exhaustion + } + catch (const std::bad_alloc&) + { + // Handle out-of-memory gracefully + return -1; + } + + return 0; +} diff --git a/archived/projt-launcher/fuzz/fuzz_qjson_parse.cpp b/archived/projt-launcher/fuzz/fuzz_qjson_parse.cpp new file mode 100644 index 0000000000..ee4db92ed6 --- /dev/null +++ b/archived/projt-launcher/fuzz/fuzz_qjson_parse.cpp @@ -0,0 +1,17 @@ +#include <cstdint> + +#include <QJsonDocument> +#include <QJsonParseError> + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) +{ + if (!data || size == 0) + { + return 0; + } + + QJsonParseError error{}; + QJsonDocument::fromJson(QByteArray(reinterpret_cast<const char*>(data), static_cast<int>(size)), &error); + + return 0; +} diff --git a/archived/projt-launcher/fuzz/fuzz_separator_prefix_tree.cpp b/archived/projt-launcher/fuzz/fuzz_separator_prefix_tree.cpp new file mode 100644 index 0000000000..d50f616050 --- /dev/null +++ b/archived/projt-launcher/fuzz/fuzz_separator_prefix_tree.cpp @@ -0,0 +1,123 @@ +#include <cstddef> +#include <cstdint> +#include <cstdlib> + +#include <QByteArray> +#include <QString> +#include <QStringList> + +#include "SeparatorPrefixTree.h" + +namespace { +class InputCursor +{ + public: + InputCursor(const uint8_t* data, size_t size) : m_data(data), m_size(size) {} + + bool has(size_t count) const + { + return m_pos + count <= m_size; + } + + uint8_t nextByte() + { + if (!has(1)) + return 0; + return m_data[m_pos++]; + } + + QByteArray takeBytes(size_t count) + { + if (!has(count)) + count = m_size - m_pos; + QByteArray out(reinterpret_cast<const char*>(m_data + m_pos), static_cast<int>(count)); + m_pos += count; + return out; + } + + QString nextPath() + { + static const char kAlphabet[] = "abcdefghijklmnopqrstuvwxyz0123456789-_."; + const int alphabet_len = static_cast<int>(sizeof(kAlphabet) - 1); + + const int segments = 1 + (nextByte() % 4); + QStringList parts; + parts.reserve(segments); + + for (int i = 0; i < segments; ++i) { + const int length = 1 + (nextByte() % 8); + QByteArray bytes; + bytes.reserve(length); + for (int j = 0; j < length; ++j) { + bytes.append(kAlphabet[nextByte() % alphabet_len]); + } + parts.append(QString::fromLatin1(bytes)); + } + + return parts.join('/'); + } + + private: + const uint8_t* m_data = nullptr; + size_t m_size = 0; + size_t m_pos = 0; +}; +} // namespace + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) +{ + InputCursor cursor(data, size); + SeparatorPrefixTree<'/'> tree; + + const size_t max_ops = size < 256 ? size : 256; + for (size_t i = 0; i < max_ops && cursor.has(1); ++i) { + const uint8_t op = cursor.nextByte() % 8; + const QString path = cursor.nextPath(); + + switch (op) { + case 0: + tree.insert(path); + break; + case 1: + tree.remove(path); + break; + case 2: { + const bool contains = tree.contains(path); + if (contains && !tree.covers(path)) { + abort(); + } + break; + } + case 3: + tree.covers(path); + break; + case 4: { + const bool covers = tree.covers(path); + const QString cover = tree.cover(path); + if (covers && cover.isNull()) { + abort(); + } + break; + } + case 5: + tree.exists(path); + break; + case 6: { + const auto list = tree.toStringList(); + for (const auto& entry : list) { + if (!tree.contains(entry)) { + abort(); + } + } + break; + } + case 7: + tree.clear(); + break; + default: + break; + } + } + + return 0; +} |
