You've already forked wine-staging
mirror of
https://gitlab.winehq.org/wine/wine-staging.git
synced 2025-04-13 14:42:51 -07:00
Added patch to fix possible leak of explorer.exe processes and implement proper desktop refcounting.
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,253 @@
|
||||
From dd59d5fa25b8a20842e565d656c2b0f7833dae6c Mon Sep 17 00:00:00 2001
|
||||
From: Sebastian Lackner <sebastian@fds-team.de>
|
||||
Date: Fri, 4 Dec 2015 01:22:29 +0100
|
||||
Subject: server: Track desktop handle count more correctly.
|
||||
|
||||
Desktop objects should stay valid, as long as there is a handle from a
|
||||
(non-system) process. Counting only process->desktop references is not
|
||||
sufficient in practice, and causes explorer.exe process leaks.
|
||||
|
||||
Signed-off-by: Sebastian Lackner <sebastian@fds-team.de>
|
||||
---
|
||||
programs/explorer/desktop.c | 18 +++++++++-
|
||||
server/handle.c | 3 +-
|
||||
server/handle.h | 2 +-
|
||||
server/process.c | 1 -
|
||||
server/winstation.c | 88 +++++++++++++++++++++++----------------------
|
||||
5 files changed, 65 insertions(+), 47 deletions(-)
|
||||
|
||||
diff --git a/programs/explorer/desktop.c b/programs/explorer/desktop.c
|
||||
index 2b8502b..b59ed4e 100644
|
||||
--- a/programs/explorer/desktop.c
|
||||
+++ b/programs/explorer/desktop.c
|
||||
@@ -37,6 +37,8 @@
|
||||
|
||||
WINE_DEFAULT_DEBUG_CHANNEL(explorer);
|
||||
|
||||
+extern HANDLE CDECL __wine_make_process_system(void);
|
||||
+
|
||||
#define DESKTOP_CLASS_ATOM ((LPCWSTR)MAKEINTATOM(32769))
|
||||
#define DESKTOP_ALL_ACCESS 0x01ff
|
||||
|
||||
@@ -1024,8 +1026,22 @@ void manage_desktop( WCHAR *arg )
|
||||
/* run the desktop message loop */
|
||||
if (hwnd)
|
||||
{
|
||||
+ HANDLE exit_event = __wine_make_process_system();
|
||||
WINE_TRACE( "desktop message loop starting on hwnd %p\n", hwnd );
|
||||
- while (GetMessageW( &msg, 0, 0, 0 )) DispatchMessageW( &msg );
|
||||
+ while (exit_event != NULL && MsgWaitForMultipleObjectsEx( 1,
|
||||
+ &exit_event, INFINITE, QS_ALLINPUT, 0 ) == WAIT_OBJECT_0 + 1)
|
||||
+ {
|
||||
+ while (PeekMessageW( &msg, NULL, 0, 0, PM_REMOVE ))
|
||||
+ {
|
||||
+ if (msg.message == WM_QUIT)
|
||||
+ {
|
||||
+ exit_event = NULL;
|
||||
+ break;
|
||||
+ }
|
||||
+ TranslateMessage( &msg );
|
||||
+ DispatchMessageW( &msg );
|
||||
+ }
|
||||
+ }
|
||||
WINE_TRACE( "desktop message loop exiting for hwnd %p\n", hwnd );
|
||||
}
|
||||
|
||||
diff --git a/server/handle.c b/server/handle.c
|
||||
index 64db8fc..63dbf4e 100644
|
||||
--- a/server/handle.c
|
||||
+++ b/server/handle.c
|
||||
@@ -487,7 +487,7 @@ obj_handle_t find_inherited_handle( struct process *process, const struct object
|
||||
/* enumerate handles of a given type */
|
||||
/* this is needed for window stations and desktops */
|
||||
obj_handle_t enumerate_handles( struct process *process, const struct object_ops *ops,
|
||||
- unsigned int *index )
|
||||
+ obj_handle_t *index, struct object **obj )
|
||||
{
|
||||
struct handle_table *table = process->handles;
|
||||
unsigned int i;
|
||||
@@ -500,6 +500,7 @@ obj_handle_t enumerate_handles( struct process *process, const struct object_ops
|
||||
if (!entry->ptr) continue;
|
||||
if (entry->ptr->ops != ops) continue;
|
||||
*index = i + 1;
|
||||
+ if (obj) *obj = grab_object( entry->ptr );
|
||||
return index_to_handle(i);
|
||||
}
|
||||
return 0;
|
||||
diff --git a/server/handle.h b/server/handle.h
|
||||
index 821c4ef..583a25a 100644
|
||||
--- a/server/handle.h
|
||||
+++ b/server/handle.h
|
||||
@@ -48,7 +48,7 @@ extern obj_handle_t open_object( const struct namespace *namespace, const struct
|
||||
const struct object_ops *ops, unsigned int access, unsigned int attr );
|
||||
extern obj_handle_t find_inherited_handle( struct process *process, const struct object_ops *ops );
|
||||
extern obj_handle_t enumerate_handles( struct process *process, const struct object_ops *ops,
|
||||
- unsigned int *index );
|
||||
+ unsigned int *index, struct object **obj );
|
||||
extern void close_process_handles( struct process *process );
|
||||
extern struct handle_table *alloc_handle_table( struct process *process, int count );
|
||||
extern struct handle_table *copy_handle_table( struct process *process, struct process *parent );
|
||||
diff --git a/server/process.c b/server/process.c
|
||||
index bc86c24..77771e5 100644
|
||||
--- a/server/process.c
|
||||
+++ b/server/process.c
|
||||
@@ -832,7 +832,6 @@ static void process_killed( struct process *process )
|
||||
|
||||
assert( list_empty( &process->thread_list ));
|
||||
process->end_time = current_time;
|
||||
- if (!process->is_system) close_process_desktop( process );
|
||||
process->winstation = 0;
|
||||
process->desktop = 0;
|
||||
close_process_handles( process );
|
||||
diff --git a/server/winstation.c b/server/winstation.c
|
||||
index c4e55e3..78bd24c 100644
|
||||
--- a/server/winstation.c
|
||||
+++ b/server/winstation.c
|
||||
@@ -51,6 +51,7 @@ static void winstation_destroy( struct object *obj );
|
||||
static unsigned int winstation_map_access( struct object *obj, unsigned int access );
|
||||
static void desktop_dump( struct object *obj, int verbose );
|
||||
static struct object_type *desktop_get_type( struct object *obj );
|
||||
+static void desktop_alloc_handle( struct object *obj, struct process *process, obj_handle_t handle );
|
||||
static int desktop_close_handle( struct object *obj, struct process *process, obj_handle_t handle );
|
||||
static void desktop_destroy( struct object *obj );
|
||||
static unsigned int desktop_map_access( struct object *obj, unsigned int access );
|
||||
@@ -93,7 +94,7 @@ static const struct object_ops desktop_ops =
|
||||
default_set_sd, /* set_sd */
|
||||
no_lookup_name, /* lookup_name */
|
||||
no_open_file, /* open_file */
|
||||
- no_alloc_handle, /* alloc_handle */
|
||||
+ desktop_alloc_handle, /* alloc_handle */
|
||||
desktop_close_handle, /* close_handle */
|
||||
desktop_destroy /* destroy */
|
||||
};
|
||||
@@ -261,14 +262,54 @@ static struct object_type *desktop_get_type( struct object *obj )
|
||||
return get_object_type( &str );
|
||||
}
|
||||
|
||||
+static void close_desktop_timeout( void *private )
|
||||
+{
|
||||
+ struct desktop *desktop = private;
|
||||
+
|
||||
+ desktop->close_timeout = NULL;
|
||||
+ unlink_named_object( &desktop->obj ); /* make sure no other process can open it */
|
||||
+ post_desktop_message( desktop, WM_CLOSE, 0, 0 ); /* and signal the owner to quit */
|
||||
+}
|
||||
+
|
||||
+/* remove a user of the desktop and start the close timeout if necessary */
|
||||
+static void remove_desktop_user( struct desktop *desktop )
|
||||
+{
|
||||
+ assert( desktop->users > 0 );
|
||||
+ desktop->users--;
|
||||
+
|
||||
+ if (!desktop->users && get_top_window_owner( desktop ))
|
||||
+ {
|
||||
+ assert( !desktop->close_timeout );
|
||||
+ desktop->close_timeout = add_timeout_user( -TICKS_PER_SEC, close_desktop_timeout, desktop );
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+static void desktop_alloc_handle( struct object *obj, struct process *process, obj_handle_t handle )
|
||||
+{
|
||||
+ struct desktop *desktop = (struct desktop *)obj;
|
||||
+ if (process->is_system) return;
|
||||
+
|
||||
+ desktop->users++;
|
||||
+ if (desktop->close_timeout)
|
||||
+ {
|
||||
+ remove_timeout_user( desktop->close_timeout );
|
||||
+ desktop->close_timeout = NULL;
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
static int desktop_close_handle( struct object *obj, struct process *process, obj_handle_t handle )
|
||||
{
|
||||
+ struct desktop *desktop = (struct desktop *)obj;
|
||||
struct thread *thread;
|
||||
|
||||
/* check if the handle is currently used by the process or one of its threads */
|
||||
if (process->desktop == handle) return 0;
|
||||
LIST_FOR_EACH_ENTRY( thread, &process->thread_list, struct thread, proc_entry )
|
||||
if (thread->desktop == handle) return 0;
|
||||
+
|
||||
+ if (!process->is_system)
|
||||
+ remove_desktop_user( desktop );
|
||||
+
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -276,6 +317,7 @@ static void desktop_destroy( struct object *obj )
|
||||
{
|
||||
struct desktop *desktop = (struct desktop *)obj;
|
||||
|
||||
+ assert( !desktop->users );
|
||||
free_hotkeys( desktop, 0 );
|
||||
if (desktop->top_window) destroy_window( desktop->top_window );
|
||||
if (desktop->msg_window) destroy_window( desktop->msg_window );
|
||||
@@ -302,40 +344,6 @@ struct desktop *get_thread_desktop( struct thread *thread, unsigned int access )
|
||||
return get_desktop_obj( thread->process, thread->desktop, access );
|
||||
}
|
||||
|
||||
-static void close_desktop_timeout( void *private )
|
||||
-{
|
||||
- struct desktop *desktop = private;
|
||||
-
|
||||
- desktop->close_timeout = NULL;
|
||||
- unlink_named_object( &desktop->obj ); /* make sure no other process can open it */
|
||||
- post_desktop_message( desktop, WM_CLOSE, 0, 0 ); /* and signal the owner to quit */
|
||||
-}
|
||||
-
|
||||
-/* add a user of the desktop and cancel the close timeout */
|
||||
-static void add_desktop_user( struct desktop *desktop )
|
||||
-{
|
||||
- desktop->users++;
|
||||
- if (desktop->close_timeout)
|
||||
- {
|
||||
- remove_timeout_user( desktop->close_timeout );
|
||||
- desktop->close_timeout = NULL;
|
||||
- }
|
||||
-}
|
||||
-
|
||||
-/* remove a user of the desktop and start the close timeout if necessary */
|
||||
-static void remove_desktop_user( struct desktop *desktop )
|
||||
-{
|
||||
- assert( desktop->users > 0 );
|
||||
- desktop->users--;
|
||||
-
|
||||
- /* if we have one remaining user, it has to be the manager of the desktop window */
|
||||
- if (desktop->users == 1 && get_top_window_owner( desktop ))
|
||||
- {
|
||||
- assert( !desktop->close_timeout );
|
||||
- desktop->close_timeout = add_timeout_user( -TICKS_PER_SEC, close_desktop_timeout, desktop );
|
||||
- }
|
||||
-}
|
||||
-
|
||||
/* set the process default desktop handle */
|
||||
void set_process_default_desktop( struct process *process, struct desktop *desktop,
|
||||
obj_handle_t handle )
|
||||
@@ -352,12 +360,6 @@ void set_process_default_desktop( struct process *process, struct desktop *deskt
|
||||
LIST_FOR_EACH_ENTRY( thread, &process->thread_list, struct thread, proc_entry )
|
||||
if (!thread->desktop) thread->desktop = handle;
|
||||
|
||||
- if (!process->is_system && desktop != old_desktop)
|
||||
- {
|
||||
- add_desktop_user( desktop );
|
||||
- if (old_desktop) remove_desktop_user( old_desktop );
|
||||
- }
|
||||
-
|
||||
if (old_desktop) release_object( old_desktop );
|
||||
}
|
||||
|
||||
@@ -407,8 +409,8 @@ done:
|
||||
void close_process_desktop( struct process *process )
|
||||
{
|
||||
struct desktop *desktop;
|
||||
-
|
||||
- if (process->desktop && (desktop = get_desktop_obj( process, process->desktop, 0 )))
|
||||
+ unsigned int i = 0;
|
||||
+ while (enumerate_handles( process, &desktop_ops, &i, (struct object **)&desktop ))
|
||||
{
|
||||
remove_desktop_user( desktop );
|
||||
release_object( desktop );
|
||||
--
|
||||
2.6.2
|
||||
|
1
patches/server-Desktop_Refcount/definition
Normal file
1
patches/server-Desktop_Refcount/definition
Normal file
@@ -0,0 +1 @@
|
||||
Fixes: Fix possible leak of explorer.exe processes and implement proper desktop refcounting
|
Reference in New Issue
Block a user