From: Nikolay Sivov Subject: [v2 PATCH] server: Implement inherited handles list. Message-Id: <20200831142348.3171199-1-nsivov@codeweavers.com> Date: Mon, 31 Aug 2020 17:23:48 +0300 Signed-off-by: Nikolay Sivov --- 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