diff options
Diffstat (limited to 'libfreerdp/codec')
-rw-r--r-- | libfreerdp/codec/clear.c | 2 | ||||
-rw-r--r-- | libfreerdp/codec/interleaved.c | 2 | ||||
-rw-r--r-- | libfreerdp/codec/ncrush.c | 55 | ||||
-rw-r--r-- | libfreerdp/codec/nsc.c | 4 | ||||
-rw-r--r-- | libfreerdp/codec/planar.c | 54 | ||||
-rw-r--r-- | libfreerdp/codec/rfx.c | 2 | ||||
-rw-r--r-- | libfreerdp/codec/rfx_rlgr.c | 42 | ||||
-rw-r--r-- | libfreerdp/codec/test/CMakeLists.txt | 6 | ||||
-rw-r--r-- | libfreerdp/codec/test/TestFuzzCodecs.c | 480 | ||||
-rw-r--r-- | libfreerdp/codec/zgfx.c | 14 |
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(®ion); + if (!rfx_process_message(context, Data, (UINT32)Size, 0, 0, dest, FORMAT, (UINT32)stride, + IMG_HEIGHT, ®ion)) + goto fail; + + region16_clear(®ion); + if (!rfx_process_message(context, Data, (UINT32)Size, 0, 0, dest, FORMAT, (UINT32)stride, + IMG_HEIGHT, ®ion)) + goto fail; + region16_print(®ion); + + rc = 0; +fail: + region16_uninit(®ion); + 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; |