/** * PROJECT: ExectOS * COPYRIGHT: See COPYING.md in the top level directory * FILE: xtoskrnl/ob/lifecycl.cc * DESCRIPTION: Object Manager Lifecycle Management * DEVELOPERS: Aiken Harris */ #include /** * 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::KThread::EnterCriticalRegion(); 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); KE::KThread::LeaveCriticalRegion(); } /* 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; }