Coverage Report

Created: 2026-02-09 06:05

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
50.5k
{
33
  // Prevent stack overflow on deeply nested structures
34
50.5k
  if (depth > 100) {
35
74
    return;
36
74
  }
37
38
50.4k
  switch (value.type()) {
39
186
    case Json::nullValue:
40
186
      (void)value.isNull();
41
186
      break;
42
41.9k
    case Json::intValue:
43
41.9k
      (void)value.asInt64();
44
41.9k
      break;
45
111
    case Json::uintValue:
46
111
      (void)value.asUInt64();
47
111
      break;
48
1.02k
    case Json::realValue:
49
1.02k
      (void)value.asDouble();
50
1.02k
      break;
51
1.91k
    case Json::stringValue:
52
1.91k
      (void)value.asString();
53
1.91k
      break;
54
739
    case Json::booleanValue:
55
739
      (void)value.asBool();
56
739
      break;
57
2.65k
    case Json::arrayValue:
58
42.6k
      for (Json::ArrayIndex i = 0; i < value.size() && i < 1000; ++i) {
59
40.0k
        TraverseValue(value[i], depth + 1);
60
40.0k
      }
61
2.65k
      break;
62
1.89k
    case Json::objectValue:
63
9.15k
      for (auto const& name : value.getMemberNames()) {
64
9.15k
        (void)name;
65
9.15k
        TraverseValue(value[name], depth + 1);
66
9.15k
      }
67
1.89k
      break;
68
50.4k
  }
69
50.4k
}
70
71
extern "C" int LLVMFuzzerTestOneInput(uint8_t const* data, size_t size)
72
3.23k
{
73
3.23k
  if (size == 0 || size > kMaxInputSize) {
74
6
    return 0;
75
6
  }
76
77
3.22k
  std::string input(reinterpret_cast<char const*>(data), size);
78
79
3.22k
  Json::Value root;
80
3.22k
  Json::CharReaderBuilder builder;
81
3.22k
  std::string errors;
82
83
3.22k
  std::istringstream stream(input);
84
85
  // Try parsing with default settings
86
3.22k
  bool success = Json::parseFromStream(builder, stream, &root, &errors);
87
88
3.22k
  if (success) {
89
    // Traverse the parsed structure
90
1.04k
    TraverseValue(root);
91
1.04k
  }
92
93
  // Also try with strict mode
94
3.22k
  builder["strictRoot"] = true;
95
3.22k
  builder["allowComments"] = false;
96
3.22k
  builder["allowTrailingCommas"] = false;
97
98
3.22k
  stream.clear();
99
3.22k
  stream.str(input);
100
101
3.22k
  success = Json::parseFromStream(builder, stream, &root, &errors);
102
3.22k
  if (success) {
103
298
    TraverseValue(root);
104
298
  }
105
106
3.22k
  return 0;
107
3.23k
}