test: protect.c Source File

Main Page | Class Hierarchy | Class List | File List | Class Members | File Members

# protect.c

Go to the documentation of this file.

    00001 /*++
    00002
    00003 Copyright (c) 1989  Microsoft Corporation
    00004
    00005 Module Name:
    00006
    00007    protect.c
    00008
    00009 Abstract:
    00010
    00011     This module contains the routines which implement the
    00012     NtProtectVirtualMemory service.
    00013
    00014 Author:
    00015
    00016     Lou Perazzoli (loup) 18-Aug-1989
    00017     Landy Wang (landyw) 02-June-1997
    00018
    00019 Revision History:
    00020
    00021 --*/
    00022
    00023 #include "mi.h"
    00024
    00025 #if DBG
    00026 PEPROCESS MmWatchProcess;
    00027 VOID MmFooBar(VOID);
    00028 #endif // DBG
    00029
    00030 HARDWARE_PTE
    00031 MiFlushTbAndCapture(
    00032     IN PMMPTE PtePointer,
    00033     IN HARDWARE_PTE TempPte,
    00034     IN PMMPFN Pfn1
    00035     );
    00036
    00037 ULONG
    00038 MiSetProtectionOnTransitionPte (
    00039     IN PMMPTE PointerPte,
    00040     IN ULONG ProtectionMask
    00041     );
    00042
    00043 MMPTE
    00044 MiCaptureSystemPte (
    00045     IN PMMPTE PointerProtoPte,
    00046     IN PEPROCESS Process
    00047     );
    00048
    00049 #if PFN_CONSISTENCY
    00050 VOID
    00051 MiSetOriginalPteProtection (
    00052     IN PMMPFN Pfn1,
    00053     IN ULONG ProtectionMask
    00054     )
    00055 #endif // PFN_CONSISTENCY
    00056
    00057 extern CCHAR MmReadWrite[32];
    00058
    00059 #ifdef ALLOC_PRAGMA
    00060 #pragma alloc_text(PAGE,NtProtectVirtualMemory)
    00061 #pragma alloc_text(PAGE,MiProtectVirtualMemory)
    00062 #pragma alloc_text(PAGE,MiSetProtectionOnSection)
    00063 #pragma alloc_text(PAGE,MiGetPageProtection)
    00064 #pragma alloc_text(PAGE,MiChangeNoAccessForkPte)
    00065 #endif
    00066
    00067
    00068 NTSTATUS
    00069 NtProtectVirtualMemory(
    00070      IN HANDLE ProcessHandle,
    00071      IN OUT PVOID *BaseAddress,
    00072      IN OUT PSIZE_T RegionSize,
    00073      IN ULONG NewProtect,
    00074      OUT PULONG OldProtect
    00075      )
    00076
    00077 /*++
    00078
    00079 Routine Description:
    00080
    00081     This routine changes the protection on a region of committed pages
    00082     within the virtual address space of the subject process.  Setting
    00083     the protection on a range of pages causes the old protection to be
    00084     replaced by the specified protection value.
    00085
    00086 Arguments:
    00087
    00088      ProcessHandle - An open handle to a process object.
    00089
    00090      BaseAddress - The base address of the region of pages
    00091           whose protection is to be changed. This value is
    00092           rounded down to the next host page address
    00093           boundary.
    00094
    00095      RegionSize - A pointer to a variable that will receive
    00096           the actual size in bytes of the protected region
    00097           of pages. The initial value of this argument is
    00098           rounded up to the next host page size boundary.
    00099
    00100      NewProtect - The new protection desired for the
    00101           specified region of pages.
    00102
    00103      Protect Values
    00104
    00105           PAGE_NOACCESS - No access to the specified region
    00106                of pages is allowed. An attempt to read,
    00107                write, or execute the specified region
    00108                results in an access violation (i.e. a GP
    00109                fault).
    00110
    00111           PAGE_EXECUTE - Execute access to the specified
    00112                region of pages is allowed. An attempt to
    00113                read or write the specified region results in
    00114                an access violation.
    00115
    00116           PAGE_READONLY - Read only and execute access to the
    00117                specified region of pages is allowed. An
    00118                attempt to write the specified region results
    00119                in an access violation.
    00120
    00121           PAGE_READWRITE - Read, write, and execute access to
    00122                the specified region of pages is allowed. If
    00123                write access to the underlying section is
    00124                allowed, then a single copy of the pages are
    00125                shared. Otherwise the pages are shared read
    00126                only/copy on write.
    00127
    00128           PAGE_GUARD - Read, write, and execute access to the
    00129                specified region of pages is allowed,
    00130                however, access to the region causes a "guard
    00131                region entered" condition to be raised in the
    00132                subject process. If write access to the
    00133                underlying section is allowed, then a single
    00134                copy of the pages are shared. Otherwise the
    00135                pages are shared read only/copy on write.
    00136
    00137           PAGE_NOCACHE - The page should be treated as uncached.
    00138                This is only valid for non-shared pages.
    00139
    00140
    00141      OldProtect - A pointer to a variable that will receive
    00142           the old protection of the first page within the
    00143           specified region of pages.
    00144
    00145 Return Value:
    00146
    00147     Returns the status
    00148
    00149     TBS
    00150
    00151
    00152 Environment:
    00153
    00154     Kernel mode.
    00155
    00156 --*/
    00157
    00158
    00159 {
    00160     //
    00161     // note - special treatment for the following cases...
    00162     //
    00163     // if a page is locked in the working set (memory?) and the
    00164     // protection is changed to no access, the page should be
    00165     // removed from the working set... valid pages can't be no access.
    00166     //
    00167     // if page is going to be read only or no access? and is demand
    00168     // zero, make sure it is changed to a page of zeroes.
    00169     //
    00170     // update the vm spec to explain locked pages are unlocked when
    00171     // freed or protection is changed to no-access (this may be a nasty
    00172     // problem if we don't want to do this!!
    00173     //
    00174
    00175     PEPROCESS Process;
    00176     KPROCESSOR_MODE PreviousMode;
    00177     NTSTATUS Status;
    00178     ULONG Attached = FALSE;
    00179     PVOID CapturedBase;
    00180     SIZE_T CapturedRegionSize;
    00181     ULONG ProtectionMask;
    00182
    00183     ULONG LastProtect;
    00184
    00185     PAGED_CODE();
    00186
    00187     //
    00188     // Check the protection field.  This could raise an exception.
    00189     //
    00190
    00191     try {
    00192         ProtectionMask = MiMakeProtectionMask (NewProtect);
    00193     } except (EXCEPTION_EXECUTE_HANDLER) {
    00194         return GetExceptionCode();
    00195     }
    00196
    00197     PreviousMode = KeGetPreviousMode();
    00198
    00199     if (PreviousMode != KernelMode) {
    00200
    00201         //
    00202         // Capture the region size and base address under an exception handler.
    00203         //
    00204
    00205         try {
    00206
    00207             ProbeForWritePointer (BaseAddress);
    00208             ProbeForWriteUlong_ptr (RegionSize);
    00209             ProbeForWriteUlong (OldProtect);
    00210
    00211             //
    00212             // Capture the region size and base address.
    00213             //
    00214
    00215             CapturedBase = *BaseAddress;
    00216             CapturedRegionSize = *RegionSize;
    00217
    00218         } except (EXCEPTION_EXECUTE_HANDLER) {
    00219
    00220             //
    00221             // If an exception occurs during the probe or capture
    00222             // of the initial values, then handle the exception and
    00223             // return the exception code as the status value.
    00224             //
    00225
    00226             return GetExceptionCode();
    00227         }
    00228
    00229     } else {
    00230
    00231         //
    00232         // Capture the region size and base address.
    00233         //
    00234
    00235         CapturedRegionSize = *RegionSize;
    00236         CapturedBase = *BaseAddress;
    00237     }
    00238
    00239 #if DBG
    00240     if (MmDebug & MM_DBG_SHOW_NT_CALLS) {
    00241         if ( !MmWatchProcess ) {
    00242             DbgPrint("protectvm process handle %lx base address %lx size %lx protect %lx\n",
    00243                 ProcessHandle, CapturedBase, CapturedRegionSize, NewProtect);
    00244         }
    00245     }
    00246 #endif
    00247
    00248     //
    00249     // Make sure the specified starting and ending addresses are
    00250     // within the user part of the virtual address space.
    00251     //
    00252
    00253     if (CapturedBase > MM_HIGHEST_USER_ADDRESS) {
    00254
    00255         //
    00256         // Invalid base address.
    00257         //
    00258
    00259         return STATUS_INVALID_PARAMETER_2;
    00260     }
    00261
    00262     if ((ULONG_PTR)MM_HIGHEST_USER_ADDRESS - (ULONG_PTR)CapturedBase <
    00263                   CapturedRegionSize) {
    00264
    00265         //
    00266         // Invalid region size;
    00267         //
    00268
    00269         return STATUS_INVALID_PARAMETER_3;
    00270     }
    00271
    00272     if (CapturedRegionSize == 0) {
    00273         return STATUS_INVALID_PARAMETER_3;
    00274     }
    00275
    00276     Status = ObReferenceObjectByHandle ( ProcessHandle,
    00277                                          PROCESS_VM_OPERATION,
    00278                                          PsProcessType,
    00279                                          PreviousMode,
    00280                                          (PVOID *)&Process,
    00281                                          NULL );
    00282
    00283     if (!NT_SUCCESS(Status)) {
    00284         return Status;
    00285     }
    00286
    00287     //
    00288     // If the specified process is not the current process, attach
    00289     // to the specified process.
    00290     //
    00291
    00292     if (PsGetCurrentProcess() != Process) {
    00293         KeAttachProcess (&Process->Pcb);
    00294         Attached = TRUE;
    00295     }
    00296
    00297     Status = MiProtectVirtualMemory (Process,
    00298                                      &CapturedBase,
    00299                                      &CapturedRegionSize,
    00300                                      NewProtect,
    00301                                      &LastProtect);
    00302
    00303
    00304     if (Attached) {
    00305         KeDetachProcess();
    00306     }
    00307
    00308     ObDereferenceObject (Process);
    00309
    00310     //
    00311     // Establish an exception handler and write the size and base
    00312     // address.
    00313     //
    00314
    00315     try {
    00316
    00317         //
    00318         // Reprobe the addresses as certain architectures (intel 386 for one)
    00319         // do not trap kernel writes.  This is the one service which allows
    00320         // the protection of the page to change between the initial probe
    00321         // and the final argument update.
    00322         //
    00323
    00324         if (PreviousMode != KernelMode) {
    00325
    00326             ProbeForWritePointer (BaseAddress);
    00327             ProbeForWriteUlong_ptr (RegionSize);
    00328             ProbeForWriteUlong (OldProtect);
    00329         }
    00330
    00331         *RegionSize = CapturedRegionSize;
    00332         *BaseAddress = CapturedBase;
    00333         *OldProtect = LastProtect;
    00334
    00335     } except (EXCEPTION_EXECUTE_HANDLER) {
    00336         NOTHING;
    00337     }
    00338
    00339     return Status;
    00340 }
    00341
    00342
    00343 NTSTATUS
    00344 MiProtectVirtualMemory (
    00345     IN PEPROCESS Process,
    00346     IN PVOID *BaseAddress,
    00347     IN PSIZE_T RegionSize,
    00348     IN ULONG NewProtect,
    00349     IN PULONG LastProtect
    00350     )
    00351
    00352 /*++
    00353
    00354 Routine Description:
    00355
    00356     This routine changes the protection on a region of committed pages
    00357     within the virtual address space of the subject process.  Setting
    00358     the protection on a range of pages causes the old protection to be
    00359     replaced by the specified protection value.
    00360
    00361 Arguments:
    00362
    00363     Process - Supplies a pointer to the current process.
    00364
    00365     BaseAddress - Supplies the starting address to protect.
    00366
    00367     RegionsSize - Supplies the size of the region to protect.
    00368
    00369     NewProtect - Supplies the new protection to set.
    00370
    00371     LastProtect - Supplies the address of a kernel owned pointer to
    00372                 store (without probing) the old protection into.
    00373
    00374
    00375 Return Value:
    00376
    00377     the status of the protect operation.
    00378
    00379 Environment:
    00380
    00381     Kernel mode
    00382
    00383 --*/
    00384
    00385 {
    00386     PMMVAD FoundVad;
    00387     PVOID StartingAddress;
    00388     PVOID EndingAddress;
    00389     PVOID CapturedBase;
    00390     SIZE_T CapturedRegionSize;
    00391     NTSTATUS Status;
    00392     ULONG Attached;
    00393     PMMPTE PointerPte;
    00394     PMMPTE LastPte;
    00395     PMMPTE PointerPde;
    00396     PMMPTE PointerPpe;
    00397     PMMPTE PointerProtoPte;
    00398     PMMPTE LastProtoPte;
    00399     PMMPFN Pfn1;
    00400     ULONG CapturedOldProtect;
    00401     ULONG ProtectionMask;
    00402     MMPTE TempPte;
    00403     MMPTE PteContents;
    00404     MMPTE PreviousPte;
    00405     ULONG Locked;
    00406     PVOID Va;
    00407     ULONG DoAgain;
    00408     ULONG Waited;
    00409     PVOID UsedPageTableHandle;
    00410     PVOID UsedPageDirectoryHandle;
    00411     ULONG WorkingSetIndex;
    00412 #if defined(_MIALT4K_)
    00413     PVOID OriginalBase;
    00414     SIZE_T OriginalRegionSize;
    00415     ULONG OriginalProtectionMask;
    00416     PVOID StartingAddressFor4k;
    00417     PVOID EndingAddressFor4k;
    00418     SIZE_T CapturedRegionSizeFor4k;
    00419     ULONG CapturedOldProtectFor4k;
    00420     BOOLEAN EmulationFor4kPage = FALSE;
    00421 #endif
    00422
    00423     Attached = FALSE;
    00424     Locked = FALSE;
    00425
    00426     //
    00427     // Get the address creation mutex to block multiple threads from
    00428     // creating or deleting address space at the same time.
    00429     // Get the working set mutex so PTEs can be modified.
    00430     // Block APCs so an APC which takes a page
    00431     // fault does not corrupt various structures.
    00432     //
    00433
    00434     CapturedBase = *BaseAddress;
    00435     CapturedRegionSize = *RegionSize;
    00436
    00437 #if defined(_MIALT4K_)
    00438     OriginalBase = CapturedBase;
    00439     OriginalRegionSize = CapturedRegionSize;
    00440
    00441     if (Process->Wow64Process != NULL) {
    00442
    00443         StartingAddressFor4k = (PVOID)PAGE_4K_ALIGN(OriginalBase);
    00444
    00445         EndingAddressFor4k = (PVOID)(((ULONG_PTR)OriginalBase +
    00446                                       OriginalRegionSize - 1) | (PAGE_4K - 1));
    00447
    00448         CapturedRegionSizeFor4k = (ULONG_PTR)EndingAddressFor4k -
    00449             (ULONG_PTR)StartingAddressFor4k + 1L;
    00450
    00451         OriginalProtectionMask = MiMakeProtectionMask(NewProtect);
    00452
    00453         EmulationFor4kPage = TRUE;
    00454
    00455     }
    00456 #endif
    00457
    00458     try {
    00459         ProtectionMask = MiMakeProtectionMask (NewProtect);
    00460     } except (EXCEPTION_EXECUTE_HANDLER) {
    00461         return GetExceptionCode();
    00462     }
    00463
    00464     LOCK_WS_AND_ADDRESS_SPACE (Process);
    00465
    00466     //
    00467     // Make sure the address space was not deleted, if so, return an error.
    00468     //
    00469
    00470     if (Process->AddressSpaceDeleted != 0) {
    00471         Status = STATUS_PROCESS_IS_TERMINATING;
    00472         goto ErrorFound;
    00473     }
    00474
    00475     EndingAddress = (PVOID)(((ULONG_PTR)CapturedBase +
    00476                                 CapturedRegionSize - 1L) | (PAGE_SIZE - 1L));
    00477     StartingAddress = (PVOID)PAGE_ALIGN(CapturedBase);
    00478     FoundVad = MiCheckForConflictingVad (StartingAddress, EndingAddress);
    00479
    00480     if (FoundVad == (PMMVAD)NULL) {
    00481
    00482         //
    00483         // No virtual address is reserved at the specified base address,
    00484         // return an error.
    00485         //
    00486
    00487         Status = STATUS_CONFLICTING_ADDRESSES;
    00488         goto ErrorFound;
    00489     }
    00490
    00491     //
    00492     // Ensure that the starting and ending addresses are all within
    00493     // the same virtual address descriptor.
    00494     //
    00495
    00496     if ((MI_VA_TO_VPN (StartingAddress) < FoundVad->StartingVpn) ||
    00497         (MI_VA_TO_VPN (EndingAddress) > FoundVad->EndingVpn)) {
    00498
    00499         //
    00500         // Not within the section virtual address descriptor,
    00501         // return an error.
    00502         //
    00503
    00504         Status = STATUS_CONFLICTING_ADDRESSES;
    00505         goto ErrorFound;
    00506     }
    00507
    00508     if (FoundVad->u.VadFlags.UserPhysicalPages == 1) {
    00509
    00510         //
    00511         // These must always be readwrite.
    00512         //
    00513
    00514         Status = STATUS_CONFLICTING_ADDRESSES;
    00515         goto ErrorFound;
    00516     }
    00517
    00518     if (FoundVad->u.VadFlags.PhysicalMapping == 1) {
    00519
    00520         //
    00521         // Setting the protection of a physically mapped section is
    00522         // not allowed as there is no corresponding PFN database element.
    00523         //
    00524
    00525         Status = STATUS_CONFLICTING_ADDRESSES;
    00526         goto ErrorFound;
    00527     }
    00528
    00529     if (FoundVad->u.VadFlags.NoChange == 1) {
    00530
    00531         //
    00532         // An attempt is made at changing the protection
    00533         // of a secured VAD, check to see if the address range
    00534         // to change allows the change.
    00535         //
    00536
    00537         Status = MiCheckSecuredVad (FoundVad,
    00538                                     CapturedBase,
    00539                                     CapturedRegionSize,
    00540                                     ProtectionMask);
    00541
    00542         if (!NT_SUCCESS (Status)) {
    00543             goto ErrorFound;
    00544         }
    00545     }
    00546 #if defined(_MIALT4K_)
    00547     else if (EmulationFor4kPage == TRUE) {
    00548         //
    00549         // if not secured, relax the protection
    00550         //
    00551
    00552         NewProtect = MiMakeProtectForNativePage(StartingAddressFor4k,
    00553                                                 NewProtect,
    00554                                                 Process);
    00555
    00556         ProtectionMask = MiMakeProtectionMask (NewProtect);
    00557     }
    00558 #endif
    00559
    00560     if (FoundVad->u.VadFlags.PrivateMemory == 0) {
    00561
    00562
    00563         //
    00564         // For mapped sections, the NO_CACHE attribute is not allowed.
    00565         //
    00566
    00567         if (NewProtect & PAGE_NOCACHE) {
    00568
    00569             //
    00570             // Not allowed.
    00571             //
    00572
    00573             Status = STATUS_INVALID_PARAMETER_4;
    00574             goto ErrorFound;
    00575         }
    00576
    00577         //
    00578         // If this is a file mapping, then all pages must be
    00579         // committed as there can be no sparse file maps. Images
    00580         // can have non-committed pages if the alignment is greater
    00581         // than the page size.
    00582         //
    00583
    00584         if ((FoundVad->ControlArea->u.Flags.File == 0) ||
    00585             (FoundVad->ControlArea->u.Flags.Image == 1)) {
    00586
    00587             PointerProtoPte = MiGetProtoPteAddress (FoundVad,
    00588                                         MI_VA_TO_VPN (StartingAddress));
    00589             LastProtoPte = MiGetProtoPteAddress (FoundVad,
    00590                                         MI_VA_TO_VPN (EndingAddress));
    00591
    00592             //
    00593             // Release the working set mutex and acquire the section
    00594             // commit mutex.  Check all the prototype PTEs described by
    00595             // the virtual address range to ensure they are committed.
    00596             //
    00597
    00598             UNLOCK_WS_UNSAFE (Process);
    00599             ExAcquireFastMutexUnsafe (&MmSectionCommitMutex);
    00600
    00601             while (PointerProtoPte <= LastProtoPte) {
    00602
    00603                 //
    00604                 // Check to see if the prototype PTE is committed, if
    00605                 // not return an error.
    00606                 //
    00607
    00608                 if (PointerProtoPte->u.Long == 0) {
    00609
    00610                     //
    00611                     // Error, this prototype PTE is not committed.
    00612                     //
    00613
    00614                     ExReleaseFastMutexUnsafe (&MmSectionCommitMutex);
    00615                     Status = STATUS_NOT_COMMITTED;
    00616                     UNLOCK_ADDRESS_SPACE (Process);
    00617                     goto ErrorFoundNoWs;
    00618                 }
    00619                 PointerProtoPte += 1;
    00620             }
    00621
    00622             //
    00623             // The range is committed, release the section commitment
    00624             // mutex, acquire the working set mutex and update the local PTEs.
    00625             //
    00626
    00627             ExReleaseFastMutexUnsafe (&MmSectionCommitMutex);
    00628
    00629             //
    00630             // Set the protection on the section pages.  This could
    00631             // get a quota exceeded exception.
    00632             //
    00633
    00634             LOCK_WS_UNSAFE (Process);
    00635         }
    00636
    00637 #if defined(_MIALT4K_)
    00638
    00639         //
    00640         // We need to change the alternate table before PTEs are created for the protection
    00641         // change.
    00642         //
    00643
    00644         if (EmulationFor4kPage == TRUE) {
    00645
    00646             //
    00647             // Before accessing Alternate Table, unlock the working set mutex
    00648             //
    00649
    00650             UNLOCK_WS_UNSAFE (Process);
    00651
    00652             //
    00653             // Get the old protection
    00654             //
    00655
    00656             CapturedOldProtectFor4k =
    00657                 MiQueryProtectionFor4kPage(StartingAddressFor4k, Process);
    00658
    00659             if (CapturedOldProtectFor4k != 0) {
    00660
    00661                 CapturedOldProtectFor4k =
    00662                     MI_CONVERT_FROM_PTE_PROTECTION(CapturedOldProtectFor4k);
    00663
    00664             }
    00665
    00666             //
    00667             // Set the alternate permission table
    00668             //
    00669
    00670             if ((FoundVad->u.VadFlags.ImageMap == 1) ||
    00671                 (FoundVad->u2.VadFlags2.CopyOnWrite == 1)) {
    00672
    00673                 OriginalProtectionMask |= MM_PROTECTION_COPY_MASK;
    00674
    00675             }
    00676
    00677             MiProtectFor4kPage (StartingAddressFor4k,
    00678                                 CapturedRegionSizeFor4k,
    00679                                 OriginalProtectionMask,
    00680                                 ALT_CHANGE,
    00681                                 Process);
    00682
    00683             LOCK_WS_UNSAFE (Process);
    00684         }
    00685 #endif
    00686
    00687         try {
    00688             Locked = MiSetProtectionOnSection ( Process,
    00689                                                 FoundVad,
    00690                                                 StartingAddress,
    00691                                                 EndingAddress,
    00692                                                 NewProtect,
    00693                                                 &CapturedOldProtect,
    00694                                                 FALSE );
    00695
    00696         } except (EXCEPTION_EXECUTE_HANDLER) {
    00697
    00698             Status = GetExceptionCode();
    00699             goto ErrorFound;
    00700         }
    00701     } else {
    00702
    00703         //
    00704         // Not a section, private.
    00705         // For private pages, the WRITECOPY attribute is not allowed.
    00706         //
    00707
    00708         if ((NewProtect & PAGE_WRITECOPY) ||
    00709             (NewProtect & PAGE_EXECUTE_WRITECOPY)) {
    00710
    00711             //
    00712             // Not allowed.
    00713             //
    00714
    00715             Status = STATUS_INVALID_PARAMETER_4;
    00716             goto ErrorFound;
    00717         }
    00718
    00719         //
    00720         // Ensure all of the pages are already committed as described
    00721         // in the virtual address descriptor.
    00722         //
    00723
    00724         if ( !MiIsEntireRangeCommitted(StartingAddress,
    00725                                          EndingAddress,
    00726                                          FoundVad,
    00727                                          Process)) {
    00728
    00729             //
    00730             // Previously reserved pages have been decommitted, or an error
    00731             // occurred, release mutex and return status.
    00732             //
    00733
    00734             Status = STATUS_NOT_COMMITTED;
    00735             goto ErrorFound;
    00736         }
    00737
    00738 #if defined(_MIALT4K_)
    00739
    00740         //
    00741         // We need to change the alternate table before PTEs are created for the protection
    00742         // change.
    00743         //
    00744
    00745         if (EmulationFor4kPage == TRUE) {
    00746
    00747             //
    00748             // Before accessing Alternate Table, unlock the working set mutex
    00749             //
    00750
    00751             UNLOCK_WS_UNSAFE (Process);
    00752
    00753             //
    00754             // Get the old protection
    00755             //
    00756
    00757             CapturedOldProtectFor4k =
    00758                 MiQueryProtectionFor4kPage(StartingAddressFor4k, Process);
    00759
    00760             if (CapturedOldProtectFor4k != 0) {
    00761
    00762                 CapturedOldProtectFor4k =
    00763                     MI_CONVERT_FROM_PTE_PROTECTION(CapturedOldProtectFor4k);
    00764
    00765             }
    00766
    00767             //
    00768             // Set the alternate permission table
    00769             //
    00770
    00771             MiProtectFor4kPage (StartingAddressFor4k,
    00772                                 CapturedRegionSizeFor4k,
    00773                                 OriginalProtectionMask,
    00774                                 ALT_CHANGE,
    00775                                 Process);
    00776
    00777             LOCK_WS_UNSAFE (Process);
    00778         }
    00779 #endif
    00780
    00781         //
    00782         // The address range is committed, change the protection.
    00783         //
    00784
    00785         PointerPpe = MiGetPpeAddress (StartingAddress);
    00786         PointerPde = MiGetPdeAddress (StartingAddress);
    00787         PointerPte = MiGetPteAddress (StartingAddress);
    00788         LastPte = MiGetPteAddress (EndingAddress);
    00789
    00790 #if defined (_WIN64)
    00791         MiMakePpeExistAndMakeValid (PointerPpe, Process, FALSE);
    00792         if (PointerPde->u.Long == 0) {
    00793             UsedPageDirectoryHandle = MI_GET_USED_PTES_HANDLE (PointerPte);
    00794             MI_INCREMENT_USED_PTES_BY_HANDLE (UsedPageDirectoryHandle);
    00795         }
    00796 #endif
    00797
    00798         MiMakePdeExistAndMakeValid(PointerPde, Process, FALSE);
    00799
    00800         //
    00801         // Capture the protection for the first page.
    00802         //
    00803
    00804         if (PointerPte->u.Long != 0) {
    00805
    00806             CapturedOldProtect = MiGetPageProtection (PointerPte, Process);
    00807
    00808             //
    00809             // Make sure the page directory & table pages are still resident.
    00810             //
    00811
    00812 #if defined (_WIN64)
    00813             MiMakePpeExistAndMakeValid (PointerPpe, Process, FALSE);
    00814             if (PointerPde->u.Long == 0) {
    00815                 UsedPageDirectoryHandle = MI_GET_USED_PTES_HANDLE (PointerPte);
    00816                 MI_INCREMENT_USED_PTES_BY_HANDLE (UsedPageDirectoryHandle);
    00817             }
    00818 #endif
    00819
    00820             (VOID) MiMakePdeExistAndMakeValid(PointerPde, Process, FALSE);
    00821
    00822         } else {
    00823
    00824             //
    00825             // Get the protection from the VAD.
    00826             //
    00827
    00828             CapturedOldProtect =
    00829                MI_CONVERT_FROM_PTE_PROTECTION(FoundVad->u.VadFlags.Protection);
    00830         }
    00831
    00832         //
    00833         // For all the PTEs in the specified address range, set the
    00834         // protection depending on the state of the PTE.
    00835         //
    00836
    00837         while (PointerPte <= LastPte) {
    00838
    00839             if (MiIsPteOnPdeBoundary (PointerPte)) {
    00840
    00841                 PointerPde = MiGetPteAddress (PointerPte);
    00842                 PointerPpe = MiGetPdeAddress (PointerPte);
    00843
    00844 #if defined (_WIN64)
    00845                 MiMakePpeExistAndMakeValid (PointerPpe, Process, FALSE);
    00846                 if (PointerPde->u.Long == 0) {
    00847                     UsedPageDirectoryHandle = MI_GET_USED_PTES_HANDLE (PointerPte);
    00848                     MI_INCREMENT_USED_PTES_BY_HANDLE (UsedPageDirectoryHandle);
    00849                 }
    00850 #endif
    00851
    00852                 MiMakePdeExistAndMakeValid(PointerPde, Process, FALSE);
    00853             }
    00854
    00855             PteContents = *PointerPte;
    00856
    00857             if (PteContents.u.Long == 0) {
    00858
    00859                 //
    00860                 // Increment the count of non-zero page table entries
    00861                 // for this page table and the number of private pages
    00862                 // for the process.  The protection will be set as
    00863                 // if the PTE was demand zero.
    00864                 //
    00865
    00866                 UsedPageTableHandle = MI_GET_USED_PTES_HANDLE (MiGetVirtualAddressMappedByPte (PointerPte));
    00867
    00868                 MI_INCREMENT_USED_PTES_BY_HANDLE (UsedPageTableHandle);
    00869             }
    00870
    00871             if (PteContents.u.Hard.Valid == 1) {
    00872
    00873                 //
    00874                 // Set the protection into both the PTE and the original PTE
    00875                 // in the PFN database.
    00876                 //
    00877
    00878                 Pfn1 = MI_PFN_ELEMENT (PteContents.u.Hard.PageFrameNumber);
    00879
    00880                 if (Pfn1->u3.e1.PrototypePte == 1) {
    00881
    00882                     //
    00883                     // This PTE refers to a fork prototype PTE, make it
    00884                     // private.
    00885                     //
    00886
    00887                     MiCopyOnWrite (MiGetVirtualAddressMappedByPte (PointerPte),
    00888                                     PointerPte);
    00889
    00890                     //
    00891                     // This may have released the working set mutex and
    00892                     // the page directory and table pages may no longer be
    00893                     // in memory.
    00894                     //
    00895
    00896                     do {
    00897
    00898                         (VOID)MiDoesPpeExistAndMakeValid (MiGetPteAddress (PointerPde),
    00899                                                           Process,
    00900                                                           FALSE,
    00901                                                           &Waited);
    00902
    00903                         Waited = 0;
    00904
    00905                         (VOID)MiDoesPdeExistAndMakeValid (PointerPde,
    00906                                                           Process,
    00907                                                           FALSE,
    00908                                                           &Waited);
    00909
    00910                     } while (Waited != 0);
    00911
    00912                     //
    00913                     // Do the loop again for the same PTE.
    00914                     //
    00915
    00916                     continue;
    00917                 } else {
    00918
    00919                     //
    00920                     // The PTE is a private page which is valid, if the
    00921                     // specified protection is no-access or guard page
    00922                     // remove the PTE from the working set.
    00923                     //
    00924
    00925                     if ((NewProtect & PAGE_NOACCESS) ||
    00926                         (NewProtect & PAGE_GUARD)) {
    00927
    00928                         //
    00929                         // Remove the page from the working set.
    00930                         //
    00931
    00932                         Locked = MiRemovePageFromWorkingSet (PointerPte,
    00933                                                              Pfn1,
    00934                                                              &Process->Vm);
    00935
    00936
    00937                         continue;
    00938                     } else {
    00939
    00940 #if PFN_CONSISTENCY
    00941                         MiSetOriginalPteProtection (Pfn1, ProtectionMask);
    00942 #else
    00943                         Pfn1->OriginalPte.u.Soft.Protection = ProtectionMask;
    00944 #endif
    00945                         MI_MAKE_VALID_PTE (TempPte,
    00946                                            PointerPte->u.Hard.PageFrameNumber,
    00947                                            ProtectionMask,
    00948                                            PointerPte);
    00949
    00950                         WorkingSetIndex = MI_GET_WORKING_SET_FROM_PTE (&PteContents);
    00951                         MI_SET_PTE_IN_WORKING_SET (&TempPte, WorkingSetIndex);
    00952
    00953                         //
    00954                         // Flush the TB as we have changed the protection
    00955                         // of a valid PTE.
    00956                         //
    00957
    00958                         PreviousPte.u.Flush = MiFlushTbAndCapture (PointerPte,
    00959                                                                TempPte.u.Flush,
    00960                                                                Pfn1);
    00961                     }
    00962                 }
    00963             } else {
    00964
    00965                 if (PteContents.u.Soft.Prototype == 1) {
    00966
    00967                     //
    00968                     // This PTE refers to a fork prototype PTE, make the
    00969                     // page private.  This is accomplished by releasing
    00970                     // the working set mutex, reading the page thereby
    00971                     // causing a fault, and re-executing the loop, hopefully,
    00972                     // this time, we'll find the page present and will
    00973                     // turn it into a private page.
    00974                     //
    00975                     // Note, that a TRY is used to catch guard
    00976                     // page exceptions and no-access exceptions.
    00977                     //
    00978
    00979                     Va = MiGetVirtualAddressMappedByPte (PointerPte);
    00980
    00981                     DoAgain = TRUE;
    00982
    00983                     while (PteContents.u.Hard.Valid == 0) {
    00984
    00985                         UNLOCK_WS_UNSAFE (Process);
    00986
    00987                         try {
    00988
    00989                             *(volatile ULONG *)Va;
    00990                         } except (EXCEPTION_EXECUTE_HANDLER) {
    00991
    00992                             if (GetExceptionCode() ==
    00993                                                 STATUS_ACCESS_VIOLATION) {
    00994
    00995                                 //
    00996                                 // The prototype PTE must be noaccess.
    00997                                 //
    00998
    00999                                 DoAgain = MiChangeNoAccessForkPte (PointerPte,
    01000                                                                 ProtectionMask);
    01001                             } else if (GetExceptionCode() ==
    01002                                                     STATUS_IN_PAGE_ERROR) {
    01003                                 //
    01004                                 // Ignore this page and go onto the next one.
    01005                                 //
    01006
    01007                                 PointerPte += 1;
    01008                                 DoAgain = TRUE;
    01009
    01010                                 if (PointerPte > LastPte) {
    01011                                     LOCK_WS_UNSAFE (Process);
    01012                                     break;
    01013                                 }
    01014                             }
    01015                         }
    01016
    01017                         LOCK_WS_UNSAFE (Process);
    01018
    01019                         do {
    01020
    01021                             (VOID)MiDoesPpeExistAndMakeValid (MiGetPteAddress (PointerPde),
    01022                                                               Process,
    01023                                                               FALSE,
    01024                                                               &Waited);
    01025
    01026                             Waited = 0;
    01027
    01028                             (VOID)MiDoesPdeExistAndMakeValid (PointerPde,
    01029                                                               Process,
    01030                                                               FALSE,
    01031                                                               &Waited);
    01032
    01033                         } while (Waited != 0);
    01034
    01035                         PteContents = *(volatile MMPTE *)PointerPte;
    01036                     }
    01037
    01038                     if (DoAgain) {
    01039                         continue;
    01040                     }
    01041
    01042                 } else {
    01043
    01044                     if (PteContents.u.Soft.Transition == 1) {
    01045
    01046                         if (MiSetProtectionOnTransitionPte (
    01047                                                     PointerPte,
    01048                                                     ProtectionMask)) {
    01049                             continue;
    01050                         }
    01051                     } else {
    01052
    01053                         //
    01054                         // Must be page file space or demand zero.
    01055                         //
    01056
    01057                         PointerPte->u.Soft.Protection = ProtectionMask;
    01058                         ASSERT (PointerPte->u.Long != 0);
    01059                     }
    01060                 }
    01061             }
    01062             PointerPte += 1;
    01063         } //end while
    01064     }
    01065
    01066     //
    01067     // Common completion code.
    01068     //
    01069
    01070 #if defined(_MIALT4K_)
    01071
    01072     if (EmulationFor4kPage == TRUE) {
    01073
    01074         StartingAddress = StartingAddressFor4k;
    01075
    01076         EndingAddress = EndingAddressFor4k;
    01077
    01078         if (CapturedOldProtectFor4k != 0) {
    01079
    01080             //
    01081             // change CapturedOldProtect when CapturedOldProtectFor4k
    01082             // contains the true protection for the 4k page
    01083             //
    01084
    01085             CapturedOldProtect = CapturedOldProtectFor4k;
    01086
    01087         }
    01088     }
    01089 #endif
    01090
    01091     *RegionSize = (PCHAR)EndingAddress - (PCHAR)StartingAddress + 1L;
    01092     *BaseAddress = StartingAddress;
    01093     *LastProtect = CapturedOldProtect;
    01094
    01095     if (Locked) {
    01096         Status = STATUS_WAS_UNLOCKED;
    01097     } else {
    01098         Status = STATUS_SUCCESS;
    01099     }
    01100
    01101 ErrorFound:
    01102     UNLOCK_WS_AND_ADDRESS_SPACE (Process);
    01103
    01104 ErrorFoundNoWs:
    01105     return Status;
    01106 }
    01107
    01108
    01109 ULONG
    01110 MiSetProtectionOnSection (
    01111     IN PEPROCESS Process,
    01112     IN PMMVAD FoundVad,
    01113     IN PVOID StartingAddress,
    01114     IN PVOID EndingAddress,
    01115     IN ULONG NewProtect,
    01116     OUT PULONG CapturedOldProtect,
    01117     IN ULONG DontCharge
    01118     )
    01119
    01120 /*++
    01121
    01122 Routine Description:
    01123
    01124     This routine changes the protection on a region of committed pages
    01125     within the virtual address space of the subject process.  Setting
    01126     the protection on a range of pages causes the old protection to be
    01127     replaced by the specified protection value.
    01128
    01129 Arguments:
    01130
    01131     Process - Supplies a pointer to the current process.
    01132
    01133     FoundVad - Supplies a pointer to the VAD containing the range to protect.
    01134
    01135     StartingAddress - Supplies the starting address to protect.
    01136
    01137     EndingAddress - Supplies the ending address to protect.
    01138
    01139     NewProtect - Supplies the new protection to set.
    01140
    01141     CapturedOldProtect - Supplies the address of a kernel owned pointer to
    01142                 store (without probing) the old protection into.
    01143
    01144     DontCharge - Supplies TRUE if no quota or commitment should be charged.
    01145
    01146 Return Value:
    01147
    01148     Returns TRUE if a locked page was removed from the working set (protection
    01149     was guard page or no-access, FALSE otherwise.
    01150
    01151 Exceptions raised for page file quota or commitment violations.
    01152
    01153 Environment:
    01154
    01155     Kernel mode, working set mutex held, address creation mutex held
    01156     APCs disabled.
    01157
    01158 --*/
    01159
    01160 {
    01161     PMMPTE PointerPte;
    01162     PMMPTE LastPte;
    01163     PMMPTE PointerPde;
    01164     PMMPTE PointerPpe;
    01165     PMMPTE PointerProtoPte;
    01166     PMMPFN Pfn1;
    01167     MMPTE TempPte;
    01168     MMPTE PreviousPte;
    01169     ULONG Locked;
    01170     ULONG ProtectionMask;
    01171     ULONG ProtectionMaskNotCopy;
    01172     ULONG NewProtectionMask;
    01173     MMPTE PteContents;
    01174     ULONG Index;
    01175     PULONG Va;
    01176     ULONG WriteCopy;
    01177     ULONG DoAgain;
    01178     ULONG Waited;
    01179     SIZE_T QuotaCharge;
    01180     PVOID UsedPageTableHandle;
    01181     PVOID UsedPageDirectoryHandle;
    01182     ULONG WorkingSetIndex;
    01183
    01184     PAGED_CODE();
    01185
    01186     Locked = FALSE;
    01187     WriteCopy = FALSE;
    01188     QuotaCharge = 0;
    01189
    01190     //
    01191     // Make the protection field.
    01192     //
    01193
    01194     ASSERT (FoundVad->u.VadFlags.PrivateMemory == 0);
    01195
    01196     if ((FoundVad->u.VadFlags.ImageMap == 1) ||
    01197         (FoundVad->u2.VadFlags2.CopyOnWrite == 1)) {
    01198
    01199         if (NewProtect & PAGE_READWRITE) {
    01200             NewProtect &= ~PAGE_READWRITE;
    01201             NewProtect |= PAGE_WRITECOPY;
    01202         }
    01203
    01204         if (NewProtect & PAGE_EXECUTE_READWRITE) {
    01205             NewProtect &= ~PAGE_EXECUTE_READWRITE;
    01206             NewProtect |= PAGE_EXECUTE_WRITECOPY;
    01207         }
    01208     }
    01209
    01210     ProtectionMask = MiMakeProtectionMask (NewProtect);
    01211
    01212     //
    01213     // Determine if copy on write is being set.
    01214     //
    01215
    01216     ProtectionMaskNotCopy = ProtectionMask;
    01217     if ((ProtectionMask & MM_COPY_ON_WRITE_MASK) == MM_COPY_ON_WRITE_MASK) {
    01218         WriteCopy = TRUE;
    01219         ProtectionMaskNotCopy &= ~MM_PROTECTION_COPY_MASK;
    01220     }
    01221
    01222 #if defined(_MIALT4K_)
    01223
    01224     if ((Process->Wow64Process != NULL) &&
    01225         (FoundVad->u.VadFlags.ImageMap == 0) &&
    01226         (FoundVad->u2.VadFlags2.CopyOnWrite == 0) &&
    01227         (WriteCopy)) {
    01228
    01229         NTSTATUS status;
    01230
    01231         status = MiSetCopyPagesFor4kPage(Process,
    01232                                          &FoundVad,
    01233                                          &StartingAddress,
    01234                                          &EndingAddress,
    01235                                          ProtectionMask);
    01236
    01237         if (status != STATUS_SUCCESS) {
    01238             ExRaiseStatus (status);
    01239         }
    01240
    01241     }
    01242
    01243 #endif
    01244
    01245     PointerPpe = MiGetPpeAddress (StartingAddress);
    01246     PointerPde = MiGetPdeAddress (StartingAddress);
    01247     PointerPte = MiGetPteAddress (StartingAddress);
    01248     LastPte = MiGetPteAddress (EndingAddress);
    01249
    01250 #if defined (_WIN64)
    01251     MiMakePpeExistAndMakeValid (PointerPpe, Process, FALSE);
    01252     if (PointerPde->u.Long == 0) {
    01253         UsedPageDirectoryHandle = MI_GET_USED_PTES_HANDLE (PointerPte);
    01254         MI_INCREMENT_USED_PTES_BY_HANDLE (UsedPageDirectoryHandle);
    01255     }
    01256 #endif
    01257
    01258     MiMakePdeExistAndMakeValid(PointerPde, Process, FALSE);
    01259
    01260     //
    01261     // Capture the protection for the first page.
    01262     //
    01263
    01264     if (PointerPte->u.Long != 0) {
    01265
    01266         *CapturedOldProtect = MiGetPageProtection (PointerPte, Process);
    01267
    01268         //
    01269         // Make sure the Page table page is still resident.
    01270         //
    01271
    01272         PointerPpe = MiGetPteAddress (PointerPde);
    01273
    01274         do {
    01275
    01276             (VOID)MiDoesPpeExistAndMakeValid (PointerPpe, Process, FALSE, &Waited);
    01277             Waited = 0;
    01278
    01279             (VOID)MiDoesPdeExistAndMakeValid (PointerPde, Process, FALSE, &Waited);
    01280         } while (Waited != 0);
    01281
    01282     } else {
    01283
    01284         //
    01285         // Get the protection from the VAD, unless image file.
    01286         //
    01287
    01288         if (FoundVad->u.VadFlags.ImageMap == 0) {
    01289
    01290             //
    01291             // This is not an image file, the protection is in the VAD.
    01292             //
    01293
    01294             *CapturedOldProtect =
    01295                 MI_CONVERT_FROM_PTE_PROTECTION(FoundVad->u.VadFlags.Protection);
    01296         } else {
    01297
    01298             //
    01299             // This is an image file, the protection is in the
    01300             // prototype PTE.
    01301             //
    01302
    01303             PointerProtoPte = MiGetProtoPteAddress (FoundVad,
    01304                                     MI_VA_TO_VPN (
    01305                                     MiGetVirtualAddressMappedByPte (PointerPte)));
    01306
    01307             TempPte = MiCaptureSystemPte (PointerProtoPte, Process);
    01308
    01309             *CapturedOldProtect = MiGetPageProtection (&TempPte,
    01310                                                        Process);
    01311
    01312             //
    01313             // Make sure the Page directory and table pages are still resident.
    01314             //
    01315
    01316             PointerPpe = MiGetPteAddress (PointerPde);
    01317
    01318             do {
    01319
    01320                 (VOID)MiDoesPpeExistAndMakeValid(PointerPpe, Process, FALSE, &Waited);
    01321                 Waited = 0;
    01322
    01323                 (VOID)MiDoesPdeExistAndMakeValid(PointerPde, Process, FALSE, &Waited);
    01324             } while (Waited != 0);
    01325         }
    01326     }
    01327
    01328     //
    01329     // If the page protection is being change to be copy-on-write, the
    01330     // commitment and page file quota for the potentially dirty private pages
    01331     // must be calculated and charged.  This must be done before any
    01332     // protections are changed as the changes cannot be undone.
    01333     //
    01334
    01335     if (WriteCopy) {
    01336
    01337         //
    01338         // Calculate the charges.  If the page is shared and not write copy
    01339         // it is counted as a charged page.
    01340         //
    01341
    01342         while (PointerPte <= LastPte) {
    01343
    01344             if (MiIsPteOnPdeBoundary (PointerPte)) {
    01345
    01346                 PointerPde = MiGetPteAddress (PointerPte);
    01347                 PointerPpe = MiGetPteAddress (PointerPde);
    01348
    01349                 do {
    01350
    01351                     while (!MiDoesPpeExistAndMakeValid(PointerPpe, Process, FALSE, &Waited)) {
    01352
    01353                         //
    01354                         // No PPE exists for this address.  Therefore
    01355                         // all the PTEs are shared and not copy on write.
    01356                         // go to the next PPE.
    01357                         //
    01358
    01359                         PointerPpe += 1;
    01360                         PointerPde = MiGetVirtualAddressMappedByPte (PointerPpe);
    01361                         PointerProtoPte = PointerPte;
    01362                         PointerPte = MiGetVirtualAddressMappedByPte (PointerPde);
    01363
    01364                         if (PointerPte > LastPte) {
    01365                             QuotaCharge += 1 + LastPte - PointerProtoPte;
    01366                             goto Done;
    01367                         }
    01368                         QuotaCharge += PointerPte - PointerProtoPte;
    01369                     }
    01370
    01371                     Waited = 0;
    01372
    01373                     while (!MiDoesPdeExistAndMakeValid(PointerPde, Process, FALSE, &Waited)) {
    01374
    01375                         //
    01376                         // No PDE exists for this address.  Therefore
    01377                         // all the PTEs are shared and not copy on write.
    01378                         // go to the next PDE.
    01379                         //
    01380
    01381                         PointerPde += 1;
    01382                         PointerProtoPte = PointerPte;
    01383                         PointerPpe = MiGetPteAddress (PointerPde);
    01384                         PointerPte = MiGetVirtualAddressMappedByPte (PointerPde);
    01385
    01386                         if (PointerPte > LastPte) {
    01387                             QuotaCharge += 1 + LastPte - PointerProtoPte;
    01388                             goto Done;
    01389                         }
    01390                         QuotaCharge += PointerPte - PointerProtoPte;
    01391 #if defined (_WIN64)
    01392                         if (MiIsPteOnPdeBoundary (PointerPde)) {
    01393                             Waited = 1;
    01394                             break;
    01395                         }
    01396 #endif
    01397                     }
    01398                 } while (Waited != 0);
    01399             }
    01400
    01401             PteContents = *PointerPte;
    01402
    01403             if (PteContents.u.Long == 0) {
    01404
    01405                 //
    01406                 // The PTE has not been evaluated, assume copy on write.
    01407                 //
    01408
    01409                 QuotaCharge += 1;
    01410
    01411             } else if ((PteContents.u.Hard.Valid == 1) &&
    01412                 (PteContents.u.Hard.CopyOnWrite == 0)) {
    01413
    01414                 //
    01415                 // See if this is a prototype PTE, if so charge it.
    01416                 //
    01417
    01418                 Pfn1 = MI_PFN_ELEMENT (PteContents.u.Hard.PageFrameNumber);
    01419
    01420                 if (Pfn1->u3.e1.PrototypePte == 1) {
    01421                     QuotaCharge += 1;
    01422                 }
    01423             } else {
    01424
    01425                 if (PteContents.u.Soft.Prototype == 1) {
    01426
    01427                     //
    01428                     // This is a prototype PTE.  Charge if it is not
    01429                     // in copy on write format.
    01430                     //
    01431
    01432                     if (PteContents.u.Soft.PageFileHigh == MI_PTE_LOOKUP_NEEDED) {
    01433
    01434                         //
    01435                         // Page protection is within the PTE.
    01436                         //
    01437
    01438                         if (!MI_IS_PTE_PROTECTION_COPY_WRITE(PteContents.u.Soft.Protection)) {
    01439                             QuotaCharge += 1;
    01440                         }
    01441                     } else {
    01442
    01443                         //
    01444                         // The PTE references the prototype directly, therefore
    01445                         // it can't be copy on write.  Charge.
    01446                         //
    01447
    01448                         QuotaCharge += 1;
    01449                     }
    01450                 }
    01451             }
    01452             PointerPte += 1;
    01453         }
    01454
    01455 Done:
    01456         NOTHING;
    01457
    01458         //
    01459         // Charge for the quota.
    01460         //
    01461
    01462         if (!DontCharge) {
    01463             MiChargePageFileQuota (QuotaCharge, Process);
    01464
    01465             if (Process->CommitChargeLimit) {
    01466                 if (Process->CommitCharge + QuotaCharge > Process->CommitChargeLimit) {
    01467                     MiReturnPageFileQuota (QuotaCharge, Process);
    01468                     if (Process->Job) {
    01469                         PsReportProcessMemoryLimitViolation ();
    01470                     }
    01471                     ExRaiseStatus (STATUS_COMMITMENT_LIMIT);
    01472                 }
    01473             }
    01474             if (Process->JobStatus & PS_JOB_STATUS_REPORT_COMMIT_CHANGES) {
    01475                 if (PsChangeJobMemoryUsage(QuotaCharge) == FALSE) {
    01476                     MiReturnPageFileQuota (QuotaCharge, Process);
    01477                     ExRaiseStatus (STATUS_COMMITMENT_LIMIT);
    01478                 }
    01479             }
    01480
    01481             if (MiChargeCommitment (QuotaCharge, Process) == FALSE) {
    01482                 if (Process->JobStatus & PS_JOB_STATUS_REPORT_COMMIT_CHANGES) {
    01483
    01484                     //
    01485                     // Temporarily up the process commit charge as the
    01486                     // job code will be referencing it as though everything
    01487                     // has succeeded.
    01488                     //
    01489
    01490                     Process->CommitCharge += QuotaCharge;
    01491                     PsChangeJobMemoryUsage(-(SSIZE_T)QuotaCharge);
    01492                     Process->CommitCharge -= QuotaCharge;
    01493                 }
    01494                 MiReturnPageFileQuota (QuotaCharge, Process);
    01495                 ExRaiseStatus STATUS_COMMITMENT_LIMIT;
    01496             }
    01497
    01498             //
    01499             // Add the quota into the charge to the VAD.
    01500             //
    01501
    01502             MM_TRACK_COMMIT (MM_DBG_COMMIT_SET_PROTECTION, QuotaCharge);
    01503             FoundVad->u.VadFlags.CommitCharge += QuotaCharge;
    01504             Process->CommitCharge += QuotaCharge;
    01505             if (Process->CommitCharge > Process->CommitChargePeak) {
    01506                 Process->CommitChargePeak = Process->CommitCharge;
    01507             }
    01508         }
    01509     }
    01510
    01511     //
    01512     // For all the PTEs in the specified address range, set the
    01513     // protection depending on the state of the PTE.
    01514     //
    01515
    01516     //
    01517     // If the PTE was copy on write (but not written) and the
    01518     // new protection is NOT copy-on-write, return page file quota
    01519     // and commitment.
    01520     //
    01521
    01522     PointerPpe = MiGetPpeAddress (StartingAddress);
    01523     PointerPde = MiGetPdeAddress (StartingAddress);
    01524     PointerPte = MiGetPteAddress (StartingAddress);
    01525
    01526     do {
    01527
    01528         MiDoesPpeExistAndMakeValid (PointerPpe, Process, FALSE, &Waited);
    01529
    01530         Waited = 0;
    01531
    01532         MiDoesPdeExistAndMakeValid (PointerPde, Process, FALSE, &Waited);
    01533
    01534     } while (Waited != 0);
    01535
    01536     QuotaCharge = 0;
    01537
    01538     while (PointerPte <= LastPte) {
    01539
    01540         if (MiIsPteOnPdeBoundary (PointerPte)) {
    01541             PointerPde = MiGetPteAddress (PointerPte);
    01542             PointerPpe = MiGetPdeAddress (PointerPte);
    01543
    01544 #if defined (_WIN64)
    01545             MiMakePpeExistAndMakeValid (PointerPpe, Process, FALSE);
    01546             if (PointerPde->u.Long == 0) {
    01547                 UsedPageDirectoryHandle = MI_GET_USED_PTES_HANDLE (PointerPte);
    01548                 MI_INCREMENT_USED_PTES_BY_HANDLE (UsedPageDirectoryHandle);
    01549             }
    01550 #endif
    01551
    01552             MiMakePdeExistAndMakeValid (PointerPde, Process, FALSE);
    01553         }
    01554
    01555         PteContents = *PointerPte;
    01556
    01557         if (PteContents.u.Long == 0) {
    01558
    01559             //
    01560             // The PTE is zero, set it into prototype PTE format
    01561             // with the protection in the prototype PTE.
    01562             //
    01563
    01564             TempPte = PrototypePte;
    01565             TempPte.u.Soft.Protection = ProtectionMask;
    01566             MI_WRITE_INVALID_PTE (PointerPte, TempPte);
    01567
    01568             //
    01569             // Increment the count of non-zero page table entries
    01570             // for this page table and the number of private pages
    01571             // for the process.
    01572             //
    01573
    01574             UsedPageTableHandle = MI_GET_USED_PTES_HANDLE (MiGetVirtualAddressMappedByPte (PointerPte));
    01575
    01576             MI_INCREMENT_USED_PTES_BY_HANDLE (UsedPageTableHandle);
    01577
    01578         } else if (PteContents.u.Hard.Valid == 1) {
    01579
    01580             //
    01581             // Set the protection into both the PTE and the original PTE
    01582             // in the PFN database for private pages only.
    01583             //
    01584
    01585             NewProtectionMask = ProtectionMask;
    01586
    01587             Pfn1 = MI_PFN_ELEMENT (PteContents.u.Hard.PageFrameNumber);
    01588
    01589             if ((NewProtect & PAGE_NOACCESS) ||
    01590                 (NewProtect & PAGE_GUARD)) {
    01591
    01592                 Locked = MiRemovePageFromWorkingSet (PointerPte,
    01593                                                      Pfn1,
    01594                                                      &Process->Vm );
    01595                 continue;
    01596
    01597             } else {
    01598
    01599                 if (Pfn1->u3.e1.PrototypePte == 1) {
    01600
    01601                     //
    01602                     // The true protection may be in the WSLE, locate
    01603                     // the WSLE.
    01604                     //
    01605
    01606                     Va = (PULONG)MiGetVirtualAddressMappedByPte (PointerPte);
    01607                     Index = MiLocateWsle ((PVOID)Va, MmWorkingSetList,
    01608                                             Pfn1->u1.WsIndex);
    01609
    01610                     //
    01611                     // Check to see if this is a prototype PTE.  This
    01612                     // is done by comparing the PTE address in the
    01613                     // PFN database to the PTE address indicated by the
    01614                     // VAD.  If they are not equal, this is a prototype
    01615                     // PTE.
    01616                     //
    01617
    01618                     if (Pfn1->PteAddress !=
    01619                                   MiGetProtoPteAddress (FoundVad,
    01620                                               MI_VA_TO_VPN ((PVOID)Va))) {
    01621
    01622                         //
    01623                         // This PTE refers to a fork prototype PTE, make it
    01624                         // private.
    01625                         //
    01626
    01627                         MiCopyOnWrite ((PVOID)Va, PointerPte);
    01628
    01629                         if (WriteCopy) {
    01630                             QuotaCharge += 1;
    01631                         }
    01632
    01633                         //
    01634                         // This may have released the working set mutex and
    01635                         // the page table page may no longer be in memory.
    01636                         //
    01637
    01638                         PointerPpe = MiGetPteAddress (PointerPde);
    01639
    01640                         do {
    01641
    01642                             (VOID)MiDoesPpeExistAndMakeValid (PointerPpe,
    01643                                                               Process,
    01644                                                               FALSE,
    01645                                                               &Waited);
    01646
    01647                             Waited = 0;
    01648
    01649                             (VOID)MiDoesPdeExistAndMakeValid (PointerPde,
    01650                                                               Process,
    01651                                                               FALSE,
    01652                                                               &Waited);
    01653
    01654                         } while (Waited != 0);
    01655
    01656                         //
    01657                         // Do the loop again.
    01658                         //
    01659
    01660                         continue;
    01661
    01662                     } else {
    01663
    01664                         //
    01665                         // Update the protection field in the WSLE and
    01666                         // the PTE.
    01667                         //
    01668                         //
    01669                         // If the PTE is copy on write uncharge the
    01670                         // previously charged quota.
    01671                         //
    01672
    01673                         if ((!WriteCopy) && (PteContents.u.Hard.CopyOnWrite == 1)) {
    01674                             QuotaCharge += 1;
    01675                         }
    01676
    01677                         MmWsle[Index].u1.e1.Protection = ProtectionMask;
    01678                         MmWsle[Index].u1.e1.SameProtectAsProto = 0;
    01679                     }
    01680
    01681                 } else {
    01682
    01683                     //
    01684                     // Page is private (copy on written), protection mask
    01685                     // is stored in the original pte field.
    01686                     //
    01687
    01688 #if PFN_CONSISTENCY
    01689                     MiSetOriginalPteProtection (Pfn1, ProtectionMaskNotCopy);
    01690 #else
    01691                     Pfn1->OriginalPte.u.Soft.Protection = ProtectionMaskNotCopy;
    01692 #endif
    01693                     NewProtectionMask = ProtectionMaskNotCopy;
    01694                 }
    01695
    01696                 MI_MAKE_VALID_PTE (TempPte,
    01697                                    PteContents.u.Hard.PageFrameNumber,
    01698                                    NewProtectionMask,
    01699                                    PointerPte);
    01700
    01701                 WorkingSetIndex = MI_GET_WORKING_SET_FROM_PTE (&PteContents);
    01702
    01703                 MI_SET_PTE_IN_WORKING_SET (&TempPte, WorkingSetIndex);
    01704             }
    01705
    01706             //
    01707             // Flush the TB as we have changed the protection
    01708             // of a valid PTE.
    01709             //
    01710
    01711             PreviousPte.u.Flush = MiFlushTbAndCapture (PointerPte,
    01712                                                        TempPte.u.Flush,
    01713                                                        Pfn1);
    01714         } else {
    01715
    01716             if (PteContents.u.Soft.Prototype == 1) {
    01717
    01718                 //
    01719                 // The PTE is in prototype PTE format.
    01720                 //
    01721
    01722                 //
    01723                 // Is it a fork prototype PTE?
    01724                 //
    01725
    01726                 Va = (PULONG)MiGetVirtualAddressMappedByPte (PointerPte);
    01727
    01728                 if ((PteContents.u.Soft.PageFileHigh != MI_PTE_LOOKUP_NEEDED) &&
    01729                    (MiPteToProto (PointerPte) !=
    01730                                      MiGetProtoPteAddress (FoundVad,
    01731                                          MI_VA_TO_VPN ((PVOID)Va)))) {
    01732
    01733                     //
    01734                     // This PTE refers to a fork prototype PTE, make the
    01735                     // page private.  This is accomplished by releasing
    01736                     // the working set mutex, reading the page thereby
    01737                     // causing a fault, and re-executing the loop, hopefully,
    01738                     // this time, we'll find the page present and will
    01739                     // turn it into a private page.
    01740                     //
    01741                     // Note, that page with prototype = 1 cannot be
    01742                     // no-access.
    01743                     //
    01744
    01745                     DoAgain = TRUE;
    01746
    01747                     while (PteContents.u.Hard.Valid == 0) {
    01748
    01749                         UNLOCK_WS_UNSAFE (Process);
    01750
    01751                         try {
    01752
    01753                             *(volatile ULONG *)Va;
    01754                         } except (EXCEPTION_EXECUTE_HANDLER) {
    01755
    01756                             if (GetExceptionCode() !=
    01757                                                 STATUS_GUARD_PAGE_VIOLATION) {
    01758
    01759                                 //
    01760                                 // The prototype PTE must be noaccess.
    01761                                 //
    01762
    01763                                 DoAgain = MiChangeNoAccessForkPte (PointerPte,
    01764                                                                 ProtectionMask);
    01765                             }
    01766                         }
    01767
    01768                         PointerPpe = MiGetPteAddress (PointerPde);
    01769
    01770                         LOCK_WS_UNSAFE (Process);
    01771
    01772                         do {
    01773
    01774                             (VOID)MiDoesPpeExistAndMakeValid (PointerPpe,
    01775                                                               Process,
    01776                                                               FALSE,
    01777                                                               &Waited);
    01778
    01779                             Waited = 0;
    01780
    01781                             (VOID)MiDoesPdeExistAndMakeValid (PointerPde,
    01782                                                               Process,
    01783                                                               FALSE,
    01784                                                               &Waited);
    01785
    01786                         } while (Waited != 0);
    01787
    01788                         PteContents = *(volatile MMPTE *)PointerPte;
    01789                     }
    01790
    01791                     if (DoAgain) {
    01792                         continue;
    01793                     }
    01794
    01795                 } else {
    01796
    01797                     //
    01798                     // If the new protection is not write-copy, the PTE
    01799                     // protection is not in the prototype PTE (can't be
    01800                     // write copy for sections), and the protection in
    01801                     // the PTE is write-copy, release the page file
    01802                     // quota and commitment for this page.
    01803                     //
    01804
    01805                     if ((!WriteCopy) &&
    01806                         (PteContents.u.Soft.PageFileHigh == MI_PTE_LOOKUP_NEEDED)) {
    01807                         if (MI_IS_PTE_PROTECTION_COPY_WRITE(PteContents.u.Soft.Protection)) {
    01808                             QuotaCharge += 1;
    01809                         }
    01810
    01811                     }
    01812
    01813                     //
    01814                     // The PTE is a prototype PTE.  Make the high part
    01815                     // of the PTE indicate that the protection field
    01816                     // is in the PTE itself.
    01817                     //
    01818
    01819                     MI_WRITE_INVALID_PTE (PointerPte, PrototypePte);
    01820                     PointerPte->u.Soft.Protection = ProtectionMask;
    01821                 }
    01822
    01823             } else {
    01824
    01825                 if (PteContents.u.Soft.Transition == 1) {
    01826
    01827                     //
    01828                     // This is a transition PTE. (Page is private)
    01829                     //
    01830
    01831                     if (MiSetProtectionOnTransitionPte (
    01832                                                 PointerPte,
    01833                                                 ProtectionMaskNotCopy)) {
    01834                         continue;
    01835                     }
    01836
    01837                 } else {
    01838
    01839                     //
    01840                     // Must be page file space or demand zero.
    01841                     //
    01842
    01843                     PointerPte->u.Soft.Protection = ProtectionMaskNotCopy;
    01844                 }
    01845             }
    01846         }
    01847
    01848         PointerPte += 1;
    01849     }
    01850
    01851     //
    01852     // Return the quota charge and the commitment, if any.
    01853     //
    01854
    01855     if ((QuotaCharge > 0) && (!DontCharge)) {
    01856
    01857         MiReturnCommitment (QuotaCharge);
    01858         MM_TRACK_COMMIT (MM_DBG_COMMIT_RETURN_PROTECTION, QuotaCharge);
    01859         MiReturnPageFileQuota (QuotaCharge, Process);
    01860
    01861         ASSERT (QuotaCharge <= FoundVad->u.VadFlags.CommitCharge);
    01862
    01863         FoundVad->u.VadFlags.CommitCharge -= QuotaCharge;
    01864         if (Process->JobStatus & PS_JOB_STATUS_REPORT_COMMIT_CHANGES) {
    01865             PsChangeJobMemoryUsage(-(SSIZE_T)QuotaCharge);
    01866         }
    01867         Process->CommitCharge -= QuotaCharge;
    01868     }
    01869
    01870     return Locked;
    01871 }
    01872
    01873 ULONG
    01874 MiGetPageProtection (
    01875     IN PMMPTE PointerPte,
    01876     IN PEPROCESS Process
    01877     )
    01878
    01879 /*++
    01880
    01881 Routine Description:
    01882
    01883     This routine returns the page protection of a non-zero PTE.
    01884     It may release and reacquire the working set mutex.
    01885
    01886 Arguments:
    01887
    01888     PointerPte - Supplies a pointer to a non-zero PTE.
    01889
    01890 Return Value:
    01891
    01892     Returns the protection code.
    01893
    01894 Environment:
    01895
    01896     Kernel mode, working set and address creation mutex held.
    01897     Note, that the address creation mutex does not need to be held
    01898     if the working set mutex does not need to be released in the
    01899     case of a prototype PTE.
    01900
    01901 --*/
    01902
    01903 {
    01904
    01905     MMPTE PteContents;
    01906     MMPTE ProtoPteContents;
    01907     PMMPFN Pfn1;
    01908     PMMPTE ProtoPteAddress;
    01909     PVOID Va;
    01910     ULONG Index;
    01911
    01912     PAGED_CODE();
    01913
    01914     PteContents = *PointerPte;
    01915
    01916     if ((PteContents.u.Soft.Valid == 0) && (PteContents.u.Soft.Prototype == 1)) {
    01917
    01918         //
    01919         // This pte is in prototype format, the protection is
    01920         // stored in the prototype PTE.
    01921         //
    01922
    01923         if (MI_IS_PTE_PROTOTYPE(PointerPte) ||
    01924 #ifdef _X86_
    01925             (MiNumberOfExtraSystemPdes && (PointerPte >= (PMMPTE)KSTACK_POOL_START && PointerPte < (PMMPTE)(KSTACK_POOL_START + MiNumberOfExtraSystemPdes * MM_VA_MAPPED_BY_PDE))) ||
    01926 #endif
    01927             (PteContents.u.Soft.PageFileHigh == MI_PTE_LOOKUP_NEEDED)) {
    01928
    01929             //
    01930             // The protection is within this PTE.
    01931             //
    01932
    01933             return MI_CONVERT_FROM_PTE_PROTECTION (
    01934                                             PteContents.u.Soft.Protection);
    01935         }
    01936
    01937         ProtoPteAddress = MiPteToProto (PointerPte);
    01938
    01939         //
    01940         // Capture protopte PTE contents.
    01941         //
    01942
    01943         ProtoPteContents = MiCaptureSystemPte (ProtoPteAddress, Process);
    01944
    01945         //
    01946         // The working set mutex may have been released and the
    01947         // page may no longer be in prototype format, get the
    01948         // new contents of the PTE and obtain the protection mask.
    01949         //
    01950
    01951         PteContents = MiCaptureSystemPte (PointerPte, Process);
    01952     }
    01953
    01954     if ((PteContents.u.Soft.Valid == 0) && (PteContents.u.Soft.Prototype == 1)) {
    01955
    01956         //
    01957         // Pte is still prototype, return the protection captured
    01958         // from the prototype PTE.
    01959         //
    01960
    01961         if (ProtoPteContents.u.Hard.Valid == 1) {
    01962
    01963             //
    01964             // The prototype PTE is valid, get the protection from
    01965             // the PFN database.
    01966             //
    01967
    01968             Pfn1 = MI_PFN_ELEMENT (ProtoPteContents.u.Hard.PageFrameNumber);
    01969             return MI_CONVERT_FROM_PTE_PROTECTION(
    01970                                       Pfn1->OriginalPte.u.Soft.Protection);
    01971
    01972         } else {
    01973
    01974             //
    01975             // The prototype PTE is not valid, return the protection from the
    01976             // PTE.
    01977             //
    01978
    01979             return MI_CONVERT_FROM_PTE_PROTECTION (
    01980                                      ProtoPteContents.u.Soft.Protection);
    01981         }
    01982     }
    01983
    01984     if (PteContents.u.Hard.Valid == 1) {
    01985
    01986         //
    01987         // The page is valid, the protection field is either in the
    01988         // PFN database original PTE element or the WSLE.  If
    01989         // the page is private, get it from the PFN original PTE
    01990         // element, else use the WSLE.
    01991         //
    01992
    01993         Pfn1 = MI_PFN_ELEMENT (PteContents.u.Hard.PageFrameNumber);
    01994
    01995         if ((Pfn1->u3.e1.PrototypePte == 0) ||
    01996 #ifdef _X86_
    01997             (MiNumberOfExtraSystemPdes && (PointerPte >= (PMMPTE)KSTACK_POOL_START && PointerPte < (PMMPTE)(KSTACK_POOL_START + MiNumberOfExtraSystemPdes * MM_VA_MAPPED_BY_PDE))) ||
    01998 #endif
    01999             (MI_IS_PTE_PROTOTYPE(PointerPte))) {
    02000
    02001             //
    02002             // This is a private PTE or the PTE address is that of a
    02003             // prototype PTE, hence the protection is in
    02004             // the original PTE.
    02005             //
    02006
    02007             return MI_CONVERT_FROM_PTE_PROTECTION(
    02008                                       Pfn1->OriginalPte.u.Soft.Protection);
    02009         }
    02010
    02011         //
    02012         // The PTE was a hardware PTE, get the protection
    02013         // from the WSLE.
    02014
    02015         Va = (PULONG)MiGetVirtualAddressMappedByPte (PointerPte);
    02016         Index = MiLocateWsle ((PVOID)Va, MmWorkingSetList,
    02017                                             Pfn1->u1.WsIndex);
    02018
    02019         return MI_CONVERT_FROM_PTE_PROTECTION (MmWsle[Index].u1.e1.Protection);
    02020     }
    02021
    02022     //
    02023     // PTE is either demand zero or transition, in either
    02024     // case protection is in PTE.
    02025     //
    02026
    02027     return MI_CONVERT_FROM_PTE_PROTECTION (PteContents.u.Soft.Protection);
    02028
    02029 }
    02030
    02031 ULONG
    02032 MiChangeNoAccessForkPte (
    02033     IN PMMPTE PointerPte,
    02034     IN ULONG ProtectionMask
    02035     )
    02036
    02037 /*++
    02038
    02039 Routine Description:
    02040
    02041
    02042 Arguments:
    02043
    02044     PointerPte - Supplies a pointer to the current PTE.
    02045
    02046     ProtectionMask - Supplies the protection mask to set.
    02047
    02048 Return Value:
    02049
    02050     FALSE if the loop should be repeated for this PTE, TRUE
    02051     if protection has been set.
    02052
    02053
    02054 Environment:
    02055
    02056     Kernel mode, address creation mutex held, APCs disabled.
    02057
    02058 --*/
    02059
    02060 {
    02061     PAGED_CODE();
    02062
    02063     if (ProtectionMask == MM_NOACCESS) {
    02064
    02065         //
    02066         // No need to change the page protection.
    02067         //
    02068
    02069         return TRUE;
    02070     }
    02071
    02072     PointerPte->u.Proto.ReadOnly = 1;
    02073
    02074     return FALSE;
    02075 }
    02076
    02077
    02078 HARDWARE_PTE
    02079 MiFlushTbAndCapture(
    02080     IN PMMPTE PointerPte,
    02081     IN HARDWARE_PTE TempPte,
    02082     IN PMMPFN Pfn1
    02083     )
    02084
    02085 // non pagable helper routine.
    02086
    02087 {
    02088     MMPTE PreviousPte;
    02089     KIRQL OldIrql;
    02090     PEPROCESS Process;
    02091     PVOID VirtualAddress;
    02092
    02093     //
    02094     // Flush the TB as we have changed the protection
    02095     // of a valid PTE.
    02096     //
    02097
    02098     LOCK_PFN (OldIrql);
    02099
    02100     PreviousPte.u.Flush = KeFlushSingleTb (
    02101                             MiGetVirtualAddressMappedByPte (PointerPte),
    02102                             FALSE,
    02103                             FALSE,
    02104                             (PHARDWARE_PTE)PointerPte,
    02105                             TempPte);
    02106
    02107     ASSERT (PreviousPte.u.Hard.Valid == 1);
    02108
    02109     //
    02110     // A page protection is being changed, on certain
    02111     // hardware the dirty bit should be ORed into the
    02112     // modify bit in the PFN element.
    02113     //
    02114
    02115     MI_CAPTURE_DIRTY_BIT_TO_PFN (&PreviousPte, Pfn1);
    02116
    02117     //
    02118     // If the PTE indicates the page has been modified (this is different
    02119     // from the PFN indicating this), then ripple it back to the write watch
    02120     // bitmap now since we are still in the correct process context.
    02121     //
    02122
    02123     if (MiActiveWriteWatch != 0) {
    02124         if ((Pfn1->u3.e1.PrototypePte == 0) &&
    02125             (MI_IS_PTE_DIRTY(PreviousPte))) {
    02126
    02127             Process = PsGetCurrentProcess();
    02128
    02129             if (Process->Vm.u.Flags.WriteWatch == 1) {
    02130
    02131                 //
    02132                 // This process has (or had) write watch VADs.  Search now
    02133                 // for a write watch region encapsulating the PTE being
    02134                 // invalidated.
    02135                 //
    02136
    02137                 VirtualAddress = MiGetVirtualAddressMappedByPte (PointerPte);
    02138                 MiCaptureWriteWatchDirtyBit (Process, VirtualAddress);
    02139             }
    02140         }
    02141     }
    02142 #if DBG
    02143     else {
    02144         Process = PsGetCurrentProcess();
    02145         ASSERT (Process->Vm.u.Flags.WriteWatch == 0);
    02146     }
    02147 #endif
    02148
    02149     UNLOCK_PFN (OldIrql);
    02150     return PreviousPte.u.Flush;
    02151 }
    02152
    02153 ULONG
    02154 MiSetProtectionOnTransitionPte (
    02155     IN PMMPTE PointerPte,
    02156     IN ULONG ProtectionMask
    02157     )
    02158
    02159     // nonpaged helper routine.
    02160
    02161 {
    02162     KIRQL OldIrql;
    02163     MMPTE PteContents;
    02164     PMMPFN Pfn1;
    02165
    02166     //
    02167     // This is a transition PTE. (Page is private)
    02168     //
    02169
    02170     //
    02171     // Need pfn mutex to ensure page doesn't become
    02172     // non-transition.
    02173     //
    02174
    02175     LOCK_PFN (OldIrql);
    02176
    02177     //
    02178     // Make sure the page is still a transition page.
    02179     //
    02180
    02181     PteContents = *(volatile MMPTE *)PointerPte;
    02182
    02183     if ((PteContents.u.Soft.Prototype == 0) &&
    02184                      (PointerPte->u.Soft.Transition == 1)) {
    02185
    02186         Pfn1 = MI_PFN_ELEMENT (
    02187                       PteContents.u.Trans.PageFrameNumber);
    02188
    02189         Pfn1->OriginalPte.u.Soft.Protection = ProtectionMask;
    02190         PointerPte->u.Soft.Protection = ProtectionMask;
    02191         UNLOCK_PFN (OldIrql);
    02192         return FALSE;
    02193     }
    02194
    02195     //
    02196     // Do this loop again for the same PTE.
    02197     //
    02198
    02199     UNLOCK_PFN (OldIrql);
    02200     return TRUE;
    02201 }
    02202
    02203 MMPTE
    02204 MiCaptureSystemPte (
    02205     IN PMMPTE PointerProtoPte,
    02206     IN PEPROCESS Process
    02207     )
    02208
    02209 /*++
    02210
    02211 Routine Description:
    02212
    02213     Nonpagable helper routine to capture the contents of a pagable PTE.
    02214
    02215 Arguments:
    02216
    02217     PointerProtoPte - Supplies a pointer to the prototype PTE.
    02218
    02219     Process - Supplies the relevant process.
    02220
    02221 Return Value:
    02222
    02223     PTE contents.
    02224
    02225 Environment:
    02226
    02227     Kernel mode.  Caller holds address space and working set mutexes if
    02228     Process is set.  Working set mutex was acquired unsafe.
    02229
    02230 --*/
    02231
    02232 {
    02233     MMPTE TempPte;
    02234     KIRQL OldIrql;
    02235
    02236     LOCK_PFN (OldIrql);
    02237     MiMakeSystemAddressValidPfnWs (PointerProtoPte, Process);
    02238     TempPte = *PointerProtoPte;
    02239     UNLOCK_PFN (OldIrql);
    02240     return TempPte;
    02241 }
    02242
    02243 NTSTATUS
    02244 MiCheckSecuredVad (
    02245     IN PMMVAD Vad,
    02246     IN PVOID Base,
    02247     IN SIZE_T Size,
    02248     IN ULONG ProtectionMask
    02249     )
    02250
    02251 /*++
    02252
    02253 Routine Description:
    02254
    02255     This routine checks to see if the specified VAD is secured in such
    02256     a way as to conflict with the address range and protection mask
    02257     specified.
    02258
    02259 Arguments:
    02260
    02261     Vad - Supplies a pointer to the VAD containing the address range.
    02262
    02263     Base - Supplies the base of the range the protection starts at.
    02264
    02265     Size - Supplies the size of the range.
    02266
    02267     ProtectionMask - Supplies the protection mask being set.
    02268
    02269 Return Value:
    02270
    02271     Status value.
    02272
    02273 Environment:
    02274
    02275     Kernel mode.
    02276
    02277 --*/
    02278
    02279 {
    02280     PVOID End;
    02281     PLIST_ENTRY Next;
    02282     PMMSECURE_ENTRY Entry;
    02283     NTSTATUS Status = STATUS_SUCCESS;
    02284
    02285     End = (PVOID)((PCHAR)Base + Size);
    02286
    02287     if (ProtectionMask < MM_SECURE_DELETE_CHECK) {
    02288         if ((Vad->u.VadFlags.NoChange == 1) &&
    02289             (Vad->u2.VadFlags2.SecNoChange == 1) &&
    02290             (Vad->u.VadFlags.Protection != ProtectionMask)) {
    02291
    02292             //
    02293             // An attempt is made at changing the protection
    02294             // of a SEC_NO_CHANGE section - return an error.
    02295             //
    02296
    02297             Status = STATUS_INVALID_PAGE_PROTECTION;
    02298             goto done;
    02299         }
    02300     } else {
    02301
    02302         //
    02303         // Deletion - set to no-access for check.  SEC_NOCHANGE allows
    02304         // deletion, but does not allow page protection changes.
    02305         //
    02306
    02307         ProtectionMask = 0;
    02308     }
    02309
    02310     if (Vad->u2.VadFlags2.OneSecured) {
    02311
    02312         if (((ULONG_PTR)Base <= Vad->u3.Secured.EndVpn) &&
    02313              ((ULONG_PTR)End >= Vad->u3.Secured.StartVpn)) {
    02314
    02315             //
    02316             // This region conflicts, check the protections.
    02317             //
    02318
    02319             if (ProtectionMask & MM_GUARD_PAGE) {
    02320                 Status = STATUS_INVALID_PAGE_PROTECTION;
    02321                 goto done;
    02322             }
    02323
    02324             if (Vad->u2.VadFlags2.ReadOnly) {
    02325                 if (MmReadWrite[ProtectionMask] < 10) {
    02326                     Status = STATUS_INVALID_PAGE_PROTECTION;
    02327                     goto done;
    02328                 }
    02329             } else {
    02330                 if (MmReadWrite[ProtectionMask] < 11) {
    02331                     Status = STATUS_INVALID_PAGE_PROTECTION;
    02332                     goto done;
    02333                 }
    02334             }
    02335         }
    02336
    02337     } else if (Vad->u2.VadFlags2.MultipleSecured) {
    02338
    02339         Next = Vad->u3.List.Flink;
    02340         do {
    02341             Entry = CONTAINING_RECORD( Next,
    02342                                        MMSECURE_ENTRY,
    02343                                        List);
    02344
    02345             if (((ULONG_PTR)Base <= Entry->EndVpn) &&
    02346                 ((ULONG_PTR)End >= Entry->StartVpn)) {
    02347
    02348                 //
    02349                 // This region conflicts, check the protections.
    02350                 //
    02351
    02352                 if (ProtectionMask & MM_GUARD_PAGE) {
    02353                     Status = STATUS_INVALID_PAGE_PROTECTION;
    02354                     goto done;
    02355                 }
    02356
    02357                 if (Entry->u2.VadFlags2.ReadOnly) {
    02358                     if (MmReadWrite[ProtectionMask] < 10) {
    02359                         Status = STATUS_INVALID_PAGE_PROTECTION;
    02360                         goto done;
    02361                     }
    02362                 } else {
    02363                     if (MmReadWrite[ProtectionMask] < 11) {
    02364                         Status = STATUS_INVALID_PAGE_PROTECTION;
    02365                         goto done;
    02366                     }
    02367                 }
    02368             }
    02369             Next = Entry->List.Flink;
    02370         } while (Entry->List.Flink != &Vad->u3.List);
    02371     }
    02372
    02373 done:
    02374     return Status;
    02375 }
    02376
    02377 #ifdef PFN_CONSISTENCY
    02378
    02379 BOOLEAN MiCheckPfnState = FALSE;
    02380
    02381 VOID
    02382 MiMapInPfnDatabase (
    02383     VOID
    02384     )
    02385
    02386 /*++
    02387
    02388 Routine Description:
    02389
    02390     This routine allows PFN database access (removes write-protection).
    02391
    02392 Arguments:
    02393
    02394     None.
    02395
    02396 Return Value:
    02397
    02398     None.
    02399
    02400 Environment:
    02401
    02402     Kernel mode.  The PFN database must be locked at DISPATCH level on entry.
    02403
    02404 --*/
    02405
    02406 {
    02407     PFN_NUMBER i;
    02408     PMMPTE PointerPte;
    02409
    02410     MM_PFN_LOCK_ASSERT();
    02411
    02412     ASSERT (MiPfnStartPte);
    02413
    02414     PointerPte = MiPfnStartPte;
    02415
    02416     for (i = 0; i < MiPfnPtes; i += 1) {
    02417
    02418         //
    02419         // Account for a sparse database
    02420         //
    02421
    02422         if (PointerPte->u.Hard.Valid == 1) {
    02423 #if defined (_ALPHA_)
    02424             if (MiCheckPfnState == TRUE) {
    02425                 ASSERT (PointerPte->u.Hard.Dirty == 0);
    02426             }
    02427             PointerPte->u.Hard.Dirty = 1;
    02428 #elif defined (NT_UP)
    02429             if (MiCheckPfnState == TRUE) {
    02430                 ASSERT (PointerPte->u.Hard.Write == 0);
    02431             }
    02432             PointerPte->u.Hard.Write = 1;
    02433 #else
    02434             if (MiCheckPfnState == TRUE) {
    02435                 ASSERT (PointerPte->u.Hard.Writable == 0);
    02436             }
    02437             PointerPte->u.Hard.Writable = 1;
    02438 #endif
    02439         }
    02440         PointerPte += 1;
    02441     }
    02442     KeFlushEntireTb (TRUE, TRUE);
    02443 }
    02444
    02445
    02446 VOID
    02447 MiUnMapPfnDatabase (
    02448     VOID
    02449     )
    02450
    02451 /*++
    02452
    02453 Routine Description:
    02454
    02455     This routine write protects the PFN database to catch errant pointers.
    02456
    02457 Arguments:
    02458
    02459     None.
    02460
    02461 Return Value:
    02462
    02463     None.
    02464
    02465 Environment:
    02466
    02467     Kernel mode.  The PFN database must be locked at DISPATCH level on entry.
    02468
    02469 --*/
    02470
    02471 {
    02472     PFN_NUMBER i;
    02473     PMMPTE PointerPte;
    02474
    02475     MM_PFN_LOCK_ASSERT();
    02476
    02477     ASSERT (MiPfnStartPte);
    02478
    02479     if (MiPfnProtectionEnabled == FALSE) {
    02480         //
    02481         // We're bugchecking, so don't unmap this anymore.
    02482         //
    02483         return;
    02484     }
    02485
    02486     PointerPte = MiPfnStartPte;
    02487
    02488     for (i = 0; i < MiPfnPtes; i += 1) {
    02489
    02490         //
    02491         // Account for a sparse database
    02492         //
    02493
    02494         if (PointerPte->u.Hard.Valid == 1) {
    02495 #if defined (_ALPHA_)
    02496             if (MiCheckPfnState == TRUE) {
    02497                 ASSERT (PointerPte->u.Hard.Dirty == 1);
    02498             }
    02499             PointerPte->u.Hard.Dirty = 0;
    02500 #elif defined (NT_UP)
    02501             if (MiCheckPfnState == TRUE) {
    02502                 ASSERT (PointerPte->u.Hard.Write == 1);
    02503             }
    02504             PointerPte->u.Hard.Write = 0;
    02505 #else
    02506             if (MiCheckPfnState == TRUE) {
    02507                 ASSERT (PointerPte->u.Hard.Writable == 1);
    02508             }
    02509             PointerPte->u.Hard.Writable = 0;
    02510 #endif
    02511             PointerPte += 1;
    02512         }
    02513     }
    02514     KeFlushEntireTb (TRUE, TRUE);
    02515 }
    02516
    02517 VOID
    02518 MiSetOriginalPteProtection (
    02519     IN PMMPFN Pfn1,
    02520     IN ULONG ProtectionMask
    02521     )
    02522
    02523 /*++
    02524
    02525 Routine Description:
    02526
    02527     This routine updates the OriginalPte's protection in the
    02528     specified PFN database entry.
    02529
    02530 Arguments:
    02531
    02532     Pfn1 - Supplies a pointer to the PFN database entry.
    02533
    02534     OriginalPteProtection - Supplies the requested original PTE protection.
    02535
    02536 Return Value:
    02537
    02538     None.
    02539
    02540 Environment:
    02541
    02542     Kernel mode.
    02543
    02544 --*/
    02545
    02546 {
    02547     KIRQL OldIrql;
    02548
    02549     LOCK_PFN (OldIrql);
    02550     Pfn1->OriginalPte.u.Soft.Protection = ProtectionMask;
    02551     UNLOCK_PFN (OldIrql);
    02552 }
    02553
    02554 VOID
    02555 MiSetModified (
    02556     IN PMMPFN Pfn1,
    02557     IN ULONG Dirty
    02558     )
    02559
    02560 /*++
    02561
    02562 Routine Description:
    02563
    02564     This routine marks the specified PFN database entry as modified or clean.
    02565
    02566 Arguments:
    02567
    02568     Pfn1 - Supplies a pointer to the PFN database entry.
    02569
    02570     Dirty - Supplies TRUE if the PFN should be marked modified, FALSE if clean.
    02571
    02572 Return Value:
    02573
    02574     None.
    02575
    02576 Environment:
    02577
    02578     Kernel mode.
    02579
    02580 --*/
    02581
    02582 {
    02583     KIRQL OldIrql;
    02584
    02585     LOCK_PFN (OldIrql);
    02586     Pfn1->u3.e1.Modified = Dirty;
    02587     UNLOCK_PFN (OldIrql);
    02588 }
    02589
    02590 #endif // PFN_CONSISTENCY