https://git.reactos.org/?p=reactos.git;a=commitdiff;h=59cddd15e277dd55b222876ca8f571a0add56321

commit 59cddd15e277dd55b222876ca8f571a0add56321
Author:     Jérôme Gardou <[email protected]>
AuthorDate: Mon Jun 7 19:31:24 2021 +0200
Commit:     Jérôme Gardou <[email protected]>
CommitDate: Fri Jun 25 10:28:51 2021 +0200

    [NTOS:MM] Implement MmFlushImageSection(MmFlushForDelete)
    
    CORE-17544
---
 ntoskrnl/include/internal/mm.h |  22 +++++--
 ntoskrnl/mm/ARM3/miarm.h       |   5 --
 ntoskrnl/mm/section.c          | 137 ++++++++++++++++++++++++++++++++++++-----
 3 files changed, 141 insertions(+), 23 deletions(-)

diff --git a/ntoskrnl/include/internal/mm.h b/ntoskrnl/include/internal/mm.h
index 0b75e613687..e8ace692bd2 100644
--- a/ntoskrnl/include/internal/mm.h
+++ b/ntoskrnl/include/internal/mm.h
@@ -51,6 +51,11 @@ struct _EPROCESS;
 struct _MM_RMAP_ENTRY;
 typedef ULONG_PTR SWAPENTRY;
 
+//
+// Special IRQL value (found in assertions)
+//
+#define MM_NOIRQL ((KIRQL)0xFFFFFFFF)
+
 //
 // MmDbgCopyMemory Flags
 //
@@ -175,6 +180,7 @@ typedef ULONG_PTR SWAPENTRY;
 
 typedef struct _MM_SECTION_SEGMENT
 {
+    LONG64 RefCount;
     PFILE_OBJECT FileObject;
 
     FAST_MUTEX Lock;           /* lock which protects the page directory */
@@ -194,7 +200,6 @@ typedef struct _MM_SECTION_SEGMENT
                ULONG Characteristics;
        } Image;
 
-       LONG64 RefCount;
        ULONG SegFlags;
 
     ULONGLONG LastPage;
@@ -204,9 +209,10 @@ typedef struct _MM_SECTION_SEGMENT
 
 typedef struct _MM_IMAGE_SECTION_OBJECT
 {
-    PFILE_OBJECT FileObject;
-
     LONG64 RefCount;
+    PFILE_OBJECT FileObject;
+    ULONG SectionCount;
+    LONG MapCount;
     ULONG SegFlags;
 
     SECTION_IMAGE_INFORMATION ImageInformation;
@@ -219,6 +225,7 @@ typedef struct _MM_IMAGE_SECTION_OBJECT
 #define MM_DATAFILE_SEGMENT                 (0x2)
 #define MM_SEGMENT_INDELETE                 (0x4)
 #define MM_SEGMENT_INCREATE                 (0x8)
+#define MM_IMAGE_SECTION_FLUSH_DELETE       (0x10)
 
 
 #define MA_GetStartingAddress(_MemoryArea) ((_MemoryArea)->VadNode.StartingVpn 
<< PAGE_SHIFT)
@@ -1471,7 +1478,14 @@ MmUnsharePageEntrySectionSegment(PMEMORY_AREA MemoryArea,
 
 VOID
 NTAPI
-MmDereferenceSegment(PMM_SECTION_SEGMENT Segment);
+MmDereferenceSegmentWithLock(PMM_SECTION_SEGMENT Segment, KIRQL OldIrql);
+
+FORCEINLINE
+VOID
+MmDereferenceSegment(PMM_SECTION_SEGMENT Segment)
+{
+    MmDereferenceSegmentWithLock(Segment, MM_NOIRQL);
+}
 
 NTSTATUS
 NTAPI
diff --git a/ntoskrnl/mm/ARM3/miarm.h b/ntoskrnl/mm/ARM3/miarm.h
index 721c66c54bb..6d2f2753bb5 100644
--- a/ntoskrnl/mm/ARM3/miarm.h
+++ b/ntoskrnl/mm/ARM3/miarm.h
@@ -230,11 +230,6 @@ extern const ULONG MmProtectToValue[32];
 #error Define these please!
 #endif
 
-//
-// Special IRQL value (found in assertions)
-//
-#define MM_NOIRQL (KIRQL)0xFFFFFFFF
-
 //
 // Returns the color of a page
 //
diff --git a/ntoskrnl/mm/section.c b/ntoskrnl/mm/section.c
index d758e8d3de0..25d8db8561c 100644
--- a/ntoskrnl/mm/section.c
+++ b/ntoskrnl/mm/section.c
@@ -989,23 +989,29 @@ FreeSegmentPage(PMM_SECTION_SEGMENT Segment, 
PLARGE_INTEGER Offset)
 
 VOID
 NTAPI
-MmDereferenceSegment(PMM_SECTION_SEGMENT Segment)
+MmDereferenceSegmentWithLock(PMM_SECTION_SEGMENT Segment, KIRQL OldIrql)
 {
-    KIRQL OldIrql;
+    BOOLEAN HaveLock = FALSE;
 
     /* Lock the PFN lock because we mess around with SectionObjectPointers */
-    OldIrql = MiAcquirePfnLock();
+    if (OldIrql == MM_NOIRQL)
+    {
+        HaveLock = TRUE;
+        OldIrql = MiAcquirePfnLock();
+    }
 
     if (InterlockedDecrement64(Segment->ReferenceCount) > 0)
     {
         /* Nothing to do yet */
-        MiReleasePfnLock(OldIrql);
+        if (HaveLock)
+            MiReleasePfnLock(OldIrql);
         return;
     }
 
     *Segment->Flags |= MM_SEGMENT_INDELETE;
 
-    MiReleasePfnLock(OldIrql);
+    if (HaveLock)
+        MiReleasePfnLock(OldIrql);
 
     /* Flush the segment */
     if (*Segment->Flags & MM_DATAFILE_SEGMENT)
@@ -1013,11 +1019,13 @@ MmDereferenceSegment(PMM_SECTION_SEGMENT Segment)
         /* Free the page table. This will flush any remaining dirty data */
         MmFreePageTablesSectionSegment(Segment, FreeSegmentPage);
 
-        OldIrql = MiAcquirePfnLock();
+        if (HaveLock)
+            OldIrql = MiAcquirePfnLock();
         /* Delete the pointer on the file */
         ASSERT(Segment->FileObject->SectionObjectPointer->DataSectionObject == 
Segment);
         Segment->FileObject->SectionObjectPointer->DataSectionObject = NULL;
-        MiReleasePfnLock(OldIrql);
+        if (HaveLock)
+            MiReleasePfnLock(OldIrql);
         ObDereferenceObject(Segment->FileObject);
 
         ExFreePoolWithTag(Segment, TAG_MM_SECTION_SEGMENT);
@@ -1030,11 +1038,13 @@ MmDereferenceSegment(PMM_SECTION_SEGMENT Segment)
         ULONG NrSegments;
         ULONG i;
 
-        OldIrql = MiAcquirePfnLock();
+        if (HaveLock)
+            OldIrql = MiAcquirePfnLock();
         /* Delete the pointer on the file */
         
ASSERT(ImageSectionObject->FileObject->SectionObjectPointer->ImageSectionObject 
== ImageSectionObject);
         
ImageSectionObject->FileObject->SectionObjectPointer->ImageSectionObject = NULL;
-        MiReleasePfnLock(OldIrql);
+        if (HaveLock)
+            MiReleasePfnLock(OldIrql);
 
         ObDereferenceObject(ImageSectionObject->FileObject);
 
@@ -2113,9 +2123,13 @@ MmpDeleteSection(PVOID ObjectBody)
         if (Section->Segment == NULL)
             return;
 
+        KIRQL OldIrql = MiAcquirePfnLock();
+        ImageSectionObject->SectionCount--;
+
         /* We just dereference the first segment */
         ASSERT(ImageSectionObject->RefCount > 0);
-        MmDereferenceSegment(ImageSectionObject->Segments);
+        MmDereferenceSegmentWithLock(ImageSectionObject->Segments, OldIrql);
+        MiReleasePfnLock(OldIrql);
     }
     else
     {
@@ -2128,8 +2142,11 @@ MmpDeleteSection(PVOID ObjectBody)
         if (Segment == NULL)
             return;
 
+        KIRQL OldIrql = MiAcquirePfnLock();
         Segment->SectionCount--;
-        MmDereferenceSegment(Segment);
+
+        MmDereferenceSegmentWithLock(Segment, OldIrql);
+        MiReleasePfnLock(OldIrql);
     }
 }
 
@@ -2473,7 +2490,7 @@ grab_segment:
     else
     {
         Section->Segment = (PSEGMENT)Segment;
-        Segment->RefCount++;
+        InterlockedIncrement64(&Segment->RefCount);
         InterlockedIncrementUL(&Segment->SectionCount);
 
         MiReleasePfnLock(OldIrql);
@@ -3205,6 +3222,7 @@ grab_image_section_object:
 
         ImageSectionObject->SegFlags = MM_SEGMENT_INCREATE;
         ImageSectionObject->RefCount = 1;
+        ImageSectionObject->SectionCount = 1;
 
         OldIrql = MiAcquirePfnLock();
         if (FileObject->SectionObjectPointer->ImageSectionObject != NULL)
@@ -3281,8 +3299,12 @@ grab_image_section_object:
     }
     else
     {
+        /* If FS driver called for delete, tell them it's not possible 
anymore. */
+        ImageSectionObject->SegFlags &= ~MM_IMAGE_SECTION_FLUSH_DELETE;
+
         /* Take one ref */
-        ImageSectionObject->RefCount++;
+        InterlockedIncrement64(&ImageSectionObject->RefCount);
+        ImageSectionObject->SectionCount++;
 
         MiReleasePfnLock(OldIrql);
 
@@ -3603,6 +3625,7 @@ MiRosUnmapViewOfSection(IN PEPROCESS Process,
                 ASSERT(NT_SUCCESS(Status));
             }
         }
+        InterlockedDecrement(&ImageSectionObject->MapCount);
     }
     else
     {
@@ -4026,6 +4049,9 @@ MmMapViewOfSection(IN PVOID SectionObject,
 
         *BaseAddress = (PVOID)ImageBase;
         *ViewSize = ImageSize;
+
+        /* One more map */
+        InterlockedIncrement(&ImageSectionObject->MapCount);
     }
     else
     {
@@ -4172,6 +4198,87 @@ MmFlushImageSection (IN PSECTION_OBJECT_POINTERS 
SectionObjectPointer,
     switch(FlushType)
     {
         case MmFlushForDelete:
+        {
+            KIRQL OldIrql = MiAcquirePfnLock();
+            PMM_IMAGE_SECTION_OBJECT ImageSectionObject = 
SectionObjectPointer->ImageSectionObject;
+
+            if (!ImageSectionObject || (ImageSectionObject->SegFlags & 
MM_SEGMENT_INDELETE))
+            {
+                MiReleasePfnLock(OldIrql);
+                return TRUE;
+            }
+
+            /* Do we have open sections or mappings on it ? */
+            if ((ImageSectionObject->SectionCount) || 
(ImageSectionObject->MapCount))
+            {
+                /* We do. No way to delete it */
+                MiReleasePfnLock(OldIrql);
+                return FALSE;
+            }
+
+            /* There are no sections open on it, but we must still have pages 
around. Discard everything */
+            ImageSectionObject->SegFlags |= MM_IMAGE_SECTION_FLUSH_DELETE;
+            InterlockedIncrement64(&ImageSectionObject->RefCount);
+            MiReleasePfnLock(OldIrql);
+
+            for (ULONG i = 0; i < ImageSectionObject->NrSegments; i++)
+            {
+                PMM_SECTION_SEGMENT Segment = &ImageSectionObject->Segments[i];
+                LONGLONG Length;
+
+                MmLockSectionSegment(Segment);
+                /* Loop over all entries */
+                LARGE_INTEGER Offset;
+                Offset.QuadPart = 0;
+
+                Length = Segment->Length.QuadPart;
+                if (Length < Segment->RawLength.QuadPart)
+                    Length = Segment->RawLength.QuadPart;
+
+                while (Offset.QuadPart < Length)
+                {
+                    ULONG_PTR Entry = MmGetPageEntrySectionSegment(Segment, 
&Offset);
+
+                    /* Shared data must already be discarded, and nobody 
should be reading it. */
+                    ASSERT(!IS_SWAP_FROM_SSE(Entry));
+                    if (Entry != 0)
+                    {
+                        DPRINT1("Freeing page %lx for image section %p\n", 
PFN_FROM_SSE(Entry), ImageSectionObject);
+                        /* Release the page */
+                        ASSERT(SHARE_COUNT_FROM_SSE(Entry) == 0);
+                        ASSERT(!IS_WRITE_SSE(Entry));
+                        ASSERT(MmGetSavedSwapEntryPage(PFN_FROM_SSE(Entry)) == 
0);
+                        MmSetPageEntrySectionSegment(Segment, &Offset, 0);
+                        MmReleasePageMemoryConsumer(MC_USER, 
PFN_FROM_SSE(Entry));
+                    }
+                    Offset.QuadPart += PAGE_SIZE;
+                }
+                MmUnlockSectionSegment(Segment);
+            }
+
+            /* Grab lock again */
+            OldIrql = MiAcquirePfnLock();
+
+            if (!(ImageSectionObject->SegFlags & 
MM_IMAGE_SECTION_FLUSH_DELETE))
+            {
+                /*
+                 * Someone actually created a section while we were not 
looking.
+                 * Drop our ref and deny.
+                 */
+                MmDereferenceSegmentWithLock(&ImageSectionObject->Segments[0], 
OldIrql);
+                MiReleasePfnLock(OldIrql);
+                return FALSE;
+            }
+
+            /* We should be the last one holding a ref here. */
+            ASSERT(ImageSectionObject->RefCount == 1);
+            ASSERT(ImageSectionObject->SectionCount == 0);
+
+            /* Dereference the first segment, this will free everything & 
release the lock */
+            MmDereferenceSegmentWithLock(&ImageSectionObject->Segments[0], 
OldIrql);
+            MiReleasePfnLock(OldIrql);
+            return TRUE;
+        }
         case MmFlushForWrite:
         {
             BOOLEAN Ret = TRUE;
@@ -4237,6 +4344,9 @@ MmMapViewInSystemSpaceEx (
 
     DPRINT("MmMapViewInSystemSpaceEx() called\n");
 
+    /* unsupported for now */
+    ASSERT(Section->u.Flags.Image == 0);
+
     Section = SectionObject;
     Segment = (PMM_SECTION_SEGMENT)Section->Segment;
 
@@ -4761,7 +4871,6 @@ MmFlushSegment(
     }
 
     MmUnlockSectionSegment(Segment);
-
     MmDereferenceSegment(Segment);
 
     if (Iosb)

Reply via email to