/src/CMake/Tests/Fuzzing/cmPkgConfigResolverFuzzer.cxx
Line | Count | Source |
1 | | /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying |
2 | | file LICENSE.rst or https://cmake.org/licensing for details. */ |
3 | | |
4 | | /* |
5 | | * Fuzzer for CMake's pkg-config resolver. |
6 | | * |
7 | | * cmPkgConfigParserFuzzer already covers the .pc tokeniser, but it stops at |
8 | | * the raw key/value pairs. Everything downstream of that is untested because |
9 | | * the resolver is what drives its variable-interpolation entry points. |
10 | | * |
11 | | * The resolver is where the interesting work happens: recursive ${var} |
12 | | * expansion, -I/-L/-l fragment splitting, sysroot re-rooting, and parsing |
13 | | * "Requires:" dependency specs with version operators. .pc files come from |
14 | | * whatever is on PKG_CONFIG_PATH, so a hostile or corrupt package drop is a |
15 | | * realistic source of malformed input. |
16 | | * |
17 | | * All three strictness levels run on every input. Strictness is not chosen by |
18 | | * the input -- Strict and Permissive reject files that BestEffort accepts, so |
19 | | * running all three is what reaches the accept/reject paths in |
20 | | * HasRequiredFields() and HasStrictConflicts(). |
21 | | */ |
22 | | |
23 | | #include <cstddef> |
24 | | #include <cstdint> |
25 | | #include <string> |
26 | | #include <vector> |
27 | | |
28 | | #include <cm/optional> |
29 | | |
30 | | #include "cmPkgConfigParser.h" |
31 | | #include "cmPkgConfigResolver.h" |
32 | | |
33 | | static constexpr size_t kMaxInputSize = 64 * 1024; |
34 | | |
35 | | static cmPkgConfigParser ParseInput(uint8_t const* data, size_t size) |
36 | 4.59k | { |
37 | | // cmPkgConfigParser::Parse takes a non-const buffer (may modify in place) |
38 | 4.59k | std::vector<char> buffer(data, data + size); |
39 | | |
40 | 4.59k | cmPkgConfigParser parser; |
41 | 4.59k | parser.Parse(buffer.data(), buffer.size()); |
42 | 4.59k | parser.Finish(); |
43 | 4.59k | parser.ParseComplete(); |
44 | | |
45 | 4.59k | return parser; |
46 | 4.59k | } |
47 | | |
48 | | static void Consume(cmPkgConfigResolver const& resolver) |
49 | 5.62k | { |
50 | 5.62k | (void)resolver.Name(); |
51 | 5.62k | (void)resolver.Description(); |
52 | 5.62k | (void)resolver.Version(); |
53 | | |
54 | | // Dependency specs: version operators and the spec grammar. |
55 | 7.54M | for (auto const& spec : resolver.Requires(false)) { |
56 | 7.54M | (void)cmPkgConfigResolver::VersionReqString(spec); |
57 | 7.54M | (void)cmPkgConfigResolver::CheckVersion(spec, "1.2.3"); |
58 | 7.54M | } |
59 | 5.62k | (void)resolver.Requires(true); |
60 | 5.62k | (void)resolver.Conflicts(); |
61 | 5.62k | (void)resolver.Provides(); |
62 | | |
63 | | // Fragment splitting and sysroot re-rooting, public and private. |
64 | 5.62k | (void)resolver.Cflags(false); |
65 | 5.62k | (void)resolver.Cflags(true); |
66 | 5.62k | (void)resolver.Libs(false); |
67 | 5.62k | (void)resolver.Libs(true); |
68 | 5.62k | } |
69 | | |
70 | | extern "C" int LLVMFuzzerTestOneInput(uint8_t const* data, size_t size) |
71 | 4.60k | { |
72 | 4.60k | if (size == 0 || size > kMaxInputSize) { |
73 | 7 | return 0; |
74 | 7 | } |
75 | | |
76 | 4.59k | cmPkgConfigEnv env; |
77 | 4.59k | env.SysrootDir = std::string("/fuzz/sysroot"); |
78 | 4.59k | env.TopBuildDir = std::string("/fuzz/build"); |
79 | 4.59k | env.LibDirs = std::vector<std::string>{ "/usr/lib", "/fuzz/lib" }; |
80 | 4.59k | env.SysCflags = std::vector<std::string>{ "/usr/include" }; |
81 | 4.59k | env.SysLibs = std::vector<std::string>{ "/usr/lib" }; |
82 | | |
83 | 4.59k | std::string const pcFileDir = "/fuzz/pkgconfig"; |
84 | | |
85 | 4.59k | cmPkgConfigParser const parsed = ParseInput(data, size); |
86 | | |
87 | 4.59k | if (auto strict = |
88 | 4.59k | cmPkgConfigResolver::ResolveStrict(parsed, env, pcFileDir)) { |
89 | 485 | Consume(*strict); |
90 | 485 | } |
91 | | |
92 | 4.59k | if (auto permissive = |
93 | 4.59k | cmPkgConfigResolver::ResolvePermissive(parsed, env, pcFileDir)) { |
94 | 542 | Consume(*permissive); |
95 | 542 | } |
96 | | |
97 | 4.59k | Consume(cmPkgConfigResolver::ResolveBestEffort(parsed, env, pcFileDir)); |
98 | | |
99 | 4.59k | return 0; |
100 | 4.60k | } |