diff --git a/debian/changelog b/debian/changelog index 957b6d5..8335b38 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,15 @@ +freerdp2 (2.11.2+dfsg1-1deepin1) unstable; urgency=medium + + * Security fixes: + * CVE-2026-24675: [channels,urbdrc] do not free MsConfig on failure + * CVE-2026-24681: [channels,urbdrc] cancel all usb transfers on channel close + * CVE-2026-25941: [channels,rdpgfx] check available stream length + * CVE-2026-26986: [client,x11] fix xf_rail_window_common cleanup + * CVE-2026-29776: [core,orders] improve input validation + * CVE-2026-33952: [core,gateway] Check rpcconn_common_hdr_t::auth_length is + + -- deepin-ci-robot Thu, 28 May 2026 23:28:12 +0800 + freerdp2 (2.11.2+dfsg1-1) unstable; urgency=medium * New upstream release. (Closes: #1051638). diff --git a/debian/patches/CVE-2026-23948.patch b/debian/patches/CVE-2026-23948.patch new file mode 100644 index 0000000..db0954e --- /dev/null +++ b/debian/patches/CVE-2026-23948.patch @@ -0,0 +1,53 @@ +From 4d44e3c097656a8b9ec696353647b0888ca45860 Mon Sep 17 00:00:00 2001 +From: akallabeth +Date: Mon, 19 Jan 2026 20:11:24 +0100 +Subject: [PATCH] [core,info] fix missing NULL check + +--- + 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 0b17b75d9f64..cc54aee7baef 100644 +--- a/libfreerdp/core/info.c ++++ b/libfreerdp/core/info.c +@@ -1447,7 +1447,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; +@@ -1462,11 +1462,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, 256); /* lmcons.h UNLEN */ ++ if (info->domain) ++ domainLen = strnlen(info->domain, 256); /* lmcons.h UNLEN */ + if (domainLen >= UINT32_MAX / sizeof(WCHAR)) + return FALSE; + Stream_Write_UINT32(s, (UINT32)(domainLen + 1) * sizeof(WCHAR)); +- usernameLen = strnlen(info->username, 256); /* lmcons.h UNLEN */ ++ ++ if (info->username) ++ usernameLen = strnlen(info->username, 256); /* lmcons.h UNLEN */ + if (usernameLen >= UINT32_MAX / sizeof(WCHAR)) + return FALSE; + Stream_Write_UINT32(s, (UINT32)(usernameLen + 1) * sizeof(WCHAR)); +@@ -1534,10 +1537,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) + { + UINT16 sec_flags = 0; +- wStream* s = NULL; + BOOL status = 0; ++ ++ WINPR_ASSERT(context); + rdpRdp* rdp = context->rdp; +- s = rdp_data_pdu_init(rdp, &sec_flags); ++ wStream* s = rdp_data_pdu_init(rdp, &sec_flags); + + if (!s) + return FALSE; \ No newline at end of file diff --git a/debian/patches/CVE-2026-24491.patch b/debian/patches/CVE-2026-24491.patch new file mode 100644 index 0000000..9207c49 --- /dev/null +++ b/debian/patches/CVE-2026-24491.patch @@ -0,0 +1,49 @@ +From e02e052f6692550e539d10f99de9c35a23492db2 Mon Sep 17 00:00:00 2001 +From: akallabeth +Date: Mon, 26 Jan 2026 10:06:29 +0100 +Subject: [PATCH] [channels,drdynvc] reset channel_callback before close + +The channel_callback usually frees up the memory of the callback. To +ensure that there is no access to any of the data structures in it +invalidate the pointer used to access it before a free. +--- + channels/drdynvc/client/drdynvc_main.c | 6 ++---- + 1 file changed, 2 insertions(+), 4 deletions(-) + +diff --git a/channels/drdynvc/client/drdynvc_main.c b/channels/drdynvc/client/drdynvc_main.c +index 78dde80ee333..fe2821a6ab2e 100644 +--- a/channels/drdynvc/client/drdynvc_main.c ++++ b/channels/drdynvc/client/drdynvc_main.c +@@ -510,6 +510,7 @@ static UINT dvcman_channel_close(DVCMAN_CHANNEL* channel, BOOL perRequest, BOOL + + { + IWTSVirtualChannelCallback* cb = channel->channel_callback; ++ channel->channel_callback = NULL; + if (cb) + { + check_open_close_receive(channel); +@@ -517,8 +518,6 @@ static UINT dvcman_channel_close(DVCMAN_CHANNEL* channel, BOOL perRequest, BOOL + } + } + +- channel->channel_callback = NULL; +- + if (channel->dvcman && channel->dvcman->drdynvc) + { + if (context) +@@ -796,14 +795,13 @@ static DVCMAN_CHANNEL* dvcman_create_channel(drdynvcPlugin* drdynvc, + */ + static UINT dvcman_open_channel(drdynvcPlugin* drdynvc, DVCMAN_CHANNEL* channel) + { +- IWTSVirtualChannelCallback* pCallback = NULL; + UINT error = CHANNEL_RC_OK; + + WINPR_ASSERT(drdynvc); + WINPR_ASSERT(channel); + if (channel->state == DVC_CHANNEL_RUNNING) + { +- pCallback = channel->channel_callback; ++ IWTSVirtualChannelCallback* pCallback = channel->channel_callback; + + if (pCallback->OnOpen) + { \ No newline at end of file diff --git a/debian/patches/CVE-2026-24675.patch b/debian/patches/CVE-2026-24675.patch new file mode 100644 index 0000000..2a0d30e --- /dev/null +++ b/debian/patches/CVE-2026-24675.patch @@ -0,0 +1,24 @@ +From d676518809c319eec15911c705c13536036af2ae 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 + +let the channel handle it later. +--- + channels/urbdrc/client/data_transfer.c | 4 +--- + 1 file changed, 1 insertion(+), 3 deletions(-) + +--- freerdp2.orig/channels/urbdrc/client/data_transfer.c ++++ freerdp2/channels/urbdrc/client/data_transfer.c +@@ -570,10 +570,8 @@ static UINT urb_select_interface(IUDEVIC + 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)) + { diff --git a/debian/patches/CVE-2026-24677.patch b/debian/patches/CVE-2026-24677.patch new file mode 100644 index 0000000..ebc09a9 --- /dev/null +++ b/debian/patches/CVE-2026-24677.patch @@ -0,0 +1,160 @@ +From d2d4f449312ddafd4a4c6c8a4f856c7f0d44a3b5 Mon Sep 17 00:00:00 2001 +From: akallabeth +Date: Mon, 26 Jan 2026 10:38:03 +0100 +Subject: [PATCH] [channels,rdpecam] ensure sws context size matches + +--- + channels/rdpecam/client/camera.h | 2 + + channels/rdpecam/client/camera_device_main.c | 7 ++- + channels/rdpecam/client/encoding.c | 48 ++++++++++++++++---- + channels/remdesk/server/remdesk_main.c | 3 +- + 4 files changed, 47 insertions(+), 13 deletions(-) + +diff --git a/channels/rdpecam/client/camera.h b/channels/rdpecam/client/camera.h +index f0f4f9d9a0b1..05d9f98ba1fa 100644 +--- a/channels/rdpecam/client/camera.h ++++ b/channels/rdpecam/client/camera.h +@@ -121,6 +121,8 @@ typedef struct + #endif + + /* sws_scale */ ++ uint32_t swsWidth; ++ uint32_t swsHeight; + struct SwsContext* sws; + + } CameraDeviceStream; +diff --git a/channels/rdpecam/client/camera_device_main.c b/channels/rdpecam/client/camera_device_main.c +index db53246f01f2..b76a37ca80de 100644 +--- a/channels/rdpecam/client/camera_device_main.c ++++ b/channels/rdpecam/client/camera_device_main.c +@@ -121,7 +121,7 @@ static BOOL mediaSupportDrops(CAM_MEDIA_FORMAT format) + } + } + +-static UINT ecam_dev_send_pending(CameraDevice* dev, int streamIndex, CameraDeviceStream* stream) ++static UINT ecam_dev_send_pending(CameraDevice* dev, size_t streamIndex, CameraDeviceStream* stream) + { + WINPR_ASSERT(dev); + WINPR_ASSERT(stream); +@@ -160,8 +160,7 @@ static UINT ecam_dev_send_pending(CameraDevice* dev, int streamIndex, CameraDevi + stream->samplesRequested--; + stream->haveSample = FALSE; + +- return ecam_dev_send_sample_response(dev, WINPR_ASSERTING_INT_CAST(size_t, streamIndex), +- encodedSample, encodedSize); ++ return ecam_dev_send_sample_response(dev, streamIndex, encodedSample, encodedSize); + } + + static UINT ecam_dev_sample_captured_callback(CameraDevice* dev, int streamIndex, +@@ -229,7 +228,7 @@ static UINT ecam_dev_sample_captured_callback(CameraDevice* dev, int streamIndex + Stream_SealLength(stream->pendingSample); + stream->haveSample = TRUE; + +- ret = ecam_dev_send_pending(dev, streamIndex, stream); ++ ret = ecam_dev_send_pending(dev, WINPR_ASSERTING_INT_CAST(size_t, streamIndex), stream); + + out: + LeaveCriticalSection(&stream->lock); +diff --git a/channels/rdpecam/client/encoding.c b/channels/rdpecam/client/encoding.c +index c5535fd7fbec..3d333ce68952 100644 +--- a/channels/rdpecam/client/encoding.c ++++ b/channels/rdpecam/client/encoding.c +@@ -220,6 +220,30 @@ static enum AVPixelFormat ecamToAVPixFormat(CAM_MEDIA_FORMAT ecamFormat) + } + } + ++static void ecam_sws_free(CameraDeviceStream* stream) ++{ ++ if (stream->sws) ++ { ++ sws_freeContext(stream->sws); ++ stream->sws = NULL; ++ } ++} ++ ++static BOOL ecam_sws_valid(const CameraDeviceStream* stream) ++{ ++ if (!stream->sws) ++ return FALSE; ++ if (stream->swsWidth != stream->currMediaType.Width) ++ return FALSE; ++ if (stream->swsHeight != stream->currMediaType.Height) ++ return FALSE; ++ if (stream->currMediaType.Width > INT32_MAX) ++ return FALSE; ++ if (stream->currMediaType.Height > INT32_MAX) ++ return FALSE; ++ return TRUE; ++} ++ + /** + * Function description + * initialize libswscale +@@ -230,9 +254,16 @@ static BOOL ecam_init_sws_context(CameraDeviceStream* stream, enum AVPixelFormat + { + WINPR_ASSERT(stream); + +- if (stream->sws) ++ if (stream->currMediaType.Width > INT32_MAX) ++ return FALSE; ++ if (stream->currMediaType.Height > INT32_MAX) ++ return FALSE; ++ ++ if (ecam_sws_valid(stream)) + return TRUE; + ++ ecam_sws_free(stream); ++ + /* replacing deprecated JPEG formats, still produced by decoder */ + switch (pixFormat) + { +@@ -260,8 +291,10 @@ static BOOL ecam_init_sws_context(CameraDeviceStream* stream, enum AVPixelFormat + break; + } + +- const int width = (int)stream->currMediaType.Width; +- const int height = (int)stream->currMediaType.Height; ++ stream->swsWidth = stream->currMediaType.Width; ++ stream->swsHeight = stream->currMediaType.Height; ++ const int width = WINPR_ASSERTING_INT_CAST(int, stream->currMediaType.Width); ++ const int height = WINPR_ASSERTING_INT_CAST(int, stream->currMediaType.Height); + + const enum AVPixelFormat outPixFormat = + h264_context_get_option(stream->h264, H264_CONTEXT_OPTION_HW_ACCEL) ? AV_PIX_FMT_NV12 +@@ -295,6 +328,9 @@ static BOOL ecam_encoder_compress_h264(CameraDeviceStream* stream, const BYTE* s + CAM_MEDIA_FORMAT inputFormat = streamInputFormat(stream); + enum AVPixelFormat pixFormat = AV_PIX_FMT_NONE; + ++ if (!ecam_sws_valid(stream)) ++ return FALSE; ++ + #if defined(WITH_INPUT_FORMAT_H264) + if (inputFormat == CAM_MEDIA_FORMAT_MJPG_H264) + { +@@ -385,11 +421,7 @@ static void ecam_encoder_context_free_h264(CameraDeviceStream* stream) + { + WINPR_ASSERT(stream); + +- if (stream->sws) +- { +- sws_freeContext(stream->sws); +- stream->sws = NULL; +- } ++ ecam_sws_free(stream); + + #if defined(WITH_INPUT_FORMAT_MJPG) + if (stream->avOutFrame) +diff --git a/channels/remdesk/server/remdesk_main.c b/channels/remdesk/server/remdesk_main.c +index 713b3370cee6..445027aacc22 100644 +--- a/channels/remdesk/server/remdesk_main.c ++++ b/channels/remdesk/server/remdesk_main.c +@@ -526,7 +526,8 @@ static DWORD WINAPI remdesk_server_thread(LPVOID arg) + Stream_SealLength(s); + Stream_SetPosition(s, 0); + +- if ((error = remdesk_server_receive_pdu(context, s))) ++ error = remdesk_server_receive_pdu(context, s); ++ if (error) + { + WLog_ERR(TAG, "remdesk_server_receive_pdu failed with error %" PRIu32 "!", + error); \ No newline at end of file diff --git a/debian/patches/CVE-2026-24678.patch b/debian/patches/CVE-2026-24678.patch new file mode 100644 index 0000000..d39eca4 --- /dev/null +++ b/debian/patches/CVE-2026-24678.patch @@ -0,0 +1,24 @@ +From f3ab1a16139036179d9852745fdade18fec11600 Mon Sep 17 00:00:00 2001 +From: akallabeth +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 b76a37ca80de..9ba3c65c5d5c 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) \ No newline at end of file diff --git a/debian/patches/CVE-2026-24679.patch b/debian/patches/CVE-2026-24679.patch new file mode 100644 index 0000000..ca2782a --- /dev/null +++ b/debian/patches/CVE-2026-24679.patch @@ -0,0 +1,39 @@ +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 6c2376f74fd6..5341248ec64f 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, \ No newline at end of file diff --git a/debian/patches/CVE-2026-24680.patch b/debian/patches/CVE-2026-24680.patch new file mode 100644 index 0000000..01a1bc0 --- /dev/null +++ b/debian/patches/CVE-2026-24680.patch @@ -0,0 +1,21 @@ +From c42ecbd183b001e76bfc3614cddfad0034acc758 Mon Sep 17 00:00:00 2001 +From: akallabeth +Date: Mon, 26 Jan 2026 11:01:17 +0100 +Subject: [PATCH] [client,sdl] reset pointer after memory release + +--- + client/SDL/SDL3/sdl_pointer.cpp | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/client/SDL/SDL3/sdl_pointer.cpp b/client/SDL/SDL3/sdl_pointer.cpp +index a5e1147e3954..472e460667aa 100644 +--- a/client/SDL/SDL3/sdl_pointer.cpp ++++ b/client/SDL/SDL3/sdl_pointer.cpp +@@ -61,6 +61,7 @@ static BOOL sdl_Pointer_New(rdpContext* context, rdpPointer* pointer) + &context->gdi->palette)) + { + winpr_aligned_free(ptr->data); ++ ptr->data = nullptr; + return FALSE; + } + \ No newline at end of file diff --git a/debian/patches/CVE-2026-24681.patch b/debian/patches/CVE-2026-24681.patch new file mode 100644 index 0000000..a0228bc --- /dev/null +++ b/debian/patches/CVE-2026-24681.patch @@ -0,0 +1,19 @@ +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(+) + +--- freerdp2.orig/channels/urbdrc/client/libusb/libusb_udevice.c ++++ freerdp2/channels/urbdrc/client/libusb/libusb_udevice.c +@@ -1116,6 +1116,7 @@ static void libusb_udev_mark_channel_clo + 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); + } + } diff --git a/debian/patches/CVE-2026-24682.patch b/debian/patches/CVE-2026-24682.patch new file mode 100644 index 0000000..219972f --- /dev/null +++ b/debian/patches/CVE-2026-24682.patch @@ -0,0 +1,26 @@ +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 5046a7d6f27c..17077efa6652 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); + } \ No newline at end of file diff --git a/debian/patches/CVE-2026-24683.patch b/debian/patches/CVE-2026-24683.patch new file mode 100644 index 0000000..864ec52 --- /dev/null +++ b/debian/patches/CVE-2026-24683.patch @@ -0,0 +1,109 @@ +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 c291bd727285..5545753600a1 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); + } + \ No newline at end of file diff --git a/debian/patches/CVE-2026-24684.patch b/debian/patches/CVE-2026-24684.patch new file mode 100644 index 0000000..aff498d --- /dev/null +++ b/debian/patches/CVE-2026-24684.patch @@ -0,0 +1,62 @@ +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 49c763a87e9b..61a29ec40aa8 100644 +--- a/channels/rdpsnd/client/rdpsnd_main.c ++++ b/channels/rdpsnd/client/rdpsnd_main.c +@@ -1278,11 +1278,29 @@ static UINT rdpsnd_virtual_channel_event_connected(rdpsndPlugin* rdpsnd, LPVOID + 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); \ No newline at end of file diff --git a/debian/patches/CVE-2026-25941.patch b/debian/patches/CVE-2026-25941.patch new file mode 100644 index 0000000..671f317 --- /dev/null +++ b/debian/patches/CVE-2026-25941.patch @@ -0,0 +1,21 @@ +From 2e3b77e28ac6a398897d28ba464dcc5dfab9c9e2 Mon Sep 17 00:00:00 2001 +From: Armin Novak +Date: Mon, 9 Feb 2026 13:18:51 +0100 +Subject: [PATCH] [channels,rdpgfx] check available stream length + +--- + channels/rdpgfx/client/rdpgfx_main.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +--- freerdp2.orig/channels/rdpgfx/client/rdpgfx_main.c ++++ freerdp2/channels/rdpgfx/client/rdpgfx_main.c +@@ -983,7 +983,8 @@ static UINT rdpgfx_recv_wire_to_surface_ + } + + pdu.bitmapData = Stream_Pointer(s); +- Stream_Seek(s, pdu.bitmapDataLength); ++ if (!Stream_SafeSeek(s, pdu.bitmapDataLength)) ++ return ERROR_INVALID_DATA; + + DEBUG_RDPGFX(gfx->log, + "RecvWireToSurface1Pdu: surfaceId: %" PRIu16 " codecId: %s (0x%04" PRIX16 diff --git a/debian/patches/CVE-2026-25942.patch b/debian/patches/CVE-2026-25942.patch new file mode 100644 index 0000000..4dd09ae --- /dev/null +++ b/debian/patches/CVE-2026-25942.patch @@ -0,0 +1,102 @@ +From 9362a0bf8dda04eedbca07d5dfaec1044e67cc6b Mon Sep 17 00:00:00 2001 +From: Armin Novak +Date: Mon, 9 Feb 2026 13:39:28 +0100 +Subject: [PATCH] [client,x11] stringfiy functions for RAILS + +--- + client/X11/xf_rail.c | 68 ++++++++++++++++++++++++++++++++------------ + 1 file changed, 50 insertions(+), 18 deletions(-) + +diff --git a/client/X11/xf_rail.c b/client/X11/xf_rail.c +index 7d3869a6587a..7c7f1a3a589a 100644 +--- a/client/X11/xf_rail.c ++++ b/client/X11/xf_rail.c +@@ -37,22 +37,51 @@ + #include + #define TAG CLIENT_TAG("x11") + +-static const char* error_code_names[] = { "RAIL_EXEC_S_OK", +- "RAIL_EXEC_E_HOOK_NOT_LOADED", +- "RAIL_EXEC_E_DECODE_FAILED", +- "RAIL_EXEC_E_NOT_IN_ALLOWLIST", +- "RAIL_EXEC_E_FILE_NOT_FOUND", +- "RAIL_EXEC_E_FAIL", +- "RAIL_EXEC_E_SESSION_LOCKED" }; +- +-#ifdef WITH_DEBUG_RAIL +-static const char* movetype_names[] = { +- "(invalid)", "RAIL_WMSZ_LEFT", "RAIL_WMSZ_RIGHT", +- "RAIL_WMSZ_TOP", "RAIL_WMSZ_TOPLEFT", "RAIL_WMSZ_TOPRIGHT", +- "RAIL_WMSZ_BOTTOM", "RAIL_WMSZ_BOTTOMLEFT", "RAIL_WMSZ_BOTTOMRIGHT", +- "RAIL_WMSZ_MOVE", "RAIL_WMSZ_KEYMOVE", "RAIL_WMSZ_KEYSIZE" +-}; +-#endif ++static const char* error_code2str(UINT32 code) ++{ ++#define EVCASE(x) \ ++ case x: \ ++ return #x ++ switch (code) ++ { ++ EVCASE(RAIL_EXEC_S_OK); ++ EVCASE(RAIL_EXEC_E_HOOK_NOT_LOADED); ++ EVCASE(RAIL_EXEC_E_DECODE_FAILED); ++ EVCASE(RAIL_EXEC_E_NOT_IN_ALLOWLIST); ++ EVCASE(RAIL_EXEC_E_FILE_NOT_FOUND); ++ EVCASE(RAIL_EXEC_E_FAIL); ++ EVCASE(RAIL_EXEC_E_SESSION_LOCKED); ++ default: ++ return "RAIL_EXEC_E_UNKNOWN"; ++ } ++#undef EVCASE ++} ++ ++static const char* movetype2str(UINT32 code) ++{ ++#define EVCASE(x) \ ++ case x: \ ++ return #x ++ ++ switch (code) ++ { ++ ++ EVCASE(RAIL_WMSZ_LEFT); ++ EVCASE(RAIL_WMSZ_RIGHT); ++ EVCASE(RAIL_WMSZ_TOP); ++ EVCASE(RAIL_WMSZ_TOPLEFT); ++ EVCASE(RAIL_WMSZ_TOPRIGHT); ++ EVCASE(RAIL_WMSZ_BOTTOM); ++ EVCASE(RAIL_WMSZ_BOTTOMLEFT); ++ EVCASE(RAIL_WMSZ_BOTTOMRIGHT); ++ EVCASE(RAIL_WMSZ_MOVE); ++ EVCASE(RAIL_WMSZ_KEYMOVE); ++ EVCASE(RAIL_WMSZ_KEYSIZE); ++ default: ++ return "RAIL_WMSZ_INVALID"; ++ } ++#undef EVCASE ++} + + struct xf_rail_icon + { +@@ -1013,8 +1042,9 @@ static UINT xf_rail_server_execute_result(RailClientContext* context, + + if (execResult->execResult != RAIL_EXEC_S_OK) + { +- WLog_ERR(TAG, "RAIL exec error: execResult=%s NtError=0x%X\n", +- error_code_names[execResult->execResult], execResult->rawResult); ++ WLog_ERR(TAG, "RAIL exec error: execResult=%s [0x%08" PRIx32 "] NtError=0x%X\n", ++ error_code2str(execResult->execResult), execResult->execResult, ++ execResult->rawResult); + freerdp_abort_connect_context(&xfc->common.context); + } + +@@ -1078,6 +1108,8 @@ static UINT xf_rail_server_local_move_size(RailClientContext* context, + if (!appWindow) + return ERROR_INTERNAL_ERROR; + ++ WLog_Print(xfc->log, WLOG_TRACE, "%s [0x%08" PRIx32 "]", ++ movetype2str(localMoveSize->moveSizeType), localMoveSize->moveSizeType); + switch (localMoveSize->moveSizeType) + { + case RAIL_WMSZ_LEFT: \ No newline at end of file diff --git a/debian/patches/CVE-2026-25952.patch b/debian/patches/CVE-2026-25952.patch new file mode 100644 index 0000000..ee894dd --- /dev/null +++ b/debian/patches/CVE-2026-25952.patch @@ -0,0 +1,408 @@ +From 1994e9844212a6dfe0ff12309fef520e888986b5 Mon Sep 17 00:00:00 2001 +From: Armin Novak +Date: Mon, 9 Feb 2026 16:09:51 +0100 +Subject: [PATCH] [client,x11] lock appWindow + +When using xf_rail_get_window lock the hash talbe until xf_rail_return_window +--- + client/X11/xf_event.c | 47 +++++++++++++++++++++++------------ + client/X11/xf_graphics.c | 10 +++++--- + client/X11/xf_rail.c | 53 ++++++++++++++++++++++++++++------------ + client/X11/xf_rail.h | 4 +++ + client/X11/xf_window.c | 6 +++++ + client/X11/xf_window.h | 4 +++ + 6 files changed, 89 insertions(+), 35 deletions(-) + +diff --git a/client/X11/xf_event.c b/client/X11/xf_event.c +index ceb0874aba35..6e2f513987cb 100644 +--- a/client/X11/xf_event.c ++++ b/client/X11/xf_event.c +@@ -441,7 +441,9 @@ BOOL xf_generic_MotionNotify_(xfContext* xfc, int x, int y, Window window, BOOL + if (app) + { + /* make sure window exists */ +- if (!xf_AppWindowFromX11Window(xfc, window)) ++ xfAppWindow* appWindow = xf_AppWindowFromX11Window(xfc, window); ++ xf_rail_return_window(appWindow); ++ if (!appWindow) + return TRUE; + + /* Translate to desktop coordinates */ +@@ -547,7 +549,9 @@ BOOL xf_generic_ButtonEvent_(xfContext* xfc, int x, int y, int button, Window wi + if (app) + { + /* make sure window exists */ +- if (!xf_AppWindowFromX11Window(xfc, window)) ++ xfAppWindow* appWindow = xf_AppWindowFromX11Window(xfc, window); ++ xf_rail_return_window(appWindow); ++ if (!appWindow) + return TRUE; + + /* Translate to desktop coordinates */ +@@ -708,6 +712,7 @@ static BOOL xf_event_FocusIn(xfContext* xfc, const XFocusInEvent* event, BOOL ap + */ + if (appWindow) + xf_rail_adjust_position(xfc, appWindow); ++ xf_rail_return_window(appWindow); + } + + xf_keyboard_focus_in(xfc); +@@ -762,12 +767,13 @@ static BOOL xf_event_ClientMessage(xfContext* xfc, const XClientMessageEvent* ev + { + if (app) + { ++ BOOL rc = TRUE; + xfAppWindow* appWindow = xf_AppWindowFromX11Window(xfc, event->window); + + if (appWindow) +- return xf_rail_send_client_system_command(xfc, appWindow->windowId, SC_CLOSE); +- +- return TRUE; ++ rc = xf_rail_send_client_system_command(xfc, appWindow->windowId, SC_CLOSE); ++ xf_rail_return_window(appWindow); ++ return rc; + } + else + { +@@ -800,6 +806,7 @@ static BOOL xf_event_EnterNotify(xfContext* xfc, const XEnterWindowEvent* event, + + /* keep track of which window has focus so that we can apply pointer updates */ + xfc->appWindow = appWindow; ++ xf_rail_return_window(appWindow); + } + + return TRUE; +@@ -821,6 +828,7 @@ static BOOL xf_event_LeaveNotify(xfContext* xfc, const XLeaveWindowEvent* event, + /* keep track of which window has focus so that we can apply pointer updates */ + if (xfc->appWindow == appWindow) + xfc->appWindow = NULL; ++ xf_rail_return_window(appWindow); + } + return TRUE; + } +@@ -926,6 +934,7 @@ static BOOL xf_event_ConfigureNotify(xfContext* xfc, const XConfigureEvent* even + xf_rail_adjust_position(xfc, appWindow); + } + } ++ xf_rail_return_window(appWindow); + } + return xf_pointer_update_scale(xfc); + } +@@ -949,6 +958,7 @@ static BOOL xf_event_MapNotify(xfContext* xfc, const XMapEvent* event, BOOL app) + // xf_rail_send_client_system_command(xfc, appWindow->windowId, SC_RESTORE); + appWindow->is_mapped = TRUE; + } ++ xf_rail_return_window(appWindow); + } + + return TRUE; +@@ -963,13 +973,14 @@ static BOOL xf_event_UnmapNotify(xfContext* xfc, const XUnmapEvent* event, BOOL + xf_keyboard_release_all_keypress(xfc); + + if (!app) +- gdi_send_suppress_output(xfc->common.context.gdi, TRUE); +- else ++ return gdi_send_suppress_output(xfc->common.context.gdi, TRUE); ++ + { + xfAppWindow* appWindow = xf_AppWindowFromX11Window(xfc, event->window); + + if (appWindow) + appWindow->is_mapped = FALSE; ++ xf_rail_return_window(appWindow); + } + + return TRUE; +@@ -977,6 +988,7 @@ static BOOL xf_event_UnmapNotify(xfContext* xfc, const XUnmapEvent* event, BOOL + + static BOOL xf_event_PropertyNotify(xfContext* xfc, const XPropertyEvent* event, BOOL app) + { ++ BOOL rc = TRUE; + WINPR_ASSERT(xfc); + WINPR_ASSERT(event); + +@@ -1001,7 +1013,7 @@ static BOOL xf_event_PropertyNotify(xfContext* xfc, const XPropertyEvent* event, + appWindow = xf_AppWindowFromX11Window(xfc, event->window); + + if (!appWindow) +- return TRUE; ++ goto fail; + } + + if (event->atom == xfc->NET_WM_STATE) +@@ -1073,8 +1085,7 @@ static BOOL xf_event_PropertyNotify(xfContext* xfc, const XPropertyEvent* event, + if (appWindow->rail_state != WINDOW_SHOW_MAXIMIZED) + { + appWindow->rail_state = WINDOW_SHOW_MAXIMIZED; +- return xf_rail_send_client_system_command(xfc, appWindow->windowId, +- SC_MAXIMIZE); ++ rc = xf_rail_send_client_system_command(xfc, appWindow->windowId, SC_MAXIMIZE); + } + } + else if (appWindow->minimized) +@@ -1082,8 +1093,7 @@ static BOOL xf_event_PropertyNotify(xfContext* xfc, const XPropertyEvent* event, + if (appWindow->rail_state != WINDOW_SHOW_MINIMIZED) + { + appWindow->rail_state = WINDOW_SHOW_MINIMIZED; +- return xf_rail_send_client_system_command(xfc, appWindow->windowId, +- SC_MINIMIZE); ++ rc = xf_rail_send_client_system_command(xfc, appWindow->windowId, SC_MINIMIZE); + } + } + else +@@ -1091,15 +1101,18 @@ static BOOL xf_event_PropertyNotify(xfContext* xfc, const XPropertyEvent* event, + if (appWindow->rail_state != WINDOW_SHOW && appWindow->rail_state != WINDOW_HIDE) + { + appWindow->rail_state = WINDOW_SHOW; +- return xf_rail_send_client_system_command(xfc, appWindow->windowId, SC_RESTORE); ++ rc = xf_rail_send_client_system_command(xfc, appWindow->windowId, SC_RESTORE); + } + } + } + else if (minimizedChanged) +- gdi_send_suppress_output(xfc->common.context.gdi, minimized); ++ rc = gdi_send_suppress_output(xfc->common.context.gdi, minimized); ++ ++ fail: ++ xf_rail_return_window(appWindow); + } + +- return TRUE; ++ return rc; + } + + static BOOL xf_event_suppress_events(xfContext* xfc, xfAppWindow* appWindow, const XEvent* event) +@@ -1215,7 +1228,9 @@ BOOL xf_event_process(freerdp* instance, const XEvent* event) + /* Update "current" window for cursor change orders */ + xfc->appWindow = appWindow; + +- if (xf_event_suppress_events(xfc, appWindow, event)) ++ const BOOL rc = xf_event_suppress_events(xfc, appWindow, event); ++ xf_rail_return_window(appWindow); ++ if (rc) + return TRUE; + } + } +diff --git a/client/X11/xf_graphics.c b/client/X11/xf_graphics.c +index c61466b1a46b..51cbd0e40361 100644 +--- a/client/X11/xf_graphics.c ++++ b/client/X11/xf_graphics.c +@@ -253,12 +253,14 @@ static Window xf_Pointer_get_window(xfContext* xfc) + } + if (xfc->remote_app) + { ++ Window w = 0; ++ HashTable_Lock(xfc->railWindows); + if (!xfc->appWindow) +- { + WLog_WARN(TAG, "xf_Pointer: Invalid appWindow"); +- return 0; +- } +- return xfc->appWindow->handle; ++ else ++ w = xfc->appWindow->handle; ++ HashTable_Unlock(xfc->railWindows); ++ return w; + } + else + { +diff --git a/client/X11/xf_rail.c b/client/X11/xf_rail.c +index b09ef2a254a8..ecc81ca33aae 100644 +--- a/client/X11/xf_rail.c ++++ b/client/X11/xf_rail.c +@@ -159,6 +159,7 @@ void xf_rail_send_activate(xfContext* xfc, Window xwindow, BOOL enabled) + activate.windowId = (UINT32)appWindow->windowId; + activate.enabled = enabled; + xfc->rail->ClientActivate(xfc->rail, &activate); ++ xf_rail_return_window(appWindow); + } + + BOOL xf_rail_send_client_system_command(xfContext* xfc, UINT64 windowId, UINT16 command) +@@ -316,6 +317,7 @@ BOOL xf_rail_paint_surface(xfContext* xfc, UINT64 windowId, const RECTANGLE_16* + updateRect.right - updateRect.left, updateRect.bottom - updateRect.top); + } + region16_uninit(&windowInvalidRegion); ++ xf_rail_return_window(appWindow); + return TRUE; + } + +@@ -808,6 +810,7 @@ static void xf_rail_set_window_icon(xfContext* xfc, xfAppWindow* railWindow, xfR + static BOOL xf_rail_window_icon(rdpContext* context, const WINDOW_ORDER_INFO* orderInfo, + const WINDOW_ICON_ORDER* windowIcon) + { ++ BOOL rc = FALSE; + xfContext* xfc = (xfContext*)context; + BOOL replaceIcon = 0; + xfAppWindow* railWindow = xf_rail_get_window(xfc, orderInfo->windowId); +@@ -825,24 +828,26 @@ static BOOL xf_rail_window_icon(rdpContext* context, const WINDOW_ORDER_INFO* or + { + WLog_Print(xfc->log, WLOG_WARN, "failed to get icon from cache %02X:%04X", + windowIcon->iconInfo->cacheId, windowIcon->iconInfo->cacheEntry); +- return FALSE; + } +- +- if (!convert_rail_icon(windowIcon->iconInfo, icon)) ++ else if (!convert_rail_icon(windowIcon->iconInfo, icon)) + { + WLog_Print(xfc->log, WLOG_WARN, "failed to convert icon for window %08X", + orderInfo->windowId); +- return FALSE; + } +- +- replaceIcon = !!(orderInfo->fieldFlags & WINDOW_ORDER_STATE_NEW); +- xf_rail_set_window_icon(xfc, railWindow, icon, replaceIcon); +- return TRUE; ++ else ++ { ++ replaceIcon = !!(orderInfo->fieldFlags & WINDOW_ORDER_STATE_NEW); ++ xf_rail_set_window_icon(xfc, railWindow, icon, replaceIcon); ++ rc = TRUE; ++ } ++ xf_rail_return_window(railWindow); ++ return rc; + } + + static BOOL xf_rail_window_cached_icon(rdpContext* context, const WINDOW_ORDER_INFO* orderInfo, + const WINDOW_CACHED_ICON_ORDER* windowCachedIcon) + { ++ BOOL rc = FALSE; + xfContext* xfc = (xfContext*)context; + WINPR_ASSERT(orderInfo); + +@@ -862,12 +867,15 @@ static BOOL xf_rail_window_cached_icon(rdpContext* context, const WINDOW_ORDER_I + { + WLog_Print(xfc->log, WLOG_WARN, "failed to get icon from cache %02X:%04X", + windowCachedIcon->cachedIcon.cacheId, windowCachedIcon->cachedIcon.cacheEntry); +- return FALSE; + } +- +- replaceIcon = !!(orderInfo->fieldFlags & WINDOW_ORDER_STATE_NEW); +- xf_rail_set_window_icon(xfc, railWindow, icon, replaceIcon); +- return TRUE; ++ else ++ { ++ replaceIcon = !!(orderInfo->fieldFlags & WINDOW_ORDER_STATE_NEW); ++ xf_rail_set_window_icon(xfc, railWindow, icon, replaceIcon); ++ rc = TRUE; ++ } ++ xf_rail_return_window(railWindow); ++ return rc; + } + + static BOOL +@@ -1184,6 +1192,7 @@ static UINT xf_rail_server_local_move_size(RailClientContext* context, + else + xf_EndLocalMoveSize(xfc, appWindow); + ++ xf_rail_return_window(appWindow); + return CHANNEL_RC_OK; + } + +@@ -1208,6 +1217,7 @@ static UINT xf_rail_server_min_max_info(RailClientContext* context, + minMaxInfo->minTrackHeight, minMaxInfo->maxTrackWidth, + minMaxInfo->maxTrackHeight); + } ++ xf_rail_return_window(appWindow); + + return CHANNEL_RC_OK; + } +@@ -1381,7 +1391,20 @@ xfAppWindow* xf_rail_get_window(xfContext* xfc, UINT64 id) + return NULL; + + if (!xfc->railWindows) +- return FALSE; ++ return NULL; ++ ++ HashTable_Lock(xfc->railWindows); ++ xfAppWindow* window = HashTable_GetItemValue(xfc->railWindows, &id); ++ if (!window) ++ HashTable_Unlock(xfc->railWindows); ++ ++ return window; ++} ++ ++void xf_rail_return_window(xfAppWindow* window) ++{ ++ if (!window) ++ return; + +- return HashTable_GetItemValue(xfc->railWindows, &id); ++ HashTable_Unlock(window->xfc->railWindows); + } +diff --git a/client/X11/xf_rail.h b/client/X11/xf_rail.h +index 8d2fd704496c..0867093a24e0 100644 +--- a/client/X11/xf_rail.h ++++ b/client/X11/xf_rail.h +@@ -37,6 +37,10 @@ void xf_rail_disable_remoteapp_mode(xfContext* xfc); + + xfAppWindow* xf_rail_add_window(xfContext* xfc, UINT64 id, INT32 x, INT32 y, UINT32 width, + UINT32 height, UINT32 surfaceId); ++ ++void xf_rail_return_window(xfAppWindow* window); ++ ++WINPR_ATTR_MALLOC(xf_rail_return_window, 1) + xfAppWindow* xf_rail_get_window(xfContext* xfc, UINT64 id); + + BOOL xf_rail_del_window(xfContext* xfc, UINT64 id); +diff --git a/client/X11/xf_window.c b/client/X11/xf_window.c +index 9bffa66c505b..e276aea859b1 100644 +--- a/client/X11/xf_window.c ++++ b/client/X11/xf_window.c +@@ -1436,6 +1436,7 @@ xfAppWindow* xf_AppWindowFromX11Window(xfContext* xfc, Window wnd) + if (!xfc->railWindows) + return NULL; + ++ HashTable_Lock(xfc->railWindows); + size_t count = HashTable_GetKeys(xfc->railWindows, &pKeys); + + for (size_t index = 0; index < count; index++) +@@ -1444,17 +1445,20 @@ xfAppWindow* xf_AppWindowFromX11Window(xfContext* xfc, Window wnd) + + if (!appWindow) + { ++ HashTable_Unlock(xfc->railWindows); + free(pKeys); + return NULL; + } + + if (appWindow->handle == wnd) + { ++ HashTable_Unlock(xfc->railWindows); + free(pKeys); + return appWindow; + } + } + ++ HashTable_Unlock(xfc->railWindows); + free(pKeys); + return NULL; + } +@@ -1535,6 +1539,7 @@ UINT xf_AppUpdateWindowFromSurface(xfContext* xfc, gdiGfxSurface* surface) + + rc = CHANNEL_RC_OK; + fail: ++ xf_rail_return_window(appWindow); + LogDynAndXFlush(xfc->log, xfc->display); + xf_unlock_x11(xfc); + return rc; +@@ -1571,4 +1576,5 @@ void xf_XSetTransientForHint(xfContext* xfc, xfAppWindow* window) + return; + + (void)LogDynAndXSetTransientForHint(xfc->log, xfc->display, window->handle, parent->handle); ++ xf_rail_return_window(parent); + } +diff --git a/client/X11/xf_window.h b/client/X11/xf_window.h +index 8a41b4e47793..e258a9b261c4 100644 +--- a/client/X11/xf_window.h ++++ b/client/X11/xf_window.h +@@ -202,6 +202,10 @@ void xf_SetWindowMinMaxInfo(xfContext* xfc, xfAppWindow* appWindow, int maxWidth + int maxTrackWidth, int maxTrackHeight); + void xf_StartLocalMoveSize(xfContext* xfc, xfAppWindow* appWindow, int direction, int x, int y); + void xf_EndLocalMoveSize(xfContext* xfc, xfAppWindow* appWindow); ++ ++void xf_rail_return_window(xfAppWindow* window); ++ ++WINPR_ATTR_MALLOC(xf_rail_return_window, 1) + xfAppWindow* xf_AppWindowFromX11Window(xfContext* xfc, Window wnd); + + const char* window_styles_to_string(UINT32 style, char* buffer, size_t length); \ No newline at end of file diff --git a/debian/patches/CVE-2026-25953.patch b/debian/patches/CVE-2026-25953.patch new file mode 100644 index 0000000..ee894dd --- /dev/null +++ b/debian/patches/CVE-2026-25953.patch @@ -0,0 +1,408 @@ +From 1994e9844212a6dfe0ff12309fef520e888986b5 Mon Sep 17 00:00:00 2001 +From: Armin Novak +Date: Mon, 9 Feb 2026 16:09:51 +0100 +Subject: [PATCH] [client,x11] lock appWindow + +When using xf_rail_get_window lock the hash talbe until xf_rail_return_window +--- + client/X11/xf_event.c | 47 +++++++++++++++++++++++------------ + client/X11/xf_graphics.c | 10 +++++--- + client/X11/xf_rail.c | 53 ++++++++++++++++++++++++++++------------ + client/X11/xf_rail.h | 4 +++ + client/X11/xf_window.c | 6 +++++ + client/X11/xf_window.h | 4 +++ + 6 files changed, 89 insertions(+), 35 deletions(-) + +diff --git a/client/X11/xf_event.c b/client/X11/xf_event.c +index ceb0874aba35..6e2f513987cb 100644 +--- a/client/X11/xf_event.c ++++ b/client/X11/xf_event.c +@@ -441,7 +441,9 @@ BOOL xf_generic_MotionNotify_(xfContext* xfc, int x, int y, Window window, BOOL + if (app) + { + /* make sure window exists */ +- if (!xf_AppWindowFromX11Window(xfc, window)) ++ xfAppWindow* appWindow = xf_AppWindowFromX11Window(xfc, window); ++ xf_rail_return_window(appWindow); ++ if (!appWindow) + return TRUE; + + /* Translate to desktop coordinates */ +@@ -547,7 +549,9 @@ BOOL xf_generic_ButtonEvent_(xfContext* xfc, int x, int y, int button, Window wi + if (app) + { + /* make sure window exists */ +- if (!xf_AppWindowFromX11Window(xfc, window)) ++ xfAppWindow* appWindow = xf_AppWindowFromX11Window(xfc, window); ++ xf_rail_return_window(appWindow); ++ if (!appWindow) + return TRUE; + + /* Translate to desktop coordinates */ +@@ -708,6 +712,7 @@ static BOOL xf_event_FocusIn(xfContext* xfc, const XFocusInEvent* event, BOOL ap + */ + if (appWindow) + xf_rail_adjust_position(xfc, appWindow); ++ xf_rail_return_window(appWindow); + } + + xf_keyboard_focus_in(xfc); +@@ -762,12 +767,13 @@ static BOOL xf_event_ClientMessage(xfContext* xfc, const XClientMessageEvent* ev + { + if (app) + { ++ BOOL rc = TRUE; + xfAppWindow* appWindow = xf_AppWindowFromX11Window(xfc, event->window); + + if (appWindow) +- return xf_rail_send_client_system_command(xfc, appWindow->windowId, SC_CLOSE); +- +- return TRUE; ++ rc = xf_rail_send_client_system_command(xfc, appWindow->windowId, SC_CLOSE); ++ xf_rail_return_window(appWindow); ++ return rc; + } + else + { +@@ -800,6 +806,7 @@ static BOOL xf_event_EnterNotify(xfContext* xfc, const XEnterWindowEvent* event, + + /* keep track of which window has focus so that we can apply pointer updates */ + xfc->appWindow = appWindow; ++ xf_rail_return_window(appWindow); + } + + return TRUE; +@@ -821,6 +828,7 @@ static BOOL xf_event_LeaveNotify(xfContext* xfc, const XLeaveWindowEvent* event, + /* keep track of which window has focus so that we can apply pointer updates */ + if (xfc->appWindow == appWindow) + xfc->appWindow = NULL; ++ xf_rail_return_window(appWindow); + } + return TRUE; + } +@@ -926,6 +934,7 @@ static BOOL xf_event_ConfigureNotify(xfContext* xfc, const XConfigureEvent* even + xf_rail_adjust_position(xfc, appWindow); + } + } ++ xf_rail_return_window(appWindow); + } + return xf_pointer_update_scale(xfc); + } +@@ -949,6 +958,7 @@ static BOOL xf_event_MapNotify(xfContext* xfc, const XMapEvent* event, BOOL app) + // xf_rail_send_client_system_command(xfc, appWindow->windowId, SC_RESTORE); + appWindow->is_mapped = TRUE; + } ++ xf_rail_return_window(appWindow); + } + + return TRUE; +@@ -963,13 +973,14 @@ static BOOL xf_event_UnmapNotify(xfContext* xfc, const XUnmapEvent* event, BOOL + xf_keyboard_release_all_keypress(xfc); + + if (!app) +- gdi_send_suppress_output(xfc->common.context.gdi, TRUE); +- else ++ return gdi_send_suppress_output(xfc->common.context.gdi, TRUE); ++ + { + xfAppWindow* appWindow = xf_AppWindowFromX11Window(xfc, event->window); + + if (appWindow) + appWindow->is_mapped = FALSE; ++ xf_rail_return_window(appWindow); + } + + return TRUE; +@@ -977,6 +988,7 @@ static BOOL xf_event_UnmapNotify(xfContext* xfc, const XUnmapEvent* event, BOOL + + static BOOL xf_event_PropertyNotify(xfContext* xfc, const XPropertyEvent* event, BOOL app) + { ++ BOOL rc = TRUE; + WINPR_ASSERT(xfc); + WINPR_ASSERT(event); + +@@ -1001,7 +1013,7 @@ static BOOL xf_event_PropertyNotify(xfContext* xfc, const XPropertyEvent* event, + appWindow = xf_AppWindowFromX11Window(xfc, event->window); + + if (!appWindow) +- return TRUE; ++ goto fail; + } + + if (event->atom == xfc->NET_WM_STATE) +@@ -1073,8 +1085,7 @@ static BOOL xf_event_PropertyNotify(xfContext* xfc, const XPropertyEvent* event, + if (appWindow->rail_state != WINDOW_SHOW_MAXIMIZED) + { + appWindow->rail_state = WINDOW_SHOW_MAXIMIZED; +- return xf_rail_send_client_system_command(xfc, appWindow->windowId, +- SC_MAXIMIZE); ++ rc = xf_rail_send_client_system_command(xfc, appWindow->windowId, SC_MAXIMIZE); + } + } + else if (appWindow->minimized) +@@ -1082,8 +1093,7 @@ static BOOL xf_event_PropertyNotify(xfContext* xfc, const XPropertyEvent* event, + if (appWindow->rail_state != WINDOW_SHOW_MINIMIZED) + { + appWindow->rail_state = WINDOW_SHOW_MINIMIZED; +- return xf_rail_send_client_system_command(xfc, appWindow->windowId, +- SC_MINIMIZE); ++ rc = xf_rail_send_client_system_command(xfc, appWindow->windowId, SC_MINIMIZE); + } + } + else +@@ -1091,15 +1101,18 @@ static BOOL xf_event_PropertyNotify(xfContext* xfc, const XPropertyEvent* event, + if (appWindow->rail_state != WINDOW_SHOW && appWindow->rail_state != WINDOW_HIDE) + { + appWindow->rail_state = WINDOW_SHOW; +- return xf_rail_send_client_system_command(xfc, appWindow->windowId, SC_RESTORE); ++ rc = xf_rail_send_client_system_command(xfc, appWindow->windowId, SC_RESTORE); + } + } + } + else if (minimizedChanged) +- gdi_send_suppress_output(xfc->common.context.gdi, minimized); ++ rc = gdi_send_suppress_output(xfc->common.context.gdi, minimized); ++ ++ fail: ++ xf_rail_return_window(appWindow); + } + +- return TRUE; ++ return rc; + } + + static BOOL xf_event_suppress_events(xfContext* xfc, xfAppWindow* appWindow, const XEvent* event) +@@ -1215,7 +1228,9 @@ BOOL xf_event_process(freerdp* instance, const XEvent* event) + /* Update "current" window for cursor change orders */ + xfc->appWindow = appWindow; + +- if (xf_event_suppress_events(xfc, appWindow, event)) ++ const BOOL rc = xf_event_suppress_events(xfc, appWindow, event); ++ xf_rail_return_window(appWindow); ++ if (rc) + return TRUE; + } + } +diff --git a/client/X11/xf_graphics.c b/client/X11/xf_graphics.c +index c61466b1a46b..51cbd0e40361 100644 +--- a/client/X11/xf_graphics.c ++++ b/client/X11/xf_graphics.c +@@ -253,12 +253,14 @@ static Window xf_Pointer_get_window(xfContext* xfc) + } + if (xfc->remote_app) + { ++ Window w = 0; ++ HashTable_Lock(xfc->railWindows); + if (!xfc->appWindow) +- { + WLog_WARN(TAG, "xf_Pointer: Invalid appWindow"); +- return 0; +- } +- return xfc->appWindow->handle; ++ else ++ w = xfc->appWindow->handle; ++ HashTable_Unlock(xfc->railWindows); ++ return w; + } + else + { +diff --git a/client/X11/xf_rail.c b/client/X11/xf_rail.c +index b09ef2a254a8..ecc81ca33aae 100644 +--- a/client/X11/xf_rail.c ++++ b/client/X11/xf_rail.c +@@ -159,6 +159,7 @@ void xf_rail_send_activate(xfContext* xfc, Window xwindow, BOOL enabled) + activate.windowId = (UINT32)appWindow->windowId; + activate.enabled = enabled; + xfc->rail->ClientActivate(xfc->rail, &activate); ++ xf_rail_return_window(appWindow); + } + + BOOL xf_rail_send_client_system_command(xfContext* xfc, UINT64 windowId, UINT16 command) +@@ -316,6 +317,7 @@ BOOL xf_rail_paint_surface(xfContext* xfc, UINT64 windowId, const RECTANGLE_16* + updateRect.right - updateRect.left, updateRect.bottom - updateRect.top); + } + region16_uninit(&windowInvalidRegion); ++ xf_rail_return_window(appWindow); + return TRUE; + } + +@@ -808,6 +810,7 @@ static void xf_rail_set_window_icon(xfContext* xfc, xfAppWindow* railWindow, xfR + static BOOL xf_rail_window_icon(rdpContext* context, const WINDOW_ORDER_INFO* orderInfo, + const WINDOW_ICON_ORDER* windowIcon) + { ++ BOOL rc = FALSE; + xfContext* xfc = (xfContext*)context; + BOOL replaceIcon = 0; + xfAppWindow* railWindow = xf_rail_get_window(xfc, orderInfo->windowId); +@@ -825,24 +828,26 @@ static BOOL xf_rail_window_icon(rdpContext* context, const WINDOW_ORDER_INFO* or + { + WLog_Print(xfc->log, WLOG_WARN, "failed to get icon from cache %02X:%04X", + windowIcon->iconInfo->cacheId, windowIcon->iconInfo->cacheEntry); +- return FALSE; + } +- +- if (!convert_rail_icon(windowIcon->iconInfo, icon)) ++ else if (!convert_rail_icon(windowIcon->iconInfo, icon)) + { + WLog_Print(xfc->log, WLOG_WARN, "failed to convert icon for window %08X", + orderInfo->windowId); +- return FALSE; + } +- +- replaceIcon = !!(orderInfo->fieldFlags & WINDOW_ORDER_STATE_NEW); +- xf_rail_set_window_icon(xfc, railWindow, icon, replaceIcon); +- return TRUE; ++ else ++ { ++ replaceIcon = !!(orderInfo->fieldFlags & WINDOW_ORDER_STATE_NEW); ++ xf_rail_set_window_icon(xfc, railWindow, icon, replaceIcon); ++ rc = TRUE; ++ } ++ xf_rail_return_window(railWindow); ++ return rc; + } + + static BOOL xf_rail_window_cached_icon(rdpContext* context, const WINDOW_ORDER_INFO* orderInfo, + const WINDOW_CACHED_ICON_ORDER* windowCachedIcon) + { ++ BOOL rc = FALSE; + xfContext* xfc = (xfContext*)context; + WINPR_ASSERT(orderInfo); + +@@ -862,12 +867,15 @@ static BOOL xf_rail_window_cached_icon(rdpContext* context, const WINDOW_ORDER_I + { + WLog_Print(xfc->log, WLOG_WARN, "failed to get icon from cache %02X:%04X", + windowCachedIcon->cachedIcon.cacheId, windowCachedIcon->cachedIcon.cacheEntry); +- return FALSE; + } +- +- replaceIcon = !!(orderInfo->fieldFlags & WINDOW_ORDER_STATE_NEW); +- xf_rail_set_window_icon(xfc, railWindow, icon, replaceIcon); +- return TRUE; ++ else ++ { ++ replaceIcon = !!(orderInfo->fieldFlags & WINDOW_ORDER_STATE_NEW); ++ xf_rail_set_window_icon(xfc, railWindow, icon, replaceIcon); ++ rc = TRUE; ++ } ++ xf_rail_return_window(railWindow); ++ return rc; + } + + static BOOL +@@ -1184,6 +1192,7 @@ static UINT xf_rail_server_local_move_size(RailClientContext* context, + else + xf_EndLocalMoveSize(xfc, appWindow); + ++ xf_rail_return_window(appWindow); + return CHANNEL_RC_OK; + } + +@@ -1208,6 +1217,7 @@ static UINT xf_rail_server_min_max_info(RailClientContext* context, + minMaxInfo->minTrackHeight, minMaxInfo->maxTrackWidth, + minMaxInfo->maxTrackHeight); + } ++ xf_rail_return_window(appWindow); + + return CHANNEL_RC_OK; + } +@@ -1381,7 +1391,20 @@ xfAppWindow* xf_rail_get_window(xfContext* xfc, UINT64 id) + return NULL; + + if (!xfc->railWindows) +- return FALSE; ++ return NULL; ++ ++ HashTable_Lock(xfc->railWindows); ++ xfAppWindow* window = HashTable_GetItemValue(xfc->railWindows, &id); ++ if (!window) ++ HashTable_Unlock(xfc->railWindows); ++ ++ return window; ++} ++ ++void xf_rail_return_window(xfAppWindow* window) ++{ ++ if (!window) ++ return; + +- return HashTable_GetItemValue(xfc->railWindows, &id); ++ HashTable_Unlock(window->xfc->railWindows); + } +diff --git a/client/X11/xf_rail.h b/client/X11/xf_rail.h +index 8d2fd704496c..0867093a24e0 100644 +--- a/client/X11/xf_rail.h ++++ b/client/X11/xf_rail.h +@@ -37,6 +37,10 @@ void xf_rail_disable_remoteapp_mode(xfContext* xfc); + + xfAppWindow* xf_rail_add_window(xfContext* xfc, UINT64 id, INT32 x, INT32 y, UINT32 width, + UINT32 height, UINT32 surfaceId); ++ ++void xf_rail_return_window(xfAppWindow* window); ++ ++WINPR_ATTR_MALLOC(xf_rail_return_window, 1) + xfAppWindow* xf_rail_get_window(xfContext* xfc, UINT64 id); + + BOOL xf_rail_del_window(xfContext* xfc, UINT64 id); +diff --git a/client/X11/xf_window.c b/client/X11/xf_window.c +index 9bffa66c505b..e276aea859b1 100644 +--- a/client/X11/xf_window.c ++++ b/client/X11/xf_window.c +@@ -1436,6 +1436,7 @@ xfAppWindow* xf_AppWindowFromX11Window(xfContext* xfc, Window wnd) + if (!xfc->railWindows) + return NULL; + ++ HashTable_Lock(xfc->railWindows); + size_t count = HashTable_GetKeys(xfc->railWindows, &pKeys); + + for (size_t index = 0; index < count; index++) +@@ -1444,17 +1445,20 @@ xfAppWindow* xf_AppWindowFromX11Window(xfContext* xfc, Window wnd) + + if (!appWindow) + { ++ HashTable_Unlock(xfc->railWindows); + free(pKeys); + return NULL; + } + + if (appWindow->handle == wnd) + { ++ HashTable_Unlock(xfc->railWindows); + free(pKeys); + return appWindow; + } + } + ++ HashTable_Unlock(xfc->railWindows); + free(pKeys); + return NULL; + } +@@ -1535,6 +1539,7 @@ UINT xf_AppUpdateWindowFromSurface(xfContext* xfc, gdiGfxSurface* surface) + + rc = CHANNEL_RC_OK; + fail: ++ xf_rail_return_window(appWindow); + LogDynAndXFlush(xfc->log, xfc->display); + xf_unlock_x11(xfc); + return rc; +@@ -1571,4 +1576,5 @@ void xf_XSetTransientForHint(xfContext* xfc, xfAppWindow* window) + return; + + (void)LogDynAndXSetTransientForHint(xfc->log, xfc->display, window->handle, parent->handle); ++ xf_rail_return_window(parent); + } +diff --git a/client/X11/xf_window.h b/client/X11/xf_window.h +index 8a41b4e47793..e258a9b261c4 100644 +--- a/client/X11/xf_window.h ++++ b/client/X11/xf_window.h +@@ -202,6 +202,10 @@ void xf_SetWindowMinMaxInfo(xfContext* xfc, xfAppWindow* appWindow, int maxWidth + int maxTrackWidth, int maxTrackHeight); + void xf_StartLocalMoveSize(xfContext* xfc, xfAppWindow* appWindow, int direction, int x, int y); + void xf_EndLocalMoveSize(xfContext* xfc, xfAppWindow* appWindow); ++ ++void xf_rail_return_window(xfAppWindow* window); ++ ++WINPR_ATTR_MALLOC(xf_rail_return_window, 1) + xfAppWindow* xf_AppWindowFromX11Window(xfContext* xfc, Window wnd); + + const char* window_styles_to_string(UINT32 style, char* buffer, size_t length); \ No newline at end of file diff --git a/debian/patches/CVE-2026-25954.patch b/debian/patches/CVE-2026-25954.patch new file mode 100644 index 0000000..ee894dd --- /dev/null +++ b/debian/patches/CVE-2026-25954.patch @@ -0,0 +1,408 @@ +From 1994e9844212a6dfe0ff12309fef520e888986b5 Mon Sep 17 00:00:00 2001 +From: Armin Novak +Date: Mon, 9 Feb 2026 16:09:51 +0100 +Subject: [PATCH] [client,x11] lock appWindow + +When using xf_rail_get_window lock the hash talbe until xf_rail_return_window +--- + client/X11/xf_event.c | 47 +++++++++++++++++++++++------------ + client/X11/xf_graphics.c | 10 +++++--- + client/X11/xf_rail.c | 53 ++++++++++++++++++++++++++++------------ + client/X11/xf_rail.h | 4 +++ + client/X11/xf_window.c | 6 +++++ + client/X11/xf_window.h | 4 +++ + 6 files changed, 89 insertions(+), 35 deletions(-) + +diff --git a/client/X11/xf_event.c b/client/X11/xf_event.c +index ceb0874aba35..6e2f513987cb 100644 +--- a/client/X11/xf_event.c ++++ b/client/X11/xf_event.c +@@ -441,7 +441,9 @@ BOOL xf_generic_MotionNotify_(xfContext* xfc, int x, int y, Window window, BOOL + if (app) + { + /* make sure window exists */ +- if (!xf_AppWindowFromX11Window(xfc, window)) ++ xfAppWindow* appWindow = xf_AppWindowFromX11Window(xfc, window); ++ xf_rail_return_window(appWindow); ++ if (!appWindow) + return TRUE; + + /* Translate to desktop coordinates */ +@@ -547,7 +549,9 @@ BOOL xf_generic_ButtonEvent_(xfContext* xfc, int x, int y, int button, Window wi + if (app) + { + /* make sure window exists */ +- if (!xf_AppWindowFromX11Window(xfc, window)) ++ xfAppWindow* appWindow = xf_AppWindowFromX11Window(xfc, window); ++ xf_rail_return_window(appWindow); ++ if (!appWindow) + return TRUE; + + /* Translate to desktop coordinates */ +@@ -708,6 +712,7 @@ static BOOL xf_event_FocusIn(xfContext* xfc, const XFocusInEvent* event, BOOL ap + */ + if (appWindow) + xf_rail_adjust_position(xfc, appWindow); ++ xf_rail_return_window(appWindow); + } + + xf_keyboard_focus_in(xfc); +@@ -762,12 +767,13 @@ static BOOL xf_event_ClientMessage(xfContext* xfc, const XClientMessageEvent* ev + { + if (app) + { ++ BOOL rc = TRUE; + xfAppWindow* appWindow = xf_AppWindowFromX11Window(xfc, event->window); + + if (appWindow) +- return xf_rail_send_client_system_command(xfc, appWindow->windowId, SC_CLOSE); +- +- return TRUE; ++ rc = xf_rail_send_client_system_command(xfc, appWindow->windowId, SC_CLOSE); ++ xf_rail_return_window(appWindow); ++ return rc; + } + else + { +@@ -800,6 +806,7 @@ static BOOL xf_event_EnterNotify(xfContext* xfc, const XEnterWindowEvent* event, + + /* keep track of which window has focus so that we can apply pointer updates */ + xfc->appWindow = appWindow; ++ xf_rail_return_window(appWindow); + } + + return TRUE; +@@ -821,6 +828,7 @@ static BOOL xf_event_LeaveNotify(xfContext* xfc, const XLeaveWindowEvent* event, + /* keep track of which window has focus so that we can apply pointer updates */ + if (xfc->appWindow == appWindow) + xfc->appWindow = NULL; ++ xf_rail_return_window(appWindow); + } + return TRUE; + } +@@ -926,6 +934,7 @@ static BOOL xf_event_ConfigureNotify(xfContext* xfc, const XConfigureEvent* even + xf_rail_adjust_position(xfc, appWindow); + } + } ++ xf_rail_return_window(appWindow); + } + return xf_pointer_update_scale(xfc); + } +@@ -949,6 +958,7 @@ static BOOL xf_event_MapNotify(xfContext* xfc, const XMapEvent* event, BOOL app) + // xf_rail_send_client_system_command(xfc, appWindow->windowId, SC_RESTORE); + appWindow->is_mapped = TRUE; + } ++ xf_rail_return_window(appWindow); + } + + return TRUE; +@@ -963,13 +973,14 @@ static BOOL xf_event_UnmapNotify(xfContext* xfc, const XUnmapEvent* event, BOOL + xf_keyboard_release_all_keypress(xfc); + + if (!app) +- gdi_send_suppress_output(xfc->common.context.gdi, TRUE); +- else ++ return gdi_send_suppress_output(xfc->common.context.gdi, TRUE); ++ + { + xfAppWindow* appWindow = xf_AppWindowFromX11Window(xfc, event->window); + + if (appWindow) + appWindow->is_mapped = FALSE; ++ xf_rail_return_window(appWindow); + } + + return TRUE; +@@ -977,6 +988,7 @@ static BOOL xf_event_UnmapNotify(xfContext* xfc, const XUnmapEvent* event, BOOL + + static BOOL xf_event_PropertyNotify(xfContext* xfc, const XPropertyEvent* event, BOOL app) + { ++ BOOL rc = TRUE; + WINPR_ASSERT(xfc); + WINPR_ASSERT(event); + +@@ -1001,7 +1013,7 @@ static BOOL xf_event_PropertyNotify(xfContext* xfc, const XPropertyEvent* event, + appWindow = xf_AppWindowFromX11Window(xfc, event->window); + + if (!appWindow) +- return TRUE; ++ goto fail; + } + + if (event->atom == xfc->NET_WM_STATE) +@@ -1073,8 +1085,7 @@ static BOOL xf_event_PropertyNotify(xfContext* xfc, const XPropertyEvent* event, + if (appWindow->rail_state != WINDOW_SHOW_MAXIMIZED) + { + appWindow->rail_state = WINDOW_SHOW_MAXIMIZED; +- return xf_rail_send_client_system_command(xfc, appWindow->windowId, +- SC_MAXIMIZE); ++ rc = xf_rail_send_client_system_command(xfc, appWindow->windowId, SC_MAXIMIZE); + } + } + else if (appWindow->minimized) +@@ -1082,8 +1093,7 @@ static BOOL xf_event_PropertyNotify(xfContext* xfc, const XPropertyEvent* event, + if (appWindow->rail_state != WINDOW_SHOW_MINIMIZED) + { + appWindow->rail_state = WINDOW_SHOW_MINIMIZED; +- return xf_rail_send_client_system_command(xfc, appWindow->windowId, +- SC_MINIMIZE); ++ rc = xf_rail_send_client_system_command(xfc, appWindow->windowId, SC_MINIMIZE); + } + } + else +@@ -1091,15 +1101,18 @@ static BOOL xf_event_PropertyNotify(xfContext* xfc, const XPropertyEvent* event, + if (appWindow->rail_state != WINDOW_SHOW && appWindow->rail_state != WINDOW_HIDE) + { + appWindow->rail_state = WINDOW_SHOW; +- return xf_rail_send_client_system_command(xfc, appWindow->windowId, SC_RESTORE); ++ rc = xf_rail_send_client_system_command(xfc, appWindow->windowId, SC_RESTORE); + } + } + } + else if (minimizedChanged) +- gdi_send_suppress_output(xfc->common.context.gdi, minimized); ++ rc = gdi_send_suppress_output(xfc->common.context.gdi, minimized); ++ ++ fail: ++ xf_rail_return_window(appWindow); + } + +- return TRUE; ++ return rc; + } + + static BOOL xf_event_suppress_events(xfContext* xfc, xfAppWindow* appWindow, const XEvent* event) +@@ -1215,7 +1228,9 @@ BOOL xf_event_process(freerdp* instance, const XEvent* event) + /* Update "current" window for cursor change orders */ + xfc->appWindow = appWindow; + +- if (xf_event_suppress_events(xfc, appWindow, event)) ++ const BOOL rc = xf_event_suppress_events(xfc, appWindow, event); ++ xf_rail_return_window(appWindow); ++ if (rc) + return TRUE; + } + } +diff --git a/client/X11/xf_graphics.c b/client/X11/xf_graphics.c +index c61466b1a46b..51cbd0e40361 100644 +--- a/client/X11/xf_graphics.c ++++ b/client/X11/xf_graphics.c +@@ -253,12 +253,14 @@ static Window xf_Pointer_get_window(xfContext* xfc) + } + if (xfc->remote_app) + { ++ Window w = 0; ++ HashTable_Lock(xfc->railWindows); + if (!xfc->appWindow) +- { + WLog_WARN(TAG, "xf_Pointer: Invalid appWindow"); +- return 0; +- } +- return xfc->appWindow->handle; ++ else ++ w = xfc->appWindow->handle; ++ HashTable_Unlock(xfc->railWindows); ++ return w; + } + else + { +diff --git a/client/X11/xf_rail.c b/client/X11/xf_rail.c +index b09ef2a254a8..ecc81ca33aae 100644 +--- a/client/X11/xf_rail.c ++++ b/client/X11/xf_rail.c +@@ -159,6 +159,7 @@ void xf_rail_send_activate(xfContext* xfc, Window xwindow, BOOL enabled) + activate.windowId = (UINT32)appWindow->windowId; + activate.enabled = enabled; + xfc->rail->ClientActivate(xfc->rail, &activate); ++ xf_rail_return_window(appWindow); + } + + BOOL xf_rail_send_client_system_command(xfContext* xfc, UINT64 windowId, UINT16 command) +@@ -316,6 +317,7 @@ BOOL xf_rail_paint_surface(xfContext* xfc, UINT64 windowId, const RECTANGLE_16* + updateRect.right - updateRect.left, updateRect.bottom - updateRect.top); + } + region16_uninit(&windowInvalidRegion); ++ xf_rail_return_window(appWindow); + return TRUE; + } + +@@ -808,6 +810,7 @@ static void xf_rail_set_window_icon(xfContext* xfc, xfAppWindow* railWindow, xfR + static BOOL xf_rail_window_icon(rdpContext* context, const WINDOW_ORDER_INFO* orderInfo, + const WINDOW_ICON_ORDER* windowIcon) + { ++ BOOL rc = FALSE; + xfContext* xfc = (xfContext*)context; + BOOL replaceIcon = 0; + xfAppWindow* railWindow = xf_rail_get_window(xfc, orderInfo->windowId); +@@ -825,24 +828,26 @@ static BOOL xf_rail_window_icon(rdpContext* context, const WINDOW_ORDER_INFO* or + { + WLog_Print(xfc->log, WLOG_WARN, "failed to get icon from cache %02X:%04X", + windowIcon->iconInfo->cacheId, windowIcon->iconInfo->cacheEntry); +- return FALSE; + } +- +- if (!convert_rail_icon(windowIcon->iconInfo, icon)) ++ else if (!convert_rail_icon(windowIcon->iconInfo, icon)) + { + WLog_Print(xfc->log, WLOG_WARN, "failed to convert icon for window %08X", + orderInfo->windowId); +- return FALSE; + } +- +- replaceIcon = !!(orderInfo->fieldFlags & WINDOW_ORDER_STATE_NEW); +- xf_rail_set_window_icon(xfc, railWindow, icon, replaceIcon); +- return TRUE; ++ else ++ { ++ replaceIcon = !!(orderInfo->fieldFlags & WINDOW_ORDER_STATE_NEW); ++ xf_rail_set_window_icon(xfc, railWindow, icon, replaceIcon); ++ rc = TRUE; ++ } ++ xf_rail_return_window(railWindow); ++ return rc; + } + + static BOOL xf_rail_window_cached_icon(rdpContext* context, const WINDOW_ORDER_INFO* orderInfo, + const WINDOW_CACHED_ICON_ORDER* windowCachedIcon) + { ++ BOOL rc = FALSE; + xfContext* xfc = (xfContext*)context; + WINPR_ASSERT(orderInfo); + +@@ -862,12 +867,15 @@ static BOOL xf_rail_window_cached_icon(rdpContext* context, const WINDOW_ORDER_I + { + WLog_Print(xfc->log, WLOG_WARN, "failed to get icon from cache %02X:%04X", + windowCachedIcon->cachedIcon.cacheId, windowCachedIcon->cachedIcon.cacheEntry); +- return FALSE; + } +- +- replaceIcon = !!(orderInfo->fieldFlags & WINDOW_ORDER_STATE_NEW); +- xf_rail_set_window_icon(xfc, railWindow, icon, replaceIcon); +- return TRUE; ++ else ++ { ++ replaceIcon = !!(orderInfo->fieldFlags & WINDOW_ORDER_STATE_NEW); ++ xf_rail_set_window_icon(xfc, railWindow, icon, replaceIcon); ++ rc = TRUE; ++ } ++ xf_rail_return_window(railWindow); ++ return rc; + } + + static BOOL +@@ -1184,6 +1192,7 @@ static UINT xf_rail_server_local_move_size(RailClientContext* context, + else + xf_EndLocalMoveSize(xfc, appWindow); + ++ xf_rail_return_window(appWindow); + return CHANNEL_RC_OK; + } + +@@ -1208,6 +1217,7 @@ static UINT xf_rail_server_min_max_info(RailClientContext* context, + minMaxInfo->minTrackHeight, minMaxInfo->maxTrackWidth, + minMaxInfo->maxTrackHeight); + } ++ xf_rail_return_window(appWindow); + + return CHANNEL_RC_OK; + } +@@ -1381,7 +1391,20 @@ xfAppWindow* xf_rail_get_window(xfContext* xfc, UINT64 id) + return NULL; + + if (!xfc->railWindows) +- return FALSE; ++ return NULL; ++ ++ HashTable_Lock(xfc->railWindows); ++ xfAppWindow* window = HashTable_GetItemValue(xfc->railWindows, &id); ++ if (!window) ++ HashTable_Unlock(xfc->railWindows); ++ ++ return window; ++} ++ ++void xf_rail_return_window(xfAppWindow* window) ++{ ++ if (!window) ++ return; + +- return HashTable_GetItemValue(xfc->railWindows, &id); ++ HashTable_Unlock(window->xfc->railWindows); + } +diff --git a/client/X11/xf_rail.h b/client/X11/xf_rail.h +index 8d2fd704496c..0867093a24e0 100644 +--- a/client/X11/xf_rail.h ++++ b/client/X11/xf_rail.h +@@ -37,6 +37,10 @@ void xf_rail_disable_remoteapp_mode(xfContext* xfc); + + xfAppWindow* xf_rail_add_window(xfContext* xfc, UINT64 id, INT32 x, INT32 y, UINT32 width, + UINT32 height, UINT32 surfaceId); ++ ++void xf_rail_return_window(xfAppWindow* window); ++ ++WINPR_ATTR_MALLOC(xf_rail_return_window, 1) + xfAppWindow* xf_rail_get_window(xfContext* xfc, UINT64 id); + + BOOL xf_rail_del_window(xfContext* xfc, UINT64 id); +diff --git a/client/X11/xf_window.c b/client/X11/xf_window.c +index 9bffa66c505b..e276aea859b1 100644 +--- a/client/X11/xf_window.c ++++ b/client/X11/xf_window.c +@@ -1436,6 +1436,7 @@ xfAppWindow* xf_AppWindowFromX11Window(xfContext* xfc, Window wnd) + if (!xfc->railWindows) + return NULL; + ++ HashTable_Lock(xfc->railWindows); + size_t count = HashTable_GetKeys(xfc->railWindows, &pKeys); + + for (size_t index = 0; index < count; index++) +@@ -1444,17 +1445,20 @@ xfAppWindow* xf_AppWindowFromX11Window(xfContext* xfc, Window wnd) + + if (!appWindow) + { ++ HashTable_Unlock(xfc->railWindows); + free(pKeys); + return NULL; + } + + if (appWindow->handle == wnd) + { ++ HashTable_Unlock(xfc->railWindows); + free(pKeys); + return appWindow; + } + } + ++ HashTable_Unlock(xfc->railWindows); + free(pKeys); + return NULL; + } +@@ -1535,6 +1539,7 @@ UINT xf_AppUpdateWindowFromSurface(xfContext* xfc, gdiGfxSurface* surface) + + rc = CHANNEL_RC_OK; + fail: ++ xf_rail_return_window(appWindow); + LogDynAndXFlush(xfc->log, xfc->display); + xf_unlock_x11(xfc); + return rc; +@@ -1571,4 +1576,5 @@ void xf_XSetTransientForHint(xfContext* xfc, xfAppWindow* window) + return; + + (void)LogDynAndXSetTransientForHint(xfc->log, xfc->display, window->handle, parent->handle); ++ xf_rail_return_window(parent); + } +diff --git a/client/X11/xf_window.h b/client/X11/xf_window.h +index 8a41b4e47793..e258a9b261c4 100644 +--- a/client/X11/xf_window.h ++++ b/client/X11/xf_window.h +@@ -202,6 +202,10 @@ void xf_SetWindowMinMaxInfo(xfContext* xfc, xfAppWindow* appWindow, int maxWidth + int maxTrackWidth, int maxTrackHeight); + void xf_StartLocalMoveSize(xfContext* xfc, xfAppWindow* appWindow, int direction, int x, int y); + void xf_EndLocalMoveSize(xfContext* xfc, xfAppWindow* appWindow); ++ ++void xf_rail_return_window(xfAppWindow* window); ++ ++WINPR_ATTR_MALLOC(xf_rail_return_window, 1) + xfAppWindow* xf_AppWindowFromX11Window(xfContext* xfc, Window wnd); + + const char* window_styles_to_string(UINT32 style, char* buffer, size_t length); \ No newline at end of file diff --git a/debian/patches/CVE-2026-25955.patch b/debian/patches/CVE-2026-25955.patch new file mode 100644 index 0000000..d8458d4 --- /dev/null +++ b/debian/patches/CVE-2026-25955.patch @@ -0,0 +1,101 @@ +From 169d358734509e82663a0d6a0085ae726d439d8e Mon Sep 17 00:00:00 2001 +From: Armin Novak +Date: Mon, 9 Feb 2026 16:58:20 +0100 +Subject: [PATCH] [client,x11] destroy XImage on window unmap + +When unmapping rails window destroy the cached XImage of appWindow +--- + client/X11/xf_gfx.c | 24 ++++++++++++++++++++++++ + client/X11/xf_window.c | 2 +- + client/X11/xf_window.h | 6 +++++- + 3 files changed, 30 insertions(+), 2 deletions(-) + +diff --git a/client/X11/xf_gfx.c b/client/X11/xf_gfx.c +index 37d0bb17b457..b6de7eb86b35 100644 +--- a/client/X11/xf_gfx.c ++++ b/client/X11/xf_gfx.c +@@ -30,6 +30,7 @@ + #include "xf_gfx.h" + #include "xf_rail.h" + #include "xf_utils.h" ++#include "xf_window.h" + + #include + +@@ -444,6 +445,27 @@ static UINT xf_DeleteSurface(RdpgfxClientContext* context, + return status; + } + ++static UINT xf_UnmapWindowForSurface(RdpgfxClientContext* context, UINT64 windowID) ++{ ++ WINPR_ASSERT(context); ++ rdpGdi* gdi = (rdpGdi*)context->custom; ++ WINPR_ASSERT(gdi); ++ ++ xfContext* xfc = (xfContext*)gdi->context; ++ WINPR_ASSERT(gdi->context); ++ ++ if (freerdp_settings_get_bool(gdi->context->settings, FreeRDP_RemoteApplicationMode)) ++ { ++ xfAppWindow* appWindow = xf_rail_get_window(xfc, windowID); ++ if (appWindow) ++ xf_AppWindowDestroyImage(appWindow); ++ xf_rail_return_window(appWindow); ++ } ++ ++ WLog_WARN(TAG, "function not implemented"); ++ return CHANNEL_RC_OK; ++} ++ + static UINT xf_UpdateWindowFromSurface(RdpgfxClientContext* context, gdiGfxSurface* surface) + { + WINPR_ASSERT(context); +@@ -482,7 +504,9 @@ void xf_graphics_pipeline_init(xfContext* xfc, RdpgfxClientContext* gfx) + gfx->CreateSurface = xf_CreateSurface; + gfx->DeleteSurface = xf_DeleteSurface; + } ++ + gfx->UpdateWindowFromSurface = xf_UpdateWindowFromSurface; ++ gfx->UnmapWindowForSurface = xf_UnmapWindowForSurface; + } + + void xf_graphics_pipeline_uninit(xfContext* xfc, RdpgfxClientContext* gfx) +diff --git a/client/X11/xf_window.c b/client/X11/xf_window.c +index e276aea859b1..b1d589f0d9b0 100644 +--- a/client/X11/xf_window.c ++++ b/client/X11/xf_window.c +@@ -1380,7 +1380,7 @@ void xf_UpdateWindowArea(xfContext* xfc, xfAppWindow* appWindow, int x, int y, i + xf_unlock_x11(xfc); + } + +-static void xf_AppWindowDestroyImage(xfAppWindow* appWindow) ++void xf_AppWindowDestroyImage(xfAppWindow* appWindow) + { + WINPR_ASSERT(appWindow); + if (appWindow->image) +diff --git a/client/X11/xf_window.h b/client/X11/xf_window.h +index e258a9b261c4..26e4292ba307 100644 +--- a/client/X11/xf_window.h ++++ b/client/X11/xf_window.h +@@ -167,9 +167,12 @@ void xf_SetWindowMinimized(xfContext* xfc, xfWindow* window); + void xf_SetWindowDecorations(xfContext* xfc, Window window, BOOL show); + void xf_SetWindowUnlisted(xfContext* xfc, Window window); + ++void xf_DestroyDesktopWindow(xfContext* xfc, xfWindow* window); ++ ++WINPR_ATTR_MALLOC(xf_DestroyDesktopWindow, 2) + xfWindow* xf_CreateDesktopWindow(xfContext* xfc, char* name, int width, int height); ++ + void xf_ResizeDesktopWindow(xfContext* xfc, xfWindow* window, int width, int height); +-void xf_DestroyDesktopWindow(xfContext* xfc, xfWindow* window); + + Window xf_CreateDummyWindow(xfContext* xfc); + void xf_DestroyDummyWindow(xfContext* xfc, Window window); +@@ -196,6 +199,7 @@ void xf_UpdateWindowArea(xfContext* xfc, xfAppWindow* appWindow, int x, int y, i + int height); + UINT xf_AppUpdateWindowFromSurface(xfContext* xfc, gdiGfxSurface* surface); + ++void xf_AppWindowDestroyImage(xfAppWindow* appWindow); + void xf_DestroyWindow(xfContext* xfc, xfAppWindow* appWindow); + void xf_SetWindowMinMaxInfo(xfContext* xfc, xfAppWindow* appWindow, int maxWidth, int maxHeight, + int maxPosX, int maxPosY, int minTrackWidth, int minTrackHeight, \ No newline at end of file diff --git a/debian/patches/CVE-2026-25959.patch b/debian/patches/CVE-2026-25959.patch new file mode 100644 index 0000000..2501789 --- /dev/null +++ b/debian/patches/CVE-2026-25959.patch @@ -0,0 +1,42 @@ +From d3e8b3b9365be96a4f11dda149d71b3287227d0a Mon Sep 17 00:00:00 2001 +From: Armin Novak +Date: Mon, 9 Feb 2026 17:52:37 +0100 +Subject: [PATCH] [client,x11] lock cache when providing data + +--- + client/X11/xf_cliprdr.c | 13 +++++++++---- + 1 file changed, 9 insertions(+), 4 deletions(-) + +diff --git a/client/X11/xf_cliprdr.c b/client/X11/xf_cliprdr.c +index bc4cb753f6c9..db097e84a223 100644 +--- a/client/X11/xf_cliprdr.c ++++ b/client/X11/xf_cliprdr.c +@@ -1576,12 +1576,16 @@ static BOOL xf_cliprdr_process_selection_request(xfClipboard* clipboard, + DEBUG_CLIPRDR("formatId: 0x%08" PRIx32 ", dstFormatId: 0x%08" PRIx32 "", formatId, + dstFormatId); + ++ wHashTable* table = clipboard->cachedData; ++ if (rawTransfer) ++ table = clipboard->cachedRawData; ++ ++ HashTable_Lock(table); ++ + if (!rawTransfer) +- cached_data = HashTable_GetItemValue(clipboard->cachedData, +- format_to_cache_slot(dstFormatId)); ++ cached_data = HashTable_GetItemValue(table, format_to_cache_slot(dstFormatId)); + else +- cached_data = HashTable_GetItemValue(clipboard->cachedRawData, +- format_to_cache_slot(formatId)); ++ cached_data = HashTable_GetItemValue(table, format_to_cache_slot(formatId)); + + DEBUG_CLIPRDR("hasCachedData: %u, rawTransfer: %d", cached_data ? 1u : 0u, rawTransfer); + +@@ -1635,6 +1639,7 @@ static BOOL xf_cliprdr_process_selection_request(xfClipboard* clipboard, + delayRespond = TRUE; + xf_cliprdr_send_data_request(clipboard, formatId, cformat); + } ++ HashTable_Unlock(table); + } + } + \ No newline at end of file diff --git a/debian/patches/CVE-2026-25997.patch b/debian/patches/CVE-2026-25997.patch new file mode 100644 index 0000000..39a1bb0 --- /dev/null +++ b/debian/patches/CVE-2026-25997.patch @@ -0,0 +1,58 @@ +From 58409406afe7c2a8a71ed2dc8e22075be4f41c0c Mon Sep 17 00:00:00 2001 +From: Armin Novak +Date: Mon, 9 Feb 2026 12:58:42 +0100 +Subject: [PATCH] [client,X11] fix clipboard update + +Synchronize channel thread with RDP thread when accessing clipboard +formats +--- + client/X11/xf_cliprdr.c | 17 ++++++++++------- + 1 file changed, 10 insertions(+), 7 deletions(-) + +diff --git a/client/X11/xf_cliprdr.c b/client/X11/xf_cliprdr.c +index 39b082e2faad..bc4cb753f6c9 100644 +--- a/client/X11/xf_cliprdr.c ++++ b/client/X11/xf_cliprdr.c +@@ -869,7 +869,11 @@ static void xf_clipboard_formats_free(xfClipboard* clipboard) + { + WINPR_ASSERT(clipboard); + ++ /* Synchronize RDP/X11 thread with channel thread */ ++ xf_lock_x11(clipboard->xfc); + xf_cliprdr_free_formats(clipboard->lastSentFormats, clipboard->lastSentNumFormats); ++ xf_unlock_x11(clipboard->xfc); ++ + clipboard->lastSentFormats = NULL; + clipboard->lastSentNumFormats = 0; + } +@@ -1867,24 +1871,23 @@ static UINT xf_cliprdr_send_client_format_list_response(xfClipboard* clipboard, + static UINT xf_cliprdr_monitor_ready(CliprdrClientContext* context, + const CLIPRDR_MONITOR_READY* monitorReady) + { +- UINT ret = 0; +- xfClipboard* clipboard = NULL; +- + WINPR_ASSERT(context); + WINPR_ASSERT(monitorReady); + +- clipboard = cliprdr_file_context_get_context(context->custom); ++ xfClipboard* clipboard = cliprdr_file_context_get_context(context->custom); + WINPR_ASSERT(clipboard); + + WINPR_UNUSED(monitorReady); + +- if ((ret = xf_cliprdr_send_client_capabilities(clipboard)) != CHANNEL_RC_OK) ++ const UINT ret = xf_cliprdr_send_client_capabilities(clipboard); ++ if (ret != CHANNEL_RC_OK) + return ret; + + xf_clipboard_formats_free(clipboard); + +- if ((ret = xf_cliprdr_send_client_format_list(clipboard, TRUE)) != CHANNEL_RC_OK) +- return ret; ++ const UINT ret2 = xf_cliprdr_send_client_format_list(clipboard, TRUE); ++ if (ret2 != CHANNEL_RC_OK) ++ return ret2; + + clipboard->sync = TRUE; + return CHANNEL_RC_OK; \ No newline at end of file diff --git a/debian/patches/CVE-2026-26271.patch b/debian/patches/CVE-2026-26271.patch new file mode 100644 index 0000000..f305f9b --- /dev/null +++ b/debian/patches/CVE-2026-26271.patch @@ -0,0 +1,90 @@ +From f5e20403d6e325e11b68129803f967fb5aeec1cb Mon Sep 17 00:00:00 2001 +From: Armin Novak +Date: Fri, 13 Feb 2026 19:38:20 +0100 +Subject: [PATCH] [codec,color] fix input length checks + +* check cbBitsMask meets expected length +* Add logging for length failures +--- + libfreerdp/codec/color.c | 36 ++++++++++++++++++++++++------------ + 1 file changed, 24 insertions(+), 12 deletions(-) + +diff --git a/libfreerdp/codec/color.c b/libfreerdp/codec/color.c +index 2659c92e66bf..7ad6c8453ceb 100644 +--- a/libfreerdp/codec/color.c ++++ b/libfreerdp/codec/color.c +@@ -404,6 +404,9 @@ BOOL freerdp_image_copy_from_icon_data(BYTE* WINPR_RESTRICT pDstData, UINT32 Dst + if (!pDstData || !bitsColor) + return FALSE; + ++ if ((nWidth == 0) || (nHeight == 0)) ++ return TRUE; ++ + /* + * Color formats used by icons are DIB bitmap formats (2-bit format + * is not used by MS-RDPERP). Note that 16-bit is RGB555, not RGB565, +@@ -446,7 +449,13 @@ BOOL freerdp_image_copy_from_icon_data(BYTE* WINPR_RESTRICT pDstData, UINT32 Dst + + /* Ensure we have enough source data bytes for image copy. */ + if (cbBitsColor < nWidth * nHeight * FreeRDPGetBytesPerPixel(format)) ++ { ++ WLog_ERR(TAG, ++ "cbBitsColor{%" PRIu32 "} < nWidth{%" PRIu32 "} * nHeight{%" PRIu32 ++ "} * bpp{%" PRIu32 "}", ++ cbBitsColor, nWidth, nHeight, FreeRDPGetBytesPerPixel(format)); + return FALSE; ++ } + + fill_gdi_palette_for_icon(colorTable, cbColorTable, &palette); + if (!freerdp_image_copy_no_overlap(pDstData, DstFormat, nDstStep, nXDst, nYDst, nWidth, nHeight, +@@ -454,14 +463,8 @@ BOOL freerdp_image_copy_from_icon_data(BYTE* WINPR_RESTRICT pDstData, UINT32 Dst + return FALSE; + + /* apply alpha mask */ +- if (FreeRDPColorHasAlpha(DstFormat) && cbBitsMask) ++ if (FreeRDPColorHasAlpha(DstFormat) && (cbBitsMask > 0)) + { +- BYTE nextBit = 0; +- const BYTE* maskByte = NULL; +- UINT32 stride = 0; +- BYTE r = 0; +- BYTE g = 0; +- BYTE b = 0; + BYTE* dstBuf = pDstData; + UINT32 dstBpp = FreeRDPGetBytesPerPixel(DstFormat); + +@@ -470,20 +473,29 @@ BOOL freerdp_image_copy_from_icon_data(BYTE* WINPR_RESTRICT pDstData, UINT32 Dst + * And due to hysterical raisins, stride of DIB bitmaps must be + * a multiple of 4 bytes. + */ +- stride = round_up(div_ceil(nWidth, 8), 4); ++ const size_t stride = round_up(div_ceil(nWidth, 8), 4); ++ if (cbBitsMask < stride * (nHeight - 1ULL)) ++ { ++ WLog_ERR(TAG, ++ "cbBitsMask{%" PRIu32 "} < stride{%" PRIuz "} * (nHeight{%" PRIu32 "} - 1)", ++ cbBitsMask, stride, nHeight); ++ return FALSE; ++ } + + for (UINT32 y = 0; y < nHeight; y++) + { +- maskByte = &bitsMask[1ULL * stride * (nHeight - 1 - y)]; +- nextBit = 0x80; ++ const BYTE* maskByte = &bitsMask[stride * (nHeight - 1ULL - y)]; ++ BYTE nextBit = 0x80; + + for (UINT32 x = 0; x < nWidth; x++) + { +- UINT32 color = 0; ++ BYTE r = 0; ++ BYTE g = 0; ++ BYTE b = 0; + BYTE alpha = (*maskByte & nextBit) ? 0x00 : 0xFF; + + /* read color back, add alpha and write it back */ +- color = FreeRDPReadColor_int(dstBuf, DstFormat); ++ UINT32 color = FreeRDPReadColor_int(dstBuf, DstFormat); + FreeRDPSplitColor(color, DstFormat, &r, &g, &b, NULL, &palette); + color = FreeRDPGetColor(DstFormat, r, g, b, alpha); + FreeRDPWriteColor_int(dstBuf, DstFormat, color); \ No newline at end of file diff --git a/debian/patches/CVE-2026-26955.patch b/debian/patches/CVE-2026-26955.patch new file mode 100644 index 0000000..1e3627c --- /dev/null +++ b/debian/patches/CVE-2026-26955.patch @@ -0,0 +1,37 @@ +From 7d8fdce2d0ef337cb86cb37fc0c436c905e04d77 Mon Sep 17 00:00:00 2001 +From: Armin Novak +Date: Mon, 16 Feb 2026 19:56:55 +0100 +Subject: [PATCH] [codec,clear] fix destination checks + +check against the correct nDstWidth/nDstHeight +--- + 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 25724fed41f9..0158bacfb0b6 100644 +--- a/libfreerdp/codec/clear.c ++++ b/libfreerdp/codec/clear.c +@@ -478,16 +478,16 @@ static BOOL clear_decompress_subcodecs_data(CLEAR_CONTEXT* WINPR_RESTRICT clear, + + const UINT32 nXDstRel = nXDst + xStart; + const UINT32 nYDstRel = nYDst + yStart; +- if (1ull * nXDstRel + width > nWidth) ++ if (1ull * nXDstRel + width > nDstWidth) + { +- WLog_ERR(TAG, "nXDstRel %" PRIu16 " + width %" PRIu16 " > nWidth %" PRIu32 "", xStart, +- width, nWidth); ++ WLog_ERR(TAG, "nXDstRel %" PRIu32 " + width %" PRIu16 " > nDstWidth %" PRIu32 "", ++ nXDstRel, width, nDstWidth); + return FALSE; + } +- if (1ull * nYDstRel + height > nHeight) ++ if (1ull * nYDstRel + height > nDstHeight) + { +- WLog_ERR(TAG, "nYDstRel %" PRIu16 " + height %" PRIu16 " > nHeight %" PRIu32 "", yStart, +- height, nHeight); ++ WLog_ERR(TAG, "nYDstRel %" PRIu32 " + height %" PRIu16 " > nDstHeight %" PRIu32 "", ++ nYDstRel, height, nDstHeight); + return FALSE; + } + \ No newline at end of file diff --git a/debian/patches/CVE-2026-26965.patch b/debian/patches/CVE-2026-26965.patch new file mode 100644 index 0000000..0a2aaa5 --- /dev/null +++ b/debian/patches/CVE-2026-26965.patch @@ -0,0 +1,49 @@ +From a0be5cb87d760bb1c803ad1bb835aa1e73e62abc Mon Sep 17 00:00:00 2001 +From: Armin Novak +Date: Mon, 16 Feb 2026 09:45:58 +0100 +Subject: [PATCH] [codec,planar] fix missing destination bounds checks + +--- + 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 23e8e0ed01ac..5b85185054d6 100644 +--- a/libfreerdp/codec/planar.c ++++ b/libfreerdp/codec/planar.c +@@ -732,8 +732,9 @@ BOOL freerdp_bitmap_decompress_planar(BITMAP_PLANAR_CONTEXT* WINPR_RESTRICT plan + if (planar->maxHeight < nSrcHeight) + return FALSE; + ++ const UINT32 bpp = FreeRDPGetBytesPerPixel(DstFormat); + if (nDstStep <= 0) +- nDstStep = nDstWidth * FreeRDPGetBytesPerPixel(DstFormat); ++ nDstStep = nDstWidth * bpp; + + const BYTE* srcp = pSrcData; + +@@ -955,6 +956,24 @@ BOOL freerdp_bitmap_decompress_planar(BITMAP_PLANAR_CONTEXT* WINPR_RESTRICT plan + } + 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], WINPR_ASSERTING_INT_CAST(uint32_t, rleSizes[0]), pTempData, nTempStep, + nXDst, nYDst, nSrcWidth, nSrcHeight, 2, vFlip); /* RedPlane */ \ No newline at end of file diff --git a/debian/patches/CVE-2026-26986.patch b/debian/patches/CVE-2026-26986.patch new file mode 100644 index 0000000..483caa4 --- /dev/null +++ b/debian/patches/CVE-2026-26986.patch @@ -0,0 +1,36 @@ +From b4f0f0a18fe53aa8d47d062f91471f4e9c5e0d51 Mon Sep 17 00:00:00 2001 +From: Armin Novak +Date: Mon, 9 Feb 2026 15:50:19 +0100 +Subject: [PATCH] [client,x11] fix xf_rail_window_common cleanup + +leave the appWindow for later cleanup. +--- + client/X11/xf_rail.c | 6 +----- + 1 file changed, 1 insertion(+), 5 deletions(-) + +--- freerdp2.orig/client/X11/xf_rail.c ++++ freerdp2/client/X11/xf_rail.c +@@ -259,11 +259,10 @@ void xf_rail_paint(xfContext* xfc, INT32 + static BOOL xf_rail_window_common(rdpContext* context, const WINDOW_ORDER_INFO* orderInfo, + const WINDOW_STATE_ORDER* windowState) + { +- xfAppWindow* appWindow = NULL; + xfContext* xfc = (xfContext*)context; + UINT32 fieldFlags = orderInfo->fieldFlags; + BOOL position_or_size_updated = FALSE; +- appWindow = xf_rail_get_window(xfc, orderInfo->windowId); ++ xfAppWindow* appWindow = xf_rail_get_window(xfc, orderInfo->windowId); + + if (fieldFlags & WINDOW_ORDER_STATE_NEW) + { +@@ -308,10 +307,7 @@ static BOOL xf_rail_window_common(rdpCon + } + + if (!appWindow->title) +- { +- free(appWindow); + return FALSE; +- } + + xf_AppWindowInit(xfc, appWindow); + } diff --git a/debian/patches/CVE-2026-27015.patch b/debian/patches/CVE-2026-27015.patch new file mode 100644 index 0000000..2647a61 --- /dev/null +++ b/debian/patches/CVE-2026-27015.patch @@ -0,0 +1,97 @@ +From 65d59d3b3c2f630f2ea862687ecf5f95f8115244 Mon Sep 17 00:00:00 2001 +From: Armin Novak +Date: Sun, 15 Feb 2026 09:15:20 +0100 +Subject: [PATCH] [utils,smartcard] check stream length on padding + +When reading optional padding in smartcard channel check if padding is +actually there. +--- + libfreerdp/utils/smartcard_operations.c | 3 ++- + libfreerdp/utils/smartcard_pack.c | 32 ++++++++++++++----------- + 2 files changed, 20 insertions(+), 15 deletions(-) + +diff --git a/libfreerdp/utils/smartcard_operations.c b/libfreerdp/utils/smartcard_operations.c +index 2b0127a7899a..1b442ca82f94 100644 +--- a/libfreerdp/utils/smartcard_operations.c ++++ b/libfreerdp/utils/smartcard_operations.c +@@ -786,7 +786,8 @@ LONG smartcard_irp_device_control_decode(wStream* s, UINT32 CompletionId, UINT32 + (ioControlCode != SCARD_IOCTL_RELEASETARTEDEVENT)) + { + offset = (RDPDR_DEVICE_IO_REQUEST_LENGTH + RDPDR_DEVICE_IO_CONTROL_REQ_HDR_LENGTH); +- smartcard_unpack_read_size_align(s, Stream_GetPosition(s) - offset, 8); ++ if (smartcard_unpack_read_size_align(s, Stream_GetPosition(s) - offset, 8) < 0) ++ return STATUS_INVALID_PARAMETER; + } + + if (Stream_GetPosition(s) < Stream_Length(s)) +diff --git a/libfreerdp/utils/smartcard_pack.c b/libfreerdp/utils/smartcard_pack.c +index 6a82d99d8670..b016b26a3b33 100644 +--- a/libfreerdp/utils/smartcard_pack.c ++++ b/libfreerdp/utils/smartcard_pack.c +@@ -200,6 +200,11 @@ static LONG smartcard_ndr_read_ex(wLog* log, wStream* s, BYTE** data, size_t min + return SCARD_E_NO_MEMORY; + Stream_Read(s, r, len); + const LONG pad = smartcard_unpack_read_size_align(s, len, 4); ++ if (pad < 0) ++ { ++ free(r); ++ return STATUS_INVALID_PARAMETER; ++ } + len += (size_t)pad; + *data = r; + if (plen) +@@ -1702,25 +1707,22 @@ void smartcard_pack_private_type_header(wStream* s, UINT32 objectBufferLength) + + LONG smartcard_unpack_read_size_align(wStream* s, size_t size, UINT32 alignment) + { +- size_t pad = 0; ++ const size_t padsize = (size + alignment - 1) & ~(alignment - 1); ++ const size_t pad = padsize - size; + +- pad = size; +- size = (size + alignment - 1) & ~(alignment - 1); +- pad = size - pad; +- +- if (pad) +- Stream_Seek(s, pad); ++ if (pad > 0) ++ { ++ if (!Stream_SafeSeek(s, pad)) ++ return -1; ++ } + + return (LONG)pad; + } + + LONG smartcard_pack_write_size_align(wStream* s, size_t size, UINT32 alignment) + { +- size_t pad = 0; +- +- pad = size; +- size = (size + alignment - 1) & ~(alignment - 1); +- pad = size - pad; ++ const size_t padsize = (size + alignment - 1) & ~(alignment - 1); ++ const size_t pad = padsize - size; + + if (pad) + { +@@ -3085,7 +3087,8 @@ LONG smartcard_unpack_transmit_call(wStream* s, Transmit_Call* call) + call->pioSendPci->cbPciLength = (DWORD)(ioSendPci.cbExtraBytes + sizeof(SCARD_IO_REQUEST)); + pbExtraBytes = &((BYTE*)call->pioSendPci)[sizeof(SCARD_IO_REQUEST)]; + Stream_Read(s, pbExtraBytes, ioSendPci.cbExtraBytes); +- smartcard_unpack_read_size_align(s, ioSendPci.cbExtraBytes, 4); ++ if (smartcard_unpack_read_size_align(s, ioSendPci.cbExtraBytes, 4) < 0) ++ return STATUS_INVALID_PARAMETER; + } + else + { +@@ -3172,7 +3175,8 @@ LONG smartcard_unpack_transmit_call(wStream* s, Transmit_Call* call) + (DWORD)(ioRecvPci.cbExtraBytes + sizeof(SCARD_IO_REQUEST)); + pbExtraBytes = &((BYTE*)call->pioRecvPci)[sizeof(SCARD_IO_REQUEST)]; + Stream_Read(s, pbExtraBytes, ioRecvPci.cbExtraBytes); +- smartcard_unpack_read_size_align(s, ioRecvPci.cbExtraBytes, 4); ++ if (smartcard_unpack_read_size_align(s, ioRecvPci.cbExtraBytes, 4) < 0) ++ return STATUS_INVALID_PARAMETER; + } + else + { \ No newline at end of file diff --git a/debian/patches/CVE-2026-27950.patch b/debian/patches/CVE-2026-27950.patch new file mode 100644 index 0000000..41a785a --- /dev/null +++ b/debian/patches/CVE-2026-27950.patch @@ -0,0 +1,22 @@ +From 5f62aa11c1bdf00f94c40ea9ebb260a752740b80 Mon Sep 17 00:00:00 2001 +From: Armin Novak +Date: Mon, 23 Feb 2026 08:51:42 +0100 +Subject: [PATCH] [client,sdl] fix sdl_Pointer_New + +reset ptr->data after free. +--- + client/SDL/SDL2/sdl_pointer.cpp | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/client/SDL/SDL2/sdl_pointer.cpp b/client/SDL/SDL2/sdl_pointer.cpp +index 5f00fdf330a7..3cc8bdbbb164 100644 +--- a/client/SDL/SDL2/sdl_pointer.cpp ++++ b/client/SDL/SDL2/sdl_pointer.cpp +@@ -61,6 +61,7 @@ static BOOL sdl_Pointer_New(rdpContext* context, rdpPointer* pointer) + &context->gdi->palette)) + { + winpr_aligned_free(ptr->data); ++ ptr->data = nullptr; + return FALSE; + } + \ No newline at end of file diff --git a/debian/patches/CVE-2026-27951.patch b/debian/patches/CVE-2026-27951.patch new file mode 100644 index 0000000..46b2889 --- /dev/null +++ b/debian/patches/CVE-2026-27951.patch @@ -0,0 +1,784 @@ +From 118afc0b954ba9d5632b7836ad24e454555ed113 Mon Sep 17 00:00:00 2001 +From: Armin Novak +Date: Tue, 17 Feb 2026 12:05:42 +0100 +Subject: [PATCH] [allocations] fix growth of preallocated buffers + +* Replace * 2 with * sizeof(WCHAR) for string usages +* Grow streams and other buffers reasonably, e.g. add 128 elements per + try and check for possible overflows +* Add constant postfix to force them to 64bit +--- + channels/drive/client/drive_file.c | 2 +- + channels/remdesk/common/remdesk_common.c | 2 +- + channels/remdesk/server/remdesk_main.c | 4 +- + channels/tsmf/client/ffmpeg/tsmf_ffmpeg.c | 4 +- + channels/tsmf/client/tsmf_media.c | 2 +- + .../urbdrc/client/libusb/libusb_udevice.c | 4 +- + .../freeRDPCore/src/main/cpp/android_event.c | 14 +++- + client/Windows/wf_cliprdr.c | 11 ++- + client/X11/xf_event.c | 6 +- + libfreerdp/core/gcc.c | 3 +- + libfreerdp/core/orders.c | 2 +- + libfreerdp/core/proxy.c | 2 +- + libfreerdp/crypto/base64.c | 2 +- + winpr/libwinpr/clipboard/synthetic_file.c | 2 +- + winpr/libwinpr/crt/buffer.c | 2 +- + winpr/libwinpr/environment/environment.c | 77 ++++++++++--------- + winpr/libwinpr/file/file.c | 2 +- + winpr/libwinpr/ncrypt/ncrypt.c | 4 +- + winpr/libwinpr/ncrypt/ncrypt_pkcs11.c | 7 +- + .../libwinpr/path/include/PathAllocCombine.h | 6 +- + winpr/libwinpr/sspi/NTLM/ntlm_message.c | 4 +- + winpr/libwinpr/utils/collections/BufferPool.c | 15 +++- + .../libwinpr/utils/collections/MessageQueue.c | 19 +++-- + winpr/libwinpr/utils/collections/ObjectPool.c | 15 ++-- + winpr/libwinpr/utils/collections/PubSub.c | 16 ++-- + winpr/libwinpr/utils/collections/Stack.c | 10 ++- + winpr/libwinpr/utils/stream.c | 15 ++-- + winpr/tools/makecert/makecert.c | 5 +- + 28 files changed, 153 insertions(+), 104 deletions(-) + +diff --git a/channels/drive/client/drive_file.c b/channels/drive/client/drive_file.c +index 45d3c181ccc5..bd8b1dc6dd01 100644 +--- a/channels/drive/client/drive_file.c ++++ b/channels/drive/client/drive_file.c +@@ -1052,7 +1052,7 @@ BOOL drive_file_query_directory(DRIVE_FILE* file, UINT32 FsInformationClass, BYT + else if (!FindNextFileW(file->find_handle, &file->find_data)) + goto out_fail; + +- length = _wcslen(file->find_data.cFileName) * 2; ++ length = _wcslen(file->find_data.cFileName) * sizeof(WCHAR); + + switch (FsInformationClass) + { +diff --git a/channels/remdesk/common/remdesk_common.c b/channels/remdesk/common/remdesk_common.c +index a4600e764bba..1907fb03dea3 100644 +--- a/channels/remdesk/common/remdesk_common.c ++++ b/channels/remdesk/common/remdesk_common.c +@@ -36,7 +36,7 @@ UINT remdesk_write_channel_header(wStream* s, const REMDESK_CHANNEL_HEADER* head + } + + const size_t ChannelNameLen = +- (strnlen(header->ChannelName, sizeof(header->ChannelName)) + 1) * 2; ++ (strnlen(header->ChannelName, sizeof(header->ChannelName)) + 1) * sizeof(WCHAR); + WINPR_ASSERT(ChannelNameLen <= ARRAYSIZE(header->ChannelName)); + + Stream_Write_UINT32(s, (UINT32)ChannelNameLen); /* ChannelNameLen (4 bytes) */ +diff --git a/channels/remdesk/server/remdesk_main.c b/channels/remdesk/server/remdesk_main.c +index 445027aacc22..0189c31ecad5 100644 +--- a/channels/remdesk/server/remdesk_main.c ++++ b/channels/remdesk/server/remdesk_main.c +@@ -185,7 +185,7 @@ static UINT remdesk_recv_ctl_remote_control_desktop_pdu(RemdeskServerContext* co + return ERROR_INVALID_DATA; + + cchStringW++; +- const size_t cbRaConnectionStringW = cchStringW * 2; ++ const size_t cbRaConnectionStringW = cchStringW * sizeof(WCHAR); + pdu.raConnectionString = + ConvertWCharNToUtf8Alloc(raConnectionStringW, cbRaConnectionStringW / sizeof(WCHAR), NULL); + if (!pdu.raConnectionString) +@@ -240,7 +240,7 @@ static UINT remdesk_recv_ctl_authenticate_pdu(WINPR_ATTR_UNUSED RemdeskServerCon + return ERROR_INVALID_DATA; + + cchStringW++; +- const size_t cbExpertBlobW = cchStringW * 2; ++ const size_t cbExpertBlobW = cchStringW * sizeof(WCHAR); + pdu.raConnectionString = + ConvertWCharNToUtf8Alloc(raConnectionStringW, cbRaConnectionStringW / sizeof(WCHAR), NULL); + if (!pdu.raConnectionString) +diff --git a/channels/tsmf/client/ffmpeg/tsmf_ffmpeg.c b/channels/tsmf/client/ffmpeg/tsmf_ffmpeg.c +index 12da2601b6cb..22c55d742631 100644 +--- a/channels/tsmf/client/ffmpeg/tsmf_ffmpeg.c ++++ b/channels/tsmf/client/ffmpeg/tsmf_ffmpeg.c +@@ -488,12 +488,12 @@ static BOOL tsmf_ffmpeg_decode_audio(ITSMFDecoder* decoder, const BYTE* data, UI + if (mdecoder->decoded_size_max - mdecoder->decoded_size < MAX_AUDIO_FRAME_SIZE) + { + BYTE* tmp_data = NULL; +- tmp_data = realloc(mdecoder->decoded_data, mdecoder->decoded_size_max * 2 + 16); ++ tmp_data = realloc(mdecoder->decoded_data, mdecoder->decoded_size_max * 2ull + 16ull); + + if (!tmp_data) + return FALSE; + +- mdecoder->decoded_size_max = mdecoder->decoded_size_max * 2 + 16; ++ mdecoder->decoded_size_max = mdecoder->decoded_size_max * 2ull + 16ull; + mdecoder->decoded_data = tmp_data; + dst = (BYTE*)(((uintptr_t)mdecoder->decoded_data + 15) & ~0x0F); + +diff --git a/channels/tsmf/client/tsmf_media.c b/channels/tsmf/client/tsmf_media.c +index 25abd7ed93b7..8cb3ea614852 100644 +--- a/channels/tsmf/client/tsmf_media.c ++++ b/channels/tsmf/client/tsmf_media.c +@@ -387,7 +387,7 @@ static char* guid_to_string(const BYTE* guid, char* str, size_t len) + TSMF_PRESENTATION* tsmf_presentation_find_by_id(const BYTE* guid) + { + BOOL found = FALSE; +- char guid_str[GUID_SIZE * 2 + 1] = { 0 }; ++ char guid_str[GUID_SIZE * 2ull + 1] = { 0 }; + TSMF_PRESENTATION* presentation = NULL; + ArrayList_Lock(presentation_list); + const size_t count = ArrayList_Count(presentation_list); +diff --git a/channels/urbdrc/client/libusb/libusb_udevice.c b/channels/urbdrc/client/libusb/libusb_udevice.c +index d465ff9be45f..2491bf7e14df 100644 +--- a/channels/urbdrc/client/libusb/libusb_udevice.c ++++ b/channels/urbdrc/client/libusb/libusb_udevice.c +@@ -917,10 +917,10 @@ static UINT32 libusb_udev_control_query_device_text(IUDEVICE* idev, UINT32 TextT + * So also check the string length returned as server side does + * not honor strings with multi '\0' characters well. + */ +- const size_t rchar = _wcsnlen((WCHAR*)&data[2], sizeof(data) / 2); ++ const size_t rchar = _wcsnlen((WCHAR*)&data[2], sizeof(data) / sizeof(WCHAR)); + len = MIN((BYTE)ret - 2, slen); + len = MIN(len, inSize); +- len = MIN(len, rchar * 2 + sizeof(WCHAR)); ++ len = MIN(len, rchar * sizeof(WCHAR) + sizeof(WCHAR)); + memcpy(Buffer, &data[2], len); + + /* Just as above, the returned WCHAR string should be '\0' +diff --git a/client/Android/Studio/freeRDPCore/src/main/cpp/android_event.c b/client/Android/Studio/freeRDPCore/src/main/cpp/android_event.c +index da3d2aab2c6d..5a9fb2421702 100644 +--- a/client/Android/Studio/freeRDPCore/src/main/cpp/android_event.c ++++ b/client/Android/Studio/freeRDPCore/src/main/cpp/android_event.c +@@ -28,10 +28,16 @@ BOOL android_push_event(freerdp* inst, ANDROID_EVENT* event) + + if (aCtx->event_queue->count >= aCtx->event_queue->size) + { +- int new_size; +- void* new_events; +- new_size = aCtx->event_queue->size * 2; +- new_events = realloc((void*)aCtx->event_queue->events, sizeof(ANDROID_EVENT*) * new_size); ++ size_t new_size = aCtx->event_queue->size; ++ do ++ { ++ if (new_size >= SIZE_MAX - 128ull) ++ return FALSE; ++ ++ new_size += 128ull; ++ } while (new_size <= aCtx->event_queue->count); ++ void* new_events = ++ realloc((void*)aCtx->event_queue->events, sizeof(ANDROID_EVENT*) * new_size); + + if (!new_events) + return FALSE; +diff --git a/client/Windows/wf_cliprdr.c b/client/Windows/wf_cliprdr.c +index 61207cfbc9f3..e77b56bf887b 100644 +--- a/client/Windows/wf_cliprdr.c ++++ b/client/Windows/wf_cliprdr.c +@@ -1122,10 +1122,13 @@ static void map_ensure_capacity(wfClipboard* clipboard) + + if (clipboard->map_size >= clipboard->map_capacity) + { +- size_t new_size; +- formatMapping* new_map; +- new_size = clipboard->map_capacity * 2; +- new_map = ++ size_t new_size = clipboard->map_capacity; ++ do ++ { ++ WINPR_ASSERT(new_size <= SIZE_MAX - 128ull); ++ new_size += 128ull; ++ } while (new_size <= clipboard->map_size); ++ formatMapping* new_map = + (formatMapping*)realloc(clipboard->format_mappings, sizeof(formatMapping) * new_size); + + if (!new_map) +diff --git a/client/X11/xf_event.c b/client/X11/xf_event.c +index 9cc554cbc999..f438c52a8538 100644 +--- a/client/X11/xf_event.c ++++ b/client/X11/xf_event.c +@@ -894,10 +894,8 @@ static BOOL xf_event_ConfigureNotify(xfContext* xfc, const XConfigureEvent* even + + if (freerdp_settings_get_bool(settings, FreeRDP_DynamicResolutionUpdate)) + { +- int alignedWidth = 0; +- int alignedHeight = 0; +- alignedWidth = (xfc->window->width / 2) * 2; +- alignedHeight = (xfc->window->height / 2) * 2; ++ const int alignedWidth = (xfc->window->width / 2) * 2; ++ const int alignedHeight = (xfc->window->height / 2) * 2; + /* ask the server to resize using the display channel */ + xf_disp_handle_configureNotify(xfc, alignedWidth, alignedHeight); + } +diff --git a/libfreerdp/core/gcc.c b/libfreerdp/core/gcc.c +index 530253769ee0..ceeac6747fc7 100644 +--- a/libfreerdp/core/gcc.c ++++ b/libfreerdp/core/gcc.c +@@ -1999,7 +1999,8 @@ BOOL gcc_write_server_network_data(wStream* s, const rdpMcs* mcs) + { + WINPR_ASSERT(s); + WINPR_ASSERT(mcs); +- const size_t payloadLen = 8 + mcs->channelCount * 2 + (mcs->channelCount % 2 == 1 ? 2 : 0); ++ const size_t payloadLen = ++ 8ull + mcs->channelCount * 2ull + (mcs->channelCount % 2 == 1 ? 2ull : 0ull); + + WINPR_ASSERT(payloadLen <= UINT16_MAX); + if (!gcc_write_user_data_header(s, SC_NET, (UINT16)payloadLen)) +diff --git a/libfreerdp/core/orders.c b/libfreerdp/core/orders.c +index 8eb6c4f2dacf..3ae8d36d83a1 100644 +--- a/libfreerdp/core/orders.c ++++ b/libfreerdp/core/orders.c +@@ -3302,7 +3302,7 @@ size_t update_approximate_create_offscreen_bitmap_order( + deleteList = &(create_offscreen_bitmap->deleteList); + WINPR_ASSERT(deleteList); + +- return 32 + deleteList->cIndices * 2; ++ return 32ull + deleteList->cIndices * 2ull; + } + + BOOL update_write_create_offscreen_bitmap_order( +diff --git a/libfreerdp/core/proxy.c b/libfreerdp/core/proxy.c +index 873fad43e363..d5b988159e8b 100644 +--- a/libfreerdp/core/proxy.c ++++ b/libfreerdp/core/proxy.c +@@ -597,7 +597,7 @@ static BOOL http_proxy_connect(rdpContext* context, BIO* bufferedBio, const char + + hostLen = strlen(hostname); + portLen = strnlen(port_str, sizeof(port_str)); +- reserveSize = strlen(connect) + (hostLen + 1 + portLen) * 2 + strlen(httpheader); ++ reserveSize = strlen(connect) + (hostLen + 1ull + portLen) * 2ull + strlen(httpheader); + s = Stream_New(NULL, reserveSize); + if (!s) + goto fail; +diff --git a/libfreerdp/crypto/base64.c b/libfreerdp/crypto/base64.c +index 27394e0b3633..91cce5eecc48 100644 +--- a/libfreerdp/crypto/base64.c ++++ b/libfreerdp/crypto/base64.c +@@ -313,7 +313,7 @@ static inline char* base64_encode_ex(const BYTE* WINPR_RESTRICT alphabet, + if (crLf) + { + size_t nCrLf = (outLen + lineSize - 1) / lineSize; +- extra = nCrLf * 2; ++ extra = nCrLf * 2ull; + } + size_t outCounter = 0; + +diff --git a/winpr/libwinpr/clipboard/synthetic_file.c b/winpr/libwinpr/clipboard/synthetic_file.c +index 147d99b4b5ec..0793daf397d6 100644 +--- a/winpr/libwinpr/clipboard/synthetic_file.c ++++ b/winpr/libwinpr/clipboard/synthetic_file.c +@@ -759,7 +759,7 @@ static void* convert_filedescriptors_to_file_list(wClipboard* clipboard, UINT32 + alloc += ARRAYSIZE(dsc->cFileName) * + 8; /* Overallocate, just take the biggest value the result path can have */ + /* # (1 char) -> %23 (3 chars) , the first char is replaced inplace */ +- alloc += count_special_chars(dsc->cFileName) * 2; ++ alloc += count_special_chars(dsc->cFileName) * sizeof(WCHAR); + alloc += decoration_len; + } + } +diff --git a/winpr/libwinpr/crt/buffer.c b/winpr/libwinpr/crt/buffer.c +index fc914c9e8382..876309eca366 100644 +--- a/winpr/libwinpr/crt/buffer.c ++++ b/winpr/libwinpr/crt/buffer.c +@@ -39,7 +39,7 @@ errno_t memmove_s(void* dest, size_t numberOfElements, const void* src, size_t c + + errno_t wmemmove_s(WCHAR* dest, size_t numberOfElements, const WCHAR* src, size_t count) + { +- if (count * 2 > numberOfElements) ++ if (count * sizeof(WCHAR) > numberOfElements) + return -1; + + memmove(dest, src, count * 2); +diff --git a/winpr/libwinpr/environment/environment.c b/winpr/libwinpr/environment/environment.c +index 345037bdf6f4..b9419ec1599e 100644 +--- a/winpr/libwinpr/environment/environment.c ++++ b/winpr/libwinpr/environment/environment.c +@@ -30,6 +30,10 @@ + + #include + ++#include "../log.h" ++ ++#define TAG WINPR_TAG("environment") ++ + #ifndef _WIN32 + + #include +@@ -100,19 +104,19 @@ DWORD GetCurrentDirectoryA(DWORD nBufferLength, LPSTR lpBuffer) + + DWORD GetCurrentDirectoryW(WINPR_ATTR_UNUSED DWORD nBufferLength, WINPR_ATTR_UNUSED LPWSTR lpBuffer) + { +- WLog_ERR("TODO", "TODO: not implemented"); ++ WLog_ERR(TAG, "TODO: not implemented"); + return 0; + } + + BOOL SetCurrentDirectoryA(WINPR_ATTR_UNUSED LPCSTR lpPathName) + { +- WLog_ERR("TODO", "TODO: not implemented"); ++ WLog_ERR(TAG, "TODO: not implemented"); + return TRUE; + } + + BOOL SetCurrentDirectoryW(WINPR_ATTR_UNUSED LPCWSTR lpPathName) + { +- WLog_ERR("TODO", "TODO: not implemented"); ++ WLog_ERR(TAG, "TODO: not implemented"); + return TRUE; + } + +@@ -120,7 +124,7 @@ DWORD SearchPathA(WINPR_ATTR_UNUSED LPCSTR lpPath, WINPR_ATTR_UNUSED LPCSTR lpFi + WINPR_ATTR_UNUSED LPCSTR lpExtension, WINPR_ATTR_UNUSED DWORD nBufferLength, + WINPR_ATTR_UNUSED LPSTR lpBuffer, WINPR_ATTR_UNUSED LPSTR* lpFilePart) + { +- WLog_ERR("TODO", "TODO: not implemented"); ++ WLog_ERR(TAG, "TODO: not implemented"); + return 0; + } + +@@ -128,31 +132,31 @@ DWORD SearchPathW(WINPR_ATTR_UNUSED LPCWSTR lpPath, WINPR_ATTR_UNUSED LPCWSTR lp + WINPR_ATTR_UNUSED LPCWSTR lpExtension, WINPR_ATTR_UNUSED DWORD nBufferLength, + WINPR_ATTR_UNUSED LPWSTR lpBuffer, WINPR_ATTR_UNUSED LPWSTR* lpFilePart) + { +- WLog_ERR("TODO", "TODO: not implemented"); ++ WLog_ERR(TAG, "TODO: not implemented"); + return 0; + } + + LPSTR GetCommandLineA(VOID) + { +- WLog_ERR("TODO", "TODO: not implemented"); ++ WLog_ERR(TAG, "TODO: not implemented"); + return NULL; + } + + LPWSTR GetCommandLineW(VOID) + { +- WLog_ERR("TODO", "TODO: not implemented"); ++ WLog_ERR(TAG, "TODO: not implemented"); + return NULL; + } + + BOOL NeedCurrentDirectoryForExePathA(WINPR_ATTR_UNUSED LPCSTR ExeName) + { +- WLog_ERR("TODO", "TODO: not implemented"); ++ WLog_ERR(TAG, "TODO: not implemented"); + return TRUE; + } + + BOOL NeedCurrentDirectoryForExePathW(WINPR_ATTR_UNUSED LPCWSTR ExeName) + { +- WLog_ERR("TODO", "TODO: not implemented"); ++ WLog_ERR(TAG, "TODO: not implemented"); + return TRUE; + } + +@@ -192,7 +196,7 @@ DWORD GetEnvironmentVariableA(LPCSTR lpName, LPSTR lpBuffer, DWORD nSize) + DWORD GetEnvironmentVariableW(WINPR_ATTR_UNUSED LPCWSTR lpName, WINPR_ATTR_UNUSED LPWSTR lpBuffer, + WINPR_ATTR_UNUSED DWORD nSize) + { +- WLog_ERR("TODO", "TODO: not implemented"); ++ WLog_ERR(TAG, "TODO: not implemented"); + SetLastError(ERROR_ENVVAR_NOT_FOUND); + return 0; + } +@@ -224,7 +228,7 @@ BOOL SetEnvironmentVariableA(LPCSTR lpName, LPCSTR lpValue) + + BOOL SetEnvironmentVariableW(WINPR_ATTR_UNUSED LPCWSTR lpName, WINPR_ATTR_UNUSED LPCWSTR lpValue) + { +- WLog_ERR("TODO", "TODO: not implemented"); ++ WLog_ERR(TAG, "TODO: not implemented"); + return FALSE; + } + +@@ -249,32 +253,35 @@ extern char** environ; + LPCH GetEnvironmentStringsA(VOID) + { + #if !defined(_UWP) +- char* p = NULL; + size_t offset = 0; +- size_t length = 0; +- char** envp = NULL; +- DWORD cchEnvironmentBlock = 0; +- LPCH lpszEnvironmentBlock = NULL; ++ char** envp = environ; ++ const size_t blocksize = 128; ++ size_t cchEnvironmentBlock = blocksize; ++ LPCH lpszEnvironmentBlock = (LPCH)calloc(cchEnvironmentBlock, sizeof(CHAR)); + +- offset = 0; +- envp = environ; +- +- cchEnvironmentBlock = 128; +- lpszEnvironmentBlock = (LPCH)calloc(cchEnvironmentBlock, sizeof(CHAR)); + if (!lpszEnvironmentBlock) + return NULL; + + while (*envp) + { +- length = strlen(*envp); +- +- while ((offset + length + 8) > cchEnvironmentBlock) ++ const size_t length = strlen(*envp); ++ const size_t required = offset + length + 8ull; ++ if (required > UINT32_MAX) + { +- DWORD new_size = 0; +- LPCH new_blk = NULL; ++ WLog_ERR(TAG, "Environment block too large: %" PRIuz, required); + +- new_size = cchEnvironmentBlock * 2; +- new_blk = (LPCH)realloc(lpszEnvironmentBlock, new_size * sizeof(CHAR)); ++ free(lpszEnvironmentBlock); ++ return NULL; ++ } ++ ++ if (required > cchEnvironmentBlock) ++ { ++ size_t new_size = cchEnvironmentBlock; ++ do ++ { ++ new_size += blocksize; ++ } while (new_size <= required); ++ LPCH new_blk = (LPCH)realloc(lpszEnvironmentBlock, new_size * sizeof(CHAR)); + if (!new_blk) + { + free(lpszEnvironmentBlock); +@@ -285,12 +292,12 @@ LPCH GetEnvironmentStringsA(VOID) + cchEnvironmentBlock = new_size; + } + +- p = &(lpszEnvironmentBlock[offset]); ++ char* p = &(lpszEnvironmentBlock[offset]); + + CopyMemory(p, *envp, length * sizeof(CHAR)); + p[length] = '\0'; + +- offset += (length + 1); ++ offset += (length + 1ull); + envp++; + } + +@@ -304,33 +311,33 @@ LPCH GetEnvironmentStringsA(VOID) + + LPWCH GetEnvironmentStringsW(VOID) + { +- WLog_ERR("TODO", "TODO: not implemented"); ++ WLog_ERR(TAG, "TODO: not implemented"); + return NULL; + } + + BOOL SetEnvironmentStringsA(WINPR_ATTR_UNUSED LPCH NewEnvironment) + { +- WLog_ERR("TODO", "TODO: not implemented"); ++ WLog_ERR(TAG, "TODO: not implemented"); + return TRUE; + } + + BOOL SetEnvironmentStringsW(WINPR_ATTR_UNUSED LPWCH NewEnvironment) + { +- WLog_ERR("TODO", "TODO: not implemented"); ++ WLog_ERR(TAG, "TODO: not implemented"); + return TRUE; + } + + DWORD ExpandEnvironmentStringsA(WINPR_ATTR_UNUSED LPCSTR lpSrc, WINPR_ATTR_UNUSED LPSTR lpDst, + WINPR_ATTR_UNUSED DWORD nSize) + { +- WLog_ERR("TODO", "TODO: not implemented"); ++ WLog_ERR(TAG, "TODO: not implemented"); + return 0; + } + + DWORD ExpandEnvironmentStringsW(WINPR_ATTR_UNUSED LPCWSTR lpSrc, WINPR_ATTR_UNUSED LPWSTR lpDst, + WINPR_ATTR_UNUSED DWORD nSize) + { +- WLog_ERR("TODO", "TODO: not implemented"); ++ WLog_ERR(TAG, "TODO: not implemented"); + return 0; + } + +diff --git a/winpr/libwinpr/file/file.c b/winpr/libwinpr/file/file.c +index 112dc43a6933..051a397ccf54 100644 +--- a/winpr/libwinpr/file/file.c ++++ b/winpr/libwinpr/file/file.c +@@ -1403,7 +1403,7 @@ DWORD GetFullPathNameA(LPCSTR lpFileName, DWORD nBufferLength, LPSTR lpBuffer, L + free(lpFileNameW); + free(lpBufferW); + +- return dwStatus * 2; ++ return WINPR_ASSERTING_INT_CAST(DWORD, dwStatus * sizeof(WCHAR)); + } + + BOOL GetDiskFreeSpaceA(LPCSTR lpRootPathName, LPDWORD lpSectorsPerCluster, LPDWORD lpBytesPerSector, +diff --git a/winpr/libwinpr/ncrypt/ncrypt.c b/winpr/libwinpr/ncrypt/ncrypt.c +index 18d64e30b84b..05315b45f8fe 100644 +--- a/winpr/libwinpr/ncrypt/ncrypt.c ++++ b/winpr/libwinpr/ncrypt/ncrypt.c +@@ -120,7 +120,7 @@ SECURITY_STATUS NCryptEnumStorageProviders(DWORD* wProviderCount, + + #ifdef WITH_PKCS11 + *wProviderCount += 1; +- stringAllocSize += (_wcslen(MS_SCARD_PROV) + 1) * 2; ++ stringAllocSize += (_wcslen(MS_SCARD_PROV) + 1) * sizeof(WCHAR); + stringAllocSize += sizeof(emptyComment); + #endif + +@@ -135,7 +135,7 @@ SECURITY_STATUS NCryptEnumStorageProviders(DWORD* wProviderCount, + strPtr = (LPWSTR)(ret + *wProviderCount); + + ret->pszName = strPtr; +- copyAmount = (_wcslen(MS_SCARD_PROV) + 1) * 2; ++ copyAmount = (_wcslen(MS_SCARD_PROV) + 1) * sizeof(WCHAR); + memcpy(strPtr, MS_SCARD_PROV, copyAmount); + strPtr += copyAmount / 2; + +diff --git a/winpr/libwinpr/ncrypt/ncrypt_pkcs11.c b/winpr/libwinpr/ncrypt/ncrypt_pkcs11.c +index 55a3cf0763f9..bc31616cabc5 100644 +--- a/winpr/libwinpr/ncrypt/ncrypt_pkcs11.c ++++ b/winpr/libwinpr/ncrypt/ncrypt_pkcs11.c +@@ -821,11 +821,12 @@ static SECURITY_STATUS NCryptP11EnumKeys(NCRYPT_PROV_HANDLE hProvider, LPCWSTR p + { + /* sizeof keyName struct + "\\" + keyName->pszAlgid */ + DWORD algoSz = 0; +- size_t KEYNAME_SZ = +- (1 + (sizeof(key->slotId) * 2) /*slotId*/ + 1 + (key->idLen * 2) + 1) * 2; ++ size_t KEYNAME_SZ = (1ull + (sizeof(key->slotId) * 2ull) /*slotId*/ + 1ull + ++ (key->idLen * 2ull) + 1ull) * ++ sizeof(WCHAR); + + convertKeyType(key->keyType, NULL, 0, &algoSz); +- KEYNAME_SZ += (1ULL + algoSz) * 2ULL; ++ KEYNAME_SZ += (1ULL + algoSz) * sizeof(WCHAR); + + keyName = calloc(1, sizeof(*keyName) + KEYNAME_SZ); + if (!keyName) +diff --git a/winpr/libwinpr/path/include/PathAllocCombine.h b/winpr/libwinpr/path/include/PathAllocCombine.h +index 4837343c8e6c..5461252a9a08 100644 +--- a/winpr/libwinpr/path/include/PathAllocCombine.h ++++ b/winpr/libwinpr/path/include/PathAllocCombine.h +@@ -78,7 +78,7 @@ HRESULT PATH_ALLOC_COMBINE(PCWSTR pszPathIn, PCWSTR pszMore, + else + { + const size_t pszPathOutLength = pszPathInLength + pszMoreLength; +- const size_t sizeOfBuffer = (pszPathOutLength + 1) * 2; ++ const size_t sizeOfBuffer = (pszPathOutLength + 1) * sizeof(WCHAR); + PWSTR pszPathOut = (PWSTR)calloc(sizeOfBuffer, 2); + + if (!pszPathOut) +@@ -131,7 +131,7 @@ HRESULT PATH_ALLOC_COMBINE(PCSTR pszPathIn, PCSTR pszMore, WINPR_ATTR_UNUSED uns + if ((pszPathIn[1] == ':') && (pszPathIn[2] == CUR_PATH_SEPARATOR_CHR)) + { + const size_t pszPathOutLength = 2 + pszMoreLength; +- const size_t sizeOfBuffer = (pszPathOutLength + 1) * 2; ++ const size_t sizeOfBuffer = (pszPathOutLength + 1) * sizeof(WCHAR); + PSTR pszPathOut = calloc(sizeOfBuffer, 2); + + if (!pszPathOut) +@@ -145,7 +145,7 @@ HRESULT PATH_ALLOC_COMBINE(PCSTR pszPathIn, PCSTR pszMore, WINPR_ATTR_UNUSED uns + else + { + const size_t pszPathOutLength = pszPathInLength + pszMoreLength; +- const size_t sizeOfBuffer = (pszPathOutLength + 1) * 2; ++ const size_t sizeOfBuffer = (pszPathOutLength + 1) * sizeof(WCHAR); + PSTR pszPathOut = calloc(sizeOfBuffer, 2); + + if (!pszPathOut) +diff --git a/winpr/libwinpr/sspi/NTLM/ntlm_message.c b/winpr/libwinpr/sspi/NTLM/ntlm_message.c +index b70ce4ea20f6..a3952559c55f 100644 +--- a/winpr/libwinpr/sspi/NTLM/ntlm_message.c ++++ b/winpr/libwinpr/sspi/NTLM/ntlm_message.c +@@ -1285,11 +1285,11 @@ SECURITY_STATUS ntlm_write_AuthenticateMessage(NTLM_CONTEXT* context, SecBuffer* + if (credentials->identity.DomainLength > 0) + { + message->NegotiateFlags |= NTLMSSP_NEGOTIATE_DOMAIN_SUPPLIED; +- message->DomainName.Len = (UINT16)credentials->identity.DomainLength * 2; ++ message->DomainName.Len = (UINT16)credentials->identity.DomainLength * sizeof(WCHAR); + message->DomainName.Buffer = (BYTE*)credentials->identity.Domain; + } + +- message->UserName.Len = (UINT16)credentials->identity.UserLength * 2; ++ message->UserName.Len = (UINT16)credentials->identity.UserLength * sizeof(WCHAR); + message->UserName.Buffer = (BYTE*)credentials->identity.User; + message->LmChallengeResponse.Len = (UINT16)context->LmChallengeResponse.cbBuffer; + message->LmChallengeResponse.Buffer = (BYTE*)context->LmChallengeResponse.pvBuffer; +diff --git a/winpr/libwinpr/utils/collections/BufferPool.c b/winpr/libwinpr/utils/collections/BufferPool.c +index 6513b714bbe5..4b8bf3e1374b 100644 +--- a/winpr/libwinpr/utils/collections/BufferPool.c ++++ b/winpr/libwinpr/utils/collections/BufferPool.c +@@ -127,9 +127,20 @@ static BOOL BufferPool_ShiftUsed(wBufferPool* pool, SSIZE_T index, SSIZE_T count + { + if (count > 0) + { +- if (pool->uSize + count > pool->uCapacity) ++ const SSIZE_T required = pool->uSize + count; ++ // check for overflow ++ if ((required < count) || (required < pool->uSize)) ++ return FALSE; ++ ++ if (required > pool->uCapacity) + { +- SSIZE_T newUCapacity = pool->uCapacity * 2; ++ SSIZE_T newUCapacity = pool->uCapacity; ++ do ++ { ++ if (newUCapacity > SIZE_MAX - 128ull) ++ return FALSE; ++ newUCapacity += 128ull; ++ } while (newUCapacity <= required); + wBufferPoolItem* newUArray = NULL; + if (pool->alignment > 0) + newUArray = (wBufferPoolItem*)winpr_aligned_realloc( +diff --git a/winpr/libwinpr/utils/collections/MessageQueue.c b/winpr/libwinpr/utils/collections/MessageQueue.c +index 361685b41fae..cc1f579f3924 100644 +--- a/winpr/libwinpr/utils/collections/MessageQueue.c ++++ b/winpr/libwinpr/utils/collections/MessageQueue.c +@@ -96,16 +96,25 @@ static BOOL MessageQueue_EnsureCapacity(wMessageQueue* queue, size_t count) + { + WINPR_ASSERT(queue); + +- if (queue->size + count >= queue->capacity) ++ const size_t required = queue->size + count; ++ // check for overflow ++ if ((required < queue->size) || (required < count)) ++ return FALSE; ++ ++ if (required >= queue->capacity) + { +- wMessage* new_arr = NULL; + size_t old_capacity = queue->capacity; +- size_t new_capacity = queue->capacity * 2; ++ size_t new_capacity = queue->capacity; + +- if (new_capacity < queue->size + count) ++ if (new_capacity < required) ++ { + new_capacity = queue->size + count; ++ // check for overflow ++ if (new_capacity < old_capacity) ++ return FALSE; ++ } + +- new_arr = (wMessage*)realloc(queue->array, sizeof(wMessage) * new_capacity); ++ wMessage* new_arr = (wMessage*)realloc(queue->array, sizeof(wMessage) * new_capacity); + if (!new_arr) + return FALSE; + queue->array = new_arr; +diff --git a/winpr/libwinpr/utils/collections/ObjectPool.c b/winpr/libwinpr/utils/collections/ObjectPool.c +index 98f962ae999a..4e6efd260d5b 100644 +--- a/winpr/libwinpr/utils/collections/ObjectPool.c ++++ b/winpr/libwinpr/utils/collections/ObjectPool.c +@@ -92,13 +92,18 @@ void ObjectPool_Return(wObjectPool* pool, void* obj) + { + ObjectPool_Lock(pool); + +- if ((pool->size + 1) >= pool->capacity) ++ WINPR_ASSERT(pool->size < SIZE_MAX); ++ const size_t required = pool->size + 1ull; ++ if (required >= pool->capacity) + { +- size_t new_cap = 0; +- void** new_arr = NULL; ++ size_t new_cap = pool->capacity; ++ do ++ { ++ WINPR_ASSERT(new_cap <= SIZE_MAX - 128ull); ++ new_cap += 128ull; ++ } while (new_cap <= required); + +- new_cap = pool->capacity * 2; +- new_arr = (void**)realloc((void*)pool->array, sizeof(void*) * new_cap); ++ void** new_arr = (void**)realloc((void*)pool->array, sizeof(void*) * new_cap); + if (!new_arr) + goto out; + +diff --git a/winpr/libwinpr/utils/collections/PubSub.c b/winpr/libwinpr/utils/collections/PubSub.c +index 147135338adc..fae0bdec04e1 100644 +--- a/winpr/libwinpr/utils/collections/PubSub.c ++++ b/winpr/libwinpr/utils/collections/PubSub.c +@@ -94,13 +94,19 @@ void PubSub_AddEventTypes(wPubSub* pubSub, wEventType* events, size_t count) + if (pubSub->synchronized) + PubSub_Lock(pubSub); + +- while (pubSub->count + count >= pubSub->size) ++ const size_t required = pubSub->count + count; ++ WINPR_ASSERT((required >= pubSub->count) && (required >= count)); ++ ++ if (required >= pubSub->size) + { +- size_t new_size = 0; +- wEventType* new_event = NULL; ++ size_t new_size = pubSub->size; ++ do ++ { ++ WINPR_ASSERT(new_size <= SIZE_MAX - 128ull); ++ new_size += 128ull; ++ } while (new_size <= required); + +- new_size = pubSub->size * 2; +- new_event = (wEventType*)realloc(pubSub->events, new_size * sizeof(wEventType)); ++ wEventType* new_event = (wEventType*)realloc(pubSub->events, new_size * sizeof(wEventType)); + if (!new_event) + goto fail; + pubSub->size = new_size; +diff --git a/winpr/libwinpr/utils/collections/Stack.c b/winpr/libwinpr/utils/collections/Stack.c +index 13f6ba17326a..1f041d5bc860 100644 +--- a/winpr/libwinpr/utils/collections/Stack.c ++++ b/winpr/libwinpr/utils/collections/Stack.c +@@ -141,9 +141,15 @@ void Stack_Push(wStack* stack, void* obj) + if (stack->synchronized) + EnterCriticalSection(&stack->lock); + +- if ((stack->size + 1) >= stack->capacity) ++ WINPR_ASSERT(stack->size < SIZE_MAX); ++ if ((stack->size + 1ull) >= stack->capacity) + { +- const size_t new_cap = stack->capacity * 2; ++ size_t new_cap = stack->capacity; ++ do ++ { ++ WINPR_ASSERT(new_cap <= SIZE_MAX - 128ull); ++ new_cap += 128ull; ++ } while (new_cap <= stack->size + 1ull); + void** new_arr = (void**)realloc((void*)stack->array, sizeof(void*) * new_cap); + + if (!new_arr) +diff --git a/winpr/libwinpr/utils/stream.c b/winpr/libwinpr/utils/stream.c +index 7a43e57e1951..cbfbbfa161d6 100644 +--- a/winpr/libwinpr/utils/stream.c ++++ b/winpr/libwinpr/utils/stream.c +@@ -46,20 +46,19 @@ BOOL Stream_EnsureCapacity(wStream* s, size_t size) + WINPR_ASSERT(s); + if (s->capacity < size) + { +- size_t position = 0; +- size_t old_capacity = 0; +- size_t new_capacity = 0; + BYTE* new_buf = NULL; + +- old_capacity = s->capacity; +- new_capacity = old_capacity; ++ size_t old_capacity = s->capacity; ++ size_t new_capacity = old_capacity; + + do + { +- new_capacity *= 2; +- } while (new_capacity < size); ++ if (new_capacity > SIZE_MAX - 128ull) ++ return FALSE; ++ new_capacity += 128ull; ++ } while (new_capacity <= size); + +- position = Stream_GetPosition(s); ++ const size_t position = Stream_GetPosition(s); + + if (!s->isOwner) + { +diff --git a/winpr/tools/makecert/makecert.c b/winpr/tools/makecert/makecert.c +index 94795160e70f..f9e3575117b6 100644 +--- a/winpr/tools/makecert/makecert.c ++++ b/winpr/tools/makecert/makecert.c +@@ -78,12 +78,9 @@ static char* makecert_read_str(BIO* bio, size_t* pOffset) + + while (offset >= length) + { +- size_t new_len = 0; + size_t readBytes = 0; + char* new_str = NULL; +- new_len = length * 2; +- if (new_len == 0) +- new_len = 2048; ++ size_t new_len = length + 2048ull; + + if (new_len > INT_MAX) + { \ No newline at end of file diff --git a/debian/patches/CVE-2026-29775.patch b/debian/patches/CVE-2026-29775.patch new file mode 100644 index 0000000..6d29933 --- /dev/null +++ b/debian/patches/CVE-2026-29775.patch @@ -0,0 +1,25 @@ +From ffad58fd2b329efd81a3239e9d7e3c927b8e503f Mon Sep 17 00:00:00 2001 +From: Armin Novak +Date: Sat, 28 Feb 2026 11:38:55 +0100 +Subject: [PATCH] [cache,bitmap] overallocate bitmap cache + +--- + libfreerdp/cache/bitmap.c | 5 ++++- + 1 file changed, 4 insertions(+), 1 deletion(-) + +diff --git a/libfreerdp/cache/bitmap.c b/libfreerdp/cache/bitmap.c +index 352917339ded..ed7c7a141c57 100644 +--- a/libfreerdp/cache/bitmap.c ++++ b/libfreerdp/cache/bitmap.c +@@ -373,7 +373,10 @@ rdpBitmapCache* bitmap_cache_new(rdpContext* context) + const UINT32 BitmapCacheV2NumCells = + freerdp_settings_get_uint32(settings, FreeRDP_BitmapCacheV2NumCells); + bitmapCache->context = context; +- bitmapCache->cells = (BITMAP_V2_CELL*)calloc(BitmapCacheV2NumCells, sizeof(BITMAP_V2_CELL)); ++ ++ /* overallocate by 1. older RDP servers do send a off by 1 cache index. */ ++ bitmapCache->cells = ++ (BITMAP_V2_CELL*)calloc(BitmapCacheV2NumCells + 1ull, sizeof(BITMAP_V2_CELL)); + + if (!bitmapCache->cells) + goto fail; \ No newline at end of file diff --git a/debian/patches/CVE-2026-29776.patch b/debian/patches/CVE-2026-29776.patch new file mode 100644 index 0000000..27bd92d --- /dev/null +++ b/debian/patches/CVE-2026-29776.patch @@ -0,0 +1,22 @@ +From a9e0abf2eac8c2e370fa155bf1abb9d044c0ca8a Mon Sep 17 00:00:00 2001 +From: Armin Novak +Date: Tue, 3 Mar 2026 13:58:09 +0100 +Subject: [PATCH] [core,orders] improve input validation + +check length before subtracting. Might underflow and be cought by the +next check, but lets be strict. +--- + libfreerdp/core/orders.c | 2 ++ + 1 file changed, 2 insertions(+) + +--- freerdp2.orig/libfreerdp/core/orders.c ++++ freerdp2/libfreerdp/core/orders.c +@@ -2142,6 +2142,8 @@ static CACHE_BITMAP_ORDER* update_read_c + goto fail; + + Stream_Read(s, bitmapComprHdr, 8); /* bitmapComprHdr (8 bytes) */ ++ if (cache_bitmap->bitmapLength < 8) ++ goto fail; + cache_bitmap->bitmapLength -= 8; + } + } diff --git a/debian/patches/CVE-2026-31806.patch b/debian/patches/CVE-2026-31806.patch new file mode 100644 index 0000000..70c5437 --- /dev/null +++ b/debian/patches/CVE-2026-31806.patch @@ -0,0 +1,34 @@ +From 83d9aedea278a74af3e490ff5eeb889c016dbb2b Mon Sep 17 00:00:00 2001 +From: Armin Novak +Date: Mon, 9 Mar 2026 08:11:19 +0100 +Subject: [PATCH] [codec,nsc] limit copy area in nsc_process_message + +the rectangle decoded might not fit into the destination buffer. Limit +width and height of the area to copy to the one fitting. +--- + libfreerdp/codec/nsc.c | 14 +++++++++++--- + 1 file changed, 11 insertions(+), 3 deletions(-) + +diff --git a/libfreerdp/codec/nsc.c b/libfreerdp/codec/nsc.c +index ec9513ee83a4..4f3c1f912796 100644 +--- a/libfreerdp/codec/nsc.c ++++ b/libfreerdp/codec/nsc.c +@@ -527,7 +527,15 @@ BOOL nsc_process_message(NSC_CONTEXT* WINPR_RESTRICT context, UINT16 bpp, UINT32 + return FALSE; + } + +- return (freerdp_image_copy_no_overlap(pDstData, DstFormat, nDstStride, nXDst, nYDst, width, +- height, context->BitmapData, PIXEL_FORMAT_BGRA32, 0, 0, 0, +- nullptr, 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; ++ ++ return (freerdp_image_copy_no_overlap(pDstData, DstFormat, nDstStride, nXDst, nYDst, cwidth, ++ cheight, context->BitmapData, PIXEL_FORMAT_BGRA32, 0, 0, ++ 0, nullptr, flip)); + } \ No newline at end of file diff --git a/debian/patches/CVE-2026-31883.patch b/debian/patches/CVE-2026-31883.patch new file mode 100644 index 0000000..db6ad26 --- /dev/null +++ b/debian/patches/CVE-2026-31883.patch @@ -0,0 +1,216 @@ +From 16df2300e1e3f5a51f68fb1626429e58b531b7c8 Mon Sep 17 00:00:00 2001 +From: Armin Novak +Date: Tue, 10 Mar 2026 10:21:40 +0100 +Subject: [PATCH] [codec,dsp] fix array bounds checks + +* assert array indices where caller value is an internal constant +* add missing length/bounds checks +--- + libfreerdp/codec/dsp.c | 79 +++++++++++++++++++++++++++++++++++++----- + 1 file changed, 70 insertions(+), 9 deletions(-) + +diff --git a/libfreerdp/codec/dsp.c b/libfreerdp/codec/dsp.c +index 0add431af202..17ce5a6fa97d 100644 +--- a/libfreerdp/codec/dsp.c ++++ b/libfreerdp/codec/dsp.c +@@ -323,7 +323,14 @@ static const INT16 ima_step_size_table[] = { + static UINT16 dsp_decode_ima_adpcm_sample(ADPCM* WINPR_RESTRICT adpcm, unsigned int channel, + BYTE sample) + { +- const INT32 ss = ima_step_size_table[adpcm->ima.last_step[channel]]; ++ WINPR_ASSERT(channel < ARRAYSIZE(adpcm->ima.last_step)); ++ WINPR_ASSERT(channel < ARRAYSIZE(adpcm->ima.last_sample)); ++ ++ const INT16 offset = adpcm->ima.last_step[channel]; ++ WINPR_ASSERT(offset >= 0); ++ WINPR_ASSERT(offset < ARRAYSIZE(ima_step_size_table)); ++ ++ const INT32 ss = ima_step_size_table[offset]; + INT32 d = (ss >> 3); + + if (sample & 1) +@@ -346,6 +353,8 @@ static UINT16 dsp_decode_ima_adpcm_sample(ADPCM* WINPR_RESTRICT adpcm, unsigned + d = 32767; + + adpcm->ima.last_sample[channel] = (INT16)d; ++ ++ WINPR_ASSERT(sample < ARRAYSIZE(ima_step_index_table)); + adpcm->ima.last_step[channel] = adpcm->ima.last_step[channel] + ima_step_index_table[sample]; + + if (adpcm->ima.last_step[channel] < 0) +@@ -388,6 +397,9 @@ static BOOL freerdp_dsp_decode_ima_adpcm(FREERDP_DSP_CONTEXT* WINPR_RESTRICT con + { + if (size % block_size == 0) + { ++ if (size < 4) ++ return FALSE; ++ + context->adpcm.ima.last_sample[0] = + (INT16)(((UINT16)(*src)) | (((UINT16)(*(src + 1))) << 8)); + context->adpcm.ima.last_step[0] = (INT16)(*(src + 2)); +@@ -397,6 +409,8 @@ static BOOL freerdp_dsp_decode_ima_adpcm(FREERDP_DSP_CONTEXT* WINPR_RESTRICT con + + if (channels > 1) + { ++ if (size < 4) ++ return FALSE; + context->adpcm.ima.last_sample[1] = + (INT16)(((UINT16)(*src)) | (((UINT16)(*(src + 1))) << 8)); + context->adpcm.ima.last_step[1] = (INT16)(*(src + 2)); +@@ -408,6 +422,8 @@ static BOOL freerdp_dsp_decode_ima_adpcm(FREERDP_DSP_CONTEXT* WINPR_RESTRICT con + + if (channels > 1) + { ++ if (size < 8) ++ return FALSE; + for (size_t i = 0; i < 8; i++) + { + BYTE* dst = Stream_Pointer(out); +@@ -436,6 +452,8 @@ static BOOL freerdp_dsp_decode_ima_adpcm(FREERDP_DSP_CONTEXT* WINPR_RESTRICT con + } + else + { ++ if (size < 1) ++ return FALSE; + BYTE* dst = Stream_Pointer(out); + if (!Stream_SafeSeek(out, 4)) + return FALSE; +@@ -778,7 +796,14 @@ static const struct + + static BYTE dsp_encode_ima_adpcm_sample(ADPCM* WINPR_RESTRICT adpcm, size_t channel, INT16 sample) + { +- INT32 ss = ima_step_size_table[adpcm->ima.last_step[channel]]; ++ WINPR_ASSERT(channel < ARRAYSIZE(adpcm->ima.last_step)); ++ WINPR_ASSERT(channel < ARRAYSIZE(adpcm->ima.last_sample)); ++ ++ const INT16 offset = adpcm->ima.last_step[channel]; ++ WINPR_ASSERT(offset >= 0); ++ WINPR_ASSERT(offset < ARRAYSIZE(ima_step_size_table)); ++ ++ INT32 ss = ima_step_size_table[offset]; + INT32 e = sample - adpcm->ima.last_sample[channel]; + INT32 d = e; + INT32 diff = ss >> 3; +@@ -825,6 +850,8 @@ static BYTE dsp_encode_ima_adpcm_sample(ADPCM* WINPR_RESTRICT adpcm, size_t chan + diff = 32767; + + adpcm->ima.last_sample[channel] = (INT16)diff; ++ ++ WINPR_ASSERT(enc < ARRAYSIZE(ima_step_index_table)); + adpcm->ima.last_step[channel] = adpcm->ima.last_step[channel] + ima_step_index_table[enc]; + + if (adpcm->ima.last_step[channel] < 0) +@@ -926,11 +953,22 @@ static const INT32 ms_adpcm_coeffs2[7] = { 0, -256, 0, 64, 0, -208, -232 }; + static inline INT16 freerdp_dsp_decode_ms_adpcm_sample(ADPCM* WINPR_RESTRICT adpcm, BYTE sample, + size_t channel) + { ++ WINPR_ASSERT(channel < ARRAYSIZE(adpcm->ms.sample1)); ++ WINPR_ASSERT(channel < ARRAYSIZE(adpcm->ms.sample2)); ++ WINPR_ASSERT(channel < ARRAYSIZE(adpcm->ms.delta)); ++ WINPR_ASSERT(channel < ARRAYSIZE(adpcm->ms.predictor)); ++ + const INT8 nibble = (INT8)((sample & 0x08) ? (sample - 16) : sample); ++ const BYTE predictor = adpcm->ms.predictor[channel]; ++ INT32 coeff1 = 0; ++ if (predictor < ARRAYSIZE(ms_adpcm_coeffs1)) ++ coeff1 = ms_adpcm_coeffs1[predictor]; ++ ++ INT32 coeff2 = 0; ++ if (predictor < ARRAYSIZE(ms_adpcm_coeffs2)) ++ coeff2 = ms_adpcm_coeffs2[predictor]; + INT32 presample = +- ((adpcm->ms.sample1[channel] * ms_adpcm_coeffs1[adpcm->ms.predictor[channel]]) + +- (adpcm->ms.sample2[channel] * ms_adpcm_coeffs2[adpcm->ms.predictor[channel]])) / +- 256; ++ ((adpcm->ms.sample1[channel] * coeff1) + (adpcm->ms.sample2[channel] * coeff2)) / 256; + presample += nibble * adpcm->ms.delta[channel]; + + if (presample > 32767) +@@ -940,7 +978,12 @@ static inline INT16 freerdp_dsp_decode_ms_adpcm_sample(ADPCM* WINPR_RESTRICT adp + + adpcm->ms.sample2[channel] = adpcm->ms.sample1[channel]; + adpcm->ms.sample1[channel] = presample; +- adpcm->ms.delta[channel] = adpcm->ms.delta[channel] * ms_adpcm_adaptation_table[sample] / 256; ++ ++ INT32 tableval = 0; ++ if (sample < ARRAYSIZE(ms_adpcm_adaptation_table)) ++ tableval = ms_adpcm_adaptation_table[sample]; ++ ++ adpcm->ms.delta[channel] = adpcm->ms.delta[channel] * tableval / 256; + + if (adpcm->ms.delta[channel] < 16) + adpcm->ms.delta[channel] = 16; +@@ -981,6 +1024,9 @@ static BOOL freerdp_dsp_decode_ms_adpcm(FREERDP_DSP_CONTEXT* WINPR_RESTRICT cont + { + if (channels > 1) + { ++ if (size < 14) ++ return FALSE; ++ + context->adpcm.ms.predictor[0] = *src++; + context->adpcm.ms.predictor[1] = *src++; + context->adpcm.ms.delta[0] = read_int16(src); +@@ -1003,6 +1049,9 @@ static BOOL freerdp_dsp_decode_ms_adpcm(FREERDP_DSP_CONTEXT* WINPR_RESTRICT cont + } + else + { ++ if (size < 7) ++ return FALSE; ++ + context->adpcm.ms.predictor[0] = *src++; + context->adpcm.ms.delta[0] = read_int16(src); + src += 2; +@@ -1019,6 +1068,8 @@ static BOOL freerdp_dsp_decode_ms_adpcm(FREERDP_DSP_CONTEXT* WINPR_RESTRICT cont + if (channels > 1) + { + { ++ if (size < 1) ++ return FALSE; + const BYTE sample = *src++; + size--; + Stream_Write_INT16( +@@ -1027,6 +1078,8 @@ static BOOL freerdp_dsp_decode_ms_adpcm(FREERDP_DSP_CONTEXT* WINPR_RESTRICT cont + out, freerdp_dsp_decode_ms_adpcm_sample(&context->adpcm, sample & 0x0F, 1)); + } + { ++ if (size < 1) ++ return FALSE; + const BYTE sample = *src++; + size--; + Stream_Write_INT16( +@@ -1037,6 +1090,8 @@ static BOOL freerdp_dsp_decode_ms_adpcm(FREERDP_DSP_CONTEXT* WINPR_RESTRICT cont + } + else + { ++ if (size < 1) ++ return FALSE; + const BYTE sample = *src++; + size--; + Stream_Write_INT16(out, +@@ -1050,8 +1105,13 @@ static BOOL freerdp_dsp_decode_ms_adpcm(FREERDP_DSP_CONTEXT* WINPR_RESTRICT cont + } + + static BYTE freerdp_dsp_encode_ms_adpcm_sample(ADPCM* WINPR_RESTRICT adpcm, INT32 sample, +- int channel) ++ size_t channel) + { ++ WINPR_ASSERT(channel < ARRAYSIZE(adpcm->ms.sample1)); ++ WINPR_ASSERT(channel < ARRAYSIZE(adpcm->ms.sample2)); ++ WINPR_ASSERT(channel < ARRAYSIZE(adpcm->ms.delta)); ++ WINPR_ASSERT(channel < ARRAYSIZE(adpcm->ms.predictor)); ++ + INT32 presample = + ((adpcm->ms.sample1[channel] * ms_adpcm_coeffs1[adpcm->ms.predictor[channel]]) + + (adpcm->ms.sample2[channel] * ms_adpcm_coeffs2[adpcm->ms.predictor[channel]])) / +@@ -1075,8 +1135,9 @@ static BYTE freerdp_dsp_encode_ms_adpcm_sample(ADPCM* WINPR_RESTRICT adpcm, INT3 + + adpcm->ms.sample2[channel] = adpcm->ms.sample1[channel]; + adpcm->ms.sample1[channel] = presample; +- adpcm->ms.delta[channel] = +- adpcm->ms.delta[channel] * ms_adpcm_adaptation_table[(((BYTE)errordelta) & 0x0F)] / 256; ++ const size_t offset = (((BYTE)errordelta) & 0x0F); ++ WINPR_ASSERT(offset < ARRAYSIZE(ms_adpcm_adaptation_table)); ++ adpcm->ms.delta[channel] = adpcm->ms.delta[channel] * ms_adpcm_adaptation_table[offset] / 256; + + if (adpcm->ms.delta[channel] < 16) + adpcm->ms.delta[channel] = 16; \ No newline at end of file diff --git a/debian/patches/CVE-2026-31884.patch b/debian/patches/CVE-2026-31884.patch new file mode 100644 index 0000000..cf727eb --- /dev/null +++ b/debian/patches/CVE-2026-31884.patch @@ -0,0 +1,266 @@ +From 03b48b3601d867afccac1cdc6081de7a275edce7 Mon Sep 17 00:00:00 2001 +From: Armin Novak +Date: Tue, 10 Mar 2026 09:57:16 +0100 +Subject: [PATCH] [codec,dsp] add format checks + +To avoid issues with invalid audio format settings always check before +use. +--- + libfreerdp/codec/dsp.c | 148 +++++++++++++++++++++++++++++++---------- + 1 file changed, 112 insertions(+), 36 deletions(-) + +diff --git a/libfreerdp/codec/dsp.c b/libfreerdp/codec/dsp.c +index dfbdd6779d93..0add431af202 100644 +--- a/libfreerdp/codec/dsp.c ++++ b/libfreerdp/codec/dsp.c +@@ -356,11 +356,28 @@ static UINT16 dsp_decode_ima_adpcm_sample(ADPCM* WINPR_RESTRICT adpcm, unsigned + return (UINT16)d; + } + ++static BOOL valid_ima_adpcm_format(const FREERDP_DSP_CONTEXT* WINPR_RESTRICT context) ++{ ++ WINPR_ASSERT(context); ++ if (context->common.format.wFormatTag != WAVE_FORMAT_DVI_ADPCM) ++ return FALSE; ++ if (context->common.format.nBlockAlign <= 4ULL) ++ return FALSE; ++ if (context->common.format.nChannels < 1) ++ return FALSE; ++ if (context->common.format.wBitsPerSample == 0) ++ return FALSE; ++ return TRUE; ++} ++ + static BOOL freerdp_dsp_decode_ima_adpcm(FREERDP_DSP_CONTEXT* WINPR_RESTRICT context, + const BYTE* WINPR_RESTRICT src, size_t size, + wStream* WINPR_RESTRICT out) + { +- size_t out_size = size * 4; ++ if (!valid_ima_adpcm_format(context)) ++ return FALSE; ++ ++ size_t out_size = size * 4ull; + const UINT32 block_size = context->common.format.nBlockAlign; + const UINT32 channels = context->common.format.nChannels; + +@@ -503,27 +520,38 @@ static BOOL freerdp_dsp_encode_gsm610(FREERDP_DSP_CONTEXT* WINPR_RESTRICT contex + #endif + + #if defined(WITH_LAME) ++static BOOL valid_mp3_format(const FREERDP_DSP_CONTEXT* WINPR_RESTRICT context) ++{ ++ WINPR_ASSERT(context); ++ if (context->common.format.wFormatTag != WAVE_FORMAT_MPEGLAYER3) ++ return FALSE; ++ if (context->common.format.nChannels < 1) ++ return FALSE; ++ if (context->common.format.wBitsPerSample == 0) ++ return FALSE; ++ if (context->common.format.nSamplesPerSec == 0) ++ return FALSE; ++ return TRUE; ++} ++ + static BOOL freerdp_dsp_decode_mp3(FREERDP_DSP_CONTEXT* WINPR_RESTRICT context, + const BYTE* WINPR_RESTRICT src, size_t size, + wStream* WINPR_RESTRICT out) + { +- int rc; +- short* pcm_l; +- short* pcm_r; +- size_t buffer_size; +- + if (!context || !src || !out) + return FALSE; +- +- buffer_size = 2 * context->common.format.nChannels * context->common.format.nSamplesPerSec; ++ if (!valid_mp3_format(context)) ++ return FALSE; ++ const size_t buffer_size = ++ 2 * context->common.format.nChannels * context->common.format.nSamplesPerSec; + + if (!Stream_EnsureCapacity(context->common.buffer, 2 * buffer_size)) + return FALSE; + +- pcm_l = Stream_BufferAs(context->common.buffer, short); +- pcm_r = Stream_BufferAs(context->common.buffer, short) + buffer_size; +- rc = hip_decode(context->hip, (unsigned char*)/* API is not modifying content */ src, size, +- pcm_l, pcm_r); ++ short* pcm_l = Stream_BufferAs(context->common.buffer, short); ++ short* pcm_r = Stream_BufferAs(context->common.buffer, short) + buffer_size; ++ const int rc = hip_decode(context->hip, (unsigned char*)/* API is not modifying content */ src, ++ size, pcm_l, pcm_r); + + if (rc <= 0) + return FALSE; +@@ -544,13 +572,13 @@ static BOOL freerdp_dsp_encode_mp3(FREERDP_DSP_CONTEXT* WINPR_RESTRICT context, + const BYTE* WINPR_RESTRICT src, size_t size, + wStream* WINPR_RESTRICT out) + { +- size_t samples_per_channel; +- int rc; +- + if (!context || !src || !out) + return FALSE; + +- samples_per_channel = ++ if (!valid_mp3_format(context)) ++ return FALSE; ++ ++ size_t samples_per_channel = + size / context->common.format.nChannels / context->common.format.wBitsPerSample / 8; + + /* Ensure worst case buffer size for mp3 stream taken from LAME header */ +@@ -558,8 +586,9 @@ static BOOL freerdp_dsp_encode_mp3(FREERDP_DSP_CONTEXT* WINPR_RESTRICT context, + return FALSE; + + samples_per_channel = size / 2 /* size of a sample */ / context->common.format.nChannels; +- rc = lame_encode_buffer_interleaved(context->lame, (short*)src, samples_per_channel, +- Stream_Pointer(out), Stream_GetRemainingCapacity(out)); ++ const int rc = ++ lame_encode_buffer_interleaved(context->lame, (short*)src, samples_per_channel, ++ Stream_Pointer(out), Stream_GetRemainingCapacity(out)); + + if (rc < 0) + return FALSE; +@@ -810,6 +839,8 @@ static BOOL freerdp_dsp_encode_ima_adpcm(FREERDP_DSP_CONTEXT* WINPR_RESTRICT con + const BYTE* WINPR_RESTRICT src, size_t size, + wStream* WINPR_RESTRICT out) + { ++ if (!valid_ima_adpcm_format(context)) ++ return FALSE; + if (!Stream_EnsureRemainingCapacity(out, size)) + return FALSE; + if (!Stream_EnsureRemainingCapacity(context->common.buffer, size + 64)) +@@ -917,10 +948,26 @@ static inline INT16 freerdp_dsp_decode_ms_adpcm_sample(ADPCM* WINPR_RESTRICT adp + return (INT16)presample; + } + ++static BOOL valid_ms_adpcm_format(const FREERDP_DSP_CONTEXT* WINPR_RESTRICT context) ++{ ++ WINPR_ASSERT(context); ++ if (context->common.format.wFormatTag != WAVE_FORMAT_ADPCM) ++ return FALSE; ++ if (context->common.format.nBlockAlign <= 4ULL) ++ return FALSE; ++ if (context->common.format.nChannels < 1) ++ return FALSE; ++ if (context->common.format.wBitsPerSample == 0) ++ return FALSE; ++ return TRUE; ++} ++ + static BOOL freerdp_dsp_decode_ms_adpcm(FREERDP_DSP_CONTEXT* WINPR_RESTRICT context, + const BYTE* WINPR_RESTRICT src, size_t size, + wStream* WINPR_RESTRICT out) + { ++ if (!valid_ms_adpcm_format(context)) ++ return FALSE; + const size_t out_size = size * 4; + const UINT32 channels = context->common.format.nChannels; + const UINT32 block_size = context->common.format.nBlockAlign; +@@ -1041,6 +1088,9 @@ static BOOL freerdp_dsp_encode_ms_adpcm(FREERDP_DSP_CONTEXT* WINPR_RESTRICT cont + const BYTE* WINPR_RESTRICT src, size_t size, + wStream* WINPR_RESTRICT out) + { ++ if (!valid_ms_adpcm_format(context)) ++ return FALSE; ++ + const size_t step = 8 + ((context->common.format.nChannels > 1) ? 4 : 0); + + if (!Stream_EnsureRemainingCapacity(out, size)) +@@ -1487,21 +1537,44 @@ BOOL freerdp_dsp_context_reset(FREERDP_DSP_CONTEXT* WINPR_RESTRICT context, + + context->common.format = *targetFormat; + +- if (context->common.format.wFormatTag == WAVE_FORMAT_DVI_ADPCM) ++ switch (context->common.format.wFormatTag) + { +- size_t min_frame_data = 1ull * context->common.format.wBitsPerSample * +- context->common.format.nChannels * FramesPerPacket; +- size_t data_per_block = +- (1ULL * context->common.format.nBlockAlign - 4ULL * context->common.format.nChannels) * +- 8ULL; +- size_t nb_block_per_packet = min_frame_data / data_per_block; +- +- if (min_frame_data % data_per_block) +- nb_block_per_packet++; +- +- context->adpcm.ima.packet_size = nb_block_per_packet * context->common.format.nBlockAlign; +- Stream_EnsureCapacity(context->common.buffer, context->adpcm.ima.packet_size); +- Stream_ResetPosition(context->common.buffer); ++#if defined(WITH_LAME) ++ case WAVE_FORMAT_MPEGLAYER3: ++ if (!valid_mp3_format(context)) ++ return FALSE; ++ break; ++#endif ++ case WAVE_FORMAT_ADPCM: ++ if (!valid_ms_adpcm_format(context)) ++ return FALSE; ++ break; ++ case WAVE_FORMAT_DVI_ADPCM: ++ { ++ if (!valid_ima_adpcm_format(context)) ++ return FALSE; ++ if (FramesPerPacket == 0) ++ return FALSE; ++ ++ const size_t min_frame_data = 1ull * context->common.format.wBitsPerSample * ++ context->common.format.nChannels * FramesPerPacket; ++ const size_t data_per_block = (1ULL * context->common.format.nBlockAlign - ++ 4ULL * context->common.format.nChannels) * ++ 8ULL; ++ size_t nb_block_per_packet = min_frame_data / data_per_block; ++ ++ if (min_frame_data % data_per_block) ++ nb_block_per_packet++; ++ ++ context->adpcm.ima.packet_size = ++ nb_block_per_packet * context->common.format.nBlockAlign; ++ if (!Stream_EnsureCapacity(context->common.buffer, context->adpcm.ima.packet_size)) ++ return FALSE; ++ Stream_ResetPosition(context->common.buffer); ++ } ++ break; ++ default: ++ break; + } + + #if defined(WITH_OPUS) +@@ -1544,7 +1617,7 @@ BOOL freerdp_dsp_context_reset(FREERDP_DSP_CONTEXT* WINPR_RESTRICT context, + + if (context->common.encoder) + { +- faacEncConfigurationPtr cfg; ++ faacEncConfigurationPtr cfg = nullptr; + + if (context->faac) + faacEncClose(context->faac); +@@ -1561,20 +1634,23 @@ BOOL freerdp_dsp_context_reset(FREERDP_DSP_CONTEXT* WINPR_RESTRICT context, + cfg->mpegVersion = MPEG4; + cfg->useTns = 1; + cfg->bandWidth = targetFormat->nAvgBytesPerSec; +- faacEncSetConfiguration(context->faac, cfg); ++ const int rc = faacEncSetConfiguration(context->faac, cfg); ++ if (rc <= 0) ++ return FALSE; + } + + #endif + #if defined(WITH_SOXR) + { + soxr_io_spec_t iospec = soxr_io_spec(SOXR_INT16, SOXR_INT16); +- soxr_error_t error; ++ soxr_error_t error = nullptr; ++ + soxr_delete(context->sox); + context->sox = + soxr_create(context->common.format.nSamplesPerSec, targetFormat->nSamplesPerSec, + targetFormat->nChannels, &error, &iospec, nullptr, nullptr); + +- if (!context->sox || (error != 0)) ++ if (!context->sox || (error != nullptr)) + return FALSE; + } + #endif \ No newline at end of file diff --git a/debian/patches/CVE-2026-31885.patch b/debian/patches/CVE-2026-31885.patch new file mode 100644 index 0000000..db6ad26 --- /dev/null +++ b/debian/patches/CVE-2026-31885.patch @@ -0,0 +1,216 @@ +From 16df2300e1e3f5a51f68fb1626429e58b531b7c8 Mon Sep 17 00:00:00 2001 +From: Armin Novak +Date: Tue, 10 Mar 2026 10:21:40 +0100 +Subject: [PATCH] [codec,dsp] fix array bounds checks + +* assert array indices where caller value is an internal constant +* add missing length/bounds checks +--- + libfreerdp/codec/dsp.c | 79 +++++++++++++++++++++++++++++++++++++----- + 1 file changed, 70 insertions(+), 9 deletions(-) + +diff --git a/libfreerdp/codec/dsp.c b/libfreerdp/codec/dsp.c +index 0add431af202..17ce5a6fa97d 100644 +--- a/libfreerdp/codec/dsp.c ++++ b/libfreerdp/codec/dsp.c +@@ -323,7 +323,14 @@ static const INT16 ima_step_size_table[] = { + static UINT16 dsp_decode_ima_adpcm_sample(ADPCM* WINPR_RESTRICT adpcm, unsigned int channel, + BYTE sample) + { +- const INT32 ss = ima_step_size_table[adpcm->ima.last_step[channel]]; ++ WINPR_ASSERT(channel < ARRAYSIZE(adpcm->ima.last_step)); ++ WINPR_ASSERT(channel < ARRAYSIZE(adpcm->ima.last_sample)); ++ ++ const INT16 offset = adpcm->ima.last_step[channel]; ++ WINPR_ASSERT(offset >= 0); ++ WINPR_ASSERT(offset < ARRAYSIZE(ima_step_size_table)); ++ ++ const INT32 ss = ima_step_size_table[offset]; + INT32 d = (ss >> 3); + + if (sample & 1) +@@ -346,6 +353,8 @@ static UINT16 dsp_decode_ima_adpcm_sample(ADPCM* WINPR_RESTRICT adpcm, unsigned + d = 32767; + + adpcm->ima.last_sample[channel] = (INT16)d; ++ ++ WINPR_ASSERT(sample < ARRAYSIZE(ima_step_index_table)); + adpcm->ima.last_step[channel] = adpcm->ima.last_step[channel] + ima_step_index_table[sample]; + + if (adpcm->ima.last_step[channel] < 0) +@@ -388,6 +397,9 @@ static BOOL freerdp_dsp_decode_ima_adpcm(FREERDP_DSP_CONTEXT* WINPR_RESTRICT con + { + if (size % block_size == 0) + { ++ if (size < 4) ++ return FALSE; ++ + context->adpcm.ima.last_sample[0] = + (INT16)(((UINT16)(*src)) | (((UINT16)(*(src + 1))) << 8)); + context->adpcm.ima.last_step[0] = (INT16)(*(src + 2)); +@@ -397,6 +409,8 @@ static BOOL freerdp_dsp_decode_ima_adpcm(FREERDP_DSP_CONTEXT* WINPR_RESTRICT con + + if (channels > 1) + { ++ if (size < 4) ++ return FALSE; + context->adpcm.ima.last_sample[1] = + (INT16)(((UINT16)(*src)) | (((UINT16)(*(src + 1))) << 8)); + context->adpcm.ima.last_step[1] = (INT16)(*(src + 2)); +@@ -408,6 +422,8 @@ static BOOL freerdp_dsp_decode_ima_adpcm(FREERDP_DSP_CONTEXT* WINPR_RESTRICT con + + if (channels > 1) + { ++ if (size < 8) ++ return FALSE; + for (size_t i = 0; i < 8; i++) + { + BYTE* dst = Stream_Pointer(out); +@@ -436,6 +452,8 @@ static BOOL freerdp_dsp_decode_ima_adpcm(FREERDP_DSP_CONTEXT* WINPR_RESTRICT con + } + else + { ++ if (size < 1) ++ return FALSE; + BYTE* dst = Stream_Pointer(out); + if (!Stream_SafeSeek(out, 4)) + return FALSE; +@@ -778,7 +796,14 @@ static const struct + + static BYTE dsp_encode_ima_adpcm_sample(ADPCM* WINPR_RESTRICT adpcm, size_t channel, INT16 sample) + { +- INT32 ss = ima_step_size_table[adpcm->ima.last_step[channel]]; ++ WINPR_ASSERT(channel < ARRAYSIZE(adpcm->ima.last_step)); ++ WINPR_ASSERT(channel < ARRAYSIZE(adpcm->ima.last_sample)); ++ ++ const INT16 offset = adpcm->ima.last_step[channel]; ++ WINPR_ASSERT(offset >= 0); ++ WINPR_ASSERT(offset < ARRAYSIZE(ima_step_size_table)); ++ ++ INT32 ss = ima_step_size_table[offset]; + INT32 e = sample - adpcm->ima.last_sample[channel]; + INT32 d = e; + INT32 diff = ss >> 3; +@@ -825,6 +850,8 @@ static BYTE dsp_encode_ima_adpcm_sample(ADPCM* WINPR_RESTRICT adpcm, size_t chan + diff = 32767; + + adpcm->ima.last_sample[channel] = (INT16)diff; ++ ++ WINPR_ASSERT(enc < ARRAYSIZE(ima_step_index_table)); + adpcm->ima.last_step[channel] = adpcm->ima.last_step[channel] + ima_step_index_table[enc]; + + if (adpcm->ima.last_step[channel] < 0) +@@ -926,11 +953,22 @@ static const INT32 ms_adpcm_coeffs2[7] = { 0, -256, 0, 64, 0, -208, -232 }; + static inline INT16 freerdp_dsp_decode_ms_adpcm_sample(ADPCM* WINPR_RESTRICT adpcm, BYTE sample, + size_t channel) + { ++ WINPR_ASSERT(channel < ARRAYSIZE(adpcm->ms.sample1)); ++ WINPR_ASSERT(channel < ARRAYSIZE(adpcm->ms.sample2)); ++ WINPR_ASSERT(channel < ARRAYSIZE(adpcm->ms.delta)); ++ WINPR_ASSERT(channel < ARRAYSIZE(adpcm->ms.predictor)); ++ + const INT8 nibble = (INT8)((sample & 0x08) ? (sample - 16) : sample); ++ const BYTE predictor = adpcm->ms.predictor[channel]; ++ INT32 coeff1 = 0; ++ if (predictor < ARRAYSIZE(ms_adpcm_coeffs1)) ++ coeff1 = ms_adpcm_coeffs1[predictor]; ++ ++ INT32 coeff2 = 0; ++ if (predictor < ARRAYSIZE(ms_adpcm_coeffs2)) ++ coeff2 = ms_adpcm_coeffs2[predictor]; + INT32 presample = +- ((adpcm->ms.sample1[channel] * ms_adpcm_coeffs1[adpcm->ms.predictor[channel]]) + +- (adpcm->ms.sample2[channel] * ms_adpcm_coeffs2[adpcm->ms.predictor[channel]])) / +- 256; ++ ((adpcm->ms.sample1[channel] * coeff1) + (adpcm->ms.sample2[channel] * coeff2)) / 256; + presample += nibble * adpcm->ms.delta[channel]; + + if (presample > 32767) +@@ -940,7 +978,12 @@ static inline INT16 freerdp_dsp_decode_ms_adpcm_sample(ADPCM* WINPR_RESTRICT adp + + adpcm->ms.sample2[channel] = adpcm->ms.sample1[channel]; + adpcm->ms.sample1[channel] = presample; +- adpcm->ms.delta[channel] = adpcm->ms.delta[channel] * ms_adpcm_adaptation_table[sample] / 256; ++ ++ INT32 tableval = 0; ++ if (sample < ARRAYSIZE(ms_adpcm_adaptation_table)) ++ tableval = ms_adpcm_adaptation_table[sample]; ++ ++ adpcm->ms.delta[channel] = adpcm->ms.delta[channel] * tableval / 256; + + if (adpcm->ms.delta[channel] < 16) + adpcm->ms.delta[channel] = 16; +@@ -981,6 +1024,9 @@ static BOOL freerdp_dsp_decode_ms_adpcm(FREERDP_DSP_CONTEXT* WINPR_RESTRICT cont + { + if (channels > 1) + { ++ if (size < 14) ++ return FALSE; ++ + context->adpcm.ms.predictor[0] = *src++; + context->adpcm.ms.predictor[1] = *src++; + context->adpcm.ms.delta[0] = read_int16(src); +@@ -1003,6 +1049,9 @@ static BOOL freerdp_dsp_decode_ms_adpcm(FREERDP_DSP_CONTEXT* WINPR_RESTRICT cont + } + else + { ++ if (size < 7) ++ return FALSE; ++ + context->adpcm.ms.predictor[0] = *src++; + context->adpcm.ms.delta[0] = read_int16(src); + src += 2; +@@ -1019,6 +1068,8 @@ static BOOL freerdp_dsp_decode_ms_adpcm(FREERDP_DSP_CONTEXT* WINPR_RESTRICT cont + if (channels > 1) + { + { ++ if (size < 1) ++ return FALSE; + const BYTE sample = *src++; + size--; + Stream_Write_INT16( +@@ -1027,6 +1078,8 @@ static BOOL freerdp_dsp_decode_ms_adpcm(FREERDP_DSP_CONTEXT* WINPR_RESTRICT cont + out, freerdp_dsp_decode_ms_adpcm_sample(&context->adpcm, sample & 0x0F, 1)); + } + { ++ if (size < 1) ++ return FALSE; + const BYTE sample = *src++; + size--; + Stream_Write_INT16( +@@ -1037,6 +1090,8 @@ static BOOL freerdp_dsp_decode_ms_adpcm(FREERDP_DSP_CONTEXT* WINPR_RESTRICT cont + } + else + { ++ if (size < 1) ++ return FALSE; + const BYTE sample = *src++; + size--; + Stream_Write_INT16(out, +@@ -1050,8 +1105,13 @@ static BOOL freerdp_dsp_decode_ms_adpcm(FREERDP_DSP_CONTEXT* WINPR_RESTRICT cont + } + + static BYTE freerdp_dsp_encode_ms_adpcm_sample(ADPCM* WINPR_RESTRICT adpcm, INT32 sample, +- int channel) ++ size_t channel) + { ++ WINPR_ASSERT(channel < ARRAYSIZE(adpcm->ms.sample1)); ++ WINPR_ASSERT(channel < ARRAYSIZE(adpcm->ms.sample2)); ++ WINPR_ASSERT(channel < ARRAYSIZE(adpcm->ms.delta)); ++ WINPR_ASSERT(channel < ARRAYSIZE(adpcm->ms.predictor)); ++ + INT32 presample = + ((adpcm->ms.sample1[channel] * ms_adpcm_coeffs1[adpcm->ms.predictor[channel]]) + + (adpcm->ms.sample2[channel] * ms_adpcm_coeffs2[adpcm->ms.predictor[channel]])) / +@@ -1075,8 +1135,9 @@ static BYTE freerdp_dsp_encode_ms_adpcm_sample(ADPCM* WINPR_RESTRICT adpcm, INT3 + + adpcm->ms.sample2[channel] = adpcm->ms.sample1[channel]; + adpcm->ms.sample1[channel] = presample; +- adpcm->ms.delta[channel] = +- adpcm->ms.delta[channel] * ms_adpcm_adaptation_table[(((BYTE)errordelta) & 0x0F)] / 256; ++ const size_t offset = (((BYTE)errordelta) & 0x0F); ++ WINPR_ASSERT(offset < ARRAYSIZE(ms_adpcm_adaptation_table)); ++ adpcm->ms.delta[channel] = adpcm->ms.delta[channel] * ms_adpcm_adaptation_table[offset] / 256; + + if (adpcm->ms.delta[channel] < 16) + adpcm->ms.delta[channel] = 16; \ No newline at end of file diff --git a/debian/patches/CVE-2026-31897.patch b/debian/patches/CVE-2026-31897.patch new file mode 100644 index 0000000..f0c7aae --- /dev/null +++ b/debian/patches/CVE-2026-31897.patch @@ -0,0 +1,25 @@ +From cd27c8faca0eeb0d4309cc5837dfdf3c42eba4e7 Mon Sep 17 00:00:00 2001 +From: Armin Novak +Date: Tue, 10 Mar 2026 09:17:23 +0100 +Subject: [PATCH] [codec,planar] add early length check to avoid oob read + +--- + libfreerdp/codec/planar.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/libfreerdp/codec/planar.c b/libfreerdp/codec/planar.c +index ff161bfb75b2..fd2db2f44e28 100644 +--- a/libfreerdp/codec/planar.c ++++ b/libfreerdp/codec/planar.c +@@ -738,9 +738,9 @@ BOOL freerdp_bitmap_decompress_planar(BITMAP_PLANAR_CONTEXT* WINPR_RESTRICT plan + + const BYTE* srcp = pSrcData; + +- if (!pSrcData) ++ if (!pSrcData || (SrcSize < 1)) + { +- WLog_ERR(TAG, "Invalid argument pSrcData=nullptr"); ++ WLog_ERR(TAG, "Invalid argument pSrcData=%p [size=%" PRIu32 "]", pSrcData, SrcSize); + return FALSE; + } + \ No newline at end of file diff --git a/debian/patches/CVE-2026-33952.patch b/debian/patches/CVE-2026-33952.patch new file mode 100644 index 0000000..1194b69 --- /dev/null +++ b/debian/patches/CVE-2026-33952.patch @@ -0,0 +1,30 @@ +From 4ac0b6467d371a1ad47c1f751c5b305e4c068adb Mon Sep 17 00:00:00 2001 +From: Armin Novak +Date: Wed, 25 Mar 2026 09:04:43 +0100 +Subject: [PATCH] [core,gateway] Check rpcconn_common_hdr_t::auth_length is + valid + +Do sanity checks for rpcconn_common_hdr_t::auth_length read from +network, abort if the value is out of range. +--- + libfreerdp/core/gateway/rts.c | 9 +++++++++ + 1 file changed, 9 insertions(+) + +--- freerdp2.orig/libfreerdp/core/gateway/rts.c ++++ freerdp2/libfreerdp/core/gateway/rts.c +@@ -263,6 +263,15 @@ static BOOL rts_read_auth_verifier(wStre + const size_t expected = header->frag_length - header->auth_length - 8; + assert(pos + auth->auth_pad_length == expected); + } ++ if (header->auth_length > header->frag_length - 8ull) ++ { ++ if (!ignoreErrors) ++ WLog_WARN(TAG, ++ "Invalid header->auth_length(%" PRIu16 ") > header->frag_length(%" PRIu16 ++ ") - 8ull", ++ header->frag_length, header->auth_length); ++ return RTS_PDU_FAIL; ++ } + + return TRUE; + } diff --git a/debian/patches/CVE-2026-33977.patch b/debian/patches/CVE-2026-33977.patch new file mode 100644 index 0000000..73e54f1 --- /dev/null +++ b/debian/patches/CVE-2026-33977.patch @@ -0,0 +1,74 @@ +From 9be3f03d94a50892fd58a9f7dee72b2313c69b47 Mon Sep 17 00:00:00 2001 +From: Armin Novak +Date: Wed, 25 Mar 2026 09:23:18 +0100 +Subject: [PATCH] [codec,dsp] fix IMA ADPCM sample clamping + +--- + libfreerdp/codec/dsp.c | 26 ++++++++++++++++++-------- + 1 file changed, 18 insertions(+), 8 deletions(-) + +diff --git a/libfreerdp/codec/dsp.c b/libfreerdp/codec/dsp.c +index 7fef39ef1f6a..b0de39a4cc38 100644 +--- a/libfreerdp/codec/dsp.c ++++ b/libfreerdp/codec/dsp.c +@@ -320,6 +320,17 @@ static const INT16 ima_step_size_table[] = { + 12635, 13899, 15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, 32767 + }; + ++static inline void dsp_ima_clamp_step(ADPCM* WINPR_RESTRICT adpcm, unsigned int channel) ++{ ++ WINPR_ASSERT(adpcm); ++ if (adpcm->ima.last_step[channel] < 0) ++ adpcm->ima.last_step[channel] = 0; ++ ++ const size_t size = ARRAYSIZE(ima_step_size_table); ++ if (adpcm->ima.last_step[channel] > size) ++ adpcm->ima.last_step[channel] = size; ++} ++ + static UINT16 dsp_decode_ima_adpcm_sample(ADPCM* WINPR_RESTRICT adpcm, unsigned int channel, + BYTE sample) + { +@@ -357,10 +368,7 @@ static UINT16 dsp_decode_ima_adpcm_sample(ADPCM* WINPR_RESTRICT adpcm, unsigned + WINPR_ASSERT(sample < ARRAYSIZE(ima_step_index_table)); + adpcm->ima.last_step[channel] = adpcm->ima.last_step[channel] + ima_step_index_table[sample]; + +- if (adpcm->ima.last_step[channel] < 0) +- adpcm->ima.last_step[channel] = 0; +- else if (adpcm->ima.last_step[channel] > 88) +- adpcm->ima.last_step[channel] = 88; ++ dsp_ima_clamp_step(adpcm, channel); + + return (UINT16)d; + } +@@ -403,6 +411,9 @@ static BOOL freerdp_dsp_decode_ima_adpcm(FREERDP_DSP_CONTEXT* WINPR_RESTRICT con + context->adpcm.ima.last_sample[0] = + (INT16)(((UINT16)(*src)) | (((UINT16)(*(src + 1))) << 8)); + context->adpcm.ima.last_step[0] = (INT16)(*(src + 2)); ++ ++ dsp_ima_clamp_step(&context->adpcm, 0); ++ + src += 4; + size -= 4; + out_size -= 16; +@@ -414,6 +425,8 @@ static BOOL freerdp_dsp_decode_ima_adpcm(FREERDP_DSP_CONTEXT* WINPR_RESTRICT con + context->adpcm.ima.last_sample[1] = + (INT16)(((UINT16)(*src)) | (((UINT16)(*(src + 1))) << 8)); + context->adpcm.ima.last_step[1] = (INT16)(*(src + 2)); ++ ++ dsp_ima_clamp_step(&context->adpcm, 1); + src += 4; + size -= 4; + out_size -= 16; +@@ -854,10 +867,7 @@ static BYTE dsp_encode_ima_adpcm_sample(ADPCM* WINPR_RESTRICT adpcm, size_t chan + WINPR_ASSERT(enc < ARRAYSIZE(ima_step_index_table)); + adpcm->ima.last_step[channel] = adpcm->ima.last_step[channel] + ima_step_index_table[enc]; + +- if (adpcm->ima.last_step[channel] < 0) +- adpcm->ima.last_step[channel] = 0; +- else if (adpcm->ima.last_step[channel] > 88) +- adpcm->ima.last_step[channel] = 88; ++ dsp_ima_clamp_step(adpcm, channel); + + return enc; + } \ No newline at end of file diff --git a/debian/patches/CVE-2026-33982.patch b/debian/patches/CVE-2026-33982.patch new file mode 100644 index 0000000..3a535c6 --- /dev/null +++ b/debian/patches/CVE-2026-33982.patch @@ -0,0 +1,41 @@ +From a48dbde2c8a5b8b70a9d1c045d969a71afd6284c Mon Sep 17 00:00:00 2001 +From: Armin Novak +Date: Wed, 25 Mar 2026 09:57:19 +0100 +Subject: [PATCH] [cache,persist] use winpr_aligned_calloc + +Consistently use winpr_aligned_* family for allocating/freeing the +buffers. +--- + libfreerdp/cache/persistent.c | 5 +++-- + 1 file changed, 3 insertions(+), 2 deletions(-) + +diff --git a/libfreerdp/cache/persistent.c b/libfreerdp/cache/persistent.c +index 0e34b59bbef6..5e9744828a49 100644 +--- a/libfreerdp/cache/persistent.c ++++ b/libfreerdp/cache/persistent.c +@@ -39,6 +39,7 @@ struct rdp_persistent_cache + size_t bmpSize; + }; + ++static const size_t PERSIST_ALIGN = 32; + static const char sig_str[] = "RDP8bmp"; + + int persistent_cache_get_version(rdpPersistentCache* persistent) +@@ -155,7 +156,7 @@ static int persistent_cache_read_entry_v3(rdpPersistentCache* persistent, + { + persistent->bmpSize = size; + BYTE* bmpData = (BYTE*)winpr_aligned_recalloc(persistent->bmpData, persistent->bmpSize, +- sizeof(BYTE), 32); ++ sizeof(BYTE), PERSIST_ALIGN); + + if (!bmpData) + return -1; +@@ -350,7 +351,7 @@ rdpPersistentCache* persistent_cache_new(void) + return nullptr; + + persistent->bmpSize = 0x4000; +- persistent->bmpData = calloc(1, persistent->bmpSize); ++ persistent->bmpData = winpr_aligned_calloc(1, persistent->bmpSize, PERSIST_ALIGN); + + if (!persistent->bmpData) + { \ No newline at end of file diff --git a/debian/patches/CVE-2026-33984.patch b/debian/patches/CVE-2026-33984.patch new file mode 100644 index 0000000..8928c98 --- /dev/null +++ b/debian/patches/CVE-2026-33984.patch @@ -0,0 +1,29 @@ +From dc7fdb165095139be779a4000199bc1706b06ad5 Mon Sep 17 00:00:00 2001 +From: Armin Novak +Date: Wed, 25 Mar 2026 09:48:54 +0100 +Subject: [PATCH] [codec,clear] update CLEAR_VBAR_ENTRY::size after alloc + +--- + libfreerdp/codec/clear.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/libfreerdp/codec/clear.c b/libfreerdp/codec/clear.c +index 67ae4f3656f8..b8ae99e80b03 100644 +--- a/libfreerdp/codec/clear.c ++++ b/libfreerdp/codec/clear.c +@@ -567,7 +567,6 @@ static BOOL resize_vbar_entry(CLEAR_CONTEXT* WINPR_RESTRICT clear, + const UINT32 oldPos = vBarEntry->size * bpp; + const UINT32 diffSize = (vBarEntry->count - vBarEntry->size) * bpp; + +- vBarEntry->size = vBarEntry->count; + BYTE* tmp = + (BYTE*)winpr_aligned_recalloc(vBarEntry->pixels, vBarEntry->count, 1ull * bpp, 32); + +@@ -580,6 +579,7 @@ static BOOL resize_vbar_entry(CLEAR_CONTEXT* WINPR_RESTRICT clear, + + memset(&tmp[oldPos], 0, diffSize); + vBarEntry->pixels = tmp; ++ vBarEntry->size = vBarEntry->count; + } + + if (!vBarEntry->pixels && vBarEntry->size) \ No newline at end of file diff --git a/debian/patches/CVE-2026-33985.patch b/debian/patches/CVE-2026-33985.patch new file mode 100644 index 0000000..a67bb0e --- /dev/null +++ b/debian/patches/CVE-2026-33985.patch @@ -0,0 +1,51 @@ +From c49d1ad43b8c7b32794d0250f2623c2dccd7ef25 Mon Sep 17 00:00:00 2001 +From: Armin Novak +Date: Wed, 25 Mar 2026 09:53:38 +0100 +Subject: [PATCH] [codec,clear] Update CLEAR_GLYPH_ENTRY::count after alloc + +Ensure the value is always properly related to an existing buffer. +--- + libfreerdp/codec/clear.c | 22 ++++++++++++++++------ + 1 file changed, 16 insertions(+), 6 deletions(-) + +diff --git a/libfreerdp/codec/clear.c b/libfreerdp/codec/clear.c +index b8ae99e80b03..d9559d54383b 100644 +--- a/libfreerdp/codec/clear.c ++++ b/libfreerdp/codec/clear.c +@@ -981,20 +981,30 @@ static BOOL clear_decompress_glyph_data(CLEAR_CONTEXT* WINPR_RESTRICT clear, + { + const UINT32 bpp = FreeRDPGetBytesPerPixel(clear->format); + CLEAR_GLYPH_ENTRY* glyphEntry = &(clear->GlyphCache[glyphIndex]); +- glyphEntry->count = nWidth * nHeight; ++ const size_t count = 1ull * nWidth * nHeight; ++ const size_t hlimit = SIZE_MAX / ((nWidth > 0) ? nWidth : 1); ++ if ((nWidth == 0) || (nHeight == 0) || (hlimit < nHeight)) ++ { ++ const char* exceeded = (hlimit < nHeight) ? "within" : "outside"; ++ WLog_ERR(TAG, ++ "CLEARCODEC_FLAG_GLYPH_INDEX: nWidth=%" PRIu32 ", nHeight=%" PRIu32 ++ ", nWidth * nHeight is %s allowed range", ++ nWidth, nHeight, exceeded); ++ return FALSE; ++ } + +- if (glyphEntry->count > glyphEntry->size) ++ if (count > glyphEntry->size) + { +- BYTE* tmp = +- winpr_aligned_recalloc(glyphEntry->pixels, glyphEntry->count, 1ull * bpp, 32); ++ BYTE* tmp = winpr_aligned_recalloc(glyphEntry->pixels, count, 1ull * bpp, 32); + + if (!tmp) + { +- WLog_ERR(TAG, "glyphEntry->pixels winpr_aligned_recalloc %" PRIu32 " failed!", +- glyphEntry->count * bpp); ++ WLog_ERR(TAG, "glyphEntry->pixels winpr_aligned_recalloc %" PRIuz " failed!", ++ count * bpp); + return FALSE; + } + ++ glyphEntry->count = WINPR_ASSERTING_INT_CAST(UINT32, count); + glyphEntry->size = glyphEntry->count; + glyphEntry->pixels = (UINT32*)tmp; + } \ No newline at end of file diff --git a/debian/patches/CVE-2026-33987.patch b/debian/patches/CVE-2026-33987.patch new file mode 100644 index 0000000..30b740d --- /dev/null +++ b/debian/patches/CVE-2026-33987.patch @@ -0,0 +1,47 @@ +From 1a890eb43492b5eb707cb3dd6fc908f696e8fc1c Mon Sep 17 00:00:00 2001 +From: Armin Novak +Date: Wed, 25 Mar 2026 09:41:17 +0100 +Subject: [PATCH] [cache,persistent] update PERSISTENT_CACHE_ENTRY::size after + realloc + +Avoid invalid PERSISTENT_CACHE_ENTRY::size values in case realloc fails. +--- + libfreerdp/cache/persistent.c | 8 ++++---- + 1 file changed, 4 insertions(+), 4 deletions(-) + +diff --git a/libfreerdp/cache/persistent.c b/libfreerdp/cache/persistent.c +index c68f5c295bac..0e34b59bbef6 100644 +--- a/libfreerdp/cache/persistent.c ++++ b/libfreerdp/cache/persistent.c +@@ -36,7 +36,7 @@ struct rdp_persistent_cache + int count; + char* filename; + BYTE* bmpData; +- UINT32 bmpSize; ++ size_t bmpSize; + }; + + static const char sig_str[] = "RDP8bmp"; +@@ -149,12 +149,11 @@ static int persistent_cache_read_entry_v3(rdpPersistentCache* persistent, + const UINT64 size = 4ull * entry3.width * entry3.height; + if (size > UINT32_MAX) + return -1; +- entry->size = (UINT32)size; + entry->flags = 0; + +- if (entry->size > persistent->bmpSize) ++ if (size > persistent->bmpSize) + { +- persistent->bmpSize = entry->size; ++ persistent->bmpSize = size; + BYTE* bmpData = (BYTE*)winpr_aligned_recalloc(persistent->bmpData, persistent->bmpSize, + sizeof(BYTE), 32); + +@@ -163,6 +162,7 @@ static int persistent_cache_read_entry_v3(rdpPersistentCache* persistent, + + persistent->bmpData = bmpData; + } ++ entry->size = WINPR_ASSERTING_INT_CAST(UINT32, size); + + entry->data = persistent->bmpData; + \ No newline at end of file diff --git a/debian/patches/CVE-2026-33995.patch b/debian/patches/CVE-2026-33995.patch new file mode 100644 index 0000000..9e9a908 --- /dev/null +++ b/debian/patches/CVE-2026-33995.patch @@ -0,0 +1,155 @@ +From 8078b8af1359055972e4fb2f509f543b69169391 Mon Sep 17 00:00:00 2001 +From: Armin Novak +Date: Wed, 25 Mar 2026 14:00:28 +0100 +Subject: [PATCH] [winpr,sspi] Fix context nullptr handling + +Unify reset of PCredHandle and PCtxtHandle in all +DeleteSecurityContext and FreeCredentialsHandle implementations. +--- + winpr/libwinpr/sspi/CredSSP/credssp.c | 7 +++---- + winpr/libwinpr/sspi/Kerberos/kerberos.c | 5 ++++- + winpr/libwinpr/sspi/NTLM/ntlm.c | 3 ++- + winpr/libwinpr/sspi/Negotiate/negotiate.c | 5 +++-- + winpr/libwinpr/sspi/Schannel/schannel.c | 11 +++++------ + 5 files changed, 17 insertions(+), 14 deletions(-) + +diff --git a/winpr/libwinpr/sspi/CredSSP/credssp.c b/winpr/libwinpr/sspi/CredSSP/credssp.c +index 97b48e2444f1..ab05952c0dc8 100644 +--- a/winpr/libwinpr/sspi/CredSSP/credssp.c ++++ b/winpr/libwinpr/sspi/CredSSP/credssp.c +@@ -194,13 +194,12 @@ static SECURITY_STATUS SEC_ENTRY credssp_QueryCredentialsAttributesA( + + static SECURITY_STATUS SEC_ENTRY credssp_FreeCredentialsHandle(PCredHandle phCredential) + { +- SSPI_CREDENTIALS* credentials = nullptr; +- + if (!phCredential) + return SEC_E_INVALID_HANDLE; + +- credentials = (SSPI_CREDENTIALS*)sspi_SecureHandleGetLowerPointer(phCredential); +- ++ SSPI_CREDENTIALS* credentials = ++ (SSPI_CREDENTIALS*)sspi_SecureHandleGetLowerPointer(phCredential); ++ sspi_SecureHandleInvalidate(phCredential); + if (!credentials) + return SEC_E_INVALID_HANDLE; + +diff --git a/winpr/libwinpr/sspi/Kerberos/kerberos.c b/winpr/libwinpr/sspi/Kerberos/kerberos.c +index b2686e06912f..2be916498abb 100644 +--- a/winpr/libwinpr/sspi/Kerberos/kerberos.c ++++ b/winpr/libwinpr/sspi/Kerberos/kerberos.c +@@ -595,12 +595,12 @@ static SECURITY_STATUS + { + #ifdef WITH_KRB5 + KRB_CREDENTIALS* credentials = sspi_SecureHandleGetLowerPointer(phCredential); ++ sspi_SecureHandleInvalidate(phCredential); + if (!credentials) + return SEC_E_INVALID_HANDLE; + + credentials_unref(credentials); + +- sspi_SecureHandleInvalidate(phCredential); + return SEC_E_OK; + #else + return SEC_E_UNSUPPORTED_FUNCTION; +@@ -1276,6 +1276,7 @@ static SECURITY_STATUS SEC_ENTRY kerberos_InitializeSecurityContextA( + break; + default: + kerberos_ContextFree(context, TRUE); ++ sspi_SecureHandleInvalidate(phNewContext); + break; + } + } +@@ -1596,6 +1597,7 @@ static SECURITY_STATUS SEC_ENTRY kerberos_AcceptSecurityContext( + break; + default: + kerberos_ContextFree(context, TRUE); ++ sspi_SecureHandleInvalidate(phNewContext); + break; + } + } +@@ -1646,6 +1648,7 @@ static SECURITY_STATUS + { + #ifdef WITH_KRB5 + KRB_CONTEXT* context = get_context(phContext); ++ sspi_SecureHandleInvalidate(phContext); + if (!context) + return SEC_E_INVALID_HANDLE; + +diff --git a/winpr/libwinpr/sspi/NTLM/ntlm.c b/winpr/libwinpr/sspi/NTLM/ntlm.c +index 6538d0196c3d..1070997bf93a 100644 +--- a/winpr/libwinpr/sspi/NTLM/ntlm.c ++++ b/winpr/libwinpr/sspi/NTLM/ntlm.c +@@ -472,7 +472,7 @@ static SECURITY_STATUS SEC_ENTRY ntlm_FreeCredentialsHandle(PCredHandle phCreden + + SSPI_CREDENTIALS* credentials = + (SSPI_CREDENTIALS*)sspi_SecureHandleGetLowerPointer(phCredential); +- ++ sspi_SecureHandleInvalidate(phCredential); + if (!credentials) + return SEC_E_INVALID_HANDLE; + +@@ -783,6 +783,7 @@ static SECURITY_STATUS SEC_ENTRY ntlm_InitializeSecurityContextA( + static SECURITY_STATUS SEC_ENTRY ntlm_DeleteSecurityContext(PCtxtHandle phContext) + { + NTLM_CONTEXT* context = (NTLM_CONTEXT*)sspi_SecureHandleGetLowerPointer(phContext); ++ sspi_SecureHandleInvalidate(phContext); + ntlm_ContextFree(context); + return SEC_E_OK; + } +diff --git a/winpr/libwinpr/sspi/Negotiate/negotiate.c b/winpr/libwinpr/sspi/Negotiate/negotiate.c +index 0c8b2a1f88d4..669415914325 100644 +--- a/winpr/libwinpr/sspi/Negotiate/negotiate.c ++++ b/winpr/libwinpr/sspi/Negotiate/negotiate.c +@@ -1282,9 +1282,9 @@ static SECURITY_STATUS SEC_ENTRY negotiate_CompleteAuthToken(PCtxtHandle phConte + + static SECURITY_STATUS SEC_ENTRY negotiate_DeleteSecurityContext(PCtxtHandle phContext) + { +- NEGOTIATE_CONTEXT* context = nullptr; + SECURITY_STATUS status = SEC_E_OK; +- context = (NEGOTIATE_CONTEXT*)sspi_SecureHandleGetLowerPointer(phContext); ++ NEGOTIATE_CONTEXT* context = (NEGOTIATE_CONTEXT*)sspi_SecureHandleGetLowerPointer(phContext); ++ sspi_SecureHandleInvalidate(phContext); + const SecPkg* pkg = nullptr; + + if (!context) +@@ -1569,6 +1569,7 @@ static SECURITY_STATUS SEC_ENTRY negotiate_QueryCredentialsAttributesA( + static SECURITY_STATUS SEC_ENTRY negotiate_FreeCredentialsHandle(PCredHandle phCredential) + { + MechCred* creds = sspi_SecureHandleGetLowerPointer(phCredential); ++ sspi_SecureHandleInvalidate(phCredential); + if (!creds) + return SEC_E_INVALID_HANDLE; + +diff --git a/winpr/libwinpr/sspi/Schannel/schannel.c b/winpr/libwinpr/sspi/Schannel/schannel.c +index 328f1d7fb29b..b167977fb855 100644 +--- a/winpr/libwinpr/sspi/Schannel/schannel.c ++++ b/winpr/libwinpr/sspi/Schannel/schannel.c +@@ -183,13 +183,12 @@ static SECURITY_STATUS SEC_ENTRY schannel_AcquireCredentialsHandleA( + + static SECURITY_STATUS SEC_ENTRY schannel_FreeCredentialsHandle(PCredHandle phCredential) + { +- SCHANNEL_CREDENTIALS* credentials = nullptr; +- + if (!phCredential) + return SEC_E_INVALID_HANDLE; + +- credentials = (SCHANNEL_CREDENTIALS*)sspi_SecureHandleGetLowerPointer(phCredential); +- ++ SCHANNEL_CREDENTIALS* credentials = ++ (SCHANNEL_CREDENTIALS*)sspi_SecureHandleGetLowerPointer(phCredential); ++ sspi_SecureHandleInvalidate(phCredential); + if (!credentials) + return SEC_E_INVALID_HANDLE; + +@@ -289,8 +288,8 @@ static SECURITY_STATUS SEC_ENTRY schannel_AcceptSecurityContext( + + static SECURITY_STATUS SEC_ENTRY schannel_DeleteSecurityContext(PCtxtHandle phContext) + { +- SCHANNEL_CONTEXT* context = nullptr; +- context = (SCHANNEL_CONTEXT*)sspi_SecureHandleGetLowerPointer(phContext); ++ SCHANNEL_CONTEXT* context = (SCHANNEL_CONTEXT*)sspi_SecureHandleGetLowerPointer(phContext); ++ sspi_SecureHandleInvalidate(phContext); + + if (!context) + return SEC_E_INVALID_HANDLE; \ No newline at end of file diff --git a/debian/patches/series b/debian/patches/series new file mode 100644 index 0000000..0f1962e --- /dev/null +++ b/debian/patches/series @@ -0,0 +1,6 @@ +CVE-2026-24675.patch +CVE-2026-24681.patch +CVE-2026-25941.patch +CVE-2026-26986.patch +CVE-2026-29776.patch +CVE-2026-33952.patch