Coverage Report

Created: 2026-09-14 06:43

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/value.h>
25
26
#include "cmJSONState.h"
27
28
// Limit input size
29
static constexpr size_t kMaxInputSize = 256 * 1024; // 256KB
30
31
// Recursive helper to access all values (exercises accessor code)
32
static void TraverseValue(Json::Value const& value, int depth = 0)
33
168k
{
34
  // Prevent stack overflow on deeply nested structures
35
168k
  if (depth > 100) {
36
600
    return;
37
600
  }
38
39
167k
  switch (value.type()) {
40
3.92k
    case Json::nullValue:
41
3.92k
      (void)value.isNull();
42
3.92k
      break;
43
107k
    case Json::intValue:
44
107k
      (void)value.asInt64();
45
107k
      break;
46
561
    case Json::uintValue:
47
561
      (void)value.asUInt64();
48
561
      break;
49
3.13k
    case Json::realValue:
50
3.13k
      (void)value.asDouble();
51
3.13k
      break;
52
1.40k
    case Json::stringValue:
53
1.40k
      (void)value.asString();
54
1.40k
      break;
55
3.34k
    case Json::booleanValue:
56
3.34k
      (void)value.asBool();
57
3.34k
      break;
58
33.0k
    case Json::arrayValue:
59
153k
      for (Json::ArrayIndex i = 0; i < value.size() && i < 1000; ++i) {
60
120k
        TraverseValue(value[i], depth + 1);
61
120k
      }
62
33.0k
      break;
63
14.4k
    case Json::objectValue:
64
41.6k
      for (auto const& name : value.getMemberNames()) {
65
41.6k
        (void)name;
66
41.6k
        TraverseValue(value[name], depth + 1);
67
41.6k
      }
68
14.4k
      break;
69
167k
  }
70
167k
}
71
72
extern "C" int LLVMFuzzerTestOneInput(uint8_t const* data, size_t size)
73
3.52k
{
74
3.52k
  if (size == 0 || size > kMaxInputSize) {
75
6
    return 0;
76
6
  }
77
78
3.51k
  std::string input(reinterpret_cast<char const*>(data), size);
79
80
3.51k
  {
81
3.51k
    Json::Value root;
82
3.51k
    std::istringstream stream(input);
83
84
    // Try parsing with default settings
85
3.51k
    cmJSONState parseState(stream, &root, cmJSONState::StrictMode::Relaxed);
86
3.51k
    if (!parseState.errors.empty()) {
87
      // Traverse the parsed structure
88
2.64k
      TraverseValue(root);
89
2.64k
    }
90
3.51k
  }
91
92
3.51k
  {
93
3.51k
    Json::Value root;
94
3.51k
    std::istringstream stream(input);
95
96
    // Also try with strict mode
97
3.51k
    cmJSONState parseState(stream, &root, cmJSONState::StrictMode::Strict);
98
3.51k
    if (!parseState.errors.empty()) {
99
      // Traverse the parsed structure
100
3.48k
      TraverseValue(root);
101
3.48k
    }
102
3.51k
  }
103
104
3.51k
  return 0;
105
3.52k
}