From 33ebd2c72ce5ab048cc7b30d02a6b07b356edfe8 Mon Sep 17 00:00:00 2001 From: AlmaLinux RelEng Bot Date: Tue, 7 Apr 2026 20:33:51 -0400 Subject: [PATCH] import Oracle_OSS freerdp-3.10.3-5.el10_1.5 --- ...snd-only-clean-up-thread-before-free.patch | 117 ++++++++++++++++++ ...-lock-context-when-updating-listener.patch | 112 +++++++++++++++++ ...ix-audin_server_recv_formats-cleanup.patch | 29 +++++ ...nels-audin-free-up-old-audio-formats.patch | 27 ++++ channels-audin-reset-audin-format.patch | 33 +++++ ...et-error-when-audio_format_read-fail.patch | 34 +++++ ...s-drdynvc-check-pointer-before-reset.patch | 33 +++++ ...-reset-channel_callback-before-close.patch | 35 ++++++ channels-drive-fix-constant-type.patch | 26 ++++ ...-rdpsnd-terminate-thread-before-free.patch | 65 ++++++++++ ...al-explicitly-lock-serial-IrpThreads.patch | 47 +++++++ channels-serial-lock-list-dictionary.patch | 87 +++++++++++++ ...cancel-all-usb-transfers-on-channel-.patch | 24 ++++ ...bdrc-do-not-free-MsConfig-on-failure.patch | 29 +++++ ...ensure-InterfaceNumber-is-within-ran.patch | 42 +++++++ channels-video-fix-wrong-cast.patch | 37 ++++++ channels-video-unify-error-handling.patch | 117 ++++++++++++++++++ ...c-color-add-freerdp_glyph_convert_ex.patch | 99 +++++++++++++++ ...c-nsc-fix-use-of-nsc_process_message.patch | 91 ++++++++++++++ ...mit-copy-area-in-nsc_process_message.patch | 48 +++++++ core-info-fix-missing-NULL-check.patch | 59 +++++++++ freerdp.spec | 88 ++++++++++++- ...raphics-Use-freerdp_glyph_convert_ex.patch | 40 ++++++ ...crease-timeout-for-TestSynchCritical.patch | 30 +++++ 24 files changed, 1348 insertions(+), 1 deletion(-) create mode 100644 channel-rdpsnd-only-clean-up-thread-before-free.patch create mode 100644 channels-ainput-lock-context-when-updating-listener.patch create mode 100644 channels-audin-fix-audin_server_recv_formats-cleanup.patch create mode 100644 channels-audin-free-up-old-audio-formats.patch create mode 100644 channels-audin-reset-audin-format.patch create mode 100644 channels-audin-set-error-when-audio_format_read-fail.patch create mode 100644 channels-drdynvc-check-pointer-before-reset.patch create mode 100644 channels-drdynvc-reset-channel_callback-before-close.patch create mode 100644 channels-drive-fix-constant-type.patch create mode 100644 channels-rdpsnd-terminate-thread-before-free.patch create mode 100644 channels-serial-explicitly-lock-serial-IrpThreads.patch create mode 100644 channels-serial-lock-list-dictionary.patch create mode 100644 channels-urbdrc-cancel-all-usb-transfers-on-channel-.patch create mode 100644 channels-urbdrc-do-not-free-MsConfig-on-failure.patch create mode 100644 channels-urbdrc-ensure-InterfaceNumber-is-within-ran.patch create mode 100644 channels-video-fix-wrong-cast.patch create mode 100644 channels-video-unify-error-handling.patch create mode 100644 codec-color-add-freerdp_glyph_convert_ex.patch create mode 100644 codec-nsc-fix-use-of-nsc_process_message.patch create mode 100644 codec-nsc-limit-copy-area-in-nsc_process_message.patch create mode 100644 core-info-fix-missing-NULL-check.patch create mode 100644 gdi-graphics-Use-freerdp_glyph_convert_ex.patch create mode 100644 winpr-synch-increase-timeout-for-TestSynchCritical.patch diff --git a/channel-rdpsnd-only-clean-up-thread-before-free.patch b/channel-rdpsnd-only-clean-up-thread-before-free.patch new file mode 100644 index 0000000..46c4d08 --- /dev/null +++ b/channel-rdpsnd-only-clean-up-thread-before-free.patch @@ -0,0 +1,117 @@ +From afa6851dc80835d3101e40fcef51b6c5c0f43ea5 Mon Sep 17 00:00:00 2001 +From: akallabeth +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 + diff --git a/channels-ainput-lock-context-when-updating-listener.patch b/channels-ainput-lock-context-when-updating-listener.patch new file mode 100644 index 0000000..5c83025 --- /dev/null +++ b/channels-ainput-lock-context-when-updating-listener.patch @@ -0,0 +1,112 @@ +From d9ca272dce7a776ab475e9b1a8e8c3d2968c8486 Mon Sep 17 00:00:00 2001 +From: akallabeth +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 + diff --git a/channels-audin-fix-audin_server_recv_formats-cleanup.patch b/channels-audin-fix-audin_server_recv_formats-cleanup.patch new file mode 100644 index 0000000..b153ab9 --- /dev/null +++ b/channels-audin-fix-audin_server_recv_formats-cleanup.patch @@ -0,0 +1,29 @@ +From 1c5c74223179d425a1ce6dbbb6a3dd2a958b7aee Mon Sep 17 00:00:00 2001 +From: akallabeth +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 + diff --git a/channels-audin-free-up-old-audio-formats.patch b/channels-audin-free-up-old-audio-formats.patch new file mode 100644 index 0000000..b3ccc0b --- /dev/null +++ b/channels-audin-free-up-old-audio-formats.patch @@ -0,0 +1,27 @@ +From cd1ffa112cfbe1b40a9fd57e299a8ea12e23df0d Mon Sep 17 00:00:00 2001 +From: akallabeth +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 + diff --git a/channels-audin-reset-audin-format.patch b/channels-audin-reset-audin-format.patch new file mode 100644 index 0000000..f604ed3 --- /dev/null +++ b/channels-audin-reset-audin-format.patch @@ -0,0 +1,33 @@ +From 026b81ae5831ac1598d8f7371e0d0996fac7db00 Mon Sep 17 00:00:00 2001 +From: akallabeth +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 + diff --git a/channels-audin-set-error-when-audio_format_read-fail.patch b/channels-audin-set-error-when-audio_format_read-fail.patch new file mode 100644 index 0000000..2f3b923 --- /dev/null +++ b/channels-audin-set-error-when-audio_format_read-fail.patch @@ -0,0 +1,34 @@ +From 668352a2e241ba017679c11a22ecbe29d0b17401 Mon Sep 17 00:00:00 2001 +From: Ondrej Holy +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 + diff --git a/channels-drdynvc-check-pointer-before-reset.patch b/channels-drdynvc-check-pointer-before-reset.patch new file mode 100644 index 0000000..2f45333 --- /dev/null +++ b/channels-drdynvc-check-pointer-before-reset.patch @@ -0,0 +1,33 @@ +From 54ab7e13650f3ef9d912e9d0e336b9d7f22537ca Mon Sep 17 00:00:00 2001 +From: Ondrej Holy +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 + diff --git a/channels-drdynvc-reset-channel_callback-before-close.patch b/channels-drdynvc-reset-channel_callback-before-close.patch new file mode 100644 index 0000000..99c64b4 --- /dev/null +++ b/channels-drdynvc-reset-channel_callback-before-close.patch @@ -0,0 +1,35 @@ +From 429260893d7f67011d916394974ec0294ff65a90 Mon Sep 17 00:00:00 2001 +From: Ondrej Holy +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 + diff --git a/channels-drive-fix-constant-type.patch b/channels-drive-fix-constant-type.patch new file mode 100644 index 0000000..9bf0c8a --- /dev/null +++ b/channels-drive-fix-constant-type.patch @@ -0,0 +1,26 @@ +From 3da319570c8a6be0a79b3306f1ed354c4a943259 Mon Sep 17 00:00:00 2001 +From: akallabeth +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 + diff --git a/channels-rdpsnd-terminate-thread-before-free.patch b/channels-rdpsnd-terminate-thread-before-free.patch new file mode 100644 index 0000000..6167956 --- /dev/null +++ b/channels-rdpsnd-terminate-thread-before-free.patch @@ -0,0 +1,65 @@ +From 622bb7b4402491ca003f47472d0e478132673696 Mon Sep 17 00:00:00 2001 +From: akallabeth +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 + diff --git a/channels-serial-explicitly-lock-serial-IrpThreads.patch b/channels-serial-explicitly-lock-serial-IrpThreads.patch new file mode 100644 index 0000000..1ed5b29 --- /dev/null +++ b/channels-serial-explicitly-lock-serial-IrpThreads.patch @@ -0,0 +1,47 @@ +From 675c20f08f32ca5ec06297108bdf30147d6e2cd9 Mon Sep 17 00:00:00 2001 +From: akallabeth +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 + diff --git a/channels-serial-lock-list-dictionary.patch b/channels-serial-lock-list-dictionary.patch new file mode 100644 index 0000000..189fe7f --- /dev/null +++ b/channels-serial-lock-list-dictionary.patch @@ -0,0 +1,87 @@ +From b35aa3614d32bff3fc1272cd7c4617f711fca1a4 Mon Sep 17 00:00:00 2001 +From: akallabeth +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 + diff --git a/channels-urbdrc-cancel-all-usb-transfers-on-channel-.patch b/channels-urbdrc-cancel-all-usb-transfers-on-channel-.patch new file mode 100644 index 0000000..0fccb7d --- /dev/null +++ b/channels-urbdrc-cancel-all-usb-transfers-on-channel-.patch @@ -0,0 +1,24 @@ +From 414f701464929c217f2509bcbd6d2c1f00f7ed73 Mon Sep 17 00:00:00 2001 +From: akallabeth +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 + diff --git a/channels-urbdrc-do-not-free-MsConfig-on-failure.patch b/channels-urbdrc-do-not-free-MsConfig-on-failure.patch new file mode 100644 index 0000000..36c2985 --- /dev/null +++ b/channels-urbdrc-do-not-free-MsConfig-on-failure.patch @@ -0,0 +1,29 @@ +From a304360d05cd2692ac4d87af017277d934863616 Mon Sep 17 00:00:00 2001 +From: akallabeth +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 + diff --git a/channels-urbdrc-ensure-InterfaceNumber-is-within-ran.patch b/channels-urbdrc-ensure-InterfaceNumber-is-within-ran.patch new file mode 100644 index 0000000..70cf9dc --- /dev/null +++ b/channels-urbdrc-ensure-InterfaceNumber-is-within-ran.patch @@ -0,0 +1,42 @@ +From 2d563a50be17c1b407ca448b1321378c0726dd31 Mon Sep 17 00:00:00 2001 +From: akallabeth +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 + diff --git a/channels-video-fix-wrong-cast.patch b/channels-video-fix-wrong-cast.patch new file mode 100644 index 0000000..60585e1 --- /dev/null +++ b/channels-video-fix-wrong-cast.patch @@ -0,0 +1,37 @@ +From bd3c5d3ab73cbe10ac3c079fb0b26610d3231133 Mon Sep 17 00:00:00 2001 +From: Ondrej Holy +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 + diff --git a/channels-video-unify-error-handling.patch b/channels-video-unify-error-handling.patch new file mode 100644 index 0000000..5b7a356 --- /dev/null +++ b/channels-video-unify-error-handling.patch @@ -0,0 +1,117 @@ +From 0328bb3828b836d53b904187b0b6d49f940d1180 Mon Sep 17 00:00:00 2001 +From: Ondrej Holy +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 + diff --git a/codec-color-add-freerdp_glyph_convert_ex.patch b/codec-color-add-freerdp_glyph_convert_ex.patch new file mode 100644 index 0000000..0161bbf --- /dev/null +++ b/codec-color-add-freerdp_glyph_convert_ex.patch @@ -0,0 +1,99 @@ +From 1ea1d83a365f81628a2c38e9ec0223de6b975889 Mon Sep 17 00:00:00 2001 +From: Ondrej Holy +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 + ++#include + #include + + #include +@@ -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 + diff --git a/codec-nsc-fix-use-of-nsc_process_message.patch b/codec-nsc-fix-use-of-nsc_process_message.patch new file mode 100644 index 0000000..3b04c94 --- /dev/null +++ b/codec-nsc-fix-use-of-nsc_process_message.patch @@ -0,0 +1,91 @@ +From 7702ec2f2aadb0cfae86b00c177647e31f6159d4 Mon Sep 17 00:00:00 2001 +From: Ondrej Holy +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 + diff --git a/codec-nsc-limit-copy-area-in-nsc_process_message.patch b/codec-nsc-limit-copy-area-in-nsc_process_message.patch new file mode 100644 index 0000000..3903855 --- /dev/null +++ b/codec-nsc-limit-copy-area-in-nsc_process_message.patch @@ -0,0 +1,48 @@ +From 33e12b7cfb83875479822769ffd4fda799f294ff Mon Sep 17 00:00:00 2001 +From: Ondrej Holy +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 + ++#include + #include + #include + #include +@@ -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 + diff --git a/core-info-fix-missing-NULL-check.patch b/core-info-fix-missing-NULL-check.patch new file mode 100644 index 0000000..b96d9c6 --- /dev/null +++ b/core-info-fix-missing-NULL-check.patch @@ -0,0 +1,59 @@ +From 51847b9d1c3a3720e4742d2a6ee92429f0c1af48 Mon Sep 17 00:00:00 2001 +From: Ondrej Holy +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 + diff --git a/freerdp.spec b/freerdp.spec index ace293e..b3b9890 100644 --- a/freerdp.spec +++ b/freerdp.spec @@ -30,7 +30,7 @@ Name: freerdp Epoch: 2 Version: 3.10.3 -Release: 5%{?dist}.3 +Release: 5%{?dist}.5 Summary: Free implementation of the Remote Desktop Protocol (RDP) # The effective license is Apache-2.0 but: @@ -105,6 +105,81 @@ Patch: codec-clear-fix-destination-checks.patch # 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 @@ -427,6 +502,17 @@ find %{buildroot} -name "*.a" -delete %{_libdir}/pkgconfig/winpr-tools3.pc %changelog +* Tue Mar 31 2026 Ondrej Holy - 2:3.10.3-5.5 +- Fix use of nsc_process_message +- Increase timeout for TestSynchCritical + Resolves: RHEL-155979 + +* Fri Mar 27 2026 Ondrej Holy - 2:3.10.3-5.4 +- Backport several CVE fixes + Resolves: RHEL-147948, RHEL-147949, RHEL-147956, RHEL-147963, RHEL-147964 + Resolves: RHEL-147972, RHEL-147979, RHEL-147984, RHEL-147985, RHEL-148898 + Resolves: RHEL-148978, RHEL-148984, RHEL-149051, RHEL-155979 + * Wed Mar 25 2026 Ondrej Holy - 2:3.10.3-5.3 - Backport several CVE fixes Resolves: RHEL-151975, RHEL-152202 diff --git a/gdi-graphics-Use-freerdp_glyph_convert_ex.patch b/gdi-graphics-Use-freerdp_glyph_convert_ex.patch new file mode 100644 index 0000000..da4572f --- /dev/null +++ b/gdi-graphics-Use-freerdp_glyph_convert_ex.patch @@ -0,0 +1,40 @@ +From e1bbcba2ee4ba8bb20bb73b16717a2e5d491825b Mon Sep 17 00:00:00 2001 +From: Ondrej Holy +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 + diff --git a/winpr-synch-increase-timeout-for-TestSynchCritical.patch b/winpr-synch-increase-timeout-for-TestSynchCritical.patch new file mode 100644 index 0000000..64805f3 --- /dev/null +++ b/winpr-synch-increase-timeout-for-TestSynchCritical.patch @@ -0,0 +1,30 @@ +From 907ca47e40583a7788674bb2f06258edd0c34223 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Dan=20Hor=C3=A1k?= +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 + #include + +-#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 +