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.

3753 lines
101 KiB

  1. /*++
  2. Copyright (c) 1999 Microsoft Corporation
  3. Module Name:
  4. altperm.c
  5. Abstract:
  6. This module contains the routines to support 4K pages on IA64.
  7. An alternate set of permissions is kept that are on 4K boundaries.
  8. Permissions are kept for all memory, not just split pages
  9. and the information is updated on any call to NtVirtualProtect()
  10. and NtAllocateVirtualMemory().
  11. Author:
  12. Koichi Yamada 18-Aug-1998
  13. Landy Wang (landyw) 02-June-1997
  14. Revision History:
  15. --*/
  16. #include "mi.h"
  17. #if defined(_MIALT4K_)
  18. ULONG
  19. MiFindProtectionForNativePte (
  20. PVOID VirtualAddress
  21. );
  22. VOID
  23. MiResetAccessBitForNativePtes (
  24. IN PVOID StartVirtual,
  25. IN PVOID EndVirtual,
  26. IN PEPROCESS Process
  27. );
  28. LOGICAL
  29. MiIsSplitPage (
  30. IN PVOID Virtual
  31. );
  32. VOID
  33. MiCheckDemandZeroCopyOnWriteFor4kPage (
  34. PVOID VirtualAddress,
  35. PEPROCESS Process
  36. );
  37. LOGICAL
  38. MiIsNativeGuardPage (
  39. IN PVOID VirtualAddress
  40. );
  41. VOID
  42. MiSetNativePteProtection (
  43. IN PVOID VirtualAddress,
  44. IN ULONGLONG NewPteProtection,
  45. IN LOGICAL PageIsSplit,
  46. IN PEPROCESS CurrentProcess
  47. );
  48. VOID
  49. MiSyncAltPte (
  50. IN PVOID VirtualAddress
  51. );
  52. extern PMMPTE MmPteHit;
  53. #if defined (_MI_DEBUG_ALTPTE)
  54. typedef struct _MI_ALTPTE_TRACES {
  55. PETHREAD Thread;
  56. PMMPTE PointerPte;
  57. MMPTE PteContents;
  58. MMPTE NewPteContents;
  59. PVOID Caller;
  60. PVOID CallersCaller;
  61. PVOID Temp[2];
  62. } MI_ALTPTE_TRACES, *PMI_ALTPTE_TRACES;
  63. #define MI_ALTPTE_TRACE_SIZE 0x1000
  64. VOID
  65. FORCEINLINE
  66. MiSnapAltPte (
  67. IN PMMPTE PointerPte,
  68. IN MMPTE NewValue,
  69. IN ULONG Id
  70. )
  71. {
  72. ULONG Index;
  73. SIZE_T NumberOfBytes;
  74. PMI_ALTPTE_TRACES Information;
  75. PVOID HighestUserAddress;
  76. PLONG IndexPointer;
  77. PMI_ALTPTE_TRACES TablePointer;
  78. PWOW64_PROCESS Wow64Process;
  79. HighestUserAddress = MiGetVirtualAddressMappedByPte (MmWorkingSetList->HighestUserPte);
  80. ASSERT (HighestUserAddress <= (PVOID) _4gb);
  81. NumberOfBytes = ((ULONG_PTR)HighestUserAddress >> PTI_SHIFT) / 8;
  82. Wow64Process = PsGetCurrentProcess()->Wow64Process;
  83. ASSERT (Wow64Process != NULL);
  84. IndexPointer = (PLONG) ((PCHAR) Wow64Process->AltPermBitmap + NumberOfBytes);
  85. TablePointer = (PMI_ALTPTE_TRACES)IndexPointer + 1;
  86. Index = InterlockedIncrement (IndexPointer);
  87. Index &= (MI_ALTPTE_TRACE_SIZE - 1);
  88. Information = &TablePointer[Index];
  89. Information->Thread = PsGetCurrentThread ();
  90. Information->PteContents = *PointerPte;
  91. Information->NewPteContents = NewValue;
  92. Information->PointerPte = PointerPte;
  93. #if 1
  94. Information->Caller = MiGetInstructionPointer ();
  95. #else
  96. // Ip generates link (not compile) errors
  97. Information->Caller = (PVOID) __getReg (CV_IA64_Ip);
  98. // StIIp generates no compiler or link errors, but bugcheck 3Bs on the
  99. // execution of the actual generated mov r19=cr.iip instruction.
  100. Information->Caller = (PVOID) __getReg (CV_IA64_StIIP);
  101. #endif
  102. Information->Temp[0] = (PVOID) (ULONG_PTR) Id;
  103. Information->CallersCaller = (PVOID) _ReturnAddress ();
  104. }
  105. #define MI_ALTPTE_TRACKING_BYTES ((MI_ALTPTE_TRACE_SIZE + 1) * sizeof (MI_ALTPTE_TRACES))
  106. #define MI_LOG_ALTPTE_CHANGE(_PointerPte, _PteContents, Id) MiSnapAltPte(_PointerPte, _PteContents, Id)
  107. #else
  108. #define MI_ALTPTE_TRACKING_BYTES 0
  109. #define MI_LOG_ALTPTE_CHANGE(_PointerPte, _PteContents, Id)
  110. #endif
  111. #define MI_WRITE_ALTPTE(PointerAltPte, AltPteContents, Id) { \
  112. MI_LOG_ALTPTE_CHANGE (PointerAltPte, AltPteContents, Id); \
  113. (PointerAltPte)->u.Long = AltPteContents.u.Long; \
  114. }
  115. #if defined (_MI_DEBUG_PTE) && defined (_MI_DEBUG_ALTPTE)
  116. VOID
  117. MiLogPteInAltTrace (
  118. IN PVOID InputNativeInformation
  119. )
  120. {
  121. ULONG Index;
  122. SIZE_T NumberOfBytes;
  123. PMI_ALTPTE_TRACES Information;
  124. PVOID HighestUserAddress;
  125. PLONG IndexPointer;
  126. PMI_ALTPTE_TRACES TablePointer;
  127. PWOW64_PROCESS Wow64Process;
  128. PMI_PTE_TRACES NativeInformation;
  129. NativeInformation = (PMI_PTE_TRACES) InputNativeInformation;
  130. if (PsGetCurrentProcess()->Peb == NULL) {
  131. //
  132. // Don't log PTE traces during process creation if the altperm
  133. // bitmap pool allocation hasn't been done yet (the EPROCESS
  134. // Wow64Process pointer is already initialized) !
  135. //
  136. return;
  137. }
  138. if (PsGetCurrentProcess()->VmDeleted == 1) {
  139. //
  140. // Don't log PTE traces during process deletion as the altperm
  141. // bitmap pool allocation may have already been freed !
  142. //
  143. return;
  144. }
  145. HighestUserAddress = MiGetVirtualAddressMappedByPte (MmWorkingSetList->HighestUserPte);
  146. ASSERT (HighestUserAddress <= (PVOID) _4gb);
  147. NumberOfBytes = ((ULONG_PTR)HighestUserAddress >> PTI_SHIFT) / 8;
  148. Wow64Process = PsGetCurrentProcess()->Wow64Process;
  149. ASSERT (Wow64Process != NULL);
  150. IndexPointer = (PLONG) ((PCHAR) Wow64Process->AltPermBitmap + NumberOfBytes);
  151. TablePointer = (PMI_ALTPTE_TRACES)IndexPointer + 1;
  152. Index = InterlockedIncrement (IndexPointer);
  153. Index &= (MI_ALTPTE_TRACE_SIZE - 1);
  154. Information = &TablePointer[Index];
  155. Information->Thread = NativeInformation->Thread;
  156. Information->PteContents = NativeInformation->PteContents;
  157. Information->NewPteContents = NativeInformation->NewPteContents;
  158. Information->PointerPte = NativeInformation->PointerPte;
  159. Information->Caller = NativeInformation->StackTrace[0];
  160. Information->CallersCaller = NativeInformation->StackTrace[1];
  161. Information->Temp[0] = (PVOID) (ULONG_PTR) -1;
  162. }
  163. #endif
  164. NTSTATUS
  165. MmX86Fault (
  166. IN ULONG_PTR FaultStatus,
  167. IN PVOID VirtualAddress,
  168. IN KPROCESSOR_MODE PreviousMode,
  169. IN PVOID TrapInformation
  170. )
  171. /*++
  172. Routine Description:
  173. This function is called by the kernel on data or instruction
  174. access faults if CurrentProcess->Wow64Process is non-NULL and the
  175. faulting address is within the 32-bit user address space.
  176. This routine determines the type of fault by checking the alternate
  177. 4Kb granular page table and calls MmAccessFault() if necessary to
  178. handle the page fault or the write fault.
  179. Arguments:
  180. FaultStatus - Supplies fault status information bits.
  181. VirtualAddress - Supplies the virtual address which caused the fault.
  182. PreviousMode - Supplies the mode (kernel or user) in which the fault
  183. occurred.
  184. TrapInformation - Opaque information about the trap, interpreted by the
  185. kernel, not Mm. Needed to allow fast interlocked access
  186. to operate correctly.
  187. Return Value:
  188. Returns the status of the fault handling operation. Can be one of:
  189. - Success.
  190. - Access Violation.
  191. - Guard Page Violation.
  192. - In-page Error.
  193. Environment:
  194. Kernel mode.
  195. --*/
  196. {
  197. ULONG i;
  198. ULONG Waited;
  199. PMMVAD TempVad;
  200. MMPTE PteContents;
  201. PMMPTE PointerAltPte;
  202. PMMPTE PointerAltPte2;
  203. PMMPTE PointerAltPteForNativePage;
  204. MMPTE AltPteContents;
  205. PMMPTE PointerPte;
  206. PMMPTE PointerPde;
  207. ULONGLONG NewPteProtection;
  208. LOGICAL FillZero;
  209. LOGICAL PageIsSplit;
  210. LOGICAL SharedPageFault;
  211. LOGICAL NativeGuardPage;
  212. PEPROCESS CurrentProcess;
  213. PWOW64_PROCESS Wow64Process;
  214. KIRQL OldIrql;
  215. NTSTATUS status;
  216. ULONGLONG ProtectionMaskOriginal;
  217. PMMPTE ProtoPte;
  218. PMMPFN Pfn1;
  219. PVOID OriginalVirtualAddress;
  220. ULONG_PTR Vpn;
  221. PVOID ZeroAddress;
  222. PMMPTE PointerPpe;
  223. ULONG FirstProtect;
  224. ASSERT (VirtualAddress < MmWorkingSetList->HighestUserAddress);
  225. if (KeGetCurrentIrql () > APC_LEVEL) {
  226. return MmAccessFault (FaultStatus,
  227. VirtualAddress,
  228. PreviousMode,
  229. TrapInformation);
  230. }
  231. NewPteProtection = 0;
  232. FillZero = FALSE;
  233. PageIsSplit = FALSE;
  234. SharedPageFault = FALSE;
  235. NativeGuardPage = FALSE;
  236. OriginalVirtualAddress = VirtualAddress;
  237. CurrentProcess = PsGetCurrentProcess ();
  238. Wow64Process = CurrentProcess->Wow64Process;
  239. PointerPte = MiGetPteAddress (VirtualAddress);
  240. PointerAltPte = MiGetAltPteAddress (VirtualAddress);
  241. Vpn = MI_VA_TO_VPN (VirtualAddress);
  242. #if DBG
  243. if (PointerPte == MmPteHit) {
  244. DbgPrint ("MM: PTE hit at %p\n", MmPteHit);
  245. DbgBreakPoint ();
  246. }
  247. #endif
  248. //
  249. // Acquire the alternate table mutex, also blocking APCs.
  250. //
  251. LOCK_ALTERNATE_TABLE (Wow64Process);
  252. //
  253. // If a fork operation is in progress and the faulting thread
  254. // is not the thread performing the fork operation, block until
  255. // the fork is completed.
  256. //
  257. if (CurrentProcess->ForkInProgress != NULL) {
  258. UNLOCK_ALTERNATE_TABLE (Wow64Process);
  259. LOCK_WS (CurrentProcess);
  260. if (MiWaitForForkToComplete (CurrentProcess) == FALSE) {
  261. ASSERT (FALSE);
  262. }
  263. UNLOCK_WS (CurrentProcess);
  264. return STATUS_SUCCESS;
  265. }
  266. //
  267. // Check to see if the protection is registered in the alternate entry.
  268. //
  269. if (MI_CHECK_BIT (Wow64Process->AltPermBitmap, Vpn) == 0) {
  270. MiSyncAltPte (VirtualAddress);
  271. }
  272. //
  273. // Read the alternate PTE contents.
  274. //
  275. AltPteContents = *PointerAltPte;
  276. //
  277. // If the alternate PTE indicates no access for this 4K page
  278. // then deliver an access violation.
  279. //
  280. if (AltPteContents.u.Alt.NoAccess != 0) {
  281. status = STATUS_ACCESS_VIOLATION;
  282. MI_BREAK_ON_AV (VirtualAddress, 0x20);
  283. goto return_status;
  284. }
  285. //
  286. // Since we release the AltTable lock before calling MmAccessFault,
  287. // there is a chance that two threads may execute concurrently inside
  288. // MmAccessFault, which would yield bad results since the initial native
  289. // PTE for the page has only READ protection on it. So if two threads
  290. // fault on the same address, one of them will execute through all of
  291. // this routine, however the other one will just return STATUS_SUCCESS
  292. // which will cause another fault to happen in which the protections
  293. // will be fixed on the native page.
  294. //
  295. // Note that in addition to the dual thread case there is also the case
  296. // of a single thread which also has an overlapped I/O pending (for example)
  297. // which can trigger an APC completion memory copy to the same page.
  298. // Protect against this by remaining at APC_LEVEL until clearing the
  299. // inpage in progress in the alternate PTE.
  300. //
  301. if (AltPteContents.u.Alt.InPageInProgress == 1) {
  302. //
  303. // Release the Alt PTE lock
  304. //
  305. UNLOCK_ALTERNATE_TABLE (Wow64Process);
  306. //
  307. // Flush the TB as MiSetNativePteProtection may have edited the PTE.
  308. //
  309. KiFlushSingleTb (OriginalVirtualAddress);
  310. //
  311. // Delay execution so that if this is a high priority thread,
  312. // it won't starve the other thread (that's doing the actual inpage)
  313. // as it may be running at a lower priority.
  314. //
  315. KeDelayExecutionThread (KernelMode, FALSE, (PLARGE_INTEGER)&MmShortTime);
  316. return STATUS_SUCCESS;
  317. }
  318. //
  319. // Check to see if the alternate entry is empty or if anyone has made any
  320. // commitments for the shared pages.
  321. //
  322. if ((AltPteContents.u.Long == 0) ||
  323. ((AltPteContents.u.Alt.Commit == 0) && (AltPteContents.u.Alt.Private == 0))) {
  324. //
  325. // If empty, get the protection information and fill the entry.
  326. //
  327. LOCK_WS (CurrentProcess);
  328. ProtoPte = MiCheckVirtualAddress (VirtualAddress,
  329. &FirstProtect,
  330. &TempVad);
  331. if (ProtoPte != NULL) {
  332. if (FirstProtect == MM_UNKNOWN_PROTECTION) {
  333. //
  334. // Ultimately this must be an address backed by a prototype
  335. // PTE in an image section (and the real PTE is currently
  336. // zero). Therefore we are guaranteed that the protection
  337. // in the prototype PTE is the correct one to use (ie: there
  338. // is no WSLE overriding it).
  339. //
  340. ASSERT (!MI_IS_PHYSICAL_ADDRESS(ProtoPte));
  341. PointerPde = MiGetPteAddress (ProtoPte);
  342. LOCK_PFN (OldIrql);
  343. if (PointerPde->u.Hard.Valid == 0) {
  344. MiMakeSystemAddressValidPfn (ProtoPte, OldIrql);
  345. }
  346. PteContents = *ProtoPte;
  347. if (PteContents.u.Long == 0) {
  348. FirstProtect = MM_NOACCESS;
  349. }
  350. else if (PteContents.u.Hard.Valid == 1) {
  351. //
  352. // The prototype PTE is valid, get the protection from
  353. // the PFN database.
  354. //
  355. Pfn1 = MI_PFN_ELEMENT (PteContents.u.Hard.PageFrameNumber);
  356. FirstProtect = (ULONG) Pfn1->OriginalPte.u.Soft.Protection;
  357. }
  358. else {
  359. //
  360. // The prototype PTE is not valid, ie: subsection format,
  361. // demand zero, pagefile or in transition - in all cases,
  362. // the protection is in the PTE.
  363. //
  364. FirstProtect = (ULONG) PteContents.u.Soft.Protection;
  365. }
  366. UNLOCK_PFN (OldIrql);
  367. ASSERT (FirstProtect != MM_INVALID_PROTECTION);
  368. }
  369. UNLOCK_WS (CurrentProcess);
  370. if (FirstProtect == MM_INVALID_PROTECTION) {
  371. status = STATUS_ACCESS_VIOLATION;
  372. MI_BREAK_ON_AV (VirtualAddress, 0x21);
  373. goto return_status;
  374. }
  375. if (FirstProtect != MM_NOACCESS) {
  376. ProtectionMaskOriginal = MiMakeProtectionAteMask (FirstProtect);
  377. SharedPageFault = TRUE;
  378. ProtectionMaskOriginal |= MM_ATE_COMMIT;
  379. AltPteContents.u.Long = ProtectionMaskOriginal;
  380. AltPteContents.u.Alt.Protection = FirstProtect;
  381. //
  382. // Atomically update the PTE.
  383. //
  384. MI_WRITE_ALTPTE (PointerAltPte, AltPteContents, 1);
  385. }
  386. }
  387. else {
  388. UNLOCK_WS (CurrentProcess);
  389. }
  390. }
  391. if (AltPteContents.u.Alt.Commit == 0) {
  392. //
  393. // If the page is not committed, return an access violation.
  394. //
  395. status = STATUS_ACCESS_VIOLATION;
  396. MI_BREAK_ON_AV (VirtualAddress, 0x22);
  397. goto return_status;
  398. }
  399. //
  400. // Check whether the faulting page is split into 4k pages.
  401. //
  402. PointerAltPte2 = MiGetAltPteAddress (PAGE_ALIGN (VirtualAddress));
  403. PteContents = *PointerAltPte2;
  404. PageIsSplit = FALSE;
  405. for (i = 0; i < SPLITS_PER_PAGE; i += 1) {
  406. if ((PointerAltPte2->u.Long != 0) &&
  407. ((PointerAltPte2->u.Alt.Commit == 0) ||
  408. (PointerAltPte2->u.Alt.Accessed == 0) ||
  409. (PointerAltPte2->u.Alt.CopyOnWrite != 0) ||
  410. (PointerAltPte2->u.Alt.PteIndirect != 0) ||
  411. (PointerAltPte2->u.Alt.FillZero != 0))) {
  412. //
  413. // If it is a NoAccess, FillZero or Guard page, CopyOnWrite,
  414. // mark it as a split page.
  415. //
  416. PageIsSplit = TRUE;
  417. break;
  418. }
  419. if (PteContents.u.Long != PointerAltPte2->u.Long) {
  420. //
  421. // If the next 4kb page is different from the 1st 4k page
  422. // the page is split.
  423. //
  424. PageIsSplit = TRUE;
  425. break;
  426. }
  427. PointerAltPte2 += 1;
  428. }
  429. //
  430. // Get the real protection for the native PTE.
  431. //
  432. NewPteProtection = 0;
  433. PointerAltPte2 -= i;
  434. for (i = 0; i < SPLITS_PER_PAGE; i += 1) {
  435. PteContents.u.Long = PointerAltPte2->u.Long;
  436. if (PteContents.u.Alt.PteIndirect == 0) {
  437. NewPteProtection |= (PointerAltPte2->u.Long & ALT_PROTECTION_MASK);
  438. }
  439. PointerAltPte2 += 1;
  440. }
  441. PointerAltPte2 -= SPLITS_PER_PAGE;
  442. //
  443. // Set the protection for the native PTE.
  444. //
  445. MiSetNativePteProtection (VirtualAddress,
  446. NewPteProtection,
  447. PageIsSplit,
  448. CurrentProcess);
  449. //
  450. // Check the indirect PTE reference case. If so, set the protection for
  451. // the indirect PTE too.
  452. //
  453. if (AltPteContents.u.Alt.PteIndirect != 0) {
  454. PointerPte = (PMMPTE)(AltPteContents.u.Alt.PteOffset + PTE_UBASE);
  455. VirtualAddress = MiGetVirtualAddressMappedByPte (PointerPte);
  456. NewPteProtection = AltPteContents.u.Long & ALT_PROTECTION_MASK;
  457. if (AltPteContents.u.Alt.CopyOnWrite != 0) {
  458. NewPteProtection |= MM_PTE_COPY_ON_WRITE_MASK;
  459. }
  460. MiSetNativePteProtection (VirtualAddress,
  461. NewPteProtection,
  462. FALSE,
  463. CurrentProcess);
  464. }
  465. //
  466. // The faulting 4kb page must be a valid page, but we need to resolve it
  467. // on a case by case basis.
  468. //
  469. ASSERT (AltPteContents.u.Long != 0);
  470. ASSERT (AltPteContents.u.Alt.Commit != 0);
  471. if (AltPteContents.u.Alt.Accessed == 0) {
  472. //
  473. // When PointerAte->u.Hard.Accessed is zero, there are 4 possibilities:
  474. //
  475. // 1. Lowest Protection
  476. // 2. 4kb Demand Zero
  477. // 3. GUARD page fault
  478. // 4. This 4kb page is no access, but the other 4K page(s) within
  479. // the native page has accessible permissions.
  480. //
  481. if (AltPteContents.u.Alt.FillZero != 0) {
  482. //
  483. // Schedule it later.
  484. //
  485. FillZero = TRUE;
  486. }
  487. if ((AltPteContents.u.Alt.Protection & MM_GUARD_PAGE) != 0) {
  488. goto CheckGuardPage;
  489. }
  490. if (FillZero == FALSE) {
  491. //
  492. // This 4kb page has permission set to no access.
  493. //
  494. status = STATUS_ACCESS_VIOLATION;
  495. MI_BREAK_ON_AV (OriginalVirtualAddress, 0x23);
  496. goto return_status;
  497. }
  498. }
  499. if (MI_FAULT_STATUS_INDICATES_EXECUTION (FaultStatus)) {
  500. //
  501. // Execute permission is already given to IA32 by setting it in
  502. // MI_MAKE_VALID_PTE().
  503. //
  504. }
  505. else if (MI_FAULT_STATUS_INDICATES_WRITE(FaultStatus)) {
  506. //
  507. // Check to see if this is a copy-on-write page.
  508. //
  509. if (AltPteContents.u.Alt.CopyOnWrite != 0) {
  510. //
  511. // Let MmAccessFault() perform the copy-on-write.
  512. //
  513. status = MmAccessFault (FaultStatus,
  514. VirtualAddress,
  515. PreviousMode,
  516. TrapInformation);
  517. if (NT_SUCCESS(status)) {
  518. //
  519. // Change the protection of the alternate pages for this
  520. // copy on write native page.
  521. //
  522. ASSERT (PointerAltPte2 == MiGetAltPteAddress (PAGE_ALIGN(OriginalVirtualAddress)));
  523. for (i = 0; i < SPLITS_PER_PAGE; i += 1) {
  524. AltPteContents.u.Long = PointerAltPte2->u.Long;
  525. if (AltPteContents.u.Alt.Commit != 0) {
  526. //
  527. // When a copy-on-write page is touched, the native
  528. // page will be made private by MM, so all the sub-4k
  529. // pages within the native page should be made
  530. // private (if they are committed/mapped).
  531. //
  532. AltPteContents.u.Alt.Private = 1;
  533. if (AltPteContents.u.Alt.CopyOnWrite != 0) {
  534. AltPteContents.u.Alt.CopyOnWrite = 0;
  535. AltPteContents.u.Hard.Write = 1;
  536. AltPteContents.u.Alt.Protection =
  537. MI_MAKE_PROTECT_NOT_WRITE_COPY(AltPteContents.u.Alt.Protection);
  538. }
  539. //
  540. // Atomically update the PTE.
  541. //
  542. MI_WRITE_ALTPTE (PointerAltPte2, AltPteContents, 2);
  543. }
  544. PointerAltPte2 += 1;
  545. }
  546. }
  547. goto return_status;
  548. }
  549. if (AltPteContents.u.Hard.Write == 0) {
  550. status = STATUS_ACCESS_VIOLATION;
  551. MI_BREAK_ON_AV (OriginalVirtualAddress, 0x24);
  552. goto return_status;
  553. }
  554. }
  555. CheckGuardPage:
  556. //
  557. // Indicate that we have begun updating the PTE for this page.
  558. // Subsequent faults on this native page will be restarted.
  559. // This should happen only if the PTE isn't valid.
  560. //
  561. PointerAltPteForNativePage = MiGetAltPteAddress (PAGE_ALIGN (VirtualAddress));
  562. for (i = 0; i < SPLITS_PER_PAGE; i += 1) {
  563. AltPteContents = *PointerAltPteForNativePage;
  564. AltPteContents.u.Alt.InPageInProgress = TRUE;
  565. MI_WRITE_ALTPTE (PointerAltPteForNativePage, AltPteContents, 3);
  566. PointerAltPteForNativePage += 1;
  567. }
  568. //
  569. // Let MmAccessFault() perform an inpage, dirty-bit setting, etc.
  570. //
  571. // Release the alternate table mutex but block APCs to prevent an
  572. // incoming APC that references the same page from deadlocking this thread.
  573. // It is safe to drop allow APCs only after the in progress bit in
  574. // the alternate PTE has been cleared.
  575. //
  576. KeRaiseIrql (APC_LEVEL, &OldIrql);
  577. UNLOCK_ALTERNATE_TABLE (Wow64Process);
  578. status = MmAccessFault (FaultStatus,
  579. VirtualAddress,
  580. PreviousMode,
  581. TrapInformation);
  582. LOCK_ALTERNATE_TABLE (Wow64Process);
  583. //
  584. // This IRQL lower will have no effect since the alternate table guarded
  585. // mutex is now held again.
  586. //
  587. KeLowerIrql (OldIrql);
  588. for (i = 0; i < SPLITS_PER_PAGE; i += 1) {
  589. PointerAltPteForNativePage -= 1;
  590. AltPteContents = *PointerAltPteForNativePage;
  591. AltPteContents.u.Alt.InPageInProgress = FALSE;
  592. MI_WRITE_ALTPTE (PointerAltPteForNativePage, AltPteContents, 4);
  593. }
  594. AltPteContents = *PointerAltPte;
  595. if ((AltPteContents.u.Alt.Protection & MM_GUARD_PAGE) != 0) {
  596. AltPteContents = *PointerAltPte;
  597. AltPteContents.u.Alt.Protection &= ~MM_GUARD_PAGE;
  598. AltPteContents.u.Alt.Accessed = 1;
  599. MI_WRITE_ALTPTE (PointerAltPte, AltPteContents, 5);
  600. if ((status != STATUS_PAGE_FAULT_GUARD_PAGE) &&
  601. (status != STATUS_STACK_OVERFLOW)) {
  602. UNLOCK_ALTERNATE_TABLE (Wow64Process);
  603. status = MiCheckForUserStackOverflow (VirtualAddress);
  604. LOCK_ALTERNATE_TABLE (Wow64Process);
  605. }
  606. }
  607. else if (status == STATUS_GUARD_PAGE_VIOLATION) {
  608. //
  609. // Native PTE has the guard bit set, but the AltPte
  610. // doesn't have it.
  611. //
  612. // See if any of the AltPtes has the guard bit set.
  613. //
  614. ASSERT (PointerAltPteForNativePage == MiGetAltPteAddress (PAGE_ALIGN (VirtualAddress)));
  615. for (i = 0; i < SPLITS_PER_PAGE; i += 1) {
  616. if (PointerAltPteForNativePage->u.Alt.Protection & MM_GUARD_PAGE) {
  617. status = STATUS_SUCCESS;
  618. break;
  619. }
  620. PointerAltPteForNativePage += 1;
  621. }
  622. }
  623. else if ((SharedPageFault == TRUE) && (status == STATUS_ACCESS_VIOLATION)) {
  624. AltPteContents.u.Long = PointerAltPte->u.Long;
  625. AltPteContents.u.Alt.Commit = 0;
  626. MI_WRITE_ALTPTE (PointerAltPte, AltPteContents, 6);
  627. }
  628. return_status:
  629. KiFlushSingleTb (OriginalVirtualAddress);
  630. if (FillZero == TRUE) {
  631. //
  632. // Zero the specified 4k page.
  633. //
  634. PointerAltPte = MiGetAltPteAddress (VirtualAddress);
  635. PointerPte = MiGetPteAddress (VirtualAddress);
  636. PointerPde = MiGetPdeAddress (VirtualAddress);
  637. PointerPpe = MiGetPpeAddress (VirtualAddress);
  638. do {
  639. if (PointerAltPte->u.Alt.FillZero == 0) {
  640. //
  641. // Another thread has already completed the zero operation.
  642. //
  643. goto Finished;
  644. }
  645. //
  646. // Make the PPE and PDE valid as well as the
  647. // page table for the original PTE. This guarantees
  648. // TB forward progress for the TB indirect fault.
  649. //
  650. LOCK_WS_UNSAFE (CurrentProcess);
  651. if (MiDoesPpeExistAndMakeValid (PointerPpe,
  652. CurrentProcess,
  653. MM_NOIRQL,
  654. &Waited) == FALSE) {
  655. PteContents.u.Long = 0;
  656. }
  657. else if (MiDoesPdeExistAndMakeValid (PointerPde,
  658. CurrentProcess,
  659. MM_NOIRQL,
  660. &Waited) == FALSE) {
  661. PteContents.u.Long = 0;
  662. }
  663. else {
  664. //
  665. // Now it is safe to read PointerPte.
  666. //
  667. PteContents = *PointerPte;
  668. }
  669. //
  670. // The alternate PTE may have been trimmed during the period
  671. // prior to the working set mutex being acquired or when it
  672. // was released prior to being reacquired.
  673. //
  674. if (MiIsAddressValid (PointerAltPte, TRUE) == TRUE) {
  675. break;
  676. }
  677. UNLOCK_WS_UNSAFE (CurrentProcess);
  678. } while (TRUE);
  679. AltPteContents.u.Long = PointerAltPte->u.Long;
  680. if (PteContents.u.Hard.Valid != 0) {
  681. ZeroAddress = KSEG_ADDRESS (PteContents.u.Hard.PageFrameNumber);
  682. ZeroAddress =
  683. (PVOID)((ULONG_PTR)ZeroAddress +
  684. ((ULONG_PTR)PAGE_4K_ALIGN(VirtualAddress) & (PAGE_SIZE-1)));
  685. RtlZeroMemory (ZeroAddress, PAGE_4K);
  686. UNLOCK_WS_UNSAFE (CurrentProcess);
  687. AltPteContents.u.Alt.FillZero = 0;
  688. AltPteContents.u.Alt.Accessed = 1;
  689. }
  690. else {
  691. UNLOCK_WS_UNSAFE (CurrentProcess);
  692. AltPteContents.u.Alt.Accessed = 0;
  693. }
  694. MI_WRITE_ALTPTE (PointerAltPte, AltPteContents, 7);
  695. }
  696. Finished:
  697. UNLOCK_ALTERNATE_TABLE (Wow64Process);
  698. return status;
  699. }
  700. VOID
  701. MiSyncAltPte (
  702. IN PVOID VirtualAddress
  703. )
  704. /*++
  705. Routine Description:
  706. This function is called to compute the alternate PTE entries for the
  707. given virtual address. It is called with the alternate table mutex held
  708. and updates the alternate table bitmap before returning.
  709. Arguments:
  710. VirtualAddress - Supplies the virtual address to evaluate.
  711. Return Value:
  712. None.
  713. Environment:
  714. Kernel mode, alternate table mutex held.
  715. --*/
  716. {
  717. PMMVAD TempVad;
  718. MMPTE PteContents;
  719. PMMPTE PointerAltPte;
  720. MMPTE AltPteContents;
  721. PMMPTE PointerPde;
  722. PEPROCESS CurrentProcess;
  723. PWOW64_PROCESS Wow64Process;
  724. KIRQL OldIrql;
  725. PMMPTE ProtoPte;
  726. PMMPFN Pfn1;
  727. ULONG_PTR Vpn;
  728. ULONG FirstProtect;
  729. ULONG SecondProtect;
  730. PSUBSECTION Subsection;
  731. PSUBSECTION FirstSubsection;
  732. PCONTROL_AREA ControlArea;
  733. PMMVAD Vad;
  734. Vpn = MI_VA_TO_VPN (VirtualAddress);
  735. CurrentProcess = PsGetCurrentProcess ();
  736. Wow64Process = CurrentProcess->Wow64Process;
  737. LOCK_WS_UNSAFE (CurrentProcess);
  738. ASSERT ((MiGetPpeAddress (VirtualAddress)->u.Hard.Valid == 0) ||
  739. (MiGetPdeAddress (VirtualAddress)->u.Hard.Valid == 0) ||
  740. (MiGetPteAddress (VirtualAddress)->u.Long == 0));
  741. ProtoPte = MiCheckVirtualAddress (VirtualAddress,
  742. &FirstProtect,
  743. &TempVad);
  744. if (FirstProtect == MM_UNKNOWN_PROTECTION) {
  745. //
  746. // Ultimately this must be an address backed by a prototype PTE in
  747. // an image section (and the real PTE is currently zero). Therefore
  748. // we are guaranteed that the protection in the prototype PTE
  749. // is the correct one to use (ie: there is no WSLE overriding it).
  750. //
  751. Vad = MiLocateAddress (VirtualAddress);
  752. ASSERT (Vad != NULL);
  753. ControlArea = Vad->ControlArea;
  754. ASSERT (ControlArea->u.Flags.Image == 1);
  755. if ((ControlArea->u.Flags.Rom == 1) ||
  756. (ControlArea->u.Flags.GlobalOnlyPerSession == 1)) {
  757. FirstSubsection = (PSUBSECTION)((PLARGE_CONTROL_AREA)ControlArea + 1);
  758. }
  759. else {
  760. FirstSubsection = (PSUBSECTION)(ControlArea + 1);
  761. }
  762. ASSERT (!MI_IS_PHYSICAL_ADDRESS (ProtoPte));
  763. //
  764. // Get the original protection information from the prototype PTE.
  765. //
  766. // A non-NULL subsection indicates split permissions need to be
  767. // applied on the ALT PTEs for this native PTE.
  768. //
  769. Subsection = NULL;
  770. //
  771. // Read the prototype PTE contents without the PFN lock - the PFN
  772. // lock is only needed if the prototype PTE is valid so that the
  773. // PFN's original PTE field can be fetched.
  774. //
  775. PteContents = *ProtoPte;
  776. DecodeProto:
  777. if (PteContents.u.Hard.Valid == 0) {
  778. if (PteContents.u.Long == 0) {
  779. FirstProtect = MM_NOACCESS;
  780. }
  781. else {
  782. //
  783. // The prototype PTE is not valid, ie: subsection format,
  784. // demand zero, pagefile or in transition - in all cases,
  785. // the protection is in the PTE.
  786. //
  787. FirstProtect = (ULONG) PteContents.u.Soft.Protection;
  788. if (PteContents.u.Soft.SplitPermissions == 1) {
  789. Subsection = (PSUBSECTION) 1;
  790. }
  791. }
  792. }
  793. else {
  794. PointerPde = MiGetPteAddress (ProtoPte);
  795. LOCK_PFN (OldIrql);
  796. if (PointerPde->u.Hard.Valid == 0) {
  797. MiMakeSystemAddressValidPfn (ProtoPte, OldIrql);
  798. }
  799. PteContents = *ProtoPte;
  800. if (PteContents.u.Hard.Valid == 0) {
  801. UNLOCK_PFN (OldIrql);
  802. goto DecodeProto;
  803. }
  804. //
  805. // The prototype PTE is valid, get the protection from
  806. // the PFN database. Unless the protection is split, in
  807. // which case it must be retrieved from the subsection.
  808. // Note that if the page has been trimmed already then
  809. // the original PTE is no longer in subsection format.
  810. //
  811. Pfn1 = MI_PFN_ELEMENT (PteContents.u.Hard.PageFrameNumber);
  812. FirstProtect = (ULONG) Pfn1->OriginalPte.u.Soft.Protection;
  813. UNLOCK_PFN (OldIrql);
  814. if (PteContents.u.Hard.Cache == MM_PTE_CACHE_RESERVED) {
  815. Subsection = (PSUBSECTION) 1;
  816. }
  817. }
  818. ASSERT (FirstProtect != MM_INVALID_PROTECTION);
  819. if (Subsection != NULL) {
  820. //
  821. // Compute the subsection address as the split permissions are
  822. // stored there. Note to reduce lock contention this was
  823. // deferred until after the PFN lock was released.
  824. //
  825. Subsection = FirstSubsection;
  826. do {
  827. ASSERT (Subsection->SubsectionBase != NULL);
  828. if ((ProtoPte >= Subsection->SubsectionBase) &&
  829. (ProtoPte < Subsection->SubsectionBase + Subsection->PtesInSubsection)) {
  830. break;
  831. }
  832. Subsection = Subsection->NextSubsection;
  833. } while (TRUE);
  834. ASSERT (Subsection != NULL);
  835. //
  836. // Get the protection for each 4K page from the subsection.
  837. //
  838. FirstProtect = Subsection->u.SubsectionFlags.Protection;
  839. SecondProtect = Subsection->LastSplitPageProtection;
  840. }
  841. else {
  842. //
  843. // If demand-zero and copy-on-write, remove copy-on-write.
  844. // Note this cannot happen for images with native (ie: multiple
  845. // subsection) support.
  846. //
  847. if ((!IS_PTE_NOT_DEMAND_ZERO (PteContents)) &&
  848. (PteContents.u.Soft.Protection & MM_COPY_ON_WRITE_MASK)) {
  849. FirstProtect = FirstProtect & ~MM_PROTECTION_COPY_MASK;
  850. }
  851. SecondProtect = FirstProtect;
  852. }
  853. ASSERT ((FirstProtect != MM_INVALID_PROTECTION) &&
  854. (SecondProtect != MM_INVALID_PROTECTION));
  855. UNLOCK_WS_UNSAFE (CurrentProcess);
  856. PointerAltPte = MiGetAltPteAddress (PAGE_ALIGN (VirtualAddress));
  857. //
  858. // Update the first alternate PTE.
  859. //
  860. AltPteContents.u.Long = MiMakeProtectionAteMask (FirstProtect) | MM_ATE_COMMIT;
  861. AltPteContents.u.Alt.Protection = FirstProtect;
  862. if ((FirstProtect & MM_PROTECTION_COPY_MASK) == 0) {
  863. //
  864. // If copy-on-write is removed, make it private.
  865. //
  866. AltPteContents.u.Alt.Private = 1;
  867. }
  868. MI_WRITE_ALTPTE (PointerAltPte, AltPteContents, 8);
  869. //
  870. // Update the second alternate PTE, computing it
  871. // only if it is different from the first.
  872. //
  873. if (Subsection != NULL) {
  874. AltPteContents.u.Long = MiMakeProtectionAteMask (SecondProtect) | MM_ATE_COMMIT;
  875. AltPteContents.u.Alt.Protection = SecondProtect;
  876. if ((SecondProtect & MM_PROTECTION_COPY_MASK) == 0) {
  877. //
  878. // If copy-on-write is removed, make it private.
  879. //
  880. AltPteContents.u.Alt.Private = 1;
  881. }
  882. }
  883. }
  884. else {
  885. UNLOCK_WS_UNSAFE (CurrentProcess);
  886. AltPteContents.u.Long = MiMakeProtectionAteMask (FirstProtect);
  887. AltPteContents.u.Alt.Protection = FirstProtect;
  888. AltPteContents.u.Alt.Commit = 1;
  889. PointerAltPte = MiGetAltPteAddress (PAGE_ALIGN (VirtualAddress));
  890. //
  891. // Update the alternate PTEs.
  892. //
  893. MI_WRITE_ALTPTE (PointerAltPte, AltPteContents, 9);
  894. }
  895. MI_WRITE_ALTPTE (PointerAltPte + 1, AltPteContents, 0xA);
  896. //
  897. // Update the bitmap.
  898. //
  899. MI_SET_BIT (Wow64Process->AltPermBitmap, Vpn);
  900. return;
  901. }
  902. VOID
  903. MiProtectImageFileFor4kPage (
  904. IN PVOID VirtualAddress,
  905. IN SIZE_T ViewSize
  906. )
  907. {
  908. ULONG Vpn;
  909. PVOID EndAddress;
  910. PULONG Bitmap;
  911. PWOW64_PROCESS Wow64Process;
  912. ASSERT (BYTE_OFFSET (VirtualAddress) == 0);
  913. Vpn = (ULONG) MI_VA_TO_VPN (VirtualAddress);
  914. EndAddress = (PVOID)((PCHAR) VirtualAddress + ViewSize - 1);
  915. Wow64Process = PsGetCurrentProcess()->Wow64Process;
  916. Bitmap = Wow64Process->AltPermBitmap;
  917. LOCK_ALTERNATE_TABLE_UNSAFE (Wow64Process);
  918. do {
  919. if (MI_CHECK_BIT (Bitmap, Vpn) == 0) {
  920. MiSyncAltPte (VirtualAddress);
  921. }
  922. VirtualAddress = (PVOID)((PCHAR) VirtualAddress + PAGE_SIZE);
  923. Vpn += 1;
  924. } while (VirtualAddress <= EndAddress);
  925. UNLOCK_ALTERNATE_TABLE_UNSAFE (Wow64Process);
  926. return;
  927. }
  928. //
  929. // Define and initialize the protection conversion table for the
  930. // Alternate Permission Table Entries.
  931. //
  932. ULONGLONG MmProtectToAteMask[32] = {
  933. MM_PTE_NOACCESS | MM_ATE_NOACCESS,
  934. MM_PTE_EXECUTE_READ | MM_PTE_ACCESS_MASK,
  935. MM_PTE_EXECUTE_READ | MM_PTE_ACCESS_MASK,
  936. MM_PTE_EXECUTE_READ | MM_PTE_ACCESS_MASK,
  937. MM_PTE_EXECUTE_READWRITE | MM_PTE_ACCESS_MASK,
  938. MM_PTE_EXECUTE_READ | MM_PTE_ACCESS_MASK | MM_ATE_COPY_ON_WRITE,
  939. MM_PTE_EXECUTE_READWRITE | MM_PTE_ACCESS_MASK,
  940. MM_PTE_EXECUTE_READ | MM_PTE_ACCESS_MASK | MM_ATE_COPY_ON_WRITE,
  941. MM_PTE_NOACCESS | MM_ATE_NOACCESS,
  942. MM_PTE_EXECUTE_READ | MM_PTE_ACCESS_MASK,
  943. MM_PTE_EXECUTE_READ | MM_PTE_ACCESS_MASK,
  944. MM_PTE_EXECUTE_READ | MM_PTE_ACCESS_MASK,
  945. MM_PTE_EXECUTE_READWRITE | MM_PTE_ACCESS_MASK,
  946. MM_PTE_EXECUTE_READ | MM_PTE_ACCESS_MASK | MM_ATE_COPY_ON_WRITE,
  947. MM_PTE_EXECUTE_READWRITE | MM_PTE_ACCESS_MASK,
  948. MM_PTE_EXECUTE_READ | MM_PTE_ACCESS_MASK | MM_ATE_COPY_ON_WRITE,
  949. MM_PTE_NOACCESS | MM_ATE_NOACCESS,
  950. MM_PTE_EXECUTE_READ,
  951. MM_PTE_EXECUTE_READ,
  952. MM_PTE_EXECUTE_READ,
  953. MM_PTE_EXECUTE_READWRITE,
  954. MM_PTE_EXECUTE_READ | MM_ATE_COPY_ON_WRITE,
  955. MM_PTE_EXECUTE_READWRITE,
  956. MM_PTE_EXECUTE_READ | MM_ATE_COPY_ON_WRITE,
  957. MM_PTE_NOACCESS | MM_ATE_NOACCESS,
  958. MM_PTE_EXECUTE_READ,
  959. MM_PTE_EXECUTE_READ,
  960. MM_PTE_EXECUTE_READ,
  961. MM_PTE_EXECUTE_READWRITE,
  962. MM_PTE_EXECUTE_READ | MM_ATE_COPY_ON_WRITE,
  963. MM_PTE_EXECUTE_READWRITE,
  964. MM_PTE_EXECUTE_READ | MM_ATE_COPY_ON_WRITE
  965. };
  966. #define MiMakeProtectionAteMask(NewProtect) MmProtectToAteMask[NewProtect]
  967. VOID
  968. MiProtectFor4kPage (
  969. IN PVOID Starting4KAddress,
  970. IN SIZE_T Size,
  971. IN ULONG NewProtect,
  972. IN ULONG Flags,
  973. IN PEPROCESS Process
  974. )
  975. /*++
  976. Routine Description:
  977. This routine sets the permissions on the alternate bitmap (based on
  978. 4K page sizes). The base and size are assumed to be aligned for
  979. 4K pages already.
  980. Arguments:
  981. Starting4KAddress - Supplies the base address (assumed to be 4K aligned already).
  982. Size - Supplies the size to be protected (assumed to be 4K aligned already).
  983. NewProtect - Supplies the protection for the new pages.
  984. Flags - Supplies the alternate table entry request flags.
  985. Process - Supplies a pointer to the process in which to create the
  986. protections on the alternate table.
  987. Return Value:
  988. None.
  989. Environment:
  990. Kernel mode. Address creation mutex held at APC_LEVEL.
  991. --*/
  992. {
  993. RTL_BITMAP BitMap;
  994. ULONG NumberOfPtes;
  995. ULONG StartingNativeVpn;
  996. PVOID Ending4KAddress;
  997. ULONG NewProtectNotCopy;
  998. ULONGLONG ProtectionMask;
  999. ULONGLONG ProtectionMaskNotCopy;
  1000. PMMPTE StartAltPte;
  1001. PMMPTE EndAltPte;
  1002. PMMPTE StartAltPte0;
  1003. PMMPTE EndAltPte0;
  1004. PWOW64_PROCESS Wow64Process;
  1005. MMPTE AltPteContents;
  1006. MMPTE TempAltPte;
  1007. Ending4KAddress = (PCHAR)Starting4KAddress + Size - 1;
  1008. //
  1009. // If the addresses are not WOW64 then nothing needs to be done here.
  1010. //
  1011. if ((Starting4KAddress >= MmWorkingSetList->HighestUserAddress) ||
  1012. (Ending4KAddress >= MmWorkingSetList->HighestUserAddress)) {
  1013. return;
  1014. }
  1015. //
  1016. // Set up the protection to be used for this range of addresses.
  1017. //
  1018. ProtectionMask = MiMakeProtectionAteMask (NewProtect);
  1019. if ((NewProtect & MM_COPY_ON_WRITE_MASK) == MM_COPY_ON_WRITE_MASK) {
  1020. NewProtectNotCopy = NewProtect & ~MM_PROTECTION_COPY_MASK;
  1021. ProtectionMaskNotCopy = MiMakeProtectionAteMask (NewProtectNotCopy);
  1022. }
  1023. else {
  1024. NewProtectNotCopy = NewProtect;
  1025. ProtectionMaskNotCopy = ProtectionMask;
  1026. }
  1027. if (Flags & ALT_COMMIT) {
  1028. ProtectionMask |= MM_ATE_COMMIT;
  1029. ProtectionMaskNotCopy |= MM_ATE_COMMIT;
  1030. }
  1031. //
  1032. // Get the entry in the table for each of these addresses.
  1033. //
  1034. StartAltPte = MiGetAltPteAddress (Starting4KAddress);
  1035. EndAltPte = MiGetAltPteAddress (Ending4KAddress);
  1036. NumberOfPtes = (ULONG) ADDRESS_AND_SIZE_TO_SPAN_PAGES (Starting4KAddress,
  1037. (ULONG_PTR)Ending4KAddress -
  1038. (ULONG_PTR)Starting4KAddress);
  1039. ASSERT (NumberOfPtes != 0);
  1040. StartAltPte0 = MiGetAltPteAddress (PAGE_ALIGN (Starting4KAddress));
  1041. EndAltPte0 = MiGetAltPteAddress ((ULONG_PTR)PAGE_ALIGN(Ending4KAddress)+PAGE_SIZE-1);
  1042. Wow64Process = Process->Wow64Process;
  1043. StartingNativeVpn = (ULONG) MI_VA_TO_VPN (Starting4KAddress);
  1044. TempAltPte.u.Long = 0;
  1045. //
  1046. // Acquire the mutex guarding the alternate page table.
  1047. //
  1048. LOCK_ALTERNATE_TABLE_UNSAFE (Wow64Process);
  1049. if (!(Flags & ALT_ALLOCATE) &&
  1050. (MI_CHECK_BIT(Wow64Process->AltPermBitmap, StartingNativeVpn) == 0)) {
  1051. UNLOCK_ALTERNATE_TABLE_UNSAFE (Wow64Process);
  1052. return;
  1053. }
  1054. //
  1055. // Change all of the protections.
  1056. //
  1057. while (StartAltPte <= EndAltPte) {
  1058. AltPteContents.u.Long = StartAltPte->u.Long;
  1059. TempAltPte.u.Long = ProtectionMask;
  1060. TempAltPte.u.Alt.Protection = NewProtect;
  1061. if (!(Flags & ALT_ALLOCATE)) {
  1062. if (AltPteContents.u.Alt.Private != 0) {
  1063. //
  1064. // If it is already private, don't make it writecopy.
  1065. //
  1066. TempAltPte.u.Long = ProtectionMaskNotCopy;
  1067. TempAltPte.u.Alt.Protection = NewProtectNotCopy;
  1068. //
  1069. // Private is sticky bit.
  1070. //
  1071. TempAltPte.u.Alt.Private = 1;
  1072. }
  1073. if (AltPteContents.u.Alt.FillZero != 0) {
  1074. TempAltPte.u.Alt.Accessed = 0;
  1075. TempAltPte.u.Alt.FillZero = 1;
  1076. }
  1077. //
  1078. // Leave the other sticky attribute bits.
  1079. //
  1080. TempAltPte.u.Alt.Lock = AltPteContents.u.Alt.Lock;
  1081. TempAltPte.u.Alt.PteIndirect = AltPteContents.u.Alt.PteIndirect;
  1082. TempAltPte.u.Alt.PteOffset = AltPteContents.u.Alt.PteOffset;
  1083. }
  1084. if (Flags & ALT_CHANGE) {
  1085. //
  1086. // If it is a change request, make commit sticky.
  1087. //
  1088. TempAltPte.u.Alt.Commit = AltPteContents.u.Alt.Commit;
  1089. }
  1090. //
  1091. // Atomic PTE update.
  1092. //
  1093. MI_WRITE_ALTPTE (StartAltPte, TempAltPte, 0xB);
  1094. StartAltPte += 1;
  1095. }
  1096. ASSERT (TempAltPte.u.Long != 0);
  1097. if (Flags & ALT_ALLOCATE) {
  1098. //
  1099. // Fill the empty Alt PTE as NoAccess ATE at the end.
  1100. //
  1101. EndAltPte += 1;
  1102. while (EndAltPte <= EndAltPte0) {
  1103. if (EndAltPte->u.Long == 0) {
  1104. TempAltPte.u.Long = EndAltPte->u.Long;
  1105. TempAltPte.u.Alt.NoAccess = 1;
  1106. //
  1107. // Atomic PTE update.
  1108. //
  1109. MI_WRITE_ALTPTE (EndAltPte, TempAltPte, 0xC);
  1110. }
  1111. EndAltPte += 1;
  1112. }
  1113. //
  1114. // Update the permission bitmap.
  1115. //
  1116. // Initialize the bitmap inline for speed.
  1117. //
  1118. BitMap.SizeOfBitMap = (ULONG)((ULONG_PTR)MmWorkingSetList->HighestUserAddress >> PTI_SHIFT);
  1119. BitMap.Buffer = Wow64Process->AltPermBitmap;
  1120. RtlSetBits (&BitMap, StartingNativeVpn, NumberOfPtes);
  1121. }
  1122. MiResetAccessBitForNativePtes (Starting4KAddress, Ending4KAddress, Process);
  1123. UNLOCK_ALTERNATE_TABLE_UNSAFE (Wow64Process);
  1124. }
  1125. VOID
  1126. MiProtectMapFileFor4kPage (
  1127. IN PVOID Base,
  1128. IN SIZE_T Size,
  1129. IN ULONG NewProtect,
  1130. IN SIZE_T CommitSize,
  1131. IN PMMPTE PointerPte,
  1132. IN PMMPTE LastPte,
  1133. IN PEPROCESS Process
  1134. )
  1135. /*++
  1136. Routine Description:
  1137. This routine sets the permissions on the alternate bitmap (based on
  1138. 4K page sizes). The base and size are assumed to be aligned for
  1139. 4K pages already.
  1140. Arguments:
  1141. Base - Supplies the base address (assumed to be 4K aligned already).
  1142. Size - Supplies the size to be protected (assumed to be 4K aligned already).
  1143. NewProtect - Supplies the protection for the new pages.
  1144. CommitSize - Supplies the commit size.
  1145. PointerPte - Supplies the starting PTE.
  1146. LastPte - Supplies the last PTE.
  1147. Process - Supplies a pointer to the process in which to create the
  1148. protections on the alternate table.
  1149. Return Value:
  1150. None.
  1151. Environment:
  1152. Kernel mode. Address creation mutex held at APC_LEVEL.
  1153. --*/
  1154. {
  1155. RTL_BITMAP BitMap;
  1156. PVOID Starting4KAddress;
  1157. PVOID Ending4KAddress;
  1158. ULONGLONG ProtectionMask;
  1159. PMMPTE StartAltPte;
  1160. PMMPTE EndAltPte;
  1161. PMMPTE EndAltPte0;
  1162. PWOW64_PROCESS Wow64Process;
  1163. MMPTE TempAltPte;
  1164. PMMPTE LastCommitPte;
  1165. ULONG Vpn;
  1166. ULONG VpnRange;
  1167. Wow64Process = Process->Wow64Process;
  1168. Starting4KAddress = Base;
  1169. Ending4KAddress = (PCHAR)Base + Size - 1;
  1170. //
  1171. // If the addresses are not WOW64 then nothing needs to be done here.
  1172. //
  1173. if ((Starting4KAddress >= MmWorkingSetList->HighestUserAddress) ||
  1174. (Ending4KAddress >= MmWorkingSetList->HighestUserAddress)) {
  1175. return;
  1176. }
  1177. Vpn = (ULONG) MI_VA_TO_VPN (Base);
  1178. VpnRange = (ULONG) (MI_VA_TO_VPN (Ending4KAddress) - Vpn + 1);
  1179. //
  1180. // Set up the protection to be used for this range of addresses.
  1181. //
  1182. ProtectionMask = MiMakeProtectionAteMask (NewProtect);
  1183. //
  1184. // Get the entry in the table for each of these addresses.
  1185. //
  1186. StartAltPte = MiGetAltPteAddress (Starting4KAddress);
  1187. EndAltPte = MiGetAltPteAddress (Ending4KAddress);
  1188. EndAltPte0 = MiGetAltPteAddress ((ULONG_PTR)PAGE_ALIGN(Ending4KAddress)+PAGE_SIZE-1);
  1189. LastCommitPte = PointerPte + BYTES_TO_PAGES (CommitSize);
  1190. TempAltPte.u.Long = ProtectionMask;
  1191. TempAltPte.u.Alt.Protection = NewProtect;
  1192. //
  1193. // Initialize the bitmap inline for speed.
  1194. //
  1195. BitMap.SizeOfBitMap = (ULONG)((ULONG_PTR)MmWorkingSetList->HighestUserAddress >> PTI_SHIFT);
  1196. BitMap.Buffer = Wow64Process->AltPermBitmap;
  1197. LOCK_ALTERNATE_TABLE_UNSAFE (Wow64Process);
  1198. KeAcquireGuardedMutexUnsafe (&MmSectionCommitMutex);
  1199. //
  1200. // And then change all of the protections.
  1201. //
  1202. while (StartAltPte <= EndAltPte) {
  1203. if (PointerPte < LastCommitPte) {
  1204. TempAltPte.u.Alt.Commit = 1;
  1205. }
  1206. else if ((PointerPte <= LastPte) && (PointerPte->u.Long != 0)) {
  1207. TempAltPte.u.Alt.Commit = 1;
  1208. }
  1209. else {
  1210. TempAltPte.u.Alt.Commit = 0;
  1211. }
  1212. //
  1213. // Atomic PTE update.
  1214. //
  1215. MI_WRITE_ALTPTE (StartAltPte, TempAltPte, 0xD);
  1216. StartAltPte += 1;
  1217. if (((ULONG_PTR)StartAltPte & ((SPLITS_PER_PAGE * sizeof(MMPTE))-1)) == 0) {
  1218. PointerPte += 1;
  1219. }
  1220. }
  1221. ASSERT (TempAltPte.u.Long != 0);
  1222. KeReleaseGuardedMutexUnsafe (&MmSectionCommitMutex);
  1223. //
  1224. // Fill the empty Alt PTE as NoAccess ATE at the end.
  1225. //
  1226. EndAltPte += 1;
  1227. while (EndAltPte <= EndAltPte0) {
  1228. if (EndAltPte->u.Long == 0) {
  1229. TempAltPte.u.Long = EndAltPte->u.Long;
  1230. TempAltPte.u.Alt.NoAccess = 1;
  1231. //
  1232. // Atomic PTE size update.
  1233. //
  1234. MI_WRITE_ALTPTE (EndAltPte, TempAltPte, 0xE);
  1235. }
  1236. EndAltPte += 1;
  1237. }
  1238. RtlSetBits (&BitMap, Vpn, VpnRange);
  1239. UNLOCK_ALTERNATE_TABLE_UNSAFE (Wow64Process);
  1240. }
  1241. VOID
  1242. MiReleaseFor4kPage (
  1243. IN PVOID StartVirtual,
  1244. IN PVOID EndVirtual,
  1245. IN PEPROCESS Process
  1246. )
  1247. /*++
  1248. Routine Description:
  1249. This function releases a region of pages within the virtual address
  1250. space of the subject process.
  1251. Arguments:
  1252. StartVirtual - Supplies the start address of the region of pages
  1253. to be released.
  1254. EndVirtual - Supplies the end address of the region of pages to be released.
  1255. Process - Supplies a pointer to the process in which to release the
  1256. region of pages.
  1257. Return Value:
  1258. None.
  1259. Environment:
  1260. Kernel mode. Address creation mutex held at APC_LEVEL.
  1261. --*/
  1262. {
  1263. RTL_BITMAP BitMap;
  1264. PMMPTE StartAltPte;
  1265. PMMPTE EndAltPte;
  1266. MMPTE TempAltPte;
  1267. PVOID VirtualAddress;
  1268. PVOID OriginalStartVa;
  1269. PVOID OriginalEndVa;
  1270. ULONG i;
  1271. PWOW64_PROCESS Wow64Process;
  1272. PFN_NUMBER NumberOfAltPtes;
  1273. ASSERT (StartVirtual <= EndVirtual);
  1274. OriginalStartVa = StartVirtual;
  1275. OriginalEndVa = EndVirtual;
  1276. Wow64Process = Process->Wow64Process;
  1277. StartAltPte = MiGetAltPteAddress (StartVirtual);
  1278. EndAltPte = MiGetAltPteAddress (EndVirtual);
  1279. NumberOfAltPtes = EndAltPte - StartAltPte + 1;
  1280. TempAltPte.u.Long = 0;
  1281. TempAltPte.u.Alt.NoAccess = 1;
  1282. TempAltPte.u.Alt.FillZero = 1;
  1283. StartVirtual = PAGE_ALIGN (StartVirtual);
  1284. VirtualAddress = StartVirtual;
  1285. ASSERT ((ULONG) ADDRESS_AND_SIZE_TO_SPAN_PAGES (StartVirtual,
  1286. (ULONG_PTR)EndVirtual -
  1287. (ULONG_PTR)StartVirtual) != 0);
  1288. LOCK_ALTERNATE_TABLE_UNSAFE (Wow64Process);
  1289. do {
  1290. MI_WRITE_ALTPTE (StartAltPte, TempAltPte, 0xF);
  1291. NumberOfAltPtes -= 1;
  1292. StartAltPte += 1;
  1293. } while (NumberOfAltPtes != 0);
  1294. while (VirtualAddress <= EndVirtual) {
  1295. StartAltPte = MiGetAltPteAddress (VirtualAddress);
  1296. TempAltPte = *StartAltPte;
  1297. i = 0;
  1298. //
  1299. // Note that this check must be made as the ATE fill above may not
  1300. // have begun on a native page boundary and this scan always does.
  1301. //
  1302. while (TempAltPte.u.Long == StartAltPte->u.Long) {
  1303. i += 1;
  1304. if (i == SPLITS_PER_PAGE) {
  1305. while (i != 0) {
  1306. MI_WRITE_ALTPTE (StartAltPte, ZeroPte, 0x10);
  1307. StartAltPte -= 1;
  1308. i -= 1;
  1309. }
  1310. break;
  1311. }
  1312. StartAltPte += 1;
  1313. }
  1314. VirtualAddress = (PVOID)((PCHAR) VirtualAddress + PAGE_SIZE);
  1315. }
  1316. MiResetAccessBitForNativePtes (StartVirtual, EndVirtual, Process);
  1317. //
  1318. // Mark the native released pages as non-split so they get re-synced
  1319. // at MmX86Fault() time. NOTE: StartVirtual should be aligned on
  1320. // the native page size before executing this code.
  1321. //
  1322. if (BYTE_OFFSET (OriginalStartVa) != 0) {
  1323. if (MiArePreceding4kPagesAllocated (OriginalStartVa) != FALSE) {
  1324. StartVirtual = PAGE_ALIGN ((ULONG_PTR)StartVirtual + PAGE_SIZE);
  1325. }
  1326. }
  1327. EndVirtual = (PVOID) ((ULONG_PTR)EndVirtual | (PAGE_SIZE - 1));
  1328. if (BYTE_OFFSET (OriginalEndVa) != (PAGE_SIZE - 1)) {
  1329. if (MiAreFollowing4kPagesAllocated (OriginalEndVa) != FALSE) {
  1330. EndVirtual = (PVOID) ((ULONG_PTR)EndVirtual - PAGE_SIZE);
  1331. }
  1332. }
  1333. if (StartVirtual < EndVirtual) {
  1334. //
  1335. // Initialize the bitmap inline for speed.
  1336. //
  1337. BitMap.SizeOfBitMap = (ULONG)((ULONG_PTR)MmWorkingSetList->HighestUserAddress >> PTI_SHIFT);
  1338. BitMap.Buffer = Wow64Process->AltPermBitmap;
  1339. RtlClearBits (&BitMap,
  1340. (ULONG) MI_VA_TO_VPN (StartVirtual),
  1341. (ULONG) (MI_VA_TO_VPN (EndVirtual) - MI_VA_TO_VPN (StartVirtual) + 1));
  1342. }
  1343. UNLOCK_ALTERNATE_TABLE_UNSAFE (Wow64Process);
  1344. }
  1345. VOID
  1346. MiDecommitFor4kPage (
  1347. IN PVOID StartVirtual,
  1348. IN PVOID EndVirtual,
  1349. IN PEPROCESS Process
  1350. )
  1351. /*++
  1352. Routine Description:
  1353. This function decommits a region of pages within the virtual address
  1354. space of a subject process.
  1355. Arguments:
  1356. StartVirtual - Supplies the start address of the region of pages
  1357. to be decommitted.
  1358. EndVirtual - Supplies the end address of the region of the pages
  1359. to be decommitted.
  1360. Process - Supplies a pointer to the process in which to decommit a
  1361. a region of pages.
  1362. Return Value:
  1363. None.
  1364. Environment:
  1365. Address space mutex held at APC_LEVEL.
  1366. --*/
  1367. {
  1368. PMMPTE StartAltPte;
  1369. PMMPTE EndAltPte;
  1370. MMPTE TempAltPte;
  1371. PWOW64_PROCESS Wow64Process;
  1372. Wow64Process = Process->Wow64Process;
  1373. ASSERT (StartVirtual <= EndVirtual);
  1374. StartAltPte = MiGetAltPteAddress (StartVirtual);
  1375. EndAltPte = MiGetAltPteAddress (EndVirtual);
  1376. ASSERT ((ULONG) ADDRESS_AND_SIZE_TO_SPAN_PAGES (StartVirtual,
  1377. (ULONG_PTR)EndVirtual -
  1378. (ULONG_PTR)StartVirtual) != 0);
  1379. LOCK_ALTERNATE_TABLE_UNSAFE (Wow64Process);
  1380. while (StartAltPte <= EndAltPte) {
  1381. TempAltPte.u.Long = StartAltPte->u.Long;
  1382. TempAltPte.u.Alt.Commit = 0;
  1383. TempAltPte.u.Alt.Accessed = 0;
  1384. TempAltPte.u.Alt.FillZero = 1;
  1385. //
  1386. // Atomic PTE update.
  1387. //
  1388. MI_WRITE_ALTPTE (StartAltPte, TempAltPte, 0x11);
  1389. StartAltPte += 1;
  1390. }
  1391. //
  1392. // Update the native PTEs and flush the relevant native TB entries.
  1393. //
  1394. MiResetAccessBitForNativePtes (StartVirtual, EndVirtual, Process);
  1395. UNLOCK_ALTERNATE_TABLE_UNSAFE (Wow64Process);
  1396. }
  1397. VOID
  1398. MiDeleteFor4kPage (
  1399. IN PVOID VirtualAddress,
  1400. IN PVOID EndVirtual,
  1401. IN PEPROCESS Process
  1402. )
  1403. /*++
  1404. Routine Description:
  1405. This function deletes a region of pages within the virtual address
  1406. space of the subject process.
  1407. Arguments:
  1408. VirtualAddress - Supplies the start address of the region of pages
  1409. to be deleted.
  1410. EndVirtual - Supplies the end address of the region of pages
  1411. to be deleted.
  1412. Process - Supplies a pointer to the process in which to delete
  1413. the region of pages.
  1414. Return Value:
  1415. None.
  1416. Environment:
  1417. Kernel mode. Address creation mutex held at APC_LEVEL.
  1418. --*/
  1419. {
  1420. RTL_BITMAP BitMap;
  1421. PMMPTE EndAltPte;
  1422. PMMPTE StartAltPte;
  1423. PWOW64_PROCESS Wow64Process;
  1424. PFN_NUMBER NumberOfAltPtes;
  1425. ULONG Vpn;
  1426. ULONG VpnRange;
  1427. ASSERT (VirtualAddress <= EndVirtual);
  1428. StartAltPte = MiGetAltPteAddress (VirtualAddress);
  1429. EndAltPte = MiGetAltPteAddress (EndVirtual);
  1430. NumberOfAltPtes = EndAltPte - StartAltPte + 1;
  1431. ASSERT (ADDRESS_AND_SIZE_TO_SPAN_PAGES (VirtualAddress,
  1432. (ULONG_PTR)EndVirtual -
  1433. (ULONG_PTR)VirtualAddress) != 0);
  1434. Wow64Process = Process->Wow64Process;
  1435. Vpn = (ULONG) MI_VA_TO_VPN (VirtualAddress);
  1436. VpnRange = (ULONG) (MI_VA_TO_VPN (EndVirtual) - Vpn + 1);
  1437. //
  1438. // Initialize the bitmap inline for speed.
  1439. //
  1440. BitMap.SizeOfBitMap = (ULONG)((ULONG_PTR)MmWorkingSetList->HighestUserAddress >> PTI_SHIFT);
  1441. BitMap.Buffer = Wow64Process->AltPermBitmap;
  1442. LOCK_ALTERNATE_TABLE_UNSAFE (Wow64Process);
  1443. do {
  1444. MI_WRITE_ALTPTE (StartAltPte, ZeroPte, 0x12);
  1445. NumberOfAltPtes -= 1;
  1446. StartAltPte += 1;
  1447. } while (NumberOfAltPtes != 0);
  1448. RtlClearBits (&BitMap, Vpn, VpnRange);
  1449. //
  1450. // VirtualAddress and EndVirtual are already aligned to the native
  1451. // PAGE_SIZE so no need to readjust them before removing the split markers.
  1452. //
  1453. MiResetAccessBitForNativePtes (VirtualAddress, EndVirtual, Process);
  1454. UNLOCK_ALTERNATE_TABLE_UNSAFE (Wow64Process);
  1455. }
  1456. LOGICAL
  1457. MiArePreceding4kPagesAllocated (
  1458. IN PVOID VirtualAddress
  1459. )
  1460. /*++
  1461. Routine Description:
  1462. This function checks to see if the specified virtual address contains any
  1463. preceding 4k allocations within the native page.
  1464. Arguments:
  1465. VirtualAddress - Supplies the virtual address to check.
  1466. Return Value:
  1467. TRUE if the address has preceding 4k pages, FALSE if not.
  1468. Environment:
  1469. Kernel mode, address creation mutex held, APCs disabled.
  1470. --*/
  1471. {
  1472. PMMPTE AltPte;
  1473. PMMPTE AltPteEnd;
  1474. ASSERT (BYTE_OFFSET (VirtualAddress) != 0);
  1475. AltPte = MiGetAltPteAddress (PAGE_ALIGN(VirtualAddress));
  1476. AltPteEnd = MiGetAltPteAddress (VirtualAddress);
  1477. //
  1478. // No need to hold the AltPte mutex as the address space mutex
  1479. // is held which prevents allocation or deletion of the AltPte entries
  1480. // inside the table.
  1481. //
  1482. while (AltPte != AltPteEnd) {
  1483. if ((AltPte->u.Long == 0) ||
  1484. ((AltPte->u.Alt.NoAccess == 1) && (AltPte->u.Alt.Protection != MM_NOACCESS))) {
  1485. //
  1486. // The page's alternate PTE hasn't been allocated yet to the process
  1487. // or it's marked no access.
  1488. //
  1489. NOTHING;
  1490. }
  1491. else {
  1492. return TRUE;
  1493. }
  1494. AltPte += 1;
  1495. }
  1496. return FALSE;
  1497. }
  1498. LOGICAL
  1499. MiAreFollowing4kPagesAllocated (
  1500. IN PVOID VirtualAddress
  1501. )
  1502. /*++
  1503. Routine Description:
  1504. This function checks to see if the specified virtual address contains any
  1505. following 4k allocations within the native page.
  1506. Arguments:
  1507. VirtualAddress - Supplies the virtual address to check.
  1508. Return Value:
  1509. TRUE if the address has following 4k pages, FALSE if not.
  1510. Environment:
  1511. Kernel mode, address creation mutex held, APCs disabled.
  1512. --*/
  1513. {
  1514. PMMPTE AltPte;
  1515. PMMPTE AltPteEnd;
  1516. ASSERT (BYTE_OFFSET (VirtualAddress) != 0);
  1517. AltPteEnd = MiGetAltPteAddress (PAGE_ALIGN ((ULONG_PTR)VirtualAddress + PAGE_SIZE));
  1518. AltPte = MiGetAltPteAddress (VirtualAddress) + 1;
  1519. ASSERT (AltPte < AltPteEnd);
  1520. //
  1521. // No need to hold the AltPte mutex as the address space mutex
  1522. // is held which prevents allocation or deletion of the AltPte entries
  1523. // inside the table.
  1524. //
  1525. while (AltPte != AltPteEnd) {
  1526. if ((AltPte->u.Long == 0) ||
  1527. ((AltPte->u.Alt.NoAccess == 1) && (AltPte->u.Alt.Protection != MM_NOACCESS))) {
  1528. //
  1529. // The page's alternate PTE hasn't been allocated yet to the process
  1530. // or it's marked no access.
  1531. //
  1532. NOTHING;
  1533. }
  1534. else {
  1535. return TRUE;
  1536. }
  1537. AltPte += 1;
  1538. }
  1539. return FALSE;
  1540. }
  1541. VOID
  1542. MiResetAccessBitForNativePtes (
  1543. IN PVOID VirtualAddress,
  1544. IN PVOID EndVirtual,
  1545. IN PEPROCESS Process
  1546. )
  1547. /*++
  1548. Routine Description:
  1549. This function resets the access bit of the native PTEs which have
  1550. corresponding initialized alternate PTEs.
  1551. This results in the next access to these VAs incurring a TB miss.
  1552. The miss will then be processed by the 4k TB miss handler (if the
  1553. cache reserved bit is set in the native PTE) and either handled
  1554. inline (if the alternate PTE allows it) or via a call to MmX86Fault.
  1555. If the cache reserved bit is *NOT* set in the native PTE (ie: the
  1556. native page does not contain split permissions), then the access
  1557. will still go to KiPageFault and then to MmX86Fault and then to
  1558. MmAccessFault for general processing to set the access bit.
  1559. Arguments:
  1560. VirtualAddress - Supplies the start address of the region of pages
  1561. to be inspected.
  1562. EndVirtual - Supplies the end address of the region of the pages
  1563. to be inspected.
  1564. Process - Supplies the pointer to the process.
  1565. Return Value:
  1566. None.
  1567. Environment:
  1568. Alternate table mutex held at APC_LEVEL.
  1569. --*/
  1570. {
  1571. ULONG NumberOfPtes;
  1572. MMPTE PteContents;
  1573. PMMPTE PointerPte;
  1574. PMMPTE PointerPde;
  1575. PMMPTE PointerPpe;
  1576. LOGICAL FirstTime;
  1577. ULONG Waited;
  1578. PULONG Bitmap;
  1579. PVOID Virtual[MM_MAXIMUM_FLUSH_COUNT];
  1580. NumberOfPtes = 0;
  1581. Bitmap = Process->Wow64Process->AltPermBitmap;
  1582. VirtualAddress = PAGE_ALIGN (VirtualAddress);
  1583. PointerPte = MiGetPteAddress (VirtualAddress);
  1584. FirstTime = TRUE;
  1585. LOCK_WS_UNSAFE (Process);
  1586. while (VirtualAddress <= EndVirtual) {
  1587. if ((FirstTime == TRUE) || MiIsPteOnPdeBoundary (PointerPte)) {
  1588. PointerPde = MiGetPteAddress (PointerPte);
  1589. PointerPpe = MiGetPdeAddress (PointerPte);
  1590. if (MiDoesPpeExistAndMakeValid (PointerPpe,
  1591. Process,
  1592. MM_NOIRQL,
  1593. &Waited) == FALSE) {
  1594. //
  1595. // This page directory parent entry is empty,
  1596. // go to the next one.
  1597. //
  1598. PointerPpe += 1;
  1599. PointerPde = MiGetVirtualAddressMappedByPte (PointerPpe);
  1600. PointerPte = MiGetVirtualAddressMappedByPte (PointerPde);
  1601. VirtualAddress = MiGetVirtualAddressMappedByPte (PointerPte);
  1602. continue;
  1603. }
  1604. if (MiDoesPdeExistAndMakeValid (PointerPde,
  1605. Process,
  1606. MM_NOIRQL,
  1607. &Waited) == FALSE) {
  1608. //
  1609. // This page directory entry is empty,
  1610. // go to the next one.
  1611. //
  1612. PointerPde += 1;
  1613. PointerPte = MiGetVirtualAddressMappedByPte(PointerPde);
  1614. VirtualAddress = MiGetVirtualAddressMappedByPte(PointerPte);
  1615. continue;
  1616. }
  1617. FirstTime = FALSE;
  1618. }
  1619. PteContents = *PointerPte;
  1620. if (PteContents.u.Hard.Valid != 0) {
  1621. if ((PteContents.u.Hard.Accessed != 0) &&
  1622. (MI_CHECK_BIT (Bitmap, MI_VA_TO_VPN (VirtualAddress)))) {
  1623. PteContents.u.Hard.Accessed = 0;
  1624. MI_WRITE_VALID_PTE_NEW_PROTECTION (PointerPte, PteContents);
  1625. }
  1626. //
  1627. // Flush this valid native TB entry. Note this is done even if no
  1628. // changes were made to it above because our caller may not
  1629. // have flushed the native PTE if the starting 4k address was
  1630. // preceded by another valid 4k page within the same native page,
  1631. // or if the ending 4k address was followed by another valid 4k
  1632. // page within the same native page.
  1633. //
  1634. if (NumberOfPtes < MM_MAXIMUM_FLUSH_COUNT) {
  1635. Virtual[NumberOfPtes] = VirtualAddress;
  1636. NumberOfPtes += 1;
  1637. }
  1638. }
  1639. PointerPte += 1;
  1640. VirtualAddress = (PVOID)((ULONG_PTR)VirtualAddress + PAGE_SIZE);
  1641. }
  1642. UNLOCK_WS_UNSAFE (Process);
  1643. if (NumberOfPtes == 0) {
  1644. NOTHING;
  1645. }
  1646. else if (NumberOfPtes < MM_MAXIMUM_FLUSH_COUNT) {
  1647. KeFlushMultipleTb (NumberOfPtes, &Virtual[0], FALSE);
  1648. }
  1649. else {
  1650. KeFlushProcessTb (FALSE);
  1651. }
  1652. return;
  1653. }
  1654. VOID
  1655. MiQueryRegionFor4kPage (
  1656. IN PVOID BaseAddress,
  1657. IN PVOID EndAddress,
  1658. IN OUT PSIZE_T RegionSize,
  1659. IN OUT PULONG RegionState,
  1660. IN OUT PULONG RegionProtect,
  1661. IN PEPROCESS Process
  1662. )
  1663. /*++
  1664. Routine Description:
  1665. This function checks the size of the region which has the same memory
  1666. state.
  1667. Arguments:
  1668. BaseAddress - Supplies the base address of the region of pages
  1669. to be queried.
  1670. EndAddress - Supplies the end of address of the region of pages
  1671. to be queried.
  1672. RegionSize - Supplies the original region size. Returns a region
  1673. size for 4k pages if different.
  1674. RegionState - Supplies the original region state. Returns a region
  1675. state for 4k pages if different.
  1676. RegionProtect - Supplies the original protection. Returns a protection
  1677. for 4k pages if different.
  1678. Process - Supplies a pointer to the process to be queried.
  1679. Return Value:
  1680. Returns the size of the region.
  1681. Environment:
  1682. Kernel mode. Address creation mutex held at APC_LEVEL.
  1683. --*/
  1684. {
  1685. PMMPTE AltPte;
  1686. PMMPTE LastAltPte;
  1687. MMPTE AltContents;
  1688. PWOW64_PROCESS Wow64Process;
  1689. SIZE_T RegionSize4k;
  1690. //
  1691. // If above the Wow64 max address, just return.
  1692. //
  1693. if ((BaseAddress >= MmWorkingSetList->HighestUserAddress) ||
  1694. (EndAddress >= MmWorkingSetList->HighestUserAddress)) {
  1695. return;
  1696. }
  1697. AltPte = MiGetAltPteAddress (BaseAddress);
  1698. LastAltPte = MiGetAltPteAddress (EndAddress);
  1699. Wow64Process = Process->Wow64Process;
  1700. LOCK_ALTERNATE_TABLE_UNSAFE (Wow64Process);
  1701. if (MI_CHECK_BIT (Wow64Process->AltPermBitmap,
  1702. MI_VA_TO_VPN(BaseAddress)) == 0) {
  1703. UNLOCK_ALTERNATE_TABLE_UNSAFE (Wow64Process);
  1704. return;
  1705. }
  1706. AltContents.u.Long = AltPte->u.Long;
  1707. if (AltContents.u.Long == 0) {
  1708. UNLOCK_ALTERNATE_TABLE_UNSAFE (Wow64Process);
  1709. return;
  1710. }
  1711. *RegionProtect = MI_CONVERT_FROM_PTE_PROTECTION(AltContents.u.Alt.Protection);
  1712. if (AltContents.u.Alt.Commit != 0) {
  1713. *RegionState = MEM_COMMIT;
  1714. }
  1715. else {
  1716. if ((AltPte->u.Long == 0) ||
  1717. ((AltPte->u.Alt.NoAccess == 1) && (AltPte->u.Alt.Protection != MM_NOACCESS))) {
  1718. *RegionState = MEM_FREE;
  1719. *RegionProtect = PAGE_NOACCESS;
  1720. }
  1721. else {
  1722. *RegionState = MEM_RESERVE;
  1723. *RegionProtect = 0;
  1724. }
  1725. }
  1726. AltPte += 1;
  1727. RegionSize4k = PAGE_4K;
  1728. while (AltPte <= LastAltPte) {
  1729. if ((AltPte->u.Alt.Protection != AltContents.u.Alt.Protection) ||
  1730. (AltPte->u.Alt.Commit != AltContents.u.Alt.Commit)) {
  1731. //
  1732. // The state for this address does not match, bail.
  1733. //
  1734. break;
  1735. }
  1736. RegionSize4k += PAGE_4K;
  1737. AltPte += 1;
  1738. }
  1739. UNLOCK_ALTERNATE_TABLE_UNSAFE (Wow64Process);
  1740. *RegionSize = RegionSize4k;
  1741. }
  1742. ULONG
  1743. MiQueryProtectionFor4kPage (
  1744. IN PVOID BaseAddress,
  1745. IN PEPROCESS Process
  1746. )
  1747. /*++
  1748. Routine Description:
  1749. This function queries the protection for a specified 4k page.
  1750. Arguments:
  1751. BaseAddress - Supplies a base address of the 4k page.
  1752. Process - Supplies a pointer to the relevant process.
  1753. Return Value:
  1754. Returns the protection of the 4k page.
  1755. Environment:
  1756. Kernel mode. Address creation mutex held at APC_LEVEL.
  1757. --*/
  1758. {
  1759. ULONG Protection;
  1760. PMMPTE PointerAltPte;
  1761. PWOW64_PROCESS Wow64Process;
  1762. Wow64Process = Process->Wow64Process;
  1763. PointerAltPte = MiGetAltPteAddress (BaseAddress);
  1764. Protection = 0;
  1765. LOCK_ALTERNATE_TABLE_UNSAFE (Wow64Process);
  1766. if (MI_CHECK_BIT(Wow64Process->AltPermBitmap,
  1767. MI_VA_TO_VPN(BaseAddress)) != 0) {
  1768. Protection = (ULONG)PointerAltPte->u.Alt.Protection;
  1769. }
  1770. UNLOCK_ALTERNATE_TABLE_UNSAFE (Wow64Process);
  1771. return Protection;
  1772. }
  1773. //
  1774. // Note 1 is added to the charge to account for the page table page.
  1775. //
  1776. #define MI_ALTERNATE_PAGE_TABLE_CHARGE(HighestUserAddress) \
  1777. ((((((ULONG_PTR)HighestUserAddress) >> PAGE_4K_SHIFT) * sizeof (MMPTE)) >> PAGE_SHIFT) + 1)
  1778. NTSTATUS
  1779. MiInitializeAlternateTable (
  1780. IN PEPROCESS Process,
  1781. IN PVOID HighestUserAddress
  1782. )
  1783. /*++
  1784. Routine Description:
  1785. This function initializes the alternate table for the specified process.
  1786. Arguments:
  1787. Process - Supplies a pointer to the process to initialize the alternate
  1788. table for.
  1789. HighestUserAddress - Supplies the highest 32-bit user address for this
  1790. process.
  1791. Return Value:
  1792. NTSTATUS.
  1793. Environment:
  1794. --*/
  1795. {
  1796. PULONG AltTablePointer;
  1797. PWOW64_PROCESS Wow64Process;
  1798. SIZE_T AltPteCharge;
  1799. SIZE_T NumberOfBytes;
  1800. //
  1801. // Charge commitment now for the alternate PTE table pages as they will
  1802. // need to be dynamically created later at fault time.
  1803. //
  1804. // Add X64K to include alternate PTEs for the guard region.
  1805. //
  1806. HighestUserAddress = (PVOID)((PCHAR)HighestUserAddress + X64K);
  1807. AltPteCharge = MI_ALTERNATE_PAGE_TABLE_CHARGE (HighestUserAddress);
  1808. if (MiChargeCommitment (AltPteCharge, NULL) == FALSE) {
  1809. return STATUS_COMMITMENT_LIMIT;
  1810. }
  1811. NumberOfBytes = ((ULONG_PTR)HighestUserAddress >> PTI_SHIFT) / 8;
  1812. NumberOfBytes += MI_ALTPTE_TRACKING_BYTES;
  1813. AltTablePointer = (PULONG) ExAllocatePoolWithTag (NonPagedPool,
  1814. NumberOfBytes,
  1815. 'AlmM');
  1816. if (AltTablePointer == NULL) {
  1817. MiReturnCommitment (AltPteCharge);
  1818. return STATUS_NO_MEMORY;
  1819. }
  1820. RtlZeroMemory (AltTablePointer, NumberOfBytes);
  1821. Wow64Process = Process->Wow64Process;
  1822. Wow64Process->AltPermBitmap = AltTablePointer;
  1823. KeInitializeGuardedMutex (&Wow64Process->AlternateTableLock);
  1824. MmWorkingSetList->HighestUserPte = MiGetPteAddress (HighestUserAddress);
  1825. MmWorkingSetList->HighestAltPte = MiGetAltPteAddress (HighestUserAddress);
  1826. return STATUS_SUCCESS;
  1827. }
  1828. VOID
  1829. MiDuplicateAlternateTable (
  1830. IN PEPROCESS CurrentProcess,
  1831. IN PEPROCESS ProcessToInitialize
  1832. )
  1833. /*++
  1834. Routine Description:
  1835. This function duplicates the alternate table bitmap and the alternate PTEs
  1836. themselves for the specified process.
  1837. Arguments:
  1838. Process - Supplies a pointer to the process whose alternate information
  1839. should be copied.
  1840. ProcessToInitialize - Supplies a pointer to the target process who should
  1841. receive the new alternate information.
  1842. Return Value:
  1843. None.
  1844. Environment:
  1845. Kernel mode, APCs disabled, working set and address space mutex
  1846. and ForkInProgress flag held.
  1847. --*/
  1848. {
  1849. PVOID Source;
  1850. KAPC_STATE ApcState;
  1851. PMMPTE PointerPte;
  1852. PMMPTE PointerAltPte;
  1853. PMMPTE PointerPde;
  1854. PVOID Va;
  1855. ULONG i;
  1856. ULONG j;
  1857. ULONG Waited;
  1858. //
  1859. // It's not necessary to acquire the alternate table mutex since both the
  1860. // address space and ForkInProgress resources are held on entry.
  1861. //
  1862. RtlCopyMemory (ProcessToInitialize->Wow64Process->AltPermBitmap,
  1863. CurrentProcess->Wow64Process->AltPermBitmap,
  1864. ((ULONG_PTR)MmWorkingSetList->HighestUserAddress >> PTI_SHIFT)/8);
  1865. //
  1866. // Since the PPE for the Alternate Table is shared with hyperspace,
  1867. // we can assume it is always present without performing
  1868. // MiDoesPpeExistAndMakeValid().
  1869. //
  1870. PointerPde = MiGetPdeAddress (ALT4KB_PERMISSION_TABLE_START);
  1871. PointerPte = MiGetPteAddress (ALT4KB_PERMISSION_TABLE_START);
  1872. Va = ALT4KB_PERMISSION_TABLE_START;
  1873. do {
  1874. if (MiDoesPdeExistAndMakeValid (PointerPde,
  1875. CurrentProcess,
  1876. MM_NOIRQL,
  1877. &Waited) == TRUE) {
  1878. //
  1879. // Duplicate any addresses that exist in the parent, bringing them
  1880. // in from disk or materializing them as necessary. Note the
  1881. // KSEG address is used for each parent address to avoid allocating
  1882. // a system PTE for this mapping as this routine cannot fail (the
  1883. // overall fork is too far along to tolerate a failure).
  1884. //
  1885. for (i = 0; i < PTE_PER_PAGE; i += 1) {
  1886. if (PointerPte->u.Long != 0) {
  1887. if (MiDoesPdeExistAndMakeValid (PointerPte,
  1888. CurrentProcess,
  1889. MM_NOIRQL,
  1890. &Waited) == TRUE) {
  1891. ASSERT (PointerPte->u.Hard.Valid == 1);
  1892. Source = KSEG_ADDRESS (PointerPte->u.Hard.PageFrameNumber);
  1893. KeStackAttachProcess (&ProcessToInitialize->Pcb,
  1894. &ApcState);
  1895. RtlCopyMemory (Va, Source, PAGE_SIZE);
  1896. //
  1897. // Eliminate any bits that should NOT be copied.
  1898. //
  1899. PointerAltPte = (PMMPTE) Va;
  1900. for (j = 0; j < PTE_PER_PAGE; j += 1) {
  1901. if (PointerAltPte->u.Alt.InPageInProgress == 1) {
  1902. PointerAltPte->u.Alt.InPageInProgress = 0;
  1903. }
  1904. PointerAltPte += 1;
  1905. }
  1906. KeUnstackDetachProcess (&ApcState);
  1907. }
  1908. }
  1909. Va = (PVOID)((PCHAR) Va + PAGE_SIZE);
  1910. PointerPte += 1;
  1911. }
  1912. }
  1913. PointerPde += 1;
  1914. PointerPte = MiGetVirtualAddressMappedByPte (PointerPde);
  1915. Va = MiGetVirtualAddressMappedByPte (PointerPte);
  1916. } while (Va < ALT4KB_PERMISSION_TABLE_END);
  1917. //
  1918. // Initialize the child's 32-bit PEB to be the same as the parent's.
  1919. //
  1920. ProcessToInitialize->Wow64Process->Wow64 = CurrentProcess->Wow64Process->Wow64;
  1921. return;
  1922. }
  1923. VOID
  1924. MiDeleteAlternateTable (
  1925. IN PEPROCESS Process
  1926. )
  1927. /*++
  1928. Routine Description:
  1929. This function deletes the alternate table for the specified process.
  1930. Arguments:
  1931. Process - Supplies a pointer to the process to delete the alternate
  1932. table for.
  1933. Return Value:
  1934. None.
  1935. Environment:
  1936. Kernel mode, APCs disabled, working set mutex held.
  1937. --*/
  1938. {
  1939. PVOID HighestUserAddress;
  1940. PMMPTE PointerPte;
  1941. PMMPTE PointerPde;
  1942. PVOID Va;
  1943. PVOID TempVa;
  1944. ULONG i;
  1945. ULONG Waited;
  1946. MMPTE_FLUSH_LIST PteFlushList;
  1947. PWOW64_PROCESS Wow64Process;
  1948. KIRQL OldIrql;
  1949. Wow64Process = Process->Wow64Process;
  1950. if (Wow64Process->AltPermBitmap == NULL) {
  1951. //
  1952. // This is only NULL (and Wow64Process non-NULL) if a memory allocation
  1953. // failed during process creation.
  1954. //
  1955. return;
  1956. }
  1957. //
  1958. // Since the PPE for the Alternate Table is shared with hyperspace,
  1959. // we can assume it is always present without performing
  1960. // MiDoesPpeExistAndMakeValid().
  1961. //
  1962. Va = ALT4KB_PERMISSION_TABLE_START;
  1963. PointerPte = MiGetPteAddress (ALT4KB_PERMISSION_TABLE_START);
  1964. PointerPde = MiGetPdeAddress (ALT4KB_PERMISSION_TABLE_START);
  1965. PteFlushList.Count = 0;
  1966. do {
  1967. if (MiDoesPdeExistAndMakeValid (PointerPde,
  1968. Process,
  1969. MM_NOIRQL,
  1970. &Waited) == TRUE) {
  1971. //
  1972. // Delete the PTE entries mapping the Alternate Table.
  1973. //
  1974. TempVa = Va;
  1975. LOCK_PFN (OldIrql);
  1976. for (i = 0; i < PTE_PER_PAGE; i += 1) {
  1977. if (PointerPte->u.Long != 0) {
  1978. if (IS_PTE_NOT_DEMAND_ZERO (*PointerPte)) {
  1979. MiDeletePte (PointerPte,
  1980. TempVa,
  1981. TRUE,
  1982. Process,
  1983. NULL,
  1984. &PteFlushList,
  1985. OldIrql);
  1986. }
  1987. else {
  1988. MI_WRITE_INVALID_PTE (PointerPte, ZeroPte);
  1989. }
  1990. }
  1991. TempVa = (PVOID)((PCHAR)TempVa + PAGE_SIZE);
  1992. PointerPte += 1;
  1993. }
  1994. //
  1995. // Delete the PDE entry mapping the Alternate Table.
  1996. //
  1997. TempVa = MiGetVirtualAddressMappedByPte (PointerPde);
  1998. MiDeletePte (PointerPde,
  1999. TempVa,
  2000. TRUE,
  2001. Process,
  2002. NULL,
  2003. &PteFlushList,
  2004. OldIrql);
  2005. MiFlushPteList (&PteFlushList, FALSE);
  2006. UNLOCK_PFN (OldIrql);
  2007. }
  2008. PointerPde += 1;
  2009. PointerPte = MiGetVirtualAddressMappedByPte (PointerPde);
  2010. Va = MiGetVirtualAddressMappedByPte (PointerPte);
  2011. } while (Va < ALT4KB_PERMISSION_TABLE_END);
  2012. HighestUserAddress = MmWorkingSetList->HighestUserAddress;
  2013. ASSERT (HighestUserAddress != NULL);
  2014. //
  2015. // Add X64K to include alternate PTEs for the guard region.
  2016. //
  2017. HighestUserAddress = (PVOID)((PCHAR)HighestUserAddress + X64K);
  2018. MiReturnCommitment (MI_ALTERNATE_PAGE_TABLE_CHARGE (HighestUserAddress));
  2019. ExFreePool (Wow64Process->AltPermBitmap);
  2020. Wow64Process->AltPermBitmap = NULL;
  2021. return;
  2022. }
  2023. VOID
  2024. MiRemoveAliasedVadsApc (
  2025. IN PKAPC Apc,
  2026. OUT PKNORMAL_ROUTINE *NormalRoutine,
  2027. IN OUT PVOID NormalContext,
  2028. IN OUT PVOID *SystemArgument1,
  2029. IN OUT PVOID *SystemArgument2
  2030. )
  2031. {
  2032. ULONG i;
  2033. PALIAS_VAD_INFO2 AliasBase;
  2034. PEPROCESS Process;
  2035. PALIAS_VAD_INFO AliasInformation;
  2036. UNREFERENCED_PARAMETER (Apc);
  2037. UNREFERENCED_PARAMETER (NormalContext);
  2038. UNREFERENCED_PARAMETER (SystemArgument2);
  2039. Process = PsGetCurrentProcess ();
  2040. AliasInformation = (PALIAS_VAD_INFO) *SystemArgument1;
  2041. AliasBase = (PALIAS_VAD_INFO2)(AliasInformation + 1);
  2042. LOCK_ADDRESS_SPACE (Process);
  2043. for (i = 0; i < AliasInformation->NumberOfEntries; i += 1) {
  2044. ASSERT (AliasBase->BaseAddress < _2gb);
  2045. MiUnsecureVirtualMemory (AliasBase->SecureHandle, TRUE);
  2046. MiUnmapViewOfSection (Process,
  2047. (PVOID) (ULONG_PTR)AliasBase->BaseAddress,
  2048. TRUE);
  2049. AliasBase += 1;
  2050. }
  2051. UNLOCK_ADDRESS_SPACE (Process);
  2052. ExFreePool (AliasInformation);
  2053. //
  2054. // Clear the normal routine so this routine doesn't get called again
  2055. // for the same request.
  2056. //
  2057. *NormalRoutine = NULL;
  2058. }
  2059. VOID
  2060. MiRemoveAliasedVads (
  2061. IN PEPROCESS Process,
  2062. IN PMMVAD Vad
  2063. )
  2064. /*++
  2065. Routine Description:
  2066. This function removes all aliased VADs spawned earlier from the
  2067. argument VAD.
  2068. Arguments:
  2069. Process - Supplies the EPROCESS pointer to the current process.
  2070. Vad - Supplies a pointer to the VAD describing the range being removed.
  2071. Return Value:
  2072. None.
  2073. Environment:
  2074. Kernel mode, address creation and working set mutexes held, APCs disabled.
  2075. --*/
  2076. {
  2077. PALIAS_VAD_INFO AliasInformation;
  2078. ASSERT (Process->Wow64Process != NULL);
  2079. AliasInformation = ((PMMVAD_LONG)Vad)->AliasInformation;
  2080. ASSERT (AliasInformation != NULL);
  2081. if ((Process->Flags & PS_PROCESS_FLAGS_VM_DELETED) == 0) {
  2082. //
  2083. // This process is still alive so queue an APC to delete each aliased
  2084. // VAD. This is because the deletion must also get rid of page table
  2085. // commitment which requires that it search (and modify) VAD trees,
  2086. // etc - but the address space mutex is already held and the caller
  2087. // is not generally prepared for all this to change at this point.
  2088. //
  2089. KeInitializeApc (&AliasInformation->Apc,
  2090. &PsGetCurrentThread()->Tcb,
  2091. OriginalApcEnvironment,
  2092. (PKKERNEL_ROUTINE) MiRemoveAliasedVadsApc,
  2093. NULL,
  2094. (PKNORMAL_ROUTINE) MiRemoveAliasedVadsApc,
  2095. KernelMode,
  2096. (PVOID) AliasInformation);
  2097. KeInsertQueueApc (&AliasInformation->Apc, AliasInformation, NULL, 0);
  2098. }
  2099. else {
  2100. //
  2101. // This process is exiting so all the VADs are being rundown anyway.
  2102. // Just free the pool and let normal rundown handle the aliases.
  2103. //
  2104. ExFreePool (AliasInformation);
  2105. }
  2106. }
  2107. PVOID
  2108. MiDuplicateAliasVadList (
  2109. IN PMMVAD Vad
  2110. )
  2111. {
  2112. SIZE_T AliasInfoSize;
  2113. PALIAS_VAD_INFO AliasInfo;
  2114. PALIAS_VAD_INFO NewAliasInfo;
  2115. AliasInfo = ((PMMVAD_LONG)Vad)->AliasInformation;
  2116. ASSERT (AliasInfo != NULL);
  2117. AliasInfoSize = sizeof (ALIAS_VAD_INFO) + AliasInfo->MaximumEntries * sizeof (ALIAS_VAD_INFO2);
  2118. NewAliasInfo = ExAllocatePoolWithTag (NonPagedPool,
  2119. AliasInfoSize,
  2120. 'AdaV');
  2121. if (NewAliasInfo != NULL) {
  2122. RtlCopyMemory (NewAliasInfo, AliasInfo, AliasInfoSize);
  2123. }
  2124. return NewAliasInfo;
  2125. }
  2126. #define ALIAS_VAD_INCREMENT 4
  2127. NTSTATUS
  2128. MiSetCopyPagesFor4kPage (
  2129. IN PEPROCESS Process,
  2130. IN PMMVAD Vad,
  2131. IN OUT PVOID StartingAddress,
  2132. IN OUT PVOID EndingAddress,
  2133. IN ULONG NewProtection,
  2134. OUT PMMVAD *CallerNewVad
  2135. )
  2136. /*++
  2137. Routine Description:
  2138. This function creates another map for the existing mapped view space
  2139. and gives it copy-on-write protection. This is called when
  2140. SetProtectionOnSection() tries to change the protection from
  2141. non-copy-on-write to copy-on-write. Since a large native page cannot be
  2142. split to shared and copy-on-write 4kb pages, references to the
  2143. copy-on-write page(s) need to be fixed to reference the
  2144. new mapped view space and this is done through the smart TB handler
  2145. and the alternate page table entries.
  2146. Arguments:
  2147. Process - Supplies the EPROCESS pointer to the current process.
  2148. Vad - Supplies a pointer to the VAD describing the range to protect.
  2149. StartingAddress - Supplies a pointer to the starting address to protect.
  2150. EndingAddress - Supplies a pointer to the ending address to the protect.
  2151. NewProtect - Supplies the new protection to set.
  2152. CallerNewVad - Returns the new VAD the caller should use for this range.
  2153. Return Value:
  2154. NTSTATUS.
  2155. Environment:
  2156. Kernel mode, address creation mutex held, APCs disabled.
  2157. --*/
  2158. {
  2159. ULONG_PTR Vpn;
  2160. PALIAS_VAD_INFO2 AliasBase;
  2161. HANDLE Handle;
  2162. PMMVAD VadParent;
  2163. PMMVAD_LONG NewVad;
  2164. SIZE_T AliasInfoSize;
  2165. PALIAS_VAD_INFO AliasInfo;
  2166. PALIAS_VAD_INFO NewAliasInfo;
  2167. LARGE_INTEGER SectionOffset;
  2168. SIZE_T CapturedViewSize;
  2169. PVOID CapturedBase;
  2170. PVOID Va;
  2171. PVOID VaEnd;
  2172. PVOID Alias;
  2173. PMMPTE PointerPte;
  2174. PMMPTE AltPte;
  2175. MMPTE AltPteContents;
  2176. LOGICAL AliasReferenced;
  2177. SECTION Section;
  2178. PCONTROL_AREA ControlArea;
  2179. NTSTATUS status;
  2180. PWOW64_PROCESS Wow64Process;
  2181. ULONGLONG ProtectionMask;
  2182. ULONGLONG ProtectionMaskNotCopy;
  2183. ULONG NewProtectNotCopy;
  2184. AliasReferenced = FALSE;
  2185. StartingAddress = PAGE_ALIGN(StartingAddress);
  2186. EndingAddress = (PVOID)((ULONG_PTR)PAGE_ALIGN(EndingAddress) + PAGE_SIZE - 1);
  2187. SectionOffset.QuadPart = (ULONG_PTR)MI_64K_ALIGN((ULONG_PTR)StartingAddress -
  2188. (ULONG_PTR)(Vad->StartingVpn << PAGE_SHIFT));
  2189. CapturedBase = NULL;
  2190. Va = MI_VPN_TO_VA (Vad->StartingVpn);
  2191. VaEnd = MI_VPN_TO_VA_ENDING (Vad->EndingVpn);
  2192. CapturedViewSize = (ULONG_PTR)VaEnd - (ULONG_PTR)Va + 1L;
  2193. ControlArea = Vad->ControlArea;
  2194. RtlZeroMemory ((PVOID)&Section, sizeof(Section));
  2195. status = MiMapViewOfDataSection (ControlArea,
  2196. Process,
  2197. &CapturedBase,
  2198. &SectionOffset,
  2199. &CapturedViewSize,
  2200. &Section,
  2201. ViewShare,
  2202. (ULONG)Vad->u.VadFlags.Protection,
  2203. 0,
  2204. 0,
  2205. 0);
  2206. if (!NT_SUCCESS (status)) {
  2207. return status;
  2208. }
  2209. Handle = MiSecureVirtualMemory (CapturedBase,
  2210. CapturedViewSize,
  2211. PAGE_READONLY,
  2212. TRUE);
  2213. if (Handle == NULL) {
  2214. MiUnmapViewOfSection (Process, CapturedBase, TRUE);
  2215. return STATUS_INSUFFICIENT_RESOURCES;
  2216. }
  2217. //
  2218. // If the original VAD is a short or regular VAD, it needs to be
  2219. // reallocated as a large VAD. Note that a short VAD that was
  2220. // previously converted to a long VAD here will still be marked
  2221. // as private memory, thus to handle this case the NoChange bit
  2222. // must also be tested.
  2223. //
  2224. if (((Vad->u.VadFlags.PrivateMemory) && (Vad->u.VadFlags.NoChange == 0))
  2225. ||
  2226. (Vad->u2.VadFlags2.LongVad == 0)) {
  2227. if (Vad->u.VadFlags.PrivateMemory == 0) {
  2228. ASSERT (Vad->u2.VadFlags2.OneSecured == 0);
  2229. ASSERT (Vad->u2.VadFlags2.MultipleSecured == 0);
  2230. }
  2231. AliasInfoSize = sizeof (ALIAS_VAD_INFO) + ALIAS_VAD_INCREMENT * sizeof (ALIAS_VAD_INFO2);
  2232. AliasInfo = ExAllocatePoolWithTag (NonPagedPool,
  2233. AliasInfoSize,
  2234. 'AdaV');
  2235. if (AliasInfo == NULL) {
  2236. MiUnsecureVirtualMemory (Handle, TRUE);
  2237. MiUnmapViewOfSection (Process, CapturedBase, TRUE);
  2238. return STATUS_INSUFFICIENT_RESOURCES;
  2239. }
  2240. AliasInfo->NumberOfEntries = 0;
  2241. AliasInfo->MaximumEntries = ALIAS_VAD_INCREMENT;
  2242. NewVad = ExAllocatePoolWithTag (NonPagedPool,
  2243. sizeof(MMVAD_LONG),
  2244. 'ldaV');
  2245. if (NewVad == NULL) {
  2246. ExFreePool (AliasInfo);
  2247. MiUnsecureVirtualMemory (Handle, TRUE);
  2248. MiUnmapViewOfSection (Process, CapturedBase, TRUE);
  2249. return STATUS_INSUFFICIENT_RESOURCES;
  2250. }
  2251. RtlZeroMemory (NewVad, sizeof(MMVAD_LONG));
  2252. if (Vad->u.VadFlags.PrivateMemory) {
  2253. RtlCopyMemory (NewVad, Vad, sizeof(MMVAD_SHORT));
  2254. }
  2255. else {
  2256. RtlCopyMemory (NewVad, Vad, sizeof(MMVAD));
  2257. }
  2258. NewVad->u2.VadFlags2.LongVad = 1;
  2259. NewVad->AliasInformation = AliasInfo;
  2260. //
  2261. // Replace the current VAD with this expanded VAD.
  2262. //
  2263. LOCK_WS_UNSAFE (Process);
  2264. VadParent = (PMMVAD) SANITIZE_PARENT_NODE (Vad->u1.Parent);
  2265. ASSERT (VadParent != NULL);
  2266. if (VadParent != Vad) {
  2267. if (VadParent->RightChild == Vad) {
  2268. VadParent->RightChild = (PMMVAD) NewVad;
  2269. }
  2270. else {
  2271. ASSERT (VadParent->LeftChild == Vad);
  2272. VadParent->LeftChild = (PMMVAD) NewVad;
  2273. }
  2274. }
  2275. else {
  2276. Process->VadRoot.BalancedRoot.RightChild = (PMMADDRESS_NODE) NewVad;
  2277. }
  2278. if (Vad->LeftChild) {
  2279. Vad->LeftChild->u1.Parent = (PMMVAD) MI_MAKE_PARENT (NewVad, Vad->LeftChild->u1.Balance);
  2280. }
  2281. if (Vad->RightChild) {
  2282. Vad->RightChild->u1.Parent = (PMMVAD) MI_MAKE_PARENT (NewVad, Vad->RightChild->u1.Balance);
  2283. }
  2284. if (Process->VadRoot.NodeHint == Vad) {
  2285. Process->VadRoot.NodeHint = (PMMVAD) NewVad;
  2286. }
  2287. if (Process->VadFreeHint == Vad) {
  2288. Process->VadFreeHint = (PMMVAD) NewVad;
  2289. }
  2290. if ((Vad->u.VadFlags.PhysicalMapping == 1) ||
  2291. (Vad->u.VadFlags.WriteWatch == 1)) {
  2292. MiPhysicalViewAdjuster (Process, Vad, (PMMVAD) NewVad);
  2293. }
  2294. UNLOCK_WS_UNSAFE (Process);
  2295. ExFreePool (Vad);
  2296. Vad = (PMMVAD) NewVad;
  2297. }
  2298. else {
  2299. AliasInfo = (PALIAS_VAD_INFO) ((PMMVAD_LONG)Vad)->AliasInformation;
  2300. if (AliasInfo == NULL) {
  2301. AliasInfoSize = sizeof (ALIAS_VAD_INFO) + ALIAS_VAD_INCREMENT * sizeof (ALIAS_VAD_INFO2);
  2302. }
  2303. else if (AliasInfo->NumberOfEntries >= AliasInfo->MaximumEntries) {
  2304. AliasInfoSize = sizeof (ALIAS_VAD_INFO) + (AliasInfo->MaximumEntries + ALIAS_VAD_INCREMENT) * sizeof (ALIAS_VAD_INFO2);
  2305. }
  2306. else {
  2307. AliasInfoSize = 0;
  2308. }
  2309. if (AliasInfoSize != 0) {
  2310. NewAliasInfo = ExAllocatePoolWithTag (NonPagedPool,
  2311. AliasInfoSize,
  2312. 'AdaV');
  2313. if (NewAliasInfo == NULL) {
  2314. MiUnsecureVirtualMemory (Handle, TRUE);
  2315. MiUnmapViewOfSection (Process, CapturedBase, TRUE);
  2316. return STATUS_INSUFFICIENT_RESOURCES;
  2317. }
  2318. if (AliasInfo != NULL) {
  2319. RtlCopyMemory (NewAliasInfo, AliasInfo, AliasInfoSize - ALIAS_VAD_INCREMENT * sizeof (ALIAS_VAD_INFO2));
  2320. NewAliasInfo->MaximumEntries += ALIAS_VAD_INCREMENT;
  2321. ExFreePool (AliasInfo);
  2322. }
  2323. else {
  2324. NewAliasInfo->NumberOfEntries = 0;
  2325. NewAliasInfo->MaximumEntries = ALIAS_VAD_INCREMENT;
  2326. }
  2327. AliasInfo = NewAliasInfo;
  2328. }
  2329. }
  2330. *CallerNewVad = Vad;
  2331. Va = StartingAddress;
  2332. VaEnd = EndingAddress;
  2333. Alias = (PVOID)((ULONG_PTR)CapturedBase + ((ULONG_PTR)StartingAddress & (X64K - 1)));
  2334. ProtectionMask = MiMakeProtectionAteMask (NewProtection);
  2335. NewProtectNotCopy = NewProtection & ~MM_PROTECTION_COPY_MASK;
  2336. ProtectionMaskNotCopy = MiMakeProtectionAteMask (NewProtectNotCopy);
  2337. Wow64Process = Process->Wow64Process;
  2338. AltPte = MiGetAltPteAddress (Va);
  2339. LOCK_ALTERNATE_TABLE_UNSAFE (Wow64Process);
  2340. while (Va <= VaEnd) {
  2341. //
  2342. // Check to see if the protection is registered in the alternate entry.
  2343. //
  2344. Vpn = (ULONG) MI_VA_TO_VPN (Va);
  2345. if (MI_CHECK_BIT (Wow64Process->AltPermBitmap, Vpn) == 0) {
  2346. MiSyncAltPte (Va);
  2347. }
  2348. PointerPte = MiGetPteAddress (Alias);
  2349. AltPteContents.u.Long = AltPte->u.Long;
  2350. //
  2351. // If this address is NOT copy-on-write, AND it is not already
  2352. // redirected through an indirect entry, then redirect it now to
  2353. // the alias VAD which points at the original section.
  2354. //
  2355. if ((AltPteContents.u.Alt.CopyOnWrite == 0) &&
  2356. (AltPteContents.u.Alt.PteIndirect == 0)) {
  2357. AltPteContents.u.Alt.PteOffset = (ULONG_PTR)PointerPte - PTE_UBASE;
  2358. AltPteContents.u.Alt.PteIndirect = 1;
  2359. MI_WRITE_ALTPTE (AltPte, AltPteContents, 0x13);
  2360. AliasReferenced = TRUE;
  2361. }
  2362. Va = (PVOID)((ULONG_PTR)Va + PAGE_4K);
  2363. Alias = (PVOID)((ULONG_PTR)Alias + PAGE_4K);
  2364. AltPte += 1;
  2365. }
  2366. UNLOCK_ALTERNATE_TABLE_UNSAFE (Wow64Process);
  2367. ASSERT (AliasInfo->NumberOfEntries < AliasInfo->MaximumEntries);
  2368. if (AliasReferenced == TRUE) {
  2369. //
  2370. // The alias view of the shared section was referenced so chain it so
  2371. // the alias view can be :
  2372. //
  2373. // a) easily duplicated if the process subsequently forks.
  2374. //
  2375. // AND
  2376. //
  2377. // b) deleted when/if the original VAD is deleted later.
  2378. //
  2379. AliasBase = (PALIAS_VAD_INFO2)(AliasInfo + 1);
  2380. AliasBase += AliasInfo->NumberOfEntries;
  2381. ASSERT (CapturedBase < (PVOID)(ULONG_PTR)_2gb);
  2382. AliasBase->BaseAddress = (ULONG)(ULONG_PTR)CapturedBase;
  2383. AliasBase->SecureHandle = Handle;
  2384. AliasInfo->NumberOfEntries += 1;
  2385. }
  2386. else {
  2387. //
  2388. // The alias view of the shared section wasn't referenced, delete it.
  2389. //
  2390. MiUnsecureVirtualMemory (Handle, TRUE);
  2391. MiUnmapViewOfSection (Process, CapturedBase, TRUE);
  2392. }
  2393. PS_SET_BITS (&Process->Flags, PS_PROCESS_FLAGS_WOW64_SPLIT_PAGES);
  2394. return STATUS_SUCCESS;
  2395. }
  2396. VOID
  2397. MiLockFor4kPage (
  2398. IN PVOID CapturedBase,
  2399. IN SIZE_T CapturedRegionSize,
  2400. IN PEPROCESS Process
  2401. )
  2402. /*++
  2403. Routine Description:
  2404. This function adds the page locked attribute to the alternate table entries.
  2405. Arguments:
  2406. CapturedBase - Supplies the base address to be locked.
  2407. CapturedRegionSize - Supplies the size of the region to be locked.
  2408. Process - Supplies a pointer to the process object.
  2409. Return Value:
  2410. None.
  2411. Environment:
  2412. Kernel mode, address creation mutex held.
  2413. --*/
  2414. {
  2415. PWOW64_PROCESS Wow64Process;
  2416. PVOID EndingAddress;
  2417. PMMPTE StartAltPte;
  2418. PMMPTE EndAltPte;
  2419. MMPTE AltPteContents;
  2420. Wow64Process = Process->Wow64Process;
  2421. EndingAddress = (PVOID)((ULONG_PTR)CapturedBase + CapturedRegionSize - 1);
  2422. StartAltPte = MiGetAltPteAddress(CapturedBase);
  2423. EndAltPte = MiGetAltPteAddress(EndingAddress);
  2424. LOCK_ALTERNATE_TABLE_UNSAFE (Wow64Process);
  2425. while (StartAltPte <= EndAltPte) {
  2426. AltPteContents = *StartAltPte;
  2427. AltPteContents.u.Alt.Lock = 1;
  2428. MI_WRITE_ALTPTE (StartAltPte, AltPteContents, 0x14);
  2429. StartAltPte += 1;
  2430. }
  2431. UNLOCK_ALTERNATE_TABLE_UNSAFE (Wow64Process);
  2432. }
  2433. NTSTATUS
  2434. MiUnlockFor4kPage (
  2435. IN PVOID CapturedBase,
  2436. IN SIZE_T CapturedRegionSize,
  2437. IN PEPROCESS Process
  2438. )
  2439. /*++
  2440. Routine Description:
  2441. This function removes the page locked attribute from the
  2442. alternate table entries.
  2443. Arguments:
  2444. CapturedBase - Supplies the base address to be unlocked.
  2445. CapturedRegionSize - Supplies the size of the region to be unlocked.
  2446. Process - Supplies a pointer to the process object.
  2447. Return Value:
  2448. NTSTATUS.
  2449. Environment:
  2450. Kernel mode, address creation and working set mutexes held.
  2451. Note this routine releases and reacquires the working set mutex !!!
  2452. --*/
  2453. {
  2454. PMMPTE PointerAltPte;
  2455. PMMPTE StartAltPte;
  2456. PMMPTE EndAltPte;
  2457. PWOW64_PROCESS Wow64Process;
  2458. PVOID EndingAddress;
  2459. NTSTATUS Status;
  2460. MMPTE AltPteContents;
  2461. UNLOCK_WS_UNSAFE (Process);
  2462. Status = STATUS_SUCCESS;
  2463. Wow64Process = Process->Wow64Process;
  2464. EndingAddress = (PVOID)((ULONG_PTR)CapturedBase + CapturedRegionSize - 1);
  2465. StartAltPte = MiGetAltPteAddress (CapturedBase);
  2466. EndAltPte = MiGetAltPteAddress (EndingAddress);
  2467. PointerAltPte = StartAltPte;
  2468. LOCK_ALTERNATE_TABLE_UNSAFE (Wow64Process);
  2469. do {
  2470. if (PointerAltPte->u.Alt.Lock == 0) {
  2471. Status = STATUS_NOT_LOCKED;
  2472. goto StatusReturn;
  2473. }
  2474. PointerAltPte += 1;
  2475. } while (PointerAltPte <= EndAltPte);
  2476. PointerAltPte = StartAltPte;
  2477. do {
  2478. AltPteContents = *PointerAltPte;
  2479. AltPteContents.u.Alt.Lock = 0;
  2480. MI_WRITE_ALTPTE (PointerAltPte, AltPteContents, 0x15);
  2481. PointerAltPte += 1;
  2482. } while (PointerAltPte <= EndAltPte);
  2483. StatusReturn:
  2484. UNLOCK_ALTERNATE_TABLE_UNSAFE (Wow64Process);
  2485. LOCK_WS_UNSAFE (Process);
  2486. return Status;
  2487. }
  2488. LOGICAL
  2489. MiShouldBeUnlockedFor4kPage (
  2490. IN PVOID VirtualAddress,
  2491. IN PEPROCESS Process
  2492. )
  2493. /*++
  2494. Routine Description:
  2495. This function examines whether the page should be unlocked.
  2496. Arguments:
  2497. VirtualAddress - Supplies the virtual address to be examined.
  2498. Process - Supplies a pointer to the process object.
  2499. Return Value:
  2500. None.
  2501. Environment:
  2502. Kernel mode, address creation and working set mutexes held.
  2503. Note this routine releases and reacquires the working set mutex !!!
  2504. --*/
  2505. {
  2506. PMMPTE StartAltPte;
  2507. PMMPTE EndAltPte;
  2508. PWOW64_PROCESS Wow64Process;
  2509. PVOID VirtualAligned;
  2510. PVOID EndingAddress;
  2511. LOGICAL PageUnlocked;
  2512. UNLOCK_WS_UNSAFE (Process);
  2513. PageUnlocked = TRUE;
  2514. Wow64Process = Process->Wow64Process;
  2515. VirtualAligned = PAGE_ALIGN(VirtualAddress);
  2516. EndingAddress = (PVOID)((ULONG_PTR)VirtualAligned + PAGE_SIZE - 1);
  2517. StartAltPte = MiGetAltPteAddress (VirtualAligned);
  2518. EndAltPte = MiGetAltPteAddress (EndingAddress);
  2519. LOCK_ALTERNATE_TABLE_UNSAFE (Wow64Process);
  2520. while (StartAltPte <= EndAltPte) {
  2521. if (StartAltPte->u.Alt.Lock != 0) {
  2522. PageUnlocked = FALSE;
  2523. }
  2524. StartAltPte += 1;
  2525. }
  2526. UNLOCK_ALTERNATE_TABLE_UNSAFE (Wow64Process);
  2527. LOCK_WS_UNSAFE (Process);
  2528. return PageUnlocked;
  2529. }
  2530. ULONG
  2531. MiMakeProtectForNativePage (
  2532. IN PVOID VirtualAddress,
  2533. IN ULONG NewProtect,
  2534. IN PEPROCESS Process
  2535. )
  2536. /*++
  2537. Routine Description:
  2538. This function makes a page protection mask for native pages.
  2539. Arguments:
  2540. VirtualAddress - Supplies the virtual address for the protection mask.
  2541. NewProtect - Supplies the original protection.
  2542. Process - Supplies a pointer to the process object.
  2543. Return Value:
  2544. None.
  2545. Environment:
  2546. Kernel mode.
  2547. --*/
  2548. {
  2549. PWOW64_PROCESS Wow64Process;
  2550. Wow64Process = Process->Wow64Process;
  2551. if (MI_CHECK_BIT(Wow64Process->AltPermBitmap,
  2552. MI_VA_TO_VPN(VirtualAddress)) != 0) {
  2553. if (NewProtect & PAGE_NOACCESS) {
  2554. NewProtect &= ~PAGE_NOACCESS;
  2555. NewProtect |= PAGE_EXECUTE_READWRITE;
  2556. }
  2557. if (NewProtect & PAGE_READONLY) {
  2558. NewProtect &= ~PAGE_READONLY;
  2559. NewProtect |= PAGE_EXECUTE_READWRITE;
  2560. }
  2561. if (NewProtect & PAGE_EXECUTE) {
  2562. NewProtect &= ~PAGE_EXECUTE;
  2563. NewProtect |= PAGE_EXECUTE_READWRITE;
  2564. }
  2565. if (NewProtect & PAGE_EXECUTE_READ) {
  2566. NewProtect &= ~PAGE_EXECUTE_READ;
  2567. NewProtect |= PAGE_EXECUTE_READWRITE;
  2568. }
  2569. //
  2570. // Remove PAGE_GUARD as it is emulated by the Alternate Table.
  2571. //
  2572. if (NewProtect & PAGE_GUARD) {
  2573. NewProtect &= ~PAGE_GUARD;
  2574. }
  2575. }
  2576. return NewProtect;
  2577. }
  2578. VOID
  2579. MiSetNativePteProtection (
  2580. PVOID VirtualAddress,
  2581. ULONGLONG NewPteProtection,
  2582. LOGICAL PageIsSplit,
  2583. PEPROCESS CurrentProcess
  2584. )
  2585. {
  2586. MMPTE PteContents;
  2587. MMPTE TempPte;
  2588. PMMPTE PointerPte;
  2589. PMMPTE PointerPde;
  2590. PMMPTE PointerPpe;
  2591. ULONG Waited;
  2592. PointerPte = MiGetPteAddress (VirtualAddress);
  2593. PointerPde = MiGetPdeAddress (VirtualAddress);
  2594. PointerPpe = MiGetPpeAddress (VirtualAddress);
  2595. //
  2596. // Block APCs and acquire the working set lock.
  2597. //
  2598. LOCK_WS (CurrentProcess);
  2599. //
  2600. // Make the PPE and PDE exist and valid.
  2601. //
  2602. if (MiDoesPpeExistAndMakeValid (PointerPpe,
  2603. CurrentProcess,
  2604. MM_NOIRQL,
  2605. &Waited) == FALSE) {
  2606. UNLOCK_WS (CurrentProcess);
  2607. return;
  2608. }
  2609. if (MiDoesPdeExistAndMakeValid (PointerPde,
  2610. CurrentProcess,
  2611. MM_NOIRQL,
  2612. &Waited) == FALSE) {
  2613. UNLOCK_WS (CurrentProcess);
  2614. return;
  2615. }
  2616. //
  2617. // Now it is safe to read PointerPte.
  2618. //
  2619. PteContents = *PointerPte;
  2620. //
  2621. // Check to see if the protection for the native page should be set
  2622. // and if the access bit of the PTE should be set.
  2623. //
  2624. if (PteContents.u.Hard.Valid != 0) {
  2625. TempPte = PteContents;
  2626. //
  2627. // Perform PTE protection mask corrections.
  2628. //
  2629. TempPte.u.Long |= NewPteProtection;
  2630. if (PteContents.u.Hard.Accessed == 0) {
  2631. TempPte.u.Hard.Accessed = 1;
  2632. if (PageIsSplit == TRUE) {
  2633. TempPte.u.Hard.Cache = MM_PTE_CACHE_RESERVED;
  2634. }
  2635. }
  2636. MI_WRITE_VALID_PTE_NEW_PROTECTION (PointerPte, TempPte);
  2637. }
  2638. UNLOCK_WS (CurrentProcess);
  2639. }
  2640. #endif