Update to 3.24.2

Resolves: RHEL-80426
This commit is contained in:
Ondrej Holy 2026-04-09 16:40:48 +02:00
parent 8d16b68c4d
commit 5f89d0213d
50 changed files with 9 additions and 3091 deletions

1
.gitignore vendored
View File

@ -74,3 +74,4 @@
/FreeRDP-3.8.0-repack.tar.gz
/FreeRDP-3.9.0-repack.tar.gz
/FreeRDP-3.10.3-repack.tar.gz
/FreeRDP-3.24.2-repack.tar.gz

View File

@ -1,52 +0,0 @@
From 0e6c921b206259474b1c14bb26b183a8b2089cdb Mon Sep 17 00:00:00 2001
From: akallabeth <akallabeth@posteo.net>
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

View File

@ -1,64 +0,0 @@
From a4bb702aa62e4fad91ca99142de075265555ec18 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jonas=20=C3=85dahl?= <jadahl@gmail.com>
Date: Tue, 13 May 2025 10:34:08 +0200
Subject: [PATCH] transport: Initialize function pointers after resource
allocation
The transport instance is freed when an error occurs.
If the TransportDisconnect function pointer is initialized it
causes SIGSEGV during free.
CVE: CVE-2025-4478
---
libfreerdp/core/transport.c | 28 ++++++++++++++--------------
1 file changed, 14 insertions(+), 14 deletions(-)
diff --git a/libfreerdp/core/transport.c b/libfreerdp/core/transport.c
index d199c31be..2ca146f65 100644
--- a/libfreerdp/core/transport.c
+++ b/libfreerdp/core/transport.c
@@ -1646,20 +1646,6 @@ rdpTransport* transport_new(rdpContext* context)
if (!transport->log)
goto fail;
- // transport->io.DataHandler = transport_data_handler;
- transport->io.TCPConnect = freerdp_tcp_default_connect;
- transport->io.TLSConnect = transport_default_connect_tls;
- transport->io.TLSAccept = transport_default_accept_tls;
- transport->io.TransportAttach = transport_default_attach;
- transport->io.TransportDisconnect = transport_default_disconnect;
- transport->io.ReadPdu = transport_default_read_pdu;
- transport->io.WritePdu = transport_default_write;
- transport->io.ReadBytes = transport_read_layer;
- transport->io.GetPublicKey = transport_default_get_public_key;
- transport->io.SetBlockingMode = transport_default_set_blocking_mode;
- transport->io.ConnectLayer = transport_default_connect_layer;
- transport->io.AttachLayer = transport_default_attach_layer;
-
transport->context = context;
transport->ReceivePool = StreamPool_New(TRUE, BUFFER_SIZE);
@@ -1698,6 +1684,20 @@ rdpTransport* transport_new(rdpContext* context)
if (!InitializeCriticalSectionAndSpinCount(&(transport->WriteLock), 4000))
goto fail;
+ // transport->io.DataHandler = transport_data_handler;
+ transport->io.TCPConnect = freerdp_tcp_default_connect;
+ transport->io.TLSConnect = transport_default_connect_tls;
+ transport->io.TLSAccept = transport_default_accept_tls;
+ transport->io.TransportAttach = transport_default_attach;
+ transport->io.TransportDisconnect = transport_default_disconnect;
+ transport->io.ReadPdu = transport_default_read_pdu;
+ transport->io.WritePdu = transport_default_write;
+ transport->io.ReadBytes = transport_read_layer;
+ transport->io.GetPublicKey = transport_default_get_public_key;
+ transport->io.SetBlockingMode = transport_default_set_blocking_mode;
+ transport->io.ConnectLayer = transport_default_connect_layer;
+ transport->io.AttachLayer = transport_default_attach_layer;
+
return transport;
fail:
WINPR_PRAGMA_DIAG_PUSH
--
2.49.0

View File

@ -1,51 +0,0 @@
From 8fc7acbfd0a1892bc9237ae25e99d32270812dcc Mon Sep 17 00:00:00 2001
From: akallabeth <akallabeth@posteo.net>
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

View File

@ -1,60 +0,0 @@
From c2fc455d8f2dc60de35ed98cb300b7e6a84fe383 Mon Sep 17 00:00:00 2001
From: Armin Novak <armin.novak@thincast.com>
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

View File

@ -1,321 +0,0 @@
From 00968fd6e647af286e67bbd616168c4de7a39208 Mon Sep 17 00:00:00 2001
From: Armin Novak <armin.novak@thincast.com>
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*)&params[i], &context->priv->ThreadPoolEnv)))
+ if (!(work_objects[i] = CreateThreadpoolWork(rfx_process_message_tile_work_callback,
+ (void*)&params[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(&regionRects[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

View File

@ -1,45 +0,0 @@
From 52106a26726a2aba77aa6d86014d2eb3507f0783 Mon Sep 17 00:00:00 2001
From: akallabeth <akallabeth@posteo.net>
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

View File

@ -1,117 +0,0 @@
From afa6851dc80835d3101e40fcef51b6c5c0f43ea5 Mon Sep 17 00:00:00 2001
From: akallabeth <akallabeth@posteo.net>
Date: Wed, 28 Jan 2026 09:31:06 +0100
Subject: [PATCH] [channel,rdpsnd] only clean up thread before free
rdpsnd channel usually has multiple instances (static, dynamic, ...) so
ensure only to terminate the handler thread when the channel is actually
closed for good.
---
channels/rdpsnd/client/rdpsnd_main.c | 43 ++++++++++++++++------------
1 file changed, 25 insertions(+), 18 deletions(-)
diff --git a/channels/rdpsnd/client/rdpsnd_main.c b/channels/rdpsnd/client/rdpsnd_main.c
index 61a29ec40..5a1edaea6 100644
--- a/channels/rdpsnd/client/rdpsnd_main.c
+++ b/channels/rdpsnd/client/rdpsnd_main.c
@@ -117,6 +117,8 @@ struct rdpsnd_plugin
BOOL async;
};
+static DWORD WINAPI play_thread(LPVOID arg);
+
static const char* rdpsnd_is_dyn_str(BOOL dynamic)
{
if (dynamic)
@@ -1300,7 +1302,6 @@ static void cleanup_internals(rdpsndPlugin* rdpsnd)
if (!rdpsnd)
return;
- rdpsnd_terminate_thread(rdpsnd);
if (rdpsnd->pool)
StreamPool_Return(rdpsnd->pool, rdpsnd->data_in);
@@ -1376,6 +1377,7 @@ static void free_internals(rdpsndPlugin* rdpsnd)
if (rdpsnd->references > 0)
return;
+ rdpsnd_terminate_thread(rdpsnd);
freerdp_dsp_context_free(rdpsnd->dsp_context);
StreamPool_Free(rdpsnd->pool);
rdpsnd->pool = NULL;
@@ -1399,6 +1401,27 @@ static BOOL allocate_internals(rdpsndPlugin* rdpsnd)
if (!rdpsnd->dsp_context)
return FALSE;
}
+
+ if (rdpsnd->async)
+ {
+ if (!rdpsnd->queue)
+ {
+ wObject obj = { 0 };
+
+ obj.fnObjectFree = queue_free;
+ rdpsnd->queue = MessageQueue_New(&obj);
+ if (!rdpsnd->queue)
+ return CHANNEL_RC_NO_MEMORY;
+ }
+
+ if (!rdpsnd->thread)
+ {
+ rdpsnd->thread = CreateThread(NULL, 0, play_thread, rdpsnd, 0, NULL);
+ if (!rdpsnd->thread)
+ return CHANNEL_RC_INITIALIZATION_ERROR;
+ }
+ }
+
rdpsnd->references++;
return TRUE;
@@ -1454,20 +1477,6 @@ static UINT rdpsnd_virtual_channel_event_initialized(rdpsndPlugin* rdpsnd)
if (!rdpsnd)
return ERROR_INVALID_PARAMETER;
- if (rdpsnd->async)
- {
- wObject obj = { 0 };
-
- obj.fnObjectFree = queue_free;
- rdpsnd->queue = MessageQueue_New(&obj);
- if (!rdpsnd->queue)
- return CHANNEL_RC_NO_MEMORY;
-
- rdpsnd->thread = CreateThread(NULL, 0, play_thread, rdpsnd, 0, NULL);
- if (!rdpsnd->thread)
- return CHANNEL_RC_INITIALIZATION_ERROR;
- }
-
if (!allocate_internals(rdpsnd))
return CHANNEL_RC_NO_MEMORY;
@@ -1478,8 +1487,6 @@ void rdpsnd_virtual_channel_event_terminated(rdpsndPlugin* rdpsnd)
{
if (rdpsnd)
{
- rdpsnd_terminate_thread(rdpsnd);
-
free_internals(rdpsnd);
audio_formats_free(rdpsnd->fixed_format, 1);
free(rdpsnd->subsystem);
@@ -1701,13 +1708,13 @@ static UINT rdpsnd_on_close(IWTSVirtualChannelCallback* pChannelCallback)
cleanup_internals(rdpsnd);
+ free_internals(rdpsnd);
if (rdpsnd->device)
{
IFCALL(rdpsnd->device->Free, rdpsnd->device);
rdpsnd->device = NULL;
}
- free_internals(rdpsnd);
free(pChannelCallback);
return CHANNEL_RC_OK;
}
--
2.53.0

View File

@ -1,112 +0,0 @@
From d9ca272dce7a776ab475e9b1a8e8c3d2968c8486 Mon Sep 17 00:00:00 2001
From: akallabeth <akallabeth@posteo.net>
Date: Mon, 26 Jan 2026 12:08:48 +0100
Subject: [PATCH] [channels,ainput] lock context when updating listener
---
channels/ainput/client/ainput_main.c | 36 ++++++++++++++++++++--------
1 file changed, 26 insertions(+), 10 deletions(-)
diff --git a/channels/ainput/client/ainput_main.c b/channels/ainput/client/ainput_main.c
index c291bd727..554575360 100644
--- a/channels/ainput/client/ainput_main.c
+++ b/channels/ainput/client/ainput_main.c
@@ -45,6 +45,7 @@ struct AINPUT_PLUGIN_
AInputClientContext* context;
UINT32 MajorVersion;
UINT32 MinorVersion;
+ CRITICAL_SECTION lock;
};
/**
@@ -85,18 +86,15 @@ static UINT ainput_on_data_received(IWTSVirtualChannelCallback* pChannelCallback
static UINT ainput_send_input_event(AInputClientContext* context, UINT64 flags, INT32 x, INT32 y)
{
- AINPUT_PLUGIN* ainput = NULL;
- GENERIC_CHANNEL_CALLBACK* callback = NULL;
BYTE buffer[32] = { 0 };
- UINT64 time = 0;
wStream sbuffer = { 0 };
wStream* s = Stream_StaticInit(&sbuffer, buffer, sizeof(buffer));
WINPR_ASSERT(s);
WINPR_ASSERT(context);
- time = GetTickCount64();
- ainput = (AINPUT_PLUGIN*)context->handle;
+ const UINT64 time = GetTickCount64();
+ AINPUT_PLUGIN* ainput = (AINPUT_PLUGIN*)context->handle;
WINPR_ASSERT(ainput);
if (ainput->MajorVersion != AINPUT_VERSION_MAJOR)
@@ -105,8 +103,6 @@ static UINT ainput_send_input_event(AInputClientContext* context, UINT64 flags,
ainput->MajorVersion, ainput->MinorVersion);
return CHANNEL_RC_UNSUPPORTED_VERSION;
}
- callback = ainput->base.listener_callback->channel_callback;
- WINPR_ASSERT(callback);
{
char ebuffer[128] = { 0 };
@@ -125,10 +121,15 @@ static UINT ainput_send_input_event(AInputClientContext* context, UINT64 flags,
Stream_SealLength(s);
/* ainput back what we have received. AINPUT does not have any message IDs. */
+ EnterCriticalSection(&ainput->lock);
+ GENERIC_CHANNEL_CALLBACK* callback = ainput->base.listener_callback->channel_callback;
+ WINPR_ASSERT(callback);
WINPR_ASSERT(callback->channel);
WINPR_ASSERT(callback->channel->Write);
- return callback->channel->Write(callback->channel, (ULONG)Stream_Length(s), Stream_Buffer(s),
- NULL);
+ const UINT rc = callback->channel->Write(callback->channel, (ULONG)Stream_Length(s),
+ Stream_Buffer(s), NULL);
+ LeaveCriticalSection(&ainput->lock);
+ return rc;
}
/**
@@ -140,8 +141,16 @@ static UINT ainput_on_close(IWTSVirtualChannelCallback* pChannelCallback)
{
GENERIC_CHANNEL_CALLBACK* callback = (GENERIC_CHANNEL_CALLBACK*)pChannelCallback;
- free(callback);
+ if (callback)
+ {
+ AINPUT_PLUGIN* ainput = (AINPUT_PLUGIN*)callback->plugin;
+ WINPR_ASSERT(ainput);
+ /* Lock here to ensure that no ainput_send_input_event is in progress. */
+ EnterCriticalSection(&ainput->lock);
+ free(callback);
+ LeaveCriticalSection(&ainput->lock);
+ }
return CHANNEL_RC_OK;
}
@@ -156,14 +165,21 @@ static UINT init_plugin_cb(GENERIC_DYNVC_PLUGIN* base, WINPR_ATTR_UNUSED rdpCont
context->handle = (void*)base;
context->AInputSendInputEvent = ainput_send_input_event;
+ InitializeCriticalSection(&ainput->lock);
+
+ EnterCriticalSection(&ainput->lock);
ainput->context = context;
ainput->base.iface.pInterface = context;
+ LeaveCriticalSection(&ainput->lock);
return CHANNEL_RC_OK;
}
static void terminate_plugin_cb(GENERIC_DYNVC_PLUGIN* base)
{
AINPUT_PLUGIN* ainput = (AINPUT_PLUGIN*)base;
+ WINPR_ASSERT(ainput);
+
+ DeleteCriticalSection(&ainput->lock);
free(ainput->context);
}
--
2.53.0

View File

@ -1,29 +0,0 @@
From 1c5c74223179d425a1ce6dbbb6a3dd2a958b7aee Mon Sep 17 00:00:00 2001
From: akallabeth <akallabeth@posteo.net>
Date: Mon, 26 Jan 2026 10:14:08 +0100
Subject: [PATCH] [channels,audin] fix audin_server_recv_formats cleanup
---
channels/audin/server/audin.c | 6 +-----
1 file changed, 1 insertion(+), 5 deletions(-)
diff --git a/channels/audin/server/audin.c b/channels/audin/server/audin.c
index 5046a7d6f..17077efa6 100644
--- a/channels/audin/server/audin.c
+++ b/channels/audin/server/audin.c
@@ -146,11 +146,7 @@ static UINT audin_server_recv_formats(audin_server_context* context, wStream* s,
AUDIO_FORMAT* format = &pdu.SoundFormats[i];
if (!audio_format_read(s, format))
- {
- WLog_Print(audin->log, WLOG_ERROR, "Failed to read audio format");
- audio_formats_free(pdu.SoundFormats, i + i);
- return ERROR_INVALID_DATA;
- }
+ goto fail;
audio_format_print(audin->log, WLOG_DEBUG, format);
}
--
2.53.0

View File

@ -1,27 +0,0 @@
From cd1ffa112cfbe1b40a9fd57e299a8ea12e23df0d Mon Sep 17 00:00:00 2001
From: akallabeth <akallabeth@posteo.net>
Date: Sat, 10 Jan 2026 08:36:38 +0100
Subject: [PATCH] [channels,audin] free up old audio formats
---
channels/audin/client/audin_main.c | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/channels/audin/client/audin_main.c b/channels/audin/client/audin_main.c
index bcaf1a646..b4c8ba580 100644
--- a/channels/audin/client/audin_main.c
+++ b/channels/audin/client/audin_main.c
@@ -206,6 +206,10 @@ static UINT audin_process_formats(AUDIN_PLUGIN* audin, AUDIN_CHANNEL_CALLBACK* c
}
Stream_Seek_UINT32(s); /* cbSizeFormatsPacket */
+
+ audio_formats_free(callback->formats, callback->formats_count);
+ callback->formats_count = 0;
+
callback->formats = audio_formats_new(NumFormats);
if (!callback->formats)
--
2.53.0

View File

@ -1,33 +0,0 @@
From 026b81ae5831ac1598d8f7371e0d0996fac7db00 Mon Sep 17 00:00:00 2001
From: akallabeth <akallabeth@posteo.net>
Date: Mon, 26 Jan 2026 10:20:23 +0100
Subject: [PATCH] [channels,audin] reset audin->format
Whenever the underlying structure changes reset the pointer to NULL
---
channels/audin/client/audin_main.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/channels/audin/client/audin_main.c b/channels/audin/client/audin_main.c
index c57c65a62..76d87bb9c 100644
--- a/channels/audin/client/audin_main.c
+++ b/channels/audin/client/audin_main.c
@@ -207,6 +207,7 @@ static UINT audin_process_formats(AUDIN_PLUGIN* audin, AUDIN_CHANNEL_CALLBACK* c
Stream_Seek_UINT32(s); /* cbSizeFormatsPacket */
+ audin->format = NULL;
audio_formats_free(callback->formats, callback->formats_count);
callback->formats_count = 0;
@@ -284,6 +285,7 @@ out:
if (error != CHANNEL_RC_OK)
{
+ audin->format = NULL;
audio_formats_free(callback->formats, NumFormats);
callback->formats = NULL;
}
--
2.53.0

View File

@ -1,34 +0,0 @@
From 668352a2e241ba017679c11a22ecbe29d0b17401 Mon Sep 17 00:00:00 2001
From: Ondrej Holy <oholy@redhat.com>
Date: Tue, 17 Mar 2026 09:34:08 +0100
Subject: [PATCH] [channels,audin] set error when audio_format_read fails
Currently, `goto fail` is called when `audio_format_read` fails, but
`error` is not changed, so `CHANNEL_RC_OK` is returned. Before the
1c5c7422 commit, `ERROR_INVALID_DATA` was returned. Let's again set
that error code and also restore the debug print.
Made-with: Cursor
---
channels/audin/server/audin.c | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/channels/audin/server/audin.c b/channels/audin/server/audin.c
index ae36df6bf..1d3ddd411 100644
--- a/channels/audin/server/audin.c
+++ b/channels/audin/server/audin.c
@@ -146,7 +146,11 @@ static UINT audin_server_recv_formats(audin_server_context* context, wStream* s,
AUDIO_FORMAT* format = &pdu.SoundFormats[i];
if (!audio_format_read(s, format))
+ {
+ WLog_Print(audin->log, WLOG_ERROR, "Failed to read audio format");
+ error = ERROR_INVALID_DATA;
goto fail;
+ }
audio_format_print(audin->log, WLOG_DEBUG, format);
}
--
2.53.0

View File

@ -1,33 +0,0 @@
From 54ab7e13650f3ef9d912e9d0e336b9d7f22537ca Mon Sep 17 00:00:00 2001
From: Ondrej Holy <oholy@redhat.com>
Date: Thu, 12 Mar 2026 10:20:59 +0100
Subject: [PATCH] [channels,drdynvc] check pointer before reset
Backport of commit cb7f295bc750de86480d60a3b58cebc56a57a1c4.
Made-with: Cursor
---
channels/drdynvc/client/drdynvc_main.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/channels/drdynvc/client/drdynvc_main.c b/channels/drdynvc/client/drdynvc_main.c
index e1c121f55..06ee325e5 100644
--- a/channels/drdynvc/client/drdynvc_main.c
+++ b/channels/drdynvc/client/drdynvc_main.c
@@ -486,11 +486,12 @@ static UINT dvcman_channel_close(DVCMAN_CHANNEL* channel, BOOL perRequest, BOOL
channel->state = DVC_CHANNEL_CLOSED;
+ check_open_close_receive(channel);
+
IWTSVirtualChannelCallback* cb = channel->channel_callback;
channel->channel_callback = NULL;
if (cb)
{
- check_open_close_receive(channel);
IFCALL(cb->OnClose, cb);
}
--
2.53.0

View File

@ -1,35 +0,0 @@
From 429260893d7f67011d916394974ec0294ff65a90 Mon Sep 17 00:00:00 2001
From: Ondrej Holy <oholy@redhat.com>
Date: Wed, 11 Mar 2026 09:16:34 +0100
Subject: [PATCH] [channels,drdynvc] reset channel_callback before close
Backport of commit e02e052f6692550e539d10f99de9c35a23492db2.
Made-with: Cursor
---
channels/drdynvc/client/drdynvc_main.c | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/channels/drdynvc/client/drdynvc_main.c b/channels/drdynvc/client/drdynvc_main.c
index 62ff674e5..e1c121f55 100644
--- a/channels/drdynvc/client/drdynvc_main.c
+++ b/channels/drdynvc/client/drdynvc_main.c
@@ -487,14 +487,13 @@ static UINT dvcman_channel_close(DVCMAN_CHANNEL* channel, BOOL perRequest, BOOL
channel->state = DVC_CHANNEL_CLOSED;
IWTSVirtualChannelCallback* cb = channel->channel_callback;
+ channel->channel_callback = NULL;
if (cb)
{
check_open_close_receive(channel);
IFCALL(cb->OnClose, cb);
}
- channel->channel_callback = NULL;
-
if (channel->dvcman && channel->dvcman->drdynvc)
{
if (context)
--
2.53.0

View File

@ -1,26 +0,0 @@
From 3da319570c8a6be0a79b3306f1ed354c4a943259 Mon Sep 17 00:00:00 2001
From: akallabeth <akallabeth@posteo.net>
Date: Mon, 12 Jan 2026 03:44:06 +0100
Subject: [PATCH] [channels,drive] fix constant type
ensure constant is of 64bit integer type
---
channels/drive/client/drive_main.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/channels/drive/client/drive_main.c b/channels/drive/client/drive_main.c
index 1dce5c348..13188fbc6 100644
--- a/channels/drive/client/drive_main.c
+++ b/channels/drive/client/drive_main.c
@@ -302,7 +302,7 @@ static UINT drive_process_irp_read(DRIVE_DEVICE* drive, IRP* irp)
Length = 0;
}
- if (!Stream_EnsureRemainingCapacity(irp->output, Length + 4))
+ if (!Stream_EnsureRemainingCapacity(irp->output, 4ull + Length))
{
WLog_ERR(TAG, "Stream_EnsureRemainingCapacity failed!");
return ERROR_INTERNAL_ERROR;
--
2.53.0

View File

@ -1,69 +0,0 @@
From 19f48dc7d615984a24a9be89f50ef9eb8f9bdb6a Mon Sep 17 00:00:00 2001
From: akallabeth <akallabeth@posteo.net>
Date: Mon, 12 Jan 2026 15:02:53 +0100
Subject: [PATCH] [channels,rdpear] add checks for itemSize
when a ndr read function is called with invalid arguments abort early.
---
channels/rdpear/common/ndr.c | 22 ++++++++++++++++++----
1 file changed, 18 insertions(+), 4 deletions(-)
diff --git a/channels/rdpear/common/ndr.c b/channels/rdpear/common/ndr.c
index d0bd6d959..ddb15a340 100644
--- a/channels/rdpear/common/ndr.c
+++ b/channels/rdpear/common/ndr.c
@@ -518,6 +518,9 @@ BOOL ndr_read_uconformant_varying_array(NdrContext* context, wStream* s,
WINPR_ASSERT(itemType);
WINPR_ASSERT(ptarget);
+ if (itemType->itemSize == 0)
+ return FALSE;
+
UINT32 maxCount = 0;
UINT32 offset = 0;
UINT32 length = 0;
@@ -526,10 +529,10 @@ BOOL ndr_read_uconformant_varying_array(NdrContext* context, wStream* s,
!ndr_read_uint32(context, s, &length))
return FALSE;
- if ((length * itemType->itemSize) < hints->length)
+ if ((1ull * length * itemType->itemSize) > hints->length)
return FALSE;
- if ((maxCount * itemType->itemSize) < hints->maxLength)
+ if ((1ull * maxCount * itemType->itemSize) > hints->maxLength)
return FALSE;
BYTE* target = (BYTE*)ptarget;
@@ -552,6 +555,9 @@ BOOL ndr_write_uconformant_varying_array(NdrContext* context, wStream* s,
WINPR_ASSERT(itemType);
WINPR_ASSERT(psrc);
+ if (itemType->itemSize == 0)
+ return FALSE;
+
if (!ndr_write_uint32(context, s, hints->maxLength) || !ndr_write_uint32(context, s, 0) ||
!ndr_write_uint32(context, s, hints->length))
return FALSE;
@@ -581,8 +587,16 @@ BOOL ndr_read_uconformant_array(NdrContext* context, wStream* s, const NdrArrayH
if (!ndr_read_uint32(context, s, &count))
return FALSE;
- if ((count * itemType->itemSize < hints->count))
- return FALSE;
+ if (itemType->arity == NDR_ARITY_SIMPLE)
+ {
+ if (count > hints->count)
+ return FALSE;
+ }
+ else
+ {
+ if ((1ull * count * itemType->itemSize) > hints->count)
+ return FALSE;
+ }
BYTE* target = (BYTE*)vtarget;
for (UINT32 i = 0; i < count; i++, target += itemType->itemSize)
--
2.52.0

View File

@ -1,27 +0,0 @@
From f3ab1a16139036179d9852745fdade18fec11600 Mon Sep 17 00:00:00 2001
From: akallabeth <akallabeth@posteo.net>
Date: Mon, 26 Jan 2026 10:54:33 +0100
Subject: [PATCH] [channels,rdpecam] ensure all streams are stopped
When closing the channel ensure there are no more streams running.
---
channels/rdpecam/client/camera_device_main.c | 3 +++
1 file changed, 3 insertions(+)
diff --git a/channels/rdpecam/client/camera_device_main.c b/channels/rdpecam/client/camera_device_main.c
index b76a37ca8..9ba3c65c5 100644
--- a/channels/rdpecam/client/camera_device_main.c
+++ b/channels/rdpecam/client/camera_device_main.c
@@ -789,6 +789,9 @@ static UINT ecam_dev_on_close(IWTSVirtualChannelCallback* pChannelCallback)
WLog_DBG(TAG, "entered");
+ for (size_t i = 0; i < ECAM_DEVICE_MAX_STREAMS; i++)
+ ecam_dev_stop_stream(dev, i);
+
/* make sure this channel is not used for sample responses */
for (size_t i = 0; i < ECAM_DEVICE_MAX_STREAMS; i++)
if (dev->streams[i].hSampleReqChannel == hchannel)
--
2.52.0

View File

@ -1,65 +0,0 @@
From 622bb7b4402491ca003f47472d0e478132673696 Mon Sep 17 00:00:00 2001
From: akallabeth <akallabeth@posteo.net>
Date: Mon, 26 Jan 2026 10:48:14 +0100
Subject: [PATCH] [channels,rdpsnd] terminate thread before free
Ensure that the optional rdpsnd thread is terminated and the message
queue freed up before releasing the channel context memory
---
channels/rdpsnd/client/rdpsnd_main.c | 28 +++++++++++++++++++---------
1 file changed, 19 insertions(+), 9 deletions(-)
diff --git a/channels/rdpsnd/client/rdpsnd_main.c b/channels/rdpsnd/client/rdpsnd_main.c
index 49c763a87..61a29ec40 100644
--- a/channels/rdpsnd/client/rdpsnd_main.c
+++ b/channels/rdpsnd/client/rdpsnd_main.c
@@ -1278,11 +1278,29 @@ fail:
return CHANNEL_RC_NO_MEMORY;
}
+static void rdpsnd_terminate_thread(rdpsndPlugin* rdpsnd)
+{
+ WINPR_ASSERT(rdpsnd);
+ if (rdpsnd->queue)
+ MessageQueue_PostQuit(rdpsnd->queue, 0);
+
+ if (rdpsnd->thread)
+ {
+ (void)WaitForSingleObject(rdpsnd->thread, INFINITE);
+ (void)CloseHandle(rdpsnd->thread);
+ }
+
+ MessageQueue_Free(rdpsnd->queue);
+ rdpsnd->thread = NULL;
+ rdpsnd->queue = NULL;
+}
+
static void cleanup_internals(rdpsndPlugin* rdpsnd)
{
if (!rdpsnd)
return;
+ rdpsnd_terminate_thread(rdpsnd);
if (rdpsnd->pool)
StreamPool_Return(rdpsnd->pool, rdpsnd->data_in);
@@ -1460,15 +1478,7 @@ void rdpsnd_virtual_channel_event_terminated(rdpsndPlugin* rdpsnd)
{
if (rdpsnd)
{
- if (rdpsnd->queue)
- MessageQueue_PostQuit(rdpsnd->queue, 0);
-
- if (rdpsnd->thread)
- {
- (void)WaitForSingleObject(rdpsnd->thread, INFINITE);
- (void)CloseHandle(rdpsnd->thread);
- }
- MessageQueue_Free(rdpsnd->queue);
+ rdpsnd_terminate_thread(rdpsnd);
free_internals(rdpsnd);
audio_formats_free(rdpsnd->fixed_format, 1);
--
2.53.0

View File

@ -1,47 +0,0 @@
From 675c20f08f32ca5ec06297108bdf30147d6e2cd9 Mon Sep 17 00:00:00 2001
From: akallabeth <akallabeth@posteo.net>
Date: Tue, 13 Jan 2026 09:39:33 +0100
Subject: [PATCH] [channels,serial] explicitly lock serial->IrpThreads
---
channels/serial/client/serial_main.c | 8 +++++++-
1 file changed, 7 insertions(+), 1 deletion(-)
diff --git a/channels/serial/client/serial_main.c b/channels/serial/client/serial_main.c
index 7ec744afa..59ef0cb9b 100644
--- a/channels/serial/client/serial_main.c
+++ b/channels/serial/client/serial_main.c
@@ -626,7 +626,10 @@ static void create_irp_thread(SERIAL_DEVICE* serial, IRP* irp)
* observed with FreeRDP).
*/
key = irp->CompletionId + 1ull;
+
+ ListDictionary_Lock(serial->IrpThreads);
previousIrpThread = ListDictionary_GetItemValue(serial->IrpThreads, (void*)key);
+ ListDictionary_Unlock(serial->IrpThreads);
if (previousIrpThread)
{
@@ -742,7 +745,10 @@ static DWORD WINAPI serial_thread_func(LPVOID arg)
create_irp_thread(serial, irp);
}
+ ListDictionary_Lock(serial->IrpThreads);
ListDictionary_Clear(serial->IrpThreads);
+ ListDictionary_Unlock(serial->IrpThreads);
+
if (error && serial->rdpcontext)
setChannelError(serial->rdpcontext, error, "serial_thread_func reported an error");
@@ -971,7 +977,7 @@ FREERDP_ENTRY_POINT(
}
/* IrpThreads content only modified by create_irp_thread() */
- serial->IrpThreads = ListDictionary_New(TRUE);
+ serial->IrpThreads = ListDictionary_New(FALSE);
if (!serial->IrpThreads)
{
--
2.53.0

View File

@ -1,87 +0,0 @@
From b35aa3614d32bff3fc1272cd7c4617f711fca1a4 Mon Sep 17 00:00:00 2001
From: akallabeth <akallabeth@posteo.net>
Date: Mon, 12 Jan 2026 09:02:50 +0100
Subject: [PATCH] [channels,serial] lock list dictionary
prevent sync problems for list dictionary
---
channels/serial/client/serial_main.c | 35 ++++++++++++++++------------
1 file changed, 20 insertions(+), 15 deletions(-)
diff --git a/channels/serial/client/serial_main.c b/channels/serial/client/serial_main.c
index 308afe217..7ec744afa 100644
--- a/channels/serial/client/serial_main.c
+++ b/channels/serial/client/serial_main.c
@@ -584,6 +584,7 @@ void close_terminated_irp_thread_handles(SERIAL_DEVICE* serial, BOOL forceClose)
EnterCriticalSection(&serial->TerminatingIrpThreadsLock);
+ ListDictionary_Lock(serial->IrpThreads);
ULONG_PTR* ids = NULL;
const size_t nbIds = ListDictionary_GetKeys(serial->IrpThreads, &ids);
@@ -597,6 +598,7 @@ void close_terminated_irp_thread_handles(SERIAL_DEVICE* serial, BOOL forceClose)
}
free(ids);
+ ListDictionary_Unlock(serial->IrpThreads);
LeaveCriticalSection(&serial->TerminatingIrpThreadsLock);
}
@@ -650,19 +652,6 @@ static void create_irp_thread(SERIAL_DEVICE* serial, IRP* irp)
return;
}
- if (ListDictionary_Count(serial->IrpThreads) >= MAX_IRP_THREADS)
- {
- WLog_Print(serial->log, WLOG_WARN,
- "Number of IRP threads threshold reached: %" PRIuz ", keep on anyway",
- ListDictionary_Count(serial->IrpThreads));
- WINPR_ASSERT(FALSE); /* unimplemented */
- /* TODO: MAX_IRP_THREADS has been thought to avoid a
- * flooding of pending requests. Use
- * WaitForMultipleObjects() when available in winpr
- * for threads.
- */
- }
-
/* error_handle to be used ... */
data = (IRP_THREAD_DATA*)calloc(1, sizeof(IRP_THREAD_DATA));
@@ -685,7 +674,23 @@ static void create_irp_thread(SERIAL_DEVICE* serial, IRP* irp)
key = irp->CompletionId + 1ull;
- if (!ListDictionary_Add(serial->IrpThreads, (void*)key, irpThread))
+ ListDictionary_Lock(serial->IrpThreads);
+ if (ListDictionary_Count(serial->IrpThreads) >= MAX_IRP_THREADS)
+ {
+ WLog_Print(serial->log, WLOG_WARN,
+ "Number of IRP threads threshold reached: %" PRIuz ", keep on anyway",
+ ListDictionary_Count(serial->IrpThreads));
+ WINPR_ASSERT(FALSE); /* unimplemented */
+ /* TODO: MAX_IRP_THREADS has been thought to avoid a
+ * flooding of pending requests. Use
+ * WaitForMultipleObjects() when available in winpr
+ * for threads.
+ */
+ }
+ const BOOL added = ListDictionary_Add(serial->IrpThreads, (void*)key, irpThread);
+ ListDictionary_Unlock(serial->IrpThreads);
+
+ if (!added)
{
WLog_Print(serial->log, WLOG_ERROR, "ListDictionary_Add failed!");
goto error_handle;
@@ -966,7 +971,7 @@ FREERDP_ENTRY_POINT(
}
/* IrpThreads content only modified by create_irp_thread() */
- serial->IrpThreads = ListDictionary_New(FALSE);
+ serial->IrpThreads = ListDictionary_New(TRUE);
if (!serial->IrpThreads)
{
--
2.53.0

View File

@ -1,24 +0,0 @@
From 414f701464929c217f2509bcbd6d2c1f00f7ed73 Mon Sep 17 00:00:00 2001
From: akallabeth <akallabeth@posteo.net>
Date: Mon, 26 Jan 2026 11:07:25 +0100
Subject: [PATCH] [channels,urbdrc] cancel all usb transfers on channel close
---
channels/urbdrc/client/libusb/libusb_udevice.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/channels/urbdrc/client/libusb/libusb_udevice.c b/channels/urbdrc/client/libusb/libusb_udevice.c
index 5341248ec..9e2d3ec5a 100644
--- a/channels/urbdrc/client/libusb/libusb_udevice.c
+++ b/channels/urbdrc/client/libusb/libusb_udevice.c
@@ -1165,6 +1165,7 @@ static void libusb_udev_mark_channel_closed(IUDEVICE* idev)
const uint8_t devNr = idev->get_dev_number(idev);
pdev->status |= URBDRC_DEVICE_CHANNEL_CLOSED;
+ pdev->iface.cancel_all_transfer_request(&pdev->iface);
urbdrc->udevman->unregister_udevice(urbdrc->udevman, busNr, devNr);
}
}
--
2.53.0

View File

@ -1,197 +0,0 @@
From 7b7e6de8fe427a2f01d331056774aec69710590b Mon Sep 17 00:00:00 2001
From: akallabeth <akallabeth@posteo.net>
Date: Sat, 10 Jan 2026 08:43:40 +0100
Subject: [PATCH] [channels,urbdrc] check interface indices before use
---
channels/urbdrc/client/data_transfer.c | 6 +-
.../urbdrc/client/libusb/libusb_udevice.c | 78 ++++++++++++-------
channels/urbdrc/common/msusb.c | 6 +-
3 files changed, 54 insertions(+), 36 deletions(-)
diff --git a/channels/urbdrc/client/data_transfer.c b/channels/urbdrc/client/data_transfer.c
index e2270e565..762fe0d31 100644
--- a/channels/urbdrc/client/data_transfer.c
+++ b/channels/urbdrc/client/data_transfer.c
@@ -454,14 +454,12 @@ static void func_select_all_interface_for_msconfig(IUDEVICE* pdev,
MSUSB_CONFIG_DESCRIPTOR* MsConfig)
{
MSUSB_INTERFACE_DESCRIPTOR** MsInterfaces = MsConfig->MsInterfaces;
- BYTE InterfaceNumber = 0;
- BYTE AlternateSetting = 0;
UINT32 NumInterfaces = MsConfig->NumInterfaces;
for (UINT32 inum = 0; inum < NumInterfaces; inum++)
{
- InterfaceNumber = MsInterfaces[inum]->InterfaceNumber;
- AlternateSetting = MsInterfaces[inum]->AlternateSetting;
+ const BYTE InterfaceNumber = MsInterfaces[inum]->InterfaceNumber;
+ const BYTE AlternateSetting = MsInterfaces[inum]->AlternateSetting;
pdev->select_interface(pdev, InterfaceNumber, AlternateSetting);
}
}
diff --git a/channels/urbdrc/client/libusb/libusb_udevice.c b/channels/urbdrc/client/libusb/libusb_udevice.c
index afc7f11c6..aa5639f09 100644
--- a/channels/urbdrc/client/libusb/libusb_udevice.c
+++ b/channels/urbdrc/client/libusb/libusb_udevice.c
@@ -582,25 +582,13 @@ static MSUSB_CONFIG_DESCRIPTOR*
libusb_udev_complete_msconfig_setup(IUDEVICE* idev, MSUSB_CONFIG_DESCRIPTOR* MsConfig)
{
UDEVICE* pdev = (UDEVICE*)idev;
- MSUSB_INTERFACE_DESCRIPTOR** MsInterfaces = NULL;
- MSUSB_INTERFACE_DESCRIPTOR* MsInterface = NULL;
- MSUSB_PIPE_DESCRIPTOR** MsPipes = NULL;
- MSUSB_PIPE_DESCRIPTOR* MsPipe = NULL;
- MSUSB_PIPE_DESCRIPTOR** t_MsPipes = NULL;
- MSUSB_PIPE_DESCRIPTOR* t_MsPipe = NULL;
- LIBUSB_CONFIG_DESCRIPTOR* LibusbConfig = NULL;
- const LIBUSB_INTERFACE* LibusbInterface = NULL;
- const LIBUSB_INTERFACE_DESCRIPTOR* LibusbAltsetting = NULL;
- const LIBUSB_ENDPOINT_DESCEIPTOR* LibusbEndpoint = NULL;
- BYTE LibusbNumEndpoint = 0;
- URBDRC_PLUGIN* urbdrc = NULL;
UINT32 MsOutSize = 0;
if (!pdev || !pdev->LibusbConfig || !pdev->urbdrc || !MsConfig)
return NULL;
- urbdrc = pdev->urbdrc;
- LibusbConfig = pdev->LibusbConfig;
+ URBDRC_PLUGIN* urbdrc = pdev->urbdrc;
+ LIBUSB_CONFIG_DESCRIPTOR* LibusbConfig = pdev->LibusbConfig;
if (LibusbConfig->bNumInterfaces != MsConfig->NumInterfaces)
{
@@ -608,28 +596,57 @@ libusb_udev_complete_msconfig_setup(IUDEVICE* idev, MSUSB_CONFIG_DESCRIPTOR* MsC
"Select Configuration: Libusb NumberInterfaces(%" PRIu8 ") is different "
"with MsConfig NumberInterfaces(%" PRIu32 ")",
LibusbConfig->bNumInterfaces, MsConfig->NumInterfaces);
+ return NULL;
}
/* replace MsPipes for libusb */
- MsInterfaces = MsConfig->MsInterfaces;
+ MSUSB_INTERFACE_DESCRIPTOR** MsInterfaces = MsConfig->MsInterfaces;
for (UINT32 inum = 0; inum < MsConfig->NumInterfaces; inum++)
{
- MsInterface = MsInterfaces[inum];
+ MSUSB_INTERFACE_DESCRIPTOR* MsInterface = MsInterfaces[inum];
+ if (MsInterface->InterfaceNumber >= MsConfig->NumInterfaces)
+ {
+ WLog_Print(urbdrc->log, WLOG_ERROR,
+ "MSUSB_CONFIG_DESCRIPTOR::NumInterfaces (%" PRIu32
+ " <= MSUSB_INTERFACE_DESCRIPTOR::InterfaceNumber( %" PRIu8 ")",
+ MsConfig->NumInterfaces, MsInterface->InterfaceNumber);
+ return NULL;
+ }
+
+ const LIBUSB_INTERFACE* LibusbInterface =
+ &LibusbConfig->interface[MsInterface->InterfaceNumber];
+ if (MsInterface->AlternateSetting >= LibusbInterface->num_altsetting)
+ {
+ WLog_Print(urbdrc->log, WLOG_ERROR,
+ "LIBUSB_INTERFACE::num_altsetting (%" PRId32
+ " <= MSUSB_INTERFACE_DESCRIPTOR::AlternateSetting( %" PRIu8 ")",
+ LibusbInterface->num_altsetting, MsInterface->AlternateSetting);
+ return NULL;
+ }
+ }
+
+ for (UINT32 inum = 0; inum < MsConfig->NumInterfaces; inum++)
+ {
+ MSUSB_INTERFACE_DESCRIPTOR* MsInterface = MsInterfaces[inum];
+
/* get libusb's number of endpoints */
- LibusbInterface = &LibusbConfig->interface[MsInterface->InterfaceNumber];
- LibusbAltsetting = &LibusbInterface->altsetting[MsInterface->AlternateSetting];
- LibusbNumEndpoint = LibusbAltsetting->bNumEndpoints;
- t_MsPipes =
+ const LIBUSB_INTERFACE* LibusbInterface =
+ &LibusbConfig->interface[MsInterface->InterfaceNumber];
+ const LIBUSB_INTERFACE_DESCRIPTOR* LibusbAltsetting =
+ &LibusbInterface->altsetting[MsInterface->AlternateSetting];
+ const BYTE LibusbNumEndpoint = LibusbAltsetting->bNumEndpoints;
+ MSUSB_PIPE_DESCRIPTOR** t_MsPipes =
(MSUSB_PIPE_DESCRIPTOR**)calloc(LibusbNumEndpoint, sizeof(MSUSB_PIPE_DESCRIPTOR*));
for (UINT32 pnum = 0; pnum < LibusbNumEndpoint; pnum++)
{
- t_MsPipe = (MSUSB_PIPE_DESCRIPTOR*)calloc(1, sizeof(MSUSB_PIPE_DESCRIPTOR));
+ MSUSB_PIPE_DESCRIPTOR* t_MsPipe =
+ (MSUSB_PIPE_DESCRIPTOR*)calloc(1, sizeof(MSUSB_PIPE_DESCRIPTOR));
if (pnum < MsInterface->NumberOfPipes && MsInterface->MsPipes)
{
- MsPipe = MsInterface->MsPipes[pnum];
+ MSUSB_PIPE_DESCRIPTOR* MsPipe = MsInterface->MsPipes[pnum];
t_MsPipe->MaximumPacketSize = MsPipe->MaximumPacketSize;
t_MsPipe->MaximumTransferSize = MsPipe->MaximumTransferSize;
t_MsPipe->PipeFlags = MsPipe->PipeFlags;
@@ -668,10 +685,12 @@ libusb_udev_complete_msconfig_setup(IUDEVICE* idev, MSUSB_CONFIG_DESCRIPTOR* MsC
for (UINT32 inum = 0; inum < MsConfig->NumInterfaces; inum++)
{
MsOutSize += 16;
- MsInterface = MsInterfaces[inum];
+ MSUSB_INTERFACE_DESCRIPTOR* MsInterface = MsInterfaces[inum];
/* get libusb's interface */
- LibusbInterface = &LibusbConfig->interface[MsInterface->InterfaceNumber];
- LibusbAltsetting = &LibusbInterface->altsetting[MsInterface->AlternateSetting];
+ const LIBUSB_INTERFACE* LibusbInterface =
+ &LibusbConfig->interface[MsInterface->InterfaceNumber];
+ const LIBUSB_INTERFACE_DESCRIPTOR* LibusbAltsetting =
+ &LibusbInterface->altsetting[MsInterface->AlternateSetting];
/* InterfaceHandle: 4 bytes
* ---------------------------------------------------------------
* ||<<< 1 byte >>>|<<< 1 byte >>>|<<< 1 byte >>>|<<< 1 byte >>>||
@@ -688,15 +707,16 @@ libusb_udev_complete_msconfig_setup(IUDEVICE* idev, MSUSB_CONFIG_DESCRIPTOR* MsC
MsInterface->bInterfaceSubClass = LibusbAltsetting->bInterfaceSubClass;
MsInterface->bInterfaceProtocol = LibusbAltsetting->bInterfaceProtocol;
MsInterface->InitCompleted = 1;
- MsPipes = MsInterface->MsPipes;
- LibusbNumEndpoint = LibusbAltsetting->bNumEndpoints;
+ MSUSB_PIPE_DESCRIPTOR** MsPipes = MsInterface->MsPipes;
+ const BYTE LibusbNumEndpoint = LibusbAltsetting->bNumEndpoints;
for (UINT32 pnum = 0; pnum < LibusbNumEndpoint; pnum++)
{
MsOutSize += 20;
- MsPipe = MsPipes[pnum];
+
+ MSUSB_PIPE_DESCRIPTOR* MsPipe = MsPipes[pnum];
/* get libusb's endpoint */
- LibusbEndpoint = &LibusbAltsetting->endpoint[pnum];
+ const LIBUSB_ENDPOINT_DESCEIPTOR* LibusbEndpoint = &LibusbAltsetting->endpoint[pnum];
/* PipeHandle: 4 bytes
* ---------------------------------------------------------------
* ||<<< 1 byte >>>|<<< 1 byte >>>|<<<<<<<<<< 2 byte >>>>>>>>>>>||
diff --git a/channels/urbdrc/common/msusb.c b/channels/urbdrc/common/msusb.c
index 8d6809741..1b6e29aeb 100644
--- a/channels/urbdrc/common/msusb.c
+++ b/channels/urbdrc/common/msusb.c
@@ -134,6 +134,8 @@ BOOL msusb_msinterface_replace(MSUSB_CONFIG_DESCRIPTOR* MsConfig, BYTE Interface
{
if (!MsConfig || !MsConfig->MsInterfaces)
return FALSE;
+ if (MsConfig->NumInterfaces <= InterfaceNumber)
+ return FALSE;
msusb_msinterface_free(MsConfig->MsInterfaces[InterfaceNumber]);
MsConfig->MsInterfaces[InterfaceNumber] = NewMsInterface;
@@ -142,12 +144,10 @@ BOOL msusb_msinterface_replace(MSUSB_CONFIG_DESCRIPTOR* MsConfig, BYTE Interface
MSUSB_INTERFACE_DESCRIPTOR* msusb_msinterface_read(wStream* s)
{
- MSUSB_INTERFACE_DESCRIPTOR* MsInterface = NULL;
-
if (!Stream_CheckAndLogRequiredCapacity(TAG, (s), 12))
return NULL;
- MsInterface = msusb_msinterface_new();
+ MSUSB_INTERFACE_DESCRIPTOR* MsInterface = msusb_msinterface_new();
if (!MsInterface)
return NULL;
--
2.52.0

View File

@ -1,29 +0,0 @@
From a304360d05cd2692ac4d87af017277d934863616 Mon Sep 17 00:00:00 2001
From: akallabeth <akallabeth@posteo.net>
Date: Mon, 26 Jan 2026 11:54:56 +0100
Subject: [PATCH] [channels,urbdrc] do not free MsConfig on failure
Backport of commit d676518809c319eec15911c705c13536036af2ae.
---
channels/urbdrc/client/data_transfer.c | 4 +---
1 file changed, 1 insertion(+), 3 deletions(-)
diff --git a/channels/urbdrc/client/data_transfer.c b/channels/urbdrc/client/data_transfer.c
index 000ee2e1d..f6f366494 100644
--- a/channels/urbdrc/client/data_transfer.c
+++ b/channels/urbdrc/client/data_transfer.c
@@ -575,10 +575,8 @@ static UINT urb_select_interface(IUDEVICE* pdev, GENERIC_CHANNEL_CALLBACK* callb
MsConfig = pdev->get_MsConfig(pdev);
InterfaceNumber = MsInterface->InterfaceNumber;
if (!msusb_msinterface_replace(MsConfig, InterfaceNumber, MsInterface))
- {
- msusb_msconfig_free(MsConfig);
return ERROR_BAD_CONFIGURATION;
- }
+
/* complete configuration setup */
if (!pdev->complete_msconfig_setup(pdev, MsConfig))
{
--
2.53.0

View File

@ -1,42 +0,0 @@
From 2d563a50be17c1b407ca448b1321378c0726dd31 Mon Sep 17 00:00:00 2001
From: akallabeth <akallabeth@posteo.net>
Date: Mon, 26 Jan 2026 10:59:39 +0100
Subject: [PATCH] [channels,urbdrc] ensure InterfaceNumber is within range
---
channels/urbdrc/client/libusb/libusb_udevice.c | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/channels/urbdrc/client/libusb/libusb_udevice.c b/channels/urbdrc/client/libusb/libusb_udevice.c
index 6c2376f74..5341248ec 100644
--- a/channels/urbdrc/client/libusb/libusb_udevice.c
+++ b/channels/urbdrc/client/libusb/libusb_udevice.c
@@ -539,19 +539,19 @@ static int libusb_udev_select_interface(IUDEVICE* idev, BYTE InterfaceNumber, BY
int error = 0;
int diff = 0;
UDEVICE* pdev = (UDEVICE*)idev;
- URBDRC_PLUGIN* urbdrc = NULL;
- MSUSB_CONFIG_DESCRIPTOR* MsConfig = NULL;
- MSUSB_INTERFACE_DESCRIPTOR** MsInterfaces = NULL;
if (!pdev || !pdev->urbdrc)
return -1;
- urbdrc = pdev->urbdrc;
- MsConfig = pdev->MsConfig;
+ URBDRC_PLUGIN* urbdrc = pdev->urbdrc;
+ MSUSB_CONFIG_DESCRIPTOR* MsConfig = pdev->MsConfig;
if (MsConfig)
{
- MsInterfaces = MsConfig->MsInterfaces;
+ if (InterfaceNumber >= MsConfig->NumInterfaces)
+ return -2;
+
+ MSUSB_INTERFACE_DESCRIPTOR** MsInterfaces = MsConfig->MsInterfaces;
if (MsInterfaces)
{
WLog_Print(urbdrc->log, WLOG_INFO,
--
2.53.0

View File

@ -1,37 +0,0 @@
From bd3c5d3ab73cbe10ac3c079fb0b26610d3231133 Mon Sep 17 00:00:00 2001
From: Ondrej Holy <oholy@redhat.com>
Date: Thu, 26 Mar 2026 13:39:12 +0100
Subject: [PATCH] [channels,video] fix wrong cast
Backport of commit e01cd85c8003a245ef9778f0eda4b9235514c201.
Made-with: Cursor
---
channels/video/client/video_main.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/channels/video/client/video_main.c b/channels/video/client/video_main.c
index c2677992b..5177f6d0f 100644
--- a/channels/video/client/video_main.c
+++ b/channels/video/client/video_main.c
@@ -1005,7 +1005,7 @@ static UINT video_control_on_close(IWTSVirtualChannelCallback* pChannelCallback)
{
if (pChannelCallback)
{
- GENERIC_LISTENER_CALLBACK* listener_callback = (GENERIC_LISTENER_CALLBACK*)pChannelCallback;
+ GENERIC_CHANNEL_CALLBACK* listener_callback = (GENERIC_CHANNEL_CALLBACK*)pChannelCallback;
VIDEO_PLUGIN* video = (VIDEO_PLUGIN*)listener_callback->plugin;
if (video && video->control_callback)
{
@@ -1020,7 +1020,7 @@ static UINT video_data_on_close(IWTSVirtualChannelCallback* pChannelCallback)
{
if (pChannelCallback)
{
- GENERIC_LISTENER_CALLBACK* listener_callback = (GENERIC_LISTENER_CALLBACK*)pChannelCallback;
+ GENERIC_CHANNEL_CALLBACK* listener_callback = (GENERIC_CHANNEL_CALLBACK*)pChannelCallback;
VIDEO_PLUGIN* video = (VIDEO_PLUGIN*)listener_callback->plugin;
if (video && video->data_callback)
{
--
2.53.0

View File

@ -1,117 +0,0 @@
From 0328bb3828b836d53b904187b0b6d49f940d1180 Mon Sep 17 00:00:00 2001
From: Ondrej Holy <oholy@redhat.com>
Date: Thu, 26 Mar 2026 13:39:06 +0100
Subject: [PATCH] [channels,video] unify error handling
Partial backport of commit 635ae3c8193256db01774fab5ff11bcae57aed6b.
Made-with: Cursor
---
channels/video/client/video_main.c | 49 +++++++++++++++++++-----------
1 file changed, 32 insertions(+), 17 deletions(-)
diff --git a/channels/video/client/video_main.c b/channels/video/client/video_main.c
index 390f1723a..c2677992b 100644
--- a/channels/video/client/video_main.c
+++ b/channels/video/client/video_main.c
@@ -354,14 +354,24 @@ void VideoClientContextPriv_free(VideoClientContextPriv* priv)
free(priv);
}
+static UINT video_channel_write(VIDEO_PLUGIN* video, const BYTE* data, UINT32 length)
+{
+ WINPR_ASSERT(video);
+
+ if (!video->control_callback || !video->control_callback->channel_callback)
+ return ERROR_BAD_CONFIGURATION;
+ IWTSVirtualChannel* channel = video->control_callback->channel_callback->channel;
+ if (!channel || !channel->Write)
+ return ERROR_BAD_CONFIGURATION;
+ return channel->Write(channel, length, data, NULL);
+}
+
static UINT video_control_send_presentation_response(VideoClientContext* context,
TSMM_PRESENTATION_RESPONSE* resp)
{
BYTE buf[12] = { 0 };
wStream* s = NULL;
VIDEO_PLUGIN* video = NULL;
- IWTSVirtualChannel* channel = NULL;
- UINT ret = 0;
WINPR_ASSERT(context);
WINPR_ASSERT(resp);
@@ -379,11 +389,9 @@ static UINT video_control_send_presentation_response(VideoClientContext* context
Stream_Zero(s, 3);
Stream_SealLength(s);
- channel = video->control_callback->channel_callback->channel;
- ret = channel->Write(channel, 12, buf, NULL);
Stream_Free(s, FALSE);
- return ret;
+ return video_channel_write(video, buf, sizeof(buf));
}
static BOOL video_onMappedGeometryUpdate(MAPPED_GEOMETRY* geometry)
@@ -620,8 +628,6 @@ static UINT video_control_send_client_notification(VideoClientContext* context,
BYTE buf[100];
wStream* s = NULL;
VIDEO_PLUGIN* video = NULL;
- IWTSVirtualChannel* channel = NULL;
- UINT ret = 0;
UINT32 cbSize = 0;
WINPR_ASSERT(context);
@@ -661,16 +667,7 @@ static UINT video_control_send_client_notification(VideoClientContext* context,
Stream_Write_UINT32(s, cbSize);
Stream_Free(s, FALSE);
- WINPR_ASSERT(video->control_callback);
- WINPR_ASSERT(video->control_callback->channel_callback);
-
- channel = video->control_callback->channel_callback->channel;
- WINPR_ASSERT(channel);
- WINPR_ASSERT(channel->Write);
-
- ret = channel->Write(channel, cbSize, buf, NULL);
-
- return ret;
+ return video_channel_write(video, buf, cbSize);
}
static void video_timer(VideoClientContext* video, UINT64 now)
@@ -1006,12 +1003,30 @@ static UINT video_data_on_data_received(IWTSVirtualChannelCallback* pChannelCall
*/
static UINT video_control_on_close(IWTSVirtualChannelCallback* pChannelCallback)
{
+ if (pChannelCallback)
+ {
+ GENERIC_LISTENER_CALLBACK* listener_callback = (GENERIC_LISTENER_CALLBACK*)pChannelCallback;
+ VIDEO_PLUGIN* video = (VIDEO_PLUGIN*)listener_callback->plugin;
+ if (video && video->control_callback)
+ {
+ video->control_callback->channel_callback = NULL;
+ }
+ }
free(pChannelCallback);
return CHANNEL_RC_OK;
}
static UINT video_data_on_close(IWTSVirtualChannelCallback* pChannelCallback)
{
+ if (pChannelCallback)
+ {
+ GENERIC_LISTENER_CALLBACK* listener_callback = (GENERIC_LISTENER_CALLBACK*)pChannelCallback;
+ VIDEO_PLUGIN* video = (VIDEO_PLUGIN*)listener_callback->plugin;
+ if (video && video->data_callback)
+ {
+ video->data_callback->channel_callback = NULL;
+ }
+ }
free(pChannelCallback);
return CHANNEL_RC_OK;
}
--
2.53.0

View File

@ -1,48 +0,0 @@
From 0421b53fcb4a80c95f51342e4a2c40c68a4101d3 Mon Sep 17 00:00:00 2001
From: akallabeth <akallabeth@posteo.net>
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

View File

@ -1,78 +0,0 @@
From 4498861d2b180cae552b5e2daedebe14312c2141 Mon Sep 17 00:00:00 2001
From: Ondrej Holy <oholy@redhat.com>
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 <noreply@anthropic.com>
---
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

View File

@ -1,62 +0,0 @@
From 94235a5297db9cb83c2c23ade8a69cabe3e5f9f4 Mon Sep 17 00:00:00 2001
From: Ondrej Holy <oholy@redhat.com>
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 <noreply@anthropic.com>
---
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

View File

@ -1,42 +0,0 @@
From 6fe494ec5b0baf2fa604f5ae6a6237eb5dc0b66a Mon Sep 17 00:00:00 2001
From: Ondrej Holy <oholy@redhat.com>
Date: Mon, 9 Mar 2026 13:55:01 +0100
Subject: [PATCH] [codec,clear] fix destination checks
Backport of commit 7d8fdce2d0ef337cb86cb37fc0c436c905e04d77.
Made-with: Cursor
---
libfreerdp/codec/clear.c | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/libfreerdp/codec/clear.c b/libfreerdp/codec/clear.c
index 2a4f894ea..4c42bb2bf 100644
--- a/libfreerdp/codec/clear.c
+++ b/libfreerdp/codec/clear.c
@@ -490,16 +490,16 @@ static BOOL clear_decompress_subcodecs_data(CLEAR_CONTEXT* WINPR_RESTRICT clear,
nXDstRel = nXDst + xStart;
nYDstRel = nYDst + yStart;
- if (1ull * xStart + width > nWidth)
+ if (1ull * nXDstRel + width > nDstWidth)
{
- WLog_ERR(TAG, "xStart %" PRIu16 " + width %" PRIu16 " > nWidth %" PRIu32 "", xStart,
- width, nWidth);
+ WLog_ERR(TAG, "nXDstRel %" PRIu32 " + width %" PRIu16 " > nDstWidth %" PRIu32 "",
+ nXDstRel, width, nDstWidth);
return FALSE;
}
- if (1ull * yStart + height > nHeight)
+ if (1ull * nYDstRel + height > nDstHeight)
{
- WLog_ERR(TAG, "yStart %" PRIu16 " + height %" PRIu16 " > nHeight %" PRIu32 "", yStart,
- height, nHeight);
+ WLog_ERR(TAG, "nYDstRel %" PRIu32 " + height %" PRIu16 " > nDstHeight %" PRIu32 "",
+ nYDstRel, height, nDstHeight);
return FALSE;
}
--
2.53.0

View File

@ -1,31 +0,0 @@
From f8688b57f6cfad9a0b05475a6afbde355ffab720 Mon Sep 17 00:00:00 2001
From: akallabeth <akallabeth@posteo.net>
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

View File

@ -1,99 +0,0 @@
From 1ea1d83a365f81628a2c38e9ec0223de6b975889 Mon Sep 17 00:00:00 2001
From: Ondrej Holy <oholy@redhat.com>
Date: Tue, 10 Mar 2026 13:00:04 +0100
Subject: [PATCH] [codec,color] add freerdp_glyph_convert_ex
Backport of commit 3bc1eeb4f63ceec9a696af194e4c1ea0e67ff60c.
Made-with: Cursor
---
libfreerdp/codec/color.c | 8 ++++++-
libfreerdp/gdi/graphics.c | 46 +++++++++++++++++++++++++++++++++++++++
2 files changed, 53 insertions(+), 1 deletion(-)
diff --git a/libfreerdp/codec/color.c b/libfreerdp/codec/color.c
index 9a2be6fc6..0db9573b8 100644
--- a/libfreerdp/codec/color.c
+++ b/libfreerdp/codec/color.c
@@ -50,7 +50,13 @@ BYTE* freerdp_glyph_convert(UINT32 width, UINT32 height, const BYTE* WINPR_RESTR
* this approach uses a little more memory, but provides faster
* means of accessing individual pixels in blitting operations
*/
- const UINT32 scanline = (width + 7) / 8;
+ const size_t scanline = (width + 7ull) / 8ull;
+
+ if ((width == 0) || (height == 0))
+ return NULL;
+
+ WINPR_ASSERT(data);
+
BYTE* dstData = (BYTE*)winpr_aligned_malloc(1ull * width * height, 16);
if (!dstData)
diff --git a/libfreerdp/gdi/graphics.c b/libfreerdp/gdi/graphics.c
index bcb9f9776..99aaab37a 100644
--- a/libfreerdp/gdi/graphics.c
+++ b/libfreerdp/gdi/graphics.c
@@ -21,6 +21,7 @@
#include <freerdp/config.h>
+#include <winpr/assert.h>
#include <winpr/crt.h>
#include <freerdp/log.h>
@@ -262,6 +263,51 @@ static BOOL gdi_Bitmap_SetSurface(rdpContext* context, rdpBitmap* bitmap, BOOL p
}
/* Glyph Class */
+static BYTE* freerdp_glyph_convert_ex(UINT32 width, UINT32 height, const BYTE* WINPR_RESTRICT data,
+ size_t len)
+{
+ /*
+ * converts a 1-bit-per-pixel glyph to a one-byte-per-pixel glyph:
+ * this approach uses a little more memory, but provides faster
+ * means of accessing individual pixels in blitting operations
+ */
+ const size_t scanline = (width + 7ull) / 8ull;
+ const size_t required = scanline * height;
+ if (len < required)
+ return NULL;
+
+ if ((len == 0) || (width == 0) || (height == 0))
+ return NULL;
+
+ WINPR_ASSERT(data);
+
+ BYTE* dstData = (BYTE*)winpr_aligned_malloc(1ull * width * height, 16);
+
+ if (!dstData)
+ return NULL;
+
+ ZeroMemory(dstData, 1ULL * width * height);
+ BYTE* dstp = dstData;
+
+ for (UINT32 y = 0; y < height; y++)
+ {
+ const BYTE* srcp = &data[1ull * y * scanline];
+
+ for (UINT32 x = 0; x < width; x++)
+ {
+ if ((*srcp & (0x80 >> (x % 8))) != 0)
+ *dstp = 0xFF;
+
+ dstp++;
+
+ if (((x + 1) % 8 == 0) && x != 0)
+ srcp++;
+ }
+ }
+
+ return dstData;
+}
+
static BOOL gdi_Glyph_New(rdpContext* context, rdpGlyph* glyph)
{
BYTE* data = NULL;
--
2.53.0

View File

@ -1,91 +0,0 @@
From 7702ec2f2aadb0cfae86b00c177647e31f6159d4 Mon Sep 17 00:00:00 2001
From: Ondrej Holy <oholy@redhat.com>
Date: Mon, 30 Mar 2026 17:25:10 +0200
Subject: [PATCH] [codec,nsc] fix use of nsc_process_message
Backport of commit 169971607cece48384cb94632b829bd57336af0f.
Made-with: Cursor
---
libfreerdp/codec/clear.c | 10 ++++++----
libfreerdp/codec/nsc.c | 9 ++++++++-
libfreerdp/gdi/gdi.c | 4 ++--
3 files changed, 16 insertions(+), 7 deletions(-)
diff --git a/libfreerdp/codec/clear.c b/libfreerdp/codec/clear.c
index 4c42bb2bf..a54758173 100644
--- a/libfreerdp/codec/clear.c
+++ b/libfreerdp/codec/clear.c
@@ -133,7 +133,8 @@ static BOOL convert_color(BYTE* WINPR_RESTRICT dst, UINT32 nDstStep, UINT32 DstF
static BOOL clear_decompress_nscodec(NSC_CONTEXT* WINPR_RESTRICT nsc, UINT32 width, UINT32 height,
wStream* WINPR_RESTRICT s, UINT32 bitmapDataByteCount,
BYTE* WINPR_RESTRICT pDstData, UINT32 DstFormat,
- UINT32 nDstStep, UINT32 nXDstRel, UINT32 nYDstRel)
+ UINT32 nDstStep, UINT32 nXDstRel, UINT32 nYDstRel,
+ UINT32 nDstWidth, UINT32 nDstHeight)
{
BOOL rc = 0;
@@ -141,8 +142,8 @@ static BOOL clear_decompress_nscodec(NSC_CONTEXT* WINPR_RESTRICT nsc, UINT32 wid
return FALSE;
rc = nsc_process_message(nsc, 32, width, height, Stream_Pointer(s), bitmapDataByteCount,
- pDstData, DstFormat, nDstStep, nXDstRel, nYDstRel, width, height,
- FREERDP_FLIP_NONE);
+ pDstData, DstFormat, nDstStep, nXDstRel, nYDstRel, nDstWidth,
+ nDstHeight, FREERDP_FLIP_NONE);
Stream_Seek(s, bitmapDataByteCount);
return rc;
}
@@ -531,7 +532,8 @@ static BOOL clear_decompress_subcodecs_data(CLEAR_CONTEXT* WINPR_RESTRICT clear,
case 1: /* NSCodec */
if (!clear_decompress_nscodec(clear->nsc, width, height, s, bitmapDataByteCount,
- pDstData, DstFormat, nDstStep, nXDstRel, nYDstRel))
+ pDstData, DstFormat, nDstStep, nXDstRel, nYDstRel,
+ nDstWidth, nDstHeight))
return FALSE;
break;
diff --git a/libfreerdp/codec/nsc.c b/libfreerdp/codec/nsc.c
index 916e5ff8d..951983a1d 100644
--- a/libfreerdp/codec/nsc.c
+++ b/libfreerdp/codec/nsc.c
@@ -435,11 +435,18 @@ BOOL nsc_process_message(NSC_CONTEXT* WINPR_RESTRICT context, UINT16 bpp, UINT32
BYTE* WINPR_RESTRICT pDstData, UINT32 DstFormat, UINT32 nDstStride,
UINT32 nXDst, UINT32 nYDst, UINT32 nWidth, UINT32 nHeight, UINT32 flip)
{
+ WINPR_ASSERT(context);
+ WINPR_ASSERT(context->priv);
+
wStream* s = NULL;
wStream sbuffer = { 0 };
BOOL ret = 0;
- if (!context || !data || !pDstData)
+ if (!data || !pDstData)
+ {
+ WLog_Print(context->priv->log, WLOG_ERROR, "Invalid argument: data=%p, pDstData=%p",
+ (const void*)data, (void*)pDstData);
return FALSE;
+ }
s = Stream_StaticConstInit(&sbuffer, data, length);
diff --git a/libfreerdp/gdi/gdi.c b/libfreerdp/gdi/gdi.c
index f80ada8ae..6a18307c2 100644
--- a/libfreerdp/gdi/gdi.c
+++ b/libfreerdp/gdi/gdi.c
@@ -1101,8 +1101,8 @@ static BOOL gdi_surface_bits(rdpContext* context, const SURFACE_BITS_COMMAND* cm
if (!nsc_process_message(
context->codecs->nsc, cmd->bmp.bpp, cmd->bmp.width, cmd->bmp.height,
cmd->bmp.bitmapData, cmd->bmp.bitmapDataLength, gdi->primary_buffer, format,
- gdi->stride, cmdRect.left, cmdRect.top, cmdRect.right - cmdRect.left,
- cmdRect.bottom - cmdRect.top, FREERDP_FLIP_VERTICAL))
+ gdi->stride, cmdRect.left, cmdRect.top, (UINT32)gdi->width, (UINT32)gdi->height,
+ FREERDP_FLIP_VERTICAL))
{
WLog_ERR(TAG, "Failed to process NSCodec message");
goto out;
--
2.53.0

View File

@ -1,48 +0,0 @@
From 33e12b7cfb83875479822769ffd4fda799f294ff Mon Sep 17 00:00:00 2001
From: Ondrej Holy <oholy@redhat.com>
Date: Mon, 16 Mar 2026 14:58:52 +0100
Subject: [PATCH] [codec,nsc] limit copy area in nsc_process_message
Backport of commit 83d9aedea278a74af3e490ff5eeb889c016dbb2b.
Made-with: Cursor
---
libfreerdp/codec/nsc.c | 15 ++++++++++++---
1 file changed, 12 insertions(+), 3 deletions(-)
diff --git a/libfreerdp/codec/nsc.c b/libfreerdp/codec/nsc.c
index f7a5e920f..916e5ff8d 100644
--- a/libfreerdp/codec/nsc.c
+++ b/libfreerdp/codec/nsc.c
@@ -22,6 +22,7 @@
#include <freerdp/config.h>
+#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -502,9 +503,17 @@ BOOL nsc_process_message(NSC_CONTEXT* WINPR_RESTRICT context, UINT16 bpp, UINT32
return FALSE;
}
- if (!freerdp_image_copy_no_overlap(pDstData, DstFormat, nDstStride, nXDst, nYDst, width, height,
- context->BitmapData, PIXEL_FORMAT_BGRA32, 0, 0, 0, NULL,
- flip))
+ uint32_t cwidth = width;
+ if (1ull * nXDst + width > nWidth)
+ cwidth = nWidth - nXDst;
+
+ uint32_t cheight = height;
+ if (1ull * nYDst + height > nHeight)
+ cheight = nHeight - nYDst;
+
+ if (!freerdp_image_copy_no_overlap(pDstData, DstFormat, nDstStride, nXDst, nYDst, cwidth,
+ cheight, context->BitmapData, PIXEL_FORMAT_BGRA32, 0, 0, 0,
+ NULL, flip))
return FALSE;
return TRUE;
--
2.53.0

View File

@ -1,28 +0,0 @@
From 1bab198a2edd0d0e6e1627d21a433151ea190500 Mon Sep 17 00:00:00 2001
From: akallabeth <akallabeth@posteo.net>
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

View File

@ -1,55 +0,0 @@
From 867763b853ea3efdffb3bba0b182890bef994974 Mon Sep 17 00:00:00 2001
From: Ondrej Holy <oholy@redhat.com>
Date: Mon, 9 Mar 2026 12:50:26 +0100
Subject: [PATCH] [codec,planar] fix missing destination bounds checks
Backport of commit a0be5cb87d760bb1c803ad1bb835aa1e73e62abc.
Made-with: Cursor
---
libfreerdp/codec/planar.c | 21 ++++++++++++++++++++-
1 file changed, 20 insertions(+), 1 deletion(-)
diff --git a/libfreerdp/codec/planar.c b/libfreerdp/codec/planar.c
index 5df607051..58efbc627 100644
--- a/libfreerdp/codec/planar.c
+++ b/libfreerdp/codec/planar.c
@@ -727,8 +727,9 @@ BOOL planar_decompress(BITMAP_PLANAR_CONTEXT* WINPR_RESTRICT planar,
if (planar->maxHeight < nSrcHeight)
return FALSE;
+ const UINT32 bpp = FreeRDPGetBytesPerPixel(DstFormat);
if (nDstStep <= 0)
- nDstStep = nDstWidth * FreeRDPGetBytesPerPixel(DstFormat);
+ nDstStep = nDstWidth * bpp;
srcp = pSrcData;
@@ -948,6 +949,24 @@ BOOL planar_decompress(BITMAP_PLANAR_CONTEXT* WINPR_RESTRICT planar,
}
else /* RLE */
{
+ if (nYDst + nSrcHeight > nTotalHeight)
+ {
+ WLog_ERR(TAG,
+ "planar plane destination Y %" PRIu32 " + height %" PRIu32
+ " exceeds totalHeight %" PRIu32,
+ nYDst, nSrcHeight, nTotalHeight);
+ return FALSE;
+ }
+
+ if ((nXDst + nSrcWidth) * bpp > nDstStep)
+ {
+ WLog_ERR(TAG,
+ "planar plane destination (X %" PRIu32 " + width %" PRIu32
+ ") * bpp %" PRIu32 " exceeds stride %" PRIu32,
+ nXDst, nSrcWidth, bpp, nDstStep);
+ return FALSE;
+ }
+
status =
planar_decompress_plane_rle(planes[0], rleSizes[0], pTempData, nTempStep, nXDst,
nYDst, nSrcWidth, nSrcHeight, 2, vFlip); /* RedPlane */
--
2.53.0

View File

@ -1,37 +0,0 @@
From 60ed73552ffdb499dddf06c119be9437da7f9261 Mon Sep 17 00:00:00 2001
From: akallabeth <akallabeth@posteo.net>
Date: Sun, 29 Dec 2024 10:22:56 +0100
Subject: [PATCH] [core,connection] print SSL warnings after init
---
libfreerdp/core/connection.c | 1 +
libfreerdp/core/freerdp.c | 1 -
2 files changed, 1 insertion(+), 1 deletion(-)
diff --git a/libfreerdp/core/connection.c b/libfreerdp/core/connection.c
index 979ccdf60..001b56944 100644
--- a/libfreerdp/core/connection.c
+++ b/libfreerdp/core/connection.c
@@ -319,6 +319,7 @@ BOOL rdp_client_connect(rdpRdp* rdp)
flags |= WINPR_SSL_INIT_ENABLE_FIPS;
winpr_InitializeSSL(flags);
+ rdp_log_build_warnings(rdp);
/* FIPS Mode forces the following and overrides the following(by happening later */
/* in the command line processing): */
diff --git a/libfreerdp/core/freerdp.c b/libfreerdp/core/freerdp.c
index 94dad20a0..715da31d9 100644
--- a/libfreerdp/core/freerdp.c
+++ b/libfreerdp/core/freerdp.c
@@ -802,7 +802,6 @@ BOOL freerdp_context_new_ex(freerdp* instance, rdpSettings* settings)
if (!rdp)
goto fail;
- rdp_log_build_warnings(rdp);
context->rdp = rdp;
context->pubSub = rdp->pubSub;
--
2.51.1

View File

@ -1,59 +0,0 @@
From 51847b9d1c3a3720e4742d2a6ee92429f0c1af48 Mon Sep 17 00:00:00 2001
From: Ondrej Holy <oholy@redhat.com>
Date: Tue, 10 Mar 2026 14:53:37 +0100
Subject: [PATCH] [core,info] fix missing NULL check
Backport of commit 4d44e3c097656a8b9ec696353647b0888ca45860.
Made-with: Cursor
---
libfreerdp/core/info.c | 14 +++++++++-----
1 file changed, 9 insertions(+), 5 deletions(-)
diff --git a/libfreerdp/core/info.c b/libfreerdp/core/info.c
index 2f8e6694c..08d11e286 100644
--- a/libfreerdp/core/info.c
+++ b/libfreerdp/core/info.c
@@ -1436,7 +1436,7 @@ static BOOL rdp_write_logon_info_v1(wStream* s, logon_info* info)
return TRUE;
}
-static BOOL rdp_write_logon_info_v2(wStream* s, logon_info* info)
+static BOOL rdp_write_logon_info_v2(wStream* s, const logon_info* info)
{
size_t domainLen = 0;
size_t usernameLen = 0;
@@ -1451,11 +1451,14 @@ static BOOL rdp_write_logon_info_v2(wStream* s, logon_info* info)
*/
Stream_Write_UINT32(s, logonInfoV2Size);
Stream_Write_UINT32(s, info->sessionId);
- domainLen = strnlen(info->domain, UINT32_MAX);
+ if (info->domain)
+ domainLen = strnlen(info->domain, UINT32_MAX);
if (domainLen >= UINT32_MAX / sizeof(WCHAR))
return FALSE;
Stream_Write_UINT32(s, (UINT32)(domainLen + 1) * sizeof(WCHAR));
- usernameLen = strnlen(info->username, UINT32_MAX);
+
+ if (info->username)
+ usernameLen = strnlen(info->username, UINT32_MAX);
if (usernameLen >= UINT32_MAX / sizeof(WCHAR))
return FALSE;
Stream_Write_UINT32(s, (UINT32)(usernameLen + 1) * sizeof(WCHAR));
@@ -1522,10 +1525,11 @@ static BOOL rdp_write_logon_info_ex(wStream* s, logon_info_ex* info)
BOOL rdp_send_save_session_info(rdpContext* context, UINT32 type, void* data)
{
- wStream* s = NULL;
BOOL status = 0;
+
+ WINPR_ASSERT(context);
rdpRdp* rdp = context->rdp;
- s = rdp_data_pdu_init(rdp);
+ wStream* s = rdp_data_pdu_init(rdp);
if (!s)
return FALSE;
--
2.53.0

View File

@ -1,43 +0,0 @@
From 5a856bef31bdbecbaf69bd671d23add9779debd9 Mon Sep 17 00:00:00 2001
From: Ondrej Holy <oholy@redhat.com>
Date: Wed, 14 Jan 2026 11:37:16 +0100
Subject: [PATCH] [core,tcp] Don't ignore connect errors
Backport of commit 0bdd8da0993231216a7bb4d5e6e33e47d817a944.
Co-Authored-By: Claude <noreply@anthropic.com>
---
libfreerdp/core/tcp.c | 15 +++++++++++----
1 file changed, 11 insertions(+), 4 deletions(-)
diff --git a/libfreerdp/core/tcp.c b/libfreerdp/core/tcp.c
index 8e7ee20bd..6edfb6d71 100644
--- a/libfreerdp/core/tcp.c
+++ b/libfreerdp/core/tcp.c
@@ -857,12 +857,19 @@ static BOOL freerdp_tcp_connect_timeout(rdpContext* context, int sockfd, struct
if (WAIT_OBJECT_0 != status)
goto fail;
- const SSIZE_T res = recv(sockfd, NULL, 0, 0);
-
- if (res == SOCKET_ERROR)
{
- if (WSAGetLastError() == WSAECONNRESET)
+ INT32 optval = 0;
+ socklen_t optlen = sizeof(optval);
+ if (getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &optval, &optlen) < 0)
+ goto fail;
+
+ if (optval != 0)
+ {
+ char ebuffer[256] = { 0 };
+ WLog_DBG(TAG, "connect failed with error: %s [%" PRId32 "]",
+ winpr_strerror(optval, ebuffer, sizeof(ebuffer)), optval);
goto fail;
+ }
}
status = WSAEventSelect(sockfd, handles[0], 0);
--
2.52.0

View File

@ -1,102 +0,0 @@
From 4864954a384e4028298e95a19795505f688b21ae Mon Sep 17 00:00:00 2001
From: Ondrej Holy <oholy@redhat.com>
Date: Wed, 14 Jan 2026 11:38:11 +0100
Subject: [PATCH] [core,tcp] Fix PreferIPv6OverIPv4 fallback to IPv4 addresses
Backport of commit 0bdd8da0993231216a7bb4d5e6e33e47d817a944.
Co-Authored-By: Claude <noreply@anthropic.com>
---
libfreerdp/core/tcp.c | 61 ++++++++++++++++++++++++++++++++++++-------
1 file changed, 51 insertions(+), 10 deletions(-)
diff --git a/libfreerdp/core/tcp.c b/libfreerdp/core/tcp.c
index 6edfb6d71..83cc9af1e 100644
--- a/libfreerdp/core/tcp.c
+++ b/libfreerdp/core/tcp.c
@@ -1081,6 +1081,53 @@ int freerdp_tcp_connect(rdpContext* context, const char* hostname, int port, DWO
return transport_tcp_connect(context->rdp->transport, hostname, port, timeout);
}
+static struct addrinfo* reorder_addrinfo_by_preference(rdpContext* context, struct addrinfo* addr)
+{
+ WINPR_ASSERT(context);
+ WINPR_ASSERT(addr);
+
+ const BOOL preferIPv6 =
+ freerdp_settings_get_bool(context->settings, FreeRDP_PreferIPv6OverIPv4);
+ if (!preferIPv6)
+ return addr;
+
+ struct addrinfo* ipv6Head = NULL;
+ struct addrinfo* ipv6Tail = NULL;
+ struct addrinfo* otherHead = NULL;
+ struct addrinfo* otherTail = NULL;
+
+ /* Partition the list into IPv6 and other addresses */
+ while (addr)
+ {
+ struct addrinfo* next = addr->ai_next;
+ addr->ai_next = NULL;
+
+ if (addr->ai_family == AF_INET6)
+ {
+ if (!ipv6Head)
+ ipv6Head = addr;
+ else
+ ipv6Tail->ai_next = addr;
+ ipv6Tail = addr;
+ }
+ else
+ {
+ if (!otherHead)
+ otherHead = addr;
+ else
+ otherTail->ai_next = addr;
+ otherTail = addr;
+ }
+ addr = next;
+ }
+
+ /* Concatenate the lists */
+ if (ipv6Tail)
+ ipv6Tail->ai_next = otherHead;
+
+ return ipv6Head ? ipv6Head : otherHead;
+}
+
static int get_next_addrinfo(rdpContext* context, struct addrinfo* input, struct addrinfo** result,
UINT32 errorCode)
{
@@ -1091,14 +1138,6 @@ static int get_next_addrinfo(rdpContext* context, struct addrinfo* input, struct
if (!addr)
goto fail;
- if (freerdp_settings_get_bool(context->settings, FreeRDP_PreferIPv6OverIPv4))
- {
- while (addr && (addr->ai_family != AF_INET6))
- addr = addr->ai_next;
- if (!addr)
- addr = input;
- }
-
/* We want to force IPvX, abort if not detected */
const UINT32 IPvX = freerdp_settings_get_uint32(context->settings, FreeRDP_ForceIPvX);
switch (IPvX)
@@ -1237,9 +1276,11 @@ int freerdp_tcp_default_connect(rdpContext* context, rdpSettings* settings, cons
/* By default we take the first returned entry.
*
- * If PreferIPv6OverIPv4 = TRUE we force to IPv6 if there
- * is such an address available, but fall back to first if not found
+ * If PreferIPv6OverIPv4 = TRUE we reorder addresses by preference:
+ * IPv6 addresses come first, then other addresses.
*/
+ result = reorder_addrinfo_by_preference(context, result);
+
const int rc =
get_next_addrinfo(context, result, &addr, FREERDP_ERROR_DNS_NAME_NOT_FOUND);
if (rc < 0)
--
2.52.0

View File

@ -1,76 +0,0 @@
From e4adf227177fd807f47f8621a5e2568748619a23 Mon Sep 17 00:00:00 2001
From: Ondrej Holy <oholy@redhat.com>
Date: Wed, 14 Jan 2026 11:34:53 +0100
Subject: [PATCH] [core,tcp] Try next DNS entry on connect failure
Backport of commit bd67348eb3380a66b544835346191bd2138a5ba4.
Co-Authored-By: Claude <noreply@anthropic.com>
---
libfreerdp/core/tcp.c | 41 ++++++++++++++++++++++-------------------
1 file changed, 22 insertions(+), 19 deletions(-)
diff --git a/libfreerdp/core/tcp.c b/libfreerdp/core/tcp.c
index 53e3a412f..8e7ee20bd 100644
--- a/libfreerdp/core/tcp.c
+++ b/libfreerdp/core/tcp.c
@@ -1241,34 +1241,37 @@ int freerdp_tcp_default_connect(rdpContext* context, rdpSettings* settings, cons
do
{
sockfd = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol);
+ if (sockfd >= 0)
+ {
+ if ((peerAddress = freerdp_tcp_address_to_string(
+ (const struct sockaddr_storage*)addr->ai_addr, NULL)) != NULL)
+ {
+ WLog_DBG(TAG, "connecting to peer %s", peerAddress);
+ free(peerAddress);
+ }
+
+ if (!freerdp_tcp_connect_timeout(context, sockfd, addr->ai_addr,
+ addr->ai_addrlen, timeout))
+ {
+ close(sockfd);
+ sockfd = -1;
+ }
+ }
+
if (sockfd < 0)
{
const int lrc = get_next_addrinfo(context, addr->ai_next, &addr,
FREERDP_ERROR_CONNECT_FAILED);
if (lrc < 0)
+ {
+ freeaddrinfo(result);
+ freerdp_set_last_error_if_not(context, FREERDP_ERROR_CONNECT_FAILED);
+ WLog_ERR(TAG, "failed to connect to %s", hostname);
return lrc;
+ }
}
} while (sockfd < 0);
- if ((peerAddress = freerdp_tcp_address_to_string(
- (const struct sockaddr_storage*)addr->ai_addr, NULL)) != NULL)
- {
- WLog_DBG(TAG, "connecting to peer %s", peerAddress);
- free(peerAddress);
- }
-
- if (!freerdp_tcp_connect_timeout(context, sockfd, addr->ai_addr, addr->ai_addrlen,
- timeout))
- {
- freeaddrinfo(result);
- close(sockfd);
-
- freerdp_set_last_error_if_not(context, FREERDP_ERROR_CONNECT_FAILED);
-
- WLog_ERR(TAG, "failed to connect to %s", hostname);
- return -1;
- }
-
freeaddrinfo(result);
}
}
--
2.52.0

View File

@ -1,39 +0,0 @@
From 0a660bf3b3cf2660a4fa15bd37d8b8b3d03fbfef Mon Sep 17 00:00:00 2001
From: Ondrej Holy <oholy@redhat.com>
Date: Wed, 14 Jan 2026 12:55:42 +0100
Subject: [PATCH] [core,tcp] fix double free in get_next_addrinfo
Backport of commit 48197426444b7b3587874b5eae175af1113beab8.
Co-Authored-By: Claude <noreply@anthropic.com>
---
libfreerdp/core/tcp.c | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/libfreerdp/core/tcp.c b/libfreerdp/core/tcp.c
index 83cc9af1e..a3e12d14b 100644
--- a/libfreerdp/core/tcp.c
+++ b/libfreerdp/core/tcp.c
@@ -1164,7 +1164,7 @@ static int get_next_addrinfo(rdpContext* context, struct addrinfo* input, struct
fail:
freerdp_set_last_error_if_not(context, errorCode);
- freeaddrinfo(input);
+ *result = NULL;
return -1;
}
@@ -1284,7 +1284,10 @@ int freerdp_tcp_default_connect(rdpContext* context, rdpSettings* settings, cons
const int rc =
get_next_addrinfo(context, result, &addr, FREERDP_ERROR_DNS_NAME_NOT_FOUND);
if (rc < 0)
+ {
+ freeaddrinfo(result);
return rc;
+ }
do
{
--
2.52.0

View File

@ -1,29 +0,0 @@
From 525e717db19b57cc498bb2e3f1d9d65383f16d3f Mon Sep 17 00:00:00 2001
From: Ondrej Holy <oholy@redhat.com>
Date: Tue, 17 Feb 2026 14:54:30 +0100
Subject: [PATCH] [crypto,base64] ensure char is singend
Backport of commit 62a9e787edb2cfce9858fa4ceda5461680efc590.
Co-Authored-By: Cursor <cursoragent@cursor.com>
---
libfreerdp/crypto/base64.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/libfreerdp/crypto/base64.c b/libfreerdp/crypto/base64.c
index 315da9203..8d49d352e 100644
--- a/libfreerdp/crypto/base64.c
+++ b/libfreerdp/crypto/base64.c
@@ -400,7 +400,8 @@ static INLINE char* base64_encode(const BYTE* WINPR_RESTRICT alphabet,
static INLINE int base64_decode_char(const signed char* WINPR_RESTRICT alphabet, char c)
{
- if (c <= '\0')
+ /* ensure char is signed for this check */
+ if ((int)c <= '\0')
return -1;
return alphabet[(size_t)c];
--
2.52.0

View File

@ -29,8 +29,8 @@
Name: freerdp
Epoch: 2
Version: 3.10.3
Release: 12%{?dist}
Version: 3.24.2
Release: 1%{?dist}
Summary: Free implementation of the Remote Desktop Protocol (RDP)
# The effective license is Apache-2.0 but:
@ -47,148 +47,6 @@ URL: http://www.freerdp.com/
Source0: FreeRDP-%{version}-repack.tar.gz
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://issues.redhat.com/browse/RHEL-73724
Patch: core-connection-print-SSL-warnings-after-init.patch
# https://issues.redhat.com/browse/RHEL-140099
Patch: core-tcp-Try-next-DNS-entry-on-connect-failure.patch
Patch: core-tcp-Don-t-ignore-connect-errors.patch
Patch: core-tcp-Fix-PreferIPv6OverIPv4-fallback-to-IPv4-add.patch
Patch: core-tcp-fix-double-free-in-get_next_addrinfo.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
# CVE-2026-22853
# https://github.com/FreeRDP/FreeRDP/commit/19f48dc7d615984a24a9be89f50ef9eb8f9bdb6a
Patch: channels-rdpear-add-checks-for-itemSize.patch
# CVE-2026-22855
# https://github.com/FreeRDP/FreeRDP/commit/57c5647d98c2a026de8b681159cb188ca0439ef8
Patch: utils-smartcard-add-length-validity-checks.patch
# CVE-2026-22858
# https://github.com/FreeRDP/FreeRDP/commit/62a9e787edb2cfce9858fa4ceda5461680efc590
Patch: crypto-base64-ensure-char-is-singend.patch
# CVE-2026-22859
# https://github.com/FreeRDP/FreeRDP/commit/7b7e6de8fe427a2f01d331056774aec69710590b
Patch: channels-urbdrc-check-interface-indices-before-use.patch
# CVE-2026-24678
# https://github.com/FreeRDP/FreeRDP/commit/f3ab1a16139036179d9852745fdade18fec11600
Patch: channels-rdpecam-ensure-all-streams-are-stopped.patch
# CVE-2026-26955
# https://github.com/FreeRDP/FreeRDP/commit/7d8fdce2d0ef337cb86cb37fc0c436c905e04d77
Patch: codec-clear-fix-destination-checks.patch
# CVE-2026-26965
# https://github.com/FreeRDP/FreeRDP/commit/a0be5cb87d760bb1c803ad1bb835aa1e73e62abc
Patch: codec-planar-fix-missing-destination-bounds-checks.patch
# CVE-2026-22852
# https://github.com/FreeRDP/FreeRDP/commit/cd1ffa112cfbe1b40a9fd57e299a8ea12e23df0d
Patch: channels-audin-free-up-old-audio-formats.patch
# CVE-2026-22854
# https://github.com/FreeRDP/FreeRDP/commit/3da319570c8a6be0a79b3306f1ed354c4a943259
Patch: channels-drive-fix-constant-type.patch
# CVE-2026-22856
# https://github.com/FreeRDP/FreeRDP/commit/b35aa3614d32bff3fc1272cd7c4617f711fca1a4
# https://github.com/FreeRDP/FreeRDP/commit/675c20f08f32ca5ec06297108bdf30147d6e2cd9
Patch: channels-serial-lock-list-dictionary.patch
Patch: channels-serial-explicitly-lock-serial-IrpThreads.patch
# CVE-2026-23732
# https://github.com/FreeRDP/FreeRDP/commit/3bc1eeb4f63ceec9a696af194e4c1ea0e67ff60c
# https://github.com/FreeRDP/FreeRDP/commit/9f0eb3b7d43069a1e973464bcb43d1ef965ae65e
Patch: codec-color-add-freerdp_glyph_convert_ex.patch
Patch: gdi-graphics-Use-freerdp_glyph_convert_ex.patch
# CVE-2026-23948
# https://github.com/FreeRDP/FreeRDP/commit/4d44e3c097656a8b9ec696353647b0888ca45860
Patch: core-info-fix-missing-NULL-check.patch
# CVE-2026-24491
# https://github.com/FreeRDP/FreeRDP/commit/e02e052f6692550e539d10f99de9c35a23492db2
# https://github.com/FreeRDP/FreeRDP/commit/cb7f295bc750de86480d60a3b58cebc56a57a1c4
# https://github.com/FreeRDP/FreeRDP/commit/635ae3c8193256db01774fab5ff11bcae57aed6b
# https://github.com/FreeRDP/FreeRDP/commit/e01cd85c8003a245ef9778f0eda4b9235514c201
Patch: channels-drdynvc-reset-channel_callback-before-close.patch
Patch: channels-drdynvc-check-pointer-before-reset.patch
Patch: channels-video-unify-error-handling.patch
Patch: channels-video-fix-wrong-cast.patch
# CVE-2026-24675
# https://github.com/FreeRDP/FreeRDP/commit/d676518809c319eec15911c705c13536036af2ae
Patch: channels-urbdrc-do-not-free-MsConfig-on-failure.patch
# CVE-2026-24676
# https://github.com/FreeRDP/FreeRDP/commit/026b81ae5831ac1598d8f7371e0d0996fac7db00
Patch: channels-audin-reset-audin-format.patch
# CVE-2026-24679
# https://github.com/FreeRDP/FreeRDP/commit/2d563a50be17c1b407ca448b1321378c0726dd31
Patch: channels-urbdrc-ensure-InterfaceNumber-is-within-ran.patch
# CVE-2026-24681
# https://github.com/FreeRDP/FreeRDP/commit/414f701464929c217f2509bcbd6d2c1f00f7ed73
Patch: channels-urbdrc-cancel-all-usb-transfers-on-channel-.patch
# CVE-2026-24682
# https://github.com/FreeRDP/FreeRDP/commit/1c5c74223179d425a1ce6dbbb6a3dd2a958b7aee
# https://github.com/FreeRDP/FreeRDP/commit/668352a2e241ba017679c11a22ecbe29d0b17401
Patch: channels-audin-fix-audin_server_recv_formats-cleanup.patch
Patch: channels-audin-set-error-when-audio_format_read-fail.patch
# CVE-2026-24683
# https://github.com/FreeRDP/FreeRDP/commit/d9ca272dce7a776ab475e9b1a8e8c3d2968c8486
Patch: channels-ainput-lock-context-when-updating-listener.patch
# CVE-2026-24684
# https://github.com/FreeRDP/FreeRDP/commit/622bb7b4402491ca003f47472d0e478132673696
# https://github.com/FreeRDP/FreeRDP/commit/afa6851dc80835d3101e40fcef51b6c5c0f43ea5
Patch: channels-rdpsnd-terminate-thread-before-free.patch
Patch: channel-rdpsnd-only-clean-up-thread-before-free.patch
# CVE-2026-31806
# https://github.com/FreeRDP/FreeRDP/commit/83d9aedea278a74af3e490ff5eeb889c016dbb2b
# https://github.com/FreeRDP/FreeRDP/commit/169971607cece48384cb94632b829bd57336af0f
Patch: codec-nsc-limit-copy-area-in-nsc_process_message.patch
Patch: codec-nsc-fix-use-of-nsc_process_message.patch
# https://github.com/FreeRDP/FreeRDP/commit/907ca47e40583a7788674bb2f06258edd0c34223
Patch: winpr-synch-increase-timeout-for-TestSynchCritical.patch
BuildRequires: gcc
BuildRequires: gcc-c++
BuildRequires: alsa-lib-devel
@ -350,6 +208,7 @@ find . -name "*.c" -exec chmod 664 {} \;
-DWITH_PCSC=ON \
-DWITH_PKCS11=ON \
-DWITH_PULSE=ON \
-DWITH_RDTK=ON \
-DWITH_SAMPLE=OFF \
-DWITH_SERVER=%{?_with_server:ON}%{?!_with_server:OFF} \
-DWITH_SERVER_INTERFACE=%{?_with_server:ON}%{?!_with_server:OFF} \
@ -512,6 +371,10 @@ find %{buildroot} -name "*.a" -delete
%{_libdir}/pkgconfig/winpr-tools3.pc
%changelog
* Thu Apr 09 2026 Ondrej Holy <oholy@redhat.com> - 2:3.24.2-1
- Update to 3.24.2
Resolves: RHEL-80426
* Tue Mar 31 2026 Ondrej Holy <oholy@redhat.com> - 2:3.10.3-12
- Fix use of nsc_process_message
- Increase timeout for TestSynchCritical

View File

@ -1,48 +0,0 @@
From c4a7c371342edf0d307cea728f56d3302f0ab38c Mon Sep 17 00:00:00 2001
From: akallabeth <akallabeth@posteo.net>
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

View File

@ -1,40 +0,0 @@
From e1bbcba2ee4ba8bb20bb73b16717a2e5d491825b Mon Sep 17 00:00:00 2001
From: Ondrej Holy <oholy@redhat.com>
Date: Sun, 29 Mar 2026 05:48:58 +0200
Subject: [PATCH] [gdi,graphics] Use freerdp_glyph_convert_ex
Backport of commit 9f0eb3b7d43069a1e973464bcb43d1ef965ae65e.
---
libfreerdp/gdi/graphics.c | 7 ++-----
1 file changed, 2 insertions(+), 5 deletions(-)
diff --git a/libfreerdp/gdi/graphics.c b/libfreerdp/gdi/graphics.c
index 99aaab37a..eec5cd6b9 100644
--- a/libfreerdp/gdi/graphics.c
+++ b/libfreerdp/gdi/graphics.c
@@ -310,20 +310,17 @@ static BYTE* freerdp_glyph_convert_ex(UINT32 width, UINT32 height, const BYTE* W
static BOOL gdi_Glyph_New(rdpContext* context, rdpGlyph* glyph)
{
- BYTE* data = NULL;
- gdiGlyph* gdi_glyph = NULL;
-
if (!context || !glyph)
return FALSE;
- gdi_glyph = (gdiGlyph*)glyph;
+ gdiGlyph* gdi_glyph = (gdiGlyph*)glyph;
gdi_glyph->hdc = gdi_GetDC();
if (!gdi_glyph->hdc)
return FALSE;
gdi_glyph->hdc->format = PIXEL_FORMAT_MONO;
- data = freerdp_glyph_convert(glyph->cx, glyph->cy, glyph->aj);
+ BYTE* data = freerdp_glyph_convert_ex(glyph->cx, glyph->cy, glyph->aj, glyph->cb);
if (!data)
{
--
2.53.0

View File

@ -1 +1 @@
SHA512 (FreeRDP-3.10.3-repack.tar.gz) = cd007c28267e4b9ace47487f7d44061b03b029409fa167209bb1273f3d1b79fdd3ed96c855d78ccb03bdbedf452009e3a1a21189d282faffdfc3b2b504d03574
SHA512 (FreeRDP-3.24.2-repack.tar.gz) = e4fcd7ffede1db9541463ba3389fe412326f05b762d38a36a79361bfe0a0d5fa2f8a889988a0f1a7971277a6ee8e27669541f665568192b849ddd3544f24e72d

View File

@ -1,81 +0,0 @@
From dc7bbfec41350c7cd3465d15f9facd8f9e2b0f20 Mon Sep 17 00:00:00 2001
From: akallabeth <akallabeth@posteo.net>
Date: Sun, 11 Jan 2026 09:03:57 +0100
Subject: [PATCH] [utils,smartcard] add length validity checks
Backport of commit 57c5647d98c2a026de8b681159cb188ca0439ef8.
Co-Authored-By: Cursor <cursoragent@cursor.com>
---
libfreerdp/utils/smartcard_pack.c | 27 +++++++++++++++++++++------
1 file changed, 21 insertions(+), 6 deletions(-)
diff --git a/libfreerdp/utils/smartcard_pack.c b/libfreerdp/utils/smartcard_pack.c
index 0ae0aff2c..52165def4 100644
--- a/libfreerdp/utils/smartcard_pack.c
+++ b/libfreerdp/utils/smartcard_pack.c
@@ -97,8 +97,8 @@ static BOOL smartcard_ndr_pointer_read_(wStream* s, UINT32* index, UINT32* ptr,
return TRUE;
}
-static LONG smartcard_ndr_read(wStream* s, BYTE** data, size_t min, size_t elementSize,
- ndr_ptr_t type)
+static LONG smartcard_ndr_read_ex(wStream* s, BYTE** data, size_t min,
+ size_t elementSize, ndr_ptr_t type, size_t* plen)
{
size_t len = 0;
size_t offset = 0;
@@ -107,6 +107,9 @@ static LONG smartcard_ndr_read(wStream* s, BYTE** data, size_t min, size_t eleme
size_t required = 0;
*data = NULL;
+ if (plen)
+ *plen = 0;
+
switch (type)
{
case NDR_PTR_FULL:
@@ -181,11 +184,20 @@ static LONG smartcard_ndr_read(wStream* s, BYTE** data, size_t min, size_t eleme
if (!r)
return SCARD_E_NO_MEMORY;
Stream_Read(s, r, len);
- smartcard_unpack_read_size_align(s, len, 4);
+ const LONG pad = smartcard_unpack_read_size_align(s, len, 4);
+ len += (size_t)pad;
*data = r;
+ if (plen)
+ *plen = len;
return STATUS_SUCCESS;
}
+static LONG smartcard_ndr_read(wStream* s, BYTE** data, size_t min, size_t elementSize,
+ ndr_ptr_t type)
+{
+ return smartcard_ndr_read_ex(s, data, min, elementSize, type, NULL);
+}
+
static BOOL smartcard_ndr_pointer_write(wStream* s, UINT32* index, DWORD length)
{
const UINT32 ndrPtr = 0x20000 + (*index) * 4;
@@ -3207,12 +3219,15 @@ LONG smartcard_unpack_set_attrib_call(wStream* s, SetAttrib_Call* call)
if (ndrPtr)
{
- // TODO: call->cbAttrLen was larger than the pointer value.
- // TODO: Maybe need to refine the checks?
- status = smartcard_ndr_read(s, &call->pbAttr, 0, 1, NDR_PTR_SIMPLE);
+ size_t len = 0;
+ status = smartcard_ndr_read_ex(s, &call->pbAttr, 0, 1, NDR_PTR_SIMPLE, &len);
if (status != SCARD_S_SUCCESS)
return status;
+ if (call->cbAttrLen > len)
+ call->cbAttrLen = (DWORD)len;
}
+ else
+ call->cbAttrLen = 0;
smartcard_trace_set_attrib_call(call);
return SCARD_S_SUCCESS;
}
--
2.52.0

View File

@ -1,30 +0,0 @@
From 907ca47e40583a7788674bb2f06258edd0c34223 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Dan=20Hor=C3=A1k?= <dan@danny.cz>
Date: Fri, 22 Aug 2025 19:14:05 +0200
Subject: [PATCH] [winpr,synch] increase timeout for TestSynchCritical
Increase the deadlock detection timeout in TestSynchCritical to accommodate
longer runtime on systems with large number of CPUs/threads. The usual test
run time when the threads are finishing correctly won't change.
Fixes: https://github.com/FreeRDP/FreeRDP/issues/11800
---
winpr/libwinpr/synch/test/TestSynchCritical.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/winpr/libwinpr/synch/test/TestSynchCritical.c b/winpr/libwinpr/synch/test/TestSynchCritical.c
index 139885a81..b44a70a1e 100644
--- a/winpr/libwinpr/synch/test/TestSynchCritical.c
+++ b/winpr/libwinpr/synch/test/TestSynchCritical.c
@@ -8,7 +8,7 @@
#include <winpr/thread.h>
#include <winpr/interlocked.h>
-#define TEST_SYNC_CRITICAL_TEST1_RUNTIME_MS 50
+#define TEST_SYNC_CRITICAL_TEST1_RUNTIME_MS 100
#define TEST_SYNC_CRITICAL_TEST1_RUNS 4
static CRITICAL_SECTION critical;
--
2.53.0