xref: /reactos/ntoskrnl/mm/section.c (revision 2ffcda90)
1 /*
2  * Copyright (C) 1998-2005 ReactOS Team (and the authors from the programmers section)
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
17  *
18  *
19  * PROJECT:         ReactOS kernel
20  * FILE:            ntoskrnl/mm/section.c
21  * PURPOSE:         Implements section objects
22  *
23  * PROGRAMMERS:     Rex Jolliff
24  *                  David Welch
25  *                  Eric Kohl
26  *                  Emanuele Aliberti
27  *                  Eugene Ingerman
28  *                  Casper Hornstrup
29  *                  KJK::Hyperion
30  *                  Guido de Jong
31  *                  Ge van Geldorp
32  *                  Royce Mitchell III
33  *                  Filip Navara
34  *                  Aleksey Bragin
35  *                  Jason Filby
36  *                  Thomas Weidenmueller
37  *                  Gunnar Andre' Dalsnes
38  *                  Mike Nordell
39  *                  Alex Ionescu
40  *                  Gregor Anich
41  *                  Steven Edwards
42  *                  Herve Poussineau
43  */
44 
45 /* INCLUDES *****************************************************************/
46 
47 #include <ntoskrnl.h>
48 #include <cache/newcc.h>
49 #include <cache/section/newmm.h>
50 #define NDEBUG
51 #include <debug.h>
52 #include <reactos/exeformat.h>
53 
54 #include "ARM3/miarm.h"
55 
56 #undef MmSetPageEntrySectionSegment
57 #define MmSetPageEntrySectionSegment(S,O,E) do { \
58         DPRINT("SetPageEntrySectionSegment(old,%p,%x,%x)\n",(S),(O)->LowPart,E); \
59         _MmSetPageEntrySectionSegment((S),(O),(E),__FILE__,__LINE__);   \
60 	} while (0)
61 
62 extern MMSESSION MmSession;
63 
64 static LARGE_INTEGER TinyTime = {{-1L, -1L}};
65 
66 #ifndef NEWCC
67 KEVENT MmWaitPageEvent;
68 
69 VOID
70 NTAPI
71 _MmLockSectionSegment(PMM_SECTION_SEGMENT Segment, const char *file, int line)
72 {
73     //DPRINT("MmLockSectionSegment(%p,%s:%d)\n", Segment, file, line);
74     ExAcquireFastMutex(&Segment->Lock);
75     Segment->Locked = TRUE;
76 }
77 
78 VOID
79 NTAPI
80 _MmUnlockSectionSegment(PMM_SECTION_SEGMENT Segment, const char *file, int line)
81 {
82     ASSERT(Segment->Locked);
83     Segment->Locked = FALSE;
84     ExReleaseFastMutex(&Segment->Lock);
85     //DPRINT("MmUnlockSectionSegment(%p,%s:%d)\n", Segment, file, line);
86 }
87 #endif
88 
89 static
90 PMM_SECTION_SEGMENT
91 MiGrabDataSection(PSECTION_OBJECT_POINTERS SectionObjectPointer)
92 {
93     KIRQL OldIrql = MiAcquirePfnLock();
94     PMM_SECTION_SEGMENT Segment = NULL;
95 
96     while (TRUE)
97     {
98         Segment = SectionObjectPointer->DataSectionObject;
99         if (!Segment)
100             break;
101 
102         if (Segment->SegFlags & (MM_SEGMENT_INCREATE | MM_SEGMENT_INDELETE))
103         {
104             MiReleasePfnLock(OldIrql);
105             KeDelayExecutionThread(KernelMode, FALSE, &TinyTime);
106             OldIrql = MiAcquirePfnLock();
107             continue;
108         }
109 
110         ASSERT(Segment->SegFlags & MM_DATAFILE_SEGMENT);
111         InterlockedIncrement64(&Segment->RefCount);
112         break;
113     }
114 
115     MiReleasePfnLock(OldIrql);
116 
117     return Segment;
118 }
119 
120 /* Somewhat grotesque, but eh... */
121 PMM_IMAGE_SECTION_OBJECT ImageSectionObjectFromSegment(PMM_SECTION_SEGMENT Segment)
122 {
123     ASSERT((Segment->SegFlags & MM_DATAFILE_SEGMENT) == 0);
124 
125     return CONTAINING_RECORD(Segment->ReferenceCount, MM_IMAGE_SECTION_OBJECT, RefCount);
126 }
127 
128 NTSTATUS
129 MiMapViewInSystemSpace(IN PVOID Section,
130                        IN PVOID Session,
131                        OUT PVOID *MappedBase,
132                        IN OUT PSIZE_T ViewSize,
133                        IN PLARGE_INTEGER SectionOffset);
134 
135 NTSTATUS
136 NTAPI
137 MmCreateArm3Section(OUT PVOID *SectionObject,
138                     IN ACCESS_MASK DesiredAccess,
139                     IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL,
140                     IN PLARGE_INTEGER InputMaximumSize,
141                     IN ULONG SectionPageProtection,
142                     IN ULONG AllocationAttributes,
143                     IN HANDLE FileHandle OPTIONAL,
144                     IN PFILE_OBJECT FileObject OPTIONAL);
145 
146 NTSTATUS
147 NTAPI
148 MmMapViewOfArm3Section(IN PVOID SectionObject,
149                        IN PEPROCESS Process,
150                        IN OUT PVOID *BaseAddress,
151                        IN ULONG_PTR ZeroBits,
152                        IN SIZE_T CommitSize,
153                        IN OUT PLARGE_INTEGER SectionOffset OPTIONAL,
154                        IN OUT PSIZE_T ViewSize,
155                        IN SECTION_INHERIT InheritDisposition,
156                        IN ULONG AllocationType,
157                        IN ULONG Protect);
158 
159 //
160 // PeFmtCreateSection depends on the following:
161 //
162 C_ASSERT(EXEFMT_LOAD_HEADER_SIZE >= sizeof(IMAGE_DOS_HEADER));
163 C_ASSERT(sizeof(IMAGE_NT_HEADERS32) <= sizeof(IMAGE_NT_HEADERS64));
164 
165 C_ASSERT(TYPE_ALIGNMENT(IMAGE_NT_HEADERS32) == TYPE_ALIGNMENT(IMAGE_NT_HEADERS64));
166 C_ASSERT(RTL_SIZEOF_THROUGH_FIELD(IMAGE_NT_HEADERS32, FileHeader) == RTL_SIZEOF_THROUGH_FIELD(IMAGE_NT_HEADERS64, FileHeader));
167 C_ASSERT(FIELD_OFFSET(IMAGE_NT_HEADERS32, OptionalHeader) == FIELD_OFFSET(IMAGE_NT_HEADERS64, OptionalHeader));
168 
169 C_ASSERT(PEFMT_FIELDS_EQUAL(IMAGE_OPTIONAL_HEADER32, IMAGE_OPTIONAL_HEADER64, Magic));
170 C_ASSERT(PEFMT_FIELDS_EQUAL(IMAGE_OPTIONAL_HEADER32, IMAGE_OPTIONAL_HEADER64, SectionAlignment));
171 C_ASSERT(PEFMT_FIELDS_EQUAL(IMAGE_OPTIONAL_HEADER32, IMAGE_OPTIONAL_HEADER64, FileAlignment));
172 C_ASSERT(PEFMT_FIELDS_EQUAL(IMAGE_OPTIONAL_HEADER32, IMAGE_OPTIONAL_HEADER64, Subsystem));
173 C_ASSERT(PEFMT_FIELDS_EQUAL(IMAGE_OPTIONAL_HEADER32, IMAGE_OPTIONAL_HEADER64, MinorSubsystemVersion));
174 C_ASSERT(PEFMT_FIELDS_EQUAL(IMAGE_OPTIONAL_HEADER32, IMAGE_OPTIONAL_HEADER64, MajorSubsystemVersion));
175 C_ASSERT(PEFMT_FIELDS_EQUAL(IMAGE_OPTIONAL_HEADER32, IMAGE_OPTIONAL_HEADER64, AddressOfEntryPoint));
176 C_ASSERT(PEFMT_FIELDS_EQUAL(IMAGE_OPTIONAL_HEADER32, IMAGE_OPTIONAL_HEADER64, SizeOfCode));
177 C_ASSERT(PEFMT_FIELDS_EQUAL(IMAGE_OPTIONAL_HEADER32, IMAGE_OPTIONAL_HEADER64, SizeOfHeaders));
178 
179 /* TYPES *********************************************************************/
180 
181 typedef struct
182 {
183     PMEMORY_AREA MemoryArea;
184     PMM_SECTION_SEGMENT Segment;
185     LARGE_INTEGER Offset;
186     BOOLEAN WasDirty;
187     BOOLEAN Private;
188     PEPROCESS CallingProcess;
189     ULONG_PTR SectionEntry;
190 }
191 MM_SECTION_PAGEOUT_CONTEXT;
192 
193 /* GLOBALS *******************************************************************/
194 
195 POBJECT_TYPE MmSectionObjectType = NULL;
196 
197 ULONG_PTR MmSubsectionBase;
198 
199 static ULONG SectionCharacteristicsToProtect[16] =
200 {
201     PAGE_NOACCESS,          /* 0 = NONE */
202     PAGE_NOACCESS,          /* 1 = SHARED */
203     PAGE_EXECUTE,           /* 2 = EXECUTABLE */
204     PAGE_EXECUTE,           /* 3 = EXECUTABLE, SHARED */
205     PAGE_READONLY,          /* 4 = READABLE */
206     PAGE_READONLY,          /* 5 = READABLE, SHARED */
207     PAGE_EXECUTE_READ,      /* 6 = READABLE, EXECUTABLE */
208     PAGE_EXECUTE_READ,      /* 7 = READABLE, EXECUTABLE, SHARED */
209     /*
210      * FIXME? do we really need the WriteCopy field in segments? can't we use
211      * PAGE_WRITECOPY here?
212      */
213     PAGE_READWRITE,         /* 8 = WRITABLE */
214     PAGE_READWRITE,         /* 9 = WRITABLE, SHARED */
215     PAGE_EXECUTE_READWRITE, /* 10 = WRITABLE, EXECUTABLE */
216     PAGE_EXECUTE_READWRITE, /* 11 = WRITABLE, EXECUTABLE, SHARED */
217     PAGE_READWRITE,         /* 12 = WRITABLE, READABLE */
218     PAGE_READWRITE,         /* 13 = WRITABLE, READABLE, SHARED */
219     PAGE_EXECUTE_READWRITE, /* 14 = WRITABLE, READABLE, EXECUTABLE */
220     PAGE_EXECUTE_READWRITE, /* 15 = WRITABLE, READABLE, EXECUTABLE, SHARED */
221 };
222 
223 extern ACCESS_MASK MmMakeFileAccess[8];
224 static GENERIC_MAPPING MmpSectionMapping =
225 {
226     STANDARD_RIGHTS_READ | SECTION_MAP_READ | SECTION_QUERY,
227     STANDARD_RIGHTS_WRITE | SECTION_MAP_WRITE,
228     STANDARD_RIGHTS_EXECUTE | SECTION_MAP_EXECUTE,
229     SECTION_ALL_ACCESS
230 };
231 
232 
233 /* FUNCTIONS *****************************************************************/
234 
235 
236 
237 NTSTATUS
238 NTAPI
239 MiWritePage(PMM_SECTION_SEGMENT Segment,
240             LONGLONG SegOffset,
241             PFN_NUMBER Page)
242 /*
243  * FUNCTION: write a page for a section backed memory area.
244  * PARAMETERS:
245  *       MemoryArea - Memory area to write the page for.
246  *       Offset - Offset of the page to write.
247  *       Page - Page which contains the data to write.
248  */
249 {
250     NTSTATUS Status;
251     IO_STATUS_BLOCK IoStatus;
252     KEVENT Event;
253     UCHAR MdlBase[sizeof(MDL) + sizeof(PFN_NUMBER)];
254     PMDL Mdl = (PMDL)MdlBase;
255     PFILE_OBJECT FileObject = Segment->FileObject;
256     LARGE_INTEGER FileOffset;
257 
258     FileOffset.QuadPart = Segment->Image.FileOffset + SegOffset;
259 
260     RtlZeroMemory(MdlBase, sizeof(MdlBase));
261     MmInitializeMdl(Mdl, NULL, PAGE_SIZE);
262     MmBuildMdlFromPages(Mdl, &Page);
263     Mdl->MdlFlags |= MDL_PAGES_LOCKED;
264 
265     KeInitializeEvent(&Event, NotificationEvent, FALSE);
266     Status = IoSynchronousPageWrite(FileObject, Mdl, &FileOffset, &Event, &IoStatus);
267     if (Status == STATUS_PENDING)
268     {
269         KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
270         Status = IoStatus.Status;
271     }
272     if (Mdl->MdlFlags & MDL_MAPPED_TO_SYSTEM_VA)
273     {
274         MmUnmapLockedPages (Mdl->MappedSystemVa, Mdl);
275     }
276 
277     return Status;
278 }
279 
280 
281 /*
282  References:
283   [1] Microsoft Corporation, "Microsoft Portable Executable and Common Object
284       File Format Specification", revision 6.0 (February 1999)
285 */
286 NTSTATUS NTAPI PeFmtCreateSection(IN CONST VOID * FileHeader,
287                                   IN SIZE_T FileHeaderSize,
288                                   IN PVOID File,
289                                   OUT PMM_IMAGE_SECTION_OBJECT ImageSectionObject,
290                                   OUT PULONG Flags,
291                                   IN PEXEFMT_CB_READ_FILE ReadFileCb,
292                                   IN PEXEFMT_CB_ALLOCATE_SEGMENTS AllocateSegmentsCb)
293 {
294     NTSTATUS nStatus;
295     ULONG cbFileHeaderOffsetSize = 0;
296     ULONG cbSectionHeadersOffset = 0;
297     ULONG cbSectionHeadersSize;
298     ULONG cbSectionHeadersOffsetSize = 0;
299     ULONG cbOptHeaderSize;
300     ULONG cbHeadersSize = 0;
301     ULONG nSectionAlignment;
302     ULONG nFileAlignment;
303     ULONG_PTR ImageBase = 0;
304     const IMAGE_DOS_HEADER * pidhDosHeader;
305     const IMAGE_NT_HEADERS32 * pinhNtHeader;
306     const IMAGE_OPTIONAL_HEADER32 * piohOptHeader;
307     const IMAGE_SECTION_HEADER * pishSectionHeaders;
308     PMM_SECTION_SEGMENT pssSegments;
309     LARGE_INTEGER lnOffset;
310     PVOID pBuffer;
311     SIZE_T nPrevVirtualEndOfSegment = 0;
312     ULONG nFileSizeOfHeaders = 0;
313     ULONG i;
314     ULONG AlignedLength;
315 
316     ASSERT(FileHeader);
317     ASSERT(FileHeaderSize > 0);
318     ASSERT(File);
319     ASSERT(ImageSectionObject);
320     ASSERT(ReadFileCb);
321     ASSERT(AllocateSegmentsCb);
322 
323     ASSERT(Intsafe_CanOffsetPointer(FileHeader, FileHeaderSize));
324 
325     ASSERT(((UINT_PTR)FileHeader % TYPE_ALIGNMENT(IMAGE_DOS_HEADER)) == 0);
326 
327 #define DIE(ARGS_) { DPRINT ARGS_; goto l_Return; }
328 
329     pBuffer = NULL;
330     pidhDosHeader = FileHeader;
331 
332     /* DOS HEADER */
333     nStatus = STATUS_ROS_EXEFMT_UNKNOWN_FORMAT;
334 
335     /* image too small to be an MZ executable */
336     if(FileHeaderSize < sizeof(IMAGE_DOS_HEADER))
337         DIE(("Too small to be an MZ executable, size is %lu\n", FileHeaderSize));
338 
339     /* no MZ signature */
340     if(pidhDosHeader->e_magic != IMAGE_DOS_SIGNATURE)
341         DIE(("No MZ signature found, e_magic is %hX\n", pidhDosHeader->e_magic));
342 
343     /* NT HEADER */
344     nStatus = STATUS_INVALID_IMAGE_PROTECT;
345 
346     /* not a Windows executable */
347     if(pidhDosHeader->e_lfanew <= 0)
348         DIE(("Not a Windows executable, e_lfanew is %d\n", pidhDosHeader->e_lfanew));
349 
350     if(!Intsafe_AddULong32(&cbFileHeaderOffsetSize, pidhDosHeader->e_lfanew, RTL_SIZEOF_THROUGH_FIELD(IMAGE_NT_HEADERS32, FileHeader)))
351         DIE(("The DOS stub is too large, e_lfanew is %X\n", pidhDosHeader->e_lfanew));
352 
353     if(FileHeaderSize < cbFileHeaderOffsetSize)
354         pinhNtHeader = NULL;
355     else
356     {
357         /*
358          * we already know that Intsafe_CanOffsetPointer(FileHeader, FileHeaderSize),
359          * and FileHeaderSize >= cbFileHeaderOffsetSize, so this holds true too
360          */
361         ASSERT(Intsafe_CanOffsetPointer(FileHeader, pidhDosHeader->e_lfanew));
362         pinhNtHeader = (PVOID)((UINT_PTR)FileHeader + pidhDosHeader->e_lfanew);
363     }
364 
365     /*
366      * the buffer doesn't contain the NT file header, or the alignment is wrong: we
367      * need to read the header from the file
368      */
369     if(FileHeaderSize < cbFileHeaderOffsetSize ||
370             (UINT_PTR)pinhNtHeader % TYPE_ALIGNMENT(IMAGE_NT_HEADERS32) != 0)
371     {
372         ULONG cbNtHeaderSize;
373         ULONG cbReadSize;
374         PVOID pData;
375 
376 l_ReadHeaderFromFile:
377         cbNtHeaderSize = 0;
378         lnOffset.QuadPart = pidhDosHeader->e_lfanew;
379 
380         /* read the header from the file */
381         nStatus = ReadFileCb(File, &lnOffset, sizeof(IMAGE_NT_HEADERS64), &pData, &pBuffer, &cbReadSize);
382 
383         if(!NT_SUCCESS(nStatus))
384         {
385             NTSTATUS ReturnedStatus = nStatus;
386 
387             /* If it attempted to read past the end of the file, it means e_lfanew is invalid */
388             if (ReturnedStatus == STATUS_END_OF_FILE) nStatus = STATUS_INVALID_IMAGE_PROTECT;
389 
390             DIE(("ReadFile failed, status %08X\n", ReturnedStatus));
391         }
392 
393         ASSERT(pData);
394         ASSERT(pBuffer);
395         ASSERT(cbReadSize > 0);
396 
397         nStatus = STATUS_INVALID_IMAGE_FORMAT;
398 
399         /* the buffer doesn't contain the file header */
400         if(cbReadSize < RTL_SIZEOF_THROUGH_FIELD(IMAGE_NT_HEADERS32, FileHeader))
401             DIE(("The file doesn't contain the PE file header\n"));
402 
403         pinhNtHeader = pData;
404 
405         /* object still not aligned: copy it to the beginning of the buffer */
406         if((UINT_PTR)pinhNtHeader % TYPE_ALIGNMENT(IMAGE_NT_HEADERS32) != 0)
407         {
408             ASSERT((UINT_PTR)pBuffer % TYPE_ALIGNMENT(IMAGE_NT_HEADERS32) == 0);
409             RtlMoveMemory(pBuffer, pData, cbReadSize);
410             pinhNtHeader = pBuffer;
411         }
412 
413         /* invalid NT header */
414         nStatus = STATUS_INVALID_IMAGE_PROTECT;
415 
416         if(pinhNtHeader->Signature != IMAGE_NT_SIGNATURE)
417             DIE(("The file isn't a PE executable, Signature is %X\n", pinhNtHeader->Signature));
418 
419         nStatus = STATUS_INVALID_IMAGE_FORMAT;
420 
421         if(!Intsafe_AddULong32(&cbNtHeaderSize, pinhNtHeader->FileHeader.SizeOfOptionalHeader, FIELD_OFFSET(IMAGE_NT_HEADERS32, OptionalHeader)))
422             DIE(("The full NT header is too large\n"));
423 
424         /* the buffer doesn't contain the whole NT header */
425         if(cbReadSize < cbNtHeaderSize)
426             DIE(("The file doesn't contain the full NT header\n"));
427     }
428     else
429     {
430         ULONG cbOptHeaderOffsetSize = 0;
431 
432         nStatus = STATUS_INVALID_IMAGE_PROTECT;
433 
434         /* don't trust an invalid NT header */
435         if(pinhNtHeader->Signature != IMAGE_NT_SIGNATURE)
436             DIE(("The file isn't a PE executable, Signature is %X\n", pinhNtHeader->Signature));
437 
438         if(!Intsafe_AddULong32(&cbOptHeaderOffsetSize, pidhDosHeader->e_lfanew, FIELD_OFFSET(IMAGE_NT_HEADERS32, OptionalHeader)))
439             DIE(("The DOS stub is too large, e_lfanew is %X\n", pidhDosHeader->e_lfanew));
440 
441         nStatus = STATUS_INVALID_IMAGE_FORMAT;
442 
443         if(!Intsafe_AddULong32(&cbOptHeaderOffsetSize, cbOptHeaderOffsetSize, pinhNtHeader->FileHeader.SizeOfOptionalHeader))
444             DIE(("The NT header is too large, SizeOfOptionalHeader is %X\n", pinhNtHeader->FileHeader.SizeOfOptionalHeader));
445 
446         /* the buffer doesn't contain the whole NT header: read it from the file */
447         if(cbOptHeaderOffsetSize > FileHeaderSize)
448             goto l_ReadHeaderFromFile;
449     }
450 
451     /* read information from the NT header */
452     piohOptHeader = &pinhNtHeader->OptionalHeader;
453     cbOptHeaderSize = pinhNtHeader->FileHeader.SizeOfOptionalHeader;
454 
455     nStatus = STATUS_INVALID_IMAGE_FORMAT;
456 
457     if(!RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, Magic))
458         DIE(("The optional header doesn't contain the Magic field, SizeOfOptionalHeader is %X\n", cbOptHeaderSize));
459 
460     /* ASSUME: RtlZeroMemory(ImageSectionObject, sizeof(*ImageSectionObject)); */
461 
462     switch(piohOptHeader->Magic)
463     {
464         case IMAGE_NT_OPTIONAL_HDR64_MAGIC:
465 #ifndef _WIN64
466             nStatus = STATUS_INVALID_IMAGE_WIN_64;
467             DIE(("Win64 optional header, unsupported\n"));
468 #else
469             // Fall through.
470 #endif
471         case IMAGE_NT_OPTIONAL_HDR32_MAGIC:
472             break;
473         default:
474             DIE(("Unrecognized optional header, Magic is %X\n", piohOptHeader->Magic));
475     }
476 
477     if (RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, SectionAlignment) &&
478             RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, FileAlignment))
479     {
480         /* See [1], section 3.4.2 */
481         if(piohOptHeader->SectionAlignment < PAGE_SIZE)
482         {
483             if(piohOptHeader->FileAlignment != piohOptHeader->SectionAlignment)
484                 DIE(("Sections aren't page-aligned and the file alignment isn't the same\n"));
485         }
486         else if(piohOptHeader->SectionAlignment < piohOptHeader->FileAlignment)
487             DIE(("The section alignment is smaller than the file alignment\n"));
488 
489         nSectionAlignment = piohOptHeader->SectionAlignment;
490         nFileAlignment = piohOptHeader->FileAlignment;
491 
492         if(!IsPowerOf2(nSectionAlignment) || !IsPowerOf2(nFileAlignment))
493             DIE(("The section alignment (%u) and file alignment (%u) aren't both powers of 2\n", nSectionAlignment, nFileAlignment));
494     }
495     else
496     {
497         nSectionAlignment = PAGE_SIZE;
498         nFileAlignment = PAGE_SIZE;
499     }
500 
501     ASSERT(IsPowerOf2(nSectionAlignment));
502     ASSERT(IsPowerOf2(nFileAlignment));
503 
504     switch(piohOptHeader->Magic)
505     {
506     /* PE32 */
507     case IMAGE_NT_OPTIONAL_HDR32_MAGIC:
508     {
509         if (RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, ImageBase))
510             ImageBase = piohOptHeader->ImageBase;
511 
512         if(RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, SizeOfImage))
513             ImageSectionObject->ImageInformation.ImageFileSize = piohOptHeader->SizeOfImage;
514 
515         if(RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, SizeOfStackReserve))
516             ImageSectionObject->ImageInformation.MaximumStackSize = piohOptHeader->SizeOfStackReserve;
517 
518         if(RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, SizeOfStackCommit))
519             ImageSectionObject->ImageInformation.CommittedStackSize = piohOptHeader->SizeOfStackCommit;
520 
521         if (RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, Subsystem))
522         {
523             ImageSectionObject->ImageInformation.SubSystemType = piohOptHeader->Subsystem;
524 
525             if (RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, MinorSubsystemVersion) &&
526                     RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, MajorSubsystemVersion))
527             {
528                 ImageSectionObject->ImageInformation.SubSystemMinorVersion = piohOptHeader->MinorSubsystemVersion;
529                 ImageSectionObject->ImageInformation.SubSystemMajorVersion = piohOptHeader->MajorSubsystemVersion;
530             }
531         }
532 
533         if (RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, AddressOfEntryPoint))
534         {
535             ImageSectionObject->ImageInformation.TransferAddress = (PVOID) (ImageBase +
536                     piohOptHeader->AddressOfEntryPoint);
537         }
538 
539         if (RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, SizeOfCode))
540             ImageSectionObject->ImageInformation.ImageContainsCode = piohOptHeader->SizeOfCode != 0;
541         else
542             ImageSectionObject->ImageInformation.ImageContainsCode = TRUE;
543 
544         if (RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, AddressOfEntryPoint))
545         {
546             if (piohOptHeader->AddressOfEntryPoint == 0)
547             {
548                 ImageSectionObject->ImageInformation.ImageContainsCode = FALSE;
549             }
550         }
551 
552         if (RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, LoaderFlags))
553             ImageSectionObject->ImageInformation.LoaderFlags = piohOptHeader->LoaderFlags;
554 
555         if (RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, DllCharacteristics))
556         {
557             ImageSectionObject->ImageInformation.DllCharacteristics = piohOptHeader->DllCharacteristics;
558 
559             /*
560              * Since we don't really implement SxS yet and LD doesn't supoprt /ALLOWISOLATION:NO, hard-code
561              * this flag here, which will prevent the loader and other code from doing any .manifest or SxS
562              * magic to any binary.
563              *
564              * This will break applications that depend on SxS when running with real Windows Kernel32/SxS/etc
565              * but honestly that's not tested. It will also break them when running no ReactOS once we implement
566              * the SxS support -- at which point, duh, this should be removed.
567              *
568              * But right now, any app depending on SxS is already broken anyway, so this flag only helps.
569              */
570             ImageSectionObject->ImageInformation.DllCharacteristics |= IMAGE_DLLCHARACTERISTICS_NO_ISOLATION;
571         }
572 
573         break;
574     }
575 #ifdef _WIN64
576     /* PE64 */
577     case IMAGE_NT_OPTIONAL_HDR64_MAGIC:
578     {
579         const IMAGE_OPTIONAL_HEADER64 * pioh64OptHeader;
580 
581         pioh64OptHeader = (const IMAGE_OPTIONAL_HEADER64 *)piohOptHeader;
582 
583         if(RTL_CONTAINS_FIELD(pioh64OptHeader, cbOptHeaderSize, ImageBase))
584         {
585             ImageBase = pioh64OptHeader->ImageBase;
586             if(pioh64OptHeader->ImageBase > MAXULONG_PTR)
587                 DIE(("ImageBase exceeds the address space\n"));
588         }
589 
590         if(RTL_CONTAINS_FIELD(pioh64OptHeader, cbOptHeaderSize, SizeOfImage))
591         {
592             if(pioh64OptHeader->SizeOfImage > MAXULONG_PTR)
593                 DIE(("SizeOfImage exceeds the address space\n"));
594 
595             ImageSectionObject->ImageInformation.ImageFileSize = pioh64OptHeader->SizeOfImage;
596         }
597 
598         if(RTL_CONTAINS_FIELD(pioh64OptHeader, cbOptHeaderSize, SizeOfStackReserve))
599         {
600             if(pioh64OptHeader->SizeOfStackReserve > MAXULONG_PTR)
601                 DIE(("SizeOfStackReserve exceeds the address space\n"));
602 
603             ImageSectionObject->ImageInformation.MaximumStackSize = (ULONG_PTR) pioh64OptHeader->SizeOfStackReserve;
604         }
605 
606         if(RTL_CONTAINS_FIELD(pioh64OptHeader, cbOptHeaderSize, SizeOfStackCommit))
607         {
608             if(pioh64OptHeader->SizeOfStackCommit > MAXULONG_PTR)
609                 DIE(("SizeOfStackCommit exceeds the address space\n"));
610 
611             ImageSectionObject->ImageInformation.CommittedStackSize = (ULONG_PTR) pioh64OptHeader->SizeOfStackCommit;
612         }
613 
614         if (RTL_CONTAINS_FIELD(pioh64OptHeader, cbOptHeaderSize, Subsystem))
615         {
616             ImageSectionObject->ImageInformation.SubSystemType = pioh64OptHeader->Subsystem;
617 
618             if (RTL_CONTAINS_FIELD(pioh64OptHeader, cbOptHeaderSize, MinorSubsystemVersion) &&
619                     RTL_CONTAINS_FIELD(pioh64OptHeader, cbOptHeaderSize, MajorSubsystemVersion))
620             {
621                 ImageSectionObject->ImageInformation.SubSystemMinorVersion = pioh64OptHeader->MinorSubsystemVersion;
622                 ImageSectionObject->ImageInformation.SubSystemMajorVersion = pioh64OptHeader->MajorSubsystemVersion;
623             }
624         }
625 
626         if (RTL_CONTAINS_FIELD(pioh64OptHeader, cbOptHeaderSize, AddressOfEntryPoint))
627         {
628             ImageSectionObject->ImageInformation.TransferAddress = (PVOID) (ImageBase +
629                     pioh64OptHeader->AddressOfEntryPoint);
630         }
631 
632         if (RTL_CONTAINS_FIELD(pioh64OptHeader, cbOptHeaderSize, SizeOfCode))
633             ImageSectionObject->ImageInformation.ImageContainsCode = pioh64OptHeader->SizeOfCode != 0;
634         else
635             ImageSectionObject->ImageInformation.ImageContainsCode = TRUE;
636 
637         if (RTL_CONTAINS_FIELD(pioh64OptHeader, cbOptHeaderSize, AddressOfEntryPoint))
638         {
639             if (pioh64OptHeader->AddressOfEntryPoint == 0)
640             {
641                 ImageSectionObject->ImageInformation.ImageContainsCode = FALSE;
642             }
643         }
644 
645         if (RTL_CONTAINS_FIELD(pioh64OptHeader, cbOptHeaderSize, LoaderFlags))
646             ImageSectionObject->ImageInformation.LoaderFlags = pioh64OptHeader->LoaderFlags;
647 
648         if (RTL_CONTAINS_FIELD(pioh64OptHeader, cbOptHeaderSize, DllCharacteristics))
649             ImageSectionObject->ImageInformation.DllCharacteristics = pioh64OptHeader->DllCharacteristics;
650 
651         break;
652     }
653 #endif // _WIN64
654     }
655 
656     /* [1], section 3.4.2 */
657     if((ULONG_PTR)ImageBase % 0x10000)
658         DIE(("ImageBase is not aligned on a 64KB boundary"));
659 
660     ImageSectionObject->ImageInformation.ImageCharacteristics = pinhNtHeader->FileHeader.Characteristics;
661     ImageSectionObject->ImageInformation.Machine = pinhNtHeader->FileHeader.Machine;
662     ImageSectionObject->ImageInformation.GpValue = 0;
663     ImageSectionObject->ImageInformation.ZeroBits = 0;
664     ImageSectionObject->BasedAddress = (PVOID)ImageBase;
665 
666     /* SECTION HEADERS */
667     nStatus = STATUS_INVALID_IMAGE_FORMAT;
668 
669     /* see [1], section 3.3 */
670     if(pinhNtHeader->FileHeader.NumberOfSections > 96)
671         DIE(("Too many sections, NumberOfSections is %u\n", pinhNtHeader->FileHeader.NumberOfSections));
672 
673     /*
674      * the additional segment is for the file's headers. They need to be present for
675      * the benefit of the dynamic loader (to locate exports, defaults for thread
676      * parameters, resources, etc.)
677      */
678     ImageSectionObject->NrSegments = pinhNtHeader->FileHeader.NumberOfSections + 1;
679 
680     /* file offset for the section headers */
681     if(!Intsafe_AddULong32(&cbSectionHeadersOffset, pidhDosHeader->e_lfanew, FIELD_OFFSET(IMAGE_NT_HEADERS32, OptionalHeader)))
682         DIE(("Offset overflow\n"));
683 
684     if(!Intsafe_AddULong32(&cbSectionHeadersOffset, cbSectionHeadersOffset, pinhNtHeader->FileHeader.SizeOfOptionalHeader))
685         DIE(("Offset overflow\n"));
686 
687     /* size of the section headers */
688     ASSERT(Intsafe_CanMulULong32(pinhNtHeader->FileHeader.NumberOfSections, sizeof(IMAGE_SECTION_HEADER)));
689     cbSectionHeadersSize = pinhNtHeader->FileHeader.NumberOfSections * sizeof(IMAGE_SECTION_HEADER);
690 
691     if(!Intsafe_AddULong32(&cbSectionHeadersOffsetSize, cbSectionHeadersOffset, cbSectionHeadersSize))
692         DIE(("Section headers too large\n"));
693 
694     /* size of the executable's headers */
695     if(RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, SizeOfHeaders))
696     {
697 //        if(!IsAligned(piohOptHeader->SizeOfHeaders, nFileAlignment))
698 //            DIE(("SizeOfHeaders is not aligned\n"));
699 
700         if(cbSectionHeadersSize > piohOptHeader->SizeOfHeaders)
701             DIE(("The section headers overflow SizeOfHeaders\n"));
702 
703         cbHeadersSize = piohOptHeader->SizeOfHeaders;
704     }
705     else if(!AlignUp(&cbHeadersSize, cbSectionHeadersOffsetSize, nFileAlignment))
706         DIE(("Overflow aligning the size of headers\n"));
707 
708     if(pBuffer)
709     {
710         ExFreePool(pBuffer);
711         pBuffer = NULL;
712     }
713     /* WARNING: pinhNtHeader IS NO LONGER USABLE */
714     /* WARNING: piohOptHeader IS NO LONGER USABLE */
715     /* WARNING: pioh64OptHeader IS NO LONGER USABLE */
716 
717     if(FileHeaderSize < cbSectionHeadersOffsetSize)
718         pishSectionHeaders = NULL;
719     else
720     {
721         /*
722          * we already know that Intsafe_CanOffsetPointer(FileHeader, FileHeaderSize),
723          * and FileHeaderSize >= cbSectionHeadersOffsetSize, so this holds true too
724          */
725         ASSERT(Intsafe_CanOffsetPointer(FileHeader, cbSectionHeadersOffset));
726         pishSectionHeaders = (PVOID)((UINT_PTR)FileHeader + cbSectionHeadersOffset);
727     }
728 
729     /*
730      * the buffer doesn't contain the section headers, or the alignment is wrong:
731      * read the headers from the file
732      */
733     if(FileHeaderSize < cbSectionHeadersOffsetSize ||
734             (UINT_PTR)pishSectionHeaders % TYPE_ALIGNMENT(IMAGE_SECTION_HEADER) != 0)
735     {
736         PVOID pData;
737         ULONG cbReadSize;
738 
739         lnOffset.QuadPart = cbSectionHeadersOffset;
740 
741         /* read the header from the file */
742         nStatus = ReadFileCb(File, &lnOffset, cbSectionHeadersSize, &pData, &pBuffer, &cbReadSize);
743 
744         if(!NT_SUCCESS(nStatus))
745             DIE(("ReadFile failed with status %08X\n", nStatus));
746 
747         ASSERT(pData);
748         ASSERT(pBuffer);
749         ASSERT(cbReadSize > 0);
750 
751         nStatus = STATUS_INVALID_IMAGE_FORMAT;
752 
753         /* the buffer doesn't contain all the section headers */
754         if(cbReadSize < cbSectionHeadersSize)
755             DIE(("The file doesn't contain all of the section headers\n"));
756 
757         pishSectionHeaders = pData;
758 
759         /* object still not aligned: copy it to the beginning of the buffer */
760         if((UINT_PTR)pishSectionHeaders % TYPE_ALIGNMENT(IMAGE_SECTION_HEADER) != 0)
761         {
762             ASSERT((UINT_PTR)pBuffer % TYPE_ALIGNMENT(IMAGE_SECTION_HEADER) == 0);
763             RtlMoveMemory(pBuffer, pData, cbReadSize);
764             pishSectionHeaders = pBuffer;
765         }
766     }
767 
768     /* SEGMENTS */
769     /* allocate the segments */
770     nStatus = STATUS_INSUFFICIENT_RESOURCES;
771     ImageSectionObject->Segments = AllocateSegmentsCb(ImageSectionObject->NrSegments);
772 
773     if(ImageSectionObject->Segments == NULL)
774         DIE(("AllocateSegments failed\n"));
775 
776     /* initialize the headers segment */
777     pssSegments = ImageSectionObject->Segments;
778 
779 //  ASSERT(IsAligned(cbHeadersSize, nFileAlignment));
780 
781     if(!AlignUp(&nFileSizeOfHeaders, cbHeadersSize, nFileAlignment))
782         DIE(("Cannot align the size of the section headers\n"));
783 
784     nPrevVirtualEndOfSegment = ALIGN_UP_BY(cbHeadersSize, nSectionAlignment);
785     if (nPrevVirtualEndOfSegment < cbHeadersSize)
786         DIE(("Cannot align the size of the section headers\n"));
787 
788     pssSegments[0].Image.FileOffset = 0;
789     pssSegments[0].Protection = PAGE_READONLY;
790     pssSegments[0].Length.QuadPart = nPrevVirtualEndOfSegment;
791     pssSegments[0].RawLength.QuadPart = nFileSizeOfHeaders;
792     pssSegments[0].Image.VirtualAddress = 0;
793     pssSegments[0].Image.Characteristics = 0;
794     pssSegments[0].WriteCopy = TRUE;
795 
796     /* skip the headers segment */
797     ++ pssSegments;
798 
799     nStatus = STATUS_INVALID_IMAGE_FORMAT;
800 
801     ASSERT(ImageSectionObject->RefCount > 0);
802 
803     /* convert the executable sections into segments. See also [1], section 4 */
804     for(i = 0; i < ImageSectionObject->NrSegments - 1; ++ i)
805     {
806         ULONG nCharacteristics;
807 
808         /* validate the alignment */
809         if(!IsAligned(pishSectionHeaders[i].VirtualAddress, nSectionAlignment))
810             DIE(("Image.VirtualAddress[%u] is not aligned\n", i));
811 
812         /* sections must be contiguous, ordered by base address and non-overlapping */
813         if(pishSectionHeaders[i].VirtualAddress != nPrevVirtualEndOfSegment)
814             DIE(("Memory gap between section %u and the previous\n", i));
815 
816         /* ignore explicit BSS sections */
817         if(pishSectionHeaders[i].PointerToRawData != 0 && pishSectionHeaders[i].SizeOfRawData != 0)
818         {
819             /* validate the alignment */
820 #if 0
821             /* Yes, this should be a multiple of FileAlignment, but there's
822              * stuff out there that isn't. We can cope with that
823              */
824             if(!IsAligned(pishSectionHeaders[i].SizeOfRawData, nFileAlignment))
825                 DIE(("SizeOfRawData[%u] is not aligned\n", i));
826 #endif
827 
828 //            if(!IsAligned(pishSectionHeaders[i].PointerToRawData, nFileAlignment))
829 //                DIE(("PointerToRawData[%u] is not aligned\n", i));
830 
831             if(!Intsafe_CanAddULong32(pishSectionHeaders[i].PointerToRawData, pishSectionHeaders[i].SizeOfRawData))
832                 DIE(("SizeOfRawData[%u] too large\n", i));
833 
834             /* conversion */
835             pssSegments[i].Image.FileOffset = pishSectionHeaders[i].PointerToRawData;
836             pssSegments[i].RawLength.QuadPart = pishSectionHeaders[i].SizeOfRawData;
837         }
838         else
839         {
840             /* FIXME: Should reset PointerToRawData to 0 in the image mapping */
841             ASSERT(pssSegments[i].Image.FileOffset == 0);
842             ASSERT(pssSegments[i].RawLength.QuadPart == 0);
843         }
844 
845         ASSERT(Intsafe_CanAddLong64(pssSegments[i].Image.FileOffset, pssSegments[i].RawLength.QuadPart));
846 
847         nCharacteristics = pishSectionHeaders[i].Characteristics;
848 
849         /* no explicit protection */
850         if((nCharacteristics & (IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE)) == 0)
851         {
852             if(nCharacteristics & IMAGE_SCN_CNT_CODE)
853                 nCharacteristics |= IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_READ;
854 
855             if(nCharacteristics & IMAGE_SCN_CNT_INITIALIZED_DATA)
856                 nCharacteristics |= IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE;
857 
858             if(nCharacteristics & IMAGE_SCN_CNT_UNINITIALIZED_DATA)
859                 nCharacteristics |= IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE;
860         }
861 
862         /* see table above */
863         pssSegments[i].Protection = SectionCharacteristicsToProtect[nCharacteristics >> 28];
864         pssSegments[i].WriteCopy = !(nCharacteristics & IMAGE_SCN_MEM_SHARED);
865 
866         if(pishSectionHeaders[i].Misc.VirtualSize == 0)
867             pssSegments[i].Length.QuadPart = pishSectionHeaders[i].SizeOfRawData;
868         else
869             pssSegments[i].Length.QuadPart = pishSectionHeaders[i].Misc.VirtualSize;
870 
871         AlignedLength = ALIGN_UP_BY(pssSegments[i].Length.LowPart, nSectionAlignment);
872         if(AlignedLength < pssSegments[i].Length.LowPart)
873             DIE(("Cannot align the virtual size of section %u\n", i));
874 
875         pssSegments[i].Length.LowPart = AlignedLength;
876 
877         if(pssSegments[i].Length.QuadPart == 0)
878             DIE(("Virtual size of section %u is null\n", i));
879 
880         pssSegments[i].Image.VirtualAddress = pishSectionHeaders[i].VirtualAddress;
881         pssSegments[i].Image.Characteristics = pishSectionHeaders[i].Characteristics;
882 
883         /* ensure the memory image is no larger than 4GB */
884         nPrevVirtualEndOfSegment = (ULONG_PTR)(pssSegments[i].Image.VirtualAddress + pssSegments[i].Length.QuadPart);
885         if (nPrevVirtualEndOfSegment < pssSegments[i].Image.VirtualAddress)
886             DIE(("The image is too large\n"));
887     }
888 
889     if(nSectionAlignment >= PAGE_SIZE)
890         *Flags |= EXEFMT_LOAD_ASSUME_SEGMENTS_PAGE_ALIGNED;
891 
892     /* Success */
893     nStatus = STATUS_SUCCESS;// STATUS_ROS_EXEFMT_LOADED_FORMAT | EXEFMT_LOADED_PE32;
894 
895 l_Return:
896     if(pBuffer)
897         ExFreePool(pBuffer);
898 
899     return nStatus;
900 }
901 
902 /*
903  * FUNCTION:  Waits in kernel mode indefinitely for a file object lock.
904  * ARGUMENTS: PFILE_OBJECT to wait for.
905  * RETURNS:   Status of the wait.
906  */
907 NTSTATUS
908 MmspWaitForFileLock(PFILE_OBJECT File)
909 {
910     return STATUS_SUCCESS;
911     //return KeWaitForSingleObject(&File->Lock, 0, KernelMode, FALSE, NULL);
912 }
913 
914 
915 
916 VOID
917 NTAPI
918 MmpFreePageFileSegment(PMM_SECTION_SEGMENT Segment)
919 {
920     ULONG Length;
921     LARGE_INTEGER Offset;
922     ULONG_PTR Entry;
923     SWAPENTRY SavedSwapEntry;
924     PFN_NUMBER Page;
925 
926     Page = 0;
927 
928     MmLockSectionSegment(Segment);
929 
930     Length = PAGE_ROUND_UP(Segment->Length.QuadPart);
931     for (Offset.QuadPart = 0; Offset.QuadPart < Length; Offset.QuadPart += PAGE_SIZE)
932     {
933         Entry = MmGetPageEntrySectionSegment(Segment, &Offset);
934         if (Entry)
935         {
936             MmSetPageEntrySectionSegment(Segment, &Offset, 0);
937             if (IS_SWAP_FROM_SSE(Entry))
938             {
939                 MmFreeSwapPage(SWAPENTRY_FROM_SSE(Entry));
940             }
941             else
942             {
943                 Page = PFN_FROM_SSE(Entry);
944                 SavedSwapEntry = MmGetSavedSwapEntryPage(Page);
945                 if (SavedSwapEntry != 0)
946                 {
947                     MmSetSavedSwapEntryPage(Page, 0);
948                     MmFreeSwapPage(SavedSwapEntry);
949                 }
950                 MmReleasePageMemoryConsumer(MC_USER, Page);
951             }
952         }
953     }
954 
955     MmUnlockSectionSegment(Segment);
956 }
957 
958 static
959 VOID
960 NTAPI
961 FreeSegmentPage(PMM_SECTION_SEGMENT Segment, PLARGE_INTEGER Offset)
962 {
963     ULONG_PTR Entry;
964     PFN_NUMBER Page;
965 
966     MmLockSectionSegment(Segment);
967 
968     Entry = MmGetPageEntrySectionSegment(Segment, Offset);
969 
970     MmUnlockSectionSegment(Segment);
971 
972     /* This must be either a valid entry or nothing */
973     ASSERT(!IS_SWAP_FROM_SSE(Entry));
974 
975     /* There should be no reference anymore */
976     ASSERT(SHARE_COUNT_FROM_SSE(Entry) == 0);
977 
978     Page = PFN_FROM_SSE(Entry);
979     /* If there is a page, this must be because it's still dirty */
980     ASSERT(Page != 0);
981 
982     /* Write the page */
983     if (IS_DIRTY_SSE(Entry))
984         MiWritePage(Segment, Offset->QuadPart, Page);
985 
986     MmReleasePageMemoryConsumer(MC_USER, Page);
987 }
988 
989 _When_(OldIrql == MM_NOIRQL, _IRQL_requires_max_(DISPATCH_LEVEL))
990 _When_(OldIrql == MM_NOIRQL, _Requires_lock_not_held_(MmPfnLock))
991 _When_(OldIrql != MM_NOIRQL, _Requires_lock_held_(MmPfnLock))
992 _When_(OldIrql != MM_NOIRQL, _Releases_lock_(MmPfnLock))
993 _When_(OldIrql != MM_NOIRQL, _IRQL_requires_(DISPATCH_LEVEL))
994 VOID
995 NTAPI
996 MmDereferenceSegmentWithLock(
997     _Inout_ PMM_SECTION_SEGMENT Segment,
998     _In_ _When_(OldIrql != MM_NOIRQL, _IRQL_restores_) KIRQL OldIrql)
999 {
1000     /* Lock the PFN lock because we mess around with SectionObjectPointers */
1001     if (OldIrql == MM_NOIRQL)
1002     {
1003         OldIrql = MiAcquirePfnLock();
1004     }
1005 
1006     if (InterlockedDecrement64(Segment->ReferenceCount) > 0)
1007     {
1008         /* Nothing to do yet */
1009         MiReleasePfnLock(OldIrql);
1010         return;
1011     }
1012 
1013     *Segment->Flags |= MM_SEGMENT_INDELETE;
1014 
1015     /* Flush the segment */
1016     if (*Segment->Flags & MM_DATAFILE_SEGMENT)
1017     {
1018         MiReleasePfnLock(OldIrql);
1019         /* Free the page table. This will flush any remaining dirty data */
1020         MmFreePageTablesSectionSegment(Segment, FreeSegmentPage);
1021 
1022         OldIrql = MiAcquirePfnLock();
1023         /* Delete the pointer on the file */
1024         ASSERT(Segment->FileObject->SectionObjectPointer->DataSectionObject == Segment);
1025         Segment->FileObject->SectionObjectPointer->DataSectionObject = NULL;
1026         MiReleasePfnLock(OldIrql);
1027         ObDereferenceObject(Segment->FileObject);
1028 
1029         ExFreePoolWithTag(Segment, TAG_MM_SECTION_SEGMENT);
1030     }
1031     else
1032     {
1033         /* Most grotesque thing ever */
1034         PMM_IMAGE_SECTION_OBJECT ImageSectionObject = CONTAINING_RECORD(Segment->ReferenceCount, MM_IMAGE_SECTION_OBJECT, RefCount);
1035         PMM_SECTION_SEGMENT SectionSegments;
1036         ULONG NrSegments;
1037         ULONG i;
1038 
1039         /* Delete the pointer on the file */
1040         ASSERT(ImageSectionObject->FileObject->SectionObjectPointer->ImageSectionObject == ImageSectionObject);
1041         ImageSectionObject->FileObject->SectionObjectPointer->ImageSectionObject = NULL;
1042         MiReleasePfnLock(OldIrql);
1043 
1044         ObDereferenceObject(ImageSectionObject->FileObject);
1045 
1046         NrSegments = ImageSectionObject->NrSegments;
1047         SectionSegments = ImageSectionObject->Segments;
1048         for (i = 0; i < NrSegments; i++)
1049         {
1050             if (SectionSegments[i].Image.Characteristics & IMAGE_SCN_MEM_SHARED)
1051             {
1052                 MmpFreePageFileSegment(&SectionSegments[i]);
1053             }
1054 
1055             MmFreePageTablesSectionSegment(&SectionSegments[i], NULL);
1056         }
1057 
1058         ExFreePoolWithTag(ImageSectionObject->Segments, TAG_MM_SECTION_SEGMENT);
1059         ExFreePoolWithTag(ImageSectionObject, TAG_MM_SECTION_SEGMENT);
1060     }
1061 }
1062 
1063 VOID
1064 NTAPI
1065 MmSharePageEntrySectionSegment(PMM_SECTION_SEGMENT Segment,
1066                                PLARGE_INTEGER Offset)
1067 {
1068     ULONG_PTR Entry;
1069 
1070     Entry = MmGetPageEntrySectionSegment(Segment, Offset);
1071     if (Entry == 0)
1072     {
1073         DPRINT1("Entry == 0 for MmSharePageEntrySectionSegment\n");
1074         KeBugCheck(MEMORY_MANAGEMENT);
1075     }
1076     if (SHARE_COUNT_FROM_SSE(Entry) == MAX_SHARE_COUNT)
1077     {
1078         DPRINT1("Maximum share count reached\n");
1079         KeBugCheck(MEMORY_MANAGEMENT);
1080     }
1081     if (IS_SWAP_FROM_SSE(Entry))
1082     {
1083         KeBugCheck(MEMORY_MANAGEMENT);
1084     }
1085     MmSetPageEntrySectionSegment(Segment, Offset, BUMPREF_SSE(Entry));
1086 }
1087 
1088 BOOLEAN
1089 NTAPI
1090 MmUnsharePageEntrySectionSegment(PMEMORY_AREA MemoryArea,
1091                                  PMM_SECTION_SEGMENT Segment,
1092                                  PLARGE_INTEGER Offset,
1093                                  BOOLEAN Dirty,
1094                                  BOOLEAN PageOut,
1095                                  ULONG_PTR *InEntry)
1096 {
1097     ULONG_PTR Entry = InEntry ? *InEntry : MmGetPageEntrySectionSegment(Segment, Offset);
1098     PFN_NUMBER Page = PFN_FROM_SSE(Entry);
1099     BOOLEAN IsDataMap = BooleanFlagOn(*Segment->Flags, MM_DATAFILE_SEGMENT);
1100     SWAPENTRY SwapEntry;
1101 
1102     if (Entry == 0)
1103     {
1104         DPRINT1("Entry == 0 for MmUnsharePageEntrySectionSegment\n");
1105         KeBugCheck(MEMORY_MANAGEMENT);
1106     }
1107     if (SHARE_COUNT_FROM_SSE(Entry) == 0)
1108     {
1109         DPRINT1("Zero share count for unshare (Seg %p Offset %x Page %x)\n", Segment, Offset->LowPart, PFN_FROM_SSE(Entry));
1110         KeBugCheck(MEMORY_MANAGEMENT);
1111     }
1112     if (IS_SWAP_FROM_SSE(Entry))
1113     {
1114         KeBugCheck(MEMORY_MANAGEMENT);
1115     }
1116     Entry = DECREF_SSE(Entry);
1117     if (Dirty) Entry = DIRTY_SSE(Entry);
1118 
1119     /* If we are paging-out, pruning the page for real will be taken care of in MmCheckDirtySegment */
1120     if ((SHARE_COUNT_FROM_SSE(Entry) > 0) || PageOut)
1121     {
1122         /* Update the page mapping in the segment and we're done */
1123         MmSetPageEntrySectionSegment(Segment, Offset, Entry);
1124         return FALSE;
1125     }
1126 
1127     /* We are pruning the last mapping on this page. See if we can keep it a bit more. */
1128     ASSERT(!PageOut);
1129 
1130     if (IsDataMap)
1131     {
1132         /* We can always keep memory in for data maps */
1133         MmSetPageEntrySectionSegment(Segment, Offset, Entry);
1134         return FALSE;
1135     }
1136 
1137     if (!FlagOn(Segment->Image.Characteristics, IMAGE_SCN_MEM_SHARED))
1138     {
1139         ASSERT(Segment->WriteCopy);
1140         ASSERT(!IS_DIRTY_SSE(Entry));
1141         ASSERT(MmGetSavedSwapEntryPage(Page) == 0);
1142         /* So this must have been a read-only page. Keep it ! */
1143         MmSetPageEntrySectionSegment(Segment, Offset, Entry);
1144         return FALSE;
1145     }
1146 
1147     /*
1148      * So this is a page for a shared section of a DLL.
1149      * We can keep it if it is not dirty.
1150      */
1151     SwapEntry = MmGetSavedSwapEntryPage(Page);
1152     if ((SwapEntry == 0) && !IS_DIRTY_SSE(Entry))
1153     {
1154         MmSetPageEntrySectionSegment(Segment, Offset, Entry);
1155         return FALSE;
1156     }
1157 
1158     /* No more processes are referencing this shared dirty page. Ditch it. */
1159     if (SwapEntry)
1160     {
1161         MmSetSavedSwapEntryPage(Page, 0);
1162         MmFreeSwapPage(SwapEntry);
1163     }
1164     MmSetPageEntrySectionSegment(Segment, Offset, 0);
1165     MmReleasePageMemoryConsumer(MC_USER, Page);
1166     return TRUE;
1167 }
1168 
1169 static
1170 NTSTATUS
1171 MiCopyFromUserPage(PFN_NUMBER DestPage, const VOID *SrcAddress)
1172 {
1173     PEPROCESS Process;
1174     KIRQL Irql;
1175     PVOID DestAddress;
1176 
1177     Process = PsGetCurrentProcess();
1178     DestAddress = MiMapPageInHyperSpace(Process, DestPage, &Irql);
1179     if (DestAddress == NULL)
1180     {
1181         return STATUS_NO_MEMORY;
1182     }
1183     ASSERT((ULONG_PTR)DestAddress % PAGE_SIZE == 0);
1184     ASSERT((ULONG_PTR)SrcAddress % PAGE_SIZE == 0);
1185     RtlCopyMemory(DestAddress, SrcAddress, PAGE_SIZE);
1186     MiUnmapPageInHyperSpace(Process, DestAddress, Irql);
1187     return STATUS_SUCCESS;
1188 }
1189 
1190 static
1191 NTSTATUS
1192 NTAPI
1193 MmMakeSegmentResident(
1194     _In_ PMM_SECTION_SEGMENT Segment,
1195     _In_ LONGLONG Offset,
1196     _In_ ULONG Length,
1197     _In_opt_ PLARGE_INTEGER ValidDataLength,
1198     _In_ BOOLEAN SetDirty)
1199 {
1200     /* Let's use a 64K granularity. */
1201     LONGLONG RangeStart, RangeEnd;
1202     NTSTATUS Status;
1203     PFILE_OBJECT FileObject = Segment->FileObject;
1204 
1205     /* Calculate our range, aligned on 64K if possible. */
1206     Status = RtlLongLongAdd(Offset, Length, &RangeEnd);
1207     ASSERT(NT_SUCCESS(Status));
1208     if (!NT_SUCCESS(Status))
1209         return Status;
1210 
1211     /* If the file is not random access and we are not the page out thread
1212      * read a 64K Chunk. */
1213     if (((ULONG_PTR)IoGetTopLevelIrp() != FSRTL_MOD_WRITE_TOP_LEVEL_IRP)
1214         && !FlagOn(FileObject->Flags, FO_RANDOM_ACCESS))
1215     {
1216         RangeStart = Offset - (Offset % _64K);
1217         if (RangeEnd % _64K)
1218             RangeEnd += _64K - (RangeEnd % _64K);
1219     }
1220     else
1221     {
1222         RangeStart = Offset  - (Offset % PAGE_SIZE);
1223         if (RangeEnd % PAGE_SIZE)
1224             RangeEnd += PAGE_SIZE - (RangeEnd % PAGE_SIZE);
1225     }
1226 
1227     /* Clamp if needed */
1228     if (!FlagOn(*Segment->Flags, MM_DATAFILE_SEGMENT))
1229     {
1230         if (RangeEnd > Segment->RawLength.QuadPart)
1231             RangeEnd = Segment->RawLength.QuadPart;
1232     }
1233 
1234     /* Let's gooooooooo */
1235     for ( ; RangeStart < RangeEnd; RangeStart += _64K)
1236     {
1237         /* First take a look at where we miss pages */
1238         ULONG ToReadPageBits = 0;
1239         LONGLONG ChunkEnd = RangeStart + _64K;
1240 
1241         if (ChunkEnd > RangeEnd)
1242             ChunkEnd = RangeEnd;
1243 
1244         MmLockSectionSegment(Segment);
1245         for (LONGLONG ChunkOffset = RangeStart; ChunkOffset < ChunkEnd; ChunkOffset += PAGE_SIZE)
1246         {
1247             LARGE_INTEGER CurrentOffset;
1248 
1249             CurrentOffset.QuadPart = ChunkOffset;
1250             ULONG_PTR Entry = MmGetPageEntrySectionSegment(Segment, &CurrentOffset);
1251 
1252             /* Let any pending read proceed */
1253             while (MM_IS_WAIT_PTE(Entry))
1254             {
1255                 MmUnlockSectionSegment(Segment);
1256 
1257                 KeDelayExecutionThread(KernelMode, FALSE, &TinyTime);
1258 
1259                 MmLockSectionSegment(Segment);
1260                 Entry = MmGetPageEntrySectionSegment(Segment, &CurrentOffset);
1261             }
1262 
1263             if (Entry != 0)
1264             {
1265                 /* Dirtify it if it's a resident page and we're asked to */
1266                 if (SetDirty && !IS_SWAP_FROM_SSE(Entry))
1267                     MmSetPageEntrySectionSegment(Segment, &CurrentOffset, DIRTY_SSE(Entry));
1268                 continue;
1269             }
1270 
1271             ToReadPageBits |= 1UL << ((ChunkOffset - RangeStart) >> PAGE_SHIFT);
1272 
1273             /* Put a wait entry here */
1274             MmSetPageEntrySectionSegment(Segment, &CurrentOffset, MAKE_SWAP_SSE(MM_WAIT_ENTRY));
1275         }
1276         MmUnlockSectionSegment(Segment);
1277 
1278         if (ToReadPageBits == 0)
1279         {
1280             /* Nothing to do for this chunk */
1281             continue;
1282         }
1283 
1284         /* Now perform the actual read */
1285         LONGLONG ChunkOffset = RangeStart;
1286         while (ChunkOffset < ChunkEnd)
1287         {
1288             /* Move forward if there is a hole */
1289             ULONG BitSet;
1290             if (!_BitScanForward(&BitSet, ToReadPageBits))
1291             {
1292                 /* Nothing more to read */
1293                 break;
1294             }
1295             ToReadPageBits >>= BitSet;
1296             ChunkOffset += BitSet * PAGE_SIZE;
1297             ASSERT(ChunkOffset < ChunkEnd);
1298 
1299             /* Get the range we have to read */
1300             _BitScanForward(&BitSet, ~ToReadPageBits);
1301             ULONG ReadLength = BitSet * PAGE_SIZE;
1302 
1303             ASSERT(ReadLength <= _64K);
1304 
1305             /* Clamp (This is for image mappings */
1306             if ((ChunkOffset + ReadLength) > ChunkEnd)
1307                 ReadLength = ChunkEnd - ChunkOffset;
1308 
1309             ASSERT(ReadLength != 0);
1310 
1311             /* Allocate a MDL */
1312             PMDL Mdl = IoAllocateMdl(NULL, ReadLength, FALSE, FALSE, NULL);
1313             if (!Mdl)
1314             {
1315                 /* Damn. Roll-back. */
1316                 MmLockSectionSegment(Segment);
1317                 while (ChunkOffset < ChunkEnd)
1318                 {
1319                     if (ToReadPageBits & 1)
1320                     {
1321                         LARGE_INTEGER CurrentOffset;
1322                         CurrentOffset.QuadPart = ChunkOffset;
1323                         ASSERT(MM_IS_WAIT_PTE(MmGetPageEntrySectionSegment(Segment, &CurrentOffset)));
1324                         MmSetPageEntrySectionSegment(Segment, &CurrentOffset, 0);
1325                     }
1326                     ToReadPageBits >>= 1;
1327                     ChunkOffset += PAGE_SIZE;
1328                 }
1329                 MmUnlockSectionSegment(Segment);
1330                 return STATUS_INSUFFICIENT_RESOURCES;
1331             }
1332 
1333             /* Get our pages */
1334             PPFN_NUMBER Pages = MmGetMdlPfnArray(Mdl);
1335             RtlZeroMemory(Pages, BYTES_TO_PAGES(ReadLength) * sizeof(PFN_NUMBER));
1336             for (UINT i = 0; i < BYTES_TO_PAGES(ReadLength); i++)
1337             {
1338                 Status = MmRequestPageMemoryConsumer(MC_USER, FALSE, &Pages[i]);
1339                 if (!NT_SUCCESS(Status))
1340                 {
1341                     /* Damn. Roll-back. */
1342                     for (UINT j = 0; j < i; j++)
1343                         MmReleasePageMemoryConsumer(MC_USER, Pages[j]);
1344                     goto Failed;
1345                 }
1346             }
1347 
1348             Mdl->MdlFlags |= MDL_PAGES_LOCKED | MDL_IO_PAGE_READ;
1349 
1350             LARGE_INTEGER FileOffset;
1351             FileOffset.QuadPart = Segment->Image.FileOffset + ChunkOffset;
1352 
1353             /* Clamp to VDL */
1354             if (ValidDataLength && ((FileOffset.QuadPart + ReadLength) > ValidDataLength->QuadPart))
1355             {
1356                 if (FileOffset.QuadPart > ValidDataLength->QuadPart)
1357                 {
1358                     /* Great, nothing to read. */
1359                     goto AssignPagesToSegment;
1360                 }
1361 
1362                 Mdl->Size = (FileOffset.QuadPart + ReadLength) - ValidDataLength->QuadPart;
1363             }
1364 
1365             KEVENT Event;
1366             KeInitializeEvent(&Event, NotificationEvent, FALSE);
1367 
1368             /* Disable APCs */
1369             KIRQL OldIrql;
1370             KeRaiseIrql(APC_LEVEL, &OldIrql);
1371 
1372             IO_STATUS_BLOCK Iosb;
1373             Status = IoPageRead(FileObject, Mdl, &FileOffset, &Event, &Iosb);
1374             if (Status == STATUS_PENDING)
1375             {
1376                 KeWaitForSingleObject(&Event, WrPageIn, KernelMode, FALSE, NULL);
1377                 Status = Iosb.Status;
1378             }
1379 
1380             if (Mdl->MdlFlags & MDL_MAPPED_TO_SYSTEM_VA)
1381             {
1382                 MmUnmapLockedPages(Mdl->MappedSystemVa, Mdl);
1383             }
1384 
1385             KeLowerIrql(OldIrql);
1386 
1387             if (Status == STATUS_END_OF_FILE)
1388             {
1389                 DPRINT1("Got STATUS_END_OF_FILE at offset %I64d for file %wZ.\n", FileOffset.QuadPart, &FileObject->FileName);
1390                 Status = STATUS_SUCCESS;
1391             }
1392 
1393             if (!NT_SUCCESS(Status))
1394             {
1395                 /* Damn. Roll back. */
1396                 for (UINT i = 0; i < BYTES_TO_PAGES(ReadLength); i++)
1397                     MmReleasePageMemoryConsumer(MC_USER, Pages[i]);
1398 
1399 Failed:
1400                 MmLockSectionSegment(Segment);
1401                 while (ChunkOffset < ChunkEnd)
1402                 {
1403                     if (ToReadPageBits & 1)
1404                     {
1405                         LARGE_INTEGER CurrentOffset;
1406                         CurrentOffset.QuadPart = ChunkOffset;
1407                         ASSERT(MM_IS_WAIT_PTE(MmGetPageEntrySectionSegment(Segment, &CurrentOffset)));
1408                         MmSetPageEntrySectionSegment(Segment, &CurrentOffset, 0);
1409                     }
1410                     ToReadPageBits >>= 1;
1411                     ChunkOffset += PAGE_SIZE;
1412                 }
1413                 MmUnlockSectionSegment(Segment);
1414                 IoFreeMdl(Mdl);;
1415                 return Status;
1416             }
1417 
1418 AssignPagesToSegment:
1419             MmLockSectionSegment(Segment);
1420 
1421             for (UINT i = 0; i < BYTES_TO_PAGES(ReadLength); i++)
1422             {
1423                 ULONG_PTR Entry = MAKE_SSE(Pages[i] << PAGE_SHIFT, 0);
1424                 LARGE_INTEGER CurrentOffset;
1425                 CurrentOffset.QuadPart = ChunkOffset + (i * PAGE_SIZE);
1426 
1427                 ASSERT(MM_IS_WAIT_PTE(MmGetPageEntrySectionSegment(Segment, &CurrentOffset)));
1428 
1429                 if (SetDirty)
1430                     Entry = DIRTY_SSE(Entry);
1431 
1432                 MmSetPageEntrySectionSegment(Segment, &CurrentOffset, Entry);
1433             }
1434 
1435             MmUnlockSectionSegment(Segment);
1436 
1437             IoFreeMdl(Mdl);
1438             ToReadPageBits >>= BitSet;
1439             ChunkOffset += BitSet * PAGE_SIZE;
1440         }
1441     }
1442 
1443     return STATUS_SUCCESS;
1444 }
1445 
1446 static VOID
1447 MmAlterViewAttributes(PMMSUPPORT AddressSpace,
1448                       PVOID BaseAddress,
1449                       SIZE_T RegionSize,
1450                       ULONG OldType,
1451                       ULONG OldProtect,
1452                       ULONG NewType,
1453                       ULONG NewProtect)
1454 {
1455     PMEMORY_AREA MemoryArea;
1456     PMM_SECTION_SEGMENT Segment;
1457     BOOLEAN DoCOW = FALSE;
1458     ULONG i;
1459     PEPROCESS Process = MmGetAddressSpaceOwner(AddressSpace);
1460 
1461     MemoryArea = MmLocateMemoryAreaByAddress(AddressSpace, BaseAddress);
1462     ASSERT(MemoryArea != NULL);
1463     Segment = MemoryArea->SectionData.Segment;
1464     MmLockSectionSegment(Segment);
1465 
1466     if ((Segment->WriteCopy) &&
1467             (NewProtect == PAGE_READWRITE || NewProtect == PAGE_EXECUTE_READWRITE))
1468     {
1469         DoCOW = TRUE;
1470     }
1471 
1472     if (OldProtect != NewProtect)
1473     {
1474         for (i = 0; i < PAGE_ROUND_UP(RegionSize) / PAGE_SIZE; i++)
1475         {
1476             SWAPENTRY SwapEntry;
1477             PVOID Address = (char*)BaseAddress + (i * PAGE_SIZE);
1478             ULONG Protect = NewProtect;
1479 
1480             /* Wait for a wait entry to disappear */
1481             do
1482             {
1483                 MmGetPageFileMapping(Process, Address, &SwapEntry);
1484                 if (SwapEntry != MM_WAIT_ENTRY)
1485                     break;
1486                 MmUnlockSectionSegment(Segment);
1487                 MmUnlockAddressSpace(AddressSpace);
1488                 KeDelayExecutionThread(KernelMode, FALSE, &TinyTime);
1489                 MmLockAddressSpace(AddressSpace);
1490                 MmLockSectionSegment(Segment);
1491             }
1492             while (TRUE);
1493 
1494             /*
1495              * If we doing COW for this segment then check if the page is
1496              * already private.
1497              */
1498             if (DoCOW && (MmIsPagePresent(Process, Address) || MmIsDisabledPage(Process, Address)))
1499             {
1500                 LARGE_INTEGER Offset;
1501                 ULONG_PTR Entry;
1502                 PFN_NUMBER Page;
1503 
1504                 Offset.QuadPart = (ULONG_PTR)Address - MA_GetStartingAddress(MemoryArea)
1505                                   + MemoryArea->SectionData.ViewOffset;
1506                 Entry = MmGetPageEntrySectionSegment(Segment, &Offset);
1507                 /*
1508                  * An MM_WAIT_ENTRY is ok in this case...  It'll just count as
1509                  * IS_SWAP_FROM_SSE and we'll do the right thing.
1510                  */
1511                 Page = MmGetPfnForProcess(Process, Address);
1512 
1513                 Protect = PAGE_READONLY;
1514                 if (IS_SWAP_FROM_SSE(Entry) || PFN_FROM_SSE(Entry) != Page)
1515                 {
1516                     Protect = NewProtect;
1517                 }
1518             }
1519 
1520             if (MmIsPagePresent(Process, Address) || MmIsDisabledPage(Process, Address))
1521             {
1522                 MmSetPageProtect(Process, Address,
1523                                  Protect);
1524             }
1525         }
1526     }
1527 
1528     MmUnlockSectionSegment(Segment);
1529 }
1530 
1531 NTSTATUS
1532 NTAPI
1533 MmNotPresentFaultSectionView(PMMSUPPORT AddressSpace,
1534                              MEMORY_AREA* MemoryArea,
1535                              PVOID Address,
1536                              BOOLEAN Locked)
1537 {
1538     LARGE_INTEGER Offset;
1539     PFN_NUMBER Page;
1540     NTSTATUS Status;
1541     PMM_SECTION_SEGMENT Segment;
1542     ULONG_PTR Entry;
1543     ULONG_PTR Entry1;
1544     ULONG Attributes;
1545     PMM_REGION Region;
1546     BOOLEAN HasSwapEntry;
1547     PVOID PAddress;
1548     PEPROCESS Process = MmGetAddressSpaceOwner(AddressSpace);
1549     SWAPENTRY SwapEntry;
1550 
1551     ASSERT(Locked);
1552 
1553     /*
1554      * There is a window between taking the page fault and locking the
1555      * address space when another thread could load the page so we check
1556      * that.
1557      */
1558     if (MmIsPagePresent(Process, Address))
1559     {
1560         return STATUS_SUCCESS;
1561     }
1562 
1563     if (MmIsDisabledPage(Process, Address))
1564     {
1565         return STATUS_ACCESS_VIOLATION;
1566     }
1567 
1568     /*
1569      * Check for the virtual memory area being deleted.
1570      */
1571     if (MemoryArea->DeleteInProgress)
1572     {
1573         return STATUS_UNSUCCESSFUL;
1574     }
1575 
1576     PAddress = MM_ROUND_DOWN(Address, PAGE_SIZE);
1577     Offset.QuadPart = (ULONG_PTR)PAddress - MA_GetStartingAddress(MemoryArea)
1578                       + MemoryArea->SectionData.ViewOffset;
1579 
1580     Segment = MemoryArea->SectionData.Segment;
1581     Region = MmFindRegion((PVOID)MA_GetStartingAddress(MemoryArea),
1582                           &MemoryArea->SectionData.RegionListHead,
1583                           Address, NULL);
1584     ASSERT(Region != NULL);
1585 
1586     /* Check for a NOACCESS mapping */
1587     if (Region->Protect & PAGE_NOACCESS)
1588     {
1589         return STATUS_ACCESS_VIOLATION;
1590     }
1591 
1592     if (Region->Protect & PAGE_GUARD)
1593     {
1594         /* Remove it */
1595         Status = MmAlterRegion(AddressSpace, (PVOID)MA_GetStartingAddress(MemoryArea),
1596                 &MemoryArea->SectionData.RegionListHead,
1597                 Address, PAGE_SIZE, Region->Type, Region->Protect & ~PAGE_GUARD,
1598                 MmAlterViewAttributes);
1599 
1600         if (!NT_SUCCESS(Status))
1601         {
1602             DPRINT1("Removing PAGE_GUARD protection failed : 0x%08x.\n", Status);
1603         }
1604 
1605         return STATUS_GUARD_PAGE_VIOLATION;
1606     }
1607 
1608     HasSwapEntry = MmIsPageSwapEntry(Process, Address);
1609 
1610     /* See if we should use a private page */
1611     if (HasSwapEntry)
1612     {
1613         SWAPENTRY DummyEntry;
1614 
1615         MmGetPageFileMapping(Process, Address, &SwapEntry);
1616         if (SwapEntry == MM_WAIT_ENTRY)
1617         {
1618             MmUnlockAddressSpace(AddressSpace);
1619             KeDelayExecutionThread(KernelMode, FALSE, &TinyTime);
1620             MmLockAddressSpace(AddressSpace);
1621             return STATUS_MM_RESTART_OPERATION;
1622         }
1623 
1624         MI_SET_USAGE(MI_USAGE_SECTION);
1625         if (Process) MI_SET_PROCESS2(Process->ImageFileName);
1626         if (!Process) MI_SET_PROCESS2("Kernel Section");
1627         Status = MmRequestPageMemoryConsumer(MC_USER, TRUE, &Page);
1628         if (!NT_SUCCESS(Status))
1629         {
1630             return STATUS_NO_MEMORY;
1631         }
1632 
1633         /*
1634          * Must be private page we have swapped out.
1635          */
1636 
1637         /*
1638         * Sanity check
1639         */
1640         MmDeletePageFileMapping(Process, Address, &DummyEntry);
1641         ASSERT(DummyEntry == SwapEntry);
1642 
1643         /* Tell everyone else we are serving the fault. */
1644         MmCreatePageFileMapping(Process, Address, MM_WAIT_ENTRY);
1645 
1646         MmUnlockAddressSpace(AddressSpace);
1647 
1648         Status = MmReadFromSwapPage(SwapEntry, Page);
1649         if (!NT_SUCCESS(Status))
1650         {
1651             DPRINT1("MmReadFromSwapPage failed, status = %x\n", Status);
1652             KeBugCheck(MEMORY_MANAGEMENT);
1653         }
1654 
1655         MmLockAddressSpace(AddressSpace);
1656         MmDeletePageFileMapping(Process, PAddress, &DummyEntry);
1657         ASSERT(DummyEntry == MM_WAIT_ENTRY);
1658 
1659         Status = MmCreateVirtualMapping(Process,
1660                                         PAddress,
1661                                         Region->Protect,
1662                                         Page);
1663         if (!NT_SUCCESS(Status))
1664         {
1665             DPRINT("MmCreateVirtualMapping failed, not out of memory\n");
1666             KeBugCheck(MEMORY_MANAGEMENT);
1667             return Status;
1668         }
1669 
1670         /*
1671          * Store the swap entry for later use.
1672          */
1673         MmSetSavedSwapEntryPage(Page, SwapEntry);
1674 
1675         /*
1676          * Add the page to the process's working set
1677          */
1678         if (Process) MmInsertRmap(Page, Process, Address);
1679         /*
1680          * Finish the operation
1681          */
1682         DPRINT("Address 0x%p\n", Address);
1683         return STATUS_SUCCESS;
1684     }
1685 
1686     /*
1687      * Lock the segment
1688      */
1689     MmLockSectionSegment(Segment);
1690 
1691     /*
1692      * Satisfying a page fault on a map of /Device/PhysicalMemory is easy
1693      */
1694     if ((*Segment->Flags) & MM_PHYSICALMEMORY_SEGMENT)
1695     {
1696         MmUnlockSectionSegment(Segment);
1697         /*
1698          * Just map the desired physical page
1699          */
1700         Page = (PFN_NUMBER)(Offset.QuadPart >> PAGE_SHIFT);
1701         Status = MmCreatePhysicalMapping(Process,
1702                                          PAddress,
1703                                          Region->Protect,
1704                                          Page);
1705         if (!NT_SUCCESS(Status))
1706         {
1707             DPRINT("MmCreateVirtualMappingUnsafe failed, not out of memory\n");
1708             KeBugCheck(MEMORY_MANAGEMENT);
1709             return Status;
1710         }
1711 
1712         /*
1713          * Cleanup and release locks
1714          */
1715         DPRINT("Address 0x%p\n", Address);
1716         return STATUS_SUCCESS;
1717     }
1718 
1719     /*
1720      * Check if this page needs to be mapped COW
1721      */
1722     if ((Segment->WriteCopy) &&
1723         (Region->Protect == PAGE_READWRITE || Region->Protect == PAGE_EXECUTE_READWRITE))
1724     {
1725         Attributes = Region->Protect == PAGE_READWRITE ? PAGE_READONLY : PAGE_EXECUTE_READ;
1726     }
1727     else
1728     {
1729         Attributes = Region->Protect;
1730     }
1731 
1732 
1733     /*
1734      * Get the entry corresponding to the offset within the section
1735      */
1736     Entry = MmGetPageEntrySectionSegment(Segment, &Offset);
1737     if (Entry == 0)
1738     {
1739         /*
1740          * If the entry is zero, then we need to load the page.
1741          */
1742         if ((Offset.QuadPart >= (LONGLONG)PAGE_ROUND_UP(Segment->RawLength.QuadPart)) && (MemoryArea->VadNode.u.VadFlags.VadType == VadImageMap))
1743         {
1744             /* We are beyond the data which is on file. Just get a new page. */
1745             MI_SET_USAGE(MI_USAGE_SECTION);
1746             if (Process) MI_SET_PROCESS2(Process->ImageFileName);
1747             if (!Process) MI_SET_PROCESS2("Kernel Section");
1748             Status = MmRequestPageMemoryConsumer(MC_USER, FALSE, &Page);
1749             if (!NT_SUCCESS(Status))
1750             {
1751                 MmUnlockSectionSegment(Segment);
1752                 return STATUS_NO_MEMORY;
1753             }
1754             MmSetPageEntrySectionSegment(Segment, &Offset, MAKE_SSE(Page << PAGE_SHIFT, 1));
1755             MmUnlockSectionSegment(Segment);
1756 
1757             Status = MmCreateVirtualMapping(Process, PAddress, Attributes, Page);
1758             if (!NT_SUCCESS(Status))
1759             {
1760                 DPRINT1("Unable to create virtual mapping\n");
1761                 KeBugCheck(MEMORY_MANAGEMENT);
1762             }
1763             ASSERT(MmIsPagePresent(Process, PAddress));
1764             if (Process)
1765                 MmInsertRmap(Page, Process, Address);
1766 
1767             DPRINT("Address 0x%p\n", Address);
1768             return STATUS_SUCCESS;
1769         }
1770 
1771         MmUnlockSectionSegment(Segment);
1772         MmUnlockAddressSpace(AddressSpace);
1773 
1774         /* The data must be paged in. Lock the file, so that the VDL doesn't get updated behind us. */
1775         FsRtlAcquireFileExclusive(Segment->FileObject);
1776 
1777         PFSRTL_COMMON_FCB_HEADER FcbHeader = Segment->FileObject->FsContext;
1778 
1779         Status = MmMakeSegmentResident(Segment, Offset.QuadPart, PAGE_SIZE, &FcbHeader->ValidDataLength, FALSE);
1780 
1781         FsRtlReleaseFile(Segment->FileObject);
1782 
1783         /* Lock address space again */
1784         MmLockAddressSpace(AddressSpace);
1785         if (!NT_SUCCESS(Status))
1786         {
1787             if (Status == STATUS_NO_MEMORY)
1788             {
1789                 return Status;
1790             }
1791             /* Damn */
1792             DPRINT1("Failed to page data in!\n");
1793             return STATUS_IN_PAGE_ERROR;
1794         }
1795 
1796         /* Everything went fine. Restart the operation */
1797         return STATUS_MM_RESTART_OPERATION;
1798     }
1799     else if (IS_SWAP_FROM_SSE(Entry))
1800     {
1801         SWAPENTRY SwapEntry;
1802 
1803         SwapEntry = SWAPENTRY_FROM_SSE(Entry);
1804 
1805         /* See if a page op is running on this segment. */
1806         if (SwapEntry == MM_WAIT_ENTRY)
1807         {
1808             MmUnlockSectionSegment(Segment);
1809             MmUnlockAddressSpace(AddressSpace);
1810             KeDelayExecutionThread(KernelMode, FALSE, &TinyTime);
1811             MmLockAddressSpace(AddressSpace);
1812             return STATUS_MM_RESTART_OPERATION;
1813         }
1814 
1815         Status = MmRequestPageMemoryConsumer(MC_USER, TRUE, &Page);
1816         if (!NT_SUCCESS(Status))
1817         {
1818             MmUnlockSectionSegment(Segment);
1819             return STATUS_NO_MEMORY;
1820         }
1821 
1822         /*
1823         * Release all our locks and read in the page from disk
1824         */
1825         MmSetPageEntrySectionSegment(Segment, &Offset, MAKE_SWAP_SSE(MM_WAIT_ENTRY));
1826         MmUnlockSectionSegment(Segment);
1827 
1828         MmUnlockAddressSpace(AddressSpace);
1829 
1830         Status = MmReadFromSwapPage(SwapEntry, Page);
1831         if (!NT_SUCCESS(Status))
1832         {
1833             KeBugCheck(MEMORY_MANAGEMENT);
1834         }
1835 
1836         /*
1837          * Relock the address space and segment
1838          */
1839         MmLockAddressSpace(AddressSpace);
1840         MmLockSectionSegment(Segment);
1841 
1842         /*
1843          * Check the entry. No one should change the status of a page
1844          * that has a pending page-in.
1845          */
1846         Entry1 = MmGetPageEntrySectionSegment(Segment, &Offset);
1847         if (Entry1 != MAKE_SWAP_SSE(MM_WAIT_ENTRY))
1848         {
1849             DPRINT1("Someone changed ppte entry while we slept (%x vs %x)\n", Entry, Entry1);
1850             KeBugCheck(MEMORY_MANAGEMENT);
1851         }
1852 
1853         /*
1854          * Save the swap entry.
1855          */
1856         MmSetSavedSwapEntryPage(Page, SwapEntry);
1857 
1858         /* Map the page into the process address space */
1859         Status = MmCreateVirtualMapping(Process,
1860                                         PAddress,
1861                                         Attributes,
1862                                         Page);
1863         if (!NT_SUCCESS(Status))
1864         {
1865             DPRINT1("Unable to create virtual mapping\n");
1866             KeBugCheck(MEMORY_MANAGEMENT);
1867         }
1868         if (Process)
1869             MmInsertRmap(Page, Process, Address);
1870 
1871         /*
1872          * Mark the offset within the section as having valid, in-memory
1873          * data
1874          */
1875         Entry = MAKE_SSE(Page << PAGE_SHIFT, 1);
1876         MmSetPageEntrySectionSegment(Segment, &Offset, Entry);
1877         MmUnlockSectionSegment(Segment);
1878 
1879         DPRINT("Address 0x%p\n", Address);
1880         return STATUS_SUCCESS;
1881     }
1882     else
1883     {
1884         /* We already have a page on this section offset. Map it into the process address space. */
1885         Page = PFN_FROM_SSE(Entry);
1886 
1887         Status = MmCreateVirtualMapping(Process,
1888                                         PAddress,
1889                                         Attributes,
1890                                         Page);
1891         if (!NT_SUCCESS(Status))
1892         {
1893             DPRINT1("Unable to create virtual mapping\n");
1894             KeBugCheck(MEMORY_MANAGEMENT);
1895         }
1896 
1897         if (Process)
1898             MmInsertRmap(Page, Process, Address);
1899 
1900         /* Take a reference on it */
1901         MmSharePageEntrySectionSegment(Segment, &Offset);
1902         MmUnlockSectionSegment(Segment);
1903 
1904         DPRINT("Address 0x%p\n", Address);
1905         return STATUS_SUCCESS;
1906     }
1907 }
1908 
1909 NTSTATUS
1910 NTAPI
1911 MmAccessFaultSectionView(PMMSUPPORT AddressSpace,
1912                          MEMORY_AREA* MemoryArea,
1913                          PVOID Address,
1914                          BOOLEAN Locked)
1915 {
1916     PMM_SECTION_SEGMENT Segment;
1917     PFN_NUMBER OldPage;
1918     PFN_NUMBER NewPage;
1919     PFN_NUMBER UnmappedPage;
1920     PVOID PAddress;
1921     LARGE_INTEGER Offset;
1922     PMM_REGION Region;
1923     ULONG_PTR Entry;
1924     PEPROCESS Process = MmGetAddressSpaceOwner(AddressSpace);
1925     BOOLEAN Cow = FALSE;
1926     ULONG NewProtect;
1927     BOOLEAN Unmapped;
1928     NTSTATUS Status;
1929 
1930     DPRINT("MmAccessFaultSectionView(%p, %p, %p)\n", AddressSpace, MemoryArea, Address);
1931 
1932     /* Get the region for this address */
1933     Region = MmFindRegion((PVOID)MA_GetStartingAddress(MemoryArea),
1934                         &MemoryArea->SectionData.RegionListHead,
1935                         Address, NULL);
1936     ASSERT(Region != NULL);
1937     if (!(Region->Protect & PAGE_IS_WRITABLE))
1938         return STATUS_ACCESS_VIOLATION;
1939 
1940     /* Make sure we have a page mapping for this address.  */
1941     if (!MmIsPagePresent(Process, Address))
1942     {
1943         NTSTATUS Status = MmNotPresentFaultSectionView(AddressSpace, MemoryArea, Address, Locked);
1944         if (!NT_SUCCESS(Status))
1945         {
1946             /* This is invalid access ! */
1947             return Status;
1948         }
1949     }
1950 
1951     /*
1952      * Check if the page has already been set readwrite
1953      */
1954     if (MmGetPageProtect(Process, Address) & (PAGE_READWRITE | PAGE_EXECUTE_READWRITE))
1955     {
1956         DPRINT("Address 0x%p\n", Address);
1957         return STATUS_SUCCESS;
1958     }
1959 
1960     /* Check if we are doing Copy-On-Write */
1961     Segment = MemoryArea->SectionData.Segment;
1962     Cow = Segment->WriteCopy || (Region->Protect & PAGE_IS_WRITECOPY);
1963 
1964     if (!Cow)
1965     {
1966         /* Simply update page protection and we're done */
1967         MmSetPageProtect(Process, Address, Region->Protect);
1968         return STATUS_SUCCESS;
1969     }
1970 
1971     /* Calculate the new protection & check if we should update the region */
1972     NewProtect = Region->Protect;
1973     if (NewProtect & PAGE_IS_WRITECOPY)
1974     {
1975         NewProtect &= ~PAGE_IS_WRITECOPY;
1976         if (Region->Protect & PAGE_IS_EXECUTABLE)
1977             NewProtect |= PAGE_EXECUTE_READWRITE;
1978         else
1979             NewProtect |= PAGE_READWRITE;
1980         MmAlterRegion(AddressSpace, (PVOID)MA_GetStartingAddress(MemoryArea),
1981                 &MemoryArea->SectionData.RegionListHead,
1982                 Address, PAGE_SIZE, Region->Type, NewProtect,
1983                 MmAlterViewAttributes);
1984     }
1985 
1986     /*
1987      * Find the offset of the page
1988      */
1989     PAddress = MM_ROUND_DOWN(Address, PAGE_SIZE);
1990     Offset.QuadPart = (ULONG_PTR)PAddress - MA_GetStartingAddress(MemoryArea)
1991                       + MemoryArea->SectionData.ViewOffset;
1992 
1993     /* Get the page mapping this section offset. */
1994     MmLockSectionSegment(Segment);
1995     Entry = MmGetPageEntrySectionSegment(Segment, &Offset);
1996 
1997     /* Get the current page mapping for the process */
1998     ASSERT(MmIsPagePresent(Process, PAddress));
1999     OldPage = MmGetPfnForProcess(Process, PAddress);
2000     ASSERT(OldPage != 0);
2001 
2002     if (IS_SWAP_FROM_SSE(Entry) ||
2003             PFN_FROM_SSE(Entry) != OldPage)
2004     {
2005         MmUnlockSectionSegment(Segment);
2006         /* This is a private page. We must only change the page protection. */
2007         MmSetPageProtect(Process, PAddress, NewProtect);
2008         return STATUS_SUCCESS;
2009     }
2010 
2011     /*
2012      * Allocate a page
2013      */
2014     Status = MmRequestPageMemoryConsumer(MC_USER, TRUE, &NewPage);
2015     if (!NT_SUCCESS(Status))
2016     {
2017         MmUnlockSectionSegment(Segment);
2018         return STATUS_NO_MEMORY;
2019     }
2020 
2021     /*
2022      * Copy the old page
2023      */
2024     NT_VERIFY(NT_SUCCESS(MiCopyFromUserPage(NewPage, PAddress)));
2025 
2026     /*
2027      * Unshare the old page.
2028      */
2029     DPRINT("Swapping page (Old %x New %x)\n", OldPage, NewPage);
2030     Unmapped = MmDeleteVirtualMapping(Process, PAddress, NULL, &UnmappedPage);
2031     if (!Unmapped || (UnmappedPage != OldPage))
2032     {
2033         /* Uh , we had a page just before, but suddenly it changes. Someone corrupted us. */
2034         KeBugCheckEx(MEMORY_MANAGEMENT,
2035                     (ULONG_PTR)Process,
2036                     (ULONG_PTR)PAddress,
2037                     (ULONG_PTR)__FILE__,
2038                     __LINE__);
2039     }
2040 
2041     if (Process)
2042         MmDeleteRmap(OldPage, Process, PAddress);
2043     MmUnsharePageEntrySectionSegment(MemoryArea, Segment, &Offset, FALSE, FALSE, NULL);
2044     MmUnlockSectionSegment(Segment);
2045 
2046     /*
2047      * Set the PTE to point to the new page
2048      */
2049     if (!NT_SUCCESS(MmCreateVirtualMapping(Process, PAddress, NewProtect, NewPage)))
2050     {
2051         DPRINT1("MmCreateVirtualMapping failed, unable to create virtual mapping, not out of memory\n");
2052         KeBugCheck(MEMORY_MANAGEMENT);
2053     }
2054 
2055     if (Process)
2056         MmInsertRmap(NewPage, Process, PAddress);
2057 
2058     DPRINT("Address 0x%p\n", Address);
2059     return STATUS_SUCCESS;
2060 }
2061 
2062 NTSTATUS
2063 NTAPI
2064 MmProtectSectionView(PMMSUPPORT AddressSpace,
2065                      PMEMORY_AREA MemoryArea,
2066                      PVOID BaseAddress,
2067                      SIZE_T Length,
2068                      ULONG Protect,
2069                      PULONG OldProtect)
2070 {
2071     PMM_REGION Region;
2072     NTSTATUS Status;
2073     ULONG_PTR MaxLength;
2074 
2075     MaxLength = MA_GetEndingAddress(MemoryArea) - (ULONG_PTR)BaseAddress;
2076     if (Length > MaxLength)
2077         Length = (ULONG)MaxLength;
2078 
2079     Region = MmFindRegion((PVOID)MA_GetStartingAddress(MemoryArea),
2080                           &MemoryArea->SectionData.RegionListHead,
2081                           BaseAddress, NULL);
2082     ASSERT(Region != NULL);
2083 
2084     if ((MemoryArea->Flags & SEC_NO_CHANGE) &&
2085             Region->Protect != Protect)
2086     {
2087         return STATUS_INVALID_PAGE_PROTECTION;
2088     }
2089 
2090     *OldProtect = Region->Protect;
2091     Status = MmAlterRegion(AddressSpace, (PVOID)MA_GetStartingAddress(MemoryArea),
2092                            &MemoryArea->SectionData.RegionListHead,
2093                            BaseAddress, Length, Region->Type, Protect,
2094                            MmAlterViewAttributes);
2095 
2096     return Status;
2097 }
2098 
2099 NTSTATUS NTAPI
2100 MmQuerySectionView(PMEMORY_AREA MemoryArea,
2101                    PVOID Address,
2102                    PMEMORY_BASIC_INFORMATION Info,
2103                    PSIZE_T ResultLength)
2104 {
2105     PMM_REGION Region;
2106     PVOID RegionBaseAddress;
2107     PMM_SECTION_SEGMENT Segment;
2108 
2109     Region = MmFindRegion((PVOID)MA_GetStartingAddress(MemoryArea),
2110                           &MemoryArea->SectionData.RegionListHead,
2111                           Address, &RegionBaseAddress);
2112     if (Region == NULL)
2113     {
2114         return STATUS_UNSUCCESSFUL;
2115     }
2116 
2117     if (MemoryArea->VadNode.u.VadFlags.VadType == VadImageMap)
2118     {
2119         Segment = MemoryArea->SectionData.Segment;
2120         Info->AllocationBase = (PUCHAR)MA_GetStartingAddress(MemoryArea) - Segment->Image.VirtualAddress;
2121         Info->Type = MEM_IMAGE;
2122     }
2123     else
2124     {
2125         Info->AllocationBase = (PVOID)MA_GetStartingAddress(MemoryArea);
2126         Info->Type = MEM_MAPPED;
2127     }
2128     Info->BaseAddress = RegionBaseAddress;
2129     Info->AllocationProtect = MmProtectToValue[MemoryArea->VadNode.u.VadFlags.Protection];
2130     Info->RegionSize = Region->Length;
2131     Info->State = MEM_COMMIT;
2132     Info->Protect = Region->Protect;
2133 
2134     *ResultLength = sizeof(MEMORY_BASIC_INFORMATION);
2135     return STATUS_SUCCESS;
2136 }
2137 
2138 VOID NTAPI
2139 MmpDeleteSection(PVOID ObjectBody)
2140 {
2141     PSECTION Section = ObjectBody;
2142 
2143     /* Check if it's an ARM3, or ReactOS section */
2144     if (!MiIsRosSectionObject(Section))
2145     {
2146         MiDeleteARM3Section(ObjectBody);
2147         return;
2148     }
2149 
2150     DPRINT("MmpDeleteSection(ObjectBody %p)\n", ObjectBody);
2151     if (Section->u.Flags.Image)
2152     {
2153         PMM_IMAGE_SECTION_OBJECT ImageSectionObject = (PMM_IMAGE_SECTION_OBJECT)Section->Segment;
2154 
2155         /*
2156          * NOTE: Section->ImageSection can be NULL for short time
2157          * during the section creating. If we fail for some reason
2158          * until the image section is properly initialized we shouldn't
2159          * process further here.
2160          */
2161         if (Section->Segment == NULL)
2162             return;
2163 
2164         KIRQL OldIrql = MiAcquirePfnLock();
2165         ImageSectionObject->SectionCount--;
2166 
2167         /* We just dereference the first segment */
2168         ASSERT(ImageSectionObject->RefCount > 0);
2169         /* MmDereferenceSegmentWithLock releases PFN lock */
2170         MmDereferenceSegmentWithLock(ImageSectionObject->Segments, OldIrql);
2171     }
2172     else
2173     {
2174         PMM_SECTION_SEGMENT Segment = (PMM_SECTION_SEGMENT)Section->Segment;
2175 
2176         /*
2177          * NOTE: Section->Segment can be NULL for short time
2178          * during the section creating.
2179          */
2180         if (Segment == NULL)
2181             return;
2182 
2183         KIRQL OldIrql = MiAcquirePfnLock();
2184         Segment->SectionCount--;
2185 
2186         /* MmDereferenceSegmentWithLock releases PFN lock */
2187         MmDereferenceSegmentWithLock(Segment, OldIrql);
2188     }
2189 }
2190 
2191 VOID NTAPI
2192 MmpCloseSection(IN PEPROCESS Process OPTIONAL,
2193                 IN PVOID Object,
2194                 IN ACCESS_MASK GrantedAccess,
2195                 IN ULONG ProcessHandleCount,
2196                 IN ULONG SystemHandleCount)
2197 {
2198     DPRINT("MmpCloseSection(OB %p, HC %lu)\n", Object, ProcessHandleCount);
2199 }
2200 
2201 CODE_SEG("INIT")
2202 NTSTATUS
2203 NTAPI
2204 MmCreatePhysicalMemorySection(VOID)
2205 {
2206     PSECTION PhysSection;
2207     NTSTATUS Status;
2208     OBJECT_ATTRIBUTES Obj;
2209     UNICODE_STRING Name = RTL_CONSTANT_STRING(L"\\Device\\PhysicalMemory");
2210     LARGE_INTEGER SectionSize;
2211     HANDLE Handle;
2212     PMM_SECTION_SEGMENT Segment;
2213 
2214     /*
2215      * Create the section mapping physical memory
2216      */
2217     SectionSize.QuadPart = MmHighestPhysicalPage * PAGE_SIZE;
2218     InitializeObjectAttributes(&Obj,
2219                                &Name,
2220                                OBJ_PERMANENT | OBJ_KERNEL_EXCLUSIVE,
2221                                NULL,
2222                                NULL);
2223     /*
2224      * Create the Object
2225      */
2226     Status = ObCreateObject(KernelMode,
2227                             MmSectionObjectType,
2228                             &Obj,
2229                             ExGetPreviousMode(),
2230                             NULL,
2231                             sizeof(*PhysSection),
2232                             0,
2233                             0,
2234                             (PVOID*)&PhysSection);
2235     if (!NT_SUCCESS(Status))
2236     {
2237         DPRINT1("MmCreatePhysicalMemorySection: failed to create object (0x%lx)\n", Status);
2238         return Status;
2239     }
2240 
2241     /*
2242      * Initialize it
2243      */
2244     RtlZeroMemory(PhysSection, sizeof(*PhysSection));
2245 
2246     /* Mark this as a "ROS Section" */
2247     PhysSection->u.Flags.filler = 1;
2248     PhysSection->InitialPageProtection = PAGE_EXECUTE_READWRITE;
2249     PhysSection->u.Flags.PhysicalMemory = 1;
2250     PhysSection->SizeOfSection = SectionSize;
2251     Segment = ExAllocatePoolWithTag(NonPagedPool, sizeof(MM_SECTION_SEGMENT),
2252                                     TAG_MM_SECTION_SEGMENT);
2253     if (Segment == NULL)
2254     {
2255         ObDereferenceObject(PhysSection);
2256         return STATUS_NO_MEMORY;
2257     }
2258     RtlZeroMemory(Segment, sizeof(MM_SECTION_SEGMENT));
2259     PhysSection->Segment = (PSEGMENT)Segment;
2260     Segment->RefCount = 1;
2261 
2262     Segment->ReferenceCount = &Segment->RefCount;
2263     Segment->Flags = &Segment->SegFlags;
2264 
2265     ExInitializeFastMutex(&Segment->Lock);
2266     Segment->Image.FileOffset = 0;
2267     Segment->Protection = PAGE_EXECUTE_READWRITE;
2268     Segment->RawLength = SectionSize;
2269     Segment->Length = SectionSize;
2270     Segment->SegFlags = MM_PHYSICALMEMORY_SEGMENT;
2271     Segment->WriteCopy = FALSE;
2272     Segment->Image.VirtualAddress = 0;
2273     Segment->Image.Characteristics = 0;
2274     MiInitializeSectionPageTable(Segment);
2275 
2276     Status = ObInsertObject(PhysSection,
2277                             NULL,
2278                             SECTION_ALL_ACCESS,
2279                             0,
2280                             NULL,
2281                             &Handle);
2282     if (!NT_SUCCESS(Status))
2283     {
2284         ObDereferenceObject(PhysSection);
2285         return Status;
2286     }
2287     ObCloseHandle(Handle, KernelMode);
2288 
2289     return STATUS_SUCCESS;
2290 }
2291 
2292 CODE_SEG("INIT")
2293 NTSTATUS
2294 NTAPI
2295 MmInitSectionImplementation(VOID)
2296 {
2297     OBJECT_TYPE_INITIALIZER ObjectTypeInitializer;
2298     UNICODE_STRING Name;
2299 
2300     DPRINT("Creating Section Object Type\n");
2301 
2302     /* Initialize the section based root */
2303     ASSERT(MmSectionBasedRoot.NumberGenericTableElements == 0);
2304     MmSectionBasedRoot.BalancedRoot.u1.Parent = &MmSectionBasedRoot.BalancedRoot;
2305 
2306     /* Initialize the Section object type  */
2307     RtlZeroMemory(&ObjectTypeInitializer, sizeof(ObjectTypeInitializer));
2308     RtlInitUnicodeString(&Name, L"Section");
2309     ObjectTypeInitializer.Length = sizeof(ObjectTypeInitializer);
2310     ObjectTypeInitializer.DefaultPagedPoolCharge = sizeof(SECTION);
2311     ObjectTypeInitializer.PoolType = PagedPool;
2312     ObjectTypeInitializer.UseDefaultObject = TRUE;
2313     ObjectTypeInitializer.GenericMapping = MmpSectionMapping;
2314     ObjectTypeInitializer.DeleteProcedure = MmpDeleteSection;
2315     ObjectTypeInitializer.CloseProcedure = MmpCloseSection;
2316     ObjectTypeInitializer.ValidAccessMask = SECTION_ALL_ACCESS;
2317     ObjectTypeInitializer.InvalidAttributes = OBJ_OPENLINK;
2318     ObCreateObjectType(&Name, &ObjectTypeInitializer, NULL, &MmSectionObjectType);
2319 
2320     MmCreatePhysicalMemorySection();
2321 
2322     return STATUS_SUCCESS;
2323 }
2324 
2325 static
2326 NTSTATUS
2327 NTAPI
2328 MmCreateDataFileSection(PSECTION *SectionObject,
2329                         ACCESS_MASK DesiredAccess,
2330                         POBJECT_ATTRIBUTES ObjectAttributes,
2331                         PLARGE_INTEGER UMaximumSize,
2332                         ULONG SectionPageProtection,
2333                         ULONG AllocationAttributes,
2334                         PFILE_OBJECT FileObject,
2335                         BOOLEAN GotFileHandle)
2336 /*
2337  * Create a section backed by a data file
2338  */
2339 {
2340     PSECTION Section;
2341     NTSTATUS Status;
2342     LARGE_INTEGER MaximumSize;
2343     PMM_SECTION_SEGMENT Segment;
2344     KIRQL OldIrql;
2345 
2346     /*
2347      * Create the section
2348      */
2349     Status = ObCreateObject(ExGetPreviousMode(),
2350                             MmSectionObjectType,
2351                             ObjectAttributes,
2352                             ExGetPreviousMode(),
2353                             NULL,
2354                             sizeof(*Section),
2355                             0,
2356                             0,
2357                             (PVOID*)&Section);
2358     if (!NT_SUCCESS(Status))
2359     {
2360         return Status;
2361     }
2362     /*
2363      * Initialize it
2364      */
2365     RtlZeroMemory(Section, sizeof(*Section));
2366 
2367     /* Mark this as a "ROS" section */
2368     Section->u.Flags.filler = 1;
2369     Section->InitialPageProtection = SectionPageProtection;
2370     Section->u.Flags.File = 1;
2371 
2372     if (AllocationAttributes & SEC_NO_CHANGE)
2373         Section->u.Flags.NoChange = 1;
2374     if (AllocationAttributes & SEC_RESERVE)
2375         Section->u.Flags.Reserve = 1;
2376 
2377     if (!GotFileHandle)
2378     {
2379         ASSERT(UMaximumSize != NULL);
2380         // ASSERT(UMaximumSize->QuadPart != 0);
2381         MaximumSize = *UMaximumSize;
2382     }
2383     else
2384     {
2385         LARGE_INTEGER FileSize;
2386         Status = FsRtlGetFileSize(FileObject, &FileSize);
2387         if (!NT_SUCCESS(Status))
2388         {
2389             ObDereferenceObject(Section);
2390             return Status;
2391         }
2392 
2393         /*
2394          * FIXME: Revise this once a locking order for file size changes is
2395          * decided
2396          */
2397         if ((UMaximumSize != NULL) && (UMaximumSize->QuadPart != 0))
2398         {
2399             MaximumSize = *UMaximumSize;
2400         }
2401         else
2402         {
2403             MaximumSize = FileSize;
2404             /* Mapping zero-sized files isn't allowed. */
2405             if (MaximumSize.QuadPart == 0)
2406             {
2407                 ObDereferenceObject(Section);
2408                 return STATUS_MAPPED_FILE_SIZE_ZERO;
2409             }
2410         }
2411 
2412         if (MaximumSize.QuadPart > FileSize.QuadPart)
2413         {
2414             Status = IoSetInformation(FileObject,
2415                                       FileEndOfFileInformation,
2416                                       sizeof(LARGE_INTEGER),
2417                                       &MaximumSize);
2418             if (!NT_SUCCESS(Status))
2419             {
2420                 ObDereferenceObject(Section);
2421                 return STATUS_SECTION_NOT_EXTENDED;
2422             }
2423         }
2424     }
2425 
2426     if (FileObject->SectionObjectPointer == NULL)
2427     {
2428         ObDereferenceObject(Section);
2429         return STATUS_INVALID_FILE_FOR_SECTION;
2430     }
2431 
2432     /*
2433      * Lock the file
2434      */
2435     Status = MmspWaitForFileLock(FileObject);
2436     if (Status != STATUS_SUCCESS)
2437     {
2438         ObDereferenceObject(Section);
2439         return Status;
2440     }
2441 
2442     /* Lock the PFN lock while messing with Section Object pointers */
2443 grab_segment:
2444     OldIrql = MiAcquirePfnLock();
2445     Segment = FileObject->SectionObjectPointer->DataSectionObject;
2446 
2447     while (Segment && (Segment->SegFlags & (MM_SEGMENT_INDELETE | MM_SEGMENT_INCREATE)))
2448     {
2449         MiReleasePfnLock(OldIrql);
2450         KeDelayExecutionThread(KernelMode, FALSE, &TinyTime);
2451         OldIrql = MiAcquirePfnLock();
2452         Segment = FileObject->SectionObjectPointer->DataSectionObject;
2453     }
2454 
2455     /*
2456      * If this file hasn't been mapped as a data file before then allocate a
2457      * section segment to describe the data file mapping
2458      */
2459     if (Segment == NULL)
2460     {
2461         /* Release the lock. ExAllocatePoolWithTag might acquire it */
2462         MiReleasePfnLock(OldIrql);
2463 
2464         Segment = ExAllocatePoolWithTag(NonPagedPool, sizeof(MM_SECTION_SEGMENT),
2465                                         TAG_MM_SECTION_SEGMENT);
2466         if (Segment == NULL)
2467         {
2468             //KeSetEvent((PVOID)&FileObject->Lock, IO_NO_INCREMENT, FALSE);
2469             ObDereferenceObject(Section);
2470             return STATUS_NO_MEMORY;
2471         }
2472 
2473         /* We are creating it */
2474         RtlZeroMemory(Segment, sizeof(*Segment));
2475         Segment->SegFlags = MM_DATAFILE_SEGMENT | MM_SEGMENT_INCREATE;
2476         Segment->RefCount = 1;
2477 
2478         /* Acquire lock again */
2479         OldIrql = MiAcquirePfnLock();
2480 
2481         if (FileObject->SectionObjectPointer->DataSectionObject != NULL)
2482         {
2483             /* Well that's bad luck. Restart it all over */
2484             MiReleasePfnLock(OldIrql);
2485             ExFreePoolWithTag(Segment, TAG_MM_SECTION_SEGMENT);
2486             goto grab_segment;
2487         }
2488 
2489         FileObject->SectionObjectPointer->DataSectionObject = Segment;
2490 
2491         /* We're safe to release the lock now */
2492         MiReleasePfnLock(OldIrql);
2493 
2494         Section->Segment = (PSEGMENT)Segment;
2495 
2496         /* Self-referencing segment */
2497         Segment->Flags = &Segment->SegFlags;
2498         Segment->ReferenceCount = &Segment->RefCount;
2499 
2500         Segment->SectionCount = 1;
2501 
2502         ExInitializeFastMutex(&Segment->Lock);
2503         Segment->FileObject = FileObject;
2504         ObReferenceObject(FileObject);
2505 
2506         Segment->Image.FileOffset = 0;
2507         Segment->Protection = SectionPageProtection;
2508 
2509         Segment->Image.Characteristics = 0;
2510         Segment->WriteCopy = (SectionPageProtection & (PAGE_WRITECOPY | PAGE_EXECUTE_WRITECOPY));
2511         if (AllocationAttributes & SEC_RESERVE)
2512         {
2513             Segment->Length.QuadPart = Segment->RawLength.QuadPart = 0;
2514         }
2515         else
2516         {
2517             Segment->RawLength.QuadPart = MaximumSize.QuadPart;
2518             Segment->Length.QuadPart = PAGE_ROUND_UP(Segment->RawLength.QuadPart);
2519         }
2520         Segment->Image.VirtualAddress = 0;
2521         MiInitializeSectionPageTable(Segment);
2522 
2523         /* We're good to use it now */
2524         OldIrql = MiAcquirePfnLock();
2525         Segment->SegFlags &= ~MM_SEGMENT_INCREATE;
2526         MiReleasePfnLock(OldIrql);
2527     }
2528     else
2529     {
2530         Section->Segment = (PSEGMENT)Segment;
2531         InterlockedIncrement64(&Segment->RefCount);
2532         InterlockedIncrementUL(&Segment->SectionCount);
2533 
2534         MiReleasePfnLock(OldIrql);
2535 
2536         MmLockSectionSegment(Segment);
2537 
2538         if (MaximumSize.QuadPart > Segment->RawLength.QuadPart &&
2539                 !(AllocationAttributes & SEC_RESERVE))
2540         {
2541             Segment->RawLength.QuadPart = MaximumSize.QuadPart;
2542             Segment->Length.QuadPart = PAGE_ROUND_UP(Segment->RawLength.QuadPart);
2543         }
2544 
2545         MmUnlockSectionSegment(Segment);
2546     }
2547     Section->SizeOfSection = MaximumSize;
2548 
2549     //KeSetEvent((PVOID)&FileObject->Lock, IO_NO_INCREMENT, FALSE);
2550     *SectionObject = Section;
2551     return STATUS_SUCCESS;
2552 }
2553 
2554 /*
2555  TODO: not that great (declaring loaders statically, having to declare all of
2556  them, having to keep them extern, etc.), will fix in the future
2557 */
2558 extern NTSTATUS NTAPI PeFmtCreateSection
2559 (
2560     IN CONST VOID * FileHeader,
2561     IN SIZE_T FileHeaderSize,
2562     IN PVOID File,
2563     OUT PMM_IMAGE_SECTION_OBJECT ImageSectionObject,
2564     OUT PULONG Flags,
2565     IN PEXEFMT_CB_READ_FILE ReadFileCb,
2566     IN PEXEFMT_CB_ALLOCATE_SEGMENTS AllocateSegmentsCb
2567 );
2568 
2569 extern NTSTATUS NTAPI ElfFmtCreateSection
2570 (
2571     IN CONST VOID * FileHeader,
2572     IN SIZE_T FileHeaderSize,
2573     IN PVOID File,
2574     OUT PMM_IMAGE_SECTION_OBJECT ImageSectionObject,
2575     OUT PULONG Flags,
2576     IN PEXEFMT_CB_READ_FILE ReadFileCb,
2577     IN PEXEFMT_CB_ALLOCATE_SEGMENTS AllocateSegmentsCb
2578 );
2579 
2580 static PEXEFMT_LOADER ExeFmtpLoaders[] =
2581 {
2582     PeFmtCreateSection,
2583 #ifdef __ELF
2584     ElfFmtCreateSection
2585 #endif
2586 };
2587 
2588 static
2589 PMM_SECTION_SEGMENT
2590 NTAPI
2591 ExeFmtpAllocateSegments(IN ULONG NrSegments)
2592 {
2593     SIZE_T SizeOfSegments;
2594     PMM_SECTION_SEGMENT Segments;
2595 
2596     /* TODO: check for integer overflow */
2597     SizeOfSegments = sizeof(MM_SECTION_SEGMENT) * NrSegments;
2598 
2599     Segments = ExAllocatePoolWithTag(NonPagedPool,
2600                                      SizeOfSegments,
2601                                      TAG_MM_SECTION_SEGMENT);
2602 
2603     if(Segments)
2604         RtlZeroMemory(Segments, SizeOfSegments);
2605 
2606     return Segments;
2607 }
2608 static
2609 NTSTATUS
2610 NTAPI
2611 ExeFmtpReadFile(IN PVOID File,
2612                 IN PLARGE_INTEGER Offset,
2613                 IN ULONG Length,
2614                 OUT PVOID * Data,
2615                 OUT PVOID * AllocBase,
2616                 OUT PULONG ReadSize)
2617 {
2618     NTSTATUS Status;
2619     LARGE_INTEGER FileOffset;
2620     ULONG AdjustOffset;
2621     ULONG OffsetAdjustment;
2622     ULONG BufferSize;
2623     ULONG UsedSize;
2624     PVOID Buffer;
2625     PFILE_OBJECT FileObject = File;
2626     IO_STATUS_BLOCK Iosb;
2627 
2628     ASSERT_IRQL_LESS(DISPATCH_LEVEL);
2629 
2630     if(Length == 0)
2631     {
2632         KeBugCheck(MEMORY_MANAGEMENT);
2633     }
2634 
2635     FileOffset = *Offset;
2636 
2637     /* Negative/special offset: it cannot be used in this context */
2638     if(FileOffset.u.HighPart < 0)
2639     {
2640         KeBugCheck(MEMORY_MANAGEMENT);
2641     }
2642 
2643     AdjustOffset = PAGE_ROUND_DOWN(FileOffset.u.LowPart);
2644     OffsetAdjustment = FileOffset.u.LowPart - AdjustOffset;
2645     FileOffset.u.LowPart = AdjustOffset;
2646 
2647     BufferSize = Length + OffsetAdjustment;
2648     BufferSize = PAGE_ROUND_UP(BufferSize);
2649 
2650     /*
2651      * It's ok to use paged pool, because this is a temporary buffer only used in
2652      * the loading of executables. The assumption is that MmCreateSection is
2653      * always called at low IRQLs and that these buffers don't survive a brief
2654      * initialization phase
2655      */
2656     Buffer = ExAllocatePoolWithTag(PagedPool, BufferSize, 'rXmM');
2657     if (!Buffer)
2658     {
2659         return STATUS_INSUFFICIENT_RESOURCES;
2660     }
2661 
2662     Status = MiSimpleRead(FileObject, &FileOffset, Buffer, BufferSize, TRUE, &Iosb);
2663 
2664     UsedSize = (ULONG)Iosb.Information;
2665 
2666     if(NT_SUCCESS(Status) && UsedSize < OffsetAdjustment)
2667     {
2668         Status = STATUS_IN_PAGE_ERROR;
2669         ASSERT(!NT_SUCCESS(Status));
2670     }
2671 
2672     if(NT_SUCCESS(Status))
2673     {
2674         *Data = (PVOID)((ULONG_PTR)Buffer + OffsetAdjustment);
2675         *AllocBase = Buffer;
2676         *ReadSize = UsedSize - OffsetAdjustment;
2677     }
2678     else
2679     {
2680         ExFreePoolWithTag(Buffer, 'rXmM');
2681     }
2682 
2683     return Status;
2684 }
2685 
2686 #ifdef NASSERT
2687 # define MmspAssertSegmentsSorted(OBJ_) ((void)0)
2688 # define MmspAssertSegmentsNoOverlap(OBJ_) ((void)0)
2689 # define MmspAssertSegmentsPageAligned(OBJ_) ((void)0)
2690 #else
2691 static
2692 VOID
2693 NTAPI
2694 MmspAssertSegmentsSorted(IN PMM_IMAGE_SECTION_OBJECT ImageSectionObject)
2695 {
2696     ULONG i;
2697 
2698     for( i = 1; i < ImageSectionObject->NrSegments; ++ i )
2699     {
2700         ASSERT(ImageSectionObject->Segments[i].Image.VirtualAddress >=
2701                ImageSectionObject->Segments[i - 1].Image.VirtualAddress);
2702     }
2703 }
2704 
2705 static
2706 VOID
2707 NTAPI
2708 MmspAssertSegmentsNoOverlap(IN PMM_IMAGE_SECTION_OBJECT ImageSectionObject)
2709 {
2710     ULONG i;
2711 
2712     MmspAssertSegmentsSorted(ImageSectionObject);
2713 
2714     for( i = 0; i < ImageSectionObject->NrSegments; ++ i )
2715     {
2716         ASSERT(ImageSectionObject->Segments[i].Length.QuadPart > 0);
2717 
2718         if(i > 0)
2719         {
2720             ASSERT(ImageSectionObject->Segments[i].Image.VirtualAddress >=
2721                    (ImageSectionObject->Segments[i - 1].Image.VirtualAddress +
2722                     ImageSectionObject->Segments[i - 1].Length.QuadPart));
2723         }
2724     }
2725 }
2726 
2727 static
2728 VOID
2729 NTAPI
2730 MmspAssertSegmentsPageAligned(IN PMM_IMAGE_SECTION_OBJECT ImageSectionObject)
2731 {
2732     ULONG i;
2733 
2734     for( i = 0; i < ImageSectionObject->NrSegments; ++ i )
2735     {
2736         ASSERT((ImageSectionObject->Segments[i].Image.VirtualAddress % PAGE_SIZE) == 0);
2737         ASSERT((ImageSectionObject->Segments[i].Length.QuadPart % PAGE_SIZE) == 0);
2738     }
2739 }
2740 #endif
2741 
2742 static
2743 int
2744 __cdecl
2745 MmspCompareSegments(const void * x,
2746                     const void * y)
2747 {
2748     const MM_SECTION_SEGMENT *Segment1 = (const MM_SECTION_SEGMENT *)x;
2749     const MM_SECTION_SEGMENT *Segment2 = (const MM_SECTION_SEGMENT *)y;
2750 
2751     if (Segment1->Image.VirtualAddress > Segment2->Image.VirtualAddress)
2752         return 1;
2753     else if (Segment1->Image.VirtualAddress < Segment2->Image.VirtualAddress)
2754         return -1;
2755     else
2756         return 0;
2757 }
2758 
2759 /*
2760  * Ensures an image section's segments are sorted in memory
2761  */
2762 static
2763 VOID
2764 NTAPI
2765 MmspSortSegments(IN OUT PMM_IMAGE_SECTION_OBJECT ImageSectionObject,
2766                  IN ULONG Flags)
2767 {
2768     if (Flags & EXEFMT_LOAD_ASSUME_SEGMENTS_SORTED)
2769     {
2770         MmspAssertSegmentsSorted(ImageSectionObject);
2771     }
2772     else
2773     {
2774         qsort(ImageSectionObject->Segments,
2775               ImageSectionObject->NrSegments,
2776               sizeof(ImageSectionObject->Segments[0]),
2777               MmspCompareSegments);
2778     }
2779 }
2780 
2781 
2782 /*
2783  * Ensures an image section's segments don't overlap in memory and don't have
2784  * gaps and don't have a null size. We let them map to overlapping file regions,
2785  * though - that's not necessarily an error
2786  */
2787 static
2788 BOOLEAN
2789 NTAPI
2790 MmspCheckSegmentBounds
2791 (
2792     IN OUT PMM_IMAGE_SECTION_OBJECT ImageSectionObject,
2793     IN ULONG Flags
2794 )
2795 {
2796     ULONG i;
2797 
2798     if (Flags & EXEFMT_LOAD_ASSUME_SEGMENTS_NO_OVERLAP)
2799     {
2800         MmspAssertSegmentsNoOverlap(ImageSectionObject);
2801         return TRUE;
2802     }
2803 
2804     ASSERT(ImageSectionObject->NrSegments >= 1);
2805 
2806     for ( i = 0; i < ImageSectionObject->NrSegments; ++ i )
2807     {
2808         if(ImageSectionObject->Segments[i].Length.QuadPart == 0)
2809         {
2810             return FALSE;
2811         }
2812 
2813         if(i > 0)
2814         {
2815             /*
2816              * TODO: relax the limitation on gaps. For example, gaps smaller than a
2817              * page could be OK (Windows seems to be OK with them), and larger gaps
2818              * could lead to image sections spanning several discontiguous regions
2819              * (NtMapViewOfSection could then refuse to map them, and they could
2820              * e.g. only be allowed as parameters to NtCreateProcess, like on UNIX)
2821              */
2822             if ((ImageSectionObject->Segments[i - 1].Image.VirtualAddress +
2823                     ImageSectionObject->Segments[i - 1].Length.QuadPart) !=
2824                     ImageSectionObject->Segments[i].Image.VirtualAddress)
2825             {
2826                 return FALSE;
2827             }
2828         }
2829     }
2830 
2831     return TRUE;
2832 }
2833 
2834 /*
2835  * Merges and pads an image section's segments until they all are page-aligned
2836  * and have a size that is a multiple of the page size
2837  */
2838 static
2839 BOOLEAN
2840 NTAPI
2841 MmspPageAlignSegments
2842 (
2843     IN OUT PMM_IMAGE_SECTION_OBJECT ImageSectionObject,
2844     IN ULONG Flags
2845 )
2846 {
2847     ULONG i;
2848     ULONG LastSegment;
2849     PMM_SECTION_SEGMENT EffectiveSegment;
2850 
2851     if (Flags & EXEFMT_LOAD_ASSUME_SEGMENTS_PAGE_ALIGNED)
2852     {
2853         MmspAssertSegmentsPageAligned(ImageSectionObject);
2854         return TRUE;
2855     }
2856 
2857     LastSegment = 0;
2858     EffectiveSegment = &ImageSectionObject->Segments[LastSegment];
2859 
2860     for ( i = 0; i < ImageSectionObject->NrSegments; ++ i )
2861     {
2862         /*
2863          * The first segment requires special handling
2864          */
2865         if (i == 0)
2866         {
2867             ULONG_PTR VirtualAddress;
2868             ULONG_PTR VirtualOffset;
2869 
2870             VirtualAddress = EffectiveSegment->Image.VirtualAddress;
2871 
2872             /* Round down the virtual address to the nearest page */
2873             EffectiveSegment->Image.VirtualAddress = PAGE_ROUND_DOWN(VirtualAddress);
2874 
2875             /* Round up the virtual size to the nearest page */
2876             EffectiveSegment->Length.QuadPart = PAGE_ROUND_UP(VirtualAddress + EffectiveSegment->Length.QuadPart) -
2877                                                 EffectiveSegment->Image.VirtualAddress;
2878 
2879             /* Adjust the raw address and size */
2880             VirtualOffset = VirtualAddress - EffectiveSegment->Image.VirtualAddress;
2881 
2882             if (EffectiveSegment->Image.FileOffset < VirtualOffset)
2883             {
2884                 return FALSE;
2885             }
2886 
2887             /*
2888              * Garbage in, garbage out: unaligned base addresses make the file
2889              * offset point in curious and odd places, but that's what we were
2890              * asked for
2891              */
2892             EffectiveSegment->Image.FileOffset -= VirtualOffset;
2893             EffectiveSegment->RawLength.QuadPart += VirtualOffset;
2894         }
2895         else
2896         {
2897             PMM_SECTION_SEGMENT Segment = &ImageSectionObject->Segments[i];
2898             ULONG_PTR EndOfEffectiveSegment;
2899 
2900             EndOfEffectiveSegment = (ULONG_PTR)(EffectiveSegment->Image.VirtualAddress + EffectiveSegment->Length.QuadPart);
2901             ASSERT((EndOfEffectiveSegment % PAGE_SIZE) == 0);
2902 
2903             /*
2904              * The current segment begins exactly where the current effective
2905              * segment ended, therefore beginning a new effective segment
2906              */
2907             if (EndOfEffectiveSegment == Segment->Image.VirtualAddress)
2908             {
2909                 LastSegment ++;
2910                 ASSERT(LastSegment <= i);
2911                 ASSERT(LastSegment < ImageSectionObject->NrSegments);
2912 
2913                 EffectiveSegment = &ImageSectionObject->Segments[LastSegment];
2914 
2915                 if (LastSegment != i)
2916                 {
2917                     /*
2918                      * Copy the current segment. If necessary, the effective segment
2919                      * will be expanded later
2920                      */
2921                     *EffectiveSegment = *Segment;
2922                 }
2923 
2924                 /*
2925                  * Page-align the virtual size. We know for sure the virtual address
2926                  * already is
2927                  */
2928                 ASSERT((EffectiveSegment->Image.VirtualAddress % PAGE_SIZE) == 0);
2929                 EffectiveSegment->Length.QuadPart = PAGE_ROUND_UP(EffectiveSegment->Length.QuadPart);
2930             }
2931             /*
2932              * The current segment is still part of the current effective segment:
2933              * extend the effective segment to reflect this
2934              */
2935             else if (EndOfEffectiveSegment > Segment->Image.VirtualAddress)
2936             {
2937                 static const ULONG FlagsToProtection[16] =
2938                 {
2939                     PAGE_NOACCESS,
2940                     PAGE_READONLY,
2941                     PAGE_READWRITE,
2942                     PAGE_READWRITE,
2943                     PAGE_EXECUTE_READ,
2944                     PAGE_EXECUTE_READ,
2945                     PAGE_EXECUTE_READWRITE,
2946                     PAGE_EXECUTE_READWRITE,
2947                     PAGE_WRITECOPY,
2948                     PAGE_WRITECOPY,
2949                     PAGE_WRITECOPY,
2950                     PAGE_WRITECOPY,
2951                     PAGE_EXECUTE_WRITECOPY,
2952                     PAGE_EXECUTE_WRITECOPY,
2953                     PAGE_EXECUTE_WRITECOPY,
2954                     PAGE_EXECUTE_WRITECOPY
2955                 };
2956 
2957                 unsigned ProtectionFlags;
2958 
2959                 /*
2960                  * Extend the file size
2961                  */
2962 
2963                 /* Unaligned segments must be contiguous within the file */
2964                 if (Segment->Image.FileOffset != (EffectiveSegment->Image.FileOffset +
2965                                                   EffectiveSegment->RawLength.QuadPart))
2966                 {
2967                     return FALSE;
2968                 }
2969 
2970                 EffectiveSegment->RawLength.QuadPart += Segment->RawLength.QuadPart;
2971 
2972                 /*
2973                  * Extend the virtual size
2974                  */
2975                 ASSERT(PAGE_ROUND_UP(Segment->Image.VirtualAddress + Segment->Length.QuadPart) >= EndOfEffectiveSegment);
2976 
2977                 EffectiveSegment->Length.QuadPart = PAGE_ROUND_UP(Segment->Image.VirtualAddress + Segment->Length.QuadPart) -
2978                                                     EffectiveSegment->Image.VirtualAddress;
2979 
2980                 /*
2981                  * Merge the protection
2982                  */
2983                 EffectiveSegment->Protection |= Segment->Protection;
2984 
2985                 /* Clean up redundance */
2986                 ProtectionFlags = 0;
2987 
2988                 if(EffectiveSegment->Protection & PAGE_IS_READABLE)
2989                     ProtectionFlags |= 1 << 0;
2990 
2991                 if(EffectiveSegment->Protection & PAGE_IS_WRITABLE)
2992                     ProtectionFlags |= 1 << 1;
2993 
2994                 if(EffectiveSegment->Protection & PAGE_IS_EXECUTABLE)
2995                     ProtectionFlags |= 1 << 2;
2996 
2997                 if(EffectiveSegment->Protection & PAGE_IS_WRITECOPY)
2998                     ProtectionFlags |= 1 << 3;
2999 
3000                 ASSERT(ProtectionFlags < 16);
3001                 EffectiveSegment->Protection = FlagsToProtection[ProtectionFlags];
3002 
3003                 /* If a segment was required to be shared and cannot, fail */
3004                 if(!(Segment->Protection & PAGE_IS_WRITECOPY) &&
3005                         EffectiveSegment->Protection & PAGE_IS_WRITECOPY)
3006                 {
3007                     return FALSE;
3008                 }
3009             }
3010             /*
3011              * We assume no holes between segments at this point
3012              */
3013             else
3014             {
3015                 KeBugCheck(MEMORY_MANAGEMENT);
3016             }
3017         }
3018     }
3019     ImageSectionObject->NrSegments = LastSegment + 1;
3020 
3021     return TRUE;
3022 }
3023 
3024 NTSTATUS
3025 ExeFmtpCreateImageSection(PFILE_OBJECT FileObject,
3026                           PMM_IMAGE_SECTION_OBJECT ImageSectionObject)
3027 {
3028     LARGE_INTEGER Offset;
3029     PVOID FileHeader;
3030     PVOID FileHeaderBuffer;
3031     ULONG FileHeaderSize;
3032     ULONG Flags;
3033     ULONG OldNrSegments;
3034     NTSTATUS Status;
3035     ULONG i;
3036 
3037     /*
3038      * Read the beginning of the file (2 pages). Should be enough to contain
3039      * all (or most) of the headers
3040      */
3041     Offset.QuadPart = 0;
3042 
3043     Status = ExeFmtpReadFile (FileObject,
3044                               &Offset,
3045                               PAGE_SIZE * 2,
3046                               &FileHeader,
3047                               &FileHeaderBuffer,
3048                               &FileHeaderSize);
3049 
3050     if (!NT_SUCCESS(Status))
3051         return Status;
3052 
3053     if (FileHeaderSize == 0)
3054     {
3055         ExFreePool(FileHeaderBuffer);
3056         return STATUS_UNSUCCESSFUL;
3057     }
3058 
3059     /*
3060      * Look for a loader that can handle this executable
3061      */
3062     for (i = 0; i < RTL_NUMBER_OF(ExeFmtpLoaders); ++ i)
3063     {
3064         Flags = 0;
3065 
3066         Status = ExeFmtpLoaders[i](FileHeader,
3067                                    FileHeaderSize,
3068                                    FileObject,
3069                                    ImageSectionObject,
3070                                    &Flags,
3071                                    ExeFmtpReadFile,
3072                                    ExeFmtpAllocateSegments);
3073 
3074         if (!NT_SUCCESS(Status))
3075         {
3076             if (ImageSectionObject->Segments)
3077             {
3078                 ExFreePool(ImageSectionObject->Segments);
3079                 ImageSectionObject->Segments = NULL;
3080             }
3081         }
3082 
3083         if (Status != STATUS_ROS_EXEFMT_UNKNOWN_FORMAT)
3084             break;
3085     }
3086 
3087     ExFreePoolWithTag(FileHeaderBuffer, 'rXmM');
3088 
3089     /*
3090      * No loader handled the format
3091      */
3092     if (Status == STATUS_ROS_EXEFMT_UNKNOWN_FORMAT)
3093     {
3094         Status = STATUS_INVALID_IMAGE_NOT_MZ;
3095         ASSERT(!NT_SUCCESS(Status));
3096     }
3097 
3098     if (!NT_SUCCESS(Status))
3099         return Status;
3100 
3101     ASSERT(ImageSectionObject->Segments != NULL);
3102     ASSERT(ImageSectionObject->RefCount > 0);
3103 
3104     /*
3105      * Some defaults
3106      */
3107     /* FIXME? are these values platform-dependent? */
3108     if (ImageSectionObject->ImageInformation.MaximumStackSize == 0)
3109         ImageSectionObject->ImageInformation.MaximumStackSize = 0x40000;
3110 
3111     if(ImageSectionObject->ImageInformation.CommittedStackSize == 0)
3112         ImageSectionObject->ImageInformation.CommittedStackSize = 0x1000;
3113 
3114     if(ImageSectionObject->BasedAddress == NULL)
3115     {
3116         if(ImageSectionObject->ImageInformation.ImageCharacteristics & IMAGE_FILE_DLL)
3117             ImageSectionObject->BasedAddress = (PVOID)0x10000000;
3118         else
3119             ImageSectionObject->BasedAddress = (PVOID)0x00400000;
3120     }
3121 
3122     /*
3123      * And now the fun part: fixing the segments
3124      */
3125 
3126     /* Sort them by virtual address */
3127     MmspSortSegments(ImageSectionObject, Flags);
3128 
3129     /* Ensure they don't overlap in memory */
3130     if (!MmspCheckSegmentBounds(ImageSectionObject, Flags))
3131         return STATUS_INVALID_IMAGE_FORMAT;
3132 
3133     /* Ensure they are aligned */
3134     OldNrSegments = ImageSectionObject->NrSegments;
3135 
3136     if (!MmspPageAlignSegments(ImageSectionObject, Flags))
3137         return STATUS_INVALID_IMAGE_FORMAT;
3138 
3139     /* Trim them if the alignment phase merged some of them */
3140     if (ImageSectionObject->NrSegments < OldNrSegments)
3141     {
3142         PMM_SECTION_SEGMENT Segments;
3143         SIZE_T SizeOfSegments;
3144 
3145         SizeOfSegments = sizeof(MM_SECTION_SEGMENT) * ImageSectionObject->NrSegments;
3146 
3147         Segments = ExAllocatePoolWithTag(PagedPool,
3148                                          SizeOfSegments,
3149                                          TAG_MM_SECTION_SEGMENT);
3150 
3151         if (Segments == NULL)
3152             return STATUS_INSUFFICIENT_RESOURCES;
3153 
3154         RtlCopyMemory(Segments, ImageSectionObject->Segments, SizeOfSegments);
3155         ExFreePool(ImageSectionObject->Segments);
3156         ImageSectionObject->Segments = Segments;
3157     }
3158 
3159     /* And finish their initialization */
3160     for ( i = 0; i < ImageSectionObject->NrSegments; ++ i )
3161     {
3162         ExInitializeFastMutex(&ImageSectionObject->Segments[i].Lock);
3163         ImageSectionObject->Segments[i].ReferenceCount = &ImageSectionObject->RefCount;
3164         ImageSectionObject->Segments[i].Flags = &ImageSectionObject->SegFlags;
3165         MiInitializeSectionPageTable(&ImageSectionObject->Segments[i]);
3166         ImageSectionObject->Segments[i].FileObject = FileObject;
3167     }
3168 
3169     ASSERT(ImageSectionObject->RefCount > 0);
3170 
3171     ImageSectionObject->FileObject = FileObject;
3172 
3173     ASSERT(NT_SUCCESS(Status));
3174     return Status;
3175 }
3176 
3177 NTSTATUS
3178 MmCreateImageSection(PSECTION *SectionObject,
3179                      ACCESS_MASK DesiredAccess,
3180                      POBJECT_ATTRIBUTES ObjectAttributes,
3181                      PLARGE_INTEGER UMaximumSize,
3182                      ULONG SectionPageProtection,
3183                      ULONG AllocationAttributes,
3184                      PFILE_OBJECT FileObject)
3185 {
3186     PSECTION Section;
3187     NTSTATUS Status;
3188     PMM_IMAGE_SECTION_OBJECT ImageSectionObject;
3189     KIRQL OldIrql;
3190 
3191 
3192     if (FileObject == NULL)
3193         return STATUS_INVALID_FILE_FOR_SECTION;
3194 
3195     if (FileObject->SectionObjectPointer == NULL)
3196     {
3197         DPRINT1("Denying section creation due to missing cache initialization\n");
3198         return STATUS_INVALID_FILE_FOR_SECTION;
3199     }
3200 
3201     /*
3202      * Create the section
3203      */
3204     Status = ObCreateObject (ExGetPreviousMode(),
3205                              MmSectionObjectType,
3206                              ObjectAttributes,
3207                              ExGetPreviousMode(),
3208                              NULL,
3209                              sizeof(*Section),
3210                              0,
3211                              0,
3212                              (PVOID*)(PVOID)&Section);
3213     if (!NT_SUCCESS(Status))
3214     {
3215         return Status;
3216     }
3217 
3218     /*
3219      * Initialize it
3220      */
3221     RtlZeroMemory(Section, sizeof(*Section));
3222 
3223     /* Mark this as a "ROS" Section */
3224     Section->u.Flags.filler = 1;
3225 
3226     Section->InitialPageProtection = SectionPageProtection;
3227     Section->u.Flags.File = 1;
3228     Section->u.Flags.Image = 1;
3229     if (AllocationAttributes & SEC_NO_CHANGE)
3230         Section->u.Flags.NoChange = 1;
3231 
3232 grab_image_section_object:
3233     OldIrql = MiAcquirePfnLock();
3234 
3235     /* Wait for it to be properly created or deleted */
3236     ImageSectionObject = FileObject->SectionObjectPointer->ImageSectionObject;
3237     while(ImageSectionObject && (ImageSectionObject->SegFlags & (MM_SEGMENT_INDELETE | MM_SEGMENT_INCREATE)))
3238     {
3239         MiReleasePfnLock(OldIrql);
3240 
3241         KeDelayExecutionThread(KernelMode, FALSE, &TinyTime);
3242 
3243         OldIrql = MiAcquirePfnLock();
3244         ImageSectionObject = FileObject->SectionObjectPointer->ImageSectionObject;
3245     }
3246 
3247     if (ImageSectionObject == NULL)
3248     {
3249         NTSTATUS StatusExeFmt;
3250 
3251         /* Release the lock because ExAllocatePoolWithTag could need to acquire it */
3252         MiReleasePfnLock(OldIrql);
3253 
3254         ImageSectionObject = ExAllocatePoolZero(NonPagedPool, sizeof(MM_IMAGE_SECTION_OBJECT), TAG_MM_SECTION_SEGMENT);
3255         if (ImageSectionObject == NULL)
3256         {
3257             ObDereferenceObject(Section);
3258             return STATUS_NO_MEMORY;
3259         }
3260 
3261         ImageSectionObject->SegFlags = MM_SEGMENT_INCREATE;
3262         ImageSectionObject->RefCount = 1;
3263         ImageSectionObject->SectionCount = 1;
3264 
3265         OldIrql = MiAcquirePfnLock();
3266         if (FileObject->SectionObjectPointer->ImageSectionObject != NULL)
3267         {
3268             MiReleasePfnLock(OldIrql);
3269             /* Bad luck. Start over */
3270             ExFreePoolWithTag(ImageSectionObject, TAG_MM_SECTION_SEGMENT);
3271             goto grab_image_section_object;
3272         }
3273 
3274         FileObject->SectionObjectPointer->ImageSectionObject = ImageSectionObject;
3275 
3276         MiReleasePfnLock(OldIrql);
3277 
3278         /* Purge the cache */
3279         CcFlushCache(FileObject->SectionObjectPointer, NULL, 0, NULL);
3280 
3281         StatusExeFmt = ExeFmtpCreateImageSection(FileObject, ImageSectionObject);
3282 
3283         if (!NT_SUCCESS(StatusExeFmt))
3284         {
3285             /* Unset */
3286             OldIrql = MiAcquirePfnLock();
3287             FileObject->SectionObjectPointer->ImageSectionObject = NULL;
3288             MiReleasePfnLock(OldIrql);
3289 
3290             if(ImageSectionObject->Segments != NULL)
3291                 ExFreePool(ImageSectionObject->Segments);
3292 
3293             /*
3294              * If image file is empty, then return that the file is invalid for section
3295              */
3296             Status = StatusExeFmt;
3297             if (StatusExeFmt == STATUS_END_OF_FILE)
3298             {
3299                 Status = STATUS_INVALID_FILE_FOR_SECTION;
3300             }
3301 
3302             ExFreePoolWithTag(ImageSectionObject, TAG_MM_SECTION_SEGMENT);
3303             ObDereferenceObject(Section);
3304             return Status;
3305         }
3306 
3307         Section->Segment = (PSEGMENT)ImageSectionObject;
3308         ASSERT(ImageSectionObject->Segments);
3309         ASSERT(ImageSectionObject->RefCount > 0);
3310 
3311         /*
3312          * Lock the file
3313          */
3314         Status = MmspWaitForFileLock(FileObject);
3315         if (!NT_SUCCESS(Status))
3316         {
3317             /* Unset */
3318             OldIrql = MiAcquirePfnLock();
3319             FileObject->SectionObjectPointer->ImageSectionObject = NULL;
3320             MiReleasePfnLock(OldIrql);
3321 
3322             ExFreePool(ImageSectionObject->Segments);
3323             ExFreePool(ImageSectionObject);
3324             ObDereferenceObject(Section);
3325             return Status;
3326         }
3327 
3328         OldIrql = MiAcquirePfnLock();
3329         ImageSectionObject->SegFlags &= ~MM_SEGMENT_INCREATE;
3330 
3331         /* Take a ref on the file on behalf of the newly created structure */
3332         ObReferenceObject(FileObject);
3333 
3334         MiReleasePfnLock(OldIrql);
3335 
3336         Status = StatusExeFmt;
3337     }
3338     else
3339     {
3340         /* If FS driver called for delete, tell them it's not possible anymore. */
3341         ImageSectionObject->SegFlags &= ~MM_IMAGE_SECTION_FLUSH_DELETE;
3342 
3343         /* Take one ref */
3344         InterlockedIncrement64(&ImageSectionObject->RefCount);
3345         ImageSectionObject->SectionCount++;
3346 
3347         MiReleasePfnLock(OldIrql);
3348 
3349         Section->Segment = (PSEGMENT)ImageSectionObject;
3350 
3351         Status = STATUS_SUCCESS;
3352     }
3353     //KeSetEvent((PVOID)&FileObject->Lock, IO_NO_INCREMENT, FALSE);
3354     *SectionObject = Section;
3355     ASSERT(ImageSectionObject->RefCount > 0);
3356 
3357     return Status;
3358 }
3359 
3360 
3361 
3362 static NTSTATUS
3363 MmMapViewOfSegment(
3364     PMMSUPPORT AddressSpace,
3365     BOOLEAN AsImage,
3366     PMM_SECTION_SEGMENT Segment,
3367     PVOID* BaseAddress,
3368     SIZE_T ViewSize,
3369     ULONG Protect,
3370     LONGLONG ViewOffset,
3371     ULONG AllocationType)
3372 {
3373     PMEMORY_AREA MArea;
3374     NTSTATUS Status;
3375     ULONG Granularity;
3376 
3377     ASSERT(ViewSize != 0);
3378 
3379     if (Segment->WriteCopy)
3380     {
3381         /* We have to do this because the not present fault
3382          * and access fault handlers depend on the protection
3383          * that should be granted AFTER the COW fault takes
3384          * place to be in Region->Protect. The not present fault
3385          * handler changes this to the correct protection for COW when
3386          * mapping the pages into the process's address space. If a COW
3387          * fault takes place, the access fault handler sets the page protection
3388          * to these values for the newly copied pages
3389          */
3390         if (Protect == PAGE_WRITECOPY)
3391             Protect = PAGE_READWRITE;
3392         else if (Protect == PAGE_EXECUTE_WRITECOPY)
3393             Protect = PAGE_EXECUTE_READWRITE;
3394     }
3395 
3396     if (*BaseAddress == NULL)
3397         Granularity = MM_ALLOCATION_GRANULARITY;
3398     else
3399         Granularity = PAGE_SIZE;
3400 
3401 #ifdef NEWCC
3402     if (Segment->Flags & MM_DATAFILE_SEGMENT)
3403     {
3404         LARGE_INTEGER FileOffset;
3405         FileOffset.QuadPart = ViewOffset;
3406         ObReferenceObject(Section);
3407         return _MiMapViewOfSegment(AddressSpace, Segment, BaseAddress, ViewSize, Protect, &FileOffset, AllocationType, __FILE__, __LINE__);
3408     }
3409 #endif
3410     Status = MmCreateMemoryArea(AddressSpace,
3411                                 MEMORY_AREA_SECTION_VIEW,
3412                                 BaseAddress,
3413                                 ViewSize,
3414                                 Protect,
3415                                 &MArea,
3416                                 AllocationType,
3417                                 Granularity);
3418     if (!NT_SUCCESS(Status))
3419     {
3420         DPRINT1("Mapping between 0x%p and 0x%p failed (%X).\n",
3421                 (*BaseAddress), (char*)(*BaseAddress) + ViewSize, Status);
3422         return Status;
3423     }
3424 
3425     InterlockedIncrement64(Segment->ReferenceCount);
3426 
3427     MArea->SectionData.Segment = Segment;
3428     MArea->SectionData.ViewOffset = ViewOffset;
3429     if (AsImage)
3430     {
3431         MArea->VadNode.u.VadFlags.VadType = VadImageMap;
3432     }
3433 
3434     MmInitializeRegion(&MArea->SectionData.RegionListHead,
3435                        ViewSize, 0, Protect);
3436 
3437     return STATUS_SUCCESS;
3438 }
3439 
3440 
3441 static VOID
3442 MmFreeSectionPage(PVOID Context, MEMORY_AREA* MemoryArea, PVOID Address,
3443                   PFN_NUMBER Page, SWAPENTRY SwapEntry, BOOLEAN Dirty)
3444 {
3445     ULONG_PTR Entry;
3446     LARGE_INTEGER Offset;
3447     SWAPENTRY SavedSwapEntry;
3448     PMM_SECTION_SEGMENT Segment;
3449     PMMSUPPORT AddressSpace;
3450     PEPROCESS Process;
3451 
3452     AddressSpace = (PMMSUPPORT)Context;
3453     Process = MmGetAddressSpaceOwner(AddressSpace);
3454 
3455     Address = (PVOID)PAGE_ROUND_DOWN(Address);
3456 
3457     Offset.QuadPart = ((ULONG_PTR)Address - MA_GetStartingAddress(MemoryArea)) +
3458                       MemoryArea->SectionData.ViewOffset;
3459 
3460     Segment = MemoryArea->SectionData.Segment;
3461 
3462     Entry = MmGetPageEntrySectionSegment(Segment, &Offset);
3463     while (Entry && MM_IS_WAIT_PTE(Entry))
3464     {
3465         MmUnlockSectionSegment(Segment);
3466         MmUnlockAddressSpace(AddressSpace);
3467 
3468         KeDelayExecutionThread(KernelMode, FALSE, &TinyTime);
3469 
3470         MmLockAddressSpace(AddressSpace);
3471         MmLockSectionSegment(Segment);
3472         Entry = MmGetPageEntrySectionSegment(Segment, &Offset);
3473     }
3474 
3475     /*
3476      * For a dirty, datafile, non-private page, there shoulkd be no swap entry
3477      */
3478     if (*Segment->Flags & MM_DATAFILE_SEGMENT)
3479     {
3480         if (Page == PFN_FROM_SSE(Entry) && Dirty)
3481         {
3482             ASSERT(SwapEntry == 0);
3483         }
3484     }
3485 
3486     if (SwapEntry != 0)
3487     {
3488         /*
3489          * Sanity check
3490          */
3491         MmFreeSwapPage(SwapEntry);
3492     }
3493     else if (Page != 0)
3494     {
3495         if (IS_SWAP_FROM_SSE(Entry) ||
3496                 Page != PFN_FROM_SSE(Entry))
3497         {
3498             ASSERT(Process != NULL);
3499 
3500             /*
3501              * Just dereference private pages
3502              */
3503             SavedSwapEntry = MmGetSavedSwapEntryPage(Page);
3504             if (SavedSwapEntry != 0)
3505             {
3506                 MmFreeSwapPage(SavedSwapEntry);
3507                 MmSetSavedSwapEntryPage(Page, 0);
3508             }
3509             MmDeleteRmap(Page, Process, Address);
3510             MmReleasePageMemoryConsumer(MC_USER, Page);
3511         }
3512         else
3513         {
3514             if (Process)
3515             {
3516                 MmDeleteRmap(Page, Process, Address);
3517             }
3518 
3519             /* We don't dirtify for System Space Maps. We let Cc manage that */
3520             MmUnsharePageEntrySectionSegment(MemoryArea, Segment, &Offset, Process ? Dirty : FALSE, FALSE, NULL);
3521         }
3522     }
3523 }
3524 
3525 static NTSTATUS
3526 MmUnmapViewOfSegment(PMMSUPPORT AddressSpace,
3527                      PVOID BaseAddress)
3528 {
3529     NTSTATUS Status;
3530     PMEMORY_AREA MemoryArea;
3531     PMM_SECTION_SEGMENT Segment;
3532     PLIST_ENTRY CurrentEntry;
3533     PMM_REGION CurrentRegion;
3534     PLIST_ENTRY RegionListHead;
3535 
3536     MemoryArea = MmLocateMemoryAreaByAddress(AddressSpace,
3537                  BaseAddress);
3538     if (MemoryArea == NULL)
3539     {
3540         return STATUS_UNSUCCESSFUL;
3541     }
3542 
3543     Segment = MemoryArea->SectionData.Segment;
3544 
3545 #ifdef NEWCC
3546     if (Segment->Flags & MM_DATAFILE_SEGMENT)
3547     {
3548         MmUnlockAddressSpace(AddressSpace);
3549         Status = MmUnmapViewOfCacheSegment(AddressSpace, BaseAddress);
3550         MmLockAddressSpace(AddressSpace);
3551 
3552         return Status;
3553     }
3554 #endif
3555 
3556     MemoryArea->DeleteInProgress = TRUE;
3557 
3558     MmLockSectionSegment(Segment);
3559 
3560     RegionListHead = &MemoryArea->SectionData.RegionListHead;
3561     while (!IsListEmpty(RegionListHead))
3562     {
3563         CurrentEntry = RemoveHeadList(RegionListHead);
3564         CurrentRegion = CONTAINING_RECORD(CurrentEntry, MM_REGION, RegionListEntry);
3565         ExFreePoolWithTag(CurrentRegion, TAG_MM_REGION);
3566     }
3567 
3568     if ((*Segment->Flags) & MM_PHYSICALMEMORY_SEGMENT)
3569     {
3570         Status = MmFreeMemoryArea(AddressSpace,
3571                                   MemoryArea,
3572                                   NULL,
3573                                   NULL);
3574     }
3575     else
3576     {
3577         Status = MmFreeMemoryArea(AddressSpace,
3578                                   MemoryArea,
3579                                   MmFreeSectionPage,
3580                                   AddressSpace);
3581     }
3582     MmUnlockSectionSegment(Segment);
3583     MmDereferenceSegment(Segment);
3584     return Status;
3585 }
3586 
3587 /* This functions must be called with a locked address space */
3588 NTSTATUS
3589 NTAPI
3590 MiRosUnmapViewOfSection(IN PEPROCESS Process,
3591                         IN PVOID BaseAddress,
3592                         IN BOOLEAN SkipDebuggerNotify)
3593 {
3594     NTSTATUS Status;
3595     PMEMORY_AREA MemoryArea;
3596     PMMSUPPORT AddressSpace;
3597     PVOID ImageBaseAddress = 0;
3598 
3599     DPRINT("Opening memory area Process %p BaseAddress %p\n",
3600            Process, BaseAddress);
3601 
3602     ASSERT(Process);
3603 
3604     AddressSpace = &Process->Vm;
3605 
3606     MemoryArea = MmLocateMemoryAreaByAddress(AddressSpace,
3607                  BaseAddress);
3608     if (MemoryArea == NULL ||
3609 #ifdef NEWCC
3610             ((MemoryArea->Type != MEMORY_AREA_SECTION_VIEW) && (MemoryArea->Type != MEMORY_AREA_CACHE)) ||
3611 #else
3612             (MemoryArea->Type != MEMORY_AREA_SECTION_VIEW) ||
3613 #endif
3614             MemoryArea->DeleteInProgress)
3615 
3616     {
3617         if (MemoryArea) ASSERT(MemoryArea->Type != MEMORY_AREA_OWNED_BY_ARM3);
3618 
3619         DPRINT1("Unable to find memory area at address %p.\n", BaseAddress);
3620         return STATUS_NOT_MAPPED_VIEW;
3621     }
3622 
3623     if (MemoryArea->VadNode.u.VadFlags.VadType == VadImageMap)
3624     {
3625         ULONG i;
3626         ULONG NrSegments;
3627         PMM_IMAGE_SECTION_OBJECT ImageSectionObject;
3628         PMM_SECTION_SEGMENT SectionSegments;
3629         PMM_SECTION_SEGMENT Segment;
3630 
3631         Segment = MemoryArea->SectionData.Segment;
3632         ImageSectionObject = ImageSectionObjectFromSegment(Segment);
3633         SectionSegments = ImageSectionObject->Segments;
3634         NrSegments = ImageSectionObject->NrSegments;
3635 
3636         MemoryArea->DeleteInProgress = TRUE;
3637 
3638         /* Search for the current segment within the section segments
3639          * and calculate the image base address */
3640         for (i = 0; i < NrSegments; i++)
3641         {
3642             if (Segment == &SectionSegments[i])
3643             {
3644                 ImageBaseAddress = (char*)BaseAddress - (ULONG_PTR)SectionSegments[i].Image.VirtualAddress;
3645                 break;
3646             }
3647         }
3648         if (i >= NrSegments)
3649         {
3650             KeBugCheck(MEMORY_MANAGEMENT);
3651         }
3652 
3653         for (i = 0; i < NrSegments; i++)
3654         {
3655             PVOID SBaseAddress = (PVOID)
3656                                  ((char*)ImageBaseAddress + (ULONG_PTR)SectionSegments[i].Image.VirtualAddress);
3657 
3658             Status = MmUnmapViewOfSegment(AddressSpace, SBaseAddress);
3659             if (!NT_SUCCESS(Status))
3660             {
3661                 DPRINT1("MmUnmapViewOfSegment failed for %p (Process %p) with %lx\n",
3662                         SBaseAddress, Process, Status);
3663                 ASSERT(NT_SUCCESS(Status));
3664             }
3665         }
3666         DPRINT("One mapping less for %p\n", ImageSectionObject->FileObject->SectionObjectPointer);
3667         InterlockedDecrement(&ImageSectionObject->MapCount);
3668     }
3669     else
3670     {
3671         PMM_SECTION_SEGMENT Segment = MemoryArea->SectionData.Segment;
3672         PMMVAD Vad = &MemoryArea->VadNode;
3673         PCONTROL_AREA ControlArea  = Vad->ControlArea;
3674         PFILE_OBJECT FileObject;
3675         SIZE_T ViewSize;
3676         LARGE_INTEGER ViewOffset;
3677         ViewOffset.QuadPart = MemoryArea->SectionData.ViewOffset;
3678 
3679         InterlockedIncrement64(Segment->ReferenceCount);
3680 
3681         ViewSize = PAGE_SIZE + ((Vad->EndingVpn - Vad->StartingVpn) << PAGE_SHIFT);
3682 
3683         Status = MmUnmapViewOfSegment(AddressSpace, BaseAddress);
3684         if (!NT_SUCCESS(Status))
3685         {
3686             DPRINT1("MmUnmapViewOfSegment failed for %p (Process %p) with %lx\n",
3687                     BaseAddress, Process, Status);
3688             ASSERT(NT_SUCCESS(Status));
3689         }
3690 
3691         /* These might be deleted now */
3692         Vad = NULL;
3693         MemoryArea = NULL;
3694 
3695         if (FlagOn(*Segment->Flags, MM_PHYSICALMEMORY_SEGMENT))
3696         {
3697             /* Don't bother */
3698             MmDereferenceSegment(Segment);
3699             return STATUS_SUCCESS;
3700         }
3701         ASSERT(FlagOn(*Segment->Flags, MM_DATAFILE_SEGMENT));
3702 
3703         FileObject = Segment->FileObject;
3704         FsRtlAcquireFileExclusive(FileObject);
3705 
3706         /* Don't bother for auto-delete closed file. */
3707         if (FlagOn(FileObject->Flags, FO_DELETE_ON_CLOSE) && FlagOn(FileObject->Flags, FO_CLEANUP_COMPLETE))
3708         {
3709             FsRtlReleaseFile(FileObject);
3710             MmDereferenceSegment(Segment);
3711             return STATUS_SUCCESS;
3712         }
3713 
3714         /*
3715          * Flush only when last mapping is deleted.
3716          * FIXME: Why ControlArea == NULL? Or rather: is ControlArea ever not NULL here?
3717          */
3718         if (ControlArea == NULL || ControlArea->NumberOfMappedViews == 1)
3719         {
3720             while (ViewSize > 0)
3721             {
3722                 ULONG FlushSize = min(ViewSize, PAGE_ROUND_DOWN(MAXULONG));
3723                 MmFlushSegment(FileObject->SectionObjectPointer,
3724                                &ViewOffset,
3725                                FlushSize,
3726                                NULL);
3727                 ViewSize -= FlushSize;
3728                 ViewOffset.QuadPart += FlushSize;
3729             }
3730         }
3731 
3732         FsRtlReleaseFile(FileObject);
3733         MmDereferenceSegment(Segment);
3734     }
3735 
3736     /* Notify debugger */
3737     if (ImageBaseAddress && !SkipDebuggerNotify) DbgkUnMapViewOfSection(ImageBaseAddress);
3738 
3739     return STATUS_SUCCESS;
3740 }
3741 
3742 
3743 
3744 
3745 /**
3746  * Queries the information of a section object.
3747  *
3748  * @param SectionHandle
3749  *        Handle to the section object. It must be opened with SECTION_QUERY
3750  *        access.
3751  * @param SectionInformationClass
3752  *        Index to a certain information structure. Can be either
3753  *        SectionBasicInformation or SectionImageInformation. The latter
3754  *        is valid only for sections that were created with the SEC_IMAGE
3755  *        flag.
3756  * @param SectionInformation
3757  *        Caller supplies storage for resulting information.
3758  * @param Length
3759  *        Size of the supplied storage.
3760  * @param ResultLength
3761  *        Data written.
3762  *
3763  * @return Status.
3764  *
3765  * @implemented
3766  */
3767 NTSTATUS
3768 NTAPI
3769 NtQuerySection(
3770     _In_ HANDLE SectionHandle,
3771     _In_ SECTION_INFORMATION_CLASS SectionInformationClass,
3772     _Out_ PVOID SectionInformation,
3773     _In_ SIZE_T SectionInformationLength,
3774     _Out_opt_ PSIZE_T ResultLength)
3775 {
3776     PSECTION Section;
3777     KPROCESSOR_MODE PreviousMode;
3778     NTSTATUS Status;
3779     PAGED_CODE();
3780 
3781     PreviousMode = ExGetPreviousMode();
3782     if (PreviousMode != KernelMode)
3783     {
3784         _SEH2_TRY
3785         {
3786             ProbeForWrite(SectionInformation,
3787                           SectionInformationLength,
3788                           __alignof(ULONG));
3789             if (ResultLength != NULL)
3790             {
3791                 ProbeForWrite(ResultLength,
3792                               sizeof(*ResultLength),
3793                               __alignof(SIZE_T));
3794             }
3795         }
3796         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
3797         {
3798             _SEH2_YIELD(return _SEH2_GetExceptionCode());
3799         }
3800         _SEH2_END;
3801     }
3802 
3803     if (SectionInformationClass == SectionBasicInformation)
3804     {
3805         if (SectionInformationLength < sizeof(SECTION_BASIC_INFORMATION))
3806         {
3807             return STATUS_INFO_LENGTH_MISMATCH;
3808         }
3809     }
3810     else if (SectionInformationClass == SectionImageInformation)
3811     {
3812         if (SectionInformationLength < sizeof(SECTION_IMAGE_INFORMATION))
3813         {
3814             return STATUS_INFO_LENGTH_MISMATCH;
3815         }
3816     }
3817     else
3818     {
3819         return STATUS_INVALID_INFO_CLASS;
3820     }
3821 
3822     Status = ObReferenceObjectByHandle(SectionHandle,
3823                                        SECTION_QUERY,
3824                                        MmSectionObjectType,
3825                                        PreviousMode,
3826                                        (PVOID*)(PVOID)&Section,
3827                                        NULL);
3828     if (!NT_SUCCESS(Status))
3829     {
3830         DPRINT1("Failed to reference section: 0x%lx\n", Status);
3831         return Status;
3832     }
3833 
3834     switch(SectionInformationClass)
3835     {
3836         case SectionBasicInformation:
3837         {
3838             SECTION_BASIC_INFORMATION Sbi;
3839 
3840             Sbi.Size = Section->SizeOfSection;
3841             Sbi.BaseAddress = (PVOID)Section->Address.StartingVpn;
3842 
3843             Sbi.Attributes = 0;
3844             if (Section->u.Flags.File)
3845                 Sbi.Attributes |= SEC_FILE;
3846             if (Section->u.Flags.Image)
3847                 Sbi.Attributes |= SEC_IMAGE;
3848 
3849             /* Those are not set *************
3850             if (Section->u.Flags.Commit)
3851                 Sbi.Attributes |= SEC_COMMIT;
3852             if (Section->u.Flags.Reserve)
3853                 Sbi.Attributes |= SEC_RESERVE;
3854             **********************************/
3855 
3856             if (Section->u.Flags.Image)
3857             {
3858                 if (MiIsRosSectionObject(Section))
3859                 {
3860                     PMM_IMAGE_SECTION_OBJECT ImageSectionObject = ((PMM_IMAGE_SECTION_OBJECT)Section->Segment);
3861                     Sbi.BaseAddress = 0;
3862                     Sbi.Size.QuadPart = ImageSectionObject->ImageInformation.ImageFileSize;
3863                 }
3864                 else
3865                 {
3866                     /* Not supported yet */
3867                     ASSERT(FALSE);
3868                 }
3869             }
3870             else if (MiIsRosSectionObject(Section))
3871             {
3872                 Sbi.BaseAddress = (PVOID)((PMM_SECTION_SEGMENT)Section->Segment)->Image.VirtualAddress;
3873                 Sbi.Size.QuadPart = ((PMM_SECTION_SEGMENT)Section->Segment)->RawLength.QuadPart;
3874             }
3875             else
3876             {
3877                 DPRINT1("Unimplemented code path!");
3878             }
3879 
3880             _SEH2_TRY
3881             {
3882                 *((SECTION_BASIC_INFORMATION*)SectionInformation) = Sbi;
3883                 if (ResultLength != NULL)
3884                 {
3885                     *ResultLength = sizeof(Sbi);
3886                 }
3887             }
3888             _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
3889             {
3890                 Status = _SEH2_GetExceptionCode();
3891             }
3892             _SEH2_END;
3893             break;
3894         }
3895         case SectionImageInformation:
3896         {
3897             if (!Section->u.Flags.Image)
3898             {
3899                 Status = STATUS_SECTION_NOT_IMAGE;
3900             }
3901             else if (MiIsRosSectionObject(Section))
3902             {
3903                 PMM_IMAGE_SECTION_OBJECT ImageSectionObject = ((PMM_IMAGE_SECTION_OBJECT)Section->Segment);
3904 
3905                 _SEH2_TRY
3906                 {
3907                     PSECTION_IMAGE_INFORMATION Sii = (PSECTION_IMAGE_INFORMATION)SectionInformation;
3908                     *Sii = ImageSectionObject->ImageInformation;
3909                     if (ResultLength != NULL)
3910                     {
3911                         *ResultLength = sizeof(*Sii);
3912                     }
3913                 }
3914                 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
3915                 {
3916                     Status = _SEH2_GetExceptionCode();
3917                 }
3918                 _SEH2_END;
3919             }
3920             else
3921             {
3922                 _SEH2_TRY
3923                 {
3924                     PSECTION_IMAGE_INFORMATION Sii = (PSECTION_IMAGE_INFORMATION)SectionInformation;
3925                     *Sii = *Section->Segment->u2.ImageInformation;
3926                     if (ResultLength != NULL)
3927                         *ResultLength = sizeof(*Sii);
3928                 }
3929                 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
3930                 {
3931                     Status = _SEH2_GetExceptionCode();
3932                 }
3933                 _SEH2_END;
3934             }
3935             break;
3936         }
3937         default:
3938             DPRINT1("Unknown SectionInformationClass: %d\n", SectionInformationClass);
3939             Status = STATUS_NOT_SUPPORTED;
3940     }
3941 
3942     ObDereferenceObject(Section);
3943 
3944     return Status;
3945 }
3946 
3947 /**********************************************************************
3948  * NAME       EXPORTED
3949  * MmMapViewOfSection
3950  *
3951  * DESCRIPTION
3952  * Maps a view of a section into the virtual address space of a
3953  * process.
3954  *
3955  * ARGUMENTS
3956  * Section
3957  *  Pointer to the section object.
3958  *
3959  * ProcessHandle
3960  *  Pointer to the process.
3961  *
3962  * BaseAddress
3963  *  Desired base address (or NULL) on entry;
3964  *  Actual base address of the view on exit.
3965  *
3966  * ZeroBits
3967  *  Number of high order address bits that must be zero.
3968  *
3969  * CommitSize
3970  *  Size in bytes of the initially committed section of
3971  *  the view.
3972  *
3973  * SectionOffset
3974  *  Offset in bytes from the beginning of the section
3975  *  to the beginning of the view.
3976  *
3977  * ViewSize
3978  *  Desired length of map (or zero to map all) on entry
3979  *  Actual length mapped on exit.
3980  *
3981  * InheritDisposition
3982  *  Specified how the view is to be shared with
3983  *  child processes.
3984  *
3985  * AllocationType
3986  *  Type of allocation for the pages.
3987  *
3988  * Protect
3989  *  Protection for the committed region of the view.
3990  *
3991  * RETURN VALUE
3992  * Status.
3993  *
3994  * @implemented
3995  */
3996 NTSTATUS NTAPI
3997 MmMapViewOfSection(IN PVOID SectionObject,
3998                    IN PEPROCESS Process,
3999                    IN OUT PVOID *BaseAddress,
4000                    IN ULONG_PTR ZeroBits,
4001                    IN SIZE_T CommitSize,
4002                    IN OUT PLARGE_INTEGER SectionOffset OPTIONAL,
4003                    IN OUT PSIZE_T ViewSize,
4004                    IN SECTION_INHERIT InheritDisposition,
4005                    IN ULONG AllocationType,
4006                    IN ULONG Protect)
4007 {
4008     PSECTION Section;
4009     PMMSUPPORT AddressSpace;
4010     NTSTATUS Status = STATUS_SUCCESS;
4011     BOOLEAN NotAtBase = FALSE;
4012 
4013     if (MiIsRosSectionObject(SectionObject) == FALSE)
4014     {
4015         DPRINT("Mapping ARM3 section into %s\n", Process->ImageFileName);
4016         return MmMapViewOfArm3Section(SectionObject,
4017                                       Process,
4018                                       BaseAddress,
4019                                       ZeroBits,
4020                                       CommitSize,
4021                                       SectionOffset,
4022                                       ViewSize,
4023                                       InheritDisposition,
4024                                       AllocationType,
4025                                       Protect);
4026     }
4027 
4028     ASSERT(Process);
4029 
4030     if (!Protect || Protect & ~PAGE_FLAGS_VALID_FOR_SECTION)
4031     {
4032         return STATUS_INVALID_PAGE_PROTECTION;
4033     }
4034 
4035     /* FIXME: We should keep this, but it would break code checking equality */
4036     Protect &= ~PAGE_NOCACHE;
4037 
4038     Section = SectionObject;
4039     AddressSpace = &Process->Vm;
4040 
4041     if (Section->u.Flags.NoChange)
4042         AllocationType |= SEC_NO_CHANGE;
4043 
4044     MmLockAddressSpace(AddressSpace);
4045 
4046     if (Section->u.Flags.Image)
4047     {
4048         ULONG i;
4049         ULONG NrSegments;
4050         ULONG_PTR ImageBase;
4051         SIZE_T ImageSize;
4052         PMM_IMAGE_SECTION_OBJECT ImageSectionObject;
4053         PMM_SECTION_SEGMENT SectionSegments;
4054 
4055         ImageSectionObject = ((PMM_IMAGE_SECTION_OBJECT)Section->Segment);
4056         SectionSegments = ImageSectionObject->Segments;
4057         NrSegments = ImageSectionObject->NrSegments;
4058 
4059         ASSERT(ImageSectionObject->RefCount > 0);
4060 
4061         ImageBase = (ULONG_PTR)*BaseAddress;
4062         if (ImageBase == 0)
4063         {
4064             ImageBase = (ULONG_PTR)ImageSectionObject->BasedAddress;
4065         }
4066 
4067         ImageSize = 0;
4068         for (i = 0; i < NrSegments; i++)
4069         {
4070             ULONG_PTR MaxExtent;
4071             MaxExtent = (ULONG_PTR)(SectionSegments[i].Image.VirtualAddress +
4072                                     SectionSegments[i].Length.QuadPart);
4073             ImageSize = max(ImageSize, MaxExtent);
4074         }
4075 
4076         ImageSectionObject->ImageInformation.ImageFileSize = (ULONG)ImageSize;
4077 
4078         /* Check for an illegal base address */
4079         if (((ImageBase + ImageSize) > (ULONG_PTR)MM_HIGHEST_VAD_ADDRESS) ||
4080                 ((ImageBase + ImageSize) < ImageSize))
4081         {
4082             ASSERT(*BaseAddress == NULL);
4083             ImageBase = ALIGN_DOWN_BY((ULONG_PTR)MM_HIGHEST_VAD_ADDRESS - ImageSize,
4084                                       MM_VIRTMEM_GRANULARITY);
4085             NotAtBase = TRUE;
4086         }
4087         else if (ImageBase != ALIGN_DOWN_BY(ImageBase, MM_VIRTMEM_GRANULARITY))
4088         {
4089             ASSERT(*BaseAddress == NULL);
4090             ImageBase = ALIGN_DOWN_BY(ImageBase, MM_VIRTMEM_GRANULARITY);
4091             NotAtBase = TRUE;
4092         }
4093 
4094         /* Check there is enough space to map the section at that point. */
4095         if (MmLocateMemoryAreaByRegion(AddressSpace, (PVOID)ImageBase,
4096                                        PAGE_ROUND_UP(ImageSize)) != NULL)
4097         {
4098             /* Fail if the user requested a fixed base address. */
4099             if ((*BaseAddress) != NULL)
4100             {
4101                 MmUnlockAddressSpace(AddressSpace);
4102                 return STATUS_CONFLICTING_ADDRESSES;
4103             }
4104             /* Otherwise find a gap to map the image. */
4105             ImageBase = (ULONG_PTR)MmFindGap(AddressSpace, PAGE_ROUND_UP(ImageSize), MM_VIRTMEM_GRANULARITY, FALSE);
4106             if (ImageBase == 0)
4107             {
4108                 MmUnlockAddressSpace(AddressSpace);
4109                 return STATUS_CONFLICTING_ADDRESSES;
4110             }
4111             /* Remember that we loaded image at a different base address */
4112             NotAtBase = TRUE;
4113         }
4114 
4115         for (i = 0; i < NrSegments; i++)
4116         {
4117             PVOID SBaseAddress = (PVOID)
4118                                  ((char*)ImageBase + (ULONG_PTR)SectionSegments[i].Image.VirtualAddress);
4119             MmLockSectionSegment(&SectionSegments[i]);
4120             Status = MmMapViewOfSegment(AddressSpace,
4121                                         TRUE,
4122                                         &SectionSegments[i],
4123                                         &SBaseAddress,
4124                                         SectionSegments[i].Length.QuadPart,
4125                                         SectionSegments[i].Protection,
4126                                         0,
4127                                         0);
4128             MmUnlockSectionSegment(&SectionSegments[i]);
4129             if (!NT_SUCCESS(Status))
4130             {
4131                 /* roll-back */
4132                 while (i--)
4133                 {
4134                     SBaseAddress =  ((char*)ImageBase + (ULONG_PTR)SectionSegments[i].Image.VirtualAddress);
4135                     MmLockSectionSegment(&SectionSegments[i]);
4136                     MmUnmapViewOfSegment(AddressSpace, SBaseAddress);
4137                     MmUnlockSectionSegment(&SectionSegments[i]);
4138                 }
4139 
4140                 MmUnlockAddressSpace(AddressSpace);
4141                 return Status;
4142             }
4143         }
4144 
4145         *BaseAddress = (PVOID)ImageBase;
4146         *ViewSize = ImageSize;
4147 
4148         DPRINT("Mapped %p for section pointer %p\n", ImageSectionObject, ImageSectionObject->FileObject->SectionObjectPointer);
4149 
4150         /* One more map */
4151         InterlockedIncrement(&ImageSectionObject->MapCount);
4152     }
4153     else
4154     {
4155         PMM_SECTION_SEGMENT Segment = (PMM_SECTION_SEGMENT)Section->Segment;
4156         LONGLONG ViewOffset;
4157 
4158         ASSERT(Segment->RefCount > 0);
4159 
4160         /* check for write access */
4161         if ((Protect & (PAGE_READWRITE|PAGE_EXECUTE_READWRITE)) &&
4162                 !(Section->InitialPageProtection & (PAGE_READWRITE|PAGE_EXECUTE_READWRITE)))
4163         {
4164             MmUnlockAddressSpace(AddressSpace);
4165             return STATUS_SECTION_PROTECTION;
4166         }
4167         /* check for read access */
4168         if ((Protect & (PAGE_READONLY|PAGE_WRITECOPY|PAGE_EXECUTE_READ|PAGE_EXECUTE_WRITECOPY)) &&
4169                 !(Section->InitialPageProtection & (PAGE_READONLY|PAGE_READWRITE|PAGE_WRITECOPY|PAGE_EXECUTE_READ|PAGE_EXECUTE_READWRITE|PAGE_EXECUTE_WRITECOPY)))
4170         {
4171             MmUnlockAddressSpace(AddressSpace);
4172             return STATUS_SECTION_PROTECTION;
4173         }
4174         /* check for execute access */
4175         if ((Protect & (PAGE_EXECUTE|PAGE_EXECUTE_READ|PAGE_EXECUTE_READWRITE|PAGE_EXECUTE_WRITECOPY)) &&
4176                 !(Section->InitialPageProtection & (PAGE_EXECUTE|PAGE_EXECUTE_READ|PAGE_EXECUTE_READWRITE|PAGE_EXECUTE_WRITECOPY)))
4177         {
4178             MmUnlockAddressSpace(AddressSpace);
4179             return STATUS_SECTION_PROTECTION;
4180         }
4181 
4182         if (SectionOffset == NULL)
4183         {
4184             ViewOffset = 0;
4185         }
4186         else
4187         {
4188             ViewOffset = SectionOffset->QuadPart;
4189         }
4190 
4191         if ((ViewOffset % PAGE_SIZE) != 0)
4192         {
4193             MmUnlockAddressSpace(AddressSpace);
4194             return STATUS_MAPPED_ALIGNMENT;
4195         }
4196 
4197         if ((*ViewSize) == 0)
4198         {
4199             (*ViewSize) = Section->SizeOfSection.QuadPart - ViewOffset;
4200         }
4201         else if ((ExGetPreviousMode() == UserMode) &&
4202             (((*ViewSize)+ViewOffset) > Section->SizeOfSection.QuadPart) &&
4203             (!Section->u.Flags.Reserve))
4204         {
4205             /* Dubious */
4206             (*ViewSize) = MIN(Section->SizeOfSection.QuadPart - ViewOffset, SIZE_T_MAX - PAGE_SIZE);
4207         }
4208 
4209         *ViewSize = PAGE_ROUND_UP(*ViewSize);
4210 
4211         MmLockSectionSegment(Segment);
4212         Status = MmMapViewOfSegment(AddressSpace,
4213                                     FALSE,
4214                                     Segment,
4215                                     BaseAddress,
4216                                     *ViewSize,
4217                                     Protect,
4218                                     ViewOffset,
4219                                     AllocationType & (MEM_TOP_DOWN|SEC_NO_CHANGE));
4220         MmUnlockSectionSegment(Segment);
4221         if (!NT_SUCCESS(Status))
4222         {
4223             MmUnlockAddressSpace(AddressSpace);
4224             return Status;
4225         }
4226     }
4227 
4228     MmUnlockAddressSpace(AddressSpace);
4229 
4230     if (NotAtBase)
4231         Status = STATUS_IMAGE_NOT_AT_BASE;
4232     else
4233         Status = STATUS_SUCCESS;
4234 
4235     return Status;
4236 }
4237 
4238 /*
4239  * @unimplemented
4240  */
4241 BOOLEAN
4242 NTAPI
4243 MmCanFileBeTruncated(
4244     _In_ PSECTION_OBJECT_POINTERS SectionObjectPointer,
4245     _In_opt_ PLARGE_INTEGER NewFileSize)
4246 {
4247     BOOLEAN Ret;
4248     PMM_SECTION_SEGMENT Segment;
4249 
4250     /* Check whether an ImageSectionObject exists */
4251     if (SectionObjectPointer->ImageSectionObject != NULL)
4252     {
4253         DPRINT1("ERROR: File can't be truncated because it has an image section\n");
4254         return FALSE;
4255     }
4256 
4257     Segment = MiGrabDataSection(SectionObjectPointer);
4258     if (!Segment)
4259     {
4260         /* There is no data section. It's fine to do anything. */
4261         return TRUE;
4262     }
4263 
4264     MmLockSectionSegment(Segment);
4265     if ((Segment->SectionCount == 0) ||
4266         ((Segment->SectionCount == 1) && (SectionObjectPointer->SharedCacheMap != NULL)))
4267     {
4268         /* If the cache is the only one holding a reference to the segment, then it's fine to resize */
4269         Ret = TRUE;
4270     }
4271     else if (NewFileSize != NULL)
4272     {
4273         /* We can't shrink, but we can extend */
4274         Ret = NewFileSize->QuadPart >= Segment->RawLength.QuadPart;
4275 #if DBG
4276         if (!Ret)
4277         {
4278             DPRINT1("Cannot truncate data: New Size %I64d, Segment Size %I64d\n", NewFileSize->QuadPart, Segment->RawLength.QuadPart);
4279         }
4280 #endif
4281     }
4282     else
4283     {
4284         DPRINT1("ERROR: File can't be truncated because it has references held to its data section\n");
4285         Ret = FALSE;
4286     }
4287 
4288     MmUnlockSectionSegment(Segment);
4289     MmDereferenceSegment(Segment);
4290 
4291     DPRINT("FIXME: didn't check for outstanding write probes\n");
4292 
4293     return Ret;
4294 }
4295 
4296 static
4297 BOOLEAN
4298 MiPurgeImageSegment(PMM_SECTION_SEGMENT Segment)
4299 {
4300     PCACHE_SECTION_PAGE_TABLE PageTable;
4301 
4302     MmLockSectionSegment(Segment);
4303 
4304     /* Loop over all entries */
4305     for (PageTable = RtlEnumerateGenericTable(&Segment->PageTable, TRUE);
4306          PageTable != NULL;
4307          PageTable = RtlEnumerateGenericTable(&Segment->PageTable, FALSE))
4308     {
4309         for (ULONG i = 0; i < _countof(PageTable->PageEntries); i++)
4310         {
4311             ULONG_PTR Entry = PageTable->PageEntries[i];
4312             LARGE_INTEGER Offset;
4313 
4314             if (!Entry)
4315                 continue;
4316 
4317             if (IS_SWAP_FROM_SSE(Entry) || (SHARE_COUNT_FROM_SSE(Entry) > 0))
4318             {
4319                 /* I/O ongoing or swap entry. Someone mapped this file as we were not looking */
4320                 MmUnlockSectionSegment(Segment);
4321                 return FALSE;
4322             }
4323 
4324             /* Regular entry */
4325             ASSERT(!IS_WRITE_SSE(Entry));
4326             ASSERT(MmGetSavedSwapEntryPage(PFN_FROM_SSE(Entry)) == 0);
4327 
4328             /* Properly remove using the used API */
4329             Offset.QuadPart = PageTable->FileOffset.QuadPart + (i << PAGE_SHIFT);
4330             MmSetPageEntrySectionSegment(Segment, &Offset, 0);
4331             MmReleasePageMemoryConsumer(MC_USER, PFN_FROM_SSE(Entry));
4332         }
4333     }
4334 
4335     MmUnlockSectionSegment(Segment);
4336 
4337     return TRUE;
4338 }
4339 
4340 /*
4341  * @implemented
4342  */
4343 BOOLEAN NTAPI
4344 MmFlushImageSection (IN PSECTION_OBJECT_POINTERS SectionObjectPointer,
4345                      IN MMFLUSH_TYPE FlushType)
4346 {
4347     switch(FlushType)
4348     {
4349         case MmFlushForDelete:
4350         {
4351             /*
4352              * FIXME: Check for outstanding write probes on Data section.
4353              * How do we do that ?
4354              */
4355         }
4356         /* Fall-through */
4357         case MmFlushForWrite:
4358         {
4359             KIRQL OldIrql = MiAcquirePfnLock();
4360             PMM_IMAGE_SECTION_OBJECT ImageSectionObject = SectionObjectPointer->ImageSectionObject;
4361 
4362             DPRINT("Deleting or modifying %p\n", SectionObjectPointer);
4363 
4364             /* Wait for concurrent creation or deletion of image to be done */
4365             ImageSectionObject = SectionObjectPointer->ImageSectionObject;
4366             while (ImageSectionObject && (ImageSectionObject->SegFlags & (MM_SEGMENT_INCREATE | MM_SEGMENT_INDELETE)))
4367             {
4368                 MiReleasePfnLock(OldIrql);
4369                 KeDelayExecutionThread(KernelMode, FALSE, &TinyTime);
4370                 OldIrql = MiAcquirePfnLock();
4371                 ImageSectionObject = SectionObjectPointer->ImageSectionObject;
4372             }
4373 
4374             if (!ImageSectionObject)
4375             {
4376                 DPRINT("No image section object. Accepting\n");
4377                 /* Nothing to do */
4378                 MiReleasePfnLock(OldIrql);
4379                 return TRUE;
4380             }
4381 
4382             /* Do we have open sections or mappings on it ? */
4383             if ((ImageSectionObject->SectionCount) || (ImageSectionObject->MapCount))
4384             {
4385                 /* We do. No way to delete it */
4386                 MiReleasePfnLock(OldIrql);
4387                 DPRINT("Denying. There are mappings open\n");
4388                 return FALSE;
4389             }
4390 
4391             /* There are no sections open on it, but we must still have pages around. Discard everything */
4392             ImageSectionObject->SegFlags |= MM_IMAGE_SECTION_FLUSH_DELETE;
4393             InterlockedIncrement64(&ImageSectionObject->RefCount);
4394             MiReleasePfnLock(OldIrql);
4395 
4396             DPRINT("Purging\n");
4397 
4398             for (ULONG i = 0; i < ImageSectionObject->NrSegments; i++)
4399             {
4400                 if (!MiPurgeImageSegment(&ImageSectionObject->Segments[i]))
4401                     break;
4402             }
4403 
4404             /* Grab lock again */
4405             OldIrql = MiAcquirePfnLock();
4406 
4407             if (!(ImageSectionObject->SegFlags & MM_IMAGE_SECTION_FLUSH_DELETE))
4408             {
4409                 /*
4410                  * Someone actually created a section while we were not looking.
4411                  * Drop our ref and deny.
4412                  * MmDereferenceSegmentWithLock releases Pfn lock
4413                  */
4414                 MmDereferenceSegmentWithLock(&ImageSectionObject->Segments[0], OldIrql);
4415                 return FALSE;
4416             }
4417 
4418             /* We should be the last one holding a ref here. */
4419             ASSERT(ImageSectionObject->RefCount == 1);
4420             ASSERT(ImageSectionObject->SectionCount == 0);
4421 
4422             /* Dereference the first segment, this will free everything & release the lock */
4423             MmDereferenceSegmentWithLock(&ImageSectionObject->Segments[0], OldIrql);
4424             return TRUE;
4425         }
4426     }
4427     return FALSE;
4428 }
4429 
4430 /*
4431  * @implemented
4432  */
4433 NTSTATUS
4434 NTAPI
4435 MmMapViewInSystemSpace (IN PVOID SectionObject,
4436                         OUT PVOID * MappedBase,
4437                         IN OUT PSIZE_T ViewSize)
4438 {
4439     LARGE_INTEGER SectionOffset;
4440 
4441     SectionOffset.QuadPart = 0;
4442 
4443     return MmMapViewInSystemSpaceEx(SectionObject, MappedBase, ViewSize, &SectionOffset, 0);
4444 }
4445 
4446 NTSTATUS
4447 NTAPI
4448 MmMapViewInSystemSpaceEx (
4449     _In_ PVOID SectionObject,
4450     _Outptr_result_bytebuffer_ (*ViewSize) PVOID *MappedBase,
4451     _Inout_ PSIZE_T ViewSize,
4452     _Inout_ PLARGE_INTEGER SectionOffset,
4453     _In_ ULONG_PTR Flags
4454     )
4455 {
4456     PSECTION Section = SectionObject;
4457     PMM_SECTION_SEGMENT Segment;
4458     PMMSUPPORT AddressSpace;
4459     NTSTATUS Status;
4460 
4461     UNREFERENCED_PARAMETER(Flags);
4462 
4463     PAGED_CODE();
4464 
4465     if (MiIsRosSectionObject(SectionObject) == FALSE)
4466     {
4467         return MiMapViewInSystemSpace(SectionObject,
4468                                       &MmSession,
4469                                       MappedBase,
4470                                       ViewSize,
4471                                       SectionOffset);
4472     }
4473 
4474     DPRINT("MmMapViewInSystemSpaceEx() called\n");
4475 
4476     /* unsupported for now */
4477     ASSERT(Section->u.Flags.Image == 0);
4478 
4479     Section = SectionObject;
4480     Segment = (PMM_SECTION_SEGMENT)Section->Segment;
4481 
4482     if (*ViewSize == 0)
4483     {
4484         LONGLONG MapSizeLL;
4485 
4486         /* Page-align the mapping */
4487         SectionOffset->LowPart = PAGE_ROUND_DOWN(SectionOffset->LowPart);
4488 
4489         if (!NT_SUCCESS(RtlLongLongSub(Section->SizeOfSection.QuadPart, SectionOffset->QuadPart, &MapSizeLL)))
4490             return STATUS_INVALID_VIEW_SIZE;
4491 
4492         if (!NT_SUCCESS(RtlLongLongToSIZET(MapSizeLL, ViewSize)))
4493             return STATUS_INVALID_VIEW_SIZE;
4494     }
4495     else
4496     {
4497         LONGLONG HelperLL;
4498 
4499         /* Get the map end */
4500         if (!NT_SUCCESS(RtlLongLongAdd(SectionOffset->QuadPart, *ViewSize, &HelperLL)))
4501             return STATUS_INVALID_VIEW_SIZE;
4502 
4503         /* Round it up, if needed */
4504         if (HelperLL % PAGE_SIZE)
4505         {
4506             if (!NT_SUCCESS(RtlLongLongAdd(HelperLL, PAGE_SIZE - (HelperLL % PAGE_SIZE), &HelperLL)))
4507                 return STATUS_INVALID_VIEW_SIZE;
4508         }
4509 
4510         /* Now that we have the mapping end, we can align down its start */
4511         SectionOffset->LowPart = PAGE_ROUND_DOWN(SectionOffset->LowPart);
4512 
4513         /* Get the new size */
4514         if (!NT_SUCCESS(RtlLongLongSub(HelperLL, SectionOffset->QuadPart, &HelperLL)))
4515             return STATUS_INVALID_VIEW_SIZE;
4516 
4517         if (!NT_SUCCESS(RtlLongLongToSIZET(HelperLL, ViewSize)))
4518             return STATUS_INVALID_VIEW_SIZE;
4519     }
4520 
4521     AddressSpace = MmGetKernelAddressSpace();
4522 
4523     MmLockAddressSpace(AddressSpace);
4524 
4525     MmLockSectionSegment(Segment);
4526 
4527     Status = MmMapViewOfSegment(AddressSpace,
4528                                 Section->u.Flags.Image,
4529                                 Segment,
4530                                 MappedBase,
4531                                 *ViewSize,
4532                                 PAGE_READWRITE,
4533                                 SectionOffset->QuadPart,
4534                                 SEC_RESERVE);
4535 
4536     MmUnlockSectionSegment(Segment);
4537     MmUnlockAddressSpace(AddressSpace);
4538 
4539     return Status;
4540 }
4541 
4542 /* This function must be called with adress space lock held */
4543 NTSTATUS
4544 NTAPI
4545 MiRosUnmapViewInSystemSpace(IN PVOID MappedBase)
4546 {
4547     DPRINT("MmUnmapViewInSystemSpace() called\n");
4548 
4549     return MmUnmapViewOfSegment(MmGetKernelAddressSpace(), MappedBase);
4550 }
4551 
4552 /**********************************************************************
4553  * NAME       EXPORTED
4554  *  MmCreateSection@
4555  *
4556  * DESCRIPTION
4557  *  Creates a section object.
4558  *
4559  * ARGUMENTS
4560  * SectionObject (OUT)
4561  *  Caller supplied storage for the resulting pointer
4562  *  to a SECTION_OBJECT instance;
4563  *
4564  * DesiredAccess
4565  *  Specifies the desired access to the section can be a
4566  *  combination of:
4567  *   STANDARD_RIGHTS_REQUIRED |
4568  *   SECTION_QUERY   |
4569  *   SECTION_MAP_WRITE  |
4570  *   SECTION_MAP_READ  |
4571  *   SECTION_MAP_EXECUTE
4572  *
4573  * ObjectAttributes [OPTIONAL]
4574  *  Initialized attributes for the object can be used
4575  *  to create a named section;
4576  *
4577  * MaximumSize
4578  *  Maximizes the size of the memory section. Must be
4579  *  non-NULL for a page-file backed section.
4580  *  If value specified for a mapped file and the file is
4581  *  not large enough, file will be extended.
4582  *
4583  * SectionPageProtection
4584  *  Can be a combination of:
4585  *   PAGE_READONLY |
4586  *   PAGE_READWRITE |
4587  *   PAGE_WRITEONLY |
4588  *   PAGE_WRITECOPY
4589  *
4590  * AllocationAttributes
4591  *  Can be a combination of:
4592  *   SEC_IMAGE |
4593  *   SEC_RESERVE
4594  *
4595  * FileHandle
4596  *  Handle to a file to create a section mapped to a file
4597  *  instead of a memory backed section;
4598  *
4599  * File
4600  *  Unknown.
4601  *
4602  * RETURN VALUE
4603  *  Status.
4604  *
4605  * @implemented
4606  */
4607 NTSTATUS NTAPI
4608 MmCreateSection (OUT PVOID  * Section,
4609                  IN ACCESS_MASK  DesiredAccess,
4610                  IN POBJECT_ATTRIBUTES ObjectAttributes     OPTIONAL,
4611                  IN PLARGE_INTEGER  MaximumSize,
4612                  IN ULONG   SectionPageProtection,
4613                  IN ULONG   AllocationAttributes,
4614                  IN HANDLE   FileHandle   OPTIONAL,
4615                  IN PFILE_OBJECT  FileObject  OPTIONAL)
4616 {
4617     NTSTATUS Status;
4618     ULONG Protection;
4619     PSECTION *SectionObject = (PSECTION *)Section;
4620     BOOLEAN FileLock = FALSE;
4621 
4622     /* Check if an ARM3 section is being created instead */
4623     if (!(AllocationAttributes & (SEC_IMAGE | SEC_PHYSICALMEMORY)))
4624     {
4625         if (!(FileObject) && !(FileHandle))
4626         {
4627             return MmCreateArm3Section(Section,
4628                                        DesiredAccess,
4629                                        ObjectAttributes,
4630                                        MaximumSize,
4631                                        SectionPageProtection,
4632                                        AllocationAttributes &~ 1,
4633                                        FileHandle,
4634                                        FileObject);
4635         }
4636     }
4637 
4638     /* Convert section flag to page flag */
4639     if (AllocationAttributes & SEC_NOCACHE) SectionPageProtection |= PAGE_NOCACHE;
4640 
4641     /* Check to make sure the protection is correct. Nt* does this already */
4642     Protection = MiMakeProtectionMask(SectionPageProtection);
4643     if (Protection == MM_INVALID_PROTECTION)
4644     {
4645         DPRINT1("Page protection is invalid\n");
4646         return STATUS_INVALID_PAGE_PROTECTION;
4647     }
4648 
4649     /* Check if this is going to be a data or image backed file section */
4650     if ((FileHandle) || (FileObject))
4651     {
4652         /* These cannot be mapped with large pages */
4653         if (AllocationAttributes & SEC_LARGE_PAGES)
4654         {
4655             DPRINT1("Large pages cannot be used with an image mapping\n");
4656             return STATUS_INVALID_PARAMETER_6;
4657         }
4658 
4659         /* Did the caller pass a file object ? */
4660         if (FileObject)
4661         {
4662             /* Reference the object directly */
4663             ObReferenceObject(FileObject);
4664 
4665             /* We don't create image mappings with file objects */
4666             AllocationAttributes &= ~SEC_IMAGE;
4667         }
4668         else
4669         {
4670             /* Reference the file handle to get the object */
4671             Status = ObReferenceObjectByHandle(FileHandle,
4672                                                MmMakeFileAccess[Protection],
4673                                                IoFileObjectType,
4674                                                ExGetPreviousMode(),
4675                                                (PVOID*)&FileObject,
4676                                                NULL);
4677             if (!NT_SUCCESS(Status))
4678             {
4679                 DPRINT1("Failed to get a handle to the FO: %lx\n", Status);
4680                 return Status;
4681             }
4682 
4683             /* Lock the file */
4684             Status = FsRtlAcquireToCreateMappedSection(FileObject, SectionPageProtection);
4685             if (!NT_SUCCESS(Status))
4686             {
4687                 ObDereferenceObject(FileObject);
4688                 return Status;
4689             }
4690 
4691             FileLock = TRUE;
4692 
4693             /* Deny access if there are writes on the file */
4694 #if 0
4695             if ((AllocationAttributes & SEC_IMAGE) && (Status == STATUS_FILE_LOCKED_WITH_WRITERS))
4696             {
4697                 DPRINT1("Cannot create image maps with writers open on the file!\n");
4698                 Status = STATUS_ACCESS_DENIED;
4699                 goto Quit;
4700             }
4701 #else
4702             if ((AllocationAttributes & SEC_IMAGE) && (Status == STATUS_FILE_LOCKED_WITH_WRITERS))
4703                 DPRINT1("Creating image map with writers open on the file!\n");
4704 #endif
4705         }
4706     }
4707     else
4708     {
4709         /* A handle must be supplied with SEC_IMAGE, as this is the no-handle path */
4710         if (AllocationAttributes & SEC_IMAGE) return STATUS_INVALID_FILE_FOR_SECTION;
4711     }
4712 
4713     if (AllocationAttributes & SEC_IMAGE)
4714     {
4715         Status = MmCreateImageSection(SectionObject,
4716                                       DesiredAccess,
4717                                       ObjectAttributes,
4718                                       MaximumSize,
4719                                       SectionPageProtection,
4720                                       AllocationAttributes,
4721                                       FileObject);
4722     }
4723 #ifndef NEWCC
4724     else if (FileObject != NULL)
4725     {
4726         Status =  MmCreateDataFileSection(SectionObject,
4727                                           DesiredAccess,
4728                                           ObjectAttributes,
4729                                           MaximumSize,
4730                                           SectionPageProtection,
4731                                           AllocationAttributes,
4732                                           FileObject,
4733                                           FileHandle != NULL);
4734     }
4735 #else
4736     else if (FileHandle != NULL || FileObject != NULL)
4737     {
4738         Status = MmCreateCacheSection(SectionObject,
4739                                       DesiredAccess,
4740                                       ObjectAttributes,
4741                                       MaximumSize,
4742                                       SectionPageProtection,
4743                                       AllocationAttributes,
4744                                       FileObject);
4745     }
4746 #endif
4747     else
4748     {
4749         /* All cases should be handled above */
4750         Status = STATUS_INVALID_PARAMETER;
4751     }
4752 
4753     if (FileLock)
4754         FsRtlReleaseFile(FileObject);
4755     if (FileObject)
4756         ObDereferenceObject(FileObject);
4757 
4758     return Status;
4759 }
4760 
4761 BOOLEAN
4762 NTAPI
4763 MmArePagesResident(
4764     _In_ PEPROCESS Process,
4765     _In_ PVOID Address,
4766     _In_ ULONG Length)
4767 {
4768     PMEMORY_AREA MemoryArea;
4769     BOOLEAN Ret = TRUE;
4770     PMM_SECTION_SEGMENT Segment;
4771     LARGE_INTEGER SegmentOffset, RangeEnd;
4772     PMMSUPPORT AddressSpace = Process ? &Process->Vm : MmGetKernelAddressSpace();
4773 
4774     MmLockAddressSpace(AddressSpace);
4775 
4776     MemoryArea = MmLocateMemoryAreaByAddress(AddressSpace, Address);
4777     if (MemoryArea == NULL)
4778     {
4779         MmUnlockAddressSpace(AddressSpace);
4780         return FALSE;
4781     }
4782 
4783     /* Only supported in old Mm for now */
4784     ASSERT(MemoryArea->Type == MEMORY_AREA_SECTION_VIEW);
4785     /* For file mappings */
4786     ASSERT(MemoryArea->VadNode.u.VadFlags.VadType != VadImageMap);
4787 
4788     Segment = MemoryArea->SectionData.Segment;
4789     MmLockSectionSegment(Segment);
4790 
4791     SegmentOffset.QuadPart = PAGE_ROUND_DOWN(Address) - MA_GetStartingAddress(MemoryArea)
4792             + MemoryArea->SectionData.ViewOffset;
4793     RangeEnd.QuadPart = PAGE_ROUND_UP((ULONG_PTR)Address + Length) - MA_GetStartingAddress(MemoryArea)
4794             + MemoryArea->SectionData.ViewOffset;
4795 
4796     while (SegmentOffset.QuadPart < RangeEnd.QuadPart)
4797     {
4798         ULONG_PTR Entry = MmGetPageEntrySectionSegment(Segment, &SegmentOffset);
4799         if ((Entry == 0) || IS_SWAP_FROM_SSE(Entry))
4800         {
4801             Ret = FALSE;
4802             break;
4803         }
4804         SegmentOffset.QuadPart += PAGE_SIZE;
4805     }
4806 
4807     MmUnlockSectionSegment(Segment);
4808 
4809     MmUnlockAddressSpace(AddressSpace);
4810     return Ret;
4811 }
4812 
4813 /* Like CcPurgeCache but for the in-memory segment */
4814 BOOLEAN
4815 NTAPI
4816 MmPurgeSegment(
4817     _In_ PSECTION_OBJECT_POINTERS SectionObjectPointer,
4818     _In_opt_ PLARGE_INTEGER Offset,
4819     _In_ ULONG Length)
4820 {
4821     LARGE_INTEGER PurgeStart, PurgeEnd;
4822     PMM_SECTION_SEGMENT Segment;
4823 
4824     Segment = MiGrabDataSection(SectionObjectPointer);
4825     if (!Segment)
4826     {
4827         /* Nothing to purge */
4828         return TRUE;
4829     }
4830 
4831     PurgeStart.QuadPart = Offset ? Offset->QuadPart : 0LL;
4832     if (Length && Offset)
4833     {
4834         if (!NT_SUCCESS(RtlLongLongAdd(PurgeStart.QuadPart, Length, &PurgeEnd.QuadPart)))
4835             return FALSE;
4836     }
4837 
4838     MmLockSectionSegment(Segment);
4839 
4840     if (!Length || !Offset)
4841     {
4842         /* We must calculate the length for ourselves */
4843         /* FIXME: All of this is suboptimal */
4844         ULONG ElemCount = RtlNumberGenericTableElements(&Segment->PageTable);
4845         /* No page. Nothing to purge */
4846         if (!ElemCount)
4847         {
4848             MmUnlockSectionSegment(Segment);
4849             MmDereferenceSegment(Segment);
4850             return TRUE;
4851         }
4852 
4853         PCACHE_SECTION_PAGE_TABLE PageTable = RtlGetElementGenericTable(&Segment->PageTable, ElemCount - 1);
4854         PurgeEnd.QuadPart = PageTable->FileOffset.QuadPart + _countof(PageTable->PageEntries) * PAGE_SIZE;
4855     }
4856 
4857     while (PurgeStart.QuadPart < PurgeEnd.QuadPart)
4858     {
4859         ULONG_PTR Entry = MmGetPageEntrySectionSegment(Segment, &PurgeStart);
4860 
4861         if (Entry == 0)
4862         {
4863             PurgeStart.QuadPart += PAGE_SIZE;
4864             continue;
4865         }
4866 
4867         if (IS_SWAP_FROM_SSE(Entry))
4868         {
4869             ASSERT(SWAPENTRY_FROM_SSE(Entry) == MM_WAIT_ENTRY);
4870             /* The page is currently being read. Meaning someone will need it soon. Bad luck */
4871             MmUnlockSectionSegment(Segment);
4872             MmDereferenceSegment(Segment);
4873             return FALSE;
4874         }
4875 
4876         if (IS_WRITE_SSE(Entry))
4877         {
4878             /* We're trying to purge an entry which is being written. Restart this loop iteration */
4879             MmUnlockSectionSegment(Segment);
4880             KeDelayExecutionThread(KernelMode, FALSE, &TinyTime);
4881             MmLockSectionSegment(Segment);
4882             continue;
4883         }
4884 
4885         if (SHARE_COUNT_FROM_SSE(Entry) > 0)
4886         {
4887             /* This page is currently in use. Bad luck */
4888             MmUnlockSectionSegment(Segment);
4889             MmDereferenceSegment(Segment);
4890             return FALSE;
4891         }
4892 
4893         /* We can let this page go */
4894         MmSetPageEntrySectionSegment(Segment, &PurgeStart, 0);
4895         MmReleasePageMemoryConsumer(MC_USER, PFN_FROM_SSE(Entry));
4896 
4897         PurgeStart.QuadPart += PAGE_SIZE;
4898     }
4899 
4900     /* This page is currently in use. Bad luck */
4901     MmUnlockSectionSegment(Segment);
4902     MmDereferenceSegment(Segment);
4903     return TRUE;
4904 }
4905 
4906 NTSTATUS
4907 NTAPI
4908 MmMakeDataSectionResident(
4909     _In_ PSECTION_OBJECT_POINTERS SectionObjectPointer,
4910     _In_ LONGLONG Offset,
4911     _In_ ULONG Length,
4912     _In_ PLARGE_INTEGER ValidDataLength)
4913 {
4914     PMM_SECTION_SEGMENT Segment = MiGrabDataSection(SectionObjectPointer);
4915 
4916     /* There must be a segment for this call */
4917     ASSERT(Segment);
4918 
4919     NTSTATUS Status = MmMakeSegmentResident(Segment, Offset, Length, ValidDataLength, FALSE);
4920 
4921     MmDereferenceSegment(Segment);
4922 
4923     return Status;
4924 }
4925 
4926 NTSTATUS
4927 NTAPI
4928 MmFlushSegment(
4929     _In_ PSECTION_OBJECT_POINTERS SectionObjectPointer,
4930     _In_opt_ PLARGE_INTEGER Offset,
4931     _In_ ULONG Length,
4932     _Out_opt_ PIO_STATUS_BLOCK Iosb)
4933 {
4934     LARGE_INTEGER FlushStart, FlushEnd;
4935     NTSTATUS Status;
4936 
4937     if (Offset)
4938     {
4939         FlushStart = *Offset;
4940         Status = RtlLongLongAdd(FlushStart.QuadPart, Length, &FlushEnd.QuadPart);
4941         if (!NT_SUCCESS(Status))
4942             return Status;
4943     }
4944 
4945     if (Iosb)
4946         Iosb->Information = 0;
4947 
4948     PMM_SECTION_SEGMENT Segment = MiGrabDataSection(SectionObjectPointer);
4949     if (!Segment)
4950     {
4951         /* Nothing to flush */
4952         if (Iosb)
4953             Iosb->Status = STATUS_SUCCESS;
4954         return STATUS_SUCCESS;
4955     }
4956 
4957     ASSERT(*Segment->Flags & MM_DATAFILE_SEGMENT);
4958 
4959     MmLockSectionSegment(Segment);
4960 
4961     if (!Offset)
4962     {
4963         FlushStart.QuadPart = 0;
4964 
4965         /* FIXME: All of this is suboptimal */
4966         ULONG ElemCount = RtlNumberGenericTableElements(&Segment->PageTable);
4967         /* No page. Nothing to flush */
4968         if (!ElemCount)
4969         {
4970             MmUnlockSectionSegment(Segment);
4971             MmDereferenceSegment(Segment);
4972             if (Iosb)
4973             {
4974                 Iosb->Status = STATUS_SUCCESS;
4975                 Iosb->Information = 0;
4976             }
4977             return STATUS_SUCCESS;
4978         }
4979 
4980         PCACHE_SECTION_PAGE_TABLE PageTable = RtlGetElementGenericTable(&Segment->PageTable, ElemCount - 1);
4981         FlushEnd.QuadPart = PageTable->FileOffset.QuadPart + _countof(PageTable->PageEntries) * PAGE_SIZE;
4982     }
4983 
4984     FlushStart.QuadPart >>= PAGE_SHIFT;
4985     FlushStart.QuadPart <<= PAGE_SHIFT;
4986 
4987     while (FlushStart.QuadPart < FlushEnd.QuadPart)
4988     {
4989         ULONG_PTR Entry = MmGetPageEntrySectionSegment(Segment, &FlushStart);
4990 
4991         if (IS_DIRTY_SSE(Entry))
4992         {
4993             MmCheckDirtySegment(Segment, &FlushStart, FALSE, FALSE);
4994 
4995             if (Iosb)
4996                 Iosb->Information += PAGE_SIZE;
4997         }
4998 
4999         FlushStart.QuadPart += PAGE_SIZE;
5000     }
5001 
5002     MmUnlockSectionSegment(Segment);
5003     MmDereferenceSegment(Segment);
5004 
5005     if (Iosb)
5006         Iosb->Status = STATUS_SUCCESS;
5007 
5008     return STATUS_SUCCESS;
5009 }
5010 
5011 _Requires_exclusive_lock_held_(Segment->Lock)
5012 BOOLEAN
5013 NTAPI
5014 MmCheckDirtySegment(
5015     PMM_SECTION_SEGMENT Segment,
5016     PLARGE_INTEGER Offset,
5017     BOOLEAN ForceDirty,
5018     BOOLEAN PageOut)
5019 {
5020     ULONG_PTR Entry;
5021     NTSTATUS Status;
5022     PFN_NUMBER Page;
5023 
5024     ASSERT(Segment->Locked);
5025 
5026     ASSERT((Offset->QuadPart % PAGE_SIZE) == 0);
5027 
5028     DPRINT("Checking segment for file %wZ at offset 0x%I64X.\n", &Segment->FileObject->FileName, Offset->QuadPart);
5029 
5030     Entry = MmGetPageEntrySectionSegment(Segment, Offset);
5031     if (Entry == 0)
5032         return FALSE;
5033 
5034     Page = PFN_FROM_SSE(Entry);
5035     if ((IS_DIRTY_SSE(Entry)) || ForceDirty)
5036     {
5037         BOOLEAN DirtyAgain;
5038 
5039         /*
5040          * We got a dirty entry. This path is for the shared data,
5041          * be-it regular file maps or shared sections of DLLs
5042          */
5043         ASSERT(FlagOn(*Segment->Flags, MM_DATAFILE_SEGMENT) ||
5044                FlagOn(Segment->Image.Characteristics, IMAGE_SCN_MEM_SHARED));
5045 
5046         /* Insert the cleaned entry back. Mark it as write in progress, and clear the dirty bit. */
5047         Entry = MAKE_SSE(PAGE_FROM_SSE(Entry), SHARE_COUNT_FROM_SSE(Entry) + 1);
5048         Entry = WRITE_SSE(Entry);
5049         MmSetPageEntrySectionSegment(Segment, Offset, Entry);
5050 
5051         MmUnlockSectionSegment(Segment);
5052 
5053         if (FlagOn(*Segment->Flags, MM_DATAFILE_SEGMENT))
5054         {
5055             PERESOURCE ResourceToRelease = NULL;
5056             KIRQL OldIrql;
5057 
5058             /* We have to write it back to the file. Tell the FS driver who we are */
5059             if (PageOut)
5060             {
5061                 LARGE_INTEGER EndOffset = *Offset;
5062 
5063                 ASSERT(IoGetTopLevelIrp() == NULL);
5064 
5065                 /* We need to disable all APCs */
5066                 KeRaiseIrql(APC_LEVEL, &OldIrql);
5067 
5068                 EndOffset.QuadPart += PAGE_SIZE;
5069                 Status = FsRtlAcquireFileForModWriteEx(Segment->FileObject,
5070                                                        &EndOffset,
5071                                                        &ResourceToRelease);
5072                 if (NT_SUCCESS(Status))
5073                 {
5074                     IoSetTopLevelIrp((PIRP)FSRTL_MOD_WRITE_TOP_LEVEL_IRP);
5075                 }
5076                 else
5077                 {
5078                     /* Make sure we will not try to release anything */
5079                     ResourceToRelease = NULL;
5080                 }
5081             }
5082             else
5083             {
5084                 /* We don't have to lock. Say this is success */
5085                 Status = STATUS_SUCCESS;
5086             }
5087 
5088             /* Go ahead and write the page, if previous locking succeeded */
5089             if (NT_SUCCESS(Status))
5090             {
5091                 DPRINT("Writing page at offset %I64d for file %wZ, Pageout: %s\n",
5092                         Offset->QuadPart, &Segment->FileObject->FileName, PageOut ? "TRUE" : "FALSE");
5093                 Status = MiWritePage(Segment, Offset->QuadPart, Page);
5094             }
5095 
5096             if (PageOut)
5097             {
5098                 IoSetTopLevelIrp(NULL);
5099                 if (ResourceToRelease != NULL)
5100                 {
5101                     FsRtlReleaseFileForModWrite(Segment->FileObject, ResourceToRelease);
5102                 }
5103                 KeLowerIrql(OldIrql);
5104             }
5105         }
5106         else
5107         {
5108             /* This must only be called by the page-out path */
5109             ASSERT(PageOut);
5110 
5111             /* And this must be for a shared section in a DLL */
5112             ASSERT(FlagOn(Segment->Image.Characteristics, IMAGE_SCN_MEM_SHARED));
5113 
5114             SWAPENTRY SwapEntry = MmGetSavedSwapEntryPage(Page);
5115             if (!SwapEntry)
5116             {
5117                 SwapEntry = MmAllocSwapPage();
5118             }
5119 
5120             if (SwapEntry)
5121             {
5122                 Status = MmWriteToSwapPage(SwapEntry, Page);
5123                 if (NT_SUCCESS(Status))
5124                 {
5125                     MmSetSavedSwapEntryPage(Page, SwapEntry);
5126                 }
5127                 else
5128                 {
5129                     MmFreeSwapPage(SwapEntry);
5130                 }
5131             }
5132             else
5133             {
5134                 DPRINT1("Failed to allocate a swap page!\n");
5135                 Status = STATUS_INSUFFICIENT_RESOURCES;
5136             }
5137         }
5138 
5139         MmLockSectionSegment(Segment);
5140 
5141         /* Get the entry again */
5142         Entry = MmGetPageEntrySectionSegment(Segment, Offset);
5143         ASSERT(PFN_FROM_SSE(Entry) == Page);
5144 
5145         if (!NT_SUCCESS(Status))
5146         {
5147             /* Damn, this failed. Consider this page as still dirty */
5148             DPRINT1("MiWritePage FAILED: Status 0x%08x!\n", Status);
5149             DirtyAgain = TRUE;
5150         }
5151         else
5152         {
5153             /* Check if someone dirtified this page while we were not looking */
5154             DirtyAgain = IS_DIRTY_SSE(Entry);
5155         }
5156 
5157         /* Drop the reference we got, deleting the write altogether. */
5158         Entry = MAKE_SSE(Page << PAGE_SHIFT, SHARE_COUNT_FROM_SSE(Entry) - 1);
5159         if (DirtyAgain)
5160         {
5161             Entry = DIRTY_SSE(Entry);
5162         }
5163         MmSetPageEntrySectionSegment(Segment, Offset, Entry);
5164     }
5165 
5166     /* Were this page hanging there just for the sake of being present ? */
5167     if (!IS_DIRTY_SSE(Entry) && (SHARE_COUNT_FROM_SSE(Entry) == 0) && PageOut)
5168     {
5169         ULONG_PTR NewEntry = 0;
5170         /* Restore the swap entry here */
5171         if (!FlagOn(*Segment->Flags, MM_DATAFILE_SEGMENT))
5172         {
5173             SWAPENTRY SwapEntry = MmGetSavedSwapEntryPage(Page);
5174             if (SwapEntry)
5175                 NewEntry = MAKE_SWAP_SSE(SwapEntry);
5176         }
5177 
5178         /* Yes. Release it */
5179         MmSetPageEntrySectionSegment(Segment, Offset, NewEntry);
5180         MmReleasePageMemoryConsumer(MC_USER, Page);
5181         /* Tell the caller we released the page */
5182         return TRUE;
5183     }
5184 
5185     return FALSE;
5186 }
5187 
5188 NTSTATUS
5189 NTAPI
5190 MmMakePagesDirty(
5191     _In_ PEPROCESS Process,
5192     _In_ PVOID Address,
5193     _In_ ULONG Length)
5194 {
5195     PMEMORY_AREA MemoryArea;
5196     PMM_SECTION_SEGMENT Segment;
5197     LARGE_INTEGER SegmentOffset, RangeEnd;
5198     PMMSUPPORT AddressSpace = Process ? &Process->Vm : MmGetKernelAddressSpace();
5199 
5200     MmLockAddressSpace(AddressSpace);
5201 
5202     MemoryArea = MmLocateMemoryAreaByAddress(AddressSpace, Address);
5203     if (MemoryArea == NULL)
5204     {
5205         DPRINT1("Unable to find memory area at address %p.\n", Address);
5206         MmUnlockAddressSpace(AddressSpace);
5207         return STATUS_NOT_MAPPED_VIEW;
5208     }
5209 
5210     /* Only supported in old Mm for now */
5211     ASSERT(MemoryArea->Type == MEMORY_AREA_SECTION_VIEW);
5212     /* For file mappings */
5213     ASSERT(MemoryArea->VadNode.u.VadFlags.VadType != VadImageMap);
5214 
5215     Segment = MemoryArea->SectionData.Segment;
5216     MmLockSectionSegment(Segment);
5217 
5218     SegmentOffset.QuadPart = PAGE_ROUND_DOWN(Address) - MA_GetStartingAddress(MemoryArea)
5219             + MemoryArea->SectionData.ViewOffset;
5220     RangeEnd.QuadPart = PAGE_ROUND_UP((ULONG_PTR)Address + Length) - MA_GetStartingAddress(MemoryArea)
5221             + MemoryArea->SectionData.ViewOffset;
5222 
5223     DPRINT("MmMakePagesResident: Segment %p, 0x%I64x -> 0x%I64x\n", Segment, SegmentOffset.QuadPart, RangeEnd.QuadPart);
5224 
5225     while (SegmentOffset.QuadPart < RangeEnd.QuadPart)
5226     {
5227         ULONG_PTR Entry = MmGetPageEntrySectionSegment(Segment, &SegmentOffset);
5228 
5229         /* Let any pending read proceed */
5230         while (MM_IS_WAIT_PTE(Entry))
5231         {
5232             MmUnlockSectionSegment(Segment);
5233             MmUnlockAddressSpace(AddressSpace);
5234             KeDelayExecutionThread(KernelMode, FALSE, &TinyTime);
5235             MmLockAddressSpace(AddressSpace);
5236             MmLockSectionSegment(Segment);
5237             Entry = MmGetPageEntrySectionSegment(Segment, &SegmentOffset);
5238         }
5239 
5240         /* We are called from Cc, this can't be backed by the page files */
5241         ASSERT(!IS_SWAP_FROM_SSE(Entry));
5242 
5243         /* If there is no page there, there is nothing to make dirty */
5244         if (Entry != 0)
5245         {
5246             /* Dirtify the entry */
5247             MmSetPageEntrySectionSegment(Segment, &SegmentOffset, DIRTY_SSE(Entry));
5248         }
5249 
5250         SegmentOffset.QuadPart += PAGE_SIZE;
5251     }
5252 
5253     MmUnlockSectionSegment(Segment);
5254 
5255     MmUnlockAddressSpace(AddressSpace);
5256     return STATUS_SUCCESS;
5257 }
5258 
5259 NTSTATUS
5260 NTAPI
5261 MmExtendSection(
5262     _In_ PVOID _Section,
5263     _Inout_ PLARGE_INTEGER NewSize)
5264 {
5265     PSECTION Section = _Section;
5266 
5267     /* It makes no sense to extend an image mapping */
5268     if (Section->u.Flags.Image)
5269         return STATUS_SECTION_NOT_EXTENDED;
5270 
5271     /* Nor is it possible to extend a page file mapping */
5272     if (!Section->u.Flags.File)
5273         return STATUS_SECTION_NOT_EXTENDED;
5274 
5275     if (!MiIsRosSectionObject(Section))
5276         return STATUS_NOT_IMPLEMENTED;
5277 
5278     /* We just extend the sizes. Shrinking is a no-op ? */
5279     if (NewSize->QuadPart > Section->SizeOfSection.QuadPart)
5280     {
5281         PMM_SECTION_SEGMENT Segment = (PMM_SECTION_SEGMENT)Section->Segment;
5282         Section->SizeOfSection = *NewSize;
5283 
5284         if (!Section->u.Flags.Reserve)
5285         {
5286             MmLockSectionSegment(Segment);
5287             if (Segment->RawLength.QuadPart < NewSize->QuadPart)
5288             {
5289                 Segment->RawLength = *NewSize;
5290                 Segment->Length.QuadPart = (NewSize->QuadPart + PAGE_SIZE - 1) & ~((LONGLONG)PAGE_SIZE);
5291             }
5292             MmUnlockSectionSegment(Segment);
5293         }
5294     }
5295 
5296     return STATUS_SUCCESS;
5297 }
5298 
5299 /* EOF */
5300