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