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..6157356 --- /dev/null +++ b/client-x11-fix-deadlock-on-output-expose.patch @@ -0,0 +1,170 @@ +From a25a6b8c53602d2023dd0ad685000dc006179e94 Mon Sep 17 00:00:00 2001 +From: Ondrej Holy +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 + diff --git a/client-x11-improve-rails-window-locking.patch b/client-x11-improve-rails-window-locking.patch new file mode 100644 index 0000000..22dc762 --- /dev/null +++ b/client-x11-improve-rails-window-locking.patch @@ -0,0 +1,331 @@ +From e6edabe690ad8de63af327403e8371b6ef1319e2 Mon Sep 17 00:00:00 2001 +From: Ondrej Holy +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 + diff --git a/client-x11-lock-appwindow.patch b/client-x11-lock-appwindow.patch new file mode 100644 index 0000000..50988a3 --- /dev/null +++ b/client-x11-lock-appwindow.patch @@ -0,0 +1,404 @@ +From 8152c347e96c1e3af15cb2551d4efb6740fe9f87 Mon Sep 17 00:00:00 2001 +From: Ondrej Holy +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 + diff --git a/client-x11-refactor-locking.patch b/client-x11-refactor-locking.patch new file mode 100644 index 0000000..a945a39 --- /dev/null +++ b/client-x11-refactor-locking.patch @@ -0,0 +1,360 @@ +From eeb38c3565473c46da5dea6b43f597064e89b717 Mon Sep 17 00:00:00 2001 +From: Ondrej Holy +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 + diff --git a/freerdp.spec b/freerdp.spec index 71cb6f0..134e6a0 100644 --- a/freerdp.spec +++ b/freerdp.spec @@ -27,7 +27,7 @@ Name: freerdp Version: 2.11.7 -Release: 9%{?dist} +Release: 10%{?dist} Epoch: 2 Summary: Free implementation of the Remote Desktop Protocol (RDP) License: ASL 2.0 @@ -184,6 +184,16 @@ Patch: codec-dsp-fix-array-bounds-checks.patch # https://github.com/FreeRDP/FreeRDP/commit/c49d1ad43b8c7b32794d0250f2623c2dccd7ef25 Patch: 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 +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 + BuildRequires: gcc BuildRequires: gcc-c++ BuildRequires: alsa-lib-devel @@ -441,6 +451,10 @@ find %{buildroot} -name "*.a" -delete %{_libdir}/pkgconfig/winpr-tools2.pc %changelog +* Tue May 05 2026 Ondrej Holy - 2:2.11.7-10 +- Lock appWindow to fix use-after-free in RAIL mode (CVE-2026-25952) + Resolves: RHEL-168464 + * Tue Apr 28 2026 Ondrej Holy - 2:2.11.7-9 - Fix double free in xf_rail_window_common cleanup (CVE-2026-26986) - Fix growth of preallocated buffers (CVE-2026-27951)