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.

2316 lines
60 KiB

  1. /*++
  2. Copyright (c) 1989 Microsoft Corporation
  3. Module Name:
  4. specpool.c
  5. Abstract:
  6. This module contains the routines which allocate and deallocate
  7. pages from special pool.
  8. Author:
  9. Lou Perazzoli (loup) 6-Apr-1989
  10. Landy Wang (landyw) 02-June-1997
  11. Revision History:
  12. --*/
  13. #include "mi.h"
  14. LOGICAL
  15. MmSetSpecialPool (
  16. IN LOGICAL Enable
  17. );
  18. PVOID
  19. MiAllocateSpecialPool (
  20. IN SIZE_T NumberOfBytes,
  21. IN ULONG Tag,
  22. IN POOL_TYPE PoolType,
  23. IN ULONG SpecialPoolType
  24. );
  25. VOID
  26. MmFreeSpecialPool (
  27. IN PVOID P
  28. );
  29. LOGICAL
  30. MiProtectSpecialPool (
  31. IN PVOID VirtualAddress,
  32. IN ULONG NewProtect
  33. );
  34. LOGICAL
  35. MiExpandSpecialPool (
  36. IN POOL_TYPE PoolType,
  37. IN KIRQL OldIrql
  38. );
  39. #ifdef ALLOC_PRAGMA
  40. #if defined (_WIN64)
  41. #pragma alloc_text(PAGESPEC, MiDeleteSessionSpecialPool)
  42. #pragma alloc_text(PAGE, MiInitializeSpecialPool)
  43. #else
  44. #pragma alloc_text(INIT, MiInitializeSpecialPool)
  45. #endif
  46. #pragma alloc_text(PAGESPEC, MiExpandSpecialPool)
  47. #pragma alloc_text(PAGESPEC, MmFreeSpecialPool)
  48. #pragma alloc_text(PAGESPEC, MiAllocateSpecialPool)
  49. #pragma alloc_text(PAGESPEC, MiProtectSpecialPool)
  50. #endif
  51. ULONG MmSpecialPoolTag;
  52. PVOID MmSpecialPoolStart;
  53. PVOID MmSpecialPoolEnd;
  54. #if defined (_WIN64)
  55. PVOID MmSessionSpecialPoolStart;
  56. PVOID MmSessionSpecialPoolEnd;
  57. #else
  58. PMMPTE MiSpecialPoolExtra;
  59. ULONG MiSpecialPoolExtraCount;
  60. #endif
  61. ULONG MmSpecialPoolRejected[7];
  62. LOGICAL MmSpecialPoolCatchOverruns = TRUE;
  63. PMMPTE MiSpecialPoolFirstPte;
  64. PMMPTE MiSpecialPoolLastPte;
  65. LONG MiSpecialPagesNonPaged;
  66. LONG MiSpecialPagesPagable;
  67. LONG MmSpecialPagesInUse; // Used by the debugger
  68. ULONG MiSpecialPagesNonPagedPeak;
  69. ULONG MiSpecialPagesPagablePeak;
  70. ULONG MiSpecialPagesInUsePeak;
  71. ULONG MiSpecialPagesNonPagedMaximum;
  72. extern LOGICAL MmPagedPoolMaximumDesired;
  73. extern ULONG MmPteFailures[MaximumPtePoolTypes];
  74. #if defined (_X86_)
  75. extern ULONG MiExtraPtes1;
  76. KSPIN_LOCK MiSpecialPoolLock;
  77. #endif
  78. #if !defined (_WIN64)
  79. LOGICAL
  80. MiInitializeSpecialPool (
  81. IN POOL_TYPE PoolType
  82. )
  83. /*++
  84. Routine Description:
  85. This routine initializes the special pool used to catch pool corruptors.
  86. Arguments:
  87. None.
  88. Return Value:
  89. None.
  90. Environment:
  91. Kernel mode, no locks held.
  92. --*/
  93. {
  94. ULONG i;
  95. PMMPTE PointerPte;
  96. PMMPTE PointerPteBase;
  97. ULONG SpecialPoolPtes;
  98. UNREFERENCED_PARAMETER (PoolType);
  99. if ((MmVerifyDriverBufferLength == (ULONG)-1) &&
  100. ((MmSpecialPoolTag == 0) || (MmSpecialPoolTag == (ULONG)-1))) {
  101. return FALSE;
  102. }
  103. //
  104. // Even though we asked for some number of system PTEs to map special pool,
  105. // we may not have been given them all. Large memory systems are
  106. // autoconfigured so that a large nonpaged pool is the default.
  107. // x86 systems booted with the 3GB switch don't have enough
  108. // contiguous virtual address space to support this, so our request may
  109. // have been trimmed. Handle that intelligently here so we don't exhaust
  110. // the system PTE pool and fail to handle thread stacks and I/O.
  111. //
  112. if (MmNumberOfSystemPtes < 0x3000) {
  113. SpecialPoolPtes = MmNumberOfSystemPtes / 6;
  114. }
  115. else {
  116. SpecialPoolPtes = MmNumberOfSystemPtes / 3;
  117. }
  118. //
  119. // 32-bit systems are very cramped on virtual address space. Apply
  120. // a cap here to prevent overzealousness.
  121. //
  122. if (SpecialPoolPtes > MM_SPECIAL_POOL_PTES) {
  123. SpecialPoolPtes = MM_SPECIAL_POOL_PTES;
  124. }
  125. SpecialPoolPtes = MI_ROUND_TO_SIZE (SpecialPoolPtes, PTE_PER_PAGE);
  126. #if defined (_X86_)
  127. //
  128. // For x86, we can actually use an additional range of special PTEs to
  129. // map memory with and so we can raise the limit from 25000 to approximately
  130. // 256000.
  131. //
  132. if ((MiExtraPtes1 != 0) &&
  133. (ExpMultiUserTS == FALSE) &&
  134. (MiRequestedSystemPtes != (ULONG)-1)) {
  135. if (MmPagedPoolMaximumDesired == TRUE) {
  136. //
  137. // The low PTEs between 2 and 3GB virtual must be used
  138. // for both regular system PTE usage and special pool usage.
  139. //
  140. SpecialPoolPtes = (MiExtraPtes1 / 2);
  141. }
  142. else {
  143. //
  144. // The low PTEs between 2 and 3GB virtual can be used
  145. // exclusively for special pool.
  146. //
  147. SpecialPoolPtes = MiExtraPtes1;
  148. }
  149. }
  150. KeInitializeSpinLock (&MiSpecialPoolLock);
  151. #endif
  152. //
  153. // A PTE disappears for double mapping the system page directory.
  154. // When guard paging for system PTEs is enabled, a few more go also.
  155. // Thus, not being able to get all the PTEs we wanted is not fatal and
  156. // we just back off a bit and retry.
  157. //
  158. //
  159. // Always request an even number of PTEs so each one can be guard paged.
  160. //
  161. ASSERT ((SpecialPoolPtes & (PTE_PER_PAGE - 1)) == 0);
  162. do {
  163. PointerPte = MiReserveAlignedSystemPtes (SpecialPoolPtes,
  164. SystemPteSpace,
  165. MM_VA_MAPPED_BY_PDE);
  166. if (PointerPte != NULL) {
  167. break;
  168. }
  169. ASSERT (SpecialPoolPtes >= PTE_PER_PAGE);
  170. SpecialPoolPtes -= PTE_PER_PAGE;
  171. } while (SpecialPoolPtes != 0);
  172. //
  173. // We deliberately try to get a huge number of system PTEs. Don't let
  174. // any of these count as a real failure in our debugging counters.
  175. //
  176. MmPteFailures[SystemPteSpace] = 0;
  177. if (SpecialPoolPtes == 0) {
  178. return FALSE;
  179. }
  180. ASSERT (SpecialPoolPtes >= PTE_PER_PAGE);
  181. //
  182. // Build the list of PTE pairs using only the first page table page for
  183. // now. Keep the other PTEs in reserve so they can be returned to the
  184. // PTE pool in case some driver wants a huge amount.
  185. //
  186. PointerPteBase = PointerPte;
  187. MmSpecialPoolStart = MiGetVirtualAddressMappedByPte (PointerPte);
  188. ASSERT (MiIsVirtualAddressOnPdeBoundary (MmSpecialPoolStart));
  189. for (i = 0; i < PTE_PER_PAGE; i += 2) {
  190. PointerPte->u.List.NextEntry = ((PointerPte + 2) - MmSystemPteBase);
  191. PointerPte += 2;
  192. }
  193. MiSpecialPoolExtra = PointerPte;
  194. MiSpecialPoolExtraCount = SpecialPoolPtes - PTE_PER_PAGE;
  195. PointerPte -= 2;
  196. PointerPte->u.List.NextEntry = MM_EMPTY_PTE_LIST;
  197. MmSpecialPoolEnd = MiGetVirtualAddressMappedByPte (PointerPte + 1);
  198. MiSpecialPoolLastPte = PointerPte;
  199. MiSpecialPoolFirstPte = PointerPteBase;
  200. //
  201. // Limit nonpaged special pool based on the memory size.
  202. //
  203. MiSpecialPagesNonPagedMaximum = (ULONG)(MmResidentAvailablePages >> 4);
  204. if (MmNumberOfPhysicalPages > 0x3FFF) {
  205. MiSpecialPagesNonPagedMaximum = (ULONG)(MmResidentAvailablePages >> 3);
  206. }
  207. ExSetPoolFlags (EX_SPECIAL_POOL_ENABLED);
  208. return TRUE;
  209. }
  210. #else
  211. PMMPTE MiSpecialPoolNextPdeForSpecialPoolExpansion;
  212. PMMPTE MiSpecialPoolLastPdeForSpecialPoolExpansion;
  213. LOGICAL
  214. MiInitializeSpecialPool (
  215. IN POOL_TYPE PoolType
  216. )
  217. /*++
  218. Routine Description:
  219. This routine initializes special pool used to catch pool corruptors.
  220. Only NT64 systems have sufficient virtual address space to make use of this.
  221. Arguments:
  222. PoolType - Supplies the pool type (system global or session) being
  223. initialized.
  224. Return Value:
  225. TRUE if the requested special pool was initialized, FALSE if not.
  226. Environment:
  227. Kernel mode, no locks held.
  228. --*/
  229. {
  230. PVOID BaseAddress;
  231. PVOID EndAddress;
  232. KIRQL OldIrql;
  233. MMPTE TempPte;
  234. PMMPTE PointerPte;
  235. PMMPTE PointerPde;
  236. PMMPTE PointerPpe;
  237. PMMPTE EndPpe;
  238. PMMPTE EndPde;
  239. LOGICAL SpecialPoolCreated;
  240. SIZE_T AdditionalCommittedPages;
  241. PFN_NUMBER PageFrameIndex;
  242. PAGED_CODE ();
  243. if (PoolType & SESSION_POOL_MASK) {
  244. ASSERT (MmSessionSpace->SpecialPoolFirstPte == NULL);
  245. if (MmSessionSpecialPoolStart == 0) {
  246. return FALSE;
  247. }
  248. BaseAddress = MmSessionSpecialPoolStart;
  249. ASSERT (((ULONG_PTR)BaseAddress & (MM_VA_MAPPED_BY_PDE - 1)) == 0);
  250. EndAddress = (PVOID)((ULONG_PTR)MmSessionSpecialPoolEnd - 1);
  251. }
  252. else {
  253. if (MmSpecialPoolStart == 0) {
  254. return FALSE;
  255. }
  256. BaseAddress = MmSpecialPoolStart;
  257. ASSERT (((ULONG_PTR)BaseAddress & (MM_VA_MAPPED_BY_PDE - 1)) == 0);
  258. EndAddress = (PVOID)((ULONG_PTR)MmSpecialPoolEnd - 1);
  259. //
  260. // Construct empty page directory parent mappings as needed.
  261. //
  262. PointerPpe = MiGetPpeAddress (BaseAddress);
  263. EndPpe = MiGetPpeAddress (EndAddress);
  264. TempPte = ValidKernelPde;
  265. AdditionalCommittedPages = 0;
  266. LOCK_PFN (OldIrql);
  267. while (PointerPpe <= EndPpe) {
  268. if (PointerPpe->u.Long == 0) {
  269. PageFrameIndex = MiRemoveZeroPage (
  270. MI_GET_PAGE_COLOR_FROM_PTE (PointerPpe));
  271. TempPte.u.Hard.PageFrameNumber = PageFrameIndex;
  272. MI_WRITE_VALID_PTE (PointerPpe, TempPte);
  273. MiInitializePfn (PageFrameIndex, PointerPpe, 1);
  274. AdditionalCommittedPages += 1;
  275. }
  276. PointerPpe += 1;
  277. }
  278. MI_DECREMENT_RESIDENT_AVAILABLE (AdditionalCommittedPages,
  279. MM_RESAVAIL_ALLOCATE_SPECIAL_POOL_EXPANSION);
  280. UNLOCK_PFN (OldIrql);
  281. InterlockedExchangeAddSizeT (&MmTotalCommittedPages,
  282. AdditionalCommittedPages);
  283. }
  284. //
  285. // Build just one page table page for session special pool - the rest
  286. // are built on demand.
  287. //
  288. ASSERT (MiGetPpeAddress(BaseAddress)->u.Hard.Valid == 1);
  289. PointerPte = MiGetPteAddress (BaseAddress);
  290. PointerPde = MiGetPdeAddress (BaseAddress);
  291. EndPde = MiGetPdeAddress (EndAddress);
  292. #if DBG
  293. //
  294. // The special pool address range better be unused.
  295. //
  296. while (PointerPde <= EndPde) {
  297. ASSERT (PointerPde->u.Long == 0);
  298. PointerPde += 1;
  299. }
  300. PointerPde = MiGetPdeAddress (BaseAddress);
  301. #endif
  302. if (PoolType & SESSION_POOL_MASK) {
  303. MmSessionSpace->NextPdeForSpecialPoolExpansion = PointerPde;
  304. MmSessionSpace->LastPdeForSpecialPoolExpansion = EndPde;
  305. }
  306. else {
  307. MiSpecialPoolNextPdeForSpecialPoolExpansion = PointerPde;
  308. MiSpecialPoolLastPdeForSpecialPoolExpansion = EndPde;
  309. //
  310. // Cap nonpaged special pool based on the memory size.
  311. //
  312. MiSpecialPagesNonPagedMaximum = (ULONG)(MmResidentAvailablePages >> 4);
  313. if (MmNumberOfPhysicalPages > 0x3FFF) {
  314. MiSpecialPagesNonPagedMaximum = (ULONG)(MmResidentAvailablePages >> 3);
  315. }
  316. }
  317. LOCK_PFN (OldIrql);
  318. SpecialPoolCreated = MiExpandSpecialPool (PoolType, OldIrql);
  319. UNLOCK_PFN (OldIrql);
  320. return SpecialPoolCreated;
  321. }
  322. VOID
  323. MiDeleteSessionSpecialPool (
  324. VOID
  325. )
  326. /*++
  327. Routine Description:
  328. This routine deletes the session special pool range used to catch
  329. pool corruptors. Only NT64 systems have the extra virtual address
  330. space in the session to make use of this.
  331. Arguments:
  332. None.
  333. Return Value:
  334. None.
  335. Environment:
  336. Kernel mode, no locks held.
  337. --*/
  338. {
  339. PVOID BaseAddress;
  340. PVOID EndAddress;
  341. PMMPTE PointerPte;
  342. PMMPTE PointerPde;
  343. PMMPTE StartPde;
  344. PFN_NUMBER PageFrameIndex;
  345. PFN_NUMBER PageTablePages;
  346. PMMPTE EndPde;
  347. #if DBG
  348. PMMPTE StartPte;
  349. PMMPTE EndPte;
  350. #endif
  351. PAGED_CODE ();
  352. //
  353. // If the initial creation of this session's special pool failed, then
  354. // there's nothing to delete.
  355. //
  356. if (MmSessionSpace->SpecialPoolFirstPte == NULL) {
  357. return;
  358. }
  359. if (MmSessionSpace->SpecialPagesInUse != 0) {
  360. KeBugCheckEx (SESSION_HAS_VALID_SPECIAL_POOL_ON_EXIT,
  361. (ULONG_PTR)MmSessionSpace->SessionId,
  362. MmSessionSpace->SpecialPagesInUse,
  363. 0,
  364. 0);
  365. }
  366. //
  367. // Special pool page table pages are expanded such that all PDEs after the
  368. // first blank one must also be blank.
  369. //
  370. BaseAddress = MmSessionSpecialPoolStart;
  371. EndAddress = (PVOID)((ULONG_PTR)MmSessionSpecialPoolEnd - 1);
  372. ASSERT (((ULONG_PTR)BaseAddress & (MM_VA_MAPPED_BY_PDE - 1)) == 0);
  373. ASSERT (MiGetPpeAddress(BaseAddress)->u.Hard.Valid == 1);
  374. ASSERT (MiGetPdeAddress(BaseAddress)->u.Hard.Valid == 1);
  375. PointerPte = MiGetPteAddress (BaseAddress);
  376. PointerPde = MiGetPdeAddress (BaseAddress);
  377. EndPde = MiGetPdeAddress (EndAddress);
  378. StartPde = PointerPde;
  379. //
  380. // No need to flush the TB below as the entire TB will be flushed
  381. // on return when the rest of the session space is destroyed.
  382. //
  383. while (PointerPde <= EndPde) {
  384. if (PointerPde->u.Long == 0) {
  385. break;
  386. }
  387. #if DBG
  388. PointerPte = MiGetVirtualAddressMappedByPte (PointerPde);
  389. StartPte = PointerPte;
  390. EndPte = PointerPte + PTE_PER_PAGE;
  391. while (PointerPte < EndPte) {
  392. ASSERT ((PointerPte + 1)->u.Long == 0);
  393. PointerPte += 2;
  394. }
  395. #endif
  396. PageFrameIndex = MI_GET_PAGE_FRAME_FROM_PTE (PointerPde);
  397. MiSessionPageTableRelease (PageFrameIndex);
  398. MI_WRITE_INVALID_PTE (PointerPde, ZeroKernelPte);
  399. PointerPde += 1;
  400. }
  401. PageTablePages = PointerPde - StartPde;
  402. #if DBG
  403. //
  404. // The remaining session special pool address range better be unused.
  405. //
  406. while (PointerPde <= EndPde) {
  407. ASSERT (PointerPde->u.Long == 0);
  408. PointerPde += 1;
  409. }
  410. #endif
  411. MiReturnCommitment (PageTablePages);
  412. MM_TRACK_COMMIT (MM_DBG_COMMIT_SESSION_POOL_PAGE_TABLES, 0 - PageTablePages);
  413. MM_BUMP_SESS_COUNTER(MM_DBG_SESSION_PAGEDPOOL_PAGETABLE_ALLOC,
  414. (ULONG)(0 - PageTablePages));
  415. InterlockedExchangeAddSizeT (&MmSessionSpace->NonPagablePages, 0 - PageTablePages);
  416. InterlockedExchangeAddSizeT (&MmSessionSpace->CommittedPages, 0 - PageTablePages);
  417. MmSessionSpace->SpecialPoolFirstPte = NULL;
  418. }
  419. #endif
  420. #if defined (_X86_)
  421. LOGICAL
  422. MiRecoverSpecialPtes (
  423. IN ULONG NumberOfPtes
  424. )
  425. {
  426. KIRQL OldIrql;
  427. PMMPTE PointerPte;
  428. if (MiSpecialPoolExtraCount == 0) {
  429. return FALSE;
  430. }
  431. //
  432. // Round the requested number of PTEs up to a full page table multiple.
  433. //
  434. NumberOfPtes = MI_ROUND_TO_SIZE (NumberOfPtes, PTE_PER_PAGE);
  435. //
  436. // If the caller needs more than we have, then do nothing and return FALSE.
  437. //
  438. ExAcquireSpinLock (&MiSpecialPoolLock, &OldIrql);
  439. if (NumberOfPtes > MiSpecialPoolExtraCount) {
  440. ExReleaseSpinLock (&MiSpecialPoolLock, OldIrql);
  441. return FALSE;
  442. }
  443. //
  444. // Return the tail end of the extra reserve.
  445. //
  446. MiSpecialPoolExtraCount -= NumberOfPtes;
  447. PointerPte = MiSpecialPoolExtra + MiSpecialPoolExtraCount;
  448. ExReleaseSpinLock (&MiSpecialPoolLock, OldIrql);
  449. MiReleaseSplitSystemPtes (PointerPte, NumberOfPtes, SystemPteSpace);
  450. return TRUE;
  451. }
  452. #endif
  453. LOGICAL
  454. MiExpandSpecialPool (
  455. IN POOL_TYPE PoolType,
  456. IN KIRQL OldIrql
  457. )
  458. /*++
  459. Routine Description:
  460. This routine attempts to allocate another page table page for the
  461. requested special pool.
  462. Arguments:
  463. PoolType - Supplies the special pool type being expanded.
  464. OldIrql - Supplies the previous irql the PFN lock was acquired at.
  465. Return Value:
  466. TRUE if expansion occurred, FALSE if not.
  467. Environment:
  468. Kernel mode, PFN lock held. The PFN lock may released and reacquired.
  469. --*/
  470. {
  471. #if defined (_WIN64)
  472. PMMPTE PointerPte;
  473. PMMPTE PointerPde;
  474. PFN_NUMBER PageFrameIndex;
  475. NTSTATUS Status;
  476. PMMPTE SpecialPoolFirstPte;
  477. PMMPTE SpecialPoolLastPte;
  478. PMMPTE *NextPde;
  479. PMMPTE *LastPde;
  480. PMMPTE PteBase;
  481. PFN_NUMBER ContainingFrame;
  482. LOGICAL SessionAllocation;
  483. PMMPTE *SpecialPoolFirstPteGlobal;
  484. PMMPTE *SpecialPoolLastPteGlobal;
  485. if (PoolType & SESSION_POOL_MASK) {
  486. NextPde = &MmSessionSpace->NextPdeForSpecialPoolExpansion;
  487. LastPde = &MmSessionSpace->LastPdeForSpecialPoolExpansion;
  488. PteBase = MI_PTE_BASE_FOR_LOWEST_SESSION_ADDRESS;
  489. ContainingFrame = MmSessionSpace->SessionPageDirectoryIndex;
  490. SessionAllocation = TRUE;
  491. SpecialPoolFirstPteGlobal = &MmSessionSpace->SpecialPoolFirstPte;
  492. SpecialPoolLastPteGlobal = &MmSessionSpace->SpecialPoolLastPte;
  493. }
  494. else {
  495. NextPde = &MiSpecialPoolNextPdeForSpecialPoolExpansion;
  496. LastPde = &MiSpecialPoolLastPdeForSpecialPoolExpansion;
  497. PteBase = MmSystemPteBase;
  498. ContainingFrame = 0;
  499. SessionAllocation = FALSE;
  500. SpecialPoolFirstPteGlobal = &MiSpecialPoolFirstPte;
  501. SpecialPoolLastPteGlobal = &MiSpecialPoolLastPte;
  502. }
  503. PointerPde = *NextPde;
  504. if (PointerPde > *LastPde) {
  505. return FALSE;
  506. }
  507. UNLOCK_PFN2 (OldIrql);
  508. //
  509. // Acquire a page and initialize it. If no one else has done this in
  510. // the interim, then insert it into the list.
  511. //
  512. // Note that CantExpand commitment charging must be used because this
  513. // path can get called in the idle thread context while processing DPCs
  514. // and the normal commitment charging may queue a pagefile extension using
  515. // an event on the local stack which is illegal.
  516. //
  517. if (MiChargeCommitmentCantExpand (1, FALSE) == FALSE) {
  518. if (PoolType & SESSION_POOL_MASK) {
  519. MM_BUMP_SESSION_FAILURES (MM_SESSION_FAILURE_NO_COMMIT);
  520. }
  521. LOCK_PFN2 (OldIrql);
  522. return FALSE;
  523. }
  524. if ((PoolType & SESSION_POOL_MASK) == 0) {
  525. ContainingFrame = MI_GET_PAGE_FRAME_FROM_PTE (MiGetPteAddress(PointerPde));
  526. }
  527. Status = MiInitializeAndChargePfn (&PageFrameIndex,
  528. PointerPde,
  529. ContainingFrame,
  530. SessionAllocation);
  531. if (!NT_SUCCESS(Status)) {
  532. MiReturnCommitment (1);
  533. LOCK_PFN2 (OldIrql);
  534. //
  535. // Don't retry even if STATUS_RETRY is returned above because if we
  536. // preempted the thread that allocated the PDE before he gets a
  537. // chance to update the PTE chain, we can loop forever.
  538. //
  539. return FALSE;
  540. }
  541. PointerPte = MiGetVirtualAddressMappedByPte (PointerPde);
  542. KeZeroPages (PointerPte, PAGE_SIZE);
  543. if (PoolType & SESSION_POOL_MASK) {
  544. MM_TRACK_COMMIT (MM_DBG_COMMIT_SESSION_POOL_PAGE_TABLES, 1);
  545. MM_BUMP_SESS_COUNTER(MM_DBG_SESSION_PAGEDPOOL_PAGETABLE_ALLOC, 1);
  546. MM_BUMP_SESS_COUNTER (MM_DBG_SESSION_NP_POOL_CREATE, 1);
  547. InterlockedExchangeAddSizeT (&MmSessionSpace->NonPagablePages, 1);
  548. InterlockedExchangeAddSizeT (&MmSessionSpace->CommittedPages, 1);
  549. }
  550. else {
  551. MM_TRACK_COMMIT (MM_DBG_COMMIT_SPECIAL_POOL_MAPPING_PAGES, 1);
  552. }
  553. //
  554. // Build the list of PTE pairs.
  555. //
  556. SpecialPoolFirstPte = PointerPte;
  557. SpecialPoolLastPte = PointerPte + PTE_PER_PAGE;
  558. while (PointerPte < SpecialPoolLastPte) {
  559. PointerPte->u.List.NextEntry = (PointerPte + 2 - PteBase);
  560. (PointerPte + 1)->u.Long = 0;
  561. PointerPte += 2;
  562. }
  563. PointerPte -= 2;
  564. PointerPte->u.List.NextEntry = MM_EMPTY_PTE_LIST;
  565. ASSERT (PointerPde == *NextPde);
  566. ASSERT (PointerPde <= *LastPde);
  567. //
  568. // Insert the new page table page into the head of the current list (if
  569. // one exists) so it gets used first.
  570. //
  571. if (*SpecialPoolFirstPteGlobal == NULL) {
  572. //
  573. // This is the initial creation.
  574. //
  575. *SpecialPoolFirstPteGlobal = SpecialPoolFirstPte;
  576. *SpecialPoolLastPteGlobal = PointerPte;
  577. ExSetPoolFlags (EX_SPECIAL_POOL_ENABLED);
  578. LOCK_PFN2 (OldIrql);
  579. }
  580. else {
  581. //
  582. // This is actually an expansion.
  583. //
  584. LOCK_PFN2 (OldIrql);
  585. PointerPte->u.List.NextEntry = *SpecialPoolFirstPteGlobal - PteBase;
  586. *SpecialPoolFirstPteGlobal = SpecialPoolFirstPte;
  587. }
  588. ASSERT ((*SpecialPoolLastPteGlobal)->u.List.NextEntry == MM_EMPTY_PTE_LIST);
  589. *NextPde = *NextPde + 1;
  590. #else
  591. ULONG i;
  592. PMMPTE PointerPte;
  593. UNREFERENCED_PARAMETER (PoolType);
  594. if (MiSpecialPoolExtraCount == 0) {
  595. return FALSE;
  596. }
  597. ExAcquireSpinLock (&MiSpecialPoolLock, &OldIrql);
  598. if (MiSpecialPoolExtraCount == 0) {
  599. ExReleaseSpinLock (&MiSpecialPoolLock, OldIrql);
  600. return FALSE;
  601. }
  602. ASSERT (MiSpecialPoolExtraCount >= PTE_PER_PAGE);
  603. PointerPte = MiSpecialPoolExtra;
  604. for (i = 0; i < PTE_PER_PAGE - 2; i += 2) {
  605. PointerPte->u.List.NextEntry = ((PointerPte + 2) - MmSystemPteBase);
  606. PointerPte += 2;
  607. }
  608. PointerPte->u.List.NextEntry = MM_EMPTY_PTE_LIST;
  609. MmSpecialPoolEnd = MiGetVirtualAddressMappedByPte (PointerPte + 1);
  610. MiSpecialPoolLastPte = PointerPte;
  611. MiSpecialPoolFirstPte = MiSpecialPoolExtra;
  612. MiSpecialPoolExtraCount -= PTE_PER_PAGE;
  613. MiSpecialPoolExtra += PTE_PER_PAGE;
  614. ExReleaseSpinLock (&MiSpecialPoolLock, OldIrql);
  615. #endif
  616. return TRUE;
  617. }
  618. PVOID
  619. MmAllocateSpecialPool (
  620. IN SIZE_T NumberOfBytes,
  621. IN ULONG Tag,
  622. IN POOL_TYPE PoolType,
  623. IN ULONG SpecialPoolType
  624. )
  625. /*++
  626. Routine Description:
  627. This routine allocates virtual memory from special pool. This allocation
  628. is made from the end of a physical page with the next PTE set to no access
  629. so that any reads or writes will cause an immediate fatal system crash.
  630. This lets us catch components that corrupt pool.
  631. Arguments:
  632. NumberOfBytes - Supplies the number of bytes to commit.
  633. Tag - Supplies the tag of the requested allocation.
  634. PoolType - Supplies the pool type of the requested allocation.
  635. SpecialPoolType - Supplies the special pool type of the
  636. requested allocation.
  637. - 0 indicates overruns.
  638. - 1 indicates underruns.
  639. - 2 indicates use the systemwide pool policy.
  640. Return Value:
  641. A non-NULL pointer if the requested allocation was fulfilled from special
  642. pool. NULL if the allocation was not made.
  643. Environment:
  644. Kernel mode, no pool locks held.
  645. Note this is a nonpagable wrapper so that machines without special pool
  646. can still support drivers allocating nonpaged pool at DISPATCH_LEVEL
  647. requesting special pool.
  648. --*/
  649. {
  650. if (MiSpecialPoolFirstPte == NULL) {
  651. //
  652. // The special pool allocation code was never initialized.
  653. //
  654. return NULL;
  655. }
  656. #if defined (_WIN64)
  657. if (PoolType & SESSION_POOL_MASK) {
  658. if (MmSessionSpace->SpecialPoolFirstPte == NULL) {
  659. //
  660. // The special pool allocation code was never initialized.
  661. //
  662. return NULL;
  663. }
  664. }
  665. #endif
  666. return MiAllocateSpecialPool (NumberOfBytes,
  667. Tag,
  668. PoolType,
  669. SpecialPoolType);
  670. }
  671. PVOID
  672. MiAllocateSpecialPool (
  673. IN SIZE_T NumberOfBytes,
  674. IN ULONG Tag,
  675. IN POOL_TYPE PoolType,
  676. IN ULONG SpecialPoolType
  677. )
  678. /*++
  679. Routine Description:
  680. This routine allocates virtual memory from special pool. This allocation
  681. is made from the end of a physical page with the next PTE set to no access
  682. so that any reads or writes will cause an immediate fatal system crash.
  683. This lets us catch components that corrupt pool.
  684. Arguments:
  685. NumberOfBytes - Supplies the number of bytes to commit.
  686. Tag - Supplies the tag of the requested allocation.
  687. PoolType - Supplies the pool type of the requested allocation.
  688. SpecialPoolType - Supplies the special pool type of the
  689. requested allocation.
  690. - 0 indicates overruns.
  691. - 1 indicates underruns.
  692. - 2 indicates use the systemwide pool policy.
  693. Return Value:
  694. A non-NULL pointer if the requested allocation was fulfilled from special
  695. pool. NULL if the allocation was not made.
  696. Environment:
  697. Kernel mode, no locks (not even pool locks) held.
  698. --*/
  699. {
  700. PMMPFN Pfn1;
  701. PMMPFN Pfn2;
  702. ULONG_PTR NextEntry;
  703. PMMSUPPORT VmSupport;
  704. PETHREAD CurrentThread;
  705. MMPTE TempPte;
  706. PFN_NUMBER PageFrameIndex;
  707. PFN_NUMBER PageTableFrameIndex;
  708. PMMPTE PointerPte;
  709. KIRQL OldIrql;
  710. PVOID Entry;
  711. PPOOL_HEADER Header;
  712. LARGE_INTEGER CurrentTime;
  713. LOGICAL CatchOverruns;
  714. PMMPTE SpecialPoolFirstPte;
  715. ULONG NumberOfSpecialPages;
  716. WSLE_NUMBER WorkingSetIndex;
  717. LOGICAL TossPage;
  718. if ((PoolType & BASE_POOL_TYPE_MASK) == PagedPool) {
  719. if (KeGetCurrentIrql() > APC_LEVEL) {
  720. KeBugCheckEx (SPECIAL_POOL_DETECTED_MEMORY_CORRUPTION,
  721. KeGetCurrentIrql(),
  722. PoolType,
  723. NumberOfBytes,
  724. 0x30);
  725. }
  726. }
  727. else {
  728. if (KeGetCurrentIrql() > DISPATCH_LEVEL) {
  729. KeBugCheckEx (SPECIAL_POOL_DETECTED_MEMORY_CORRUPTION,
  730. KeGetCurrentIrql(),
  731. PoolType,
  732. NumberOfBytes,
  733. 0x30);
  734. }
  735. }
  736. #if !defined (_WIN64) && !defined (_X86PAE_)
  737. if ((MiExtraPtes1 != 0) || (MiUseMaximumSystemSpace != 0)) {
  738. extern const ULONG MMSECT;
  739. //
  740. // Prototype PTEs cannot come from lower special pool because
  741. // their address is encoded into PTEs and the encoding only covers
  742. // a max of 1GB from the start of paged pool. Likewise fork
  743. // prototype PTEs.
  744. //
  745. if (Tag == MMSECT || Tag == 'lCmM') {
  746. return NULL;
  747. }
  748. }
  749. if (Tag == 'bSmM' || Tag == 'iCmM' || Tag == 'aCmM' || Tag == 'dSmM' || Tag == 'cSmM') {
  750. //
  751. // Mm subsections cannot come from this special pool because they
  752. // get encoded into PTEs - they must come from normal nonpaged pool.
  753. //
  754. return NULL;
  755. }
  756. #endif
  757. if (MiChargeCommitmentCantExpand (1, FALSE) == FALSE) {
  758. MmSpecialPoolRejected[5] += 1;
  759. return NULL;
  760. }
  761. TempPte = ValidKernelPte;
  762. MI_SET_PTE_DIRTY (TempPte);
  763. //
  764. // Don't get too aggressive until a paging file gets set up.
  765. //
  766. if (MmNumberOfPagingFiles == 0 && (PFN_COUNT)MmSpecialPagesInUse > MmAvailablePages / 2) {
  767. MmSpecialPoolRejected[3] += 1;
  768. MiReturnCommitment (1);
  769. return NULL;
  770. }
  771. //
  772. // Cap nonpaged allocations to prevent runaways.
  773. //
  774. if (((PoolType & BASE_POOL_TYPE_MASK) == NonPagedPool) &&
  775. ((ULONG)MiSpecialPagesNonPaged > MiSpecialPagesNonPagedMaximum)) {
  776. MmSpecialPoolRejected[1] += 1;
  777. MiReturnCommitment (1);
  778. return NULL;
  779. }
  780. TossPage = FALSE;
  781. LOCK_PFN2 (OldIrql);
  782. restart:
  783. if (MmAvailablePages < MM_TIGHT_LIMIT) {
  784. UNLOCK_PFN2 (OldIrql);
  785. MmSpecialPoolRejected[0] += 1;
  786. MiReturnCommitment (1);
  787. return NULL;
  788. }
  789. SpecialPoolFirstPte = MiSpecialPoolFirstPte;
  790. #if defined (_WIN64)
  791. if (PoolType & SESSION_POOL_MASK) {
  792. SpecialPoolFirstPte = MmSessionSpace->SpecialPoolFirstPte;
  793. }
  794. #endif
  795. if (SpecialPoolFirstPte->u.List.NextEntry == MM_EMPTY_PTE_LIST) {
  796. //
  797. // Add another page table page (virtual address space and resources
  798. // permitting) and then restart the request. The PFN lock may be
  799. // released and reacquired during this call.
  800. //
  801. if (MiExpandSpecialPool (PoolType, OldIrql) == TRUE) {
  802. goto restart;
  803. }
  804. UNLOCK_PFN2 (OldIrql);
  805. MmSpecialPoolRejected[2] += 1;
  806. MiReturnCommitment (1);
  807. return NULL;
  808. }
  809. if ((PoolType & BASE_POOL_TYPE_MASK) == NonPagedPool) {
  810. if (MI_NONPAGABLE_MEMORY_AVAILABLE() < 100) {
  811. UNLOCK_PFN2 (OldIrql);
  812. MmSpecialPoolRejected[4] += 1;
  813. MiReturnCommitment (1);
  814. return NULL;
  815. }
  816. MI_DECREMENT_RESIDENT_AVAILABLE (1,
  817. MM_RESAVAIL_ALLOCATE_NONPAGED_SPECIAL_POOL);
  818. }
  819. MM_TRACK_COMMIT (MM_DBG_COMMIT_SPECIAL_POOL_PAGES, 1);
  820. PointerPte = SpecialPoolFirstPte;
  821. ASSERT (PointerPte->u.List.NextEntry != MM_EMPTY_PTE_LIST);
  822. #if defined (_WIN64)
  823. if (PoolType & SESSION_POOL_MASK) {
  824. MmSessionSpace->SpecialPoolFirstPte = PointerPte->u.List.NextEntry +
  825. MI_PTE_BASE_FOR_LOWEST_SESSION_ADDRESS;
  826. }
  827. else
  828. #endif
  829. {
  830. MiSpecialPoolFirstPte = PointerPte->u.List.NextEntry + MmSystemPteBase;
  831. }
  832. PageFrameIndex = MiRemoveAnyPage (MI_GET_PAGE_COLOR_FROM_PTE (PointerPte));
  833. TempPte.u.Hard.PageFrameNumber = PageFrameIndex;
  834. MI_WRITE_VALID_PTE (PointerPte, TempPte);
  835. MiInitializePfn (PageFrameIndex, PointerPte, 1);
  836. UNLOCK_PFN2 (OldIrql);
  837. NumberOfSpecialPages = InterlockedIncrement (&MmSpecialPagesInUse);
  838. if (NumberOfSpecialPages > MiSpecialPagesInUsePeak) {
  839. MiSpecialPagesInUsePeak = NumberOfSpecialPages;
  840. }
  841. //
  842. // Fill the page with a random pattern.
  843. //
  844. KeQueryTickCount(&CurrentTime);
  845. Entry = MiGetVirtualAddressMappedByPte (PointerPte);
  846. RtlFillMemory (Entry, PAGE_SIZE, (UCHAR) (CurrentTime.LowPart | 0x1));
  847. if (SpecialPoolType == 0) {
  848. CatchOverruns = TRUE;
  849. }
  850. else if (SpecialPoolType == 1) {
  851. CatchOverruns = FALSE;
  852. }
  853. else if (MmSpecialPoolCatchOverruns == TRUE) {
  854. CatchOverruns = TRUE;
  855. }
  856. else {
  857. CatchOverruns = FALSE;
  858. }
  859. if (CatchOverruns == TRUE) {
  860. Header = (PPOOL_HEADER) Entry;
  861. Entry = (PVOID)(((LONG_PTR)(((PCHAR)Entry + (PAGE_SIZE - NumberOfBytes)))) & ~((LONG_PTR)POOL_OVERHEAD - 1));
  862. }
  863. else {
  864. Header = (PPOOL_HEADER) ((PCHAR)Entry + PAGE_SIZE - POOL_OVERHEAD);
  865. }
  866. //
  867. // Zero the header and stash any information needed at release time.
  868. //
  869. RtlZeroMemory (Header, POOL_OVERHEAD);
  870. Header->Ulong1 = (ULONG)NumberOfBytes;
  871. ASSERT (NumberOfBytes <= PAGE_SIZE - POOL_OVERHEAD && PAGE_SIZE <= 32 * 1024);
  872. if ((PoolType & BASE_POOL_TYPE_MASK) == PagedPool) {
  873. CurrentThread = PsGetCurrentThread ();
  874. Pfn1 = MI_PFN_ELEMENT (PageFrameIndex);
  875. #if defined (_WIN64)
  876. if (PoolType & SESSION_POOL_MASK) {
  877. VmSupport = &MmSessionSpace->GlobalVirtualAddress->Vm;
  878. }
  879. else
  880. #endif
  881. {
  882. VmSupport = &MmSystemCacheWs;
  883. }
  884. LOCK_WORKING_SET (VmSupport);
  885. //
  886. // As this page is now allocated, add it to the system working set to
  887. // make it pagable.
  888. //
  889. ASSERT (Pfn1->u1.Event == 0);
  890. WorkingSetIndex = MiAddValidPageToWorkingSet (Entry,
  891. PointerPte,
  892. Pfn1,
  893. 0);
  894. if (WorkingSetIndex == 0) {
  895. //
  896. // No working set index was available, flush the PTE and the page,
  897. // and decrement the count on the containing page.
  898. //
  899. TossPage = TRUE;
  900. }
  901. ASSERT (KeAreAllApcsDisabled () == TRUE);
  902. if (VmSupport->Flags.GrowWsleHash == 1) {
  903. MiGrowWsleHash (VmSupport);
  904. }
  905. UNLOCK_WORKING_SET (VmSupport);
  906. if (TossPage == TRUE) {
  907. //
  908. // Clear the adjacent PTE to support MmIsSpecialPoolAddressFree().
  909. //
  910. MmSpecialPoolRejected[6] += 1;
  911. (PointerPte + 1)->u.Long = 0;
  912. PageTableFrameIndex = Pfn1->u4.PteFrame;
  913. Pfn2 = MI_PFN_ELEMENT (PageTableFrameIndex);
  914. MI_SET_PFN_DELETED (Pfn1);
  915. MI_WRITE_INVALID_PTE (PointerPte, ZeroKernelPte);
  916. KeFlushSingleTb (Entry, TRUE);
  917. PointerPte->u.List.NextEntry = MM_EMPTY_PTE_LIST;
  918. LOCK_PFN2 (OldIrql);
  919. MiDecrementShareCount (Pfn1, PageFrameIndex);
  920. MiDecrementShareCountInline (Pfn2, PageTableFrameIndex);
  921. #if defined (_WIN64)
  922. if (PoolType & SESSION_POOL_MASK) {
  923. NextEntry = PointerPte - MI_PTE_BASE_FOR_LOWEST_SESSION_ADDRESS;
  924. ASSERT (MmSessionSpace->SpecialPoolLastPte->u.List.NextEntry == MM_EMPTY_PTE_LIST);
  925. MmSessionSpace->SpecialPoolLastPte->u.List.NextEntry = NextEntry;
  926. MmSessionSpace->SpecialPoolLastPte = PointerPte;
  927. UNLOCK_PFN2 (OldIrql);
  928. InterlockedDecrement64 ((PLONGLONG) &MmSessionSpace->SpecialPagesInUse);
  929. }
  930. else
  931. #endif
  932. {
  933. NextEntry = PointerPte - MmSystemPteBase;
  934. ASSERT (MiSpecialPoolLastPte->u.List.NextEntry == MM_EMPTY_PTE_LIST);
  935. MiSpecialPoolLastPte->u.List.NextEntry = NextEntry;
  936. MiSpecialPoolLastPte = PointerPte;
  937. UNLOCK_PFN2 (OldIrql);
  938. }
  939. InterlockedDecrement (&MmSpecialPagesInUse);
  940. MiReturnCommitment (1);
  941. MM_TRACK_COMMIT_REDUCTION (MM_DBG_COMMIT_SPECIAL_POOL_PAGES, 1);
  942. return NULL;
  943. }
  944. Header->Ulong1 |= MI_SPECIAL_POOL_PAGABLE;
  945. (PointerPte + 1)->u.Soft.PageFileHigh = MI_SPECIAL_POOL_PTE_PAGABLE;
  946. NumberOfSpecialPages = (ULONG) InterlockedIncrement (&MiSpecialPagesPagable);
  947. if (NumberOfSpecialPages > MiSpecialPagesPagablePeak) {
  948. MiSpecialPagesPagablePeak = NumberOfSpecialPages;
  949. }
  950. }
  951. else {
  952. (PointerPte + 1)->u.Soft.PageFileHigh = MI_SPECIAL_POOL_PTE_NONPAGABLE;
  953. NumberOfSpecialPages = (ULONG) InterlockedIncrement (&MiSpecialPagesNonPaged);
  954. if (NumberOfSpecialPages > MiSpecialPagesNonPagedPeak) {
  955. MiSpecialPagesNonPagedPeak = NumberOfSpecialPages;
  956. }
  957. }
  958. #if defined (_WIN64)
  959. if (PoolType & SESSION_POOL_MASK) {
  960. Header->Ulong1 |= MI_SPECIAL_POOL_IN_SESSION;
  961. InterlockedIncrement64 ((PLONGLONG) &MmSessionSpace->SpecialPagesInUse);
  962. }
  963. #endif
  964. if (PoolType & POOL_VERIFIER_MASK) {
  965. Header->Ulong1 |= MI_SPECIAL_POOL_VERIFIER;
  966. }
  967. Header->BlockSize = (UCHAR) (CurrentTime.LowPart | 0x1);
  968. Header->PoolTag = Tag;
  969. ASSERT ((Header->PoolType & POOL_QUOTA_MASK) == 0);
  970. return Entry;
  971. }
  972. #define SPECIAL_POOL_FREE_TRACE_LENGTH 16
  973. typedef struct _SPECIAL_POOL_FREE_TRACE {
  974. PVOID StackTrace [SPECIAL_POOL_FREE_TRACE_LENGTH];
  975. } SPECIAL_POOL_FREE_TRACE, *PSPECIAL_POOL_FREE_TRACE;
  976. VOID
  977. MmFreeSpecialPool (
  978. IN PVOID P
  979. )
  980. /*++
  981. Routine Description:
  982. This routine frees a special pool allocation. The backing page is freed
  983. and the mapping virtual address is made no access (the next virtual
  984. address is already no access).
  985. The virtual address PTE pair is then placed into an LRU queue to provide
  986. maximum no-access (protection) life to catch components that access
  987. deallocated pool.
  988. Arguments:
  989. VirtualAddress - Supplies the special pool virtual address to free.
  990. Return Value:
  991. None.
  992. Environment:
  993. Kernel mode, no locks (not even pool locks) held.
  994. --*/
  995. {
  996. ULONG_PTR NextEntry;
  997. MMPTE PteContents;
  998. PFN_NUMBER PageFrameIndex;
  999. PFN_NUMBER PageTableFrameIndex;
  1000. PFN_NUMBER ResidentAvailCharge;
  1001. PMMPTE PointerPte;
  1002. PMMPFN Pfn1;
  1003. PMMPFN Pfn2;
  1004. KIRQL OldIrql;
  1005. ULONG SlopBytes;
  1006. ULONG NumberOfBytesCalculated;
  1007. ULONG NumberOfBytesRequested;
  1008. POOL_TYPE PoolType;
  1009. MMPTE LocalNoAccessPte;
  1010. PPOOL_HEADER Header;
  1011. PUCHAR Slop;
  1012. ULONG i;
  1013. LOGICAL BufferAtPageEnd;
  1014. PMI_FREED_SPECIAL_POOL AllocationBase;
  1015. LARGE_INTEGER CurrentTime;
  1016. #if defined(_X86_) || defined(_AMD64_)
  1017. PULONG_PTR StackPointer;
  1018. #else
  1019. ULONG Hash;
  1020. #endif
  1021. PointerPte = MiGetPteAddress (P);
  1022. PteContents = *PointerPte;
  1023. //
  1024. // Check the PTE now so we can give a more friendly bugcheck rather than
  1025. // crashing below on a bad reference.
  1026. //
  1027. if (PteContents.u.Hard.Valid == 0) {
  1028. if ((PteContents.u.Soft.Protection == 0) ||
  1029. (PteContents.u.Soft.Protection == MM_NOACCESS)) {
  1030. KeBugCheckEx (SPECIAL_POOL_DETECTED_MEMORY_CORRUPTION,
  1031. (ULONG_PTR)P,
  1032. (ULONG_PTR)PteContents.u.Long,
  1033. 0,
  1034. 0x20);
  1035. }
  1036. }
  1037. if (((ULONG_PTR)P & (PAGE_SIZE - 1))) {
  1038. Header = PAGE_ALIGN (P);
  1039. BufferAtPageEnd = TRUE;
  1040. }
  1041. else {
  1042. Header = (PPOOL_HEADER)((PCHAR)PAGE_ALIGN (P) + PAGE_SIZE - POOL_OVERHEAD);
  1043. BufferAtPageEnd = FALSE;
  1044. }
  1045. if (Header->Ulong1 & MI_SPECIAL_POOL_PAGABLE) {
  1046. ASSERT ((PointerPte + 1)->u.Soft.PageFileHigh == MI_SPECIAL_POOL_PTE_PAGABLE);
  1047. if (KeGetCurrentIrql() > APC_LEVEL) {
  1048. KeBugCheckEx (SPECIAL_POOL_DETECTED_MEMORY_CORRUPTION,
  1049. KeGetCurrentIrql(),
  1050. PagedPool,
  1051. (ULONG_PTR)P,
  1052. 0x31);
  1053. }
  1054. PoolType = PagedPool;
  1055. }
  1056. else {
  1057. ASSERT ((PointerPte + 1)->u.Soft.PageFileHigh == MI_SPECIAL_POOL_PTE_NONPAGABLE);
  1058. if (KeGetCurrentIrql() > DISPATCH_LEVEL) {
  1059. KeBugCheckEx (SPECIAL_POOL_DETECTED_MEMORY_CORRUPTION,
  1060. KeGetCurrentIrql(),
  1061. NonPagedPool,
  1062. (ULONG_PTR)P,
  1063. 0x31);
  1064. }
  1065. PoolType = NonPagedPool;
  1066. }
  1067. #if defined (_WIN64)
  1068. if (Header->Ulong1 & MI_SPECIAL_POOL_IN_SESSION) {
  1069. PoolType |= SESSION_POOL_MASK;
  1070. NextEntry = PointerPte - MI_PTE_BASE_FOR_LOWEST_SESSION_ADDRESS;
  1071. }
  1072. else
  1073. #endif
  1074. {
  1075. NextEntry = PointerPte - MmSystemPteBase;
  1076. }
  1077. NumberOfBytesRequested = (ULONG)(USHORT)(Header->Ulong1 & ~(MI_SPECIAL_POOL_PAGABLE | MI_SPECIAL_POOL_VERIFIER | MI_SPECIAL_POOL_IN_SESSION));
  1078. //
  1079. // We gave the caller pool-header aligned data, so account for
  1080. // that when checking here.
  1081. //
  1082. if (BufferAtPageEnd == TRUE) {
  1083. NumberOfBytesCalculated = PAGE_SIZE - BYTE_OFFSET(P);
  1084. if (NumberOfBytesRequested > NumberOfBytesCalculated) {
  1085. //
  1086. // Seems like we didn't give the caller enough - this is an error.
  1087. //
  1088. KeBugCheckEx (SPECIAL_POOL_DETECTED_MEMORY_CORRUPTION,
  1089. (ULONG_PTR)P,
  1090. NumberOfBytesRequested,
  1091. NumberOfBytesCalculated,
  1092. 0x21);
  1093. }
  1094. if (NumberOfBytesRequested + POOL_OVERHEAD < NumberOfBytesCalculated) {
  1095. //
  1096. // Seems like we gave the caller too much - also an error.
  1097. //
  1098. KeBugCheckEx (SPECIAL_POOL_DETECTED_MEMORY_CORRUPTION,
  1099. (ULONG_PTR)P,
  1100. NumberOfBytesRequested,
  1101. NumberOfBytesCalculated,
  1102. 0x22);
  1103. }
  1104. //
  1105. // Check the memory before the start of the caller's allocation.
  1106. //
  1107. Slop = (PUCHAR)(Header + 1);
  1108. if (Header->Ulong1 & MI_SPECIAL_POOL_VERIFIER) {
  1109. Slop += sizeof(MI_VERIFIER_POOL_HEADER);
  1110. }
  1111. for ( ; Slop < (PUCHAR)P; Slop += 1) {
  1112. if (*Slop != Header->BlockSize) {
  1113. KeBugCheckEx (SPECIAL_POOL_DETECTED_MEMORY_CORRUPTION,
  1114. (ULONG_PTR)P,
  1115. (ULONG_PTR)Slop,
  1116. Header->Ulong1,
  1117. 0x23);
  1118. }
  1119. }
  1120. }
  1121. else {
  1122. NumberOfBytesCalculated = 0;
  1123. }
  1124. //
  1125. // Check the memory after the end of the caller's allocation.
  1126. //
  1127. Slop = (PUCHAR)P + NumberOfBytesRequested;
  1128. SlopBytes = (ULONG)((PUCHAR)(PAGE_ALIGN(P)) + PAGE_SIZE - Slop);
  1129. if (BufferAtPageEnd == FALSE) {
  1130. SlopBytes -= POOL_OVERHEAD;
  1131. if (Header->Ulong1 & MI_SPECIAL_POOL_VERIFIER) {
  1132. SlopBytes -= sizeof(MI_VERIFIER_POOL_HEADER);
  1133. }
  1134. }
  1135. for (i = 0; i < SlopBytes; i += 1) {
  1136. if (*Slop != Header->BlockSize) {
  1137. //
  1138. // The caller wrote slop between the free alignment we gave and the
  1139. // end of the page (this is not detectable from page protection).
  1140. //
  1141. KeBugCheckEx (SPECIAL_POOL_DETECTED_MEMORY_CORRUPTION,
  1142. (ULONG_PTR)P,
  1143. (ULONG_PTR)Slop,
  1144. Header->Ulong1,
  1145. 0x24);
  1146. }
  1147. Slop += 1;
  1148. }
  1149. //
  1150. // Note session pool is directly tracked by default already so there is
  1151. // no need to notify the verifier for session special pool allocations.
  1152. //
  1153. if ((Header->Ulong1 & (MI_SPECIAL_POOL_VERIFIER | MI_SPECIAL_POOL_IN_SESSION)) == MI_SPECIAL_POOL_VERIFIER) {
  1154. VerifierFreeTrackedPool (P,
  1155. NumberOfBytesRequested,
  1156. PoolType,
  1157. TRUE);
  1158. }
  1159. AllocationBase = (PMI_FREED_SPECIAL_POOL)(PAGE_ALIGN (P));
  1160. AllocationBase->Signature = MI_FREED_SPECIAL_POOL_SIGNATURE;
  1161. KeQueryTickCount(&CurrentTime);
  1162. AllocationBase->TickCount = CurrentTime.LowPart;
  1163. AllocationBase->NumberOfBytesRequested = NumberOfBytesRequested;
  1164. AllocationBase->Pagable = (ULONG)PoolType;
  1165. AllocationBase->VirtualAddress = P;
  1166. AllocationBase->Thread = PsGetCurrentThread ();
  1167. #if defined(_X86_) || defined(_AMD64_)
  1168. #if defined (_X86_)
  1169. _asm {
  1170. mov StackPointer, esp
  1171. }
  1172. #endif
  1173. #if defined(_AMD64_)
  1174. {
  1175. CONTEXT Context;
  1176. RtlCaptureContext (&Context);
  1177. StackPointer = (PULONG_PTR) Context.Rsp;
  1178. }
  1179. #endif
  1180. AllocationBase->StackPointer = StackPointer;
  1181. //
  1182. // For now, don't get fancy with copying more than what's in the current
  1183. // stack page. To do so would require checking the thread stack limits,
  1184. // DPC stack limits, etc.
  1185. //
  1186. AllocationBase->StackBytes = PAGE_SIZE - BYTE_OFFSET(StackPointer);
  1187. if (AllocationBase->StackBytes != 0) {
  1188. if (AllocationBase->StackBytes > MI_STACK_BYTES) {
  1189. AllocationBase->StackBytes = MI_STACK_BYTES;
  1190. }
  1191. RtlCopyMemory (AllocationBase->StackData,
  1192. StackPointer,
  1193. AllocationBase->StackBytes);
  1194. }
  1195. #else
  1196. AllocationBase->StackPointer = NULL;
  1197. AllocationBase->StackBytes = 0;
  1198. RtlZeroMemory (AllocationBase->StackData, sizeof (SPECIAL_POOL_FREE_TRACE));
  1199. RtlCaptureStackBackTrace (0,
  1200. SPECIAL_POOL_FREE_TRACE_LENGTH,
  1201. (PVOID *)AllocationBase->StackData,
  1202. &Hash);
  1203. #endif
  1204. //
  1205. // Clear the adjacent PTE to support MmIsSpecialPoolAddressFree().
  1206. //
  1207. (PointerPte + 1)->u.Long = 0;
  1208. ResidentAvailCharge = 0;
  1209. if ((PoolType & BASE_POOL_TYPE_MASK) == PagedPool) {
  1210. LocalNoAccessPte.u.Long = MM_KERNEL_NOACCESS_PTE;
  1211. MiDeleteSystemPagableVm (PointerPte,
  1212. 1,
  1213. LocalNoAccessPte,
  1214. (PoolType & SESSION_POOL_MASK) ? TRUE : FALSE,
  1215. NULL);
  1216. PointerPte->u.List.NextEntry = MM_EMPTY_PTE_LIST;
  1217. InterlockedDecrement (&MiSpecialPagesPagable);
  1218. LOCK_PFN (OldIrql);
  1219. }
  1220. else {
  1221. PageFrameIndex = MI_GET_PAGE_FRAME_FROM_PTE (PointerPte);
  1222. Pfn1 = MI_PFN_ELEMENT (PageFrameIndex);
  1223. PageTableFrameIndex = Pfn1->u4.PteFrame;
  1224. Pfn2 = MI_PFN_ELEMENT (PageTableFrameIndex);
  1225. MI_SET_PFN_DELETED (Pfn1);
  1226. InterlockedDecrement (&MiSpecialPagesNonPaged);
  1227. MI_WRITE_INVALID_PTE (PointerPte, ZeroKernelPte);
  1228. KeFlushSingleTb (P, TRUE);
  1229. PointerPte->u.List.NextEntry = MM_EMPTY_PTE_LIST;
  1230. LOCK_PFN2 (OldIrql);
  1231. MiDecrementShareCount (Pfn1, PageFrameIndex);
  1232. MiDecrementShareCountInline (Pfn2, PageTableFrameIndex);
  1233. ResidentAvailCharge = 1;
  1234. }
  1235. #if defined (_WIN64)
  1236. if (PoolType & SESSION_POOL_MASK) {
  1237. ASSERT (MmSessionSpace->SpecialPoolLastPte->u.List.NextEntry == MM_EMPTY_PTE_LIST);
  1238. MmSessionSpace->SpecialPoolLastPte->u.List.NextEntry = NextEntry;
  1239. MmSessionSpace->SpecialPoolLastPte = PointerPte;
  1240. UNLOCK_PFN2 (OldIrql);
  1241. InterlockedDecrement64 ((PLONGLONG) &MmSessionSpace->SpecialPagesInUse);
  1242. }
  1243. else
  1244. #endif
  1245. {
  1246. ASSERT (MiSpecialPoolLastPte->u.List.NextEntry == MM_EMPTY_PTE_LIST);
  1247. MiSpecialPoolLastPte->u.List.NextEntry = NextEntry;
  1248. MiSpecialPoolLastPte = PointerPte;
  1249. UNLOCK_PFN2 (OldIrql);
  1250. }
  1251. if (ResidentAvailCharge != 0) {
  1252. MI_INCREMENT_RESIDENT_AVAILABLE (1,
  1253. MM_RESAVAIL_FREE_NONPAGED_SPECIAL_POOL);
  1254. }
  1255. InterlockedDecrement (&MmSpecialPagesInUse);
  1256. MiReturnCommitment (1);
  1257. MM_TRACK_COMMIT_REDUCTION (MM_DBG_COMMIT_SPECIAL_POOL_PAGES, 1);
  1258. return;
  1259. }
  1260. SIZE_T
  1261. MmQuerySpecialPoolBlockSize (
  1262. IN PVOID P
  1263. )
  1264. /*++
  1265. Routine Description:
  1266. This routine returns the size of a special pool allocation.
  1267. Arguments:
  1268. VirtualAddress - Supplies the special pool virtual address to query.
  1269. Return Value:
  1270. The size in bytes of the allocation.
  1271. Environment:
  1272. Kernel mode, APC_LEVEL or below for pagable addresses, DISPATCH_LEVEL or
  1273. below for nonpaged addresses.
  1274. --*/
  1275. {
  1276. PPOOL_HEADER Header;
  1277. #if defined (_WIN64)
  1278. ASSERT (((P >= MmSessionSpecialPoolStart) && (P < MmSessionSpecialPoolEnd)) ||
  1279. ((P >= MmSpecialPoolStart) && (P < MmSpecialPoolEnd)));
  1280. #else
  1281. ASSERT ((P >= MmSpecialPoolStart) && (P < MmSpecialPoolEnd));
  1282. #endif
  1283. if (((ULONG_PTR)P & (PAGE_SIZE - 1))) {
  1284. Header = PAGE_ALIGN (P);
  1285. }
  1286. else {
  1287. Header = (PPOOL_HEADER)((PCHAR)PAGE_ALIGN (P) + PAGE_SIZE - POOL_OVERHEAD);
  1288. }
  1289. return (SIZE_T) (Header->Ulong1 & (PAGE_SIZE - 1));
  1290. }
  1291. LOGICAL
  1292. MmIsSpecialPoolAddress (
  1293. IN PVOID VirtualAddress
  1294. )
  1295. /*++
  1296. Routine Description:
  1297. This function returns TRUE if the argument address is in special pool.
  1298. FALSE if not.
  1299. Arguments:
  1300. VirtualAddress - Supplies the address in question.
  1301. Return Value:
  1302. See above.
  1303. Environment:
  1304. Kernel mode.
  1305. --*/
  1306. {
  1307. if ((VirtualAddress >= MmSpecialPoolStart) &&
  1308. (VirtualAddress < MmSpecialPoolEnd)) {
  1309. return TRUE;
  1310. }
  1311. #if defined (_WIN64)
  1312. if ((VirtualAddress >= MmSessionSpecialPoolStart) &&
  1313. (VirtualAddress < MmSessionSpecialPoolEnd)) {
  1314. return TRUE;
  1315. }
  1316. #endif
  1317. return FALSE;
  1318. }
  1319. LOGICAL
  1320. MmIsSpecialPoolAddressFree (
  1321. IN PVOID VirtualAddress
  1322. )
  1323. /*++
  1324. Routine Description:
  1325. This function returns TRUE if a special pool address has been freed.
  1326. FALSE is returned if it is inuse (ie: the caller overran).
  1327. Arguments:
  1328. VirtualAddress - Supplies the special pool address in question.
  1329. Return Value:
  1330. See above.
  1331. Environment:
  1332. Kernel mode.
  1333. --*/
  1334. {
  1335. PMMPTE PointerPte;
  1336. //
  1337. // Caller must check that the address in in special pool.
  1338. //
  1339. ASSERT (MmIsSpecialPoolAddress (VirtualAddress) == TRUE);
  1340. PointerPte = MiGetPteAddress (VirtualAddress);
  1341. //
  1342. // Take advantage of the fact that adjacent PTEs have the paged/nonpaged
  1343. // bits set when in use and these bits are cleared on free. Note also
  1344. // that freed pages get their PTEs chained together through PageFileHigh.
  1345. //
  1346. if ((PointerPte->u.Soft.PageFileHigh == MI_SPECIAL_POOL_PTE_PAGABLE) ||
  1347. (PointerPte->u.Soft.PageFileHigh == MI_SPECIAL_POOL_PTE_NONPAGABLE)) {
  1348. return FALSE;
  1349. }
  1350. return TRUE;
  1351. }
  1352. LOGICAL
  1353. MiIsSpecialPoolAddressNonPaged (
  1354. IN PVOID VirtualAddress
  1355. )
  1356. /*++
  1357. Routine Description:
  1358. This function returns TRUE if the special pool address is nonpaged,
  1359. FALSE if not.
  1360. Arguments:
  1361. VirtualAddress - Supplies the special pool address in question.
  1362. Return Value:
  1363. See above.
  1364. Environment:
  1365. Kernel mode.
  1366. --*/
  1367. {
  1368. PMMPTE PointerPte;
  1369. //
  1370. // Caller must check that the address in in special pool.
  1371. //
  1372. ASSERT (MmIsSpecialPoolAddress (VirtualAddress) == TRUE);
  1373. PointerPte = MiGetPteAddress (VirtualAddress);
  1374. //
  1375. // Take advantage of the fact that adjacent PTEs have the paged/nonpaged
  1376. // bits set when in use and these bits are cleared on free. Note also
  1377. // that freed pages get their PTEs chained together through PageFileHigh.
  1378. //
  1379. if ((PointerPte + 1)->u.Soft.PageFileHigh == MI_SPECIAL_POOL_PTE_NONPAGABLE) {
  1380. return TRUE;
  1381. }
  1382. return FALSE;
  1383. }
  1384. LOGICAL
  1385. MmProtectSpecialPool (
  1386. IN PVOID VirtualAddress,
  1387. IN ULONG NewProtect
  1388. )
  1389. /*++
  1390. Routine Description:
  1391. This function protects a special pool allocation.
  1392. Arguments:
  1393. VirtualAddress - Supplies the special pool address to protect.
  1394. NewProtect - Supplies the protection to set the pages to (PAGE_XX).
  1395. Return Value:
  1396. TRUE if the protection was successfully applied, FALSE if not.
  1397. Environment:
  1398. Kernel mode, IRQL at APC_LEVEL or below for pagable pool, DISPATCH or
  1399. below for nonpagable pool.
  1400. Note that setting an allocation to NO_ACCESS implies that an accessible
  1401. protection must be applied by the caller prior to this allocation being
  1402. freed.
  1403. Note this is a nonpagable wrapper so that machines without special pool
  1404. can still support code attempting to protect special pool at
  1405. DISPATCH_LEVEL.
  1406. --*/
  1407. {
  1408. if (MiSpecialPoolFirstPte == NULL) {
  1409. //
  1410. // The special pool allocation code was never initialized.
  1411. //
  1412. return (ULONG)-1;
  1413. }
  1414. return MiProtectSpecialPool (VirtualAddress, NewProtect);
  1415. }
  1416. LOGICAL
  1417. MiProtectSpecialPool (
  1418. IN PVOID VirtualAddress,
  1419. IN ULONG NewProtect
  1420. )
  1421. /*++
  1422. Routine Description:
  1423. This function protects a special pool allocation.
  1424. Arguments:
  1425. VirtualAddress - Supplies the special pool address to protect.
  1426. NewProtect - Supplies the protection to set the pages to (PAGE_XX).
  1427. Return Value:
  1428. TRUE if the protection was successfully applied, FALSE if not.
  1429. Environment:
  1430. Kernel mode, IRQL at APC_LEVEL or below for pagable pool, DISPATCH or
  1431. below for nonpagable pool.
  1432. Note that setting an allocation to NO_ACCESS implies that an accessible
  1433. protection must be applied by the caller prior to this allocation being
  1434. freed.
  1435. --*/
  1436. {
  1437. KIRQL OldIrql;
  1438. MMPTE PteContents;
  1439. MMPTE NewPteContents;
  1440. MMPTE PreviousPte;
  1441. PMMPTE PointerPte;
  1442. PMMPFN Pfn1;
  1443. ULONG ProtectionMask;
  1444. WSLE_NUMBER WsIndex;
  1445. LOGICAL Pagable;
  1446. LOGICAL SystemWsLocked;
  1447. PMMSUPPORT VmSupport;
  1448. #if defined (_WIN64)
  1449. if ((VirtualAddress >= MmSessionSpecialPoolStart) &&
  1450. (VirtualAddress < MmSessionSpecialPoolEnd)) {
  1451. VmSupport = &MmSessionSpace->GlobalVirtualAddress->Vm;
  1452. }
  1453. else
  1454. #endif
  1455. if (VirtualAddress >= MmSpecialPoolStart && VirtualAddress < MmSpecialPoolEnd)
  1456. {
  1457. VmSupport = &MmSystemCacheWs;
  1458. }
  1459. #if defined (_PROTECT_PAGED_POOL)
  1460. else if ((VirtualAddress >= MmPagedPoolStart) &&
  1461. (VirtualAddress < PagedPoolEnd)) {
  1462. VmSupport = &MmSystemCacheWs;
  1463. }
  1464. #endif
  1465. else {
  1466. return (ULONG)-1;
  1467. }
  1468. ProtectionMask = MiMakeProtectionMask (NewProtect);
  1469. if (ProtectionMask == MM_INVALID_PROTECTION) {
  1470. return (ULONG)-1;
  1471. }
  1472. SystemWsLocked = FALSE;
  1473. PointerPte = MiGetPteAddress (VirtualAddress);
  1474. #if defined (_PROTECT_PAGED_POOL)
  1475. if ((VirtualAddress >= MmPagedPoolStart) &&
  1476. (VirtualAddress < PagedPoolEnd)) {
  1477. Pagable = TRUE;
  1478. }
  1479. else
  1480. #endif
  1481. if ((PointerPte + 1)->u.Soft.PageFileHigh == MI_SPECIAL_POOL_PTE_PAGABLE) {
  1482. Pagable = TRUE;
  1483. SystemWsLocked = TRUE;
  1484. LOCK_WORKING_SET (VmSupport);
  1485. }
  1486. else {
  1487. Pagable = FALSE;
  1488. }
  1489. PteContents = *PointerPte;
  1490. if (ProtectionMask == MM_NOACCESS) {
  1491. if (SystemWsLocked == TRUE) {
  1492. retry1:
  1493. ASSERT (SystemWsLocked == TRUE);
  1494. if (PteContents.u.Hard.Valid == 1) {
  1495. Pfn1 = MI_PFN_ELEMENT (PteContents.u.Hard.PageFrameNumber);
  1496. WsIndex = Pfn1->u1.WsIndex;
  1497. ASSERT (WsIndex != 0);
  1498. Pfn1->OriginalPte.u.Soft.Protection = ProtectionMask;
  1499. MiRemovePageFromWorkingSet (PointerPte,
  1500. Pfn1,
  1501. VmSupport);
  1502. }
  1503. else if (PteContents.u.Soft.Transition == 1) {
  1504. LOCK_PFN2 (OldIrql);
  1505. PteContents = *PointerPte;
  1506. if (PteContents.u.Soft.Transition == 0) {
  1507. UNLOCK_PFN2 (OldIrql);
  1508. goto retry1;
  1509. }
  1510. Pfn1 = MI_PFN_ELEMENT (PteContents.u.Trans.PageFrameNumber);
  1511. Pfn1->OriginalPte.u.Soft.Protection = ProtectionMask;
  1512. PointerPte->u.Soft.Protection = ProtectionMask;
  1513. UNLOCK_PFN2 (OldIrql);
  1514. }
  1515. else {
  1516. //
  1517. // Must be page file space or demand zero.
  1518. //
  1519. PointerPte->u.Soft.Protection = ProtectionMask;
  1520. }
  1521. ASSERT (SystemWsLocked == TRUE);
  1522. UNLOCK_WORKING_SET (VmSupport);
  1523. }
  1524. else {
  1525. ASSERT (SystemWsLocked == FALSE);
  1526. //
  1527. // Make it no access regardless of its previous protection state.
  1528. // Note that the page frame number is preserved.
  1529. //
  1530. PteContents.u.Hard.Valid = 0;
  1531. PteContents.u.Soft.Prototype = 0;
  1532. PteContents.u.Soft.Protection = MM_NOACCESS;
  1533. Pfn1 = MI_PFN_ELEMENT (PteContents.u.Hard.PageFrameNumber);
  1534. LOCK_PFN2 (OldIrql);
  1535. Pfn1->OriginalPte.u.Soft.Protection = ProtectionMask;
  1536. PreviousPte = *PointerPte;
  1537. MI_WRITE_INVALID_PTE (PointerPte, PteContents);
  1538. KeFlushSingleTb (VirtualAddress, TRUE);
  1539. MI_CAPTURE_DIRTY_BIT_TO_PFN (&PreviousPte, Pfn1);
  1540. UNLOCK_PFN2 (OldIrql);
  1541. }
  1542. return TRUE;
  1543. }
  1544. //
  1545. // No guard pages, noncached pages or copy-on-write for special pool.
  1546. //
  1547. if ((ProtectionMask >= MM_NOCACHE) || (ProtectionMask == MM_WRITECOPY) || (ProtectionMask == MM_EXECUTE_WRITECOPY)) {
  1548. if (SystemWsLocked == TRUE) {
  1549. UNLOCK_WORKING_SET (VmSupport);
  1550. }
  1551. return FALSE;
  1552. }
  1553. //
  1554. // Set accessible permissions - the page may already be protected or not.
  1555. //
  1556. if (Pagable == FALSE) {
  1557. Pfn1 = MI_PFN_ELEMENT (PteContents.u.Hard.PageFrameNumber);
  1558. Pfn1->OriginalPte.u.Soft.Protection = ProtectionMask;
  1559. MI_MAKE_VALID_PTE (NewPteContents,
  1560. PteContents.u.Hard.PageFrameNumber,
  1561. ProtectionMask,
  1562. PointerPte);
  1563. if (PteContents.u.Hard.Valid == 1) {
  1564. MI_WRITE_VALID_PTE_NEW_PROTECTION (PointerPte, NewPteContents);
  1565. KeFlushSingleTb (VirtualAddress, TRUE);
  1566. }
  1567. else {
  1568. MI_WRITE_VALID_PTE (PointerPte, NewPteContents);
  1569. }
  1570. ASSERT (SystemWsLocked == FALSE);
  1571. return TRUE;
  1572. }
  1573. retry2:
  1574. ASSERT (SystemWsLocked == TRUE);
  1575. if (PteContents.u.Hard.Valid == 1) {
  1576. Pfn1 = MI_PFN_ELEMENT (PteContents.u.Hard.PageFrameNumber);
  1577. ASSERT (Pfn1->u1.WsIndex != 0);
  1578. LOCK_PFN2 (OldIrql);
  1579. Pfn1->OriginalPte.u.Soft.Protection = ProtectionMask;
  1580. MI_MAKE_VALID_PTE (PteContents,
  1581. PteContents.u.Hard.PageFrameNumber,
  1582. ProtectionMask,
  1583. PointerPte);
  1584. PreviousPte = *PointerPte;
  1585. MI_WRITE_VALID_PTE_NEW_PROTECTION (PointerPte, PteContents);
  1586. KeFlushSingleTb (VirtualAddress, TRUE);
  1587. MI_CAPTURE_DIRTY_BIT_TO_PFN (&PreviousPte, Pfn1);
  1588. UNLOCK_PFN2 (OldIrql);
  1589. }
  1590. else if (PteContents.u.Soft.Transition == 1) {
  1591. LOCK_PFN2 (OldIrql);
  1592. PteContents = *PointerPte;
  1593. if (PteContents.u.Soft.Transition == 0) {
  1594. UNLOCK_PFN2 (OldIrql);
  1595. goto retry2;
  1596. }
  1597. Pfn1 = MI_PFN_ELEMENT (PteContents.u.Trans.PageFrameNumber);
  1598. Pfn1->OriginalPte.u.Soft.Protection = ProtectionMask;
  1599. PointerPte->u.Soft.Protection = ProtectionMask;
  1600. UNLOCK_PFN2 (OldIrql);
  1601. }
  1602. else {
  1603. //
  1604. // Must be page file space or demand zero.
  1605. //
  1606. PointerPte->u.Soft.Protection = ProtectionMask;
  1607. }
  1608. UNLOCK_WORKING_SET (VmSupport);
  1609. return TRUE;
  1610. }
  1611. LOGICAL
  1612. MiCheckSingleFilter (
  1613. ULONG Tag,
  1614. ULONG Filter
  1615. )
  1616. /*++
  1617. Routine Description:
  1618. This function checks if a pool tag matches a given pattern.
  1619. ? - matches a single character
  1620. * - terminates match with TRUE
  1621. N.B.: ability inspired by the !poolfind debugger extension.
  1622. Arguments:
  1623. Tag - a pool tag
  1624. Filter - a globish pattern (chars and/or ?,*)
  1625. Return Value:
  1626. TRUE if a match exists, FALSE otherwise.
  1627. --*/
  1628. {
  1629. ULONG i;
  1630. PUCHAR tc;
  1631. PUCHAR fc;
  1632. tc = (PUCHAR) &Tag;
  1633. fc = (PUCHAR) &Filter;
  1634. for (i = 0; i < 4; i += 1, tc += 1, fc += 1) {
  1635. if (*fc == '*') {
  1636. break;
  1637. }
  1638. if (*fc == '?') {
  1639. continue;
  1640. }
  1641. if (i == 3 && ((*tc) & ~(PROTECTED_POOL >> 24)) == *fc) {
  1642. continue;
  1643. }
  1644. if (*tc != *fc) {
  1645. return FALSE;
  1646. }
  1647. }
  1648. return TRUE;
  1649. }
  1650. LOGICAL
  1651. MmUseSpecialPool (
  1652. IN SIZE_T NumberOfBytes,
  1653. IN ULONG Tag
  1654. )
  1655. /*++
  1656. Routine Description:
  1657. This routine checks whether the specified allocation should be attempted
  1658. from special pool. Both the tag string and the number of bytes are used
  1659. to match against, if either cause a hit, then special pool is recommended.
  1660. Arguments:
  1661. NumberOfBytes - Supplies the number of bytes to commit.
  1662. Tag - Supplies the tag of the requested allocation.
  1663. Return Value:
  1664. TRUE if the caller should attempt to satisfy the requested allocation from
  1665. special pool, FALSE if not.
  1666. Environment:
  1667. Kernel mode, no locks (not even pool locks) held.
  1668. --*/
  1669. {
  1670. if ((NumberOfBytes <= POOL_BUDDY_MAX) &&
  1671. (MmSpecialPoolTag != 0) &&
  1672. (NumberOfBytes != 0)) {
  1673. //
  1674. // Check for a special pool tag match by tag string and size ranges.
  1675. //
  1676. if ((MiCheckSingleFilter (Tag, MmSpecialPoolTag)) ||
  1677. ((MmSpecialPoolTag >= (NumberOfBytes + POOL_OVERHEAD)) &&
  1678. (MmSpecialPoolTag < (NumberOfBytes + POOL_OVERHEAD + POOL_SMALLEST_BLOCK)))) {
  1679. return TRUE;
  1680. }
  1681. }
  1682. return FALSE;
  1683. }