- Make use of _SEH2_YIELD in Mm
[reactos.git] / reactos / ntoskrnl / mm / virtual.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS kernel
4 * FILE: ntoskrnl/mm/virtual.c
5 * PURPOSE: Implementing operations on virtual memory.
6 *
7 * PROGRAMMERS: David Welch
8 */
9
10 /* INCLUDE ********************************************************************/
11
12 #include <ntoskrnl.h>
13 #define NDEBUG
14 #include <debug.h>
15
16 #define MI_MAPPED_COPY_PAGES 16
17 #define MI_POOL_COPY_BYTES 512
18 #define MI_MAX_TRANSFER_SIZE 64 * 1024
19 #define TAG_VM 'wRmV'
20
21 /* PRIVATE FUNCTIONS **********************************************************/
22
23 LONG
24 MiGetExceptionInfo(EXCEPTION_POINTERS *ExceptionInfo, BOOLEAN * HaveBadAddress, ULONG_PTR * BadAddress)
25 {
26 PEXCEPTION_RECORD ExceptionRecord;
27 PAGED_CODE();
28
29 /* Assume default */
30 *HaveBadAddress = FALSE;
31
32 /* Get the exception record */
33 ExceptionRecord = ExceptionInfo->ExceptionRecord;
34
35 /* Look at the exception code */
36 if ((ExceptionRecord->ExceptionCode == STATUS_ACCESS_VIOLATION) ||
37 (ExceptionRecord->ExceptionCode == STATUS_GUARD_PAGE_VIOLATION) ||
38 (ExceptionRecord->ExceptionCode == STATUS_IN_PAGE_ERROR))
39 {
40 /* We can tell the address if we have more than one parameter */
41 if (ExceptionRecord->NumberParameters > 1)
42 {
43 /* Return the address */
44 *HaveBadAddress = TRUE;
45 *BadAddress = ExceptionRecord->ExceptionInformation[1];
46 }
47 }
48
49 /* Continue executing the next handler */
50 return EXCEPTION_EXECUTE_HANDLER;
51 }
52
53 NTSTATUS
54 NTAPI
55 MiDoMappedCopy(IN PEPROCESS SourceProcess,
56 IN PVOID SourceAddress,
57 IN PEPROCESS TargetProcess,
58 OUT PVOID TargetAddress,
59 IN SIZE_T BufferSize,
60 IN KPROCESSOR_MODE PreviousMode,
61 OUT PSIZE_T ReturnSize)
62 {
63 PFN_NUMBER MdlBuffer[(sizeof(MDL) / sizeof(PFN_NUMBER)) + MI_MAPPED_COPY_PAGES + 1];
64 PMDL Mdl = (PMDL)MdlBuffer;
65 SIZE_T TotalSize, CurrentSize, RemainingSize;
66 volatile BOOLEAN FailedInProbe = FALSE, FailedInMapping = FALSE, FailedInMoving;
67 volatile BOOLEAN PagesLocked;
68 PVOID CurrentAddress = SourceAddress, CurrentTargetAddress = TargetAddress;
69 volatile PVOID MdlAddress;
70 KAPC_STATE ApcState;
71 BOOLEAN HaveBadAddress;
72 ULONG_PTR BadAddress;
73 PAGED_CODE();
74
75 /* Calculate the maximum amount of data to move */
76 TotalSize = (MI_MAPPED_COPY_PAGES - 2) * PAGE_SIZE;
77 if (BufferSize <= TotalSize) TotalSize = BufferSize;
78 CurrentSize = TotalSize;
79 RemainingSize = BufferSize;
80
81 /* Loop as long as there is still data */
82 while (RemainingSize > 0)
83 {
84 /* Check if this transfer will finish everything off */
85 if (RemainingSize < CurrentSize) CurrentSize = RemainingSize;
86
87 /* Attach to the source address space */
88 KeStackAttachProcess(&SourceProcess->Pcb, &ApcState);
89
90 /* Reset state for this pass */
91 MdlAddress = NULL;
92 PagesLocked = FALSE;
93 FailedInMoving = FALSE;
94 ASSERT(FailedInProbe == FALSE);
95
96 /* Protect user-mode copy */
97 _SEH2_TRY
98 {
99 /* If this is our first time, probe the buffer */
100 if ((CurrentAddress == SourceAddress) && (PreviousMode != KernelMode))
101 {
102 /* Catch a failure here */
103 FailedInProbe = TRUE;
104
105 /* Do the probe */
106 ProbeForRead(SourceAddress, BufferSize, sizeof(CHAR));
107
108 /* Passed */
109 FailedInProbe = FALSE;
110 }
111
112 /* Initialize and probe and lock the MDL */
113 MmInitializeMdl (Mdl, CurrentAddress, CurrentSize);
114 MmProbeAndLockPages (Mdl, PreviousMode, IoReadAccess);
115 PagesLocked = TRUE;
116
117 /* Now map the pages */
118 MdlAddress = MmMapLockedPagesSpecifyCache(Mdl,
119 KernelMode,
120 MmCached,
121 NULL,
122 FALSE,
123 HighPagePriority);
124 if (!MdlAddress)
125 {
126 /* Use our SEH handler to pick this up */
127 FailedInMapping = TRUE;
128 ExRaiseStatus(STATUS_INSUFFICIENT_RESOURCES);
129 }
130
131 /* Now let go of the source and grab to the target process */
132 KeUnstackDetachProcess(&ApcState);
133 KeStackAttachProcess(&TargetProcess->Pcb, &ApcState);
134
135 /* Check if this is our first time through */
136 if ((CurrentAddress == SourceAddress) && (PreviousMode != KernelMode))
137 {
138 /* Catch a failure here */
139 FailedInProbe = TRUE;
140
141 /* Do the probe */
142 ProbeForWrite(TargetAddress, BufferSize, sizeof(CHAR));
143
144 /* Passed */
145 FailedInProbe = FALSE;
146 }
147
148 /* Now do the actual move */
149 FailedInMoving = TRUE;
150 RtlCopyMemory(CurrentTargetAddress, MdlAddress, CurrentSize);
151 }
152 _SEH2_EXCEPT(MiGetExceptionInfo(_SEH2_GetExceptionInformation(), &HaveBadAddress, &BadAddress))
153 {
154 /* Detach from whoever we may be attached to */
155 KeUnstackDetachProcess(&ApcState);
156
157 /* Check if we had mapped the pages */
158 if (MdlAddress) MmUnmapLockedPages(MdlAddress, Mdl);
159
160 /* Check if we had locked the pages */
161 if (PagesLocked) MmUnlockPages(Mdl);
162
163 /* Check if we failed during the probe or mapping */
164 if ((FailedInProbe) || (FailedInMapping))
165 {
166 /* Exit */
167 _SEH2_YIELD(return _SEH2_GetExceptionCode());
168 }
169
170 /* Otherwise, we failed probably during the move */
171 *ReturnSize = BufferSize - RemainingSize;
172 if (FailedInMoving)
173 {
174 /* Check if we know exactly where we stopped copying */
175 if (HaveBadAddress)
176 {
177 /* Return the exact number of bytes copied */
178 *ReturnSize = BadAddress - (ULONG_PTR)SourceAddress;
179 }
180 }
181
182 /* Return partial copy */
183 _SEH2_YIELD(return STATUS_PARTIAL_COPY);
184 }
185 _SEH2_END;
186
187 /* Detach from target */
188 KeUnstackDetachProcess(&ApcState);
189
190 /* Unmap and unlock */
191 MmUnmapLockedPages(MdlAddress, Mdl);
192 MmUnlockPages(Mdl);
193
194 /* Update location and size */
195 RemainingSize -= CurrentSize;
196 CurrentAddress = (PVOID)((ULONG_PTR)CurrentAddress + CurrentSize);
197 CurrentTargetAddress = (PVOID)((ULONG_PTR)CurrentTargetAddress + CurrentSize);
198 }
199
200 /* All bytes read */
201 *ReturnSize = BufferSize;
202 return STATUS_SUCCESS;
203 }
204
205 NTSTATUS
206 NTAPI
207 MiDoPoolCopy(IN PEPROCESS SourceProcess,
208 IN PVOID SourceAddress,
209 IN PEPROCESS TargetProcess,
210 OUT PVOID TargetAddress,
211 IN SIZE_T BufferSize,
212 IN KPROCESSOR_MODE PreviousMode,
213 OUT PSIZE_T ReturnSize)
214 {
215 UCHAR StackBuffer[MI_POOL_COPY_BYTES];
216 SIZE_T TotalSize, CurrentSize, RemainingSize;
217 volatile BOOLEAN FailedInProbe = FALSE, FailedInMoving, HavePoolAddress = FALSE;
218 PVOID CurrentAddress = SourceAddress, CurrentTargetAddress = TargetAddress;
219 PVOID PoolAddress;
220 KAPC_STATE ApcState;
221 BOOLEAN HaveBadAddress;
222 ULONG_PTR BadAddress;
223 PAGED_CODE();
224
225 /* Calculate the maximum amount of data to move */
226 TotalSize = MI_MAX_TRANSFER_SIZE;
227 if (BufferSize <= MI_MAX_TRANSFER_SIZE) TotalSize = BufferSize;
228 CurrentSize = TotalSize;
229 RemainingSize = BufferSize;
230
231 /* Check if we can use the stack */
232 if (BufferSize <= MI_POOL_COPY_BYTES)
233 {
234 /* Use it */
235 PoolAddress = (PVOID)StackBuffer;
236 }
237 else
238 {
239 /* Allocate pool */
240 PoolAddress = ExAllocatePoolWithTag(NonPagedPool, TotalSize, TAG_VM);
241 if (!PoolAddress) ASSERT(FALSE);
242 HavePoolAddress = TRUE;
243 }
244
245 /* Loop as long as there is still data */
246 while (RemainingSize > 0)
247 {
248 /* Check if this transfer will finish everything off */
249 if (RemainingSize < CurrentSize) CurrentSize = RemainingSize;
250
251 /* Attach to the source address space */
252 KeStackAttachProcess(&SourceProcess->Pcb, &ApcState);
253
254 /* Reset state for this pass */
255 FailedInMoving = FALSE;
256 ASSERT(FailedInProbe == FALSE);
257
258 /* Protect user-mode copy */
259 _SEH2_TRY
260 {
261 /* If this is our first time, probe the buffer */
262 if ((CurrentAddress == SourceAddress) && (PreviousMode != KernelMode))
263 {
264 /* Catch a failure here */
265 FailedInProbe = TRUE;
266
267 /* Do the probe */
268 ProbeForRead(SourceAddress, BufferSize, sizeof(CHAR));
269
270 /* Passed */
271 FailedInProbe = FALSE;
272 }
273
274 /* Do the copy */
275 RtlCopyMemory(PoolAddress, CurrentAddress, CurrentSize);
276
277 /* Now let go of the source and grab to the target process */
278 KeUnstackDetachProcess(&ApcState);
279 KeStackAttachProcess(&TargetProcess->Pcb, &ApcState);
280
281 /* Check if this is our first time through */
282 if ((CurrentAddress == SourceAddress) && (PreviousMode != KernelMode))
283 {
284 /* Catch a failure here */
285 FailedInProbe = TRUE;
286
287 /* Do the probe */
288 ProbeForWrite(TargetAddress, BufferSize, sizeof(CHAR));
289
290 /* Passed */
291 FailedInProbe = FALSE;
292 }
293
294 /* Now do the actual move */
295 FailedInMoving = TRUE;
296 RtlCopyMemory(CurrentTargetAddress, PoolAddress, CurrentSize);
297 }
298 _SEH2_EXCEPT(MiGetExceptionInfo(_SEH2_GetExceptionInformation(), &HaveBadAddress, &BadAddress))
299 {
300 /* Detach from whoever we may be attached to */
301 KeUnstackDetachProcess(&ApcState);
302
303 /* Check if we had allocated pool */
304 if (HavePoolAddress) ExFreePool(PoolAddress);
305
306 /* Check if we failed during the probe */
307 if (FailedInProbe)
308 {
309 /* Exit */
310 _SEH2_YIELD(return _SEH2_GetExceptionCode());
311 }
312
313 /* Otherwise, we failed probably during the move */
314 *ReturnSize = BufferSize - RemainingSize;
315 if (FailedInMoving)
316 {
317 /* Check if we know exactly where we stopped copying */
318 if (HaveBadAddress)
319 {
320 /* Return the exact number of bytes copied */
321 *ReturnSize = BadAddress - (ULONG_PTR)SourceAddress;
322 }
323 }
324
325 /* Return partial copy */
326 _SEH2_YIELD(return STATUS_PARTIAL_COPY);
327 }
328 _SEH2_END;
329
330 /* Detach from target */
331 KeUnstackDetachProcess(&ApcState);
332
333 /* Update location and size */
334 RemainingSize -= CurrentSize;
335 CurrentAddress = (PVOID)((ULONG_PTR)CurrentAddress + CurrentSize);
336 CurrentTargetAddress = (PVOID)((ULONG_PTR)CurrentTargetAddress + CurrentSize);
337 }
338
339 /* Check if we had allocated pool */
340 if (HavePoolAddress) ExFreePool(PoolAddress);
341
342 /* All bytes read */
343 *ReturnSize = BufferSize;
344 return STATUS_SUCCESS;
345 }
346
347 NTSTATUS
348 NTAPI
349 MmCopyVirtualMemory(IN PEPROCESS SourceProcess,
350 IN PVOID SourceAddress,
351 IN PEPROCESS TargetProcess,
352 OUT PVOID TargetAddress,
353 IN SIZE_T BufferSize,
354 IN KPROCESSOR_MODE PreviousMode,
355 OUT PSIZE_T ReturnSize)
356 {
357 NTSTATUS Status;
358 PEPROCESS Process = SourceProcess;
359
360 /* Don't accept zero-sized buffers */
361 if (!BufferSize) return STATUS_SUCCESS;
362
363 /* If we are copying from ourselves, lock the target instead */
364 if (SourceProcess == PsGetCurrentProcess()) Process = TargetProcess;
365
366 /* Acquire rundown protection */
367 if (!ExAcquireRundownProtection(&Process->RundownProtect))
368 {
369 /* Fail */
370 return STATUS_PROCESS_IS_TERMINATING;
371 }
372
373 /* See if we should use the pool copy */
374 if (BufferSize > MI_POOL_COPY_BYTES)
375 {
376 /* Use MDL-copy */
377 Status = MiDoMappedCopy(SourceProcess,
378 SourceAddress,
379 TargetProcess,
380 TargetAddress,
381 BufferSize,
382 PreviousMode,
383 ReturnSize);
384 }
385 else
386 {
387 /* Do pool copy */
388 Status = MiDoPoolCopy(SourceProcess,
389 SourceAddress,
390 TargetProcess,
391 TargetAddress,
392 BufferSize,
393 PreviousMode,
394 ReturnSize);
395 }
396
397 /* Release the lock */
398 ExReleaseRundownProtection(&Process->RundownProtect);
399 return Status;
400 }
401
402 NTSTATUS FASTCALL
403 MiQueryVirtualMemory(IN HANDLE ProcessHandle,
404 IN PVOID Address,
405 IN MEMORY_INFORMATION_CLASS VirtualMemoryInformationClass,
406 OUT PVOID VirtualMemoryInformation,
407 IN SIZE_T Length,
408 OUT PSIZE_T ResultLength)
409 {
410 NTSTATUS Status;
411 PEPROCESS Process;
412 MEMORY_AREA* MemoryArea;
413 PMMSUPPORT AddressSpace;
414
415 Status = ObReferenceObjectByHandle(ProcessHandle,
416 PROCESS_QUERY_INFORMATION,
417 NULL,
418 UserMode,
419 (PVOID*)(&Process),
420 NULL);
421
422 if (!NT_SUCCESS(Status))
423 {
424 DPRINT("NtQueryVirtualMemory() = %x\n",Status);
425 return(Status);
426 }
427
428 AddressSpace = &Process->Vm;
429
430 MmLockAddressSpace(AddressSpace);
431 MemoryArea = MmLocateMemoryAreaByAddress(AddressSpace, Address);
432 switch(VirtualMemoryInformationClass)
433 {
434 case MemoryBasicInformation:
435 {
436 PMEMORY_BASIC_INFORMATION Info =
437 (PMEMORY_BASIC_INFORMATION)VirtualMemoryInformation;
438 if (Length != sizeof(MEMORY_BASIC_INFORMATION))
439 {
440 MmUnlockAddressSpace(AddressSpace);
441 ObDereferenceObject(Process);
442 return(STATUS_INFO_LENGTH_MISMATCH);
443 }
444
445 if (MemoryArea == NULL)
446 {
447 Info->Type = 0;
448 Info->State = MEM_FREE;
449 Info->Protect = PAGE_NOACCESS;
450 Info->AllocationProtect = 0;
451 Info->BaseAddress = (PVOID)PAGE_ROUND_DOWN(Address);
452 Info->AllocationBase = NULL;
453 Info->RegionSize = MmFindGapAtAddress(AddressSpace, Info->BaseAddress);
454 Status = STATUS_SUCCESS;
455 *ResultLength = sizeof(MEMORY_BASIC_INFORMATION);
456 }
457 else
458 {
459 switch(MemoryArea->Type)
460 {
461 case MEMORY_AREA_VIRTUAL_MEMORY:
462 case MEMORY_AREA_PEB_OR_TEB:
463 Status = MmQueryAnonMem(MemoryArea, Address, Info,
464 ResultLength);
465 break;
466
467 case MEMORY_AREA_SECTION_VIEW:
468 Status = MmQuerySectionView(MemoryArea, Address, Info,
469 ResultLength);
470 break;
471
472 case MEMORY_AREA_NO_ACCESS:
473 Info->Type = MEM_PRIVATE;
474 Info->State = MEM_RESERVE;
475 Info->Protect = MemoryArea->Protect;
476 Info->AllocationProtect = MemoryArea->Protect;
477 Info->BaseAddress = MemoryArea->StartingAddress;
478 Info->AllocationBase = MemoryArea->StartingAddress;
479 Info->RegionSize = (ULONG_PTR)MemoryArea->EndingAddress -
480 (ULONG_PTR)MemoryArea->StartingAddress;
481 Status = STATUS_SUCCESS;
482 *ResultLength = sizeof(MEMORY_BASIC_INFORMATION);
483 break;
484
485 case MEMORY_AREA_SHARED_DATA:
486 Info->Type = MEM_PRIVATE;
487 Info->State = MEM_COMMIT;
488 Info->Protect = MemoryArea->Protect;
489 Info->AllocationProtect = MemoryArea->Protect;
490 Info->BaseAddress = MemoryArea->StartingAddress;
491 Info->AllocationBase = MemoryArea->StartingAddress;
492 Info->RegionSize = (ULONG_PTR)MemoryArea->EndingAddress -
493 (ULONG_PTR)MemoryArea->StartingAddress;
494 Status = STATUS_SUCCESS;
495 *ResultLength = sizeof(MEMORY_BASIC_INFORMATION);
496 break;
497
498 case MEMORY_AREA_SYSTEM:
499 Info->Type = 0;
500 Info->State = MEM_COMMIT;
501 Info->Protect = MemoryArea->Protect;
502 Info->AllocationProtect = MemoryArea->Protect;
503 Info->BaseAddress = MemoryArea->StartingAddress;
504 Info->AllocationBase = MemoryArea->StartingAddress;
505 Info->RegionSize = (ULONG_PTR)MemoryArea->EndingAddress -
506 (ULONG_PTR)MemoryArea->StartingAddress;
507 Status = STATUS_SUCCESS;
508 *ResultLength = sizeof(MEMORY_BASIC_INFORMATION);
509 break;
510
511 case MEMORY_AREA_KERNEL_STACK:
512 Info->Type = 0;
513 Info->State = MEM_COMMIT;
514 Info->Protect = MemoryArea->Protect;
515 Info->AllocationProtect = MemoryArea->Protect;
516 Info->BaseAddress = MemoryArea->StartingAddress;
517 Info->AllocationBase = MemoryArea->StartingAddress;
518 Info->RegionSize = (ULONG_PTR)MemoryArea->EndingAddress -
519 (ULONG_PTR)MemoryArea->StartingAddress;
520 Status = STATUS_SUCCESS;
521 *ResultLength = sizeof(MEMORY_BASIC_INFORMATION);
522 break;
523
524 case MEMORY_AREA_PAGED_POOL:
525 Info->Type = 0;
526 Info->State = MEM_COMMIT;
527 Info->Protect = MemoryArea->Protect;
528 Info->AllocationProtect = MemoryArea->Protect;
529 Info->BaseAddress = MemoryArea->StartingAddress;
530 Info->AllocationBase = MemoryArea->StartingAddress;
531 Info->RegionSize = (ULONG_PTR)MemoryArea->EndingAddress -
532 (ULONG_PTR)MemoryArea->StartingAddress;
533 Status = STATUS_SUCCESS;
534 *ResultLength = sizeof(MEMORY_BASIC_INFORMATION);
535 break;
536
537 default:
538 DPRINT1("unhandled memory area type: 0x%x\n", MemoryArea->Type);
539 Status = STATUS_UNSUCCESSFUL;
540 *ResultLength = 0;
541 }
542 }
543 break;
544 }
545
546 default:
547 {
548 DPRINT1("Unsupported or unimplemented class: %lx\n", VirtualMemoryInformationClass);
549 Status = STATUS_INVALID_INFO_CLASS;
550 *ResultLength = 0;
551 break;
552 }
553 }
554
555 MmUnlockAddressSpace(AddressSpace);
556 ObDereferenceObject(Process);
557
558 return Status;
559 }
560
561 NTSTATUS NTAPI
562 MiProtectVirtualMemory(IN PEPROCESS Process,
563 IN OUT PVOID *BaseAddress,
564 IN OUT PSIZE_T NumberOfBytesToProtect,
565 IN ULONG NewAccessProtection,
566 OUT PULONG OldAccessProtection OPTIONAL)
567 {
568 PMEMORY_AREA MemoryArea;
569 PMMSUPPORT AddressSpace;
570 ULONG OldAccessProtection_;
571 NTSTATUS Status;
572
573 *NumberOfBytesToProtect =
574 PAGE_ROUND_UP((ULONG_PTR)(*BaseAddress) + (*NumberOfBytesToProtect)) -
575 PAGE_ROUND_DOWN(*BaseAddress);
576 *BaseAddress = (PVOID)PAGE_ROUND_DOWN(*BaseAddress);
577
578 AddressSpace = &Process->Vm;
579
580 MmLockAddressSpace(AddressSpace);
581 MemoryArea = MmLocateMemoryAreaByAddress(AddressSpace, *BaseAddress);
582 if (MemoryArea == NULL)
583 {
584 MmUnlockAddressSpace(AddressSpace);
585 return STATUS_UNSUCCESSFUL;
586 }
587
588 if (OldAccessProtection == NULL)
589 OldAccessProtection = &OldAccessProtection_;
590
591 if (MemoryArea->Type == MEMORY_AREA_VIRTUAL_MEMORY)
592 {
593 Status = MmProtectAnonMem(AddressSpace, MemoryArea, *BaseAddress,
594 *NumberOfBytesToProtect, NewAccessProtection,
595 OldAccessProtection);
596 }
597 else if (MemoryArea->Type == MEMORY_AREA_SECTION_VIEW)
598 {
599 Status = MmProtectSectionView(AddressSpace, MemoryArea, *BaseAddress,
600 *NumberOfBytesToProtect,
601 NewAccessProtection,
602 OldAccessProtection);
603 }
604 else
605 {
606 /* FIXME: Should we return failure or success in this case? */
607 Status = STATUS_CONFLICTING_ADDRESSES;
608 }
609
610 MmUnlockAddressSpace(AddressSpace);
611
612 return Status;
613 }
614
615 PVOID
616 NTAPI
617 MiMapLockedPagesInUserSpace(IN PMDL Mdl,
618 IN PVOID BaseVa,
619 IN MEMORY_CACHING_TYPE CacheType,
620 IN PVOID BaseAddress)
621 {
622 PVOID Base;
623 PPFN_NUMBER MdlPages;
624 ULONG PageCount;
625 PEPROCESS CurrentProcess;
626 NTSTATUS Status;
627 ULONG Protect;
628 MEMORY_AREA *Result;
629 LARGE_INTEGER BoundaryAddressMultiple;
630
631 /* Calculate the number of pages required. */
632 MdlPages = (PPFN_NUMBER)(Mdl + 1);
633 PageCount = PAGE_ROUND_UP(Mdl->ByteCount + Mdl->ByteOffset) / PAGE_SIZE;
634
635 /* Set default page protection */
636 Protect = PAGE_READWRITE;
637 if (CacheType == MmNonCached) Protect |= PAGE_NOCACHE;
638
639 BoundaryAddressMultiple.QuadPart = 0;
640 Base = BaseAddress;
641
642 CurrentProcess = PsGetCurrentProcess();
643
644 MmLockAddressSpace(&CurrentProcess->Vm);
645 Status = MmCreateMemoryArea(&CurrentProcess->Vm,
646 MEMORY_AREA_MDL_MAPPING,
647 &Base,
648 PageCount * PAGE_SIZE,
649 Protect,
650 &Result,
651 (Base != NULL),
652 0,
653 BoundaryAddressMultiple);
654 MmUnlockAddressSpace(&CurrentProcess->Vm);
655 if (!NT_SUCCESS(Status))
656 {
657 if (Mdl->MdlFlags & MDL_MAPPING_CAN_FAIL)
658 {
659 return NULL;
660 }
661
662 /* Throw exception */
663 ExRaiseStatus(STATUS_ACCESS_VIOLATION);
664 ASSERT(0);
665 }
666
667 /* Set the virtual mappings for the MDL pages. */
668 if (Mdl->MdlFlags & MDL_IO_SPACE)
669 {
670 /* Map the pages */
671 Status = MmCreateVirtualMappingUnsafe(CurrentProcess,
672 Base,
673 Protect,
674 MdlPages,
675 PageCount);
676 }
677 else
678 {
679 /* Map the pages */
680 Status = MmCreateVirtualMapping(CurrentProcess,
681 Base,
682 Protect,
683 MdlPages,
684 PageCount);
685 }
686
687 /* Check if the mapping suceeded */
688 if (!NT_SUCCESS(Status))
689 {
690 /* If it can fail, return NULL */
691 if (Mdl->MdlFlags & MDL_MAPPING_CAN_FAIL) return NULL;
692
693 /* Throw exception */
694 ExRaiseStatus(STATUS_ACCESS_VIOLATION);
695 }
696
697 /* Return the base */
698 Base = (PVOID)((ULONG_PTR)Base + Mdl->ByteOffset);
699 return Base;
700 }
701
702 VOID
703 NTAPI
704 MiUnmapLockedPagesInUserSpace(IN PVOID BaseAddress,
705 IN PMDL Mdl)
706 {
707 PMEMORY_AREA MemoryArea;
708
709 /* Sanity check */
710 ASSERT(Mdl->Process == PsGetCurrentProcess());
711
712 /* Find the memory area */
713 MemoryArea = MmLocateMemoryAreaByAddress(&Mdl->Process->Vm,
714 BaseAddress);
715 ASSERT(MemoryArea);
716
717 /* Free it */
718 MmFreeMemoryArea(&Mdl->Process->Vm,
719 MemoryArea,
720 NULL,
721 NULL);
722 }
723
724 /* PUBLIC FUNCTIONS ***********************************************************/
725
726 /*
727 * @unimplemented
728 */
729 PVOID
730 NTAPI
731 MmGetVirtualForPhysical(IN PHYSICAL_ADDRESS PhysicalAddress)
732 {
733 UNIMPLEMENTED;
734 return 0;
735 }
736
737 /*
738 * @unimplemented
739 */
740 PVOID
741 NTAPI
742 MmSecureVirtualMemory(IN PVOID Address,
743 IN SIZE_T Length,
744 IN ULONG Mode)
745 {
746 UNIMPLEMENTED;
747 return NULL;
748 }
749
750 /*
751 * @unimplemented
752 */
753 VOID
754 NTAPI
755 MmUnsecureVirtualMemory(IN PVOID SecureMem)
756 {
757 UNIMPLEMENTED;
758 }
759
760 /* SYSTEM CALLS ***************************************************************/
761
762 NTSTATUS
763 NTAPI
764 NtReadVirtualMemory(IN HANDLE ProcessHandle,
765 IN PVOID BaseAddress,
766 OUT PVOID Buffer,
767 IN SIZE_T NumberOfBytesToRead,
768 OUT PSIZE_T NumberOfBytesRead OPTIONAL)
769 {
770 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
771 PEPROCESS Process;
772 NTSTATUS Status;
773 SIZE_T BytesRead = 0;
774 PAGED_CODE();
775
776 /* Check if we came from user mode */
777 if (PreviousMode != KernelMode)
778 {
779 /* Validate the read addresses */
780 if ((((ULONG_PTR)BaseAddress + NumberOfBytesToRead) < (ULONG_PTR)BaseAddress) ||
781 (((ULONG_PTR)Buffer + NumberOfBytesToRead) < (ULONG_PTR)Buffer) ||
782 (((ULONG_PTR)BaseAddress + NumberOfBytesToRead) > MmUserProbeAddress) ||
783 (((ULONG_PTR)Buffer + NumberOfBytesToRead) > MmUserProbeAddress))
784 {
785 /* Don't allow to write into kernel space */
786 return STATUS_ACCESS_VIOLATION;
787 }
788
789 /* Enter SEH for probe */
790 _SEH2_TRY
791 {
792 /* Probe the output value */
793 if (NumberOfBytesRead) ProbeForWriteSize_t(NumberOfBytesRead);
794 }
795 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
796 {
797 /* Return the exception code */
798 _SEH2_YIELD(return _SEH2_GetExceptionCode());
799 }
800 _SEH2_END;
801 }
802
803 /* Reference the process */
804 Status = ObReferenceObjectByHandle(ProcessHandle,
805 PROCESS_VM_READ,
806 PsProcessType,
807 PreviousMode,
808 (PVOID*)(&Process),
809 NULL);
810 if (NT_SUCCESS(Status))
811 {
812 /* Do the copy */
813 Status = MmCopyVirtualMemory(Process,
814 BaseAddress,
815 PsGetCurrentProcess(),
816 Buffer,
817 NumberOfBytesToRead,
818 PreviousMode,
819 &BytesRead);
820
821 /* Dereference the process */
822 ObDereferenceObject(Process);
823 }
824
825 /* Check if the caller sent this parameter */
826 if (NumberOfBytesRead)
827 {
828 /* Enter SEH to guard write */
829 _SEH2_TRY
830 {
831 /* Return the number of bytes read */
832 *NumberOfBytesRead = BytesRead;
833 }
834 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
835 {
836 /* Handle exception */
837 Status = _SEH2_GetExceptionCode();
838 }
839 _SEH2_END;
840 }
841
842 /* Return status */
843 return Status;
844 }
845
846 NTSTATUS
847 NTAPI
848 NtWriteVirtualMemory(IN HANDLE ProcessHandle,
849 IN PVOID BaseAddress,
850 IN PVOID Buffer,
851 IN SIZE_T NumberOfBytesToWrite,
852 OUT PSIZE_T NumberOfBytesWritten OPTIONAL)
853 {
854 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
855 PEPROCESS Process;
856 NTSTATUS Status;
857 ULONG BytesWritten = 0;
858 PAGED_CODE();
859
860 /* Check if we came from user mode */
861 if (PreviousMode != KernelMode)
862 {
863 /* Validate the read addresses */
864 if ((((ULONG_PTR)BaseAddress + NumberOfBytesToWrite) < (ULONG_PTR)BaseAddress) ||
865 (((ULONG_PTR)Buffer + NumberOfBytesToWrite) < (ULONG_PTR)Buffer) ||
866 (((ULONG_PTR)BaseAddress + NumberOfBytesToWrite) > MmUserProbeAddress) ||
867 (((ULONG_PTR)Buffer + NumberOfBytesToWrite) > MmUserProbeAddress))
868 {
869 /* Don't allow to write into kernel space */
870 return STATUS_ACCESS_VIOLATION;
871 }
872
873 /* Enter SEH for probe */
874 _SEH2_TRY
875 {
876 /* Probe the output value */
877 if (NumberOfBytesWritten) ProbeForWriteSize_t(NumberOfBytesWritten);
878 }
879 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
880 {
881 /* Return the exception code */
882 _SEH2_YIELD(return _SEH2_GetExceptionCode());
883 }
884 _SEH2_END;
885 }
886
887 /* Reference the process */
888 Status = ObReferenceObjectByHandle(ProcessHandle,
889 PROCESS_VM_WRITE,
890 PsProcessType,
891 PreviousMode,
892 (PVOID*)&Process,
893 NULL);
894 if (NT_SUCCESS(Status))
895 {
896 /* Do the copy */
897 Status = MmCopyVirtualMemory(PsGetCurrentProcess(),
898 Buffer,
899 Process,
900 BaseAddress,
901 NumberOfBytesToWrite,
902 PreviousMode,
903 &BytesWritten);
904
905 /* Dereference the process */
906 ObDereferenceObject(Process);
907 }
908
909 /* Check if the caller sent this parameter */
910 if (NumberOfBytesWritten)
911 {
912 /* Enter SEH to guard write */
913 _SEH2_TRY
914 {
915 /* Return the number of bytes read */
916 *NumberOfBytesWritten = BytesWritten;
917 }
918 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
919 {
920 /* Handle exception */
921 Status = _SEH2_GetExceptionCode();
922 }
923 _SEH2_END;
924 }
925
926 /* Return status */
927 return Status;
928 }
929
930 NTSTATUS
931 NTAPI
932 NtProtectVirtualMemory(IN HANDLE ProcessHandle,
933 IN OUT PVOID *UnsafeBaseAddress,
934 IN OUT SIZE_T *UnsafeNumberOfBytesToProtect,
935 IN ULONG NewAccessProtection,
936 OUT PULONG UnsafeOldAccessProtection)
937 {
938 PEPROCESS Process;
939 ULONG OldAccessProtection;
940 ULONG Protection;
941 PVOID BaseAddress = NULL;
942 SIZE_T NumberOfBytesToProtect = 0;
943 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
944 NTSTATUS Status;
945 PAGED_CODE();
946
947 /* Check for valid protection flags */
948 Protection = NewAccessProtection & ~(PAGE_GUARD|PAGE_NOCACHE);
949 if (Protection != PAGE_NOACCESS &&
950 Protection != PAGE_READONLY &&
951 Protection != PAGE_READWRITE &&
952 Protection != PAGE_WRITECOPY &&
953 Protection != PAGE_EXECUTE &&
954 Protection != PAGE_EXECUTE_READ &&
955 Protection != PAGE_EXECUTE_READWRITE &&
956 Protection != PAGE_EXECUTE_WRITECOPY)
957 {
958 return STATUS_INVALID_PAGE_PROTECTION;
959 }
960
961 /* Check if we came from user mode */
962 if (PreviousMode != KernelMode)
963 {
964 /* Enter SEH for probing */
965 _SEH2_TRY
966 {
967 /* Validate all outputs */
968 ProbeForWritePointer(UnsafeBaseAddress);
969 ProbeForWriteSize_t(UnsafeNumberOfBytesToProtect);
970 ProbeForWriteUlong(UnsafeOldAccessProtection);
971
972 /* Capture them */
973 BaseAddress = *UnsafeBaseAddress;
974 NumberOfBytesToProtect = *UnsafeNumberOfBytesToProtect;
975 }
976 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
977 {
978 /* Return the exception code */
979 _SEH2_YIELD(return _SEH2_GetExceptionCode());
980 }
981 _SEH2_END;
982 }
983 else
984 {
985 /* Capture directly */
986 BaseAddress = *UnsafeBaseAddress;
987 NumberOfBytesToProtect = *UnsafeNumberOfBytesToProtect;
988 }
989
990 /* Catch illegal base address */
991 if (BaseAddress > (PVOID)MmUserProbeAddress) return STATUS_INVALID_PARAMETER_2;
992
993 /* Catch illegal region size */
994 if ((MmUserProbeAddress - (ULONG_PTR)BaseAddress) < NumberOfBytesToProtect)
995 {
996 /* Fail */
997 return STATUS_INVALID_PARAMETER_3;
998 }
999
1000 /* 0 is also illegal */
1001 if (!NumberOfBytesToProtect) return STATUS_INVALID_PARAMETER_3;
1002
1003 /* Get a reference to the process */
1004 Status = ObReferenceObjectByHandle(ProcessHandle,
1005 PROCESS_VM_OPERATION,
1006 PsProcessType,
1007 PreviousMode,
1008 (PVOID*)(&Process),
1009 NULL);
1010 if (!NT_SUCCESS(Status)) return Status;
1011
1012 /* Do the actual work */
1013 Status = MiProtectVirtualMemory(Process,
1014 &BaseAddress,
1015 &NumberOfBytesToProtect,
1016 NewAccessProtection,
1017 &OldAccessProtection);
1018
1019 /* Release reference */
1020 ObDereferenceObject(Process);
1021
1022 /* Enter SEH to return data */
1023 _SEH2_TRY
1024 {
1025 /* Return data to user */
1026 *UnsafeOldAccessProtection = OldAccessProtection;
1027 *UnsafeBaseAddress = BaseAddress;
1028 *UnsafeNumberOfBytesToProtect = NumberOfBytesToProtect;
1029 }
1030 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1031 {
1032 /* Catch exception */
1033 Status = _SEH2_GetExceptionCode();
1034 }
1035 _SEH2_END;
1036
1037 /* Return status */
1038 return Status;
1039 }
1040
1041 NTSTATUS NTAPI
1042 NtQueryVirtualMemory(IN HANDLE ProcessHandle,
1043 IN PVOID Address,
1044 IN MEMORY_INFORMATION_CLASS VirtualMemoryInformationClass,
1045 OUT PVOID VirtualMemoryInformation,
1046 IN SIZE_T Length,
1047 OUT PSIZE_T UnsafeResultLength)
1048 {
1049 NTSTATUS Status;
1050 SIZE_T ResultLength = 0;
1051 KPROCESSOR_MODE PreviousMode;
1052 WCHAR ModuleFileNameBuffer[MAX_PATH] = {0};
1053 UNICODE_STRING ModuleFileName;
1054 PMEMORY_SECTION_NAME SectionName = NULL;
1055 PEPROCESS Process;
1056 union
1057 {
1058 MEMORY_BASIC_INFORMATION BasicInfo;
1059 }
1060 VirtualMemoryInfo;
1061
1062 DPRINT("NtQueryVirtualMemory(ProcessHandle %x, Address %x, "
1063 "VirtualMemoryInformationClass %d, VirtualMemoryInformation %x, "
1064 "Length %lu ResultLength %x)\n",ProcessHandle,Address,
1065 VirtualMemoryInformationClass,VirtualMemoryInformation,
1066 Length,ResultLength);
1067
1068 PreviousMode = ExGetPreviousMode();
1069
1070 if (PreviousMode != KernelMode)
1071 {
1072 _SEH2_TRY
1073 {
1074 ProbeForWrite(VirtualMemoryInformation,
1075 Length,
1076 sizeof(ULONG_PTR));
1077
1078 if (UnsafeResultLength) ProbeForWriteSize_t(UnsafeResultLength);
1079 }
1080 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1081 {
1082 /* Return the exception code */
1083 _SEH2_YIELD(return _SEH2_GetExceptionCode());
1084 }
1085 _SEH2_END;
1086 }
1087
1088 if (Address >= MmSystemRangeStart)
1089 {
1090 DPRINT1("Invalid parameter\n");
1091 return STATUS_INVALID_PARAMETER;
1092 }
1093
1094 /* FIXME: Move this inside MiQueryVirtualMemory */
1095 if (VirtualMemoryInformationClass == MemorySectionName)
1096 {
1097 Status = ObReferenceObjectByHandle(ProcessHandle,
1098 PROCESS_QUERY_INFORMATION,
1099 NULL,
1100 PreviousMode,
1101 (PVOID*)(&Process),
1102 NULL);
1103
1104 if (!NT_SUCCESS(Status))
1105 {
1106 DPRINT("NtQueryVirtualMemory() = %x\n",Status);
1107 return(Status);
1108 }
1109
1110 RtlInitEmptyUnicodeString(&ModuleFileName, ModuleFileNameBuffer, sizeof(ModuleFileNameBuffer));
1111 Status = MmGetFileNameForAddress(Address, &ModuleFileName);
1112
1113 if (NT_SUCCESS(Status))
1114 {
1115 SectionName = VirtualMemoryInformation;
1116 if (PreviousMode != KernelMode)
1117 {
1118 _SEH2_TRY
1119 {
1120 RtlInitUnicodeString(&SectionName->SectionFileName, SectionName->NameBuffer);
1121 SectionName->SectionFileName.MaximumLength = Length;
1122 RtlCopyUnicodeString(&SectionName->SectionFileName, &ModuleFileName);
1123
1124 if (UnsafeResultLength != NULL)
1125 {
1126 *UnsafeResultLength = ModuleFileName.Length;
1127 }
1128 }
1129 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1130 {
1131 Status = _SEH2_GetExceptionCode();
1132 }
1133 _SEH2_END;
1134 }
1135 else
1136 {
1137 RtlInitUnicodeString(&SectionName->SectionFileName, SectionName->NameBuffer);
1138 SectionName->SectionFileName.MaximumLength = Length;
1139 RtlCopyUnicodeString(&SectionName->SectionFileName, &ModuleFileName);
1140
1141 if (UnsafeResultLength != NULL)
1142 {
1143 *UnsafeResultLength = ModuleFileName.Length;
1144 }
1145 }
1146 }
1147 ObDereferenceObject(Process);
1148 return Status;
1149 }
1150 else
1151 {
1152 Status = MiQueryVirtualMemory(ProcessHandle,
1153 Address,
1154 VirtualMemoryInformationClass,
1155 &VirtualMemoryInfo,
1156 Length,
1157 &ResultLength);
1158 }
1159
1160 if (NT_SUCCESS(Status))
1161 {
1162 if (PreviousMode != KernelMode)
1163 {
1164 _SEH2_TRY
1165 {
1166 if (ResultLength > 0)
1167 {
1168 ProbeForWrite(VirtualMemoryInformation,
1169 ResultLength,
1170 1);
1171 RtlCopyMemory(VirtualMemoryInformation,
1172 &VirtualMemoryInfo,
1173 ResultLength);
1174 }
1175 if (UnsafeResultLength != NULL)
1176 {
1177 *UnsafeResultLength = ResultLength;
1178 }
1179 }
1180 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1181 {
1182 Status = _SEH2_GetExceptionCode();
1183 }
1184 _SEH2_END;
1185 }
1186 else
1187 {
1188 if (ResultLength > 0)
1189 {
1190 RtlCopyMemory(VirtualMemoryInformation,
1191 &VirtualMemoryInfo,
1192 ResultLength);
1193 }
1194
1195 if (UnsafeResultLength != NULL)
1196 {
1197 *UnsafeResultLength = ResultLength;
1198 }
1199 }
1200 }
1201
1202 return(Status);
1203 }
1204
1205 NTSTATUS
1206 NTAPI
1207 NtLockVirtualMemory(IN HANDLE ProcessHandle,
1208 IN PVOID BaseAddress,
1209 IN SIZE_T NumberOfBytesToLock,
1210 OUT PSIZE_T NumberOfBytesLocked OPTIONAL)
1211 {
1212 UNIMPLEMENTED;
1213 if (NumberOfBytesLocked) *NumberOfBytesLocked = 0;
1214 return STATUS_SUCCESS;
1215 }
1216
1217 NTSTATUS
1218 NTAPI
1219 NtUnlockVirtualMemory(IN HANDLE ProcessHandle,
1220 IN PVOID BaseAddress,
1221 IN SIZE_T NumberOfBytesToUnlock,
1222 OUT PSIZE_T NumberOfBytesUnlocked OPTIONAL)
1223 {
1224 UNIMPLEMENTED;
1225 if (NumberOfBytesUnlocked) *NumberOfBytesUnlocked = 0;
1226 return STATUS_SUCCESS;
1227 }
1228
1229 NTSTATUS
1230 NTAPI
1231 NtFlushVirtualMemory(IN HANDLE ProcessHandle,
1232 IN OUT PVOID *BaseAddress,
1233 IN OUT PSIZE_T NumberOfBytesToFlush,
1234 OUT PIO_STATUS_BLOCK IoStatusBlock)
1235 {
1236 UNIMPLEMENTED;
1237 return STATUS_SUCCESS;
1238 }
1239
1240 /*
1241 * @unimplemented
1242 */
1243 NTSTATUS
1244 NTAPI
1245 NtGetWriteWatch(IN HANDLE ProcessHandle,
1246 IN ULONG Flags,
1247 IN PVOID BaseAddress,
1248 IN ULONG RegionSize,
1249 IN PVOID *UserAddressArray,
1250 OUT PULONG EntriesInUserAddressArray,
1251 OUT PULONG Granularity)
1252 {
1253 if (!EntriesInUserAddressArray || !Granularity)
1254 {
1255 return STATUS_ACCESS_VIOLATION;
1256 }
1257
1258 if (!*EntriesInUserAddressArray || !RegionSize)
1259 {
1260 return STATUS_INVALID_PARAMETER;
1261 }
1262
1263 if (!UserAddressArray)
1264 {
1265 return STATUS_ACCESS_VIOLATION;
1266 }
1267
1268 /* HACK: Set granularity to PAGE_SIZE */
1269 *Granularity = PAGE_SIZE;
1270
1271 UNIMPLEMENTED;
1272 return STATUS_NOT_IMPLEMENTED;
1273 }
1274
1275 /*
1276 * @unimplemented
1277 */
1278 NTSTATUS
1279 NTAPI
1280 NtResetWriteWatch(IN HANDLE ProcessHandle,
1281 IN PVOID BaseAddress,
1282 IN ULONG RegionSize)
1283 {
1284 if (!RegionSize)
1285 {
1286 return STATUS_INVALID_PARAMETER;
1287 }
1288
1289 UNIMPLEMENTED;
1290 return STATUS_NOT_IMPLEMENTED;
1291 }
1292
1293 /* EOF */