summaryrefslogtreecommitdiffstats
path: root/libfreerdp/codec
diff options
context:
space:
mode:
Diffstat (limited to 'libfreerdp/codec')
-rw-r--r--libfreerdp/codec/clear.c2
-rw-r--r--libfreerdp/codec/interleaved.c2
-rw-r--r--libfreerdp/codec/ncrush.c55
-rw-r--r--libfreerdp/codec/nsc.c4
-rw-r--r--libfreerdp/codec/planar.c54
-rw-r--r--libfreerdp/codec/rfx.c2
-rw-r--r--libfreerdp/codec/rfx_rlgr.c42
-rw-r--r--libfreerdp/codec/test/CMakeLists.txt6
-rw-r--r--libfreerdp/codec/test/TestFuzzCodecs.c480
-rw-r--r--libfreerdp/codec/zgfx.c14
10 files changed, 586 insertions, 75 deletions
diff --git a/libfreerdp/codec/clear.c b/libfreerdp/codec/clear.c
index 5c009d8..512aeae 100644
--- a/libfreerdp/codec/clear.c
+++ b/libfreerdp/codec/clear.c
@@ -409,7 +409,7 @@ static BOOL clear_decompress_residual_data(CLEAR_CONTEXT* clear, wStream* s,
}
}
- if ((pixelIndex + runLengthFactor) > pixelCount)
+ if ((pixelIndex >= pixelCount) || (runLengthFactor > (pixelCount - pixelIndex)))
{
WLog_ERR(TAG,
"pixelIndex %" PRIu32 " + runLengthFactor %" PRIu32 " > pixelCount %" PRIu32
diff --git a/libfreerdp/codec/interleaved.c b/libfreerdp/codec/interleaved.c
index 75b2e27..df148b6 100644
--- a/libfreerdp/codec/interleaved.c
+++ b/libfreerdp/codec/interleaved.c
@@ -237,7 +237,7 @@ static UINT ExtractRunLengthLiteFgBg(const BYTE* pbOrderHdr, const BYTE* pbEnd,
runLength = *pbOrderHdr & g_MaskLiteRunLength;
if (runLength == 0)
{
- if (!buffer_within_range(pbOrderHdr, 1, pbEnd))
+ if (!buffer_within_range(pbOrderHdr, 2, pbEnd))
{
*advance = 0;
return 0;
diff --git a/libfreerdp/codec/ncrush.c b/libfreerdp/codec/ncrush.c
index 4a7162c..28b98d9 100644
--- a/libfreerdp/codec/ncrush.c
+++ b/libfreerdp/codec/ncrush.c
@@ -2068,6 +2068,12 @@ int ncrush_decompress(NCRUSH_CONTEXT* ncrush, const BYTE* pSrcData, UINT32 SrcSi
return 1;
}
+ if (SrcSize < 4)
+ {
+ WLog_ERR(TAG, "Input size short: SrcSize %" PRIu32 " < 4", SrcSize);
+ return -1;
+ }
+
const BYTE* SrcEnd = &pSrcData[SrcSize];
const BYTE* SrcPtr = pSrcData + 4;
@@ -2119,7 +2125,7 @@ int ncrush_decompress(NCRUSH_CONTEXT* ncrush, const BYTE* pSrcData, UINT32 SrcSi
CopyOffset = ncrush->OffsetCache[OffsetCacheIndex];
const UINT16 Mask = get_word(&HuffTableMask[21]);
const UINT32 MaskedBits = bits & Mask;
- if (MaskedBits > ARRAYSIZE(HuffTableLOM))
+ if (MaskedBits >= ARRAYSIZE(HuffTableLOM))
return -1;
LengthOfMatch = HuffTableLOM[MaskedBits] & 0xFFF;
BitLength = HuffTableLOM[MaskedBits] >> 12;
@@ -2480,50 +2486,39 @@ static int ncrush_find_best_match(NCRUSH_CONTEXT* ncrush, UINT16 HistoryOffset,
static int ncrush_move_encoder_windows(NCRUSH_CONTEXT* ncrush, BYTE* HistoryPtr)
{
- int NewHash = 0;
- int NewMatch = 0;
- UINT32 HistoryOffset = 0;
-
WINPR_ASSERT(ncrush);
WINPR_ASSERT(HistoryPtr);
- if (HistoryPtr < &ncrush->HistoryBuffer[32768])
+ const size_t history_half = ARRAYSIZE(ncrush->HistoryBuffer) / 2;
+ if (HistoryPtr < &ncrush->HistoryBuffer[history_half])
return -1;
- if (HistoryPtr > &ncrush->HistoryBuffer[65536])
+ if (HistoryPtr > &ncrush->HistoryBuffer[ARRAYSIZE(ncrush->HistoryBuffer)])
return -1;
- MoveMemory(ncrush->HistoryBuffer, HistoryPtr - 32768, 32768);
- const intptr_t hsize = HistoryPtr - 32768 - ncrush->HistoryBuffer;
- WINPR_ASSERT(hsize <= UINT32_MAX);
+ MoveMemory(ncrush->HistoryBuffer, HistoryPtr - history_half, history_half * sizeof(BYTE));
+ const intptr_t hsize = HistoryPtr - history_half - ncrush->HistoryBuffer;
+ WINPR_ASSERT(hsize <= UINT16_MAX);
WINPR_ASSERT(hsize >= 0);
- HistoryOffset = (UINT32)hsize;
+ INT32 HistoryOffset = (INT32)hsize;
- for (int i = 0; i < 65536; i += 4)
+ for (size_t i = 0; i < ARRAYSIZE(ncrush->HashTable); i++)
{
- NewHash = ncrush->HashTable[i + 0] - HistoryOffset;
- ncrush->HashTable[i + 0] = (NewHash <= 0) ? 0 : NewHash;
- NewHash = ncrush->HashTable[i + 1] - HistoryOffset;
- ncrush->HashTable[i + 1] = (NewHash <= 0) ? 0 : NewHash;
- NewHash = ncrush->HashTable[i + 2] - HistoryOffset;
- ncrush->HashTable[i + 2] = (NewHash <= 0) ? 0 : NewHash;
- NewHash = ncrush->HashTable[i + 3] - HistoryOffset;
- ncrush->HashTable[i + 3] = (NewHash <= 0) ? 0 : NewHash;
+ INT32 NewHash = ncrush->HashTable[i] - HistoryOffset;
+ ncrush->HashTable[i] = (NewHash <= 0) ? 0 : NewHash;
}
- for (int j = 0; j < 32768; j += 4)
+ const size_t match_half = ARRAYSIZE(ncrush->MatchTable) / 2;
+ for (size_t j = 0; j < match_half; j++)
{
- NewMatch = ncrush->MatchTable[HistoryOffset + j + 0] - HistoryOffset;
- ncrush->MatchTable[j + 0] = (NewMatch <= 0) ? 0 : NewMatch;
- NewMatch = ncrush->MatchTable[HistoryOffset + j + 1] - HistoryOffset;
- ncrush->MatchTable[j + 1] = (NewMatch <= 0) ? 0 : NewMatch;
- NewMatch = ncrush->MatchTable[HistoryOffset + j + 2] - HistoryOffset;
- ncrush->MatchTable[j + 2] = (NewMatch <= 0) ? 0 : NewMatch;
- NewMatch = ncrush->MatchTable[HistoryOffset + j + 3] - HistoryOffset;
- ncrush->MatchTable[j + 3] = (NewMatch <= 0) ? 0 : NewMatch;
+ if (HistoryOffset + j >= ARRAYSIZE(ncrush->MatchTable))
+ continue;
+
+ INT32 NewMatch = ncrush->MatchTable[HistoryOffset + j] - HistoryOffset;
+ ncrush->MatchTable[j] = (NewMatch <= 0) ? 0 : NewMatch;
}
- ZeroMemory(&ncrush->MatchTable[32768], 65536);
+ ZeroMemory(&ncrush->MatchTable[match_half], match_half * sizeof(UINT16));
return 1;
}
diff --git a/libfreerdp/codec/nsc.c b/libfreerdp/codec/nsc.c
index 049b541..74f4e28 100644
--- a/libfreerdp/codec/nsc.c
+++ b/libfreerdp/codec/nsc.c
@@ -160,7 +160,7 @@ static BOOL nsc_rle_decode(const BYTE* in, size_t inSize, BYTE* out, UINT32 outS
len |= ((UINT32)(*in++)) << 24U;
}
- if (outSize < len)
+ if ((outSize < len) || (left < len))
return FALSE;
outSize -= len;
@@ -262,7 +262,7 @@ static BOOL nsc_context_initialize(NSC_CONTEXT* context, wStream* s)
if (!nsc_stream_initialize(context, s))
return FALSE;
- const size_t blength = context->width * context->height * 4ull;
+ const size_t blength = 4ull * context->width * context->height;
if (!context->BitmapData || (blength > context->BitmapDataLength))
{
diff --git a/libfreerdp/codec/planar.c b/libfreerdp/codec/planar.c
index 0ec0862..4b51a02 100644
--- a/libfreerdp/codec/planar.c
+++ b/libfreerdp/codec/planar.c
@@ -788,18 +788,26 @@ BOOL planar_decompress(BITMAP_PLANAR_CONTEXT* planar, const BYTE* pSrcData, UINT
rawHeights[3] = nSrcHeight;
}
+ const size_t diff = srcp - pSrcData;
+ if (SrcSize < diff)
+ {
+ WLog_ERR(TAG, "Size mismatch %" PRIu32 " < %" PRIuz, SrcSize, diff);
+ return FALSE;
+ }
+
if (!rle) /* RAW */
{
+
UINT32 base = planeSize * 3;
if (cs)
base = planeSize + planeSize / 2;
if (alpha)
{
- if ((SrcSize - (srcp - pSrcData)) < (planeSize + base))
+ if ((SrcSize - diff) < (planeSize + base))
{
- WLog_ERR(TAG, "Alpha plane size mismatch %" PRIu32 " < %" PRIu32,
- SrcSize - (srcp - pSrcData), (planeSize + base));
+ WLog_ERR(TAG, "Alpha plane size mismatch %" PRIuz " < %" PRIu32, SrcSize - diff,
+ (planeSize + base));
return FALSE;
}
@@ -817,10 +825,9 @@ BOOL planar_decompress(BITMAP_PLANAR_CONTEXT* planar, const BYTE* pSrcData, UINT
}
else
{
- if ((SrcSize - (srcp - pSrcData)) < base)
+ if ((SrcSize - diff) < base)
{
- WLog_ERR(TAG, "plane size mismatch %" PRIu32 " < %" PRIu32,
- SrcSize - (srcp - pSrcData), base);
+ WLog_ERR(TAG, "plane size mismatch %" PRIu32 " < %" PRIu32, SrcSize - diff, base);
return FALSE;
}
@@ -841,8 +848,8 @@ BOOL planar_decompress(BITMAP_PLANAR_CONTEXT* planar, const BYTE* pSrcData, UINT
if (alpha)
{
planes[3] = srcp;
- rleSizes[3] = planar_skip_plane_rle(planes[3], SrcSize - (planes[3] - pSrcData),
- rawWidths[3], rawHeights[3]); /* AlphaPlane */
+ rleSizes[3] = planar_skip_plane_rle(planes[3], SrcSize - diff, rawWidths[3],
+ rawHeights[3]); /* AlphaPlane */
if (rleSizes[3] < 0)
return FALSE;
@@ -852,22 +859,41 @@ BOOL planar_decompress(BITMAP_PLANAR_CONTEXT* planar, const BYTE* pSrcData, UINT
else
planes[0] = srcp;
- rleSizes[0] = planar_skip_plane_rle(planes[0], SrcSize - (planes[0] - pSrcData),
- rawWidths[0], rawHeights[0]); /* RedPlane */
+ const size_t diff0 = (planes[0] - pSrcData);
+ if (SrcSize < diff0)
+ {
+ WLog_ERR(TAG, "Size mismatch %" PRIu32 " < %" PRIuz, SrcSize, diff0);
+ return FALSE;
+ }
+ rleSizes[0] = planar_skip_plane_rle(planes[0], SrcSize - diff0, rawWidths[0],
+ rawHeights[0]); /* RedPlane */
if (rleSizes[0] < 0)
return FALSE;
planes[1] = planes[0] + rleSizes[0];
- rleSizes[1] = planar_skip_plane_rle(planes[1], SrcSize - (planes[1] - pSrcData),
- rawWidths[1], rawHeights[1]); /* GreenPlane */
+
+ const size_t diff1 = (planes[1] - pSrcData);
+ if (SrcSize < diff1)
+ {
+ WLog_ERR(TAG, "Size mismatch %" PRIu32 " < %" PRIuz, SrcSize, diff1);
+ return FALSE;
+ }
+ rleSizes[1] = planar_skip_plane_rle(planes[1], SrcSize - diff1, rawWidths[1],
+ rawHeights[1]); /* GreenPlane */
if (rleSizes[1] < 1)
return FALSE;
planes[2] = planes[1] + rleSizes[1];
- rleSizes[2] = planar_skip_plane_rle(planes[2], SrcSize - (planes[2] - pSrcData),
- rawWidths[2], rawHeights[2]); /* BluePlane */
+ const size_t diff2 = (planes[2] - pSrcData);
+ if (SrcSize < diff2)
+ {
+ WLog_ERR(TAG, "Size mismatch %" PRIu32 " < %" PRIuz, SrcSize, diff);
+ return FALSE;
+ }
+ rleSizes[2] = planar_skip_plane_rle(planes[2], SrcSize - diff2, rawWidths[2],
+ rawHeights[2]); /* BluePlane */
if (rleSizes[2] < 1)
return FALSE;
diff --git a/libfreerdp/codec/rfx.c b/libfreerdp/codec/rfx.c
index c83cfd5..66ed1e0 100644
--- a/libfreerdp/codec/rfx.c
+++ b/libfreerdp/codec/rfx.c
@@ -1368,7 +1368,7 @@ const RFX_TILE** rfx_message_get_tiles(const RFX_MESSAGE* message, UINT16* numTi
WINPR_ASSERT(message);
if (numTiles)
*numTiles = message->numTiles;
- return message->tiles;
+ return (const RFX_TILE**)message->tiles;
}
UINT16 rfx_message_get_tile_count(const RFX_MESSAGE* message)
diff --git a/libfreerdp/codec/rfx_rlgr.c b/libfreerdp/codec/rfx_rlgr.c
index da1b63f..6ecbfbe 100644
--- a/libfreerdp/codec/rfx_rlgr.c
+++ b/libfreerdp/codec/rfx_rlgr.c
@@ -50,12 +50,12 @@
#define GetMinBits(_val, _nbits) \
do \
{ \
- UINT32 _v = _val; \
- _nbits = 0; \
+ UINT32 _v = (_val); \
+ (_nbits) = 0; \
while (_v) \
{ \
_v >>= 1; \
- _nbits++; \
+ (_nbits)++; \
} \
} while (0)
@@ -66,12 +66,12 @@
#define UpdateParam(_param, _deltaP, _k) \
do \
{ \
- _param += _deltaP; \
- if (_param > KPMAX) \
- _param = KPMAX; \
- if (_param < 0) \
- _param = 0; \
- _k = (_param >> LSGR); \
+ (_param) += (_deltaP); \
+ if ((_param) > KPMAX) \
+ (_param) = KPMAX; \
+ if ((_param) < 0) \
+ (_param) = 0; \
+ (_k) = ((_param) >> LSGR); \
} while (0)
static BOOL g_LZCNT = FALSE;
@@ -568,18 +568,18 @@ int rfx_rlgr_decode(RLGR_MODE mode, const BYTE* WINPR_RESTRICT pSrcData, UINT32
}
/* Returns the next coefficient (a signed int) to encode, from the input stream */
-#define GetNextInput(_n) \
- do \
- { \
- if (data_size > 0) \
- { \
- _n = *data++; \
- data_size--; \
- } \
- else \
- { \
- _n = 0; \
- } \
+#define GetNextInput(_n) \
+ do \
+ { \
+ if (data_size > 0) \
+ { \
+ (_n) = *data++; \
+ data_size--; \
+ } \
+ else \
+ { \
+ (_n) = 0; \
+ } \
} while (0)
/* Emit bitPattern to the output bitstream */
diff --git a/libfreerdp/codec/test/CMakeLists.txt b/libfreerdp/codec/test/CMakeLists.txt
index 4258b50..ebc7b8e 100644
--- a/libfreerdp/codec/test/CMakeLists.txt
+++ b/libfreerdp/codec/test/CMakeLists.txt
@@ -35,3 +35,9 @@ endforeach()
set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "FreeRDP/Test")
+set(FUZZERS
+ TestFuzzCodecs.c
+)
+
+include (AddFuzzerTest)
+add_fuzzer_test("${FUZZERS}" "freerdp winpr")
diff --git a/libfreerdp/codec/test/TestFuzzCodecs.c b/libfreerdp/codec/test/TestFuzzCodecs.c
new file mode 100644
index 0000000..d83d8d4
--- /dev/null
+++ b/libfreerdp/codec/test/TestFuzzCodecs.c
@@ -0,0 +1,480 @@
+/* https://github.com/ergnoorr/fuzzrdp
+ *
+ * MIT License
+ *
+ * Copyright (c) 2024 ergnoorr
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#include <freerdp/assistance.h>
+
+#include <winpr/crt.h>
+#include <winpr/print.h>
+#include <winpr/platform.h>
+#include <freerdp/codec/interleaved.h>
+#include <freerdp/codec/planar.h>
+#include <freerdp/codec/bulk.h>
+#include <freerdp/codec/clear.h>
+#include <freerdp/codec/zgfx.h>
+#include <freerdp/log.h>
+#include <winpr/bitstream.h>
+#include <freerdp/codec/rfx.h>
+#include <freerdp/codec/progressive.h>
+
+#include <freerdp/freerdp.h>
+#include <freerdp/gdi/gdi.h>
+
+#include "../progressive.h"
+#include "../mppc.h"
+#include "../xcrush.h"
+#include "../ncrush.h"
+
+static BOOL test_ClearDecompressExample(UINT32 nr, UINT32 width, UINT32 height,
+ const BYTE* pSrcData, const UINT32 SrcSize)
+{
+ BOOL rc = FALSE;
+ int status = 0;
+ BYTE* pDstData = calloc(1ull * width * height, 4);
+ CLEAR_CONTEXT* clear = clear_context_new(FALSE);
+
+ WINPR_UNUSED(nr);
+ if (!clear || !pDstData)
+ goto fail;
+
+ status = clear_decompress(clear, pSrcData, SrcSize, width, height, pDstData,
+ PIXEL_FORMAT_XRGB32, 0, 0, 0, width, height, NULL);
+ // printf("clear_decompress example %" PRIu32 " status: %d\n", nr, status);
+ // fflush(stdout);
+ rc = (status == 0);
+fail:
+ clear_context_free(clear);
+ free(pDstData);
+ return rc;
+}
+
+static int TestFreeRDPCodecClear(const uint8_t* Data, size_t Size)
+{
+ if (Size > UINT32_MAX)
+ return -1;
+ test_ClearDecompressExample(2, 78, 17, Data, (UINT32)Size);
+ test_ClearDecompressExample(3, 64, 24, Data, (UINT32)Size);
+ test_ClearDecompressExample(4, 7, 15, Data, (UINT32)Size);
+ return 0;
+}
+
+static int TestFreeRDPCodecXCrush(const uint8_t* Data, size_t Size)
+{
+ if (Size > UINT32_MAX)
+ return -1;
+
+ const BYTE* OutputBuffer = NULL;
+ UINT32 DstSize = 0;
+ XCRUSH_CONTEXT* xcrush = xcrush_context_new(TRUE);
+ if (!xcrush)
+ return 0;
+ xcrush_decompress(xcrush, Data, (UINT32)Size, &OutputBuffer, &DstSize, 0);
+ xcrush_context_free(xcrush);
+ return 0;
+}
+
+static int test_ZGfxDecompressFoxSingle(const uint8_t* Data, size_t Size)
+{
+ if (Size > UINT32_MAX)
+ return -1;
+ int rc = -1;
+ int status = 0;
+ UINT32 Flags = 0;
+ const BYTE* pSrcData = (const BYTE*)Data;
+ UINT32 SrcSize = (UINT32)Size;
+ UINT32 DstSize = 0;
+ BYTE* pDstData = NULL;
+ ZGFX_CONTEXT* zgfx = zgfx_context_new(TRUE);
+
+ if (!zgfx)
+ return -1;
+
+ status = zgfx_decompress(zgfx, pSrcData, SrcSize, &pDstData, &DstSize, Flags);
+ if (status < 0)
+ goto fail;
+
+ rc = 0;
+fail:
+ free(pDstData);
+ zgfx_context_free(zgfx);
+ return rc;
+}
+
+static int TestFreeRDPCodecZGfx(const uint8_t* Data, size_t Size)
+{
+ test_ZGfxDecompressFoxSingle(Data, Size);
+ return 0;
+}
+
+static BOOL test_NCrushDecompressBells(const uint8_t* Data, size_t Size)
+{
+ if (Size > UINT32_MAX)
+ return FALSE;
+
+ BOOL rc = FALSE;
+ int status = 0;
+ UINT32 Flags = PACKET_COMPRESSED | 2;
+ const BYTE* pSrcData = (const BYTE*)Data;
+ UINT32 SrcSize = (UINT32)Size;
+ UINT32 DstSize = 0;
+ const BYTE* pDstData = NULL;
+ NCRUSH_CONTEXT* ncrush = ncrush_context_new(FALSE);
+
+ if (!ncrush)
+ return rc;
+
+ status = ncrush_decompress(ncrush, pSrcData, SrcSize, &pDstData, &DstSize, Flags);
+ if (status < 0)
+ goto fail;
+
+ rc = TRUE;
+fail:
+ ncrush_context_free(ncrush);
+ return rc;
+}
+
+static int TestFreeRDPCodecNCrush(const uint8_t* Data, size_t Size)
+{
+ test_NCrushDecompressBells(Data, Size);
+ return 0;
+}
+
+static const size_t IMG_WIDTH = 64;
+static const size_t IMG_HEIGHT = 64;
+static const size_t FORMAT_SIZE = 4;
+static const UINT32 FORMAT = PIXEL_FORMAT_XRGB32;
+
+static int TestFreeRDPCodecRemoteFX(const uint8_t* Data, size_t Size)
+{
+ int rc = -1;
+ REGION16 region = { 0 };
+ RFX_CONTEXT* context = rfx_context_new(FALSE);
+ BYTE* dest = calloc(IMG_WIDTH * IMG_HEIGHT, FORMAT_SIZE);
+ size_t stride = FORMAT_SIZE * IMG_WIDTH;
+ if (!context)
+ goto fail;
+ if (Size > UINT32_MAX)
+ goto fail;
+ if (stride > UINT32_MAX)
+ goto fail;
+ if (!dest)
+ goto fail;
+
+ region16_init(&region);
+ if (!rfx_process_message(context, Data, (UINT32)Size, 0, 0, dest, FORMAT, (UINT32)stride,
+ IMG_HEIGHT, &region))
+ goto fail;
+
+ region16_clear(&region);
+ if (!rfx_process_message(context, Data, (UINT32)Size, 0, 0, dest, FORMAT, (UINT32)stride,
+ IMG_HEIGHT, &region))
+ goto fail;
+ region16_print(&region);
+
+ rc = 0;
+fail:
+ region16_uninit(&region);
+ rfx_context_free(context);
+ free(dest);
+ return rc;
+}
+
+static int test_MppcDecompressBellsRdp5(const uint8_t* Data, size_t Size)
+{
+ int rc = -1;
+ int status = 0;
+ UINT32 Flags = PACKET_AT_FRONT | PACKET_COMPRESSED | 1;
+ const BYTE* pSrcData = Data;
+ UINT32 SrcSize = (UINT32)Size;
+ UINT32 DstSize = 0;
+ const BYTE* pDstData = NULL;
+ MPPC_CONTEXT* mppc = mppc_context_new(1, FALSE);
+
+ if (!mppc)
+ return -1;
+
+ status = mppc_decompress(mppc, pSrcData, SrcSize, &pDstData, &DstSize, Flags);
+
+ if (status < 0)
+ goto fail;
+
+ rc = 0;
+
+fail:
+ mppc_context_free(mppc);
+ return rc;
+}
+
+static int test_MppcDecompressBellsRdp4(const uint8_t* Data, size_t Size)
+{
+ if (Size > UINT32_MAX)
+ return -1;
+ int rc = -1;
+ int status = 0;
+ UINT32 Flags = PACKET_AT_FRONT | PACKET_COMPRESSED | 0;
+ const BYTE* pSrcData = (const BYTE*)Data;
+ UINT32 SrcSize = (UINT32)Size;
+ UINT32 DstSize = 0;
+ const BYTE* pDstData = NULL;
+ MPPC_CONTEXT* mppc = mppc_context_new(0, FALSE);
+
+ if (!mppc)
+ return -1;
+
+ status = mppc_decompress(mppc, pSrcData, SrcSize, &pDstData, &DstSize, Flags);
+
+ if (status < 0)
+ goto fail;
+
+ rc = 0;
+fail:
+ mppc_context_free(mppc);
+ return rc;
+}
+
+static int test_MppcDecompressBufferRdp5(const uint8_t* Data, size_t Size)
+{
+ if (Size > UINT32_MAX)
+ return -1;
+ int rc = -1;
+ int status = 0;
+ UINT32 Flags = PACKET_AT_FRONT | PACKET_COMPRESSED | 1;
+ const BYTE* pSrcData = (const BYTE*)Data;
+ UINT32 SrcSize = (UINT32)Size;
+ UINT32 DstSize = 0;
+ const BYTE* pDstData = NULL;
+ MPPC_CONTEXT* mppc = mppc_context_new(1, FALSE);
+
+ if (!mppc)
+ return -1;
+
+ status = mppc_decompress(mppc, pSrcData, SrcSize, &pDstData, &DstSize, Flags);
+
+ if (status < 0)
+ goto fail;
+
+ rc = 0;
+fail:
+ mppc_context_free(mppc);
+ return rc;
+}
+
+static int TestFreeRDPCodecMppc(const uint8_t* Data, size_t Size)
+{
+ test_MppcDecompressBellsRdp5(Data, Size);
+ test_MppcDecompressBellsRdp4(Data, Size);
+ test_MppcDecompressBufferRdp5(Data, Size);
+ return 0;
+}
+
+static BOOL progressive_decode(const uint8_t* Data, size_t Size)
+{
+ BOOL res = FALSE;
+ int rc = 0;
+ BYTE* resultData = NULL;
+ UINT32 ColorFormat = PIXEL_FORMAT_BGRX32;
+ REGION16 invalidRegion = { 0 };
+ UINT32 scanline = 4240;
+ UINT32 width = 1060;
+ UINT32 height = 827;
+ if (Size > UINT32_MAX)
+ return FALSE;
+
+ PROGRESSIVE_CONTEXT* progressiveDec = progressive_context_new(FALSE);
+
+ region16_init(&invalidRegion);
+ if (!progressiveDec)
+ goto fail;
+
+ resultData = calloc(scanline, height);
+ if (!resultData)
+ goto fail;
+
+ rc = progressive_create_surface_context(progressiveDec, 0, width, height);
+ if (rc <= 0)
+ goto fail;
+
+ rc = progressive_decompress(progressiveDec, Data, (UINT32)Size, resultData, ColorFormat,
+ scanline, 0, 0, &invalidRegion, 0, 0);
+ if (rc < 0)
+ goto fail;
+
+ res = TRUE;
+fail:
+ region16_uninit(&invalidRegion);
+ progressive_context_free(progressiveDec);
+ free(resultData);
+ return res;
+}
+
+static int TestFreeRDPCodecProgressive(const uint8_t* Data, size_t Size)
+{
+ progressive_decode(Data, Size);
+ return 0;
+}
+
+static BOOL i_run_encode_decode(UINT16 bpp, BITMAP_INTERLEAVED_CONTEXT* encoder,
+ BITMAP_INTERLEAVED_CONTEXT* decoder, const uint8_t* Data,
+ size_t Size)
+{
+ BOOL rc2 = FALSE;
+ BOOL rc = 0;
+ const UINT32 w = 64;
+ const UINT32 h = 64;
+ const UINT32 x = 0;
+ const UINT32 y = 0;
+ const UINT32 format = PIXEL_FORMAT_RGBX32;
+ const size_t step = (w + 13ull) * 4ull;
+ const size_t SrcSize = step * h;
+ BYTE* pSrcData = calloc(1, SrcSize);
+ BYTE* pDstData = calloc(1, SrcSize);
+ BYTE* tmp = calloc(1, SrcSize);
+
+ WINPR_UNUSED(encoder);
+ if (!pSrcData || !pDstData || !tmp)
+ goto fail;
+
+ if (Size > UINT32_MAX)
+ goto fail;
+
+ winpr_RAND(pSrcData, SrcSize);
+
+ if (!bitmap_interleaved_context_reset(decoder))
+ goto fail;
+
+ rc = interleaved_decompress(decoder, Data, (UINT32)Size, w, h, bpp, pDstData, format, step, x,
+ y, w, h, NULL);
+
+ if (!rc)
+ goto fail;
+
+ rc2 = TRUE;
+fail:
+ free(pSrcData);
+ free(pDstData);
+ free(tmp);
+ return rc2;
+}
+
+static int TestFreeRDPCodecInterleaved(const uint8_t* Data, size_t Size)
+{
+ int rc = -1;
+ BITMAP_INTERLEAVED_CONTEXT* decoder = bitmap_interleaved_context_new(FALSE);
+
+ if (!decoder)
+ goto fail;
+
+ i_run_encode_decode(24, NULL, decoder, Data, Size);
+ i_run_encode_decode(16, NULL, decoder, Data, Size);
+ i_run_encode_decode(15, NULL, decoder, Data, Size);
+ rc = 0;
+fail:
+ bitmap_interleaved_context_free(decoder);
+ return rc;
+}
+
+static BOOL RunTestPlanar(BITMAP_PLANAR_CONTEXT* planar, const BYTE* srcBitmap,
+ const UINT32 srcFormat, const UINT32 dstFormat, const UINT32 width,
+ const UINT32 height, const uint8_t* Data, size_t Size)
+{
+ BOOL rc = FALSE;
+ WINPR_UNUSED(srcBitmap);
+ WINPR_UNUSED(srcFormat);
+ if (Size > UINT32_MAX)
+ return FALSE;
+ UINT32 dstSize = (UINT32)Size;
+ const BYTE* compressedBitmap = Data;
+ BYTE* decompressedBitmap =
+ (BYTE*)calloc(height, 1ull * width * FreeRDPGetBytesPerPixel(dstFormat));
+ rc = TRUE;
+
+ if (!decompressedBitmap)
+ goto fail;
+
+ if (!planar_decompress(planar, compressedBitmap, dstSize, width, height, decompressedBitmap,
+ dstFormat, 0, 0, 0, width, height, FALSE))
+ {
+ goto fail;
+ }
+
+ rc = TRUE;
+fail:
+ free(decompressedBitmap);
+ return rc;
+}
+
+static BOOL TestPlanar(const UINT32 format, const uint8_t* Data, size_t Size)
+{
+ BOOL rc = FALSE;
+ const DWORD planarFlags = PLANAR_FORMAT_HEADER_NA | PLANAR_FORMAT_HEADER_RLE;
+ BITMAP_PLANAR_CONTEXT* planar = freerdp_bitmap_planar_context_new(planarFlags, 64, 64);
+
+ if (!planar)
+ goto fail;
+
+ RunTestPlanar(planar, NULL, PIXEL_FORMAT_RGBX32, format, 64, 64, Data, Size);
+
+ RunTestPlanar(planar, NULL, PIXEL_FORMAT_RGB16, format, 32, 32, Data, Size);
+
+ rc = TRUE;
+fail:
+ freerdp_bitmap_planar_context_free(planar);
+ return rc;
+}
+
+static int TestFreeRDPCodecPlanar(const uint8_t* Data, size_t Size)
+{
+ TestPlanar(0, Data, Size);
+ return 0;
+}
+
+int LLVMFuzzerTestOneInput(const uint8_t* Data, size_t Size)
+{
+ if (Size < 4)
+ return 0;
+
+ int i = 0;
+ winpr_RAND(&i, sizeof(i));
+ i = i % 18;
+
+ if (i < 2)
+ TestFreeRDPCodecClear(Data, Size);
+ else if (i < 4)
+ TestFreeRDPCodecXCrush(Data, Size);
+ else if (i < 6)
+ TestFreeRDPCodecZGfx(Data, Size);
+ else if (i < 8)
+ TestFreeRDPCodecNCrush(Data, Size);
+ else if (i < 10)
+ TestFreeRDPCodecRemoteFX(Data, Size);
+ else if (i < 12)
+ TestFreeRDPCodecMppc(Data, Size);
+ else if (i < 14)
+ TestFreeRDPCodecProgressive(Data, Size);
+ else if (i < 16)
+ TestFreeRDPCodecInterleaved(Data, Size);
+ else if (i < 18)
+ TestFreeRDPCodecPlanar(Data, Size);
+
+ return 0;
+}
diff --git a/libfreerdp/codec/zgfx.c b/libfreerdp/codec/zgfx.c
index 881823a..b7ee275 100644
--- a/libfreerdp/codec/zgfx.c
+++ b/libfreerdp/codec/zgfx.c
@@ -227,7 +227,10 @@ static BOOL zgfx_decompress_segment(ZGFX_CONTEXT* zgfx, wStream* stream, size_t
BYTE* pbSegment = NULL;
size_t cbSegment = 0;
- if (!zgfx || !stream || (segmentSize < 2))
+ WINPR_ASSERT(zgfx);
+ WINPR_ASSERT(stream);
+
+ if (segmentSize < 2)
return FALSE;
cbSegment = segmentSize - 1;
@@ -346,8 +349,9 @@ static BOOL zgfx_decompress_segment(ZGFX_CONTEXT* zgfx, wStream* stream, size_t
if (count > sizeof(zgfx->OutputBuffer) - zgfx->OutputCount)
return FALSE;
-
- if (count > zgfx->cBitsRemaining / 8)
+ else if (count > zgfx->cBitsRemaining / 8)
+ return FALSE;
+ else if (zgfx->pbInputCurrent + count > zgfx->pbInputEnd)
return FALSE;
CopyMemory(&(zgfx->OutputBuffer[zgfx->OutputCount]), zgfx->pbInputCurrent,
@@ -386,8 +390,8 @@ int zgfx_decompress(ZGFX_CONTEXT* zgfx, const BYTE* pSrcData, UINT32 SrcSize, BY
wStream sbuffer = { 0 };
wStream* stream = Stream_StaticConstInit(&sbuffer, pSrcData, SrcSize);
- if (!stream)
- return -1;
+ WINPR_ASSERT(zgfx);
+ WINPR_ASSERT(stream);
if (!Stream_CheckAndLogRequiredLength(TAG, stream, 1))
goto fail;