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 DPRINT1("Page table: %lx\n", PageTableIndex
);
84 Pfn2
= MiGetPfnEntry(PageTableIndex
);
86 /* Lock the PFN database */
87 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
89 /* Delete it the page */
90 MI_SET_PFN_DELETED(Pfn1
);
91 MiDecrementShareCount(Pfn1
, PageFrameIndex
);
93 /* Decrement the page table too */
94 #if 0 // ARM3: Dont't trust this yet
95 MiDecrementShareCount(Pfn2
, PageTableIndex
);
98 /* Release the PFN database */
99 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
101 /* Destroy the PTE */
102 PointerPte
->u
.Long
= 0;
105 /* Actual legitimate pages */
111 * The only other ARM3 possibility is a demand zero page, which would
112 * mean freeing some of the paged pool pages that haven't even been
113 * touched yet, as part of a larger allocation.
115 * Right now, we shouldn't expect any page file information in the PTE
117 ASSERT(PointerPte
->u
.Soft
.PageFileHigh
== 0);
119 /* Destroy the PTE */
120 PointerPte
->u
.Long
= 0;
129 ASSERT(KeAreAllApcsDisabled() == TRUE
);
130 CurrentThread
->OwnsSystemWorkingSetExclusive
= 0;
131 KeLeaveGuardedRegion();
132 KeLowerIrql(LockIrql
);
134 /* Flush the entire TLB */
135 KeFlushEntireTb(TRUE
, TRUE
);
142 MiGetExceptionInfo(IN PEXCEPTION_POINTERS ExceptionInfo
,
143 OUT PBOOLEAN HaveBadAddress
,
144 OUT PULONG_PTR BadAddress
)
146 PEXCEPTION_RECORD ExceptionRecord
;
152 *HaveBadAddress
= FALSE
;
155 // Get the exception record
157 ExceptionRecord
= ExceptionInfo
->ExceptionRecord
;
160 // Look at the exception code
162 if ((ExceptionRecord
->ExceptionCode
== STATUS_ACCESS_VIOLATION
) ||
163 (ExceptionRecord
->ExceptionCode
== STATUS_GUARD_PAGE_VIOLATION
) ||
164 (ExceptionRecord
->ExceptionCode
== STATUS_IN_PAGE_ERROR
))
167 // We can tell the address if we have more than one parameter
169 if (ExceptionRecord
->NumberParameters
> 1)
172 // Return the address
174 *HaveBadAddress
= TRUE
;
175 *BadAddress
= ExceptionRecord
->ExceptionInformation
[1];
180 // Continue executing the next handler
182 return EXCEPTION_EXECUTE_HANDLER
;
187 MiDoMappedCopy(IN PEPROCESS SourceProcess
,
188 IN PVOID SourceAddress
,
189 IN PEPROCESS TargetProcess
,
190 OUT PVOID TargetAddress
,
191 IN SIZE_T BufferSize
,
192 IN KPROCESSOR_MODE PreviousMode
,
193 OUT PSIZE_T ReturnSize
)
195 PFN_NUMBER MdlBuffer
[(sizeof(MDL
) / sizeof(PFN_NUMBER
)) + MI_MAPPED_COPY_PAGES
+ 1];
196 PMDL Mdl
= (PMDL
)MdlBuffer
;
197 SIZE_T TotalSize
, CurrentSize
, RemainingSize
;
198 volatile BOOLEAN FailedInProbe
= FALSE
, FailedInMapping
= FALSE
, FailedInMoving
;
199 volatile BOOLEAN PagesLocked
;
200 PVOID CurrentAddress
= SourceAddress
, CurrentTargetAddress
= TargetAddress
;
201 volatile PVOID MdlAddress
;
203 BOOLEAN HaveBadAddress
;
204 ULONG_PTR BadAddress
;
205 NTSTATUS Status
= STATUS_SUCCESS
;
209 // Calculate the maximum amount of data to move
211 TotalSize
= MI_MAPPED_COPY_PAGES
* PAGE_SIZE
;
212 if (BufferSize
<= TotalSize
) TotalSize
= BufferSize
;
213 CurrentSize
= TotalSize
;
214 RemainingSize
= BufferSize
;
217 // Loop as long as there is still data
219 while (RemainingSize
> 0)
222 // Check if this transfer will finish everything off
224 if (RemainingSize
< CurrentSize
) CurrentSize
= RemainingSize
;
227 // Attach to the source address space
229 KeStackAttachProcess(&SourceProcess
->Pcb
, &ApcState
);
232 // Reset state for this pass
236 FailedInMoving
= FALSE
;
237 ASSERT(FailedInProbe
== FALSE
);
240 // Protect user-mode copy
245 // If this is our first time, probe the buffer
247 if ((CurrentAddress
== SourceAddress
) && (PreviousMode
!= KernelMode
))
250 // Catch a failure here
252 FailedInProbe
= TRUE
;
257 ProbeForRead(SourceAddress
, BufferSize
, sizeof(CHAR
));
262 FailedInProbe
= FALSE
;
266 // Initialize and probe and lock the MDL
268 MmInitializeMdl(Mdl
, CurrentAddress
, CurrentSize
);
269 MmProbeAndLockPages(Mdl
, PreviousMode
, IoReadAccess
);
275 MdlAddress
= MmMapLockedPagesSpecifyCache(Mdl
,
284 // Use our SEH handler to pick this up
286 FailedInMapping
= TRUE
;
287 ExRaiseStatus(STATUS_INSUFFICIENT_RESOURCES
);
291 // Now let go of the source and grab to the target process
293 KeUnstackDetachProcess(&ApcState
);
294 KeStackAttachProcess(&TargetProcess
->Pcb
, &ApcState
);
297 // Check if this is our first time through
299 if ((CurrentAddress
== SourceAddress
) && (PreviousMode
!= KernelMode
))
302 // Catch a failure here
304 FailedInProbe
= TRUE
;
309 ProbeForWrite(TargetAddress
, BufferSize
, sizeof(CHAR
));
314 FailedInProbe
= FALSE
;
318 // Now do the actual move
320 FailedInMoving
= TRUE
;
321 RtlCopyMemory(CurrentTargetAddress
, MdlAddress
, CurrentSize
);
323 _SEH2_EXCEPT(MiGetExceptionInfo(_SEH2_GetExceptionInformation(),
328 // Detach from whoever we may be attached to
330 KeUnstackDetachProcess(&ApcState
);
333 // Check if we had mapped the pages
335 if (MdlAddress
) MmUnmapLockedPages(MdlAddress
, Mdl
);
338 // Check if we had locked the pages
340 if (PagesLocked
) MmUnlockPages(Mdl
);
343 // Check if we hit working set quota
345 if (_SEH2_GetExceptionCode() == STATUS_WORKING_SET_QUOTA
)
350 return STATUS_WORKING_SET_QUOTA
;
354 // Check if we failed during the probe or mapping
356 if ((FailedInProbe
) || (FailedInMapping
))
361 Status
= _SEH2_GetExceptionCode();
362 _SEH2_YIELD(return Status
);
366 // Otherwise, we failed probably during the move
368 *ReturnSize
= BufferSize
- RemainingSize
;
372 // Check if we know exactly where we stopped copying
377 // Return the exact number of bytes copied
379 *ReturnSize
= BadAddress
- (ULONG_PTR
)SourceAddress
;
384 // Return partial copy
386 Status
= STATUS_PARTIAL_COPY
;
391 // Check for SEH status
393 if (Status
!= STATUS_SUCCESS
) return Status
;
396 // Detach from target
398 KeUnstackDetachProcess(&ApcState
);
403 MmUnmapLockedPages(MdlAddress
, Mdl
);
407 // Update location and size
409 RemainingSize
-= CurrentSize
;
410 CurrentAddress
= (PVOID
)((ULONG_PTR
)CurrentAddress
+ CurrentSize
);
411 CurrentTargetAddress
= (PVOID
)((ULONG_PTR
)CurrentTargetAddress
+ CurrentSize
);
417 *ReturnSize
= BufferSize
;
418 return STATUS_SUCCESS
;
423 MiDoPoolCopy(IN PEPROCESS SourceProcess
,
424 IN PVOID SourceAddress
,
425 IN PEPROCESS TargetProcess
,
426 OUT PVOID TargetAddress
,
427 IN SIZE_T BufferSize
,
428 IN KPROCESSOR_MODE PreviousMode
,
429 OUT PSIZE_T ReturnSize
)
431 UCHAR StackBuffer
[MI_POOL_COPY_BYTES
];
432 SIZE_T TotalSize
, CurrentSize
, RemainingSize
;
433 volatile BOOLEAN FailedInProbe
= FALSE
, FailedInMoving
, HavePoolAddress
= FALSE
;
434 PVOID CurrentAddress
= SourceAddress
, CurrentTargetAddress
= TargetAddress
;
437 BOOLEAN HaveBadAddress
;
438 ULONG_PTR BadAddress
;
439 NTSTATUS Status
= STATUS_SUCCESS
;
443 // Calculate the maximum amount of data to move
445 TotalSize
= MI_MAX_TRANSFER_SIZE
;
446 if (BufferSize
<= MI_MAX_TRANSFER_SIZE
) TotalSize
= BufferSize
;
447 CurrentSize
= TotalSize
;
448 RemainingSize
= BufferSize
;
451 // Check if we can use the stack
453 if (BufferSize
<= MI_POOL_COPY_BYTES
)
458 PoolAddress
= (PVOID
)StackBuffer
;
465 PoolAddress
= ExAllocatePoolWithTag(NonPagedPool
, TotalSize
, 'VmRw');
466 if (!PoolAddress
) ASSERT(FALSE
);
467 HavePoolAddress
= TRUE
;
471 // Loop as long as there is still data
473 while (RemainingSize
> 0)
476 // Check if this transfer will finish everything off
478 if (RemainingSize
< CurrentSize
) CurrentSize
= RemainingSize
;
481 // Attach to the source address space
483 KeStackAttachProcess(&SourceProcess
->Pcb
, &ApcState
);
486 // Reset state for this pass
488 FailedInMoving
= FALSE
;
489 ASSERT(FailedInProbe
== FALSE
);
492 // Protect user-mode copy
497 // If this is our first time, probe the buffer
499 if ((CurrentAddress
== SourceAddress
) && (PreviousMode
!= KernelMode
))
502 // Catch a failure here
504 FailedInProbe
= TRUE
;
509 ProbeForRead(SourceAddress
, BufferSize
, sizeof(CHAR
));
514 FailedInProbe
= FALSE
;
520 RtlCopyMemory(PoolAddress
, CurrentAddress
, CurrentSize
);
523 // Now let go of the source and grab to the target process
525 KeUnstackDetachProcess(&ApcState
);
526 KeStackAttachProcess(&TargetProcess
->Pcb
, &ApcState
);
529 // Check if this is our first time through
531 if ((CurrentAddress
== SourceAddress
) && (PreviousMode
!= KernelMode
))
534 // Catch a failure here
536 FailedInProbe
= TRUE
;
541 ProbeForWrite(TargetAddress
, BufferSize
, sizeof(CHAR
));
546 FailedInProbe
= FALSE
;
550 // Now do the actual move
552 FailedInMoving
= TRUE
;
553 RtlCopyMemory(CurrentTargetAddress
, PoolAddress
, CurrentSize
);
555 _SEH2_EXCEPT(MiGetExceptionInfo(_SEH2_GetExceptionInformation(),
560 // Detach from whoever we may be attached to
562 KeUnstackDetachProcess(&ApcState
);
565 // Check if we had allocated pool
567 if (HavePoolAddress
) ExFreePool(PoolAddress
);
570 // Check if we failed during the probe
577 Status
= _SEH2_GetExceptionCode();
578 _SEH2_YIELD(return Status
);
582 // Otherwise, we failed, probably during the move
584 *ReturnSize
= BufferSize
- RemainingSize
;
588 // Check if we know exactly where we stopped copying
593 // Return the exact number of bytes copied
595 *ReturnSize
= BadAddress
- (ULONG_PTR
)SourceAddress
;
600 // Return partial copy
602 Status
= STATUS_PARTIAL_COPY
;
607 // Check for SEH status
609 if (Status
!= STATUS_SUCCESS
) return Status
;
612 // Detach from target
614 KeUnstackDetachProcess(&ApcState
);
617 // Update location and size
619 RemainingSize
-= CurrentSize
;
620 CurrentAddress
= (PVOID
)((ULONG_PTR
)CurrentAddress
+ CurrentSize
);
621 CurrentTargetAddress
= (PVOID
)((ULONG_PTR
)CurrentTargetAddress
+
626 // Check if we had allocated pool
628 if (HavePoolAddress
) ExFreePool(PoolAddress
);
633 *ReturnSize
= BufferSize
;
634 return STATUS_SUCCESS
;
639 MmCopyVirtualMemory(IN PEPROCESS SourceProcess
,
640 IN PVOID SourceAddress
,
641 IN PEPROCESS TargetProcess
,
642 OUT PVOID TargetAddress
,
643 IN SIZE_T BufferSize
,
644 IN KPROCESSOR_MODE PreviousMode
,
645 OUT PSIZE_T ReturnSize
)
648 PEPROCESS Process
= SourceProcess
;
651 // Don't accept zero-sized buffers
653 if (!BufferSize
) return STATUS_SUCCESS
;
656 // If we are copying from ourselves, lock the target instead
658 if (SourceProcess
== PsGetCurrentProcess()) Process
= TargetProcess
;
661 // Acquire rundown protection
663 if (!ExAcquireRundownProtection(&Process
->RundownProtect
))
668 return STATUS_PROCESS_IS_TERMINATING
;
672 // See if we should use the pool copy
674 if (BufferSize
> MI_POOL_COPY_BYTES
)
679 Status
= MiDoMappedCopy(SourceProcess
,
692 Status
= MiDoPoolCopy(SourceProcess
,
704 ExReleaseRundownProtection(&Process
->RundownProtect
);
710 MmFlushVirtualMemory(IN PEPROCESS Process
,
711 IN OUT PVOID
*BaseAddress
,
712 IN OUT PSIZE_T RegionSize
,
713 OUT PIO_STATUS_BLOCK IoStatusBlock
)
721 return STATUS_SUCCESS
;
724 /* PUBLIC FUNCTIONS ***********************************************************/
731 MmGetVirtualForPhysical(IN PHYSICAL_ADDRESS PhysicalAddress
)
742 MmSecureVirtualMemory(IN PVOID Address
,
755 MmUnsecureVirtualMemory(IN PVOID SecureMem
)
760 /* SYSTEM CALLS ***************************************************************/
764 NtReadVirtualMemory(IN HANDLE ProcessHandle
,
765 IN PVOID BaseAddress
,
767 IN SIZE_T NumberOfBytesToRead
,
768 OUT PSIZE_T NumberOfBytesRead OPTIONAL
)
770 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
772 NTSTATUS Status
= STATUS_SUCCESS
;
773 SIZE_T BytesRead
= 0;
777 // Check if we came from user mode
779 if (PreviousMode
!= KernelMode
)
782 // Validate the read addresses
784 if ((((ULONG_PTR
)BaseAddress
+ NumberOfBytesToRead
) < (ULONG_PTR
)BaseAddress
) ||
785 (((ULONG_PTR
)Buffer
+ NumberOfBytesToRead
) < (ULONG_PTR
)Buffer
) ||
786 (((ULONG_PTR
)BaseAddress
+ NumberOfBytesToRead
) > MmUserProbeAddress
) ||
787 (((ULONG_PTR
)Buffer
+ NumberOfBytesToRead
) > MmUserProbeAddress
))
790 // Don't allow to write into kernel space
792 return STATUS_ACCESS_VIOLATION
;
796 // Enter SEH for probe
801 // Probe the output value
803 if (NumberOfBytesRead
) ProbeForWriteSize_t(NumberOfBytesRead
);
805 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
808 // Get exception code
810 _SEH2_YIELD(return _SEH2_GetExceptionCode());
816 // Don't do zero-byte transfers
818 if (NumberOfBytesToRead
)
821 // Reference the process
823 Status
= ObReferenceObjectByHandle(ProcessHandle
,
829 if (NT_SUCCESS(Status
))
834 Status
= MmCopyVirtualMemory(Process
,
836 PsGetCurrentProcess(),
843 // Dereference the process
845 ObDereferenceObject(Process
);
850 // Check if the caller sent this parameter
852 if (NumberOfBytesRead
)
855 // Enter SEH to guard write
860 // Return the number of bytes read
862 *NumberOfBytesRead
= BytesRead
;
864 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
878 NtWriteVirtualMemory(IN HANDLE ProcessHandle
,
879 IN PVOID BaseAddress
,
881 IN SIZE_T NumberOfBytesToWrite
,
882 OUT PSIZE_T NumberOfBytesWritten OPTIONAL
)
884 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
886 NTSTATUS Status
= STATUS_SUCCESS
;
887 ULONG BytesWritten
= 0;
891 // Check if we came from user mode
893 if (PreviousMode
!= KernelMode
)
896 // Validate the read addresses
898 if ((((ULONG_PTR
)BaseAddress
+ NumberOfBytesToWrite
) < (ULONG_PTR
)BaseAddress
) ||
899 (((ULONG_PTR
)Buffer
+ NumberOfBytesToWrite
) < (ULONG_PTR
)Buffer
) ||
900 (((ULONG_PTR
)BaseAddress
+ NumberOfBytesToWrite
) > MmUserProbeAddress
) ||
901 (((ULONG_PTR
)Buffer
+ NumberOfBytesToWrite
) > MmUserProbeAddress
))
904 // Don't allow to write into kernel space
906 return STATUS_ACCESS_VIOLATION
;
910 // Enter SEH for probe
915 // Probe the output value
917 if (NumberOfBytesWritten
) ProbeForWriteSize_t(NumberOfBytesWritten
);
919 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
922 // Get exception code
924 _SEH2_YIELD(return _SEH2_GetExceptionCode());
930 // Don't do zero-byte transfers
932 if (NumberOfBytesToWrite
)
935 // Reference the process
937 Status
= ObReferenceObjectByHandle(ProcessHandle
,
943 if (NT_SUCCESS(Status
))
948 Status
= MmCopyVirtualMemory(PsGetCurrentProcess(),
952 NumberOfBytesToWrite
,
957 // Dereference the process
959 ObDereferenceObject(Process
);
964 // Check if the caller sent this parameter
966 if (NumberOfBytesWritten
)
969 // Enter SEH to guard write
974 // Return the number of bytes written
976 *NumberOfBytesWritten
= BytesWritten
;
978 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
992 NtProtectVirtualMemory(IN HANDLE ProcessHandle
,
993 IN OUT PVOID
*UnsafeBaseAddress
,
994 IN OUT SIZE_T
*UnsafeNumberOfBytesToProtect
,
995 IN ULONG NewAccessProtection
,
996 OUT PULONG UnsafeOldAccessProtection
)
999 ULONG OldAccessProtection
;
1001 PEPROCESS CurrentProcess
= PsGetCurrentProcess();
1002 PVOID BaseAddress
= NULL
;
1003 SIZE_T NumberOfBytesToProtect
= 0;
1004 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
1006 BOOLEAN Attached
= FALSE
;
1007 KAPC_STATE ApcState
;
1011 // Check for valid protection flags
1013 Protection
= NewAccessProtection
& ~(PAGE_GUARD
|PAGE_NOCACHE
);
1014 if (Protection
!= PAGE_NOACCESS
&&
1015 Protection
!= PAGE_READONLY
&&
1016 Protection
!= PAGE_READWRITE
&&
1017 Protection
!= PAGE_WRITECOPY
&&
1018 Protection
!= PAGE_EXECUTE
&&
1019 Protection
!= PAGE_EXECUTE_READ
&&
1020 Protection
!= PAGE_EXECUTE_READWRITE
&&
1021 Protection
!= PAGE_EXECUTE_WRITECOPY
)
1026 return STATUS_INVALID_PAGE_PROTECTION
;
1030 // Check if we came from user mode
1032 if (PreviousMode
!= KernelMode
)
1035 // Enter SEH for probing
1040 // Validate all outputs
1042 ProbeForWritePointer(UnsafeBaseAddress
);
1043 ProbeForWriteSize_t(UnsafeNumberOfBytesToProtect
);
1044 ProbeForWriteUlong(UnsafeOldAccessProtection
);
1049 BaseAddress
= *UnsafeBaseAddress
;
1050 NumberOfBytesToProtect
= *UnsafeNumberOfBytesToProtect
;
1052 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1055 // Get exception code
1057 _SEH2_YIELD(return _SEH2_GetExceptionCode());
1066 BaseAddress
= *UnsafeBaseAddress
;
1067 NumberOfBytesToProtect
= *UnsafeNumberOfBytesToProtect
;
1071 // Catch illegal base address
1073 if (BaseAddress
> MM_HIGHEST_USER_ADDRESS
) return STATUS_INVALID_PARAMETER_2
;
1076 // Catch illegal region size
1078 if ((MmUserProbeAddress
- (ULONG_PTR
)BaseAddress
) < NumberOfBytesToProtect
)
1083 return STATUS_INVALID_PARAMETER_3
;
1087 // 0 is also illegal
1089 if (!NumberOfBytesToProtect
) return STATUS_INVALID_PARAMETER_3
;
1092 // Get a reference to the process
1094 Status
= ObReferenceObjectByHandle(ProcessHandle
,
1095 PROCESS_VM_OPERATION
,
1100 if (!NT_SUCCESS(Status
)) return Status
;
1103 // Check if we should attach
1105 if (CurrentProcess
!= Process
)
1110 KeStackAttachProcess(&Process
->Pcb
, &ApcState
);
1115 // Do the actual work
1117 Status
= MiProtectVirtualMemory(Process
,
1119 &NumberOfBytesToProtect
,
1120 NewAccessProtection
,
1121 &OldAccessProtection
);
1126 if (Attached
) KeUnstackDetachProcess(&ApcState
);
1129 // Release reference
1131 ObDereferenceObject(Process
);
1134 // Enter SEH to return data
1139 // Return data to user
1141 *UnsafeOldAccessProtection
= OldAccessProtection
;
1142 *UnsafeBaseAddress
= BaseAddress
;
1143 *UnsafeNumberOfBytesToProtect
= NumberOfBytesToProtect
;
1145 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1158 NtLockVirtualMemory(IN HANDLE ProcessHandle
,
1159 IN OUT PVOID
*BaseAddress
,
1160 IN OUT PSIZE_T NumberOfBytesToLock
,
1164 PEPROCESS CurrentProcess
= PsGetCurrentProcess();
1166 BOOLEAN Attached
= FALSE
;
1167 KAPC_STATE ApcState
;
1168 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
1169 PVOID CapturedBaseAddress
;
1170 SIZE_T CapturedBytesToLock
;
1176 if ((MapType
& ~(MAP_PROCESS
| MAP_SYSTEM
)))
1179 // Invalid set of flags
1181 return STATUS_INVALID_PARAMETER
;
1185 // At least one flag must be specified
1187 if (!(MapType
& (MAP_PROCESS
| MAP_SYSTEM
)))
1192 return STATUS_INVALID_PARAMETER
;
1196 // Enter SEH for probing
1201 // Validate output data
1203 ProbeForWritePointer(BaseAddress
);
1204 ProbeForWriteSize_t(NumberOfBytesToLock
);
1209 CapturedBaseAddress
= *BaseAddress
;
1210 CapturedBytesToLock
= *NumberOfBytesToLock
;
1212 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1215 // Get exception code
1217 _SEH2_YIELD(return _SEH2_GetExceptionCode());
1222 // Catch illegal base address
1224 if (CapturedBaseAddress
> MM_HIGHEST_USER_ADDRESS
) return STATUS_INVALID_PARAMETER
;
1227 // Catch illegal region size
1229 if ((MmUserProbeAddress
- (ULONG_PTR
)CapturedBaseAddress
) < CapturedBytesToLock
)
1234 return STATUS_INVALID_PARAMETER
;
1238 // 0 is also illegal
1240 if (!CapturedBytesToLock
) return STATUS_INVALID_PARAMETER
;
1243 // Get a reference to the process
1245 Status
= ObReferenceObjectByHandle(ProcessHandle
,
1246 PROCESS_VM_OPERATION
,
1251 if (!NT_SUCCESS(Status
)) return Status
;
1254 // Check if this is is system-mapped
1256 if (MapType
& MAP_SYSTEM
)
1259 // Check for required privilege
1261 if (!SeSinglePrivilegeCheck(SeLockMemoryPrivilege
, PreviousMode
))
1264 // Fail: Don't have it
1266 ObDereferenceObject(Process
);
1267 return STATUS_PRIVILEGE_NOT_HELD
;
1272 // Check if we should attach
1274 if (CurrentProcess
!= Process
)
1279 KeStackAttachProcess(&Process
->Pcb
, &ApcState
);
1291 if (Attached
) KeUnstackDetachProcess(&ApcState
);
1294 // Release reference
1296 ObDereferenceObject(Process
);
1299 // Enter SEH to return data
1304 // Return data to user
1306 *BaseAddress
= CapturedBaseAddress
;
1307 *NumberOfBytesToLock
= 0;
1309 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1312 // Get exception code
1314 _SEH2_YIELD(return _SEH2_GetExceptionCode());
1321 return STATUS_SUCCESS
;
1326 NtUnlockVirtualMemory(IN HANDLE ProcessHandle
,
1327 IN OUT PVOID
*BaseAddress
,
1328 IN OUT PSIZE_T NumberOfBytesToUnlock
,
1332 PEPROCESS CurrentProcess
= PsGetCurrentProcess();
1334 BOOLEAN Attached
= FALSE
;
1335 KAPC_STATE ApcState
;
1336 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
1337 PVOID CapturedBaseAddress
;
1338 SIZE_T CapturedBytesToUnlock
;
1344 if ((MapType
& ~(MAP_PROCESS
| MAP_SYSTEM
)))
1347 // Invalid set of flags
1349 return STATUS_INVALID_PARAMETER
;
1353 // At least one flag must be specified
1355 if (!(MapType
& (MAP_PROCESS
| MAP_SYSTEM
)))
1360 return STATUS_INVALID_PARAMETER
;
1364 // Enter SEH for probing
1369 // Validate output data
1371 ProbeForWritePointer(BaseAddress
);
1372 ProbeForWriteSize_t(NumberOfBytesToUnlock
);
1377 CapturedBaseAddress
= *BaseAddress
;
1378 CapturedBytesToUnlock
= *NumberOfBytesToUnlock
;
1380 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1383 // Get exception code
1385 _SEH2_YIELD(return _SEH2_GetExceptionCode());
1390 // Catch illegal base address
1392 if (CapturedBaseAddress
> MM_HIGHEST_USER_ADDRESS
) return STATUS_INVALID_PARAMETER
;
1395 // Catch illegal region size
1397 if ((MmUserProbeAddress
- (ULONG_PTR
)CapturedBaseAddress
) < CapturedBytesToUnlock
)
1402 return STATUS_INVALID_PARAMETER
;
1406 // 0 is also illegal
1408 if (!CapturedBytesToUnlock
) return STATUS_INVALID_PARAMETER
;
1411 // Get a reference to the process
1413 Status
= ObReferenceObjectByHandle(ProcessHandle
,
1414 PROCESS_VM_OPERATION
,
1419 if (!NT_SUCCESS(Status
)) return Status
;
1422 // Check if this is is system-mapped
1424 if (MapType
& MAP_SYSTEM
)
1427 // Check for required privilege
1429 if (!SeSinglePrivilegeCheck(SeLockMemoryPrivilege
, PreviousMode
))
1432 // Fail: Don't have it
1434 ObDereferenceObject(Process
);
1435 return STATUS_PRIVILEGE_NOT_HELD
;
1440 // Check if we should attach
1442 if (CurrentProcess
!= Process
)
1447 KeStackAttachProcess(&Process
->Pcb
, &ApcState
);
1459 if (Attached
) KeUnstackDetachProcess(&ApcState
);
1462 // Release reference
1464 ObDereferenceObject(Process
);
1467 // Enter SEH to return data
1472 // Return data to user
1474 *BaseAddress
= PAGE_ALIGN(CapturedBaseAddress
);
1475 *NumberOfBytesToUnlock
= 0;
1477 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1480 // Get exception code
1482 _SEH2_YIELD(return _SEH2_GetExceptionCode());
1489 return STATUS_SUCCESS
;
1494 NtFlushVirtualMemory(IN HANDLE ProcessHandle
,
1495 IN OUT PVOID
*BaseAddress
,
1496 IN OUT PSIZE_T NumberOfBytesToFlush
,
1497 OUT PIO_STATUS_BLOCK IoStatusBlock
)
1501 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
1502 PVOID CapturedBaseAddress
;
1503 SIZE_T CapturedBytesToFlush
;
1504 IO_STATUS_BLOCK LocalStatusBlock
;
1508 // Check if we came from user mode
1510 if (PreviousMode
!= KernelMode
)
1513 // Enter SEH for probing
1518 // Validate all outputs
1520 ProbeForWritePointer(BaseAddress
);
1521 ProbeForWriteSize_t(NumberOfBytesToFlush
);
1522 ProbeForWriteIoStatusBlock(IoStatusBlock
);
1527 CapturedBaseAddress
= *BaseAddress
;
1528 CapturedBytesToFlush
= *NumberOfBytesToFlush
;
1530 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1533 // Get exception code
1535 _SEH2_YIELD(return _SEH2_GetExceptionCode());
1544 CapturedBaseAddress
= *BaseAddress
;
1545 CapturedBytesToFlush
= *NumberOfBytesToFlush
;
1549 // Catch illegal base address
1551 if (CapturedBaseAddress
> MM_HIGHEST_USER_ADDRESS
) return STATUS_INVALID_PARAMETER
;
1554 // Catch illegal region size
1556 if ((MmUserProbeAddress
- (ULONG_PTR
)CapturedBaseAddress
) < CapturedBytesToFlush
)
1561 return STATUS_INVALID_PARAMETER
;
1565 // Get a reference to the process
1567 Status
= ObReferenceObjectByHandle(ProcessHandle
,
1568 PROCESS_VM_OPERATION
,
1573 if (!NT_SUCCESS(Status
)) return Status
;
1578 Status
= MmFlushVirtualMemory(Process
,
1579 &CapturedBaseAddress
,
1580 &CapturedBytesToFlush
,
1584 // Release reference
1586 ObDereferenceObject(Process
);
1589 // Enter SEH to return data
1594 // Return data to user
1596 *BaseAddress
= PAGE_ALIGN(CapturedBaseAddress
);
1597 *NumberOfBytesToFlush
= 0;
1598 *IoStatusBlock
= LocalStatusBlock
;
1600 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1616 NtGetWriteWatch(IN HANDLE ProcessHandle
,
1618 IN PVOID BaseAddress
,
1619 IN SIZE_T RegionSize
,
1620 IN PVOID
*UserAddressArray
,
1621 OUT PULONG_PTR EntriesInUserAddressArray
,
1622 OUT PULONG Granularity
)
1627 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
1628 ULONG_PTR CapturedEntryCount
;
1632 // Check if we came from user mode
1634 if (PreviousMode
!= KernelMode
)
1637 // Enter SEH for probing
1642 // Catch illegal base address
1644 if (BaseAddress
> MM_HIGHEST_USER_ADDRESS
) return STATUS_INVALID_PARAMETER_2
;
1647 // Catch illegal region size
1649 if ((MmUserProbeAddress
- (ULONG_PTR
)BaseAddress
) < RegionSize
)
1654 return STATUS_INVALID_PARAMETER_3
;
1658 // Validate all data
1660 ProbeForWriteSize_t(EntriesInUserAddressArray
);
1661 ProbeForWriteUlong(Granularity
);
1666 CapturedEntryCount
= *EntriesInUserAddressArray
;
1669 // Must have a count
1671 if (CapturedEntryCount
== 0) return STATUS_INVALID_PARAMETER_5
;
1674 // Can't be larger than the maximum
1676 if (CapturedEntryCount
> (MAXULONG_PTR
/ sizeof(ULONG_PTR
)))
1681 return STATUS_INVALID_PARAMETER_5
;
1685 // Probe the actual array
1687 ProbeForWrite(UserAddressArray
,
1688 CapturedEntryCount
* sizeof(PVOID
),
1691 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1694 // Get exception code
1696 _SEH2_YIELD(return _SEH2_GetExceptionCode());
1705 CapturedEntryCount
= *EntriesInUserAddressArray
;
1706 ASSERT(CapturedEntryCount
!= 0);
1710 // Check if this is a local request
1712 if (ProcessHandle
== NtCurrentProcess())
1715 // No need to reference the process
1717 Process
= PsGetCurrentProcess();
1722 // Reference the target
1724 Status
= ObReferenceObjectByHandle(ProcessHandle
,
1725 PROCESS_VM_OPERATION
,
1730 if (!NT_SUCCESS(Status
)) return Status
;
1734 // Compute the last address and validate it
1736 EndAddress
= (PVOID
)((ULONG_PTR
)BaseAddress
+ RegionSize
- 1);
1737 if (BaseAddress
> EndAddress
)
1742 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
1743 return STATUS_INVALID_PARAMETER_4
;
1752 // Dereference if needed
1754 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
1757 // Enter SEH to return data
1762 // Return data to user
1764 *EntriesInUserAddressArray
= 0;
1765 *Granularity
= PAGE_SIZE
;
1767 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1770 // Get exception code
1772 Status
= _SEH2_GetExceptionCode();
1779 return STATUS_SUCCESS
;
1787 NtResetWriteWatch(IN HANDLE ProcessHandle
,
1788 IN PVOID BaseAddress
,
1789 IN SIZE_T RegionSize
)
1794 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
1795 ASSERT (KeGetCurrentIrql() == PASSIVE_LEVEL
);
1798 // Catch illegal base address
1800 if (BaseAddress
> MM_HIGHEST_USER_ADDRESS
) return STATUS_INVALID_PARAMETER_2
;
1803 // Catch illegal region size
1805 if ((MmUserProbeAddress
- (ULONG_PTR
)BaseAddress
) < RegionSize
)
1810 return STATUS_INVALID_PARAMETER_3
;
1814 // Check if this is a local request
1816 if (ProcessHandle
== NtCurrentProcess())
1819 // No need to reference the process
1821 Process
= PsGetCurrentProcess();
1826 // Reference the target
1828 Status
= ObReferenceObjectByHandle(ProcessHandle
,
1829 PROCESS_VM_OPERATION
,
1834 if (!NT_SUCCESS(Status
)) return Status
;
1838 // Compute the last address and validate it
1840 EndAddress
= (PVOID
)((ULONG_PTR
)BaseAddress
+ RegionSize
- 1);
1841 if (BaseAddress
> EndAddress
)
1846 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
1847 return STATUS_INVALID_PARAMETER_3
;
1856 // Dereference if needed
1858 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
1863 return STATUS_SUCCESS
;