Blame src/process/ntapi_tt_fork.c

a823bc
268ef4
/********************************************************/
268ef4
/*  ntapi: Native API core library                      */
64e606
/*  Copyright (C) 2013--2021  SysDeer Technologies, LLC */
268ef4
/*  Released under GPLv2 and GPLv3; see COPYING.NTAPI.  */
268ef4
/********************************************************/
268ef4
268ef4
#include <psxtypes/psxtypes.h>
268ef4
#include <ntapi/nt_atomic.h>
268ef4
#include <ntapi/nt_status.h>
268ef4
#include <ntapi/nt_object.h>
268ef4
#include <ntapi/nt_memory.h>
268ef4
#include <ntapi/nt_thread.h>
268ef4
#include <ntapi/nt_process.h>
268ef4
#include <ntapi/ntapi.h>
268ef4
#include "ntapi_impl.h"
268ef4
0a9ee4
#define __FORK_THREAD_STACK_COMMIT  (0x10000)
0a9ee4
#define __FORK_THREAD_STACK_RESERVE (0x20000)
0a9ee4
268ef4
static intptr_t	__fork_retry_stats  = 0;
268ef4
static intptr_t	__fork_resume_stats = 0;
268ef4
3841d3
static int __ipc_memfn(
3841d3
	struct dalist_ex *	dlist,
3841d3
	void **			addr,
3841d3
	size_t *		alloc_size)
3841d3
{
3841d3
	(void)dlist;
3841d3
	(void)addr;
3841d3
	(void)alloc_size;
3841d3
3841d3
	return DALIST_EMEMFN;
3841d3
}
3841d3
a823bc
static int32_t __fastcall __ntapi_tt_fork_finalize(void ** hprocess)
72fe5e
{
72fe5e
	int32_t			status;
30d28d
	int			page;
edb085
	nt_rtdata *		rtdata;
72fe5e
	ntapi_internals *	__internals;
72fe5e
72fe5e
	__internals = __ntapi_internals();
edb085
	rtdata      = __internals->rtdata;
72fe5e
abd389
	rtdata->cid_parent.process_id = rtdata->cid_self.process_id;
abd389
	rtdata->cid_parent.thread_id  = rtdata->cid_self.thread_id;
abd389
abd389
	rtdata->cid_self.process_id   = pe_get_current_process_id();
abd389
	rtdata->cid_self.thread_id    = pe_get_current_thread_id();
abd389
80b89c
	if ((status = __ntapi->zw_duplicate_object(
80b89c
			__internals->hprocess,
80b89c
			__internals->hprocess,
80b89c
			__internals->hprocess,
80b89c
			hprocess,0,0,
80b89c
			NT_DUPLICATE_SAME_ATTRIBUTES
80b89c
			|NT_DUPLICATE_SAME_ACCESS)))
72fe5e
		return status;
72fe5e
30d28d
	if ((status = dalist_init_ex(
30d28d
			&__internals->ipc_conns,
30d28d
			sizeof(nt_ipc_conn),
30d28d
			NT_ALLOCATION_GRANULARITY,
3841d3
			__ipc_memfn,
30d28d
			DALIST_MEMFN_CUSTOM)))
30d28d
		return status;
30d28d
30d28d
	dalist_deposit_memory_block(
30d28d
		&__internals->ipc_conns,
30d28d
		__internals->ntapi_img_sec_bss->ipc_buffer,
30d28d
		__NT_BSS_IPC_BUFFER_SIZE);
30d28d
846d29
	for (page=0; page<__internals->ipc_page; page++)
30d28d
		dalist_deposit_memory_block(
30d28d
			&__internals->ipc_conns,
30d28d
			__internals->ipc_pages[page],
30d28d
			NT_ALLOCATION_GRANULARITY);
30d28d
edb085
	rtdata->hsemctl     = 0;
edb085
	rtdata->hsempid     = 0;
edb085
47f21a
	rtdata->hmsqctl     = 0;
47f21a
	rtdata->hmsqpid     = 0;
47f21a
dbeebd
	rtdata->haflctl     = 0;
dbeebd
	rtdata->haflpid     = 0;
dbeebd
edb085
	rtdata->ipc_keys[0] = 0;
edb085
	rtdata->ipc_keys[1] = 0;
edb085
	rtdata->ipc_keys[2] = 0;
edb085
	rtdata->ipc_keys[3] = 0;
edb085
	rtdata->ipc_keys[4] = 0;
edb085
	rtdata->ipc_keys[5] = 0;
edb085
a823bc
	return NT_STATUS_SUCCESS;
72fe5e
}
72fe5e
268ef4
static int32_t __stdcall __fork_thread(void * ctx)
268ef4
{
268ef4
	intptr_t *	pstate;
268ef4
	intptr_t	state;
268ef4
	void *		hready;
268ef4
268ef4
	pstate = (intptr_t *)ctx;
268ef4
	state  = *pstate;
268ef4
	hready = (void *)state;
268ef4
268ef4
	at_store(
268ef4
		pstate,
268ef4
		0);
268ef4
268ef4
	return __ntapi->zw_terminate_thread(
268ef4
		NT_CURRENT_THREAD_HANDLE,
268ef4
		__ntapi->zw_set_event(
268ef4
			hready,0));
268ef4
}
268ef4
a823bc
static int32_t __fastcall __ntapi_tt_fork_child(
268ef4
	void *		hresumed,
80b89c
	void *		hready,
80b89c
	void **		hthread)
268ef4
{
268ef4
	int32_t			status;
268ef4
	nt_thread_params	tparams;
268ef4
	nt_timeout		timeout;
268ef4
	nt_timeout		zerowait;
268ef4
	intptr_t		state;
80b89c
	nt_oa			oa;
80b89c
	nt_cid			cid;
80b89c
	ntapi_internals *	__internals;
80b89c
80b89c
	oa.len		= sizeof(oa);
80b89c
	oa.root_dir	= 0;
80b89c
	oa.obj_name	= 0;
80b89c
	oa.obj_attr	= 0;
80b89c
	oa.sec_desc	= &__internals->seq_desc;
80b89c
	oa.sec_qos	= &__internals->seq_qos;
80b89c
80b89c
	cid.process_id	= pe_get_current_process_id();
80b89c
	cid.thread_id	= pe_get_current_thread_id();
80b89c
80b89c
	__internals = __ntapi_internals();
80b89c
80b89c
	status = __ntapi->zw_open_process(
80b89c
		&__internals->hprocess,
80b89c
		NT_PROCESS_ALL_ACCESS,
80b89c
		&oa,&cid;;
80b89c
80b89c
	if (status == NT_STATUS_SUCCESS)
80b89c
		status = __ntapi->zw_open_thread(
80b89c
			hthread,
80b89c
			NT_THREAD_ALL_ACCESS,
80b89c
			&oa,&cid;;
80b89c
80b89c
	if (status) {
80b89c
		__ntapi->zw_set_event(
80b89c
			hresumed,0);
80b89c
80b89c
		__ntapi->zw_terminate_process(
80b89c
			NT_CURRENT_PROCESS_HANDLE,
80b89c
			status);
80b89c
	}
268ef4
268ef4
	at_store(
268ef4
		&state,
268ef4
		(intptr_t)hready);
268ef4
268ef4
	__ntapi->tt_aligned_block_memset(
268ef4
		&tparams,0,sizeof(tparams));
268ef4
268ef4
	tparams.start			= __fork_thread;
268ef4
	tparams.arg			= &stat;;
0a9ee4
	tparams.stack_size_commit	= __FORK_THREAD_STACK_COMMIT;
0a9ee4
	tparams.stack_size_reserve	= __FORK_THREAD_STACK_RESERVE;
268ef4
268ef4
	status = __ntapi->tt_create_local_thread(
268ef4
		&tparams);
268ef4
268ef4
	__ntapi->zw_set_event(
268ef4
		hresumed,0);
268ef4
268ef4
	if (status)
268ef4
		__ntapi->zw_terminate_process(
268ef4
			NT_CURRENT_PROCESS_HANDLE,
268ef4
			status);
268ef4
268ef4
	if (!state) {
268ef4
		__ntapi->zw_close(hresumed);
268ef4
		__ntapi->zw_close(hready);
268ef4
		__ntapi->zw_close(tparams.hthread);
a823bc
		return NT_STATUS_SUCCESS;
268ef4
	}
268ef4
268ef4
	timeout.quad  = (-1) * 10 * 1000 * 250;
268ef4
268ef4
	status = __ntapi->zw_wait_for_single_object(
268ef4
		hready,
268ef4
		NT_SYNC_NON_ALERTABLE,
268ef4
		&timeout);
268ef4
268ef4
	if (status == NT_STATUS_SUCCESS) {
268ef4
		__ntapi->zw_close(hresumed);
268ef4
		__ntapi->zw_close(hready);
268ef4
		__ntapi->zw_close(tparams.hthread);
a823bc
		return NT_STATUS_SUCCESS;
268ef4
	}
268ef4
268ef4
	__ntapi->zw_terminate_thread(
268ef4
		tparams.hthread,
268ef4
		NT_STATUS_MORE_PROCESSING_REQUIRED);
268ef4
268ef4
	zerowait.quad = 0;
268ef4
268ef4
	status = __ntapi->zw_wait_for_single_object(
268ef4
		hready,
268ef4
		NT_SYNC_NON_ALERTABLE,
268ef4
		&zerowait);
268ef4
268ef4
	if (status == NT_STATUS_SUCCESS) {
268ef4
		__ntapi->zw_close(hresumed);
268ef4
		__ntapi->zw_close(hready);
268ef4
		__ntapi->zw_close(tparams.hthread);
a823bc
		return NT_STATUS_SUCCESS;
268ef4
	}
268ef4
268ef4
	return __ntapi->zw_terminate_process(
268ef4
		NT_CURRENT_PROCESS_HANDLE,
268ef4
		status);
268ef4
}
268ef4
268ef4
static intptr_t __fastcall __ntapi_tt_fork_parent(
268ef4
	void **		hprocess,
268ef4
	void **		hthread,
268ef4
	void *		hresumed,
268ef4
	void *		hready)
268ef4
{
268ef4
	int32_t		status;
268ef4
	nt_timeout	timeout;
268ef4
	nt_timeout	zerowait;
268ef4
	uint32_t	prev;
268ef4
268ef4
	__ntapi->zw_wait_for_single_object(
268ef4
		hresumed,
268ef4
		NT_SYNC_NON_ALERTABLE,
268ef4
		0);
268ef4
268ef4
	timeout.quad = (-1) * 10 * 1000 * 500;
268ef4
268ef4
	status = __ntapi->zw_wait_for_single_object(
268ef4
		hready,
268ef4
		NT_SYNC_NON_ALERTABLE,
268ef4
		&timeout);
268ef4
a823bc
	if (status == NT_STATUS_SUCCESS)
268ef4
		return NT_STATUS_SUCCESS;
268ef4
268ef4
	__ntapi->zw_suspend_thread(
268ef4
		*hthread,&prev;;
268ef4
268ef4
	zerowait.quad = 0;
268ef4
268ef4
	status = __ntapi->zw_wait_for_single_object(
268ef4
		hready,
268ef4
		NT_SYNC_NON_ALERTABLE,
268ef4
		&zerowait);
268ef4
268ef4
	if (status == NT_STATUS_SUCCESS) {
268ef4
		at_locked_inc(
268ef4
			&__fork_resume_stats);
268ef4
268ef4
		__ntapi->zw_resume_thread(
268ef4
			*hthread,0);
268ef4
268ef4
		return NT_STATUS_SUCCESS;
268ef4
	}
268ef4
268ef4
	at_locked_inc(
268ef4
		&__fork_retry_stats);
268ef4
268ef4
	__ntapi->zw_terminate_process(
268ef4
		*hprocess,
268ef4
		status);
268ef4
268ef4
	__ntapi->zw_close(*hprocess);
268ef4
	__ntapi->zw_close(*hthread);
268ef4
268ef4
	return status;
268ef4
}
268ef4
a823bc
int32_t __fastcall __ntapi_tt_fork(
268ef4
	__out	void **		hprocess,
a823bc
	__out	void **		hthread,
a823bc
	__out	nt_cid *	cid)
268ef4
{
268ef4
	int32_t			status;
268ef4
	void *			hresumed;
268ef4
	void *			hready;
268ef4
	int			i;
268ef4
268ef4
	if ((status = __ntapi->tt_create_inheritable_event(
268ef4
			&hresumed,
268ef4
			NT_NOTIFICATION_EVENT,
268ef4
			NT_EVENT_NOT_SIGNALED)))
a823bc
		return status;
268ef4
25d708
	status = __ntapi->tt_create_inheritable_event(
25d708
		&hready,
25d708
		NT_NOTIFICATION_EVENT,
25d708
		NT_EVENT_NOT_SIGNALED);
25d708
25d708
	if (status) {
25d708
		__ntapi->zw_close(hresumed);
a823bc
		return status;
25d708
	}
268ef4
268ef4
	for (i=0; i<32; i++) {
268ef4
		if (__ntapi->zw_create_user_process)
a823bc
			status = __ntapi_tt_fork_v2(hprocess,hthread,cid);
268ef4
		else
a823bc
			status = __ntapi_tt_fork_v1(hprocess,hthread,cid);
268ef4
a823bc
		if (status) {
a823bc
			__ntapi->zw_close(hresumed);
a823bc
			__ntapi->zw_close(hready);
a823bc
			return status;
a823bc
		}
a823bc
a823bc
		if (cid->process_id == 0) {
72fe5e
			__ntapi_tt_fork_child(
80b89c
				hresumed,hready,hthread);
268ef4
72fe5e
			return __ntapi_tt_fork_finalize(
80b89c
				hprocess);
a823bc
		}
72fe5e
a823bc
		status = __ntapi_tt_fork_parent(
a823bc
			hprocess,hthread,
a823bc
			hresumed,hready);
268ef4
a823bc
		if (status == NT_STATUS_SUCCESS) {
268ef4
			__ntapi->zw_close(hresumed);
268ef4
			__ntapi->zw_close(hready);
a823bc
			return NT_STATUS_SUCCESS;
268ef4
		}
268ef4
	}
268ef4
268ef4
	__ntapi->zw_close(hresumed);
268ef4
	__ntapi->zw_close(hready);
268ef4
a823bc
	return NT_STATUS_UNSUCCESSFUL;
268ef4
}