FreeRDP
TestFuzzCodecs.c
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 {
51  BOOL rc = FALSE;
52  int status = 0;
53  BYTE* pDstData = calloc(1ull * width * height, 4);
54  CLEAR_CONTEXT* clear = clear_context_new(FALSE);
55 
56  WINPR_UNUSED(nr);
57  if (!clear || !pDstData)
58  goto fail;
59 
60  status = clear_decompress(clear, pSrcData, SrcSize, width, height, pDstData,
61  PIXEL_FORMAT_XRGB32, 0, 0, 0, width, height, NULL);
62  // printf("clear_decompress example %" PRIu32 " status: %d\n", nr, status);
63  // fflush(stdout);
64  rc = (status == 0);
65 fail:
66  clear_context_free(clear);
67  free(pDstData);
68  return rc;
69 }
70 
71 static int TestFreeRDPCodecClear(const uint8_t* Data, size_t Size)
72 {
73  if (Size > UINT32_MAX)
74  return -1;
75  test_ClearDecompressExample(2, 78, 17, Data, (UINT32)Size);
76  test_ClearDecompressExample(3, 64, 24, Data, (UINT32)Size);
77  test_ClearDecompressExample(4, 7, 15, Data, (UINT32)Size);
78  return 0;
79 }
80 
81 static int TestFreeRDPCodecXCrush(const uint8_t* Data, size_t Size)
82 {
83  if (Size > UINT32_MAX)
84  return -1;
85 
86  const BYTE* OutputBuffer = NULL;
87  UINT32 DstSize = 0;
88  XCRUSH_CONTEXT* xcrush = xcrush_context_new(TRUE);
89  if (!xcrush)
90  return 0;
91  xcrush_decompress(xcrush, Data, (UINT32)Size, &OutputBuffer, &DstSize, 0);
92  xcrush_context_free(xcrush);
93  return 0;
94 }
95 
96 static int test_ZGfxDecompressFoxSingle(const uint8_t* Data, size_t Size)
97 {
98  if (Size > UINT32_MAX)
99  return -1;
100  int rc = -1;
101  int status = 0;
102  UINT32 Flags = 0;
103  const BYTE* pSrcData = (const BYTE*)Data;
104  UINT32 SrcSize = (UINT32)Size;
105  UINT32 DstSize = 0;
106  BYTE* pDstData = NULL;
107  ZGFX_CONTEXT* zgfx = zgfx_context_new(TRUE);
108 
109  if (!zgfx)
110  return -1;
111 
112  status = zgfx_decompress(zgfx, pSrcData, SrcSize, &pDstData, &DstSize, Flags);
113  if (status < 0)
114  goto fail;
115 
116  rc = 0;
117 fail:
118  free(pDstData);
119  zgfx_context_free(zgfx);
120  return rc;
121 }
122 
123 static int TestFreeRDPCodecZGfx(const uint8_t* Data, size_t Size)
124 {
125  test_ZGfxDecompressFoxSingle(Data, Size);
126  return 0;
127 }
128 
129 static BOOL test_NCrushDecompressBells(const uint8_t* Data, size_t Size)
130 {
131  if (Size > UINT32_MAX)
132  return FALSE;
133 
134  BOOL rc = FALSE;
135  int status = 0;
136  UINT32 Flags = PACKET_COMPRESSED | 2;
137  const BYTE* pSrcData = (const BYTE*)Data;
138  UINT32 SrcSize = (UINT32)Size;
139  UINT32 DstSize = 0;
140  const BYTE* pDstData = NULL;
141  NCRUSH_CONTEXT* ncrush = ncrush_context_new(FALSE);
142 
143  if (!ncrush)
144  return rc;
145 
146  status = ncrush_decompress(ncrush, pSrcData, SrcSize, &pDstData, &DstSize, Flags);
147  if (status < 0)
148  goto fail;
149 
150  rc = TRUE;
151 fail:
152  ncrush_context_free(ncrush);
153  return rc;
154 }
155 
156 static int TestFreeRDPCodecNCrush(const uint8_t* Data, size_t Size)
157 {
158  test_NCrushDecompressBells(Data, Size);
159  return 0;
160 }
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 {
169  int rc = -1;
170  REGION16 region = { 0 };
171  RFX_CONTEXT* context = rfx_context_new(FALSE);
172  BYTE* dest = calloc(IMG_WIDTH * IMG_HEIGHT, FORMAT_SIZE);
173  size_t stride = FORMAT_SIZE * IMG_WIDTH;
174  if (!context)
175  goto fail;
176  if (Size > UINT32_MAX)
177  goto fail;
178  if (stride > UINT32_MAX)
179  goto fail;
180  if (!dest)
181  goto fail;
182 
183  region16_init(&region);
184  if (!rfx_process_message(context, Data, (UINT32)Size, 0, 0, dest, FORMAT, (UINT32)stride,
185  IMG_HEIGHT, &region))
186  goto fail;
187 
188  region16_clear(&region);
189  if (!rfx_process_message(context, Data, (UINT32)Size, 0, 0, dest, FORMAT, (UINT32)stride,
190  IMG_HEIGHT, &region))
191  goto fail;
192  region16_print(&region);
193 
194  rc = 0;
195 fail:
196  region16_uninit(&region);
197  rfx_context_free(context);
198  free(dest);
199  return rc;
200 }
201 
202 static int test_MppcDecompressBellsRdp5(const uint8_t* Data, size_t Size)
203 {
204  int rc = -1;
205  int status = 0;
206  UINT32 Flags = PACKET_AT_FRONT | PACKET_COMPRESSED | 1;
207  const BYTE* pSrcData = Data;
208  UINT32 SrcSize = (UINT32)Size;
209  UINT32 DstSize = 0;
210  const BYTE* pDstData = NULL;
211  MPPC_CONTEXT* mppc = mppc_context_new(1, FALSE);
212 
213  if (!mppc)
214  return -1;
215 
216  status = mppc_decompress(mppc, pSrcData, SrcSize, &pDstData, &DstSize, Flags);
217 
218  if (status < 0)
219  goto fail;
220 
221  rc = 0;
222 
223 fail:
224  mppc_context_free(mppc);
225  return rc;
226 }
227 
228 static int test_MppcDecompressBellsRdp4(const uint8_t* Data, size_t Size)
229 {
230  if (Size > UINT32_MAX)
231  return -1;
232  int rc = -1;
233  int status = 0;
234  UINT32 Flags = PACKET_AT_FRONT | PACKET_COMPRESSED | 0;
235  const BYTE* pSrcData = (const BYTE*)Data;
236  UINT32 SrcSize = (UINT32)Size;
237  UINT32 DstSize = 0;
238  const BYTE* pDstData = NULL;
239  MPPC_CONTEXT* mppc = mppc_context_new(0, FALSE);
240 
241  if (!mppc)
242  return -1;
243 
244  status = mppc_decompress(mppc, pSrcData, SrcSize, &pDstData, &DstSize, Flags);
245 
246  if (status < 0)
247  goto fail;
248 
249  rc = 0;
250 fail:
251  mppc_context_free(mppc);
252  return rc;
253 }
254 
255 static int test_MppcDecompressBufferRdp5(const uint8_t* Data, size_t Size)
256 {
257  if (Size > UINT32_MAX)
258  return -1;
259  int rc = -1;
260  int status = 0;
261  UINT32 Flags = PACKET_AT_FRONT | PACKET_COMPRESSED | 1;
262  const BYTE* pSrcData = (const BYTE*)Data;
263  UINT32 SrcSize = (UINT32)Size;
264  UINT32 DstSize = 0;
265  const BYTE* pDstData = NULL;
266  MPPC_CONTEXT* mppc = mppc_context_new(1, FALSE);
267 
268  if (!mppc)
269  return -1;
270 
271  status = mppc_decompress(mppc, pSrcData, SrcSize, &pDstData, &DstSize, Flags);
272 
273  if (status < 0)
274  goto fail;
275 
276  rc = 0;
277 fail:
278  mppc_context_free(mppc);
279  return rc;
280 }
281 
282 static int TestFreeRDPCodecMppc(const uint8_t* Data, size_t Size)
283 {
284  test_MppcDecompressBellsRdp5(Data, Size);
285  test_MppcDecompressBellsRdp4(Data, Size);
286  test_MppcDecompressBufferRdp5(Data, Size);
287  return 0;
288 }
289 
290 static BOOL progressive_decode(const uint8_t* Data, size_t Size)
291 {
292  BOOL res = FALSE;
293  int rc = 0;
294  BYTE* resultData = NULL;
295  UINT32 ColorFormat = PIXEL_FORMAT_BGRX32;
296  REGION16 invalidRegion = { 0 };
297  UINT32 scanline = 4240;
298  UINT32 width = 1060;
299  UINT32 height = 827;
300  if (Size > UINT32_MAX)
301  return FALSE;
302 
303  PROGRESSIVE_CONTEXT* progressiveDec = progressive_context_new(FALSE);
304 
305  region16_init(&invalidRegion);
306  if (!progressiveDec)
307  goto fail;
308 
309  resultData = calloc(scanline, height);
310  if (!resultData)
311  goto fail;
312 
313  rc = progressive_create_surface_context(progressiveDec, 0, width, height);
314  if (rc <= 0)
315  goto fail;
316 
317  rc = progressive_decompress(progressiveDec, Data, (UINT32)Size, resultData, ColorFormat,
318  scanline, 0, 0, &invalidRegion, 0, 0);
319  if (rc < 0)
320  goto fail;
321 
322  res = TRUE;
323 fail:
324  region16_uninit(&invalidRegion);
325  progressive_context_free(progressiveDec);
326  free(resultData);
327  return res;
328 }
329 
330 static int TestFreeRDPCodecProgressive(const uint8_t* Data, size_t Size)
331 {
332  progressive_decode(Data, Size);
333  return 0;
334 }
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 {
340  BOOL rc2 = FALSE;
341  BOOL rc = 0;
342  const UINT32 w = 64;
343  const UINT32 h = 64;
344  const UINT32 x = 0;
345  const UINT32 y = 0;
346  const UINT32 format = PIXEL_FORMAT_RGBX32;
347  const size_t step = (w + 13ull) * 4ull;
348  const size_t SrcSize = step * h;
349  BYTE* pSrcData = calloc(1, SrcSize);
350  BYTE* pDstData = calloc(1, SrcSize);
351  BYTE* tmp = calloc(1, SrcSize);
352 
353  WINPR_UNUSED(encoder);
354  if (!pSrcData || !pDstData || !tmp)
355  goto fail;
356 
357  if (Size > UINT32_MAX)
358  goto fail;
359 
360  winpr_RAND(pSrcData, SrcSize);
361 
362  if (!bitmap_interleaved_context_reset(decoder))
363  goto fail;
364 
365  rc = interleaved_decompress(decoder, Data, (UINT32)Size, w, h, bpp, pDstData, format, step, x,
366  y, w, h, NULL);
367 
368  if (!rc)
369  goto fail;
370 
371  rc2 = TRUE;
372 fail:
373  free(pSrcData);
374  free(pDstData);
375  free(tmp);
376  return rc2;
377 }
378 
379 static int TestFreeRDPCodecInterleaved(const uint8_t* Data, size_t Size)
380 {
381  int rc = -1;
382  BITMAP_INTERLEAVED_CONTEXT* decoder = bitmap_interleaved_context_new(FALSE);
383 
384  if (!decoder)
385  goto fail;
386 
387  i_run_encode_decode(24, NULL, decoder, Data, Size);
388  i_run_encode_decode(16, NULL, decoder, Data, Size);
389  i_run_encode_decode(15, NULL, decoder, Data, Size);
390  rc = 0;
391 fail:
392  bitmap_interleaved_context_free(decoder);
393  return rc;
394 }
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 {
400  BOOL rc = FALSE;
401  WINPR_UNUSED(srcBitmap);
402  WINPR_UNUSED(srcFormat);
403  if (Size > UINT32_MAX)
404  return FALSE;
405  UINT32 dstSize = (UINT32)Size;
406  const BYTE* compressedBitmap = Data;
407  BYTE* decompressedBitmap =
408  (BYTE*)calloc(height, 1ull * width * FreeRDPGetBytesPerPixel(dstFormat));
409  rc = TRUE;
410 
411  if (!decompressedBitmap)
412  goto fail;
413 
414  if (!planar_decompress(planar, compressedBitmap, dstSize, width, height, decompressedBitmap,
415  dstFormat, 0, 0, 0, width, height, FALSE))
416  {
417  goto fail;
418  }
419 
420  rc = TRUE;
421 fail:
422  free(decompressedBitmap);
423  return rc;
424 }
425 
426 static BOOL TestPlanar(const UINT32 format, const uint8_t* Data, size_t Size)
427 {
428  BOOL rc = FALSE;
429  const DWORD planarFlags = PLANAR_FORMAT_HEADER_NA | PLANAR_FORMAT_HEADER_RLE;
430  BITMAP_PLANAR_CONTEXT* planar = freerdp_bitmap_planar_context_new(planarFlags, 64, 64);
431 
432  if (!planar)
433  goto fail;
434 
435  RunTestPlanar(planar, NULL, PIXEL_FORMAT_RGBX32, format, 64, 64, Data, Size);
436 
437  RunTestPlanar(planar, NULL, PIXEL_FORMAT_RGB16, format, 32, 32, Data, Size);
438 
439  rc = TRUE;
440 fail:
441  freerdp_bitmap_planar_context_free(planar);
442  return rc;
443 }
444 
445 static int TestFreeRDPCodecPlanar(const uint8_t* Data, size_t Size)
446 {
447  TestPlanar(0, Data, Size);
448  return 0;
449 }
450 
451 int LLVMFuzzerTestOneInput(const uint8_t* Data, size_t Size)
452 {
453  if (Size < 4)
454  return 0;
455 
456  TestFreeRDPCodecClear(Data, Size);
457  TestFreeRDPCodecXCrush(Data, Size);
458  TestFreeRDPCodecZGfx(Data, Size);
459  TestFreeRDPCodecNCrush(Data, Size);
460  TestFreeRDPCodecRemoteFX(Data, Size);
461  TestFreeRDPCodecMppc(Data, Size);
462  TestFreeRDPCodecProgressive(Data, Size);
463  TestFreeRDPCodecInterleaved(Data, Size);
464  TestFreeRDPCodecPlanar(Data, Size);
465 
466  return 0;
467 }