Signed-off-by: Nikolay Sivov <nsivov(a)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