You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2947 lines
76 KiB
2947 lines
76 KiB
/*++
|
|
|
|
Copyright (c) 1989 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
obref.c
|
|
|
|
Abstract:
|
|
|
|
Object open API
|
|
|
|
Author:
|
|
|
|
Steve Wood (stevewo) 31-Mar-1989
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include "obp.h"
|
|
|
|
#undef ObReferenceObjectByHandle
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text(INIT,ObpInitStackTrace)
|
|
#pragma alloc_text(PAGE,ObOpenObjectByName)
|
|
#pragma alloc_text(PAGE,ObOpenObjectByPointer)
|
|
#pragma alloc_text(PAGE,ObReferenceObjectByHandle)
|
|
#pragma alloc_text(PAGE,ObpReferenceProcessObjectByHandle)
|
|
#pragma alloc_text(PAGE,ObReferenceObjectByName)
|
|
#pragma alloc_text(PAGE,ObReferenceFileObjectForWrite)
|
|
#pragma alloc_text(PAGE,ObpProcessRemoveObjectQueue)
|
|
#pragma alloc_text(PAGE,ObpRemoveObjectRoutine)
|
|
#pragma alloc_text(PAGE,ObpDeleteNameCheck)
|
|
#pragma alloc_text(PAGE,ObpAuditObjectAccess)
|
|
#pragma alloc_text(PAGE,ObIsObjectDeletionInline)
|
|
#pragma alloc_text(PAGE,ObAuditObjectAccess)
|
|
#endif
|
|
|
|
//
|
|
//
|
|
// Stack Trace code
|
|
//
|
|
//
|
|
ULONG ObpTraceNoDeregister = 0;
|
|
WCHAR ObpTracePoolTagsBuffer[128] = { 0 };
|
|
ULONG ObpTracePoolTagsLength = sizeof(ObpTracePoolTagsBuffer);
|
|
ULONG ObpTracePoolTags[16];
|
|
BOOLEAN ObpTraceEnabled = FALSE;
|
|
|
|
#ifdef POOL_TAGGING
|
|
|
|
#define OBTRACE_OBJECTBUCKETS 401 // # of buckets in the object hash table (a prime)
|
|
#define OBTRACE_STACKS 14747 // max # of unique stack traces (a prime)
|
|
#define OBTRACE_STACKSPEROBJECT 32768 // max number of object references
|
|
#define OBTRACE_TRACEDEPTH 16 // depth of stack traces
|
|
|
|
//
|
|
// The constants below are used by the !obtrace debugger extension
|
|
//
|
|
|
|
const ObpObjectBuckets = OBTRACE_OBJECTBUCKETS;
|
|
const ObpMaxStacks = OBTRACE_STACKS;
|
|
const ObpStacksPerObject = OBTRACE_STACKSPEROBJECT;
|
|
const ObpTraceDepth = OBTRACE_TRACEDEPTH;
|
|
|
|
//
|
|
// Object reference stacktrace structure
|
|
//
|
|
|
|
typedef struct _OBJECT_REF_TRACE {
|
|
PVOID StackTrace[OBTRACE_TRACEDEPTH];
|
|
} OBJECT_REF_TRACE, *POBJECT_REF_TRACE;
|
|
|
|
|
|
typedef struct _OBJECT_REF_STACK_INFO {
|
|
USHORT Sequence;
|
|
USHORT Index;
|
|
} OBJECT_REF_STACK_INFO, *POBJECT_REF_STACK_INFO;
|
|
|
|
//
|
|
// Object reference info structure
|
|
//
|
|
|
|
typedef struct _OBJECT_REF_INFO {
|
|
POBJECT_HEADER ObjectHeader;
|
|
PVOID NextRef;
|
|
UCHAR ImageFileName[16];
|
|
ULONG NextPos;
|
|
OBJECT_REF_STACK_INFO StackInfo[OBTRACE_STACKSPEROBJECT];
|
|
} OBJECT_REF_INFO, *POBJECT_REF_INFO;
|
|
|
|
//
|
|
// The stack hash table, and the object hash table
|
|
//
|
|
|
|
OBJECT_REF_TRACE *ObpStackTable = NULL;
|
|
POBJECT_REF_INFO *ObpObjectTable = NULL;
|
|
|
|
//
|
|
// Some statistics
|
|
//
|
|
|
|
ULONG ObpNumStackTraces;
|
|
ULONG ObpNumTracedObjects;
|
|
ULONG ObpStackSequence;
|
|
|
|
//
|
|
// Spin lock for object tracing
|
|
//
|
|
|
|
KSPIN_LOCK ObpStackTraceLock;
|
|
|
|
#define OBTRACE_HASHOBJECT(x) (((((ULONG)(ULONG_PTR)(&(x)->Body)) >> 4) & 0xfffff) % OBTRACE_OBJECTBUCKETS)
|
|
|
|
POBJECT_REF_INFO
|
|
ObpGetObjectRefInfo (
|
|
POBJECT_HEADER ObjectHeader
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine returns a pointer to the OBJECT_REF_INFO for the
|
|
specified object, or NULL, if it doesn't exist.
|
|
|
|
Arguments:
|
|
|
|
ObjectHeader - Pointer to the object header
|
|
|
|
Return Value:
|
|
|
|
The pointer to a OBJECT_REF_INFO object for the specified object.
|
|
|
|
--*/
|
|
|
|
{
|
|
POBJECT_REF_INFO ObjectRefInfo = ObpObjectTable[OBTRACE_HASHOBJECT(ObjectHeader)];
|
|
|
|
while (ObjectRefInfo && ObjectRefInfo->ObjectHeader != ObjectHeader) {
|
|
|
|
ObjectRefInfo = (POBJECT_REF_INFO)ObjectRefInfo->NextRef;
|
|
}
|
|
|
|
return ObjectRefInfo;
|
|
}
|
|
|
|
|
|
ULONG
|
|
ObpGetTraceIndex (
|
|
POBJECT_REF_TRACE Trace
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine returns the index of 'Trace' in the stack
|
|
trace hash table (ObpStackTable). If Trace does not exist
|
|
in the table, it is added, and the new index is returned.
|
|
|
|
Arguments:
|
|
|
|
Trace - Pointer to a stack trace to find in the table
|
|
|
|
Return Value:
|
|
|
|
The index of Trace in ObpStackTable
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG_PTR Value = 0;
|
|
ULONG Index;
|
|
PUSHORT Key;
|
|
ULONG Hash;
|
|
|
|
//
|
|
// Determine the hash value for the stack trace
|
|
//
|
|
|
|
Key = (PUSHORT)Trace->StackTrace;
|
|
for (Index = 0; Index < sizeof(Trace->StackTrace) / sizeof(*Key); Index += 2) {
|
|
|
|
Value += Key[Index] ^ Key[Index + 1];
|
|
}
|
|
|
|
Hash = ((ULONG)Value) % OBTRACE_STACKS;
|
|
|
|
//
|
|
// Look up the trace at that index (linear probing)
|
|
//
|
|
|
|
while (ObpStackTable[Hash].StackTrace[0] != NULL &&
|
|
RtlCompareMemory(&ObpStackTable[Hash], Trace, sizeof(OBJECT_REF_TRACE)) != sizeof(OBJECT_REF_TRACE)) {
|
|
|
|
Hash = (Hash + 1) % OBTRACE_STACKS;
|
|
if (Hash == ((ULONG)Value) % OBTRACE_STACKS) {
|
|
|
|
return OBTRACE_STACKS;
|
|
}
|
|
}
|
|
|
|
//
|
|
// If the trace doesn't already exist in the table, add it.
|
|
//
|
|
|
|
if (ObpStackTable[Hash].StackTrace[0] == NULL) {
|
|
|
|
RtlCopyMemory(&ObpStackTable[Hash], Trace, sizeof(OBJECT_REF_TRACE));
|
|
ObpNumStackTraces++;
|
|
}
|
|
|
|
return Hash;
|
|
}
|
|
|
|
|
|
VOID
|
|
ObpInitStackTrace()
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Initialize the ob ref/deref stack-tracing code.
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG i,j;
|
|
|
|
KeInitializeSpinLock( &ObpStackTraceLock );
|
|
RtlZeroMemory(ObpTracePoolTags, sizeof(ObpTracePoolTags));
|
|
ObpStackSequence = 0;
|
|
ObpNumStackTraces = 0;
|
|
ObpNumTracedObjects = 0;
|
|
ObpTraceEnabled = FALSE;
|
|
|
|
//
|
|
// Loop through the ObpTracePoolTagsBuffer string, and convert it to
|
|
// an array of pool tags.
|
|
//
|
|
// The string should be in the form "Tag1;Tag2;Tag3; ..."
|
|
//
|
|
|
|
for (i = 0; i < sizeof(ObpTracePoolTags) / sizeof(ULONG); i++) {
|
|
for (j = 0; j < 4; j++) {
|
|
ObpTracePoolTags[i] = (ObpTracePoolTags[i] << 8) | ObpTracePoolTagsBuffer[5*i+(3-j)];
|
|
}
|
|
}
|
|
|
|
//
|
|
// If object tracing was turned on via the registry key, then we
|
|
// need to allocate memory for the tables. If the memory allocations
|
|
// fail, we turn off tracing by clearing the pool tag array.
|
|
//
|
|
|
|
if (ObpTracePoolTags[0] != 0) {
|
|
|
|
ObpStackTable = ExAllocatePoolWithTag( NonPagedPool,
|
|
OBTRACE_STACKS * sizeof(OBJECT_REF_TRACE),
|
|
'TSbO' );
|
|
|
|
if (ObpStackTable != NULL) {
|
|
|
|
RtlZeroMemory(ObpStackTable, OBTRACE_STACKS * sizeof(OBJECT_REF_TRACE));
|
|
|
|
ObpObjectTable = ExAllocatePoolWithTag( NonPagedPool,
|
|
OBTRACE_OBJECTBUCKETS * sizeof(POBJECT_REF_INFO),
|
|
'TSbO' );
|
|
if (ObpObjectTable != NULL) {
|
|
|
|
RtlZeroMemory(ObpObjectTable, OBTRACE_OBJECTBUCKETS * sizeof(POBJECT_REF_INFO));
|
|
ObpTraceEnabled = TRUE;
|
|
|
|
} else {
|
|
|
|
ExFreePoolWithTag( ObpStackTable, 'TSbO' );
|
|
ObpStackTable = NULL;
|
|
RtlZeroMemory(ObpTracePoolTags, sizeof(ObpTracePoolTags));
|
|
}
|
|
|
|
} else {
|
|
|
|
RtlZeroMemory(ObpTracePoolTags, sizeof(ObpTracePoolTags));
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
ObpIsObjectTraced (
|
|
POBJECT_HEADER ObjectHeader
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine determines if an object should have its references
|
|
and dereferences traced.
|
|
|
|
Arguments:
|
|
|
|
ObjectHeader - The object to check
|
|
|
|
Return Value:
|
|
|
|
TRUE, if the object should be traced, and FALSE, otherwise
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG i;
|
|
|
|
if (ObjectHeader != NULL) {
|
|
|
|
//
|
|
// Loop through the ObpTracePoolTags array, and return true if
|
|
// the object type key matches one of them.
|
|
//
|
|
|
|
for (i = 0; i < sizeof(ObpTracePoolTags) / sizeof(ULONG); i++) {
|
|
|
|
if (ObjectHeader->Type->Key == ObpTracePoolTags[i]) {
|
|
|
|
return TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
VOID
|
|
ObpRegisterObject (
|
|
POBJECT_HEADER ObjectHeader
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called once for each object that is created.
|
|
It determines if the object should be traced, and if so, adds
|
|
it to the hash table.
|
|
|
|
Arguments:
|
|
|
|
ObjectHeader - The object to register
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
|
|
{
|
|
KIRQL OldIrql;
|
|
POBJECT_REF_INFO ObjectRefInfo = NULL;
|
|
|
|
//
|
|
// Are we tracing this object?
|
|
//
|
|
|
|
if (ObpIsObjectTraced( ObjectHeader )) {
|
|
|
|
ExAcquireSpinLock( &ObpStackTraceLock, &OldIrql );
|
|
|
|
ObjectRefInfo = ObpGetObjectRefInfo(ObjectHeader);
|
|
|
|
if (ObjectRefInfo == NULL) {
|
|
|
|
//
|
|
// Allocate a new OBJECT_REF_INFO for the object
|
|
//
|
|
|
|
ObjectRefInfo = ExAllocatePoolWithTag( NonPagedPool,
|
|
sizeof(OBJECT_REF_INFO),
|
|
'TSbO' );
|
|
|
|
if (ObjectRefInfo != NULL) {
|
|
|
|
//
|
|
// Place the object into the hash table (at the beginning of the bucket)
|
|
//
|
|
|
|
ObjectRefInfo->NextRef = ObpObjectTable[OBTRACE_HASHOBJECT(ObjectHeader)];
|
|
ObpObjectTable[OBTRACE_HASHOBJECT(ObjectHeader)] = ObjectRefInfo;
|
|
|
|
} else {
|
|
|
|
DbgPrint( "ObpRegisterObject - ExAllocatePoolWithTag failed.\n" );
|
|
}
|
|
}
|
|
|
|
if (ObjectRefInfo != NULL) {
|
|
|
|
ObpNumTracedObjects++;
|
|
|
|
//
|
|
// Initialize the OBJECT_REF_INFO
|
|
//
|
|
|
|
ObjectRefInfo->ObjectHeader = ObjectHeader;
|
|
RtlCopyMemory( ObjectRefInfo->ImageFileName,
|
|
PsGetCurrentProcess()->ImageFileName,
|
|
sizeof(ObjectRefInfo->ImageFileName) );
|
|
ObjectRefInfo->NextPos = 0;
|
|
RtlZeroMemory( ObjectRefInfo->StackInfo,
|
|
sizeof(ObjectRefInfo->StackInfo) );
|
|
}
|
|
|
|
ExReleaseSpinLock( &ObpStackTraceLock, OldIrql );
|
|
}
|
|
}
|
|
|
|
|
|
VOID
|
|
ObpDeregisterObject (
|
|
POBJECT_HEADER ObjectHeader
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called once for each object that is deleted.
|
|
It determines if the object is traced, and if so, deletes
|
|
it from the hash table.
|
|
|
|
Arguments:
|
|
|
|
ObjectHeader - The object to deregister
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
|
|
{
|
|
KIRQL OldIrql;
|
|
POBJECT_REF_INFO ObjectRefInfo = NULL;
|
|
|
|
//
|
|
// Are we tracing this object?
|
|
//
|
|
|
|
if (ObpIsObjectTraced( ObjectHeader )) {
|
|
|
|
ExAcquireSpinLock( &ObpStackTraceLock, &OldIrql );
|
|
|
|
ObjectRefInfo = ObpObjectTable[OBTRACE_HASHOBJECT(ObjectHeader)];
|
|
|
|
if (ObjectRefInfo != NULL) {
|
|
|
|
//
|
|
// Remove the entry from the list
|
|
//
|
|
|
|
if (ObjectRefInfo->ObjectHeader == ObjectHeader) {
|
|
|
|
ObpObjectTable[OBTRACE_HASHOBJECT(ObjectHeader)] = ObjectRefInfo->NextRef;
|
|
|
|
} else {
|
|
|
|
POBJECT_REF_INFO PrevObjectRefInfo;
|
|
do {
|
|
PrevObjectRefInfo = ObjectRefInfo;
|
|
ObjectRefInfo = ObjectRefInfo->NextRef;
|
|
} while (ObjectRefInfo && (ObjectRefInfo->ObjectHeader != ObjectHeader));
|
|
|
|
if (ObjectRefInfo && (ObjectRefInfo->ObjectHeader == ObjectHeader)) {
|
|
|
|
PrevObjectRefInfo->NextRef = ObjectRefInfo->NextRef;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Free the object we just removed from the list
|
|
//
|
|
|
|
if (ObjectRefInfo != NULL) {
|
|
|
|
ExFreePoolWithTag( ObjectRefInfo, 'TSbO' );
|
|
}
|
|
|
|
ExReleaseSpinLock( &ObpStackTraceLock, OldIrql );
|
|
}
|
|
}
|
|
|
|
|
|
VOID
|
|
ObpPushStackInfo (
|
|
POBJECT_HEADER ObjectHeader,
|
|
BOOLEAN IsRef
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called each time an object is referenced or
|
|
dereferenced. It determines if the object is traced, and if
|
|
so, adds the necessary trace to the object reference info.
|
|
|
|
Arguments:
|
|
|
|
ObjectHeader - The object to trace.
|
|
IsRef - TRUE if this is a ref, FALSE if a deref
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
|
|
{
|
|
KIRQL OldIrql;
|
|
POBJECT_REF_INFO ObjectInfo;
|
|
|
|
//
|
|
// Are we tracing this object?
|
|
//
|
|
|
|
if (ObpIsObjectTraced( ObjectHeader )) {
|
|
|
|
ExAcquireSpinLock( &ObpStackTraceLock, &OldIrql );
|
|
|
|
ObjectInfo = ObpGetObjectRefInfo( ObjectHeader );
|
|
|
|
if (ObjectInfo) {
|
|
|
|
OBJECT_REF_TRACE Stack = { 0 };
|
|
ULONG StackIndex;
|
|
ULONG CapturedTraces;
|
|
|
|
//
|
|
// Capture the stack trace
|
|
//
|
|
|
|
CapturedTraces = RtlCaptureStackBackTrace( 1, OBTRACE_TRACEDEPTH, Stack.StackTrace, &StackIndex );
|
|
|
|
if (CapturedTraces >= 1) {
|
|
|
|
//
|
|
// Get the table index for the trace
|
|
//
|
|
|
|
StackIndex = ObpGetTraceIndex( &Stack );
|
|
|
|
if (StackIndex < OBTRACE_STACKS) {
|
|
|
|
//
|
|
// Add new reference info to the object
|
|
//
|
|
|
|
if (ObjectInfo->NextPos < OBTRACE_STACKSPEROBJECT) {
|
|
|
|
ObjectInfo->StackInfo[ObjectInfo->NextPos].Index = (USHORT)StackIndex | (IsRef ? 0x8000 : 0);
|
|
ObpStackSequence++;
|
|
ObjectInfo->StackInfo[ObjectInfo->NextPos].Sequence = (USHORT)ObpStackSequence;
|
|
ObjectInfo->NextPos++;
|
|
}
|
|
|
|
} else {
|
|
DbgPrint( "ObpPushStackInfo -- ObpStackTable overflow!\n" );
|
|
}
|
|
}
|
|
}
|
|
|
|
ExReleaseSpinLock( &ObpStackTraceLock, OldIrql );
|
|
}
|
|
}
|
|
|
|
#endif //POOL_TAGGING
|
|
//
|
|
//
|
|
// End Stack trace code
|
|
//
|
|
|
|
typedef struct _OB_TEMP_BUFFER {
|
|
|
|
ACCESS_STATE LocalAccessState;
|
|
OBJECT_CREATE_INFORMATION ObjectCreateInfo;
|
|
OBP_LOOKUP_CONTEXT LookupContext;
|
|
AUX_ACCESS_DATA AuxData;
|
|
|
|
} OB_TEMP_BUFFER, *POB_TEMP_BUFFER;
|
|
|
|
|
|
NTSTATUS
|
|
ObOpenObjectByName (
|
|
IN POBJECT_ATTRIBUTES ObjectAttributes,
|
|
IN POBJECT_TYPE ObjectType OPTIONAL,
|
|
IN KPROCESSOR_MODE AccessMode,
|
|
IN OUT PACCESS_STATE AccessState OPTIONAL,
|
|
IN ACCESS_MASK DesiredAccess OPTIONAL,
|
|
IN OUT PVOID ParseContext OPTIONAL,
|
|
OUT PHANDLE Handle
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
This function opens an object with full access validation and auditing.
|
|
Soon after entering we capture the SubjectContext for the caller. This
|
|
context must remain captured until auditing is complete, and passed to
|
|
any routine that may have to do access checking or auditing.
|
|
|
|
Arguments:
|
|
|
|
ObjectAttributes - Supplies a pointer to the object attributes.
|
|
|
|
ObjectType - Supplies an optional pointer to the object type descriptor.
|
|
|
|
AccessMode - Supplies the processor mode of the access.
|
|
|
|
AccessState - Supplies an optional pointer to the current access status
|
|
describing already granted access types, the privileges used to get
|
|
them, and any access types yet to be granted.
|
|
|
|
DesiredAcess - Supplies the desired access to the object.
|
|
|
|
ParseContext - Supplies an optional pointer to parse context.
|
|
|
|
Handle - Supplies a pointer to a variable that receives the handle value.
|
|
|
|
Return Value:
|
|
|
|
If the object is successfully opened, then a handle for the object is
|
|
created and a success status is returned. Otherwise, an error status is
|
|
returned.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
NTSTATUS HandleStatus;
|
|
PVOID ExistingObject;
|
|
HANDLE NewHandle;
|
|
OB_OPEN_REASON OpenReason;
|
|
POBJECT_HEADER ObjectHeader;
|
|
UNICODE_STRING CapturedObjectName;
|
|
PGENERIC_MAPPING GenericMapping;
|
|
|
|
PAGED_CODE();
|
|
|
|
ObpValidateIrql("ObOpenObjectByName");
|
|
|
|
//
|
|
// If the object attributes are not specified, then return an error.
|
|
//
|
|
|
|
*Handle = NULL;
|
|
|
|
if (!ARGUMENT_PRESENT(ObjectAttributes)) {
|
|
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
|
|
} else {
|
|
|
|
POB_TEMP_BUFFER TempBuffer;
|
|
|
|
TempBuffer = ExAllocatePoolWithTag( NonPagedPool,
|
|
sizeof(OB_TEMP_BUFFER),
|
|
'tSbO'
|
|
);
|
|
|
|
if (TempBuffer == NULL) {
|
|
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
//
|
|
// Capture the object creation information.
|
|
//
|
|
|
|
Status = ObpCaptureObjectCreateInformation( ObjectType,
|
|
AccessMode,
|
|
AccessMode,
|
|
ObjectAttributes,
|
|
&CapturedObjectName,
|
|
&TempBuffer->ObjectCreateInfo,
|
|
TRUE );
|
|
|
|
//
|
|
// If the object creation information is successfully captured,
|
|
// then generate the access state.
|
|
//
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
|
|
if (!ARGUMENT_PRESENT(AccessState)) {
|
|
|
|
//
|
|
// If an object type descriptor is specified, then use
|
|
// associated generic mapping. Otherwise, use no generic
|
|
// mapping.
|
|
//
|
|
|
|
GenericMapping = NULL;
|
|
|
|
if (ARGUMENT_PRESENT(ObjectType)) {
|
|
|
|
GenericMapping = &ObjectType->TypeInfo.GenericMapping;
|
|
}
|
|
|
|
AccessState = &TempBuffer->LocalAccessState;
|
|
|
|
Status = SeCreateAccessState( &TempBuffer->LocalAccessState,
|
|
&TempBuffer->AuxData,
|
|
DesiredAccess,
|
|
GenericMapping );
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto FreeCreateInfo;
|
|
}
|
|
}
|
|
|
|
//
|
|
// If there is a security descriptor specified in the object
|
|
// attributes, then capture it in the access state.
|
|
//
|
|
|
|
if (TempBuffer->ObjectCreateInfo.SecurityDescriptor != NULL) {
|
|
|
|
AccessState->SecurityDescriptor = TempBuffer->ObjectCreateInfo.SecurityDescriptor;
|
|
}
|
|
|
|
//
|
|
// Validate the access state.
|
|
//
|
|
|
|
Status = ObpValidateAccessMask(AccessState);
|
|
|
|
//
|
|
// If the access state is valid, then lookup the object by
|
|
// name.
|
|
//
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
|
|
Status = ObpLookupObjectName( TempBuffer->ObjectCreateInfo.RootDirectory,
|
|
&CapturedObjectName,
|
|
TempBuffer->ObjectCreateInfo.Attributes,
|
|
ObjectType,
|
|
AccessMode,
|
|
ParseContext,
|
|
TempBuffer->ObjectCreateInfo.SecurityQos,
|
|
NULL,
|
|
AccessState,
|
|
&TempBuffer->LookupContext,
|
|
&ExistingObject );
|
|
|
|
//
|
|
// If the object was successfully looked up, then attempt
|
|
// to create or open a handle.
|
|
//
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
|
|
ObjectHeader = OBJECT_TO_OBJECT_HEADER(ExistingObject);
|
|
|
|
//
|
|
// If the object is being created, then the operation
|
|
// must be a open-if operation. Otherwise, a handle to
|
|
// an object is being opened.
|
|
//
|
|
|
|
if (ObjectHeader->Flags & OB_FLAG_NEW_OBJECT) {
|
|
|
|
OpenReason = ObCreateHandle;
|
|
|
|
if (ObjectHeader->ObjectCreateInfo != NULL) {
|
|
|
|
ObpFreeObjectCreateInformation(ObjectHeader->ObjectCreateInfo);
|
|
ObjectHeader->ObjectCreateInfo = NULL;
|
|
}
|
|
|
|
} else {
|
|
|
|
OpenReason = ObOpenHandle;
|
|
}
|
|
|
|
//
|
|
// If any of the object attributes are invalid, then
|
|
// return an error status.
|
|
//
|
|
|
|
if (ObjectHeader->Type->TypeInfo.InvalidAttributes & TempBuffer->ObjectCreateInfo.Attributes) {
|
|
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
|
|
ObpReleaseLookupContext( &TempBuffer->LookupContext );
|
|
|
|
} else {
|
|
|
|
//
|
|
// The status returned by the object lookup routine
|
|
// must be returned if the creation of a handle is
|
|
// successful. Otherwise, the handle creation status
|
|
// is returned.
|
|
//
|
|
|
|
HandleStatus = ObpCreateHandle( OpenReason,
|
|
ExistingObject,
|
|
ObjectType,
|
|
AccessState,
|
|
0,
|
|
TempBuffer->ObjectCreateInfo.Attributes,
|
|
&TempBuffer->LookupContext,
|
|
AccessMode,
|
|
(PVOID *)NULL,
|
|
&NewHandle );
|
|
|
|
if (!NT_SUCCESS(HandleStatus)) {
|
|
|
|
ObDereferenceObject(ExistingObject);
|
|
|
|
Status = HandleStatus;
|
|
|
|
} else {
|
|
|
|
*Handle = NewHandle;
|
|
}
|
|
}
|
|
|
|
} else {
|
|
|
|
ObpReleaseLookupContext( &TempBuffer->LookupContext );
|
|
}
|
|
}
|
|
|
|
//
|
|
// If the access state was generated, then delete the access
|
|
// state.
|
|
//
|
|
|
|
if (AccessState == &TempBuffer->LocalAccessState) {
|
|
|
|
SeDeleteAccessState(AccessState);
|
|
}
|
|
|
|
//
|
|
// Free the create information.
|
|
//
|
|
|
|
FreeCreateInfo:
|
|
|
|
ObpReleaseObjectCreateInformation(&TempBuffer->ObjectCreateInfo);
|
|
|
|
if (CapturedObjectName.Buffer != NULL) {
|
|
|
|
ObpFreeObjectNameBuffer(&CapturedObjectName);
|
|
}
|
|
}
|
|
|
|
ExFreePool(TempBuffer);
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
ObOpenObjectByPointer (
|
|
IN PVOID Object,
|
|
IN ULONG HandleAttributes,
|
|
IN PACCESS_STATE PassedAccessState OPTIONAL,
|
|
IN ACCESS_MASK DesiredAccess,
|
|
IN POBJECT_TYPE ObjectType,
|
|
IN KPROCESSOR_MODE AccessMode,
|
|
OUT PHANDLE Handle
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine opens an object referenced by a pointer.
|
|
|
|
Arguments:
|
|
|
|
Object - A pointer to the object being opened.
|
|
|
|
HandleAttributes - The desired attributes for the handle, such
|
|
as OBJ_INHERIT, OBJ_PERMANENT, OBJ_EXCLUSIVE, OBJ_CASE_INSENSITIVE,
|
|
OBJ_OPENIF, and OBJ_OPENLINK
|
|
|
|
PassedAccessState - Supplies an optional pointer to the current access
|
|
status describing already granted access types, the privileges used
|
|
to get them, and any access types yet to be granted.
|
|
|
|
DesiredAcess - Supplies the desired access to the object.
|
|
|
|
ObjectType - Supplies the type of the object being opened
|
|
|
|
AccessMode - Supplies the processor mode of the access.
|
|
|
|
Handle - Supplies a pointer to a variable that receives the handle value.
|
|
|
|
Return Value:
|
|
|
|
An appropriate NTSTATUS value
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
HANDLE NewHandle = (HANDLE)-1;
|
|
POBJECT_HEADER ObjectHeader;
|
|
ACCESS_STATE LocalAccessState;
|
|
PACCESS_STATE AccessState = NULL;
|
|
AUX_ACCESS_DATA AuxData;
|
|
|
|
PAGED_CODE();
|
|
|
|
ObpValidateIrql( "ObOpenObjectByPointer" );
|
|
|
|
//
|
|
// First increment the pointer count for the object. This routine
|
|
// also checks the object types
|
|
//
|
|
|
|
Status = ObReferenceObjectByPointer( Object,
|
|
0,
|
|
ObjectType,
|
|
AccessMode );
|
|
|
|
if (NT_SUCCESS( Status )) {
|
|
|
|
//
|
|
// Get the object header for the input object body
|
|
//
|
|
|
|
ObjectHeader = OBJECT_TO_OBJECT_HEADER( Object );
|
|
|
|
//
|
|
// If the caller did not pass in an access state then
|
|
// we will create a new one based on the desired access
|
|
// and the object types generic mapping
|
|
//
|
|
|
|
if (!ARGUMENT_PRESENT( PassedAccessState )) {
|
|
|
|
Status = SeCreateAccessState( &LocalAccessState,
|
|
&AuxData,
|
|
DesiredAccess,
|
|
&ObjectHeader->Type->TypeInfo.GenericMapping );
|
|
|
|
if (!NT_SUCCESS( Status )) {
|
|
|
|
ObDereferenceObject( Object );
|
|
|
|
return(Status);
|
|
}
|
|
|
|
AccessState = &LocalAccessState;
|
|
|
|
//
|
|
// Otherwise the caller did specify an access state so
|
|
// we use the one passed in.
|
|
//
|
|
|
|
} else {
|
|
|
|
AccessState = PassedAccessState;
|
|
}
|
|
|
|
//
|
|
// Make sure the caller is asking for handle attributes that are
|
|
// valid for the given object type
|
|
//
|
|
|
|
if (ObjectHeader->Type->TypeInfo.InvalidAttributes & HandleAttributes) {
|
|
|
|
if (AccessState == &LocalAccessState) {
|
|
|
|
SeDeleteAccessState( AccessState );
|
|
}
|
|
|
|
ObDereferenceObject( Object );
|
|
|
|
return( STATUS_INVALID_PARAMETER );
|
|
}
|
|
|
|
//
|
|
// We've referenced the object and have an access state to give
|
|
// the new handle so now create a new handle for the object.
|
|
//
|
|
|
|
Status = ObpCreateHandle( ObOpenHandle,
|
|
Object,
|
|
ObjectType,
|
|
AccessState,
|
|
0,
|
|
HandleAttributes,
|
|
NULL,
|
|
AccessMode,
|
|
(PVOID *)NULL,
|
|
&NewHandle );
|
|
|
|
if (!NT_SUCCESS( Status )) {
|
|
|
|
ObDereferenceObject( Object );
|
|
}
|
|
}
|
|
|
|
//
|
|
// If we successfully opened by object and created a new handle
|
|
// then set the output variable correctly
|
|
//
|
|
|
|
if (NT_SUCCESS( Status )) {
|
|
|
|
*Handle = NewHandle;
|
|
|
|
} else {
|
|
|
|
*Handle = NULL;
|
|
}
|
|
|
|
//
|
|
// Check if we used our own access state and now need to cleanup
|
|
//
|
|
|
|
if (AccessState == &LocalAccessState) {
|
|
|
|
SeDeleteAccessState( AccessState );
|
|
}
|
|
|
|
//
|
|
// And return to our caller
|
|
//
|
|
|
|
return( Status );
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
ObReferenceObjectByHandle (
|
|
IN HANDLE Handle,
|
|
IN ACCESS_MASK DesiredAccess,
|
|
IN POBJECT_TYPE ObjectType OPTIONAL,
|
|
IN KPROCESSOR_MODE AccessMode,
|
|
OUT PVOID *Object,
|
|
OUT POBJECT_HANDLE_INFORMATION HandleInformation OPTIONAL
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Given a handle to an object this routine returns a pointer
|
|
to the body of the object with proper ref counts
|
|
|
|
Arguments:
|
|
|
|
Handle - Supplies a handle to the object being referenced. It can
|
|
also be the result of NtCurrentProcess or NtCurrentThread
|
|
|
|
DesiredAccess - Supplies the access being requested by the caller
|
|
|
|
ObjectType - Optionally supplies the type of the object we
|
|
are expecting
|
|
|
|
AccessMode - Supplies the processor mode of the access
|
|
|
|
Object - Receives a pointer to the object body if the operation
|
|
is successful
|
|
|
|
HandleInformation - Optionally receives information regarding the
|
|
input handle.
|
|
|
|
Return Value:
|
|
|
|
An appropriate NTSTATUS value
|
|
|
|
--*/
|
|
|
|
{
|
|
ACCESS_MASK GrantedAccess;
|
|
PHANDLE_TABLE HandleTable;
|
|
POBJECT_HEADER ObjectHeader;
|
|
PHANDLE_TABLE_ENTRY ObjectTableEntry;
|
|
PEPROCESS Process;
|
|
NTSTATUS Status;
|
|
PETHREAD Thread;
|
|
|
|
ObpValidateIrql("ObReferenceObjectByHandle");
|
|
|
|
Thread = PsGetCurrentThread ();
|
|
*Object = NULL;
|
|
|
|
//
|
|
// Check is this handle is a kernel handle or one of the two builtin pseudo handles
|
|
//
|
|
if ((LONG)(ULONG_PTR) Handle < 0) {
|
|
//
|
|
// If the handle is equal to the current process handle and the object
|
|
// type is NULL or type process, then attempt to translate a handle to
|
|
// the current process. Otherwise, check if the handle is the current
|
|
// thread handle.
|
|
//
|
|
|
|
if (Handle == NtCurrentProcess()) {
|
|
|
|
if ((ObjectType == PsProcessType) || (ObjectType == NULL)) {
|
|
|
|
Process = PsGetCurrentProcessByThread(Thread);
|
|
GrantedAccess = Process->GrantedAccess;
|
|
|
|
if ((SeComputeDeniedAccesses(GrantedAccess, DesiredAccess) == 0) ||
|
|
(AccessMode == KernelMode)) {
|
|
|
|
ObjectHeader = OBJECT_TO_OBJECT_HEADER(Process);
|
|
|
|
if (ARGUMENT_PRESENT(HandleInformation)) {
|
|
|
|
HandleInformation->GrantedAccess = GrantedAccess;
|
|
HandleInformation->HandleAttributes = 0;
|
|
}
|
|
|
|
ObpIncrPointerCount(ObjectHeader);
|
|
*Object = Process;
|
|
|
|
ASSERT( *Object != NULL );
|
|
|
|
Status = STATUS_SUCCESS;
|
|
|
|
} else {
|
|
|
|
Status = STATUS_ACCESS_DENIED;
|
|
}
|
|
|
|
} else {
|
|
|
|
Status = STATUS_OBJECT_TYPE_MISMATCH;
|
|
}
|
|
|
|
return Status;
|
|
|
|
//
|
|
// If the handle is equal to the current thread handle and the object
|
|
// type is NULL or type thread, then attempt to translate a handle to
|
|
// the current thread. Otherwise, the we'll try and translate the
|
|
// handle
|
|
//
|
|
|
|
} else if (Handle == NtCurrentThread()) {
|
|
|
|
if ((ObjectType == PsThreadType) || (ObjectType == NULL)) {
|
|
|
|
GrantedAccess = Thread->GrantedAccess;
|
|
|
|
if ((SeComputeDeniedAccesses(GrantedAccess, DesiredAccess) == 0) ||
|
|
(AccessMode == KernelMode)) {
|
|
|
|
ObjectHeader = OBJECT_TO_OBJECT_HEADER(Thread);
|
|
|
|
if (ARGUMENT_PRESENT(HandleInformation)) {
|
|
|
|
HandleInformation->GrantedAccess = GrantedAccess;
|
|
HandleInformation->HandleAttributes = 0;
|
|
}
|
|
|
|
ObpIncrPointerCount(ObjectHeader);
|
|
*Object = Thread;
|
|
|
|
ASSERT( *Object != NULL );
|
|
|
|
Status = STATUS_SUCCESS;
|
|
|
|
} else {
|
|
|
|
Status = STATUS_ACCESS_DENIED;
|
|
}
|
|
|
|
} else {
|
|
|
|
Status = STATUS_OBJECT_TYPE_MISMATCH;
|
|
}
|
|
|
|
return Status;
|
|
|
|
} else if (AccessMode == KernelMode) {
|
|
//
|
|
// Make the handle look like a regular handle
|
|
//
|
|
|
|
Handle = DecodeKernelHandle( Handle );
|
|
|
|
//
|
|
// The global kernel handle table
|
|
//
|
|
|
|
HandleTable = ObpKernelHandleTable;
|
|
} else {
|
|
//
|
|
// The previous mode was user for this kernel handle value. Reject it here.
|
|
//
|
|
|
|
return STATUS_INVALID_HANDLE;
|
|
}
|
|
|
|
} else {
|
|
HandleTable = PsGetCurrentProcessByThread(Thread)->ObjectTable;
|
|
}
|
|
|
|
ASSERT(HandleTable != NULL);
|
|
|
|
//
|
|
// Protect this thread from being suspended while we hold the handle table entry lock
|
|
//
|
|
|
|
KeEnterCriticalRegionThread(&Thread->Tcb);
|
|
|
|
//
|
|
// Translate the specified handle to an object table index.
|
|
//
|
|
|
|
ObjectTableEntry = ExMapHandleToPointerEx ( HandleTable, Handle, AccessMode );
|
|
|
|
//
|
|
// Make sure the object table entry really does exist
|
|
//
|
|
|
|
if (ObjectTableEntry != NULL) {
|
|
|
|
ObjectHeader = (POBJECT_HEADER)(((ULONG_PTR)(ObjectTableEntry->Object)) & ~OBJ_HANDLE_ATTRIBUTES);
|
|
|
|
if ((ObjectHeader->Type == ObjectType) || (ObjectType == NULL)) {
|
|
|
|
#if i386
|
|
if (NtGlobalFlag & FLG_KERNEL_STACK_TRACE_DB) {
|
|
|
|
GrantedAccess = ObpTranslateGrantedAccessIndex( ObjectTableEntry->GrantedAccessIndex );
|
|
|
|
} else {
|
|
|
|
GrantedAccess = ObpDecodeGrantedAccess(ObjectTableEntry->GrantedAccess);
|
|
}
|
|
#else
|
|
GrantedAccess = ObpDecodeGrantedAccess(ObjectTableEntry->GrantedAccess);
|
|
|
|
#endif // i386
|
|
|
|
if ((SeComputeDeniedAccesses(GrantedAccess, DesiredAccess) == 0) ||
|
|
(AccessMode == KernelMode)) {
|
|
|
|
PHANDLE_TABLE_ENTRY_INFO ObjectInfo;
|
|
|
|
ObjectInfo = ExGetHandleInfo(HandleTable, Handle, TRUE);
|
|
|
|
//
|
|
// Access to the object is allowed. Return the handle
|
|
// information is requested, increment the object
|
|
// pointer count, unlock the handle table and return
|
|
// a success status.
|
|
//
|
|
// Note that this is the only successful return path
|
|
// out of this routine if the user did not specify
|
|
// the current process or current thread in the input
|
|
// handle.
|
|
//
|
|
|
|
if (ARGUMENT_PRESENT(HandleInformation)) {
|
|
|
|
HandleInformation->GrantedAccess = GrantedAccess;
|
|
HandleInformation->HandleAttributes = ObpGetHandleAttributes(ObjectTableEntry);
|
|
}
|
|
|
|
//
|
|
// If this object was audited when it was opened, it may
|
|
// be necessary to generate an audit now. Check the audit
|
|
// mask that was saved when the handle was created.
|
|
//
|
|
// It is safe to do this check in a non-atomic fashion,
|
|
// because bits will never be added to this mask once it is
|
|
// created.
|
|
//
|
|
|
|
if ( (ObjectTableEntry->ObAttributes & OBJ_AUDIT_OBJECT_CLOSE) &&
|
|
(ObjectInfo != NULL) &&
|
|
(ObjectInfo->AuditMask != 0) &&
|
|
(DesiredAccess != 0) &&
|
|
(AccessMode != KernelMode)) {
|
|
|
|
|
|
ObpAuditObjectAccess( Handle, ObjectInfo, &ObjectHeader->Type->Name, DesiredAccess );
|
|
}
|
|
|
|
ObpIncrPointerCount(ObjectHeader);
|
|
|
|
ExUnlockHandleTableEntry( HandleTable, ObjectTableEntry );
|
|
|
|
KeLeaveCriticalRegionThread(&Thread->Tcb);
|
|
|
|
*Object = &ObjectHeader->Body;
|
|
|
|
ASSERT( *Object != NULL );
|
|
|
|
return STATUS_SUCCESS;
|
|
|
|
} else {
|
|
|
|
Status = STATUS_ACCESS_DENIED;
|
|
}
|
|
|
|
} else {
|
|
|
|
Status = STATUS_OBJECT_TYPE_MISMATCH;
|
|
}
|
|
|
|
ExUnlockHandleTableEntry( HandleTable, ObjectTableEntry );
|
|
|
|
} else {
|
|
|
|
Status = STATUS_INVALID_HANDLE;
|
|
}
|
|
|
|
KeLeaveCriticalRegionThread(&Thread->Tcb);
|
|
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
ObpReferenceProcessObjectByHandle (
|
|
IN HANDLE Handle,
|
|
IN PEPROCESS Process,
|
|
IN PHANDLE_TABLE HandleTable,
|
|
IN KPROCESSOR_MODE AccessMode,
|
|
OUT PVOID *Object,
|
|
OUT POBJECT_HANDLE_INFORMATION HandleInformation,
|
|
OUT PACCESS_MASK AuditMask
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Given a handle to an object a process and its handle table
|
|
this routine returns a pointer to the body of the object with
|
|
proper ref counts
|
|
|
|
Arguments:
|
|
|
|
Handle - Supplies a handle to the object being referenced. It can
|
|
also be the result of NtCurrentProcess or NtCurrentThread
|
|
|
|
Process - Process that the handle should be referenced from.
|
|
|
|
HandleTable - Handle table of target process
|
|
|
|
AccessMode - Supplies the processor mode of the access
|
|
|
|
Object - Receives a pointer to the object body if the operation
|
|
is successful
|
|
|
|
HandleInformation - receives information regarding the
|
|
input handle.
|
|
|
|
AuditMask - Pointer to any audit mask associated with the handle.
|
|
|
|
Return Value:
|
|
|
|
An appropriate NTSTATUS value
|
|
|
|
--*/
|
|
|
|
{
|
|
ACCESS_MASK GrantedAccess;
|
|
POBJECT_HEADER ObjectHeader;
|
|
PHANDLE_TABLE_ENTRY ObjectTableEntry;
|
|
NTSTATUS Status;
|
|
PETHREAD Thread;
|
|
PHANDLE_TABLE_ENTRY_INFO ObjectInfo;
|
|
|
|
ObpValidateIrql("ObReferenceObjectByHandle");
|
|
|
|
Thread = PsGetCurrentThread ();
|
|
*Object = NULL;
|
|
|
|
//
|
|
// Check is this handle is a kernel handle or one of the two builtin pseudo handles
|
|
//
|
|
if ((LONG)(ULONG_PTR) Handle < 0) {
|
|
//
|
|
// If the handle is equal to the current process handle and the object
|
|
// type is NULL or type process, then attempt to translate a handle to
|
|
// the current process. Otherwise, check if the handle is the current
|
|
// thread handle.
|
|
//
|
|
|
|
if (Handle == NtCurrentProcess()) {
|
|
|
|
GrantedAccess = Process->GrantedAccess;
|
|
|
|
ObjectHeader = OBJECT_TO_OBJECT_HEADER(Process);
|
|
|
|
HandleInformation->GrantedAccess = GrantedAccess;
|
|
HandleInformation->HandleAttributes = 0;
|
|
|
|
*AuditMask = 0;
|
|
|
|
ObpIncrPointerCount(ObjectHeader);
|
|
*Object = Process;
|
|
|
|
ASSERT( *Object != NULL );
|
|
|
|
Status = STATUS_SUCCESS;
|
|
|
|
return Status;
|
|
|
|
//
|
|
// If the handle is equal to the current thread handle and the object
|
|
// type is NULL or type thread, then attempt to translate a handle to
|
|
// the current thread. Otherwise, the we'll try and translate the
|
|
// handle
|
|
//
|
|
|
|
} else if (Handle == NtCurrentThread()) {
|
|
|
|
GrantedAccess = Thread->GrantedAccess;
|
|
|
|
ObjectHeader = OBJECT_TO_OBJECT_HEADER(Thread);
|
|
|
|
HandleInformation->GrantedAccess = GrantedAccess;
|
|
HandleInformation->HandleAttributes = 0;
|
|
|
|
*AuditMask = 0;
|
|
|
|
ObpIncrPointerCount(ObjectHeader);
|
|
*Object = Thread;
|
|
|
|
ASSERT( *Object != NULL );
|
|
|
|
Status = STATUS_SUCCESS;
|
|
|
|
return Status;
|
|
|
|
} else if (AccessMode == KernelMode) {
|
|
//
|
|
// Make the handle look like a regular handle
|
|
//
|
|
|
|
Handle = DecodeKernelHandle( Handle );
|
|
|
|
//
|
|
// The global kernel handle table
|
|
//
|
|
|
|
HandleTable = ObpKernelHandleTable;
|
|
} else {
|
|
//
|
|
// The previous mode was user for this kernel handle value. Reject it here.
|
|
//
|
|
|
|
return STATUS_INVALID_HANDLE;
|
|
}
|
|
|
|
}
|
|
|
|
ASSERT(HandleTable != NULL);
|
|
|
|
//
|
|
// Protect this thread from being suspended while we hold the handle table entry lock
|
|
//
|
|
|
|
KeEnterCriticalRegionThread(&Thread->Tcb);
|
|
|
|
//
|
|
// Translate the specified handle to an object table index.
|
|
//
|
|
|
|
ObjectTableEntry = ExMapHandleToPointer ( HandleTable, Handle );
|
|
|
|
//
|
|
// Make sure the object table entry really does exist
|
|
//
|
|
|
|
if (ObjectTableEntry != NULL) {
|
|
|
|
ObjectHeader = (POBJECT_HEADER)(((ULONG_PTR)(ObjectTableEntry->Object)) & ~OBJ_HANDLE_ATTRIBUTES);
|
|
|
|
#if i386
|
|
if (NtGlobalFlag & FLG_KERNEL_STACK_TRACE_DB) {
|
|
|
|
GrantedAccess = ObpTranslateGrantedAccessIndex( ObjectTableEntry->GrantedAccessIndex );
|
|
|
|
} else {
|
|
|
|
GrantedAccess = ObpDecodeGrantedAccess(ObjectTableEntry->GrantedAccess);
|
|
}
|
|
#else
|
|
GrantedAccess = ObpDecodeGrantedAccess(ObjectTableEntry->GrantedAccess);
|
|
|
|
#endif // i386
|
|
|
|
|
|
ObjectInfo = ExGetHandleInfo(HandleTable, Handle, TRUE);
|
|
|
|
//
|
|
// Return the handle information, increment the object
|
|
// pointer count, unlock the handle table and return
|
|
// a success status.
|
|
//
|
|
// Note that this is the only successful return path
|
|
// out of this routine if the user did not specify
|
|
// the current process or current thread in the input
|
|
// handle.
|
|
//
|
|
|
|
HandleInformation->GrantedAccess = GrantedAccess;
|
|
HandleInformation->HandleAttributes = ObpGetHandleAttributes(ObjectTableEntry);
|
|
|
|
//
|
|
// Return handle audit information to the caller
|
|
//
|
|
if (ObjectInfo != NULL) {
|
|
*AuditMask = ObjectInfo->AuditMask;
|
|
} else {
|
|
*AuditMask = 0;
|
|
}
|
|
|
|
ObpIncrPointerCount(ObjectHeader);
|
|
|
|
ExUnlockHandleTableEntry( HandleTable, ObjectTableEntry );
|
|
|
|
KeLeaveCriticalRegionThread(&Thread->Tcb);
|
|
|
|
*Object = &ObjectHeader->Body;
|
|
|
|
ASSERT( *Object != NULL );
|
|
|
|
return STATUS_SUCCESS;
|
|
|
|
|
|
} else {
|
|
|
|
Status = STATUS_INVALID_HANDLE;
|
|
}
|
|
|
|
KeLeaveCriticalRegionThread(&Thread->Tcb);
|
|
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
|
|
NTSTATUS
|
|
ObReferenceFileObjectForWrite(
|
|
IN HANDLE Handle,
|
|
IN KPROCESSOR_MODE AccessMode,
|
|
OUT PVOID *FileObject,
|
|
OUT POBJECT_HANDLE_INFORMATION HandleInformation
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Given a handle to a file object this routine returns a pointer
|
|
to the body of the object with proper ref counts and auditing. This
|
|
routine is meant to solve a very particular handle reference issue with
|
|
file object access auditing. Do not call this unless you understand exactly
|
|
what you are doing.
|
|
|
|
Arguments:
|
|
|
|
Handle - Supplies a handle to the IoFileObjectType being referenced.
|
|
|
|
AccessMode - Supplies the processor mode of the access
|
|
|
|
FileObject - Receives a pointer to the object body if the operation
|
|
is successful
|
|
|
|
HandleInformation - receives information regarding the input handle.
|
|
|
|
Return Value:
|
|
|
|
An appropriate NTSTATUS value
|
|
|
|
--*/
|
|
|
|
{
|
|
ACCESS_MASK GrantedAccess;
|
|
ACCESS_MASK DesiredAccess;
|
|
PHANDLE_TABLE HandleTable;
|
|
POBJECT_HEADER ObjectHeader;
|
|
PHANDLE_TABLE_ENTRY ObjectTableEntry;
|
|
NTSTATUS Status;
|
|
PETHREAD Thread;
|
|
PHANDLE_TABLE_ENTRY_INFO ObjectInfo;
|
|
|
|
ObpValidateIrql("ObReferenceFileObjectForWrite");
|
|
|
|
Thread = PsGetCurrentThread ();
|
|
|
|
//
|
|
// Check is this handle is a kernel handle
|
|
//
|
|
|
|
if ((LONG)(ULONG_PTR) Handle < 0) {
|
|
|
|
if ((AccessMode == KernelMode) && (Handle != NtCurrentProcess()) && (Handle != NtCurrentThread())) {
|
|
|
|
//
|
|
// Make the handle look like a regular handle
|
|
//
|
|
|
|
Handle = DecodeKernelHandle( Handle );
|
|
|
|
//
|
|
// The global kernel handle table
|
|
//
|
|
|
|
HandleTable = ObpKernelHandleTable;
|
|
} else {
|
|
//
|
|
// The previous mode was user for this kernel handle value, or it was a builtin handle. Reject it here.
|
|
//
|
|
|
|
return STATUS_INVALID_HANDLE;
|
|
}
|
|
} else {
|
|
HandleTable = PsGetCurrentProcessByThread(Thread)->ObjectTable;
|
|
}
|
|
|
|
ASSERT(HandleTable != NULL);
|
|
|
|
//
|
|
// Protect this thread from being suspended while we hold the handle table entry lock
|
|
//
|
|
|
|
KeEnterCriticalRegionThread(&Thread->Tcb);
|
|
|
|
//
|
|
// Translate the specified handle to an object table index.
|
|
//
|
|
|
|
ObjectTableEntry = ExMapHandleToPointerEx ( HandleTable, Handle, AccessMode );
|
|
|
|
//
|
|
// Make sure the object table entry really does exist
|
|
//
|
|
|
|
if (ObjectTableEntry != NULL) {
|
|
|
|
ObjectHeader = (POBJECT_HEADER)(((ULONG_PTR)(ObjectTableEntry->Object)) & ~OBJ_HANDLE_ATTRIBUTES);
|
|
|
|
if (NT_SUCCESS(IoComputeDesiredAccessFileObject((PFILE_OBJECT)&ObjectHeader->Body, (PNTSTATUS)&DesiredAccess))) {
|
|
|
|
#if i386
|
|
if (NtGlobalFlag & FLG_KERNEL_STACK_TRACE_DB) {
|
|
|
|
GrantedAccess = ObpTranslateGrantedAccessIndex( ObjectTableEntry->GrantedAccessIndex );
|
|
|
|
} else {
|
|
|
|
GrantedAccess = ObpDecodeGrantedAccess(ObjectTableEntry->GrantedAccess);
|
|
}
|
|
#else
|
|
GrantedAccess = ObpDecodeGrantedAccess(ObjectTableEntry->GrantedAccess);
|
|
|
|
#endif // i386
|
|
|
|
ObjectInfo = ExGetHandleInfo(HandleTable, Handle, TRUE);
|
|
|
|
//
|
|
// Access to the object is allowed. Return the handle
|
|
// information, increment the object pointer count,
|
|
// compute correct access, audit, unlock the handle
|
|
// table and return a success status.
|
|
//
|
|
// Note that this is the only successful return path
|
|
// out of this routine.
|
|
//
|
|
|
|
HandleInformation->GrantedAccess = GrantedAccess;
|
|
HandleInformation->HandleAttributes = ObpGetHandleAttributes(ObjectTableEntry);
|
|
|
|
//
|
|
// Check to ensure that the caller has either WRITE_DATA or APPEND_DATA
|
|
// access to the file. If not, cleanup and return an access denied
|
|
// error status value. Note that if this is a pipe then the APPEND_DATA
|
|
// access check may not be made since this access code is overlaid with
|
|
// CREATE_PIPE_INSTANCE access.
|
|
//
|
|
|
|
if (SeComputeGrantedAccesses( GrantedAccess, DesiredAccess )) {
|
|
|
|
//
|
|
// If this object was audited when it was opened, it may
|
|
// be necessary to generate an audit now. Check the audit
|
|
// mask that was saved when the handle was created.
|
|
//
|
|
// It is safe to do this check in a non-atomic fashion,
|
|
// because bits will never be added to this mask once it is
|
|
// created.
|
|
//
|
|
|
|
if ( (ObjectTableEntry->ObAttributes & OBJ_AUDIT_OBJECT_CLOSE) &&
|
|
(ObjectInfo != NULL) &&
|
|
(ObjectInfo->AuditMask != 0) &&
|
|
(DesiredAccess != 0) &&
|
|
(AccessMode != KernelMode)) {
|
|
|
|
ObpAuditObjectAccess( Handle, ObjectInfo, &ObjectHeader->Type->Name, DesiredAccess );
|
|
}
|
|
|
|
ObpIncrPointerCount(ObjectHeader);
|
|
ExUnlockHandleTableEntry( HandleTable, ObjectTableEntry );
|
|
KeLeaveCriticalRegionThread(&Thread->Tcb);
|
|
|
|
*FileObject = &ObjectHeader->Body;
|
|
|
|
ASSERT( *FileObject != NULL );
|
|
|
|
return STATUS_SUCCESS;
|
|
|
|
} else {
|
|
|
|
Status = STATUS_ACCESS_DENIED;
|
|
}
|
|
|
|
} else {
|
|
|
|
Status = STATUS_OBJECT_TYPE_MISMATCH;
|
|
}
|
|
|
|
ExUnlockHandleTableEntry( HandleTable, ObjectTableEntry );
|
|
|
|
} else {
|
|
|
|
Status = STATUS_INVALID_HANDLE;
|
|
}
|
|
|
|
KeLeaveCriticalRegionThread(&Thread->Tcb);
|
|
|
|
//
|
|
// No handle translation is possible. Set the object address to NULL
|
|
// and return an error status.
|
|
//
|
|
|
|
*FileObject = NULL;
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
VOID
|
|
ObAuditObjectAccess(
|
|
IN HANDLE Handle,
|
|
IN POBJECT_HANDLE_INFORMATION HandleInformation OPTIONAL,
|
|
IN KPROCESSOR_MODE AccessMode,
|
|
IN ACCESS_MASK DesiredAccess
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine will determine if it is necessary to audit the operation being
|
|
performed on the passed handle. If so, it will clear the bits in the handle
|
|
and generate the appropriate audit before returning.
|
|
|
|
The bits in the handle's audit mask are cleared in an atomic way so that
|
|
multiple threads coming through this code do not generate more than one
|
|
audit for the same operation.
|
|
|
|
Arguments:
|
|
|
|
Handle - Supplies the handle being accessed.
|
|
|
|
AccessMode - The mode (kernel or user) that originated the handle.
|
|
|
|
DesiredAccess - Supplies the access mask describing how the handle is being used
|
|
in this operation.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PHANDLE_TABLE HandleTable;
|
|
PHANDLE_TABLE_ENTRY ObjectTableEntry;
|
|
POBJECT_HEADER ObjectHeader;
|
|
PKTHREAD CurrentThread;
|
|
|
|
//
|
|
// Exit fast if we have nothing to do.
|
|
//
|
|
|
|
if (ARGUMENT_PRESENT(HandleInformation)) {
|
|
if (!(HandleInformation->HandleAttributes & OBJ_AUDIT_OBJECT_CLOSE)) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Do not currently support this on kernel mode
|
|
// handles.
|
|
//
|
|
|
|
if (AccessMode == KernelMode) {
|
|
return;
|
|
}
|
|
|
|
HandleTable = ObpGetObjectTable();
|
|
|
|
ASSERT(HandleTable != NULL);
|
|
|
|
//
|
|
// Translate the specified handle to an object table index.
|
|
//
|
|
|
|
CurrentThread = KeGetCurrentThread ();
|
|
KeEnterCriticalRegionThread (CurrentThread);
|
|
|
|
ObjectTableEntry = ExMapHandleToPointer( HandleTable, Handle );
|
|
|
|
//
|
|
// Make sure the object table entry really does exist
|
|
//
|
|
|
|
if (ObjectTableEntry != NULL) {
|
|
|
|
PHANDLE_TABLE_ENTRY_INFO ObjectInfo;
|
|
|
|
ObjectInfo = ExGetHandleInfo(HandleTable, Handle, TRUE);
|
|
|
|
ObjectHeader = (POBJECT_HEADER)(((ULONG_PTR)(ObjectTableEntry->Object)) & ~OBJ_HANDLE_ATTRIBUTES);
|
|
|
|
//
|
|
// If this object was audited when it was opened, it may
|
|
// be necessary to generate an audit now. Check the audit
|
|
// mask that was saved when the handle was created.
|
|
//
|
|
// It is safe to do this check in a non-atomic fashion,
|
|
// because bits will never be added to this mask once it is
|
|
// created.
|
|
//
|
|
// Note: is OBJ_AUDIT_OBJECT_CLOSE in ObAttributes kept in synch with
|
|
// HandleAttributes?
|
|
//
|
|
|
|
if ( (ObjectTableEntry->ObAttributes & OBJ_AUDIT_OBJECT_CLOSE) &&
|
|
(ObjectInfo != NULL) &&
|
|
(ObjectInfo->AuditMask != 0) &&
|
|
(DesiredAccess != 0)) {
|
|
|
|
ObpAuditObjectAccess( Handle, ObjectInfo, &ObjectHeader->Type->Name, DesiredAccess );
|
|
}
|
|
|
|
ExUnlockHandleTableEntry( HandleTable, ObjectTableEntry );
|
|
}
|
|
KeLeaveCriticalRegionThread (CurrentThread);
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
ObpAuditObjectAccess(
|
|
IN HANDLE Handle,
|
|
IN PHANDLE_TABLE_ENTRY_INFO ObjectTableEntryInfo,
|
|
IN PUNICODE_STRING ObjectTypeName,
|
|
IN ACCESS_MASK DesiredAccess
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine will determine if it is necessary to audit the operation being
|
|
performed on the passed handle. If so, it will clear the bits in the handle
|
|
and generate the appropriate audit before returning.
|
|
|
|
The bits in the handle's audit mask are cleared in an atomic way so that
|
|
multiple threads coming through this code do not generate more than one
|
|
audit for the same operation.
|
|
|
|
Arguments:
|
|
|
|
Handle - Supplies the handle being accessed.
|
|
|
|
ObjectTableEntry - Supplies the object table entry for the handle passed in the
|
|
first parameter.
|
|
|
|
DesiredAccess - Supplies the access mask describing how the handle is being used
|
|
in this operation.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
ACCESS_MASK t1, t2, r;
|
|
ACCESS_MASK BitsToAudit;
|
|
|
|
//
|
|
// Determine if this access is to
|
|
// be audited, and if so, clear the bits
|
|
// in the ObjectTableEntry.
|
|
//
|
|
|
|
while (ObjectTableEntryInfo->AuditMask != 0) {
|
|
|
|
t1 = ObjectTableEntryInfo->AuditMask;
|
|
t2 = t1 & ~DesiredAccess;
|
|
|
|
if (t2 != t1) {
|
|
|
|
r = (ACCESS_MASK) InterlockedCompareExchange((PLONG)&ObjectTableEntryInfo->AuditMask, t2, t1);
|
|
|
|
if (r == t1) {
|
|
|
|
//
|
|
// AuditMask was == t1, so AuditMask is now == t2
|
|
// it worked, r contains what was in AuditMask, which
|
|
// we can examine safely.
|
|
//
|
|
|
|
BitsToAudit = r & DesiredAccess;
|
|
|
|
//
|
|
// Generate audit here
|
|
//
|
|
|
|
if (BitsToAudit != 0) {
|
|
|
|
SeOperationAuditAlarm( NULL,
|
|
Handle,
|
|
ObjectTypeName,
|
|
BitsToAudit,
|
|
NULL
|
|
);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
//
|
|
// else, somebody changed it, go around for another try
|
|
//
|
|
|
|
} else {
|
|
|
|
//
|
|
// There are no bits in the AuditMask that we
|
|
// want to audit here, just leave.
|
|
//
|
|
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
ObReferenceObjectByName (
|
|
IN PUNICODE_STRING ObjectName,
|
|
IN ULONG Attributes,
|
|
IN PACCESS_STATE AccessState OPTIONAL,
|
|
IN ACCESS_MASK DesiredAccess OPTIONAL,
|
|
IN POBJECT_TYPE ObjectType,
|
|
IN KPROCESSOR_MODE AccessMode,
|
|
IN OUT PVOID ParseContext OPTIONAL,
|
|
OUT PVOID *Object
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Given a name of an object this routine returns a pointer
|
|
to the body of the object with proper ref counts
|
|
|
|
Arguments:
|
|
|
|
ObjectName - Supplies the name of the object being referenced
|
|
|
|
Attributes - Supplies the desired handle attributes
|
|
|
|
AccessState - Supplies an optional pointer to the current access
|
|
status describing already granted access types, the privileges used
|
|
to get them, and any access types yet to be granted.
|
|
|
|
DesiredAccess - Optionally supplies the desired access to the
|
|
for the object
|
|
|
|
ObjectType - Specifies the object type according to the caller
|
|
|
|
AccessMode - Supplies the processor mode of the access
|
|
|
|
ParseContext - Optionally supplies a context to pass down to the
|
|
parse routine
|
|
|
|
Object - Receives a pointer to the referenced object body
|
|
|
|
Return Value:
|
|
|
|
An appropriate NTSTATUS value
|
|
|
|
--*/
|
|
|
|
{
|
|
UNICODE_STRING CapturedObjectName;
|
|
PVOID ExistingObject;
|
|
ACCESS_STATE LocalAccessState;
|
|
AUX_ACCESS_DATA AuxData;
|
|
NTSTATUS Status;
|
|
OBP_LOOKUP_CONTEXT LookupContext;
|
|
|
|
PAGED_CODE();
|
|
|
|
ObpValidateIrql("ObReferenceObjectByName");
|
|
|
|
//
|
|
// If the object name descriptor is not specified, or the object name
|
|
// length is zero (tested after capture), then the object name is
|
|
// invalid.
|
|
//
|
|
|
|
if (ObjectName == NULL) {
|
|
|
|
return STATUS_OBJECT_NAME_INVALID;
|
|
}
|
|
|
|
//
|
|
// Capture the object name.
|
|
//
|
|
|
|
Status = ObpCaptureObjectName( AccessMode,
|
|
ObjectName,
|
|
&CapturedObjectName,
|
|
TRUE );
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
|
|
//
|
|
// No buffer has been allocated for a zero length name so no free
|
|
// needed
|
|
//
|
|
|
|
if (CapturedObjectName.Length == 0) {
|
|
|
|
return STATUS_OBJECT_NAME_INVALID;
|
|
}
|
|
|
|
//
|
|
// If the access state is not specified, then create the access
|
|
// state.
|
|
//
|
|
|
|
if (!ARGUMENT_PRESENT(AccessState)) {
|
|
|
|
AccessState = &LocalAccessState;
|
|
|
|
Status = SeCreateAccessState( &LocalAccessState,
|
|
&AuxData,
|
|
DesiredAccess,
|
|
&ObjectType->TypeInfo.GenericMapping );
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
goto FreeBuffer;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Lookup object by name.
|
|
//
|
|
|
|
Status = ObpLookupObjectName( NULL,
|
|
&CapturedObjectName,
|
|
Attributes,
|
|
ObjectType,
|
|
AccessMode,
|
|
ParseContext,
|
|
NULL,
|
|
NULL,
|
|
AccessState,
|
|
&LookupContext,
|
|
&ExistingObject );
|
|
|
|
//
|
|
// If the directory is returned locked, then unlock it.
|
|
//
|
|
|
|
ObpReleaseLookupContext( &LookupContext );
|
|
//
|
|
// If the lookup was successful, then return the existing
|
|
// object if access is allowed. Otherwise, return NULL.
|
|
//
|
|
|
|
*Object = NULL;
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
|
|
if (ObpCheckObjectReference( ExistingObject,
|
|
AccessState,
|
|
FALSE,
|
|
AccessMode,
|
|
&Status )) {
|
|
|
|
*Object = ExistingObject;
|
|
}
|
|
}
|
|
|
|
//
|
|
// If the access state was generated, then delete the access
|
|
// state.
|
|
//
|
|
|
|
if (AccessState == &LocalAccessState) {
|
|
|
|
SeDeleteAccessState(AccessState);
|
|
}
|
|
|
|
//
|
|
// Free the object name buffer.
|
|
//
|
|
|
|
FreeBuffer:
|
|
|
|
ObpFreeObjectNameBuffer(&CapturedObjectName);
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
ObReferenceObjectByPointer (
|
|
IN PVOID Object,
|
|
IN ACCESS_MASK DesiredAccess,
|
|
IN POBJECT_TYPE ObjectType,
|
|
IN KPROCESSOR_MODE AccessMode
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine adds another reference count to an object denoted by
|
|
a pointer to the object body
|
|
|
|
Arguments:
|
|
|
|
Object - Supplies a pointer to the object being referenced
|
|
|
|
DesiredAccess - Specifies the desired access for the reference
|
|
|
|
ObjectType - Specifies the object type according to the caller
|
|
|
|
AccessMode - Supplies the processor mode of the access
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS if successful and STATUS_OBJECT_TYPE_MISMATCH otherwise
|
|
|
|
--*/
|
|
|
|
{
|
|
POBJECT_HEADER ObjectHeader;
|
|
|
|
UNREFERENCED_PARAMETER (DesiredAccess);
|
|
|
|
//
|
|
// Translate the pointer to the object body to a pointer to the
|
|
// object header
|
|
//
|
|
|
|
ObjectHeader = OBJECT_TO_OBJECT_HEADER( Object );
|
|
|
|
//
|
|
// If the specified object type does not match and either the caller is
|
|
// not kernel mode or it is not a symbolic link object then it is an
|
|
// error
|
|
//
|
|
|
|
if ((ObjectHeader->Type != ObjectType) && (AccessMode != KernelMode ||
|
|
ObjectType == ObpSymbolicLinkObjectType)) {
|
|
|
|
return( STATUS_OBJECT_TYPE_MISMATCH );
|
|
}
|
|
|
|
//
|
|
// Otherwise increment the pointer count and return success to
|
|
// our caller
|
|
//
|
|
|
|
ObpIncrPointerCount( ObjectHeader );
|
|
|
|
return( STATUS_SUCCESS );
|
|
}
|
|
|
|
VOID
|
|
ObpDeferObjectDeletion (
|
|
IN POBJECT_HEADER ObjectHeader
|
|
)
|
|
{
|
|
PVOID OldValue;
|
|
//
|
|
// Push this object on the list. If we make an empty to non-empty
|
|
// transition then we may have to start a worker thread.
|
|
//
|
|
|
|
while (1) {
|
|
OldValue = ObpRemoveObjectList;
|
|
ObjectHeader->NextToFree = OldValue;
|
|
if (InterlockedCompareExchangePointer (&ObpRemoveObjectList,
|
|
ObjectHeader,
|
|
OldValue) == OldValue) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (OldValue == NULL) {
|
|
//
|
|
// If we have to start the worker thread then go ahead
|
|
// and enqueue the work item
|
|
//
|
|
|
|
ExQueueWorkItem( &ObpRemoveObjectWorkItem, CriticalWorkQueue );
|
|
}
|
|
|
|
}
|
|
|
|
|
|
LONG_PTR
|
|
FASTCALL
|
|
ObfReferenceObject (
|
|
IN PVOID Object
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function increments the reference count for an object.
|
|
|
|
N.B. This function should be used to increment the reference count
|
|
when the accessing mode is kernel or the objct type is known.
|
|
|
|
Arguments:
|
|
|
|
Object - Supplies a pointer to the object whose reference count is
|
|
incremented.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
POBJECT_HEADER ObjectHeader;
|
|
LONG_PTR RetVal;
|
|
|
|
ObjectHeader = OBJECT_TO_OBJECT_HEADER( Object );
|
|
|
|
RetVal = ObpIncrPointerCount( ObjectHeader );
|
|
ASSERT (RetVal != 1);
|
|
return RetVal;
|
|
}
|
|
|
|
LONG_PTR
|
|
FASTCALL
|
|
ObReferenceObjectEx (
|
|
IN PVOID Object,
|
|
IN ULONG Count
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function increments the reference count for an object by the specified amount.
|
|
|
|
Arguments:
|
|
|
|
Object - Supplies a pointer to the object whose reference count is
|
|
incremented.
|
|
Count - Amount to increment by
|
|
|
|
Return Value:
|
|
|
|
LONG - New value of count
|
|
|
|
--*/
|
|
{
|
|
POBJECT_HEADER ObjectHeader;
|
|
|
|
ObjectHeader = OBJECT_TO_OBJECT_HEADER( Object );
|
|
return ObpIncrPointerCountEx (ObjectHeader, Count);
|
|
}
|
|
|
|
LONG_PTR
|
|
FASTCALL
|
|
ObDereferenceObjectEx (
|
|
IN PVOID Object,
|
|
IN ULONG Count
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function decrements the reference count for an object by the specified amount.
|
|
|
|
Arguments:
|
|
|
|
Object - Supplies a pointer to the object whose reference count is
|
|
incremented.
|
|
Count - Amount to decrement by
|
|
|
|
Return Value:
|
|
|
|
LONG - New value of count
|
|
|
|
--*/
|
|
{
|
|
POBJECT_HEADER ObjectHeader;
|
|
LONG_PTR Result;
|
|
|
|
ObjectHeader = OBJECT_TO_OBJECT_HEADER( Object );
|
|
Result = ObpDecrPointerCountEx (ObjectHeader, Count);
|
|
if (Result == 0) {
|
|
ObpDeferObjectDeletion (ObjectHeader);
|
|
}
|
|
return Result;
|
|
}
|
|
|
|
|
|
|
|
BOOLEAN
|
|
FASTCALL
|
|
ObReferenceObjectSafe (
|
|
IN PVOID Object
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function increments the reference count for an object. It returns
|
|
FALSE if the object is being deleted or TRUE if it's safe to use the object further
|
|
|
|
Arguments:
|
|
|
|
Object - Supplies a pointer to the object whose reference count is
|
|
incremented.
|
|
|
|
Return Value:
|
|
|
|
TRUE - The object was successfuly referenced and safe to use
|
|
FALSE - The object is being deleted
|
|
|
|
--*/
|
|
|
|
{
|
|
POBJECT_HEADER ObjectHeader;
|
|
|
|
ObjectHeader = OBJECT_TO_OBJECT_HEADER( Object );
|
|
|
|
if (ObpSafeInterlockedIncrement(&ObjectHeader->PointerCount)) {
|
|
|
|
#ifdef POOL_TAGGING
|
|
if(ObpTraceEnabled) {
|
|
ObpPushStackInfo(ObjectHeader, TRUE);
|
|
}
|
|
#endif // POOL_TAGGING
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
|
|
LONG_PTR
|
|
FASTCALL
|
|
ObfDereferenceObject (
|
|
IN PVOID Object
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine decrments the refernce count of the specified object and
|
|
does whatever cleanup there is if the count goes to zero.
|
|
|
|
Arguments:
|
|
|
|
Object - Supplies a pointer to the body of the object being dereferenced
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
POBJECT_HEADER ObjectHeader;
|
|
POBJECT_TYPE ObjectType;
|
|
KIRQL OldIrql;
|
|
LONG_PTR Result;
|
|
|
|
//
|
|
// Translate a pointer to the object body to a pointer to the object
|
|
// header.
|
|
//
|
|
|
|
ObjectHeader = OBJECT_TO_OBJECT_HEADER( Object );
|
|
|
|
#if DBG
|
|
{
|
|
POBJECT_HEADER_NAME_INFO NameInfo;
|
|
|
|
NameInfo = OBJECT_HEADER_TO_NAME_INFO( ObjectHeader );
|
|
|
|
if (NameInfo) {
|
|
|
|
InterlockedDecrement(&NameInfo->DbgDereferenceCount) ;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
//
|
|
// Decrement the point count and if the result is now then
|
|
// there is extra work to do
|
|
//
|
|
|
|
ObjectType = ObjectHeader->Type;
|
|
|
|
|
|
Result = ObpDecrPointerCount( ObjectHeader );
|
|
|
|
if (Result == 0) {
|
|
|
|
//
|
|
// Find out the level we're at and the object type
|
|
//
|
|
|
|
OldIrql = KeGetCurrentIrql();
|
|
|
|
ASSERT(ObjectHeader->HandleCount == 0);
|
|
|
|
//
|
|
// If we're at the passive level then go ahead and delete the
|
|
// object now.
|
|
//
|
|
|
|
if (OldIrql == PASSIVE_LEVEL) {
|
|
|
|
#ifdef POOL_TAGGING
|
|
//
|
|
// The object is going away, so we deregister it.
|
|
//
|
|
|
|
if (ObpTraceEnabled && !ObpTraceNoDeregister) {
|
|
|
|
ObpDeregisterObject( ObjectHeader );
|
|
}
|
|
#endif //POOL_TAGGING
|
|
|
|
ObpRemoveObjectRoutine( Object, FALSE );
|
|
|
|
return Result;
|
|
|
|
} else {
|
|
|
|
//
|
|
// Objects can't be deleted from an IRQL above PASSIVE_LEVEL.
|
|
// So queue the delete operation.
|
|
//
|
|
|
|
ObpDeferObjectDeletion (ObjectHeader);
|
|
}
|
|
}
|
|
|
|
return Result;
|
|
}
|
|
|
|
VOID
|
|
ObDereferenceObjectDeferDelete (
|
|
IN PVOID Object
|
|
)
|
|
{
|
|
POBJECT_HEADER ObjectHeader;
|
|
LONG_PTR Result;
|
|
|
|
#if DBG
|
|
POBJECT_HEADER_NAME_INFO NameInfo;
|
|
#endif
|
|
|
|
//
|
|
// Translate a pointer to the object body to a pointer to the object
|
|
// header.
|
|
//
|
|
|
|
ObjectHeader = OBJECT_TO_OBJECT_HEADER( Object );
|
|
|
|
#if DBG
|
|
NameInfo = OBJECT_HEADER_TO_NAME_INFO( ObjectHeader );
|
|
|
|
if (NameInfo) {
|
|
|
|
InterlockedDecrement(&NameInfo->DbgDereferenceCount) ;
|
|
}
|
|
#endif
|
|
|
|
//
|
|
// Decrement the point count and if the result is now then
|
|
// there is extra work to do
|
|
//
|
|
|
|
Result = ObpDecrPointerCount( ObjectHeader );
|
|
|
|
if (Result == 0) {
|
|
ObpDeferObjectDeletion (ObjectHeader);
|
|
}
|
|
}
|
|
|
|
|
|
VOID
|
|
ObpProcessRemoveObjectQueue (
|
|
PVOID Parameter
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is the work routine for the remove object work queue. Its
|
|
job is to remove and process items from the remove object queue.
|
|
|
|
Arguments:
|
|
|
|
Parameter - Ignored
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
POBJECT_HEADER ObjectHeader, NextObject;
|
|
|
|
UNREFERENCED_PARAMETER (Parameter);
|
|
//
|
|
// Process the list of defered delete objects.
|
|
// The list head serves two purposes. First it maintains
|
|
// the list of objects we need to delete and second
|
|
// it signals that this thread is active.
|
|
// While we are processing the latest list we leave the
|
|
// header as the value 1. This will never be an object address
|
|
// as the bottom bits should be clear for an object.
|
|
//
|
|
while (1) {
|
|
ObjectHeader = InterlockedExchangePointer (&ObpRemoveObjectList,
|
|
(PVOID) 1);
|
|
while (1) {
|
|
#ifdef POOL_TAGGING
|
|
if (ObpTraceEnabled && !ObpTraceNoDeregister) {
|
|
|
|
ObpDeregisterObject( ObjectHeader );
|
|
}
|
|
#endif
|
|
NextObject = ObjectHeader->NextToFree;
|
|
ObpRemoveObjectRoutine( &ObjectHeader->Body, TRUE );
|
|
ObjectHeader = NextObject;
|
|
if (ObjectHeader == NULL || ObjectHeader == (PVOID) 1) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (ObpRemoveObjectList == (PVOID) 1 &&
|
|
InterlockedCompareExchangePointer (&ObpRemoveObjectList,
|
|
NULL,
|
|
(PVOID) 1) == (PVOID) 1) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
VOID
|
|
ObpRemoveObjectRoutine (
|
|
IN PVOID Object,
|
|
IN BOOLEAN CalledOnWorkerThread
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is used to delete an object whose reference count has
|
|
gone to zero.
|
|
|
|
Arguments:
|
|
|
|
Object - Supplies a pointer to the body of the object being deleted
|
|
|
|
CalledOnWorkerThread - TRUE if called on worker thread, FALSE if called in
|
|
the context of the ObDereferenceObject.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
POBJECT_HEADER ObjectHeader;
|
|
POBJECT_TYPE ObjectType;
|
|
POBJECT_HEADER_CREATOR_INFO CreatorInfo;
|
|
POBJECT_HEADER_NAME_INFO NameInfo;
|
|
|
|
PAGED_CODE();
|
|
|
|
ObpValidateIrql( "ObpRemoveObjectRoutine" );
|
|
|
|
//
|
|
// Retrieve an object header from the object body, and also get
|
|
// the object type, creator and name info if available
|
|
//
|
|
|
|
ObjectHeader = OBJECT_TO_OBJECT_HEADER( Object );
|
|
ObjectType = ObjectHeader->Type;
|
|
CreatorInfo = OBJECT_HEADER_TO_CREATOR_INFO( ObjectHeader );
|
|
NameInfo = OBJECT_HEADER_TO_NAME_INFO( ObjectHeader );
|
|
|
|
|
|
//
|
|
// If there is a creator info record and we are on the list
|
|
// for the object type then remove this object from the list
|
|
//
|
|
|
|
if (CreatorInfo != NULL && !IsListEmpty( &CreatorInfo->TypeList )) {
|
|
|
|
//
|
|
// Get exclusive access to the object type object
|
|
//
|
|
|
|
ObpEnterObjectTypeMutex( ObjectType );
|
|
|
|
RemoveEntryList( &CreatorInfo->TypeList );
|
|
|
|
//
|
|
// We are done with the object type object so we can now release it
|
|
//
|
|
|
|
ObpLeaveObjectTypeMutex( ObjectType );
|
|
}
|
|
|
|
//
|
|
// If there is a name info record and the name buffer is not null
|
|
// then free the buffer and zero out the name record
|
|
//
|
|
|
|
if (NameInfo != NULL && NameInfo->Name.Buffer != NULL) {
|
|
|
|
ExFreePool( NameInfo->Name.Buffer );
|
|
|
|
NameInfo->Name.Buffer = NULL;
|
|
NameInfo->Name.Length = 0;
|
|
NameInfo->Name.MaximumLength = 0;
|
|
}
|
|
|
|
|
|
//
|
|
// Security descriptor deletion must precede the
|
|
// call to the object's DeleteProcedure. Check if we have
|
|
// a security descriptor and if so then call the routine
|
|
// to delete the security descritpor.
|
|
//
|
|
|
|
if (ObjectHeader->SecurityDescriptor != NULL) {
|
|
|
|
#if DBG
|
|
KIRQL SaveIrql;
|
|
#endif
|
|
|
|
ObpBeginTypeSpecificCallOut( SaveIrql );
|
|
|
|
Status = (ObjectType->TypeInfo.SecurityProcedure)( Object,
|
|
DeleteSecurityDescriptor,
|
|
NULL, NULL, NULL,
|
|
&ObjectHeader->SecurityDescriptor,
|
|
0,
|
|
NULL );
|
|
|
|
ObpEndTypeSpecificCallOut( SaveIrql, "Security", ObjectType, Object );
|
|
}
|
|
|
|
//
|
|
// Now if there is a delete callback for the object type invoke
|
|
// the routine
|
|
//
|
|
|
|
if (ObjectType->TypeInfo.DeleteProcedure) {
|
|
|
|
#if DBG
|
|
KIRQL SaveIrql;
|
|
#endif
|
|
|
|
ObpBeginTypeSpecificCallOut( SaveIrql );
|
|
|
|
if (!CalledOnWorkerThread) {
|
|
|
|
ObjectHeader->Flags |= OB_FLAG_DELETED_INLINE;
|
|
}
|
|
|
|
(*(ObjectType->TypeInfo.DeleteProcedure))(Object);
|
|
|
|
ObpEndTypeSpecificCallOut( SaveIrql, "Delete", ObjectType, Object );
|
|
}
|
|
|
|
//
|
|
// Finally return the object back to pool including releasing any quota
|
|
// charges
|
|
//
|
|
|
|
ObpFreeObject( Object );
|
|
}
|
|
|
|
|
|
VOID
|
|
ObpDeleteNameCheck (
|
|
IN PVOID Object
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine removes the name of an object from its parent directory
|
|
|
|
Arguments:
|
|
|
|
Object - Supplies a pointer to the object body whose name is being checked
|
|
|
|
TypeMutexHeld - Indicates if the lock on object type is being held by the
|
|
caller
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
POBJECT_HEADER ObjectHeader;
|
|
POBJECT_TYPE ObjectType;
|
|
POBJECT_HEADER_NAME_INFO NameInfo;
|
|
PVOID DirObject;
|
|
OBP_LOOKUP_CONTEXT LookupContext;
|
|
|
|
PAGED_CODE();
|
|
|
|
ObpValidateIrql( "ObpDeleteNameCheck" );
|
|
|
|
//
|
|
// Translate the object body to an object header also get
|
|
// the object type and name info if present
|
|
//
|
|
|
|
ObjectHeader = OBJECT_TO_OBJECT_HEADER( Object );
|
|
NameInfo = ObpReferenceNameInfo( ObjectHeader );
|
|
ObjectType = ObjectHeader->Type;
|
|
|
|
//
|
|
// Make sure that the object has a zero handle count, has a non
|
|
// empty name buffer, and is not a permanent object
|
|
//
|
|
|
|
if ((ObjectHeader->HandleCount == 0) &&
|
|
(NameInfo != NULL) &&
|
|
(NameInfo->Name.Length != 0) &&
|
|
(!(ObjectHeader->Flags & OB_FLAG_PERMANENT_OBJECT)) &&
|
|
(NameInfo->Directory != NULL)) {
|
|
|
|
ObpInitializeLookupContext(&LookupContext);
|
|
ObpLockLookupContext ( &LookupContext, NameInfo->Directory );
|
|
|
|
DirObject = NULL;
|
|
|
|
//
|
|
// Check that the object we is still in the directory otherwise
|
|
// then is nothing for us to remove
|
|
//
|
|
|
|
if (Object == ObpLookupDirectoryEntry( NameInfo->Directory,
|
|
&NameInfo->Name,
|
|
0,
|
|
FALSE,
|
|
&LookupContext )) {
|
|
|
|
//
|
|
// Now reacquire the lock on the object type and
|
|
// check check the handle count again. If it is still
|
|
// zero then we can do the actual delete name operation
|
|
//
|
|
//
|
|
// Delete the directory entry, if the entry is still there
|
|
//
|
|
|
|
ObpLockObject( ObjectHeader );
|
|
|
|
if (ObjectHeader->HandleCount == 0 &&
|
|
(ObjectHeader->Flags & OB_FLAG_PERMANENT_OBJECT) == 0) {
|
|
|
|
//
|
|
// Delete the directory entry
|
|
//
|
|
|
|
ObpDeleteDirectoryEntry( &LookupContext );
|
|
|
|
//
|
|
// If this is a symbolic link object then we also need to
|
|
// delete the symbolic link
|
|
//
|
|
|
|
if (ObjectType == ObpSymbolicLinkObjectType) {
|
|
|
|
ObpDeleteSymbolicLinkName( (POBJECT_SYMBOLIC_LINK)Object );
|
|
}
|
|
|
|
DirObject = NameInfo->Directory;
|
|
}
|
|
|
|
ObpUnlockObject( ObjectHeader );
|
|
}
|
|
|
|
ObpReleaseLookupContext( &LookupContext );
|
|
|
|
//
|
|
// If there is a directory object for the name then decrement
|
|
// its reference count for it and for the object
|
|
//
|
|
|
|
if (DirObject != NULL) {
|
|
|
|
//
|
|
// Dereference the name twice: one because we referenced it to
|
|
// saftely access the name info, and the second deref is because
|
|
// we want a deletion for the NameInfo
|
|
//
|
|
|
|
ObpDereferenceNameInfo(NameInfo);
|
|
ObpDereferenceNameInfo(NameInfo);
|
|
|
|
ObDereferenceObject( Object );
|
|
}
|
|
|
|
} else {
|
|
|
|
ObpDereferenceNameInfo(NameInfo);
|
|
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
//
|
|
// Thunks to support standard call callers
|
|
//
|
|
|
|
#ifdef ObDereferenceObject
|
|
#undef ObDereferenceObject
|
|
#endif
|
|
|
|
LONG_PTR
|
|
ObDereferenceObject (
|
|
IN PVOID Object
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is really just a thunk for the Obf version of the dereference
|
|
routine
|
|
|
|
Arguments:
|
|
|
|
Object - Supplies a pointer to the body of the object being dereferenced
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
return ObfDereferenceObject (Object) ;
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
ObIsObjectDeletionInline(
|
|
IN PVOID Object
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is available only of object DeleteProcedure callbacks. It allows the
|
|
callback to determine whether the stack on which it is invoked is
|
|
Arguments:
|
|
|
|
Object - Supplies a pointer to the body of the object being deleted
|
|
|
|
Return Value:
|
|
|
|
TRUE if the deletion procedure is being invoked on the same stack as the
|
|
ObDereferenceObject, and FALSE if the procedure is being invoked from a
|
|
queued work item.
|
|
|
|
--*/
|
|
{
|
|
POBJECT_HEADER ObjectHeader;
|
|
|
|
ObjectHeader = OBJECT_TO_OBJECT_HEADER( Object );
|
|
|
|
return ((ObjectHeader->Flags & OB_FLAG_DELETED_INLINE) != 0);
|
|
}
|
|
|