diff --git a/reactos/dll/win32/kernel32/mem/virtual.c b/reactos/dll/win32/kernel32/mem/virtual.c index f7a1e02baa9..5ccc3dff170 100644 --- a/reactos/dll/win32/kernel32/mem/virtual.c +++ b/reactos/dll/win32/kernel32/mem/virtual.c @@ -170,14 +170,15 @@ NTAPI VirtualLock(IN LPVOID lpAddress, IN SIZE_T dwSize) { - ULONG BytesLocked; NTSTATUS Status; + ULONG RegionSize = dwSize; + PVOID BaseAddress = lpAddress; /* Lock the memory */ Status = NtLockVirtualMemory(NtCurrentProcess(), - lpAddress, - dwSize, - &BytesLocked); + &BaseAddress, + &RegionSize, + MAP_PROCESS); if (!NT_SUCCESS(Status)) { /* We failed */ @@ -244,14 +245,15 @@ NTAPI VirtualUnlock(IN LPVOID lpAddress, IN SIZE_T dwSize) { - ULONG BytesLocked; NTSTATUS Status; - - /* Unlock the memory */ + ULONG RegionSize = dwSize; + PVOID BaseAddress = lpAddress; + + /* Lock the memory */ Status = NtUnlockVirtualMemory(NtCurrentProcess(), - lpAddress, - dwSize, - &BytesLocked); + &BaseAddress, + &RegionSize, + MAP_PROCESS); if (!NT_SUCCESS(Status)) { /* We failed */ diff --git a/reactos/include/ddk/ntifs.h b/reactos/include/ddk/ntifs.h index 4a7657824b0..62d29154029 100644 --- a/reactos/include/ddk/ntifs.h +++ b/reactos/include/ddk/ntifs.h @@ -329,8 +329,6 @@ typedef enum _SECURITY_LOGON_TYPE #define MAILSLOT_SIZE_AUTO 0 -#define MAP_PROCESS 1L -#define MAP_SYSTEM 2L #define MEM_DOS_LIM 0x40000000 #define MCB_FLAG_RAISE_ON_ALLOCATION_FAILURE 1 diff --git a/reactos/include/ndk/mmfuncs.h b/reactos/include/ndk/mmfuncs.h index a1c7e8b3383..30347061554 100644 --- a/reactos/include/ndk/mmfuncs.h +++ b/reactos/include/ndk/mmfuncs.h @@ -173,10 +173,10 @@ NTSYSCALLAPI NTSTATUS NTAPI NtLockVirtualMemory( - HANDLE ProcessHandle, - PVOID BaseAddress, - SIZE_T NumberOfBytesToLock, - PSIZE_T NumberOfBytesLocked + IN HANDLE ProcessHandle, + IN OUT PVOID *BaseAddress, + IN OUT PSIZE_T NumberOfBytesToLock, + IN ULONG MapType ); NTSTATUS @@ -278,9 +278,9 @@ NTSTATUS NTAPI NtUnlockVirtualMemory( IN HANDLE ProcessHandle, - IN PVOID BaseAddress, - IN SIZE_T NumberOfBytesToUnlock, - OUT PSIZE_T NumberOfBytesUnlocked OPTIONAL + IN OUT PVOID *BaseAddress, + IN OUT PSIZE_T NumberOfBytesToUnlock, + IN ULONG MapType ); NTSYSCALLAPI @@ -367,10 +367,10 @@ NTSYSAPI NTSTATUS NTAPI ZwLockVirtualMemory( - HANDLE ProcessHandle, - PVOID BaseAddress, - SIZE_T NumberOfBytesToLock, - PSIZE_T NumberOfBytesLocked + IN HANDLE ProcessHandle, + IN OUT PVOID *BaseAddress, + IN OUT PSIZE_T NumberOfBytesToLock, + IN ULONG MapType ); NTSYSAPI @@ -448,9 +448,9 @@ NTSTATUS NTAPI ZwUnlockVirtualMemory( IN HANDLE ProcessHandle, - IN PVOID BaseAddress, - IN SIZE_T NumberOfBytesToUnlock, - OUT PSIZE_T NumberOfBytesUnlocked OPTIONAL + IN OUT PVOID *BaseAddress, + IN OUT PSIZE_T NumberOfBytesToUnlock, + IN ULONG MapType ); NTSYSAPI diff --git a/reactos/include/ndk/mmtypes.h b/reactos/include/ndk/mmtypes.h index fcc8faad6c2..05f4cbe8974 100644 --- a/reactos/include/ndk/mmtypes.h +++ b/reactos/include/ndk/mmtypes.h @@ -57,6 +57,12 @@ Author: #define MMPFNUSE_DRIVERLOCKPAGE 10 #define MMPFNUSE_KERNELSTACK 11 +// +// Lock/Unlock Virtuam Memory Flags +// +#define MAP_PROCESS 1 +#define MAP_SYSTEM 2 + #ifndef NTOS_MODE_USER // diff --git a/reactos/ntoskrnl/mm/ARM3/virtual.c b/reactos/ntoskrnl/mm/ARM3/virtual.c new file mode 100644 index 00000000000..d10bf87d417 --- /dev/null +++ b/reactos/ntoskrnl/mm/ARM3/virtual.c @@ -0,0 +1,1757 @@ +/* + * PROJECT: ReactOS Kernel + * LICENSE: BSD - See COPYING.ARM in the top level directory + * FILE: ntoskrnl/mm/ARM3/virtual.c + * PURPOSE: ARM Memory Manager Virtual Memory Management + * PROGRAMMERS: ReactOS Portable Systems Group + */ + +/* INCLUDES *******************************************************************/ + +#include +#define NDEBUG +#include + +#line 15 "ARM³::VIRTUAL" +#define MODULE_INVOLVED_IN_ARM3 +#include "../ARM3/miarm.h" + +#define MI_MAPPED_COPY_PAGES 14 +#define MI_POOL_COPY_BYTES 512 +#define MI_MAX_TRANSFER_SIZE 64 * 1024 + +NTSTATUS NTAPI +MiProtectVirtualMemory(IN PEPROCESS Process, + IN OUT PVOID *BaseAddress, + IN OUT PSIZE_T NumberOfBytesToProtect, + IN ULONG NewAccessProtection, + OUT PULONG OldAccessProtection OPTIONAL); + +/* PRIVATE FUNCTIONS **********************************************************/ + +LONG +MiGetExceptionInfo(IN PEXCEPTION_POINTERS ExceptionInfo, + OUT PBOOLEAN HaveBadAddress, + OUT PULONG_PTR BadAddress) +{ + PEXCEPTION_RECORD ExceptionRecord; + PAGED_CODE(); + + // + // Assume default + // + *HaveBadAddress = FALSE; + + // + // Get the exception record + // + ExceptionRecord = ExceptionInfo->ExceptionRecord; + + // + // Look at the exception code + // + if ((ExceptionRecord->ExceptionCode == STATUS_ACCESS_VIOLATION) || + (ExceptionRecord->ExceptionCode == STATUS_GUARD_PAGE_VIOLATION) || + (ExceptionRecord->ExceptionCode == STATUS_IN_PAGE_ERROR)) + { + // + // We can tell the address if we have more than one parameter + // + if (ExceptionRecord->NumberParameters > 1) + { + // + // Return the address + // + *HaveBadAddress = TRUE; + *BadAddress = ExceptionRecord->ExceptionInformation[1]; + } + } + + // + // Continue executing the next handler + // + return EXCEPTION_EXECUTE_HANDLER; +} + +NTSTATUS +NTAPI +MiDoMappedCopy(IN PEPROCESS SourceProcess, + IN PVOID SourceAddress, + IN PEPROCESS TargetProcess, + OUT PVOID TargetAddress, + IN SIZE_T BufferSize, + IN KPROCESSOR_MODE PreviousMode, + OUT PSIZE_T ReturnSize) +{ + PFN_NUMBER MdlBuffer[(sizeof(MDL) / sizeof(PFN_NUMBER)) + MI_MAPPED_COPY_PAGES + 1]; + PMDL Mdl = (PMDL)MdlBuffer; + SIZE_T TotalSize, CurrentSize, RemainingSize; + volatile BOOLEAN FailedInProbe = FALSE, FailedInMapping = FALSE, FailedInMoving; + volatile BOOLEAN PagesLocked; + PVOID CurrentAddress = SourceAddress, CurrentTargetAddress = TargetAddress; + volatile PVOID MdlAddress; + KAPC_STATE ApcState; + BOOLEAN HaveBadAddress; + ULONG_PTR BadAddress; + NTSTATUS Status = STATUS_SUCCESS; + PAGED_CODE(); + + // + // Calculate the maximum amount of data to move + // + TotalSize = MI_MAPPED_COPY_PAGES * PAGE_SIZE; + if (BufferSize <= TotalSize) TotalSize = BufferSize; + CurrentSize = TotalSize; + RemainingSize = BufferSize; + + // + // Loop as long as there is still data + // + while (RemainingSize > 0) + { + // + // Check if this transfer will finish everything off + // + if (RemainingSize < CurrentSize) CurrentSize = RemainingSize; + + // + // Attach to the source address space + // + KeStackAttachProcess(&SourceProcess->Pcb, &ApcState); + + // + // Reset state for this pass + // + MdlAddress = NULL; + PagesLocked = FALSE; + FailedInMoving = FALSE; + ASSERT(FailedInProbe == FALSE); + + // + // Protect user-mode copy + // + _SEH2_TRY + { + // + // If this is our first time, probe the buffer + // + if ((CurrentAddress == SourceAddress) && (PreviousMode != KernelMode)) + { + // + // Catch a failure here + // + FailedInProbe = TRUE; + + // + // Do the probe + // + ProbeForRead(SourceAddress, BufferSize, sizeof(CHAR)); + + // + // Passed + // + FailedInProbe = FALSE; + } + + // + // Initialize and probe and lock the MDL + // + MmInitializeMdl(Mdl, CurrentAddress, CurrentSize); + MmProbeAndLockPages(Mdl, PreviousMode, IoReadAccess); + PagesLocked = TRUE; + + // + // Now map the pages + // + MdlAddress = MmMapLockedPagesSpecifyCache(Mdl, + KernelMode, + MmCached, + NULL, + FALSE, + HighPagePriority); + if (!MdlAddress) + { + // + // Use our SEH handler to pick this up + // + FailedInMapping = TRUE; + ExRaiseStatus(STATUS_INSUFFICIENT_RESOURCES); + } + + // + // Now let go of the source and grab to the target process + // + KeUnstackDetachProcess(&ApcState); + KeStackAttachProcess(&TargetProcess->Pcb, &ApcState); + + // + // Check if this is our first time through + // + if ((CurrentAddress == SourceAddress) && (PreviousMode != KernelMode)) + { + // + // Catch a failure here + // + FailedInProbe = TRUE; + + // + // Do the probe + // + ProbeForWrite(TargetAddress, BufferSize, sizeof(CHAR)); + + // + // Passed + // + FailedInProbe = FALSE; + } + + // + // Now do the actual move + // + FailedInMoving = TRUE; + RtlCopyMemory(CurrentTargetAddress, MdlAddress, CurrentSize); + } + _SEH2_EXCEPT(MiGetExceptionInfo(_SEH2_GetExceptionInformation(), + &HaveBadAddress, + &BadAddress)) + { + // + // Detach from whoever we may be attached to + // + KeUnstackDetachProcess(&ApcState); + + // + // Check if we had mapped the pages + // + if (MdlAddress) MmUnmapLockedPages(MdlAddress, Mdl); + + // + // Check if we had locked the pages + // + if (PagesLocked) MmUnlockPages(Mdl); + + // + // Check if we hit working set quota + // + if (_SEH2_GetExceptionCode() == STATUS_WORKING_SET_QUOTA) + { + // + // Return the error + // + return STATUS_WORKING_SET_QUOTA; + } + + // + // Check if we failed during the probe or mapping + // + if ((FailedInProbe) || (FailedInMapping)) + { + // + // Exit + // + Status = _SEH2_GetExceptionCode(); + _SEH2_YIELD(return Status); + } + + // + // Otherwise, we failed probably during the move + // + *ReturnSize = BufferSize - RemainingSize; + if (FailedInMoving) + { + // + // Check if we know exactly where we stopped copying + // + if (HaveBadAddress) + { + // + // Return the exact number of bytes copied + // + *ReturnSize = BadAddress - (ULONG_PTR)SourceAddress; + } + } + + // + // Return partial copy + // + Status = STATUS_PARTIAL_COPY; + } + _SEH2_END; + + // + // Check for SEH status + // + if (Status != STATUS_SUCCESS) return Status; + + // + // Detach from target + // + KeUnstackDetachProcess(&ApcState); + + // + // Unmap and unlock + // + MmUnmapLockedPages(MdlAddress, Mdl); + MmUnlockPages(Mdl); + + // + // Update location and size + // + RemainingSize -= CurrentSize; + CurrentAddress = (PVOID)((ULONG_PTR)CurrentAddress + CurrentSize); + CurrentTargetAddress = (PVOID)((ULONG_PTR)CurrentTargetAddress + CurrentSize); + } + + // + // All bytes read + // + *ReturnSize = BufferSize; + return STATUS_SUCCESS; +} + +NTSTATUS +NTAPI +MiDoPoolCopy(IN PEPROCESS SourceProcess, + IN PVOID SourceAddress, + IN PEPROCESS TargetProcess, + OUT PVOID TargetAddress, + IN SIZE_T BufferSize, + IN KPROCESSOR_MODE PreviousMode, + OUT PSIZE_T ReturnSize) +{ + UCHAR StackBuffer[MI_POOL_COPY_BYTES]; + SIZE_T TotalSize, CurrentSize, RemainingSize; + volatile BOOLEAN FailedInProbe = FALSE, FailedInMoving, HavePoolAddress = FALSE; + PVOID CurrentAddress = SourceAddress, CurrentTargetAddress = TargetAddress; + PVOID PoolAddress; + KAPC_STATE ApcState; + BOOLEAN HaveBadAddress; + ULONG_PTR BadAddress; + NTSTATUS Status = STATUS_SUCCESS; + PAGED_CODE(); + + // + // Calculate the maximum amount of data to move + // + TotalSize = MI_MAX_TRANSFER_SIZE; + if (BufferSize <= MI_MAX_TRANSFER_SIZE) TotalSize = BufferSize; + CurrentSize = TotalSize; + RemainingSize = BufferSize; + + // + // Check if we can use the stack + // + if (BufferSize <= MI_POOL_COPY_BYTES) + { + // + // Use it + // + PoolAddress = (PVOID)StackBuffer; + } + else + { + // + // Allocate pool + // + PoolAddress = ExAllocatePoolWithTag(NonPagedPool, TotalSize, 'VmRw'); + if (!PoolAddress) ASSERT(FALSE); + HavePoolAddress = TRUE; + } + + // + // Loop as long as there is still data + // + while (RemainingSize > 0) + { + // + // Check if this transfer will finish everything off + // + if (RemainingSize < CurrentSize) CurrentSize = RemainingSize; + + // + // Attach to the source address space + // + KeStackAttachProcess(&SourceProcess->Pcb, &ApcState); + + // + // Reset state for this pass + // + FailedInMoving = FALSE; + ASSERT(FailedInProbe == FALSE); + + // + // Protect user-mode copy + // + _SEH2_TRY + { + // + // If this is our first time, probe the buffer + // + if ((CurrentAddress == SourceAddress) && (PreviousMode != KernelMode)) + { + // + // Catch a failure here + // + FailedInProbe = TRUE; + + // + // Do the probe + // + ProbeForRead(SourceAddress, BufferSize, sizeof(CHAR)); + + // + // Passed + // + FailedInProbe = FALSE; + } + + // + // Do the copy + // + RtlCopyMemory(PoolAddress, CurrentAddress, CurrentSize); + + // + // Now let go of the source and grab to the target process + // + KeUnstackDetachProcess(&ApcState); + KeStackAttachProcess(&TargetProcess->Pcb, &ApcState); + + // + // Check if this is our first time through + // + if ((CurrentAddress == SourceAddress) && (PreviousMode != KernelMode)) + { + // + // Catch a failure here + // + FailedInProbe = TRUE; + + // + // Do the probe + // + ProbeForWrite(TargetAddress, BufferSize, sizeof(CHAR)); + + // + // Passed + // + FailedInProbe = FALSE; + } + + // + // Now do the actual move + // + FailedInMoving = TRUE; + RtlCopyMemory(CurrentTargetAddress, PoolAddress, CurrentSize); + } + _SEH2_EXCEPT(MiGetExceptionInfo(_SEH2_GetExceptionInformation(), + &HaveBadAddress, + &BadAddress)) + { + // + // Detach from whoever we may be attached to + // + KeUnstackDetachProcess(&ApcState); + + // + // Check if we had allocated pool + // + if (HavePoolAddress) ExFreePool(PoolAddress); + + // + // Check if we failed during the probe + // + if (FailedInProbe) + { + // + // Exit + // + Status = _SEH2_GetExceptionCode(); + _SEH2_YIELD(return Status); + } + + // + // Otherwise, we failed, probably during the move + // + *ReturnSize = BufferSize - RemainingSize; + if (FailedInMoving) + { + // + // Check if we know exactly where we stopped copying + // + if (HaveBadAddress) + { + // + // Return the exact number of bytes copied + // + *ReturnSize = BadAddress - (ULONG_PTR)SourceAddress; + } + } + + // + // Return partial copy + // + Status = STATUS_PARTIAL_COPY; + } + _SEH2_END; + + // + // Check for SEH status + // + if (Status != STATUS_SUCCESS) return Status; + + // + // Detach from target + // + KeUnstackDetachProcess(&ApcState); + + // + // Update location and size + // + RemainingSize -= CurrentSize; + CurrentAddress = (PVOID)((ULONG_PTR)CurrentAddress + CurrentSize); + CurrentTargetAddress = (PVOID)((ULONG_PTR)CurrentTargetAddress + + CurrentSize); + } + + // + // Check if we had allocated pool + // + if (HavePoolAddress) ExFreePool(PoolAddress); + + // + // All bytes read + // + *ReturnSize = BufferSize; + return STATUS_SUCCESS; +} + +NTSTATUS +NTAPI +MmCopyVirtualMemory(IN PEPROCESS SourceProcess, + IN PVOID SourceAddress, + IN PEPROCESS TargetProcess, + OUT PVOID TargetAddress, + IN SIZE_T BufferSize, + IN KPROCESSOR_MODE PreviousMode, + OUT PSIZE_T ReturnSize) +{ + NTSTATUS Status; + PEPROCESS Process = SourceProcess; + + // + // Don't accept zero-sized buffers + // + if (!BufferSize) return STATUS_SUCCESS; + + // + // If we are copying from ourselves, lock the target instead + // + if (SourceProcess == PsGetCurrentProcess()) Process = TargetProcess; + + // + // Acquire rundown protection + // + if (!ExAcquireRundownProtection(&Process->RundownProtect)) + { + // + // Fail + // + return STATUS_PROCESS_IS_TERMINATING; + } + + // + // See if we should use the pool copy + // + if (BufferSize > MI_POOL_COPY_BYTES) + { + // + // Use MDL-copy + // + Status = MiDoMappedCopy(SourceProcess, + SourceAddress, + TargetProcess, + TargetAddress, + BufferSize, + PreviousMode, + ReturnSize); + } + else + { + // + // Do pool copy + // + Status = MiDoPoolCopy(SourceProcess, + SourceAddress, + TargetProcess, + TargetAddress, + BufferSize, + PreviousMode, + ReturnSize); + } + + // + // Release the lock + // + ExReleaseRundownProtection(&Process->RundownProtect); + return Status; +} + +NTSTATUS +NTAPI +MmFlushVirtualMemory(IN PEPROCESS Process, + IN OUT PVOID *BaseAddress, + IN OUT PSIZE_T RegionSize, + OUT PIO_STATUS_BLOCK IoStatusBlock) +{ + PAGED_CODE(); + UNIMPLEMENTED; + + // + // Fake success + // + return STATUS_SUCCESS; +} + +/* PUBLIC FUNCTIONS ***********************************************************/ + +/* + * @unimplemented + */ +PVOID +NTAPI +MmGetVirtualForPhysical(IN PHYSICAL_ADDRESS PhysicalAddress) +{ + UNIMPLEMENTED; + return 0; +} + +/* + * @unimplemented + */ +PVOID +NTAPI +MmSecureVirtualMemory(IN PVOID Address, + IN SIZE_T Length, + IN ULONG Mode) +{ + UNIMPLEMENTED; + return NULL; +} + +/* + * @unimplemented + */ +VOID +NTAPI +MmUnsecureVirtualMemory(IN PVOID SecureMem) +{ + UNIMPLEMENTED; +} + +/* SYSTEM CALLS ***************************************************************/ + +NTSTATUS +NTAPI +NtReadVirtualMemory(IN HANDLE ProcessHandle, + IN PVOID BaseAddress, + OUT PVOID Buffer, + IN SIZE_T NumberOfBytesToRead, + OUT PSIZE_T NumberOfBytesRead OPTIONAL) +{ + KPROCESSOR_MODE PreviousMode = ExGetPreviousMode(); + PEPROCESS Process; + NTSTATUS Status = STATUS_SUCCESS; + SIZE_T BytesRead = 0; + PAGED_CODE(); + + // + // Check if we came from user mode + // + if (PreviousMode != KernelMode) + { + // + // Validate the read addresses + // + if ((((ULONG_PTR)BaseAddress + NumberOfBytesToRead) < (ULONG_PTR)BaseAddress) || + (((ULONG_PTR)Buffer + NumberOfBytesToRead) < (ULONG_PTR)Buffer) || + (((ULONG_PTR)BaseAddress + NumberOfBytesToRead) > MmUserProbeAddress) || + (((ULONG_PTR)Buffer + NumberOfBytesToRead) > MmUserProbeAddress)) + { + // + // Don't allow to write into kernel space + // + return STATUS_ACCESS_VIOLATION; + } + + // + // Enter SEH for probe + // + _SEH2_TRY + { + // + // Probe the output value + // + if (NumberOfBytesRead) ProbeForWriteSize_t(NumberOfBytesRead); + } + _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) + { + // + // Get exception code + // + _SEH2_YIELD(return _SEH2_GetExceptionCode()); + } + _SEH2_END; + } + + // + // Don't do zero-byte transfers + // + if (NumberOfBytesToRead) + { + // + // Reference the process + // + Status = ObReferenceObjectByHandle(ProcessHandle, + PROCESS_VM_READ, + PsProcessType, + PreviousMode, + (PVOID*)(&Process), + NULL); + if (NT_SUCCESS(Status)) + { + // + // Do the copy + // + Status = MmCopyVirtualMemory(Process, + BaseAddress, + PsGetCurrentProcess(), + Buffer, + NumberOfBytesToRead, + PreviousMode, + &BytesRead); + + // + // Dereference the process + // + ObDereferenceObject(Process); + } + } + + // + // Check if the caller sent this parameter + // + if (NumberOfBytesRead) + { + // + // Enter SEH to guard write + // + _SEH2_TRY + { + // + // Return the number of bytes read + // + *NumberOfBytesRead = BytesRead; + } + _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) + { + } + _SEH2_END; + } + + // + // Return status + // + return Status; +} + +NTSTATUS +NTAPI +NtWriteVirtualMemory(IN HANDLE ProcessHandle, + IN PVOID BaseAddress, + IN PVOID Buffer, + IN SIZE_T NumberOfBytesToWrite, + OUT PSIZE_T NumberOfBytesWritten OPTIONAL) +{ + KPROCESSOR_MODE PreviousMode = ExGetPreviousMode(); + PEPROCESS Process; + NTSTATUS Status = STATUS_SUCCESS; + ULONG BytesWritten = 0; + PAGED_CODE(); + + // + // Check if we came from user mode + // + if (PreviousMode != KernelMode) + { + // + // Validate the read addresses + // + if ((((ULONG_PTR)BaseAddress + NumberOfBytesToWrite) < (ULONG_PTR)BaseAddress) || + (((ULONG_PTR)Buffer + NumberOfBytesToWrite) < (ULONG_PTR)Buffer) || + (((ULONG_PTR)BaseAddress + NumberOfBytesToWrite) > MmUserProbeAddress) || + (((ULONG_PTR)Buffer + NumberOfBytesToWrite) > MmUserProbeAddress)) + { + // + // Don't allow to write into kernel space + // + return STATUS_ACCESS_VIOLATION; + } + + // + // Enter SEH for probe + // + _SEH2_TRY + { + // + // Probe the output value + // + if (NumberOfBytesWritten) ProbeForWriteSize_t(NumberOfBytesWritten); + } + _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) + { + // + // Get exception code + // + _SEH2_YIELD(return _SEH2_GetExceptionCode()); + } + _SEH2_END; + } + + // + // Don't do zero-byte transfers + // + if (NumberOfBytesToWrite) + { + // + // Reference the process + // + Status = ObReferenceObjectByHandle(ProcessHandle, + PROCESS_VM_WRITE, + PsProcessType, + PreviousMode, + (PVOID*)&Process, + NULL); + if (NT_SUCCESS(Status)) + { + // + // Do the copy + // + Status = MmCopyVirtualMemory(PsGetCurrentProcess(), + Buffer, + Process, + BaseAddress, + NumberOfBytesToWrite, + PreviousMode, + &BytesWritten); + + // + // Dereference the process + // + ObDereferenceObject(Process); + } + } + + // + // Check if the caller sent this parameter + // + if (NumberOfBytesWritten) + { + // + // Enter SEH to guard write + // + _SEH2_TRY + { + // + // Return the number of bytes written + // + *NumberOfBytesWritten = BytesWritten; + } + _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) + { + } + _SEH2_END; + } + + // + // Return status + // + return Status; +} + +NTSTATUS +NTAPI +NtProtectVirtualMemory(IN HANDLE ProcessHandle, + IN OUT PVOID *UnsafeBaseAddress, + IN OUT SIZE_T *UnsafeNumberOfBytesToProtect, + IN ULONG NewAccessProtection, + OUT PULONG UnsafeOldAccessProtection) +{ + PEPROCESS Process; + ULONG OldAccessProtection; + ULONG Protection; + PEPROCESS CurrentProcess = PsGetCurrentProcess(); + PVOID BaseAddress = NULL; + SIZE_T NumberOfBytesToProtect = 0; + KPROCESSOR_MODE PreviousMode = ExGetPreviousMode(); + NTSTATUS Status; + BOOLEAN Attached = FALSE; + KAPC_STATE ApcState; + PAGED_CODE(); + + // + // Check for valid protection flags + // + Protection = NewAccessProtection & ~(PAGE_GUARD|PAGE_NOCACHE); + if (Protection != PAGE_NOACCESS && + Protection != PAGE_READONLY && + Protection != PAGE_READWRITE && + Protection != PAGE_WRITECOPY && + Protection != PAGE_EXECUTE && + Protection != PAGE_EXECUTE_READ && + Protection != PAGE_EXECUTE_READWRITE && + Protection != PAGE_EXECUTE_WRITECOPY) + { + // + // Fail + // + return STATUS_INVALID_PAGE_PROTECTION; + } + + // + // Check if we came from user mode + // + if (PreviousMode != KernelMode) + { + // + // Enter SEH for probing + // + _SEH2_TRY + { + // + // Validate all outputs + // + ProbeForWritePointer(UnsafeBaseAddress); + ProbeForWriteSize_t(UnsafeNumberOfBytesToProtect); + ProbeForWriteUlong(UnsafeOldAccessProtection); + + // + // Capture them + // + BaseAddress = *UnsafeBaseAddress; + NumberOfBytesToProtect = *UnsafeNumberOfBytesToProtect; + } + _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) + { + // + // Get exception code + // + _SEH2_YIELD(return _SEH2_GetExceptionCode()); + } + _SEH2_END; + } + else + { + // + // Capture directly + // + BaseAddress = *UnsafeBaseAddress; + NumberOfBytesToProtect = *UnsafeNumberOfBytesToProtect; + } + + // + // Catch illegal base address + // + if (BaseAddress > MM_HIGHEST_USER_ADDRESS) return STATUS_INVALID_PARAMETER_2; + + // + // Catch illegal region size + // + if ((MmUserProbeAddress - (ULONG_PTR)BaseAddress) < NumberOfBytesToProtect) + { + // + // Fail + // + return STATUS_INVALID_PARAMETER_3; + } + + // + // 0 is also illegal + // + if (!NumberOfBytesToProtect) return STATUS_INVALID_PARAMETER_3; + + // + // Get a reference to the process + // + Status = ObReferenceObjectByHandle(ProcessHandle, + PROCESS_VM_OPERATION, + PsProcessType, + PreviousMode, + (PVOID*)(&Process), + NULL); + if (!NT_SUCCESS(Status)) return Status; + + // + // Check if we should attach + // + if (CurrentProcess != Process) + { + // + // Do it + // + KeStackAttachProcess(&Process->Pcb, &ApcState); + Attached = TRUE; + } + + // + // Do the actual work + // + Status = MiProtectVirtualMemory(Process, + &BaseAddress, + &NumberOfBytesToProtect, + NewAccessProtection, + &OldAccessProtection); + + // + // Detach if needed + // + if (Attached) KeUnstackDetachProcess(&ApcState); + + // + // Release reference + // + ObDereferenceObject(Process); + + // + // Enter SEH to return data + // + _SEH2_TRY + { + // + // Return data to user + // + *UnsafeOldAccessProtection = OldAccessProtection; + *UnsafeBaseAddress = BaseAddress; + *UnsafeNumberOfBytesToProtect = NumberOfBytesToProtect; + } + _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) + { + } + _SEH2_END; + + // + // Return status + // + return Status; +} + +NTSTATUS +NTAPI +NtLockVirtualMemory(IN HANDLE ProcessHandle, + IN OUT PVOID *BaseAddress, + IN OUT PSIZE_T NumberOfBytesToLock, + IN ULONG MapType) +{ + PEPROCESS Process; + PEPROCESS CurrentProcess = PsGetCurrentProcess(); + NTSTATUS Status; + BOOLEAN Attached = FALSE; + KAPC_STATE ApcState; + KPROCESSOR_MODE PreviousMode = ExGetPreviousMode(); + PVOID CapturedBaseAddress; + SIZE_T CapturedBytesToLock; + PAGED_CODE(); + + // + // Validate flags + // + if ((MapType & ~(MAP_PROCESS | MAP_SYSTEM))) + { + // + // Invalid set of flags + // + return STATUS_INVALID_PARAMETER; + } + + // + // At least one flag must be specified + // + if (!(MapType & (MAP_PROCESS | MAP_SYSTEM))) + { + // + // No flag given + // + return STATUS_INVALID_PARAMETER; + } + + // + // Enter SEH for probing + // + _SEH2_TRY + { + // + // Validate output data + // + ProbeForWritePointer(BaseAddress); + ProbeForWriteSize_t(NumberOfBytesToLock); + + // + // Capture it + // + CapturedBaseAddress = *BaseAddress; + CapturedBytesToLock = *NumberOfBytesToLock; + } + _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) + { + // + // Get exception code + // + _SEH2_YIELD(return _SEH2_GetExceptionCode()); + } + _SEH2_END; + + // + // Catch illegal base address + // + if (CapturedBaseAddress > MM_HIGHEST_USER_ADDRESS) return STATUS_INVALID_PARAMETER; + + // + // Catch illegal region size + // + if ((MmUserProbeAddress - (ULONG_PTR)CapturedBaseAddress) < CapturedBytesToLock) + { + // + // Fail + // + return STATUS_INVALID_PARAMETER; + } + + // + // 0 is also illegal + // + if (!CapturedBytesToLock) return STATUS_INVALID_PARAMETER; + + // + // Get a reference to the process + // + Status = ObReferenceObjectByHandle(ProcessHandle, + PROCESS_VM_OPERATION, + PsProcessType, + PreviousMode, + (PVOID*)(&Process), + NULL); + if (!NT_SUCCESS(Status)) return Status; + + // + // Check if this is is system-mapped + // + if (MapType & MAP_SYSTEM) + { + // + // Check for required privilege + // + if (!SeSinglePrivilegeCheck(SeLockMemoryPrivilege, PreviousMode)) + { + // + // Fail: Don't have it + // + ObDereferenceObject(Process); + return STATUS_PRIVILEGE_NOT_HELD; + } + } + + // + // Check if we should attach + // + if (CurrentProcess != Process) + { + // + // Do it + // + KeStackAttachProcess(&Process->Pcb, &ApcState); + Attached = TRUE; + } + + // + // Oops :( + // + UNIMPLEMENTED; + + // + // Detach if needed + // + if (Attached) KeUnstackDetachProcess(&ApcState); + + // + // Release reference + // + ObDereferenceObject(Process); + + // + // Enter SEH to return data + // + _SEH2_TRY + { + // + // Return data to user + // + *BaseAddress = CapturedBaseAddress; + *NumberOfBytesToLock = 0; + } + _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) + { + // + // Get exception code + // + _SEH2_YIELD(return _SEH2_GetExceptionCode()); + } + _SEH2_END; + + // + // Return status + // + return STATUS_SUCCESS; +} + +NTSTATUS +NTAPI +NtUnlockVirtualMemory(IN HANDLE ProcessHandle, + IN OUT PVOID *BaseAddress, + IN OUT PSIZE_T NumberOfBytesToUnlock, + IN ULONG MapType) +{ + PEPROCESS Process; + PEPROCESS CurrentProcess = PsGetCurrentProcess(); + NTSTATUS Status; + BOOLEAN Attached = FALSE; + KAPC_STATE ApcState; + KPROCESSOR_MODE PreviousMode = ExGetPreviousMode(); + PVOID CapturedBaseAddress; + SIZE_T CapturedBytesToUnlock; + PAGED_CODE(); + + // + // Validate flags + // + if ((MapType & ~(MAP_PROCESS | MAP_SYSTEM))) + { + // + // Invalid set of flags + // + return STATUS_INVALID_PARAMETER; + } + + // + // At least one flag must be specified + // + if (!(MapType & (MAP_PROCESS | MAP_SYSTEM))) + { + // + // No flag given + // + return STATUS_INVALID_PARAMETER; + } + + // + // Enter SEH for probing + // + _SEH2_TRY + { + // + // Validate output data + // + ProbeForWritePointer(BaseAddress); + ProbeForWriteSize_t(NumberOfBytesToUnlock); + + // + // Capture it + // + CapturedBaseAddress = *BaseAddress; + CapturedBytesToUnlock = *NumberOfBytesToUnlock; + } + _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) + { + // + // Get exception code + // + _SEH2_YIELD(return _SEH2_GetExceptionCode()); + } + _SEH2_END; + + // + // Catch illegal base address + // + if (CapturedBaseAddress > MM_HIGHEST_USER_ADDRESS) return STATUS_INVALID_PARAMETER; + + // + // Catch illegal region size + // + if ((MmUserProbeAddress - (ULONG_PTR)CapturedBaseAddress) < CapturedBytesToUnlock) + { + // + // Fail + // + return STATUS_INVALID_PARAMETER; + } + + // + // 0 is also illegal + // + if (!CapturedBytesToUnlock) return STATUS_INVALID_PARAMETER; + + // + // Get a reference to the process + // + Status = ObReferenceObjectByHandle(ProcessHandle, + PROCESS_VM_OPERATION, + PsProcessType, + PreviousMode, + (PVOID*)(&Process), + NULL); + if (!NT_SUCCESS(Status)) return Status; + + // + // Check if this is is system-mapped + // + if (MapType & MAP_SYSTEM) + { + // + // Check for required privilege + // + if (!SeSinglePrivilegeCheck(SeLockMemoryPrivilege, PreviousMode)) + { + // + // Fail: Don't have it + // + ObDereferenceObject(Process); + return STATUS_PRIVILEGE_NOT_HELD; + } + } + + // + // Check if we should attach + // + if (CurrentProcess != Process) + { + // + // Do it + // + KeStackAttachProcess(&Process->Pcb, &ApcState); + Attached = TRUE; + } + + // + // Oops :( + // + UNIMPLEMENTED; + + // + // Detach if needed + // + if (Attached) KeUnstackDetachProcess(&ApcState); + + // + // Release reference + // + ObDereferenceObject(Process); + + // + // Enter SEH to return data + // + _SEH2_TRY + { + // + // Return data to user + // + *BaseAddress = PAGE_ALIGN(CapturedBaseAddress); + *NumberOfBytesToUnlock = 0; + } + _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) + { + // + // Get exception code + // + _SEH2_YIELD(return _SEH2_GetExceptionCode()); + } + _SEH2_END; + + // + // Return status + // + return STATUS_SUCCESS; +} + +NTSTATUS +NTAPI +NtFlushVirtualMemory(IN HANDLE ProcessHandle, + IN OUT PVOID *BaseAddress, + IN OUT PSIZE_T NumberOfBytesToFlush, + OUT PIO_STATUS_BLOCK IoStatusBlock) +{ + PEPROCESS Process; + NTSTATUS Status; + KPROCESSOR_MODE PreviousMode = ExGetPreviousMode(); + PVOID CapturedBaseAddress; + SIZE_T CapturedBytesToFlush; + IO_STATUS_BLOCK LocalStatusBlock; + PAGED_CODE(); + + // + // Check if we came from user mode + // + if (PreviousMode != KernelMode) + { + // + // Enter SEH for probing + // + _SEH2_TRY + { + // + // Validate all outputs + // + ProbeForWritePointer(BaseAddress); + ProbeForWriteSize_t(NumberOfBytesToFlush); + ProbeForWriteIoStatusBlock(IoStatusBlock); + + // + // Capture them + // + CapturedBaseAddress = *BaseAddress; + CapturedBytesToFlush = *NumberOfBytesToFlush; + } + _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) + { + // + // Get exception code + // + _SEH2_YIELD(return _SEH2_GetExceptionCode()); + } + _SEH2_END; + } + else + { + // + // Capture directly + // + CapturedBaseAddress = *BaseAddress; + CapturedBytesToFlush = *NumberOfBytesToFlush; + } + + // + // Catch illegal base address + // + if (CapturedBaseAddress > MM_HIGHEST_USER_ADDRESS) return STATUS_INVALID_PARAMETER; + + // + // Catch illegal region size + // + if ((MmUserProbeAddress - (ULONG_PTR)CapturedBaseAddress) < CapturedBytesToFlush) + { + // + // Fail + // + return STATUS_INVALID_PARAMETER; + } + + // + // Get a reference to the process + // + Status = ObReferenceObjectByHandle(ProcessHandle, + PROCESS_VM_OPERATION, + PsProcessType, + PreviousMode, + (PVOID*)(&Process), + NULL); + if (!NT_SUCCESS(Status)) return Status; + + // + // Do it + // + Status = MmFlushVirtualMemory(Process, + &CapturedBaseAddress, + &CapturedBytesToFlush, + &LocalStatusBlock); + + // + // Release reference + // + ObDereferenceObject(Process); + + // + // Enter SEH to return data + // + _SEH2_TRY + { + // + // Return data to user + // + *BaseAddress = PAGE_ALIGN(CapturedBaseAddress); + *NumberOfBytesToFlush = 0; + *IoStatusBlock = LocalStatusBlock; + } + _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) + { + } + _SEH2_END; + + // + // Return status + // + return Status; +} + +/* + * @unimplemented + */ +NTSTATUS +NTAPI +NtGetWriteWatch(IN HANDLE ProcessHandle, + IN ULONG Flags, + IN PVOID BaseAddress, + IN SIZE_T RegionSize, + IN PVOID *UserAddressArray, + OUT PULONG_PTR EntriesInUserAddressArray, + OUT PULONG Granularity) +{ + PEPROCESS Process; + NTSTATUS Status; + PVOID EndAddress; + KPROCESSOR_MODE PreviousMode = ExGetPreviousMode(); + ULONG_PTR CapturedEntryCount; + PAGED_CODE(); + + // + // Check if we came from user mode + // + if (PreviousMode != KernelMode) + { + // + // Enter SEH for probing + // + _SEH2_TRY + { + // + // Catch illegal base address + // + if (BaseAddress > MM_HIGHEST_USER_ADDRESS) return STATUS_INVALID_PARAMETER_2; + + // + // Catch illegal region size + // + if ((MmUserProbeAddress - (ULONG_PTR)BaseAddress) < RegionSize) + { + // + // Fail + // + return STATUS_INVALID_PARAMETER_3; + } + + // + // Validate all data + // + ProbeForWriteSize_t(EntriesInUserAddressArray); + ProbeForWriteUlong(Granularity); + + // + // Capture them + // + CapturedEntryCount = *EntriesInUserAddressArray; + + // + // Must have a count + // + if (CapturedEntryCount == 0) return STATUS_INVALID_PARAMETER_5; + + // + // Can't be larger than the maximum + // + if (CapturedEntryCount > (MAXULONG_PTR / sizeof(ULONG_PTR))) + { + // + // Fail + // + return STATUS_INVALID_PARAMETER_5; + } + + // + // Probe the actual array + // + ProbeForWrite(UserAddressArray, + CapturedEntryCount * sizeof(PVOID), + sizeof(PVOID)); + } + _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) + { + // + // Get exception code + // + _SEH2_YIELD(return _SEH2_GetExceptionCode()); + } + _SEH2_END; + } + else + { + // + // Capture directly + // + CapturedEntryCount = *EntriesInUserAddressArray; + ASSERT(CapturedEntryCount != 0); + } + + // + // Check if this is a local request + // + if (ProcessHandle == NtCurrentProcess()) + { + // + // No need to reference the process + // + Process = PsGetCurrentProcess(); + } + else + { + // + // Reference the target + // + Status = ObReferenceObjectByHandle(ProcessHandle, + PROCESS_VM_OPERATION, + PsProcessType, + PreviousMode, + (PVOID *)&Process, + NULL); + if (!NT_SUCCESS(Status)) return Status; + } + + // + // Compute the last address and validate it + // + EndAddress = (PVOID)((ULONG_PTR)BaseAddress + RegionSize - 1); + if (BaseAddress > EndAddress) + { + // + // Fail + // + if (ProcessHandle != NtCurrentProcess()) ObDereferenceObject(Process); + return STATUS_INVALID_PARAMETER_4; + } + + // + // Oops :( + // + UNIMPLEMENTED; + + // + // Dereference if needed + // + if (ProcessHandle != NtCurrentProcess()) ObDereferenceObject(Process); + + // + // Enter SEH to return data + // + _SEH2_TRY + { + // + // Return data to user + // + *EntriesInUserAddressArray = 0; + *Granularity = PAGE_SIZE; + } + _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) + { + // + // Get exception code + // + Status = _SEH2_GetExceptionCode(); + } + _SEH2_END; + + // + // Return success + // + return STATUS_SUCCESS; +} + +/* + * @unimplemented + */ +NTSTATUS +NTAPI +NtResetWriteWatch(IN HANDLE ProcessHandle, + IN PVOID BaseAddress, + IN SIZE_T RegionSize) +{ + PVOID EndAddress; + PEPROCESS Process; + NTSTATUS Status; + KPROCESSOR_MODE PreviousMode = ExGetPreviousMode(); + ASSERT (KeGetCurrentIrql() == PASSIVE_LEVEL); + + // + // Catch illegal base address + // + if (BaseAddress > MM_HIGHEST_USER_ADDRESS) return STATUS_INVALID_PARAMETER_2; + + // + // Catch illegal region size + // + if ((MmUserProbeAddress - (ULONG_PTR)BaseAddress) < RegionSize) + { + // + // Fail + // + return STATUS_INVALID_PARAMETER_3; + } + + // + // Check if this is a local request + // + if (ProcessHandle == NtCurrentProcess()) + { + // + // No need to reference the process + // + Process = PsGetCurrentProcess(); + } + else + { + // + // Reference the target + // + Status = ObReferenceObjectByHandle(ProcessHandle, + PROCESS_VM_OPERATION, + PsProcessType, + PreviousMode, + (PVOID *)&Process, + NULL); + if (!NT_SUCCESS(Status)) return Status; + } + + // + // Compute the last address and validate it + // + EndAddress = (PVOID)((ULONG_PTR)BaseAddress + RegionSize - 1); + if (BaseAddress > EndAddress) + { + // + // Fail + // + if (ProcessHandle != NtCurrentProcess()) ObDereferenceObject(Process); + return STATUS_INVALID_PARAMETER_3; + } + + // + // Oops :( + // + UNIMPLEMENTED; + + // + // Dereference if needed + // + if (ProcessHandle != NtCurrentProcess()) ObDereferenceObject(Process); + + // + // Return success + // + return STATUS_SUCCESS; +} + +/* EOF */ diff --git a/reactos/ntoskrnl/mm/virtual.c b/reactos/ntoskrnl/mm/virtual.c index cc9464995e6..8e6e845290b 100644 --- a/reactos/ntoskrnl/mm/virtual.c +++ b/reactos/ntoskrnl/mm/virtual.c @@ -13,392 +13,8 @@ #define NDEBUG #include -#define MI_MAPPED_COPY_PAGES 16 -#define MI_POOL_COPY_BYTES 512 -#define MI_MAX_TRANSFER_SIZE 64 * 1024 -#define TAG_VM 'wRmV' - /* PRIVATE FUNCTIONS **********************************************************/ -LONG -MiGetExceptionInfo(EXCEPTION_POINTERS *ExceptionInfo, BOOLEAN * HaveBadAddress, ULONG_PTR * BadAddress) -{ - PEXCEPTION_RECORD ExceptionRecord; - PAGED_CODE(); - - /* Assume default */ - *HaveBadAddress = FALSE; - - /* Get the exception record */ - ExceptionRecord = ExceptionInfo->ExceptionRecord; - - /* Look at the exception code */ - if ((ExceptionRecord->ExceptionCode == STATUS_ACCESS_VIOLATION) || - (ExceptionRecord->ExceptionCode == STATUS_GUARD_PAGE_VIOLATION) || - (ExceptionRecord->ExceptionCode == STATUS_IN_PAGE_ERROR)) - { - /* We can tell the address if we have more than one parameter */ - if (ExceptionRecord->NumberParameters > 1) - { - /* Return the address */ - *HaveBadAddress = TRUE; - *BadAddress = ExceptionRecord->ExceptionInformation[1]; - } - } - - /* Continue executing the next handler */ - return EXCEPTION_EXECUTE_HANDLER; -} - -NTSTATUS -NTAPI -MiDoMappedCopy(IN PEPROCESS SourceProcess, - IN PVOID SourceAddress, - IN PEPROCESS TargetProcess, - OUT PVOID TargetAddress, - IN SIZE_T BufferSize, - IN KPROCESSOR_MODE PreviousMode, - OUT PSIZE_T ReturnSize) -{ - PFN_NUMBER MdlBuffer[(sizeof(MDL) / sizeof(PFN_NUMBER)) + MI_MAPPED_COPY_PAGES + 1]; - PMDL Mdl = (PMDL)MdlBuffer; - SIZE_T TotalSize, CurrentSize, RemainingSize; - volatile BOOLEAN FailedInProbe = FALSE, FailedInMapping = FALSE, FailedInMoving; - volatile BOOLEAN PagesLocked; - PVOID CurrentAddress = SourceAddress, CurrentTargetAddress = TargetAddress; - volatile PVOID MdlAddress; - KAPC_STATE ApcState; - BOOLEAN HaveBadAddress; - ULONG_PTR BadAddress; - PAGED_CODE(); - - /* Calculate the maximum amount of data to move */ - TotalSize = (MI_MAPPED_COPY_PAGES - 2) * PAGE_SIZE; - if (BufferSize <= TotalSize) TotalSize = BufferSize; - CurrentSize = TotalSize; - RemainingSize = BufferSize; - - /* Loop as long as there is still data */ - while (RemainingSize > 0) - { - /* Check if this transfer will finish everything off */ - if (RemainingSize < CurrentSize) CurrentSize = RemainingSize; - - /* Attach to the source address space */ - KeStackAttachProcess(&SourceProcess->Pcb, &ApcState); - - /* Reset state for this pass */ - MdlAddress = NULL; - PagesLocked = FALSE; - FailedInMoving = FALSE; - ASSERT(FailedInProbe == FALSE); - - /* Protect user-mode copy */ - _SEH2_TRY - { - /* If this is our first time, probe the buffer */ - if ((CurrentAddress == SourceAddress) && (PreviousMode != KernelMode)) - { - /* Catch a failure here */ - FailedInProbe = TRUE; - - /* Do the probe */ - ProbeForRead(SourceAddress, BufferSize, sizeof(CHAR)); - - /* Passed */ - FailedInProbe = FALSE; - } - - /* Initialize and probe and lock the MDL */ - MmInitializeMdl (Mdl, CurrentAddress, CurrentSize); - MmProbeAndLockPages (Mdl, PreviousMode, IoReadAccess); - PagesLocked = TRUE; - - /* Now map the pages */ - MdlAddress = MmMapLockedPagesSpecifyCache(Mdl, - KernelMode, - MmCached, - NULL, - FALSE, - HighPagePriority); - if (!MdlAddress) - { - /* Use our SEH handler to pick this up */ - FailedInMapping = TRUE; - ExRaiseStatus(STATUS_INSUFFICIENT_RESOURCES); - } - - /* Now let go of the source and grab to the target process */ - KeUnstackDetachProcess(&ApcState); - KeStackAttachProcess(&TargetProcess->Pcb, &ApcState); - - /* Check if this is our first time through */ - if ((CurrentAddress == SourceAddress) && (PreviousMode != KernelMode)) - { - /* Catch a failure here */ - FailedInProbe = TRUE; - - /* Do the probe */ - ProbeForWrite(TargetAddress, BufferSize, sizeof(CHAR)); - - /* Passed */ - FailedInProbe = FALSE; - } - - /* Now do the actual move */ - FailedInMoving = TRUE; - RtlCopyMemory(CurrentTargetAddress, MdlAddress, CurrentSize); - } - _SEH2_EXCEPT(MiGetExceptionInfo(_SEH2_GetExceptionInformation(), &HaveBadAddress, &BadAddress)) - { - /* Detach from whoever we may be attached to */ - KeUnstackDetachProcess(&ApcState); - - /* Check if we had mapped the pages */ - if (MdlAddress) MmUnmapLockedPages(MdlAddress, Mdl); - - /* Check if we had locked the pages */ - if (PagesLocked) MmUnlockPages(Mdl); - - /* Check if we failed during the probe or mapping */ - if ((FailedInProbe) || (FailedInMapping)) - { - /* Exit */ - _SEH2_YIELD(return _SEH2_GetExceptionCode()); - } - - /* Otherwise, we failed probably during the move */ - *ReturnSize = BufferSize - RemainingSize; - if (FailedInMoving) - { - /* Check if we know exactly where we stopped copying */ - if (HaveBadAddress) - { - /* Return the exact number of bytes copied */ - *ReturnSize = BadAddress - (ULONG_PTR)SourceAddress; - } - } - - /* Return partial copy */ - _SEH2_YIELD(return STATUS_PARTIAL_COPY); - } - _SEH2_END; - - /* Detach from target */ - KeUnstackDetachProcess(&ApcState); - - /* Unmap and unlock */ - MmUnmapLockedPages(MdlAddress, Mdl); - MmUnlockPages(Mdl); - - /* Update location and size */ - RemainingSize -= CurrentSize; - CurrentAddress = (PVOID)((ULONG_PTR)CurrentAddress + CurrentSize); - CurrentTargetAddress = (PVOID)((ULONG_PTR)CurrentTargetAddress + CurrentSize); - } - - /* All bytes read */ - *ReturnSize = BufferSize; - return STATUS_SUCCESS; -} - -NTSTATUS -NTAPI -MiDoPoolCopy(IN PEPROCESS SourceProcess, - IN PVOID SourceAddress, - IN PEPROCESS TargetProcess, - OUT PVOID TargetAddress, - IN SIZE_T BufferSize, - IN KPROCESSOR_MODE PreviousMode, - OUT PSIZE_T ReturnSize) -{ - UCHAR StackBuffer[MI_POOL_COPY_BYTES]; - SIZE_T TotalSize, CurrentSize, RemainingSize; - volatile BOOLEAN FailedInProbe = FALSE, FailedInMoving, HavePoolAddress = FALSE; - PVOID CurrentAddress = SourceAddress, CurrentTargetAddress = TargetAddress; - PVOID PoolAddress; - KAPC_STATE ApcState; - BOOLEAN HaveBadAddress; - ULONG_PTR BadAddress; - PAGED_CODE(); - - /* Calculate the maximum amount of data to move */ - TotalSize = MI_MAX_TRANSFER_SIZE; - if (BufferSize <= MI_MAX_TRANSFER_SIZE) TotalSize = BufferSize; - CurrentSize = TotalSize; - RemainingSize = BufferSize; - - /* Check if we can use the stack */ - if (BufferSize <= MI_POOL_COPY_BYTES) - { - /* Use it */ - PoolAddress = (PVOID)StackBuffer; - } - else - { - /* Allocate pool */ - PoolAddress = ExAllocatePoolWithTag(NonPagedPool, TotalSize, TAG_VM); - if (!PoolAddress) ASSERT(FALSE); - HavePoolAddress = TRUE; - } - - /* Loop as long as there is still data */ - while (RemainingSize > 0) - { - /* Check if this transfer will finish everything off */ - if (RemainingSize < CurrentSize) CurrentSize = RemainingSize; - - /* Attach to the source address space */ - KeStackAttachProcess(&SourceProcess->Pcb, &ApcState); - - /* Reset state for this pass */ - FailedInMoving = FALSE; - ASSERT(FailedInProbe == FALSE); - - /* Protect user-mode copy */ - _SEH2_TRY - { - /* If this is our first time, probe the buffer */ - if ((CurrentAddress == SourceAddress) && (PreviousMode != KernelMode)) - { - /* Catch a failure here */ - FailedInProbe = TRUE; - - /* Do the probe */ - ProbeForRead(SourceAddress, BufferSize, sizeof(CHAR)); - - /* Passed */ - FailedInProbe = FALSE; - } - - /* Do the copy */ - RtlCopyMemory(PoolAddress, CurrentAddress, CurrentSize); - - /* Now let go of the source and grab to the target process */ - KeUnstackDetachProcess(&ApcState); - KeStackAttachProcess(&TargetProcess->Pcb, &ApcState); - - /* Check if this is our first time through */ - if ((CurrentAddress == SourceAddress) && (PreviousMode != KernelMode)) - { - /* Catch a failure here */ - FailedInProbe = TRUE; - - /* Do the probe */ - ProbeForWrite(TargetAddress, BufferSize, sizeof(CHAR)); - - /* Passed */ - FailedInProbe = FALSE; - } - - /* Now do the actual move */ - FailedInMoving = TRUE; - RtlCopyMemory(CurrentTargetAddress, PoolAddress, CurrentSize); - } - _SEH2_EXCEPT(MiGetExceptionInfo(_SEH2_GetExceptionInformation(), &HaveBadAddress, &BadAddress)) - { - /* Detach from whoever we may be attached to */ - KeUnstackDetachProcess(&ApcState); - - /* Check if we had allocated pool */ - if (HavePoolAddress) ExFreePool(PoolAddress); - - /* Check if we failed during the probe */ - if (FailedInProbe) - { - /* Exit */ - _SEH2_YIELD(return _SEH2_GetExceptionCode()); - } - - /* Otherwise, we failed probably during the move */ - *ReturnSize = BufferSize - RemainingSize; - if (FailedInMoving) - { - /* Check if we know exactly where we stopped copying */ - if (HaveBadAddress) - { - /* Return the exact number of bytes copied */ - *ReturnSize = BadAddress - (ULONG_PTR)SourceAddress; - } - } - - /* Return partial copy */ - _SEH2_YIELD(return STATUS_PARTIAL_COPY); - } - _SEH2_END; - - /* Detach from target */ - KeUnstackDetachProcess(&ApcState); - - /* Update location and size */ - RemainingSize -= CurrentSize; - CurrentAddress = (PVOID)((ULONG_PTR)CurrentAddress + CurrentSize); - CurrentTargetAddress = (PVOID)((ULONG_PTR)CurrentTargetAddress + CurrentSize); - } - - /* Check if we had allocated pool */ - if (HavePoolAddress) ExFreePool(PoolAddress); - - /* All bytes read */ - *ReturnSize = BufferSize; - return STATUS_SUCCESS; -} - -NTSTATUS -NTAPI -MmCopyVirtualMemory(IN PEPROCESS SourceProcess, - IN PVOID SourceAddress, - IN PEPROCESS TargetProcess, - OUT PVOID TargetAddress, - IN SIZE_T BufferSize, - IN KPROCESSOR_MODE PreviousMode, - OUT PSIZE_T ReturnSize) -{ - NTSTATUS Status; - PEPROCESS Process = SourceProcess; - - /* Don't accept zero-sized buffers */ - if (!BufferSize) return STATUS_SUCCESS; - - /* If we are copying from ourselves, lock the target instead */ - if (SourceProcess == PsGetCurrentProcess()) Process = TargetProcess; - - /* Acquire rundown protection */ - if (!ExAcquireRundownProtection(&Process->RundownProtect)) - { - /* Fail */ - return STATUS_PROCESS_IS_TERMINATING; - } - - /* See if we should use the pool copy */ - if (BufferSize > MI_POOL_COPY_BYTES) - { - /* Use MDL-copy */ - Status = MiDoMappedCopy(SourceProcess, - SourceAddress, - TargetProcess, - TargetAddress, - BufferSize, - PreviousMode, - ReturnSize); - } - else - { - /* Do pool copy */ - Status = MiDoPoolCopy(SourceProcess, - SourceAddress, - TargetProcess, - TargetAddress, - BufferSize, - PreviousMode, - ReturnSize); - } - - /* Release the lock */ - ExReleaseRundownProtection(&Process->RundownProtect); - return Status; -} - NTSTATUS FASTCALL MiQueryVirtualMemory(IN HANDLE ProcessHandle, IN PVOID Address, @@ -721,323 +337,8 @@ MiUnmapLockedPagesInUserSpace(IN PVOID BaseAddress, NULL); } -/* PUBLIC FUNCTIONS ***********************************************************/ - -/* - * @unimplemented - */ -PVOID -NTAPI -MmGetVirtualForPhysical(IN PHYSICAL_ADDRESS PhysicalAddress) -{ - UNIMPLEMENTED; - return 0; -} - -/* - * @unimplemented - */ -PVOID -NTAPI -MmSecureVirtualMemory(IN PVOID Address, - IN SIZE_T Length, - IN ULONG Mode) -{ - UNIMPLEMENTED; - return NULL; -} - -/* - * @unimplemented - */ -VOID -NTAPI -MmUnsecureVirtualMemory(IN PVOID SecureMem) -{ - UNIMPLEMENTED; -} - /* SYSTEM CALLS ***************************************************************/ -NTSTATUS -NTAPI -NtReadVirtualMemory(IN HANDLE ProcessHandle, - IN PVOID BaseAddress, - OUT PVOID Buffer, - IN SIZE_T NumberOfBytesToRead, - OUT PSIZE_T NumberOfBytesRead OPTIONAL) -{ - KPROCESSOR_MODE PreviousMode = ExGetPreviousMode(); - PEPROCESS Process; - NTSTATUS Status; - SIZE_T BytesRead = 0; - PAGED_CODE(); - - /* Check if we came from user mode */ - if (PreviousMode != KernelMode) - { - /* Validate the read addresses */ - if ((((ULONG_PTR)BaseAddress + NumberOfBytesToRead) < (ULONG_PTR)BaseAddress) || - (((ULONG_PTR)Buffer + NumberOfBytesToRead) < (ULONG_PTR)Buffer) || - (((ULONG_PTR)BaseAddress + NumberOfBytesToRead) > MmUserProbeAddress) || - (((ULONG_PTR)Buffer + NumberOfBytesToRead) > MmUserProbeAddress)) - { - /* Don't allow to write into kernel space */ - return STATUS_ACCESS_VIOLATION; - } - - /* Enter SEH for probe */ - _SEH2_TRY - { - /* Probe the output value */ - if (NumberOfBytesRead) ProbeForWriteSize_t(NumberOfBytesRead); - } - _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) - { - /* Return the exception code */ - _SEH2_YIELD(return _SEH2_GetExceptionCode()); - } - _SEH2_END; - } - - /* Reference the process */ - Status = ObReferenceObjectByHandle(ProcessHandle, - PROCESS_VM_READ, - PsProcessType, - PreviousMode, - (PVOID*)(&Process), - NULL); - if (NT_SUCCESS(Status)) - { - /* Do the copy */ - Status = MmCopyVirtualMemory(Process, - BaseAddress, - PsGetCurrentProcess(), - Buffer, - NumberOfBytesToRead, - PreviousMode, - &BytesRead); - - /* Dereference the process */ - ObDereferenceObject(Process); - } - - /* Check if the caller sent this parameter */ - if (NumberOfBytesRead) - { - /* Enter SEH to guard write */ - _SEH2_TRY - { - /* Return the number of bytes read */ - *NumberOfBytesRead = BytesRead; - } - _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) - { - /* Handle exception */ - Status = _SEH2_GetExceptionCode(); - } - _SEH2_END; - } - - /* Return status */ - return Status; -} - -NTSTATUS -NTAPI -NtWriteVirtualMemory(IN HANDLE ProcessHandle, - IN PVOID BaseAddress, - IN PVOID Buffer, - IN SIZE_T NumberOfBytesToWrite, - OUT PSIZE_T NumberOfBytesWritten OPTIONAL) -{ - KPROCESSOR_MODE PreviousMode = ExGetPreviousMode(); - PEPROCESS Process; - NTSTATUS Status; - ULONG BytesWritten = 0; - PAGED_CODE(); - - /* Check if we came from user mode */ - if (PreviousMode != KernelMode) - { - /* Validate the read addresses */ - if ((((ULONG_PTR)BaseAddress + NumberOfBytesToWrite) < (ULONG_PTR)BaseAddress) || - (((ULONG_PTR)Buffer + NumberOfBytesToWrite) < (ULONG_PTR)Buffer) || - (((ULONG_PTR)BaseAddress + NumberOfBytesToWrite) > MmUserProbeAddress) || - (((ULONG_PTR)Buffer + NumberOfBytesToWrite) > MmUserProbeAddress)) - { - /* Don't allow to write into kernel space */ - return STATUS_ACCESS_VIOLATION; - } - - /* Enter SEH for probe */ - _SEH2_TRY - { - /* Probe the output value */ - if (NumberOfBytesWritten) ProbeForWriteSize_t(NumberOfBytesWritten); - } - _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) - { - /* Return the exception code */ - _SEH2_YIELD(return _SEH2_GetExceptionCode()); - } - _SEH2_END; - } - - /* Reference the process */ - Status = ObReferenceObjectByHandle(ProcessHandle, - PROCESS_VM_WRITE, - PsProcessType, - PreviousMode, - (PVOID*)&Process, - NULL); - if (NT_SUCCESS(Status)) - { - /* Do the copy */ - Status = MmCopyVirtualMemory(PsGetCurrentProcess(), - Buffer, - Process, - BaseAddress, - NumberOfBytesToWrite, - PreviousMode, - &BytesWritten); - - /* Dereference the process */ - ObDereferenceObject(Process); - } - - /* Check if the caller sent this parameter */ - if (NumberOfBytesWritten) - { - /* Enter SEH to guard write */ - _SEH2_TRY - { - /* Return the number of bytes read */ - *NumberOfBytesWritten = BytesWritten; - } - _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) - { - /* Handle exception */ - Status = _SEH2_GetExceptionCode(); - } - _SEH2_END; - } - - /* Return status */ - return Status; -} - -NTSTATUS -NTAPI -NtProtectVirtualMemory(IN HANDLE ProcessHandle, - IN OUT PVOID *UnsafeBaseAddress, - IN OUT SIZE_T *UnsafeNumberOfBytesToProtect, - IN ULONG NewAccessProtection, - OUT PULONG UnsafeOldAccessProtection) -{ - PEPROCESS Process; - ULONG OldAccessProtection; - ULONG Protection; - PVOID BaseAddress = NULL; - SIZE_T NumberOfBytesToProtect = 0; - KPROCESSOR_MODE PreviousMode = ExGetPreviousMode(); - NTSTATUS Status; - PAGED_CODE(); - - /* Check for valid protection flags */ - Protection = NewAccessProtection & ~(PAGE_GUARD|PAGE_NOCACHE); - if (Protection != PAGE_NOACCESS && - Protection != PAGE_READONLY && - Protection != PAGE_READWRITE && - Protection != PAGE_WRITECOPY && - Protection != PAGE_EXECUTE && - Protection != PAGE_EXECUTE_READ && - Protection != PAGE_EXECUTE_READWRITE && - Protection != PAGE_EXECUTE_WRITECOPY) - { - return STATUS_INVALID_PAGE_PROTECTION; - } - - /* Check if we came from user mode */ - if (PreviousMode != KernelMode) - { - /* Enter SEH for probing */ - _SEH2_TRY - { - /* Validate all outputs */ - ProbeForWritePointer(UnsafeBaseAddress); - ProbeForWriteSize_t(UnsafeNumberOfBytesToProtect); - ProbeForWriteUlong(UnsafeOldAccessProtection); - - /* Capture them */ - BaseAddress = *UnsafeBaseAddress; - NumberOfBytesToProtect = *UnsafeNumberOfBytesToProtect; - } - _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) - { - /* Return the exception code */ - _SEH2_YIELD(return _SEH2_GetExceptionCode()); - } - _SEH2_END; - } - else - { - /* Capture directly */ - BaseAddress = *UnsafeBaseAddress; - NumberOfBytesToProtect = *UnsafeNumberOfBytesToProtect; - } - - /* Catch illegal base address */ - if (BaseAddress > (PVOID)MmUserProbeAddress) return STATUS_INVALID_PARAMETER_2; - - /* Catch illegal region size */ - if ((MmUserProbeAddress - (ULONG_PTR)BaseAddress) < NumberOfBytesToProtect) - { - /* Fail */ - return STATUS_INVALID_PARAMETER_3; - } - - /* 0 is also illegal */ - if (!NumberOfBytesToProtect) return STATUS_INVALID_PARAMETER_3; - - /* Get a reference to the process */ - Status = ObReferenceObjectByHandle(ProcessHandle, - PROCESS_VM_OPERATION, - PsProcessType, - PreviousMode, - (PVOID*)(&Process), - NULL); - if (!NT_SUCCESS(Status)) return Status; - - /* Do the actual work */ - Status = MiProtectVirtualMemory(Process, - &BaseAddress, - &NumberOfBytesToProtect, - NewAccessProtection, - &OldAccessProtection); - - /* Release reference */ - ObDereferenceObject(Process); - - /* Enter SEH to return data */ - _SEH2_TRY - { - /* Return data to user */ - *UnsafeOldAccessProtection = OldAccessProtection; - *UnsafeBaseAddress = BaseAddress; - *UnsafeNumberOfBytesToProtect = NumberOfBytesToProtect; - } - _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) - { - /* Catch exception */ - Status = _SEH2_GetExceptionCode(); - } - _SEH2_END; - - /* Return status */ - return Status; -} - NTSTATUS NTAPI NtQueryVirtualMemory(IN HANDLE ProcessHandle, IN PVOID Address, @@ -1202,92 +503,4 @@ NtQueryVirtualMemory(IN HANDLE ProcessHandle, return(Status); } -NTSTATUS -NTAPI -NtLockVirtualMemory(IN HANDLE ProcessHandle, - IN PVOID BaseAddress, - IN SIZE_T NumberOfBytesToLock, - OUT PSIZE_T NumberOfBytesLocked OPTIONAL) -{ - UNIMPLEMENTED; - if (NumberOfBytesLocked) *NumberOfBytesLocked = 0; - return STATUS_SUCCESS; -} - -NTSTATUS -NTAPI -NtUnlockVirtualMemory(IN HANDLE ProcessHandle, - IN PVOID BaseAddress, - IN SIZE_T NumberOfBytesToUnlock, - OUT PSIZE_T NumberOfBytesUnlocked OPTIONAL) -{ - UNIMPLEMENTED; - if (NumberOfBytesUnlocked) *NumberOfBytesUnlocked = 0; - return STATUS_SUCCESS; -} - -NTSTATUS -NTAPI -NtFlushVirtualMemory(IN HANDLE ProcessHandle, - IN OUT PVOID *BaseAddress, - IN OUT PSIZE_T NumberOfBytesToFlush, - OUT PIO_STATUS_BLOCK IoStatusBlock) -{ - UNIMPLEMENTED; - return STATUS_SUCCESS; -} - -/* - * @unimplemented - */ -NTSTATUS -NTAPI -NtGetWriteWatch(IN HANDLE ProcessHandle, - IN ULONG Flags, - IN PVOID BaseAddress, - IN ULONG RegionSize, - IN PVOID *UserAddressArray, - OUT PULONG EntriesInUserAddressArray, - OUT PULONG Granularity) -{ - if (!EntriesInUserAddressArray || !Granularity) - { - return STATUS_ACCESS_VIOLATION; - } - - if (!*EntriesInUserAddressArray || !RegionSize) - { - return STATUS_INVALID_PARAMETER; - } - - if (!UserAddressArray) - { - return STATUS_ACCESS_VIOLATION; - } - - /* HACK: Set granularity to PAGE_SIZE */ - *Granularity = PAGE_SIZE; - - UNIMPLEMENTED; - return STATUS_NOT_IMPLEMENTED; -} - -/* - * @unimplemented - */ -NTSTATUS -NTAPI -NtResetWriteWatch(IN HANDLE ProcessHandle, - IN PVOID BaseAddress, - IN ULONG RegionSize) -{ - if (!RegionSize) - { - return STATUS_INVALID_PARAMETER; - } - - UNIMPLEMENTED; - return STATUS_NOT_IMPLEMENTED; -} - /* EOF */ diff --git a/reactos/ntoskrnl/ntoskrnl-generic.rbuild b/reactos/ntoskrnl/ntoskrnl-generic.rbuild index 10c672c192e..bf90c3bf352 100644 --- a/reactos/ntoskrnl/ntoskrnl-generic.rbuild +++ b/reactos/ntoskrnl/ntoskrnl-generic.rbuild @@ -394,6 +394,7 @@ pool.c procsup.c syspte.c + virtual.c anonmem.c balance.c