Signed-off-by: Paul Gofman pgofman@codeweavers.com --- dlls/kernel32/tests/process.c | 54 +++++++++++++++++++++++++++++++++++ dlls/kernelbase/process.c | 8 ++++++ 2 files changed, 62 insertions(+)
diff --git a/dlls/kernel32/tests/process.c b/dlls/kernel32/tests/process.c index 52c3036fe59..9acf0aaaaee 100644 --- a/dlls/kernel32/tests/process.c +++ b/dlls/kernel32/tests/process.c @@ -4537,6 +4537,59 @@ static void test_nested_jobs(void) CloseHandle(job2); }
+static void test_job_list_attribute(void) +{ + PPROC_THREAD_ATTRIBUTE_LIST attrs; + HANDLE jobs[2]; + SIZE_T size; + BOOL ret; + + if (!pInitializeProcThreadAttributeList) + { + win_skip("No support for ProcThreadAttributeList\n"); + return; + } + + ret = pInitializeProcThreadAttributeList(NULL, 1, 0, &size); + ok(!ret && GetLastError() == ERROR_INSUFFICIENT_BUFFER, + "Got unexpected ret %#x, GetLastError() %u.\n", ret, GetLastError()); + attrs = heap_alloc(size); + + + jobs[0] = (HANDLE)0xdeadbeef; + jobs[1] = NULL; + + ret = pInitializeProcThreadAttributeList(attrs, 1, 0, &size); + ok(ret, "Got unexpected ret %#x, GetLastError() %u.\n", ret, GetLastError()); + ret = pUpdateProcThreadAttribute(attrs, 0, PROC_THREAD_ATTRIBUTE_JOB_LIST, jobs, + sizeof(*jobs), NULL, NULL); + if (!ret && GetLastError() == ERROR_NOT_SUPPORTED) + { + /* Supported since Win10. */ + win_skip("PROC_THREAD_ATTRIBUTE_JOB_LIST is not supported.\n"); + pDeleteProcThreadAttributeList(attrs); + heap_free(attrs); + return; + } + ok(ret, "Got unexpected ret %#x, GetLastError() %u.\n", ret, GetLastError()); + + ret = pInitializeProcThreadAttributeList(attrs, 1, 0, &size); + ok(ret, "Got unexpected ret %#x, GetLastError() %u.\n", ret, GetLastError()); + ret = pUpdateProcThreadAttribute(attrs, 0, PROC_THREAD_ATTRIBUTE_JOB_LIST, jobs, + 3, NULL, NULL); + ok(!ret && GetLastError() == ERROR_BAD_LENGTH, "Got unexpected ret %#x, GetLastError() %u.\n", + ret, GetLastError()); + + ret = pInitializeProcThreadAttributeList(attrs, 1, 0, &size); + ok(ret, "Got unexpected ret %#x, GetLastError() %u.\n", ret, GetLastError()); + ret = pUpdateProcThreadAttribute(attrs, 0, PROC_THREAD_ATTRIBUTE_JOB_LIST, jobs, + sizeof(*jobs) * 2, NULL, NULL); + ok(ret, "Got unexpected ret %#x, GetLastError() %u.\n", ret, GetLastError()); + + pDeleteProcThreadAttributeList(attrs); + heap_free(attrs); +} + START_TEST(process) { HANDLE job, hproc, h, h2; @@ -4688,6 +4741,7 @@ START_TEST(process) test_nested_jobs(); job = test_AddSelfToJob(); test_jobInheritance(job); + test_job_list_attribute(); test_BreakawayOk(job); CloseHandle(job); } diff --git a/dlls/kernelbase/process.c b/dlls/kernelbase/process.c index ae91168ecf4..06a90bf55ca 100644 --- a/dlls/kernelbase/process.c +++ b/dlls/kernelbase/process.c @@ -1687,6 +1687,14 @@ BOOL WINAPI DECLSPEC_HOTPATCH UpdateProcThreadAttribute( struct _PROC_THREAD_ATT } break;
+ case PROC_THREAD_ATTRIBUTE_JOB_LIST: + if ((size / sizeof(HANDLE)) * sizeof(HANDLE) != size) + { + SetLastError( ERROR_BAD_LENGTH ); + return FALSE; + } + break; + default: SetLastError( ERROR_NOT_SUPPORTED ); FIXME( "Unhandled attribute %lu\n", attr & PROC_THREAD_ATTRIBUTE_NUMBER );
Signed-off-by: Paul Gofman pgofman@codeweavers.com --- dlls/kernelbase/process.c | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-)
diff --git a/dlls/kernelbase/process.c b/dlls/kernelbase/process.c index 06a90bf55ca..ab89d3bcf31 100644 --- a/dlls/kernelbase/process.c +++ b/dlls/kernelbase/process.c @@ -249,11 +249,12 @@ static NTSTATUS create_nt_process( HANDLE token, HANDLE debug, SECURITY_ATTRIBUT SECURITY_ATTRIBUTES *tsa, DWORD process_flags, RTL_USER_PROCESS_PARAMETERS *params, RTL_USER_PROCESS_INFORMATION *info, HANDLE parent, - const struct proc_thread_attr *handle_list ) + const struct proc_thread_attr *handle_list, + const struct proc_thread_attr *job_list) { OBJECT_ATTRIBUTES process_attr, thread_attr; PS_CREATE_INFO create_info; - ULONG_PTR buffer[offsetof( PS_ATTRIBUTE_LIST, Attributes[7] ) / sizeof(ULONG_PTR)]; + ULONG_PTR buffer[offsetof( PS_ATTRIBUTE_LIST, Attributes[8] ) / sizeof(ULONG_PTR)]; PS_ATTRIBUTE_LIST *attr = (PS_ATTRIBUTE_LIST *)buffer; UNICODE_STRING nameW; NTSTATUS status; @@ -312,6 +313,14 @@ static NTSTATUS create_nt_process( HANDLE token, HANDLE debug, SECURITY_ATTRIBUT attr->Attributes[pos].ReturnLength = NULL; pos++; } + if (job_list) + { + attr->Attributes[pos].Attribute = PS_ATTRIBUTE_JOB_LIST; + attr->Attributes[pos].Size = job_list->size; + attr->Attributes[pos].ValuePtr = job_list->value; + attr->Attributes[pos].ReturnLength = NULL; + pos++; + } attr->TotalLength = offsetof( PS_ATTRIBUTE_LIST, Attributes[pos] );
InitializeObjectAttributes( &process_attr, NULL, 0, NULL, psa ? psa->lpSecurityDescriptor : NULL ); @@ -353,7 +362,7 @@ static NTSTATUS create_vdm_process( HANDLE token, HANDLE debug, SECURITY_ATTRIBU winevdm, params->ImagePathName.Buffer, params->CommandLine.Buffer ); RtlInitUnicodeString( ¶ms->ImagePathName, winevdm ); RtlInitUnicodeString( ¶ms->CommandLine, newcmdline ); - status = create_nt_process( token, debug, psa, tsa, flags, params, info, NULL, NULL ); + status = create_nt_process( token, debug, psa, tsa, flags, params, info, NULL, NULL, NULL ); HeapFree( GetProcessHeap(), 0, newcmdline ); return status; } @@ -382,7 +391,7 @@ static NTSTATUS create_cmd_process( HANDLE token, HANDLE debug, SECURITY_ATTRIBU swprintf( newcmdline, len, L"%s /s/c "%s"", comspec, params->CommandLine.Buffer ); RtlInitUnicodeString( ¶ms->ImagePathName, comspec ); RtlInitUnicodeString( ¶ms->CommandLine, newcmdline ); - status = create_nt_process( token, debug, psa, tsa, flags, params, info, NULL, NULL ); + status = create_nt_process( token, debug, psa, tsa, flags, params, info, NULL, NULL, NULL ); RtlFreeHeap( GetProcessHeap(), 0, newcmdline ); return status; } @@ -485,7 +494,7 @@ BOOL WINAPI DECLSPEC_HOTPATCH CreateProcessInternalW( HANDLE token, const WCHAR const WCHAR *cur_dir, STARTUPINFOW *startup_info, PROCESS_INFORMATION *info, HANDLE *new_token ) { - const struct proc_thread_attr *handle_list = NULL; + const struct proc_thread_attr *handle_list = NULL, *job_list = NULL; WCHAR name[MAX_PATH]; WCHAR *p, *tidy_cmdline = cmd_line; RTL_USER_PROCESS_PARAMETERS *params = NULL; @@ -580,6 +589,11 @@ BOOL WINAPI DECLSPEC_HOTPATCH CreateProcessInternalW( HANDLE token, const WCHAR params->ConsoleHandle = console->reference; break; } + case PROC_THREAD_ATTRIBUTE_JOB_LIST: + job_list = &attrs->attrs[i]; + TRACE( "PROC_THREAD_ATTRIBUTE_JOB_LIST handle count %Iu.\n", + attrs->attrs[i].size / sizeof(HANDLE) ); + break; default: FIXME("Unsupported attribute %#Ix.\n", attrs->attrs[i].attr); break; @@ -594,7 +608,7 @@ BOOL WINAPI DECLSPEC_HOTPATCH CreateProcessInternalW( HANDLE token, const WCHAR if (flags & CREATE_SUSPENDED) nt_flags |= PROCESS_CREATE_FLAGS_SUSPENDED;
status = create_nt_process( token, debug, process_attr, thread_attr, - nt_flags, params, &rtl_info, parent, handle_list ); + nt_flags, params, &rtl_info, parent, handle_list, job_list ); switch (status) { case STATUS_SUCCESS:
Signed-off-by: Paul Gofman pgofman@codeweavers.com --- dlls/kernel32/tests/process.c | 27 +++++++++++++++++++++++++++ dlls/ntdll/unix/process.c | 20 +++++++++++++++++--- server/process.c | 27 +++++++++++++++++++++++++++ server/protocol.def | 2 ++ 4 files changed, 73 insertions(+), 3 deletions(-)
diff --git a/dlls/kernel32/tests/process.c b/dlls/kernel32/tests/process.c index 9acf0aaaaee..44c37c668fc 100644 --- a/dlls/kernel32/tests/process.c +++ b/dlls/kernel32/tests/process.c @@ -4540,6 +4540,9 @@ static void test_nested_jobs(void) static void test_job_list_attribute(void) { PPROC_THREAD_ATTRIBUTE_LIST attrs; + char buffer[MAX_PATH + 19]; + PROCESS_INFORMATION pi; + STARTUPINFOEXA si; HANDLE jobs[2]; SIZE_T size; BOOL ret; @@ -4586,6 +4589,30 @@ static void test_job_list_attribute(void) sizeof(*jobs) * 2, NULL, NULL); ok(ret, "Got unexpected ret %#x, GetLastError() %u.\n", ret, GetLastError());
+ ret = pInitializeProcThreadAttributeList(attrs, 1, 0, &size); + ok(ret, "Got unexpected ret %#x, GetLastError() %u.\n", ret, GetLastError()); + ret = pUpdateProcThreadAttribute(attrs, 0, PROC_THREAD_ATTRIBUTE_JOB_LIST, jobs, + sizeof(*jobs), NULL, NULL); + + memset(&si, 0, sizeof(si)); + si.StartupInfo.cb = sizeof(si); + si.lpAttributeList = attrs; + sprintf(buffer, ""%s" process wait", selfname); + + ret = CreateProcessA(NULL, buffer, NULL, NULL, FALSE, EXTENDED_STARTUPINFO_PRESENT, NULL, NULL, + (STARTUPINFOA *)&si, &pi); + ok(!ret && GetLastError() == ERROR_INVALID_HANDLE, "Got unexpected ret %#x, GetLastError() %u.\n", + ret, GetLastError()); + + ret = pInitializeProcThreadAttributeList(attrs, 1, 0, &size); + ok(ret, "Got unexpected ret %#x, GetLastError() %u.\n", ret, GetLastError()); + ret = pUpdateProcThreadAttribute(attrs, 0, PROC_THREAD_ATTRIBUTE_JOB_LIST, jobs + 1, + sizeof(*jobs), NULL, NULL); + ret = CreateProcessA(NULL, buffer, NULL, NULL, FALSE, EXTENDED_STARTUPINFO_PRESENT, NULL, NULL, + (STARTUPINFOA *)&si, &pi); + ok(!ret && GetLastError() == ERROR_INVALID_HANDLE, "Got unexpected ret %#x, GetLastError() %u.\n", + ret, GetLastError()); + pDeleteProcThreadAttributeList(attrs); heap_free(attrs); } diff --git a/dlls/ntdll/unix/process.c b/dlls/ntdll/unix/process.c index 54e9680699e..340fe9cc304 100644 --- a/dlls/ntdll/unix/process.c +++ b/dlls/ntdll/unix/process.c @@ -628,9 +628,9 @@ NTSTATUS WINAPI NtCreateUserProcess( HANDLE *process_handle_ptr, HANDLE *thread_ UNICODE_STRING redir, path = {0}; OBJECT_ATTRIBUTES attr, empty_attr = { sizeof(empty_attr) }; SIZE_T i, attr_count = (ps_attr->TotalLength - sizeof(ps_attr->TotalLength)) / sizeof(PS_ATTRIBUTE); - const PS_ATTRIBUTE *handles_attr = NULL; - data_size_t handles_size; - obj_handle_t *handles; + const PS_ATTRIBUTE *handles_attr = NULL, *jobs_attr = NULL; + data_size_t handles_size, jobs_size; + obj_handle_t *handles, *jobs;
for (i = 0; i < attr_count; i++) { @@ -653,6 +653,9 @@ NTSTATUS WINAPI NtCreateUserProcess( HANDLE *process_handle_ptr, HANDLE *thread_ if (process_flags & PROCESS_CREATE_FLAGS_INHERIT_HANDLES) handles_attr = &ps_attr->Attributes[i]; break; + case PS_ATTRIBUTE_JOB_LIST: + jobs_attr = &ps_attr->Attributes[i]; + break; default: if (ps_attr->Attributes[i].Attribute & PS_ATTRIBUTE_INPUT) FIXME( "unhandled input attribute %lx\n", ps_attr->Attributes[i].Attribute ); @@ -690,6 +693,13 @@ NTSTATUS WINAPI NtCreateUserProcess( HANDLE *process_handle_ptr, HANDLE *thread_ goto done; }
+ if ((status = alloc_handle_list( jobs_attr, &jobs, &jobs_size ))) + { + free( objattr ); + free( handles ); + goto done; + } + /* create the socket for the new process */
if (socketpair( PF_UNIX, SOCK_STREAM, 0, socketfd ) == -1) @@ -697,6 +707,7 @@ NTSTATUS WINAPI NtCreateUserProcess( HANDLE *process_handle_ptr, HANDLE *thread_ status = STATUS_TOO_MANY_OPENED_FILES; free( objattr ); free( handles ); + free( jobs ); goto done; } #ifdef SO_PASSCRED @@ -722,8 +733,10 @@ NTSTATUS WINAPI NtCreateUserProcess( HANDLE *process_handle_ptr, HANDLE *thread_ req->access = process_access; req->info_size = startup_info_size; req->handles_size = handles_size; + req->jobs_size = jobs_size; wine_server_add_data( req, objattr, attr_len ); wine_server_add_data( req, handles, handles_size ); + wine_server_add_data( req, jobs, jobs_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 ))) @@ -736,6 +749,7 @@ NTSTATUS WINAPI NtCreateUserProcess( HANDLE *process_handle_ptr, HANDLE *thread_ SERVER_END_REQ; free( objattr ); free( handles ); + free( jobs );
if (status) { diff --git a/server/process.c b/server/process.c index 32c6fdd269b..35b0f9d20fb 100644 --- a/server/process.c +++ b/server/process.c @@ -1106,6 +1106,8 @@ DECL_HANDLER(new_process) struct thread *parent_thread = current; int socket_fd = thread_get_inflight_fd( current, req->socket_fd ); const obj_handle_t *handles = NULL; + const obj_handle_t *job_handles = NULL; + unsigned int i, job_handle_count; struct job *job;
if (socket_fd == -1) @@ -1178,6 +1180,31 @@ DECL_HANDLER(new_process) info_ptr = (const char *)info_ptr + req->handles_size; info->data_size -= req->handles_size; } + + if ((req->jobs_size & 3) || req->jobs_size > info->data_size) + { + set_error( STATUS_INVALID_PARAMETER ); + close( socket_fd ); + goto done; + } + if (req->jobs_size) + { + job_handles = info_ptr; + info_ptr = (const char *)info_ptr + req->jobs_size; + info->data_size -= req->jobs_size; + } + + job_handle_count = req->jobs_size / sizeof(*handles); + for (i = 0; i < job_handle_count; ++i) + { + if (!(job = get_job_obj( current->process, job_handles[i], JOB_OBJECT_ASSIGN_PROCESS ))) + { + close( socket_fd ); + goto done; + } + release_object( job ); + } + info->info_size = min( req->info_size, info->data_size );
if (req->info_size < sizeof(*info->data)) diff --git a/server/protocol.def b/server/protocol.def index 6d8208b128b..4239be834d1 100644 --- a/server/protocol.def +++ b/server/protocol.def @@ -854,8 +854,10 @@ typedef struct unsigned short machine; /* architecture 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 */ + data_size_t jobs_size; /* length of jobs list */ VARARG(objattr,object_attributes); /* object attributes */ VARARG(handles,uints,handles_size); /* handles list */ + VARARG(jobs,uints,jobs_size); /* jobs list */ VARARG(info,startup_info,info_size); /* startup information */ VARARG(env,unicode_str); /* environment for new process */ @REPLY
Hi,
While running your changed tests, I think I found new failures. Being a bot and all I'm not very good at pattern recognition, so I might be wrong, but could you please double-check?
Full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=90895
Your paranoid android.
=== debiant2 (32 bit WoW report) ===
ntdll: om.c:2307: Test failed: got 87
Paul Gofman pgofman@codeweavers.com writes:
- job_handle_count = req->jobs_size / sizeof(*handles);
- for (i = 0; i < job_handle_count; ++i)
- {
if (!(job = get_job_obj( current->process, job_handles[i], JOB_OBJECT_ASSIGN_PROCESS )))
{
close( socket_fd );
goto done;
}
release_object( job );
- }
It doesn't seem useful to pre-check the handles, you'll need to handle failures when assigning the process anyway.
On 5/20/21 10:56, Alexandre Julliard wrote:
Paul Gofman pgofman@codeweavers.com writes:
- job_handle_count = req->jobs_size / sizeof(*handles);
- for (i = 0; i < job_handle_count; ++i)
- {
if (!(job = get_job_obj( current->process, job_handles[i], JOB_OBJECT_ASSIGN_PROCESS )))
{
close( socket_fd );
goto done;
}
release_object( job );
- }
It doesn't seem useful to pre-check the handles, you'll need to handle failures when assigning the process anyway.
The reason I did it this way is that invalid handle aborts the process creation without changing any job's state, while error adding job to process leaves the state of the jobs earlier in the list altered if aborting on adding next job. So I probably need to precheck (get) the handles before assigning the process anyway. Should I move this check to that place (and squash the patches)?
Paul Gofman pgofman@codeweavers.com writes:
On 5/20/21 10:56, Alexandre Julliard wrote:
Paul Gofman pgofman@codeweavers.com writes:
- job_handle_count = req->jobs_size / sizeof(*handles);
- for (i = 0; i < job_handle_count; ++i)
- {
if (!(job = get_job_obj( current->process, job_handles[i], JOB_OBJECT_ASSIGN_PROCESS )))
{
close( socket_fd );
goto done;
}
release_object( job );
- }
It doesn't seem useful to pre-check the handles, you'll need to handle failures when assigning the process anyway.
The reason I did it this way is that invalid handle aborts the process creation without changing any job's state, while error adding job to process leaves the state of the jobs earlier in the list altered if aborting on adding next job. So I probably need to precheck (get) the handles before assigning the process anyway. Should I move this check to that place (and squash the patches)?
I'd expect that the process cleanup would undo what was done to the previous jobs. Otherwise you can't allow add_job_process() to fail in patch 4.
On 5/20/21 11:56, Alexandre Julliard wrote:
Paul Gofman pgofman@codeweavers.com writes:
On 5/20/21 10:56, Alexandre Julliard wrote:
Paul Gofman pgofman@codeweavers.com writes:
- job_handle_count = req->jobs_size / sizeof(*handles);
- for (i = 0; i < job_handle_count; ++i)
- {
if (!(job = get_job_obj( current->process, job_handles[i], JOB_OBJECT_ASSIGN_PROCESS )))
{
close( socket_fd );
goto done;
}
release_object( job );
- }
It doesn't seem useful to pre-check the handles, you'll need to handle failures when assigning the process anyway.
The reason I did it this way is that invalid handle aborts the process creation without changing any job's state, while error adding job to process leaves the state of the jobs earlier in the list altered if aborting on adding next job. So I probably need to precheck (get) the handles before assigning the process anyway. Should I move this check to that place (and squash the patches)?
I'd expect that the process cleanup would undo what was done to the previous jobs. Otherwise you can't allow add_job_process() to fail in patch 4.
But my tests in patch 4 show that this is not the case. When the process can't be assigned to the second job in list, the first one still gets the parent and even sends completions for adding and removing nonexistent process. My test, besides querying process counts from that unexpectedly altered job, checks that it got the parent by attempting another process create to be sure.
Paul Gofman pgofman@codeweavers.com writes:
On 5/20/21 11:56, Alexandre Julliard wrote:
Paul Gofman pgofman@codeweavers.com writes:
On 5/20/21 10:56, Alexandre Julliard wrote:
Paul Gofman pgofman@codeweavers.com writes:
- job_handle_count = req->jobs_size / sizeof(*handles);
- for (i = 0; i < job_handle_count; ++i)
- {
if (!(job = get_job_obj( current->process, job_handles[i], JOB_OBJECT_ASSIGN_PROCESS )))
{
close( socket_fd );
goto done;
}
release_object( job );
- }
It doesn't seem useful to pre-check the handles, you'll need to handle failures when assigning the process anyway.
The reason I did it this way is that invalid handle aborts the process creation without changing any job's state, while error adding job to process leaves the state of the jobs earlier in the list altered if aborting on adding next job. So I probably need to precheck (get) the handles before assigning the process anyway. Should I move this check to that place (and squash the patches)?
I'd expect that the process cleanup would undo what was done to the previous jobs. Otherwise you can't allow add_job_process() to fail in patch 4.
But my tests in patch 4 show that this is not the case. When the process can't be assigned to the second job in list, the first one still gets the parent and even sends completions for adding and removing nonexistent process. My test, besides querying process counts from that unexpectedly altered job, checks that it got the parent by attempting another process create to be sure.
Ah, I see. Sounds very much like a Windows bug, but I guess we have to do the same then. Thanks!
Signed-off-by: Paul Gofman pgofman@codeweavers.com --- dlls/kernel32/tests/process.c | 260 +++++++++++++++++++++++++++++++++- server/process.c | 12 ++ 2 files changed, 268 insertions(+), 4 deletions(-)
diff --git a/dlls/kernel32/tests/process.c b/dlls/kernel32/tests/process.c index 44c37c668fc..beb69352e4d 100644 --- a/dlls/kernel32/tests/process.c +++ b/dlls/kernel32/tests/process.c @@ -4537,15 +4537,22 @@ static void test_nested_jobs(void) CloseHandle(job2); }
-static void test_job_list_attribute(void) +static void test_job_list_attribute(HANDLE parent_job) { + JOBOBJECT_BASIC_ACCOUNTING_INFORMATION job_info; + JOBOBJECT_EXTENDED_LIMIT_INFORMATION limit_info; + JOBOBJECT_ASSOCIATE_COMPLETION_PORT port_info; PPROC_THREAD_ATTRIBUTE_LIST attrs; char buffer[MAX_PATH + 19]; PROCESS_INFORMATION pi; + OVERLAPPED *overlapped; + HANDLE jobs[2], port; STARTUPINFOEXA si; - HANDLE jobs[2]; + ULONG_PTR value; + BOOL ret, out; + HANDLE tmp; SIZE_T size; - BOOL ret; + DWORD key;
if (!pInitializeProcThreadAttributeList) { @@ -4613,8 +4620,253 @@ static void test_job_list_attribute(void) ok(!ret && GetLastError() == ERROR_INVALID_HANDLE, "Got unexpected ret %#x, GetLastError() %u.\n", ret, GetLastError());
+ ret = pInitializeProcThreadAttributeList(attrs, 1, 0, &size); + ok(ret, "Got unexpected ret %#x, GetLastError() %u.\n", ret, GetLastError()); + ret = pUpdateProcThreadAttribute(attrs, 0, PROC_THREAD_ATTRIBUTE_JOB_LIST, &parent_job, + sizeof(parent_job), NULL, NULL); + ret = CreateProcessA(NULL, buffer, NULL, NULL, FALSE, EXTENDED_STARTUPINFO_PRESENT, NULL, NULL, + (STARTUPINFOA *)&si, &pi); + ok(ret, "Got unexpected ret %#x, GetLastError() %u.\n", ret, GetLastError()); + + ret = pIsProcessInJob(pi.hProcess, parent_job, &out); + ok(ret, "IsProcessInJob error %u\n", GetLastError()); + ok(out, "IsProcessInJob returned out=%u\n", out); + + TerminateProcess(pi.hProcess, 0); + wait_and_close_child_process(&pi); + + jobs[0] = pCreateJobObjectW(NULL, NULL); + ok(!!jobs[0], "CreateJobObjectA error %u\n", GetLastError()); + jobs[1] = pCreateJobObjectW(NULL, NULL); + ok(!!jobs[1], "CreateJobObjectA error %u\n", GetLastError()); + + /* Breakaway works for the inherited job only. */ + limit_info.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_BREAKAWAY_OK; + ret = pSetInformationJobObject(parent_job, JobObjectExtendedLimitInformation, &limit_info, sizeof(limit_info)); + ok(ret, "SetInformationJobObject error %u\n", GetLastError()); + limit_info.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_BREAKAWAY_OK + | JOB_OBJECT_LIMIT_SILENT_BREAKAWAY_OK; + ret = pSetInformationJobObject(jobs[1], JobObjectExtendedLimitInformation, &limit_info, sizeof(limit_info)); + ok(ret, "SetInformationJobObject error %u\n", GetLastError()); + + ret = pInitializeProcThreadAttributeList(attrs, 1, 0, &size); + ok(ret, "Got unexpected ret %#x, GetLastError() %u.\n", ret, GetLastError()); + ret = pUpdateProcThreadAttribute(attrs, 0, PROC_THREAD_ATTRIBUTE_JOB_LIST, jobs + 1, + sizeof(*jobs), NULL, NULL); + ret = CreateProcessA(NULL, buffer, NULL, NULL, FALSE, EXTENDED_STARTUPINFO_PRESENT + | CREATE_BREAKAWAY_FROM_JOB, NULL, NULL, (STARTUPINFOA *)&si, &pi); + ok(ret, "Got unexpected ret %#x, GetLastError() %u.\n", ret, GetLastError()); + + ret = pIsProcessInJob(pi.hProcess, parent_job, &out); + ok(ret, "IsProcessInJob error %u\n", GetLastError()); + ok(!out, "IsProcessInJob returned out=%u\n", out); + + ret = pIsProcessInJob(pi.hProcess, jobs[1], &out); + ok(ret, "IsProcessInJob error %u\n", GetLastError()); + ok(out, "IsProcessInJob returned out=%u\n", out); + + ret = pIsProcessInJob(pi.hProcess, jobs[0], &out); + ok(ret, "IsProcessInJob error %u\n", GetLastError()); + ok(!out, "IsProcessInJob returned out=%u\n", out); + + TerminateProcess(pi.hProcess, 0); + wait_and_close_child_process(&pi); + + CloseHandle(jobs[1]); + jobs[1] = pCreateJobObjectW(NULL, NULL); + ok(!!jobs[1], "CreateJobObjectA error %u\n", GetLastError()); + + ret = pInitializeProcThreadAttributeList(attrs, 1, 0, &size); + ok(ret, "Got unexpected ret %#x, GetLastError() %u.\n", ret, GetLastError()); + ret = pUpdateProcThreadAttribute(attrs, 0, PROC_THREAD_ATTRIBUTE_JOB_LIST, jobs + 1, + sizeof(*jobs), NULL, NULL); + ret = CreateProcessA(NULL, buffer, NULL, NULL, FALSE, EXTENDED_STARTUPINFO_PRESENT, + NULL, NULL, (STARTUPINFOA *)&si, &pi); + ok(ret, "Got unexpected ret %#x, GetLastError() %u.\n", ret, GetLastError()); + + ret = pIsProcessInJob(pi.hProcess, parent_job, &out); + ok(ret, "IsProcessInJob error %u\n", GetLastError()); + ok(out, "IsProcessInJob returned out=%u\n", out); + + ret = pIsProcessInJob(pi.hProcess, jobs[1], &out); + ok(ret, "IsProcessInJob error %u\n", GetLastError()); + ok(out, "IsProcessInJob returned out=%u\n", out); + + ret = pIsProcessInJob(pi.hProcess, jobs[0], &out); + ok(ret, "IsProcessInJob error %u\n", GetLastError()); + ok(!out, "IsProcessInJob returned out=%u\n", out); + + TerminateProcess(pi.hProcess, 0); + wait_and_close_child_process(&pi); + + ret = pQueryInformationJobObject(jobs[0], JobObjectBasicAccountingInformation, &job_info, + sizeof(job_info), NULL); + ok(ret, "QueryInformationJobObject error %u\n", GetLastError()); + ok(!job_info.TotalProcesses, "Got unexpected TotalProcesses %u.\n", job_info.TotalProcesses); + ok(!job_info.ActiveProcesses, "Got unexpected ActiveProcesses %u.\n", job_info.ActiveProcesses); + + ret = pQueryInformationJobObject(jobs[1], JobObjectBasicAccountingInformation, &job_info, + sizeof(job_info), NULL); + ok(ret, "QueryInformationJobObject error %u\n", GetLastError()); + ok(job_info.TotalProcesses == 1, "Got unexpected TotalProcesses %u.\n", job_info.TotalProcesses); + ok(!job_info.ActiveProcesses || job_info.ActiveProcesses == 1, "Got unexpected ActiveProcesses %u.\n", + job_info.ActiveProcesses); + + /* Fails due to the second job already has the parent other than the first job in the list. */ + ret = pInitializeProcThreadAttributeList(attrs, 1, 0, &size); + ok(ret, "Got unexpected ret %#x, GetLastError() %u.\n", ret, GetLastError()); + ret = pUpdateProcThreadAttribute(attrs, 0, PROC_THREAD_ATTRIBUTE_JOB_LIST, jobs, + 2 * sizeof(*jobs), NULL, NULL); + + port = pCreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 1); + ok(!!port, "CreateIoCompletionPort error %u\n", GetLastError()); + + port_info.CompletionPort = port; + port_info.CompletionKey = jobs[0]; + ret = pSetInformationJobObject(jobs[0], JobObjectAssociateCompletionPortInformation, &port_info, sizeof(port_info)); + ok(ret, "SetInformationJobObject error %u\n", GetLastError()); + + ret = GetQueuedCompletionStatus(port, &key, &value, &overlapped, 0); + ok(!ret, "GetQueuedCompletionStatus succeeded.\n"); + + ret = CreateProcessA(NULL, buffer, NULL, NULL, FALSE, EXTENDED_STARTUPINFO_PRESENT, NULL, NULL, + (STARTUPINFOA *)&si, &pi); + ok(!ret && GetLastError() == ERROR_ACCESS_DENIED, "Got unexpected ret %#x, GetLastError() %u.\n", + ret, GetLastError()); + + ret = GetQueuedCompletionStatus(port, &key, &value, &overlapped, 100); + ok(ret, "GetQueuedCompletionStatus: %x\n", GetLastError()); + ok(key == JOB_OBJECT_MSG_NEW_PROCESS, "unexpected key %x\n", key); + ok((HANDLE)value == jobs[0], "unexpected value %p\n", (void *)value); + ok(!!overlapped, "Got zero pid.\n"); + + ret = GetQueuedCompletionStatus(port, &key, &value, &overlapped, 100); + ok(ret, "GetQueuedCompletionStatus: %x\n", GetLastError()); + ok(key == JOB_OBJECT_MSG_EXIT_PROCESS, "unexpected key %x\n", key); + ok((HANDLE)value == jobs[0], "unexpected value %p\n", (void *)value); + ok(!!overlapped, "Got zero pid.\n"); + + ret = GetQueuedCompletionStatus(port, &key, &value, &overlapped, 100); + ok(ret, "GetQueuedCompletionStatus: %x\n", GetLastError()); + ok(key == JOB_OBJECT_MSG_ACTIVE_PROCESS_ZERO, "unexpected key %x\n", key); + ok((HANDLE)value == jobs[0], "unexpected value %p\n", (void *)value); + ok(!overlapped, "Got unexpected overlapped %p.\n", overlapped); + + ret = GetQueuedCompletionStatus(port, &key, &value, &overlapped, 0); + ok(!ret, "GetQueuedCompletionStatus succeeded.\n"); + + CloseHandle(port); + + /* The first job got updated even though the process creation failed. */ + ret = pQueryInformationJobObject(jobs[0], JobObjectBasicAccountingInformation, &job_info, + sizeof(job_info), NULL); + ok(ret, "QueryInformationJobObject error %u\n", GetLastError()); + ok(job_info.TotalProcesses == 1, "Got unexpected TotalProcesses %u.\n", job_info.TotalProcesses); + ok(!job_info.ActiveProcesses, "Got unexpected ActiveProcesses %u.\n", job_info.ActiveProcesses); + + ret = pQueryInformationJobObject(jobs[1], JobObjectBasicAccountingInformation, &job_info, + sizeof(job_info), NULL); + ok(ret, "QueryInformationJobObject error %u\n", GetLastError()); + ok(job_info.TotalProcesses == 1, "Got unexpected TotalProcesses %u.\n", job_info.TotalProcesses); + ok(!job_info.ActiveProcesses, "Got unexpected ActiveProcesses %u.\n", job_info.ActiveProcesses); + + /* Check that the first job actually got the job_parent as parent. */ + ret = pInitializeProcThreadAttributeList(attrs, 1, 0, &size); + ok(ret, "Got unexpected ret %#x, GetLastError() %u.\n", ret, GetLastError()); + ret = pUpdateProcThreadAttribute(attrs, 0, PROC_THREAD_ATTRIBUTE_JOB_LIST, jobs, + sizeof(*jobs), NULL, NULL); + ret = CreateProcessA(NULL, buffer, NULL, NULL, FALSE, EXTENDED_STARTUPINFO_PRESENT + | CREATE_BREAKAWAY_FROM_JOB, NULL, NULL, (STARTUPINFOA *)&si, &pi); + ok(ret, "Got unexpected ret %#x, GetLastError() %u.\n", ret, GetLastError()); + + ret = pIsProcessInJob(pi.hProcess, parent_job, &out); + ok(ret, "IsProcessInJob error %u\n", GetLastError()); + ok(out, "IsProcessInJob returned out=%u\n", out); + + ret = pIsProcessInJob(pi.hProcess, jobs[0], &out); + ok(ret, "IsProcessInJob error %u\n", GetLastError()); + ok(out, "IsProcessInJob returned out=%u\n", out); + + TerminateProcess(pi.hProcess, 0); + wait_and_close_child_process(&pi); + + tmp = jobs[0]; + jobs[0] = jobs[1]; + jobs[1] = tmp; + + ret = pInitializeProcThreadAttributeList(attrs, 1, 0, &size); + ok(ret, "Got unexpected ret %#x, GetLastError() %u.\n", ret, GetLastError()); + ret = pUpdateProcThreadAttribute(attrs, 0, PROC_THREAD_ATTRIBUTE_JOB_LIST, jobs, + 2 * sizeof(*jobs), NULL, NULL); + ret = CreateProcessA(NULL, buffer, NULL, NULL, FALSE, EXTENDED_STARTUPINFO_PRESENT, NULL, NULL, + (STARTUPINFOA *)&si, &pi); + ok(!ret && GetLastError() == ERROR_ACCESS_DENIED, "Got unexpected ret %#x, GetLastError() %u.\n", + ret, GetLastError()); + + CloseHandle(jobs[0]); + CloseHandle(jobs[1]); + + jobs[0] = pCreateJobObjectW(NULL, NULL); + ok(!!jobs[0], "CreateJobObjectA error %u\n", GetLastError()); + jobs[1] = pCreateJobObjectW(NULL, NULL); + ok(!!jobs[1], "CreateJobObjectA error %u\n", GetLastError()); + + /* Create the job chain successfully and check the job chain. */ + ret = pInitializeProcThreadAttributeList(attrs, 1, 0, &size); + ok(ret, "Got unexpected ret %#x, GetLastError() %u.\n", ret, GetLastError()); + ret = pUpdateProcThreadAttribute(attrs, 0, PROC_THREAD_ATTRIBUTE_JOB_LIST, jobs, + 2 * sizeof(*jobs), NULL, NULL); + ret = CreateProcessA(NULL, buffer, NULL, NULL, FALSE, EXTENDED_STARTUPINFO_PRESENT, + NULL, NULL, (STARTUPINFOA *)&si, &pi); + ok(ret, "Got unexpected ret %#x, GetLastError() %u.\n", ret, GetLastError()); + + ret = pIsProcessInJob(pi.hProcess, parent_job, &out); + ok(ret, "IsProcessInJob error %u\n", GetLastError()); + ok(out, "IsProcessInJob returned out=%u\n", out); + + ret = pIsProcessInJob(pi.hProcess, jobs[0], &out); + ok(ret, "IsProcessInJob error %u\n", GetLastError()); + ok(out, "IsProcessInJob returned out=%u\n", out); + + ret = pIsProcessInJob(pi.hProcess, jobs[1], &out); + ok(ret, "IsProcessInJob error %u\n", GetLastError()); + ok(out, "IsProcessInJob returned out=%u\n", out); + + TerminateProcess(pi.hProcess, 0); + wait_and_close_child_process(&pi); + + ret = pInitializeProcThreadAttributeList(attrs, 1, 0, &size); + ok(ret, "Got unexpected ret %#x, GetLastError() %u.\n", ret, GetLastError()); + ret = pUpdateProcThreadAttribute(attrs, 0, PROC_THREAD_ATTRIBUTE_JOB_LIST, jobs + 1, + sizeof(*jobs), NULL, NULL); + ret = CreateProcessA(NULL, buffer, NULL, NULL, FALSE, EXTENDED_STARTUPINFO_PRESENT + | CREATE_BREAKAWAY_FROM_JOB, NULL, NULL, (STARTUPINFOA *)&si, &pi); + ok(ret, "Got unexpected ret %#x, GetLastError() %u.\n", ret, GetLastError()); + + ret = pIsProcessInJob(pi.hProcess, parent_job, &out); + ok(ret, "IsProcessInJob error %u\n", GetLastError()); + ok(out, "IsProcessInJob returned out=%u\n", out); + + ret = pIsProcessInJob(pi.hProcess, jobs[0], &out); + ok(ret, "IsProcessInJob error %u\n", GetLastError()); + ok(out, "IsProcessInJob returned out=%u\n", out); + + ret = pIsProcessInJob(pi.hProcess, jobs[1], &out); + ok(ret, "IsProcessInJob error %u\n", GetLastError()); + ok(out, "IsProcessInJob returned out=%u\n", out); + + TerminateProcess(pi.hProcess, 0); + wait_and_close_child_process(&pi); + + CloseHandle(jobs[0]); + CloseHandle(jobs[1]); + pDeleteProcThreadAttributeList(attrs); heap_free(attrs); + + limit_info.BasicLimitInformation.LimitFlags = 0; + ret = pSetInformationJobObject(parent_job, JobObjectExtendedLimitInformation, &limit_info, sizeof(limit_info)); + ok(ret, "SetInformationJobObject error %u\n", GetLastError()); }
START_TEST(process) @@ -4768,7 +5020,7 @@ START_TEST(process) test_nested_jobs(); job = test_AddSelfToJob(); test_jobInheritance(job); - test_job_list_attribute(); + test_job_list_attribute(job); test_BreakawayOk(job); CloseHandle(job); } diff --git a/server/process.c b/server/process.c index 35b0f9d20fb..e91a63bed08 100644 --- a/server/process.c +++ b/server/process.c @@ -1276,6 +1276,18 @@ DECL_HANDLER(new_process) job = job->parent; }
+ for (i = 0; i < job_handle_count; ++i) + { + job = get_job_obj( current->process, job_handles[i], JOB_OBJECT_ASSIGN_PROCESS ); + add_job_process( job, process ); + release_object( job ); + if (get_error()) + { + release_job_process( process ); + goto done; + } + } + /* connect to the window station */ connect_process_winstation( process, parent_thread, parent );
Hi,
While running your changed tests, I think I found new failures. Being a bot and all I'm not very good at pattern recognition, so I might be wrong, but could you please double-check?
Full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=90893
Your paranoid android.
=== w10pro64_he (64 bit report) ===
kernel32: process.c:4264: Test failed: Unexpected return value, error 203.