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