/src/FreeRDP/libfreerdp/codec/test/TestFuzzCodecs.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* https://github.com/ergnoorr/fuzzrdp |
2 | | * |
3 | | * MIT License |
4 | | * |
5 | | * Copyright (c) 2024 ergnoorr |
6 | | * |
7 | | * Permission is hereby granted, free of charge, to any person obtaining a copy |
8 | | * of this software and associated documentation files (the "Software"), to deal |
9 | | * in the Software without restriction, including without limitation the rights |
10 | | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
11 | | * copies of the Software, and to permit persons to whom the Software is |
12 | | * furnished to do so, subject to the following conditions: |
13 | | * |
14 | | * The above copyright notice and this permission notice shall be included in all |
15 | | * copies or substantial portions of the Software. |
16 | | * |
17 | | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
18 | | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
19 | | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
20 | | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
21 | | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
22 | | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
23 | | * SOFTWARE. |
24 | | */ |
25 | | #include <freerdp/assistance.h> |
26 | | |
27 | | #include <winpr/crt.h> |
28 | | #include <winpr/print.h> |
29 | | #include <winpr/platform.h> |
30 | | #include <freerdp/codec/interleaved.h> |
31 | | #include <freerdp/codec/planar.h> |
32 | | #include <freerdp/codec/bulk.h> |
33 | | #include <freerdp/codec/clear.h> |
34 | | #include <freerdp/codec/zgfx.h> |
35 | | #include <freerdp/log.h> |
36 | | #include <winpr/bitstream.h> |
37 | | #include <freerdp/codec/rfx.h> |
38 | | #include <freerdp/codec/progressive.h> |
39 | | |
40 | | #include <freerdp/freerdp.h> |
41 | | #include <freerdp/gdi/gdi.h> |
42 | | |
43 | | #include "../progressive.h" |
44 | | #include "../mppc.h" |
45 | | #include "../xcrush.h" |
46 | | #include "../ncrush.h" |
47 | | |
48 | | static BOOL test_ClearDecompressExample(UINT32 nr, UINT32 width, UINT32 height, |
49 | | const BYTE* pSrcData, const UINT32 SrcSize) |
50 | 0 | { |
51 | 0 | BOOL rc = FALSE; |
52 | 0 | int status = 0; |
53 | 0 | BYTE* pDstData = calloc(1ull * width * height, 4); |
54 | 0 | CLEAR_CONTEXT* clear = clear_context_new(FALSE); |
55 | |
|
56 | 0 | WINPR_UNUSED(nr); |
57 | 0 | if (!clear || !pDstData) |
58 | 0 | goto fail; |
59 | | |
60 | 0 | status = clear_decompress(clear, pSrcData, SrcSize, width, height, pDstData, |
61 | 0 | PIXEL_FORMAT_XRGB32, 0, 0, 0, width, height, NULL); |
62 | | // printf("clear_decompress example %" PRIu32 " status: %d\n", nr, status); |
63 | | // fflush(stdout); |
64 | 0 | rc = (status == 0); |
65 | 0 | fail: |
66 | 0 | clear_context_free(clear); |
67 | 0 | free(pDstData); |
68 | 0 | return rc; |
69 | 0 | } |
70 | | |
71 | | static int TestFreeRDPCodecClear(const uint8_t* Data, size_t Size) |
72 | 0 | { |
73 | 0 | if (Size > UINT32_MAX) |
74 | 0 | return -1; |
75 | 0 | test_ClearDecompressExample(2, 78, 17, Data, (UINT32)Size); |
76 | 0 | test_ClearDecompressExample(3, 64, 24, Data, (UINT32)Size); |
77 | 0 | test_ClearDecompressExample(4, 7, 15, Data, (UINT32)Size); |
78 | 0 | return 0; |
79 | 0 | } |
80 | | |
81 | | static int TestFreeRDPCodecXCrush(const uint8_t* Data, size_t Size) |
82 | 0 | { |
83 | 0 | if (Size > UINT32_MAX) |
84 | 0 | return -1; |
85 | | |
86 | 0 | const BYTE* OutputBuffer = NULL; |
87 | 0 | UINT32 DstSize = 0; |
88 | 0 | XCRUSH_CONTEXT* xcrush = xcrush_context_new(TRUE); |
89 | 0 | if (!xcrush) |
90 | 0 | return 0; |
91 | 0 | xcrush_decompress(xcrush, Data, (UINT32)Size, &OutputBuffer, &DstSize, 0); |
92 | 0 | xcrush_context_free(xcrush); |
93 | 0 | return 0; |
94 | 0 | } |
95 | | |
96 | | static int test_ZGfxDecompressFoxSingle(const uint8_t* Data, size_t Size) |
97 | 0 | { |
98 | 0 | if (Size > UINT32_MAX) |
99 | 0 | return -1; |
100 | 0 | int rc = -1; |
101 | 0 | int status = 0; |
102 | 0 | UINT32 Flags = 0; |
103 | 0 | const BYTE* pSrcData = (const BYTE*)Data; |
104 | 0 | UINT32 SrcSize = (UINT32)Size; |
105 | 0 | UINT32 DstSize = 0; |
106 | 0 | BYTE* pDstData = NULL; |
107 | 0 | ZGFX_CONTEXT* zgfx = zgfx_context_new(TRUE); |
108 | |
|
109 | 0 | if (!zgfx) |
110 | 0 | return -1; |
111 | | |
112 | 0 | status = zgfx_decompress(zgfx, pSrcData, SrcSize, &pDstData, &DstSize, Flags); |
113 | 0 | if (status < 0) |
114 | 0 | goto fail; |
115 | | |
116 | 0 | rc = 0; |
117 | 0 | fail: |
118 | 0 | free(pDstData); |
119 | 0 | zgfx_context_free(zgfx); |
120 | 0 | return rc; |
121 | 0 | } |
122 | | |
123 | | static int TestFreeRDPCodecZGfx(const uint8_t* Data, size_t Size) |
124 | 0 | { |
125 | 0 | test_ZGfxDecompressFoxSingle(Data, Size); |
126 | 0 | return 0; |
127 | 0 | } |
128 | | |
129 | | static BOOL test_NCrushDecompressBells(const uint8_t* Data, size_t Size) |
130 | 0 | { |
131 | 0 | if (Size > UINT32_MAX) |
132 | 0 | return FALSE; |
133 | | |
134 | 0 | BOOL rc = FALSE; |
135 | 0 | int status = 0; |
136 | 0 | UINT32 Flags = PACKET_COMPRESSED | 2; |
137 | 0 | const BYTE* pSrcData = (const BYTE*)Data; |
138 | 0 | UINT32 SrcSize = (UINT32)Size; |
139 | 0 | UINT32 DstSize = 0; |
140 | 0 | const BYTE* pDstData = NULL; |
141 | 0 | NCRUSH_CONTEXT* ncrush = ncrush_context_new(FALSE); |
142 | |
|
143 | 0 | if (!ncrush) |
144 | 0 | return rc; |
145 | | |
146 | 0 | status = ncrush_decompress(ncrush, pSrcData, SrcSize, &pDstData, &DstSize, Flags); |
147 | 0 | if (status < 0) |
148 | 0 | goto fail; |
149 | | |
150 | 0 | rc = TRUE; |
151 | 0 | fail: |
152 | 0 | ncrush_context_free(ncrush); |
153 | 0 | return rc; |
154 | 0 | } |
155 | | |
156 | | static int TestFreeRDPCodecNCrush(const uint8_t* Data, size_t Size) |
157 | 0 | { |
158 | 0 | test_NCrushDecompressBells(Data, Size); |
159 | 0 | return 0; |
160 | 0 | } |
161 | | |
162 | | static const size_t IMG_WIDTH = 64; |
163 | | static const size_t IMG_HEIGHT = 64; |
164 | | static const size_t FORMAT_SIZE = 4; |
165 | | static const UINT32 FORMAT = PIXEL_FORMAT_XRGB32; |
166 | | |
167 | | static int TestFreeRDPCodecRemoteFX(const uint8_t* Data, size_t Size) |
168 | 0 | { |
169 | 0 | int rc = -1; |
170 | 0 | REGION16 region = { 0 }; |
171 | 0 | RFX_CONTEXT* context = rfx_context_new(FALSE); |
172 | 0 | BYTE* dest = calloc(IMG_WIDTH * IMG_HEIGHT, FORMAT_SIZE); |
173 | 0 | size_t stride = FORMAT_SIZE * IMG_WIDTH; |
174 | 0 | if (!context) |
175 | 0 | goto fail; |
176 | 0 | if (Size > UINT32_MAX) |
177 | 0 | goto fail; |
178 | 0 | if (stride > UINT32_MAX) |
179 | 0 | goto fail; |
180 | 0 | if (!dest) |
181 | 0 | goto fail; |
182 | | |
183 | 0 | region16_init(®ion); |
184 | 0 | if (!rfx_process_message(context, Data, (UINT32)Size, 0, 0, dest, FORMAT, (UINT32)stride, |
185 | 0 | IMG_HEIGHT, ®ion)) |
186 | 0 | goto fail; |
187 | | |
188 | 0 | region16_clear(®ion); |
189 | 0 | if (!rfx_process_message(context, Data, (UINT32)Size, 0, 0, dest, FORMAT, (UINT32)stride, |
190 | 0 | IMG_HEIGHT, ®ion)) |
191 | 0 | goto fail; |
192 | 0 | region16_print(®ion); |
193 | |
|
194 | 0 | rc = 0; |
195 | 0 | fail: |
196 | 0 | region16_uninit(®ion); |
197 | 0 | rfx_context_free(context); |
198 | 0 | free(dest); |
199 | 0 | return rc; |
200 | 0 | } |
201 | | |
202 | | static int test_MppcDecompressBellsRdp5(const uint8_t* Data, size_t Size) |
203 | 0 | { |
204 | 0 | int rc = -1; |
205 | 0 | int status = 0; |
206 | 0 | UINT32 Flags = PACKET_AT_FRONT | PACKET_COMPRESSED | 1; |
207 | 0 | const BYTE* pSrcData = Data; |
208 | 0 | UINT32 SrcSize = (UINT32)Size; |
209 | 0 | UINT32 DstSize = 0; |
210 | 0 | const BYTE* pDstData = NULL; |
211 | 0 | MPPC_CONTEXT* mppc = mppc_context_new(1, FALSE); |
212 | |
|
213 | 0 | if (!mppc) |
214 | 0 | return -1; |
215 | | |
216 | 0 | status = mppc_decompress(mppc, pSrcData, SrcSize, &pDstData, &DstSize, Flags); |
217 | |
|
218 | 0 | if (status < 0) |
219 | 0 | goto fail; |
220 | | |
221 | 0 | rc = 0; |
222 | |
|
223 | 0 | fail: |
224 | 0 | mppc_context_free(mppc); |
225 | 0 | return rc; |
226 | 0 | } |
227 | | |
228 | | static int test_MppcDecompressBellsRdp4(const uint8_t* Data, size_t Size) |
229 | 0 | { |
230 | 0 | if (Size > UINT32_MAX) |
231 | 0 | return -1; |
232 | 0 | int rc = -1; |
233 | 0 | int status = 0; |
234 | 0 | UINT32 Flags = PACKET_AT_FRONT | PACKET_COMPRESSED | 0; |
235 | 0 | const BYTE* pSrcData = (const BYTE*)Data; |
236 | 0 | UINT32 SrcSize = (UINT32)Size; |
237 | 0 | UINT32 DstSize = 0; |
238 | 0 | const BYTE* pDstData = NULL; |
239 | 0 | MPPC_CONTEXT* mppc = mppc_context_new(0, FALSE); |
240 | |
|
241 | 0 | if (!mppc) |
242 | 0 | return -1; |
243 | | |
244 | 0 | status = mppc_decompress(mppc, pSrcData, SrcSize, &pDstData, &DstSize, Flags); |
245 | |
|
246 | 0 | if (status < 0) |
247 | 0 | goto fail; |
248 | | |
249 | 0 | rc = 0; |
250 | 0 | fail: |
251 | 0 | mppc_context_free(mppc); |
252 | 0 | return rc; |
253 | 0 | } |
254 | | |
255 | | static int test_MppcDecompressBufferRdp5(const uint8_t* Data, size_t Size) |
256 | 0 | { |
257 | 0 | if (Size > UINT32_MAX) |
258 | 0 | return -1; |
259 | 0 | int rc = -1; |
260 | 0 | int status = 0; |
261 | 0 | UINT32 Flags = PACKET_AT_FRONT | PACKET_COMPRESSED | 1; |
262 | 0 | const BYTE* pSrcData = (const BYTE*)Data; |
263 | 0 | UINT32 SrcSize = (UINT32)Size; |
264 | 0 | UINT32 DstSize = 0; |
265 | 0 | const BYTE* pDstData = NULL; |
266 | 0 | MPPC_CONTEXT* mppc = mppc_context_new(1, FALSE); |
267 | |
|
268 | 0 | if (!mppc) |
269 | 0 | return -1; |
270 | | |
271 | 0 | status = mppc_decompress(mppc, pSrcData, SrcSize, &pDstData, &DstSize, Flags); |
272 | |
|
273 | 0 | if (status < 0) |
274 | 0 | goto fail; |
275 | | |
276 | 0 | rc = 0; |
277 | 0 | fail: |
278 | 0 | mppc_context_free(mppc); |
279 | 0 | return rc; |
280 | 0 | } |
281 | | |
282 | | static int TestFreeRDPCodecMppc(const uint8_t* Data, size_t Size) |
283 | 0 | { |
284 | 0 | test_MppcDecompressBellsRdp5(Data, Size); |
285 | 0 | test_MppcDecompressBellsRdp4(Data, Size); |
286 | 0 | test_MppcDecompressBufferRdp5(Data, Size); |
287 | 0 | return 0; |
288 | 0 | } |
289 | | |
290 | | static BOOL progressive_decode(const uint8_t* Data, size_t Size) |
291 | 0 | { |
292 | 0 | BOOL res = FALSE; |
293 | 0 | int rc = 0; |
294 | 0 | BYTE* resultData = NULL; |
295 | 0 | UINT32 ColorFormat = PIXEL_FORMAT_BGRX32; |
296 | 0 | REGION16 invalidRegion = { 0 }; |
297 | 0 | UINT32 scanline = 4240; |
298 | 0 | UINT32 width = 1060; |
299 | 0 | UINT32 height = 827; |
300 | 0 | if (Size > UINT32_MAX) |
301 | 0 | return FALSE; |
302 | | |
303 | 0 | PROGRESSIVE_CONTEXT* progressiveDec = progressive_context_new(FALSE); |
304 | |
|
305 | 0 | region16_init(&invalidRegion); |
306 | 0 | if (!progressiveDec) |
307 | 0 | goto fail; |
308 | | |
309 | 0 | resultData = calloc(scanline, height); |
310 | 0 | if (!resultData) |
311 | 0 | goto fail; |
312 | | |
313 | 0 | rc = progressive_create_surface_context(progressiveDec, 0, width, height); |
314 | 0 | if (rc <= 0) |
315 | 0 | goto fail; |
316 | | |
317 | 0 | rc = progressive_decompress(progressiveDec, Data, (UINT32)Size, resultData, ColorFormat, |
318 | 0 | scanline, 0, 0, &invalidRegion, 0, 0); |
319 | 0 | if (rc < 0) |
320 | 0 | goto fail; |
321 | | |
322 | 0 | res = TRUE; |
323 | 0 | fail: |
324 | 0 | region16_uninit(&invalidRegion); |
325 | 0 | progressive_context_free(progressiveDec); |
326 | 0 | free(resultData); |
327 | 0 | return res; |
328 | 0 | } |
329 | | |
330 | | static int TestFreeRDPCodecProgressive(const uint8_t* Data, size_t Size) |
331 | 0 | { |
332 | 0 | progressive_decode(Data, Size); |
333 | 0 | return 0; |
334 | 0 | } |
335 | | |
336 | | static BOOL i_run_encode_decode(UINT16 bpp, BITMAP_INTERLEAVED_CONTEXT* encoder, |
337 | | BITMAP_INTERLEAVED_CONTEXT* decoder, const uint8_t* Data, |
338 | | size_t Size) |
339 | 0 | { |
340 | 0 | BOOL rc2 = FALSE; |
341 | 0 | BOOL rc = 0; |
342 | 0 | const UINT32 w = 64; |
343 | 0 | const UINT32 h = 64; |
344 | 0 | const UINT32 x = 0; |
345 | 0 | const UINT32 y = 0; |
346 | 0 | const UINT32 format = PIXEL_FORMAT_RGBX32; |
347 | 0 | const size_t step = (w + 13ull) * 4ull; |
348 | 0 | const size_t SrcSize = step * h; |
349 | 0 | BYTE* pSrcData = calloc(1, SrcSize); |
350 | 0 | BYTE* pDstData = calloc(1, SrcSize); |
351 | 0 | BYTE* tmp = calloc(1, SrcSize); |
352 | |
|
353 | 0 | WINPR_UNUSED(encoder); |
354 | 0 | if (!pSrcData || !pDstData || !tmp) |
355 | 0 | goto fail; |
356 | | |
357 | 0 | if (Size > UINT32_MAX) |
358 | 0 | goto fail; |
359 | | |
360 | 0 | winpr_RAND(pSrcData, SrcSize); |
361 | |
|
362 | 0 | if (!bitmap_interleaved_context_reset(decoder)) |
363 | 0 | goto fail; |
364 | | |
365 | 0 | rc = interleaved_decompress(decoder, Data, (UINT32)Size, w, h, bpp, pDstData, format, step, x, |
366 | 0 | y, w, h, NULL); |
367 | |
|
368 | 0 | if (!rc) |
369 | 0 | goto fail; |
370 | | |
371 | 0 | rc2 = TRUE; |
372 | 0 | fail: |
373 | 0 | free(pSrcData); |
374 | 0 | free(pDstData); |
375 | 0 | free(tmp); |
376 | 0 | return rc2; |
377 | 0 | } |
378 | | |
379 | | static int TestFreeRDPCodecInterleaved(const uint8_t* Data, size_t Size) |
380 | 0 | { |
381 | 0 | int rc = -1; |
382 | 0 | BITMAP_INTERLEAVED_CONTEXT* decoder = bitmap_interleaved_context_new(FALSE); |
383 | |
|
384 | 0 | if (!decoder) |
385 | 0 | goto fail; |
386 | | |
387 | 0 | i_run_encode_decode(24, NULL, decoder, Data, Size); |
388 | 0 | i_run_encode_decode(16, NULL, decoder, Data, Size); |
389 | 0 | i_run_encode_decode(15, NULL, decoder, Data, Size); |
390 | 0 | rc = 0; |
391 | 0 | fail: |
392 | 0 | bitmap_interleaved_context_free(decoder); |
393 | 0 | return rc; |
394 | 0 | } |
395 | | |
396 | | static BOOL RunTestPlanar(BITMAP_PLANAR_CONTEXT* planar, const BYTE* srcBitmap, |
397 | | const UINT32 srcFormat, const UINT32 dstFormat, const UINT32 width, |
398 | | const UINT32 height, const uint8_t* Data, size_t Size) |
399 | 0 | { |
400 | 0 | BOOL rc = FALSE; |
401 | 0 | WINPR_UNUSED(srcBitmap); |
402 | 0 | WINPR_UNUSED(srcFormat); |
403 | 0 | if (Size > UINT32_MAX) |
404 | 0 | return FALSE; |
405 | 0 | UINT32 dstSize = (UINT32)Size; |
406 | 0 | const BYTE* compressedBitmap = Data; |
407 | 0 | BYTE* decompressedBitmap = |
408 | 0 | (BYTE*)calloc(height, 1ull * width * FreeRDPGetBytesPerPixel(dstFormat)); |
409 | 0 | rc = TRUE; |
410 | |
|
411 | 0 | if (!decompressedBitmap) |
412 | 0 | goto fail; |
413 | | |
414 | 0 | if (!planar_decompress(planar, compressedBitmap, dstSize, width, height, decompressedBitmap, |
415 | 0 | dstFormat, 0, 0, 0, width, height, FALSE)) |
416 | 0 | { |
417 | 0 | goto fail; |
418 | 0 | } |
419 | | |
420 | 0 | rc = TRUE; |
421 | 0 | fail: |
422 | 0 | free(decompressedBitmap); |
423 | 0 | return rc; |
424 | 0 | } |
425 | | |
426 | | static BOOL TestPlanar(const UINT32 format, const uint8_t* Data, size_t Size) |
427 | 0 | { |
428 | 0 | BOOL rc = FALSE; |
429 | 0 | const DWORD planarFlags = PLANAR_FORMAT_HEADER_NA | PLANAR_FORMAT_HEADER_RLE; |
430 | 0 | BITMAP_PLANAR_CONTEXT* planar = freerdp_bitmap_planar_context_new(planarFlags, 64, 64); |
431 | |
|
432 | 0 | if (!planar) |
433 | 0 | goto fail; |
434 | | |
435 | 0 | RunTestPlanar(planar, NULL, PIXEL_FORMAT_RGBX32, format, 64, 64, Data, Size); |
436 | |
|
437 | 0 | RunTestPlanar(planar, NULL, PIXEL_FORMAT_RGB16, format, 32, 32, Data, Size); |
438 | |
|
439 | 0 | rc = TRUE; |
440 | 0 | fail: |
441 | 0 | freerdp_bitmap_planar_context_free(planar); |
442 | 0 | return rc; |
443 | 0 | } |
444 | | |
445 | | static int TestFreeRDPCodecPlanar(const uint8_t* Data, size_t Size) |
446 | 0 | { |
447 | 0 | TestPlanar(0, Data, Size); |
448 | 0 | return 0; |
449 | 0 | } |
450 | | |
451 | | int LLVMFuzzerTestOneInput(const uint8_t* Data, size_t Size) |
452 | 0 | { |
453 | 0 | if (Size < 4) |
454 | 0 | return 0; |
455 | | |
456 | 0 | TestFreeRDPCodecClear(Data, Size); |
457 | 0 | TestFreeRDPCodecXCrush(Data, Size); |
458 | 0 | TestFreeRDPCodecZGfx(Data, Size); |
459 | 0 | TestFreeRDPCodecNCrush(Data, Size); |
460 | 0 | TestFreeRDPCodecRemoteFX(Data, Size); |
461 | 0 | TestFreeRDPCodecMppc(Data, Size); |
462 | 0 | TestFreeRDPCodecProgressive(Data, Size); |
463 | 0 | TestFreeRDPCodecInterleaved(Data, Size); |
464 | 0 | TestFreeRDPCodecPlanar(Data, Size); |
465 | |
|
466 | 0 | return 0; |
467 | 0 | } |