- Massive re-write of some parts of Ps, based on a patch I wrote almost a year ago and peer-reviewed with Thomas and Filip. Causes some shutdown regressions and process leaks (will fix). Needs more work. Changelog:

- Architectural changes to match information in Windows Internals 4 and other documented sources of information (Windows Internals II by Probert). Code should match Win2003 layout.
    - Handle almost any possible process/thread sub structure and add its cleanup code stubs, so that when we actually implement them, we won't forget to clean them up in the process code.
    - Add interlocked usage of process and thread flags in order to make everything more thread-safe.
    - Better handle cases where threads die instantly, race conditions, and other weird issues.
    - Better handle process termination and thread termination.
    - Implement NtCreateProcessEx and update PspCreateProcess/NtCreateProcess.
    - Improve cleanup of process object in PspProcessDelete.
    - Optimize some things like User Thread startup.
    - Add some extra asserts, paged_code checks and also user-mode security check.
    - Implement helper API PsGetNextProcessThread
    - Optimize thread reaper (thanks Filip)
    - Do proper referencing/dereferencing of thread/processes (thanks Thomas)
    - Document FIXMEs needed for Ps code to be up to standards and complete.

svn path=/trunk/; revision=22976
This commit is contained in:
Alex Ionescu 2006-07-09 18:54:13 +00:00
parent d5dbbd1211
commit f4539b7037
32 changed files with 1941 additions and 1022 deletions

View file

@ -54,4 +54,18 @@ DbgkCreateThread(PVOID StartAddress)
#endif #endif
} }
VOID
NTAPI
DbgkExitProcess(IN NTSTATUS ExitStatus)
{
/* FIXME */
}
VOID
NTAPI
DbgkExitThread(IN NTSTATUS ExitStatus)
{
/* FIXME */
}
/* EOF */ /* EOF */

View file

@ -14,8 +14,17 @@
#include <internal/debug.h> #include <internal/debug.h>
POBJECT_TYPE DbgkDebugObjectType; POBJECT_TYPE DbgkDebugObjectType;
/* FUNCTIONS *****************************************************************/ /* FUNCTIONS *****************************************************************/
VOID
NTAPI
DbgkCopyProcessDebugPort(IN PEPROCESS Process,
IN PEPROCESS Parent)
{
/* FIXME: Implement */
}
NTSTATUS NTSTATUS
NTAPI NTAPI
NtCreateDebugObject(OUT PHANDLE DebugHandle, NtCreateDebugObject(OUT PHANDLE DebugHandle,

View file

@ -153,7 +153,7 @@ ShutdownThreadMain(PVOID Context)
sizeof(PCH))]); sizeof(PCH))]);
} }
PiShutdownProcessManager(); PspShutdownProcessManager();
Waittime.QuadPart = (LONGLONG)-10000000; /* 1sec */ Waittime.QuadPart = (LONGLONG)-10000000; /* 1sec */
KeDelayExecutionThread(KernelMode, FALSE, &Waittime); KeDelayExecutionThread(KernelMode, FALSE, &Waittime);

View file

@ -5,6 +5,21 @@ VOID
STDCALL STDCALL
DbgkCreateThread(PVOID StartAddress); DbgkCreateThread(PVOID StartAddress);
VOID
NTAPI
DbgkExitProcess(IN NTSTATUS ExitStatus);
VOID
NTAPI
DbgkExitThread(IN NTSTATUS ExitStatus);
VOID
NTAPI
DbgkCopyProcessDebugPort(
IN PEPROCESS Process,
IN PEPROCESS Parent
);
#endif #endif
/* EOF */ /* EOF */

View file

@ -498,7 +498,8 @@ ULONG
STDCALL STDCALL
KeSetProcess( KeSetProcess(
struct _KPROCESS* Process, struct _KPROCESS* Process,
KPRIORITY Increment KPRIORITY Increment,
BOOLEAN InWait
); );
VOID VOID

View file

@ -88,7 +88,7 @@ LpcSendTerminationPort(
LARGE_INTEGER CreationTime LARGE_INTEGER CreationTime
); );
/* Code in ntoskrnl/lpc/close.h */ /* Code in ntoskrnl/lpc/close.c */
VOID VOID
STDCALL STDCALL
@ -104,6 +104,10 @@ VOID
STDCALL STDCALL
LpcpDeletePort(IN PVOID ObjectBody); LpcpDeletePort(IN PVOID ObjectBody);
VOID
NTAPI
LpcExitThread(IN PETHREAD Thread);
/* Code in ntoskrnl/lpc/queue.c */ /* Code in ntoskrnl/lpc/queue.c */
VOID VOID

View file

@ -619,7 +619,7 @@ MmShowOutOfSpaceMessagePagingFile(VOID);
NTSTATUS NTSTATUS
STDCALL STDCALL
MmCreateProcessAddressSpace( MmCreateProcessAddressSpace(
IN struct _EPROCESS* Process, IN PEPROCESS Process,
IN PROS_SECTION_OBJECT Section OPTIONAL IN PROS_SECTION_OBJECT Section OPTIONAL
); );
@ -627,7 +627,7 @@ NTSTATUS
STDCALL STDCALL
MmCreatePeb(struct _EPROCESS *Process); MmCreatePeb(struct _EPROCESS *Process);
struct _TEB* PTEB
STDCALL STDCALL
MmCreateTeb( MmCreateTeb(
struct _EPROCESS *Process, struct _EPROCESS *Process,
@ -639,9 +639,21 @@ VOID
STDCALL STDCALL
MmDeleteTeb( MmDeleteTeb(
struct _EPROCESS *Process, struct _EPROCESS *Process,
struct _TEB* Teb PTEB Teb
); );
VOID
NTAPI
MmCleanProcessAddressSpace(IN PEPROCESS Process);
NTSTATUS
NTAPI
MmDeleteProcessAddressSpace(IN PEPROCESS Process);
ULONG
NTAPI
MmGetSessionLocaleId(VOID);
/* i386/pfault.c *************************************************************/ /* i386/pfault.c *************************************************************/
NTSTATUS NTSTATUS

View file

@ -101,6 +101,26 @@ InterlockedAnd(IN OUT LONG volatile *Target,
return j; return j;
} }
FORCEINLINE
LONG
InterlockedOr(IN OUT LONG volatile *Target,
IN LONG Set)
{
LONG i;
LONG j;
j = *Target;
do {
i = j;
j = InterlockedCompareExchange((PLONG)Target,
i | Set,
i);
} while (i != j);
return j;
}
/* /*
* generic information class probing code * generic information class probing code
*/ */

View file

@ -198,6 +198,17 @@ ObpReapObject(
IN PVOID Unused IN PVOID Unused
); );
VOID
NTAPI
ObDereferenceDeviceMap(IN PEPROCESS Process);
VOID
NTAPI
ObInheritDeviceMap(
IN PEPROCESS Parent,
IN PEPROCESS Process
);
VOID VOID
FASTCALL FASTCALL
ObpSetPermanentObject( ObpSetPermanentObject(
@ -211,9 +222,12 @@ ObpDeleteNameCheck(
IN PVOID Object IN PVOID Object
); );
// VOID
// Security functions NTAPI
// ObClearProcessHandleTable(IN PEPROCESS Process);
/* Security descriptor cache functions */
NTSTATUS NTSTATUS
NTAPI NTAPI
ObpInitSdCache( ObpInitSdCache(

View file

@ -14,4 +14,8 @@ NTSTATUS
NTAPI NTAPI
PopSetSystemPowerState(SYSTEM_POWER_STATE PowerState); PopSetSystemPowerState(SYSTEM_POWER_STATE PowerState);
VOID
NTAPI
PopCleanupPowerState(IN PPOWER_STATE PowerState);
#endif /* __NTOSKRNL_INCLUDE_INTERNAL_PO_H */ #endif /* __NTOSKRNL_INCLUDE_INTERNAL_PO_H */

View file

@ -40,7 +40,7 @@ PiInitProcessManager(VOID);
VOID VOID
NTAPI NTAPI
PiShutdownProcessManager(VOID); PspShutdownProcessManager(VOID);
VOID VOID
NTAPI NTAPI
@ -172,6 +172,13 @@ PspAssignPrimaryToken(
HANDLE TokenHandle HANDLE TokenHandle
); );
PETHREAD
NTAPI
PsGetNextProcessThread(
IN PEPROCESS Process,
IN PETHREAD Thread OPTIONAL
);
VOID VOID
STDCALL STDCALL
PsExitSpecialApc( PsExitSpecialApc(
@ -218,14 +225,15 @@ VOID
STDCALL STDCALL
PspExitThread(NTSTATUS ExitStatus); PspExitThread(NTSTATUS ExitStatus);
VOID NTSTATUS
STDCALL STDCALL
PspTerminateThreadByPointer( PspTerminateThreadByPointer(
PETHREAD Thread, PETHREAD Thread,
NTSTATUS ExitStatus NTSTATUS ExitStatus,
BOOLEAN bSelf
); );
VOID NTSTATUS
NTAPI NTAPI
PsUnfreezeOtherThread(PETHREAD Thread); PsUnfreezeOtherThread(PETHREAD Thread);
@ -287,9 +295,10 @@ VOID
NTAPI NTAPI
PsInitialiseSuspendImplementation(VOID); PsInitialiseSuspendImplementation(VOID);
NTSTATUS VOID
STDCALL STDCALL
PspExitProcess(PEPROCESS Process); PspExitProcess(BOOLEAN LastThread,
PEPROCESS Process);
VOID VOID
STDCALL STDCALL
@ -376,4 +385,34 @@ VOID
NTAPI NTAPI
PsUnlockProcess(PEPROCESS Process); PsUnlockProcess(PEPROCESS Process);
VOID
NTAPI
PspRemoveProcessFromJob(
IN PEPROCESS Process,
IN PEJOB Job
);
NTSTATUS
NTAPI
PspDeleteLdt(IN PEPROCESS Process);
NTSTATUS
NTAPI
PspDeleteVdmObjects(IN PEPROCESS Process);
VOID
NTAPI
PspDeleteProcessSecurity(IN PEPROCESS Process);
VOID
NTAPI
PspDeleteThreadSecurity(IN PETHREAD Thread);
VOID
NTAPI
PspExitProcessFromJob(
IN PEJOB Job,
IN PEPROCESS Process
);
#endif /* __INCLUDE_INTERNAL_PS_H */ #endif /* __INCLUDE_INTERNAL_PS_H */

View file

@ -136,6 +136,14 @@ PTOKEN
STDCALL STDCALL
SepCreateSystemProcessToken(VOID); SepCreateSystemProcessToken(VOID);
BOOLEAN
NTAPI
SeDetailedAuditingWithToken(IN PTOKEN Token);
VOID
NTAPI
SeAuditProcessExit(IN PEPROCESS Process);
NTSTATUS NTSTATUS
NTAPI NTAPI
SeExchangePrimaryToken( SeExchangePrimaryToken(

View file

@ -156,4 +156,7 @@
#define TAG_LPC_MESSAGE TAG('L', 'p', 'c', 'M') #define TAG_LPC_MESSAGE TAG('L', 'p', 'c', 'M')
#define TAG_LPC_ZONE TAG('L', 'p', 'c', 'Z') #define TAG_LPC_ZONE TAG('L', 'p', 'c', 'Z')
/* Se Process Audit */
#define TAG_SEPA TAG('S', 'e', 'P', 'a')
#endif /* _NTOSKRNL_TAG_H */ #endif /* _NTOSKRNL_TAG_H */

View file

@ -22,7 +22,7 @@ extern EX_WORK_QUEUE ExWorkerQueue[MaximumWorkQueue];
LIST_ENTRY PriorityListHead[MAXIMUM_PRIORITY]; LIST_ENTRY PriorityListHead[MAXIMUM_PRIORITY];
static ULONG PriorityListMask = 0; static ULONG PriorityListMask = 0;
ULONG IdleProcessorMask = 0; ULONG IdleProcessorMask = 0;
extern PETHREAD PspReaperList; extern LIST_ENTRY PspReaperListHead;
/* FUNCTIONS *****************************************************************/ /* FUNCTIONS *****************************************************************/
@ -1383,34 +1383,56 @@ KeTerminateThread(IN KPRIORITY Increment)
{ {
KIRQL OldIrql; KIRQL OldIrql;
PKTHREAD Thread = KeGetCurrentThread(); PKTHREAD Thread = KeGetCurrentThread();
PKPROCESS Process = Thread->ApcState.Process;
PLIST_ENTRY *ListHead;
PETHREAD Entry, SavedEntry;
PETHREAD *ThreadAddr;
DPRINT("Terminating\n");
/* Lock the Dispatcher Database and the APC Queue */ /* Lock the Dispatcher Database and the APC Queue */
DPRINT("Terminating\n"); ASSERT_IRQL(DISPATCH_LEVEL);
OldIrql = KeAcquireDispatcherDatabaseLock(); OldIrql = KeAcquireDispatcherDatabaseLock();
ASSERT(Thread->SwapBusy == FALSE);
/* Remove the thread from the list */ /* Make sure we won't get Swapped */
RemoveEntryList(&Thread->ThreadListEntry); Thread->SwapBusy = TRUE;
/* Insert into the Reaper List */ /* Save the Kernel and User Times */
DPRINT("List: %p\n", PspReaperList); Process->KernelTime += Thread->KernelTime;
((PETHREAD)Thread)->ReaperLink = PspReaperList; Process->UserTime += Thread->UserTime;
PspReaperList = (PETHREAD)Thread;
DPRINT("List: %p\n", PspReaperList);
/* Check if it's active */ /* Get the current entry and our Port */
if (PspReaping == FALSE) { Entry = (PETHREAD)PspReaperListHead.Flink;
ThreadAddr = &((PETHREAD)Thread)->ReaperLink;
/* Activate it. We use the internal function for speed, and use the Hyper Critical Queue */ /* Add it to the reaper's list */
PspReaping = TRUE; do
DPRINT("Terminating\n"); {
/* Get the list head */
ListHead = &PspReaperListHead.Flink;
/* Link ourselves */
*ThreadAddr = Entry;
SavedEntry = Entry;
/* Now try to do the exchange */
Entry = InterlockedCompareExchangePointer(ListHead, ThreadAddr, Entry);
/* Break out if the change was succesful */
} while (Entry != SavedEntry);
/* Check if the reaper wasn't active */
if (!Entry)
{
/* Activate it as a work item, directly through its Queue */
KiInsertQueue(&ExWorkerQueue[HyperCriticalWorkQueue].WorkerQueue, KiInsertQueue(&ExWorkerQueue[HyperCriticalWorkQueue].WorkerQueue,
&PspReaperWorkItem.List, &PspReaperWorkItem.List,
FALSE); FALSE);
} }
/* Handle Kernel Queues */ /* Handle Kernel Queues */
if (Thread->Queue) { if (Thread->Queue)
{
DPRINT("Waking Queue\n"); DPRINT("Waking Queue\n");
RemoveEntryList(&Thread->QueueListEntry); RemoveEntryList(&Thread->QueueListEntry);
KiWakeQueue(Thread->Queue); KiWakeQueue(Thread->Queue);
@ -1418,12 +1440,19 @@ KeTerminateThread(IN KPRIORITY Increment)
/* Signal the thread */ /* Signal the thread */
Thread->DispatcherHeader.SignalState = TRUE; Thread->DispatcherHeader.SignalState = TRUE;
if (IsListEmpty(&Thread->DispatcherHeader.WaitListHead) != TRUE) { if (IsListEmpty(&Thread->DispatcherHeader.WaitListHead) != TRUE)
{
/* Satisfy waits */ /* Satisfy waits */
KiWaitTest((PVOID)Thread, Increment); KiWaitTest((PVOID)Thread, Increment);
} }
/* Remove the thread from the list */
RemoveEntryList(&Thread->ThreadListEntry);
/* Set us as terminated, decrease the Process's stack count */
Thread->State = Terminated;
Process->StackCount--;
/* Find a new Thread */ /* Find a new Thread */
KiDispatchThreadNoLock(Terminated); KiDispatchThreadNoLock(Terminated);
} }

View file

@ -145,7 +145,8 @@ KeInitializeProcess(PKPROCESS Process,
ULONG ULONG
NTAPI NTAPI
KeSetProcess(PKPROCESS Process, KeSetProcess(PKPROCESS Process,
KPRIORITY Increment) KPRIORITY Increment,
BOOLEAN InWait)
{ {
KIRQL OldIrql; KIRQL OldIrql;
ULONG OldState; ULONG OldState;

View file

@ -16,6 +16,24 @@
/* FUNCTIONS *****************************************************************/ /* FUNCTIONS *****************************************************************/
VOID
NTAPI
LpcExitThread(IN PETHREAD Thread)
{
/* Make sure that the Reply Chain is empty */
if (!IsListEmpty(&Thread->LpcReplyChain))
{
/* It's not, remove the entry */
RemoveEntryList(&Thread->LpcReplyChain);
}
/* Set the thread in exit mode */
Thread->LpcExitThreadCalled = TRUE;
Thread->LpcReplyMessageId = 0;
/* FIXME: Reply to the LpcReplyMessage */
}
/********************************************************************** /**********************************************************************
* NAME * NAME
* *

View file

@ -15,37 +15,6 @@
#define NDEBUG #define NDEBUG
#include <internal/debug.h> #include <internal/debug.h>
/**********************************************************************
* NAME
* LpcSendTerminationPort/2
*
* DESCRIPTION
*
* ARGUMENTS
*
* RETURN VALUE
*
* REVISIONS
*/
NTSTATUS STDCALL
LpcSendTerminationPort (IN PEPORT Port,
IN LARGE_INTEGER CreateTime)
{
NTSTATUS Status;
CLIENT_DIED_MSG Msg;
#ifdef __USE_NT_LPC__
Msg.h.u2.s2.Type = LPC_CLIENT_DIED;
#endif
Msg.h.u1.s1.TotalLength = sizeof(Msg);
Msg.h.u1.s1.DataLength = sizeof(Msg) - sizeof(PORT_MESSAGE);
Msg.CreateTime = CreateTime;
Status = LpcRequestPort (Port, &Msg.h);
return(Status);
}
/********************************************************************** /**********************************************************************
* NAME * NAME
* LpcSendDebugMessagePort/3 * LpcSendDebugMessagePort/3

View file

@ -22,66 +22,12 @@ ULONG MmUserProbeAddress = 0;
PVOID MmHighestUserAddress = NULL; PVOID MmHighestUserAddress = NULL;
PBOOLEAN Mm64BitPhysicalAddress = FALSE; PBOOLEAN Mm64BitPhysicalAddress = FALSE;
PVOID MmSystemRangeStart = NULL; PVOID MmSystemRangeStart = NULL;
ULONG MmReadClusterSize;
MM_STATS MmStats; MM_STATS MmStats;
/* FUNCTIONS ****************************************************************/ /* FUNCTIONS ****************************************************************/
NTSTATUS
NTAPI
MmReleaseMmInfo(PEPROCESS Process)
{
PVOID Address;
PMEMORY_AREA MemoryArea;
DPRINT("MmReleaseMmInfo(Process %x (%s))\n", Process,
Process->ImageFileName);
MmLockAddressSpace((PMADDRESS_SPACE)&Process->VadRoot);
while ((MemoryArea = ((PMADDRESS_SPACE)&Process->VadRoot)->MemoryAreaRoot) != NULL)
{
switch (MemoryArea->Type)
{
case MEMORY_AREA_SECTION_VIEW:
Address = (PVOID)MemoryArea->StartingAddress;
MmUnlockAddressSpace((PMADDRESS_SPACE)&Process->VadRoot);
MmUnmapViewOfSection((PEPROCESS)Process, Address);
MmLockAddressSpace((PMADDRESS_SPACE)&Process->VadRoot);
break;
case MEMORY_AREA_VIRTUAL_MEMORY:
case MEMORY_AREA_PEB_OR_TEB:
MmFreeVirtualMemory(Process, MemoryArea);
break;
case MEMORY_AREA_SHARED_DATA:
case MEMORY_AREA_NO_ACCESS:
MmFreeMemoryArea((PMADDRESS_SPACE)&Process->VadRoot,
MemoryArea,
NULL,
NULL);
break;
case MEMORY_AREA_MDL_MAPPING:
KEBUGCHECK(PROCESS_HAS_LOCKED_PAGES);
break;
default:
KEBUGCHECK(0);
}
}
Mmi386ReleaseMmInfo(Process);
MmUnlockAddressSpace((PMADDRESS_SPACE)&Process->VadRoot);
MmDestroyAddressSpace((PMADDRESS_SPACE)&Process->VadRoot);
DPRINT("Finished MmReleaseMmInfo()\n");
return(STATUS_SUCCESS);
}
/* /*
* @implemented * @implemented
*/ */

View file

@ -23,6 +23,33 @@ extern ULONG NtGlobalFlag;
/* FUNCTIONS *****************************************************************/ /* FUNCTIONS *****************************************************************/
LCID
NTAPI
MmGetSessionLocaleId(VOID)
{
PEPROCESS Process;
PAGED_CODE();
/* Get the current process */
Process = PsGetCurrentProcess();
/* Check if it's the Session Leader */
if (Process->Vm.Flags.SessionLeader)
{
/* Make sure it has a valid Session */
if (Process->Session)
{
/* Get the Locale ID */
#if ROS_HAS_SESSIONS
return ((PMM_SESSION_SPACE)Process->Session)->LocaleId;
#endif
}
}
/* Not a session leader, return the default */
return PsDefaultThreadLocaleId;
}
PVOID PVOID
STDCALL STDCALL
MiCreatePebOrTeb(PEPROCESS Process, MiCreatePebOrTeb(PEPROCESS Process,
@ -489,6 +516,9 @@ MmCreateProcessAddressSpace(IN PEPROCESS Process,
goto exit; goto exit;
} }
/* The process now has an address space */
Process->HasAddressSpace = TRUE;
/* Check if there's a Section Object */ /* Check if there's a Section Object */
if (Section) if (Section)
{ {
@ -559,3 +589,64 @@ exit:
/* Return status to caller */ /* Return status to caller */
return Status; return Status;
} }
VOID
NTAPI
MmCleanProcessAddressSpace(IN PEPROCESS Process)
{
/* FIXME: Add part of MmDeleteProcessAddressSpace here */
}
NTSTATUS
NTAPI
MmDeleteProcessAddressSpace(PEPROCESS Process)
{
PVOID Address;
PMEMORY_AREA MemoryArea;
DPRINT("MmDeleteProcessAddressSpace(Process %x (%s))\n", Process,
Process->ImageFileName);
MmLockAddressSpace((PMADDRESS_SPACE)&Process->VadRoot);
while ((MemoryArea = ((PMADDRESS_SPACE)&Process->VadRoot)->MemoryAreaRoot) != NULL)
{
switch (MemoryArea->Type)
{
case MEMORY_AREA_SECTION_VIEW:
Address = (PVOID)MemoryArea->StartingAddress;
MmUnlockAddressSpace((PMADDRESS_SPACE)&Process->VadRoot);
MmUnmapViewOfSection(Process, Address);
MmLockAddressSpace((PMADDRESS_SPACE)&Process->VadRoot);
break;
case MEMORY_AREA_VIRTUAL_MEMORY:
case MEMORY_AREA_PEB_OR_TEB:
MmFreeVirtualMemory(Process, MemoryArea);
break;
case MEMORY_AREA_SHARED_DATA:
case MEMORY_AREA_NO_ACCESS:
MmFreeMemoryArea((PMADDRESS_SPACE)&Process->VadRoot,
MemoryArea,
NULL,
NULL);
break;
case MEMORY_AREA_MDL_MAPPING:
KEBUGCHECK(PROCESS_HAS_LOCKED_PAGES);
break;
default:
KEBUGCHECK(0);
}
}
Mmi386ReleaseMmInfo(Process);
MmUnlockAddressSpace((PMADDRESS_SPACE)&Process->VadRoot);
MmDestroyAddressSpace((PMADDRESS_SPACE)&Process->VadRoot);
DPRINT("Finished MmReleaseMmInfo()\n");
return(STATUS_SUCCESS);
}

View file

@ -979,6 +979,14 @@ Language=English
SPIN_LOCK_INIT_FAILURE SPIN_LOCK_INIT_FAILURE
. .
MessageId=0x94
Severity=Success
Facility=System
SymbolicName=KERNEL_STACK_LOCKED_AT_EXIT
Language=English
KERNEL_STACK_LOCKED_AT_EXIT
.
MessageId=0x96 MessageId=0x96
Severity=Success Severity=Success
Facility=System Facility=System
@ -1085,6 +1093,23 @@ Language=English
certain\n conditions. There is absolutely no warranty for ReactOS.\n certain\n conditions. There is absolutely no warranty for ReactOS.\n
. .
MessageId=0xE9
Severity=Success
Facility=System
SymbolicName=ACTIVE_EX_WORKER_THREAD_TERMINATION
Language=English
ACTIVE_EX_WORKER_THREAD_TERMINATION
.
MessageId=0xEF
Severity=Success
Facility=System
SymbolicName=CRITICAL_PROCESS_DIED
Language=English
CRITICAL_PROCESS_DIED
.
MessageId=0xFC MessageId=0xFC
Severity=Success Severity=Success
Facility=System Facility=System

View file

@ -244,7 +244,7 @@ ObpCloseHandleTableEntry(IN PHANDLE_TABLE HandleTable,
// //
// WE DONT CLOSE REGISTRY HANDLES BECAUSE CM IS BRAINDEAD // WE DONT CLOSE REGISTRY HANDLES BECAUSE CM IS BRAINDEAD
// //
DPRINT1("NOT CLOSING THE KEY\n"); DPRINT("NOT CLOSING THE KEY\n");
} }
else else
{ {
@ -1119,6 +1119,13 @@ ObpDuplicateHandleCallback(IN PHANDLE_TABLE HandleTable,
return Ret; return Ret;
} }
VOID
NTAPI
ObClearProcessHandleTable(IN PEPROCESS Process)
{
/* FIXME */
}
/*++ /*++
* @name ObpCreateHandleTable * @name ObpCreateHandleTable
* *

View file

@ -11,6 +11,7 @@
/* INCLUDES ******************************************************************/ /* INCLUDES ******************************************************************/
#define NTDDI_VERSION NTDDI_WINXP
#include <ntoskrnl.h> #include <ntoskrnl.h>
#define NDEBUG #define NDEBUG
#include <internal/debug.h> #include <internal/debug.h>
@ -20,6 +21,49 @@ POBJECT_DIRECTORY ObpTypeDirectoryObject = NULL;
/* PRIVATE FUNCTIONS *********************************************************/ /* PRIVATE FUNCTIONS *********************************************************/
VOID
NTAPI
ObDereferenceDeviceMap(IN PEPROCESS Process)
{
//KIRQL OldIrql;
PDEVICE_MAP DeviceMap = Process->DeviceMap;
/* FIXME: We don't use Process Devicemaps yet */
if (DeviceMap)
{
/* FIXME: Acquire the DeviceMap Spinlock */
// KeAcquireSpinLock(DeviceMap->Lock, &OldIrql);
/* Delete the device map link and dereference it */
Process->DeviceMap = NULL;
if (--DeviceMap->ReferenceCount)
{
/* Nobody is referencing it anymore, unlink the DOS directory */
DeviceMap->DosDevicesDirectory->DeviceMap = NULL;
/* FIXME: Release the DeviceMap Spinlock */
// KeReleasepinLock(DeviceMap->Lock, OldIrql);
/* Dereference the DOS Devices Directory and free the Device Map */
ObDereferenceObject(DeviceMap->DosDevicesDirectory);
ExFreePool(DeviceMap);
}
else
{
/* FIXME: Release the DeviceMap Spinlock */
// KeReleasepinLock(DeviceMap->Lock, OldIrql);
}
}
}
VOID
NTAPI
ObInheritDeviceMap(IN PEPROCESS Parent,
IN PEPROCESS Process)
{
/* FIXME: Devicemap Support */
}
/*++ /*++
* @name ObpDeleteNameCheck * @name ObpDeleteNameCheck
* *

View file

@ -90,7 +90,7 @@ ObfDereferenceObject(IN PVOID Object)
if (Header->PointerCount < Header->HandleCount) if (Header->PointerCount < Header->HandleCount)
{ {
DPRINT1("Misbehaving object: %wZ\n", &Header->Type->Name); DPRINT("Misbehaving object: %wZ\n", &Header->Type->Name);
return; return;
} }
@ -100,7 +100,7 @@ ObfDereferenceObject(IN PVOID Object)
/* Sanity check */ /* Sanity check */
if (Header->HandleCount) if (Header->HandleCount)
{ {
DPRINT1("Misbehaving object: %wZ\n", &Header->Type->Name); DPRINT("Misbehaving object: %wZ\n", &Header->Type->Name);
return; return;
} }

View file

@ -183,6 +183,13 @@ PoSetDeviceBusy(
{ {
} }
VOID
NTAPI
PopCleanupPowerState(IN PPOWER_STATE PowerState)
{
/* FIXME */
}
/* /*
* @unimplemented * @unimplemented
*/ */

View file

@ -4,16 +4,14 @@
* PROJECT: ReactOS kernel * PROJECT: ReactOS kernel
* FILE: ntoskrnl/ps/job.c * FILE: ntoskrnl/ps/job.c
* PURPOSE: Job Native Functions * PURPOSE: Job Native Functions
*
* PROGRAMMERS: Alex Ionescu (alex@relsoft.net) (stubs) * PROGRAMMERS: Alex Ionescu (alex@relsoft.net) (stubs)
* Thomas Weidenmueller <w3seek@reactos.com> * Thomas Weidenmueller <w3seek@reactos.com>
*/ */
/* Note: Jobs are only supported on Win2K+ */
/* INCLUDES *****************************************************************/ /* INCLUDES *****************************************************************/
#define NDEBUG
#include <ntoskrnl.h> #include <ntoskrnl.h>
#define NDEBUG
#include <internal/debug.h> #include <internal/debug.h>
#if defined (ALLOC_PRAGMA) #if defined (ALLOC_PRAGMA)
@ -39,7 +37,8 @@ static GENERIC_MAPPING PiJobMapping =
/* FUNCTIONS *****************************************************************/ /* FUNCTIONS *****************************************************************/
VOID STDCALL VOID
NTAPI
PiDeleteJob ( PVOID ObjectBody ) PiDeleteJob ( PVOID ObjectBody )
{ {
PEJOB Job = (PEJOB)ObjectBody; PEJOB Job = (PEJOB)ObjectBody;
@ -89,8 +88,7 @@ PsInitJobManagment ( VOID )
NTSTATUS NTSTATUS
NTAPI NTAPI
PspAssignProcessToJob ( PspAssignProcessToJob(PEPROCESS Process,
PEPROCESS Process,
PEJOB Job) PEJOB Job)
{ {
DPRINT("PspAssignProcessToJob() is unimplemented!\n"); DPRINT("PspAssignProcessToJob() is unimplemented!\n");
@ -99,8 +97,7 @@ PspAssignProcessToJob (
NTSTATUS NTSTATUS
NTAPI NTAPI
PspTerminateJobObject ( PspTerminateJobObject(PEJOB Job,
PEJOB Job,
KPROCESSOR_MODE AccessMode, KPROCESSOR_MODE AccessMode,
NTSTATUS ExitStatus ) NTSTATUS ExitStatus )
{ {
@ -108,6 +105,21 @@ PspTerminateJobObject (
return STATUS_NOT_IMPLEMENTED; return STATUS_NOT_IMPLEMENTED;
} }
VOID
NTAPI
PspRemoveProcessFromJob(IN PEPROCESS Process,
IN PEJOB Job)
{
/* FIXME */
}
VOID
NTAPI
PspExitProcessFromJob(IN PEJOB Job,
IN PEPROCESS Process)
{
/* FIXME */
}
/* /*
* @unimplemented * @unimplemented
@ -547,8 +559,7 @@ PsGetJobUIRestrictionsClass ( PEJOB Job )
*/ */
VOID VOID
STDCALL STDCALL
PsSetJobUIRestrictionsClass ( PsSetJobUIRestrictionsClass(PEJOB Job,
PEJOB Job,
ULONG UIRestrictionsClass) ULONG UIRestrictionsClass)
{ {
ASSERT(Job); ASSERT(Job);

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -64,15 +64,6 @@ NTSTATUS STDCALL INIT_FUNCTION PspLookupKernelUserEntryPoints(VOID);
/* FUNCTIONS ***************************************************************/ /* FUNCTIONS ***************************************************************/
VOID
NTAPI
PiShutdownProcessManager(VOID)
{
DPRINT("PiShutdownProcessManager()\n");
PspKillMostProcesses();
}
VOID VOID
INIT_FUNCTION INIT_FUNCTION
NTAPI NTAPI

View file

@ -39,6 +39,40 @@ PspUnlockProcessSecurityShared(PEPROCESS Process)
KeLeaveGuardedRegion(); KeLeaveGuardedRegion();
} }
VOID
NTAPI
PspDeleteProcessSecurity(IN PEPROCESS Process)
{
/* Check if we have a token */
if (Process->Token.Object)
{
/* Deassign it */
SeDeassignPrimaryToken(Process);
Process->Token.Object = NULL;
}
}
VOID
NTAPI
PspDeleteThreadSecurity(IN PETHREAD Thread)
{
/* Check if we have active impersonation info */
if (Thread->ActiveImpersonationInfo)
{
/* Dereference its token */
ObDereferenceObject(Thread->ImpersonationInfo->Token);
}
/* Check if we have impersonation info */
if (Thread->ImpersonationInfo)
{
/* Free it */
ExFreePool(Thread->ImpersonationInfo);
Thread->ActiveImpersonationInfo = FALSE;
Thread->ImpersonationInfo = NULL;
}
}
/* FUNCTIONS *****************************************************************/ /* FUNCTIONS *****************************************************************/
/* /*

View file

@ -1,11 +1,23 @@
/* /*
* COPYRIGHT: See COPYING in the top level directory * PROJECT: ReactOS Kernel
* PROJECT: ReactOS kernel * LICENSE: GPL - See COPYING in the top level directory
* FILE: ntoskrnl/ps/thread.c * FILE: ntoskrnl/ps/thread.c
* PURPOSE: Thread managment * PURPOSE: Process Manager: Thread Management
* * PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org)
* PROGRAMMERS: David Welch (welch@mcmail.com) * Thomas Weidenmueller (w3seek@reactos.org
* Phillip Susi */
/*
* Alex FIXMEs:
* - CRITICAL: NtCurrentTeb returns KPCR.
* - CRITICAL: Verify rundown APIs (ex/rundown.c) and use them where necessary.
* - MAJOR: Implement Pushlocks and use them as process lock.
* - MAJOR: Implement Safe Referencing (See PsGetNextProcess/Thread).
* - MAJOR: Implement Fast Referencing (mostly for tokens).
* - MAJOR: Use Guarded Mutex instead of Fast Mutex for Active Process Locks.
* - Generate process cookie for user-more thread.
* - Add security calls where necessary.
* - KeInit/StartThread for better isolation of code
*/ */
/* INCLUDES ****************************************************************/ /* INCLUDES ****************************************************************/
@ -19,83 +31,112 @@
extern LIST_ENTRY PsActiveProcessHead; extern LIST_ENTRY PsActiveProcessHead;
extern PEPROCESS PsIdleProcess; extern PEPROCESS PsIdleProcess;
extern PVOID PspSystemDllEntryPoint; extern PVOID PspSystemDllEntryPoint;
extern PVOID PspSystemDllBase;
extern PHANDLE_TABLE PspCidTable; extern PHANDLE_TABLE PspCidTable;
extern BOOLEAN CcPfEnablePrefetcher;
extern ULONG MmReadClusterSize;
POBJECT_TYPE PsThreadType = NULL; POBJECT_TYPE PsThreadType = NULL;
/* FUNCTIONS ***************************************************************/ /* FUNCTIONS ***************************************************************/
VOID VOID
STDCALL NTAPI
PspThreadSpecialApc(PKAPC Apc,
PKNORMAL_ROUTINE* NormalRoutine,
PVOID* NormalContext,
PVOID* SystemArgument1,
PVOID* SystemArgument2)
{
ExFreePool(Apc);
}
VOID
STDCALL
PspUserThreadStartup(PKSTART_ROUTINE StartRoutine, PspUserThreadStartup(PKSTART_ROUTINE StartRoutine,
PVOID StartContext) PVOID StartContext)
{ {
PKAPC ThreadApc; PETHREAD Thread;
PETHREAD Thread = PsGetCurrentThread(); PTEB Teb;
BOOLEAN DeadThread = FALSE;
PAGED_CODE();
DPRINT("I am a new USER thread. This is my start routine: %p. This my context: %p." /* Go to Passive Level */
"This is my IRQL: %d. This is my Thread Pointer: %x.\n", StartRoutine, KeLowerIrql(PASSIVE_LEVEL);
StartContext, KeGetCurrentIrql(), Thread); Thread = PsGetCurrentThread();
if (!Thread->Terminated) { /* Check if the thread is dead */
if (Thread->DeadThread)
/* Allocate the APC */ {
ThreadApc = ExAllocatePoolWithTag(NonPagedPool, sizeof(KAPC), TAG('T', 'h', 'r','d')); /* Remember that we're dead */
DPRINT1("This thread is already dead\n");
/* Initialize it */ DeadThread = TRUE;
KeInitializeApc(ThreadApc, }
&Thread->Tcb, else
OriginalApcEnvironment, {
PspThreadSpecialApc, /* Get the Locale ID and save Preferred Proc */
NULL, Teb = NtCurrentTeb(); /* FIXME: This returns KPCR!!! */
PspSystemDllEntryPoint, //Teb->CurrentLocale = MmGetSessionLocaleId();
UserMode, //Teb->IdealProcessor = Thread->Tcb.IdealProcessor;
NULL);
/* Insert it into the queue */
KeInsertQueueApc(ThreadApc, NULL, NULL, IO_NO_INCREMENT);
Thread->Tcb.ApcState.UserApcPending = TRUE;
} }
/* Go to Passive Level and notify debugger */ /* Check if this is a system thread, or if we're hiding */
KeLowerIrql(PASSIVE_LEVEL); if ((Thread->SystemThread) || (Thread->HideFromDebugger))
DbgkCreateThread(StartContext); {
/* Notify the debugger */
DbgkCreateThread(StartContext);
}
/* Make sure we're not already dead */
if (!DeadThread)
{
/* Check if the Prefetcher is enabled */
if (CcPfEnablePrefetcher)
{
/* FIXME: Prepare to prefetch this process */
}
/* Raise to APC */
KfRaiseIrql(APC_LEVEL);
/* Queue the User APC */
KiInitializeUserApc(NULL,
(PVOID)((ULONG_PTR)Thread->Tcb.InitialStack -
sizeof(KTRAP_FRAME) -
sizeof(FX_SAVE_AREA)),
PspSystemDllEntryPoint,
NULL,
PspSystemDllBase,
NULL);
/* Lower it back to passive */
KeLowerIrql(PASSIVE_LEVEL);
}
else
{
/* We're dead, kill us now */
PspTerminateThreadByPointer(Thread, STATUS_THREAD_IS_TERMINATING, TRUE);
}
/* Do we have a cookie set yet? */
if (!SharedUserData->Cookie)
{
/* FIXME: Generate cookie */
}
} }
VOID VOID
STDCALL NTAPI
PspSystemThreadStartup(PKSTART_ROUTINE StartRoutine, PspSystemThreadStartup(PKSTART_ROUTINE StartRoutine,
PVOID StartContext) PVOID StartContext)
{ {
PETHREAD Thread = PsGetCurrentThread(); PETHREAD Thread;
/* Unlock the dispatcher Database */ /* Unlock the dispatcher Database */
KeLowerIrql(PASSIVE_LEVEL); KeLowerIrql(PASSIVE_LEVEL);
Thread = PsGetCurrentThread();
/* Make sure it's not terminated by now */ /* Make sure the thread isn't gone */
if (!Thread->Terminated) { if (!(Thread->Terminated) || !(Thread->DeadThread))
{
/* Call it */ /* Call it the Start Routine */
(StartRoutine)(StartContext); StartRoutine(StartContext);
} }
/* Exit the thread */ /* Exit the thread */
PspExitThread(STATUS_SUCCESS); PspTerminateThreadByPointer(Thread, STATUS_SUCCESS, TRUE);
} }
NTSTATUS NTSTATUS
STDCALL NTAPI
PspCreateThread(OUT PHANDLE ThreadHandle, PspCreateThread(OUT PHANDLE ThreadHandle,
IN ACCESS_MASK DesiredAccess, IN ACCESS_MASK DesiredAccess,
IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL, IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL,
@ -117,46 +158,54 @@ PspCreateThread(OUT PHANDLE ThreadHandle,
NTSTATUS Status; NTSTATUS Status;
HANDLE_TABLE_ENTRY CidEntry; HANDLE_TABLE_ENTRY CidEntry;
ULONG_PTR KernelStack; ULONG_PTR KernelStack;
PAGED_CODE();
/* If we were called from PsCreateSystemThread, then we're kernel mode */
if (StartRoutine) PreviousMode = KernelMode;
/* Reference the Process by handle or pointer, depending on what we got */ /* Reference the Process by handle or pointer, depending on what we got */
DPRINT("PspCreateThread: %x, %x, %x\n", ProcessHandle, TargetProcess, ThreadContext); if (ProcessHandle)
if (ProcessHandle) { {
/* Normal thread or System Thread */ /* Normal thread or System Thread */
DPRINT("Referencing Parent Process\n");
Status = ObReferenceObjectByHandle(ProcessHandle, Status = ObReferenceObjectByHandle(ProcessHandle,
PROCESS_CREATE_THREAD, PROCESS_CREATE_THREAD,
PsProcessType, PsProcessType,
PreviousMode, PreviousMode,
(PVOID*)&Process, (PVOID*)&Process,
NULL); NULL);
} else { }
else
{
/* System thread inside System Process, or Normal Thread with a bug */ /* System thread inside System Process, or Normal Thread with a bug */
if (StartRoutine) { if (StartRoutine)
{
/* Reference the Process by Pointer */ /* Reference the Process by Pointer */
DPRINT("Referencing Parent System Process\n");
ObReferenceObject(TargetProcess); ObReferenceObject(TargetProcess);
Process = TargetProcess; Process = TargetProcess;
Status = STATUS_SUCCESS; Status = STATUS_SUCCESS;
}
} else { else
{
/* Fake ObReference returning this */ /* Fake ObReference returning this */
Status = STATUS_INVALID_HANDLE; Status = STATUS_INVALID_HANDLE;
} }
} }
/* Check for success */ /* Check for success */
if(!NT_SUCCESS(Status)) { if(!NT_SUCCESS(Status))
{
DPRINT1("Invalid Process Handle, or no handle given\n"); DPRINT1("Invalid Process Handle, or no handle given\n");
return(Status); return Status;
}
/* Also make sure that User-Mode isn't trying to create a system thread */
if ((PreviousMode != KernelMode) && (Process == PsInitialSystemProcess))
{
ObDereferenceObject(Process);
return STATUS_INVALID_HANDLE;
} }
/* Create Thread Object */ /* Create Thread Object */
DPRINT("Creating Thread Object\n");
Status = ObCreateObject(PreviousMode, Status = ObCreateObject(PreviousMode,
PsThreadType, PsThreadType,
ObjectAttributes, ObjectAttributes,
@ -166,69 +215,61 @@ PspCreateThread(OUT PHANDLE ThreadHandle,
0, 0,
0, 0,
(PVOID*)&Thread); (PVOID*)&Thread);
if (!NT_SUCCESS(Status))
/* Check for success */ {
if (!NT_SUCCESS(Status)) { /* We failed; dereference the process and exit */
/* Dereference the Process */
DPRINT1("Failed to Create Thread Object\n"); DPRINT1("Failed to Create Thread Object\n");
ObDereferenceObject(Process); ObDereferenceObject(Process);
return(Status); return Status;
} }
/* Zero the Object entirely */ /* Zero the Object entirely */
DPRINT("Cleaning Thread Object\n");
RtlZeroMemory(Thread, sizeof(ETHREAD)); RtlZeroMemory(Thread, sizeof(ETHREAD));
/* Set the Process CID */
Thread->ThreadsProcess = Process;
Thread->Cid.UniqueProcess = Process->UniqueProcessId;
/* Create Cid Handle */ /* Create Cid Handle */
DPRINT("Creating Thread Handle (CID)\n");
CidEntry.Object = Thread; CidEntry.Object = Thread;
CidEntry.GrantedAccess = 0; CidEntry.GrantedAccess = 0;
Thread->Cid.UniqueThread = ExCreateHandle(PspCidTable, &CidEntry); Thread->Cid.UniqueThread = ExCreateHandle(PspCidTable, &CidEntry);
if (!Thread->Cid.UniqueThread) { if (!Thread->Cid.UniqueThread)
{
/* We couldn't create the CID, dereference everything and fail */
DPRINT1("Failed to create Thread Handle (CID)\n"); DPRINT1("Failed to create Thread Handle (CID)\n");
ObDereferenceObject(Process); ObDereferenceObject(Process);
ObDereferenceObject(Thread); ObDereferenceObject(Thread);
return STATUS_INSUFFICIENT_RESOURCES; return STATUS_INSUFFICIENT_RESOURCES;
} }
/* Initialize Lists */ /* Save the read cluster size */
DPRINT("Initialliazing Thread Lists and Locks\n"); Thread->ReadClusterSize = MmReadClusterSize;
/* Initialize the LPC Reply Semaphore */
KeInitializeSemaphore(&Thread->LpcReplySemaphore, 0, MAXLONG);
/* Initialize the list heads and locks */
InitializeListHead(&Thread->LpcReplyChain); InitializeListHead(&Thread->LpcReplyChain);
InitializeListHead(&Thread->IrpList); InitializeListHead(&Thread->IrpList);
InitializeListHead(&Thread->PostBlockList);
InitializeListHead(&Thread->ActiveTimerListHead); InitializeListHead(&Thread->ActiveTimerListHead);
KeInitializeSpinLock(&Thread->ActiveTimerListLock); KeInitializeSpinLock(&Thread->ActiveTimerListLock);
/* Initialize LPC */
DPRINT("Initialliazing Thread Semaphore\n");
KeInitializeSemaphore(&Thread->LpcReplySemaphore, 0, MAXLONG);
/* Allocate Stack for non-GUI Thread */ /* Allocate Stack for non-GUI Thread */
DPRINT("Initialliazing Thread Stack\n");
KernelStack = (ULONG_PTR)MmCreateKernelStack(FALSE) + KERNEL_STACK_SIZE; KernelStack = (ULONG_PTR)MmCreateKernelStack(FALSE) + KERNEL_STACK_SIZE;
/* Set the Process CID */
DPRINT("Initialliazing Thread PID and Parent Process\n");
Thread->Cid.UniqueProcess = Process->UniqueProcessId;
Thread->ThreadsProcess = Process;
/* Now let the kernel initialize the context */ /* Now let the kernel initialize the context */
if (ThreadContext) { if (ThreadContext)
{
/* User-mode Thread */ /* User-mode Thread, create Teb */
/* Create Teb */
DPRINT("Initialliazing Thread PEB\n");
TebBase = MmCreateTeb(Process, &Thread->Cid, InitialTeb); TebBase = MmCreateTeb(Process, &Thread->Cid, InitialTeb);
/* Set the Start Addresses */ /* Set the Start Addresses */
DPRINT("Initialliazing Thread Start Addresses :%x, %x\n", ThreadContext->Eip, ThreadContext->Eax);
Thread->StartAddress = (PVOID)ThreadContext->Eip; Thread->StartAddress = (PVOID)ThreadContext->Eip;
Thread->Win32StartAddress = (PVOID)ThreadContext->Eax; Thread->Win32StartAddress = (PVOID)ThreadContext->Eax;
/* Let the kernel intialize the Thread */ /* Let the kernel intialize the Thread */
DPRINT("Initialliazing Kernel Thread\n");
KeInitializeThread(&Process->Pcb, KeInitializeThread(&Process->Pcb,
&Thread->Tcb, &Thread->Tcb,
PspUserThreadStartup, PspUserThreadStartup,
@ -237,16 +278,14 @@ PspCreateThread(OUT PHANDLE ThreadHandle,
ThreadContext, ThreadContext,
TebBase, TebBase,
(PVOID)KernelStack); (PVOID)KernelStack);
}
} else { else
{
/* System Thread */ /* System Thread */
DPRINT("Initialliazing Thread Start Address :%x\n", StartRoutine);
Thread->StartAddress = StartRoutine; Thread->StartAddress = StartRoutine;
Thread->SystemThread = TRUE; InterlockedOr(&Thread->CrossThreadFlags, 0x10);
/* Let the kernel intialize the Thread */ /* Let the kernel intialize the Thread */
DPRINT("Initialliazing Kernel Thread\n");
KeInitializeThread(&Process->Pcb, KeInitializeThread(&Process->Pcb,
&Thread->Tcb, &Thread->Tcb,
PspSystemThreadStartup, PspSystemThreadStartup,
@ -262,61 +301,63 @@ PspCreateThread(OUT PHANDLE ThreadHandle,
* Note, this is the ETHREAD Thread List. It is removed in * Note, this is the ETHREAD Thread List. It is removed in
* ps/kill.c!PspExitThread. * ps/kill.c!PspExitThread.
*/ */
DPRINT("Inserting into Process Thread List \n");
InsertTailList(&Process->ThreadListHead, &Thread->ThreadListEntry); InsertTailList(&Process->ThreadListHead, &Thread->ThreadListEntry);
Process->ActiveThreads++;
/* Notify WMI */
//WmiTraceProcess(Process, TRUE);
//WmiTraceThread(Thread, InitialTeb, TRUE);
/* Notify Thread Creation */ /* Notify Thread Creation */
DPRINT("Running Thread Notify \n");
PspRunCreateThreadNotifyRoutines(Thread, TRUE); PspRunCreateThreadNotifyRoutines(Thread, TRUE);
/* Suspend the Thread if we have to */ /* Suspend the Thread if we have to */
if (CreateSuspended) { if (CreateSuspended)
{
DPRINT("Suspending Thread\n");
KeSuspendThread(&Thread->Tcb); KeSuspendThread(&Thread->Tcb);
} }
/* Check if we were already terminated */
if (Thread->Terminated)
{
/* Force us to wake up to terminate */
KeForceResumeThread(&Thread->Tcb);
}
/* Reference ourselves as a keep-alive */ /* Reference ourselves as a keep-alive */
ObReferenceObject(Thread); ObReferenceObject(Thread);
/* Insert the Thread into the Object Manager */ /* Insert the Thread into the Object Manager */
DPRINT("Inserting Thread\n");
Status = ObInsertObject((PVOID)Thread, Status = ObInsertObject((PVOID)Thread,
NULL, NULL,
DesiredAccess, DesiredAccess,
0, 0,
NULL, NULL,
&hThread); &hThread);
if(NT_SUCCESS(Status))
/* Return Cid and Handle */ {
DPRINT("All worked great!\n"); /* Wrap in SEH to protect against bad user-mode pointers */
if(NT_SUCCESS(Status)) { _SEH_TRY
{
_SEH_TRY { /* Return Cid and Handle */
if(ClientId) *ClientId = Thread->Cid;
if(ClientId != NULL) {
*ClientId = Thread->Cid;
}
*ThreadHandle = hThread; *ThreadHandle = hThread;
}
} _SEH_HANDLE { _SEH_HANDLE
{
Status = _SEH_GetExceptionCode(); Status = _SEH_GetExceptionCode();
}
} _SEH_END; _SEH_END;
} }
/* FIXME: SECURITY */ /* FIXME: SECURITY */
/* Dispatch thread */ /* Dispatch thread */
DPRINT("About to dispatch the thread: %x!\n", &Thread->Tcb);
OldIrql = KeAcquireDispatcherDatabaseLock (); OldIrql = KeAcquireDispatcherDatabaseLock ();
KiUnblockThread(&Thread->Tcb, NULL, 0); KiUnblockThread(&Thread->Tcb, NULL, 0);
KeReleaseDispatcherDatabaseLock(OldIrql); KeReleaseDispatcherDatabaseLock(OldIrql);
/* Return */ /* Return */
DPRINT("Returning\n");
return Status; return Status;
} }
@ -324,7 +365,7 @@ PspCreateThread(OUT PHANDLE ThreadHandle,
* @implemented * @implemented
*/ */
NTSTATUS NTSTATUS
STDCALL NTAPI
PsCreateSystemThread(PHANDLE ThreadHandle, PsCreateSystemThread(PHANDLE ThreadHandle,
ACCESS_MASK DesiredAccess, ACCESS_MASK DesiredAccess,
POBJECT_ATTRIBUTES ObjectAttributes, POBJECT_ATTRIBUTES ObjectAttributes,
@ -335,10 +376,11 @@ PsCreateSystemThread(PHANDLE ThreadHandle,
{ {
PEPROCESS TargetProcess = NULL; PEPROCESS TargetProcess = NULL;
HANDLE Handle = ProcessHandle; HANDLE Handle = ProcessHandle;
PAGED_CODE();
/* Check if we have a handle. If not, use the System Process */ /* Check if we have a handle. If not, use the System Process */
if (!ProcessHandle) { if (!ProcessHandle)
{
Handle = NULL; Handle = NULL;
TargetProcess = PsInitialSystemProcess; TargetProcess = PsInitialSystemProcess;
} }
@ -361,7 +403,7 @@ PsCreateSystemThread(PHANDLE ThreadHandle,
* @implemented * @implemented
*/ */
NTSTATUS NTSTATUS
STDCALL NTAPI
PsLookupThreadByThreadId(IN HANDLE ThreadId, PsLookupThreadByThreadId(IN HANDLE ThreadId,
OUT PETHREAD *Thread) OUT PETHREAD *Thread)
{ {
@ -369,7 +411,6 @@ PsLookupThreadByThreadId(IN HANDLE ThreadId,
PETHREAD FoundThread; PETHREAD FoundThread;
NTSTATUS Status = STATUS_INVALID_PARAMETER; NTSTATUS Status = STATUS_INVALID_PARAMETER;
PAGED_CODE(); PAGED_CODE();
KeEnterCriticalRegion(); KeEnterCriticalRegion();
/* Get the CID Handle Entry */ /* Get the CID Handle Entry */
@ -392,9 +433,8 @@ PsLookupThreadByThreadId(IN HANDLE ThreadId,
ExUnlockHandleTableEntry(PspCidTable, CidEntry); ExUnlockHandleTableEntry(PspCidTable, CidEntry);
} }
KeLeaveCriticalRegion();
/* Return to caller */ /* Return to caller */
KeLeaveCriticalRegion();
return Status; return Status;
} }
@ -402,7 +442,7 @@ PsLookupThreadByThreadId(IN HANDLE ThreadId,
* @implemented * @implemented
*/ */
HANDLE HANDLE
STDCALL NTAPI
PsGetCurrentThreadId(VOID) PsGetCurrentThreadId(VOID)
{ {
return(PsGetCurrentThread()->Cid.UniqueThread); return(PsGetCurrentThread()->Cid.UniqueThread);
@ -412,7 +452,7 @@ PsGetCurrentThreadId(VOID)
* @implemented * @implemented
*/ */
ULONG ULONG
STDCALL NTAPI
PsGetThreadFreezeCount(PETHREAD Thread) PsGetThreadFreezeCount(PETHREAD Thread)
{ {
return Thread->Tcb.FreezeCount; return Thread->Tcb.FreezeCount;
@ -422,7 +462,7 @@ PsGetThreadFreezeCount(PETHREAD Thread)
* @implemented * @implemented
*/ */
BOOLEAN BOOLEAN
STDCALL NTAPI
PsGetThreadHardErrorsAreDisabled(PETHREAD Thread) PsGetThreadHardErrorsAreDisabled(PETHREAD Thread)
{ {
return Thread->HardErrorsAreDisabled; return Thread->HardErrorsAreDisabled;
@ -432,7 +472,7 @@ PsGetThreadHardErrorsAreDisabled(PETHREAD Thread)
* @implemented * @implemented
*/ */
HANDLE HANDLE
STDCALL NTAPI
PsGetThreadId(PETHREAD Thread) PsGetThreadId(PETHREAD Thread)
{ {
return Thread->Cid.UniqueThread; return Thread->Cid.UniqueThread;
@ -442,7 +482,7 @@ PsGetThreadId(PETHREAD Thread)
* @implemented * @implemented
*/ */
PEPROCESS PEPROCESS
STDCALL NTAPI
PsGetThreadProcess(PETHREAD Thread) PsGetThreadProcess(PETHREAD Thread)
{ {
return Thread->ThreadsProcess; return Thread->ThreadsProcess;
@ -452,7 +492,7 @@ PsGetThreadProcess(PETHREAD Thread)
* @implemented * @implemented
*/ */
HANDLE HANDLE
STDCALL NTAPI
PsGetThreadProcessId(PETHREAD Thread) PsGetThreadProcessId(PETHREAD Thread)
{ {
return Thread->Cid.UniqueProcess; return Thread->Cid.UniqueProcess;
@ -462,7 +502,7 @@ PsGetThreadProcessId(PETHREAD Thread)
* @implemented * @implemented
*/ */
HANDLE HANDLE
STDCALL NTAPI
PsGetThreadSessionId(PETHREAD Thread) PsGetThreadSessionId(PETHREAD Thread)
{ {
return (HANDLE)Thread->ThreadsProcess->Session; return (HANDLE)Thread->ThreadsProcess->Session;
@ -472,7 +512,7 @@ PsGetThreadSessionId(PETHREAD Thread)
* @implemented * @implemented
*/ */
PTEB PTEB
STDCALL NTAPI
PsGetThreadTeb(PETHREAD Thread) PsGetThreadTeb(PETHREAD Thread)
{ {
return Thread->Tcb.Teb; return Thread->Tcb.Teb;
@ -482,7 +522,7 @@ PsGetThreadTeb(PETHREAD Thread)
* @implemented * @implemented
*/ */
PVOID PVOID
STDCALL NTAPI
PsGetThreadWin32Thread(PETHREAD Thread) PsGetThreadWin32Thread(PETHREAD Thread)
{ {
return Thread->Tcb.Win32Thread; return Thread->Tcb.Win32Thread;
@ -492,7 +532,7 @@ PsGetThreadWin32Thread(PETHREAD Thread)
* @implemented * @implemented
*/ */
KPROCESSOR_MODE KPROCESSOR_MODE
STDCALL NTAPI
PsGetCurrentThreadPreviousMode(VOID) PsGetCurrentThreadPreviousMode(VOID)
{ {
return (KPROCESSOR_MODE)PsGetCurrentThread()->Tcb.PreviousMode; return (KPROCESSOR_MODE)PsGetCurrentThread()->Tcb.PreviousMode;
@ -502,7 +542,7 @@ PsGetCurrentThreadPreviousMode(VOID)
* @implemented * @implemented
*/ */
PVOID PVOID
STDCALL NTAPI
PsGetCurrentThreadStackBase(VOID) PsGetCurrentThreadStackBase(VOID)
{ {
return PsGetCurrentThread()->Tcb.StackBase; return PsGetCurrentThread()->Tcb.StackBase;
@ -512,7 +552,7 @@ PsGetCurrentThreadStackBase(VOID)
* @implemented * @implemented
*/ */
PVOID PVOID
STDCALL NTAPI
PsGetCurrentThreadStackLimit(VOID) PsGetCurrentThreadStackLimit(VOID)
{ {
return (PVOID)PsGetCurrentThread()->Tcb.StackLimit; return (PVOID)PsGetCurrentThread()->Tcb.StackLimit;
@ -522,7 +562,7 @@ PsGetCurrentThreadStackLimit(VOID)
* @implemented * @implemented
*/ */
BOOLEAN BOOLEAN
STDCALL NTAPI
PsIsThreadTerminating(IN PETHREAD Thread) PsIsThreadTerminating(IN PETHREAD Thread)
{ {
return (Thread->Terminated ? TRUE : FALSE); return (Thread->Terminated ? TRUE : FALSE);
@ -532,7 +572,7 @@ PsIsThreadTerminating(IN PETHREAD Thread)
* @implemented * @implemented
*/ */
BOOLEAN BOOLEAN
STDCALL NTAPI
PsIsSystemThread(PETHREAD Thread) PsIsSystemThread(PETHREAD Thread)
{ {
return (Thread->SystemThread ? TRUE: FALSE); return (Thread->SystemThread ? TRUE: FALSE);
@ -542,7 +582,7 @@ PsIsSystemThread(PETHREAD Thread)
* @implemented * @implemented
*/ */
BOOLEAN BOOLEAN
STDCALL NTAPI
PsIsThreadImpersonating(PETHREAD Thread) PsIsThreadImpersonating(PETHREAD Thread)
{ {
return Thread->ActiveImpersonationInfo; return Thread->ActiveImpersonationInfo;
@ -552,7 +592,7 @@ PsIsThreadImpersonating(PETHREAD Thread)
* @implemented * @implemented
*/ */
VOID VOID
STDCALL NTAPI
PsSetThreadHardErrorsAreDisabled(PETHREAD Thread, PsSetThreadHardErrorsAreDisabled(PETHREAD Thread,
BOOLEAN HardErrorsAreDisabled) BOOLEAN HardErrorsAreDisabled)
{ {
@ -563,7 +603,7 @@ PsSetThreadHardErrorsAreDisabled(PETHREAD Thread,
* @implemented * @implemented
*/ */
VOID VOID
STDCALL NTAPI
PsSetThreadWin32Thread(PETHREAD Thread, PsSetThreadWin32Thread(PETHREAD Thread,
PVOID Win32Thread) PVOID Win32Thread)
{ {
@ -571,7 +611,7 @@ PsSetThreadWin32Thread(PETHREAD Thread,
} }
NTSTATUS NTSTATUS
STDCALL NTAPI
NtCreateThread(OUT PHANDLE ThreadHandle, NtCreateThread(OUT PHANDLE ThreadHandle,
IN ACCESS_MASK DesiredAccess, IN ACCESS_MASK DesiredAccess,
IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL, IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL,
@ -582,55 +622,53 @@ NtCreateThread(OUT PHANDLE ThreadHandle,
IN BOOLEAN CreateSuspended) IN BOOLEAN CreateSuspended)
{ {
INITIAL_TEB SafeInitialTeb; INITIAL_TEB SafeInitialTeb;
CONTEXT SafeContext;
NTSTATUS Status = STATUS_SUCCESS; NTSTATUS Status = STATUS_SUCCESS;
PAGED_CODE(); PAGED_CODE();
DPRINT("NtCreateThread(ThreadHandle %x, PCONTEXT %x)\n", /* Check if this was from user-mode */
ThreadHandle,ThreadContext); if(KeGetPreviousMode() != KernelMode)
{
if(KeGetPreviousMode() != KernelMode) { /* Make sure that we got a context */
if (!ThreadContext)
if (ThreadContext == NULL) { {
DPRINT1("No context for User-Mode Thread!!\n"); DPRINT1("No context for User-Mode Thread!!\n");
return STATUS_INVALID_PARAMETER; return STATUS_INVALID_PARAMETER;
} }
_SEH_TRY { /* Protect checks */
_SEH_TRY
{
/* Make sure the handle pointer we got is valid */
ProbeForWriteHandle(ThreadHandle); ProbeForWriteHandle(ThreadHandle);
if(ClientId != NULL) { /* Check if the caller wants a client id */
if(ClientId)
ProbeForWrite(ClientId, {
sizeof(CLIENT_ID), /* Make sure we can write to it */
sizeof(ULONG)); ProbeForWrite(ClientId, sizeof(CLIENT_ID), sizeof(ULONG));
} }
if(ThreadContext != NULL) { /* Make sure that the entire context is readable */
ProbeForRead(ThreadContext, sizeof(CONTEXT), sizeof(ULONG));
ProbeForRead(ThreadContext, /* Check the Initial TEB */
sizeof(CONTEXT), ProbeForRead(InitialTeb, sizeof(INITIAL_TEB), sizeof(ULONG));
sizeof(ULONG));
SafeContext = *ThreadContext;
ThreadContext = &SafeContext;
}
ProbeForRead(InitialTeb,
sizeof(INITIAL_TEB),
sizeof(ULONG));
SafeInitialTeb = *InitialTeb; SafeInitialTeb = *InitialTeb;
InitialTeb = &SafeInitialTeb; }
_SEH_HANDLE
} _SEH_HANDLE { {
Status = _SEH_GetExceptionCode(); Status = _SEH_GetExceptionCode();
}
_SEH_END;
} _SEH_END; /* Handle any failures in our SEH checks */
if (!NT_SUCCESS(Status)) return Status; if (!NT_SUCCESS(Status)) return Status;
} }
else
{
/* Use the Initial TEB as is */
SafeInitialTeb = *InitialTeb;
}
/* Call the shared function */ /* Call the shared function */
return PspCreateThread(ThreadHandle, return PspCreateThread(ThreadHandle,
@ -640,7 +678,7 @@ NtCreateThread(OUT PHANDLE ThreadHandle,
NULL, NULL,
ClientId, ClientId,
ThreadContext, ThreadContext,
InitialTeb, &SafeInitialTeb,
CreateSuspended, CreateSuspended,
NULL, NULL,
NULL); NULL);
@ -650,7 +688,7 @@ NtCreateThread(OUT PHANDLE ThreadHandle,
* @implemented * @implemented
*/ */
NTSTATUS NTSTATUS
STDCALL NTAPI
NtOpenThread(OUT PHANDLE ThreadHandle, NtOpenThread(OUT PHANDLE ThreadHandle,
IN ACCESS_MASK DesiredAccess, IN ACCESS_MASK DesiredAccess,
IN POBJECT_ATTRIBUTES ObjectAttributes, IN POBJECT_ATTRIBUTES ObjectAttributes,
@ -724,19 +762,14 @@ NtOpenThread(OUT PHANDLE ThreadHandle,
DesiredAccess, DesiredAccess,
NULL, NULL,
&hThread); &hThread);
if (!NT_SUCCESS(Status)) DPRINT1("Could not open object by name\n");
if (!NT_SUCCESS(Status))
{
DPRINT1("Could not open object by name\n");
} }
}
else if (ClientId != NULL) else if (ClientId != NULL)
{ {
/* Open by Thread ID */ /* Open by Thread ID */
if (ClientId->UniqueProcess) if (ClientId->UniqueProcess)
{ {
/* Get the Process */ /* Get the Process */
DPRINT("Opening by Process ID: %x\n", ClientId->UniqueProcess);
Status = PsLookupProcessThreadByCid(ClientId, Status = PsLookupProcessThreadByCid(ClientId,
NULL, NULL,
&Thread); &Thread);
@ -744,7 +777,6 @@ NtOpenThread(OUT PHANDLE ThreadHandle,
else else
{ {
/* Get the Process */ /* Get the Process */
DPRINT("Opening by Thread ID: %x\n", ClientId->UniqueThread);
Status = PsLookupThreadByThreadId(ClientId->UniqueThread, Status = PsLookupThreadByThreadId(ClientId->UniqueThread,
&Thread); &Thread);
} }
@ -777,11 +809,13 @@ NtOpenThread(OUT PHANDLE ThreadHandle,
return STATUS_INVALID_PARAMETER_MIX; return STATUS_INVALID_PARAMETER_MIX;
} }
/* Write back the handle */ /* Check for success */
if(NT_SUCCESS(Status)) if(NT_SUCCESS(Status))
{ {
/* Protect against bad user-mode pointers */
_SEH_TRY _SEH_TRY
{ {
/* Write back the handle */
*ThreadHandle = hThread; *ThreadHandle = hThread;
} }
_SEH_HANDLE _SEH_HANDLE
@ -796,7 +830,7 @@ NtOpenThread(OUT PHANDLE ThreadHandle,
} }
NTSTATUS NTSTATUS
STDCALL NTAPI
NtYieldExecution(VOID) NtYieldExecution(VOID)
{ {
KiDispatchThread(Ready); KiDispatchThread(Ready);
@ -804,7 +838,7 @@ NtYieldExecution(VOID)
} }
NTSTATUS NTSTATUS
STDCALL NTAPI
NtTestAlert(VOID) NtTestAlert(VOID)
{ {
/* Check and Alert Thread if needed */ /* Check and Alert Thread if needed */
@ -815,7 +849,7 @@ NtTestAlert(VOID)
* @implemented * @implemented
*/ */
KPROCESSOR_MODE KPROCESSOR_MODE
STDCALL NTAPI
ExGetPreviousMode (VOID) ExGetPreviousMode (VOID)
{ {
return (KPROCESSOR_MODE)PsGetCurrentThread()->Tcb.PreviousMode; return (KPROCESSOR_MODE)PsGetCurrentThread()->Tcb.PreviousMode;

View file

@ -16,8 +16,8 @@
/* GLOBALS ******************************************************************/ /* GLOBALS ******************************************************************/
static PKWIN32_PROCESS_CALLOUT PspWin32ProcessCallback = NULL; PKWIN32_PROCESS_CALLOUT PspW32ProcessCallout = NULL;
static PKWIN32_THREAD_CALLOUT PspWin32ThreadCallback = NULL; PKWIN32_THREAD_CALLOUT PspW32ThreadCallout = NULL;
extern PKWIN32_PARSEMETHOD_CALLOUT ExpWindowStationObjectParse; extern PKWIN32_PARSEMETHOD_CALLOUT ExpWindowStationObjectParse;
extern PKWIN32_DELETEMETHOD_CALLOUT ExpWindowStationObjectDelete; extern PKWIN32_DELETEMETHOD_CALLOUT ExpWindowStationObjectDelete;
extern PKWIN32_DELETEMETHOD_CALLOUT ExpDesktopObjectDelete; extern PKWIN32_DELETEMETHOD_CALLOUT ExpDesktopObjectDelete;
@ -53,8 +53,8 @@ VOID
STDCALL STDCALL
PsEstablishWin32Callouts(PWIN32_CALLOUTS_FPNS CalloutData) PsEstablishWin32Callouts(PWIN32_CALLOUTS_FPNS CalloutData)
{ {
PspWin32ProcessCallback = CalloutData->ProcessCallout; PspW32ProcessCallout = CalloutData->ProcessCallout;
PspWin32ThreadCallback = CalloutData->ThreadCallout; PspW32ThreadCallout = CalloutData->ThreadCallout;
ExpWindowStationObjectParse = CalloutData->WindowStationParseProcedure; ExpWindowStationObjectParse = CalloutData->WindowStationParseProcedure;
ExpWindowStationObjectDelete = CalloutData->WindowStationDeleteProcedure; ExpWindowStationObjectDelete = CalloutData->WindowStationDeleteProcedure;
ExpDesktopObjectDelete = CalloutData->DesktopDeleteProcedure; ExpDesktopObjectDelete = CalloutData->DesktopDeleteProcedure;
@ -79,7 +79,7 @@ PsConvertToGuiThread(VOID)
} }
/* Make sure win32k is here */ /* Make sure win32k is here */
if (!PspWin32ProcessCallback) if (!PspW32ProcessCallout)
{ {
DPRINT1("Danger: Win32K call attempted but Win32k not ready!\n"); DPRINT1("Danger: Win32K call attempted but Win32k not ready!\n");
return STATUS_ACCESS_DENIED; return STATUS_ACCESS_DENIED;
@ -122,7 +122,7 @@ PsConvertToGuiThread(VOID)
if (!Process->Win32Process) if (!Process->Win32Process)
{ {
/* Now tell win32k about us */ /* Now tell win32k about us */
Status = PspWin32ProcessCallback(Process, TRUE); Status = PspW32ProcessCallout(Process, TRUE);
if (!NT_SUCCESS(Status)) if (!NT_SUCCESS(Status))
{ {
DPRINT1("Danger: Win32k wasn't happy about us!\n"); DPRINT1("Danger: Win32k wasn't happy about us!\n");
@ -135,7 +135,7 @@ PsConvertToGuiThread(VOID)
ASSERT(Thread->Tcb.Win32Thread == 0); ASSERT(Thread->Tcb.Win32Thread == 0);
/* Tell Win32k about our thread */ /* Tell Win32k about our thread */
Status = PspWin32ThreadCallback(Thread, PsW32ThreadCalloutInitialize); Status = PspW32ThreadCallout(Thread, PsW32ThreadCalloutInitialize);
if (!NT_SUCCESS(Status)) if (!NT_SUCCESS(Status))
{ {
/* Revert our table */ /* Revert our table */
@ -147,39 +147,6 @@ PsConvertToGuiThread(VOID)
return Status; return Status;
} }
VOID
NTAPI
PsTerminateWin32Process (PEPROCESS Process)
{
if (Process->Win32Process == NULL)
return;
if (PspWin32ProcessCallback != NULL)
{
PspWin32ProcessCallback (Process, FALSE);
}
/* don't delete the W32PROCESS structure at this point, wait until the
EPROCESS structure is being freed */
}
VOID
NTAPI
PsTerminateWin32Thread (PETHREAD Thread)
{
if (Thread->Tcb.Win32Thread != NULL)
{
if (PspWin32ThreadCallback != NULL)
{
PspWin32ThreadCallback (Thread, PsW32ThreadCalloutExit);
}
/* don't delete the W32THREAD structure at this point, wait until the
ETHREAD structure is being freed */
}
}
NTSTATUS NTSTATUS
STDCALL STDCALL
NtW32Call(IN ULONG RoutineIndex, NtW32Call(IN ULONG RoutineIndex,

View file

@ -13,6 +13,22 @@
#include <ntoskrnl.h> #include <ntoskrnl.h>
#include <internal/debug.h> #include <internal/debug.h>
/* INTERNAL *****************************************************************/
BOOLEAN
NTAPI
SeDetailedAuditingWithToken(IN PTOKEN Token)
{
/* FIXME */
return FALSE;
}
VOID
NTAPI
SeAuditProcessExit(IN PEPROCESS Process)
{
/* FIXME */
}
/* FUNCTIONS ****************************************************************/ /* FUNCTIONS ****************************************************************/