[v2 PATCH] server: Implement inherited handles list.

Nikolay Sivov nsivov at codeweavers.com
Mon Aug 31 09:23:48 CDT 2020


Signed-off-by: Nikolay Sivov <nsivov at codeweavers.com>
---

Version 2 does not abuse startup info structure to pass handles,
adds tracing for new request data, along with other minor improvements.

 dlls/ntdll/unix/process.c      | 39 ++++++++++++++++++++++++++++++++++
 include/wine/server_protocol.h |  5 +++--
 server/handle.c                | 38 +++++++++++++++++++++++++--------
 server/handle.h                |  3 ++-
 server/process.c               | 27 +++++++++++++++++------
 server/process.h               |  3 ++-
 server/protocol.def            |  2 ++
 server/request.c               |  2 +-
 server/request.h               |  1 +
 server/trace.c                 |  2 ++
 10 files changed, 102 insertions(+), 20 deletions(-)

diff --git a/dlls/ntdll/unix/process.c b/dlls/ntdll/unix/process.c
index 6b41080ceeb..f31a10459ea 100644
--- a/dlls/ntdll/unix/process.c
+++ b/dlls/ntdll/unix/process.c
@@ -774,6 +774,28 @@ done:
     return status;
 }
 
+static NTSTATUS alloc_handle_list( const PS_ATTRIBUTE *handles_attr, obj_handle_t **handles, data_size_t *handles_len )
+{
+    SIZE_T count, i;
+    HANDLE *src;
+
+    *handles = NULL;
+    *handles_len = 0;
+
+    if (!handles_attr) return STATUS_SUCCESS;
+
+    count = handles_attr->Size / sizeof(HANDLE);
+
+    if (!(*handles = calloc( sizeof(**handles), count ))) return STATUS_NO_MEMORY;
+
+    src = handles_attr->ValuePtr;
+    for (i = 0; i < count; ++i)
+        (*handles)[i] = wine_server_obj_handle( src[i] );
+
+    *handles_len = count * sizeof(**handles);
+
+    return STATUS_SUCCESS;
+}
 
 /**********************************************************************
  *           NtCreateUserProcess  (NTDLL.@)
@@ -799,6 +821,9 @@ NTSTATUS WINAPI NtCreateUserProcess( HANDLE *process_handle_ptr, HANDLE *thread_
     HANDLE parent = 0, debug = 0, token = 0;
     UNICODE_STRING path = {0};
     SIZE_T i, attr_count = (attr->TotalLength - sizeof(attr->TotalLength)) / sizeof(PS_ATTRIBUTE);
+    const PS_ATTRIBUTE *handles_attr = NULL;
+    data_size_t handles_size;
+    obj_handle_t *handles;
 
     for (i = 0; i < attr_count; i++)
     {
@@ -817,6 +842,10 @@ NTSTATUS WINAPI NtCreateUserProcess( HANDLE *process_handle_ptr, HANDLE *thread_
         case PS_ATTRIBUTE_TOKEN:
             token = attr->Attributes[i].ValuePtr;
             break;
+        case PS_ATTRIBUTE_HANDLE_LIST:
+            if (process_flags & PROCESS_CREATE_FLAGS_INHERIT_HANDLES)
+                handles_attr = &attr->Attributes[i];
+            break;
         default:
             if (attr->Attributes[i].Attribute & PS_ATTRIBUTE_INPUT)
                 FIXME( "unhandled input attribute %lx\n", attr->Attributes[i].Attribute );
@@ -845,12 +874,19 @@ NTSTATUS WINAPI NtCreateUserProcess( HANDLE *process_handle_ptr, HANDLE *thread_
 
     if ((status = alloc_object_attributes( process_attr, &objattr, &attr_len ))) goto done;
 
+    if ((status = alloc_handle_list( handles_attr, &handles, &handles_size )))
+    {
+        free( objattr );
+        goto done;
+    }
+
     /* create the socket for the new process */
 
     if (socketpair( PF_UNIX, SOCK_STREAM, 0, socketfd ) == -1)
     {
         status = STATUS_TOO_MANY_OPENED_FILES;
         free( objattr );
+        free( handles );
         goto done;
     }
 #ifdef SO_PASSCRED
@@ -876,7 +912,9 @@ NTSTATUS WINAPI NtCreateUserProcess( HANDLE *process_handle_ptr, HANDLE *thread_
         req->access         = process_access;
         req->cpu            = pe_info.cpu;
         req->info_size      = startup_info_size;
+        req->handles_size   = handles_size;
         wine_server_add_data( req, objattr, attr_len );
+        wine_server_add_data( req, handles, handles_size );
         wine_server_add_data( req, startup_info, startup_info_size );
         wine_server_add_data( req, params->Environment, env_size );
         if (!(status = wine_server_call( req )))
@@ -888,6 +926,7 @@ NTSTATUS WINAPI NtCreateUserProcess( HANDLE *process_handle_ptr, HANDLE *thread_
     }
     SERVER_END_REQ;
     free( objattr );
+    free( handles );
 
     if (status)
     {
diff --git a/include/wine/server_protocol.h b/include/wine/server_protocol.h
index 98e4825439e..8058860a436 100644
--- a/include/wine/server_protocol.h
+++ b/include/wine/server_protocol.h
@@ -781,10 +781,11 @@ struct new_process_request
     unsigned int access;
     client_cpu_t cpu;
     data_size_t  info_size;
+    data_size_t  handles_size;
     /* VARARG(objattr,object_attributes); */
+    /* VARARG(handles,uints,handles_size); */
     /* VARARG(info,startup_info,info_size); */
     /* VARARG(env,unicode_str); */
-    char __pad_44[4];
 };
 struct new_process_reply
 {
@@ -6337,7 +6338,7 @@ union generic_reply
 
 /* ### protocol_version begin ### */
 
-#define SERVER_PROTOCOL_VERSION 640
+#define SERVER_PROTOCOL_VERSION 641
 
 /* ### protocol_version end ### */
 
diff --git a/server/handle.c b/server/handle.c
index 9ae99cd0c63..3ef7fb1bdb9 100644
--- a/server/handle.c
+++ b/server/handle.c
@@ -355,7 +355,8 @@ static void shrink_handle_table( struct handle_table *table )
 
 /* copy the handle table of the parent process */
 /* return 1 if OK, 0 on error */
-struct handle_table *copy_handle_table( struct process *process, struct process *parent )
+struct handle_table *copy_handle_table( struct process *process, struct process *parent,
+                                        const obj_handle_t *handles, unsigned int handle_count )
 {
     struct handle_table *parent_table = parent->handles;
     struct handle_table *table;
@@ -364,18 +365,37 @@ struct handle_table *copy_handle_table( struct process *process, struct process
     assert( parent_table );
     assert( parent_table->obj.ops == &handle_table_ops );
 
-    if (!(table = alloc_handle_table( process, parent_table->count )))
+    if (!(table = alloc_handle_table( process, handle_count ? handle_count : parent_table->count )))
         return NULL;
 
-    if ((table->last = parent_table->last) >= 0)
+    if (handles)
     {
-        struct handle_entry *ptr = table->entries;
-        memcpy( ptr, parent_table->entries, (table->last + 1) * sizeof(struct handle_entry) );
-        for (i = 0; i <= table->last; i++, ptr++)
+        struct handle_entry *dst, *src;
+        int j;
+
+        dst = table->entries;
+        memset( dst, 0, handle_count * sizeof(*dst) );
+
+        for (i = 0, j = 0; i < handle_count; i++)
+        {
+            src = get_handle( parent, handles[i] );
+            if (!src || !(src->access & RESERVED_INHERIT)) continue;
+            grab_object_for_handle( src->ptr );
+            dst[j++] = *src;
+        }
+    }
+    else
+    {
+        if ((table->last = parent_table->last) >= 0)
         {
-            if (!ptr->ptr) continue;
-            if (ptr->access & RESERVED_INHERIT) grab_object_for_handle( ptr->ptr );
-            else ptr->ptr = NULL; /* don't inherit this entry */
+            struct handle_entry *ptr = table->entries;
+            memcpy( ptr, parent_table->entries, (table->last + 1) * sizeof(struct handle_entry) );
+            for (i = 0; i <= table->last; i++, ptr++)
+            {
+                if (!ptr->ptr) continue;
+                if (ptr->access & RESERVED_INHERIT) grab_object_for_handle( ptr->ptr );
+                else ptr->ptr = NULL; /* don't inherit this entry */
+            }
         }
     }
     /* attempt to shrink the table */
diff --git a/server/handle.h b/server/handle.h
index f1deb79fb5f..736fd360173 100644
--- a/server/handle.h
+++ b/server/handle.h
@@ -52,7 +52,8 @@ extern obj_handle_t enumerate_handles( struct process *process, const struct obj
                                        unsigned int *index );
 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 );
+extern struct handle_table *copy_handle_table( struct process *process, struct process *parent,
+                                               const obj_handle_t *handles, unsigned int handle_count );
 extern unsigned int get_handle_table_count( struct process *process);
 
 #endif  /* __WINE_SERVER_HANDLE_H */
diff --git a/server/process.c b/server/process.c
index c1bdb591f60..8206d8ad322 100644
--- a/server/process.c
+++ b/server/process.c
@@ -500,7 +500,8 @@ static void start_sigkill_timer( struct process *process )
 /* create a new process */
 /* if the function fails the fd is closed */
 struct process *create_process( int fd, struct process *parent, int inherit_all,
-                                const struct security_descriptor *sd )
+                                const struct security_descriptor *sd, const obj_handle_t *handles,
+                                unsigned int handle_count )
 {
     struct process *process;
 
@@ -573,8 +574,9 @@ struct process *create_process( int fd, struct process *parent, int inherit_all,
     else
     {
         process->parent_id = parent->id;
-        process->handles = inherit_all ? copy_handle_table( process, parent )
+        process->handles = inherit_all ? copy_handle_table( process, parent, handles, handle_count )
                                        : alloc_handle_table( process, 0 );
+
         /* Note: for security reasons, starting a new process does not attempt
          * to use the current impersonation token for the new process */
         process->token = token_duplicate( parent->token, TRUE, 0, NULL );
@@ -1098,6 +1100,8 @@ DECL_HANDLER(new_process)
     struct process *parent;
     struct thread *parent_thread = current;
     int socket_fd = thread_get_inflight_fd( current, req->socket_fd );
+    const obj_handle_t *handles = NULL;
+    data_size_t data_size;
 
     if (socket_fd == -1)
     {
@@ -1158,8 +1162,18 @@ DECL_HANDLER(new_process)
     info->process  = NULL;
     info->data     = NULL;
 
-    info_ptr = get_req_data_after_objattr( objattr, &info->data_size );
-    info->info_size = min( req->info_size, info->data_size );
+    info_ptr = get_req_data_after_objattr( objattr, &data_size );
+
+    if (req->handles_size)
+    {
+        if (req->handles_size < data_size)
+            handles = info_ptr;
+        info_ptr = (const char *)info_ptr + req->handles_size;
+        data_size -= req->handles_size;
+    }
+    info->data_size = data_size;
+
+    info->info_size = min( req->info_size, data_size );
 
     if (req->info_size < sizeof(*info->data))
     {
@@ -1199,7 +1213,8 @@ DECL_HANDLER(new_process)
 #undef FIXUP_LEN
     }
 
-    if (!(process = create_process( socket_fd, parent, req->inherit_all, sd ))) goto done;
+    if (!(process = create_process( socket_fd, parent, req->inherit_all, sd, handles, req->handles_size / sizeof(*handles) )))
+        goto done;
 
     process->startup_info = (struct startup_info *)grab_object( info );
 
@@ -1294,7 +1309,7 @@ DECL_HANDLER(exec_process)
         close( socket_fd );
         return;
     }
-    if (!(process = create_process( socket_fd, NULL, 0, NULL ))) return;
+    if (!(process = create_process( socket_fd, NULL, 0, NULL, NULL, 0 ))) return;
     create_thread( -1, process, NULL );
     release_object( process );
 }
diff --git a/server/process.h b/server/process.h
index 0fdf070b78e..7facc4f6dfa 100644
--- a/server/process.h
+++ b/server/process.h
@@ -109,7 +109,8 @@ extern unsigned int alloc_ptid( void *ptr );
 extern void free_ptid( unsigned int id );
 extern void *get_ptid_entry( unsigned int id );
 extern struct process *create_process( int fd, struct process *parent, int inherit_all,
-                                       const struct security_descriptor *sd );
+                                       const struct security_descriptor *sd, const obj_handle_t *handles,
+                                       unsigned int handle_count );
 extern data_size_t init_process( struct thread *thread );
 extern struct thread *get_process_first_thread( struct process *process );
 extern struct process *get_process_from_id( process_id_t id );
diff --git a/server/protocol.def b/server/protocol.def
index 6e92f86cc03..68dec5b594b 100644
--- a/server/protocol.def
+++ b/server/protocol.def
@@ -795,7 +795,9 @@ struct rawinput_device
     unsigned int access;         /* access rights for process object */
     client_cpu_t cpu;            /* CPU that the new process will use */
     data_size_t  info_size;      /* size of startup info */
+    data_size_t  handles_size;   /* length of explicit handles list to inherit */
     VARARG(objattr,object_attributes);   /* object attributes */
+    VARARG(handles,uints,handles_size);  /* handles list */
     VARARG(info,startup_info,info_size); /* startup information */
     VARARG(env,unicode_str);     /* environment for new process */
 @REPLY
diff --git a/server/request.c b/server/request.c
index 4c1f30a5fe7..8f3bad8701d 100644
--- a/server/request.c
+++ b/server/request.c
@@ -582,7 +582,7 @@ static void master_socket_poll_event( struct fd *fd, int event )
         int client = accept( get_unix_fd( master_socket->fd ), (struct sockaddr *) &dummy, &len );
         if (client == -1) return;
         fcntl( client, F_SETFL, O_NONBLOCK );
-        if ((process = create_process( client, NULL, 0, NULL )))
+        if ((process = create_process( client, NULL, 0, NULL, NULL, 0 )))
         {
             create_thread( -1, process, NULL );
             release_object( process );
diff --git a/server/request.h b/server/request.h
index 5a602d3d816..ef52c5e6923 100644
--- a/server/request.h
+++ b/server/request.h
@@ -731,6 +731,7 @@ C_ASSERT( FIELD_OFFSET(struct new_process_request, exe_file) == 28 );
 C_ASSERT( FIELD_OFFSET(struct new_process_request, access) == 32 );
 C_ASSERT( FIELD_OFFSET(struct new_process_request, cpu) == 36 );
 C_ASSERT( FIELD_OFFSET(struct new_process_request, info_size) == 40 );
+C_ASSERT( FIELD_OFFSET(struct new_process_request, handles_size) == 44 );
 C_ASSERT( sizeof(struct new_process_request) == 48 );
 C_ASSERT( FIELD_OFFSET(struct new_process_reply, info) == 8 );
 C_ASSERT( FIELD_OFFSET(struct new_process_reply, pid) == 12 );
diff --git a/server/trace.c b/server/trace.c
index 93e53d2f42d..f973098c6a5 100644
--- a/server/trace.c
+++ b/server/trace.c
@@ -1288,7 +1288,9 @@ static void dump_new_process_request( const struct new_process_request *req )
     fprintf( stderr, ", access=%08x", req->access );
     dump_client_cpu( ", cpu=", &req->cpu );
     fprintf( stderr, ", info_size=%u", req->info_size );
+    fprintf( stderr, ", handles_size=%u", req->handles_size );
     dump_varargs_object_attributes( ", objattr=", cur_size );
+    dump_varargs_uints( ", handles=", min(cur_size,req->handles_size) );
     dump_varargs_startup_info( ", info=", min(cur_size,req->info_size) );
     dump_varargs_unicode_str( ", env=", cur_size );
 }
-- 
2.28.0




More information about the wine-devel mailing list