Coverage Report

Created: 2026-03-12 06:35

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/CMake/Tests/Fuzzing/cmJSONParserFuzzer.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 JSON parsing (via jsoncpp)
6
 *
7
 * CMake parses JSON files for CMakePresets.json, compile_commands.json,
8
 * and various other configuration files. This fuzzer tests the JSON
9
 * parser for crashes and undefined behavior.
10
 *
11
 * Coverage targets:
12
 * - JSON value parsing (strings, numbers, booleans, null)
13
 * - Array and object parsing
14
 * - Nested structures
15
 * - Unicode handling
16
 * - Error recovery
17
 */
18
19
#include <cstddef>
20
#include <cstdint>
21
#include <sstream>
22
#include <string>
23
24
#include <cm3p/json/reader.h>
25
#include <cm3p/json/value.h>
26
27
// Limit input size
28
static constexpr size_t kMaxInputSize = 256 * 1024; // 256KB
29
30
// Recursive helper to access all values (exercises accessor code)
31
static void TraverseValue(Json::Value const& value, int depth = 0)
32
66.1k
{
33
  // Prevent stack overflow on deeply nested structures
34
66.1k
  if (depth > 100) {
35
200
    return;
36
200
  }
37
38
65.9k
  switch (value.type()) {
39
77
    case Json::nullValue:
40
77
      (void)value.isNull();
41
77
      break;
42
57.2k
    case Json::intValue:
43
57.2k
      (void)value.asInt64();
44
57.2k
      break;
45
138
    case Json::uintValue:
46
138
      (void)value.asUInt64();
47
138
      break;
48
623
    case Json::realValue:
49
623
      (void)value.asDouble();
50
623
      break;
51
1.56k
    case Json::stringValue:
52
1.56k
      (void)value.asString();
53
1.56k
      break;
54
1.66k
    case Json::booleanValue:
55
1.66k
      (void)value.asBool();
56
1.66k
      break;
57
3.25k
    case Json::arrayValue:
58
59.7k
      for (Json::ArrayIndex i = 0; i < value.size() && i < 1000; ++i) {
59
56.4k
        TraverseValue(value[i], depth + 1);
60
56.4k
      }
61
3.25k
      break;
62
1.34k
    case Json::objectValue:
63
8.25k
      for (auto const& name : value.getMemberNames()) {
64
8.25k
        (void)name;
65
8.25k
        TraverseValue(value[name], depth + 1);
66
8.25k
      }
67
1.34k
      break;
68
65.9k
  }
69
65.9k
}
70
71
extern "C" int LLVMFuzzerTestOneInput(uint8_t const* data, size_t size)
72
3.43k
{
73
3.43k
  if (size == 0 || size > kMaxInputSize) {
74
6
    return 0;
75
6
  }
76
77
3.42k
  std::string input(reinterpret_cast<char const*>(data), size);
78
79
3.42k
  Json::Value root;
80
3.42k
  Json::CharReaderBuilder builder;
81
3.42k
  std::string errors;
82
83
3.42k
  std::istringstream stream(input);
84
85
  // Try parsing with default settings
86
3.42k
  bool success = Json::parseFromStream(builder, stream, &root, &errors);
87
88
3.42k
  if (success) {
89
    // Traverse the parsed structure
90
1.13k
    TraverseValue(root);
91
1.13k
  }
92
93
  // Also try with strict mode
94
3.42k
  builder["strictRoot"] = true;
95
3.42k
  builder["allowComments"] = false;
96
3.42k
  builder["allowTrailingCommas"] = false;
97
98
3.42k
  stream.clear();
99
3.42k
  stream.str(input);
100
101
3.42k
  success = Json::parseFromStream(builder, stream, &root, &errors);
102
3.42k
  if (success) {
103
279
    TraverseValue(root);
104
279
  }
105
106
3.42k
  return 0;
107
3.43k
}