Coverage Report

Created: 2026-10-28 06:38

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
181k
{
34
  // Prevent stack overflow on deeply nested structures
35
181k
  if (depth > 100) {
36
727
    return;
37
727
  }
38
39
180k
  switch (value.type()) {
40
3.88k
    case Json::nullValue:
41
3.88k
      (void)value.isNull();
42
3.88k
      break;
43
119k
    case Json::intValue:
44
119k
      (void)value.asInt64();
45
119k
      break;
46
442
    case Json::uintValue:
47
442
      (void)value.asUInt64();
48
442
      break;
49
2.80k
    case Json::realValue:
50
2.80k
      (void)value.asDouble();
51
2.80k
      break;
52
1.30k
    case Json::stringValue:
53
1.30k
      (void)value.asString();
54
1.30k
      break;
55
3.22k
    case Json::booleanValue:
56
3.22k
      (void)value.asBool();
57
3.22k
      break;
58
33.6k
    case Json::arrayValue:
59
165k
      for (Json::ArrayIndex i = 0; i < value.size() && i < 1000; ++i) {
60
131k
        TraverseValue(value[i], depth + 1);
61
131k
      }
62
33.6k
      break;
63
15.3k
    case Json::objectValue:
64
43.0k
      for (auto const& name : value.getMemberNames()) {
65
43.0k
        (void)name;
66
43.0k
        TraverseValue(value[name], depth + 1);
67
43.0k
      }
68
15.3k
      break;
69
180k
  }
70
180k
}
71
72
extern "C" int LLVMFuzzerTestOneInput(uint8_t const* data, size_t size)
73
3.58k
{
74
3.58k
  if (size == 0 || size > kMaxInputSize) {
75
6
    return 0;
76
6
  }
77
78
3.57k
  std::string input(reinterpret_cast<char const*>(data), size);
79
80
3.57k
  {
81
3.57k
    Json::Value root;
82
3.57k
    std::istringstream stream(input);
83
84
    // Try parsing with default settings
85
3.57k
    cmJSONState parseState(stream, &root, cmJSONState::StrictMode::Relaxed);
86
3.57k
    if (!parseState.errors.empty()) {
87
      // Traverse the parsed structure
88
2.72k
      TraverseValue(root);
89
2.72k
    }
90
3.57k
  }
91
92
3.57k
  {
93
3.57k
    Json::Value root;
94
3.57k
    std::istringstream stream(input);
95
96
    // Also try with strict mode
97
3.57k
    cmJSONState parseState(stream, &root, cmJSONState::StrictMode::Strict);
98
3.57k
    if (!parseState.errors.empty()) {
99
      // Traverse the parsed structure
100
3.54k
      TraverseValue(root);
101
3.54k
    }
102
3.57k
  }
103
104
3.57k
  return 0;
105
3.58k
}