CreateProcessW + PROC_THREAD_ATTRIBUTE_JOB_LIST: guarantees if the creator terminates during process creation

2026-11-11T05:51:36.9533333+00:00

Hello,

I am evaluating the documented containment guarantees of CreateProcessW when used with STARTUPINFOEXW, EXTENDED_STARTUPINFO_PRESENT, and PROC_THREAD_ATTRIBUTE_JOB_LIST.

My question concerns an unexpected termination of the creating process while CreateProcessW is still executing, before the creator receives a return value.

Does Windows guarantee that either:

No newly created child capable of executing application code survives; or

Every surviving child capable of executing application code has already been assigned to all specified Job Objects?

If neither is guaranteed, what outcomes are permitted?

I would also appreciate clarification regarding partial Job association or rollback, and whether an independent supervisor holding a Job handle can reliably establish the identity of a child when the creator never receives its process handles.

I am looking for the supported API contract, including version-specific preconditions and exceptions, rather than behavior observed in typical executions.

Could a Microsoft Windows API engineer clarify this or point me to authoritative documentation?

Thank you!

Windows for business | Windows Client for IT Pros | User experience | Other
0 comments No comments

2 answers

Sort by: Oldest
  1. AI answer

    2026-11-11T05:52:40.9066667+00:00

    Neither proposed outcome is guaranteed by the published Win32 API contract when the creating process terminates before CreateProcessW returns; code requiring that guarantee must place process creation and Job ownership in the long-lived supervisor itself.

    PROC_THREAD_ATTRIBUTE_JOB_LIST specifies that the listed Job handles are assigned to the child in list order, but the documentation does not define CreateProcessW as a transaction, nor does it specify cancellation, rollback, or all-or-nothing behavior when the caller disappears during the call.^1^

    Supported contract

    For an uninterrupted call:

    • A nonzero return means process creation succeeded, and PROCESS_INFORMATION receives the new process and initial-thread handles and identifiers.
    • Zero means the call failed; GetLastError supplies the failure status.
    • A successful return does not mean application initialization completed. CreateProcessW returns before initialization finishes, and a required-DLL failure can subsequently terminate the process.^2^
    • With PROC_THREAD_ATTRIBUTE_JOB_LIST, the child is to be assigned to the listed Jobs in the specified order.^1^

    The Job-list attribute is supported only on Windows 10 or later and Windows Server 2016 or later. Its backing Job-handle array must remain valid until the attribute list is destroyed.^1^

    Interrupted-call boundary

    The documented contract does not state what happens if the caller is terminated while CreateProcessW is in progress. In particular, it does not promise:

    • cancellation before the child can execute;
    • completion of every requested Job association;
    • rollback of associations already made;
    • termination of a partially created process;
    • delivery of PROCESS_INFORMATION elsewhere; or
    • an externally discoverable transaction identifier.

    Consequently, the documentation does not provide a finite authoritative list of “permitted outcomes.” A child may not exist, may already have terminated, or may exist after some internal creation work; however, treating any specific intermediate state—including partial Job membership—as supported behavior would rely on implementation details rather than the API contract.

    Job association itself is persistent once established: after a process is associated with a Job, that association cannot be broken. Multiple associations require a valid nested-Job hierarchy; nested Jobs were introduced in Windows 8 and Windows Server 2012.^3^ This permanence still does not document whether PROC_THREAD_ATTRIBUTE_JOB_LIST rolls back earlier assignments if a later assignment or the creation operation is interrupted.

    Reliable containment design

    Move the CreateProcessW call into the independent supervisor that already owns the Job handles. Keep that supervisor alive for the complete operation and, preferably, for the contained process’s lifetime. This gives it the returned process handle on success rather than requiring reconstruction after the creator dies.

    If the supervisor must ensure that processes already admitted to a Job are terminated when containment ownership disappears:

    1. Keep the controlling Job handle in the supervisor.
    2. Configure JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE.
    3. Ensure no unintended process retains another handle to that Job.

    Closing the last Job handle then terminates associated processes; without that limit, a Job is not destroyed until its last handle is closed and all associated processes have terminated.^3^ This protects processes that actually reached Job membership; it cannot extend the undocumented interruption semantics of process creation.

    Can another supervisor recover the child’s identity?

    It can observe current Job membership, but it cannot reliably reconstruct the unique process object from a PID alone:

    • QueryInformationJobObject(..., JobObjectBasicProcessIdList, ...) returns the Job’s current process-ID list. For nested Jobs, the list includes processes in child Jobs.^4^
    • The buffer may be incomplete when NumberOfProcessIdsInList is less than NumberOfAssignedProcesses; resize and retry.^5^
    • A Job completion port can report JOB_OBJECT_MSG_NEW_PROCESS with a PID, but ordinary Job messages are not guaranteed. Moreover, a reported PID may already have terminated and been recycled unless an open process handle is retained.^6^

    Therefore, neither Job enumeration nor completion-port notification is a reliable substitute for the process handle that CreateProcessW would have returned. The robust architecture is for the long-lived supervisor to perform creation and retain that handle. If an authoritative guarantee for caller death during the system call is required, it would need an explicit Microsoft API-contract clarification; the public documentation currently does not define one.


    References

    1. UpdateProcThreadAttribute function (processthreadsapi.h)
    2. CreateProcessW function (processthreadsapi.h)
    3. Job Objects
    4. QueryInformationJobObject function (jobapi2.h)
    5. JOBOBJECT_BASIC_PROCESS_ID_LIST structure (winnt.h)
    6. JOBOBJECT_ASSOCIATE_COMPLETION_PORT structure (winnt.h)
    AI-generated content may be incorrect. Read our transparency notes for more information.

    Was this answer helpful?

  2. Emmanuel ASIFIWE 0 Reputation points
    2026-11-11T07:02:06.5066667+00:00

    Hello,

    The current public Win32 API documentation does not explicitly guarantee your proposed all-or-nothing outcome when the creating process terminates before ********CreateProcessW returns.

    What Microsoft does document is below:

    PROC_THREAD_ATTRIBUTE_JOB_LIST supplies an ordered list of jobs to which the child is assigned. It is supported on Windows 10 and later and Windows Server 2016 and later. The backing attribute value must remain valid for the lifetime described by the attribute-list documentation. UpdateProcThreadAttribute

    Microsoft engineering guidance explains that the assignments are performed sequentially and before the initial thread is permitted to run. This removes the ordinary race in the traditional “create suspended, assign to job, resume” sequence. A more direct and mistake-free way of creating a process in a job object

    For a completed call, CreateProcessW reports success or failure and, on success, returns the process and initial-thread identities in PROCESS_INFORMATION. The documentation does not describe cancellation or result delivery when the caller itself disappears before observing the return. CreateProcessW

    The published contract does not state:

    that caller termination cancels the creation operation;

    that assignment to several jobs is transactional;

    that earlier assignments are rolled back if a later assignment cannot be completed;

    that a partially constructed process must be terminated;

    or that another process receives the missing PROCESS_INFORMATION.

    Therefore, if the creator terminates before the call returns, the independent supervisor must treat the launch result as indeterminate under the public contract. This does not prove that Windows can allow an executable child outside the requested jobs. It means the cited documentation is insufficient to use your proposed disjunction as a formal correctness guarantee or to enumerate every possible intermediate state.

    The ordered-list wording also does not establish externally observable partial association or rollback semantics. Multiple associations must form a valid nested-job hierarchy, with the assignments ordered accordingly, but the public documentation does not define interruption-time behavior between those assignments. Nested Jobs

    Regarding recovery by an independent supervisor:

    • QueryInformationJobObject with JobObjectBasicProcessIdList can return the job’s current process IDs, including processes in child jobs. This is a current-membership query, not an atomic record of a particular creation attempt. JOBOBJECT_BASIC_PROCESS_ID_LIST
    • Job completion-port notifications are not generally guaranteed, and Microsoft warns that PID-bearing notifications are subject to races and PID reuse unless an open process handle is retained. JOBOBJECT_ASSOCIATE_COMPLETION_PORT
    • A process handle provides stable object identity; a PID by itself does not provide equivalent lifetime protection. Process handles and identifiers

    Consequently, job enumeration or completion-port notification is not a reliable replacement for the process handle returned by CreateProcessW. The robust supported architecture is for the long-lived supervisor to perform CreateProcessW itself and retain hProcess. A dedicated per-launch job may reduce ambiguity operationally, but it does not create a documented atomic identity handoff.

    JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE can terminate processes that successfully reached job membership when the last job handle closes. In your scenario, an independent supervisor deliberately retains a handle, so termination of the creator is not the last-handle event and does not activate that limit. Job Objects

    If this interruption invariant is required for a security boundary or formal correctness argument, the public documentation is presently insufficient. It would require an explicit Windows API contract clarification from Microsoft, preferably through a Windows SDK developer-support case.

    Was this answer helpful?

    0 comments No comments

Your answer

Answers can be marked as 'Accepted' by the question author and 'Recommended' by moderators, which helps users know the answer solved the author's problem.