2020-09-24 20:51:15 +00:00
|
|
|
/*++
|
|
|
|
|
|
|
|
Copyright (c) Microsoft Corporation
|
|
|
|
|
|
|
|
Module Name:
|
|
|
|
|
|
|
|
FxObject.hpp
|
|
|
|
|
|
|
|
Abstract:
|
|
|
|
|
|
|
|
This is the C++ header for the FxObject
|
|
|
|
|
|
|
|
Author:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Revision History:
|
|
|
|
|
|
|
|
|
|
|
|
Made mode agnostic
|
|
|
|
|
|
|
|
IMPORTANT: Common code must call Initialize method of
|
|
|
|
FxObject before using it
|
|
|
|
|
|
|
|
Cannot put CreateAndInitialize method on this class as it
|
|
|
|
cannot be instantiated
|
|
|
|
|
|
|
|
|
|
|
|
--*/
|
|
|
|
|
|
|
|
#ifndef _FXOBJECT_H_
|
|
|
|
#define _FXOBJECT_H_
|
|
|
|
|
|
|
|
extern "C" {
|
|
|
|
|
|
|
|
#if defined(EVENT_TRACING)
|
|
|
|
#include "FxObject.hpp.tmh"
|
|
|
|
#endif
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// Macros to pass line and file information to AddRef and Release calls.
|
|
|
|
//
|
|
|
|
// NOTE: ADDREF and RELEASE can be used by any class derived from FxObject or
|
|
|
|
// IFxMemory while the OFFSET only works with an FxObject derivation
|
|
|
|
//
|
|
|
|
#define ADDREF(_tag) AddRef(_tag, __LINE__, __FILE__)
|
|
|
|
#define RELEASE(_tag) Release(_tag, __LINE__, __FILE__)
|
|
|
|
|
|
|
|
#define ADDREF_OFFSET(_tag, offset) AddRefOverride(offset, _tag, __LINE__, __FILE__)
|
|
|
|
#define RELEASE_OFFSET(_tag, offset) ReleaseOverride(offset, _tag, __LINE__, __FILE__)
|
|
|
|
|
|
|
|
//
|
|
|
|
// This assumes that the bottom 3 bits of an FxObject are clear, ie that the
|
|
|
|
// FxObject is 8 byte aligned. The conversion from and to a handle value
|
|
|
|
// will set / clear these bits as appropriate.
|
|
|
|
//
|
|
|
|
enum FxHandleFlags {
|
|
|
|
FxHandleFlagIsOffset = 0x1,
|
|
|
|
FxHandleFlagMask = 0x7, // bottom 3 bits are free
|
|
|
|
};
|
|
|
|
|
|
|
|
//
|
|
|
|
// We cannot define FxHandleValueMask as an enumerant in FxHandleFlags because
|
|
|
|
// an enum is limited to sizeof(ULONG), which doesn't work for us on a 64 bit OS
|
|
|
|
//
|
2020-10-16 03:30:51 +00:00
|
|
|
extern DECLSPEC_SELECTANY const ULONG_PTR FxHandleValueMask = (~((ULONG_PTR) FxHandleFlagMask));
|
2020-09-24 20:51:15 +00:00
|
|
|
|
|
|
|
//
|
|
|
|
// The type itself is aligned, but the pointer is not b/c those interested in the
|
|
|
|
// offset do not need it to be aligned.
|
|
|
|
//
|
|
|
|
// The offset is aligned on an 8 byte boundary so that we have the lower 3 bits
|
|
|
|
// of the low byte to use for a bit field
|
|
|
|
//
|
|
|
|
typedef DECLSPEC_ALIGN(8) USHORT WDFOBJECT_OFFSET_ALIGNED;
|
|
|
|
|
|
|
|
typedef USHORT WDFOBJECT_OFFSET, *PWDFOBJECT_OFFSET;
|
|
|
|
|
|
|
|
struct FxQueryInterfaceParams {
|
|
|
|
//
|
|
|
|
// We intentionally do not declare a constructor for this structure.
|
|
|
|
// Instead, code should use an inline initializer list. This reduces the
|
|
|
|
// number of cycles on hot paths by removing a funciton call.
|
|
|
|
//
|
|
|
|
// FxQueryInterfaceParams(
|
|
|
|
// PVOID* Object,
|
|
|
|
// WDFTYPE Type
|
|
|
|
// ) :
|
|
|
|
// Type(Type), Object(Object), Offset(0) {if (Object != NULL) *Object = NULL; }}
|
|
|
|
|
|
|
|
//
|
|
|
|
// Object to query for
|
|
|
|
//
|
|
|
|
PVOID* Object;
|
|
|
|
|
|
|
|
//
|
|
|
|
// Type for the object to query for
|
|
|
|
//
|
|
|
|
WDFTYPE Type;
|
|
|
|
|
|
|
|
//
|
|
|
|
// Offset of handle within its owning object. If zero, the Object was the
|
|
|
|
// handle. If not zero, ((PUCHAR) Object)-Offset will yield the owning
|
|
|
|
// Object.
|
|
|
|
//
|
|
|
|
WDFOBJECT_OFFSET Offset;
|
|
|
|
};
|
|
|
|
|
|
|
|
//
|
|
|
|
// type of object being allocated. An internal object does *NOT*
|
|
|
|
// 1) have its size rounded up to an alignment value
|
|
|
|
// 2) extra size and context header appended to the allocation
|
|
|
|
//
|
2020-10-16 03:30:51 +00:00
|
|
|
enum FxObjectType : UINT32 {
|
2020-09-24 20:51:15 +00:00
|
|
|
FxObjectTypeInvalid = 0,
|
|
|
|
FxObjectTypeInternal,
|
|
|
|
FxObjectTypeExternal,
|
|
|
|
FxObjectTypeEmbedded,
|
|
|
|
};
|
|
|
|
|
|
|
|
// Ensures that a BOOL type is generated from a flag mask
|
|
|
|
#define FLAG_TO_BOOL(_Flags, _FlagMask) (!!((_Flags) & (_FlagMask)))
|
|
|
|
|
|
|
|
enum FxObjectLockState {
|
|
|
|
ObjectDoNotLock = 0,
|
|
|
|
ObjectLock = 1
|
|
|
|
};
|
|
|
|
|
|
|
|
//
|
|
|
|
// Defines for FxObject::m_ObjectFlags
|
|
|
|
//
|
|
|
|
// NOTE: if you modify (add, remove, reassign a value) this enum in any way
|
|
|
|
// you must also change FxObject::m_ObjectFlagsByName.* to match your changes!!
|
|
|
|
//
|
|
|
|
enum FXOBJECT_FLAGS {
|
|
|
|
FXOBJECT_FLAGS_PASSIVE_CALLBACKS = 0x00000001, // Object must have all callbacks at passive level
|
|
|
|
// implies passive destroy
|
|
|
|
FXOBJECT_FLAGS_NODELETEDDI = 0x00000002, // The WdfObjectDelete DDI is invalid
|
|
|
|
FXOBJECT_FLAGS_DELETECALLED = 0x00000004, // DeleteObject method called
|
|
|
|
FXOBJECT_FLAGS_COMMITTED = 0x00000008, // Commit called
|
|
|
|
FXOBJECT_FLAGS_PASSIVE_DISPOSE = 0x00000010, // Object must be Dispose()'d at passive level
|
|
|
|
FXOBJECT_FLAGS_FORCE_DISPOSE_THREAD = 0x00000020, // Object is always disposed in the dispose thread
|
|
|
|
// UNUSED = 0x00000040,
|
|
|
|
FXOBJECT_FLAGS_HAS_DEBUG = 0x00000080, // has FxObjectDebugExtension before object pointer
|
|
|
|
FXOBJECT_FLAGS_EARLY_DISPOSED_EXT = 0x00000100, // Early disposed externally to the state machine
|
|
|
|
FXOBJECT_FLAGS_TRACE_STATE = 0x00000200, // log state changes to the IFR
|
|
|
|
FXOBJECT_FLAGS_HAS_CLEANUP = 0x00000400,
|
|
|
|
FXOBJECT_FLAGS_DISPOSE_OVERRIDE = 0x00000800,
|
|
|
|
};
|
|
|
|
|
|
|
|
//
|
|
|
|
// FxObject state represents whether an object is
|
|
|
|
// tearing down, and what phase its in.
|
|
|
|
//
|
|
|
|
enum FxObjectState {
|
|
|
|
FxObjectStateInvalid = 0,
|
|
|
|
FxObjectStateCreated,
|
|
|
|
FxObjectStateDisposed,
|
|
|
|
FxObjectStateDisposingEarly,
|
|
|
|
FxObjectStateDisposingDisposeChildren,
|
|
|
|
FxObjectStateDeferedDisposing,
|
|
|
|
FxObjectStateDeferedDeleting,
|
|
|
|
FxObjectStateWaitingForEarlyDispose,
|
|
|
|
FxObjectStateWaitingForParentDeleteAndDisposed,
|
|
|
|
FxObjectStateDeletedDisposing,
|
|
|
|
FxObjectStateDeletedAndDisposed,
|
|
|
|
FxObjectStateDeferedDestroy,
|
|
|
|
FxObjectStateDestroyed,
|
|
|
|
FxObjectStateMaximum,
|
|
|
|
};
|
|
|
|
|
|
|
|
enum FxObjectDroppedEvent {
|
|
|
|
FxObjectDroppedEventAssignParentObject = 0,
|
|
|
|
FxObjectDroppedEventAddChildObjectInternal,
|
|
|
|
FxObjectDroppedEventRemoveChildObjectInternal,
|
|
|
|
FxObjectDroppedEventDeleteObject,
|
|
|
|
FxObjectDroppedEventPerformEarlyDispose,
|
|
|
|
FxObjectDroppedEventRemoveParentAssignment,
|
|
|
|
FxObjectDroppedEventParentDeleteEvent,
|
|
|
|
};
|
|
|
|
|
|
|
|
// begin_wpp config
|
|
|
|
// CUSTOM_TYPE(FxObjectState, ItemEnum(FxObjectState));
|
|
|
|
// CUSTOM_TYPE(FxObjectDroppedEvent, ItemEnum(FxObjectDroppedEvent));
|
|
|
|
// end_wpp
|
|
|
|
|
|
|
|
#define DECLARE_INTERNAL_NEW_OPERATOR() \
|
|
|
|
PVOID \
|
|
|
|
__inline \
|
|
|
|
operator new( \
|
|
|
|
__in size_t Size, \
|
|
|
|
__in PFX_DRIVER_GLOBALS FxDriverGlobals \
|
|
|
|
) \
|
|
|
|
{ \
|
|
|
|
return FxObjectHandleAlloc(FxDriverGlobals, \
|
|
|
|
NonPagedPool, \
|
|
|
|
Size, \
|
|
|
|
0, \
|
|
|
|
WDF_NO_OBJECT_ATTRIBUTES,\
|
|
|
|
0, \
|
|
|
|
FxObjectTypeInternal); \
|
|
|
|
}
|
|
|
|
|
|
|
|
struct FxObjectDebugExtension {
|
|
|
|
FxTagTracker* TagTracker;
|
|
|
|
|
|
|
|
FxVerifierLock* VerifierLock;
|
|
|
|
|
|
|
|
BYTE StateHistory[8];
|
|
|
|
|
|
|
|
LONG StateHistoryIndex;
|
|
|
|
|
|
|
|
//
|
|
|
|
// Signature lives after all the fields to avoid byte padding if it is
|
|
|
|
// first on 64 bit systems.
|
|
|
|
//
|
|
|
|
ULONG Signature;
|
|
|
|
|
|
|
|
DECLSPEC_ALIGN(MEMORY_ALLOCATION_ALIGNMENT) ULONG AllocationStart[1];
|
|
|
|
};
|
|
|
|
|
|
|
|
enum FxObjectDebugExtensionValues {
|
|
|
|
FxObjectDebugExtensionSize = FIELD_OFFSET(FxObjectDebugExtension, AllocationStart),
|
|
|
|
FxObjectDebugExtensionSignature = 'DOxF',
|
|
|
|
};
|
|
|
|
|
|
|
|
class UfxObject;
|
|
|
|
|
|
|
|
class FxObject {
|
|
|
|
|
|
|
|
friend UfxObject; //UMDF object wrapper
|
|
|
|
|
|
|
|
friend FxDisposeList;
|
|
|
|
friend VOID GetTriageInfo(VOID);
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
|
|
|
#if FX_CORE_MODE==FX_CORE_USER_MODE
|
|
|
|
#ifndef INLINE_WRAPPER_ALLOCATION
|
|
|
|
PVOID m_COMWrapper;
|
|
|
|
#endif
|
|
|
|
#endif
|
|
|
|
|
|
|
|
WDFTYPE m_Type;
|
|
|
|
|
|
|
|
//
|
|
|
|
// This is the already computed value of
|
|
|
|
// WDF_ALIGN_SIZE_UP(sizeof(derived object), MEMORY_ALLOCATION_ALIGNMENT) +
|
|
|
|
// WDF_ALIGN_SIZE_UP(extraContext, MEMORY_ALLOCATION_ALIGNMENT)
|
|
|
|
//
|
|
|
|
USHORT m_ObjectSize;
|
|
|
|
|
|
|
|
// Reference count is operated on with interlocked operations
|
|
|
|
LONG m_Refcnt;
|
|
|
|
|
|
|
|
PFX_DRIVER_GLOBALS m_Globals;
|
|
|
|
|
|
|
|
// Flags are protected by m_SpinLock, bits defined by the enum FXOBJECT_FLAGS
|
|
|
|
union {
|
|
|
|
USHORT m_ObjectFlags;
|
|
|
|
|
|
|
|
// Each field in this struct correspond to ascending enumerant values
|
|
|
|
// in FXOBJECT_FLAGS.
|
|
|
|
//
|
|
|
|
// NOTE: you should never touch these fields in any code, they are here
|
|
|
|
// strictly for use by the debugger extension so that it doesn't
|
|
|
|
// have to result FXOBJECT_FLAGS enumerant name strings into values.
|
|
|
|
//
|
|
|
|
struct {
|
|
|
|
USHORT PassiveCallbacks : 1;
|
|
|
|
USHORT NoDeleteDDI : 1;
|
|
|
|
USHORT DeleteCalled : 1;
|
|
|
|
USHORT Committed : 1;
|
|
|
|
USHORT PassiveDispose : 1;
|
|
|
|
USHORT ForceDisposeThread : 1;
|
|
|
|
USHORT Unused : 1;
|
|
|
|
USHORT HasDebug : 1;
|
|
|
|
USHORT EarlyDisposedExt : 1;
|
|
|
|
USHORT TraceState : 1;
|
|
|
|
} m_ObjectFlagsByName;
|
|
|
|
};
|
|
|
|
|
|
|
|
//
|
|
|
|
// m_ObjectState is protected by m_SpinLock. Contains values in the
|
|
|
|
// FxObjectState enum.
|
|
|
|
//
|
|
|
|
USHORT m_ObjectState;
|
|
|
|
|
|
|
|
// List of Child Objects
|
|
|
|
LIST_ENTRY m_ChildListHead;
|
|
|
|
|
|
|
|
// SpinLock protects m_ObjectState, m_ObjectFlags, m_ChildListHead, and m_ParentObject
|
|
|
|
MxLock m_SpinLock;
|
|
|
|
|
|
|
|
//
|
|
|
|
// Parent object when this object is a child.
|
|
|
|
//
|
|
|
|
// This is protected by the child objects m_SpinLock
|
|
|
|
//
|
|
|
|
FxObject* m_ParentObject;
|
|
|
|
|
|
|
|
//
|
|
|
|
// Link for when this object is a child
|
|
|
|
//
|
|
|
|
// This is accessed by the parent of this object, and
|
|
|
|
// protected by the parents spinlock.
|
|
|
|
//
|
|
|
|
LIST_ENTRY m_ChildEntry;
|
|
|
|
|
|
|
|
//
|
|
|
|
//
|
|
|
|
//
|
|
|
|
SINGLE_LIST_ENTRY m_DisposeSingleEntry;
|
|
|
|
|
|
|
|
protected:
|
|
|
|
union {
|
|
|
|
//
|
|
|
|
// This field is set when the object is being contructed or before
|
|
|
|
// Commit() and from then on is read only.
|
|
|
|
//
|
|
|
|
// FxDeviceBase* is used by the core object state machine. Derived
|
|
|
|
// objects might need the fuller FxDevice* (and they can safely get at
|
|
|
|
// it since they know they are a part of the derived FxDevice*).
|
|
|
|
//
|
|
|
|
CfxDeviceBase* m_DeviceBase;
|
|
|
|
CfxDevice* m_Device;
|
|
|
|
};
|
|
|
|
|
|
|
|
private:
|
|
|
|
FxObject(
|
|
|
|
VOID
|
|
|
|
)
|
|
|
|
{
|
|
|
|
// Always make the caller supply a type and size
|
|
|
|
}
|
|
|
|
|
|
|
|
// Do not specify argument names
|
|
|
|
FX_DECLARE_VF_FUNCTION_P1(
|
|
|
|
VOID,
|
|
|
|
VerifyConstruct,
|
|
|
|
_In_ BOOLEAN
|
|
|
|
);
|
|
|
|
|
|
|
|
VOID
|
|
|
|
__inline
|
|
|
|
Construct(
|
|
|
|
__in BOOLEAN Embedded
|
|
|
|
)
|
|
|
|
{
|
|
|
|
m_Refcnt = 1;
|
|
|
|
m_ObjectState = FxObjectStateCreated;
|
|
|
|
m_ObjectFlags = 0;
|
|
|
|
m_ParentObject = NULL;
|
|
|
|
|
|
|
|
InitializeListHead(&m_ChildListHead);
|
|
|
|
InitializeListHead(&m_ChildEntry);
|
|
|
|
m_DisposeSingleEntry.Next = NULL;
|
|
|
|
|
|
|
|
m_DeviceBase = NULL;
|
|
|
|
|
|
|
|
VerifyConstruct(m_Globals, Embedded);
|
|
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
|
|
__inline
|
|
|
|
SetObjectStateLocked(
|
|
|
|
__in FxObjectState NewState
|
|
|
|
)
|
|
|
|
{
|
|
|
|
if (m_ObjectFlags & FXOBJECT_FLAGS_TRACE_STATE) {
|
|
|
|
DoTraceLevelMessage(
|
|
|
|
m_Globals, TRACE_LEVEL_VERBOSE, TRACINGOBJECT,
|
|
|
|
"Object %p, WDFOBJECT %p transitioning from %!FxObjectState! to "
|
|
|
|
"%!FxObjectState!", this, GetObjectHandleUnchecked(),
|
|
|
|
m_ObjectState, NewState);
|
|
|
|
if (IsDebug()) {
|
|
|
|
FxObjectDebugExtension* pExtension;
|
|
|
|
|
|
|
|
pExtension = GetDebugExtension();
|
|
|
|
pExtension->StateHistory[
|
|
|
|
InterlockedIncrement(&pExtension->StateHistoryIndex)] =
|
|
|
|
(BYTE) NewState;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
m_ObjectState = (USHORT) NewState;
|
|
|
|
}
|
|
|
|
|
|
|
|
protected:
|
|
|
|
FxObject(
|
|
|
|
__in WDFTYPE Type,
|
|
|
|
__in USHORT Size,
|
|
|
|
__in PFX_DRIVER_GLOBALS FxDriverGlobals,
|
|
|
|
__in FxObjectType ObjectType
|
|
|
|
);
|
|
|
|
|
|
|
|
FxObjectDebugExtension*
|
|
|
|
GetDebugExtension(
|
|
|
|
VOID
|
|
|
|
)
|
|
|
|
{
|
|
|
|
return CONTAINING_RECORD(this, FxObjectDebugExtension, AllocationStart);
|
|
|
|
}
|
|
|
|
|
|
|
|
BOOLEAN
|
|
|
|
IsDebug(
|
|
|
|
VOID
|
|
|
|
)
|
|
|
|
{
|
|
|
|
return FLAG_TO_BOOL(m_ObjectFlags, FXOBJECT_FLAGS_HAS_DEBUG);
|
|
|
|
}
|
|
|
|
|
|
|
|
static
|
|
|
|
PVOID
|
|
|
|
_GetBase(
|
|
|
|
__in FxObject* Object
|
|
|
|
)
|
|
|
|
{
|
|
|
|
if (Object->IsDebug()) {
|
|
|
|
return _GetDebugBase(Object);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
return (PVOID) Object;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
|
|
AllocateTagTracker(
|
|
|
|
__in WDFTYPE Type
|
|
|
|
);
|
|
|
|
|
|
|
|
virtual
|
|
|
|
VOID
|
|
|
|
SelfDestruct(
|
|
|
|
VOID
|
|
|
|
)
|
|
|
|
{
|
|
|
|
delete this;
|
|
|
|
}
|
|
|
|
|
|
|
|
PVOID
|
|
|
|
__inline
|
|
|
|
GetObjectHandleUnchecked(
|
|
|
|
VOID
|
|
|
|
)
|
|
|
|
{
|
|
|
|
//
|
|
|
|
// We don't support offset handles in the base FxObject implementation.
|
|
|
|
// Offsets are specialized to internal objects of an FxObject
|
|
|
|
//
|
|
|
|
if (m_ObjectSize > 0) {
|
|
|
|
return _ToHandle(this);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
|
|
__inline
|
|
|
|
DestroyChildren(
|
|
|
|
VOID
|
|
|
|
)
|
|
|
|
{
|
|
|
|
PLIST_ENTRY ple;
|
|
|
|
FxObject *pChild;
|
|
|
|
|
|
|
|
while (!IsListEmpty(&m_ChildListHead)) {
|
|
|
|
ple = RemoveHeadList(&m_ChildListHead);
|
|
|
|
|
|
|
|
pChild = CONTAINING_RECORD(ple, FxObject, m_ChildEntry);
|
|
|
|
|
|
|
|
//
|
|
|
|
// Mark entry as unlinked
|
|
|
|
//
|
|
|
|
InitializeListHead(&pChild->m_ChildEntry);
|
|
|
|
|
|
|
|
//
|
|
|
|
// Inform child object of destruction. Object may be gone after return.
|
|
|
|
//
|
|
|
|
pChild->ParentDeleteEvent();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
|
|
|
#ifdef INLINE_WRAPPER_ALLOCATION
|
|
|
|
|
|
|
|
#if FX_CORE_MODE==FX_CORE_USER_MODE
|
|
|
|
static
|
|
|
|
USHORT
|
|
|
|
GetWrapperSize(
|
|
|
|
);
|
|
|
|
|
|
|
|
virtual
|
|
|
|
PVOID
|
|
|
|
GetCOMWrapper(
|
|
|
|
) = 0;
|
|
|
|
|
|
|
|
#else
|
|
|
|
static
|
|
|
|
USHORT
|
|
|
|
__inline
|
|
|
|
GetWrapperSize(
|
|
|
|
)
|
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#else
|
|
|
|
|
|
|
|
#if FX_CORE_MODE==FX_CORE_USER_MODE
|
|
|
|
PVOID GetCOMWrapper(){ return m_COMWrapper; }
|
|
|
|
|
|
|
|
void SetCOMWrapper(__drv_aliasesMem PVOID Wrapper){ m_COMWrapper = Wrapper; }
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
FxObject(
|
|
|
|
__in WDFTYPE Type,
|
|
|
|
__in USHORT Size,
|
|
|
|
__in PFX_DRIVER_GLOBALS FxDriverGlobals
|
|
|
|
);
|
|
|
|
|
|
|
|
virtual
|
|
|
|
~FxObject(
|
|
|
|
VOID
|
|
|
|
);
|
|
|
|
|
|
|
|
PVOID
|
|
|
|
__inline
|
|
|
|
operator new(
|
|
|
|
__in size_t Size,
|
|
|
|
__in PFX_DRIVER_GLOBALS FxDriverGlobals,
|
|
|
|
__in FxObjectType Type
|
|
|
|
)
|
|
|
|
{
|
|
|
|
UNREFERENCED_PARAMETER(Type);
|
|
|
|
|
|
|
|
return FxObjectHandleAlloc(FxDriverGlobals,
|
|
|
|
NonPagedPool,
|
|
|
|
Size,
|
|
|
|
0,
|
|
|
|
WDF_NO_OBJECT_ATTRIBUTES,
|
|
|
|
0,
|
|
|
|
Type);
|
|
|
|
}
|
|
|
|
|
|
|
|
PVOID
|
|
|
|
__inline
|
|
|
|
operator new(
|
|
|
|
__in size_t Size,
|
|
|
|
__in PFX_DRIVER_GLOBALS FxDriverGlobals,
|
|
|
|
__in_opt PWDF_OBJECT_ATTRIBUTES Attributes,
|
|
|
|
__in USHORT ExtraSize = 0
|
|
|
|
)
|
|
|
|
{
|
|
|
|
return FxObjectHandleAlloc(FxDriverGlobals,
|
|
|
|
NonPagedPool,
|
|
|
|
Size,
|
|
|
|
0,
|
|
|
|
Attributes,
|
|
|
|
ExtraSize,
|
|
|
|
FxObjectTypeExternal);
|
|
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
|
|
operator delete(
|
|
|
|
__in PVOID Memory
|
|
|
|
);
|
|
|
|
|
|
|
|
static
|
|
|
|
FxObject*
|
|
|
|
_FromDisposeEntry(
|
|
|
|
__in PSINGLE_LIST_ENTRY Entry
|
|
|
|
)
|
|
|
|
{
|
|
|
|
return CONTAINING_RECORD(Entry, FxObject, m_DisposeSingleEntry);
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// m_ObjectSize contains the size of the object + extra size. m_ObjectSize
|
|
|
|
// was already rounded up to the correct alignment when it was contructed.
|
|
|
|
//
|
|
|
|
#define GET_CONTEXT_HEADER() WDF_PTR_ADD_OFFSET_TYPE(this, m_ObjectSize, FxContextHeader*)
|
|
|
|
|
|
|
|
VOID
|
|
|
|
SetNoContextHeader(
|
|
|
|
VOID
|
|
|
|
)
|
|
|
|
{
|
|
|
|
m_ObjectSize = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
PVOID
|
|
|
|
__inline
|
|
|
|
GetObjectHandle(
|
|
|
|
VOID
|
|
|
|
)
|
|
|
|
{
|
|
|
|
ASSERT(GetRefCnt() > 0);
|
|
|
|
return GetObjectHandleUnchecked();
|
|
|
|
}
|
|
|
|
|
|
|
|
static
|
|
|
|
FxObject*
|
|
|
|
_GetObjectFromHandle(
|
|
|
|
__in WDFOBJECT Handle,
|
|
|
|
__inout PWDFOBJECT_OFFSET ObjectOffset
|
|
|
|
)
|
|
|
|
{
|
|
|
|
ULONG_PTR handle, flags;
|
|
|
|
|
|
|
|
handle = (ULONG_PTR) Handle;
|
|
|
|
|
|
|
|
//
|
|
|
|
// store the flags and then clear them off so we have a valid value
|
|
|
|
//
|
|
|
|
flags = FxHandleFlagMask & handle;
|
|
|
|
handle &= ~FxHandleFlagMask;
|
|
|
|
|
|
|
|
//
|
|
|
|
// It is assumed the caller has already set the offset to zero
|
|
|
|
//
|
|
|
|
// *ObjectOffset = 0;
|
|
|
|
|
|
|
|
//
|
|
|
|
// We always apply the mask
|
|
|
|
//
|
|
|
|
handle = handle ^ FxHandleValueMask;
|
|
|
|
|
|
|
|
if (flags & FxHandleFlagIsOffset) {
|
|
|
|
//
|
|
|
|
// The handle is a pointer to an offset value. Return the offset
|
|
|
|
// to the caller and then compute the object since the pointer
|
|
|
|
// to the offset is part of the object we are returning.
|
|
|
|
//
|
|
|
|
*ObjectOffset = *(PWDFOBJECT_OFFSET) handle;
|
|
|
|
|
|
|
|
return (FxObject*) (((PUCHAR) handle) - *ObjectOffset);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
//
|
|
|
|
// No offset, no verification. We pass the FxObject as the handle
|
|
|
|
//
|
|
|
|
return (FxObject*) handle;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static
|
|
|
|
PVOID
|
|
|
|
__inline
|
|
|
|
_ToHandle(
|
|
|
|
__in FxObject* Object
|
|
|
|
)
|
|
|
|
{
|
|
|
|
//
|
|
|
|
// Always XOR the constant. Faster than checking
|
|
|
|
// FxDriverGlobals->FxVerifierHandle.
|
|
|
|
//
|
|
|
|
return (PVOID) (((ULONG_PTR) Object) ^ FxHandleValueMask);
|
|
|
|
}
|
|
|
|
|
|
|
|
static
|
|
|
|
VOID
|
|
|
|
__inline
|
|
|
|
_ReferenceActual(
|
|
|
|
__in WDFOBJECT Object,
|
|
|
|
__in_opt PVOID Tag,
|
|
|
|
__in LONG Line,
|
|
|
|
__in PSTR File
|
|
|
|
)
|
|
|
|
{
|
|
|
|
FxObject* pObject;
|
|
|
|
WDFOBJECT_OFFSET offset;
|
|
|
|
|
|
|
|
offset = 0;
|
|
|
|
pObject = FxObject::_GetObjectFromHandle(Object, &offset);
|
|
|
|
|
|
|
|
if (offset == 0) {
|
|
|
|
pObject->AddRef(Tag, Line, File);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
pObject->AddRefOverride(offset, Tag, Line, File);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static
|
|
|
|
VOID
|
|
|
|
__inline
|
|
|
|
_DereferenceActual(
|
|
|
|
__in WDFOBJECT Object,
|
|
|
|
__in_opt PVOID Tag,
|
|
|
|
__in LONG Line,
|
|
|
|
__in PSTR File
|
|
|
|
)
|
|
|
|
{
|
|
|
|
FxObject* pObject;
|
|
|
|
WDFOBJECT_OFFSET offset;
|
|
|
|
|
|
|
|
offset = 0;
|
|
|
|
pObject = FxObject::_GetObjectFromHandle(Object, &offset);
|
|
|
|
|
|
|
|
if (offset == 0) {
|
|
|
|
pObject->Release(Tag, Line, File);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
pObject->ReleaseOverride(offset, Tag, Line, File);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
__inline
|
|
|
|
FxContextHeader*
|
|
|
|
GetContextHeader(
|
|
|
|
VOID
|
|
|
|
)
|
|
|
|
{
|
|
|
|
if (m_ObjectSize == 0) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
return GET_CONTEXT_HEADER();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
__inline
|
|
|
|
PFX_DRIVER_GLOBALS
|
|
|
|
GetDriverGlobals(
|
|
|
|
VOID
|
|
|
|
)
|
|
|
|
{
|
|
|
|
return m_Globals;
|
|
|
|
}
|
|
|
|
|
|
|
|
WDFTYPE
|
|
|
|
GetType(
|
|
|
|
VOID
|
|
|
|
)
|
|
|
|
{
|
|
|
|
return m_Type;
|
|
|
|
}
|
|
|
|
|
|
|
|
USHORT
|
|
|
|
GetObjectSize(
|
|
|
|
VOID
|
|
|
|
)
|
|
|
|
{
|
|
|
|
return m_ObjectSize;
|
|
|
|
}
|
|
|
|
|
|
|
|
LONG
|
|
|
|
GetRefCnt(
|
|
|
|
VOID
|
|
|
|
)
|
|
|
|
{
|
|
|
|
return m_Refcnt;
|
|
|
|
}
|
|
|
|
|
|
|
|
FxTagTracker*
|
|
|
|
GetTagTracker(
|
|
|
|
VOID
|
|
|
|
)
|
|
|
|
{
|
|
|
|
if (IsDebug()) {
|
|
|
|
return CONTAINING_RECORD(this,
|
|
|
|
FxObjectDebugExtension,
|
|
|
|
AllocationStart)->TagTracker;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
CfxDevice*
|
|
|
|
GetDevice(
|
|
|
|
VOID
|
|
|
|
)
|
|
|
|
{
|
|
|
|
return m_Device;
|
|
|
|
}
|
|
|
|
|
|
|
|
CfxDeviceBase*
|
|
|
|
GetDeviceBase(
|
|
|
|
VOID
|
|
|
|
)
|
|
|
|
{
|
|
|
|
return m_DeviceBase;
|
|
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
|
|
SetDeviceBase(
|
|
|
|
__in CfxDeviceBase* DeviceBase
|
|
|
|
)
|
|
|
|
{
|
|
|
|
m_DeviceBase = DeviceBase;
|
|
|
|
}
|
|
|
|
|
|
|
|
static
|
|
|
|
PVOID
|
|
|
|
_GetDebugBase(
|
|
|
|
__in FxObject* Object
|
|
|
|
)
|
|
|
|
{
|
|
|
|
return CONTAINING_RECORD(Object, FxObjectDebugExtension, AllocationStart);
|
|
|
|
}
|
|
|
|
|
|
|
|
__inline
|
|
|
|
VOID
|
|
|
|
CallCleanup(
|
|
|
|
VOID
|
|
|
|
)
|
|
|
|
{
|
|
|
|
if (m_ObjectFlags & FXOBJECT_FLAGS_HAS_CLEANUP) {
|
|
|
|
CallCleanupCallbacks();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ULONG
|
|
|
|
__inline
|
|
|
|
AddRef(
|
|
|
|
__in_opt PVOID Tag = NULL,
|
|
|
|
__in LONG Line = 0,
|
|
|
|
__in_opt PSTR File = NULL
|
|
|
|
)
|
|
|
|
{
|
|
|
|
FxTagTracker* pTagTracker;
|
|
|
|
ULONG c;
|
|
|
|
|
|
|
|
c = InterlockedIncrement(&m_Refcnt);
|
|
|
|
|
|
|
|
//
|
|
|
|
// Catch the transition from 0 to 1. Since the REF_OBJ starts off at 1,
|
|
|
|
// we should never have to increment to get to this value.
|
|
|
|
//
|
|
|
|
ASSERT(c > 1);
|
|
|
|
|
|
|
|
pTagTracker = GetTagTracker();
|
|
|
|
if (pTagTracker != NULL) {
|
|
|
|
pTagTracker->UpdateTagHistory(Tag, Line, File, TagAddRef, c);
|
|
|
|
}
|
|
|
|
|
|
|
|
return c;
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual
|
|
|
|
ULONG
|
|
|
|
Release(
|
|
|
|
__in_opt PVOID Tag = NULL,
|
|
|
|
__in LONG Line = 0,
|
|
|
|
__in_opt PSTR File = NULL
|
|
|
|
)
|
|
|
|
{
|
|
|
|
FxTagTracker* pTagTracker;
|
|
|
|
ULONG c;
|
|
|
|
|
|
|
|
pTagTracker = GetTagTracker();
|
|
|
|
if (pTagTracker != NULL) {
|
|
|
|
pTagTracker->UpdateTagHistory(Tag, Line, File, TagRelease, m_Refcnt - 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
c = InterlockedDecrement(&m_Refcnt);
|
|
|
|
|
|
|
|
if (c == 0) {
|
|
|
|
FinalRelease();
|
|
|
|
}
|
|
|
|
|
|
|
|
return c;
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual
|
|
|
|
ULONG
|
|
|
|
AddRefOverride(
|
|
|
|
__in WDFOBJECT_OFFSET Offset,
|
|
|
|
__in_opt PVOID Tag = NULL,
|
|
|
|
__in LONG Line = 0,
|
|
|
|
__in_opt PSTR File = NULL
|
|
|
|
)
|
|
|
|
{
|
|
|
|
UNREFERENCED_PARAMETER(Offset);
|
|
|
|
|
|
|
|
return AddRef(Tag, Line, File);
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual
|
|
|
|
ULONG
|
|
|
|
ReleaseOverride(
|
|
|
|
__in WDFOBJECT_OFFSET Offset,
|
|
|
|
__in_opt PVOID Tag = NULL,
|
|
|
|
__in LONG Line = 0,
|
|
|
|
__in_opt PSTR File = NULL
|
|
|
|
)
|
|
|
|
{
|
|
|
|
UNREFERENCED_PARAMETER(Offset);
|
|
|
|
|
|
|
|
return Release(Tag, Line, File);
|
|
|
|
}
|
|
|
|
|
|
|
|
_Must_inspect_result_
|
|
|
|
virtual
|
|
|
|
NTSTATUS
|
|
|
|
QueryInterface(
|
|
|
|
__in FxQueryInterfaceParams* Params
|
|
|
|
);
|
|
|
|
|
|
|
|
VOID
|
|
|
|
MarkTraceState(
|
|
|
|
VOID
|
|
|
|
)
|
|
|
|
{
|
|
|
|
m_ObjectFlags |= FXOBJECT_FLAGS_TRACE_STATE;
|
|
|
|
}
|
|
|
|
|
|
|
|
BOOLEAN
|
|
|
|
__inline
|
|
|
|
IsTraceState(
|
|
|
|
VOID
|
|
|
|
)
|
|
|
|
{
|
|
|
|
return FLAG_TO_BOOL(m_ObjectFlags, FXOBJECT_FLAGS_TRACE_STATE);
|
|
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
|
|
__inline
|
|
|
|
TraceDroppedEvent(
|
|
|
|
__in FxObjectDroppedEvent Event
|
|
|
|
)
|
|
|
|
{
|
|
|
|
if (IsTraceState()) {
|
|
|
|
DoTraceLevelMessage(
|
|
|
|
m_Globals, TRACE_LEVEL_INFORMATION, TRACINGOBJECT,
|
|
|
|
"Object %p, WDFOBJECT %p, state %!FxObjectState! dropping event"
|
|
|
|
" %!FxObjectDroppedEvent!",
|
|
|
|
this, GetObjectHandleUnchecked(), m_ObjectState, Event);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
|
|
MarkPassiveDispose(
|
|
|
|
__in FxObjectLockState State = ObjectLock
|
|
|
|
)
|
|
|
|
{
|
|
|
|
//
|
|
|
|
// Object which can have > passive level locks, but needs to be Dispose()'d
|
|
|
|
// at passive level. This means that the object's client cleanup
|
|
|
|
// routines will also be guaranteed to run at passive.
|
|
|
|
//
|
|
|
|
if (State == ObjectLock) {
|
|
|
|
KIRQL oldIrql;
|
|
|
|
|
|
|
|
m_SpinLock.Acquire(&oldIrql);
|
|
|
|
m_ObjectFlags |= FXOBJECT_FLAGS_PASSIVE_DISPOSE;
|
|
|
|
m_SpinLock.Release(oldIrql);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
m_ObjectFlags |= FXOBJECT_FLAGS_PASSIVE_DISPOSE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// Sets that the object is a passive level only object who
|
|
|
|
// accesses page-able pool, routines, or has a driver
|
|
|
|
// specified passive constraint on callbacks such as
|
|
|
|
// Cleanup and Destroy.
|
|
|
|
//
|
|
|
|
VOID
|
|
|
|
MarkPassiveCallbacks(
|
|
|
|
__in FxObjectLockState State = ObjectLock
|
|
|
|
)
|
|
|
|
{
|
|
|
|
if (State == ObjectLock) {
|
|
|
|
KIRQL oldIrql;
|
|
|
|
|
|
|
|
m_SpinLock.Acquire(&oldIrql);
|
|
|
|
m_ObjectFlags |= FXOBJECT_FLAGS_PASSIVE_CALLBACKS | FXOBJECT_FLAGS_PASSIVE_DISPOSE;
|
|
|
|
m_SpinLock.Release(oldIrql);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
m_ObjectFlags |= FXOBJECT_FLAGS_PASSIVE_CALLBACKS | FXOBJECT_FLAGS_PASSIVE_DISPOSE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
|
|
MarkForceDisposeThread(
|
|
|
|
__in FxObjectLockState State = ObjectLock
|
|
|
|
)
|
|
|
|
{
|
|
|
|
//
|
|
|
|
// Object must always be disposed in a separate thread
|
|
|
|
// to allow waiting for some outstanding async
|
|
|
|
// operation to complete.
|
|
|
|
//
|
|
|
|
if (State == ObjectLock) {
|
|
|
|
KIRQL oldIrql;
|
|
|
|
|
|
|
|
m_SpinLock.Acquire(&oldIrql);
|
|
|
|
m_ObjectFlags |= FXOBJECT_FLAGS_FORCE_DISPOSE_THREAD;
|
|
|
|
m_SpinLock.Release(oldIrql);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
m_ObjectFlags |= FXOBJECT_FLAGS_FORCE_DISPOSE_THREAD;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
BOOLEAN
|
|
|
|
IsPassiveCallbacks(
|
|
|
|
__in BOOLEAN AcquireLock = TRUE
|
|
|
|
)
|
|
|
|
{
|
|
|
|
BOOLEAN result;
|
|
|
|
KIRQL oldIrql = PASSIVE_LEVEL;
|
|
|
|
|
|
|
|
if (AcquireLock) {
|
|
|
|
m_SpinLock.Acquire(&oldIrql);
|
|
|
|
}
|
|
|
|
|
|
|
|
result = IsPassiveCallbacksLocked();
|
|
|
|
|
|
|
|
if (AcquireLock) {
|
|
|
|
m_SpinLock.Release(oldIrql);
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
BOOLEAN
|
|
|
|
IsPassiveDispose(
|
|
|
|
__in BOOLEAN AcquireLock = TRUE
|
|
|
|
)
|
|
|
|
{
|
|
|
|
BOOLEAN result;
|
|
|
|
KIRQL oldIrql = PASSIVE_LEVEL;
|
|
|
|
|
|
|
|
if (AcquireLock) {
|
|
|
|
m_SpinLock.Acquire(&oldIrql);
|
|
|
|
}
|
|
|
|
|
|
|
|
result = IsPassiveDisposeLocked();
|
|
|
|
|
|
|
|
if (AcquireLock) {
|
|
|
|
m_SpinLock.Release(oldIrql);
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
BOOLEAN
|
|
|
|
IsForceDisposeThread(
|
|
|
|
__in BOOLEAN AcquireLock = TRUE
|
|
|
|
)
|
|
|
|
{
|
|
|
|
BOOLEAN result;
|
|
|
|
KIRQL oldIrql = PASSIVE_LEVEL;
|
|
|
|
|
|
|
|
if (AcquireLock) {
|
|
|
|
m_SpinLock.Acquire(&oldIrql);
|
|
|
|
}
|
|
|
|
|
|
|
|
result = IsForceDisposeThreadLocked();
|
|
|
|
|
|
|
|
if (AcquireLock) {
|
|
|
|
m_SpinLock.Release(oldIrql);
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
|
|
MarkCommitted(
|
|
|
|
VOID
|
|
|
|
)
|
|
|
|
{
|
|
|
|
//
|
|
|
|
// Since no client code is accessing the handle yet, we don't need to
|
|
|
|
// acquire the object state lock to set the flag since this will be
|
|
|
|
// the only thread touching m_ObjectFlags.
|
|
|
|
//
|
|
|
|
m_ObjectFlags |= FXOBJECT_FLAGS_COMMITTED;
|
|
|
|
}
|
|
|
|
|
|
|
|
BOOLEAN
|
|
|
|
IsCommitted(
|
|
|
|
VOID
|
|
|
|
)
|
|
|
|
{
|
|
|
|
//
|
|
|
|
// No need to acquire the lock because it is assumed the caller is
|
|
|
|
// calling on an object whose ref count has gone to zero so there are
|
|
|
|
// no other callers to contend with who might set this flag or modify
|
|
|
|
// m_ObjectFlags.
|
|
|
|
//
|
|
|
|
return FLAG_TO_BOOL(m_ObjectFlags, FXOBJECT_FLAGS_COMMITTED);
|
|
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
|
|
MarkDisposeOverride(
|
|
|
|
__in FxObjectLockState State = ObjectLock
|
|
|
|
)
|
|
|
|
{
|
|
|
|
if (State == ObjectLock) {
|
|
|
|
KIRQL irql;
|
|
|
|
|
|
|
|
m_SpinLock.Acquire(&irql);
|
|
|
|
m_ObjectFlags |= FXOBJECT_FLAGS_DISPOSE_OVERRIDE;
|
|
|
|
m_SpinLock.Release(irql);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
m_ObjectFlags |= FXOBJECT_FLAGS_DISPOSE_OVERRIDE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
|
|
MarkNoDeleteDDI(
|
|
|
|
__in FxObjectLockState State = ObjectLock
|
|
|
|
)
|
|
|
|
{
|
|
|
|
if (State == ObjectLock) {
|
|
|
|
KIRQL irql;
|
|
|
|
|
|
|
|
m_SpinLock.Acquire(&irql);
|
|
|
|
m_ObjectFlags |= FXOBJECT_FLAGS_NODELETEDDI;
|
|
|
|
m_SpinLock.Release(irql);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
m_ObjectFlags |= FXOBJECT_FLAGS_NODELETEDDI;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
BOOLEAN
|
|
|
|
IsNoDeleteDDI(
|
|
|
|
VOID
|
|
|
|
)
|
|
|
|
{
|
|
|
|
// No need for lock since its only set in constructor/init
|
|
|
|
return FLAG_TO_BOOL(m_ObjectFlags, FXOBJECT_FLAGS_NODELETEDDI);
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// Commit the WDF object before returning handle to the caller.
|
|
|
|
//
|
|
|
|
_Must_inspect_result_
|
|
|
|
NTSTATUS
|
|
|
|
Commit(
|
|
|
|
__in_opt PWDF_OBJECT_ATTRIBUTES Attributes,
|
|
|
|
__out_opt WDFOBJECT* ObjectHandle,
|
|
|
|
__in_opt FxObject* Parent = NULL,
|
|
|
|
__in BOOLEAN AssignDriverAsDefaultParent = TRUE
|
|
|
|
);
|
|
|
|
|
|
|
|
VOID
|
|
|
|
DeleteFromFailedCreate(
|
|
|
|
VOID
|
|
|
|
);
|
|
|
|
|
|
|
|
VOID
|
|
|
|
ClearEvtCallbacks(
|
|
|
|
VOID
|
|
|
|
);
|
|
|
|
|
|
|
|
BOOLEAN
|
|
|
|
EarlyDispose(
|
|
|
|
VOID
|
|
|
|
);
|
|
|
|
|
|
|
|
//
|
|
|
|
// Request that an object be deleted.
|
|
|
|
//
|
|
|
|
// This can be the result of a WDF API or a WDM event.
|
|
|
|
//
|
|
|
|
virtual
|
|
|
|
VOID
|
|
|
|
DeleteObject(
|
|
|
|
VOID
|
|
|
|
);
|
|
|
|
|
|
|
|
//
|
|
|
|
// Invoked by FxObject *once* when the object is either
|
|
|
|
// being deleted, or rundown due to its parent object
|
|
|
|
// being deleted.
|
|
|
|
//
|
|
|
|
// An object can override this to perform per object
|
|
|
|
// cleanup if required.
|
|
|
|
//
|
|
|
|
// TRUE means that the cleanup callbacks should be called when the function
|
|
|
|
// returns. FALSE indicates that the caller will take care of calling the
|
|
|
|
// cleanup callbacks on behalf of the state machine.
|
|
|
|
//
|
|
|
|
virtual
|
|
|
|
BOOLEAN
|
|
|
|
Dispose(
|
|
|
|
VOID
|
|
|
|
);
|
|
|
|
|
|
|
|
//
|
|
|
|
// Request to make ParentObject the parent for this object.
|
|
|
|
//
|
|
|
|
_Must_inspect_result_
|
|
|
|
NTSTATUS
|
|
|
|
AssignParentObject(
|
|
|
|
__in FxObject* ParentObject
|
|
|
|
);
|
|
|
|
|
|
|
|
_Must_inspect_result_
|
|
|
|
NTSTATUS
|
|
|
|
AddContext(
|
|
|
|
__in FxContextHeader *Header,
|
|
|
|
__in PVOID* Context,
|
|
|
|
__in PWDF_OBJECT_ATTRIBUTES Attributes
|
|
|
|
);
|
|
|
|
|
|
|
|
//
|
|
|
|
// Request that this Object be removed from the child association
|
|
|
|
// list for its parent
|
|
|
|
//
|
|
|
|
_Must_inspect_result_
|
|
|
|
NTSTATUS
|
|
|
|
RemoveParentAssignment(
|
|
|
|
VOID
|
|
|
|
);
|
|
|
|
|
|
|
|
//
|
|
|
|
// Adds a reference to the parent object pointer if != NULL
|
|
|
|
//
|
|
|
|
_Must_inspect_result_
|
|
|
|
FxObject*
|
|
|
|
GetParentObjectReferenced(
|
|
|
|
__in PVOID Tag
|
|
|
|
);
|
|
|
|
|
|
|
|
//
|
|
|
|
// This is public to allow debug code to assert that
|
|
|
|
// an object has been properly disposed either through
|
|
|
|
// calling DeleteObject, or being disposed by its parent.
|
|
|
|
//
|
|
|
|
BOOLEAN
|
|
|
|
IsDisposed(
|
|
|
|
VOID
|
|
|
|
)
|
|
|
|
{
|
|
|
|
KIRQL oldIrql;
|
|
|
|
BOOLEAN disposed;
|
|
|
|
|
|
|
|
if (m_Globals->FxVerifierOn &&
|
|
|
|
m_Globals->FxVerifierHandle) {
|
|
|
|
m_SpinLock.Acquire(&oldIrql);
|
|
|
|
|
|
|
|
if (m_ObjectState == FxObjectStateCreated) {
|
|
|
|
disposed = FALSE;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
//
|
|
|
|
// Parent is disposing us, or we are being disposed
|
|
|
|
//
|
|
|
|
disposed = TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
m_SpinLock.Release(oldIrql);
|
|
|
|
|
|
|
|
return disposed;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static
|
|
|
|
PFX_POOL_HEADER
|
|
|
|
_CleanupPointer(
|
|
|
|
__in PFX_DRIVER_GLOBALS FxDriverGlobals,
|
|
|
|
__in FxObject* Object
|
|
|
|
)
|
|
|
|
{
|
|
|
|
PFX_POOL_HEADER pHeader;
|
|
|
|
PVOID pObjectBase;
|
|
|
|
|
|
|
|
pObjectBase = _GetBase(Object);
|
|
|
|
|
|
|
|
pHeader = CONTAINING_RECORD(pObjectBase, FX_POOL_HEADER, AllocationStart);
|
|
|
|
|
|
|
|
//
|
|
|
|
// If PoolTracker is on then do....
|
|
|
|
//
|
|
|
|
if (FxDriverGlobals->IsPoolTrackingOn()) {
|
|
|
|
//
|
|
|
|
// Decommission this NonPaged Allocation tracker
|
|
|
|
//
|
|
|
|
FxPoolRemoveNonPagedAllocateTracker((PFX_POOL_TRACKER) pHeader->Base);
|
|
|
|
}
|
|
|
|
|
|
|
|
return pHeader;
|
|
|
|
}
|
|
|
|
|
|
|
|
_Must_inspect_result_
|
|
|
|
static
|
|
|
|
NTSTATUS
|
|
|
|
_GetEffectiveLock(
|
|
|
|
__in FxObject* Object,
|
|
|
|
__in_opt IFxHasCallbacks* Callbacks,
|
|
|
|
__in BOOLEAN AutomaticLocking,
|
|
|
|
__in BOOLEAN PassiveCallbacks,
|
|
|
|
__out FxCallbackLock** CallbackLock,
|
|
|
|
__out_opt FxObject** CallbackLockObject
|
|
|
|
);
|
|
|
|
|
|
|
|
//
|
|
|
|
// Implementation for WdfObjectQuery
|
|
|
|
//
|
|
|
|
_Must_inspect_result_
|
|
|
|
static
|
|
|
|
NTSTATUS
|
|
|
|
_ObjectQuery(
|
|
|
|
_In_ FxObject* Object,
|
|
|
|
_In_ CONST GUID* Guid,
|
|
|
|
_In_ ULONG QueryBufferLength,
|
|
|
|
_Out_writes_bytes_(QueryBufferLength)
|
|
|
|
PVOID QueryBuffer
|
|
|
|
);
|
|
|
|
|
|
|
|
protected:
|
|
|
|
VOID
|
|
|
|
DeleteEarlyDisposedObject(
|
|
|
|
VOID
|
|
|
|
);
|
|
|
|
|
|
|
|
private:
|
|
|
|
VOID
|
|
|
|
FinalRelease(
|
|
|
|
VOID
|
|
|
|
);
|
|
|
|
|
|
|
|
//
|
|
|
|
// This is used by verifier to ensure that DeleteObject
|
|
|
|
// is only called once.
|
|
|
|
//
|
|
|
|
// It must be accessed under the m_SpinLock.
|
|
|
|
//
|
|
|
|
// It returns TRUE if this is the first call.
|
|
|
|
//
|
|
|
|
BOOLEAN
|
|
|
|
MarkDeleteCalledLocked(
|
|
|
|
VOID
|
|
|
|
)
|
|
|
|
{
|
|
|
|
BOOLEAN retval;
|
|
|
|
|
|
|
|
retval = !(m_ObjectFlags & FXOBJECT_FLAGS_DELETECALLED);
|
|
|
|
|
|
|
|
m_ObjectFlags |= FXOBJECT_FLAGS_DELETECALLED;
|
|
|
|
|
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
|
|
|
BOOLEAN
|
|
|
|
IsPassiveCallbacksLocked(
|
|
|
|
VOID
|
|
|
|
)
|
|
|
|
{
|
|
|
|
return FLAG_TO_BOOL(m_ObjectFlags, FXOBJECT_FLAGS_PASSIVE_CALLBACKS);
|
|
|
|
}
|
|
|
|
|
|
|
|
BOOLEAN
|
|
|
|
IsPassiveDisposeLocked(
|
|
|
|
VOID
|
|
|
|
)
|
|
|
|
{
|
|
|
|
return FLAG_TO_BOOL(m_ObjectFlags, FXOBJECT_FLAGS_PASSIVE_DISPOSE);
|
|
|
|
}
|
|
|
|
|
|
|
|
BOOLEAN
|
|
|
|
IsForceDisposeThreadLocked(
|
|
|
|
VOID
|
|
|
|
)
|
|
|
|
{
|
|
|
|
return FLAG_TO_BOOL(m_ObjectFlags, FXOBJECT_FLAGS_FORCE_DISPOSE_THREAD);
|
|
|
|
}
|
|
|
|
|
|
|
|
BOOLEAN
|
|
|
|
ShouldDeferDisposeLocked(
|
|
|
|
__out_opt PKIRQL PreviousIrql = NULL
|
|
|
|
)
|
|
|
|
{
|
|
|
|
if (IsForceDisposeThreadLocked()) {
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
else if (IsPassiveDisposeLocked()) {
|
|
|
|
//
|
|
|
|
// Only call KeGetCurrentIrql() if absolutely necessary. It is an
|
|
|
|
// expensive call and we want to minimize the cycles required when
|
|
|
|
// destroying an object that requires passive rundown
|
|
|
|
//
|
|
|
|
|
|
|
|
//
|
|
|
|
// Cases:
|
|
|
|
// 1) Caller does not know the current IRQL, so we must query for it
|
|
|
|
//
|
|
|
|
// 2) Caller knew prev IRQL, so we used the caller's value
|
|
|
|
//
|
|
|
|
if (PreviousIrql == NULL) { // case 1
|
|
|
|
if (Mx::MxGetCurrentIrql() != PASSIVE_LEVEL) {
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (*PreviousIrql != PASSIVE_LEVEL) { // case 2
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
|
|
ParentDeleteEvent(
|
|
|
|
VOID
|
|
|
|
);
|
|
|
|
|
|
|
|
BOOLEAN
|
|
|
|
PerformEarlyDispose(
|
|
|
|
VOID
|
|
|
|
);
|
|
|
|
|
|
|
|
_Releases_lock_(this->m_SpinLock.m_Lock)
|
|
|
|
__drv_requiresIRQL(DISPATCH_LEVEL)
|
|
|
|
BOOLEAN
|
|
|
|
PerformEarlyDisposeWorkerAndUnlock(
|
|
|
|
__in __drv_restoresIRQL KIRQL OldIrql,
|
|
|
|
__in BOOLEAN CanDefer
|
|
|
|
);
|
|
|
|
|
|
|
|
_Releases_lock_(this->m_SpinLock.m_Lock)
|
|
|
|
__drv_requiresIRQL(DISPATCH_LEVEL)
|
|
|
|
BOOLEAN
|
|
|
|
PerformDisposingDisposeChildrenLocked(
|
|
|
|
__in __drv_restoresIRQL KIRQL OldIrql,
|
|
|
|
__in BOOLEAN CanDefer
|
|
|
|
);
|
|
|
|
|
|
|
|
_Releases_lock_(this->m_SpinLock.m_Lock)
|
|
|
|
__drv_requiresIRQL(DISPATCH_LEVEL)
|
|
|
|
BOOLEAN
|
|
|
|
DeleteWorkerAndUnlock(
|
|
|
|
__in __drv_restoresIRQL KIRQL OldIrql,
|
|
|
|
__in BOOLEAN CanDefer
|
|
|
|
);
|
|
|
|
|
|
|
|
VOID
|
|
|
|
QueueDeferredDisposeLocked(
|
|
|
|
__in FxObjectState NewDeferedState
|
|
|
|
);
|
|
|
|
|
|
|
|
VOID
|
|
|
|
DeferredDisposeWorkItem(
|
|
|
|
VOID
|
|
|
|
);
|
|
|
|
|
|
|
|
_Releases_lock_(this->m_SpinLock.m_Lock)
|
|
|
|
__drv_requiresIRQL(DISPATCH_LEVEL)
|
|
|
|
BOOLEAN
|
|
|
|
DisposeChildrenWorker(
|
|
|
|
__in FxObjectState NewDeferedState,
|
|
|
|
__in __drv_restoresIRQL KIRQL OldIrql,
|
|
|
|
__in BOOLEAN CanDefer
|
|
|
|
);
|
|
|
|
|
|
|
|
_When_(Unlock, _Releases_lock_(this->m_SpinLock.m_Lock))
|
|
|
|
__drv_when(Unlock, __drv_requiresIRQL(DISPATCH_LEVEL))
|
|
|
|
VOID
|
|
|
|
DeletedAndDisposedWorkerLocked(
|
|
|
|
__in __drv_when(Unlock, __drv_restoresIRQL) KIRQL OldIrql,
|
|
|
|
__in BOOLEAN Unlock = TRUE
|
|
|
|
);
|
|
|
|
|
|
|
|
_Must_inspect_result_
|
|
|
|
NTSTATUS
|
|
|
|
RemoveParentAssociation(
|
|
|
|
VOID
|
|
|
|
);
|
|
|
|
|
|
|
|
_Must_inspect_result_
|
|
|
|
NTSTATUS
|
|
|
|
AddChildObjectInternal(
|
|
|
|
__in FxObject* ChildObject
|
|
|
|
);
|
|
|
|
|
|
|
|
_Must_inspect_result_
|
|
|
|
NTSTATUS
|
|
|
|
RemoveChildObjectInternal(
|
|
|
|
__in FxObject* ChildObject
|
|
|
|
);
|
|
|
|
|
|
|
|
VOID
|
|
|
|
ProcessDestroy(
|
|
|
|
VOID
|
|
|
|
);
|
|
|
|
|
|
|
|
VOID
|
|
|
|
CallCleanupCallbacks(
|
|
|
|
VOID
|
|
|
|
);
|
|
|
|
};
|
|
|
|
|
|
|
|
#endif // _FXOBJECT_H_
|