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.

2474 lines
71 KiB

  1. /*++
  2. Copyright (c) 1989 Microsoft Corporation
  3. Module Name:
  4. protect.c
  5. Abstract:
  6. This module contains the routines which implement the
  7. NtProtectVirtualMemory service.
  8. Author:
  9. Lou Perazzoli (loup) 18-Aug-1989
  10. Landy Wang (landyw) 02-June-1997
  11. Revision History:
  12. --*/
  13. #include "mi.h"
  14. #if DBG
  15. PEPROCESS MmWatchProcess;
  16. #endif // DBG
  17. VOID
  18. MiFlushTbAndCapture (
  19. IN PMMVAD FoundVad,
  20. IN PMMPTE PtePointer,
  21. IN MMPTE TempPte,
  22. IN PMMPFN Pfn1
  23. );
  24. ULONG
  25. MiSetProtectionOnTransitionPte (
  26. IN PMMPTE PointerPte,
  27. IN ULONG ProtectionMask
  28. );
  29. MMPTE
  30. MiCaptureSystemPte (
  31. IN PMMPTE PointerProtoPte,
  32. IN PEPROCESS Process
  33. );
  34. extern CCHAR MmReadWrite[32];
  35. #ifdef ALLOC_PRAGMA
  36. #pragma alloc_text(PAGE,NtProtectVirtualMemory)
  37. #pragma alloc_text(PAGE,MiProtectVirtualMemory)
  38. #pragma alloc_text(PAGE,MiSetProtectionOnSection)
  39. #pragma alloc_text(PAGE,MiGetPageProtection)
  40. #pragma alloc_text(PAGE,MiChangeNoAccessForkPte)
  41. #pragma alloc_text(PAGE,MiCheckSecuredVad)
  42. #endif
  43. NTSTATUS
  44. NtProtectVirtualMemory (
  45. IN HANDLE ProcessHandle,
  46. IN OUT PVOID *BaseAddress,
  47. IN OUT PSIZE_T RegionSize,
  48. IN ULONG NewProtect,
  49. OUT PULONG OldProtect
  50. )
  51. /*++
  52. Routine Description:
  53. This routine changes the protection on a region of committed pages
  54. within the virtual address space of the subject process. Setting
  55. the protection on a range of pages causes the old protection to be
  56. replaced by the specified protection value.
  57. Note if a virtual address is locked in the working set and the
  58. protection is changed to no access, the page is removed from the
  59. working set since valid pages can't be no access.
  60. Arguments:
  61. ProcessHandle - An open handle to a process object.
  62. BaseAddress - The base address of the region of pages
  63. whose protection is to be changed. This value is
  64. rounded down to the next host page address
  65. boundary.
  66. RegionSize - A pointer to a variable that will receive
  67. the actual size in bytes of the protected region
  68. of pages. The initial value of this argument is
  69. rounded up to the next host page size boundary.
  70. NewProtect - The new protection desired for the specified region of pages.
  71. Protect Values
  72. PAGE_NOACCESS - No access to the specified region
  73. of pages is allowed. An attempt to read,
  74. write, or execute the specified region
  75. results in an access violation.
  76. PAGE_EXECUTE - Execute access to the specified
  77. region of pages is allowed. An attempt to
  78. read or write the specified region results in
  79. an access violation.
  80. PAGE_READONLY - Read only and execute access to the
  81. specified region of pages is allowed. An
  82. attempt to write the specified region results
  83. in an access violation.
  84. PAGE_READWRITE - Read, write, and execute access to
  85. the specified region of pages is allowed. If
  86. write access to the underlying section is
  87. allowed, then a single copy of the pages are
  88. shared. Otherwise the pages are shared read
  89. only/copy on write.
  90. PAGE_GUARD - Read, write, and execute access to the
  91. specified region of pages is allowed,
  92. however, access to the region causes a "guard
  93. region entered" condition to be raised in the
  94. subject process. If write access to the
  95. underlying section is allowed, then a single
  96. copy of the pages are shared. Otherwise the
  97. pages are shared read only/copy on write.
  98. PAGE_NOCACHE - The page should be treated as uncached.
  99. This is only valid for non-shared pages.
  100. OldProtect - A pointer to a variable that will receive
  101. the old protection of the first page within the
  102. specified region of pages.
  103. Return Value:
  104. NTSTATUS.
  105. Environment:
  106. Kernel mode.
  107. --*/
  108. {
  109. KAPC_STATE ApcState;
  110. PEPROCESS Process;
  111. KPROCESSOR_MODE PreviousMode;
  112. NTSTATUS Status;
  113. ULONG Attached = FALSE;
  114. PVOID CapturedBase;
  115. SIZE_T CapturedRegionSize;
  116. ULONG ProtectionMask;
  117. ULONG LastProtect;
  118. PETHREAD CurrentThread;
  119. PEPROCESS CurrentProcess;
  120. PAGED_CODE();
  121. //
  122. // Check the protection field.
  123. //
  124. ProtectionMask = MiMakeProtectionMask (NewProtect);
  125. if (ProtectionMask == MM_INVALID_PROTECTION) {
  126. return STATUS_INVALID_PAGE_PROTECTION;
  127. }
  128. CurrentThread = PsGetCurrentThread ();
  129. CurrentProcess = PsGetCurrentProcessByThread (CurrentThread);
  130. PreviousMode = KeGetPreviousModeByThread (&CurrentThread->Tcb);
  131. if (PreviousMode != KernelMode) {
  132. //
  133. // Capture the region size and base address under an exception handler.
  134. //
  135. try {
  136. ProbeForWritePointer (BaseAddress);
  137. ProbeForWriteUlong_ptr (RegionSize);
  138. ProbeForWriteUlong (OldProtect);
  139. //
  140. // Capture the region size and base address.
  141. //
  142. CapturedBase = *BaseAddress;
  143. CapturedRegionSize = *RegionSize;
  144. } except (EXCEPTION_EXECUTE_HANDLER) {
  145. //
  146. // If an exception occurs during the probe or capture
  147. // of the initial values, then handle the exception and
  148. // return the exception code as the status value.
  149. //
  150. return GetExceptionCode();
  151. }
  152. }
  153. else {
  154. //
  155. // Capture the region size and base address.
  156. //
  157. CapturedRegionSize = *RegionSize;
  158. CapturedBase = *BaseAddress;
  159. }
  160. //
  161. // Make sure the specified starting and ending addresses are
  162. // within the user part of the virtual address space.
  163. //
  164. if (CapturedBase > MM_HIGHEST_USER_ADDRESS) {
  165. //
  166. // Invalid base address.
  167. //
  168. return STATUS_INVALID_PARAMETER_2;
  169. }
  170. if ((ULONG_PTR)MM_HIGHEST_USER_ADDRESS - (ULONG_PTR)CapturedBase <
  171. CapturedRegionSize) {
  172. //
  173. // Invalid region size;
  174. //
  175. return STATUS_INVALID_PARAMETER_3;
  176. }
  177. if (CapturedRegionSize == 0) {
  178. return STATUS_INVALID_PARAMETER_3;
  179. }
  180. Status = ObReferenceObjectByHandle (ProcessHandle,
  181. PROCESS_VM_OPERATION,
  182. PsProcessType,
  183. PreviousMode,
  184. (PVOID *)&Process,
  185. NULL);
  186. if (!NT_SUCCESS(Status)) {
  187. return Status;
  188. }
  189. //
  190. // If the specified process is not the current process, attach
  191. // to the specified process.
  192. //
  193. if (CurrentProcess != Process) {
  194. KeStackAttachProcess (&Process->Pcb, &ApcState);
  195. Attached = TRUE;
  196. }
  197. Status = MiProtectVirtualMemory (Process,
  198. &CapturedBase,
  199. &CapturedRegionSize,
  200. NewProtect,
  201. &LastProtect);
  202. if (Attached) {
  203. KeUnstackDetachProcess (&ApcState);
  204. }
  205. ObDereferenceObject (Process);
  206. //
  207. // Establish an exception handler and write the size and base
  208. // address.
  209. //
  210. try {
  211. *RegionSize = CapturedRegionSize;
  212. *BaseAddress = CapturedBase;
  213. *OldProtect = LastProtect;
  214. } except (EXCEPTION_EXECUTE_HANDLER) {
  215. NOTHING;
  216. }
  217. return Status;
  218. }
  219. NTSTATUS
  220. MiProtectVirtualMemory (
  221. IN PEPROCESS Process,
  222. IN PVOID *BaseAddress,
  223. IN PSIZE_T RegionSize,
  224. IN ULONG NewProtect,
  225. IN PULONG LastProtect
  226. )
  227. /*++
  228. Routine Description:
  229. This routine changes the protection on a region of committed pages
  230. within the virtual address space of the subject process. Setting
  231. the protection on a range of pages causes the old protection to be
  232. replaced by the specified protection value.
  233. Arguments:
  234. Process - Supplies a pointer to the current process.
  235. BaseAddress - Supplies the starting address to protect.
  236. RegionsSize - Supplies the size of the region to protect.
  237. NewProtect - Supplies the new protection to set.
  238. LastProtect - Supplies the address of a kernel owned pointer to
  239. store (without probing) the old protection into.
  240. Return Value:
  241. NTSTATUS.
  242. Environment:
  243. Kernel mode, APC_LEVEL or below.
  244. --*/
  245. {
  246. PMMVAD FoundVad;
  247. PVOID StartingAddress;
  248. PVOID EndingAddress;
  249. PVOID CapturedBase;
  250. SIZE_T CapturedRegionSize;
  251. NTSTATUS Status;
  252. ULONG Attached;
  253. PMMPTE PointerPte;
  254. PMMPTE LastPte;
  255. PMMPTE PointerPde;
  256. PMMPTE PointerProtoPte;
  257. PMMPTE LastProtoPte;
  258. PMMPFN Pfn1;
  259. ULONG CapturedOldProtect;
  260. ULONG ProtectionMask;
  261. MMPTE TempPte;
  262. MMPTE PteContents;
  263. ULONG Locked;
  264. PVOID Va;
  265. ULONG DoAgain;
  266. PVOID UsedPageTableHandle;
  267. ULONG WorkingSetIndex;
  268. ULONG OriginalProtect;
  269. LOGICAL WsHeld;
  270. #if defined(_MIALT4K_)
  271. PVOID OriginalBase;
  272. SIZE_T OriginalRegionSize;
  273. ULONG OriginalProtectionMask;
  274. PVOID StartingAddressFor4k;
  275. PVOID EndingAddressFor4k;
  276. SIZE_T CapturedRegionSizeFor4k;
  277. ULONG CapturedOldProtectFor4k;
  278. LOGICAL EmulationFor4kPage;
  279. #endif
  280. Attached = FALSE;
  281. Locked = FALSE;
  282. //
  283. // Get the address creation mutex to block multiple threads from
  284. // creating or deleting address space at the same time.
  285. // Get the working set mutex so PTEs can be modified.
  286. // Block APCs so an APC which takes a page
  287. // fault does not corrupt various structures.
  288. //
  289. CapturedBase = *BaseAddress;
  290. CapturedRegionSize = *RegionSize;
  291. OriginalProtect = NewProtect;
  292. #if defined(_MIALT4K_)
  293. EmulationFor4kPage = FALSE;
  294. OriginalBase = CapturedBase;
  295. OriginalRegionSize = CapturedRegionSize;
  296. CapturedOldProtectFor4k = 0;
  297. OriginalProtectionMask = 0;
  298. if (Process->Wow64Process != NULL) {
  299. StartingAddressFor4k = (PVOID)PAGE_4K_ALIGN(OriginalBase);
  300. EndingAddressFor4k = (PVOID)(((ULONG_PTR)OriginalBase +
  301. OriginalRegionSize - 1) | (PAGE_4K - 1));
  302. CapturedRegionSizeFor4k = (ULONG_PTR)EndingAddressFor4k -
  303. (ULONG_PTR)StartingAddressFor4k + 1L;
  304. OriginalProtectionMask = MiMakeProtectionMask(NewProtect);
  305. if (OriginalProtectionMask == MM_INVALID_PROTECTION) {
  306. return STATUS_INVALID_PAGE_PROTECTION;
  307. }
  308. EmulationFor4kPage = TRUE;
  309. }
  310. else {
  311. //
  312. // Initializing these is not needed for correctness, but
  313. // without it the compiler cannot compile this code W4 to check
  314. // for use of uninitialized variables.
  315. //
  316. StartingAddressFor4k = 0;
  317. EndingAddressFor4k = 0;
  318. CapturedRegionSizeFor4k = 0;
  319. }
  320. #endif
  321. ProtectionMask = MiMakeProtectionMask (NewProtect);
  322. if (ProtectionMask == MM_INVALID_PROTECTION) {
  323. return STATUS_INVALID_PAGE_PROTECTION;
  324. }
  325. EndingAddress = (PVOID)(((ULONG_PTR)CapturedBase +
  326. CapturedRegionSize - 1L) | (PAGE_SIZE - 1L));
  327. StartingAddress = (PVOID)PAGE_ALIGN(CapturedBase);
  328. LOCK_ADDRESS_SPACE (Process);
  329. //
  330. // Make sure the address space was not deleted, if so, return an error.
  331. //
  332. if (Process->Flags & PS_PROCESS_FLAGS_VM_DELETED) {
  333. Status = STATUS_PROCESS_IS_TERMINATING;
  334. goto ErrorFound;
  335. }
  336. FoundVad = MiCheckForConflictingVad (Process, StartingAddress, EndingAddress);
  337. if (FoundVad == NULL) {
  338. //
  339. // No virtual address is reserved at the specified base address,
  340. // return an error.
  341. //
  342. Status = STATUS_CONFLICTING_ADDRESSES;
  343. goto ErrorFound;
  344. }
  345. //
  346. // Ensure that the starting and ending addresses are all within
  347. // the same virtual address descriptor.
  348. //
  349. if ((MI_VA_TO_VPN (StartingAddress) < FoundVad->StartingVpn) ||
  350. (MI_VA_TO_VPN (EndingAddress) > FoundVad->EndingVpn)) {
  351. //
  352. // Not within the section virtual address descriptor,
  353. // return an error.
  354. //
  355. Status = STATUS_CONFLICTING_ADDRESSES;
  356. goto ErrorFound;
  357. }
  358. if ((FoundVad->u.VadFlags.UserPhysicalPages == 1) ||
  359. (FoundVad->u.VadFlags.LargePages == 1)) {
  360. //
  361. // These regions are always readwrite (but no execute).
  362. //
  363. if (ProtectionMask == MM_READWRITE) {
  364. UNLOCK_ADDRESS_SPACE (Process);
  365. *RegionSize = (PCHAR)EndingAddress - (PCHAR)StartingAddress + 1L;
  366. *BaseAddress = StartingAddress;
  367. *LastProtect = PAGE_READWRITE;
  368. return STATUS_SUCCESS;
  369. }
  370. Status = STATUS_CONFLICTING_ADDRESSES;
  371. goto ErrorFound;
  372. }
  373. if (FoundVad->u.VadFlags.PhysicalMapping == 1) {
  374. //
  375. // Setting the protection of a physically mapped section is
  376. // not allowed as there is no corresponding PFN database element.
  377. //
  378. Status = STATUS_CONFLICTING_ADDRESSES;
  379. goto ErrorFound;
  380. }
  381. if (FoundVad->u.VadFlags.NoChange == 1) {
  382. //
  383. // An attempt is made at changing the protection
  384. // of a secured VAD, check to see if the address range
  385. // to change allows the change.
  386. //
  387. Status = MiCheckSecuredVad (FoundVad,
  388. CapturedBase,
  389. CapturedRegionSize,
  390. ProtectionMask);
  391. if (!NT_SUCCESS (Status)) {
  392. goto ErrorFound;
  393. }
  394. }
  395. #if defined(_MIALT4K_)
  396. else if (EmulationFor4kPage == TRUE) {
  397. if (StartingAddressFor4k >= MmWorkingSetList->HighestUserAddress) {
  398. Status = STATUS_INVALID_PAGE_PROTECTION;
  399. goto ErrorFound;
  400. }
  401. //
  402. // If not secured, relax the protection.
  403. //
  404. NewProtect = MiMakeProtectForNativePage (StartingAddressFor4k,
  405. NewProtect,
  406. Process);
  407. ProtectionMask = MiMakeProtectionMask(NewProtect);
  408. if (ProtectionMask == MM_INVALID_PROTECTION) {
  409. Status = STATUS_INVALID_PAGE_PROTECTION;
  410. goto ErrorFound;
  411. }
  412. }
  413. #endif
  414. if (FoundVad->u.VadFlags.PrivateMemory == 0) {
  415. //
  416. // For mapped sections, the NO_CACHE attribute is not allowed.
  417. //
  418. if (NewProtect & PAGE_NOCACHE) {
  419. //
  420. // Not allowed.
  421. //
  422. Status = STATUS_INVALID_PARAMETER_4;
  423. goto ErrorFound;
  424. }
  425. //
  426. // Make sure the section page protection is compatible with
  427. // the specified page protection.
  428. //
  429. if ((FoundVad->ControlArea->u.Flags.Image == 0) &&
  430. (!MiIsPteProtectionCompatible ((ULONG)FoundVad->u.VadFlags.Protection,
  431. OriginalProtect))) {
  432. Status = STATUS_SECTION_PROTECTION;
  433. goto ErrorFound;
  434. }
  435. //
  436. // If this is a file mapping, then all pages must be
  437. // committed as there can be no sparse file maps. Images
  438. // can have non-committed pages if the alignment is greater
  439. // than the page size.
  440. //
  441. if ((FoundVad->ControlArea->u.Flags.File == 0) ||
  442. (FoundVad->ControlArea->u.Flags.Image == 1)) {
  443. PointerProtoPte = MiGetProtoPteAddress (FoundVad,
  444. MI_VA_TO_VPN (StartingAddress));
  445. LastProtoPte = MiGetProtoPteAddress (FoundVad,
  446. MI_VA_TO_VPN (EndingAddress));
  447. //
  448. // Release the working set mutex and acquire the section
  449. // commit mutex. Check all the prototype PTEs described by
  450. // the virtual address range to ensure they are committed.
  451. //
  452. KeAcquireGuardedMutexUnsafe (&MmSectionCommitMutex);
  453. while (PointerProtoPte <= LastProtoPte) {
  454. //
  455. // Check to see if the prototype PTE is committed, if
  456. // not return an error.
  457. //
  458. if (PointerProtoPte->u.Long == 0) {
  459. //
  460. // Error, this prototype PTE is not committed.
  461. //
  462. KeReleaseGuardedMutexUnsafe (&MmSectionCommitMutex);
  463. Status = STATUS_NOT_COMMITTED;
  464. goto ErrorFound;
  465. }
  466. PointerProtoPte += 1;
  467. }
  468. //
  469. // The range is committed, release the section commitment
  470. // mutex, acquire the working set mutex and update the local PTEs.
  471. //
  472. KeReleaseGuardedMutexUnsafe (&MmSectionCommitMutex);
  473. }
  474. #if defined(_MIALT4K_)
  475. //
  476. // The alternate permission table must be updated before PTEs
  477. // are created for the protection change.
  478. //
  479. if (EmulationFor4kPage == TRUE) {
  480. //
  481. // Capture the old protection.
  482. //
  483. CapturedOldProtectFor4k =
  484. MiQueryProtectionFor4kPage (StartingAddressFor4k, Process);
  485. if (CapturedOldProtectFor4k != 0) {
  486. CapturedOldProtectFor4k =
  487. MI_CONVERT_FROM_PTE_PROTECTION(CapturedOldProtectFor4k);
  488. }
  489. //
  490. // Update the alternate permission table.
  491. //
  492. if ((FoundVad->u.VadFlags.ImageMap == 1) ||
  493. (FoundVad->u2.VadFlags2.CopyOnWrite == 1)) {
  494. //
  495. // Only set the MM_PROTECTION_COPY_MASK if the new protection
  496. // includes MM_PROTECTION_WRITE_MASK, otherwise, it will be
  497. // considered as MM_READ inside MiProtectFor4kPage ().
  498. //
  499. if ((OriginalProtectionMask & MM_PROTECTION_WRITE_MASK) == MM_PROTECTION_WRITE_MASK) {
  500. OriginalProtectionMask |= MM_PROTECTION_COPY_MASK;
  501. }
  502. }
  503. MiProtectFor4kPage (StartingAddressFor4k,
  504. CapturedRegionSizeFor4k,
  505. OriginalProtectionMask,
  506. ALT_CHANGE,
  507. Process);
  508. }
  509. #endif
  510. //
  511. // Set the protection on the section pages.
  512. //
  513. Status = MiSetProtectionOnSection (Process,
  514. FoundVad,
  515. StartingAddress,
  516. EndingAddress,
  517. NewProtect,
  518. &CapturedOldProtect,
  519. FALSE,
  520. &Locked);
  521. //
  522. // *** WARNING ***
  523. //
  524. // The alternate PTE support routines called by MiSetProtectionOnSection
  525. // may have deleted the old (small) VAD and replaced it with a different
  526. // (large) VAD - if so, the old VAD is freed and cannot be referenced.
  527. //
  528. if (!NT_SUCCESS (Status)) {
  529. goto ErrorFound;
  530. }
  531. }
  532. else {
  533. //
  534. // Not a section, private.
  535. // For private pages, the WRITECOPY attribute is not allowed.
  536. //
  537. if ((NewProtect & PAGE_WRITECOPY) ||
  538. (NewProtect & PAGE_EXECUTE_WRITECOPY)) {
  539. //
  540. // Not allowed.
  541. //
  542. Status = STATUS_INVALID_PARAMETER_4;
  543. goto ErrorFound;
  544. }
  545. LOCK_WS_UNSAFE (Process);
  546. //
  547. // Ensure all of the pages are already committed as described
  548. // in the virtual address descriptor.
  549. //
  550. if ( !MiIsEntireRangeCommitted (StartingAddress,
  551. EndingAddress,
  552. FoundVad,
  553. Process)) {
  554. //
  555. // Previously reserved pages have been decommitted, or an error
  556. // occurred, release mutex and return status.
  557. //
  558. UNLOCK_WS_UNSAFE (Process);
  559. Status = STATUS_NOT_COMMITTED;
  560. goto ErrorFound;
  561. }
  562. #if defined(_MIALT4K_)
  563. //
  564. // The alternate permission table must be updated before PTEs
  565. // are created for the protection change.
  566. //
  567. if (EmulationFor4kPage == TRUE) {
  568. //
  569. // Before accessing Alternate Table, unlock the working set mutex.
  570. //
  571. UNLOCK_WS_UNSAFE (Process);
  572. //
  573. // Get the old protection
  574. //
  575. CapturedOldProtectFor4k =
  576. MiQueryProtectionFor4kPage(StartingAddressFor4k, Process);
  577. if (CapturedOldProtectFor4k != 0) {
  578. CapturedOldProtectFor4k =
  579. MI_CONVERT_FROM_PTE_PROTECTION(CapturedOldProtectFor4k);
  580. }
  581. //
  582. // Update the alternate permission table.
  583. //
  584. MiProtectFor4kPage (StartingAddressFor4k,
  585. CapturedRegionSizeFor4k,
  586. OriginalProtectionMask,
  587. ALT_CHANGE,
  588. Process);
  589. LOCK_WS_UNSAFE (Process);
  590. }
  591. #endif
  592. //
  593. // The address range is committed, change the protection.
  594. //
  595. PointerPde = MiGetPdeAddress (StartingAddress);
  596. PointerPte = MiGetPteAddress (StartingAddress);
  597. LastPte = MiGetPteAddress (EndingAddress);
  598. MiMakePdeExistAndMakeValid (PointerPde, Process, MM_NOIRQL);
  599. //
  600. // Capture the protection for the first page.
  601. //
  602. if (PointerPte->u.Long != 0) {
  603. CapturedOldProtect = MiGetPageProtection (PointerPte, Process, FALSE);
  604. //
  605. // Make sure the page directory & table pages are still resident.
  606. //
  607. MiMakePdeExistAndMakeValid (PointerPde, Process, MM_NOIRQL);
  608. }
  609. else {
  610. //
  611. // Get the protection from the VAD.
  612. //
  613. CapturedOldProtect =
  614. MI_CONVERT_FROM_PTE_PROTECTION (FoundVad->u.VadFlags.Protection);
  615. }
  616. //
  617. // For all the PTEs in the specified address range, set the
  618. // protection depending on the state of the PTE.
  619. //
  620. while (PointerPte <= LastPte) {
  621. if (MiIsPteOnPdeBoundary (PointerPte)) {
  622. PointerPde = MiGetPteAddress (PointerPte);
  623. MiMakePdeExistAndMakeValid (PointerPde, Process, MM_NOIRQL);
  624. }
  625. PteContents = *PointerPte;
  626. if (PteContents.u.Long == 0) {
  627. //
  628. // Increment the count of non-zero page table entries
  629. // for this page table and the number of private pages
  630. // for the process. The protection will be set as
  631. // if the PTE was demand zero.
  632. //
  633. UsedPageTableHandle = MI_GET_USED_PTES_HANDLE (MiGetVirtualAddressMappedByPte (PointerPte));
  634. MI_INCREMENT_USED_PTES_BY_HANDLE (UsedPageTableHandle);
  635. }
  636. if (PteContents.u.Hard.Valid == 1) {
  637. //
  638. // Set the protection into both the PTE and the original PTE
  639. // in the PFN database.
  640. //
  641. Pfn1 = MI_PFN_ELEMENT (PteContents.u.Hard.PageFrameNumber);
  642. if (Pfn1->u3.e1.PrototypePte == 1) {
  643. //
  644. // This PTE refers to a fork prototype PTE, make it
  645. // private.
  646. //
  647. MiCopyOnWrite (MiGetVirtualAddressMappedByPte (PointerPte),
  648. PointerPte);
  649. //
  650. // This may have released the working set mutex and
  651. // the page directory and table pages may no longer be
  652. // in memory.
  653. //
  654. MiMakePdeExistAndMakeValid (PointerPde, Process, MM_NOIRQL);
  655. //
  656. // Do the loop again for the same PTE.
  657. //
  658. continue;
  659. }
  660. //
  661. // The PTE is a private page which is valid, if the
  662. // specified protection is no-access or guard page
  663. // remove the PTE from the working set.
  664. //
  665. if ((NewProtect & PAGE_NOACCESS) || (NewProtect & PAGE_GUARD)) {
  666. //
  667. // Remove the page from the working set.
  668. //
  669. Locked = MiRemovePageFromWorkingSet (PointerPte,
  670. Pfn1,
  671. &Process->Vm);
  672. continue;
  673. }
  674. Pfn1->OriginalPte.u.Soft.Protection = ProtectionMask;
  675. MI_MAKE_VALID_PTE (TempPte,
  676. PointerPte->u.Hard.PageFrameNumber,
  677. ProtectionMask,
  678. PointerPte);
  679. #if defined(_MIALT4K_)
  680. //
  681. // Preserve the split protections if they exist.
  682. //
  683. TempPte.u.Hard.Cache = PointerPte->u.Hard.Cache;
  684. #endif
  685. WorkingSetIndex = MI_GET_WORKING_SET_FROM_PTE (&PteContents);
  686. MI_SET_PTE_IN_WORKING_SET (&TempPte, WorkingSetIndex);
  687. //
  688. // Flush the TB as we have changed the protection
  689. // of a valid PTE.
  690. //
  691. MiFlushTbAndCapture (FoundVad,
  692. PointerPte,
  693. TempPte,
  694. Pfn1);
  695. }
  696. else if (PteContents.u.Soft.Prototype == 1) {
  697. //
  698. // This PTE refers to a fork prototype PTE, make the
  699. // page private. This is accomplished by releasing
  700. // the working set mutex, reading the page thereby
  701. // causing a fault, and re-executing the loop. Hopefully,
  702. // this time, we'll find the page present and we'll
  703. // turn it into a private page.
  704. //
  705. // Note, that a TRY is used to catch guard
  706. // page exceptions and no-access exceptions.
  707. //
  708. Va = MiGetVirtualAddressMappedByPte (PointerPte);
  709. DoAgain = TRUE;
  710. while (PteContents.u.Hard.Valid == 0) {
  711. UNLOCK_WS_UNSAFE (Process);
  712. WsHeld = FALSE;
  713. try {
  714. *(volatile ULONG *)Va;
  715. } except (EXCEPTION_EXECUTE_HANDLER) {
  716. if (GetExceptionCode() == STATUS_ACCESS_VIOLATION) {
  717. //
  718. // The prototype PTE must be noaccess.
  719. //
  720. WsHeld = TRUE;
  721. LOCK_WS_UNSAFE (Process);
  722. MiMakePdeExistAndMakeValid (PointerPde,
  723. Process,
  724. MM_NOIRQL);
  725. if (MiChangeNoAccessForkPte (PointerPte, ProtectionMask) == TRUE) {
  726. DoAgain = FALSE;
  727. }
  728. }
  729. else if (GetExceptionCode() == STATUS_IN_PAGE_ERROR) {
  730. //
  731. // Ignore this page and go on to the next one.
  732. //
  733. PointerPte += 1;
  734. DoAgain = TRUE;
  735. WsHeld = TRUE;
  736. LOCK_WS_UNSAFE (Process);
  737. break;
  738. }
  739. }
  740. if (WsHeld == FALSE) {
  741. LOCK_WS_UNSAFE (Process);
  742. }
  743. MiMakePdeExistAndMakeValid (PointerPde, Process, MM_NOIRQL);
  744. PteContents = *PointerPte;
  745. }
  746. if (DoAgain) {
  747. continue;
  748. }
  749. }
  750. else if (PteContents.u.Soft.Transition == 1) {
  751. if (MiSetProtectionOnTransitionPte (PointerPte,
  752. ProtectionMask)) {
  753. continue;
  754. }
  755. }
  756. else {
  757. //
  758. // Must be page file space or demand zero.
  759. //
  760. PointerPte->u.Soft.Protection = ProtectionMask;
  761. ASSERT (PointerPte->u.Long != 0);
  762. }
  763. PointerPte += 1;
  764. } //end while
  765. UNLOCK_WS_UNSAFE (Process);
  766. }
  767. UNLOCK_ADDRESS_SPACE (Process);
  768. //
  769. // Common completion code.
  770. //
  771. #if defined(_MIALT4K_)
  772. if (EmulationFor4kPage == TRUE) {
  773. StartingAddress = StartingAddressFor4k;
  774. EndingAddress = EndingAddressFor4k;
  775. if (CapturedOldProtectFor4k != 0) {
  776. //
  777. // change CapturedOldProtect when CapturedOldProtectFor4k
  778. // contains the true protection for the 4k page
  779. //
  780. CapturedOldProtect = CapturedOldProtectFor4k;
  781. }
  782. }
  783. #endif
  784. *RegionSize = (PCHAR)EndingAddress - (PCHAR)StartingAddress + 1L;
  785. *BaseAddress = StartingAddress;
  786. *LastProtect = CapturedOldProtect;
  787. if (Locked) {
  788. return STATUS_WAS_UNLOCKED;
  789. }
  790. return STATUS_SUCCESS;
  791. ErrorFound:
  792. UNLOCK_ADDRESS_SPACE (Process);
  793. return Status;
  794. }
  795. NTSTATUS
  796. MiSetProtectionOnSection (
  797. IN PEPROCESS Process,
  798. IN PMMVAD FoundVad,
  799. IN PVOID StartingAddress,
  800. IN PVOID EndingAddress,
  801. IN ULONG NewProtect,
  802. OUT PULONG CapturedOldProtect,
  803. IN ULONG DontCharge,
  804. OUT PULONG Locked
  805. )
  806. /*++
  807. Routine Description:
  808. This routine changes the protection on a region of committed pages
  809. within the virtual address space of the subject process. Setting
  810. the protection on a range of pages causes the old protection to be
  811. replaced by the specified protection value.
  812. Arguments:
  813. Process - Supplies a pointer to the current process.
  814. FoundVad - Supplies a pointer to the VAD containing the range to protect.
  815. StartingAddress - Supplies the starting address to protect.
  816. EndingAddress - Supplies the ending address to protect.
  817. NewProtect - Supplies the new protection to set.
  818. CapturedOldProtect - Supplies the address of a kernel owned pointer to
  819. store (without probing) the old protection into.
  820. DontCharge - Supplies TRUE if no quota or commitment should be charged.
  821. Locked - Receives TRUE if a locked page was removed from the working
  822. set (protection was guard page or no-access), FALSE otherwise.
  823. Return Value:
  824. NTSTATUS.
  825. Environment:
  826. Kernel mode, address creation mutex held, APCs disabled.
  827. --*/
  828. {
  829. LOGICAL WsHeld;
  830. PMMPTE PointerPte;
  831. PMMPTE LastPte;
  832. PMMPTE PointerPde;
  833. PMMPTE PointerPpe;
  834. PMMPTE PointerPxe;
  835. PMMPTE PointerProtoPte;
  836. PMMPFN Pfn1;
  837. MMPTE TempPte;
  838. ULONG ProtectionMask;
  839. ULONG ProtectionMaskNotCopy;
  840. ULONG NewProtectionMask;
  841. MMPTE PteContents;
  842. WSLE_NUMBER Index;
  843. PULONG Va;
  844. ULONG WriteCopy;
  845. ULONG DoAgain;
  846. ULONG Waited;
  847. SIZE_T QuotaCharge;
  848. PVOID UsedPageTableHandle;
  849. ULONG WorkingSetIndex;
  850. NTSTATUS Status;
  851. #if DBG
  852. #define PTES_TRACKED 0x10
  853. ULONG PteIndex = 0;
  854. MMPTE PteTracker[PTES_TRACKED];
  855. MMPFN PfnTracker[PTES_TRACKED];
  856. SIZE_T PteQuotaCharge;
  857. #endif
  858. PAGED_CODE();
  859. *Locked = FALSE;
  860. WriteCopy = FALSE;
  861. QuotaCharge = 0;
  862. //
  863. // Make the protection field.
  864. //
  865. ASSERT (FoundVad->u.VadFlags.PrivateMemory == 0);
  866. if ((FoundVad->u.VadFlags.ImageMap == 1) ||
  867. (FoundVad->u2.VadFlags2.CopyOnWrite == 1)) {
  868. if (NewProtect & PAGE_READWRITE) {
  869. NewProtect &= ~PAGE_READWRITE;
  870. NewProtect |= PAGE_WRITECOPY;
  871. }
  872. if (NewProtect & PAGE_EXECUTE_READWRITE) {
  873. NewProtect &= ~PAGE_EXECUTE_READWRITE;
  874. NewProtect |= PAGE_EXECUTE_WRITECOPY;
  875. }
  876. }
  877. ProtectionMask = MiMakeProtectionMask (NewProtect);
  878. if (ProtectionMask == MM_INVALID_PROTECTION) {
  879. //
  880. // Return the error.
  881. //
  882. return STATUS_INVALID_PAGE_PROTECTION;
  883. }
  884. //
  885. // Determine if copy on write is being set.
  886. //
  887. ProtectionMaskNotCopy = ProtectionMask;
  888. if ((ProtectionMask & MM_COPY_ON_WRITE_MASK) == MM_COPY_ON_WRITE_MASK) {
  889. WriteCopy = TRUE;
  890. ProtectionMaskNotCopy &= ~MM_PROTECTION_COPY_MASK;
  891. }
  892. #if defined(_MIALT4K_)
  893. if ((Process->Wow64Process != NULL) &&
  894. (FoundVad->u.VadFlags.ImageMap == 0) &&
  895. (FoundVad->u2.VadFlags2.CopyOnWrite == 0) &&
  896. (WriteCopy)) {
  897. PMMVAD NewVad;
  898. Status = MiSetCopyPagesFor4kPage (Process,
  899. FoundVad,
  900. StartingAddress,
  901. EndingAddress,
  902. ProtectionMask,
  903. &NewVad);
  904. if (!NT_SUCCESS (Status)) {
  905. return Status;
  906. }
  907. //
  908. // *** WARNING ***
  909. //
  910. // The alternate PTE support routines may need to expand the entry
  911. // VAD - if so, the old VAD is freed and cannot be referenced.
  912. //
  913. ASSERT (NewVad != NULL);
  914. FoundVad = NewVad;
  915. }
  916. #endif
  917. PointerPxe = MiGetPxeAddress (StartingAddress);
  918. PointerPpe = MiGetPpeAddress (StartingAddress);
  919. PointerPde = MiGetPdeAddress (StartingAddress);
  920. PointerPte = MiGetPteAddress (StartingAddress);
  921. LastPte = MiGetPteAddress (EndingAddress);
  922. LOCK_WS_UNSAFE (Process);
  923. MiMakePdeExistAndMakeValid (PointerPde, Process, MM_NOIRQL);
  924. //
  925. // Capture the protection for the first page.
  926. //
  927. if (PointerPte->u.Long != 0) {
  928. *CapturedOldProtect = MiGetPageProtection (PointerPte, Process, FALSE);
  929. //
  930. // Ensure the PDE (and any table above it) are still resident.
  931. //
  932. MiMakePdeExistAndMakeValid (PointerPde, Process, MM_NOIRQL);
  933. }
  934. else {
  935. //
  936. // Get the protection from the VAD, unless image file.
  937. //
  938. if (FoundVad->u.VadFlags.ImageMap == 0) {
  939. //
  940. // This is not an image file, the protection is in the VAD.
  941. //
  942. *CapturedOldProtect =
  943. MI_CONVERT_FROM_PTE_PROTECTION(FoundVad->u.VadFlags.Protection);
  944. }
  945. else {
  946. //
  947. // This is an image file, the protection is in the
  948. // prototype PTE.
  949. //
  950. PointerProtoPte = MiGetProtoPteAddress (FoundVad,
  951. MI_VA_TO_VPN (
  952. MiGetVirtualAddressMappedByPte (PointerPte)));
  953. TempPte = MiCaptureSystemPte (PointerProtoPte, Process);
  954. *CapturedOldProtect = MiGetPageProtection (&TempPte,
  955. Process,
  956. TRUE);
  957. //
  958. // Ensure the PDE (and any table above it) are still resident.
  959. //
  960. MiMakePdeExistAndMakeValid (PointerPde, Process, MM_NOIRQL);
  961. }
  962. }
  963. //
  964. // If the page protection is being changed to be copy-on-write, the
  965. // commitment and page file quota for the potentially dirty private pages
  966. // must be calculated and charged. This must be done before any
  967. // protections are changed as the changes cannot be undone.
  968. //
  969. if (WriteCopy) {
  970. //
  971. // Calculate the charges. If the page is shared and not write copy
  972. // it is counted as a charged page.
  973. //
  974. while (PointerPte <= LastPte) {
  975. if (MiIsPteOnPdeBoundary (PointerPte)) {
  976. PointerPde = MiGetPteAddress (PointerPte);
  977. PointerPpe = MiGetPteAddress (PointerPde);
  978. PointerPxe = MiGetPdeAddress (PointerPde);
  979. #if (_MI_PAGING_LEVELS >= 4)
  980. retry:
  981. #endif
  982. do {
  983. while (!MiDoesPxeExistAndMakeValid(PointerPxe, Process, MM_NOIRQL, &Waited)) {
  984. //
  985. // No PXE exists for this address. Therefore
  986. // all the PTEs are shared and not copy on write.
  987. // go to the next PXE.
  988. //
  989. PointerPxe += 1;
  990. PointerPpe = MiGetVirtualAddressMappedByPte (PointerPxe);
  991. PointerPde = MiGetVirtualAddressMappedByPte (PointerPpe);
  992. PointerProtoPte = PointerPte;
  993. PointerPte = MiGetVirtualAddressMappedByPte (PointerPde);
  994. if (PointerPte > LastPte) {
  995. QuotaCharge += 1 + LastPte - PointerProtoPte;
  996. goto Done;
  997. }
  998. QuotaCharge += PointerPte - PointerProtoPte;
  999. }
  1000. #if (_MI_PAGING_LEVELS >= 4)
  1001. Waited = 0;
  1002. #endif
  1003. while (!MiDoesPpeExistAndMakeValid(PointerPpe, Process, MM_NOIRQL, &Waited)) {
  1004. //
  1005. // No PPE exists for this address. Therefore
  1006. // all the PTEs are shared and not copy on write.
  1007. // go to the next PPE.
  1008. //
  1009. PointerPpe += 1;
  1010. PointerPxe = MiGetPteAddress (PointerPpe);
  1011. PointerPde = MiGetVirtualAddressMappedByPte (PointerPpe);
  1012. PointerProtoPte = PointerPte;
  1013. PointerPte = MiGetVirtualAddressMappedByPte (PointerPde);
  1014. if (PointerPte > LastPte) {
  1015. QuotaCharge += 1 + LastPte - PointerProtoPte;
  1016. goto Done;
  1017. }
  1018. #if (_MI_PAGING_LEVELS >= 4)
  1019. if (MiIsPteOnPdeBoundary (PointerPpe)) {
  1020. PointerPxe = MiGetPdeAddress (PointerPde);
  1021. goto retry;
  1022. }
  1023. #endif
  1024. QuotaCharge += PointerPte - PointerProtoPte;
  1025. }
  1026. #if (_MI_PAGING_LEVELS < 4)
  1027. Waited = 0;
  1028. #endif
  1029. while (!MiDoesPdeExistAndMakeValid(PointerPde, Process, MM_NOIRQL, &Waited)) {
  1030. //
  1031. // No PDE exists for this address. Therefore
  1032. // all the PTEs are shared and not copy on write.
  1033. // go to the next PDE.
  1034. //
  1035. PointerPde += 1;
  1036. PointerProtoPte = PointerPte;
  1037. PointerPpe = MiGetPteAddress (PointerPde);
  1038. PointerPxe = MiGetPteAddress (PointerPpe);
  1039. PointerPte = MiGetVirtualAddressMappedByPte (PointerPde);
  1040. if (PointerPte > LastPte) {
  1041. QuotaCharge += 1 + LastPte - PointerProtoPte;
  1042. goto Done;
  1043. }
  1044. QuotaCharge += PointerPte - PointerProtoPte;
  1045. #if (_MI_PAGING_LEVELS >= 3)
  1046. if (MiIsPteOnPdeBoundary (PointerPde)) {
  1047. Waited = 1;
  1048. break;
  1049. }
  1050. #endif
  1051. }
  1052. } while (Waited != 0);
  1053. }
  1054. PteContents = *PointerPte;
  1055. if (PteContents.u.Long == 0) {
  1056. //
  1057. // The PTE has not been evaluated, assume copy on write.
  1058. //
  1059. QuotaCharge += 1;
  1060. }
  1061. else if (PteContents.u.Hard.Valid == 1) {
  1062. if (PteContents.u.Hard.CopyOnWrite == 0) {
  1063. //
  1064. // See if this is a prototype PTE, if so charge it.
  1065. //
  1066. Pfn1 = MI_PFN_ELEMENT (PteContents.u.Hard.PageFrameNumber);
  1067. if (Pfn1->u3.e1.PrototypePte == 1) {
  1068. QuotaCharge += 1;
  1069. }
  1070. }
  1071. }
  1072. else {
  1073. if (PteContents.u.Soft.Prototype == 1) {
  1074. //
  1075. // This is a prototype PTE. Charge if it is not
  1076. // in copy on write format.
  1077. //
  1078. if (PteContents.u.Soft.PageFileHigh == MI_PTE_LOOKUP_NEEDED) {
  1079. //
  1080. // Page protection is within the PTE.
  1081. //
  1082. if (!MI_IS_PTE_PROTECTION_COPY_WRITE(PteContents.u.Soft.Protection)) {
  1083. QuotaCharge += 1;
  1084. }
  1085. }
  1086. else {
  1087. //
  1088. // The PTE references the prototype directly, therefore
  1089. // it can't be copy on write. Charge.
  1090. //
  1091. QuotaCharge += 1;
  1092. }
  1093. }
  1094. }
  1095. PointerPte += 1;
  1096. }
  1097. Done:
  1098. //
  1099. // If any quota is required, charge for it now.
  1100. //
  1101. if ((!DontCharge) && (QuotaCharge != 0)) {
  1102. Status = PsChargeProcessPageFileQuota (Process, QuotaCharge);
  1103. if (!NT_SUCCESS (Status)) {
  1104. UNLOCK_WS_UNSAFE (Process);
  1105. return STATUS_PAGEFILE_QUOTA_EXCEEDED;
  1106. }
  1107. if (Process->CommitChargeLimit) {
  1108. if (Process->CommitCharge + QuotaCharge > Process->CommitChargeLimit) {
  1109. PsReturnProcessPageFileQuota (Process, QuotaCharge);
  1110. if (Process->Job) {
  1111. PsReportProcessMemoryLimitViolation ();
  1112. }
  1113. UNLOCK_WS_UNSAFE (Process);
  1114. return STATUS_COMMITMENT_LIMIT;
  1115. }
  1116. }
  1117. if (Process->JobStatus & PS_JOB_STATUS_REPORT_COMMIT_CHANGES) {
  1118. if (PsChangeJobMemoryUsage(PS_JOB_STATUS_REPORT_COMMIT_CHANGES, QuotaCharge) == FALSE) {
  1119. PsReturnProcessPageFileQuota (Process, QuotaCharge);
  1120. UNLOCK_WS_UNSAFE (Process);
  1121. return STATUS_COMMITMENT_LIMIT;
  1122. }
  1123. }
  1124. if (MiChargeCommitment (QuotaCharge, Process) == FALSE) {
  1125. if (Process->JobStatus & PS_JOB_STATUS_REPORT_COMMIT_CHANGES) {
  1126. PsChangeJobMemoryUsage(PS_JOB_STATUS_REPORT_COMMIT_CHANGES, -(SSIZE_T)QuotaCharge);
  1127. }
  1128. PsReturnProcessPageFileQuota (Process, QuotaCharge);
  1129. UNLOCK_WS_UNSAFE (Process);
  1130. return STATUS_COMMITMENT_LIMIT;
  1131. }
  1132. //
  1133. // Add the quota into the charge to the VAD.
  1134. //
  1135. MM_TRACK_COMMIT (MM_DBG_COMMIT_SET_PROTECTION, QuotaCharge);
  1136. FoundVad->u.VadFlags.CommitCharge += QuotaCharge;
  1137. Process->CommitCharge += QuotaCharge;
  1138. if (Process->CommitCharge > Process->CommitChargePeak) {
  1139. Process->CommitChargePeak = Process->CommitCharge;
  1140. }
  1141. MI_INCREMENT_TOTAL_PROCESS_COMMIT (QuotaCharge);
  1142. }
  1143. }
  1144. #if DBG
  1145. PteQuotaCharge = QuotaCharge;
  1146. #endif
  1147. //
  1148. // For all the PTEs in the specified address range, set the
  1149. // protection depending on the state of the PTE.
  1150. //
  1151. //
  1152. // If the PTE was copy on write (but not written) and the
  1153. // new protection is NOT copy-on-write, return page file quota
  1154. // and commitment.
  1155. //
  1156. PointerPxe = MiGetPxeAddress (StartingAddress);
  1157. PointerPpe = MiGetPpeAddress (StartingAddress);
  1158. PointerPde = MiGetPdeAddress (StartingAddress);
  1159. PointerPte = MiGetPteAddress (StartingAddress);
  1160. //
  1161. // Ensure the PDE (and any table above it) are still resident.
  1162. //
  1163. MiMakePdeExistAndMakeValid (PointerPde, Process, MM_NOIRQL);
  1164. QuotaCharge = 0;
  1165. while (PointerPte <= LastPte) {
  1166. if (MiIsPteOnPdeBoundary (PointerPte)) {
  1167. PointerPde = MiGetPteAddress (PointerPte);
  1168. PointerPpe = MiGetPdeAddress (PointerPte);
  1169. PointerPxe = MiGetPpeAddress (PointerPte);
  1170. MiMakePdeExistAndMakeValid (PointerPde, Process, MM_NOIRQL);
  1171. }
  1172. PteContents = *PointerPte;
  1173. if (PteContents.u.Long == 0) {
  1174. //
  1175. // Increment the count of non-zero page table entries
  1176. // for this page table and the number of private pages
  1177. // for the process.
  1178. //
  1179. UsedPageTableHandle = MI_GET_USED_PTES_HANDLE (MiGetVirtualAddressMappedByPte (PointerPte));
  1180. MI_INCREMENT_USED_PTES_BY_HANDLE (UsedPageTableHandle);
  1181. //
  1182. // The PTE is zero, set it into prototype PTE format
  1183. // with the protection in the prototype PTE.
  1184. //
  1185. TempPte = PrototypePte;
  1186. TempPte.u.Soft.Protection = ProtectionMask;
  1187. MI_WRITE_INVALID_PTE (PointerPte, TempPte);
  1188. }
  1189. else if (PteContents.u.Hard.Valid == 1) {
  1190. //
  1191. // Set the protection into both the PTE and the original PTE
  1192. // in the PFN database for private pages only.
  1193. //
  1194. NewProtectionMask = ProtectionMask;
  1195. Pfn1 = MI_PFN_ELEMENT (PteContents.u.Hard.PageFrameNumber);
  1196. #if DBG
  1197. if (PteIndex < PTES_TRACKED) {
  1198. PteTracker[PteIndex] = PteContents;
  1199. PfnTracker[PteIndex] = *Pfn1;
  1200. PteIndex += 1;
  1201. }
  1202. #endif
  1203. if ((NewProtect & PAGE_NOACCESS) ||
  1204. (NewProtect & PAGE_GUARD)) {
  1205. *Locked = MiRemovePageFromWorkingSet (PointerPte,
  1206. Pfn1,
  1207. &Process->Vm);
  1208. continue;
  1209. }
  1210. if (Pfn1->u3.e1.PrototypePte == 1) {
  1211. Va = (PULONG)MiGetVirtualAddressMappedByPte (PointerPte);
  1212. //
  1213. // Check to see if this is a prototype PTE. This
  1214. // is done by comparing the PTE address in the
  1215. // PFN database to the PTE address indicated by the VAD.
  1216. // If they are not equal, this is a fork prototype PTE.
  1217. //
  1218. if (Pfn1->PteAddress != MiGetProtoPteAddress (FoundVad,
  1219. MI_VA_TO_VPN ((PVOID)Va))) {
  1220. //
  1221. // This PTE refers to a fork prototype PTE, make it
  1222. // private. But don't charge quota for it if the PTE
  1223. // was already copy on write (because it's already
  1224. // been charged for this case).
  1225. //
  1226. if (MiCopyOnWrite ((PVOID)Va, PointerPte) == TRUE) {
  1227. if ((WriteCopy) && (PteContents.u.Hard.CopyOnWrite == 0)) {
  1228. QuotaCharge += 1;
  1229. }
  1230. }
  1231. //
  1232. // Ensure the PDE (and any table above it) are still
  1233. // resident (they may have been trimmed when the working
  1234. // set mutex was released above).
  1235. //
  1236. MiMakePdeExistAndMakeValid (PointerPde, Process, MM_NOIRQL);
  1237. //
  1238. // Do the loop again.
  1239. //
  1240. continue;
  1241. }
  1242. //
  1243. // Update the protection field in the WSLE and the PTE.
  1244. //
  1245. // If the PTE is copy on write uncharge the
  1246. // previously charged quota.
  1247. //
  1248. if ((!WriteCopy) && (PteContents.u.Hard.CopyOnWrite == 1)) {
  1249. QuotaCharge += 1;
  1250. }
  1251. //
  1252. // The true protection may be in the WSLE, locate it.
  1253. //
  1254. Index = MiLocateWsle ((PVOID)Va,
  1255. MmWorkingSetList,
  1256. Pfn1->u1.WsIndex);
  1257. MmWsle[Index].u1.e1.Protection = ProtectionMask;
  1258. MmWsle[Index].u1.e1.SameProtectAsProto = 0;
  1259. }
  1260. else {
  1261. //
  1262. // Page is private (copy on written), protection mask
  1263. // is stored in the original PTE field.
  1264. //
  1265. Pfn1->OriginalPte.u.Soft.Protection = ProtectionMaskNotCopy;
  1266. NewProtectionMask = ProtectionMaskNotCopy;
  1267. }
  1268. MI_SNAP_DATA (Pfn1, PointerPte, 7);
  1269. MI_MAKE_VALID_PTE (TempPte,
  1270. PteContents.u.Hard.PageFrameNumber,
  1271. NewProtectionMask,
  1272. PointerPte);
  1273. #if defined(_MIALT4K_)
  1274. //
  1275. // Preserve the split protections if they exist.
  1276. //
  1277. TempPte.u.Hard.Cache = PteContents.u.Hard.Cache;
  1278. #endif
  1279. WorkingSetIndex = MI_GET_WORKING_SET_FROM_PTE (&PteContents);
  1280. MI_SET_PTE_IN_WORKING_SET (&TempPte, WorkingSetIndex);
  1281. //
  1282. // Flush the TB as we have changed the protection
  1283. // of a valid PTE.
  1284. //
  1285. MiFlushTbAndCapture (FoundVad,
  1286. PointerPte,
  1287. TempPte,
  1288. Pfn1);
  1289. }
  1290. else if (PteContents.u.Soft.Prototype == 1) {
  1291. #if DBG
  1292. if (PteIndex < PTES_TRACKED) {
  1293. PteTracker[PteIndex] = PteContents;
  1294. *(PULONG)(&PfnTracker[PteIndex]) = 0x88;
  1295. PteIndex += 1;
  1296. }
  1297. #endif
  1298. //
  1299. // The PTE is in prototype PTE format.
  1300. //
  1301. // Is it a fork prototype PTE?
  1302. //
  1303. Va = (PULONG)MiGetVirtualAddressMappedByPte (PointerPte);
  1304. if ((PteContents.u.Soft.PageFileHigh != MI_PTE_LOOKUP_NEEDED) &&
  1305. (MiPteToProto (PointerPte) !=
  1306. MiGetProtoPteAddress (FoundVad,
  1307. MI_VA_TO_VPN ((PVOID)Va)))) {
  1308. //
  1309. // This PTE refers to a fork prototype PTE, make the
  1310. // page private. This is accomplished by releasing
  1311. // the working set mutex, reading the page thereby
  1312. // causing a fault, and re-executing the loop, hopefully,
  1313. // this time, we'll find the page present and will
  1314. // turn it into a private page.
  1315. //
  1316. // Note, that page with prototype = 1 cannot be
  1317. // no-access.
  1318. //
  1319. DoAgain = TRUE;
  1320. while (PteContents.u.Hard.Valid == 0) {
  1321. UNLOCK_WS_UNSAFE (Process);
  1322. WsHeld = FALSE;
  1323. try {
  1324. *(volatile ULONG *)Va;
  1325. } except (EXCEPTION_EXECUTE_HANDLER) {
  1326. if (GetExceptionCode() != STATUS_GUARD_PAGE_VIOLATION) {
  1327. //
  1328. // The prototype PTE must be noaccess.
  1329. //
  1330. WsHeld = TRUE;
  1331. LOCK_WS_UNSAFE (Process);
  1332. MiMakePdeExistAndMakeValid (PointerPde,
  1333. Process,
  1334. MM_NOIRQL);
  1335. if (MiChangeNoAccessForkPte (PointerPte, ProtectionMask) == TRUE) {
  1336. DoAgain = FALSE;
  1337. }
  1338. }
  1339. }
  1340. PointerPpe = MiGetPteAddress (PointerPde);
  1341. PointerPxe = MiGetPdeAddress (PointerPde);
  1342. if (WsHeld == FALSE) {
  1343. LOCK_WS_UNSAFE (Process);
  1344. }
  1345. MiMakePdeExistAndMakeValid (PointerPde, Process, MM_NOIRQL);
  1346. PteContents = *PointerPte;
  1347. }
  1348. if (DoAgain) {
  1349. continue;
  1350. }
  1351. }
  1352. else {
  1353. //
  1354. // If the new protection is not write-copy, the PTE
  1355. // protection is not in the prototype PTE (can't be
  1356. // write copy for sections), and the protection in
  1357. // the PTE is write-copy, release the page file
  1358. // quota and commitment for this page.
  1359. //
  1360. if ((!WriteCopy) &&
  1361. (PteContents.u.Soft.PageFileHigh == MI_PTE_LOOKUP_NEEDED)) {
  1362. if (MI_IS_PTE_PROTECTION_COPY_WRITE(PteContents.u.Soft.Protection)) {
  1363. QuotaCharge += 1;
  1364. }
  1365. }
  1366. //
  1367. // The PTE is a prototype PTE. Make the high part
  1368. // of the PTE indicate that the protection field
  1369. // is in the PTE itself.
  1370. //
  1371. MI_WRITE_INVALID_PTE (PointerPte, PrototypePte);
  1372. PointerPte->u.Soft.Protection = ProtectionMask;
  1373. }
  1374. }
  1375. else if (PteContents.u.Soft.Transition == 1) {
  1376. #if DBG
  1377. if (PteIndex < PTES_TRACKED) {
  1378. PteTracker[PteIndex] = PteContents;
  1379. Pfn1 = MI_PFN_ELEMENT (MI_GET_PAGE_FRAME_FROM_TRANSITION_PTE(&PteContents));
  1380. PfnTracker[PteIndex] = *Pfn1;
  1381. PteIndex += 1;
  1382. }
  1383. #endif
  1384. //
  1385. // This is a transition PTE. (Page is private)
  1386. //
  1387. if (MiSetProtectionOnTransitionPte (
  1388. PointerPte,
  1389. ProtectionMaskNotCopy)) {
  1390. continue;
  1391. }
  1392. }
  1393. else {
  1394. #if DBG
  1395. if (PteIndex < PTES_TRACKED) {
  1396. PteTracker[PteIndex] = PteContents;
  1397. *(PULONG)(&PfnTracker[PteIndex]) = 0x99;
  1398. PteIndex += 1;
  1399. }
  1400. #endif
  1401. //
  1402. // Must be page file space or demand zero.
  1403. //
  1404. PointerPte->u.Soft.Protection = ProtectionMaskNotCopy;
  1405. }
  1406. PointerPte += 1;
  1407. }
  1408. //
  1409. // Return the quota charge and the commitment, if any.
  1410. //
  1411. if ((QuotaCharge > 0) && (!DontCharge)) {
  1412. MiReturnCommitment (QuotaCharge);
  1413. MM_TRACK_COMMIT (MM_DBG_COMMIT_RETURN_PROTECTION, QuotaCharge);
  1414. PsReturnProcessPageFileQuota (Process, QuotaCharge);
  1415. #if DBG
  1416. if (QuotaCharge > FoundVad->u.VadFlags.CommitCharge) {
  1417. DbgPrint ("MMPROTECT QUOTA FAILURE: %p %p %x %p\n",
  1418. PteTracker, PfnTracker, PteIndex, PteQuotaCharge);
  1419. DbgBreakPoint ();
  1420. }
  1421. #endif
  1422. ASSERT (QuotaCharge <= FoundVad->u.VadFlags.CommitCharge);
  1423. FoundVad->u.VadFlags.CommitCharge -= QuotaCharge;
  1424. if (Process->JobStatus & PS_JOB_STATUS_REPORT_COMMIT_CHANGES) {
  1425. PsChangeJobMemoryUsage(PS_JOB_STATUS_REPORT_COMMIT_CHANGES, -(SSIZE_T)QuotaCharge);
  1426. }
  1427. Process->CommitCharge -= QuotaCharge;
  1428. MI_INCREMENT_TOTAL_PROCESS_COMMIT (0 - QuotaCharge);
  1429. }
  1430. UNLOCK_WS_UNSAFE (Process);
  1431. return STATUS_SUCCESS;
  1432. }
  1433. ULONG
  1434. MiGetPageProtection (
  1435. IN PMMPTE PointerPte,
  1436. IN PEPROCESS Process,
  1437. IN LOGICAL PteCapturedToLocalStack
  1438. )
  1439. /*++
  1440. Routine Description:
  1441. This routine returns the page protection of a non-zero PTE.
  1442. It may release and reacquire the working set mutex.
  1443. Arguments:
  1444. PointerPte - Supplies a pointer to a non-zero PTE.
  1445. Process - Supplies the relevant process if its working set mutex is held.
  1446. PteCapturedToLocalStack - Supplies TRUE if PointerPte points at a
  1447. captured local stack location.
  1448. Return Value:
  1449. Returns the protection code.
  1450. Environment:
  1451. Kernel mode, working set and address creation mutex held.
  1452. Note, that the address creation mutex does not need to be held
  1453. if the working set mutex does not need to be released in the
  1454. case of a prototype PTE.
  1455. --*/
  1456. {
  1457. MMPTE PteContents;
  1458. MMPTE ProtoPteContents;
  1459. PMMPFN Pfn1;
  1460. PMMPTE ProtoPteAddress;
  1461. PVOID Va;
  1462. WSLE_NUMBER Index;
  1463. PAGED_CODE();
  1464. //
  1465. // Initializing ProtoPteContents is not needed for correctness,
  1466. // but without it the compiler cannot compile this code W4 to check
  1467. // for use of uninitialized variables.
  1468. //
  1469. ProtoPteContents = ZeroKernelPte;
  1470. PteContents = *PointerPte;
  1471. if ((PteContents.u.Soft.Valid == 0) && (PteContents.u.Soft.Prototype == 1)) {
  1472. //
  1473. // This PTE is in prototype format, the protection is
  1474. // stored in the prototype PTE.
  1475. //
  1476. if ((MI_IS_PTE_PROTOTYPE(PointerPte)) ||
  1477. (PteCapturedToLocalStack == TRUE) ||
  1478. (PteContents.u.Soft.PageFileHigh == MI_PTE_LOOKUP_NEEDED)) {
  1479. //
  1480. // The protection is within this PTE.
  1481. //
  1482. return MI_CONVERT_FROM_PTE_PROTECTION (
  1483. PteContents.u.Soft.Protection);
  1484. }
  1485. ProtoPteAddress = MiPteToProto (PointerPte);
  1486. //
  1487. // Capture protopte PTE contents.
  1488. //
  1489. ProtoPteContents = MiCaptureSystemPte (ProtoPteAddress, Process);
  1490. //
  1491. // The working set mutex may have been released and the
  1492. // page may no longer be in prototype format, get the
  1493. // new contents of the PTE and obtain the protection mask.
  1494. //
  1495. PteContents = MiCaptureSystemPte (PointerPte, Process);
  1496. }
  1497. if ((PteContents.u.Soft.Valid == 0) && (PteContents.u.Soft.Prototype == 1)) {
  1498. //
  1499. // Pte is still prototype, return the protection captured
  1500. // from the prototype PTE.
  1501. //
  1502. if (ProtoPteContents.u.Hard.Valid == 1) {
  1503. //
  1504. // The prototype PTE is valid, get the protection from
  1505. // the PFN database.
  1506. //
  1507. Pfn1 = MI_PFN_ELEMENT (ProtoPteContents.u.Hard.PageFrameNumber);
  1508. return MI_CONVERT_FROM_PTE_PROTECTION(
  1509. Pfn1->OriginalPte.u.Soft.Protection);
  1510. }
  1511. else {
  1512. //
  1513. // The prototype PTE is not valid, return the protection from the
  1514. // PTE.
  1515. //
  1516. return MI_CONVERT_FROM_PTE_PROTECTION (
  1517. ProtoPteContents.u.Soft.Protection);
  1518. }
  1519. }
  1520. if (PteContents.u.Hard.Valid == 1) {
  1521. //
  1522. // The page is valid, the protection field is either in the
  1523. // PFN database original PTE element or the WSLE. If
  1524. // the page is private, get it from the PFN original PTE
  1525. // element, else use the WSLE.
  1526. //
  1527. Pfn1 = MI_PFN_ELEMENT (PteContents.u.Hard.PageFrameNumber);
  1528. if ((Pfn1->u3.e1.PrototypePte == 0) ||
  1529. (PteCapturedToLocalStack == TRUE) ||
  1530. (MI_IS_PTE_PROTOTYPE(PointerPte))) {
  1531. if (Pfn1->u4.AweAllocation == 1) {
  1532. //
  1533. // This is an AWE frame - the original PTE field in the PFN
  1534. // actually contains the AweReferenceCount. Since these pages
  1535. // are always readwrite, just return it as such.
  1536. //
  1537. return PAGE_READWRITE;
  1538. }
  1539. //
  1540. // This is a private PTE or the PTE address is that of a
  1541. // prototype PTE, hence the protection is in
  1542. // the original PTE.
  1543. //
  1544. return MI_CONVERT_FROM_PTE_PROTECTION(
  1545. Pfn1->OriginalPte.u.Soft.Protection);
  1546. }
  1547. //
  1548. // The PTE was a hardware PTE, get the protection
  1549. // from the WSLE.
  1550. Va = (PULONG)MiGetVirtualAddressMappedByPte (PointerPte);
  1551. Index = MiLocateWsle ((PVOID)Va,
  1552. MmWorkingSetList,
  1553. Pfn1->u1.WsIndex);
  1554. return MI_CONVERT_FROM_PTE_PROTECTION (MmWsle[Index].u1.e1.Protection);
  1555. }
  1556. //
  1557. // PTE is either demand zero or transition, in either
  1558. // case protection is in PTE.
  1559. //
  1560. return MI_CONVERT_FROM_PTE_PROTECTION (PteContents.u.Soft.Protection);
  1561. }
  1562. ULONG
  1563. MiChangeNoAccessForkPte (
  1564. IN PMMPTE PointerPte,
  1565. IN ULONG ProtectionMask
  1566. )
  1567. /*++
  1568. Routine Description:
  1569. Arguments:
  1570. PointerPte - Supplies a pointer to the current PTE.
  1571. ProtectionMask - Supplies the protection mask to set.
  1572. Return Value:
  1573. TRUE if the loop does NOT need to be repeated for this PTE, FALSE
  1574. if it does need retrying.
  1575. Environment:
  1576. Kernel mode, address creation mutex held, APCs disabled.
  1577. --*/
  1578. {
  1579. PAGED_CODE();
  1580. if (ProtectionMask == MM_NOACCESS) {
  1581. //
  1582. // No need to change the page protection.
  1583. //
  1584. return TRUE;
  1585. }
  1586. PointerPte->u.Proto.ReadOnly = 1;
  1587. return FALSE;
  1588. }
  1589. VOID
  1590. MiFlushTbAndCapture (
  1591. IN PMMVAD FoundVad,
  1592. IN PMMPTE PointerPte,
  1593. IN MMPTE TempPte,
  1594. IN PMMPFN Pfn1
  1595. )
  1596. /*++
  1597. Routine Description:
  1598. Nonpagable helper routine to change a PTE & flush the relevant TB entry.
  1599. Arguments:
  1600. FoundVad - Supplies a writewatch VAD to update or NULL.
  1601. PointerPte - Supplies the PTE to update.
  1602. TempPte - Supplies the new PTE contents.
  1603. Pfn1 - Supplies the PFN database entry to update.
  1604. Return Value:
  1605. None.
  1606. Environment:
  1607. Kernel mode.
  1608. --*/
  1609. {
  1610. MMPTE PreviousPte;
  1611. KIRQL OldIrql;
  1612. PVOID VirtualAddress;
  1613. VirtualAddress = MiGetVirtualAddressMappedByPte (PointerPte);
  1614. //
  1615. // Flush the TB as we have changed the protection of a valid PTE.
  1616. //
  1617. LOCK_PFN (OldIrql);
  1618. PreviousPte = *PointerPte;
  1619. MI_WRITE_VALID_PTE_NEW_PROTECTION (PointerPte, TempPte);
  1620. ASSERT (PreviousPte.u.Hard.Valid == 1);
  1621. KeFlushSingleTb (VirtualAddress, FALSE);
  1622. ASSERT (PreviousPte.u.Hard.Valid == 1);
  1623. //
  1624. // A page protection is being changed, on certain
  1625. // hardware the dirty bit should be ORed into the
  1626. // modify bit in the PFN element.
  1627. //
  1628. MI_CAPTURE_DIRTY_BIT_TO_PFN (&PreviousPte, Pfn1);
  1629. //
  1630. // If the PTE indicates the page has been modified (this is different
  1631. // from the PFN indicating this), then ripple it back to the write watch
  1632. // bitmap now since we are still in the correct process context.
  1633. //
  1634. if (FoundVad->u.VadFlags.WriteWatch == 1) {
  1635. ASSERT ((PsGetCurrentProcess()->Flags & PS_PROCESS_FLAGS_USING_WRITE_WATCH));
  1636. ASSERT (Pfn1->u3.e1.PrototypePte == 0);
  1637. if (MI_IS_PTE_DIRTY(PreviousPte)) {
  1638. //
  1639. // This process has (or had) write watch VADs. Update the
  1640. // bitmap now.
  1641. //
  1642. MiCaptureWriteWatchDirtyBit (PsGetCurrentProcess(), VirtualAddress);
  1643. }
  1644. }
  1645. UNLOCK_PFN (OldIrql);
  1646. return;
  1647. }
  1648. ULONG
  1649. MiSetProtectionOnTransitionPte (
  1650. IN PMMPTE PointerPte,
  1651. IN ULONG ProtectionMask
  1652. )
  1653. /*++
  1654. Routine Description:
  1655. Nonpagable helper routine to change the protection of a transition PTE.
  1656. Arguments:
  1657. PointerPte - Supplies a pointer to the PTE.
  1658. ProtectionMask - Supplies the new protection mask.
  1659. Return Value:
  1660. TRUE if the caller needs to retry, FALSE if the protection was successfully
  1661. changed.
  1662. Environment:
  1663. Kernel mode. The PFN lock is needed to ensure the (private) page
  1664. doesn't become non-transition.
  1665. --*/
  1666. {
  1667. KIRQL OldIrql;
  1668. MMPTE PteContents;
  1669. PMMPFN Pfn1;
  1670. LOCK_PFN (OldIrql);
  1671. //
  1672. // Make sure the page is still a transition page.
  1673. //
  1674. PteContents = *PointerPte;
  1675. if ((PteContents.u.Soft.Prototype == 0) &&
  1676. (PointerPte->u.Soft.Transition == 1)) {
  1677. Pfn1 = MI_PFN_ELEMENT (PteContents.u.Trans.PageFrameNumber);
  1678. Pfn1->OriginalPte.u.Soft.Protection = ProtectionMask;
  1679. PointerPte->u.Soft.Protection = ProtectionMask;
  1680. UNLOCK_PFN (OldIrql);
  1681. return FALSE;
  1682. }
  1683. //
  1684. // Do this loop again for the same PTE.
  1685. //
  1686. UNLOCK_PFN (OldIrql);
  1687. return TRUE;
  1688. }
  1689. MMPTE
  1690. MiCaptureSystemPte (
  1691. IN PMMPTE PointerProtoPte,
  1692. IN PEPROCESS Process
  1693. )
  1694. /*++
  1695. Routine Description:
  1696. Nonpagable helper routine to capture the contents of a pagable PTE.
  1697. Arguments:
  1698. PointerProtoPte - Supplies a pointer to the prototype PTE.
  1699. Process - Supplies the relevant process.
  1700. Return Value:
  1701. PTE contents.
  1702. Environment:
  1703. Kernel mode. Caller holds address space and working set mutexes if
  1704. Process is set. Working set mutex was acquired unsafe.
  1705. --*/
  1706. {
  1707. MMPTE TempPte;
  1708. KIRQL OldIrql;
  1709. PMMPTE PointerPde;
  1710. PointerPde = MiGetPteAddress (PointerProtoPte);
  1711. LOCK_PFN (OldIrql);
  1712. if (PointerPde->u.Hard.Valid == 0) {
  1713. MiMakeSystemAddressValidPfnWs (PointerProtoPte, Process, OldIrql);
  1714. }
  1715. TempPte = *PointerProtoPte;
  1716. UNLOCK_PFN (OldIrql);
  1717. return TempPte;
  1718. }
  1719. NTSTATUS
  1720. MiCheckSecuredVad (
  1721. IN PMMVAD Vad,
  1722. IN PVOID Base,
  1723. IN SIZE_T Size,
  1724. IN ULONG ProtectionMask
  1725. )
  1726. /*++
  1727. Routine Description:
  1728. This routine checks to see if the specified VAD is secured in such
  1729. a way as to conflict with the address range and protection mask
  1730. specified.
  1731. Arguments:
  1732. Vad - Supplies a pointer to the VAD containing the address range.
  1733. Base - Supplies the base of the range the protection starts at.
  1734. Size - Supplies the size of the range.
  1735. ProtectionMask - Supplies the protection mask being set.
  1736. Return Value:
  1737. Status value.
  1738. Environment:
  1739. Kernel mode.
  1740. --*/
  1741. {
  1742. PVOID End;
  1743. PLIST_ENTRY Next;
  1744. PMMSECURE_ENTRY Entry;
  1745. NTSTATUS Status = STATUS_SUCCESS;
  1746. End = (PVOID)((PCHAR)Base + Size);
  1747. if (ProtectionMask < MM_SECURE_DELETE_CHECK) {
  1748. if ((Vad->u.VadFlags.NoChange == 1) &&
  1749. (Vad->u2.VadFlags2.SecNoChange == 1) &&
  1750. (Vad->u.VadFlags.Protection != ProtectionMask)) {
  1751. //
  1752. // An attempt is made at changing the protection
  1753. // of a SEC_NO_CHANGE section - return an error.
  1754. //
  1755. Status = STATUS_INVALID_PAGE_PROTECTION;
  1756. goto done;
  1757. }
  1758. }
  1759. else {
  1760. //
  1761. // Deletion - set to no-access for check. SEC_NOCHANGE allows
  1762. // deletion, but does not allow page protection changes.
  1763. //
  1764. ProtectionMask = 0;
  1765. }
  1766. if (Vad->u2.VadFlags2.OneSecured) {
  1767. if (((ULONG_PTR)Base <= ((PMMVAD_LONG)Vad)->u3.Secured.EndVpn) &&
  1768. ((ULONG_PTR)End >= ((PMMVAD_LONG)Vad)->u3.Secured.StartVpn)) {
  1769. //
  1770. // This region conflicts, check the protections.
  1771. //
  1772. if (ProtectionMask & MM_GUARD_PAGE) {
  1773. Status = STATUS_INVALID_PAGE_PROTECTION;
  1774. goto done;
  1775. }
  1776. if (Vad->u2.VadFlags2.ReadOnly) {
  1777. if (MmReadWrite[ProtectionMask] < 10) {
  1778. Status = STATUS_INVALID_PAGE_PROTECTION;
  1779. goto done;
  1780. }
  1781. }
  1782. else {
  1783. if (MmReadWrite[ProtectionMask] < 11) {
  1784. Status = STATUS_INVALID_PAGE_PROTECTION;
  1785. goto done;
  1786. }
  1787. }
  1788. }
  1789. }
  1790. else if (Vad->u2.VadFlags2.MultipleSecured) {
  1791. Next = ((PMMVAD_LONG)Vad)->u3.List.Flink;
  1792. do {
  1793. Entry = CONTAINING_RECORD( Next,
  1794. MMSECURE_ENTRY,
  1795. List);
  1796. if (((ULONG_PTR)Base <= Entry->EndVpn) &&
  1797. ((ULONG_PTR)End >= Entry->StartVpn)) {
  1798. //
  1799. // This region conflicts, check the protections.
  1800. //
  1801. if (ProtectionMask & MM_GUARD_PAGE) {
  1802. Status = STATUS_INVALID_PAGE_PROTECTION;
  1803. goto done;
  1804. }
  1805. if (Entry->u2.VadFlags2.ReadOnly) {
  1806. if (MmReadWrite[ProtectionMask] < 10) {
  1807. Status = STATUS_INVALID_PAGE_PROTECTION;
  1808. goto done;
  1809. }
  1810. }
  1811. else {
  1812. if (MmReadWrite[ProtectionMask] < 11) {
  1813. Status = STATUS_INVALID_PAGE_PROTECTION;
  1814. goto done;
  1815. }
  1816. }
  1817. }
  1818. Next = Entry->List.Flink;
  1819. } while (Entry->List.Flink != &((PMMVAD_LONG)Vad)->u3.List);
  1820. }
  1821. done:
  1822. return Status;
  1823. }