| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147 |
- /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
- file LICENSE.rst or https://cmake.org/licensing for details. */
- /*
- * Fuzzer for CMake script execution
- *
- * This fuzzer executes CMake scripts in script mode (-P).
- * This exercises the majority of CMake's codebase including:
- * - All built-in commands
- * - Variable expansion
- * - Control flow (if, foreach, while, function, macro)
- * - String/list/file operations
- * - Generator expressions
- *
- * This is the highest-impact fuzzer for coverage.
- *
- * Performance notes:
- * - Uses memfd_create on Linux for memory-backed file I/O
- * - Falls back to temp files on other platforms
- */
- #include <cstddef>
- #include <cstdint>
- #include <cstdio>
- #include <cstdlib>
- #include <string>
- #include <vector>
- #include <unistd.h>
- #include "cmCMakePolicyCommand.h"
- #include "cmExecutionStatus.h"
- #include "cmGlobalGenerator.h"
- #include "cmMakefile.h"
- #include "cmMessenger.h"
- #include "cmState.h"
- #include "cmStateSnapshot.h"
- #include "cmSystemTools.h"
- #include "cmake.h"
- #ifdef __linux__
- # include <sys/mman.h>
- # ifndef MFD_CLOEXEC
- # define MFD_CLOEXEC 0x0001U
- # endif
- #endif
- static constexpr size_t kMaxInputSize = 256 * 1024;
- static std::string g_testDir;
- static std::string g_scriptFile;
- static bool g_useMemfd = false;
- extern "C" int LLVMFuzzerInitialize(int* argc, char*** argv)
- {
- (void)argc;
- (void)argv;
- // Suppress output during fuzzing (set once at init)
- cmSystemTools::SetMessageCallback(
- [](std::string const&, cmMessageMetadata const&) {});
- cmSystemTools::SetStdoutCallback([](std::string const&) {});
- cmSystemTools::SetStderrCallback([](std::string const&) {});
- // Create unique test directory (even with memfd, scripts can create files)
- char tmpl[] = "/tmp/cmake_fuzz_script_XXXXXX";
- char* dir = mkdtemp(tmpl);
- if (dir) {
- g_testDir = dir;
- } else {
- g_testDir = "/tmp/cmake_fuzz_script";
- cmSystemTools::MakeDirectory(g_testDir);
- }
- #ifdef __linux__
- // Try to use memfd for better performance
- int fd = memfd_create("cmake_fuzz", MFD_CLOEXEC);
- if (fd >= 0) {
- g_useMemfd = true;
- // Create path via /proc/self/fd
- g_scriptFile = "/proc/self/fd/" + std::to_string(fd);
- // Keep fd open - will be reused
- } else
- #endif
- {
- g_scriptFile = g_testDir + "/fuzz_script.cmake";
- }
- return 0;
- }
- extern "C" int LLVMFuzzerTestOneInput(uint8_t const* data, size_t size)
- {
- if (size == 0 || size > kMaxInputSize) {
- return 0;
- }
- #ifdef __linux__
- if (g_useMemfd) {
- // Extract fd from path and write directly
- int fd = std::atoi(g_scriptFile.c_str() + 14); // "/proc/self/fd/"
- ftruncate(fd, 0);
- lseek(fd, 0, SEEK_SET);
- if (write(fd, data, size) != static_cast<ssize_t>(size)) {
- return 0;
- }
- } else
- #endif
- {
- // Write script to temp file
- FILE* fp = fopen(g_scriptFile.c_str(), "wb");
- if (!fp)
- return 0;
- fwrite(data, 1, size, fp);
- fclose(fp);
- }
- // Save CWD in case script uses file(CHDIR)
- std::string cwd = cmSystemTools::GetCurrentWorkingDirectory();
- // Create cmake instance for script mode
- cmake cm(cmState::Role::Script);
- cm.SetHomeDirectory(g_testDir);
- cm.SetHomeOutputDirectory(g_testDir);
- // Run the script
- std::vector<std::string> args;
- args.push_back("cmake");
- args.push_back("-P");
- args.push_back(g_scriptFile);
- (void)cm.Run(args, false);
- // Restore CWD before cleanup (script may have changed it via file(CHDIR))
- cmSystemTools::ChangeDirectory(cwd);
- // Cleanup temp file (memfd doesn't need cleanup)
- if (!g_useMemfd) {
- unlink(g_scriptFile.c_str());
- }
- // Clean up any files the script may have created in g_testDir
- // This prevents disk growth and non-determinism from previous iterations
- cmSystemTools::RemoveADirectory(g_testDir);
- cmSystemTools::MakeDirectory(g_testDir);
- return 0;
- }
|