Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

294 lines
6.3 KiB

  1. /*++
  2. Copyright (c) 2000 Microsoft Corporation
  3. Module Name:
  4. HeapLookasideFree.cpp
  5. Abstract:
  6. Check for the following heap management problems:
  7. 1. Delay heap free calls by command line
  8. 2. Validate free calls to make sure they are in the correct heap.
  9. 3. Allocate new blocks out of the delay free pool if the size is
  10. identical
  11. The delay of calls is implemented by means of circular array. As soon
  12. as it's full, the oldest free call is validated and executed.
  13. Notes:
  14. This is a general purpose shim.
  15. History:
  16. 04/03/2000 linstev Created
  17. --*/
  18. #include "precomp.h"
  19. IMPLEMENT_SHIM_BEGIN(HeapLookasideFree)
  20. #include "ShimHookMacro.h"
  21. APIHOOK_ENUM_BEGIN
  22. APIHOOK_ENUM_ENTRY(RtlAllocateHeap)
  23. APIHOOK_ENUM_ENTRY(RtlFreeHeap)
  24. APIHOOK_ENUM_ENTRY(HeapDestroy)
  25. APIHOOK_ENUM_END
  26. #define DEFAULT_BUFFER_SIZE 16
  27. DWORD g_dwBufferSize;
  28. DWORD g_bHead, g_bTail;
  29. CRITICAL_SECTION g_csHeap;
  30. struct ENTRY
  31. {
  32. HANDLE hHeap;
  33. PVOID lpMem;
  34. ULONG Flags;
  35. ULONG Size;
  36. };
  37. ENTRY *g_pEntry;
  38. /*++
  39. Try and find an entry in the list.
  40. --*/
  41. PVOID
  42. APIHOOK(RtlAllocateHeap)(
  43. HANDLE hHeap,
  44. ULONG Flags,
  45. SIZE_T Size
  46. )
  47. {
  48. PVOID pRet = NULL;
  49. // Make sure we are the only ones touching our heap list
  50. EnterCriticalSection(&g_csHeap);
  51. // Check if we are active - we may have shut down already.
  52. if (g_pEntry && Size)
  53. {
  54. DWORD bTail = (g_bTail + g_dwBufferSize - 1) % g_dwBufferSize;
  55. DWORD bHead = (g_bHead + g_dwBufferSize - 1) % g_dwBufferSize;
  56. while (bTail != bHead)
  57. {
  58. ENTRY *pEntry = g_pEntry + bTail;
  59. if ((pEntry->Size == Size) &&
  60. (pEntry->hHeap == hHeap) &&
  61. (pEntry->Flags == Flags))
  62. {
  63. pRet = pEntry->lpMem;
  64. pEntry->hHeap = 0;
  65. break;
  66. }
  67. bTail = (bTail + 1) % g_dwBufferSize;
  68. }
  69. }
  70. if (!pRet)
  71. {
  72. pRet = ORIGINAL_API(RtlAllocateHeap)(hHeap, Flags, Size);
  73. }
  74. // Done using the list
  75. LeaveCriticalSection(&g_csHeap);
  76. if (!pRet)
  77. {
  78. DPFN( eDbgLevelWarning,
  79. "Allocation of size %d failed", Size);
  80. }
  81. return pRet;
  82. }
  83. /*++
  84. Buffer the call and free the oldest entry if it's valid.
  85. --*/
  86. BOOL
  87. APIHOOK(RtlFreeHeap)(
  88. PVOID hHeap,
  89. ULONG Flags,
  90. PVOID lpMem
  91. )
  92. {
  93. BOOL bRet = TRUE;
  94. // Check if we are active - we may have shut down already.
  95. if (g_pEntry && lpMem)
  96. {
  97. // Make sure we are the only ones touching our heap list
  98. EnterCriticalSection(&g_csHeap);
  99. // Go ahead and free the oldest allocation
  100. ENTRY *pEntry = g_pEntry + g_bHead;
  101. if (pEntry->hHeap)
  102. {
  103. if (HeapValidate(
  104. pEntry->hHeap,
  105. pEntry->Flags,
  106. pEntry->lpMem))
  107. {
  108. ORIGINAL_API(RtlFreeHeap)(
  109. pEntry->hHeap,
  110. pEntry->Flags,
  111. pEntry->lpMem);
  112. pEntry->hHeap = 0;
  113. }
  114. }
  115. // Add a new entry to the table
  116. __try
  117. {
  118. pEntry = g_pEntry + g_bTail;
  119. pEntry->Size = HeapSize(hHeap, Flags, lpMem);
  120. pEntry->hHeap = hHeap;
  121. pEntry->Flags = Flags;
  122. pEntry->lpMem = lpMem;
  123. g_bHead = (g_bHead + 1) % g_dwBufferSize;
  124. g_bTail = (g_bTail + 1) % g_dwBufferSize;
  125. }
  126. __except(1)
  127. {
  128. }
  129. // Done using the list
  130. LeaveCriticalSection(&g_csHeap);
  131. }
  132. else
  133. {
  134. // We're no longer active, so just work normally
  135. bRet = ORIGINAL_API(RtlFreeHeap)(
  136. hHeap,
  137. Flags,
  138. lpMem);
  139. }
  140. return bRet;
  141. }
  142. /*++
  143. Clear all entries of this heap from our table.
  144. --*/
  145. BOOL
  146. APIHOOK(HeapDestroy)(
  147. HANDLE hHeap
  148. )
  149. {
  150. // Make sure we are the only ones touching our heap list
  151. EnterCriticalSection(&g_csHeap);
  152. if (g_pEntry)
  153. {
  154. // Remove entries in this heap from our list
  155. for (ULONG i=0; i<g_dwBufferSize; i++)
  156. {
  157. ENTRY *pEntry = g_pEntry + i;
  158. if (pEntry->hHeap == hHeap)
  159. {
  160. pEntry->hHeap = 0;
  161. }
  162. }
  163. }
  164. // We're done with the list
  165. LeaveCriticalSection(&g_csHeap);
  166. return ORIGINAL_API(HeapDestroy)(hHeap);
  167. }
  168. /*++
  169. Handle DLL_PROCESS_ATTACH and DLL_PROCESS_DETACH in your notify function
  170. to do initialization and uninitialization.
  171. IMPORTANT: Make sure you ONLY call NTDLL and KERNEL32 APIs during
  172. DLL_PROCESS_ATTACH notification. No other DLLs are initialized at that
  173. point.
  174. If your shim cannot initialize properly, return FALSE and none of the
  175. APIs specified will be hooked.
  176. --*/
  177. BOOL
  178. NOTIFY_FUNCTION(
  179. DWORD fdwReason)
  180. {
  181. if (fdwReason == DLL_PROCESS_ATTACH)
  182. {
  183. CSTRING_TRY
  184. {
  185. CString csCl(COMMAND_LINE);
  186. if (! csCl.IsEmpty())
  187. {
  188. WCHAR * unused;
  189. g_dwBufferSize= wcstol(csCl, &unused, 10);
  190. }
  191. if (g_dwBufferSize == 0)
  192. {
  193. g_dwBufferSize = DEFAULT_BUFFER_SIZE;
  194. }
  195. InitializeCriticalSection(&g_csHeap);
  196. g_bHead = 0;
  197. g_bTail = g_dwBufferSize - 1;
  198. g_pEntry = (ENTRY *)VirtualAlloc(0,
  199. sizeof(ENTRY) * g_dwBufferSize,
  200. MEM_COMMIT,
  201. PAGE_READWRITE);
  202. }
  203. CSTRING_CATCH
  204. {
  205. return FALSE;
  206. }
  207. return TRUE;
  208. }
  209. if (fdwReason == DLL_PROCESS_DETACH)
  210. {
  211. EnterCriticalSection(&g_csHeap);
  212. VirtualFree(g_pEntry, 0, MEM_RELEASE);
  213. g_pEntry = (ENTRY *)NULL;
  214. LeaveCriticalSection(&g_csHeap);
  215. // Don't delete this critical section in case we get called after detach
  216. // DeleteCriticalSection(&g_csHeap);
  217. return TRUE;
  218. }
  219. return TRUE;
  220. }
  221. /*++
  222. Register hooked functions
  223. --*/
  224. HOOK_BEGIN
  225. APIHOOK_ENTRY(NTDLL.DLL, RtlAllocateHeap)
  226. APIHOOK_ENTRY(NTDLL.DLL, RtlFreeHeap)
  227. APIHOOK_ENTRY(KERNEL32.DLL, HeapDestroy)
  228. CALL_NOTIFY_FUNCTION
  229. HOOK_END
  230. IMPLEMENT_SHIM_END