/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 | } |