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