Browse Source

Tests/Fuzzing: Add CMake_BUILD_FUZZING option and build infrastructure

Add infrastructure for building fuzz testing targets with libFuzzer or
other fuzzing engines (e.g., OSS-Fuzz's LIB_FUZZING_ENGINE).

Features:
- CMake_BUILD_FUZZING option in root CMakeLists.txt
- Fuzzing/CMakeLists.txt with add_fuzzer() macro
- Support for libFuzzer and external fuzzing engines
- Documentation in Tests/Fuzzing/README.rst

The infrastructure is opt-in and requires a compatible fuzzing engine.
If CMake_BUILD_FUZZING is enabled but no engine is found, configuration
fails with a clear error message.

See Tests/Fuzzing/README.rst for build instructions.
Leslie P. Polzer 1 tháng trước cách đây
mục cha
commit
8535a7963c
65 tập tin đã thay đổi với 528 bổ sung6 xóa
  1. 3 0
      CMakeLists.txt
  2. 5 0
      Tests/CMakeLists.txt
  3. 54 0
      Tests/Fuzzing/CMakeLists.txt
  4. 49 6
      Tests/Fuzzing/README.rst
  5. 26 0
      Tests/Fuzzing/corpus/README.rst
  6. BIN
      Tests/Fuzzing/corpus/archive/simple.tar
  7. BIN
      Tests/Fuzzing/corpus/archive/simple.tar.gz
  8. BIN
      Tests/Fuzzing/corpus/archive/simple.zip
  9. BIN
      Tests/Fuzzing/corpus/archive/with_dir.tar
  10. 4 0
      Tests/Fuzzing/corpus/depfile/multiline.d
  11. 1 0
      Tests/Fuzzing/corpus/depfile/simple.d
  12. 1 0
      Tests/Fuzzing/corpus/depfile/spaces.d
  13. 109 0
      Tests/Fuzzing/corpus/elf/generate.py
  14. BIN
      Tests/Fuzzing/corpus/elf/minimal32.elf
  15. BIN
      Tests/Fuzzing/corpus/elf/minimal64.elf
  16. 1 0
      Tests/Fuzzing/corpus/expr/hex.txt
  17. 1 0
      Tests/Fuzzing/corpus/expr/not.txt
  18. 1 0
      Tests/Fuzzing/corpus/expr/parens.txt
  19. 1 0
      Tests/Fuzzing/corpus/expr/precedence.txt
  20. 1 0
      Tests/Fuzzing/corpus/expr/shift.txt
  21. 1 0
      Tests/Fuzzing/corpus/expr/simple.txt
  22. BIN
      Tests/Fuzzing/corpus/filelock/custom_name.bin
  23. BIN
      Tests/Fuzzing/corpus/filelock/mode0.bin
  24. BIN
      Tests/Fuzzing/corpus/filelock/mode1.bin
  25. BIN
      Tests/Fuzzing/corpus/filelock/mode2.bin
  26. BIN
      Tests/Fuzzing/corpus/filelock/mode3.bin
  27. BIN
      Tests/Fuzzing/corpus/filelock/symlink.bin
  28. BIN
      Tests/Fuzzing/corpus/filelock/timeout.bin
  29. 1 0
      Tests/Fuzzing/corpus/genex/basic.txt
  30. 1 0
      Tests/Fuzzing/corpus/genex/complex.txt
  31. 1 0
      Tests/Fuzzing/corpus/genex/interface.txt
  32. 1 0
      Tests/Fuzzing/corpus/genex/nested.txt
  33. 1 0
      Tests/Fuzzing/corpus/genex/target.txt
  34. 1 0
      Tests/Fuzzing/corpus/json/nested.json
  35. 10 0
      Tests/Fuzzing/corpus/json/preset.json
  36. 9 0
      Tests/Fuzzing/corpus/json/types.json
  37. 3 0
      Tests/Fuzzing/corpus/listfile/basic.txt
  38. 2 0
      Tests/Fuzzing/corpus/listfile/bracket.txt
  39. 29 0
      Tests/Fuzzing/corpus/listfile/complex.txt
  40. 1 0
      Tests/Fuzzing/corpus/path/dotdot.txt
  41. 1 0
      Tests/Fuzzing/corpus/path/relative.txt
  42. 1 0
      Tests/Fuzzing/corpus/path/unix.txt
  43. 1 0
      Tests/Fuzzing/corpus/path/windows.txt
  44. 5 0
      Tests/Fuzzing/corpus/pkgconfig/deps.pc
  45. 9 0
      Tests/Fuzzing/corpus/pkgconfig/simple.pc
  46. 20 0
      Tests/Fuzzing/corpus/script/cmake_path.cmake
  47. 14 0
      Tests/Fuzzing/corpus/script/complex_string.cmake
  48. 7 0
      Tests/Fuzzing/corpus/script/configure_file.cmake
  49. 12 0
      Tests/Fuzzing/corpus/script/control_flow.cmake
  50. 10 0
      Tests/Fuzzing/corpus/script/execute_process.cmake
  51. 4 0
      Tests/Fuzzing/corpus/script/file_ops.cmake
  52. 7 0
      Tests/Fuzzing/corpus/script/function.cmake
  53. 17 0
      Tests/Fuzzing/corpus/script/list_ops.cmake
  54. 3 0
      Tests/Fuzzing/corpus/script/math_ops.cmake
  55. 6 0
      Tests/Fuzzing/corpus/script/string_ops.cmake
  56. 5 0
      Tests/Fuzzing/corpus/script/variables.cmake
  57. 1 0
      Tests/Fuzzing/corpus/string/cmake_var.txt
  58. 1 0
      Tests/Fuzzing/corpus/string/list.txt
  59. 1 0
      Tests/Fuzzing/corpus/string/quoted.txt
  60. 1 0
      Tests/Fuzzing/corpus/version/long.txt
  61. 1 0
      Tests/Fuzzing/corpus/version/semver.txt
  62. 1 0
      Tests/Fuzzing/corpus/version/simple.txt
  63. 1 0
      Tests/Fuzzing/corpus/version/tweak.txt
  64. 75 0
      Tests/Fuzzing/xml_parser.dict
  65. 6 0
      Tests/README.rst

+ 3 - 0
CMakeLists.txt

@@ -224,6 +224,9 @@ endif()
 
 option(CMake_BUILD_PCH "Compile CMake with precompiled headers" OFF)
 
+option(CMake_BUILD_FUZZING "Build fuzz testing targets" OFF)
+mark_as_advanced(CMake_BUILD_FUZZING)
+
 # Check whether to build support for the debugger mode.
 if(NOT CMake_TEST_EXTERNAL_CMAKE)
   if(NOT DEFINED CMake_ENABLE_DEBUGGER)

+ 5 - 0
Tests/CMakeLists.txt

@@ -80,6 +80,11 @@ endif()
 configure_file(${CMake_SOURCE_DIR}/Tests/EnforceConfig.cmake.in
                ${CMake_BINARY_DIR}/Tests/EnforceConfig.cmake @ONLY)
 
+# Fuzzing targets (outside BUILD_TESTING for OSS-Fuzz compatibility)
+if(CMake_BUILD_FUZZING AND NOT CMake_TEST_EXTERNAL_CMAKE)
+  add_subdirectory(Fuzzing)
+endif()
+
 # Testing
 if(BUILD_TESTING)
   set(CMake_TEST_DEVENV "")

+ 54 - 0
Tests/Fuzzing/CMakeLists.txt

@@ -0,0 +1,54 @@
+# Fuzzing targets for CMake
+# See README.rst for documentation.
+
+# Determine fuzzing engine
+# OSS-Fuzz sets LIB_FUZZING_ENGINE, otherwise use libFuzzer
+if(DEFINED ENV{LIB_FUZZING_ENGINE})
+  set(FUZZING_ENGINE $ENV{LIB_FUZZING_ENGINE})
+  set(FUZZING_ENGINE_FOUND TRUE)
+elseif(CMAKE_CXX_COMPILER_ID MATCHES "Clang")
+  # Check if libFuzzer is available (needs both compile and link flags)
+  include(CheckCXXSourceCompiles)
+  set(CMAKE_REQUIRED_FLAGS "-fsanitize=fuzzer")
+  set(CMAKE_REQUIRED_LINK_OPTIONS "-fsanitize=fuzzer")
+  check_cxx_source_compiles("extern \"C\" int LLVMFuzzerTestOneInput(const char *data, long size) { return 0; }" HAVE_LIBFUZZER)
+  unset(CMAKE_REQUIRED_FLAGS)
+  unset(CMAKE_REQUIRED_LINK_OPTIONS)
+  if(HAVE_LIBFUZZER)
+    set(FUZZING_ENGINE "-fsanitize=fuzzer")
+    set(FUZZING_ENGINE_FOUND TRUE)
+  endif()
+endif()
+
+if(NOT FUZZING_ENGINE_FOUND)
+  message(FATAL_ERROR "No fuzzing engine found. CMake_BUILD_FUZZING requires libFuzzer or LIB_FUZZING_ENGINE.")
+endif()
+
+# Common link libraries
+set(FUZZER_LINK_LIBS
+  CMakeLib
+)
+
+# Macro to add a fuzzer target
+macro(add_fuzzer name source)
+  add_executable(${name} ${source})
+  target_link_libraries(${name} PRIVATE ${FUZZER_LINK_LIBS})
+
+  # If using libFuzzer directly, add the flag
+  if(FUZZING_ENGINE STREQUAL "-fsanitize=fuzzer")
+    target_compile_options(${name} PRIVATE -fsanitize=fuzzer)
+    target_link_options(${name} PRIVATE -fsanitize=fuzzer)
+  else()
+    # OSS-Fuzz provides engine as a library
+    target_link_libraries(${name} PRIVATE ${FUZZING_ENGINE})
+  endif()
+
+  # Ensure we don't apply clang-tidy to fuzzers
+  set_property(TARGET ${name} PROPERTY C_CLANG_TIDY "")
+  set_property(TARGET ${name} PROPERTY CXX_CLANG_TIDY "")
+endmacro()
+
+# Existing fuzzer from OSS-Fuzz integration
+add_fuzzer(xml_parser_fuzzer xml_parser_fuzzer.cc)
+
+message(STATUS "Fuzzing targets enabled with engine: ${FUZZING_ENGINE}")

+ 49 - 6
Tests/Fuzzing/README.rst

@@ -1,8 +1,51 @@
-The fuzzers in this directory are run continuously through OSS-fuzz.
-All fuzzers are implemented by way of the `libFuzzer engine`_.
+CMake Fuzzing Suite
+===================
 
-The link to the OSS-fuzz integration can be found here: (pending)
-All email addresses in the ``project.yaml`` file on OSS-fuzz will have access
-to detailed bug reports and will be notified via email if/when bugs are found.
+This directory contains fuzz targets for testing CMake with coverage-guided
+fuzzing. These fuzzers are integrated with `OSS-Fuzz`_ for continuous fuzzing.
 
-.. _`libFuzzer Engine`: https://llvm.org/docs/LibFuzzer.html
+All fuzzers are implemented using the `libFuzzer engine`_.
+
+.. _`OSS-Fuzz`: https://github.com/google/oss-fuzz
+.. _`libFuzzer engine`: https://llvm.org/docs/LibFuzzer.html
+
+Building Locally
+----------------
+
+To build the fuzzers locally with Clang and libFuzzer::
+
+  mkdir build && cd build
+  cmake -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ \
+        -DCMake_BUILD_FUZZING=ON \
+        -DCMAKE_C_FLAGS="-fsanitize=fuzzer-no-link,address" \
+        -DCMAKE_CXX_FLAGS="-fsanitize=fuzzer-no-link,address" \
+        ..
+  make -j$(nproc)
+
+Seed Corpora
+------------
+
+Each fuzzer has a corresponding seed corpus in ``corpus/<fuzzer_name>/``.
+
+Dictionaries
+------------
+
+Fuzzer dictionaries are provided to improve coverage. Each fuzzer has a
+corresponding ``.dict`` file with format-specific tokens.
+
+Security Considerations
+-----------------------
+
+These fuzzers specifically target security-sensitive code paths:
+
+- **Path traversal**: Archive extraction with ``../`` sequences
+- **Symlink attacks**: File handling with symlinks
+- **Memory safety**: Buffer handling in parsers
+- **Integer overflows**: Size calculations in binary parsers
+
+Bug Reports
+-----------
+
+Security bugs found by OSS-Fuzz are reported to CMake maintainers via the
+OSS-Fuzz security disclosure process. Non-security bugs are filed as public
+issues after a 90-day disclosure window.

+ 26 - 0
Tests/Fuzzing/corpus/README.rst

@@ -0,0 +1,26 @@
+Fuzzing Seed Corpora
+====================
+
+This directory contains seed corpus files for the CMake fuzz targets.
+Each subdirectory corresponds to a specific fuzzer.
+
+Regenerating Binary Corpus Files
+--------------------------------
+
+Some corpus files are binary and include generation scripts for reproducibility:
+
+- ``elf/generate.py`` - Generates minimal ELF headers for cmELFFuzzer
+
+To regenerate::
+
+    cd elf && python3 generate.py
+
+Adding New Corpus Files
+-----------------------
+
+When adding new corpus files:
+
+1. Keep files minimal - smaller seeds lead to faster fuzzing
+2. For binary formats, include a generation script
+3. Cover different code paths and edge cases
+4. Avoid duplicating coverage between files

BIN
Tests/Fuzzing/corpus/archive/simple.tar


BIN
Tests/Fuzzing/corpus/archive/simple.tar.gz


BIN
Tests/Fuzzing/corpus/archive/simple.zip


BIN
Tests/Fuzzing/corpus/archive/with_dir.tar


+ 4 - 0
Tests/Fuzzing/corpus/depfile/multiline.d

@@ -0,0 +1,4 @@
+target.o: \
+  source.cpp \
+  header1.h \
+  header2.h

+ 1 - 0
Tests/Fuzzing/corpus/depfile/simple.d

@@ -0,0 +1 @@
+main.o: main.cpp header.h

+ 1 - 0
Tests/Fuzzing/corpus/depfile/spaces.d

@@ -0,0 +1 @@
+"path with spaces.o": "source file.cpp" "my header.h"

+ 109 - 0
Tests/Fuzzing/corpus/elf/generate.py

@@ -0,0 +1,109 @@
+#!/usr/bin/env python3
+"""Generate minimal ELF corpus files for fuzzing.
+
+These are hand-crafted minimal ELF headers that exercise the ELF parser
+without requiring actual compiled binaries. They contain just enough
+structure to be recognized as valid ELF files.
+
+Usage: python3 generate.py
+
+Output files can be verified with: file *.elf
+"""
+
+import os
+import struct
+
+SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
+
+
+def generate_minimal_elf64():
+    """Generate a minimal 64-bit ELF executable header (72 bytes).
+
+    Structure: ELF64 header (64 bytes) with e_type=ET_EXEC, e_machine=EM_X86_64,
+    plus 8 bytes padding for fuzzer mutation headroom.
+    """
+    elf = bytearray()
+
+    # e_ident[16]: ELF identification
+    elf.extend(b'\x7fELF')  # EI_MAG: magic number
+    elf.append(2)           # EI_CLASS: ELFCLASS64
+    elf.append(1)           # EI_DATA: ELFDATA2LSB (little-endian)
+    elf.append(1)           # EI_VERSION: EV_CURRENT
+    elf.append(0)           # EI_OSABI: ELFOSABI_NONE
+    elf.extend(b'\x00' * 8) # EI_PAD: padding
+
+    # ELF header fields
+    elf.extend(struct.pack('<H', 2))      # e_type: ET_EXEC
+    elf.extend(struct.pack('<H', 0x3e))   # e_machine: EM_X86_64
+    elf.extend(struct.pack('<I', 1))      # e_version: EV_CURRENT
+    elf.extend(struct.pack('<Q', 0))      # e_entry: entry point
+    elf.extend(struct.pack('<Q', 0))      # e_phoff: program header offset
+    elf.extend(struct.pack('<Q', 0))      # e_shoff: section header offset
+    elf.extend(struct.pack('<I', 0))      # e_flags
+    elf.extend(struct.pack('<H', 0))      # e_ehsize
+    elf.extend(struct.pack('<H', 0))      # e_phentsize
+    elf.extend(struct.pack('<H', 0))      # e_phnum
+    elf.extend(struct.pack('<H', 0))      # e_shentsize
+    elf.extend(struct.pack('<H', 0))      # e_shnum
+    elf.extend(struct.pack('<H', 0))      # e_shstrndx
+
+    # Padding for fuzzer mutation headroom
+    elf.extend(b'\x00' * 8)
+
+    return bytes(elf)
+
+
+def generate_minimal_elf32():
+    """Generate a minimal 32-bit ELF PIE header (60 bytes).
+
+    Structure: ELF32 header (52 bytes) with e_type=ET_DYN, e_machine=EM_386,
+    plus 8 bytes padding for fuzzer mutation headroom.
+    """
+    elf = bytearray()
+
+    # e_ident[16]: ELF identification
+    elf.extend(b'\x7fELF')  # EI_MAG: magic number
+    elf.append(1)           # EI_CLASS: ELFCLASS32
+    elf.append(1)           # EI_DATA: ELFDATA2LSB (little-endian)
+    elf.append(1)           # EI_VERSION: EV_CURRENT
+    elf.append(0)           # EI_OSABI: ELFOSABI_NONE
+    elf.extend(b'\x00' * 8) # EI_PAD: padding
+
+    # ELF header fields
+    elf.extend(struct.pack('<H', 3))      # e_type: ET_DYN (PIE)
+    elf.extend(struct.pack('<H', 3))      # e_machine: EM_386
+    elf.extend(struct.pack('<I', 1))      # e_version: EV_CURRENT
+    elf.extend(struct.pack('<I', 0))      # e_entry: entry point
+    elf.extend(struct.pack('<I', 0))      # e_phoff: program header offset
+    elf.extend(struct.pack('<I', 0))      # e_shoff: section header offset
+    elf.extend(struct.pack('<I', 0))      # e_flags
+    elf.extend(struct.pack('<H', 0))      # e_ehsize
+    elf.extend(struct.pack('<H', 0))      # e_phentsize
+    elf.extend(struct.pack('<H', 0))      # e_phnum
+    elf.extend(struct.pack('<H', 0))      # e_shentsize
+    elf.extend(struct.pack('<H', 0))      # e_shnum
+    elf.extend(struct.pack('<H', 0))      # e_shstrndx
+
+    # Padding for fuzzer mutation headroom
+    elf.extend(b'\x00' * 8)
+
+    return bytes(elf)
+
+
+def main():
+    os.chdir(SCRIPT_DIR)
+
+    elf64 = generate_minimal_elf64()
+    elf32 = generate_minimal_elf32()
+
+    with open('minimal64.elf', 'wb') as f:
+        f.write(elf64)
+    print(f"Generated minimal64.elf ({len(elf64)} bytes)")
+
+    with open('minimal32.elf', 'wb') as f:
+        f.write(elf32)
+    print(f"Generated minimal32.elf ({len(elf32)} bytes)")
+
+
+if __name__ == '__main__':
+    main()

BIN
Tests/Fuzzing/corpus/elf/minimal32.elf


BIN
Tests/Fuzzing/corpus/elf/minimal64.elf


+ 1 - 0
Tests/Fuzzing/corpus/expr/hex.txt

@@ -0,0 +1 @@
+0xff & 0x0f

+ 1 - 0
Tests/Fuzzing/corpus/expr/not.txt

@@ -0,0 +1 @@
+~0

+ 1 - 0
Tests/Fuzzing/corpus/expr/parens.txt

@@ -0,0 +1 @@
+(1+2)*3

+ 1 - 0
Tests/Fuzzing/corpus/expr/precedence.txt

@@ -0,0 +1 @@
+1+2*3

+ 1 - 0
Tests/Fuzzing/corpus/expr/shift.txt

@@ -0,0 +1 @@
+1 << 4

+ 1 - 0
Tests/Fuzzing/corpus/expr/simple.txt

@@ -0,0 +1 @@
+1+2

BIN
Tests/Fuzzing/corpus/filelock/custom_name.bin


BIN
Tests/Fuzzing/corpus/filelock/mode0.bin


BIN
Tests/Fuzzing/corpus/filelock/mode1.bin


BIN
Tests/Fuzzing/corpus/filelock/mode2.bin


BIN
Tests/Fuzzing/corpus/filelock/mode3.bin


BIN
Tests/Fuzzing/corpus/filelock/symlink.bin


BIN
Tests/Fuzzing/corpus/filelock/timeout.bin


+ 1 - 0
Tests/Fuzzing/corpus/genex/basic.txt

@@ -0,0 +1 @@
+$<TARGET_FILE:mylib>

+ 1 - 0
Tests/Fuzzing/corpus/genex/complex.txt

@@ -0,0 +1 @@
+$<IF:$<AND:$<BOOL:${BUILD_SHARED},$<NOT:$<PLATFORM_ID:Windows>>>>,shared,static>

+ 1 - 0
Tests/Fuzzing/corpus/genex/interface.txt

@@ -0,0 +1 @@
+$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>$<INSTALL_INTERFACE:include>

+ 1 - 0
Tests/Fuzzing/corpus/genex/nested.txt

@@ -0,0 +1 @@
+$<$<CONFIG:Debug>:DEBUG_MODE>

+ 1 - 0
Tests/Fuzzing/corpus/genex/target.txt

@@ -0,0 +1 @@
+$<TARGET_PROPERTY:mylib,INTERFACE_INCLUDE_DIRECTORIES>

+ 1 - 0
Tests/Fuzzing/corpus/json/nested.json

@@ -0,0 +1 @@
+{"a":{"b":{"c":{"d":[1,2,3]}}}}

+ 10 - 0
Tests/Fuzzing/corpus/json/preset.json

@@ -0,0 +1,10 @@
+{
+  "version": 3,
+  "configurePresets": [
+    {
+      "name": "default",
+      "generator": "Ninja",
+      "binaryDir": "${sourceDir}/build"
+    }
+  ]
+}

+ 9 - 0
Tests/Fuzzing/corpus/json/types.json

@@ -0,0 +1,9 @@
+{
+  "string": "hello",
+  "number": 42,
+  "float": 3.14,
+  "bool": true,
+  "null": null,
+  "array": [1, "two", false],
+  "object": {"nested": "value"}
+}

+ 3 - 0
Tests/Fuzzing/corpus/listfile/basic.txt

@@ -0,0 +1,3 @@
+cmake_minimum_required(VERSION 3.10)
+project(Test)
+add_executable(main main.cpp)

+ 2 - 0
Tests/Fuzzing/corpus/listfile/bracket.txt

@@ -0,0 +1,2 @@
+set(x [=[nested [[brackets]] here]=])
+set(y [==[even more [=[nested]=] brackets]==])

+ 29 - 0
Tests/Fuzzing/corpus/listfile/complex.txt

@@ -0,0 +1,29 @@
+cmake_minimum_required(VERSION 3.16)
+project(ComplexProject VERSION 1.0.0 LANGUAGES CXX)
+
+set(CMAKE_CXX_STANDARD 17)
+
+option(BUILD_TESTS "Build tests" ON)
+
+if(BUILD_TESTS)
+  enable_testing()
+  add_subdirectory(tests)
+endif()
+
+function(my_function arg1 arg2)
+  message(STATUS "Function called with ${arg1} and ${arg2}")
+endfunction()
+
+# Bracket comment [[
+This is a bracket comment
+]]
+
+set(LONG_STRING [[
+This is a bracket
+argument with multiple
+lines
+]])
+
+foreach(item IN ITEMS a b c)
+  message("Item: ${item}")
+endforeach()

+ 1 - 0
Tests/Fuzzing/corpus/path/dotdot.txt

@@ -0,0 +1 @@
+/home/user/../other/./path

+ 1 - 0
Tests/Fuzzing/corpus/path/relative.txt

@@ -0,0 +1 @@
+../build/CMakeFiles

+ 1 - 0
Tests/Fuzzing/corpus/path/unix.txt

@@ -0,0 +1 @@
+/usr/local/lib/cmake

+ 1 - 0
Tests/Fuzzing/corpus/path/windows.txt

@@ -0,0 +1 @@
+C:\Program Files\CMake\bin

+ 5 - 0
Tests/Fuzzing/corpus/pkgconfig/deps.pc

@@ -0,0 +1,5 @@
+Name: WithDeps
+Version: 2.0
+Requires: dep1 >= 1.0, dep2
+Requires.private: internal
+Libs: -lwithdeps

+ 9 - 0
Tests/Fuzzing/corpus/pkgconfig/simple.pc

@@ -0,0 +1,9 @@
+prefix=/usr
+libdir=${prefix}/lib
+includedir=${prefix}/include
+
+Name: Example
+Description: An example library
+Version: 1.0.0
+Libs: -L${libdir} -lexample
+Cflags: -I${includedir}

+ 20 - 0
Tests/Fuzzing/corpus/script/cmake_path.cmake

@@ -0,0 +1,20 @@
+cmake_path(SET p "/usr/local/lib/cmake/foo.cmake")
+cmake_path(GET p ROOT_NAME root)
+cmake_path(GET p ROOT_DIRECTORY rootdir)
+cmake_path(GET p ROOT_PATH rootpath)
+cmake_path(GET p FILENAME fname)
+cmake_path(GET p EXTENSION ext)
+cmake_path(GET p STEM stem)
+cmake_path(GET p RELATIVE_PART rel)
+cmake_path(GET p PARENT_PATH parent)
+cmake_path(APPEND p "bar" OUTPUT_VARIABLE appended)
+cmake_path(REMOVE_FILENAME p)
+cmake_path(REPLACE_FILENAME p "new.txt")
+cmake_path(REMOVE_EXTENSION p)
+cmake_path(REPLACE_EXTENSION p ".hpp")
+cmake_path(NORMAL_PATH p)
+cmake_path(IS_ABSOLUTE p result)
+cmake_path(IS_RELATIVE p result)
+cmake_path(HAS_ROOT_NAME p result)
+cmake_path(HAS_FILENAME p result)
+cmake_path(COMPARE p EQUAL "/usr/local" equal_result)

+ 14 - 0
Tests/Fuzzing/corpus/script/complex_string.cmake

@@ -0,0 +1,14 @@
+set(input "Hello;World;From;CMake")
+string(REPLACE ";" " " spaced "${input}")
+string(REGEX REPLACE "([A-Z])" "_\\1" snake "${spaced}")
+string(REGEX MATCHALL "[A-Za-z]+" words "${spaced}")
+string(GENEX_STRIP "$<TARGET_FILE:foo>" stripped)
+string(APPEND result "line1\n" "line2\n")
+string(PREPEND result "header\n")
+string(CONCAT full "a" "b" "c" "d")
+string(JOIN ":" joined a b c d)
+string(MAKE_C_IDENTIFIER "my-var.name" c_id)
+string(RANDOM LENGTH 16 ALPHABET "0123456789ABCDEF" random)
+string(TIMESTAMP ts "%Y-%m-%d %H:%M:%S")
+string(UUID uuid NAMESPACE 6ba7b810-9dad-11d1-80b4-00c04fd430c8 NAME test TYPE SHA1)
+string(JSON json_val GET "{\"key\":\"value\"}" "key")

+ 7 - 0
Tests/Fuzzing/corpus/script/configure_file.cmake

@@ -0,0 +1,7 @@
+set(VERSION "1.0.0")
+set(AUTHOR "Test Author")
+file(WRITE /tmp/config.h.in "#define VERSION \"@VERSION@\"\n#define AUTHOR \"@AUTHOR@\"\n")
+configure_file(/tmp/config.h.in /tmp/config.h @ONLY)
+file(READ /tmp/config.h config_content)
+message(STATUS "Configured: ${config_content}")
+file(REMOVE /tmp/config.h.in /tmp/config.h)

+ 12 - 0
Tests/Fuzzing/corpus/script/control_flow.cmake

@@ -0,0 +1,12 @@
+set(X 5)
+if(X GREATER 3)
+  message("X > 3")
+elseif(X EQUAL 3)
+  message("X == 3")
+else()
+  message("X < 3")
+endif()
+
+foreach(i RANGE 3)
+  message("i = ${i}")
+endforeach()

+ 10 - 0
Tests/Fuzzing/corpus/script/execute_process.cmake

@@ -0,0 +1,10 @@
+execute_process(
+  COMMAND echo "Hello from process"
+  OUTPUT_VARIABLE output
+  ERROR_VARIABLE error
+  RESULT_VARIABLE result
+  TIMEOUT 5
+  WORKING_DIRECTORY /tmp
+)
+message(STATUS "Output: ${output}")
+message(STATUS "Result: ${result}")

+ 4 - 0
Tests/Fuzzing/corpus/script/file_ops.cmake

@@ -0,0 +1,4 @@
+file(WRITE "/tmp/cmake_fuzz_test.txt" "test content")
+file(READ "/tmp/cmake_fuzz_test.txt" content)
+file(REMOVE "/tmp/cmake_fuzz_test.txt")
+file(GLOB files "/tmp/*.txt")

+ 7 - 0
Tests/Fuzzing/corpus/script/function.cmake

@@ -0,0 +1,7 @@
+function(my_func arg1 arg2)
+  message("arg1: ${arg1}, arg2: ${arg2}")
+  set(RESULT "${arg1}_${arg2}" PARENT_SCOPE)
+endfunction()
+
+my_func("hello" "world")
+message("Result: ${RESULT}")

+ 17 - 0
Tests/Fuzzing/corpus/script/list_ops.cmake

@@ -0,0 +1,17 @@
+set(MYLIST a b c d e f)
+list(LENGTH MYLIST len)
+list(GET MYLIST 0 first)
+list(GET MYLIST -1 last)
+list(APPEND MYLIST g h)
+list(PREPEND MYLIST z)
+list(INSERT MYLIST 2 x y)
+list(FIND MYLIST c idx)
+list(REMOVE_AT MYLIST 0)
+list(REMOVE_ITEM MYLIST e)
+list(REMOVE_DUPLICATES MYLIST)
+list(REVERSE MYLIST)
+list(SORT MYLIST)
+list(SUBLIST MYLIST 1 3 sub)
+list(JOIN MYLIST "," joined)
+list(TRANSFORM MYLIST TOUPPER)
+list(FILTER MYLIST INCLUDE REGEX "[A-Z]")

+ 3 - 0
Tests/Fuzzing/corpus/script/math_ops.cmake

@@ -0,0 +1,3 @@
+math(EXPR result "1 + 2 * 3")
+math(EXPR hex "0xFF" OUTPUT_FORMAT HEXADECIMAL)
+math(EXPR dec "0x10" OUTPUT_FORMAT DECIMAL)

+ 6 - 0
Tests/Fuzzing/corpus/script/string_ops.cmake

@@ -0,0 +1,6 @@
+set(STR "Hello World")
+string(LENGTH "${STR}" len)
+string(TOUPPER "${STR}" upper)
+string(TOLOWER "${STR}" lower)
+string(REPLACE "World" "CMake" result "${STR}")
+string(REGEX MATCH "[A-Z]+" match "${STR}")

+ 5 - 0
Tests/Fuzzing/corpus/script/variables.cmake

@@ -0,0 +1,5 @@
+set(MY_VAR "hello")
+set(MY_LIST a b c d)
+message(STATUS "VAR: ${MY_VAR}")
+list(LENGTH MY_LIST len)
+message(STATUS "Length: ${len}")

+ 1 - 0
Tests/Fuzzing/corpus/string/cmake_var.txt

@@ -0,0 +1 @@
+${CMAKE_SOURCE_DIR}

+ 1 - 0
Tests/Fuzzing/corpus/string/list.txt

@@ -0,0 +1 @@
+item1;item2;item3;item4

+ 1 - 0
Tests/Fuzzing/corpus/string/quoted.txt

@@ -0,0 +1 @@
+"quoted string with spaces"

+ 1 - 0
Tests/Fuzzing/corpus/version/long.txt

@@ -0,0 +1 @@
+3.28.0.20231201

+ 1 - 0
Tests/Fuzzing/corpus/version/semver.txt

@@ -0,0 +1 @@
+1.2.3-alpha+build

+ 1 - 0
Tests/Fuzzing/corpus/version/simple.txt

@@ -0,0 +1 @@
+3.28.0

+ 1 - 0
Tests/Fuzzing/corpus/version/tweak.txt

@@ -0,0 +1 @@
+3.28.0.1

+ 75 - 0
Tests/Fuzzing/xml_parser.dict

@@ -0,0 +1,75 @@
+# XML dictionary for xml_parser_fuzzer
+
+# Processing instruction
+"<?xml"
+"?>"
+"version="
+"encoding="
+"standalone="
+
+# Document structure
+"<!DOCTYPE"
+"<!ELEMENT"
+"<!ATTLIST"
+"<!ENTITY"
+"<!NOTATION"
+"SYSTEM"
+"PUBLIC"
+
+# Tags
+"<"
+">"
+"/>"
+"</"
+
+# Attributes
+"="
+"\x22"
+"\x27"
+
+# CDATA and comments
+"<![CDATA["
+"]]>"
+"<!--"
+"-->"
+
+# Namespaces
+"xmlns"
+"xmlns:"
+
+# Common CMake XML elements
+"<cmake"
+"<project"
+"<target"
+"<file"
+"<dependency"
+"<source"
+"<include"
+"<define"
+"<flag"
+"<option"
+"<property"
+"<configuration"
+"<install"
+"<test"
+"<cache"
+
+# Entity references
+"&lt;"
+"&gt;"
+"&amp;"
+"&quot;"
+"&apos;"
+"&#"
+"&#x"
+
+# Whitespace
+"\x09"
+"\x0a"
+"\x0d"
+"\x20"
+
+# Special patterns
+"]]]]><![CDATA["
+"-->"
+"?>"

+ 6 - 0
Tests/README.rst

@@ -33,4 +33,10 @@ are organized as follows.
 
   See `RunCMake/README.rst`_.
 
+* ``Fuzzing/``:
+  Fuzz testing targets using libFuzzer, integrated with OSS-Fuzz.
+
+  See `Fuzzing/README.rst`_.
+
 .. _`RunCMake/README.rst`: RunCMake/README.rst
+.. _`Fuzzing/README.rst`: Fuzzing/README.rst