From: Nikolay Sivov Subject: [v3 PATCH 4/4] server: Implement inherited handles list. Message-Id: <20200922102421.1800527-4-nsivov@codeweavers.com> Date: Tue, 22 Sep 2020 13:24:21 +0300 In-Reply-To: <20200922102421.1800527-1-nsivov@codeweavers.com> References: <20200922102421.1800527-1-nsivov@codeweavers.com> Signed-off-by: Nikolay Sivov --- v3: fixed indexing of duplicated handles, handle table updated correctly now. dlls/kernel32/tests/process.c | 1 - dlls/ntdll/unix/process.c | 39 ++++++++++++++++++++++++++++++++++ include/wine/server_protocol.h | 5 +++-- server/handle.c | 38 ++++++++++++++++++++++++++------- server/handle.h | 3 ++- server/process.c | 25 ++++++++++++++++------ server/process.h | 3 ++- server/protocol.def | 2 ++ server/request.c | 2 +- server/request.h | 1 + server/trace.c | 2 ++ 11 files changed, 101 insertions(+), 20 deletions(-) diff --git a/dlls/kernel32/tests/process.c b/dlls/kernel32/tests/process.c index a666434035e..109322d6844 100644 --- a/dlls/kernel32/tests/process.c +++ b/dlls/kernel32/tests/process.c @@ -4102,7 +4102,6 @@ static void test_handle_list_attribute(BOOL child, HANDLE handle1, HANDLE handle CloseHandle(handle1); ret = GetHandleInformation(handle2, &flags); - todo_wine ok(!ret && GetLastError() == ERROR_INVALID_HANDLE, "Unexpected return value, error %d.\n", GetLastError()); return; diff --git a/dlls/ntdll/unix/process.c b/dlls/ntdll/unix/process.c index 64380aba036..6c7aa0c2aa9 100644 --- a/dlls/ntdll/unix/process.c +++ b/dlls/ntdll/unix/process.c @@ -812,6 +812,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.@) @@ -837,6 +859,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++) { @@ -855,6 +880,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 ); @@ -883,12 +912,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 @@ -914,7 +950,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 ))) @@ -926,6 +964,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 2d4e0164783..ab033732f58 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 { @@ -6305,7 +6306,7 @@ union generic_reply /* ### protocol_version begin ### */ -#define SERVER_PROTOCOL_VERSION 642 +#define SERVER_PROTOCOL_VERSION 643 /* ### protocol_version end ### */ diff --git a/server/handle.c b/server/handle.c index 10e6a52e571..e0a21329449 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; @@ -367,15 +368,36 @@ struct handle_table *copy_handle_table( struct process *process, struct process if (!(table = alloc_handle_table( process, 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 index; + + dst = table->entries; + memset( dst, 0, parent_table->count * sizeof(*dst) ); + + for (i = 0; i < handle_count; i++) + { + src = get_handle( parent, handles[i] ); + if (!src || !(src->access & RESERVED_INHERIT)) continue; + grab_object_for_handle( src->ptr ); + index = handle_to_index( handles[i] ); + dst[index] = *src; + table->last = max( table->last, index ); + } + } + 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 40b3b427c2a..bb541de13b1 100644 --- a/server/handle.h +++ b/server/handle.h @@ -50,7 +50,8 @@ extern obj_handle_t open_object( struct process *process, obj_handle_t parent, u extern obj_handle_t find_inherited_handle( struct process *process, const struct object_ops *ops ); 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 9bf5e447d37..83f5fb6c732 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,7 +574,7 @@ 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 */ @@ -1101,6 +1102,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) { @@ -1161,8 +1164,17 @@ 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)) { @@ -1202,7 +1214,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 ); @@ -1297,7 +1310,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 fd9b721d693..83067e5e932 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 */ 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 39f3ce7b2ef..bc57d1dc91d 100644 --- a/server/request.h +++ b/server/request.h @@ -727,6 +727,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 707a952357f..86b234bc324 100644 --- a/server/trace.c +++ b/server/trace.c @@ -1292,7 +1292,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