From 5335061a798d36590d4ff2fd76f5dac2a54e5d0d Mon Sep 17 00:00:00 2001 From: AlmaLinux RelEng Bot Date: Tue, 21 Jul 2026 06:46:43 -0400 Subject: [PATCH] import Oracle_OSS freerdp-3.10.3-12.el10_2.6 --- ...nels-cliprdr-abort-on-duplicate-caps.patch | 143 ++++++ ...e-capabilitySetLength-in-server-caps.patch | 73 +++ ...-rdpear-disable-ndr-pointer-aliasing.patch | 31 ++ channels-rdpear-fix-ndr_read-checks.patch | 36 ++ ...nt-x11-fix-deadlock-on-output-expose.patch | 170 +++++++ client-x11-improve-rails-window-locking.patch | 413 ++++++++++++++++ client-x11-lock-appwindow.patch | 439 +++++++++++++++++ client-x11-refactor-locking.patch | 446 ++++++++++++++++++ codec-planar-fix-bounds-checks.patch | 30 ++ ...ection-print-SSL-warnings-after-init.patch | 37 ++ core-tcp-Don-t-ignore-connect-errors.patch | 43 ++ ...ferIPv6OverIPv4-fallback-to-IPv4-add.patch | 102 ++++ ...ry-next-DNS-entry-on-connect-failure.patch | 76 +++ ...fix-double-free-in-get_next_addrinfo.patch | 39 ++ freerdp.spec | 102 +++- ...-the-cache-element-can-hold-the-data.patch | 28 ++ gdi-gfx-fix-bounds-checks.patch | 31 ++ 17 files changed, 2220 insertions(+), 19 deletions(-) create mode 100644 channels-cliprdr-abort-on-duplicate-caps.patch create mode 100644 channels-cliprdr-validate-capabilitySetLength-in-server-caps.patch create mode 100644 channels-rdpear-disable-ndr-pointer-aliasing.patch create mode 100644 channels-rdpear-fix-ndr_read-checks.patch create mode 100644 client-x11-fix-deadlock-on-output-expose.patch create mode 100644 client-x11-improve-rails-window-locking.patch create mode 100644 client-x11-lock-appwindow.patch create mode 100644 client-x11-refactor-locking.patch create mode 100644 codec-planar-fix-bounds-checks.patch create mode 100644 core-connection-print-SSL-warnings-after-init.patch create mode 100644 core-tcp-Don-t-ignore-connect-errors.patch create mode 100644 core-tcp-Fix-PreferIPv6OverIPv4-fallback-to-IPv4-add.patch create mode 100644 core-tcp-Try-next-DNS-entry-on-connect-failure.patch create mode 100644 core-tcp-fix-double-free-in-get_next_addrinfo.patch create mode 100644 gdi-gfx-ensure-the-cache-element-can-hold-the-data.patch create mode 100644 gdi-gfx-fix-bounds-checks.patch diff --git a/channels-cliprdr-abort-on-duplicate-caps.patch b/channels-cliprdr-abort-on-duplicate-caps.patch new file mode 100644 index 0000000..70a3876 --- /dev/null +++ b/channels-cliprdr-abort-on-duplicate-caps.patch @@ -0,0 +1,143 @@ +From abf9e9c31e64630aa7459ac22e1aff3a277fd830 Mon Sep 17 00:00:00 2001 +From: Armin Novak +Date: Thu, 30 Apr 2026 10:42:42 +0200 +Subject: [PATCH] [channels,cliprdr] abort on duplicate caps + +--- + channels/cliprdr/server/cliprdr_main.c | 70 +++++++++++++++----------- + 1 file changed, 42 insertions(+), 28 deletions(-) + +diff --git a/channels/cliprdr/server/cliprdr_main.c b/channels/cliprdr/server/cliprdr_main.c +index 1aef5b373..345236d27 100644 +--- a/channels/cliprdr/server/cliprdr_main.c ++++ b/channels/cliprdr/server/cliprdr_main.c +@@ -472,6 +472,15 @@ static UINT cliprdr_server_receive_general_capability(CliprdrServerContext* cont + WINPR_ASSERT(context); + WINPR_ASSERT(cap_set); + ++ if (cap_set->capabilitySetLength != sizeof(CLIPRDR_GENERAL_CAPABILITY_SET)) ++ { ++ WLog_ERR(TAG, ++ "invalid capabilitySetLength %" PRIu16 " != %" PRIuz ++ " (capabilitySetType=CB_CAPSTYPE_GENERAL)", ++ cap_set->capabilitySetLength, sizeof(CLIPRDR_GENERAL_CAPABILITY_SET)); ++ return ERROR_INVALID_DATA; ++ } ++ + if (!Stream_CheckAndLogRequiredLength(TAG, s, 8)) + return ERROR_INVALID_DATA; + +@@ -500,6 +509,26 @@ static UINT cliprdr_server_receive_general_capability(CliprdrServerContext* cont + return CHANNEL_RC_OK; + } + ++static BOOL cliprdr_capabilities_contain_capset(const CLIPRDR_CAPABILITIES* capabilities, ++ const CLIPRDR_CAPABILITY_SET* capset) ++{ ++ WINPR_ASSERT(capabilities); ++ ++ const BYTE* offset = (const BYTE*)capabilities->capabilitySets; ++ for (UINT32 x = 0; x < capabilities->cCapabilitiesSets; x++) ++ { ++ const CLIPRDR_CAPABILITY_SET* cur = (const CLIPRDR_CAPABILITY_SET*)offset; ++ offset += cur->capabilitySetLength; ++ ++ if (cur->capabilitySetType == capset->capabilitySetType) ++ { ++ WLog_ERR(TAG, "duplicate CLIPRDR_CAPABILITY_SET %" PRIu16, cur->capabilitySetType); ++ return TRUE; ++ } ++ } ++ return FALSE; ++} ++ + /** + * Function description + * +@@ -508,12 +537,9 @@ static UINT cliprdr_server_receive_general_capability(CliprdrServerContext* cont + static UINT cliprdr_server_receive_capabilities(CliprdrServerContext* context, wStream* s, + const CLIPRDR_HEADER* header) + { +- UINT16 capabilitySetType = 0; +- UINT16 capabilitySetLength = 0; + UINT error = ERROR_INVALID_DATA; + size_t cap_sets_size = 0; + CLIPRDR_CAPABILITIES capabilities = { 0 }; +- CLIPRDR_CAPABILITY_SET* capSet = NULL; + + WINPR_ASSERT(context); + WINPR_UNUSED(header); +@@ -522,17 +548,16 @@ static UINT cliprdr_server_receive_capabilities(CliprdrServerContext* context, w + if (!Stream_CheckAndLogRequiredLength(TAG, s, 4)) + return ERROR_INVALID_DATA; + +- Stream_Read_UINT16(s, capabilities.cCapabilitiesSets); /* cCapabilitiesSets (2 bytes) */ ++ const UINT16 cCapabilitiesSets = Stream_Get_UINT16(s); /* cCapabilitiesSets (2 bytes) */ + Stream_Seek_UINT16(s); /* pad1 (2 bytes) */ + +- for (size_t index = 0; index < capabilities.cCapabilitiesSets; index++) ++ for (size_t index = 0; index < cCapabilitiesSets; index++) + { +- void* tmp = NULL; + const size_t cap_set_offset = cap_sets_size; + if (!Stream_CheckAndLogRequiredLength(TAG, s, sizeof(CLIPRDR_CAPABILITY_SET))) + goto out; +- Stream_Read_UINT16(s, capabilitySetType); /* capabilitySetType (2 bytes) */ +- Stream_Read_UINT16(s, capabilitySetLength); /* capabilitySetLength (2 bytes) */ ++ const UINT16 capabilitySetType = Stream_Get_UINT16(s); /* capabilitySetType (2 bytes) */ ++ const UINT16 capabilitySetLength = Stream_Get_UINT16(s); /* capabilitySetLength (2 bytes) */ + + if (capabilitySetLength < sizeof(CLIPRDR_CAPABILITY_SET)) + { +@@ -547,26 +572,9 @@ static UINT cliprdr_server_receive_capabilities(CliprdrServerContext* context, w + TAG, s, (size_t)capabilitySetLength - sizeof(CLIPRDR_CAPABILITY_SET))) + goto out; + +- switch (capabilitySetType) +- { +- case CB_CAPSTYPE_GENERAL: +- if (capabilitySetLength != sizeof(CLIPRDR_GENERAL_CAPABILITY_SET)) +- { +- WLog_ERR(TAG, +- "invalid capabilitySetLength %" PRIu16 " != %" PRIuz +- " (capabilitySetType=CB_CAPSTYPE_GENERAL)", +- capabilitySetLength, sizeof(CLIPRDR_GENERAL_CAPABILITY_SET)); +- goto out; +- } +- break; +- default: +- WLog_ERR(TAG, "unknown cliprdr capability set: %" PRIu16 "", capabilitySetType); +- goto out; +- } +- + cap_sets_size += capabilitySetLength; + +- tmp = realloc(capabilities.capabilitySets, cap_sets_size); ++ CLIPRDR_CAPABILITY_SET* tmp = realloc(capabilities.capabilitySets, cap_sets_size); + if (tmp == NULL) + { + WLog_ERR(TAG, "capabilities.capabilitySets realloc failed!"); +@@ -574,13 +582,19 @@ static UINT cliprdr_server_receive_capabilities(CliprdrServerContext* context, w + return CHANNEL_RC_NO_MEMORY; + } + +- capabilities.capabilitySets = (CLIPRDR_CAPABILITY_SET*)tmp; ++ capabilities.capabilitySets = tmp; + +- capSet = (CLIPRDR_CAPABILITY_SET*)(((BYTE*)capabilities.capabilitySets) + cap_set_offset); ++ CLIPRDR_CAPABILITY_SET* capSet = ++ (CLIPRDR_CAPABILITY_SET*)(((BYTE*)capabilities.capabilitySets) + cap_set_offset); + + capSet->capabilitySetType = capabilitySetType; + capSet->capabilitySetLength = capabilitySetLength; + ++ if (cliprdr_capabilities_contain_capset(&capabilities, capSet)) ++ goto out; ++ ++ capabilities.cCapabilitiesSets++; ++ + switch (capSet->capabilitySetType) + { + case CB_CAPSTYPE_GENERAL: +-- +2.52.0 diff --git a/channels-cliprdr-validate-capabilitySetLength-in-server-caps.patch b/channels-cliprdr-validate-capabilitySetLength-in-server-caps.patch new file mode 100644 index 0000000..1c396ac --- /dev/null +++ b/channels-cliprdr-validate-capabilitySetLength-in-server-caps.patch @@ -0,0 +1,73 @@ +From ff87e97d62f699826f6b8fc8f754e979d2c87ecb Mon Sep 17 00:00:00 2001 +From: Kevin Valerio +Date: Wed, 29 Apr 2026 22:39:54 +0200 +Subject: [PATCH] fix(cliprdr): validate capabilitySetLength in server caps + +--- + channels/cliprdr/server/cliprdr_main.c | 38 +++++++++++++++++++++++--- + 1 file changed, 34 insertions(+), 4 deletions(-) + +diff --git a/channels/cliprdr/server/cliprdr_main.c b/channels/cliprdr/server/cliprdr_main.c +index 3cfa057e3..1aef5b373 100644 +--- a/channels/cliprdr/server/cliprdr_main.c ++++ b/channels/cliprdr/server/cliprdr_main.c +@@ -528,15 +528,45 @@ static UINT cliprdr_server_receive_capabilities(CliprdrServerContext* context, w + for (size_t index = 0; index < capabilities.cCapabilitiesSets; index++) + { + void* tmp = NULL; +- if (!Stream_CheckAndLogRequiredLength(TAG, s, 4)) ++ const size_t cap_set_offset = cap_sets_size; ++ if (!Stream_CheckAndLogRequiredLength(TAG, s, sizeof(CLIPRDR_CAPABILITY_SET))) + goto out; + Stream_Read_UINT16(s, capabilitySetType); /* capabilitySetType (2 bytes) */ + Stream_Read_UINT16(s, capabilitySetLength); /* capabilitySetLength (2 bytes) */ + ++ if (capabilitySetLength < sizeof(CLIPRDR_CAPABILITY_SET)) ++ { ++ WLog_ERR(TAG, ++ "invalid capabilitySetLength %" PRIu16 " < %" PRIuz ++ " (capabilitySetType=%" PRIu16 ")", ++ capabilitySetLength, sizeof(CLIPRDR_CAPABILITY_SET), capabilitySetType); ++ goto out; ++ } ++ ++ if (!Stream_CheckAndLogRequiredLength( ++ TAG, s, (size_t)capabilitySetLength - sizeof(CLIPRDR_CAPABILITY_SET))) ++ goto out; ++ ++ switch (capabilitySetType) ++ { ++ case CB_CAPSTYPE_GENERAL: ++ if (capabilitySetLength != sizeof(CLIPRDR_GENERAL_CAPABILITY_SET)) ++ { ++ WLog_ERR(TAG, ++ "invalid capabilitySetLength %" PRIu16 " != %" PRIuz ++ " (capabilitySetType=CB_CAPSTYPE_GENERAL)", ++ capabilitySetLength, sizeof(CLIPRDR_GENERAL_CAPABILITY_SET)); ++ goto out; ++ } ++ break; ++ default: ++ WLog_ERR(TAG, "unknown cliprdr capability set: %" PRIu16 "", capabilitySetType); ++ goto out; ++ } ++ + cap_sets_size += capabilitySetLength; + +- if (cap_sets_size > 0) +- tmp = realloc(capabilities.capabilitySets, cap_sets_size); ++ tmp = realloc(capabilities.capabilitySets, cap_sets_size); + if (tmp == NULL) + { + WLog_ERR(TAG, "capabilities.capabilitySets realloc failed!"); +@@ -546,7 +576,7 @@ static UINT cliprdr_server_receive_capabilities(CliprdrServerContext* context, w + + capabilities.capabilitySets = (CLIPRDR_CAPABILITY_SET*)tmp; + +- capSet = &(capabilities.capabilitySets[index]); ++ capSet = (CLIPRDR_CAPABILITY_SET*)(((BYTE*)capabilities.capabilitySets) + cap_set_offset); + + capSet->capabilitySetType = capabilitySetType; + capSet->capabilitySetLength = capabilitySetLength; +-- +2.52.0 diff --git a/channels-rdpear-disable-ndr-pointer-aliasing.patch b/channels-rdpear-disable-ndr-pointer-aliasing.patch new file mode 100644 index 0000000..5f53877 --- /dev/null +++ b/channels-rdpear-disable-ndr-pointer-aliasing.patch @@ -0,0 +1,31 @@ +From ae03a9ff981ce7be1ab09dba2cd319d54984f910 Mon Sep 17 00:00:00 2001 +From: David Fort +Date: Wed, 6 May 2026 09:52:33 +0200 +Subject: [PATCH] Fix for GHSA-j9q5-7g8m-jc9v, disable NDR pointer aliasing + +The usage of aliased pointers can be used to trigger a double free, or a type confusion +(using the same memory location twice and pretend it's 2 different types of objects). +As pointer aliasing is never seen in practice with RDPEAR, let's just return an error +when we see an aliased pointer. +--- + channels/rdpear/common/ndr.c | 5 +++++ + 1 file changed, 5 insertions(+) + +diff --git a/channels/rdpear/common/ndr.c b/channels/rdpear/common/ndr.c +index 6e4b10aa8..20cc31c6e 100644 +--- a/channels/rdpear/common/ndr.c ++++ b/channels/rdpear/common/ndr.c +@@ -931,6 +931,11 @@ BOOL ndr_read_pointedMessageEx(NdrContext* context, wStream* s, ndr_refid ptrId, + return FALSE; + } + } ++ else ++ { ++ WLog_ERR(TAG, "aliased pointer aren't supported for now"); ++ return FALSE; ++ } + + *target = ret; + return TRUE; +-- +2.54.0 diff --git a/channels-rdpear-fix-ndr_read-checks.patch b/channels-rdpear-fix-ndr_read-checks.patch new file mode 100644 index 0000000..cc0969c --- /dev/null +++ b/channels-rdpear-fix-ndr_read-checks.patch @@ -0,0 +1,36 @@ +From 668fcb49d4856fad28f685db54a572af2a284b50 Mon Sep 17 00:00:00 2001 +From: akallabeth +Date: Sat, 10 Jan 2026 09:33:54 +0100 +Subject: [PATCH] [channels,rdpear] fix ndr_read checks + +--- + channels/rdpear/common/ndr.c | 6 +++++- + 1 file changed, 5 insertions(+), 1 deletion(-) + +diff --git a/channels/rdpear/common/ndr.c b/channels/rdpear/common/ndr.c +index d318ed63c..d0bd6d959 100644 +--- a/channels/rdpear/common/ndr.c ++++ b/channels/rdpear/common/ndr.c +@@ -574,8 +574,10 @@ BOOL ndr_read_uconformant_array(NdrContext* context, wStream* s, const NdrArrayH + WINPR_ASSERT(itemType); + WINPR_ASSERT(vtarget); + +- UINT32 count = 0; ++ if (itemType->itemSize == 0) ++ return FALSE; + ++ UINT32 count = 0; + if (!ndr_read_uint32(context, s, &count)) + return FALSE; + +@@ -910,6 +912,8 @@ BOOL ndr_read_pointedMessageEx(NdrContext* context, wStream* s, ndr_refid ptrId, + if (!ret) + { + size_t itemCount = ndr_hintsCount(descr, hints); ++ if (itemCount == 0) ++ return FALSE; + ret = calloc(itemCount, descr->itemSize); + if (!ret) + return FALSE; +-- +2.54.0 diff --git a/client-x11-fix-deadlock-on-output-expose.patch b/client-x11-fix-deadlock-on-output-expose.patch new file mode 100644 index 0000000..2d35ab6 --- /dev/null +++ b/client-x11-fix-deadlock-on-output-expose.patch @@ -0,0 +1,170 @@ +From 4c5a54419f0d6b62454acf2069ca5bf97f6952e7 Mon Sep 17 00:00:00 2001 +From: Ondrej Holy +Date: Tue, 5 May 2026 11:04:31 +0000 +Subject: [PATCH] [client,x11] fix deadlock on output expose + +Backport of commit a278ff74117444c635c50ffa5084ecf517171f5a. + +Adapted for 3.10.3: `WINPR_ATTR_NODISCARD` attribute removed +(not available in 3.10.3), C99 `bool` replaced with `BOOL`, +adjusted hunk offsets. + +Made-with: Cursor +--- + client/X11/xf_client.c | 3 ++ + client/X11/xf_event.c | 78 +++++++++++++++++++++++++----------------- + client/X11/xf_event.h | 3 ++ + client/X11/xfreerdp.h | 3 ++ + 4 files changed, 56 insertions(+), 31 deletions(-) + +diff --git a/client/X11/xf_client.c b/client/X11/xf_client.c +index 7ad30d6..08dc2ca 100644 +--- a/client/X11/xf_client.c ++++ b/client/X11/xf_client.c +@@ -514,6 +514,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 a1fbb84..172fb68 100644 +--- a/client/X11/xf_event.c ++++ b/client/X11/xf_event.c +@@ -370,50 +370,30 @@ 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 = 0; +- int y = 0; +- int w = 0; +- int h = 0; +- rdpSettings* settings = NULL; +- + WINPR_ASSERT(xfc); + WINPR_ASSERT(event); + +- settings = xfc->common.context.settings; ++ rdpSettings* settings = xfc->common.context.settings; + WINPR_ASSERT(settings); + + if (!app && (freerdp_settings_get_bool(settings, FreeRDP_SmartSizing) || + freerdp_settings_get_bool(settings, FreeRDP_MultiTouchGestures))) + { +- x = 0; +- y = 0; +- w = freerdp_settings_get_uint32(settings, FreeRDP_DesktopWidth); +- h = freerdp_settings_get_uint32(settings, FreeRDP_DesktopHeight); ++ xfc->exposedArea.x = 0; ++ xfc->exposedArea.y = 0; ++ xfc->exposedArea.w = freerdp_settings_get_uint32(settings, FreeRDP_DesktopWidth); ++ xfc->exposedArea.h = freerdp_settings_get_uint32(settings, FreeRDP_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->common.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; + } +@@ -1372,3 +1352,39 @@ BOOL xf_generic_RawButtonEvent(xfContext* xfc, int button, BOOL app, BOOL down) + + return TRUE; + } ++ ++BOOL xf_event_update_screen(freerdp* instance) ++{ ++ WINPR_ASSERT(instance); ++ ++ xfContext* xfc = (xfContext*)instance->context; ++ WINPR_ASSERT(xfc); ++ ++ rdpSettings* settings = xfc->common.context.settings; ++ WINPR_ASSERT(settings); ++ ++ if (!xfc->exposeRequested) ++ return TRUE; ++ xfc->exposeRequested = false; ++ ++ if (!xfc->remote_app) ++ { ++ if (xfc->common.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 2f4ab07..299d2d3 100644 +--- a/client/X11/xf_event.h ++++ b/client/X11/xf_event.h +@@ -31,6 +31,9 @@ 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 ba41553..677e3bc 100644 +--- a/client/X11/xfreerdp.h ++++ b/client/X11/xfreerdp.h +@@ -319,6 +319,9 @@ struct xf_context + BOOL xi_event; + HANDLE pipethread; + wLog* log; ++ bool exposeRequested; ++ GDI_RGN exposedArea; ++ Window exposedWindow; + }; + + BOOL xf_create_window(xfContext* xfc); +-- +2.54.0 + diff --git a/client-x11-improve-rails-window-locking.patch b/client-x11-improve-rails-window-locking.patch new file mode 100644 index 0000000..79b11a6 --- /dev/null +++ b/client-x11-improve-rails-window-locking.patch @@ -0,0 +1,413 @@ +From 24ef57b3085bfc353ab375ebba6a1c29d13929f1 Mon Sep 17 00:00:00 2001 +From: Ondrej Holy +Date: Tue, 5 May 2026 11:04:17 +0000 +Subject: [PATCH] [client,x11] improve rails window locking + +Backport of commit 78fd7f580d5f9e6d9d582d82e5ea96003844fbdf. + +Adapted for 3.10.3: `nullptr` replaced with `NULL`, +`WINPR_ATTR_MALLOC` updated for renamed function, +`WINPR_ATTR_UNUSED` removed from `xfAppWindowsLockFrom`/ +`UnlockFrom` signatures, `LogDynAndXSetTransientForHint` +replaced with inline `XSetTransientForHint` + `WLog_WARN`, +`xf_rail_add_window` parameter types `INT32` to `UINT32`, +`WLog_PrintTextMessage` replaced with `WLog_PrintMessage` +(not available in 3.10.3). + +Made-with: Cursor +--- + client/X11/xf_event.c | 3 +- + client/X11/xf_graphics.c | 5 +-- + client/X11/xf_rail.c | 52 +++++++++++++---------------- + client/X11/xf_rail.h | 11 +++++-- + client/X11/xf_window.c | 70 ++++++++++++++++++++++++++++++++++++---- + client/X11/xf_window.h | 12 +++++-- + client/X11/xfreerdp.h | 4 +++ + 7 files changed, 112 insertions(+), 45 deletions(-) + +diff --git a/client/X11/xf_event.c b/client/X11/xf_event.c +index b26ece0..f82ab94 100644 +--- a/client/X11/xf_event.c ++++ b/client/X11/xf_event.c +@@ -411,9 +411,8 @@ 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); + } + + return TRUE; +diff --git a/client/X11/xf_graphics.c b/client/X11/xf_graphics.c +index 5e68354..310f2a4 100644 +--- a/client/X11/xf_graphics.c ++++ b/client/X11/xf_graphics.c +@@ -39,6 +39,7 @@ + + #include "xf_graphics.h" + #include "xf_event.h" ++#include "xf_window.h" + + #include + #define TAG CLIENT_TAG("x11") +@@ -252,12 +253,12 @@ static Window xf_Pointer_get_window(xfContext* xfc) + if (xfc->remote_app) + { + Window w = 0; +- HashTable_Lock(xfc->railWindows); ++ xf_AppWindowsLock(xfc); + if (!xfc->appWindow) + WLog_WARN(TAG, "xf_Pointer: Invalid appWindow"); + else + w = xfc->appWindow->handle; +- HashTable_Unlock(xfc->railWindows); ++ xf_AppWindowsUnlock(xfc); + return w; + } + else +diff --git a/client/X11/xf_rail.c b/client/X11/xf_rail.c +index 2d79867..3353a8d 100644 +--- a/client/X11/xf_rail.c ++++ b/client/X11/xf_rail.c +@@ -109,9 +109,9 @@ void xf_rail_send_activate(xfContext* xfc, Window xwindow, BOOL enabled) + + WINPR_ASSERT(appWindow->windowId <= UINT32_MAX); + activate.windowId = (UINT32)appWindow->windowId; ++ xf_rail_return_window(appWindow); + activate.enabled = enabled; + xfc->rail->ClientActivate(xfc->rail, &activate); +- xf_rail_return_window(appWindow); + } + + BOOL xf_rail_send_client_system_command(xfContext* xfc, UINT64 windowId, UINT16 command) +@@ -309,10 +309,12 @@ static void window_state_log_style_int(wLog* log, const WINDOW_STATE_ORDER* wind + static BOOL xf_rail_window_common(rdpContext* context, const WINDOW_ORDER_INFO* orderInfo, + const WINDOW_STATE_ORDER* windowState) + { ++ BOOL rc = FALSE; ++ xfAppWindow* appWindow = NULL; + xfContext* xfc = (xfContext*)context; + UINT32 fieldFlags = orderInfo->fieldFlags; + BOOL position_or_size_updated = FALSE; +- xfAppWindow* appWindow = xf_rail_get_window(xfc, orderInfo->windowId); ++ appWindow = xf_rail_get_window(xfc, orderInfo->windowId); + + if (fieldFlags & WINDOW_ORDER_STATE_NEW) + { +@@ -322,7 +324,7 @@ static BOOL xf_rail_window_common(rdpContext* context, const WINDOW_ORDER_INFO* + windowState->windowHeight, 0xFFFFFFFF); + + if (!appWindow) +- return FALSE; ++ goto fail; + + appWindow->dwStyle = windowState->style; + appWindow->dwExStyle = windowState->extendedStyle; +@@ -363,13 +365,13 @@ static BOOL xf_rail_window_common(rdpContext* context, const WINDOW_ORDER_INFO* + } + + if (!appWindow->title) +- return FALSE; ++ goto fail; + + xf_AppWindowInit(xfc, appWindow); + } + + if (!appWindow) +- return FALSE; ++ goto fail; + + /* Keep track of any position/size update so that we can force a refresh of the window */ + if ((fieldFlags & WINDOW_ORDER_FIELD_WND_OFFSET) || +@@ -441,14 +443,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 (!(title = ConvertWCharNToUtf8Alloc( + cnv.wc, windowState->titleInfo.length / sizeof(WCHAR), NULL))) + { + WLog_ERR(TAG, "failed to convert window title"); +- return FALSE; ++ goto fail; + } + + free(appWindow->title); +@@ -489,7 +491,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)); +@@ -518,7 +520,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)); +@@ -594,7 +596,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) +@@ -1012,14 +1017,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; + default: + break; + } +@@ -1196,13 +1201,16 @@ xfAppWindow* xf_rail_add_window(xfContext* xfc, UINT64 id, UINT32 x, UINT32 y, U + appWindow->width = width; + appWindow->height = height; + ++ xf_AppWindowsLock(xfc); + if (!xf_AppWindowCreate(xfc, appWindow)) + goto fail; ++ + if (!HashTable_Insert(xfc->railWindows, &appWindow->windowId, (void*)appWindow)) + goto fail; + return appWindow; + fail: + rail_window_free(appWindow); ++ xf_AppWindowsUnlock(xfc); + return NULL; + } + +@@ -1217,26 +1225,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; +- +- HashTable_Lock(xfc->railWindows); +- xfAppWindow* window = HashTable_GetItemValue(xfc->railWindows, &id); +- if (!window) +- HashTable_Unlock(xfc->railWindows); +- +- return window; +-} +- +-void xf_rail_return_window(xfAppWindow* window) ++void xf_rail_return_windowFrom(xfAppWindow* window, const char* file, const char* fkt, size_t line) + { + if (!window) + return; + +- HashTable_Unlock(window->xfc->railWindows); ++ xfAppWindowsUnlockFrom(window->xfc, file, fkt, line); + } +diff --git a/client/X11/xf_rail.h b/client/X11/xf_rail.h +index 8e8ed33..2b365b5 100644 +--- a/client/X11/xf_rail.h ++++ b/client/X11/xf_rail.h +@@ -38,9 +38,14 @@ 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); ++#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_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 801efbc..8d7bcd6 100644 +--- a/client/X11/xf_window.c ++++ b/client/X11/xf_window.c +@@ -37,6 +37,7 @@ + + #include + #include ++#include + #include + #include + +@@ -1404,7 +1405,31 @@ 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) + { + ULONG_PTR* pKeys = NULL; + +@@ -1412,29 +1437,28 @@ xfAppWindow* xf_AppWindowFromX11Window(xfContext* xfc, Window wnd) + if (!xfc->railWindows) + return NULL; + +- HashTable_Lock(xfc->railWindows); ++ xfAppWindowsLockFrom(xfc, file, fkt, line); + size_t count = HashTable_GetKeys(xfc->railWindows, &pKeys); + + for (size_t index = 0; index < count; index++) + { +- xfAppWindow* appWindow = HashTable_GetItemValue(xfc->railWindows, (void*)pKeys[index]); ++ xfAppWindow* appWindow = get_windowUnlocked(xfc, *(UINT64*)pKeys[index]); + + if (!appWindow) + { +- HashTable_Unlock(xfc->railWindows); ++ xfAppWindowsUnlockFrom(xfc, file, fkt, line); + free(pKeys); + return NULL; + } + + if (appWindow->handle == wnd) + { +- HashTable_Unlock(xfc->railWindows); + free(pKeys); + return appWindow; + } + } + +- HashTable_Unlock(xfc->railWindows); ++ xfAppWindowsUnlockFrom(xfc, file, fkt, line); + free(pKeys); + return NULL; + } +@@ -1556,3 +1580,37 @@ void xf_XSetTransientForHint(xfContext* xfc, xfAppWindow* window) + } + xf_rail_return_window(parent); + } ++ ++void xfAppWindowsLockFrom(xfContext* xfc, const char* file, const char* fkt, size_t line) ++{ ++ WINPR_ASSERT(xfc); ++ ++#if defined(WITH_VERBOSE_WINPR_ASSERT) ++ const DWORD level = WLOG_TRACE; ++ if (WLog_IsLevelActive(xfc->log, level)) ++ WLog_PrintMessage(xfc->log, WLOG_MESSAGE_TEXT, level, line, file, fkt, "[rails] locking [%s]", fkt); ++#endif ++ ++ HashTable_Lock(xfc->railWindows); ++ ++#if defined(WITH_VERBOSE_WINPR_ASSERT) ++ WINPR_ASSERT(!xfc->isRailWindowsLocked); ++ xfc->isRailWindowsLocked = TRUE; ++#endif ++} ++ ++void xfAppWindowsUnlockFrom(xfContext* xfc, const char* file, const char* fkt, size_t line) ++{ ++ WINPR_ASSERT(xfc); ++ ++#if defined(WITH_VERBOSE_WINPR_ASSERT) ++ const DWORD level = WLOG_TRACE; ++ if (WLog_IsLevelActive(xfc->log, level)) ++ WLog_PrintMessage(xfc->log, WLOG_MESSAGE_TEXT, level, line, file, fkt, "[rails] unocking [%s]", fkt); ++ ++ WINPR_ASSERT(xfc->isRailWindowsLocked); ++ xfc->isRailWindowsLocked = FALSE; ++#endif ++ ++ HashTable_Unlock(xfc->railWindows); ++} +diff --git a/client/X11/xf_window.h b/client/X11/xf_window.h +index 2384d61..25eedb3 100644 +--- a/client/X11/xf_window.h ++++ b/client/X11/xf_window.h +@@ -202,8 +202,16 @@ 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); +-xfAppWindow* xf_AppWindowFromX11Window(xfContext* xfc, Window wnd); +-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); ++ ++#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); + + const char* window_styles_to_string(UINT32 style, char* buffer, size_t length); + const char* window_styles_ex_to_string(UINT32 styleEx, char* buffer, size_t length); +diff --git a/client/X11/xfreerdp.h b/client/X11/xfreerdp.h +index 000ba56..ba41553 100644 +--- a/client/X11/xfreerdp.h ++++ b/client/X11/xfreerdp.h +@@ -290,6 +290,10 @@ struct xf_context + wHashTable* railWindows; + xfRailIconCache* railIconCache; + ++#if defined(WITH_VERBOSE_WINPR_ASSERT) ++ BOOL isRailWindowsLocked; ++#endif ++ + BOOL xkbAvailable; + BOOL xrenderAvailable; + +-- +2.54.0 + diff --git a/client-x11-lock-appwindow.patch b/client-x11-lock-appwindow.patch new file mode 100644 index 0000000..4b26969 --- /dev/null +++ b/client-x11-lock-appwindow.patch @@ -0,0 +1,439 @@ +From 87ef0851132df9b00380f3cfe9d63272a85d7c49 Mon Sep 17 00:00:00 2001 +From: Ondrej Holy +Date: Tue, 5 May 2026 11:03:44 +0000 +Subject: [PATCH] [client,x11] lock appWindow + +Backport of commit 1994e9844212a6dfe0ff12309fef520e888986b5. + +Adapted for 3.10.3: `xf_generic_MotionNotify_` renamed to +`xf_generic_MotionNotify`, `xf_generic_ButtonEvent_` to +`xf_generic_ButtonEvent`, `WINPR_ATTR_MALLOC` attribute removed +(not available in 3.10.3), adjusted hunk offsets. + +Made-with: Cursor +--- + client/X11/xf_event.c | 47 ++++++++++++++++++---------- + client/X11/xf_graphics.c | 10 +++--- + client/X11/xf_rail.c | 67 ++++++++++++++++++++++++++-------------- + client/X11/xf_rail.h | 3 ++ + client/X11/xf_window.c | 8 ++++- + client/X11/xf_window.h | 1 + + 6 files changed, 92 insertions(+), 44 deletions(-) + +diff --git a/client/X11/xf_event.c b/client/X11/xf_event.c +index 0fce625..b26ece0 100644 +--- a/client/X11/xf_event.c ++++ b/client/X11/xf_event.c +@@ -442,7 +442,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 */ +@@ -536,7 +538,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 */ +@@ -694,6 +698,7 @@ static BOOL xf_event_FocusIn(xfContext* xfc, const XFocusInEvent* event, BOOL ap + */ + if (appWindow) + xf_rail_adjust_position(xfc, appWindow); ++ xf_rail_return_window(appWindow); + } + + xf_keyboard_focus_in(xfc); +@@ -734,12 +739,13 @@ static BOOL xf_event_ClientMessage(xfContext* xfc, const XClientMessageEvent* ev + { + if (app) + { ++ BOOL rc = TRUE; + xfAppWindow* appWindow = xf_AppWindowFromX11Window(xfc, event->window); + + if (appWindow) +- return xf_rail_send_client_system_command(xfc, appWindow->windowId, SC_CLOSE); +- +- return TRUE; ++ rc = xf_rail_send_client_system_command(xfc, appWindow->windowId, SC_CLOSE); ++ xf_rail_return_window(appWindow); ++ return rc; + } + else + { +@@ -772,6 +778,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; +@@ -793,6 +800,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; + } +@@ -893,6 +901,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); + } +@@ -916,6 +925,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; +@@ -930,13 +940,14 @@ static BOOL xf_event_UnmapNotify(xfContext* xfc, const XUnmapEvent* event, BOOL + xf_keyboard_release_all_keypress(xfc); + + if (!app) +- gdi_send_suppress_output(xfc->common.context.gdi, TRUE); +- else ++ return gdi_send_suppress_output(xfc->common.context.gdi, TRUE); ++ + { + xfAppWindow* appWindow = xf_AppWindowFromX11Window(xfc, event->window); + + if (appWindow) + appWindow->is_mapped = FALSE; ++ xf_rail_return_window(appWindow); + } + + return TRUE; +@@ -944,6 +955,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); + +@@ -968,7 +980,7 @@ static BOOL xf_event_PropertyNotify(xfContext* xfc, const XPropertyEvent* event, + appWindow = xf_AppWindowFromX11Window(xfc, event->window); + + if (!appWindow) +- return TRUE; ++ goto fail; + } + + if (event->atom == xfc->_NET_WM_STATE) +@@ -1040,8 +1052,7 @@ static BOOL xf_event_PropertyNotify(xfContext* xfc, const XPropertyEvent* event, + if (appWindow->rail_state != WINDOW_SHOW_MAXIMIZED) + { + appWindow->rail_state = WINDOW_SHOW_MAXIMIZED; +- return xf_rail_send_client_system_command(xfc, appWindow->windowId, +- SC_MAXIMIZE); ++ rc = xf_rail_send_client_system_command(xfc, appWindow->windowId, SC_MAXIMIZE); + } + } + else if (appWindow->minimized) +@@ -1049,8 +1060,7 @@ static BOOL xf_event_PropertyNotify(xfContext* xfc, const XPropertyEvent* event, + if (appWindow->rail_state != WINDOW_SHOW_MINIMIZED) + { + appWindow->rail_state = WINDOW_SHOW_MINIMIZED; +- return xf_rail_send_client_system_command(xfc, appWindow->windowId, +- SC_MINIMIZE); ++ rc = xf_rail_send_client_system_command(xfc, appWindow->windowId, SC_MINIMIZE); + } + } + else +@@ -1058,15 +1068,18 @@ static BOOL xf_event_PropertyNotify(xfContext* xfc, const XPropertyEvent* event, + if (appWindow->rail_state != WINDOW_SHOW && appWindow->rail_state != WINDOW_HIDE) + { + appWindow->rail_state = WINDOW_SHOW; +- return xf_rail_send_client_system_command(xfc, appWindow->windowId, SC_RESTORE); ++ rc = xf_rail_send_client_system_command(xfc, appWindow->windowId, SC_RESTORE); + } + } + } + else if (minimizedChanged) +- gdi_send_suppress_output(xfc->common.context.gdi, minimized); ++ rc = gdi_send_suppress_output(xfc->common.context.gdi, minimized); ++ ++ fail: ++ xf_rail_return_window(appWindow); + } + +- return TRUE; ++ return rc; + } + + static BOOL xf_event_suppress_events(xfContext* xfc, xfAppWindow* appWindow, const XEvent* event) +@@ -1182,7 +1195,9 @@ BOOL xf_event_process(freerdp* instance, const XEvent* event) + /* Update "current" window for cursor change orders */ + xfc->appWindow = appWindow; + +- if (xf_event_suppress_events(xfc, appWindow, event)) ++ const BOOL rc = xf_event_suppress_events(xfc, appWindow, event); ++ xf_rail_return_window(appWindow); ++ if (rc) + return TRUE; + } + } +diff --git a/client/X11/xf_graphics.c b/client/X11/xf_graphics.c +index a6950ad..5e68354 100644 +--- a/client/X11/xf_graphics.c ++++ b/client/X11/xf_graphics.c +@@ -251,12 +251,14 @@ static Window xf_Pointer_get_window(xfContext* xfc) + } + if (xfc->remote_app) + { ++ Window w = 0; ++ HashTable_Lock(xfc->railWindows); + if (!xfc->appWindow) +- { + WLog_WARN(TAG, "xf_Pointer: Invalid appWindow"); +- return 0; +- } +- return xfc->appWindow->handle; ++ else ++ w = xfc->appWindow->handle; ++ HashTable_Unlock(xfc->railWindows); ++ return w; + } + else + { +diff --git a/client/X11/xf_rail.c b/client/X11/xf_rail.c +index f931b50..2d79867 100644 +--- a/client/X11/xf_rail.c ++++ b/client/X11/xf_rail.c +@@ -111,6 +111,7 @@ void xf_rail_send_activate(xfContext* xfc, Window xwindow, BOOL enabled) + activate.windowId = (UINT32)appWindow->windowId; + activate.enabled = enabled; + xfc->rail->ClientActivate(xfc->rail, &activate); ++ xf_rail_return_window(appWindow); + } + + BOOL xf_rail_send_client_system_command(xfContext* xfc, UINT64 windowId, UINT16 command) +@@ -255,6 +256,7 @@ BOOL xf_rail_paint_surface(xfContext* xfc, UINT64 windowId, const RECTANGLE_16* + updateRect.right - updateRect.left, updateRect.bottom - updateRect.top); + } + region16_uninit(&windowInvalidRegion); ++ xf_rail_return_window(appWindow); + return TRUE; + } + +@@ -729,61 +731,65 @@ 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 = NULL; +- xfRailIcon* icon = NULL; + BOOL replaceIcon = 0; +- railWindow = xf_rail_get_window(xfc, orderInfo->windowId); ++ 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 = NULL; +- xfRailIcon* icon = NULL; ++ WINPR_ASSERT(orderInfo); ++ + BOOL replaceIcon = 0; +- railWindow = xf_rail_get_window(xfc, orderInfo->windowId); ++ 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, +@@ -1023,6 +1029,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; + } + +@@ -1044,6 +1051,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; + } +@@ -1215,7 +1223,20 @@ xfAppWindow* xf_rail_get_window(xfContext* xfc, UINT64 id) + return NULL; + + if (!xfc->railWindows) +- return FALSE; ++ return NULL; ++ ++ HashTable_Lock(xfc->railWindows); ++ xfAppWindow* window = HashTable_GetItemValue(xfc->railWindows, &id); ++ if (!window) ++ HashTable_Unlock(xfc->railWindows); ++ ++ return window; ++} ++ ++void xf_rail_return_window(xfAppWindow* window) ++{ ++ if (!window) ++ return; + +- return HashTable_GetItemValue(xfc->railWindows, &id); ++ HashTable_Unlock(window->xfc->railWindows); + } +diff --git a/client/X11/xf_rail.h b/client/X11/xf_rail.h +index 9cd54f0..8e8ed33 100644 +--- a/client/X11/xf_rail.h ++++ b/client/X11/xf_rail.h +@@ -37,6 +37,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 cf3d8bc..801efbc 100644 +--- a/client/X11/xf_window.c ++++ b/client/X11/xf_window.c +@@ -1412,25 +1412,29 @@ xfAppWindow* xf_AppWindowFromX11Window(xfContext* xfc, Window wnd) + if (!xfc->railWindows) + return NULL; + ++ HashTable_Lock(xfc->railWindows); + size_t count = HashTable_GetKeys(xfc->railWindows, &pKeys); + + for (size_t index = 0; index < count; index++) + { +- xfAppWindow* appWindow = xf_rail_get_window(xfc, *(UINT64*)pKeys[index]); ++ xfAppWindow* appWindow = HashTable_GetItemValue(xfc->railWindows, (void*)pKeys[index]); + + if (!appWindow) + { ++ HashTable_Unlock(xfc->railWindows); + free(pKeys); + return NULL; + } + + if (appWindow->handle == wnd) + { ++ HashTable_Unlock(xfc->railWindows); + free(pKeys); + return appWindow; + } + } + ++ HashTable_Unlock(xfc->railWindows); + free(pKeys); + return NULL; + } +@@ -1509,6 +1513,7 @@ UINT xf_AppUpdateWindowFromSurface(xfContext* xfc, gdiGfxSurface* surface) + + rc = CHANNEL_RC_OK; + fail: ++ xf_rail_return_window(appWindow); + XFlush(xfc->display); + xf_unlock_x11(xfc); + return rc; +@@ -1549,4 +1554,5 @@ void xf_XSetTransientForHint(xfContext* xfc, xfAppWindow* window) + WLog_WARN(TAG, "XSetTransientForHint [%d]{%s}", rc, + x11_error_to_string(xfc, rc, buffer, sizeof(buffer))); + } ++ xf_rail_return_window(parent); + } +diff --git a/client/X11/xf_window.h b/client/X11/xf_window.h +index efbc67d..2384d61 100644 +--- a/client/X11/xf_window.h ++++ b/client/X11/xf_window.h +@@ -203,6 +203,7 @@ void xf_SetWindowMinMaxInfo(xfContext* xfc, xfAppWindow* appWindow, int maxWidth + void xf_StartLocalMoveSize(xfContext* xfc, xfAppWindow* appWindow, int direction, int x, int y); + void xf_EndLocalMoveSize(xfContext* xfc, xfAppWindow* appWindow); + xfAppWindow* xf_AppWindowFromX11Window(xfContext* xfc, Window wnd); ++void xf_rail_return_window(xfAppWindow* window); + + const char* window_styles_to_string(UINT32 style, char* buffer, size_t length); + const char* window_styles_ex_to_string(UINT32 styleEx, char* buffer, size_t length); +-- +2.54.0 + diff --git a/client-x11-refactor-locking.patch b/client-x11-refactor-locking.patch new file mode 100644 index 0000000..6aed524 --- /dev/null +++ b/client-x11-refactor-locking.patch @@ -0,0 +1,446 @@ +From aaa9a2609069494e89b166417ad47570d205c3bc Mon Sep 17 00:00:00 2001 +From: Ondrej Holy +Date: Tue, 5 May 2026 11:04:31 +0000 +Subject: [PATCH] [client,x11] refactor locking + +Backport of commit 4ff57b68c2960fa414d03c78ff0e0660be1cc5bd. + +Adapted for 3.10.3: `LogDynAndXSetClipMask`/`LogDynAndXSync` +replaced with direct `XSetClipMask`/`XSync` calls, +`LogDynAndXPutImage`/`LogDynAndXFlush` replaced with direct +`XPutImage`/`XFlush` calls, adjusted hunk offsets. + +Made-with: Cursor +--- + client/X11/xf_event.c | 24 ++++++++++++------------ + client/X11/xf_gfx.c | 4 ++-- + client/X11/xf_rail.c | 37 ++++++++++++++++++++++--------------- + client/X11/xf_rail.h | 17 +++++++++-------- + client/X11/xf_window.c | 27 ++++++++++++++------------- + 5 files changed, 59 insertions(+), 50 deletions(-) + +diff --git a/client/X11/xf_event.c b/client/X11/xf_event.c +index f82ab94..a1fbb84 100644 +--- a/client/X11/xf_event.c ++++ b/client/X11/xf_event.c +@@ -412,7 +412,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; +@@ -442,7 +442,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; + +@@ -538,7 +538,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; + +@@ -697,7 +697,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); +@@ -743,7 +743,7 @@ static BOOL xf_event_ClientMessage(xfContext* xfc, const XClientMessageEvent* ev + + if (appWindow) + rc = 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 +@@ -777,7 +777,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; +@@ -799,7 +799,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; + } +@@ -900,7 +900,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); + } +@@ -924,7 +924,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; +@@ -946,7 +946,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; +@@ -1075,7 +1075,7 @@ static BOOL xf_event_PropertyNotify(xfContext* xfc, const XPropertyEvent* event, + rc = gdi_send_suppress_output(xfc->common.context.gdi, minimized); + + fail: +- xf_rail_return_window(appWindow); ++ xf_rail_return_window(appWindow, FALSE); + } + + return rc; +@@ -1195,7 +1195,7 @@ BOOL xf_event_process(freerdp* instance, const XEvent* event) + xfc->appWindow = appWindow; + + const BOOL rc = xf_event_suppress_events(xfc, appWindow, event); +- xf_rail_return_window(appWindow); ++ xf_rail_return_window(appWindow, FALSE); + if (rc) + return TRUE; + } +diff --git a/client/X11/xf_gfx.c b/client/X11/xf_gfx.c +index 48a2755..b748cf4 100644 +--- a/client/X11/xf_gfx.c ++++ b/client/X11/xf_gfx.c +@@ -66,6 +66,7 @@ static UINT xf_OutputUpdate(xfContext* xfc, xfGfxSurface* surface) + if (!(rects = region16_rects(&surface->gdi.invalidRegion, &nbRects))) + return CHANNEL_RC_OK; + ++ xf_lock_x11(xfc); + for (UINT32 x = 0; x < nbRects; x++) + { + const RECTANGLE_16* rect = &rects[x]; +@@ -90,9 +91,7 @@ static UINT xf_OutputUpdate(xfContext* xfc, xfGfxSurface* surface) + { + XPutImage(xfc->display, xfc->primary, xfc->gc, surface->image, nXSrc, nYSrc, nXDst, + nYDst, dwidth, dheight); +- xf_lock_x11(xfc); + xf_rail_paint_surface(xfc, surface->gdi.windowId, rect); +- xf_unlock_x11(xfc); + } + else + #ifdef WITH_XRENDER +@@ -116,6 +115,7 @@ fail: + region16_clear(&surface->gdi.invalidRegion); + XSetClipMask(xfc->display, xfc->gc, None); + XSync(xfc->display, False); ++ xf_unlock_x11(xfc); + return rc; + } + +diff --git a/client/X11/xf_rail.c b/client/X11/xf_rail.c +index 3353a8d..5322859 100644 +--- a/client/X11/xf_rail.c ++++ b/client/X11/xf_rail.c +@@ -109,7 +109,7 @@ void xf_rail_send_activate(xfContext* xfc, Window xwindow, BOOL enabled) + + WINPR_ASSERT(appWindow->windowId <= UINT32_MAX); + activate.windowId = (UINT32)appWindow->windowId; +- xf_rail_return_window(appWindow); ++ xf_rail_return_window(appWindow, FALSE); + activate.enabled = enabled; + xfc->rail->ClientActivate(xfc->rail, &activate); + } +@@ -227,7 +227,7 @@ void xf_rail_end_local_move(xfContext* xfc, xfAppWindow* appWindow) + + BOOL xf_rail_paint_surface(xfContext* xfc, UINT64 windowId, const RECTANGLE_16* rect) + { +- xfAppWindow* appWindow = xf_rail_get_window(xfc, windowId); ++ xfAppWindow* appWindow = xf_rail_get_window(xfc, windowId, FALSE); + + WINPR_ASSERT(rect); + +@@ -256,7 +256,7 @@ BOOL xf_rail_paint_surface(xfContext* xfc, UINT64 windowId, const RECTANGLE_16* + updateRect.right - updateRect.left, updateRect.bottom - updateRect.top); + } + region16_uninit(&windowInvalidRegion); +- xf_rail_return_window(appWindow); ++ xf_rail_return_window(appWindow, FALSE); + return TRUE; + } + +@@ -314,7 +314,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; +- appWindow = xf_rail_get_window(xfc, orderInfo->windowId); ++ appWindow = xf_rail_get_window(xfc, orderInfo->windowId, FALSE); + + if (fieldFlags & WINDOW_ORDER_STATE_NEW) + { +@@ -598,7 +598,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; + } + +@@ -739,7 +739,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; +@@ -762,7 +762,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; + } + +@@ -774,7 +774,7 @@ static BOOL xf_rail_window_cached_icon(rdpContext* context, const WINDOW_ORDER_I + WINPR_ASSERT(orderInfo); + + 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; +@@ -793,7 +793,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; + } + +@@ -951,7 +951,7 @@ static UINT xf_rail_server_local_move_size(RailClientContext* context, + int direction = 0; + Window child_window = 0; + 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; +@@ -1034,7 +1034,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; + } + +@@ -1047,7 +1047,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) + { +@@ -1056,7 +1056,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; + } +@@ -1222,13 +1222,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 2b365b5..dafbd51 100644 +--- a/client/X11/xf_rail.h ++++ b/client/X11/xf_rail.h +@@ -38,14 +38,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); + +-#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_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); ++#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, alreadyLocked) \ ++ xf_rail_get_windowFrom((xfc), (id), (alreadyLocked), __FILE__, __func__, __LINE__) ++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 8d7bcd6..3a67e0e 100644 +--- a/client/X11/xf_window.c ++++ b/client/X11/xf_window.c +@@ -1343,8 +1343,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 (freerdp_settings_get_bool(settings, FreeRDP_SoftwareGdi)) + { + XPutImage(xfc->display, appWindow->pixmap, appWindow->gc, xfc->image, ax, ay, x, y, width, +@@ -1354,7 +1352,6 @@ void xf_UpdateWindowArea(xfContext* xfc, xfAppWindow* appWindow, int x, int y, i + XCopyArea(xfc->display, appWindow->pixmap, appWindow->handle, appWindow->gc, x, y, width, + height, x, y); + XFlush(xfc->display); +- xf_unlock_x11(xfc); + } + + static void xf_AppWindowDestroyImage(xfAppWindow* appWindow) +@@ -1411,8 +1408,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; +@@ -1420,9 +1417,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; +@@ -1471,7 +1471,7 @@ UINT xf_AppUpdateWindowFromSurface(xfContext* xfc, gdiGfxSurface* surface) + WINPR_ASSERT(xfc); + WINPR_ASSERT(surface); + +- xfAppWindow* appWindow = xf_rail_get_window(xfc, surface->windowId); ++ xfAppWindow* appWindow = xf_rail_get_window(xfc, surface->windowId, FALSE); + if (!appWindow) + { + WLog_VRB(TAG, "Failed to find a window for id=0x%08" PRIx64, surface->windowId); +@@ -1482,7 +1482,6 @@ UINT xf_AppUpdateWindowFromSurface(xfContext* xfc, gdiGfxSurface* surface) + UINT32 nrects = 0; + const RECTANGLE_16* rects = region16_rects(&surface->invalidRegion, &nrects); + +- xf_lock_x11(xfc); + if (swGdi) + { + if (appWindow->surfaceId != surface->surfaceId) +@@ -1537,9 +1536,9 @@ UINT xf_AppUpdateWindowFromSurface(xfContext* xfc, gdiGfxSurface* surface) + + rc = CHANNEL_RC_OK; + fail: +- xf_rail_return_window(appWindow); ++ xf_rail_return_window(appWindow, FALSE); + XFlush(xfc->display); +- xf_unlock_x11(xfc); ++ + return rc; + } + +@@ -1567,7 +1566,7 @@ void xf_XSetTransientForHint(xfContext* xfc, xfAppWindow* window) + if (window->ownerWindowId == 0) + return; + +- xfAppWindow* parent = xf_rail_get_window(xfc, window->ownerWindowId); ++ xfAppWindow* parent = xf_rail_get_window(xfc, window->ownerWindowId, TRUE); + if (!parent) + return; + +@@ -1578,7 +1577,7 @@ void xf_XSetTransientForHint(xfContext* xfc, xfAppWindow* window) + WLog_WARN(TAG, "XSetTransientForHint [%d]{%s}", rc, + x11_error_to_string(xfc, rc, buffer, sizeof(buffer))); + } +- xf_rail_return_window(parent); ++ xf_rail_return_window(parent, TRUE); + } + + void xfAppWindowsLockFrom(xfContext* xfc, const char* file, const char* fkt, size_t line) +@@ -1591,6 +1590,7 @@ void xfAppWindowsLockFrom(xfContext* xfc, const char* file, const char* fkt, siz + WLog_PrintMessage(xfc->log, WLOG_MESSAGE_TEXT, level, line, file, fkt, "[rails] locking [%s]", fkt); + #endif + ++ xf_lock_x11(xfc); + HashTable_Lock(xfc->railWindows); + + #if defined(WITH_VERBOSE_WINPR_ASSERT) +@@ -1613,4 +1613,5 @@ void xfAppWindowsUnlockFrom(xfContext* xfc, const char* file, const char* fkt, s + #endif + + HashTable_Unlock(xfc->railWindows); ++ xf_unlock_x11(xfc); + } +-- +2.54.0 + diff --git a/codec-planar-fix-bounds-checks.patch b/codec-planar-fix-bounds-checks.patch new file mode 100644 index 0000000..1eb57d4 --- /dev/null +++ b/codec-planar-fix-bounds-checks.patch @@ -0,0 +1,30 @@ +From d9bf1e5fca30a89603accff808b2452750333cb6 Mon Sep 17 00:00:00 2001 +From: Armin Novak +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 58efbc627..0c4129fb1 100644 +--- a/libfreerdp/codec/planar.c ++++ b/libfreerdp/codec/planar.c +@@ -958,12 +958,12 @@ BOOL planar_decompress(BITMAP_PLANAR_CONTEXT* WINPR_RESTRICT planar, + 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 diff --git a/core-connection-print-SSL-warnings-after-init.patch b/core-connection-print-SSL-warnings-after-init.patch new file mode 100644 index 0000000..f1368dd --- /dev/null +++ b/core-connection-print-SSL-warnings-after-init.patch @@ -0,0 +1,37 @@ +From 60ed73552ffdb499dddf06c119be9437da7f9261 Mon Sep 17 00:00:00 2001 +From: akallabeth +Date: Sun, 29 Dec 2024 10:22:56 +0100 +Subject: [PATCH] [core,connection] print SSL warnings after init + +--- + libfreerdp/core/connection.c | 1 + + libfreerdp/core/freerdp.c | 1 - + 2 files changed, 1 insertion(+), 1 deletion(-) + +diff --git a/libfreerdp/core/connection.c b/libfreerdp/core/connection.c +index 979ccdf60..001b56944 100644 +--- a/libfreerdp/core/connection.c ++++ b/libfreerdp/core/connection.c +@@ -319,6 +319,7 @@ BOOL rdp_client_connect(rdpRdp* rdp) + flags |= WINPR_SSL_INIT_ENABLE_FIPS; + + winpr_InitializeSSL(flags); ++ rdp_log_build_warnings(rdp); + + /* FIPS Mode forces the following and overrides the following(by happening later */ + /* in the command line processing): */ +diff --git a/libfreerdp/core/freerdp.c b/libfreerdp/core/freerdp.c +index 94dad20a0..715da31d9 100644 +--- a/libfreerdp/core/freerdp.c ++++ b/libfreerdp/core/freerdp.c +@@ -802,7 +802,6 @@ BOOL freerdp_context_new_ex(freerdp* instance, rdpSettings* settings) + if (!rdp) + goto fail; + +- rdp_log_build_warnings(rdp); + context->rdp = rdp; + context->pubSub = rdp->pubSub; + +-- +2.51.1 + diff --git a/core-tcp-Don-t-ignore-connect-errors.patch b/core-tcp-Don-t-ignore-connect-errors.patch new file mode 100644 index 0000000..b080e9b --- /dev/null +++ b/core-tcp-Don-t-ignore-connect-errors.patch @@ -0,0 +1,43 @@ +From 5a856bef31bdbecbaf69bd671d23add9779debd9 Mon Sep 17 00:00:00 2001 +From: Ondrej Holy +Date: Wed, 14 Jan 2026 11:37:16 +0100 +Subject: [PATCH] [core,tcp] Don't ignore connect errors + +Backport of commit 0bdd8da0993231216a7bb4d5e6e33e47d817a944. + +Co-Authored-By: Claude +--- + libfreerdp/core/tcp.c | 15 +++++++++++---- + 1 file changed, 11 insertions(+), 4 deletions(-) + +diff --git a/libfreerdp/core/tcp.c b/libfreerdp/core/tcp.c +index 8e7ee20bd..6edfb6d71 100644 +--- a/libfreerdp/core/tcp.c ++++ b/libfreerdp/core/tcp.c +@@ -857,12 +857,19 @@ static BOOL freerdp_tcp_connect_timeout(rdpContext* context, int sockfd, struct + if (WAIT_OBJECT_0 != status) + goto fail; + +- const SSIZE_T res = recv(sockfd, NULL, 0, 0); +- +- if (res == SOCKET_ERROR) + { +- if (WSAGetLastError() == WSAECONNRESET) ++ INT32 optval = 0; ++ socklen_t optlen = sizeof(optval); ++ if (getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &optval, &optlen) < 0) ++ goto fail; ++ ++ if (optval != 0) ++ { ++ char ebuffer[256] = { 0 }; ++ WLog_DBG(TAG, "connect failed with error: %s [%" PRId32 "]", ++ winpr_strerror(optval, ebuffer, sizeof(ebuffer)), optval); + goto fail; ++ } + } + + status = WSAEventSelect(sockfd, handles[0], 0); +-- +2.52.0 + diff --git a/core-tcp-Fix-PreferIPv6OverIPv4-fallback-to-IPv4-add.patch b/core-tcp-Fix-PreferIPv6OverIPv4-fallback-to-IPv4-add.patch new file mode 100644 index 0000000..8267f8d --- /dev/null +++ b/core-tcp-Fix-PreferIPv6OverIPv4-fallback-to-IPv4-add.patch @@ -0,0 +1,102 @@ +From 4864954a384e4028298e95a19795505f688b21ae Mon Sep 17 00:00:00 2001 +From: Ondrej Holy +Date: Wed, 14 Jan 2026 11:38:11 +0100 +Subject: [PATCH] [core,tcp] Fix PreferIPv6OverIPv4 fallback to IPv4 addresses + +Backport of commit 0bdd8da0993231216a7bb4d5e6e33e47d817a944. + +Co-Authored-By: Claude +--- + libfreerdp/core/tcp.c | 61 ++++++++++++++++++++++++++++++++++++------- + 1 file changed, 51 insertions(+), 10 deletions(-) + +diff --git a/libfreerdp/core/tcp.c b/libfreerdp/core/tcp.c +index 6edfb6d71..83cc9af1e 100644 +--- a/libfreerdp/core/tcp.c ++++ b/libfreerdp/core/tcp.c +@@ -1081,6 +1081,53 @@ int freerdp_tcp_connect(rdpContext* context, const char* hostname, int port, DWO + return transport_tcp_connect(context->rdp->transport, hostname, port, timeout); + } + ++static struct addrinfo* reorder_addrinfo_by_preference(rdpContext* context, struct addrinfo* addr) ++{ ++ WINPR_ASSERT(context); ++ WINPR_ASSERT(addr); ++ ++ const BOOL preferIPv6 = ++ freerdp_settings_get_bool(context->settings, FreeRDP_PreferIPv6OverIPv4); ++ if (!preferIPv6) ++ return addr; ++ ++ struct addrinfo* ipv6Head = NULL; ++ struct addrinfo* ipv6Tail = NULL; ++ struct addrinfo* otherHead = NULL; ++ struct addrinfo* otherTail = NULL; ++ ++ /* Partition the list into IPv6 and other addresses */ ++ while (addr) ++ { ++ struct addrinfo* next = addr->ai_next; ++ addr->ai_next = NULL; ++ ++ if (addr->ai_family == AF_INET6) ++ { ++ if (!ipv6Head) ++ ipv6Head = addr; ++ else ++ ipv6Tail->ai_next = addr; ++ ipv6Tail = addr; ++ } ++ else ++ { ++ if (!otherHead) ++ otherHead = addr; ++ else ++ otherTail->ai_next = addr; ++ otherTail = addr; ++ } ++ addr = next; ++ } ++ ++ /* Concatenate the lists */ ++ if (ipv6Tail) ++ ipv6Tail->ai_next = otherHead; ++ ++ return ipv6Head ? ipv6Head : otherHead; ++} ++ + static int get_next_addrinfo(rdpContext* context, struct addrinfo* input, struct addrinfo** result, + UINT32 errorCode) + { +@@ -1091,14 +1138,6 @@ static int get_next_addrinfo(rdpContext* context, struct addrinfo* input, struct + if (!addr) + goto fail; + +- if (freerdp_settings_get_bool(context->settings, FreeRDP_PreferIPv6OverIPv4)) +- { +- while (addr && (addr->ai_family != AF_INET6)) +- addr = addr->ai_next; +- if (!addr) +- addr = input; +- } +- + /* We want to force IPvX, abort if not detected */ + const UINT32 IPvX = freerdp_settings_get_uint32(context->settings, FreeRDP_ForceIPvX); + switch (IPvX) +@@ -1237,9 +1276,11 @@ int freerdp_tcp_default_connect(rdpContext* context, rdpSettings* settings, cons + + /* By default we take the first returned entry. + * +- * If PreferIPv6OverIPv4 = TRUE we force to IPv6 if there +- * is such an address available, but fall back to first if not found ++ * If PreferIPv6OverIPv4 = TRUE we reorder addresses by preference: ++ * IPv6 addresses come first, then other addresses. + */ ++ result = reorder_addrinfo_by_preference(context, result); ++ + const int rc = + get_next_addrinfo(context, result, &addr, FREERDP_ERROR_DNS_NAME_NOT_FOUND); + if (rc < 0) +-- +2.52.0 + diff --git a/core-tcp-Try-next-DNS-entry-on-connect-failure.patch b/core-tcp-Try-next-DNS-entry-on-connect-failure.patch new file mode 100644 index 0000000..c225276 --- /dev/null +++ b/core-tcp-Try-next-DNS-entry-on-connect-failure.patch @@ -0,0 +1,76 @@ +From e4adf227177fd807f47f8621a5e2568748619a23 Mon Sep 17 00:00:00 2001 +From: Ondrej Holy +Date: Wed, 14 Jan 2026 11:34:53 +0100 +Subject: [PATCH] [core,tcp] Try next DNS entry on connect failure + +Backport of commit bd67348eb3380a66b544835346191bd2138a5ba4. + +Co-Authored-By: Claude +--- + libfreerdp/core/tcp.c | 41 ++++++++++++++++++++++------------------- + 1 file changed, 22 insertions(+), 19 deletions(-) + +diff --git a/libfreerdp/core/tcp.c b/libfreerdp/core/tcp.c +index 53e3a412f..8e7ee20bd 100644 +--- a/libfreerdp/core/tcp.c ++++ b/libfreerdp/core/tcp.c +@@ -1241,34 +1241,37 @@ int freerdp_tcp_default_connect(rdpContext* context, rdpSettings* settings, cons + do + { + sockfd = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol); ++ if (sockfd >= 0) ++ { ++ if ((peerAddress = freerdp_tcp_address_to_string( ++ (const struct sockaddr_storage*)addr->ai_addr, NULL)) != NULL) ++ { ++ WLog_DBG(TAG, "connecting to peer %s", peerAddress); ++ free(peerAddress); ++ } ++ ++ if (!freerdp_tcp_connect_timeout(context, sockfd, addr->ai_addr, ++ addr->ai_addrlen, timeout)) ++ { ++ close(sockfd); ++ sockfd = -1; ++ } ++ } ++ + if (sockfd < 0) + { + const int lrc = get_next_addrinfo(context, addr->ai_next, &addr, + FREERDP_ERROR_CONNECT_FAILED); + if (lrc < 0) ++ { ++ freeaddrinfo(result); ++ freerdp_set_last_error_if_not(context, FREERDP_ERROR_CONNECT_FAILED); ++ WLog_ERR(TAG, "failed to connect to %s", hostname); + return lrc; ++ } + } + } while (sockfd < 0); + +- if ((peerAddress = freerdp_tcp_address_to_string( +- (const struct sockaddr_storage*)addr->ai_addr, NULL)) != NULL) +- { +- WLog_DBG(TAG, "connecting to peer %s", peerAddress); +- free(peerAddress); +- } +- +- if (!freerdp_tcp_connect_timeout(context, sockfd, addr->ai_addr, addr->ai_addrlen, +- timeout)) +- { +- freeaddrinfo(result); +- close(sockfd); +- +- freerdp_set_last_error_if_not(context, FREERDP_ERROR_CONNECT_FAILED); +- +- WLog_ERR(TAG, "failed to connect to %s", hostname); +- return -1; +- } +- + freeaddrinfo(result); + } + } +-- +2.52.0 + diff --git a/core-tcp-fix-double-free-in-get_next_addrinfo.patch b/core-tcp-fix-double-free-in-get_next_addrinfo.patch new file mode 100644 index 0000000..04759ec --- /dev/null +++ b/core-tcp-fix-double-free-in-get_next_addrinfo.patch @@ -0,0 +1,39 @@ +From 0a660bf3b3cf2660a4fa15bd37d8b8b3d03fbfef Mon Sep 17 00:00:00 2001 +From: Ondrej Holy +Date: Wed, 14 Jan 2026 12:55:42 +0100 +Subject: [PATCH] [core,tcp] fix double free in get_next_addrinfo + +Backport of commit 48197426444b7b3587874b5eae175af1113beab8. + +Co-Authored-By: Claude +--- + libfreerdp/core/tcp.c | 5 ++++- + 1 file changed, 4 insertions(+), 1 deletion(-) + +diff --git a/libfreerdp/core/tcp.c b/libfreerdp/core/tcp.c +index 83cc9af1e..a3e12d14b 100644 +--- a/libfreerdp/core/tcp.c ++++ b/libfreerdp/core/tcp.c +@@ -1164,7 +1164,7 @@ static int get_next_addrinfo(rdpContext* context, struct addrinfo* input, struct + + fail: + freerdp_set_last_error_if_not(context, errorCode); +- freeaddrinfo(input); ++ *result = NULL; + return -1; + } + +@@ -1284,7 +1284,10 @@ int freerdp_tcp_default_connect(rdpContext* context, rdpSettings* settings, cons + const int rc = + get_next_addrinfo(context, result, &addr, FREERDP_ERROR_DNS_NAME_NOT_FOUND); + if (rc < 0) ++ { ++ freeaddrinfo(result); + return rc; ++ } + + do + { +-- +2.52.0 + diff --git a/freerdp.spec b/freerdp.spec index d9b29c2..161a9dc 100644 --- a/freerdp.spec +++ b/freerdp.spec @@ -30,7 +30,7 @@ Name: freerdp Epoch: 2 Version: 3.10.3 -Release: 5%{?dist}.8 +Release: 12%{?dist}.6 Summary: Free implementation of the Remote Desktop Protocol (RDP) # The effective license is Apache-2.0 but: @@ -56,6 +56,15 @@ Patch2: Use-default-threadpool.patch Patch3: Default-minimum-thread-count.patch Patch4: Limit-minimum-threadpool-size.patch +# https://issues.redhat.com/browse/RHEL-73724 +Patch: core-connection-print-SSL-warnings-after-init.patch + +# https://issues.redhat.com/browse/RHEL-140099 +Patch: core-tcp-Try-next-DNS-entry-on-connect-failure.patch +Patch: core-tcp-Don-t-ignore-connect-errors.patch +Patch: core-tcp-Fix-PreferIPv6OverIPv4-fallback-to-IPv4-add.patch +Patch: core-tcp-fix-double-free-in-get_next_addrinfo.patch + # https://github.com/FreeRDP/FreeRDP/commit/c4a7c371342edf0d307cea728f56d3302f0ab38c Patch: gdi-gfx-properly-clamp-SurfaceToSurface.patch @@ -227,6 +236,40 @@ Patch: codec-clear-update-clear_glyph_entry-count-after-alloc.patch # https://github.com/FreeRDP/FreeRDP/commit/a48dbde2c8a5b8b70a9d1c045d969a71afd6284c Patch: cache-persist-use-winpr_aligned_calloc.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 +Patch: client-x11-lock-appwindow.patch +Patch: client-x11-improve-rails-window-locking.patch +Patch: client-x11-refactor-locking.patch +Patch: client-x11-fix-deadlock-on-output-expose.patch + +# CVE-2026-40033 +# https://github.com/FreeRDP/FreeRDP/commit/f951d8677ce6d34d9778b951f73b3072b01853cb +Patch: gdi-gfx-fix-bounds-checks.patch + +# CVE-2026-44421 +# https://github.com/FreeRDP/FreeRDP/commit/b877c8c0fef1b5ccda29ccd5a1a58696486545ed +Patch: gdi-gfx-ensure-the-cache-element-can-hold-the-data.patch + +# CVE-2026-45700 +# https://github.com/FreeRDP/FreeRDP/commit/4a065a941ae134a0433d1497c03b3c3eb91b9f85 +Patch: codec-planar-fix-bounds-checks.patch + +# CVE-2026-44420 +# https://github.com/FreeRDP/FreeRDP/commit/0fba1f4dbff7585c9c99873d7c07d7dac46510c3 +# https://github.com/FreeRDP/FreeRDP/commit/d00dc5d3be369fb400344eb12e6882db2ee184f0 +Patch: channels-cliprdr-validate-capabilitySetLength-in-server-caps.patch +Patch: channels-cliprdr-abort-on-duplicate-caps.patch + +# CVE-2026-44422 +# https://github.com/FreeRDP/FreeRDP/commit/668fcb49d4856fad28f685db54a572af2a284b50 +# https://github.com/FreeRDP/FreeRDP/commit/ae03a9ff981ce7be1ab09dba2cd319d54984f910 +Patch: channels-rdpear-fix-ndr_read-checks.patch +Patch: channels-rdpear-disable-ndr-pointer-aliasing.patch + BuildRequires: gcc BuildRequires: gcc-c++ BuildRequires: alsa-lib-devel @@ -398,6 +441,7 @@ find . -name "*.c" -exec chmod 664 {} \; -DWITH_TIMEZONE_COMPILED=OFF \ -DWITH_TIMEZONE_FROM_FILE=ON \ -DWITH_URIPARSER=%{?_with_uriparser:ON}%{?!_with_uriparser:OFF} \ + -DWITH_VERBOSE_WINPR_ASSERT=OFF \ -DWITH_VIDEO_FFMPEG=%{?_with_ffmpeg:ON}%{?!_with_ffmpeg:OFF} \ -DWITH_WAYLAND=ON \ -DWITH_WEBVIEW=%{?_with_webview:ON}%{?!_with_webview:OFF} \ @@ -549,7 +593,16 @@ find %{buildroot} -name "*.a" -delete %{_libdir}/pkgconfig/winpr-tools3.pc %changelog -* Wed Apr 29 2026 Ondrej Holy - 2:3.10.3-5.8 +* Mon Jun 29 2026 Ondrej Holy - 2:3.10.3-12.6 +- Backport several CVE fixes (CVE-2026-40033, CVE-2026-44420, CVE-2026-44421, + CVE-2026-44422, CVE-2026-45700) + Resolves: RHEL-186978, RHEL-186967, RHEL-186958, RHEL-186950, RHEL-186093 + +* Tue May 05 2026 Ondrej Holy - 2:3.10.3-12.5 +- Lock appWindow to fix use-after-free in RAIL mode (CVE-2026-25952) + Resolves: RHEL-159848 + +* Wed Apr 29 2026 Ondrej Holy - 2:3.10.3-12.4 - Fix double free in xf_rail_window_common cleanup (CVE-2026-26986) - Fix clipboard use-after-free during auto-reconnect (CVE-2026-25997) - Fix heap-buffer-overflow in bitmap_cache_put (CVE-2026-29775) @@ -559,37 +612,48 @@ find %{buildroot} -name "*.a" -delete - Update PERSISTENT_CACHE_ENTRY::size after realloc (CVE-2026-33987) - Update CLEAR_GLYPH_ENTRY::count after alloc (CVE-2026-33985) - Use winpr_aligned_calloc in persistent cache (CVE-2026-33982) - Resolves: RHEL-159803, RHEL-159659, RHEL-161033, RHEL-161468 - Resolves: RHEL-161504, RHEL-161071, RHEL-163653, RHEL-167791, RHEL-162930 + Resolves: RHEL-159804, RHEL-159660, RHEL-161034, RHEL-161469 + Resolves: RHEL-161505, RHEL-161072, RHEL-163654, RHEL-168462, RHEL-162931 -* Fri Apr 10 2026 Ondrej Holy - 2:3.10.3-5.6 +* Fri Apr 10 2026 Ondrej Holy - 2:3.10.3-12.2 - Update CLEAR_VBAR_ENTRY size after alloc (CVE-2026-33984) - Fail progressive_rfx_quant_sub on invalid values (CVE-2026-33983) - Resolves: RHEL-162946, RHEL-162962 + Resolves: RHEL-162947, RHEL-162963 -* Tue Mar 31 2026 Ondrej Holy - 2:3.10.3-5.5 +* Thu Apr 09 2026 Ondrej Holy - 2:3.10.3-12.1 +- Rebuilt for errata + Resolves: RHEL-155980 + +* Tue Mar 31 2026 Ondrej Holy - 2:3.10.3-12 - Fix use of nsc_process_message - Increase timeout for TestSynchCritical - Resolves: RHEL-155979 + Resolves: RHEL-155980 -* Fri Mar 27 2026 Ondrej Holy - 2:3.10.3-5.4 +* Fri Mar 27 2026 Ondrej Holy - 2:3.10.3-11 - Backport several CVE fixes - Resolves: RHEL-147948, RHEL-147949, RHEL-147956, RHEL-147963, RHEL-147964 - Resolves: RHEL-147972, RHEL-147979, RHEL-147984, RHEL-147985, RHEL-148898 - Resolves: RHEL-148978, RHEL-148984, RHEL-149051, RHEL-155979 + Resolves: RHEL-147950, RHEL-147951, RHEL-147966, RHEL-147967, RHEL-147971 + Resolves: RHEL-147997, RHEL-147998, RHEL-148006, RHEL-148007, RHEL-148900 + Resolves: RHEL-148986, RHEL-148989, RHEL-149053, RHEL-155980 -* Wed Mar 25 2026 Ondrej Holy - 2:3.10.3-5.3 +* Wed Mar 25 2026 Ondrej Holy - 2:3.10.3-10 - Backport several CVE fixes - Resolves: RHEL-151975, RHEL-152202 + Resolves: RHEL-151976, RHEL-152203 -* Tue Feb 17 2026 Ondrej Holy - 2:3.10.3-5.2 +* Tue Feb 17 2026 Ondrej Holy - 2:3.10.3-9 - Backport several CVE fixes - Resolves: RHEL-147912, RHEL-148815, RHEL-148859, RHEL-148892, RHEL-148973 + Resolves: RHEL-147913, RHEL-148817, RHEL-148861, RHEL-148977, RHEL-148894 -* Tue Jan 27 2026 Ondrej Holy - 2:3.10.3-5.1 +* Tue Jan 27 2026 Ondrej Holy - 2:3.10.3-8 - Backport several CVE fixes - Resolves: RHEL-142413, RHEL-142397, RHEL-142381, RHEL-142365, RHEL-142349 - Resolves: RHEL-142333, RHEL-142317 + Resolves: RHEL-142414, RHEL-142398, RHEL-142382, RHEL-142366, RHEL-142350 + Resolves: RHEL-142334, RHEL-142318 + +* Fri Jan 16 2026 Ondrej Holy - 2:3.10.3-7 +- Try next DNS entry on connect failure + Resolves: RHEL-140099 + +* Tue Dec 16 2025 Ondrej Holy - 2:3.10.3-6 +- Fix broken SSL checks and disable runtime checks (RHEL-73724) * Tue Sep 30 2025 Marek Kasik - 2:3.10.3-5 - Silence abidiff diff --git a/gdi-gfx-ensure-the-cache-element-can-hold-the-data.patch b/gdi-gfx-ensure-the-cache-element-can-hold-the-data.patch new file mode 100644 index 0000000..3fdbc65 --- /dev/null +++ b/gdi-gfx-ensure-the-cache-element-can-hold-the-data.patch @@ -0,0 +1,28 @@ +From 45898fbb38d755d2c3211ed6064e13ee9815f24c Mon Sep 17 00:00:00 2001 +From: Armin Novak +Date: Thu, 30 Apr 2026 10:49:01 +0200 +Subject: [PATCH] [gdi,gfx] ensure the cache element can hold the data + +For future color format compatibility ensure that the bytes per +pixel are MAX(4, FreeRDPGetBytesPerPixel(format)) +--- + libfreerdp/gdi/gfx.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/libfreerdp/gdi/gfx.c b/libfreerdp/gdi/gfx.c +index d22405445..7ac597d92 100644 +--- a/libfreerdp/gdi/gfx.c ++++ b/libfreerdp/gdi/gfx.c +@@ -1414,7 +1414,9 @@ static gdiGfxCacheEntry* gdi_GfxCacheEntryNew(UINT64 cacheKey, UINT32 width, UIN + cacheEntry->width = width; + cacheEntry->height = height; + cacheEntry->format = format; +- cacheEntry->scanline = gfx_align_scanline(cacheEntry->width * 4, 16); ++ ++ const UINT32 bpp = MAX(4, FreeRDPGetBytesPerPixel(format)); ++ cacheEntry->scanline = gfx_align_scanline(cacheEntry->width * bpp, 16); + + if ((cacheEntry->width > 0) && (cacheEntry->height > 0)) + { +-- +2.52.0 diff --git a/gdi-gfx-fix-bounds-checks.patch b/gdi-gfx-fix-bounds-checks.patch new file mode 100644 index 0000000..81f8add --- /dev/null +++ b/gdi-gfx-fix-bounds-checks.patch @@ -0,0 +1,31 @@ +From f2b44427946bf42128609b66b4f6623c9b58a482 Mon Sep 17 00:00:00 2001 +From: Armin Novak +Date: Thu, 30 Apr 2026 10:04:24 +0200 +Subject: [PATCH] [gdi,gfx] fix bounds checks + +--- + libfreerdp/gdi/gfx.c | 8 +++++--- + 1 file changed, 5 insertions(+), 3 deletions(-) + +diff --git a/libfreerdp/gdi/gfx.c b/libfreerdp/gdi/gfx.c +index 3cbea681d..d22405445 100644 +--- a/libfreerdp/gdi/gfx.c ++++ b/libfreerdp/gdi/gfx.c +@@ -1519,10 +1519,12 @@ static UINT gdi_CacheToSurface(RdpgfxClientContext* context, + if (!is_rect_valid(&rect, surface->width, surface->height)) + goto fail; + ++ const UINT32 w = rect.right - rect.left; ++ const UINT32 h = rect.bottom - rect.top; + if (!freerdp_image_copy_no_overlap(surface->data, surface->format, surface->scanline, +- destPt->x, destPt->y, cacheEntry->width, +- cacheEntry->height, cacheEntry->data, cacheEntry->format, +- cacheEntry->scanline, 0, 0, NULL, FREERDP_FLIP_NONE)) ++ destPt->x, destPt->y, w, h, cacheEntry->data, ++ cacheEntry->format, cacheEntry->scanline, 0, 0, NULL, ++ FREERDP_FLIP_NONE)) + goto fail; + + invalidRect = rect; +-- +2.52.0