Author: tkreuzer Date: Sun May 18 16:25:40 2014 New Revision: 63356
URL: http://svn.reactos.org/svn/reactos?rev=63356&view=rev Log: [NTOSKRNL] - Fix a bug in MiQueryAddressState that prevented it from returning a valid protection - Add support for PAE and x64 to MiQueryAddressState - Acquire the working set lock in MiQueryMemoryBasicInformation before MiQueryAddressState - Fix RegionSize calculation in MiQueryMemoryBasicInformation - Handle ZeroBits and Process->VmTopDown in NtAllocateVirtualMemory - Fix a bug in calculating the ending address of a virtual allocation - Gracefully handle Vad allocation failure - Free Vad allocation on failure - Write values back to usermode only in case of success
Modified: trunk/reactos/ntoskrnl/include/internal/amd64/mm.h trunk/reactos/ntoskrnl/mm/ARM3/miarm.h trunk/reactos/ntoskrnl/mm/ARM3/virtual.c
Modified: trunk/reactos/ntoskrnl/include/internal/amd64/mm.h URL: http://svn.reactos.org/svn/reactos/trunk/reactos/ntoskrnl/include/internal/a... ============================================================================== --- trunk/reactos/ntoskrnl/include/internal/amd64/mm.h [iso-8859-1] (original) +++ trunk/reactos/ntoskrnl/include/internal/amd64/mm.h [iso-8859-1] Sun May 18 16:25:40 2014 @@ -78,6 +78,7 @@ #define MI_ZERO_PTES (32) /* FIXME - different architectures have different cache line sizes... */ #define MM_CACHE_LINE_SIZE 32 +#define MI_MAX_ZERO_BITS 53
/* Helper macros */ #define PAGE_MASK(x) ((x)&(~0xfff)) @@ -143,11 +144,13 @@
//#define TEB_BASE 0x7FFDE000
-/* On x86, these two are the same */ +/* On x64, these are the same */ #define MMPDE MMPTE #define PMMPDE PMMPTE #define MMPPE MMPTE #define PMMPPE PMMPTE +#define MMPXE MMPTE +#define PMMPXE PMMPTE #define MI_WRITE_VALID_PPE MI_WRITE_VALID_PTE
#define ValidKernelPpe ValidKernelPde
Modified: trunk/reactos/ntoskrnl/mm/ARM3/miarm.h URL: http://svn.reactos.org/svn/reactos/trunk/reactos/ntoskrnl/mm/ARM3/miarm.h?re... ============================================================================== --- trunk/reactos/ntoskrnl/mm/ARM3/miarm.h [iso-8859-1] (original) +++ trunk/reactos/ntoskrnl/mm/ARM3/miarm.h [iso-8859-1] Sun May 18 16:25:40 2014 @@ -52,6 +52,7 @@ #define MI_LOWEST_VAD_ADDRESS (PVOID)MM_LOWEST_USER_ADDRESS
#define MI_DEFAULT_SYSTEM_PTE_COUNT 50000 +#define MI_MAX_ZERO_BITS 21
#endif /* !_M_AMD64 */
Modified: trunk/reactos/ntoskrnl/mm/ARM3/virtual.c URL: http://svn.reactos.org/svn/reactos/trunk/reactos/ntoskrnl/mm/ARM3/virtual.c?... ============================================================================== --- trunk/reactos/ntoskrnl/mm/ARM3/virtual.c [iso-8859-1] (original) +++ trunk/reactos/ntoskrnl/mm/ARM3/virtual.c [iso-8859-1] Sun May 18 16:25:40 2014 @@ -57,7 +57,7 @@ if (Vad->u.VadFlags.MemCommit == 1) { /* This is a committed VAD, so Assume the whole range is committed */ - CommittedPages = BYTES_TO_PAGES(EndingAddress - StartingAddress); + CommittedPages = (ULONG)BYTES_TO_PAGES(EndingAddress - StartingAddress);
/* Is the PDE demand-zero? */ PointerPde = MiAddressToPte(PointerPte); @@ -1309,6 +1309,12 @@
PMMPTE PointerPte, ProtoPte; PMMPDE PointerPde; +#if (_MI_PAGING_LEVELS >= 3) + PMMPPE PointerPpe; +#endif +#if (_MI_PAGING_LEVELS >= 4) + PMMPXE PointerPxe; +#endif MMPTE TempPte, TempProtoPte; BOOLEAN DemandZeroPte = TRUE, ValidPte = FALSE; ULONG State = MEM_RESERVE, Protect = 0; @@ -1321,27 +1327,70 @@ /* Get the PDE and PTE for the address */ PointerPde = MiAddressToPde(Va); PointerPte = MiAddressToPte(Va); +#if (_MI_PAGING_LEVELS >= 3) + PointerPpe = MiAddressToPpe(Va); +#endif +#if (_MI_PAGING_LEVELS >= 4) + PointerPxe = MiAddressToPxe(Va); +#endif
/* Return the next range */ *NextVa = (PVOID)((ULONG_PTR)Va + PAGE_SIZE);
- /* Is the PDE demand-zero? */ - if (PointerPde->u.Long != 0) - { - /* It is not. Is it valid? */ + do + { +#if (_MI_PAGING_LEVELS >= 4) + /* Does the PXE exist? */ + if (PointerPxe->u.Long == 0) + { + /* It does not, next range starts at the next PXE */ + *NextVa = MiPxeToAddress(PointerPxe + 1); + break; + } + + /* Is the PXE valid? */ + if (PointerPxe->u.Hard.Valid == 0) + { + /* Is isn't, fault it in (make the PPE accessible) */ + MiMakeSystemAddressValid(PointerPpe, TargetProcess); + } +#endif +#if (_MI_PAGING_LEVELS >= 3) + /* Does the PPE exist? */ + if (PointerPpe->u.Long == 0) + { + /* It does not, next range starts at the next PPE */ + *NextVa = MiPpeToAddress(PointerPpe + 1); + break; + } + + /* Is the PPE valid? */ + if (PointerPpe->u.Hard.Valid == 0) + { + /* Is isn't, fault it in (make the PDE accessible) */ + MiMakeSystemAddressValid(PointerPde, TargetProcess); + } +#endif + + /* Does the PDE exist? */ + if (PointerPde->u.Long == 0) + { + /* It does not, next range starts at the next PDE */ + *NextVa = MiPdeToAddress(PointerPde + 1); + break; + } + + /* Is the PDE valid? */ if (PointerPde->u.Hard.Valid == 0) { - /* Is isn't, fault it in */ - PointerPte = MiPteToAddress(PointerPde); + /* Is isn't, fault it in (make the PTE accessible) */ MiMakeSystemAddressValid(PointerPte, TargetProcess); - ValidPte = TRUE; - } - } - else - { - /* It is, skip it and move to the next PDE */ - *NextVa = MiPdeToAddress(PointerPde + 1); - } + } + + /* We have a PTE that we can access now! */ + ValidPte = TRUE; + + } while (FALSE);
/* Is it safe to try reading the PTE? */ if (ValidPte) @@ -1708,6 +1757,9 @@ MemoryInfo.AllocationProtect = MmProtectToValue[Vad->u.VadFlags.Protection]; MemoryInfo.Type = MEM_PRIVATE;
+ /* Acquire the working set lock (shared is enough) */ + MiLockProcessWorkingSetShared(TargetProcess, PsGetCurrentThread()); + /* Find the largest chunk of memory which has the same state and protection mask */ MemoryInfo.State = MiQueryAddressState(Address, Vad, @@ -1722,6 +1774,16 @@ if ((NewState != MemoryInfo.State) || (NewProtect != MemoryInfo.Protect)) break; Address = NextAddress; } + + /* Release the working set lock */ + MiUnlockProcessWorkingSetShared(TargetProcess, PsGetCurrentThread()); + + /* Check if we went outside of the VAD */ + if (((ULONG_PTR)Address >> PAGE_SHIFT) > Vad->EndingVpn) + { + /* Set the end of the VAD as the end address */ + Address = (PVOID)((Vad->EndingVpn + 1) << PAGE_SHIFT); + }
/* Now that we know the last VA address, calculate the region size */ MemoryInfo.RegionSize = ((ULONG_PTR)Address - (ULONG_PTR)MemoryInfo.BaseAddress); @@ -4088,11 +4150,11 @@ PEPROCESS Process; PMEMORY_AREA MemoryArea; PFN_NUMBER PageCount; - PMMVAD Vad, FoundVad; + PMMVAD Vad = NULL, FoundVad; NTSTATUS Status; PMMSUPPORT AddressSpace; PVOID PBaseAddress; - ULONG_PTR PRegionSize, StartingAddress, EndingAddress; + ULONG_PTR PRegionSize, StartingAddress, EndingAddress, HighestAddress; PEPROCESS CurrentProcess = PsGetCurrentProcess(); KPROCESSOR_MODE PreviousMode = KeGetPreviousMode(); PETHREAD CurrentThread = PsGetCurrentThread(); @@ -4106,7 +4168,7 @@ PAGED_CODE();
/* Check for valid Zero bits */ - if (ZeroBits > 21) + if (ZeroBits > MI_MAX_ZERO_BITS) { DPRINT1("Too many zero bits\n"); return STATUS_INVALID_PARAMETER_3; @@ -4114,7 +4176,7 @@
/* Check for valid Allocation Types */ if ((AllocationType & ~(MEM_COMMIT | MEM_RESERVE | MEM_RESET | MEM_PHYSICAL | - MEM_TOP_DOWN | MEM_WRITE_WATCH))) + MEM_TOP_DOWN | MEM_WRITE_WATCH | MEM_LARGE_PAGES))) { DPRINT1("Invalid Allocation Type\n"); return STATUS_INVALID_PARAMETER_5; @@ -4159,16 +4221,16 @@ return STATUS_INVALID_PARAMETER_5; }
- /* MEM_PHYSICAL can only be used if MEM_RESERVE is also used */ - if ((AllocationType & MEM_PHYSICAL) && !(AllocationType & MEM_RESERVE)) - { - DPRINT1("MEM_WRITE_WATCH used without MEM_RESERVE\n"); - return STATUS_INVALID_PARAMETER_5; - } - /* Check for valid MEM_PHYSICAL usage */ if (AllocationType & MEM_PHYSICAL) { + /* MEM_PHYSICAL can only be used if MEM_RESERVE is also used */ + if (!(AllocationType & MEM_RESERVE)) + { + DPRINT1("MEM_PHYSICAL used without MEM_RESERVE\n"); + return STATUS_INVALID_PARAMETER_5; + } + /* Only these flags are allowed with MEM_PHYSIAL */ if (AllocationType & ~(MEM_RESERVE | MEM_TOP_DOWN | MEM_PHYSICAL)) { @@ -4200,7 +4262,7 @@ { /* Make sure they are writable */ ProbeForWritePointer(UBaseAddress); - ProbeForWriteUlong(URegionSize); + ProbeForWriteSize_t(URegionSize); }
/* Capture their values */ @@ -4215,7 +4277,7 @@ _SEH2_END;
/* Make sure the allocation isn't past the VAD area */ - if (PBaseAddress >= MM_HIGHEST_VAD_ADDRESS) + if (PBaseAddress > MM_HIGHEST_VAD_ADDRESS) { DPRINT1("Virtual allocation base above User Space\n"); return STATUS_INVALID_PARAMETER_2; @@ -4280,12 +4342,6 @@ // // Fail on the things we don't yet support // - if (ZeroBits != 0) - { - DPRINT1("Zero bits not supported\n"); - Status = STATUS_INVALID_PARAMETER; - goto FailPathNoLock; - } if ((AllocationType & MEM_LARGE_PAGES) == MEM_LARGE_PAGES) { DPRINT1("MEM_LARGE_PAGES not supported\n"); @@ -4304,18 +4360,6 @@ Status = STATUS_INVALID_PARAMETER; goto FailPathNoLock; } - if ((AllocationType & MEM_TOP_DOWN) == MEM_TOP_DOWN) - { - DPRINT1("MEM_TOP_DOWN not supported\n"); - AllocationType &= ~MEM_TOP_DOWN; - } - - if (Process->VmTopDown == 1) - { - DPRINT1("VmTopDown not supported\n"); - Status = STATUS_INVALID_PARAMETER; - goto FailPathNoLock; - }
// // Check if the caller is reserving memory, or committing memory and letting @@ -4326,7 +4370,7 @@ // // Do not allow COPY_ON_WRITE through this API // - if ((Protect & PAGE_WRITECOPY) || (Protect & PAGE_EXECUTE_WRITECOPY)) + if (Protect & (PAGE_WRITECOPY | PAGE_EXECUTE_WRITECOPY)) { DPRINT1("Copy on write not allowed through this path\n"); Status = STATUS_INVALID_PAGE_PROTECTION; @@ -4345,6 +4389,29 @@ PageCount = BYTES_TO_PAGES(PRegionSize); EndingAddress = 0; StartingAddress = 0; + + // + // Check if ZeroBits were specified + // + if (ZeroBits != 0) + { + // + // Calculate the highest address and check if it's valid + // + HighestAddress = MAXULONG_PTR >> ZeroBits; + if (HighestAddress > (ULONG_PTR)MM_HIGHEST_VAD_ADDRESS) + { + Status = STATUS_INVALID_PARAMETER_3; + goto FailPathNoLock; + } + } + else + { + // + // Use the highest VAD address as maximum + // + HighestAddress = (ULONG_PTR)MM_HIGHEST_VAD_ADDRESS; + } } else { @@ -4353,8 +4420,8 @@ // expected 64KB granularity, and see where the ending address will // fall based on the aligned address and the passed in region size // + EndingAddress = ((ULONG_PTR)PBaseAddress + PRegionSize - 1) | (PAGE_SIZE - 1); StartingAddress = ROUND_DOWN((ULONG_PTR)PBaseAddress, _64K); - EndingAddress = ((ULONG_PTR)PBaseAddress + PRegionSize - 1) | (PAGE_SIZE - 1); PageCount = BYTES_TO_PAGES(EndingAddress - StartingAddress); }
@@ -4362,7 +4429,13 @@ // Allocate and initialize the VAD // Vad = ExAllocatePoolWithTag(NonPagedPool, sizeof(MMVAD_LONG), 'SdaV'); - ASSERT(Vad != NULL); + if (Vad == NULL) + { + DPRINT1("Failed to allocate a VAD!\n"); + Status = STATUS_INSUFFICIENT_RESOURCES; + goto FailPathNoLock; + } + Vad->u.LongFlags = 0; if (AllocationType & MEM_COMMIT) Vad->u.VadFlags.MemCommit = 1; Vad->u.VadFlags.Protection = ProtectionMask; @@ -4389,11 +4462,11 @@ if (!PBaseAddress) { /* Which way should we search? */ - if (AllocationType & MEM_TOP_DOWN) + if ((AllocationType & MEM_TOP_DOWN) || Process->VmTopDown) { /* Find an address top-down */ Result = MiFindEmptyAddressRangeDownTree(PRegionSize, - (ULONG_PTR)MM_HIGHEST_VAD_ADDRESS, + HighestAddress, _64K, &Process->VadRoot, &StartingAddress, @@ -4419,9 +4492,11 @@ // Now we know where the allocation ends. Make sure it doesn't end up // somewhere in kernel mode. // - NT_ASSERT(StartingAddress != 0); + ASSERT(StartingAddress != 0); + ASSERT(StartingAddress < (ULONG_PTR)MM_HIGHEST_USER_ADDRESS); EndingAddress = (StartingAddress + PRegionSize - 1) | (PAGE_SIZE - 1); - if ((PVOID)EndingAddress > MM_HIGHEST_VAD_ADDRESS) + ASSERT(EndingAddress > StartingAddress); + if (EndingAddress > HighestAddress) { Status = STATUS_NO_MEMORY; goto FailPath; @@ -4447,8 +4522,8 @@ // // Write out the VAD fields for this allocation // - Vad->StartingVpn = (ULONG_PTR)StartingAddress >> PAGE_SHIFT; - Vad->EndingVpn = (ULONG_PTR)EndingAddress >> PAGE_SHIFT; + Vad->StartingVpn = StartingAddress >> PAGE_SHIFT; + Vad->EndingVpn = EndingAddress >> PAGE_SHIFT;
// // FIXME: Should setup VAD bitmap @@ -4465,6 +4540,13 @@ MiUnlockProcessWorkingSetUnsafe(Process, CurrentThread);
// + // Make sure the actual region size is at least as big as the + // requested region size and update the value + // + ASSERT(PRegionSize <= (EndingAddress + 1 - StartingAddress)); + PRegionSize = (EndingAddress + 1 - StartingAddress); + + // // Update the virtual size of the process, and if this is now the highest // virtual size we have ever seen, update the peak virtual size to reflect // this. @@ -4497,6 +4579,9 @@ } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { + // + // Ignore exception! + // } _SEH2_END; return STATUS_SUCCESS; @@ -4814,6 +4899,14 @@ FailPath: MmUnlockAddressSpace(AddressSpace);
+ if (!NT_SUCCESS(Status)) + { + if (Vad != NULL) + { + ExFreePoolWithTag(Vad, 'SdaV'); + } + } + // // Check if we need to update the protection // @@ -4838,21 +4931,28 @@ if (ProcessHandle != NtCurrentProcess()) ObDereferenceObject(Process);
// - // Use SEH to write back the base address and the region size. In the case - // of an exception, we strangely do return back the exception code, even - // though the memory *has* been allocated. This mimics Windows behavior and - // there is not much we can do about it. - // - _SEH2_TRY - { - *URegionSize = PRegionSize; - *UBaseAddress = (PVOID)StartingAddress; - } - _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) - { - Status = _SEH2_GetExceptionCode(); - } - _SEH2_END; + // Only write back results on success + // + if (NT_SUCCESS(Status)) + { + // + // Use SEH to write back the base address and the region size. In the case + // of an exception, we strangely do return back the exception code, even + // though the memory *has* been allocated. This mimics Windows behavior and + // there is not much we can do about it. + // + _SEH2_TRY + { + *URegionSize = PRegionSize; + *UBaseAddress = (PVOID)StartingAddress; + } + _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) + { + Status = _SEH2_GetExceptionCode(); + } + _SEH2_END; + } + return Status; }