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