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..8532a7e --- /dev/null +++ b/cache-bitmap-initialize-overallocated-bitmap-cache-extra-slot.patch @@ -0,0 +1,52 @@ +From ce86fd834bc1002f3f4a827b73fbd1fdc870dc42 Mon Sep 17 00:00:00 2001 +From: Ondrej Holy +Date: Tue, 28 Apr 2026 04:19:39 +0000 +Subject: [PATCH] [cache,bitmap] initialize overallocated bitmap cache extra + slot + +Backport of commit 8270e0bb3d6726c947d57c93ba9caa92a052b557. + +Adjusted hunk offsets for 3.10.3. + +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 8083fd9..1dc9853 100644 +--- a/libfreerdp/cache/bitmap.c ++++ b/libfreerdp/cache/bitmap.c +@@ -390,6 +390,19 @@ rdpBitmapCache* bitmap_cache_new(rdpContext* context) + 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: + WINPR_PRAGMA_DIAG_PUSH +@@ -408,7 +421,8 @@ void bitmap_cache_free(rdpBitmapCache* bitmapCache) + + if (bitmapCache->cells) + { +- for (UINT32 i = 0; i < bitmapCache->maxCells; i++) ++ /* iterate through maxCells + 1 to also free the overallocated extra slot */ ++ for (UINT32 i = 0; i <= bitmapCache->maxCells; i++) + { + UINT32 j = 0; + 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..e922e88 --- /dev/null +++ b/cache-bitmap-overallocate-bitmap-cache.patch @@ -0,0 +1,33 @@ +From 3c4666445b42c983a767e59888c5863bb420f331 Mon Sep 17 00:00:00 2001 +From: Ondrej Holy +Date: Tue, 28 Apr 2026 04:19:33 +0000 +Subject: [PATCH] [cache,bitmap] overallocate bitmap cache + +Backport of commit ffad58fd2b329efd81a3239e9d7e3c927b8e503f. + +Adjusted hunk offsets for 3.10.3. + +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 6b6463c..8083fd9 100644 +--- a/libfreerdp/cache/bitmap.c ++++ b/libfreerdp/cache/bitmap.c +@@ -367,7 +367,10 @@ rdpBitmapCache* bitmap_cache_new(rdpContext* context) + const UINT32 BitmapCacheV2NumCells = + freerdp_settings_get_uint32(settings, FreeRDP_BitmapCacheV2NumCells); + bitmapCache->context = context; +- bitmapCache->cells = (BITMAP_V2_CELL*)calloc(BitmapCacheV2NumCells, sizeof(BITMAP_V2_CELL)); ++ ++ /* overallocate by 1. older RDP servers do send a off by 1 cache index. */ ++ bitmapCache->cells = ++ (BITMAP_V2_CELL*)calloc(BitmapCacheV2NumCells + 1ull, sizeof(BITMAP_V2_CELL)); + + if (!bitmapCache->cells) + goto fail; +-- +2.53.0 + diff --git a/cache-persist-use-winpr_aligned_calloc.patch b/cache-persist-use-winpr_aligned_calloc.patch new file mode 100644 index 0000000..4d114d3 --- /dev/null +++ b/cache-persist-use-winpr_aligned_calloc.patch @@ -0,0 +1,47 @@ +From 4d45e4715adfa57dfc0a829ec351dbe2680e756b Mon Sep 17 00:00:00 2001 +From: Ondrej Holy +Date: Tue, 28 Apr 2026 05:15:25 +0000 +Subject: [PATCH] [cache,persist] use winpr_aligned_calloc + +Backport of commit a48dbde2c8a5b8b70a9d1c045d969a71afd6284c. + +`nullptr` replaced with `NULL` (not available in 3.10.3). + +Made-with: Cursor +--- + libfreerdp/cache/persistent.c | 5 +++-- + 1 file changed, 3 insertions(+), 2 deletions(-) + +diff --git a/libfreerdp/cache/persistent.c b/libfreerdp/cache/persistent.c +index 39be803..e963c5e 100644 +--- a/libfreerdp/cache/persistent.c ++++ b/libfreerdp/cache/persistent.c +@@ -39,6 +39,7 @@ struct rdp_persistent_cache + size_t bmpSize; + }; + ++static const size_t PERSIST_ALIGN = 32; + static const char sig_str[] = "RDP8bmp"; + + int persistent_cache_get_version(rdpPersistentCache* persistent) +@@ -155,7 +156,7 @@ static int persistent_cache_read_entry_v3(rdpPersistentCache* persistent, + { + persistent->bmpSize = size; + BYTE* bmpData = (BYTE*)winpr_aligned_recalloc(persistent->bmpData, persistent->bmpSize, +- sizeof(BYTE), 32); ++ sizeof(BYTE), PERSIST_ALIGN); + + if (!bmpData) + return -1; +@@ -350,7 +351,7 @@ rdpPersistentCache* persistent_cache_new(void) + return NULL; + + persistent->bmpSize = 0x4000; +- persistent->bmpData = calloc(1, persistent->bmpSize); ++ persistent->bmpData = winpr_aligned_calloc(1, persistent->bmpSize, PERSIST_ALIGN); + + if (!persistent->bmpData) + { +-- +2.53.0 + diff --git a/cache-persistent-update-persistent_cache_entry-size-after-realloc.patch b/cache-persistent-update-persistent_cache_entry-size-after-realloc.patch new file mode 100644 index 0000000..550bf4a --- /dev/null +++ b/cache-persistent-update-persistent_cache_entry-size-after-realloc.patch @@ -0,0 +1,54 @@ +From 1d86433a0f30490e1e0fdc3bb8f76d259ef67a9d Mon Sep 17 00:00:00 2001 +From: Ondrej Holy +Date: Tue, 28 Apr 2026 05:14:58 +0000 +Subject: [PATCH] [cache,persistent] update PERSISTENT_CACHE_ENTRY::size after + realloc + +Backport of commit 1a890eb43492b5eb707cb3dd6fc908f696e8fc1c. + +WINPR_ASSERTING_INT_CAST replaced with plain cast (macro not available in 3.10.3). + +Made-with: Cursor +--- + libfreerdp/cache/persistent.c | 8 ++++---- + 1 file changed, 4 insertions(+), 4 deletions(-) + +diff --git a/libfreerdp/cache/persistent.c b/libfreerdp/cache/persistent.c +index 16025b8..39be803 100644 +--- a/libfreerdp/cache/persistent.c ++++ b/libfreerdp/cache/persistent.c +@@ -36,7 +36,7 @@ struct rdp_persistent_cache + int count; + char* filename; + BYTE* bmpData; +- UINT32 bmpSize; ++ size_t bmpSize; + }; + + static const char sig_str[] = "RDP8bmp"; +@@ -149,12 +149,11 @@ static int persistent_cache_read_entry_v3(rdpPersistentCache* persistent, + const UINT64 size = 4ull * entry3.width * entry3.height; + if (size > UINT32_MAX) + return -1; +- entry->size = (UINT32)size; + entry->flags = 0; + +- if (entry->size > persistent->bmpSize) ++ if (size > persistent->bmpSize) + { +- persistent->bmpSize = entry->size; ++ persistent->bmpSize = size; + BYTE* bmpData = (BYTE*)winpr_aligned_recalloc(persistent->bmpData, persistent->bmpSize, + sizeof(BYTE), 32); + +@@ -163,6 +162,7 @@ static int persistent_cache_read_entry_v3(rdpPersistentCache* persistent, + + persistent->bmpData = bmpData; + } ++ entry->size = (UINT32)size; + + entry->data = persistent->bmpData; + +-- +2.53.0 + diff --git a/client-x11-fix-clipboard-update.patch b/client-x11-fix-clipboard-update.patch new file mode 100644 index 0000000..799decb --- /dev/null +++ b/client-x11-fix-clipboard-update.patch @@ -0,0 +1,64 @@ +From d9f55987f530bc881556fa61512865f4b07b1bb6 Mon Sep 17 00:00:00 2001 +From: Ondrej Holy +Date: Mon, 27 Apr 2026 14:53:46 +0000 +Subject: [PATCH] [client,X11] fix clipboard update + +Backport of commit 58409406afe7c2a8a71ed2dc8e22075be4f41c0c. + +Adjusted hunk offsets for 3.10.3. + +Made-with: Cursor +--- + client/X11/xf_cliprdr.c | 17 ++++++++++------- + 1 file changed, 10 insertions(+), 7 deletions(-) + +diff --git a/client/X11/xf_cliprdr.c b/client/X11/xf_cliprdr.c +index 265a66d..7a8ef71 100644 +--- a/client/X11/xf_cliprdr.c ++++ b/client/X11/xf_cliprdr.c +@@ -850,7 +850,11 @@ static void xf_clipboard_formats_free(xfClipboard* clipboard) + { + WINPR_ASSERT(clipboard); + ++ /* Synchronize RDP/X11 thread with channel thread */ ++ xf_lock_x11(clipboard->xfc); + xf_cliprdr_free_formats(clipboard->lastSentFormats, clipboard->lastSentNumFormats); ++ xf_unlock_x11(clipboard->xfc); ++ + clipboard->lastSentFormats = NULL; + clipboard->lastSentNumFormats = 0; + } +@@ -1862,24 +1866,23 @@ static UINT xf_cliprdr_send_client_format_list_response(xfClipboard* clipboard, + static UINT xf_cliprdr_monitor_ready(CliprdrClientContext* context, + const CLIPRDR_MONITOR_READY* monitorReady) + { +- UINT ret = 0; +- xfClipboard* clipboard = NULL; +- + WINPR_ASSERT(context); + WINPR_ASSERT(monitorReady); + +- clipboard = cliprdr_file_context_get_context(context->custom); ++ xfClipboard* clipboard = cliprdr_file_context_get_context(context->custom); + WINPR_ASSERT(clipboard); + + WINPR_UNUSED(monitorReady); + +- if ((ret = xf_cliprdr_send_client_capabilities(clipboard)) != CHANNEL_RC_OK) ++ const UINT ret = xf_cliprdr_send_client_capabilities(clipboard); ++ if (ret != CHANNEL_RC_OK) + return ret; + + xf_clipboard_formats_free(clipboard); + +- if ((ret = xf_cliprdr_send_client_format_list(clipboard, TRUE)) != CHANNEL_RC_OK) +- return ret; ++ const UINT ret2 = xf_cliprdr_send_client_format_list(clipboard, TRUE); ++ if (ret2 != CHANNEL_RC_OK) ++ return ret2; + + clipboard->sync = TRUE; + return CHANNEL_RC_OK; +-- +2.53.0 + diff --git a/client-x11-fix-residual-race-in-xf_clipboard_formats_free.patch b/client-x11-fix-residual-race-in-xf_clipboard_formats_free.patch new file mode 100644 index 0000000..32b3cbc --- /dev/null +++ b/client-x11-fix-residual-race-in-xf_clipboard_formats_free.patch @@ -0,0 +1,39 @@ +From 3ac579a0844c200b71e160dd49208bc53ce06e8d Mon Sep 17 00:00:00 2001 +From: Ondrej Holy +Date: Tue, 28 Apr 2026 18:37:44 +0000 +Subject: [PATCH] [client,X11] fix residual race in xf_clipboard_formats_free + +Backport of commit 4c9f7e8a7129c8be15f6e2686559d3f17936677d. + +Adjusted hunk offsets for 3.10.3, `nullptr` replaced with `NULL`. + +Made-with: Cursor +--- + client/X11/xf_cliprdr.c | 7 ++++--- + 1 file changed, 4 insertions(+), 3 deletions(-) + +diff --git a/client/X11/xf_cliprdr.c b/client/X11/xf_cliprdr.c +index 7a8ef71..82878e7 100644 +--- a/client/X11/xf_cliprdr.c ++++ b/client/X11/xf_cliprdr.c +@@ -850,13 +850,14 @@ static void xf_clipboard_formats_free(xfClipboard* clipboard) + { + WINPR_ASSERT(clipboard); + +- /* Synchronize RDP/X11 thread with channel thread */ ++ /* Synchronize RDP/X11 thread with channel thread. ++ * Reset the pointer and count inside the lock so that ++ * xf_clipboard_changed cannot observe a freed-but-non-NULL pointer. */ + xf_lock_x11(clipboard->xfc); + xf_cliprdr_free_formats(clipboard->lastSentFormats, clipboard->lastSentNumFormats); +- xf_unlock_x11(clipboard->xfc); +- + clipboard->lastSentFormats = NULL; + clipboard->lastSentNumFormats = 0; ++ xf_unlock_x11(clipboard->xfc); + } + + static BOOL xf_clipboard_copy_formats(xfClipboard* clipboard, const CLIPRDR_FORMAT* formats, +-- +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..d36dc52 --- /dev/null +++ b/client-x11-fix-xf_rail_window_common-cleanup.patch @@ -0,0 +1,45 @@ +From 97d6df1ca09102ca95ada1ae3ef9284881b2130c Mon Sep 17 00:00:00 2001 +From: Ondrej Holy +Date: Mon, 27 Apr 2026 14:52:49 +0000 +Subject: [PATCH] [client,x11] fix xf_rail_window_common cleanup + +Backport of commit b4f0f0a18fe53aa8d47d062f91471f4e9c5e0d51. + +Adjusted hunk offsets for 3.10.3. + +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 5c42f7a..afcfc67 100644 +--- a/client/X11/xf_rail.c ++++ b/client/X11/xf_rail.c +@@ -307,11 +307,10 @@ static void window_state_log_style_int(wLog* log, const WINDOW_STATE_ORDER* wind + 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) + { +@@ -362,10 +361,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..cb5057e --- /dev/null +++ b/codec-clear-update-clear_glyph_entry-count-after-alloc.patch @@ -0,0 +1,58 @@ +From daa338be37d8fdc8c4c924b5e868f5b979e49065 Mon Sep 17 00:00:00 2001 +From: Ondrej Holy +Date: Tue, 28 Apr 2026 05:15:04 +0000 +Subject: [PATCH] [codec,clear] Update CLEAR_GLYPH_ENTRY::count after alloc + +Backport of commit c49d1ad43b8c7b32794d0250f2623c2dccd7ef25. + +WINPR_ASSERTING_INT_CAST replaced with plain cast (macro not available in 3.10.3). + +Made-with: Cursor +--- + libfreerdp/codec/clear.c | 22 ++++++++++++++++------ + 1 file changed, 16 insertions(+), 6 deletions(-) + +diff --git a/libfreerdp/codec/clear.c b/libfreerdp/codec/clear.c +index 2d58aba..8b1571a 100644 +--- a/libfreerdp/codec/clear.c ++++ b/libfreerdp/codec/clear.c +@@ -980,20 +980,30 @@ static BOOL clear_decompress_glyph_data(CLEAR_CONTEXT* WINPR_RESTRICT clear, + { + const UINT32 bpp = FreeRDPGetBytesPerPixel(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 = +- winpr_aligned_recalloc(glyphEntry->pixels, glyphEntry->count, 1ull * bpp, 32); ++ BYTE* tmp = winpr_aligned_recalloc(glyphEntry->pixels, count, 1ull * bpp, 32); + + if (!tmp) + { +- WLog_ERR(TAG, "glyphEntry->pixels winpr_aligned_recalloc %" PRIu32 " failed!", +- glyphEntry->count * bpp); ++ WLog_ERR(TAG, "glyphEntry->pixels winpr_aligned_recalloc %" PRIuz " failed!", ++ count * bpp); + return FALSE; + } + ++ glyphEntry->count = (UINT32)count; + glyphEntry->size = glyphEntry->count; + glyphEntry->pixels = (UINT32*)tmp; + } +-- +2.53.0 + diff --git a/codec-dsp-add-format-checks.patch b/codec-dsp-add-format-checks.patch new file mode 100644 index 0000000..8a077ab --- /dev/null +++ b/codec-dsp-add-format-checks.patch @@ -0,0 +1,277 @@ +From 81283063b678b6582244a0f32f2059c8d8bba8cb Mon Sep 17 00:00:00 2001 +From: Ondrej Holy +Date: Tue, 28 Apr 2026 04:41:15 +0000 +Subject: [PATCH] [codec,dsp] add format checks + +Backport of commit 03b48b3601d867afccac1cdc6081de7a275edce7. + +Adapted for 3.10.x: `nullptr` replaced with `NULL`, +`Stream_ResetPosition` replaced with `Stream_SetPosition`, +`error != nullptr` replaced with `error != NULL`. + +Made-with: Cursor +--- + libfreerdp/codec/dsp.c | 148 +++++++++++++++++++++++++++++++---------- + 1 file changed, 112 insertions(+), 36 deletions(-) + +diff --git a/libfreerdp/codec/dsp.c b/libfreerdp/codec/dsp.c +index c5f5949..5e0f78a 100644 +--- a/libfreerdp/codec/dsp.c ++++ b/libfreerdp/codec/dsp.c +@@ -354,11 +354,28 @@ static UINT16 dsp_decode_ima_adpcm_sample(ADPCM* WINPR_RESTRICT adpcm, unsigned + return (UINT16)d; + } + ++static BOOL valid_ima_adpcm_format(const FREERDP_DSP_CONTEXT* WINPR_RESTRICT context) ++{ ++ WINPR_ASSERT(context); ++ if (context->common.format.wFormatTag != WAVE_FORMAT_DVI_ADPCM) ++ return FALSE; ++ if (context->common.format.nBlockAlign <= 4ULL) ++ return FALSE; ++ if (context->common.format.nChannels < 1) ++ return FALSE; ++ if (context->common.format.wBitsPerSample == 0) ++ return FALSE; ++ return TRUE; ++} ++ + static BOOL freerdp_dsp_decode_ima_adpcm(FREERDP_DSP_CONTEXT* WINPR_RESTRICT context, + const BYTE* WINPR_RESTRICT src, size_t size, + wStream* WINPR_RESTRICT out) + { +- size_t out_size = size * 4; ++ if (!valid_ima_adpcm_format(context)) ++ return FALSE; ++ ++ size_t out_size = size * 4ull; + const UINT32 block_size = context->common.format.nBlockAlign; + const UINT32 channels = context->common.format.nChannels; + +@@ -501,27 +518,38 @@ static BOOL freerdp_dsp_encode_gsm610(FREERDP_DSP_CONTEXT* WINPR_RESTRICT contex + #endif + + #if defined(WITH_LAME) ++static BOOL valid_mp3_format(const FREERDP_DSP_CONTEXT* WINPR_RESTRICT context) ++{ ++ WINPR_ASSERT(context); ++ if (context->common.format.wFormatTag != WAVE_FORMAT_MPEGLAYER3) ++ return FALSE; ++ if (context->common.format.nChannels < 1) ++ return FALSE; ++ if (context->common.format.wBitsPerSample == 0) ++ return FALSE; ++ if (context->common.format.nSamplesPerSec == 0) ++ return FALSE; ++ return TRUE; ++} ++ + static BOOL freerdp_dsp_decode_mp3(FREERDP_DSP_CONTEXT* WINPR_RESTRICT context, + const BYTE* WINPR_RESTRICT src, size_t size, + wStream* WINPR_RESTRICT out) + { +- int rc; +- short* pcm_l; +- short* pcm_r; +- size_t buffer_size; +- + if (!context || !src || !out) + return FALSE; +- +- buffer_size = 2 * context->common.format.nChannels * context->common.format.nSamplesPerSec; ++ if (!valid_mp3_format(context)) ++ return FALSE; ++ const size_t buffer_size = ++ 2 * context->common.format.nChannels * context->common.format.nSamplesPerSec; + + if (!Stream_EnsureCapacity(context->common.buffer, 2 * buffer_size)) + return FALSE; + +- pcm_l = Stream_BufferAs(context->common.buffer, short); +- pcm_r = Stream_BufferAs(context->common.buffer, short) + buffer_size; +- rc = hip_decode(context->hip, (unsigned char*)/* API is not modifying content */ src, size, +- pcm_l, pcm_r); ++ short* pcm_l = Stream_BufferAs(context->common.buffer, short); ++ short* pcm_r = Stream_BufferAs(context->common.buffer, short) + buffer_size; ++ const int rc = hip_decode(context->hip, (unsigned char*)/* API is not modifying content */ src, ++ size, pcm_l, pcm_r); + + if (rc <= 0) + return FALSE; +@@ -542,13 +570,13 @@ static BOOL freerdp_dsp_encode_mp3(FREERDP_DSP_CONTEXT* WINPR_RESTRICT context, + const BYTE* WINPR_RESTRICT src, size_t size, + wStream* WINPR_RESTRICT out) + { +- size_t samples_per_channel; +- int rc; +- + if (!context || !src || !out) + return FALSE; + +- samples_per_channel = ++ if (!valid_mp3_format(context)) ++ return FALSE; ++ ++ size_t samples_per_channel = + size / context->common.format.nChannels / context->common.format.wBitsPerSample / 8; + + /* Ensure worst case buffer size for mp3 stream taken from LAME header */ +@@ -556,8 +584,9 @@ static BOOL freerdp_dsp_encode_mp3(FREERDP_DSP_CONTEXT* WINPR_RESTRICT context, + return FALSE; + + samples_per_channel = size / 2 /* size of a sample */ / context->common.format.nChannels; +- rc = lame_encode_buffer_interleaved(context->lame, (short*)src, samples_per_channel, +- Stream_Pointer(out), Stream_GetRemainingCapacity(out)); ++ const int rc = ++ lame_encode_buffer_interleaved(context->lame, (short*)src, samples_per_channel, ++ Stream_Pointer(out), Stream_GetRemainingCapacity(out)); + + if (rc < 0) + return FALSE; +@@ -807,6 +836,8 @@ static BOOL freerdp_dsp_encode_ima_adpcm(FREERDP_DSP_CONTEXT* WINPR_RESTRICT con + const BYTE* WINPR_RESTRICT src, size_t size, + wStream* WINPR_RESTRICT out) + { ++ if (!valid_ima_adpcm_format(context)) ++ return FALSE; + if (!Stream_EnsureRemainingCapacity(out, size)) + return FALSE; + if (!Stream_EnsureRemainingCapacity(context->common.buffer, size + 64)) +@@ -889,6 +920,20 @@ static const INT32 ms_adpcm_coeffs1[7] = { 256, 512, 0, 192, 240, 460, 392 }; + + static const INT32 ms_adpcm_coeffs2[7] = { 0, -256, 0, 64, 0, -208, -232 }; + ++static BOOL valid_ms_adpcm_format(const FREERDP_DSP_CONTEXT* WINPR_RESTRICT context) ++{ ++ WINPR_ASSERT(context); ++ if (context->common.format.wFormatTag != WAVE_FORMAT_ADPCM) ++ return FALSE; ++ if (context->common.format.nBlockAlign <= 4ULL) ++ return FALSE; ++ if (context->common.format.nChannels < 1) ++ return FALSE; ++ if (context->common.format.wBitsPerSample == 0) ++ return FALSE; ++ return TRUE; ++} ++ + static INLINE INT16 freerdp_dsp_decode_ms_adpcm_sample(ADPCM* WINPR_RESTRICT adpcm, BYTE sample, + int channel) + { +@@ -918,6 +963,8 @@ static BOOL freerdp_dsp_decode_ms_adpcm(FREERDP_DSP_CONTEXT* WINPR_RESTRICT cont + const BYTE* WINPR_RESTRICT src, size_t size, + wStream* WINPR_RESTRICT out) + { ++ if (!valid_ms_adpcm_format(context)) ++ return FALSE; + const size_t out_size = size * 4; + const UINT32 channels = context->common.format.nChannels; + const UINT32 block_size = context->common.format.nBlockAlign; +@@ -1038,6 +1085,9 @@ static BOOL freerdp_dsp_encode_ms_adpcm(FREERDP_DSP_CONTEXT* WINPR_RESTRICT cont + const BYTE* WINPR_RESTRICT src, size_t size, + wStream* WINPR_RESTRICT out) + { ++ if (!valid_ms_adpcm_format(context)) ++ return FALSE; ++ + const size_t step = 8 + ((context->common.format.nChannels > 1) ? 4 : 0); + + if (!Stream_EnsureRemainingCapacity(out, size)) +@@ -1482,21 +1532,44 @@ BOOL freerdp_dsp_context_reset(FREERDP_DSP_CONTEXT* WINPR_RESTRICT context, + + context->common.format = *targetFormat; + +- if (context->common.format.wFormatTag == WAVE_FORMAT_DVI_ADPCM) ++ switch (context->common.format.wFormatTag) + { +- size_t min_frame_data = 1ull * context->common.format.wBitsPerSample * +- context->common.format.nChannels * FramesPerPacket; +- size_t data_per_block = +- (1ULL * context->common.format.nBlockAlign - 4ULL * context->common.format.nChannels) * +- 8ULL; +- size_t nb_block_per_packet = min_frame_data / data_per_block; +- +- if (min_frame_data % data_per_block) +- nb_block_per_packet++; +- +- context->adpcm.ima.packet_size = nb_block_per_packet * context->common.format.nBlockAlign; +- Stream_EnsureCapacity(context->common.buffer, context->adpcm.ima.packet_size); +- Stream_SetPosition(context->common.buffer, 0); ++#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; ++ if (FramesPerPacket == 0) ++ return FALSE; ++ ++ const size_t min_frame_data = 1ull * context->common.format.wBitsPerSample * ++ context->common.format.nChannels * FramesPerPacket; ++ const size_t data_per_block = (1ULL * context->common.format.nBlockAlign - ++ 4ULL * context->common.format.nChannels) * ++ 8ULL; ++ size_t nb_block_per_packet = min_frame_data / data_per_block; ++ ++ if (min_frame_data % data_per_block) ++ nb_block_per_packet++; ++ ++ context->adpcm.ima.packet_size = ++ nb_block_per_packet * context->common.format.nBlockAlign; ++ if (!Stream_EnsureCapacity(context->common.buffer, context->adpcm.ima.packet_size)) ++ return FALSE; ++ Stream_SetPosition(context->common.buffer, 0); ++ } ++ break; ++ default: ++ break; + } + + #if defined(WITH_OPUS) +@@ -1539,7 +1612,7 @@ BOOL freerdp_dsp_context_reset(FREERDP_DSP_CONTEXT* WINPR_RESTRICT context, + + if (context->common.encoder) + { +- faacEncConfigurationPtr cfg; ++ faacEncConfigurationPtr cfg = NULL; + + if (context->faac) + faacEncClose(context->faac); +@@ -1556,20 +1629,23 @@ BOOL freerdp_dsp_context_reset(FREERDP_DSP_CONTEXT* WINPR_RESTRICT context, + cfg->mpegVersion = MPEG4; + cfg->useTns = 1; + cfg->bandWidth = targetFormat->nAvgBytesPerSec; +- faacEncSetConfiguration(context->faac, cfg); ++ const int rc = faacEncSetConfiguration(context->faac, cfg); ++ if (rc <= 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->common.format.nSamplesPerSec, targetFormat->nSamplesPerSec, + targetFormat->nChannels, &error, &iospec, NULL, NULL); + +- if (!context->sox || (error != 0)) ++ if (!context->sox || (error != NULL)) + return FALSE; + } + #endif +-- +2.53.0 + diff --git a/codec-dsp-fix-array-bounds-checks.patch b/codec-dsp-fix-array-bounds-checks.patch new file mode 100644 index 0000000..e7f2469 --- /dev/null +++ b/codec-dsp-fix-array-bounds-checks.patch @@ -0,0 +1,224 @@ +From 3bd10b4f0ea8c5a7e2da2b8d00a15b6ddf26e66f 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 3.10.x: kept `int channel` parameter type in +`dsp_encode_ima_adpcm_sample` (not `size_t`), kept `+=` style +for step index updates. + +Made-with: Cursor +--- + libfreerdp/codec/dsp.c | 79 +++++++++++++++++++++++++++++++++++++----- + 1 file changed, 70 insertions(+), 9 deletions(-) + +diff --git a/libfreerdp/codec/dsp.c b/libfreerdp/codec/dsp.c +index 5e0f78a..b672074 100644 +--- a/libfreerdp/codec/dsp.c ++++ b/libfreerdp/codec/dsp.c +@@ -321,7 +321,14 @@ static const INT16 ima_step_size_table[] = { + static UINT16 dsp_decode_ima_adpcm_sample(ADPCM* WINPR_RESTRICT adpcm, unsigned int channel, + BYTE sample) + { +- const INT32 ss = ima_step_size_table[adpcm->ima.last_step[channel]]; ++ WINPR_ASSERT(channel < ARRAYSIZE(adpcm->ima.last_step)); ++ WINPR_ASSERT(channel < ARRAYSIZE(adpcm->ima.last_sample)); ++ ++ const INT16 offset = adpcm->ima.last_step[channel]; ++ WINPR_ASSERT(offset >= 0); ++ WINPR_ASSERT(offset < ARRAYSIZE(ima_step_size_table)); ++ ++ const INT32 ss = ima_step_size_table[offset]; + INT32 d = (ss >> 3); + + if (sample & 1) +@@ -344,6 +351,8 @@ static UINT16 dsp_decode_ima_adpcm_sample(ADPCM* WINPR_RESTRICT adpcm, unsigned + 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) +@@ -386,6 +395,9 @@ static BOOL freerdp_dsp_decode_ima_adpcm(FREERDP_DSP_CONTEXT* WINPR_RESTRICT con + { + 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)); +@@ -395,6 +407,8 @@ static BOOL freerdp_dsp_decode_ima_adpcm(FREERDP_DSP_CONTEXT* WINPR_RESTRICT con + + 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)); +@@ -406,6 +420,8 @@ static BOOL freerdp_dsp_decode_ima_adpcm(FREERDP_DSP_CONTEXT* WINPR_RESTRICT con + + if (channels > 1) + { ++ if (size < 8) ++ return FALSE; + for (size_t i = 0; i < 8; i++) + { + BYTE* dst = Stream_Pointer(out); +@@ -434,6 +450,8 @@ static BOOL freerdp_dsp_decode_ima_adpcm(FREERDP_DSP_CONTEXT* WINPR_RESTRICT con + } + else + { ++ if (size < 1) ++ return FALSE; + BYTE* dst = Stream_Pointer(out); + if (!Stream_SafeSeek(out, 4)) + return FALSE; +@@ -775,7 +793,14 @@ static const struct + + static BYTE dsp_encode_ima_adpcm_sample(ADPCM* WINPR_RESTRICT adpcm, int channel, INT16 sample) + { +- INT32 ss = ima_step_size_table[adpcm->ima.last_step[channel]]; ++ WINPR_ASSERT(channel < ARRAYSIZE(adpcm->ima.last_step)); ++ WINPR_ASSERT(channel < ARRAYSIZE(adpcm->ima.last_sample)); ++ ++ const INT16 offset = adpcm->ima.last_step[channel]; ++ WINPR_ASSERT(offset >= 0); ++ WINPR_ASSERT(offset < ARRAYSIZE(ima_step_size_table)); ++ ++ INT32 ss = ima_step_size_table[offset]; + INT32 e = sample - adpcm->ima.last_sample[channel]; + INT32 d = e; + INT32 diff = ss >> 3; +@@ -822,6 +847,8 @@ static BYTE dsp_encode_ima_adpcm_sample(ADPCM* WINPR_RESTRICT adpcm, int channel + 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) +@@ -937,11 +964,22 @@ static BOOL valid_ms_adpcm_format(const FREERDP_DSP_CONTEXT* WINPR_RESTRICT cont + static INLINE INT16 freerdp_dsp_decode_ms_adpcm_sample(ADPCM* WINPR_RESTRICT adpcm, BYTE sample, + int channel) + { ++ 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)); ++ + const INT8 nibble = (sample & 0x08 ? (INT8)sample - 16 : (INT8)sample); ++ const BYTE predictor = adpcm->ms.predictor[channel]; ++ INT32 coeff1 = 0; ++ if (predictor < ARRAYSIZE(ms_adpcm_coeffs1)) ++ coeff1 = ms_adpcm_coeffs1[predictor]; ++ ++ INT32 coeff2 = 0; ++ if (predictor < ARRAYSIZE(ms_adpcm_coeffs2)) ++ coeff2 = ms_adpcm_coeffs2[predictor]; + INT32 presample = +- ((adpcm->ms.sample1[channel] * ms_adpcm_coeffs1[adpcm->ms.predictor[channel]]) + +- (adpcm->ms.sample2[channel] * ms_adpcm_coeffs2[adpcm->ms.predictor[channel]])) / +- 256; ++ ((adpcm->ms.sample1[channel] * coeff1) + (adpcm->ms.sample2[channel] * coeff2)) / 256; + presample += nibble * adpcm->ms.delta[channel]; + + if (presample > 32767) +@@ -951,7 +989,12 @@ static INLINE INT16 freerdp_dsp_decode_ms_adpcm_sample(ADPCM* WINPR_RESTRICT adp + + 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; +@@ -978,6 +1021,9 @@ static BOOL freerdp_dsp_decode_ms_adpcm(FREERDP_DSP_CONTEXT* WINPR_RESTRICT cont + { + 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); +@@ -1000,6 +1046,9 @@ static BOOL freerdp_dsp_decode_ms_adpcm(FREERDP_DSP_CONTEXT* WINPR_RESTRICT cont + } + else + { ++ if (size < 7) ++ return FALSE; ++ + context->adpcm.ms.predictor[0] = *src++; + context->adpcm.ms.delta[0] = read_int16(src); + src += 2; +@@ -1016,6 +1065,8 @@ static BOOL freerdp_dsp_decode_ms_adpcm(FREERDP_DSP_CONTEXT* WINPR_RESTRICT cont + if (channels > 1) + { + { ++ if (size < 1) ++ return FALSE; + const BYTE sample = *src++; + size--; + Stream_Write_INT16( +@@ -1024,6 +1075,8 @@ static BOOL freerdp_dsp_decode_ms_adpcm(FREERDP_DSP_CONTEXT* WINPR_RESTRICT cont + out, freerdp_dsp_decode_ms_adpcm_sample(&context->adpcm, sample & 0x0F, 1)); + } + { ++ if (size < 1) ++ return FALSE; + const BYTE sample = *src++; + size--; + Stream_Write_INT16( +@@ -1034,6 +1087,8 @@ static BOOL freerdp_dsp_decode_ms_adpcm(FREERDP_DSP_CONTEXT* WINPR_RESTRICT cont + } + else + { ++ if (size < 1) ++ return FALSE; + const BYTE sample = *src++; + size--; + Stream_Write_INT16(out, +@@ -1047,8 +1102,13 @@ static BOOL freerdp_dsp_decode_ms_adpcm(FREERDP_DSP_CONTEXT* WINPR_RESTRICT cont + } + + static BYTE freerdp_dsp_encode_ms_adpcm_sample(ADPCM* WINPR_RESTRICT adpcm, INT32 sample, +- int channel) ++ size_t channel) + { ++ 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)); ++ + INT32 presample = + ((adpcm->ms.sample1[channel] * ms_adpcm_coeffs1[adpcm->ms.predictor[channel]]) + + (adpcm->ms.sample2[channel] * ms_adpcm_coeffs2[adpcm->ms.predictor[channel]])) / +@@ -1072,8 +1132,9 @@ static BYTE freerdp_dsp_encode_ms_adpcm_sample(ADPCM* WINPR_RESTRICT adpcm, INT3 + + 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 e1e1b24..d9b29c2 100644 --- a/freerdp.spec +++ b/freerdp.spec @@ -30,7 +30,7 @@ Name: freerdp Epoch: 2 Version: 3.10.3 -Release: 5%{?dist}.6 +Release: 5%{?dist}.8 Summary: Free implementation of the Remote Desktop Protocol (RDP) # The effective license is Apache-2.0 but: @@ -190,6 +190,43 @@ Patch: codec-clear-update-CLEAR_VBAR_ENTRY-size-after-alloc.patch Patch: codec-progressive-fail-progressive_rfx_quant_sub-on-invalid-values.patch Patch: codec-progressive-fix-underflow-guard-in-progressive_rfx_quant_sub.patch +# CVE-2026-26986 +# https://github.com/FreeRDP/FreeRDP/commit/b4f0f0a18fe53aa8d47d062f91471f4e9c5e0d51 +Patch: client-x11-fix-xf_rail_window_common-cleanup.patch + +# CVE-2026-25997 +# https://github.com/FreeRDP/FreeRDP/commit/58409406afe7c2a8a71ed2dc8e22075be4f41c0c +# https://github.com/FreeRDP/FreeRDP/commit/4c9f7e8a7129c8be15f6e2686559d3f17936677d +Patch: client-x11-fix-clipboard-update.patch +Patch: client-x11-fix-residual-race-in-xf_clipboard_formats_free.patch + +# CVE-2026-29775 +# https://github.com/FreeRDP/FreeRDP/commit/ffad58fd2b329efd81a3239e9d7e3c927b8e503f +# https://github.com/FreeRDP/FreeRDP/commit/8270e0bb3d6726c947d57c93ba9caa92a052b557 +Patch: cache-bitmap-overallocate-bitmap-cache.patch +Patch: cache-bitmap-initialize-overallocated-bitmap-cache-extra-slot.patch + +# CVE-2026-31884 +# https://github.com/FreeRDP/FreeRDP/commit/03b48b3601d867afccac1cdc6081de7a275edce7 +Patch: codec-dsp-add-format-checks.patch + +# CVE-2026-31883 +# CVE-2026-31885 +# https://github.com/FreeRDP/FreeRDP/commit/16df2300e1e3f5a51f68fb1626429e58b531b7c8 +Patch: codec-dsp-fix-array-bounds-checks.patch + +# CVE-2026-33987 +# https://github.com/FreeRDP/FreeRDP/commit/1a890eb43492b5eb707cb3dd6fc908f696e8fc1c +Patch: cache-persistent-update-persistent_cache_entry-size-after-realloc.patch + +# CVE-2026-33985 +# https://github.com/FreeRDP/FreeRDP/commit/c49d1ad43b8c7b32794d0250f2623c2dccd7ef25 +Patch: codec-clear-update-clear_glyph_entry-count-after-alloc.patch + +# CVE-2026-33982 +# https://github.com/FreeRDP/FreeRDP/commit/a48dbde2c8a5b8b70a9d1c045d969a71afd6284c +Patch: cache-persist-use-winpr_aligned_calloc.patch + BuildRequires: gcc BuildRequires: gcc-c++ BuildRequires: alsa-lib-devel @@ -512,6 +549,19 @@ find %{buildroot} -name "*.a" -delete %{_libdir}/pkgconfig/winpr-tools3.pc %changelog +* Wed Apr 29 2026 Ondrej Holy - 2:3.10.3-5.8 +- Fix double free in xf_rail_window_common cleanup (CVE-2026-26986) +- Fix clipboard use-after-free during auto-reconnect (CVE-2026-25997) +- 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 PERSISTENT_CACHE_ENTRY::size after realloc (CVE-2026-33987) +- Update CLEAR_GLYPH_ENTRY::count after alloc (CVE-2026-33985) +- Use winpr_aligned_calloc in persistent cache (CVE-2026-33982) + Resolves: RHEL-159803, RHEL-159659, RHEL-161033, RHEL-161468 + Resolves: RHEL-161504, RHEL-161071, RHEL-163653, RHEL-167791, RHEL-162930 + * Fri Apr 10 2026 Ondrej Holy - 2:3.10.3-5.6 - Update CLEAR_VBAR_ENTRY size after alloc (CVE-2026-33984) - Fail progressive_rfx_quant_sub on invalid values (CVE-2026-33983)