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 | 3.62k | static void global_init() { |
58 | 3.62k | if (g_initialized) |
59 | 3.62k | 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 | 1.82k | static void fuzz_mesh(const uint8_t *data, size_t size) { |
79 | 1.82k | Ogre::MeshPtr mesh = |
80 | 1.82k | Ogre::MeshManager::getSingleton().create("fuzz.mesh", "General"); |
81 | | |
82 | 1.82k | Ogre::DataStreamPtr stream( |
83 | 1.82k | new Ogre::MemoryDataStream(const_cast<uint8_t *>(data), size, false, true)); |
84 | | |
85 | 1.82k | Ogre::MeshSerializer serializer; |
86 | 1.82k | try { |
87 | 1.82k | serializer.importMesh(stream, mesh.get()); |
88 | 1.82k | } catch (Ogre::Exception &) { |
89 | 533 | } catch (std::exception &) { |
90 | 8 | } |
91 | | |
92 | 1.82k | Ogre::MeshManager::getSingleton().remove(mesh); |
93 | 1.82k | } |
94 | | |
95 | 734 | static void fuzz_skeleton(const uint8_t *data, size_t size) { |
96 | 734 | Ogre::SkeletonPtr skeleton = |
97 | 734 | Ogre::SkeletonManager::getSingleton().create("fuzz.skeleton", "General"); |
98 | | |
99 | 734 | Ogre::DataStreamPtr stream( |
100 | 734 | new Ogre::MemoryDataStream(const_cast<uint8_t *>(data), size, false, true)); |
101 | | |
102 | 734 | Ogre::SkeletonSerializer serializer; |
103 | 734 | try { |
104 | 734 | serializer.importSkeleton(stream, skeleton.get()); |
105 | 734 | } catch (Ogre::Exception &) { |
106 | 163 | } catch (std::exception &) { |
107 | 0 | } |
108 | | |
109 | 734 | Ogre::SkeletonManager::getSingleton().remove(skeleton); |
110 | 734 | } |
111 | | |
112 | 936 | static void fuzz_config(const uint8_t *data, size_t size) { |
113 | 936 | Ogre::DataStreamPtr stream(new Ogre::MemoryDataStream( |
114 | 936 | "fuzz.cfg", const_cast<uint8_t *>(data), size, false, true)); |
115 | | |
116 | 936 | Ogre::ConfigFile cf; |
117 | 936 | try { |
118 | 936 | cf.load(stream); |
119 | | |
120 | | /* Exercise iteration over parsed sections and settings */ |
121 | 3.29k | for (auto &sec : cf.getSettingsBySection()) { |
122 | 17.0k | for (auto &kv : sec.second) { |
123 | 17.0k | volatile const char *k = kv.first.c_str(); |
124 | 17.0k | volatile const char *v = kv.second.c_str(); |
125 | 17.0k | (void)k; |
126 | 17.0k | (void)v; |
127 | 17.0k | } |
128 | 3.29k | } |
129 | 936 | } catch (Ogre::Exception &) { |
130 | 0 | } catch (std::exception &) { |
131 | 0 | } |
132 | 936 | } |
133 | | |
134 | 137 | static void fuzz_stream_serialiser(const uint8_t *data, size_t size) { |
135 | 137 | Ogre::DataStreamPtr stream( |
136 | 137 | new Ogre::MemoryDataStream(const_cast<uint8_t *>(data), size, false, true)); |
137 | | |
138 | 137 | try { |
139 | 137 | Ogre::StreamSerialiser serialiser(stream); |
140 | | |
141 | | /* Try to read multiple chunks, exercising the chunk state machine */ |
142 | 137 | for (int i = 0; i < 32; i++) { |
143 | 137 | const Ogre::StreamSerialiser::Chunk *c = serialiser.readChunkBegin(); |
144 | 137 | if (!c) |
145 | 0 | break; |
146 | | |
147 | | /* Try reading various data types from the chunk */ |
148 | 137 | Ogre::String str; |
149 | 137 | try { serialiser.read(&str); } catch (...) {} |
150 | | |
151 | 137 | Ogre::Real r; |
152 | 0 | try { serialiser.read(&r); } catch (...) {} |
153 | |
|
154 | 0 | Ogre::Vector3 v3; |
155 | 0 | try { serialiser.read(&v3); } catch (...) {} |
156 | |
|
157 | 0 | Ogre::Vector4 v4; |
158 | 0 | try { serialiser.read(&v4); } catch (...) {} |
159 | |
|
160 | 0 | serialiser.readChunkEnd(c->id); |
161 | 0 | } |
162 | 137 | } catch (Ogre::Exception &) { |
163 | 137 | } catch (std::exception &) { |
164 | 0 | } |
165 | 137 | } |
166 | | |
167 | 3.63k | extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { |
168 | 3.63k | if (size < 2) |
169 | 1 | return 0; |
170 | | |
171 | 3.62k | global_init(); |
172 | | |
173 | 3.62k | FuzzedDataProvider provider(data, size); |
174 | 3.62k | uint8_t target = provider.ConsumeIntegral<uint8_t>() % FUZZ_TARGET_COUNT; |
175 | 3.62k | auto remaining = provider.ConsumeRemainingBytes<uint8_t>(); |
176 | | |
177 | 3.62k | if (remaining.empty()) |
178 | 0 | return 0; |
179 | | |
180 | 3.62k | switch (target) { |
181 | 1.82k | case FUZZ_MESH: |
182 | 1.82k | fuzz_mesh(remaining.data(), remaining.size()); |
183 | 1.82k | break; |
184 | 734 | case FUZZ_SKELETON: |
185 | 734 | fuzz_skeleton(remaining.data(), remaining.size()); |
186 | 734 | break; |
187 | 936 | case FUZZ_CONFIG: |
188 | 936 | fuzz_config(remaining.data(), remaining.size()); |
189 | 936 | break; |
190 | 137 | case FUZZ_STREAM_SERIALISER: |
191 | 137 | fuzz_stream_serialiser(remaining.data(), remaining.size()); |
192 | 137 | break; |
193 | 3.62k | } |
194 | | |
195 | 3.62k | return 0; |
196 | 3.62k | } |