[CMAKE]
[reactos.git] / 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
11 #include <ntoskrnl.h>
12 #define NDEBUG
13 #include <debug.h>
14
15 #define MODULE_INVOLVED_IN_ARM3
16 #include "../ARM3/miarm.h"
17
18 #define MI_MAPPED_COPY_PAGES 14
19 #define MI_POOL_COPY_BYTES 512
20 #define MI_MAX_TRANSFER_SIZE 64 * 1024
21
22 NTSTATUS NTAPI
23 MiProtectVirtualMemory(IN PEPROCESS Process,
24 IN OUT PVOID *BaseAddress,
25 IN OUT PSIZE_T NumberOfBytesToProtect,
26 IN ULONG NewAccessProtection,
27 OUT PULONG OldAccessProtection OPTIONAL);
28
29 /* PRIVATE FUNCTIONS **********************************************************/
30
31 ULONG
32 NTAPI
33 MiMakeSystemAddressValid(IN PVOID PageTableVirtualAddress,
34 IN PEPROCESS CurrentProcess)
35 {
36 NTSTATUS Status;
37 BOOLEAN LockChange = FALSE;
38
39 /* Must be a non-pool page table, since those are double-mapped already */
40 ASSERT(PageTableVirtualAddress > MM_HIGHEST_USER_ADDRESS);
41 ASSERT((PageTableVirtualAddress < MmPagedPoolStart) ||
42 (PageTableVirtualAddress > MmPagedPoolEnd));
43
44 /* Working set lock or PFN lock should be held */
45 ASSERT(KeAreAllApcsDisabled() == TRUE);
46
47 /* Check if the page table is valid */
48 while (!MmIsAddressValid(PageTableVirtualAddress))
49 {
50 /* Fault it in */
51 Status = MmAccessFault(FALSE, PageTableVirtualAddress, KernelMode, NULL);
52 if (!NT_SUCCESS(Status))
53 {
54 /* This should not fail */
55 KeBugCheckEx(KERNEL_DATA_INPAGE_ERROR,
56 1,
57 Status,
58 (ULONG_PTR)CurrentProcess,
59 (ULONG_PTR)PageTableVirtualAddress);
60 }
61
62 /* This flag will be useful later when we do better locking */
63 LockChange = TRUE;
64 }
65
66 /* Let caller know what the lock state is */
67 return LockChange;
68 }
69
70 ULONG
71 NTAPI
72 MiMakeSystemAddressValidPfn(IN PVOID VirtualAddress,
73 IN KIRQL OldIrql)
74 {
75 NTSTATUS Status;
76 BOOLEAN LockChange = FALSE;
77
78 /* Must be e kernel address */
79 ASSERT(VirtualAddress > MM_HIGHEST_USER_ADDRESS);
80
81 /* Check if the page is valid */
82 while (!MmIsAddressValid(VirtualAddress))
83 {
84 /* Release the PFN database */
85 KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
86
87 /* Fault it in */
88 Status = MmAccessFault(FALSE, VirtualAddress, KernelMode, NULL);
89 if (!NT_SUCCESS(Status))
90 {
91 /* This should not fail */
92 KeBugCheckEx(KERNEL_DATA_INPAGE_ERROR,
93 3,
94 Status,
95 0,
96 (ULONG_PTR)VirtualAddress);
97 }
98
99 /* This flag will be useful later when we do better locking */
100 LockChange = TRUE;
101
102 /* Lock the PFN database */
103 OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
104 }
105
106 /* Let caller know what the lock state is */
107 return LockChange;
108 }
109
110 PFN_NUMBER
111 NTAPI
112 MiDeleteSystemPageableVm(IN PMMPTE PointerPte,
113 IN PFN_NUMBER PageCount,
114 IN ULONG Flags,
115 OUT PPFN_NUMBER ValidPages)
116 {
117 PFN_NUMBER ActualPages = 0;
118 PETHREAD CurrentThread = PsGetCurrentThread();
119 PMMPFN Pfn1, Pfn2;
120 PFN_NUMBER PageFrameIndex, PageTableIndex;
121 KIRQL OldIrql;
122 ASSERT(KeGetCurrentIrql() <= APC_LEVEL);
123
124 /* Lock the system working set */
125 MiLockWorkingSet(CurrentThread, &MmSystemCacheWs);
126
127 /* Loop all pages */
128 while (PageCount)
129 {
130 /* Make sure there's some data about the page */
131 if (PointerPte->u.Long)
132 {
133 /* As always, only handle current ARM3 scenarios */
134 ASSERT(PointerPte->u.Soft.Prototype == 0);
135 ASSERT(PointerPte->u.Soft.Transition == 0);
136
137 /* Normally this is one possibility -- freeing a valid page */
138 if (PointerPte->u.Hard.Valid)
139 {
140 /* Get the page PFN */
141 PageFrameIndex = PFN_FROM_PTE(PointerPte);
142 Pfn1 = MiGetPfnEntry(PageFrameIndex);
143
144 /* Should not have any working set data yet */
145 ASSERT(Pfn1->u1.WsIndex == 0);
146
147 /* Actual valid, legitimate, pages */
148 if (ValidPages) (*ValidPages)++;
149
150 /* Get the page table entry */
151 PageTableIndex = Pfn1->u4.PteFrame;
152 Pfn2 = MiGetPfnEntry(PageTableIndex);
153
154 /* Lock the PFN database */
155 OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
156
157 /* Delete it the page */
158 MI_SET_PFN_DELETED(Pfn1);
159 MiDecrementShareCount(Pfn1, PageFrameIndex);
160
161 /* Decrement the page table too */
162 DPRINT("FIXME: ARM3 should decrement the pool PDE refcount for: %p\n", PageTableIndex);
163 #if 0 // ARM3: Dont't trust this yet
164 MiDecrementShareCount(Pfn2, PageTableIndex);
165 #endif
166
167 /* Release the PFN database */
168 KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
169
170 /* Destroy the PTE */
171 PointerPte->u.Long = 0;
172 }
173
174 /* Actual legitimate pages */
175 ActualPages++;
176 }
177 else
178 {
179 /*
180 * The only other ARM3 possibility is a demand zero page, which would
181 * mean freeing some of the paged pool pages that haven't even been
182 * touched yet, as part of a larger allocation.
183 *
184 * Right now, we shouldn't expect any page file information in the PTE
185 */
186 ASSERT(PointerPte->u.Soft.PageFileHigh == 0);
187
188 /* Destroy the PTE */
189 PointerPte->u.Long = 0;
190 }
191
192 /* Keep going */
193 PointerPte++;
194 PageCount--;
195 }
196
197 /* Release the working set */
198 MiUnlockWorkingSet(CurrentThread, &MmSystemCacheWs);
199
200 /* Flush the entire TLB */
201 KeFlushEntireTb(TRUE, TRUE);
202
203 /* Done */
204 return ActualPages;
205 }
206
207 VOID
208 NTAPI
209 MiDeletePte(IN PMMPTE PointerPte,
210 IN PVOID VirtualAddress,
211 IN PEPROCESS CurrentProcess,
212 IN PMMPTE PrototypePte)
213 {
214 PMMPFN Pfn1;
215 MMPTE TempPte;
216 PFN_NUMBER PageFrameIndex;
217 PMMPDE PointerPde;
218
219 /* PFN lock must be held */
220 ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
221
222 /* Capture the PTE */
223 TempPte = *PointerPte;
224
225 /* We only support valid PTEs for now */
226 ASSERT(TempPte.u.Hard.Valid == 1);
227 if (TempPte.u.Hard.Valid == 0)
228 {
229 /* Invalid PTEs not supported yet */
230 ASSERT(TempPte.u.Soft.Prototype == 0);
231 ASSERT(TempPte.u.Soft.Transition == 0);
232 }
233
234 /* Get the PFN entry */
235 PageFrameIndex = PFN_FROM_PTE(&TempPte);
236 Pfn1 = MiGetPfnEntry(PageFrameIndex);
237
238 /* Check if this is a valid, prototype PTE */
239 if (Pfn1->u3.e1.PrototypePte == 1)
240 {
241 /* Get the PDE and make sure it's faulted in */
242 PointerPde = MiAddressToPde(PointerPte);
243 if (PointerPde->u.Hard.Valid == 0)
244 {
245 #if (_MI_PAGING_LEVELS == 2)
246 /* Could be paged pool access from a new process -- synchronize the page directories */
247 if (!NT_SUCCESS(MiCheckPdeForPagedPool(VirtualAddress)))
248 {
249 #endif
250 /* The PDE must be valid at this point */
251 KeBugCheckEx(MEMORY_MANAGEMENT,
252 0x61940,
253 (ULONG_PTR)PointerPte,
254 PointerPte->u.Long,
255 (ULONG_PTR)VirtualAddress);
256 }
257 #if (_MI_PAGING_LEVELS == 2)
258 }
259 #endif
260 /* FIXME: Drop the reference on the page table. For now, leak it until RosMM is gone */
261 //MiDecrementShareCount(MiGetPfnEntry(PFN_FROM_PTE(PointerPde)), PFN_FROM_PTE(PointerPde));
262
263 /* Drop the share count */
264 MiDecrementShareCount(Pfn1, PageFrameIndex);
265
266 /* No fork yet */
267 if (PointerPte <= MiHighestUserPte) ASSERT(PrototypePte == Pfn1->PteAddress);
268 }
269 else
270 {
271 /* Make sure the saved PTE address is valid */
272 if ((PMMPTE)((ULONG_PTR)Pfn1->PteAddress & ~0x1) != PointerPte)
273 {
274 /* The PFN entry is illegal, or invalid */
275 KeBugCheckEx(MEMORY_MANAGEMENT,
276 0x401,
277 (ULONG_PTR)PointerPte,
278 PointerPte->u.Long,
279 (ULONG_PTR)Pfn1->PteAddress);
280 }
281
282 /* There should only be 1 shared reference count */
283 ASSERT(Pfn1->u2.ShareCount == 1);
284
285 /* FIXME: Drop the reference on the page table. For now, leak it until RosMM is gone */
286 //MiDecrementShareCount(MiGetPfnEntry(Pfn1->u4.PteFrame), Pfn1->u4.PteFrame);
287
288 /* Mark the PFN for deletion and dereference what should be the last ref */
289 MI_SET_PFN_DELETED(Pfn1);
290 MiDecrementShareCount(Pfn1, PageFrameIndex);
291
292 /* We should eventually do this */
293 //CurrentProcess->NumberOfPrivatePages--;
294 }
295
296 /* Destroy the PTE and flush the TLB */
297 PointerPte->u.Long = 0;
298 KeFlushCurrentTb();
299 }
300
301 VOID
302 NTAPI
303 MiDeleteVirtualAddresses(IN ULONG_PTR Va,
304 IN ULONG_PTR EndingAddress,
305 IN PMMVAD Vad)
306 {
307 PMMPTE PointerPte, PrototypePte, LastPrototypePte;
308 PMMPDE PointerPde;
309 MMPTE TempPte;
310 PEPROCESS CurrentProcess;
311 KIRQL OldIrql;
312 BOOLEAN AddressGap = FALSE;
313 PSUBSECTION Subsection;
314
315 /* Get out if this is a fake VAD, RosMm will free the marea pages */
316 if ((Vad) && (Vad->u.VadFlags.Spare == 1)) return;
317
318 /* Grab the process and PTE/PDE for the address being deleted */
319 CurrentProcess = PsGetCurrentProcess();
320 PointerPde = MiAddressToPde(Va);
321 PointerPte = MiAddressToPte(Va);
322
323 /* Check if this is a section VAD or a VM VAD */
324 if (!(Vad) || (Vad->u.VadFlags.PrivateMemory) || !(Vad->FirstPrototypePte))
325 {
326 /* Don't worry about prototypes */
327 PrototypePte = LastPrototypePte = NULL;
328 }
329 else
330 {
331 /* Get the prototype PTE */
332 PrototypePte = Vad->FirstPrototypePte;
333 LastPrototypePte = Vad->FirstPrototypePte + 1;
334 }
335
336 /* In all cases, we don't support fork() yet */
337 ASSERT(CurrentProcess->CloneRoot == NULL);
338
339 /* Loop the PTE for each VA */
340 while (TRUE)
341 {
342 /* First keep going until we find a valid PDE */
343 while (!PointerPde->u.Long)
344 {
345 /* There are gaps in the address space */
346 AddressGap = TRUE;
347
348 /* Still no valid PDE, try the next 4MB (or whatever) */
349 PointerPde++;
350
351 /* Update the PTE on this new boundary */
352 PointerPte = MiPteToAddress(PointerPde);
353
354 /* Check if all the PDEs are invalid, so there's nothing to free */
355 Va = (ULONG_PTR)MiPteToAddress(PointerPte);
356 if (Va > EndingAddress) return;
357 }
358
359 /* Now check if the PDE is mapped in */
360 if (!PointerPde->u.Hard.Valid)
361 {
362 /* It isn't, so map it in */
363 PointerPte = MiPteToAddress(PointerPde);
364 MiMakeSystemAddressValid(PointerPte, CurrentProcess);
365 }
366
367 /* Now we should have a valid PDE, mapped in, and still have some VA */
368 ASSERT(PointerPde->u.Hard.Valid == 1);
369 ASSERT(Va <= EndingAddress);
370
371 /* Check if this is a section VAD with gaps in it */
372 if ((AddressGap) && (LastPrototypePte))
373 {
374 /* We need to skip to the next correct prototype PTE */
375 PrototypePte = MI_GET_PROTOTYPE_PTE_FOR_VPN(Vad, Va >> PAGE_SHIFT);
376
377 /* And we need the subsection to skip to the next last prototype PTE */
378 Subsection = MiLocateSubsection(Vad, Va >> PAGE_SHIFT);
379 if (Subsection)
380 {
381 /* Found it! */
382 LastPrototypePte = &Subsection->SubsectionBase[Subsection->PtesInSubsection];
383 }
384 else
385 {
386 /* No more subsections, we are done with prototype PTEs */
387 PrototypePte = NULL;
388 }
389 }
390
391 /* Lock the PFN Database while we delete the PTEs */
392 OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
393 do
394 {
395 /* Capture the PDE and make sure it exists */
396 TempPte = *PointerPte;
397 if (TempPte.u.Long)
398 {
399 /* Check if the PTE is actually mapped in */
400 if (TempPte.u.Long & 0xFFFFFC01)
401 {
402 /* Are we dealing with section VAD? */
403 if ((LastPrototypePte) && (PrototypePte > LastPrototypePte))
404 {
405 /* We need to skip to the next correct prototype PTE */
406 PrototypePte = MI_GET_PROTOTYPE_PTE_FOR_VPN(Vad, Va >> PAGE_SHIFT);
407
408 /* And we need the subsection to skip to the next last prototype PTE */
409 Subsection = MiLocateSubsection(Vad, Va >> PAGE_SHIFT);
410 if (Subsection)
411 {
412 /* Found it! */
413 LastPrototypePte = &Subsection->SubsectionBase[Subsection->PtesInSubsection];
414 }
415 else
416 {
417 /* No more subsections, we are done with prototype PTEs */
418 PrototypePte = NULL;
419 }
420 }
421
422 /* Check for prototype PTE */
423 if ((TempPte.u.Hard.Valid == 0) &&
424 (TempPte.u.Soft.Prototype == 1))
425 {
426 /* Just nuke it */
427 PointerPte->u.Long = 0;
428 }
429 else
430 {
431 /* Delete the PTE proper */
432 MiDeletePte(PointerPte,
433 (PVOID)Va,
434 CurrentProcess,
435 PrototypePte);
436 }
437 }
438 else
439 {
440 /* The PTE was never mapped, just nuke it here */
441 PointerPte->u.Long = 0;
442 }
443 }
444
445 /* Update the address and PTE for it */
446 Va += PAGE_SIZE;
447 PointerPte++;
448 PrototypePte++;
449
450 /* Making sure the PDE is still valid */
451 ASSERT(PointerPde->u.Hard.Valid == 1);
452 }
453 while ((Va & (PDE_MAPPED_VA - 1)) && (Va <= EndingAddress));
454
455 /* The PDE should still be valid at this point */
456 ASSERT(PointerPde->u.Hard.Valid == 1);
457
458 /* Release the lock and get out if we're done */
459 KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
460 if (Va > EndingAddress) return;
461
462 /* Otherwise, we exited because we hit a new PDE boundary, so start over */
463 PointerPde = MiAddressToPde(Va);
464 AddressGap = FALSE;
465 }
466 }
467
468 LONG
469 MiGetExceptionInfo(IN PEXCEPTION_POINTERS ExceptionInfo,
470 OUT PBOOLEAN HaveBadAddress,
471 OUT PULONG_PTR BadAddress)
472 {
473 PEXCEPTION_RECORD ExceptionRecord;
474 PAGED_CODE();
475
476 //
477 // Assume default
478 //
479 *HaveBadAddress = FALSE;
480
481 //
482 // Get the exception record
483 //
484 ExceptionRecord = ExceptionInfo->ExceptionRecord;
485
486 //
487 // Look at the exception code
488 //
489 if ((ExceptionRecord->ExceptionCode == STATUS_ACCESS_VIOLATION) ||
490 (ExceptionRecord->ExceptionCode == STATUS_GUARD_PAGE_VIOLATION) ||
491 (ExceptionRecord->ExceptionCode == STATUS_IN_PAGE_ERROR))
492 {
493 //
494 // We can tell the address if we have more than one parameter
495 //
496 if (ExceptionRecord->NumberParameters > 1)
497 {
498 //
499 // Return the address
500 //
501 *HaveBadAddress = TRUE;
502 *BadAddress = ExceptionRecord->ExceptionInformation[1];
503 }
504 }
505
506 //
507 // Continue executing the next handler
508 //
509 return EXCEPTION_EXECUTE_HANDLER;
510 }
511
512 NTSTATUS
513 NTAPI
514 MiDoMappedCopy(IN PEPROCESS SourceProcess,
515 IN PVOID SourceAddress,
516 IN PEPROCESS TargetProcess,
517 OUT PVOID TargetAddress,
518 IN SIZE_T BufferSize,
519 IN KPROCESSOR_MODE PreviousMode,
520 OUT PSIZE_T ReturnSize)
521 {
522 PFN_NUMBER MdlBuffer[(sizeof(MDL) / sizeof(PFN_NUMBER)) + MI_MAPPED_COPY_PAGES + 1];
523 PMDL Mdl = (PMDL)MdlBuffer;
524 SIZE_T TotalSize, CurrentSize, RemainingSize;
525 volatile BOOLEAN FailedInProbe = FALSE, FailedInMapping = FALSE, FailedInMoving;
526 volatile BOOLEAN PagesLocked;
527 PVOID CurrentAddress = SourceAddress, CurrentTargetAddress = TargetAddress;
528 volatile PVOID MdlAddress;
529 KAPC_STATE ApcState;
530 BOOLEAN HaveBadAddress;
531 ULONG_PTR BadAddress;
532 NTSTATUS Status = STATUS_SUCCESS;
533 PAGED_CODE();
534
535 //
536 // Calculate the maximum amount of data to move
537 //
538 TotalSize = MI_MAPPED_COPY_PAGES * PAGE_SIZE;
539 if (BufferSize <= TotalSize) TotalSize = BufferSize;
540 CurrentSize = TotalSize;
541 RemainingSize = BufferSize;
542
543 //
544 // Loop as long as there is still data
545 //
546 while (RemainingSize > 0)
547 {
548 //
549 // Check if this transfer will finish everything off
550 //
551 if (RemainingSize < CurrentSize) CurrentSize = RemainingSize;
552
553 //
554 // Attach to the source address space
555 //
556 KeStackAttachProcess(&SourceProcess->Pcb, &ApcState);
557
558 //
559 // Reset state for this pass
560 //
561 MdlAddress = NULL;
562 PagesLocked = FALSE;
563 FailedInMoving = FALSE;
564 ASSERT(FailedInProbe == FALSE);
565
566 //
567 // Protect user-mode copy
568 //
569 _SEH2_TRY
570 {
571 //
572 // If this is our first time, probe the buffer
573 //
574 if ((CurrentAddress == SourceAddress) && (PreviousMode != KernelMode))
575 {
576 //
577 // Catch a failure here
578 //
579 FailedInProbe = TRUE;
580
581 //
582 // Do the probe
583 //
584 ProbeForRead(SourceAddress, BufferSize, sizeof(CHAR));
585
586 //
587 // Passed
588 //
589 FailedInProbe = FALSE;
590 }
591
592 //
593 // Initialize and probe and lock the MDL
594 //
595 MmInitializeMdl(Mdl, CurrentAddress, CurrentSize);
596 MmProbeAndLockPages(Mdl, PreviousMode, IoReadAccess);
597 PagesLocked = TRUE;
598
599 //
600 // Now map the pages
601 //
602 MdlAddress = MmMapLockedPagesSpecifyCache(Mdl,
603 KernelMode,
604 MmCached,
605 NULL,
606 FALSE,
607 HighPagePriority);
608 if (!MdlAddress)
609 {
610 //
611 // Use our SEH handler to pick this up
612 //
613 FailedInMapping = TRUE;
614 ExRaiseStatus(STATUS_INSUFFICIENT_RESOURCES);
615 }
616
617 //
618 // Now let go of the source and grab to the target process
619 //
620 KeUnstackDetachProcess(&ApcState);
621 KeStackAttachProcess(&TargetProcess->Pcb, &ApcState);
622
623 //
624 // Check if this is our first time through
625 //
626 if ((CurrentAddress == SourceAddress) && (PreviousMode != KernelMode))
627 {
628 //
629 // Catch a failure here
630 //
631 FailedInProbe = TRUE;
632
633 //
634 // Do the probe
635 //
636 ProbeForWrite(TargetAddress, BufferSize, sizeof(CHAR));
637
638 //
639 // Passed
640 //
641 FailedInProbe = FALSE;
642 }
643
644 //
645 // Now do the actual move
646 //
647 FailedInMoving = TRUE;
648 RtlCopyMemory(CurrentTargetAddress, MdlAddress, CurrentSize);
649 }
650 _SEH2_EXCEPT(MiGetExceptionInfo(_SEH2_GetExceptionInformation(),
651 &HaveBadAddress,
652 &BadAddress))
653 {
654 //
655 // Detach from whoever we may be attached to
656 //
657 KeUnstackDetachProcess(&ApcState);
658
659 //
660 // Check if we had mapped the pages
661 //
662 if (MdlAddress) MmUnmapLockedPages(MdlAddress, Mdl);
663
664 //
665 // Check if we had locked the pages
666 //
667 if (PagesLocked) MmUnlockPages(Mdl);
668
669 //
670 // Check if we hit working set quota
671 //
672 if (_SEH2_GetExceptionCode() == STATUS_WORKING_SET_QUOTA)
673 {
674 //
675 // Return the error
676 //
677 return STATUS_WORKING_SET_QUOTA;
678 }
679
680 //
681 // Check if we failed during the probe or mapping
682 //
683 if ((FailedInProbe) || (FailedInMapping))
684 {
685 //
686 // Exit
687 //
688 Status = _SEH2_GetExceptionCode();
689 _SEH2_YIELD(return Status);
690 }
691
692 //
693 // Otherwise, we failed probably during the move
694 //
695 *ReturnSize = BufferSize - RemainingSize;
696 if (FailedInMoving)
697 {
698 //
699 // Check if we know exactly where we stopped copying
700 //
701 if (HaveBadAddress)
702 {
703 //
704 // Return the exact number of bytes copied
705 //
706 *ReturnSize = BadAddress - (ULONG_PTR)SourceAddress;
707 }
708 }
709
710 //
711 // Return partial copy
712 //
713 Status = STATUS_PARTIAL_COPY;
714 }
715 _SEH2_END;
716
717 //
718 // Check for SEH status
719 //
720 if (Status != STATUS_SUCCESS) return Status;
721
722 //
723 // Detach from target
724 //
725 KeUnstackDetachProcess(&ApcState);
726
727 //
728 // Unmap and unlock
729 //
730 MmUnmapLockedPages(MdlAddress, Mdl);
731 MmUnlockPages(Mdl);
732
733 //
734 // Update location and size
735 //
736 RemainingSize -= CurrentSize;
737 CurrentAddress = (PVOID)((ULONG_PTR)CurrentAddress + CurrentSize);
738 CurrentTargetAddress = (PVOID)((ULONG_PTR)CurrentTargetAddress + CurrentSize);
739 }
740
741 //
742 // All bytes read
743 //
744 *ReturnSize = BufferSize;
745 return STATUS_SUCCESS;
746 }
747
748 NTSTATUS
749 NTAPI
750 MiDoPoolCopy(IN PEPROCESS SourceProcess,
751 IN PVOID SourceAddress,
752 IN PEPROCESS TargetProcess,
753 OUT PVOID TargetAddress,
754 IN SIZE_T BufferSize,
755 IN KPROCESSOR_MODE PreviousMode,
756 OUT PSIZE_T ReturnSize)
757 {
758 UCHAR StackBuffer[MI_POOL_COPY_BYTES];
759 SIZE_T TotalSize, CurrentSize, RemainingSize;
760 volatile BOOLEAN FailedInProbe = FALSE, FailedInMoving, HavePoolAddress = FALSE;
761 PVOID CurrentAddress = SourceAddress, CurrentTargetAddress = TargetAddress;
762 PVOID PoolAddress;
763 KAPC_STATE ApcState;
764 BOOLEAN HaveBadAddress;
765 ULONG_PTR BadAddress;
766 NTSTATUS Status = STATUS_SUCCESS;
767 PAGED_CODE();
768
769 //
770 // Calculate the maximum amount of data to move
771 //
772 TotalSize = MI_MAX_TRANSFER_SIZE;
773 if (BufferSize <= MI_MAX_TRANSFER_SIZE) TotalSize = BufferSize;
774 CurrentSize = TotalSize;
775 RemainingSize = BufferSize;
776
777 //
778 // Check if we can use the stack
779 //
780 if (BufferSize <= MI_POOL_COPY_BYTES)
781 {
782 //
783 // Use it
784 //
785 PoolAddress = (PVOID)StackBuffer;
786 }
787 else
788 {
789 //
790 // Allocate pool
791 //
792 PoolAddress = ExAllocatePoolWithTag(NonPagedPool, TotalSize, 'VmRw');
793 if (!PoolAddress) ASSERT(FALSE);
794 HavePoolAddress = TRUE;
795 }
796
797 //
798 // Loop as long as there is still data
799 //
800 while (RemainingSize > 0)
801 {
802 //
803 // Check if this transfer will finish everything off
804 //
805 if (RemainingSize < CurrentSize) CurrentSize = RemainingSize;
806
807 //
808 // Attach to the source address space
809 //
810 KeStackAttachProcess(&SourceProcess->Pcb, &ApcState);
811
812 //
813 // Reset state for this pass
814 //
815 FailedInMoving = FALSE;
816 ASSERT(FailedInProbe == FALSE);
817
818 //
819 // Protect user-mode copy
820 //
821 _SEH2_TRY
822 {
823 //
824 // If this is our first time, probe the buffer
825 //
826 if ((CurrentAddress == SourceAddress) && (PreviousMode != KernelMode))
827 {
828 //
829 // Catch a failure here
830 //
831 FailedInProbe = TRUE;
832
833 //
834 // Do the probe
835 //
836 ProbeForRead(SourceAddress, BufferSize, sizeof(CHAR));
837
838 //
839 // Passed
840 //
841 FailedInProbe = FALSE;
842 }
843
844 //
845 // Do the copy
846 //
847 RtlCopyMemory(PoolAddress, CurrentAddress, CurrentSize);
848
849 //
850 // Now let go of the source and grab to the target process
851 //
852 KeUnstackDetachProcess(&ApcState);
853 KeStackAttachProcess(&TargetProcess->Pcb, &ApcState);
854
855 //
856 // Check if this is our first time through
857 //
858 if ((CurrentAddress == SourceAddress) && (PreviousMode != KernelMode))
859 {
860 //
861 // Catch a failure here
862 //
863 FailedInProbe = TRUE;
864
865 //
866 // Do the probe
867 //
868 ProbeForWrite(TargetAddress, BufferSize, sizeof(CHAR));
869
870 //
871 // Passed
872 //
873 FailedInProbe = FALSE;
874 }
875
876 //
877 // Now do the actual move
878 //
879 FailedInMoving = TRUE;
880 RtlCopyMemory(CurrentTargetAddress, PoolAddress, CurrentSize);
881 }
882 _SEH2_EXCEPT(MiGetExceptionInfo(_SEH2_GetExceptionInformation(),
883 &HaveBadAddress,
884 &BadAddress))
885 {
886 //
887 // Detach from whoever we may be attached to
888 //
889 KeUnstackDetachProcess(&ApcState);
890
891 //
892 // Check if we had allocated pool
893 //
894 if (HavePoolAddress) ExFreePool(PoolAddress);
895
896 //
897 // Check if we failed during the probe
898 //
899 if (FailedInProbe)
900 {
901 //
902 // Exit
903 //
904 Status = _SEH2_GetExceptionCode();
905 _SEH2_YIELD(return Status);
906 }
907
908 //
909 // Otherwise, we failed, probably during the move
910 //
911 *ReturnSize = BufferSize - RemainingSize;
912 if (FailedInMoving)
913 {
914 //
915 // Check if we know exactly where we stopped copying
916 //
917 if (HaveBadAddress)
918 {
919 //
920 // Return the exact number of bytes copied
921 //
922 *ReturnSize = BadAddress - (ULONG_PTR)SourceAddress;
923 }
924 }
925
926 //
927 // Return partial copy
928 //
929 Status = STATUS_PARTIAL_COPY;
930 }
931 _SEH2_END;
932
933 //
934 // Check for SEH status
935 //
936 if (Status != STATUS_SUCCESS) return Status;
937
938 //
939 // Detach from target
940 //
941 KeUnstackDetachProcess(&ApcState);
942
943 //
944 // Update location and size
945 //
946 RemainingSize -= CurrentSize;
947 CurrentAddress = (PVOID)((ULONG_PTR)CurrentAddress + CurrentSize);
948 CurrentTargetAddress = (PVOID)((ULONG_PTR)CurrentTargetAddress +
949 CurrentSize);
950 }
951
952 //
953 // Check if we had allocated pool
954 //
955 if (HavePoolAddress) ExFreePool(PoolAddress);
956
957 //
958 // All bytes read
959 //
960 *ReturnSize = BufferSize;
961 return STATUS_SUCCESS;
962 }
963
964 NTSTATUS
965 NTAPI
966 MmCopyVirtualMemory(IN PEPROCESS SourceProcess,
967 IN PVOID SourceAddress,
968 IN PEPROCESS TargetProcess,
969 OUT PVOID TargetAddress,
970 IN SIZE_T BufferSize,
971 IN KPROCESSOR_MODE PreviousMode,
972 OUT PSIZE_T ReturnSize)
973 {
974 NTSTATUS Status;
975 PEPROCESS Process = SourceProcess;
976
977 //
978 // Don't accept zero-sized buffers
979 //
980 if (!BufferSize) return STATUS_SUCCESS;
981
982 //
983 // If we are copying from ourselves, lock the target instead
984 //
985 if (SourceProcess == PsGetCurrentProcess()) Process = TargetProcess;
986
987 //
988 // Acquire rundown protection
989 //
990 if (!ExAcquireRundownProtection(&Process->RundownProtect))
991 {
992 //
993 // Fail
994 //
995 return STATUS_PROCESS_IS_TERMINATING;
996 }
997
998 //
999 // See if we should use the pool copy
1000 //
1001 if (BufferSize > MI_POOL_COPY_BYTES)
1002 {
1003 //
1004 // Use MDL-copy
1005 //
1006 Status = MiDoMappedCopy(SourceProcess,
1007 SourceAddress,
1008 TargetProcess,
1009 TargetAddress,
1010 BufferSize,
1011 PreviousMode,
1012 ReturnSize);
1013 }
1014 else
1015 {
1016 //
1017 // Do pool copy
1018 //
1019 Status = MiDoPoolCopy(SourceProcess,
1020 SourceAddress,
1021 TargetProcess,
1022 TargetAddress,
1023 BufferSize,
1024 PreviousMode,
1025 ReturnSize);
1026 }
1027
1028 //
1029 // Release the lock
1030 //
1031 ExReleaseRundownProtection(&Process->RundownProtect);
1032 return Status;
1033 }
1034
1035 NTSTATUS
1036 NTAPI
1037 MmFlushVirtualMemory(IN PEPROCESS Process,
1038 IN OUT PVOID *BaseAddress,
1039 IN OUT PSIZE_T RegionSize,
1040 OUT PIO_STATUS_BLOCK IoStatusBlock)
1041 {
1042 PAGED_CODE();
1043 UNIMPLEMENTED;
1044
1045 //
1046 // Fake success
1047 //
1048 return STATUS_SUCCESS;
1049 }
1050
1051 ULONG
1052 NTAPI
1053 MiGetPageProtection(IN PMMPTE PointerPte)
1054 {
1055 MMPTE TempPte;
1056 PMMPFN Pfn;
1057 PAGED_CODE();
1058
1059 /* Copy this PTE's contents */
1060 TempPte = *PointerPte;
1061
1062 /* Assure it's not totally zero */
1063 ASSERT(TempPte.u.Long);
1064
1065 /* Check for a special prototype format */
1066 if (TempPte.u.Soft.Valid == 0 &&
1067 TempPte.u.Soft.Prototype == 1)
1068 {
1069 /* Unsupported now */
1070 UNIMPLEMENTED;
1071 ASSERT(FALSE);
1072 }
1073
1074 /* In the easy case of transition or demand zero PTE just return its protection */
1075 if (!TempPte.u.Hard.Valid) return MmProtectToValue[TempPte.u.Soft.Protection];
1076
1077 /* If we get here, the PTE is valid, so look up the page in PFN database */
1078 Pfn = MiGetPfnEntry(TempPte.u.Hard.PageFrameNumber);
1079
1080 if (!Pfn->u3.e1.PrototypePte)
1081 {
1082 /* Return protection of the original pte */
1083 return MmProtectToValue[Pfn->OriginalPte.u.Soft.Protection];
1084 }
1085
1086 /* This is hardware PTE */
1087 UNIMPLEMENTED;
1088 ASSERT(FALSE);
1089
1090 return PAGE_NOACCESS;
1091 }
1092
1093 ULONG
1094 NTAPI
1095 MiQueryAddressState(IN PVOID Va,
1096 IN PMMVAD Vad,
1097 IN PEPROCESS TargetProcess,
1098 OUT PULONG ReturnedProtect,
1099 OUT PVOID *NextVa)
1100 {
1101
1102 PMMPTE PointerPte;
1103 PMMPDE PointerPde;
1104 MMPTE TempPte;
1105 BOOLEAN DemandZeroPte = TRUE, ValidPte = FALSE;
1106 ULONG State = MEM_RESERVE, Protect = 0, LockChange;
1107 ASSERT((Vad->StartingVpn <= ((ULONG_PTR)Va >> PAGE_SHIFT)) &&
1108 (Vad->EndingVpn >= ((ULONG_PTR)Va >> PAGE_SHIFT)));
1109
1110 /* Only normal VADs supported */
1111 ASSERT(Vad->u.VadFlags.VadType == VadNone);
1112
1113 /* Get the PDE and PTE for the address */
1114 PointerPde = MiAddressToPde(Va);
1115 PointerPte = MiAddressToPte(Va);
1116
1117 /* Return the next range */
1118 *NextVa = (PVOID)((ULONG_PTR)Va + PAGE_SIZE);
1119
1120 /* Loop to make sure the PDE is valid */
1121 do
1122 {
1123 /* Try again */
1124 LockChange = 0;
1125
1126 /* Is the PDE empty? */
1127 if (!PointerPde->u.Long)
1128 {
1129 /* No address in this range used yet, move to the next PDE range */
1130 *NextVa = MiPdeToAddress(PointerPde + 1);
1131 break;
1132 }
1133
1134 /* The PDE is not empty, but is it faulted in? */
1135 if (!PointerPde->u.Hard.Valid)
1136 {
1137 /* It isn't, go ahead and do the fault */
1138 LockChange = MiMakeSystemAddressValid(MiPdeToPte(PointerPde),
1139 TargetProcess);
1140 }
1141
1142 /* Check if the PDE was faulted in, making the PTE readable */
1143 if (!LockChange) ValidPte = TRUE;
1144 } while (LockChange);
1145
1146 /* Is it safe to try reading the PTE? */
1147 if (ValidPte)
1148 {
1149 /* FIXME: watch out for large pages */
1150
1151 /* Capture the PTE */
1152 TempPte = *PointerPte;
1153 if (TempPte.u.Long)
1154 {
1155 /* The PTE is valid, so it's not zeroed out */
1156 DemandZeroPte = FALSE;
1157
1158 /* Check if it's valid or has a valid protection mask */
1159 ASSERT(TempPte.u.Soft.Prototype == 0);
1160 if ((TempPte.u.Soft.Protection != MM_DECOMMIT) ||
1161 (TempPte.u.Hard.Valid == 1))
1162 {
1163 /* This means it's committed */
1164 State = MEM_COMMIT;
1165
1166 /* Get protection state of this page */
1167 Protect = MiGetPageProtection(PointerPte);
1168 }
1169 else
1170 {
1171 /* Otherwise our defaults should hold */
1172 ASSERT(Protect == 0);
1173 ASSERT(State == MEM_RESERVE);
1174 }
1175 }
1176 }
1177
1178 /* Check if this was a demand-zero PTE, since we need to find the state */
1179 if (DemandZeroPte)
1180 {
1181 /* Check if the VAD is for committed memory */
1182 if (Vad->u.VadFlags.MemCommit)
1183 {
1184 /* This is committed memory */
1185 State = MEM_COMMIT;
1186
1187 /* Convert the protection */
1188 Protect = MmProtectToValue[Vad->u.VadFlags.Protection];
1189 }
1190 }
1191
1192 /* Return the protection code */
1193 *ReturnedProtect = Protect;
1194 return State;
1195 }
1196
1197 /* PUBLIC FUNCTIONS ***********************************************************/
1198
1199 /*
1200 * @unimplemented
1201 */
1202 PVOID
1203 NTAPI
1204 MmGetVirtualForPhysical(IN PHYSICAL_ADDRESS PhysicalAddress)
1205 {
1206 UNIMPLEMENTED;
1207 return 0;
1208 }
1209
1210 /*
1211 * @unimplemented
1212 */
1213 PVOID
1214 NTAPI
1215 MmSecureVirtualMemory(IN PVOID Address,
1216 IN SIZE_T Length,
1217 IN ULONG Mode)
1218 {
1219 static BOOLEAN Warn; if (!Warn++) UNIMPLEMENTED;
1220 return Address;
1221 }
1222
1223 /*
1224 * @unimplemented
1225 */
1226 VOID
1227 NTAPI
1228 MmUnsecureVirtualMemory(IN PVOID SecureMem)
1229 {
1230 static BOOLEAN Warn; if (!Warn++) UNIMPLEMENTED;
1231 }
1232
1233 /* SYSTEM CALLS ***************************************************************/
1234
1235 NTSTATUS
1236 NTAPI
1237 NtReadVirtualMemory(IN HANDLE ProcessHandle,
1238 IN PVOID BaseAddress,
1239 OUT PVOID Buffer,
1240 IN SIZE_T NumberOfBytesToRead,
1241 OUT PSIZE_T NumberOfBytesRead OPTIONAL)
1242 {
1243 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
1244 PEPROCESS Process;
1245 NTSTATUS Status = STATUS_SUCCESS;
1246 SIZE_T BytesRead = 0;
1247 PAGED_CODE();
1248
1249 //
1250 // Check if we came from user mode
1251 //
1252 if (PreviousMode != KernelMode)
1253 {
1254 //
1255 // Validate the read addresses
1256 //
1257 if ((((ULONG_PTR)BaseAddress + NumberOfBytesToRead) < (ULONG_PTR)BaseAddress) ||
1258 (((ULONG_PTR)Buffer + NumberOfBytesToRead) < (ULONG_PTR)Buffer) ||
1259 (((ULONG_PTR)BaseAddress + NumberOfBytesToRead) > MmUserProbeAddress) ||
1260 (((ULONG_PTR)Buffer + NumberOfBytesToRead) > MmUserProbeAddress))
1261 {
1262 //
1263 // Don't allow to write into kernel space
1264 //
1265 return STATUS_ACCESS_VIOLATION;
1266 }
1267
1268 //
1269 // Enter SEH for probe
1270 //
1271 _SEH2_TRY
1272 {
1273 //
1274 // Probe the output value
1275 //
1276 if (NumberOfBytesRead) ProbeForWriteSize_t(NumberOfBytesRead);
1277 }
1278 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1279 {
1280 //
1281 // Get exception code
1282 //
1283 _SEH2_YIELD(return _SEH2_GetExceptionCode());
1284 }
1285 _SEH2_END;
1286 }
1287
1288 //
1289 // Don't do zero-byte transfers
1290 //
1291 if (NumberOfBytesToRead)
1292 {
1293 //
1294 // Reference the process
1295 //
1296 Status = ObReferenceObjectByHandle(ProcessHandle,
1297 PROCESS_VM_READ,
1298 PsProcessType,
1299 PreviousMode,
1300 (PVOID*)(&Process),
1301 NULL);
1302 if (NT_SUCCESS(Status))
1303 {
1304 //
1305 // Do the copy
1306 //
1307 Status = MmCopyVirtualMemory(Process,
1308 BaseAddress,
1309 PsGetCurrentProcess(),
1310 Buffer,
1311 NumberOfBytesToRead,
1312 PreviousMode,
1313 &BytesRead);
1314
1315 //
1316 // Dereference the process
1317 //
1318 ObDereferenceObject(Process);
1319 }
1320 }
1321
1322 //
1323 // Check if the caller sent this parameter
1324 //
1325 if (NumberOfBytesRead)
1326 {
1327 //
1328 // Enter SEH to guard write
1329 //
1330 _SEH2_TRY
1331 {
1332 //
1333 // Return the number of bytes read
1334 //
1335 *NumberOfBytesRead = BytesRead;
1336 }
1337 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1338 {
1339 }
1340 _SEH2_END;
1341 }
1342
1343 //
1344 // Return status
1345 //
1346 return Status;
1347 }
1348
1349 NTSTATUS
1350 NTAPI
1351 NtWriteVirtualMemory(IN HANDLE ProcessHandle,
1352 IN PVOID BaseAddress,
1353 IN PVOID Buffer,
1354 IN SIZE_T NumberOfBytesToWrite,
1355 OUT PSIZE_T NumberOfBytesWritten OPTIONAL)
1356 {
1357 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
1358 PEPROCESS Process;
1359 NTSTATUS Status = STATUS_SUCCESS;
1360 SIZE_T BytesWritten = 0;
1361 PAGED_CODE();
1362
1363 //
1364 // Check if we came from user mode
1365 //
1366 if (PreviousMode != KernelMode)
1367 {
1368 //
1369 // Validate the read addresses
1370 //
1371 if ((((ULONG_PTR)BaseAddress + NumberOfBytesToWrite) < (ULONG_PTR)BaseAddress) ||
1372 (((ULONG_PTR)Buffer + NumberOfBytesToWrite) < (ULONG_PTR)Buffer) ||
1373 (((ULONG_PTR)BaseAddress + NumberOfBytesToWrite) > MmUserProbeAddress) ||
1374 (((ULONG_PTR)Buffer + NumberOfBytesToWrite) > MmUserProbeAddress))
1375 {
1376 //
1377 // Don't allow to write into kernel space
1378 //
1379 return STATUS_ACCESS_VIOLATION;
1380 }
1381
1382 //
1383 // Enter SEH for probe
1384 //
1385 _SEH2_TRY
1386 {
1387 //
1388 // Probe the output value
1389 //
1390 if (NumberOfBytesWritten) ProbeForWriteSize_t(NumberOfBytesWritten);
1391 }
1392 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1393 {
1394 //
1395 // Get exception code
1396 //
1397 _SEH2_YIELD(return _SEH2_GetExceptionCode());
1398 }
1399 _SEH2_END;
1400 }
1401
1402 //
1403 // Don't do zero-byte transfers
1404 //
1405 if (NumberOfBytesToWrite)
1406 {
1407 //
1408 // Reference the process
1409 //
1410 Status = ObReferenceObjectByHandle(ProcessHandle,
1411 PROCESS_VM_WRITE,
1412 PsProcessType,
1413 PreviousMode,
1414 (PVOID*)&Process,
1415 NULL);
1416 if (NT_SUCCESS(Status))
1417 {
1418 //
1419 // Do the copy
1420 //
1421 Status = MmCopyVirtualMemory(PsGetCurrentProcess(),
1422 Buffer,
1423 Process,
1424 BaseAddress,
1425 NumberOfBytesToWrite,
1426 PreviousMode,
1427 &BytesWritten);
1428
1429 //
1430 // Dereference the process
1431 //
1432 ObDereferenceObject(Process);
1433 }
1434 }
1435
1436 //
1437 // Check if the caller sent this parameter
1438 //
1439 if (NumberOfBytesWritten)
1440 {
1441 //
1442 // Enter SEH to guard write
1443 //
1444 _SEH2_TRY
1445 {
1446 //
1447 // Return the number of bytes written
1448 //
1449 *NumberOfBytesWritten = BytesWritten;
1450 }
1451 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1452 {
1453 }
1454 _SEH2_END;
1455 }
1456
1457 //
1458 // Return status
1459 //
1460 return Status;
1461 }
1462
1463 NTSTATUS
1464 NTAPI
1465 NtProtectVirtualMemory(IN HANDLE ProcessHandle,
1466 IN OUT PVOID *UnsafeBaseAddress,
1467 IN OUT SIZE_T *UnsafeNumberOfBytesToProtect,
1468 IN ULONG NewAccessProtection,
1469 OUT PULONG UnsafeOldAccessProtection)
1470 {
1471 PEPROCESS Process;
1472 ULONG OldAccessProtection;
1473 ULONG Protection;
1474 PEPROCESS CurrentProcess = PsGetCurrentProcess();
1475 PVOID BaseAddress = NULL;
1476 SIZE_T NumberOfBytesToProtect = 0;
1477 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
1478 NTSTATUS Status;
1479 BOOLEAN Attached = FALSE;
1480 KAPC_STATE ApcState;
1481 PAGED_CODE();
1482
1483 //
1484 // Check for valid protection flags
1485 //
1486 Protection = NewAccessProtection & ~(PAGE_GUARD|PAGE_NOCACHE);
1487 if (Protection != PAGE_NOACCESS &&
1488 Protection != PAGE_READONLY &&
1489 Protection != PAGE_READWRITE &&
1490 Protection != PAGE_WRITECOPY &&
1491 Protection != PAGE_EXECUTE &&
1492 Protection != PAGE_EXECUTE_READ &&
1493 Protection != PAGE_EXECUTE_READWRITE &&
1494 Protection != PAGE_EXECUTE_WRITECOPY)
1495 {
1496 //
1497 // Fail
1498 //
1499 return STATUS_INVALID_PAGE_PROTECTION;
1500 }
1501
1502 //
1503 // Check if we came from user mode
1504 //
1505 if (PreviousMode != KernelMode)
1506 {
1507 //
1508 // Enter SEH for probing
1509 //
1510 _SEH2_TRY
1511 {
1512 //
1513 // Validate all outputs
1514 //
1515 ProbeForWritePointer(UnsafeBaseAddress);
1516 ProbeForWriteSize_t(UnsafeNumberOfBytesToProtect);
1517 ProbeForWriteUlong(UnsafeOldAccessProtection);
1518
1519 //
1520 // Capture them
1521 //
1522 BaseAddress = *UnsafeBaseAddress;
1523 NumberOfBytesToProtect = *UnsafeNumberOfBytesToProtect;
1524 }
1525 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1526 {
1527 //
1528 // Get exception code
1529 //
1530 _SEH2_YIELD(return _SEH2_GetExceptionCode());
1531 }
1532 _SEH2_END;
1533 }
1534 else
1535 {
1536 //
1537 // Capture directly
1538 //
1539 BaseAddress = *UnsafeBaseAddress;
1540 NumberOfBytesToProtect = *UnsafeNumberOfBytesToProtect;
1541 }
1542
1543 //
1544 // Catch illegal base address
1545 //
1546 if (BaseAddress > MM_HIGHEST_USER_ADDRESS) return STATUS_INVALID_PARAMETER_2;
1547
1548 //
1549 // Catch illegal region size
1550 //
1551 if ((MmUserProbeAddress - (ULONG_PTR)BaseAddress) < NumberOfBytesToProtect)
1552 {
1553 //
1554 // Fail
1555 //
1556 return STATUS_INVALID_PARAMETER_3;
1557 }
1558
1559 //
1560 // 0 is also illegal
1561 //
1562 if (!NumberOfBytesToProtect) return STATUS_INVALID_PARAMETER_3;
1563
1564 //
1565 // Get a reference to the process
1566 //
1567 Status = ObReferenceObjectByHandle(ProcessHandle,
1568 PROCESS_VM_OPERATION,
1569 PsProcessType,
1570 PreviousMode,
1571 (PVOID*)(&Process),
1572 NULL);
1573 if (!NT_SUCCESS(Status)) return Status;
1574
1575 //
1576 // Check if we should attach
1577 //
1578 if (CurrentProcess != Process)
1579 {
1580 //
1581 // Do it
1582 //
1583 KeStackAttachProcess(&Process->Pcb, &ApcState);
1584 Attached = TRUE;
1585 }
1586
1587 //
1588 // Do the actual work
1589 //
1590 Status = MiProtectVirtualMemory(Process,
1591 &BaseAddress,
1592 &NumberOfBytesToProtect,
1593 NewAccessProtection,
1594 &OldAccessProtection);
1595
1596 //
1597 // Detach if needed
1598 //
1599 if (Attached) KeUnstackDetachProcess(&ApcState);
1600
1601 //
1602 // Release reference
1603 //
1604 ObDereferenceObject(Process);
1605
1606 //
1607 // Enter SEH to return data
1608 //
1609 _SEH2_TRY
1610 {
1611 //
1612 // Return data to user
1613 //
1614 *UnsafeOldAccessProtection = OldAccessProtection;
1615 *UnsafeBaseAddress = BaseAddress;
1616 *UnsafeNumberOfBytesToProtect = NumberOfBytesToProtect;
1617 }
1618 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1619 {
1620 }
1621 _SEH2_END;
1622
1623 //
1624 // Return status
1625 //
1626 return Status;
1627 }
1628
1629 NTSTATUS
1630 NTAPI
1631 NtLockVirtualMemory(IN HANDLE ProcessHandle,
1632 IN OUT PVOID *BaseAddress,
1633 IN OUT PSIZE_T NumberOfBytesToLock,
1634 IN ULONG MapType)
1635 {
1636 PEPROCESS Process;
1637 PEPROCESS CurrentProcess = PsGetCurrentProcess();
1638 NTSTATUS Status;
1639 BOOLEAN Attached = FALSE;
1640 KAPC_STATE ApcState;
1641 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
1642 PVOID CapturedBaseAddress;
1643 SIZE_T CapturedBytesToLock;
1644 PAGED_CODE();
1645
1646 //
1647 // Validate flags
1648 //
1649 if ((MapType & ~(MAP_PROCESS | MAP_SYSTEM)))
1650 {
1651 //
1652 // Invalid set of flags
1653 //
1654 return STATUS_INVALID_PARAMETER;
1655 }
1656
1657 //
1658 // At least one flag must be specified
1659 //
1660 if (!(MapType & (MAP_PROCESS | MAP_SYSTEM)))
1661 {
1662 //
1663 // No flag given
1664 //
1665 return STATUS_INVALID_PARAMETER;
1666 }
1667
1668 //
1669 // Enter SEH for probing
1670 //
1671 _SEH2_TRY
1672 {
1673 //
1674 // Validate output data
1675 //
1676 ProbeForWritePointer(BaseAddress);
1677 ProbeForWriteSize_t(NumberOfBytesToLock);
1678
1679 //
1680 // Capture it
1681 //
1682 CapturedBaseAddress = *BaseAddress;
1683 CapturedBytesToLock = *NumberOfBytesToLock;
1684 }
1685 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1686 {
1687 //
1688 // Get exception code
1689 //
1690 _SEH2_YIELD(return _SEH2_GetExceptionCode());
1691 }
1692 _SEH2_END;
1693
1694 //
1695 // Catch illegal base address
1696 //
1697 if (CapturedBaseAddress > MM_HIGHEST_USER_ADDRESS) return STATUS_INVALID_PARAMETER;
1698
1699 //
1700 // Catch illegal region size
1701 //
1702 if ((MmUserProbeAddress - (ULONG_PTR)CapturedBaseAddress) < CapturedBytesToLock)
1703 {
1704 //
1705 // Fail
1706 //
1707 return STATUS_INVALID_PARAMETER;
1708 }
1709
1710 //
1711 // 0 is also illegal
1712 //
1713 if (!CapturedBytesToLock) return STATUS_INVALID_PARAMETER;
1714
1715 //
1716 // Get a reference to the process
1717 //
1718 Status = ObReferenceObjectByHandle(ProcessHandle,
1719 PROCESS_VM_OPERATION,
1720 PsProcessType,
1721 PreviousMode,
1722 (PVOID*)(&Process),
1723 NULL);
1724 if (!NT_SUCCESS(Status)) return Status;
1725
1726 //
1727 // Check if this is is system-mapped
1728 //
1729 if (MapType & MAP_SYSTEM)
1730 {
1731 //
1732 // Check for required privilege
1733 //
1734 if (!SeSinglePrivilegeCheck(SeLockMemoryPrivilege, PreviousMode))
1735 {
1736 //
1737 // Fail: Don't have it
1738 //
1739 ObDereferenceObject(Process);
1740 return STATUS_PRIVILEGE_NOT_HELD;
1741 }
1742 }
1743
1744 //
1745 // Check if we should attach
1746 //
1747 if (CurrentProcess != Process)
1748 {
1749 //
1750 // Do it
1751 //
1752 KeStackAttachProcess(&Process->Pcb, &ApcState);
1753 Attached = TRUE;
1754 }
1755
1756 //
1757 // Oops :(
1758 //
1759 UNIMPLEMENTED;
1760
1761 //
1762 // Detach if needed
1763 //
1764 if (Attached) KeUnstackDetachProcess(&ApcState);
1765
1766 //
1767 // Release reference
1768 //
1769 ObDereferenceObject(Process);
1770
1771 //
1772 // Enter SEH to return data
1773 //
1774 _SEH2_TRY
1775 {
1776 //
1777 // Return data to user
1778 //
1779 *BaseAddress = CapturedBaseAddress;
1780 *NumberOfBytesToLock = 0;
1781 }
1782 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1783 {
1784 //
1785 // Get exception code
1786 //
1787 _SEH2_YIELD(return _SEH2_GetExceptionCode());
1788 }
1789 _SEH2_END;
1790
1791 //
1792 // Return status
1793 //
1794 return STATUS_SUCCESS;
1795 }
1796
1797 NTSTATUS
1798 NTAPI
1799 NtUnlockVirtualMemory(IN HANDLE ProcessHandle,
1800 IN OUT PVOID *BaseAddress,
1801 IN OUT PSIZE_T NumberOfBytesToUnlock,
1802 IN ULONG MapType)
1803 {
1804 PEPROCESS Process;
1805 PEPROCESS CurrentProcess = PsGetCurrentProcess();
1806 NTSTATUS Status;
1807 BOOLEAN Attached = FALSE;
1808 KAPC_STATE ApcState;
1809 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
1810 PVOID CapturedBaseAddress;
1811 SIZE_T CapturedBytesToUnlock;
1812 PAGED_CODE();
1813
1814 //
1815 // Validate flags
1816 //
1817 if ((MapType & ~(MAP_PROCESS | MAP_SYSTEM)))
1818 {
1819 //
1820 // Invalid set of flags
1821 //
1822 return STATUS_INVALID_PARAMETER;
1823 }
1824
1825 //
1826 // At least one flag must be specified
1827 //
1828 if (!(MapType & (MAP_PROCESS | MAP_SYSTEM)))
1829 {
1830 //
1831 // No flag given
1832 //
1833 return STATUS_INVALID_PARAMETER;
1834 }
1835
1836 //
1837 // Enter SEH for probing
1838 //
1839 _SEH2_TRY
1840 {
1841 //
1842 // Validate output data
1843 //
1844 ProbeForWritePointer(BaseAddress);
1845 ProbeForWriteSize_t(NumberOfBytesToUnlock);
1846
1847 //
1848 // Capture it
1849 //
1850 CapturedBaseAddress = *BaseAddress;
1851 CapturedBytesToUnlock = *NumberOfBytesToUnlock;
1852 }
1853 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1854 {
1855 //
1856 // Get exception code
1857 //
1858 _SEH2_YIELD(return _SEH2_GetExceptionCode());
1859 }
1860 _SEH2_END;
1861
1862 //
1863 // Catch illegal base address
1864 //
1865 if (CapturedBaseAddress > MM_HIGHEST_USER_ADDRESS) return STATUS_INVALID_PARAMETER;
1866
1867 //
1868 // Catch illegal region size
1869 //
1870 if ((MmUserProbeAddress - (ULONG_PTR)CapturedBaseAddress) < CapturedBytesToUnlock)
1871 {
1872 //
1873 // Fail
1874 //
1875 return STATUS_INVALID_PARAMETER;
1876 }
1877
1878 //
1879 // 0 is also illegal
1880 //
1881 if (!CapturedBytesToUnlock) return STATUS_INVALID_PARAMETER;
1882
1883 //
1884 // Get a reference to the process
1885 //
1886 Status = ObReferenceObjectByHandle(ProcessHandle,
1887 PROCESS_VM_OPERATION,
1888 PsProcessType,
1889 PreviousMode,
1890 (PVOID*)(&Process),
1891 NULL);
1892 if (!NT_SUCCESS(Status)) return Status;
1893
1894 //
1895 // Check if this is is system-mapped
1896 //
1897 if (MapType & MAP_SYSTEM)
1898 {
1899 //
1900 // Check for required privilege
1901 //
1902 if (!SeSinglePrivilegeCheck(SeLockMemoryPrivilege, PreviousMode))
1903 {
1904 //
1905 // Fail: Don't have it
1906 //
1907 ObDereferenceObject(Process);
1908 return STATUS_PRIVILEGE_NOT_HELD;
1909 }
1910 }
1911
1912 //
1913 // Check if we should attach
1914 //
1915 if (CurrentProcess != Process)
1916 {
1917 //
1918 // Do it
1919 //
1920 KeStackAttachProcess(&Process->Pcb, &ApcState);
1921 Attached = TRUE;
1922 }
1923
1924 //
1925 // Oops :(
1926 //
1927 UNIMPLEMENTED;
1928
1929 //
1930 // Detach if needed
1931 //
1932 if (Attached) KeUnstackDetachProcess(&ApcState);
1933
1934 //
1935 // Release reference
1936 //
1937 ObDereferenceObject(Process);
1938
1939 //
1940 // Enter SEH to return data
1941 //
1942 _SEH2_TRY
1943 {
1944 //
1945 // Return data to user
1946 //
1947 *BaseAddress = PAGE_ALIGN(CapturedBaseAddress);
1948 *NumberOfBytesToUnlock = 0;
1949 }
1950 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1951 {
1952 //
1953 // Get exception code
1954 //
1955 _SEH2_YIELD(return _SEH2_GetExceptionCode());
1956 }
1957 _SEH2_END;
1958
1959 //
1960 // Return status
1961 //
1962 return STATUS_SUCCESS;
1963 }
1964
1965 NTSTATUS
1966 NTAPI
1967 NtFlushVirtualMemory(IN HANDLE ProcessHandle,
1968 IN OUT PVOID *BaseAddress,
1969 IN OUT PSIZE_T NumberOfBytesToFlush,
1970 OUT PIO_STATUS_BLOCK IoStatusBlock)
1971 {
1972 PEPROCESS Process;
1973 NTSTATUS Status;
1974 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
1975 PVOID CapturedBaseAddress;
1976 SIZE_T CapturedBytesToFlush;
1977 IO_STATUS_BLOCK LocalStatusBlock;
1978 PAGED_CODE();
1979
1980 //
1981 // Check if we came from user mode
1982 //
1983 if (PreviousMode != KernelMode)
1984 {
1985 //
1986 // Enter SEH for probing
1987 //
1988 _SEH2_TRY
1989 {
1990 //
1991 // Validate all outputs
1992 //
1993 ProbeForWritePointer(BaseAddress);
1994 ProbeForWriteSize_t(NumberOfBytesToFlush);
1995 ProbeForWriteIoStatusBlock(IoStatusBlock);
1996
1997 //
1998 // Capture them
1999 //
2000 CapturedBaseAddress = *BaseAddress;
2001 CapturedBytesToFlush = *NumberOfBytesToFlush;
2002 }
2003 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2004 {
2005 //
2006 // Get exception code
2007 //
2008 _SEH2_YIELD(return _SEH2_GetExceptionCode());
2009 }
2010 _SEH2_END;
2011 }
2012 else
2013 {
2014 //
2015 // Capture directly
2016 //
2017 CapturedBaseAddress = *BaseAddress;
2018 CapturedBytesToFlush = *NumberOfBytesToFlush;
2019 }
2020
2021 //
2022 // Catch illegal base address
2023 //
2024 if (CapturedBaseAddress > MM_HIGHEST_USER_ADDRESS) return STATUS_INVALID_PARAMETER;
2025
2026 //
2027 // Catch illegal region size
2028 //
2029 if ((MmUserProbeAddress - (ULONG_PTR)CapturedBaseAddress) < CapturedBytesToFlush)
2030 {
2031 //
2032 // Fail
2033 //
2034 return STATUS_INVALID_PARAMETER;
2035 }
2036
2037 //
2038 // Get a reference to the process
2039 //
2040 Status = ObReferenceObjectByHandle(ProcessHandle,
2041 PROCESS_VM_OPERATION,
2042 PsProcessType,
2043 PreviousMode,
2044 (PVOID*)(&Process),
2045 NULL);
2046 if (!NT_SUCCESS(Status)) return Status;
2047
2048 //
2049 // Do it
2050 //
2051 Status = MmFlushVirtualMemory(Process,
2052 &CapturedBaseAddress,
2053 &CapturedBytesToFlush,
2054 &LocalStatusBlock);
2055
2056 //
2057 // Release reference
2058 //
2059 ObDereferenceObject(Process);
2060
2061 //
2062 // Enter SEH to return data
2063 //
2064 _SEH2_TRY
2065 {
2066 //
2067 // Return data to user
2068 //
2069 *BaseAddress = PAGE_ALIGN(CapturedBaseAddress);
2070 *NumberOfBytesToFlush = 0;
2071 *IoStatusBlock = LocalStatusBlock;
2072 }
2073 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2074 {
2075 }
2076 _SEH2_END;
2077
2078 //
2079 // Return status
2080 //
2081 return Status;
2082 }
2083
2084 /*
2085 * @unimplemented
2086 */
2087 NTSTATUS
2088 NTAPI
2089 NtGetWriteWatch(IN HANDLE ProcessHandle,
2090 IN ULONG Flags,
2091 IN PVOID BaseAddress,
2092 IN SIZE_T RegionSize,
2093 IN PVOID *UserAddressArray,
2094 OUT PULONG_PTR EntriesInUserAddressArray,
2095 OUT PULONG Granularity)
2096 {
2097 PEPROCESS Process;
2098 NTSTATUS Status;
2099 PVOID EndAddress;
2100 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
2101 ULONG_PTR CapturedEntryCount;
2102 PAGED_CODE();
2103
2104 //
2105 // Check if we came from user mode
2106 //
2107 if (PreviousMode != KernelMode)
2108 {
2109 //
2110 // Enter SEH for probing
2111 //
2112 _SEH2_TRY
2113 {
2114 //
2115 // Catch illegal base address
2116 //
2117 if (BaseAddress > MM_HIGHEST_USER_ADDRESS) return STATUS_INVALID_PARAMETER_2;
2118
2119 //
2120 // Catch illegal region size
2121 //
2122 if ((MmUserProbeAddress - (ULONG_PTR)BaseAddress) < RegionSize)
2123 {
2124 //
2125 // Fail
2126 //
2127 return STATUS_INVALID_PARAMETER_3;
2128 }
2129
2130 //
2131 // Validate all data
2132 //
2133 ProbeForWriteSize_t(EntriesInUserAddressArray);
2134 ProbeForWriteUlong(Granularity);
2135
2136 //
2137 // Capture them
2138 //
2139 CapturedEntryCount = *EntriesInUserAddressArray;
2140
2141 //
2142 // Must have a count
2143 //
2144 if (CapturedEntryCount == 0) return STATUS_INVALID_PARAMETER_5;
2145
2146 //
2147 // Can't be larger than the maximum
2148 //
2149 if (CapturedEntryCount > (MAXULONG_PTR / sizeof(ULONG_PTR)))
2150 {
2151 //
2152 // Fail
2153 //
2154 return STATUS_INVALID_PARAMETER_5;
2155 }
2156
2157 //
2158 // Probe the actual array
2159 //
2160 ProbeForWrite(UserAddressArray,
2161 CapturedEntryCount * sizeof(PVOID),
2162 sizeof(PVOID));
2163 }
2164 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2165 {
2166 //
2167 // Get exception code
2168 //
2169 _SEH2_YIELD(return _SEH2_GetExceptionCode());
2170 }
2171 _SEH2_END;
2172 }
2173 else
2174 {
2175 //
2176 // Capture directly
2177 //
2178 CapturedEntryCount = *EntriesInUserAddressArray;
2179 ASSERT(CapturedEntryCount != 0);
2180 }
2181
2182 //
2183 // Check if this is a local request
2184 //
2185 if (ProcessHandle == NtCurrentProcess())
2186 {
2187 //
2188 // No need to reference the process
2189 //
2190 Process = PsGetCurrentProcess();
2191 }
2192 else
2193 {
2194 //
2195 // Reference the target
2196 //
2197 Status = ObReferenceObjectByHandle(ProcessHandle,
2198 PROCESS_VM_OPERATION,
2199 PsProcessType,
2200 PreviousMode,
2201 (PVOID *)&Process,
2202 NULL);
2203 if (!NT_SUCCESS(Status)) return Status;
2204 }
2205
2206 //
2207 // Compute the last address and validate it
2208 //
2209 EndAddress = (PVOID)((ULONG_PTR)BaseAddress + RegionSize - 1);
2210 if (BaseAddress > EndAddress)
2211 {
2212 //
2213 // Fail
2214 //
2215 if (ProcessHandle != NtCurrentProcess()) ObDereferenceObject(Process);
2216 return STATUS_INVALID_PARAMETER_4;
2217 }
2218
2219 //
2220 // Oops :(
2221 //
2222 UNIMPLEMENTED;
2223
2224 //
2225 // Dereference if needed
2226 //
2227 if (ProcessHandle != NtCurrentProcess()) ObDereferenceObject(Process);
2228
2229 //
2230 // Enter SEH to return data
2231 //
2232 _SEH2_TRY
2233 {
2234 //
2235 // Return data to user
2236 //
2237 *EntriesInUserAddressArray = 0;
2238 *Granularity = PAGE_SIZE;
2239 }
2240 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2241 {
2242 //
2243 // Get exception code
2244 //
2245 Status = _SEH2_GetExceptionCode();
2246 }
2247 _SEH2_END;
2248
2249 //
2250 // Return success
2251 //
2252 return STATUS_SUCCESS;
2253 }
2254
2255 /*
2256 * @unimplemented
2257 */
2258 NTSTATUS
2259 NTAPI
2260 NtResetWriteWatch(IN HANDLE ProcessHandle,
2261 IN PVOID BaseAddress,
2262 IN SIZE_T RegionSize)
2263 {
2264 PVOID EndAddress;
2265 PEPROCESS Process;
2266 NTSTATUS Status;
2267 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
2268 ASSERT (KeGetCurrentIrql() == PASSIVE_LEVEL);
2269
2270 //
2271 // Catch illegal base address
2272 //
2273 if (BaseAddress > MM_HIGHEST_USER_ADDRESS) return STATUS_INVALID_PARAMETER_2;
2274
2275 //
2276 // Catch illegal region size
2277 //
2278 if ((MmUserProbeAddress - (ULONG_PTR)BaseAddress) < RegionSize)
2279 {
2280 //
2281 // Fail
2282 //
2283 return STATUS_INVALID_PARAMETER_3;
2284 }
2285
2286 //
2287 // Check if this is a local request
2288 //
2289 if (ProcessHandle == NtCurrentProcess())
2290 {
2291 //
2292 // No need to reference the process
2293 //
2294 Process = PsGetCurrentProcess();
2295 }
2296 else
2297 {
2298 //
2299 // Reference the target
2300 //
2301 Status = ObReferenceObjectByHandle(ProcessHandle,
2302 PROCESS_VM_OPERATION,
2303 PsProcessType,
2304 PreviousMode,
2305 (PVOID *)&Process,
2306 NULL);
2307 if (!NT_SUCCESS(Status)) return Status;
2308 }
2309
2310 //
2311 // Compute the last address and validate it
2312 //
2313 EndAddress = (PVOID)((ULONG_PTR)BaseAddress + RegionSize - 1);
2314 if (BaseAddress > EndAddress)
2315 {
2316 //
2317 // Fail
2318 //
2319 if (ProcessHandle != NtCurrentProcess()) ObDereferenceObject(Process);
2320 return STATUS_INVALID_PARAMETER_3;
2321 }
2322
2323 //
2324 // Oops :(
2325 //
2326 UNIMPLEMENTED;
2327
2328 //
2329 // Dereference if needed
2330 //
2331 if (ProcessHandle != NtCurrentProcess()) ObDereferenceObject(Process);
2332
2333 //
2334 // Return success
2335 //
2336 return STATUS_SUCCESS;
2337 }
2338
2339 NTSTATUS
2340 NTAPI
2341 MiQueryMemoryBasicInformation(IN HANDLE ProcessHandle,
2342 IN PVOID BaseAddress,
2343 OUT PVOID MemoryInformation,
2344 IN SIZE_T MemoryInformationLength,
2345 OUT PSIZE_T ReturnLength)
2346 {
2347 PEPROCESS TargetProcess;
2348 NTSTATUS Status = STATUS_SUCCESS;
2349 PMMVAD Vad = NULL;
2350 PVOID Address, NextAddress;
2351 BOOLEAN Found = FALSE;
2352 ULONG NewProtect, NewState, BaseVpn;
2353 MEMORY_BASIC_INFORMATION MemoryInfo;
2354 KAPC_STATE ApcState;
2355 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
2356 PMEMORY_AREA MemoryArea;
2357 SIZE_T ResultLength;
2358
2359 /* Check for illegal addresses in user-space, or the shared memory area */
2360 if ((BaseAddress > MM_HIGHEST_VAD_ADDRESS) ||
2361 (PAGE_ALIGN(BaseAddress) == (PVOID)USER_SHARED_DATA))
2362 {
2363 Address = PAGE_ALIGN(BaseAddress);
2364
2365 /* Make up an info structure describing this range */
2366 MemoryInfo.BaseAddress = Address;
2367 MemoryInfo.AllocationProtect = PAGE_READONLY;
2368 MemoryInfo.Type = MEM_PRIVATE;
2369
2370 /* Special case for shared data */
2371 if (Address == (PVOID)USER_SHARED_DATA)
2372 {
2373 MemoryInfo.AllocationBase = (PVOID)USER_SHARED_DATA;
2374 MemoryInfo.State = MEM_COMMIT;
2375 MemoryInfo.Protect = PAGE_READONLY;
2376 MemoryInfo.RegionSize = PAGE_SIZE;
2377 }
2378 else
2379 {
2380 MemoryInfo.AllocationBase = (PCHAR)MM_HIGHEST_VAD_ADDRESS + 1;
2381 MemoryInfo.State = MEM_RESERVE;
2382 MemoryInfo.Protect = PAGE_NOACCESS;
2383 MemoryInfo.RegionSize = (ULONG_PTR)MM_HIGHEST_USER_ADDRESS + 1 - (ULONG_PTR)Address;
2384 }
2385
2386 /* Return the data, NtQueryInformation already probed it*/
2387 if (PreviousMode != KernelMode)
2388 {
2389 _SEH2_TRY
2390 {
2391 *(PMEMORY_BASIC_INFORMATION)MemoryInformation = MemoryInfo;
2392 if (ReturnLength) *ReturnLength = sizeof(MEMORY_BASIC_INFORMATION);
2393 }
2394 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2395 {
2396 Status = _SEH2_GetExceptionCode();
2397 }
2398 _SEH2_END;
2399 }
2400 else
2401 {
2402 *(PMEMORY_BASIC_INFORMATION)MemoryInformation = MemoryInfo;
2403 if (ReturnLength) *ReturnLength = sizeof(MEMORY_BASIC_INFORMATION);
2404 }
2405
2406 return Status;
2407 }
2408
2409 /* Check if this is for a local or remote process */
2410 if (ProcessHandle == NtCurrentProcess())
2411 {
2412 TargetProcess = PsGetCurrentProcess();
2413 }
2414 else
2415 {
2416 /* Reference the target process */
2417 Status = ObReferenceObjectByHandle(ProcessHandle,
2418 PROCESS_QUERY_INFORMATION,
2419 PsProcessType,
2420 ExGetPreviousMode(),
2421 (PVOID*)&TargetProcess,
2422 NULL);
2423 if (!NT_SUCCESS(Status)) return Status;
2424
2425 /* Attach to it now */
2426 KeStackAttachProcess(&TargetProcess->Pcb, &ApcState);
2427 }
2428
2429 /* Loop the VADs */
2430 ASSERT(TargetProcess->VadRoot.NumberGenericTableElements);
2431 if (TargetProcess->VadRoot.NumberGenericTableElements)
2432 {
2433 /* Scan on the right */
2434 Vad = (PMMVAD)TargetProcess->VadRoot.BalancedRoot.RightChild;
2435 BaseVpn = (ULONG_PTR)BaseAddress >> PAGE_SHIFT;
2436 while (Vad)
2437 {
2438 /* Check if this VAD covers the allocation range */
2439 if ((BaseVpn >= Vad->StartingVpn) &&
2440 (BaseVpn <= Vad->EndingVpn))
2441 {
2442 /* We're done */
2443 Found = TRUE;
2444 break;
2445 }
2446
2447 /* Check if this VAD is too high */
2448 if (BaseVpn < Vad->StartingVpn)
2449 {
2450 /* Stop if there is no left child */
2451 if (!Vad->LeftChild) break;
2452
2453 /* Search on the left next */
2454 Vad = Vad->LeftChild;
2455 }
2456 else
2457 {
2458 /* Then this VAD is too low, keep searching on the right */
2459 ASSERT(BaseVpn > Vad->EndingVpn);
2460
2461 /* Stop if there is no right child */
2462 if (!Vad->RightChild) break;
2463
2464 /* Search on the right next */
2465 Vad = Vad->RightChild;
2466 }
2467 }
2468 }
2469
2470 /* Was a VAD found? */
2471 if (!Found)
2472 {
2473 Address = PAGE_ALIGN(BaseAddress);
2474
2475 /* Calculate region size */
2476 if (Vad)
2477 {
2478 if (Vad->StartingVpn >= BaseVpn)
2479 {
2480 /* Region size is the free space till the start of that VAD */
2481 MemoryInfo.RegionSize = (ULONG_PTR)(Vad->StartingVpn << PAGE_SHIFT) - (ULONG_PTR)Address;
2482 }
2483 else
2484 {
2485 /* Get the next VAD */
2486 Vad = (PMMVAD)MiGetNextNode((PMMADDRESS_NODE)Vad);
2487 if (Vad)
2488 {
2489 /* Region size is the free space till the start of that VAD */
2490 MemoryInfo.RegionSize = (ULONG_PTR)(Vad->StartingVpn << PAGE_SHIFT) - (ULONG_PTR)Address;
2491 }
2492 else
2493 {
2494 /* Maximum possible region size with that base address */
2495 MemoryInfo.RegionSize = (PCHAR)MM_HIGHEST_VAD_ADDRESS + 1 - (PCHAR)Address;
2496 }
2497 }
2498 }
2499 else
2500 {
2501 /* Maximum possible region size with that base address */
2502 MemoryInfo.RegionSize = (PCHAR)MM_HIGHEST_VAD_ADDRESS + 1 - (PCHAR)Address;
2503 }
2504
2505 /* Check if we were attached */
2506 if (ProcessHandle != NtCurrentProcess())
2507 {
2508 /* Detach and derefernece the process */
2509 KeUnstackDetachProcess(&ApcState);
2510 ObDereferenceObject(TargetProcess);
2511 }
2512
2513 /* Build the rest of the initial information block */
2514 MemoryInfo.BaseAddress = Address;
2515 MemoryInfo.AllocationBase = NULL;
2516 MemoryInfo.AllocationProtect = 0;
2517 MemoryInfo.State = MEM_FREE;
2518 MemoryInfo.Protect = PAGE_NOACCESS;
2519 MemoryInfo.Type = 0;
2520
2521 /* Return the data, NtQueryInformation already probed it*/
2522 if (PreviousMode != KernelMode)
2523 {
2524 _SEH2_TRY
2525 {
2526 *(PMEMORY_BASIC_INFORMATION)MemoryInformation = MemoryInfo;
2527 if (ReturnLength) *ReturnLength = sizeof(MEMORY_BASIC_INFORMATION);
2528 }
2529 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2530 {
2531 Status = _SEH2_GetExceptionCode();
2532 }
2533 _SEH2_END;
2534 }
2535 else
2536 {
2537 *(PMEMORY_BASIC_INFORMATION)MemoryInformation = MemoryInfo;
2538 if (ReturnLength) *ReturnLength = sizeof(MEMORY_BASIC_INFORMATION);
2539 }
2540
2541 return Status;
2542 }
2543
2544 /* This must be a VM VAD */
2545 ASSERT(Vad->u.VadFlags.PrivateMemory);
2546
2547 /* Lock the address space of the process */
2548 MmLockAddressSpace(&TargetProcess->Vm);
2549
2550 /* Find the memory area the specified address belongs to */
2551 MemoryArea = MmLocateMemoryAreaByAddress(&TargetProcess->Vm, BaseAddress);
2552 ASSERT(MemoryArea != NULL);
2553
2554 /* Determine information dependent on the memory area type */
2555 if (MemoryArea->Type == MEMORY_AREA_SECTION_VIEW)
2556 {
2557 Status = MmQuerySectionView(MemoryArea, BaseAddress, &MemoryInfo, &ResultLength);
2558 ASSERT(NT_SUCCESS(Status));
2559 }
2560 else
2561 {
2562 /* Build the initial information block */
2563 Address = PAGE_ALIGN(BaseAddress);
2564 MemoryInfo.BaseAddress = Address;
2565 MemoryInfo.AllocationBase = (PVOID)(Vad->StartingVpn << PAGE_SHIFT);
2566 MemoryInfo.AllocationProtect = MmProtectToValue[Vad->u.VadFlags.Protection];
2567 MemoryInfo.Type = MEM_PRIVATE;
2568
2569 /* Find the largest chunk of memory which has the same state and protection mask */
2570 MemoryInfo.State = MiQueryAddressState(Address,
2571 Vad,
2572 TargetProcess,
2573 &MemoryInfo.Protect,
2574 &NextAddress);
2575 Address = NextAddress;
2576 while (((ULONG_PTR)Address >> PAGE_SHIFT) <= Vad->EndingVpn)
2577 {
2578 /* Keep going unless the state or protection mask changed */
2579 NewState = MiQueryAddressState(Address, Vad, TargetProcess, &NewProtect, &NextAddress);
2580 if ((NewState != MemoryInfo.State) || (NewProtect != MemoryInfo.Protect)) break;
2581 Address = NextAddress;
2582 }
2583
2584 /* Now that we know the last VA address, calculate the region size */
2585 MemoryInfo.RegionSize = ((ULONG_PTR)Address - (ULONG_PTR)MemoryInfo.BaseAddress);
2586 }
2587
2588 /* Unlock the address space of the process */
2589 MmUnlockAddressSpace(&TargetProcess->Vm);
2590
2591 /* Check if we were attached */
2592 if (ProcessHandle != NtCurrentProcess())
2593 {
2594 /* Detach and derefernece the process */
2595 KeUnstackDetachProcess(&ApcState);
2596 ObDereferenceObject(TargetProcess);
2597 }
2598
2599 /* Return the data, NtQueryInformation already probed it*/
2600 if (PreviousMode != KernelMode)
2601 {
2602 _SEH2_TRY
2603 {
2604 *(PMEMORY_BASIC_INFORMATION)MemoryInformation = MemoryInfo;
2605 if (ReturnLength) *ReturnLength = sizeof(MEMORY_BASIC_INFORMATION);
2606 }
2607 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2608 {
2609 Status = _SEH2_GetExceptionCode();
2610 }
2611 _SEH2_END;
2612 }
2613 else
2614 {
2615 *(PMEMORY_BASIC_INFORMATION)MemoryInformation = MemoryInfo;
2616 if (ReturnLength) *ReturnLength = sizeof(MEMORY_BASIC_INFORMATION);
2617 }
2618
2619 /* All went well */
2620 DPRINT("Base: %p AllocBase: %p AllocProtect: %lx Protect: %lx "
2621 "State: %lx Type: %lx Size: %lx\n",
2622 MemoryInfo.BaseAddress, MemoryInfo.AllocationBase,
2623 MemoryInfo.AllocationProtect, MemoryInfo.Protect,
2624 MemoryInfo.State, MemoryInfo.Type, MemoryInfo.RegionSize);
2625
2626 return Status;
2627 }
2628
2629 NTSTATUS
2630 NTAPI
2631 MiQueryMemorySectionName(IN HANDLE ProcessHandle,
2632 IN PVOID BaseAddress,
2633 OUT PVOID MemoryInformation,
2634 IN SIZE_T MemoryInformationLength,
2635 OUT PSIZE_T ReturnLength)
2636 {
2637 PEPROCESS Process;
2638 NTSTATUS Status;
2639 WCHAR ModuleFileNameBuffer[MAX_PATH] = {0};
2640 UNICODE_STRING ModuleFileName;
2641 PMEMORY_SECTION_NAME SectionName = NULL;
2642 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
2643
2644 Status = ObReferenceObjectByHandle(ProcessHandle,
2645 PROCESS_QUERY_INFORMATION,
2646 NULL,
2647 PreviousMode,
2648 (PVOID*)(&Process),
2649 NULL);
2650
2651 if (!NT_SUCCESS(Status))
2652 {
2653 DPRINT("MiQueryMemorySectionName: ObReferenceObjectByHandle returned %x\n",Status);
2654 return Status;
2655 }
2656
2657 RtlInitEmptyUnicodeString(&ModuleFileName, ModuleFileNameBuffer, sizeof(ModuleFileNameBuffer));
2658 Status = MmGetFileNameForAddress(BaseAddress, &ModuleFileName);
2659
2660 if (NT_SUCCESS(Status))
2661 {
2662 SectionName = MemoryInformation;
2663 if (PreviousMode != KernelMode)
2664 {
2665 _SEH2_TRY
2666 {
2667 RtlInitUnicodeString(&SectionName->SectionFileName, SectionName->NameBuffer);
2668 SectionName->SectionFileName.MaximumLength = MemoryInformationLength;
2669 RtlCopyUnicodeString(&SectionName->SectionFileName, &ModuleFileName);
2670
2671 if (ReturnLength) *ReturnLength = ModuleFileName.Length;
2672
2673 }
2674 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2675 {
2676 Status = _SEH2_GetExceptionCode();
2677 }
2678 _SEH2_END;
2679 }
2680 else
2681 {
2682 RtlInitUnicodeString(&SectionName->SectionFileName, SectionName->NameBuffer);
2683 SectionName->SectionFileName.MaximumLength = MemoryInformationLength;
2684 RtlCopyUnicodeString(&SectionName->SectionFileName, &ModuleFileName);
2685
2686 if (ReturnLength) *ReturnLength = ModuleFileName.Length;
2687
2688 }
2689 }
2690 ObDereferenceObject(Process);
2691 return Status;
2692 }
2693
2694 NTSTATUS
2695 NTAPI
2696 NtQueryVirtualMemory(IN HANDLE ProcessHandle,
2697 IN PVOID BaseAddress,
2698 IN MEMORY_INFORMATION_CLASS MemoryInformationClass,
2699 OUT PVOID MemoryInformation,
2700 IN SIZE_T MemoryInformationLength,
2701 OUT PSIZE_T ReturnLength)
2702 {
2703 NTSTATUS Status = STATUS_SUCCESS;
2704 KPROCESSOR_MODE PreviousMode;
2705
2706 DPRINT("Querying class %d about address: %p\n", MemoryInformationClass, BaseAddress);
2707
2708 /* Bail out if the address is invalid */
2709 if (BaseAddress > MM_HIGHEST_USER_ADDRESS) return STATUS_INVALID_PARAMETER;
2710
2711 /* Probe return buffer */
2712 PreviousMode = ExGetPreviousMode();
2713 if (PreviousMode != KernelMode)
2714 {
2715 _SEH2_TRY
2716 {
2717 ProbeForWrite(MemoryInformation,
2718 MemoryInformationLength,
2719 sizeof(ULONG_PTR));
2720
2721 if (ReturnLength) ProbeForWriteSize_t(ReturnLength);
2722 }
2723 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2724 {
2725 Status = _SEH2_GetExceptionCode();
2726 }
2727 _SEH2_END;
2728
2729 if (!NT_SUCCESS(Status))
2730 {
2731 return Status;
2732 }
2733 }
2734
2735 switch(MemoryInformationClass)
2736 {
2737 case MemoryBasicInformation:
2738 /* Validate the size information of the class */
2739 if (MemoryInformationLength < sizeof(MEMORY_BASIC_INFORMATION))
2740 {
2741 /* The size is invalid */
2742 return STATUS_INFO_LENGTH_MISMATCH;
2743 }
2744 Status = MiQueryMemoryBasicInformation(ProcessHandle,
2745 BaseAddress,
2746 MemoryInformation,
2747 MemoryInformationLength,
2748 ReturnLength);
2749 break;
2750
2751 case MemorySectionName:
2752 /* Validate the size information of the class */
2753 if (MemoryInformationLength < sizeof(MEMORY_SECTION_NAME))
2754 {
2755 /* The size is invalid */
2756 return STATUS_INFO_LENGTH_MISMATCH;
2757 }
2758 Status = MiQueryMemorySectionName(ProcessHandle,
2759 BaseAddress,
2760 MemoryInformation,
2761 MemoryInformationLength,
2762 ReturnLength);
2763 break;
2764 case MemoryWorkingSetList:
2765 case MemoryBasicVlmInformation:
2766 default:
2767 DPRINT1("Unhandled memory information class %d\n", MemoryInformationClass);
2768 break;
2769 }
2770
2771 return Status;
2772 }
2773
2774 /* EOF */