Process Hacker
ref.c
Go to the documentation of this file.
1 /*
2  * Process Hacker -
3  * object manager
4  *
5  * Copyright (C) 2009-2015 wj32
6  *
7  * This file is part of Process Hacker.
8  *
9  * Process Hacker is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation, either version 3 of the License, or
12  * (at your option) any later version.
13  *
14  * Process Hacker is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with Process Hacker. If not, see <http://www.gnu.org/licenses/>.
21  */
22 
23 #define _PH_REF_PRIVATE
24 #include <phbase.h>
25 #include <phintrnl.h>
26 #include <refp.h>
27 
32 
35 
36 static ULONG PhpAutoPoolTlsIndex;
37 
38 #ifdef DEBUG
39 LIST_ENTRY PhDbgObjectListHead;
40 PH_QUEUED_LOCK PhDbgObjectListLock = PH_QUEUED_LOCK_INIT;
41 PPH_CREATE_OBJECT_HOOK PhDbgCreateObjectHook = NULL;
42 #endif
43 
44 #define REF_STAT_UP(Name) PHLIB_INC_STATISTIC(Name)
45 
49 NTSTATUS PhInitializeRef(
50  VOID
51  )
52 {
53  PH_OBJECT_TYPE dummyObjectType;
54 
55 #ifdef DEBUG
56  InitializeListHead(&PhDbgObjectListHead);
57 #endif
58 
59  RtlInitializeSListHead(&PhObjectDeferDeleteListHead);
61  &PhObjectSmallFreeList,
64  );
65 
66  // Create the fundamental object type.
67 
68  memset(&dummyObjectType, 0, sizeof(PH_OBJECT_TYPE));
69  PhObjectTypeObject = &dummyObjectType; // PhCreateObject expects an object type.
70  PhObjectTypeTable[0] = &dummyObjectType; // PhCreateObject also expects PhObjectTypeTable[0] to be filled in.
71  PhObjectTypeObject = PhCreateObjectType(L"Type", 0, NULL);
72 
73  // Now that the fundamental object type exists, fix it up.
74  PhObjectToObjectHeader(PhObjectTypeObject)->TypeIndex = PhObjectTypeObject->TypeIndex;
75  PhObjectTypeObject->NumberOfObjects = 1;
76 
77  // Create the allocated memory object type.
78  PhAllocType = PhCreateObjectType(L"Alloc", 0, NULL);
79 
80  // Reserve a slot for the auto pool.
81  PhpAutoPoolTlsIndex = TlsAlloc();
82 
83  if (PhpAutoPoolTlsIndex == TLS_OUT_OF_INDEXES)
84  return STATUS_INSUFFICIENT_RESOURCES;
85 
86  return STATUS_SUCCESS;
87 }
88 
98  _In_ SIZE_T ObjectSize,
99  _In_ PPH_OBJECT_TYPE ObjectType
100  )
101 {
102  NTSTATUS status = STATUS_SUCCESS;
103  PPH_OBJECT_HEADER objectHeader;
104 
105  // Allocate storage for the object. Note that this includes the object header followed by the object body.
106  objectHeader = PhpAllocateObject(ObjectType, ObjectSize);
107 
108  // Object type statistics.
109  _InterlockedIncrement((PLONG)&ObjectType->NumberOfObjects);
110 
111  // Initialize the object header.
112  objectHeader->RefCount = 1;
113  objectHeader->TypeIndex = ObjectType->TypeIndex;
114  // objectHeader->Flags is set by PhpAllocateObject.
115 
116  REF_STAT_UP(RefObjectsCreated);
117 
118 #ifdef DEBUG
119  {
120  USHORT capturedFrames;
121 
122  capturedFrames = RtlCaptureStackBackTrace(1, 16, objectHeader->StackBackTrace, NULL);
123  memset(
124  &objectHeader->StackBackTrace[capturedFrames],
125  0,
126  sizeof(objectHeader->StackBackTrace) - capturedFrames * sizeof(PVOID)
127  );
128  }
129 
130  PhAcquireQueuedLockExclusive(&PhDbgObjectListLock);
131  InsertTailList(&PhDbgObjectListHead, &objectHeader->ObjectListEntry);
132  PhReleaseQueuedLockExclusive(&PhDbgObjectListLock);
133 
134  {
135  PPH_CREATE_OBJECT_HOOK dbgCreateObjectHook;
136 
137  dbgCreateObjectHook = PhDbgCreateObjectHook;
138 
139  if (dbgCreateObjectHook)
140  {
141  dbgCreateObjectHook(
142  PhObjectHeaderToObject(objectHeader),
143  ObjectSize,
144  0,
145  ObjectType
146  );
147  }
148  }
149 #endif
150 
151  return PhObjectHeaderToObject(objectHeader);
152 }
153 
162  _In_ PVOID Object
163  )
164 {
165  PPH_OBJECT_HEADER objectHeader;
166 
167  objectHeader = PhObjectToObjectHeader(Object);
168  // Increment the reference count.
169  _InterlockedIncrement(&objectHeader->RefCount);
170 
171  return Object;
172 }
173 
183  _In_ PVOID Object,
184  _In_ LONG RefCount
185  )
186 {
187  PPH_OBJECT_HEADER objectHeader;
188  LONG oldRefCount;
189 
190  assert(!(RefCount < 0));
191 
192  objectHeader = PhObjectToObjectHeader(Object);
193  // Increase the reference count.
194  oldRefCount = _InterlockedExchangeAdd(&objectHeader->RefCount, RefCount);
195 
196  return oldRefCount + RefCount;
197 }
198 
216  _In_ PVOID Object
217  )
218 {
219  PPH_OBJECT_HEADER objectHeader;
220  BOOLEAN result;
221 
222  objectHeader = PhObjectToObjectHeader(Object);
223  // Increase the reference count only if it isn't 0 (atomically).
224  result = PhpInterlockedIncrementSafe(&objectHeader->RefCount);
225 
226  return result;
227 }
228 
238  _In_ PVOID Object
239  )
240 {
241  PPH_OBJECT_HEADER objectHeader;
242  LONG newRefCount;
243 
244  objectHeader = PhObjectToObjectHeader(Object);
245  // Decrement the reference count.
246  newRefCount = _InterlockedDecrement(&objectHeader->RefCount);
247  ASSUME_ASSERT(newRefCount >= 0);
248 
249  // Free the object if it has 0 references.
250  if (newRefCount == 0)
251  {
252  PhpFreeObject(objectHeader);
253  }
254 }
255 
266  _In_ PVOID Object
267  )
268 {
269  return PhDereferenceObjectEx(Object, 1, TRUE) == 0;
270 }
271 
283  _In_ PVOID Object,
284  _In_ LONG RefCount,
285  _In_ BOOLEAN DeferDelete
286  )
287 {
288  PPH_OBJECT_HEADER objectHeader;
289  LONG oldRefCount;
290  LONG newRefCount;
291 
292  assert(!(RefCount < 0));
293 
294  objectHeader = PhObjectToObjectHeader(Object);
295 
296  // Decrease the reference count.
297  oldRefCount = _InterlockedExchangeAdd(&objectHeader->RefCount, -RefCount);
298  newRefCount = oldRefCount - RefCount;
299 
300  // Free the object if it has 0 references.
301  if (newRefCount == 0)
302  {
303  if (DeferDelete)
304  {
305  PhpDeferDeleteObject(objectHeader);
306  }
307  else
308  {
309  // Free the object.
310  PhpFreeObject(objectHeader);
311  }
312  }
313  else if (newRefCount < 0)
314  {
315  PhRaiseStatus(STATUS_INVALID_PARAMETER);
316  }
317 
318  return newRefCount;
319 }
320 
329  _In_ PVOID Object
330  )
331 {
332  return PhObjectTypeTable[PhObjectToObjectHeader(Object)->TypeIndex];
333 }
334 
351  _In_ PWSTR Name,
352  _In_ ULONG Flags,
353  _In_opt_ PPH_TYPE_DELETE_PROCEDURE DeleteProcedure
354  )
355 {
356  return PhCreateObjectTypeEx(
357  Name,
358  Flags,
359  DeleteProcedure,
360  NULL
361  );
362 }
363 
382  _In_ PWSTR Name,
383  _In_ ULONG Flags,
384  _In_opt_ PPH_TYPE_DELETE_PROCEDURE DeleteProcedure,
385  _In_opt_ PPH_OBJECT_TYPE_PARAMETERS Parameters
386  )
387 {
388  NTSTATUS status = STATUS_SUCCESS;
389  PPH_OBJECT_TYPE objectType;
390 
391  // Check the flags.
392  if ((Flags & PH_OBJECT_TYPE_VALID_FLAGS) != Flags) /* Valid flag mask */
393  PhRaiseStatus(STATUS_INVALID_PARAMETER_3);
394  if ((Flags & PH_OBJECT_TYPE_USE_FREE_LIST) && !Parameters)
395  PhRaiseStatus(STATUS_INVALID_PARAMETER_MIX);
396 
397  // Create the type object.
398  objectType = PhCreateObject(sizeof(PH_OBJECT_TYPE), PhObjectTypeObject);
399 
400  // Initialize the type object.
401  objectType->Flags = (USHORT)Flags;
402  objectType->TypeIndex = (USHORT)_InterlockedIncrement(&PhObjectTypeCount) - 1;
403  objectType->NumberOfObjects = 0;
404  objectType->DeleteProcedure = DeleteProcedure;
405  objectType->Name = Name;
406 
407  if (objectType->TypeIndex < PH_OBJECT_TYPE_TABLE_SIZE)
408  PhObjectTypeTable[objectType->TypeIndex] = objectType;
409  else
410  PhRaiseStatus(STATUS_UNSUCCESSFUL);
411 
412  if (Parameters)
413  {
414  if (Flags & PH_OBJECT_TYPE_USE_FREE_LIST)
415  {
417  &objectType->FreeList,
418  PhAddObjectHeaderSize(Parameters->FreeListSize),
419  Parameters->FreeListCount
420  );
421  }
422  }
423 
424  return objectType;
425 }
426 
434  _In_ PPH_OBJECT_TYPE ObjectType,
435  _Out_ PPH_OBJECT_TYPE_INFORMATION Information
436  )
437 {
438  Information->Name = ObjectType->Name;
439  Information->NumberOfObjects = ObjectType->NumberOfObjects;
440  Information->Flags = ObjectType->Flags;
441  Information->TypeIndex = ObjectType->TypeIndex;
442 }
443 
451  _In_ PPH_OBJECT_TYPE ObjectType,
452  _In_ SIZE_T ObjectSize
453  )
454 {
455  PPH_OBJECT_HEADER objectHeader;
456 
457  if (ObjectType->Flags & PH_OBJECT_TYPE_USE_FREE_LIST)
458  {
459  assert(ObjectType->FreeList.Size == PhAddObjectHeaderSize(ObjectSize));
460 
461  objectHeader = PhAllocateFromFreeList(&ObjectType->FreeList);
462  objectHeader->Flags = PH_OBJECT_FROM_TYPE_FREE_LIST;
463  REF_STAT_UP(RefObjectsAllocatedFromTypeFreeList);
464  }
465  else if (ObjectSize <= PH_OBJECT_SMALL_OBJECT_SIZE)
466  {
467  objectHeader = PhAllocateFromFreeList(&PhObjectSmallFreeList);
468  objectHeader->Flags = PH_OBJECT_FROM_SMALL_FREE_LIST;
469  REF_STAT_UP(RefObjectsAllocatedFromSmallFreeList);
470  }
471  else
472  {
473  objectHeader = PhAllocate(PhAddObjectHeaderSize(ObjectSize));
474  objectHeader->Flags = 0;
475  REF_STAT_UP(RefObjectsAllocated);
476  }
477 
478  return objectHeader;
479 }
480 
488  _In_ PPH_OBJECT_HEADER ObjectHeader
489  )
490 {
491  PPH_OBJECT_TYPE objectType;
492 
493  objectType = PhObjectTypeTable[ObjectHeader->TypeIndex];
494 
495  // Object type statistics.
496  _InterlockedDecrement(&objectType->NumberOfObjects);
497 
498 #ifdef DEBUG
499  PhAcquireQueuedLockExclusive(&PhDbgObjectListLock);
500  RemoveEntryList(&ObjectHeader->ObjectListEntry);
501  PhReleaseQueuedLockExclusive(&PhDbgObjectListLock);
502 #endif
503 
504  REF_STAT_UP(RefObjectsDestroyed);
505 
506  // Call the delete procedure if we have one.
507  if (objectType->DeleteProcedure)
508  {
509  objectType->DeleteProcedure(PhObjectHeaderToObject(ObjectHeader), 0);
510  }
511 
512  if (ObjectHeader->Flags & PH_OBJECT_FROM_TYPE_FREE_LIST)
513  {
514  PhFreeToFreeList(&objectType->FreeList, ObjectHeader);
515  REF_STAT_UP(RefObjectsFreedToTypeFreeList);
516  }
517  else if (ObjectHeader->Flags & PH_OBJECT_FROM_SMALL_FREE_LIST)
518  {
519  PhFreeToFreeList(&PhObjectSmallFreeList, ObjectHeader);
520  REF_STAT_UP(RefObjectsFreedToSmallFreeList);
521  }
522  else
523  {
524  PhFree(ObjectHeader);
525  REF_STAT_UP(RefObjectsFreed);
526  }
527 }
528 
536  _In_ PPH_OBJECT_HEADER ObjectHeader
537  )
538 {
539  PSLIST_ENTRY oldFirstEntry;
540 
541  oldFirstEntry = RtlFirstEntrySList(&PhObjectDeferDeleteListHead);
542  RtlInterlockedPushEntrySList(&PhObjectDeferDeleteListHead, &ObjectHeader->DeferDeleteListEntry);
543  REF_STAT_UP(RefObjectsDeleteDeferred);
544 
545  // Was the to-free list empty before? If so, we need to queue a work item.
546  if (!oldFirstEntry)
547  {
549  }
550 }
551 
556  _In_ PVOID Parameter
557  )
558 {
559  PSLIST_ENTRY listEntry;
560  PPH_OBJECT_HEADER objectHeader;
561 
562  // Clear the list and obtain the first object to free.
563  listEntry = RtlInterlockedFlushSList(&PhObjectDeferDeleteListHead);
564 
565  while (listEntry)
566  {
567  objectHeader = CONTAINING_RECORD(listEntry, PH_OBJECT_HEADER, DeferDeleteListEntry);
568  listEntry = listEntry->Next;
569 
570  PhpFreeObject(objectHeader);
571  }
572 
573  return STATUS_SUCCESS;
574 }
575 
584  _In_ SIZE_T Size
585  )
586 {
587  return PhCreateObject(Size, PhAllocType);
588 }
589 
594  VOID
595  )
596 {
597  return (PPH_AUTO_POOL)TlsGetValue(PhpAutoPoolTlsIndex);
598 }
599 
604  _In_ PPH_AUTO_POOL AutoPool
605  )
606 {
607  if (!TlsSetValue(PhpAutoPoolTlsIndex, AutoPool))
608  PhRaiseStatus(STATUS_UNSUCCESSFUL);
609 
610 #ifdef DEBUG
611  {
612  PPHP_BASE_THREAD_DBG dbg;
613 
614  dbg = (PPHP_BASE_THREAD_DBG)TlsGetValue(PhDbgThreadDbgTlsIndex);
615 
616  if (dbg)
617  {
618  dbg->CurrentAutoPool = AutoPool;
619  }
620  }
621 #endif
622 }
623 
633  _Out_ PPH_AUTO_POOL AutoPool
634  )
635 {
636  AutoPool->StaticCount = 0;
637  AutoPool->DynamicCount = 0;
638  AutoPool->DynamicAllocated = 0;
639  AutoPool->DynamicObjects = NULL;
640 
641  // Add the pool to the stack.
642  AutoPool->NextPool = PhpGetCurrentAutoPool();
643  PhpSetCurrentAutoPool(AutoPool);
644 
645  REF_STAT_UP(RefAutoPoolsCreated);
646 }
647 
657  _Inout_ PPH_AUTO_POOL AutoPool
658  )
659 {
660  PhDrainAutoPool(AutoPool);
661 
662  if (PhpGetCurrentAutoPool() != AutoPool)
663  PhRaiseStatus(STATUS_UNSUCCESSFUL);
664 
665  // Remove the pool from the stack.
666  PhpSetCurrentAutoPool(AutoPool->NextPool);
667 
668  // Free the dynamic array if it hasn't been freed yet.
669  if (AutoPool->DynamicObjects)
670  PhFree(AutoPool->DynamicObjects);
671 
672  REF_STAT_UP(RefAutoPoolsDestroyed);
673 }
674 
681  _In_ PPH_AUTO_POOL AutoPool
682  )
683 {
684  ULONG i;
685 
686  for (i = 0; i < AutoPool->StaticCount; i++)
687  PhDereferenceObject(AutoPool->StaticObjects[i]);
688 
689  AutoPool->StaticCount = 0;
690 
691  if (AutoPool->DynamicObjects)
692  {
693  for (i = 0; i < AutoPool->DynamicCount; i++)
694  {
695  PhDereferenceObject(AutoPool->DynamicObjects[i]);
696  }
697 
698  AutoPool->DynamicCount = 0;
699 
700  if (AutoPool->DynamicAllocated > PH_AUTO_POOL_DYNAMIC_BIG_SIZE)
701  {
702  AutoPool->DynamicAllocated = 0;
703  PhFree(AutoPool->DynamicObjects);
704  AutoPool->DynamicObjects = NULL;
705  }
706  }
707 }
708 
718  _In_opt_ PVOID Object
719  )
720 {
722 
723 #ifdef DEBUG
724  // If we don't have an auto-dereference pool, we don't want to leak the
725  // object (unlike what Apple does with NSAutoreleasePool).
726  if (!autoPool)
727  PhRaiseStatus(STATUS_UNSUCCESSFUL);
728 #endif
729 
730  if (!Object)
731  return NULL;
732 
733  // See if we can use the static array.
734  if (autoPool->StaticCount < PH_AUTO_POOL_STATIC_SIZE)
735  {
736  autoPool->StaticObjects[autoPool->StaticCount++] = Object;
737  return Object;
738  }
739 
740  // Use the dynamic array.
741 
742  // Allocate the array if we haven't already.
743  if (!autoPool->DynamicObjects)
744  {
745  autoPool->DynamicAllocated = 64;
746  autoPool->DynamicObjects = PhAllocate(
747  sizeof(PVOID) * autoPool->DynamicAllocated
748  );
749  REF_STAT_UP(RefAutoPoolsDynamicAllocated);
750  }
751 
752  // See if we need to resize the array.
753  if (autoPool->DynamicCount == autoPool->DynamicAllocated)
754  {
755  autoPool->DynamicAllocated *= 2;
756  autoPool->DynamicObjects = PhReAllocate(
757  autoPool->DynamicObjects,
758  sizeof(PVOID) * autoPool->DynamicAllocated
759  );
760  REF_STAT_UP(RefAutoPoolsDynamicResized);
761  }
762 
763  autoPool->DynamicObjects[autoPool->DynamicCount++] = Object;
764 
765  return Object;
766 }
767 
769 {
770  if (!Object)
771  PhRaiseStatus(STATUS_INVALID_PARAMETER);
772 
773  PhAutoDereferenceObject(Object);
774 }