OSS-Fuzz: Add new fuzzer targets pkg Config Resolver

Signed-off-by: Arthur Chan <arthur.chan@adalogics.com>
This commit is contained in:
Arthur Chan
2026-09-21 18:02:35 -04:00
committed by Brad King
parent c512ae97ce
commit 8932f356bd
3 changed files with 164 additions and 0 deletions
+1
View File
@@ -67,6 +67,7 @@ add_fuzzer(cmExprParserFuzzer cmExprParserFuzzer.cxx)
# pkg-config parser fuzzer # pkg-config parser fuzzer
add_fuzzer(cmPkgConfigParserFuzzer cmPkgConfigParserFuzzer.cxx) add_fuzzer(cmPkgConfigParserFuzzer cmPkgConfigParserFuzzer.cxx)
add_fuzzer(cmPkgConfigResolverFuzzer cmPkgConfigResolverFuzzer.cxx)
# JSON parser fuzzer # JSON parser fuzzer
add_fuzzer(cmJSONParserFuzzer cmJSONParserFuzzer.cxx) add_fuzzer(cmJSONParserFuzzer cmJSONParserFuzzer.cxx)
+63
View File
@@ -0,0 +1,63 @@
# pkg-config File Dictionary
# Key-value separators
"="
":"
# Variable reference
"${"
"}"
# Common keywords
"Name"
"Description"
"Version"
"Requires"
"Requires.private"
"Conflicts"
"Libs"
"Libs.private"
"Cflags"
"URL"
# Common variables
"prefix"
"exec_prefix"
"libdir"
"includedir"
"datarootdir"
"datadir"
"sysconfdir"
"sharedstatedir"
"localstatedir"
# Common values
"/usr"
"/usr/local"
"/usr/lib"
"/usr/include"
"-I"
"-L"
"-l"
"-D"
"-pthread"
# Whitespace
" "
"\x09"
"\x0a"
"\x0d\x0a"
# Comments
"#"
# Version operators
">="
"<="
">"
"<"
"!="
# Escapes
"\\"
"\\$"
+100
View File
@@ -0,0 +1,100 @@
/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
file LICENSE.rst or https://cmake.org/licensing for details. */
/*
* Fuzzer for CMake's pkg-config resolver.
*
* cmPkgConfigParserFuzzer already covers the .pc tokeniser, but it stops at
* the raw key/value pairs. Everything downstream of that is untested because
* the resolver is what drives its variable-interpolation entry points.
*
* The resolver is where the interesting work happens: recursive ${var}
* expansion, -I/-L/-l fragment splitting, sysroot re-rooting, and parsing
* "Requires:" dependency specs with version operators. .pc files come from
* whatever is on PKG_CONFIG_PATH, so a hostile or corrupt package drop is a
* realistic source of malformed input.
*
* All three strictness levels run on every input. Strictness is not chosen by
* the input -- Strict and Permissive reject files that BestEffort accepts, so
* running all three is what reaches the accept/reject paths in
* HasRequiredFields() and HasStrictConflicts().
*/
#include <cstddef>
#include <cstdint>
#include <string>
#include <vector>
#include <cm/optional>
#include "cmPkgConfigParser.h"
#include "cmPkgConfigResolver.h"
static constexpr size_t kMaxInputSize = 64 * 1024;
static cmPkgConfigParser ParseInput(uint8_t const* data, size_t size)
{
// cmPkgConfigParser::Parse takes a non-const buffer (may modify in place)
std::vector<char> buffer(data, data + size);
cmPkgConfigParser parser;
parser.Parse(buffer.data(), buffer.size());
parser.Finish();
parser.ParseComplete();
return parser;
}
static void Consume(cmPkgConfigResolver const& resolver)
{
(void)resolver.Name();
(void)resolver.Description();
(void)resolver.Version();
// Dependency specs: version operators and the spec grammar.
for (auto const& spec : resolver.Requires(false)) {
(void)cmPkgConfigResolver::VersionReqString(spec);
(void)cmPkgConfigResolver::CheckVersion(spec, "1.2.3");
}
(void)resolver.Requires(true);
(void)resolver.Conflicts();
(void)resolver.Provides();
// Fragment splitting and sysroot re-rooting, public and private.
(void)resolver.Cflags(false);
(void)resolver.Cflags(true);
(void)resolver.Libs(false);
(void)resolver.Libs(true);
}
extern "C" int LLVMFuzzerTestOneInput(uint8_t const* data, size_t size)
{
if (size == 0 || size > kMaxInputSize) {
return 0;
}
cmPkgConfigEnv env;
env.SysrootDir = std::string("/fuzz/sysroot");
env.TopBuildDir = std::string("/fuzz/build");
env.LibDirs = std::vector<std::string>{ "/usr/lib", "/fuzz/lib" };
env.SysCflags = std::vector<std::string>{ "/usr/include" };
env.SysLibs = std::vector<std::string>{ "/usr/lib" };
std::string const pcFileDir = "/fuzz/pkgconfig";
cmPkgConfigParser const parsed = ParseInput(data, size);
if (auto strict =
cmPkgConfigResolver::ResolveStrict(parsed, env, pcFileDir)) {
Consume(*strict);
}
if (auto permissive =
cmPkgConfigResolver::ResolvePermissive(parsed, env, pcFileDir)) {
Consume(*permissive);
}
Consume(cmPkgConfigResolver::ResolveBestEffort(parsed, env, pcFileDir));
return 0;
}