mirror of
https://gitlab.kitware.com/cmake/cmake.git
synced 2026-01-29 12:24:29 +00:00
148 lines
3.8 KiB
C++
148 lines
3.8 KiB
C++
/* 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;
|
|
}
|