xref: /reactos/ntoskrnl/mm/ARM3/virtual.c (revision 5329e475)
1 /*
2  * PROJECT:         ReactOS Kernel
3  * LICENSE:         BSD - See COPYING.ARM in the top level directory
4  * FILE:            ntoskrnl/mm/ARM3/virtual.c
5  * PURPOSE:         ARM Memory Manager Virtual Memory Management
6  * PROGRAMMERS:     ReactOS Portable Systems Group
7  */
8 
9 /* INCLUDES *******************************************************************/
10 
11 #include <ntoskrnl.h>
12 #define NDEBUG
13 #include <debug.h>
14 
15 #define MODULE_INVOLVED_IN_ARM3
16 #include <mm/ARM3/miarm.h>
17 
18 #define MI_MAPPED_COPY_PAGES  14
19 #define MI_POOL_COPY_BYTES    512
20 #define MI_MAX_TRANSFER_SIZE  64 * 1024
21 
22 NTSTATUS NTAPI
23 MiProtectVirtualMemory(IN PEPROCESS Process,
24                        IN OUT PVOID *BaseAddress,
25                        IN OUT PSIZE_T NumberOfBytesToProtect,
26                        IN ULONG NewAccessProtection,
27                        OUT PULONG OldAccessProtection  OPTIONAL);
28 
29 VOID
30 NTAPI
31 MiFlushTbAndCapture(IN PMMVAD FoundVad,
32                     IN PMMPTE PointerPte,
33                     IN ULONG ProtectionMask,
34                     IN PMMPFN Pfn1,
35                     IN BOOLEAN CaptureDirtyBit);
36 
37 
38 /* PRIVATE FUNCTIONS **********************************************************/
39 
40 ULONG
41 NTAPI
MiCalculatePageCommitment(IN ULONG_PTR StartingAddress,IN ULONG_PTR EndingAddress,IN PMMVAD Vad,IN PEPROCESS Process)42 MiCalculatePageCommitment(IN ULONG_PTR StartingAddress,
43                           IN ULONG_PTR EndingAddress,
44                           IN PMMVAD Vad,
45                           IN PEPROCESS Process)
46 {
47     PMMPTE PointerPte, LastPte;
48     PMMPDE PointerPde;
49     BOOLEAN OnPdeBoundary = TRUE;
50 #if _MI_PAGING_LEVELS >= 3
51     PMMPPE PointerPpe;
52     BOOLEAN OnPpeBoundary = TRUE;
53 #if _MI_PAGING_LEVELS == 4
54     PMMPXE PointerPxe;
55     BOOLEAN OnPxeBoundary = TRUE;
56 #endif
57 #endif
58 
59     /* Make sure this all makes sense */
60     ASSERT(PsGetCurrentThread()->OwnsProcessWorkingSetExclusive || PsGetCurrentThread()->OwnsProcessWorkingSetShared);
61     ASSERT(EndingAddress >= StartingAddress);
62     PointerPte = MiAddressToPte(StartingAddress);
63     LastPte = MiAddressToPte(EndingAddress);
64 
65     /*
66      * In case this is a committed VAD, assume the whole range is committed
67      * and count the individually decommitted pages.
68      * In case it is not, assume the range is not committed and count the individually committed pages.
69      */
70     ULONG_PTR CommittedPages = Vad->u.VadFlags.MemCommit ? BYTES_TO_PAGES(EndingAddress - StartingAddress) : 0;
71 
72     while (PointerPte <= LastPte)
73     {
74 #if _MI_PAGING_LEVELS == 4
75         /* Check if PXE was ever paged in. */
76         if (OnPxeBoundary)
77         {
78             PointerPxe = MiPteToPxe(PointerPte);
79 
80             /* Check that this loop is sane */
81             ASSERT(OnPpeBoundary);
82             ASSERT(OnPdeBoundary);
83 
84             if (PointerPxe->u.Long == 0)
85             {
86                 PointerPxe++;
87                 PointerPte = MiPxeToPte(PointerPde);
88                 continue;
89             }
90 
91             if (PointerPxe->u.Hard.Valid == 0)
92                 MiMakeSystemAddressValid(MiPteToPpe(PointerPte), Process);
93         }
94         ASSERT(PointerPxe->u.Hard.Valid == 1);
95 #endif
96 
97 #if _MI_PAGING_LEVELS >= 3
98         /* Now PPE */
99         if (OnPpeBoundary)
100         {
101             PointerPpe = MiPteToPpe(PointerPte);
102 
103             /* Sanity again */
104             ASSERT(OnPdeBoundary);
105 
106             if (PointerPpe->u.Long == 0)
107             {
108                 PointerPpe++;
109                 PointerPte = MiPpeToPte(PointerPpe);
110 #if _MI_PAGING_LEVELS == 4
111                 OnPxeBoundary = MiIsPteOnPxeBoundary(PointerPte);
112 #endif
113                 continue;
114             }
115 
116             if (PointerPpe->u.Hard.Valid == 0)
117                 MiMakeSystemAddressValid(MiPteToPde(PointerPte), Process);
118         }
119         ASSERT(PointerPpe->u.Hard.Valid == 1);
120 #endif
121 
122         /* Last level is the PDE */
123         if (OnPdeBoundary)
124         {
125             PointerPde = MiPteToPde(PointerPte);
126             if (PointerPde->u.Long == 0)
127             {
128                 PointerPde++;
129                 PointerPte = MiPdeToPte(PointerPde);
130 #if _MI_PAGING_LEVELS >= 3
131                 OnPpeBoundary = MiIsPteOnPpeBoundary(PointerPte);
132 #if _MI_PAGING_LEVELS == 4
133                 OnPxeBoundary = MiIsPteOnPxeBoundary(PointerPte);
134 #endif
135 #endif
136                 continue;
137             }
138 
139             if (PointerPde->u.Hard.Valid == 0)
140                 MiMakeSystemAddressValid(PointerPte, Process);
141         }
142         ASSERT(PointerPde->u.Hard.Valid == 1);
143 
144         /* Is this PTE demand zero? */
145         if (PointerPte->u.Long != 0)
146         {
147             /* It isn't -- is it a decommited, invalid, or faulted PTE? */
148             if ((PointerPte->u.Hard.Valid == 0) &&
149                 (PointerPte->u.Soft.Protection == MM_DECOMMIT) &&
150                 ((PointerPte->u.Soft.Prototype == 0) ||
151                     (PointerPte->u.Soft.PageFileHigh == MI_PTE_LOOKUP_NEEDED)))
152             {
153                 /* It is, so remove it from the count of committed pages if we have to */
154                 if (Vad->u.VadFlags.MemCommit)
155                     CommittedPages--;
156             }
157             else if (!Vad->u.VadFlags.MemCommit)
158             {
159                 /* It is a valid, non-decommited, non-paged out PTE. Count it in. */
160                 CommittedPages++;
161             }
162         }
163 
164         /* Move to the next PTE */
165         PointerPte++;
166         /* Manage page tables */
167         OnPdeBoundary = MiIsPteOnPdeBoundary(PointerPte);
168 #if _MI_PAGING_LEVELS >= 3
169         OnPpeBoundary = MiIsPteOnPpeBoundary(PointerPte);
170 #if _MI_PAGING_LEVELS == 4
171         OnPxeBoundary = MiIsPteOnPxeBoundary(PointerPte);
172 #endif
173 #endif
174     }
175 
176     /* Make sure we didn't mess this up */
177     ASSERT(CommittedPages <= BYTES_TO_PAGES(EndingAddress - StartingAddress));
178     return CommittedPages;
179 }
180 
181 ULONG
182 NTAPI
MiMakeSystemAddressValid(IN PVOID PageTableVirtualAddress,IN PEPROCESS CurrentProcess)183 MiMakeSystemAddressValid(IN PVOID PageTableVirtualAddress,
184                          IN PEPROCESS CurrentProcess)
185 {
186     NTSTATUS Status;
187     BOOLEAN WsShared = FALSE, WsSafe = FALSE, LockChange = FALSE;
188     PETHREAD CurrentThread = PsGetCurrentThread();
189 
190     /* Must be a non-pool page table, since those are double-mapped already */
191     ASSERT(PageTableVirtualAddress > MM_HIGHEST_USER_ADDRESS);
192     ASSERT((PageTableVirtualAddress < MmPagedPoolStart) ||
193            (PageTableVirtualAddress > MmPagedPoolEnd));
194 
195     /* Working set lock or PFN lock should be held */
196     ASSERT(KeAreAllApcsDisabled() == TRUE);
197 
198     /* Check if the page table is valid */
199     while (!MmIsAddressValid(PageTableVirtualAddress))
200     {
201         /* Release the working set lock */
202         MiUnlockProcessWorkingSetForFault(CurrentProcess,
203                                           CurrentThread,
204                                           &WsSafe,
205                                           &WsShared);
206 
207         /* Fault it in */
208         Status = MmAccessFault(FALSE, PageTableVirtualAddress, KernelMode, NULL);
209         if (!NT_SUCCESS(Status))
210         {
211             /* This should not fail */
212             KeBugCheckEx(KERNEL_DATA_INPAGE_ERROR,
213                          1,
214                          Status,
215                          (ULONG_PTR)CurrentProcess,
216                          (ULONG_PTR)PageTableVirtualAddress);
217         }
218 
219         /* Lock the working set again */
220         MiLockProcessWorkingSetForFault(CurrentProcess,
221                                         CurrentThread,
222                                         WsSafe,
223                                         WsShared);
224 
225         /* This flag will be useful later when we do better locking */
226         LockChange = TRUE;
227     }
228 
229     /* Let caller know what the lock state is */
230     return LockChange;
231 }
232 
233 ULONG
234 NTAPI
MiMakeSystemAddressValidPfn(IN PVOID VirtualAddress,IN KIRQL OldIrql)235 MiMakeSystemAddressValidPfn(IN PVOID VirtualAddress,
236                             IN KIRQL OldIrql)
237 {
238     NTSTATUS Status;
239     BOOLEAN LockChange = FALSE;
240 
241     /* Must be e kernel address */
242     ASSERT(VirtualAddress > MM_HIGHEST_USER_ADDRESS);
243 
244     /* Check if the page is valid */
245     while (!MmIsAddressValid(VirtualAddress))
246     {
247         /* Release the PFN database */
248         MiReleasePfnLock(OldIrql);
249 
250         /* Fault it in */
251         Status = MmAccessFault(FALSE, VirtualAddress, KernelMode, NULL);
252         if (!NT_SUCCESS(Status))
253         {
254             /* This should not fail */
255             KeBugCheckEx(KERNEL_DATA_INPAGE_ERROR,
256                          3,
257                          Status,
258                          0,
259                          (ULONG_PTR)VirtualAddress);
260         }
261 
262         /* This flag will be useful later when we do better locking */
263         LockChange = TRUE;
264 
265         /* Lock the PFN database */
266         OldIrql = MiAcquirePfnLock();
267     }
268 
269     /* Let caller know what the lock state is */
270     return LockChange;
271 }
272 
273 PFN_COUNT
274 NTAPI
MiDeleteSystemPageableVm(IN PMMPTE PointerPte,IN PFN_NUMBER PageCount,IN ULONG Flags,OUT PPFN_NUMBER ValidPages)275 MiDeleteSystemPageableVm(IN PMMPTE PointerPte,
276                          IN PFN_NUMBER PageCount,
277                          IN ULONG Flags,
278                          OUT PPFN_NUMBER ValidPages)
279 {
280     PFN_COUNT ActualPages = 0;
281     PETHREAD CurrentThread = PsGetCurrentThread();
282     PMMPFN Pfn1, Pfn2;
283     PFN_NUMBER PageFrameIndex, PageTableIndex;
284     KIRQL OldIrql;
285     ASSERT(KeGetCurrentIrql() <= APC_LEVEL);
286 
287     /* Lock the system working set */
288     MiLockWorkingSet(CurrentThread, &MmSystemCacheWs);
289 
290     /* Loop all pages */
291     while (PageCount)
292     {
293         /* Make sure there's some data about the page */
294         if (PointerPte->u.Long)
295         {
296             /* Normally this is one possibility -- freeing a valid page */
297             if (PointerPte->u.Hard.Valid)
298             {
299                 /* Get the page PFN */
300                 PageFrameIndex = PFN_FROM_PTE(PointerPte);
301                 Pfn1 = MiGetPfnEntry(PageFrameIndex);
302 
303                 /* Should not have any working set data yet */
304                 ASSERT(Pfn1->u1.WsIndex == 0);
305 
306                 /* Actual valid, legitimate, pages */
307                 if (ValidPages) (*ValidPages)++;
308 
309                 /* Get the page table entry */
310                 PageTableIndex = Pfn1->u4.PteFrame;
311                 Pfn2 = MiGetPfnEntry(PageTableIndex);
312 
313                 /* Lock the PFN database */
314                 OldIrql = MiAcquirePfnLock();
315 
316                 /* Delete it the page */
317                 MI_SET_PFN_DELETED(Pfn1);
318                 MiDecrementShareCount(Pfn1, PageFrameIndex);
319 
320                 /* Decrement the page table too */
321                 MiDecrementShareCount(Pfn2, PageTableIndex);
322 
323                 /* Release the PFN database */
324                 MiReleasePfnLock(OldIrql);
325 
326                 /* Destroy the PTE */
327                 MI_ERASE_PTE(PointerPte);
328             }
329             else
330             {
331                 /* As always, only handle current ARM3 scenarios */
332                 ASSERT(PointerPte->u.Soft.Prototype == 0);
333                 ASSERT(PointerPte->u.Soft.Transition == 0);
334 
335                 /*
336                  * The only other ARM3 possibility is a demand zero page, which would
337                  * mean freeing some of the paged pool pages that haven't even been
338                  * touched yet, as part of a larger allocation.
339                  *
340                  * Right now, we shouldn't expect any page file information in the PTE
341                  */
342                 ASSERT(PointerPte->u.Soft.PageFileHigh == 0);
343 
344                 /* Destroy the PTE */
345                 MI_ERASE_PTE(PointerPte);
346             }
347 
348             /* Actual legitimate pages */
349             ActualPages++;
350         }
351 
352         /* Keep going */
353         PointerPte++;
354         PageCount--;
355     }
356 
357     /* Release the working set */
358     MiUnlockWorkingSet(CurrentThread, &MmSystemCacheWs);
359 
360     /* Flush the entire TLB */
361     KeFlushEntireTb(TRUE, TRUE);
362 
363     /* Done */
364     return ActualPages;
365 }
366 
367 VOID
368 NTAPI
MiDeletePte(IN PMMPTE PointerPte,IN PVOID VirtualAddress,IN PEPROCESS CurrentProcess,IN PMMPTE PrototypePte)369 MiDeletePte(IN PMMPTE PointerPte,
370             IN PVOID VirtualAddress,
371             IN PEPROCESS CurrentProcess,
372             IN PMMPTE PrototypePte)
373 {
374     PMMPFN Pfn1;
375     MMPTE TempPte;
376     PFN_NUMBER PageFrameIndex;
377     PMMPDE PointerPde;
378 
379     /* PFN lock must be held */
380     MI_ASSERT_PFN_LOCK_HELD();
381 
382     /* WorkingSet must be exclusively locked */
383     ASSERT(MM_ANY_WS_LOCK_HELD_EXCLUSIVE(PsGetCurrentThread()));
384 
385     /* This must be current process. */
386     ASSERT(CurrentProcess == PsGetCurrentProcess());
387 
388     /* Capture the PTE */
389     TempPte = *PointerPte;
390 
391     /* See if the PTE is valid */
392     if (TempPte.u.Hard.Valid == 0)
393     {
394         /* Prototype and paged out PTEs not supported yet */
395         ASSERT(TempPte.u.Soft.Prototype == 0);
396         ASSERT((TempPte.u.Soft.PageFileHigh == 0) || (TempPte.u.Soft.Transition == 1));
397 
398         if (TempPte.u.Soft.Transition)
399         {
400             /* Get the PFN entry */
401             PageFrameIndex = PFN_FROM_PTE(&TempPte);
402             Pfn1 = MiGetPfnEntry(PageFrameIndex);
403 
404             DPRINT("Pte %p is transitional!\n", PointerPte);
405 
406             /* Make sure the saved PTE address is valid */
407             ASSERT((PMMPTE)((ULONG_PTR)Pfn1->PteAddress & ~0x1) == PointerPte);
408 
409             /* Destroy the PTE */
410             MI_ERASE_PTE(PointerPte);
411 
412             /* Drop the reference on the page table. */
413             MiDecrementShareCount(MiGetPfnEntry(Pfn1->u4.PteFrame), Pfn1->u4.PteFrame);
414 
415             /* In case of shared page, the prototype PTE must be in transition, not the process one */
416             ASSERT(Pfn1->u3.e1.PrototypePte == 0);
417 
418             /* Delete the PFN */
419             MI_SET_PFN_DELETED(Pfn1);
420 
421             /* It must be either free (refcount == 0) or being written (refcount == 1) */
422             ASSERT(Pfn1->u3.e2.ReferenceCount == Pfn1->u3.e1.WriteInProgress);
423 
424             /* See if we must free it ourselves, or if it will be freed once I/O is over */
425             if (Pfn1->u3.e2.ReferenceCount == 0)
426             {
427                 /* And it should be in standby or modified list */
428                 ASSERT((Pfn1->u3.e1.PageLocation == ModifiedPageList) || (Pfn1->u3.e1.PageLocation == StandbyPageList));
429 
430                 /* Unlink it and set its reference count to one */
431                 MiUnlinkPageFromList(Pfn1);
432                 Pfn1->u3.e2.ReferenceCount++;
433 
434                 /* This will put it back in free list and clean properly up */
435                 MiDecrementReferenceCount(Pfn1, PageFrameIndex);
436             }
437             return;
438         }
439     }
440 
441     /* Get the PFN entry */
442     PageFrameIndex = PFN_FROM_PTE(&TempPte);
443     Pfn1 = MiGetPfnEntry(PageFrameIndex);
444 
445     /* Check if this is a valid, prototype PTE */
446     if (Pfn1->u3.e1.PrototypePte == 1)
447     {
448         /* Get the PDE and make sure it's faulted in */
449         PointerPde = MiPteToPde(PointerPte);
450         if (PointerPde->u.Hard.Valid == 0)
451         {
452 #if (_MI_PAGING_LEVELS == 2)
453             /* Could be paged pool access from a new process -- synchronize the page directories */
454             if (!NT_SUCCESS(MiCheckPdeForPagedPool(VirtualAddress)))
455             {
456 #endif
457                 /* The PDE must be valid at this point */
458                 KeBugCheckEx(MEMORY_MANAGEMENT,
459                              0x61940,
460                              (ULONG_PTR)PointerPte,
461                              PointerPte->u.Long,
462                              (ULONG_PTR)VirtualAddress);
463             }
464 #if (_MI_PAGING_LEVELS == 2)
465         }
466 #endif
467         /* Drop the share count on the page table */
468         PointerPde = MiPteToPde(PointerPte);
469         MiDecrementShareCount(MiGetPfnEntry(PointerPde->u.Hard.PageFrameNumber),
470             PointerPde->u.Hard.PageFrameNumber);
471 
472         /* Drop the share count */
473         MiDecrementShareCount(Pfn1, PageFrameIndex);
474 
475         /* Either a fork, or this is the shared user data page */
476         if ((PointerPte <= MiHighestUserPte) && (PrototypePte != Pfn1->PteAddress))
477         {
478             /* If it's not the shared user page, then crash, since there's no fork() yet */
479             if ((PAGE_ALIGN(VirtualAddress) != (PVOID)USER_SHARED_DATA) ||
480                  (MmHighestUserAddress <= (PVOID)USER_SHARED_DATA))
481             {
482                 /* Must be some sort of memory corruption */
483                 KeBugCheckEx(MEMORY_MANAGEMENT,
484                              0x400,
485                              (ULONG_PTR)PointerPte,
486                              (ULONG_PTR)PrototypePte,
487                              (ULONG_PTR)Pfn1->PteAddress);
488             }
489         }
490 
491         /* Erase it */
492         MI_ERASE_PTE(PointerPte);
493     }
494     else
495     {
496         /* Make sure the saved PTE address is valid */
497         if ((PMMPTE)((ULONG_PTR)Pfn1->PteAddress & ~0x1) != PointerPte)
498         {
499             /* The PFN entry is illegal, or invalid */
500             KeBugCheckEx(MEMORY_MANAGEMENT,
501                          0x401,
502                          (ULONG_PTR)PointerPte,
503                          PointerPte->u.Long,
504                          (ULONG_PTR)Pfn1->PteAddress);
505         }
506 
507         /* Erase the PTE */
508         MI_ERASE_PTE(PointerPte);
509 
510         /* There should only be 1 shared reference count */
511         ASSERT(Pfn1->u2.ShareCount == 1);
512 
513         /* Drop the reference on the page table. */
514         MiDecrementShareCount(MiGetPfnEntry(Pfn1->u4.PteFrame), Pfn1->u4.PteFrame);
515 
516         /* Mark the PFN for deletion and dereference what should be the last ref */
517         MI_SET_PFN_DELETED(Pfn1);
518         MiDecrementShareCount(Pfn1, PageFrameIndex);
519 
520         /* We should eventually do this */
521         //CurrentProcess->NumberOfPrivatePages--;
522     }
523 
524     /* Flush the TLB */
525     KeFlushCurrentTb();
526 }
527 
528 VOID
529 NTAPI
MiDeleteVirtualAddresses(IN ULONG_PTR Va,IN ULONG_PTR EndingAddress,IN PMMVAD Vad)530 MiDeleteVirtualAddresses(IN ULONG_PTR Va,
531                          IN ULONG_PTR EndingAddress,
532                          IN PMMVAD Vad)
533 {
534     PMMPTE PointerPte, PrototypePte, LastPrototypePte;
535     PMMPDE PointerPde;
536 #if (_MI_PAGING_LEVELS >= 3)
537     PMMPPE PointerPpe;
538 #endif
539 #if (_MI_PAGING_LEVELS >= 4)
540     PMMPPE PointerPxe;
541 #endif
542     MMPTE TempPte;
543     PEPROCESS CurrentProcess;
544     KIRQL OldIrql;
545     BOOLEAN AddressGap = FALSE;
546     PSUBSECTION Subsection;
547 
548     /* Get out if this is a fake VAD, RosMm will free the marea pages */
549     if ((Vad) && (Vad->u.VadFlags.Spare == 1)) return;
550 
551     /* Get the current process */
552     CurrentProcess = PsGetCurrentProcess();
553 
554     /* Check if this is a section VAD or a VM VAD */
555     if (!(Vad) || (Vad->u.VadFlags.PrivateMemory) || !(Vad->FirstPrototypePte))
556     {
557         /* Don't worry about prototypes */
558         PrototypePte = LastPrototypePte = NULL;
559     }
560     else
561     {
562         /* Get the prototype PTE */
563         PrototypePte = Vad->FirstPrototypePte;
564         LastPrototypePte = Vad->FirstPrototypePte + 1;
565     }
566 
567     /* In all cases, we don't support fork() yet */
568     ASSERT(CurrentProcess->CloneRoot == NULL);
569 
570     /* Loop the PTE for each VA (EndingAddress is inclusive!) */
571     while (Va <= EndingAddress)
572     {
573 #if (_MI_PAGING_LEVELS >= 4)
574         /* Get the PXE and check if it's valid */
575         PointerPxe = MiAddressToPxe((PVOID)Va);
576         if (!PointerPxe->u.Hard.Valid)
577         {
578             /* Check for unmapped range and skip it */
579             if (!PointerPxe->u.Long)
580             {
581                 /* There are gaps in the address space */
582                 AddressGap = TRUE;
583 
584                 /* Update Va and continue looping */
585                 Va = (ULONG_PTR)MiPxeToAddress(PointerPxe + 1);
586                 continue;
587             }
588 
589             /* Make the PXE valid */
590             MiMakeSystemAddressValid(MiPteToAddress(PointerPxe), CurrentProcess);
591         }
592 #endif
593 #if (_MI_PAGING_LEVELS >= 3)
594         /* Get the PPE and check if it's valid */
595         PointerPpe = MiAddressToPpe((PVOID)Va);
596         if (!PointerPpe->u.Hard.Valid)
597         {
598             /* Check for unmapped range and skip it */
599             if (!PointerPpe->u.Long)
600             {
601                 /* There are gaps in the address space */
602                 AddressGap = TRUE;
603 
604                 /* Update Va and continue looping */
605                 Va = (ULONG_PTR)MiPpeToAddress(PointerPpe + 1);
606                 continue;
607             }
608 
609             /* Make the PPE valid */
610             MiMakeSystemAddressValid(MiPteToAddress(PointerPpe), CurrentProcess);
611         }
612 #endif
613         /* Skip invalid PDEs */
614         PointerPde = MiAddressToPde((PVOID)Va);
615         if (!PointerPde->u.Long)
616         {
617             /* There are gaps in the address space */
618             AddressGap = TRUE;
619 
620             /* Check if all the PDEs are invalid, so there's nothing to free */
621             Va = (ULONG_PTR)MiPdeToAddress(PointerPde + 1);
622             continue;
623         }
624 
625         /* Now check if the PDE is mapped in */
626         if (!PointerPde->u.Hard.Valid)
627         {
628             /* It isn't, so map it in */
629             PointerPte = MiPteToAddress(PointerPde);
630             MiMakeSystemAddressValid(PointerPte, CurrentProcess);
631         }
632 
633         /* Now we should have a valid PDE, mapped in, and still have some VA */
634         ASSERT(PointerPde->u.Hard.Valid == 1);
635         ASSERT(Va <= EndingAddress);
636 
637         /* Check if this is a section VAD with gaps in it */
638         if ((AddressGap) && (LastPrototypePte))
639         {
640             /* We need to skip to the next correct prototype PTE */
641             PrototypePte = MI_GET_PROTOTYPE_PTE_FOR_VPN(Vad, Va >> PAGE_SHIFT);
642 
643             /* And we need the subsection to skip to the next last prototype PTE */
644             Subsection = MiLocateSubsection(Vad, Va >> PAGE_SHIFT);
645             if (Subsection)
646             {
647                 /* Found it! */
648                 LastPrototypePte = &Subsection->SubsectionBase[Subsection->PtesInSubsection];
649             }
650             else
651             {
652                 /* No more subsections, we are done with prototype PTEs */
653                 PrototypePte = NULL;
654             }
655         }
656 
657         /* Lock the PFN Database while we delete the PTEs */
658         OldIrql = MiAcquirePfnLock();
659         PointerPte = MiAddressToPte(Va);
660         do
661         {
662             /* Making sure the PDE is still valid */
663             ASSERT(PointerPde->u.Hard.Valid == 1);
664 
665             /* Capture the PDE and make sure it exists */
666             TempPte = *PointerPte;
667             if (TempPte.u.Long)
668             {
669                 /* Check if the PTE is actually mapped in */
670                 if (MI_IS_MAPPED_PTE(&TempPte))
671                 {
672                     /* Are we dealing with section VAD? */
673                     if ((LastPrototypePte) && (PrototypePte > LastPrototypePte))
674                     {
675                         /* We need to skip to the next correct prototype PTE */
676                         PrototypePte = MI_GET_PROTOTYPE_PTE_FOR_VPN(Vad, Va >> PAGE_SHIFT);
677 
678                         /* And we need the subsection to skip to the next last prototype PTE */
679                         Subsection = MiLocateSubsection(Vad, Va >> PAGE_SHIFT);
680                         if (Subsection)
681                         {
682                             /* Found it! */
683                             LastPrototypePte = &Subsection->SubsectionBase[Subsection->PtesInSubsection];
684                         }
685                         else
686                         {
687                             /* No more subsections, we are done with prototype PTEs */
688                             PrototypePte = NULL;
689                         }
690                     }
691 
692                     /* Check for prototype PTE */
693                     if ((TempPte.u.Hard.Valid == 0) &&
694                         (TempPte.u.Soft.Prototype == 1))
695                     {
696                         /* Just nuke it */
697                         MI_ERASE_PTE(PointerPte);
698                     }
699                     else
700                     {
701                         /* Delete the PTE proper */
702                         MiDeletePte(PointerPte,
703                                     (PVOID)Va,
704                                     CurrentProcess,
705                                     PrototypePte);
706                     }
707                 }
708                 else
709                 {
710                     /* The PTE was never mapped, just nuke it here */
711                     MI_ERASE_PTE(PointerPte);
712                 }
713 
714                 if (MiDecrementPageTableReferences((PVOID)Va) == 0)
715                 {
716                     ASSERT(PointerPde->u.Long != 0);
717 
718                     /* Delete the PDE proper */
719                     MiDeletePde(PointerPde, CurrentProcess);
720 
721                     /* Continue with the next PDE */
722                     Va = (ULONG_PTR)MiPdeToAddress(PointerPde + 1);
723 
724                     /* Use this to detect address gaps */
725                     PointerPte++;
726 
727                     PrototypePte++;
728                     break;
729                 }
730             }
731 
732             /* Update the address and PTE for it */
733             Va += PAGE_SIZE;
734             PointerPte++;
735             PrototypePte++;
736         } while ((Va & (PDE_MAPPED_VA - 1)) && (Va <= EndingAddress));
737 
738         /* Release the lock */
739         MiReleasePfnLock(OldIrql);
740 
741         if (Va > EndingAddress) return;
742 
743         /* Check if we exited the loop regularly */
744         AddressGap = (PointerPte != MiAddressToPte(Va));
745     }
746 }
747 
748 LONG
MiGetExceptionInfo(IN PEXCEPTION_POINTERS ExceptionInfo,OUT PBOOLEAN HaveBadAddress,OUT PULONG_PTR BadAddress)749 MiGetExceptionInfo(IN PEXCEPTION_POINTERS ExceptionInfo,
750                    OUT PBOOLEAN HaveBadAddress,
751                    OUT PULONG_PTR BadAddress)
752 {
753     PEXCEPTION_RECORD ExceptionRecord;
754     PAGED_CODE();
755 
756     //
757     // Assume default
758     //
759     *HaveBadAddress = FALSE;
760 
761     //
762     // Get the exception record
763     //
764     ExceptionRecord = ExceptionInfo->ExceptionRecord;
765 
766     //
767     // Look at the exception code
768     //
769     if ((ExceptionRecord->ExceptionCode == STATUS_ACCESS_VIOLATION) ||
770         (ExceptionRecord->ExceptionCode == STATUS_GUARD_PAGE_VIOLATION) ||
771         (ExceptionRecord->ExceptionCode == STATUS_IN_PAGE_ERROR))
772     {
773         //
774         // We can tell the address if we have more than one parameter
775         //
776         if (ExceptionRecord->NumberParameters > 1)
777         {
778             //
779             // Return the address
780             //
781             *HaveBadAddress = TRUE;
782             *BadAddress = ExceptionRecord->ExceptionInformation[1];
783         }
784     }
785 
786     //
787     // Continue executing the next handler
788     //
789     return EXCEPTION_EXECUTE_HANDLER;
790 }
791 
792 NTSTATUS
793 NTAPI
MiDoMappedCopy(IN PEPROCESS SourceProcess,IN PVOID SourceAddress,IN PEPROCESS TargetProcess,OUT PVOID TargetAddress,IN SIZE_T BufferSize,IN KPROCESSOR_MODE PreviousMode,OUT PSIZE_T ReturnSize)794 MiDoMappedCopy(IN PEPROCESS SourceProcess,
795                IN PVOID SourceAddress,
796                IN PEPROCESS TargetProcess,
797                OUT PVOID TargetAddress,
798                IN SIZE_T BufferSize,
799                IN KPROCESSOR_MODE PreviousMode,
800                OUT PSIZE_T ReturnSize)
801 {
802     PFN_NUMBER MdlBuffer[(sizeof(MDL) / sizeof(PFN_NUMBER)) + MI_MAPPED_COPY_PAGES + 1];
803     PMDL Mdl = (PMDL)MdlBuffer;
804     SIZE_T TotalSize, CurrentSize, RemainingSize;
805     volatile BOOLEAN FailedInProbe = FALSE;
806     volatile BOOLEAN PagesLocked = FALSE;
807     PVOID CurrentAddress = SourceAddress, CurrentTargetAddress = TargetAddress;
808     volatile PVOID MdlAddress = NULL;
809     KAPC_STATE ApcState;
810     BOOLEAN HaveBadAddress;
811     ULONG_PTR BadAddress;
812     NTSTATUS Status = STATUS_SUCCESS;
813     PAGED_CODE();
814 
815     //
816     // Calculate the maximum amount of data to move
817     //
818     TotalSize = MI_MAPPED_COPY_PAGES * PAGE_SIZE;
819     if (BufferSize <= TotalSize) TotalSize = BufferSize;
820     CurrentSize = TotalSize;
821     RemainingSize = BufferSize;
822 
823     //
824     // Loop as long as there is still data
825     //
826     while (RemainingSize > 0)
827     {
828         //
829         // Check if this transfer will finish everything off
830         //
831         if (RemainingSize < CurrentSize) CurrentSize = RemainingSize;
832 
833         //
834         // Attach to the source address space
835         //
836         KeStackAttachProcess(&SourceProcess->Pcb, &ApcState);
837 
838         //
839         // Check state for this pass
840         //
841         ASSERT(MdlAddress == NULL);
842         ASSERT(PagesLocked == FALSE);
843         ASSERT(FailedInProbe == FALSE);
844 
845         //
846         // Protect user-mode copy
847         //
848         _SEH2_TRY
849         {
850             //
851             // If this is our first time, probe the buffer
852             //
853             if ((CurrentAddress == SourceAddress) && (PreviousMode != KernelMode))
854             {
855                 //
856                 // Catch a failure here
857                 //
858                 FailedInProbe = TRUE;
859 
860                 //
861                 // Do the probe
862                 //
863                 ProbeForRead(SourceAddress, BufferSize, sizeof(CHAR));
864 
865                 //
866                 // Passed
867                 //
868                 FailedInProbe = FALSE;
869             }
870 
871             //
872             // Initialize and probe and lock the MDL
873             //
874             MmInitializeMdl(Mdl, CurrentAddress, CurrentSize);
875             MmProbeAndLockPages(Mdl, PreviousMode, IoReadAccess);
876             PagesLocked = TRUE;
877         }
878         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
879         {
880             Status = _SEH2_GetExceptionCode();
881         }
882         _SEH2_END
883 
884         /* Detach from source process */
885         KeUnstackDetachProcess(&ApcState);
886 
887         if (Status != STATUS_SUCCESS)
888         {
889             goto Exit;
890         }
891 
892         //
893         // Now map the pages
894         //
895         MdlAddress = MmMapLockedPagesSpecifyCache(Mdl,
896                                                   KernelMode,
897                                                   MmCached,
898                                                   NULL,
899                                                   FALSE,
900                                                   HighPagePriority);
901         if (!MdlAddress)
902         {
903             Status = STATUS_INSUFFICIENT_RESOURCES;
904             goto Exit;
905         }
906 
907         //
908         // Grab to the target process
909         //
910         KeStackAttachProcess(&TargetProcess->Pcb, &ApcState);
911 
912         _SEH2_TRY
913         {
914             //
915             // Check if this is our first time through
916             //
917             if ((CurrentTargetAddress == TargetAddress) && (PreviousMode != KernelMode))
918             {
919                 //
920                 // Catch a failure here
921                 //
922                 FailedInProbe = TRUE;
923 
924                 //
925                 // Do the probe
926                 //
927                 ProbeForWrite(TargetAddress, BufferSize, sizeof(CHAR));
928 
929                 //
930                 // Passed
931                 //
932                 FailedInProbe = FALSE;
933             }
934 
935             //
936             // Now do the actual move
937             //
938             RtlCopyMemory(CurrentTargetAddress, MdlAddress, CurrentSize);
939         }
940         _SEH2_EXCEPT(MiGetExceptionInfo(_SEH2_GetExceptionInformation(),
941                                         &HaveBadAddress,
942                                         &BadAddress))
943         {
944             *ReturnSize = BufferSize - RemainingSize;
945             //
946             // Check if we failed during the probe
947             //
948             if (FailedInProbe)
949             {
950                 //
951                 // Exit
952                 //
953                 Status = _SEH2_GetExceptionCode();
954             }
955             else
956             {
957                 //
958                 // Othewise we failed during the move.
959                 // Check if we know exactly where we stopped copying
960                 //
961                 if (HaveBadAddress)
962                 {
963                     //
964                     // Return the exact number of bytes copied
965                     //
966                     *ReturnSize = BadAddress - (ULONG_PTR)SourceAddress;
967                 }
968                 //
969                 // Return partial copy
970                 //
971                 Status = STATUS_PARTIAL_COPY;
972             }
973         }
974         _SEH2_END;
975 
976         /* Detach from target process */
977         KeUnstackDetachProcess(&ApcState);
978 
979         //
980         // Check for SEH status
981         //
982         if (Status != STATUS_SUCCESS)
983         {
984             goto Exit;
985         }
986 
987         //
988         // Unmap and unlock
989         //
990         MmUnmapLockedPages(MdlAddress, Mdl);
991         MdlAddress = NULL;
992         MmUnlockPages(Mdl);
993         PagesLocked = FALSE;
994 
995         //
996         // Update location and size
997         //
998         RemainingSize -= CurrentSize;
999         CurrentAddress = (PVOID)((ULONG_PTR)CurrentAddress + CurrentSize);
1000         CurrentTargetAddress = (PVOID)((ULONG_PTR)CurrentTargetAddress + CurrentSize);
1001     }
1002 
1003 Exit:
1004     if (MdlAddress != NULL)
1005         MmUnmapLockedPages(MdlAddress, Mdl);
1006     if (PagesLocked)
1007         MmUnlockPages(Mdl);
1008 
1009     //
1010     // All bytes read
1011     //
1012     if (Status == STATUS_SUCCESS)
1013         *ReturnSize = BufferSize;
1014     return Status;
1015 }
1016 
1017 NTSTATUS
1018 NTAPI
MiDoPoolCopy(IN PEPROCESS SourceProcess,IN PVOID SourceAddress,IN PEPROCESS TargetProcess,OUT PVOID TargetAddress,IN SIZE_T BufferSize,IN KPROCESSOR_MODE PreviousMode,OUT PSIZE_T ReturnSize)1019 MiDoPoolCopy(IN PEPROCESS SourceProcess,
1020              IN PVOID SourceAddress,
1021              IN PEPROCESS TargetProcess,
1022              OUT PVOID TargetAddress,
1023              IN SIZE_T BufferSize,
1024              IN KPROCESSOR_MODE PreviousMode,
1025              OUT PSIZE_T ReturnSize)
1026 {
1027     UCHAR StackBuffer[MI_POOL_COPY_BYTES];
1028     SIZE_T TotalSize, CurrentSize, RemainingSize;
1029     volatile BOOLEAN FailedInProbe = FALSE, HavePoolAddress = FALSE;
1030     PVOID CurrentAddress = SourceAddress, CurrentTargetAddress = TargetAddress;
1031     PVOID PoolAddress;
1032     KAPC_STATE ApcState;
1033     BOOLEAN HaveBadAddress;
1034     ULONG_PTR BadAddress;
1035     NTSTATUS Status = STATUS_SUCCESS;
1036     PAGED_CODE();
1037 
1038     DPRINT("Copying %Iu bytes from process %p (address %p) to process %p (Address %p)\n",
1039         BufferSize, SourceProcess, SourceAddress, TargetProcess, TargetAddress);
1040 
1041     //
1042     // Calculate the maximum amount of data to move
1043     //
1044     TotalSize = MI_MAX_TRANSFER_SIZE;
1045     if (BufferSize <= MI_MAX_TRANSFER_SIZE) TotalSize = BufferSize;
1046     CurrentSize = TotalSize;
1047     RemainingSize = BufferSize;
1048 
1049     //
1050     // Check if we can use the stack
1051     //
1052     if (BufferSize <= MI_POOL_COPY_BYTES)
1053     {
1054         //
1055         // Use it
1056         //
1057         PoolAddress = (PVOID)StackBuffer;
1058     }
1059     else
1060     {
1061         //
1062         // Allocate pool
1063         //
1064         PoolAddress = ExAllocatePoolWithTag(NonPagedPool, TotalSize, 'VmRw');
1065         if (!PoolAddress) ASSERT(FALSE);
1066         HavePoolAddress = TRUE;
1067     }
1068 
1069     //
1070     // Loop as long as there is still data
1071     //
1072     while (RemainingSize > 0)
1073     {
1074         //
1075         // Check if this transfer will finish everything off
1076         //
1077         if (RemainingSize < CurrentSize) CurrentSize = RemainingSize;
1078 
1079         //
1080         // Attach to the source address space
1081         //
1082         KeStackAttachProcess(&SourceProcess->Pcb, &ApcState);
1083 
1084         /* Check that state is sane */
1085         ASSERT(FailedInProbe == FALSE);
1086         ASSERT(Status == STATUS_SUCCESS);
1087 
1088         //
1089         // Protect user-mode copy
1090         //
1091         _SEH2_TRY
1092         {
1093             //
1094             // If this is our first time, probe the buffer
1095             //
1096             if ((CurrentAddress == SourceAddress) && (PreviousMode != KernelMode))
1097             {
1098                 //
1099                 // Catch a failure here
1100                 //
1101                 FailedInProbe = TRUE;
1102 
1103                 //
1104                 // Do the probe
1105                 //
1106                 ProbeForRead(SourceAddress, BufferSize, sizeof(CHAR));
1107 
1108                 //
1109                 // Passed
1110                 //
1111                 FailedInProbe = FALSE;
1112             }
1113 
1114             //
1115             // Do the copy
1116             //
1117             RtlCopyMemory(PoolAddress, CurrentAddress, CurrentSize);
1118         }
1119         _SEH2_EXCEPT(MiGetExceptionInfo(_SEH2_GetExceptionInformation(),
1120                                         &HaveBadAddress,
1121                                         &BadAddress))
1122         {
1123             *ReturnSize = BufferSize - RemainingSize;
1124 
1125             //
1126             // Check if we failed during the probe
1127             //
1128             if (FailedInProbe)
1129             {
1130                 //
1131                 // Exit
1132                 //
1133                 Status = _SEH2_GetExceptionCode();
1134             }
1135             else
1136             {
1137                 //
1138                 // We failed during the move.
1139                 // Check if we know exactly where we stopped copying
1140                 //
1141                 if (HaveBadAddress)
1142                 {
1143                     //
1144                     // Return the exact number of bytes copied
1145                     //
1146                     *ReturnSize = BadAddress - (ULONG_PTR)SourceAddress;
1147                 }
1148                 //
1149                 // Return partial copy
1150                 //
1151                 Status = STATUS_PARTIAL_COPY;
1152             }
1153         }
1154         _SEH2_END
1155 
1156         /* Let go of the source */
1157         KeUnstackDetachProcess(&ApcState);
1158 
1159         if (Status != STATUS_SUCCESS)
1160         {
1161             goto Exit;
1162         }
1163 
1164         /* Grab the target process */
1165         KeStackAttachProcess(&TargetProcess->Pcb, &ApcState);
1166 
1167         _SEH2_TRY
1168         {
1169             //
1170             // Check if this is our first time through
1171             //
1172             if ((CurrentTargetAddress == TargetAddress) && (PreviousMode != KernelMode))
1173             {
1174                 //
1175                 // Catch a failure here
1176                 //
1177                 FailedInProbe = TRUE;
1178 
1179                 //
1180                 // Do the probe
1181                 //
1182                 ProbeForWrite(TargetAddress, BufferSize, sizeof(CHAR));
1183 
1184                 //
1185                 // Passed
1186                 //
1187                 FailedInProbe = FALSE;
1188             }
1189 
1190             //
1191             // Now do the actual move
1192             //
1193             RtlCopyMemory(CurrentTargetAddress, PoolAddress, CurrentSize);
1194         }
1195         _SEH2_EXCEPT(MiGetExceptionInfo(_SEH2_GetExceptionInformation(),
1196                                         &HaveBadAddress,
1197                                         &BadAddress))
1198         {
1199             *ReturnSize = BufferSize - RemainingSize;
1200             //
1201             // Check if we failed during the probe
1202             //
1203             if (FailedInProbe)
1204             {
1205                 //
1206                 // Exit
1207                 //
1208                 Status = _SEH2_GetExceptionCode();
1209             }
1210             else
1211             {
1212                 //
1213                 // Otherwise we failed during the move.
1214                 // Check if we know exactly where we stopped copying
1215                 //
1216                 if (HaveBadAddress)
1217                 {
1218                     //
1219                     // Return the exact number of bytes copied
1220                     //
1221                     *ReturnSize = BadAddress - (ULONG_PTR)SourceAddress;
1222                 }
1223                 //
1224                 // Return partial copy
1225                 //
1226                 Status = STATUS_PARTIAL_COPY;
1227             }
1228         }
1229         _SEH2_END;
1230 
1231         //
1232         // Detach from target
1233         //
1234         KeUnstackDetachProcess(&ApcState);
1235 
1236         //
1237         // Check for SEH status
1238         //
1239         if (Status != STATUS_SUCCESS)
1240         {
1241             goto Exit;
1242         }
1243 
1244         //
1245         // Update location and size
1246         //
1247         RemainingSize -= CurrentSize;
1248         CurrentAddress = (PVOID)((ULONG_PTR)CurrentAddress + CurrentSize);
1249         CurrentTargetAddress = (PVOID)((ULONG_PTR)CurrentTargetAddress +
1250                                        CurrentSize);
1251     }
1252 
1253 Exit:
1254     //
1255     // Check if we had allocated pool
1256     //
1257     if (HavePoolAddress)
1258         ExFreePoolWithTag(PoolAddress, 'VmRw');
1259 
1260     //
1261     // All bytes read
1262     //
1263     if (Status == STATUS_SUCCESS)
1264         *ReturnSize = BufferSize;
1265     return Status;
1266 }
1267 
1268 NTSTATUS
1269 NTAPI
MmCopyVirtualMemory(IN PEPROCESS SourceProcess,IN PVOID SourceAddress,IN PEPROCESS TargetProcess,OUT PVOID TargetAddress,IN SIZE_T BufferSize,IN KPROCESSOR_MODE PreviousMode,OUT PSIZE_T ReturnSize)1270 MmCopyVirtualMemory(IN PEPROCESS SourceProcess,
1271                     IN PVOID SourceAddress,
1272                     IN PEPROCESS TargetProcess,
1273                     OUT PVOID TargetAddress,
1274                     IN SIZE_T BufferSize,
1275                     IN KPROCESSOR_MODE PreviousMode,
1276                     OUT PSIZE_T ReturnSize)
1277 {
1278     NTSTATUS Status;
1279     PEPROCESS Process = SourceProcess;
1280 
1281     //
1282     // Don't accept zero-sized buffers
1283     //
1284     if (!BufferSize) return STATUS_SUCCESS;
1285 
1286     //
1287     // If we are copying from ourselves, lock the target instead
1288     //
1289     if (SourceProcess == PsGetCurrentProcess()) Process = TargetProcess;
1290 
1291     //
1292     // Acquire rundown protection
1293     //
1294     if (!ExAcquireRundownProtection(&Process->RundownProtect))
1295     {
1296         //
1297         // Fail
1298         //
1299         return STATUS_PROCESS_IS_TERMINATING;
1300     }
1301 
1302     //
1303     // See if we should use the pool copy
1304     //
1305     if (BufferSize > MI_POOL_COPY_BYTES)
1306     {
1307         //
1308         // Use MDL-copy
1309         //
1310         Status = MiDoMappedCopy(SourceProcess,
1311                                 SourceAddress,
1312                                 TargetProcess,
1313                                 TargetAddress,
1314                                 BufferSize,
1315                                 PreviousMode,
1316                                 ReturnSize);
1317     }
1318     else
1319     {
1320         //
1321         // Do pool copy
1322         //
1323         Status = MiDoPoolCopy(SourceProcess,
1324                               SourceAddress,
1325                               TargetProcess,
1326                               TargetAddress,
1327                               BufferSize,
1328                               PreviousMode,
1329                               ReturnSize);
1330     }
1331 
1332     //
1333     // Release the lock
1334     //
1335     ExReleaseRundownProtection(&Process->RundownProtect);
1336     return Status;
1337 }
1338 
1339 NTSTATUS
1340 NTAPI
MmFlushVirtualMemory(IN PEPROCESS Process,IN OUT PVOID * BaseAddress,IN OUT PSIZE_T RegionSize,OUT PIO_STATUS_BLOCK IoStatusBlock)1341 MmFlushVirtualMemory(IN PEPROCESS Process,
1342                      IN OUT PVOID *BaseAddress,
1343                      IN OUT PSIZE_T RegionSize,
1344                      OUT PIO_STATUS_BLOCK IoStatusBlock)
1345 {
1346     PAGED_CODE();
1347 
1348     UNIMPLEMENTED;
1349 
1350     return STATUS_NOT_IMPLEMENTED;
1351 }
1352 
1353 ULONG
1354 NTAPI
MiGetPageProtection(IN PMMPTE PointerPte)1355 MiGetPageProtection(IN PMMPTE PointerPte)
1356 {
1357     MMPTE TempPte;
1358     PMMPFN Pfn;
1359     PEPROCESS CurrentProcess;
1360     PETHREAD CurrentThread;
1361     BOOLEAN WsSafe, WsShared;
1362     ULONG Protect;
1363     KIRQL OldIrql;
1364     PAGED_CODE();
1365 
1366     /* Copy this PTE's contents */
1367     TempPte = *PointerPte;
1368 
1369     /* Assure it's not totally zero */
1370     ASSERT(TempPte.u.Long);
1371 
1372     /* Check for a special prototype format */
1373     if ((TempPte.u.Soft.Valid == 0) &&
1374         (TempPte.u.Soft.Prototype == 1))
1375     {
1376         /* Check if the prototype PTE is not yet pointing to a PTE */
1377         if (TempPte.u.Soft.PageFileHigh == MI_PTE_LOOKUP_NEEDED)
1378         {
1379             /* The prototype PTE contains the protection */
1380             return MmProtectToValue[TempPte.u.Soft.Protection];
1381         }
1382 
1383         /* Get a pointer to the underlying shared PTE */
1384         PointerPte = MiProtoPteToPte(&TempPte);
1385 
1386         /* Since the PTE we want to read can be paged out at any time, we need
1387            to release the working set lock first, so that it can be paged in */
1388         CurrentThread = PsGetCurrentThread();
1389         CurrentProcess = PsGetCurrentProcess();
1390         MiUnlockProcessWorkingSetForFault(CurrentProcess,
1391                                           CurrentThread,
1392                                           &WsSafe,
1393                                           &WsShared);
1394 
1395         /* Now read the PTE value */
1396         TempPte = *PointerPte;
1397 
1398         /* Check if that one is invalid */
1399         if (!TempPte.u.Hard.Valid)
1400         {
1401             /* We get the protection directly from this PTE */
1402             Protect = MmProtectToValue[TempPte.u.Soft.Protection];
1403         }
1404         else
1405         {
1406             /* The PTE is valid, so we might need to get the protection from
1407                the PFN. Lock the PFN database */
1408             OldIrql = MiAcquirePfnLock();
1409 
1410             /* Check if the PDE is still valid */
1411             if (MiAddressToPte(PointerPte)->u.Hard.Valid == 0)
1412             {
1413                 /* It's not, make it valid */
1414                 MiMakeSystemAddressValidPfn(PointerPte, OldIrql);
1415             }
1416 
1417             /* Now it's safe to read the PTE value again */
1418             TempPte = *PointerPte;
1419             ASSERT(TempPte.u.Long != 0);
1420 
1421             /* Check again if the PTE is invalid */
1422             if (!TempPte.u.Hard.Valid)
1423             {
1424                 /* The PTE is not valid, so we can use it's protection field */
1425                 Protect = MmProtectToValue[TempPte.u.Soft.Protection];
1426             }
1427             else
1428             {
1429                 /* The PTE is valid, so we can find the protection in the
1430                    OriginalPte field of the PFN */
1431                 Pfn = MI_PFN_ELEMENT(TempPte.u.Hard.PageFrameNumber);
1432                 Protect = MmProtectToValue[Pfn->OriginalPte.u.Soft.Protection];
1433             }
1434 
1435             /* Release the PFN database */
1436             MiReleasePfnLock(OldIrql);
1437         }
1438 
1439         /* Lock the working set again */
1440         MiLockProcessWorkingSetForFault(CurrentProcess,
1441                                         CurrentThread,
1442                                         WsSafe,
1443                                         WsShared);
1444 
1445         return Protect;
1446     }
1447 
1448     /* In the easy case of transition or demand zero PTE just return its protection */
1449     if (!TempPte.u.Hard.Valid) return MmProtectToValue[TempPte.u.Soft.Protection];
1450 
1451     /* If we get here, the PTE is valid, so look up the page in PFN database */
1452     Pfn = MiGetPfnEntry(TempPte.u.Hard.PageFrameNumber);
1453     if (!Pfn->u3.e1.PrototypePte)
1454     {
1455         /* Return protection of the original pte */
1456         ASSERT(Pfn->u4.AweAllocation == 0);
1457         return MmProtectToValue[Pfn->OriginalPte.u.Soft.Protection];
1458     }
1459 
1460     /* This is software PTE */
1461     DPRINT("Prototype PTE: %lx %p\n", TempPte.u.Hard.PageFrameNumber, Pfn);
1462     DPRINT("VA: %p\n", MiPteToAddress(&TempPte));
1463     DPRINT("Mask: %lx\n", TempPte.u.Soft.Protection);
1464     DPRINT("Mask2: %lx\n", Pfn->OriginalPte.u.Soft.Protection);
1465     return MmProtectToValue[TempPte.u.Soft.Protection];
1466 }
1467 
1468 ULONG
1469 NTAPI
MiQueryAddressState(IN PVOID Va,IN PMMVAD Vad,IN PEPROCESS TargetProcess,OUT PULONG ReturnedProtect,OUT PVOID * NextVa)1470 MiQueryAddressState(IN PVOID Va,
1471                     IN PMMVAD Vad,
1472                     IN PEPROCESS TargetProcess,
1473                     OUT PULONG ReturnedProtect,
1474                     OUT PVOID *NextVa)
1475 {
1476 
1477     PMMPTE PointerPte, ProtoPte;
1478     PMMPDE PointerPde;
1479 #if (_MI_PAGING_LEVELS >= 3)
1480     PMMPPE PointerPpe;
1481 #endif
1482 #if (_MI_PAGING_LEVELS >= 4)
1483     PMMPXE PointerPxe;
1484 #endif
1485     MMPTE TempPte, TempProtoPte;
1486     BOOLEAN DemandZeroPte = TRUE, ValidPte = FALSE;
1487     ULONG State = MEM_RESERVE, Protect = 0;
1488     ASSERT((Vad->StartingVpn <= ((ULONG_PTR)Va >> PAGE_SHIFT)) &&
1489            (Vad->EndingVpn >= ((ULONG_PTR)Va >> PAGE_SHIFT)));
1490 
1491     /* Only normal VADs supported */
1492     ASSERT(Vad->u.VadFlags.VadType == VadNone);
1493 
1494     /* Get the PDE and PTE for the address */
1495     PointerPde = MiAddressToPde(Va);
1496     PointerPte = MiAddressToPte(Va);
1497 #if (_MI_PAGING_LEVELS >= 3)
1498     PointerPpe = MiAddressToPpe(Va);
1499 #endif
1500 #if (_MI_PAGING_LEVELS >= 4)
1501     PointerPxe = MiAddressToPxe(Va);
1502 #endif
1503 
1504     /* Return the next range */
1505     *NextVa = (PVOID)((ULONG_PTR)Va + PAGE_SIZE);
1506 
1507     do
1508     {
1509 #if (_MI_PAGING_LEVELS >= 4)
1510         /* Does the PXE exist? */
1511         if (PointerPxe->u.Long == 0)
1512         {
1513             /* It does not, next range starts at the next PXE */
1514             *NextVa = MiPxeToAddress(PointerPxe + 1);
1515             break;
1516         }
1517 
1518         /* Is the PXE valid? */
1519         if (PointerPxe->u.Hard.Valid == 0)
1520         {
1521             /* Is isn't, fault it in (make the PPE accessible) */
1522             MiMakeSystemAddressValid(PointerPpe, TargetProcess);
1523         }
1524 #endif
1525 #if (_MI_PAGING_LEVELS >= 3)
1526         /* Does the PPE exist? */
1527         if (PointerPpe->u.Long == 0)
1528         {
1529             /* It does not, next range starts at the next PPE */
1530             *NextVa = MiPpeToAddress(PointerPpe + 1);
1531             break;
1532         }
1533 
1534         /* Is the PPE valid? */
1535         if (PointerPpe->u.Hard.Valid == 0)
1536         {
1537             /* Is isn't, fault it in (make the PDE accessible) */
1538             MiMakeSystemAddressValid(PointerPde, TargetProcess);
1539         }
1540 #endif
1541 
1542         /* Does the PDE exist? */
1543         if (PointerPde->u.Long == 0)
1544         {
1545             /* It does not, next range starts at the next PDE */
1546             *NextVa = MiPdeToAddress(PointerPde + 1);
1547             break;
1548         }
1549 
1550         /* Is the PDE valid? */
1551         if (PointerPde->u.Hard.Valid == 0)
1552         {
1553             /* Is isn't, fault it in (make the PTE accessible) */
1554             MiMakeSystemAddressValid(PointerPte, TargetProcess);
1555         }
1556 
1557         /* We have a PTE that we can access now! */
1558         ValidPte = TRUE;
1559 
1560     } while (FALSE);
1561 
1562     /* Is it safe to try reading the PTE? */
1563     if (ValidPte)
1564     {
1565         /* FIXME: watch out for large pages */
1566         ASSERT(PointerPde->u.Hard.LargePage == FALSE);
1567 
1568         /* Capture the PTE */
1569         TempPte = *PointerPte;
1570         if (TempPte.u.Long != 0)
1571         {
1572             /* The PTE is valid, so it's not zeroed out */
1573             DemandZeroPte = FALSE;
1574 
1575             /* Is it a decommited, invalid, or faulted PTE? */
1576             if ((TempPte.u.Soft.Protection == MM_DECOMMIT) &&
1577                 (TempPte.u.Hard.Valid == 0) &&
1578                 ((TempPte.u.Soft.Prototype == 0) ||
1579                  (TempPte.u.Soft.PageFileHigh == MI_PTE_LOOKUP_NEEDED)))
1580             {
1581                 /* Otherwise our defaults should hold */
1582                 ASSERT(Protect == 0);
1583                 ASSERT(State == MEM_RESERVE);
1584             }
1585             else
1586             {
1587                 /* This means it's committed */
1588                 State = MEM_COMMIT;
1589 
1590                 /* We don't support these */
1591                 ASSERT(Vad->u.VadFlags.VadType != VadDevicePhysicalMemory);
1592                 ASSERT(Vad->u.VadFlags.VadType != VadRotatePhysical);
1593                 ASSERT(Vad->u.VadFlags.VadType != VadAwe);
1594 
1595                 /* Get protection state of this page */
1596                 Protect = MiGetPageProtection(PointerPte);
1597 
1598                 /* Check if this is an image-backed VAD */
1599                 if ((TempPte.u.Soft.Valid == 0) &&
1600                     (TempPte.u.Soft.Prototype == 1) &&
1601                     (Vad->u.VadFlags.PrivateMemory == 0) &&
1602                     (Vad->ControlArea))
1603                 {
1604                     DPRINT1("Not supported\n");
1605                     ASSERT(FALSE);
1606                 }
1607             }
1608         }
1609     }
1610 
1611     /* Check if this was a demand-zero PTE, since we need to find the state */
1612     if (DemandZeroPte)
1613     {
1614         /* Not yet handled */
1615         ASSERT(Vad->u.VadFlags.VadType != VadDevicePhysicalMemory);
1616         ASSERT(Vad->u.VadFlags.VadType != VadAwe);
1617 
1618         /* Check if this is private commited memory, or an section-backed VAD */
1619         if ((Vad->u.VadFlags.PrivateMemory == 0) && (Vad->ControlArea))
1620         {
1621             /* Tell caller about the next range */
1622             *NextVa = (PVOID)((ULONG_PTR)Va + PAGE_SIZE);
1623 
1624             /* Get the prototype PTE for this VAD */
1625             ProtoPte = MI_GET_PROTOTYPE_PTE_FOR_VPN(Vad,
1626                                                     (ULONG_PTR)Va >> PAGE_SHIFT);
1627             if (ProtoPte)
1628             {
1629                 /* We should unlock the working set, but it's not being held! */
1630 
1631                 /* Is the prototype PTE actually valid (committed)? */
1632                 TempProtoPte = *ProtoPte;
1633                 if (TempProtoPte.u.Long)
1634                 {
1635                     /* Unless this is a memory-mapped file, handle it like private VAD */
1636                     State = MEM_COMMIT;
1637                     ASSERT(Vad->u.VadFlags.VadType != VadImageMap);
1638                     Protect = MmProtectToValue[Vad->u.VadFlags.Protection];
1639                 }
1640 
1641                 /* We should re-lock the working set */
1642             }
1643         }
1644         else if (Vad->u.VadFlags.MemCommit)
1645         {
1646             /* This is committed memory */
1647             State = MEM_COMMIT;
1648 
1649             /* Convert the protection */
1650             Protect = MmProtectToValue[Vad->u.VadFlags.Protection];
1651         }
1652     }
1653 
1654     /* Return the protection code */
1655     *ReturnedProtect = Protect;
1656     return State;
1657 }
1658 
1659 NTSTATUS
1660 NTAPI
MiQueryMemoryBasicInformation(IN HANDLE ProcessHandle,IN PVOID BaseAddress,OUT PVOID MemoryInformation,IN SIZE_T MemoryInformationLength,OUT PSIZE_T ReturnLength)1661 MiQueryMemoryBasicInformation(IN HANDLE ProcessHandle,
1662                               IN PVOID BaseAddress,
1663                               OUT PVOID MemoryInformation,
1664                               IN SIZE_T MemoryInformationLength,
1665                               OUT PSIZE_T ReturnLength)
1666 {
1667     PEPROCESS TargetProcess;
1668     NTSTATUS Status = STATUS_SUCCESS;
1669     PMMVAD Vad = NULL;
1670     PVOID Address, NextAddress;
1671     BOOLEAN Found = FALSE;
1672     ULONG NewProtect, NewState;
1673     ULONG_PTR BaseVpn;
1674     MEMORY_BASIC_INFORMATION MemoryInfo;
1675     KAPC_STATE ApcState;
1676     KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
1677     PMEMORY_AREA MemoryArea;
1678     SIZE_T ResultLength;
1679 
1680     /* Check for illegal addresses in user-space, or the shared memory area */
1681     if ((BaseAddress > MM_HIGHEST_VAD_ADDRESS) ||
1682         (PAGE_ALIGN(BaseAddress) == (PVOID)MM_SHARED_USER_DATA_VA))
1683     {
1684         Address = PAGE_ALIGN(BaseAddress);
1685 
1686         /* Make up an info structure describing this range */
1687         MemoryInfo.BaseAddress = Address;
1688         MemoryInfo.AllocationProtect = PAGE_READONLY;
1689         MemoryInfo.Type = MEM_PRIVATE;
1690 
1691         /* Special case for shared data */
1692         if (Address == (PVOID)MM_SHARED_USER_DATA_VA)
1693         {
1694             MemoryInfo.AllocationBase = (PVOID)MM_SHARED_USER_DATA_VA;
1695             MemoryInfo.State = MEM_COMMIT;
1696             MemoryInfo.Protect = PAGE_READONLY;
1697             MemoryInfo.RegionSize = PAGE_SIZE;
1698         }
1699         else
1700         {
1701             MemoryInfo.AllocationBase = (PCHAR)MM_HIGHEST_VAD_ADDRESS + 1;
1702             MemoryInfo.State = MEM_RESERVE;
1703             MemoryInfo.Protect = PAGE_NOACCESS;
1704             MemoryInfo.RegionSize = (ULONG_PTR)MM_HIGHEST_USER_ADDRESS + 1 - (ULONG_PTR)Address;
1705         }
1706 
1707         /* Return the data, NtQueryInformation already probed it*/
1708         if (PreviousMode != KernelMode)
1709         {
1710             _SEH2_TRY
1711             {
1712                 *(PMEMORY_BASIC_INFORMATION)MemoryInformation = MemoryInfo;
1713                 if (ReturnLength) *ReturnLength = sizeof(MEMORY_BASIC_INFORMATION);
1714             }
1715              _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1716             {
1717                 Status = _SEH2_GetExceptionCode();
1718             }
1719             _SEH2_END;
1720         }
1721         else
1722         {
1723             *(PMEMORY_BASIC_INFORMATION)MemoryInformation = MemoryInfo;
1724             if (ReturnLength) *ReturnLength = sizeof(MEMORY_BASIC_INFORMATION);
1725         }
1726 
1727         return Status;
1728     }
1729 
1730     /* Check if this is for a local or remote process */
1731     if (ProcessHandle == NtCurrentProcess())
1732     {
1733         TargetProcess = PsGetCurrentProcess();
1734     }
1735     else
1736     {
1737         /* Reference the target process */
1738         Status = ObReferenceObjectByHandle(ProcessHandle,
1739                                            PROCESS_QUERY_INFORMATION,
1740                                            PsProcessType,
1741                                            ExGetPreviousMode(),
1742                                            (PVOID*)&TargetProcess,
1743                                            NULL);
1744         if (!NT_SUCCESS(Status)) return Status;
1745 
1746         /* Attach to it now */
1747         KeStackAttachProcess(&TargetProcess->Pcb, &ApcState);
1748     }
1749 
1750     /* Lock the address space and make sure the process isn't already dead */
1751     MmLockAddressSpace(&TargetProcess->Vm);
1752     if (TargetProcess->VmDeleted)
1753     {
1754         /* Unlock the address space of the process */
1755         MmUnlockAddressSpace(&TargetProcess->Vm);
1756 
1757         /* Check if we were attached */
1758         if (ProcessHandle != NtCurrentProcess())
1759         {
1760             /* Detach and dereference the process */
1761             KeUnstackDetachProcess(&ApcState);
1762             ObDereferenceObject(TargetProcess);
1763         }
1764 
1765         /* Bail out */
1766         DPRINT1("Process is dying\n");
1767         return STATUS_PROCESS_IS_TERMINATING;
1768     }
1769 
1770     /* Loop the VADs */
1771     ASSERT(TargetProcess->VadRoot.NumberGenericTableElements);
1772     if (TargetProcess->VadRoot.NumberGenericTableElements)
1773     {
1774         /* Scan on the right */
1775         Vad = (PMMVAD)TargetProcess->VadRoot.BalancedRoot.RightChild;
1776         BaseVpn = (ULONG_PTR)BaseAddress >> PAGE_SHIFT;
1777         while (Vad)
1778         {
1779             /* Check if this VAD covers the allocation range */
1780             if ((BaseVpn >= Vad->StartingVpn) &&
1781                 (BaseVpn <= Vad->EndingVpn))
1782             {
1783                 /* We're done */
1784                 Found = TRUE;
1785                 break;
1786             }
1787 
1788             /* Check if this VAD is too high */
1789             if (BaseVpn < Vad->StartingVpn)
1790             {
1791                 /* Stop if there is no left child */
1792                 if (!Vad->LeftChild) break;
1793 
1794                 /* Search on the left next */
1795                 Vad = Vad->LeftChild;
1796             }
1797             else
1798             {
1799                 /* Then this VAD is too low, keep searching on the right */
1800                 ASSERT(BaseVpn > Vad->EndingVpn);
1801 
1802                 /* Stop if there is no right child */
1803                 if (!Vad->RightChild) break;
1804 
1805                 /* Search on the right next */
1806                 Vad = Vad->RightChild;
1807             }
1808         }
1809     }
1810 
1811     /* Was a VAD found? */
1812     if (!Found)
1813     {
1814         Address = PAGE_ALIGN(BaseAddress);
1815 
1816         /* Calculate region size */
1817         if (Vad)
1818         {
1819             if (Vad->StartingVpn >= BaseVpn)
1820             {
1821                 /* Region size is the free space till the start of that VAD */
1822                 MemoryInfo.RegionSize = (ULONG_PTR)(Vad->StartingVpn << PAGE_SHIFT) - (ULONG_PTR)Address;
1823             }
1824             else
1825             {
1826                 /* Get the next VAD */
1827                 Vad = (PMMVAD)MiGetNextNode((PMMADDRESS_NODE)Vad);
1828                 if (Vad)
1829                 {
1830                     /* Region size is the free space till the start of that VAD */
1831                     MemoryInfo.RegionSize = (ULONG_PTR)(Vad->StartingVpn << PAGE_SHIFT) - (ULONG_PTR)Address;
1832                 }
1833                 else
1834                 {
1835                     /* Maximum possible region size with that base address */
1836                     MemoryInfo.RegionSize = (PCHAR)MM_HIGHEST_VAD_ADDRESS + 1 - (PCHAR)Address;
1837                 }
1838             }
1839         }
1840         else
1841         {
1842             /* Maximum possible region size with that base address */
1843             MemoryInfo.RegionSize = (PCHAR)MM_HIGHEST_VAD_ADDRESS + 1 - (PCHAR)Address;
1844         }
1845 
1846         /* Unlock the address space of the process */
1847         MmUnlockAddressSpace(&TargetProcess->Vm);
1848 
1849         /* Check if we were attached */
1850         if (ProcessHandle != NtCurrentProcess())
1851         {
1852             /* Detach and dereference the process */
1853             KeUnstackDetachProcess(&ApcState);
1854             ObDereferenceObject(TargetProcess);
1855         }
1856 
1857         /* Build the rest of the initial information block */
1858         MemoryInfo.BaseAddress = Address;
1859         MemoryInfo.AllocationBase = NULL;
1860         MemoryInfo.AllocationProtect = 0;
1861         MemoryInfo.State = MEM_FREE;
1862         MemoryInfo.Protect = PAGE_NOACCESS;
1863         MemoryInfo.Type = 0;
1864 
1865         /* Return the data, NtQueryInformation already probed it*/
1866         if (PreviousMode != KernelMode)
1867         {
1868             _SEH2_TRY
1869             {
1870                 *(PMEMORY_BASIC_INFORMATION)MemoryInformation = MemoryInfo;
1871                 if (ReturnLength) *ReturnLength = sizeof(MEMORY_BASIC_INFORMATION);
1872             }
1873              _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1874             {
1875                 Status = _SEH2_GetExceptionCode();
1876             }
1877             _SEH2_END;
1878         }
1879         else
1880         {
1881             *(PMEMORY_BASIC_INFORMATION)MemoryInformation = MemoryInfo;
1882             if (ReturnLength) *ReturnLength = sizeof(MEMORY_BASIC_INFORMATION);
1883         }
1884 
1885         return Status;
1886     }
1887 
1888     /* Set the correct memory type based on what kind of VAD this is */
1889     if ((Vad->u.VadFlags.PrivateMemory) ||
1890         (Vad->u.VadFlags.VadType == VadRotatePhysical))
1891     {
1892         MemoryInfo.Type = MEM_PRIVATE;
1893     }
1894     else if (Vad->u.VadFlags.VadType == VadImageMap)
1895     {
1896         MemoryInfo.Type = MEM_IMAGE;
1897     }
1898     else
1899     {
1900         MemoryInfo.Type = MEM_MAPPED;
1901     }
1902 
1903     /* Find the memory area the specified address belongs to */
1904     MemoryArea = MmLocateMemoryAreaByAddress(&TargetProcess->Vm, BaseAddress);
1905     ASSERT(MemoryArea != NULL);
1906 
1907     /* Determine information dependent on the memory area type */
1908     if (MemoryArea->Type == MEMORY_AREA_SECTION_VIEW)
1909     {
1910         Status = MmQuerySectionView(MemoryArea, BaseAddress, &MemoryInfo, &ResultLength);
1911         if (!NT_SUCCESS(Status))
1912         {
1913             DPRINT1("MmQuerySectionView failed. MemoryArea=%p (%p-%p), BaseAddress=%p\n",
1914                     MemoryArea, MA_GetStartingAddress(MemoryArea), MA_GetEndingAddress(MemoryArea), BaseAddress);
1915             ASSERT(NT_SUCCESS(Status));
1916         }
1917     }
1918     else
1919     {
1920         /* Build the initial information block */
1921         Address = PAGE_ALIGN(BaseAddress);
1922         MemoryInfo.BaseAddress = Address;
1923         MemoryInfo.AllocationBase = (PVOID)(Vad->StartingVpn << PAGE_SHIFT);
1924         MemoryInfo.AllocationProtect = MmProtectToValue[Vad->u.VadFlags.Protection];
1925         MemoryInfo.Type = MEM_PRIVATE;
1926 
1927         /* Acquire the working set lock (shared is enough) */
1928         MiLockProcessWorkingSetShared(TargetProcess, PsGetCurrentThread());
1929 
1930         /* Find the largest chunk of memory which has the same state and protection mask */
1931         MemoryInfo.State = MiQueryAddressState(Address,
1932                                                Vad,
1933                                                TargetProcess,
1934                                                &MemoryInfo.Protect,
1935                                                &NextAddress);
1936         Address = NextAddress;
1937         while (((ULONG_PTR)Address >> PAGE_SHIFT) <= Vad->EndingVpn)
1938         {
1939             /* Keep going unless the state or protection mask changed */
1940             NewState = MiQueryAddressState(Address, Vad, TargetProcess, &NewProtect, &NextAddress);
1941             if ((NewState != MemoryInfo.State) || (NewProtect != MemoryInfo.Protect)) break;
1942             Address = NextAddress;
1943         }
1944 
1945         /* Release the working set lock */
1946         MiUnlockProcessWorkingSetShared(TargetProcess, PsGetCurrentThread());
1947 
1948         /* Check if we went outside of the VAD */
1949          if (((ULONG_PTR)Address >> PAGE_SHIFT) > Vad->EndingVpn)
1950          {
1951             /* Set the end of the VAD as the end address */
1952             Address = (PVOID)((Vad->EndingVpn + 1) << PAGE_SHIFT);
1953          }
1954 
1955         /* Now that we know the last VA address, calculate the region size */
1956         MemoryInfo.RegionSize = ((ULONG_PTR)Address - (ULONG_PTR)MemoryInfo.BaseAddress);
1957     }
1958 
1959     /* Unlock the address space of the process */
1960     MmUnlockAddressSpace(&TargetProcess->Vm);
1961 
1962     /* Check if we were attached */
1963     if (ProcessHandle != NtCurrentProcess())
1964     {
1965         /* Detach and dereference the process */
1966         KeUnstackDetachProcess(&ApcState);
1967         ObDereferenceObject(TargetProcess);
1968     }
1969 
1970     /* Return the data, NtQueryInformation already probed it */
1971     if (PreviousMode != KernelMode)
1972     {
1973         _SEH2_TRY
1974         {
1975             *(PMEMORY_BASIC_INFORMATION)MemoryInformation = MemoryInfo;
1976             if (ReturnLength) *ReturnLength = sizeof(MEMORY_BASIC_INFORMATION);
1977         }
1978          _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1979         {
1980             Status = _SEH2_GetExceptionCode();
1981         }
1982         _SEH2_END;
1983     }
1984     else
1985     {
1986         *(PMEMORY_BASIC_INFORMATION)MemoryInformation = MemoryInfo;
1987         if (ReturnLength) *ReturnLength = sizeof(MEMORY_BASIC_INFORMATION);
1988     }
1989 
1990     /* All went well */
1991     DPRINT("Base: %p AllocBase: %p AllocProtect: %lx Protect: %lx "
1992             "State: %lx Type: %lx Size: %lx\n",
1993             MemoryInfo.BaseAddress, MemoryInfo.AllocationBase,
1994             MemoryInfo.AllocationProtect, MemoryInfo.Protect,
1995             MemoryInfo.State, MemoryInfo.Type, MemoryInfo.RegionSize);
1996 
1997     return Status;
1998 }
1999 
2000 BOOLEAN
2001 NTAPI
MiIsEntireRangeCommitted(IN ULONG_PTR StartingAddress,IN ULONG_PTR EndingAddress,IN PMMVAD Vad,IN PEPROCESS Process)2002 MiIsEntireRangeCommitted(IN ULONG_PTR StartingAddress,
2003                          IN ULONG_PTR EndingAddress,
2004                          IN PMMVAD Vad,
2005                          IN PEPROCESS Process)
2006 {
2007     PMMPTE PointerPte, LastPte;
2008     PMMPDE PointerPde;
2009     BOOLEAN OnPdeBoundary = TRUE;
2010 #if _MI_PAGING_LEVELS >= 3
2011     PMMPPE PointerPpe;
2012     BOOLEAN OnPpeBoundary = TRUE;
2013 #if _MI_PAGING_LEVELS == 4
2014     PMMPXE PointerPxe;
2015     BOOLEAN OnPxeBoundary = TRUE;
2016 #endif
2017 #endif
2018 
2019     PAGED_CODE();
2020 
2021     /* Check that we hols the right locks */
2022     ASSERT(PsGetCurrentThread()->OwnsProcessWorkingSetExclusive || PsGetCurrentThread()->OwnsProcessWorkingSetShared);
2023 
2024     /* Get the PTE addresses */
2025     PointerPte = MiAddressToPte(StartingAddress);
2026     LastPte = MiAddressToPte(EndingAddress);
2027 
2028     /* Loop all the PTEs */
2029     while (PointerPte <= LastPte)
2030     {
2031 #if _MI_PAGING_LEVELS == 4
2032         /* Check for new PXE boundary */
2033         if (OnPxeBoundary)
2034         {
2035             PointerPxe = MiPteToPxe(PointerPte);
2036 
2037             /* Check that this loop is sane */
2038             ASSERT(OnPpeBoundary);
2039             ASSERT(OnPdeBoundary);
2040 
2041             if (PointerPxe->u.Long != 0)
2042             {
2043                 /* Make it valid if needed */
2044                 if (PointerPxe->u.Hard.Valid == 0)
2045                     MiMakeSystemAddressValid(MiPteToPpe(PointerPte), Process);
2046             }
2047             else
2048             {
2049                 /* Is the entire VAD committed? If not, fail */
2050                 if (!Vad->u.VadFlags.MemCommit) return FALSE;
2051 
2052                 PointerPxe++;
2053                 PointerPte = MiPxeToPte(PointerPte);
2054                 continue;
2055             }
2056         }
2057 #endif
2058 
2059 #if _MI_PAGING_LEVELS >= 3
2060         /* Check for new PPE boundary */
2061         if (OnPpeBoundary)
2062         {
2063             PointerPpe = MiPteToPpe(PointerPte);
2064 
2065             /* Check that this loop is sane */
2066             ASSERT(OnPdeBoundary);
2067 
2068             if (PointerPpe->u.Long != 0)
2069             {
2070                 /* Make it valid if needed */
2071                 if (PointerPpe->u.Hard.Valid == 0)
2072                     MiMakeSystemAddressValid(MiPteToPde(PointerPte), Process);
2073             }
2074             else
2075             {
2076                 /* Is the entire VAD committed? If not, fail */
2077                 if (!Vad->u.VadFlags.MemCommit) return FALSE;
2078 
2079                 PointerPpe++;
2080                 PointerPte = MiPpeToPte(PointerPpe);
2081 #if _MI_PAGING_LEVELS == 4
2082                 OnPxeBoundary = MiIsPteOnPxeBoundary(PointerPte);
2083 #endif
2084                 continue;
2085             }
2086         }
2087 #endif
2088         /* Check if we've hit a new PDE boundary */
2089         if (OnPdeBoundary)
2090         {
2091             /* Is this PDE demand zero? */
2092             PointerPde = MiPteToPde(PointerPte);
2093             if (PointerPde->u.Long != 0)
2094             {
2095                 /* It isn't -- is it valid? */
2096                 if (PointerPde->u.Hard.Valid == 0)
2097                 {
2098                     /* Nope, fault it in */
2099                     MiMakeSystemAddressValid(PointerPte, Process);
2100                 }
2101             }
2102             else
2103             {
2104                 /* Is the entire VAD committed? If not, fail */
2105                 if (!Vad->u.VadFlags.MemCommit) return FALSE;
2106 
2107                 /* The PTE was already valid, so move to the next one */
2108                 PointerPde++;
2109                 PointerPte = MiPdeToPte(PointerPde);
2110 #if _MI_PAGING_LEVELS >= 3
2111                 OnPpeBoundary = MiIsPteOnPpeBoundary(PointerPte);
2112 #if _MI_PAGING_LEVELS == 4
2113                 OnPxeBoundary = MiIsPteOnPxeBoundary(PointerPte);
2114 #endif
2115 #endif
2116 
2117                 /* New loop iteration with our new, on-boundary PTE. */
2118                 continue;
2119             }
2120         }
2121 
2122         /* Is the PTE demand zero? */
2123         if (PointerPte->u.Long == 0)
2124         {
2125             /* Is the entire VAD committed? If not, fail */
2126             if (!Vad->u.VadFlags.MemCommit) return FALSE;
2127         }
2128         else
2129         {
2130             /* It isn't -- is it a decommited, invalid, or faulted PTE? */
2131             if ((PointerPte->u.Soft.Protection == MM_DECOMMIT) &&
2132                 (PointerPte->u.Hard.Valid == 0) &&
2133                 ((PointerPte->u.Soft.Prototype == 0) ||
2134                  (PointerPte->u.Soft.PageFileHigh == MI_PTE_LOOKUP_NEEDED)))
2135             {
2136                 /* Then part of the range is decommitted, so fail */
2137                 return FALSE;
2138             }
2139         }
2140 
2141         /* Move to the next PTE */
2142         PointerPte++;
2143         OnPdeBoundary = MiIsPteOnPdeBoundary(PointerPte);
2144 #if _MI_PAGING_LEVELS >= 3
2145         OnPpeBoundary = MiIsPteOnPpeBoundary(PointerPte);
2146 #if _MI_PAGING_LEVELS == 4
2147         OnPxeBoundary = MiIsPteOnPxeBoundary(PointerPte);
2148 #endif
2149 #endif
2150     }
2151 
2152     /* All PTEs seem valid, and no VAD checks failed, the range is okay */
2153     return TRUE;
2154 }
2155 
2156 NTSTATUS
2157 NTAPI
MiRosProtectVirtualMemory(IN PEPROCESS Process,IN OUT PVOID * BaseAddress,IN OUT PSIZE_T NumberOfBytesToProtect,IN ULONG NewAccessProtection,OUT PULONG OldAccessProtection OPTIONAL)2158 MiRosProtectVirtualMemory(IN PEPROCESS Process,
2159                           IN OUT PVOID *BaseAddress,
2160                           IN OUT PSIZE_T NumberOfBytesToProtect,
2161                           IN ULONG NewAccessProtection,
2162                           OUT PULONG OldAccessProtection OPTIONAL)
2163 {
2164     PMEMORY_AREA MemoryArea;
2165     PMMSUPPORT AddressSpace;
2166     ULONG OldAccessProtection_;
2167     NTSTATUS Status;
2168 
2169     *NumberOfBytesToProtect = PAGE_ROUND_UP((ULONG_PTR)(*BaseAddress) + (*NumberOfBytesToProtect)) - PAGE_ROUND_DOWN(*BaseAddress);
2170     *BaseAddress = (PVOID)PAGE_ROUND_DOWN(*BaseAddress);
2171 
2172     AddressSpace = &Process->Vm;
2173     MmLockAddressSpace(AddressSpace);
2174     MemoryArea = MmLocateMemoryAreaByAddress(AddressSpace, *BaseAddress);
2175     if (MemoryArea == NULL || MemoryArea->DeleteInProgress)
2176     {
2177         MmUnlockAddressSpace(AddressSpace);
2178         return STATUS_UNSUCCESSFUL;
2179     }
2180 
2181     if (OldAccessProtection == NULL) OldAccessProtection = &OldAccessProtection_;
2182 
2183     ASSERT(MemoryArea->Type == MEMORY_AREA_SECTION_VIEW);
2184     Status = MmProtectSectionView(AddressSpace,
2185                                   MemoryArea,
2186                                   *BaseAddress,
2187                                   *NumberOfBytesToProtect,
2188                                   NewAccessProtection,
2189                                   OldAccessProtection);
2190 
2191     MmUnlockAddressSpace(AddressSpace);
2192 
2193     return Status;
2194 }
2195 
2196 NTSTATUS
2197 NTAPI
MiProtectVirtualMemory(IN PEPROCESS Process,IN OUT PVOID * BaseAddress,IN OUT PSIZE_T NumberOfBytesToProtect,IN ULONG NewAccessProtection,OUT PULONG OldAccessProtection OPTIONAL)2198 MiProtectVirtualMemory(IN PEPROCESS Process,
2199                        IN OUT PVOID *BaseAddress,
2200                        IN OUT PSIZE_T NumberOfBytesToProtect,
2201                        IN ULONG NewAccessProtection,
2202                        OUT PULONG OldAccessProtection OPTIONAL)
2203 {
2204     PMEMORY_AREA MemoryArea;
2205     PMMVAD Vad;
2206     PMMSUPPORT AddressSpace;
2207     ULONG_PTR StartingAddress, EndingAddress;
2208     PMMPTE PointerPte, LastPte;
2209     PMMPDE PointerPde;
2210     MMPTE PteContents;
2211     PMMPFN Pfn1;
2212     ULONG ProtectionMask, OldProtect;
2213     BOOLEAN Committed;
2214     NTSTATUS Status = STATUS_SUCCESS;
2215     PETHREAD Thread = PsGetCurrentThread();
2216     TABLE_SEARCH_RESULT Result;
2217 
2218     /* We must be attached */
2219     ASSERT(Process == PsGetCurrentProcess());
2220 
2221     /* Calculate base address for the VAD */
2222     StartingAddress = (ULONG_PTR)PAGE_ALIGN((*BaseAddress));
2223     EndingAddress = (((ULONG_PTR)*BaseAddress + *NumberOfBytesToProtect - 1) | (PAGE_SIZE - 1));
2224 
2225     /* Calculate the protection mask and make sure it's valid */
2226     ProtectionMask = MiMakeProtectionMask(NewAccessProtection);
2227     if (ProtectionMask == MM_INVALID_PROTECTION)
2228     {
2229         DPRINT1("Invalid protection mask\n");
2230         return STATUS_INVALID_PAGE_PROTECTION;
2231     }
2232 
2233     /* Lock the address space and make sure the process isn't already dead */
2234     AddressSpace = MmGetCurrentAddressSpace();
2235     MmLockAddressSpace(AddressSpace);
2236     if (Process->VmDeleted)
2237     {
2238         DPRINT1("Process is dying\n");
2239         Status = STATUS_PROCESS_IS_TERMINATING;
2240         goto FailPath;
2241     }
2242 
2243     /* Check for ROS specific memory area */
2244     MemoryArea = MmLocateMemoryAreaByAddress(&Process->Vm, *BaseAddress);
2245     if ((MemoryArea) && (MemoryArea->Type != MEMORY_AREA_OWNED_BY_ARM3))
2246     {
2247         /* Evil hack */
2248         MmUnlockAddressSpace(AddressSpace);
2249         return MiRosProtectVirtualMemory(Process,
2250                                          BaseAddress,
2251                                          NumberOfBytesToProtect,
2252                                          NewAccessProtection,
2253                                          OldAccessProtection);
2254     }
2255 
2256     /* Get the VAD for this address range, and make sure it exists */
2257     Result = MiCheckForConflictingNode(StartingAddress >> PAGE_SHIFT,
2258                                        EndingAddress >> PAGE_SHIFT,
2259                                        &Process->VadRoot,
2260                                        (PMMADDRESS_NODE*)&Vad);
2261     if (Result != TableFoundNode)
2262     {
2263         DPRINT("Could not find a VAD for this allocation\n");
2264         Status = STATUS_CONFLICTING_ADDRESSES;
2265         goto FailPath;
2266     }
2267 
2268     /* Make sure the address is within this VAD's boundaries */
2269     if ((((ULONG_PTR)StartingAddress >> PAGE_SHIFT) < Vad->StartingVpn) ||
2270         (((ULONG_PTR)EndingAddress >> PAGE_SHIFT) > Vad->EndingVpn))
2271     {
2272         Status = STATUS_CONFLICTING_ADDRESSES;
2273         goto FailPath;
2274     }
2275 
2276     /* These kinds of VADs are not supported atm  */
2277     if ((Vad->u.VadFlags.VadType == VadAwe) ||
2278         (Vad->u.VadFlags.VadType == VadDevicePhysicalMemory) ||
2279         (Vad->u.VadFlags.VadType == VadLargePages))
2280     {
2281         DPRINT1("Illegal VAD for attempting to set protection\n");
2282         Status = STATUS_CONFLICTING_ADDRESSES;
2283         goto FailPath;
2284     }
2285 
2286     /* Check for a VAD whose protection can't be changed */
2287     if (Vad->u.VadFlags.NoChange == 1)
2288     {
2289         DPRINT1("Trying to change protection of a NoChange VAD\n");
2290         Status = STATUS_INVALID_PAGE_PROTECTION;
2291         goto FailPath;
2292     }
2293 
2294     /* Is this section, or private memory? */
2295     if (Vad->u.VadFlags.PrivateMemory == 0)
2296     {
2297         /* Not yet supported */
2298         if (Vad->u.VadFlags.VadType == VadLargePageSection)
2299         {
2300             DPRINT1("Illegal VAD for attempting to set protection\n");
2301             Status = STATUS_CONFLICTING_ADDRESSES;
2302             goto FailPath;
2303         }
2304 
2305         /* Rotate VADs are not yet supported */
2306         if (Vad->u.VadFlags.VadType == VadRotatePhysical)
2307         {
2308             DPRINT1("Illegal VAD for attempting to set protection\n");
2309             Status = STATUS_CONFLICTING_ADDRESSES;
2310             goto FailPath;
2311         }
2312 
2313         /* Not valid on section files */
2314         if (NewAccessProtection & (PAGE_NOCACHE | PAGE_WRITECOMBINE))
2315         {
2316             /* Fail */
2317             DPRINT1("Invalid protection flags for section\n");
2318             Status = STATUS_INVALID_PARAMETER_4;
2319             goto FailPath;
2320         }
2321 
2322         /* Check if data or page file mapping protection PTE is compatible */
2323         if (!Vad->ControlArea->u.Flags.Image)
2324         {
2325             /* Not yet */
2326             DPRINT1("Fixme: Not checking for valid protection\n");
2327         }
2328 
2329         /* This is a section, and this is not yet supported */
2330         DPRINT1("Section protection not yet supported\n");
2331         OldProtect = 0;
2332     }
2333     else
2334     {
2335         /* Private memory, check protection flags */
2336         if ((NewAccessProtection & PAGE_WRITECOPY) ||
2337             (NewAccessProtection & PAGE_EXECUTE_WRITECOPY))
2338         {
2339             DPRINT1("Invalid protection flags for private memory\n");
2340             Status = STATUS_INVALID_PARAMETER_4;
2341             goto FailPath;
2342         }
2343 
2344         /* Lock the working set */
2345         MiLockProcessWorkingSetUnsafe(Process, Thread);
2346 
2347         /* Check if all pages in this range are committed */
2348         Committed = MiIsEntireRangeCommitted(StartingAddress,
2349                                              EndingAddress,
2350                                              Vad,
2351                                              Process);
2352         if (!Committed)
2353         {
2354             /* Fail */
2355             DPRINT1("The entire range is not committed\n");
2356             Status = STATUS_NOT_COMMITTED;
2357             MiUnlockProcessWorkingSetUnsafe(Process, Thread);
2358             goto FailPath;
2359         }
2360 
2361         /* Compute starting and ending PTE and PDE addresses */
2362         PointerPde = MiAddressToPde(StartingAddress);
2363         PointerPte = MiAddressToPte(StartingAddress);
2364         LastPte = MiAddressToPte(EndingAddress);
2365 
2366         /* Make this PDE valid */
2367         MiMakePdeExistAndMakeValid(PointerPde, Process, MM_NOIRQL);
2368 
2369         /* Save protection of the first page */
2370         if (PointerPte->u.Long != 0)
2371         {
2372             /* Capture the page protection and make the PDE valid */
2373             OldProtect = MiGetPageProtection(PointerPte);
2374             MiMakePdeExistAndMakeValid(PointerPde, Process, MM_NOIRQL);
2375         }
2376         else
2377         {
2378             /* Grab the old protection from the VAD itself */
2379             OldProtect = MmProtectToValue[Vad->u.VadFlags.Protection];
2380         }
2381 
2382         /* Loop all the PTEs now */
2383         while (PointerPte <= LastPte)
2384         {
2385             /* Check if we've crossed a PDE boundary and make the new PDE valid too */
2386             if (MiIsPteOnPdeBoundary(PointerPte))
2387             {
2388                 PointerPde = MiPteToPde(PointerPte);
2389                 MiMakePdeExistAndMakeValid(PointerPde, Process, MM_NOIRQL);
2390             }
2391 
2392             /* Capture the PTE and check if it was empty */
2393             PteContents = *PointerPte;
2394             if (PteContents.u.Long == 0)
2395             {
2396                 /* This used to be a zero PTE and it no longer is, so we must add a
2397                    reference to the pagetable. */
2398                 MiIncrementPageTableReferences(MiPteToAddress(PointerPte));
2399             }
2400 
2401             /* Check what kind of PTE we are dealing with */
2402             if (PteContents.u.Hard.Valid == 1)
2403             {
2404                 /* Get the PFN entry */
2405                 Pfn1 = MiGetPfnEntry(PFN_FROM_PTE(&PteContents));
2406 
2407                 /* We don't support this yet */
2408                 ASSERT(Pfn1->u3.e1.PrototypePte == 0);
2409 
2410                 /* Check if the page should not be accessible at all */
2411                 if ((NewAccessProtection & PAGE_NOACCESS) ||
2412                     (NewAccessProtection & PAGE_GUARD))
2413                 {
2414                     KIRQL OldIrql = MiAcquirePfnLock();
2415 
2416                     /* Mark the PTE as transition and change its protection */
2417                     PteContents.u.Hard.Valid = 0;
2418                     PteContents.u.Soft.Transition = 1;
2419                     PteContents.u.Trans.Protection = ProtectionMask;
2420                     /* Decrease PFN share count and write the PTE */
2421                     MiDecrementShareCount(Pfn1, PFN_FROM_PTE(&PteContents));
2422                     // FIXME: remove the page from the WS
2423                     MI_WRITE_INVALID_PTE(PointerPte, PteContents);
2424 #ifdef CONFIG_SMP
2425                     // FIXME: Should invalidate entry in every CPU TLB
2426                     ASSERT(KeNumberProcessors == 1);
2427 #endif
2428                     KeInvalidateTlbEntry(MiPteToAddress(PointerPte));
2429 
2430                     /* We are done for this PTE */
2431                     MiReleasePfnLock(OldIrql);
2432                 }
2433                 else
2434                 {
2435                     /* Write the protection mask and write it with a TLB flush */
2436                     Pfn1->OriginalPte.u.Soft.Protection = ProtectionMask;
2437                     MiFlushTbAndCapture(Vad,
2438                                         PointerPte,
2439                                         ProtectionMask,
2440                                         Pfn1,
2441                                         TRUE);
2442                 }
2443             }
2444             else
2445             {
2446                 /* We don't support these cases yet */
2447                 ASSERT(PteContents.u.Soft.Prototype == 0);
2448                 //ASSERT(PteContents.u.Soft.Transition == 0);
2449 
2450                 /* The PTE is already demand-zero, just update the protection mask */
2451                 PteContents.u.Soft.Protection = ProtectionMask;
2452                 MI_WRITE_INVALID_PTE(PointerPte, PteContents);
2453                 ASSERT(PointerPte->u.Long != 0);
2454             }
2455 
2456             /* Move to the next PTE */
2457             PointerPte++;
2458         }
2459 
2460         /* Unlock the working set */
2461         MiUnlockProcessWorkingSetUnsafe(Process, Thread);
2462     }
2463 
2464     /* Unlock the address space */
2465     MmUnlockAddressSpace(AddressSpace);
2466 
2467     /* Return parameters and success */
2468     *NumberOfBytesToProtect = EndingAddress - StartingAddress + 1;
2469     *BaseAddress = (PVOID)StartingAddress;
2470     *OldAccessProtection = OldProtect;
2471     return STATUS_SUCCESS;
2472 
2473 FailPath:
2474     /* Unlock the address space and return the failure code */
2475     MmUnlockAddressSpace(AddressSpace);
2476     return Status;
2477 }
2478 
2479 VOID
2480 NTAPI
MiMakePdeExistAndMakeValid(IN PMMPDE PointerPde,IN PEPROCESS TargetProcess,IN KIRQL OldIrql)2481 MiMakePdeExistAndMakeValid(IN PMMPDE PointerPde,
2482                            IN PEPROCESS TargetProcess,
2483                            IN KIRQL OldIrql)
2484 {
2485    PMMPTE PointerPte;
2486 #if _MI_PAGING_LEVELS >= 3
2487    PMMPPE PointerPpe = MiPdeToPpe(PointerPde);
2488 #if _MI_PAGING_LEVELS == 4
2489    PMMPXE PointerPxe = MiPdeToPxe(PointerPde);
2490 #endif
2491 #endif
2492 
2493    //
2494    // Sanity checks. The latter is because we only use this function with the
2495    // PFN lock not held, so it may go away in the future.
2496    //
2497    ASSERT(KeAreAllApcsDisabled() == TRUE);
2498    ASSERT(OldIrql == MM_NOIRQL);
2499 
2500    //
2501    // If everything is already valid, there is nothing to do.
2502    //
2503    if (
2504 #if _MI_PAGING_LEVELS == 4
2505        (PointerPxe->u.Hard.Valid) &&
2506 #endif
2507 #if _MI_PAGING_LEVELS >= 3
2508        (PointerPpe->u.Hard.Valid) &&
2509 #endif
2510        (PointerPde->u.Hard.Valid))
2511    {
2512        return;
2513    }
2514 
2515    //
2516    // At least something is invalid, so begin by getting the PTE for the PDE itself
2517    // and then lookup each additional level. We must do it in this precise order
2518    // because the pagfault.c code (as well as in Windows) depends that the next
2519    // level up (higher) must be valid when faulting a lower level
2520    //
2521    PointerPte = MiPteToAddress(PointerPde);
2522    do
2523    {
2524        //
2525        // Make sure APCs continued to be disabled
2526        //
2527        ASSERT(KeAreAllApcsDisabled() == TRUE);
2528 
2529 #if _MI_PAGING_LEVELS == 4
2530        //
2531        // First, make the PXE valid if needed
2532        //
2533        if (!PointerPxe->u.Hard.Valid)
2534        {
2535            MiMakeSystemAddressValid(PointerPpe, TargetProcess);
2536            ASSERT(PointerPxe->u.Hard.Valid == 1);
2537        }
2538 #endif
2539 
2540 #if _MI_PAGING_LEVELS >= 3
2541        //
2542        // Next, the PPE
2543        //
2544        if (!PointerPpe->u.Hard.Valid)
2545        {
2546            MiMakeSystemAddressValid(PointerPde, TargetProcess);
2547            ASSERT(PointerPpe->u.Hard.Valid == 1);
2548        }
2549 #endif
2550 
2551        //
2552        // And finally, make the PDE itself valid.
2553        //
2554        MiMakeSystemAddressValid(PointerPte, TargetProcess);
2555 
2556        /* Do not increment Page table refcount here for the PDE, this must be managed by caller */
2557 
2558        //
2559        // This should've worked the first time so the loop is really just for
2560        // show -- ASSERT that we're actually NOT going to be looping.
2561        //
2562        ASSERT(PointerPde->u.Hard.Valid == 1);
2563    } while (
2564 #if _MI_PAGING_LEVELS == 4
2565         !PointerPxe->u.Hard.Valid ||
2566 #endif
2567 #if _MI_PAGING_LEVELS >= 3
2568         !PointerPpe->u.Hard.Valid ||
2569 #endif
2570         !PointerPde->u.Hard.Valid);
2571 }
2572 
2573 VOID
2574 NTAPI
MiProcessValidPteList(IN PMMPTE * ValidPteList,IN ULONG Count)2575 MiProcessValidPteList(IN PMMPTE *ValidPteList,
2576                       IN ULONG Count)
2577 {
2578     KIRQL OldIrql;
2579     ULONG i;
2580     MMPTE TempPte;
2581     PFN_NUMBER PageFrameIndex;
2582     PMMPFN Pfn1, Pfn2;
2583 
2584     //
2585     // Acquire the PFN lock and loop all the PTEs in the list
2586     //
2587     OldIrql = MiAcquirePfnLock();
2588     for (i = 0; i != Count; i++)
2589     {
2590         //
2591         // The PTE must currently be valid
2592         //
2593         TempPte = *ValidPteList[i];
2594         ASSERT(TempPte.u.Hard.Valid == 1);
2595 
2596         //
2597         // Get the PFN entry for the page itself, and then for its page table
2598         //
2599         PageFrameIndex = PFN_FROM_PTE(&TempPte);
2600         Pfn1 = MiGetPfnEntry(PageFrameIndex);
2601         Pfn2 = MiGetPfnEntry(Pfn1->u4.PteFrame);
2602 
2603         //
2604         // Decrement the share count on the page table, and then on the page
2605         // itself
2606         //
2607         MiDecrementShareCount(Pfn2, Pfn1->u4.PteFrame);
2608         MI_SET_PFN_DELETED(Pfn1);
2609         MiDecrementShareCount(Pfn1, PageFrameIndex);
2610 
2611         //
2612         // Make the page decommitted
2613         //
2614         MI_WRITE_INVALID_PTE(ValidPteList[i], MmDecommittedPte);
2615     }
2616 
2617     //
2618     // All the PTEs have been dereferenced and made invalid, flush the TLB now
2619     // and then release the PFN lock
2620     //
2621     KeFlushCurrentTb();
2622     MiReleasePfnLock(OldIrql);
2623 }
2624 
2625 ULONG
2626 NTAPI
MiDecommitPages(IN PVOID StartingAddress,IN PMMPTE EndingPte,IN PEPROCESS Process,IN PMMVAD Vad)2627 MiDecommitPages(IN PVOID StartingAddress,
2628                 IN PMMPTE EndingPte,
2629                 IN PEPROCESS Process,
2630                 IN PMMVAD Vad)
2631 {
2632     PMMPTE PointerPte, CommitPte = NULL;
2633     PMMPDE PointerPde;
2634     ULONG CommitReduction = 0;
2635     PMMPTE ValidPteList[256];
2636     ULONG PteCount = 0;
2637     PMMPFN Pfn1;
2638     MMPTE PteContents;
2639     PETHREAD CurrentThread = PsGetCurrentThread();
2640 
2641     //
2642     // Get the PTE and PTE for the address, and lock the working set
2643     // If this was a VAD for a MEM_COMMIT allocation, also figure out where the
2644     // commited range ends so that we can do the right accounting.
2645     //
2646     PointerPde = MiAddressToPde(StartingAddress);
2647     PointerPte = MiAddressToPte(StartingAddress);
2648     if (Vad->u.VadFlags.MemCommit) CommitPte = MiAddressToPte(Vad->EndingVpn << PAGE_SHIFT);
2649     MiLockProcessWorkingSetUnsafe(Process, CurrentThread);
2650 
2651     //
2652     // Make the PDE valid, and now loop through each page's worth of data
2653     //
2654     MiMakePdeExistAndMakeValid(PointerPde, Process, MM_NOIRQL);
2655     while (PointerPte <= EndingPte)
2656     {
2657         //
2658         // Check if we've crossed a PDE boundary
2659         //
2660         if (MiIsPteOnPdeBoundary(PointerPte))
2661         {
2662             //
2663             // Get the new PDE and flush the valid PTEs we had built up until
2664             // now. This helps reduce the amount of TLB flushing we have to do.
2665             // Note that Windows does a much better job using timestamps and
2666             // such, and does not flush the entire TLB all the time, but right
2667             // now we have bigger problems to worry about than TLB flushing.
2668             //
2669             PointerPde = MiAddressToPde(StartingAddress);
2670             if (PteCount)
2671             {
2672                 MiProcessValidPteList(ValidPteList, PteCount);
2673                 PteCount = 0;
2674             }
2675 
2676             //
2677             // Make this PDE valid
2678             //
2679             MiMakePdeExistAndMakeValid(PointerPde, Process, MM_NOIRQL);
2680         }
2681 
2682         //
2683         // Read this PTE. It might be active or still demand-zero.
2684         //
2685         PteContents = *PointerPte;
2686         if (PteContents.u.Long)
2687         {
2688             //
2689             // The PTE is active. It might be valid and in a working set, or
2690             // it might be a prototype PTE or paged out or even in transition.
2691             //
2692             if (PointerPte->u.Long == MmDecommittedPte.u.Long)
2693             {
2694                 //
2695                 // It's already decommited, so there's nothing for us to do here
2696                 //
2697                 CommitReduction++;
2698             }
2699             else
2700             {
2701                 //
2702                 // Remove it from the counters, and check if it was valid or not
2703                 //
2704                 //Process->NumberOfPrivatePages--;
2705                 if (PteContents.u.Hard.Valid)
2706                 {
2707                     //
2708                     // It's valid. At this point make sure that it is not a ROS
2709                     // PFN. Also, we don't support ProtoPTEs in this code path.
2710                     //
2711                     Pfn1 = MiGetPfnEntry(PteContents.u.Hard.PageFrameNumber);
2712                     ASSERT(MI_IS_ROS_PFN(Pfn1) == FALSE);
2713                     ASSERT(Pfn1->u3.e1.PrototypePte == FALSE);
2714 
2715                     //
2716                     // Flush any pending PTEs that we had not yet flushed, if our
2717                     // list has gotten too big, then add this PTE to the flush list.
2718                     //
2719                     if (PteCount == 256)
2720                     {
2721                         MiProcessValidPteList(ValidPteList, PteCount);
2722                         PteCount = 0;
2723                     }
2724                     ValidPteList[PteCount++] = PointerPte;
2725                 }
2726                 else
2727                 {
2728                     //
2729                     // We do not support any of these other scenarios at the moment
2730                     //
2731                     ASSERT(PteContents.u.Soft.Prototype == 0);
2732                     ASSERT(PteContents.u.Soft.Transition == 0);
2733                     ASSERT(PteContents.u.Soft.PageFileHigh == 0);
2734 
2735                     //
2736                     // So the only other possibility is that it is still a demand
2737                     // zero PTE, in which case we undo the accounting we did
2738                     // earlier and simply make the page decommitted.
2739                     //
2740                     //Process->NumberOfPrivatePages++;
2741                     MI_WRITE_INVALID_PTE(PointerPte, MmDecommittedPte);
2742                 }
2743             }
2744         }
2745         else
2746         {
2747             //
2748             // This used to be a zero PTE and it no longer is, so we must add a
2749             // reference to the pagetable.
2750             //
2751             MiIncrementPageTableReferences(StartingAddress);
2752 
2753             //
2754             // Next, we account for decommitted PTEs and make the PTE as such
2755             //
2756             if (PointerPte > CommitPte) CommitReduction++;
2757             MI_WRITE_INVALID_PTE(PointerPte, MmDecommittedPte);
2758         }
2759 
2760         //
2761         // Move to the next PTE and the next address
2762         //
2763         PointerPte++;
2764         StartingAddress = (PVOID)((ULONG_PTR)StartingAddress + PAGE_SIZE);
2765     }
2766 
2767     //
2768     // Flush any dangling PTEs from the loop in the last page table, and then
2769     // release the working set and return the commit reduction accounting.
2770     //
2771     if (PteCount) MiProcessValidPteList(ValidPteList, PteCount);
2772     MiUnlockProcessWorkingSetUnsafe(Process, CurrentThread);
2773     return CommitReduction;
2774 }
2775 
2776 /* PUBLIC FUNCTIONS ***********************************************************/
2777 
2778 /*
2779  * @unimplemented
2780  */
2781 PVOID
2782 NTAPI
MmGetVirtualForPhysical(IN PHYSICAL_ADDRESS PhysicalAddress)2783 MmGetVirtualForPhysical(IN PHYSICAL_ADDRESS PhysicalAddress)
2784 {
2785     UNIMPLEMENTED;
2786     return 0;
2787 }
2788 
2789 /*
2790  * @unimplemented
2791  */
2792 PVOID
2793 NTAPI
MmSecureVirtualMemory(IN PVOID Address,IN SIZE_T Length,IN ULONG Mode)2794 MmSecureVirtualMemory(IN PVOID Address,
2795                       IN SIZE_T Length,
2796                       IN ULONG Mode)
2797 {
2798     static ULONG Warn; if (!Warn++) UNIMPLEMENTED;
2799     return Address;
2800 }
2801 
2802 /*
2803  * @unimplemented
2804  */
2805 VOID
2806 NTAPI
MmUnsecureVirtualMemory(IN PVOID SecureMem)2807 MmUnsecureVirtualMemory(IN PVOID SecureMem)
2808 {
2809     static ULONG Warn; if (!Warn++) UNIMPLEMENTED;
2810 }
2811 
2812 /* SYSTEM CALLS ***************************************************************/
2813 
2814 NTSTATUS
2815 NTAPI
NtReadVirtualMemory(IN HANDLE ProcessHandle,IN PVOID BaseAddress,OUT PVOID Buffer,IN SIZE_T NumberOfBytesToRead,OUT PSIZE_T NumberOfBytesRead OPTIONAL)2816 NtReadVirtualMemory(IN HANDLE ProcessHandle,
2817                     IN PVOID BaseAddress,
2818                     OUT PVOID Buffer,
2819                     IN SIZE_T NumberOfBytesToRead,
2820                     OUT PSIZE_T NumberOfBytesRead OPTIONAL)
2821 {
2822     KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
2823     PEPROCESS Process;
2824     NTSTATUS Status = STATUS_SUCCESS;
2825     SIZE_T BytesRead = 0;
2826     PAGED_CODE();
2827 
2828     //
2829     // Check if we came from user mode
2830     //
2831     if (PreviousMode != KernelMode)
2832     {
2833         //
2834         // Validate the read addresses
2835         //
2836         if ((((ULONG_PTR)BaseAddress + NumberOfBytesToRead) < (ULONG_PTR)BaseAddress) ||
2837             (((ULONG_PTR)Buffer + NumberOfBytesToRead) < (ULONG_PTR)Buffer) ||
2838             (((ULONG_PTR)BaseAddress + NumberOfBytesToRead) > MmUserProbeAddress) ||
2839             (((ULONG_PTR)Buffer + NumberOfBytesToRead) > MmUserProbeAddress))
2840         {
2841             //
2842             // Don't allow to write into kernel space
2843             //
2844             return STATUS_ACCESS_VIOLATION;
2845         }
2846 
2847         //
2848         // Enter SEH for probe
2849         //
2850         _SEH2_TRY
2851         {
2852             //
2853             // Probe the output value
2854             //
2855             if (NumberOfBytesRead) ProbeForWriteSize_t(NumberOfBytesRead);
2856         }
2857         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2858         {
2859             //
2860             // Get exception code
2861             //
2862             _SEH2_YIELD(return _SEH2_GetExceptionCode());
2863         }
2864         _SEH2_END;
2865     }
2866 
2867     //
2868     // Don't do zero-byte transfers
2869     //
2870     if (NumberOfBytesToRead)
2871     {
2872         //
2873         // Reference the process
2874         //
2875         Status = ObReferenceObjectByHandle(ProcessHandle,
2876                                            PROCESS_VM_READ,
2877                                            PsProcessType,
2878                                            PreviousMode,
2879                                            (PVOID*)(&Process),
2880                                            NULL);
2881         if (NT_SUCCESS(Status))
2882         {
2883             //
2884             // Do the copy
2885             //
2886             Status = MmCopyVirtualMemory(Process,
2887                                          BaseAddress,
2888                                          PsGetCurrentProcess(),
2889                                          Buffer,
2890                                          NumberOfBytesToRead,
2891                                          PreviousMode,
2892                                          &BytesRead);
2893 
2894             //
2895             // Dereference the process
2896             //
2897             ObDereferenceObject(Process);
2898         }
2899     }
2900 
2901     //
2902     // Check if the caller sent this parameter
2903     //
2904     if (NumberOfBytesRead)
2905     {
2906         //
2907         // Enter SEH to guard write
2908         //
2909         _SEH2_TRY
2910         {
2911             //
2912             // Return the number of bytes read
2913             //
2914             *NumberOfBytesRead = BytesRead;
2915         }
2916         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2917         {
2918         }
2919         _SEH2_END;
2920     }
2921 
2922     //
2923     // Return status
2924     //
2925     return Status;
2926 }
2927 
2928 NTSTATUS
2929 NTAPI
NtWriteVirtualMemory(IN HANDLE ProcessHandle,IN PVOID BaseAddress,IN PVOID Buffer,IN SIZE_T NumberOfBytesToWrite,OUT PSIZE_T NumberOfBytesWritten OPTIONAL)2930 NtWriteVirtualMemory(IN HANDLE ProcessHandle,
2931                      IN PVOID BaseAddress,
2932                      IN PVOID Buffer,
2933                      IN SIZE_T NumberOfBytesToWrite,
2934                      OUT PSIZE_T NumberOfBytesWritten OPTIONAL)
2935 {
2936     KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
2937     PEPROCESS Process;
2938     NTSTATUS Status = STATUS_SUCCESS;
2939     SIZE_T BytesWritten = 0;
2940     PAGED_CODE();
2941 
2942     //
2943     // Check if we came from user mode
2944     //
2945     if (PreviousMode != KernelMode)
2946     {
2947         //
2948         // Validate the read addresses
2949         //
2950         if ((((ULONG_PTR)BaseAddress + NumberOfBytesToWrite) < (ULONG_PTR)BaseAddress) ||
2951             (((ULONG_PTR)Buffer + NumberOfBytesToWrite) < (ULONG_PTR)Buffer) ||
2952             (((ULONG_PTR)BaseAddress + NumberOfBytesToWrite) > MmUserProbeAddress) ||
2953             (((ULONG_PTR)Buffer + NumberOfBytesToWrite) > MmUserProbeAddress))
2954         {
2955             //
2956             // Don't allow to write into kernel space
2957             //
2958             return STATUS_ACCESS_VIOLATION;
2959         }
2960 
2961         //
2962         // Enter SEH for probe
2963         //
2964         _SEH2_TRY
2965         {
2966             //
2967             // Probe the output value
2968             //
2969             if (NumberOfBytesWritten) ProbeForWriteSize_t(NumberOfBytesWritten);
2970         }
2971         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2972         {
2973             //
2974             // Get exception code
2975             //
2976             _SEH2_YIELD(return _SEH2_GetExceptionCode());
2977         }
2978         _SEH2_END;
2979     }
2980 
2981     //
2982     // Don't do zero-byte transfers
2983     //
2984     if (NumberOfBytesToWrite)
2985     {
2986         //
2987         // Reference the process
2988         //
2989         Status = ObReferenceObjectByHandle(ProcessHandle,
2990                                            PROCESS_VM_WRITE,
2991                                            PsProcessType,
2992                                            PreviousMode,
2993                                            (PVOID*)&Process,
2994                                            NULL);
2995         if (NT_SUCCESS(Status))
2996         {
2997             //
2998             // Do the copy
2999             //
3000             Status = MmCopyVirtualMemory(PsGetCurrentProcess(),
3001                                          Buffer,
3002                                          Process,
3003                                          BaseAddress,
3004                                          NumberOfBytesToWrite,
3005                                          PreviousMode,
3006                                          &BytesWritten);
3007 
3008             //
3009             // Dereference the process
3010             //
3011             ObDereferenceObject(Process);
3012         }
3013     }
3014 
3015     //
3016     // Check if the caller sent this parameter
3017     //
3018     if (NumberOfBytesWritten)
3019     {
3020         //
3021         // Enter SEH to guard write
3022         //
3023         _SEH2_TRY
3024         {
3025             //
3026             // Return the number of bytes written
3027             //
3028             *NumberOfBytesWritten = BytesWritten;
3029         }
3030         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
3031         {
3032         }
3033         _SEH2_END;
3034     }
3035 
3036     //
3037     // Return status
3038     //
3039     return Status;
3040 }
3041 
3042 NTSTATUS
3043 NTAPI
NtFlushInstructionCache(_In_ HANDLE ProcessHandle,_In_opt_ PVOID BaseAddress,_In_ SIZE_T FlushSize)3044 NtFlushInstructionCache(_In_ HANDLE ProcessHandle,
3045                         _In_opt_ PVOID BaseAddress,
3046                         _In_ SIZE_T FlushSize)
3047 {
3048     KAPC_STATE ApcState;
3049     PKPROCESS Process;
3050     NTSTATUS Status;
3051     PAGED_CODE();
3052 
3053     /* Is a base address given? */
3054     if (BaseAddress != NULL)
3055     {
3056         /* If the requested size is 0, there is nothing to do */
3057         if (FlushSize == 0)
3058         {
3059             return STATUS_SUCCESS;
3060         }
3061 
3062         /* Is this a user mode call? */
3063         if (ExGetPreviousMode() != KernelMode)
3064         {
3065             /* Make sure the base address is in user space */
3066             if (BaseAddress > MmHighestUserAddress)
3067             {
3068                 DPRINT1("Invalid BaseAddress 0x%p\n", BaseAddress);
3069                 return STATUS_ACCESS_VIOLATION;
3070             }
3071         }
3072     }
3073 
3074     /* Is another process requested? */
3075     if (ProcessHandle != NtCurrentProcess())
3076     {
3077         /* Reference the process */
3078         Status = ObReferenceObjectByHandle(ProcessHandle,
3079                                            PROCESS_VM_WRITE,
3080                                            PsProcessType,
3081                                            ExGetPreviousMode(),
3082                                            (PVOID*)&Process,
3083                                            NULL);
3084         if (!NT_SUCCESS(Status))
3085         {
3086             DPRINT1("Failed to reference the process %p\n", ProcessHandle);
3087             return Status;
3088         }
3089 
3090         /* Attach to the process */
3091         KeStackAttachProcess(Process, &ApcState);
3092     }
3093 
3094     /* Forward to Ke */
3095     KeSweepICache(BaseAddress, FlushSize);
3096 
3097     /* Check if we attached */
3098     if (ProcessHandle != NtCurrentProcess())
3099     {
3100         /* Detach from the process and dereference it */
3101         KeUnstackDetachProcess(&ApcState);
3102         ObDereferenceObject(Process);
3103     }
3104 
3105     /* All done, return to caller */
3106     return STATUS_SUCCESS;
3107 }
3108 
3109 NTSTATUS
3110 NTAPI
NtProtectVirtualMemory(IN HANDLE ProcessHandle,IN OUT PVOID * UnsafeBaseAddress,IN OUT SIZE_T * UnsafeNumberOfBytesToProtect,IN ULONG NewAccessProtection,OUT PULONG UnsafeOldAccessProtection)3111 NtProtectVirtualMemory(IN HANDLE ProcessHandle,
3112                        IN OUT PVOID *UnsafeBaseAddress,
3113                        IN OUT SIZE_T *UnsafeNumberOfBytesToProtect,
3114                        IN ULONG NewAccessProtection,
3115                        OUT PULONG UnsafeOldAccessProtection)
3116 {
3117     PEPROCESS Process;
3118     ULONG OldAccessProtection;
3119     ULONG Protection;
3120     PEPROCESS CurrentProcess = PsGetCurrentProcess();
3121     PVOID BaseAddress = NULL;
3122     SIZE_T NumberOfBytesToProtect = 0;
3123     KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
3124     NTSTATUS Status;
3125     BOOLEAN Attached = FALSE;
3126     KAPC_STATE ApcState;
3127     PAGED_CODE();
3128 
3129     //
3130     // Check for valid protection flags
3131     //
3132     Protection = NewAccessProtection & ~(PAGE_GUARD|PAGE_NOCACHE);
3133     if (Protection != PAGE_NOACCESS &&
3134         Protection != PAGE_READONLY &&
3135         Protection != PAGE_READWRITE &&
3136         Protection != PAGE_WRITECOPY &&
3137         Protection != PAGE_EXECUTE &&
3138         Protection != PAGE_EXECUTE_READ &&
3139         Protection != PAGE_EXECUTE_READWRITE &&
3140         Protection != PAGE_EXECUTE_WRITECOPY)
3141     {
3142         //
3143         // Fail
3144         //
3145         return STATUS_INVALID_PAGE_PROTECTION;
3146     }
3147 
3148     //
3149     // Check if we came from user mode
3150     //
3151     if (PreviousMode != KernelMode)
3152     {
3153         //
3154         // Enter SEH for probing
3155         //
3156         _SEH2_TRY
3157         {
3158             //
3159             // Validate all outputs
3160             //
3161             ProbeForWritePointer(UnsafeBaseAddress);
3162             ProbeForWriteSize_t(UnsafeNumberOfBytesToProtect);
3163             ProbeForWriteUlong(UnsafeOldAccessProtection);
3164 
3165             //
3166             // Capture them
3167             //
3168             BaseAddress = *UnsafeBaseAddress;
3169             NumberOfBytesToProtect = *UnsafeNumberOfBytesToProtect;
3170         }
3171         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
3172         {
3173             //
3174             // Get exception code
3175             //
3176             _SEH2_YIELD(return _SEH2_GetExceptionCode());
3177         }
3178         _SEH2_END;
3179     }
3180     else
3181     {
3182         //
3183         // Capture directly
3184         //
3185         BaseAddress = *UnsafeBaseAddress;
3186         NumberOfBytesToProtect = *UnsafeNumberOfBytesToProtect;
3187     }
3188 
3189     //
3190     // Catch illegal base address
3191     //
3192     if (BaseAddress > MM_HIGHEST_USER_ADDRESS) return STATUS_INVALID_PARAMETER_2;
3193 
3194     //
3195     // Catch illegal region size
3196     //
3197     if ((MmUserProbeAddress - (ULONG_PTR)BaseAddress) < NumberOfBytesToProtect)
3198     {
3199         //
3200         // Fail
3201         //
3202         return STATUS_INVALID_PARAMETER_3;
3203     }
3204 
3205     //
3206     // 0 is also illegal
3207     //
3208     if (!NumberOfBytesToProtect) return STATUS_INVALID_PARAMETER_3;
3209 
3210     //
3211     // Get a reference to the process
3212     //
3213     Status = ObReferenceObjectByHandle(ProcessHandle,
3214                                        PROCESS_VM_OPERATION,
3215                                        PsProcessType,
3216                                        PreviousMode,
3217                                        (PVOID*)(&Process),
3218                                        NULL);
3219     if (!NT_SUCCESS(Status)) return Status;
3220 
3221     //
3222     // Check if we should attach
3223     //
3224     if (CurrentProcess != Process)
3225     {
3226         //
3227         // Do it
3228         //
3229         KeStackAttachProcess(&Process->Pcb, &ApcState);
3230         Attached = TRUE;
3231     }
3232 
3233     //
3234     // Do the actual work
3235     //
3236     Status = MiProtectVirtualMemory(Process,
3237                                     &BaseAddress,
3238                                     &NumberOfBytesToProtect,
3239                                     NewAccessProtection,
3240                                     &OldAccessProtection);
3241 
3242     //
3243     // Detach if needed
3244     //
3245     if (Attached) KeUnstackDetachProcess(&ApcState);
3246 
3247     //
3248     // Release reference
3249     //
3250     ObDereferenceObject(Process);
3251 
3252     //
3253     // Enter SEH to return data
3254     //
3255     _SEH2_TRY
3256     {
3257         //
3258         // Return data to user
3259         //
3260         *UnsafeOldAccessProtection = OldAccessProtection;
3261         *UnsafeBaseAddress = BaseAddress;
3262         *UnsafeNumberOfBytesToProtect = NumberOfBytesToProtect;
3263     }
3264     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
3265     {
3266     }
3267     _SEH2_END;
3268 
3269     //
3270     // Return status
3271     //
3272     return Status;
3273 }
3274 
3275 FORCEINLINE
3276 BOOLEAN
MI_IS_LOCKED_VA(PMMPFN Pfn1,ULONG LockType)3277 MI_IS_LOCKED_VA(
3278     PMMPFN Pfn1,
3279     ULONG LockType)
3280 {
3281     // HACK until we have proper WSLIST support
3282     PMMWSLE Wsle = &Pfn1->Wsle;
3283 
3284     if ((LockType & MAP_PROCESS) && (Wsle->u1.e1.LockedInWs))
3285         return TRUE;
3286     if ((LockType & MAP_SYSTEM) && (Wsle->u1.e1.LockedInMemory))
3287         return TRUE;
3288 
3289     return FALSE;
3290 }
3291 
3292 FORCEINLINE
3293 VOID
MI_LOCK_VA(PMMPFN Pfn1,ULONG LockType)3294 MI_LOCK_VA(
3295     PMMPFN Pfn1,
3296     ULONG LockType)
3297 {
3298     // HACK until we have proper WSLIST support
3299     PMMWSLE Wsle = &Pfn1->Wsle;
3300 
3301     if (!Wsle->u1.e1.LockedInWs &&
3302         !Wsle->u1.e1.LockedInMemory)
3303     {
3304         MiReferenceProbedPageAndBumpLockCount(Pfn1);
3305     }
3306 
3307     if (LockType & MAP_PROCESS)
3308         Wsle->u1.e1.LockedInWs = 1;
3309     if (LockType & MAP_SYSTEM)
3310         Wsle->u1.e1.LockedInMemory = 1;
3311 }
3312 
3313 FORCEINLINE
3314 VOID
MI_UNLOCK_VA(PMMPFN Pfn1,ULONG LockType)3315 MI_UNLOCK_VA(
3316     PMMPFN Pfn1,
3317     ULONG LockType)
3318 {
3319     // HACK until we have proper WSLIST support
3320     PMMWSLE Wsle = &Pfn1->Wsle;
3321 
3322     if (LockType & MAP_PROCESS)
3323         Wsle->u1.e1.LockedInWs = 0;
3324     if (LockType & MAP_SYSTEM)
3325         Wsle->u1.e1.LockedInMemory = 0;
3326 
3327     if (!Wsle->u1.e1.LockedInWs &&
3328         !Wsle->u1.e1.LockedInMemory)
3329     {
3330         MiDereferencePfnAndDropLockCount(Pfn1);
3331     }
3332 }
3333 
3334 static
3335 NTSTATUS
MiCheckVadsForLockOperation(_Inout_ PVOID * BaseAddress,_Inout_ PSIZE_T RegionSize,_Inout_ PVOID * EndAddress)3336 MiCheckVadsForLockOperation(
3337     _Inout_ PVOID *BaseAddress,
3338     _Inout_ PSIZE_T RegionSize,
3339     _Inout_ PVOID *EndAddress)
3340 
3341 {
3342     PMMVAD Vad;
3343     PVOID CurrentVa;
3344 
3345     /* Get the base address and align the start address */
3346     *EndAddress = (PUCHAR)*BaseAddress + *RegionSize;
3347     *EndAddress = ALIGN_UP_POINTER_BY(*EndAddress, PAGE_SIZE);
3348     *BaseAddress = ALIGN_DOWN_POINTER_BY(*BaseAddress, PAGE_SIZE);
3349 
3350     /* First loop and check all VADs */
3351     CurrentVa = *BaseAddress;
3352     while (CurrentVa < *EndAddress)
3353     {
3354         /* Get VAD */
3355         Vad = MiLocateAddress(CurrentVa);
3356         if (Vad == NULL)
3357         {
3358             /// FIXME: this might be a memory area for a section view...
3359             return STATUS_ACCESS_VIOLATION;
3360         }
3361 
3362         /* Check VAD type */
3363         if ((Vad->u.VadFlags.VadType != VadNone) &&
3364             (Vad->u.VadFlags.VadType != VadImageMap) &&
3365             (Vad->u.VadFlags.VadType != VadWriteWatch))
3366         {
3367             *EndAddress = CurrentVa;
3368             *RegionSize = (PUCHAR)*EndAddress - (PUCHAR)*BaseAddress;
3369             return STATUS_INCOMPATIBLE_FILE_MAP;
3370         }
3371 
3372         CurrentVa = (PVOID)((Vad->EndingVpn + 1) << PAGE_SHIFT);
3373     }
3374 
3375     *RegionSize = (PUCHAR)*EndAddress - (PUCHAR)*BaseAddress;
3376     return STATUS_SUCCESS;
3377 }
3378 
3379 static
3380 NTSTATUS
MiLockVirtualMemory(IN OUT PVOID * BaseAddress,IN OUT PSIZE_T RegionSize,IN ULONG MapType)3381 MiLockVirtualMemory(
3382     IN OUT PVOID *BaseAddress,
3383     IN OUT PSIZE_T RegionSize,
3384     IN ULONG MapType)
3385 {
3386     PEPROCESS CurrentProcess;
3387     PMMSUPPORT AddressSpace;
3388     PVOID CurrentVa, EndAddress;
3389     PMMPTE PointerPte, LastPte;
3390     PMMPDE PointerPde;
3391 #if (_MI_PAGING_LEVELS >= 3)
3392     PMMPDE PointerPpe;
3393 #endif
3394 #if (_MI_PAGING_LEVELS == 4)
3395     PMMPDE PointerPxe;
3396 #endif
3397     PMMPFN Pfn1;
3398     NTSTATUS Status, TempStatus;
3399 
3400     /* Lock the address space */
3401     AddressSpace = MmGetCurrentAddressSpace();
3402     MmLockAddressSpace(AddressSpace);
3403 
3404     /* Make sure we still have an address space */
3405     CurrentProcess = PsGetCurrentProcess();
3406     if (CurrentProcess->VmDeleted)
3407     {
3408         Status = STATUS_PROCESS_IS_TERMINATING;
3409         goto Cleanup;
3410     }
3411 
3412     /* Check the VADs in the requested range */
3413     Status = MiCheckVadsForLockOperation(BaseAddress, RegionSize, &EndAddress);
3414     if (!NT_SUCCESS(Status))
3415     {
3416         goto Cleanup;
3417     }
3418 
3419     /* Enter SEH for probing */
3420     _SEH2_TRY
3421     {
3422         /* Loop all pages and probe them */
3423         CurrentVa = *BaseAddress;
3424         while (CurrentVa < EndAddress)
3425         {
3426             (void)(*(volatile CHAR*)CurrentVa);
3427             CurrentVa = (PUCHAR)CurrentVa + PAGE_SIZE;
3428         }
3429     }
3430     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
3431     {
3432         Status = _SEH2_GetExceptionCode();
3433         goto Cleanup;
3434     }
3435     _SEH2_END;
3436 
3437     /* All pages were accessible, since we hold the address space lock, nothing
3438        can be de-committed. Assume success for now. */
3439     Status = STATUS_SUCCESS;
3440 
3441     /* Get the PTE and PDE */
3442     PointerPte = MiAddressToPte(*BaseAddress);
3443     PointerPde = MiAddressToPde(*BaseAddress);
3444 #if (_MI_PAGING_LEVELS >= 3)
3445     PointerPpe = MiAddressToPpe(*BaseAddress);
3446 #endif
3447 #if (_MI_PAGING_LEVELS == 4)
3448     PointerPxe = MiAddressToPxe(*BaseAddress);
3449 #endif
3450 
3451     /* Get the last PTE */
3452     LastPte = MiAddressToPte((PVOID)((ULONG_PTR)EndAddress - 1));
3453 
3454     /* Lock the process working set */
3455     MiLockProcessWorkingSet(CurrentProcess, PsGetCurrentThread());
3456 
3457     /* Loop the pages */
3458     do
3459     {
3460         /* Check for a page that is not accessible */
3461         while (
3462 #if (_MI_PAGING_LEVELS == 4)
3463                (PointerPxe->u.Hard.Valid == 0) ||
3464 #endif
3465 #if (_MI_PAGING_LEVELS >= 3)
3466                (PointerPpe->u.Hard.Valid == 0) ||
3467 #endif
3468                (PointerPde->u.Hard.Valid == 0) ||
3469                (PointerPte->u.Hard.Valid == 0))
3470         {
3471             /* Release process working set */
3472             MiUnlockProcessWorkingSet(CurrentProcess, PsGetCurrentThread());
3473 
3474             /* Access the page */
3475             CurrentVa = MiPteToAddress(PointerPte);
3476 
3477             //HACK: Pass a placeholder TrapInformation so the fault handler knows we're unlocked
3478             TempStatus = MmAccessFault(TRUE, CurrentVa, KernelMode, (PVOID)(ULONG_PTR)0xBADBADA3BADBADA3ULL);
3479             if (!NT_SUCCESS(TempStatus))
3480             {
3481                 // This should only happen, when remote backing storage is not accessible
3482                 ASSERT(FALSE);
3483                 Status = TempStatus;
3484                 goto Cleanup;
3485             }
3486 
3487             /* Lock the process working set */
3488             MiLockProcessWorkingSet(CurrentProcess, PsGetCurrentThread());
3489         }
3490 
3491         /* Get the PFN */
3492         Pfn1 = MiGetPfnEntry(PFN_FROM_PTE(PointerPte));
3493         ASSERT(Pfn1 != NULL);
3494 
3495         /* Check the previous lock status */
3496         if (MI_IS_LOCKED_VA(Pfn1, MapType))
3497         {
3498             Status = STATUS_WAS_LOCKED;
3499         }
3500 
3501         /* Lock it */
3502         MI_LOCK_VA(Pfn1, MapType);
3503 
3504         /* Go to the next PTE */
3505         PointerPte++;
3506 
3507         /* Check if we're on a PDE boundary */
3508         if (MiIsPteOnPdeBoundary(PointerPte)) PointerPde++;
3509 #if (_MI_PAGING_LEVELS >= 3)
3510         if (MiIsPteOnPpeBoundary(PointerPte)) PointerPpe++;
3511 #endif
3512 #if (_MI_PAGING_LEVELS == 4)
3513         if (MiIsPteOnPxeBoundary(PointerPte)) PointerPxe++;
3514 #endif
3515     } while (PointerPte <= LastPte);
3516 
3517     /* Release process working set */
3518     MiUnlockProcessWorkingSet(CurrentProcess, PsGetCurrentThread());
3519 
3520 Cleanup:
3521     /* Unlock address space */
3522     MmUnlockAddressSpace(AddressSpace);
3523 
3524     return Status;
3525 }
3526 
3527 NTSTATUS
3528 NTAPI
NtLockVirtualMemory(IN HANDLE ProcessHandle,IN OUT PVOID * BaseAddress,IN OUT PSIZE_T NumberOfBytesToLock,IN ULONG MapType)3529 NtLockVirtualMemory(IN HANDLE ProcessHandle,
3530                     IN OUT PVOID *BaseAddress,
3531                     IN OUT PSIZE_T NumberOfBytesToLock,
3532                     IN ULONG MapType)
3533 {
3534     PEPROCESS Process;
3535     PEPROCESS CurrentProcess = PsGetCurrentProcess();
3536     NTSTATUS Status;
3537     BOOLEAN Attached = FALSE;
3538     KAPC_STATE ApcState;
3539     KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
3540     PVOID CapturedBaseAddress;
3541     SIZE_T CapturedBytesToLock;
3542     PAGED_CODE();
3543 
3544     //
3545     // Validate flags
3546     //
3547     if ((MapType & ~(MAP_PROCESS | MAP_SYSTEM)))
3548     {
3549         //
3550         // Invalid set of flags
3551         //
3552         return STATUS_INVALID_PARAMETER;
3553     }
3554 
3555     //
3556     // At least one flag must be specified
3557     //
3558     if (!(MapType & (MAP_PROCESS | MAP_SYSTEM)))
3559     {
3560         //
3561         // No flag given
3562         //
3563         return STATUS_INVALID_PARAMETER;
3564     }
3565 
3566     //
3567     // Enter SEH for probing
3568     //
3569     _SEH2_TRY
3570     {
3571         //
3572         // Validate output data
3573         //
3574         ProbeForWritePointer(BaseAddress);
3575         ProbeForWriteSize_t(NumberOfBytesToLock);
3576 
3577         //
3578         // Capture it
3579         //
3580         CapturedBaseAddress = *BaseAddress;
3581         CapturedBytesToLock = *NumberOfBytesToLock;
3582     }
3583     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
3584     {
3585         //
3586         // Get exception code
3587         //
3588         _SEH2_YIELD(return _SEH2_GetExceptionCode());
3589     }
3590     _SEH2_END;
3591 
3592     //
3593     // Catch illegal base address
3594     //
3595     if (CapturedBaseAddress > MM_HIGHEST_USER_ADDRESS) return STATUS_INVALID_PARAMETER;
3596 
3597     //
3598     // Catch illegal region size
3599     //
3600     if ((MmUserProbeAddress - (ULONG_PTR)CapturedBaseAddress) < CapturedBytesToLock)
3601     {
3602         //
3603         // Fail
3604         //
3605         return STATUS_INVALID_PARAMETER;
3606     }
3607 
3608     //
3609     // 0 is also illegal
3610     //
3611     if (!CapturedBytesToLock) return STATUS_INVALID_PARAMETER;
3612 
3613     //
3614     // Get a reference to the process
3615     //
3616     Status = ObReferenceObjectByHandle(ProcessHandle,
3617                                        PROCESS_VM_OPERATION,
3618                                        PsProcessType,
3619                                        PreviousMode,
3620                                        (PVOID*)(&Process),
3621                                        NULL);
3622     if (!NT_SUCCESS(Status)) return Status;
3623 
3624     //
3625     // Check if this is is system-mapped
3626     //
3627     if (MapType & MAP_SYSTEM)
3628     {
3629         //
3630         // Check for required privilege
3631         //
3632         if (!SeSinglePrivilegeCheck(SeLockMemoryPrivilege, PreviousMode))
3633         {
3634             //
3635             // Fail: Don't have it
3636             //
3637             ObDereferenceObject(Process);
3638             return STATUS_PRIVILEGE_NOT_HELD;
3639         }
3640     }
3641 
3642     //
3643     // Check if we should attach
3644     //
3645     if (CurrentProcess != Process)
3646     {
3647         //
3648         // Do it
3649         //
3650         KeStackAttachProcess(&Process->Pcb, &ApcState);
3651         Attached = TRUE;
3652     }
3653 
3654     //
3655     // Call the internal function
3656     //
3657     Status = MiLockVirtualMemory(&CapturedBaseAddress,
3658                                  &CapturedBytesToLock,
3659                                  MapType);
3660 
3661     //
3662     // Detach if needed
3663     //
3664     if (Attached) KeUnstackDetachProcess(&ApcState);
3665 
3666     //
3667     // Release reference
3668     //
3669     ObDereferenceObject(Process);
3670 
3671     //
3672     // Enter SEH to return data
3673     //
3674     _SEH2_TRY
3675     {
3676         //
3677         // Return data to user
3678         //
3679         *BaseAddress = CapturedBaseAddress;
3680         *NumberOfBytesToLock = CapturedBytesToLock;
3681     }
3682     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
3683     {
3684         //
3685         // Get exception code
3686         //
3687         _SEH2_YIELD(return _SEH2_GetExceptionCode());
3688     }
3689     _SEH2_END;
3690 
3691     //
3692     // Return status
3693     //
3694     return Status;
3695 }
3696 
3697 
3698 static
3699 NTSTATUS
MiUnlockVirtualMemory(IN OUT PVOID * BaseAddress,IN OUT PSIZE_T RegionSize,IN ULONG MapType)3700 MiUnlockVirtualMemory(
3701     IN OUT PVOID *BaseAddress,
3702     IN OUT PSIZE_T RegionSize,
3703     IN ULONG MapType)
3704 {
3705     PEPROCESS CurrentProcess;
3706     PMMSUPPORT AddressSpace;
3707     PVOID EndAddress;
3708     PMMPTE PointerPte, LastPte;
3709     PMMPDE PointerPde;
3710 #if (_MI_PAGING_LEVELS >= 3)
3711     PMMPDE PointerPpe;
3712 #endif
3713 #if (_MI_PAGING_LEVELS == 4)
3714     PMMPDE PointerPxe;
3715 #endif
3716     PMMPFN Pfn1;
3717     NTSTATUS Status;
3718 
3719     /* Lock the address space */
3720     AddressSpace = MmGetCurrentAddressSpace();
3721     MmLockAddressSpace(AddressSpace);
3722 
3723     /* Make sure we still have an address space */
3724     CurrentProcess = PsGetCurrentProcess();
3725     if (CurrentProcess->VmDeleted)
3726     {
3727         Status = STATUS_PROCESS_IS_TERMINATING;
3728         goto Cleanup;
3729     }
3730 
3731     /* Check the VADs in the requested range */
3732     Status = MiCheckVadsForLockOperation(BaseAddress, RegionSize, &EndAddress);
3733 
3734     /* Note: only bail out, if we hit an area without a VAD. If we hit an
3735        incompatible VAD we continue, like Windows does */
3736     if (Status == STATUS_ACCESS_VIOLATION)
3737     {
3738         Status = STATUS_NOT_LOCKED;
3739         goto Cleanup;
3740     }
3741 
3742     /* Get the PTE and PDE */
3743     PointerPte = MiAddressToPte(*BaseAddress);
3744     PointerPde = MiAddressToPde(*BaseAddress);
3745 #if (_MI_PAGING_LEVELS >= 3)
3746     PointerPpe = MiAddressToPpe(*BaseAddress);
3747 #endif
3748 #if (_MI_PAGING_LEVELS == 4)
3749     PointerPxe = MiAddressToPxe(*BaseAddress);
3750 #endif
3751 
3752     /* Get the last PTE */
3753     LastPte = MiAddressToPte((PVOID)((ULONG_PTR)EndAddress - 1));
3754 
3755     /* Lock the process working set */
3756     MiLockProcessWorkingSet(CurrentProcess, PsGetCurrentThread());
3757 
3758     /* Loop the pages */
3759     do
3760     {
3761         /* Check for a page that is not present */
3762         if (
3763 #if (_MI_PAGING_LEVELS == 4)
3764                (PointerPxe->u.Hard.Valid == 0) ||
3765 #endif
3766 #if (_MI_PAGING_LEVELS >= 3)
3767                (PointerPpe->u.Hard.Valid == 0) ||
3768 #endif
3769                (PointerPde->u.Hard.Valid == 0) ||
3770                (PointerPte->u.Hard.Valid == 0))
3771         {
3772             /* Remember it, but keep going */
3773             Status = STATUS_NOT_LOCKED;
3774         }
3775         else
3776         {
3777             /* Get the PFN */
3778             Pfn1 = MiGetPfnEntry(PFN_FROM_PTE(PointerPte));
3779             ASSERT(Pfn1 != NULL);
3780 
3781             /* Check if all of the requested locks are present */
3782             if (((MapType & MAP_SYSTEM) && !MI_IS_LOCKED_VA(Pfn1, MAP_SYSTEM)) ||
3783                 ((MapType & MAP_PROCESS) && !MI_IS_LOCKED_VA(Pfn1, MAP_PROCESS)))
3784             {
3785                 /* Remember it, but keep going */
3786                 Status = STATUS_NOT_LOCKED;
3787 
3788                 /* Check if no lock is present */
3789                 if (!MI_IS_LOCKED_VA(Pfn1, MAP_PROCESS | MAP_SYSTEM))
3790                 {
3791                     DPRINT1("FIXME: Should remove the page from WS\n");
3792                 }
3793             }
3794         }
3795 
3796         /* Go to the next PTE */
3797         PointerPte++;
3798 
3799         /* Check if we're on a PDE boundary */
3800         if (MiIsPteOnPdeBoundary(PointerPte)) PointerPde++;
3801 #if (_MI_PAGING_LEVELS >= 3)
3802         if (MiIsPteOnPpeBoundary(PointerPte)) PointerPpe++;
3803 #endif
3804 #if (_MI_PAGING_LEVELS == 4)
3805         if (MiIsPteOnPxeBoundary(PointerPte)) PointerPxe++;
3806 #endif
3807     } while (PointerPte <= LastPte);
3808 
3809     /* Check if we hit a page that was not locked */
3810     if (Status == STATUS_NOT_LOCKED)
3811     {
3812         goto CleanupWithWsLock;
3813     }
3814 
3815     /* All pages in the region were locked, so unlock them all */
3816 
3817     /* Get the PTE and PDE */
3818     PointerPte = MiAddressToPte(*BaseAddress);
3819     PointerPde = MiAddressToPde(*BaseAddress);
3820 #if (_MI_PAGING_LEVELS >= 3)
3821     PointerPpe = MiAddressToPpe(*BaseAddress);
3822 #endif
3823 #if (_MI_PAGING_LEVELS == 4)
3824     PointerPxe = MiAddressToPxe(*BaseAddress);
3825 #endif
3826 
3827     /* Loop the pages */
3828     do
3829     {
3830         /* Unlock it */
3831         Pfn1 = MiGetPfnEntry(PFN_FROM_PTE(PointerPte));
3832         MI_UNLOCK_VA(Pfn1, MapType);
3833 
3834         /* Go to the next PTE */
3835         PointerPte++;
3836 
3837         /* Check if we're on a PDE boundary */
3838         if (MiIsPteOnPdeBoundary(PointerPte)) PointerPde++;
3839 #if (_MI_PAGING_LEVELS >= 3)
3840         if (MiIsPteOnPpeBoundary(PointerPte)) PointerPpe++;
3841 #endif
3842 #if (_MI_PAGING_LEVELS == 4)
3843         if (MiIsPteOnPxeBoundary(PointerPte)) PointerPxe++;
3844 #endif
3845     } while (PointerPte <= LastPte);
3846 
3847     /* Everything is done */
3848     Status = STATUS_SUCCESS;
3849 
3850 CleanupWithWsLock:
3851 
3852     /* Release process working set */
3853     MiUnlockProcessWorkingSet(CurrentProcess, PsGetCurrentThread());
3854 
3855 Cleanup:
3856     /* Unlock address space */
3857     MmUnlockAddressSpace(AddressSpace);
3858 
3859     return Status;
3860 }
3861 
3862 
3863 NTSTATUS
3864 NTAPI
NtUnlockVirtualMemory(IN HANDLE ProcessHandle,IN OUT PVOID * BaseAddress,IN OUT PSIZE_T NumberOfBytesToUnlock,IN ULONG MapType)3865 NtUnlockVirtualMemory(IN HANDLE ProcessHandle,
3866                       IN OUT PVOID *BaseAddress,
3867                       IN OUT PSIZE_T NumberOfBytesToUnlock,
3868                       IN ULONG MapType)
3869 {
3870     PEPROCESS Process;
3871     PEPROCESS CurrentProcess = PsGetCurrentProcess();
3872     NTSTATUS Status;
3873     BOOLEAN Attached = FALSE;
3874     KAPC_STATE ApcState;
3875     KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
3876     PVOID CapturedBaseAddress;
3877     SIZE_T CapturedBytesToUnlock;
3878     PAGED_CODE();
3879 
3880     //
3881     // Validate flags
3882     //
3883     if ((MapType & ~(MAP_PROCESS | MAP_SYSTEM)))
3884     {
3885         //
3886         // Invalid set of flags
3887         //
3888         return STATUS_INVALID_PARAMETER;
3889     }
3890 
3891     //
3892     // At least one flag must be specified
3893     //
3894     if (!(MapType & (MAP_PROCESS | MAP_SYSTEM)))
3895     {
3896         //
3897         // No flag given
3898         //
3899         return STATUS_INVALID_PARAMETER;
3900     }
3901 
3902     //
3903     // Enter SEH for probing
3904     //
3905     _SEH2_TRY
3906     {
3907         //
3908         // Validate output data
3909         //
3910         ProbeForWritePointer(BaseAddress);
3911         ProbeForWriteSize_t(NumberOfBytesToUnlock);
3912 
3913         //
3914         // Capture it
3915         //
3916         CapturedBaseAddress = *BaseAddress;
3917         CapturedBytesToUnlock = *NumberOfBytesToUnlock;
3918     }
3919     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
3920     {
3921         //
3922         // Get exception code
3923         //
3924         _SEH2_YIELD(return _SEH2_GetExceptionCode());
3925     }
3926     _SEH2_END;
3927 
3928     //
3929     // Catch illegal base address
3930     //
3931     if (CapturedBaseAddress > MM_HIGHEST_USER_ADDRESS) return STATUS_INVALID_PARAMETER;
3932 
3933     //
3934     // Catch illegal region size
3935     //
3936     if ((MmUserProbeAddress - (ULONG_PTR)CapturedBaseAddress) < CapturedBytesToUnlock)
3937     {
3938         //
3939         // Fail
3940         //
3941         return STATUS_INVALID_PARAMETER;
3942     }
3943 
3944     //
3945     // 0 is also illegal
3946     //
3947     if (!CapturedBytesToUnlock) return STATUS_INVALID_PARAMETER;
3948 
3949     //
3950     // Get a reference to the process
3951     //
3952     Status = ObReferenceObjectByHandle(ProcessHandle,
3953                                        PROCESS_VM_OPERATION,
3954                                        PsProcessType,
3955                                        PreviousMode,
3956                                        (PVOID*)(&Process),
3957                                        NULL);
3958     if (!NT_SUCCESS(Status)) return Status;
3959 
3960     //
3961     // Check if this is is system-mapped
3962     //
3963     if (MapType & MAP_SYSTEM)
3964     {
3965         //
3966         // Check for required privilege
3967         //
3968         if (!SeSinglePrivilegeCheck(SeLockMemoryPrivilege, PreviousMode))
3969         {
3970             //
3971             // Fail: Don't have it
3972             //
3973             ObDereferenceObject(Process);
3974             return STATUS_PRIVILEGE_NOT_HELD;
3975         }
3976     }
3977 
3978     //
3979     // Check if we should attach
3980     //
3981     if (CurrentProcess != Process)
3982     {
3983         //
3984         // Do it
3985         //
3986         KeStackAttachProcess(&Process->Pcb, &ApcState);
3987         Attached = TRUE;
3988     }
3989 
3990     //
3991     // Call the internal function
3992     //
3993     Status = MiUnlockVirtualMemory(&CapturedBaseAddress,
3994                                    &CapturedBytesToUnlock,
3995                                    MapType);
3996 
3997     //
3998     // Detach if needed
3999     //
4000     if (Attached) KeUnstackDetachProcess(&ApcState);
4001 
4002     //
4003     // Release reference
4004     //
4005     ObDereferenceObject(Process);
4006 
4007     //
4008     // Enter SEH to return data
4009     //
4010     _SEH2_TRY
4011     {
4012         //
4013         // Return data to user
4014         //
4015         *BaseAddress = CapturedBaseAddress;
4016         *NumberOfBytesToUnlock = CapturedBytesToUnlock;
4017     }
4018     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
4019     {
4020         //
4021         // Get exception code
4022         //
4023         _SEH2_YIELD(return _SEH2_GetExceptionCode());
4024     }
4025     _SEH2_END;
4026 
4027     //
4028     // Return status
4029     //
4030     return STATUS_SUCCESS;
4031 }
4032 
4033 NTSTATUS
4034 NTAPI
NtFlushVirtualMemory(IN HANDLE ProcessHandle,IN OUT PVOID * BaseAddress,IN OUT PSIZE_T NumberOfBytesToFlush,OUT PIO_STATUS_BLOCK IoStatusBlock)4035 NtFlushVirtualMemory(IN HANDLE ProcessHandle,
4036                      IN OUT PVOID *BaseAddress,
4037                      IN OUT PSIZE_T NumberOfBytesToFlush,
4038                      OUT PIO_STATUS_BLOCK IoStatusBlock)
4039 {
4040     PEPROCESS Process;
4041     NTSTATUS Status;
4042     KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
4043     PVOID CapturedBaseAddress;
4044     SIZE_T CapturedBytesToFlush;
4045     IO_STATUS_BLOCK LocalStatusBlock;
4046     PAGED_CODE();
4047 
4048     //
4049     // Check if we came from user mode
4050     //
4051     if (PreviousMode != KernelMode)
4052     {
4053         //
4054         // Enter SEH for probing
4055         //
4056         _SEH2_TRY
4057         {
4058             //
4059             // Validate all outputs
4060             //
4061             ProbeForWritePointer(BaseAddress);
4062             ProbeForWriteSize_t(NumberOfBytesToFlush);
4063             ProbeForWriteIoStatusBlock(IoStatusBlock);
4064 
4065             //
4066             // Capture them
4067             //
4068             CapturedBaseAddress = *BaseAddress;
4069             CapturedBytesToFlush = *NumberOfBytesToFlush;
4070         }
4071         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
4072         {
4073             //
4074             // Get exception code
4075             //
4076             _SEH2_YIELD(return _SEH2_GetExceptionCode());
4077         }
4078         _SEH2_END;
4079     }
4080     else
4081     {
4082         //
4083         // Capture directly
4084         //
4085         CapturedBaseAddress = *BaseAddress;
4086         CapturedBytesToFlush = *NumberOfBytesToFlush;
4087     }
4088 
4089     //
4090     // Catch illegal base address
4091     //
4092     if (CapturedBaseAddress > MM_HIGHEST_USER_ADDRESS) return STATUS_INVALID_PARAMETER;
4093 
4094     //
4095     // Catch illegal region size
4096     //
4097     if ((MmUserProbeAddress - (ULONG_PTR)CapturedBaseAddress) < CapturedBytesToFlush)
4098     {
4099         //
4100         // Fail
4101         //
4102         return STATUS_INVALID_PARAMETER;
4103     }
4104 
4105     //
4106     // Get a reference to the process
4107     //
4108     Status = ObReferenceObjectByHandle(ProcessHandle,
4109                                        PROCESS_VM_OPERATION,
4110                                        PsProcessType,
4111                                        PreviousMode,
4112                                        (PVOID*)(&Process),
4113                                        NULL);
4114     if (!NT_SUCCESS(Status)) return Status;
4115 
4116     //
4117     // Do it
4118     //
4119     Status = MmFlushVirtualMemory(Process,
4120                                   &CapturedBaseAddress,
4121                                   &CapturedBytesToFlush,
4122                                   &LocalStatusBlock);
4123 
4124     //
4125     // Release reference
4126     //
4127     ObDereferenceObject(Process);
4128 
4129     //
4130     // Enter SEH to return data
4131     //
4132     _SEH2_TRY
4133     {
4134         //
4135         // Return data to user
4136         //
4137         *BaseAddress = PAGE_ALIGN(CapturedBaseAddress);
4138         *NumberOfBytesToFlush = 0;
4139         *IoStatusBlock = LocalStatusBlock;
4140     }
4141     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
4142     {
4143     }
4144     _SEH2_END;
4145 
4146     //
4147     // Return status
4148     //
4149     return Status;
4150 }
4151 
4152 /*
4153  * @unimplemented
4154  */
4155 NTSTATUS
4156 NTAPI
NtGetWriteWatch(IN HANDLE ProcessHandle,IN ULONG Flags,IN PVOID BaseAddress,IN SIZE_T RegionSize,IN PVOID * UserAddressArray,OUT PULONG_PTR EntriesInUserAddressArray,OUT PULONG Granularity)4157 NtGetWriteWatch(IN HANDLE ProcessHandle,
4158                 IN ULONG Flags,
4159                 IN PVOID BaseAddress,
4160                 IN SIZE_T RegionSize,
4161                 IN PVOID *UserAddressArray,
4162                 OUT PULONG_PTR EntriesInUserAddressArray,
4163                 OUT PULONG Granularity)
4164 {
4165     PEPROCESS Process;
4166     NTSTATUS Status;
4167     PVOID EndAddress;
4168     KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
4169     ULONG_PTR CapturedEntryCount;
4170     PAGED_CODE();
4171 
4172     //
4173     // Check if we came from user mode
4174     //
4175     if (PreviousMode != KernelMode)
4176     {
4177         //
4178         // Enter SEH for probing
4179         //
4180         _SEH2_TRY
4181         {
4182             //
4183             // Catch illegal base address
4184             //
4185             if (BaseAddress > MM_HIGHEST_USER_ADDRESS) _SEH2_YIELD(return STATUS_INVALID_PARAMETER_2);
4186 
4187             //
4188             // Catch illegal region size
4189             //
4190             if ((MmUserProbeAddress - (ULONG_PTR)BaseAddress) < RegionSize)
4191             {
4192                 //
4193                 // Fail
4194                 //
4195                 _SEH2_YIELD(return STATUS_INVALID_PARAMETER_3);
4196             }
4197 
4198             //
4199             // Validate all data
4200             //
4201             ProbeForWriteSize_t(EntriesInUserAddressArray);
4202             ProbeForWriteUlong(Granularity);
4203 
4204             //
4205             // Capture them
4206             //
4207             CapturedEntryCount = *EntriesInUserAddressArray;
4208 
4209             //
4210             // Must have a count
4211             //
4212             if (CapturedEntryCount == 0) _SEH2_YIELD(return STATUS_INVALID_PARAMETER_5);
4213 
4214             //
4215             // Can't be larger than the maximum
4216             //
4217             if (CapturedEntryCount > (MAXULONG_PTR / sizeof(ULONG_PTR)))
4218             {
4219                 //
4220                 // Fail
4221                 //
4222                 _SEH2_YIELD(return STATUS_INVALID_PARAMETER_5);
4223             }
4224 
4225             //
4226             // Probe the actual array
4227             //
4228             ProbeForWrite(UserAddressArray,
4229                           CapturedEntryCount * sizeof(PVOID),
4230                           sizeof(PVOID));
4231         }
4232         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
4233         {
4234             //
4235             // Get exception code
4236             //
4237             _SEH2_YIELD(return _SEH2_GetExceptionCode());
4238         }
4239         _SEH2_END;
4240     }
4241     else
4242     {
4243         //
4244         // Capture directly
4245         //
4246         CapturedEntryCount = *EntriesInUserAddressArray;
4247         ASSERT(CapturedEntryCount != 0);
4248     }
4249 
4250     //
4251     // Check if this is a local request
4252     //
4253     if (ProcessHandle == NtCurrentProcess())
4254     {
4255         //
4256         // No need to reference the process
4257         //
4258         Process = PsGetCurrentProcess();
4259     }
4260     else
4261     {
4262         //
4263         // Reference the target
4264         //
4265         Status = ObReferenceObjectByHandle(ProcessHandle,
4266                                            PROCESS_VM_OPERATION,
4267                                            PsProcessType,
4268                                            PreviousMode,
4269                                            (PVOID *)&Process,
4270                                            NULL);
4271         if (!NT_SUCCESS(Status)) return Status;
4272     }
4273 
4274     //
4275     // Compute the last address and validate it
4276     //
4277     EndAddress = (PVOID)((ULONG_PTR)BaseAddress + RegionSize - 1);
4278     if (BaseAddress > EndAddress)
4279     {
4280         //
4281         // Fail
4282         //
4283         if (ProcessHandle != NtCurrentProcess()) ObDereferenceObject(Process);
4284         return STATUS_INVALID_PARAMETER_4;
4285     }
4286 
4287     //
4288     // Oops :(
4289     //
4290     UNIMPLEMENTED;
4291 
4292     //
4293     // Dereference if needed
4294     //
4295     if (ProcessHandle != NtCurrentProcess()) ObDereferenceObject(Process);
4296 
4297     //
4298     // Enter SEH to return data
4299     //
4300     _SEH2_TRY
4301     {
4302         //
4303         // Return data to user
4304         //
4305         *EntriesInUserAddressArray = 0;
4306         *Granularity = PAGE_SIZE;
4307     }
4308     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
4309     {
4310         //
4311         // Get exception code
4312         //
4313         Status = _SEH2_GetExceptionCode();
4314     }
4315     _SEH2_END;
4316 
4317     //
4318     // Return success
4319     //
4320     return STATUS_SUCCESS;
4321 }
4322 
4323 /*
4324  * @unimplemented
4325  */
4326 NTSTATUS
4327 NTAPI
NtResetWriteWatch(IN HANDLE ProcessHandle,IN PVOID BaseAddress,IN SIZE_T RegionSize)4328 NtResetWriteWatch(IN HANDLE ProcessHandle,
4329                   IN PVOID BaseAddress,
4330                   IN SIZE_T RegionSize)
4331 {
4332     PVOID EndAddress;
4333     PEPROCESS Process;
4334     NTSTATUS Status;
4335     KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
4336     ASSERT (KeGetCurrentIrql() == PASSIVE_LEVEL);
4337 
4338     //
4339     // Catch illegal base address
4340     //
4341     if (BaseAddress > MM_HIGHEST_USER_ADDRESS) return STATUS_INVALID_PARAMETER_2;
4342 
4343     //
4344     // Catch illegal region size
4345     //
4346     if ((MmUserProbeAddress - (ULONG_PTR)BaseAddress) < RegionSize)
4347     {
4348         //
4349         // Fail
4350         //
4351         return STATUS_INVALID_PARAMETER_3;
4352     }
4353 
4354     //
4355     // Check if this is a local request
4356     //
4357     if (ProcessHandle == NtCurrentProcess())
4358     {
4359         //
4360         // No need to reference the process
4361         //
4362         Process = PsGetCurrentProcess();
4363     }
4364     else
4365     {
4366         //
4367         // Reference the target
4368         //
4369         Status = ObReferenceObjectByHandle(ProcessHandle,
4370                                            PROCESS_VM_OPERATION,
4371                                            PsProcessType,
4372                                            PreviousMode,
4373                                            (PVOID *)&Process,
4374                                            NULL);
4375         if (!NT_SUCCESS(Status)) return Status;
4376     }
4377 
4378     //
4379     // Compute the last address and validate it
4380     //
4381     EndAddress = (PVOID)((ULONG_PTR)BaseAddress + RegionSize - 1);
4382     if (BaseAddress > EndAddress)
4383     {
4384         //
4385         // Fail
4386         //
4387         if (ProcessHandle != NtCurrentProcess()) ObDereferenceObject(Process);
4388         return STATUS_INVALID_PARAMETER_3;
4389     }
4390 
4391     //
4392     // Oops :(
4393     //
4394     UNIMPLEMENTED;
4395 
4396     //
4397     // Dereference if needed
4398     //
4399     if (ProcessHandle != NtCurrentProcess()) ObDereferenceObject(Process);
4400 
4401     //
4402     // Return success
4403     //
4404     return STATUS_SUCCESS;
4405 }
4406 
4407 NTSTATUS
4408 NTAPI
NtQueryVirtualMemory(IN HANDLE ProcessHandle,IN PVOID BaseAddress,IN MEMORY_INFORMATION_CLASS MemoryInformationClass,OUT PVOID MemoryInformation,IN SIZE_T MemoryInformationLength,OUT PSIZE_T ReturnLength)4409 NtQueryVirtualMemory(IN HANDLE ProcessHandle,
4410                      IN PVOID BaseAddress,
4411                      IN MEMORY_INFORMATION_CLASS MemoryInformationClass,
4412                      OUT PVOID MemoryInformation,
4413                      IN SIZE_T MemoryInformationLength,
4414                      OUT PSIZE_T ReturnLength)
4415 {
4416     NTSTATUS Status = STATUS_SUCCESS;
4417     KPROCESSOR_MODE PreviousMode;
4418 
4419     DPRINT("Querying class %d about address: %p\n", MemoryInformationClass, BaseAddress);
4420 
4421     /* Bail out if the address is invalid */
4422     if (BaseAddress > MM_HIGHEST_USER_ADDRESS) return STATUS_INVALID_PARAMETER;
4423 
4424     /* Probe return buffer */
4425     PreviousMode =  ExGetPreviousMode();
4426     if (PreviousMode != KernelMode)
4427     {
4428         _SEH2_TRY
4429         {
4430             ProbeForWrite(MemoryInformation,
4431                           MemoryInformationLength,
4432                           sizeof(ULONG_PTR));
4433 
4434             if (ReturnLength) ProbeForWriteSize_t(ReturnLength);
4435         }
4436         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
4437         {
4438             Status = _SEH2_GetExceptionCode();
4439         }
4440         _SEH2_END;
4441 
4442         if (!NT_SUCCESS(Status))
4443         {
4444             return Status;
4445         }
4446     }
4447 
4448     switch(MemoryInformationClass)
4449     {
4450         case MemoryBasicInformation:
4451             /* Validate the size information of the class */
4452             if (MemoryInformationLength < sizeof(MEMORY_BASIC_INFORMATION))
4453             {
4454                 /* The size is invalid */
4455                 return STATUS_INFO_LENGTH_MISMATCH;
4456             }
4457             Status = MiQueryMemoryBasicInformation(ProcessHandle,
4458                                                    BaseAddress,
4459                                                    MemoryInformation,
4460                                                    MemoryInformationLength,
4461                                                    ReturnLength);
4462             break;
4463 
4464         case MemorySectionName:
4465             /* Validate the size information of the class */
4466             if (MemoryInformationLength < sizeof(MEMORY_SECTION_NAME))
4467             {
4468                 /* The size is invalid */
4469                 return STATUS_INFO_LENGTH_MISMATCH;
4470             }
4471             Status = MiQueryMemorySectionName(ProcessHandle,
4472                                               BaseAddress,
4473                                               MemoryInformation,
4474                                               MemoryInformationLength,
4475                                               ReturnLength);
4476             break;
4477         case MemoryWorkingSetList:
4478         case MemoryBasicVlmInformation:
4479         default:
4480             DPRINT1("Unhandled memory information class %d\n", MemoryInformationClass);
4481             break;
4482     }
4483 
4484     return Status;
4485 }
4486 
4487 /*
4488  * @implemented
4489  */
4490 NTSTATUS
4491 NTAPI
NtAllocateVirtualMemory(IN HANDLE ProcessHandle,IN OUT PVOID * UBaseAddress,IN ULONG_PTR ZeroBits,IN OUT PSIZE_T URegionSize,IN ULONG AllocationType,IN ULONG Protect)4492 NtAllocateVirtualMemory(IN HANDLE ProcessHandle,
4493                         IN OUT PVOID* UBaseAddress,
4494                         IN ULONG_PTR ZeroBits,
4495                         IN OUT PSIZE_T URegionSize,
4496                         IN ULONG AllocationType,
4497                         IN ULONG Protect)
4498 {
4499     PEPROCESS Process;
4500     PMEMORY_AREA MemoryArea;
4501     PMMVAD Vad = NULL, FoundVad;
4502     NTSTATUS Status;
4503     PMMSUPPORT AddressSpace;
4504     PVOID PBaseAddress;
4505     ULONG_PTR PRegionSize, StartingAddress, EndingAddress;
4506     ULONG_PTR HighestAddress = (ULONG_PTR)MM_HIGHEST_VAD_ADDRESS;
4507     PEPROCESS CurrentProcess = PsGetCurrentProcess();
4508     KPROCESSOR_MODE PreviousMode = KeGetPreviousMode();
4509     PETHREAD CurrentThread = PsGetCurrentThread();
4510     KAPC_STATE ApcState;
4511     ULONG ProtectionMask, QuotaCharge = 0, QuotaFree = 0;
4512     BOOLEAN Attached = FALSE, ChangeProtection = FALSE, QuotaCharged = FALSE;
4513     MMPTE TempPte;
4514     PMMPTE PointerPte, LastPte;
4515     PMMPDE PointerPde;
4516     TABLE_SEARCH_RESULT Result;
4517     PAGED_CODE();
4518 
4519     /* Check for valid Zero bits */
4520     if (ZeroBits > MI_MAX_ZERO_BITS)
4521     {
4522         DPRINT1("Too many zero bits\n");
4523         return STATUS_INVALID_PARAMETER_3;
4524     }
4525 
4526     /* Check for valid Allocation Types */
4527     if ((AllocationType & ~(MEM_COMMIT | MEM_RESERVE | MEM_RESET | MEM_PHYSICAL |
4528                     MEM_TOP_DOWN | MEM_WRITE_WATCH | MEM_LARGE_PAGES)))
4529     {
4530         DPRINT1("Invalid Allocation Type\n");
4531         return STATUS_INVALID_PARAMETER_5;
4532     }
4533 
4534     /* Check for at least one of these Allocation Types to be set */
4535     if (!(AllocationType & (MEM_COMMIT | MEM_RESERVE | MEM_RESET)))
4536     {
4537         DPRINT1("No memory allocation base type\n");
4538         return STATUS_INVALID_PARAMETER_5;
4539     }
4540 
4541     /* MEM_RESET is an exclusive flag, make sure that is valid too */
4542     if ((AllocationType & MEM_RESET) && (AllocationType != MEM_RESET))
4543     {
4544         DPRINT1("Invalid use of MEM_RESET\n");
4545         return STATUS_INVALID_PARAMETER_5;
4546     }
4547 
4548     /* Check if large pages are being used */
4549     if (AllocationType & MEM_LARGE_PAGES)
4550     {
4551         /* Large page allocations MUST be committed */
4552         if (!(AllocationType & MEM_COMMIT))
4553         {
4554             DPRINT1("Must supply MEM_COMMIT with MEM_LARGE_PAGES\n");
4555             return STATUS_INVALID_PARAMETER_5;
4556         }
4557 
4558         /* These flags are not allowed with large page allocations */
4559         if (AllocationType & (MEM_PHYSICAL | MEM_RESET | MEM_WRITE_WATCH))
4560         {
4561             DPRINT1("Using illegal flags with MEM_LARGE_PAGES\n");
4562             return STATUS_INVALID_PARAMETER_5;
4563         }
4564     }
4565 
4566     /* MEM_WRITE_WATCH can only be used if MEM_RESERVE is also used */
4567     if ((AllocationType & MEM_WRITE_WATCH) && !(AllocationType & MEM_RESERVE))
4568     {
4569         DPRINT1("MEM_WRITE_WATCH used without MEM_RESERVE\n");
4570         return STATUS_INVALID_PARAMETER_5;
4571     }
4572 
4573     /* Check for valid MEM_PHYSICAL usage */
4574     if (AllocationType & MEM_PHYSICAL)
4575     {
4576         /* MEM_PHYSICAL can only be used if MEM_RESERVE is also used */
4577         if (!(AllocationType & MEM_RESERVE))
4578         {
4579             DPRINT1("MEM_PHYSICAL used without MEM_RESERVE\n");
4580             return STATUS_INVALID_PARAMETER_5;
4581         }
4582 
4583         /* Only these flags are allowed with MEM_PHYSIAL */
4584         if (AllocationType & ~(MEM_RESERVE | MEM_TOP_DOWN | MEM_PHYSICAL))
4585         {
4586             DPRINT1("Using illegal flags with MEM_PHYSICAL\n");
4587             return STATUS_INVALID_PARAMETER_5;
4588         }
4589 
4590         /* Then make sure PAGE_READWRITE is used */
4591         if (Protect != PAGE_READWRITE)
4592         {
4593             DPRINT1("MEM_PHYSICAL used without PAGE_READWRITE\n");
4594             return STATUS_INVALID_PARAMETER_6;
4595         }
4596     }
4597 
4598     /* Calculate the protection mask and make sure it's valid */
4599     ProtectionMask = MiMakeProtectionMask(Protect);
4600     if (ProtectionMask == MM_INVALID_PROTECTION)
4601     {
4602         DPRINT1("Invalid protection mask\n");
4603         return STATUS_INVALID_PAGE_PROTECTION;
4604     }
4605 
4606     /* Enter SEH */
4607     _SEH2_TRY
4608     {
4609         /* Check for user-mode parameters */
4610         if (PreviousMode != KernelMode)
4611         {
4612             /* Make sure they are writable */
4613             ProbeForWritePointer(UBaseAddress);
4614             ProbeForWriteSize_t(URegionSize);
4615         }
4616 
4617         /* Capture their values */
4618         PBaseAddress = *UBaseAddress;
4619         PRegionSize = *URegionSize;
4620     }
4621     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
4622     {
4623         /* Return the exception code */
4624         _SEH2_YIELD(return _SEH2_GetExceptionCode());
4625     }
4626     _SEH2_END;
4627 
4628     /* Make sure the allocation isn't past the VAD area */
4629     if (PBaseAddress > MM_HIGHEST_VAD_ADDRESS)
4630     {
4631         DPRINT1("Virtual allocation base above User Space\n");
4632         return STATUS_INVALID_PARAMETER_2;
4633     }
4634 
4635     /* Make sure the allocation wouldn't overflow past the VAD area */
4636     if ((((ULONG_PTR)MM_HIGHEST_VAD_ADDRESS + 1) - (ULONG_PTR)PBaseAddress) < PRegionSize)
4637     {
4638         DPRINT1("Region size would overflow into kernel-memory\n");
4639         return STATUS_INVALID_PARAMETER_4;
4640     }
4641 
4642     /* Make sure there's a size specified */
4643     if (!PRegionSize)
4644     {
4645         DPRINT1("Region size is invalid (zero)\n");
4646         return STATUS_INVALID_PARAMETER_4;
4647     }
4648 
4649     //
4650     // If this is for the current process, just use PsGetCurrentProcess
4651     //
4652     if (ProcessHandle == NtCurrentProcess())
4653     {
4654         Process = CurrentProcess;
4655     }
4656     else
4657     {
4658         //
4659         // Otherwise, reference the process with VM rights and attach to it if
4660         // this isn't the current process. We must attach because we'll be touching
4661         // PTEs and PDEs that belong to user-mode memory, and also touching the
4662         // Working Set which is stored in Hyperspace.
4663         //
4664         Status = ObReferenceObjectByHandle(ProcessHandle,
4665                                            PROCESS_VM_OPERATION,
4666                                            PsProcessType,
4667                                            PreviousMode,
4668                                            (PVOID*)&Process,
4669                                            NULL);
4670         if (!NT_SUCCESS(Status)) return Status;
4671         if (CurrentProcess != Process)
4672         {
4673             KeStackAttachProcess(&Process->Pcb, &ApcState);
4674             Attached = TRUE;
4675         }
4676     }
4677 
4678     DPRINT("NtAllocateVirtualMemory: Process 0x%p, Address 0x%p, Zerobits %lu , RegionSize 0x%x, Allocation type 0x%x, Protect 0x%x.\n",
4679         Process, PBaseAddress, ZeroBits, PRegionSize, AllocationType, Protect);
4680 
4681     //
4682     // Check for large page allocations and make sure that the required privilege
4683     // is being held, before attempting to handle them.
4684     //
4685     if ((AllocationType & MEM_LARGE_PAGES) &&
4686         !(SeSinglePrivilegeCheck(SeLockMemoryPrivilege, PreviousMode)))
4687     {
4688         /* Fail without it */
4689         DPRINT1("Privilege not held for MEM_LARGE_PAGES\n");
4690         Status = STATUS_PRIVILEGE_NOT_HELD;
4691         goto FailPathNoLock;
4692     }
4693 
4694     //
4695     // Fail on the things we don't yet support
4696     //
4697     if ((AllocationType & MEM_LARGE_PAGES) == MEM_LARGE_PAGES)
4698     {
4699         DPRINT1("MEM_LARGE_PAGES not supported\n");
4700         Status = STATUS_INVALID_PARAMETER;
4701         goto FailPathNoLock;
4702     }
4703     if ((AllocationType & MEM_PHYSICAL) == MEM_PHYSICAL)
4704     {
4705         DPRINT1("MEM_PHYSICAL not supported\n");
4706         Status = STATUS_INVALID_PARAMETER;
4707         goto FailPathNoLock;
4708     }
4709     if ((AllocationType & MEM_WRITE_WATCH) == MEM_WRITE_WATCH)
4710     {
4711         DPRINT1("MEM_WRITE_WATCH not supported\n");
4712         Status = STATUS_INVALID_PARAMETER;
4713         goto FailPathNoLock;
4714     }
4715 
4716     //
4717     // Check if the caller is reserving memory, or committing memory and letting
4718     // us pick the base address
4719     //
4720     if (!(PBaseAddress) || (AllocationType & MEM_RESERVE))
4721     {
4722         //
4723         //  Do not allow COPY_ON_WRITE through this API
4724         //
4725         if (Protect & (PAGE_WRITECOPY | PAGE_EXECUTE_WRITECOPY))
4726         {
4727             DPRINT1("Copy on write not allowed through this path\n");
4728             Status = STATUS_INVALID_PAGE_PROTECTION;
4729             goto FailPathNoLock;
4730         }
4731 
4732         //
4733         // Does the caller have an address in mind, or is this a blind commit?
4734         //
4735         if (!PBaseAddress)
4736         {
4737             //
4738             // This is a blind commit, all we need is the region size
4739             //
4740             PRegionSize = ROUND_TO_PAGES(PRegionSize);
4741             EndingAddress = 0;
4742             StartingAddress = 0;
4743 
4744             //
4745             // Check if ZeroBits were specified
4746             //
4747             if (ZeroBits != 0)
4748             {
4749                 //
4750                 // Calculate the highest address and check if it's valid
4751                 //
4752                 HighestAddress = MAXULONG_PTR >> ZeroBits;
4753                 if (HighestAddress > (ULONG_PTR)MM_HIGHEST_VAD_ADDRESS)
4754                 {
4755                     Status = STATUS_INVALID_PARAMETER_3;
4756                     goto FailPathNoLock;
4757                 }
4758             }
4759         }
4760         else
4761         {
4762             //
4763             // This is a reservation, so compute the starting address on the
4764             // expected 64KB granularity, and see where the ending address will
4765             // fall based on the aligned address and the passed in region size
4766             //
4767             EndingAddress = ((ULONG_PTR)PBaseAddress + PRegionSize - 1) | (PAGE_SIZE - 1);
4768             PRegionSize = EndingAddress + 1 - ROUND_DOWN((ULONG_PTR)PBaseAddress, _64K);
4769             StartingAddress = (ULONG_PTR)PBaseAddress;
4770         }
4771 
4772         // Charge quotas for the VAD
4773         Status = PsChargeProcessNonPagedPoolQuota(Process, sizeof(MMVAD_LONG));
4774         if (!NT_SUCCESS(Status))
4775         {
4776             DPRINT1("Quota exceeded.\n");
4777             goto FailPathNoLock;
4778         }
4779 
4780         QuotaCharged = TRUE;
4781 
4782         //
4783         // Allocate and initialize the VAD
4784         //
4785         Vad = ExAllocatePoolWithTag(NonPagedPool, sizeof(MMVAD_LONG), 'SdaV');
4786         if (Vad == NULL)
4787         {
4788             DPRINT1("Failed to allocate a VAD!\n");
4789             Status = STATUS_INSUFFICIENT_RESOURCES;
4790             goto FailPathNoLock;
4791         }
4792 
4793         RtlZeroMemory(Vad, sizeof(MMVAD_LONG));
4794         if (AllocationType & MEM_COMMIT) Vad->u.VadFlags.MemCommit = 1;
4795         Vad->u.VadFlags.Protection = ProtectionMask;
4796         Vad->u.VadFlags.PrivateMemory = 1;
4797         Vad->ControlArea = NULL; // For Memory-Area hack
4798 
4799         //
4800         // Insert the VAD
4801         //
4802         Status = MiInsertVadEx(Vad,
4803                                &StartingAddress,
4804                                PRegionSize,
4805                                HighestAddress,
4806                                MM_VIRTMEM_GRANULARITY,
4807                                AllocationType);
4808         if (!NT_SUCCESS(Status))
4809         {
4810             DPRINT1("Failed to insert the VAD!\n");
4811             ExFreePoolWithTag(Vad, 'SdaV');
4812             goto FailPathNoLock;
4813         }
4814 
4815         //
4816         // Detach and dereference the target process if
4817         // it was different from the current process
4818         //
4819         if (Attached) KeUnstackDetachProcess(&ApcState);
4820         if (ProcessHandle != NtCurrentProcess()) ObDereferenceObject(Process);
4821 
4822         //
4823         // Use SEH to write back the base address and the region size. In the case
4824         // of an exception, we do not return back the exception code, as the memory
4825         // *has* been allocated. The caller would now have to call VirtualQuery
4826         // or do some other similar trick to actually find out where its memory
4827         // allocation ended up
4828         //
4829         _SEH2_TRY
4830         {
4831             *URegionSize = PRegionSize;
4832             *UBaseAddress = (PVOID)StartingAddress;
4833         }
4834         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
4835         {
4836             //
4837             // Ignore exception!
4838             //
4839         }
4840         _SEH2_END;
4841         DPRINT("Reserved %x bytes at %p.\n", PRegionSize, StartingAddress);
4842         return STATUS_SUCCESS;
4843     }
4844 
4845     //
4846     // This is a MEM_COMMIT on top of an existing address which must have been
4847     // MEM_RESERVED already. Compute the start and ending base addresses based
4848     // on the user input, and then compute the actual region size once all the
4849     // alignments have been done.
4850     //
4851     EndingAddress = (((ULONG_PTR)PBaseAddress + PRegionSize - 1) | (PAGE_SIZE - 1));
4852     StartingAddress = (ULONG_PTR)PAGE_ALIGN(PBaseAddress);
4853     PRegionSize = EndingAddress - StartingAddress + 1;
4854 
4855     //
4856     // Lock the address space and make sure the process isn't already dead
4857     //
4858     AddressSpace = MmGetCurrentAddressSpace();
4859     MmLockAddressSpace(AddressSpace);
4860     if (Process->VmDeleted)
4861     {
4862         DPRINT1("Process is dying\n");
4863         Status = STATUS_PROCESS_IS_TERMINATING;
4864         goto FailPath;
4865     }
4866 
4867     //
4868     // Get the VAD for this address range, and make sure it exists
4869     //
4870     Result = MiCheckForConflictingNode(StartingAddress >> PAGE_SHIFT,
4871                                        EndingAddress >> PAGE_SHIFT,
4872                                        &Process->VadRoot,
4873                                        (PMMADDRESS_NODE*)&FoundVad);
4874     if (Result != TableFoundNode)
4875     {
4876         DPRINT1("Could not find a VAD for this allocation\n");
4877         Status = STATUS_CONFLICTING_ADDRESSES;
4878         goto FailPath;
4879     }
4880 
4881     if ((AllocationType & MEM_RESET) == MEM_RESET)
4882     {
4883         /// @todo HACK: pretend success
4884         DPRINT("MEM_RESET not supported\n");
4885         Status = STATUS_SUCCESS;
4886         goto FailPath;
4887     }
4888 
4889     //
4890     // These kinds of VADs are illegal for this Windows function when trying to
4891     // commit an existing range
4892     //
4893     if ((FoundVad->u.VadFlags.VadType == VadAwe) ||
4894         (FoundVad->u.VadFlags.VadType == VadDevicePhysicalMemory) ||
4895         (FoundVad->u.VadFlags.VadType == VadLargePages))
4896     {
4897         DPRINT1("Illegal VAD for attempting a MEM_COMMIT\n");
4898         Status = STATUS_CONFLICTING_ADDRESSES;
4899         goto FailPath;
4900     }
4901 
4902     //
4903     // Make sure that this address range actually fits within the VAD for it
4904     //
4905     if (((StartingAddress >> PAGE_SHIFT) < FoundVad->StartingVpn) ||
4906         ((EndingAddress >> PAGE_SHIFT) > FoundVad->EndingVpn))
4907     {
4908         DPRINT1("Address range does not fit into the VAD\n");
4909         Status = STATUS_CONFLICTING_ADDRESSES;
4910         goto FailPath;
4911     }
4912 
4913     //
4914     // Make sure this is an ARM3 section
4915     //
4916     MemoryArea = MmLocateMemoryAreaByAddress(AddressSpace, (PVOID)PAGE_ROUND_DOWN(PBaseAddress));
4917     ASSERT(MemoryArea != NULL);
4918     if (MemoryArea->Type != MEMORY_AREA_OWNED_BY_ARM3)
4919     {
4920         DPRINT1("Illegal commit of non-ARM3 section!\n");
4921         Status = STATUS_ALREADY_COMMITTED;
4922         goto FailPath;
4923     }
4924 
4925     // Is this a previously reserved section being committed? If so, enter the
4926     // special section path
4927     //
4928     if (FoundVad->u.VadFlags.PrivateMemory == FALSE)
4929     {
4930         //
4931         // You cannot commit large page sections through this API
4932         //
4933         if (FoundVad->u.VadFlags.VadType == VadLargePageSection)
4934         {
4935             DPRINT1("Large page sections cannot be VirtualAlloc'd\n");
4936             Status = STATUS_INVALID_PAGE_PROTECTION;
4937             goto FailPath;
4938         }
4939 
4940         //
4941         // You can only use caching flags on a rotate VAD
4942         //
4943         if ((Protect & (PAGE_NOCACHE | PAGE_WRITECOMBINE)) &&
4944             (FoundVad->u.VadFlags.VadType != VadRotatePhysical))
4945         {
4946             DPRINT1("Cannot use caching flags with anything but rotate VADs\n");
4947             Status = STATUS_INVALID_PAGE_PROTECTION;
4948             goto FailPath;
4949         }
4950 
4951         //
4952         // We should make sure that the section's permissions aren't being
4953         // messed with
4954         //
4955         if (FoundVad->u.VadFlags.NoChange)
4956         {
4957             //
4958             // Make sure it's okay to touch it
4959             // Note: The Windows 2003 kernel has a bug here, passing the
4960             // unaligned base address together with the aligned size,
4961             // potentially covering a region larger than the actual allocation.
4962             // Might be exposed through NtGdiCreateDIBSection w/ section handle
4963             // For now we keep this behavior.
4964             // TODO: analyze possible implications, create test case
4965             //
4966             Status = MiCheckSecuredVad(FoundVad,
4967                                        PBaseAddress,
4968                                        PRegionSize,
4969                                        ProtectionMask);
4970             if (!NT_SUCCESS(Status))
4971             {
4972                 DPRINT1("Secured VAD being messed around with\n");
4973                 goto FailPath;
4974             }
4975         }
4976 
4977         //
4978         // ARM3 does not support file-backed sections, only shared memory
4979         //
4980         ASSERT(FoundVad->ControlArea->FilePointer == NULL);
4981 
4982         //
4983         // Rotate VADs cannot be guard pages or inaccessible, nor copy on write
4984         //
4985         if ((FoundVad->u.VadFlags.VadType == VadRotatePhysical) &&
4986             (Protect & (PAGE_WRITECOPY | PAGE_EXECUTE_WRITECOPY | PAGE_NOACCESS | PAGE_GUARD)))
4987         {
4988             DPRINT1("Invalid page protection for rotate VAD\n");
4989             Status = STATUS_INVALID_PAGE_PROTECTION;
4990             goto FailPath;
4991         }
4992 
4993         //
4994         // Compute PTE addresses and the quota charge, then grab the commit lock
4995         //
4996         PointerPte = MI_GET_PROTOTYPE_PTE_FOR_VPN(FoundVad, StartingAddress >> PAGE_SHIFT);
4997         LastPte = MI_GET_PROTOTYPE_PTE_FOR_VPN(FoundVad, EndingAddress >> PAGE_SHIFT);
4998         QuotaCharge = (ULONG)(LastPte - PointerPte + 1);
4999         KeAcquireGuardedMutexUnsafe(&MmSectionCommitMutex);
5000 
5001         //
5002         // Get the segment template PTE and start looping each page
5003         //
5004         TempPte = FoundVad->ControlArea->Segment->SegmentPteTemplate;
5005         ASSERT(TempPte.u.Long != 0);
5006         while (PointerPte <= LastPte)
5007         {
5008             //
5009             // For each non-already-committed page, write the invalid template PTE
5010             //
5011             if (PointerPte->u.Long == 0)
5012             {
5013                 MI_WRITE_INVALID_PTE(PointerPte, TempPte);
5014             }
5015             else
5016             {
5017                 QuotaFree++;
5018             }
5019             PointerPte++;
5020         }
5021 
5022         //
5023         // Now do the commit accounting and release the lock
5024         //
5025         ASSERT(QuotaCharge >= QuotaFree);
5026         QuotaCharge -= QuotaFree;
5027         FoundVad->ControlArea->Segment->NumberOfCommittedPages += QuotaCharge;
5028         KeReleaseGuardedMutexUnsafe(&MmSectionCommitMutex);
5029 
5030         //
5031         // We are done with committing the section pages
5032         //
5033         Status = STATUS_SUCCESS;
5034         goto FailPath;
5035     }
5036 
5037     //
5038     // This is a specific ReactOS check because we only use normal VADs
5039     //
5040     ASSERT(FoundVad->u.VadFlags.VadType == VadNone);
5041 
5042     //
5043     // While this is an actual Windows check
5044     //
5045     ASSERT(FoundVad->u.VadFlags.VadType != VadRotatePhysical);
5046 
5047     //
5048     // Throw out attempts to use copy-on-write through this API path
5049     //
5050     if ((Protect & PAGE_WRITECOPY) || (Protect & PAGE_EXECUTE_WRITECOPY))
5051     {
5052         DPRINT1("Write copy attempted when not allowed\n");
5053         Status = STATUS_INVALID_PAGE_PROTECTION;
5054         goto FailPath;
5055     }
5056 
5057     //
5058     // Initialize a demand-zero PTE
5059     //
5060     TempPte.u.Long = 0;
5061     TempPte.u.Soft.Protection = ProtectionMask;
5062     ASSERT(TempPte.u.Long != 0);
5063 
5064     //
5065     // Get the PTE, PDE and the last PTE for this address range
5066     //
5067     PointerPde = MiAddressToPde(StartingAddress);
5068     PointerPte = MiAddressToPte(StartingAddress);
5069     LastPte = MiAddressToPte(EndingAddress);
5070 
5071     //
5072     // Update the commit charge in the VAD as well as in the process, and check
5073     // if this commit charge was now higher than the last recorded peak, in which
5074     // case we also update the peak
5075     //
5076     FoundVad->u.VadFlags.CommitCharge += (1 + LastPte - PointerPte);
5077     Process->CommitCharge += (1 + LastPte - PointerPte);
5078     if (Process->CommitCharge > Process->CommitChargePeak)
5079     {
5080         Process->CommitChargePeak = Process->CommitCharge;
5081     }
5082 
5083     //
5084     // Lock the working set while we play with user pages and page tables
5085     //
5086     MiLockProcessWorkingSetUnsafe(Process, CurrentThread);
5087 
5088     //
5089     // Make the current page table valid, and then loop each page within it
5090     //
5091     MiMakePdeExistAndMakeValid(PointerPde, Process, MM_NOIRQL);
5092     while (PointerPte <= LastPte)
5093     {
5094         //
5095         // Have we crossed into a new page table?
5096         //
5097         if (MiIsPteOnPdeBoundary(PointerPte))
5098         {
5099             //
5100             // Get the PDE and now make it valid too
5101             //
5102             PointerPde = MiPteToPde(PointerPte);
5103             MiMakePdeExistAndMakeValid(PointerPde, Process, MM_NOIRQL);
5104         }
5105 
5106         //
5107         // Is this a zero PTE as expected?
5108         //
5109         if (PointerPte->u.Long == 0)
5110         {
5111             //
5112             // First increment the count of pages in the page table for this
5113             // process
5114             //
5115             MiIncrementPageTableReferences(MiPteToAddress(PointerPte));
5116 
5117             //
5118             // And now write the invalid demand-zero PTE as requested
5119             //
5120             MI_WRITE_INVALID_PTE(PointerPte, TempPte);
5121         }
5122         else if (PointerPte->u.Long == MmDecommittedPte.u.Long)
5123         {
5124             //
5125             // If the PTE was already decommitted, there is nothing else to do
5126             // but to write the new demand-zero PTE
5127             //
5128             MI_WRITE_INVALID_PTE(PointerPte, TempPte);
5129         }
5130         else if (!(ChangeProtection) && (Protect != MiGetPageProtection(PointerPte)))
5131         {
5132             //
5133             // We don't handle these scenarios yet
5134             //
5135             if (PointerPte->u.Soft.Valid == 0)
5136             {
5137                 ASSERT(PointerPte->u.Soft.Prototype == 0);
5138                 ASSERT((PointerPte->u.Soft.PageFileHigh == 0) || (PointerPte->u.Soft.Transition == 1));
5139             }
5140 
5141             //
5142             // There's a change in protection, remember this for later, but do
5143             // not yet handle it.
5144             //
5145             ChangeProtection = TRUE;
5146         }
5147 
5148         //
5149         // Move to the next PTE
5150         //
5151         PointerPte++;
5152     }
5153 
5154     //
5155     // Release the working set lock, unlock the address space, and detach from
5156     // the target process if it was not the current process. Also dereference the
5157     // target process if this wasn't the case.
5158     //
5159     MiUnlockProcessWorkingSetUnsafe(Process, CurrentThread);
5160     Status = STATUS_SUCCESS;
5161 FailPath:
5162     MmUnlockAddressSpace(AddressSpace);
5163 
5164     if (!NT_SUCCESS(Status))
5165     {
5166         if (Vad != NULL)
5167         {
5168             ExFreePoolWithTag(Vad, 'SdaV');
5169         }
5170     }
5171 
5172     //
5173     // Check if we need to update the protection
5174     //
5175     if (ChangeProtection)
5176     {
5177         PVOID ProtectBaseAddress = (PVOID)StartingAddress;
5178         SIZE_T ProtectSize = PRegionSize;
5179         ULONG OldProtection;
5180 
5181         //
5182         // Change the protection of the region
5183         //
5184         MiProtectVirtualMemory(Process,
5185                                &ProtectBaseAddress,
5186                                &ProtectSize,
5187                                Protect,
5188                                &OldProtection);
5189     }
5190 
5191 FailPathNoLock:
5192     if (Attached) KeUnstackDetachProcess(&ApcState);
5193     if (ProcessHandle != NtCurrentProcess()) ObDereferenceObject(Process);
5194 
5195     //
5196     // Only write back results on success
5197     //
5198     if (NT_SUCCESS(Status))
5199     {
5200         //
5201         // Use SEH to write back the base address and the region size. In the case
5202         // of an exception, we strangely do return back the exception code, even
5203         // though the memory *has* been allocated. This mimics Windows behavior and
5204         // there is not much we can do about it.
5205         //
5206         _SEH2_TRY
5207         {
5208             *URegionSize = PRegionSize;
5209             *UBaseAddress = (PVOID)StartingAddress;
5210         }
5211         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
5212         {
5213             Status = _SEH2_GetExceptionCode();
5214         }
5215         _SEH2_END;
5216     }
5217     else if (QuotaCharged)
5218     {
5219         PsReturnProcessNonPagedPoolQuota(Process, sizeof(MMVAD_LONG));
5220     }
5221 
5222     return Status;
5223 }
5224 
5225 /*
5226  * @implemented
5227  */
5228 NTSTATUS
5229 NTAPI
NtFreeVirtualMemory(IN HANDLE ProcessHandle,IN PVOID * UBaseAddress,IN PSIZE_T URegionSize,IN ULONG FreeType)5230 NtFreeVirtualMemory(IN HANDLE ProcessHandle,
5231                     IN PVOID* UBaseAddress,
5232                     IN PSIZE_T URegionSize,
5233                     IN ULONG FreeType)
5234 {
5235     PMEMORY_AREA MemoryArea;
5236     SIZE_T PRegionSize;
5237     PVOID PBaseAddress;
5238     LONG_PTR AlreadyDecommitted, CommitReduction = 0;
5239     LONG_PTR FirstCommit;
5240     ULONG_PTR StartingAddress, EndingAddress;
5241     PMMVAD Vad;
5242     PMMVAD NewVad;
5243     NTSTATUS Status;
5244     PEPROCESS Process;
5245     PMMSUPPORT AddressSpace;
5246     PETHREAD CurrentThread = PsGetCurrentThread();
5247     PEPROCESS CurrentProcess = PsGetCurrentProcess();
5248     KPROCESSOR_MODE PreviousMode = KeGetPreviousMode();
5249     KAPC_STATE ApcState;
5250     BOOLEAN Attached = FALSE;
5251     PAGED_CODE();
5252 
5253     //
5254     // Only two flags are supported, exclusively.
5255     //
5256     if (FreeType != MEM_RELEASE && FreeType != MEM_DECOMMIT)
5257     {
5258         DPRINT1("Invalid FreeType (0x%08lx)\n", FreeType);
5259         return STATUS_INVALID_PARAMETER_4;
5260     }
5261 
5262     //
5263     // Enter SEH for probe and capture. On failure, return back to the caller
5264     // with an exception violation.
5265     //
5266     _SEH2_TRY
5267     {
5268         //
5269         // Check for user-mode parameters and make sure that they are writeable
5270         //
5271         if (PreviousMode != KernelMode)
5272         {
5273             ProbeForWritePointer(UBaseAddress);
5274             ProbeForWriteUlong(URegionSize);
5275         }
5276 
5277         //
5278         // Capture the current values
5279         //
5280         PBaseAddress = *UBaseAddress;
5281         PRegionSize = *URegionSize;
5282     }
5283     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
5284     {
5285         _SEH2_YIELD(return _SEH2_GetExceptionCode());
5286     }
5287     _SEH2_END;
5288 
5289     //
5290     // Make sure the allocation isn't past the user area
5291     //
5292     if (PBaseAddress >= MM_HIGHEST_USER_ADDRESS)
5293     {
5294         DPRINT1("Virtual free base above User Space\n");
5295         return STATUS_INVALID_PARAMETER_2;
5296     }
5297 
5298     //
5299     // Make sure the allocation wouldn't overflow past the user area
5300     //
5301     if (((ULONG_PTR)MM_HIGHEST_USER_ADDRESS - (ULONG_PTR)PBaseAddress) < PRegionSize)
5302     {
5303         DPRINT1("Region size would overflow into kernel-memory\n");
5304         return STATUS_INVALID_PARAMETER_3;
5305     }
5306 
5307     //
5308     // If this is for the current process, just use PsGetCurrentProcess
5309     //
5310     if (ProcessHandle == NtCurrentProcess())
5311     {
5312         Process = CurrentProcess;
5313     }
5314     else
5315     {
5316         //
5317         // Otherwise, reference the process with VM rights and attach to it if
5318         // this isn't the current process. We must attach because we'll be touching
5319         // PTEs and PDEs that belong to user-mode memory, and also touching the
5320         // Working Set which is stored in Hyperspace.
5321         //
5322         Status = ObReferenceObjectByHandle(ProcessHandle,
5323                                            PROCESS_VM_OPERATION,
5324                                            PsProcessType,
5325                                            PreviousMode,
5326                                            (PVOID*)&Process,
5327                                            NULL);
5328         if (!NT_SUCCESS(Status)) return Status;
5329         if (CurrentProcess != Process)
5330         {
5331             KeStackAttachProcess(&Process->Pcb, &ApcState);
5332             Attached = TRUE;
5333         }
5334     }
5335 
5336     DPRINT("NtFreeVirtualMemory: Process 0x%p, Address 0x%p, Size 0x%Ix, FreeType 0x%08lx\n",
5337            Process, PBaseAddress, PRegionSize, FreeType);
5338 
5339     //
5340     // Lock the address space
5341     //
5342     AddressSpace = MmGetCurrentAddressSpace();
5343     MmLockAddressSpace(AddressSpace);
5344 
5345     //
5346     // If the address space is being deleted, fail the de-allocation since it's
5347     // too late to do anything about it
5348     //
5349     if (Process->VmDeleted)
5350     {
5351         DPRINT1("Process is dead\n");
5352         Status = STATUS_PROCESS_IS_TERMINATING;
5353         goto FailPath;
5354     }
5355 
5356     //
5357     // Compute start and end addresses, and locate the VAD
5358     //
5359     StartingAddress = (ULONG_PTR)PAGE_ALIGN(PBaseAddress);
5360     EndingAddress = ((ULONG_PTR)PBaseAddress + PRegionSize - 1) | (PAGE_SIZE - 1);
5361     Vad = MiLocateAddress((PVOID)StartingAddress);
5362     if (!Vad)
5363     {
5364         DPRINT1("Unable to find VAD for address 0x%p\n", StartingAddress);
5365         Status = STATUS_MEMORY_NOT_ALLOCATED;
5366         goto FailPath;
5367     }
5368 
5369     //
5370     // If the range exceeds the VAD's ending VPN, fail this request
5371     //
5372     if (Vad->EndingVpn < (EndingAddress >> PAGE_SHIFT))
5373     {
5374         DPRINT1("Address 0x%p is beyond the VAD\n", EndingAddress);
5375         Status = STATUS_UNABLE_TO_FREE_VM;
5376         goto FailPath;
5377     }
5378 
5379     //
5380     // Only private memory (except rotate VADs) can be freed through here */
5381     //
5382     if ((!(Vad->u.VadFlags.PrivateMemory) &&
5383          (Vad->u.VadFlags.VadType != VadRotatePhysical)) ||
5384         (Vad->u.VadFlags.VadType == VadDevicePhysicalMemory))
5385     {
5386         DPRINT("Attempt to free section memory\n");
5387         Status = STATUS_UNABLE_TO_DELETE_SECTION;
5388         goto FailPath;
5389     }
5390 
5391     //
5392     // ARM3 does not yet handle protected VM
5393     //
5394     ASSERT(Vad->u.VadFlags.NoChange == 0);
5395 
5396     //
5397     // Finally, make sure there is a ReactOS Mm MEMORY_AREA for this allocation
5398     // and that is is an ARM3 memory area, and not a section view, as we currently
5399     // don't support freeing those though this interface.
5400     //
5401     MemoryArea = MmLocateMemoryAreaByAddress(AddressSpace, (PVOID)StartingAddress);
5402     ASSERT(MemoryArea);
5403     ASSERT(MemoryArea->Type == MEMORY_AREA_OWNED_BY_ARM3);
5404 
5405     //
5406     //  Now we can try the operation. First check if this is a RELEASE or a DECOMMIT
5407     //
5408     if (FreeType & MEM_RELEASE)
5409     {
5410         //
5411         // ARM3 only supports this VAD in this path
5412         //
5413         ASSERT(Vad->u.VadFlags.VadType == VadNone);
5414 
5415         //
5416         // Is the caller trying to remove the whole VAD, or remove only a portion
5417         // of it? If no region size is specified, then the assumption is that the
5418         // whole VAD is to be destroyed
5419         //
5420         if (!PRegionSize)
5421         {
5422             //
5423             // The caller must specify the base address identically to the range
5424             // that is stored in the VAD.
5425             //
5426             if (((ULONG_PTR)PBaseAddress >> PAGE_SHIFT) != Vad->StartingVpn)
5427             {
5428                 DPRINT1("Address 0x%p does not match the VAD\n", PBaseAddress);
5429                 Status = STATUS_FREE_VM_NOT_AT_BASE;
5430                 goto FailPath;
5431             }
5432 
5433             //
5434             // Now compute the actual start/end addresses based on the VAD
5435             //
5436             StartingAddress = Vad->StartingVpn << PAGE_SHIFT;
5437             EndingAddress = (Vad->EndingVpn << PAGE_SHIFT) | (PAGE_SIZE - 1);
5438 
5439             //
5440             // Finally lock the working set and remove the VAD from the VAD tree
5441             //
5442             MiLockProcessWorkingSetUnsafe(Process, CurrentThread);
5443             ASSERT(Process->VadRoot.NumberGenericTableElements >= 1);
5444             MiRemoveNode((PMMADDRESS_NODE)Vad, &Process->VadRoot);
5445             PsReturnProcessNonPagedPoolQuota(Process, sizeof(MMVAD_LONG));
5446         }
5447         else
5448         {
5449             //
5450             // This means the caller wants to release a specific region within
5451             // the range. We have to find out which range this is -- the following
5452             // possibilities exist plus their union (CASE D):
5453             //
5454             // STARTING ADDRESS                                   ENDING ADDRESS
5455             // [<========][========================================][=========>]
5456             //   CASE A                  CASE B                       CASE C
5457             //
5458             //
5459             // First, check for case A or D
5460             //
5461             if ((StartingAddress >> PAGE_SHIFT) == Vad->StartingVpn)
5462             {
5463                 //
5464                 // Check for case D
5465                 //
5466                 if ((EndingAddress >> PAGE_SHIFT) == Vad->EndingVpn)
5467                 {
5468                     //
5469                     // Case D (freeing the entire region)
5470                     //
5471                     // This is the easiest one to handle -- it is identical to
5472                     // the code path above when the caller sets a zero region size
5473                     // and the whole VAD is destroyed
5474                     //
5475                     MiLockProcessWorkingSetUnsafe(Process, CurrentThread);
5476                     ASSERT(Process->VadRoot.NumberGenericTableElements >= 1);
5477                     MiRemoveNode((PMMADDRESS_NODE)Vad, &Process->VadRoot);
5478                     PsReturnProcessNonPagedPoolQuota(Process, sizeof(MMVAD_LONG));
5479                 }
5480                 else
5481                 {
5482                     //
5483                     // Case A (freeing a part at the beginning)
5484                     //
5485                     // This case is pretty easy too -- we compute a bunch of
5486                     // pages to decommit, and then push the VAD's starting address
5487                     // a bit further down, then decrement the commit charge
5488                     //
5489                     MiLockProcessWorkingSetUnsafe(Process, CurrentThread);
5490                     CommitReduction = MiCalculatePageCommitment(StartingAddress,
5491                                                                 EndingAddress,
5492                                                                 Vad,
5493                                                                 Process);
5494                     Vad->u.VadFlags.CommitCharge -= CommitReduction;
5495                     // For ReactOS: shrink the corresponding memory area
5496                     ASSERT(Vad->StartingVpn == MemoryArea->VadNode.StartingVpn);
5497                     ASSERT(Vad->EndingVpn == MemoryArea->VadNode.EndingVpn);
5498                     Vad->StartingVpn = (EndingAddress + 1) >> PAGE_SHIFT;
5499                     MemoryArea->VadNode.StartingVpn = Vad->StartingVpn;
5500 
5501                     //
5502                     // After analyzing the VAD, set it to NULL so that we don't
5503                     // free it in the exit path
5504                     //
5505                     Vad = NULL;
5506                 }
5507             }
5508             else
5509             {
5510                 //
5511                 // This is case B or case C. First check for case C
5512                 //
5513                 if ((EndingAddress >> PAGE_SHIFT) == Vad->EndingVpn)
5514                 {
5515                     //
5516                     // Case C (freeing a part at the end)
5517                     //
5518                     // This is pretty easy and similar to case A. We compute the
5519                     // amount of pages to decommit, update the VAD's commit charge
5520                     // and then change the ending address of the VAD to be a bit
5521                     // smaller.
5522                     //
5523                     MiLockProcessWorkingSetUnsafe(Process, CurrentThread);
5524                     CommitReduction = MiCalculatePageCommitment(StartingAddress,
5525                                                                 EndingAddress,
5526                                                                 Vad,
5527                                                                 Process);
5528                     Vad->u.VadFlags.CommitCharge -= CommitReduction;
5529                     // For ReactOS: shrink the corresponding memory area
5530                     ASSERT(Vad->StartingVpn == MemoryArea->VadNode.StartingVpn);
5531                     ASSERT(Vad->EndingVpn == MemoryArea->VadNode.EndingVpn);
5532                     Vad->EndingVpn = (StartingAddress - 1) >> PAGE_SHIFT;
5533                     MemoryArea->VadNode.EndingVpn = Vad->EndingVpn;
5534                 }
5535                 else
5536                 {
5537                     //
5538                     // Case B (freeing a part in the middle)
5539                     //
5540                     // This is the hardest one. Because we are removing a chunk
5541                     // of memory from the very middle of the VAD, we must actually
5542                     // split the VAD into two new VADs and compute the commit
5543                     // charges for each of them, and reinsert new charges.
5544                     //
5545                     NewVad = ExAllocatePoolZero(NonPagedPool, sizeof(MMVAD_LONG), 'SdaV');
5546                     if (NewVad == NULL)
5547                     {
5548                         DPRINT1("Failed to allocate a VAD!\n");
5549                         Status = STATUS_INSUFFICIENT_RESOURCES;
5550                         goto FailPath;
5551                     }
5552 
5553                     // Charge quota for the new VAD
5554                     Status = PsChargeProcessNonPagedPoolQuota(Process, sizeof(MMVAD_LONG));
5555 
5556                     if (!NT_SUCCESS(Status))
5557                     {
5558                         DPRINT1("Ran out of process quota whilst creating new VAD!\n");
5559                         ExFreePoolWithTag(NewVad, 'SdaV');
5560                         Status = STATUS_QUOTA_EXCEEDED;
5561                         goto FailPath;
5562                     }
5563 
5564                     //
5565                     // This new VAD describes the second chunk, so we keep the end
5566                     // address of the original and adjust the start to point past
5567                     // the released region.
5568                     // The commit charge will be calculated below.
5569                     //
5570                     NewVad->StartingVpn = (EndingAddress + 1) >> PAGE_SHIFT;
5571                     NewVad->EndingVpn = Vad->EndingVpn;
5572                     NewVad->u.LongFlags = Vad->u.LongFlags;
5573                     NewVad->u.VadFlags.CommitCharge = 0;
5574                     ASSERT(NewVad->EndingVpn >= NewVad->StartingVpn);
5575 
5576                     //
5577                     // Get the commit charge for the released region
5578                     //
5579                     MiLockProcessWorkingSetUnsafe(Process, CurrentThread);
5580                     CommitReduction = MiCalculatePageCommitment(StartingAddress,
5581                                                                 EndingAddress,
5582                                                                 Vad,
5583                                                                 Process);
5584 
5585                     //
5586                     // Adjust the end of the original VAD (first chunk).
5587                     // For ReactOS: shrink the corresponding memory area
5588                     //
5589                     ASSERT(Vad->StartingVpn == MemoryArea->VadNode.StartingVpn);
5590                     ASSERT(Vad->EndingVpn == MemoryArea->VadNode.EndingVpn);
5591                     Vad->EndingVpn = (StartingAddress - 1) >> PAGE_SHIFT;
5592                     MemoryArea->VadNode.EndingVpn = Vad->EndingVpn;
5593 
5594                     //
5595                     // Now the addresses for both VADs are consistent,
5596                     // so insert the new one.
5597                     // ReactOS: This will take care of creating a second MEMORY_AREA.
5598                     //
5599                     MiInsertVad(NewVad, &Process->VadRoot);
5600 
5601                     //
5602                     // Calculate the commit charge for the first split.
5603                     // The second chunk's size is the original size, minus the
5604                     // released region's size, minus this first chunk.
5605                     //
5606                     FirstCommit = MiCalculatePageCommitment(Vad->StartingVpn << PAGE_SHIFT,
5607                                                             StartingAddress - 1,
5608                                                             Vad,
5609                                                             Process);
5610                     NewVad->u.VadFlags.CommitCharge = Vad->u.VadFlags.CommitCharge - CommitReduction - FirstCommit;
5611                     Vad->u.VadFlags.CommitCharge = FirstCommit;
5612                 }
5613 
5614                 //
5615                 // After analyzing the VAD, set it to NULL so that we don't
5616                 // free it in the exit path
5617                 //
5618                 Vad = NULL;
5619             }
5620         }
5621 
5622         //
5623         // Now we have a range of pages to dereference, so call the right API
5624         // to do that and then release the working set, since we're done messing
5625         // around with process pages.
5626         //
5627         MiDeleteVirtualAddresses(StartingAddress, EndingAddress, NULL);
5628         MiUnlockProcessWorkingSetUnsafe(Process, CurrentThread);
5629         Status = STATUS_SUCCESS;
5630 
5631 FinalPath:
5632         //
5633         // Update the process counters
5634         //
5635         PRegionSize = EndingAddress - StartingAddress + 1;
5636         Process->CommitCharge -= CommitReduction;
5637         if (FreeType & MEM_RELEASE) Process->VirtualSize -= PRegionSize;
5638 
5639         //
5640         // Unlock the address space and free the VAD in failure cases. Next,
5641         // detach from the target process so we can write the region size and the
5642         // base address to the correct source process, and dereference the target
5643         // process.
5644         //
5645         MmUnlockAddressSpace(AddressSpace);
5646         if (Vad) ExFreePool(Vad);
5647         if (Attached) KeUnstackDetachProcess(&ApcState);
5648         if (ProcessHandle != NtCurrentProcess()) ObDereferenceObject(Process);
5649 
5650         //
5651         // Use SEH to safely return the region size and the base address of the
5652         // deallocation. If we get an access violation, don't return a failure code
5653         // as the deallocation *has* happened. The caller will just have to figure
5654         // out another way to find out where it is (such as VirtualQuery).
5655         //
5656         _SEH2_TRY
5657         {
5658             *URegionSize = PRegionSize;
5659             *UBaseAddress = (PVOID)StartingAddress;
5660         }
5661         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
5662         {
5663         }
5664         _SEH2_END;
5665         return Status;
5666     }
5667 
5668     //
5669     // This is the decommit path. You cannot decommit from the following VADs in
5670     // Windows, so fail the vall
5671     //
5672     if ((Vad->u.VadFlags.VadType == VadAwe) ||
5673         (Vad->u.VadFlags.VadType == VadLargePages) ||
5674         (Vad->u.VadFlags.VadType == VadRotatePhysical))
5675     {
5676         DPRINT1("Trying to decommit from invalid VAD\n");
5677         Status = STATUS_MEMORY_NOT_ALLOCATED;
5678         goto FailPath;
5679     }
5680 
5681     //
5682     // If the caller did not specify a region size, first make sure that this
5683     // region is actually committed. If it is, then compute the ending address
5684     // based on the VAD.
5685     //
5686     if (!PRegionSize)
5687     {
5688         if (((ULONG_PTR)PBaseAddress >> PAGE_SHIFT) != Vad->StartingVpn)
5689         {
5690             DPRINT1("Decomitting non-committed memory\n");
5691             Status = STATUS_FREE_VM_NOT_AT_BASE;
5692             goto FailPath;
5693         }
5694         EndingAddress = (Vad->EndingVpn << PAGE_SHIFT) | (PAGE_SIZE - 1);
5695     }
5696 
5697     //
5698     // Decommit the PTEs for the range plus the actual backing pages for the
5699     // range, then reduce that amount from the commit charge in the VAD
5700     //
5701     AlreadyDecommitted = MiDecommitPages((PVOID)StartingAddress,
5702                                          MiAddressToPte(EndingAddress),
5703                                          Process,
5704                                          Vad);
5705     CommitReduction = MiAddressToPte(EndingAddress) -
5706                       MiAddressToPte(StartingAddress) +
5707                       1 -
5708                       AlreadyDecommitted;
5709 
5710     ASSERT(CommitReduction >= 0);
5711     ASSERT(Vad->u.VadFlags.CommitCharge >= CommitReduction);
5712     Vad->u.VadFlags.CommitCharge -= CommitReduction;
5713 
5714     //
5715     // We are done, go to the exit path without freeing the VAD as it remains
5716     // valid since we have not released the allocation.
5717     //
5718     Vad = NULL;
5719     Status = STATUS_SUCCESS;
5720     goto FinalPath;
5721 
5722     //
5723     // In the failure path, we detach and dereference the target process, and
5724     // return whatever failure code was sent.
5725     //
5726 FailPath:
5727     MmUnlockAddressSpace(AddressSpace);
5728     if (Attached) KeUnstackDetachProcess(&ApcState);
5729     if (ProcessHandle != NtCurrentProcess()) ObDereferenceObject(Process);
5730     return Status;
5731 }
5732 
5733 
5734 PHYSICAL_ADDRESS
5735 NTAPI
MmGetPhysicalAddress(PVOID Address)5736 MmGetPhysicalAddress(PVOID Address)
5737 {
5738     PHYSICAL_ADDRESS PhysicalAddress;
5739     MMPDE TempPde;
5740     MMPTE TempPte;
5741 
5742     /* Check if the PXE/PPE/PDE is valid */
5743     if (
5744 #if (_MI_PAGING_LEVELS == 4)
5745         (MiAddressToPxe(Address)->u.Hard.Valid) &&
5746 #endif
5747 #if (_MI_PAGING_LEVELS >= 3)
5748         (MiAddressToPpe(Address)->u.Hard.Valid) &&
5749 #endif
5750         (MiAddressToPde(Address)->u.Hard.Valid))
5751     {
5752         /* Check for large pages */
5753         TempPde = *MiAddressToPde(Address);
5754         if (TempPde.u.Hard.LargePage)
5755         {
5756             /* Physical address is base page + large page offset */
5757             PhysicalAddress.QuadPart = (ULONG64)TempPde.u.Hard.PageFrameNumber << PAGE_SHIFT;
5758             PhysicalAddress.QuadPart += ((ULONG_PTR)Address & (PAGE_SIZE * PTE_PER_PAGE - 1));
5759             return PhysicalAddress;
5760         }
5761 
5762         /* Check if the PTE is valid */
5763         TempPte = *MiAddressToPte(Address);
5764         if (TempPte.u.Hard.Valid)
5765         {
5766             /* Physical address is base page + page offset */
5767             PhysicalAddress.QuadPart = (ULONG64)TempPte.u.Hard.PageFrameNumber << PAGE_SHIFT;
5768             PhysicalAddress.QuadPart += ((ULONG_PTR)Address & (PAGE_SIZE - 1));
5769             return PhysicalAddress;
5770         }
5771     }
5772 
5773     KeRosDumpStackFrames(NULL, 20);
5774     DPRINT1("MM:MmGetPhysicalAddressFailed base address was %p\n", Address);
5775     PhysicalAddress.QuadPart = 0;
5776     return PhysicalAddress;
5777 }
5778 
5779 
5780 /* EOF */
5781