Source code of Windows XP (NT5)
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.

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