Compare commits

...

No commits in common. "c8" and "c8-beta" have entirely different histories.
c8 ... c8-beta

74 changed files with 6889 additions and 5367 deletions

View File

@ -1 +1 @@
14a73f092e227a77f3d483658033d15d4501a753 SOURCES/FreeRDP-2.11.7.tar.gz
84f51c81388c8b49346b9c5a7c4c982909017443 SOURCES/FreeRDP-2.2.0.tar.gz

2
.gitignore vendored
View File

@ -1 +1 @@
SOURCES/FreeRDP-2.11.7.tar.gz
SOURCES/FreeRDP-2.2.0.tar.gz

315
SOURCES/6741.patch Normal file
View File

@ -0,0 +1,315 @@
From 3ee4cabcfad3a9ccc3c59be21245b57c17e7ae75 Mon Sep 17 00:00:00 2001
From: Ondrej Holy <oholy@redhat.com>
Date: Thu, 21 Jan 2021 14:21:09 +0100
Subject: [PATCH 1/3] client: Fix exit codes for /help and similar option
Currently, non-zero exit code is returned for /version, /buildconfig, /help,
/monitor-list, /kbd-list and /kbd-lang-list command-line options for several
clients. This is against conventions because 0 is usually returned in
such cases. Also, there is potentially another problem that the returned
codes overflow on UNIX systems (where the exit code is a number between 0
and 255). Let's fix the clients to return 0 in the mentioned cases to honor
conventions and 1 for the command-line parsing errors (or -1 for clients
who already use that value).
Fixes: https://github.com/FreeRDP/FreeRDP/issues/6686
---
client/Sample/tf_freerdp.c | 9 +++++----
client/Wayland/wlfreerdp.c | 13 +++++++------
client/Windows/cli/wfreerdp.c | 4 ++++
client/X11/cli/xfreerdp.c | 22 +++++++++++++---------
winpr/include/winpr/cmdline.h | 1 +
5 files changed, 30 insertions(+), 19 deletions(-)
diff --git a/client/Sample/tf_freerdp.c b/client/Sample/tf_freerdp.c
index 3ba82c78338..49412cb417c 100644
--- a/client/Sample/tf_freerdp.c
+++ b/client/Sample/tf_freerdp.c
@@ -338,12 +338,13 @@ int main(int argc, char* argv[])
goto fail;
status = freerdp_client_settings_parse_command_line(context->settings, argc, argv, FALSE);
- status =
- freerdp_client_settings_command_line_status_print(context->settings, status, argc, argv);
-
if (status)
{
- rc = 0;
+ freerdp_client_settings_command_line_status_print(context->settings, status, argc, argv);
+
+ if (status <= COMMAND_LINE_STATUS_PRINT && status >= COMMAND_LINE_STATUS_PRINT_LAST)
+ rc = 0;
+
goto fail;
}
diff --git a/client/Wayland/wlfreerdp.c b/client/Wayland/wlfreerdp.c
index 329d1200941..d77b47986ea 100644
--- a/client/Wayland/wlfreerdp.c
+++ b/client/Wayland/wlfreerdp.c
@@ -628,18 +628,19 @@ int main(int argc, char* argv[])
settings = context->settings;
status = freerdp_client_settings_parse_command_line(settings, argc, argv, FALSE);
- status = freerdp_client_settings_command_line_status_print(settings, status, argc, argv);
-
if (status)
{
BOOL list = settings->ListMonitors;
+
+ freerdp_client_settings_command_line_status_print(settings, status, argc, argv);
+
if (list)
wlf_list_monitors(wlc);
- freerdp_client_context_free(context);
- if (list)
- return 0;
- return status;
+ if (status <= COMMAND_LINE_STATUS_PRINT && status >= COMMAND_LINE_STATUS_PRINT_LAST)
+ rc = 0;
+
+ goto fail;
}
if (freerdp_client_start(context) != 0)
diff --git a/client/Windows/cli/wfreerdp.c b/client/Windows/cli/wfreerdp.c
index 7a76eeb9b59..b623067e98e 100644
--- a/client/Windows/cli/wfreerdp.c
+++ b/client/Windows/cli/wfreerdp.c
@@ -108,6 +108,10 @@ INT WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine
if (status)
{
freerdp_client_settings_command_line_status_print(settings, status, argc, argv);
+
+ if (status <= COMMAND_LINE_STATUS_PRINT && status >= COMMAND_LINE_STATUS_PRINT_LAST)
+ rc = 0;
+
goto out;
}
diff --git a/client/X11/cli/xfreerdp.c b/client/X11/cli/xfreerdp.c
index c8a77f335f4..a3505b24d3c 100644
--- a/client/X11/cli/xfreerdp.c
+++ b/client/X11/cli/xfreerdp.c
@@ -34,6 +34,7 @@
int main(int argc, char* argv[])
{
+ int rc = 1;
int status;
HANDLE thread;
xfContext* xfc;
@@ -56,31 +57,34 @@ int main(int argc, char* argv[])
xfc = (xfContext*)context;
status = freerdp_client_settings_parse_command_line(context->settings, argc, argv, FALSE);
-
- status = freerdp_client_settings_command_line_status_print(settings, status, argc, argv);
-
if (status)
{
BOOL list = settings->ListMonitors;
+
+ freerdp_client_settings_command_line_status_print(settings, status, argc, argv);
+
if (list)
xf_list_monitors(xfc);
- freerdp_client_context_free(context);
- if (list)
- return 0;
- return status;
+ if (status <= COMMAND_LINE_STATUS_PRINT && status >= COMMAND_LINE_STATUS_PRINT_LAST)
+ rc = 0;
+
+ goto out;
}
- freerdp_client_start(context);
+ if (freerdp_client_start(context) != 0)
+ goto out;
thread = freerdp_client_get_thread(context);
WaitForSingleObject(thread, INFINITE);
GetExitCodeThread(thread, &dwExitCode);
+ rc = xf_exit_code_from_disconnect_reason(dwExitCode);
freerdp_client_stop(context);
+out:
freerdp_client_context_free(context);
- return xf_exit_code_from_disconnect_reason(dwExitCode);
+ return rc;
}
diff --git a/winpr/include/winpr/cmdline.h b/winpr/include/winpr/cmdline.h
index 865ee8f25c7..9276cda8eb1 100644
--- a/winpr/include/winpr/cmdline.h
+++ b/winpr/include/winpr/cmdline.h
@@ -81,6 +81,7 @@
#define COMMAND_LINE_STATUS_PRINT_HELP -2002
#define COMMAND_LINE_STATUS_PRINT_VERSION -2003
#define COMMAND_LINE_STATUS_PRINT_BUILDCONFIG -2004
+#define COMMAND_LINE_STATUS_PRINT_LAST -2999
/* Command-Line Macros */
From 531dd81836f2c97fcfcfeabdb9671fb76409ce8d Mon Sep 17 00:00:00 2001
From: akallabeth <akallabeth@posteo.net>
Date: Fri, 22 Jan 2021 08:40:03 +0100
Subject: [PATCH 2/3] Refactored
freerdp_client_settings_command_line_status_print_ex
Now returns 0 if help or version information was requested.
---
client/Sample/tf_freerdp.c | 7 ++-----
client/Wayland/wlfreerdp.c | 5 +----
client/Windows/cli/wfreerdp.c | 6 +-----
client/X11/cli/xfreerdp.c | 5 +----
client/common/cmdline.c | 14 +++++++++-----
5 files changed, 14 insertions(+), 23 deletions(-)
diff --git a/client/Sample/tf_freerdp.c b/client/Sample/tf_freerdp.c
index 49412cb417c..e9b9fe8397e 100644
--- a/client/Sample/tf_freerdp.c
+++ b/client/Sample/tf_freerdp.c
@@ -340,11 +340,8 @@ int main(int argc, char* argv[])
status = freerdp_client_settings_parse_command_line(context->settings, argc, argv, FALSE);
if (status)
{
- freerdp_client_settings_command_line_status_print(context->settings, status, argc, argv);
-
- if (status <= COMMAND_LINE_STATUS_PRINT && status >= COMMAND_LINE_STATUS_PRINT_LAST)
- rc = 0;
-
+ rc = freerdp_client_settings_command_line_status_print(context->settings, status, argc,
+ argv);
goto fail;
}
diff --git a/client/Wayland/wlfreerdp.c b/client/Wayland/wlfreerdp.c
index d77b47986ea..4a583068968 100644
--- a/client/Wayland/wlfreerdp.c
+++ b/client/Wayland/wlfreerdp.c
@@ -632,14 +632,11 @@ int main(int argc, char* argv[])
{
BOOL list = settings->ListMonitors;
- freerdp_client_settings_command_line_status_print(settings, status, argc, argv);
+ rc = freerdp_client_settings_command_line_status_print(settings, status, argc, argv);
if (list)
wlf_list_monitors(wlc);
- if (status <= COMMAND_LINE_STATUS_PRINT && status >= COMMAND_LINE_STATUS_PRINT_LAST)
- rc = 0;
-
goto fail;
}
diff --git a/client/Windows/cli/wfreerdp.c b/client/Windows/cli/wfreerdp.c
index b623067e98e..e325f84771f 100644
--- a/client/Windows/cli/wfreerdp.c
+++ b/client/Windows/cli/wfreerdp.c
@@ -107,11 +107,7 @@ INT WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine
if (status)
{
- freerdp_client_settings_command_line_status_print(settings, status, argc, argv);
-
- if (status <= COMMAND_LINE_STATUS_PRINT && status >= COMMAND_LINE_STATUS_PRINT_LAST)
- rc = 0;
-
+ ret = freerdp_client_settings_command_line_status_print(settings, status, argc, argv);
goto out;
}
diff --git a/client/X11/cli/xfreerdp.c b/client/X11/cli/xfreerdp.c
index a3505b24d3c..5b702194448 100644
--- a/client/X11/cli/xfreerdp.c
+++ b/client/X11/cli/xfreerdp.c
@@ -61,14 +61,11 @@ int main(int argc, char* argv[])
{
BOOL list = settings->ListMonitors;
- freerdp_client_settings_command_line_status_print(settings, status, argc, argv);
+ rc = freerdp_client_settings_command_line_status_print(settings, status, argc, argv);
if (list)
xf_list_monitors(xfc);
- if (status <= COMMAND_LINE_STATUS_PRINT && status >= COMMAND_LINE_STATUS_PRINT_LAST)
- rc = 0;
-
goto out;
}
diff --git a/client/common/cmdline.c b/client/common/cmdline.c
index ed467afb7d0..dc8367b7cd2 100644
--- a/client/common/cmdline.c
+++ b/client/common/cmdline.c
@@ -1403,14 +1403,14 @@ int freerdp_client_settings_command_line_status_print_ex(rdpSettings* settings,
if (status == COMMAND_LINE_STATUS_PRINT_VERSION)
{
freerdp_client_print_version();
- return COMMAND_LINE_STATUS_PRINT_VERSION;
+ goto out;
}
if (status == COMMAND_LINE_STATUS_PRINT_BUILDCONFIG)
{
freerdp_client_print_version();
freerdp_client_print_buildconfig();
- return COMMAND_LINE_STATUS_PRINT_BUILDCONFIG;
+ goto out;
}
else if (status == COMMAND_LINE_STATUS_PRINT)
{
@@ -1465,15 +1465,19 @@ int freerdp_client_settings_command_line_status_print_ex(rdpSettings* settings,
settings->ListMonitors = TRUE;
}
- return COMMAND_LINE_STATUS_PRINT;
+ goto out;
}
else if (status < 0)
{
freerdp_client_print_command_line_help_ex(argc, argv, custom);
- return COMMAND_LINE_STATUS_PRINT_HELP;
+ status = COMMAND_LINE_STATUS_PRINT_HELP;
+ goto out;
}
- return 0;
+out:
+ if (status <= COMMAND_LINE_STATUS_PRINT && status >= COMMAND_LINE_STATUS_PRINT_LAST)
+ return 0;
+ return status;
}
static BOOL ends_with(const char* str, const char* ext)
From 050a68fec901030c7428852f8f536ace055eb2f7 Mon Sep 17 00:00:00 2001
From: akallabeth <akallabeth@posteo.net>
Date: Fri, 22 Jan 2021 09:32:51 +0100
Subject: [PATCH 3/3] Do not eliminate original error status.
---
client/common/cmdline.c | 1 -
1 file changed, 1 deletion(-)
diff --git a/client/common/cmdline.c b/client/common/cmdline.c
index dc8367b7cd2..66d3c4fffef 100644
--- a/client/common/cmdline.c
+++ b/client/common/cmdline.c
@@ -1470,7 +1470,6 @@ int freerdp_client_settings_command_line_status_print_ex(rdpSettings* settings,
else if (status < 0)
{
freerdp_client_print_command_line_help_ex(argc, argv, custom);
- status = COMMAND_LINE_STATUS_PRINT_HELP;
goto out;
}

View File

@ -0,0 +1,146 @@
From 4a4ecc32b160e93ee1e37e5617a0197e2d022643 Mon Sep 17 00:00:00 2001
From: Armin Novak <armin.novak@thincast.com>
Date: Thu, 4 Nov 2021 11:07:41 +0100
Subject: [PATCH] Add checks for bitmap and glyph width/heigth values
CVE-2021-41160
https://github.com/FreeRDP/FreeRDP/pull/7349
Signed-off-by: Felipe Borges <felipeborges@gnome.org>
---
libfreerdp/core/orders.c | 14 ++++++++++++
libfreerdp/core/surface.c | 45 +++++++++++++++++++++++++++++++++++++++
libfreerdp/core/update.c | 7 ++++++
3 files changed, 66 insertions(+)
diff --git a/libfreerdp/core/orders.c b/libfreerdp/core/orders.c
index 74870fae6..44d23a61a 100644
--- a/libfreerdp/core/orders.c
+++ b/libfreerdp/core/orders.c
@@ -1848,6 +1848,13 @@ static BOOL update_read_fast_glyph_order(wStream* s, const ORDER_INFO* orderInfo
new_cb = ((glyph->cx + 7) / 8) * glyph->cy;
new_cb += ((new_cb % 4) > 0) ? 4 - (new_cb % 4) : 0;
+ if ((glyph->cx == 0) || (glyph->cy == 0))
+ {
+ WLog_ERR(TAG, "GLYPH_DATA_V2::cx=%" PRIu32 ", GLYPH_DATA_V2::cy=%" PRIu32,
+ glyph->cx, glyph->cy);
+ return FALSE;
+ }
+
if (fastGlyph->cbData < new_cb)
return FALSE;
@@ -2825,6 +2832,13 @@ update_read_create_offscreen_bitmap_order(wStream* s,
Stream_Read_UINT16(s, create_offscreen_bitmap->cy); /* cy (2 bytes) */
deleteList = &(create_offscreen_bitmap->deleteList);
+ if ((create_offscreen_bitmap->cx == 0) || (create_offscreen_bitmap->cy == 0))
+ {
+ WLog_ERR(TAG, "Invalid OFFSCREEN_DELETE_LIST: cx=%" PRIu16 ", cy=%" PRIu16,
+ create_offscreen_bitmap->cx, create_offscreen_bitmap->cy);
+ return FALSE;
+ }
+
if (deleteListPresent)
{
UINT32 i;
diff --git a/libfreerdp/core/surface.c b/libfreerdp/core/surface.c
index d5c709885..ca89d230c 100644
--- a/libfreerdp/core/surface.c
+++ b/libfreerdp/core/surface.c
@@ -21,6 +21,8 @@
#include "config.h"
#endif
+#include <assert.h>
+
#include <freerdp/utils/pcap.h>
#include <freerdp/log.h>
@@ -62,6 +64,13 @@ static BOOL update_recv_surfcmd_bitmap_ex(wStream* s, TS_BITMAP_DATA_EX* bmp)
Stream_Read_UINT16(s, bmp->height);
Stream_Read_UINT32(s, bmp->bitmapDataLength);
+ if ((bmp->width == 0) || (bmp->height == 0))
+ {
+ WLog_ERR(TAG, "invalid size value width=%" PRIu16 ", height=%" PRIu16, bmp->width,
+ bmp->height);
+ return FALSE;
+ }
+
if ((bmp->bpp < 1) || (bmp->bpp > 32))
{
WLog_ERR(TAG, "invalid bpp value %" PRIu32 "", bmp->bpp);
@@ -85,6 +94,39 @@ static BOOL update_recv_surfcmd_bitmap_ex(wStream* s, TS_BITMAP_DATA_EX* bmp)
return TRUE;
}
+static BOOL update_recv_surfcmd_is_rect_valid(const rdpContext* context,
+ const SURFACE_BITS_COMMAND* cmd)
+{
+ assert(context);
+ assert(context->settings);
+ assert(cmd);
+
+ /* We need a rectangle with left/top being smaller than right/bottom.
+ * Also do not allow empty rectangles. */
+ if ((cmd->destTop >= cmd->destBottom) || (cmd->destLeft >= cmd->destRight))
+ {
+ WLog_WARN(TAG,
+ "Empty surface bits command rectangle: %" PRIu16 "x%" PRIu16 "-%" PRIu16
+ "x%" PRIu16,
+ cmd->destLeft, cmd->destTop, cmd->destRight, cmd->destBottom);
+ return FALSE;
+ }
+
+ /* The rectangle needs to fit into our session size */
+ if ((cmd->destRight > context->settings->DesktopWidth) ||
+ (cmd->destBottom > context->settings->DesktopHeight))
+ {
+ WLog_WARN(TAG,
+ "Invalid surface bits command rectangle: %" PRIu16 "x%" PRIu16 "-%" PRIu16
+ "x%" PRIu16 " does not fit %" PRIu32 "x%" PRIu32,
+ cmd->destLeft, cmd->destTop, cmd->destRight, cmd->destBottom,
+ context->settings->DesktopWidth, context->settings->DesktopHeight);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
static BOOL update_recv_surfcmd_surface_bits(rdpUpdate* update, wStream* s, UINT16 cmdType)
{
SURFACE_BITS_COMMAND cmd = { 0 };
@@ -98,6 +140,9 @@ static BOOL update_recv_surfcmd_surface_bits(rdpUpdate* update, wStream* s, UINT
Stream_Read_UINT16(s, cmd.destRight);
Stream_Read_UINT16(s, cmd.destBottom);
+ if (!update_recv_surfcmd_is_rect_valid(update->context, &cmd))
+ goto fail;
+
if (!update_recv_surfcmd_bitmap_ex(s, &cmd.bmp))
goto fail;
diff --git a/libfreerdp/core/update.c b/libfreerdp/core/update.c
index ebb82fc2d..e137c3de8 100644
--- a/libfreerdp/core/update.c
+++ b/libfreerdp/core/update.c
@@ -99,6 +99,13 @@ static BOOL update_read_bitmap_data(rdpUpdate* update, wStream* s, BITMAP_DATA*
Stream_Read_UINT16(s, bitmapData->flags);
Stream_Read_UINT16(s, bitmapData->bitmapLength);
+ if ((bitmapData->width == 0) || (bitmapData->height == 0))
+ {
+ WLog_ERR(TAG, "Invalid BITMAP_DATA: width=%" PRIu16 ", height=%" PRIu16, bitmapData->width,
+ bitmapData->height);
+ return FALSE;
+ }
+
if (bitmapData->flags & BITMAP_COMPRESSION)
{
if (!(bitmapData->flags & NO_BITMAP_COMPRESSION_HDR))
--
2.32.0

View File

@ -0,0 +1,62 @@
From e482b394efc371412ce659b731a9b1e1d73bdf0e Mon Sep 17 00:00:00 2001
From: akallabeth <akallabeth@posteo.net>
Date: Mon, 24 Oct 2022 10:42:56 +0200
Subject: [PATCH] Added function _wcsncmp
* Compare WCHAR strings up to n characters
(cherry picked from commit 8178ed26a459356ece17414c6e871a7e0735a4ec)
---
winpr/include/winpr/string.h | 2 ++
winpr/libwinpr/crt/string.c | 15 ++++++++++++++-
2 files changed, 16 insertions(+), 1 deletion(-)
diff --git a/winpr/include/winpr/string.h b/winpr/include/winpr/string.h
index 8ce83bc1d..3b907c444 100644
--- a/winpr/include/winpr/string.h
+++ b/winpr/include/winpr/string.h
@@ -57,6 +57,7 @@ extern "C"
WINPR_API int _strnicmp(const char* string1, const char* string2, size_t count);
WINPR_API int _wcscmp(const WCHAR* string1, const WCHAR* string2);
+ WINPR_API int _wcsncmp(const WCHAR* string1, const WCHAR* string2, size_t count);
WINPR_API size_t _wcslen(const WCHAR* str);
WINPR_API size_t _wcsnlen(const WCHAR* str, size_t maxNumberOfElements);
@@ -70,6 +71,7 @@ extern "C"
#else
#define _wcscmp wcscmp
+#define _wcsncmp wcsncmp
#define _wcslen wcslen
#define _wcsnlen wcsnlen
#define _wcschr wcschr
diff --git a/winpr/libwinpr/crt/string.c b/winpr/libwinpr/crt/string.c
index 37fcb4b25..c25ffa279 100644
--- a/winpr/libwinpr/crt/string.c
+++ b/winpr/libwinpr/crt/string.c
@@ -90,7 +90,20 @@ int _wcscmp(const WCHAR* string1, const WCHAR* string2)
Data_Read_UINT16(string1, value1);
Data_Read_UINT16(string2, value2);
- return value1 - value2;
+ return (int)value1 - value2;
+}
+
+int _wcsncmp(const WCHAR* string1, const WCHAR* string2, size_t count)
+{
+ for (size_t x = 0; x < count; x++)
+ {
+ const WCHAR a = string1[x];
+ const WCHAR b = string2[x];
+
+ if (a != b)
+ return (int)a - b;
+ }
+ return 0;
}
/* _wcslen -> wcslen */
--
2.37.1

View File

@ -0,0 +1,29 @@
From 8c513f127549433c830575202d1551b0e9dd182d Mon Sep 17 00:00:00 2001
From: akallabeth <akallabeth@posteo.net>
Date: Thu, 13 Oct 2022 09:00:48 +0200
Subject: [PATCH] Added missing length check in urb_control_transfer
(cherry picked from commit ce838e2477cb8173ea5e98f35ad55ff41ea5117d)
---
channels/urbdrc/client/data_transfer.c | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/channels/urbdrc/client/data_transfer.c b/channels/urbdrc/client/data_transfer.c
index 9a44e6e09..bb2784055 100644
--- a/channels/urbdrc/client/data_transfer.c
+++ b/channels/urbdrc/client/data_transfer.c
@@ -673,7 +673,11 @@ static UINT urb_control_transfer(IUDEVICE* pdev, URBDRC_CHANNEL_CALLBACK* callba
buffer = Stream_Pointer(out);
if (transferDir == USBD_TRANSFER_DIRECTION_OUT)
+ {
+ if (!Stream_CheckAndLogRequiredLength(TAG, s, OutputBufferSize))
+ return ERROR_INVALID_DATA;
Stream_Copy(s, out, OutputBufferSize);
+ }
/** process TS_URB_CONTROL_TRANSFER */
if (!pdev->control_transfer(pdev, RequestId, EndpointAddress, TransferFlags, bmRequestType,
--
2.37.1

View File

@ -0,0 +1,51 @@
From babbd1e433d273634637f5199429986714864033 Mon Sep 17 00:00:00 2001
From: akallabeth <akallabeth@posteo.net>
Date: Thu, 13 Oct 2022 09:09:28 +0200
Subject: [PATCH] Added missing length checks in zgfx_decompress_segment
(cherry picked from commit 64716b335858109d14f27b51acc4c4d71a92a816)
---
libfreerdp/codec/zgfx.c | 11 +++++++----
1 file changed, 7 insertions(+), 4 deletions(-)
diff --git a/libfreerdp/codec/zgfx.c b/libfreerdp/codec/zgfx.c
index 1a2878bd9..04ddeadb2 100644
--- a/libfreerdp/codec/zgfx.c
+++ b/libfreerdp/codec/zgfx.c
@@ -230,19 +230,19 @@ static BOOL zgfx_decompress_segment(ZGFX_CONTEXT* zgfx, wStream* stream, size_t
BYTE* pbSegment;
size_t cbSegment;
- if (!zgfx || !stream)
+ if (!zgfx || !stream || (segmentSize < 2))
return FALSE;
cbSegment = segmentSize - 1;
- if ((Stream_GetRemainingLength(stream) < segmentSize) || (segmentSize < 1) ||
- (segmentSize > UINT32_MAX))
+ if ((Stream_GetRemainingLength(stream) < segmentSize) || (segmentSize > UINT32_MAX))
return FALSE;
Stream_Read_UINT8(stream, flags); /* header (1 byte) */
zgfx->OutputCount = 0;
pbSegment = Stream_Pointer(stream);
- Stream_Seek(stream, cbSegment);
+ if (!Stream_SafeSeek(stream, cbSegment))
+ return FALSE;
if (!(flags & PACKET_COMPRESSED))
{
@@ -346,6 +346,9 @@ static BOOL zgfx_decompress_segment(ZGFX_CONTEXT* zgfx, wStream* stream, size_t
if (count > sizeof(zgfx->OutputBuffer) - zgfx->OutputCount)
return FALSE;
+ if (count > zgfx->cBitsRemaining / 8)
+ return FALSE;
+
CopyMemory(&(zgfx->OutputBuffer[zgfx->OutputCount]), zgfx->pbInputCurrent,
count);
zgfx_history_buffer_ring_write(zgfx, zgfx->pbInputCurrent, count);
--
2.37.1

View File

@ -0,0 +1,32 @@
From b3a695e9f38a42f1ef0cade0d5e1fe60cf68864e Mon Sep 17 00:00:00 2001
From: akallabeth <akallabeth@posteo.net>
Date: Thu, 13 Oct 2022 08:36:26 +0200
Subject: [PATCH] Ensure urb_create_iocompletion uses size_t for calculation
(cherry picked from commit de7e0f062ee53d00b4a966a43855a716e3478150)
---
channels/urbdrc/client/data_transfer.c | 8 +++++++-
1 file changed, 7 insertions(+), 1 deletion(-)
diff --git a/channels/urbdrc/client/data_transfer.c b/channels/urbdrc/client/data_transfer.c
index 80e84af48..8642c8506 100644
--- a/channels/urbdrc/client/data_transfer.c
+++ b/channels/urbdrc/client/data_transfer.c
@@ -97,7 +97,13 @@ static wStream* urb_create_iocompletion(UINT32 InterfaceField, UINT32 MessageId,
UINT32 OutputBufferSize)
{
const UINT32 InterfaceId = (STREAM_ID_PROXY << 30) | (InterfaceField & 0x3FFFFFFF);
- wStream* out = Stream_New(NULL, OutputBufferSize + 28);
+
+#if UINT32_MAX >= SIZE_MAX
+ if (OutputBufferSize > UINT32_MAX - 28ull)
+ return NULL;
+#endif
+
+ wStream* out = Stream_New(NULL, OutputBufferSize + 28ull);
if (!out)
return NULL;
--
2.37.1

View File

@ -0,0 +1,37 @@
From 64544c7f4ed72b3023955ebe6ad3b118ebb8d6c7 Mon Sep 17 00:00:00 2001
From: akallabeth <akallabeth@posteo.net>
Date: Thu, 6 Oct 2022 09:12:40 +0200
Subject: [PATCH] Fix length checks in parallel driver
The length requested was not checked against the length read from
the port.
(cherry picked from commit 094cc5a4596c299595b732effd59ee149181fd61)
---
channels/parallel/client/parallel_main.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/channels/parallel/client/parallel_main.c b/channels/parallel/client/parallel_main.c
index af3e82703..993605a65 100644
--- a/channels/parallel/client/parallel_main.c
+++ b/channels/parallel/client/parallel_main.c
@@ -159,7 +159,7 @@ static UINT parallel_process_irp_read(PARALLEL_DEVICE* parallel, IRP* irp)
return ERROR_INVALID_DATA;
Stream_Read_UINT32(irp->input, Length);
Stream_Read_UINT64(irp->input, Offset);
- buffer = (BYTE*)malloc(Length);
+ buffer = (BYTE*)calloc(Length, sizeof(BYTE));
if (!buffer)
{
@@ -178,6 +178,7 @@ static UINT parallel_process_irp_read(PARALLEL_DEVICE* parallel, IRP* irp)
}
else
{
+ Length = status;
}
Stream_Write_UINT32(irp->output, Length);
--
2.37.1

View File

@ -0,0 +1,49 @@
From bf9bce2c3e6c716cc708716484124c1fb47cb531 Mon Sep 17 00:00:00 2001
From: Max Roncace <me@caseif.net>
Date: Wed, 24 Feb 2021 17:44:28 -0500
Subject: [PATCH] Fix /monitor-list flag being ignored on X11/Wayland
---
client/Wayland/wlfreerdp.c | 4 +++-
client/X11/cli/xfreerdp.c | 4 +++-
2 files changed, 6 insertions(+), 2 deletions(-)
diff --git a/client/Wayland/wlfreerdp.c b/client/Wayland/wlfreerdp.c
index aca734211..26c5452a5 100644
--- a/client/Wayland/wlfreerdp.c
+++ b/client/Wayland/wlfreerdp.c
@@ -651,10 +651,12 @@ int main(int argc, char* argv[])
status = freerdp_client_settings_parse_command_line(settings, argc, argv, FALSE);
if (status)
{
- BOOL list = settings->ListMonitors;
+ BOOL list;
rc = freerdp_client_settings_command_line_status_print(settings, status, argc, argv);
+ list = settings->ListMonitors;
+
if (list)
wlf_list_monitors(wlc);
diff --git a/client/X11/cli/xfreerdp.c b/client/X11/cli/xfreerdp.c
index 5b7021944..786a7eb5d 100644
--- a/client/X11/cli/xfreerdp.c
+++ b/client/X11/cli/xfreerdp.c
@@ -59,10 +59,12 @@ int main(int argc, char* argv[])
status = freerdp_client_settings_parse_command_line(context->settings, argc, argv, FALSE);
if (status)
{
- BOOL list = settings->ListMonitors;
+ BOOL list;
rc = freerdp_client_settings_command_line_status_print(settings, status, argc, argv);
+ list = settings->ListMonitors;
+
if (list)
xf_list_monitors(xfc);
--
2.37.1

View File

@ -0,0 +1,39 @@
From b9c5e3668c4022b34734ac8ccb07dd044d4ff38c Mon Sep 17 00:00:00 2001
From: akallabeth <akallabeth@posteo.net>
Date: Thu, 13 Oct 2022 08:27:41 +0200
Subject: [PATCH] Fixed division by zero in urbdrc
(cherry picked from commit 731f8419d04b481d7160de1f34062d630ed48765)
---
channels/urbdrc/client/libusb/libusb_udevice.c | 12 +++++++++---
1 file changed, 9 insertions(+), 3 deletions(-)
diff --git a/channels/urbdrc/client/libusb/libusb_udevice.c b/channels/urbdrc/client/libusb/libusb_udevice.c
index aa69890ae..5784d9fe2 100644
--- a/channels/urbdrc/client/libusb/libusb_udevice.c
+++ b/channels/urbdrc/client/libusb/libusb_udevice.c
@@ -1149,12 +1149,18 @@ static int libusb_udev_isoch_transfer(IUDEVICE* idev, URBDRC_CHANNEL_CALLBACK* c
else
Stream_Seek(user_data->data, (NumberOfPackets * 12));
- iso_packet_size = BufferSize / NumberOfPackets;
- iso_transfer = libusb_alloc_transfer(NumberOfPackets);
+ if (NumberOfPackets > 0)
+ {
+ iso_packet_size = BufferSize / NumberOfPackets;
+ iso_transfer = libusb_alloc_transfer((int)NumberOfPackets);
+ }
if (iso_transfer == NULL)
{
- WLog_Print(urbdrc->log, WLOG_ERROR, "Error: libusb_alloc_transfer.");
+ WLog_Print(urbdrc->log, WLOG_ERROR,
+ "Error: libusb_alloc_transfer [NumberOfPackets=%" PRIu32 ", BufferSize=%" PRIu32
+ " ]",
+ NumberOfPackets, BufferSize);
async_transfer_user_data_free(user_data);
return -1;
}
--
2.37.1

View File

@ -0,0 +1,122 @@
From 6ed2f7d1a379f69cca102e8166d20eb5ed38652b Mon Sep 17 00:00:00 2001
From: akallabeth <akallabeth@posteo.net>
Date: Fri, 22 Apr 2022 16:27:21 +0200
Subject: [PATCH] Fixed format string for Stream_CheckAndLogRequiredLength
__LINE__ requires %d and not %PRIuz
(cherry picked from commit 74c1a006e940308b0653427d25a87ea5a24cb573)
---
winpr/include/winpr/stream.h | 14 ++++++++
winpr/libwinpr/utils/stream.c | 65 +++++++++++++++++++++++++++++++++++
2 files changed, 79 insertions(+)
diff --git a/winpr/include/winpr/stream.h b/winpr/include/winpr/stream.h
index f351eaa15..ed637f034 100644
--- a/winpr/include/winpr/stream.h
+++ b/winpr/include/winpr/stream.h
@@ -27,6 +27,8 @@
#include <winpr/wtypes.h>
#include <winpr/endian.h>
#include <winpr/synch.h>
+#include <winpr/wlog.h>
+#include <winpr/debug.h>
#ifdef __cplusplus
extern "C"
@@ -56,6 +57,19 @@ extern "C"
WINPR_API void Stream_StaticInit(wStream* s, BYTE* buffer, size_t size);
WINPR_API void Stream_Free(wStream* s, BOOL bFreeBuffer);
+#define Stream_CheckAndLogRequiredLength(tag, s, len) \
+ Stream_CheckAndLogRequiredLengthEx(tag, WLOG_WARN, s, len, "%s(%s:%d)", __FUNCTION__, \
+ __FILE__, __LINE__)
+ WINPR_API BOOL Stream_CheckAndLogRequiredLengthEx(const char* tag, DWORD level, wStream* s,
+ UINT64 len, const char* fmt, ...);
+ WINPR_API BOOL Stream_CheckAndLogRequiredLengthExVa(const char* tag, DWORD level, wStream* s,
+ UINT64 len, const char* fmt, va_list args);
+ WINPR_API BOOL Stream_CheckAndLogRequiredLengthWLogEx(wLog* log, DWORD level, wStream* s,
+ UINT64 len, const char* fmt, ...);
+ WINPR_API BOOL Stream_CheckAndLogRequiredLengthWLogExVa(wLog* log, DWORD level, wStream* s,
+ UINT64 len, const char* fmt,
+ va_list args);
+
static INLINE void Stream_Seek(wStream* s, size_t _offset)
{
s->pointer += (_offset);
diff --git a/winpr/libwinpr/utils/stream.c b/winpr/libwinpr/utils/stream.c
index 1271981b7..cc119c771 100644
--- a/winpr/libwinpr/utils/stream.c
+++ b/winpr/libwinpr/utils/stream.c
@@ -132,3 +132,68 @@ void Stream_Free(wStream* s, BOOL bFreeBuffer)
free(s);
}
}
+
+BOOL Stream_CheckAndLogRequiredLengthEx(const char* tag, DWORD level, wStream* s, UINT64 len,
+ const char* fmt, ...)
+{
+ const size_t actual = Stream_GetRemainingLength(s);
+
+ if (actual < len)
+ {
+ va_list args;
+
+ va_start(args, fmt);
+ Stream_CheckAndLogRequiredLengthExVa(tag, level, s, len, fmt, args);
+ va_end(args);
+
+ return FALSE;
+ }
+ return TRUE;
+}
+
+BOOL Stream_CheckAndLogRequiredLengthExVa(const char* tag, DWORD level, wStream* s, UINT64 len,
+ const char* fmt, va_list args)
+{
+ const size_t actual = Stream_GetRemainingLength(s);
+
+ if (actual < len)
+ return Stream_CheckAndLogRequiredLengthWLogExVa(WLog_Get(tag), level, s, len, fmt, args);
+ return TRUE;
+}
+
+BOOL Stream_CheckAndLogRequiredLengthWLogEx(wLog* log, DWORD level, wStream* s, UINT64 len,
+ const char* fmt, ...)
+{
+ const size_t actual = Stream_GetRemainingLength(s);
+
+ if (actual < len)
+ {
+ va_list args;
+
+ va_start(args, fmt);
+ Stream_CheckAndLogRequiredLengthWLogExVa(log, level, s, len, fmt, args);
+ va_end(args);
+
+ return FALSE;
+ }
+ return TRUE;
+}
+
+BOOL Stream_CheckAndLogRequiredLengthWLogExVa(wLog* log, DWORD level, wStream* s, UINT64 len,
+ const char* fmt, va_list args)
+{
+ const size_t actual = Stream_GetRemainingLength(s);
+
+ if (actual < len)
+ {
+ char prefix[1024] = { 0 };
+
+ vsnprintf(prefix, sizeof(prefix), fmt, args);
+
+ WLog_Print(log, level, "[%s] invalid length, got %" PRIuz ", require at least %" PRIu64,
+ prefix, actual, len);
+ winpr_log_backtrace_ex(log, level, 20);
+ return FALSE;
+ }
+ return TRUE;
+}
--
2.38.1

View File

@ -0,0 +1,58 @@
From ddf9b3f852c31311f8d726012131f657c9857276 Mon Sep 17 00:00:00 2001
From: akallabeth <akallabeth@posteo.net>
Date: Thu, 13 Oct 2022 08:47:51 +0200
Subject: [PATCH] Fixed missing input buffer length check in urbdrc
(cherry picked from commit 497df00f741dd4fc89292aaef2db7368aee45d0d)
---
channels/urbdrc/client/data_transfer.c | 20 ++++++++++++++++++++
1 file changed, 20 insertions(+)
diff --git a/channels/urbdrc/client/data_transfer.c b/channels/urbdrc/client/data_transfer.c
index bb2784055..80e84af48 100644
--- a/channels/urbdrc/client/data_transfer.c
+++ b/channels/urbdrc/client/data_transfer.c
@@ -241,6 +241,10 @@ static UINT urbdrc_process_io_control(IUDEVICE* pdev, URBDRC_CHANNEL_CALLBACK* c
Stream_Read_UINT32(s, OutputBufferSize);
Stream_Read_UINT32(s, RequestId);
+
+ if (OutputBufferSize > UINT32_MAX - 4)
+ return ERROR_INVALID_DATA;
+
InterfaceId = ((STREAM_ID_PROXY << 30) | pdev->get_ReqCompletion(pdev));
out = urb_create_iocompletion(InterfaceId, MessageId, RequestId, OutputBufferSize);
@@ -724,6 +728,15 @@ static UINT urb_bulk_or_interrupt_transfer(IUDEVICE* pdev, URBDRC_CHANNEL_CALLBA
Stream_Read_UINT32(s, TransferFlags); /** TransferFlags */
Stream_Read_UINT32(s, OutputBufferSize);
EndpointAddress = (PipeHandle & 0x000000ff);
+
+ if (transferDir == USBD_TRANSFER_DIRECTION_OUT)
+ {
+ if (!Stream_CheckAndLogRequiredLength(TAG, s, OutputBufferSize))
+ {
+ return ERROR_INVALID_DATA;
+ }
+ }
+
/** process TS_URB_BULK_OR_INTERRUPT_TRANSFER */
return pdev->bulk_or_interrupt_transfer(pdev, callback, MessageId, RequestId, EndpointAddress,
TransferFlags, noAck, OutputBufferSize,
@@ -808,6 +821,13 @@ static UINT urb_isoch_transfer(IUDEVICE* pdev, URBDRC_CHANNEL_CALLBACK* callback
packetDescriptorData = Stream_Pointer(s);
Stream_Seek(s, NumberOfPackets * 12);
Stream_Read_UINT32(s, OutputBufferSize);
+
+ if (transferDir == USBD_TRANSFER_DIRECTION_OUT)
+ {
+ if (!Stream_CheckAndLogRequiredLength(TAG, s, OutputBufferSize))
+ return ERROR_INVALID_DATA;
+ }
+
return pdev->isoch_transfer(
pdev, callback, MessageId, RequestId, EndpointAddress, TransferFlags, StartFrame,
ErrorCount, noAck, packetDescriptorData, NumberOfPackets, OutputBufferSize,
--
2.37.1

View File

@ -0,0 +1,29 @@
From bf28ea249de57acc6dfadbd778afef2093c1c283 Mon Sep 17 00:00:00 2001
From: akallabeth <akallabeth@posteo.net>
Date: Thu, 6 Oct 2022 09:15:40 +0200
Subject: [PATCH] Fixed missing length check in video channel
Data received in video redirection channel was not checked for
proper length.
(cherry picked from commit eeffd1050e9284d1464b58e049b2b4d88726632b)
---
channels/video/client/video_main.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/channels/video/client/video_main.c b/channels/video/client/video_main.c
index a21e7cdf2..a8031fc86 100644
--- a/channels/video/client/video_main.c
+++ b/channels/video/client/video_main.c
@@ -930,6 +930,8 @@ static UINT video_data_on_data_received(IWTSVirtualChannelCallback* pChannelCall
Stream_Read_UINT16(s, data.PacketsInSample);
Stream_Read_UINT32(s, data.SampleNumber);
Stream_Read_UINT32(s, data.cbSample);
+ if (!Stream_CheckAndLogRequiredLength(TAG, s, data.cbSample))
+ return ERROR_INVALID_DATA;
data.pSample = Stream_Pointer(s);
/*
--
2.37.1

View File

@ -0,0 +1,28 @@
From 80b2483373c00baec3a26b1d82027f16dfdd8859 Mon Sep 17 00:00:00 2001
From: akallabeth <akallabeth@posteo.net>
Date: Mon, 24 Oct 2022 08:45:05 +0200
Subject: [PATCH] Fixed missing stream length check in
drive_file_query_directory
(cherry picked from commit 4e4bb79795d6ac85473fb7a83e53ccf63d204b93)
---
channels/drive/client/drive_main.c | 3 +++
1 file changed, 3 insertions(+)
diff --git a/channels/drive/client/drive_main.c b/channels/drive/client/drive_main.c
index d3776381c..b6cf2ad32 100644
--- a/channels/drive/client/drive_main.c
+++ b/channels/drive/client/drive_main.c
@@ -629,6 +629,9 @@ static UINT drive_process_irp_query_directory(DRIVE_DEVICE* drive, IRP* irp)
Stream_Read_UINT32(irp->input, PathLength);
Stream_Seek(irp->input, 23); /* Padding */
path = (WCHAR*)Stream_Pointer(irp->input);
+ if (!Stream_CheckAndLogRequiredLength(TAG, irp->input, PathLength))
+ return ERROR_INVALID_DATA;
+
file = drive_get_file_by_id(drive, irp->FileId);
if (file == NULL)
--
2.37.1

View File

@ -0,0 +1,296 @@
From 865ba07a0fd4fbc7a8203482411aacca3bbfbb9f Mon Sep 17 00:00:00 2001
From: akallabeth <akallabeth@posteo.net>
Date: Mon, 24 Oct 2022 10:41:55 +0200
Subject: [PATCH] Fixed path validation in drive channel
Check that canonical path is a subpath of the shared directory
(cherry picked from commit 844c94e6d0438fa7bd8ff8d5513c3f69c3018b85)
---
channels/drive/client/drive_file.c | 106 ++++++++++++++++++-----------
channels/drive/client/drive_file.h | 8 +--
channels/drive/client/drive_main.c | 8 +--
3 files changed, 73 insertions(+), 49 deletions(-)
diff --git a/channels/drive/client/drive_file.c b/channels/drive/client/drive_file.c
index 305438593..1ea4ab9da 100644
--- a/channels/drive/client/drive_file.c
+++ b/channels/drive/client/drive_file.c
@@ -34,6 +34,7 @@
#include <stdlib.h>
#include <string.h>
#include <time.h>
+#include <assert.h>
#include <winpr/wtypes.h>
#include <winpr/crt.h>
@@ -61,10 +62,14 @@
} while (0)
#endif
-static void drive_file_fix_path(WCHAR* path)
+static BOOL drive_file_fix_path(WCHAR* path, size_t length)
{
size_t i;
- size_t length = _wcslen(path);
+
+ if ((length == 0) || (length > UINT32_MAX))
+ return FALSE;
+
+ assert(path);
for (i = 0; i < length; i++)
{
@@ -75,58 +79,82 @@ static void drive_file_fix_path(WCHAR* path)
#ifdef WIN32
if ((length == 3) && (path[1] == L':') && (path[2] == L'/'))
- return;
+ return FALSE;
#else
if ((length == 1) && (path[0] == L'/'))
- return;
+ return FALSE;
#endif
if ((length > 0) && (path[length - 1] == L'/'))
path[length - 1] = L'\0';
+
+ return TRUE;
}
static WCHAR* drive_file_combine_fullpath(const WCHAR* base_path, const WCHAR* path,
- size_t PathLength)
+ size_t PathWCharLength)
{
- WCHAR* fullpath;
- size_t base_path_length;
+ BOOL ok = FALSE;
+ WCHAR* fullpath = NULL;
+ size_t length;
- if (!base_path || (!path && (PathLength > 0)))
- return NULL;
+ if (!base_path || (!path && (PathWCharLength > 0)))
+ goto fail;
- base_path_length = _wcslen(base_path) * 2;
- fullpath = (WCHAR*)calloc(1, base_path_length + PathLength + sizeof(WCHAR));
+ const size_t base_path_length = _wcsnlen(base_path, MAX_PATH);
+ length = base_path_length + PathWCharLength + 1;
+ fullpath = (WCHAR*)calloc(length, sizeof(WCHAR));
if (!fullpath)
+ goto fail;
+
+ CopyMemory(fullpath, base_path, base_path_length * sizeof(WCHAR));
+ if (path)
+ CopyMemory(&fullpath[base_path_length], path, PathWCharLength * sizeof(WCHAR));
+
+ if (!drive_file_fix_path(fullpath, length))
+ goto fail;
+
+ /* Ensure the path does not contain sequences like '..' */
+ const WCHAR dotdot[] = { '.', '.', '\0' };
+ if (_wcsstr(&fullpath[base_path_length], dotdot))
{
- WLog_ERR(TAG, "malloc failed!");
- return NULL;
+ char abuffer[MAX_PATH] = { 0 };
+ ConvertFromUnicode(CP_UTF8, 0, &fullpath[base_path_length], -1, (char**)&abuffer,
+ ARRAYSIZE(abuffer) - 1, NULL, NULL);
+
+ WLog_WARN(TAG, "[rdpdr] received invalid file path '%s' from server, aborting!",
+ &abuffer[base_path_length]);
+ goto fail;
}
- CopyMemory(fullpath, base_path, base_path_length);
- if (path)
- CopyMemory((char*)fullpath + base_path_length, path, PathLength);
- drive_file_fix_path(fullpath);
+ ok = TRUE;
+fail:
+ if (!ok)
+ {
+ free(fullpath);
+ fullpath = NULL;
+ }
return fullpath;
}
static BOOL drive_file_remove_dir(const WCHAR* path)
{
- WIN32_FIND_DATAW findFileData;
+ WIN32_FIND_DATAW findFileData = { 0 };
BOOL ret = TRUE;
- HANDLE dir;
- WCHAR* fullpath;
- WCHAR* path_slash;
- size_t base_path_length;
+ HANDLE dir = INVALID_HANDLE_VALUE;
+ WCHAR* fullpath = NULL;
+ WCHAR* path_slash = NULL;
+ size_t base_path_length = 0;
if (!path)
return FALSE;
- base_path_length = _wcslen(path) * 2;
- path_slash = (WCHAR*)calloc(1, base_path_length + sizeof(WCHAR) * 3);
+ base_path_length = _wcslen(path);
+ path_slash = (WCHAR*)calloc(base_path_length + 3, sizeof(WCHAR));
if (!path_slash)
{
@@ -134,12 +162,11 @@ static BOOL drive_file_remove_dir(const WCHAR* path)
return FALSE;
}
- CopyMemory(path_slash, path, base_path_length);
- path_slash[base_path_length / 2] = L'/';
- path_slash[base_path_length / 2 + 1] = L'*';
+ CopyMemory(path_slash, path, base_path_length * sizeof(WCHAR));
+ path_slash[base_path_length] = L'/';
+ path_slash[base_path_length + 1] = L'*';
DEBUG_WSTR("Search in %s", path_slash);
dir = FindFirstFileW(path_slash, &findFileData);
- path_slash[base_path_length / 2 + 1] = 0;
if (dir == INVALID_HANDLE_VALUE)
{
@@ -149,7 +176,7 @@ static BOOL drive_file_remove_dir(const WCHAR* path)
do
{
- size_t len = _wcslen(findFileData.cFileName);
+ const size_t len = _wcsnlen(findFileData.cFileName, ARRAYSIZE(findFileData.cFileName));
if ((len == 1 && findFileData.cFileName[0] == L'.') ||
(len == 2 && findFileData.cFileName[0] == L'.' && findFileData.cFileName[1] == L'.'))
@@ -157,7 +184,7 @@ static BOOL drive_file_remove_dir(const WCHAR* path)
continue;
}
- fullpath = drive_file_combine_fullpath(path_slash, findFileData.cFileName, len * 2);
+ fullpath = drive_file_combine_fullpath(path_slash, findFileData.cFileName, len);
DEBUG_WSTR("Delete %s", fullpath);
if (findFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
@@ -333,13 +360,13 @@ static BOOL drive_file_init(DRIVE_FILE* file)
return file->file_handle != INVALID_HANDLE_VALUE;
}
-DRIVE_FILE* drive_file_new(const WCHAR* base_path, const WCHAR* path, UINT32 PathLength, UINT32 id,
- UINT32 DesiredAccess, UINT32 CreateDisposition, UINT32 CreateOptions,
- UINT32 FileAttributes, UINT32 SharedAccess)
+DRIVE_FILE* drive_file_new(const WCHAR* base_path, const WCHAR* path, UINT32 PathWCharLength,
+ UINT32 id, UINT32 DesiredAccess, UINT32 CreateDisposition,
+ UINT32 CreateOptions, UINT32 FileAttributes, UINT32 SharedAccess)
{
DRIVE_FILE* file;
- if (!base_path || (!path && (PathLength > 0)))
+ if (!base_path || (!path && (PathWCharLength > 0)))
return NULL;
file = (DRIVE_FILE*)calloc(1, sizeof(DRIVE_FILE));
@@ -359,7 +386,7 @@ DRIVE_FILE* drive_file_new(const WCHAR* base_path, const WCHAR* path, UINT32 Pat
file->CreateDisposition = CreateDisposition;
file->CreateOptions = CreateOptions;
file->SharedAccess = SharedAccess;
- drive_file_set_fullpath(file, drive_file_combine_fullpath(base_path, path, PathLength));
+ drive_file_set_fullpath(file, drive_file_combine_fullpath(base_path, path, PathWCharLength));
if (!drive_file_init(file))
{
@@ -714,13 +741,10 @@ BOOL drive_file_set_information(DRIVE_FILE* file, UINT32 FsInformationClass, UIN
return FALSE;
fullpath = drive_file_combine_fullpath(file->basepath, (WCHAR*)Stream_Pointer(input),
- FileNameLength);
+ FileNameLength / sizeof(WCHAR));
if (!fullpath)
- {
- WLog_ERR(TAG, "drive_file_combine_fullpath failed!");
return FALSE;
- }
#ifdef _WIN32
@@ -759,7 +783,7 @@ BOOL drive_file_set_information(DRIVE_FILE* file, UINT32 FsInformationClass, UIN
}
BOOL drive_file_query_directory(DRIVE_FILE* file, UINT32 FsInformationClass, BYTE InitialQuery,
- const WCHAR* path, UINT32 PathLength, wStream* output)
+ const WCHAR* path, UINT32 PathWCharLength, wStream* output)
{
size_t length;
WCHAR* ent_path;
@@ -773,7 +797,7 @@ BOOL drive_file_query_directory(DRIVE_FILE* file, UINT32 FsInformationClass, BYT
if (file->find_handle != INVALID_HANDLE_VALUE)
FindClose(file->find_handle);
- ent_path = drive_file_combine_fullpath(file->basepath, path, PathLength);
+ ent_path = drive_file_combine_fullpath(file->basepath, path, PathWCharLength);
/* open new search handle and retrieve the first entry */
file->find_handle = FindFirstFileW(ent_path, &file->find_data);
free(ent_path);
diff --git a/channels/drive/client/drive_file.h b/channels/drive/client/drive_file.h
index ed789d6f0..6d3bd7045 100644
--- a/channels/drive/client/drive_file.h
+++ b/channels/drive/client/drive_file.h
@@ -51,9 +51,9 @@ struct _DRIVE_FILE
UINT32 CreateOptions;
};
-DRIVE_FILE* drive_file_new(const WCHAR* base_path, const WCHAR* path, UINT32 PathLength, UINT32 id,
- UINT32 DesiredAccess, UINT32 CreateDisposition, UINT32 CreateOptions,
- UINT32 FileAttributes, UINT32 SharedAccess);
+DRIVE_FILE* drive_file_new(const WCHAR* base_path, const WCHAR* path, UINT32 PathWCharLength,
+ UINT32 id, UINT32 DesiredAccess, UINT32 CreateDisposition,
+ UINT32 CreateOptions, UINT32 FileAttributes, UINT32 SharedAccess);
BOOL drive_file_free(DRIVE_FILE* file);
BOOL drive_file_open(DRIVE_FILE* file);
@@ -64,6 +64,6 @@ BOOL drive_file_query_information(DRIVE_FILE* file, UINT32 FsInformationClass, w
BOOL drive_file_set_information(DRIVE_FILE* file, UINT32 FsInformationClass, UINT32 Length,
wStream* input);
BOOL drive_file_query_directory(DRIVE_FILE* file, UINT32 FsInformationClass, BYTE InitialQuery,
- const WCHAR* path, UINT32 PathLength, wStream* output);
+ const WCHAR* path, UINT32 PathWCharLength, wStream* output);
#endif /* FREERDP_CHANNEL_DRIVE_FILE_H */
diff --git a/channels/drive/client/drive_main.c b/channels/drive/client/drive_main.c
index 1b5422522..d3776381c 100644
--- a/channels/drive/client/drive_main.c
+++ b/channels/drive/client/drive_main.c
@@ -184,8 +184,8 @@ static UINT drive_process_irp_create(DRIVE_DEVICE* drive, IRP* irp)
path = (const WCHAR*)Stream_Pointer(irp->input);
FileId = irp->devman->id_sequence++;
- file = drive_file_new(drive->path, path, PathLength, FileId, DesiredAccess, CreateDisposition,
- CreateOptions, FileAttributes, SharedAccess);
+ file = drive_file_new(drive->path, path, PathLength / sizeof(WCHAR), FileId, DesiredAccess,
+ CreateDisposition, CreateOptions, FileAttributes, SharedAccess);
if (!file)
{
@@ -636,8 +636,8 @@ static UINT drive_process_irp_query_directory(DRIVE_DEVICE* drive, IRP* irp)
irp->IoStatus = STATUS_UNSUCCESSFUL;
Stream_Write_UINT32(irp->output, 0); /* Length */
}
- else if (!drive_file_query_directory(file, FsInformationClass, InitialQuery, path, PathLength,
- irp->output))
+ else if (!drive_file_query_directory(file, FsInformationClass, InitialQuery, path,
+ PathLength / sizeof(WCHAR), irp->output))
{
irp->IoStatus = drive_map_windows_err(GetLastError());
}
--
2.37.1

View File

@ -0,0 +1,433 @@
From ea20ba13ec764b30d6642cd570929e71e97d00f1 Mon Sep 17 00:00:00 2001
From: akallabeth <akallabeth@posteo.net>
Date: Tue, 12 Oct 2021 13:33:41 +0200
Subject: [PATCH] Implemented missing TSG debug functions
(cherry picked from commit c06c4638061b0305a269dbb8f7c87009cf886f02)
---
libfreerdp/core/gateway/tsg.c | 330 ++++++++++++++++++++++++++++++++--
1 file changed, 313 insertions(+), 17 deletions(-)
diff --git a/libfreerdp/core/gateway/tsg.c b/libfreerdp/core/gateway/tsg.c
index 1626b91..3376fb0 100644
--- a/libfreerdp/core/gateway/tsg.c
+++ b/libfreerdp/core/gateway/tsg.c
@@ -24,6 +24,7 @@
#include "config.h"
#endif
+#include <winpr/assert.h>
#include <winpr/crt.h>
#include <winpr/ndr.h>
#include <winpr/error.h>
@@ -39,13 +40,14 @@
#define TAG FREERDP_TAG("core.gateway.tsg")
+#define TSG_CAPABILITY_TYPE_NAP 0x00000001
+
#define TSG_PACKET_TYPE_HEADER 0x00004844
#define TSG_PACKET_TYPE_VERSIONCAPS 0x00005643
#define TSG_PACKET_TYPE_QUARCONFIGREQUEST 0x00005143
#define TSG_PACKET_TYPE_QUARREQUEST 0x00005152
#define TSG_PACKET_TYPE_RESPONSE 0x00005052
#define TSG_PACKET_TYPE_QUARENC_RESPONSE 0x00004552
-#define TSG_CAPABILITY_TYPE_NAP 0x00000001
#define TSG_PACKET_TYPE_CAPS_RESPONSE 0x00004350
#define TSG_PACKET_TYPE_MSGREQUEST_PACKET 0x00004752
#define TSG_PACKET_TYPE_MESSAGE_PACKET 0x00004750
@@ -308,16 +310,35 @@ static BOOL tsg_print(char** buffer, size_t* len, const char* fmt, ...)
static BOOL tsg_packet_header_to_string(char** buffer, size_t* length,
const TSG_PACKET_HEADER* header)
{
+ WINPR_ASSERT(buffer);
+ WINPR_ASSERT(length);
+ WINPR_ASSERT(header);
+
return tsg_print(buffer, length,
"header { ComponentId=0x%04" PRIx16 ", PacketId=0x%04" PRIx16 " }",
header->ComponentId, header->PacketId);
}
+static BOOL tsg_type_capability_nap_to_string(char** buffer, size_t* length,
+ const TSG_CAPABILITY_NAP* cur)
+{
+ WINPR_ASSERT(buffer);
+ WINPR_ASSERT(length);
+ WINPR_ASSERT(cur);
+
+ return tsg_print(buffer, length, "%s { capabilities=0x%08" PRIx32 " }",
+ tsg_packet_id_to_string(TSG_CAPABILITY_TYPE_NAP), cur->capabilities);
+}
+
static BOOL tsg_packet_capabilities_to_string(char** buffer, size_t* length,
const TSG_PACKET_CAPABILITIES* caps, UINT32 numCaps)
{
UINT32 x;
+ WINPR_ASSERT(buffer);
+ WINPR_ASSERT(length);
+ WINPR_ASSERT(caps);
+
if (!tsg_print(buffer, length, "capabilities { "))
return FALSE;
@@ -327,9 +348,7 @@ static BOOL tsg_packet_capabilities_to_string(char** buffer, size_t* length,
switch (cur->capabilityType)
{
case TSG_CAPABILITY_TYPE_NAP:
- if (!tsg_print(buffer, length, "%s { capabilities=0x%08" PRIx32 " }",
- tsg_packet_id_to_string(cur->capabilityType),
- cur->tsgPacket.tsgCapNap.capabilities))
+ if (!tsg_type_capability_nap_to_string(buffer, length, &cur->tsgPacket.tsgCapNap))
return FALSE;
break;
default:
@@ -344,6 +363,10 @@ static BOOL tsg_packet_capabilities_to_string(char** buffer, size_t* length,
static BOOL tsg_packet_versioncaps_to_string(char** buffer, size_t* length,
const TSG_PACKET_VERSIONCAPS* caps)
{
+ WINPR_ASSERT(buffer);
+ WINPR_ASSERT(length);
+ WINPR_ASSERT(caps);
+
if (!tsg_print(buffer, length, "versioncaps { "))
return FALSE;
if (!tsg_packet_header_to_string(buffer, length, &caps->tsgHeader))
@@ -365,6 +388,277 @@ static BOOL tsg_packet_versioncaps_to_string(char** buffer, size_t* length,
return tsg_print(buffer, length, " }");
}
+static BOOL tsg_packet_quarconfigrequest_to_string(char** buffer, size_t* length,
+ const TSG_PACKET_QUARCONFIGREQUEST* caps)
+{
+ WINPR_ASSERT(buffer);
+ WINPR_ASSERT(length);
+ WINPR_ASSERT(caps);
+
+ if (!tsg_print(buffer, length, "quarconfigrequest { "))
+ return FALSE;
+
+ if (!tsg_print(buffer, length, " "))
+ return FALSE;
+
+ if (!tsg_print(buffer, length, " flags=0x%08" PRIx32, caps->flags))
+ return FALSE;
+
+ return tsg_print(buffer, length, " }");
+}
+
+static BOOL tsg_packet_quarrequest_to_string(char** buffer, size_t* length,
+ const TSG_PACKET_QUARREQUEST* caps)
+{
+ BOOL rc = FALSE;
+ char* name = NULL;
+ char* strdata = NULL;
+
+ WINPR_ASSERT(buffer);
+ WINPR_ASSERT(length);
+ WINPR_ASSERT(caps);
+
+ if (!tsg_print(buffer, length, "quarrequest { "))
+ return FALSE;
+
+ if (!tsg_print(buffer, length, " "))
+ return FALSE;
+
+ if (caps->nameLength > 0)
+ {
+ if (ConvertFromUnicode(CP_UTF8, 0, caps->machineName, caps->nameLength, &name, 0, NULL,
+ NULL) < 0)
+ return FALSE;
+ }
+
+ strdata = winpr_BinToHexString(caps->data, caps->dataLen, TRUE);
+ if (strdata || (caps->dataLen == 0))
+ rc = tsg_print(buffer, length,
+ " flags=0x%08" PRIx32 ", machineName=%s [%" PRIu32 "], data[%" PRIu32 "]=%s",
+ caps->flags, name, caps->nameLength, caps->dataLen, strdata);
+ free(name);
+ free(strdata);
+ if (!rc)
+ return FALSE;
+
+ return tsg_print(buffer, length, " }");
+}
+
+static const char* tsg_bool_to_string(BOOL val)
+{
+ if (val)
+ return "true";
+ return "false";
+}
+
+static const char* tsg_redirection_flags_to_string(char* buffer, size_t size,
+ const TSG_REDIRECTION_FLAGS* flags)
+{
+ WINPR_ASSERT(buffer || (size == 0));
+ WINPR_ASSERT(flags);
+
+ _snprintf(buffer, size,
+ "enableAllRedirections=%s, disableAllRedirections=%s, driveRedirectionDisabled=%s, "
+ "printerRedirectionDisabled=%s, portRedirectionDisabled=%s, reserved=%s, "
+ "clipboardRedirectionDisabled=%s, pnpRedirectionDisabled=%s",
+ tsg_bool_to_string(flags->enableAllRedirections),
+ tsg_bool_to_string(flags->disableAllRedirections),
+ tsg_bool_to_string(flags->driveRedirectionDisabled),
+ tsg_bool_to_string(flags->printerRedirectionDisabled),
+ tsg_bool_to_string(flags->portRedirectionDisabled),
+ tsg_bool_to_string(flags->reserved),
+ tsg_bool_to_string(flags->clipboardRedirectionDisabled),
+ tsg_bool_to_string(flags->pnpRedirectionDisabled));
+ return buffer;
+}
+
+static BOOL tsg_packet_response_to_string(char** buffer, size_t* length,
+ const TSG_PACKET_RESPONSE* caps)
+{
+ BOOL rc = FALSE;
+ char* strdata = NULL;
+ char tbuffer[8192] = { 0 };
+
+ WINPR_ASSERT(buffer);
+ WINPR_ASSERT(length);
+ WINPR_ASSERT(caps);
+
+ if (!tsg_print(buffer, length, "response { "))
+ return FALSE;
+
+ if (!tsg_print(buffer, length, " "))
+ return FALSE;
+
+ strdata = winpr_BinToHexString(caps->responseData, caps->responseDataLen, TRUE);
+ if (strdata || (caps->responseDataLen == 0))
+ rc = tsg_print(
+ buffer, length,
+ " flags=0x%08" PRIx32 ", reserved=0x%08" PRIx32 ", responseData[%" PRIu32
+ "]=%s, redirectionFlags={ %s }",
+ caps->flags, caps->reserved, caps->responseDataLen, strdata,
+ tsg_redirection_flags_to_string(tbuffer, ARRAYSIZE(tbuffer), &caps->redirectionFlags));
+ free(strdata);
+ if (!rc)
+ return FALSE;
+
+ return tsg_print(buffer, length, " }");
+}
+
+static BOOL tsg_packet_quarenc_response_to_string(char** buffer, size_t* length,
+ const TSG_PACKET_QUARENC_RESPONSE* caps)
+{
+ BOOL rc = FALSE;
+ char* strdata = NULL;
+ RPC_CSTR uuid;
+ char tbuffer[8192] = { 0 };
+ size_t size = ARRAYSIZE(tbuffer);
+ char* ptbuffer = tbuffer;
+
+ WINPR_ASSERT(buffer);
+ WINPR_ASSERT(length);
+ WINPR_ASSERT(caps);
+
+ if (!tsg_print(buffer, length, "quarenc_response { "))
+ return FALSE;
+
+ if (!tsg_print(buffer, length, " "))
+ return FALSE;
+
+ if (caps->certChainLen > 0)
+ {
+ if (ConvertFromUnicode(CP_UTF8, 0, caps->certChainData, caps->certChainLen, &strdata, 0,
+ NULL, NULL) <= 0)
+ return FALSE;
+ }
+
+ tsg_packet_versioncaps_to_string(&ptbuffer, &size, caps->versionCaps);
+ UuidToStringA(&caps->nonce, &uuid);
+ if (strdata || (caps->certChainLen == 0))
+ rc =
+ tsg_print(buffer, length,
+ " flags=0x%08" PRIx32 ", certChain[%" PRIu32 "]=%s, nonce=%s, versionCaps=%s",
+ caps->flags, caps->certChainLen, strdata, uuid, tbuffer);
+ free(strdata);
+ free(uuid);
+ if (!rc)
+ return FALSE;
+
+ return tsg_print(buffer, length, " }");
+}
+
+static BOOL tsg_packet_message_response_to_string(char** buffer, size_t* length,
+ const TSG_PACKET_MSG_RESPONSE* caps)
+{
+ WINPR_ASSERT(buffer);
+ WINPR_ASSERT(length);
+ WINPR_ASSERT(caps);
+
+ if (!tsg_print(buffer, length, "msg_response { "))
+ return FALSE;
+
+ if (!tsg_print(buffer, length,
+ " msgID=0x%08" PRIx32 ", msgType=0x%08" PRIx32 ", isMsgPresent=%" PRId32,
+ caps->msgID, caps->msgType, caps->isMsgPresent))
+ return FALSE;
+
+ return tsg_print(buffer, length, " }");
+}
+
+static BOOL tsg_packet_caps_response_to_string(char** buffer, size_t* length,
+ const TSG_PACKET_CAPS_RESPONSE* caps)
+{
+ WINPR_ASSERT(buffer);
+ WINPR_ASSERT(length);
+ WINPR_ASSERT(caps);
+
+ if (!tsg_print(buffer, length, "caps_response { "))
+ return FALSE;
+
+ if (!tsg_packet_quarenc_response_to_string(buffer, length, &caps->pktQuarEncResponse))
+ return FALSE;
+
+ if (!tsg_packet_message_response_to_string(buffer, length, &caps->pktConsentMessage))
+ return FALSE;
+
+ return tsg_print(buffer, length, " }");
+}
+
+static BOOL tsg_packet_message_request_to_string(char** buffer, size_t* length,
+ const TSG_PACKET_MSG_REQUEST* caps)
+{
+ WINPR_ASSERT(buffer);
+ WINPR_ASSERT(length);
+ WINPR_ASSERT(caps);
+
+ if (!tsg_print(buffer, length, "caps_message_request { "))
+ return FALSE;
+
+ if (!tsg_print(buffer, length, " maxMessagesPerBatch=%" PRIu32, caps->maxMessagesPerBatch))
+ return FALSE;
+
+ return tsg_print(buffer, length, " }");
+}
+
+static BOOL tsg_packet_auth_to_string(char** buffer, size_t* length, const TSG_PACKET_AUTH* caps)
+{
+ BOOL rc = FALSE;
+ char* strdata = NULL;
+ WINPR_ASSERT(buffer);
+ WINPR_ASSERT(length);
+ WINPR_ASSERT(caps);
+
+ if (!tsg_print(buffer, length, "caps_message_request { "))
+ return FALSE;
+
+ if (!tsg_packet_versioncaps_to_string(buffer, length, &caps->tsgVersionCaps))
+ return FALSE;
+
+ strdata = winpr_BinToHexString(caps->cookie, caps->cookieLen, TRUE);
+ if (strdata || (caps->cookieLen == 0))
+ rc = tsg_print(buffer, length, " cookie[%" PRIu32 "]=%s", caps->cookieLen, strdata);
+ free(strdata);
+ if (!rc)
+ return FALSE;
+
+ return tsg_print(buffer, length, " }");
+}
+
+static BOOL tsg_packet_reauth_to_string(char** buffer, size_t* length,
+ const TSG_PACKET_REAUTH* caps)
+{
+ BOOL rc = FALSE;
+ WINPR_ASSERT(buffer);
+ WINPR_ASSERT(length);
+ WINPR_ASSERT(caps);
+
+ if (!tsg_print(buffer, length, "caps_message_request { "))
+ return FALSE;
+
+ if (!tsg_print(buffer, length, " tunnelContext=0x%08" PRIx32 ", packetId=%s [0x%08" PRIx32 "]",
+ caps->tunnelContext, tsg_packet_id_to_string(caps->packetId), caps->packetId))
+ return FALSE;
+
+ switch (caps->packetId)
+ {
+ case TSG_PACKET_TYPE_VERSIONCAPS:
+ rc = tsg_packet_versioncaps_to_string(buffer, length,
+ caps->tsgInitialPacket.packetVersionCaps);
+ break;
+ case TSG_PACKET_TYPE_AUTH:
+ rc = tsg_packet_auth_to_string(buffer, length, caps->tsgInitialPacket.packetAuth);
+ break;
+ default:
+ rc = tsg_print(buffer, length, "TODO: Unhandled packet type %s [0x%08" PRIx32 "]",
+ tsg_packet_id_to_string(caps->packetId), caps->packetId);
+ break;
+ }
+
+ if (!rc)
+ return FALSE;
+
+ return tsg_print(buffer, length, " }");
+}
+
static const char* tsg_packet_to_string(const TSG_PACKET* packet)
{
size_t len = 8192;
@@ -387,43 +681,45 @@ static const char* tsg_packet_to_string(const TSG_PACKET* packet)
goto fail;
break;
case TSG_PACKET_TYPE_QUARCONFIGREQUEST:
- if (!tsg_print(&buffer, &len, "TODO"))
+ if (!tsg_packet_quarconfigrequest_to_string(&buffer, &len,
+ packet->tsgPacket.packetQuarConfigRequest))
goto fail;
break;
case TSG_PACKET_TYPE_QUARREQUEST:
- if (!tsg_print(&buffer, &len, "TODO"))
+ if (!tsg_packet_quarrequest_to_string(&buffer, &len,
+ packet->tsgPacket.packetQuarRequest))
goto fail;
break;
case TSG_PACKET_TYPE_RESPONSE:
- if (!tsg_print(&buffer, &len, "TODO"))
+ if (!tsg_packet_response_to_string(&buffer, &len, packet->tsgPacket.packetResponse))
goto fail;
break;
case TSG_PACKET_TYPE_QUARENC_RESPONSE:
- if (!tsg_print(&buffer, &len, "TODO"))
- goto fail;
- break;
- case TSG_CAPABILITY_TYPE_NAP:
- if (!tsg_print(&buffer, &len, "TODO"))
+ if (!tsg_packet_quarenc_response_to_string(&buffer, &len,
+ packet->tsgPacket.packetQuarEncResponse))
goto fail;
break;
case TSG_PACKET_TYPE_CAPS_RESPONSE:
- if (!tsg_print(&buffer, &len, "TODO"))
+ if (!tsg_packet_caps_response_to_string(&buffer, &len,
+ packet->tsgPacket.packetCapsResponse))
goto fail;
break;
case TSG_PACKET_TYPE_MSGREQUEST_PACKET:
- if (!tsg_print(&buffer, &len, "TODO"))
+ if (!tsg_packet_message_request_to_string(&buffer, &len,
+ packet->tsgPacket.packetMsgRequest))
goto fail;
break;
case TSG_PACKET_TYPE_MESSAGE_PACKET:
- if (!tsg_print(&buffer, &len, "TODO"))
+ if (!tsg_packet_message_response_to_string(&buffer, &len,
+ packet->tsgPacket.packetMsgResponse))
goto fail;
break;
case TSG_PACKET_TYPE_AUTH:
- if (!tsg_print(&buffer, &len, "TODO"))
+ if (!tsg_packet_auth_to_string(&buffer, &len, packet->tsgPacket.packetAuth))
goto fail;
break;
case TSG_PACKET_TYPE_REAUTH:
- if (!tsg_print(&buffer, &len, "TODO"))
+ if (!tsg_packet_reauth_to_string(&buffer, &len, packet->tsgPacket.packetReauth))
goto fail;
break;
default:

File diff suppressed because it is too large Load Diff

View File

@ -1,560 +0,0 @@
From a441e68fccc8ef137c9e8e667fed6adaab34ba37 Mon Sep 17 00:00:00 2001
From: Ondrej Holy <oholy@redhat.com>
Date: Tue, 1 Oct 2024 15:43:07 +0200
Subject: [PATCH] Revert "Moved clipboard utils to core library, fixes #6760
(#7752)"
This reverts commit 26a83e6ccde272c1bbc2b2591325dc7a493811bc.
---
channels/cliprdr/client/cliprdr_format.c | 195 +++++++++++++++++++
include/freerdp/channels/cliprdr.h | 12 +-
include/freerdp/utils/cliprdr_utils.h | 48 -----
libfreerdp/utils/CMakeLists.txt | 1 -
libfreerdp/utils/cliprdr_utils.c | 235 -----------------------
5 files changed, 206 insertions(+), 285 deletions(-)
delete mode 100644 include/freerdp/utils/cliprdr_utils.h
delete mode 100644 libfreerdp/utils/cliprdr_utils.c
diff --git a/channels/cliprdr/client/cliprdr_format.c b/channels/cliprdr/client/cliprdr_format.c
index 0b6111b96..4c31a1b08 100644
--- a/channels/cliprdr/client/cliprdr_format.c
+++ b/channels/cliprdr/client/cliprdr_format.c
@@ -173,3 +173,198 @@ UINT cliprdr_process_format_data_response(cliprdrPlugin* cliprdr, wStream* s, UI
return error;
}
+
+static UINT64 filetime_to_uint64(FILETIME value)
+{
+ UINT64 converted = 0;
+ converted |= (UINT32)value.dwHighDateTime;
+ converted <<= 32;
+ converted |= (UINT32)value.dwLowDateTime;
+ return converted;
+}
+
+static FILETIME uint64_to_filetime(UINT64 value)
+{
+ FILETIME converted;
+ converted.dwLowDateTime = (UINT32)(value >> 0);
+ converted.dwHighDateTime = (UINT32)(value >> 32);
+ return converted;
+}
+
+#define CLIPRDR_FILEDESCRIPTOR_SIZE (4 + 32 + 4 + 16 + 8 + 8 + 520)
+
+/**
+ * Parse a packed file list.
+ *
+ * The resulting array must be freed with the `free()` function.
+ *
+ * @param [in] format_data packed `CLIPRDR_FILELIST` to parse.
+ * @param [in] format_data_length length of `format_data` in bytes.
+ * @param [out] file_descriptor_array parsed array of `FILEDESCRIPTOR` structs.
+ * @param [out] file_descriptor_count number of elements in `file_descriptor_array`.
+ *
+ * @returns 0 on success, otherwise a Win32 error code.
+ */
+UINT cliprdr_parse_file_list(const BYTE* format_data, UINT32 format_data_length,
+ FILEDESCRIPTORW** file_descriptor_array, UINT32* file_descriptor_count)
+{
+ UINT result = NO_ERROR;
+ UINT32 i;
+ UINT32 count = 0;
+ wStream* s = NULL;
+
+ if (!format_data || !file_descriptor_array || !file_descriptor_count)
+ return ERROR_BAD_ARGUMENTS;
+
+ s = Stream_New((BYTE*)format_data, format_data_length);
+ if (!s)
+ return ERROR_NOT_ENOUGH_MEMORY;
+
+ if (Stream_GetRemainingLength(s) < 4)
+ {
+ WLog_ERR(TAG, "invalid packed file list");
+
+ result = ERROR_INCORRECT_SIZE;
+ goto out;
+ }
+
+ Stream_Read_UINT32(s, count); /* cItems (4 bytes) */
+
+ if (Stream_GetRemainingLength(s) / CLIPRDR_FILEDESCRIPTOR_SIZE < count)
+ {
+ WLog_ERR(TAG, "packed file list is too short: expected %" PRIuz ", have %" PRIuz,
+ ((size_t)count) * CLIPRDR_FILEDESCRIPTOR_SIZE, Stream_GetRemainingLength(s));
+
+ result = ERROR_INCORRECT_SIZE;
+ goto out;
+ }
+
+ *file_descriptor_count = count;
+ *file_descriptor_array = calloc(count, sizeof(FILEDESCRIPTORW));
+ if (!*file_descriptor_array)
+ {
+ result = ERROR_NOT_ENOUGH_MEMORY;
+ goto out;
+ }
+
+ for (i = 0; i < count; i++)
+ {
+ int c;
+ UINT64 lastWriteTime;
+ FILEDESCRIPTORW* file = &((*file_descriptor_array)[i]);
+
+ Stream_Read_UINT32(s, file->dwFlags); /* flags (4 bytes) */
+ Stream_Seek(s, 32); /* reserved1 (32 bytes) */
+ Stream_Read_UINT32(s, file->dwFileAttributes); /* fileAttributes (4 bytes) */
+ Stream_Seek(s, 16); /* reserved2 (16 bytes) */
+ Stream_Read_UINT64(s, lastWriteTime); /* lastWriteTime (8 bytes) */
+ file->ftLastWriteTime = uint64_to_filetime(lastWriteTime);
+ Stream_Read_UINT32(s, file->nFileSizeHigh); /* fileSizeHigh (4 bytes) */
+ Stream_Read_UINT32(s, file->nFileSizeLow); /* fileSizeLow (4 bytes) */
+ for (c = 0; c < 260; c++) /* cFileName (520 bytes) */
+ Stream_Read_UINT16(s, file->cFileName[c]);
+ }
+
+ if (Stream_GetRemainingLength(s) > 0)
+ WLog_WARN(TAG, "packed file list has %" PRIuz " excess bytes",
+ Stream_GetRemainingLength(s));
+out:
+ Stream_Free(s, FALSE);
+
+ return result;
+}
+
+#define CLIPRDR_MAX_FILE_SIZE (2U * 1024 * 1024 * 1024)
+
+/**
+ * Serialize a packed file list.
+ *
+ * The resulting format data must be freed with the `free()` function.
+ *
+ * @param [in] file_descriptor_array array of `FILEDESCRIPTOR` structs to serialize.
+ * @param [in] file_descriptor_count number of elements in `file_descriptor_array`.
+ * @param [out] format_data serialized CLIPRDR_FILELIST.
+ * @param [out] format_data_length length of `format_data` in bytes.
+ *
+ * @returns 0 on success, otherwise a Win32 error code.
+ */
+UINT cliprdr_serialize_file_list(const FILEDESCRIPTORW* file_descriptor_array,
+ UINT32 file_descriptor_count, BYTE** format_data,
+ UINT32* format_data_length)
+{
+ return cliprdr_serialize_file_list_ex(CB_STREAM_FILECLIP_ENABLED, file_descriptor_array,
+ file_descriptor_count, format_data, format_data_length);
+}
+
+UINT cliprdr_serialize_file_list_ex(UINT32 flags, const FILEDESCRIPTORW* file_descriptor_array,
+ UINT32 file_descriptor_count, BYTE** format_data,
+ UINT32* format_data_length)
+{
+ UINT result = NO_ERROR;
+ UINT32 i;
+ wStream* s = NULL;
+
+ if (!file_descriptor_array || !format_data || !format_data_length)
+ return ERROR_BAD_ARGUMENTS;
+
+ if ((flags & CB_STREAM_FILECLIP_ENABLED) == 0)
+ {
+ WLog_WARN(TAG, "No file clipboard support annouonced!");
+ return ERROR_BAD_ARGUMENTS;
+ }
+
+ s = Stream_New(NULL, 4 + file_descriptor_count * CLIPRDR_FILEDESCRIPTOR_SIZE);
+ if (!s)
+ return ERROR_NOT_ENOUGH_MEMORY;
+
+ Stream_Write_UINT32(s, file_descriptor_count); /* cItems (4 bytes) */
+
+ for (i = 0; i < file_descriptor_count; i++)
+ {
+ int c;
+ UINT64 lastWriteTime;
+ const FILEDESCRIPTORW* file = &file_descriptor_array[i];
+
+ /*
+ * There is a known issue with Windows server getting stuck in
+ * an infinite loop when downloading files that are larger than
+ * 2 gigabytes. Do not allow clients to send such file lists.
+ *
+ * https://support.microsoft.com/en-us/help/2258090
+ */
+ if ((flags & CB_HUGE_FILE_SUPPORT_ENABLED) == 0)
+ {
+ if ((file->nFileSizeHigh > 0) || (file->nFileSizeLow >= CLIPRDR_MAX_FILE_SIZE))
+ {
+ WLog_ERR(TAG, "cliprdr does not support files over 2 GB");
+ result = ERROR_FILE_TOO_LARGE;
+ goto error;
+ }
+ }
+
+ Stream_Write_UINT32(s, file->dwFlags); /* flags (4 bytes) */
+ Stream_Zero(s, 32); /* reserved1 (32 bytes) */
+ Stream_Write_UINT32(s, file->dwFileAttributes); /* fileAttributes (4 bytes) */
+ Stream_Zero(s, 16); /* reserved2 (16 bytes) */
+ lastWriteTime = filetime_to_uint64(file->ftLastWriteTime);
+ Stream_Write_UINT64(s, lastWriteTime); /* lastWriteTime (8 bytes) */
+ Stream_Write_UINT32(s, file->nFileSizeHigh); /* fileSizeHigh (4 bytes) */
+ Stream_Write_UINT32(s, file->nFileSizeLow); /* fileSizeLow (4 bytes) */
+ for (c = 0; c < 260; c++) /* cFileName (520 bytes) */
+ Stream_Write_UINT16(s, file->cFileName[c]);
+ }
+
+ Stream_SealLength(s);
+
+ Stream_GetBuffer(s, *format_data);
+ Stream_GetLength(s, *format_data_length);
+
+ Stream_Free(s, FALSE);
+
+ return result;
+
+error:
+ Stream_Free(s, TRUE);
+
+ return result;
+}
diff --git a/include/freerdp/channels/cliprdr.h b/include/freerdp/channels/cliprdr.h
index fbf23f6e5..86fc65890 100644
--- a/include/freerdp/channels/cliprdr.h
+++ b/include/freerdp/channels/cliprdr.h
@@ -22,7 +22,6 @@
#include <freerdp/api.h>
#include <freerdp/types.h>
-#include <freerdp/utils/cliprdr_utils.h>
#include <winpr/shell.h>
@@ -94,6 +93,17 @@ extern "C"
{
#endif
+ FREERDP_API UINT cliprdr_parse_file_list(const BYTE* format_data, UINT32 format_data_length,
+ FILEDESCRIPTORW** file_descriptor_array,
+ UINT32* file_descriptor_count);
+ FREERDP_API UINT cliprdr_serialize_file_list(const FILEDESCRIPTORW* file_descriptor_array,
+ UINT32 file_descriptor_count, BYTE** format_data,
+ UINT32* format_data_length);
+ FREERDP_API UINT cliprdr_serialize_file_list_ex(UINT32 flags,
+ const FILEDESCRIPTORW* file_descriptor_array,
+ UINT32 file_descriptor_count,
+ BYTE** format_data, UINT32* format_data_length);
+
#ifdef __cplusplus
}
#endif
diff --git a/include/freerdp/utils/cliprdr_utils.h b/include/freerdp/utils/cliprdr_utils.h
deleted file mode 100644
index 59ecf848f..000000000
--- a/include/freerdp/utils/cliprdr_utils.h
+++ /dev/null
@@ -1,48 +0,0 @@
-/**
- * FreeRDP: A Remote Desktop Protocol Implementation
- * RDPDR utility functions
- *
- * Copyright 2022 Armin Novak <armin.novak@thincast.com>
- * Copyright 2022 Thincast Technologies GmbH
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef FREERDP_UTILS_CLIPRDR_H
-#define FREERDP_UTILS_CLIPRDR_H
-
-#include <winpr/wtypes.h>
-#include <winpr/shell.h>
-#include <freerdp/api.h>
-
-#ifdef __cplusplus
-extern "C"
-{
-#endif
-
- FREERDP_API UINT cliprdr_parse_file_list(const BYTE* format_data, UINT32 format_data_length,
- FILEDESCRIPTORW** file_descriptor_array,
- UINT32* file_descriptor_count);
- FREERDP_API UINT cliprdr_serialize_file_list(const FILEDESCRIPTORW* file_descriptor_array,
- UINT32 file_descriptor_count, BYTE** format_data,
- UINT32* format_data_length);
- FREERDP_API UINT cliprdr_serialize_file_list_ex(UINT32 flags,
- const FILEDESCRIPTORW* file_descriptor_array,
- UINT32 file_descriptor_count,
- BYTE** format_data, UINT32* format_data_length);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
diff --git a/libfreerdp/utils/CMakeLists.txt b/libfreerdp/utils/CMakeLists.txt
index 2ec561d33..7bff6736a 100644
--- a/libfreerdp/utils/CMakeLists.txt
+++ b/libfreerdp/utils/CMakeLists.txt
@@ -20,7 +20,6 @@ set(MODULE_PREFIX "FREERDP_UTILS")
set(${MODULE_PREFIX}_SRCS
passphrase.c
- cliprdr_utils.c
pcap.c
profiler.c
ringbuffer.c
diff --git a/libfreerdp/utils/cliprdr_utils.c b/libfreerdp/utils/cliprdr_utils.c
deleted file mode 100644
index 7fb99d63a..000000000
--- a/libfreerdp/utils/cliprdr_utils.c
+++ /dev/null
@@ -1,235 +0,0 @@
-/**
- * FreeRDP: A Remote Desktop Protocol Implementation
- * Clipboard Virtual Channel Extension
- *
- * Copyright 2013 Marc-Andre Moreau <marcandre.moreau@gmail.com>
- * Copyright 2022 Armin Novak <anovak@thincast.com
- * Copyright 2022 Thincast Technologies GmbH
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <winpr/stream.h>
-#include <freerdp/utils/cliprdr_utils.h>
-#include <freerdp/channels/cliprdr.h>
-
-#include <freerdp/log.h>
-#define TAG FREERDP_TAG("utils." CLIPRDR_SVC_CHANNEL_NAME)
-
-#define CLIPRDR_FILEDESCRIPTOR_SIZE (4 + 32 + 4 + 16 + 8 + 8 + 520)
-#define CLIPRDR_MAX_FILE_SIZE (2U * 1024 * 1024 * 1024)
-
-static UINT64 filetime_to_uint64(FILETIME value)
-{
- UINT64 converted = 0;
- converted |= (UINT32)value.dwHighDateTime;
- converted <<= 32;
- converted |= (UINT32)value.dwLowDateTime;
- return converted;
-}
-
-static FILETIME uint64_to_filetime(UINT64 value)
-{
- FILETIME converted;
- converted.dwLowDateTime = (UINT32)(value >> 0);
- converted.dwHighDateTime = (UINT32)(value >> 32);
- return converted;
-}
-
-/**
- * Parse a packed file list.
- *
- * The resulting array must be freed with the `free()` function.
- *
- * @param [in] format_data packed `CLIPRDR_FILELIST` to parse.
- * @param [in] format_data_length length of `format_data` in bytes.
- * @param [out] file_descriptor_array parsed array of `FILEDESCRIPTOR` structs.
- * @param [out] file_descriptor_count number of elements in `file_descriptor_array`.
- *
- * @returns 0 on success, otherwise a Win32 error code.
- */
-UINT cliprdr_parse_file_list(const BYTE* format_data, UINT32 format_data_length,
- FILEDESCRIPTORW** file_descriptor_array, UINT32* file_descriptor_count)
-{
- UINT result = NO_ERROR;
- UINT32 i;
- UINT32 count = 0;
- wStream sbuffer;
- wStream* s = &sbuffer;
-
- if (!format_data || !file_descriptor_array || !file_descriptor_count)
- return ERROR_BAD_ARGUMENTS;
-
- Stream_StaticInit(&sbuffer, format_data, format_data_length);
- if (!s)
- return ERROR_NOT_ENOUGH_MEMORY;
-
- if (Stream_GetRemainingLength(s) < 4)
- {
- WLog_ERR(TAG, "invalid packed file list");
-
- result = ERROR_INCORRECT_SIZE;
- goto out;
- }
-
- Stream_Read_UINT32(s, count); /* cItems (4 bytes) */
-
- if (Stream_GetRemainingLength(s) / CLIPRDR_FILEDESCRIPTOR_SIZE < count)
- {
- WLog_ERR(TAG, "packed file list is too short: expected %" PRIuz ", have %" PRIuz,
- ((size_t)count) * CLIPRDR_FILEDESCRIPTOR_SIZE, Stream_GetRemainingLength(s));
-
- result = ERROR_INCORRECT_SIZE;
- goto out;
- }
-
- *file_descriptor_count = count;
- *file_descriptor_array = calloc(count, sizeof(FILEDESCRIPTORW));
- if (!*file_descriptor_array)
- {
- result = ERROR_NOT_ENOUGH_MEMORY;
- goto out;
- }
-
- for (i = 0; i < count; i++)
- {
- UINT64 tmp;
- FILEDESCRIPTORW* file = &((*file_descriptor_array)[i]);
-
- Stream_Read_UINT32(s, file->dwFlags); /* flags (4 bytes) */
- Stream_Read_UINT32(s, file->clsid.Data1);
- Stream_Read_UINT16(s, file->clsid.Data2);
- Stream_Read_UINT16(s, file->clsid.Data3);
- Stream_Read(s, &file->clsid.Data4, sizeof(file->clsid.Data4));
- Stream_Read_INT32(s, file->sizel.cx);
- Stream_Read_INT32(s, file->sizel.cy);
- Stream_Read_INT32(s, file->pointl.x);
- Stream_Read_INT32(s, file->pointl.y);
- Stream_Read_UINT32(s, file->dwFileAttributes); /* fileAttributes (4 bytes) */
- Stream_Read_UINT64(s, tmp); /* ftCreationTime (8 bytes) */
- file->ftCreationTime = uint64_to_filetime(tmp);
- Stream_Read_UINT64(s, tmp); /* ftLastAccessTime (8 bytes) */
- file->ftLastAccessTime = uint64_to_filetime(tmp);
- Stream_Read_UINT64(s, tmp); /* lastWriteTime (8 bytes) */
- file->ftLastWriteTime = uint64_to_filetime(tmp);
- Stream_Read_UINT32(s, file->nFileSizeHigh); /* fileSizeHigh (4 bytes) */
- Stream_Read_UINT32(s, file->nFileSizeLow); /* fileSizeLow (4 bytes) */
- Stream_Read_UTF16_String(s, file->cFileName,
- ARRAYSIZE(file->cFileName)); /* cFileName (520 bytes) */
- }
-
- if (Stream_GetRemainingLength(s) > 0)
- WLog_WARN(TAG, "packed file list has %" PRIuz " excess bytes",
- Stream_GetRemainingLength(s));
-out:
-
- return result;
-}
-
-/**
- * Serialize a packed file list.
- *
- * The resulting format data must be freed with the `free()` function.
- *
- * @param [in] file_descriptor_array array of `FILEDESCRIPTOR` structs to serialize.
- * @param [in] file_descriptor_count number of elements in `file_descriptor_array`.
- * @param [out] format_data serialized CLIPRDR_FILELIST.
- * @param [out] format_data_length length of `format_data` in bytes.
- *
- * @returns 0 on success, otherwise a Win32 error code.
- */
-UINT cliprdr_serialize_file_list(const FILEDESCRIPTORW* file_descriptor_array,
- UINT32 file_descriptor_count, BYTE** format_data,
- UINT32* format_data_length)
-{
- return cliprdr_serialize_file_list_ex(CB_STREAM_FILECLIP_ENABLED, file_descriptor_array,
- file_descriptor_count, format_data, format_data_length);
-}
-
-UINT cliprdr_serialize_file_list_ex(UINT32 flags, const FILEDESCRIPTORW* file_descriptor_array,
- UINT32 file_descriptor_count, BYTE** format_data,
- UINT32* format_data_length)
-{
- UINT result = NO_ERROR;
- UINT32 i;
- size_t len;
- wStream* s = NULL;
-
- if (!file_descriptor_array || !format_data || !format_data_length)
- return ERROR_BAD_ARGUMENTS;
-
- if ((flags & CB_STREAM_FILECLIP_ENABLED) == 0)
- {
- WLog_WARN(TAG, "No file clipboard support annouonced!");
- return ERROR_BAD_ARGUMENTS;
- }
-
- s = Stream_New(NULL, 4 + file_descriptor_count * CLIPRDR_FILEDESCRIPTOR_SIZE);
- if (!s)
- return ERROR_NOT_ENOUGH_MEMORY;
-
- Stream_Write_UINT32(s, file_descriptor_count); /* cItems (4 bytes) */
-
- for (i = 0; i < file_descriptor_count; i++)
- {
- int c;
- UINT64 lastWriteTime;
- const FILEDESCRIPTORW* file = &file_descriptor_array[i];
-
- /*
- * There is a known issue with Windows server getting stuck in
- * an infinite loop when downloading files that are larger than
- * 2 gigabytes. Do not allow clients to send such file lists.
- *
- * https://support.microsoft.com/en-us/help/2258090
- */
- if ((flags & CB_HUGE_FILE_SUPPORT_ENABLED) == 0)
- {
- if ((file->nFileSizeHigh > 0) || (file->nFileSizeLow >= CLIPRDR_MAX_FILE_SIZE))
- {
- WLog_ERR(TAG, "cliprdr does not support files over 2 GB");
- result = ERROR_FILE_TOO_LARGE;
- goto error;
- }
- }
-
- Stream_Write_UINT32(s, file->dwFlags); /* flags (4 bytes) */
- Stream_Zero(s, 32); /* reserved1 (32 bytes) */
- Stream_Write_UINT32(s, file->dwFileAttributes); /* fileAttributes (4 bytes) */
- Stream_Zero(s, 16); /* reserved2 (16 bytes) */
- lastWriteTime = filetime_to_uint64(file->ftLastWriteTime);
- Stream_Write_UINT64(s, lastWriteTime); /* lastWriteTime (8 bytes) */
- Stream_Write_UINT32(s, file->nFileSizeHigh); /* fileSizeHigh (4 bytes) */
- Stream_Write_UINT32(s, file->nFileSizeLow); /* fileSizeLow (4 bytes) */
- for (c = 0; c < 260; c++) /* cFileName (520 bytes) */
- Stream_Write_UINT16(s, file->cFileName[c]);
- }
-
- Stream_SealLength(s);
-
- Stream_GetBuffer(s, *format_data);
- Stream_GetLength(s, len);
- if (len > UINT32_MAX)
- goto error;
-
- *format_data_length = (UINT32)len;
-
- Stream_Free(s, FALSE);
-
- return result;
-
-error:
- Stream_Free(s, TRUE);
-
- return result;
-}
--
2.46.1

View File

@ -1,36 +0,0 @@
From 563a19a683d6e0e7b1218e4558a0fb75333b5b34 Mon Sep 17 00:00:00 2001
From: Ondrej Holy <oholy@redhat.com>
Date: Tue, 28 Apr 2026 04:14:23 +0000
Subject: [PATCH] [allocations] fix growth of preallocated buffers
Partial backport of commit 118afc0b954ba9d5632b7836ad24e454555ed113.
Only the `Stream_EnsureCapacity` fix is included. Other changes from
the upstream commit (`sizeof(WCHAR)` replacements, collection growth
patterns, environment block handling) are unrelated to CVE-2026-27951.
Made-with: Cursor
---
winpr/libwinpr/utils/stream.c | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/winpr/libwinpr/utils/stream.c b/winpr/libwinpr/utils/stream.c
index cc119c7..cb145b0 100644
--- a/winpr/libwinpr/utils/stream.c
+++ b/winpr/libwinpr/utils/stream.c
@@ -40,8 +40,10 @@ BOOL Stream_EnsureCapacity(wStream* s, size_t size)
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);
--
2.53.0

View File

@ -1,52 +0,0 @@
From 23320a6d5f2e1c8a9b7d6f4e3c2a1b0987654321 Mon Sep 17 00:00:00 2001
From: Ondrej Holy <oholy@redhat.com>
Date: Tue, 28 Apr 2026 04:25:58 +0000
Subject: [PATCH] [cache,bitmap] initialize overallocated bitmap cache extra
slot
Backport of commit 8270e0bb3d6726c947d57c93ba9caa92a052b557.
Adjusted hunk offsets for 2.11.7.
Made-with: Cursor
---
libfreerdp/cache/bitmap.c | 16 +++++++++++++++-
1 file changed, 15 insertions(+), 1 deletion(-)
diff --git a/libfreerdp/cache/bitmap.c b/libfreerdp/cache/bitmap.c
index b8a4f21..23320a6 100644
--- a/libfreerdp/cache/bitmap.c
+++ b/libfreerdp/cache/bitmap.c
@@ -303,6 +303,19 @@ rdpBitmapCache* bitmap_cache_new(rdpSettings* settings)
cell->number = nr;
}
+ /* initialize the overallocated extra slot for old RDP servers that send
+ * cacheId == maxCells; use a minimal allocation since no protocol-negotiated
+ * capacity exists for this slot */
+ {
+ BITMAP_V2_CELL* extra = &bitmapCache->cells[bitmapCache->maxCells];
+ /* allocate an extra entry for BITMAP_CACHE_WAITING_LIST_INDEX */
+ extra->entries = (rdpBitmap**)calloc(1, sizeof(rdpBitmap*));
+
+ if (!extra->entries)
+ goto fail;
+ extra->number = 0;
+ }
+
return bitmapCache;
fail:
@@ -315,7 +328,8 @@ void bitmap_cache_free(rdpBitmapCache* bitmapCache)
if (bitmapCache)
{
UINT32 i;
- for (i = 0; i < bitmapCache->maxCells; i++)
+ /* iterate through maxCells + 1 to also free the overallocated extra slot */
+ for (i = 0; i <= bitmapCache->maxCells; i++)
{
UINT32 j;
BITMAP_V2_CELL* cell = &bitmapCache->cells[i];
--
2.53.0

View File

@ -1,33 +0,0 @@
From 608c5d40f6ab4cabd4d5793b2d641f401e146233 Mon Sep 17 00:00:00 2001
From: Ondrej Holy <oholy@redhat.com>
Date: Tue, 28 Apr 2026 04:25:52 +0000
Subject: [PATCH] [cache,bitmap] overallocate bitmap cache
Backport of commit ffad58fd2b329efd81a3239e9d7e3c927b8e503f.
Adjusted hunk offsets for 2.11.7.
Made-with: Cursor
---
libfreerdp/cache/bitmap.c | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/libfreerdp/cache/bitmap.c b/libfreerdp/cache/bitmap.c
index 0ce2599..b8a4f21 100644
--- a/libfreerdp/cache/bitmap.c
+++ b/libfreerdp/cache/bitmap.c
@@ -281,8 +281,10 @@ rdpBitmapCache* bitmap_cache_new(rdpSettings* settings)
bitmapCache->settings = settings;
bitmapCache->update = ((freerdp*)settings->instance)->update;
bitmapCache->context = bitmapCache->update->context;
+
+ /* overallocate by 1. older RDP servers do send a off by 1 cache index. */
bitmapCache->cells =
- (BITMAP_V2_CELL*)calloc(settings->BitmapCacheV2NumCells, sizeof(BITMAP_V2_CELL));
+ (BITMAP_V2_CELL*)calloc(settings->BitmapCacheV2NumCells + 1ull, sizeof(BITMAP_V2_CELL));
if (!bitmapCache->cells)
goto fail;
--
2.53.0

View File

@ -1,46 +0,0 @@
From 4a4126380430a3517d1015b628475b816705ceb4 Mon Sep 17 00:00:00 2001
From: Ondrej Holy <oholy@redhat.com>
Date: Thu, 22 Jan 2026 12:52:16 +0100
Subject: [PATCH] [cache,offscreen] invalidate bitmap before free
Backport of commit 52106a26726a2aba77aa6d86014d2eb3507f0783.
Co-Authored-By: Claude <noreply@anthropic.com>
---
libfreerdp/cache/offscreen.c | 10 ++++++++--
1 file changed, 8 insertions(+), 2 deletions(-)
diff --git a/libfreerdp/cache/offscreen.c b/libfreerdp/cache/offscreen.c
index cdad56b4d..11e6caf21 100644
--- a/libfreerdp/cache/offscreen.c
+++ b/libfreerdp/cache/offscreen.c
@@ -160,7 +160,7 @@ void offscreen_cache_put(rdpOffscreenCache* offscreenCache, UINT32 index, rdpBit
void offscreen_cache_delete(rdpOffscreenCache* offscreenCache, UINT32 index)
{
- rdpBitmap* prevBitmap;
+ WINPR_ASSERT(offscreenCache);
if (index >= offscreenCache->maxEntries)
{
@@ -168,10 +168,16 @@ void offscreen_cache_delete(rdpOffscreenCache* offscreenCache, UINT32 index)
return;
}
- prevBitmap = offscreenCache->entries[index];
+ rdpBitmap* prevBitmap = offscreenCache->entries[index];
if (prevBitmap != NULL)
+ {
+ WINPR_ASSERT(offscreenCache->update->context);
+
+ /* Ensure that the bitmap is no longer used in GDI */
+ IFCALL(prevBitmap->SetSurface, offscreenCache->update->context, NULL, FALSE);
Bitmap_Free(offscreenCache->update->context, prevBitmap);
+ }
offscreenCache->entries[index] = NULL;
}
--
2.52.0

View File

@ -1,117 +0,0 @@
From b96bfa485a286335bcb4c698fa6490fb4a98b9e6 Mon Sep 17 00:00:00 2001
From: Ondrej Holy <oholy@redhat.com>
Date: Fri, 13 Mar 2026 15:47:11 +0100
Subject: [PATCH] [channel,rdpsnd] only clean up thread before free
Backport of commit afa6851dc80835d3101e40fcef51b6c5c0f43ea5.
Made-with: Cursor
---
channels/rdpsnd/client/rdpsnd_main.c | 37 ++++++++++++++++------------
1 file changed, 21 insertions(+), 16 deletions(-)
diff --git a/channels/rdpsnd/client/rdpsnd_main.c b/channels/rdpsnd/client/rdpsnd_main.c
index c7660a857..576e065d2 100644
--- a/channels/rdpsnd/client/rdpsnd_main.c
+++ b/channels/rdpsnd/client/rdpsnd_main.c
@@ -133,6 +133,8 @@ struct rdpsnd_plugin
BOOL applyVolume;
};
+static DWORD WINAPI play_thread(LPVOID arg);
+
static const char* rdpsnd_is_dyn_str(BOOL dynamic)
{
if (dynamic)
@@ -1267,7 +1269,6 @@ static void cleanup_internals(rdpsndPlugin* rdpsnd)
if (!rdpsnd)
return;
- rdpsnd_terminate_thread(rdpsnd);
if (rdpsnd->pool)
StreamPool_Return(rdpsnd->pool, rdpsnd->data_in);
@@ -1331,6 +1332,7 @@ static void free_internals(rdpsndPlugin* rdpsnd)
if (!rdpsnd)
return;
+ rdpsnd_terminate_thread(rdpsnd);
freerdp_dsp_context_free(rdpsnd->dsp_context);
StreamPool_Free(rdpsnd->pool);
rdpsnd->pool = NULL;
@@ -1353,6 +1355,23 @@ static BOOL allocate_internals(rdpsndPlugin* rdpsnd)
return FALSE;
}
+ if (!rdpsnd->queue)
+ {
+ wObject obj = { 0 };
+
+ obj.fnObjectFree = _queue_free;
+ rdpsnd->queue = MessageQueue_New(&obj);
+ if (!rdpsnd->queue)
+ return FALSE;
+ }
+
+ if (!rdpsnd->thread)
+ {
+ rdpsnd->thread = CreateThread(NULL, 0, play_thread, rdpsnd, 0, NULL);
+ if (!rdpsnd->thread)
+ return FALSE;
+ }
+
return TRUE;
}
@@ -1391,23 +1410,12 @@ static DWORD WINAPI play_thread(LPVOID arg)
static UINT rdpsnd_virtual_channel_event_initialized(rdpsndPlugin* rdpsnd)
{
- wObject obj = { 0 };
-
if (!rdpsnd)
return ERROR_INVALID_PARAMETER;
- obj.fnObjectFree = _queue_free;
- rdpsnd->queue = MessageQueue_New(&obj);
- if (!rdpsnd->queue)
- return CHANNEL_RC_NO_MEMORY;
-
if (!allocate_internals(rdpsnd))
return CHANNEL_RC_NO_MEMORY;
- rdpsnd->thread = CreateThread(NULL, 0, play_thread, rdpsnd, 0, NULL);
- if (!rdpsnd->thread)
- return CHANNEL_RC_INITIALIZATION_ERROR;
-
return CHANNEL_RC_OK;
}
@@ -1415,8 +1423,6 @@ void rdpsnd_virtual_channel_event_terminated(rdpsndPlugin* rdpsnd)
{
if (rdpsnd)
{
- rdpsnd_terminate_thread(rdpsnd);
-
free_internals(rdpsnd);
audio_formats_free(rdpsnd->fixed_format, 1);
free(rdpsnd->subsystem);
@@ -1604,14 +1610,13 @@ static UINT rdpsnd_on_close(IWTSVirtualChannelCallback* pChannelCallback)
IFCALL(rdpsnd->device->Close, rdpsnd->device);
cleanup_internals(rdpsnd);
-
+ free_internals(rdpsnd);
if (rdpsnd->device)
{
IFCALL(rdpsnd->device->Free, rdpsnd->device);
rdpsnd->device = NULL;
}
- free_internals(rdpsnd);
free(pChannelCallback);
return CHANNEL_RC_OK;
}
--
2.53.0

View File

@ -1,132 +0,0 @@
From 6bf4e2558861e06fd68ef8036796ff475a1c23fa Mon Sep 17 00:00:00 2001
From: Ondrej Holy <oholy@redhat.com>
Date: Fri, 13 Mar 2026 15:24:09 +0100
Subject: [PATCH] [channels,ainput] lock context when updating listener
Backport of commit d9ca272dce7a776ab475e9b1a8e8c3d2968c8486.
Made-with: Cursor
---
channels/ainput/client/ainput_main.c | 38 ++++++++++++++++++++--------
1 file changed, 27 insertions(+), 11 deletions(-)
diff --git a/channels/ainput/client/ainput_main.c b/channels/ainput/client/ainput_main.c
index d8eb8ec59..ff901d2a0 100644
--- a/channels/ainput/client/ainput_main.c
+++ b/channels/ainput/client/ainput_main.c
@@ -28,6 +28,7 @@
#include <winpr/crt.h>
#include <winpr/assert.h>
#include <winpr/stream.h>
+#include <winpr/synch.h>
#include <winpr/sysinfo.h>
#include "ainput_main.h"
@@ -69,6 +70,7 @@ struct AINPUT_PLUGIN_
UINT32 MajorVersion;
UINT32 MinorVersion;
BOOL initialized;
+ CRITICAL_SECTION lock;
};
/**
@@ -109,10 +111,7 @@ 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;
- AINPUT_CHANNEL_CALLBACK* callback;
BYTE buffer[32] = { 0 };
- UINT64 time;
wStream sbuffer = { 0 };
wStream* s = &sbuffer;
@@ -121,8 +120,8 @@ static UINT ainput_send_input_event(AInputClientContext* context, UINT64 flags,
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);
WINPR_ASSERT(ainput->listener_callback);
@@ -132,8 +131,6 @@ static UINT ainput_send_input_event(AInputClientContext* context, UINT64 flags,
ainput->MajorVersion, ainput->MinorVersion);
return CHANNEL_RC_UNSUPPORTED_VERSION;
}
- callback = ainput->listener_callback->channel_callback;
- WINPR_ASSERT(callback);
{
char buffer[128] = { 0 };
@@ -152,10 +149,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);
+ AINPUT_CHANNEL_CALLBACK* callback = ainput->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;
}
/**
@@ -167,8 +169,16 @@ static UINT ainput_on_close(IWTSVirtualChannelCallback* pChannelCallback)
{
AINPUT_CHANNEL_CALLBACK* callback = (AINPUT_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;
}
@@ -255,6 +265,7 @@ static UINT ainput_plugin_initialize(IWTSPlugin* pPlugin, IWTSVirtualChannelMana
static UINT ainput_plugin_terminated(IWTSPlugin* pPlugin)
{
AINPUT_PLUGIN* ainput = (AINPUT_PLUGIN*)pPlugin;
+
if (ainput && ainput->listener_callback)
{
IWTSVirtualChannelManager* mgr = ainput->listener_callback->channel_mgr;
@@ -263,6 +274,7 @@ static UINT ainput_plugin_terminated(IWTSPlugin* pPlugin)
}
if (ainput)
{
+ DeleteCriticalSection(&ainput->lock);
free(ainput->listener_callback);
free(ainput->iface.pInterface);
}
@@ -306,8 +318,12 @@ UINT DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints)
context->handle = (void*)ainput;
context->AInputSendInputEvent = ainput_send_input_event;
- ainput->iface.pInterface = (void*)context;
+ InitializeCriticalSection(&ainput->lock);
+
+ EnterCriticalSection(&ainput->lock);
+ ainput->iface.pInterface = (void*)context;
+ LeaveCriticalSection(&ainput->lock);
status = pEntryPoints->RegisterPlugin(pEntryPoints, AINPUT_CHANNEL_NAME, &ainput->iface);
}
--
2.53.0

View File

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

View File

@ -1,37 +0,0 @@
From 97e9d90528690f6973398c9d7e4f8da52145f669 Mon Sep 17 00:00:00 2001
From: Armin Novak <armin.novak@thincast.com>
Date: Sun, 12 Jul 2026 17:37:48 +0200
Subject: [PATCH] [channels,audin] limit FramesPerPacket
---
channels/audin/client/audin_main.c | 10 +++++++++-
1 file changed, 9 insertions(+), 1 deletion(-)
diff --git a/channels/audin/client/audin_main.c b/channels/audin/client/audin_main.c
index 332aef9da..186e356cd 100644
--- a/channels/audin/client/audin_main.c
+++ b/channels/audin/client/audin_main.c
@@ -488,7 +488,6 @@ static UINT audin_process_open(AUDIN_PLUGIN* audin, AUDIN_CHANNEL_CALLBACK* cal
Stream_Read_UINT32(s, initialFormat);
WLog_Print(audin->log, WLOG_DEBUG, "FramesPerPacket=%" PRIu32 " initialFormat=%" PRIu32 "",
FramesPerPacket, initialFormat);
- audin->FramesPerPacket = FramesPerPacket;
if (initialFormat >= callback->formats_count)
{
@@ -496,6 +496,15 @@ static UINT audin_process_open(AUDIN_PLUGIN* audin, AUDIN_CHANNEL_CALLBACK* cal
return ERROR_INVALID_DATA;
}
+ /* The RDP protocol field allows UINT32_MAX, but most backend API use INT32 as input parameter
+ * or do some math with it, so ensure that the value does not exceed reasonable limits */
+ if (FramesPerPacket >= INT32_MAX)
+ {
+ WLog_Print(audin->log, WLOG_ERROR, "invalid frames per packet %" PRIu32, FramesPerPacket);
+ return ERROR_INVALID_DATA;
+ }
+
+ audin->FramesPerPacket = FramesPerPacket;
audin->format = &callback->formats[initialFormat];
if (!audin_open_device(audin, callback))

View File

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

View File

@ -1,34 +0,0 @@
From 7865a2832ebdfd04374c787c917b3ea7f078ba2c Mon Sep 17 00:00:00 2001
From: Ondrej Holy <oholy@redhat.com>
Date: Wed, 11 Mar 2026 16:55:44 +0100
Subject: [PATCH] [channels,drdynvc] reset channel_callback before close
Backport of commit e02e052f6692550e539d10f99de9c35a23492db2.
Made-with: Cursor
---
channels/drdynvc/client/drdynvc_main.c | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/channels/drdynvc/client/drdynvc_main.c b/channels/drdynvc/client/drdynvc_main.c
index b2005a903..0a4af7179 100644
--- a/channels/drdynvc/client/drdynvc_main.c
+++ b/channels/drdynvc/client/drdynvc_main.c
@@ -346,10 +346,11 @@ static void dvcman_channel_free(void* arg)
if (channel)
{
- if (channel->channel_callback)
+ IWTSVirtualChannelCallback* cb = channel->channel_callback;
+ channel->channel_callback = NULL;
+ if (cb)
{
- IFCALL(channel->channel_callback->OnClose, channel->channel_callback);
- channel->channel_callback = NULL;
+ IFCALL(cb->OnClose, cb);
}
if (channel->status == CHANNEL_RC_OK)
--
2.53.0

View File

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

View File

@ -1,73 +0,0 @@
From c9f2b4222cf51a259b77ed4e83c45f6a08884cb8 Mon Sep 17 00:00:00 2001
From: Ondrej Holy <oholy@redhat.com>
Date: Fri, 13 Mar 2026 15:46:17 +0100
Subject: [PATCH] [channels,rdpsnd] terminate thread before free
Backport of commit 622bb7b4402491ca003f47472d0e478132673696.
Made-with: Cursor
---
channels/rdpsnd/client/rdpsnd_main.c | 28 ++++++++++++++++++++--------
1 file changed, 20 insertions(+), 8 deletions(-)
diff --git a/channels/rdpsnd/client/rdpsnd_main.c b/channels/rdpsnd/client/rdpsnd_main.c
index 3de4cbc66..c7660a857 100644
--- a/channels/rdpsnd/client/rdpsnd_main.c
+++ b/channels/rdpsnd/client/rdpsnd_main.c
@@ -36,6 +36,7 @@
#include <string.h>
#include <errno.h>
+#include <winpr/assert.h>
#include <winpr/crt.h>
#include <winpr/wlog.h>
#include <winpr/stream.h>
@@ -1244,11 +1245,29 @@ fail:
return CHANNEL_RC_NO_MEMORY;
}
+static void rdpsnd_terminate_thread(rdpsndPlugin* rdpsnd)
+{
+ WINPR_ASSERT(rdpsnd);
+ if (rdpsnd->queue)
+ MessageQueue_PostQuit(rdpsnd->queue, 0);
+
+ if (rdpsnd->thread)
+ {
+ (void)WaitForSingleObject(rdpsnd->thread, INFINITE);
+ (void)CloseHandle(rdpsnd->thread);
+ }
+
+ MessageQueue_Free(rdpsnd->queue);
+ rdpsnd->thread = NULL;
+ rdpsnd->queue = NULL;
+}
+
static void cleanup_internals(rdpsndPlugin* rdpsnd)
{
if (!rdpsnd)
return;
+ rdpsnd_terminate_thread(rdpsnd);
if (rdpsnd->pool)
StreamPool_Return(rdpsnd->pool, rdpsnd->data_in);
@@ -1396,14 +1415,7 @@ void rdpsnd_virtual_channel_event_terminated(rdpsndPlugin* rdpsnd)
{
if (rdpsnd)
{
- if (rdpsnd->queue)
- MessageQueue_PostQuit(rdpsnd->queue, 0);
- if (rdpsnd->thread)
- {
- WaitForSingleObject(rdpsnd->thread, INFINITE);
- CloseHandle(rdpsnd->thread);
- }
- MessageQueue_Free(rdpsnd->queue);
+ rdpsnd_terminate_thread(rdpsnd);
free_internals(rdpsnd);
audio_formats_free(rdpsnd->fixed_format, 1);
--
2.53.0

View File

@ -1,55 +0,0 @@
From c0283812c918c7738facafac52cf51d916536138 Mon Sep 17 00:00:00 2001
From: Ondrej Holy <oholy@redhat.com>
Date: Tue, 10 Mar 2026 08:47:57 +0100
Subject: [PATCH] [channels,serial] explicitly lock serial->IrpThreads
Backport of commit 675c20f08f32ca5ec06297108bdf30147d6e2cd9.
Made-with: Cursor
---
channels/serial/client/serial_main.c | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/channels/serial/client/serial_main.c b/channels/serial/client/serial_main.c
index d76d725f1..c8a27c46c 100644
--- a/channels/serial/client/serial_main.c
+++ b/channels/serial/client/serial_main.c
@@ -597,7 +597,10 @@ static void create_irp_thread(SERIAL_DEVICE* serial, IRP* irp)
* observed with FreeRDP).
*/
key = irp->CompletionId;
+
+ ListDictionary_Lock(serial->IrpThreads);
previousIrpThread = ListDictionary_GetItemValue(serial->IrpThreads, (void*)key);
+ ListDictionary_Unlock(serial->IrpThreads);
if (previousIrpThread)
{
@@ -678,6 +681,7 @@ static void terminate_pending_irp_threads(SERIAL_DEVICE* serial)
{
ULONG_PTR* ids;
int i, nbIds;
+ ListDictionary_Lock(serial->IrpThreads);
nbIds = ListDictionary_GetKeys(serial->IrpThreads, &ids);
WLog_Print(serial->log, WLOG_DEBUG, "Terminating %d IRP thread(s)", nbIds);
@@ -699,6 +703,7 @@ static void terminate_pending_irp_threads(SERIAL_DEVICE* serial)
}
ListDictionary_Clear(serial->IrpThreads);
+ ListDictionary_Unlock(serial->IrpThreads);
free(ids);
}
@@ -932,7 +937,7 @@ UINT DeviceServiceEntry(PDEVICE_SERVICE_ENTRY_POINTS pEntryPoints)
}
/* IrpThreads content only modified by create_irp_thread() */
- serial->IrpThreads = ListDictionary_New(TRUE);
+ serial->IrpThreads = ListDictionary_New(FALSE);
if (!serial->IrpThreads)
{
--
2.53.0

View File

@ -1,89 +0,0 @@
From 7cf742159bd2675366c7187c4bc51018b548d228 Mon Sep 17 00:00:00 2001
From: Ondrej Holy <oholy@redhat.com>
Date: Tue, 10 Mar 2026 08:47:45 +0100
Subject: [PATCH] [channels,serial] lock list dictionary
Backport of commit b35aa3614d32bff3fc1272cd7c4617f711fca1a4.
Made-with: Cursor
---
channels/serial/client/serial_main.c | 35 ++++++++++++++++------------
1 file changed, 20 insertions(+), 15 deletions(-)
diff --git a/channels/serial/client/serial_main.c b/channels/serial/client/serial_main.c
index afe67b4d3..d76d725f1 100644
--- a/channels/serial/client/serial_main.c
+++ b/channels/serial/client/serial_main.c
@@ -540,6 +540,7 @@ static void create_irp_thread(SERIAL_DEVICE* serial, IRP* irp)
HANDLE irpThread;
ULONG_PTR* ids;
int i, nbIds;
+ ListDictionary_Lock(serial->IrpThreads);
nbIds = ListDictionary_GetKeys(serial->IrpThreads, &ids);
for (i = 0; i < nbIds; i++)
@@ -581,6 +582,7 @@ static void create_irp_thread(SERIAL_DEVICE* serial, IRP* irp)
}
free(ids);
+ ListDictionary_Unlock(serial->IrpThreads);
}
LeaveCriticalSection(&serial->TerminatingIrpThreadsLock);
@@ -621,19 +623,6 @@ static void create_irp_thread(SERIAL_DEVICE* serial, IRP* irp)
return;
}
- if (ListDictionary_Count(serial->IrpThreads) >= MAX_IRP_THREADS)
- {
- WLog_Print(serial->log, WLOG_WARN,
- "Number of IRP threads threshold reached: %d, keep on anyway",
- ListDictionary_Count(serial->IrpThreads));
- assert(FALSE); /* unimplemented */
- /* TODO: MAX_IRP_THREADS has been thought to avoid a
- * flooding of pending requests. Use
- * WaitForMultipleObjects() when available in winpr
- * for threads.
- */
- }
-
/* error_handle to be used ... */
data = (IRP_THREAD_DATA*)calloc(1, sizeof(IRP_THREAD_DATA));
@@ -656,7 +645,23 @@ static void create_irp_thread(SERIAL_DEVICE* serial, IRP* irp)
key = irp->CompletionId;
- if (!ListDictionary_Add(serial->IrpThreads, (void*)key, irpThread))
+ ListDictionary_Lock(serial->IrpThreads);
+ if (ListDictionary_Count(serial->IrpThreads) >= MAX_IRP_THREADS)
+ {
+ WLog_Print(serial->log, WLOG_WARN,
+ "Number of IRP threads threshold reached: %d, keep on anyway",
+ ListDictionary_Count(serial->IrpThreads));
+ assert(FALSE); /* unimplemented */
+ /* TODO: MAX_IRP_THREADS has been thought to avoid a
+ * flooding of pending requests. Use
+ * WaitForMultipleObjects() when available in winpr
+ * for threads.
+ */
+ }
+ const BOOL added = ListDictionary_Add(serial->IrpThreads, (void*)key, irpThread);
+ ListDictionary_Unlock(serial->IrpThreads);
+
+ if (!added)
{
WLog_ERR(TAG, "ListDictionary_Add failed!");
goto error_handle;
@@ -927,7 +932,7 @@ UINT DeviceServiceEntry(PDEVICE_SERVICE_ENTRY_POINTS pEntryPoints)
}
/* IrpThreads content only modified by create_irp_thread() */
- serial->IrpThreads = ListDictionary_New(FALSE);
+ serial->IrpThreads = ListDictionary_New(TRUE);
if (!serial->IrpThreads)
{
--
2.53.0

View File

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

View File

@ -1,70 +0,0 @@
From f8453bda5069314d271973eff69dc2e04f6df980 Mon Sep 17 00:00:00 2001
From: Ondrej Holy <oholy@redhat.com>
Date: Tue, 17 Feb 2026 13:01:37 +0100
Subject: [PATCH] [channels,urbdrc] check interface indices before use
Backport of commit 7b7e6de8fe427a2f01d331056774aec69710590b (functional changes only).
Co-authored-by: Cursor <cursoragent@cursor.com>
---
.../urbdrc/client/libusb/libusb_udevice.c | 25 +++++++++++++++++++
channels/urbdrc/common/msusb.c | 2 ++
2 files changed, 27 insertions(+)
diff --git a/channels/urbdrc/client/libusb/libusb_udevice.c b/channels/urbdrc/client/libusb/libusb_udevice.c
index ef87f195f..b2d80243b 100644
--- a/channels/urbdrc/client/libusb/libusb_udevice.c
+++ b/channels/urbdrc/client/libusb/libusb_udevice.c
@@ -597,11 +597,36 @@ libusb_udev_complete_msconfig_setup(IUDEVICE* idev, MSUSB_CONFIG_DESCRIPTOR* MsC
"Select Configuration: Libusb NumberInterfaces(%" PRIu8 ") is different "
"with MsConfig NumberInterfaces(%" PRIu32 ")",
LibusbConfig->bNumInterfaces, MsConfig->NumInterfaces);
+ return NULL;
}
/* replace MsPipes for libusb */
MsInterfaces = MsConfig->MsInterfaces;
+ /* check interface indices before use */
+ for (inum = 0; inum < MsConfig->NumInterfaces; inum++)
+ {
+ MsInterface = MsInterfaces[inum];
+ if (MsInterface->InterfaceNumber >= MsConfig->NumInterfaces)
+ {
+ WLog_Print(urbdrc->log, WLOG_ERROR,
+ "MSUSB_CONFIG_DESCRIPTOR::NumInterfaces (%" PRIu32
+ " <= MSUSB_INTERFACE_DESCRIPTOR::InterfaceNumber( %" PRIu8 ")",
+ MsConfig->NumInterfaces, MsInterface->InterfaceNumber);
+ return NULL;
+ }
+
+ LibusbInterface = &LibusbConfig->interface[MsInterface->InterfaceNumber];
+ if (MsInterface->AlternateSetting >= LibusbInterface->num_altsetting)
+ {
+ WLog_Print(urbdrc->log, WLOG_ERROR,
+ "LIBUSB_INTERFACE::num_altsetting (%" PRId32
+ " <= MSUSB_INTERFACE_DESCRIPTOR::AlternateSetting( %" PRIu8 ")",
+ LibusbInterface->num_altsetting, MsInterface->AlternateSetting);
+ return NULL;
+ }
+ }
+
for (inum = 0; inum < MsConfig->NumInterfaces; inum++)
{
MsInterface = MsInterfaces[inum];
diff --git a/channels/urbdrc/common/msusb.c b/channels/urbdrc/common/msusb.c
index bb517ce5d..113b7c96b 100644
--- a/channels/urbdrc/common/msusb.c
+++ b/channels/urbdrc/common/msusb.c
@@ -139,6 +139,8 @@ BOOL msusb_msinterface_replace(MSUSB_CONFIG_DESCRIPTOR* MsConfig, BYTE Interface
{
if (!MsConfig || !MsConfig->MsInterfaces)
return FALSE;
+ if (MsConfig->NumInterfaces <= InterfaceNumber)
+ return FALSE;
msusb_msinterface_free(MsConfig->MsInterfaces[InterfaceNumber]);
MsConfig->MsInterfaces[InterfaceNumber] = NewMsInterface;
--
2.52.0

View File

@ -1,31 +0,0 @@
From aaad4718d80b0354fd807d680b12390075fe4fa0 Mon Sep 17 00:00:00 2001
From: Ondrej Holy <oholy@redhat.com>
Date: Wed, 11 Mar 2026 12:27:47 +0100
Subject: [PATCH] [channels,urbdrc] do not free MsConfig on failure
Backport of commit d676518809c319eec15911c705c13536036af2ae.
Made-with: Cursor
---
channels/urbdrc/client/data_transfer.c | 4 +---
1 file changed, 1 insertion(+), 3 deletions(-)
diff --git a/channels/urbdrc/client/data_transfer.c b/channels/urbdrc/client/data_transfer.c
index ed6ccf32d..c547d52d7 100644
--- a/channels/urbdrc/client/data_transfer.c
+++ b/channels/urbdrc/client/data_transfer.c
@@ -570,10 +570,8 @@ static UINT urb_select_interface(IUDEVICE* pdev, URBDRC_CHANNEL_CALLBACK* callba
MsConfig = pdev->get_MsConfig(pdev);
InterfaceNumber = MsInterface->InterfaceNumber;
if (!msusb_msinterface_replace(MsConfig, InterfaceNumber, MsInterface))
- {
- msusb_msconfig_free(MsConfig);
return ERROR_BAD_CONFIGURATION;
- }
+
/* complete configuration setup */
if (!pdev->complete_msconfig_setup(pdev, MsConfig))
{
--
2.53.0

View File

@ -1,45 +0,0 @@
From ea49391b3463b7df18eb9c358cdec04edf9c4001 Mon Sep 17 00:00:00 2001
From: Ondrej Holy <oholy@redhat.com>
Date: Thu, 12 Mar 2026 11:03:20 +0100
Subject: [PATCH] [channels,urbdrc] ensure InterfaceNumber is within range
Backport of commit 2d563a50be17c1b407ca448b1321378c0726dd31.
Made-with: Cursor
---
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 b2d80243b..ae8bbb4c5 100644
--- a/channels/urbdrc/client/libusb/libusb_udevice.c
+++ b/channels/urbdrc/client/libusb/libusb_udevice.c
@@ -528,19 +528,19 @@ static int libusb_udev_select_interface(IUDEVICE* idev, BYTE InterfaceNumber, BY
{
int error = 0, diff = 0;
UDEVICE* pdev = (UDEVICE*)idev;
- URBDRC_PLUGIN* urbdrc;
- MSUSB_CONFIG_DESCRIPTOR* MsConfig;
- MSUSB_INTERFACE_DESCRIPTOR** MsInterfaces;
if (!pdev || !pdev->urbdrc)
return -1;
- urbdrc = pdev->urbdrc;
- MsConfig = pdev->MsConfig;
+ URBDRC_PLUGIN* urbdrc = pdev->urbdrc;
+ MSUSB_CONFIG_DESCRIPTOR* MsConfig = pdev->MsConfig;
if (MsConfig)
{
- MsInterfaces = MsConfig->MsInterfaces;
+ if (InterfaceNumber >= MsConfig->NumInterfaces)
+ return -2;
+
+ MSUSB_INTERFACE_DESCRIPTOR** MsInterfaces = MsConfig->MsInterfaces;
if (MsInterfaces)
{
WLog_Print(urbdrc->log, WLOG_INFO,
--
2.53.0

View File

@ -1,37 +0,0 @@
From 970ad2311e69589004d6a84961aa9d1e3b33c085 Mon Sep 17 00:00:00 2001
From: Ondrej Holy <oholy@redhat.com>
Date: Thu, 26 Mar 2026 15:57:34 +0100
Subject: [PATCH] [channels,video] fix wrong cast
Backport of commit e01cd85c8003a245ef9778f0eda4b9235514c201.
Made-with: Cursor
---
channels/video/client/video_main.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/channels/video/client/video_main.c b/channels/video/client/video_main.c
index 641231b1d..0af78140e 100644
--- a/channels/video/client/video_main.c
+++ b/channels/video/client/video_main.c
@@ -959,7 +959,7 @@ static UINT video_control_on_close(IWTSVirtualChannelCallback* pChannelCallback)
{
if (pChannelCallback)
{
- VIDEO_LISTENER_CALLBACK* listener_callback = (VIDEO_LISTENER_CALLBACK*)pChannelCallback;
+ VIDEO_CHANNEL_CALLBACK* listener_callback = (VIDEO_CHANNEL_CALLBACK*)pChannelCallback;
VIDEO_PLUGIN* video = (VIDEO_PLUGIN*)listener_callback->plugin;
if (video && video->control_callback)
{
@@ -974,7 +974,7 @@ static UINT video_data_on_close(IWTSVirtualChannelCallback* pChannelCallback)
{
if (pChannelCallback)
{
- VIDEO_LISTENER_CALLBACK* listener_callback = (VIDEO_LISTENER_CALLBACK*)pChannelCallback;
+ VIDEO_CHANNEL_CALLBACK* listener_callback = (VIDEO_CHANNEL_CALLBACK*)pChannelCallback;
VIDEO_PLUGIN* video = (VIDEO_PLUGIN*)listener_callback->plugin;
if (video && video->data_callback)
{
--
2.53.0

View File

@ -1,121 +0,0 @@
From 2cfdc16faed93a8d5d1ec391788547409df986de Mon Sep 17 00:00:00 2001
From: Ondrej Holy <oholy@redhat.com>
Date: Thu, 26 Mar 2026 15:57:28 +0100
Subject: [PATCH] [channels,video] unify error handling
Partial backport of commit 635ae3c8193256db01774fab5ff11bcae57aed6b.
Made-with: Cursor
---
channels/video/client/video_main.c | 45 ++++++++++++++++++++++--------
1 file changed, 34 insertions(+), 11 deletions(-)
diff --git a/channels/video/client/video_main.c b/channels/video/client/video_main.c
index a8031fc86..641231b1d 100644
--- a/channels/video/client/video_main.c
+++ b/channels/video/client/video_main.c
@@ -25,7 +25,9 @@
#include <stdlib.h>
#include <string.h>
+#include <winpr/assert.h>
#include <winpr/crt.h>
+#include <winpr/error.h>
#include <winpr/synch.h>
#include <winpr/print.h>
#include <winpr/stream.h>
@@ -351,14 +353,24 @@ static void VideoClientContextPriv_free(VideoClientContextPriv* priv)
free(priv);
}
+static UINT video_channel_write(VIDEO_PLUGIN* video, const BYTE* data, UINT32 length)
+{
+ WINPR_ASSERT(video);
+
+ if (!video->control_callback || !video->control_callback->channel_callback)
+ return ERROR_BAD_CONFIGURATION;
+ IWTSVirtualChannel* channel = video->control_callback->channel_callback->channel;
+ if (!channel || !channel->Write)
+ return ERROR_BAD_CONFIGURATION;
+ return channel->Write(channel, length, data, NULL);
+}
+
static UINT video_control_send_presentation_response(VideoClientContext* context,
TSMM_PRESENTATION_RESPONSE* resp)
{
BYTE buf[12];
wStream* s;
VIDEO_PLUGIN* video = (VIDEO_PLUGIN*)context->handle;
- IWTSVirtualChannel* channel;
- UINT ret;
s = Stream_New(buf, 12);
if (!s)
@@ -370,11 +382,9 @@ static UINT video_control_send_presentation_response(VideoClientContext* context
Stream_Zero(s, 3);
Stream_SealLength(s);
- channel = video->control_callback->channel_callback->channel;
- ret = channel->Write(channel, 12, buf, NULL);
Stream_Free(s, FALSE);
- return ret;
+ return video_channel_write(video, buf, sizeof(buf));
}
static BOOL video_onMappedGeometryUpdate(MAPPED_GEOMETRY* geometry)
@@ -592,8 +602,6 @@ static UINT video_control_send_client_notification(VideoClientContext* context,
BYTE buf[100];
wStream* s;
VIDEO_PLUGIN* video = (VIDEO_PLUGIN*)context->handle;
- IWTSVirtualChannel* channel;
- UINT ret;
UINT32 cbSize;
s = Stream_New(buf, 30);
@@ -627,10 +635,7 @@ static UINT video_control_send_client_notification(VideoClientContext* context,
Stream_Write_UINT32(s, cbSize);
Stream_Free(s, FALSE);
- channel = video->control_callback->channel_callback->channel;
- ret = channel->Write(channel, cbSize, buf, NULL);
-
- return ret;
+ return video_channel_write(video, buf, cbSize);
}
static void video_timer(VideoClientContext* video, UINT64 now)
@@ -952,12 +957,30 @@ static UINT video_data_on_data_received(IWTSVirtualChannelCallback* pChannelCall
*/
static UINT video_control_on_close(IWTSVirtualChannelCallback* pChannelCallback)
{
+ if (pChannelCallback)
+ {
+ VIDEO_LISTENER_CALLBACK* listener_callback = (VIDEO_LISTENER_CALLBACK*)pChannelCallback;
+ VIDEO_PLUGIN* video = (VIDEO_PLUGIN*)listener_callback->plugin;
+ if (video && video->control_callback)
+ {
+ video->control_callback->channel_callback = NULL;
+ }
+ }
free(pChannelCallback);
return CHANNEL_RC_OK;
}
static UINT video_data_on_close(IWTSVirtualChannelCallback* pChannelCallback)
{
+ if (pChannelCallback)
+ {
+ VIDEO_LISTENER_CALLBACK* listener_callback = (VIDEO_LISTENER_CALLBACK*)pChannelCallback;
+ VIDEO_PLUGIN* video = (VIDEO_PLUGIN*)listener_callback->plugin;
+ if (video && video->data_callback)
+ {
+ video->data_callback->channel_callback = NULL;
+ }
+ }
free(pChannelCallback);
return CHANNEL_RC_OK;
}
--
2.53.0

View File

@ -1,65 +0,0 @@
From 20a3489c23763f94d31dcf8fd7ed880e07cbb2e8 Mon Sep 17 00:00:00 2001
From: Armin Novak <armin.novak@thincast.com>
Date: Thu, 2 Jul 2026 10:21:54 +0200
Subject: [PATCH] [client,common] deactivate cli parsing in RDP files
This feature was uncodumented and might have unintended side effects.
Deactivate it by default and (for the time being) keep an option to
enable it at compile time.
---
client/common/CMakeLists.txt | 5 +++++
client/common/file.c | 4 ++++
2 files changed, 9 insertions(+)
diff --git a/client/common/CMakeLists.txt b/client/common/CMakeLists.txt
index b465a6311..7bc7cf083 100644
--- a/client/common/CMakeLists.txt
+++ b/client/common/CMakeLists.txt
@@ -85,6 +85,11 @@ if (WITH_DEBUG_SYMBOLS AND MSVC AND BUILD_SHARED_LIBS)
install(FILES ${CMAKE_PDB_BINARY_DIR}/${OUTPUT_FILENAME}.pdb DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT symbols)
endif()
+option(WITH_EMBEDDED_CLI_IN_RDP_FILES "[dangrous] allow embedded cli arguments in rdp files" OFF)
+if(WITH_EMBEDDED_CLI_IN_RDP_FILES)
+ add_compile_definitions(WITH_EMBEDDED_CLI_IN_RDP_FILES)
+endif()
+
set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "Client/Common")
if(BUILD_TESTING)
diff --git a/client/common/file.c b/client/common/file.c
index b56fe6f06..bff94bad0 100644
--- a/client/common/file.c
+++ b/client/common/file.c
@@ -26,6 +26,7 @@
#include <winpr/file.h>
+
#include <freerdp/client/file.h>
#include <freerdp/client/cmdline.h>
@@ -661,6 +662,7 @@ static BOOL trim_strings(rdpFile* file)
return FALSE;
if (!trim(&file->KdcProxyName))
return FALSE;
+
return TRUE;
}
@@ -721,6 +723,7 @@ BOOL freerdp_client_parse_rdp_file_buffer_ex(rdpFile* file, const BYTE* buffer,
if (freerdp_client_parse_rdp_file_add_line(file, line, index) == -1)
goto fail;
+#if defined(WITH_EMBEDDED_CLI_IN_RDP_FILES)
if (beg[0] == '/')
{
if (!freerdp_client_parse_rdp_file_option(file, line, index))
@@ -728,6 +731,7 @@ BOOL freerdp_client_parse_rdp_file_buffer_ex(rdpFile* file, const BYTE* buffer,
goto next_line; /* FreeRDP option */
}
+#endif
d1 = strchr(line, ':');

View File

@ -1,170 +0,0 @@
From a25a6b8c53602d2023dd0ad685000dc006179e94 Mon Sep 17 00:00:00 2001
From: Ondrej Holy <oholy@redhat.com>
Date: Mon, 27 Apr 2026 20:12:42 +0000
Subject: [PATCH] [client,x11] fix deadlock on output expose
Defer screen update from xf_event_Expose to after X11 lock is released,
preventing a deadlock between X11 lock and railWindows lock.
Backport of commit a278ff74117444c635c50ffa5084ecf517171f5a.
Adjusted hunk offsets for 2.11.7.
Made-with: Cursor
---
client/X11/xf_client.c | 3 ++
client/X11/xf_event.c | 77 ++++++++++++++++++++++++++++--------------
client/X11/xf_event.h | 2 ++
client/X11/xfreerdp.h | 4 +++
4 files changed, 60 insertions(+), 26 deletions(-)
diff --git a/client/X11/xf_client.c b/client/X11/xf_client.c
index bd3eb0d..f14cb55 100644
--- a/client/X11/xf_client.c
+++ b/client/X11/xf_client.c
@@ -516,6 +516,9 @@ static BOOL xf_process_x_events(freerdp* instance)
xf_unlock_x11(xfc);
if (!status)
break;
+ status = xf_event_update_screen(instance);
+ if (!status)
+ break;
}
return status;
diff --git a/client/X11/xf_event.c b/client/X11/xf_event.c
index e1421ad..524e5e3 100644
--- a/client/X11/xf_event.c
+++ b/client/X11/xf_event.c
@@ -324,45 +324,34 @@ void xf_event_adjust_coordinates(xfContext* xfc, int* x, int* y)
}
static BOOL xf_event_Expose(xfContext* xfc, const XExposeEvent* event, BOOL app)
{
- int x, y;
- int w, h;
+ WINPR_ASSERT(xfc);
+ WINPR_ASSERT(event);
+
rdpSettings* settings = xfc->context.settings;
+ WINPR_ASSERT(settings);
if (!app && (settings->SmartSizing || settings->MultiTouchGestures))
{
- x = 0;
- y = 0;
- w = settings->DesktopWidth;
- h = settings->DesktopHeight;
+ xfc->exposedArea.x = 0;
+ xfc->exposedArea.y = 0;
+ xfc->exposedArea.w = settings->DesktopWidth;
+ xfc->exposedArea.h = settings->DesktopHeight;
}
else
{
- x = event->x;
- y = event->y;
- w = event->width;
- h = event->height;
+ xfc->exposedArea.x = event->x;
+ xfc->exposedArea.y = event->y;
+ xfc->exposedArea.w = event->width;
+ xfc->exposedArea.h = event->height;
}
- if (!app)
- {
- if (xfc->context.gdi->gfx)
- {
- xf_OutputExpose(xfc, x, y, w, h);
- return TRUE;
- }
- xf_draw_screen(xfc, x, y, w, h);
- }
- else
- {
- xfAppWindow* appWindow = xf_AppWindowFromX11Window(xfc, event->window);
- if (appWindow)
- xf_UpdateWindowArea(xfc, appWindow, x, y, w, h);
- xf_rail_return_window(appWindow, FALSE);
- }
+ xfc->exposedWindow = event->window;
+ xfc->exposeRequested = TRUE;
return TRUE;
}
+
static BOOL xf_event_VisibilityNotify(xfContext* xfc, const XVisibilityEvent* event, BOOL app)
{
WINPR_UNUSED(app);
@@ -1180,3 +1169,39 @@ BOOL xf_event_process(freerdp* instance, const XEvent* event)
XSync(xfc->display, FALSE);
return status;
}
+
+BOOL xf_event_update_screen(freerdp* instance)
+{
+ WINPR_ASSERT(instance);
+
+ xfContext* xfc = (xfContext*)instance->context;
+ WINPR_ASSERT(xfc);
+
+ rdpSettings* settings = xfc->context.settings;
+ WINPR_ASSERT(settings);
+
+ if (!xfc->exposeRequested)
+ return TRUE;
+ xfc->exposeRequested = FALSE;
+
+ if (!xfc->remote_app)
+ {
+ if (xfc->context.gdi->gfx)
+ {
+ xf_OutputExpose(xfc, xfc->exposedArea.x, xfc->exposedArea.y,
+ xfc->exposedArea.w, xfc->exposedArea.h);
+ return TRUE;
+ }
+ xf_draw_screen(xfc, xfc->exposedArea.x, xfc->exposedArea.y, xfc->exposedArea.w,
+ xfc->exposedArea.h);
+ }
+ else
+ {
+ xfAppWindow* appWindow = xf_AppWindowFromX11Window(xfc, xfc->exposedWindow);
+ if (appWindow)
+ xf_UpdateWindowArea(xfc, appWindow, xfc->exposedArea.x, xfc->exposedArea.y,
+ xfc->exposedArea.w, xfc->exposedArea.h);
+ xf_rail_return_window(appWindow, FALSE);
+ }
+ return TRUE;
+}
diff --git a/client/X11/xf_event.h b/client/X11/xf_event.h
index 2269d3e..e025d19 100644
--- a/client/X11/xf_event.h
+++ b/client/X11/xf_event.h
@@ -29,6 +29,8 @@ BOOL xf_event_action_script_init(xfContext* xfc);
void xf_event_action_script_free(xfContext* xfc);
BOOL xf_event_process(freerdp* instance, const XEvent* event);
+
+BOOL xf_event_update_screen(freerdp* instance);
void xf_event_SendClientEvent(xfContext* xfc, xfWindow* window, Atom atom, unsigned int numArgs,
...);
diff --git a/client/X11/xfreerdp.h b/client/X11/xfreerdp.h
index 636e60a..a1a33b2 100644
--- a/client/X11/xfreerdp.h
+++ b/client/X11/xfreerdp.h
@@ -261,6 +261,10 @@ struct xf_context
wHashTable* railWindows;
xfRailIconCache* railIconCache;
+ BOOL exposeRequested;
+ GDI_RGN exposedArea;
+ Window exposedWindow;
+
BOOL xkbAvailable;
BOOL xrenderAvailable;
--
2.53.0

View File

@ -1,53 +0,0 @@
From c0e85d92e45dff6c9a12e6af5be2e9ed10b4990c Mon Sep 17 00:00:00 2001
From: Ondrej Holy <oholy@redhat.com>
Date: Thu, 22 Jan 2026 12:51:08 +0100
Subject: [PATCH] [client,x11] fix double free in case of invalid pointer
Backport of commit 0421b53fcb4a80c95f51342e4a2c40c68a4101d3.
Co-Authored-By: Claude <noreply@anthropic.com>
---
client/X11/xf_graphics.c | 13 ++++++-------
1 file changed, 6 insertions(+), 7 deletions(-)
diff --git a/client/X11/xf_graphics.c b/client/X11/xf_graphics.c
index 5aa1fd48b..d596b23da 100644
--- a/client/X11/xf_graphics.c
+++ b/client/X11/xf_graphics.c
@@ -406,7 +406,6 @@ static BOOL xf_Pointer_New(rdpContext* context, rdpPointer* pointer)
BOOL rc = FALSE;
#ifdef WITH_XCURSOR
UINT32 CursorFormat;
- size_t size;
xfContext* xfc = (xfContext*)context;
xfPointer* xpointer = (xfPointer*)pointer;
@@ -421,19 +420,19 @@ static BOOL xf_Pointer_New(rdpContext* context, rdpPointer* pointer)
xpointer->nCursors = 0;
xpointer->mCursors = 0;
- size = 1ull * pointer->height * pointer->width * GetBytesPerPixel(CursorFormat);
+ const size_t size =
+ 1ull * pointer->height * pointer->width * GetBytesPerPixel(CursorFormat);
- if (!(xpointer->cursorPixels = (XcursorPixel*)_aligned_malloc(size, 16)))
+ xpointer->cursorPixels = (XcursorPixel*)_aligned_malloc(size, 16);
+ if (!xpointer->cursorPixels)
goto fail;
if (!freerdp_image_copy_from_pointer_data(
(BYTE*)xpointer->cursorPixels, CursorFormat, 0, 0, 0, pointer->width, pointer->height,
pointer->xorMaskData, pointer->lengthXorMask, pointer->andMaskData,
pointer->lengthAndMask, pointer->xorBpp, &context->gdi->palette))
- {
- _aligned_free(xpointer->cursorPixels);
- return FALSE;
- }
+ goto fail;
+
rc = TRUE;
#endif
--
2.52.0

View File

@ -1,45 +0,0 @@
From 562cc4b33257074d1d50535138998bc81796d447 Mon Sep 17 00:00:00 2001
From: Ondrej Holy <oholy@redhat.com>
Date: Mon, 27 Apr 2026 19:00:00 +0000
Subject: [PATCH] [client,x11] fix xf_rail_window_common cleanup
Backport of commit b4f0f0a18fe53aa8d47d062f91471f4e9c5e0d51.
Adjusted hunk offsets for 2.11.7.
Made-with: Cursor
---
client/X11/xf_rail.c | 6 +-----
1 file changed, 1 insertion(+), 5 deletions(-)
diff --git a/client/X11/xf_rail.c b/client/X11/xf_rail.c
index 242c54b..4c4f47d 100644
--- a/client/X11/xf_rail.c
+++ b/client/X11/xf_rail.c
@@ -260,11 +260,10 @@ static void window_state_log_style(wLog* log, const WINDOW_STATE_ORDER* windowSt
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)
{
@@ -310,10 +309,7 @@ static BOOL xf_rail_window_common(rdpContext* context, const WINDOW_ORDER_INFO*
}
if (!appWindow->title)
- {
- free(appWindow);
return FALSE;
- }
xf_AppWindowInit(xfc, appWindow);
}
--
2.53.0

View File

@ -1,331 +0,0 @@
From e6edabe690ad8de63af327403e8371b6ef1319e2 Mon Sep 17 00:00:00 2001
From: Ondrej Holy <oholy@redhat.com>
Date: Mon, 27 Apr 2026 19:38:47 +0000
Subject: [PATCH] [client,x11] improve rails window locking
Backport of commit 78fd7f580d5f9e6d9d582d82e5ea96003844fbdf.
Adapted for 2.11.7: C89 declaration style, simplified
`xfAppWindowsLockFrom`/`UnlockFrom` signatures (no
`WINPR_ATTR_UNUSED`), `XSetTransientForHint` inline error handling.
Made-with: Cursor
---
client/X11/xf_event.c | 7 ++----
client/X11/xf_graphics.c | 4 +--
client/X11/xf_rail.c | 53 +++++++++++++++++++--------------------
client/X11/xf_rail.h | 10 ++++++--
client/X11/xf_window.c | 54 ++++++++++++++++++++++++++++++++--------
client/X11/xf_window.h | 11 ++++++--
6 files changed, 89 insertions(+), 50 deletions(-)
diff --git a/client/X11/xf_event.c b/client/X11/xf_event.c
index 8bebcd4..8801fbd 100644
--- a/client/X11/xf_event.c
+++ b/client/X11/xf_event.c
@@ -354,13 +354,10 @@ static BOOL xf_event_Expose(xfContext* xfc, const XExposeEvent* event, BOOL app)
}
else
{
- xfAppWindow* appWindow;
- appWindow = xf_AppWindowFromX11Window(xfc, event->window);
-
+ xfAppWindow* appWindow = xf_AppWindowFromX11Window(xfc, event->window);
if (appWindow)
- {
xf_UpdateWindowArea(xfc, appWindow, x, y, w, h);
- }
+ xf_rail_return_window(appWindow);
}
return TRUE;
diff --git a/client/X11/xf_graphics.c b/client/X11/xf_graphics.c
index 70979bd..f5b4f3d 100644
--- a/client/X11/xf_graphics.c
+++ b/client/X11/xf_graphics.c
@@ -372,12 +372,12 @@ static Window xf_Pointer_get_window(xfContext* xfc)
if (xfc->remote_app)
{
Window w = 0;
- EnterCriticalSection(&xfc->railWindows->lock);
+ xf_AppWindowsLock(xfc);
if (!xfc->appWindow)
WLog_WARN(TAG, "xf_Pointer: Invalid appWindow");
else
w = xfc->appWindow->handle;
- LeaveCriticalSection(&xfc->railWindows->lock);
+ xf_AppWindowsUnlock(xfc);
return w;
}
else
diff --git a/client/X11/xf_rail.c b/client/X11/xf_rail.c
index 1938bdd..1e57046 100644
--- a/client/X11/xf_rail.c
+++ b/client/X11/xf_rail.c
@@ -101,9 +101,10 @@ void xf_rail_send_activate(xfContext* xfc, Window xwindow, BOOL enabled)
xf_SetWindowStyle(xfc, appWindow, 0, 0);
activate.windowId = appWindow->windowId;
+ xf_rail_return_window(appWindow);
+
activate.enabled = enabled;
xfc->rail->ClientActivate(xfc->rail, &activate);
- xf_rail_return_window(appWindow);
}
void xf_rail_send_client_system_command(xfContext* xfc, UINT32 windowId, UINT16 command)
@@ -260,6 +261,7 @@ void xf_rail_paint(xfContext* xfc, INT32 uleft, INT32 utop, UINT32 uright, UINT3
static BOOL xf_rail_window_common(rdpContext* context, const WINDOW_ORDER_INFO* orderInfo,
const WINDOW_STATE_ORDER* windowState)
{
+ BOOL rc = FALSE;
xfContext* xfc = (xfContext*)context;
UINT32 fieldFlags = orderInfo->fieldFlags;
BOOL position_or_size_updated = FALSE;
@@ -379,14 +381,14 @@ static BOOL xf_rail_window_common(rdpContext* context, const WINDOW_ORDER_INFO*
if (!(title = _strdup("")))
{
WLog_ERR(TAG, "failed to duplicate empty window title string");
- return FALSE;
+ goto fail;
}
}
else if (ConvertFromUnicode(CP_UTF8, 0, (WCHAR*)windowState->titleInfo.string,
windowState->titleInfo.length / 2, &title, 0, NULL, NULL) < 1)
{
WLog_ERR(TAG, "failed to convert window title");
- return FALSE;
+ goto fail;
}
free(appWindow->title);
@@ -427,7 +429,7 @@ static BOOL xf_rail_window_common(rdpContext* context, const WINDOW_ORDER_INFO*
(RECTANGLE_16*)calloc(appWindow->numWindowRects, sizeof(RECTANGLE_16));
if (!appWindow->windowRects)
- return FALSE;
+ goto fail;
CopyMemory(appWindow->windowRects, windowState->windowRects,
appWindow->numWindowRects * sizeof(RECTANGLE_16));
@@ -456,7 +458,7 @@ static BOOL xf_rail_window_common(rdpContext* context, const WINDOW_ORDER_INFO*
(RECTANGLE_16*)calloc(appWindow->numVisibilityRects, sizeof(RECTANGLE_16));
if (!appWindow->visibilityRects)
- return FALSE;
+ goto fail;
CopyMemory(appWindow->visibilityRects, windowState->visibilityRects,
appWindow->numVisibilityRects * sizeof(RECTANGLE_16));
@@ -522,7 +524,10 @@ static BOOL xf_rail_window_common(rdpContext* context, const WINDOW_ORDER_INFO*
{
xf_SetWindowRects(xfc, appWindow, appWindow->windowRects, appWindow->numWindowRects);
}*/
- return TRUE;
+ rc = TRUE;
+fail:
+ xf_rail_return_window(appWindow);
+ return rc;
}
static BOOL xf_rail_window_delete(rdpContext* context, const WINDOW_ORDER_INFO* orderInfo)
@@ -1000,14 +1005,14 @@ static UINT xf_rail_server_local_move_size(RailClientContext* context,
x = localMoveSize->posX;
y = localMoveSize->posY;
/* FIXME: local keyboard moves not working */
- return CHANNEL_RC_OK;
+ break;
case RAIL_WMSZ_KEYSIZE:
direction = _NET_WM_MOVERESIZE_SIZE_KEYBOARD;
x = localMoveSize->posX;
y = localMoveSize->posY;
/* FIXME: local keyboard moves not working */
- return CHANNEL_RC_OK;
+ break;
}
if (localMoveSize->isMoveSizeStart)
@@ -1173,9 +1178,17 @@ xfAppWindow* xf_rail_add_window(xfContext* xfc, UINT64 id, UINT32 x, UINT32 y, U
appWindow->y = y;
appWindow->width = width;
appWindow->height = height;
- xf_AppWindowCreate(xfc, appWindow);
- HashTable_Add(xfc->railWindows, &appWindow->windowId, (void*)appWindow);
+ xf_AppWindowsLock(xfc);
+ if (xf_AppWindowCreate(xfc, appWindow) < 0)
+ goto fail;
+
+ if (HashTable_Add(xfc->railWindows, &appWindow->windowId, (void*)appWindow) < 0)
+ goto fail;
return appWindow;
+fail:
+ free(appWindow);
+ xf_AppWindowsUnlock(xfc);
+ return NULL;
}
BOOL xf_rail_del_window(xfContext* xfc, UINT64 id)
@@ -1189,26 +1202,10 @@ BOOL xf_rail_del_window(xfContext* xfc, UINT64 id)
return HashTable_Remove(xfc->railWindows, &id);
}
-xfAppWindow* xf_rail_get_window(xfContext* xfc, UINT64 id)
-{
- if (!xfc)
- return NULL;
-
- if (!xfc->railWindows)
- return NULL;
-
- EnterCriticalSection(&xfc->railWindows->lock);
- xfAppWindow* window = HashTable_GetItemValue(xfc->railWindows, &id);
- if (!window)
- LeaveCriticalSection(&xfc->railWindows->lock);
-
- return window;
-}
-
-void xf_rail_return_window(xfAppWindow* window)
+void xf_rail_return_windowFrom(xfAppWindow* window, const char* file, const char* fkt, size_t line)
{
if (!window)
return;
- LeaveCriticalSection(&window->xfc->railWindows->lock);
+ xfAppWindowsUnlockFrom(window->xfc, file, fkt, line);
}
diff --git a/client/X11/xf_rail.h b/client/X11/xf_rail.h
index 5fbc316..83d5fff 100644
--- a/client/X11/xf_rail.h
+++ b/client/X11/xf_rail.h
@@ -36,9 +36,15 @@ void xf_rail_disable_remoteapp_mode(xfContext* xfc);
xfAppWindow* xf_rail_add_window(xfContext* xfc, UINT64 id, UINT32 x, UINT32 y, UINT32 width,
UINT32 height, UINT32 surfaceId);
-void xf_rail_return_window(xfAppWindow* window);
+#define xf_rail_return_window(window) \
+ xf_rail_return_windowFrom((window), __FILE__, __func__, __LINE__)
+void xf_rail_return_windowFrom(xfAppWindow* window, const char* file, const char* fkt, size_t line);
-xfAppWindow* xf_rail_get_window(xfContext* xfc, UINT64 id);
+#define xf_rail_get_window(xfc, id) \
+ xf_rail_get_windowFrom((xfc), (id), __FILE__, __func__, __LINE__)
+
+xfAppWindow* xf_rail_get_windowFrom(xfContext* xfc, UINT64 id, const char* file, const char* fkt,
+ size_t line);
BOOL xf_rail_del_window(xfContext* xfc, UINT64 id);
diff --git a/client/X11/xf_window.c b/client/X11/xf_window.c
index 7e936fe..09b4a81 100644
--- a/client/X11/xf_window.c
+++ b/client/X11/xf_window.c
@@ -1116,39 +1116,71 @@ void xf_DestroyWindow(xfContext* xfc, xfAppWindow* appWindow)
free(appWindow);
}
-xfAppWindow* xf_AppWindowFromX11Window(xfContext* xfc, Window wnd)
+static xfAppWindow* get_windowUnlocked(xfContext* xfc, UINT64 id)
+{
+ WINPR_ASSERT(xfc);
+ return HashTable_GetItemValue(xfc->railWindows, &id);
+}
+
+xfAppWindow* xf_rail_get_windowFrom(xfContext* xfc, UINT64 id, const char* file, const char* fkt,
+ size_t line)
+{
+ if (!xfc)
+ return NULL;
+
+ if (!xfc->railWindows)
+ return NULL;
+
+ xfAppWindowsLockFrom(xfc, file, fkt, line);
+ xfAppWindow* window = get_windowUnlocked(xfc, id);
+ if (!window)
+ xfAppWindowsUnlockFrom(xfc, file, fkt, line);
+
+ return window;
+}
+
+xfAppWindow* xf_AppWindowFromX11WindowFrom(xfContext* xfc, Window wnd, const char* file,
+ const char* fkt, size_t line)
{
- int index;
- int count;
ULONG_PTR* pKeys = NULL;
- xfAppWindow* appWindow;
if (!xfc->railWindows)
return NULL;
- EnterCriticalSection(&xfc->railWindows->lock);
- count = HashTable_GetKeys(xfc->railWindows, &pKeys);
+ xfAppWindowsLockFrom(xfc, file, fkt, line);
+ size_t count = HashTable_GetKeys(xfc->railWindows, &pKeys);
- for (index = 0; index < count; index++)
+ for (size_t index = 0; index < count; index++)
{
- appWindow = HashTable_GetItemValue(xfc->railWindows, (void*)pKeys[index]);
+ xfAppWindow* appWindow = get_windowUnlocked(xfc, *(UINT64*)pKeys[index]);
if (!appWindow)
{
- LeaveCriticalSection(&xfc->railWindows->lock);
+ xfAppWindowsUnlockFrom(xfc, file, fkt, line);
free(pKeys);
return NULL;
}
if (appWindow->handle == wnd)
{
- LeaveCriticalSection(&xfc->railWindows->lock);
free(pKeys);
return appWindow;
}
}
- LeaveCriticalSection(&xfc->railWindows->lock);
+ xfAppWindowsUnlockFrom(xfc, file, fkt, line);
free(pKeys);
return NULL;
}
+
+void xfAppWindowsLockFrom(xfContext* xfc, const char* file, const char* fkt, size_t line)
+{
+ WINPR_ASSERT(xfc);
+ EnterCriticalSection(&xfc->railWindows->lock);
+}
+
+void xfAppWindowsUnlockFrom(xfContext* xfc, const char* file, const char* fkt, size_t line)
+{
+ WINPR_ASSERT(xfc);
+ LeaveCriticalSection(&xfc->railWindows->lock);
+}
diff --git a/client/X11/xf_window.h b/client/X11/xf_window.h
index c7702ed..45d40bb 100644
--- a/client/X11/xf_window.h
+++ b/client/X11/xf_window.h
@@ -187,8 +187,15 @@ 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);
+#define xf_AppWindowFromX11Window(xfc, wnd) \
+ xf_AppWindowFromX11WindowFrom((xfc), (wnd), __FILE__, __func__, __LINE__)
+xfAppWindow* xf_AppWindowFromX11WindowFrom(xfContext* xfc, Window wnd, const char* file,
+ const char* fkt, size_t line);
-xfAppWindow* xf_AppWindowFromX11Window(xfContext* xfc, Window wnd);
+#define xf_AppWindowsLock(xfc) xfAppWindowsLockFrom((xfc), __FILE__, __func__, __LINE__)
+void xfAppWindowsLockFrom(xfContext* xfc, const char* file, const char* fkt, size_t line);
+
+#define xf_AppWindowsUnlock(xfc) xfAppWindowsUnlockFrom((xfc), __FILE__, __func__, __LINE__)
+void xfAppWindowsUnlockFrom(xfContext* xfc, const char* file, const char* fkt, size_t line);
#endif /* FREERDP_CLIENT_X11_WINDOW_H */
--
2.53.0

View File

@ -1,404 +0,0 @@
From 8152c347e96c1e3af15cb2551d4efb6740fe9f87 Mon Sep 17 00:00:00 2001
From: Ondrej Holy <oholy@redhat.com>
Date: Mon, 27 Apr 2026 19:37:40 +0000
Subject: [PATCH] [client,x11] lock appWindow
Backport of commit 1994e9844212a6dfe0ff12309fef520e888986b5.
Adapted for 2.11.7: C89 declaration style, `(Atom)` cast for
`xfc->_NET_WM_STATE` comparison, `xf_rail_send_client_system_command`
return value handling adapted (no direct return), `HashTable_GetKeys`
variable pre-declared, `xf_AppWindowFromX11Window` loop adapted.
Made-with: Cursor
---
client/X11/xf_event.c | 46 ++++++++++++++++-----------
client/X11/xf_graphics.c | 10 +++---
client/X11/xf_rail.c | 68 +++++++++++++++++++++++++---------------
client/X11/xf_rail.h | 3 ++
client/X11/xf_window.c | 13 +++++++-
client/X11/xf_window.h | 2 ++
6 files changed, 94 insertions(+), 48 deletions(-)
diff --git a/client/X11/xf_event.c b/client/X11/xf_event.c
index eb50ef1..8bebcd4 100644
--- a/client/X11/xf_event.c
+++ b/client/X11/xf_event.c
@@ -388,7 +388,9 @@ BOOL xf_generic_MotionNotify(xfContext* xfc, int x, int y, int state, Window win
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 */
@@ -465,7 +467,9 @@ BOOL xf_generic_ButtonEvent(xfContext* xfc, int x, int y, int button, Window win
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 */
@@ -574,9 +578,8 @@ static BOOL xf_event_FocusIn(xfContext* xfc, const XFocusInEvent* event, BOOL ap
/* Update the server with any window changes that occurred while the window was not focused.
*/
if (appWindow)
- {
xf_rail_adjust_position(xfc, appWindow);
- }
+ xf_rail_return_window(appWindow);
}
xf_keyboard_focus_in(xfc);
@@ -623,15 +626,13 @@ static BOOL xf_event_ClientMessage(xfContext* xfc, const XClientMessageEvent* ev
{
if (app)
{
- xfAppWindow* appWindow;
- appWindow = xf_AppWindowFromX11Window(xfc, event->window);
+ BOOL rc = TRUE;
+ xfAppWindow* appWindow = xf_AppWindowFromX11Window(xfc, event->window);
if (appWindow)
- {
xf_rail_send_client_system_command(xfc, appWindow->windowId, SC_CLOSE);
- }
-
- return TRUE;
+ xf_rail_return_window(appWindow);
+ return rc;
}
else
{
@@ -665,6 +666,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;
@@ -684,6 +686,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;
}
@@ -774,6 +777,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);
}
@@ -798,6 +802,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;
@@ -813,15 +818,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->context.gdi, TRUE);
- else
+ return gdi_send_suppress_output(xfc->context.gdi, TRUE);
+
{
appWindow = xf_AppWindowFromX11Window(xfc, event->window);
if (appWindow)
- {
appWindow->is_mapped = FALSE;
- }
+ xf_rail_return_window(appWindow);
}
return TRUE;
@@ -829,6 +833,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);
@@ -856,7 +861,7 @@ static BOOL xf_event_PropertyNotify(xfContext* xfc, const XPropertyEvent* event,
appWindow = xf_AppWindowFromX11Window(xfc, event->window);
if (!appWindow)
- return TRUE;
+ goto fail;
}
if ((Atom)event->atom == xfc->_NET_WM_STATE)
@@ -949,10 +954,13 @@ static BOOL xf_event_PropertyNotify(xfContext* xfc, const XPropertyEvent* event,
}
}
else if (minimizedChanged)
- gdi_send_suppress_output(xfc->context.gdi, minimized);
+ rc = gdi_send_suppress_output(xfc->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)
@@ -1060,7 +1068,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))
+ BOOL suppress = xf_event_suppress_events(xfc, appWindow, event);
+ xf_rail_return_window(appWindow);
+ if (suppress)
return TRUE;
}
}
diff --git a/client/X11/xf_graphics.c b/client/X11/xf_graphics.c
index d596b23..70979bd 100644
--- a/client/X11/xf_graphics.c
+++ b/client/X11/xf_graphics.c
@@ -371,12 +371,14 @@ static Window xf_Pointer_get_window(xfContext* xfc)
}
if (xfc->remote_app)
{
+ Window w = 0;
+ EnterCriticalSection(&xfc->railWindows->lock);
if (!xfc->appWindow)
- {
WLog_WARN(TAG, "xf_Pointer: Invalid appWindow");
- return 0;
- }
- return xfc->appWindow->handle;
+ else
+ w = xfc->appWindow->handle;
+ LeaveCriticalSection(&xfc->railWindows->lock);
+ return w;
}
else
{
diff --git a/client/X11/xf_rail.c b/client/X11/xf_rail.c
index fdc2244..1938bdd 100644
--- a/client/X11/xf_rail.c
+++ b/client/X11/xf_rail.c
@@ -103,6 +103,7 @@ void xf_rail_send_activate(xfContext* xfc, Window xwindow, BOOL enabled)
activate.windowId = appWindow->windowId;
activate.enabled = enabled;
xfc->rail->ClientActivate(xfc->rail, &activate);
+ xf_rail_return_window(appWindow);
}
void xf_rail_send_client_system_command(xfContext* xfc, UINT32 windowId, UINT16 command)
@@ -658,61 +659,63 @@ 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;
- xfAppWindow* railWindow;
- xfRailIcon* icon;
- BOOL replaceIcon;
- railWindow = xf_rail_get_window(xfc, orderInfo->windowId);
+ BOOL replaceIcon = 0;
+ xfAppWindow* railWindow = xf_rail_get_window(xfc, orderInfo->windowId);
if (!railWindow)
return TRUE;
- icon = RailIconCache_Lookup(xfc->railIconCache, windowIcon->iconInfo->cacheId,
+ xfRailIcon* icon = RailIconCache_Lookup(xfc->railIconCache, windowIcon->iconInfo->cacheId,
windowIcon->iconInfo->cacheEntry);
if (!icon)
{
WLog_WARN(TAG, "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_WARN(TAG, "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;
- xfAppWindow* railWindow;
- xfRailIcon* icon;
- BOOL replaceIcon;
- railWindow = xf_rail_get_window(xfc, orderInfo->windowId);
+ BOOL replaceIcon = 0;
+ xfAppWindow* railWindow = xf_rail_get_window(xfc, orderInfo->windowId);
if (!railWindow)
return TRUE;
- icon = RailIconCache_Lookup(xfc->railIconCache, windowCachedIcon->cachedIcon.cacheId,
+ xfRailIcon* icon = RailIconCache_Lookup(xfc->railIconCache, windowCachedIcon->cachedIcon.cacheId,
windowCachedIcon->cachedIcon.cacheEntry);
if (!icon)
{
WLog_WARN(TAG, "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 xf_rail_notify_icon_common(rdpContext* context, const WINDOW_ORDER_INFO* orderInfo,
@@ -1012,6 +1015,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;
}
@@ -1033,6 +1037,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;
}
@@ -1190,7 +1195,20 @@ xfAppWindow* xf_rail_get_window(xfContext* xfc, UINT64 id)
return NULL;
if (!xfc->railWindows)
- return FALSE;
+ return NULL;
+
+ EnterCriticalSection(&xfc->railWindows->lock);
+ xfAppWindow* window = HashTable_GetItemValue(xfc->railWindows, &id);
+ if (!window)
+ LeaveCriticalSection(&xfc->railWindows->lock);
+
+ return window;
+}
+
+void xf_rail_return_window(xfAppWindow* window)
+{
+ if (!window)
+ return;
- return HashTable_GetItemValue(xfc->railWindows, &id);
+ LeaveCriticalSection(&window->xfc->railWindows->lock);
}
diff --git a/client/X11/xf_rail.h b/client/X11/xf_rail.h
index c99ed70..5fbc316 100644
--- a/client/X11/xf_rail.h
+++ b/client/X11/xf_rail.h
@@ -35,6 +35,9 @@ void xf_rail_disable_remoteapp_mode(xfContext* xfc);
xfAppWindow* xf_rail_add_window(xfContext* xfc, UINT64 id, UINT32 x, UINT32 y, UINT32 width,
UINT32 height, UINT32 surfaceId);
+
+void xf_rail_return_window(xfAppWindow* window);
+
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 9b5b1c4..7e936fe 100644
--- a/client/X11/xf_window.c
+++ b/client/X11/xf_window.c
@@ -1122,22 +1122,33 @@ xfAppWindow* xf_AppWindowFromX11Window(xfContext* xfc, Window wnd)
int count;
ULONG_PTR* pKeys = NULL;
xfAppWindow* appWindow;
+
+ if (!xfc->railWindows)
+ return NULL;
+
+ EnterCriticalSection(&xfc->railWindows->lock);
count = HashTable_GetKeys(xfc->railWindows, &pKeys);
for (index = 0; index < count; index++)
{
- appWindow = xf_rail_get_window(xfc, *(UINT64*)pKeys[index]);
+ appWindow = HashTable_GetItemValue(xfc->railWindows, (void*)pKeys[index]);
if (!appWindow)
+ {
+ LeaveCriticalSection(&xfc->railWindows->lock);
+ free(pKeys);
return NULL;
+ }
if (appWindow->handle == wnd)
{
+ LeaveCriticalSection(&xfc->railWindows->lock);
free(pKeys);
return appWindow;
}
}
+ LeaveCriticalSection(&xfc->railWindows->lock);
free(pKeys);
return NULL;
}
diff --git a/client/X11/xf_window.h b/client/X11/xf_window.h
index 0f85af1..c7702ed 100644
--- a/client/X11/xf_window.h
+++ b/client/X11/xf_window.h
@@ -187,6 +187,8 @@ 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);
+
xfAppWindow* xf_AppWindowFromX11Window(xfContext* xfc, Window wnd);
#endif /* FREERDP_CLIENT_X11_WINDOW_H */
--
2.53.0

View File

@ -1,360 +0,0 @@
From eeb38c3565473c46da5dea6b43f597064e89b717 Mon Sep 17 00:00:00 2001
From: Ondrej Holy <oholy@redhat.com>
Date: Mon, 27 Apr 2026 20:12:11 +0000
Subject: [PATCH] [client,x11] refactor locking
X11 and railWindows lock must be held at the same time to avoid
deadlocking.
Backport of commit 4ff57b68c2960fa414d03c78ff0e0660be1cc5bd.
Adapted for 2.11.7: C89 declaration style, direct X11 calls instead
of `LogDynAnd*` wrappers, `WINPR_ATTR_UNUSED` removed from
`xfAppWindowsLockFrom`/`UnlockFrom`.
Made-with: Cursor
---
client/X11/xf_event.c | 24 ++++++++++++------------
client/X11/xf_rail.c | 35 +++++++++++++++++++++--------------
client/X11/xf_rail.h | 14 ++++++++------
client/X11/xf_window.c | 16 +++++++++-------
4 files changed, 50 insertions(+), 39 deletions(-)
diff --git a/client/X11/xf_event.c b/client/X11/xf_event.c
index 8801fbd..e1421ad 100644
--- a/client/X11/xf_event.c
+++ b/client/X11/xf_event.c
@@ -357,7 +357,7 @@ static BOOL xf_event_Expose(xfContext* xfc, const XExposeEvent* event, BOOL app)
xfAppWindow* appWindow = xf_AppWindowFromX11Window(xfc, event->window);
if (appWindow)
xf_UpdateWindowArea(xfc, appWindow, x, y, w, h);
- xf_rail_return_window(appWindow);
+ xf_rail_return_window(appWindow, FALSE);
}
return TRUE;
@@ -386,7 +386,7 @@ BOOL xf_generic_MotionNotify(xfContext* xfc, int x, int y, int state, Window win
{
/* make sure window exists */
xfAppWindow* appWindow = xf_AppWindowFromX11Window(xfc, window);
- xf_rail_return_window(appWindow);
+ xf_rail_return_window(appWindow, FALSE);
if (!appWindow)
return TRUE;
@@ -465,7 +465,7 @@ BOOL xf_generic_ButtonEvent(xfContext* xfc, int x, int y, int button, Window win
{
/* make sure window exists */
xfAppWindow* appWindow = xf_AppWindowFromX11Window(xfc, window);
- xf_rail_return_window(appWindow);
+ xf_rail_return_window(appWindow, FALSE);
if (!appWindow)
return TRUE;
@@ -576,7 +576,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_rail_return_window(appWindow, FALSE);
}
xf_keyboard_focus_in(xfc);
@@ -628,7 +628,7 @@ static BOOL xf_event_ClientMessage(xfContext* xfc, const XClientMessageEvent* ev
if (appWindow)
xf_rail_send_client_system_command(xfc, appWindow->windowId, SC_CLOSE);
- xf_rail_return_window(appWindow);
+ xf_rail_return_window(appWindow, FALSE);
return rc;
}
else
@@ -663,7 +663,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);
+ xf_rail_return_window(appWindow, FALSE);
}
return TRUE;
@@ -683,7 +683,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);
+ xf_rail_return_window(appWindow, FALSE);
}
return TRUE;
}
@@ -774,7 +774,7 @@ static BOOL xf_event_ConfigureNotify(xfContext* xfc, const XConfigureEvent* even
xf_rail_adjust_position(xfc, appWindow);
}
}
- xf_rail_return_window(appWindow);
+ xf_rail_return_window(appWindow, FALSE);
}
return xf_pointer_update_scale(xfc);
}
@@ -799,7 +799,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);
+ xf_rail_return_window(appWindow, FALSE);
}
return TRUE;
@@ -822,7 +822,7 @@ static BOOL xf_event_UnmapNotify(xfContext* xfc, const XUnmapEvent* event, BOOL
if (appWindow)
appWindow->is_mapped = FALSE;
- xf_rail_return_window(appWindow);
+ xf_rail_return_window(appWindow, FALSE);
}
return TRUE;
@@ -954,7 +954,7 @@ static BOOL xf_event_PropertyNotify(xfContext* xfc, const XPropertyEvent* event,
rc = gdi_send_suppress_output(xfc->context.gdi, minimized);
fail:
- xf_rail_return_window(appWindow);
+ xf_rail_return_window(appWindow, FALSE);
}
return rc;
@@ -1066,7 +1066,7 @@ BOOL xf_event_process(freerdp* instance, const XEvent* event)
xfc->appWindow = appWindow;
BOOL suppress = xf_event_suppress_events(xfc, appWindow, event);
- xf_rail_return_window(appWindow);
+ xf_rail_return_window(appWindow, FALSE);
if (suppress)
return TRUE;
}
diff --git a/client/X11/xf_rail.c b/client/X11/xf_rail.c
index 1e57046..d42b864 100644
--- a/client/X11/xf_rail.c
+++ b/client/X11/xf_rail.c
@@ -101,7 +101,7 @@ void xf_rail_send_activate(xfContext* xfc, Window xwindow, BOOL enabled)
xf_SetWindowStyle(xfc, appWindow, 0, 0);
activate.windowId = appWindow->windowId;
- xf_rail_return_window(appWindow);
+ xf_rail_return_window(appWindow, FALSE);
activate.enabled = enabled;
xfc->rail->ClientActivate(xfc->rail, &activate);
@@ -213,7 +213,7 @@ static void xf_rail_invalidate_region(xfContext* xfc, REGION16* invalidRegion)
for (index = 0; index < count; index++)
{
- appWindow = xf_rail_get_window(xfc, *(UINT64*)pKeys[index]);
+ appWindow = xf_rail_get_window(xfc, *(UINT64*)pKeys[index], FALSE);
if (appWindow)
{
@@ -265,7 +265,7 @@ static BOOL xf_rail_window_common(rdpContext* context, const WINDOW_ORDER_INFO*
xfContext* xfc = (xfContext*)context;
UINT32 fieldFlags = orderInfo->fieldFlags;
BOOL position_or_size_updated = FALSE;
- xfAppWindow* appWindow = xf_rail_get_window(xfc, orderInfo->windowId);
+ xfAppWindow* appWindow = xf_rail_get_window(xfc, orderInfo->windowId, FALSE);
if (fieldFlags & WINDOW_ORDER_STATE_NEW)
{
@@ -526,7 +526,7 @@ static BOOL xf_rail_window_common(rdpContext* context, const WINDOW_ORDER_INFO*
}*/
rc = TRUE;
fail:
- xf_rail_return_window(appWindow);
+ xf_rail_return_window(appWindow, FALSE);
return rc;
}
@@ -667,7 +667,7 @@ static BOOL xf_rail_window_icon(rdpContext* context, const WINDOW_ORDER_INFO* or
BOOL rc = FALSE;
xfContext* xfc = (xfContext*)context;
BOOL replaceIcon = 0;
- xfAppWindow* railWindow = xf_rail_get_window(xfc, orderInfo->windowId);
+ xfAppWindow* railWindow = xf_rail_get_window(xfc, orderInfo->windowId, FALSE);
if (!railWindow)
return TRUE;
@@ -690,7 +690,7 @@ static BOOL xf_rail_window_icon(rdpContext* context, const WINDOW_ORDER_INFO* or
xf_rail_set_window_icon(xfc, railWindow, icon, replaceIcon);
rc = TRUE;
}
- xf_rail_return_window(railWindow);
+ xf_rail_return_window(railWindow, FALSE);
return rc;
}
@@ -700,7 +700,7 @@ static BOOL xf_rail_window_cached_icon(rdpContext* context, const WINDOW_ORDER_I
BOOL rc = FALSE;
xfContext* xfc = (xfContext*)context;
BOOL replaceIcon = 0;
- xfAppWindow* railWindow = xf_rail_get_window(xfc, orderInfo->windowId);
+ xfAppWindow* railWindow = xf_rail_get_window(xfc, orderInfo->windowId, FALSE);
if (!railWindow)
return TRUE;
@@ -719,7 +719,7 @@ static BOOL xf_rail_window_cached_icon(rdpContext* context, const WINDOW_ORDER_I
xf_rail_set_window_icon(xfc, railWindow, icon, replaceIcon);
rc = TRUE;
}
- xf_rail_return_window(railWindow);
+ xf_rail_return_window(railWindow, FALSE);
return rc;
}
@@ -939,7 +939,7 @@ static UINT xf_rail_server_local_move_size(RailClientContext* context,
int direction = 0;
Window child_window;
xfContext* xfc = (xfContext*)context->custom;
- xfAppWindow* appWindow = xf_rail_get_window(xfc, localMoveSize->windowId);
+ xfAppWindow* appWindow = xf_rail_get_window(xfc, localMoveSize->windowId, FALSE);
if (!appWindow)
return ERROR_INTERNAL_ERROR;
@@ -1020,7 +1020,7 @@ static UINT xf_rail_server_local_move_size(RailClientContext* context,
else
xf_EndLocalMoveSize(xfc, appWindow);
- xf_rail_return_window(appWindow);
+ xf_rail_return_window(appWindow, FALSE);
return CHANNEL_RC_OK;
}
@@ -1033,7 +1033,7 @@ static UINT xf_rail_server_min_max_info(RailClientContext* context,
const RAIL_MINMAXINFO_ORDER* minMaxInfo)
{
xfContext* xfc = (xfContext*)context->custom;
- xfAppWindow* appWindow = xf_rail_get_window(xfc, minMaxInfo->windowId);
+ xfAppWindow* appWindow = xf_rail_get_window(xfc, minMaxInfo->windowId, FALSE);
if (appWindow)
{
@@ -1042,7 +1042,7 @@ static UINT xf_rail_server_min_max_info(RailClientContext* context,
minMaxInfo->minTrackHeight, minMaxInfo->maxTrackWidth,
minMaxInfo->maxTrackHeight);
}
- xf_rail_return_window(appWindow);
+ xf_rail_return_window(appWindow, FALSE);
return CHANNEL_RC_OK;
}
@@ -1199,13 +1199,20 @@ BOOL xf_rail_del_window(xfContext* xfc, UINT64 id)
if (!xfc->railWindows)
return FALSE;
- return HashTable_Remove(xfc->railWindows, &id);
+ xf_lock_x11(xfc);
+ const BOOL res = HashTable_Remove(xfc->railWindows, &id);
+ xf_unlock_x11(xfc);
+ return res;
}
-void xf_rail_return_windowFrom(xfAppWindow* window, const char* file, const char* fkt, size_t line)
+void xf_rail_return_windowFrom(xfAppWindow* window, BOOL alreadyLocked, const char* file,
+ const char* fkt, size_t line)
{
if (!window)
return;
+ if (alreadyLocked)
+ return;
+
xfAppWindowsUnlockFrom(window->xfc, file, fkt, line);
}
diff --git a/client/X11/xf_rail.h b/client/X11/xf_rail.h
index 83d5fff..a91c1bf 100644
--- a/client/X11/xf_rail.h
+++ b/client/X11/xf_rail.h
@@ -36,14 +36,16 @@ void xf_rail_disable_remoteapp_mode(xfContext* xfc);
xfAppWindow* xf_rail_add_window(xfContext* xfc, UINT64 id, UINT32 x, UINT32 y, UINT32 width,
UINT32 height, UINT32 surfaceId);
-#define xf_rail_return_window(window) \
- xf_rail_return_windowFrom((window), __FILE__, __func__, __LINE__)
-void xf_rail_return_windowFrom(xfAppWindow* window, const char* file, const char* fkt, size_t line);
+#define xf_rail_return_window(window, alreadyLocked) \
+ xf_rail_return_windowFrom((window), (alreadyLocked), __FILE__, __func__, __LINE__)
+void xf_rail_return_windowFrom(xfAppWindow* window, BOOL alreadyLocked, const char* file,
+ const char* fkt, size_t line);
-#define xf_rail_get_window(xfc, id) \
- xf_rail_get_windowFrom((xfc), (id), __FILE__, __func__, __LINE__)
+#define xf_rail_get_window(xfc, id, alreadyLocked) \
+ xf_rail_get_windowFrom((xfc), (id), (alreadyLocked), __FILE__, __func__, __LINE__)
-xfAppWindow* xf_rail_get_windowFrom(xfContext* xfc, UINT64 id, const char* file, const char* fkt,
+xfAppWindow* xf_rail_get_windowFrom(xfContext* xfc, UINT64 id, BOOL alreadyLocked, const char* file,
+ const char* fkt,
size_t line);
BOOL xf_rail_del_window(xfContext* xfc, UINT64 id);
diff --git a/client/X11/xf_window.c b/client/X11/xf_window.c
index 09b4a81..8d09ced 100644
--- a/client/X11/xf_window.c
+++ b/client/X11/xf_window.c
@@ -1070,8 +1070,6 @@ void xf_UpdateWindowArea(xfContext* xfc, xfAppWindow* appWindow, int x, int y, i
if (ay + height > appWindow->windowOffsetY + appWindow->height)
height = (appWindow->windowOffsetY + appWindow->height - 1) - ay;
- xf_lock_x11(xfc);
-
if (xfc->context.settings->SoftwareGdi)
{
XPutImage(xfc->display, xfc->primary, appWindow->gc, xfc->image, ax, ay, ax, ay, width,
@@ -1081,7 +1079,6 @@ void xf_UpdateWindowArea(xfContext* xfc, xfAppWindow* appWindow, int x, int y, i
XCopyArea(xfc->display, xfc->primary, appWindow->handle, appWindow->gc, ax, ay, width, height,
x, y);
XFlush(xfc->display);
- xf_unlock_x11(xfc);
}
void xf_DestroyWindow(xfContext* xfc, xfAppWindow* appWindow)
@@ -1122,8 +1119,8 @@ static xfAppWindow* get_windowUnlocked(xfContext* xfc, UINT64 id)
return HashTable_GetItemValue(xfc->railWindows, &id);
}
-xfAppWindow* xf_rail_get_windowFrom(xfContext* xfc, UINT64 id, const char* file, const char* fkt,
- size_t line)
+xfAppWindow* xf_rail_get_windowFrom(xfContext* xfc, UINT64 id, BOOL alreadyLocked, const char* file,
+ const char* fkt, size_t line)
{
if (!xfc)
return NULL;
@@ -1131,9 +1128,12 @@ xfAppWindow* xf_rail_get_windowFrom(xfContext* xfc, UINT64 id, const char* file,
if (!xfc->railWindows)
return NULL;
- xfAppWindowsLockFrom(xfc, file, fkt, line);
+ if (!alreadyLocked)
+ xfAppWindowsLockFrom(xfc, file, fkt, line);
+
xfAppWindow* window = get_windowUnlocked(xfc, id);
- if (!window)
+
+ if (!window && !alreadyLocked)
xfAppWindowsUnlockFrom(xfc, file, fkt, line);
return window;
@@ -1176,6 +1176,7 @@ xfAppWindow* xf_AppWindowFromX11WindowFrom(xfContext* xfc, Window wnd, const cha
void xfAppWindowsLockFrom(xfContext* xfc, const char* file, const char* fkt, size_t line)
{
WINPR_ASSERT(xfc);
+ xf_lock_x11(xfc);
EnterCriticalSection(&xfc->railWindows->lock);
}
@@ -1183,4 +1184,5 @@ void xfAppWindowsUnlockFrom(xfContext* xfc, const char* file, const char* fkt, s
{
WINPR_ASSERT(xfc);
LeaveCriticalSection(&xfc->railWindows->lock);
+ xf_unlock_x11(xfc);
}
--
2.53.0

View File

@ -1,76 +0,0 @@
From 3a4ee3f0020977320066b99da16456b6364c1365 Mon Sep 17 00:00:00 2001
From: Ondrej Holy <oholy@redhat.com>
Date: Thu, 22 Jan 2026 12:50:22 +0100
Subject: [PATCH] [codec,clear] check clear_decomress glyphData
Backport of commit 243ecf804bb122e8e643a5c142ad5a49d7aa19ee.
Co-Authored-By: Claude <noreply@anthropic.com>
---
libfreerdp/codec/clear.c | 50 +++++++++++++++++++++++++++++++++++++++-
1 file changed, 49 insertions(+), 1 deletion(-)
diff --git a/libfreerdp/codec/clear.c b/libfreerdp/codec/clear.c
index 4f0aead10..fcafb9235 100644
--- a/libfreerdp/codec/clear.c
+++ b/libfreerdp/codec/clear.c
@@ -1145,7 +1145,55 @@ INT32 clear_decompress(CLEAR_CONTEXT* clear, const BYTE* pSrcData, UINT32 SrcSiz
if (glyphData)
{
- if (!freerdp_image_copy(glyphData, clear->format, 0, 0, 0, nWidth, nHeight, pDstData,
+ uint32_t w = MIN(nWidth, nDstWidth);
+ if (nXDst > nDstWidth)
+ {
+ WLog_WARN(TAG, "glyphData copy area x exceeds destination: x=%" PRIu32 " > %" PRIu32,
+ nXDst, nDstWidth);
+ w = 0;
+ }
+ else if (nXDst + w > nDstWidth)
+ {
+ WLog_WARN(TAG,
+ "glyphData copy area x + width exceeds destination: x=%" PRIu32 " + %" PRIu32
+ " > %" PRIu32,
+ nXDst, w, nDstWidth);
+ w = nDstWidth - nXDst;
+ }
+
+ if (w != nWidth)
+ {
+ WLog_WARN(TAG,
+ "glyphData copy area width truncated: requested=%" PRIu32
+ ", truncated to %" PRIu32,
+ nWidth, w);
+ }
+
+ uint32_t h = MIN(nHeight, nDstHeight);
+ if (nYDst > nDstHeight)
+ {
+ WLog_WARN(TAG, "glyphData copy area y exceeds destination: y=%" PRIu32 " > %" PRIu32,
+ nYDst, nDstHeight);
+ h = 0;
+ }
+ else if (nYDst + h > nDstHeight)
+ {
+ WLog_WARN(TAG,
+ "glyphData copy area y + height exceeds destination: x=%" PRIu32 " + %" PRIu32
+ " > %" PRIu32,
+ nYDst, h, nDstHeight);
+ h = nDstHeight - nYDst;
+ }
+
+ if (h != nHeight)
+ {
+ WLog_WARN(TAG,
+ "glyphData copy area height truncated: requested=%" PRIu32
+ ", truncated to %" PRIu32,
+ nHeight, h);
+ }
+
+ if (!freerdp_image_copy(glyphData, clear->format, 0, 0, 0, w, h, pDstData,
DstFormat, nDstStep, nXDst, nYDst, palette, FREERDP_FLIP_NONE))
goto fail;
}
--
2.52.0

View File

@ -1,61 +0,0 @@
From 00a593f9eda67212539e4dcac68ea5a699eb3e93 Mon Sep 17 00:00:00 2001
From: Ondrej Holy <oholy@redhat.com>
Date: Thu, 22 Jan 2026 12:48:37 +0100
Subject: [PATCH] [codec,clear] fix clear_resize_buffer checks
Backport of commit c4391827d7facfc874ca7f61a92afb82232a5748.
Co-Authored-By: Claude <noreply@anthropic.com>
---
libfreerdp/codec/clear.c | 17 +++++++++--------
1 file changed, 9 insertions(+), 8 deletions(-)
diff --git a/libfreerdp/codec/clear.c b/libfreerdp/codec/clear.c
index e38fa0dcf..299acef2e 100644
--- a/libfreerdp/codec/clear.c
+++ b/libfreerdp/codec/clear.c
@@ -62,7 +62,7 @@ struct _CLEAR_CONTEXT
NSC_CONTEXT* nsc;
UINT32 seqNumber;
BYTE* TempBuffer;
- UINT32 TempSize;
+ size_t TempSize;
UINT32 nTempStep;
UINT32 TempFormat;
UINT32 format;
@@ -313,24 +313,25 @@ static BOOL clear_decompress_subcode_rlex(wStream* s, UINT32 bitmapDataByteCount
static BOOL clear_resize_buffer(CLEAR_CONTEXT* clear, UINT32 width, UINT32 height)
{
- UINT32 size;
-
if (!clear)
return FALSE;
- size = ((width + 16) * (height + 16) * GetBytesPerPixel(clear->format));
+ const UINT64 size = 1ull * (width + 16ull) * (height + 16ull);
+ const size_t bpp = GetBytesPerPixel(clear->format);
+ if (size > UINT32_MAX / bpp)
+ return FALSE;
- if (size > clear->TempSize)
+ if (size > clear->TempSize / bpp)
{
- BYTE* tmp = (BYTE*)realloc(clear->TempBuffer, size);
+ BYTE* tmp = (BYTE*)realloc(clear->TempBuffer, size * bpp);
if (!tmp)
{
- WLog_ERR(TAG, "clear->TempBuffer realloc failed for %" PRIu32 " bytes", size);
+ WLog_ERR(TAG, "clear->TempBuffer realloc failed for %" PRIu64 " bytes", size);
return FALSE;
}
- clear->TempSize = size;
+ clear->TempSize = size * bpp;
clear->TempBuffer = tmp;
}
--
2.52.0

View File

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

View File

@ -1,35 +0,0 @@
From 5b03db3926bd7bc1bf5819de068ed71a73b25236 Mon Sep 17 00:00:00 2001
From: Ondrej Holy <oholy@redhat.com>
Date: Thu, 22 Jan 2026 12:49:28 +0100
Subject: [PATCH] [codec,clear] fix off by one length check
Backport of commit f8688b57f6cfad9a0b05475a6afbde355ffab720.
Co-Authored-By: Claude <noreply@anthropic.com>
---
libfreerdp/codec/clear.c | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/libfreerdp/codec/clear.c b/libfreerdp/codec/clear.c
index 09f5dad6d..4f0aead10 100644
--- a/libfreerdp/codec/clear.c
+++ b/libfreerdp/codec/clear.c
@@ -881,11 +881,14 @@ static BOOL clear_decompress_bands_data(CLEAR_CONTEXT* clear, wStream* s, UINT32
if (count > nHeight)
count = nHeight;
- if (nXDstRel + i > nDstWidth)
+ if (nXDstRel + i >= nDstWidth)
return FALSE;
for (UINT32 y = 0; y < count; y++)
{
+ if (nYDstRel + y >= nDstHeight)
+ return FALSE;
+
BYTE* pDstPixel8 = &pDstData[((nYDstRel + y) * nDstStep) +
((nXDstRel + i) * GetBytesPerPixel(DstFormat))];
UINT32 color = ReadColor(cpSrcPixel, clear->format);
--
2.52.0

View File

@ -1,35 +0,0 @@
From a2dde6d9832cb032e8cf12cab3da84dafbab9006 Mon Sep 17 00:00:00 2001
From: Ondrej Holy <oholy@redhat.com>
Date: Fri, 10 Apr 2026 11:32:09 +0200
Subject: [PATCH] [codec,clear] update CLEAR_VBAR_ENTRY::size after alloc
Backport of commit a2dde6d9832cb032e8cf12cab3da84dafbab9006.
Made-with: Cursor
---
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 e38fa0d..eda30ad 100644
--- a/libfreerdp/codec/clear.c
+++ b/libfreerdp/codec/clear.c
@@ -565,7 +565,6 @@ static BOOL resize_vbar_entry(CLEAR_CONTEXT* clear, CLEAR_VBAR_ENTRY* vBarEntry)
const UINT32 oldPos = vBarEntry->size * bpp;
const UINT32 diffSize = (vBarEntry->count - vBarEntry->size) * bpp;
BYTE* tmp;
- vBarEntry->size = vBarEntry->count;
tmp = (BYTE*)realloc(vBarEntry->pixels, 1ull * vBarEntry->count * bpp);
if (!tmp)
@@ -576,6 +575,7 @@ static BOOL resize_vbar_entry(CLEAR_CONTEXT* clear, CLEAR_VBAR_ENTRY* vBarEntry)
memset(&tmp[oldPos], 0, diffSize);
vBarEntry->pixels = tmp;
+ vBarEntry->size = vBarEntry->count;
}
if (!vBarEntry->pixels && vBarEntry->size)
--
2.49.0

View File

@ -1,57 +0,0 @@
From 947feeadfddf01d30dda5aa16ebc335bfcc23ae0 Mon Sep 17 00:00:00 2001
From: Ondrej Holy <oholy@redhat.com>
Date: Tue, 29 Apr 2026 13:42:00 +0000
Subject: [PATCH] [codec,clear] Update CLEAR_GLYPH_ENTRY::count after alloc
Backport of commit c49d1ad43b8c7b32794d0250f2623c2dccd7ef25.
Adapted for 2.11.7: uses GetBytesPerPixel instead of FreeRDPGetBytesPerPixel,
plain realloc instead of winpr_aligned_recalloc; overflow checks match upstream
(size_t count, hlimit / exceeded logging); glyphEntry->count set via (UINT32)cast
after successful realloc.
Made-with: Cursor
---
libfreerdp/codec/clear.c | 22 +++++++++++++++++-----
1 file changed, 17 insertions(+), 5 deletions(-)
diff --git a/libfreerdp/codec/clear.c b/libfreerdp/codec/clear.c
--- a/libfreerdp/codec/clear.c
+++ b/libfreerdp/codec/clear.c
@@ -979,20 +979,31 @@
{
const UINT32 bpp = GetBytesPerPixel(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;
- tmp = realloc(glyphEntry->pixels, 1ull * glyphEntry->count * bpp);
+ tmp = realloc(glyphEntry->pixels, 1ull * count * bpp);
if (!tmp)
{
- WLog_ERR(TAG, "glyphEntry->pixels realloc %" PRIu32 " failed!",
- glyphEntry->count * bpp);
+ WLog_ERR(TAG, "glyphEntry->pixels realloc %" PRIuz " failed!",
+ count * bpp);
return FALSE;
}
+ glyphEntry->count = (UINT32)count;
glyphEntry->size = glyphEntry->count;
glyphEntry->pixels = (UINT32*)tmp;
}

View File

@ -1,114 +0,0 @@
From b54003ad2dcb69b3c7836ae1445c48af8b39cd4c Mon Sep 17 00:00:00 2001
From: Ondrej Holy <oholy@redhat.com>
Date: Sat, 28 Mar 2026 12:13:04 +0100
Subject: [PATCH] [codec,color] add freerdp_glyph_convert_ex
Backport of commit 3bc1eeb4f63ceec9a696af194e4c1ea0e67ff60c.
Made-with: Cursor
---
libfreerdp/codec/color.c | 12 +++++++++--
libfreerdp/gdi/graphics.c | 45 +++++++++++++++++++++++++++++++++++++++
2 files changed, 55 insertions(+), 2 deletions(-)
diff --git a/libfreerdp/codec/color.c b/libfreerdp/codec/color.c
index f8467c295..4c72ee3e6 100644
--- a/libfreerdp/codec/color.c
+++ b/libfreerdp/codec/color.c
@@ -28,6 +28,7 @@
#include <stdlib.h>
#include <winpr/crt.h>
+#include <winpr/assert.h>
#include <freerdp/log.h>
#include <freerdp/freerdp.h>
@@ -49,13 +50,20 @@ BYTE* freerdp_glyph_convert(UINT32 width, UINT32 height, const BYTE* data)
const BYTE* srcp;
BYTE* dstp;
BYTE* dstData;
- UINT32 scanline;
+ size_t scanline;
+
/*
* converts a 1-bit-per-pixel glyph to a one-byte-per-pixel glyph:
* this approach uses a little more memory, but provides faster
* means of accessing individual pixels in blitting operations
*/
- scanline = (width + 7) / 8;
+ scanline = (width + 7ull) / 8ull;
+
+ if ((width == 0) || (height == 0))
+ return NULL;
+
+ WINPR_ASSERT(data);
+
dstData = (BYTE*)_aligned_malloc(1ull * width * height, 16);
if (!dstData)
diff --git a/libfreerdp/gdi/graphics.c b/libfreerdp/gdi/graphics.c
index 479eac9b8..dab159c43 100644
--- a/libfreerdp/gdi/graphics.c
+++ b/libfreerdp/gdi/graphics.c
@@ -23,6 +23,7 @@
#include "config.h"
#endif
+#include <winpr/assert.h>
#include <winpr/crt.h>
#include <freerdp/log.h>
@@ -215,6 +216,50 @@ static BOOL gdi_Bitmap_SetSurface(rdpContext* context, rdpBitmap* bitmap, BOOL p
}
/* Glyph Class */
+static BYTE* freerdp_glyph_convert_ex(UINT32 width, UINT32 height, const BYTE* data, size_t len)
+{
+ /*
+ * converts a 1-bit-per-pixel glyph to a one-byte-per-pixel glyph:
+ * this approach uses a little more memory, but provides faster
+ * means of accessing individual pixels in blitting operations
+ */
+ const size_t scanline = (width + 7ull) / 8ull;
+ const size_t required = scanline * height;
+ if (len < required)
+ return NULL;
+
+ if ((len == 0) || (width == 0) || (height == 0))
+ return NULL;
+
+ WINPR_ASSERT(data);
+
+ BYTE* dstData = (BYTE*)_aligned_malloc(1ull * width * height, 16);
+
+ if (!dstData)
+ return NULL;
+
+ ZeroMemory(dstData, 1ULL * width * height);
+ BYTE* dstp = dstData;
+
+ for (UINT32 y = 0; y < height; y++)
+ {
+ const BYTE* srcp = &data[1ull * y * scanline];
+
+ for (UINT32 x = 0; x < width; x++)
+ {
+ if ((*srcp & (0x80 >> (x % 8))) != 0)
+ *dstp = 0xFF;
+
+ dstp++;
+
+ if (((x + 1) % 8 == 0) && x != 0)
+ srcp++;
+ }
+ }
+
+ return dstData;
+}
+
static BOOL gdi_Glyph_New(rdpContext* context, const rdpGlyph* glyph)
{
BYTE* data;
--
2.53.0

View File

@ -1,218 +0,0 @@
From 74ff8a097c3606710327e3af2863aa52d2dac79e Mon Sep 17 00:00:00 2001
From: Ondrej Holy <oholy@redhat.com>
Date: Tue, 29 Apr 2026 15:10:00 +0000
Subject: [PATCH] [codec,dsp] add format checks
Backport of commit 03b48b3601d867afccac1cdc6081de7a275edce7.
Adapted for 2.11.x: uses `context->format` instead of
`context->common.format`, no `WINPR_RESTRICT`, added
`#include <winpr/assert.h>`. Kept C89 declaration style in
`freerdp_dsp_decode_mp3` and `freerdp_dsp_encode_mp3`. Omitted
LAME/mp3 variable modernization. DVI_ADPCM block in
`freerdp_dsp_context_reset` omits FramesPerPacket sizing from upstream 3.x
but adds `Stream_SetPosition(context->buffer, 0)` after validation (same
intent as upstream `Stream_ResetPosition`). `freerdp_dsp_decode_ima_adpcm`
matches upstream order: validate format before locals; `out_size` uses
`size * 4ull`. `Stream_BufferAs` replaced with
`Stream_Buffer` cast. `nullptr` replaced with `NULL`.
Made-with: Cursor
---
libfreerdp/codec/dsp.c | 96 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 93 insertions(+), 4 deletions(-)
diff --git a/libfreerdp/codec/dsp.c b/libfreerdp/codec/dsp.c
index a18d044..e79911d 100644
--- a/libfreerdp/codec/dsp.c
+++ b/libfreerdp/codec/dsp.c
@@ -27,6 +27,7 @@
#include <string.h>
#include <winpr/crt.h>
+#include <winpr/assert.h>
#include <freerdp/types.h>
#include <freerdp/log.h>
@@ -329,13 +330,30 @@
return (UINT16)d;
}
+static BOOL valid_ima_adpcm_format(const FREERDP_DSP_CONTEXT* context)
+{
+ WINPR_ASSERT(context);
+ if (context->format.wFormatTag != WAVE_FORMAT_DVI_ADPCM)
+ return FALSE;
+ if (context->format.nBlockAlign <= 4ULL)
+ return FALSE;
+ if (context->format.nChannels < 1)
+ return FALSE;
+ if (context->format.wBitsPerSample == 0)
+ return FALSE;
+ return TRUE;
+}
+
static BOOL freerdp_dsp_decode_ima_adpcm(FREERDP_DSP_CONTEXT* context, const BYTE* src, size_t size,
wStream* out)
{
+ if (!valid_ima_adpcm_format(context))
+ return FALSE;
+
BYTE* dst;
BYTE sample;
UINT16 decoded;
- size_t out_size = size * 4;
+ size_t out_size = size * 4ull;
UINT32 channel;
const UINT32 block_size = context->format.nBlockAlign;
const UINT32 channels = context->format.nChannels;
@@ -464,6 +482,20 @@
#endif
#if defined(WITH_LAME)
+static BOOL valid_mp3_format(const FREERDP_DSP_CONTEXT* context)
+{
+ WINPR_ASSERT(context);
+ if (context->format.wFormatTag != WAVE_FORMAT_MPEGLAYER3)
+ return FALSE;
+ if (context->format.nChannels < 1)
+ return FALSE;
+ if (context->format.wBitsPerSample == 0)
+ return FALSE;
+ if (context->format.nSamplesPerSec == 0)
+ return FALSE;
+ return TRUE;
+}
+
static BOOL freerdp_dsp_decode_mp3(FREERDP_DSP_CONTEXT* context, const BYTE* src, size_t size,
wStream* out)
{
@@ -474,6 +506,8 @@
if (!context || !src || !out)
return FALSE;
+ if (!valid_mp3_format(context))
+ return FALSE;
buffer_size = 2 * context->format.nChannels * context->format.nSamplesPerSec;
@@ -509,6 +543,9 @@
if (!context || !src || !out)
return FALSE;
+ if (!valid_mp3_format(context))
+ return FALSE;
+
samples_per_channel = size / context->format.nChannels / context->format.wBitsPerSample / 8;
/* Ensure worst case buffer size for mp3 stream taken from LAME header */
@@ -716,6 +753,10 @@
BYTE encoded;
size_t out_size;
size_t align;
+
+ if (!valid_ima_adpcm_format(context))
+ return FALSE;
+
out_size = size / 2;
if (!Stream_EnsureRemainingCapacity(out, size))
@@ -788,6 +829,20 @@
static const INT32 ms_adpcm_coeffs2[7] = { 0, -256, 0, 64, 0, -208, -232 };
+static BOOL valid_ms_adpcm_format(const FREERDP_DSP_CONTEXT* context)
+{
+ WINPR_ASSERT(context);
+ if (context->format.wFormatTag != WAVE_FORMAT_ADPCM)
+ return FALSE;
+ if (context->format.nBlockAlign <= 4ULL)
+ return FALSE;
+ if (context->format.nChannels < 1)
+ return FALSE;
+ if (context->format.wBitsPerSample == 0)
+ return FALSE;
+ return TRUE;
+}
+
static INLINE INT16 freerdp_dsp_decode_ms_adpcm_sample(ADPCM* adpcm, BYTE sample, int channel)
{
INT8 nibble;
@@ -819,6 +874,9 @@
BYTE* dst;
BYTE sample;
const size_t out_size = size * 4;
+
+ if (!valid_ms_adpcm_format(context))
+ return FALSE;
const UINT32 channels = context->format.nChannels;
const UINT32 block_size = context->format.nBlockAlign;
@@ -947,6 +1005,10 @@
INT32 sample;
size_t out_size;
const size_t step = 8 + ((context->format.nChannels > 1) ? 4 : 0);
+
+ if (!valid_ms_adpcm_format(context))
+ return FALSE;
+
out_size = size / 2;
if (!Stream_EnsureRemainingCapacity(out, size))
@@ -1308,6 +1370,28 @@
return FALSE;
context->format = *targetFormat;
+
+ switch (context->format.wFormatTag)
+ {
+#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;
+ Stream_SetPosition(context->buffer, 0);
+ break;
+ default:
+ break;
+ }
+
#if defined(WITH_FAAD2)
context->faadSetup = FALSE;
#endif
@@ -1328,19 +1412,24 @@
cfg = faacEncGetCurrentConfiguration(context->faac);
cfg->bitRate = 10000;
- faacEncSetConfiguration(context->faac, cfg);
+ {
+ const int frc = faacEncSetConfiguration(context->faac, cfg);
+ if (frc <= 0)
+ return FALSE;
+ }
}
#endif
#if defined(WITH_SOXR)
{
soxr_io_spec_t iospec = soxr_io_spec(SOXR_INT16, SOXR_INT16);
- soxr_error_t error;
+ soxr_error_t error = NULL;
+
soxr_delete(context->sox);
context->sox = soxr_create(context->format.nSamplesPerSec, targetFormat->nSamplesPerSec,
targetFormat->nChannels, &error, &iospec, NULL, NULL);
- if (!context->sox || (error != 0))
+ if (!context->sox || (error != NULL))
return FALSE;
}
#endif

View File

@ -1,237 +0,0 @@
From aaf333862fb54d4cfc19a1866b76500d86679719 Mon Sep 17 00:00:00 2001
From: Ondrej Holy <oholy@redhat.com>
Date: Tue, 28 Apr 2026 04:41:15 +0000
Subject: [PATCH] [codec,dsp] fix array bounds checks
Backport of commit 16df2300e1e3f5a51f68fb1626429e58b531b7c8.
Adapted for 2.11.x: uses C89 declaration style (variables declared
at top of scope, new variables wrapped in blocks), no
`WINPR_RESTRICT`, kept `int channel` in decode function,
`dsp_encode_ima_adpcm_sample` keeps `int channel`.
Made-with: Cursor
---
libfreerdp/codec/dsp.c | 91 +++++++++++++++++++++++++++++++++++++-----
1 file changed, 82 insertions(+), 9 deletions(-)
diff --git a/libfreerdp/codec/dsp.c b/libfreerdp/codec/dsp.c
index e79911d..6d4f780 100644
--- a/libfreerdp/codec/dsp.c
+++ b/libfreerdp/codec/dsp.c
@@ -297,7 +297,16 @@ static UINT16 dsp_decode_ima_adpcm_sample(ADPCM* adpcm, unsigned int channel, BY
{
INT32 ss;
INT32 d;
- ss = ima_step_size_table[adpcm->ima.last_step[channel]];
+ INT16 offset;
+
+ WINPR_ASSERT(channel < ARRAYSIZE(adpcm->ima.last_step));
+ WINPR_ASSERT(channel < ARRAYSIZE(adpcm->ima.last_sample));
+
+ offset = adpcm->ima.last_step[channel];
+ WINPR_ASSERT(offset >= 0);
+ WINPR_ASSERT(offset < ARRAYSIZE(ima_step_size_table));
+
+ ss = ima_step_size_table[offset];
d = (ss >> 3);
if (sample & 1)
@@ -320,6 +329,8 @@ static UINT16 dsp_decode_ima_adpcm_sample(ADPCM* adpcm, unsigned int channel, BY
d = 32767;
adpcm->ima.last_sample[channel] = (INT16)d;
+
+ WINPR_ASSERT(sample < ARRAYSIZE(ima_step_index_table));
adpcm->ima.last_step[channel] += ima_step_index_table[sample];
if (adpcm->ima.last_step[channel] < 0)
@@ -368,6 +379,9 @@ static BOOL freerdp_dsp_decode_ima_adpcm(FREERDP_DSP_CONTEXT* context, const BYT
{
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));
@@ -377,6 +391,8 @@ static BOOL freerdp_dsp_decode_ima_adpcm(FREERDP_DSP_CONTEXT* context, const BYT
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));
@@ -388,6 +404,8 @@ static BOOL freerdp_dsp_decode_ima_adpcm(FREERDP_DSP_CONTEXT* context, const BYT
if (channels > 1)
{
+ if (size < 8)
+ return FALSE;
for (i = 0; i < 8; i++)
{
channel = (i < 4 ? 0 : 1);
@@ -407,6 +425,8 @@ static BOOL freerdp_dsp_decode_ima_adpcm(FREERDP_DSP_CONTEXT* context, const BYT
}
else
{
+ if (size < 1)
+ return FALSE;
sample = ((*src) & 0x0f);
decoded = dsp_decode_ima_adpcm_sample(&context->adpcm, 0, sample);
*dst++ = (decoded & 0xFF);
@@ -687,7 +707,16 @@ static BYTE dsp_encode_ima_adpcm_sample(ADPCM* adpcm, int channel, INT16 sample)
INT32 ss;
BYTE enc;
INT32 diff;
- ss = ima_step_size_table[adpcm->ima.last_step[channel]];
+ INT16 offset;
+
+ WINPR_ASSERT(channel < ARRAYSIZE(adpcm->ima.last_step));
+ WINPR_ASSERT(channel < ARRAYSIZE(adpcm->ima.last_sample));
+
+ offset = adpcm->ima.last_step[channel];
+ WINPR_ASSERT(offset >= 0);
+ WINPR_ASSERT(offset < ARRAYSIZE(ima_step_size_table));
+
+ ss = ima_step_size_table[offset];
d = e = sample - adpcm->ima.last_sample[channel];
diff = ss >> 3;
enc = 0;
@@ -733,6 +762,8 @@ static BYTE dsp_encode_ima_adpcm_sample(ADPCM* adpcm, int channel, INT16 sample)
diff = 32767;
adpcm->ima.last_sample[channel] = (INT16)diff;
+
+ WINPR_ASSERT(enc < ARRAYSIZE(ima_step_index_table));
adpcm->ima.last_step[channel] += ima_step_index_table[enc];
if (adpcm->ima.last_step[channel] < 0)
@@ -847,10 +878,24 @@ static INLINE INT16 freerdp_dsp_decode_ms_adpcm_sample(ADPCM* adpcm, BYTE sample
{
INT8 nibble;
INT32 presample;
+ BYTE predictor;
+ INT32 coeff1 = 0;
+ INT32 coeff2 = 0;
+
+ 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));
+
nibble = (sample & 0x08 ? (INT8)sample - 16 : (INT8)sample);
- presample = ((adpcm->ms.sample1[channel] * ms_adpcm_coeffs1[adpcm->ms.predictor[channel]]) +
- (adpcm->ms.sample2[channel] * ms_adpcm_coeffs2[adpcm->ms.predictor[channel]])) /
- 256;
+ predictor = adpcm->ms.predictor[channel];
+ if (predictor < ARRAYSIZE(ms_adpcm_coeffs1))
+ coeff1 = ms_adpcm_coeffs1[predictor];
+
+ if (predictor < ARRAYSIZE(ms_adpcm_coeffs2))
+ coeff2 = ms_adpcm_coeffs2[predictor];
+ presample =
+ ((adpcm->ms.sample1[channel] * coeff1) + (adpcm->ms.sample2[channel] * coeff2)) / 256;
presample += nibble * adpcm->ms.delta[channel];
if (presample > 32767)
@@ -860,7 +905,14 @@ static INLINE INT16 freerdp_dsp_decode_ms_adpcm_sample(ADPCM* adpcm, BYTE sample
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;
@@ -891,6 +943,9 @@ static BOOL freerdp_dsp_decode_ms_adpcm(FREERDP_DSP_CONTEXT* context, const BYTE
{
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);
@@ -917,6 +972,9 @@ static BOOL freerdp_dsp_decode_ms_adpcm(FREERDP_DSP_CONTEXT* context, const BYTE
}
else
{
+ if (size < 7)
+ return FALSE;
+
context->adpcm.ms.predictor[0] = *src++;
context->adpcm.ms.delta[0] = read_int16(src);
src += 2;
@@ -934,12 +992,16 @@ static BOOL freerdp_dsp_decode_ms_adpcm(FREERDP_DSP_CONTEXT* context, const BYTE
if (channels > 1)
{
+ if (size < 1)
+ return FALSE;
sample = *src++;
size--;
write_int16(dst, freerdp_dsp_decode_ms_adpcm_sample(&context->adpcm, sample >> 4, 0));
dst += 2;
write_int16(dst, freerdp_dsp_decode_ms_adpcm_sample(&context->adpcm, sample & 0x0F, 1));
dst += 2;
+ if (size < 1)
+ return FALSE;
sample = *src++;
size--;
write_int16(dst, freerdp_dsp_decode_ms_adpcm_sample(&context->adpcm, sample >> 4, 0));
@@ -949,6 +1011,8 @@ static BOOL freerdp_dsp_decode_ms_adpcm(FREERDP_DSP_CONTEXT* context, const BYTE
}
else
{
+ if (size < 1)
+ return FALSE;
sample = *src++;
size--;
write_int16(dst, freerdp_dsp_decode_ms_adpcm_sample(&context->adpcm, sample >> 4, 0));
@@ -962,10 +1026,16 @@ static BOOL freerdp_dsp_decode_ms_adpcm(FREERDP_DSP_CONTEXT* context, const BYTE
return TRUE;
}
-static BYTE freerdp_dsp_encode_ms_adpcm_sample(ADPCM* adpcm, INT32 sample, int channel)
+static BYTE freerdp_dsp_encode_ms_adpcm_sample(ADPCM* adpcm, INT32 sample, size_t channel)
{
INT32 presample;
INT32 errordelta;
+
+ 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));
+
presample = ((adpcm->ms.sample1[channel] * ms_adpcm_coeffs1[adpcm->ms.predictor[channel]]) +
(adpcm->ms.sample2[channel] * ms_adpcm_coeffs2[adpcm->ms.predictor[channel]])) /
256;
@@ -988,8 +1058,11 @@ static BYTE freerdp_dsp_encode_ms_adpcm_sample(ADPCM* adpcm, INT32 sample, int c
adpcm->ms.sample2[channel] = adpcm->ms.sample1[channel];
adpcm->ms.sample1[channel] = presample;
- adpcm->ms.delta[channel] =
- adpcm->ms.delta[channel] * ms_adpcm_adaptation_table[(((BYTE)errordelta) & 0x0F)] / 256;
+ {
+ const size_t offset = (((BYTE)errordelta) & 0x0F);
+ WINPR_ASSERT(offset < ARRAYSIZE(ms_adpcm_adaptation_table));
+ adpcm->ms.delta[channel] = adpcm->ms.delta[channel] * ms_adpcm_adaptation_table[offset] / 256;
+ }
if (adpcm->ms.delta[channel] < 16)
adpcm->ms.delta[channel] = 16;
--
2.53.0

View File

@ -1,90 +0,0 @@
From 21cb0e2b96e4e86b4ed8b7a76d819808b898e93e Mon Sep 17 00:00:00 2001
From: Ondrej Holy <oholy@redhat.com>
Date: Tue, 31 Mar 2026 07:08:04 +0200
Subject: [PATCH] [codec,nsc] fix use of nsc_process_message
Backport of commit 169971607cece48384cb94632b829bd57336af0f.
Made-with: Cursor
---
libfreerdp/codec/clear.c | 10 ++++++----
libfreerdp/codec/nsc.c | 9 ++++++++-
libfreerdp/gdi/gdi.c | 4 ++--
3 files changed, 16 insertions(+), 7 deletions(-)
diff --git a/libfreerdp/codec/clear.c b/libfreerdp/codec/clear.c
index 4dbe72376..6627b21d6 100644
--- a/libfreerdp/codec/clear.c
+++ b/libfreerdp/codec/clear.c
@@ -103,7 +103,8 @@ static BOOL convert_color(BYTE* dst, UINT32 nDstStep, UINT32 DstFormat, UINT32 n
static BOOL clear_decompress_nscodec(NSC_CONTEXT* nsc, UINT32 width, UINT32 height, wStream* s,
UINT32 bitmapDataByteCount, BYTE* pDstData, UINT32 DstFormat,
- UINT32 nDstStep, UINT32 nXDstRel, UINT32 nYDstRel)
+ UINT32 nDstStep, UINT32 nXDstRel, UINT32 nYDstRel,
+ UINT32 nDstWidth, UINT32 nDstHeight)
{
BOOL rc;
@@ -115,8 +116,8 @@ static BOOL clear_decompress_nscodec(NSC_CONTEXT* nsc, UINT32 width, UINT32 heig
}
rc = nsc_process_message(nsc, 32, width, height, Stream_Pointer(s), bitmapDataByteCount,
- pDstData, DstFormat, nDstStep, nXDstRel, nYDstRel, width, height,
- FREERDP_FLIP_NONE);
+ pDstData, DstFormat, nDstStep, nXDstRel, nYDstRel, nDstWidth,
+ nDstHeight, FREERDP_FLIP_NONE);
Stream_Seek(s, bitmapDataByteCount);
return rc;
}
@@ -535,7 +536,8 @@ static BOOL clear_decompress_subcodecs_data(CLEAR_CONTEXT* clear, wStream* s,
case 1: /* NSCodec */
if (!clear_decompress_nscodec(clear->nsc, width, height, s, bitmapDataByteCount,
- pDstData, DstFormat, nDstStep, nXDstRel, nYDstRel))
+ pDstData, DstFormat, nDstStep, nXDstRel, nYDstRel,
+ nDstWidth, nDstHeight))
return FALSE;
break;
diff --git a/libfreerdp/codec/nsc.c b/libfreerdp/codec/nsc.c
index a257ae24a..f50a1ae89 100644
--- a/libfreerdp/codec/nsc.c
+++ b/libfreerdp/codec/nsc.c
@@ -447,10 +447,17 @@ BOOL nsc_process_message(NSC_CONTEXT* context, UINT16 bpp, UINT32 width, UINT32
UINT32 nDstStride, UINT32 nXDst, UINT32 nYDst, UINT32 nWidth,
UINT32 nHeight, UINT32 flip)
{
+ WINPR_ASSERT(context);
+ WINPR_ASSERT(context->priv);
+
wStream* s;
BOOL ret;
- if (!context || !data || !pDstData)
+ if (!data || !pDstData)
+ {
+ WLog_Print(context->priv->log, WLOG_ERROR, "Invalid argument: data=%p, pDstData=%p",
+ (const void*)data, (void*)pDstData);
return FALSE;
+ }
s = Stream_New((BYTE*)data, length);
diff --git a/libfreerdp/gdi/gdi.c b/libfreerdp/gdi/gdi.c
index 37a010431..8c5e4bf34 100644
--- a/libfreerdp/gdi/gdi.c
+++ b/libfreerdp/gdi/gdi.c
@@ -1074,8 +1074,8 @@ static BOOL gdi_surface_bits(rdpContext* context, const SURFACE_BITS_COMMAND* cm
if (!nsc_process_message(
context->codecs->nsc, cmd->bmp.bpp, cmd->bmp.width, cmd->bmp.height,
cmd->bmp.bitmapData, cmd->bmp.bitmapDataLength, gdi->primary_buffer, format,
- gdi->stride, cmdRect.left, cmdRect.top, cmdRect.right - cmdRect.left,
- cmdRect.bottom - cmdRect.top, FREERDP_FLIP_VERTICAL))
+ gdi->stride, cmdRect.left, cmdRect.top, (UINT32)gdi->width, (UINT32)gdi->height,
+ FREERDP_FLIP_VERTICAL))
{
WLog_ERR(TAG, "Failed to process NSCodec message");
goto out;
--
2.53.0

View File

@ -1,44 +0,0 @@
From 9a9e00529b1b04718370a1d8e2d8b6768c9ee913 Mon Sep 17 00:00:00 2001
From: Ondrej Holy <oholy@redhat.com>
Date: Mon, 16 Mar 2026 14:59:12 +0100
Subject: [PATCH] [codec,nsc] limit copy area in nsc_process_message
Backport of commit 83d9aedea278a74af3e490ff5eeb889c016dbb2b.
Made-with: Cursor
---
libfreerdp/codec/nsc.c | 11 ++++++++++-
1 file changed, 10 insertions(+), 1 deletion(-)
diff --git a/libfreerdp/codec/nsc.c b/libfreerdp/codec/nsc.c
index 5dd2646ea..a257ae24a 100644
--- a/libfreerdp/codec/nsc.c
+++ b/libfreerdp/codec/nsc.c
@@ -24,6 +24,7 @@
#include "config.h"
#endif
+#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -515,7 +516,15 @@ BOOL nsc_process_message(NSC_CONTEXT* context, UINT16 bpp, UINT32 width, UINT32
return FALSE;
}
- if (!freerdp_image_copy(pDstData, DstFormat, nDstStride, nXDst, nYDst, width, height,
+ uint32_t cwidth = width;
+ if (1ull * nXDst + width > nWidth)
+ cwidth = nWidth - nXDst;
+
+ uint32_t cheight = height;
+ if (1ull * nYDst + height > nHeight)
+ cheight = nHeight - nYDst;
+
+ if (!freerdp_image_copy(pDstData, DstFormat, nDstStride, nXDst, nYDst, cwidth, cheight,
context->BitmapData, PIXEL_FORMAT_BGRA32, 0, 0, 0, NULL, flip))
return FALSE;
--
2.53.0

View File

@ -1,31 +0,0 @@
From 836d5b6f6d305ba6542fac8ba2d16b2e4e8b13a7 Mon Sep 17 00:00:00 2001
From: Armin Novak <armin.novak@thincast.com>
Date: Thu, 30 Apr 2026 15:03:21 +0200
Subject: [PATCH] [codec,planar] fix bounds checks
---
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 9b4d13057..67fc4212e 100644
--- a/libfreerdp/codec/planar.c
+++ b/libfreerdp/codec/planar.c
@@ -841,12 +841,12 @@ BOOL planar_decompress(BITMAP_PLANAR_CONTEXT* planar, const BYTE* pSrcData, UINT
return FALSE;
}
- if ((nXDst + nSrcWidth) * bpp > nDstStep)
+ if ((nXDst + nSrcWidth) * bpp > nTempStep)
{
WLog_ERR(TAG,
"planar plane destination (X %" PRIu32 " + width %" PRIu32
") * bpp %" PRIu32 " exceeds stride %" PRIu32,
- nXDst, nSrcWidth, bpp, nDstStep);
+ nXDst, nSrcWidth, bpp, nTempStep);
return FALSE;
}
--
2.52.0

View File

@ -1,31 +0,0 @@
From 2d656eb6b29a68de7f19e8a1cce169259e7506b4 Mon Sep 17 00:00:00 2001
From: Ondrej Holy <oholy@redhat.com>
Date: Tue, 27 Jan 2026 14:35:43 +0100
Subject: [PATCH] [codec,planar] fix decoder length checks
Backport of commit 1bab198a2edd0d0e6e1627d21a433151ea190.
Co-Authored-By: Claude <noreply@anthropic.com>
---
libfreerdp/codec/planar.c | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/libfreerdp/codec/planar.c b/libfreerdp/codec/planar.c
index fe27011e1..1cb2e22bc 100644
--- a/libfreerdp/codec/planar.c
+++ b/libfreerdp/codec/planar.c
@@ -616,6 +616,11 @@ BOOL planar_decompress(BITMAP_PLANAR_CONTEXT* planar, const BYTE* pSrcData, UINT
WINPR_ASSERT(planar);
WINPR_ASSERT(prims);
+ if (planar->maxWidth < nSrcWidth)
+ return FALSE;
+ if (planar->maxHeight < nSrcHeight)
+ return FALSE;
+
if (nDstStep <= 0)
nDstStep = nDstWidth * GetBytesPerPixel(DstFormat);
--
2.52.0

View File

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

View File

@ -1,89 +0,0 @@
From 78188ab479c8e6eb9ba2475b3732c76b4bbe5425 Mon Sep 17 00:00:00 2001
From: Ondrej Holy <oholy@redhat.com>
Date: Mon, 13 Apr 2026 14:00:00 +0200
Subject: [PATCH] [codec,progressive] Fail progressive_rfx_quant_sub on invalid
values
Backport of commit 78188ab479c8e6eb9ba2475b3732c76b4bbe5425.
Made-with: Cursor
---
libfreerdp/codec/progressive.c | 42 ++++++++++++++++++++++++++++++----
1 file changed, 38 insertions(+), 4 deletions(-)
diff --git a/libfreerdp/codec/progressive.c b/libfreerdp/codec/progressive.c
index 8894b35..bbcc921 100644
--- a/libfreerdp/codec/progressive.c
+++ b/libfreerdp/codec/progressive.c
@@ -155,20 +155,51 @@ static INLINE void progressive_rfx_quant_lsub(RFX_COMPONENT_CODEC_QUANT* q, int
q->LL3 -= val; /* LL3 */
}
-static INLINE void progressive_rfx_quant_sub(const RFX_COMPONENT_CODEC_QUANT* q1,
+static INLINE BOOL progressive_rfx_quant_sub(const RFX_COMPONENT_CODEC_QUANT* q1,
const RFX_COMPONENT_CODEC_QUANT* q2,
RFX_COMPONENT_CODEC_QUANT* dst)
{
+ if (q1->HH1 < q2->HL1)
+ return FALSE;
dst->HL1 = q1->HL1 - q2->HL1; /* HL1 */
+
+ if (q1->LH1 < q2->LH1)
+ return FALSE;
dst->LH1 = q1->LH1 - q2->LH1; /* LH1 */
+
+ if (q1->HH1 < q2->HH1)
+ return FALSE;
dst->HH1 = q1->HH1 - q2->HH1; /* HH1 */
+
+ if (q1->HL2 < q2->HL2)
+ return FALSE;
dst->HL2 = q1->HL2 - q2->HL2; /* HL2 */
+
+ if (q1->LH2 < q2->LH2)
+ return FALSE;
dst->LH2 = q1->LH2 - q2->LH2; /* LH2 */
+
+ if (q1->HH2 < q2->HH2)
+ return FALSE;
dst->HH2 = q1->HH2 - q2->HH2; /* HH2 */
+
+ if (q1->HL3 < q2->HL3)
+ return FALSE;
dst->HL3 = q1->HL3 - q2->HL3; /* HL3 */
+
+ if (q1->LH3 < q2->LH3)
+ return FALSE;
dst->LH3 = q1->LH3 - q2->LH3; /* LH3 */
+
+ if (q1->HH3 < q2->HH3)
+ return FALSE;
dst->HH3 = q1->HH3 - q2->HH3; /* HH3 */
+
+ if (q1->LL3 < q2->LL3)
+ return FALSE;
dst->LL3 = q1->LL3 - q2->LL3; /* LL3 */
+
+ return TRUE;
}
static INLINE BOOL progressive_rfx_quant_lcmp_less_equal(const RFX_COMPONENT_CODEC_QUANT* q,
@@ -1433,9 +1464,12 @@ static INLINE int progressive_decompress_tile_upgrade(PROGRESSIVE_CONTEXT* progr
progressive_rfx_quant_add(quantY, quantProgY, &yBitPos);
progressive_rfx_quant_add(quantCb, quantProgCb, &cbBitPos);
progressive_rfx_quant_add(quantCr, quantProgCr, &crBitPos);
- progressive_rfx_quant_sub(&(tile->yBitPos), &yBitPos, &yNumBits);
- progressive_rfx_quant_sub(&(tile->cbBitPos), &cbBitPos, &cbNumBits);
- progressive_rfx_quant_sub(&(tile->crBitPos), &crBitPos, &crNumBits);
+ if (!progressive_rfx_quant_sub(&(tile->yBitPos), &yBitPos, &yNumBits))
+ goto fail;
+ if (!progressive_rfx_quant_sub(&(tile->cbBitPos), &cbBitPos, &cbNumBits))
+ goto fail;
+ if (!progressive_rfx_quant_sub(&(tile->crBitPos), &crBitPos, &crNumBits))
+ goto fail;
progressive_rfx_quant_add(quantY, quantProgY, &shiftY);
progressive_rfx_quant_lsub(&shiftY, 1); /* -6 + 5 = -1 */
progressive_rfx_quant_add(quantCb, quantProgCb, &shiftCb);
--
2.49.0

View File

@ -1,29 +0,0 @@
From 78677dc6e262f46937d00c3aa52381e4bb198fa5 Mon Sep 17 00:00:00 2001
From: Ondrej Holy <oholy@redhat.com>
Date: Mon, 13 Apr 2026 14:00:00 +0200
Subject: [PATCH] [codec,progressive] fix underflow guard in
progressive_rfx_quant_sub
Backport of commit 78677dc6e262f46937d00c3aa52381e4bb198fa5.
Made-with: Cursor
---
libfreerdp/codec/progressive.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/libfreerdp/codec/progressive.c b/libfreerdp/codec/progressive.c
index bbcc921..1234567 100644
--- a/libfreerdp/codec/progressive.c
+++ b/libfreerdp/codec/progressive.c
@@ -158,7 +158,7 @@ static INLINE BOOL progressive_rfx_quant_sub(const RFX_COMPONENT_CODEC_QUANT* q1
const RFX_COMPONENT_CODEC_QUANT* q2,
RFX_COMPONENT_CODEC_QUANT* dst)
{
- if (q1->HH1 < q2->HL1)
+ if (q1->HL1 < q2->HL1)
return FALSE;
dst->HL1 = q1->HL1 - q2->HL1; /* HL1 */
--
2.49.0

View File

@ -1,71 +0,0 @@
From 6a329a9a1352c9461a3a85e55d84230106b19500 Mon Sep 17 00:00:00 2001
From: Ondrej Holy <oholy@redhat.com>
Date: Tue, 10 Mar 2026 14:53:29 +0100
Subject: [PATCH] [core,info] fix missing NULL check
Backport of commit 4d44e3c097656a8b9ec696353647b0888ca45860.
Made-with: Cursor
---
libfreerdp/core/info.c | 17 +++++++++++------
1 file changed, 11 insertions(+), 6 deletions(-)
diff --git a/libfreerdp/core/info.c b/libfreerdp/core/info.c
index 9aaa6cff6..56676f0fb 100644
--- a/libfreerdp/core/info.c
+++ b/libfreerdp/core/info.c
@@ -24,6 +24,7 @@
#endif
#include <winpr/crt.h>
+#include <winpr/assert.h>
#include <freerdp/crypto/crypto.h>
#include <freerdp/log.h>
#include <freerdp/session.h>
@@ -1363,10 +1364,11 @@ 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)
{
UINT32 Size = 2 + 4 + 4 + 4 + 4 + 558;
- size_t domainLen, usernameLen;
+ size_t domainLen = 0;
+ size_t usernameLen = 0;
int len;
WCHAR* wString = NULL;
@@ -1376,11 +1378,13 @@ static BOOL rdp_write_logon_info_v2(wStream* s, logon_info* info)
Stream_Write_UINT16(s, SAVE_SESSION_PDU_VERSION_ONE);
Stream_Write_UINT32(s, Size);
Stream_Write_UINT32(s, info->sessionId);
- domainLen = strlen(info->domain);
+ if (info->domain)
+ domainLen = strlen(info->domain);
if (domainLen > UINT32_MAX)
return FALSE;
Stream_Write_UINT32(s, (UINT32)(domainLen + 1) * 2);
- usernameLen = strlen(info->username);
+ if (info->username)
+ usernameLen = strlen(info->username);
if (usernameLen > UINT32_MAX)
return FALSE;
Stream_Write_UINT32(s, (UINT32)(usernameLen + 1) * 2);
@@ -1457,10 +1461,11 @@ static BOOL rdp_write_logon_info_ex(wStream* s, logon_info_ex* info)
BOOL rdp_send_save_session_info(rdpContext* context, UINT32 type, void* data)
{
- wStream* s;
BOOL status;
+
+ WINPR_ASSERT(context);
rdpRdp* rdp = context->rdp;
- s = rdp_data_pdu_init(rdp);
+ wStream* s = rdp_data_pdu_init(rdp);
if (!s)
return FALSE;
--
2.53.0

View File

@ -1,37 +0,0 @@
From 9397fbeed4b2862ae68f9556950af415163c9398 Mon Sep 17 00:00:00 2001
From: Armin Novak <armin.novak@thincast.com>
Date: Tue, 7 Jul 2026 12:58:11 +0200
Subject: [PATCH] [core,message] fix update_message_WindowIcon
---
libfreerdp/core/message.c | 7 +++++--
1 file changed, 5 insertions(+), 2 deletions(-)
diff --git a/libfreerdp/core/message.c b/libfreerdp/core/message.c
index 7af29e94d..1ab0d4e4e 100644
--- a/libfreerdp/core/message.c
+++ b/libfreerdp/core/message.c
@@ -1147,18 +1147,21 @@ static BOOL update_message_WindowIcon(rdpContext* context, const WINDOW_ORDER_IN
if (!wParam)
return FALSE;
- CopyMemory(wParam, orderInfo, sizeof(WINDOW_ORDER_INFO));
+ *wParam = *orderInfo;
+
lParam = (WINDOW_ICON_ORDER*)calloc(1, sizeof(WINDOW_ICON_ORDER));
if (!lParam)
goto out_fail;
+ *lParam = *windowIcon;
lParam->iconInfo = calloc(1, sizeof(ICON_INFO));
if (!lParam->iconInfo)
goto out_fail;
- CopyMemory(lParam, windowIcon, sizeof(WINDOW_ICON_ORDER));
+ *lParam->iconInfo = *windowIcon->iconInfo;
+
WLog_VRB(TAG, "update_message_WindowIcon");
if (windowIcon->iconInfo->cbBitsColor > 0)

View File

@ -1,163 +0,0 @@
From cdabc78fc50a5a1859faebc1e41a3dfbbd007341 Mon Sep 17 00:00:00 2001
From: Armin Novak <armin.novak@thincast.com>
Date: Thu, 9 Jul 2026 08:34:24 +0200
Subject: [PATCH] [core,redirection] check redirection values for validity
Add some basic validity checks for data provided by server.
---
libfreerdp/core/redirection.c | 25 ++++++++++++++++++++-----
libfreerdp/core/utils.c | 33 +++++++++++++++++++++++++++++++++
libfreerdp/core/utils.h | 2 ++
3 files changed, 55 insertions(+), 5 deletions(-)
diff --git a/libfreerdp/core/redirection.c b/libfreerdp/core/redirection.c
index 59c6dbcee..b4b222eb2 100644
--- a/libfreerdp/core/redirection.c
+++ b/libfreerdp/core/redirection.c
@@ -406,6 +406,8 @@ static int rdp_recv_server_redirection_pdu(rdpRdp* rdp, wStream* s)
{
if (!rdp_redirection_read_unicode_string(s, &(redirection->TargetNetAddress), 80))
return -1;
+ if (!utils_is_valid_ip(redirection->TargetNetAddress))
+ return -1;
}
if (redirection->flags & LB_LOAD_BALANCE_INFO)
@@ -419,13 +421,17 @@ static int rdp_recv_server_redirection_pdu(rdpRdp* rdp, wStream* s)
if (!rdp_redirection_read_data(LB_LOAD_BALANCE_INFO, s, &redirection->LoadBalanceInfoLength,
&redirection->LoadBalanceInfo))
return -1;
+ if (!winpr_str_is_valid_urlN((const char*)redirection->LoadBalanceInfo,
+ redirection->LoadBalanceInfoLength))
+ return -1;
}
if (redirection->flags & LB_USERNAME)
{
if (!rdp_redirection_read_unicode_string(s, &(redirection->Username), 512))
return -1;
-
+ if (winpr_str_has_newlines(redirection->Username))
+ return -1;
WLog_DBG(TAG, "Username: %s", redirection->Username);
}
@@ -433,7 +439,8 @@ static int rdp_recv_server_redirection_pdu(rdpRdp* rdp, wStream* s)
{
if (!rdp_redirection_read_unicode_string(s, &(redirection->Domain), 52))
return -1;
-
+ if (winpr_str_has_newlines(redirection->Domain))
+ return -1;
WLog_DBG(TAG, "Domain: %s", redirection->Domain);
}
@@ -484,7 +491,8 @@ static int rdp_recv_server_redirection_pdu(rdpRdp* rdp, wStream* s)
{
if (!rdp_redirection_read_unicode_string(s, &(redirection->TargetFQDN), 512))
return -1;
-
+ if (!winpr_str_is_valid_url(redirection->TargetFQDN))
+ return -1;
WLog_DBG(TAG, "TargetFQDN: %s", redirection->TargetFQDN);
}
@@ -492,7 +500,8 @@ static int rdp_recv_server_redirection_pdu(rdpRdp* rdp, wStream* s)
{
if (!rdp_redirection_read_unicode_string(s, &(redirection->TargetNetBiosName), 32))
return -1;
-
+ if (!winpr_str_is_valid_url(redirection->TargetNetBiosName))
+ return -1;
WLog_DBG(TAG, "TargetNetBiosName: %s", redirection->TargetNetBiosName);
}
@@ -501,6 +510,8 @@ static int rdp_recv_server_redirection_pdu(rdpRdp* rdp, wStream* s)
if (!rdp_redirection_read_data(LB_CLIENT_TSV_URL, s, &redirection->TsvUrlLength,
&redirection->TsvUrl))
return -1;
+ if (!winpr_str_is_valid_urlN((const char*)redirection->TsvUrl, redirection->TsvUrlLength))
+ return -1;
}
if (redirection->flags & LB_REDIRECTION_GUID)
@@ -541,7 +552,8 @@ static int rdp_recv_server_redirection_pdu(rdpRdp* rdp, wStream* s)
{
if (!rdp_redirection_read_unicode_string(s, &(redirection->TargetNetAddresses[i]), 80))
return -1;
-
+ if (!utils_is_valid_ip(redirection->TargetNetAddresses[i]))
+ return -1;
WLog_DBG(TAG, "TargetNetAddresses[%" PRIuz "]: %s", i,
redirection->TargetNetAddresses[i]);
}
@@ -569,7 +581,10 @@ int rdp_recv_enhanced_security_redirection_packet(rdpRdp* rdp, wStream* s)
status = rdp_recv_server_redirection_pdu(rdp, s);
if (status < 0)
+ {
+ WLog_ERR(TAG, "redirection packet invalid, aborting");
return status;
+ }
if (Stream_GetRemainingLength(s) >= 1)
{
diff --git a/libfreerdp/core/utils.c b/libfreerdp/core/utils.c
index 139c1a433..102819915 100644
--- a/libfreerdp/core/utils.c
+++ b/libfreerdp/core/utils.c
@@ -22,6 +22,16 @@
#include "config.h"
#endif
+#include <string.h>
+
+#ifdef _WIN32
+#include <ws2tcpip.h>
+#else
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#endif
+
#include <winpr/assert.h>
#include <freerdp/freerdp.h>
@@ -65,3 +75,26 @@ BOOL utils_str_is_empty(const char* str)
return TRUE;
return FALSE;
}
+
+static BOOL isValidIPv4(const char* ipAddress)
+{
+ struct sockaddr_in sa;
+ int result;
+ memset(&sa, 0, sizeof(sa));
+ result = inet_pton(AF_INET, ipAddress, &(sa.sin_addr));
+ return result != 0;
+}
+
+static BOOL isValidIPv6(const char* ipAddress)
+{
+ struct sockaddr_in6 sa;
+ int result;
+ memset(&sa, 0, sizeof(sa));
+ result = inet_pton(AF_INET6, ipAddress, &(sa.sin6_addr));
+ return result != 0;
+}
+
+BOOL utils_is_valid_ip(const char* ipAddress)
+{
+ return isValidIPv4(ipAddress) || isValidIPv6(ipAddress);
+}
diff --git a/libfreerdp/core/utils.h b/libfreerdp/core/utils.h
index 432951812..4b150c416 100644
--- a/libfreerdp/core/utils.h
+++ b/libfreerdp/core/utils.h
@@ -30,4 +30,6 @@ BOOL utils_abort_connect(rdpContext* context);
BOOL utils_str_is_empty(const char* str);
BOOL utils_str_copy(const char* value, char** dst);
+FREERDP_LOCAL BOOL utils_is_valid_ip(const char* ipAddress);
+
#endif /* FREERDP_LIB_CORE_UTILS_H */

View File

@ -1,34 +0,0 @@
From b3cff65a1adbbdeda478f035218eeadc72ca0032 Mon Sep 17 00:00:00 2001
From: Armin Novak <armin.novak@thincast.com>
Date: Tue, 14 Jul 2026 12:30:27 +0200
Subject: [PATCH] [core,redirection] relax checks
* TsvUrl is opaque, do not assume any format
* LoadBalanceInfo is opaque, do not assume any format
---
libfreerdp/core/redirection.c | 5 -----
1 file changed, 5 deletions(-)
diff --git a/libfreerdp/core/redirection.c b/libfreerdp/core/redirection.c
index b4b222eb2..773496603 100644
--- a/libfreerdp/core/redirection.c
+++ b/libfreerdp/core/redirection.c
@@ -421,9 +421,6 @@ static int rdp_recv_server_redirection_pdu(rdpRdp* rdp, wStream* s)
if (!rdp_redirection_read_data(LB_LOAD_BALANCE_INFO, s, &redirection->LoadBalanceInfoLength,
&redirection->LoadBalanceInfo))
return -1;
- if (!winpr_str_is_valid_urlN((const char*)redirection->LoadBalanceInfo,
- redirection->LoadBalanceInfoLength))
- return -1;
}
if (redirection->flags & LB_USERNAME)
@@ -510,8 +507,6 @@ static int rdp_recv_server_redirection_pdu(rdpRdp* rdp, wStream* s)
if (!rdp_redirection_read_data(LB_CLIENT_TSV_URL, s, &redirection->TsvUrlLength,
&redirection->TsvUrl))
return -1;
- if (!winpr_str_is_valid_urlN((const char*)redirection->TsvUrl, redirection->TsvUrlLength))
- return -1;
}
if (redirection->flags & LB_REDIRECTION_GUID)

View File

@ -1,30 +0,0 @@
From 7e1759c739002dc95d8f15672d0f700ca8dcce0e Mon Sep 17 00:00:00 2001
From: Ondrej Holy <oholy@redhat.com>
Date: Tue, 17 Feb 2026 14:23:29 +0100
Subject: [PATCH] [crypto,base64] ensure char is singend
Backport of commit 62a9e787edb2cfce9858fa4ceda5461680efc590.
Co-Authored-By: Cursor <cursoragent@cursor.com>
---
libfreerdp/crypto/base64.c | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/libfreerdp/crypto/base64.c b/libfreerdp/crypto/base64.c
index 5a5da89a4..c084de38e 100644
--- a/libfreerdp/crypto/base64.c
+++ b/libfreerdp/crypto/base64.c
@@ -93,6 +93,10 @@ char* crypto_base64_encode(const BYTE* data, int length)
static int base64_decode_char(char c)
{
+ /* ensure char is signed for this check */
+ if ((int)c <= '\0')
+ return -1;
+
if (c >= 'A' && c <= 'Z')
return c - 'A';
--
2.52.0

View File

@ -1,50 +0,0 @@
From ef99da020599a666b7d171eec6ab527b47dd33f3 Mon Sep 17 00:00:00 2001
From: Ondrej Holy <oholy@redhat.com>
Date: Thu, 22 Jan 2026 12:47:42 +0100
Subject: [PATCH] [gdi,gfx] properly clamp SurfaceToSurface
Backport of commit c4a7c371342edf0d307cea728f56d3302f0ab38c.
Co-Authored-By: Claude <noreply@anthropic.com>
---
libfreerdp/gdi/gfx.c | 9 +++++----
1 file changed, 5 insertions(+), 4 deletions(-)
diff --git a/libfreerdp/gdi/gfx.c b/libfreerdp/gdi/gfx.c
index 3970715e0..968a5a17c 100644
--- a/libfreerdp/gdi/gfx.c
+++ b/libfreerdp/gdi/gfx.c
@@ -1175,7 +1175,6 @@ static UINT gdi_SurfaceToSurface(RdpgfxClientContext* context,
UINT status = ERROR_INTERNAL_ERROR;
UINT16 index;
BOOL sameSurface;
- UINT32 nWidth, nHeight;
const RECTANGLE_16* rectSrc;
RECTANGLE_16 invalidRect;
gdiGfxSurface* surfaceSrc;
@@ -1199,8 +1198,8 @@ static UINT gdi_SurfaceToSurface(RdpgfxClientContext* context,
if (!is_rect_valid(rectSrc, surfaceSrc->width, surfaceSrc->height))
goto fail;
- nWidth = rectSrc->right - rectSrc->left;
- nHeight = rectSrc->bottom - rectSrc->top;
+ const UINT32 nWidth = rectSrc->right - rectSrc->left;
+ const UINT32 nHeight = rectSrc->bottom - rectSrc->top;
for (index = 0; index < surfaceToSurface->destPtsCount; index++)
{
@@ -1209,8 +1208,10 @@ static UINT gdi_SurfaceToSurface(RdpgfxClientContext* context,
if (!is_rect_valid(&rect, surfaceDst->width, surfaceDst->height))
goto fail;
+ const UINT32 rwidth = rect.right - rect.left;
+ const UINT32 rheight = rect.bottom - rect.top;
if (!freerdp_image_copy(surfaceDst->data, surfaceDst->format, surfaceDst->scanline,
- destPt->x, destPt->y, nWidth, nHeight, surfaceSrc->data,
+ destPt->x, destPt->y, rwidth, rheight, surfaceSrc->data,
surfaceSrc->format, surfaceSrc->scanline, rectSrc->left,
rectSrc->top, NULL, FREERDP_FLIP_NONE))
goto fail;
--
2.52.0

View File

@ -1,36 +0,0 @@
From d25ffd608bf2bd38022a3930d0b3fc1b7c873c1a Mon Sep 17 00:00:00 2001
From: Ondrej Holy <oholy@redhat.com>
Date: Sat, 28 Mar 2026 12:13:06 +0100
Subject: [PATCH] [gdi,graphics] Use freerdp_glyph_convert_ex
Backport of commit 9f0eb3b7d43069a1e973464bcb43d1ef965ae65e.
Made-with: Cursor
---
libfreerdp/gdi/graphics.c | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/libfreerdp/gdi/graphics.c b/libfreerdp/gdi/graphics.c
index dab159c43..b1129fb7c 100644
--- a/libfreerdp/gdi/graphics.c
+++ b/libfreerdp/gdi/graphics.c
@@ -262,7 +262,6 @@ static BYTE* freerdp_glyph_convert_ex(UINT32 width, UINT32 height, const BYTE* d
static BOOL gdi_Glyph_New(rdpContext* context, const rdpGlyph* glyph)
{
- BYTE* data;
gdiGlyph* gdi_glyph;
if (!context || !glyph)
@@ -275,7 +274,7 @@ static BOOL gdi_Glyph_New(rdpContext* context, const rdpGlyph* glyph)
return FALSE;
gdi_glyph->hdc->format = PIXEL_FORMAT_MONO;
- data = freerdp_glyph_convert(glyph->cx, glyph->cy, glyph->aj);
+ BYTE* data = freerdp_glyph_convert_ex(glyph->cx, glyph->cy, glyph->aj, glyph->cb);
if (!data)
{
--
2.53.0

View File

@ -1,79 +0,0 @@
From 070f9e070c59a121cd93541835dc0086084bb6ad Mon Sep 17 00:00:00 2001
From: Ondrej Holy <oholy@redhat.com>
Date: Tue, 17 Feb 2026 11:05:59 +0100
Subject: [PATCH] [utils,smartcard] add length validity checks
Backport of commit 57c5647d98c2a026de8b681159cb188ca0439ef8.
Co-Authored-By: Cursor <cursoragent@cursor.com>
---
channels/smartcard/client/smartcard_pack.c | 27 +++++++++++++++++-----
1 file changed, 21 insertions(+), 6 deletions(-)
diff --git a/channels/smartcard/client/smartcard_pack.c b/channels/smartcard/client/smartcard_pack.c
index f70eb4e5d..687f1110b 100644
--- a/channels/smartcard/client/smartcard_pack.c
+++ b/channels/smartcard/client/smartcard_pack.c
@@ -98,13 +98,16 @@ static BOOL smartcard_ndr_pointer_read_(wStream* s, UINT32* index, UINT32* ptr,
return TRUE;
}
-static LONG smartcard_ndr_read(wStream* s, BYTE** data, size_t min, size_t elementSize,
- ndr_ptr_t type)
+static LONG smartcard_ndr_read_ex(wStream* s, BYTE** data, size_t min,
+ size_t elementSize, ndr_ptr_t type, size_t* plen)
{
size_t len, offset, len2;
void* r;
size_t required;
+ if (plen)
+ *plen = 0;
+
switch (type)
{
case NDR_PTR_FULL:
@@ -181,11 +184,20 @@ static LONG smartcard_ndr_read(wStream* s, BYTE** data, size_t min, size_t eleme
if (!r)
return SCARD_E_NO_MEMORY;
Stream_Read(s, r, len);
- smartcard_unpack_read_size_align(NULL, s, len, 4);
+ const LONG pad = smartcard_unpack_read_size_align(NULL, s, len, 4);
+ len += (size_t)pad;
*data = r;
+ if (plen)
+ *plen = len;
return STATUS_SUCCESS;
}
+static LONG smartcard_ndr_read(wStream* s, BYTE** data, size_t min, size_t elementSize,
+ ndr_ptr_t type)
+{
+ return smartcard_ndr_read_ex(s, data, min, elementSize, type, NULL);
+}
+
static BOOL smartcard_ndr_pointer_write(wStream* s, UINT32* index, DWORD length)
{
const UINT32 ndrPtr = 0x20000 + (*index) * 4;
@@ -3427,12 +3439,15 @@ LONG smartcard_unpack_set_attrib_call(SMARTCARD_DEVICE* smartcard, wStream* s, S
if (ndrPtr)
{
- // TODO: call->cbAttrLen was larger than the pointer value.
- // TODO: Maybe need to refine the checks?
- status = smartcard_ndr_read(s, &call->pbAttr, 0, 1, NDR_PTR_SIMPLE);
+ size_t len = 0;
+ status = smartcard_ndr_read_ex(s, &call->pbAttr, 0, 1, NDR_PTR_SIMPLE, &len);
if (status != SCARD_S_SUCCESS)
return status;
+ if (call->cbAttrLen > len)
+ call->cbAttrLen = (DWORD)len;
}
+ else
+ call->cbAttrLen = 0;
smartcard_trace_set_attrib_call(smartcard, call);
return SCARD_S_SUCCESS;
}
--
2.52.0

View File

@ -0,0 +1,66 @@
From ddc6dacd06b41ed5001b1c884b5d5c9e0a70e275 Mon Sep 17 00:00:00 2001
From: akallabeth <akallabeth@posteo.net>
Date: Thu, 10 Nov 2022 15:54:28 +0100
Subject: [PATCH] [winpr, crt] Added wcsstr implementation
(cherry picked from commit 6c034ba6117a4efc9266e845fe9a9a92ed4ee61d)
---
winpr/include/winpr/string.h | 3 +++
winpr/libwinpr/crt/string.c | 20 ++++++++++++++++++++
2 files changed, 23 insertions(+)
diff --git a/winpr/include/winpr/string.h b/winpr/include/winpr/string.h
index 3b907c444..2d7126210 100644
--- a/winpr/include/winpr/string.h
+++ b/winpr/include/winpr/string.h
@@ -62,6 +62,8 @@ extern "C"
WINPR_API size_t _wcslen(const WCHAR* str);
WINPR_API size_t _wcsnlen(const WCHAR* str, size_t maxNumberOfElements);
+ WINPR_API WCHAR* _wcsstr(const WCHAR* str, const WCHAR* strSearch);
+
WINPR_API WCHAR* _wcschr(const WCHAR* str, WCHAR c);
WINPR_API WCHAR* _wcsrchr(const WCHAR* str, WCHAR c);
@@ -74,6 +76,7 @@ extern "C"
#define _wcsncmp wcsncmp
#define _wcslen wcslen
#define _wcsnlen wcsnlen
+#define _wcsstr wcsstr
#define _wcschr wcschr
#define _wcsrchr wcsrchr
diff --git a/winpr/libwinpr/crt/string.c b/winpr/libwinpr/crt/string.c
index 5dcf4b3f1..efd7d166c 100644
--- a/winpr/libwinpr/crt/string.c
+++ b/winpr/libwinpr/crt/string.c
@@ -147,6 +147,26 @@ size_t _wcsnlen(const WCHAR* str, size_t max)
return x;
}
+/* _wcsstr -> wcsstr */
+
+WCHAR* _wcsstr(const WCHAR* str, const WCHAR* strSearch)
+{
+ assert(str);
+ assert(strSearch);
+
+ if (strSearch[0] == '\0')
+ return str;
+
+ const size_t searchLen = _wcslen(strSearch);
+ while (*str)
+ {
+ if (_wcsncmp(str, strSearch, searchLen) == 0)
+ return str;
+ str++;
+ }
+ return NULL;
+}
+
/* _wcschr -> wcschr */
WCHAR* _wcschr(const WCHAR* str, WCHAR c)
--
2.37.1

View File

@ -0,0 +1,90 @@
From fb9d753af70b449dd7a17898d46fd57822a08dc1 Mon Sep 17 00:00:00 2001
From: akallabeth <akallabeth@posteo.net>
Date: Thu, 10 Nov 2022 14:21:22 +0100
Subject: [PATCH] [winpr, crt] Fix wcs*cmp and wcs*len checks
(cherry picked from commit b60fac1a0470fe83e8d0b448f0fd7e9e6d6a0f96)
---
winpr/libwinpr/crt/string.c | 30 +++++++++++++++++++-----------
1 file changed, 19 insertions(+), 11 deletions(-)
diff --git a/winpr/libwinpr/crt/string.c b/winpr/libwinpr/crt/string.c
index c25ffa279..5dcf4b3f1 100644
--- a/winpr/libwinpr/crt/string.c
+++ b/winpr/libwinpr/crt/string.c
@@ -26,6 +26,7 @@
#include <wctype.h>
#include <winpr/crt.h>
+#include <assert.h>
#include <winpr/endian.h>
/* String Manipulation (CRT): http://msdn.microsoft.com/en-us/library/f0151s4x.aspx */
@@ -80,21 +81,28 @@ int _strnicmp(const char* string1, const char* string2, size_t count)
int _wcscmp(const WCHAR* string1, const WCHAR* string2)
{
- WCHAR value1, value2;
+ assert(string1);
+ assert(string2);
- while (*string1 && (*string1 == *string2))
+ while (TRUE)
{
- string1++;
- string2++;
+ const WCHAR w1 = *string1++;
+ const WCHAR w2 = *string2++;
+
+ if (w1 != w2)
+ return (int)w1 - w2;
+ else if ((w1 == '\0') || (w2 == '\0'))
+ return (int)w1 - w2;
}
- Data_Read_UINT16(string1, value1);
- Data_Read_UINT16(string2, value2);
- return (int)value1 - value2;
+ return 0;
}
int _wcsncmp(const WCHAR* string1, const WCHAR* string2, size_t count)
{
+ assert(string1);
+ assert(string2);
+
for (size_t x = 0; x < count; x++)
{
const WCHAR a = string1[x];
@@ -102,6 +110,8 @@ int _wcsncmp(const WCHAR* string1, const WCHAR* string2, size_t count)
if (a != b)
return (int)a - b;
+ else if ((a == '\0') || (b == '\0'))
+ return (int)a - b;
}
return 0;
}
@@ -112,8 +122,7 @@ size_t _wcslen(const WCHAR* str)
{
const WCHAR* p = (const WCHAR*)str;
- if (!p)
- return 0;
+ assert(p);
while (*p)
p++;
@@ -127,8 +136,7 @@ size_t _wcsnlen(const WCHAR* str, size_t max)
{
size_t x;
- if (!str)
- return 0;
+ assert(str);
for (x = 0; x < max; x++)
{
--
2.37.1

View File

@ -1,266 +0,0 @@
From a7a47bc0bdab723b674fb05f51e93ae007ed6412 Mon Sep 17 00:00:00 2001
From: Armin Novak <armin.novak@thincast.com>
Date: Thu, 9 Jul 2026 08:34:23 +0200
Subject: [PATCH] [winpr,crt] add functions to test string
* winpr_str_has_newlines to check for newlines in a string
* winpr_str_is_valid_url and winpr_str_is_valid_urlN to check if a
string is a valid url
---
winpr/include/winpr/string.h | 5 ++
winpr/libwinpr/crt/CMakeLists.txt | 8 +++
winpr/libwinpr/crt/string.c | 96 ++++++++++++++++++++++++++++
winpr/libwinpr/crt/test/TestString.c | 82 ++++++++++++++++++++++++
4 files changed, 191 insertions(+)
diff --git a/winpr/include/winpr/string.h b/winpr/include/winpr/string.h
index bb98dffdc..7de9f934f 100644
--- a/winpr/include/winpr/string.h
+++ b/winpr/include/winpr/string.h
@@ -31,6 +31,11 @@ extern "C"
{
#endif
+ WINPR_API BOOL winpr_str_is_valid_url(const char* str);
+ WINPR_API BOOL winpr_str_is_valid_urlN(const char* str, size_t len);
+ WINPR_API BOOL winpr_str_has_newlines(const char* str);
+
+
WINPR_API BOOL winpr_str_append(const char* what, char* buffer, size_t size,
const char* separator);
diff --git a/winpr/libwinpr/crt/CMakeLists.txt b/winpr/libwinpr/crt/CMakeLists.txt
index 5ee4bbd1e..538e74dcf 100644
--- a/winpr/libwinpr/crt/CMakeLists.txt
+++ b/winpr/libwinpr/crt/CMakeLists.txt
@@ -22,6 +22,14 @@ set (CRT_FILES alignment.c
unicode.c
string.c)
+include(CheckSymbolExists)
+check_symbol_exists(regcomp regex.h WINPR_HAVE_REGCOMP)
+check_symbol_exists(regexec regex.h WINPR_HAVE_REGEXEC)
+check_symbol_exists(regfree regex.h WINPR_HAVE_REGFREE)
+if(WINPR_HAVE_REGCOMP AND WINPR_HAVE_REGEXEC AND WINPR_HAVE_REGFREE)
+ winpr_definition_add(-DWINPR_HAVE_REGCOMP)
+endif()
+
if (NOT WITH_ICU)
set (CRT_FILES ${CRT_FILES}
utf.c
diff --git a/winpr/libwinpr/crt/string.c b/winpr/libwinpr/crt/string.c
index 6596b42f5..465b8d133 100644
--- a/winpr/libwinpr/crt/string.c
+++ b/winpr/libwinpr/crt/string.c
@@ -29,11 +29,71 @@
#include <winpr/assert.h>
#include <winpr/endian.h>
+#if defined(WINPR_HAVE_REGCOMP)
+#include <regex.h>
+#endif
+
/* String Manipulation (CRT): http://msdn.microsoft.com/en-us/library/f0151s4x.aspx */
#include "../log.h"
#define TAG WINPR_TAG("crt")
+BOOL winpr_str_is_valid_urlN(const char* str, size_t len)
+{
+ if (!str || (len == 0))
+ return FALSE;
+
+ char* url = strndup(str, len);
+ if (!url)
+ return FALSE;
+
+ const BOOL rc = winpr_str_is_valid_url(url);
+ free(url);
+ return rc;
+}
+
+#if defined(_WIN32)
+#include <shlwapi.h>
+
+BOOL winpr_str_is_valid_url(const char* str)
+{
+ if (!str)
+ return FALSE;
+ return PathIsURLA(str);
+}
+
+#elif defined(WINPR_HAVE_REGCOMP)
+BOOL winpr_str_is_valid_url(const char* str)
+{
+ regex_t regex;
+ const char pattern[] = "^([a-z]+://)?([a-zA-Z0-9.-]+)(:[0-9]+)?(/[a-zA-Z0-9./?=&%-]*)?$";
+ int ret;
+
+ if (!str)
+ return FALSE;
+ memset(&regex, 0, sizeof(regex));
+
+ ret = regcomp(&regex, pattern, REG_EXTENDED);
+ if (ret)
+ {
+ printf("Could not compile regex\n");
+ return FALSE;
+ }
+
+ ret = regexec(&regex, str, 0, NULL, 0);
+ regfree(&regex);
+ return ret == 0;
+}
+
+#else
+BOOL winpr_str_is_valid_url(const char* str)
+{
+ WINPR_UNUSED(str);
+ WLog_WARN(TAG, "URL validation not supported by your build, not performing check.");
+ return TRUE;
+}
+#endif
+
BOOL winpr_str_append(const char* what, char* buffer, size_t size, const char* separator)
{
const size_t used = strnlen(buffer, size);
@@ -636,3 +696,39 @@ INT64 GetLine(char** lineptr, size_t* size, FILE* stream)
return -1;
#endif
}
+
+BOOL winpr_str_has_newlines(const char* str)
+{
+ if (!str)
+ return FALSE;
+ do
+ {
+ char c = *str++;
+ switch (c)
+ {
+ case '\r':
+ case '\n':
+ case 0x0b: /* VT vertical tab */
+ case 0x0c: /* FF form feed */
+ case (char)0x85: /* NEL next line */
+ return TRUE;
+ case 0x20:
+ {
+ switch (*str)
+ {
+ case 0x28: /* LS line separator */
+ case 0x29: /* PS paragraph separator */
+ return TRUE;
+ default:
+ break;
+ }
+ }
+ break;
+
+ case '\0':
+ return FALSE;
+ default:
+ break;
+ }
+ } while (1);
+}
diff --git a/winpr/libwinpr/crt/test/TestString.c b/winpr/libwinpr/crt/test/TestString.c
index df154fe1c..bca470bdd 100644
--- a/winpr/libwinpr/crt/test/TestString.c
+++ b/winpr/libwinpr/crt/test/TestString.c
@@ -27,6 +27,79 @@ static WCHAR testDelimiter[] = { '\r', '\n', '\0' };
#define testDelimiter_Length ((sizeof(testDelimiter) / sizeof(WCHAR)) - 1)
+static BOOL test_valid_url(void)
+{
+ struct test_t
+ {
+ char* string;
+ size_t len;
+ BOOL isUrl;
+ };
+
+ static const struct test_t tests[] = { { "foo.bar.blabla", 15, TRUE },
+ { "somehostname", 12, TRUE },
+ { "somehostname:1234", 17, TRUE },
+ { "tsv://somehostname", 18, TRUE },
+ { "tsv://somehostname/path/foo/gaga", 32, TRUE },
+ { "tsv://somehostname:1234", 23, TRUE },
+ { "tsv://somehostname:1234/path/foo", 33, TRUE },
+ { "lala\rgaga\n", 12, FALSE },
+ { "192.168.0.1", 11, TRUE },
+ //{ "192.168.0.1:1234", 16, FALSE },
+ //{ "192.168.0.1:1234/foo/", 21, FALSE },
+ { "192.168.0.1/bar/foo/", 20, TRUE },
+ { "[::1]", 5, FALSE },
+ { "[::1]:1234", 8, FALSE },
+ { "[2001:db8:3333:4444:5555:6666:7777:8888]", 40,
+ FALSE },
+ { "[2001:db8::1]", 13, FALSE } };
+ BOOL rc = TRUE;
+ for (size_t x = 0; x < ARRAYSIZE(tests); x++)
+ {
+ const struct test_t* cur = &tests[x];
+
+ const BOOL rc1 = winpr_str_is_valid_url(cur->string);
+ const BOOL rc2 = winpr_str_is_valid_urlN(cur->string, cur->len);
+
+#if defined(WINPR_HAVE_REGCOMP) || defined(WITH_URIPARSER)
+ if (rc1 != rc2)
+ rc = FALSE;
+ if (rc1 != cur->isUrl)
+ rc = FALSE;
+#else
+ fprintf(stderr, "[%s] TODO: !defined(WINPR_HAVE_REGCOMP) && !defined(WITH_URIPARSER)\n",
+ __func__);
+#endif
+ }
+
+ return rc;
+}
+
+static BOOL test_newline(void)
+{
+ struct test_t
+ {
+ char* string;
+ size_t len;
+ BOOL hasNewlines;
+ };
+
+ const struct test_t tests[] = { { "foo.bar.blabla", 15, FALSE },
+ { "somehostname", 12, FALSE },
+ { "lala\rgaga\n", 13, TRUE } };
+
+ for (size_t x = 0; x < ARRAYSIZE(tests); x++)
+ {
+ const struct test_t* cur = &tests[x];
+
+ const BOOL rc1 = winpr_str_has_newlines(cur->string);
+ if (rc1 != cur->hasNewlines)
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
int TestString(int argc, char* argv[])
{
WCHAR* p;
@@ -34,6 +107,15 @@ int TestString(int argc, char* argv[])
size_t length;
WCHAR* context;
+ WINPR_UNUSED(argc);
+ WINPR_UNUSED(argv);
+
+ if (!test_valid_url())
+ return -1;
+
+ if (!test_newline())
+ return -1;
+
#ifdef __BIG_ENDIAN__
/* Be sure that we always use LE encoded string */
ByteSwapUnicode(testStringW, testStringW_Length);

View File

@ -1,13 +1,10 @@
# Can be rebuilt with FFmpeg/OpenH264 support enabled by passing
# "--with=ffmpeg", or "--with=openh264" to mock/rpmbuild; or by globally
# setting these variables:
# https://bugzilla.redhat.com/show_bug.cgi?id=2242028
#global _with_ffmpeg 1
#global _with_openh264 1
# Can be rebuilt with FFmpeg/H264 support enabled by passing "--with=ffmpeg",
# "--with=x264" or "--with=openh264" to mock/rpmbuild; or by globally setting
# these variables:
# Can be rebuilt with OpenCL support enabled by passing # "--with=opencl"
# or by globally setting:
#global _opencl 1
#global _with_ffmpeg 1
#global _with_x264 1
#global _with_openh264 1
# Momentarily disable GSS support
# https://github.com/FreeRDP/FreeRDP/issues/4348
@ -15,9 +12,7 @@
# Disable server support in RHEL
# https://bugzilla.redhat.com/show_bug.cgi?id=1639165
%if 0%{?fedora} || 0%{?rhel} >= 10
%global _with_server 1
%endif
%{!?rhel:%global _with_server 1}
# Disable support for missing codecs in RHEL
%{!?rhel:%global _with_soxr 1}
@ -26,8 +21,8 @@
%endif
Name: freerdp
Version: 2.11.7
Release: 11%{?dist}
Version: 2.2.0
Release: 10%{?dist}
Epoch: 2
Summary: Free implementation of the Remote Desktop Protocol (RDP)
License: ASL 2.0
@ -35,185 +30,37 @@ URL: http://www.freerdp.com/
Source0: https://github.com/FreeRDP/FreeRDP/archive/%{version}/FreeRDP-%{version}.tar.gz
# Revert changes that break API
# https://issues.redhat.com/browse/RHEL-53081
Patch0: Revert-Moved-clipboard-utils-to-core-library-fixes-6.patch
# https://bugzilla.redhat.com/show_bug.cgi?id=1910029
Patch0: 6741.patch
# https://github.com/FreeRDP/FreeRDP/commit/c4a7c371342edf0d307cea728f56d3302f0ab38c
Patch1: gdi-gfx-properly-clamp-SurfaceToSurface.patch
# CVE-2021-41160 freerdp: improper region checks in all clients allow out of bound write to memory
Patch1: Add-checks-for-bitmap-and-glyph-width-heigth-values.patch
# https://github.com/FreeRDP/FreeRDP/commit/c4391827d7facfc874ca7f61a92afb82232a5748
Patch2: codec-clear-fix-clear_resize_buffer-checks.patch
# CVE-2021-41159 freerdp: improper client input validation for gateway connections allows to overwrite memory
Patch2: Implement-missing-TSG-debug-functions.patch
Patch3: Refactored-RPC-gateway-parser.patch
# https://github.com/FreeRDP/FreeRDP/commit/f8688b57f6cfad9a0b05475a6afbde355ffab720
Patch3: codec-clear-fix-off-by-one-length-check.patch
# https://bugzilla.redhat.com/show_bug.cgi?id=2108866
Patch4: Fix-monitor-list-flag-being-ignored-on-X11-Wayland.patch
# https://github.com/FreeRDP/FreeRDP/commit/1bab198a2edd0d0e6e1627d21a433151ea190500
Patch4: codec-planar-fix-decoder-length-checks.patch
# https://bugzilla.redhat.com/show_bug.cgi?id=2136151
Patch5: Fix-length-checks-in-parallel-driver.patch
# https://github.com/FreeRDP/FreeRDP/commit/243ecf804bb122e8e643a5c142ad5a49d7aa19ee
Patch5: codec-clear-check-clear_decomress-glyphData.patch
# https://bugzilla.redhat.com/show_bug.cgi?id=2136153
Patch6: Fixed-missing-length-check-in-video-channel.patch
# https://github.com/FreeRDP/FreeRDP/commit/0421b53fcb4a80c95f51342e4a2c40c68a4101d3
Patch6: client-x11-fix-double-free-in-case-of-invalid-pointe.patch
# https://github.com/FreeRDP/FreeRDP/commit/52106a26726a2aba77aa6d86014d2eb3507f0783
Patch7: cache-offscreen-invalidate-bitmap-before-free.patch
# CVE-2026-22855
# https://github.com/FreeRDP/FreeRDP/commit/57c5647d98c2a026de8b681159cb188ca0439ef8
Patch8: utils-smartcard-add-length-validity-checks.patch
# CVE-2026-22858
# https://github.com/FreeRDP/FreeRDP/commit/62a9e787edb2cfce9858fa4ceda5461680efc590
Patch9: crypto-base64-ensure-char-is-singend.patch
# CVE-2026-22859
# https://github.com/FreeRDP/FreeRDP/commit/7b7e6de8fe427a2f01d331056774aec69710590b
Patch10: channels-urbdrc-check-interface-indices-before-use.patch
# CVE-2026-26955
# https://github.com/FreeRDP/FreeRDP/commit/7d8fdce2d0ef337cb86cb37fc0c436c905e04d77
Patch11: codec-clear-fix-destination-checks.patch
# CVE-2026-26965
# https://github.com/FreeRDP/FreeRDP/commit/a0be5cb87d760bb1c803ad1bb835aa1e73e62abc
Patch12: codec-planar-fix-missing-destination-bounds-checks.patch
# CVE-2026-22852
# https://github.com/FreeRDP/FreeRDP/commit/cd1ffa112cfbe1b40a9fd57e299a8ea12e23df0d
Patch13: channels-audin-free-up-old-audio-formats.patch
# CVE-2026-22854
# https://github.com/FreeRDP/FreeRDP/commit/3da319570c8a6be0a79b3306f1ed354c4a943259
Patch14: channels-drive-fix-constant-type.patch
# CVE-2026-22856
# https://github.com/FreeRDP/FreeRDP/commit/b35aa3614d32bff3fc1272cd7c4617f711fca1a4
# https://github.com/FreeRDP/FreeRDP/commit/675c20f08f32ca5ec06297108bdf30147d6e2cd9
Patch15: channels-serial-lock-list-dictionary.patch
Patch16: channels-serial-explicitly-lock-serial-IrpThreads.patch
# CVE-2026-23732
# https://github.com/FreeRDP/FreeRDP/commit/3bc1eeb4f63ceec9a696af194e4c1ea0e67ff60c
# https://github.com/FreeRDP/FreeRDP/commit/9f0eb3b7d43069a1e973464bcb43d1ef965ae65e
Patch17: codec-color-add-freerdp_glyph_convert_ex.patch
Patch18: gdi-graphics-Use-freerdp_glyph_convert_ex.patch
# CVE-2026-23948
# https://github.com/FreeRDP/FreeRDP/commit/4d44e3c097656a8b9ec696353647b0888ca45860
Patch19: core-info-fix-missing-NULL-check.patch
# CVE-2026-24491
# https://github.com/FreeRDP/FreeRDP/commit/e02e052f6692550e539d10f99de9c35a23492db2
# https://github.com/FreeRDP/FreeRDP/commit/635ae3c8193256db01774fab5ff11bcae57aed6b
# https://github.com/FreeRDP/FreeRDP/commit/e01cd85c8003a245ef9778f0eda4b9235514c201
Patch20: channels-drdynvc-reset-channel_callback-before-close.patch
Patch21: channels-video-unify-error-handling.patch
Patch22: channels-video-fix-wrong-cast.patch
# CVE-2026-24675
# https://github.com/FreeRDP/FreeRDP/commit/d676518809c319eec15911c705c13536036af2ae
Patch23: channels-urbdrc-do-not-free-MsConfig-on-failure.patch
# CVE-2026-24676
# https://github.com/FreeRDP/FreeRDP/commit/026b81ae5831ac1598d8f7371e0d0996fac7db00
Patch24: channels-audin-reset-audin-format.patch
# CVE-2026-24679
# https://github.com/FreeRDP/FreeRDP/commit/2d563a50be17c1b407ca448b1321378c0726dd31
Patch25: channels-urbdrc-ensure-InterfaceNumber-is-within-ran.patch
# CVE-2026-24681
# https://github.com/FreeRDP/FreeRDP/commit/414f701464929c217f2509bcbd6d2c1f00f7ed73
Patch26: channels-urbdrc-cancel-all-usb-transfers-on-channel-.patch
# CVE-2026-24683
# https://github.com/FreeRDP/FreeRDP/commit/d9ca272dce7a776ab475e9b1a8e8c3d2968c8486
Patch27: channels-ainput-lock-context-when-updating-listener.patch
# CVE-2026-24684
# https://github.com/FreeRDP/FreeRDP/commit/622bb7b4402491ca003f47472d0e478132673696
# https://github.com/FreeRDP/FreeRDP/commit/afa6851dc80835d3101e40fcef51b6c5c0f43ea5
Patch28: channels-rdpsnd-terminate-thread-before-free.patch
Patch29: channel-rdpsnd-only-clean-up-thread-before-free.patch
# CVE-2026-31806
# https://github.com/FreeRDP/FreeRDP/commit/83d9aedea278a74af3e490ff5eeb889c016dbb2b
# https://github.com/FreeRDP/FreeRDP/commit/169971607cece48384cb94632b829bd57336af0f
Patch30: codec-nsc-limit-copy-area-in-nsc_process_message.patch
Patch31: codec-nsc-fix-use-of-nsc_process_message.patch
# CVE-2026-33984
# https://github.com/FreeRDP/FreeRDP/commit/a2dde6d9832cb032e8cf12cab3da84dafbab9006
Patch32: codec-clear-update-CLEAR_VBAR_ENTRY-size-after-alloc.patch
# CVE-2026-33983
# https://github.com/FreeRDP/FreeRDP/commit/78188ab479c8e6eb9ba2475b3732c76b4bbe5425
# https://github.com/FreeRDP/FreeRDP/commit/78677dc6e262f46937d00c3aa52381e4bb198fa5
Patch33: codec-progressive-fail-progressive_rfx_quant_sub-on-invalid-values.patch
Patch34: codec-progressive-fix-underflow-guard-in-progressive_rfx_quant_sub.patch
# CVE-2026-26986
# https://github.com/FreeRDP/FreeRDP/commit/b4f0f0a18fe53aa8d47d062f91471f4e9c5e0d51
Patch35: client-x11-fix-xf_rail_window_common-cleanup.patch
# CVE-2026-27951
# https://github.com/FreeRDP/FreeRDP/commit/118afc0b954ba9d5632b7836ad24e454555ed113
Patch36: allocations-fix-growth-of-preallocated-buffers.patch
# CVE-2026-29775
# https://github.com/FreeRDP/FreeRDP/commit/ffad58fd2b329efd81a3239e9d7e3c927b8e503f
# https://github.com/FreeRDP/FreeRDP/commit/8270e0bb3d6726c947d57c93ba9caa92a052b557
Patch37: cache-bitmap-overallocate-bitmap-cache.patch
Patch38: cache-bitmap-initialize-overallocated-bitmap-cache-extra-slot.patch
# CVE-2026-31884
# https://github.com/FreeRDP/FreeRDP/commit/03b48b3601d867afccac1cdc6081de7a275edce7
Patch39: codec-dsp-add-format-checks.patch
# CVE-2026-31883
# CVE-2026-31885
# https://github.com/FreeRDP/FreeRDP/commit/16df2300e1e3f5a51f68fb1626429e58b531b7c8
Patch40: codec-dsp-fix-array-bounds-checks.patch
# CVE-2026-33985
# https://github.com/FreeRDP/FreeRDP/commit/c49d1ad43b8c7b32794d0250f2623c2dccd7ef25
Patch41: codec-clear-update-clear_glyph_entry-count-after-alloc.patch
# CVE-2026-25952
# https://github.com/FreeRDP/FreeRDP/commit/1994e9844212a6dfe0ff12309fef520e888986b5
# https://github.com/FreeRDP/FreeRDP/commit/78fd7f580d5f9e6d9d582d82e5ea96003844fbdf
# https://github.com/FreeRDP/FreeRDP/commit/4ff57b68c2960fa414d03c78ff0e0660be1cc5bd
# https://github.com/FreeRDP/FreeRDP/commit/a278ff74117444c635c50ffa5084ecf517171f5a
Patch42: client-x11-lock-appwindow.patch
Patch43: client-x11-improve-rails-window-locking.patch
Patch44: client-x11-refactor-locking.patch
Patch45: client-x11-fix-deadlock-on-output-expose.patch
# CVE-2026-45700
# https://github.com/FreeRDP/FreeRDP/commit/4a065a941ae134a0433d1497c03b3c3eb91b9f85
Patch46: codec-planar-fix-bounds-checks.patch
# CVE-2026-64624
# https://github.com/FreeRDP/FreeRDP/commit/22c5deea52404f51a13276b3abda44e1e60704cf
Patch47: client-common-deactivate-cli-parsing-in-rdp-files.patch
# CVE-2026-67289
# https://github.com/FreeRDP/FreeRDP/commit/f163b2d2080d922cb725800781f43f16bc2882c0
# https://github.com/FreeRDP/FreeRDP/commit/ab5905cbada0d34cf85ffd32881cacaf8275744f
# https://github.com/FreeRDP/FreeRDP/commit/1db16b4b121a1221c0454f7d223096cac87bf244
Patch48: winpr-crt-add-functions-to-test-string.patch
Patch49: core-redirection-check-redirection-values-for-validity.patch
Patch50: core-redirection-relax-checks.patch
# CVE-2026-68580
# https://github.com/FreeRDP/FreeRDP/commit/3bf07d4470bbe42faf4ef8197c8fe1b6b759b00f
Patch51: channels-audin-limit-FramesPerPacket.patch
# CVE-2026-67299
# https://github.com/FreeRDP/FreeRDP/commit/172edbddd24f33033b9eafae6ea1fff0641a24f4
Patch52: core-message-fix-update_message_WindowIcon.patch
# https://bugzilla.redhat.com/show_bug.cgi?id=2145139
Patch7: Added-missing-length-checks-in-zgfx_decompress_segme.patch
Patch8: Fixed-division-by-zero-in-urbdrc.patch
Patch9: Added-missing-length-check-in-urb_control_transfer.patch
Patch10: Fixed-missing-input-buffer-length-check-in-urbdrc.patch
Patch11: Ensure-urb_create_iocompletion-uses-size_t-for-calcu.patch
Patch12: Added-function-_wcsncmp.patch
Patch13: winpr-crt-Fix-wcs-cmp-and-wcs-len-checks.patch
Patch14: winpr-crt-Added-wcsstr-implementation.patch
Patch15: Fixed-path-validation-in-drive-channel.patch
Patch16: Fixed-missing-stream-length-check-in-drive_file_quer.patch
Patch17: Fixed-format-string-for-Stream_CheckAndLogRequiredLe.patch
BuildRequires: gcc
BuildRequires: gcc-c++
@ -234,8 +81,6 @@ BuildRequires: libxkbfile-devel
BuildRequires: libXrandr-devel
%{?_with_server:BuildRequires: libXtst-devel}
BuildRequires: libXv-devel
%{?_with_opencl:BuildRequires: opencl-headers >= 3.0}
%{?_with_opencl:BuildRequires: ocl-icd-devel}
%{?_with_openh264:BuildRequires: openh264-devel}
%{?_with_x264:BuildRequires: x264-devel}
%{?_with_server:BuildRequires: pam-devel}
@ -244,6 +89,16 @@ BuildRequires: zlib-devel
BuildRequires: multilib-rpm-config
BuildRequires: pkgconfig(cairo)
BuildRequires: pkgconfig(dbus-1)
BuildRequires: pkgconfig(dbus-glib-1)
BuildRequires: pkgconfig(glib-2.0)
BuildRequires: pkgconfig(gstreamer-1.0)
BuildRequires: pkgconfig(gstreamer-base-1.0)
BuildRequires: pkgconfig(gstreamer-app-1.0)
BuildRequires: pkgconfig(gstreamer-audio-1.0)
BuildRequires: pkgconfig(gstreamer-fft-1.0)
BuildRequires: pkgconfig(gstreamer-pbutils-1.0)
BuildRequires: pkgconfig(gstreamer-video-1.0)
%{?_with_gss:BuildRequires: pkgconfig(krb5) >= 1.13}
BuildRequires: pkgconfig(libpcsclite)
BuildRequires: pkgconfig(libpulse)
@ -336,21 +191,20 @@ find . -name "*.c" -exec chmod 664 {} \;
%cmake %{?_cmake_skip_rpath} \
-DCMAKE_INSTALL_LIBDIR:PATH=%{_lib} \
-DWITH_ALSA=ON \
-DWITH_CAIRO=ON \
-DWITH_CUPS=ON \
-DWITH_CHANNELS=ON -DBUILTIN_CHANNELS=OFF \
-DWITH_CLIENT=ON \
-DWITH_DIRECTFB=OFF \
-DWITH_DSP_FFMPEG=%{?_with_ffmpeg:ON}%{?!_with_ffmpeg:OFF} \
-DWITH_FFMPEG=%{?_with_ffmpeg:ON}%{?!_with_ffmpeg:OFF} \
-DWITH_GSM=ON \
-DWITH_GSSAPI=%{?_with_gss:ON}%{?!_with_gss:OFF} \
-DWITH_GSTREAMER_1_0=ON -DWITH_GSTREAMER_0_10=OFF \
-DGSTREAMER_1_0_INCLUDE_DIRS=%{_includedir}/gstreamer-1.0 \
-DWITH_ICU=ON \
-DWITH_IPP=OFF \
-DWITH_JPEG=ON \
-DWITH_LAME=%{?_with_lame:ON}%{?!_with_lame:OFF} \
-DWITH_MANPAGES=ON \
-DWITH_OPENCL=%{?_with_opencl:ON}%{?!_with_opencl:OFF} \
-DWITH_OPENH264=%{?_with_openh264:ON}%{?!_with_openh264:OFF} \
-DWITH_OPENSSL=ON \
-DWITH_PCSC=ON \
@ -362,6 +216,7 @@ find . -name "*.c" -exec chmod 664 {} \;
-DWITH_SOXR=%{?_with_soxr:ON}%{?!_with_soxr:OFF} \
-DWITH_WAYLAND=ON \
-DWITH_X11=ON \
-DWITH_X264=%{?_with_x264:ON}%{?!_with_x264:OFF} \
-DWITH_XCURSOR=ON \
-DWITH_XEXT=ON \
-DWITH_XKBFILE=ON \
@ -389,12 +244,17 @@ find . -name "*.c" -exec chmod 664 {} \;
-DARM_FP_ABI=soft \
-DWITH_NEON=OFF \
%endif
%{nil}
.
%cmake_build
%make_build
pushd winpr/tools/makecert-cli
%make_build
popd
%install
%cmake_install
%make_install
%make_install COMPONENT=tools
find %{buildroot} -name "*.a" -delete
@ -472,61 +332,6 @@ find %{buildroot} -name "*.a" -delete
%{_libdir}/pkgconfig/winpr-tools2.pc
%changelog
* Tue Aug 04 2026 RHEL Packaging Agent <redhat-ymir-agent@redhat.com> - 2:2.11.7-11
- Backport several CVE fixes (CVE-2026-64624, CVE-2026-67289, CVE-2026-67299,
CVE-2026-68580)
Resolves: RHEL-213173, RHEL-222796, RHEL-222972, RHEL-223606
* Wed Jun 24 2026 RHEL Packaging Agent <rhel-se-jotnar@redhat.com> - 2:2.11.7-10
- Fix incorrect bounds checks in planar codec (CVE-2026-45700)
Resolves: RHEL-186983
* Tue May 05 2026 Ondrej Holy <oholy@redhat.com> - 2:2.11.7-9
- Lock appWindow to fix use-after-free in RAIL mode (CVE-2026-25952)
Resolves: RHEL-159850
* Tue Apr 28 2026 Ondrej Holy <oholy@redhat.com> - 2:2.11.7-8
- Fix double free in xf_rail_window_common cleanup (CVE-2026-26986)
- Fix growth of preallocated buffers (CVE-2026-27951)
- Fix heap-buffer-overflow in bitmap_cache_put (CVE-2026-29775)
- Add DSP format checks (CVE-2026-31884)
- Fix DSP array bounds checks (CVE-2026-31883)
- Fix DSP array bounds checks (CVE-2026-31885)
- Update CLEAR_GLYPH_ENTRY::count after alloc (CVE-2026-33985)
Resolves: RHEL-159806, RHEL-155468, RHEL-161037, RHEL-161472
Resolves: RHEL-161508, RHEL-161075, RHEL-167794
* Fri Apr 10 2026 Ondrej Holy <oholy@redhat.com> - 2:2.11.7-7
- Update CLEAR_VBAR_ENTRY size after alloc (CVE-2026-33984)
- Fail progressive_rfx_quant_sub on invalid values (CVE-2026-33983)
Resolves: RHEL-162949, RHEL-162965
* Tue Mar 31 2026 Ondrej Holy <oholy@redhat.com> - 2:2.11.7-6
- Fix use of nsc_process_message
Resolves: RHEL-155984
* Fri Mar 27 2026 Ondrej Holy <oholy@redhat.com> - 2:2.11.7-5
- Backport several CVE fixes
Resolves: RHEL-147954, RHEL-147955, RHEL-147970, RHEL-147977, RHEL-147980
Resolves: RHEL-148002, RHEL-148014, RHEL-148031, RHEL-148906, RHEL-148996
Resolves: RHEL-149007, RHEL-149056, RHEL-155984
* Wed Mar 25 2026 Ondrej Holy <oholy@redhat.com> - 2:2.11.7-4
- Backport several CVE fixes
Resolves: RHEL-151979, RHEL-152206
* Tue Feb 17 2026 Ondrej Holy <oholy@redhat.com> - 2:2.11.7-3
- Backport several CVE fixes
Resolves: RHEL-148825, RHEL-148865, RHEL-148982
* Tue Jan 27 2026 Ondrej Holy <oholy@redhat.com> - 2:2.11.7-2
- Backport several CVE fixes
Resolves: RHEL-142417, RHEL-142401, RHEL-142385, RHEL-142369, RHEL-142353
Resolves: RHEL-142337, RHEL-142321
* Tue Oct 01 2024 Ondrej Holy <oholy@redhat.com> - 2:2.11.7-1
- Update to 2.11.7 (RHEL-53081)
* Tue Dec 13 2022 Ondrej Holy <oholy@redhat.com> - 2:2.2.0-10
- Fix "implicit declaration of function" errors (#2136153, #2145139)