reactos/ntoskrnl/ps/process.c
Pierre Schweitzer d6dc1fd231
[NTOSKRNL] Add a raw implementation of !irpfind in kdbg
This is far from perfect, and totally doesn't match the
WinDBG way of doing it. Instead of browsing pool to find
matching 'IRP' tags, we just browse all the processes
to find the queued IRP. This requires the IRPs to be queued,
obviously, and will make us miss the leaked IRPs, for instance.

Proper way to do it would be to implement !poolfind and then
rely on its implementation to find our IRPs.

Perhaps later ;-)
2019-01-01 22:00:56 +01:00

1733 lines
48 KiB
C

/*
* PROJECT: ReactOS Kernel
* LICENSE: GPL - See COPYING in the top level directory
* FILE: ntoskrnl/ps/process.c
* PURPOSE: Process Manager: Process Management
* PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org)
* Thomas Weidenmueller (w3seek@reactos.org
*/
/* INCLUDES ******************************************************************/
#include <ntoskrnl.h>
#define NDEBUG
#include <debug.h>
/* GLOBALS *******************************************************************/
extern ULONG PsMinimumWorkingSet, PsMaximumWorkingSet;
POBJECT_TYPE PsProcessType = NULL;
LIST_ENTRY PsActiveProcessHead;
KGUARDED_MUTEX PspActiveProcessMutex;
LARGE_INTEGER ShortPsLockDelay;
ULONG PsRawPrioritySeparation;
ULONG PsPrioritySeparation;
CHAR PspForegroundQuantum[3];
/* Fixed quantum table */
CHAR PspFixedQuantums[6] =
{
/* Short quantums */
3 * 6, /* Level 1 */
3 * 6, /* Level 2 */
3 * 6, /* Level 3 */
/* Long quantums */
6 * 6, /* Level 1 */
6 * 6, /* Level 2 */
6 * 6 /* Level 3 */
};
/* Variable quantum table */
CHAR PspVariableQuantums[6] =
{
/* Short quantums */
1 * 6, /* Level 1 */
2 * 6, /* Level 2 */
3 * 6, /* Level 3 */
/* Long quantums */
2 * 6, /* Level 1 */
4 * 6, /* Level 2 */
6 * 6 /* Level 3 */
};
/* Priority table */
KPRIORITY PspPriorityTable[PROCESS_PRIORITY_CLASS_ABOVE_NORMAL + 1] =
{
8,
4,
8,
13,
24,
6,
10
};
/* PRIVATE FUNCTIONS *********************************************************/
PETHREAD
NTAPI
PsGetNextProcessThread(IN PEPROCESS Process,
IN PETHREAD Thread OPTIONAL)
{
PETHREAD FoundThread = NULL;
PLIST_ENTRY ListHead, Entry;
PAGED_CODE();
PSTRACE(PS_PROCESS_DEBUG,
"Process: %p Thread: %p\n", Process, Thread);
/* Lock the process */
KeEnterCriticalRegion();
ExAcquirePushLockShared(&Process->ProcessLock);
/* Check if we're already starting somewhere */
if (Thread)
{
/* Start where we left off */
Entry = Thread->ThreadListEntry.Flink;
}
else
{
/* Start at the beginning */
Entry = Process->ThreadListHead.Flink;
}
/* Set the list head and start looping */
ListHead = &Process->ThreadListHead;
while (ListHead != Entry)
{
/* Get the Thread */
FoundThread = CONTAINING_RECORD(Entry, ETHREAD, ThreadListEntry);
/* Safe reference the thread */
if (ObReferenceObjectSafe(FoundThread)) break;
/* Nothing found, keep looping */
FoundThread = NULL;
Entry = Entry->Flink;
}
/* Unlock the process */
ExReleasePushLockShared(&Process->ProcessLock);
KeLeaveCriticalRegion();
/* Check if we had a starting thread, and dereference it */
if (Thread) ObDereferenceObject(Thread);
/* Return what we found */
return FoundThread;
}
PEPROCESS
NTAPI
PsGetNextProcess(IN PEPROCESS OldProcess)
{
PLIST_ENTRY Entry;
PEPROCESS FoundProcess = NULL;
PAGED_CODE();
PSTRACE(PS_PROCESS_DEBUG, "Process: %p\n", OldProcess);
/* Acquire the Active Process Lock */
KeAcquireGuardedMutex(&PspActiveProcessMutex);
/* Check if we're already starting somewhere */
if (OldProcess)
{
/* Start where we left off */
Entry = OldProcess->ActiveProcessLinks.Flink;
}
else
{
/* Start at the beginning */
Entry = PsActiveProcessHead.Flink;
}
/* Loop the process list */
while (Entry != &PsActiveProcessHead)
{
/* Get the process */
FoundProcess = CONTAINING_RECORD(Entry, EPROCESS, ActiveProcessLinks);
/* Reference the process */
if (ObReferenceObjectSafe(FoundProcess)) break;
/* Nothing found, keep trying */
FoundProcess = NULL;
Entry = Entry->Flink;
}
/* Release the lock */
KeReleaseGuardedMutex(&PspActiveProcessMutex);
/* Dereference the Process we had referenced earlier */
if (OldProcess) ObDereferenceObject(OldProcess);
return FoundProcess;
}
KPRIORITY
NTAPI
PspComputeQuantumAndPriority(IN PEPROCESS Process,
IN PSPROCESSPRIORITYMODE Mode,
OUT PUCHAR Quantum)
{
ULONG i;
UCHAR LocalQuantum, MemoryPriority;
PAGED_CODE();
PSTRACE(PS_PROCESS_DEBUG, "Process: %p Mode: %lx\n", Process, Mode);
/* Check if this is a foreground process */
if (Mode == PsProcessPriorityForeground)
{
/* Set the memory priority and use priority separation */
MemoryPriority = MEMORY_PRIORITY_FOREGROUND;
i = PsPrioritySeparation;
}
else
{
/* Set the background memory priority and no separation */
MemoryPriority = MEMORY_PRIORITY_BACKGROUND;
i = 0;
}
/* Make sure that the process mode isn't spinning */
if (Mode != PsProcessPrioritySpinning)
{
/* Set the priority */
MmSetMemoryPriorityProcess(Process, MemoryPriority);
}
/* Make sure that the process isn't idle */
if (Process->PriorityClass != PROCESS_PRIORITY_CLASS_IDLE)
{
/* Does the process have a job? */
if ((Process->Job) && (PspUseJobSchedulingClasses))
{
/* Use job quantum */
LocalQuantum = PspJobSchedulingClasses[Process->Job->
SchedulingClass];
}
else
{
/* Use calculated quantum */
LocalQuantum = PspForegroundQuantum[i];
}
}
else
{
/* Process is idle, use default quantum */
LocalQuantum = 6;
}
/* Return quantum to caller */
*Quantum = LocalQuantum;
/* Return priority */
return PspPriorityTable[Process->PriorityClass];
}
VOID
NTAPI
PsChangeQuantumTable(IN BOOLEAN Immediate,
IN ULONG PrioritySeparation)
{
PEPROCESS Process = NULL;
ULONG i;
UCHAR Quantum;
PCHAR QuantumTable;
PAGED_CODE();
PSTRACE(PS_PROCESS_DEBUG,
"%lx PrioritySeparation: %lx\n", Immediate, PrioritySeparation);
/* Write the current priority separation */
PsPrioritySeparation = PspPrioritySeparationFromMask(PrioritySeparation);
/* Normalize it if it was too high */
if (PsPrioritySeparation == 3) PsPrioritySeparation = 2;
/* Get the quantum table to use */
if (PspQuantumTypeFromMask(PrioritySeparation) == PSP_VARIABLE_QUANTUMS)
{
/* Use a variable table */
QuantumTable = PspVariableQuantums;
}
else if (PspQuantumTypeFromMask(PrioritySeparation) == PSP_FIXED_QUANTUMS)
{
/* Use fixed table */
QuantumTable = PspFixedQuantums;
}
else
{
/* Use default for the type of system we're on */
QuantumTable = MmIsThisAnNtAsSystem() ? PspFixedQuantums : PspVariableQuantums;
}
/* Now check if we should use long or short */
if (PspQuantumLengthFromMask(PrioritySeparation) == PSP_LONG_QUANTUMS)
{
/* Use long quantums */
QuantumTable += 3;
}
else if (PspQuantumLengthFromMask(PrioritySeparation) == PSP_SHORT_QUANTUMS)
{
/* Keep existing table */
NOTHING;
}
else
{
/* Use default for the type of system we're on */
QuantumTable += MmIsThisAnNtAsSystem() ? 3 : 0;
}
/* Check if we're using long fixed quantums */
if (QuantumTable == &PspFixedQuantums[3])
{
/* Use Job scheduling classes */
PspUseJobSchedulingClasses = TRUE;
}
else
{
/* Otherwise, we don't */
PspUseJobSchedulingClasses = FALSE;
}
/* Copy the selected table into the Foreground Quantum table */
RtlCopyMemory(PspForegroundQuantum,
QuantumTable,
sizeof(PspForegroundQuantum));
/* Check if we should apply these changes real-time */
if (Immediate)
{
/* We are...loop every process */
Process = PsGetNextProcess(Process);
while (Process)
{
/* Use the priority separation if this is a foreground process */
i = (Process->Vm.Flags.MemoryPriority ==
MEMORY_PRIORITY_BACKGROUND) ?
0: PsPrioritySeparation;
/* Make sure that the process isn't idle */
if (Process->PriorityClass != PROCESS_PRIORITY_CLASS_IDLE)
{
/* Does the process have a job? */
if ((Process->Job) && (PspUseJobSchedulingClasses))
{
/* Use job quantum */
Quantum = PspJobSchedulingClasses[Process->Job->SchedulingClass];
}
else
{
/* Use calculated quantum */
Quantum = PspForegroundQuantum[i];
}
}
else
{
/* Process is idle, use default quantum */
Quantum = 6;
}
/* Now set the quantum */
KeSetQuantumProcess(&Process->Pcb, Quantum);
/* Get the next process */
Process = PsGetNextProcess(Process);
}
}
}
NTSTATUS
NTAPI
PspCreateProcess(OUT PHANDLE ProcessHandle,
IN ACCESS_MASK DesiredAccess,
IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL,
IN HANDLE ParentProcess OPTIONAL,
IN ULONG Flags,
IN HANDLE SectionHandle OPTIONAL,
IN HANDLE DebugPort OPTIONAL,
IN HANDLE ExceptionPort OPTIONAL,
IN BOOLEAN InJob)
{
HANDLE hProcess;
PEPROCESS Process, Parent;
PVOID ExceptionPortObject;
PDEBUG_OBJECT DebugObject;
PSECTION_OBJECT SectionObject;
NTSTATUS Status, AccessStatus;
ULONG_PTR DirectoryTableBase[2] = {0,0};
KAFFINITY Affinity;
HANDLE_TABLE_ENTRY CidEntry;
PETHREAD CurrentThread = PsGetCurrentThread();
KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
PEPROCESS CurrentProcess = PsGetCurrentProcess();
ULONG MinWs, MaxWs;
ACCESS_STATE LocalAccessState;
PACCESS_STATE AccessState = &LocalAccessState;
AUX_ACCESS_DATA AuxData;
UCHAR Quantum;
BOOLEAN Result, SdAllocated;
PSECURITY_DESCRIPTOR SecurityDescriptor;
SECURITY_SUBJECT_CONTEXT SubjectContext;
BOOLEAN NeedsPeb = FALSE;
INITIAL_PEB InitialPeb;
PAGED_CODE();
PSTRACE(PS_PROCESS_DEBUG,
"ProcessHandle: %p Parent: %p\n", ProcessHandle, ParentProcess);
/* Validate flags */
if (Flags & ~PROCESS_CREATE_FLAGS_LEGAL_MASK) return STATUS_INVALID_PARAMETER;
/* Check for parent */
if (ParentProcess)
{
/* Reference it */
Status = ObReferenceObjectByHandle(ParentProcess,
PROCESS_CREATE_PROCESS,
PsProcessType,
PreviousMode,
(PVOID*)&Parent,
NULL);
if (!NT_SUCCESS(Status)) return Status;
/* If this process should be in a job but the parent isn't */
if ((InJob) && (!Parent->Job))
{
/* This is illegal. Dereference the parent and fail */
ObDereferenceObject(Parent);
return STATUS_INVALID_PARAMETER;
}
/* Inherit Parent process's Affinity. */
Affinity = Parent->Pcb.Affinity;
}
else
{
/* We have no parent */
Parent = NULL;
Affinity = KeActiveProcessors;
}
/* Save working set data */
MinWs = PsMinimumWorkingSet;
MaxWs = PsMaximumWorkingSet;
/* Create the Object */
Status = ObCreateObject(PreviousMode,
PsProcessType,
ObjectAttributes,
PreviousMode,
NULL,
sizeof(EPROCESS),
0,
0,
(PVOID*)&Process);
if (!NT_SUCCESS(Status)) goto Cleanup;
/* Clean up the Object */
RtlZeroMemory(Process, sizeof(EPROCESS));
/* Initialize pushlock and rundown protection */
ExInitializeRundownProtection(&Process->RundownProtect);
Process->ProcessLock.Value = 0;
/* Setup the Thread List Head */
InitializeListHead(&Process->ThreadListHead);
/* Set up the Quota Block from the Parent */
PspInheritQuota(Process, Parent);
/* Set up Dos Device Map from the Parent */
ObInheritDeviceMap(Parent, Process);
/* Check if we have a parent */
if (Parent)
{
/* Inherit PID and hard-error processing */
Process->InheritedFromUniqueProcessId = Parent->UniqueProcessId;
Process->DefaultHardErrorProcessing = Parent->DefaultHardErrorProcessing;
}
else
{
/* Use default hard-error processing */
Process->DefaultHardErrorProcessing = SEM_FAILCRITICALERRORS;
}
/* Check for a section handle */
if (SectionHandle)
{
/* Get a pointer to it */
Status = ObReferenceObjectByHandle(SectionHandle,
SECTION_MAP_EXECUTE,
MmSectionObjectType,
PreviousMode,
(PVOID*)&SectionObject,
NULL);
if (!NT_SUCCESS(Status)) goto CleanupWithRef;
}
else
{
/* Assume no section object */
SectionObject = NULL;
/* Is the parent the initial process?
* Check for NULL also, as at initialization PsInitialSystemProcess is NULL */
if (Parent != PsInitialSystemProcess && (Parent != NULL))
{
/* It's not, so acquire the process rundown */
if (ExAcquireRundownProtection(&Parent->RundownProtect))
{
/* If the parent has a section, use it */
SectionObject = Parent->SectionObject;
if (SectionObject) ObReferenceObject(SectionObject);
/* Release process rundown */
ExReleaseRundownProtection(&Parent->RundownProtect);
}
/* If we don't have a section object */
if (!SectionObject)
{
/* Then the process is in termination, so fail */
Status = STATUS_PROCESS_IS_TERMINATING;
goto CleanupWithRef;
}
}
}
/* Save the pointer to the section object */
Process->SectionObject = SectionObject;
/* Check for the debug port */
if (DebugPort)
{
/* Reference it */
Status = ObReferenceObjectByHandle(DebugPort,
DEBUG_OBJECT_ADD_REMOVE_PROCESS,
DbgkDebugObjectType,
PreviousMode,
(PVOID*)&DebugObject,
NULL);
if (!NT_SUCCESS(Status)) goto CleanupWithRef;
/* Save the debug object */
Process->DebugPort = DebugObject;
/* Check if the caller doesn't want the debug stuff inherited */
if (Flags & PROCESS_CREATE_FLAGS_NO_DEBUG_INHERIT)
{
/* Set the process flag */
InterlockedOr((PLONG)&Process->Flags, PSF_NO_DEBUG_INHERIT_BIT);
}
}
else
{
/* Do we have a parent? Copy his debug port */
if (Parent) DbgkCopyProcessDebugPort(Process, Parent);
}
/* Now check for an exception port */
if (ExceptionPort)
{
/* Reference it */
Status = ObReferenceObjectByHandle(ExceptionPort,
PORT_ALL_ACCESS,
LpcPortObjectType,
PreviousMode,
(PVOID*)&ExceptionPortObject,
NULL);
if (!NT_SUCCESS(Status)) goto CleanupWithRef;
/* Save the exception port */
Process->ExceptionPort = ExceptionPortObject;
}
/* Save the pointer to the section object */
Process->SectionObject = SectionObject;
/* Set default exit code */
Process->ExitStatus = STATUS_PENDING;
/* Check if this is the initial process being built */
if (Parent)
{
/* Create the address space for the child */
if (!MmCreateProcessAddressSpace(MinWs,
Process,
DirectoryTableBase))
{
/* Failed */
Status = STATUS_INSUFFICIENT_RESOURCES;
goto CleanupWithRef;
}
}
else
{
/* Otherwise, we are the boot process, we're already semi-initialized */
Process->ObjectTable = CurrentProcess->ObjectTable;
Status = MmInitializeHandBuiltProcess(Process, DirectoryTableBase);
if (!NT_SUCCESS(Status)) goto CleanupWithRef;
}
/* We now have an address space */
InterlockedOr((PLONG)&Process->Flags, PSF_HAS_ADDRESS_SPACE_BIT);
/* Set the maximum WS */
Process->Vm.MaximumWorkingSetSize = MaxWs;
/* Now initialize the Kernel Process */
KeInitializeProcess(&Process->Pcb,
PROCESS_PRIORITY_NORMAL,
Affinity,
DirectoryTableBase,
BooleanFlagOn(Process->DefaultHardErrorProcessing,
SEM_NOALIGNMENTFAULTEXCEPT));
/* Duplicate Parent Token */
Status = PspInitializeProcessSecurity(Process, Parent);
if (!NT_SUCCESS(Status)) goto CleanupWithRef;
/* Set default priority class */
Process->PriorityClass = PROCESS_PRIORITY_CLASS_NORMAL;
/* Check if we have a parent */
if (Parent)
{
/* Check our priority class */
if (Parent->PriorityClass == PROCESS_PRIORITY_CLASS_IDLE ||
Parent->PriorityClass == PROCESS_PRIORITY_CLASS_BELOW_NORMAL)
{
/* Normalize it */
Process->PriorityClass = Parent->PriorityClass;
}
/* Initialize object manager for the process */
Status = ObInitProcess(Flags & PROCESS_CREATE_FLAGS_INHERIT_HANDLES ?
Parent : NULL,
Process);
if (!NT_SUCCESS(Status)) goto CleanupWithRef;
}
else
{
/* Do the second part of the boot process memory setup */
Status = MmInitializeHandBuiltProcess2(Process);
if (!NT_SUCCESS(Status)) goto CleanupWithRef;
}
/* Set success for now */
Status = STATUS_SUCCESS;
/* Check if this is a real user-mode process */
if (SectionHandle)
{
/* Initialize the address space */
Status = MmInitializeProcessAddressSpace(Process,
NULL,
SectionObject,
&Flags,
&Process->
SeAuditProcessCreationInfo.
ImageFileName);
if (!NT_SUCCESS(Status)) goto CleanupWithRef;
//
// We need a PEB
//
NeedsPeb = TRUE;
}
else if (Parent)
{
/* Check if this is a child of the system process */
if (Parent != PsInitialSystemProcess)
{
//
// We need a PEB
//
NeedsPeb = TRUE;
/* This is a clone! */
ASSERTMSG("No support for cloning yet\n", FALSE);
}
else
{
/* This is the initial system process */
Flags &= ~PROCESS_CREATE_FLAGS_LARGE_PAGES;
Status = MmInitializeProcessAddressSpace(Process,
NULL,
NULL,
&Flags,
NULL);
if (!NT_SUCCESS(Status)) goto CleanupWithRef;
/* Create a dummy image file name */
Process->SeAuditProcessCreationInfo.ImageFileName =
ExAllocatePoolWithTag(PagedPool,
sizeof(OBJECT_NAME_INFORMATION),
TAG_SEPA);
if (!Process->SeAuditProcessCreationInfo.ImageFileName)
{
/* Fail */
Status = STATUS_INSUFFICIENT_RESOURCES;
goto CleanupWithRef;
}
/* Zero it out */
RtlZeroMemory(Process->SeAuditProcessCreationInfo.ImageFileName,
sizeof(OBJECT_NAME_INFORMATION));
}
}
#if MI_TRACE_PFNS
/* Copy the process name now that we have it */
memcpy(MiGetPfnEntry(Process->Pcb.DirectoryTableBase[0] >> PAGE_SHIFT)->ProcessName, Process->ImageFileName, 16);
if (Process->Pcb.DirectoryTableBase[1]) memcpy(MiGetPfnEntry(Process->Pcb.DirectoryTableBase[1] >> PAGE_SHIFT)->ProcessName, Process->ImageFileName, 16);
if (Process->WorkingSetPage) memcpy(MiGetPfnEntry(Process->WorkingSetPage)->ProcessName, Process->ImageFileName, 16);
#endif
/* Check if we have a section object and map the system DLL */
if (SectionObject) PspMapSystemDll(Process, NULL, FALSE);
/* Create a handle for the Process */
CidEntry.Object = Process;
CidEntry.GrantedAccess = 0;
Process->UniqueProcessId = ExCreateHandle(PspCidTable, &CidEntry);
if (!Process->UniqueProcessId)
{
/* Fail */
Status = STATUS_INSUFFICIENT_RESOURCES;
goto CleanupWithRef;
}
/* Set the handle table PID */
Process->ObjectTable->UniqueProcessId = Process->UniqueProcessId;
/* Check if we need to audit */
if (SeDetailedAuditingWithToken(NULL)) SeAuditProcessCreate(Process);
/* Check if the parent had a job */
if ((Parent) && (Parent->Job))
{
/* FIXME: We need to insert this process */
DPRINT1("Jobs not yet supported\n");
}
/* Create PEB only for User-Mode Processes */
if ((Parent) && (NeedsPeb))
{
//
// Set up the initial PEB
//
RtlZeroMemory(&InitialPeb, sizeof(INITIAL_PEB));
InitialPeb.Mutant = (HANDLE)-1;
InitialPeb.ImageUsesLargePages = 0; // FIXME: Not yet supported
//
// Create it only if we have an image section
//
if (SectionHandle)
{
//
// Create it
//
Status = MmCreatePeb(Process, &InitialPeb, &Process->Peb);
if (!NT_SUCCESS(Status)) goto CleanupWithRef;
}
else
{
//
// We have to clone it
//
ASSERTMSG("No support for cloning yet\n", FALSE);
}
}
/* The process can now be activated */
KeAcquireGuardedMutex(&PspActiveProcessMutex);
InsertTailList(&PsActiveProcessHead, &Process->ActiveProcessLinks);
KeReleaseGuardedMutex(&PspActiveProcessMutex);
/* Create an access state */
Status = SeCreateAccessStateEx(CurrentThread,
((Parent) &&
(Parent == PsInitialSystemProcess)) ?
Parent : CurrentProcess,
&LocalAccessState,
&AuxData,
DesiredAccess,
&PsProcessType->TypeInfo.GenericMapping);
if (!NT_SUCCESS(Status)) goto CleanupWithRef;
/* Insert the Process into the Object Directory */
Status = ObInsertObject(Process,
AccessState,
DesiredAccess,
1,
NULL,
&hProcess);
/* Free the access state */
if (AccessState) SeDeleteAccessState(AccessState);
/* Cleanup on failure */
if (!NT_SUCCESS(Status)) goto Cleanup;
/* Compute Quantum and Priority */
ASSERT(IsListEmpty(&Process->ThreadListHead) == TRUE);
Process->Pcb.BasePriority =
(SCHAR)PspComputeQuantumAndPriority(Process,
PsProcessPriorityBackground,
&Quantum);
Process->Pcb.QuantumReset = Quantum;
/* Check if we have a parent other then the initial system process */
Process->GrantedAccess = PROCESS_TERMINATE;
if ((Parent) && (Parent != PsInitialSystemProcess))
{
/* Get the process's SD */
Status = ObGetObjectSecurity(Process,
&SecurityDescriptor,
&SdAllocated);
if (!NT_SUCCESS(Status))
{
/* We failed, close the handle and clean up */
ObCloseHandle(hProcess, PreviousMode);
goto CleanupWithRef;
}
/* Create the subject context */
SubjectContext.ProcessAuditId = Process;
SubjectContext.PrimaryToken = PsReferencePrimaryToken(Process);
SubjectContext.ClientToken = NULL;
/* Do the access check */
Result = SeAccessCheck(SecurityDescriptor,
&SubjectContext,
FALSE,
MAXIMUM_ALLOWED,
0,
NULL,
&PsProcessType->TypeInfo.GenericMapping,
PreviousMode,
&Process->GrantedAccess,
&AccessStatus);
/* Dereference the token and let go the SD */
ObFastDereferenceObject(&Process->Token,
SubjectContext.PrimaryToken);
ObReleaseObjectSecurity(SecurityDescriptor, SdAllocated);
/* Remove access if it failed */
if (!Result) Process->GrantedAccess = 0;
/* Give the process some basic access */
Process->GrantedAccess |= (PROCESS_VM_OPERATION |
PROCESS_VM_READ |
PROCESS_VM_WRITE |
PROCESS_QUERY_INFORMATION |
PROCESS_TERMINATE |
PROCESS_CREATE_THREAD |
PROCESS_DUP_HANDLE |
PROCESS_CREATE_PROCESS |
PROCESS_SET_INFORMATION |
STANDARD_RIGHTS_ALL |
PROCESS_SET_QUOTA);
}
else
{
/* Set full granted access */
Process->GrantedAccess = PROCESS_ALL_ACCESS;
}
/* Set the Creation Time */
KeQuerySystemTime(&Process->CreateTime);
/* Protect against bad user-mode pointer */
_SEH2_TRY
{
/* Hacky way of returning the PEB to the user-mode creator */
if ((Process->Peb) && (CurrentThread->Tcb.Teb))
{
CurrentThread->Tcb.Teb->NtTib.ArbitraryUserPointer = Process->Peb;
}
/* Save the process handle */
*ProcessHandle = hProcess;
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
/* Get the exception code */
Status = _SEH2_GetExceptionCode();
}
_SEH2_END;
/* Run the Notification Routines */
PspRunCreateProcessNotifyRoutines(Process, TRUE);
/* If 12 processes have been created, enough of user-mode is ready */
if (++ProcessCount == 12) Ki386PerfEnd();
CleanupWithRef:
/*
* Dereference the process. For failures, kills the process and does
* cleanup present in PspDeleteProcess. For success, kills the extra
* reference added by ObInsertObject.
*/
ObDereferenceObject(Process);
Cleanup:
/* Dereference the parent */
if (Parent) ObDereferenceObject(Parent);
/* Return status to caller */
return Status;
}
/* PUBLIC FUNCTIONS **********************************************************/
/*
* @implemented
*/
NTSTATUS
NTAPI
PsCreateSystemProcess(OUT PHANDLE ProcessHandle,
IN ACCESS_MASK DesiredAccess,
IN POBJECT_ATTRIBUTES ObjectAttributes)
{
/* Call the internal API */
return PspCreateProcess(ProcessHandle,
DesiredAccess,
ObjectAttributes,
NULL,
0,
NULL,
NULL,
NULL,
FALSE);
}
/*
* @implemented
*/
NTSTATUS
NTAPI
PsLookupProcessByProcessId(IN HANDLE ProcessId,
OUT PEPROCESS *Process)
{
PHANDLE_TABLE_ENTRY CidEntry;
PEPROCESS FoundProcess;
NTSTATUS Status = STATUS_INVALID_PARAMETER;
PAGED_CODE();
PSTRACE(PS_PROCESS_DEBUG, "ProcessId: %p\n", ProcessId);
KeEnterCriticalRegion();
/* Get the CID Handle Entry */
CidEntry = ExMapHandleToPointer(PspCidTable, ProcessId);
if (CidEntry)
{
/* Get the Process */
FoundProcess = CidEntry->Object;
/* Make sure it's really a process */
if (FoundProcess->Pcb.Header.Type == ProcessObject)
{
/* Safe Reference and return it */
if (ObReferenceObjectSafe(FoundProcess))
{
*Process = FoundProcess;
Status = STATUS_SUCCESS;
}
}
/* Unlock the Entry */
ExUnlockHandleTableEntry(PspCidTable, CidEntry);
}
/* Return to caller */
KeLeaveCriticalRegion();
return Status;
}
/*
* @implemented
*/
NTSTATUS
NTAPI
PsLookupProcessThreadByCid(IN PCLIENT_ID Cid,
OUT PEPROCESS *Process OPTIONAL,
OUT PETHREAD *Thread)
{
PHANDLE_TABLE_ENTRY CidEntry;
PETHREAD FoundThread;
NTSTATUS Status = STATUS_INVALID_CID;
PAGED_CODE();
PSTRACE(PS_PROCESS_DEBUG, "Cid: %p\n", Cid);
KeEnterCriticalRegion();
/* Get the CID Handle Entry */
CidEntry = ExMapHandleToPointer(PspCidTable, Cid->UniqueThread);
if (CidEntry)
{
/* Get the Process */
FoundThread = CidEntry->Object;
/* Make sure it's really a thread and this process' */
if ((FoundThread->Tcb.Header.Type == ThreadObject) &&
(FoundThread->Cid.UniqueProcess == Cid->UniqueProcess))
{
/* Safe Reference and return it */
if (ObReferenceObjectSafe(FoundThread))
{
*Thread = FoundThread;
Status = STATUS_SUCCESS;
/* Check if we should return the Process too */
if (Process)
{
/* Return it and reference it */
*Process = FoundThread->ThreadsProcess;
ObReferenceObject(*Process);
}
}
}
/* Unlock the Entry */
ExUnlockHandleTableEntry(PspCidTable, CidEntry);
}
/* Return to caller */
KeLeaveCriticalRegion();
return Status;
}
/*
* @implemented
*/
LARGE_INTEGER
NTAPI
PsGetProcessExitTime(VOID)
{
return PsGetCurrentProcess()->ExitTime;
}
/*
* @implemented
*/
LONGLONG
NTAPI
PsGetProcessCreateTimeQuadPart(PEPROCESS Process)
{
return Process->CreateTime.QuadPart;
}
/*
* @implemented
*/
PVOID
NTAPI
PsGetProcessDebugPort(PEPROCESS Process)
{
return Process->DebugPort;
}
/*
* @implemented
*/
BOOLEAN
NTAPI
PsGetProcessExitProcessCalled(PEPROCESS Process)
{
return (BOOLEAN)Process->ProcessExiting;
}
/*
* @implemented
*/
NTSTATUS
NTAPI
PsGetProcessExitStatus(PEPROCESS Process)
{
return Process->ExitStatus;
}
/*
* @implemented
*/
HANDLE
NTAPI
PsGetProcessId(PEPROCESS Process)
{
return (HANDLE)Process->UniqueProcessId;
}
/*
* @implemented
*/
LPSTR
NTAPI
PsGetProcessImageFileName(PEPROCESS Process)
{
return (LPSTR)Process->ImageFileName;
}
/*
* @implemented
*/
HANDLE
NTAPI
PsGetProcessInheritedFromUniqueProcessId(PEPROCESS Process)
{
return Process->InheritedFromUniqueProcessId;
}
/*
* @implemented
*/
PEJOB
NTAPI
PsGetProcessJob(PEPROCESS Process)
{
return Process->Job;
}
/*
* @implemented
*/
PPEB
NTAPI
PsGetProcessPeb(PEPROCESS Process)
{
return Process->Peb;
}
/*
* @implemented
*/
ULONG
NTAPI
PsGetProcessPriorityClass(PEPROCESS Process)
{
return Process->PriorityClass;
}
/*
* @implemented
*/
HANDLE
NTAPI
PsGetCurrentProcessId(VOID)
{
return (HANDLE)PsGetCurrentProcess()->UniqueProcessId;
}
/*
* @implemented
*/
ULONG
NTAPI
PsGetCurrentProcessSessionId(VOID)
{
return MmGetSessionId(PsGetCurrentProcess());
}
/*
* @implemented
*/
PVOID
NTAPI
PsGetProcessSectionBaseAddress(PEPROCESS Process)
{
return Process->SectionBaseAddress;
}
/*
* @implemented
*/
PVOID
NTAPI
PsGetProcessSecurityPort(PEPROCESS Process)
{
return Process->SecurityPort;
}
/*
* @implemented
*/
ULONG
NTAPI
PsGetProcessSessionId(IN PEPROCESS Process)
{
return MmGetSessionId(Process);
}
/*
* @implemented
*/
ULONG
NTAPI
PsGetProcessSessionIdEx(IN PEPROCESS Process)
{
return MmGetSessionIdEx(Process);
}
/*
* @implemented
*/
PVOID
NTAPI
PsGetCurrentProcessWin32Process(VOID)
{
return PsGetCurrentProcess()->Win32Process;
}
/*
* @implemented
*/
PVOID
NTAPI
PsGetProcessWin32Process(PEPROCESS Process)
{
return Process->Win32Process;
}
/*
* @implemented
*/
PVOID
NTAPI
PsGetProcessWin32WindowStation(PEPROCESS Process)
{
return Process->Win32WindowStation;
}
/*
* @implemented
*/
BOOLEAN
NTAPI
PsIsProcessBeingDebugged(PEPROCESS Process)
{
return Process->DebugPort != NULL;
}
/*
* @implemented
*/
BOOLEAN
NTAPI
PsIsSystemProcess(IN PEPROCESS Process)
{
/* Return if this is the System Process */
return Process == PsInitialSystemProcess;
}
/*
* @implemented
*/
VOID
NTAPI
PsSetProcessPriorityClass(PEPROCESS Process,
ULONG PriorityClass)
{
Process->PriorityClass = (UCHAR)PriorityClass;
}
/*
* @implemented
*/
NTSTATUS
NTAPI
PsSetProcessSecurityPort(PEPROCESS Process,
PVOID SecurityPort)
{
Process->SecurityPort = SecurityPort;
return STATUS_SUCCESS;
}
/*
* @implemented
*/
NTSTATUS
NTAPI
PsSetProcessWin32Process(
_Inout_ PEPROCESS Process,
_In_opt_ PVOID Win32Process,
_In_opt_ PVOID OldWin32Process)
{
NTSTATUS Status;
/* Assume success */
Status = STATUS_SUCCESS;
/* Lock the process */
KeEnterCriticalRegion();
ExAcquirePushLockExclusive(&Process->ProcessLock);
/* Check if we set a new win32 process */
if (Win32Process != NULL)
{
/* Check if the process is in the right state */
if (((Process->Flags & PSF_PROCESS_DELETE_BIT) == 0) &&
(Process->Win32Process == NULL))
{
/* Set the new win32 process */
Process->Win32Process = Win32Process;
}
else
{
/* Otherwise fail */
Status = STATUS_PROCESS_IS_TERMINATING;
}
}
else
{
/* Reset the win32 process, did the caller specify the correct old value? */
if (Process->Win32Process == OldWin32Process)
{
/* Yes, so reset the win32 process to NULL */
Process->Win32Process = NULL;
}
else
{
/* Otherwise fail */
Status = STATUS_UNSUCCESSFUL;
}
}
/* Unlock the process */
ExReleasePushLockExclusive(&Process->ProcessLock);
KeLeaveCriticalRegion();
return Status;
}
/*
* @implemented
*/
VOID
NTAPI
PsSetProcessWindowStation(PEPROCESS Process,
PVOID WindowStation)
{
Process->Win32WindowStation = WindowStation;
}
/*
* @implemented
*/
VOID
NTAPI
PsSetProcessPriorityByClass(IN PEPROCESS Process,
IN PSPROCESSPRIORITYMODE Type)
{
UCHAR Quantum;
ULONG Priority;
PSTRACE(PS_PROCESS_DEBUG, "Process: %p Type: %lx\n", Process, Type);
/* Compute quantum and priority */
Priority = PspComputeQuantumAndPriority(Process, Type, &Quantum);
/* Set them */
KeSetPriorityAndQuantumProcess(&Process->Pcb, Priority, Quantum);
}
/*
* @implemented
*/
NTSTATUS
NTAPI
NtCreateProcessEx(OUT PHANDLE ProcessHandle,
IN ACCESS_MASK DesiredAccess,
IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL,
IN HANDLE ParentProcess,
IN ULONG Flags,
IN HANDLE SectionHandle OPTIONAL,
IN HANDLE DebugPort OPTIONAL,
IN HANDLE ExceptionPort OPTIONAL,
IN BOOLEAN InJob)
{
KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
NTSTATUS Status;
PAGED_CODE();
PSTRACE(PS_PROCESS_DEBUG,
"ParentProcess: %p Flags: %lx\n", ParentProcess, Flags);
/* Check if we came from user mode */
if (PreviousMode != KernelMode)
{
_SEH2_TRY
{
/* Probe process handle */
ProbeForWriteHandle(ProcessHandle);
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
/* Return the exception code */
_SEH2_YIELD(return _SEH2_GetExceptionCode());
}
_SEH2_END;
}
/* Make sure there's a parent process */
if (!ParentProcess)
{
/* Can't create System Processes like this */
Status = STATUS_INVALID_PARAMETER;
}
else
{
/* Create a user Process */
Status = PspCreateProcess(ProcessHandle,
DesiredAccess,
ObjectAttributes,
ParentProcess,
Flags,
SectionHandle,
DebugPort,
ExceptionPort,
InJob);
}
/* Return Status */
return Status;
}
/*
* @implemented
*/
NTSTATUS
NTAPI
NtCreateProcess(OUT PHANDLE ProcessHandle,
IN ACCESS_MASK DesiredAccess,
IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL,
IN HANDLE ParentProcess,
IN BOOLEAN InheritObjectTable,
IN HANDLE SectionHandle OPTIONAL,
IN HANDLE DebugPort OPTIONAL,
IN HANDLE ExceptionPort OPTIONAL)
{
ULONG Flags = 0;
PSTRACE(PS_PROCESS_DEBUG,
"Parent: %p Attributes: %p\n", ParentProcess, ObjectAttributes);
/* Set new-style flags */
if ((ULONG_PTR)SectionHandle & 1) Flags |= PROCESS_CREATE_FLAGS_BREAKAWAY;
if ((ULONG_PTR)DebugPort & 1) Flags |= PROCESS_CREATE_FLAGS_NO_DEBUG_INHERIT;
if (InheritObjectTable) Flags |= PROCESS_CREATE_FLAGS_INHERIT_HANDLES;
/* Call the new API */
return NtCreateProcessEx(ProcessHandle,
DesiredAccess,
ObjectAttributes,
ParentProcess,
Flags,
SectionHandle,
DebugPort,
ExceptionPort,
FALSE);
}
/*
* @implemented
*/
NTSTATUS
NTAPI
NtOpenProcess(OUT PHANDLE ProcessHandle,
IN ACCESS_MASK DesiredAccess,
IN POBJECT_ATTRIBUTES ObjectAttributes,
IN PCLIENT_ID ClientId)
{
KPROCESSOR_MODE PreviousMode = KeGetPreviousMode();
CLIENT_ID SafeClientId;
ULONG Attributes = 0;
HANDLE hProcess;
BOOLEAN HasObjectName = FALSE;
PETHREAD Thread = NULL;
PEPROCESS Process = NULL;
NTSTATUS Status;
ACCESS_STATE AccessState;
AUX_ACCESS_DATA AuxData;
PAGED_CODE();
PSTRACE(PS_PROCESS_DEBUG,
"ClientId: %p Attributes: %p\n", ClientId, ObjectAttributes);
/* Check if we were called from user mode */
if (PreviousMode != KernelMode)
{
/* Enter SEH for probing */
_SEH2_TRY
{
/* Probe the thread handle */
ProbeForWriteHandle(ProcessHandle);
/* Check for a CID structure */
if (ClientId)
{
/* Probe and capture it */
ProbeForRead(ClientId, sizeof(CLIENT_ID), sizeof(ULONG));
SafeClientId = *ClientId;
ClientId = &SafeClientId;
}
/*
* Just probe the object attributes structure, don't capture it
* completely. This is done later if necessary
*/
ProbeForRead(ObjectAttributes,
sizeof(OBJECT_ATTRIBUTES),
sizeof(ULONG));
HasObjectName = (ObjectAttributes->ObjectName != NULL);
/* Validate user attributes */
Attributes = ObpValidateAttributes(ObjectAttributes->Attributes, PreviousMode);
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
/* Return the exception code */
_SEH2_YIELD(return _SEH2_GetExceptionCode());
}
_SEH2_END;
}
else
{
/* Otherwise just get the data directly */
HasObjectName = (ObjectAttributes->ObjectName != NULL);
/* Still have to sanitize attributes */
Attributes = ObpValidateAttributes(ObjectAttributes->Attributes, PreviousMode);
}
/* Can't pass both, fail */
if ((HasObjectName) && (ClientId)) return STATUS_INVALID_PARAMETER_MIX;
/* Create an access state */
Status = SeCreateAccessState(&AccessState,
&AuxData,
DesiredAccess,
&PsProcessType->TypeInfo.GenericMapping);
if (!NT_SUCCESS(Status)) return Status;
/* Check if this is a debugger */
if (SeSinglePrivilegeCheck(SeDebugPrivilege, PreviousMode))
{
/* Did he want full access? */
if (AccessState.RemainingDesiredAccess & MAXIMUM_ALLOWED)
{
/* Give it to him */
AccessState.PreviouslyGrantedAccess |= PROCESS_ALL_ACCESS;
}
else
{
/* Otherwise just give every other access he could want */
AccessState.PreviouslyGrantedAccess |=
AccessState.RemainingDesiredAccess;
}
/* The caller desires nothing else now */
AccessState.RemainingDesiredAccess = 0;
}
/* Open by name if one was given */
if (HasObjectName)
{
/* Open it */
Status = ObOpenObjectByName(ObjectAttributes,
PsProcessType,
PreviousMode,
&AccessState,
0,
NULL,
&hProcess);
/* Get rid of the access state */
SeDeleteAccessState(&AccessState);
}
else if (ClientId)
{
/* Open by Thread ID */
if (ClientId->UniqueThread)
{
/* Get the Process */
Status = PsLookupProcessThreadByCid(ClientId, &Process, &Thread);
}
else
{
/* Get the Process */
Status = PsLookupProcessByProcessId(ClientId->UniqueProcess,
&Process);
}
/* Check if we didn't find anything */
if (!NT_SUCCESS(Status))
{
/* Get rid of the access state and return */
SeDeleteAccessState(&AccessState);
return Status;
}
/* Open the Process Object */
Status = ObOpenObjectByPointer(Process,
Attributes,
&AccessState,
0,
PsProcessType,
PreviousMode,
&hProcess);
/* Delete the access state */
SeDeleteAccessState(&AccessState);
/* Dereference the thread if we used it */
if (Thread) ObDereferenceObject(Thread);
/* Dereference the Process */
ObDereferenceObject(Process);
}
else
{
/* neither an object name nor a client id was passed */
return STATUS_INVALID_PARAMETER_MIX;
}
/* Check for success */
if (NT_SUCCESS(Status))
{
/* Use SEH for write back */
_SEH2_TRY
{
/* Write back the handle */
*ProcessHandle = hProcess;
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
/* Get the exception code */
Status = _SEH2_GetExceptionCode();
}
_SEH2_END;
}
/* Return status */
return Status;
}
#if DBG && defined(KDBG)
BOOLEAN
PspKdbgIrpFind(
ULONG Argc,
PCHAR Argv[])
{
PLIST_ENTRY PsEntry, TdEntry, IrpEntry;
PEPROCESS Process = NULL;
PETHREAD Thread = NULL;
PIRP Irp = NULL;
PIO_STACK_LOCATION IoStack = NULL;
PUNICODE_STRING DriverName;
ULONG_PTR SData = 0;
ULONG Criteria = 0;
/*
* FIXME: To improve, badly
* This should just be a wrapper over !poolfind
* As a hack, here we just browse all the queued IRPs
* and return them
* If that's not *that* wrong, it makes sure that leaked
* IRPs are invisible!
* We also don't care about pool type, nor address start
*/
/* Gets the criteria and its data */
if (Argc > 2)
{
if (!KdbpGetHexNumber(Argv[2], &SData))
{
SData = 0;
}
else
{
if (strcmp(Argv[1], "device") == 0)
{
Criteria = 0x1;
}
else if (strcmp(Argv[1], "fileobject") == 0)
{
Criteria = 0x2;
}
else if (strcmp(Argv[1], "mdlprocess") == 0)
{
Criteria = 0x4;
}
else if (strcmp(Argv[1], "thread") == 0)
{
Criteria = 0x8;
}
else if (strcmp(Argv[1], "userevent") == 0)
{
Criteria = 0x10;
}
else if (strcmp(Argv[1], "arg") == 0)
{
Criteria = 0x1f;
}
}
}
PsEntry = PsActiveProcessHead.Flink;
/* Loop the process list */
while (PsEntry != &PsActiveProcessHead)
{
/* Get the process */
Process = CONTAINING_RECORD(PsEntry, EPROCESS, ActiveProcessLinks);
/* Loop the thread list */
TdEntry = Process->ThreadListHead.Flink;
while (TdEntry != &Process->ThreadListHead)
{
/* Get the thread */
Thread = CONTAINING_RECORD(TdEntry, ETHREAD, ThreadListEntry);
/* Loop the IRP list */
IrpEntry = Thread->IrpList.Flink;
while (IrpEntry != &Thread->IrpList)
{
/* Get the IRP and its current stack */
Irp = CONTAINING_RECORD(IrpEntry, IRP, ThreadListEntry);
IoStack = IoGetCurrentIrpStackLocation(Irp);
/* Get associated driver */
if (IoStack->DeviceObject && IoStack->DeviceObject->DriverObject)
DriverName = &IoStack->DeviceObject->DriverObject->DriverName;
else
DriverName = NULL;
/* Display if: no data, no criteria or if criteria matches data */
if (SData == 0 || Criteria == 0 ||
(Criteria & 0x1 && SData == (ULONG_PTR)IoStack->DeviceObject) ||
(Criteria & 0x2 && SData == (ULONG_PTR)Irp->Tail.Overlay.OriginalFileObject) ||
(Criteria & 0x4 && Irp->MdlAddress && SData == (ULONG_PTR)Irp->MdlAddress->Process) ||
(Criteria & 0x8 && SData == (ULONG_PTR)Irp->Tail.Overlay.Thread) ||
(Criteria & 0x10 && SData == (ULONG_PTR)Irp->UserEvent))
{
KdbpPrint("%p Thread %p current stack belongs to %wZ\n", Irp, Thread, DriverName);
}
IrpEntry = IrpEntry->Flink;
}
TdEntry = TdEntry->Flink;
}
PsEntry = PsEntry->Flink;
}
return TRUE;
}
#endif // DBG && KDBG
/* EOF */