Leaked source code of windows server 2003
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.

822 lines
18 KiB

  1. /*++
  2. Copyright (c) Microsoft Corporation. All rights reserved.
  3. Module Name:
  4. memory.c
  5. Abstract:
  6. Memory handling routines for Windows NT Setup API dll.
  7. Author:
  8. Ted Miller (tedm) 11-Jan-1995
  9. Revision History:
  10. Jamie Hunter (jamiehun) 13-Feb-1998
  11. Improved this further for debugging
  12. added linked list,
  13. alloc tracing,
  14. memory fills
  15. and memory leak detection
  16. jamiehun 30-April-1998
  17. Added some more consistancy checks
  18. Put try/except around access
  19. jimschm 27-Oct-1998
  20. Wrote fast allocation routines to speed up setupapi.dll on Win9x
  21. JamieHun Jun-26-2000
  22. Moved to sputils
  23. Changed to use a private heap
  24. --*/
  25. #include "precomp.h"
  26. #pragma hdrstop
  27. static BOOL Initialized = FALSE;
  28. static HANDLE _pSpUtilsHeap = NULL;
  29. #define ALLOC(x) HeapAlloc(_pSpUtilsHeap,0,x)
  30. #define FREE(x) HeapFree(_pSpUtilsHeap,0,x)
  31. #define REALLOC(x,y) HeapReAlloc(_pSpUtilsHeap,0,x,y)
  32. #define MEMSIZE(x) HeapSize(_pSpUtilsHeap,0,x)
  33. #define INITIALHEAPSIZE (0x100000)
  34. //
  35. // Internal debugging features
  36. //
  37. #if MEM_DBG
  38. #define MEMERROR(x) _pSpUtilsAssertFail(__FILE__,__LINE__,#x)
  39. DWORD _pSpUtilsDbgAllocNum = 0;
  40. DWORD _pSpUtilsMemoryFlags = 0;
  41. struct _MemHeader {
  42. struct _MemHeader * PrevAlloc; // previous on chain
  43. struct _MemHeader * NextAlloc; // next on chain
  44. DWORD MemoryTag; // tag - to pair off Malloc/Free
  45. DWORD BlockSize; // bytes of "real" data
  46. DWORD AllocNum; // number of this allocation, ie AllocCount at the time this was allocated
  47. PCSTR AllocFile; // name of file that did allocation, if set
  48. DWORD AllocLine; // line of this allocation
  49. DWORD HeadMemSig; // head-check, stop writing before actual data
  50. BYTE Data[sizeof(DWORD)]; // size allows for tail-check at end of actual data
  51. };
  52. struct _MemStats {
  53. struct _MemHeader * FirstAlloc; // will be NULL if no allocations, else earliest malloc/realloc in chain
  54. struct _MemHeader * LastAlloc; // last alloc/realloc goes to end of chain
  55. DWORD MemoryAllocated; // bytes, excluding headers
  56. DWORD AllocCount; // incremented for every alloc
  57. DWORD ReallocCount; // incremented for every realloc
  58. DWORD FreeCount; // incremented for every free
  59. BOOL DoneInitDebugMutex;
  60. CRITICAL_SECTION DebugMutex; // We need a mutex to manage memstats, setupapi is MT
  61. } _pSpUtilsMemStats = {
  62. NULL, NULL, 0, 0, 0, 0, FALSE, 0
  63. };
  64. //
  65. // Checked builds have a block head/tail check
  66. // and extra statistics
  67. //
  68. #define HEAD_MEMSIG 0x4d444554 // = MDET (MSB to LSB) or TEDM (LSB to MSB)
  69. #define TAIL_MEMSIG 0x5445444d // = TEDM (MSB to LSB) or MDET (LSB to MSB)
  70. #define MEM_ALLOCCHAR 0xdd // makes sure we fill with non-null
  71. #define MEM_FREECHAR 0xee // if we see this, memory has been de-allocated
  72. #define MEM_DEADSIG 0xdeaddead
  73. #define MEM_TOOBIG 0x80000000 // use this to pick up big allocs
  74. #define MemMutexLock() EnterCriticalSection(&_pSpUtilsMemStats.DebugMutex)
  75. #define MemMutexUnlock() LeaveCriticalSection(&_pSpUtilsMemStats.DebugMutex)
  76. static
  77. BOOL MemBlockCheck(
  78. struct _MemHeader * Mem
  79. )
  80. /*++
  81. Routine Description:
  82. Verify a block header is valid
  83. Arguments:
  84. Mem = Header to verify
  85. Returns:
  86. TRUE if valid
  87. FALSE if not valid
  88. ++*/
  89. {
  90. if (Mem == NULL) {
  91. return TRUE;
  92. }
  93. if (Mem->HeadMemSig != HEAD_MEMSIG) {
  94. MEMERROR("Internal heap error - HeadMemSig invalid");
  95. return FALSE;
  96. }
  97. if (Mem->BlockSize >= MEM_TOOBIG) {
  98. MEMERROR("Internal heap error - BlockSize too big");
  99. return FALSE;
  100. }
  101. if((Mem->PrevAlloc == Mem) || (Mem->NextAlloc == Mem)) {
  102. //
  103. // we should have failed the MEMSIG, but it's ok as an extra check
  104. //
  105. MEMERROR("Internal heap error - self link");
  106. return FALSE;
  107. }
  108. if ((*(DWORD UNALIGNED *)(Mem->Data+Mem->BlockSize)) != TAIL_MEMSIG) {
  109. MEMERROR("Internal heap error - TailMemSig invalid");
  110. return FALSE;
  111. }
  112. return TRUE;
  113. }
  114. static
  115. struct _MemHeader *
  116. MemBlockGet(
  117. IN PVOID Block
  118. )
  119. /*++
  120. Routine Description:
  121. Verify a block is valid, and return real memory pointer
  122. Arguments:
  123. Block - address the application uses
  124. ++*/
  125. {
  126. struct _MemHeader * Mem;
  127. if((DWORD_PTR)Block < offsetof(struct _MemHeader,Data[0])) {
  128. MEMERROR("Internal heap error - Block address is invalid");
  129. return NULL;
  130. }
  131. Mem = (struct _MemHeader *)(((PBYTE)Block) - offsetof(struct _MemHeader,Data[0]));
  132. if (MemBlockCheck(Mem)==FALSE) {
  133. //
  134. // block fails test
  135. //
  136. return NULL;
  137. }
  138. if(Mem->PrevAlloc != NULL) {
  139. if(MemBlockCheck(Mem->PrevAlloc)==FALSE) {
  140. //
  141. // back link is invalid
  142. //
  143. return NULL;
  144. }
  145. } else if (_pSpUtilsMemStats.FirstAlloc != Mem) {
  146. //
  147. // _pSpUtilsMemStats.FirstAlloc is invalid wrt Mem
  148. //
  149. MEMERROR("Internal heap error - FirstAlloc invalid");
  150. return NULL;
  151. }
  152. if(Mem->NextAlloc != NULL) {
  153. if(MemBlockCheck(Mem->NextAlloc)==FALSE) {
  154. //
  155. // forward link is invalid
  156. //
  157. return NULL;
  158. }
  159. } else if (_pSpUtilsMemStats.LastAlloc != Mem) {
  160. //
  161. // _pSpUtilsMemStats.LastAlloc is invalid wrt Mem
  162. //
  163. MEMERROR("Internal heap error - LastAlloc invalid");
  164. return NULL;
  165. }
  166. //
  167. // seems pretty good
  168. //
  169. return Mem;
  170. }
  171. static
  172. PVOID
  173. MemBlockLink(
  174. struct _MemHeader * Mem
  175. )
  176. {
  177. if (Mem == NULL) {
  178. return NULL;
  179. }
  180. Mem->PrevAlloc = _pSpUtilsMemStats.LastAlloc;
  181. Mem->NextAlloc = NULL;
  182. _pSpUtilsMemStats.LastAlloc = Mem;
  183. if (Mem->PrevAlloc == NULL) {
  184. _pSpUtilsMemStats.FirstAlloc = Mem;
  185. } else {
  186. if (MemBlockCheck(Mem->PrevAlloc)) {
  187. Mem->PrevAlloc->NextAlloc = Mem;
  188. }
  189. }
  190. Mem->HeadMemSig = HEAD_MEMSIG;
  191. *(DWORD UNALIGNED *)(Mem->Data+Mem->BlockSize) = TAIL_MEMSIG;
  192. return (PVOID)(Mem->Data);
  193. }
  194. static
  195. PVOID
  196. MemBlockUnLink(
  197. struct _MemHeader * Mem
  198. )
  199. {
  200. if (Mem == NULL) {
  201. return NULL;
  202. }
  203. if((Mem->PrevAlloc == Mem) || (Mem->NextAlloc == Mem) || (Mem->HeadMemSig == MEM_DEADSIG)) {
  204. MEMERROR("Internal heap error - MemBlockUnLink");
  205. }
  206. if (Mem->PrevAlloc == NULL) {
  207. _pSpUtilsMemStats.FirstAlloc = Mem->NextAlloc;
  208. } else {
  209. Mem->PrevAlloc->NextAlloc = Mem->NextAlloc;
  210. }
  211. if (Mem->NextAlloc == NULL) {
  212. _pSpUtilsMemStats.LastAlloc = Mem->PrevAlloc;
  213. } else {
  214. Mem->NextAlloc->PrevAlloc = Mem->PrevAlloc;
  215. }
  216. Mem->PrevAlloc = Mem; // make pointers harmless and also adds as an exta debug check
  217. Mem->NextAlloc = Mem; // make pointers harmless and also adds as an exta debug check
  218. Mem->HeadMemSig = MEM_DEADSIG;
  219. *(DWORD UNALIGNED *)(Mem->Data+Mem->BlockSize) = MEM_DEADSIG;
  220. return Mem->Data;
  221. }
  222. static
  223. BOOL
  224. MemDebugInitialize(
  225. VOID
  226. )
  227. {
  228. try {
  229. InitializeCriticalSection(&_pSpUtilsMemStats.DebugMutex);
  230. _pSpUtilsMemStats.DoneInitDebugMutex = TRUE;
  231. } except(EXCEPTION_EXECUTE_HANDLER) {
  232. }
  233. return _pSpUtilsMemStats.DoneInitDebugMutex;
  234. }
  235. static
  236. BOOL
  237. MemDebugUninitialize(
  238. VOID
  239. )
  240. {
  241. struct _MemHeader *Mem;
  242. TCHAR Msg[1024];
  243. TCHAR Process[MAX_PATH];
  244. //
  245. // Dump the leaks
  246. //
  247. Mem = _pSpUtilsMemStats.FirstAlloc;
  248. GetModuleFileName( GetModuleHandle(NULL),Process, sizeof(Process)/sizeof(TCHAR));
  249. while (Mem) {
  250. wsprintf (Msg, TEXT("SPUTILS: Leak (%d bytes) at %hs line %u (allocation #%d) in process %s \r\n"), Mem->BlockSize, Mem->AllocFile, Mem->AllocLine, Mem->AllocNum, Process );
  251. pSetupDebugPrintEx(DPFLTR_WARNING_LEVEL, Msg);
  252. if (_pSpUtilsMemoryFlags != 0) {
  253. if (Mem->BlockSize > 1024) {
  254. pSetupDebugPrintEx(DPFLTR_ERROR_LEVEL, TEXT("Leak of > 1K. Calling DebugBreak.\n"));
  255. DebugBreak();
  256. }
  257. }
  258. Mem = Mem->NextAlloc;
  259. }
  260. //
  261. // Clean up
  262. //
  263. if(_pSpUtilsMemStats.DoneInitDebugMutex) {
  264. DeleteCriticalSection(&_pSpUtilsMemStats.DebugMutex);
  265. }
  266. //
  267. // any last minute checks
  268. //
  269. return TRUE;
  270. }
  271. #endif // MEM_DBG
  272. //
  273. // published functions
  274. //
  275. PVOID
  276. pSetupDebugMallocWithTag(
  277. IN DWORD Size,
  278. IN PCSTR Filename,
  279. IN DWORD Line,
  280. IN DWORD Tag
  281. )
  282. /*++
  283. Routine Description:
  284. Debug version of Malloc
  285. Resulting allocated block has prefix/suffix and is filled with MEM_ALLOCCHAR
  286. Arguments:
  287. Size - size in bytes of block to be allocated. The size may be 0.
  288. Filename/Line - debugging information
  289. Tag - match malloc with free/realloc's
  290. Return Value:
  291. Pointer to block of memory, or NULL if a block could not be allocated.
  292. --*/
  293. {
  294. #if MEM_DBG
  295. struct _MemHeader *Mem;
  296. PVOID Ptr = NULL;
  297. BOOL locked = FALSE;
  298. LPEXCEPTION_POINTERS ExceptionPointers = NULL;
  299. MYASSERT(Initialized);
  300. try {
  301. MemMutexLock();
  302. locked = TRUE;
  303. _pSpUtilsMemStats.AllocCount++;
  304. if (Size >= MEM_TOOBIG) {
  305. MEMERROR("pSetupDebugMalloc - requested size too big (negative?)");
  306. leave;
  307. }
  308. if((Mem = (struct _MemHeader*) ALLOC(Size+sizeof(struct _MemHeader))) == NULL) {
  309. leave; // it failed ALLOC, but prob not due to a bug
  310. }
  311. Mem->MemoryTag = Tag;
  312. Mem->BlockSize = Size;
  313. Mem->AllocNum = _pSpUtilsMemStats.AllocCount;
  314. Mem->AllocFile = Filename;
  315. Mem->AllocLine = Line;
  316. // init memory we have allocated (to make sure we don't accidently get zero's)
  317. FillMemory(Mem->Data,Size,MEM_ALLOCCHAR);
  318. _pSpUtilsMemStats.MemoryAllocated += Size;
  319. Ptr = MemBlockLink(Mem);
  320. if (_pSpUtilsMemoryFlags && (_pSpUtilsDbgAllocNum == Mem->AllocNum)) {
  321. MEMERROR("_pSpUtilsDbgAllocNum hit");
  322. }
  323. } except(ExceptionPointers = GetExceptionInformation(),
  324. EXCEPTION_EXECUTE_HANDLER) {
  325. MEMERROR("pSetupDebugMalloc - Exception");
  326. Ptr = NULL;
  327. }
  328. if(locked) {
  329. MemMutexUnlock();
  330. }
  331. return Ptr;
  332. #else
  333. return ALLOC(Size);
  334. #endif
  335. }
  336. PVOID
  337. pSetupDebugMalloc(
  338. IN DWORD Size,
  339. IN PCSTR Filename,
  340. IN DWORD Line
  341. )
  342. /*++
  343. Routine Description:
  344. Allocate a chunk of memory. The memory is not zero-initialized.
  345. Arguments:
  346. Size - size in bytes of block to be allocated. The size may be 0.
  347. Return Value:
  348. Pointer to block of memory, or NULL if a block could not be allocated.
  349. --*/
  350. {
  351. MYASSERT(Initialized);
  352. #if MEM_DBG
  353. return pSetupDebugMallocWithTag(Size, Filename , Line, 0);
  354. #else
  355. return ALLOC(Size);
  356. #endif
  357. }
  358. PVOID
  359. pSetupMalloc(
  360. IN DWORD Size
  361. )
  362. /*++
  363. Routine Description:
  364. Allocate a chunk of memory. The memory is not zero-initialized.
  365. Arguments:
  366. Size - size in bytes of block to be allocated. The size may be 0.
  367. Return Value:
  368. Pointer to block of memory, or NULL if a block could not be allocated.
  369. --*/
  370. {
  371. MYASSERT(Initialized);
  372. #if MEM_DBG
  373. return pSetupDebugMallocWithTag(Size, NULL , 0, 0);
  374. #else
  375. return ALLOC(Size);
  376. #endif
  377. }
  378. PVOID
  379. pSetupReallocWithTag(
  380. IN PVOID Block,
  381. IN DWORD NewSize,
  382. IN DWORD Tag
  383. )
  384. /*++
  385. Routine Description:
  386. Realloc routine Debug/Non-Debug versions
  387. Note that a general assumption here, is that if NewSize <= OriginalSize
  388. the reallocation *should* not fail
  389. Arguments:
  390. Block - pointer to block to be reallocated.
  391. NewSize - new size in bytes of block. If the size is 0, this function
  392. works like pSetupFree, and the return value is NULL.
  393. Tag - match realloc with malloc
  394. Return Value:
  395. Pointer to block of memory, or NULL if a block could not be allocated.
  396. In that case the original block remains unchanged.
  397. --*/
  398. {
  399. #if MEM_DBG
  400. PVOID p;
  401. DWORD OldSize;
  402. struct _MemHeader *Mem;
  403. PVOID Ptr = NULL;
  404. BOOL locked = FALSE;
  405. LPEXCEPTION_POINTERS ExceptionPointers = NULL;
  406. MYASSERT(Initialized);
  407. try {
  408. MemMutexLock();
  409. locked = TRUE;
  410. _pSpUtilsMemStats.ReallocCount++;
  411. if (Block == NULL) {
  412. leave;
  413. }
  414. if (NewSize >= MEM_TOOBIG) {
  415. MEMERROR("pSetupRealloc - requested size too big (negative?)");
  416. leave;
  417. }
  418. Mem = MemBlockGet(Block);
  419. if (Mem == NULL) {
  420. leave;
  421. }
  422. if (Mem->MemoryTag != Tag) {
  423. MEMERROR("pSetupRealloc - Tag mismatch");
  424. leave;
  425. }
  426. OldSize = Mem->BlockSize;
  427. MemBlockUnLink(Mem);
  428. if (NewSize < OldSize) {
  429. // trash memory we're about to free
  430. FillMemory(Mem->Data+NewSize,OldSize-NewSize+sizeof(DWORD),MEM_FREECHAR);
  431. }
  432. if((p = REALLOC(Mem, NewSize+sizeof(struct _MemHeader))) == NULL) {
  433. //
  434. // failed to re-alloc
  435. //
  436. MemBlockLink(Mem);
  437. leave;
  438. }
  439. Mem = (struct _MemHeader*)p;
  440. Mem->BlockSize = NewSize;
  441. if (NewSize > OldSize) {
  442. // init extra memory we have allocated
  443. FillMemory(Mem->Data+OldSize,NewSize-OldSize,MEM_ALLOCCHAR);
  444. }
  445. _pSpUtilsMemStats.MemoryAllocated -= OldSize;
  446. _pSpUtilsMemStats.MemoryAllocated += NewSize;
  447. Ptr = MemBlockLink(Mem);
  448. } except(ExceptionPointers = GetExceptionInformation(),
  449. EXCEPTION_EXECUTE_HANDLER) {
  450. MEMERROR("pSetupRealloc - Exception");
  451. Ptr = NULL;
  452. }
  453. if(locked) {
  454. MemMutexUnlock();
  455. }
  456. return Ptr;
  457. #else
  458. return REALLOC(Block, NewSize);
  459. #endif
  460. }
  461. PVOID
  462. pSetupRealloc(
  463. IN PVOID Block,
  464. IN DWORD NewSize
  465. )
  466. /*++
  467. Routine Description:
  468. Realloc routine Debug/Non-Debug versions
  469. Note that a general assumption here, is that if NewSize <= OriginalSize
  470. the reallocation *should* not fail
  471. Arguments:
  472. Block - pointer to block to be reallocated.
  473. NewSize - new size in bytes of block. If the size is 0, this function
  474. works like pSetupFree, and the return value is NULL.
  475. Return Value:
  476. Pointer to block of memory, or NULL if a block could not be allocated.
  477. In that case the original block remains unchanged.
  478. --*/
  479. {
  480. #if MEM_DBG
  481. return pSetupReallocWithTag(Block,NewSize,0);
  482. #else
  483. return REALLOC(Block, NewSize);
  484. #endif
  485. }
  486. VOID
  487. pSetupFreeWithTag(
  488. IN CONST VOID *Block,
  489. IN DWORD Tag
  490. )
  491. /*++
  492. Routine Description:
  493. Free (debug/non-debug versions)
  494. Arguments:
  495. Buffer - pointer to block to be freed.
  496. Tag - match free with malloc
  497. Return Value:
  498. None.
  499. --*/
  500. {
  501. #if MEM_DBG
  502. DWORD OldSize;
  503. struct _MemHeader *Mem;
  504. BOOL locked = FALSE;
  505. LPEXCEPTION_POINTERS ExceptionPointers = NULL;
  506. MYASSERT(Initialized);
  507. try {
  508. MemMutexLock();
  509. locked = TRUE;
  510. _pSpUtilsMemStats.FreeCount++;
  511. if (Block == NULL) {
  512. leave;
  513. }
  514. Mem = MemBlockGet((PVOID)Block);
  515. if (Mem == NULL) {
  516. leave;
  517. }
  518. if (Mem->MemoryTag != Tag) {
  519. MEMERROR("pSetupFree - Tag mismatch");
  520. leave;
  521. }
  522. OldSize = Mem->BlockSize;
  523. MemBlockUnLink(Mem);
  524. _pSpUtilsMemStats.MemoryAllocated -= OldSize;
  525. //
  526. // trash memory we're about to free, so we can immediately see it has been free'd!!!!
  527. // we keep head/tail stuff to have more info available when debugging
  528. //
  529. FillMemory((PVOID)Block,OldSize,MEM_FREECHAR);
  530. Mem->MemoryTag = (DWORD)(-1);
  531. FREE(Mem);
  532. } except(ExceptionPointers = GetExceptionInformation(),
  533. EXCEPTION_EXECUTE_HANDLER) {
  534. MEMERROR("pSetupFree - Exception");
  535. }
  536. if(locked) {
  537. MemMutexUnlock();
  538. }
  539. #else
  540. FREE ((void *)Block);
  541. #endif
  542. }
  543. VOID
  544. pSetupFree(
  545. IN CONST VOID *Block
  546. )
  547. /*++
  548. Routine Description:
  549. Free (debug/non-debug versions)
  550. Arguments:
  551. Buffer - pointer to block to be freed.
  552. Return Value:
  553. None.
  554. --*/
  555. {
  556. #if MEM_DBG
  557. pSetupFreeWithTag(Block,0);
  558. #else
  559. FREE ((void *)Block);
  560. #endif
  561. }
  562. HANDLE
  563. pSetupGetHeap(
  564. VOID
  565. )
  566. {
  567. MYASSERT(Initialized);
  568. return _pSpUtilsHeap;
  569. }
  570. //
  571. // initialization functions
  572. //
  573. BOOL
  574. _pSpUtilsMemoryInitialize(
  575. VOID
  576. )
  577. {
  578. #if MEM_DBG
  579. _pSpUtilsHeap = HeapCreate(0,INITIALHEAPSIZE,0);
  580. if(_pSpUtilsHeap == NULL) {
  581. return FALSE;
  582. }
  583. MemDebugInitialize();
  584. #else
  585. _pSpUtilsHeap = GetProcessHeap();
  586. #endif
  587. #if MEM_DBG
  588. #endif
  589. Initialized = TRUE;
  590. return TRUE;
  591. }
  592. BOOL
  593. _pSpUtilsMemoryUninitialize(
  594. VOID
  595. )
  596. {
  597. if(Initialized) {
  598. #if MEM_DBG
  599. MemDebugUninitialize();
  600. if(_pSpUtilsHeap) {
  601. HeapDestroy(_pSpUtilsHeap);
  602. _pSpUtilsHeap = NULL;
  603. }
  604. #endif
  605. Initialized = FALSE;
  606. }
  607. return TRUE;
  608. }