ReactOS  0.4.15-dev-2504-g2b52f3b
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 
1360  UNIMPLEMENTED;
1361 
1362  return STATUS_NOT_IMPLEMENTED;
1363 }
1364 
1365 ULONG
1366 NTAPI
1368 {
1369  MMPTE TempPte;
1370  PMMPFN Pfn;
1372  PETHREAD CurrentThread;
1373  BOOLEAN WsSafe, WsShared;
1374  ULONG Protect;
1375  KIRQL OldIrql;
1376  PAGED_CODE();
1377 
1378  /* Copy this PTE's contents */
1379  TempPte = *PointerPte;
1380 
1381  /* Assure it's not totally zero */
1382  ASSERT(TempPte.u.Long);
1383 
1384  /* Check for a special prototype format */
1385  if ((TempPte.u.Soft.Valid == 0) &&
1386  (TempPte.u.Soft.Prototype == 1))
1387  {
1388  /* Check if the prototype PTE is not yet pointing to a PTE */
1389  if (TempPte.u.Soft.PageFileHigh == MI_PTE_LOOKUP_NEEDED)
1390  {
1391  /* The prototype PTE contains the protection */
1392  return MmProtectToValue[TempPte.u.Soft.Protection];
1393  }
1394 
1395  /* Get a pointer to the underlying shared PTE */
1396  PointerPte = MiProtoPteToPte(&TempPte);
1397 
1398  /* Since the PTE we want to read can be paged out at any time, we need
1399  to release the working set lock first, so that it can be paged in */
1400  CurrentThread = PsGetCurrentThread();
1403  CurrentThread,
1404  &WsSafe,
1405  &WsShared);
1406 
1407  /* Now read the PTE value */
1408  TempPte = *PointerPte;
1409 
1410  /* Check if that one is invalid */
1411  if (!TempPte.u.Hard.Valid)
1412  {
1413  /* We get the protection directly from this PTE */
1414  Protect = MmProtectToValue[TempPte.u.Soft.Protection];
1415  }
1416  else
1417  {
1418  /* The PTE is valid, so we might need to get the protection from
1419  the PFN. Lock the PFN database */
1421 
1422  /* Check if the PDE is still valid */
1423  if (MiAddressToPte(PointerPte)->u.Hard.Valid == 0)
1424  {
1425  /* It's not, make it valid */
1426  MiMakeSystemAddressValidPfn(PointerPte, OldIrql);
1427  }
1428 
1429  /* Now it's safe to read the PTE value again */
1430  TempPte = *PointerPte;
1431  ASSERT(TempPte.u.Long != 0);
1432 
1433  /* Check again if the PTE is invalid */
1434  if (!TempPte.u.Hard.Valid)
1435  {
1436  /* The PTE is not valid, so we can use it's protection field */
1437  Protect = MmProtectToValue[TempPte.u.Soft.Protection];
1438  }
1439  else
1440  {
1441  /* The PTE is valid, so we can find the protection in the
1442  OriginalPte field of the PFN */
1443  Pfn = MI_PFN_ELEMENT(TempPte.u.Hard.PageFrameNumber);
1445  }
1446 
1447  /* Release the PFN database */
1449  }
1450 
1451  /* Lock the working set again */
1453  CurrentThread,
1454  WsSafe,
1455  WsShared);
1456 
1457  return Protect;
1458  }
1459 
1460  /* In the easy case of transition or demand zero PTE just return its protection */
1461  if (!TempPte.u.Hard.Valid) return MmProtectToValue[TempPte.u.Soft.Protection];
1462 
1463  /* If we get here, the PTE is valid, so look up the page in PFN database */
1464  Pfn = MiGetPfnEntry(TempPte.u.Hard.PageFrameNumber);
1465  if (!Pfn->u3.e1.PrototypePte)
1466  {
1467  /* Return protection of the original pte */
1468  ASSERT(Pfn->u4.AweAllocation == 0);
1470  }
1471 
1472  /* This is software PTE */
1473  DPRINT("Prototype PTE: %lx %p\n", TempPte.u.Hard.PageFrameNumber, Pfn);
1474  DPRINT("VA: %p\n", MiPteToAddress(&TempPte));
1475  DPRINT("Mask: %lx\n", TempPte.u.Soft.Protection);
1476  DPRINT("Mask2: %lx\n", Pfn->OriginalPte.u.Soft.Protection);
1477  return MmProtectToValue[TempPte.u.Soft.Protection];
1478 }
1479 
1480 ULONG
1481 NTAPI
1483  IN PMMVAD Vad,
1484  IN PEPROCESS TargetProcess,
1485  OUT PULONG ReturnedProtect,
1486  OUT PVOID *NextVa)
1487 {
1488 
1489  PMMPTE PointerPte, ProtoPte;
1490  PMMPDE PointerPde;
1491 #if (_MI_PAGING_LEVELS >= 3)
1492  PMMPPE PointerPpe;
1493 #endif
1494 #if (_MI_PAGING_LEVELS >= 4)
1495  PMMPXE PointerPxe;
1496 #endif
1497  MMPTE TempPte, TempProtoPte;
1498  BOOLEAN DemandZeroPte = TRUE, ValidPte = FALSE;
1499  ULONG State = MEM_RESERVE, Protect = 0;
1500  ASSERT((Vad->StartingVpn <= ((ULONG_PTR)Va >> PAGE_SHIFT)) &&
1501  (Vad->EndingVpn >= ((ULONG_PTR)Va >> PAGE_SHIFT)));
1502 
1503  /* Only normal VADs supported */
1504  ASSERT(Vad->u.VadFlags.VadType == VadNone);
1505 
1506  /* Get the PDE and PTE for the address */
1507  PointerPde = MiAddressToPde(Va);
1508  PointerPte = MiAddressToPte(Va);
1509 #if (_MI_PAGING_LEVELS >= 3)
1510  PointerPpe = MiAddressToPpe(Va);
1511 #endif
1512 #if (_MI_PAGING_LEVELS >= 4)
1513  PointerPxe = MiAddressToPxe(Va);
1514 #endif
1515 
1516  /* Return the next range */
1517  *NextVa = (PVOID)((ULONG_PTR)Va + PAGE_SIZE);
1518 
1519  do
1520  {
1521 #if (_MI_PAGING_LEVELS >= 4)
1522  /* Does the PXE exist? */
1523  if (PointerPxe->u.Long == 0)
1524  {
1525  /* It does not, next range starts at the next PXE */
1526  *NextVa = MiPxeToAddress(PointerPxe + 1);
1527  break;
1528  }
1529 
1530  /* Is the PXE valid? */
1531  if (PointerPxe->u.Hard.Valid == 0)
1532  {
1533  /* Is isn't, fault it in (make the PPE accessible) */
1534  MiMakeSystemAddressValid(PointerPpe, TargetProcess);
1535  }
1536 #endif
1537 #if (_MI_PAGING_LEVELS >= 3)
1538  /* Does the PPE exist? */
1539  if (PointerPpe->u.Long == 0)
1540  {
1541  /* It does not, next range starts at the next PPE */
1542  *NextVa = MiPpeToAddress(PointerPpe + 1);
1543  break;
1544  }
1545 
1546  /* Is the PPE valid? */
1547  if (PointerPpe->u.Hard.Valid == 0)
1548  {
1549  /* Is isn't, fault it in (make the PDE accessible) */
1550  MiMakeSystemAddressValid(PointerPde, TargetProcess);
1551  }
1552 #endif
1553 
1554  /* Does the PDE exist? */
1555  if (PointerPde->u.Long == 0)
1556  {
1557  /* It does not, next range starts at the next PDE */
1558  *NextVa = MiPdeToAddress(PointerPde + 1);
1559  break;
1560  }
1561 
1562  /* Is the PDE valid? */
1563  if (PointerPde->u.Hard.Valid == 0)
1564  {
1565  /* Is isn't, fault it in (make the PTE accessible) */
1566  MiMakeSystemAddressValid(PointerPte, TargetProcess);
1567  }
1568 
1569  /* We have a PTE that we can access now! */
1570  ValidPte = TRUE;
1571 
1572  } while (FALSE);
1573 
1574  /* Is it safe to try reading the PTE? */
1575  if (ValidPte)
1576  {
1577  /* FIXME: watch out for large pages */
1578  ASSERT(PointerPde->u.Hard.LargePage == FALSE);
1579 
1580  /* Capture the PTE */
1581  TempPte = *PointerPte;
1582  if (TempPte.u.Long != 0)
1583  {
1584  /* The PTE is valid, so it's not zeroed out */
1585  DemandZeroPte = FALSE;
1586 
1587  /* Is it a decommited, invalid, or faulted PTE? */
1588  if ((TempPte.u.Soft.Protection == MM_DECOMMIT) &&
1589  (TempPte.u.Hard.Valid == 0) &&
1590  ((TempPte.u.Soft.Prototype == 0) ||
1591  (TempPte.u.Soft.PageFileHigh == MI_PTE_LOOKUP_NEEDED)))
1592  {
1593  /* Otherwise our defaults should hold */
1594  ASSERT(Protect == 0);
1595  ASSERT(State == MEM_RESERVE);
1596  }
1597  else
1598  {
1599  /* This means it's committed */
1600  State = MEM_COMMIT;
1601 
1602  /* We don't support these */
1603  ASSERT(Vad->u.VadFlags.VadType != VadDevicePhysicalMemory);
1604  ASSERT(Vad->u.VadFlags.VadType != VadRotatePhysical);
1605  ASSERT(Vad->u.VadFlags.VadType != VadAwe);
1606 
1607  /* Get protection state of this page */
1608  Protect = MiGetPageProtection(PointerPte);
1609 
1610  /* Check if this is an image-backed VAD */
1611  if ((TempPte.u.Soft.Valid == 0) &&
1612  (TempPte.u.Soft.Prototype == 1) &&
1613  (Vad->u.VadFlags.PrivateMemory == 0) &&
1614  (Vad->ControlArea))
1615  {
1616  DPRINT1("Not supported\n");
1617  ASSERT(FALSE);
1618  }
1619  }
1620  }
1621  }
1622 
1623  /* Check if this was a demand-zero PTE, since we need to find the state */
1624  if (DemandZeroPte)
1625  {
1626  /* Not yet handled */
1627  ASSERT(Vad->u.VadFlags.VadType != VadDevicePhysicalMemory);
1628  ASSERT(Vad->u.VadFlags.VadType != VadAwe);
1629 
1630  /* Check if this is private commited memory, or an section-backed VAD */
1631  if ((Vad->u.VadFlags.PrivateMemory == 0) && (Vad->ControlArea))
1632  {
1633  /* Tell caller about the next range */
1634  *NextVa = (PVOID)((ULONG_PTR)Va + PAGE_SIZE);
1635 
1636  /* Get the prototype PTE for this VAD */
1637  ProtoPte = MI_GET_PROTOTYPE_PTE_FOR_VPN(Vad,
1638  (ULONG_PTR)Va >> PAGE_SHIFT);
1639  if (ProtoPte)
1640  {
1641  /* We should unlock the working set, but it's not being held! */
1642 
1643  /* Is the prototype PTE actually valid (committed)? */
1644  TempProtoPte = *ProtoPte;
1645  if (TempProtoPte.u.Long)
1646  {
1647  /* Unless this is a memory-mapped file, handle it like private VAD */
1648  State = MEM_COMMIT;
1649  ASSERT(Vad->u.VadFlags.VadType != VadImageMap);
1650  Protect = MmProtectToValue[Vad->u.VadFlags.Protection];
1651  }
1652 
1653  /* We should re-lock the working set */
1654  }
1655  }
1656  else if (Vad->u.VadFlags.MemCommit)
1657  {
1658  /* This is committed memory */
1659  State = MEM_COMMIT;
1660 
1661  /* Convert the protection */
1662  Protect = MmProtectToValue[Vad->u.VadFlags.Protection];
1663  }
1664  }
1665 
1666  /* Return the protection code */
1667  *ReturnedProtect = Protect;
1668  return State;
1669 }
1670 
1671 NTSTATUS
1672 NTAPI
1675  OUT PVOID MemoryInformation,
1676  IN SIZE_T MemoryInformationLength,
1678 {
1679  PEPROCESS TargetProcess;
1681  PMMVAD Vad = NULL;
1682  PVOID Address, NextAddress;
1683  BOOLEAN Found = FALSE;
1684  ULONG NewProtect, NewState;
1685  ULONG_PTR BaseVpn;
1686  MEMORY_BASIC_INFORMATION MemoryInfo;
1691 
1692  /* Check for illegal addresses in user-space, or the shared memory area */
1695  {
1697 
1698  /* Make up an info structure describing this range */
1699  MemoryInfo.BaseAddress = Address;
1700  MemoryInfo.AllocationProtect = PAGE_READONLY;
1701  MemoryInfo.Type = MEM_PRIVATE;
1702 
1703  /* Special case for shared data */
1705  {
1707  MemoryInfo.State = MEM_COMMIT;
1708  MemoryInfo.Protect = PAGE_READONLY;
1709  MemoryInfo.RegionSize = PAGE_SIZE;
1710  }
1711  else
1712  {
1713  MemoryInfo.AllocationBase = (PCHAR)MM_HIGHEST_VAD_ADDRESS + 1;
1714  MemoryInfo.State = MEM_RESERVE;
1715  MemoryInfo.Protect = PAGE_NOACCESS;
1717  }
1718 
1719  /* Return the data, NtQueryInformation already probed it*/
1720  if (PreviousMode != KernelMode)
1721  {
1722  _SEH2_TRY
1723  {
1724  *(PMEMORY_BASIC_INFORMATION)MemoryInformation = MemoryInfo;
1726  }
1728  {
1730  }
1731  _SEH2_END;
1732  }
1733  else
1734  {
1735  *(PMEMORY_BASIC_INFORMATION)MemoryInformation = MemoryInfo;
1737  }
1738 
1739  return Status;
1740  }
1741 
1742  /* Check if this is for a local or remote process */
1744  {
1745  TargetProcess = PsGetCurrentProcess();
1746  }
1747  else
1748  {
1749  /* Reference the target process */
1752  PsProcessType,
1754  (PVOID*)&TargetProcess,
1755  NULL);
1756  if (!NT_SUCCESS(Status)) return Status;
1757 
1758  /* Attach to it now */
1759  KeStackAttachProcess(&TargetProcess->Pcb, &ApcState);
1760  }
1761 
1762  /* Lock the address space and make sure the process isn't already dead */
1763  MmLockAddressSpace(&TargetProcess->Vm);
1764  if (TargetProcess->VmDeleted)
1765  {
1766  /* Unlock the address space of the process */
1767  MmUnlockAddressSpace(&TargetProcess->Vm);
1768 
1769  /* Check if we were attached */
1771  {
1772  /* Detach and dereference the process */
1774  ObDereferenceObject(TargetProcess);
1775  }
1776 
1777  /* Bail out */
1778  DPRINT1("Process is dying\n");
1780  }
1781 
1782  /* Loop the VADs */
1783  ASSERT(TargetProcess->VadRoot.NumberGenericTableElements);
1784  if (TargetProcess->VadRoot.NumberGenericTableElements)
1785  {
1786  /* Scan on the right */
1787  Vad = (PMMVAD)TargetProcess->VadRoot.BalancedRoot.RightChild;
1788  BaseVpn = (ULONG_PTR)BaseAddress >> PAGE_SHIFT;
1789  while (Vad)
1790  {
1791  /* Check if this VAD covers the allocation range */
1792  if ((BaseVpn >= Vad->StartingVpn) &&
1793  (BaseVpn <= Vad->EndingVpn))
1794  {
1795  /* We're done */
1796  Found = TRUE;
1797  break;
1798  }
1799 
1800  /* Check if this VAD is too high */
1801  if (BaseVpn < Vad->StartingVpn)
1802  {
1803  /* Stop if there is no left child */
1804  if (!Vad->LeftChild) break;
1805 
1806  /* Search on the left next */
1807  Vad = Vad->LeftChild;
1808  }
1809  else
1810  {
1811  /* Then this VAD is too low, keep searching on the right */
1812  ASSERT(BaseVpn > Vad->EndingVpn);
1813 
1814  /* Stop if there is no right child */
1815  if (!Vad->RightChild) break;
1816 
1817  /* Search on the right next */
1818  Vad = Vad->RightChild;
1819  }
1820  }
1821  }
1822 
1823  /* Was a VAD found? */
1824  if (!Found)
1825  {
1827 
1828  /* Calculate region size */
1829  if (Vad)
1830  {
1831  if (Vad->StartingVpn >= BaseVpn)
1832  {
1833  /* Region size is the free space till the start of that VAD */
1834  MemoryInfo.RegionSize = (ULONG_PTR)(Vad->StartingVpn << PAGE_SHIFT) - (ULONG_PTR)Address;
1835  }
1836  else
1837  {
1838  /* Get the next VAD */
1839  Vad = (PMMVAD)MiGetNextNode((PMMADDRESS_NODE)Vad);
1840  if (Vad)
1841  {
1842  /* Region size is the free space till the start of that VAD */
1843  MemoryInfo.RegionSize = (ULONG_PTR)(Vad->StartingVpn << PAGE_SHIFT) - (ULONG_PTR)Address;
1844  }
1845  else
1846  {
1847  /* Maximum possible region size with that base address */
1848  MemoryInfo.RegionSize = (PCHAR)MM_HIGHEST_VAD_ADDRESS + 1 - (PCHAR)Address;
1849  }
1850  }
1851  }
1852  else
1853  {
1854  /* Maximum possible region size with that base address */
1855  MemoryInfo.RegionSize = (PCHAR)MM_HIGHEST_VAD_ADDRESS + 1 - (PCHAR)Address;
1856  }
1857 
1858  /* Unlock the address space of the process */
1859  MmUnlockAddressSpace(&TargetProcess->Vm);
1860 
1861  /* Check if we were attached */
1863  {
1864  /* Detach and derefernece the process */
1866  ObDereferenceObject(TargetProcess);
1867  }
1868 
1869  /* Build the rest of the initial information block */
1870  MemoryInfo.BaseAddress = Address;
1871  MemoryInfo.AllocationBase = NULL;
1872  MemoryInfo.AllocationProtect = 0;
1873  MemoryInfo.State = MEM_FREE;
1874  MemoryInfo.Protect = PAGE_NOACCESS;
1875  MemoryInfo.Type = 0;
1876 
1877  /* Return the data, NtQueryInformation already probed it*/
1878  if (PreviousMode != KernelMode)
1879  {
1880  _SEH2_TRY
1881  {
1882  *(PMEMORY_BASIC_INFORMATION)MemoryInformation = MemoryInfo;
1884  }
1886  {
1888  }
1889  _SEH2_END;
1890  }
1891  else
1892  {
1893  *(PMEMORY_BASIC_INFORMATION)MemoryInformation = MemoryInfo;
1895  }
1896 
1897  return Status;
1898  }
1899 
1900  /* Set the correct memory type based on what kind of VAD this is */
1901  if ((Vad->u.VadFlags.PrivateMemory) ||
1902  (Vad->u.VadFlags.VadType == VadRotatePhysical))
1903  {
1904  MemoryInfo.Type = MEM_PRIVATE;
1905  }
1906  else if (Vad->u.VadFlags.VadType == VadImageMap)
1907  {
1908  MemoryInfo.Type = MEM_IMAGE;
1909  }
1910  else
1911  {
1912  MemoryInfo.Type = MEM_MAPPED;
1913  }
1914 
1915  /* Find the memory area the specified address belongs to */
1917  ASSERT(MemoryArea != NULL);
1918 
1919  /* Determine information dependent on the memory area type */
1921  {
1923  if (!NT_SUCCESS(Status))
1924  {
1925  DPRINT1("MmQuerySectionView failed. MemoryArea=%p (%p-%p), BaseAddress=%p\n",
1928  }
1929  }
1930  else
1931  {
1932  /* Build the initial information block */
1934  MemoryInfo.BaseAddress = Address;
1935  MemoryInfo.AllocationBase = (PVOID)(Vad->StartingVpn << PAGE_SHIFT);
1937  MemoryInfo.Type = MEM_PRIVATE;
1938 
1939  /* Acquire the working set lock (shared is enough) */
1941 
1942  /* Find the largest chunk of memory which has the same state and protection mask */
1943  MemoryInfo.State = MiQueryAddressState(Address,
1944  Vad,
1945  TargetProcess,
1946  &MemoryInfo.Protect,
1947  &NextAddress);
1948  Address = NextAddress;
1949  while (((ULONG_PTR)Address >> PAGE_SHIFT) <= Vad->EndingVpn)
1950  {
1951  /* Keep going unless the state or protection mask changed */
1952  NewState = MiQueryAddressState(Address, Vad, TargetProcess, &NewProtect, &NextAddress);
1953  if ((NewState != MemoryInfo.State) || (NewProtect != MemoryInfo.Protect)) break;
1954  Address = NextAddress;
1955  }
1956 
1957  /* Release the working set lock */
1959 
1960  /* Check if we went outside of the VAD */
1961  if (((ULONG_PTR)Address >> PAGE_SHIFT) > Vad->EndingVpn)
1962  {
1963  /* Set the end of the VAD as the end address */
1964  Address = (PVOID)((Vad->EndingVpn + 1) << PAGE_SHIFT);
1965  }
1966 
1967  /* Now that we know the last VA address, calculate the region size */
1968  MemoryInfo.RegionSize = ((ULONG_PTR)Address - (ULONG_PTR)MemoryInfo.BaseAddress);
1969  }
1970 
1971  /* Unlock the address space of the process */
1972  MmUnlockAddressSpace(&TargetProcess->Vm);
1973 
1974  /* Check if we were attached */
1976  {
1977  /* Detach and derefernece the process */
1979  ObDereferenceObject(TargetProcess);
1980  }
1981 
1982  /* Return the data, NtQueryInformation already probed it */
1983  if (PreviousMode != KernelMode)
1984  {
1985  _SEH2_TRY
1986  {
1987  *(PMEMORY_BASIC_INFORMATION)MemoryInformation = MemoryInfo;
1989  }
1991  {
1993  }
1994  _SEH2_END;
1995  }
1996  else
1997  {
1998  *(PMEMORY_BASIC_INFORMATION)MemoryInformation = MemoryInfo;
2000  }
2001 
2002  /* All went well */
2003  DPRINT("Base: %p AllocBase: %p AllocProtect: %lx Protect: %lx "
2004  "State: %lx Type: %lx Size: %lx\n",
2005  MemoryInfo.BaseAddress, MemoryInfo.AllocationBase,
2006  MemoryInfo.AllocationProtect, MemoryInfo.Protect,
2007  MemoryInfo.State, MemoryInfo.Type, MemoryInfo.RegionSize);
2008 
2009  return Status;
2010 }
2011 
2012 BOOLEAN
2013 NTAPI
2015  IN ULONG_PTR EndingAddress,
2016  IN PMMVAD Vad,
2018 {
2019  PMMPTE PointerPte, LastPte;
2020  PMMPDE PointerPde;
2021  BOOLEAN OnBoundary = TRUE;
2022  PAGED_CODE();
2023 
2024  /* Get the PDE and PTE addresses */
2025  PointerPde = MiAddressToPde(StartingAddress);
2026  PointerPte = MiAddressToPte(StartingAddress);
2027  LastPte = MiAddressToPte(EndingAddress);
2028 
2029  /* Loop all the PTEs */
2030  while (PointerPte <= LastPte)
2031  {
2032  /* Check if we've hit an new PDE boundary */
2033  if (OnBoundary)
2034  {
2035  /* Is this PDE demand zero? */
2036  PointerPde = MiPteToPde(PointerPte);
2037  if (PointerPde->u.Long != 0)
2038  {
2039  /* It isn't -- is it valid? */
2040  if (PointerPde->u.Hard.Valid == 0)
2041  {
2042  /* Nope, fault it in */
2043  MiMakeSystemAddressValid(PointerPte, Process);
2044  }
2045  }
2046  else
2047  {
2048  /* The PTE was already valid, so move to the next one */
2049  PointerPde++;
2050  PointerPte = MiPdeToPte(PointerPde);
2051 
2052  /* Is the entire VAD committed? If not, fail */
2053  if (!Vad->u.VadFlags.MemCommit) return FALSE;
2054 
2055  /* New loop iteration with our new, on-boundary PTE. */
2056  continue;
2057  }
2058  }
2059 
2060  /* Is the PTE demand zero? */
2061  if (PointerPte->u.Long == 0)
2062  {
2063  /* Is the entire VAD committed? If not, fail */
2064  if (!Vad->u.VadFlags.MemCommit) return FALSE;
2065  }
2066  else
2067  {
2068  /* It isn't -- is it a decommited, invalid, or faulted PTE? */
2069  if ((PointerPte->u.Soft.Protection == MM_DECOMMIT) &&
2070  (PointerPte->u.Hard.Valid == 0) &&
2071  ((PointerPte->u.Soft.Prototype == 0) ||
2072  (PointerPte->u.Soft.PageFileHigh == MI_PTE_LOOKUP_NEEDED)))
2073  {
2074  /* Then part of the range is decommitted, so fail */
2075  return FALSE;
2076  }
2077  }
2078 
2079  /* Move to the next PTE */
2080  PointerPte++;
2081  OnBoundary = MiIsPteOnPdeBoundary(PointerPte);
2082  }
2083 
2084  /* All PTEs seem valid, and no VAD checks failed, the range is okay */
2085  return TRUE;
2086 }
2087 
2088 NTSTATUS
2089 NTAPI
2092  IN OUT PSIZE_T NumberOfBytesToProtect,
2093  IN ULONG NewAccessProtection,
2094  OUT PULONG OldAccessProtection OPTIONAL)
2095 {
2098  ULONG OldAccessProtection_;
2099  NTSTATUS Status;
2100 
2101  *NumberOfBytesToProtect = PAGE_ROUND_UP((ULONG_PTR)(*BaseAddress) + (*NumberOfBytesToProtect)) - PAGE_ROUND_DOWN(*BaseAddress);
2103 
2104  AddressSpace = &Process->Vm;
2108  {
2110  return STATUS_UNSUCCESSFUL;
2111  }
2112 
2113  if (OldAccessProtection == NULL) OldAccessProtection = &OldAccessProtection_;
2114 
2117  MemoryArea,
2118  *BaseAddress,
2119  *NumberOfBytesToProtect,
2120  NewAccessProtection,
2121  OldAccessProtection);
2122 
2124 
2125  return Status;
2126 }
2127 
2128 NTSTATUS
2129 NTAPI
2132  IN OUT PSIZE_T NumberOfBytesToProtect,
2133  IN ULONG NewAccessProtection,
2134  OUT PULONG OldAccessProtection OPTIONAL)
2135 {
2137  PMMVAD Vad;
2139  ULONG_PTR StartingAddress, EndingAddress;
2140  PMMPTE PointerPte, LastPte;
2141  PMMPDE PointerPde;
2142  MMPTE PteContents;
2143  PMMPFN Pfn1;
2144  ULONG ProtectionMask, OldProtect;
2145  BOOLEAN Committed;
2149 
2150  /* Calculate base address for the VAD */
2151  StartingAddress = (ULONG_PTR)PAGE_ALIGN((*BaseAddress));
2152  EndingAddress = (((ULONG_PTR)*BaseAddress + *NumberOfBytesToProtect - 1) | (PAGE_SIZE - 1));
2153 
2154  /* Calculate the protection mask and make sure it's valid */
2155  ProtectionMask = MiMakeProtectionMask(NewAccessProtection);
2156  if (ProtectionMask == MM_INVALID_PROTECTION)
2157  {
2158  DPRINT1("Invalid protection mask\n");
2160  }
2161 
2162  /* Check for ROS specific memory area */
2165  {
2166  /* Evil hack */
2168  BaseAddress,
2169  NumberOfBytesToProtect,
2170  NewAccessProtection,
2171  OldAccessProtection);
2172  }
2173 
2174  /* Lock the address space and make sure the process isn't already dead */
2177  if (Process->VmDeleted)
2178  {
2179  DPRINT1("Process is dying\n");
2181  goto FailPath;
2182  }
2183 
2184  /* Get the VAD for this address range, and make sure it exists */
2185  Result = MiCheckForConflictingNode(StartingAddress >> PAGE_SHIFT,
2186  EndingAddress >> PAGE_SHIFT,
2187  &Process->VadRoot,
2188  (PMMADDRESS_NODE*)&Vad);
2189  if (Result != TableFoundNode)
2190  {
2191  DPRINT("Could not find a VAD for this allocation\n");
2193  goto FailPath;
2194  }
2195 
2196  /* Make sure the address is within this VAD's boundaries */
2197  if ((((ULONG_PTR)StartingAddress >> PAGE_SHIFT) < Vad->StartingVpn) ||
2198  (((ULONG_PTR)EndingAddress >> PAGE_SHIFT) > Vad->EndingVpn))
2199  {
2201  goto FailPath;
2202  }
2203 
2204  /* These kinds of VADs are not supported atm */
2205  if ((Vad->u.VadFlags.VadType == VadAwe) ||
2206  (Vad->u.VadFlags.VadType == VadDevicePhysicalMemory) ||
2207  (Vad->u.VadFlags.VadType == VadLargePages))
2208  {
2209  DPRINT1("Illegal VAD for attempting to set protection\n");
2211  goto FailPath;
2212  }
2213 
2214  /* Check for a VAD whose protection can't be changed */
2215  if (Vad->u.VadFlags.NoChange == 1)
2216  {
2217  DPRINT1("Trying to change protection of a NoChange VAD\n");
2219  goto FailPath;
2220  }
2221 
2222  /* Is this section, or private memory? */
2223  if (Vad->u.VadFlags.PrivateMemory == 0)
2224  {
2225  /* Not yet supported */
2226  if (Vad->u.VadFlags.VadType == VadLargePageSection)
2227  {
2228  DPRINT1("Illegal VAD for attempting to set protection\n");
2230  goto FailPath;
2231  }
2232 
2233  /* Rotate VADs are not yet supported */
2234  if (Vad->u.VadFlags.VadType == VadRotatePhysical)
2235  {
2236  DPRINT1("Illegal VAD for attempting to set protection\n");
2238  goto FailPath;
2239  }
2240 
2241  /* Not valid on section files */
2242  if (NewAccessProtection & (PAGE_NOCACHE | PAGE_WRITECOMBINE))
2243  {
2244  /* Fail */
2245  DPRINT1("Invalid protection flags for section\n");
2247  goto FailPath;
2248  }
2249 
2250  /* Check if data or page file mapping protection PTE is compatible */
2251  if (!Vad->ControlArea->u.Flags.Image)
2252  {
2253  /* Not yet */
2254  DPRINT1("Fixme: Not checking for valid protection\n");
2255  }
2256 
2257  /* This is a section, and this is not yet supported */
2258  DPRINT1("Section protection not yet supported\n");
2259  OldProtect = 0;
2260  }
2261  else
2262  {
2263  /* Private memory, check protection flags */
2264  if ((NewAccessProtection & PAGE_WRITECOPY) ||
2265  (NewAccessProtection & PAGE_EXECUTE_WRITECOPY))
2266  {
2267  DPRINT1("Invalid protection flags for private memory\n");
2269  goto FailPath;
2270  }
2271 
2272  /* Lock the working set */
2274 
2275  /* Check if all pages in this range are committed */
2276  Committed = MiIsEntireRangeCommitted(StartingAddress,
2277  EndingAddress,
2278  Vad,
2279  Process);
2280  if (!Committed)
2281  {
2282  /* Fail */
2283  DPRINT1("The entire range is not committed\n");
2286  goto FailPath;
2287  }
2288 
2289  /* Compute starting and ending PTE and PDE addresses */
2290  PointerPde = MiAddressToPde(StartingAddress);
2291  PointerPte = MiAddressToPte(StartingAddress);
2292  LastPte = MiAddressToPte(EndingAddress);
2293 
2294  /* Make this PDE valid */
2296 
2297  /* Save protection of the first page */
2298  if (PointerPte->u.Long != 0)
2299  {
2300  /* Capture the page protection and make the PDE valid */
2301  OldProtect = MiGetPageProtection(PointerPte);
2303  }
2304  else
2305  {
2306  /* Grab the old protection from the VAD itself */
2307  OldProtect = MmProtectToValue[Vad->u.VadFlags.Protection];
2308  }
2309 
2310  /* Loop all the PTEs now */
2311  while (PointerPte <= LastPte)
2312  {
2313  /* Check if we've crossed a PDE boundary and make the new PDE valid too */
2314  if (MiIsPteOnPdeBoundary(PointerPte))
2315  {
2316  PointerPde = MiPteToPde(PointerPte);
2318  }
2319 
2320  /* Capture the PTE and check if it was empty */
2321  PteContents = *PointerPte;
2322  if (PteContents.u.Long == 0)
2323  {
2324  /* This used to be a zero PTE and it no longer is, so we must add a
2325  reference to the pagetable. */
2327  }
2328 
2329  /* Check what kind of PTE we are dealing with */
2330  if (PteContents.u.Hard.Valid == 1)
2331  {
2332  /* Get the PFN entry */
2333  Pfn1 = MiGetPfnEntry(PFN_FROM_PTE(&PteContents));
2334 
2335  /* We don't support this yet */
2336  ASSERT(Pfn1->u3.e1.PrototypePte == 0);
2337 
2338  /* Check if the page should not be accessible at all */
2339  if ((NewAccessProtection & PAGE_NOACCESS) ||
2340  (NewAccessProtection & PAGE_GUARD))
2341  {
2343 
2344  /* Mark the PTE as transition and change its protection */
2345  PteContents.u.Hard.Valid = 0;
2346  PteContents.u.Soft.Transition = 1;
2347  PteContents.u.Trans.Protection = ProtectionMask;
2348  /* Decrease PFN share count and write the PTE */
2349  MiDecrementShareCount(Pfn1, PFN_FROM_PTE(&PteContents));
2350  // FIXME: remove the page from the WS
2351  MI_WRITE_INVALID_PTE(PointerPte, PteContents);
2352 #ifdef CONFIG_SMP
2353  // FIXME: Should invalidate entry in every CPU TLB
2354  ASSERT(FALSE);
2355 #endif
2356  KeInvalidateTlbEntry(MiPteToAddress(PointerPte));
2357 
2358  /* We are done for this PTE */
2360  }
2361  else
2362  {
2363  /* Write the protection mask and write it with a TLB flush */
2364  Pfn1->OriginalPte.u.Soft.Protection = ProtectionMask;
2365  MiFlushTbAndCapture(Vad,
2366  PointerPte,
2367  ProtectionMask,
2368  Pfn1,
2369  TRUE);
2370  }
2371  }
2372  else
2373  {
2374  /* We don't support these cases yet */
2375  ASSERT(PteContents.u.Soft.Prototype == 0);
2376  //ASSERT(PteContents.u.Soft.Transition == 0);
2377 
2378  /* The PTE is already demand-zero, just update the protection mask */
2379  PteContents.u.Soft.Protection = ProtectionMask;
2380  MI_WRITE_INVALID_PTE(PointerPte, PteContents);
2381  ASSERT(PointerPte->u.Long != 0);
2382  }
2383 
2384  /* Move to the next PTE */
2385  PointerPte++;
2386  }
2387 
2388  /* Unlock the working set */
2390  }
2391 
2392  /* Unlock the address space */
2394 
2395  /* Return parameters and success */
2396  *NumberOfBytesToProtect = EndingAddress - StartingAddress + 1;
2397  *BaseAddress = (PVOID)StartingAddress;
2398  *OldAccessProtection = OldProtect;
2399  return STATUS_SUCCESS;
2400 
2401 FailPath:
2402  /* Unlock the address space and return the failure code */
2404  return Status;
2405 }
2406 
2407 VOID
2408 NTAPI
2410  IN PEPROCESS TargetProcess,
2411  IN KIRQL OldIrql)
2412 {
2413  PMMPTE PointerPte, PointerPpe, PointerPxe;
2414 
2415  //
2416  // Sanity checks. The latter is because we only use this function with the
2417  // PFN lock not held, so it may go away in the future.
2418  //
2420  ASSERT(OldIrql == MM_NOIRQL);
2421 
2422  //
2423  // Also get the PPE and PXE. This is okay not to #ifdef because they will
2424  // return the same address as the PDE on 2-level page table systems.
2425  //
2426  // If everything is already valid, there is nothing to do.
2427  //
2428  PointerPpe = MiAddressToPte(PointerPde);
2429  PointerPxe = MiAddressToPde(PointerPde);
2430  if ((PointerPxe->u.Hard.Valid) &&
2431  (PointerPpe->u.Hard.Valid) &&
2432  (PointerPde->u.Hard.Valid))
2433  {
2434  return;
2435  }
2436 
2437  //
2438  // At least something is invalid, so begin by getting the PTE for the PDE itself
2439  // and then lookup each additional level. We must do it in this precise order
2440  // because the pagfault.c code (as well as in Windows) depends that the next
2441  // level up (higher) must be valid when faulting a lower level
2442  //
2443  PointerPte = MiPteToAddress(PointerPde);
2444  do
2445  {
2446  //
2447  // Make sure APCs continued to be disabled
2448  //
2450 
2451  //
2452  // First, make the PXE valid if needed
2453  //
2454  if (!PointerPxe->u.Hard.Valid)
2455  {
2456  MiMakeSystemAddressValid(PointerPpe, TargetProcess);
2457  ASSERT(PointerPxe->u.Hard.Valid == 1);
2458  }
2459 
2460  //
2461  // Next, the PPE
2462  //
2463  if (!PointerPpe->u.Hard.Valid)
2464  {
2465  MiMakeSystemAddressValid(PointerPde, TargetProcess);
2466  ASSERT(PointerPpe->u.Hard.Valid == 1);
2467  }
2468 
2469  //
2470  // And finally, make the PDE itself valid.
2471  //
2472  MiMakeSystemAddressValid(PointerPte, TargetProcess);
2473 
2474  //
2475  // This should've worked the first time so the loop is really just for
2476  // show -- ASSERT that we're actually NOT going to be looping.
2477  //
2478  ASSERT(PointerPxe->u.Hard.Valid == 1);
2479  ASSERT(PointerPpe->u.Hard.Valid == 1);
2480  ASSERT(PointerPde->u.Hard.Valid == 1);
2481  } while (!(PointerPxe->u.Hard.Valid) ||
2482  !(PointerPpe->u.Hard.Valid) ||
2483  !(PointerPde->u.Hard.Valid));
2484 }
2485 
2486 VOID
2487 NTAPI
2489  IN ULONG Count)
2490 {
2491  KIRQL OldIrql;
2492  ULONG i;
2493  MMPTE TempPte;
2494  PFN_NUMBER PageFrameIndex;
2495  PMMPFN Pfn1, Pfn2;
2496 
2497  //
2498  // Acquire the PFN lock and loop all the PTEs in the list
2499  //
2501  for (i = 0; i != Count; i++)
2502  {
2503  //
2504  // The PTE must currently be valid
2505  //
2506  TempPte = *ValidPteList[i];
2507  ASSERT(TempPte.u.Hard.Valid == 1);
2508 
2509  //
2510  // Get the PFN entry for the page itself, and then for its page table
2511  //
2512  PageFrameIndex = PFN_FROM_PTE(&TempPte);
2513  Pfn1 = MiGetPfnEntry(PageFrameIndex);
2514  Pfn2 = MiGetPfnEntry(Pfn1->u4.PteFrame);
2515 
2516  //
2517  // Decrement the share count on the page table, and then on the page
2518  // itself
2519  //
2520  MiDecrementShareCount(Pfn2, Pfn1->u4.PteFrame);
2521  MI_SET_PFN_DELETED(Pfn1);
2522  MiDecrementShareCount(Pfn1, PageFrameIndex);
2523 
2524  //
2525  // Make the page decommitted
2526  //
2527  MI_WRITE_INVALID_PTE(ValidPteList[i], MmDecommittedPte);
2528  }
2529 
2530  //
2531  // All the PTEs have been dereferenced and made invalid, flush the TLB now
2532  // and then release the PFN lock
2533  //
2534  KeFlushCurrentTb();
2536 }
2537 
2538 ULONG
2539 NTAPI
2540 MiDecommitPages(IN PVOID StartingAddress,
2541  IN PMMPTE EndingPte,
2543  IN PMMVAD Vad)
2544 {
2545  PMMPTE PointerPte, CommitPte = NULL;
2546  PMMPDE PointerPde;
2547  ULONG CommitReduction = 0;
2548  PMMPTE ValidPteList[256];
2549  ULONG PteCount = 0;
2550  PMMPFN Pfn1;
2551  MMPTE PteContents;
2552  PETHREAD CurrentThread = PsGetCurrentThread();
2553 
2554  //
2555  // Get the PTE and PTE for the address, and lock the working set
2556  // If this was a VAD for a MEM_COMMIT allocation, also figure out where the
2557  // commited range ends so that we can do the right accounting.
2558  //
2559  PointerPde = MiAddressToPde(StartingAddress);
2560  PointerPte = MiAddressToPte(StartingAddress);
2561  if (Vad->u.VadFlags.MemCommit) CommitPte = MiAddressToPte(Vad->EndingVpn << PAGE_SHIFT);
2562  MiLockProcessWorkingSetUnsafe(Process, CurrentThread);
2563 
2564  //
2565  // Make the PDE valid, and now loop through each page's worth of data
2566  //
2568  while (PointerPte <= EndingPte)
2569  {
2570  //
2571  // Check if we've crossed a PDE boundary
2572  //
2573  if (MiIsPteOnPdeBoundary(PointerPte))
2574  {
2575  //
2576  // Get the new PDE and flush the valid PTEs we had built up until
2577  // now. This helps reduce the amount of TLB flushing we have to do.
2578  // Note that Windows does a much better job using timestamps and
2579  // such, and does not flush the entire TLB all the time, but right
2580  // now we have bigger problems to worry about than TLB flushing.
2581  //
2582  PointerPde = MiAddressToPde(StartingAddress);
2583  if (PteCount)
2584  {
2585  MiProcessValidPteList(ValidPteList, PteCount);
2586  PteCount = 0;
2587  }
2588 
2589  //
2590  // Make this PDE valid
2591  //
2593  }
2594 
2595  //
2596  // Read this PTE. It might be active or still demand-zero.
2597  //
2598  PteContents = *PointerPte;
2599  if (PteContents.u.Long)
2600  {
2601  //
2602  // The PTE is active. It might be valid and in a working set, or
2603  // it might be a prototype PTE or paged out or even in transition.
2604  //
2605  if (PointerPte->u.Long == MmDecommittedPte.u.Long)
2606  {
2607  //
2608  // It's already decommited, so there's nothing for us to do here
2609  //
2610  CommitReduction++;
2611  }
2612  else
2613  {
2614  //
2615  // Remove it from the counters, and check if it was valid or not
2616  //
2617  //Process->NumberOfPrivatePages--;
2618  if (PteContents.u.Hard.Valid)
2619  {
2620  //
2621  // It's valid. At this point make sure that it is not a ROS
2622  // PFN. Also, we don't support ProtoPTEs in this code path.
2623  //
2624  Pfn1 = MiGetPfnEntry(PteContents.u.Hard.PageFrameNumber);
2625  ASSERT(MI_IS_ROS_PFN(Pfn1) == FALSE);
2626  ASSERT(Pfn1->u3.e1.PrototypePte == FALSE);
2627 
2628  //
2629  // Flush any pending PTEs that we had not yet flushed, if our
2630  // list has gotten too big, then add this PTE to the flush list.
2631  //
2632  if (PteCount == 256)
2633  {
2634  MiProcessValidPteList(ValidPteList, PteCount);
2635  PteCount = 0;
2636  }
2637  ValidPteList[PteCount++] = PointerPte;
2638  }
2639  else
2640  {
2641  //
2642  // We do not support any of these other scenarios at the moment
2643  //
2644  ASSERT(PteContents.u.Soft.Prototype == 0);
2645  ASSERT(PteContents.u.Soft.Transition == 0);
2646  ASSERT(PteContents.u.Soft.PageFileHigh == 0);
2647 
2648  //
2649  // So the only other possibility is that it is still a demand
2650  // zero PTE, in which case we undo the accounting we did
2651  // earlier and simply make the page decommitted.
2652  //
2653  //Process->NumberOfPrivatePages++;
2655  }
2656  }
2657  }
2658  else
2659  {
2660  //
2661  // This used to be a zero PTE and it no longer is, so we must add a
2662  // reference to the pagetable.
2663  //
2664  MiIncrementPageTableReferences(StartingAddress);
2665 
2666  //
2667  // Next, we account for decommitted PTEs and make the PTE as such
2668  //
2669  if (PointerPte > CommitPte) CommitReduction++;
2671  }
2672 
2673  //
2674  // Move to the next PTE and the next address
2675  //
2676  PointerPte++;
2677  StartingAddress = (PVOID)((ULONG_PTR)StartingAddress + PAGE_SIZE);
2678  }
2679 
2680  //
2681  // Flush any dangling PTEs from the loop in the last page table, and then
2682  // release the working set and return the commit reduction accounting.
2683  //
2684  if (PteCount) MiProcessValidPteList(ValidPteList, PteCount);
2685  MiUnlockProcessWorkingSetUnsafe(Process, CurrentThread);
2686  return CommitReduction;
2687 }
2688 
2689 /* PUBLIC FUNCTIONS ***********************************************************/
2690 
2691 /*
2692  * @unimplemented
2693  */
2694 PVOID
2695 NTAPI
2697 {
2698  UNIMPLEMENTED;
2699  return 0;
2700 }
2701 
2702 /*
2703  * @unimplemented
2704  */
2705 PVOID
2706 NTAPI
2708  IN SIZE_T Length,
2709  IN ULONG Mode)
2710 {
2711  static ULONG Warn; if (!Warn++) UNIMPLEMENTED;
2712  return Address;
2713 }
2714 
2715 /*
2716  * @unimplemented
2717  */
2718 VOID
2719 NTAPI
2721 {
2722  static ULONG Warn; if (!Warn++) UNIMPLEMENTED;
2723 }
2724 
2725 /* SYSTEM CALLS ***************************************************************/
2726 
2727 NTSTATUS
2728 NTAPI
2731  OUT PVOID Buffer,
2732  IN SIZE_T NumberOfBytesToRead,
2733  OUT PSIZE_T NumberOfBytesRead OPTIONAL)
2734 {
2738  SIZE_T BytesRead = 0;
2739  PAGED_CODE();
2740 
2741  //
2742  // Check if we came from user mode
2743  //
2744  if (PreviousMode != KernelMode)
2745  {
2746  //
2747  // Validate the read addresses
2748  //
2749  if ((((ULONG_PTR)BaseAddress + NumberOfBytesToRead) < (ULONG_PTR)BaseAddress) ||
2750  (((ULONG_PTR)Buffer + NumberOfBytesToRead) < (ULONG_PTR)Buffer) ||
2751  (((ULONG_PTR)BaseAddress + NumberOfBytesToRead) > MmUserProbeAddress) ||
2752  (((ULONG_PTR)Buffer + NumberOfBytesToRead) > MmUserProbeAddress))
2753  {
2754  //
2755  // Don't allow to write into kernel space
2756  //
2757  return STATUS_ACCESS_VIOLATION;
2758  }
2759 
2760  //
2761  // Enter SEH for probe
2762  //
2763  _SEH2_TRY
2764  {
2765  //
2766  // Probe the output value
2767  //
2768  if (NumberOfBytesRead) ProbeForWriteSize_t(NumberOfBytesRead);
2769  }
2771  {
2772  //
2773  // Get exception code
2774  //
2776  }
2777  _SEH2_END;
2778  }
2779 
2780  //
2781  // Don't do zero-byte transfers
2782  //
2783  if (NumberOfBytesToRead)
2784  {
2785  //
2786  // Reference the process
2787  //
2790  PsProcessType,
2791  PreviousMode,
2792  (PVOID*)(&Process),
2793  NULL);
2794  if (NT_SUCCESS(Status))
2795  {
2796  //
2797  // Do the copy
2798  //
2800  BaseAddress,
2802  Buffer,
2803  NumberOfBytesToRead,
2804  PreviousMode,
2805  &BytesRead);
2806 
2807  //
2808  // Dereference the process
2809  //
2811  }
2812  }
2813 
2814  //
2815  // Check if the caller sent this parameter
2816  //
2817  if (NumberOfBytesRead)
2818  {
2819  //
2820  // Enter SEH to guard write
2821  //
2822  _SEH2_TRY
2823  {
2824  //
2825  // Return the number of bytes read
2826  //
2827  *NumberOfBytesRead = BytesRead;
2828  }
2830  {
2831  }
2832  _SEH2_END;
2833  }
2834 
2835  //
2836  // Return status
2837  //
2838  return Status;
2839 }
2840 
2841 NTSTATUS
2842 NTAPI
2845  IN PVOID Buffer,
2846  IN SIZE_T NumberOfBytesToWrite,
2847  OUT PSIZE_T NumberOfBytesWritten OPTIONAL)
2848 {
2852  SIZE_T BytesWritten = 0;
2853  PAGED_CODE();
2854 
2855  //
2856  // Check if we came from user mode
2857  //
2858  if (PreviousMode != KernelMode)
2859  {
2860  //
2861  // Validate the read addresses
2862  //
2863  if ((((ULONG_PTR)BaseAddress + NumberOfBytesToWrite) < (ULONG_PTR)BaseAddress) ||
2864  (((ULONG_PTR)Buffer + NumberOfBytesToWrite) < (ULONG_PTR)Buffer) ||
2865  (((ULONG_PTR)BaseAddress + NumberOfBytesToWrite) > MmUserProbeAddress) ||
2866  (((ULONG_PTR)Buffer + NumberOfBytesToWrite) > MmUserProbeAddress))
2867  {
2868  //
2869  // Don't allow to write into kernel space
2870  //
2871  return STATUS_ACCESS_VIOLATION;
2872  }
2873 
2874  //
2875  // Enter SEH for probe
2876  //
2877  _SEH2_TRY
2878  {
2879  //
2880  // Probe the output value
2881  //
2882  if (NumberOfBytesWritten) ProbeForWriteSize_t(NumberOfBytesWritten);
2883  }
2885  {
2886  //
2887  // Get exception code
2888  //
2890  }
2891  _SEH2_END;
2892  }
2893 
2894  //
2895  // Don't do zero-byte transfers
2896  //
2897  if (NumberOfBytesToWrite)
2898  {
2899  //
2900  // Reference the process
2901  //
2904  PsProcessType,
2905  PreviousMode,
2906  (PVOID*)&Process,
2907  NULL);
2908  if (NT_SUCCESS(Status))
2909  {
2910  //
2911  // Do the copy
2912  //
2914  Buffer,
2915  Process,
2916  BaseAddress,
2917  NumberOfBytesToWrite,
2918  PreviousMode,
2919  &BytesWritten);
2920 
2921  //
2922  // Dereference the process
2923  //
2925  }
2926  }
2927 
2928  //
2929  // Check if the caller sent this parameter
2930  //
2931  if (NumberOfBytesWritten)
2932  {
2933  //
2934  // Enter SEH to guard write
2935  //
2936  _SEH2_TRY
2937  {
2938  //
2939  // Return the number of bytes written
2940  //
2941  *NumberOfBytesWritten = BytesWritten;
2942  }
2944  {
2945  }
2946  _SEH2_END;
2947  }
2948 
2949  //
2950  // Return status
2951  //
2952  return Status;
2953 }
2954 
2955 NTSTATUS
2956 NTAPI
2959  _In_ SIZE_T FlushSize)
2960 {
2963  NTSTATUS Status;
2964  PAGED_CODE();
2965 
2966  /* Is a base address given? */
2967  if (BaseAddress != NULL)
2968  {
2969  /* If the requested size is 0, there is nothing to do */
2970  if (FlushSize == 0)
2971  {
2972  return STATUS_SUCCESS;
2973  }
2974 
2975  /* Is this a user mode call? */
2976  if (ExGetPreviousMode() != KernelMode)
2977  {
2978  /* Make sure the base address is in user space */
2980  {
2981  DPRINT1("Invalid BaseAddress 0x%p\n", BaseAddress);
2982  return STATUS_ACCESS_VIOLATION;
2983  }
2984  }
2985  }
2986 
2987  /* Is another process requested? */
2989  {
2990  /* Reference the process */
2993  PsProcessType,
2995  (PVOID*)&Process,
2996  NULL);
2997  if (!NT_SUCCESS(Status))
2998  {
2999  DPRINT1("Failed to reference the process %p\n", ProcessHandle);
3000  return Status;
3001  }
3002 
3003  /* Attach to the process */
3005  }
3006 
3007  /* Forward to Ke */
3008  KeSweepICache(BaseAddress, FlushSize);
3009 
3010  /* Check if we attached */
3012  {
3013  /* Detach from the process and dereference it */
3016  }
3017 
3018  /* All done, return to caller */
3019  return STATUS_SUCCESS;
3020 }
3021 
3022 NTSTATUS
3023 NTAPI
3025  IN OUT PVOID *UnsafeBaseAddress,
3026  IN OUT SIZE_T *UnsafeNumberOfBytesToProtect,
3027  IN ULONG NewAccessProtection,
3028  OUT PULONG UnsafeOldAccessProtection)
3029 {
3031  ULONG OldAccessProtection;
3032  ULONG Protection;
3035  SIZE_T NumberOfBytesToProtect = 0;
3037  NTSTATUS Status;
3040  PAGED_CODE();
3041 
3042  //
3043  // Check for valid protection flags
3044  //
3045  Protection = NewAccessProtection & ~(PAGE_GUARD|PAGE_NOCACHE);
3046  if (Protection != PAGE_NOACCESS &&
3047  Protection != PAGE_READONLY &&
3048  Protection != PAGE_READWRITE &&
3049  Protection != PAGE_WRITECOPY &&
3050  Protection != PAGE_EXECUTE &&
3051  Protection != PAGE_EXECUTE_READ &&
3052  Protection != PAGE_EXECUTE_READWRITE &&
3053  Protection != PAGE_EXECUTE_WRITECOPY)
3054  {
3055  //
3056  // Fail
3057  //
3059  }
3060 
3061  //
3062  // Check if we came from user mode
3063  //
3064  if (PreviousMode != KernelMode)
3065  {
3066  //
3067  // Enter SEH for probing
3068  //
3069  _SEH2_TRY
3070  {
3071  //
3072  // Validate all outputs
3073  //
3074  ProbeForWritePointer(UnsafeBaseAddress);
3075  ProbeForWriteSize_t(UnsafeNumberOfBytesToProtect);
3076  ProbeForWriteUlong(UnsafeOldAccessProtection);
3077 
3078  //
3079  // Capture them
3080  //
3081  BaseAddress = *UnsafeBaseAddress;
3082  NumberOfBytesToProtect = *UnsafeNumberOfBytesToProtect;
3083  }
3085  {
3086  //
3087  // Get exception code
3088  //
3090  }
3091  _SEH2_END;
3092  }
3093  else
3094  {
3095  //
3096  // Capture directly
3097  //
3098  BaseAddress = *UnsafeBaseAddress;
3099  NumberOfBytesToProtect = *UnsafeNumberOfBytesToProtect;
3100  }
3101 
3102  //
3103  // Catch illegal base address
3104  //
3106 
3107  //
3108  // Catch illegal region size
3109  //
3110  if ((MmUserProbeAddress - (ULONG_PTR)BaseAddress) < NumberOfBytesToProtect)
3111  {
3112  //
3113  // Fail
3114  //
3116  }
3117 
3118  //
3119  // 0 is also illegal
3120  //
3121  if (!NumberOfBytesToProtect) return STATUS_INVALID_PARAMETER_3;
3122 
3123  //
3124  // Get a reference to the process
3125  //
3128  PsProcessType,
3129  PreviousMode,
3130  (PVOID*)(&Process),
3131  NULL);
3132  if (!NT_SUCCESS(Status)) return Status;
3133 
3134  //
3135  // Check if we should attach
3136  //
3137  if (CurrentProcess != Process)
3138  {
3139  //
3140  // Do it
3141  //
3143  Attached = TRUE;
3144  }
3145 
3146  //
3147  // Do the actual work
3148  //
3150  &BaseAddress,
3151  &NumberOfBytesToProtect,
3152  NewAccessProtection,
3153  &OldAccessProtection);
3154 
3155  //
3156  // Detach if needed
3157  //
3159 
3160  //
3161  // Release reference
3162  //
3164 
3165  //
3166  // Enter SEH to return data
3167  //
3168  _SEH2_TRY
3169  {
3170  //
3171  // Return data to user
3172  //
3173  *UnsafeOldAccessProtection = OldAccessProtection;
3174  *UnsafeBaseAddress = BaseAddress;
3175  *UnsafeNumberOfBytesToProtect = NumberOfBytesToProtect;
3176  }
3178  {
3179  }
3180  _SEH2_END;
3181 
3182  //
3183  // Return status
3184  //
3185  return Status;
3186 }
3187 
3189 BOOLEAN
3191  PMMPFN Pfn1,
3192  ULONG LockType)
3193 {
3194  // HACK until we have proper WSLIST support
3195  PMMWSLE Wsle = &Pfn1->Wsle;
3196 
3197  if ((LockType & MAP_PROCESS) && (Wsle->u1.e1.LockedInWs))
3198  return TRUE;
3199  if ((LockType & MAP_SYSTEM) && (Wsle->u1.e1.LockedInMemory))
3200  return TRUE;
3201 
3202  return FALSE;
3203 }
3204 
3206 VOID
3208  PMMPFN Pfn1,
3209  ULONG LockType)
3210 {
3211  // HACK until we have proper WSLIST support
3212  PMMWSLE Wsle = &Pfn1->Wsle;
3213 
3214  if (!Wsle->u1.e1.LockedInWs &&
3215  !Wsle->u1.e1.LockedInMemory)
3216  {
3218  }
3219 
3220  if (LockType & MAP_PROCESS)
3221  Wsle->u1.e1.LockedInWs = 1;
3222  if (LockType & MAP_SYSTEM)
3223  Wsle->u1.e1.LockedInMemory = 1;
3224 }
3225 
3227 VOID
3229  PMMPFN Pfn1,
3230  ULONG LockType)
3231 {
3232  // HACK until we have proper WSLIST support
3233  PMMWSLE Wsle = &Pfn1->Wsle;
3234 
3235  if (LockType & MAP_PROCESS)
3236  Wsle->u1.e1.LockedInWs = 0;
3237  if (LockType & MAP_SYSTEM)
3238  Wsle->u1.e1.LockedInMemory = 0;
3239 
3240  if (!Wsle->u1.e1.LockedInWs &&
3241  !Wsle->u1.e1.LockedInMemory)
3242  {
3244  }
3245 }
3246 
3247 static
3248 NTSTATUS
3252  _Inout_ PVOID *EndAddress)
3253 
3254 {
3255  PMMVAD Vad;
3256  PVOID CurrentVa;
3257 
3258  /* Get the base address and align the start address */
3259  *EndAddress = (PUCHAR)*BaseAddress + *RegionSize;
3260  *EndAddress = ALIGN_UP_POINTER_BY(*EndAddress, PAGE_SIZE);
3262 
3263  /* First loop and check all VADs */
3264  CurrentVa = *BaseAddress;
3265  while (CurrentVa < *EndAddress)
3266  {
3267  /* Get VAD */
3268  Vad = MiLocateAddress(CurrentVa);
3269  if (Vad == NULL)
3270  {
3272  return STATUS_ACCESS_VIOLATION;
3273  }
3274 
3275  /* Check VAD type */
3276  if ((Vad->u.VadFlags.VadType != VadNone) &&
3277  (Vad->u.VadFlags.VadType != VadImageMap) &&
3278  (Vad->u.VadFlags.VadType != VadWriteWatch))
3279  {
3280  *EndAddress = CurrentVa;
3281  *RegionSize = (PUCHAR)*EndAddress - (PUCHAR)*BaseAddress;
3283  }
3284 
3285  CurrentVa = (PVOID)((Vad->EndingVpn + 1) << PAGE_SHIFT);
3286  }
3287 
3288  *RegionSize = (PUCHAR)*EndAddress - (PUCHAR)*BaseAddress;
3289  return STATUS_SUCCESS;
3290 }
3291 
3292 static
3293 NTSTATUS
3297  IN ULONG MapType)
3298 {
3301  PVOID CurrentVa, EndAddress;
3302  PMMPTE PointerPte, LastPte;
3303  PMMPDE PointerPde;
3304 #if (_MI_PAGING_LEVELS >= 3)
3305  PMMPDE PointerPpe;
3306 #endif
3307 #if (_MI_PAGING_LEVELS == 4)
3308  PMMPDE PointerPxe;
3309 #endif
3310  PMMPFN Pfn1;
3311  NTSTATUS Status, TempStatus;
3312 
3313  /* Lock the address space */
3316 
3317  /* Make sure we still have an address space */
3319  if (CurrentProcess->VmDeleted)
3320  {
3322  goto Cleanup;
3323  }
3324 
3325  /* Check the VADs in the requested range */
3327  if (!NT_SUCCESS(Status))
3328  {
3329  goto Cleanup;
3330  }
3331 
3332  /* Enter SEH for probing */
3333  _SEH2_TRY
3334  {
3335  /* Loop all pages and probe them */
3336  CurrentVa = *BaseAddress;
3337  while (CurrentVa < EndAddress)
3338  {
3339  (void)(*(volatile CHAR*)CurrentVa);
3340  CurrentVa = (PUCHAR)CurrentVa + PAGE_SIZE;
3341  }
3342  }
3344  {
3346  goto Cleanup;
3347  }
3348  _SEH2_END;
3349 
3350  /* All pages were accessible, since we hold the address space lock, nothing
3351  can be de-committed. Assume success for now. */
3353 
3354  /* Get the PTE and PDE */
3355  PointerPte = MiAddressToPte(*BaseAddress);
3356  PointerPde = MiAddressToPde(*BaseAddress);
3357 #if (_MI_PAGING_LEVELS >= 3)
3358  PointerPpe = MiAddressToPpe(*BaseAddress);
3359 #endif
3360 #if (_MI_PAGING_LEVELS == 4)
3361  PointerPxe = MiAddressToPxe(*BaseAddress);
3362 #endif
3363 
3364  /* Get the last PTE */
3365  LastPte = MiAddressToPte((PVOID)((ULONG_PTR)EndAddress - 1));
3366 
3367  /* Lock the process working set */
3369 
3370  /* Loop the pages */
3371  do
3372  {
3373  /* Check for a page that is not accessible */
3374  while (
3375 #if (_MI_PAGING_LEVELS == 4)
3376  (PointerPxe->u.Hard.Valid == 0) ||
3377 #endif
3378 #if (_MI_PAGING_LEVELS >= 3)
3379  (PointerPpe->u.Hard.Valid == 0) ||
3380 #endif
3381  (PointerPde->u.Hard.Valid == 0) ||
3382  (PointerPte->u.Hard.Valid == 0))
3383  {
3384  /* Release process working set */
3386 
3387  /* Access the page */
3388  CurrentVa = MiPteToAddress(PointerPte);
3389 
3390  //HACK: Pass a placeholder TrapInformation so the fault handler knows we're unlocked
3391  TempStatus = MmAccessFault(TRUE, CurrentVa, KernelMode, (PVOID)(ULONG_PTR)0xBADBADA3BADBADA3ULL);
3392  if (!NT_SUCCESS(TempStatus))
3393  {
3394  // This should only happen, when remote backing storage is not accessible
3395  ASSERT(FALSE);
3396  Status = TempStatus;
3397  goto Cleanup;
3398  }
3399 
3400  /* Lock the process working set */
3402  }
3403 
3404  /* Get the PFN */
3405  Pfn1 = MiGetPfnEntry(PFN_FROM_PTE(PointerPte));
3406  ASSERT(Pfn1 != NULL);
3407 
3408  /* Check the previous lock status */
3409  if (MI_IS_LOCKED_VA(Pfn1, MapType))
3410  {
3412  }
3413 
3414  /* Lock it */
3415  MI_LOCK_VA(Pfn1, MapType);
3416 
3417  /* Go to the next PTE */
3418  PointerPte++;
3419 
3420  /* Check if we're on a PDE boundary */
3421  if (MiIsPteOnPdeBoundary(PointerPte)) PointerPde++;
3422 #if (_MI_PAGING_LEVELS >= 3)
3423  if (MiIsPteOnPpeBoundary(PointerPte)) PointerPpe++;
3424 #endif
3425 #if (_MI_PAGING_LEVELS == 4)
3426  if (MiIsPteOnPxeBoundary(PointerPte)) PointerPxe++;
3427 #endif
3428  } while (PointerPte <= LastPte);
3429 
3430  /* Release process working set */
3432 
3433 Cleanup:
3434  /* Unlock address space */
3436 
3437  return Status;
3438 }
3439 
3440 NTSTATUS
3441 NTAPI
3444  IN OUT PSIZE_T NumberOfBytesToLock,
3445  IN ULONG MapType)
3446 {
3449  NTSTATUS Status;
3453  PVOID CapturedBaseAddress;
3454  SIZE_T CapturedBytesToLock;
3455  PAGED_CODE();
3456 
3457  //
3458  // Validate flags
3459  //
3460  if ((MapType & ~(MAP_PROCESS | MAP_SYSTEM)))
3461  {
3462  //
3463  // Invalid set of flags
3464  //
3465  return STATUS_INVALID_PARAMETER;
3466  }
3467 
3468  //
3469  // At least one flag must be specified
3470  //
3471  if (!(MapType & (MAP_PROCESS | MAP_SYSTEM)))
3472  {
3473  //
3474  // No flag given
3475  //
3476  return STATUS_INVALID_PARAMETER;
3477  }
3478 
3479  //
3480  // Enter SEH for probing
3481  //
3482  _SEH2_TRY
3483  {
3484  //
3485  // Validate output data
3486  //
3488  ProbeForWriteSize_t(NumberOfBytesToLock);
3489 
3490  //
3491  // Capture it
3492  //
3493  CapturedBaseAddress = *BaseAddress;
3494  CapturedBytesToLock = *NumberOfBytesToLock;
3495  }
3497  {
3498  //
3499  // Get exception code
3500  //
3502  }
3503  _SEH2_END;
3504 
3505  //
3506  // Catch illegal base address
3507  //
3508  if (CapturedBaseAddress > MM_HIGHEST_USER_ADDRESS) return STATUS_INVALID_PARAMETER;
3509 
3510  //
3511  // Catch illegal region size
3512  //
3513  if ((MmUserProbeAddress - (ULONG_PTR)CapturedBaseAddress) < CapturedBytesToLock)
3514  {
3515  //
3516  // Fail
3517  //
3518  return STATUS_INVALID_PARAMETER;
3519  }
3520 
3521  //
3522  // 0 is also illegal
3523  //
3524  if (!CapturedBytesToLock) return STATUS_INVALID_PARAMETER;
3525 
3526  //
3527  // Get a reference to the process
3528  //
3531  PsProcessType,
3532  PreviousMode,
3533  (PVOID*)(&Process),
3534  NULL);
3535  if (!NT_SUCCESS(Status)) return Status;
3536 
3537  //
3538  // Check if this is is system-mapped
3539  //
3540  if (MapType & MAP_SYSTEM)
3541  {
3542  //
3543  // Check for required privilege
3544  //
3546  {
3547  //
3548  // Fail: Don't have it
3549  //
3552  }
3553  }
3554 
3555  //
3556  // Check if we should attach
3557  //
3558  if (CurrentProcess != Process)
3559  {
3560  //
3561  // Do it
3562  //
3564  Attached = TRUE;
3565  }
3566 
3567  //
3568  // Call the internal function
3569  //
3570  Status = MiLockVirtualMemory(&CapturedBaseAddress,
3571  &CapturedBytesToLock,
3572  MapType);
3573 
3574  //
3575  // Detach if needed
3576  //
3578 
3579  //
3580  // Release reference
3581  //
3583 
3584  //
3585  // Enter SEH to return data
3586  //
3587  _SEH2_TRY
3588  {
3589  //
3590  // Return data to user
3591  //
3592  *BaseAddress = CapturedBaseAddress;
3593  *NumberOfBytesToLock = CapturedBytesToLock;
3594  }
3596  {
3597  //
3598  // Get exception code
3599  //
3601  }
3602  _SEH2_END;
3603 
3604  //
3605  // Return status
3606  //
3607  return Status;
3608 }
3609 
3610 
3611 static
3612 NTSTATUS
3616  IN ULONG MapType)
3617 {
3620  PVOID EndAddress;
3621  PMMPTE PointerPte, LastPte;
3622  PMMPDE PointerPde;
3623 #if (_MI_PAGING_LEVELS >= 3)
3624  PMMPDE PointerPpe;
3625 #endif
3626 #if (_MI_PAGING_LEVELS == 4)
3627  PMMPDE PointerPxe;
3628 #endif
3629  PMMPFN Pfn1;
3630  NTSTATUS Status;
3631 
3632  /* Lock the address space */
3635 
3636  /* Make sure we still have an address space */
3638  if (CurrentProcess->VmDeleted)
3639  {
3641  goto Cleanup;
3642  }
3643 
3644  /* Check the VADs in the requested range */
3646 
3647  /* Note: only bail out, if we hit an area without a VAD. If we hit an
3648  incompatible VAD we continue, like Windows does */
3650  {
3652  goto Cleanup;
3653  }
3654 
3655  /* Get the PTE and PDE */
3656  PointerPte = MiAddressToPte(*BaseAddress);
3657  PointerPde = MiAddressToPde(*BaseAddress);
3658 #if (_MI_PAGING_LEVELS >= 3)
3659  PointerPpe = MiAddressToPpe(*BaseAddress);
3660 #endif
3661 #if (_MI_PAGING_LEVELS == 4)
3662  PointerPxe = MiAddressToPxe(*BaseAddress);
3663 #endif
3664 
3665  /* Get the last PTE */
3666  LastPte = MiAddressToPte((PVOID)((ULONG_PTR)EndAddress - 1));
3667 
3668  /* Lock the process working set */
3670 
3671  /* Loop the pages */
3672  do
3673  {
3674  /* Check for a page that is not present */
3675  if (
3676 #if (_MI_PAGING_LEVELS == 4)
3677  (PointerPxe->u.Hard.Valid == 0) ||
3678 #endif
3679 #if (_MI_PAGING_LEVELS >= 3)
3680  (PointerPpe->u.Hard.Valid == 0) ||
3681 #endif
3682  (PointerPde->u.Hard.Valid == 0) ||
3683  (PointerPte->u.Hard.Valid == 0))
3684  {
3685  /* Remember it, but keep going */
3687  }
3688  else
3689  {
3690  /* Get the PFN */
3691  Pfn1 = MiGetPfnEntry(PFN_FROM_PTE(PointerPte));
3692  ASSERT(Pfn1 != NULL);
3693 
3694  /* Check if all of the requested locks are present */
3695  if (((MapType & MAP_SYSTEM) && !MI_IS_LOCKED_VA(Pfn1, MAP_SYSTEM)) ||
3696  ((MapType & MAP_PROCESS) && !MI_IS_LOCKED_VA(Pfn1, MAP_PROCESS)))
3697  {
3698  /* Remember it, but keep going */
3700 
3701  /* Check if no lock is present */
3702  if (!MI_IS_LOCKED_VA(Pfn1, MAP_PROCESS | MAP_SYSTEM))
3703  {
3704  DPRINT1("FIXME: Should remove the page from WS\n");
3705  }
3706  }
3707  }
3708 
3709  /* Go to the next PTE */
3710  PointerPte++;
3711 
3712  /* Check if we're on a PDE boundary */
3713  if (MiIsPteOnPdeBoundary(PointerPte)) PointerPde++;
3714 #if (_MI_PAGING_LEVELS >= 3)
3715  if (MiIsPteOnPpeBoundary(PointerPte)) PointerPpe++;
3716 #endif
3717 #if (_MI_PAGING_LEVELS == 4)
3718  if (MiIsPteOnPxeBoundary(PointerPte)) PointerPxe++;
3719 #endif
3720  } while (PointerPte <= LastPte);
3721 
3722  /* Check if we hit a page that was not locked */
3723  if (Status == STATUS_NOT_LOCKED)
3724  {
3725  goto CleanupWithWsLock;
3726  }
3727 
3728  /* All pages in the region were locked, so unlock them all */
3729 
3730  /* Get the PTE and PDE */
3731  PointerPte = MiAddressToPte(*BaseAddress);
3732  PointerPde = MiAddressToPde(*BaseAddress);
3733 #if (_MI_PAGING_LEVELS >= 3)
3734  PointerPpe = MiAddressToPpe(*BaseAddress);
3735 #endif
3736 #if (_MI_PAGING_LEVELS == 4)
3737  PointerPxe = MiAddressToPxe(*BaseAddress);
3738 #endif
3739 
3740  /* Loop the pages */
3741  do
3742  {
3743  /* Unlock it */
3744  Pfn1 = MiGetPfnEntry(PFN_FROM_PTE(PointerPte));
3745  MI_UNLOCK_VA(Pfn1, MapType);
3746 
3747  /* Go to the next PTE */
3748  PointerPte++;
3749 
3750  /* Check if we're on a PDE boundary */
3751  if (MiIsPteOnPdeBoundary(PointerPte)) PointerPde++;
3752 #if (_MI_PAGING_LEVELS >= 3)
3753  if (MiIsPteOnPpeBoundary(PointerPte)) PointerPpe++;
3754 #endif
3755 #if (_MI_PAGING_LEVELS == 4)
3756  if (MiIsPteOnPxeBoundary(PointerPte)) PointerPxe++;
3757 #endif
3758  } while (PointerPte <= LastPte);
3759 
3760  /* Everything is done */
3762 
3763 CleanupWithWsLock:
3764 
3765  /* Release process working set */
3767 
3768 Cleanup:
3769  /* Unlock address space */
3771 
3772  return Status;
3773 }
3774 
3775 
3776 NTSTATUS
3777 NTAPI
3780  IN OUT PSIZE_T NumberOfBytesToUnlock,
3781  IN ULONG MapType)
3782 {
3785  NTSTATUS Status;
3789  PVOID CapturedBaseAddress;
3790  SIZE_T CapturedBytesToUnlock;
3791  PAGED_CODE();
3792 
3793  //
3794  // Validate flags
3795  //
3796  if ((MapType & ~(MAP_PROCESS | MAP_SYSTEM)))
3797  {
3798  //
3799  // Invalid set of flags
3800  //
3801  return STATUS_INVALID_PARAMETER;
3802  }
3803 
3804  //
3805  // At least one flag must be specified
3806  //
3807  if (!(MapType & (MAP_PROCESS | MAP_SYSTEM)))
3808  {
3809  //
3810  // No flag given
3811  //
3812  return STATUS_INVALID_PARAMETER;
3813  }
3814 
3815  //
3816  // Enter SEH for probing
3817  //
3818  _SEH2_TRY
3819  {
3820  //
3821  // Validate output data
3822  //
3824  ProbeForWriteSize_t(NumberOfBytesToUnlock);
3825 
3826  //
3827  // Capture it
3828  //
3829  CapturedBaseAddress = *BaseAddress;
3830  CapturedBytesToUnlock = *NumberOfBytesToUnlock;
3831  }
3833  {
3834  //
3835  // Get exception code
3836  //
3838  }
3839  _SEH2_END;
3840 
3841  //
3842  // Catch illegal base address
3843  //
3844  if (CapturedBaseAddress > MM_HIGHEST_USER_ADDRESS) return STATUS_INVALID_PARAMETER;
3845 
3846  //
3847  // Catch illegal region size
3848  //
3849  if ((MmUserProbeAddress - (ULONG_PTR)CapturedBaseAddress) < CapturedBytesToUnlock)
3850  {
3851  //
3852  // Fail
3853  //
3854  return STATUS_INVALID_PARAMETER;
3855  }
3856 
3857  //
3858  // 0 is also illegal
3859  //
3860  if (!CapturedBytesToUnlock) return STATUS_INVALID_PARAMETER;
3861 
3862  //
3863  // Get a reference to the process
3864  //
3867  PsProcessType,
3868  PreviousMode,
3869  (PVOID*)(&Process),
3870  NULL);
3871  if (!NT_SUCCESS(Status)) return Status;
3872 
3873  //
3874  // Check if this is is system-mapped
3875  //
3876  if (MapType & MAP_SYSTEM)
3877  {
3878  //
3879  // Check for required privilege
3880  //
3882  {
3883  //
3884  // Fail: Don't have it
3885  //
3888  }
3889  }
3890 
3891  //
3892  // Check if we should attach
3893  //
3894  if (CurrentProcess != Process)
3895  {
3896  //
3897  // Do it
3898  //
3900  Attached = TRUE;
3901  }
3902 
3903  //
3904  // Call the internal function
3905  //
3906  Status = MiUnlockVirtualMemory(&CapturedBaseAddress,
3907  &CapturedBytesToUnlock,
3908  MapType);
3909 
3910  //
3911  // Detach if needed
3912  //
3914 
3915  //
3916  // Release reference
3917  //
3919 
3920  //
3921  // Enter SEH to return data
3922  //
3923  _SEH2_TRY
3924  {
3925  //
3926  // Return data to user
3927  //
3928  *BaseAddress = CapturedBaseAddress;
3929  *NumberOfBytesToUnlock = CapturedBytesToUnlock;
3930  }
3932  {
3933  //
3934  // Get exception code
3935  //
3937  }
3938  _SEH2_END;
3939 
3940  //
3941  // Return status
3942  //
3943  return STATUS_SUCCESS;
3944 }
3945 
3946 NTSTATUS
3947 NTAPI
3950  IN OUT PSIZE_T NumberOfBytesToFlush,
3952 {
3954  NTSTATUS Status;
3956  PVOID CapturedBaseAddress;
3957  SIZE_T CapturedBytesToFlush;
3958  IO_STATUS_BLOCK LocalStatusBlock;
3959  PAGED_CODE();
3960 
3961  //
3962  // Check if we came from user mode
3963  //
3964  if (PreviousMode != KernelMode)
3965  {
3966  //
3967  // Enter SEH for probing
3968  //
3969  _SEH2_TRY
3970  {
3971  //
3972  // Validate all outputs
3973  //
3975  ProbeForWriteSize_t(NumberOfBytesToFlush);
3977 
3978  //
3979  // Capture them
3980  //
3981  CapturedBaseAddress = *BaseAddress;
3982  CapturedBytesToFlush = *NumberOfBytesToFlush;
3983  }
3985  {
3986  //
3987  // Get exception code
3988  //
3990  }
3991  _SEH2_END;
3992  }
3993  else
3994  {
3995  //
3996  // Capture directly
3997  //
3998  CapturedBaseAddress = *BaseAddress;
3999  CapturedBytesToFlush = *NumberOfBytesToFlush;
4000  }
4001 
4002  //
4003  // Catch illegal base address
4004  //
4005  if (CapturedBaseAddress > MM_HIGHEST_USER_ADDRESS) return STATUS_INVALID_PARAMETER;
4006 
4007  //
4008  // Catch illegal region size
4009  //
4010  if ((MmUserProbeAddress - (ULONG_PTR)CapturedBaseAddress) < CapturedBytesToFlush)
4011  {
4012  //
4013  // Fail
4014  //
4015  return STATUS_INVALID_PARAMETER;
4016  }
4017 
4018  //
4019  // Get a reference to the process
4020  //
4023  PsProcessType,
4024  PreviousMode,
4025  (PVOID*)(&Process),
4026  NULL);
4027  if (!NT_SUCCESS(Status)) return Status;
4028 
4029  //
4030  // Do it
4031  //
4033  &CapturedBaseAddress,
4034  &CapturedBytesToFlush,
4035  &LocalStatusBlock);
4036 
4037  //
4038  // Release reference
4039  //
4041 
4042  //
4043  // Enter SEH to return data
4044  //
4045  _SEH2_TRY
4046  {
4047  //
4048  // Return data to user
4049  //
4050  *BaseAddress = PAGE_ALIGN(CapturedBaseAddress);
4051  *NumberOfBytesToFlush = 0;
4052  *IoStatusBlock = LocalStatusBlock;
4053  }
4055  {
4056  }
4057  _SEH2_END;
4058 
4059  //
4060  // Return status
4061  //
4062  return Status;
4063 }
4064 
4065 /*
4066  * @unimplemented
4067  */
4068 NTSTATUS
4069 NTAPI
4071  IN ULONG Flags,
4074  IN PVOID *UserAddressArray,
4075  OUT PULONG_PTR EntriesInUserAddressArray,
4076  OUT PULONG Granularity)
4077 {
4079  NTSTATUS Status;
4080  PVOID EndAddress;
4082  ULONG_PTR CapturedEntryCount;
4083  PAGED_CODE();
4084 
4085  //
4086  // Check if we came from user mode
4087  //
4088  if (PreviousMode != KernelMode)
4089  {
4090  //
4091  // Enter SEH for probing
4092  //
4093  _SEH2_TRY
4094  {
4095  //
4096  // Catch illegal base address
4097  //
4099 
4100  //
4101  // Catch illegal region size
4102  //
4104  {
4105  //
4106  // Fail
4107  //
4109  }
4110 
4111  //
4112  // Validate all data
4113  //
4114  ProbeForWriteSize_t(EntriesInUserAddressArray);
4115  ProbeForWriteUlong(Granularity);
4116 
4117  //
4118  // Capture them
4119  //
4120  CapturedEntryCount = *EntriesInUserAddressArray;
4121 
4122  //
4123  // Must have a count
4124  //
4125  if (CapturedEntryCount == 0) _SEH2_YIELD(return STATUS_INVALID_PARAMETER_5);
4126 
4127  //
4128  // Can't be larger than the maximum
4129  //
4130  if (CapturedEntryCount > (MAXULONG_PTR / sizeof(ULONG_PTR)))
4131  {
4132  //
4133  // Fail
4134  //
4136  }
4137 
4138  //
4139  // Probe the actual array
4140  //
4141  ProbeForWrite(UserAddressArray,
4142  CapturedEntryCount * sizeof(PVOID),
4143  sizeof(PVOID));
4144  }
4146  {
4147  //
4148  // Get exception code
4149  //
4151  }
4152  _SEH2_END;
4153  }
4154  else
4155  {
4156  //
4157  // Capture directly
4158  //
4159  CapturedEntryCount = *EntriesInUserAddressArray;
4160  ASSERT(CapturedEntryCount != 0);
4161  }
4162 
4163  //
4164  // Check if this is a local request
4165  //
4167  {
4168  //
4169  // No need to reference the process
4170  //
4172  }
4173  else
4174  {
4175  //
4176  // Reference the target
4177  //
4180  PsProcessType,
4181  PreviousMode,
4182  (PVOID *)&Process,
4183  NULL);
4184  if (!NT_SUCCESS(Status)) return Status;
4185  }
4186 
4187  //
4188  // Compute the last address and validate it
4189  //
4190  EndAddress = (PVOID)((ULONG_PTR)BaseAddress + RegionSize - 1);
4191  if (BaseAddress > EndAddress)
4192  {
4193  //
4194  // Fail
4195  //
4198  }
4199 
4200  //
4201  // Oops :(
4202  //
4203  UNIMPLEMENTED;
4204 
4205  //
4206  // Dereference if needed
4207  //
4209 
4210  //
4211  // Enter SEH to return data
4212  //
4213  _SEH2_TRY
4214  {
4215  //
4216  // Return data to user
4217  //
4218  *EntriesInUserAddressArray = 0;
4219  *Granularity = PAGE_SIZE;
4220  }
4222  {
4223  //
4224  // Get exception code
4225  //
4227  }
4228  _SEH2_END;
4229 
4230  //
4231  // Return success
4232  //
4233  return STATUS_SUCCESS;
4234 }
4235 
4236 /*
4237  * @unimplemented
4238  */
4239 NTSTATUS
4240 NTAPI
4244 {
4245  PVOID EndAddress;
4247  NTSTATUS Status;
4250 
4251  //
4252  // Catch illegal base address
4253  //
4255 
4256  //
4257  // Catch illegal region size
4258  //
4260  {
4261  //
4262  // Fail
4263  //
4265  }
4266 
4267  //
4268  // Check if this is a local request
4269  //
4271  {
4272  //
4273  // No need to reference the process
4274  //
4276  }
4277  else
4278  {
4279  //
4280  // Reference the target
4281  //
4284  PsProcessType,
4285  PreviousMode,
4286  (PVOID *)&Process,
4287  NULL);
4288  if (!NT_SUCCESS(Status)) return Status;
4289  }
4290 
4291  //
4292  // Compute the last address and validate it
4293  //
4294  EndAddress = (PVOID)((ULONG_PTR)BaseAddress + RegionSize - 1);
4295  if (BaseAddress > EndAddress)
4296  {
4297  //
4298  // Fail
4299  //
4302  }
4303 
4304  //
4305  // Oops :(
4306  //
4307  UNIMPLEMENTED;
4308 
4309  //
4310  // Dereference if needed
4311  //
4313 
4314  //
4315  // Return success
4316  //
4317  return STATUS_SUCCESS;
4318 }
4319 
4320 NTSTATUS
4321 NTAPI
4324  IN MEMORY_INFORMATION_CLASS MemoryInformationClass,
4325  OUT PVOID MemoryInformation,
4326  IN SIZE_T MemoryInformationLength,
4328 {
4331 
4332  DPRINT("Querying class %d about address: %p\n", MemoryInformationClass, BaseAddress);
4333 
4334  /* Bail out if the address is invalid */
4336 
4337  /* Probe return buffer */
4339  if (PreviousMode != KernelMode)
4340  {
4341  _SEH2_TRY
4342  {
4343  ProbeForWrite(MemoryInformation,
4344  MemoryInformationLength,
4345  sizeof(ULONG_PTR));
4346 
4348  }
4350  {
4352  }
4353  _SEH2_END;
4354 
4355  if (!NT_SUCCESS(Status))
4356  {
4357  return Status;
4358  }
4359  }
4360 
4361  switch(MemoryInformationClass)
4362  {
4364  /* Validate the size information of the class */
4365  if (MemoryInformationLength < sizeof(MEMORY_BASIC_INFORMATION))
4366  {
4367  /* The size is invalid */
4369  }
4371  BaseAddress,
4372  MemoryInformation,
4373  MemoryInformationLength,
4374  ReturnLength);
4375  break;
4376 
4377  case MemorySectionName:
4378  /* Validate the size information of the class */
4379  if (MemoryInformationLength < sizeof(MEMORY_SECTION_NAME))
4380  {
4381  /* The size is invalid */
4383  }
4385  BaseAddress,
4386  MemoryInformation,
4387  MemoryInformationLength,
4388  ReturnLength);
4389  break;
4390  case MemoryWorkingSetList:
4392  default:
4393  DPRINT1("Unhandled memory information class %d\n", MemoryInformationClass);
4394  break;
4395  }
4396 
4397  return Status;
4398 }
4399 
4400 /*
4401  * @implemented
4402  */
4403 NTSTATUS
4404 NTAPI
4406  IN OUT PVOID* UBaseAddress,
4408  IN OUT PSIZE_T URegionSize,
4410  IN ULONG Protect)
4411 {
4414  PMMVAD Vad = NULL, FoundVad;
4415  NTSTATUS Status;
4417  PVOID PBaseAddress;
4418  ULONG_PTR PRegionSize, StartingAddress, EndingAddress;
4419  ULONG_PTR HighestAddress = (ULONG_PTR)MM_HIGHEST_VAD_ADDRESS;
4422  PETHREAD CurrentThread = PsGetCurrentThread();
4424  ULONG ProtectionMask, QuotaCharge = 0, QuotaFree = 0;
4425  BOOLEAN Attached = FALSE, ChangeProtection = FALSE;
4426  MMPTE TempPte;
4427  PMMPTE PointerPte, LastPte;
4428  PMMPDE PointerPde;
4430  PAGED_CODE();
4431 
4432  /* Check for valid Zero bits */
4433  if (ZeroBits > MI_MAX_ZERO_BITS)
4434  {
4435  DPRINT1("Too many zero bits\n");
4437  }
4438 
4439  /* Check for valid Allocation Types */
4442  {
4443  DPRINT1("Invalid Allocation Type\n");
4445  }
4446 
4447  /* Check for at least one of these Allocation Types to be set */
4449  {
4450  DPRINT1("No memory allocation base type\n");
4452  }
4453 
4454  /* MEM_RESET is an exclusive flag, make sure that is valid too */
4456  {
4457  DPRINT1("Invalid use of MEM_RESET\n");
4459  }
4460 
4461  /* Check if large pages are being used */
4463  {
4464  /* Large page allocations MUST be committed */
4465  if (!(AllocationType & MEM_COMMIT))
4466  {
4467  DPRINT1("Must supply MEM_COMMIT with MEM_LARGE_PAGES\n");
4469  }
4470 
4471  /* These flags are not allowed with large page allocations */
4473  {
4474  DPRINT1("Using illegal flags with MEM_LARGE_PAGES\n");
4476  }
4477  }
4478 
4479  /* MEM_WRITE_WATCH can only be used if MEM_RESERVE is also used */
4481  {
4482  DPRINT1("MEM_WRITE_WATCH used without MEM_RESERVE\n");
4484  }
4485 
4486  /* Check for valid MEM_PHYSICAL usage */
4488  {
4489  /* MEM_PHYSICAL can only be used if MEM_RESERVE is also used */
4490  if (!(AllocationType & MEM_RESERVE))
4491  {
4492  DPRINT1("MEM_PHYSICAL used without MEM_RESERVE\n");
4494  }
4495 
4496  /* Only these flags are allowed with MEM_PHYSIAL */
4498  {
4499  DPRINT1("Using illegal flags with MEM_PHYSICAL\n");
4501  }
4502 
4503  /* Then make sure PAGE_READWRITE is used */
4504  if (Protect != PAGE_READWRITE)
4505  {
4506  DPRINT1("MEM_PHYSICAL used without PAGE_READWRITE\n");
4508  }
4509  }
4510 
4511  /* Calculate the protection mask and make sure it's valid */
4512  ProtectionMask = MiMakeProtectionMask(Protect);
4513  if (ProtectionMask == MM_INVALID_PROTECTION)
4514  {
4515  DPRINT1("Invalid protection mask\n");
4517  }
4518 
4519  /* Enter SEH */
4520  _SEH2_TRY
4521  {
4522  /* Check for user-mode parameters */
4523  if (PreviousMode != KernelMode)
4524  {
4525  /* Make sure they are writable */
4526  ProbeForWritePointer(UBaseAddress);
4527  ProbeForWriteSize_t(URegionSize);
4528  }
4529 
4530  /* Capture their values */
4531  PBaseAddress = *UBaseAddress;
4532  PRegionSize = *URegionSize;
4533  }
4535  {
4536  /* Return the exception code */
4538  }
4539  _SEH2_END;
4540 
4541  /* Make sure the allocation isn't past the VAD area */
4542  if (PBaseAddress > MM_HIGHEST_VAD_ADDRESS)
4543  {
4544  DPRINT1("Virtual allocation base above User Space\n");
4546  }
4547 
4548  /* Make sure the allocation wouldn't overflow past the VAD area */
4549  if ((((ULONG_PTR)MM_HIGHEST_VAD_ADDRESS + 1) - (ULONG_PTR)PBaseAddress) < PRegionSize)
4550  {
4551  DPRINT1("Region size would overflow into kernel-memory\n");
4553  }
4554 
4555  /* Make sure there's a size specified */
4556  if (!PRegionSize)
4557  {
4558  DPRINT1("Region size is invalid (zero)\n");
4560  }
4561 
4562  //
4563  // If this is for the current process, just use PsGetCurrentProcess
4564  //
4566  {
4568  }
4569  else
4570  {
4571  //
4572  // Otherwise, reference the process with VM rights and attach to it if
4573  // this isn't the current process. We must attach because we'll be touching
4574  // PTEs and PDEs that belong to user-mode memory, and also touching the
4575  // Working Set which is stored in Hyperspace.
4576  //
4579  PsProcessType,
4580  PreviousMode,
4581  (PVOID*)&Process,
4582  NULL);
4583  if (!NT_SUCCESS(Status)) return Status;
4584  if (CurrentProcess != Process)
4585  {
4587  Attached = TRUE;
4588  }
4589  }
4590 
4591  DPRINT("NtAllocateVirtualMemory: Process 0x%p, Address 0x%p, Zerobits %lu , RegionSize 0x%x, Allocation type 0x%x, Protect 0x%x.\n",
4592  Process, PBaseAddress, ZeroBits, PRegionSize, AllocationType, Protect);
4593 
4594  //
4595  // Check for large page allocations and make sure that the required privilege
4596  // is being held, before attempting to handle them.
4597  //
4598  if ((AllocationType & MEM_LARGE_PAGES) &&
4600  {
4601  /* Fail without it */
4602  DPRINT1("Privilege not held for MEM_LARGE_PAGES\n");
4604  goto FailPathNoLock;
4605  }
4606 
4607  //
4608  // Fail on the things we don't yet support
4609  //
4611  {
4612  DPRINT1("MEM_LARGE_PAGES not supported\n");
4614  goto FailPathNoLock;
4615  }
4617  {
4618  DPRINT1("MEM_PHYSICAL not supported\n");
4620  goto FailPathNoLock;
4621  }
4623  {
4624  DPRINT1("MEM_WRITE_WATCH not supported\n");
4626  goto FailPathNoLock;
4627  }
4628 
4629  //
4630  // Check if the caller is reserving memory, or committing memory and letting
4631  // us pick the base address
4632  //
4633  if (!(PBaseAddress) || (AllocationType & MEM_RESERVE))
4634  {
4635  //
4636  // Do not allow COPY_ON_WRITE through this API
4637  //
4639  {
4640  DPRINT1("Copy on write not allowed through this path\n");
4642  goto FailPathNoLock;
4643  }
4644 
4645  //
4646  // Does the caller have an address in mind, or is this a blind commit?
4647  //
4648  if (!PBaseAddress)
4649  {
4650  //
4651  // This is a blind commit, all we need is the region size
4652  //
4653  PRegionSize = ROUND_TO_PAGES(PRegionSize);
4654  EndingAddress = 0;
4655  StartingAddress = 0;
4656 
4657  //
4658  // Check if ZeroBits were specified
4659  //
4660  if (ZeroBits != 0)
4661  {
4662  //
4663  // Calculate the highest address and check if it's valid
4664  //
4665  HighestAddress = MAXULONG_PTR >> ZeroBits;
4666  if (HighestAddress > (ULONG_PTR)MM_HIGHEST_VAD_ADDRESS)
4667  {
4669  goto FailPathNoLock;
4670  }
4671  }
4672  }
4673  else
4674  {
4675  //
4676  // This is a reservation, so compute the starting address on the
4677  // expected 64KB granularity, and see where the ending address will
4678  // fall based on the aligned address and the passed in region size
4679  //
4680  EndingAddress = ((ULONG_PTR)PBaseAddress + PRegionSize - 1) | (PAGE_SIZE - 1);
4681  PRegionSize = EndingAddress + 1 - ROUND_DOWN((ULONG_PTR)PBaseAddress, _64K);
4682  StartingAddress = (ULONG_PTR)PBaseAddress;
4683  }
4684 
4685  //
4686  // Allocate and initialize the VAD
4687  //
4688  Vad = ExAllocatePoolWithTag(NonPagedPool, sizeof(MMVAD_LONG), 'SdaV');
4689  if (Vad == NULL)
4690  {
4691  DPRINT1("Failed to allocate a VAD!\n");
4693  goto FailPathNoLock;
4694  }
4695 
4696  RtlZeroMemory(Vad, sizeof(MMVAD_LONG));
4697  if (AllocationType & MEM_COMMIT) Vad->u.VadFlags.MemCommit = 1;
4698  Vad->u.VadFlags.Protection = ProtectionMask;
4699  Vad->u.VadFlags.PrivateMemory = 1;
4700  Vad->ControlArea = NULL; // For Memory-Area hack
4701 
4702  //
4703  // Insert the VAD
4704  //
4705  Status = MiInsertVadEx(Vad,
4706  &StartingAddress,
4707  PRegionSize,
4708  HighestAddress,
4710  AllocationType);
4711  if (!NT_SUCCESS(Status))
4712  {
4713  DPRINT1("Failed to insert the VAD!\n");
4714  goto FailPathNoLock;
4715  }
4716 
4717  //
4718  // Detach and dereference the target process if
4719  // it was different from the current process
4720  //
4723 
4724  //
4725  // Use SEH to write back the base address and the region size. In the case
4726  // of an exception, we do not return back the exception code, as the memory
4727  // *has* been allocated. The caller would now have to call VirtualQuery
4728  // or do some other similar trick to actually find out where its memory
4729  // allocation ended up
4730  //
4731  _SEH2_TRY
4732  {
4733  *URegionSize = PRegionSize;
4734  *UBaseAddress = (PVOID)StartingAddress;
4735  }
4737  {
4738  //
4739  // Ignore exception!
4740  //
4741  }
4742  _SEH2_END;
4743  DPRINT("Reserved %x bytes at %p.\n", PRegionSize, StartingAddress);
4744  return STATUS_SUCCESS;
4745  }
4746 
4747  //
4748  // This is a MEM_COMMIT on top of an existing address which must have been
4749  // MEM_RESERVED already. Compute the start and ending base addresses based
4750  // on the user input, and then compute the actual region size once all the
4751  // alignments have been done.
4752  //
4753  EndingAddress = (((ULONG_PTR)PBaseAddress + PRegionSize - 1) | (PAGE_SIZE - 1));
4754  StartingAddress = (ULONG_PTR)PAGE_ALIGN(PBaseAddress);
4755  PRegionSize = EndingAddress - StartingAddress + 1;
4756 
4757  //
4758  // Lock the address space and make sure the process isn't already dead
4759  //
4762  if (Process->VmDeleted)
4763  {
4764  DPRINT1("Process is dying\n");
4766  goto FailPath;
4767  }
4768 
4769  //
4770  // Get the VAD for this address range, and make sure it exists
4771  //
4772  Result = MiCheckForConflictingNode(StartingAddress >> PAGE_SHIFT,
4773  EndingAddress >> PAGE_SHIFT,
4774  &Process->VadRoot,
4775  (PMMADDRESS_NODE*)&FoundVad);
4776  if (Result != TableFoundNode)
4777  {
4778  DPRINT1("Could not find a VAD for this allocation\n");
4780  goto FailPath;
4781  }
4782 
4783  if ((AllocationType & MEM_RESET) == MEM_RESET)
4784  {
4786  DPRINT("MEM_RESET not supported\n");
4788  goto FailPath;
4789  }
4790 
4791  //
4792  // These kinds of VADs are illegal for this Windows function when trying to
4793  // commit an existing range
4794  //
4795  if ((FoundVad->u.VadFlags.VadType == VadAwe) ||
4796  (FoundVad->u.VadFlags.VadType == VadDevicePhysicalMemory) ||
4797  (FoundVad->u.VadFlags.VadType == VadLargePages))
4798  {
4799  DPRINT1("Illegal VAD for attempting a MEM_COMMIT\n");
4801  goto FailPath;
4802  }
4803 
4804  //
4805  // Make sure that this address range actually fits within the VAD for it
4806  //
4807  if (((StartingAddress >> PAGE_SHIFT) < FoundVad->StartingVpn) ||
4808  ((EndingAddress >> PAGE_SHIFT) > FoundVad->EndingVpn))
4809  {
4810  DPRINT1("Address range does not fit into the VAD\n");
4812  goto FailPath;
4813  }
4814 
4815  //
4816  // Make sure this is an ARM3 section
4817  //
4819  ASSERT(MemoryArea != NULL);
4821  {
4822  DPRINT1("Illegal commit of non-ARM3 section!\n");
4824  goto FailPath;
4825  }
4826 
4827  // Is this a previously reserved section being committed? If so, enter the
4828  // special section path
4829  //
4830  if (FoundVad->u.VadFlags.PrivateMemory == FALSE)
4831  {
4832  //
4833  // You cannot commit large page sections through this API
4834  //
4835  if (FoundVad->u.VadFlags.VadType == VadLargePageSection)
4836  {
4837  DPRINT1("Large page sections cannot be VirtualAlloc'd\n");
4839  goto FailPath;
4840  }
4841 
4842  //
4843  // You can only use caching flags on a rotate VAD
4844  //
4845  if ((Protect & (PAGE_NOCACHE | PAGE_WRITECOMBINE)) &&
4846  (FoundVad->u.VadFlags.VadType != VadRotatePhysical))
4847  {
4848  DPRINT1("Cannot use caching flags with anything but rotate VADs\n");
4850  goto FailPath;
4851  }
4852 
4853  //
4854  // We should make sure that the section's permissions aren't being
4855  // messed with
4856  //
4857  if (FoundVad->u.VadFlags.NoChange)
4858  {
4859  //
4860  // Make sure it's okay to touch it
4861  // Note: The Windows 2003 kernel has a bug here, passing the
4862  // unaligned base address together with the aligned size,
4863  // potentially covering a region larger than the actual allocation.
4864  // Might be exposed through NtGdiCreateDIBSection w/ section handle
4865  // For now we keep this behavior.
4866  // TODO: analyze possible implications, create test case
4867  //
4868  Status = MiCheckSecuredVad(FoundVad,
4869  PBaseAddress,
4870  PRegionSize,
4871  ProtectionMask);
4872  if (!NT_SUCCESS(Status))
4873  {
4874  DPRINT1("Secured VAD being messed around with\n");
4875  goto FailPath;
4876  }
4877  }
4878 
4879  //
4880  // ARM3 does not support file-backed sections, only shared memory
4881  //
4882  ASSERT(FoundVad->ControlArea->FilePointer == NULL);
4883 
4884  //
4885  // Rotate VADs cannot be guard pages or inaccessible, nor copy on write
4886  //
4887  if ((FoundVad->u.VadFlags.VadType == VadRotatePhysical) &&
4889  {
4890  DPRINT1("Invalid page protection for rotate VAD\n");
4892  goto FailPath;
4893  }
4894 
4895  //
4896  // Compute PTE addresses and the quota charge, then grab the commit lock
4897  //
4898  PointerPte = MI_GET_PROTOTYPE_PTE_FOR_VPN(FoundVad, StartingAddress >> PAGE_SHIFT);
4899  LastPte = MI_GET_PROTOTYPE_PTE_FOR_VPN(FoundVad, EndingAddress >> PAGE_SHIFT);
4900  QuotaCharge = (ULONG)(LastPte - PointerPte + 1);
4902 
4903  //
4904  // Get the segment template PTE and start looping each page
4905  //
4906  TempPte = FoundVad->ControlArea->Segment->SegmentPteTemplate;
4907  ASSERT(TempPte.u.Long != 0);
4908  while (PointerPte <= LastPte)
4909  {
4910  //
4911  // For each non-already-committed page, write the invalid template PTE
4912  //
4913  if (PointerPte->u.Long == 0)
4914  {
4915  MI_WRITE_INVALID_PTE(PointerPte, TempPte);
4916  }
4917  else
4918  {
4919  QuotaFree++;
4920  }
4921  PointerPte++;
4922  }
4923 
4924  //
4925  // Now do the commit accounting and release the lock
4926  //
4927  ASSERT(QuotaCharge >= QuotaFree);
4928  QuotaCharge -= QuotaFree;
4929  FoundVad->ControlArea->Segment->NumberOfCommittedPages += QuotaCharge;
4931 
4932  //
4933  // We are done with committing the section pages
4934  //
4936  goto FailPath;
4937  }
4938 
4939  //
4940  // This is a specific ReactOS check because we only use normal VADs
4941  //
4942  ASSERT(FoundVad->u.VadFlags.VadType == VadNone);
4943 
4944  //
4945  // While this is an actual Windows check
4946  //
4947  ASSERT(FoundVad->u.VadFlags.VadType != VadRotatePhysical);
4948 
4949  //
4950  // Throw out attempts to use copy-on-write through this API path
4951  //
4953  {
4954  DPRINT1("Write copy attempted when not allowed\n");
4956  goto FailPath;
4957  }
4958 
4959  //
4960  // Initialize a demand-zero PTE
4961  //
4962  TempPte.u.Long = 0;
4963  TempPte.u.Soft.Protection = ProtectionMask;
4964  ASSERT(TempPte.u.Long != 0);
4965 
4966  //
4967  // Get the PTE, PDE and the last PTE for this address range
4968  //
4969  PointerPde = MiAddressToPde(StartingAddress);
4970  PointerPte = MiAddressToPte(StartingAddress);
4971  LastPte = MiAddressToPte(EndingAddress);
4972 
4973  //
4974  // Update the commit charge in the VAD as well as in the process, and check
4975  // if this commit charge was now higher than the last recorded peak, in which
4976  // case we also update the peak
4977  //
4978  FoundVad->u.VadFlags.CommitCharge += (1 + LastPte - PointerPte);
4979  Process->CommitCharge += (1 + LastPte - PointerPte);
4980  if (Process->CommitCharge > Process->CommitChargePeak)
4981  {
4982  Process->CommitChargePeak = Process->CommitCharge;
4983  }
4984 
4985  //
4986  // Lock the working set while we play with user pages and page tables
4987  //
4988  MiLockProcessWorkingSetUnsafe(Process, CurrentThread);
4989 
4990  //
4991  // Make the current page table valid, and then loop each page within it
4992  //
4994  while (PointerPte <= LastPte)
4995  {
4996  //
4997  // Have we crossed into a new page table?
4998  //
4999  if (MiIsPteOnPdeBoundary(PointerPte))
5000  {
5001  //
5002  // Get the PDE and now make it valid too
5003  //
5004  PointerPde = MiPteToPde(PointerPte);
5006  }
5007 
5008  //
5009  // Is this a zero PTE as expected?
5010  //
5011  if (PointerPte->u.Long == 0)
5012  {
5013  //
5014  // First increment the count of pages in the page table for this
5015  // process
5016  //
5018 
5019  //
5020  // And now write the invalid demand-zero PTE as requested
5021  //
5022  MI_WRITE_INVALID_PTE(PointerPte, TempPte);
5023  }
5024  else if (PointerPte->u.Long == MmDecommittedPte.u.Long)
5025  {
5026  //
5027  // If the PTE was already decommitted, there is nothing else to do
5028  // but to write the new demand-zero PTE
5029  //
5030  MI_WRITE_INVALID_PTE(PointerPte, TempPte);
5031  }
5032  else if (!(ChangeProtection) && (Protect != MiGetPageProtection(PointerPte)))
5033  {
5034  //
5035  // We don't handle these scenarios yet
5036  //
5037  if (PointerPte->u.Soft.Valid == 0)
5038  {
5039  ASSERT(PointerPte->u.Soft.Prototype == 0);
5040  ASSERT((PointerPte->u.Soft.PageFileHigh == 0) || (PointerPte->u.Soft.Transition == 1));
5041  }
5042 
5043  //
5044  // There's a change in protection, remember this for later, but do
5045  // not yet handle it.
5046  //
5047  ChangeProtection = TRUE;
5048  }
5049 
5050  //
5051  // Move to the next PTE
5052  //
5053  PointerPte++;
5054  }
5055 
5056  //
5057  // Release the working set lock, unlock the address space, and detach from
5058  // the target process if it was not the current process. Also dereference the
5059  // target process if this wasn't the case.
5060  //
5061  MiUnlockProcessWorkingSetUnsafe(Process, CurrentThread);
5063 FailPath:
5065 
5066  if (!NT_SUCCESS(Status))
5067  {
5068  if (Vad != NULL)
5069  {
5070  ExFreePoolWithTag(Vad, 'SdaV');
5071  }
5072  }
5073 
5074  //
5075  // Check if we need to update the protection
5076  //
5077  if (ChangeProtection)
5078  {
5079  PVOID ProtectBaseAddress = (PVOID)StartingAddress;
5080  SIZE_T ProtectSize = PRegionSize;
5081  ULONG OldProtection;
5082 
5083  //
5084  // Change the protection of the region
5085  //
5087  &ProtectBaseAddress,
5088  &ProtectSize,
5089  Protect,
5090  &OldProtection);
5091  }
5092 
5093 FailPathNoLock:
5096 
5097  //
5098  // Only write back results on success
5099  //
5100  if (NT_SUCCESS(Status))
5101  {
5102  //
5103  // Use SEH to write back the base address and the region size. In the case
5104  // of an exception, we strangely do return back the exception code, even
5105  // though the memory *has* been allocated. This mimics Windows behavior and
5106  // there is not much we can do about it.
5107  //
5108  _SEH2_TRY
5109  {
5110  *URegionSize = PRegionSize;
5111  *UBaseAddress = (PVOID)StartingAddress;
5112  }
5114  {
5116  }
5117  _SEH2_END;
5118  }
5119 
5120  return Status;
5121 }
5122 
5123 /*
5124  * @implemented
5125  */
5126 NTSTATUS
5127 NTAPI
5129  IN PVOID* UBaseAddress,
5130  IN PSIZE_T URegionSize,
5131  IN ULONG FreeType)
5132 {
5134  SIZE_T PRegionSize;
5135  PVOID PBaseAddress;
5136  LONG_PTR AlreadyDecommitted, CommitReduction = 0;
5137  ULONG_PTR StartingAddress, EndingAddress;
5138  PMMVAD Vad;
5139  NTSTATUS Status;
5142  PETHREAD CurrentThread = PsGetCurrentThread();
5147  PAGED_CODE();
5148 
5149  //
5150  // Only two flags are supported, exclusively.
5151  //
5153  {
5154  DPRINT1("Invalid FreeType (0x%08lx)\n", FreeType);
5156  }
5157 
5158  //
5159  // Enter SEH for probe and capture. On failure, return back to the caller
5160  // with an exception violation.
5161  //
5162  _SEH2_TRY
5163  {
5164  //
5165  // Check for user-mode parameters and make sure that they are writeable
5166  //
5167  if (PreviousMode != KernelMode)
5168  {
5169  ProbeForWritePointer(UBaseAddress);
5170  ProbeForWriteUlong(URegionSize);
5171  }
5172 
5173  //
5174  // Capture the current values
5175  //
5176  PBaseAddress = *UBaseAddress;
5177  PRegionSize = *URegionSize;
5178  }
5180  {
5182  }
5183  _SEH2_END;
5184 
5185  //
5186  // Make sure the allocation isn't past the user area
5187  //
5188  if (PBaseAddress >= MM_HIGHEST_USER_ADDRESS)
5189  {
5190  DPRINT1("Virtual free base above User Space\n");
5192  }
5193 
5194  //
5195  // Make sure the allocation wouldn't overflow past the user area
5196  //
5197  if (((ULONG_PTR)MM_HIGHEST_USER_ADDRESS - (ULONG_PTR)PBaseAddress) < PRegionSize)
5198  {
5199  DPRINT1("Region size would overflow into kernel-memory\n");
5201  }
5202 
5203  //
5204  // If this is for the current process, just use PsGetCurrentProcess
5205  //
5207  {
5209  }
5210  else
5211  {
5212  //
5213  // Otherwise, reference the process with VM rights and attach to it if
5214  // this isn't the current process. We must attach because we'll be touching
5215  // PTEs and PDEs that belong to user-mode memory, and also touching the
5216  // Working Set which is stored in Hyperspace.
5217  //
5220  PsProcessType,
5221  PreviousMode,
5222  (PVOID*)&Process,
5223  NULL);
5224  if (!NT_SUCCESS(Status)) return Status;
5225  if (CurrentProcess != Process)
5226  {
5228  Attached = TRUE;
5229  }
5230  }
5231 
5232  DPRINT("NtFreeVirtualMemory: Process 0x%p, Address 0x%p, Size 0x%Ix, FreeType 0x%08lx\n",
5233  Process, PBaseAddress, PRegionSize, FreeType);
5234 
5235  //
5236  // Lock the address space
5237  //
5240 
5241  //
5242  // If the address space is being deleted, fail the de-allocation since it's
5243  // too late to do anything about it
5244  //
5245  if (Process->VmDeleted)
5246  {
5247  DPRINT1("Process is dead\n");
5249  goto FailPath;
5250  }
5251 
5252  //
5253  // Compute start and end addresses, and locate the VAD
5254  //
5255  StartingAddress = (ULONG_PTR)PAGE_ALIGN(PBaseAddress);
5256  EndingAddress = ((ULONG_PTR)PBaseAddress + PRegionSize - 1) | (PAGE_SIZE - 1);
5257  Vad = MiLocateAddress((PVOID)StartingAddress);
5258  if (!Vad)
5259  {
5260  DPRINT1("Unable to find VAD for address 0x%p\n", StartingAddress);
5262  goto FailPath;
5263  }
5264 
5265  //
5266  // If the range exceeds the VAD's ending VPN, fail this request
5267  //
5268  if (Vad->EndingVpn < (EndingAddress >> PAGE_SHIFT))
5269  {
5270  DPRINT1("Address 0x%p is beyond the VAD\n", EndingAddress);
5272  goto FailPath;
5273  }
5274 
5275  //
5276  // Only private memory (except rotate VADs) can be freed through here */
5277  //
5278  if ((!(Vad->u.VadFlags.PrivateMemory) &&
5279  (Vad->u.VadFlags.VadType != VadRotatePhysical)) ||
5281  {
5282  DPRINT("Attempt to free section memory\n");
5284  goto FailPath;
5285  }
5286 
5287  //
5288  // ARM3 does not yet handle protected VM
5289  //
5290  ASSERT(Vad->u.VadFlags.NoChange == 0);
5291 
5292  //
5293  // Finally, make sure there is a ReactOS Mm MEMORY_AREA for this allocation
5294  // and that is is an ARM3 memory area, and not a section view, as we currently
5295  // don't support freeing those though this interface.
5296  //
5298  ASSERT(MemoryArea);
5300 
5301