[NTOSKRNL]: This is kind of embarssing, but after doing a local grep for some of cgutman and zekflop's changes (respectively the patch that had added MmDeleteProcessPageDirectory, and the mshtml fix patch), I fell upon a .patch file. It included 3 memory-leak-fixing patches from richard that had been sent to me during his last days -- which, had I paid attention, would've fixed the MSHTML bug and the memory leaks months ago! I've tried my best to now re-integrate a portion of the patch (the other two portions deal with freeing the loader block, and freeing .INIT sections, and I will commit them later) with the current state of trunk. Some things that patch did, no longer seem to work, and I've commented where appropriate. But in general it does seem to dereference/delete some PTEs that had been left behind before (such as deleted TEBs, and deleted VA mappings). It no longer seems to be able to delete the root PDE nor the shared data page however (this worked in the original patch's timeframe). Zekflop, tkreuzer, please take a look. I hope this will set us on a better path!

svn path=/trunk/; revision=55666
This commit is contained in:
Alex Ionescu 2012-02-17 07:07:47 +00:00
parent 7cb7765a31
commit c21ab0f761
5 changed files with 149 additions and 88 deletions

View file

@ -199,6 +199,9 @@ MmDeleteTeb(IN PEPROCESS Process,
ASSERT(VadTree->NumberGenericTableElements >= 1);
MiRemoveNode((PMMADDRESS_NODE)Vad, VadTree);
/* Delete the pages */
MiDeleteVirtualAddresses((ULONG_PTR)Teb, TebEnd, NULL);
/* Release the working set */
MiUnlockProcessWorkingSet(Process, Thread);
@ -219,7 +222,7 @@ MmDeleteKernelStack(IN PVOID StackBase,
IN BOOLEAN GuiStack)
{
PMMPTE PointerPte;
PFN_NUMBER PageFrameNumber;//, PageTableFrameNumber;
PFN_NUMBER PageFrameNumber, PageTableFrameNumber;
PFN_COUNT StackPages;
PMMPFN Pfn1;//, Pfn2;
ULONG i;
@ -253,13 +256,14 @@ MmDeleteKernelStack(IN PVOID StackBase,
/* Get the PTE's page */
PageFrameNumber = PFN_FROM_PTE(PointerPte);
Pfn1 = MiGetPfnEntry(PageFrameNumber);
#if 0 // ARM3 might not own the page table, so don't take this risk. Leak it instead!
#if 1 // ARM3 might not own the page table, so don't take this risk. Leak it instead!
/* Now get the page of the page table mapping it */
PageTableFrameNumber = Pfn1->u4.PteFrame;
Pfn2 = MiGetPfnEntry(PageTableFrameNumber);
//Pfn2 = MiGetPfnEntry(PageTableFrameNumber);
/* Remove a shared reference, since the page is going away */
MiDecrementShareCount(Pfn2, PageTableFrameNumber);
DPRINT("SystemPTE PDE: %lx\n", PageTableFrameNumber);
//MiDecrementShareCount(Pfn2, PageTableFrameNumber);
#endif
/* Set the special pending delete marker */
MI_SET_PFN_DELETED(Pfn1);
@ -1340,6 +1344,102 @@ MmCleanProcessAddressSpace(IN PEPROCESS Process)
MmUnlockAddressSpace(&Process->Vm);
}
VOID
NTAPI
MmDeleteProcessAddressSpace2(IN PEPROCESS Process)
{
PMMPFN Pfn1, Pfn2;
KIRQL OldIrql;
PFN_NUMBER PageFrameIndex;
PULONG PageDir;
ULONG i;
//ASSERT(Process->CommitCharge == 0);
/* Delete the shared user data section (Should be done in clean, not delete) */
ASSERT(MmHighestUserAddress > (PVOID)USER_SHARED_DATA);
KeAttachProcess(&Process->Pcb);
//DPRINT1("Killing shared user data page no longer works -- has someone changed ARM3 in a way to make this fail now?\n");
//MiDeleteVirtualAddresses(USER_SHARED_DATA, USER_SHARED_DATA, NULL);
//DPRINT1("Done\n");
KeDetachProcess();
/* Acquire the PFN lock */
OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
/* Check for fully initialized process */
if (Process->AddressSpaceInitialized == 2)
{
/* Map the PDE */
PageDir = MmCreateHyperspaceMapping(Process->Pcb.DirectoryTableBase[0] >> PAGE_SHIFT);
for (i = 0; i < ADDR_TO_PDE_OFFSET(MmSystemRangeStart); i++)
{
/* Loop all page tables */
if (PageDir[i] != 0)
{
/* Check if they are RosMm-owned */
if (MI_IS_ROS_PFN(MiGetPfnEntry(PageDir[i] >> PAGE_SHIFT)))
{
/* Free them through the RosMm ABI */
//DPRINT1("Freeing MC_SYSTEM: %lx\n", PageDir[i]);
MmReleasePageMemoryConsumer(MC_SYSTEM, PageDir[i] >> PAGE_SHIFT);
}
}
}
/* Unmap the PDE */
//DPRINT1("Done\n");
MmDeleteHyperspaceMapping(PageDir);
/* Map the working set page and its page table */
Pfn1 = MiGetPfnEntry(Process->WorkingSetPage);
Pfn2 = MiGetPfnEntry(Pfn1->u4.PteFrame);
/* Nuke it */
MI_SET_PFN_DELETED(Pfn1);
MiDecrementShareCount(Pfn2, Pfn1->u4.PteFrame);
MiDecrementShareCount(Pfn1, Process->WorkingSetPage);
ASSERT((Pfn1->u3.e2.ReferenceCount == 0) || (Pfn1->u3.e1.WriteInProgress));
/* Now map hyperspace and its page table */
PageFrameIndex = Process->Pcb.DirectoryTableBase[1] >> PAGE_SHIFT;
Pfn1 = MiGetPfnEntry(PageFrameIndex);
Pfn2 = MiGetPfnEntry(Pfn1->u4.PteFrame);
/* Nuke it */
MI_SET_PFN_DELETED(Pfn1);
MiDecrementShareCount(Pfn2, Pfn1->u4.PteFrame);
MiDecrementShareCount(Pfn1, PageFrameIndex);
ASSERT((Pfn1->u3.e2.ReferenceCount == 0) || (Pfn1->u3.e1.WriteInProgress));
/* Finally, nuke the PDE itself */
PageFrameIndex = Process->Pcb.DirectoryTableBase[0] >> PAGE_SHIFT;
Pfn1 = MiGetPfnEntry(PageFrameIndex);
MI_SET_PFN_DELETED(Pfn1);
MiDecrementShareCount(Pfn1, PageFrameIndex);
MiDecrementShareCount(Pfn1, PageFrameIndex);
/* HACK: In Richard's original patch this ASSERT did work */
//DPRINT1("Ref count: %lx %lx\n", Pfn1->u3.e2.ReferenceCount, Pfn1->u2.ShareCount);
//ASSERT((Pfn1->u3.e2.ReferenceCount == 0) || (Pfn1->u3.e1.WriteInProgress));
}
else
{
/* A partly-initialized process should never exit through here */
ASSERT(FALSE);
}
/* Release the PFN lock */
KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
/* No support for sessions yet */
ASSERT(Process->Session == 0);
/* Clear out the PDE pages */
Process->Pcb.DirectoryTableBase[0] = 0;
Process->Pcb.DirectoryTableBase[1] = 0;
}
/* SESSION CODE TO MOVE TO SESSION.C ******************************************/
KGUARDED_MUTEX MiSessionIdMutex;

View file

@ -264,8 +264,21 @@ MiDeletePte(IN PMMPTE PointerPte,
/* Drop the share count */
MiDecrementShareCount(Pfn1, PageFrameIndex);
/* No fork yet */
if (PointerPte <= MiHighestUserPte) ASSERT(PrototypePte == Pfn1->PteAddress);
/* Either a fork, or this is the shared user data page */
if (PointerPte <= MiHighestUserPte)
{
/* If it's not the shared user page, then crash, since there's no fork() yet */
if ((PAGE_ALIGN(VirtualAddress) != (PVOID)USER_SHARED_DATA) ||
(MmHighestUserAddress <= (PVOID)USER_SHARED_DATA))
{
/* Must be some sort of memory corruption */
KeBugCheckEx(MEMORY_MANAGEMENT,
0x400,
(ULONG_PTR)PointerPte,
(ULONG_PTR)PrototypePte,
(ULONG_PTR)Pfn1->PteAddress);
}
}
}
else
{
@ -284,7 +297,8 @@ MiDeletePte(IN PMMPTE PointerPte,
ASSERT(Pfn1->u2.ShareCount == 1);
/* FIXME: Drop the reference on the page table. For now, leak it until RosMM is gone */
//MiDecrementShareCount(MiGetPfnEntry(Pfn1->u4.PteFrame), Pfn1->u4.PteFrame);
//DPRINT1("Dropping a ref...\n");
MiDecrementShareCount(MiGetPfnEntry(Pfn1->u4.PteFrame), Pfn1->u4.PteFrame);
/* Mark the PFN for deletion and dereference what should be the last ref */
MI_SET_PFN_DELETED(Pfn1);
@ -312,6 +326,7 @@ MiDeleteVirtualAddresses(IN ULONG_PTR Va,
KIRQL OldIrql;
BOOLEAN AddressGap = FALSE;
PSUBSECTION Subsection;
PUSHORT UsedPageTableEntries;
/* Get out if this is a fake VAD, RosMm will free the marea pages */
if ((Vad) && (Vad->u.VadFlags.Spare == 1)) return;
@ -368,6 +383,7 @@ MiDeleteVirtualAddresses(IN ULONG_PTR Va,
/* Now we should have a valid PDE, mapped in, and still have some VA */
ASSERT(PointerPde->u.Hard.Valid == 1);
ASSERT(Va <= EndingAddress);
UsedPageTableEntries = &MmWorkingSetList->UsedPageTableEntries[MiGetPdeOffset(Va)];
/* Check if this is a section VAD with gaps in it */
if ((AddressGap) && (LastPrototypePte))
@ -397,6 +413,11 @@ MiDeleteVirtualAddresses(IN ULONG_PTR Va,
TempPte = *PointerPte;
if (TempPte.u.Long)
{
DPRINT("Decrement used PTEs by address: %lx\n", Va);
(*UsedPageTableEntries)--;
ASSERT((*UsedPageTableEntries) < PTE_COUNT);
DPRINT("Refs: %lx\n", (*UsedPageTableEntries));
/* Check if the PTE is actually mapped in */
if (TempPte.u.Long & 0xFFFFFC01)
{
@ -456,6 +477,22 @@ MiDeleteVirtualAddresses(IN ULONG_PTR Va,
/* The PDE should still be valid at this point */
ASSERT(PointerPde->u.Hard.Valid == 1);
DPRINT("Should check if handles for: %p are zero (PDE: %lx)\n", Va, PointerPde->u.Hard.PageFrameNumber);
if (!(*UsedPageTableEntries))
{
DPRINT("They are!\n");
if (PointerPde->u.Long != 0)
{
DPRINT("PDE active: %lx in %16s\n", PointerPde->u.Hard.PageFrameNumber, CurrentProcess->ImageFileName);
/* Delete the PTE proper */
MiDeletePte(PointerPde,
MiPteToAddress(PointerPde),
CurrentProcess,
NULL);
}
}
/* Release the lock and get out if we're done */
KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
if (Va > EndingAddress) return;

View file

@ -188,29 +188,6 @@ MmDeletePageTablePfn(PFN_NUMBER PageFrameNumber, ULONG Level)
}
}
VOID
NTAPI
MmDeleteProcessPageDirectory(PEPROCESS Process)
{
PFN_NUMBER TableBasePfn;
PMMPTE PageDir;
/* Get the page directory PFN */
TableBasePfn = Process->Pcb.DirectoryTableBase[0] >> PAGE_SHIFT;
/* Map the page directory in hyperspace */
PageDir = (PMMPTE)MmCreateHyperspaceMapping(TableBasePfn);
/* Free the hyperspace mapping page (ARM3) */
//MmDeletePageTablePfn(PageDir[ADDR_TO_PDE_OFFSET(HYPERSPACE)].u.Hard.PageFrameNumber, 3);
/* Delete the hyperspace mapping */
MmDeleteHyperspaceMapping(PageDir);
/* Recursively free the page directories */
MmDeletePageTablePfn(TableBasePfn, 4);
}
static
PMMPTE
MiGetPteForProcess(

View file

@ -198,62 +198,6 @@ ProtectToPTE(ULONG flProtect)
return(Attributes);
}
VOID
FORCEINLINE
MmDeletePageDirectoryEntry(PMMPDE PointerPde)
{
KIRQL OldIrql;
PMMPFN Page;
Page = MiGetPfnEntry(PointerPde->u.Hard.PageFrameNumber);
/* This should not be a legacy allocation*/
ASSERT(MI_IS_ROS_PFN(Page) == FALSE);
OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
/* Free it using the ARM3 API */
MI_SET_PFN_DELETED(Page);
MiDecrementShareCount(Page, PointerPde->u.Hard.PageFrameNumber);
KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
}
VOID
NTAPI
MmDeleteProcessPageDirectory(PEPROCESS Process)
{
PMMPDE PdeBase, PointerPde;
ULONG PdeOffset;
KIRQL OldIrql;
/* Map the page directory in hyperspace */
PdeBase = MiMapPageInHyperSpace(PsGetCurrentProcess(),
Process->Pcb.DirectoryTableBase[0] >> PAGE_SHIFT,
&OldIrql);
/* Loop the user land page directory */
for (PdeOffset = 0; PdeOffset < MiGetPdeOffset(MmSystemRangeStart); PdeOffset++)
{
PointerPde = PdeBase + PdeOffset;
/* Check if a valid PDE exists here */
if (PointerPde->u.Hard.Valid)
{
/* Free the page that backs it */
MmDeletePageDirectoryEntry(PointerPde);
}
}
/* Free the hyperspace mapping page (ARM3) */
MmDeletePageDirectoryEntry(PdeBase + MiGetPdeOffset(HYPERSPACE));
/* Delete the hyperspace mapping */
MiUnmapPageInHyperSpace(PsGetCurrentProcess(), PdeBase, OldIrql);
/* Free the PDE page itself (ARM3) */
MmDeletePageDirectoryEntry((PMMPDE)&Process->Pcb.DirectoryTableBase[0]);
}
/* Taken from ARM3/pagfault.c */
BOOLEAN
FORCEINLINE

View file

@ -994,6 +994,10 @@ MmMapMemoryArea(PVOID BaseAddress,
}
}
VOID
NTAPI
MmDeleteProcessAddressSpace2(IN PEPROCESS Process);
NTSTATUS
NTAPI
MmDeleteProcessAddressSpace(PEPROCESS Process)
@ -1036,11 +1040,10 @@ MmDeleteProcessAddressSpace(PEPROCESS Process)
}
}
MmDeleteProcessPageDirectory(Process);
MmUnlockAddressSpace(&Process->Vm);
DPRINT("Finished MmReleaseMmInfo()\n");
MmDeleteProcessAddressSpace2(Process);
return(STATUS_SUCCESS);
}