/********************************************************/
/* ntapi: Native API core library */
/* Copyright (C) 2013--2021 SysDeer Technologies, LLC */
/* Released under GPLv2 and GPLv3; see COPYING.NTAPI. */
/********************************************************/
#include <psxtypes/psxtypes.h>
#include <ntapi/nt_atomic.h>
#include <ntapi/nt_status.h>
#include <ntapi/nt_object.h>
#include <ntapi/nt_memory.h>
#include <ntapi/nt_thread.h>
#include <ntapi/nt_process.h>
#include <ntapi/nt_string.h>
#include <ntapi/ntapi.h>
#include "ntapi_impl.h"
intptr_t __cdecl __attr_hidden__ __tt_fork_v1(void);
uint32_t __fastcall __attr_hidden__ __tt_fork_child_entry_point(uintptr_t saved_regs_stack_pointer);
uint32_t __fastcall __attr_hidden__ __tt_fork_child_entry_point_adj(uintptr_t saved_regs_stack_pointer);
/** legacy fork chronology:
*
* parent:
* __ntapi_tt_fork ->
* __tt_fork ->
* __tt_fork_impl ->
* return to __tt_fork -->
* __ntapi_tt_fork
* -> return to caller
*
* child:
* __tt_fork_child_entry_point[_adj] ->
* __ntapi_tt_fork (internal return) ->
* -> return to caller
**/
static intptr_t __tt_fork_cancel(void * hprocess,int32_t status)
{
__ntapi->zw_terminate_process(hprocess,status);
__ntapi->zw_close(hprocess);
return status;
}
__attr_protected__
intptr_t __fastcall __tt_fork_impl_v1(
uintptr_t saved_regs_stack_pointer,
uintptr_t stack_adjustment)
{
int32_t status;
void * hprocess;
void * hthread;
void ** hport_session;
ntapi_internals * __internals;
nt_object_attributes oa;
nt_process_basic_information pbi;
nt_thread_context context;
nt_user_stack stack;
nt_memory_basic_information mbi;
nt_client_id cid;
nt_large_integer timeout;
cid.process_id = 0;
cid.thread_id = 0;
hprocess = 0;
hthread = 0;
oa.len = sizeof(nt_object_attributes);
oa.root_dir = 0;
oa.obj_name = 0;
oa.obj_attr = 0;
oa.sec_desc = 0;
oa.sec_qos = 0;
if ((status = __ntapi->zw_create_process(
&hprocess,
NT_PROCESS_ALL_ACCESS,
&oa,
NT_CURRENT_PROCESS_HANDLE,
1,0,0,0)))
return status;
if ((status = __ntapi->zw_query_information_process(
hprocess,
NT_PROCESS_BASIC_INFORMATION,
(void *)&pbi,
sizeof(nt_process_basic_information),
0)))
return __tt_fork_cancel(hprocess,status);
__ntapi->tt_aligned_block_memset(
&context,0,sizeof(nt_thread_context));
__INIT_CONTEXT(context);
context.STACK_POINTER_REGISTER = saved_regs_stack_pointer;
context.FAST_CALL_ARG0 = saved_regs_stack_pointer;
context.INSTRUCTION_POINTER_REGISTER = stack_adjustment
? (uintptr_t)__tt_fork_child_entry_point_adj
: (uintptr_t)__tt_fork_child_entry_point;
if ((status = __ntapi->zw_query_virtual_memory(
NT_CURRENT_PROCESS_HANDLE,
(void *)context.STACK_POINTER_REGISTER,
NT_MEMORY_BASIC_INFORMATION,
&mbi,sizeof(nt_memory_basic_information),0)))
return __tt_fork_cancel(hprocess,status);
stack.fixed_stack_base = (void *)0;
stack.fixed_stack_limit = (void *)0;
stack.expandable_stack_base = (void *)((uintptr_t)mbi.base_address + mbi.region_size);
stack.expandable_stack_limit = (void *)mbi.base_address;
stack.expandable_stack_bottom = (void *)mbi.allocation_base;
__internals = __ntapi_internals();
hport_session = &__internals->hport_tty_session;
timeout.quad = (-1) * 10 * 1000 * __NT_FORK_CHILD_WAIT_MILLISEC;
if (hport_session && *hport_session)
if ((status = __ntapi->tty_client_process_register(
*hport_session,
pbi.unique_process_id,
0, 0, &timeout)))
return __tt_fork_cancel(hprocess,status);
if ((status = __ntapi->zw_create_thread(
&hthread,
NT_THREAD_ALL_ACCESS,
&oa,hprocess,&cid,
&context,&stack,0)))
return __tt_fork_cancel(hprocess,status);
if (cid.process_id > 0) {
__internals->hany[0] = hprocess;
__internals->hany[1] = hthread;
__internals->hany[2] = (void *)cid.process_id;
__internals->hany[3] = (void *)cid.thread_id;
} else {
__internals->hany[0] = 0;
__internals->hany[1] = 0;
__internals->hany[2] = 0;
__internals->hany[3] = 0;
}
/* hoppla */
return NT_STATUS_SUCCESS;
}
int32_t __fastcall __ntapi_tt_fork_v1(
__out void ** hprocess,
__out void ** hthread,
__out nt_cid * cid)
{
int32_t status;
nt_large_integer timeout;
void ** hport_session;
void * htty_connected;
ntapi_internals * __internals;
__internals = __ntapi_internals();
hport_session = &__internals->hport_tty_session;
timeout.quad = (-1) * 10 * 1000 * __NT_FORK_CHILD_WAIT_MILLISEC;
htty_connected = 0;
while (at_locked_cas(&__internals->hlock,0,1))
__ntapi->zw_delay_execution(
NT_SYNC_NON_ALERTABLE,
&(nt_timeout){{0,0}});
if (hport_session && *hport_session) {
status = __ntapi_tt_create_inheritable_event(
&htty_connected,
NT_NOTIFICATION_EVENT,
NT_EVENT_NOT_SIGNALED);
if (status) {
at_store(&__internals->hlock,0);
return status;
}
}
if ((status = __tt_fork_v1())) {
at_store(&__internals->hlock,0);
return status;
}
*hprocess = __internals->hany[0];
*hthread = __internals->hany[1];
cid->process_id = (uintptr_t)__internals->hany[2];
cid->thread_id = (uintptr_t)__internals->hany[3];
at_store(&__internals->hlock,0);
if (hport_session && *hport_session) {
if (cid->process_id == 0) {
if ((status = __ntapi->tty_connect(
hport_session,
__internals->subsystem->base_named_objects,
NT_SECURITY_IMPERSONATION)))
return __tt_fork_cancel(NT_CURRENT_PROCESS_HANDLE,status);
__internals->hdev_mount_point_mgr = 0;
if (__internals->rtdata)
__internals->rtdata->hsession = *hport_session;
__ntapi->zw_set_event(
htty_connected,
0);
} else {
status = __ntapi->zw_wait_for_single_object(
htty_connected,
NT_SYNC_NON_ALERTABLE,
&timeout);
if (status && __PSX_DEBUG)
if ((status = __ntapi->zw_wait_for_single_object(
htty_connected,
NT_SYNC_NON_ALERTABLE,
0)))
status = __tt_fork_cancel(*hprocess,status);
}
__ntapi->zw_close(htty_connected);
}
return status;
}