diff --git a/allocations-fix-growth-of-preallocated-buffers.patch b/allocations-fix-growth-of-preallocated-buffers.patch new file mode 100644 index 0000000..834839d --- /dev/null +++ b/allocations-fix-growth-of-preallocated-buffers.patch @@ -0,0 +1,36 @@ +From 563a19a683d6e0e7b1218e4558a0fb75333b5b34 Mon Sep 17 00:00:00 2001 +From: Ondrej Holy +Date: Tue, 28 Apr 2026 04:14:23 +0000 +Subject: [PATCH] [allocations] fix growth of preallocated buffers + +Partial backport of commit 118afc0b954ba9d5632b7836ad24e454555ed113. + +Only the `Stream_EnsureCapacity` fix is included. Other changes from +the upstream commit (`sizeof(WCHAR)` replacements, collection growth +patterns, environment block handling) are unrelated to CVE-2026-27951. + +Made-with: Cursor +--- + winpr/libwinpr/utils/stream.c | 6 ++++-- + 1 file changed, 4 insertions(+), 2 deletions(-) + +diff --git a/winpr/libwinpr/utils/stream.c b/winpr/libwinpr/utils/stream.c +index cc119c7..cb145b0 100644 +--- a/winpr/libwinpr/utils/stream.c ++++ b/winpr/libwinpr/utils/stream.c +@@ -40,8 +40,10 @@ BOOL Stream_EnsureCapacity(wStream* s, size_t size) + + do + { +- new_capacity *= 2; +- } while (new_capacity < size); ++ if (new_capacity > SIZE_MAX - 128ull) ++ return FALSE; ++ new_capacity += 128ull; ++ } while (new_capacity <= size); + + position = Stream_GetPosition(s); + +-- +2.53.0 + diff --git a/cache-bitmap-initialize-overallocated-bitmap-cache-extra-slot.patch b/cache-bitmap-initialize-overallocated-bitmap-cache-extra-slot.patch new file mode 100644 index 0000000..f301fb7 --- /dev/null +++ b/cache-bitmap-initialize-overallocated-bitmap-cache-extra-slot.patch @@ -0,0 +1,52 @@ +From 23320a6d5f2e1c8a9b7d6f4e3c2a1b0987654321 Mon Sep 17 00:00:00 2001 +From: Ondrej Holy +Date: Tue, 28 Apr 2026 04:25:58 +0000 +Subject: [PATCH] [cache,bitmap] initialize overallocated bitmap cache extra + slot + +Backport of commit 8270e0bb3d6726c947d57c93ba9caa92a052b557. + +Adjusted hunk offsets for 2.11.7. + +Made-with: Cursor +--- + libfreerdp/cache/bitmap.c | 16 +++++++++++++++- + 1 file changed, 15 insertions(+), 1 deletion(-) + +diff --git a/libfreerdp/cache/bitmap.c b/libfreerdp/cache/bitmap.c +index b8a4f21..23320a6 100644 +--- a/libfreerdp/cache/bitmap.c ++++ b/libfreerdp/cache/bitmap.c +@@ -303,6 +303,19 @@ rdpBitmapCache* bitmap_cache_new(rdpSettings* settings) + cell->number = nr; + } + ++ /* initialize the overallocated extra slot for old RDP servers that send ++ * cacheId == maxCells; use a minimal allocation since no protocol-negotiated ++ * capacity exists for this slot */ ++ { ++ BITMAP_V2_CELL* extra = &bitmapCache->cells[bitmapCache->maxCells]; ++ /* allocate an extra entry for BITMAP_CACHE_WAITING_LIST_INDEX */ ++ extra->entries = (rdpBitmap**)calloc(1, sizeof(rdpBitmap*)); ++ ++ if (!extra->entries) ++ goto fail; ++ extra->number = 0; ++ } ++ + return bitmapCache; + fail: + +@@ -315,7 +328,8 @@ void bitmap_cache_free(rdpBitmapCache* bitmapCache) + if (bitmapCache) + { + UINT32 i; +- for (i = 0; i < bitmapCache->maxCells; i++) ++ /* iterate through maxCells + 1 to also free the overallocated extra slot */ ++ for (i = 0; i <= bitmapCache->maxCells; i++) + { + UINT32 j; + BITMAP_V2_CELL* cell = &bitmapCache->cells[i]; +-- +2.53.0 + diff --git a/cache-bitmap-overallocate-bitmap-cache.patch b/cache-bitmap-overallocate-bitmap-cache.patch new file mode 100644 index 0000000..d5f0edc --- /dev/null +++ b/cache-bitmap-overallocate-bitmap-cache.patch @@ -0,0 +1,33 @@ +From 608c5d40f6ab4cabd4d5793b2d641f401e146233 Mon Sep 17 00:00:00 2001 +From: Ondrej Holy +Date: Tue, 28 Apr 2026 04:25:52 +0000 +Subject: [PATCH] [cache,bitmap] overallocate bitmap cache + +Backport of commit ffad58fd2b329efd81a3239e9d7e3c927b8e503f. + +Adjusted hunk offsets for 2.11.7. + +Made-with: Cursor +--- + libfreerdp/cache/bitmap.c | 5 ++++- + 1 file changed, 4 insertions(+), 1 deletion(-) + +diff --git a/libfreerdp/cache/bitmap.c b/libfreerdp/cache/bitmap.c +index 0ce2599..b8a4f21 100644 +--- a/libfreerdp/cache/bitmap.c ++++ b/libfreerdp/cache/bitmap.c +@@ -281,8 +281,10 @@ rdpBitmapCache* bitmap_cache_new(rdpSettings* settings) + bitmapCache->settings = settings; + bitmapCache->update = ((freerdp*)settings->instance)->update; + bitmapCache->context = bitmapCache->update->context; ++ ++ /* overallocate by 1. older RDP servers do send a off by 1 cache index. */ + bitmapCache->cells = +- (BITMAP_V2_CELL*)calloc(settings->BitmapCacheV2NumCells, sizeof(BITMAP_V2_CELL)); ++ (BITMAP_V2_CELL*)calloc(settings->BitmapCacheV2NumCells + 1ull, sizeof(BITMAP_V2_CELL)); + + if (!bitmapCache->cells) + goto fail; +-- +2.53.0 + diff --git a/client-x11-fix-xf_rail_window_common-cleanup.patch b/client-x11-fix-xf_rail_window_common-cleanup.patch new file mode 100644 index 0000000..0e4b155 --- /dev/null +++ b/client-x11-fix-xf_rail_window_common-cleanup.patch @@ -0,0 +1,45 @@ +From 562cc4b33257074d1d50535138998bc81796d447 Mon Sep 17 00:00:00 2001 +From: Ondrej Holy +Date: Mon, 27 Apr 2026 19:00:00 +0000 +Subject: [PATCH] [client,x11] fix xf_rail_window_common cleanup + +Backport of commit b4f0f0a18fe53aa8d47d062f91471f4e9c5e0d51. + +Adjusted hunk offsets for 2.11.7. + +Made-with: Cursor +--- + client/X11/xf_rail.c | 6 +----- + 1 file changed, 1 insertion(+), 5 deletions(-) + +diff --git a/client/X11/xf_rail.c b/client/X11/xf_rail.c +index 242c54b..4c4f47d 100644 +--- a/client/X11/xf_rail.c ++++ b/client/X11/xf_rail.c +@@ -260,11 +260,10 @@ static void window_state_log_style(wLog* log, const WINDOW_STATE_ORDER* windowSt + static BOOL xf_rail_window_common(rdpContext* context, const WINDOW_ORDER_INFO* orderInfo, + const WINDOW_STATE_ORDER* windowState) + { +- xfAppWindow* appWindow = NULL; + xfContext* xfc = (xfContext*)context; + UINT32 fieldFlags = orderInfo->fieldFlags; + BOOL position_or_size_updated = FALSE; +- appWindow = xf_rail_get_window(xfc, orderInfo->windowId); ++ xfAppWindow* appWindow = xf_rail_get_window(xfc, orderInfo->windowId); + + if (fieldFlags & WINDOW_ORDER_STATE_NEW) + { +@@ -310,10 +309,7 @@ static BOOL xf_rail_window_common(rdpContext* context, const WINDOW_ORDER_INFO* + } + + if (!appWindow->title) +- { +- free(appWindow); + return FALSE; +- } + + xf_AppWindowInit(xfc, appWindow); + } +-- +2.53.0 + diff --git a/codec-clear-update-clear_glyph_entry-count-after-alloc.patch b/codec-clear-update-clear_glyph_entry-count-after-alloc.patch new file mode 100644 index 0000000..ebe4c6e --- /dev/null +++ b/codec-clear-update-clear_glyph_entry-count-after-alloc.patch @@ -0,0 +1,57 @@ +From 947feeadfddf01d30dda5aa16ebc335bfcc23ae0 Mon Sep 17 00:00:00 2001 +From: Ondrej Holy +Date: Tue, 29 Apr 2026 13:42:00 +0000 +Subject: [PATCH] [codec,clear] Update CLEAR_GLYPH_ENTRY::count after alloc + +Backport of commit c49d1ad43b8c7b32794d0250f2623c2dccd7ef25. + +Adapted for 2.11.7: uses GetBytesPerPixel instead of FreeRDPGetBytesPerPixel, +plain realloc instead of winpr_aligned_recalloc; overflow checks match upstream +(size_t count, hlimit / exceeded logging); glyphEntry->count set via (UINT32)cast +after successful realloc. + +Made-with: Cursor +--- + libfreerdp/codec/clear.c | 22 +++++++++++++++++----- + 1 file changed, 17 insertions(+), 5 deletions(-) + +diff --git a/libfreerdp/codec/clear.c b/libfreerdp/codec/clear.c +--- a/libfreerdp/codec/clear.c ++++ b/libfreerdp/codec/clear.c +@@ -979,20 +979,31 @@ + { + const UINT32 bpp = GetBytesPerPixel(clear->format); + CLEAR_GLYPH_ENTRY* glyphEntry = &(clear->GlyphCache[glyphIndex]); +- glyphEntry->count = nWidth * nHeight; ++ const size_t count = 1ull * nWidth * nHeight; ++ const size_t hlimit = SIZE_MAX / ((nWidth > 0) ? nWidth : 1); ++ if ((nWidth == 0) || (nHeight == 0) || (hlimit < nHeight)) ++ { ++ const char* exceeded = (hlimit < nHeight) ? "within" : "outside"; ++ WLog_ERR(TAG, ++ "CLEARCODEC_FLAG_GLYPH_INDEX: nWidth=%" PRIu32 ", nHeight=%" PRIu32 ++ ", nWidth * nHeight is %s allowed range", ++ nWidth, nHeight, exceeded); ++ return FALSE; ++ } + +- if (glyphEntry->count > glyphEntry->size) ++ if (count > glyphEntry->size) + { + BYTE* tmp; +- tmp = realloc(glyphEntry->pixels, 1ull * glyphEntry->count * bpp); ++ tmp = realloc(glyphEntry->pixels, 1ull * count * bpp); + + if (!tmp) + { +- WLog_ERR(TAG, "glyphEntry->pixels realloc %" PRIu32 " failed!", +- glyphEntry->count * bpp); ++ WLog_ERR(TAG, "glyphEntry->pixels realloc %" PRIuz " failed!", ++ count * bpp); + return FALSE; + } + ++ glyphEntry->count = (UINT32)count; + glyphEntry->size = glyphEntry->count; + glyphEntry->pixels = (UINT32*)tmp; + } diff --git a/codec-dsp-add-format-checks.patch b/codec-dsp-add-format-checks.patch new file mode 100644 index 0000000..5a23870 --- /dev/null +++ b/codec-dsp-add-format-checks.patch @@ -0,0 +1,218 @@ +From 74ff8a097c3606710327e3af2863aa52d2dac79e Mon Sep 17 00:00:00 2001 +From: Ondrej Holy +Date: Tue, 29 Apr 2026 15:10:00 +0000 +Subject: [PATCH] [codec,dsp] add format checks + +Backport of commit 03b48b3601d867afccac1cdc6081de7a275edce7. + +Adapted for 2.11.x: uses `context->format` instead of +`context->common.format`, no `WINPR_RESTRICT`, added +`#include `. Kept C89 declaration style in +`freerdp_dsp_decode_mp3` and `freerdp_dsp_encode_mp3`. Omitted +LAME/mp3 variable modernization. DVI_ADPCM block in +`freerdp_dsp_context_reset` omits FramesPerPacket sizing from upstream 3.x +but adds `Stream_SetPosition(context->buffer, 0)` after validation (same +intent as upstream `Stream_ResetPosition`). `freerdp_dsp_decode_ima_adpcm` +matches upstream order: validate format before locals; `out_size` uses +`size * 4ull`. `Stream_BufferAs` replaced with +`Stream_Buffer` cast. `nullptr` replaced with `NULL`. + +Made-with: Cursor +--- + libfreerdp/codec/dsp.c | 96 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 93 insertions(+), 4 deletions(-) + +diff --git a/libfreerdp/codec/dsp.c b/libfreerdp/codec/dsp.c +index a18d044..e79911d 100644 +--- a/libfreerdp/codec/dsp.c ++++ b/libfreerdp/codec/dsp.c +@@ -27,6 +27,7 @@ + #include + + #include ++#include + + #include + #include +@@ -329,13 +330,30 @@ + return (UINT16)d; + } + ++static BOOL valid_ima_adpcm_format(const FREERDP_DSP_CONTEXT* context) ++{ ++ WINPR_ASSERT(context); ++ if (context->format.wFormatTag != WAVE_FORMAT_DVI_ADPCM) ++ return FALSE; ++ if (context->format.nBlockAlign <= 4ULL) ++ return FALSE; ++ if (context->format.nChannels < 1) ++ return FALSE; ++ if (context->format.wBitsPerSample == 0) ++ return FALSE; ++ return TRUE; ++} ++ + static BOOL freerdp_dsp_decode_ima_adpcm(FREERDP_DSP_CONTEXT* context, const BYTE* src, size_t size, + wStream* out) + { ++ if (!valid_ima_adpcm_format(context)) ++ return FALSE; ++ + BYTE* dst; + BYTE sample; + UINT16 decoded; +- size_t out_size = size * 4; ++ size_t out_size = size * 4ull; + UINT32 channel; + const UINT32 block_size = context->format.nBlockAlign; + const UINT32 channels = context->format.nChannels; +@@ -464,6 +482,20 @@ + #endif + + #if defined(WITH_LAME) ++static BOOL valid_mp3_format(const FREERDP_DSP_CONTEXT* context) ++{ ++ WINPR_ASSERT(context); ++ if (context->format.wFormatTag != WAVE_FORMAT_MPEGLAYER3) ++ return FALSE; ++ if (context->format.nChannels < 1) ++ return FALSE; ++ if (context->format.wBitsPerSample == 0) ++ return FALSE; ++ if (context->format.nSamplesPerSec == 0) ++ return FALSE; ++ return TRUE; ++} ++ + static BOOL freerdp_dsp_decode_mp3(FREERDP_DSP_CONTEXT* context, const BYTE* src, size_t size, + wStream* out) + { +@@ -474,6 +506,8 @@ + + if (!context || !src || !out) + return FALSE; ++ if (!valid_mp3_format(context)) ++ return FALSE; + + buffer_size = 2 * context->format.nChannels * context->format.nSamplesPerSec; + +@@ -509,6 +543,9 @@ + if (!context || !src || !out) + return FALSE; + ++ if (!valid_mp3_format(context)) ++ return FALSE; ++ + samples_per_channel = size / context->format.nChannels / context->format.wBitsPerSample / 8; + + /* Ensure worst case buffer size for mp3 stream taken from LAME header */ +@@ -716,6 +753,10 @@ + BYTE encoded; + size_t out_size; + size_t align; ++ ++ if (!valid_ima_adpcm_format(context)) ++ return FALSE; ++ + out_size = size / 2; + + if (!Stream_EnsureRemainingCapacity(out, size)) +@@ -788,6 +829,20 @@ + + static const INT32 ms_adpcm_coeffs2[7] = { 0, -256, 0, 64, 0, -208, -232 }; + ++static BOOL valid_ms_adpcm_format(const FREERDP_DSP_CONTEXT* context) ++{ ++ WINPR_ASSERT(context); ++ if (context->format.wFormatTag != WAVE_FORMAT_ADPCM) ++ return FALSE; ++ if (context->format.nBlockAlign <= 4ULL) ++ return FALSE; ++ if (context->format.nChannels < 1) ++ return FALSE; ++ if (context->format.wBitsPerSample == 0) ++ return FALSE; ++ return TRUE; ++} ++ + static INLINE INT16 freerdp_dsp_decode_ms_adpcm_sample(ADPCM* adpcm, BYTE sample, int channel) + { + INT8 nibble; +@@ -819,6 +874,9 @@ + BYTE* dst; + BYTE sample; + const size_t out_size = size * 4; ++ ++ if (!valid_ms_adpcm_format(context)) ++ return FALSE; + const UINT32 channels = context->format.nChannels; + const UINT32 block_size = context->format.nBlockAlign; + +@@ -947,6 +1005,10 @@ + INT32 sample; + size_t out_size; + const size_t step = 8 + ((context->format.nChannels > 1) ? 4 : 0); ++ ++ if (!valid_ms_adpcm_format(context)) ++ return FALSE; ++ + out_size = size / 2; + + if (!Stream_EnsureRemainingCapacity(out, size)) +@@ -1308,6 +1370,28 @@ + return FALSE; + + context->format = *targetFormat; ++ ++ switch (context->format.wFormatTag) ++ { ++#if defined(WITH_LAME) ++ case WAVE_FORMAT_MPEGLAYER3: ++ if (!valid_mp3_format(context)) ++ return FALSE; ++ break; ++#endif ++ case WAVE_FORMAT_ADPCM: ++ if (!valid_ms_adpcm_format(context)) ++ return FALSE; ++ break; ++ case WAVE_FORMAT_DVI_ADPCM: ++ if (!valid_ima_adpcm_format(context)) ++ return FALSE; ++ Stream_SetPosition(context->buffer, 0); ++ break; ++ default: ++ break; ++ } ++ + #if defined(WITH_FAAD2) + context->faadSetup = FALSE; + #endif +@@ -1328,19 +1412,24 @@ + + cfg = faacEncGetCurrentConfiguration(context->faac); + cfg->bitRate = 10000; +- faacEncSetConfiguration(context->faac, cfg); ++ { ++ const int frc = faacEncSetConfiguration(context->faac, cfg); ++ if (frc <= 0) ++ return FALSE; ++ } + } + + #endif + #if defined(WITH_SOXR) + { + soxr_io_spec_t iospec = soxr_io_spec(SOXR_INT16, SOXR_INT16); +- soxr_error_t error; ++ soxr_error_t error = NULL; ++ + soxr_delete(context->sox); + context->sox = soxr_create(context->format.nSamplesPerSec, targetFormat->nSamplesPerSec, + targetFormat->nChannels, &error, &iospec, NULL, NULL); + +- if (!context->sox || (error != 0)) ++ if (!context->sox || (error != NULL)) + return FALSE; + } + #endif diff --git a/codec-dsp-fix-array-bounds-checks.patch b/codec-dsp-fix-array-bounds-checks.patch new file mode 100644 index 0000000..0754fec --- /dev/null +++ b/codec-dsp-fix-array-bounds-checks.patch @@ -0,0 +1,237 @@ +From aaf333862fb54d4cfc19a1866b76500d86679719 Mon Sep 17 00:00:00 2001 +From: Ondrej Holy +Date: Tue, 28 Apr 2026 04:41:15 +0000 +Subject: [PATCH] [codec,dsp] fix array bounds checks + +Backport of commit 16df2300e1e3f5a51f68fb1626429e58b531b7c8. + +Adapted for 2.11.x: uses C89 declaration style (variables declared +at top of scope, new variables wrapped in blocks), no +`WINPR_RESTRICT`, kept `int channel` in decode function, +`dsp_encode_ima_adpcm_sample` keeps `int channel`. + +Made-with: Cursor +--- + libfreerdp/codec/dsp.c | 91 +++++++++++++++++++++++++++++++++++++----- + 1 file changed, 82 insertions(+), 9 deletions(-) + +diff --git a/libfreerdp/codec/dsp.c b/libfreerdp/codec/dsp.c +index e79911d..6d4f780 100644 +--- a/libfreerdp/codec/dsp.c ++++ b/libfreerdp/codec/dsp.c +@@ -297,7 +297,16 @@ static UINT16 dsp_decode_ima_adpcm_sample(ADPCM* adpcm, unsigned int channel, BY + { + INT32 ss; + INT32 d; +- ss = ima_step_size_table[adpcm->ima.last_step[channel]]; ++ INT16 offset; ++ ++ WINPR_ASSERT(channel < ARRAYSIZE(adpcm->ima.last_step)); ++ WINPR_ASSERT(channel < ARRAYSIZE(adpcm->ima.last_sample)); ++ ++ offset = adpcm->ima.last_step[channel]; ++ WINPR_ASSERT(offset >= 0); ++ WINPR_ASSERT(offset < ARRAYSIZE(ima_step_size_table)); ++ ++ ss = ima_step_size_table[offset]; + d = (ss >> 3); + + if (sample & 1) +@@ -320,6 +329,8 @@ static UINT16 dsp_decode_ima_adpcm_sample(ADPCM* adpcm, unsigned int channel, BY + d = 32767; + + adpcm->ima.last_sample[channel] = (INT16)d; ++ ++ WINPR_ASSERT(sample < ARRAYSIZE(ima_step_index_table)); + adpcm->ima.last_step[channel] += ima_step_index_table[sample]; + + if (adpcm->ima.last_step[channel] < 0) +@@ -368,6 +379,9 @@ static BOOL freerdp_dsp_decode_ima_adpcm(FREERDP_DSP_CONTEXT* context, const BYT + { + if (size % block_size == 0) + { ++ if (size < 4) ++ return FALSE; ++ + context->adpcm.ima.last_sample[0] = + (INT16)(((UINT16)(*src)) | (((UINT16)(*(src + 1))) << 8)); + context->adpcm.ima.last_step[0] = (INT16)(*(src + 2)); +@@ -377,6 +391,8 @@ static BOOL freerdp_dsp_decode_ima_adpcm(FREERDP_DSP_CONTEXT* context, const BYT + + if (channels > 1) + { ++ if (size < 4) ++ return FALSE; + context->adpcm.ima.last_sample[1] = + (INT16)(((UINT16)(*src)) | (((UINT16)(*(src + 1))) << 8)); + context->adpcm.ima.last_step[1] = (INT16)(*(src + 2)); +@@ -388,6 +404,8 @@ static BOOL freerdp_dsp_decode_ima_adpcm(FREERDP_DSP_CONTEXT* context, const BYT + + if (channels > 1) + { ++ if (size < 8) ++ return FALSE; + for (i = 0; i < 8; i++) + { + channel = (i < 4 ? 0 : 1); +@@ -407,6 +425,8 @@ static BOOL freerdp_dsp_decode_ima_adpcm(FREERDP_DSP_CONTEXT* context, const BYT + } + else + { ++ if (size < 1) ++ return FALSE; + sample = ((*src) & 0x0f); + decoded = dsp_decode_ima_adpcm_sample(&context->adpcm, 0, sample); + *dst++ = (decoded & 0xFF); +@@ -687,7 +707,16 @@ static BYTE dsp_encode_ima_adpcm_sample(ADPCM* adpcm, int channel, INT16 sample) + INT32 ss; + BYTE enc; + INT32 diff; +- ss = ima_step_size_table[adpcm->ima.last_step[channel]]; ++ INT16 offset; ++ ++ WINPR_ASSERT(channel < ARRAYSIZE(adpcm->ima.last_step)); ++ WINPR_ASSERT(channel < ARRAYSIZE(adpcm->ima.last_sample)); ++ ++ offset = adpcm->ima.last_step[channel]; ++ WINPR_ASSERT(offset >= 0); ++ WINPR_ASSERT(offset < ARRAYSIZE(ima_step_size_table)); ++ ++ ss = ima_step_size_table[offset]; + d = e = sample - adpcm->ima.last_sample[channel]; + diff = ss >> 3; + enc = 0; +@@ -733,6 +762,8 @@ static BYTE dsp_encode_ima_adpcm_sample(ADPCM* adpcm, int channel, INT16 sample) + diff = 32767; + + adpcm->ima.last_sample[channel] = (INT16)diff; ++ ++ WINPR_ASSERT(enc < ARRAYSIZE(ima_step_index_table)); + adpcm->ima.last_step[channel] += ima_step_index_table[enc]; + + if (adpcm->ima.last_step[channel] < 0) +@@ -847,10 +878,24 @@ static INLINE INT16 freerdp_dsp_decode_ms_adpcm_sample(ADPCM* adpcm, BYTE sample + { + INT8 nibble; + INT32 presample; ++ BYTE predictor; ++ INT32 coeff1 = 0; ++ INT32 coeff2 = 0; ++ ++ WINPR_ASSERT(channel < ARRAYSIZE(adpcm->ms.sample1)); ++ WINPR_ASSERT(channel < ARRAYSIZE(adpcm->ms.sample2)); ++ WINPR_ASSERT(channel < ARRAYSIZE(adpcm->ms.delta)); ++ WINPR_ASSERT(channel < ARRAYSIZE(adpcm->ms.predictor)); ++ + nibble = (sample & 0x08 ? (INT8)sample - 16 : (INT8)sample); +- presample = ((adpcm->ms.sample1[channel] * ms_adpcm_coeffs1[adpcm->ms.predictor[channel]]) + +- (adpcm->ms.sample2[channel] * ms_adpcm_coeffs2[adpcm->ms.predictor[channel]])) / +- 256; ++ predictor = adpcm->ms.predictor[channel]; ++ if (predictor < ARRAYSIZE(ms_adpcm_coeffs1)) ++ coeff1 = ms_adpcm_coeffs1[predictor]; ++ ++ if (predictor < ARRAYSIZE(ms_adpcm_coeffs2)) ++ coeff2 = ms_adpcm_coeffs2[predictor]; ++ presample = ++ ((adpcm->ms.sample1[channel] * coeff1) + (adpcm->ms.sample2[channel] * coeff2)) / 256; + presample += nibble * adpcm->ms.delta[channel]; + + if (presample > 32767) +@@ -860,7 +905,14 @@ static INLINE INT16 freerdp_dsp_decode_ms_adpcm_sample(ADPCM* adpcm, BYTE sample + + adpcm->ms.sample2[channel] = adpcm->ms.sample1[channel]; + adpcm->ms.sample1[channel] = presample; +- adpcm->ms.delta[channel] = adpcm->ms.delta[channel] * ms_adpcm_adaptation_table[sample] / 256; ++ ++ { ++ INT32 tableval = 0; ++ if (sample < ARRAYSIZE(ms_adpcm_adaptation_table)) ++ tableval = ms_adpcm_adaptation_table[sample]; ++ ++ adpcm->ms.delta[channel] = adpcm->ms.delta[channel] * tableval / 256; ++ } + + if (adpcm->ms.delta[channel] < 16) + adpcm->ms.delta[channel] = 16; +@@ -891,6 +943,9 @@ static BOOL freerdp_dsp_decode_ms_adpcm(FREERDP_DSP_CONTEXT* context, const BYTE + { + if (channels > 1) + { ++ if (size < 14) ++ return FALSE; ++ + context->adpcm.ms.predictor[0] = *src++; + context->adpcm.ms.predictor[1] = *src++; + context->adpcm.ms.delta[0] = read_int16(src); +@@ -917,6 +972,9 @@ static BOOL freerdp_dsp_decode_ms_adpcm(FREERDP_DSP_CONTEXT* context, const BYTE + } + else + { ++ if (size < 7) ++ return FALSE; ++ + context->adpcm.ms.predictor[0] = *src++; + context->adpcm.ms.delta[0] = read_int16(src); + src += 2; +@@ -934,12 +992,16 @@ static BOOL freerdp_dsp_decode_ms_adpcm(FREERDP_DSP_CONTEXT* context, const BYTE + + if (channels > 1) + { ++ if (size < 1) ++ return FALSE; + sample = *src++; + size--; + write_int16(dst, freerdp_dsp_decode_ms_adpcm_sample(&context->adpcm, sample >> 4, 0)); + dst += 2; + write_int16(dst, freerdp_dsp_decode_ms_adpcm_sample(&context->adpcm, sample & 0x0F, 1)); + dst += 2; ++ if (size < 1) ++ return FALSE; + sample = *src++; + size--; + write_int16(dst, freerdp_dsp_decode_ms_adpcm_sample(&context->adpcm, sample >> 4, 0)); +@@ -949,6 +1011,8 @@ static BOOL freerdp_dsp_decode_ms_adpcm(FREERDP_DSP_CONTEXT* context, const BYTE + } + else + { ++ if (size < 1) ++ return FALSE; + sample = *src++; + size--; + write_int16(dst, freerdp_dsp_decode_ms_adpcm_sample(&context->adpcm, sample >> 4, 0)); +@@ -962,10 +1026,16 @@ static BOOL freerdp_dsp_decode_ms_adpcm(FREERDP_DSP_CONTEXT* context, const BYTE + return TRUE; + } + +-static BYTE freerdp_dsp_encode_ms_adpcm_sample(ADPCM* adpcm, INT32 sample, int channel) ++static BYTE freerdp_dsp_encode_ms_adpcm_sample(ADPCM* adpcm, INT32 sample, size_t channel) + { + INT32 presample; + INT32 errordelta; ++ ++ WINPR_ASSERT(channel < ARRAYSIZE(adpcm->ms.sample1)); ++ WINPR_ASSERT(channel < ARRAYSIZE(adpcm->ms.sample2)); ++ WINPR_ASSERT(channel < ARRAYSIZE(adpcm->ms.delta)); ++ WINPR_ASSERT(channel < ARRAYSIZE(adpcm->ms.predictor)); ++ + presample = ((adpcm->ms.sample1[channel] * ms_adpcm_coeffs1[adpcm->ms.predictor[channel]]) + + (adpcm->ms.sample2[channel] * ms_adpcm_coeffs2[adpcm->ms.predictor[channel]])) / + 256; +@@ -988,8 +1058,11 @@ static BYTE freerdp_dsp_encode_ms_adpcm_sample(ADPCM* adpcm, INT32 sample, int c + + adpcm->ms.sample2[channel] = adpcm->ms.sample1[channel]; + adpcm->ms.sample1[channel] = presample; +- adpcm->ms.delta[channel] = +- adpcm->ms.delta[channel] * ms_adpcm_adaptation_table[(((BYTE)errordelta) & 0x0F)] / 256; ++ { ++ const size_t offset = (((BYTE)errordelta) & 0x0F); ++ WINPR_ASSERT(offset < ARRAYSIZE(ms_adpcm_adaptation_table)); ++ adpcm->ms.delta[channel] = adpcm->ms.delta[channel] * ms_adpcm_adaptation_table[offset] / 256; ++ } + + if (adpcm->ms.delta[channel] < 16) + adpcm->ms.delta[channel] = 16; +-- +2.53.0 + diff --git a/freerdp.spec b/freerdp.spec index 26e78ea..dff75c2 100644 --- a/freerdp.spec +++ b/freerdp.spec @@ -27,7 +27,7 @@ Name: freerdp Version: 2.11.7 -Release: 7%{?dist} +Release: 8%{?dist} Epoch: 2 Summary: Free implementation of the Remote Desktop Protocol (RDP) License: ASL 2.0 @@ -154,6 +154,33 @@ Patch32: codec-clear-update-CLEAR_VBAR_ENTRY-size-after-alloc.patch Patch33: codec-progressive-fail-progressive_rfx_quant_sub-on-invalid-values.patch Patch34: codec-progressive-fix-underflow-guard-in-progressive_rfx_quant_sub.patch +# CVE-2026-26986 +# https://github.com/FreeRDP/FreeRDP/commit/b4f0f0a18fe53aa8d47d062f91471f4e9c5e0d51 +Patch35: client-x11-fix-xf_rail_window_common-cleanup.patch + +# CVE-2026-27951 +# https://github.com/FreeRDP/FreeRDP/commit/118afc0b954ba9d5632b7836ad24e454555ed113 +Patch36: allocations-fix-growth-of-preallocated-buffers.patch + +# CVE-2026-29775 +# https://github.com/FreeRDP/FreeRDP/commit/ffad58fd2b329efd81a3239e9d7e3c927b8e503f +# https://github.com/FreeRDP/FreeRDP/commit/8270e0bb3d6726c947d57c93ba9caa92a052b557 +Patch37: cache-bitmap-overallocate-bitmap-cache.patch +Patch38: cache-bitmap-initialize-overallocated-bitmap-cache-extra-slot.patch + +# CVE-2026-31884 +# https://github.com/FreeRDP/FreeRDP/commit/03b48b3601d867afccac1cdc6081de7a275edce7 +Patch39: codec-dsp-add-format-checks.patch + +# CVE-2026-31883 +# CVE-2026-31885 +# https://github.com/FreeRDP/FreeRDP/commit/16df2300e1e3f5a51f68fb1626429e58b531b7c8 +Patch40: codec-dsp-fix-array-bounds-checks.patch + +# CVE-2026-33985 +# https://github.com/FreeRDP/FreeRDP/commit/c49d1ad43b8c7b32794d0250f2623c2dccd7ef25 +Patch41: codec-clear-update-clear_glyph_entry-count-after-alloc.patch + BuildRequires: gcc BuildRequires: gcc-c++ BuildRequires: alsa-lib-devel @@ -411,6 +438,17 @@ find %{buildroot} -name "*.a" -delete %{_libdir}/pkgconfig/winpr-tools2.pc %changelog +* Tue Apr 28 2026 Ondrej Holy - 2:2.11.7-8 +- Fix double free in xf_rail_window_common cleanup (CVE-2026-26986) +- Fix growth of preallocated buffers (CVE-2026-27951) +- Fix heap-buffer-overflow in bitmap_cache_put (CVE-2026-29775) +- Add DSP format checks (CVE-2026-31884) +- Fix DSP array bounds checks (CVE-2026-31883) +- Fix DSP array bounds checks (CVE-2026-31885) +- Update CLEAR_GLYPH_ENTRY::count after alloc (CVE-2026-33985) + Resolves: RHEL-159806, RHEL-155468, RHEL-161037, RHEL-161472 + Resolves: RHEL-161508, RHEL-161075, RHEL-167794 + * Fri Apr 10 2026 Ondrej Holy - 2:2.11.7-7 - Update CLEAR_VBAR_ENTRY size after alloc (CVE-2026-33984) - Fail progressive_rfx_quant_sub on invalid values (CVE-2026-33983)