mirror of
https://github.com/reactos/reactos.git
synced 2024-11-20 06:15:26 +00:00
[NTOS]: Use MI_SET_PFN_DELETED where we missed it.
[NTOS]: Implement support for deleting user-mode pageable VM addresses. Now when cleaning up the process address space, MiDeleteVirtualAddresses is called for the VADs, so this will now actually free the PEB/TEB pages that were previously getting leaked for each thread/process (a known regression I introduced when moving to VADs for PEB/TEB). svn path=/trunk/; revision=49187
This commit is contained in:
parent
3910f94f4a
commit
84328d78db
3 changed files with 223 additions and 3 deletions
|
@ -1205,6 +1205,21 @@ MiMakeProtectionMask(
|
|||
IN ULONG Protect
|
||||
);
|
||||
|
||||
VOID
|
||||
NTAPI
|
||||
MiDeleteVirtualAddresses(
|
||||
IN ULONG_PTR Va,
|
||||
IN ULONG_PTR EndingAddress,
|
||||
IN PMMVAD Vad
|
||||
);
|
||||
|
||||
ULONG
|
||||
NTAPI
|
||||
MiMakeSystemAddressValid(
|
||||
IN PVOID PageTableVirtualAddress,
|
||||
IN PEPROCESS CurrentProcess
|
||||
);
|
||||
|
||||
//
|
||||
// MiRemoveZeroPage will use inline code to zero out the page manually if only
|
||||
// free pages are available. In some scenarios, we don't/can't run that piece of
|
||||
|
|
|
@ -139,6 +139,8 @@ MiCreatePebOrTeb(IN PEPROCESS Process,
|
|||
/* Insert the VAD */
|
||||
ASSERT(Vad->EndingVpn >= Vad->StartingVpn);
|
||||
Process->VadRoot.NodeHint = Vad;
|
||||
Vad->ControlArea = NULL; // For Memory-Area hack
|
||||
Vad->FirstPrototypePte = NULL;
|
||||
MiInsertNode(&Process->VadRoot, (PVOID)Vad, Parent, Result);
|
||||
|
||||
/* Release the working set */
|
||||
|
@ -258,7 +260,7 @@ MmDeleteKernelStack(IN PVOID StackBase,
|
|||
MiDecrementShareCount(Pfn2, PageTableFrameNumber);
|
||||
#endif
|
||||
/* Set the special pending delete marker */
|
||||
Pfn1->PteAddress = (PMMPTE)((ULONG_PTR)Pfn1->PteAddress | 1);
|
||||
MI_SET_PFN_DELETED(Pfn1);
|
||||
|
||||
/* And now delete the actual stack page */
|
||||
MiDecrementShareCount(Pfn1, PageFrameNumber);
|
||||
|
@ -1174,7 +1176,6 @@ MmCleanProcessAddressSpace(IN PEPROCESS Process)
|
|||
|
||||
/* Enumerate the VADs */
|
||||
VadTree = &Process->VadRoot;
|
||||
DPRINT("Cleaning up VADs: %d\n", VadTree->NumberGenericTableElements);
|
||||
while (VadTree->NumberGenericTableElements)
|
||||
{
|
||||
/* Grab the current VAD */
|
||||
|
@ -1186,12 +1187,16 @@ MmCleanProcessAddressSpace(IN PEPROCESS Process)
|
|||
/* Remove this VAD from the tree */
|
||||
ASSERT(VadTree->NumberGenericTableElements >= 1);
|
||||
MiRemoveNode((PMMADDRESS_NODE)Vad, VadTree);
|
||||
DPRINT("Moving on: %d\n", VadTree->NumberGenericTableElements);
|
||||
|
||||
/* Only PEB/TEB VADs supported for now */
|
||||
ASSERT(Vad->u.VadFlags.PrivateMemory == 1);
|
||||
ASSERT(Vad->u.VadFlags.VadType == VadNone);
|
||||
|
||||
/* Delete the addresses */
|
||||
MiDeleteVirtualAddresses(Vad->StartingVpn << PAGE_SHIFT,
|
||||
(Vad->EndingVpn << PAGE_SHIFT) | (PAGE_SIZE - 1),
|
||||
Vad);
|
||||
|
||||
/* Release the working set */
|
||||
MiUnlockProcessWorkingSet(Process, Thread);
|
||||
|
||||
|
|
|
@ -29,6 +29,45 @@ MiProtectVirtualMemory(IN PEPROCESS Process,
|
|||
|
||||
/* PRIVATE FUNCTIONS **********************************************************/
|
||||
|
||||
ULONG
|
||||
NTAPI
|
||||
MiMakeSystemAddressValid(IN PVOID PageTableVirtualAddress,
|
||||
IN PEPROCESS CurrentProcess)
|
||||
{
|
||||
NTSTATUS Status;
|
||||
BOOLEAN LockChange = FALSE;
|
||||
|
||||
/* Must be a non-pool page table, since those are double-mapped already */
|
||||
ASSERT(PageTableVirtualAddress > MM_HIGHEST_USER_ADDRESS);
|
||||
ASSERT((PageTableVirtualAddress < MmPagedPoolStart) ||
|
||||
(PageTableVirtualAddress > MmPagedPoolEnd));
|
||||
|
||||
/* Working set lock or PFN lock should be held */
|
||||
ASSERT(KeAreAllApcsDisabled() == TRUE);
|
||||
|
||||
/* Check if the page table is valid */
|
||||
while (!MmIsAddressValid(PageTableVirtualAddress))
|
||||
{
|
||||
/* Fault it in */
|
||||
Status = MmAccessFault(FALSE, PageTableVirtualAddress, KernelMode, NULL);
|
||||
if (!NT_SUCCESS(Status))
|
||||
{
|
||||
/* This should not fail */
|
||||
KeBugCheckEx(KERNEL_DATA_INPAGE_ERROR,
|
||||
1,
|
||||
Status,
|
||||
(ULONG_PTR)CurrentProcess,
|
||||
(ULONG_PTR)PageTableVirtualAddress);
|
||||
}
|
||||
|
||||
/* This flag will be useful later when we do better locking */
|
||||
LockChange = TRUE;
|
||||
}
|
||||
|
||||
/* Let caller know what the lock state is */
|
||||
return LockChange;
|
||||
}
|
||||
|
||||
PFN_NUMBER
|
||||
NTAPI
|
||||
MiDeleteSystemPageableVm(IN PMMPTE PointerPte,
|
||||
|
@ -126,6 +165,167 @@ MiDeleteSystemPageableVm(IN PMMPTE PointerPte,
|
|||
return ActualPages;
|
||||
}
|
||||
|
||||
VOID
|
||||
NTAPI
|
||||
MiDeletePte(IN PMMPTE PointerPte,
|
||||
IN PVOID VirtualAddress,
|
||||
IN PEPROCESS CurrentProcess)
|
||||
{
|
||||
PMMPFN Pfn1;
|
||||
MMPTE PteContents;
|
||||
PFN_NUMBER PageFrameIndex;
|
||||
|
||||
/* PFN lock must be held */
|
||||
ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
|
||||
|
||||
/* Capture the PTE */
|
||||
PteContents = *PointerPte;
|
||||
|
||||
/* We only support valid PTEs for now */
|
||||
ASSERT(PteContents.u.Hard.Valid == 1);
|
||||
ASSERT(PteContents.u.Soft.Prototype == 0);
|
||||
ASSERT(PteContents.u.Soft.Transition == 0);
|
||||
|
||||
/* Get the PFN entry */
|
||||
PageFrameIndex = PFN_FROM_PTE(&PteContents);
|
||||
Pfn1 = MiGetPfnEntry(PageFrameIndex);
|
||||
|
||||
/* We don't support deleting prototype PTEs for now */
|
||||
ASSERT(Pfn1->u3.e1.PrototypePte == 0);
|
||||
|
||||
/* 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,
|
||||
0x401,
|
||||
(ULONG_PTR)PointerPte,
|
||||
PointerPte->u.Long,
|
||||
(ULONG_PTR)Pfn1->PteAddress);
|
||||
}
|
||||
|
||||
/* There should only be 1 shared reference count */
|
||||
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);
|
||||
|
||||
/* Mark the PFN for deletion and dereference what should be the last ref */
|
||||
MI_SET_PFN_DELETED(Pfn1);
|
||||
MiDecrementShareCount(Pfn1, PageFrameIndex);
|
||||
|
||||
/* We should eventually do this */
|
||||
//CurrentProcess->NumberOfPrivatePages--;
|
||||
|
||||
/* Destroy the PTE and flush the TLB */
|
||||
PointerPte->u.Long = 0;
|
||||
KeFlushCurrentTb();
|
||||
}
|
||||
|
||||
VOID
|
||||
NTAPI
|
||||
MiDeleteVirtualAddresses(IN ULONG_PTR Va,
|
||||
IN ULONG_PTR EndingAddress,
|
||||
IN PMMVAD Vad)
|
||||
{
|
||||
PMMPTE PointerPte, PointerPde;
|
||||
MMPTE TempPte;
|
||||
PEPROCESS CurrentProcess;
|
||||
KIRQL OldIrql;
|
||||
|
||||
/* Grab the process and PTE/PDE for the address being deleted */
|
||||
CurrentProcess = PsGetCurrentProcess();
|
||||
PointerPde = MiAddressToPde(Va);
|
||||
PointerPte = MiAddressToPte(Va);
|
||||
|
||||
/* We usually only get a VAD when it's not a VM address */
|
||||
if (Vad)
|
||||
{
|
||||
/* At process deletion, we may get a VAD, but it should be a VM VAD */
|
||||
ASSERT(Vad->u.VadFlags.PrivateMemory);
|
||||
ASSERT(Vad->FirstPrototypePte == NULL);
|
||||
|
||||
/* Get out if this is a fake VAD, RosMm will free the marea pages */
|
||||
if (Vad->u.VadFlags.Spare == 1) return;
|
||||
}
|
||||
|
||||
/* In all cases, we don't support fork() yet */
|
||||
ASSERT(CurrentProcess->CloneRoot == NULL);
|
||||
|
||||
/* Loop the PTE for each VA */
|
||||
while (TRUE)
|
||||
{
|
||||
/* First keep going until we find a valid PDE */
|
||||
while (PointerPde->u.Long == 0)
|
||||
{
|
||||
/* Still no valid PDE, try the next 4MB (or whatever) */
|
||||
PointerPde++;
|
||||
|
||||
/* Update the PTE on this new boundary */
|
||||
PointerPte = MiPteToAddress(PointerPde);
|
||||
|
||||
/* Check if all the PDEs are invalid, so there's nothing to free */
|
||||
Va = (ULONG_PTR)MiPteToAddress(PointerPte);
|
||||
if (Va > EndingAddress) return;
|
||||
}
|
||||
|
||||
/* Now check if the PDE is mapped in */
|
||||
if (PointerPde->u.Hard.Valid == 0)
|
||||
{
|
||||
/* It isn't, so map it in */
|
||||
PointerPte = MiPteToAddress(PointerPde);
|
||||
MiMakeSystemAddressValid(PointerPte, CurrentProcess);
|
||||
}
|
||||
|
||||
/* Now we should have a valid PDE, mapped in, and still have some VA */
|
||||
ASSERT(PointerPde->u.Hard.Valid == 1);
|
||||
ASSERT(Va <= EndingAddress);
|
||||
|
||||
/* Lock the PFN Database while we delete the PTEs */
|
||||
OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
|
||||
do
|
||||
{
|
||||
/* Capture the PDE and make sure it exists */
|
||||
TempPte = *PointerPte;
|
||||
if (TempPte.u.Long)
|
||||
{
|
||||
/* Check if the PTE is actually mapped in */
|
||||
if (TempPte.u.Long & 0xFFFFFC01)
|
||||
{
|
||||
/* It is, we don't support prototype PTEs for now though */
|
||||
ASSERT(TempPte.u.Soft.Prototype == 0);
|
||||
|
||||
/* Delete the PTE proper */
|
||||
MiDeletePte(PointerPte, (PVOID)Va, CurrentProcess);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* The PTE was never mapped, just nuke it here */
|
||||
PointerPte->u.Long = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Update the address and PTE for it */
|
||||
Va += PAGE_SIZE;
|
||||
PointerPte++;
|
||||
|
||||
/* Making sure the PDE is still valid */
|
||||
ASSERT(PointerPde->u.Hard.Valid == 1);
|
||||
}
|
||||
while ((Va & (PDE_MAPPED_VA - 1)) && (Va <= EndingAddress));
|
||||
|
||||
/* The PDE should still be valid at this point */
|
||||
ASSERT(PointerPde->u.Hard.Valid == 1);
|
||||
|
||||
/* Release the lock and get out if we're done */
|
||||
KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
|
||||
if (Va > EndingAddress) return;
|
||||
|
||||
/* Otherwise, we exited because we hit a new PDE boundary, so start over */
|
||||
PointerPde = MiAddressToPde(Va);
|
||||
} while (TRUE);
|
||||
}
|
||||
|
||||
LONG
|
||||
MiGetExceptionInfo(IN PEXCEPTION_POINTERS ExceptionInfo,
|
||||
OUT PBOOLEAN HaveBadAddress,
|
||||
|
|
Loading…
Reference in a new issue