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