Coverage Report

Created: 2025-12-10 06:43

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