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
9 /* INCLUDES *******************************************************************/
15 #line 15 "ARMĀ³::VIRTUAL"
16 #define MODULE_INVOLVED_IN_ARM3
17 #include "../ARM3/miarm.h"
19 #define MI_MAPPED_COPY_PAGES 14
20 #define MI_POOL_COPY_BYTES 512
21 #define MI_MAX_TRANSFER_SIZE 64 * 1024
24 MiProtectVirtualMemory(IN PEPROCESS Process
,
25 IN OUT PVOID
*BaseAddress
,
26 IN OUT PSIZE_T NumberOfBytesToProtect
,
27 IN ULONG NewAccessProtection
,
28 OUT PULONG OldAccessProtection OPTIONAL
);
30 /* PRIVATE FUNCTIONS **********************************************************/
34 MiDeleteSystemPageableVm(IN PMMPTE PointerPte
,
35 IN PFN_NUMBER PageCount
,
37 OUT PPFN_NUMBER ValidPages
)
39 PFN_NUMBER ActualPages
= 0;
40 PETHREAD CurrentThread
;
42 PFN_NUMBER PageFrameIndex
, PageTableIndex
;
43 KIRQL OldIrql
, LockIrql
;
44 ASSERT(KeGetCurrentIrql() <= APC_LEVEL
);
47 * Now we must raise to APC_LEVEL and mark the thread as owner
48 * We don't actually implement a working set pushlock, so this is only
49 * for internal consistency (and blocking APCs)
51 KeRaiseIrql(APC_LEVEL
, &LockIrql
);
52 CurrentThread
= PsGetCurrentThread();
53 KeEnterGuardedRegion();
54 ASSERT((CurrentThread
->OwnsSystemWorkingSetExclusive
== 0) &&
55 (CurrentThread
->OwnsSystemWorkingSetShared
== 0));
56 CurrentThread
->OwnsSystemWorkingSetExclusive
= 1;
61 /* Make sure there's some data about the page */
62 if (PointerPte
->u
.Long
)
64 /* As always, only handle current ARM3 scenarios */
65 ASSERT(PointerPte
->u
.Soft
.Prototype
== 0);
66 ASSERT(PointerPte
->u
.Soft
.Transition
== 0);
68 /* Normally this is one possibility -- freeing a valid page */
69 if (PointerPte
->u
.Hard
.Valid
)
71 /* Get the page PFN */
72 PageFrameIndex
= PFN_FROM_PTE(PointerPte
);
73 Pfn1
= MiGetPfnEntry(PageFrameIndex
);
75 /* Should not have any working set data yet */
76 ASSERT(Pfn1
->u1
.WsIndex
== 0);
78 /* Actual valid, legitimate, pages */
79 if (ValidPages
) *ValidPages
++;
81 /* Get the page table entry */
82 PageTableIndex
= Pfn1
->u4
.PteFrame
;
83 Pfn2
= MiGetPfnEntry(PageTableIndex
);
85 /* Lock the PFN database */
86 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
88 /* Delete it the page */
89 MI_SET_PFN_DELETED(Pfn1
);
90 MiDecrementShareCount(Pfn1
, PageFrameIndex
);
92 /* Decrement the page table too */
93 #if 0 // ARM3: Dont't trust this yet
94 MiDecrementShareCount(Pfn2
, PageTableIndex
);
97 /* Release the PFN database */
98 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
100 /* Destroy the PTE */
101 PointerPte
->u
.Long
= 0;
104 /* Actual legitimate pages */
110 * The only other ARM3 possibility is a demand zero page, which would
111 * mean freeing some of the paged pool pages that haven't even been
112 * touched yet, as part of a larger allocation.
114 * Right now, we shouldn't expect any page file information in the PTE
116 ASSERT(PointerPte
->u
.Soft
.PageFileHigh
== 0);
118 /* Destroy the PTE */
119 PointerPte
->u
.Long
= 0;
128 ASSERT(KeAreAllApcsDisabled() == TRUE
);
129 CurrentThread
->OwnsSystemWorkingSetExclusive
= 0;
130 KeLeaveGuardedRegion();
131 KeLowerIrql(LockIrql
);
133 /* Flush the entire TLB */
134 KeFlushEntireTb(TRUE
, TRUE
);
141 MiGetExceptionInfo(IN PEXCEPTION_POINTERS ExceptionInfo
,
142 OUT PBOOLEAN HaveBadAddress
,
143 OUT PULONG_PTR BadAddress
)
145 PEXCEPTION_RECORD ExceptionRecord
;
151 *HaveBadAddress
= FALSE
;
154 // Get the exception record
156 ExceptionRecord
= ExceptionInfo
->ExceptionRecord
;
159 // Look at the exception code
161 if ((ExceptionRecord
->ExceptionCode
== STATUS_ACCESS_VIOLATION
) ||
162 (ExceptionRecord
->ExceptionCode
== STATUS_GUARD_PAGE_VIOLATION
) ||
163 (ExceptionRecord
->ExceptionCode
== STATUS_IN_PAGE_ERROR
))
166 // We can tell the address if we have more than one parameter
168 if (ExceptionRecord
->NumberParameters
> 1)
171 // Return the address
173 *HaveBadAddress
= TRUE
;
174 *BadAddress
= ExceptionRecord
->ExceptionInformation
[1];
179 // Continue executing the next handler
181 return EXCEPTION_EXECUTE_HANDLER
;
186 MiDoMappedCopy(IN PEPROCESS SourceProcess
,
187 IN PVOID SourceAddress
,
188 IN PEPROCESS TargetProcess
,
189 OUT PVOID TargetAddress
,
190 IN SIZE_T BufferSize
,
191 IN KPROCESSOR_MODE PreviousMode
,
192 OUT PSIZE_T ReturnSize
)
194 PFN_NUMBER MdlBuffer
[(sizeof(MDL
) / sizeof(PFN_NUMBER
)) + MI_MAPPED_COPY_PAGES
+ 1];
195 PMDL Mdl
= (PMDL
)MdlBuffer
;
196 SIZE_T TotalSize
, CurrentSize
, RemainingSize
;
197 volatile BOOLEAN FailedInProbe
= FALSE
, FailedInMapping
= FALSE
, FailedInMoving
;
198 volatile BOOLEAN PagesLocked
;
199 PVOID CurrentAddress
= SourceAddress
, CurrentTargetAddress
= TargetAddress
;
200 volatile PVOID MdlAddress
;
202 BOOLEAN HaveBadAddress
;
203 ULONG_PTR BadAddress
;
204 NTSTATUS Status
= STATUS_SUCCESS
;
208 // Calculate the maximum amount of data to move
210 TotalSize
= MI_MAPPED_COPY_PAGES
* PAGE_SIZE
;
211 if (BufferSize
<= TotalSize
) TotalSize
= BufferSize
;
212 CurrentSize
= TotalSize
;
213 RemainingSize
= BufferSize
;
216 // Loop as long as there is still data
218 while (RemainingSize
> 0)
221 // Check if this transfer will finish everything off
223 if (RemainingSize
< CurrentSize
) CurrentSize
= RemainingSize
;
226 // Attach to the source address space
228 KeStackAttachProcess(&SourceProcess
->Pcb
, &ApcState
);
231 // Reset state for this pass
235 FailedInMoving
= FALSE
;
236 ASSERT(FailedInProbe
== FALSE
);
239 // Protect user-mode copy
244 // If this is our first time, probe the buffer
246 if ((CurrentAddress
== SourceAddress
) && (PreviousMode
!= KernelMode
))
249 // Catch a failure here
251 FailedInProbe
= TRUE
;
256 ProbeForRead(SourceAddress
, BufferSize
, sizeof(CHAR
));
261 FailedInProbe
= FALSE
;
265 // Initialize and probe and lock the MDL
267 MmInitializeMdl(Mdl
, CurrentAddress
, CurrentSize
);
268 MmProbeAndLockPages(Mdl
, PreviousMode
, IoReadAccess
);
274 MdlAddress
= MmMapLockedPagesSpecifyCache(Mdl
,
283 // Use our SEH handler to pick this up
285 FailedInMapping
= TRUE
;
286 ExRaiseStatus(STATUS_INSUFFICIENT_RESOURCES
);
290 // Now let go of the source and grab to the target process
292 KeUnstackDetachProcess(&ApcState
);
293 KeStackAttachProcess(&TargetProcess
->Pcb
, &ApcState
);
296 // Check if this is our first time through
298 if ((CurrentAddress
== SourceAddress
) && (PreviousMode
!= KernelMode
))
301 // Catch a failure here
303 FailedInProbe
= TRUE
;
308 ProbeForWrite(TargetAddress
, BufferSize
, sizeof(CHAR
));
313 FailedInProbe
= FALSE
;
317 // Now do the actual move
319 FailedInMoving
= TRUE
;
320 RtlCopyMemory(CurrentTargetAddress
, MdlAddress
, CurrentSize
);
322 _SEH2_EXCEPT(MiGetExceptionInfo(_SEH2_GetExceptionInformation(),
327 // Detach from whoever we may be attached to
329 KeUnstackDetachProcess(&ApcState
);
332 // Check if we had mapped the pages
334 if (MdlAddress
) MmUnmapLockedPages(MdlAddress
, Mdl
);
337 // Check if we had locked the pages
339 if (PagesLocked
) MmUnlockPages(Mdl
);
342 // Check if we hit working set quota
344 if (_SEH2_GetExceptionCode() == STATUS_WORKING_SET_QUOTA
)
349 return STATUS_WORKING_SET_QUOTA
;
353 // Check if we failed during the probe or mapping
355 if ((FailedInProbe
) || (FailedInMapping
))
360 Status
= _SEH2_GetExceptionCode();
361 _SEH2_YIELD(return Status
);
365 // Otherwise, we failed probably during the move
367 *ReturnSize
= BufferSize
- RemainingSize
;
371 // Check if we know exactly where we stopped copying
376 // Return the exact number of bytes copied
378 *ReturnSize
= BadAddress
- (ULONG_PTR
)SourceAddress
;
383 // Return partial copy
385 Status
= STATUS_PARTIAL_COPY
;
390 // Check for SEH status
392 if (Status
!= STATUS_SUCCESS
) return Status
;
395 // Detach from target
397 KeUnstackDetachProcess(&ApcState
);
402 MmUnmapLockedPages(MdlAddress
, Mdl
);
406 // Update location and size
408 RemainingSize
-= CurrentSize
;
409 CurrentAddress
= (PVOID
)((ULONG_PTR
)CurrentAddress
+ CurrentSize
);
410 CurrentTargetAddress
= (PVOID
)((ULONG_PTR
)CurrentTargetAddress
+ CurrentSize
);
416 *ReturnSize
= BufferSize
;
417 return STATUS_SUCCESS
;
422 MiDoPoolCopy(IN PEPROCESS SourceProcess
,
423 IN PVOID SourceAddress
,
424 IN PEPROCESS TargetProcess
,
425 OUT PVOID TargetAddress
,
426 IN SIZE_T BufferSize
,
427 IN KPROCESSOR_MODE PreviousMode
,
428 OUT PSIZE_T ReturnSize
)
430 UCHAR StackBuffer
[MI_POOL_COPY_BYTES
];
431 SIZE_T TotalSize
, CurrentSize
, RemainingSize
;
432 volatile BOOLEAN FailedInProbe
= FALSE
, FailedInMoving
, HavePoolAddress
= FALSE
;
433 PVOID CurrentAddress
= SourceAddress
, CurrentTargetAddress
= TargetAddress
;
436 BOOLEAN HaveBadAddress
;
437 ULONG_PTR BadAddress
;
438 NTSTATUS Status
= STATUS_SUCCESS
;
442 // Calculate the maximum amount of data to move
444 TotalSize
= MI_MAX_TRANSFER_SIZE
;
445 if (BufferSize
<= MI_MAX_TRANSFER_SIZE
) TotalSize
= BufferSize
;
446 CurrentSize
= TotalSize
;
447 RemainingSize
= BufferSize
;
450 // Check if we can use the stack
452 if (BufferSize
<= MI_POOL_COPY_BYTES
)
457 PoolAddress
= (PVOID
)StackBuffer
;
464 PoolAddress
= ExAllocatePoolWithTag(NonPagedPool
, TotalSize
, 'VmRw');
465 if (!PoolAddress
) ASSERT(FALSE
);
466 HavePoolAddress
= TRUE
;
470 // Loop as long as there is still data
472 while (RemainingSize
> 0)
475 // Check if this transfer will finish everything off
477 if (RemainingSize
< CurrentSize
) CurrentSize
= RemainingSize
;
480 // Attach to the source address space
482 KeStackAttachProcess(&SourceProcess
->Pcb
, &ApcState
);
485 // Reset state for this pass
487 FailedInMoving
= FALSE
;
488 ASSERT(FailedInProbe
== FALSE
);
491 // Protect user-mode copy
496 // If this is our first time, probe the buffer
498 if ((CurrentAddress
== SourceAddress
) && (PreviousMode
!= KernelMode
))
501 // Catch a failure here
503 FailedInProbe
= TRUE
;
508 ProbeForRead(SourceAddress
, BufferSize
, sizeof(CHAR
));
513 FailedInProbe
= FALSE
;
519 RtlCopyMemory(PoolAddress
, CurrentAddress
, CurrentSize
);
522 // Now let go of the source and grab to the target process
524 KeUnstackDetachProcess(&ApcState
);
525 KeStackAttachProcess(&TargetProcess
->Pcb
, &ApcState
);
528 // Check if this is our first time through
530 if ((CurrentAddress
== SourceAddress
) && (PreviousMode
!= KernelMode
))
533 // Catch a failure here
535 FailedInProbe
= TRUE
;
540 ProbeForWrite(TargetAddress
, BufferSize
, sizeof(CHAR
));
545 FailedInProbe
= FALSE
;
549 // Now do the actual move
551 FailedInMoving
= TRUE
;
552 RtlCopyMemory(CurrentTargetAddress
, PoolAddress
, CurrentSize
);
554 _SEH2_EXCEPT(MiGetExceptionInfo(_SEH2_GetExceptionInformation(),
559 // Detach from whoever we may be attached to
561 KeUnstackDetachProcess(&ApcState
);
564 // Check if we had allocated pool
566 if (HavePoolAddress
) ExFreePool(PoolAddress
);
569 // Check if we failed during the probe
576 Status
= _SEH2_GetExceptionCode();
577 _SEH2_YIELD(return Status
);
581 // Otherwise, we failed, probably during the move
583 *ReturnSize
= BufferSize
- RemainingSize
;
587 // Check if we know exactly where we stopped copying
592 // Return the exact number of bytes copied
594 *ReturnSize
= BadAddress
- (ULONG_PTR
)SourceAddress
;
599 // Return partial copy
601 Status
= STATUS_PARTIAL_COPY
;
606 // Check for SEH status
608 if (Status
!= STATUS_SUCCESS
) return Status
;
611 // Detach from target
613 KeUnstackDetachProcess(&ApcState
);
616 // Update location and size
618 RemainingSize
-= CurrentSize
;
619 CurrentAddress
= (PVOID
)((ULONG_PTR
)CurrentAddress
+ CurrentSize
);
620 CurrentTargetAddress
= (PVOID
)((ULONG_PTR
)CurrentTargetAddress
+
625 // Check if we had allocated pool
627 if (HavePoolAddress
) ExFreePool(PoolAddress
);
632 *ReturnSize
= BufferSize
;
633 return STATUS_SUCCESS
;
638 MmCopyVirtualMemory(IN PEPROCESS SourceProcess
,
639 IN PVOID SourceAddress
,
640 IN PEPROCESS TargetProcess
,
641 OUT PVOID TargetAddress
,
642 IN SIZE_T BufferSize
,
643 IN KPROCESSOR_MODE PreviousMode
,
644 OUT PSIZE_T ReturnSize
)
647 PEPROCESS Process
= SourceProcess
;
650 // Don't accept zero-sized buffers
652 if (!BufferSize
) return STATUS_SUCCESS
;
655 // If we are copying from ourselves, lock the target instead
657 if (SourceProcess
== PsGetCurrentProcess()) Process
= TargetProcess
;
660 // Acquire rundown protection
662 if (!ExAcquireRundownProtection(&Process
->RundownProtect
))
667 return STATUS_PROCESS_IS_TERMINATING
;
671 // See if we should use the pool copy
673 if (BufferSize
> MI_POOL_COPY_BYTES
)
678 Status
= MiDoMappedCopy(SourceProcess
,
691 Status
= MiDoPoolCopy(SourceProcess
,
703 ExReleaseRundownProtection(&Process
->RundownProtect
);
709 MmFlushVirtualMemory(IN PEPROCESS Process
,
710 IN OUT PVOID
*BaseAddress
,
711 IN OUT PSIZE_T RegionSize
,
712 OUT PIO_STATUS_BLOCK IoStatusBlock
)
720 return STATUS_SUCCESS
;
723 /* PUBLIC FUNCTIONS ***********************************************************/
730 MmGetVirtualForPhysical(IN PHYSICAL_ADDRESS PhysicalAddress
)
741 MmSecureVirtualMemory(IN PVOID Address
,
745 static BOOLEAN Warn
; if (!Warn
++) UNIMPLEMENTED
;
754 MmUnsecureVirtualMemory(IN PVOID SecureMem
)
756 static BOOLEAN Warn
; if (!Warn
++) UNIMPLEMENTED
;
759 /* SYSTEM CALLS ***************************************************************/
763 NtReadVirtualMemory(IN HANDLE ProcessHandle
,
764 IN PVOID BaseAddress
,
766 IN SIZE_T NumberOfBytesToRead
,
767 OUT PSIZE_T NumberOfBytesRead OPTIONAL
)
769 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
771 NTSTATUS Status
= STATUS_SUCCESS
;
772 SIZE_T BytesRead
= 0;
776 // Check if we came from user mode
778 if (PreviousMode
!= KernelMode
)
781 // Validate the read addresses
783 if ((((ULONG_PTR
)BaseAddress
+ NumberOfBytesToRead
) < (ULONG_PTR
)BaseAddress
) ||
784 (((ULONG_PTR
)Buffer
+ NumberOfBytesToRead
) < (ULONG_PTR
)Buffer
) ||
785 (((ULONG_PTR
)BaseAddress
+ NumberOfBytesToRead
) > MmUserProbeAddress
) ||
786 (((ULONG_PTR
)Buffer
+ NumberOfBytesToRead
) > MmUserProbeAddress
))
789 // Don't allow to write into kernel space
791 return STATUS_ACCESS_VIOLATION
;
795 // Enter SEH for probe
800 // Probe the output value
802 if (NumberOfBytesRead
) ProbeForWriteSize_t(NumberOfBytesRead
);
804 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
807 // Get exception code
809 _SEH2_YIELD(return _SEH2_GetExceptionCode());
815 // Don't do zero-byte transfers
817 if (NumberOfBytesToRead
)
820 // Reference the process
822 Status
= ObReferenceObjectByHandle(ProcessHandle
,
828 if (NT_SUCCESS(Status
))
833 Status
= MmCopyVirtualMemory(Process
,
835 PsGetCurrentProcess(),
842 // Dereference the process
844 ObDereferenceObject(Process
);
849 // Check if the caller sent this parameter
851 if (NumberOfBytesRead
)
854 // Enter SEH to guard write
859 // Return the number of bytes read
861 *NumberOfBytesRead
= BytesRead
;
863 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
877 NtWriteVirtualMemory(IN HANDLE ProcessHandle
,
878 IN PVOID BaseAddress
,
880 IN SIZE_T NumberOfBytesToWrite
,
881 OUT PSIZE_T NumberOfBytesWritten OPTIONAL
)
883 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
885 NTSTATUS Status
= STATUS_SUCCESS
;
886 SIZE_T BytesWritten
= 0;
890 // Check if we came from user mode
892 if (PreviousMode
!= KernelMode
)
895 // Validate the read addresses
897 if ((((ULONG_PTR
)BaseAddress
+ NumberOfBytesToWrite
) < (ULONG_PTR
)BaseAddress
) ||
898 (((ULONG_PTR
)Buffer
+ NumberOfBytesToWrite
) < (ULONG_PTR
)Buffer
) ||
899 (((ULONG_PTR
)BaseAddress
+ NumberOfBytesToWrite
) > MmUserProbeAddress
) ||
900 (((ULONG_PTR
)Buffer
+ NumberOfBytesToWrite
) > MmUserProbeAddress
))
903 // Don't allow to write into kernel space
905 return STATUS_ACCESS_VIOLATION
;
909 // Enter SEH for probe
914 // Probe the output value
916 if (NumberOfBytesWritten
) ProbeForWriteSize_t(NumberOfBytesWritten
);
918 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
921 // Get exception code
923 _SEH2_YIELD(return _SEH2_GetExceptionCode());
929 // Don't do zero-byte transfers
931 if (NumberOfBytesToWrite
)
934 // Reference the process
936 Status
= ObReferenceObjectByHandle(ProcessHandle
,
942 if (NT_SUCCESS(Status
))
947 Status
= MmCopyVirtualMemory(PsGetCurrentProcess(),
951 NumberOfBytesToWrite
,
956 // Dereference the process
958 ObDereferenceObject(Process
);
963 // Check if the caller sent this parameter
965 if (NumberOfBytesWritten
)
968 // Enter SEH to guard write
973 // Return the number of bytes written
975 *NumberOfBytesWritten
= BytesWritten
;
977 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
991 NtProtectVirtualMemory(IN HANDLE ProcessHandle
,
992 IN OUT PVOID
*UnsafeBaseAddress
,
993 IN OUT SIZE_T
*UnsafeNumberOfBytesToProtect
,
994 IN ULONG NewAccessProtection
,
995 OUT PULONG UnsafeOldAccessProtection
)
998 ULONG OldAccessProtection
;
1000 PEPROCESS CurrentProcess
= PsGetCurrentProcess();
1001 PVOID BaseAddress
= NULL
;
1002 SIZE_T NumberOfBytesToProtect
= 0;
1003 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
1005 BOOLEAN Attached
= FALSE
;
1006 KAPC_STATE ApcState
;
1010 // Check for valid protection flags
1012 Protection
= NewAccessProtection
& ~(PAGE_GUARD
|PAGE_NOCACHE
);
1013 if (Protection
!= PAGE_NOACCESS
&&
1014 Protection
!= PAGE_READONLY
&&
1015 Protection
!= PAGE_READWRITE
&&
1016 Protection
!= PAGE_WRITECOPY
&&
1017 Protection
!= PAGE_EXECUTE
&&
1018 Protection
!= PAGE_EXECUTE_READ
&&
1019 Protection
!= PAGE_EXECUTE_READWRITE
&&
1020 Protection
!= PAGE_EXECUTE_WRITECOPY
)
1025 return STATUS_INVALID_PAGE_PROTECTION
;
1029 // Check if we came from user mode
1031 if (PreviousMode
!= KernelMode
)
1034 // Enter SEH for probing
1039 // Validate all outputs
1041 ProbeForWritePointer(UnsafeBaseAddress
);
1042 ProbeForWriteSize_t(UnsafeNumberOfBytesToProtect
);
1043 ProbeForWriteUlong(UnsafeOldAccessProtection
);
1048 BaseAddress
= *UnsafeBaseAddress
;
1049 NumberOfBytesToProtect
= *UnsafeNumberOfBytesToProtect
;
1051 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1054 // Get exception code
1056 _SEH2_YIELD(return _SEH2_GetExceptionCode());
1065 BaseAddress
= *UnsafeBaseAddress
;
1066 NumberOfBytesToProtect
= *UnsafeNumberOfBytesToProtect
;
1070 // Catch illegal base address
1072 if (BaseAddress
> MM_HIGHEST_USER_ADDRESS
) return STATUS_INVALID_PARAMETER_2
;
1075 // Catch illegal region size
1077 if ((MmUserProbeAddress
- (ULONG_PTR
)BaseAddress
) < NumberOfBytesToProtect
)
1082 return STATUS_INVALID_PARAMETER_3
;
1086 // 0 is also illegal
1088 if (!NumberOfBytesToProtect
) return STATUS_INVALID_PARAMETER_3
;
1091 // Get a reference to the process
1093 Status
= ObReferenceObjectByHandle(ProcessHandle
,
1094 PROCESS_VM_OPERATION
,
1099 if (!NT_SUCCESS(Status
)) return Status
;
1102 // Check if we should attach
1104 if (CurrentProcess
!= Process
)
1109 KeStackAttachProcess(&Process
->Pcb
, &ApcState
);
1114 // Do the actual work
1116 Status
= MiProtectVirtualMemory(Process
,
1118 &NumberOfBytesToProtect
,
1119 NewAccessProtection
,
1120 &OldAccessProtection
);
1125 if (Attached
) KeUnstackDetachProcess(&ApcState
);
1128 // Release reference
1130 ObDereferenceObject(Process
);
1133 // Enter SEH to return data
1138 // Return data to user
1140 *UnsafeOldAccessProtection
= OldAccessProtection
;
1141 *UnsafeBaseAddress
= BaseAddress
;
1142 *UnsafeNumberOfBytesToProtect
= NumberOfBytesToProtect
;
1144 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1157 NtLockVirtualMemory(IN HANDLE ProcessHandle
,
1158 IN OUT PVOID
*BaseAddress
,
1159 IN OUT PSIZE_T NumberOfBytesToLock
,
1163 PEPROCESS CurrentProcess
= PsGetCurrentProcess();
1165 BOOLEAN Attached
= FALSE
;
1166 KAPC_STATE ApcState
;
1167 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
1168 PVOID CapturedBaseAddress
;
1169 SIZE_T CapturedBytesToLock
;
1175 if ((MapType
& ~(MAP_PROCESS
| MAP_SYSTEM
)))
1178 // Invalid set of flags
1180 return STATUS_INVALID_PARAMETER
;
1184 // At least one flag must be specified
1186 if (!(MapType
& (MAP_PROCESS
| MAP_SYSTEM
)))
1191 return STATUS_INVALID_PARAMETER
;
1195 // Enter SEH for probing
1200 // Validate output data
1202 ProbeForWritePointer(BaseAddress
);
1203 ProbeForWriteSize_t(NumberOfBytesToLock
);
1208 CapturedBaseAddress
= *BaseAddress
;
1209 CapturedBytesToLock
= *NumberOfBytesToLock
;
1211 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1214 // Get exception code
1216 _SEH2_YIELD(return _SEH2_GetExceptionCode());
1221 // Catch illegal base address
1223 if (CapturedBaseAddress
> MM_HIGHEST_USER_ADDRESS
) return STATUS_INVALID_PARAMETER
;
1226 // Catch illegal region size
1228 if ((MmUserProbeAddress
- (ULONG_PTR
)CapturedBaseAddress
) < CapturedBytesToLock
)
1233 return STATUS_INVALID_PARAMETER
;
1237 // 0 is also illegal
1239 if (!CapturedBytesToLock
) return STATUS_INVALID_PARAMETER
;
1242 // Get a reference to the process
1244 Status
= ObReferenceObjectByHandle(ProcessHandle
,
1245 PROCESS_VM_OPERATION
,
1250 if (!NT_SUCCESS(Status
)) return Status
;
1253 // Check if this is is system-mapped
1255 if (MapType
& MAP_SYSTEM
)
1258 // Check for required privilege
1260 if (!SeSinglePrivilegeCheck(SeLockMemoryPrivilege
, PreviousMode
))
1263 // Fail: Don't have it
1265 ObDereferenceObject(Process
);
1266 return STATUS_PRIVILEGE_NOT_HELD
;
1271 // Check if we should attach
1273 if (CurrentProcess
!= Process
)
1278 KeStackAttachProcess(&Process
->Pcb
, &ApcState
);
1290 if (Attached
) KeUnstackDetachProcess(&ApcState
);
1293 // Release reference
1295 ObDereferenceObject(Process
);
1298 // Enter SEH to return data
1303 // Return data to user
1305 *BaseAddress
= CapturedBaseAddress
;
1306 *NumberOfBytesToLock
= 0;
1308 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1311 // Get exception code
1313 _SEH2_YIELD(return _SEH2_GetExceptionCode());
1320 return STATUS_SUCCESS
;
1325 NtUnlockVirtualMemory(IN HANDLE ProcessHandle
,
1326 IN OUT PVOID
*BaseAddress
,
1327 IN OUT PSIZE_T NumberOfBytesToUnlock
,
1331 PEPROCESS CurrentProcess
= PsGetCurrentProcess();
1333 BOOLEAN Attached
= FALSE
;
1334 KAPC_STATE ApcState
;
1335 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
1336 PVOID CapturedBaseAddress
;
1337 SIZE_T CapturedBytesToUnlock
;
1343 if ((MapType
& ~(MAP_PROCESS
| MAP_SYSTEM
)))
1346 // Invalid set of flags
1348 return STATUS_INVALID_PARAMETER
;
1352 // At least one flag must be specified
1354 if (!(MapType
& (MAP_PROCESS
| MAP_SYSTEM
)))
1359 return STATUS_INVALID_PARAMETER
;
1363 // Enter SEH for probing
1368 // Validate output data
1370 ProbeForWritePointer(BaseAddress
);
1371 ProbeForWriteSize_t(NumberOfBytesToUnlock
);
1376 CapturedBaseAddress
= *BaseAddress
;
1377 CapturedBytesToUnlock
= *NumberOfBytesToUnlock
;
1379 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1382 // Get exception code
1384 _SEH2_YIELD(return _SEH2_GetExceptionCode());
1389 // Catch illegal base address
1391 if (CapturedBaseAddress
> MM_HIGHEST_USER_ADDRESS
) return STATUS_INVALID_PARAMETER
;
1394 // Catch illegal region size
1396 if ((MmUserProbeAddress
- (ULONG_PTR
)CapturedBaseAddress
) < CapturedBytesToUnlock
)
1401 return STATUS_INVALID_PARAMETER
;
1405 // 0 is also illegal
1407 if (!CapturedBytesToUnlock
) return STATUS_INVALID_PARAMETER
;
1410 // Get a reference to the process
1412 Status
= ObReferenceObjectByHandle(ProcessHandle
,
1413 PROCESS_VM_OPERATION
,
1418 if (!NT_SUCCESS(Status
)) return Status
;
1421 // Check if this is is system-mapped
1423 if (MapType
& MAP_SYSTEM
)
1426 // Check for required privilege
1428 if (!SeSinglePrivilegeCheck(SeLockMemoryPrivilege
, PreviousMode
))
1431 // Fail: Don't have it
1433 ObDereferenceObject(Process
);
1434 return STATUS_PRIVILEGE_NOT_HELD
;
1439 // Check if we should attach
1441 if (CurrentProcess
!= Process
)
1446 KeStackAttachProcess(&Process
->Pcb
, &ApcState
);
1458 if (Attached
) KeUnstackDetachProcess(&ApcState
);
1461 // Release reference
1463 ObDereferenceObject(Process
);
1466 // Enter SEH to return data
1471 // Return data to user
1473 *BaseAddress
= PAGE_ALIGN(CapturedBaseAddress
);
1474 *NumberOfBytesToUnlock
= 0;
1476 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1479 // Get exception code
1481 _SEH2_YIELD(return _SEH2_GetExceptionCode());
1488 return STATUS_SUCCESS
;
1493 NtFlushVirtualMemory(IN HANDLE ProcessHandle
,
1494 IN OUT PVOID
*BaseAddress
,
1495 IN OUT PSIZE_T NumberOfBytesToFlush
,
1496 OUT PIO_STATUS_BLOCK IoStatusBlock
)
1500 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
1501 PVOID CapturedBaseAddress
;
1502 SIZE_T CapturedBytesToFlush
;
1503 IO_STATUS_BLOCK LocalStatusBlock
;
1507 // Check if we came from user mode
1509 if (PreviousMode
!= KernelMode
)
1512 // Enter SEH for probing
1517 // Validate all outputs
1519 ProbeForWritePointer(BaseAddress
);
1520 ProbeForWriteSize_t(NumberOfBytesToFlush
);
1521 ProbeForWriteIoStatusBlock(IoStatusBlock
);
1526 CapturedBaseAddress
= *BaseAddress
;
1527 CapturedBytesToFlush
= *NumberOfBytesToFlush
;
1529 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1532 // Get exception code
1534 _SEH2_YIELD(return _SEH2_GetExceptionCode());
1543 CapturedBaseAddress
= *BaseAddress
;
1544 CapturedBytesToFlush
= *NumberOfBytesToFlush
;
1548 // Catch illegal base address
1550 if (CapturedBaseAddress
> MM_HIGHEST_USER_ADDRESS
) return STATUS_INVALID_PARAMETER
;
1553 // Catch illegal region size
1555 if ((MmUserProbeAddress
- (ULONG_PTR
)CapturedBaseAddress
) < CapturedBytesToFlush
)
1560 return STATUS_INVALID_PARAMETER
;
1564 // Get a reference to the process
1566 Status
= ObReferenceObjectByHandle(ProcessHandle
,
1567 PROCESS_VM_OPERATION
,
1572 if (!NT_SUCCESS(Status
)) return Status
;
1577 Status
= MmFlushVirtualMemory(Process
,
1578 &CapturedBaseAddress
,
1579 &CapturedBytesToFlush
,
1583 // Release reference
1585 ObDereferenceObject(Process
);
1588 // Enter SEH to return data
1593 // Return data to user
1595 *BaseAddress
= PAGE_ALIGN(CapturedBaseAddress
);
1596 *NumberOfBytesToFlush
= 0;
1597 *IoStatusBlock
= LocalStatusBlock
;
1599 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1615 NtGetWriteWatch(IN HANDLE ProcessHandle
,
1617 IN PVOID BaseAddress
,
1618 IN SIZE_T RegionSize
,
1619 IN PVOID
*UserAddressArray
,
1620 OUT PULONG_PTR EntriesInUserAddressArray
,
1621 OUT PULONG Granularity
)
1626 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
1627 ULONG_PTR CapturedEntryCount
;
1631 // Check if we came from user mode
1633 if (PreviousMode
!= KernelMode
)
1636 // Enter SEH for probing
1641 // Catch illegal base address
1643 if (BaseAddress
> MM_HIGHEST_USER_ADDRESS
) return STATUS_INVALID_PARAMETER_2
;
1646 // Catch illegal region size
1648 if ((MmUserProbeAddress
- (ULONG_PTR
)BaseAddress
) < RegionSize
)
1653 return STATUS_INVALID_PARAMETER_3
;
1657 // Validate all data
1659 ProbeForWriteSize_t(EntriesInUserAddressArray
);
1660 ProbeForWriteUlong(Granularity
);
1665 CapturedEntryCount
= *EntriesInUserAddressArray
;
1668 // Must have a count
1670 if (CapturedEntryCount
== 0) return STATUS_INVALID_PARAMETER_5
;
1673 // Can't be larger than the maximum
1675 if (CapturedEntryCount
> (MAXULONG_PTR
/ sizeof(ULONG_PTR
)))
1680 return STATUS_INVALID_PARAMETER_5
;
1684 // Probe the actual array
1686 ProbeForWrite(UserAddressArray
,
1687 CapturedEntryCount
* sizeof(PVOID
),
1690 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1693 // Get exception code
1695 _SEH2_YIELD(return _SEH2_GetExceptionCode());
1704 CapturedEntryCount
= *EntriesInUserAddressArray
;
1705 ASSERT(CapturedEntryCount
!= 0);
1709 // Check if this is a local request
1711 if (ProcessHandle
== NtCurrentProcess())
1714 // No need to reference the process
1716 Process
= PsGetCurrentProcess();
1721 // Reference the target
1723 Status
= ObReferenceObjectByHandle(ProcessHandle
,
1724 PROCESS_VM_OPERATION
,
1729 if (!NT_SUCCESS(Status
)) return Status
;
1733 // Compute the last address and validate it
1735 EndAddress
= (PVOID
)((ULONG_PTR
)BaseAddress
+ RegionSize
- 1);
1736 if (BaseAddress
> EndAddress
)
1741 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
1742 return STATUS_INVALID_PARAMETER_4
;
1751 // Dereference if needed
1753 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
1756 // Enter SEH to return data
1761 // Return data to user
1763 *EntriesInUserAddressArray
= 0;
1764 *Granularity
= PAGE_SIZE
;
1766 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1769 // Get exception code
1771 Status
= _SEH2_GetExceptionCode();
1778 return STATUS_SUCCESS
;
1786 NtResetWriteWatch(IN HANDLE ProcessHandle
,
1787 IN PVOID BaseAddress
,
1788 IN SIZE_T RegionSize
)
1793 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
1794 ASSERT (KeGetCurrentIrql() == PASSIVE_LEVEL
);
1797 // Catch illegal base address
1799 if (BaseAddress
> MM_HIGHEST_USER_ADDRESS
) return STATUS_INVALID_PARAMETER_2
;
1802 // Catch illegal region size
1804 if ((MmUserProbeAddress
- (ULONG_PTR
)BaseAddress
) < RegionSize
)
1809 return STATUS_INVALID_PARAMETER_3
;
1813 // Check if this is a local request
1815 if (ProcessHandle
== NtCurrentProcess())
1818 // No need to reference the process
1820 Process
= PsGetCurrentProcess();
1825 // Reference the target
1827 Status
= ObReferenceObjectByHandle(ProcessHandle
,
1828 PROCESS_VM_OPERATION
,
1833 if (!NT_SUCCESS(Status
)) return Status
;
1837 // Compute the last address and validate it
1839 EndAddress
= (PVOID
)((ULONG_PTR
)BaseAddress
+ RegionSize
- 1);
1840 if (BaseAddress
> EndAddress
)
1845 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
1846 return STATUS_INVALID_PARAMETER_3
;
1855 // Dereference if needed
1857 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
1862 return STATUS_SUCCESS
;