From 1994e9844212a6dfe0ff12309fef520e888986b5 Mon Sep 17 00:00:00 2001
From: Armin Novak <armin.novak@thincast.com>
Date: Mon, 9 Feb 2026 16:09:51 +0100
Subject: [PATCH] [client,x11] lock appWindow

When using xf_rail_get_window lock the hash talbe until xf_rail_return_window
---
 client/X11/xf_event.c    | 47 +++++++++++++++++++++++------------
 client/X11/xf_graphics.c | 10 +++++---
 client/X11/xf_rail.c     | 53 ++++++++++++++++++++++++++++------------
 client/X11/xf_rail.h     |  4 +++
 client/X11/xf_window.c   |  6 +++++
 client/X11/xf_window.h   |  4 +++
 6 files changed, 89 insertions(+), 35 deletions(-)

Index: freerdp-2.11.7/client/X11/xf_event.c
===================================================================
--- freerdp-2.11.7.orig/client/X11/xf_event.c
+++ freerdp-2.11.7/client/X11/xf_event.c
@@ -388,8 +388,10 @@ BOOL xf_generic_MotionNotify(xfContext*
 	if (app)
 	{
 		/* make sure window exists */
-		if (!xf_AppWindowFromX11Window(xfc, window))
-			return TRUE;
+		xfAppWindow* appWindow = xf_AppWindowFromX11Window(xfc, window);
+		xf_rail_return_window(appWindow);
+		if (!appWindow)
+            return TRUE;
 
 		/* Translate to desktop coordinates */
 		XTranslateCoordinates(xfc->display, window, RootWindowOfScreen(xfc->screen), x, y, &x, &y,
@@ -465,7 +467,9 @@ BOOL xf_generic_ButtonEvent(xfContext* x
 			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 */
@@ -577,6 +581,7 @@ static BOOL xf_event_FocusIn(xfContext*
 		{
 			xf_rail_adjust_position(xfc, appWindow);
 		}
+		xf_rail_return_window(appWindow);
 	}
 
 	xf_keyboard_focus_in(xfc);
@@ -627,11 +632,12 @@ static BOOL xf_event_ClientMessage(xfCon
 			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 TRUE;
 		}
 		else
 		{
@@ -665,6 +671,7 @@ static BOOL xf_event_EnterNotify(xfConte
 
 		/* 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 +691,7 @@ static BOOL xf_event_LeaveNotify(xfConte
 		/* 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 +782,7 @@ static BOOL xf_event_ConfigureNotify(xfC
 					xf_rail_adjust_position(xfc, appWindow);
 			}
 		}
+		xf_rail_return_window(appWindow);
 	}
 	return xf_pointer_update_scale(xfc);
 }
@@ -798,6 +807,7 @@ static BOOL xf_event_MapNotify(xfContext
 			// xf_rail_send_client_system_command(xfc, appWindow->windowId, SC_RESTORE);
 			appWindow->is_mapped = TRUE;
 		}
+		xf_rail_return_window(appWindow);
 	}
 
 	return TRUE;
@@ -813,8 +823,7 @@ static BOOL xf_event_UnmapNotify(xfConte
 	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);
 
@@ -822,6 +831,7 @@ static BOOL xf_event_UnmapNotify(xfConte
 		{
 			appWindow->is_mapped = FALSE;
 		}
+        xf_rail_return_window(appWindow);
 	}
 
 	return TRUE;
@@ -856,7 +866,7 @@ static BOOL xf_event_PropertyNotify(xfCo
 			appWindow = xf_AppWindowFromX11Window(xfc, event->window);
 
 			if (!appWindow)
-				return TRUE;
+				goto fail;
 		}
 
 		if ((Atom)event->atom == xfc->_NET_WM_STATE)
@@ -950,6 +960,9 @@ static BOOL xf_event_PropertyNotify(xfCo
 		}
 		else if (minimizedChanged)
 			gdi_send_suppress_output(xfc->context.gdi, minimized);
+
+	fail:
+		xf_rail_return_window(appWindow);
 	}
 
 	return TRUE;
Index: freerdp-2.11.7/client/X11/xf_graphics.c
===================================================================
--- freerdp-2.11.7.orig/client/X11/xf_graphics.c
+++ freerdp-2.11.7/client/X11/xf_graphics.c
@@ -371,12 +371,14 @@ static Window xf_Pointer_get_window(xfCo
 	}
 	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
 	{
Index: freerdp-2.11.7/client/X11/xf_rail.c
===================================================================
--- freerdp-2.11.7.orig/client/X11/xf_rail.c
+++ freerdp-2.11.7/client/X11/xf_rail.c
@@ -132,6 +132,7 @@ void xf_rail_send_activate(xfContext* xf
 	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)
@@ -691,6 +692,7 @@ static void xf_rail_set_window_icon(xfCo
 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;
@@ -707,23 +709,25 @@ static BOOL xf_rail_window_icon(rdpConte
 	{
 		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;
@@ -740,12 +744,15 @@ static BOOL xf_rail_window_cached_icon(r
 	{
 		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,
@@ -1046,6 +1053,7 @@ static UINT xf_rail_server_local_move_si
 	else
 		xf_EndLocalMoveSize(xfc, appWindow);
 
+	xf_rail_return_window(appWindow);
 	return CHANNEL_RC_OK;
 }
 
@@ -1067,6 +1075,7 @@ static UINT xf_rail_server_min_max_info(
 		                       minMaxInfo->minTrackHeight, minMaxInfo->maxTrackWidth,
 		                       minMaxInfo->maxTrackHeight);
 	}
+	xf_rail_return_window(appWindow);
 
 	return CHANNEL_RC_OK;
 }
@@ -1224,7 +1233,20 @@ xfAppWindow* xf_rail_get_window(xfContex
 		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);
 }
Index: freerdp-2.11.7/client/X11/xf_rail.h
===================================================================
--- freerdp-2.11.7.orig/client/X11/xf_rail.h
+++ freerdp-2.11.7/client/X11/xf_rail.h
@@ -24,6 +24,7 @@
 #include "xfreerdp.h"
 
 #include <freerdp/client/rail.h>
+#include <winpr/winpr.h>
 
 void xf_rail_paint(xfContext* xfc, INT32 uleft, INT32 utop, UINT32 uright, UINT32 ubottom);
 void xf_rail_send_client_system_command(xfContext* xfc, UINT32 windowId, UINT16 command);
@@ -35,6 +36,9 @@ void xf_rail_disable_remoteapp_mode(xfCo
 
 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);
+
+WINPR_ATTR_MALLOC(xf_rail_return_window, 1)
 xfAppWindow* xf_rail_get_window(xfContext* xfc, UINT64 id);
 
 BOOL xf_rail_del_window(xfContext* xfc, UINT64 id);
Index: freerdp-2.11.7/client/X11/xf_window.c
===================================================================
--- freerdp-2.11.7.orig/client/X11/xf_window.c
+++ freerdp-2.11.7/client/X11/xf_window.c
@@ -1122,6 +1122,7 @@ xfAppWindow* xf_AppWindowFromX11Window(x
 	int count;
 	ULONG_PTR* pKeys = NULL;
 	xfAppWindow* appWindow;
+	HashTable_Lock(xfc->railWindows);
 	count = HashTable_GetKeys(xfc->railWindows, &pKeys);
 
 	for (index = 0; index < count; index++)
@@ -1129,15 +1130,18 @@ xfAppWindow* xf_AppWindowFromX11Window(x
 		appWindow = xf_rail_get_window(xfc, *(UINT64*)pKeys[index]);
 
 		if (!appWindow)
+			HashTable_Unlock(xfc->railWindows);
 			return NULL;
 
 		if (appWindow->handle == wnd)
 		{
+			HashTable_Unlock(xfc->railWindows);
 			free(pKeys);
 			return appWindow;
 		}
 	}
 
+	HashTable_Unlock(xfc->railWindows);
 	free(pKeys);
 	return NULL;
 }
Index: freerdp-2.11.7/client/X11/xf_window.h
===================================================================
--- freerdp-2.11.7.orig/client/X11/xf_window.h
+++ freerdp-2.11.7/client/X11/xf_window.h
@@ -23,6 +23,7 @@
 #include <X11/Xlib.h>
 
 #include <freerdp/freerdp.h>
+#include <winpr/winpr.h>
 
 typedef struct xf_app_window xfAppWindow;
 
@@ -187,6 +188,9 @@ void xf_SetWindowMinMaxInfo(xfContext* x
                             int maxTrackWidth, int maxTrackHeight);
 void xf_StartLocalMoveSize(xfContext* xfc, xfAppWindow* appWindow, int direction, int x, int y);
 void xf_EndLocalMoveSize(xfContext* xfc, xfAppWindow* appWindow);
+void xf_rail_return_window(xfAppWindow* window);
+
+WINPR_ATTR_MALLOC(xf_rail_return_window, 1);
 xfAppWindow* xf_AppWindowFromX11Window(xfContext* xfc, Window wnd);
 
 #endif /* FREERDP_CLIENT_X11_WINDOW_H */
Index: freerdp-2.11.7/winpr/include/winpr/collections.h
===================================================================
--- freerdp-2.11.7.orig/winpr/include/winpr/collections.h
+++ freerdp-2.11.7/winpr/include/winpr/collections.h
@@ -374,6 +374,9 @@ extern "C"
 	WINPR_API wHashTable* HashTable_New(BOOL synchronized);
 	WINPR_API void HashTable_Free(wHashTable* table);
 
+	WINPR_API void HashTable_Lock(wHashTable* table);
+	WINPR_API void HashTable_Unlock(wHashTable* table);
+
 	/* BufferPool */
 
 	struct _wBufferPoolItem
Index: freerdp-2.11.7/winpr/libwinpr/utils/collections/HashTable.c
===================================================================
--- freerdp-2.11.7.orig/winpr/libwinpr/utils/collections/HashTable.c
+++ freerdp-2.11.7/winpr/libwinpr/utils/collections/HashTable.c
@@ -22,7 +22,7 @@
 #endif
 
 #include <winpr/crt.h>
-
+#include <winpr/assert.h>
 #include <winpr/collections.h>
 
 /**
@@ -636,3 +636,15 @@ void HashTable_Free(wHashTable* table)
 		free(table);
 	}
 }
+
+void HashTable_Lock(wHashTable* table)
+{
+	WINPR_ASSERT(table);
+	EnterCriticalSection(&table->lock);
+}
+
+void HashTable_Unlock(wHashTable* table)
+{
+	WINPR_ASSERT(table);
+	LeaveCriticalSection(&table->lock);
+}
