Coverage Report

Created: 2026-07-25 06:52

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/ogre/Tests/fuzz/ogre_deep_fuzz.cpp
Line
Count
Source
1
/* Copyright 2026 Google LLC
2
Licensed under the Apache License, Version 2.0 (the "License");
3
you may not use this file except in compliance with the License.
4
You may obtain a copy of the License at
5
      http://www.apache.org/licenses/LICENSE-2.0
6
Unless required by applicable law or agreed to in writing, software
7
distributed under the License is distributed on an "AS IS" BASIS,
8
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9
See the License for the specific language governing permissions and
10
limitations under the License.
11
*/
12
13
/*
14
 * Multi-target fuzzer for Ogre3D covering:
15
 * 1. Mesh binary format deserialization (7 format versions)
16
 * 2. Skeleton binary format deserialization
17
 * 3. Script lexer and parser pipeline
18
 * 4. ConfigFile parsing
19
 * 5. StreamSerialiser chunk reading (extended)
20
 *
21
 * Uses FuzzedDataProvider to select which target to exercise per input,
22
 * maximizing code coverage across the Ogre codebase.
23
 */
24
25
#include <stdint.h>
26
#include <stdlib.h>
27
#include <string.h>
28
29
#include <fuzzer/FuzzedDataProvider.h>
30
#include <string>
31
32
#include "OgreConfigFile.h"
33
#include "OgreDataStream.h"
34
#include "OgreDefaultHardwareBufferManager.h"
35
#include "OgreException.h"
36
#include "OgreLodStrategyManager.h"
37
#include "OgreLogManager.h"
38
#include "OgreMaterialManager.h"
39
#include "OgreMesh.h"
40
#include "OgreMeshManager.h"
41
#include "OgreMeshSerializer.h"
42
#include "OgreSkeleton.h"
43
#include "OgreSkeletonManager.h"
44
#include "OgreSkeletonSerializer.h"
45
#include "OgreStreamSerialiser.h"
46
47
enum FuzzTarget {
48
  FUZZ_MESH = 0,
49
  FUZZ_SKELETON,
50
  FUZZ_CONFIG,
51
  FUZZ_STREAM_SERIALISER,
52
  FUZZ_TARGET_COUNT
53
};
54
55
static bool g_initialized = false;
56
57
7.37k
static void global_init() {
58
7.37k
  if (g_initialized)
59
7.37k
    return;
60
61
  /* Suppress all log output */
62
1
  auto *logMgr = new Ogre::LogManager();
63
1
  logMgr->createLog("fuzz.log", true, false, true); /* suppressFileOutput */
64
1
  logMgr->setMinLogLevel(Ogre::LML_CRITICAL);
65
66
  /* Create singletons needed for Mesh/Skeleton operations */
67
1
  new Ogre::ResourceGroupManager();
68
1
  new Ogre::LodStrategyManager();
69
1
  new Ogre::DefaultHardwareBufferManager();
70
1
  new Ogre::MeshManager();
71
1
  new Ogre::SkeletonManager();
72
1
  auto *matMgr = new Ogre::MaterialManager();
73
1
  matMgr->initialise();
74
75
1
  g_initialized = true;
76
1
}
77
78
4.81k
static void fuzz_mesh(const uint8_t *data, size_t size) {
79
  // manual, so we can load it from memory without a file
80
4.81k
  auto mesh = Ogre::MeshManager::getSingleton().create("fuzz.mesh", "General", true);
81
4.81k
  mesh->load(); // ensure cleanup runs
82
83
4.81k
  Ogre::DataStreamPtr stream(
84
4.81k
      new Ogre::MemoryDataStream(const_cast<uint8_t *>(data), size, false, true));
85
86
4.81k
  Ogre::MeshSerializer serializer;
87
4.81k
  try {
88
4.81k
    serializer.importMesh(stream, mesh.get());
89
4.81k
  } catch (std::exception &) {
90
3.09k
  }
91
92
4.81k
  Ogre::MeshManager::getSingleton().remove(mesh);
93
4.81k
}
94
95
1.22k
static void fuzz_skeleton(const uint8_t *data, size_t size) {
96
  // manual, so we can load it from memory without a file
97
1.22k
  auto skeleton = Ogre::SkeletonManager::getSingleton().create("fuzz.skeleton", "General", true);
98
1.22k
  skeleton->prepare();   // ensure cleanup runs
99
100
1.22k
  Ogre::DataStreamPtr stream(
101
1.22k
      new Ogre::MemoryDataStream(const_cast<uint8_t *>(data), size, false, true));
102
103
1.22k
  Ogre::SkeletonSerializer serializer;
104
1.22k
  try {
105
1.22k
    serializer.importSkeleton(stream, skeleton.get());
106
1.22k
  } catch (std::exception &) {
107
639
  }
108
109
1.22k
  Ogre::SkeletonManager::getSingleton().remove(skeleton);
110
1.22k
}
111
112
953
static void fuzz_config(const uint8_t *data, size_t size) {
113
953
  Ogre::DataStreamPtr stream(new Ogre::MemoryDataStream(
114
953
      "fuzz.cfg", const_cast<uint8_t *>(data), size, false, true));
115
116
953
  Ogre::ConfigFile cf;
117
953
  try {
118
953
    cf.load(stream);
119
120
    /* Exercise iteration over parsed sections and settings */
121
6.37k
    for (auto &sec : cf.getSettingsBySection()) {
122
23.5k
      for (auto &kv : sec.second) {
123
23.5k
        volatile const char *k = kv.first.c_str();
124
23.5k
        volatile const char *v = kv.second.c_str();
125
23.5k
        (void)k;
126
23.5k
        (void)v;
127
23.5k
      }
128
6.37k
    }
129
953
  } catch (Ogre::Exception &) {
130
0
  } catch (std::exception &) {
131
0
  }
132
953
}
133
134
387
static void fuzz_stream_serialiser(const uint8_t *data, size_t size) {
135
387
  Ogre::DataStreamPtr stream(
136
387
      new Ogre::MemoryDataStream(const_cast<uint8_t *>(data), size, false, true));
137
138
387
  try {
139
387
    Ogre::StreamSerialiser serialiser(stream);
140
141
    /* Try to read multiple chunks, exercising the chunk state machine */
142
951
    for (int i = 0; i < 32; i++) {
143
949
      const Ogre::StreamSerialiser::Chunk *c = serialiser.readChunkBegin();
144
949
      if (!c)
145
0
        break;
146
147
      /* Try reading various data types from the chunk */
148
949
      Ogre::String str;
149
949
      try { serialiser.read(&str); } catch (...) {}
150
151
949
      Ogre::Real r;
152
564
      try { serialiser.read(&r); } catch (...) {}
153
154
564
      Ogre::Vector3 v3;
155
564
      try { serialiser.read(&v3); } catch (...) {}
156
157
564
      Ogre::Vector4 v4;
158
564
      try { serialiser.read(&v4); } catch (...) {}
159
160
564
      serialiser.readChunkEnd(c->id);
161
564
    }
162
387
  } catch (Ogre::Exception &) {
163
385
  } catch (std::exception &) {
164
0
  }
165
387
}
166
167
7.37k
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
168
7.37k
  if (size < 2)
169
1
    return 0;
170
171
7.37k
  global_init();
172
173
7.37k
  FuzzedDataProvider provider(data, size);
174
7.37k
  uint8_t target = provider.ConsumeIntegral<uint8_t>() % FUZZ_TARGET_COUNT;
175
7.37k
  auto remaining = provider.ConsumeRemainingBytes<uint8_t>();
176
177
7.37k
  if (remaining.empty())
178
0
    return 0;
179
180
7.37k
  switch (target) {
181
4.81k
  case FUZZ_MESH:
182
4.81k
    fuzz_mesh(remaining.data(), remaining.size());
183
4.81k
    break;
184
1.22k
  case FUZZ_SKELETON:
185
1.22k
    fuzz_skeleton(remaining.data(), remaining.size());
186
1.22k
    break;
187
953
  case FUZZ_CONFIG:
188
953
    fuzz_config(remaining.data(), remaining.size());
189
953
    break;
190
387
  case FUZZ_STREAM_SERIALISER:
191
387
    fuzz_stream_serialiser(remaining.data(), remaining.size());
192
387
    break;
193
7.37k
  }
194
195
7.37k
  return 0;
196
7.37k
}