Index: reactos/ntoskrnl/mm/ARM3/virtual.c =================================================================== --- reactos/ntoskrnl/mm/ARM3/virtual.c (revision 75148) +++ reactos/ntoskrnl/mm/ARM3/virtual.c (working copy) @@ -419,19 +419,32 @@ DPRINT("Pte %p is transitional!\n", PointerPte); + /* Make sure the saved PTE address is valid */ + if ((PMMPTE)((ULONG_PTR)Pfn1->PteAddress & ~0x1) != PointerPte) + { + /* The PFN entry is illegal, or invalid */ + KeBugCheckEx(MEMORY_MANAGEMENT, + 0x402, + (ULONG_PTR)PointerPte, + PointerPte->u.Long, + (ULONG_PTR)Pfn1->PteAddress); + } + /* Destroy the PTE */ MI_ERASE_PTE(PointerPte); + /* Make sure it actually gets deleted */ + //MI_SET_PFN_DELETED(Pfn1); + /* Drop the reference on the page table. */ MiDecrementShareCount(MiGetPfnEntry(Pfn1->u4.PteFrame), Pfn1->u4.PteFrame); ASSERT(Pfn1->u3.e1.PrototypePte == 0); + ASSERT(Pfn1->u3.e2.ReferenceCount == 0); /* Make the page free. For prototypes, it will be made free when deleting the section object */ - if (Pfn1->u2.ShareCount == 0) + if (Pfn1->u3.e2.ReferenceCount == 0) { - ASSERT(Pfn1->u3.e2.ReferenceCount == 0); - /* And it should be in standby or modified list */ ASSERT((Pfn1->u3.e1.PageLocation == ModifiedPageList) || (Pfn1->u3.e1.PageLocation == StandbyPageList)); @@ -438,7 +451,7 @@ /* Unlink it and temporarily mark it as active */ MiUnlinkPageFromList(Pfn1); Pfn1->u3.e2.ReferenceCount++; - Pfn1->u3.e1.PageLocation = ActiveAndValid; + Pfn1->u3.e1.PageLocation = TransitionPage; /* This will put it back in free list and clean properly up */ MI_SET_PFN_DELETED(Pfn1); Index: rostests/apitests/ntdll/NtProtectVirtualMemory.c =================================================================== --- rostests/apitests/ntdll/NtProtectVirtualMemory.c (revision 75148) +++ rostests/apitests/ntdll/NtProtectVirtualMemory.c (working copy) @@ -2,6 +2,8 @@ * PROJECT: ReactOS API Tests * LICENSE: GPLv2+ - See COPYING in the top level directory * PURPOSE: Test for the NtProtectVirtualMemory API + * PROGRAMMERS: Jérôme Gardou + * Thomas Faber */ #include @@ -10,13 +12,15 @@ #include #include -START_TEST(NtProtectVirtualMemory) +static +void +TestReadWrite(void) { ULONG* allocationStart = NULL; NTSTATUS status; SIZE_T allocationSize; ULONG oldProtection; - + /* Reserve a page */ allocationSize = PAGE_SIZE; status = NtAllocateVirtualMemory(NtCurrentProcess(), @@ -26,7 +30,7 @@ MEM_RESERVE, PAGE_NOACCESS); ok(NT_SUCCESS(status), "Reserving memory failed\n"); - + /* Commit the page (RW) */ status = NtAllocateVirtualMemory(NtCurrentProcess(), (void**)&allocationStart, @@ -35,19 +39,19 @@ MEM_COMMIT, PAGE_READWRITE); ok(NT_SUCCESS(status), "Commiting memory failed\n"); - + /* Try writing it */ StartSeh() { *allocationStart = 0xFF; } EndSeh(STATUS_SUCCESS); - + /* Try reading it */ StartSeh() { ok(*allocationStart == 0xFF, "Memory was not written\n"); } EndSeh(STATUS_SUCCESS); - + /* Set it as read only */ status = NtProtectVirtualMemory(NtCurrentProcess(), (void**)&allocationStart, @@ -56,19 +60,19 @@ &oldProtection); ok(NT_SUCCESS(status), "NtProtectVirtualMemory failed.\n"); ok(oldProtection == PAGE_READWRITE, "Expected PAGE_READWRITE, got %08lx.\n", oldProtection); - + /* Try writing it */ StartSeh() { *allocationStart = 0xAA; } EndSeh(STATUS_ACCESS_VIOLATION); - + /* Try reading it */ StartSeh() { ok(*allocationStart == 0xFF, "read-only memory were changed.\n"); } EndSeh(STATUS_SUCCESS); - + /* Set it as no access */ status = NtProtectVirtualMemory(NtCurrentProcess(), (void**)&allocationStart, @@ -77,13 +81,13 @@ &oldProtection); ok(NT_SUCCESS(status), "NtProtectVirtualMemory failed.\n"); ok(oldProtection == PAGE_READONLY, "Expected PAGE_READONLY, got %08lx.\n", oldProtection); - + /* Try writing it */ StartSeh() { *allocationStart = 0xAA; } EndSeh(STATUS_ACCESS_VIOLATION); - + /* Try reading it */ StartSeh() { @@ -110,7 +114,7 @@ { ok(*allocationStart == 0xFF, "Memory content was not preserved.\n"); } EndSeh(STATUS_SUCCESS); - + /* Free memory */ status = NtFreeVirtualMemory(NtCurrentProcess(), (void**)&allocationStart, @@ -118,3 +122,66 @@ MEM_RELEASE); ok(NT_SUCCESS(status), "Failed freeing memory.\n"); } + +/* Regression test for CORE-13311 */ +static +void +TestFreeNoAccess(void) +{ + PVOID Mem; + SIZE_T Size; + NTSTATUS Status; + ULONG Iteration, PageNumber; + PUCHAR Page; + ULONG OldProtection; + + for (Iteration = 0; Iteration < 100000; Iteration++) + { + if (Iteration % 5000 == 0) + trace("%lu / 100000\n", Iteration); + Mem = NULL; + Size = 16 * PAGE_SIZE; + Status = NtAllocateVirtualMemory(NtCurrentProcess(), + &Mem, + 0, + &Size, + MEM_COMMIT, + PAGE_READWRITE); + ok_ntstatus(Status, STATUS_SUCCESS); + if (!NT_SUCCESS(Status)) + { + break; + } + + for (PageNumber = 0; PageNumber < 16; PageNumber++) + { + Page = Mem; + Page += PageNumber * PAGE_SIZE; + ok(*Page == 0, + "[%lu, %lu] Got non-zero memory. %x at %p\n", + Iteration, PageNumber, *Page, Page); + *Page = 123; + } + + Status = NtProtectVirtualMemory(NtCurrentProcess(), + &Mem, + &Size, + PAGE_NOACCESS, + &OldProtection); + ok_ntstatus(Status, STATUS_SUCCESS); + ok_hex(OldProtection, PAGE_READWRITE); + + Size = 0; + Status = NtFreeVirtualMemory(NtCurrentProcess(), + &Mem, + &Size, + MEM_RELEASE); + ok_ntstatus(Status, STATUS_SUCCESS); + } +} + +START_TEST(NtProtectVirtualMemory) +{ + TestReadWrite(); + TestFreeNoAccess(); +}