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.

746 lines
21 KiB

  1. /*++
  2. Copyright (c) 2000 Microsoft Corporation
  3. Module Name:
  4. cmdown.c
  5. Abstract:
  6. This module cleans up all the memory used by CM.
  7. Author:
  8. Dragos C. Sambotin (dragoss) 21-Feb-00
  9. Environment:
  10. This routine is intended to be called at system shutdown
  11. in order to detect memory leaks. It is supposed to free
  12. all registry data that is not freed by CmShutdownSystem.
  13. Revision History:
  14. --*/
  15. #include "cmp.h"
  16. //
  17. // externals
  18. //
  19. extern LIST_ENTRY CmpHiveListHead;
  20. extern PUCHAR CmpStashBuffer;
  21. extern PCM_KEY_HASH *CmpCacheTable;
  22. extern ULONG CmpDelayedCloseSize;
  23. extern CM_DELAYED_CLOSE_ENTRY *CmpDelayedCloseTable;
  24. extern PCM_NAME_HASH *CmpNameCacheTable;
  25. extern BOOLEAN HvShutdownComplete;
  26. extern BOOLEAN CmFirstTime;
  27. extern HIVE_LIST_ENTRY CmpMachineHiveList[];
  28. VOID
  29. CmpFreeAllMemory(
  30. VOID
  31. );
  32. VOID
  33. CmpDereferenceNameControlBlockWithLock(
  34. PCM_NAME_CONTROL_BLOCK Ncb
  35. );
  36. VOID
  37. CmpDumpKeyBodyList(
  38. IN PCM_KEY_CONTROL_BLOCK kcb,
  39. IN PULONG Count,
  40. IN PVOID Context
  41. );
  42. #ifdef CM_SAVE_KCB_CACHE
  43. VOID
  44. CmpSaveKcbCache(
  45. VOID
  46. );
  47. #endif //CM_SAVE_KCB_CACHE
  48. #ifdef ALLOC_PRAGMA
  49. #pragma alloc_text(PAGE,CmpFreeAllMemory)
  50. #pragma alloc_text(PAGE,CmShutdownSystem)
  51. #ifdef CM_SAVE_KCB_CACHE
  52. #pragma alloc_text(PAGE,CmpSaveKcbCache)
  53. #endif //CM_SAVE_KCB_CACHE
  54. #endif
  55. VOID
  56. CmpFreeAllMemory(
  57. VOID
  58. )
  59. /*++
  60. Routine Description:
  61. - All hives are freed
  62. - KCB table is freed
  63. - Name hash table is freed
  64. - delay close table is freed - question: We need to clean/free all delayed close KCBs
  65. - all notifications/postblocks-aso.
  66. * equivalent with MmReleaseAllMemory
  67. Arguments:
  68. Return Value:
  69. --*/
  70. {
  71. PCMHIVE CmHive;
  72. LONG i;
  73. PCM_KEY_CONTROL_BLOCK KeyControlBlock;
  74. PCM_DELAYED_CLOSE_ENTRY DelayedEntry;
  75. PLIST_ENTRY NotifyPtr;
  76. PCM_NOTIFY_BLOCK NotifyBlock;
  77. PCM_POST_BLOCK PostBlock;
  78. PCM_KEY_HASH Current;
  79. PLIST_ENTRY AnchorAddr;
  80. ULONG Count;
  81. BOOLEAN MessageDisplayed;
  82. //
  83. // Iterate through the list of the hives in the system
  84. //
  85. while (IsListEmpty(&CmpHiveListHead) == FALSE) {
  86. //
  87. // Remove the hive from the list
  88. //
  89. CmHive = (PCMHIVE)RemoveHeadList(&CmpHiveListHead);
  90. CmHive = (PCMHIVE)CONTAINING_RECORD(CmHive,
  91. CMHIVE,
  92. HiveList);
  93. //
  94. // close hive handles (the ones that are open)
  95. //
  96. for (i=0; i<HFILE_TYPE_MAX; i++) {
  97. // these should be closed by CmShutdownSystem
  98. ASSERT( CmHive->FileHandles[i] == NULL );
  99. /*
  100. if (CmHive->FileHandles[i] != NULL) {
  101. CmCloseHandle(CmHive->FileHandles[i]);
  102. CmHive->FileHandles[i] = NULL;
  103. }
  104. */ }
  105. //
  106. // free the hive lock and view lock
  107. //
  108. ASSERT( CmHive->HiveLock != NULL );
  109. ExFreePool(CmHive->HiveLock);
  110. ASSERT( CmHive->ViewLock != NULL );
  111. ExFreePool(CmHive->ViewLock);
  112. /*
  113. DRAGOSS: we don't want ot do that! rather, we want to detect why we still
  114. have notifications at this point!!!!
  115. //
  116. // free notify-related stuff
  117. //
  118. NotifyPtr = &(CmHive->NotifyList);
  119. NotifyPtr = NotifyPtr->Flink;
  120. while( NotifyPtr != NULL ) {
  121. NotifyBlock = CONTAINING_RECORD(NotifyPtr, CM_NOTIFY_BLOCK, HiveList);
  122. // free post blocks; we assume that all threads have been terminated at this point
  123. while (IsListEmpty(&(NotifyBlock->PostList)) == FALSE) {
  124. PostBlock = (PCM_POST_BLOCK)RemoveHeadList(&(NotifyBlock->PostList));
  125. PostBlock = CONTAINING_RECORD(PostBlock,
  126. CM_POST_BLOCK,
  127. NotifyList);
  128. if( PostBlock->PostKeyBody ) {
  129. ExFreePool(PostBlock->PostKeyBody);
  130. }
  131. if( IsMasterPostBlock(PostBlock) ) {
  132. //
  133. // this members are allocated only for master post blocks
  134. //
  135. switch (PostBlockType(PostBlock)) {
  136. case PostSynchronous:
  137. ExFreePool(PostBlock->u->Sync.SystemEvent);
  138. break;
  139. case PostAsyncUser:
  140. ExFreePool(PostBlock->u->AsyncUser.Apc);
  141. break;
  142. case PostAsyncKernel:
  143. break;
  144. }
  145. ExFreePool(PostBlock->u);
  146. }
  147. ExFreePool(PostBlock);
  148. }
  149. NotifyPtr = NotifyPtr->Flink;
  150. ExFreePool(NotifyBlock);
  151. }
  152. */
  153. //
  154. // Spew in the debugger the names of the keynodes having notifies still set
  155. //
  156. NotifyPtr = &(CmHive->NotifyList);
  157. NotifyPtr = NotifyPtr->Flink;
  158. MessageDisplayed = FALSE;
  159. while( NotifyPtr != NULL ) {
  160. NotifyBlock = CONTAINING_RECORD(NotifyPtr, CM_NOTIFY_BLOCK, HiveList);
  161. AnchorAddr = &(NotifyBlock->PostList);
  162. PostBlock = (PCM_POST_BLOCK)(NotifyBlock->PostList.Flink);
  163. //
  164. // walk through the list and spew the keynames and postblock types.
  165. //
  166. while ( PostBlock != (PCM_POST_BLOCK)AnchorAddr ) {
  167. PostBlock = CONTAINING_RECORD(PostBlock,
  168. CM_POST_BLOCK,
  169. NotifyList);
  170. if( PostBlock->PostKeyBody ) {
  171. if( MessageDisplayed == FALSE ){
  172. MessageDisplayed = TRUE;
  173. DbgPrintEx(DPFLTR_CONFIG_ID,DPFLTR_ERROR_LEVEL,"Dumping untriggered notifications for hive (%lx) (%.*S) \n\n",CmHive,
  174. HBASE_NAME_ALLOC / sizeof(WCHAR),CmHive->Hive.BaseBlock->FileName);
  175. }
  176. switch (PostBlockType(PostBlock)) {
  177. case PostSynchronous:
  178. DbgPrintEx(DPFLTR_CONFIG_ID,DPFLTR_ERROR_LEVEL,"Synchronous ");
  179. break;
  180. case PostAsyncUser:
  181. DbgPrintEx(DPFLTR_CONFIG_ID,DPFLTR_ERROR_LEVEL,"AsyncUser ");
  182. break;
  183. case PostAsyncKernel:
  184. DbgPrintEx(DPFLTR_CONFIG_ID,DPFLTR_ERROR_LEVEL,"AsyncKernel ");
  185. break;
  186. }
  187. DbgPrintEx(DPFLTR_CONFIG_ID,DPFLTR_ERROR_LEVEL,"Notification, PostBlock %p not triggered on KCB %p\n",PostBlock,
  188. PostBlock->PostKeyBody->KeyBody->KeyControlBlock);
  189. }
  190. //
  191. // skip to the next element
  192. //
  193. PostBlock = (PCM_POST_BLOCK)(PostBlock->NotifyList.Flink);
  194. }
  195. NotifyPtr = NotifyPtr->Flink;
  196. }
  197. //
  198. // free security cache
  199. //
  200. CmpDestroySecurityCache (CmHive);
  201. //
  202. // free the hv level structure
  203. //
  204. HvFreeHive(&(CmHive->Hive));
  205. //
  206. // free the cm level structure
  207. //
  208. CmpFree(CmHive, sizeof(CMHIVE));
  209. }
  210. //
  211. // Now free the CM globals
  212. //
  213. // the stash buffer
  214. if( CmpStashBuffer != NULL ) {
  215. ExFreePool( CmpStashBuffer );
  216. }
  217. //
  218. // first, take care of all delayed closed KCBs
  219. // free their memory and dereference all the related.
  220. // name, hint, KeyHash
  221. //
  222. for (i=0; i<(LONG)CmpDelayedCloseSize; i++) {
  223. DelayedEntry = &(CmpDelayedCloseTable[i]);
  224. if( DelayedEntry->KeyControlBlock == NULL ) {
  225. //
  226. // this is a free entry
  227. //
  228. continue;
  229. }
  230. KeyControlBlock = DelayedEntry->KeyControlBlock;
  231. ASSERT( (LONG)KeyControlBlock->DelayedCloseIndex == i );
  232. ASSERT( KeyControlBlock->RefCount == 0 );
  233. //
  234. // this will take care of other stuff kcb is pointing on.
  235. //
  236. CmpCleanUpKcbCacheWithLock(KeyControlBlock);
  237. }
  238. //
  239. // Spew open handles and associated processes
  240. //
  241. Count = 0;
  242. MessageDisplayed = FALSE;
  243. for (i=0; i<(LONG)CmpHashTableSize; i++) {
  244. Current = CmpCacheTable[i];
  245. while (Current) {
  246. KeyControlBlock = CONTAINING_RECORD(Current, CM_KEY_CONTROL_BLOCK, KeyHash);
  247. if( MessageDisplayed == FALSE ){
  248. MessageDisplayed = TRUE;
  249. DbgPrintEx(DPFLTR_CONFIG_ID,DPFLTR_ERROR_LEVEL,"\nDumping open handles : \n\n");
  250. }
  251. CmpDumpKeyBodyList(KeyControlBlock,&Count,NULL);
  252. Current = Current->NextHash;
  253. }
  254. }
  255. if( Count != 0 ) {
  256. //
  257. // there some open handles; bugcheck
  258. //
  259. CM_BUGCHECK( REGISTRY_ERROR,HANDLES_STILL_OPEN_AT_SHUTDOWN,1,Count,0);
  260. }
  261. //
  262. // in case of private alloc, free pages
  263. //
  264. CmpDestroyCmPrivateAlloc();
  265. //
  266. // For the 3 tables below, the objects actually pointed from inside
  267. // should be cleaned up (freed) at the last handle closure time
  268. // the related handles are closed
  269. //
  270. // KCB cache table
  271. ASSERT( CmpCacheTable != NULL );
  272. ExFreePool(CmpCacheTable);
  273. // NameCacheTable
  274. ASSERT( CmpNameCacheTable != NULL );
  275. ExFreePool( CmpNameCacheTable );
  276. // DelayedCloseTable
  277. ASSERT( CmpDelayedCloseTable != NULL );
  278. ExFreePool( CmpDelayedCloseTable );
  279. }
  280. #ifdef CMP_STATS
  281. VOID CmpKcbStatDpcRoutine(IN PKDPC Dpc,IN PVOID DeferredContext,IN PVOID SystemArgument1,IN PVOID SystemArgument2);
  282. #endif
  283. #ifdef CM_SAVE_KCB_CACHE
  284. #define CACHE_DMP_FILE_NAME L"Cache.dmp"
  285. VOID
  286. CmpSaveKcbCache(
  287. VOID
  288. )
  289. /*++
  290. Routine Description:
  291. Saves the content of the kcb cache to \system32\config\cache.dmp
  292. Format of the file:
  293. [ULONG] NumberOfKeys
  294. [ULONG] Length
  295. [WCHAR*Length] Path
  296. [ULONG] Length
  297. [WCHAR*Length] Path
  298. [ULONG] Length
  299. [WCHAR*Length] Path
  300. [ULONG] Length
  301. [WCHAR*Length] Path
  302. [.................]
  303. Arguments:
  304. NONE
  305. Return Value:
  306. NONE
  307. --*/
  308. {
  309. UCHAR FileBuffer[MAX_NAME];
  310. UNICODE_STRING FileName;
  311. UNICODE_STRING TempName;
  312. HANDLE FileHandle;
  313. NTSTATUS Status;
  314. OBJECT_ATTRIBUTES ObjectAttributes;
  315. IO_STATUS_BLOCK IoStatus;
  316. ULONG KcbNo = 0;
  317. LARGE_INTEGER Offset;
  318. ULONG FileOffset;
  319. ULONG i;
  320. PCM_KEY_CONTROL_BLOCK KeyControlBlock;
  321. PCM_KEY_HASH Current;
  322. PUNICODE_STRING Name;
  323. ULONG Tmp;
  324. PCM_DELAYED_CLOSE_ENTRY DelayedEntry;
  325. PAGED_CODE();
  326. //
  327. // first, open the file.
  328. //
  329. FileName.MaximumLength = MAX_NAME;
  330. FileName.Length = 0;
  331. FileName.Buffer = (PWSTR)&(FileBuffer[0]);
  332. RtlInitUnicodeString(
  333. &TempName,
  334. INIT_SYSTEMROOT_HIVEPATH
  335. );
  336. RtlAppendStringToString((PSTRING)&FileName, (PSTRING)&TempName);
  337. RtlInitUnicodeString(
  338. &TempName,
  339. CACHE_DMP_FILE_NAME
  340. );
  341. RtlAppendStringToString((PSTRING)&FileName, (PSTRING)&TempName);
  342. InitializeObjectAttributes(
  343. &ObjectAttributes,
  344. &FileName,
  345. OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
  346. NULL,
  347. NULL
  348. );
  349. ASSERT_PASSIVE_LEVEL();
  350. Status = ZwCreateFile(
  351. &FileHandle,
  352. FILE_READ_DATA | FILE_WRITE_DATA,
  353. &ObjectAttributes,
  354. &IoStatus,
  355. NULL, // alloc size = none
  356. FILE_ATTRIBUTE_NORMAL,
  357. 0, // share nothing
  358. FILE_OPEN_IF,
  359. FILE_RANDOM_ACCESS,
  360. NULL, // eabuffer
  361. 0 // ealength
  362. );
  363. if( !NT_SUCCESS(Status) ) {
  364. // bad luck
  365. return;
  366. }
  367. //
  368. // write the number of kcbs (we'll rewrite it at the end).
  369. //
  370. Offset.LowPart = FileOffset = 0;
  371. Offset.HighPart = 0L;
  372. Status = ZwWriteFile(FileHandle,
  373. NULL,
  374. NULL,
  375. NULL,
  376. &IoStatus,
  377. &KcbNo,
  378. sizeof(ULONG),
  379. &Offset,
  380. NULL);
  381. if( !NT_SUCCESS(Status) ) {
  382. goto Exit;
  383. }
  384. FileOffset = Offset.LowPart + sizeof(ULONG);
  385. //
  386. // iterate through the cache and dump all kcbs
  387. //
  388. for (i=0; i<CmpHashTableSize; i++) {
  389. Current = CmpCacheTable[i];
  390. while (Current) {
  391. KeyControlBlock = CONTAINING_RECORD(Current, CM_KEY_CONTROL_BLOCK, KeyHash);
  392. Name = CmpConstructName(KeyControlBlock);
  393. if( Name ){
  394. Tmp = (ULONG)Name->Length;
  395. //
  396. // write off the length
  397. //
  398. Offset.LowPart = FileOffset;
  399. Status = ZwWriteFile(FileHandle,
  400. NULL,
  401. NULL,
  402. NULL,
  403. &IoStatus,
  404. &Tmp,
  405. sizeof(ULONG),
  406. &Offset,
  407. NULL);
  408. if( !NT_SUCCESS(Status) ) {
  409. goto Exit;
  410. }
  411. FileOffset = Offset.LowPart + sizeof(ULONG);
  412. //
  413. // and the buffer
  414. //
  415. Offset.LowPart = FileOffset;
  416. Status = ZwWriteFile(FileHandle,
  417. NULL,
  418. NULL,
  419. NULL,
  420. &IoStatus,
  421. Name->Buffer,
  422. Tmp,
  423. &Offset,
  424. NULL);
  425. if( !NT_SUCCESS(Status) ) {
  426. goto Exit;
  427. }
  428. FileOffset = Offset.LowPart + Tmp;
  429. //
  430. // record a new kcb and free the name
  431. //
  432. KcbNo++;
  433. ExFreePoolWithTag(Name, CM_NAME_TAG | PROTECTED_POOL);
  434. }
  435. Current = Current->NextHash;
  436. }
  437. }
  438. //
  439. // then, take care of all delayed closed KCBs
  440. //
  441. for (i=0; i<CmpDelayedCloseSize; i++) {
  442. DelayedEntry = &(CmpDelayedCloseTable[i]);
  443. if( DelayedEntry->KeyControlBlock == NULL ) {
  444. //
  445. // this is a free entry
  446. //
  447. continue;
  448. }
  449. KeyControlBlock = DelayedEntry->KeyControlBlock;
  450. ASSERT( KeyControlBlock->DelayedCloseIndex == i );
  451. ASSERT( KeyControlBlock->RefCount == 0 );
  452. Name = CmpConstructName(KeyControlBlock);
  453. if( Name ){
  454. Tmp = (ULONG)Name->Length;
  455. //
  456. // write off the length
  457. //
  458. Offset.LowPart = FileOffset;
  459. Status = ZwWriteFile(FileHandle,
  460. NULL,
  461. NULL,
  462. NULL,
  463. &IoStatus,
  464. &Tmp,
  465. sizeof(ULONG),
  466. &Offset,
  467. NULL);
  468. if( !NT_SUCCESS(Status) ) {
  469. goto Exit;
  470. }
  471. FileOffset = Offset.LowPart + sizeof(ULONG);
  472. //
  473. // and the buffer
  474. //
  475. Offset.LowPart = FileOffset;
  476. Status = ZwWriteFile(FileHandle,
  477. NULL,
  478. NULL,
  479. NULL,
  480. &IoStatus,
  481. Name->Buffer,
  482. Tmp,
  483. &Offset,
  484. NULL);
  485. if( !NT_SUCCESS(Status) ) {
  486. goto Exit;
  487. }
  488. FileOffset = Offset.LowPart + Tmp;
  489. //
  490. // record a new kcb and free the name
  491. //
  492. KcbNo++;
  493. ExFreePoolWithTag(Name, CM_NAME_TAG | PROTECTED_POOL);
  494. }
  495. }
  496. //
  497. // write the number of kcbs
  498. //
  499. Offset.LowPart = 0;
  500. Status = ZwWriteFile(FileHandle,
  501. NULL,
  502. NULL,
  503. NULL,
  504. &IoStatus,
  505. &KcbNo,
  506. sizeof(ULONG),
  507. &Offset,
  508. NULL);
  509. if( !NT_SUCCESS(Status) ) {
  510. goto Exit;
  511. }
  512. ZwFlushBuffersFile(
  513. FileHandle,
  514. &IoStatus
  515. );
  516. Exit:
  517. CmCloseHandle(FileHandle);
  518. }
  519. #endif //CM_SAVE_KCB_CACHE
  520. VOID
  521. CmShutdownSystem(
  522. VOID
  523. )
  524. /*++
  525. Routine Description:
  526. Shuts down the registry.
  527. Arguments:
  528. NONE
  529. Return Value:
  530. NONE
  531. --*/
  532. {
  533. PLIST_ENTRY p;
  534. PCMHIVE CmHive;
  535. NTSTATUS Status;
  536. PVOID RegistryRoot;
  537. PAGED_CODE();
  538. if (CmpRegistryRootHandle) {
  539. Status = ObReferenceObjectByHandle(CmpRegistryRootHandle,
  540. KEY_READ,
  541. NULL,
  542. KernelMode,
  543. &RegistryRoot,
  544. NULL);
  545. if (NT_SUCCESS(Status)) {
  546. // We want to dereference the object twice -- once for the
  547. // reference we just made, and once for the reference
  548. // fromCmpCreateRegistryRoot.
  549. ObDereferenceObject(RegistryRoot);
  550. ObDereferenceObject(RegistryRoot);
  551. }
  552. ObCloseHandle(CmpRegistryRootHandle, KernelMode);
  553. }
  554. CmpLockRegistryExclusive();
  555. //
  556. // Stop the workers; only if registry has been inited
  557. //
  558. if( CmFirstTime == FALSE ) {
  559. CmpShutdownWorkers();
  560. }
  561. //
  562. // shut down the registry
  563. //
  564. CmpDoFlushAll(TRUE);
  565. //
  566. // try to compress the system hive
  567. //
  568. CmCompressKey( &(CmpMachineHiveList[SYSTEM_HIVE_INDEX].CmHive->Hive) );
  569. #ifdef CM_SAVE_KCB_CACHE
  570. //
  571. // dump the cache for perf warm-up at next boot
  572. //
  573. CmpSaveKcbCache();
  574. #endif //CM_SAVE_KCB_CACHE
  575. //
  576. // close all the hive files
  577. //
  578. p = CmpHiveListHead.Flink;
  579. while(p != &CmpHiveListHead) {
  580. CmHive = CONTAINING_RECORD(p, CMHIVE, HiveList);
  581. //
  582. // we need to unmap all views mapped for this hive first
  583. //
  584. CmpDestroyHiveViewList(CmHive);
  585. CmpUnJoinClassOfTrust(CmHive);
  586. //
  587. // dereference the fileobject (if any).
  588. //
  589. CmpDropFileObjectForHive(CmHive);
  590. //
  591. // now we can safely close all the handles
  592. //
  593. CmpCmdHiveClose(CmHive);
  594. p=p->Flink;
  595. }
  596. #ifdef CMP_STATS
  597. // last chance to dump statistics
  598. if( CmFirstTime == FALSE ) {
  599. CmpKcbStatDpcRoutine(NULL,NULL,NULL,NULL);
  600. }
  601. #endif
  602. HvShutdownComplete = TRUE; // Tell HvSyncHive to ignore all
  603. // further requests
  604. if((PoCleanShutdownEnabled() & PO_CLEAN_SHUTDOWN_REGISTRY) && (CmFirstTime == FALSE)){
  605. //
  606. // Free aux memory used internally by CM
  607. //
  608. CmpFreeAllMemory();
  609. }
  610. CmpUnlockRegistry();
  611. return;
  612. }