c9ebe5e3a079d4d153be649012e50ec0b86f7870
[reactos.git] / reactos / ntoskrnl / mm / ARM3 / virtual.c
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 /* So long, and Thanks for All the Fish */
11
12 #include <ntoskrnl.h>
13 #define NDEBUG
14 #include <debug.h>
15
16 #define MODULE_INVOLVED_IN_ARM3
17 #include "../ARM3/miarm.h"
18
19 #define MI_MAPPED_COPY_PAGES 14
20 #define MI_POOL_COPY_BYTES 512
21 #define MI_MAX_TRANSFER_SIZE 64 * 1024
22
23 NTSTATUS NTAPI
24 MiProtectVirtualMemory(IN PEPROCESS Process,
25 IN OUT PVOID *BaseAddress,
26 IN OUT PSIZE_T NumberOfBytesToProtect,
27 IN ULONG NewAccessProtection,
28 OUT PULONG OldAccessProtection OPTIONAL);
29
30 VOID
31 NTAPI
32 MiFlushTbAndCapture(IN PMMVAD FoundVad,
33 IN PMMPTE PointerPte,
34 IN ULONG ProtectionMask,
35 IN PMMPFN Pfn1,
36 IN BOOLEAN CaptureDirtyBit);
37
38
39 /* PRIVATE FUNCTIONS **********************************************************/
40
41 ULONG
42 NTAPI
43 MiCalculatePageCommitment(IN ULONG_PTR StartingAddress,
44 IN ULONG_PTR EndingAddress,
45 IN PMMVAD Vad,
46 IN PEPROCESS Process)
47 {
48 PMMPTE PointerPte, LastPte, 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 = BYTES_TO_PAGES(EndingAddress - StartingAddress);
61
62 /* Is the PDE demand-zero? */
63 PointerPde = MiAddressToPte(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 = MiAddressToPte(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 = MiAddressToPte(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 = MiAddressToPte(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,
206 IN PEPROCESS CurrentProcess)
207 {
208 NTSTATUS Status;
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 */
218 ASSERT(KeAreAllApcsDisabled() == TRUE);
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
257 MiMakeSystemAddressValidPfn(IN PVOID VirtualAddress,
258 IN KIRQL OldIrql)
259 {
260 NTSTATUS Status;
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 */
270 KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
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 */
288 OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
289 }
290
291 /* Let caller know what the lock state is */
292 return LockChange;
293 }
294
295 PFN_COUNT
296 NTAPI
297 MiDeleteSystemPageableVm(IN PMMPTE PointerPte,
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;
307 ASSERT(KeGetCurrentIrql() <= APC_LEVEL);
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 */
340 OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
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 */
350 KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
351
352 /* Destroy the PTE */
353 PointerPte->u.Long = 0;
354 }
355
356 /* Actual legitimate pages */
357 ActualPages++;
358 }
359 else
360 {
361 /*
362 * The only other ARM3 possibility is a demand zero page, which would
363 * mean freeing some of the paged pool pages that haven't even been
364 * touched yet, as part of a larger allocation.
365 *
366 * Right now, we shouldn't expect any page file information in the PTE
367 */
368 ASSERT(PointerPte->u.Soft.PageFileHigh == 0);
369
370 /* Destroy the PTE */
371 PointerPte->u.Long = 0;
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 */
383 KeFlushEntireTb(TRUE, TRUE);
384
385 /* Done */
386 return ActualPages;
387 }
388
389 VOID
390 NTAPI
391 MiDeletePte(IN PMMPTE PointerPte,
392 IN PVOID VirtualAddress,
393 IN PEPROCESS CurrentProcess,
394 IN PMMPTE PrototypePte)
395 {
396 PMMPFN Pfn1;
397 MMPTE TempPte;
398 PFN_NUMBER PageFrameIndex;
399 PMMPDE PointerPde;
400
401 /* PFN lock must be held */
402 ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
403
404 /* Capture the PTE */
405 TempPte = *PointerPte;
406
407 /* We only support valid PTEs for now */
408 ASSERT(TempPte.u.Hard.Valid == 1);
409 if (TempPte.u.Hard.Valid == 0)
410 {
411 /* Invalid PTEs not supported yet */
412 ASSERT(TempPte.u.Soft.Prototype == 0);
413 ASSERT(TempPte.u.Soft.Transition == 0);
414 }
415
416 /* Get the PFN entry */
417 PageFrameIndex = PFN_FROM_PTE(&TempPte);
418 Pfn1 = MiGetPfnEntry(PageFrameIndex);
419
420 /* Check if this is a valid, prototype PTE */
421 if (Pfn1->u3.e1.PrototypePte == 1)
422 {
423 /* Get the PDE and make sure it's faulted in */
424 PointerPde = MiPteToPde(PointerPte);
425 if (PointerPde->u.Hard.Valid == 0)
426 {
427 #if (_MI_PAGING_LEVELS == 2)
428 /* Could be paged pool access from a new process -- synchronize the page directories */
429 if (!NT_SUCCESS(MiCheckPdeForPagedPool(VirtualAddress)))
430 {
431 #endif
432 /* The PDE must be valid at this point */
433 KeBugCheckEx(MEMORY_MANAGEMENT,
434 0x61940,
435 (ULONG_PTR)PointerPte,
436 PointerPte->u.Long,
437 (ULONG_PTR)VirtualAddress);
438 }
439 #if (_MI_PAGING_LEVELS == 2)
440 }
441 #endif
442 /* Drop the share count */
443 MiDecrementShareCount(Pfn1, PageFrameIndex);
444
445 /* Either a fork, or this is the shared user data page */
446 if ((PointerPte <= MiHighestUserPte) && (PrototypePte != Pfn1->PteAddress))
447 {
448 /* If it's not the shared user page, then crash, since there's no fork() yet */
449 if ((PAGE_ALIGN(VirtualAddress) != (PVOID)USER_SHARED_DATA) ||
450 (MmHighestUserAddress <= (PVOID)USER_SHARED_DATA))
451 {
452 /* Must be some sort of memory corruption */
453 KeBugCheckEx(MEMORY_MANAGEMENT,
454 0x400,
455 (ULONG_PTR)PointerPte,
456 (ULONG_PTR)PrototypePte,
457 (ULONG_PTR)Pfn1->PteAddress);
458 }
459 }
460 }
461 else
462 {
463 /* Make sure the saved PTE address is valid */
464 if ((PMMPTE)((ULONG_PTR)Pfn1->PteAddress & ~0x1) != PointerPte)
465 {
466 /* The PFN entry is illegal, or invalid */
467 KeBugCheckEx(MEMORY_MANAGEMENT,
468 0x401,
469 (ULONG_PTR)PointerPte,
470 PointerPte->u.Long,
471 (ULONG_PTR)Pfn1->PteAddress);
472 }
473
474 /* There should only be 1 shared reference count */
475 ASSERT(Pfn1->u2.ShareCount == 1);
476
477 /* Drop the reference on the page table. */
478 MiDecrementShareCount(MiGetPfnEntry(Pfn1->u4.PteFrame), Pfn1->u4.PteFrame);
479
480 /* Mark the PFN for deletion and dereference what should be the last ref */
481 MI_SET_PFN_DELETED(Pfn1);
482 MiDecrementShareCount(Pfn1, PageFrameIndex);
483
484 /* We should eventually do this */
485 //CurrentProcess->NumberOfPrivatePages--;
486 }
487
488 /* Destroy the PTE and flush the TLB */
489 PointerPte->u.Long = 0;
490 KeFlushCurrentTb();
491 }
492
493 VOID
494 NTAPI
495 MiDeleteVirtualAddresses(IN ULONG_PTR Va,
496 IN ULONG_PTR EndingAddress,
497 IN PMMVAD Vad)
498 {
499 PMMPTE PointerPte, PrototypePte, LastPrototypePte;
500 PMMPDE PointerPde;
501 MMPTE TempPte;
502 PEPROCESS CurrentProcess;
503 KIRQL OldIrql;
504 BOOLEAN AddressGap = FALSE;
505 PSUBSECTION Subsection;
506 PUSHORT UsedPageTableEntries;
507
508 /* Get out if this is a fake VAD, RosMm will free the marea pages */
509 if ((Vad) && (Vad->u.VadFlags.Spare == 1)) return;
510
511 /* Grab the process and PTE/PDE for the address being deleted */
512 CurrentProcess = PsGetCurrentProcess();
513 PointerPde = MiAddressToPde(Va);
514 PointerPte = MiAddressToPte(Va);
515
516 /* Check if this is a section VAD or a VM VAD */
517 if (!(Vad) || (Vad->u.VadFlags.PrivateMemory) || !(Vad->FirstPrototypePte))
518 {
519 /* Don't worry about prototypes */
520 PrototypePte = LastPrototypePte = NULL;
521 }
522 else
523 {
524 /* Get the prototype PTE */
525 PrototypePte = Vad->FirstPrototypePte;
526 LastPrototypePte = Vad->FirstPrototypePte + 1;
527 }
528
529 /* In all cases, we don't support fork() yet */
530 ASSERT(CurrentProcess->CloneRoot == NULL);
531
532 /* Loop the PTE for each VA */
533 while (TRUE)
534 {
535 /* First keep going until we find a valid PDE */
536 while (!PointerPde->u.Long)
537 {
538 /* There are gaps in the address space */
539 AddressGap = TRUE;
540
541 /* Still no valid PDE, try the next 4MB (or whatever) */
542 PointerPde++;
543
544 /* Update the PTE on this new boundary */
545 PointerPte = MiPteToAddress(PointerPde);
546
547 /* Check if all the PDEs are invalid, so there's nothing to free */
548 Va = (ULONG_PTR)MiPteToAddress(PointerPte);
549 if (Va > EndingAddress) return;
550 }
551
552 /* Now check if the PDE is mapped in */
553 if (!PointerPde->u.Hard.Valid)
554 {
555 /* It isn't, so map it in */
556 PointerPte = MiPteToAddress(PointerPde);
557 MiMakeSystemAddressValid(PointerPte, CurrentProcess);
558 }
559
560 /* Now we should have a valid PDE, mapped in, and still have some VA */
561 ASSERT(PointerPde->u.Hard.Valid == 1);
562 ASSERT(Va <= EndingAddress);
563 UsedPageTableEntries = &MmWorkingSetList->UsedPageTableEntries[MiGetPdeOffset(Va)];
564
565 /* Check if this is a section VAD with gaps in it */
566 if ((AddressGap) && (LastPrototypePte))
567 {
568 /* We need to skip to the next correct prototype PTE */
569 PrototypePte = MI_GET_PROTOTYPE_PTE_FOR_VPN(Vad, Va >> PAGE_SHIFT);
570
571 /* And we need the subsection to skip to the next last prototype PTE */
572 Subsection = MiLocateSubsection(Vad, Va >> PAGE_SHIFT);
573 if (Subsection)
574 {
575 /* Found it! */
576 LastPrototypePte = &Subsection->SubsectionBase[Subsection->PtesInSubsection];
577 }
578 else
579 {
580 /* No more subsections, we are done with prototype PTEs */
581 PrototypePte = NULL;
582 }
583 }
584
585 /* Lock the PFN Database while we delete the PTEs */
586 OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
587 do
588 {
589 /* Capture the PDE and make sure it exists */
590 TempPte = *PointerPte;
591 if (TempPte.u.Long)
592 {
593 *UsedPageTableEntries -= 1;
594 ASSERT((*UsedPageTableEntries) < PTE_COUNT);
595
596 /* Check if the PTE is actually mapped in */
597 if (TempPte.u.Long & 0xFFFFFC01)
598 {
599 /* Are we dealing with section VAD? */
600 if ((LastPrototypePte) && (PrototypePte > LastPrototypePte))
601 {
602 /* We need to skip to the next correct prototype PTE */
603 PrototypePte = MI_GET_PROTOTYPE_PTE_FOR_VPN(Vad, Va >> PAGE_SHIFT);
604
605 /* And we need the subsection to skip to the next last prototype PTE */
606 Subsection = MiLocateSubsection(Vad, Va >> PAGE_SHIFT);
607 if (Subsection)
608 {
609 /* Found it! */
610 LastPrototypePte = &Subsection->SubsectionBase[Subsection->PtesInSubsection];
611 }
612 else
613 {
614 /* No more subsections, we are done with prototype PTEs */
615 PrototypePte = NULL;
616 }
617 }
618
619 /* Check for prototype PTE */
620 if ((TempPte.u.Hard.Valid == 0) &&
621 (TempPte.u.Soft.Prototype == 1))
622 {
623 /* Just nuke it */
624 PointerPte->u.Long = 0;
625 }
626 else
627 {
628 /* Delete the PTE proper */
629 MiDeletePte(PointerPte,
630 (PVOID)Va,
631 CurrentProcess,
632 PrototypePte);
633 }
634 }
635 else
636 {
637 /* The PTE was never mapped, just nuke it here */
638 PointerPte->u.Long = 0;
639 }
640 }
641
642 /* Update the address and PTE for it */
643 Va += PAGE_SIZE;
644 PointerPte++;
645 PrototypePte++;
646
647 /* Making sure the PDE is still valid */
648 ASSERT(PointerPde->u.Hard.Valid == 1);
649 }
650 while ((Va & (PDE_MAPPED_VA - 1)) && (Va <= EndingAddress));
651
652 /* The PDE should still be valid at this point */
653 ASSERT(PointerPde->u.Hard.Valid == 1);
654
655 if (*UsedPageTableEntries == 0)
656 {
657 if (PointerPde->u.Long != 0)
658 {
659 /* Delete the PTE proper */
660 MiDeletePte(PointerPde,
661 MiPteToAddress(PointerPde),
662 CurrentProcess,
663 NULL);
664 }
665 }
666
667 /* Release the lock and get out if we're done */
668 KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
669 if (Va > EndingAddress) return;
670
671 /* Otherwise, we exited because we hit a new PDE boundary, so start over */
672 PointerPde = MiAddressToPde(Va);
673 AddressGap = FALSE;
674 }
675 }
676
677 LONG
678 MiGetExceptionInfo(IN PEXCEPTION_POINTERS ExceptionInfo,
679 OUT PBOOLEAN HaveBadAddress,
680 OUT PULONG_PTR BadAddress)
681 {
682 PEXCEPTION_RECORD ExceptionRecord;
683 PAGED_CODE();
684
685 //
686 // Assume default
687 //
688 *HaveBadAddress = FALSE;
689
690 //
691 // Get the exception record
692 //
693 ExceptionRecord = ExceptionInfo->ExceptionRecord;
694
695 //
696 // Look at the exception code
697 //
698 if ((ExceptionRecord->ExceptionCode == STATUS_ACCESS_VIOLATION) ||
699 (ExceptionRecord->ExceptionCode == STATUS_GUARD_PAGE_VIOLATION) ||
700 (ExceptionRecord->ExceptionCode == STATUS_IN_PAGE_ERROR))
701 {
702 //
703 // We can tell the address if we have more than one parameter
704 //
705 if (ExceptionRecord->NumberParameters > 1)
706 {
707 //
708 // Return the address
709 //
710 *HaveBadAddress = TRUE;
711 *BadAddress = ExceptionRecord->ExceptionInformation[1];
712 }
713 }
714
715 //
716 // Continue executing the next handler
717 //
718 return EXCEPTION_EXECUTE_HANDLER;
719 }
720
721 NTSTATUS
722 NTAPI
723 MiDoMappedCopy(IN PEPROCESS SourceProcess,
724 IN PVOID SourceAddress,
725 IN PEPROCESS TargetProcess,
726 OUT PVOID TargetAddress,
727 IN SIZE_T BufferSize,
728 IN KPROCESSOR_MODE PreviousMode,
729 OUT PSIZE_T ReturnSize)
730 {
731 PFN_NUMBER MdlBuffer[(sizeof(MDL) / sizeof(PFN_NUMBER)) + MI_MAPPED_COPY_PAGES + 1];
732 PMDL Mdl = (PMDL)MdlBuffer;
733 SIZE_T TotalSize, CurrentSize, RemainingSize;
734 volatile BOOLEAN FailedInProbe = FALSE, FailedInMapping = FALSE, FailedInMoving;
735 volatile BOOLEAN PagesLocked;
736 PVOID CurrentAddress = SourceAddress, CurrentTargetAddress = TargetAddress;
737 volatile PVOID MdlAddress;
738 KAPC_STATE ApcState;
739 BOOLEAN HaveBadAddress;
740 ULONG_PTR BadAddress;
741 NTSTATUS Status = STATUS_SUCCESS;
742 PAGED_CODE();
743
744 //
745 // Calculate the maximum amount of data to move
746 //
747 TotalSize = MI_MAPPED_COPY_PAGES * PAGE_SIZE;
748 if (BufferSize <= TotalSize) TotalSize = BufferSize;
749 CurrentSize = TotalSize;
750 RemainingSize = BufferSize;
751
752 //
753 // Loop as long as there is still data
754 //
755 while (RemainingSize > 0)
756 {
757 //
758 // Check if this transfer will finish everything off
759 //
760 if (RemainingSize < CurrentSize) CurrentSize = RemainingSize;
761
762 //
763 // Attach to the source address space
764 //
765 KeStackAttachProcess(&SourceProcess->Pcb, &ApcState);
766
767 //
768 // Reset state for this pass
769 //
770 MdlAddress = NULL;
771 PagesLocked = FALSE;
772 FailedInMoving = FALSE;
773 ASSERT(FailedInProbe == FALSE);
774
775 //
776 // Protect user-mode copy
777 //
778 _SEH2_TRY
779 {
780 //
781 // If this is our first time, probe the buffer
782 //
783 if ((CurrentAddress == SourceAddress) && (PreviousMode != KernelMode))
784 {
785 //
786 // Catch a failure here
787 //
788 FailedInProbe = TRUE;
789
790 //
791 // Do the probe
792 //
793 ProbeForRead(SourceAddress, BufferSize, sizeof(CHAR));
794
795 //
796 // Passed
797 //
798 FailedInProbe = FALSE;
799 }
800
801 //
802 // Initialize and probe and lock the MDL
803 //
804 MmInitializeMdl(Mdl, CurrentAddress, CurrentSize);
805 MmProbeAndLockPages(Mdl, PreviousMode, IoReadAccess);
806 PagesLocked = TRUE;
807
808 //
809 // Now map the pages
810 //
811 MdlAddress = MmMapLockedPagesSpecifyCache(Mdl,
812 KernelMode,
813 MmCached,
814 NULL,
815 FALSE,
816 HighPagePriority);
817 if (!MdlAddress)
818 {
819 //
820 // Use our SEH handler to pick this up
821 //
822 FailedInMapping = TRUE;
823 ExRaiseStatus(STATUS_INSUFFICIENT_RESOURCES);
824 }
825
826 //
827 // Now let go of the source and grab to the target process
828 //
829 KeUnstackDetachProcess(&ApcState);
830 KeStackAttachProcess(&TargetProcess->Pcb, &ApcState);
831
832 //
833 // Check if this is our first time through
834 //
835 if ((CurrentAddress == SourceAddress) && (PreviousMode != KernelMode))
836 {
837 //
838 // Catch a failure here
839 //
840 FailedInProbe = TRUE;
841
842 //
843 // Do the probe
844 //
845 ProbeForWrite(TargetAddress, BufferSize, sizeof(CHAR));
846
847 //
848 // Passed
849 //
850 FailedInProbe = FALSE;
851 }
852
853 //
854 // Now do the actual move
855 //
856 FailedInMoving = TRUE;
857 RtlCopyMemory(CurrentTargetAddress, MdlAddress, CurrentSize);
858 }
859 _SEH2_EXCEPT(MiGetExceptionInfo(_SEH2_GetExceptionInformation(),
860 &HaveBadAddress,
861 &BadAddress))
862 {
863 //
864 // Detach from whoever we may be attached to
865 //
866 KeUnstackDetachProcess(&ApcState);
867
868 //
869 // Check if we had mapped the pages
870 //
871 if (MdlAddress) MmUnmapLockedPages(MdlAddress, Mdl);
872
873 //
874 // Check if we had locked the pages
875 //
876 if (PagesLocked) MmUnlockPages(Mdl);
877
878 //
879 // Check if we hit working set quota
880 //
881 if (_SEH2_GetExceptionCode() == STATUS_WORKING_SET_QUOTA)
882 {
883 //
884 // Return the error
885 //
886 return STATUS_WORKING_SET_QUOTA;
887 }
888
889 //
890 // Check if we failed during the probe or mapping
891 //
892 if ((FailedInProbe) || (FailedInMapping))
893 {
894 //
895 // Exit
896 //
897 Status = _SEH2_GetExceptionCode();
898 _SEH2_YIELD(return Status);
899 }
900
901 //
902 // Otherwise, we failed probably during the move
903 //
904 *ReturnSize = BufferSize - RemainingSize;
905 if (FailedInMoving)
906 {
907 //
908 // Check if we know exactly where we stopped copying
909 //
910 if (HaveBadAddress)
911 {
912 //
913 // Return the exact number of bytes copied
914 //
915 *ReturnSize = BadAddress - (ULONG_PTR)SourceAddress;
916 }
917 }
918
919 //
920 // Return partial copy
921 //
922 Status = STATUS_PARTIAL_COPY;
923 }
924 _SEH2_END;
925
926 //
927 // Check for SEH status
928 //
929 if (Status != STATUS_SUCCESS) return Status;
930
931 //
932 // Detach from target
933 //
934 KeUnstackDetachProcess(&ApcState);
935
936 //
937 // Unmap and unlock
938 //
939 MmUnmapLockedPages(MdlAddress, Mdl);
940 MmUnlockPages(Mdl);
941
942 //
943 // Update location and size
944 //
945 RemainingSize -= CurrentSize;
946 CurrentAddress = (PVOID)((ULONG_PTR)CurrentAddress + CurrentSize);
947 CurrentTargetAddress = (PVOID)((ULONG_PTR)CurrentTargetAddress + CurrentSize);
948 }
949
950 //
951 // All bytes read
952 //
953 *ReturnSize = BufferSize;
954 return STATUS_SUCCESS;
955 }
956
957 NTSTATUS
958 NTAPI
959 MiDoPoolCopy(IN PEPROCESS SourceProcess,
960 IN PVOID SourceAddress,
961 IN PEPROCESS TargetProcess,
962 OUT PVOID TargetAddress,
963 IN SIZE_T BufferSize,
964 IN KPROCESSOR_MODE PreviousMode,
965 OUT PSIZE_T ReturnSize)
966 {
967 UCHAR StackBuffer[MI_POOL_COPY_BYTES];
968 SIZE_T TotalSize, CurrentSize, RemainingSize;
969 volatile BOOLEAN FailedInProbe = FALSE, FailedInMoving, HavePoolAddress = FALSE;
970 PVOID CurrentAddress = SourceAddress, CurrentTargetAddress = TargetAddress;
971 PVOID PoolAddress;
972 KAPC_STATE ApcState;
973 BOOLEAN HaveBadAddress;
974 ULONG_PTR BadAddress;
975 NTSTATUS Status = STATUS_SUCCESS;
976 PAGED_CODE();
977
978 //
979 // Calculate the maximum amount of data to move
980 //
981 TotalSize = MI_MAX_TRANSFER_SIZE;
982 if (BufferSize <= MI_MAX_TRANSFER_SIZE) TotalSize = BufferSize;
983 CurrentSize = TotalSize;
984 RemainingSize = BufferSize;
985
986 //
987 // Check if we can use the stack
988 //
989 if (BufferSize <= MI_POOL_COPY_BYTES)
990 {
991 //
992 // Use it
993 //
994 PoolAddress = (PVOID)StackBuffer;
995 }
996 else
997 {
998 //
999 // Allocate pool
1000 //
1001 PoolAddress = ExAllocatePoolWithTag(NonPagedPool, TotalSize, 'VmRw');
1002 if (!PoolAddress) ASSERT(FALSE);
1003 HavePoolAddress = TRUE;
1004 }
1005
1006 //
1007 // Loop as long as there is still data
1008 //
1009 while (RemainingSize > 0)
1010 {
1011 //
1012 // Check if this transfer will finish everything off
1013 //
1014 if (RemainingSize < CurrentSize) CurrentSize = RemainingSize;
1015
1016 //
1017 // Attach to the source address space
1018 //
1019 KeStackAttachProcess(&SourceProcess->Pcb, &ApcState);
1020
1021 //
1022 // Reset state for this pass
1023 //
1024 FailedInMoving = FALSE;
1025 ASSERT(FailedInProbe == FALSE);
1026
1027 //
1028 // Protect user-mode copy
1029 //
1030 _SEH2_TRY
1031 {
1032 //
1033 // If this is our first time, probe the buffer
1034 //
1035 if ((CurrentAddress == SourceAddress) && (PreviousMode != KernelMode))
1036 {
1037 //
1038 // Catch a failure here
1039 //
1040 FailedInProbe = TRUE;
1041
1042 //
1043 // Do the probe
1044 //
1045 ProbeForRead(SourceAddress, BufferSize, sizeof(CHAR));
1046
1047 //
1048 // Passed
1049 //
1050 FailedInProbe = FALSE;
1051 }
1052
1053 //
1054 // Do the copy
1055 //
1056 RtlCopyMemory(PoolAddress, CurrentAddress, CurrentSize);
1057
1058 //
1059 // Now let go of the source and grab to the target process
1060 //
1061 KeUnstackDetachProcess(&ApcState);
1062 KeStackAttachProcess(&TargetProcess->Pcb, &ApcState);
1063
1064 //
1065 // Check if this is our first time through
1066 //
1067 if ((CurrentAddress == SourceAddress) && (PreviousMode != KernelMode))
1068 {
1069 //
1070 // Catch a failure here
1071 //
1072 FailedInProbe = TRUE;
1073
1074 //
1075 // Do the probe
1076 //
1077 ProbeForWrite(TargetAddress, BufferSize, sizeof(CHAR));
1078
1079 //
1080 // Passed
1081 //
1082 FailedInProbe = FALSE;
1083 }
1084
1085 //
1086 // Now do the actual move
1087 //
1088 FailedInMoving = TRUE;
1089 RtlCopyMemory(CurrentTargetAddress, PoolAddress, CurrentSize);
1090 }
1091 _SEH2_EXCEPT(MiGetExceptionInfo(_SEH2_GetExceptionInformation(),
1092 &HaveBadAddress,
1093 &BadAddress))
1094 {
1095 //
1096 // Detach from whoever we may be attached to
1097 //
1098 KeUnstackDetachProcess(&ApcState);
1099
1100 //
1101 // Check if we had allocated pool
1102 //
1103 if (HavePoolAddress) ExFreePoolWithTag(PoolAddress, 'VmRw');
1104
1105 //
1106 // Check if we failed during the probe
1107 //
1108 if (FailedInProbe)
1109 {
1110 //
1111 // Exit
1112 //
1113 Status = _SEH2_GetExceptionCode();
1114 _SEH2_YIELD(return Status);
1115 }
1116
1117 //
1118 // Otherwise, we failed, probably during the move
1119 //
1120 *ReturnSize = BufferSize - RemainingSize;
1121 if (FailedInMoving)
1122 {
1123 //
1124 // Check if we know exactly where we stopped copying
1125 //
1126 if (HaveBadAddress)
1127 {
1128 //
1129 // Return the exact number of bytes copied
1130 //
1131 *ReturnSize = BadAddress - (ULONG_PTR)SourceAddress;
1132 }
1133 }
1134
1135 //
1136 // Return partial copy
1137 //
1138 Status = STATUS_PARTIAL_COPY;
1139 }
1140 _SEH2_END;
1141
1142 //
1143 // Check for SEH status
1144 //
1145 if (Status != STATUS_SUCCESS) return Status;
1146
1147 //
1148 // Detach from target
1149 //
1150 KeUnstackDetachProcess(&ApcState);
1151
1152 //
1153 // Update location and size
1154 //
1155 RemainingSize -= CurrentSize;
1156 CurrentAddress = (PVOID)((ULONG_PTR)CurrentAddress + CurrentSize);
1157 CurrentTargetAddress = (PVOID)((ULONG_PTR)CurrentTargetAddress +
1158 CurrentSize);
1159 }
1160
1161 //
1162 // Check if we had allocated pool
1163 //
1164 if (HavePoolAddress) ExFreePoolWithTag(PoolAddress, 'VmRw');
1165
1166 //
1167 // All bytes read
1168 //
1169 *ReturnSize = BufferSize;
1170 return STATUS_SUCCESS;
1171 }
1172
1173 NTSTATUS
1174 NTAPI
1175 MmCopyVirtualMemory(IN PEPROCESS SourceProcess,
1176 IN PVOID SourceAddress,
1177 IN PEPROCESS TargetProcess,
1178 OUT PVOID TargetAddress,
1179 IN SIZE_T BufferSize,
1180 IN KPROCESSOR_MODE PreviousMode,
1181 OUT PSIZE_T ReturnSize)
1182 {
1183 NTSTATUS Status;
1184 PEPROCESS Process = SourceProcess;
1185
1186 //
1187 // Don't accept zero-sized buffers
1188 //
1189 if (!BufferSize) return STATUS_SUCCESS;
1190
1191 //
1192 // If we are copying from ourselves, lock the target instead
1193 //
1194 if (SourceProcess == PsGetCurrentProcess()) Process = TargetProcess;
1195
1196 //
1197 // Acquire rundown protection
1198 //
1199 if (!ExAcquireRundownProtection(&Process->RundownProtect))
1200 {
1201 //
1202 // Fail
1203 //
1204 return STATUS_PROCESS_IS_TERMINATING;
1205 }
1206
1207 //
1208 // See if we should use the pool copy
1209 //
1210 if (BufferSize > MI_POOL_COPY_BYTES)
1211 {
1212 //
1213 // Use MDL-copy
1214 //
1215 Status = MiDoMappedCopy(SourceProcess,
1216 SourceAddress,
1217 TargetProcess,
1218 TargetAddress,
1219 BufferSize,
1220 PreviousMode,
1221 ReturnSize);
1222 }
1223 else
1224 {
1225 //
1226 // Do pool copy
1227 //
1228 Status = MiDoPoolCopy(SourceProcess,
1229 SourceAddress,
1230 TargetProcess,
1231 TargetAddress,
1232 BufferSize,
1233 PreviousMode,
1234 ReturnSize);
1235 }
1236
1237 //
1238 // Release the lock
1239 //
1240 ExReleaseRundownProtection(&Process->RundownProtect);
1241 return Status;
1242 }
1243
1244 NTSTATUS
1245 NTAPI
1246 MmFlushVirtualMemory(IN PEPROCESS Process,
1247 IN OUT PVOID *BaseAddress,
1248 IN OUT PSIZE_T RegionSize,
1249 OUT PIO_STATUS_BLOCK IoStatusBlock)
1250 {
1251 PAGED_CODE();
1252 UNIMPLEMENTED;
1253
1254 //
1255 // Fake success
1256 //
1257 return STATUS_SUCCESS;
1258 }
1259
1260 ULONG
1261 NTAPI
1262 MiGetPageProtection(IN PMMPTE PointerPte)
1263 {
1264 MMPTE TempPte;
1265 PMMPFN Pfn;
1266 PAGED_CODE();
1267
1268 /* Copy this PTE's contents */
1269 TempPte = *PointerPte;
1270
1271 /* Assure it's not totally zero */
1272 ASSERT(TempPte.u.Long);
1273
1274 /* Check for a special prototype format */
1275 if (TempPte.u.Soft.Valid == 0 &&
1276 TempPte.u.Soft.Prototype == 1)
1277 {
1278 /* Unsupported now */
1279 UNIMPLEMENTED;
1280 ASSERT(FALSE);
1281 }
1282
1283 /* In the easy case of transition or demand zero PTE just return its protection */
1284 if (!TempPte.u.Hard.Valid) return MmProtectToValue[TempPte.u.Soft.Protection];
1285
1286 /* If we get here, the PTE is valid, so look up the page in PFN database */
1287 Pfn = MiGetPfnEntry(TempPte.u.Hard.PageFrameNumber);
1288 if (!Pfn->u3.e1.PrototypePte)
1289 {
1290 /* Return protection of the original pte */
1291 ASSERT(Pfn->u4.AweAllocation == 0);
1292 return MmProtectToValue[Pfn->OriginalPte.u.Soft.Protection];
1293 }
1294
1295 /* This is software PTE */
1296 DPRINT1("Prototype PTE: %lx %p\n", TempPte.u.Hard.PageFrameNumber, Pfn);
1297 DPRINT1("VA: %p\n", MiPteToAddress(&TempPte));
1298 DPRINT1("Mask: %lx\n", TempPte.u.Soft.Protection);
1299 DPRINT1("Mask2: %lx\n", Pfn->OriginalPte.u.Soft.Protection);
1300 return MmProtectToValue[TempPte.u.Soft.Protection];
1301 }
1302
1303 ULONG
1304 NTAPI
1305 MiQueryAddressState(IN PVOID Va,
1306 IN PMMVAD Vad,
1307 IN PEPROCESS TargetProcess,
1308 OUT PULONG ReturnedProtect,
1309 OUT PVOID *NextVa)
1310 {
1311
1312 PMMPTE PointerPte, ProtoPte;
1313 PMMPDE PointerPde;
1314 MMPTE TempPte, TempProtoPte;
1315 BOOLEAN DemandZeroPte = TRUE, ValidPte = FALSE;
1316 ULONG State = MEM_RESERVE, Protect = 0;
1317 ASSERT((Vad->StartingVpn <= ((ULONG_PTR)Va >> PAGE_SHIFT)) &&
1318 (Vad->EndingVpn >= ((ULONG_PTR)Va >> PAGE_SHIFT)));
1319
1320 /* Only normal VADs supported */
1321 ASSERT(Vad->u.VadFlags.VadType == VadNone);
1322
1323 /* Get the PDE and PTE for the address */
1324 PointerPde = MiAddressToPde(Va);
1325 PointerPte = MiAddressToPte(Va);
1326
1327 /* Return the next range */
1328 *NextVa = (PVOID)((ULONG_PTR)Va + PAGE_SIZE);
1329
1330 /* Is the PDE demand-zero? */
1331 if (PointerPde->u.Long != 0)
1332 {
1333 /* It is not. Is it valid? */
1334 if (PointerPde->u.Hard.Valid == 0)
1335 {
1336 /* Is isn't, fault it in */
1337 PointerPte = MiPteToAddress(PointerPde);
1338 MiMakeSystemAddressValid(PointerPte, TargetProcess);
1339 ValidPte = TRUE;
1340 }
1341 }
1342 else
1343 {
1344 /* It is, skip it and move to the next PDE */
1345 *NextVa = MiPdeToAddress(PointerPde + 1);
1346 }
1347
1348 /* Is it safe to try reading the PTE? */
1349 if (ValidPte)
1350 {
1351 /* FIXME: watch out for large pages */
1352 ASSERT(PointerPde->u.Hard.LargePage == FALSE);
1353
1354 /* Capture the PTE */
1355 TempPte = *PointerPte;
1356 if (TempPte.u.Long != 0)
1357 {
1358 /* The PTE is valid, so it's not zeroed out */
1359 DemandZeroPte = FALSE;
1360
1361 /* Is it a decommited, invalid, or faulted PTE? */
1362 if ((TempPte.u.Soft.Protection == MM_DECOMMIT) &&
1363 (TempPte.u.Hard.Valid == 0) &&
1364 ((TempPte.u.Soft.Prototype == 0) ||
1365 (TempPte.u.Soft.PageFileHigh == MI_PTE_LOOKUP_NEEDED)))
1366 {
1367 /* Otherwise our defaults should hold */
1368 ASSERT(Protect == 0);
1369 ASSERT(State == MEM_RESERVE);
1370 }
1371 else
1372 {
1373 /* This means it's committed */
1374 State = MEM_COMMIT;
1375
1376 /* We don't support these */
1377 ASSERT(Vad->u.VadFlags.VadType != VadDevicePhysicalMemory);
1378 ASSERT(Vad->u.VadFlags.VadType != VadRotatePhysical);
1379 ASSERT(Vad->u.VadFlags.VadType != VadAwe);
1380
1381 /* Get protection state of this page */
1382 Protect = MiGetPageProtection(PointerPte);
1383
1384 /* Check if this is an image-backed VAD */
1385 if ((TempPte.u.Soft.Valid == 0) &&
1386 (TempPte.u.Soft.Prototype == 1) &&
1387 (Vad->u.VadFlags.PrivateMemory == 0) &&
1388 (Vad->ControlArea))
1389 {
1390 DPRINT1("Not supported\n");
1391 ASSERT(FALSE);
1392 }
1393 }
1394 }
1395 }
1396
1397 /* Check if this was a demand-zero PTE, since we need to find the state */
1398 if (DemandZeroPte)
1399 {
1400 /* Not yet handled */
1401 ASSERT(Vad->u.VadFlags.VadType != VadDevicePhysicalMemory);
1402 ASSERT(Vad->u.VadFlags.VadType != VadAwe);
1403
1404 /* Check if this is private commited memory, or an section-backed VAD */
1405 if ((Vad->u.VadFlags.PrivateMemory == 0) && (Vad->ControlArea))
1406 {
1407 /* Tell caller about the next range */
1408 *NextVa = (PVOID)((ULONG_PTR)Va + PAGE_SIZE);
1409
1410 /* Get the prototype PTE for this VAD */
1411 ProtoPte = MI_GET_PROTOTYPE_PTE_FOR_VPN(Vad,
1412 (ULONG_PTR)Va >> PAGE_SHIFT);
1413 if (ProtoPte)
1414 {
1415 /* We should unlock the working set, but it's not being held! */
1416
1417 /* Is the prototype PTE actually valid (committed)? */
1418 TempProtoPte = *ProtoPte;
1419 if (TempProtoPte.u.Long)
1420 {
1421 /* Unless this is a memory-mapped file, handle it like private VAD */
1422 State = MEM_COMMIT;
1423 ASSERT(Vad->u.VadFlags.VadType != VadImageMap);
1424 Protect = MmProtectToValue[Vad->u.VadFlags.Protection];
1425 }
1426
1427 /* We should re-lock the working set */
1428 }
1429 }
1430 else if (Vad->u.VadFlags.MemCommit)
1431 {
1432 /* This is committed memory */
1433 State = MEM_COMMIT;
1434
1435 /* Convert the protection */
1436 Protect = MmProtectToValue[Vad->u.VadFlags.Protection];
1437 }
1438 }
1439
1440 /* Return the protection code */
1441 *ReturnedProtect = Protect;
1442 return State;
1443 }
1444
1445 NTSTATUS
1446 NTAPI
1447 MiQueryMemoryBasicInformation(IN HANDLE ProcessHandle,
1448 IN PVOID BaseAddress,
1449 OUT PVOID MemoryInformation,
1450 IN SIZE_T MemoryInformationLength,
1451 OUT PSIZE_T ReturnLength)
1452 {
1453 PEPROCESS TargetProcess;
1454 NTSTATUS Status = STATUS_SUCCESS;
1455 PMMVAD Vad = NULL;
1456 PVOID Address, NextAddress;
1457 BOOLEAN Found = FALSE;
1458 ULONG NewProtect, NewState;
1459 ULONG_PTR BaseVpn;
1460 MEMORY_BASIC_INFORMATION MemoryInfo;
1461 KAPC_STATE ApcState;
1462 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
1463 PMEMORY_AREA MemoryArea;
1464 SIZE_T ResultLength;
1465
1466 /* Check for illegal addresses in user-space, or the shared memory area */
1467 if ((BaseAddress > MM_HIGHEST_VAD_ADDRESS) ||
1468 (PAGE_ALIGN(BaseAddress) == (PVOID)MM_SHARED_USER_DATA_VA))
1469 {
1470 Address = PAGE_ALIGN(BaseAddress);
1471
1472 /* Make up an info structure describing this range */
1473 MemoryInfo.BaseAddress = Address;
1474 MemoryInfo.AllocationProtect = PAGE_READONLY;
1475 MemoryInfo.Type = MEM_PRIVATE;
1476
1477 /* Special case for shared data */
1478 if (Address == (PVOID)MM_SHARED_USER_DATA_VA)
1479 {
1480 MemoryInfo.AllocationBase = (PVOID)MM_SHARED_USER_DATA_VA;
1481 MemoryInfo.State = MEM_COMMIT;
1482 MemoryInfo.Protect = PAGE_READONLY;
1483 MemoryInfo.RegionSize = PAGE_SIZE;
1484 }
1485 else
1486 {
1487 MemoryInfo.AllocationBase = (PCHAR)MM_HIGHEST_VAD_ADDRESS + 1;
1488 MemoryInfo.State = MEM_RESERVE;
1489 MemoryInfo.Protect = PAGE_NOACCESS;
1490 MemoryInfo.RegionSize = (ULONG_PTR)MM_HIGHEST_USER_ADDRESS + 1 - (ULONG_PTR)Address;
1491 }
1492
1493 /* Return the data, NtQueryInformation already probed it*/
1494 if (PreviousMode != KernelMode)
1495 {
1496 _SEH2_TRY
1497 {
1498 *(PMEMORY_BASIC_INFORMATION)MemoryInformation = MemoryInfo;
1499 if (ReturnLength) *ReturnLength = sizeof(MEMORY_BASIC_INFORMATION);
1500 }
1501 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1502 {
1503 Status = _SEH2_GetExceptionCode();
1504 }
1505 _SEH2_END;
1506 }
1507 else
1508 {
1509 *(PMEMORY_BASIC_INFORMATION)MemoryInformation = MemoryInfo;
1510 if (ReturnLength) *ReturnLength = sizeof(MEMORY_BASIC_INFORMATION);
1511 }
1512
1513 return Status;
1514 }
1515
1516 /* Check if this is for a local or remote process */
1517 if (ProcessHandle == NtCurrentProcess())
1518 {
1519 TargetProcess = PsGetCurrentProcess();
1520 }
1521 else
1522 {
1523 /* Reference the target process */
1524 Status = ObReferenceObjectByHandle(ProcessHandle,
1525 PROCESS_QUERY_INFORMATION,
1526 PsProcessType,
1527 ExGetPreviousMode(),
1528 (PVOID*)&TargetProcess,
1529 NULL);
1530 if (!NT_SUCCESS(Status)) return Status;
1531
1532 /* Attach to it now */
1533 KeStackAttachProcess(&TargetProcess->Pcb, &ApcState);
1534 }
1535
1536 /* Lock the address space and make sure the process isn't already dead */
1537 MmLockAddressSpace(&TargetProcess->Vm);
1538 if (TargetProcess->VmDeleted)
1539 {
1540 /* Unlock the address space of the process */
1541 MmUnlockAddressSpace(&TargetProcess->Vm);
1542
1543 /* Check if we were attached */
1544 if (ProcessHandle != NtCurrentProcess())
1545 {
1546 /* Detach and dereference the process */
1547 KeUnstackDetachProcess(&ApcState);
1548 ObDereferenceObject(TargetProcess);
1549 }
1550
1551 /* Bail out */
1552 DPRINT1("Process is dying\n");
1553 return STATUS_PROCESS_IS_TERMINATING;
1554 }
1555
1556 /* Loop the VADs */
1557 ASSERT(TargetProcess->VadRoot.NumberGenericTableElements);
1558 if (TargetProcess->VadRoot.NumberGenericTableElements)
1559 {
1560 /* Scan on the right */
1561 Vad = (PMMVAD)TargetProcess->VadRoot.BalancedRoot.RightChild;
1562 BaseVpn = (ULONG_PTR)BaseAddress >> PAGE_SHIFT;
1563 while (Vad)
1564 {
1565 /* Check if this VAD covers the allocation range */
1566 if ((BaseVpn >= Vad->StartingVpn) &&
1567 (BaseVpn <= Vad->EndingVpn))
1568 {
1569 /* We're done */
1570 Found = TRUE;
1571 break;
1572 }
1573
1574 /* Check if this VAD is too high */
1575 if (BaseVpn < Vad->StartingVpn)
1576 {
1577 /* Stop if there is no left child */
1578 if (!Vad->LeftChild) break;
1579
1580 /* Search on the left next */
1581 Vad = Vad->LeftChild;
1582 }
1583 else
1584 {
1585 /* Then this VAD is too low, keep searching on the right */
1586 ASSERT(BaseVpn > Vad->EndingVpn);
1587
1588 /* Stop if there is no right child */
1589 if (!Vad->RightChild) break;
1590
1591 /* Search on the right next */
1592 Vad = Vad->RightChild;
1593 }
1594 }
1595 }
1596
1597 /* Was a VAD found? */
1598 if (!Found)
1599 {
1600 Address = PAGE_ALIGN(BaseAddress);
1601
1602 /* Calculate region size */
1603 if (Vad)
1604 {
1605 if (Vad->StartingVpn >= BaseVpn)
1606 {
1607 /* Region size is the free space till the start of that VAD */
1608 MemoryInfo.RegionSize = (ULONG_PTR)(Vad->StartingVpn << PAGE_SHIFT) - (ULONG_PTR)Address;
1609 }
1610 else
1611 {
1612 /* Get the next VAD */
1613 Vad = (PMMVAD)MiGetNextNode((PMMADDRESS_NODE)Vad);
1614 if (Vad)
1615 {
1616 /* Region size is the free space till the start of that VAD */
1617 MemoryInfo.RegionSize = (ULONG_PTR)(Vad->StartingVpn << PAGE_SHIFT) - (ULONG_PTR)Address;
1618 }
1619 else
1620 {
1621 /* Maximum possible region size with that base address */
1622 MemoryInfo.RegionSize = (PCHAR)MM_HIGHEST_VAD_ADDRESS + 1 - (PCHAR)Address;
1623 }
1624 }
1625 }
1626 else
1627 {
1628 /* Maximum possible region size with that base address */
1629 MemoryInfo.RegionSize = (PCHAR)MM_HIGHEST_VAD_ADDRESS + 1 - (PCHAR)Address;
1630 }
1631
1632 /* Unlock the address space of the process */
1633 MmUnlockAddressSpace(&TargetProcess->Vm);
1634
1635 /* Check if we were attached */
1636 if (ProcessHandle != NtCurrentProcess())
1637 {
1638 /* Detach and derefernece the process */
1639 KeUnstackDetachProcess(&ApcState);
1640 ObDereferenceObject(TargetProcess);
1641 }
1642
1643 /* Build the rest of the initial information block */
1644 MemoryInfo.BaseAddress = Address;
1645 MemoryInfo.AllocationBase = NULL;
1646 MemoryInfo.AllocationProtect = 0;
1647 MemoryInfo.State = MEM_FREE;
1648 MemoryInfo.Protect = PAGE_NOACCESS;
1649 MemoryInfo.Type = 0;
1650
1651 /* Return the data, NtQueryInformation already probed it*/
1652 if (PreviousMode != KernelMode)
1653 {
1654 _SEH2_TRY
1655 {
1656 *(PMEMORY_BASIC_INFORMATION)MemoryInformation = MemoryInfo;
1657 if (ReturnLength) *ReturnLength = sizeof(MEMORY_BASIC_INFORMATION);
1658 }
1659 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1660 {
1661 Status = _SEH2_GetExceptionCode();
1662 }
1663 _SEH2_END;
1664 }
1665 else
1666 {
1667 *(PMEMORY_BASIC_INFORMATION)MemoryInformation = MemoryInfo;
1668 if (ReturnLength) *ReturnLength = sizeof(MEMORY_BASIC_INFORMATION);
1669 }
1670
1671 return Status;
1672 }
1673
1674 /* Set the correct memory type based on what kind of VAD this is */
1675 if ((Vad->u.VadFlags.PrivateMemory) ||
1676 (Vad->u.VadFlags.VadType == VadRotatePhysical))
1677 {
1678 MemoryInfo.Type = MEM_PRIVATE;
1679 }
1680 else if (Vad->u.VadFlags.VadType == VadImageMap)
1681 {
1682 MemoryInfo.Type = MEM_IMAGE;
1683 }
1684 else
1685 {
1686 MemoryInfo.Type = MEM_MAPPED;
1687 }
1688
1689 /* Find the memory area the specified address belongs to */
1690 MemoryArea = MmLocateMemoryAreaByAddress(&TargetProcess->Vm, BaseAddress);
1691 ASSERT(MemoryArea != NULL);
1692
1693 /* Determine information dependent on the memory area type */
1694 if (MemoryArea->Type == MEMORY_AREA_SECTION_VIEW)
1695 {
1696 Status = MmQuerySectionView(MemoryArea, BaseAddress, &MemoryInfo, &ResultLength);
1697 ASSERT(NT_SUCCESS(Status));
1698 }
1699 else
1700 {
1701 /* Build the initial information block */
1702 Address = PAGE_ALIGN(BaseAddress);
1703 MemoryInfo.BaseAddress = Address;
1704 MemoryInfo.AllocationBase = (PVOID)(Vad->StartingVpn << PAGE_SHIFT);
1705 MemoryInfo.AllocationProtect = MmProtectToValue[Vad->u.VadFlags.Protection];
1706 MemoryInfo.Type = MEM_PRIVATE;
1707
1708 /* Find the largest chunk of memory which has the same state and protection mask */
1709 MemoryInfo.State = MiQueryAddressState(Address,
1710 Vad,
1711 TargetProcess,
1712 &MemoryInfo.Protect,
1713 &NextAddress);
1714 Address = NextAddress;
1715 while (((ULONG_PTR)Address >> PAGE_SHIFT) <= Vad->EndingVpn)
1716 {
1717 /* Keep going unless the state or protection mask changed */
1718 NewState = MiQueryAddressState(Address, Vad, TargetProcess, &NewProtect, &NextAddress);
1719 if ((NewState != MemoryInfo.State) || (NewProtect != MemoryInfo.Protect)) break;
1720 Address = NextAddress;
1721 }
1722
1723 /* Now that we know the last VA address, calculate the region size */
1724 MemoryInfo.RegionSize = ((ULONG_PTR)Address - (ULONG_PTR)MemoryInfo.BaseAddress);
1725 }
1726
1727 /* Unlock the address space of the process */
1728 MmUnlockAddressSpace(&TargetProcess->Vm);
1729
1730 /* Check if we were attached */
1731 if (ProcessHandle != NtCurrentProcess())
1732 {
1733 /* Detach and derefernece the process */
1734 KeUnstackDetachProcess(&ApcState);
1735 ObDereferenceObject(TargetProcess);
1736 }
1737
1738 /* Return the data, NtQueryInformation already probed it*/
1739 if (PreviousMode != KernelMode)
1740 {
1741 _SEH2_TRY
1742 {
1743 *(PMEMORY_BASIC_INFORMATION)MemoryInformation = MemoryInfo;
1744 if (ReturnLength) *ReturnLength = sizeof(MEMORY_BASIC_INFORMATION);
1745 }
1746 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1747 {
1748 Status = _SEH2_GetExceptionCode();
1749 }
1750 _SEH2_END;
1751 }
1752 else
1753 {
1754 *(PMEMORY_BASIC_INFORMATION)MemoryInformation = MemoryInfo;
1755 if (ReturnLength) *ReturnLength = sizeof(MEMORY_BASIC_INFORMATION);
1756 }
1757
1758 /* All went well */
1759 DPRINT("Base: %p AllocBase: %p AllocProtect: %lx Protect: %lx "
1760 "State: %lx Type: %lx Size: %lx\n",
1761 MemoryInfo.BaseAddress, MemoryInfo.AllocationBase,
1762 MemoryInfo.AllocationProtect, MemoryInfo.Protect,
1763 MemoryInfo.State, MemoryInfo.Type, MemoryInfo.RegionSize);
1764
1765 return Status;
1766 }
1767
1768 BOOLEAN
1769 NTAPI
1770 MiIsEntireRangeCommitted(IN ULONG_PTR StartingAddress,
1771 IN ULONG_PTR EndingAddress,
1772 IN PMMVAD Vad,
1773 IN PEPROCESS Process)
1774 {
1775 PMMPTE PointerPte, LastPte, PointerPde;
1776 BOOLEAN OnBoundary = TRUE;
1777 PAGED_CODE();
1778
1779 /* Get the PDE and PTE addresses */
1780 PointerPde = MiAddressToPde(StartingAddress);
1781 PointerPte = MiAddressToPte(StartingAddress);
1782 LastPte = MiAddressToPte(EndingAddress);
1783
1784 /* Loop all the PTEs */
1785 while (PointerPte <= LastPte)
1786 {
1787 /* Check if we've hit an new PDE boundary */
1788 if (OnBoundary)
1789 {
1790 /* Is this PDE demand zero? */
1791 PointerPde = MiAddressToPte(PointerPte);
1792 if (PointerPde->u.Long != 0)
1793 {
1794 /* It isn't -- is it valid? */
1795 if (PointerPde->u.Hard.Valid == 0)
1796 {
1797 /* Nope, fault it in */
1798 PointerPte = MiPteToAddress(PointerPde);
1799 MiMakeSystemAddressValid(PointerPte, Process);
1800 }
1801 }
1802 else
1803 {
1804 /* The PTE was already valid, so move to the next one */
1805 PointerPde++;
1806 PointerPte = MiPteToAddress(PointerPde);
1807
1808 /* Is the entire VAD committed? If not, fail */
1809 if (!Vad->u.VadFlags.MemCommit) return FALSE;
1810
1811 /* Everything is committed so far past the range, return true */
1812 if (PointerPte > LastPte) return TRUE;
1813 }
1814 }
1815
1816 /* Is the PTE demand zero? */
1817 if (PointerPte->u.Long == 0)
1818 {
1819 /* Is the entire VAD committed? If not, fail */
1820 if (!Vad->u.VadFlags.MemCommit) return FALSE;
1821 }
1822 else
1823 {
1824 /* It isn't -- is it a decommited, invalid, or faulted PTE? */
1825 if ((PointerPte->u.Soft.Protection == MM_DECOMMIT) &&
1826 (PointerPte->u.Hard.Valid == 0) &&
1827 ((PointerPte->u.Soft.Prototype == 0) ||
1828 (PointerPte->u.Soft.PageFileHigh == MI_PTE_LOOKUP_NEEDED)))
1829 {
1830 /* Then part of the range is decommitted, so fail */
1831 return FALSE;
1832 }
1833 }
1834
1835 /* Move to the next PTE */
1836 PointerPte++;
1837 OnBoundary = MiIsPteOnPdeBoundary(PointerPte);
1838 }
1839
1840 /* All PTEs seem valid, and no VAD checks failed, the range is okay */
1841 return TRUE;
1842 }
1843
1844 NTSTATUS
1845 NTAPI
1846 MiRosProtectVirtualMemory(IN PEPROCESS Process,
1847 IN OUT PVOID *BaseAddress,
1848 IN OUT PSIZE_T NumberOfBytesToProtect,
1849 IN ULONG NewAccessProtection,
1850 OUT PULONG OldAccessProtection OPTIONAL)
1851 {
1852 PMEMORY_AREA MemoryArea;
1853 PMMSUPPORT AddressSpace;
1854 ULONG OldAccessProtection_;
1855 NTSTATUS Status;
1856
1857 *NumberOfBytesToProtect = PAGE_ROUND_UP((ULONG_PTR)(*BaseAddress) + (*NumberOfBytesToProtect)) - PAGE_ROUND_DOWN(*BaseAddress);
1858 *BaseAddress = (PVOID)PAGE_ROUND_DOWN(*BaseAddress);
1859
1860 AddressSpace = &Process->Vm;
1861 MmLockAddressSpace(AddressSpace);
1862 MemoryArea = MmLocateMemoryAreaByAddress(AddressSpace, *BaseAddress);
1863 if (MemoryArea == NULL || MemoryArea->DeleteInProgress)
1864 {
1865 MmUnlockAddressSpace(AddressSpace);
1866 return STATUS_UNSUCCESSFUL;
1867 }
1868
1869 if (OldAccessProtection == NULL) OldAccessProtection = &OldAccessProtection_;
1870
1871 ASSERT(MemoryArea->Type == MEMORY_AREA_SECTION_VIEW);
1872 Status = MmProtectSectionView(AddressSpace,
1873 MemoryArea,
1874 *BaseAddress,
1875 *NumberOfBytesToProtect,
1876 NewAccessProtection,
1877 OldAccessProtection);
1878
1879 MmUnlockAddressSpace(AddressSpace);
1880
1881 return Status;
1882 }
1883
1884 NTSTATUS
1885 NTAPI
1886 MiProtectVirtualMemory(IN PEPROCESS Process,
1887 IN OUT PVOID *BaseAddress,
1888 IN OUT PSIZE_T NumberOfBytesToProtect,
1889 IN ULONG NewAccessProtection,
1890 OUT PULONG OldAccessProtection OPTIONAL)
1891 {
1892 PMEMORY_AREA MemoryArea;
1893 PMMVAD Vad;
1894 PMMSUPPORT AddressSpace;
1895 ULONG_PTR StartingAddress, EndingAddress;
1896 PMMPTE PointerPde, PointerPte, LastPte;
1897 MMPTE PteContents;
1898 PMMPFN Pfn1;
1899 ULONG ProtectionMask, OldProtect;
1900 BOOLEAN Committed;
1901 NTSTATUS Status = STATUS_SUCCESS;
1902 PETHREAD Thread = PsGetCurrentThread();
1903
1904 /* Calculate base address for the VAD */
1905 StartingAddress = (ULONG_PTR)PAGE_ALIGN((*BaseAddress));
1906 EndingAddress = (((ULONG_PTR)*BaseAddress + *NumberOfBytesToProtect - 1) | (PAGE_SIZE - 1));
1907
1908 /* Calculate the protection mask and make sure it's valid */
1909 ProtectionMask = MiMakeProtectionMask(NewAccessProtection);
1910 if (ProtectionMask == MM_INVALID_PROTECTION)
1911 {
1912 DPRINT1("Invalid protection mask\n");
1913 return STATUS_INVALID_PAGE_PROTECTION;
1914 }
1915
1916 /* Check for ROS specific memory area */
1917 MemoryArea = MmLocateMemoryAreaByAddress(&Process->Vm, *BaseAddress);
1918 if ((MemoryArea) && (MemoryArea->Type == MEMORY_AREA_SECTION_VIEW))
1919 {
1920 /* Evil hack */
1921 return MiRosProtectVirtualMemory(Process,
1922 BaseAddress,
1923 NumberOfBytesToProtect,
1924 NewAccessProtection,
1925 OldAccessProtection);
1926 }
1927
1928 /* Lock the address space and make sure the process isn't already dead */
1929 AddressSpace = MmGetCurrentAddressSpace();
1930 MmLockAddressSpace(AddressSpace);
1931 if (Process->VmDeleted)
1932 {
1933 DPRINT1("Process is dying\n");
1934 Status = STATUS_PROCESS_IS_TERMINATING;
1935 goto FailPath;
1936 }
1937
1938 /* Get the VAD for this address range, and make sure it exists */
1939 Vad = (PMMVAD)MiCheckForConflictingNode(StartingAddress >> PAGE_SHIFT,
1940 EndingAddress >> PAGE_SHIFT,
1941 &Process->VadRoot);
1942 if (!Vad)
1943 {
1944 DPRINT("Could not find a VAD for this allocation\n");
1945 Status = STATUS_CONFLICTING_ADDRESSES;
1946 goto FailPath;
1947 }
1948
1949 /* Make sure the address is within this VAD's boundaries */
1950 if ((((ULONG_PTR)StartingAddress >> PAGE_SHIFT) < Vad->StartingVpn) ||
1951 (((ULONG_PTR)EndingAddress >> PAGE_SHIFT) > Vad->EndingVpn))
1952 {
1953 Status = STATUS_CONFLICTING_ADDRESSES;
1954 goto FailPath;
1955 }
1956
1957 /* These kinds of VADs are not supported atm */
1958 if ((Vad->u.VadFlags.VadType == VadAwe) ||
1959 (Vad->u.VadFlags.VadType == VadDevicePhysicalMemory) ||
1960 (Vad->u.VadFlags.VadType == VadLargePages))
1961 {
1962 DPRINT1("Illegal VAD for attempting to set protection\n");
1963 Status = STATUS_CONFLICTING_ADDRESSES;
1964 goto FailPath;
1965 }
1966
1967 /* Check for a VAD whose protection can't be changed */
1968 if (Vad->u.VadFlags.NoChange == 1)
1969 {
1970 DPRINT1("Trying to change protection of a NoChange VAD\n");
1971 Status = STATUS_INVALID_PAGE_PROTECTION;
1972 goto FailPath;
1973 }
1974
1975 /* Is this section, or private memory? */
1976 if (Vad->u.VadFlags.PrivateMemory == 0)
1977 {
1978 /* Not yet supported */
1979 if (Vad->u.VadFlags.VadType == VadLargePageSection)
1980 {
1981 DPRINT1("Illegal VAD for attempting to set protection\n");
1982 Status = STATUS_CONFLICTING_ADDRESSES;
1983 goto FailPath;
1984 }
1985
1986 /* Rotate VADs are not yet supported */
1987 if (Vad->u.VadFlags.VadType == VadRotatePhysical)
1988 {
1989 DPRINT1("Illegal VAD for attempting to set protection\n");
1990 Status = STATUS_CONFLICTING_ADDRESSES;
1991 goto FailPath;
1992 }
1993
1994 /* Not valid on section files */
1995 if (NewAccessProtection & (PAGE_NOCACHE | PAGE_WRITECOMBINE))
1996 {
1997 /* Fail */
1998 DPRINT1("Invalid protection flags for section\n");
1999 Status = STATUS_INVALID_PARAMETER_4;
2000 goto FailPath;
2001 }
2002
2003 /* Check if data or page file mapping protection PTE is compatible */
2004 if (!Vad->ControlArea->u.Flags.Image)
2005 {
2006 /* Not yet */
2007 DPRINT1("Fixme: Not checking for valid protection\n");
2008 }
2009
2010 /* This is a section, and this is not yet supported */
2011 DPRINT1("Section protection not yet supported\n");
2012 OldProtect = 0;
2013 }
2014 else
2015 {
2016 /* Private memory, check protection flags */
2017 if ((NewAccessProtection & PAGE_WRITECOPY) ||
2018 (NewAccessProtection & PAGE_EXECUTE_WRITECOPY))
2019 {
2020 DPRINT1("Invalid protection flags for private memory\n");
2021 Status = STATUS_INVALID_PARAMETER_4;
2022 goto FailPath;
2023 }
2024
2025 /* Lock the working set */
2026 MiLockProcessWorkingSetUnsafe(Process, Thread);
2027
2028 /* Check if all pages in this range are committed */
2029 Committed = MiIsEntireRangeCommitted(StartingAddress,
2030 EndingAddress,
2031 Vad,
2032 Process);
2033 if (!Committed)
2034 {
2035 /* Fail */
2036 DPRINT1("The entire range is not committed\n");
2037 Status = STATUS_NOT_COMMITTED;
2038 MiUnlockProcessWorkingSetUnsafe(Process, Thread);
2039 goto FailPath;
2040 }
2041
2042 /* Compute starting and ending PTE and PDE addresses */
2043 PointerPde = MiAddressToPde(StartingAddress);
2044 PointerPte = MiAddressToPte(StartingAddress);
2045 LastPte = MiAddressToPte(EndingAddress);
2046
2047 /* Make this PDE valid */
2048 MiMakePdeExistAndMakeValid(PointerPde, Process, MM_NOIRQL);
2049
2050 /* Save protection of the first page */
2051 if (PointerPte->u.Long != 0)
2052 {
2053 /* Capture the page protection and make the PDE valid */
2054 OldProtect = MiGetPageProtection(PointerPte);
2055 MiMakePdeExistAndMakeValid(PointerPde, Process, MM_NOIRQL);
2056 }
2057 else
2058 {
2059 /* Grab the old protection from the VAD itself */
2060 OldProtect = MmProtectToValue[Vad->u.VadFlags.Protection];
2061 }
2062
2063 /* Loop all the PTEs now */
2064 while (PointerPte <= LastPte)
2065 {
2066 /* Check if we've crossed a PDE boundary and make the new PDE valid too */
2067 if (MiIsPteOnPdeBoundary(PointerPte))
2068 {
2069 PointerPde = MiAddressToPte(PointerPte);
2070 MiMakePdeExistAndMakeValid(PointerPde, Process, MM_NOIRQL);
2071 }
2072
2073 /* Capture the PTE and check if it was empty */
2074 PteContents = *PointerPte;
2075 if (PteContents.u.Long == 0)
2076 {
2077 /* This used to be a zero PTE and it no longer is, so we must add a
2078 reference to the pagetable. */
2079 MiIncrementPageTableReferences(MiPteToAddress(PointerPte));
2080 }
2081
2082 /* Check what kind of PTE we are dealing with */
2083 if (PteContents.u.Hard.Valid == 1)
2084 {
2085 /* Get the PFN entry */
2086 Pfn1 = MiGetPfnEntry(PFN_FROM_PTE(&PteContents));
2087
2088 /* We don't support this yet */
2089 ASSERT(Pfn1->u3.e1.PrototypePte == 0);
2090
2091 /* Check if the page should not be accessible at all */
2092 if ((NewAccessProtection & PAGE_NOACCESS) ||
2093 (NewAccessProtection & PAGE_GUARD))
2094 {
2095 /* The page should be in the WS and we should make it transition now */
2096 DPRINT1("Making valid page invalid is not yet supported!\n");
2097 Status = STATUS_NOT_IMPLEMENTED;
2098 /* Unlock the working set */
2099 MiUnlockProcessWorkingSetUnsafe(Process, Thread);
2100 goto FailPath;
2101 }
2102
2103 /* Write the protection mask and write it with a TLB flush */
2104 Pfn1->OriginalPte.u.Soft.Protection = ProtectionMask;
2105 MiFlushTbAndCapture(Vad,
2106 PointerPte,
2107 ProtectionMask,
2108 Pfn1,
2109 TRUE);
2110 }
2111 else
2112 {
2113 /* We don't support these cases yet */
2114 ASSERT(PteContents.u.Soft.Prototype == 0);
2115 ASSERT(PteContents.u.Soft.Transition == 0);
2116
2117 /* The PTE is already demand-zero, just update the protection mask */
2118 PointerPte->u.Soft.Protection = ProtectionMask;
2119 ASSERT(PointerPte->u.Long != 0);
2120 }
2121
2122 /* Move to the next PTE */
2123 PointerPte++;
2124 }
2125
2126 /* Unlock the working set */
2127 MiUnlockProcessWorkingSetUnsafe(Process, Thread);
2128 }
2129
2130 /* Unlock the address space */
2131 MmUnlockAddressSpace(AddressSpace);
2132
2133 /* Return parameters and success */
2134 *NumberOfBytesToProtect = EndingAddress - StartingAddress + 1;
2135 *BaseAddress = (PVOID)StartingAddress;
2136 *OldAccessProtection = OldProtect;
2137 return STATUS_SUCCESS;
2138
2139 FailPath:
2140 /* Unlock the address space and return the failure code */
2141 MmUnlockAddressSpace(AddressSpace);
2142 return Status;
2143 }
2144
2145 VOID
2146 NTAPI
2147 MiMakePdeExistAndMakeValid(IN PMMPTE PointerPde,
2148 IN PEPROCESS TargetProcess,
2149 IN KIRQL OldIrql)
2150 {
2151 PMMPTE PointerPte, PointerPpe, PointerPxe;
2152
2153 //
2154 // Sanity checks. The latter is because we only use this function with the
2155 // PFN lock not held, so it may go away in the future.
2156 //
2157 ASSERT(KeAreAllApcsDisabled() == TRUE);
2158 ASSERT(OldIrql == MM_NOIRQL);
2159
2160 //
2161 // Also get the PPE and PXE. This is okay not to #ifdef because they will
2162 // return the same address as the PDE on 2-level page table systems.
2163 //
2164 // If everything is already valid, there is nothing to do.
2165 //
2166 PointerPpe = MiAddressToPte(PointerPde);
2167 PointerPxe = MiAddressToPde(PointerPde);
2168 if ((PointerPxe->u.Hard.Valid) &&
2169 (PointerPpe->u.Hard.Valid) &&
2170 (PointerPde->u.Hard.Valid))
2171 {
2172 return;
2173 }
2174
2175 //
2176 // At least something is invalid, so begin by getting the PTE for the PDE itself
2177 // and then lookup each additional level. We must do it in this precise order
2178 // because the pagfault.c code (as well as in Windows) depends that the next
2179 // level up (higher) must be valid when faulting a lower level
2180 //
2181 PointerPte = MiPteToAddress(PointerPde);
2182 do
2183 {
2184 //
2185 // Make sure APCs continued to be disabled
2186 //
2187 ASSERT(KeAreAllApcsDisabled() == TRUE);
2188
2189 //
2190 // First, make the PXE valid if needed
2191 //
2192 if (!PointerPxe->u.Hard.Valid)
2193 {
2194 MiMakeSystemAddressValid(PointerPpe, TargetProcess);
2195 ASSERT(PointerPxe->u.Hard.Valid == 1);
2196 }
2197
2198 //
2199 // Next, the PPE
2200 //
2201 if (!PointerPpe->u.Hard.Valid)
2202 {
2203 MiMakeSystemAddressValid(PointerPde, TargetProcess);
2204 ASSERT(PointerPpe->u.Hard.Valid == 1);
2205 }
2206
2207 //
2208 // And finally, make the PDE itself valid.
2209 //
2210 MiMakeSystemAddressValid(PointerPte, TargetProcess);
2211
2212 //
2213 // This should've worked the first time so the loop is really just for
2214 // show -- ASSERT that we're actually NOT going to be looping.
2215 //
2216 ASSERT(PointerPxe->u.Hard.Valid == 1);
2217 ASSERT(PointerPpe->u.Hard.Valid == 1);
2218 ASSERT(PointerPde->u.Hard.Valid == 1);
2219 } while (!(PointerPxe->u.Hard.Valid) ||
2220 !(PointerPpe->u.Hard.Valid) ||
2221 !(PointerPde->u.Hard.Valid));
2222 }
2223
2224 VOID
2225 NTAPI
2226 MiProcessValidPteList(IN PMMPTE *ValidPteList,
2227 IN ULONG Count)
2228 {
2229 KIRQL OldIrql;
2230 ULONG i;
2231 MMPTE TempPte;
2232 PFN_NUMBER PageFrameIndex;
2233 PMMPFN Pfn1, Pfn2;
2234
2235 //
2236 // Acquire the PFN lock and loop all the PTEs in the list
2237 //
2238 OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
2239 for (i = 0; i != Count; i++)
2240 {
2241 //
2242 // The PTE must currently be valid
2243 //
2244 TempPte = *ValidPteList[i];
2245 ASSERT(TempPte.u.Hard.Valid == 1);
2246
2247 //
2248 // Get the PFN entry for the page itself, and then for its page table
2249 //
2250 PageFrameIndex = PFN_FROM_PTE(&TempPte);
2251 Pfn1 = MiGetPfnEntry(PageFrameIndex);
2252 Pfn2 = MiGetPfnEntry(Pfn1->u4.PteFrame);
2253
2254 //
2255 // Decrement the share count on the page table, and then on the page
2256 // itself
2257 //
2258 MiDecrementShareCount(Pfn2, Pfn1->u4.PteFrame);
2259 MI_SET_PFN_DELETED(Pfn1);
2260 MiDecrementShareCount(Pfn1, PageFrameIndex);
2261
2262 //
2263 // Make the page decommitted
2264 //
2265 MI_WRITE_INVALID_PTE(ValidPteList[i], MmDecommittedPte);
2266 }
2267
2268 //
2269 // All the PTEs have been dereferenced and made invalid, flush the TLB now
2270 // and then release the PFN lock
2271 //
2272 KeFlushCurrentTb();
2273 KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
2274 }
2275
2276 ULONG
2277 NTAPI
2278 MiDecommitPages(IN PVOID StartingAddress,
2279 IN PMMPTE EndingPte,
2280 IN PEPROCESS Process,
2281 IN PMMVAD Vad)
2282 {
2283 PMMPTE PointerPde, PointerPte, CommitPte = NULL;
2284 ULONG CommitReduction = 0;
2285 PMMPTE ValidPteList[256];
2286 ULONG PteCount = 0;
2287 PMMPFN Pfn1;
2288 MMPTE PteContents;
2289 PETHREAD CurrentThread = PsGetCurrentThread();
2290
2291 //
2292 // Get the PTE and PTE for the address, and lock the working set
2293 // If this was a VAD for a MEM_COMMIT allocation, also figure out where the
2294 // commited range ends so that we can do the right accounting.
2295 //
2296 PointerPde = MiAddressToPde(StartingAddress);
2297 PointerPte = MiAddressToPte(StartingAddress);
2298 if (Vad->u.VadFlags.MemCommit) CommitPte = MiAddressToPte(Vad->EndingVpn << PAGE_SHIFT);
2299 MiLockProcessWorkingSetUnsafe(Process, CurrentThread);
2300
2301 //
2302 // Make the PDE valid, and now loop through each page's worth of data
2303 //
2304 MiMakePdeExistAndMakeValid(PointerPde, Process, MM_NOIRQL);
2305 while (PointerPte <= EndingPte)
2306 {
2307 //
2308 // Check if we've crossed a PDE boundary
2309 //
2310 if (MiIsPteOnPdeBoundary(PointerPte))
2311 {
2312 //
2313 // Get the new PDE and flush the valid PTEs we had built up until
2314 // now. This helps reduce the amount of TLB flushing we have to do.
2315 // Note that Windows does a much better job using timestamps and
2316 // such, and does not flush the entire TLB all the time, but right
2317 // now we have bigger problems to worry about than TLB flushing.
2318 //
2319 PointerPde = MiAddressToPde(StartingAddress);
2320 if (PteCount)
2321 {
2322 MiProcessValidPteList(ValidPteList, PteCount);
2323 PteCount = 0;
2324 }
2325
2326 //
2327 // Make this PDE valid
2328 //
2329 MiMakePdeExistAndMakeValid(PointerPde, Process, MM_NOIRQL);
2330 }
2331
2332 //
2333 // Read this PTE. It might be active or still demand-zero.
2334 //
2335 PteContents = *PointerPte;
2336 if (PteContents.u.Long)
2337 {
2338 //
2339 // The PTE is active. It might be valid and in a working set, or
2340 // it might be a prototype PTE or paged out or even in transition.
2341 //
2342 if (PointerPte->u.Long == MmDecommittedPte.u.Long)
2343 {
2344 //
2345 // It's already decommited, so there's nothing for us to do here
2346 //
2347 CommitReduction++;
2348 }
2349 else
2350 {
2351 //
2352 // Remove it from the counters, and check if it was valid or not
2353 //
2354 //Process->NumberOfPrivatePages--;
2355 if (PteContents.u.Hard.Valid)
2356 {
2357 //
2358 // It's valid. At this point make sure that it is not a ROS
2359 // PFN. Also, we don't support ProtoPTEs in this code path.
2360 //
2361 Pfn1 = MiGetPfnEntry(PteContents.u.Hard.PageFrameNumber);
2362 ASSERT(MI_IS_ROS_PFN(Pfn1) == FALSE);
2363 ASSERT(Pfn1->u3.e1.PrototypePte == FALSE);
2364
2365 //
2366 // Flush any pending PTEs that we had not yet flushed, if our
2367 // list has gotten too big, then add this PTE to the flush list.
2368 //
2369 if (PteCount == 256)
2370 {
2371 MiProcessValidPteList(ValidPteList, PteCount);
2372 PteCount = 0;
2373 }
2374 ValidPteList[PteCount++] = PointerPte;
2375 }
2376 else
2377 {
2378 //
2379 // We do not support any of these other scenarios at the moment
2380 //
2381 ASSERT(PteContents.u.Soft.Prototype == 0);
2382 ASSERT(PteContents.u.Soft.Transition == 0);
2383 ASSERT(PteContents.u.Soft.PageFileHigh == 0);
2384
2385 //
2386 // So the only other possibility is that it is still a demand
2387 // zero PTE, in which case we undo the accounting we did
2388 // earlier and simply make the page decommitted.
2389 //
2390 //Process->NumberOfPrivatePages++;
2391 MI_WRITE_INVALID_PTE(PointerPte, MmDecommittedPte);
2392 }
2393 }
2394 }
2395 else
2396 {
2397 //
2398 // This used to be a zero PTE and it no longer is, so we must add a
2399 // reference to the pagetable.
2400 //
2401 MiIncrementPageTableReferences(StartingAddress);
2402
2403 //
2404 // Next, we account for decommitted PTEs and make the PTE as such
2405 //
2406 if (PointerPte > CommitPte) CommitReduction++;
2407 MI_WRITE_INVALID_PTE(PointerPte, MmDecommittedPte);
2408 }
2409
2410 //
2411 // Move to the next PTE and the next address
2412 //
2413 PointerPte++;
2414 StartingAddress = (PVOID)((ULONG_PTR)StartingAddress + PAGE_SIZE);
2415 }
2416
2417 //
2418 // Flush any dangling PTEs from the loop in the last page table, and then
2419 // release the working set and return the commit reduction accounting.
2420 //
2421 if (PteCount) MiProcessValidPteList(ValidPteList, PteCount);
2422 MiUnlockProcessWorkingSetUnsafe(Process, CurrentThread);
2423 return CommitReduction;
2424 }
2425
2426 /* PUBLIC FUNCTIONS ***********************************************************/
2427
2428 /*
2429 * @unimplemented
2430 */
2431 PVOID
2432 NTAPI
2433 MmGetVirtualForPhysical(IN PHYSICAL_ADDRESS PhysicalAddress)
2434 {
2435 UNIMPLEMENTED;
2436 return 0;
2437 }
2438
2439 /*
2440 * @unimplemented
2441 */
2442 PVOID
2443 NTAPI
2444 MmSecureVirtualMemory(IN PVOID Address,
2445 IN SIZE_T Length,
2446 IN ULONG Mode)
2447 {
2448 static BOOLEAN Warn; if (!Warn++) UNIMPLEMENTED;
2449 return Address;
2450 }
2451
2452 /*
2453 * @unimplemented
2454 */
2455 VOID
2456 NTAPI
2457 MmUnsecureVirtualMemory(IN PVOID SecureMem)
2458 {
2459 static BOOLEAN Warn; if (!Warn++) UNIMPLEMENTED;
2460 }
2461
2462 /* SYSTEM CALLS ***************************************************************/
2463
2464 NTSTATUS
2465 NTAPI
2466 NtReadVirtualMemory(IN HANDLE ProcessHandle,
2467 IN PVOID BaseAddress,
2468 OUT PVOID Buffer,
2469 IN SIZE_T NumberOfBytesToRead,
2470 OUT PSIZE_T NumberOfBytesRead OPTIONAL)
2471 {
2472 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
2473 PEPROCESS Process;
2474 NTSTATUS Status = STATUS_SUCCESS;
2475 SIZE_T BytesRead = 0;
2476 PAGED_CODE();
2477
2478 //
2479 // Check if we came from user mode
2480 //
2481 if (PreviousMode != KernelMode)
2482 {
2483 //
2484 // Validate the read addresses
2485 //
2486 if ((((ULONG_PTR)BaseAddress + NumberOfBytesToRead) < (ULONG_PTR)BaseAddress) ||
2487 (((ULONG_PTR)Buffer + NumberOfBytesToRead) < (ULONG_PTR)Buffer) ||
2488 (((ULONG_PTR)BaseAddress + NumberOfBytesToRead) > MmUserProbeAddress) ||
2489 (((ULONG_PTR)Buffer + NumberOfBytesToRead) > MmUserProbeAddress))
2490 {
2491 //
2492 // Don't allow to write into kernel space
2493 //
2494 return STATUS_ACCESS_VIOLATION;
2495 }
2496
2497 //
2498 // Enter SEH for probe
2499 //
2500 _SEH2_TRY
2501 {
2502 //
2503 // Probe the output value
2504 //
2505 if (NumberOfBytesRead) ProbeForWriteSize_t(NumberOfBytesRead);
2506 }
2507 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2508 {
2509 //
2510 // Get exception code
2511 //
2512 _SEH2_YIELD(return _SEH2_GetExceptionCode());
2513 }
2514 _SEH2_END;
2515 }
2516
2517 //
2518 // Don't do zero-byte transfers
2519 //
2520 if (NumberOfBytesToRead)
2521 {
2522 //
2523 // Reference the process
2524 //
2525 Status = ObReferenceObjectByHandle(ProcessHandle,
2526 PROCESS_VM_READ,
2527 PsProcessType,
2528 PreviousMode,
2529 (PVOID*)(&Process),
2530 NULL);
2531 if (NT_SUCCESS(Status))
2532 {
2533 //
2534 // Do the copy
2535 //
2536 Status = MmCopyVirtualMemory(Process,
2537 BaseAddress,
2538 PsGetCurrentProcess(),
2539 Buffer,
2540 NumberOfBytesToRead,
2541 PreviousMode,
2542 &BytesRead);
2543
2544 //
2545 // Dereference the process
2546 //
2547 ObDereferenceObject(Process);
2548 }
2549 }
2550
2551 //
2552 // Check if the caller sent this parameter
2553 //
2554 if (NumberOfBytesRead)
2555 {
2556 //
2557 // Enter SEH to guard write
2558 //
2559 _SEH2_TRY
2560 {
2561 //
2562 // Return the number of bytes read
2563 //
2564 *NumberOfBytesRead = BytesRead;
2565 }
2566 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2567 {
2568 }
2569 _SEH2_END;
2570 }
2571
2572 //
2573 // Return status
2574 //
2575 return Status;
2576 }
2577
2578 NTSTATUS
2579 NTAPI
2580 NtWriteVirtualMemory(IN HANDLE ProcessHandle,
2581 IN PVOID BaseAddress,
2582 IN PVOID Buffer,
2583 IN SIZE_T NumberOfBytesToWrite,
2584 OUT PSIZE_T NumberOfBytesWritten OPTIONAL)
2585 {
2586 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
2587 PEPROCESS Process;
2588 NTSTATUS Status = STATUS_SUCCESS;
2589 SIZE_T BytesWritten = 0;
2590 PAGED_CODE();
2591
2592 //
2593 // Check if we came from user mode
2594 //
2595 if (PreviousMode != KernelMode)
2596 {
2597 //
2598 // Validate the read addresses
2599 //
2600 if ((((ULONG_PTR)BaseAddress + NumberOfBytesToWrite) < (ULONG_PTR)BaseAddress) ||
2601 (((ULONG_PTR)Buffer + NumberOfBytesToWrite) < (ULONG_PTR)Buffer) ||
2602 (((ULONG_PTR)BaseAddress + NumberOfBytesToWrite) > MmUserProbeAddress) ||
2603 (((ULONG_PTR)Buffer + NumberOfBytesToWrite) > MmUserProbeAddress))
2604 {
2605 //
2606 // Don't allow to write into kernel space
2607 //
2608 return STATUS_ACCESS_VIOLATION;
2609 }
2610
2611 //
2612 // Enter SEH for probe
2613 //
2614 _SEH2_TRY
2615 {
2616 //
2617 // Probe the output value
2618 //
2619 if (NumberOfBytesWritten) ProbeForWriteSize_t(NumberOfBytesWritten);
2620 }
2621 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2622 {
2623 //
2624 // Get exception code
2625 //
2626 _SEH2_YIELD(return _SEH2_GetExceptionCode());
2627 }
2628 _SEH2_END;
2629 }
2630
2631 //
2632 // Don't do zero-byte transfers
2633 //
2634 if (NumberOfBytesToWrite)
2635 {
2636 //
2637 // Reference the process
2638 //
2639 Status = ObReferenceObjectByHandle(ProcessHandle,
2640 PROCESS_VM_WRITE,
2641 PsProcessType,
2642 PreviousMode,
2643 (PVOID*)&Process,
2644 NULL);
2645 if (NT_SUCCESS(Status))
2646 {
2647 //
2648 // Do the copy
2649 //
2650 Status = MmCopyVirtualMemory(PsGetCurrentProcess(),
2651 Buffer,
2652 Process,
2653 BaseAddress,
2654 NumberOfBytesToWrite,
2655 PreviousMode,
2656 &BytesWritten);
2657
2658 //
2659 // Dereference the process
2660 //
2661 ObDereferenceObject(Process);
2662 }
2663 }
2664
2665 //
2666 // Check if the caller sent this parameter
2667 //
2668 if (NumberOfBytesWritten)
2669 {
2670 //
2671 // Enter SEH to guard write
2672 //
2673 _SEH2_TRY
2674 {
2675 //
2676 // Return the number of bytes written
2677 //
2678 *NumberOfBytesWritten = BytesWritten;
2679 }
2680 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2681 {
2682 }
2683 _SEH2_END;
2684 }
2685
2686 //
2687 // Return status
2688 //
2689 return Status;
2690 }
2691
2692 NTSTATUS
2693 NTAPI
2694 NtProtectVirtualMemory(IN HANDLE ProcessHandle,
2695 IN OUT PVOID *UnsafeBaseAddress,
2696 IN OUT SIZE_T *UnsafeNumberOfBytesToProtect,
2697 IN ULONG NewAccessProtection,
2698 OUT PULONG UnsafeOldAccessProtection)
2699 {
2700 PEPROCESS Process;
2701 ULONG OldAccessProtection;
2702 ULONG Protection;
2703 PEPROCESS CurrentProcess = PsGetCurrentProcess();
2704 PVOID BaseAddress = NULL;
2705 SIZE_T NumberOfBytesToProtect = 0;
2706 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
2707 NTSTATUS Status;
2708 BOOLEAN Attached = FALSE;
2709 KAPC_STATE ApcState;
2710 PAGED_CODE();
2711
2712 //
2713 // Check for valid protection flags
2714 //
2715 Protection = NewAccessProtection & ~(PAGE_GUARD|PAGE_NOCACHE);
2716 if (Protection != PAGE_NOACCESS &&
2717 Protection != PAGE_READONLY &&
2718 Protection != PAGE_READWRITE &&
2719 Protection != PAGE_WRITECOPY &&
2720 Protection != PAGE_EXECUTE &&
2721 Protection != PAGE_EXECUTE_READ &&
2722 Protection != PAGE_EXECUTE_READWRITE &&
2723 Protection != PAGE_EXECUTE_WRITECOPY)
2724 {
2725 //
2726 // Fail
2727 //
2728 return STATUS_INVALID_PAGE_PROTECTION;
2729 }
2730
2731 //
2732 // Check if we came from user mode
2733 //
2734 if (PreviousMode != KernelMode)
2735 {
2736 //
2737 // Enter SEH for probing
2738 //
2739 _SEH2_TRY
2740 {
2741 //
2742 // Validate all outputs
2743 //
2744 ProbeForWritePointer(UnsafeBaseAddress);
2745 ProbeForWriteSize_t(UnsafeNumberOfBytesToProtect);
2746 ProbeForWriteUlong(UnsafeOldAccessProtection);
2747
2748 //
2749 // Capture them
2750 //
2751 BaseAddress = *UnsafeBaseAddress;
2752 NumberOfBytesToProtect = *UnsafeNumberOfBytesToProtect;
2753 }
2754 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2755 {
2756 //
2757 // Get exception code
2758 //
2759 _SEH2_YIELD(return _SEH2_GetExceptionCode());
2760 }
2761 _SEH2_END;
2762 }
2763 else
2764 {
2765 //
2766 // Capture directly
2767 //
2768 BaseAddress = *UnsafeBaseAddress;
2769 NumberOfBytesToProtect = *UnsafeNumberOfBytesToProtect;
2770 }
2771
2772 //
2773 // Catch illegal base address
2774 //
2775 if (BaseAddress > MM_HIGHEST_USER_ADDRESS) return STATUS_INVALID_PARAMETER_2;
2776
2777 //
2778 // Catch illegal region size
2779 //
2780 if ((MmUserProbeAddress - (ULONG_PTR)BaseAddress) < NumberOfBytesToProtect)
2781 {
2782 //
2783 // Fail
2784 //
2785 return STATUS_INVALID_PARAMETER_3;
2786 }
2787
2788 //
2789 // 0 is also illegal
2790 //
2791 if (!NumberOfBytesToProtect) return STATUS_INVALID_PARAMETER_3;
2792
2793 //
2794 // Get a reference to the process
2795 //
2796 Status = ObReferenceObjectByHandle(ProcessHandle,
2797 PROCESS_VM_OPERATION,
2798 PsProcessType,
2799 PreviousMode,
2800 (PVOID*)(&Process),
2801 NULL);
2802 if (!NT_SUCCESS(Status)) return Status;
2803
2804 //
2805 // Check if we should attach
2806 //
2807 if (CurrentProcess != Process)
2808 {
2809 //
2810 // Do it
2811 //
2812 KeStackAttachProcess(&Process->Pcb, &ApcState);
2813 Attached = TRUE;
2814 }
2815
2816 //
2817 // Do the actual work
2818 //
2819 Status = MiProtectVirtualMemory(Process,
2820 &BaseAddress,
2821 &NumberOfBytesToProtect,
2822 NewAccessProtection,
2823 &OldAccessProtection);
2824
2825 //
2826 // Detach if needed
2827 //
2828 if (Attached) KeUnstackDetachProcess(&ApcState);
2829
2830 //
2831 // Release reference
2832 //
2833 ObDereferenceObject(Process);
2834
2835 //
2836 // Enter SEH to return data
2837 //
2838 _SEH2_TRY
2839 {
2840 //
2841 // Return data to user
2842 //
2843 *UnsafeOldAccessProtection = OldAccessProtection;
2844 *UnsafeBaseAddress = BaseAddress;
2845 *UnsafeNumberOfBytesToProtect = NumberOfBytesToProtect;
2846 }
2847 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2848 {
2849 }
2850 _SEH2_END;
2851
2852 //
2853 // Return status
2854 //
2855 return Status;
2856 }
2857
2858 NTSTATUS
2859 NTAPI
2860 NtLockVirtualMemory(IN HANDLE ProcessHandle,
2861 IN OUT PVOID *BaseAddress,
2862 IN OUT PSIZE_T NumberOfBytesToLock,
2863 IN ULONG MapType)
2864 {
2865 PEPROCESS Process;
2866 PEPROCESS CurrentProcess = PsGetCurrentProcess();
2867 NTSTATUS Status;
2868 BOOLEAN Attached = FALSE;
2869 KAPC_STATE ApcState;
2870 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
2871 PVOID CapturedBaseAddress;
2872 SIZE_T CapturedBytesToLock;
2873 PAGED_CODE();
2874
2875 //
2876 // Validate flags
2877 //
2878 if ((MapType & ~(MAP_PROCESS | MAP_SYSTEM)))
2879 {
2880 //
2881 // Invalid set of flags
2882 //
2883 return STATUS_INVALID_PARAMETER;
2884 }
2885
2886 //
2887 // At least one flag must be specified
2888 //
2889 if (!(MapType & (MAP_PROCESS | MAP_SYSTEM)))
2890 {
2891 //
2892 // No flag given
2893 //
2894 return STATUS_INVALID_PARAMETER;
2895 }
2896
2897 //
2898 // Enter SEH for probing
2899 //
2900 _SEH2_TRY
2901 {
2902 //
2903 // Validate output data
2904 //
2905 ProbeForWritePointer(BaseAddress);
2906 ProbeForWriteSize_t(NumberOfBytesToLock);
2907
2908 //
2909 // Capture it
2910 //
2911 CapturedBaseAddress = *BaseAddress;
2912 CapturedBytesToLock = *NumberOfBytesToLock;
2913 }
2914 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2915 {
2916 //
2917 // Get exception code
2918 //
2919 _SEH2_YIELD(return _SEH2_GetExceptionCode());
2920 }
2921 _SEH2_END;
2922
2923 //
2924 // Catch illegal base address
2925 //
2926 if (CapturedBaseAddress > MM_HIGHEST_USER_ADDRESS) return STATUS_INVALID_PARAMETER;
2927
2928 //
2929 // Catch illegal region size
2930 //
2931 if ((MmUserProbeAddress - (ULONG_PTR)CapturedBaseAddress) < CapturedBytesToLock)
2932 {
2933 //
2934 // Fail
2935 //
2936 return STATUS_INVALID_PARAMETER;
2937 }
2938
2939 //
2940 // 0 is also illegal
2941 //
2942 if (!CapturedBytesToLock) return STATUS_INVALID_PARAMETER;
2943
2944 //
2945 // Get a reference to the process
2946 //
2947 Status = ObReferenceObjectByHandle(ProcessHandle,
2948 PROCESS_VM_OPERATION,
2949 PsProcessType,
2950 PreviousMode,
2951 (PVOID*)(&Process),
2952 NULL);
2953 if (!NT_SUCCESS(Status)) return Status;
2954
2955 //
2956 // Check if this is is system-mapped
2957 //
2958 if (MapType & MAP_SYSTEM)
2959 {
2960 //
2961 // Check for required privilege
2962 //
2963 if (!SeSinglePrivilegeCheck(SeLockMemoryPrivilege, PreviousMode))
2964 {
2965 //
2966 // Fail: Don't have it
2967 //
2968 ObDereferenceObject(Process);
2969 return STATUS_PRIVILEGE_NOT_HELD;
2970 }
2971 }
2972
2973 //
2974 // Check if we should attach
2975 //
2976 if (CurrentProcess != Process)
2977 {
2978 //
2979 // Do it
2980 //
2981 KeStackAttachProcess(&Process->Pcb, &ApcState);
2982 Attached = TRUE;
2983 }
2984
2985 //
2986 // Oops :(
2987 //
2988 UNIMPLEMENTED;
2989
2990 //
2991 // Detach if needed
2992 //
2993 if (Attached) KeUnstackDetachProcess(&ApcState);
2994
2995 //
2996 // Release reference
2997 //
2998 ObDereferenceObject(Process);
2999
3000 //
3001 // Enter SEH to return data
3002 //
3003 _SEH2_TRY
3004 {
3005 //
3006 // Return data to user
3007 //
3008 *BaseAddress = CapturedBaseAddress;
3009 *NumberOfBytesToLock = 0;
3010 }
3011 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
3012 {
3013 //
3014 // Get exception code
3015 //
3016 _SEH2_YIELD(return _SEH2_GetExceptionCode());
3017 }
3018 _SEH2_END;
3019
3020 //
3021 // Return status
3022 //
3023 return STATUS_SUCCESS;
3024 }
3025
3026 NTSTATUS
3027 NTAPI
3028 NtUnlockVirtualMemory(IN HANDLE ProcessHandle,
3029 IN OUT PVOID *BaseAddress,
3030 IN OUT PSIZE_T NumberOfBytesToUnlock,
3031 IN ULONG MapType)
3032 {
3033 PEPROCESS Process;
3034 PEPROCESS CurrentProcess = PsGetCurrentProcess();
3035 NTSTATUS Status;
3036 BOOLEAN Attached = FALSE;
3037 KAPC_STATE ApcState;
3038 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
3039 PVOID CapturedBaseAddress;
3040 SIZE_T CapturedBytesToUnlock;
3041 PAGED_CODE();
3042
3043 //
3044 // Validate flags
3045 //
3046 if ((MapType & ~(MAP_PROCESS | MAP_SYSTEM)))
3047 {
3048 //
3049 // Invalid set of flags
3050 //
3051 return STATUS_INVALID_PARAMETER;
3052 }
3053
3054 //
3055 // At least one flag must be specified
3056 //
3057 if (!(MapType & (MAP_PROCESS | MAP_SYSTEM)))
3058 {
3059 //
3060 // No flag given
3061 //
3062 return STATUS_INVALID_PARAMETER;
3063 }
3064
3065 //
3066 // Enter SEH for probing
3067 //
3068 _SEH2_TRY
3069 {
3070 //
3071 // Validate output data
3072 //
3073 ProbeForWritePointer(BaseAddress);
3074 ProbeForWriteSize_t(NumberOfBytesToUnlock);
3075
3076 //
3077 // Capture it
3078 //
3079 CapturedBaseAddress = *BaseAddress;
3080 CapturedBytesToUnlock = *NumberOfBytesToUnlock;
3081 }
3082 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
3083 {
3084 //
3085 // Get exception code
3086 //
3087 _SEH2_YIELD(return _SEH2_GetExceptionCode());
3088 }
3089 _SEH2_END;
3090
3091 //
3092 // Catch illegal base address
3093 //
3094 if (CapturedBaseAddress > MM_HIGHEST_USER_ADDRESS) return STATUS_INVALID_PARAMETER;
3095
3096 //
3097 // Catch illegal region size
3098 //
3099 if ((MmUserProbeAddress - (ULONG_PTR)CapturedBaseAddress) < CapturedBytesToUnlock)
3100 {
3101 //
3102 // Fail
3103 //
3104 return STATUS_INVALID_PARAMETER;
3105 }
3106
3107 //
3108 // 0 is also illegal
3109 //
3110 if (!CapturedBytesToUnlock) return STATUS_INVALID_PARAMETER;
3111
3112 //
3113 // Get a reference to the process
3114 //
3115 Status = ObReferenceObjectByHandle(ProcessHandle,
3116 PROCESS_VM_OPERATION,
3117 PsProcessType,
3118 PreviousMode,
3119 (PVOID*)(&Process),
3120 NULL);
3121 if (!NT_SUCCESS(Status)) return Status;
3122
3123 //
3124 // Check if this is is system-mapped
3125 //
3126 if (MapType & MAP_SYSTEM)
3127 {
3128 //
3129 // Check for required privilege
3130 //
3131 if (!SeSinglePrivilegeCheck(SeLockMemoryPrivilege, PreviousMode))
3132 {
3133 //
3134 // Fail: Don't have it
3135 //
3136 ObDereferenceObject(Process);
3137 return STATUS_PRIVILEGE_NOT_HELD;
3138 }
3139 }
3140
3141 //
3142 // Check if we should attach
3143 //
3144 if (CurrentProcess != Process)
3145 {
3146 //
3147 // Do it
3148 //
3149 KeStackAttachProcess(&Process->Pcb, &ApcState);
3150 Attached = TRUE;
3151 }
3152
3153 //
3154 // Oops :(
3155 //
3156 UNIMPLEMENTED;
3157
3158 //
3159 // Detach if needed
3160 //
3161 if (Attached) KeUnstackDetachProcess(&ApcState);
3162
3163 //
3164 // Release reference
3165 //
3166 ObDereferenceObject(Process);
3167
3168 //
3169 // Enter SEH to return data
3170 //
3171 _SEH2_TRY
3172 {
3173 //
3174 // Return data to user
3175 //
3176 *BaseAddress = PAGE_ALIGN(CapturedBaseAddress);
3177 *NumberOfBytesToUnlock = 0;
3178 }
3179 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
3180 {
3181 //
3182 // Get exception code
3183 //
3184 _SEH2_YIELD(return _SEH2_GetExceptionCode());
3185 }
3186 _SEH2_END;
3187
3188 //
3189 // Return status
3190 //
3191 return STATUS_SUCCESS;
3192 }
3193
3194 NTSTATUS
3195 NTAPI
3196 NtFlushVirtualMemory(IN HANDLE ProcessHandle,
3197 IN OUT PVOID *BaseAddress,
3198 IN OUT PSIZE_T NumberOfBytesToFlush,
3199 OUT PIO_STATUS_BLOCK IoStatusBlock)
3200 {
3201 PEPROCESS Process;
3202 NTSTATUS Status;
3203 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
3204 PVOID CapturedBaseAddress;
3205 SIZE_T CapturedBytesToFlush;
3206 IO_STATUS_BLOCK LocalStatusBlock;
3207 PAGED_CODE();
3208
3209 //
3210 // Check if we came from user mode
3211 //
3212 if (PreviousMode != KernelMode)
3213 {
3214 //
3215 // Enter SEH for probing
3216 //
3217 _SEH2_TRY
3218 {
3219 //
3220 // Validate all outputs
3221 //
3222 ProbeForWritePointer(BaseAddress);
3223 ProbeForWriteSize_t(NumberOfBytesToFlush);
3224 ProbeForWriteIoStatusBlock(IoStatusBlock);
3225
3226 //
3227 // Capture them
3228 //
3229 CapturedBaseAddress = *BaseAddress;
3230 CapturedBytesToFlush = *NumberOfBytesToFlush;
3231 }
3232 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
3233 {
3234 //
3235 // Get exception code
3236 //
3237 _SEH2_YIELD(return _SEH2_GetExceptionCode());
3238 }
3239 _SEH2_END;
3240 }
3241 else
3242 {
3243 //
3244 // Capture directly
3245 //
3246 CapturedBaseAddress = *BaseAddress;
3247 CapturedBytesToFlush = *NumberOfBytesToFlush;
3248 }
3249
3250 //
3251 // Catch illegal base address
3252 //
3253 if (CapturedBaseAddress > MM_HIGHEST_USER_ADDRESS) return STATUS_INVALID_PARAMETER;
3254
3255 //
3256 // Catch illegal region size
3257 //
3258 if ((MmUserProbeAddress - (ULONG_PTR)CapturedBaseAddress) < CapturedBytesToFlush)
3259 {
3260 //
3261 // Fail
3262 //
3263 return STATUS_INVALID_PARAMETER;
3264 }
3265
3266 //
3267 // Get a reference to the process
3268 //
3269 Status = ObReferenceObjectByHandle(ProcessHandle,
3270 PROCESS_VM_OPERATION,
3271 PsProcessType,
3272 PreviousMode,
3273 (PVOID*)(&Process),
3274 NULL);
3275 if (!NT_SUCCESS(Status)) return Status;
3276
3277 //
3278 // Do it
3279 //
3280 Status = MmFlushVirtualMemory(Process,
3281 &CapturedBaseAddress,
3282 &CapturedBytesToFlush,
3283 &LocalStatusBlock);
3284
3285 //
3286 // Release reference
3287 //
3288 ObDereferenceObject(Process);
3289
3290 //
3291 // Enter SEH to return data
3292 //
3293 _SEH2_TRY
3294 {
3295 //
3296 // Return data to user
3297 //
3298 *BaseAddress = PAGE_ALIGN(CapturedBaseAddress);
3299 *NumberOfBytesToFlush = 0;
3300 *IoStatusBlock = LocalStatusBlock;
3301 }
3302 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
3303 {
3304 }
3305 _SEH2_END;
3306
3307 //
3308 // Return status
3309 //
3310 return Status;
3311 }
3312
3313 /*
3314 * @unimplemented
3315 */
3316 NTSTATUS
3317 NTAPI
3318 NtGetWriteWatch(IN HANDLE ProcessHandle,
3319 IN ULONG Flags,
3320 IN PVOID BaseAddress,
3321 IN SIZE_T RegionSize,
3322 IN PVOID *UserAddressArray,
3323 OUT PULONG_PTR EntriesInUserAddressArray,
3324 OUT PULONG Granularity)
3325 {
3326 PEPROCESS Process;
3327 NTSTATUS Status;
3328 PVOID EndAddress;
3329 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
3330 ULONG_PTR CapturedEntryCount;
3331 PAGED_CODE();
3332
3333 //
3334 // Check if we came from user mode
3335 //
3336 if (PreviousMode != KernelMode)
3337 {
3338 //
3339 // Enter SEH for probing
3340 //
3341 _SEH2_TRY
3342 {
3343 //
3344 // Catch illegal base address
3345 //
3346 if (BaseAddress > MM_HIGHEST_USER_ADDRESS) return STATUS_INVALID_PARAMETER_2;
3347
3348 //
3349 // Catch illegal region size
3350 //
3351 if ((MmUserProbeAddress - (ULONG_PTR)BaseAddress) < RegionSize)
3352 {
3353 //
3354 // Fail
3355 //
3356 return STATUS_INVALID_PARAMETER_3;
3357 }
3358
3359 //
3360 // Validate all data
3361 //
3362 ProbeForWriteSize_t(EntriesInUserAddressArray);
3363 ProbeForWriteUlong(Granularity);
3364
3365 //
3366 // Capture them
3367 //
3368 CapturedEntryCount = *EntriesInUserAddressArray;
3369
3370 //
3371 // Must have a count
3372 //
3373 if (CapturedEntryCount == 0) return STATUS_INVALID_PARAMETER_5;
3374
3375 //
3376 // Can't be larger than the maximum
3377 //
3378 if (CapturedEntryCount > (MAXULONG_PTR / sizeof(ULONG_PTR)))
3379 {
3380 //
3381 // Fail
3382 //
3383 return STATUS_INVALID_PARAMETER_5;
3384 }
3385
3386 //
3387 // Probe the actual array
3388 //
3389 ProbeForWrite(UserAddressArray,
3390 CapturedEntryCount * sizeof(PVOID),
3391 sizeof(PVOID));
3392 }
3393 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
3394 {
3395 //
3396 // Get exception code
3397 //
3398 _SEH2_YIELD(return _SEH2_GetExceptionCode());
3399 }
3400 _SEH2_END;
3401 }
3402 else
3403 {
3404 //
3405 // Capture directly
3406 //
3407 CapturedEntryCount = *EntriesInUserAddressArray;
3408 ASSERT(CapturedEntryCount != 0);
3409 }
3410
3411 //
3412 // Check if this is a local request
3413 //
3414 if (ProcessHandle == NtCurrentProcess())
3415 {
3416 //
3417 // No need to reference the process
3418 //
3419 Process = PsGetCurrentProcess();
3420 }
3421 else
3422 {
3423 //
3424 // Reference the target
3425 //
3426 Status = ObReferenceObjectByHandle(ProcessHandle,
3427 PROCESS_VM_OPERATION,
3428 PsProcessType,
3429 PreviousMode,
3430 (PVOID *)&Process,
3431 NULL);
3432 if (!NT_SUCCESS(Status)) return Status;
3433 }
3434
3435 //
3436 // Compute the last address and validate it
3437 //
3438 EndAddress = (PVOID)((ULONG_PTR)BaseAddress + RegionSize - 1);
3439 if (BaseAddress > EndAddress)
3440 {
3441 //
3442 // Fail
3443 //
3444 if (ProcessHandle != NtCurrentProcess()) ObDereferenceObject(Process);
3445 return STATUS_INVALID_PARAMETER_4;
3446 }
3447
3448 //
3449 // Oops :(
3450 //
3451 UNIMPLEMENTED;
3452
3453 //
3454 // Dereference if needed
3455 //
3456 if (ProcessHandle != NtCurrentProcess()) ObDereferenceObject(Process);
3457
3458 //
3459 // Enter SEH to return data
3460 //
3461 _SEH2_TRY
3462 {
3463 //
3464 // Return data to user
3465 //
3466 *EntriesInUserAddressArray = 0;
3467 *Granularity = PAGE_SIZE;
3468 }
3469 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
3470 {
3471 //
3472 // Get exception code
3473 //
3474 Status = _SEH2_GetExceptionCode();
3475 }
3476 _SEH2_END;
3477
3478 //
3479 // Return success
3480 //
3481 return STATUS_SUCCESS;
3482 }
3483
3484 /*
3485 * @unimplemented
3486 */
3487 NTSTATUS
3488 NTAPI
3489 NtResetWriteWatch(IN HANDLE ProcessHandle,
3490 IN PVOID BaseAddress,
3491 IN SIZE_T RegionSize)
3492 {
3493 PVOID EndAddress;
3494 PEPROCESS Process;
3495 NTSTATUS Status;
3496 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
3497 ASSERT (KeGetCurrentIrql() == PASSIVE_LEVEL);
3498
3499 //
3500 // Catch illegal base address
3501 //
3502 if (BaseAddress > MM_HIGHEST_USER_ADDRESS) return STATUS_INVALID_PARAMETER_2;
3503
3504 //
3505 // Catch illegal region size
3506 //
3507 if ((MmUserProbeAddress - (ULONG_PTR)BaseAddress) < RegionSize)
3508 {
3509 //
3510 // Fail
3511 //
3512 return STATUS_INVALID_PARAMETER_3;
3513 }
3514
3515 //
3516 // Check if this is a local request
3517 //
3518 if (ProcessHandle == NtCurrentProcess())
3519 {
3520 //
3521 // No need to reference the process
3522 //
3523 Process = PsGetCurrentProcess();
3524 }
3525 else
3526 {
3527 //
3528 // Reference the target
3529 //
3530 Status = ObReferenceObjectByHandle(ProcessHandle,
3531 PROCESS_VM_OPERATION,
3532 PsProcessType,
3533 PreviousMode,
3534 (PVOID *)&Process,
3535 NULL);
3536 if (!NT_SUCCESS(Status)) return Status;
3537 }
3538
3539 //
3540 // Compute the last address and validate it
3541 //
3542 EndAddress = (PVOID)((ULONG_PTR)BaseAddress + RegionSize - 1);
3543 if (BaseAddress > EndAddress)
3544 {
3545 //
3546 // Fail
3547 //
3548 if (ProcessHandle != NtCurrentProcess()) ObDereferenceObject(Process);
3549 return STATUS_INVALID_PARAMETER_3;
3550 }
3551
3552 //
3553 // Oops :(
3554 //
3555 UNIMPLEMENTED;
3556
3557 //
3558 // Dereference if needed
3559 //
3560 if (ProcessHandle != NtCurrentProcess()) ObDereferenceObject(Process);
3561
3562 //
3563 // Return success
3564 //
3565 return STATUS_SUCCESS;
3566 }
3567
3568 NTSTATUS
3569 NTAPI
3570 NtQueryVirtualMemory(IN HANDLE ProcessHandle,
3571 IN PVOID BaseAddress,
3572 IN MEMORY_INFORMATION_CLASS MemoryInformationClass,
3573 OUT PVOID MemoryInformation,
3574 IN SIZE_T MemoryInformationLength,
3575 OUT PSIZE_T ReturnLength)
3576 {
3577 NTSTATUS Status = STATUS_SUCCESS;
3578 KPROCESSOR_MODE PreviousMode;
3579
3580 DPRINT("Querying class %d about address: %p\n", MemoryInformationClass, BaseAddress);
3581
3582 /* Bail out if the address is invalid */
3583 if (BaseAddress > MM_HIGHEST_USER_ADDRESS) return STATUS_INVALID_PARAMETER;
3584
3585 /* Probe return buffer */
3586 PreviousMode = ExGetPreviousMode();
3587 if (PreviousMode != KernelMode)
3588 {
3589 _SEH2_TRY
3590 {
3591 ProbeForWrite(MemoryInformation,
3592 MemoryInformationLength,
3593 sizeof(ULONG_PTR));
3594
3595 if (ReturnLength) ProbeForWriteSize_t(ReturnLength);
3596 }
3597 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
3598 {
3599 Status = _SEH2_GetExceptionCode();
3600 }
3601 _SEH2_END;
3602
3603 if (!NT_SUCCESS(Status))
3604 {
3605 return Status;
3606 }
3607 }
3608
3609 switch(MemoryInformationClass)
3610 {
3611 case MemoryBasicInformation:
3612 /* Validate the size information of the class */
3613 if (MemoryInformationLength < sizeof(MEMORY_BASIC_INFORMATION))
3614 {
3615 /* The size is invalid */
3616 return STATUS_INFO_LENGTH_MISMATCH;
3617 }
3618 Status = MiQueryMemoryBasicInformation(ProcessHandle,
3619 BaseAddress,
3620 MemoryInformation,
3621 MemoryInformationLength,
3622 ReturnLength);
3623 break;
3624
3625 case MemorySectionName:
3626 /* Validate the size information of the class */
3627 if (MemoryInformationLength < sizeof(MEMORY_SECTION_NAME))
3628 {
3629 /* The size is invalid */
3630 return STATUS_INFO_LENGTH_MISMATCH;
3631 }
3632 Status = MiQueryMemorySectionName(ProcessHandle,
3633 BaseAddress,
3634 MemoryInformation,
3635 MemoryInformationLength,
3636 ReturnLength);
3637 break;
3638 case MemoryWorkingSetList:
3639 case MemoryBasicVlmInformation:
3640 default:
3641 DPRINT1("Unhandled memory information class %d\n", MemoryInformationClass);
3642 break;
3643 }
3644
3645 return Status;
3646 }
3647
3648 /*
3649 * @implemented
3650 */
3651 NTSTATUS
3652 NTAPI
3653 NtAllocateVirtualMemory(IN HANDLE ProcessHandle,
3654 IN OUT PVOID* UBaseAddress,
3655 IN ULONG_PTR ZeroBits,
3656 IN OUT PSIZE_T URegionSize,
3657 IN ULONG AllocationType,
3658 IN ULONG Protect)
3659 {
3660 PEPROCESS Process;
3661 PMEMORY_AREA MemoryArea;
3662 PFN_NUMBER PageCount;
3663 PMMVAD Vad, FoundVad;
3664 NTSTATUS Status;
3665 PMMSUPPORT AddressSpace;
3666 PVOID PBaseAddress;
3667 ULONG_PTR PRegionSize, StartingAddress, EndingAddress;
3668 PEPROCESS CurrentProcess = PsGetCurrentProcess();
3669 KPROCESSOR_MODE PreviousMode = KeGetPreviousMode();
3670 PETHREAD CurrentThread = PsGetCurrentThread();
3671 KAPC_STATE ApcState;
3672 ULONG ProtectionMask, QuotaCharge = 0, QuotaFree = 0;
3673 BOOLEAN Attached = FALSE, ChangeProtection = FALSE;
3674 MMPTE TempPte;
3675 PMMPTE PointerPte, PointerPde, LastPte;
3676 PAGED_CODE();
3677
3678 /* Check for valid Zero bits */
3679 if (ZeroBits > 21)
3680 {
3681 DPRINT1("Too many zero bits\n");
3682 return STATUS_INVALID_PARAMETER_3;
3683 }
3684
3685 /* Check for valid Allocation Types */
3686 if ((AllocationType & ~(MEM_COMMIT | MEM_RESERVE | MEM_RESET | MEM_PHYSICAL |
3687 MEM_TOP_DOWN | MEM_WRITE_WATCH)))
3688 {
3689 DPRINT1("Invalid Allocation Type\n");
3690 return STATUS_INVALID_PARAMETER_5;
3691 }
3692
3693 /* Check for at least one of these Allocation Types to be set */
3694 if (!(AllocationType & (MEM_COMMIT | MEM_RESERVE | MEM_RESET)))
3695 {
3696 DPRINT1("No memory allocation base type\n");
3697 return STATUS_INVALID_PARAMETER_5;
3698 }
3699
3700 /* MEM_RESET is an exclusive flag, make sure that is valid too */
3701 if ((AllocationType & MEM_RESET) && (AllocationType != MEM_RESET))
3702 {
3703 DPRINT1("Invalid use of MEM_RESET\n");
3704 return STATUS_INVALID_PARAMETER_5;
3705 }
3706
3707 /* Check if large pages are being used */
3708 if (AllocationType & MEM_LARGE_PAGES)
3709 {
3710 /* Large page allocations MUST be committed */
3711 if (!(AllocationType & MEM_COMMIT))
3712 {
3713 DPRINT1("Must supply MEM_COMMIT with MEM_LARGE_PAGES\n");
3714 return STATUS_INVALID_PARAMETER_5;
3715 }
3716
3717 /* These flags are not allowed with large page allocations */
3718 if (AllocationType & (MEM_PHYSICAL | MEM_RESET | MEM_WRITE_WATCH))
3719 {
3720 DPRINT1("Using illegal flags with MEM_LARGE_PAGES\n");
3721 return STATUS_INVALID_PARAMETER_5;
3722 }
3723 }
3724
3725 /* MEM_WRITE_WATCH can only be used if MEM_RESERVE is also used */
3726 if ((AllocationType & MEM_WRITE_WATCH) && !(AllocationType & MEM_RESERVE))
3727 {
3728 DPRINT1("MEM_WRITE_WATCH used without MEM_RESERVE\n");
3729 return STATUS_INVALID_PARAMETER_5;
3730 }
3731
3732 /* MEM_PHYSICAL can only be used if MEM_RESERVE is also used */
3733 if ((AllocationType & MEM_PHYSICAL) && !(AllocationType & MEM_RESERVE))
3734 {
3735 DPRINT1("MEM_WRITE_WATCH used without MEM_RESERVE\n");
3736 return STATUS_INVALID_PARAMETER_5;
3737 }
3738
3739 /* Check for valid MEM_PHYSICAL usage */
3740 if (AllocationType & MEM_PHYSICAL)
3741 {
3742 /* Only these flags are allowed with MEM_PHYSIAL */
3743 if (AllocationType & ~(MEM_RESERVE | MEM_TOP_DOWN | MEM_PHYSICAL))
3744 {
3745 DPRINT1("Using illegal flags with MEM_PHYSICAL\n");
3746 return STATUS_INVALID_PARAMETER_5;
3747 }
3748
3749 /* Then make sure PAGE_READWRITE is used */
3750 if (Protect != PAGE_READWRITE)
3751 {
3752 DPRINT1("MEM_PHYSICAL used without PAGE_READWRITE\n");
3753 return STATUS_INVALID_PARAMETER_6;
3754 }
3755 }
3756
3757 /* Calculate the protection mask and make sure it's valid */
3758 ProtectionMask = MiMakeProtectionMask(Protect);
3759 if (ProtectionMask == MM_INVALID_PROTECTION)
3760 {
3761 DPRINT1("Invalid protection mask\n");
3762 return STATUS_INVALID_PAGE_PROTECTION;
3763 }
3764
3765 /* Enter SEH */
3766 _SEH2_TRY
3767 {
3768 /* Check for user-mode parameters */
3769 if (PreviousMode != KernelMode)
3770 {
3771 /* Make sure they are writable */
3772 ProbeForWritePointer(UBaseAddress);
3773 ProbeForWriteUlong(URegionSize);
3774 }
3775
3776 /* Capture their values */
3777 PBaseAddress = *UBaseAddress;
3778 PRegionSize = *URegionSize;
3779 }
3780 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
3781 {
3782 /* Return the exception code */
3783 _SEH2_YIELD(return _SEH2_GetExceptionCode());
3784 }
3785 _SEH2_END;
3786
3787 /* Make sure the allocation isn't past the VAD area */
3788 if (PBaseAddress >= MM_HIGHEST_VAD_ADDRESS)
3789 {
3790 DPRINT1("Virtual allocation base above User Space\n");
3791 return STATUS_INVALID_PARAMETER_2;
3792 }
3793
3794 /* Make sure the allocation wouldn't overflow past the VAD area */
3795 if ((((ULONG_PTR)MM_HIGHEST_VAD_ADDRESS + 1) - (ULONG_PTR)PBaseAddress) < PRegionSize)
3796 {
3797 DPRINT1("Region size would overflow into kernel-memory\n");
3798 return STATUS_INVALID_PARAMETER_4;
3799 }
3800
3801 /* Make sure there's a size specified */
3802 if (!PRegionSize)
3803 {
3804 DPRINT1("Region size is invalid (zero)\n");
3805 return STATUS_INVALID_PARAMETER_4;
3806 }
3807
3808 //
3809 // If this is for the current process, just use PsGetCurrentProcess
3810 //
3811 if (ProcessHandle == NtCurrentProcess())
3812 {
3813 Process = CurrentProcess;
3814 }
3815 else
3816 {
3817 //
3818 // Otherwise, reference the process with VM rights and attach to it if
3819 // this isn't the current process. We must attach because we'll be touching
3820 // PTEs and PDEs that belong to user-mode memory, and also touching the
3821 // Working Set which is stored in Hyperspace.
3822 //
3823 Status = ObReferenceObjectByHandle(ProcessHandle,
3824 PROCESS_VM_OPERATION,
3825 PsProcessType,
3826 PreviousMode,
3827 (PVOID*)&Process,
3828 NULL);
3829 if (!NT_SUCCESS(Status)) return Status;
3830 if (CurrentProcess != Process)
3831 {
3832 KeStackAttachProcess(&Process->Pcb, &ApcState);
3833 Attached = TRUE;
3834 }
3835 }
3836
3837 //
3838 // Check for large page allocations and make sure that the required privilege
3839 // is being held, before attempting to handle them.
3840 //
3841 if ((AllocationType & MEM_LARGE_PAGES) &&
3842 !(SeSinglePrivilegeCheck(SeLockMemoryPrivilege, PreviousMode)))
3843 {
3844 /* Fail without it */
3845 DPRINT1("Privilege not held for MEM_LARGE_PAGES\n");
3846 Status = STATUS_PRIVILEGE_NOT_HELD;
3847 goto FailPathNoLock;
3848 }
3849
3850 //
3851 // Fail on the things we don't yet support
3852 //
3853 if (ZeroBits != 0)
3854 {
3855 DPRINT1("Zero bits not supported\n");
3856 Status = STATUS_INVALID_PARAMETER;
3857 goto FailPathNoLock;
3858 }
3859 if ((AllocationType & MEM_LARGE_PAGES) == MEM_LARGE_PAGES)
3860 {
3861 DPRINT1("MEM_LARGE_PAGES not supported\n");
3862 Status = STATUS_INVALID_PARAMETER;
3863 goto FailPathNoLock;
3864 }
3865 if ((AllocationType & MEM_PHYSICAL) == MEM_PHYSICAL)
3866 {
3867 DPRINT1("MEM_PHYSICAL not supported\n");
3868 Status = STATUS_INVALID_PARAMETER;
3869 goto FailPathNoLock;
3870 }
3871 if ((AllocationType & MEM_WRITE_WATCH) == MEM_WRITE_WATCH)
3872 {
3873 DPRINT1("MEM_WRITE_WATCH not supported\n");
3874 Status = STATUS_INVALID_PARAMETER;
3875 goto FailPathNoLock;
3876 }
3877 if ((AllocationType & MEM_TOP_DOWN) == MEM_TOP_DOWN)
3878 {
3879 DPRINT1("MEM_TOP_DOWN not supported\n");
3880 AllocationType &= ~MEM_TOP_DOWN;
3881 }
3882
3883 if (Process->VmTopDown == 1)
3884 {
3885 DPRINT1("VmTopDown not supported\n");
3886 Status = STATUS_INVALID_PARAMETER;
3887 goto FailPathNoLock;
3888 }
3889
3890 //
3891 // Check if the caller is reserving memory, or committing memory and letting
3892 // us pick the base address
3893 //
3894 if (!(PBaseAddress) || (AllocationType & MEM_RESERVE))
3895 {
3896 //
3897 // Do not allow COPY_ON_WRITE through this API
3898 //
3899 if ((Protect & PAGE_WRITECOPY) || (Protect & PAGE_EXECUTE_WRITECOPY))
3900 {
3901 DPRINT1("Copy on write not allowed through this path\n");
3902 Status = STATUS_INVALID_PAGE_PROTECTION;
3903 goto FailPathNoLock;
3904 }
3905
3906 //
3907 // Does the caller have an address in mind, or is this a blind commit?
3908 //
3909 if (!PBaseAddress)
3910 {
3911 //
3912 // This is a blind commit, all we need is the region size
3913 //
3914 PRegionSize = ROUND_TO_PAGES(PRegionSize);
3915 PageCount = BYTES_TO_PAGES(PRegionSize);
3916 EndingAddress = 0;
3917 StartingAddress = 0;
3918 }
3919 else
3920 {
3921 //
3922 // This is a reservation, so compute the starting address on the
3923 // expected 64KB granularity, and see where the ending address will
3924 // fall based on the aligned address and the passed in region size
3925 //
3926 StartingAddress = ROUND_DOWN((ULONG_PTR)PBaseAddress, _64K);
3927 EndingAddress = ((ULONG_PTR)PBaseAddress + PRegionSize - 1) | (PAGE_SIZE - 1);
3928 PageCount = BYTES_TO_PAGES(EndingAddress - StartingAddress);
3929 }
3930
3931 //
3932 // Allocate and initialize the VAD
3933 //
3934 Vad = ExAllocatePoolWithTag(NonPagedPool, sizeof(MMVAD_LONG), 'SdaV');
3935 ASSERT(Vad != NULL);
3936 Vad->u.LongFlags = 0;
3937 if (AllocationType & MEM_COMMIT) Vad->u.VadFlags.MemCommit = 1;
3938 Vad->u.VadFlags.Protection = ProtectionMask;
3939 Vad->u.VadFlags.PrivateMemory = 1;
3940 Vad->u.VadFlags.CommitCharge = AllocationType & MEM_COMMIT ? PageCount : 0;
3941
3942 //
3943 // Lock the address space and make sure the process isn't already dead
3944 //
3945 AddressSpace = MmGetCurrentAddressSpace();
3946 MmLockAddressSpace(AddressSpace);
3947 if (Process->VmDeleted)
3948 {
3949 Status = STATUS_PROCESS_IS_TERMINATING;
3950 goto FailPath;
3951 }
3952
3953 //
3954 // Did we have a base address? If no, find a valid address that is 64KB
3955 // aligned in the VAD tree. Otherwise, make sure that the address range
3956 // which was passed in isn't already conflicting with an existing address
3957 // range.
3958 //
3959 if (!PBaseAddress)
3960 {
3961 Status = MiFindEmptyAddressRangeInTree(PRegionSize,
3962 _64K,
3963 &Process->VadRoot,
3964 (PMMADDRESS_NODE*)&Process->VadFreeHint,
3965 &StartingAddress);
3966 if (!NT_SUCCESS(Status)) goto FailPath;
3967
3968 //
3969 // Now we know where the allocation ends. Make sure it doesn't end up
3970 // somewhere in kernel mode.
3971 //
3972 EndingAddress = ((ULONG_PTR)StartingAddress + PRegionSize - 1) | (PAGE_SIZE - 1);
3973 if ((PVOID)EndingAddress > MM_HIGHEST_VAD_ADDRESS)
3974 {
3975 Status = STATUS_NO_MEMORY;
3976 goto FailPath;
3977 }
3978 }
3979 else if (MiCheckForConflictingNode(StartingAddress >> PAGE_SHIFT,
3980 EndingAddress >> PAGE_SHIFT,
3981 &Process->VadRoot))
3982 {
3983 //
3984 // The address specified is in conflict!
3985 //
3986 Status = STATUS_CONFLICTING_ADDRESSES;
3987 goto FailPath;
3988 }
3989
3990 //
3991 // Write out the VAD fields for this allocation
3992 //
3993 Vad->StartingVpn = (ULONG_PTR)StartingAddress >> PAGE_SHIFT;
3994 Vad->EndingVpn = (ULONG_PTR)EndingAddress >> PAGE_SHIFT;
3995
3996 //
3997 // FIXME: Should setup VAD bitmap
3998 //
3999 Status = STATUS_SUCCESS;
4000
4001 //
4002 // Lock the working set and insert the VAD into the process VAD tree
4003 //
4004 MiLockProcessWorkingSetUnsafe(Process, CurrentThread);
4005 Vad->ControlArea = NULL; // For Memory-Area hack
4006 MiInsertVad(Vad, Process);
4007 MiUnlockProcessWorkingSetUnsafe(Process, CurrentThread);
4008
4009 //
4010 // Update the virtual size of the process, and if this is now the highest
4011 // virtual size we have ever seen, update the peak virtual size to reflect
4012 // this.
4013 //
4014 Process->VirtualSize += PRegionSize;
4015 if (Process->VirtualSize > Process->PeakVirtualSize)
4016 {
4017 Process->PeakVirtualSize = Process->VirtualSize;
4018 }
4019
4020 //
4021 // Release address space and detach and dereference the target process if
4022 // it was different from the current process
4023 //
4024 MmUnlockAddressSpace(AddressSpace);
4025 if (Attached) KeUnstackDetachProcess(&ApcState);
4026 if (ProcessHandle != NtCurrentProcess()) ObDereferenceObject(Process);
4027
4028 //
4029 // Use SEH to write back the base address and the region size. In the case
4030 // of an exception, we do not return back the exception code, as the memory
4031