diff --git a/CMakeLists.txt b/CMakeLists.txt index f4ce73cc..55f1a6df 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -12,6 +12,12 @@ include(${CMAKE_CURRENT_SOURCE_DIR}/cmake/project-defaults.cmake) add_subdirectory(yoga) add_subdirectory(tests) +option(BUILD_FUZZ_TESTS "Build fuzz tests" OFF) + +if ('${CMAKE_CXX_COMPILER_ID}' MATCHES 'Clang' AND BUILD_FUZZ_TESTS) + add_subdirectory(fuzz) +endif() + # cmake install config include(GNUInstallDirs) include(CMakePackageConfigHelpers) diff --git a/fuzz/CMakeLists.txt b/fuzz/CMakeLists.txt new file mode 100644 index 00000000..8b06cb3f --- /dev/null +++ b/fuzz/CMakeLists.txt @@ -0,0 +1,16 @@ +# If google/oss-fuzz has set the fuzzing engine +if(DEFINED ENV{LIB_FUZZING_ENGINE}) + set(FUZZING_ENGINE $ENV{LIB_FUZZING_ENGINE}) + set(FUZZING_COMPILE_FLAGS "") + set(FUZZING_LINK_FLAGS "${FUZZING_ENGINE}") +else() + set(FUZZING_COMPILE_FLAGS "-fsanitize=fuzzer") + set(FUZZING_LINK_FLAGS "-fsanitize=fuzzer") +endif() + +add_executable(fuzz_layout fuzz_layout.cpp) +set_target_properties(fuzz_layout PROPERTIES + COMPILE_FLAGS "${FUZZING_COMPILE_FLAGS}" + LINK_FLAGS "${FUZZING_LINK_FLAGS}" +) +target_link_libraries(fuzz_layout yogacore) diff --git a/fuzz/fuzz_layout.cpp b/fuzz/fuzz_layout.cpp new file mode 100644 index 00000000..0ca3d2a9 --- /dev/null +++ b/fuzz/fuzz_layout.cpp @@ -0,0 +1,50 @@ +#include +#include +#include + +YGFlexDirection fuzzed_flex_direction(FuzzedDataProvider& fdp) { + return fdp.PickValueInArray({ + YGFlexDirectionColumn, + YGFlexDirectionColumnReverse, + YGFlexDirectionRow, + YGFlexDirectionRowReverse, + }); +} + +void FillFuzzedTree( + FuzzedDataProvider& fdp, + const YGConfigRef& config, + const YGNodeRef& root, + size_t depth = 0) { + constexpr size_t kMaxDepth = 20; + constexpr size_t kMaxChildren = 20; + + if (depth > kMaxDepth) { + return; + } + + size_t children = fdp.ConsumeIntegralInRange(0, kMaxChildren); + for (size_t i = 0; i < children; i++) { + const YGNodeRef child = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexDirection(root, fuzzed_flex_direction(fdp)); + YGNodeStyleSetWidth(child, fdp.ConsumeFloatingPoint()); + YGNodeStyleSetGap( + child, YGGutterAll, fdp.ConsumeProbability() * 100); + YGNodeStyleSetHeight(child, fdp.ConsumeFloatingPoint()); + YGNodeInsertChild(root, child, i); + FillFuzzedTree(fdp, config, child, depth + 1); + } +} + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + FuzzedDataProvider fdp(data, size); + const YGConfigRef config = YGConfigNew(); + const YGNodeRef root = YGNodeNewWithConfig(config); + FillFuzzedTree(fdp, config, root); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + YGNodeFreeRecursive(root); + YGConfigFree(config); + return 0; +}