Coverage Report

Created: 2025-07-01 06:46

/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(&region);
184
0
  if (!rfx_process_message(context, Data, (UINT32)Size, 0, 0, dest, FORMAT, (UINT32)stride,
185
0
                           IMG_HEIGHT, &region))
186
0
    goto fail;
187
188
0
  region16_clear(&region);
189
0
  if (!rfx_process_message(context, Data, (UINT32)Size, 0, 0, dest, FORMAT, (UINT32)stride,
190
0
                           IMG_HEIGHT, &region))
191
0
    goto fail;
192
0
  region16_print(&region);
193
194
0
  rc = 0;
195
0
fail:
196
0
  region16_uninit(&region);
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
}