From 756f74c6de53f4bade074299c5c0db7cb8e3085d Mon Sep 17 00:00:00 2001 From: Andrew Noyes Date: Fri, 19 Jan 2024 15:48:07 -0800 Subject: [PATCH] Add fuzz test --- .clangd | 2 +- CMakeLists.txt | 24 ++++++++++++++++++++---- ConflictSet.cpp | 39 +++++++++++++++++++++++++++++++++++++-- 3 files changed, 58 insertions(+), 7 deletions(-) diff --git a/.clangd b/.clangd index 5702466..a1cb04d 100644 --- a/.clangd +++ b/.clangd @@ -1,2 +1,2 @@ CompileFlags: - Add: [-DENABLE_TESTS, -UNDEBUG] + Add: [-DENABLE_TESTS, -UNDEBUG, -DENABLE_FUZZ] diff --git a/CMakeLists.txt b/CMakeLists.txt index 12e6cd6..28ea00e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -14,19 +14,21 @@ target_compile_options(conflict_set PRIVATE -fno-exceptions -fvisibility=hidden) target_link_options(conflict_set PRIVATE -nodefaultlibs -lc -fvisibility=hidden) add_custom_command(TARGET conflict_set POST_BUILD COMMAND ${CMAKE_STRIP} -x $) +include(CTest) + +# unit test + add_executable(conflict_set_test ConflictSet.cpp ConflictSet.h) target_compile_definitions(conflict_set_test PRIVATE ENABLE_TESTS) # keep asserts for test target_compile_options(conflict_set_test PRIVATE -UNDEBUG) # Only emit compile warnings for test target_compile_options(conflict_set_test PRIVATE -Wall -Wextra -Wpedantic -Wunreachable-code) - -include(CTest) add_test(NAME conflict_set_test COMMAND conflict_set_test) # api smoke tests -# c89 +# c99 add_executable(conflict_set_c_api_test conflict_set_c_api_test.c ConflictSet.h) target_link_libraries(conflict_set_c_api_test PRIVATE conflict_set) target_compile_options(conflict_set_c_api_test PRIVATE -UNDEBUG) @@ -40,4 +42,18 @@ target_link_libraries(conflict_set_cxx_api_test PRIVATE conflict_set) target_compile_options(conflict_set_cxx_api_test PRIVATE -UNDEBUG) set_property(TARGET conflict_set_cxx_api_test PROPERTY CXX_STANDARD 98) add_test(NAME conflict_set_cxx_api_test COMMAND conflict_set_cxx_api_test) -target_compile_options(conflict_set_cxx_api_test PRIVATE -Wall -Wextra -Wpedantic -Wunreachable-code -Werror) \ No newline at end of file +target_compile_options(conflict_set_cxx_api_test PRIVATE -Wall -Wextra -Wpedantic -Wunreachable-code -Werror) + +# fuzz test +include(CheckCXXCompilerFlag) +check_cxx_compiler_flag(HAS_LIB_FUZZER -fsanitize=fuzzer) + +if (HAS_LIB_FUZZER) + add_executable(conflict_set_fuzz_test ConflictSet.cpp ConflictSet.h) + target_compile_definitions(conflict_set_fuzz_test PRIVATE ENABLE_FUZZ) + # keep asserts for test + target_compile_options(conflict_set_fuzz_test PRIVATE -UNDEBUG) + target_compile_options(conflict_set_fuzz_test PRIVATE -Wall -Wextra -Wpedantic -Wunreachable-code) + target_compile_options(conflict_set_fuzz_test PRIVATE -fsanitize=fuzzer) + target_link_options(conflict_set_fuzz_test PRIVATE -fsanitize=fuzzer) +endif() diff --git a/ConflictSet.cpp b/ConflictSet.cpp index f902695..e30f337 100644 --- a/ConflictSet.cpp +++ b/ConflictSet.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -825,7 +826,7 @@ void rotate(Node **node, bool dir) { updateMaxVersion(l); } -void checkParentPointers(Node *node, bool &success) { +[[maybe_unused]] void checkParentPointers(Node *node, bool &success) { for (int i = 0; i < 2; ++i) { if (node->child[i] != nullptr) { if (node->child[i]->parent != node) { @@ -839,7 +840,7 @@ void checkParentPointers(Node *node, bool &success) { } } -int64_t checkMaxVersion(Node *node, bool &success) { +[[maybe_unused]] int64_t checkMaxVersion(Node *node, bool &success) { int64_t expected = std::max(node->pointVersion, node->rangeVersion); for (int i = 0; i < 2; ++i) { if (node->child[i] != nullptr) { @@ -1213,4 +1214,38 @@ int main(void) { bool success = checkCorrectness(cs.root, refImpl); return success ? 0 : 1; } +#endif + +#ifdef ENABLE_FUZZ +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + initFuzz(data, size); + + int64_t writeVersion = 0; + ConflictSet::Impl cs{writeVersion}; + ReferenceImpl refImpl{writeVersion}; + + while (gArbitrary.hasEntropy()) { + Arena arena; + int numWrites = gArbitrary.bounded(10); + int64_t v = ++writeVersion; + auto *writes = new (arena) ConflictSet::WriteRange[numWrites]; + std::set keys; + while (int(keys.size()) < numWrites) { + keys.insert(gRandom.bounded(100)); + } + auto iter = keys.begin(); + for (int i = 0; i < numWrites; ++i) { + writes[i].begin = toKey(arena, *iter++); + writes[i].end.len = 0; + writes[i].writeVersion = v; + } + cs.addWrites(writes, numWrites); + refImpl.addWrites(writes, numWrites); + bool success = checkCorrectness(cs.root, refImpl); + if (!success) { + abort(); + } + } + return 0; +} #endif \ No newline at end of file