mirror of
https://github.com/reactos/reactos.git
synced 2025-02-22 16:36:33 +00:00
- Implement ObReferenceProcessHandleTable and ObDereferenceProcessHandleTable and use them where appropriate to avoid race issues if the process is being killed meanwhile.
- Implement ObpReferenceProcessObjectByHandle and simplfy ObDuplicateObject. - Disable hard errors while closing handles, and protect against races. Also print our error message since it seems handles aren't being closed now (message displays leak count). - Honour DUPLICATE_CLOSE_SOURCE during failure paths in ObDuplicateObject, and catch race conditions. - Add some more sanity checks and speed up some internal referencing. svn path=/trunk/; revision=25408
This commit is contained in:
parent
6e3ccb6ffd
commit
70d1578a28
4 changed files with 289 additions and 47 deletions
|
@ -8,11 +8,6 @@
|
|||
// Do NOT ask when it will be fixed.
|
||||
// Failure to respect this will *ACHIEVE NOTHING*.
|
||||
//
|
||||
//
|
||||
// Ob:
|
||||
// - Add Directory Lock.
|
||||
// - Add Object Table Referencing.
|
||||
//
|
||||
// Ex:
|
||||
// - Fixup existing code that talks to Ke.
|
||||
// - Implement Generic Callback mechanism.
|
||||
|
|
|
@ -171,6 +171,18 @@ ObpCreateHandleTable(
|
|||
IN PEPROCESS Process
|
||||
);
|
||||
|
||||
PHANDLE_TABLE
|
||||
NTAPI
|
||||
ObReferenceProcessHandleTable(
|
||||
IN PEPROCESS Process
|
||||
);
|
||||
|
||||
VOID
|
||||
NTAPI
|
||||
ObDereferenceProcessHandleTable(
|
||||
IN PEPROCESS Process
|
||||
);
|
||||
|
||||
VOID
|
||||
NTAPI
|
||||
ObKillProcess(
|
||||
|
|
|
@ -23,6 +23,165 @@ PHANDLE_TABLE ObpKernelHandleTable = NULL;
|
|||
|
||||
/* PRIVATE FUNCTIONS *********************************************************/
|
||||
|
||||
PHANDLE_TABLE
|
||||
NTAPI
|
||||
ObReferenceProcessHandleTable(IN PEPROCESS Process)
|
||||
{
|
||||
PHANDLE_TABLE HandleTable = NULL;
|
||||
|
||||
/* Lock the process */
|
||||
if (ExAcquireRundownProtection(&Process->RundownProtect))
|
||||
{
|
||||
/* Get the handle table */
|
||||
HandleTable = Process->ObjectTable;
|
||||
if (!HandleTable)
|
||||
{
|
||||
/* No table, release the lock */
|
||||
ExReleaseRundownProtection(&Process->RundownProtect);
|
||||
}
|
||||
}
|
||||
|
||||
/* Return the handle table */
|
||||
return HandleTable;
|
||||
}
|
||||
|
||||
VOID
|
||||
NTAPI
|
||||
ObDereferenceProcessHandleTable(IN PEPROCESS Process)
|
||||
{
|
||||
/* Release the process lock */
|
||||
ExReleaseRundownProtection(&Process->RundownProtect);
|
||||
}
|
||||
|
||||
NTSTATUS
|
||||
NTAPI
|
||||
ObpReferenceProcessObjectByHandle(IN HANDLE Handle,
|
||||
IN PEPROCESS Process,
|
||||
IN PHANDLE_TABLE HandleTable,
|
||||
IN KPROCESSOR_MODE AccessMode,
|
||||
OUT PVOID *Object,
|
||||
OUT POBJECT_HANDLE_INFORMATION HandleInformation)
|
||||
{
|
||||
PHANDLE_TABLE_ENTRY HandleEntry;
|
||||
POBJECT_HEADER ObjectHeader;
|
||||
ACCESS_MASK GrantedAccess;
|
||||
ULONG Attributes;
|
||||
PEPROCESS CurrentProcess;
|
||||
PETHREAD CurrentThread;
|
||||
NTSTATUS Status;
|
||||
PAGED_CODE();
|
||||
|
||||
/* Assume failure */
|
||||
*Object = NULL;
|
||||
|
||||
/* Check if the caller wants the current process */
|
||||
if (Handle == NtCurrentProcess())
|
||||
{
|
||||
/* Get the current process */
|
||||
CurrentProcess = PsGetCurrentProcess();
|
||||
|
||||
/* Check if the caller wanted handle information */
|
||||
if (HandleInformation)
|
||||
{
|
||||
/* Return it */
|
||||
HandleInformation->HandleAttributes = 0;
|
||||
HandleInformation->GrantedAccess = Process->GrantedAccess;
|
||||
}
|
||||
|
||||
/* Reference ourselves */
|
||||
ObjectHeader = OBJECT_TO_OBJECT_HEADER(CurrentProcess);
|
||||
InterlockedExchangeAdd(&ObjectHeader->PointerCount, 1);
|
||||
|
||||
/* Return the pointer */
|
||||
*Object = CurrentProcess;
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
/* Check if the caller wants the current thread */
|
||||
if (Handle == NtCurrentThread())
|
||||
{
|
||||
/* Get the current thread */
|
||||
CurrentThread = PsGetCurrentThread();
|
||||
|
||||
/* Check if the caller wanted handle information */
|
||||
if (HandleInformation)
|
||||
{
|
||||
/* Return it */
|
||||
HandleInformation->HandleAttributes = 0;
|
||||
HandleInformation->GrantedAccess = CurrentThread->GrantedAccess;
|
||||
}
|
||||
|
||||
/* Reference ourselves */
|
||||
ObjectHeader = OBJECT_TO_OBJECT_HEADER(CurrentThread);
|
||||
InterlockedExchangeAdd(&ObjectHeader->PointerCount, 1);
|
||||
|
||||
/* Return the pointer */
|
||||
*Object = CurrentThread;
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
/* Check if this is a kernel handle */
|
||||
if (ObIsKernelHandle(Handle, AccessMode))
|
||||
{
|
||||
/* Use the kernel handle table and get the actual handle value */
|
||||
Handle = ObKernelHandleToHandle(Handle);
|
||||
HandleTable = ObpKernelHandleTable;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Otherwise use this process's handle table */
|
||||
HandleTable = PsGetCurrentProcess()->ObjectTable;
|
||||
}
|
||||
|
||||
/* Enter a critical region while we touch the handle table */
|
||||
ASSERT(HandleTable != NULL);
|
||||
KeEnterCriticalRegion();
|
||||
|
||||
/* Get the handle entry */
|
||||
HandleEntry = ExMapHandleToPointer(HandleTable, Handle);
|
||||
if (HandleEntry)
|
||||
{
|
||||
/* Get the object header and validate the type*/
|
||||
ObjectHeader = EX_HTE_TO_HDR(HandleEntry);
|
||||
|
||||
/* Get the granted access and validate it */
|
||||
GrantedAccess = HandleEntry->GrantedAccess;
|
||||
|
||||
/* Mask out the internal attributes */
|
||||
Attributes = HandleEntry->ObAttributes &
|
||||
(EX_HANDLE_ENTRY_PROTECTFROMCLOSE |
|
||||
EX_HANDLE_ENTRY_INHERITABLE |
|
||||
EX_HANDLE_ENTRY_AUDITONCLOSE);
|
||||
|
||||
/* Fill out the information */
|
||||
HandleInformation->HandleAttributes = Attributes;
|
||||
HandleInformation->GrantedAccess = GrantedAccess;
|
||||
|
||||
/* Return the pointer */
|
||||
*Object = &ObjectHeader->Body;
|
||||
|
||||
/* Add a reference */
|
||||
InterlockedExchangeAdd(&ObjectHeader->PointerCount, 1);
|
||||
|
||||
/* Unlock the handle */
|
||||
ExUnlockHandleTableEntry(HandleTable, HandleEntry);
|
||||
KeLeaveCriticalRegion();
|
||||
|
||||
/* Return success */
|
||||
ASSERT(*Object != NULL);
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Invalid handle */
|
||||
Status = STATUS_INVALID_HANDLE;
|
||||
}
|
||||
|
||||
/* Return failure status */
|
||||
KeLeaveCriticalRegion();
|
||||
return Status;
|
||||
}
|
||||
|
||||
BOOLEAN
|
||||
NTAPI
|
||||
ObpEnumFindHandleProcedure(IN PHANDLE_TABLE_ENTRY HandleEntry,
|
||||
|
@ -1741,11 +1900,15 @@ ObpCreateHandleTable(IN PEPROCESS Parent,
|
|||
/* Check if we have a parent */
|
||||
if (Parent)
|
||||
{
|
||||
/* Get the parent's table */
|
||||
HandleTable = ObReferenceProcessHandleTable(Parent);
|
||||
if (!HandleTable) return STATUS_PROCESS_IS_TERMINATING;
|
||||
|
||||
/* Duplicate the parent's */
|
||||
HandleTable = ExDupHandleTable(Process,
|
||||
ObpDuplicateHandleCallback,
|
||||
NULL,
|
||||
Parent->ObjectTable);
|
||||
HandleTable);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -1753,11 +1916,14 @@ ObpCreateHandleTable(IN PEPROCESS Parent,
|
|||
HandleTable = ExCreateHandleTable(Process);
|
||||
}
|
||||
|
||||
/* Now write it and make sure we got one */
|
||||
/* Now write it */
|
||||
Process->ObjectTable = HandleTable;
|
||||
if (!HandleTable) return STATUS_INSUFFICIENT_RESOURCES;
|
||||
|
||||
/* If we got here then the table was created OK */
|
||||
/* Dereference the parent's handle table if we have one */
|
||||
if (Parent) ObDereferenceProcessHandleTable(Parent);
|
||||
|
||||
/* Fail or succeed depending on whether we got a handle table or not */
|
||||
if (!HandleTable) return STATUS_INSUFFICIENT_RESOURCES;
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
|
@ -1778,10 +1944,21 @@ VOID
|
|||
NTAPI
|
||||
ObKillProcess(IN PEPROCESS Process)
|
||||
{
|
||||
PHANDLE_TABLE HandleTable = Process->ObjectTable;
|
||||
PHANDLE_TABLE HandleTable;
|
||||
OBP_CLOSE_HANDLE_CONTEXT Context;
|
||||
BOOLEAN HardErrors;
|
||||
PAGED_CODE();
|
||||
|
||||
/* Wait for process rundown */
|
||||
ExWaitForRundownProtectionRelease(&Process->RundownProtect);
|
||||
|
||||
/* Get the object table */
|
||||
HandleTable = Process->ObjectTable;
|
||||
if (!HandleTable) return;
|
||||
|
||||
/* Disable hard errors while we close handles */
|
||||
HardErrors = IoSetThreadHardErrorMode(FALSE);
|
||||
|
||||
/* Enter a critical region */
|
||||
KeEnterCriticalRegion();
|
||||
|
||||
|
@ -1793,13 +1970,20 @@ ObKillProcess(IN PEPROCESS Process)
|
|||
ExSweepHandleTable(HandleTable,
|
||||
ObpCloseHandleCallback,
|
||||
&Context);
|
||||
if (HandleTable->HandleCount != 0)
|
||||
{
|
||||
DPRINT1("FIXME: %d handles remain!\n", HandleTable->HandleCount);
|
||||
}
|
||||
|
||||
/* Destroy the table and leave the critical region */
|
||||
ExDestroyHandleTable(HandleTable);
|
||||
/* Leave the critical region */
|
||||
KeLeaveCriticalRegion();
|
||||
|
||||
/* Clear the object table */
|
||||
/* Re-enable hard errors */
|
||||
IoSetThreadHardErrorMode(HardErrors);
|
||||
|
||||
/* Destroy the object table */
|
||||
Process->ObjectTable = NULL;
|
||||
ExDestroyHandleTable(HandleTable);
|
||||
}
|
||||
|
||||
NTSTATUS
|
||||
|
@ -1820,12 +2004,12 @@ ObDuplicateObject(IN PEPROCESS SourceProcess,
|
|||
POBJECT_TYPE ObjectType;
|
||||
HANDLE NewHandle;
|
||||
KAPC_STATE ApcState;
|
||||
NTSTATUS Status = STATUS_SUCCESS;
|
||||
NTSTATUS Status;
|
||||
ACCESS_MASK TargetAccess, SourceAccess;
|
||||
ACCESS_STATE AccessState;
|
||||
PACCESS_STATE PassedAccessState = NULL;
|
||||
AUX_DATA AuxData;
|
||||
PHANDLE_TABLE HandleTable = NULL;
|
||||
PHANDLE_TABLE HandleTable;
|
||||
OBJECT_HANDLE_INFORMATION HandleInformation;
|
||||
PAGED_CODE();
|
||||
OBTRACE(OB_HANDLE_DEBUG,
|
||||
|
@ -1835,32 +2019,77 @@ ObDuplicateObject(IN PEPROCESS SourceProcess,
|
|||
SourceProcess,
|
||||
TargetProcess);
|
||||
|
||||
/* Check if we're not in the source process */
|
||||
if (SourceProcess != PsGetCurrentProcess())
|
||||
/* Check if we're not duplicating the same access */
|
||||
if (!(Options & DUPLICATE_SAME_ACCESS))
|
||||
{
|
||||
/* Attach to it */
|
||||
KeStackAttachProcess(&SourceProcess->Pcb, &ApcState);
|
||||
AttachedToProcess = TRUE;
|
||||
/* Validate the desired access */
|
||||
Status = STATUS_SUCCESS; //ObpValidateDesiredAccess(DesiredAccess);
|
||||
if (!NT_SUCCESS(Status)) return Status;
|
||||
}
|
||||
|
||||
/* Now reference the source handle */
|
||||
Status = ObReferenceObjectByHandle(SourceHandle,
|
||||
0,
|
||||
NULL,
|
||||
PreviousMode,
|
||||
(PVOID*)&SourceObject,
|
||||
&HandleInformation);
|
||||
/* Reference the object table */
|
||||
HandleTable = ObReferenceProcessHandleTable(SourceProcess);
|
||||
if (!HandleTable) return STATUS_PROCESS_IS_TERMINATING;
|
||||
|
||||
/* Check if we were attached */
|
||||
if (AttachedToProcess)
|
||||
/* Reference the process object */
|
||||
Status = ObpReferenceProcessObjectByHandle(SourceHandle,
|
||||
0,
|
||||
HandleTable,
|
||||
PreviousMode,
|
||||
&SourceObject,
|
||||
&HandleInformation);
|
||||
if (!NT_SUCCESS(Status))
|
||||
{
|
||||
/* We can safely detach now */
|
||||
KeUnstackDetachProcess(&ApcState);
|
||||
AttachedToProcess = FALSE;
|
||||
/* Fail */
|
||||
ObDereferenceProcessHandleTable(SourceProcess);
|
||||
return Status;
|
||||
}
|
||||
|
||||
/* Fail if we couldn't reference it */
|
||||
if (!NT_SUCCESS(Status)) return Status;
|
||||
/* Check if there's no target process */
|
||||
if (!TargetProcess)
|
||||
{
|
||||
/* Check if the caller wanted actual duplication */
|
||||
if (!(Options & DUPLICATE_CLOSE_SOURCE))
|
||||
{
|
||||
/* Invalid request */
|
||||
Status = STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Otherwise, do the attach */
|
||||
KeStackAttachProcess(&SourceProcess->Pcb, &ApcState);
|
||||
|
||||
/* Close the handle and detach */
|
||||
NtClose(SourceHandle);
|
||||
KeUnstackDetachProcess(&ApcState);
|
||||
}
|
||||
|
||||
/* Return */
|
||||
ObDereferenceProcessHandleTable(SourceProcess);
|
||||
ObDereferenceObject(SourceObject);
|
||||
return Status;
|
||||
}
|
||||
|
||||
/* Get the target handle table */
|
||||
HandleTable = ObReferenceProcessHandleTable(TargetProcess);
|
||||
if (!HandleTable)
|
||||
{
|
||||
/* Check if the caller wanted us to close the handle */
|
||||
if (Options & DUPLICATE_CLOSE_SOURCE)
|
||||
{
|
||||
/* Do the attach */
|
||||
KeStackAttachProcess(&SourceProcess->Pcb, &ApcState);
|
||||
|
||||
/* Close the handle and detach */
|
||||
NtClose(SourceHandle);
|
||||
KeUnstackDetachProcess(&ApcState);
|
||||
}
|
||||
|
||||
/* Return */
|
||||
ObDereferenceProcessHandleTable(SourceProcess);
|
||||
ObDereferenceObject(SourceObject);
|
||||
return STATUS_PROCESS_IS_TERMINATING;
|
||||
}
|
||||
|
||||
/* Get the source access */
|
||||
SourceAccess = HandleInformation.GrantedAccess;
|
||||
|
@ -1898,7 +2127,8 @@ ObDuplicateObject(IN PEPROCESS SourceProcess,
|
|||
if (DesiredAccess & GENERIC_ACCESS)
|
||||
{
|
||||
/* Map it */
|
||||
RtlMapGenericMask(&DesiredAccess, &ObjectType->TypeInfo.GenericMapping);
|
||||
RtlMapGenericMask(&DesiredAccess,
|
||||
&ObjectType->TypeInfo.GenericMapping);
|
||||
}
|
||||
|
||||
/* Set the target access */
|
||||
|
@ -1940,9 +2170,6 @@ ObDuplicateObject(IN PEPROCESS SourceProcess,
|
|||
HandleAttributes,
|
||||
PsGetCurrentProcess(),
|
||||
ObDuplicateHandle);
|
||||
|
||||
/* Set the handle table, now that we know this handle was added */
|
||||
HandleTable = PsGetCurrentProcess()->ObjectTable;
|
||||
}
|
||||
|
||||
/* Check if we were attached */
|
||||
|
@ -1990,6 +2217,10 @@ ObDuplicateObject(IN PEPROCESS SourceProcess,
|
|||
/* Return the handle */
|
||||
if (TargetHandle) *TargetHandle = NewHandle;
|
||||
|
||||
/* Dereference handle tables */
|
||||
ObDereferenceProcessHandleTable(SourceProcess);
|
||||
ObDereferenceProcessHandleTable(TargetProcess);
|
||||
|
||||
/* Return status */
|
||||
OBTRACE(OB_HANDLE_DEBUG,
|
||||
"%s - Duplicated handle: %lx for %p into %p. Source: %p HC PC %lx %lx\n",
|
||||
|
|
|
@ -478,8 +478,8 @@ ObReferenceObjectByHandle(IN HANDLE Handle,
|
|||
NTSTATUS Status;
|
||||
PAGED_CODE();
|
||||
|
||||
/* Fail immediately if the handle is NULL */
|
||||
if (!Handle) return STATUS_INVALID_HANDLE;
|
||||
/* Assume failure */
|
||||
*Object = NULL;
|
||||
|
||||
/* Check if the caller wants the current process */
|
||||
if ((Handle == NtCurrentProcess()) &&
|
||||
|
@ -488,9 +488,6 @@ ObReferenceObjectByHandle(IN HANDLE Handle,
|
|||
/* Get the current process */
|
||||
CurrentProcess = PsGetCurrentProcess();
|
||||
|
||||
/* Reference ourselves */
|
||||
ObReferenceObject(CurrentProcess);
|
||||
|
||||
/* Check if the caller wanted handle information */
|
||||
if (HandleInformation)
|
||||
{
|
||||
|
@ -499,6 +496,10 @@ ObReferenceObjectByHandle(IN HANDLE Handle,
|
|||
HandleInformation->GrantedAccess = PROCESS_ALL_ACCESS;
|
||||
}
|
||||
|
||||
/* Reference ourselves */
|
||||
ObjectHeader = OBJECT_TO_OBJECT_HEADER(CurrentProcess);
|
||||
InterlockedExchangeAdd(&ObjectHeader->PointerCount, 1);
|
||||
|
||||
/* Return the pointer */
|
||||
*Object = CurrentProcess;
|
||||
return STATUS_SUCCESS;
|
||||
|
@ -516,9 +517,6 @@ ObReferenceObjectByHandle(IN HANDLE Handle,
|
|||
/* Get the current thread */
|
||||
CurrentThread = PsGetCurrentThread();
|
||||
|
||||
/* Reference ourselves */
|
||||
ObReferenceObject(CurrentThread);
|
||||
|
||||
/* Check if the caller wanted handle information */
|
||||
if (HandleInformation)
|
||||
{
|
||||
|
@ -527,6 +525,10 @@ ObReferenceObjectByHandle(IN HANDLE Handle,
|
|||
HandleInformation->GrantedAccess = THREAD_ALL_ACCESS;
|
||||
}
|
||||
|
||||
/* Reference ourselves */
|
||||
ObjectHeader = OBJECT_TO_OBJECT_HEADER(CurrentThread);
|
||||
InterlockedExchangeAdd(&ObjectHeader->PointerCount, 1);
|
||||
|
||||
/* Return the pointer */
|
||||
*Object = CurrentThread;
|
||||
return STATUS_SUCCESS;
|
||||
|
@ -551,6 +553,7 @@ ObReferenceObjectByHandle(IN HANDLE Handle,
|
|||
}
|
||||
|
||||
/* Enter a critical region while we touch the handle table */
|
||||
ASSERT(HandleTable != NULL);
|
||||
KeEnterCriticalRegion();
|
||||
|
||||
/* Get the handle entry */
|
||||
|
@ -588,9 +591,10 @@ ObReferenceObjectByHandle(IN HANDLE Handle,
|
|||
|
||||
/* Unlock the handle */
|
||||
ExUnlockHandleTableEntry(HandleTable, HandleEntry);
|
||||
KeLeaveCriticalRegion();
|
||||
|
||||
/* Return success */
|
||||
KeLeaveCriticalRegion();
|
||||
ASSERT(*Object != NULL);
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
else
|
||||
|
|
Loading…
Reference in a new issue