1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147
|
/* 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;
}
|