diff --git a/dlls/kernel32/tests/process.c b/dlls/kernel32/tests/process.c index a666434035e112c6d008254e53a2ac00e331f89c..109322d6844254593cf979dcdde0b3a3200e2cba 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 3e0bd02a59baaef15efd9f0bdfd67572ce88bbef..a6be00646a8a32db14aab577630a6f935eb2a4ce 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 8e2ea08500196644e94fe58d786b2e45651100fa..40c86791cc42e364d2f0e202b3c0f54c84292bff 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 { @@ -6282,7 +6283,7 @@ union generic_reply /* ### protocol_version begin ### */ -#define SERVER_PROTOCOL_VERSION 643 +#define SERVER_PROTOCOL_VERSION 644 /* ### protocol_version end ### */ diff --git a/server/handle.c b/server/handle.c index 10e6a52e57114c887277bfbb838f3c5daeb2215a..e0a21329449a9e8b257960f560663cf80221fe13 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 40b3b427c2ae330b68b3dc35b7855f177692fc0c..bb541de13b12dff9fd8c421752700aaa45db1ca5 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 9bf5e447d372c09636ce2f77f11b7d057559985d..72ffa8e9a5ab9ac2a283d46fe61db4d55d82aaf1 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,7 @@ 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; if (socket_fd == -1) { @@ -1162,6 +1164,19 @@ DECL_HANDLER(new_process) info->data = NULL; info_ptr = get_req_data_after_objattr( objattr, &info->data_size ); + + if ((req->handles_size & 3) || req->handles_size > info->data_size) + { + set_error( STATUS_INVALID_PARAMETER ); + close( socket_fd ); + goto done; + } + if (req->handles_size) + { + handles = info_ptr; + info_ptr = (const char *)info_ptr + req->handles_size; + info->data_size -= req->handles_size; + } info->info_size = min( req->info_size, info->data_size ); if (req->info_size < sizeof(*info->data)) @@ -1202,7 +1217,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 +1313,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 0fdf070b78eed00c947a08bd900b4079ae06f275..7facc4f6dfad2233dbf0eb8ae6ec7dd11290449b 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 94ae63fec52330c508f74db4eb62a7f96151d572..ed8b10ce77865d5ad00eb9f55494662a643a5fe3 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 4c1f30a5fe7c9859a5e93710908c79c136b2534e..8f3bad8701dbe7f1a79e525e719d2f58f1cc1cae 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 de821516982c82f3d6209cfc3bda6ee59cecd6ff..0982e919903cff85b23b0fb12151740f8592b961 100644 --- a/server/request.h +++ b/server/request.h @@ -725,6 +725,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 3aa6ecc1b7a8cbb4e928b7b2ad9caaf29ec6c4b4..be65fe3d66382a7afca3a10233943d013b69b30d 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 ); }