From: Paul Gofman Subject: [PATCH v2 2/4] ntdll: Support creating processes with specified parent. Message-Id: <20191209215114.210057-2-gofmanp@gmail.com> Date: Tue, 10 Dec 2019 00:51:12 +0300 In-Reply-To: <20191209215114.210057-1-gofmanp@gmail.com> References: <20191209215114.210057-1-gofmanp@gmail.com> Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=47817 Signed-off-by: Paul Gofman --- v2: - don't use arbitrary thread as parent. dlls/kernel32/tests/process.c | 4 ++-- dlls/ntdll/process.c | 11 +++++++++-- include/wine/server_protocol.h | 4 +++- server/console.c | 32 ++++++++++++++++---------------- server/process.c | 30 ++++++++++++++++++++++++------ server/process.h | 3 ++- server/protocol.def | 1 + server/request.h | 17 +++++++++-------- server/trace.c | 3 ++- server/user.h | 3 ++- server/winstation.c | 24 ++++++++++++++++-------- 11 files changed, 86 insertions(+), 46 deletions(-) diff --git a/dlls/kernel32/tests/process.c b/dlls/kernel32/tests/process.c index 3efbfa2402..6d7a9a74c3 100644 --- a/dlls/kernel32/tests/process.c +++ b/dlls/kernel32/tests/process.c @@ -3874,14 +3874,14 @@ void test_parent_process_attribute(unsigned int level, HANDLE read_pipe) memset(&parent_data, 0, sizeof(parent_data)); ret = ReadFile(read_pipe, &parent_data, sizeof(parent_data), &size, NULL); - todo_wine_if(level == 2) ok((level == 2 && ret) || (level == 1 && !ret && GetLastError() == ERROR_INVALID_HANDLE), + ok((level == 2 && ret) || (level == 1 && !ret && GetLastError() == ERROR_INVALID_HANDLE), "Got unexpected ret %#x, level %u, GetLastError() %u.\n", ret, level, GetLastError()); } if (level == 2) { - todo_wine ok(parent_id == parent_data.parent_id, "Got parent id %u, parent_data.parent_id %u.\n", + ok(parent_id == parent_data.parent_id, "Got parent id %u, parent_data.parent_id %u.\n", parent_id, parent_data.parent_id); return; } diff --git a/dlls/ntdll/process.c b/dlls/ntdll/process.c index 52d7ea429e..5d75a27e97 100644 --- a/dlls/ntdll/process.c +++ b/dlls/ntdll/process.c @@ -1667,8 +1667,14 @@ NTSTATUS WINAPI RtlCreateUserProcess( UNICODE_STRING *path, ULONG attributes, RtlNormalizeProcessParams( params ); - TRACE( "%s image %s cmdline %s\n", debugstr_us( path ), - debugstr_us( ¶ms->ImagePathName ), debugstr_us( ¶ms->CommandLine )); + TRACE( "%s image %s cmdline %s, parent %p.\n", debugstr_us( path ), + debugstr_us( ¶ms->ImagePathName ), debugstr_us( ¶ms->CommandLine ), parent); + + if (parent == INVALID_HANDLE_VALUE) + { + memset(info, 0, sizeof(*info)); + return STATUS_INVALID_HANDLE; + } if ((status = get_pe_file_info( path, attributes, &file_handle, &pe_info ))) { @@ -1709,6 +1715,7 @@ NTSTATUS WINAPI RtlCreateUserProcess( UNICODE_STRING *path, ULONG attributes, SERVER_START_REQ( new_process ) { + req->parent_process = wine_server_obj_handle(parent); req->inherit_all = inherit; req->create_flags = params->DebugFlags; /* hack: creation flags stored in DebugFlags for now */ req->socket_fd = socketfd[1]; diff --git a/include/wine/server_protocol.h b/include/wine/server_protocol.h index aaa5fd2e33..98ecd98b08 100644 --- a/include/wine/server_protocol.h +++ b/include/wine/server_protocol.h @@ -769,6 +769,7 @@ struct rawinput_device struct new_process_request { struct request_header __header; + obj_handle_t parent_process; int inherit_all; unsigned int create_flags; int socket_fd; @@ -779,6 +780,7 @@ struct new_process_request /* VARARG(objattr,object_attributes); */ /* VARARG(info,startup_info,info_size); */ /* VARARG(env,unicode_str); */ + char __pad_44[4]; }; struct new_process_reply { @@ -6702,6 +6704,6 @@ union generic_reply struct resume_process_reply resume_process_reply; }; -#define SERVER_PROTOCOL_VERSION 593 +#define SERVER_PROTOCOL_VERSION 594 #endif /* __WINE_WINE_SERVER_PROTOCOL_H */ diff --git a/server/console.c b/server/console.c index 59f8843a75..691a0bf05a 100644 --- a/server/console.c +++ b/server/console.c @@ -504,37 +504,37 @@ int free_console( struct process *process ) * 2/ parent is a renderer which launches process, and process should attach to the console * rendered by parent */ -void inherit_console(struct thread *parent_thread, struct process *process, obj_handle_t hconin) +void inherit_console(struct thread *parent_thread, struct process *parent, struct process *process, + obj_handle_t hconin) { int done = 0; - struct process* parent = parent_thread->process; /* if parent is a renderer, then attach current process to its console * a bit hacky.... */ - if (hconin) + if (hconin && parent_thread) { - struct console_input* console; + struct console_input *console; /* FIXME: should we check some access rights ? */ - if ((console = (struct console_input*)get_handle_obj( parent, hconin, - 0, &console_input_ops ))) - { + if ((console = (struct console_input *)get_handle_obj( parent, hconin, + 0, &console_input_ops ))) + { if (console->renderer == parent_thread) - { - process->console = (struct console_input*)grab_object( console ); - process->console->num_proc++; - done = 1; - } - release_object( console ); - } + { + process->console = (struct console_input*)grab_object( console ); + process->console->num_proc++; + done = 1; + } + release_object( console ); + } else clear_error(); /* ignore error */ } /* otherwise, if parent has a console, attach child to this console */ if (!done && parent->console) { - process->console = (struct console_input*)grab_object( parent->console ); - process->console->num_proc++; + process->console = (struct console_input*)grab_object( parent->console ); + process->console->num_proc++; } } diff --git a/server/process.c b/server/process.c index 16bb5d57e7..00ea45b068 100644 --- a/server/process.c +++ b/server/process.c @@ -1117,6 +1117,7 @@ DECL_HANDLER(new_process) const struct object_attributes *objattr = get_req_object_attributes( &sd, &name, NULL ); struct process *process = NULL; struct process *parent = current->process; + struct thread *parent_thread = current; int socket_fd = thread_get_inflight_fd( current, req->socket_fd ); if (socket_fd == -1) @@ -1148,11 +1149,26 @@ DECL_HANDLER(new_process) return; } + if (req->parent_process) + { + if (!(parent = get_process_from_handle( req->parent_process, PROCESS_CREATE_PROCESS))) + { + set_error(STATUS_INVALID_HANDLE); + close(socket_fd); + return; + } + parent_thread = NULL; + } + if (parent->job && (req->create_flags & CREATE_BREAKAWAY_FROM_JOB) && !(parent->job->limit_flags & (JOB_OBJECT_LIMIT_BREAKAWAY_OK | JOB_OBJECT_LIMIT_SILENT_BREAKAWAY_OK))) { set_error( STATUS_ACCESS_DENIED ); close( socket_fd ); + + if (req->parent_process) + release_object(parent); + return; } @@ -1222,7 +1238,7 @@ DECL_HANDLER(new_process) } /* connect to the window station */ - connect_process_winstation( process, current ); + connect_process_winstation( process, parent_thread, parent ); /* set the process console */ if (!(req->create_flags & (DETACHED_PROCESS | CREATE_NEW_CONSOLE))) @@ -1231,7 +1247,7 @@ DECL_HANDLER(new_process) * like if hConOut and hConIn are console handles, then they should be on the same * physical console */ - inherit_console( current, process, req->inherit_all ? info->data->hstdin : 0 ); + inherit_console( parent_thread, parent, process, req->inherit_all ? info->data->hstdin : 0 ); } if (!req->inherit_all && !(req->create_flags & CREATE_NEW_CONSOLE)) @@ -1246,16 +1262,15 @@ DECL_HANDLER(new_process) if (get_error() == STATUS_INVALID_HANDLE || get_error() == STATUS_OBJECT_TYPE_MISMATCH) clear_error(); } - /* attach to the debugger if requested */ if (req->create_flags & (DEBUG_PROCESS | DEBUG_ONLY_THIS_PROCESS)) { set_process_debugger( process, current ); process->debug_children = !(req->create_flags & DEBUG_ONLY_THIS_PROCESS); } - else if (parent->debugger && parent->debug_children) + else if (current->process->debugger && current->process->debug_children) { - set_process_debugger( process, parent->debugger ); + set_process_debugger( process, current->process->debugger ); /* debug_children is set to 1 by default */ } @@ -1265,9 +1280,12 @@ DECL_HANDLER(new_process) info->process = (struct process *)grab_object( process ); reply->info = alloc_handle( current->process, info, SYNCHRONIZE, 0 ); reply->pid = get_process_id( process ); - reply->handle = alloc_handle_no_access_check( parent, process, req->access, objattr->attributes ); + reply->handle = alloc_handle_no_access_check( current->process, process, req->access, objattr->attributes ); done: + if (req->parent_process) + release_object(parent); + if (process) release_object( process ); release_object( info ); } diff --git a/server/process.h b/server/process.h index 20ff6beda6..d8453eeaf2 100644 --- a/server/process.h +++ b/server/process.h @@ -141,7 +141,8 @@ extern struct process_snapshot *process_snap( int *count ); extern void enum_processes( int (*cb)(struct process*, void*), void *user); /* console functions */ -extern void inherit_console(struct thread *parent_thread, struct process *process, obj_handle_t hconin); +extern void inherit_console(struct thread *parent_thread, struct process *parent, + struct process *process, obj_handle_t hconin); extern int free_console( struct process *process ); extern struct thread *console_get_renderer( struct console_input *console ); diff --git a/server/protocol.def b/server/protocol.def index 1cb1fea602..7f9ec3a149 100644 --- a/server/protocol.def +++ b/server/protocol.def @@ -783,6 +783,7 @@ struct rawinput_device /* Create a new process from the context of the parent */ @REQ(new_process) + obj_handle_t parent_process; /* parent process */ int inherit_all; /* inherit all handles from parent */ unsigned int create_flags; /* creation flags */ int socket_fd; /* file descriptor for process socket */ diff --git a/server/request.h b/server/request.h index 90a3180a6c..9f36bcb711 100644 --- a/server/request.h +++ b/server/request.h @@ -745,14 +745,15 @@ C_ASSERT( sizeof(unsigned char) == 1 ); C_ASSERT( sizeof(unsigned int) == 4 ); C_ASSERT( sizeof(unsigned short) == 2 ); C_ASSERT( sizeof(user_handle_t) == 4 ); -C_ASSERT( FIELD_OFFSET(struct new_process_request, inherit_all) == 12 ); -C_ASSERT( FIELD_OFFSET(struct new_process_request, create_flags) == 16 ); -C_ASSERT( FIELD_OFFSET(struct new_process_request, socket_fd) == 20 ); -C_ASSERT( FIELD_OFFSET(struct new_process_request, exe_file) == 24 ); -C_ASSERT( FIELD_OFFSET(struct new_process_request, access) == 28 ); -C_ASSERT( FIELD_OFFSET(struct new_process_request, cpu) == 32 ); -C_ASSERT( FIELD_OFFSET(struct new_process_request, info_size) == 36 ); -C_ASSERT( sizeof(struct new_process_request) == 40 ); +C_ASSERT( FIELD_OFFSET(struct new_process_request, parent_process) == 12 ); +C_ASSERT( FIELD_OFFSET(struct new_process_request, inherit_all) == 16 ); +C_ASSERT( FIELD_OFFSET(struct new_process_request, create_flags) == 20 ); +C_ASSERT( FIELD_OFFSET(struct new_process_request, socket_fd) == 24 ); +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( 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 ); C_ASSERT( FIELD_OFFSET(struct new_process_reply, handle) == 16 ); diff --git a/server/trace.c b/server/trace.c index 411369a4f6..026aba9c50 100644 --- a/server/trace.c +++ b/server/trace.c @@ -1243,7 +1243,8 @@ typedef void (*dump_func)( const void *req ); static void dump_new_process_request( const struct new_process_request *req ) { - fprintf( stderr, " inherit_all=%d", req->inherit_all ); + fprintf( stderr, " parent_process=%04x", req->parent_process ); + fprintf( stderr, ", inherit_all=%d", req->inherit_all ); fprintf( stderr, ", create_flags=%08x", req->create_flags ); fprintf( stderr, ", socket_fd=%d", req->socket_fd ); fprintf( stderr, ", exe_file=%04x", req->exe_file ); diff --git a/server/user.h b/server/user.h index eb1b7ce1e4..35184d8780 100644 --- a/server/user.h +++ b/server/user.h @@ -183,7 +183,8 @@ extern client_ptr_t get_class_client_ptr( struct window_class *class ); extern struct desktop *get_desktop_obj( struct process *process, obj_handle_t handle, unsigned int access ); extern struct winstation *get_process_winstation( struct process *process, unsigned int access ); extern struct desktop *get_thread_desktop( struct thread *thread, unsigned int access ); -extern void connect_process_winstation( struct process *process, struct thread *parent ); +extern void connect_process_winstation( struct process *process, struct thread *parent_thread, + struct process *parent_process ); extern void set_process_default_desktop( struct process *process, struct desktop *desktop, obj_handle_t handle ); extern void close_process_desktop( struct process *process ); diff --git a/server/winstation.c b/server/winstation.c index a09ca03e3b..f7932a9dc6 100644 --- a/server/winstation.c +++ b/server/winstation.c @@ -372,7 +372,8 @@ void set_process_default_desktop( struct process *process, struct desktop *deskt } /* connect a process to its window station */ -void connect_process_winstation( struct process *process, struct thread *parent ) +void connect_process_winstation( struct process *process, struct thread *parent_thread, + struct process *parent_process) { struct winstation *winstation = NULL; struct desktop *desktop = NULL; @@ -383,9 +384,9 @@ void connect_process_winstation( struct process *process, struct thread *parent { winstation = (struct winstation *)get_handle_obj( process, handle, 0, &winstation_ops ); } - else if (parent && parent->process->winstation) + else if (parent_process->winstation) { - handle = duplicate_handle( parent->process, parent->process->winstation, + handle = duplicate_handle( parent_process, parent_process->winstation, process, 0, 0, DUP_HANDLE_SAME_ACCESS ); winstation = (struct winstation *)get_handle_obj( process, handle, 0, &winstation_ops ); } @@ -397,14 +398,21 @@ void connect_process_winstation( struct process *process, struct thread *parent desktop = get_desktop_obj( process, handle, 0 ); if (!desktop || desktop->winstation != winstation) goto done; } - else if (parent && parent->desktop) + else { - desktop = get_desktop_obj( parent->process, parent->desktop, 0 ); + if (parent_thread && parent_thread->desktop) + handle = parent_thread->desktop; + else if (parent_process->desktop) + handle = parent_process->desktop; + else + goto done; + + desktop = get_desktop_obj( parent_process, handle, 0 ); + if (!desktop || desktop->winstation != winstation) goto done; - handle = duplicate_handle( parent->process, parent->desktop, - process, 0, 0, DUP_HANDLE_SAME_ACCESS ); - } + handle = duplicate_handle( parent_process, handle, process, 0, 0, DUP_HANDLE_SAME_ACCESS ); + } if (handle) set_process_default_desktop( process, desktop, handle ); done: -- 2.23.0