Files
exectos/xtoskrnl/ob/lifecycl.cc
Aiken Harris e8fa736c7a
Some checks failed
Builds / ExectOS (i686, release) (push) Failing after 30s
Builds / ExectOS (amd64, release) (push) Failing after 32s
Builds / ExectOS (i686, debug) (push) Failing after 40s
Builds / ExectOS (amd64, debug) (push) Failing after 43s
Implement RAII guard for critical regions
2026-07-10 08:03:58 +02:00

1541 lines
50 KiB
C++

/**
* PROJECT: ExectOS
* COPYRIGHT: See COPYING.md in the top level directory
* FILE: xtoskrnl/ob/lifecycl.cc
* DESCRIPTION: Object Manager Lifecycle Management
* DEVELOPERS: Aiken Harris <harraiken91@gmail.com>
*/
#include <xtos.hh>
/**
* Allocates and initializes the memory block for a new object and its headers.
*
* @param CreateInfo
* Supplies a pointer to the object creation information.
*
* @param OwnerProcessorMode
* Supplies the processor mode that will own the object.
*
* @param ObjectType
* Supplies an optional pointer to the object type definition.
*
* @param ObjectName
* Supplies an optional pointer to the unicode string representing the object's name.
*
* @param ObjectBodySize
* Supplies the size, in bytes, of the object body itself.
*
* @param ReturnedObjectHeader
* Receives a pointer to the newly allocated object header.
*
* @return This routine returns a status code indicating the success or failure of the operation.
*
* @since XT 1.0
*/
XTAPI
XTSTATUS
OB::LifeCycle::AllocateObject(IN POBJECT_CREATE_INFORMATION CreateInfo,
IN KPROCESSOR_MODE OwnerProcessorMode,
IN POBJECT_TYPE ObjectType,
IN PUNICODE_STRING ObjectName,
IN ULONG ObjectBodySize,
OUT POBJECT_HEADER *ReturnedObjectHeader)
{
POBJECT_HEADER_CREATOR_INFO CreatorInfoHeader;
POBJECT_HEADER_HANDLE_INFO HandleInfoHeader;
POBJECT_HEADER_QUOTA_INFO QuotaInfoHeader;
POBJECT_HEADER_NAME_INFO NameInfoHeader;
OBJECT_OPTIONAL_HEADER_LAYOUT Layout;
ULONG Tag, TotalAllocationSize;
POBJECT_HEADER ObjectHeader;
MMPOOL_TYPE PoolType;
PVOID PoolBuffer;
XTSTATUS Status;
PUCHAR Offset;
/* Calculate the sizes for all optional headers */
CalculateOptionalHeaderSize(CreateInfo, ObjectType, ObjectName, &Layout);
/* Determine the pool type */
if(ObjectType == NULLPTR || ObjectType->TypeInfo.PoolType == NonPagedPool)
{
/* Use non-paged pool */
PoolType = NonPagedPool;
}
else
{
/* Use paged pool */
PoolType = PagedPool;
}
/* Compute the total required block size */
TotalAllocationSize = Layout.TotalSize + sizeof(OBJECT_HEADER) + ObjectBodySize;
Tag = (ObjectType == NULLPTR) ? TAG_OB_OBJECT_TYPE : ObjectType->Key;
/* Allocate the memory block from the selected pool */
Status = MM::Allocator::AllocatePool(PoolType, TotalAllocationSize, &PoolBuffer, Tag);
if(Status != STATUS_SUCCESS || PoolBuffer == NULLPTR)
{
/* Allocation failed, return error code */
return STATUS_INSUFFICIENT_RESOURCES;
}
/* Set the initial offset */
Offset = (PUCHAR)PoolBuffer;
/* Check if there is a Quota Information header */
if(Layout.QuotaInfoSize != 0)
{
/* Initialize the Quota Information header */
QuotaInfoHeader = (POBJECT_HEADER_QUOTA_INFO)Offset;
QuotaInfoHeader->ExclusiveProcess = NULLPTR;
QuotaInfoHeader->NonPagedPoolCharge = CreateInfo->NonPagedPoolCharge;
QuotaInfoHeader->PagedPoolCharge = CreateInfo->PagedPoolCharge;
QuotaInfoHeader->SecurityDescriptorCharge = CreateInfo->SecurityDescriptorCharge;
/* Advance the pointer */
Offset += Layout.QuotaInfoSize;
}
/* Check if there is a Handle Information header */
if(Layout.HandleInfoSize != 0)
{
/* Initialize the Handle Information header */
HandleInfoHeader = (POBJECT_HEADER_HANDLE_INFO)Offset;
HandleInfoHeader->SingleEntry.HandleCount = 0;
/* Advance the pointer */
Offset += Layout.HandleInfoSize;
}
/* Check if there is a Name Information header */
if(Layout.NameInfoSize != 0)
{
/* Initialize the Name Information header */
NameInfoHeader = (POBJECT_HEADER_NAME_INFO)Offset;
NameInfoHeader->Directory = NULLPTR;
NameInfoHeader->Name = *ObjectName;
NameInfoHeader->QueryReferences = 1;
/* Advance the pointer */
Offset += Layout.NameInfoSize;
}
/* Check if there is a Creator Information header */
if(Layout.CreatorInfoSize != 0)
{
/* Initialize the Creator Information header */
CreatorInfoHeader = (POBJECT_HEADER_CREATOR_INFO)Offset;
CreatorInfoHeader->CreatorBackTraceIndex = 0;
CreatorInfoHeader->CreatorUniqueProcess = PS::Process::GetCurrentProcess()->UniqueProcessId;
RTL::LinkedList::InitializeListHead(&CreatorInfoHeader->TypeList);
/* Advance the pointer */
Offset += Layout.CreatorInfoSize;
}
/* Retrieve the object header */
ObjectHeader = (POBJECT_HEADER)Offset;
/* Initialize the object header */
ObjectHeader->Flags = OBJECT_FLAG_NEW_OBJECT;
ObjectHeader->HandleInfoOffset = Layout.HandleInfoSize ? (UCHAR)(Layout.TotalSize - Layout.QuotaInfoSize) : 0;
ObjectHeader->NameInfoOffset = Layout.NameInfoSize ? (UCHAR)(Layout.CreatorInfoSize + Layout.NameInfoSize) : 0;
ObjectHeader->QuotaInfoOffset = Layout.QuotaInfoSize ? (UCHAR)Layout.TotalSize : 0;
/* Check if there is creator information */
if(Layout.CreatorInfoSize != 0)
{
/* Flag the presence of creator information */
ObjectHeader->Flags |= OBJECT_FLAG_CREATOR_INFO;
}
/* Check if there is handle information */
if(Layout.HandleInfoSize != 0)
{
/* Flag the presence of handle information */
ObjectHeader->Flags |= OBJECT_FLAG_SINGLE_PROCESS;
}
/* Check if the object is being created in kernel mode */
if(OwnerProcessorMode == KernelMode)
{
/* Assign kernel mode ownership */
ObjectHeader->Flags |= OBJECT_FLAG_KERNEL_MODE;
}
/* Check if creation information was provided */
if(CreateInfo)
{
/* Check if the object is permanent */
if(CreateInfo->Attributes & OBJECT_PERMANENT)
{
/* Flag the object to prevent deletion */
ObjectHeader->Flags |= OBJECT_FLAG_PERMANENT;
}
/* Check if the object is exclusive */
if(CreateInfo->Attributes & OBJECT_EXCLUSIVE)
{
/* Flag the object as exclusive to a single process */
ObjectHeader->Flags |= OBJECT_FLAG_EXCLUSIVE;
}
}
/* Initialize reference counters and data associations */
ObjectHeader->HandleCount = 0;
ObjectHeader->ObjectCreateInfo = CreateInfo;
ObjectHeader->PointerCount = 1;
ObjectHeader->SecurityDescriptor = NULLPTR;
ObjectHeader->Type = ObjectType;
/* Check if an object type was provided */
if(ObjectType)
{
/* Increment the total active objects counter */
RTL::Atomic::Increment32((VOLATILE PLONG)&ObjectType->TotalNumberOfObjects);
/* Check if the current total exceeds the previous peak */
if(ObjectType->TotalNumberOfObjects > ObjectType->HighWaterNumberOfObjects)
{
/* Update the high-water mark */
ObjectType->HighWaterNumberOfObjects = ObjectType->TotalNumberOfObjects;
}
}
/* Return the pointer to the initialized object */
*ReturnedObjectHeader = ObjectHeader;
/* Return success */
return STATUS_SUCCESS;
}
/**
* Allocates a buffer for an object name string.
*
* @param Length
* Supplies the exact length of the object name in bytes, excluding the NULL terminator.
*
* @param UseLookaside
* Indicates whether the allocation should attempt to draw from the paged pool lookaside list.
*
* @param ObjectName
* Supplies a pointer to the Unicode string descriptor to be initialized with the new buffer.
*
* @return This routine returns a pointer to the allocated buffer, or NULLPTR if allocation fails.
*
* @since XT 1.0
*/
XTFASTCALL
PWCH
OB::LifeCycle::AllocateObjectName(IN ULONG Length,
IN BOOLEAN UseLookaside,
IN OUT PUNICODE_STRING ObjectName)
{
ULONG MaximumLength;
PVOID Buffer;
/* Calculate the required maximum length and initialize the buffer pointer */
MaximumLength = Length + sizeof(WCHAR);
Buffer = NULLPTR;
/* Check if the general pool must be used */
if(!UseLookaside || MaximumLength > OBJECT_NAME_BUFFER_SIZE)
{
/* Allocate from the Memory Manager */
MM::Allocator::AllocatePool(PagedPool, MaximumLength, (PVOID *)&Buffer, TAG_OB_NAME);
}
else
{
/* Set maximum length to the fixed lookaside entry size */
MaximumLength = OBJECT_NAME_BUFFER_SIZE;
/* Allocate from the lookaside list */
Buffer = EX::LookasideList::AllocateFromPerProcessorList(LookasideNameBufferList);
}
/* Initialize the Unicode string descriptor */
ObjectName->Buffer = (PWCH)Buffer;
ObjectName->Length = (USHORT)Length;
ObjectName->MaximumLength = (USHORT)MaximumLength;
/* Return the buffer pointer */
return (PWCH)Buffer;
}
/**
* Calculates the memory layout and sizes for optional object header components.
*
* @param CreateInfo
* Supplies a pointer to the object creation information.
*
* @param ObjectType
* Supplies an optional pointer to the object type definition.
*
* @param ObjectName
* Supplies an optional pointer to the unicode string representing the object's name.
*
* @param Layout
* Supplies a pointer to the layout structure that will receive the calculated sizes.
*
* @return This routine does not return any value.
*
* @since XT 1.0
*/
XTAPI
VOID
OB::LifeCycle::CalculateOptionalHeaderSize(IN POBJECT_CREATE_INFORMATION CreateInfo,
IN POBJECT_TYPE ObjectType,
IN PUNICODE_STRING ObjectName,
OUT POBJECT_OPTIONAL_HEADER_LAYOUT Layout)
{
/* Zero the layout structure */
RTL::Memory::ZeroMemory(Layout, sizeof(OBJECT_OPTIONAL_HEADER_LAYOUT));
/* Check if the CreateInfo pointer is valid */
if(CreateInfo == NULLPTR)
{
/* Assign default sizes */
Layout->NameInfoSize = sizeof(OBJECT_HEADER_NAME_INFO);
Layout->CreatorInfoSize = sizeof(OBJECT_HEADER_CREATOR_INFO);
}
else
{
/* Determine if quota tracking is required */
if(ObjectType != NULLPTR &&
(((CreateInfo->PagedPoolCharge != ObjectType->TypeInfo.DefaultPagedPoolCharge ||
CreateInfo->NonPagedPoolCharge != ObjectType->TypeInfo.DefaultNonPagedPoolCharge ||
CreateInfo->SecurityDescriptorCharge > SE_DEFAULT_SECURITY_QUOTA) &&
PS::Process::GetCurrentProcess() != PS::Process::GetSystemProcess()) ||
(CreateInfo->Attributes & OBJECT_EXCLUSIVE)))
{
/* Set the size for the Quota Information header */
Layout->QuotaInfoSize = sizeof(OBJECT_HEADER_QUOTA_INFO);
}
/* Check if the object type requires maintaining handle counts */
if(ObjectType && ObjectType->TypeInfo.MaintainHandleCount)
{
/* Set the size for the Handle Information header */
Layout->HandleInfoSize = sizeof(OBJECT_HEADER_HANDLE_INFO);
}
/* Check if a valid object name was provided */
if(ObjectName && ObjectName->Buffer)
{
/* Set the size for the Name Information header */
Layout->NameInfoSize = sizeof(OBJECT_HEADER_NAME_INFO);
}
/* Check if the object type requires maintaining a creator type list */
if(ObjectType && ObjectType->TypeInfo.MaintainTypeList)
{
/* Set the size for the Creator Information header */
Layout->CreatorInfoSize = sizeof(OBJECT_HEADER_CREATOR_INFO);
}
}
/* Calculate the total size required for all optional headers */
Layout->TotalSize = Layout->QuotaInfoSize +
Layout->HandleInfoSize +
Layout->NameInfoSize +
Layout->CreatorInfoSize;
}
/**
* Captures and validates object creation information and attributes passed.
*
* @param ObjectType
* Optionally supplies a pointer to the type of object being created.
*
* @param ProcessorMode
* Supplies the processor mode used to probe user-mode structures for accessibility.
*
* @param OwnerProcessorMode
* Supplies the processor mode that will own the object.
*
* @param ObjectAttributes
* Supplies a pointer to the object attributes structure.
*
* @param CapturedObjectName
* Supplies a pointer to a Unicode string descriptor that will receive the captured name.
*
* @param ObjectCreateInfo
* Supplies a pointer to the internal structure receiving the validated creation parameters.
*
* @param UseLookaside
* Indicates whether lookaside lists should be used for name buffer allocations.
*
* @return This routine returns a status code indicating the success or failure of the operation.
*
* @since XT 1.0
*/
XTAPI
XTSTATUS
OB::LifeCycle::CaptureObjectCreateInformation(IN POBJECT_TYPE ObjectType,
IN KPROCESSOR_MODE ProcessorMode,
IN KPROCESSOR_MODE OwnerProcessorMode,
IN POBJECT_ATTRIBUTES ObjectAttributes,
IN OUT PUNICODE_STRING CapturedObjectName,
IN POBJECT_CREATE_INFORMATION ObjectCreateInfo,
IN BOOLEAN UseLookaside)
{
PSECURITY_QUALITY_OF_SERVICE SecurityQos;
PSECURITY_DESCRIPTOR SecurityDescriptor;
PUNICODE_STRING ObjectName;
XTSTATUS Status;
ULONG Size;
/* Initialize local state variables */
ObjectName = NULLPTR;
Status = STATUS_SUCCESS;
/* Zero out the output structure */
RTL::Memory::ZeroMemory(ObjectCreateInfo, sizeof(OBJECT_CREATE_INFORMATION));
/* Enter structured exception handler */
__try
{
/* Check if the caller supplied object attributes */
if(ObjectAttributes)
{
/* Check if the request originated from user mode */
if(ProcessorMode != KernelMode)
{
/* Probe the object attributes structure */
MM::Probe::ProbeForReadStructure(ObjectAttributes, sizeof(OBJECT_ATTRIBUTES), sizeof(ULONG_PTR));
}
/* Validate structure length and verify that no unknown attributes are specified */
if((ObjectAttributes->Length != sizeof(OBJECT_ATTRIBUTES)) ||
(ObjectAttributes->Attributes & ~OBJECT_VALID_ATTRIBUTES))
{
/* Return error code */
Status = STATUS_INVALID_PARAMETER;
}
else
{
/* Capture the root directory and sanitize basic attribute flags */
ObjectCreateInfo->RootDirectory = ObjectAttributes->RootDirectory;
ObjectCreateInfo->Attributes = ObjectAttributes->Attributes & OBJECT_VALID_ATTRIBUTES;
/* Check if the owner works in user mode */
if(OwnerProcessorMode != KernelMode)
{
/* Enforce kernel handle restrictions */
ObjectCreateInfo->Attributes &= ~OBJECT_KERNEL_HANDLE;
}
/* Cache pointers for subsequent capture and validation */
ObjectName = ObjectAttributes->ObjectName;
SecurityDescriptor = ObjectAttributes->SecurityDescriptor;
SecurityQos = ObjectAttributes->SecurityQualityOfService;
/* Check if security descriptor is present */
if(SecurityDescriptor)
{
/* Capture the security descriptor*/
Status = SE::Descriptor::CaptureSecurityDescriptor(SecurityDescriptor, ProcessorMode, PagedPool,
TRUE, &ObjectCreateInfo->SecurityDescriptor);
if(Status != STATUS_SUCCESS)
{
/* Clear the descriptor pointer on failure */
ObjectCreateInfo->SecurityDescriptor = NULLPTR;
}
else
{
/* Compute and assign the security quota charge for the descriptor */
SE::Descriptor::ComputeQuotaInformationSize(ObjectCreateInfo->SecurityDescriptor, &Size);
ObjectCreateInfo->ProbeMode = ProcessorMode;
ObjectCreateInfo->SecurityDescriptorCharge = SE::Descriptor::ComputeSecurityQuota(Size);
}
}
/* Check if previous operations succeeded and QoS data is available */
if(Status == STATUS_SUCCESS && SecurityQos)
{
/* Check if the request originated from user mode */
if(ProcessorMode != KernelMode)
{
/* Probe the QoS structure */
MM::Probe::ProbeForReadStructure(SecurityQos, sizeof(SECURITY_QUALITY_OF_SERVICE), sizeof(ULONG));
}
/* Copy QoS data into the local structure and link the pointer */
ObjectCreateInfo->SecurityQualityOfService = *SecurityQos;
ObjectCreateInfo->SecurityQos = &ObjectCreateInfo->SecurityQualityOfService;
}
}
}
}
__except (RTL::Exception::SystemFilter())
{
/* Catch memory access violations */
Status = EXCEPTION_CODE;
}
/* Check If all structure and security captures succeeded */
if(Status == STATUS_SUCCESS)
{
/* Validate if an object name is available */
if(ObjectName)
{
/* Capture object name */
Status = CaptureObjectName(ProcessorMode, ObjectName, CapturedObjectName, UseLookaside);
}
else
{
/* Initialize an empty name descriptor */
CapturedObjectName->Buffer = NULLPTR;
CapturedObjectName->Length = 0;
CapturedObjectName->MaximumLength = 0;
/* Check if this is a root directory */
if(ObjectCreateInfo->RootDirectory)
{
/* Root directory cannot be used without an object name, set error code */
Status = STATUS_OBJECT_NAME_INVALID;
}
}
}
/* Check if any steps failed */
if(Status != STATUS_SUCCESS)
{
/* Release object creation resources */
ReleaseObjectCreateInformation(ObjectCreateInfo);
}
/* Return status code */
return Status;
}
/**
* Captures an object name from user-mode or kernel-mode memory into a kernel buffer.
*
* @param ProcessorMode
* Supplies the processor mode used to probe user-mode structures for accessibility.
*
* @param ObjectName
* Supplies a pointer to the original Unicode string descriptor to be captured.
*
* @param CapturedObjectName
* Supplies a pointer to the Unicode string descriptor that will receive the captured buffer.
*
* @param UseLookaside
* Indicates whether a lookaside list should be used for the buffer allocation if applicable.
*
* @return Returns STATUS_SUCCESS if the name was captured safely, or an appropriate error code.
*
* @since XT 1.0
*/
XTAPI
XTSTATUS
OB::LifeCycle::CaptureObjectName(IN KPROCESSOR_MODE ProcessorMode,
IN PUNICODE_STRING ObjectName,
IN OUT PUNICODE_STRING CapturedObjectName,
IN BOOLEAN UseLookaside)
{
UNICODE_STRING InputObjectName;
XTSTATUS Status;
ULONG Length;
PWCH Buffer;
/* Initialize local variables */
Buffer = NULLPTR;
Status = STATUS_SUCCESS;
/* Initialize the captured name descriptor */
CapturedObjectName->Buffer = NULLPTR;
CapturedObjectName->Length = 0;
CapturedObjectName->MaximumLength = 0;
/* Enter structural exception handler */
__try
{
/* Check if the string originates from user-mode */
if(ProcessorMode != KernelMode)
{
/* Probe and copy the Unicode string descriptor */
MM::Probe::ProbeForRead(ObjectName, sizeof(UNICODE_STRING), alignof(UNICODE_STRING));
InputObjectName = *(CONST VOLATILE PUNICODE_STRING)ObjectName;
/* Probe the string buffer for read access */
MM::Probe::ProbeForRead(InputObjectName.Buffer, InputObjectName.Length, sizeof(WCHAR));
}
else
{
/* Copy the descriptor */
InputObjectName = *ObjectName;
}
/* Check if the string is not empty */
Length = InputObjectName.Length;
if(Length > 0)
{
/* Ensure the length is properly aligned */
if(((Length & (sizeof(WCHAR) - 1)) != 0) || (Length == (MAXUSHORT - sizeof(WCHAR) + 1)))
{
/* Set status code */
Status = STATUS_OBJECT_NAME_INVALID;
}
else
{
/* Allocate the name buffer */
Buffer = AllocateObjectName(Length, UseLookaside, CapturedObjectName);
if(!Buffer)
{
/* Buffer allocation failed, set status code */
Status = STATUS_INSUFFICIENT_RESOURCES;
}
else
{
/* Copy the string data into the buffer */
RTL::Memory::CopyMemory(Buffer, InputObjectName.Buffer, Length);
/* NULL-terminate the string */
Buffer[Length / sizeof(WCHAR)] = (WCHAR)0;
}
}
}
}
__except (RTL::Exception::SystemFilter())
{
/* Catch memory access violations */
Status = EXCEPTION_CODE;
/* Check if a buffer was allocated */
if(Buffer)
{
/* Release the buffer */
FreeObjectName(CapturedObjectName);
}
}
/* Return status code */
return Status;
}
/**
* Creates a new object of the specified type and initializes it.
*
* @param ProcessorMode
* Supplies the processor mode used to probe parameters.
*
* @param ObjectType
* Supplies a pointer to the object type of the new object.
*
* @param ObjectAttributes
* Supplies an optional pointer to the object attributes structure.
*
* @param OwnerProcessorMode
* Supplies the processor mode that will own the object.
*
* @param ParseContext
* Supplies an optional pointer to a parse context structure.
*
* @param ObjectBodySize
* Supplies the size, in bytes, of the object body.
*
* @param PagedPoolCharge
* Supplies the paged pool charge for the object. If zero, default is used.
*
* @param NonPagedPoolCharge
* Supplies the non-paged pool charge for the object. If zero, default is used.
*
* @param Object
* Receives a pointer to the body of the newly created object.
*
* @return This routine returns a status code indicating the success or failure of the operation.
*
* @since XT 1.0
*/
XTAPI
XTSTATUS
OB::LifeCycle::CreateObject(IN KPROCESSOR_MODE ProcessorMode,
IN POBJECT_TYPE ObjectType,
IN POBJECT_ATTRIBUTES ObjectAttributes,
IN KPROCESSOR_MODE OwnerProcessorMode,
IN OUT PVOID ParseContext,
IN ULONG ObjectBodySize,
IN ULONG PagedPoolCharge,
IN ULONG NonPagedPoolCharge,
OUT PVOID *Object)
{
POBJECT_CREATE_INFORMATION ObjectCreateInfo;
UNICODE_STRING CapturedObjectName;
POBJECT_HEADER ObjectHeader;
XTSTATUS Status;
/* Allocate the buffer for creation parameters */
ObjectCreateInfo = (POBJECT_CREATE_INFORMATION)EX::LookasideList::AllocateFromPerProcessorList(LookasideCreateInfoList);
if(!ObjectCreateInfo)
{
/* Allocation failed, return error code */
return STATUS_INSUFFICIENT_RESOURCES;
}
/* Capture and probe the object attributes */
Status = CaptureObjectCreateInformation(ObjectType, ProcessorMode, OwnerProcessorMode, ObjectAttributes,
&CapturedObjectName, ObjectCreateInfo, FALSE);
if(Status != STATUS_SUCCESS)
{
/* Capture failed, clean up and return error code */
EX::LookasideList::FreeToPerProcessorList(LookasideCreateInfoList, ObjectCreateInfo);
return Status;
}
/* Validate requested attributes */
if(ObjectType && ObjectType->TypeInfo.InvalidAttributes & ObjectCreateInfo->Attributes)
{
/* Release the captured creation info structure */
FreeObjectCreateInformation(ObjectCreateInfo);
/* Check if object name was captured */
if(CapturedObjectName.Buffer)
{
/* Release the object name buffer */
FreeObjectName(&CapturedObjectName);
}
/* Return error code */
return STATUS_INVALID_PARAMETER;
}
/* Check if paged pool charge was specified */
if(PagedPoolCharge == 0)
{
/* Use default paged pool charge */
PagedPoolCharge = ObjectType->TypeInfo.DefaultPagedPoolCharge;
}
/* Check if non-paged pool charge was specified */
if(NonPagedPoolCharge == 0)
{
/* Use default non-paged pool charge */
NonPagedPoolCharge = ObjectType->TypeInfo.DefaultNonPagedPoolCharge;
}
/* Set the paged and non-paged pool charges */
ObjectCreateInfo->PagedPoolCharge = PagedPoolCharge;
ObjectCreateInfo->NonPagedPoolCharge = NonPagedPoolCharge;
/* Allocate the physical memory layout for the object */
Status = AllocateObject(ObjectCreateInfo, OwnerProcessorMode, ObjectType,
&CapturedObjectName, ObjectBodySize, &ObjectHeader);
if(Status != STATUS_SUCCESS)
{
/* Allocation failed, cleanup the captured data */
FreeObjectCreateInformation(ObjectCreateInfo);
/* Check if object name was captured */
if(CapturedObjectName.Buffer)
{
/* Free the captured object name */
FreeObjectName(&CapturedObjectName);
}
/* Return error code */
return Status;
}
/* Point the output pointer to the object */
*Object = &ObjectHeader->Body;
/* Check if the object is permanent */
if(ObjectHeader->Flags & OBJECT_FLAG_PERMANENT)
{
/* Apply security constraints */
if(!SE::Privileges::CheckSinglePrivilege(SE_LUID_CREATE_PERMANENT_PRIVILEGE, ProcessorMode))
{
/* Privilege check failed, release the object and return error code */
FreeObject(*Object);
return STATUS_PRIVILEGE_NOT_HELD;
}
}
/* Return success */
return STATUS_SUCCESS;
}
/**
* Enqueues an object header into a deferred deletion queue and queues a system worker thread if the list was empty.
*
* @param ObjectHeader
* Supplies a pointer to the object header to be queued for deletion.
*
* @return This routine does not return any value.
*
* @since XT 1.0
*/
XTAPI
VOID
OB::LifeCycle::DeferObjectDeletion(IN POBJECT_HEADER ObjectHeader)
{
PVOID OldListHead, ActualListHead;
/* Capture the snapshot of the removal list */
OldListHead = RemoveObjectList;
/* Enter the CAS loop */
while(TRUE)
{
/* Link the current head to object header */
ObjectHeader->NextToFree = OldListHead;
/* Attempt to push the header to the list head */
ActualListHead = RTL::Atomic::CompareExchangePointer(&RemoveObjectList, OldListHead, ObjectHeader);
if(ActualListHead == OldListHead)
{
/* Successfully pushed to the stack, break the loop */
break;
}
/* The list changed mid-flight, update the snapshot and retry */
OldListHead = ActualListHead;
}
/* Check if the list was empty */
if(!OldListHead)
{
/* Queue the deletion work item to the system worker queue */
EX::WorkItem::QueueWorkItem(&RemoveObjectWorkItem, CriticalWorkQueue);
}
}
/**
* Destructs an object, including invoking security and type-specific deletion callbacks and freeing memory.
*
* @param Object
* Supplies a pointer to the object being destroyed.
*
* @param WorkerThread
* Indicates whether this routine is executed in the context of the system worker thread.
*
* @return This routine does not return any value.
*
* @since XT 1.0
*/
XTAPI
VOID
OB::LifeCycle::DeleteObject(IN PVOID Object,
IN BOOLEAN WorkerThread)
{
POBJECT_HEADER_CREATOR_INFO CreatorInfo;
POBJECT_HEADER_NAME_INFO NameInfo;
POBJECT_HEADER ObjectHeader;
POBJECT_TYPE ObjectType;
/* Resolve object header */
ObjectHeader = CONTAIN_RECORD(Object, OBJECT_HEADER, Body);
/* Resolve creator information, name information pointers and object type*/
CreatorInfo = GetObjectCreatorInformation(ObjectHeader);
NameInfo = GetObjectNameInformation(ObjectHeader);
ObjectType = ObjectHeader->Type;
/* Check if the object has creator info and is linked in a type tracking list */
if(CreatorInfo && !RTL::LinkedList::ListEmpty(&CreatorInfo->TypeList))
{
/* Acquire exclusive access to the object type tracking list */
KE::CriticalRegionGuard CriticalRegion;
KE::PushLock::AcquireExclusivePushLock(&ObjectType->TypeLock);
/* Unlink the object from the creator type list */
RTL::LinkedList::RemoveEntryList(&CreatorInfo->TypeList);
/* Release the object type tracking list */
KE::PushLock::ReleaseExclusivePushLock(&ObjectType->TypeLock);
}
/* Check if the object has an allocated name buffer */
if(NameInfo && NameInfo->Name.Buffer)
{
/* Free the name buffer pool allocation */
FreeObjectName(&NameInfo->Name);
/* Clear the name information fields */
NameInfo->Name.Buffer = NULLPTR;
NameInfo->Name.Length = 0;
NameInfo->Name.MaximumLength = 0;
}
/* Check if a security descriptor is attached */
if(ObjectHeader->SecurityDescriptor)
{
/* Invoke the security procedure to delete the descriptor */
ObjectType->TypeInfo.SecurityProcedure(Object, DeleteSecurityDescriptor, NULLPTR, NULLPTR, NULLPTR,
&ObjectHeader->SecurityDescriptor, NonPagedPool, NULLPTR);
}
/* Check if a custom deletion procedure is registered */
if(ObjectType->TypeInfo.DeleteProcedure)
{
/* Check if execution is occurring outside of the system worker thread */
if(!WorkerThread)
{
/* Mark the object for deferred deletion */
ObjectHeader->Flags |= OBJECT_FLAG_DEFER_DELETE;
}
/* Invoke the type-specific delete procedure */
ObjectType->TypeInfo.DeleteProcedure(Object);
}
/* Free the object memory */
FreeObject(Object);
}
/**
* Decrements the pointer reference count of an object.
*
* @param Object
* Supplies a pointer to the object body to dereference.
*
* @return This routine returns the updated reference count.
*/
XTFASTCALL
LONG_PTR
OB::LifeCycle::DereferenceObject(IN PVOID Object)
{
POBJECT_HEADER ObjectHeader;
LONG_PTR NewCount;
/* Resolve the object header */
ObjectHeader = CONTAIN_RECORD(Object, OBJECT_HEADER, Body);
/* Decrement the reference count */
NewCount = RTL::Atomic::Decrement64(&ObjectHeader->PointerCount);
/* Check for object expiration */
if(NewCount == 0)
{
/* Check if object can be deleted */
if(KE::RunLevel::GetCurrentRunLevel() == PASSIVE_LEVEL)
{
/* Delete the object immediately */
DeleteObject(Object, FALSE);
}
else
{
/* Defer deletion to worker thread */
DeferObjectDeletion(ObjectHeader);
}
}
/* Return the updated reference count */
return NewCount;
}
/**
* Decrements the pointer reference count of an object by a specified amount.
*
* @param Object
* Supplies a pointer to the object body to dereference.
*
* @param Count
* Specifies a number of references to release.
*
* @return This routine returns the updated reference count.
*/
XTFASTCALL
LONG_PTR
OB::LifeCycle::DereferenceObject(IN PVOID Object,
IN ULONG Count)
{
POBJECT_HEADER ObjectHeader;
LONG_PTR NewCount;
/* Resolve the object header */
ObjectHeader = CONTAIN_RECORD(Object, OBJECT_HEADER, Body);
/* Decrement the reference count */
NewCount = RTL::Atomic::ExchangeAdd64(&ObjectHeader->PointerCount, -(LONG_PTR)(Count)) - Count;
/* Check for object expiration */
if(NewCount == 0)
{
/* Defer deletion to worker thread */
DeferObjectDeletion(ObjectHeader);
}
/* Return the updated reference count */
return NewCount;
}
/**
* Decrements the reference count of an object and defers its deletion to a worker thread if the count reaches zero.
*
* @param Object
* Supplies a pointer to the object body.
*
* @return This routine returns the updated reference count.
*
* @since XT 1.0
*/
XTFASTCALL
LONG_PTR
OB::LifeCycle::DereferenceObjectDeferDelete(IN PVOID Object)
{
POBJECT_HEADER ObjectHeader;
LONG_PTR NewCount;
/* Resolve the object header */
ObjectHeader = CONTAIN_RECORD(Object, OBJECT_HEADER, Body);
/* Decrement the reference count */
NewCount = RTL::Atomic::Decrement64(&ObjectHeader->PointerCount);
/* Check for object expiration */
if(NewCount == 0)
{
/* Defer deletion to worker thread */
DeferObjectDeletion(ObjectHeader);
}
/* Return the updated reference count */
return NewCount;
}
/**
* Decrements the query reference count of a NameInfo structure and frees its resources if the count reaches zero.
*
* @param NameInfo
* Supplies a pointer to the object header name information structure.
*
* @return This routine does not return any value.
*
* @since XT 1.0
*/
XTFASTCALL
VOID
OB::LifeCycle::DereferenceObjectNameInformation(IN POBJECT_HEADER_NAME_INFO NameInfo)
{
POBJECT_DIRECTORY Directory;
/* Ensure the name info pointer is valid */
if(!NameInfo)
{
/* Name info pointer is invalid, return */
return;
}
/* Decrement the query reference count */
if(RTL::Atomic::Decrement32((PLONG)&NameInfo->QueryReferences) != 0)
{
/* References are still active, return */
return;
}
/* Check if the name buffer is allocated */
if(NameInfo->Name.Buffer)
{
/* Free the name buffer */
FreeObjectName(&NameInfo->Name);
/* Reset the name string fields */
NameInfo->Name.Buffer = NULLPTR;
NameInfo->Name.Length = 0;
NameInfo->Name.MaximumLength = 0;
}
/* Extract the directory pointer and check if it is valid */
Directory = NameInfo->Directory;
if(Directory != NULLPTR)
{
/* Clear the directory reference */
NameInfo->Directory = NULLPTR;
/* Defer the deletion of the directory object */
DereferenceObjectDeferDelete(Directory);
}
}
/**
* Frees the memory allocated for an object and its associated optional headers.
*
* @param Object
* Supplies a pointer to the object body to be freed.
*
* @return This routine does not return any value.
*
* @since XT 1.0
*/
XTAPI
VOID
OB::LifeCycle::FreeObject(IN PVOID Object)
{
POBJECT_HEADER_HANDLE_INFO HandleInfo;
POBJECT_HEADER_NAME_INFO NameInfo;
POBJECT_HEADER ObjectHeader;
POBJECT_TYPE ObjectType;
PVOID AllocationBase;
/* Resolve the object header and object type */
ObjectHeader = CONTAIN_RECORD(Object, OBJECT_HEADER, Body);
ObjectType = ObjectHeader->Type;
/* Calculate the base of the pool allocation */
AllocationBase = GetObjectAllocationBase(ObjectHeader);
/* Check if the object type exists */
if(ObjectType)
{
/* Decrement the count of active objects for this type */
RTL::Atomic::Decrement32((VOLATILE PLONG)&ObjectType->TotalNumberOfObjects);
}
/* Check if the object is not fully initialized */
if(ObjectHeader->Flags & OBJECT_FLAG_NEW_OBJECT)
{
/* Check if creation information exists */
if(ObjectHeader->ObjectCreateInfo)
{
/* Free the captured creation parameters */
FreeObjectCreateInformation((ObjectHeader->ObjectCreateInfo));
/* Nullify the pointer */
ObjectHeader->ObjectCreateInfo = NULLPTR;
}
}
else
{
/* Fully initialized object, return quotas */
ReturnObjectQuota(ObjectHeader, ObjectType);
}
/* Locate handle information */
HandleInfo = (POBJECT_HEADER_HANDLE_INFO)(!ObjectHeader->HandleInfoOffset ? NULLPTR : (PCHAR)ObjectHeader -
ObjectHeader->HandleInfoOffset);
if(HandleInfo && !(ObjectHeader->Flags & OBJECT_FLAG_SINGLE_PROCESS))
{
/* Free the handle */
MM::Allocator::FreePool(HandleInfo->HandleCountDatabase);
}
/* Locate name information */
NameInfo = (POBJECT_HEADER_NAME_INFO)(!ObjectHeader->NameInfoOffset ? NULLPTR : (PCHAR)ObjectHeader -
ObjectHeader->NameInfoOffset);
if(NameInfo && NameInfo->Name.Buffer)
{
/* Free the object name buffer */
FreeObjectName(&NameInfo->Name);
}
/* Mark object type and free memory */
ObjectHeader->Type = (POBJECT_TYPE)(ULONG_PTR)0xBAD0B0B0;
MM::Allocator::FreePool(AllocationBase, (ObjectType == NULLPTR ? TAG_OB_OBJECT_TYPE : ObjectType->Key));
}
/**
* Releases creation information resources and returns them back to the lookaside list.
*
* @param ObjectCreateInfo
* Supplies a pointer to the object creation information structure to be freed.
*
* @return This routine does not return any value.
*
* @since XT 1.0
*/
XTFASTCALL
VOID
OB::LifeCycle::FreeObjectCreateInformation(IN POBJECT_CREATE_INFORMATION CreateInfo)
{
/* Release the object creation information */
ReleaseObjectCreateInformation(CreateInfo);
/* Free the attributes from the lookaside list */
EX::LookasideList::FreeToPerProcessorList(LookasideCreateInfoList, CreateInfo);
}
/**
* Frees an object name buffer either to the general pool or a dedicated lookaside list.
*
* @param ObjectName
* Supplies a pointer to the Unicode string whose buffer is being released.
*
* @return This routine does not return any value.
*
* @since XT 1.0
*/
XTFASTCALL
VOID
OB::LifeCycle::FreeObjectName(IN OUT PUNICODE_STRING ObjectName)
{
/* Check if the buffer size differs from the lookaside list entry size */
if(ObjectName->MaximumLength != OBJECT_NAME_BUFFER_SIZE)
{
/* Size mismatch, free the allocation to the system pool */
MM::Allocator::FreePool(ObjectName->Buffer);
}
else
{
/* Size matches, return the buffer to the lookaside list */
EX::LookasideList::FreeToPerProcessorList(LookasideNameBufferList, ObjectName->Buffer);
}
}
/**
* Resolves the base address of the pool allocation for a given object.
*
* @param ObjectHeader
* Supplies a pointer to the object header.
*
* @return This routine returns the pointer to the start of the memory block.
*
* @since XT 1.0
*/
XTAPI
PVOID
OB::LifeCycle::GetObjectAllocationBase(IN POBJECT_HEADER ObjectHeader)
{
/* Check if the Quota Information header is present */
if(ObjectHeader->QuotaInfoOffset)
{
/* Return the base address including the Quota Information header */
return (PUCHAR)ObjectHeader - ObjectHeader->QuotaInfoOffset;
}
/* Check if the Handle Information header is present */
if(ObjectHeader->HandleInfoOffset)
{
/* Return the base address including the Handle Information header */
return (PUCHAR)ObjectHeader - ObjectHeader->HandleInfoOffset;
}
/* Check if the Name Information header is present */
if(ObjectHeader->NameInfoOffset)
{
/* Return the base address including the Name Information header */
return (PUCHAR)ObjectHeader - ObjectHeader->NameInfoOffset;
}
/* Check if the Creator Information header is present */
if(ObjectHeader->Flags & OBJECT_FLAG_CREATOR_INFO)
{
/* Return the base address including the Creator Information header */
return (PUCHAR)ObjectHeader - sizeof(OBJECT_HEADER_CREATOR_INFO);
}
/* No optional headers exist, return the header address */
return ObjectHeader;
}
/**
* Retrieves the optional creator information header for a given object.
*
* @param Header
* Supplies a pointer to the object's base header.
*
* @return This routine returns a pointer to the creator information, or NULLPTR if not present.
*
* @since XT 1.0
*/
XTFASTCALL
POBJECT_HEADER_CREATOR_INFO
OB::LifeCycle::GetObjectCreatorInformation(IN POBJECT_HEADER Header)
{
/* Verify if the creator flag is set */
if((Header->Flags & OBJECT_FLAG_CREATOR_INFO) == 0)
{
/* Creator information not present, return NULL pounter */
return NULLPTR;
}
/* Return the creator information */
return (POBJECT_HEADER_CREATOR_INFO)((PCHAR)Header - sizeof(OBJECT_HEADER_CREATOR_INFO));
}
/**
* Retrieves the optional name information header for a given object.
*
* @param Header
* Supplies a pointer to the object's base header.
*
* @return This routine returns a pointer to the name information, or NULLPTR if not present.
*
* @since XT 1.0
*/
XTFASTCALL
POBJECT_HEADER_NAME_INFO
OB::LifeCycle::GetObjectNameInformation(IN POBJECT_HEADER Header)
{
/* Check if the header is present */
if(!Header->NameInfoOffset)
{
/* Name information not present, return NULL pointer */
return NULLPTR;
}
/* Return the name information */
return (POBJECT_HEADER_NAME_INFO)((PCHAR)Header - Header->NameInfoOffset);
}
/**
* Initializes the Object lifecycle subsystem.
*
* @return This routine does not return any value.
*
* @since XT 1.0
*/
XTAPI
VOID
OB::LifeCycle::InitializeObjectLifeCycle(VOID)
{
/* Clear tracking pointer */
RemoveObjectList = NULLPTR;
/* Initialize deferred removal work item */
EX::WorkItem::InitializeWorkItem(&RemoveObjectWorkItem, ProcessDeferredDeletionQueue, NULLPTR);
}
/**
* Processes the deferred object removal queue.
*
* @param Parameter
* Supplies an optional context parameter.
*
* @return This routine does not return a value.
*
* @since XT 1.0
*/
XTAPI
VOID
OB::LifeCycle::ProcessDeferredDeletionQueue(IN PVOID Parameter)
{
POBJECT_HEADER ObjectHeader;
POBJECT_HEADER NextObject;
PVOID OldValue;
/* Process the deferred removal queue */
while(TRUE)
{
/* Exchange the queue pointer */
ObjectHeader = (POBJECT_HEADER)RTL::Atomic::ExchangePointer(&RemoveObjectList, OBJECT_REMOVE_QUEUE_LOCKED);
/* Traverse and process the detached list */
while(ObjectHeader && ObjectHeader != (POBJECT_HEADER)OBJECT_REMOVE_QUEUE_LOCKED)
{
/* Capture the pointer to the next object */
NextObject = (POBJECT_HEADER)ObjectHeader->NextToFree;
/* Destroy object */
DeleteObject(&ObjectHeader->Body, TRUE);
/* Advance to the next object */
ObjectHeader = NextObject;
}
/* Check if the queue is still locked */
if(RemoveObjectList == OBJECT_REMOVE_QUEUE_LOCKED)
{
/* Attempt to clear the lock */
OldValue = RTL::Atomic::CompareExchangePointer(&RemoveObjectList, OBJECT_REMOVE_QUEUE_LOCKED, NULLPTR);
if(OldValue == OBJECT_REMOVE_QUEUE_LOCKED)
{
/* Empty list, exit the worker */
break;
}
}
}
}
/**
* Increments the pointer reference count of an object.
*
* @param Object
* Supplies a pointer to the object body to reference.
*
* @return This routine returns the updated reference count.
*/
XTFASTCALL
LONG_PTR
OB::LifeCycle::ReferenceObject(IN PVOID Object)
{
POBJECT_HEADER ObjectHeader;
/* Resolve the object header */
ObjectHeader = CONTAIN_RECORD(Object, OBJECT_HEADER, Body);
/* Increment the reference count */
return RTL::Atomic::Increment64(&ObjectHeader->PointerCount);
}
/**
* Increments the pointer reference count of an object by a specified amount.
*
* @param Object
* Supplies a pointer to the object body to reference.
*
* @param Count
* Specifies a number of references to acquire.
*
* @return This routine returns the updated reference count.
*/
XTFASTCALL
LONG_PTR
OB::LifeCycle::ReferenceObject(IN PVOID Object,
IN ULONG Count)
{
POBJECT_HEADER ObjectHeader;
/* Resolve the object header */
ObjectHeader = CONTAIN_RECORD(Object, OBJECT_HEADER, Body);
/* Increment the reference count */
return RTL::Atomic::ExchangeAdd64(&ObjectHeader->PointerCount, Count) + Count;
}
/**
* Increments the query reference count of a NameInfo structure.
*
* @param ObjectHeader
* Supplies a pointer to the object header associated with the NameInfo.
*
* @return This routine returns a pointer to the referenced NameInfo, or NULLPTR if the reference could not be acquired.
*
* @since XT 1.0
*/
XTFASTCALL
POBJECT_HEADER_NAME_INFO
OB::LifeCycle::ReferenceObjectNameInformation(IN POBJECT_HEADER ObjectHeader)
{
LONG OldReferences, NewReferences, ActualReferences;
POBJECT_HEADER_NAME_INFO NameInfo;
/* Resolve the name information structure from the object header */
NameInfo = GetObjectNameInformation(ObjectHeader);
/* Ensure the name info structure is present */
if(!NameInfo)
{
/* Return NULL pointer */
return NULLPTR;
}
/* Initialize the old reference count snapshot */
OldReferences = *(volatile LONG*)&NameInfo->QueryReferences;
/* Enter the CAS loop */
while(TRUE)
{
/* Check if the reference count has dropped to zero */
if((OldReferences & ~OBJECT_NAME_LOCKED) == 0)
{
/* Abort and return NULL pointer */
return NULLPTR;
}
/* Calculate the incremented reference count */
NewReferences = OldReferences + 1;
/* Attempt to commit the incremented count */
ActualReferences = RTL::Atomic::CompareExchange32((PLONG)&NameInfo->QueryReferences, OldReferences, NewReferences);
if(ActualReferences == OldReferences)
{
/* Break the loop */
break;
}
/* State changed, update the snapshot and retry */
OldReferences = ActualReferences;
}
/* Check if the name is currently locked */
if(OldReferences & OBJECT_NAME_LOCKED)
{
/* Synchronize with the directory lock */
KE::PushLock::WaitOnPushLock(&NameInfo->Directory->Lock);
}
/* Return the referenced name info */
return NameInfo;
}
/**
* Releases security resources associated with an object creation information structure.
*
* @param CreateInfo
* Supplies a pointer to the object creation information structure to be released.
*
* @return This routine does not return any value.
*
* @since XT 1.0
*/
XTFASTCALL
VOID
OB::LifeCycle::ReleaseObjectCreateInformation(IN POBJECT_CREATE_INFORMATION CreateInfo)
{
/* Verify if a security descriptor was captured and is actively attached */
if(CreateInfo->SecurityDescriptor)
{
/* Release the descriptor */
SE::Descriptor::ReleaseSecurityDescriptor(CreateInfo->SecurityDescriptor, CreateInfo->ProbeMode, TRUE);
/* Nullify the pointer */
CreateInfo->SecurityDescriptor = NULLPTR;
}
}
/**
* Processes and returns pool quotas associated with an object's execution block.
*
* @param ObjectHeader
* Supplies a pointer to the object header.
*
* @param ObjectType
* Supplies a pointer to the object type definition.
*
* @return This routine does not return any value.
*
* @since XT 1.0
*/
XTAPI
VOID
OB::LifeCycle::ReturnObjectQuota(IN POBJECT_HEADER Header,
IN POBJECT_TYPE ObjectType)
{
POBJECT_HEADER_QUOTA_INFO Quota;
ULONG NonPagedPoolCharge;
ULONG PagedPoolCharge;
/* Verify if a quota block is charged for the object */
if(!Header->QuotaBlockCharged)
{
/* No quota charged, nothing to do */
return;
}
/* Retrieve the quota information header */
Quota = (POBJECT_HEADER_QUOTA_INFO)(Header->QuotaInfoOffset == 0 ? NULLPTR : ((PCHAR)Header - Header->QuotaInfoOffset));
if(Quota)
{
/* Extract the pool charges */
NonPagedPoolCharge = Quota->NonPagedPoolCharge;
PagedPoolCharge = Quota->PagedPoolCharge + Quota->SecurityDescriptorCharge;
}
else
{
/* No quota header present, use default charges */
NonPagedPoolCharge = ObjectType->TypeInfo.DefaultNonPagedPoolCharge;
PagedPoolCharge = ObjectType->TypeInfo.DefaultPagedPoolCharge;
/* Check if the object holds a default security descriptor quota */
if(Header->Flags & OBJECT_FLAG_SECURITY_QUOTA)
{
/* Add the default security quota to the paged pool charge */
PagedPoolCharge += SE_DEFAULT_SECURITY_QUOTA;
}
}
/* Return the calculated pool charges */
PS::Quota::ReturnSharedPoolQuota((PEPROCESS_QUOTA_BLOCK)Header->QuotaBlockCharged,
PagedPoolCharge, NonPagedPoolCharge);
/* Nullify the quota block pointer */
Header->QuotaBlockCharged = NULLPTR;
}