From: Jacek Caban Subject: [PATCH 3/8] server: Allow kernel to keep reference to server objects by pointer. Message-Id: <8908dbaf-3ee7-8238-e4e5-faaeb59de78d@codeweavers.com> Date: Wed, 27 Feb 2019 15:15:47 +0100 Signed-off-by: Jacek Caban --- At some point, I considered and prototyped storing a handle on client side as a handle. It lead into a number of problems. For example handle count was not right, breaking things like IRP_MJ_CLOSE implementation. A separated mechanism worked much better. dlls/ntoskrnl.exe/ntoskrnl.c | 45 ++++++++++++++++++++++++--- server/device.c | 60 ++++++++++++++++++++++++++++++++++++ server/protocol.def | 14 +++++++++ 3 files changed, 115 insertions(+), 4 deletions(-) diff --git a/dlls/ntoskrnl.exe/ntoskrnl.c b/dlls/ntoskrnl.exe/ntoskrnl.c index f98449798d..d7b13a3770 100644 --- a/dlls/ntoskrnl.exe/ntoskrnl.c +++ b/dlls/ntoskrnl.exe/ntoskrnl.c @@ -294,6 +294,14 @@ void *alloc_kernel_object( POBJECT_TYPE type, HANDLE handle, SIZE_T size, LONG r return header + 1; } +static CRITICAL_SECTION obref_cs; +static CRITICAL_SECTION_DEBUG obref_critsect_debug = +{ + 0, 0, &obref_cs, + { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList }, + 0, 0, { (DWORD_PTR)(__FILE__ ": obref_cs") } +}; +static CRITICAL_SECTION obref_cs = { &obref_critsect_debug, -1, 0, 0, 0, 0 }; /*********************************************************************** * ObDereferenceObject (NTOSKRNL.EXE.@) @@ -309,13 +317,28 @@ void WINAPI ObDereferenceObject( void *obj ) return; } - ref = InterlockedDecrement( &header->ref ); + EnterCriticalSection( &obref_cs ); + ref = --header->ref; TRACE( "(%p) ref=%u\n", obj, ref ); if (!ref) { - if (header->type->release) header->type->release( obj ); - else FIXME( "no destructor\n" ); + if (header->type->release) + { + header->type->release( obj ); + } + else + { + SERVER_START_REQ( release_kernel_object ) + { + req->manager = wine_server_obj_handle( get_device_manager() ); + req->user_ptr = wine_server_client_ptr( obj ); + if (wine_server_call( req )) ERR( "failed to release %p failed\n", obj ); + } + SERVER_END_REQ; + } } + + LeaveCriticalSection( &obref_cs ); } static void ObReferenceObject( void *obj ) @@ -329,8 +352,22 @@ static void ObReferenceObject( void *obj ) return; } - ref = InterlockedIncrement( &header->ref ); + EnterCriticalSection( &obref_cs ); + + ref = ++header->ref; TRACE( "(%p) ref=%u\n", obj, ref ); + if (ref == 1) + { + SERVER_START_REQ( grab_kernel_object ) + { + req->manager = wine_server_obj_handle( get_device_manager() ); + req->user_ptr = wine_server_client_ptr( obj ); + if (wine_server_call( req )) ERR( "failed to grab %p reference\n", obj ); + } + SERVER_END_REQ; + } + + LeaveCriticalSection( &obref_cs ); } static CRITICAL_SECTION handle_map_cs; diff --git a/server/device.c b/server/device.c index 50b3b1d0de..d5cd6e4d5c 100644 --- a/server/device.c +++ b/server/device.c @@ -224,6 +224,7 @@ struct kernel_object struct device_manager *manager; client_ptr_t user_ptr; struct object *object; + int owned; struct list list_entry; struct wine_rb_entry rb_entry; }; @@ -259,6 +260,7 @@ static struct kernel_object *set_kernel_object( struct device_manager *manager, kernel_object->manager = manager; kernel_object->user_ptr = user_ptr; kernel_object->object = obj; + kernel_object->owned = 0; if (wine_rb_put( &manager->kernel_objects, &user_ptr, &kernel_object->rb_entry )) { @@ -271,6 +273,21 @@ static struct kernel_object *set_kernel_object( struct device_manager *manager, return kernel_object; } +static struct kernel_object *kernel_object_from_ptr( struct device_manager *manager, client_ptr_t client_ptr ) +{ + struct wine_rb_entry *entry = wine_rb_get( &manager->kernel_objects, &client_ptr ); + return entry ? WINE_RB_ENTRY_VALUE( entry, struct kernel_object, rb_entry ) : NULL; +} + +static void grab_kernel_object( struct kernel_object *ptr ) +{ + if (!ptr->owned) + { + grab_object( ptr->object ); + ptr->owned = 1; + } +} + static void irp_call_dump( struct object *obj, int verbose ) { struct irp_call *irp = (struct irp_call *)obj; @@ -670,6 +687,7 @@ static void device_manager_destroy( struct object *obj ) kernel_object = WINE_RB_ENTRY_VALUE( manager->kernel_objects.root, struct kernel_object, rb_entry ); wine_rb_remove( &manager->kernel_objects, &kernel_object->rb_entry ); list_remove( &kernel_object->list_entry ); + if (kernel_object->owned) release_object( kernel_object->object ); free( kernel_object ); } @@ -700,6 +718,7 @@ void free_kernel_objects( struct list *kernel_objects ) while ((ptr = list_head( kernel_objects ))) { struct kernel_object *kernel_object = LIST_ENTRY( ptr, struct kernel_object, list_entry ); + assert( !kernel_object->owned ); list_remove( &kernel_object->list_entry ); wine_rb_remove( &kernel_object->manager->kernel_objects, &kernel_object->rb_entry ); free( kernel_object ); @@ -876,3 +895,44 @@ DECL_HANDLER(set_kernel_object_ptr) release_object( object ); release_object( manager ); } + + +/* grab server object reference from kernel object pointer */ +DECL_HANDLER(grab_kernel_object) +{ + struct device_manager *manager; + struct kernel_object *ref; + + if (!(manager = (struct device_manager *)get_handle_obj( current->process, req->manager, + 0, &device_manager_ops ))) + return; + + if ((ref = kernel_object_from_ptr( manager, req->user_ptr )) && !ref->owned + && ref->object->ops != &device_manager_ops /* avoid circular references */) + grab_kernel_object( ref ); + else + set_error( STATUS_INVALID_HANDLE ); + + release_object( manager ); +} + + +/* release server object reference from kernel object pointer */ +DECL_HANDLER(release_kernel_object) +{ + struct device_manager *manager; + struct kernel_object *ref; + + if (!(manager = (struct device_manager *)get_handle_obj( current->process, req->manager, + 0, &device_manager_ops ))) + return; + + if ((ref = kernel_object_from_ptr( manager, req->user_ptr )) && ref->owned) + { + ref->owned = 0; + release_object( ref->object ); + } + else set_error( STATUS_INVALID_HANDLE ); + + release_object( manager ); +} diff --git a/server/protocol.def b/server/protocol.def index 5faff89f58..fe6670b189 100644 --- a/server/protocol.def +++ b/server/protocol.def @@ -3638,6 +3638,20 @@ struct handle_info @END +/* Grab server object reference from kernel object pointer */ +@REQ(grab_kernel_object) + obj_handle_t manager; /* handle to the device manager */ + client_ptr_t user_ptr; /* kernel object pointer */ +@END + + +/* Release server object reference from kernel object pointer */ +@REQ(release_kernel_object) + obj_handle_t manager; /* handle to the device manager */ + client_ptr_t user_ptr; /* kernel object pointer */ +@END + + /* Make the current process a system process */ @REQ(make_process_system) @REPLY