mirror of
https://gitlab.kitware.com/cmake/cmake.git
synced 2026-09-25 04:09:36 +03:00
OSS-Fuzz: Add new fuzzer targets CMake Presets
Signed-off-by: Arthur Chan <arthur.chan@adalogics.com>
This commit is contained in:
@@ -71,6 +71,9 @@ add_fuzzer(cmPkgConfigParserFuzzer cmPkgConfigParserFuzzer.cxx)
|
||||
# JSON parser fuzzer
|
||||
add_fuzzer(cmJSONParserFuzzer cmJSONParserFuzzer.cxx)
|
||||
|
||||
# CMakePresets.json graph fuzzer
|
||||
add_fuzzer(cmCMakePresetsFuzzer cmCMakePresetsFuzzer.cxx)
|
||||
|
||||
# GCC dependency file fuzzer
|
||||
add_fuzzer(cmGccDepfileFuzzer cmGccDepfileFuzzer.cxx)
|
||||
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
# CMakePresets.json dictionary
|
||||
|
||||
# Structure
|
||||
"{"
|
||||
"}"
|
||||
"["
|
||||
"]"
|
||||
":"
|
||||
","
|
||||
"\""
|
||||
|
||||
# Top-level
|
||||
"version"
|
||||
"cmakeMinimumRequired"
|
||||
"include"
|
||||
"vendor"
|
||||
"configurePresets"
|
||||
"buildPresets"
|
||||
"testPresets"
|
||||
"packagePresets"
|
||||
"workflowPresets"
|
||||
|
||||
# Common preset fields
|
||||
"name"
|
||||
"inherits"
|
||||
"hidden"
|
||||
"displayName"
|
||||
"description"
|
||||
"environment"
|
||||
"condition"
|
||||
|
||||
# Configure preset fields
|
||||
"generator"
|
||||
"architecture"
|
||||
"toolset"
|
||||
"toolchainFile"
|
||||
"binaryDir"
|
||||
"installDir"
|
||||
"cacheVariables"
|
||||
"warnings"
|
||||
"errors"
|
||||
"debug"
|
||||
"strategy"
|
||||
"value"
|
||||
"type"
|
||||
|
||||
# Build, test, package and workflow preset fields
|
||||
"configurePreset"
|
||||
"configuration"
|
||||
"targets"
|
||||
"jobs"
|
||||
"generators"
|
||||
"steps"
|
||||
|
||||
# Macros expanded by ExpandMacros()
|
||||
"${sourceDir}"
|
||||
"${sourceParentDir}"
|
||||
"${sourceDirName}"
|
||||
"${presetName}"
|
||||
"${generator}"
|
||||
"${hostSystemName}"
|
||||
"${fileDir}"
|
||||
"${dollar}"
|
||||
"${pathListSep}"
|
||||
"$env{"
|
||||
"$penv{"
|
||||
"$vendor{"
|
||||
|
||||
# Condition types
|
||||
"const"
|
||||
"equals"
|
||||
"notEquals"
|
||||
"inList"
|
||||
"notInList"
|
||||
"matches"
|
||||
"notMatches"
|
||||
"anyOf"
|
||||
"allOf"
|
||||
"not"
|
||||
"conditions"
|
||||
"lhs"
|
||||
"rhs"
|
||||
"regex"
|
||||
"list"
|
||||
"string"
|
||||
|
||||
# Values seen in real files
|
||||
"included.json"
|
||||
"Ninja"
|
||||
"true"
|
||||
"false"
|
||||
@@ -0,0 +1,170 @@
|
||||
/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
|
||||
file LICENSE.rst or https://cmake.org/licensing for details. */
|
||||
|
||||
/*
|
||||
* Fuzzer for CMakePresets.json / CMakeUserPresets.json handling
|
||||
*
|
||||
* cmJSONParserFuzzer already covers the jsoncpp syntax layer. This fuzzer
|
||||
* targets the layer above it, cmCMakePresetsGraph, which turns a parsed
|
||||
* document into a resolved preset graph.
|
||||
*
|
||||
* That code runs before any build logic does: `cmake --list-presets` reads
|
||||
* these files without configuring anything, and IDEs parse them to populate
|
||||
* their UI as soon as a folder is opened. The input is therefore untrusted
|
||||
* data rather than the trusted, executable build code that CMakeLists.txt is.
|
||||
*
|
||||
* Coverage targets:
|
||||
* - schema validation of configure/build/test/package/workflow presets
|
||||
* - "include" chains between preset files, and their cycle detection
|
||||
* - "inherits" resolution, and its cycle detection
|
||||
* - macro expansion ($env{}, $penv{}, ${sourceDir}, ...)
|
||||
* - version gating and the project/user file interaction
|
||||
*/
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <initializer_list>
|
||||
#include <string>
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
#include "cmCMakePresetsGraph.h"
|
||||
#include "cmMessageMetadata.h"
|
||||
#include "cmSystemTools.h"
|
||||
|
||||
static constexpr size_t kMaxInputSize = 64 * 1024;
|
||||
|
||||
static std::string g_testDir;
|
||||
static std::string g_projectFile;
|
||||
static std::string g_userFile;
|
||||
static std::string g_explicitFile;
|
||||
static std::string g_includedFile;
|
||||
|
||||
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&) {});
|
||||
|
||||
char tmpl[] = "/tmp/cmake_fuzz_presets_XXXXXX";
|
||||
char* dir = mkdtemp(tmpl);
|
||||
if (dir) {
|
||||
g_testDir = dir;
|
||||
} else {
|
||||
g_testDir = "/tmp/cmake_fuzz_presets";
|
||||
cmSystemTools::MakeDirectory(g_testDir);
|
||||
}
|
||||
|
||||
g_projectFile = g_testDir + "/CMakePresets.json";
|
||||
g_userFile = g_testDir + "/CMakeUserPresets.json";
|
||||
g_explicitFile = g_testDir + "/explicit.json";
|
||||
g_includedFile = g_testDir + "/included.json";
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
// Which root file is on disk decides which reader runs, so the layout *is* the
|
||||
// mode -- see the comment in RunOneLayout().
|
||||
enum class RootLayout
|
||||
{
|
||||
Project,
|
||||
User,
|
||||
Explicit,
|
||||
};
|
||||
|
||||
bool WriteWholeFile(std::string const& path, uint8_t const* data, size_t size)
|
||||
{
|
||||
FILE* fp = fopen(path.c_str(), "wb");
|
||||
if (!fp) {
|
||||
return false;
|
||||
}
|
||||
bool const ok = fwrite(data, 1, size, fp) == size;
|
||||
fclose(fp);
|
||||
return ok;
|
||||
}
|
||||
|
||||
// Run the whole pipeline once, for one root layout, from a clean directory.
|
||||
void RunOneLayout(RootLayout layout, uint8_t const* data, size_t size)
|
||||
{
|
||||
unlink(g_projectFile.c_str());
|
||||
unlink(g_userFile.c_str());
|
||||
unlink(g_explicitFile.c_str());
|
||||
|
||||
std::string presetsFileArg;
|
||||
switch (layout) {
|
||||
case RootLayout::Project:
|
||||
if (!WriteWholeFile(g_projectFile, data, size)) {
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case RootLayout::User:
|
||||
if (!WriteWholeFile(g_userFile, data, size)) {
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case RootLayout::Explicit:
|
||||
if (!WriteWholeFile(g_explicitFile, data, size)) {
|
||||
return;
|
||||
}
|
||||
presetsFileArg = g_explicitFile;
|
||||
break;
|
||||
}
|
||||
|
||||
// A fresh graph per layout: ClearPresets() empties the preset maps but
|
||||
// leaves "errors" and "parseState" behind, so a reused graph would carry
|
||||
// diagnostic state from one layout into the next.
|
||||
cmCMakePresetsGraph graph;
|
||||
if (!graph.ReadProjectPresets(g_testDir, presetsFileArg)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Walking the graph reaches the inheritance and macro-expansion results, not
|
||||
// just the parse that produced them.
|
||||
for (auto const& it : graph.ConfigurePresets) {
|
||||
(void)graph.GetGeneratorForPreset(it.first);
|
||||
(void)it.second.Expanded.has_value();
|
||||
}
|
||||
for (auto const& it : graph.BuildPresets) {
|
||||
(void)graph.GetGeneratorForPreset(it.first);
|
||||
(void)it.second.Expanded.has_value();
|
||||
}
|
||||
for (auto const& it : graph.TestPresets) {
|
||||
(void)graph.GetGeneratorForPreset(it.first);
|
||||
(void)it.second.Expanded.has_value();
|
||||
}
|
||||
for (auto const& it : graph.PackagePresets) {
|
||||
(void)it.second.Expanded.has_value();
|
||||
}
|
||||
for (auto const& it : graph.WorkflowPresets) {
|
||||
(void)it.second.Expanded.has_value();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extern "C" int LLVMFuzzerTestOneInput(uint8_t const* data, size_t size)
|
||||
{
|
||||
if (size == 0 || size > kMaxInputSize) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!WriteWholeFile(g_includedFile, data, size)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (RootLayout layout :
|
||||
{ RootLayout::Project, RootLayout::User, RootLayout::Explicit }) {
|
||||
RunOneLayout(layout, data, size);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"version": 6,
|
||||
"cmakeMinimumRequired": { "major": 3, "minor": 25, "patch": 0 },
|
||||
"configurePresets": [
|
||||
{
|
||||
"name": "default",
|
||||
"displayName": "Default",
|
||||
"generator": "Ninja",
|
||||
"binaryDir": "${sourceDir}/build",
|
||||
"cacheVariables": { "CMAKE_BUILD_TYPE": "Release" }
|
||||
}
|
||||
],
|
||||
"buildPresets": [ { "name": "default", "configurePreset": "default" } ],
|
||||
"testPresets": [ { "name": "default", "configurePreset": "default" } ]
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"version": 6,
|
||||
"configurePresets": [
|
||||
{
|
||||
"name": "cond",
|
||||
"generator": "Ninja",
|
||||
"binaryDir": "${sourceDir}/c",
|
||||
"condition": {
|
||||
"type": "allOf",
|
||||
"conditions": [
|
||||
{ "type": "equals", "lhs": "${hostSystemName}", "rhs": "Linux" },
|
||||
{ "type": "matches", "string": "${presetName}", "regex": "^co" },
|
||||
{ "type": "inList", "string": "a", "list": [ "a", "b" ] },
|
||||
{ "type": "not", "condition": { "type": "const", "value": false } }
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"version": 6,
|
||||
"configurePresets": [
|
||||
{ "name": "a", "inherits": "b", "generator": "Ninja", "binaryDir": "x" },
|
||||
{ "name": "b", "inherits": "a", "generator": "Ninja", "binaryDir": "y" }
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"version": 6,
|
||||
"include": [ "included.json" ],
|
||||
"configurePresets": [ { "name": "outer", "generator": "Ninja", "binaryDir": "${sourceDir}/o" } ]
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"version": 6,
|
||||
"configurePresets": [
|
||||
{ "name": "base", "hidden": true, "generator": "Ninja", "binaryDir": "${sourceDir}/b" },
|
||||
{ "name": "mid", "hidden": true, "inherits": "base", "cacheVariables": { "A": "1" } },
|
||||
{ "name": "leaf", "inherits": [ "mid", "base" ], "cacheVariables": { "B": { "type": "BOOL", "value": "ON" } } }
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"version": 6,
|
||||
"configurePresets": [
|
||||
{
|
||||
"name": "macros",
|
||||
"generator": "Ninja",
|
||||
"binaryDir": "${sourceDir}/${presetName}/${hostSystemName}/${sourceDirName}",
|
||||
"installDir": "$env{HOME}/$penv{PATH}/${dollar}/${pathListSep}",
|
||||
"toolchainFile": "${fileDir}/tc.cmake",
|
||||
"environment": { "X": "${sourceParentDir}", "Y": "$env{X}" },
|
||||
"cacheVariables": { "G": "${generator}" }
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"version": 8,
|
||||
"configurePresets": [ { "name": "c", "generator": "Ninja", "binaryDir": "${sourceDir}/w" } ],
|
||||
"buildPresets": [ { "name": "b", "configurePreset": "c", "targets": [ "all" ], "jobs": 2 } ],
|
||||
"testPresets": [ { "name": "t", "configurePreset": "c" } ],
|
||||
"packagePresets": [ { "name": "p", "configurePreset": "c", "generators": [ "TGZ" ] } ],
|
||||
"workflowPresets": [
|
||||
{ "name": "w", "steps": [
|
||||
{ "type": "configure", "name": "c" },
|
||||
{ "type": "build", "name": "b" },
|
||||
{ "type": "test", "name": "t" },
|
||||
{ "type": "package", "name": "p" }
|
||||
] }
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user