Skip to content

Commit

Permalink
feat: initial integration for fuzzing
Browse files Browse the repository at this point in the history
  • Loading branch information
cktii committed Feb 18, 2025
1 parent 34d2adf commit fc29358
Show file tree
Hide file tree
Showing 8 changed files with 1,429 additions and 58 deletions.
140 changes: 82 additions & 58 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,74 +6,98 @@ project(lexy VERSION 2022.12.1 LANGUAGES CXX)

set(LEXY_USER_CONFIG_HEADER "" CACHE FILEPATH "The user config header for lexy.")
option(LEXY_FORCE_CPP17 "Whether or not lexy should use C++17 even if compiler supports C++20." OFF)
option(LEXY_BUILD_FUZZERS "whether or not fuzzers should be built" OFF)
option(LEXY_BUILD_FUZZERS_AFLPLUSPLUS "Use AFL++ instead of libFuzzer" OFF)
option(FORCE_STATIC_LINKING "Force static linking for fuzzing targets" OFF)

add_subdirectory(src)
if(LEXY_BUILD_FUZZERS)
execute_process(
COMMAND ${CMAKE_CXX_COMPILER} --version
OUTPUT_VARIABLE CXX_COMPILER_VERSION
OUTPUT_STRIP_TRAILING_WHITESPACE
)
if(NOT CXX_COMPILER_VERSION MATCHES "clang")
message(FATAL_ERROR "Fuzzing requires a Clang-based compiler")
endif()

option(LEXY_ENABLE_INSTALL "whether or not to enable the install rule" ON)
if(LEXY_ENABLE_INSTALL)
include(CMakePackageConfigHelpers)
include(GNUInstallDirs)
if(CMAKE_C_COMPILER MATCHES ".*afl-.*" OR CMAKE_CXX_COMPILER MATCHES ".*afl-.*")
set(LEXY_BUILD_FUZZERS_AFLPLUSPLUS ON CACHE BOOL "Use AFL++ instead of libFuzzer" FORCE)
message(STATUS "AFL++ compiler detected - automatically enabling AFL++ mode")
endif()

install(TARGETS lexy lexy_core lexy_file lexy_unicode lexy_ext _lexy_base lexy_dev
EXPORT ${PROJECT_NAME}Targets
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR})
add_subdirectory(src)
add_subdirectory(fuzzers)
else()
add_subdirectory(src)

install(EXPORT ${PROJECT_NAME}Targets
NAMESPACE foonathan::
DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}")
if(CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR)
cmake_minimum_required(VERSION 3.18)
option(LEXY_BUILD_BENCHMARKS "whether or not benchmarks should be built" OFF)
option(LEXY_BUILD_EXAMPLES "whether or not examples should be built" ON)
option(LEXY_BUILD_TESTS "whether or not tests should be built" ON)
option(LEXY_BUILD_DOCS "whether or not docs should be built" OFF)
option(LEXY_BUILD_PACKAGE "whether or not the package should be built" ON)
option(LEXY_ENABLE_INSTALL "whether or not to enable the install rule" ON)

configure_package_config_file(
cmake/lexyConfig.cmake.in
"${PROJECT_BINARY_DIR}/${PROJECT_NAME}Config.cmake"
INSTALL_DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}")
install(FILES "${PROJECT_BINARY_DIR}/${PROJECT_NAME}Config.cmake"
DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}")
# Optional components
if(LEXY_BUILD_EXAMPLES)
add_subdirectory(examples)
endif()
if(LEXY_BUILD_BENCHMARKS)
add_subdirectory(benchmarks)
endif()
if(LEXY_BUILD_TESTS)
set(DOCTEST_NO_INSTALL ON)
enable_testing()
add_subdirectory(tests)
endif()
if(LEXY_BUILD_DOCS)
add_subdirectory(docs EXCLUDE_FROM_ALL)
endif()

# YYYY.MM.N1 is compatible with YYYY.MM.N2.
write_basic_package_version_file(
"${PROJECT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake"
COMPATIBILITY SameMinorVersion)
# Package creation
if(LEXY_BUILD_PACKAGE)
set(package_files include/ src/ cmake/ CMakeLists.txt LICENSE)
add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/lexy-src.zip
COMMAND ${CMAKE_COMMAND} -E tar c ${CMAKE_CURRENT_BINARY_DIR}/lexy-src.zip --format=zip -- ${package_files}
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
DEPENDS ${package_files})
add_custom_target(lexy_package DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/lexy-src.zip)
endif()

install(FILES "${PROJECT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake"
DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}")
# Installation configuration
if(LEXY_ENABLE_INSTALL)
include(CMakePackageConfigHelpers)
include(GNUInstallDirs)

install(DIRECTORY include/lexy include/lexy_ext
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
FILES_MATCHING
PATTERN "*.hpp")
endif()
install(TARGETS lexy lexy_core lexy_file lexy_unicode lexy_ext _lexy_base lexy_dev
EXPORT ${PROJECT_NAME}Targets
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR})

if(CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR)
cmake_minimum_required(VERSION 3.18)
option(LEXY_BUILD_BENCHMARKS "whether or not benchmarks should be built" OFF)
option(LEXY_BUILD_EXAMPLES "whether or not examples should be built" ON)
option(LEXY_BUILD_TESTS "whether or not tests should be built" ON)
option(LEXY_BUILD_DOCS "whether or not docs should be built" OFF)
option(LEXY_BUILD_PACKAGE "whether or not the package should be built" ON)
install(EXPORT ${PROJECT_NAME}Targets
NAMESPACE foonathan::
DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}")

if(LEXY_BUILD_PACKAGE)
set(package_files include/ src/ cmake/ CMakeLists.txt LICENSE)
add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/lexy-src.zip
COMMAND ${CMAKE_COMMAND} -E tar c ${CMAKE_CURRENT_BINARY_DIR}/lexy-src.zip --format=zip -- ${package_files}
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
DEPENDS ${package_files})
add_custom_target(lexy_package DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/lexy-src.zip)
endif()
configure_package_config_file(
cmake/lexyConfig.cmake.in
"${PROJECT_BINARY_DIR}/${PROJECT_NAME}Config.cmake"
INSTALL_DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}")
install(FILES "${PROJECT_BINARY_DIR}/${PROJECT_NAME}Config.cmake"
DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}")

if(LEXY_BUILD_EXAMPLES)
add_subdirectory(examples)
endif()
if(LEXY_BUILD_BENCHMARKS)
add_subdirectory(benchmarks)
endif()
if(LEXY_BUILD_TESTS)
set(DOCTEST_NO_INSTALL ON)
enable_testing()
add_subdirectory(tests)
endif()
if(LEXY_BUILD_DOCS)
add_subdirectory(docs EXCLUDE_FROM_ALL)
write_basic_package_version_file(
"${PROJECT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake"
COMPATIBILITY SameMinorVersion)

install(FILES "${PROJECT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake"
DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}")

install(DIRECTORY include/lexy include/lexy_ext
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
FILES_MATCHING
PATTERN "*.hpp")
endif()
endif()
endif()
149 changes: 149 additions & 0 deletions fuzzers/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
cmake_minimum_required(VERSION 3.18)

set(BASE_FLAGS -O2 -ggdb -fno-omit-frame-pointer)

if(FORCE_STATIC_LINKING)
set(CMAKE_FIND_LIBRARY_SUFFIXES .a ${CMAKE_FIND_LIBRARY_SUFFIXES})
set(BUILD_SHARED_LIBS OFF)
endif()

# Handle fuzzing-specific settings
if(LEXY_FUZZING_ENGINE STREQUAL "libfuzzer")
# For libFuzzer, use fuzzer-no-link for the library
set(SANITIZER_FLAGS
-fsanitize=address,undefined,fuzzer-no-link
)

set(FUZZING_FLAGS
-fsanitize=fuzzer,address,undefined
)
else() # AFL
set(SANITIZER_FLAGS
-fsanitize=address,undefined
)

set(FUZZING_FLAGS
-fsanitize=fuzzer
)
endif()

add_compile_options(${BASE_FLAGS} ${SANITIZER_FLAGS})
add_link_options(${BASE_FLAGS} ${SANITIZER_FLAGS})

function(add_lexy_fuzzer TARGET_NAME SOURCE_FILE EXAMPLE_FILE SEED_CONTENT)
# Create corpus directories
file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/${TARGET_NAME}_corpus)
file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/${TARGET_NAME}_artifacts)

if(EXISTS "${CMAKE_SOURCE_DIR}/examples/${EXAMPLE_FILE}")
configure_file(
${CMAKE_SOURCE_DIR}/examples/${EXAMPLE_FILE}
${CMAKE_CURRENT_BINARY_DIR}/${EXAMPLE_FILE}
COPYONLY
)
endif()

file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/${TARGET_NAME}_corpus/simple.${TARGET_NAME}
"${SEED_CONTENT}"
)

add_executable(${TARGET_NAME} ${SOURCE_FILE})

target_compile_options(${TARGET_NAME} PRIVATE
${BASE_FLAGS}
${SANITIZER_FLAGS}
${FUZZING_FLAGS}
)

# Handle static linking options
if(FORCE_STATIC_LINKING)
if(LEXY_FUZZING_ENGINE STREQUAL "libfuzzer")
target_link_options(${TARGET_NAME} PRIVATE
${BASE_FLAGS}
${FUZZING_FLAGS}
${SANITIZER_FLAGS}
-static-libstdc++
-static-libgcc
)
else() # AFL
target_link_options(${TARGET_NAME} PRIVATE
${BASE_FLAGS}
${SANITIZER_FLAGS}
${FUZZING_FLAGS}
-static-libstdc++
-static-libgcc
)
endif()
else()
target_link_options(${TARGET_NAME} PRIVATE
${BASE_FLAGS}
${SANITIZER_FLAGS}
${FUZZING_FLAGS}
)
endif()

# Link against the libraries
if(FORCE_STATIC_LINKING)
target_link_libraries(${TARGET_NAME}
PRIVATE
-static-libstdc++
-static-libgcc
lexy_core
lexy_file
lexy_unicode
)
else()
target_link_libraries(${TARGET_NAME}
PRIVATE
lexy_core
lexy_file
lexy_unicode
)
endif()

target_include_directories(${TARGET_NAME}
PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/../include
${CMAKE_CURRENT_BINARY_DIR}
)

set_target_properties(${TARGET_NAME}
PROPERTIES
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}"
)
endfunction()

add_lexy_fuzzer(
json_fuzzer
json_fuzzer.cpp
json.cpp
"{\"test\": true, \"number\": 42, \"string\": \"hello\", \"array\": [1,2,3]}"
)

add_lexy_fuzzer(
xml_fuzzer
xml_fuzzer.cpp
xml.cpp
"<root><test>Hello</test><data><![CDATA[Test data]]></data><empty/></root>"
)

add_lexy_fuzzer(
parse_tree_fuzzer
parse_tree_fuzzer.cpp
"parse_tree.hpp"
"<root><node>Sample</node></root>"
)

add_lexy_fuzzer(
visualize_fuzzer
visualize_fuzzer.cpp
"visualize.hpp"
"normal string\n\\u0041\\u0042\\n\\t\\r\\u{10FFFF}\\u{000000}\n<unicode>⚡️🌈🌍</unicode>\n\\u0000"
)

add_lexy_fuzzer(
buffer_fuzzer
buffer_fuzzer.cpp
"_detail/buffer_builder.hpp"
"ABCD\\0\\xFF\\x80\\xFE\\0\\1\\2\\3\\4\\5\\6\\7\\x42\\xAA\\xBB\\xCC\\xDD"
)
Loading

0 comments on commit fc29358

Please sign in to comment.