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