diff --git a/Default-minimum-thread-count.patch b/Default-minimum-thread-count.patch new file mode 100644 index 0000000..497f992 --- /dev/null +++ b/Default-minimum-thread-count.patch @@ -0,0 +1,52 @@ +From 0e6c921b206259474b1c14bb26b183a8b2089cdb Mon Sep 17 00:00:00 2001 +From: akallabeth +Date: Fri, 12 Sep 2025 08:25:16 +0200 +Subject: [PATCH 082/100] [winpr,threadpool] default minimum thread count + +Set a default minimum of 4 threads in a pool. Avoids issues with system +running with a single processor (might lead to deadlocks if the code +assumes > 1 thread handling stuff) +--- + winpr/libwinpr/pool/CMakeLists.txt | 2 ++ + winpr/libwinpr/pool/pool.c | 9 +++++++-- + 2 files changed, 9 insertions(+), 2 deletions(-) + +diff --git a/winpr/libwinpr/pool/CMakeLists.txt b/winpr/libwinpr/pool/CMakeLists.txt +index cfa9bc891..6b5938fd0 100644 +--- a/winpr/libwinpr/pool/CMakeLists.txt ++++ b/winpr/libwinpr/pool/CMakeLists.txt +@@ -16,7 +16,9 @@ + # limitations under the License. + + set(WINPR_THREADPOOL_DEFAULT_MAX_COUNT "16" CACHE STRING "The maximum (default) number of threads in a pool") ++set(WINPR_THREADPOOL_DEFAULT_MIN_COUNT "4" CACHE STRING "The minimum (default) number of threads in a pool") + winpr_definition_add(WINPR_THREADPOOL_DEFAULT_MAX_COUNT=${WINPR_THREADPOOL_DEFAULT_MAX_COUNT}) ++winpr_definition_add(WINPR_THREADPOOL_DEFAULT_MIN_COUNT=${WINPR_THREADPOOL_DEFAULT_MIN_COUNT}) + winpr_module_add( + synch.c + work.c +diff --git a/winpr/libwinpr/pool/pool.c b/winpr/libwinpr/pool/pool.c +index 96db6b247..cbfc274c5 100644 +--- a/winpr/libwinpr/pool/pool.c ++++ b/winpr/libwinpr/pool/pool.c +@@ -127,10 +127,15 @@ static BOOL InitializeThreadpool(PTP_POOL pool) + obj = ArrayList_Object(pool->Threads); + obj->fnObjectFree = threads_close; + ++#if !defined(WINPR_THREADPOOL_DEFAULT_MIN_COUNT) ++#error "WINPR_THREADPOOL_DEFAULT_MIN_COUNT must be defined" ++#endif ++ + SYSTEM_INFO info = { 0 }; + GetSystemInfo(&info); +- if (info.dwNumberOfProcessors < 1) +- info.dwNumberOfProcessors = 1; ++ if (info.dwNumberOfProcessors < WINPR_THREADPOOL_DEFAULT_MIN_COUNT) ++ info.dwNumberOfProcessors = WINPR_THREADPOOL_DEFAULT_MIN_COUNT; ++ + if (!SetThreadpoolThreadMinimum(pool, info.dwNumberOfProcessors)) + goto fail; + +-- +2.51.0 + diff --git a/Limit-minimum-threadpool-size.patch b/Limit-minimum-threadpool-size.patch new file mode 100644 index 0000000..a007bc9 --- /dev/null +++ b/Limit-minimum-threadpool-size.patch @@ -0,0 +1,51 @@ +From 8fc7acbfd0a1892bc9237ae25e99d32270812dcc Mon Sep 17 00:00:00 2001 +From: akallabeth +Date: Mon, 29 Sep 2025 08:37:06 +0200 +Subject: [PATCH] [winpr,pool] limit minimum threadpool size + +--- + winpr/libwinpr/pool/pool.c | 20 ++++++++++++-------- + 1 file changed, 12 insertions(+), 8 deletions(-) + +diff --git a/winpr/libwinpr/pool/pool.c b/winpr/libwinpr/pool/pool.c +index cbfc274c5..2cbe9a4fa 100644 +--- a/winpr/libwinpr/pool/pool.c ++++ b/winpr/libwinpr/pool/pool.c +@@ -129,22 +129,26 @@ static BOOL InitializeThreadpool(PTP_POOL pool) + + #if !defined(WINPR_THREADPOOL_DEFAULT_MIN_COUNT) + #error "WINPR_THREADPOOL_DEFAULT_MIN_COUNT must be defined" ++#endif ++#if !defined(WINPR_THREADPOOL_DEFAULT_MAX_COUNT) ++#error "WINPR_THREADPOOL_DEFAULT_MAX_COUNT must be defined" + #endif + + SYSTEM_INFO info = { 0 }; + GetSystemInfo(&info); ++ ++ DWORD min = info.dwNumberOfProcessors; ++ DWORD max = info.dwNumberOfProcessors; + if (info.dwNumberOfProcessors < WINPR_THREADPOOL_DEFAULT_MIN_COUNT) +- info.dwNumberOfProcessors = WINPR_THREADPOOL_DEFAULT_MIN_COUNT; ++ min = WINPR_THREADPOOL_DEFAULT_MIN_COUNT; ++ if (info.dwNumberOfProcessors > WINPR_THREADPOOL_DEFAULT_MAX_COUNT) ++ max = WINPR_THREADPOOL_DEFAULT_MAX_COUNT; ++ if (min > max) ++ min = max; + +- if (!SetThreadpoolThreadMinimum(pool, info.dwNumberOfProcessors)) ++ if (!SetThreadpoolThreadMinimum(pool, min)) + goto fail; + +-#if !defined(WINPR_THREADPOOL_DEFAULT_MAX_COUNT) +-#error "WINPR_THREADPOOL_DEFAULT_MAX_COUNT must be defined" +-#endif +- DWORD max = info.dwNumberOfProcessors; +- if (max > WINPR_THREADPOOL_DEFAULT_MAX_COUNT) +- max = WINPR_THREADPOOL_DEFAULT_MAX_COUNT; + SetThreadpoolThreadMaximum(pool, max); + + rc = TRUE; +-- +2.51.0 + diff --git a/Limit-threadpool-to-16-threads.patch b/Limit-threadpool-to-16-threads.patch new file mode 100644 index 0000000..3b2ec74 --- /dev/null +++ b/Limit-threadpool-to-16-threads.patch @@ -0,0 +1,60 @@ +From c2fc455d8f2dc60de35ed98cb300b7e6a84fe383 Mon Sep 17 00:00:00 2001 +From: Armin Novak +Date: Wed, 3 Sep 2025 14:03:10 +0200 +Subject: [PATCH 066/100] [winpr,pool] limit threadpool to 16 threads by + default + +Initialize the threadpool to minimum of number of processors and 16 by +default. +--- + winpr/libwinpr/pool/CMakeLists.txt | 2 ++ + winpr/libwinpr/pool/pool.c | 13 +++++++++---- + 2 files changed, 11 insertions(+), 4 deletions(-) + +diff --git a/winpr/libwinpr/pool/CMakeLists.txt b/winpr/libwinpr/pool/CMakeLists.txt +index fb2dee58f..cfa9bc891 100644 +--- a/winpr/libwinpr/pool/CMakeLists.txt ++++ b/winpr/libwinpr/pool/CMakeLists.txt +@@ -15,6 +15,8 @@ + # See the License for the specific language governing permissions and + # limitations under the License. + ++set(WINPR_THREADPOOL_DEFAULT_MAX_COUNT "16" CACHE STRING "The maximum (default) number of threads in a pool") ++winpr_definition_add(WINPR_THREADPOOL_DEFAULT_MAX_COUNT=${WINPR_THREADPOOL_DEFAULT_MAX_COUNT}) + winpr_module_add( + synch.c + work.c +diff --git a/winpr/libwinpr/pool/pool.c b/winpr/libwinpr/pool/pool.c +index 39bbaaac9..96db6b247 100644 +--- a/winpr/libwinpr/pool/pool.c ++++ b/winpr/libwinpr/pool/pool.c +@@ -133,7 +133,14 @@ static BOOL InitializeThreadpool(PTP_POOL pool) + info.dwNumberOfProcessors = 1; + if (!SetThreadpoolThreadMinimum(pool, info.dwNumberOfProcessors)) + goto fail; +- SetThreadpoolThreadMaximum(pool, info.dwNumberOfProcessors); ++ ++#if !defined(WINPR_THREADPOOL_DEFAULT_MAX_COUNT) ++#error "WINPR_THREADPOOL_DEFAULT_MAX_COUNT must be defined" ++#endif ++ DWORD max = info.dwNumberOfProcessors; ++ if (max > WINPR_THREADPOOL_DEFAULT_MAX_COUNT) ++ max = WINPR_THREADPOOL_DEFAULT_MAX_COUNT; ++ SetThreadpoolThreadMaximum(pool, max); + + rc = TRUE; + +@@ -143,9 +150,7 @@ fail: + + PTP_POOL GetDefaultThreadpool(void) + { +- PTP_POOL pool = NULL; +- +- pool = &DEFAULT_POOL; ++ PTP_POOL pool = &DEFAULT_POOL; + + if (!InitializeThreadpool(pool)) + return NULL; +-- +2.51.0 + diff --git a/Use-default-threadpool.patch b/Use-default-threadpool.patch new file mode 100644 index 0000000..d3940e3 --- /dev/null +++ b/Use-default-threadpool.patch @@ -0,0 +1,321 @@ +From 00968fd6e647af286e67bbd616168c4de7a39208 Mon Sep 17 00:00:00 2001 +From: Armin Novak +Date: Wed, 3 Sep 2025 14:40:47 +0200 +Subject: [PATCH 067/100] [codec] use default threadpool + +Backported together with these commits to be applicable: + [codec,yuv] fix thread count calculation + - 17315c593655d476cccfb9a1b56e41f37030f8e1 + [codec,yuv] fix worker object handling + - 1cc64eb58b2f5ce9e4ad8aa8610e085f696e578c + Fix YUV conversion for systems with lots of CPUs + - 774ee652a9c73c0bc3cdba26eba0c112f079cee4 + +--- + libfreerdp/codec/progressive.c | 5 ++- + libfreerdp/codec/rfx.c | 56 +++++++--------------------------- + libfreerdp/codec/rfx_types.h | 6 ---- + libfreerdp/codec/yuv.c | 32 ++----------------- + 4 files changed, 16 insertions(+), 83 deletions(-) + +diff --git a/libfreerdp/codec/progressive.c b/libfreerdp/codec/progressive.c +index 6396e0f2a..8b5200546 100644 +--- a/libfreerdp/codec/progressive.c ++++ b/libfreerdp/codec/progressive.c +@@ -1698,9 +1698,8 @@ static INLINE SSIZE_T progressive_process_tiles( + + if (progressive->rfx_context->priv->UseThreads) + { +- progressive->work_objects[idx] = +- CreateThreadpoolWork(progressive_process_tiles_tile_work_callback, (void*)param, +- &progressive->rfx_context->priv->ThreadPoolEnv); ++ progressive->work_objects[idx] = CreateThreadpoolWork( ++ progressive_process_tiles_tile_work_callback, (void*)param, NULL); + if (!progressive->work_objects[idx]) + { + WLog_Print(progressive->log, WLOG_ERROR, +diff --git a/libfreerdp/codec/rfx.c b/libfreerdp/codec/rfx.c +index 8832cd740..2ec4eb521 100644 +--- a/libfreerdp/codec/rfx.c ++++ b/libfreerdp/codec/rfx.c +@@ -205,16 +205,8 @@ RFX_CONTEXT* rfx_context_new(BOOL encoder) + + RFX_CONTEXT* rfx_context_new_ex(BOOL encoder, UINT32 ThreadingFlags) + { +- HKEY hKey = NULL; +- LONG status = 0; +- DWORD dwType = 0; +- DWORD dwSize = 0; +- DWORD dwValue = 0; +- SYSTEM_INFO sysinfo; +- RFX_CONTEXT* context = NULL; +- wObject* pool = NULL; + RFX_CONTEXT_PRIV* priv = NULL; +- context = (RFX_CONTEXT*)winpr_aligned_calloc(1, sizeof(RFX_CONTEXT), 32); ++ RFX_CONTEXT* context = (RFX_CONTEXT*)winpr_aligned_calloc(1, sizeof(RFX_CONTEXT), 32); + + if (!context) + return NULL; +@@ -233,7 +225,7 @@ RFX_CONTEXT* rfx_context_new_ex(BOOL encoder, UINT32 ThreadingFlags) + if (!priv->TilePool) + goto fail; + +- pool = ObjectPool_Object(priv->TilePool); ++ wObject* pool = ObjectPool_Object(priv->TilePool); + pool->fnObjectInit = rfx_tile_init; + + if (context->encoder) +@@ -267,29 +259,22 @@ RFX_CONTEXT* rfx_context_new_ex(BOOL encoder, UINT32 ThreadingFlags) + + if (!(ThreadingFlags & THREADING_FLAGS_DISABLE_THREADS)) + { ++ HKEY hKey = NULL; + priv->UseThreads = TRUE; + +- GetNativeSystemInfo(&sysinfo); +- priv->MinThreadCount = sysinfo.dwNumberOfProcessors; +- priv->MaxThreadCount = 0; +- status = RegOpenKeyExA(HKEY_LOCAL_MACHINE, RFX_KEY, 0, KEY_READ | KEY_WOW64_64KEY, &hKey); ++ const LONG status = ++ RegOpenKeyExA(HKEY_LOCAL_MACHINE, RFX_KEY, 0, KEY_READ | KEY_WOW64_64KEY, &hKey); + + if (status == ERROR_SUCCESS) + { +- dwSize = sizeof(dwValue); ++ DWORD dwType = 0; ++ DWORD dwValue = 0; ++ DWORD dwSize = sizeof(dwValue); + + if (RegQueryValueEx(hKey, _T("UseThreads"), NULL, &dwType, (BYTE*)&dwValue, &dwSize) == + ERROR_SUCCESS) + priv->UseThreads = dwValue ? 1 : 0; + +- if (RegQueryValueEx(hKey, _T("MinThreadCount"), NULL, &dwType, (BYTE*)&dwValue, +- &dwSize) == ERROR_SUCCESS) +- priv->MinThreadCount = dwValue; +- +- if (RegQueryValueEx(hKey, _T("MaxThreadCount"), NULL, &dwType, (BYTE*)&dwValue, +- &dwSize) == ERROR_SUCCESS) +- priv->MaxThreadCount = dwValue; +- + RegCloseKey(hKey); + } + } +@@ -304,20 +289,6 @@ RFX_CONTEXT* rfx_context_new_ex(BOOL encoder, UINT32 ThreadingFlags) + /* from multiple threads. This call will initialize all function pointers correctly */ + /* before any decoding threads are started */ + primitives_get(); +- priv->ThreadPool = CreateThreadpool(NULL); +- +- if (!priv->ThreadPool) +- goto fail; +- +- InitializeThreadpoolEnvironment(&priv->ThreadPoolEnv); +- SetThreadpoolCallbackPool(&priv->ThreadPoolEnv, priv->ThreadPool); +- +- if (priv->MinThreadCount) +- if (!SetThreadpoolThreadMinimum(priv->ThreadPool, priv->MinThreadCount)) +- goto fail; +- +- if (priv->MaxThreadCount) +- SetThreadpoolThreadMaximum(priv->ThreadPool, priv->MaxThreadCount); + } + + /* initialize the default pixel format */ +@@ -370,9 +341,6 @@ void rfx_context_free(RFX_CONTEXT* context) + ObjectPool_Free(priv->TilePool); + if (priv->UseThreads) + { +- if (priv->ThreadPool) +- CloseThreadpool(priv->ThreadPool); +- DestroyThreadpoolEnvironment(&priv->ThreadPoolEnv); + winpr_aligned_free((void*)priv->workObjects); + winpr_aligned_free(priv->tileWorkParams); + #ifdef WITH_PROFILER +@@ -1086,9 +1054,8 @@ static INLINE BOOL rfx_process_message_tileset(RFX_CONTEXT* WINPR_RESTRICT conte + params[i].context = context; + params[i].tile = message->tiles[i]; + +- if (!(work_objects[i] = +- CreateThreadpoolWork(rfx_process_message_tile_work_callback, +- (void*)¶ms[i], &context->priv->ThreadPoolEnv))) ++ if (!(work_objects[i] = CreateThreadpoolWork(rfx_process_message_tile_work_callback, ++ (void*)¶ms[i], NULL))) + { + WLog_Print(context->priv->log, WLOG_ERROR, "CreateThreadpoolWork failed."); + rc = FALSE; +@@ -1829,8 +1796,7 @@ RFX_MESSAGE* rfx_encode_message(RFX_CONTEXT* WINPR_RESTRICT context, + workParam->tile = tile; + + if (!(*workObject = CreateThreadpoolWork(rfx_compose_message_tile_work_callback, +- (void*)workParam, +- &context->priv->ThreadPoolEnv))) ++ (void*)workParam, NULL))) + { + goto skip_encoding_loop; + } +diff --git a/libfreerdp/codec/rfx_types.h b/libfreerdp/codec/rfx_types.h +index a9cd314da..c5a62259b 100644 +--- a/libfreerdp/codec/rfx_types.h ++++ b/libfreerdp/codec/rfx_types.h +@@ -69,11 +69,11 @@ struct S_RFX_CONTEXT_PRIV + PTP_WORK* workObjects; + RFX_TILE_COMPOSE_WORK_PARAM* tileWorkParams; + +- DWORD MinThreadCount; ++ DWORD MinThreadCount; /* Kept only to silence abidiff */ +- DWORD MaxThreadCount; ++ DWORD MaxThreadCount; /* Kept only to silence abidiff */ + +- PTP_POOL ThreadPool; ++ PTP_POOL ThreadPool; /* Kept only to silence abidiff */ +- TP_CALLBACK_ENVIRON ThreadPoolEnv; ++ TP_CALLBACK_ENVIRON ThreadPoolEnv; /* Kept only to silence abidiff */ + + wBufferPool* BufferPool; + +diff --git a/libfreerdp/codec/yuv.c b/libfreerdp/codec/yuv.c +index 75d8a6a89..4025782dd 100644 +--- a/libfreerdp/codec/yuv.c ++++ b/libfreerdp/codec/yuv.c +@@ -55,11 +55,11 @@ struct S_YUV_CONTEXT + UINT32 width, height; + BOOL useThreads; + BOOL encoder; +- UINT32 nthreads; ++ UINT32 nthreads; /* Kept only to silence abidiff */ + UINT32 heightStep; + +- PTP_POOL threadPool; ++ PTP_POOL threadPool; /* Kept only to silence abidiff */ +- TP_CALLBACK_ENVIRON ThreadPoolEnv; ++ TP_CALLBACK_ENVIRON ThreadPoolEnv; /* Kept only to silence abidiff */ + + UINT32 work_object_count; + PTP_WORK* work_objects; +@@ -166,16 +162,21 @@ BOOL yuv_context_reset(YUV_CONTEXT* WINPR_RESTRICT context, UINT32 width, UINT32 + + context->width = width; + context->height = height; +- context->heightStep = (height / context->nthreads); ++ ++ context->heightStep = height; + + if (context->useThreads) + { +- const UINT32 pw = (width + TILE_SIZE - width % TILE_SIZE) / TILE_SIZE; +- const UINT32 ph = (height + TILE_SIZE - height % TILE_SIZE) / TILE_SIZE; ++ context->heightStep = 16; ++ /* Preallocate workers for 16x16 tiles. ++ * this is overallocation for most cases. ++ * ++ * ~2MB total for a 4k resolution, so negligible. ++ */ ++ const size_t pw = (width + TILE_SIZE - width % TILE_SIZE) / 16; ++ const size_t ph = (height + TILE_SIZE - height % TILE_SIZE) / 16; + +- /* We´ve calculated the amount of workers for 64x64 tiles, but the decoder +- * might get 16x16 tiles mixed in. */ +- const UINT32 count = pw * ph * 16; ++ const size_t count = pw * ph; + + context->work_object_count = 0; + if (context->encoder) +@@ -237,33 +234,13 @@ YUV_CONTEXT* yuv_context_new(BOOL encoder, UINT32 ThreadingFlags) + primitives_get(); + + ret->encoder = encoder; +- ret->nthreads = 1; + if (!(ThreadingFlags & THREADING_FLAGS_DISABLE_THREADS)) + { + GetNativeSystemInfo(&sysInfos); + ret->useThreads = (sysInfos.dwNumberOfProcessors > 1); +- if (ret->useThreads) +- { +- ret->nthreads = sysInfos.dwNumberOfProcessors; +- ret->threadPool = CreateThreadpool(NULL); +- if (!ret->threadPool) +- { +- goto error_threadpool; +- } +- +- InitializeThreadpoolEnvironment(&ret->ThreadPoolEnv); +- SetThreadpoolCallbackPool(&ret->ThreadPoolEnv, ret->threadPool); +- } + } + + return ret; +- +-error_threadpool: +- WINPR_PRAGMA_DIAG_PUSH +- WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC +- yuv_context_free(ret); +- WINPR_PRAGMA_DIAG_POP +- return NULL; + } + + void yuv_context_free(YUV_CONTEXT* context) +@@ -272,9 +249,6 @@ void yuv_context_free(YUV_CONTEXT* context) + return; + if (context->useThreads) + { +- if (context->threadPool) +- CloseThreadpool(context->threadPool); +- DestroyThreadpoolEnvironment(&context->ThreadPoolEnv); + winpr_aligned_free((void*)context->work_objects); + winpr_aligned_free(context->work_combined_params); + winpr_aligned_free(context->work_enc_params); +@@ -330,7 +304,7 @@ static BOOL submit_object(PTP_WORK* WINPR_RESTRICT work_object, PTP_WORK_CALLBAC + if (!param || !context) + return FALSE; + +- *work_object = CreateThreadpoolWork(cb, cnv.pv, &context->ThreadPoolEnv); ++ *work_object = CreateThreadpoolWork(cb, cnv.pv, NULL); + if (!*work_object) + return FALSE; + +@@ -439,11 +417,8 @@ static BOOL pool_decode(YUV_CONTEXT* WIN + + if (context->work_object_count <= waitCount) + { +- WLog_ERR(TAG, +- "YUV decoder: invalid number of tiles, only support less than %" PRIu32 +- ", got %" PRIu32, +- context->work_object_count, waitCount); +- goto fail; ++ free_objects(context->work_objects, context->work_object_count); ++ waitCount = 0; + } + + YUV_PROCESS_WORK_PARAM* cur = &context->work_dec_params[waitCount]; +@@ -586,11 +561,8 @@ static BOOL pool_decode_rect(YUV_CONTEXT + + if (context->work_object_count <= waitCount) + { +- WLog_ERR(TAG, +- "YUV rect decoder: invalid number of tiles, only support less than %" PRIu32 +- ", got %" PRIu32, +- context->work_object_count, waitCount); +- goto fail; ++ free_objects(context->work_objects, context->work_object_count); ++ waitCount = 0; + } + current = &context->work_combined_params[waitCount]; + *current = pool_decode_rect_param(®ionRects[waitCount], context, type, pYUVData, iStride, +@@ -848,11 +820,8 @@ static BOOL pool_encode(YUV_CONTEXT* WIN + + if (context->work_object_count <= waitCount) + { +- WLog_ERR(TAG, +- "YUV encoder: invalid number of tiles, only support less than %" PRIu32 +- ", got %" PRIu32, +- context->work_object_count, waitCount); +- goto fail; ++ free_objects(context->work_objects, context->work_object_count); ++ waitCount = 0; + } + + current = &context->work_enc_params[waitCount]; +-- +2.51.0 + diff --git a/cache-offscreen-invalidate-bitmap-before-free.patch b/cache-offscreen-invalidate-bitmap-before-free.patch new file mode 100644 index 0000000..26527c0 --- /dev/null +++ b/cache-offscreen-invalidate-bitmap-before-free.patch @@ -0,0 +1,45 @@ +From 52106a26726a2aba77aa6d86014d2eb3507f0783 Mon Sep 17 00:00:00 2001 +From: akallabeth +Date: Mon, 19 Jan 2026 08:58:22 +0100 +Subject: [PATCH] [cache,offscreen] invalidate bitmap before free + +First ensure the bitmap is no longer used for drawing before calling the +free function. +--- + libfreerdp/cache/offscreen.c | 10 +++++++--- + 1 file changed, 7 insertions(+), 3 deletions(-) + +diff --git a/libfreerdp/cache/offscreen.c b/libfreerdp/cache/offscreen.c +index 91fa38f32..d3c85f95d 100644 +--- a/libfreerdp/cache/offscreen.c ++++ b/libfreerdp/cache/offscreen.c +@@ -164,8 +164,6 @@ void offscreen_cache_put(rdpOffscreenCache* offscreenCache, UINT32 index, rdpBit + + void offscreen_cache_delete(rdpOffscreenCache* offscreenCache, UINT32 index) + { +- rdpBitmap* prevBitmap = NULL; +- + WINPR_ASSERT(offscreenCache); + + if (index >= offscreenCache->maxEntries) +@@ -174,10 +172,16 @@ void offscreen_cache_delete(rdpOffscreenCache* offscreenCache, UINT32 index) + return; + } + +- prevBitmap = offscreenCache->entries[index]; ++ rdpBitmap* prevBitmap = offscreenCache->entries[index]; + + if (prevBitmap != NULL) ++ { ++ WINPR_ASSERT(offscreenCache->context); ++ ++ /* Ensure that the bitmap is no longer used in GDI */ ++ IFCALL(prevBitmap->SetSurface, offscreenCache->context, NULL, FALSE); + Bitmap_Free(offscreenCache->context, prevBitmap); ++ } + + offscreenCache->entries[index] = NULL; + } +-- +2.52.0 + diff --git a/client-x11-fix-double-free-in-case-of-invalid-pointe.patch b/client-x11-fix-double-free-in-case-of-invalid-pointe.patch new file mode 100644 index 0000000..5a12e67 --- /dev/null +++ b/client-x11-fix-double-free-in-case-of-invalid-pointe.patch @@ -0,0 +1,48 @@ +From 0421b53fcb4a80c95f51342e4a2c40c68a4101d3 Mon Sep 17 00:00:00 2001 +From: akallabeth +Date: Mon, 19 Jan 2026 08:52:51 +0100 +Subject: [PATCH] [client,x11] fix double free in case of invalid pointer + +--- + client/X11/xf_graphics.c | 10 ++++------ + 1 file changed, 4 insertions(+), 6 deletions(-) + +diff --git a/client/X11/xf_graphics.c b/client/X11/xf_graphics.c +index df95a22b5..9d432c98b 100644 +--- a/client/X11/xf_graphics.c ++++ b/client/X11/xf_graphics.c +@@ -289,7 +289,6 @@ static BOOL xf_Pointer_New(rdpContext* context, rdpPointer* pointer) + + #ifdef WITH_XCURSOR + UINT32 CursorFormat = 0; +- size_t size = 0; + xfContext* xfc = (xfContext*)context; + xfPointer* xpointer = (xfPointer*)pointer; + +@@ -304,19 +303,18 @@ static BOOL xf_Pointer_New(rdpContext* context, rdpPointer* pointer) + xpointer->nCursors = 0; + xpointer->mCursors = 0; + +- size = 1ull * pointer->height * pointer->width * FreeRDPGetBytesPerPixel(CursorFormat); ++ const size_t size = ++ 1ull * pointer->height * pointer->width * FreeRDPGetBytesPerPixel(CursorFormat); + +- if (!(xpointer->cursorPixels = (XcursorPixel*)winpr_aligned_malloc(size, 16))) ++ xpointer->cursorPixels = (XcursorPixel*)winpr_aligned_malloc(size, 16); ++ if (!xpointer->cursorPixels) + goto fail; + + if (!freerdp_image_copy_from_pointer_data( + (BYTE*)xpointer->cursorPixels, CursorFormat, 0, 0, 0, pointer->width, pointer->height, + pointer->xorMaskData, pointer->lengthXorMask, pointer->andMaskData, + pointer->lengthAndMask, pointer->xorBpp, &context->gdi->palette)) +- { +- winpr_aligned_free(xpointer->cursorPixels); + goto fail; +- } + + #endif + +-- +2.52.0 + diff --git a/codec-clear-check-clear_decomress-glyphData.patch b/codec-clear-check-clear_decomress-glyphData.patch new file mode 100644 index 0000000..2a9be7c --- /dev/null +++ b/codec-clear-check-clear_decomress-glyphData.patch @@ -0,0 +1,78 @@ +From 4498861d2b180cae552b5e2daedebe14312c2141 Mon Sep 17 00:00:00 2001 +From: Ondrej Holy +Date: Mon, 26 Jan 2026 16:32:13 +0100 +Subject: [PATCH] [codec,clear] check clear_decomress glyphData + +Backport of commit 243ecf804bb122e8e643a5c142ad5a49d7aa19ee. + +Co-Authored-By: Claude +--- + libfreerdp/codec/clear.c | 52 ++++++++++++++++++++++++++++++++++++++-- + 1 file changed, 50 insertions(+), 2 deletions(-) + +diff --git a/libfreerdp/codec/clear.c b/libfreerdp/codec/clear.c +index 603863c36..ca821f575 100644 +--- a/libfreerdp/codec/clear.c ++++ b/libfreerdp/codec/clear.c +@@ -1139,8 +1139,56 @@ INT32 clear_decompress(CLEAR_CONTEXT* WINPR_RESTRICT clear, const BYTE* WINPR_RE + + if (glyphData) + { +- if (!freerdp_image_copy_no_overlap(glyphData, clear->format, 0, 0, 0, nWidth, nHeight, +- pDstData, DstFormat, nDstStep, nXDst, nYDst, palette, ++ uint32_t w = MIN(nWidth, nDstWidth); ++ if (nXDst > nDstWidth) ++ { ++ WLog_WARN(TAG, "glyphData copy area x exceeds destination: x=%" PRIu32 " > %" PRIu32, ++ nXDst, nDstWidth); ++ w = 0; ++ } ++ else if (nXDst + w > nDstWidth) ++ { ++ WLog_WARN(TAG, ++ "glyphData copy area x + width exceeds destination: x=%" PRIu32 " + %" PRIu32 ++ " > %" PRIu32, ++ nXDst, w, nDstWidth); ++ w = nDstWidth - nXDst; ++ } ++ ++ if (w != nWidth) ++ { ++ WLog_WARN(TAG, ++ "glyphData copy area width truncated: requested=%" PRIu32 ++ ", truncated to %" PRIu32, ++ nWidth, w); ++ } ++ ++ uint32_t h = MIN(nHeight, nDstHeight); ++ if (nYDst > nDstHeight) ++ { ++ WLog_WARN(TAG, "glyphData copy area y exceeds destination: y=%" PRIu32 " > %" PRIu32, ++ nYDst, nDstHeight); ++ h = 0; ++ } ++ else if (nYDst + h > nDstHeight) ++ { ++ WLog_WARN(TAG, ++ "glyphData copy area y + height exceeds destination: x=%" PRIu32 " + %" PRIu32 ++ " > %" PRIu32, ++ nYDst, h, nDstHeight); ++ h = nDstHeight - nYDst; ++ } ++ ++ if (h != nHeight) ++ { ++ WLog_WARN(TAG, ++ "glyphData copy area height truncated: requested=%" PRIu32 ++ ", truncated to %" PRIu32, ++ nHeight, h); ++ } ++ ++ if (!freerdp_image_copy_no_overlap(glyphData, clear->format, 0, 0, 0, w, h, pDstData, ++ DstFormat, nDstStep, nXDst, nYDst, palette, + FREERDP_KEEP_DST_ALPHA)) + goto fail; + } +-- +2.52.0 + diff --git a/codec-clear-fix-clear_resize_buffer-checks.patch b/codec-clear-fix-clear_resize_buffer-checks.patch new file mode 100644 index 0000000..9d32e9e --- /dev/null +++ b/codec-clear-fix-clear_resize_buffer-checks.patch @@ -0,0 +1,62 @@ +From 94235a5297db9cb83c2c23ade8a69cabe3e5f9f4 Mon Sep 17 00:00:00 2001 +From: Ondrej Holy +Date: Tue, 27 Jan 2026 16:15:28 +0100 +Subject: [PATCH] [codec,clear] fix clear_resize_buffer checks + +Backport of commit c4391827d7facfc874ca7f61a92afb82232a5748. + +Co-Authored-By: Claude +--- + libfreerdp/codec/clear.c | 17 +++++++++-------- + 1 file changed, 9 insertions(+), 8 deletions(-) + +diff --git a/libfreerdp/codec/clear.c b/libfreerdp/codec/clear.c +index b0813937d..28450b357 100644 +--- a/libfreerdp/codec/clear.c ++++ b/libfreerdp/codec/clear.c +@@ -58,7 +58,7 @@ struct S_CLEAR_CONTEXT + NSC_CONTEXT* nsc; + UINT32 seqNumber; + BYTE* TempBuffer; +- UINT32 TempSize; ++ size_t TempSize; + UINT32 nTempStep; + UINT32 TempFormat; + UINT32 format; +@@ -328,25 +328,26 @@ static BOOL clear_decompress_subcode_rlex(wStream* WINPR_RESTRICT s, UINT32 bitm + + static BOOL clear_resize_buffer(CLEAR_CONTEXT* WINPR_RESTRICT clear, UINT32 width, UINT32 height) + { +- UINT32 size = 0; +- + if (!clear) + return FALSE; + +- size = ((width + 16) * (height + 16) * FreeRDPGetBytesPerPixel(clear->format)); ++ const UINT64 size = 1ull * (width + 16ull) * (height + 16ull); ++ const size_t bpp = FreeRDPGetBytesPerPixel(clear->format); ++ if (size > UINT32_MAX / bpp) ++ return FALSE; + +- if (size > clear->TempSize) ++ if (size > clear->TempSize / bpp) + { +- BYTE* tmp = (BYTE*)winpr_aligned_recalloc(clear->TempBuffer, size, sizeof(BYTE), 32); ++ BYTE* tmp = (BYTE*)winpr_aligned_recalloc(clear->TempBuffer, size, bpp, 32); + + if (!tmp) + { +- WLog_ERR(TAG, "clear->TempBuffer winpr_aligned_recalloc failed for %" PRIu32 " bytes", ++ WLog_ERR(TAG, "clear->TempBuffer winpr_aligned_recalloc failed for %" PRIu64 " bytes", + size); + return FALSE; + } + +- clear->TempSize = size; ++ clear->TempSize = size * bpp; + clear->TempBuffer = tmp; + } + +-- +2.52.0 + diff --git a/codec-clear-fix-off-by-one-length-check.patch b/codec-clear-fix-off-by-one-length-check.patch new file mode 100644 index 0000000..36b4d51 --- /dev/null +++ b/codec-clear-fix-off-by-one-length-check.patch @@ -0,0 +1,31 @@ +From f8688b57f6cfad9a0b05475a6afbde355ffab720 Mon Sep 17 00:00:00 2001 +From: akallabeth +Date: Thu, 15 Jan 2026 12:19:53 +0100 +Subject: [PATCH] [codec,clear] fix off by one length check + +--- + libfreerdp/codec/clear.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/libfreerdp/codec/clear.c b/libfreerdp/codec/clear.c +index 4a67a8ed6..0efa89f8d 100644 +--- a/libfreerdp/codec/clear.c ++++ b/libfreerdp/codec/clear.c +@@ -876,12 +876,12 @@ static BOOL clear_decompress_bands_data(CLEAR_CONTEXT* WINPR_RESTRICT clear, + if (count > nHeight) + count = nHeight; + +- if (nXDstRel + i > nDstWidth) ++ if (nXDstRel + i >= nDstWidth) + return FALSE; + + for (UINT32 y = 0; y < count; y++) + { +- if (nYDstRel + y > nDstHeight) ++ if (nYDstRel + y >= nDstHeight) + return FALSE; + + BYTE* pDstPixel8 = +-- +2.52.0 + diff --git a/codec-planar-fix-decoder-length-checks.patch b/codec-planar-fix-decoder-length-checks.patch new file mode 100644 index 0000000..a8a9fee --- /dev/null +++ b/codec-planar-fix-decoder-length-checks.patch @@ -0,0 +1,28 @@ +From 1bab198a2edd0d0e6e1627d21a433151ea190500 Mon Sep 17 00:00:00 2001 +From: akallabeth +Date: Thu, 15 Jan 2026 12:02:02 +0100 +Subject: [PATCH] [codec,planar] fix decoder length checks + +--- + libfreerdp/codec/planar.c | 5 +++++ + 1 file changed, 5 insertions(+) + +diff --git a/libfreerdp/codec/planar.c b/libfreerdp/codec/planar.c +index 1a06e36ed..94a640a55 100644 +--- a/libfreerdp/codec/planar.c ++++ b/libfreerdp/codec/planar.c +@@ -727,6 +727,11 @@ BOOL freerdp_bitmap_decompress_planar(BITMAP_PLANAR_CONTEXT* WINPR_RESTRICT plan + WINPR_ASSERT(planar); + WINPR_ASSERT(prims); + ++ if (planar->maxWidth < nSrcWidth) ++ return FALSE; ++ if (planar->maxHeight < nSrcHeight) ++ return FALSE; ++ + if (nDstStep <= 0) + nDstStep = nDstWidth * FreeRDPGetBytesPerPixel(DstFormat); + +-- +2.52.0 + diff --git a/freerdp.spec b/freerdp.spec index bd50aa1..92e07f6 100644 --- a/freerdp.spec +++ b/freerdp.spec @@ -30,7 +30,7 @@ Name: freerdp Epoch: 2 Version: 3.10.3 -Release: 3%{?dist} +Release: 5%{?dist}.1 Summary: Free implementation of the Remote Desktop Protocol (RDP) # The effective license is Apache-2.0 but: @@ -50,6 +50,33 @@ Source1: freerdp_download_and_repack.sh # https://bugzilla.redhat.com/show_bug.cgi?id=2365232 Patch0: Initialize-function-pointers-after-resource-allocation.patch +# https://issues.redhat.com/browse/RHEL-86251 +Patch1: Limit-threadpool-to-16-threads.patch +Patch2: Use-default-threadpool.patch +Patch3: Default-minimum-thread-count.patch +Patch4: Limit-minimum-threadpool-size.patch + +# https://github.com/FreeRDP/FreeRDP/commit/c4a7c371342edf0d307cea728f56d3302f0ab38c +Patch: gdi-gfx-properly-clamp-SurfaceToSurface.patch + +# https://github.com/FreeRDP/FreeRDP/commit/c4391827d7facfc874ca7f61a92afb82232a5748 +Patch: codec-clear-fix-clear_resize_buffer-checks.patch + +# https://github.com/FreeRDP/FreeRDP/commit/f8688b57f6cfad9a0b05475a6afbde355ffab720 +Patch: codec-clear-fix-off-by-one-length-check.patch + +# https://github.com/FreeRDP/FreeRDP/commit/1bab198a2edd0d0e6e1627d21a433151ea190500 +Patch: codec-planar-fix-decoder-length-checks.patch + +# https://github.com/FreeRDP/FreeRDP/commit/243ecf804bb122e8e643a5c142ad5a49d7aa19ee +Patch: codec-clear-check-clear_decomress-glyphData.patch + +# https://github.com/FreeRDP/FreeRDP/commit/0421b53fcb4a80c95f51342e4a2c40c68a4101d3 +Patch: client-x11-fix-double-free-in-case-of-invalid-pointe.patch + +# https://github.com/FreeRDP/FreeRDP/commit/52106a26726a2aba77aa6d86014d2eb3507f0783 +Patch: cache-offscreen-invalidate-bitmap-before-free.patch + BuildRequires: gcc BuildRequires: gcc-c++ BuildRequires: alsa-lib-devel @@ -372,6 +399,19 @@ find %{buildroot} -name "*.a" -delete %{_libdir}/pkgconfig/winpr-tools3.pc %changelog +* Tue Jan 27 2026 Ondrej Holy - 2:3.10.3-5.1 +- Backport several CVE fixes + Resolves: RHEL-142413, RHEL-142397, RHEL-142381, RHEL-142365, RHEL-142349 + Resolves: RHEL-142333, RHEL-142317 + +* Tue Sep 30 2025 Marek Kasik - 2:3.10.3-5 +- Silence abidiff +- Resolves: RHEL-86251 + +* Mon Sep 29 2025 Marek Kasik - 2:3.10.3-4 +- Limit threadpool to 16 threads +- Resolves: RHEL-86251 + * Mon Jun 16 2025 Marek Kasik - 2:3.10.3-3 - Initialize function pointers after resource allocation - Fixes CVE-2025-4478 diff --git a/freerdp_download_and_repack.sh b/freerdp_download_and_repack.sh old mode 100755 new mode 100644 diff --git a/gdi-gfx-properly-clamp-SurfaceToSurface.patch b/gdi-gfx-properly-clamp-SurfaceToSurface.patch new file mode 100644 index 0000000..8d51104 --- /dev/null +++ b/gdi-gfx-properly-clamp-SurfaceToSurface.patch @@ -0,0 +1,48 @@ +From c4a7c371342edf0d307cea728f56d3302f0ab38c Mon Sep 17 00:00:00 2001 +From: akallabeth +Date: Thu, 15 Jan 2026 12:04:36 +0100 +Subject: [PATCH] [gdi,gfx] properly clamp SurfaceToSurface + +--- + libfreerdp/gdi/gfx.c | 10 +++++----- + 1 file changed, 5 insertions(+), 5 deletions(-) + +diff --git a/libfreerdp/gdi/gfx.c b/libfreerdp/gdi/gfx.c +index 56e6ff9ed..96ce10070 100644 +--- a/libfreerdp/gdi/gfx.c ++++ b/libfreerdp/gdi/gfx.c +@@ -1335,8 +1335,6 @@ static UINT gdi_SurfaceToSurface(RdpgfxClientContext* context, + { + UINT status = ERROR_INTERNAL_ERROR; + BOOL sameSurface = 0; +- UINT32 nWidth = 0; +- UINT32 nHeight = 0; + const RECTANGLE_16* rectSrc = NULL; + RECTANGLE_16 invalidRect; + gdiGfxSurface* surfaceSrc = NULL; +@@ -1362,8 +1360,8 @@ static UINT gdi_SurfaceToSurface(RdpgfxClientContext* context, + if (!is_rect_valid(rectSrc, surfaceSrc->width, surfaceSrc->height)) + goto fail; + +- nWidth = rectSrc->right - rectSrc->left; +- nHeight = rectSrc->bottom - rectSrc->top; ++ const UINT32 nWidth = rectSrc->right - rectSrc->left; ++ const UINT32 nHeight = rectSrc->bottom - rectSrc->top; + + for (UINT16 index = 0; index < surfaceToSurface->destPtsCount; index++) + { +@@ -1374,8 +1372,10 @@ static UINT gdi_SurfaceToSurface(RdpgfxClientContext* context, + if (!is_rect_valid(&rect, surfaceDst->width, surfaceDst->height)) + goto fail; + ++ const UINT32 rwidth = rect.right - rect.left; ++ const UINT32 rheight = rect.bottom - rect.top; + if (!freerdp_image_copy(surfaceDst->data, surfaceDst->format, surfaceDst->scanline, +- destPt->x, destPt->y, nWidth, nHeight, surfaceSrc->data, ++ destPt->x, destPt->y, rwidth, rheight, surfaceSrc->data, + surfaceSrc->format, surfaceSrc->scanline, rectSrc->left, + rectSrc->top, NULL, FREERDP_FLIP_NONE)) + goto fail; +-- +2.52.0 +