Coverage Report

Created: 2026-02-26 06:18

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/meshoptimizer/tools/codecfuzz.cpp
Line
Count
Source
1
#include "../src/meshoptimizer.h"
2
3
#include <stdint.h>
4
#include <stdlib.h>
5
#include <string.h>
6
7
void fuzzDecoder(const uint8_t* data, size_t size, size_t stride, int (*decode)(void*, size_t, size_t, const unsigned char*, size_t))
8
16.8k
{
9
16.8k
  size_t count = 66; // must be divisible by 3 for decodeIndexBuffer; should be >=64 to cover large vertex blocks
10
11
16.8k
  void* destination = malloc(count * stride);
12
16.8k
  assert(destination);
13
14
16.8k
  int rc = decode(destination, count, stride, reinterpret_cast<const unsigned char*>(data), size);
15
16.8k
  (void)rc;
16
17
16.8k
  free(destination);
18
16.8k
}
19
20
void fuzzRoundtrip(const uint8_t* data, size_t size, size_t stride, int level)
21
8.42k
{
22
8.42k
  size_t count = size / stride;
23
24
8.42k
  size_t bound = meshopt_encodeVertexBufferBound(count, stride);
25
8.42k
  void* encoded = malloc(bound);
26
8.42k
  void* decoded = malloc(count * stride);
27
8.42k
  assert(encoded && decoded);
28
29
8.42k
  size_t res = meshopt_encodeVertexBufferLevel(static_cast<unsigned char*>(encoded), bound, data, count, stride, level, -1);
30
8.42k
  assert(res > 0 && res <= bound);
31
32
  // encode again at the boundary to check for memory safety
33
  // this should produce the same output because encoder is deterministic
34
8.42k
  size_t rese = meshopt_encodeVertexBufferLevel(static_cast<unsigned char*>(encoded) + bound - res, res, data, count, stride, level, -1);
35
8.42k
  assert(rese == res);
36
37
8.42k
  int rc = meshopt_decodeVertexBuffer(decoded, count, stride, static_cast<unsigned char*>(encoded) + bound - res, res);
38
8.42k
  assert(rc == 0);
39
40
8.42k
  assert(memcmp(data, decoded, count * stride) == 0);
41
42
8.42k
  free(decoded);
43
8.42k
  free(encoded);
44
8.42k
}
45
46
size_t align(size_t value, size_t alignment)
47
12.5k
{
48
12.5k
  return (value + alignment - 1) & ~(alignment - 1);
49
12.5k
}
50
51
void fuzzDecodeMeshlet(size_t vertex_count, size_t triangle_count, const unsigned char* data, size_t size)
52
2.09k
{
53
  // raw decoding: allowed to write align(count, 4) elements
54
2.09k
  unsigned int rt[256];
55
2.09k
  unsigned int rv[256];
56
2.09k
  meshopt_decodeMeshletRaw(rv + 256 - align(vertex_count, 4), vertex_count, rt + 256 - align(triangle_count, 4), triangle_count, data, size);
57
58
  // regular decoding: allowed to write align(count * size, 4) bytes
59
  // with variations for 3-byte triangles and 2-byte vertex references
60
2.09k
  unsigned short rsv[256];
61
2.09k
  unsigned char rbt[256 * 3];
62
63
2.09k
  meshopt_decodeMeshlet(rv + 256 - vertex_count, vertex_count, 4, rt + 256 - triangle_count, triangle_count, 4, data, size);
64
2.09k
  meshopt_decodeMeshlet(rsv + 256 - align(vertex_count, 2), vertex_count, 2, rt + 256 - triangle_count, triangle_count, 4, data, size);
65
2.09k
  meshopt_decodeMeshlet(rv + 256 - vertex_count, vertex_count, 4, rbt + 256 * 3 - align(triangle_count * 3, 4), triangle_count, 3, data, size);
66
2.09k
  meshopt_decodeMeshlet(rsv + 256 - align(vertex_count, 2), vertex_count, 2, rbt + 256 * 3 - align(triangle_count * 3, 4), triangle_count, 3, data, size);
67
2.09k
}
68
69
void fuzzRoundtripMeshlet(const uint8_t* data, size_t size)
70
2.10k
{
71
2.10k
  size_t triangle_count = size / 3;
72
2.10k
  if (triangle_count > 256)
73
501
    triangle_count = 256;
74
75
2.10k
  unsigned char buf[4096];
76
2.10k
  size_t enc = meshopt_encodeMeshlet(buf, sizeof(buf), NULL, 0, reinterpret_cast<const unsigned char*>(data), triangle_count);
77
2.10k
  assert(enc > 0);
78
2.10k
  assert(enc <= meshopt_encodeMeshletBound(0, triangle_count));
79
80
2.10k
  unsigned int rt4[256];
81
2.10k
  int rc4 = meshopt_decodeMeshlet(static_cast<unsigned int*>(NULL), 0, rt4, triangle_count, buf, enc);
82
2.10k
  assert(rc4 == 0);
83
84
178k
  for (size_t i = 0; i < triangle_count; ++i)
85
176k
  {
86
176k
    unsigned char a = data[i * 3 + 0], b = data[i * 3 + 1], c = data[i * 3 + 2];
87
88
176k
    unsigned int abc = (a << 0) | (b << 8) | (c << 16);
89
176k
    unsigned int bca = (b << 0) | (c << 8) | (a << 16);
90
176k
    unsigned int cba = (c << 0) | (a << 8) | (b << 16);
91
92
176k
    unsigned int tri = rt4[i];
93
94
176k
    assert(tri == abc || tri == bca || tri == cba);
95
176k
  }
96
97
2.10k
  unsigned char rt3[256 * 3];
98
2.10k
  int rc3 = meshopt_decodeMeshlet(static_cast<unsigned int*>(NULL), 0, rt3, triangle_count, buf, enc);
99
2.10k
  assert(rc3 == 0);
100
101
178k
  for (size_t i = 0; i < triangle_count; ++i)
102
176k
  {
103
176k
    unsigned char a = data[i * 3 + 0], b = data[i * 3 + 1], c = data[i * 3 + 2];
104
105
176k
    unsigned int abc = (a << 0) | (b << 8) | (c << 16);
106
176k
    unsigned int bca = (b << 0) | (c << 8) | (a << 16);
107
176k
    unsigned int cba = (c << 0) | (a << 8) | (b << 16);
108
109
176k
    unsigned int tri = rt3[i * 3 + 0] | (rt3[i * 3 + 1] << 8) | (rt3[i * 3 + 2] << 16);
110
111
176k
    assert(tri == abc || tri == bca || tri == cba);
112
176k
  }
113
2.10k
}
114
115
void fuzzRoundtripMeshletV(const uint8_t* data, size_t size)
116
2.10k
{
117
2.10k
  size_t vertex_count = size / 4;
118
2.10k
  if (vertex_count > 256)
119
449
    vertex_count = 256;
120
121
2.10k
  unsigned char tri[4] = {0, 1, 2};
122
123
2.10k
  unsigned char buf[4096];
124
2.10k
  size_t enc = meshopt_encodeMeshlet(buf, sizeof(buf), reinterpret_cast<const uint32_t*>(data), vertex_count, tri, 1);
125
2.10k
  assert(enc > 0);
126
2.10k
  assert(enc <= meshopt_encodeMeshletBound(vertex_count, 1));
127
128
2.10k
  unsigned int rv4[256];
129
2.10k
  int rc4 = meshopt_decodeMeshlet(rv4, vertex_count, tri, 1, buf, enc);
130
2.10k
  assert(rc4 == 0);
131
132
165k
  for (size_t i = 0; i < vertex_count; ++i)
133
163k
    assert(rv4[i] == reinterpret_cast<const uint32_t*>(data)[i]);
134
135
2.10k
  unsigned short rv2[256];
136
2.10k
  int rc2 = meshopt_decodeMeshlet(rv2, vertex_count, tri, 1, buf, enc);
137
2.10k
  assert(rc2 == 0);
138
139
165k
  for (size_t i = 0; i < vertex_count; ++i)
140
163k
    assert(rv2[i] == uint16_t(reinterpret_cast<const uint32_t*>(data)[i]));
141
2.10k
}
142
143
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size)
144
2.10k
{
145
  // decodeIndexBuffer supports 2 and 4-byte indices
146
2.10k
  fuzzDecoder(data, size, 2, meshopt_decodeIndexBuffer);
147
2.10k
  fuzzDecoder(data, size, 4, meshopt_decodeIndexBuffer);
148
149
  // decodeIndexSequence supports 2 and 4-byte indices
150
2.10k
  fuzzDecoder(data, size, 2, meshopt_decodeIndexSequence);
151
2.10k
  fuzzDecoder(data, size, 4, meshopt_decodeIndexSequence);
152
153
  // decodeVertexBuffer supports any strides divisible by 4 in 4-256 interval
154
  // it's a waste of time to check all of them, so we'll just check a few with different alignment mod 16
155
2.10k
  fuzzDecoder(data, size, 4, meshopt_decodeVertexBuffer);
156
2.10k
  fuzzDecoder(data, size, 16, meshopt_decodeVertexBuffer);
157
2.10k
  fuzzDecoder(data, size, 24, meshopt_decodeVertexBuffer);
158
2.10k
  fuzzDecoder(data, size, 32, meshopt_decodeVertexBuffer);
159
160
  // encodeVertexBuffer/decodeVertexBuffer should roundtrip for any stride, check a few with different alignment mod 16
161
  // this also checks memory safety properties of the encoder
162
  // to conserve time, we only check one version/level combination, biased towards version 1
163
2.10k
  uint8_t data0 = size > 0 ? data[0] : 0;
164
2.10k
  int level = data0 % 5;
165
166
2.10k
  meshopt_encodeVertexVersion(level < 4 ? 1 : 0);
167
168
2.10k
  fuzzRoundtrip(data, size, 4, level);
169
2.10k
  fuzzRoundtrip(data, size, 16, level);
170
2.10k
  fuzzRoundtrip(data, size, 24, level);
171
2.10k
  fuzzRoundtrip(data, size, 32, level);
172
173
  // validate that decodeMeshlet works on untrusted data and is memory safe within documented limits
174
2.10k
  if (size > 2)
175
2.09k
    fuzzDecodeMeshlet(data[0] + 1, data[1] + 1, reinterpret_cast<const unsigned char*>(data + 2), size - 2);
176
177
  // validate that index data roundtrips in meshlet encoding modulo rotation
178
2.10k
  fuzzRoundtripMeshlet(data, size);
179
180
  // validate that vertex data roundtrips in meshlet encoding
181
2.10k
  fuzzRoundtripMeshletV(data, size);
182
183
2.10k
  return 0;
184
2.10k
}